Merge branch 'master' of git://git.infradead.org/users/linville/wireless-next into...
authorJohn W. Linville <linville@tuxdriver.com>
Tue, 11 Oct 2011 19:35:42 +0000 (15:35 -0400)
committerJohn W. Linville <linville@tuxdriver.com>
Tue, 11 Oct 2011 19:35:42 +0000 (15:35 -0400)
Conflicts:
Documentation/feature-removal-schedule.txt

1  2 
Documentation/feature-removal-schedule.txt
drivers/net/wireless/libertas/main.c
drivers/net/wireless/mwifiex/main.c
include/linux/if_ether.h
net/mac80211/agg-rx.c
net/mac80211/cfg.c
net/mac80211/ibss.c
net/mac80211/iface.c
net/mac80211/sta_info.c

@@@ -296,6 -296,15 +296,6 @@@ Who:      Ravikiran Thirumalai <kiran@scalex
  
  ---------------------------
  
 -What: CONFIG_THERMAL_HWMON
 -When: January 2009
 -Why:  This option was introduced just to allow older lm-sensors userspace
 -      to keep working over the upgrade to 2.6.26. At the scheduled time of
 -      removal fixed lm-sensors (2.x or 3.x) should be readily available.
 -Who:  Rene Herman <rene.herman@gmail.com>
 -
 ----------------------------
 -
  What: Code that is now under CONFIG_WIRELESS_EXT_SYSFS
        (in net/core/net-sysfs.c)
  When: After the only user (hal) has seen a release with the patches
@@@ -518,41 -527,6 +518,41 @@@ Who:     Laurent Pinchart <laurent.pinchart
  
  ----------------------------
  
 +What: Support for driver specific ioctls in the pwc driver (everything
 +      defined in media/pwc-ioctl.h)
 +When: 3.3
 +Why:  This stems from the v4l1 era, with v4l2 everything can be done with
 +      standardized v4l2 API calls
 +Who:  Hans de Goede <hdegoede@redhat.com>
 +
 +----------------------------
 +
 +What: Driver specific sysfs API in the pwc driver
 +When: 3.3
 +Why:  Setting pan/tilt should be done with v4l2 controls, like with other
 +      cams. The button is available as a standard input device
 +Who:  Hans de Goede <hdegoede@redhat.com>
 +
 +----------------------------
 +
 +What: Driver specific use of pixfmt.priv in the pwc driver
 +When: 3.3
 +Why:  The .priv field never was intended for this, setting a framerate is
 +      support using the standardized S_PARM ioctl
 +Who:  Hans de Goede <hdegoede@redhat.com>
 +
 +----------------------------
 +
 +What: Software emulation of arbritary resolutions in the pwc driver
 +When: 3.3
 +Why:  The pwc driver claims to support any resolution between 160x120
 +      and 640x480, but emulates this by simply drawing a black border
 +      around the image. Userspace can draw its own black border if it
 +      really wants one.
 +Who:  Hans de Goede <hdegoede@redhat.com>
 +
 +----------------------------
 +
  What: For VIDIOC_S_FREQUENCY the type field must match the device node's type.
        If not, return -EINVAL.
  When: 3.2
@@@ -581,22 -555,8 +581,31 @@@ Why:     This driver has been superseded b
  Who:  Alan Stern <stern@rowland.harvard.edu>
  
  ----------------------------
 -Who:  Don Fry <donald.h.fry@intel.com>
 +
 +What:   threeg and interface sysfs files in /sys/devices/platform/acer-wmi
 +When:   2012
 +Why:    In 3.0, we can now autodetect internal 3G device and already have
 +      the threeg rfkill device. So, we plan to remove threeg sysfs support
 +      for it's no longer necessary.
 +
 +      We also plan to remove interface sysfs file that exposed which ACPI-WMI
 +      interface that was used by acer-wmi driver. It will replaced by
 +      information log when acer-wmi initial.
 +Who:    Lee, Chun-Yi <jlee@novell.com>
 +
 +----------------------------
++
 +What: The XFS nodelaylog mount option
 +When: 3.3
 +Why:  The delaylog mode that has been the default since 2.6.39 has proven
 +      stable, and the old code is in the way of additional improvements in
 +      the log code.
 +Who:  Christoph Hellwig <hch@lst.de>
++
++----------------------------
++
+ What: iwlagn alias support
+ When: 3.5
+ Why:  The iwlagn module has been renamed iwlwifi.  The alias will be around
+       for backward compatibility for several cycles and then dropped.
++Who:  Don Fry <donald.h.fry@intel.com>
@@@ -188,6 -188,7 +188,7 @@@ int lbs_stop_iface(struct lbs_private *
        spin_unlock_irqrestore(&priv->driver_lock, flags);
  
        cancel_work_sync(&priv->mcast_work);
+       del_timer_sync(&priv->tx_lockup_timer);
  
        /* Disable command processing, and wait for all commands to complete */
        lbs_deb_main("waiting for commands to complete\n");
@@@ -243,6 -244,7 +244,7 @@@ void lbs_host_to_card_done(struct lbs_p
        lbs_deb_enter(LBS_DEB_THREAD);
  
        spin_lock_irqsave(&priv->driver_lock, flags);
+       del_timer(&priv->tx_lockup_timer);
  
        priv->dnld_sent = DNLD_RES_RECEIVED;
  
@@@ -585,6 -587,9 +587,9 @@@ static int lbs_thread(void *data
                        if (ret) {
                                lbs_deb_tx("host_to_card failed %d\n", ret);
                                priv->dnld_sent = DNLD_RES_RECEIVED;
+                       } else {
+                               mod_timer(&priv->tx_lockup_timer,
+                                         jiffies + (HZ * 5));
                        }
                        priv->tx_pending_len = 0;
                        if (!priv->currenttxskb) {
        }
  
        del_timer(&priv->command_timer);
+       del_timer(&priv->tx_lockup_timer);
        del_timer(&priv->auto_deepsleep_timer);
  
        lbs_deb_leave(LBS_DEB_THREAD);
@@@ -734,6 -740,32 +740,32 @@@ out
        lbs_deb_leave(LBS_DEB_CMD);
  }
  
+ /**
+  * lbs_tx_lockup_handler - handles the timeout of the passing of TX frames
+  * to the hardware. This is known to frequently happen with SD8686 when
+  * waking up after a Wake-on-WLAN-triggered resume.
+  *
+  * @data: &struct lbs_private pointer
+  */
+ static void lbs_tx_lockup_handler(unsigned long data)
+ {
+       struct lbs_private *priv = (struct lbs_private *)data;
+       unsigned long flags;
+       lbs_deb_enter(LBS_DEB_TX);
+       spin_lock_irqsave(&priv->driver_lock, flags);
+       netdev_info(priv->dev, "TX lockup detected\n");
+       if (priv->reset_card)
+               priv->reset_card(priv);
+       priv->dnld_sent = DNLD_RES_RECEIVED;
+       wake_up_interruptible(&priv->waitq);
+       spin_unlock_irqrestore(&priv->driver_lock, flags);
+       lbs_deb_leave(LBS_DEB_TX);
+ }
  /**
   * auto_deepsleep_timer_fn - put the device back to deep sleep mode when
   * timer expires and no activity (command, event, data etc.) is detected.
@@@ -820,6 -852,8 +852,8 @@@ static int lbs_init_adapter(struct lbs_
  
        setup_timer(&priv->command_timer, lbs_cmd_timeout_handler,
                (unsigned long)priv);
+       setup_timer(&priv->tx_lockup_timer, lbs_tx_lockup_handler,
+               (unsigned long)priv);
        setup_timer(&priv->auto_deepsleep_timer, auto_deepsleep_timer_fn,
                        (unsigned long)priv);
  
@@@ -857,6 -891,7 +891,7 @@@ static void lbs_free_adapter(struct lbs
        lbs_free_cmd_buffer(priv);
        kfifo_free(&priv->event_fifo);
        del_timer(&priv->command_timer);
+       del_timer(&priv->tx_lockup_timer);
        del_timer(&priv->auto_deepsleep_timer);
  
        lbs_deb_leave(LBS_DEB_MAIN);
@@@ -867,7 -902,7 +902,7 @@@ static const struct net_device_ops lbs_
        .ndo_stop               = lbs_eth_stop,
        .ndo_start_xmit         = lbs_hard_start_xmit,
        .ndo_set_mac_address    = lbs_set_mac_address,
 -      .ndo_set_multicast_list = lbs_set_multicast_list,
 +      .ndo_set_rx_mode        = lbs_set_multicast_list,
        .ndo_change_mtu         = eth_change_mtu,
        .ndo_validate_addr      = eth_validate_addr,
  };
  
  const char driver_version[] = "mwifiex " VERSION " (%s) ";
  
- static struct mwifiex_bss_attr mwifiex_bss_sta[] = {
-       {MWIFIEX_BSS_TYPE_STA, MWIFIEX_DATA_FRAME_TYPE_ETH_II, true, 0, 0},
- };
- static int drv_mode = DRV_MODE_STA;
- /* Supported drv_mode table */
- static struct mwifiex_drv_mode mwifiex_drv_mode_tbl[] = {
-       {
-               .drv_mode = DRV_MODE_STA,
-               .intf_num = ARRAY_SIZE(mwifiex_bss_sta),
-               .bss_attr = mwifiex_bss_sta,
-       },
- };
  /*
   * This function registers the device and performs all the necessary
   * initializations.
@@@ -57,7 -42,6 +42,6 @@@
   * proper cleanup before exiting.
   */
  static int mwifiex_register(void *card, struct mwifiex_if_ops *if_ops,
-                           struct mwifiex_drv_mode *drv_mode_ptr,
                            void **padapter)
  {
        struct mwifiex_adapter *adapter;
                goto error;
  
        adapter->priv_num = 0;
-       for (i = 0; i < drv_mode_ptr->intf_num; i++) {
-               adapter->priv[i] = NULL;
-               if (!drv_mode_ptr->bss_attr[i].active)
-                       continue;
-               /* Allocate memory for private structure */
-               adapter->priv[i] = kzalloc(sizeof(struct mwifiex_private),
-                               GFP_KERNEL);
-               if (!adapter->priv[i]) {
-                       dev_err(adapter->dev, "%s: failed to alloc priv[%d]\n",
-                              __func__, i);
-                       goto error;
-               }
  
-               adapter->priv_num++;
-               adapter->priv[i]->adapter = adapter;
-               /* Save bss_type, frame_type & bss_priority */
-               adapter->priv[i]->bss_type = drv_mode_ptr->bss_attr[i].bss_type;
-               adapter->priv[i]->frame_type =
-                                       drv_mode_ptr->bss_attr[i].frame_type;
-               adapter->priv[i]->bss_priority =
-                                       drv_mode_ptr->bss_attr[i].bss_priority;
-               if (drv_mode_ptr->bss_attr[i].bss_type == MWIFIEX_BSS_TYPE_STA)
-                       adapter->priv[i]->bss_role = MWIFIEX_BSS_ROLE_STA;
-               else if (drv_mode_ptr->bss_attr[i].bss_type ==
-                                                       MWIFIEX_BSS_TYPE_UAP)
-                       adapter->priv[i]->bss_role = MWIFIEX_BSS_ROLE_UAP;
-               /* Save bss_index & bss_num */
-               adapter->priv[i]->bss_index = i;
-               adapter->priv[i]->bss_num = drv_mode_ptr->bss_attr[i].bss_num;
+       /* Allocate memory for private structure */
+       adapter->priv[0] = kzalloc(sizeof(struct mwifiex_private),
+                       GFP_KERNEL);
+       if (!adapter->priv[0]) {
+               dev_err(adapter->dev, "%s: failed to alloc priv[0]\n",
+                      __func__);
+               goto error;
        }
-       adapter->drv_mode = drv_mode_ptr;
  
-       if (mwifiex_init_lock_list(adapter))
-               goto error;
+       adapter->priv_num++;
+       adapter->priv[0]->adapter = adapter;
+       mwifiex_init_lock_list(adapter);
  
        init_timer(&adapter->cmd_timer);
        adapter->cmd_timer.function = mwifiex_cmd_timeout_func;
  error:
        dev_dbg(adapter->dev, "info: leave mwifiex_register with error\n");
  
-       mwifiex_free_lock_list(adapter);
-       for (i = 0; i < drv_mode_ptr->intf_num; i++)
+       for (i = 0; i < adapter->priv_num; i++)
                kfree(adapter->priv[i]);
        kfree(adapter);
  
        return -1;
@@@ -315,38 -275,6 +275,6 @@@ exit_main_proc
        return ret;
  }
  
- /*
-  * This function initializes the software.
-  *
-  * The main work includes allocating and initializing the adapter structure
-  * and initializing the private structures.
-  */
- static int
- mwifiex_init_sw(void *card, struct mwifiex_if_ops *if_ops, void **padapter)
- {
-       int i;
-       struct mwifiex_drv_mode *drv_mode_ptr;
-       /* find mwifiex_drv_mode entry from mwifiex_drv_mode_tbl */
-       drv_mode_ptr = NULL;
-       for (i = 0; i < ARRAY_SIZE(mwifiex_drv_mode_tbl); i++) {
-               if (mwifiex_drv_mode_tbl[i].drv_mode == drv_mode) {
-                       drv_mode_ptr = &mwifiex_drv_mode_tbl[i];
-                       break;
-               }
-       }
-       if (!drv_mode_ptr) {
-               pr_err("invalid drv_mode=%d\n", drv_mode);
-               return -1;
-       }
-       if (mwifiex_register(card, if_ops, drv_mode_ptr, padapter))
-               return -1;
-       return 0;
- }
  /*
   * This function frees the adapter structure.
   *
@@@ -627,7 -555,7 +555,7 @@@ static const struct net_device_ops mwif
        .ndo_set_mac_address = mwifiex_set_mac_address,
        .ndo_tx_timeout = mwifiex_tx_timeout,
        .ndo_get_stats = mwifiex_get_stats,
 -      .ndo_set_multicast_list = mwifiex_set_multicast_list,
 +      .ndo_set_rx_mode = mwifiex_set_multicast_list,
  };
  
  /*
   *
   * In addition, the CFG80211 work queue is also created.
   */
- static void
mwifiex_init_priv_params(struct mwifiex_private *priv, struct net_device *dev)
+ void mwifiex_init_priv_params(struct mwifiex_private *priv,
                                              struct net_device *dev)
  {
        dev->netdev_ops = &mwifiex_netdev_ops;
        /* Initialize private structure */
        memcpy(dev->dev_addr, priv->curr_addr, ETH_ALEN);
  }
  
- /*
-  * This function adds a new logical interface.
-  *
-  * It allocates, initializes and registers the interface by performing
-  * the following opearations -
-  *      - Allocate a new net device structure
-  *      - Assign device name
-  *      - Register the new device with CFG80211 subsystem
-  *      - Initialize semaphore and private structure
-  *      - Register the new device with kernel
-  *      - Create the complete debug FS structure if configured
-  */
- static struct mwifiex_private *mwifiex_add_interface(
-                       struct mwifiex_adapter *adapter,
-                       u8 bss_index, u8 bss_type)
- {
-       struct net_device *dev;
-       struct mwifiex_private *priv;
-       void *mdev_priv;
-       dev = alloc_netdev_mq(sizeof(struct mwifiex_private *), "mlan%d",
-                             ether_setup, 1);
-       if (!dev) {
-               dev_err(adapter->dev, "no memory available for netdevice\n");
-               goto error;
-       }
-       if (mwifiex_register_cfg80211(dev, adapter->priv[bss_index]->curr_addr,
-                                     adapter->priv[bss_index]) != 0) {
-               dev_err(adapter->dev, "cannot register netdevice with cfg80211\n");
-               goto error;
-       }
-       /* Save the priv pointer in netdev */
-       priv = adapter->priv[bss_index];
-       mdev_priv = netdev_priv(dev);
-       *((unsigned long *) mdev_priv) = (unsigned long) priv;
-       priv->netdev = dev;
-       sema_init(&priv->async_sem, 1);
-       priv->scan_pending_on_block = false;
-       mwifiex_init_priv_params(priv, dev);
-       SET_NETDEV_DEV(dev, adapter->dev);
-       /* Register network device */
-       if (register_netdev(dev)) {
-               dev_err(adapter->dev, "cannot register virtual network device\n");
-               goto error;
-       }
-       dev_dbg(adapter->dev, "info: %s: Marvell 802.11 Adapter\n", dev->name);
- #ifdef CONFIG_DEBUG_FS
-       mwifiex_dev_debugfs_init(priv);
- #endif
-       return priv;
- error:
-       if (dev)
-               free_netdev(dev);
-       return NULL;
- }
- /*
-  * This function removes a logical interface.
-  *
-  * It deregisters, resets and frees the interface by performing
-  * the following operations -
-  *      - Disconnect the device if connected, send wireless event to
-  *        notify applications.
-  *      - Remove the debug FS structure if configured
-  *      - Unregister the device from kernel
-  *      - Free the net device structure
-  *      - Cancel all works and destroy work queue
-  *      - Unregister and free the wireless device from CFG80211 subsystem
-  */
- static void
- mwifiex_remove_interface(struct mwifiex_adapter *adapter, u8 bss_index)
- {
-       struct net_device *dev;
-       struct mwifiex_private *priv = adapter->priv[bss_index];
-       if (!priv)
-               return;
-       dev = priv->netdev;
-       if (priv->media_connected)
-               priv->media_connected = false;
- #ifdef CONFIG_DEBUG_FS
-       mwifiex_dev_debugfs_remove(priv);
- #endif
-       /* Last reference is our one */
-       dev_dbg(adapter->dev, "info: %s: refcnt = %d\n",
-                               dev->name, netdev_refcnt_read(dev));
-       if (dev->reg_state == NETREG_REGISTERED)
-               unregister_netdev(dev);
-       /* Clear the priv in adapter */
-       priv->netdev = NULL;
-       if (dev)
-               free_netdev(dev);
-       cancel_work_sync(&priv->cfg_workqueue);
-       flush_workqueue(priv->workqueue);
-       destroy_workqueue(priv->workqueue);
-       wiphy_unregister(priv->wdev->wiphy);
-       wiphy_free(priv->wdev->wiphy);
-       kfree(priv->wdev);
- }
  /*
   * This function check if command is pending.
   */
@@@ -847,14 -663,14 +663,14 @@@ in
  mwifiex_add_card(void *card, struct semaphore *sem,
                 struct mwifiex_if_ops *if_ops)
  {
-       int i;
        struct mwifiex_adapter *adapter;
        char fmt[64];
+       struct mwifiex_private *priv;
  
        if (down_interruptible(sem))
                goto exit_sem_err;
  
-       if (mwifiex_init_sw(card, if_ops, (void **)&adapter)) {
+       if (mwifiex_register(card, if_ops, (void **)&adapter)) {
                pr_err("%s: software init failed\n", __func__);
                goto err_init_sw;
        }
                goto err_init_fw;
        }
  
-       /* Add interfaces */
-       for (i = 0; i < adapter->drv_mode->intf_num; i++) {
-               if (!mwifiex_add_interface(adapter, i,
-                               adapter->drv_mode->bss_attr[i].bss_type)) {
-                       goto err_add_intf;
-               }
+       priv = adapter->priv[0];
+       if (mwifiex_register_cfg80211(priv) != 0) {
+               dev_err(adapter->dev, "cannot register netdevice"
+                              " with cfg80211\n");
+                       goto err_init_fw;
+       }
+       rtnl_lock();
+       /* Create station interface by default */
+       if (!mwifiex_add_virtual_intf(priv->wdev->wiphy, "mlan%d",
+                               NL80211_IFTYPE_STATION, NULL, NULL)) {
+               rtnl_unlock();
+               dev_err(adapter->dev, "cannot create default station"
+                               " interface\n");
+               goto err_add_intf;
        }
  
+       rtnl_unlock();
        up(sem);
  
        mwifiex_drv_get_driver_version(adapter, fmt, sizeof(fmt) - 1);
        return 0;
  
  err_add_intf:
-       for (i = 0; i < adapter->priv_num; i++)
-               mwifiex_remove_interface(adapter, i);
+       rtnl_lock();
+       mwifiex_del_virtual_intf(priv->wdev->wiphy, priv->netdev);
+       rtnl_unlock();
  err_init_fw:
        pr_debug("info: %s: unregister device\n", __func__);
        adapter->if_ops.unregister_dev(adapter);
@@@ -960,7 -789,7 +789,7 @@@ int mwifiex_remove_card(struct mwifiex_
        /* Stop data */
        for (i = 0; i < adapter->priv_num; i++) {
                priv = adapter->priv[i];
-               if (priv) {
+               if (priv && priv->netdev) {
                        if (!netif_queue_stopped(priv->netdev))
                                netif_stop_queue(priv->netdev);
                        if (netif_carrier_ok(priv->netdev))
                       atomic_read(&adapter->cmd_pending));
        }
  
-       /* Remove interface */
-       for (i = 0; i < adapter->priv_num; i++)
-               mwifiex_remove_interface(adapter, i);
+       for (i = 0; i < adapter->priv_num; i++) {
+               priv = adapter->priv[i];
+               if (!priv)
+                       continue;
+               rtnl_lock();
+               mwifiex_del_virtual_intf(priv->wdev->wiphy, priv->netdev);
+               rtnl_unlock();
+       }
+       wiphy_unregister(priv->wdev->wiphy);
+       wiphy_free(priv->wdev->wiphy);
+       kfree(priv->wdev);
  
        mwifiex_terminate_workqueue(adapter);
  
diff --combined include/linux/if_ether.h
  #define ETH_P_8021AH  0x88E7          /* 802.1ah Backbone Service Tag */
  #define ETH_P_1588    0x88F7          /* IEEE 1588 Timesync */
  #define ETH_P_FCOE    0x8906          /* Fibre Channel over Ethernet  */
+ #define ETH_P_TDLS    0x890D          /* TDLS */
  #define ETH_P_FIP     0x8914          /* FCoE Initialization Protocol */
  #define ETH_P_QINQ1   0x9100          /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */
  #define ETH_P_QINQ2   0x9200          /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */
  #define ETH_P_QINQ3   0x9300          /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */
  #define ETH_P_EDSA    0xDADA          /* Ethertype DSA [ NOT AN OFFICIALLY REGISTERED ID ] */
 +#define ETH_P_AF_IUCV   0xFBFB                /* IBM af_iucv [ NOT AN OFFICIALLY REGISTERED ID ] */
  
  /*
   *    Non DIX types. Won't clash for 1500 types.
diff --combined net/mac80211/agg-rx.c
@@@ -69,7 -69,7 +69,7 @@@ void ___ieee80211_stop_rx_ba_session(st
        if (!tid_rx)
                return;
  
 -      rcu_assign_pointer(sta->ampdu_mlme.tid_rx[tid], NULL);
 +      RCU_INIT_POINTER(sta->ampdu_mlme.tid_rx[tid], NULL);
  
  #ifdef CONFIG_MAC80211_HT_DEBUG
        printk(KERN_DEBUG "Rx BA session stop requested for %pM tid %u\n",
@@@ -223,7 -223,7 +223,7 @@@ void ieee80211_process_addba_request(st
  
        status = WLAN_STATUS_REQUEST_DECLINED;
  
-       if (test_sta_flags(sta, WLAN_STA_BLOCK_BA)) {
+       if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) {
  #ifdef CONFIG_MAC80211_HT_DEBUG
                printk(KERN_DEBUG "Suspend in progress. "
                       "Denying ADDBA request\n");
        status = WLAN_STATUS_SUCCESS;
  
        /* activate it for RX */
 -      rcu_assign_pointer(sta->ampdu_mlme.tid_rx[tid], tid_agg_rx);
 +      RCU_INIT_POINTER(sta->ampdu_mlme.tid_rx[tid], tid_agg_rx);
  
        if (timeout)
                mod_timer(&tid_agg_rx->session_timer, TU_TO_EXP_TIME(timeout));
diff --combined net/mac80211/cfg.c
@@@ -12,6 -12,7 +12,7 @@@
  #include <linux/slab.h>
  #include <net/net_namespace.h>
  #include <linux/rcupdate.h>
+ #include <linux/if_ether.h>
  #include <net/cfg80211.h>
  #include "ieee80211_i.h"
  #include "driver-ops.h"
@@@ -62,7 -63,7 +63,7 @@@ static int ieee80211_change_iface(struc
  
        if (type == NL80211_IFTYPE_AP_VLAN &&
            params && params->use_4addr == 0)
 -              rcu_assign_pointer(sdata->u.vlan.sta, NULL);
 +              RCU_INIT_POINTER(sdata->u.vlan.sta, NULL);
        else if (type == NL80211_IFTYPE_STATION &&
                 params && params->use_4addr >= 0)
                sdata->u.mgd.use_4addr = params->use_4addr;
@@@ -556,7 -557,7 +557,7 @@@ static int ieee80211_config_beacon(stru
  
        sdata->vif.bss_conf.dtim_period = new->dtim_period;
  
 -      rcu_assign_pointer(sdata->u.ap.beacon, new);
 +      RCU_INIT_POINTER(sdata->u.ap.beacon, new);
  
        synchronize_rcu();
  
@@@ -611,7 -612,7 +612,7 @@@ static int ieee80211_del_beacon(struct 
        if (!old)
                return -ENOENT;
  
 -      rcu_assign_pointer(sdata->u.ap.beacon, NULL);
 +      RCU_INIT_POINTER(sdata->u.ap.beacon, NULL);
        synchronize_rcu();
        kfree(old);
  
@@@ -667,7 -668,6 +668,6 @@@ static void sta_apply_parameters(struc
                                 struct sta_info *sta,
                                 struct station_parameters *params)
  {
-       unsigned long flags;
        u32 rates;
        int i, j;
        struct ieee80211_supported_band *sband;
  
        sband = local->hw.wiphy->bands[local->oper_channel->band];
  
-       spin_lock_irqsave(&sta->flaglock, flags);
        mask = params->sta_flags_mask;
        set = params->sta_flags_set;
  
        if (mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
-               sta->flags &= ~WLAN_STA_AUTHORIZED;
                if (set & BIT(NL80211_STA_FLAG_AUTHORIZED))
-                       sta->flags |= WLAN_STA_AUTHORIZED;
+                       set_sta_flag(sta, WLAN_STA_AUTHORIZED);
+               else
+                       clear_sta_flag(sta, WLAN_STA_AUTHORIZED);
        }
  
        if (mask & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) {
-               sta->flags &= ~WLAN_STA_SHORT_PREAMBLE;
                if (set & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE))
-                       sta->flags |= WLAN_STA_SHORT_PREAMBLE;
+                       set_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE);
+               else
+                       clear_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE);
        }
  
        if (mask & BIT(NL80211_STA_FLAG_WME)) {
-               sta->flags &= ~WLAN_STA_WME;
-               sta->sta.wme = false;
                if (set & BIT(NL80211_STA_FLAG_WME)) {
-                       sta->flags |= WLAN_STA_WME;
+                       set_sta_flag(sta, WLAN_STA_WME);
                        sta->sta.wme = true;
+               } else {
+                       clear_sta_flag(sta, WLAN_STA_WME);
+                       sta->sta.wme = false;
                }
        }
  
        if (mask & BIT(NL80211_STA_FLAG_MFP)) {
-               sta->flags &= ~WLAN_STA_MFP;
                if (set & BIT(NL80211_STA_FLAG_MFP))
-                       sta->flags |= WLAN_STA_MFP;
+                       set_sta_flag(sta, WLAN_STA_MFP);
+               else
+                       clear_sta_flag(sta, WLAN_STA_MFP);
        }
  
        if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) {
-               sta->flags &= ~WLAN_STA_AUTH;
                if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED))
-                       sta->flags |= WLAN_STA_AUTH;
+                       set_sta_flag(sta, WLAN_STA_AUTH);
+               else
+                       clear_sta_flag(sta, WLAN_STA_AUTH);
        }
-       spin_unlock_irqrestore(&sta->flaglock, flags);
  
-       sta->sta.uapsd_queues = params->uapsd_queues;
-       sta->sta.max_sp = params->max_sp;
+       if (mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) {
+               if (set & BIT(NL80211_STA_FLAG_TDLS_PEER))
+                       set_sta_flag(sta, WLAN_STA_TDLS_PEER);
+               else
+                       clear_sta_flag(sta, WLAN_STA_TDLS_PEER);
+       }
+       if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD) {
+               sta->sta.uapsd_queues = params->uapsd_queues;
+               sta->sta.max_sp = params->max_sp;
+       }
  
        /*
         * cfg80211 validates this (1-2007) and allows setting the AID
@@@ -806,10 -818,17 +818,17 @@@ static int ieee80211_add_station(struc
        if (!sta)
                return -ENOMEM;
  
-       sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC;
+       set_sta_flag(sta, WLAN_STA_AUTH);
+       set_sta_flag(sta, WLAN_STA_ASSOC);
  
        sta_apply_parameters(local, sta, params);
  
+       /* Only TDLS-supporting stations can add TDLS peers */
+       if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
+           !((wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) &&
+             sdata->vif.type == NL80211_IFTYPE_STATION))
+               return -ENOTSUPP;
        rate_control_rate_init(sta);
  
        layer2_update = sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
@@@ -862,6 -881,14 +881,14 @@@ static int ieee80211_change_station(str
                return -ENOENT;
        }
  
+       /* The TDLS bit cannot be toggled after the STA was added */
+       if ((params->sta_flags_mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) &&
+           !!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) !=
+           !!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
+               rcu_read_unlock();
+               return -EINVAL;
+       }
        if (params->vlan && params->vlan != sta->sdata->dev) {
                vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
  
                                return -EBUSY;
                        }
  
 -                      rcu_assign_pointer(vlansdata->u.vlan.sta, sta);
 +                      RCU_INIT_POINTER(vlansdata->u.vlan.sta, sta);
                }
  
                sta->sdata = vlansdata;
@@@ -2126,6 -2153,323 +2153,323 @@@ static int ieee80211_set_rekey_data(str
        return 0;
  }
  
+ static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb)
+ {
+       u8 *pos = (void *)skb_put(skb, 7);
+       *pos++ = WLAN_EID_EXT_CAPABILITY;
+       *pos++ = 5; /* len */
+       *pos++ = 0x0;
+       *pos++ = 0x0;
+       *pos++ = 0x0;
+       *pos++ = 0x0;
+       *pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED;
+ }
+ static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata)
+ {
+       struct ieee80211_local *local = sdata->local;
+       u16 capab;
+       capab = 0;
+       if (local->oper_channel->band != IEEE80211_BAND_2GHZ)
+               return capab;
+       if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE))
+               capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
+       if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE))
+               capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
+       return capab;
+ }
+ static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, u8 *src_addr,
+                                      u8 *peer, u8 *bssid)
+ {
+       struct ieee80211_tdls_lnkie *lnkid;
+       lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie));
+       lnkid->ie_type = WLAN_EID_LINK_ID;
+       lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2;
+       memcpy(lnkid->bssid, bssid, ETH_ALEN);
+       memcpy(lnkid->init_sta, src_addr, ETH_ALEN);
+       memcpy(lnkid->resp_sta, peer, ETH_ALEN);
+ }
+ static int
+ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
+                              u8 *peer, u8 action_code, u8 dialog_token,
+                              u16 status_code, struct sk_buff *skb)
+ {
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_tdls_data *tf;
+       tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u));
+       memcpy(tf->da, peer, ETH_ALEN);
+       memcpy(tf->sa, sdata->vif.addr, ETH_ALEN);
+       tf->ether_type = cpu_to_be16(ETH_P_TDLS);
+       tf->payload_type = WLAN_TDLS_SNAP_RFTYPE;
+       switch (action_code) {
+       case WLAN_TDLS_SETUP_REQUEST:
+               tf->category = WLAN_CATEGORY_TDLS;
+               tf->action_code = WLAN_TDLS_SETUP_REQUEST;
+               skb_put(skb, sizeof(tf->u.setup_req));
+               tf->u.setup_req.dialog_token = dialog_token;
+               tf->u.setup_req.capability =
+                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
+               ieee80211_add_srates_ie(&sdata->vif, skb);
+               ieee80211_add_ext_srates_ie(&sdata->vif, skb);
+               ieee80211_tdls_add_ext_capab(skb);
+               break;
+       case WLAN_TDLS_SETUP_RESPONSE:
+               tf->category = WLAN_CATEGORY_TDLS;
+               tf->action_code = WLAN_TDLS_SETUP_RESPONSE;
+               skb_put(skb, sizeof(tf->u.setup_resp));
+               tf->u.setup_resp.status_code = cpu_to_le16(status_code);
+               tf->u.setup_resp.dialog_token = dialog_token;
+               tf->u.setup_resp.capability =
+                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
+               ieee80211_add_srates_ie(&sdata->vif, skb);
+               ieee80211_add_ext_srates_ie(&sdata->vif, skb);
+               ieee80211_tdls_add_ext_capab(skb);
+               break;
+       case WLAN_TDLS_SETUP_CONFIRM:
+               tf->category = WLAN_CATEGORY_TDLS;
+               tf->action_code = WLAN_TDLS_SETUP_CONFIRM;
+               skb_put(skb, sizeof(tf->u.setup_cfm));
+               tf->u.setup_cfm.status_code = cpu_to_le16(status_code);
+               tf->u.setup_cfm.dialog_token = dialog_token;
+               break;
+       case WLAN_TDLS_TEARDOWN:
+               tf->category = WLAN_CATEGORY_TDLS;
+               tf->action_code = WLAN_TDLS_TEARDOWN;
+               skb_put(skb, sizeof(tf->u.teardown));
+               tf->u.teardown.reason_code = cpu_to_le16(status_code);
+               break;
+       case WLAN_TDLS_DISCOVERY_REQUEST:
+               tf->category = WLAN_CATEGORY_TDLS;
+               tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST;
+               skb_put(skb, sizeof(tf->u.discover_req));
+               tf->u.discover_req.dialog_token = dialog_token;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+ }
+ static int
+ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
+                          u8 *peer, u8 action_code, u8 dialog_token,
+                          u16 status_code, struct sk_buff *skb)
+ {
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_mgmt *mgmt;
+       mgmt = (void *)skb_put(skb, 24);
+       memset(mgmt, 0, 24);
+       memcpy(mgmt->da, peer, ETH_ALEN);
+       memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
+       memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN);
+       mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+                                         IEEE80211_STYPE_ACTION);
+       switch (action_code) {
+       case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+               skb_put(skb, 1 + sizeof(mgmt->u.action.u.tdls_discover_resp));
+               mgmt->u.action.category = WLAN_CATEGORY_PUBLIC;
+               mgmt->u.action.u.tdls_discover_resp.action_code =
+                       WLAN_PUB_ACTION_TDLS_DISCOVER_RES;
+               mgmt->u.action.u.tdls_discover_resp.dialog_token =
+                       dialog_token;
+               mgmt->u.action.u.tdls_discover_resp.capability =
+                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
+               ieee80211_add_srates_ie(&sdata->vif, skb);
+               ieee80211_add_ext_srates_ie(&sdata->vif, skb);
+               ieee80211_tdls_add_ext_capab(skb);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+ }
+ static int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
+                              u8 *peer, u8 action_code, u8 dialog_token,
+                              u16 status_code, const u8 *extra_ies,
+                              size_t extra_ies_len)
+ {
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_tx_info *info;
+       struct sk_buff *skb = NULL;
+       bool send_direct;
+       int ret;
+       if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
+               return -ENOTSUPP;
+       /* make sure we are in managed mode, and associated */
+       if (sdata->vif.type != NL80211_IFTYPE_STATION ||
+           !sdata->u.mgd.associated)
+               return -EINVAL;
+ #ifdef CONFIG_MAC80211_VERBOSE_TDLS_DEBUG
+       printk(KERN_DEBUG "TDLS mgmt action %d peer %pM\n", action_code, peer);
+ #endif
+       skb = dev_alloc_skb(local->hw.extra_tx_headroom +
+                           max(sizeof(struct ieee80211_mgmt),
+                               sizeof(struct ieee80211_tdls_data)) +
+                           50 + /* supported rates */
+                           7 + /* ext capab */
+                           extra_ies_len +
+                           sizeof(struct ieee80211_tdls_lnkie));
+       if (!skb)
+               return -ENOMEM;
+       info = IEEE80211_SKB_CB(skb);
+       skb_reserve(skb, local->hw.extra_tx_headroom);
+       switch (action_code) {
+       case WLAN_TDLS_SETUP_REQUEST:
+       case WLAN_TDLS_SETUP_RESPONSE:
+       case WLAN_TDLS_SETUP_CONFIRM:
+       case WLAN_TDLS_TEARDOWN:
+       case WLAN_TDLS_DISCOVERY_REQUEST:
+               ret = ieee80211_prep_tdls_encap_data(wiphy, dev, peer,
+                                                    action_code, dialog_token,
+                                                    status_code, skb);
+               send_direct = false;
+               break;
+       case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+               ret = ieee80211_prep_tdls_direct(wiphy, dev, peer, action_code,
+                                                dialog_token, status_code,
+                                                skb);
+               send_direct = true;
+               break;
+       default:
+               ret = -ENOTSUPP;
+               break;
+       }
+       if (ret < 0)
+               goto fail;
+       if (extra_ies_len)
+               memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len);
+       /* the TDLS link IE is always added last */
+       switch (action_code) {
+       case WLAN_TDLS_SETUP_REQUEST:
+       case WLAN_TDLS_SETUP_CONFIRM:
+       case WLAN_TDLS_TEARDOWN:
+       case WLAN_TDLS_DISCOVERY_REQUEST:
+               /* we are the initiator */
+               ieee80211_tdls_add_link_ie(skb, sdata->vif.addr, peer,
+                                          sdata->u.mgd.bssid);
+               break;
+       case WLAN_TDLS_SETUP_RESPONSE:
+       case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+               /* we are the responder */
+               ieee80211_tdls_add_link_ie(skb, peer, sdata->vif.addr,
+                                          sdata->u.mgd.bssid);
+               break;
+       default:
+               ret = -ENOTSUPP;
+               goto fail;
+       }
+       if (send_direct) {
+               ieee80211_tx_skb(sdata, skb);
+               return 0;
+       }
+       /*
+        * According to 802.11z: Setup req/resp are sent in AC_BK, otherwise
+        * we should default to AC_VI.
+        */
+       switch (action_code) {
+       case WLAN_TDLS_SETUP_REQUEST:
+       case WLAN_TDLS_SETUP_RESPONSE:
+               skb_set_queue_mapping(skb, IEEE80211_AC_BK);
+               skb->priority = 2;
+               break;
+       default:
+               skb_set_queue_mapping(skb, IEEE80211_AC_VI);
+               skb->priority = 5;
+               break;
+       }
+       /* disable bottom halves when entering the Tx path */
+       local_bh_disable();
+       ret = ieee80211_subif_start_xmit(skb, dev);
+       local_bh_enable();
+       return ret;
+ fail:
+       dev_kfree_skb(skb);
+       return ret;
+ }
+ static int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
+                              u8 *peer, enum nl80211_tdls_operation oper)
+ {
+       struct sta_info *sta;
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
+               return -ENOTSUPP;
+       if (sdata->vif.type != NL80211_IFTYPE_STATION)
+               return -EINVAL;
+ #ifdef CONFIG_MAC80211_VERBOSE_TDLS_DEBUG
+       printk(KERN_DEBUG "TDLS oper %d peer %pM\n", oper, peer);
+ #endif
+       switch (oper) {
+       case NL80211_TDLS_ENABLE_LINK:
+               rcu_read_lock();
+               sta = sta_info_get(sdata, peer);
+               if (!sta) {
+                       rcu_read_unlock();
+                       return -ENOLINK;
+               }
+               set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
+               rcu_read_unlock();
+               break;
+       case NL80211_TDLS_DISABLE_LINK:
+               return sta_info_destroy_addr(sdata, peer);
+       case NL80211_TDLS_TEARDOWN:
+       case NL80211_TDLS_SETUP:
+       case NL80211_TDLS_DISCOVERY_REQ:
+               /* We don't support in-driver setup/teardown/discovery */
+               return -ENOTSUPP;
+       default:
+               return -ENOTSUPP;
+       }
+       return 0;
+ }
  struct cfg80211_ops mac80211_config_ops = {
        .add_virtual_intf = ieee80211_add_iface,
        .del_virtual_intf = ieee80211_del_iface,
        .set_ringparam = ieee80211_set_ringparam,
        .get_ringparam = ieee80211_get_ringparam,
        .set_rekey_data = ieee80211_set_rekey_data,
+       .tdls_oper = ieee80211_tdls_oper,
+       .tdls_mgmt = ieee80211_tdls_mgmt,
  };
diff --combined net/mac80211/ibss.c
@@@ -84,7 -84,7 +84,7 @@@ static void __ieee80211_sta_join_ibss(s
        drv_reset_tsf(local, sdata);
  
        skb = ifibss->skb;
 -      rcu_assign_pointer(ifibss->presp, NULL);
 +      RCU_INIT_POINTER(ifibss->presp, NULL);
        synchronize_rcu();
        skb->data = skb->head;
        skb->len = 0;
                *pos++ = 0; /* U-APSD no in use */
        }
  
 -      rcu_assign_pointer(ifibss->presp, skb);
 +      RCU_INIT_POINTER(ifibss->presp, skb);
  
        sdata->vif.bss_conf.beacon_int = beacon_int;
        sdata->vif.bss_conf.basic_rates = basic_rates;
@@@ -314,7 -314,7 +314,7 @@@ static void ieee80211_rx_bss_info(struc
                }
  
                if (sta && elems->wmm_info)
-                       set_sta_flags(sta, WLAN_STA_WME);
+                       set_sta_flag(sta, WLAN_STA_WME);
  
                rcu_read_unlock();
        }
@@@ -452,7 -452,7 +452,7 @@@ struct sta_info *ieee80211_ibss_add_sta
                return NULL;
  
        sta->last_rx = jiffies;
-       set_sta_flags(sta, WLAN_STA_AUTHORIZED);
+       set_sta_flag(sta, WLAN_STA_AUTHORIZED);
  
        /* make sure mandatory rates are always added */
        sta->sta.supp_rates[band] = supp_rates |
@@@ -995,7 -995,7 +995,7 @@@ int ieee80211_ibss_leave(struct ieee802
        kfree(sdata->u.ibss.ie);
        skb = rcu_dereference_protected(sdata->u.ibss.presp,
                                        lockdep_is_held(&sdata->u.ibss.mtx));
 -      rcu_assign_pointer(sdata->u.ibss.presp, NULL);
 +      RCU_INIT_POINTER(sdata->u.ibss.presp, NULL);
        sdata->vif.bss_conf.ibss_joined = false;
        ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED |
                                                BSS_CHANGED_IBSS);
diff --combined net/mac80211/iface.c
@@@ -299,8 -299,8 +299,8 @@@ static int ieee80211_do_open(struct net
                        goto err_del_interface;
                }
  
-               /* no locking required since STA is not live yet */
-               sta->flags |= WLAN_STA_AUTHORIZED;
+               /* no atomic bitop required since STA is not live yet */
+               set_sta_flag(sta, WLAN_STA_AUTHORIZED);
  
                res = sta_info_insert(sta);
                if (res) {
@@@ -456,7 -456,7 +456,7 @@@ static void ieee80211_do_stop(struct ie
                                                 BSS_CHANGED_BEACON_ENABLED);
  
                /* remove beacon */
 -              rcu_assign_pointer(sdata->u.ap.beacon, NULL);
 +              RCU_INIT_POINTER(sdata->u.ap.beacon, NULL);
                synchronize_rcu();
                kfree(old_beacon);
  
@@@ -643,7 -643,7 +643,7 @@@ static const struct net_device_ops ieee
        .ndo_stop               = ieee80211_stop,
        .ndo_uninit             = ieee80211_teardown_sdata,
        .ndo_start_xmit         = ieee80211_subif_start_xmit,
 -      .ndo_set_multicast_list = ieee80211_set_multicast_list,
 +      .ndo_set_rx_mode        = ieee80211_set_multicast_list,
        .ndo_change_mtu         = ieee80211_change_mtu,
        .ndo_set_mac_address    = ieee80211_change_mac,
        .ndo_select_queue       = ieee80211_netdev_select_queue,
@@@ -687,7 -687,7 +687,7 @@@ static const struct net_device_ops ieee
        .ndo_stop               = ieee80211_stop,
        .ndo_uninit             = ieee80211_teardown_sdata,
        .ndo_start_xmit         = ieee80211_monitor_start_xmit,
 -      .ndo_set_multicast_list = ieee80211_set_multicast_list,
 +      .ndo_set_rx_mode        = ieee80211_set_multicast_list,
        .ndo_change_mtu         = ieee80211_change_mtu,
        .ndo_set_mac_address    = eth_mac_addr,
        .ndo_select_queue       = ieee80211_monitor_select_queue,
diff --combined net/mac80211/sta_info.c
@@@ -24,6 -24,7 +24,7 @@@
  #include "sta_info.h"
  #include "debugfs_sta.h"
  #include "mesh.h"
+ #include "wme.h"
  
  /**
   * DOC: STA information lifetime rules
@@@ -72,7 -73,7 +73,7 @@@ static int sta_info_hash_del(struct iee
        if (!s)
                return -ENOENT;
        if (s == sta) {
 -              rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)],
 +              RCU_INIT_POINTER(local->sta_hash[STA_HASH(sta->sta.addr)],
                                   s->hnext);
                return 0;
        }
@@@ -82,7 -83,7 +83,7 @@@
                s = rcu_dereference_protected(s->hnext,
                                        lockdep_is_held(&local->sta_lock));
        if (rcu_access_pointer(s->hnext)) {
 -              rcu_assign_pointer(s->hnext, sta->hnext);
 +              RCU_INIT_POINTER(s->hnext, sta->hnext);
                return 0;
        }
  
@@@ -231,7 -232,7 +232,7 @@@ static void sta_info_hash_add(struct ie
                              struct sta_info *sta)
  {
        sta->hnext = local->sta_hash[STA_HASH(sta->sta.addr)];
 -      rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)], sta);
 +      RCU_INIT_POINTER(local->sta_hash[STA_HASH(sta->sta.addr)], sta);
  }
  
  static void sta_unblock(struct work_struct *wk)
        if (sta->dead)
                return;
  
-       if (!test_sta_flags(sta, WLAN_STA_PS_STA))
+       if (!test_sta_flag(sta, WLAN_STA_PS_STA))
                ieee80211_sta_ps_deliver_wakeup(sta);
-       else if (test_and_clear_sta_flags(sta, WLAN_STA_PSPOLL)) {
-               clear_sta_flags(sta, WLAN_STA_PS_DRIVER);
+       else if (test_and_clear_sta_flag(sta, WLAN_STA_PSPOLL)) {
+               clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
+               local_bh_disable();
                ieee80211_sta_ps_deliver_poll_response(sta);
+               local_bh_enable();
+       } else if (test_and_clear_sta_flag(sta, WLAN_STA_UAPSD)) {
+               clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
+               local_bh_disable();
+               ieee80211_sta_ps_deliver_uapsd(sta);
+               local_bh_enable();
        } else
-               clear_sta_flags(sta, WLAN_STA_PS_DRIVER);
+               clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
  }
  
  static int sta_prepare_rate_control(struct ieee80211_local *local,
@@@ -282,7 -292,6 +292,6 @@@ struct sta_info *sta_info_alloc(struct 
                return NULL;
  
        spin_lock_init(&sta->lock);
-       spin_lock_init(&sta->flaglock);
        INIT_WORK(&sta->drv_unblock_wk, sta_unblock);
        INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
        mutex_init(&sta->ampdu_mlme.mtx);
                 */
                sta->timer_to_tid[i] = i;
        }
-       skb_queue_head_init(&sta->ps_tx_buf);
-       skb_queue_head_init(&sta->tx_filtered);
+       for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+               skb_queue_head_init(&sta->ps_tx_buf[i]);
+               skb_queue_head_init(&sta->tx_filtered[i]);
+       }
  
        for (i = 0; i < NUM_RX_DATA_QUEUES; i++)
                sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX);
@@@ -641,54 -652,84 +652,84 @@@ static inline void __bss_tim_clear(stru
        bss->tim[aid / 8] &= ~(1 << (aid % 8));
  }
  
- static void __sta_info_set_tim_bit(struct ieee80211_if_ap *bss,
-                                  struct sta_info *sta)
+ static unsigned long ieee80211_tids_for_ac(int ac)
  {
-       BUG_ON(!bss);
-       __bss_tim_set(bss, sta->sta.aid);
-       if (sta->local->ops->set_tim) {
-               sta->local->tim_in_locked_section = true;
-               drv_set_tim(sta->local, &sta->sta, true);
-               sta->local->tim_in_locked_section = false;
+       /* If we ever support TIDs > 7, this obviously needs to be adjusted */
+       switch (ac) {
+       case IEEE80211_AC_VO:
+               return BIT(6) | BIT(7);
+       case IEEE80211_AC_VI:
+               return BIT(4) | BIT(5);
+       case IEEE80211_AC_BE:
+               return BIT(0) | BIT(3);
+       case IEEE80211_AC_BK:
+               return BIT(1) | BIT(2);
+       default:
+               WARN_ON(1);
+               return 0;
        }
  }
  
- void sta_info_set_tim_bit(struct sta_info *sta)
+ void sta_info_recalc_tim(struct sta_info *sta)
  {
+       struct ieee80211_local *local = sta->local;
+       struct ieee80211_if_ap *bss = sta->sdata->bss;
        unsigned long flags;
+       bool indicate_tim = false;
+       u8 ignore_for_tim = sta->sta.uapsd_queues;
+       int ac;
  
-       BUG_ON(!sta->sdata->bss);
+       if (WARN_ON_ONCE(!sta->sdata->bss))
+               return;
  
-       spin_lock_irqsave(&sta->local->sta_lock, flags);
-       __sta_info_set_tim_bit(sta->sdata->bss, sta);
-       spin_unlock_irqrestore(&sta->local->sta_lock, flags);
- }
+       /* No need to do anything if the driver does all */
+       if (local->hw.flags & IEEE80211_HW_AP_LINK_PS)
+               return;
  
- static void __sta_info_clear_tim_bit(struct ieee80211_if_ap *bss,
-                                    struct sta_info *sta)
- {
-       BUG_ON(!bss);
+       if (sta->dead)
+               goto done;
+       /*
+        * If all ACs are delivery-enabled then we should build
+        * the TIM bit for all ACs anyway; if only some are then
+        * we ignore those and build the TIM bit using only the
+        * non-enabled ones.
+        */
+       if (ignore_for_tim == BIT(IEEE80211_NUM_ACS) - 1)
+               ignore_for_tim = 0;
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+               unsigned long tids;
  
-       __bss_tim_clear(bss, sta->sta.aid);
+               if (ignore_for_tim & BIT(ac))
+                       continue;
+               indicate_tim |= !skb_queue_empty(&sta->tx_filtered[ac]) ||
+                               !skb_queue_empty(&sta->ps_tx_buf[ac]);
+               if (indicate_tim)
+                       break;
  
-       if (sta->local->ops->set_tim) {
-               sta->local->tim_in_locked_section = true;
-               drv_set_tim(sta->local, &sta->sta, false);
-               sta->local->tim_in_locked_section = false;
+               tids = ieee80211_tids_for_ac(ac);
+               indicate_tim |=
+                       sta->driver_buffered_tids & tids;
        }
- }
  
- void sta_info_clear_tim_bit(struct sta_info *sta)
- {
-       unsigned long flags;
+  done:
+       spin_lock_irqsave(&local->sta_lock, flags);
  
-       BUG_ON(!sta->sdata->bss);
+       if (indicate_tim)
+               __bss_tim_set(bss, sta->sta.aid);
+       else
+               __bss_tim_clear(bss, sta->sta.aid);
+       if (local->ops->set_tim) {
+               local->tim_in_locked_section = true;
+               drv_set_tim(local, &sta->sta, indicate_tim);
+               local->tim_in_locked_section = false;
+       }
  
-       spin_lock_irqsave(&sta->local->sta_lock, flags);
-       __sta_info_clear_tim_bit(sta->sdata->bss, sta);
-       spin_unlock_irqrestore(&sta->local->sta_lock, flags);
+       spin_unlock_irqrestore(&local->sta_lock, flags);
  }
  
  static bool sta_info_buffer_expired(struct sta_info *sta, struct sk_buff *skb)
  }
  
  
- static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
-                                            struct sta_info *sta)
+ static bool sta_info_cleanup_expire_buffered_ac(struct ieee80211_local *local,
+                                               struct sta_info *sta, int ac)
  {
        unsigned long flags;
        struct sk_buff *skb;
  
+       /*
+        * First check for frames that should expire on the filtered
+        * queue. Frames here were rejected by the driver and are on
+        * a separate queue to avoid reordering with normal PS-buffered
+        * frames. They also aren't accounted for right now in the
+        * total_ps_buffered counter.
+        */
+       for (;;) {
+               spin_lock_irqsave(&sta->tx_filtered[ac].lock, flags);
+               skb = skb_peek(&sta->tx_filtered[ac]);
+               if (sta_info_buffer_expired(sta, skb))
+                       skb = __skb_dequeue(&sta->tx_filtered[ac]);
+               else
+                       skb = NULL;
+               spin_unlock_irqrestore(&sta->tx_filtered[ac].lock, flags);
+               /*
+                * Frames are queued in order, so if this one
+                * hasn't expired yet we can stop testing. If
+                * we actually reached the end of the queue we
+                * also need to stop, of course.
+                */
+               if (!skb)
+                       break;
+               dev_kfree_skb(skb);
+       }
+       /*
+        * Now also check the normal PS-buffered queue, this will
+        * only find something if the filtered queue was emptied
+        * since the filtered frames are all before the normal PS
+        * buffered frames.
+        */
        for (;;) {
-               spin_lock_irqsave(&sta->ps_tx_buf.lock, flags);
-               skb = skb_peek(&sta->ps_tx_buf);
+               spin_lock_irqsave(&sta->ps_tx_buf[ac].lock, flags);
+               skb = skb_peek(&sta->ps_tx_buf[ac]);
                if (sta_info_buffer_expired(sta, skb))
-                       skb = __skb_dequeue(&sta->ps_tx_buf);
+                       skb = __skb_dequeue(&sta->ps_tx_buf[ac]);
                else
                        skb = NULL;
-               spin_unlock_irqrestore(&sta->ps_tx_buf.lock, flags);
+               spin_unlock_irqrestore(&sta->ps_tx_buf[ac].lock, flags);
  
+               /*
+                * frames are queued in order, so if this one
+                * hasn't expired yet (or we reached the end of
+                * the queue) we can stop testing
+                */
                if (!skb)
                        break;
  
                       sta->sta.addr);
  #endif
                dev_kfree_skb(skb);
-               if (skb_queue_empty(&sta->ps_tx_buf) &&
-                   !test_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF))
-                       sta_info_clear_tim_bit(sta);
        }
  
-       return !skb_queue_empty(&sta->ps_tx_buf);
+       /*
+        * Finally, recalculate the TIM bit for this station -- it might
+        * now be clear because the station was too slow to retrieve its
+        * frames.
+        */
+       sta_info_recalc_tim(sta);
+       /*
+        * Return whether there are any frames still buffered, this is
+        * used to check whether the cleanup timer still needs to run,
+        * if there are no frames we don't need to rearm the timer.
+        */
+       return !(skb_queue_empty(&sta->ps_tx_buf[ac]) &&
+                skb_queue_empty(&sta->tx_filtered[ac]));
+ }
+ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
+                                            struct sta_info *sta)
+ {
+       bool have_buffered = false;
+       int ac;
+       /* This is only necessary for stations on BSS interfaces */
+       if (!sta->sdata->bss)
+               return false;
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+               have_buffered |=
+                       sta_info_cleanup_expire_buffered_ac(local, sta, ac);
+       return have_buffered;
  }
  
  static int __must_check __sta_info_destroy(struct sta_info *sta)
  {
        struct ieee80211_local *local;
        struct ieee80211_sub_if_data *sdata;
-       struct sk_buff *skb;
        unsigned long flags;
-       int ret, i;
+       int ret, i, ac;
  
        might_sleep();
  
         * sessions -- block that to make sure the tear-down
         * will be sufficient.
         */
-       set_sta_flags(sta, WLAN_STA_BLOCK_BA);
+       set_sta_flag(sta, WLAN_STA_BLOCK_BA);
        ieee80211_sta_tear_down_BA_sessions(sta, true);
  
        spin_lock_irqsave(&local->sta_lock, flags);
  
        sta->dead = true;
  
-       if (test_and_clear_sta_flags(sta,
-                               WLAN_STA_PS_STA | WLAN_STA_PS_DRIVER)) {
+       if (test_sta_flag(sta, WLAN_STA_PS_STA) ||
+           test_sta_flag(sta, WLAN_STA_PS_DRIVER)) {
                BUG_ON(!sdata->bss);
  
+               clear_sta_flag(sta, WLAN_STA_PS_STA);
+               clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
                atomic_dec(&sdata->bss->num_sta_ps);
-               sta_info_clear_tim_bit(sta);
+               sta_info_recalc_tim(sta);
        }
  
        local->num_sta--;
        local->sta_generation++;
  
        if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
 -              rcu_assign_pointer(sdata->u.vlan.sta, NULL);
 +              RCU_INIT_POINTER(sdata->u.vlan.sta, NULL);
  
        if (sta->uploaded) {
                if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
         */
        synchronize_rcu();
  
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+               local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]);
+               __skb_queue_purge(&sta->ps_tx_buf[ac]);
+               __skb_queue_purge(&sta->tx_filtered[ac]);
+       }
  #ifdef CONFIG_MAC80211_MESH
        if (ieee80211_vif_is_mesh(&sdata->vif))
                mesh_accept_plinks_update(sdata);
        }
  #endif
  
-       while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) {
-               local->total_ps_buffered--;
-               dev_kfree_skb_any(skb);
-       }
-       while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL)
-               dev_kfree_skb_any(skb);
        __sta_info_free(local, sta);
  
        return 0;
@@@ -1013,7 -1118,8 +1118,8 @@@ static void clear_sta_ps_flags(void *_s
  {
        struct sta_info *sta = _sta;
  
-       clear_sta_flags(sta, WLAN_STA_PS_DRIVER | WLAN_STA_PS_STA);
+       clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
+       clear_sta_flag(sta, WLAN_STA_PS_STA);
  }
  
  /* powersave support code */
@@@ -1021,88 -1127,343 +1127,343 @@@ void ieee80211_sta_ps_deliver_wakeup(st
  {
        struct ieee80211_sub_if_data *sdata = sta->sdata;
        struct ieee80211_local *local = sdata->local;
-       int sent, buffered;
+       struct sk_buff_head pending;
+       int filtered = 0, buffered = 0, ac;
+       clear_sta_flag(sta, WLAN_STA_SP);
+       BUILD_BUG_ON(BITS_TO_LONGS(STA_TID_NUM) > 1);
+       sta->driver_buffered_tids = 0;
  
-       clear_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF);
        if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS))
                drv_sta_notify(local, sdata, STA_NOTIFY_AWAKE, &sta->sta);
  
-       if (!skb_queue_empty(&sta->ps_tx_buf))
-               sta_info_clear_tim_bit(sta);
+       skb_queue_head_init(&pending);
  
        /* Send all buffered frames to the station */
-       sent = ieee80211_add_pending_skbs(local, &sta->tx_filtered);
-       buffered = ieee80211_add_pending_skbs_fn(local, &sta->ps_tx_buf,
-                                                clear_sta_ps_flags, sta);
-       sent += buffered;
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+               int count = skb_queue_len(&pending), tmp;
+               skb_queue_splice_tail_init(&sta->tx_filtered[ac], &pending);
+               tmp = skb_queue_len(&pending);
+               filtered += tmp - count;
+               count = tmp;
+               skb_queue_splice_tail_init(&sta->ps_tx_buf[ac], &pending);
+               tmp = skb_queue_len(&pending);
+               buffered += tmp - count;
+       }
+       ieee80211_add_pending_skbs_fn(local, &pending, clear_sta_ps_flags, sta);
        local->total_ps_buffered -= buffered;
  
+       sta_info_recalc_tim(sta);
  #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
        printk(KERN_DEBUG "%s: STA %pM aid %d sending %d filtered/%d PS frames "
               "since STA not sleeping anymore\n", sdata->name,
-              sta->sta.addr, sta->sta.aid, sent - buffered, buffered);
+              sta->sta.addr, sta->sta.aid, filtered, buffered);
  #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
  }
  
- void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta)
+ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
+                                        struct sta_info *sta, int tid,
+                                        enum ieee80211_frame_release_type reason)
  {
-       struct ieee80211_sub_if_data *sdata = sta->sdata;
        struct ieee80211_local *local = sdata->local;
+       struct ieee80211_qos_hdr *nullfunc;
        struct sk_buff *skb;
-       int no_pending_pkts;
+       int size = sizeof(*nullfunc);
+       __le16 fc;
+       bool qos = test_sta_flag(sta, WLAN_STA_WME);
+       struct ieee80211_tx_info *info;
  
-       skb = skb_dequeue(&sta->tx_filtered);
-       if (!skb) {
-               skb = skb_dequeue(&sta->ps_tx_buf);
-               if (skb)
-                       local->total_ps_buffered--;
+       if (qos) {
+               fc = cpu_to_le16(IEEE80211_FTYPE_DATA |
+                                IEEE80211_STYPE_QOS_NULLFUNC |
+                                IEEE80211_FCTL_FROMDS);
+       } else {
+               size -= 2;
+               fc = cpu_to_le16(IEEE80211_FTYPE_DATA |
+                                IEEE80211_STYPE_NULLFUNC |
+                                IEEE80211_FCTL_FROMDS);
        }
-       no_pending_pkts = skb_queue_empty(&sta->tx_filtered) &&
-               skb_queue_empty(&sta->ps_tx_buf);
  
-       if (skb) {
-               struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-               struct ieee80211_hdr *hdr =
-                       (struct ieee80211_hdr *) skb->data;
+       skb = dev_alloc_skb(local->hw.extra_tx_headroom + size);
+       if (!skb)
+               return;
+       skb_reserve(skb, local->hw.extra_tx_headroom);
+       nullfunc = (void *) skb_put(skb, size);
+       nullfunc->frame_control = fc;
+       nullfunc->duration_id = 0;
+       memcpy(nullfunc->addr1, sta->sta.addr, ETH_ALEN);
+       memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN);
+       memcpy(nullfunc->addr3, sdata->vif.addr, ETH_ALEN);
+       if (qos) {
+               skb->priority = tid;
+               skb_set_queue_mapping(skb, ieee802_1d_to_ac[tid]);
+               nullfunc->qos_ctrl = cpu_to_le16(tid);
+               if (reason == IEEE80211_FRAME_RELEASE_UAPSD)
+                       nullfunc->qos_ctrl |=
+                               cpu_to_le16(IEEE80211_QOS_CTL_EOSP);
+       }
+       info = IEEE80211_SKB_CB(skb);
+       /*
+        * Tell TX path to send this frame even though the
+        * STA may still remain is PS mode after this frame
+        * exchange. Also set EOSP to indicate this packet
+        * ends the poll/service period.
+        */
+       info->flags |= IEEE80211_TX_CTL_POLL_RESPONSE |
+                      IEEE80211_TX_STATUS_EOSP |
+                      IEEE80211_TX_CTL_REQ_TX_STATUS;
+       drv_allow_buffered_frames(local, sta, BIT(tid), 1, reason, false);
+       ieee80211_xmit(sdata, skb);
+ }
+ static void
+ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
+                                 int n_frames, u8 ignored_acs,
+                                 enum ieee80211_frame_release_type reason)
+ {
+       struct ieee80211_sub_if_data *sdata = sta->sdata;
+       struct ieee80211_local *local = sdata->local;
+       bool found = false;
+       bool more_data = false;
+       int ac;
+       unsigned long driver_release_tids = 0;
+       struct sk_buff_head frames;
+       /* Service or PS-Poll period starts */
+       set_sta_flag(sta, WLAN_STA_SP);
+       __skb_queue_head_init(&frames);
+       /*
+        * Get response frame(s) and more data bit for it.
+        */
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+               unsigned long tids;
+               if (ignored_acs & BIT(ac))
+                       continue;
+               tids = ieee80211_tids_for_ac(ac);
+               if (!found) {
+                       driver_release_tids = sta->driver_buffered_tids & tids;
+                       if (driver_release_tids) {
+                               found = true;
+                       } else {
+                               struct sk_buff *skb;
+                               while (n_frames > 0) {
+                                       skb = skb_dequeue(&sta->tx_filtered[ac]);
+                                       if (!skb) {
+                                               skb = skb_dequeue(
+                                                       &sta->ps_tx_buf[ac]);
+                                               if (skb)
+                                                       local->total_ps_buffered--;
+                                       }
+                                       if (!skb)
+                                               break;
+                                       n_frames--;
+                                       found = true;
+                                       __skb_queue_tail(&frames, skb);
+                               }
+                       }
+                       /*
+                        * If the driver has data on more than one TID then
+                        * certainly there's more data if we release just a
+                        * single frame now (from a single TID).
+                        */
+                       if (reason == IEEE80211_FRAME_RELEASE_PSPOLL &&
+                           hweight16(driver_release_tids) > 1) {
+                               more_data = true;
+                               driver_release_tids =
+                                       BIT(ffs(driver_release_tids) - 1);
+                               break;
+                       }
+               }
+               if (!skb_queue_empty(&sta->tx_filtered[ac]) ||
+                   !skb_queue_empty(&sta->ps_tx_buf[ac])) {
+                       more_data = true;
+                       break;
+               }
+       }
+       if (!found) {
+               int tid;
  
                /*
-                * Tell TX path to send this frame even though the STA may
-                * still remain is PS mode after this frame exchange.
+                * For PS-Poll, this can only happen due to a race condition
+                * when we set the TIM bit and the station notices it, but
+                * before it can poll for the frame we expire it.
+                *
+                * For uAPSD, this is said in the standard (11.2.1.5 h):
+                *      At each unscheduled SP for a non-AP STA, the AP shall
+                *      attempt to transmit at least one MSDU or MMPDU, but no
+                *      more than the value specified in the Max SP Length field
+                *      in the QoS Capability element from delivery-enabled ACs,
+                *      that are destined for the non-AP STA.
+                *
+                * Since we have no other MSDU/MMPDU, transmit a QoS null frame.
                 */
-               info->flags |= IEEE80211_TX_CTL_PSPOLL_RESPONSE;
  
- #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
-               printk(KERN_DEBUG "STA %pM aid %d: PS Poll (entries after %d)\n",
-                      sta->sta.addr, sta->sta.aid,
-                      skb_queue_len(&sta->ps_tx_buf));
- #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
+               /* This will evaluate to 1, 3, 5 or 7. */
+               tid = 7 - ((ffs(~ignored_acs) - 1) << 1);
  
-               /* Use MoreData flag to indicate whether there are more
-                * buffered frames for this STA */
-               if (no_pending_pkts)
-                       hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREDATA);
-               else
-                       hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+               ieee80211_send_null_response(sdata, sta, tid, reason);
+               return;
+       }
  
-               ieee80211_add_pending_skb(local, skb);
+       if (!driver_release_tids) {
+               struct sk_buff_head pending;
+               struct sk_buff *skb;
+               int num = 0;
+               u16 tids = 0;
+               skb_queue_head_init(&pending);
+               while ((skb = __skb_dequeue(&frames))) {
+                       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+                       struct ieee80211_hdr *hdr = (void *) skb->data;
+                       u8 *qoshdr = NULL;
+                       num++;
+                       /*
+                        * Tell TX path to send this frame even though the
+                        * STA may still remain is PS mode after this frame
+                        * exchange.
+                        */
+                       info->flags |= IEEE80211_TX_CTL_POLL_RESPONSE;
+                       /*
+                        * Use MoreData flag to indicate whether there are
+                        * more buffered frames for this STA
+                        */
+                       if (!more_data)
+                               hdr->frame_control &=
+                                       cpu_to_le16(~IEEE80211_FCTL_MOREDATA);
+                       else
+                               hdr->frame_control |=
+                                       cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+                       if (ieee80211_is_data_qos(hdr->frame_control) ||
+                           ieee80211_is_qos_nullfunc(hdr->frame_control))
+                               qoshdr = ieee80211_get_qos_ctl(hdr);
+                       /* set EOSP for the frame */
+                       if (reason == IEEE80211_FRAME_RELEASE_UAPSD &&
+                           qoshdr && skb_queue_empty(&frames))
+                               *qoshdr |= IEEE80211_QOS_CTL_EOSP;
+                       info->flags |= IEEE80211_TX_STATUS_EOSP |
+                                      IEEE80211_TX_CTL_REQ_TX_STATUS;
+                       if (qoshdr)
+                               tids |= BIT(*qoshdr & IEEE80211_QOS_CTL_TID_MASK);
+                       else
+                               tids |= BIT(0);
+                       __skb_queue_tail(&pending, skb);
+               }
  
-               if (no_pending_pkts)
-                       sta_info_clear_tim_bit(sta);
- #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
+               drv_allow_buffered_frames(local, sta, tids, num,
+                                         reason, more_data);
+               ieee80211_add_pending_skbs(local, &pending);
+               sta_info_recalc_tim(sta);
        } else {
                /*
-                * FIXME: This can be the result of a race condition between
-                *        us expiring a frame and the station polling for it.
-                *        Should we send it a null-func frame indicating we
-                *        have nothing buffered for it?
+                * We need to release a frame that is buffered somewhere in the
+                * driver ... it'll have to handle that.
+                * Note that, as per the comment above, it'll also have to see
+                * if there is more than just one frame on the specific TID that
+                * we're releasing from, and it needs to set the more-data bit
+                * accordingly if we tell it that there's no more data. If we do
+                * tell it there's more data, then of course the more-data bit
+                * needs to be set anyway.
+                */
+               drv_release_buffered_frames(local, sta, driver_release_tids,
+                                           n_frames, reason, more_data);
+               /*
+                * Note that we don't recalculate the TIM bit here as it would
+                * most likely have no effect at all unless the driver told us
+                * that the TID became empty before returning here from the
+                * release function.
+                * Either way, however, when the driver tells us that the TID
+                * became empty we'll do the TIM recalculation.
                 */
-               printk(KERN_DEBUG "%s: STA %pM sent PS Poll even "
-                      "though there are no buffered frames for it\n",
-                      sdata->name, sta->sta.addr);
- #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
        }
  }
  
+ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta)
+ {
+       u8 ignore_for_response = sta->sta.uapsd_queues;
+       /*
+        * If all ACs are delivery-enabled then we should reply
+        * from any of them, if only some are enabled we reply
+        * only from the non-enabled ones.
+        */
+       if (ignore_for_response == BIT(IEEE80211_NUM_ACS) - 1)
+               ignore_for_response = 0;
+       ieee80211_sta_ps_deliver_response(sta, 1, ignore_for_response,
+                                         IEEE80211_FRAME_RELEASE_PSPOLL);
+ }
+ void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta)
+ {
+       int n_frames = sta->sta.max_sp;
+       u8 delivery_enabled = sta->sta.uapsd_queues;
+       /*
+        * If we ever grow support for TSPEC this might happen if
+        * the TSPEC update from hostapd comes in between a trigger
+        * frame setting WLAN_STA_UAPSD in the RX path and this
+        * actually getting called.
+        */
+       if (!delivery_enabled)
+               return;
+       switch (sta->sta.max_sp) {
+       case 1:
+               n_frames = 2;
+               break;
+       case 2:
+               n_frames = 4;
+               break;
+       case 3:
+               n_frames = 6;
+               break;
+       case 0:
+               /* XXX: what is a good value? */
+               n_frames = 8;
+               break;
+       }
+       ieee80211_sta_ps_deliver_response(sta, n_frames, ~delivery_enabled,
+                                         IEEE80211_FRAME_RELEASE_UAPSD);
+ }
  void ieee80211_sta_block_awake(struct ieee80211_hw *hw,
                               struct ieee80211_sta *pubsta, bool block)
  {
        trace_api_sta_block_awake(sta->local, pubsta, block);
  
        if (block)
-               set_sta_flags(sta, WLAN_STA_PS_DRIVER);
-       else if (test_sta_flags(sta, WLAN_STA_PS_DRIVER))
+               set_sta_flag(sta, WLAN_STA_PS_DRIVER);
+       else if (test_sta_flag(sta, WLAN_STA_PS_DRIVER))
                ieee80211_queue_work(hw, &sta->drv_unblock_wk);
  }
  EXPORT_SYMBOL(ieee80211_sta_block_awake);
  
- void ieee80211_sta_set_tim(struct ieee80211_sta *pubsta)
+ void ieee80211_sta_eosp_irqsafe(struct ieee80211_sta *pubsta)
+ {
+       struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
+       struct ieee80211_local *local = sta->local;
+       struct sk_buff *skb;
+       struct skb_eosp_msg_data *data;
+       trace_api_eosp(local, pubsta);
+       skb = alloc_skb(0, GFP_ATOMIC);
+       if (!skb) {
+               /* too bad ... but race is better than loss */
+               clear_sta_flag(sta, WLAN_STA_SP);
+               return;
+       }
+       data = (void *)skb->cb;
+       memcpy(data->sta, pubsta->addr, ETH_ALEN);
+       memcpy(data->iface, sta->sdata->vif.addr, ETH_ALEN);
+       skb->pkt_type = IEEE80211_EOSP_MSG;
+       skb_queue_tail(&local->skb_queue, skb);
+       tasklet_schedule(&local->tasklet);
+ }
+ EXPORT_SYMBOL(ieee80211_sta_eosp_irqsafe);
+ void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta,
+                               u8 tid, bool buffered)
  {
        struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
  
-       set_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF);
-       sta_info_set_tim_bit(sta);
+       if (WARN_ON(tid >= STA_TID_NUM))
+               return;
+       if (buffered)
+               set_bit(tid, &sta->driver_buffered_tids);
+       else
+               clear_bit(tid, &sta->driver_buffered_tids);
+       sta_info_recalc_tim(sta);
  }
- EXPORT_SYMBOL(ieee80211_sta_set_tim);
+ EXPORT_SYMBOL(ieee80211_sta_set_buffered);