194dc1dadSImre Vadász /*-
294dc1dadSImre Vadász * Based on BSD-licensed source modules in the Linux iwlwifi driver,
394dc1dadSImre Vadász * which were used as the reference documentation for this implementation.
494dc1dadSImre Vadász *
594dc1dadSImre Vadász ******************************************************************************
694dc1dadSImre Vadász *
794dc1dadSImre Vadász * This file is provided under a dual BSD/GPLv2 license. When using or
894dc1dadSImre Vadász * redistributing this file, you may do so under either license.
994dc1dadSImre Vadász *
1094dc1dadSImre Vadász * GPL LICENSE SUMMARY
1194dc1dadSImre Vadász *
1294dc1dadSImre Vadász * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
1394dc1dadSImre Vadász * Copyright(c) 2015 Intel Deutschland GmbH
1494dc1dadSImre Vadász *
1594dc1dadSImre Vadász * This program is free software; you can redistribute it and/or modify
1694dc1dadSImre Vadász * it under the terms of version 2 of the GNU General Public License as
1794dc1dadSImre Vadász * published by the Free Software Foundation.
1894dc1dadSImre Vadász *
1994dc1dadSImre Vadász * This program is distributed in the hope that it will be useful, but
2094dc1dadSImre Vadász * WITHOUT ANY WARRANTY; without even the implied warranty of
2194dc1dadSImre Vadász * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2294dc1dadSImre Vadász * General Public License for more details.
2394dc1dadSImre Vadász *
2494dc1dadSImre Vadász * You should have received a copy of the GNU General Public License
2594dc1dadSImre Vadász * along with this program; if not, write to the Free Software
2694dc1dadSImre Vadász * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
2794dc1dadSImre Vadász * USA
2894dc1dadSImre Vadász *
2994dc1dadSImre Vadász * The full GNU General Public License is included in this distribution
3094dc1dadSImre Vadász * in the file called COPYING.
3194dc1dadSImre Vadász *
3294dc1dadSImre Vadász * Contact Information:
3394dc1dadSImre Vadász * Intel Linux Wireless <linuxwifi@intel.com>
3494dc1dadSImre Vadász * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
3594dc1dadSImre Vadász *
3694dc1dadSImre Vadász * BSD LICENSE
3794dc1dadSImre Vadász *
3894dc1dadSImre Vadász * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
3994dc1dadSImre Vadász * All rights reserved.
4094dc1dadSImre Vadász *
4194dc1dadSImre Vadász * Redistribution and use in source and binary forms, with or without
4294dc1dadSImre Vadász * modification, are permitted provided that the following conditions
4394dc1dadSImre Vadász * are met:
4494dc1dadSImre Vadász *
4594dc1dadSImre Vadász * * Redistributions of source code must retain the above copyright
4694dc1dadSImre Vadász * notice, this list of conditions and the following disclaimer.
4794dc1dadSImre Vadász * * Redistributions in binary form must reproduce the above copyright
4894dc1dadSImre Vadász * notice, this list of conditions and the following disclaimer in
4994dc1dadSImre Vadász * the documentation and/or other materials provided with the
5094dc1dadSImre Vadász * distribution.
5194dc1dadSImre Vadász * * Neither the name Intel Corporation nor the names of its
5294dc1dadSImre Vadász * contributors may be used to endorse or promote products derived
5394dc1dadSImre Vadász * from this software without specific prior written permission.
5494dc1dadSImre Vadász *
5594dc1dadSImre Vadász * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
5694dc1dadSImre Vadász * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5794dc1dadSImre Vadász * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
5894dc1dadSImre Vadász * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
5994dc1dadSImre Vadász * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
6094dc1dadSImre Vadász * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
6194dc1dadSImre Vadász * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
6294dc1dadSImre Vadász * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
6394dc1dadSImre Vadász * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
6494dc1dadSImre Vadász * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
6594dc1dadSImre Vadász * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
6694dc1dadSImre Vadász *
6794dc1dadSImre Vadász *****************************************************************************/
68*6acbba79SMatthew Dillon
6994dc1dadSImre Vadász #include <sys/param.h>
7094dc1dadSImre Vadász #include <sys/systm.h>
7194dc1dadSImre Vadász #include <sys/bus.h>
7294dc1dadSImre Vadász #include <sys/kernel.h>
7394dc1dadSImre Vadász #include <sys/lock.h>
74*6acbba79SMatthew Dillon #include <sys/malloc.h>
7594dc1dadSImre Vadász #include <sys/queue.h>
7694dc1dadSImre Vadász
7794dc1dadSImre Vadász #include "if_iwm_notif_wait.h"
7894dc1dadSImre Vadász
79*6acbba79SMatthew Dillon #define IWM_WAIT_LOCK_INIT(_n, _s) \
80*6acbba79SMatthew Dillon lockinit(&(_n)->lk_lk, (_s), 0, 0)
81*6acbba79SMatthew Dillon #define IWM_WAIT_LOCK(_n) lockmgr(&(_n)->lk_lk, LK_EXCLUSIVE)
82*6acbba79SMatthew Dillon #define IWM_WAIT_UNLOCK(_n) lockmgr(&(_n)->lk_lk, LK_RELEASE)
83*6acbba79SMatthew Dillon #define IWM_WAIT_LOCK_DESTROY(_n) lockuninit(&(_n)->lk_lk)
84*6acbba79SMatthew Dillon
8594dc1dadSImre Vadász struct iwm_notif_wait_data {
86*6acbba79SMatthew Dillon struct lock lk_lk;
87*6acbba79SMatthew Dillon char lk_buf[32];
8894dc1dadSImre Vadász STAILQ_HEAD(, iwm_notification_wait) list;
8994dc1dadSImre Vadász struct iwm_softc *sc;
9094dc1dadSImre Vadász };
9194dc1dadSImre Vadász
9294dc1dadSImre Vadász struct iwm_notif_wait_data *
iwm_notification_wait_init(struct iwm_softc * sc)9394dc1dadSImre Vadász iwm_notification_wait_init(struct iwm_softc *sc)
9494dc1dadSImre Vadász {
9594dc1dadSImre Vadász struct iwm_notif_wait_data *data;
9694dc1dadSImre Vadász
97*6acbba79SMatthew Dillon data = kmalloc(sizeof(*data), M_DEVBUF, M_INTWAIT | M_ZERO);
9894dc1dadSImre Vadász if (data != NULL) {
99*6acbba79SMatthew Dillon ksnprintf(data->lk_buf, 32, "iwm wait_notif");
100*6acbba79SMatthew Dillon IWM_WAIT_LOCK_INIT(data, data->lk_buf);
10194dc1dadSImre Vadász STAILQ_INIT(&data->list);
10294dc1dadSImre Vadász data->sc = sc;
10394dc1dadSImre Vadász }
10494dc1dadSImre Vadász
10594dc1dadSImre Vadász return data;
10694dc1dadSImre Vadász }
10794dc1dadSImre Vadász
10894dc1dadSImre Vadász void
iwm_notification_wait_free(struct iwm_notif_wait_data * notif_data)10994dc1dadSImre Vadász iwm_notification_wait_free(struct iwm_notif_wait_data *notif_data)
11094dc1dadSImre Vadász {
111*6acbba79SMatthew Dillon KASSERT(STAILQ_EMPTY(¬if_data->list), ("notif list isn't empty"));
112*6acbba79SMatthew Dillon IWM_WAIT_LOCK_DESTROY(notif_data);
11394dc1dadSImre Vadász kfree(notif_data, M_DEVBUF);
11494dc1dadSImre Vadász }
11594dc1dadSImre Vadász
11694dc1dadSImre Vadász /* XXX Get rid of separate cmd argument, like in iwlwifi's code */
11794dc1dadSImre Vadász void
iwm_notification_wait_notify(struct iwm_notif_wait_data * notif_data,uint16_t cmd,struct iwm_rx_packet * pkt)11894dc1dadSImre Vadász iwm_notification_wait_notify(struct iwm_notif_wait_data *notif_data,
11994dc1dadSImre Vadász uint16_t cmd, struct iwm_rx_packet *pkt)
12094dc1dadSImre Vadász {
12194dc1dadSImre Vadász struct iwm_notification_wait *wait_entry;
12294dc1dadSImre Vadász
123*6acbba79SMatthew Dillon IWM_WAIT_LOCK(notif_data);
12494dc1dadSImre Vadász STAILQ_FOREACH(wait_entry, ¬if_data->list, entry) {
12594dc1dadSImre Vadász int found = FALSE;
12694dc1dadSImre Vadász int i;
12794dc1dadSImre Vadász
12894dc1dadSImre Vadász /*
12994dc1dadSImre Vadász * If it already finished (triggered) or has been
13094dc1dadSImre Vadász * aborted then don't evaluate it again to avoid races,
13194dc1dadSImre Vadász * Otherwise the function could be called again even
13294dc1dadSImre Vadász * though it returned true before
13394dc1dadSImre Vadász */
13494dc1dadSImre Vadász if (wait_entry->triggered || wait_entry->aborted)
13594dc1dadSImre Vadász continue;
13694dc1dadSImre Vadász
13794dc1dadSImre Vadász for (i = 0; i < wait_entry->n_cmds; i++) {
13894dc1dadSImre Vadász if (cmd == wait_entry->cmds[i]) {
13994dc1dadSImre Vadász found = TRUE;
14094dc1dadSImre Vadász break;
14194dc1dadSImre Vadász }
14294dc1dadSImre Vadász }
14394dc1dadSImre Vadász if (!found)
14494dc1dadSImre Vadász continue;
14594dc1dadSImre Vadász
14694dc1dadSImre Vadász if (!wait_entry->fn ||
14794dc1dadSImre Vadász wait_entry->fn(notif_data->sc, pkt, wait_entry->fn_data)) {
14894dc1dadSImre Vadász wait_entry->triggered = 1;
14994dc1dadSImre Vadász wakeup(wait_entry);
15094dc1dadSImre Vadász }
15194dc1dadSImre Vadász }
152*6acbba79SMatthew Dillon IWM_WAIT_UNLOCK(notif_data);
15394dc1dadSImre Vadász }
15494dc1dadSImre Vadász
15594dc1dadSImre Vadász void
iwm_abort_notification_waits(struct iwm_notif_wait_data * notif_data)15694dc1dadSImre Vadász iwm_abort_notification_waits(struct iwm_notif_wait_data *notif_data)
15794dc1dadSImre Vadász {
15894dc1dadSImre Vadász struct iwm_notification_wait *wait_entry;
15994dc1dadSImre Vadász
160*6acbba79SMatthew Dillon IWM_WAIT_LOCK(notif_data);
16194dc1dadSImre Vadász STAILQ_FOREACH(wait_entry, ¬if_data->list, entry) {
16294dc1dadSImre Vadász wait_entry->aborted = 1;
16394dc1dadSImre Vadász wakeup(wait_entry);
16494dc1dadSImre Vadász }
165*6acbba79SMatthew Dillon IWM_WAIT_UNLOCK(notif_data);
16694dc1dadSImre Vadász }
16794dc1dadSImre Vadász
16894dc1dadSImre Vadász void
iwm_init_notification_wait(struct iwm_notif_wait_data * notif_data,struct iwm_notification_wait * wait_entry,const uint16_t * cmds,int n_cmds,int (* fn)(struct iwm_softc * sc,struct iwm_rx_packet * pkt,void * data),void * fn_data)16994dc1dadSImre Vadász iwm_init_notification_wait(struct iwm_notif_wait_data *notif_data,
17094dc1dadSImre Vadász struct iwm_notification_wait *wait_entry, const uint16_t *cmds, int n_cmds,
17194dc1dadSImre Vadász int (*fn)(struct iwm_softc *sc, struct iwm_rx_packet *pkt, void *data),
17294dc1dadSImre Vadász void *fn_data)
17394dc1dadSImre Vadász {
174*6acbba79SMatthew Dillon KASSERT(n_cmds <= IWM_MAX_NOTIF_CMDS,
175*6acbba79SMatthew Dillon ("n_cmds %d is too large", n_cmds));
17694dc1dadSImre Vadász wait_entry->fn = fn;
17794dc1dadSImre Vadász wait_entry->fn_data = fn_data;
17894dc1dadSImre Vadász wait_entry->n_cmds = n_cmds;
17994dc1dadSImre Vadász memcpy(wait_entry->cmds, cmds, n_cmds * sizeof(uint16_t));
18094dc1dadSImre Vadász wait_entry->triggered = 0;
18194dc1dadSImre Vadász wait_entry->aborted = 0;
18294dc1dadSImre Vadász
183*6acbba79SMatthew Dillon IWM_WAIT_LOCK(notif_data);
18494dc1dadSImre Vadász STAILQ_INSERT_TAIL(¬if_data->list, wait_entry, entry);
185*6acbba79SMatthew Dillon IWM_WAIT_UNLOCK(notif_data);
18694dc1dadSImre Vadász }
18794dc1dadSImre Vadász
18894dc1dadSImre Vadász int
iwm_wait_notification(struct iwm_notif_wait_data * notif_data,struct iwm_notification_wait * wait_entry,int timeout)18994dc1dadSImre Vadász iwm_wait_notification(struct iwm_notif_wait_data *notif_data,
19094dc1dadSImre Vadász struct iwm_notification_wait *wait_entry, int timeout)
19194dc1dadSImre Vadász {
19294dc1dadSImre Vadász int ret = 0;
19394dc1dadSImre Vadász
194*6acbba79SMatthew Dillon IWM_WAIT_LOCK(notif_data);
19594dc1dadSImre Vadász if (!wait_entry->triggered && !wait_entry->aborted) {
196*6acbba79SMatthew Dillon ret = lksleep(wait_entry, ¬if_data->lk_lk, 0, "iwm_notif",
19794dc1dadSImre Vadász timeout);
19894dc1dadSImre Vadász }
19994dc1dadSImre Vadász STAILQ_REMOVE(¬if_data->list, wait_entry, iwm_notification_wait,
20094dc1dadSImre Vadász entry);
201*6acbba79SMatthew Dillon IWM_WAIT_UNLOCK(notif_data);
20294dc1dadSImre Vadász
20394dc1dadSImre Vadász return ret;
20494dc1dadSImre Vadász }
20594dc1dadSImre Vadász
20694dc1dadSImre Vadász void
iwm_remove_notification(struct iwm_notif_wait_data * notif_data,struct iwm_notification_wait * wait_entry)20794dc1dadSImre Vadász iwm_remove_notification(struct iwm_notif_wait_data *notif_data,
20894dc1dadSImre Vadász struct iwm_notification_wait *wait_entry)
20994dc1dadSImre Vadász {
210*6acbba79SMatthew Dillon IWM_WAIT_LOCK(notif_data);
21194dc1dadSImre Vadász STAILQ_REMOVE(¬if_data->list, wait_entry, iwm_notification_wait,
21294dc1dadSImre Vadász entry);
213*6acbba79SMatthew Dillon IWM_WAIT_UNLOCK(notif_data);
21494dc1dadSImre Vadász }
215