xref: /freebsd/sys/dev/iwm/if_iwm_time_event.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
1d4886179SRui Paulo /*	$OpenBSD: if_iwm.c,v 1.39 2015/03/23 00:35:19 jsg Exp $	*/
2d4886179SRui Paulo 
3d4886179SRui Paulo /*
4d4886179SRui Paulo  * Copyright (c) 2014 genua mbh <info@genua.de>
5d4886179SRui Paulo  * Copyright (c) 2014 Fixup Software Ltd.
6d4886179SRui Paulo  *
7d4886179SRui Paulo  * Permission to use, copy, modify, and distribute this software for any
8d4886179SRui Paulo  * purpose with or without fee is hereby granted, provided that the above
9d4886179SRui Paulo  * copyright notice and this permission notice appear in all copies.
10d4886179SRui Paulo  *
11d4886179SRui Paulo  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12d4886179SRui Paulo  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13d4886179SRui Paulo  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14d4886179SRui Paulo  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15d4886179SRui Paulo  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16d4886179SRui Paulo  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17d4886179SRui Paulo  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18d4886179SRui Paulo  */
19d4886179SRui Paulo 
20d4886179SRui Paulo /*-
21d4886179SRui Paulo  * Based on BSD-licensed source modules in the Linux iwlwifi driver,
22d4886179SRui Paulo  * which were used as the reference documentation for this implementation.
23d4886179SRui Paulo  *
24d4886179SRui Paulo  * Driver version we are currently based off of is
25d4886179SRui Paulo  * Linux 3.14.3 (tag id a2df521e42b1d9a23f620ac79dbfe8655a8391dd)
26d4886179SRui Paulo  *
27d4886179SRui Paulo  ***********************************************************************
28d4886179SRui Paulo  *
29d4886179SRui Paulo  * This file is provided under a dual BSD/GPLv2 license.  When using or
30d4886179SRui Paulo  * redistributing this file, you may do so under either license.
31d4886179SRui Paulo  *
32d4886179SRui Paulo  * GPL LICENSE SUMMARY
33d4886179SRui Paulo  *
34d4886179SRui Paulo  * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
35d4886179SRui Paulo  *
36d4886179SRui Paulo  * This program is free software; you can redistribute it and/or modify
37d4886179SRui Paulo  * it under the terms of version 2 of the GNU General Public License as
38d4886179SRui Paulo  * published by the Free Software Foundation.
39d4886179SRui Paulo  *
40d4886179SRui Paulo  * This program is distributed in the hope that it will be useful, but
41d4886179SRui Paulo  * WITHOUT ANY WARRANTY; without even the implied warranty of
42d4886179SRui Paulo  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
43d4886179SRui Paulo  * General Public License for more details.
44d4886179SRui Paulo  *
45d4886179SRui Paulo  * You should have received a copy of the GNU General Public License
46d4886179SRui Paulo  * along with this program; if not, write to the Free Software
47d4886179SRui Paulo  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
48d4886179SRui Paulo  * USA
49d4886179SRui Paulo  *
50d4886179SRui Paulo  * The full GNU General Public License is included in this distribution
51d4886179SRui Paulo  * in the file called COPYING.
52d4886179SRui Paulo  *
53d4886179SRui Paulo  * Contact Information:
54d4886179SRui Paulo  *  Intel Linux Wireless <ilw@linux.intel.com>
55d4886179SRui Paulo  * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
56d4886179SRui Paulo  *
57d4886179SRui Paulo  *
58d4886179SRui Paulo  * BSD LICENSE
59d4886179SRui Paulo  *
60d4886179SRui Paulo  * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
61d4886179SRui Paulo  * All rights reserved.
62d4886179SRui Paulo  *
63d4886179SRui Paulo  * Redistribution and use in source and binary forms, with or without
64d4886179SRui Paulo  * modification, are permitted provided that the following conditions
65d4886179SRui Paulo  * are met:
66d4886179SRui Paulo  *
67d4886179SRui Paulo  *  * Redistributions of source code must retain the above copyright
68d4886179SRui Paulo  *    notice, this list of conditions and the following disclaimer.
69d4886179SRui Paulo  *  * Redistributions in binary form must reproduce the above copyright
70d4886179SRui Paulo  *    notice, this list of conditions and the following disclaimer in
71d4886179SRui Paulo  *    the documentation and/or other materials provided with the
72d4886179SRui Paulo  *    distribution.
73d4886179SRui Paulo  *  * Neither the name Intel Corporation nor the names of its
74d4886179SRui Paulo  *    contributors may be used to endorse or promote products derived
75d4886179SRui Paulo  *    from this software without specific prior written permission.
76d4886179SRui Paulo  *
77d4886179SRui Paulo  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
78d4886179SRui Paulo  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
79d4886179SRui Paulo  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
80d4886179SRui Paulo  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
81d4886179SRui Paulo  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
82d4886179SRui Paulo  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
83d4886179SRui Paulo  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
84d4886179SRui Paulo  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
85d4886179SRui Paulo  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
86d4886179SRui Paulo  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
87d4886179SRui Paulo  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
88d4886179SRui Paulo  */
89d4886179SRui Paulo 
90d4886179SRui Paulo /*-
91d4886179SRui Paulo  * Copyright (c) 2007-2010 Damien Bergamini <damien.bergamini@free.fr>
92d4886179SRui Paulo  *
93d4886179SRui Paulo  * Permission to use, copy, modify, and distribute this software for any
94d4886179SRui Paulo  * purpose with or without fee is hereby granted, provided that the above
95d4886179SRui Paulo  * copyright notice and this permission notice appear in all copies.
96d4886179SRui Paulo  *
97d4886179SRui Paulo  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
98d4886179SRui Paulo  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
99d4886179SRui Paulo  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
100d4886179SRui Paulo  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
101d4886179SRui Paulo  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
102d4886179SRui Paulo  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
103d4886179SRui Paulo  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
104d4886179SRui Paulo  */
105d4886179SRui Paulo #include <sys/cdefs.h>
106b789292fSAndriy Voskoboinyk #include "opt_wlan.h"
107616201d1SAdrian Chadd #include "opt_iwm.h"
108b789292fSAndriy Voskoboinyk 
109d4886179SRui Paulo #include <sys/param.h>
110d4886179SRui Paulo #include <sys/bus.h>
111d4886179SRui Paulo #include <sys/conf.h>
112d4886179SRui Paulo #include <sys/endian.h>
113d4886179SRui Paulo #include <sys/firmware.h>
114d4886179SRui Paulo #include <sys/kernel.h>
115d4886179SRui Paulo #include <sys/malloc.h>
116d4886179SRui Paulo #include <sys/mbuf.h>
117d4886179SRui Paulo #include <sys/mutex.h>
118d4886179SRui Paulo #include <sys/module.h>
119d4886179SRui Paulo #include <sys/proc.h>
120d4886179SRui Paulo #include <sys/rman.h>
121d4886179SRui Paulo #include <sys/socket.h>
122d4886179SRui Paulo #include <sys/sockio.h>
123d4886179SRui Paulo #include <sys/sysctl.h>
124d4886179SRui Paulo #include <sys/linker.h>
125d4886179SRui Paulo 
126d4886179SRui Paulo #include <machine/bus.h>
127d4886179SRui Paulo #include <machine/endian.h>
128d4886179SRui Paulo #include <machine/resource.h>
129d4886179SRui Paulo 
130d4886179SRui Paulo #include <dev/pci/pcivar.h>
131d4886179SRui Paulo #include <dev/pci/pcireg.h>
132d4886179SRui Paulo 
133d4886179SRui Paulo #include <net/bpf.h>
134d4886179SRui Paulo 
135d4886179SRui Paulo #include <net/if.h>
136d4886179SRui Paulo #include <net/if_var.h>
137d4886179SRui Paulo #include <net/if_arp.h>
138d4886179SRui Paulo #include <net/if_dl.h>
139d4886179SRui Paulo #include <net/if_media.h>
140d4886179SRui Paulo #include <net/if_types.h>
141d4886179SRui Paulo 
142d4886179SRui Paulo #include <netinet/in.h>
143d4886179SRui Paulo #include <netinet/in_systm.h>
144d4886179SRui Paulo #include <netinet/if_ether.h>
145d4886179SRui Paulo #include <netinet/ip.h>
146d4886179SRui Paulo 
147d4886179SRui Paulo #include <net80211/ieee80211_var.h>
148d4886179SRui Paulo #include <net80211/ieee80211_regdomain.h>
149d4886179SRui Paulo #include <net80211/ieee80211_ratectl.h>
150d4886179SRui Paulo #include <net80211/ieee80211_radiotap.h>
151d4886179SRui Paulo 
15249fdbf0aSRui Paulo #include <dev/iwm/if_iwmreg.h>
15349fdbf0aSRui Paulo #include <dev/iwm/if_iwmvar.h>
15449fdbf0aSRui Paulo #include <dev/iwm/if_iwm_debug.h>
15549fdbf0aSRui Paulo #include <dev/iwm/if_iwm_util.h>
1569a949c99SKyle Evans #include <dev/iwm/if_iwm_notif_wait.h>
15749fdbf0aSRui Paulo #include <dev/iwm/if_iwm_pcie_trans.h>
15849fdbf0aSRui Paulo #include <dev/iwm/if_iwm_time_event.h>
159d4886179SRui Paulo 
160df34d80aSKyle Evans #define TU_TO_HZ(tu)	(((uint64_t)(tu) * 1024 * hz) / 1000000)
161df34d80aSKyle Evans 
162df34d80aSKyle Evans static void
iwm_te_clear_data(struct iwm_softc * sc)163e7065dd1SMark Johnston iwm_te_clear_data(struct iwm_softc *sc)
164df34d80aSKyle Evans {
165df34d80aSKyle Evans 	sc->sc_time_event_uid = 0;
166df34d80aSKyle Evans 	sc->sc_time_event_duration = 0;
167df34d80aSKyle Evans 	sc->sc_time_event_end_ticks = 0;
168df34d80aSKyle Evans 	sc->sc_flags &= ~IWM_FLAG_TE_ACTIVE;
169df34d80aSKyle Evans }
170df34d80aSKyle Evans 
171d4886179SRui Paulo /*
172df34d80aSKyle Evans  * Handles a FW notification for an event that is known to the driver.
173df34d80aSKyle Evans  *
174df34d80aSKyle Evans  * @mvm: the mvm component
175df34d80aSKyle Evans  * @te_data: the time event data
176df34d80aSKyle Evans  * @notif: the notification data corresponding the time event data.
177d4886179SRui Paulo  */
178df34d80aSKyle Evans static void
iwm_te_handle_notif(struct iwm_softc * sc,struct iwm_time_event_notif * notif)179e7065dd1SMark Johnston iwm_te_handle_notif(struct iwm_softc *sc,
180df34d80aSKyle Evans     struct iwm_time_event_notif *notif)
181df34d80aSKyle Evans {
182df34d80aSKyle Evans 	IWM_DPRINTF(sc, IWM_DEBUG_TE,
183df34d80aSKyle Evans 	    "Handle time event notif - UID = 0x%x action %d\n",
184df34d80aSKyle Evans 	    le32toh(notif->unique_id),
185df34d80aSKyle Evans 	    le32toh(notif->action));
186df34d80aSKyle Evans 
187df34d80aSKyle Evans 	if (!le32toh(notif->status)) {
188*f7daf710SMateusz Guzik #ifdef IWM_DEBUG
189df34d80aSKyle Evans 		const char *msg;
190df34d80aSKyle Evans 
191df34d80aSKyle Evans 		if (notif->action & htole32(IWM_TE_V2_NOTIF_HOST_EVENT_START))
192df34d80aSKyle Evans 			msg = "Time Event start notification failure";
193df34d80aSKyle Evans 		else
194df34d80aSKyle Evans 			msg = "Time Event end notification failure";
195df34d80aSKyle Evans 
196df34d80aSKyle Evans 		IWM_DPRINTF(sc, IWM_DEBUG_TE, "%s\n", msg);
197*f7daf710SMateusz Guzik #endif
198df34d80aSKyle Evans 	}
199df34d80aSKyle Evans 
200df34d80aSKyle Evans 	if (le32toh(notif->action) & IWM_TE_V2_NOTIF_HOST_EVENT_END) {
201df34d80aSKyle Evans 		IWM_DPRINTF(sc, IWM_DEBUG_TE,
202df34d80aSKyle Evans 		    "TE ended - current time %d, estimated end %d\n",
203df34d80aSKyle Evans 		    ticks, sc->sc_time_event_end_ticks);
204df34d80aSKyle Evans 
205e7065dd1SMark Johnston 		iwm_te_clear_data(sc);
206df34d80aSKyle Evans 	} else if (le32toh(notif->action) & IWM_TE_V2_NOTIF_HOST_EVENT_START) {
207df34d80aSKyle Evans 		sc->sc_time_event_end_ticks =
208df34d80aSKyle Evans 		    ticks + TU_TO_HZ(sc->sc_time_event_duration);
209df34d80aSKyle Evans 	} else {
210df34d80aSKyle Evans 		device_printf(sc->sc_dev, "Got TE with unknown action\n");
211df34d80aSKyle Evans 	}
212df34d80aSKyle Evans }
213df34d80aSKyle Evans 
214df34d80aSKyle Evans /*
215df34d80aSKyle Evans  * The Rx handler for time event notifications
216df34d80aSKyle Evans  */
217df34d80aSKyle Evans void
iwm_rx_time_event_notif(struct iwm_softc * sc,struct iwm_rx_packet * pkt)218e7065dd1SMark Johnston iwm_rx_time_event_notif(struct iwm_softc *sc, struct iwm_rx_packet *pkt)
219df34d80aSKyle Evans {
220df34d80aSKyle Evans 	struct iwm_time_event_notif *notif = (void *)pkt->data;
221df34d80aSKyle Evans 
222df34d80aSKyle Evans 	IWM_DPRINTF(sc, IWM_DEBUG_TE,
223df34d80aSKyle Evans 	    "Time event notification - UID = 0x%x action %d\n",
224df34d80aSKyle Evans 	    le32toh(notif->unique_id),
225df34d80aSKyle Evans 	    le32toh(notif->action));
226df34d80aSKyle Evans 
227e7065dd1SMark Johnston 	iwm_te_handle_notif(sc, notif);
228df34d80aSKyle Evans }
229d4886179SRui Paulo 
230d4886179SRui Paulo static int
iwm_te_notif(struct iwm_softc * sc,struct iwm_rx_packet * pkt,void * data)231e7065dd1SMark Johnston iwm_te_notif(struct iwm_softc *sc, struct iwm_rx_packet *pkt,
2329a949c99SKyle Evans     void *data)
2339a949c99SKyle Evans {
2349a949c99SKyle Evans 	struct iwm_time_event_notif *resp;
2359a949c99SKyle Evans 	int resp_len = iwm_rx_packet_payload_len(pkt);
2369a949c99SKyle Evans 
2379a949c99SKyle Evans 	if (pkt->hdr.code != IWM_TIME_EVENT_NOTIFICATION ||
2389a949c99SKyle Evans 	    resp_len != sizeof(*resp)) {
2399a949c99SKyle Evans 		IWM_DPRINTF(sc, IWM_DEBUG_TE,
2409a949c99SKyle Evans 		    "Invalid TIME_EVENT_NOTIFICATION response\n");
2419a949c99SKyle Evans 		return 1;
2429a949c99SKyle Evans 	}
2439a949c99SKyle Evans 
2449a949c99SKyle Evans 	resp = (void *)pkt->data;
2459a949c99SKyle Evans 
2469a949c99SKyle Evans 	/* te_data->uid is already set in the TIME_EVENT_CMD response */
2479a949c99SKyle Evans 	if (le32toh(resp->unique_id) != sc->sc_time_event_uid)
2489a949c99SKyle Evans 		return false;
2499a949c99SKyle Evans 
2509a949c99SKyle Evans 	IWM_DPRINTF(sc, IWM_DEBUG_TE,
2519a949c99SKyle Evans 	    "TIME_EVENT_NOTIFICATION response - UID = 0x%x\n",
2529a949c99SKyle Evans 	    sc->sc_time_event_uid);
2539a949c99SKyle Evans 	if (!resp->status) {
2549a949c99SKyle Evans 		IWM_DPRINTF(sc, IWM_DEBUG_TE,
2559a949c99SKyle Evans 		    "TIME_EVENT_NOTIFICATION received but not executed\n");
2569a949c99SKyle Evans 	}
2579a949c99SKyle Evans 
2589a949c99SKyle Evans 	return 1;
2599a949c99SKyle Evans }
2609a949c99SKyle Evans 
2619a949c99SKyle Evans static int
iwm_time_event_response(struct iwm_softc * sc,struct iwm_rx_packet * pkt,void * data)262e7065dd1SMark Johnston iwm_time_event_response(struct iwm_softc *sc, struct iwm_rx_packet *pkt,
2639a949c99SKyle Evans     void *data)
2649a949c99SKyle Evans {
2659a949c99SKyle Evans 	struct iwm_time_event_resp *resp;
2669a949c99SKyle Evans 	int resp_len = iwm_rx_packet_payload_len(pkt);
2679a949c99SKyle Evans 
2689a949c99SKyle Evans 	if (pkt->hdr.code != IWM_TIME_EVENT_CMD ||
2699a949c99SKyle Evans 	    resp_len != sizeof(*resp)) {
2709a949c99SKyle Evans 		IWM_DPRINTF(sc, IWM_DEBUG_TE,
2719a949c99SKyle Evans 		    "Invalid TIME_EVENT_CMD response\n");
2729a949c99SKyle Evans 		return 1;
2739a949c99SKyle Evans 	}
2749a949c99SKyle Evans 
2759a949c99SKyle Evans 	resp = (void *)pkt->data;
2769a949c99SKyle Evans 
2779a949c99SKyle Evans 	/* we should never get a response to another TIME_EVENT_CMD here */
2789a949c99SKyle Evans 	if (le32toh(resp->id) != IWM_TE_BSS_STA_AGGRESSIVE_ASSOC) {
2799a949c99SKyle Evans 		IWM_DPRINTF(sc, IWM_DEBUG_TE,
2809a949c99SKyle Evans 		    "Got TIME_EVENT_CMD response with wrong id: %d\n",
2819a949c99SKyle Evans 		    le32toh(resp->id));
2829a949c99SKyle Evans 		return 0;
2839a949c99SKyle Evans 	}
2849a949c99SKyle Evans 
2859a949c99SKyle Evans 	sc->sc_time_event_uid = le32toh(resp->unique_id);
2869a949c99SKyle Evans 	IWM_DPRINTF(sc, IWM_DEBUG_TE,
2879a949c99SKyle Evans 	    "TIME_EVENT_CMD response - UID = 0x%x\n", sc->sc_time_event_uid);
2889a949c99SKyle Evans 	return 1;
2899a949c99SKyle Evans }
2909a949c99SKyle Evans 
2919a949c99SKyle Evans 
2929a949c99SKyle Evans /* XXX Use the te_data function argument properly, like in iwlwifi's code. */
2939a949c99SKyle Evans 
2949a949c99SKyle Evans static int
iwm_time_event_send_add(struct iwm_softc * sc,struct iwm_vap * ivp,void * te_data,struct iwm_time_event_cmd * te_cmd)295e7065dd1SMark Johnston iwm_time_event_send_add(struct iwm_softc *sc, struct iwm_vap *ivp,
296d045c744SAdrian Chadd 	void *te_data, struct iwm_time_event_cmd *te_cmd)
297d4886179SRui Paulo {
2989a949c99SKyle Evans 	static const uint16_t time_event_response[] = { IWM_TIME_EVENT_CMD };
2999a949c99SKyle Evans 	struct iwm_notification_wait wait_time_event;
300d4886179SRui Paulo 	int ret;
301d4886179SRui Paulo 
3029a949c99SKyle Evans 	IWM_DPRINTF(sc, IWM_DEBUG_TE,
303d4886179SRui Paulo 	    "Add new TE, duration %d TU\n", le32toh(te_cmd->duration));
304d4886179SRui Paulo 
305df34d80aSKyle Evans 	sc->sc_time_event_duration = le32toh(te_cmd->duration);
306df34d80aSKyle Evans 
3079a949c99SKyle Evans 	/*
3089a949c99SKyle Evans 	 * Use a notification wait, which really just processes the
3099a949c99SKyle Evans 	 * command response and doesn't wait for anything, in order
3109a949c99SKyle Evans 	 * to be able to process the response and get the UID inside
3119a949c99SKyle Evans 	 * the RX path. Using CMD_WANT_SKB doesn't work because it
3129a949c99SKyle Evans 	 * stores the buffer and then wakes up this thread, by which
3139a949c99SKyle Evans 	 * time another notification (that the time event started)
3149a949c99SKyle Evans 	 * might already be processed unsuccessfully.
3159a949c99SKyle Evans 	 */
3169a949c99SKyle Evans 	iwm_init_notification_wait(sc->sc_notif_wait, &wait_time_event,
3179a949c99SKyle Evans 				   time_event_response,
3189a949c99SKyle Evans 				   nitems(time_event_response),
319e7065dd1SMark Johnston 				   iwm_time_event_response, /*te_data*/NULL);
3209a949c99SKyle Evans 
321e7065dd1SMark Johnston 	ret = iwm_send_cmd_pdu(sc, IWM_TIME_EVENT_CMD, 0, sizeof(*te_cmd),
3229a949c99SKyle Evans 	    te_cmd);
323d4886179SRui Paulo 	if (ret) {
3249a949c99SKyle Evans 		IWM_DPRINTF(sc, IWM_DEBUG_TE,
325d4886179SRui Paulo 		    "%s: Couldn't send IWM_TIME_EVENT_CMD: %d\n",
326d4886179SRui Paulo 		    __func__, ret);
3279a949c99SKyle Evans 		iwm_remove_notification(sc->sc_notif_wait, &wait_time_event);
3289a949c99SKyle Evans 		return ret;
3299a949c99SKyle Evans 	}
3309a949c99SKyle Evans 
3319a949c99SKyle Evans 	/* No need to wait for anything, so just pass 1 (0 isn't valid) */
3329a949c99SKyle Evans 	IWM_UNLOCK(sc);
3339a949c99SKyle Evans 	ret = iwm_wait_notification(sc->sc_notif_wait, &wait_time_event, 1);
3349a949c99SKyle Evans 	IWM_LOCK(sc);
3359a949c99SKyle Evans 	/* should never fail */
3369a949c99SKyle Evans 	if (ret) {
3379a949c99SKyle Evans 		IWM_DPRINTF(sc, IWM_DEBUG_TE,
3389a949c99SKyle Evans 		    "%s: Failed to get response for IWM_TIME_EVENT_CMD: %d\n",
3399a949c99SKyle Evans 		    __func__, ret);
340d4886179SRui Paulo 	}
341d4886179SRui Paulo 
342d4886179SRui Paulo 	return ret;
343d4886179SRui Paulo }
344d4886179SRui Paulo 
345d4886179SRui Paulo void
iwm_protect_session(struct iwm_softc * sc,struct iwm_vap * ivp,uint32_t duration,uint32_t max_delay,boolean_t wait_for_notif)346e7065dd1SMark Johnston iwm_protect_session(struct iwm_softc *sc, struct iwm_vap *ivp,
3479a949c99SKyle Evans 	uint32_t duration, uint32_t max_delay, boolean_t wait_for_notif)
348d4886179SRui Paulo {
3499a949c99SKyle Evans 	const uint16_t te_notif_response[] = { IWM_TIME_EVENT_NOTIFICATION };
3509a949c99SKyle Evans 	struct iwm_notification_wait wait_te_notif;
351d045c744SAdrian Chadd 	struct iwm_time_event_cmd time_cmd = {};
352d4886179SRui Paulo 
3539a949c99SKyle Evans 	/* Do nothing if a time event is already scheduled. */
3549a949c99SKyle Evans 	if (sc->sc_flags & IWM_FLAG_TE_ACTIVE)
3559a949c99SKyle Evans 		return;
3569a949c99SKyle Evans 
357d4886179SRui Paulo 	time_cmd.action = htole32(IWM_FW_CTXT_ACTION_ADD);
358d4886179SRui Paulo 	time_cmd.id_and_color =
359bdba6830SAdrian Chadd 	    htole32(IWM_FW_CMD_ID_AND_COLOR(ivp->id, ivp->color));
360d4886179SRui Paulo 	time_cmd.id = htole32(IWM_TE_BSS_STA_AGGRESSIVE_ASSOC);
361d4886179SRui Paulo 
3626a5bc1d1SSean Bruno 	time_cmd.apply_time = htole32(0);
363d4886179SRui Paulo 
364d4886179SRui Paulo 	time_cmd.max_frags = IWM_TE_V2_FRAG_NONE;
365d4886179SRui Paulo 	time_cmd.max_delay = htole32(max_delay);
366d4886179SRui Paulo 	/* TODO: why do we need to interval = bi if it is not periodic? */
367d4886179SRui Paulo 	time_cmd.interval = htole32(1);
368d4886179SRui Paulo 	time_cmd.duration = htole32(duration);
369d4886179SRui Paulo 	time_cmd.repeat = 1;
370d4886179SRui Paulo 	time_cmd.policy
371b47237ceSImre Vadász 	    = htole16(IWM_TE_V2_NOTIF_HOST_EVENT_START |
3726a5bc1d1SSean Bruno 	        IWM_TE_V2_NOTIF_HOST_EVENT_END |
3736a5bc1d1SSean Bruno 		IWM_T2_V2_START_IMMEDIATELY);
374d4886179SRui Paulo 
3759a949c99SKyle Evans 	if (!wait_for_notif) {
376e7065dd1SMark Johnston 		iwm_time_event_send_add(sc, ivp, /*te_data*/NULL, &time_cmd);
3779a949c99SKyle Evans 		DELAY(100);
3789a949c99SKyle Evans 		sc->sc_flags |= IWM_FLAG_TE_ACTIVE;
3799a949c99SKyle Evans 		return;
3809a949c99SKyle Evans 	}
3819a949c99SKyle Evans 
3829a949c99SKyle Evans 	/*
3839a949c99SKyle Evans 	 * Create notification_wait for the TIME_EVENT_NOTIFICATION to use
3849a949c99SKyle Evans 	 * right after we send the time event
3859a949c99SKyle Evans 	 */
3869a949c99SKyle Evans 	iwm_init_notification_wait(sc->sc_notif_wait, &wait_te_notif,
3879a949c99SKyle Evans 	    te_notif_response, nitems(te_notif_response),
388e7065dd1SMark Johnston 	    iwm_te_notif, /*te_data*/NULL);
3899a949c99SKyle Evans 
3909a949c99SKyle Evans 	/* If TE was sent OK - wait for the notification that started */
391e7065dd1SMark Johnston 	if (iwm_time_event_send_add(sc, ivp, /*te_data*/NULL, &time_cmd)) {
3929a949c99SKyle Evans 		IWM_DPRINTF(sc, IWM_DEBUG_TE,
3939a949c99SKyle Evans 		    "%s: Failed to add TE to protect session\n", __func__);
3949a949c99SKyle Evans 		iwm_remove_notification(sc->sc_notif_wait, &wait_te_notif);
3959a949c99SKyle Evans 	} else {
3969a949c99SKyle Evans 		sc->sc_flags |= IWM_FLAG_TE_ACTIVE;
3979a949c99SKyle Evans 		IWM_UNLOCK(sc);
3989a949c99SKyle Evans 		if (iwm_wait_notification(sc->sc_notif_wait, &wait_te_notif,
3999a949c99SKyle Evans 		    TU_TO_HZ(max_delay))) {
4009a949c99SKyle Evans 			IWM_DPRINTF(sc, IWM_DEBUG_TE,
4019a949c99SKyle Evans 			    "%s: Failed to protect session until TE\n",
4029a949c99SKyle Evans 			    __func__);
4039a949c99SKyle Evans 		}
4049a949c99SKyle Evans 		IWM_LOCK(sc);
4059a949c99SKyle Evans 	}
4069a949c99SKyle Evans }
4079a949c99SKyle Evans 
4089a949c99SKyle Evans void
iwm_stop_session_protection(struct iwm_softc * sc,struct iwm_vap * ivp)409e7065dd1SMark Johnston iwm_stop_session_protection(struct iwm_softc *sc, struct iwm_vap *ivp)
4109a949c99SKyle Evans {
4119a949c99SKyle Evans 	struct iwm_time_event_cmd time_cmd = {};
4129a949c99SKyle Evans 
4139a949c99SKyle Evans 	/* Do nothing if the time event has already ended. */
4149a949c99SKyle Evans 	if ((sc->sc_flags & IWM_FLAG_TE_ACTIVE) == 0)
4159a949c99SKyle Evans 		return;
4169a949c99SKyle Evans 
4179a949c99SKyle Evans 	time_cmd.id = htole32(sc->sc_time_event_uid);
4189a949c99SKyle Evans 	time_cmd.action = htole32(IWM_FW_CTXT_ACTION_REMOVE);
4199a949c99SKyle Evans 	time_cmd.id_and_color =
4209a949c99SKyle Evans 	    htole32(IWM_FW_CMD_ID_AND_COLOR(ivp->id, ivp->color));
4219a949c99SKyle Evans 
4229a949c99SKyle Evans 	IWM_DPRINTF(sc, IWM_DEBUG_TE,
4239a949c99SKyle Evans 	    "%s: Removing TE 0x%x\n", __func__, le32toh(time_cmd.id));
424e7065dd1SMark Johnston 	if (iwm_send_cmd_pdu(sc, IWM_TIME_EVENT_CMD, 0, sizeof(time_cmd),
4259a949c99SKyle Evans 	    &time_cmd) == 0)
426e7065dd1SMark Johnston 		iwm_te_clear_data(sc);
4279a949c99SKyle Evans 
4289a949c99SKyle Evans 	DELAY(100);
429d4886179SRui Paulo }
430