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