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