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:
committed by
John W. Linville
parent
f945f1087f
commit
7194207cee
@@ -473,6 +473,11 @@ void iwlagn_rx_handler_setup(struct iwl_priv *priv)
|
|||||||
priv->rx_handlers[CALIBRATION_COMPLETE_NOTIFICATION] =
|
priv->rx_handlers[CALIBRATION_COMPLETE_NOTIFICATION] =
|
||||||
iwlagn_rx_calib_complete;
|
iwlagn_rx_calib_complete;
|
||||||
priv->rx_handlers[REPLY_TX] = iwlagn_rx_reply_tx;
|
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)
|
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;
|
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);
|
||||||
|
}
|
||||||
|
@@ -910,6 +910,27 @@ static void iwl_rx_handle(struct iwl_priv *priv)
|
|||||||
(pkt->hdr.cmd != STATISTICS_NOTIFICATION) &&
|
(pkt->hdr.cmd != STATISTICS_NOTIFICATION) &&
|
||||||
(pkt->hdr.cmd != REPLY_TX);
|
(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,
|
/* Based on type of command response or notification,
|
||||||
* handle those that need handling via function in
|
* handle those that need handling via function in
|
||||||
* rx_handlers table. See iwl_setup_rx_handlers() */
|
* rx_handlers table. See iwl_setup_rx_handlers() */
|
||||||
|
@@ -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);
|
int iwlcore_eeprom_acquire_semaphore(struct iwl_priv *priv);
|
||||||
void iwlcore_eeprom_release_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) */
|
/* mac80211 handlers (for 4965) */
|
||||||
int iwlagn_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb);
|
int iwlagn_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb);
|
||||||
int iwlagn_mac_start(struct ieee80211_hw *hw);
|
int iwlagn_mac_start(struct ieee80211_hw *hw);
|
||||||
|
@@ -34,6 +34,7 @@
|
|||||||
|
|
||||||
#include <linux/pci.h> /* for struct pci_device_id */
|
#include <linux/pci.h> /* for struct pci_device_id */
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/wait.h>
|
||||||
#include <net/ieee80211_radiotap.h>
|
#include <net/ieee80211_radiotap.h>
|
||||||
|
|
||||||
#include "iwl-eeprom.h"
|
#include "iwl-eeprom.h"
|
||||||
@@ -1139,6 +1140,33 @@ struct iwl_force_reset {
|
|||||||
*/
|
*/
|
||||||
#define IWLAGN_EXT_BEACON_TIME_POS 22
|
#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 {
|
enum iwl_rxon_context_id {
|
||||||
IWL_RXON_CTX_BSS,
|
IWL_RXON_CTX_BSS,
|
||||||
IWL_RXON_CTX_PAN,
|
IWL_RXON_CTX_PAN,
|
||||||
@@ -1463,6 +1491,11 @@ struct iwl_priv {
|
|||||||
struct iwl_bt_notif_statistics delta_statistics_bt;
|
struct iwl_bt_notif_statistics delta_statistics_bt;
|
||||||
struct iwl_bt_notif_statistics max_delta_bt;
|
struct iwl_bt_notif_statistics max_delta_bt;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* notification wait support */
|
||||||
|
struct list_head notif_waits;
|
||||||
|
spinlock_t notif_wait_lock;
|
||||||
|
wait_queue_head_t notif_waitq;
|
||||||
} _agn;
|
} _agn;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user