1 /****************************************************************************** 2 * 3 * This file is provided under a dual BSD/GPLv2 license. When using or 4 * redistributing this file, you may do so under either license. 5 * 6 * GPL LICENSE SUMMARY 7 * 8 * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. 9 * Copyright(c) 2015 - 2017 Intel Deutschland GmbH 10 * 11 * This program is free software; you can redistribute it and/or modify 12 * it under the terms of version 2 of the GNU General Public License as 13 * published by the Free Software Foundation. 14 * 15 * This program is distributed in the hope that it will be useful, but 16 * WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * General Public License for more details. 19 * 20 * The full GNU General Public License is included in this distribution 21 * in the file called COPYING. 22 * 23 * Contact Information: 24 * Intel Linux Wireless <linuxwifi@intel.com> 25 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 26 * 27 * BSD LICENSE 28 * 29 * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. 30 * Copyright(c) 2015 - 2017 Intel Deutschland GmbH 31 * All rights reserved. 32 * 33 * Redistribution and use in source and binary forms, with or without 34 * modification, are permitted provided that the following conditions 35 * are met: 36 * 37 * * Redistributions of source code must retain the above copyright 38 * notice, this list of conditions and the following disclaimer. 39 * * Redistributions in binary form must reproduce the above copyright 40 * notice, this list of conditions and the following disclaimer in 41 * the documentation and/or other materials provided with the 42 * distribution. 43 * * Neither the name Intel Corporation nor the names of its 44 * contributors may be used to endorse or promote products derived 45 * from this software without specific prior written permission. 46 * 47 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 48 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 49 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 50 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 51 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 52 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 53 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 54 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 55 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 56 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 57 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 58 * 59 *****************************************************************************/ 60 #include <linux/sched.h> 61 #include <linux/export.h> 62 63 #include "iwl-drv.h" 64 #include "notif-wait.h" 65 66 67 void iwl_notification_wait_init(struct iwl_notif_wait_data *notif_wait) 68 { 69 spin_lock_init(¬if_wait->notif_wait_lock); 70 INIT_LIST_HEAD(¬if_wait->notif_waits); 71 init_waitqueue_head(¬if_wait->notif_waitq); 72 } 73 IWL_EXPORT_SYMBOL(iwl_notification_wait_init); 74 75 bool iwl_notification_wait(struct iwl_notif_wait_data *notif_wait, 76 struct iwl_rx_packet *pkt) 77 { 78 bool triggered = false; 79 80 if (!list_empty(¬if_wait->notif_waits)) { 81 struct iwl_notification_wait *w; 82 83 spin_lock(¬if_wait->notif_wait_lock); 84 list_for_each_entry(w, ¬if_wait->notif_waits, list) { 85 int i; 86 bool found = false; 87 88 /* 89 * If it already finished (triggered) or has been 90 * aborted then don't evaluate it again to avoid races, 91 * Otherwise the function could be called again even 92 * though it returned true before 93 */ 94 if (w->triggered || w->aborted) 95 continue; 96 97 for (i = 0; i < w->n_cmds; i++) { 98 u16 rec_id = WIDE_ID(pkt->hdr.group_id, 99 pkt->hdr.cmd); 100 101 if (w->cmds[i] == rec_id || 102 (!iwl_cmd_groupid(w->cmds[i]) && 103 DEF_ID(w->cmds[i]) == rec_id)) { 104 found = true; 105 break; 106 } 107 } 108 if (!found) 109 continue; 110 111 if (!w->fn || w->fn(notif_wait, pkt, w->fn_data)) { 112 w->triggered = true; 113 triggered = true; 114 } 115 } 116 spin_unlock(¬if_wait->notif_wait_lock); 117 } 118 119 return triggered; 120 } 121 IWL_EXPORT_SYMBOL(iwl_notification_wait); 122 123 void iwl_abort_notification_waits(struct iwl_notif_wait_data *notif_wait) 124 { 125 struct iwl_notification_wait *wait_entry; 126 127 spin_lock(¬if_wait->notif_wait_lock); 128 list_for_each_entry(wait_entry, ¬if_wait->notif_waits, list) 129 wait_entry->aborted = true; 130 spin_unlock(¬if_wait->notif_wait_lock); 131 132 wake_up_all(¬if_wait->notif_waitq); 133 } 134 IWL_EXPORT_SYMBOL(iwl_abort_notification_waits); 135 136 void 137 iwl_init_notification_wait(struct iwl_notif_wait_data *notif_wait, 138 struct iwl_notification_wait *wait_entry, 139 const u16 *cmds, int n_cmds, 140 bool (*fn)(struct iwl_notif_wait_data *notif_wait, 141 struct iwl_rx_packet *pkt, void *data), 142 void *fn_data) 143 { 144 if (WARN_ON(n_cmds > MAX_NOTIF_CMDS)) 145 n_cmds = MAX_NOTIF_CMDS; 146 147 wait_entry->fn = fn; 148 wait_entry->fn_data = fn_data; 149 wait_entry->n_cmds = n_cmds; 150 memcpy(wait_entry->cmds, cmds, n_cmds * sizeof(u16)); 151 wait_entry->triggered = false; 152 wait_entry->aborted = false; 153 154 spin_lock_bh(¬if_wait->notif_wait_lock); 155 list_add(&wait_entry->list, ¬if_wait->notif_waits); 156 spin_unlock_bh(¬if_wait->notif_wait_lock); 157 } 158 IWL_EXPORT_SYMBOL(iwl_init_notification_wait); 159 160 void iwl_remove_notification(struct iwl_notif_wait_data *notif_wait, 161 struct iwl_notification_wait *wait_entry) 162 { 163 spin_lock_bh(¬if_wait->notif_wait_lock); 164 list_del(&wait_entry->list); 165 spin_unlock_bh(¬if_wait->notif_wait_lock); 166 } 167 IWL_EXPORT_SYMBOL(iwl_remove_notification); 168 169 int iwl_wait_notification(struct iwl_notif_wait_data *notif_wait, 170 struct iwl_notification_wait *wait_entry, 171 unsigned long timeout) 172 { 173 int ret; 174 175 ret = wait_event_timeout(notif_wait->notif_waitq, 176 wait_entry->triggered || wait_entry->aborted, 177 timeout); 178 179 iwl_remove_notification(notif_wait, wait_entry); 180 181 if (wait_entry->aborted) 182 return -EIO; 183 184 /* return value is always >= 0 */ 185 if (ret <= 0) 186 return -ETIMEDOUT; 187 return 0; 188 } 189 IWL_EXPORT_SYMBOL(iwl_wait_notification); 190