iwlagn: add support for waiting for notifications

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>
This commit is contained in:
Johannes Berg 2011-01-04 16:22:00 -08:00 committed by John W. Linville
parent f945f1087f
commit 7194207cee
4 changed files with 115 additions and 0 deletions

View 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);
}

View 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() */

View 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);

View 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
};