1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 /* 3 * Copyright (C) 2005-2014 Intel Corporation 4 * Copyright (C) 2015-2017 Intel Deutschland GmbH 5 */ 6 #include <linux/sched.h> 7 #include <linux/export.h> 8 9 #include "iwl-drv.h" 10 #include "notif-wait.h" 11 12 13 void iwl_notification_wait_init(struct iwl_notif_wait_data *notif_wait) 14 { 15 spin_lock_init(¬if_wait->notif_wait_lock); 16 INIT_LIST_HEAD(¬if_wait->notif_waits); 17 init_waitqueue_head(¬if_wait->notif_waitq); 18 } 19 IWL_EXPORT_SYMBOL(iwl_notification_wait_init); 20 21 bool iwl_notification_wait(struct iwl_notif_wait_data *notif_wait, 22 struct iwl_rx_packet *pkt) 23 { 24 bool triggered = false; 25 26 if (!list_empty(¬if_wait->notif_waits)) { 27 struct iwl_notification_wait *w; 28 29 spin_lock(¬if_wait->notif_wait_lock); 30 list_for_each_entry(w, ¬if_wait->notif_waits, list) { 31 int i; 32 bool found = false; 33 34 /* 35 * If it already finished (triggered) or has been 36 * aborted then don't evaluate it again to avoid races, 37 * Otherwise the function could be called again even 38 * though it returned true before 39 */ 40 if (w->triggered || w->aborted) 41 continue; 42 43 for (i = 0; i < w->n_cmds; i++) { 44 u16 rec_id = WIDE_ID(pkt->hdr.group_id, 45 pkt->hdr.cmd); 46 47 if (w->cmds[i] == rec_id || 48 (!iwl_cmd_groupid(w->cmds[i]) && 49 DEF_ID(w->cmds[i]) == rec_id)) { 50 found = true; 51 break; 52 } 53 } 54 if (!found) 55 continue; 56 57 if (!w->fn || w->fn(notif_wait, pkt, w->fn_data)) { 58 w->triggered = true; 59 triggered = true; 60 } 61 } 62 spin_unlock(¬if_wait->notif_wait_lock); 63 } 64 65 return triggered; 66 } 67 IWL_EXPORT_SYMBOL(iwl_notification_wait); 68 69 void iwl_abort_notification_waits(struct iwl_notif_wait_data *notif_wait) 70 { 71 struct iwl_notification_wait *wait_entry; 72 73 spin_lock(¬if_wait->notif_wait_lock); 74 list_for_each_entry(wait_entry, ¬if_wait->notif_waits, list) 75 wait_entry->aborted = true; 76 spin_unlock(¬if_wait->notif_wait_lock); 77 78 wake_up_all(¬if_wait->notif_waitq); 79 } 80 IWL_EXPORT_SYMBOL(iwl_abort_notification_waits); 81 82 void 83 iwl_init_notification_wait(struct iwl_notif_wait_data *notif_wait, 84 struct iwl_notification_wait *wait_entry, 85 const u16 *cmds, int n_cmds, 86 bool (*fn)(struct iwl_notif_wait_data *notif_wait, 87 struct iwl_rx_packet *pkt, void *data), 88 void *fn_data) 89 { 90 if (WARN_ON(n_cmds > MAX_NOTIF_CMDS)) 91 n_cmds = MAX_NOTIF_CMDS; 92 93 wait_entry->fn = fn; 94 wait_entry->fn_data = fn_data; 95 wait_entry->n_cmds = n_cmds; 96 memcpy(wait_entry->cmds, cmds, n_cmds * sizeof(u16)); 97 wait_entry->triggered = false; 98 wait_entry->aborted = false; 99 100 spin_lock_bh(¬if_wait->notif_wait_lock); 101 list_add(&wait_entry->list, ¬if_wait->notif_waits); 102 spin_unlock_bh(¬if_wait->notif_wait_lock); 103 } 104 IWL_EXPORT_SYMBOL(iwl_init_notification_wait); 105 106 void iwl_remove_notification(struct iwl_notif_wait_data *notif_wait, 107 struct iwl_notification_wait *wait_entry) 108 { 109 spin_lock_bh(¬if_wait->notif_wait_lock); 110 list_del(&wait_entry->list); 111 spin_unlock_bh(¬if_wait->notif_wait_lock); 112 } 113 IWL_EXPORT_SYMBOL(iwl_remove_notification); 114 115 int iwl_wait_notification(struct iwl_notif_wait_data *notif_wait, 116 struct iwl_notification_wait *wait_entry, 117 unsigned long timeout) 118 { 119 int ret; 120 121 ret = wait_event_timeout(notif_wait->notif_waitq, 122 wait_entry->triggered || wait_entry->aborted, 123 timeout); 124 125 iwl_remove_notification(notif_wait, wait_entry); 126 127 if (wait_entry->aborted) 128 return -EIO; 129 130 /* return value is always >= 0 */ 131 if (ret <= 0) 132 return -ETIMEDOUT; 133 return 0; 134 } 135 IWL_EXPORT_SYMBOL(iwl_wait_notification); 136