diff options
author | Johannes Berg <johannes.berg@intel.com> | 2011-01-04 19:22:00 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-01-21 15:32:20 -0500 |
commit | 7194207ceea7a54c846e0865d2459f4887fe1e0d (patch) | |
tree | eff27d7461dc336187a3765fd79e0daba7d8fc49 | |
parent | f945f1087f1fe20f9c626daa175b677736fc614d (diff) |
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>
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn-lib.c | 46 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn.c | 21 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn.h | 15 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-dev.h | 33 |
4 files changed, 115 insertions, 0 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c index 3dee87e8f55d..29dcda0bde65 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c | |||
@@ -473,6 +473,11 @@ void iwlagn_rx_handler_setup(struct iwl_priv *priv) | |||
473 | priv->rx_handlers[CALIBRATION_COMPLETE_NOTIFICATION] = | 473 | priv->rx_handlers[CALIBRATION_COMPLETE_NOTIFICATION] = |
474 | iwlagn_rx_calib_complete; | 474 | iwlagn_rx_calib_complete; |
475 | priv->rx_handlers[REPLY_TX] = iwlagn_rx_reply_tx; | 475 | priv->rx_handlers[REPLY_TX] = iwlagn_rx_reply_tx; |
476 | |||
477 | /* set up notification wait support */ | ||
478 | spin_lock_init(&priv->_agn.notif_wait_lock); | ||
479 | INIT_LIST_HEAD(&priv->_agn.notif_waits); | ||
480 | init_waitqueue_head(&priv->_agn.notif_waitq); | ||
476 | } | 481 | } |
477 | 482 | ||
478 | void iwlagn_setup_deferred_work(struct iwl_priv *priv) | 483 | 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) | |||
2389 | } | 2394 | } |
2390 | return 0; | 2395 | return 0; |
2391 | } | 2396 | } |
2397 | |||
2398 | /* notification wait support */ | ||
2399 | void iwlagn_init_notification_wait(struct iwl_priv *priv, | ||
2400 | struct iwl_notification_wait *wait_entry, | ||
2401 | void (*fn)(struct iwl_priv *priv, | ||
2402 | struct iwl_rx_packet *pkt), | ||
2403 | u8 cmd) | ||
2404 | { | ||
2405 | wait_entry->fn = fn; | ||
2406 | wait_entry->cmd = cmd; | ||
2407 | wait_entry->triggered = false; | ||
2408 | |||
2409 | spin_lock_bh(&priv->_agn.notif_wait_lock); | ||
2410 | list_add(&wait_entry->list, &priv->_agn.notif_waits); | ||
2411 | spin_unlock_bh(&priv->_agn.notif_wait_lock); | ||
2412 | } | ||
2413 | |||
2414 | signed long iwlagn_wait_notification(struct iwl_priv *priv, | ||
2415 | struct iwl_notification_wait *wait_entry, | ||
2416 | unsigned long timeout) | ||
2417 | { | ||
2418 | int ret; | ||
2419 | |||
2420 | ret = wait_event_timeout(priv->_agn.notif_waitq, | ||
2421 | &wait_entry->triggered, | ||
2422 | timeout); | ||
2423 | |||
2424 | spin_lock_bh(&priv->_agn.notif_wait_lock); | ||
2425 | list_del(&wait_entry->list); | ||
2426 | spin_unlock_bh(&priv->_agn.notif_wait_lock); | ||
2427 | |||
2428 | return ret; | ||
2429 | } | ||
2430 | |||
2431 | void iwlagn_remove_notification(struct iwl_priv *priv, | ||
2432 | struct iwl_notification_wait *wait_entry) | ||
2433 | { | ||
2434 | spin_lock_bh(&priv->_agn.notif_wait_lock); | ||
2435 | list_del(&wait_entry->list); | ||
2436 | spin_unlock_bh(&priv->_agn.notif_wait_lock); | ||
2437 | } | ||
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 624f174f8664..97657d04aa68 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c | |||
@@ -910,6 +910,27 @@ static void iwl_rx_handle(struct iwl_priv *priv) | |||
910 | (pkt->hdr.cmd != STATISTICS_NOTIFICATION) && | 910 | (pkt->hdr.cmd != STATISTICS_NOTIFICATION) && |
911 | (pkt->hdr.cmd != REPLY_TX); | 911 | (pkt->hdr.cmd != REPLY_TX); |
912 | 912 | ||
913 | /* | ||
914 | * Do the notification wait before RX handlers so | ||
915 | * even if the RX handler consumes the RXB we have | ||
916 | * access to it in the notification wait entry. | ||
917 | */ | ||
918 | if (!list_empty(&priv->_agn.notif_waits)) { | ||
919 | struct iwl_notification_wait *w; | ||
920 | |||
921 | spin_lock(&priv->_agn.notif_wait_lock); | ||
922 | list_for_each_entry(w, &priv->_agn.notif_waits, list) { | ||
923 | if (w->cmd == pkt->hdr.cmd) { | ||
924 | w->triggered = true; | ||
925 | if (w->fn) | ||
926 | w->fn(priv, pkt); | ||
927 | } | ||
928 | } | ||
929 | spin_unlock(&priv->_agn.notif_wait_lock); | ||
930 | |||
931 | wake_up_all(&priv->_agn.notif_waitq); | ||
932 | } | ||
933 | |||
913 | /* Based on type of command response or notification, | 934 | /* Based on type of command response or notification, |
914 | * handle those that need handling via function in | 935 | * handle those that need handling via function in |
915 | * rx_handlers table. See iwl_setup_rx_handlers() */ | 936 | * rx_handlers table. See iwl_setup_rx_handlers() */ |
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.h b/drivers/net/wireless/iwlwifi/iwl-agn.h index c30dc4f7a619..74d72ff24d05 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.h +++ b/drivers/net/wireless/iwlwifi/iwl-agn.h | |||
@@ -329,6 +329,21 @@ void iwl_eeprom_get_mac(const struct iwl_priv *priv, u8 *mac); | |||
329 | int iwlcore_eeprom_acquire_semaphore(struct iwl_priv *priv); | 329 | int iwlcore_eeprom_acquire_semaphore(struct iwl_priv *priv); |
330 | void iwlcore_eeprom_release_semaphore(struct iwl_priv *priv); | 330 | void iwlcore_eeprom_release_semaphore(struct iwl_priv *priv); |
331 | 331 | ||
332 | /* notification wait support */ | ||
333 | void __acquires(wait_entry) | ||
334 | iwlagn_init_notification_wait(struct iwl_priv *priv, | ||
335 | struct iwl_notification_wait *wait_entry, | ||
336 | void (*fn)(struct iwl_priv *priv, | ||
337 | struct iwl_rx_packet *pkt), | ||
338 | u8 cmd); | ||
339 | signed long __releases(wait_entry) | ||
340 | iwlagn_wait_notification(struct iwl_priv *priv, | ||
341 | struct iwl_notification_wait *wait_entry, | ||
342 | unsigned long timeout); | ||
343 | void __releases(wait_entry) | ||
344 | iwlagn_remove_notification(struct iwl_priv *priv, | ||
345 | struct iwl_notification_wait *wait_entry); | ||
346 | |||
332 | /* mac80211 handlers (for 4965) */ | 347 | /* mac80211 handlers (for 4965) */ |
333 | int iwlagn_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb); | 348 | int iwlagn_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb); |
334 | int iwlagn_mac_start(struct ieee80211_hw *hw); | 349 | int iwlagn_mac_start(struct ieee80211_hw *hw); |
diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index 8dda67850af4..2ec680bb8f6d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h | |||
@@ -34,6 +34,7 @@ | |||
34 | 34 | ||
35 | #include <linux/pci.h> /* for struct pci_device_id */ | 35 | #include <linux/pci.h> /* for struct pci_device_id */ |
36 | #include <linux/kernel.h> | 36 | #include <linux/kernel.h> |
37 | #include <linux/wait.h> | ||
37 | #include <net/ieee80211_radiotap.h> | 38 | #include <net/ieee80211_radiotap.h> |
38 | 39 | ||
39 | #include "iwl-eeprom.h" | 40 | #include "iwl-eeprom.h" |
@@ -1139,6 +1140,33 @@ struct iwl_force_reset { | |||
1139 | */ | 1140 | */ |
1140 | #define IWLAGN_EXT_BEACON_TIME_POS 22 | 1141 | #define IWLAGN_EXT_BEACON_TIME_POS 22 |
1141 | 1142 | ||
1143 | /** | ||
1144 | * struct iwl_notification_wait - notification wait entry | ||
1145 | * @list: list head for global list | ||
1146 | * @fn: function called with the notification | ||
1147 | * @cmd: command ID | ||
1148 | * | ||
1149 | * This structure is not used directly, to wait for a | ||
1150 | * notification declare it on the stack, and call | ||
1151 | * iwlagn_init_notification_wait() with appropriate | ||
1152 | * parameters. Then do whatever will cause the ucode | ||
1153 | * to notify the driver, and to wait for that then | ||
1154 | * call iwlagn_wait_notification(). | ||
1155 | * | ||
1156 | * Each notification is one-shot. If at some point we | ||
1157 | * need to support multi-shot notifications (which | ||
1158 | * can't be allocated on the stack) we need to modify | ||
1159 | * the code for them. | ||
1160 | */ | ||
1161 | struct iwl_notification_wait { | ||
1162 | struct list_head list; | ||
1163 | |||
1164 | void (*fn)(struct iwl_priv *priv, struct iwl_rx_packet *pkt); | ||
1165 | |||
1166 | u8 cmd; | ||
1167 | bool triggered; | ||
1168 | }; | ||
1169 | |||
1142 | enum iwl_rxon_context_id { | 1170 | enum iwl_rxon_context_id { |
1143 | IWL_RXON_CTX_BSS, | 1171 | IWL_RXON_CTX_BSS, |
1144 | IWL_RXON_CTX_PAN, | 1172 | IWL_RXON_CTX_PAN, |
@@ -1463,6 +1491,11 @@ struct iwl_priv { | |||
1463 | struct iwl_bt_notif_statistics delta_statistics_bt; | 1491 | struct iwl_bt_notif_statistics delta_statistics_bt; |
1464 | struct iwl_bt_notif_statistics max_delta_bt; | 1492 | struct iwl_bt_notif_statistics max_delta_bt; |
1465 | #endif | 1493 | #endif |
1494 | |||
1495 | /* notification wait support */ | ||
1496 | struct list_head notif_waits; | ||
1497 | spinlock_t notif_wait_lock; | ||
1498 | wait_queue_head_t notif_waitq; | ||
1466 | } _agn; | 1499 | } _agn; |
1467 | #endif | 1500 | #endif |
1468 | }; | 1501 | }; |