162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2005-2014, 2021 Intel Corporation 462306a36Sopenharmony_ci * Copyright (C) 2015-2017 Intel Deutschland GmbH 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include <linux/sched.h> 762306a36Sopenharmony_ci#include <linux/export.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "iwl-drv.h" 1062306a36Sopenharmony_ci#include "notif-wait.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_civoid iwl_notification_wait_init(struct iwl_notif_wait_data *notif_wait) 1462306a36Sopenharmony_ci{ 1562306a36Sopenharmony_ci spin_lock_init(¬if_wait->notif_wait_lock); 1662306a36Sopenharmony_ci INIT_LIST_HEAD(¬if_wait->notif_waits); 1762306a36Sopenharmony_ci init_waitqueue_head(¬if_wait->notif_waitq); 1862306a36Sopenharmony_ci} 1962306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_notification_wait_init); 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cibool iwl_notification_wait(struct iwl_notif_wait_data *notif_wait, 2262306a36Sopenharmony_ci struct iwl_rx_packet *pkt) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci bool triggered = false; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci if (!list_empty(¬if_wait->notif_waits)) { 2762306a36Sopenharmony_ci struct iwl_notification_wait *w; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci spin_lock_bh(¬if_wait->notif_wait_lock); 3062306a36Sopenharmony_ci list_for_each_entry(w, ¬if_wait->notif_waits, list) { 3162306a36Sopenharmony_ci int i; 3262306a36Sopenharmony_ci bool found = false; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci /* 3562306a36Sopenharmony_ci * If it already finished (triggered) or has been 3662306a36Sopenharmony_ci * aborted then don't evaluate it again to avoid races, 3762306a36Sopenharmony_ci * Otherwise the function could be called again even 3862306a36Sopenharmony_ci * though it returned true before 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_ci if (w->triggered || w->aborted) 4162306a36Sopenharmony_ci continue; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci for (i = 0; i < w->n_cmds; i++) { 4462306a36Sopenharmony_ci u16 rec_id = WIDE_ID(pkt->hdr.group_id, 4562306a36Sopenharmony_ci pkt->hdr.cmd); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci if (w->cmds[i] == rec_id || 4862306a36Sopenharmony_ci (!iwl_cmd_groupid(w->cmds[i]) && 4962306a36Sopenharmony_ci DEF_ID(w->cmds[i]) == rec_id)) { 5062306a36Sopenharmony_ci found = true; 5162306a36Sopenharmony_ci break; 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci if (!found) 5562306a36Sopenharmony_ci continue; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (!w->fn || w->fn(notif_wait, pkt, w->fn_data)) { 5862306a36Sopenharmony_ci w->triggered = true; 5962306a36Sopenharmony_ci triggered = true; 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci spin_unlock_bh(¬if_wait->notif_wait_lock); 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci return triggered; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_notification_wait); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_civoid iwl_abort_notification_waits(struct iwl_notif_wait_data *notif_wait) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci struct iwl_notification_wait *wait_entry; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci spin_lock_bh(¬if_wait->notif_wait_lock); 7462306a36Sopenharmony_ci list_for_each_entry(wait_entry, ¬if_wait->notif_waits, list) 7562306a36Sopenharmony_ci wait_entry->aborted = true; 7662306a36Sopenharmony_ci spin_unlock_bh(¬if_wait->notif_wait_lock); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci wake_up_all(¬if_wait->notif_waitq); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_abort_notification_waits); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_civoid 8362306a36Sopenharmony_ciiwl_init_notification_wait(struct iwl_notif_wait_data *notif_wait, 8462306a36Sopenharmony_ci struct iwl_notification_wait *wait_entry, 8562306a36Sopenharmony_ci const u16 *cmds, int n_cmds, 8662306a36Sopenharmony_ci bool (*fn)(struct iwl_notif_wait_data *notif_wait, 8762306a36Sopenharmony_ci struct iwl_rx_packet *pkt, void *data), 8862306a36Sopenharmony_ci void *fn_data) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci if (WARN_ON(n_cmds > MAX_NOTIF_CMDS)) 9162306a36Sopenharmony_ci n_cmds = MAX_NOTIF_CMDS; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci wait_entry->fn = fn; 9462306a36Sopenharmony_ci wait_entry->fn_data = fn_data; 9562306a36Sopenharmony_ci wait_entry->n_cmds = n_cmds; 9662306a36Sopenharmony_ci memcpy(wait_entry->cmds, cmds, n_cmds * sizeof(u16)); 9762306a36Sopenharmony_ci wait_entry->triggered = false; 9862306a36Sopenharmony_ci wait_entry->aborted = false; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci spin_lock_bh(¬if_wait->notif_wait_lock); 10162306a36Sopenharmony_ci list_add(&wait_entry->list, ¬if_wait->notif_waits); 10262306a36Sopenharmony_ci spin_unlock_bh(¬if_wait->notif_wait_lock); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_init_notification_wait); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_civoid iwl_remove_notification(struct iwl_notif_wait_data *notif_wait, 10762306a36Sopenharmony_ci struct iwl_notification_wait *wait_entry) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci spin_lock_bh(¬if_wait->notif_wait_lock); 11062306a36Sopenharmony_ci list_del(&wait_entry->list); 11162306a36Sopenharmony_ci spin_unlock_bh(¬if_wait->notif_wait_lock); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_remove_notification); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ciint iwl_wait_notification(struct iwl_notif_wait_data *notif_wait, 11662306a36Sopenharmony_ci struct iwl_notification_wait *wait_entry, 11762306a36Sopenharmony_ci unsigned long timeout) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci int ret; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci ret = wait_event_timeout(notif_wait->notif_waitq, 12262306a36Sopenharmony_ci wait_entry->triggered || wait_entry->aborted, 12362306a36Sopenharmony_ci timeout); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci iwl_remove_notification(notif_wait, wait_entry); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (wait_entry->aborted) 12862306a36Sopenharmony_ci return -EIO; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* return value is always >= 0 */ 13162306a36Sopenharmony_ci if (ret <= 0) 13262306a36Sopenharmony_ci return -ETIMEDOUT; 13362306a36Sopenharmony_ci return 0; 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_wait_notification); 136