iwlagn: add support for waiting for notifications
authorJohannes Berg <johannes.berg@intel.com>
Wed, 5 Jan 2011 00:22:00 +0000 (16:22 -0800)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 21 Jan 2011 20:32:20 +0000 (15:32 -0500)
In order to implement waiting for notifications,
add a structure that captures the information,
and a list of such structures that will be
traversed when a command is received from the
ucode.

Use sparse checking to make sure calls to the
prepare/wait/cancel functions are always nested
correctly.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/iwlwifi/iwl-agn-lib.c
drivers/net/wireless/iwlwifi/iwl-agn.c
drivers/net/wireless/iwlwifi/iwl-agn.h
drivers/net/wireless/iwlwifi/iwl-dev.h

index 3dee87e..29dcda0 100644 (file)
@@ -473,6 +473,11 @@ void iwlagn_rx_handler_setup(struct iwl_priv *priv)
        priv->rx_handlers[CALIBRATION_COMPLETE_NOTIFICATION] =
                                        iwlagn_rx_calib_complete;
        priv->rx_handlers[REPLY_TX] = iwlagn_rx_reply_tx;
+
+       /* set up notification wait support */
+       spin_lock_init(&priv->_agn.notif_wait_lock);
+       INIT_LIST_HEAD(&priv->_agn.notif_waits);
+       init_waitqueue_head(&priv->_agn.notif_waitq);
 }
 
 void iwlagn_setup_deferred_work(struct iwl_priv *priv)
@@ -2389,3 +2394,44 @@ int iwl_dump_fh(struct iwl_priv *priv, char **buf, bool display)
        }
        return 0;
 }
+
+/* notification wait support */
+void iwlagn_init_notification_wait(struct iwl_priv *priv,
+                                  struct iwl_notification_wait *wait_entry,
+                                  void (*fn)(struct iwl_priv *priv,
+                                             struct iwl_rx_packet *pkt),
+                                  u8 cmd)
+{
+       wait_entry->fn = fn;
+       wait_entry->cmd = cmd;
+       wait_entry->triggered = false;
+
+       spin_lock_bh(&priv->_agn.notif_wait_lock);
+       list_add(&wait_entry->list, &priv->_agn.notif_waits);
+       spin_unlock_bh(&priv->_agn.notif_wait_lock);
+}
+
+signed long iwlagn_wait_notification(struct iwl_priv *priv,
+                                    struct iwl_notification_wait *wait_entry,
+                                    unsigned long timeout)
+{
+       int ret;
+
+       ret = wait_event_timeout(priv->_agn.notif_waitq,
+                                &wait_entry->triggered,
+                                timeout);
+
+       spin_lock_bh(&priv->_agn.notif_wait_lock);
+       list_del(&wait_entry->list);
+       spin_unlock_bh(&priv->_agn.notif_wait_lock);
+
+       return ret;
+}
+
+void iwlagn_remove_notification(struct iwl_priv *priv,
+                               struct iwl_notification_wait *wait_entry)
+{
+       spin_lock_bh(&priv->_agn.notif_wait_lock);
+       list_del(&wait_entry->list);
+       spin_unlock_bh(&priv->_agn.notif_wait_lock);
+}
index 624f174..97657d0 100644 (file)
@@ -910,6 +910,27 @@ static void iwl_rx_handle(struct iwl_priv *priv)
                        (pkt->hdr.cmd != STATISTICS_NOTIFICATION) &&
                        (pkt->hdr.cmd != REPLY_TX);
 
+               /*
+                * Do the notification wait before RX handlers so
+                * even if the RX handler consumes the RXB we have
+                * access to it in the notification wait entry.
+                */
+               if (!list_empty(&priv->_agn.notif_waits)) {
+                       struct iwl_notification_wait *w;
+
+                       spin_lock(&priv->_agn.notif_wait_lock);
+                       list_for_each_entry(w, &priv->_agn.notif_waits, list) {
+                               if (w->cmd == pkt->hdr.cmd) {
+                                       w->triggered = true;
+                                       if (w->fn)
+                                               w->fn(priv, pkt);
+                               }
+                       }
+                       spin_unlock(&priv->_agn.notif_wait_lock);
+
+                       wake_up_all(&priv->_agn.notif_waitq);
+               }
+
                /* Based on type of command response or notification,
                 *   handle those that need handling via function in
                 *   rx_handlers table.  See iwl_setup_rx_handlers() */
index c30dc4f..74d72ff 100644 (file)
@@ -329,6 +329,21 @@ void iwl_eeprom_get_mac(const struct iwl_priv *priv, u8 *mac);
 int iwlcore_eeprom_acquire_semaphore(struct iwl_priv *priv);
 void iwlcore_eeprom_release_semaphore(struct iwl_priv *priv);
 
+/* notification wait support */
+void __acquires(wait_entry)
+iwlagn_init_notification_wait(struct iwl_priv *priv,
+                             struct iwl_notification_wait *wait_entry,
+                             void (*fn)(struct iwl_priv *priv,
+                                        struct iwl_rx_packet *pkt),
+                             u8 cmd);
+signed long __releases(wait_entry)
+iwlagn_wait_notification(struct iwl_priv *priv,
+                        struct iwl_notification_wait *wait_entry,
+                        unsigned long timeout);
+void __releases(wait_entry)
+iwlagn_remove_notification(struct iwl_priv *priv,
+                          struct iwl_notification_wait *wait_entry);
+
 /* mac80211 handlers (for 4965) */
 int iwlagn_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb);
 int iwlagn_mac_start(struct ieee80211_hw *hw);
index 8dda678..2ec680b 100644 (file)
@@ -34,6 +34,7 @@
 
 #include <linux/pci.h> /* for struct pci_device_id */
 #include <linux/kernel.h>
+#include <linux/wait.h>
 #include <net/ieee80211_radiotap.h>
 
 #include "iwl-eeprom.h"
@@ -1139,6 +1140,33 @@ struct iwl_force_reset {
  */
 #define IWLAGN_EXT_BEACON_TIME_POS     22
 
+/**
+ * struct iwl_notification_wait - notification wait entry
+ * @list: list head for global list
+ * @fn: function called with the notification
+ * @cmd: command ID
+ *
+ * This structure is not used directly, to wait for a
+ * notification declare it on the stack, and call
+ * iwlagn_init_notification_wait() with appropriate
+ * parameters. Then do whatever will cause the ucode
+ * to notify the driver, and to wait for that then
+ * call iwlagn_wait_notification().
+ *
+ * Each notification is one-shot. If at some point we
+ * need to support multi-shot notifications (which
+ * can't be allocated on the stack) we need to modify
+ * the code for them.
+ */
+struct iwl_notification_wait {
+       struct list_head list;
+
+       void (*fn)(struct iwl_priv *priv, struct iwl_rx_packet *pkt);
+
+       u8 cmd;
+       bool triggered;
+};
+
 enum iwl_rxon_context_id {
        IWL_RXON_CTX_BSS,
        IWL_RXON_CTX_PAN,
@@ -1463,6 +1491,11 @@ struct iwl_priv {
                        struct iwl_bt_notif_statistics delta_statistics_bt;
                        struct iwl_bt_notif_statistics max_delta_bt;
 #endif
+
+                       /* notification wait support */
+                       struct list_head notif_waits;
+                       spinlock_t notif_wait_lock;
+                       wait_queue_head_t notif_waitq;
                } _agn;
 #endif
        };