xref: /freebsd/sys/dev/iwx/if_iwx.c (revision 8d51f2aba9ade8cfba84f18cfb0e20f321ae89c2)
12ad0f7e9STom Jones /*-
22ad0f7e9STom Jones  * SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) AND ISC
32ad0f7e9STom Jones  */
42ad0f7e9STom Jones 
52ad0f7e9STom Jones /*	$OpenBSD: if_iwx.c,v 1.175 2023/07/05 15:07:28 stsp Exp $	*/
62ad0f7e9STom Jones 
72ad0f7e9STom Jones /*
82ad0f7e9STom Jones  *
92ad0f7e9STom Jones  * Copyright (c) 2025 The FreeBSD Foundation
102ad0f7e9STom Jones  *
112ad0f7e9STom Jones  * Portions of this software were developed by Tom Jones <thj@FreeBSD.org>
122ad0f7e9STom Jones  * under sponsorship from the FreeBSD Foundation.
132ad0f7e9STom Jones  *
142ad0f7e9STom Jones  * Permission to use, copy, modify, and distribute this software for any
152ad0f7e9STom Jones  * purpose with or without fee is hereby granted, provided that the above
162ad0f7e9STom Jones  * copyright notice and this permission notice appear in all copies.
172ad0f7e9STom Jones  *
182ad0f7e9STom Jones  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
192ad0f7e9STom Jones  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
202ad0f7e9STom Jones  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
212ad0f7e9STom Jones  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
222ad0f7e9STom Jones  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
232ad0f7e9STom Jones  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
242ad0f7e9STom Jones  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
252ad0f7e9STom Jones  *
262ad0f7e9STom Jones  */
272ad0f7e9STom Jones 
282ad0f7e9STom Jones /*-
292ad0f7e9STom Jones  * Copyright (c) 2024 Future Crew, LLC
302ad0f7e9STom Jones  *   Author: Mikhail Pchelin <misha@FreeBSD.org>
312ad0f7e9STom Jones  *
322ad0f7e9STom Jones  * Permission to use, copy, modify, and distribute this software for any
332ad0f7e9STom Jones  * purpose with or without fee is hereby granted, provided that the above
342ad0f7e9STom Jones  * copyright notice and this permission notice appear in all copies.
352ad0f7e9STom Jones  *
362ad0f7e9STom Jones  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
372ad0f7e9STom Jones  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
382ad0f7e9STom Jones  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
392ad0f7e9STom Jones  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
402ad0f7e9STom Jones  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
412ad0f7e9STom Jones  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
422ad0f7e9STom Jones  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
432ad0f7e9STom Jones  */
442ad0f7e9STom Jones 
452ad0f7e9STom Jones /*
462ad0f7e9STom Jones  * Copyright (c) 2014, 2016 genua gmbh <info@genua.de>
472ad0f7e9STom Jones  *   Author: Stefan Sperling <stsp@openbsd.org>
482ad0f7e9STom Jones  * Copyright (c) 2014 Fixup Software Ltd.
492ad0f7e9STom Jones  * Copyright (c) 2017, 2019, 2020 Stefan Sperling <stsp@openbsd.org>
502ad0f7e9STom Jones  *
512ad0f7e9STom Jones  * Permission to use, copy, modify, and distribute this software for any
522ad0f7e9STom Jones  * purpose with or without fee is hereby granted, provided that the above
532ad0f7e9STom Jones  * copyright notice and this permission notice appear in all copies.
542ad0f7e9STom Jones  *
552ad0f7e9STom Jones  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
562ad0f7e9STom Jones  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
572ad0f7e9STom Jones  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
582ad0f7e9STom Jones  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
592ad0f7e9STom Jones  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
602ad0f7e9STom Jones  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
612ad0f7e9STom Jones  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
622ad0f7e9STom Jones  */
632ad0f7e9STom Jones 
642ad0f7e9STom Jones /*-
652ad0f7e9STom Jones  * Based on BSD-licensed source modules in the Linux iwlwifi driver,
662ad0f7e9STom Jones  * which were used as the reference documentation for this implementation.
672ad0f7e9STom Jones  *
682ad0f7e9STom Jones  ******************************************************************************
692ad0f7e9STom Jones  *
702ad0f7e9STom Jones  * This file is provided under a dual BSD/GPLv2 license.  When using or
712ad0f7e9STom Jones  * redistributing this file, you may do so under either license.
722ad0f7e9STom Jones  *
732ad0f7e9STom Jones  * GPL LICENSE SUMMARY
742ad0f7e9STom Jones  *
752ad0f7e9STom Jones  * Copyright(c) 2017 Intel Deutschland GmbH
762ad0f7e9STom Jones  * Copyright(c) 2018 - 2019 Intel Corporation
772ad0f7e9STom Jones  *
782ad0f7e9STom Jones  * This program is free software; you can redistribute it and/or modify
792ad0f7e9STom Jones  * it under the terms of version 2 of the GNU General Public License as
802ad0f7e9STom Jones  * published by the Free Software Foundation.
812ad0f7e9STom Jones  *
822ad0f7e9STom Jones  * This program is distributed in the hope that it will be useful, but
832ad0f7e9STom Jones  * WITHOUT ANY WARRANTY; without even the implied warranty of
842ad0f7e9STom Jones  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
852ad0f7e9STom Jones  * General Public License for more details.
862ad0f7e9STom Jones  *
872ad0f7e9STom Jones  * BSD LICENSE
882ad0f7e9STom Jones  *
892ad0f7e9STom Jones  * Copyright(c) 2017 Intel Deutschland GmbH
902ad0f7e9STom Jones  * Copyright(c) 2018 - 2019 Intel Corporation
912ad0f7e9STom Jones  * All rights reserved.
922ad0f7e9STom Jones  *
932ad0f7e9STom Jones  * Redistribution and use in source and binary forms, with or without
942ad0f7e9STom Jones  * modification, are permitted provided that the following conditions
952ad0f7e9STom Jones  * are met:
962ad0f7e9STom Jones  *
972ad0f7e9STom Jones  *  * Redistributions of source code must retain the above copyright
982ad0f7e9STom Jones  *    notice, this list of conditions and the following disclaimer.
992ad0f7e9STom Jones  *  * Redistributions in binary form must reproduce the above copyright
1002ad0f7e9STom Jones  *    notice, this list of conditions and the following disclaimer in
1012ad0f7e9STom Jones  *    the documentation and/or other materials provided with the
1022ad0f7e9STom Jones  *    distribution.
1032ad0f7e9STom Jones  *  * Neither the name Intel Corporation nor the names of its
1042ad0f7e9STom Jones  *    contributors may be used to endorse or promote products derived
1052ad0f7e9STom Jones  *    from this software without specific prior written permission.
1062ad0f7e9STom Jones  *
1072ad0f7e9STom Jones  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1082ad0f7e9STom Jones  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1092ad0f7e9STom Jones  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1102ad0f7e9STom Jones  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1112ad0f7e9STom Jones  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1122ad0f7e9STom Jones  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1132ad0f7e9STom Jones  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1142ad0f7e9STom Jones  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1152ad0f7e9STom Jones  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1162ad0f7e9STom Jones  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1172ad0f7e9STom Jones  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1182ad0f7e9STom Jones  *
1192ad0f7e9STom Jones  *****************************************************************************
1202ad0f7e9STom Jones  */
1212ad0f7e9STom Jones 
1222ad0f7e9STom Jones /*-
1232ad0f7e9STom Jones  * Copyright (c) 2007-2010 Damien Bergamini <damien.bergamini@free.fr>
1242ad0f7e9STom Jones  *
1252ad0f7e9STom Jones  * Permission to use, copy, modify, and distribute this software for any
1262ad0f7e9STom Jones  * purpose with or without fee is hereby granted, provided that the above
1272ad0f7e9STom Jones  * copyright notice and this permission notice appear in all copies.
1282ad0f7e9STom Jones  *
1292ad0f7e9STom Jones  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1302ad0f7e9STom Jones  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1312ad0f7e9STom Jones  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1322ad0f7e9STom Jones  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1332ad0f7e9STom Jones  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1342ad0f7e9STom Jones  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1352ad0f7e9STom Jones  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1362ad0f7e9STom Jones  */
1372ad0f7e9STom Jones 
1382ad0f7e9STom Jones #include <sys/param.h>
1392ad0f7e9STom Jones #include <sys/bus.h>
1402ad0f7e9STom Jones #include <sys/module.h>
1412ad0f7e9STom Jones #include <sys/conf.h>
1422ad0f7e9STom Jones #include <sys/kernel.h>
1432ad0f7e9STom Jones #include <sys/malloc.h>
1442ad0f7e9STom Jones #include <sys/mbuf.h>
1452ad0f7e9STom Jones #include <sys/mutex.h>
1462ad0f7e9STom Jones #include <sys/proc.h>
1472ad0f7e9STom Jones #include <sys/rman.h>
1482ad0f7e9STom Jones #include <sys/rwlock.h>
1492ad0f7e9STom Jones #include <sys/socket.h>
1502ad0f7e9STom Jones #include <sys/sockio.h>
1512ad0f7e9STom Jones #include <sys/systm.h>
1522ad0f7e9STom Jones #include <sys/endian.h>
1532ad0f7e9STom Jones #include <sys/linker.h>
1542ad0f7e9STom Jones #include <sys/firmware.h>
1552ad0f7e9STom Jones #include <sys/epoch.h>
1562ad0f7e9STom Jones #include <sys/kdb.h>
1572ad0f7e9STom Jones 
1582ad0f7e9STom Jones #include <machine/bus.h>
1592ad0f7e9STom Jones #include <machine/endian.h>
1602ad0f7e9STom Jones #include <machine/resource.h>
1612ad0f7e9STom Jones 
1622ad0f7e9STom Jones #include <dev/pci/pcireg.h>
1632ad0f7e9STom Jones #include <dev/pci/pcivar.h>
1642ad0f7e9STom Jones 
1652ad0f7e9STom Jones #include <net/bpf.h>
1662ad0f7e9STom Jones 
1672ad0f7e9STom Jones #include <net/if.h>
1682ad0f7e9STom Jones #include <net/if_var.h>
1692ad0f7e9STom Jones #include <net/if_dl.h>
1702ad0f7e9STom Jones #include <net/if_media.h>
1712ad0f7e9STom Jones 
1722ad0f7e9STom Jones #include <netinet/in.h>
1732ad0f7e9STom Jones #include <netinet/if_ether.h>
1742ad0f7e9STom Jones 
1752ad0f7e9STom Jones #include <net80211/ieee80211_var.h>
1762ad0f7e9STom Jones #include <net80211/ieee80211_radiotap.h>
1772ad0f7e9STom Jones #include <net80211/ieee80211_regdomain.h>
1782ad0f7e9STom Jones #include <net80211/ieee80211_ratectl.h>
1792ad0f7e9STom Jones #include <net80211/ieee80211_vht.h>
1802ad0f7e9STom Jones 
1812ad0f7e9STom Jones int iwx_himark = 224;
1822ad0f7e9STom Jones int iwx_lomark = 192;
1832ad0f7e9STom Jones 
1842ad0f7e9STom Jones #define IWX_FBSD_RSP_V3 3
1852ad0f7e9STom Jones #define IWX_FBSD_RSP_V4 4
1862ad0f7e9STom Jones 
1872ad0f7e9STom Jones #define DEVNAME(_sc)    (device_get_nameunit((_sc)->sc_dev))
1882ad0f7e9STom Jones #define IC2IFP(ic) (((struct ieee80211vap *)TAILQ_FIRST(&(ic)->ic_vaps))->iv_ifp)
1892ad0f7e9STom Jones 
1902ad0f7e9STom Jones #define le16_to_cpup(_a_) (le16toh(*(const uint16_t *)(_a_)))
1912ad0f7e9STom Jones #define le32_to_cpup(_a_) (le32toh(*(const uint32_t *)(_a_)))
1922ad0f7e9STom Jones 
1932ad0f7e9STom Jones #include <dev/iwx/if_iwxreg.h>
1942ad0f7e9STom Jones #include <dev/iwx/if_iwxvar.h>
1952ad0f7e9STom Jones 
1962ad0f7e9STom Jones #include <dev/iwx/if_iwx_debug.h>
1972ad0f7e9STom Jones 
198969f6c63STom Jones #define	PCI_CFG_RETRY_TIMEOUT	0x41
199969f6c63STom Jones 
2002ad0f7e9STom Jones #define PCI_VENDOR_INTEL		0x8086
2012ad0f7e9STom Jones #define	PCI_PRODUCT_INTEL_WL_22500_1	0x2723		/* Wi-Fi 6 AX200 */
2022ad0f7e9STom Jones #define	PCI_PRODUCT_INTEL_WL_22500_2	0x02f0		/* Wi-Fi 6 AX201 */
2032ad0f7e9STom Jones #define	PCI_PRODUCT_INTEL_WL_22500_3	0xa0f0		/* Wi-Fi 6 AX201 */
2042ad0f7e9STom Jones #define	PCI_PRODUCT_INTEL_WL_22500_4	0x34f0		/* Wi-Fi 6 AX201 */
2052ad0f7e9STom Jones #define	PCI_PRODUCT_INTEL_WL_22500_5	0x06f0		/* Wi-Fi 6 AX201 */
2062ad0f7e9STom Jones #define	PCI_PRODUCT_INTEL_WL_22500_6	0x43f0		/* Wi-Fi 6 AX201 */
2072ad0f7e9STom Jones #define	PCI_PRODUCT_INTEL_WL_22500_7	0x3df0		/* Wi-Fi 6 AX201 */
2082ad0f7e9STom Jones #define	PCI_PRODUCT_INTEL_WL_22500_8	0x4df0		/* Wi-Fi 6 AX201 */
2092ad0f7e9STom Jones #define	PCI_PRODUCT_INTEL_WL_22500_9	0x2725		/* Wi-Fi 6 AX210 */
2102ad0f7e9STom Jones #define	PCI_PRODUCT_INTEL_WL_22500_10	0x2726		/* Wi-Fi 6 AX211 */
2112ad0f7e9STom Jones #define	PCI_PRODUCT_INTEL_WL_22500_11	0x51f0		/* Wi-Fi 6 AX211 */
2122ad0f7e9STom Jones #define	PCI_PRODUCT_INTEL_WL_22500_12	0x7a70		/* Wi-Fi 6 AX211 */
2132ad0f7e9STom Jones #define	PCI_PRODUCT_INTEL_WL_22500_13	0x7af0		/* Wi-Fi 6 AX211 */
2142ad0f7e9STom Jones #define	PCI_PRODUCT_INTEL_WL_22500_14	0x7e40		/* Wi-Fi 6 AX210 */
2152ad0f7e9STom Jones #define	PCI_PRODUCT_INTEL_WL_22500_15	0x7f70		/* Wi-Fi 6 AX211 */
2162ad0f7e9STom Jones #define	PCI_PRODUCT_INTEL_WL_22500_16	0x54f0		/* Wi-Fi 6 AX211 */
2172ad0f7e9STom Jones #define	PCI_PRODUCT_INTEL_WL_22500_17	0x51f1		/* Wi-Fi 6 AX211 */
2182ad0f7e9STom Jones 
2192ad0f7e9STom Jones static const struct iwx_devices {
2202ad0f7e9STom Jones 	uint16_t		device;
2212ad0f7e9STom Jones 	char			*name;
2222ad0f7e9STom Jones } iwx_devices[] = {
2232ad0f7e9STom Jones 	{ PCI_PRODUCT_INTEL_WL_22500_1,		"Wi-Fi 6 AX200"	},
2242ad0f7e9STom Jones 	{ PCI_PRODUCT_INTEL_WL_22500_2,		"Wi-Fi 6 AX201"	},
2252ad0f7e9STom Jones 	{ PCI_PRODUCT_INTEL_WL_22500_3,		"Wi-Fi 6 AX201"	},
2262ad0f7e9STom Jones 	{ PCI_PRODUCT_INTEL_WL_22500_4,		"Wi-Fi 6 AX201"	},
2272ad0f7e9STom Jones 	{ PCI_PRODUCT_INTEL_WL_22500_5,		"Wi-Fi 6 AX201"	},
2282ad0f7e9STom Jones 	{ PCI_PRODUCT_INTEL_WL_22500_6,		"Wi-Fi 6 AX201"	},
2292ad0f7e9STom Jones 	{ PCI_PRODUCT_INTEL_WL_22500_7,		"Wi-Fi 6 AX201"	},
2302ad0f7e9STom Jones 	{ PCI_PRODUCT_INTEL_WL_22500_8,		"Wi-Fi 6 AX201"	},
2312ad0f7e9STom Jones 	{ PCI_PRODUCT_INTEL_WL_22500_9,		"Wi-Fi 6 AX210"	},
2322ad0f7e9STom Jones 	{ PCI_PRODUCT_INTEL_WL_22500_10,	"Wi-Fi 6 AX211"	},
2332ad0f7e9STom Jones 	{ PCI_PRODUCT_INTEL_WL_22500_11,	"Wi-Fi 6 AX211"	},
2342ad0f7e9STom Jones 	{ PCI_PRODUCT_INTEL_WL_22500_12,	"Wi-Fi 6 AX211"	},
2352ad0f7e9STom Jones 	{ PCI_PRODUCT_INTEL_WL_22500_13,	"Wi-Fi 6 AX211"	},
2362ad0f7e9STom Jones 	{ PCI_PRODUCT_INTEL_WL_22500_14,	"Wi-Fi 6 AX210"	},
2372ad0f7e9STom Jones 	{ PCI_PRODUCT_INTEL_WL_22500_15,	"Wi-Fi 6 AX211"	},
2382ad0f7e9STom Jones 	{ PCI_PRODUCT_INTEL_WL_22500_16,	"Wi-Fi 6 AX211"	},
2392ad0f7e9STom Jones 	{ PCI_PRODUCT_INTEL_WL_22500_17,	"Wi-Fi 6 AX211"	},
2402ad0f7e9STom Jones };
2412ad0f7e9STom Jones 
2422ad0f7e9STom Jones static const uint8_t iwx_nvm_channels_8000[] = {
2432ad0f7e9STom Jones 	/* 2.4 GHz */
2442ad0f7e9STom Jones 	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
2452ad0f7e9STom Jones 	/* 5 GHz */
2462ad0f7e9STom Jones 	36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92,
2472ad0f7e9STom Jones 	96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144,
2482ad0f7e9STom Jones 	149, 153, 157, 161, 165, 169, 173, 177, 181
2492ad0f7e9STom Jones };
2502ad0f7e9STom Jones 
2512ad0f7e9STom Jones static const uint8_t iwx_nvm_channels_uhb[] = {
2522ad0f7e9STom Jones 	/* 2.4 GHz */
2532ad0f7e9STom Jones 	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
2542ad0f7e9STom Jones 	/* 5 GHz */
2552ad0f7e9STom Jones 	36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92,
2562ad0f7e9STom Jones 	96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144,
2572ad0f7e9STom Jones 	149, 153, 157, 161, 165, 169, 173, 177, 181,
2582ad0f7e9STom Jones 	/* 6-7 GHz */
2592ad0f7e9STom Jones 	1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 65, 69,
2602ad0f7e9STom Jones 	73, 77, 81, 85, 89, 93, 97, 101, 105, 109, 113, 117, 121, 125, 129,
2612ad0f7e9STom Jones 	133, 137, 141, 145, 149, 153, 157, 161, 165, 169, 173, 177, 181, 185,
2622ad0f7e9STom Jones 	189, 193, 197, 201, 205, 209, 213, 217, 221, 225, 229, 233
2632ad0f7e9STom Jones };
2642ad0f7e9STom Jones 
2652ad0f7e9STom Jones #define IWX_NUM_2GHZ_CHANNELS	14
2662ad0f7e9STom Jones #define IWX_NUM_5GHZ_CHANNELS	37
2672ad0f7e9STom Jones 
2682ad0f7e9STom Jones const struct iwx_rate {
2692ad0f7e9STom Jones 	uint16_t rate;
2702ad0f7e9STom Jones 	uint8_t plcp;
2712ad0f7e9STom Jones 	uint8_t ht_plcp;
2722ad0f7e9STom Jones } iwx_rates[] = {
2732ad0f7e9STom Jones 		/* Legacy */		/* HT */
2742ad0f7e9STom Jones 	{   2,	IWX_RATE_1M_PLCP,	IWX_RATE_HT_SISO_MCS_INV_PLCP  },
2752ad0f7e9STom Jones 	{   4,	IWX_RATE_2M_PLCP,	IWX_RATE_HT_SISO_MCS_INV_PLCP },
2762ad0f7e9STom Jones 	{  11,	IWX_RATE_5M_PLCP,	IWX_RATE_HT_SISO_MCS_INV_PLCP  },
2772ad0f7e9STom Jones 	{  22,	IWX_RATE_11M_PLCP,	IWX_RATE_HT_SISO_MCS_INV_PLCP },
2782ad0f7e9STom Jones 	{  12,	IWX_RATE_6M_PLCP,	IWX_RATE_HT_SISO_MCS_0_PLCP },
2792ad0f7e9STom Jones 	{  18,	IWX_RATE_9M_PLCP,	IWX_RATE_HT_SISO_MCS_INV_PLCP  },
2802ad0f7e9STom Jones 	{  24,	IWX_RATE_12M_PLCP,	IWX_RATE_HT_SISO_MCS_1_PLCP },
2812ad0f7e9STom Jones 	{  26,	IWX_RATE_INVM_PLCP,	IWX_RATE_HT_MIMO2_MCS_8_PLCP },
2822ad0f7e9STom Jones 	{  36,	IWX_RATE_18M_PLCP,	IWX_RATE_HT_SISO_MCS_2_PLCP },
2832ad0f7e9STom Jones 	{  48,	IWX_RATE_24M_PLCP,	IWX_RATE_HT_SISO_MCS_3_PLCP },
2842ad0f7e9STom Jones 	{  52,	IWX_RATE_INVM_PLCP,	IWX_RATE_HT_MIMO2_MCS_9_PLCP },
2852ad0f7e9STom Jones 	{  72,	IWX_RATE_36M_PLCP,	IWX_RATE_HT_SISO_MCS_4_PLCP },
2862ad0f7e9STom Jones 	{  78,	IWX_RATE_INVM_PLCP,	IWX_RATE_HT_MIMO2_MCS_10_PLCP },
2872ad0f7e9STom Jones 	{  96,	IWX_RATE_48M_PLCP,	IWX_RATE_HT_SISO_MCS_5_PLCP },
2882ad0f7e9STom Jones 	{ 104,	IWX_RATE_INVM_PLCP,	IWX_RATE_HT_MIMO2_MCS_11_PLCP },
2892ad0f7e9STom Jones 	{ 108,	IWX_RATE_54M_PLCP,	IWX_RATE_HT_SISO_MCS_6_PLCP },
2902ad0f7e9STom Jones 	{ 128,	IWX_RATE_INVM_PLCP,	IWX_RATE_HT_SISO_MCS_7_PLCP },
2912ad0f7e9STom Jones 	{ 156,	IWX_RATE_INVM_PLCP,	IWX_RATE_HT_MIMO2_MCS_12_PLCP },
2922ad0f7e9STom Jones 	{ 208,	IWX_RATE_INVM_PLCP,	IWX_RATE_HT_MIMO2_MCS_13_PLCP },
2932ad0f7e9STom Jones 	{ 234,	IWX_RATE_INVM_PLCP,	IWX_RATE_HT_MIMO2_MCS_14_PLCP },
2942ad0f7e9STom Jones 	{ 260,	IWX_RATE_INVM_PLCP,	IWX_RATE_HT_MIMO2_MCS_15_PLCP },
2952ad0f7e9STom Jones };
2962ad0f7e9STom Jones #define IWX_RIDX_CCK	0
2972ad0f7e9STom Jones #define IWX_RIDX_OFDM	4
2982ad0f7e9STom Jones #define IWX_RIDX_MAX	(nitems(iwx_rates)-1)
2992ad0f7e9STom Jones #define IWX_RIDX_IS_CCK(_i_) ((_i_) < IWX_RIDX_OFDM)
3002ad0f7e9STom Jones #define IWX_RIDX_IS_OFDM(_i_) ((_i_) >= IWX_RIDX_OFDM)
3012ad0f7e9STom Jones #define IWX_RVAL_IS_OFDM(_i_) ((_i_) >= 12 && (_i_) != 22)
3022ad0f7e9STom Jones 
3032ad0f7e9STom Jones /* Convert an MCS index into an iwx_rates[] index. */
3042ad0f7e9STom Jones const int iwx_mcs2ridx[] = {
3052ad0f7e9STom Jones 	IWX_RATE_MCS_0_INDEX,
3062ad0f7e9STom Jones 	IWX_RATE_MCS_1_INDEX,
3072ad0f7e9STom Jones 	IWX_RATE_MCS_2_INDEX,
3082ad0f7e9STom Jones 	IWX_RATE_MCS_3_INDEX,
3092ad0f7e9STom Jones 	IWX_RATE_MCS_4_INDEX,
3102ad0f7e9STom Jones 	IWX_RATE_MCS_5_INDEX,
3112ad0f7e9STom Jones 	IWX_RATE_MCS_6_INDEX,
3122ad0f7e9STom Jones 	IWX_RATE_MCS_7_INDEX,
3132ad0f7e9STom Jones 	IWX_RATE_MCS_8_INDEX,
3142ad0f7e9STom Jones 	IWX_RATE_MCS_9_INDEX,
3152ad0f7e9STom Jones 	IWX_RATE_MCS_10_INDEX,
3162ad0f7e9STom Jones 	IWX_RATE_MCS_11_INDEX,
3172ad0f7e9STom Jones 	IWX_RATE_MCS_12_INDEX,
3182ad0f7e9STom Jones 	IWX_RATE_MCS_13_INDEX,
3192ad0f7e9STom Jones 	IWX_RATE_MCS_14_INDEX,
3202ad0f7e9STom Jones 	IWX_RATE_MCS_15_INDEX,
3212ad0f7e9STom Jones };
3222ad0f7e9STom Jones 
3232ad0f7e9STom Jones static uint8_t	iwx_lookup_cmd_ver(struct iwx_softc *, uint8_t, uint8_t);
3242ad0f7e9STom Jones static uint8_t	iwx_lookup_notif_ver(struct iwx_softc *, uint8_t, uint8_t);
3252ad0f7e9STom Jones static int	iwx_store_cscheme(struct iwx_softc *, const uint8_t *, size_t);
3262ad0f7e9STom Jones #if 0
3272ad0f7e9STom Jones static int	iwx_alloc_fw_monitor_block(struct iwx_softc *, uint8_t, uint8_t);
3282ad0f7e9STom Jones static int	iwx_alloc_fw_monitor(struct iwx_softc *, uint8_t);
3292ad0f7e9STom Jones #endif
3302ad0f7e9STom Jones static int	iwx_apply_debug_destination(struct iwx_softc *);
3312ad0f7e9STom Jones static void	iwx_set_ltr(struct iwx_softc *);
3322ad0f7e9STom Jones static int	iwx_ctxt_info_init(struct iwx_softc *, const struct iwx_fw_sects *);
3332ad0f7e9STom Jones static int	iwx_ctxt_info_gen3_init(struct iwx_softc *,
3342ad0f7e9STom Jones 	    const struct iwx_fw_sects *);
3352ad0f7e9STom Jones static void	iwx_ctxt_info_free_fw_img(struct iwx_softc *);
3362ad0f7e9STom Jones static void	iwx_ctxt_info_free_paging(struct iwx_softc *);
3372ad0f7e9STom Jones static int	iwx_init_fw_sec(struct iwx_softc *, const struct iwx_fw_sects *,
3382ad0f7e9STom Jones 	    struct iwx_context_info_dram *);
3392ad0f7e9STom Jones static void	iwx_fw_version_str(char *, size_t, uint32_t, uint32_t, uint32_t);
3402ad0f7e9STom Jones static int	iwx_firmware_store_section(struct iwx_softc *, enum iwx_ucode_type,
3412ad0f7e9STom Jones 	    const uint8_t *, size_t);
3422ad0f7e9STom Jones static int	iwx_set_default_calib(struct iwx_softc *, const void *);
3432ad0f7e9STom Jones static void	iwx_fw_info_free(struct iwx_fw_info *);
3442ad0f7e9STom Jones static int	iwx_read_firmware(struct iwx_softc *);
3452ad0f7e9STom Jones static uint32_t iwx_prph_addr_mask(struct iwx_softc *);
3462ad0f7e9STom Jones static uint32_t iwx_read_prph_unlocked(struct iwx_softc *, uint32_t);
3472ad0f7e9STom Jones static uint32_t iwx_read_prph(struct iwx_softc *, uint32_t);
3482ad0f7e9STom Jones static void	iwx_write_prph_unlocked(struct iwx_softc *, uint32_t, uint32_t);
3492ad0f7e9STom Jones static void	iwx_write_prph(struct iwx_softc *, uint32_t, uint32_t);
3502ad0f7e9STom Jones static uint32_t iwx_read_umac_prph(struct iwx_softc *, uint32_t);
3512ad0f7e9STom Jones static void	iwx_write_umac_prph(struct iwx_softc *, uint32_t, uint32_t);
3522ad0f7e9STom Jones static int	iwx_read_mem(struct iwx_softc *, uint32_t, void *, int);
3532ad0f7e9STom Jones static int	iwx_poll_bit(struct iwx_softc *, int, uint32_t, uint32_t, int);
3542ad0f7e9STom Jones static int	iwx_nic_lock(struct iwx_softc *);
3552ad0f7e9STom Jones static void	iwx_nic_assert_locked(struct iwx_softc *);
3562ad0f7e9STom Jones static void	iwx_nic_unlock(struct iwx_softc *);
3572ad0f7e9STom Jones static int	iwx_set_bits_mask_prph(struct iwx_softc *, uint32_t, uint32_t,
3582ad0f7e9STom Jones 	    uint32_t);
3592ad0f7e9STom Jones static int	iwx_set_bits_prph(struct iwx_softc *, uint32_t, uint32_t);
3602ad0f7e9STom Jones static int	iwx_clear_bits_prph(struct iwx_softc *, uint32_t, uint32_t);
3612ad0f7e9STom Jones static void iwx_dma_map_addr(void *, bus_dma_segment_t *, int, int);
3622ad0f7e9STom Jones static int iwx_dma_contig_alloc(bus_dma_tag_t, struct iwx_dma_info *,
3632ad0f7e9STom Jones     bus_size_t, bus_size_t);
3642ad0f7e9STom Jones static void iwx_dma_contig_free(struct iwx_dma_info *);
3652ad0f7e9STom Jones static int iwx_alloc_rx_ring(struct iwx_softc *, struct iwx_rx_ring *);
3662ad0f7e9STom Jones static void	iwx_disable_rx_dma(struct iwx_softc *);
3672ad0f7e9STom Jones static void	iwx_reset_rx_ring(struct iwx_softc *, struct iwx_rx_ring *);
3682ad0f7e9STom Jones static void	iwx_free_rx_ring(struct iwx_softc *, struct iwx_rx_ring *);
3692ad0f7e9STom Jones static int iwx_alloc_tx_ring(struct iwx_softc *, struct iwx_tx_ring *, int);
3702ad0f7e9STom Jones static void	iwx_reset_tx_ring(struct iwx_softc *, struct iwx_tx_ring *);
3712ad0f7e9STom Jones static void	iwx_free_tx_ring(struct iwx_softc *, struct iwx_tx_ring *);
3722ad0f7e9STom Jones static void	iwx_enable_rfkill_int(struct iwx_softc *);
3732ad0f7e9STom Jones static int	iwx_check_rfkill(struct iwx_softc *);
3742ad0f7e9STom Jones static void	iwx_enable_interrupts(struct iwx_softc *);
3752ad0f7e9STom Jones static void	iwx_enable_fwload_interrupt(struct iwx_softc *);
3762ad0f7e9STom Jones #if 0
3772ad0f7e9STom Jones static void	iwx_restore_interrupts(struct iwx_softc *);
3782ad0f7e9STom Jones #endif
3792ad0f7e9STom Jones static void	iwx_disable_interrupts(struct iwx_softc *);
3802ad0f7e9STom Jones static void	iwx_ict_reset(struct iwx_softc *);
3812ad0f7e9STom Jones static int	iwx_set_hw_ready(struct iwx_softc *);
3822ad0f7e9STom Jones static int	iwx_prepare_card_hw(struct iwx_softc *);
3832ad0f7e9STom Jones static int	iwx_force_power_gating(struct iwx_softc *);
3842ad0f7e9STom Jones static void	iwx_apm_config(struct iwx_softc *);
3852ad0f7e9STom Jones static int	iwx_apm_init(struct iwx_softc *);
3862ad0f7e9STom Jones static void	iwx_apm_stop(struct iwx_softc *);
3872ad0f7e9STom Jones static int	iwx_allow_mcast(struct iwx_softc *);
3882ad0f7e9STom Jones static void	iwx_init_msix_hw(struct iwx_softc *);
3892ad0f7e9STom Jones static void	iwx_conf_msix_hw(struct iwx_softc *, int);
3902ad0f7e9STom Jones static int	iwx_clear_persistence_bit(struct iwx_softc *);
3912ad0f7e9STom Jones static int	iwx_start_hw(struct iwx_softc *);
3922ad0f7e9STom Jones static void	iwx_stop_device(struct iwx_softc *);
3932ad0f7e9STom Jones static void	iwx_nic_config(struct iwx_softc *);
3942ad0f7e9STom Jones static int	iwx_nic_rx_init(struct iwx_softc *);
3952ad0f7e9STom Jones static int	iwx_nic_init(struct iwx_softc *);
3962ad0f7e9STom Jones static int	iwx_enable_txq(struct iwx_softc *, int, int, int, int);
3972ad0f7e9STom Jones static int	iwx_disable_txq(struct iwx_softc *sc, int, int, uint8_t);
3982ad0f7e9STom Jones static void	iwx_post_alive(struct iwx_softc *);
3992ad0f7e9STom Jones static int	iwx_schedule_session_protection(struct iwx_softc *,
4002ad0f7e9STom Jones     struct iwx_node *, uint32_t);
4012ad0f7e9STom Jones static void	iwx_unprotect_session(struct iwx_softc *, struct iwx_node *);
4022ad0f7e9STom Jones static void	iwx_init_channel_map(struct ieee80211com *, int, int *,
4032ad0f7e9STom Jones     struct ieee80211_channel[]);
4042ad0f7e9STom Jones static int	iwx_mimo_enabled(struct iwx_softc *);
4052ad0f7e9STom Jones static void	iwx_init_reorder_buffer(struct iwx_reorder_buffer *, uint16_t,
4062ad0f7e9STom Jones 	    uint16_t);
4072ad0f7e9STom Jones static void	iwx_clear_reorder_buffer(struct iwx_softc *, struct iwx_rxba_data *);
4082ad0f7e9STom Jones static void	iwx_sta_rx_agg(struct iwx_softc *, struct ieee80211_node *, uint8_t,
4092ad0f7e9STom Jones 	    uint16_t, uint16_t, int, int);
4102ad0f7e9STom Jones static void	iwx_sta_tx_agg_start(struct iwx_softc *,
4112ad0f7e9STom Jones     struct ieee80211_node *, uint8_t);
4122ad0f7e9STom Jones static void	iwx_ba_rx_task(void *, int);
4132ad0f7e9STom Jones static void	iwx_ba_tx_task(void *, int);
4142ad0f7e9STom Jones static void	iwx_set_mac_addr_from_csr(struct iwx_softc *, struct iwx_nvm_data *);
4152ad0f7e9STom Jones static int	iwx_is_valid_mac_addr(const uint8_t *);
4162ad0f7e9STom Jones static void	iwx_flip_hw_address(uint32_t, uint32_t, uint8_t *);
4172ad0f7e9STom Jones static int	iwx_nvm_get(struct iwx_softc *);
4182ad0f7e9STom Jones static int	iwx_load_firmware(struct iwx_softc *);
4192ad0f7e9STom Jones static int	iwx_start_fw(struct iwx_softc *);
4202ad0f7e9STom Jones static int	iwx_pnvm_handle_section(struct iwx_softc *, const uint8_t *, size_t);
4212ad0f7e9STom Jones static int	iwx_pnvm_parse(struct iwx_softc *, const uint8_t *, size_t);
4222ad0f7e9STom Jones static void	iwx_ctxt_info_gen3_set_pnvm(struct iwx_softc *);
4232ad0f7e9STom Jones static int	iwx_load_pnvm(struct iwx_softc *);
4242ad0f7e9STom Jones static int	iwx_send_tx_ant_cfg(struct iwx_softc *, uint8_t);
4252ad0f7e9STom Jones static int	iwx_send_phy_cfg_cmd(struct iwx_softc *);
4262ad0f7e9STom Jones static int	iwx_load_ucode_wait_alive(struct iwx_softc *);
4272ad0f7e9STom Jones static int	iwx_send_dqa_cmd(struct iwx_softc *);
4282ad0f7e9STom Jones static int	iwx_run_init_mvm_ucode(struct iwx_softc *, int);
4292ad0f7e9STom Jones static int	iwx_config_ltr(struct iwx_softc *);
4302ad0f7e9STom Jones static void 	iwx_update_rx_desc(struct iwx_softc *, struct iwx_rx_ring *, int, bus_dma_segment_t *);
4312ad0f7e9STom Jones static int 	iwx_rx_addbuf(struct iwx_softc *, int, int);
4322ad0f7e9STom Jones static int	iwx_rxmq_get_signal_strength(struct iwx_softc *, struct iwx_rx_mpdu_desc *);
4332ad0f7e9STom Jones static void	iwx_rx_rx_phy_cmd(struct iwx_softc *, struct iwx_rx_packet *,
4342ad0f7e9STom Jones     struct iwx_rx_data *);
4352ad0f7e9STom Jones static int	iwx_get_noise(const struct iwx_statistics_rx_non_phy *);
4362ad0f7e9STom Jones static int	iwx_rx_hwdecrypt(struct iwx_softc *, struct mbuf *, uint32_t);
4372ad0f7e9STom Jones #if 0
4382ad0f7e9STom Jones int	iwx_ccmp_decap(struct iwx_softc *, struct mbuf *,
4392ad0f7e9STom Jones 	    struct ieee80211_node *, struct ieee80211_rxinfo *);
4402ad0f7e9STom Jones #endif
4412ad0f7e9STom Jones static void	iwx_rx_frame(struct iwx_softc *, struct mbuf *, int, uint32_t,
4422ad0f7e9STom Jones     int, int, uint32_t, uint8_t);
4432ad0f7e9STom Jones static void	iwx_clear_tx_desc(struct iwx_softc *, struct iwx_tx_ring *, int);
4442ad0f7e9STom Jones static void	iwx_txd_done(struct iwx_softc *, struct iwx_tx_ring *,
4452ad0f7e9STom Jones     struct iwx_tx_data *);
4462ad0f7e9STom Jones static void	iwx_txq_advance(struct iwx_softc *, struct iwx_tx_ring *, uint16_t);
4472ad0f7e9STom Jones static void	iwx_rx_tx_cmd(struct iwx_softc *, struct iwx_rx_packet *,
4482ad0f7e9STom Jones 	    struct iwx_rx_data *);
4492ad0f7e9STom Jones static void	iwx_clear_oactive(struct iwx_softc *, struct iwx_tx_ring *);
4502ad0f7e9STom Jones static void	iwx_rx_bmiss(struct iwx_softc *, struct iwx_rx_packet *,
4512ad0f7e9STom Jones     struct iwx_rx_data *);
4522ad0f7e9STom Jones static int	iwx_binding_cmd(struct iwx_softc *, struct iwx_node *, uint32_t);
4532ad0f7e9STom Jones static uint8_t	iwx_get_vht_ctrl_pos(struct ieee80211com *, struct ieee80211_channel *);
4542ad0f7e9STom Jones static int	iwx_phy_ctxt_cmd_uhb_v3_v4(struct iwx_softc *,
4552ad0f7e9STom Jones     struct iwx_phy_ctxt *, uint8_t, uint8_t, uint32_t, uint8_t, uint8_t, int);
4562ad0f7e9STom Jones #if 0
4572ad0f7e9STom Jones static int	iwx_phy_ctxt_cmd_v3_v4(struct iwx_softc *, struct iwx_phy_ctxt *,
4582ad0f7e9STom Jones     uint8_t, uint8_t, uint32_t, uint8_t, uint8_t, int);
4592ad0f7e9STom Jones #endif
4602ad0f7e9STom Jones static int	iwx_phy_ctxt_cmd(struct iwx_softc *, struct iwx_phy_ctxt *,
4612ad0f7e9STom Jones     uint8_t, uint8_t, uint32_t, uint32_t, uint8_t, uint8_t);
4622ad0f7e9STom Jones static int	iwx_send_cmd(struct iwx_softc *, struct iwx_host_cmd *);
4632ad0f7e9STom Jones static int	iwx_send_cmd_pdu(struct iwx_softc *, uint32_t, uint32_t, uint16_t,
4642ad0f7e9STom Jones 	    const void *);
4652ad0f7e9STom Jones static int	iwx_send_cmd_status(struct iwx_softc *, struct iwx_host_cmd *,
4662ad0f7e9STom Jones 	    uint32_t *);
4672ad0f7e9STom Jones static int	iwx_send_cmd_pdu_status(struct iwx_softc *, uint32_t, uint16_t,
4682ad0f7e9STom Jones 	    const void *, uint32_t *);
4692ad0f7e9STom Jones static void	iwx_free_resp(struct iwx_softc *, struct iwx_host_cmd *);
4702ad0f7e9STom Jones static void	iwx_cmd_done(struct iwx_softc *, int, int, int);
4712ad0f7e9STom Jones static uint32_t iwx_fw_rateidx_ofdm(uint8_t);
4722ad0f7e9STom Jones static uint32_t iwx_fw_rateidx_cck(uint8_t);
4732ad0f7e9STom Jones static const struct iwx_rate *iwx_tx_fill_cmd(struct iwx_softc *,
4742ad0f7e9STom Jones     struct iwx_node *, struct ieee80211_frame *, uint16_t *, uint32_t *,
4752ad0f7e9STom Jones     struct mbuf *);
4762ad0f7e9STom Jones static void	iwx_tx_update_byte_tbl(struct iwx_softc *, struct iwx_tx_ring *, int,
4772ad0f7e9STom Jones 	    uint16_t, uint16_t);
4782ad0f7e9STom Jones static int	iwx_tx(struct iwx_softc *, struct mbuf *,
4792ad0f7e9STom Jones     struct ieee80211_node *);
4802ad0f7e9STom Jones static int	iwx_flush_sta_tids(struct iwx_softc *, int, uint16_t);
4812ad0f7e9STom Jones static int	iwx_drain_sta(struct iwx_softc *sc, struct iwx_node *, int);
4822ad0f7e9STom Jones static int	iwx_flush_sta(struct iwx_softc *, struct iwx_node *);
4832ad0f7e9STom Jones static int	iwx_beacon_filter_send_cmd(struct iwx_softc *,
4842ad0f7e9STom Jones 	    struct iwx_beacon_filter_cmd *);
4852ad0f7e9STom Jones static int	iwx_update_beacon_abort(struct iwx_softc *, struct iwx_node *,
4862ad0f7e9STom Jones     int);
4872ad0f7e9STom Jones static void	iwx_power_build_cmd(struct iwx_softc *, struct iwx_node *,
4882ad0f7e9STom Jones 	    struct iwx_mac_power_cmd *);
4892ad0f7e9STom Jones static int	iwx_power_mac_update_mode(struct iwx_softc *, struct iwx_node *);
4902ad0f7e9STom Jones static int	iwx_power_update_device(struct iwx_softc *);
4912ad0f7e9STom Jones #if 0
4922ad0f7e9STom Jones static int	iwx_enable_beacon_filter(struct iwx_softc *, struct iwx_node *);
4932ad0f7e9STom Jones #endif
4942ad0f7e9STom Jones static int	iwx_disable_beacon_filter(struct iwx_softc *);
4952ad0f7e9STom Jones static int	iwx_add_sta_cmd(struct iwx_softc *, struct iwx_node *, int);
4962ad0f7e9STom Jones static int	iwx_rm_sta_cmd(struct iwx_softc *, struct iwx_node *);
4972ad0f7e9STom Jones static int	iwx_rm_sta(struct iwx_softc *, struct iwx_node *);
4982ad0f7e9STom Jones static int	iwx_fill_probe_req(struct iwx_softc *,
4992ad0f7e9STom Jones     struct iwx_scan_probe_req *);
5002ad0f7e9STom Jones static int	iwx_config_umac_scan_reduced(struct iwx_softc *);
5012ad0f7e9STom Jones static uint16_t iwx_scan_umac_flags_v2(struct iwx_softc *, int);
5022ad0f7e9STom Jones static void	iwx_scan_umac_dwell_v10(struct iwx_softc *,
5032ad0f7e9STom Jones 	    struct iwx_scan_general_params_v10 *, int);
5042ad0f7e9STom Jones static void	iwx_scan_umac_fill_general_p_v10(struct iwx_softc *,
5052ad0f7e9STom Jones 	    struct iwx_scan_general_params_v10 *, uint16_t, int);
5062ad0f7e9STom Jones static void	iwx_scan_umac_fill_ch_p_v6(struct iwx_softc *,
5072ad0f7e9STom Jones 	    struct iwx_scan_channel_params_v6 *, uint32_t, int);
5082ad0f7e9STom Jones static int	iwx_umac_scan_v14(struct iwx_softc *, int);
5092ad0f7e9STom Jones static void	iwx_mcc_update(struct iwx_softc *, struct iwx_mcc_chub_notif *);
5102ad0f7e9STom Jones static uint8_t	iwx_ridx2rate(struct ieee80211_rateset *, int);
5112ad0f7e9STom Jones static int	iwx_rval2ridx(int);
5122ad0f7e9STom Jones static void	iwx_ack_rates(struct iwx_softc *, struct iwx_node *, int *,
5132ad0f7e9STom Jones     int *);
5142ad0f7e9STom Jones static void	iwx_mac_ctxt_cmd_common(struct iwx_softc *, struct iwx_node *,
5152ad0f7e9STom Jones 	    struct iwx_mac_ctx_cmd *, uint32_t);
5162ad0f7e9STom Jones static void	iwx_mac_ctxt_cmd_fill_sta(struct iwx_softc *, struct iwx_node *,
5172ad0f7e9STom Jones 	    struct iwx_mac_data_sta *, int);
5182ad0f7e9STom Jones static int	iwx_mac_ctxt_cmd(struct iwx_softc *, struct iwx_node *,
5192ad0f7e9STom Jones     uint32_t, int);
5202ad0f7e9STom Jones static int	iwx_clear_statistics(struct iwx_softc *);
5212ad0f7e9STom Jones static int	iwx_scan(struct iwx_softc *);
5222ad0f7e9STom Jones static int	iwx_bgscan(struct ieee80211com *);
5232ad0f7e9STom Jones static int	iwx_enable_mgmt_queue(struct iwx_softc *);
5242ad0f7e9STom Jones static int	iwx_disable_mgmt_queue(struct iwx_softc *);
5252ad0f7e9STom Jones static int	iwx_rs_rval2idx(uint8_t);
5262ad0f7e9STom Jones static uint16_t iwx_rs_ht_rates(struct iwx_softc *, struct ieee80211_node *,
5272ad0f7e9STom Jones     int);
5282ad0f7e9STom Jones static uint16_t iwx_rs_vht_rates(struct iwx_softc *, struct ieee80211_node *, int);
5292ad0f7e9STom Jones static int	iwx_rs_init_v3(struct iwx_softc *, struct iwx_node *);
5302ad0f7e9STom Jones static int	iwx_rs_init_v4(struct iwx_softc *, struct iwx_node *);
5312ad0f7e9STom Jones static int	iwx_rs_init(struct iwx_softc *, struct iwx_node *);
5322ad0f7e9STom Jones static int	iwx_phy_send_rlc(struct iwx_softc *, struct iwx_phy_ctxt *,
5332ad0f7e9STom Jones 	    uint8_t, uint8_t);
5342ad0f7e9STom Jones static int	iwx_phy_ctxt_update(struct iwx_softc *, struct iwx_phy_ctxt *,
5352ad0f7e9STom Jones 	    struct ieee80211_channel *, uint8_t, uint8_t, uint32_t, uint8_t,
5362ad0f7e9STom Jones 	    uint8_t);
5372ad0f7e9STom Jones static int	iwx_auth(struct ieee80211vap *, struct iwx_softc *);
5382ad0f7e9STom Jones static int	iwx_deauth(struct iwx_softc *);
5392ad0f7e9STom Jones static int	iwx_run(struct ieee80211vap *, struct iwx_softc *);
5402ad0f7e9STom Jones static int	iwx_run_stop(struct iwx_softc *);
5412ad0f7e9STom Jones static struct ieee80211_node * iwx_node_alloc(struct ieee80211vap *,
5422ad0f7e9STom Jones     const uint8_t[IEEE80211_ADDR_LEN]);
5432ad0f7e9STom Jones #if 0
5442ad0f7e9STom Jones int	iwx_set_key(struct ieee80211com *, struct ieee80211_node *,
5452ad0f7e9STom Jones 	    struct ieee80211_key *);
5462ad0f7e9STom Jones void	iwx_setkey_task(void *);
5472ad0f7e9STom Jones void	iwx_delete_key(struct ieee80211com *,
5482ad0f7e9STom Jones 	    struct ieee80211_node *, struct ieee80211_key *);
5492ad0f7e9STom Jones #endif
5502ad0f7e9STom Jones static int	iwx_newstate(struct ieee80211vap *, enum ieee80211_state, int);
5512ad0f7e9STom Jones static void	iwx_endscan(struct iwx_softc *);
5522ad0f7e9STom Jones static void	iwx_fill_sf_command(struct iwx_softc *, struct iwx_sf_cfg_cmd *,
5532ad0f7e9STom Jones 	    struct ieee80211_node *);
5542ad0f7e9STom Jones static int	iwx_sf_config(struct iwx_softc *, int);
5552ad0f7e9STom Jones static int	iwx_send_bt_init_conf(struct iwx_softc *);
5562ad0f7e9STom Jones static int	iwx_send_soc_conf(struct iwx_softc *);
5572ad0f7e9STom Jones static int	iwx_send_update_mcc_cmd(struct iwx_softc *, const char *);
5582ad0f7e9STom Jones static int	iwx_send_temp_report_ths_cmd(struct iwx_softc *);
5592ad0f7e9STom Jones static int	iwx_init_hw(struct iwx_softc *);
5602ad0f7e9STom Jones static int	iwx_init(struct iwx_softc *);
5612ad0f7e9STom Jones static void	iwx_stop(struct iwx_softc *);
5622ad0f7e9STom Jones static void	iwx_watchdog(void *);
5632ad0f7e9STom Jones static const char *iwx_desc_lookup(uint32_t);
5642ad0f7e9STom Jones static void	iwx_nic_error(struct iwx_softc *);
5652ad0f7e9STom Jones static void	iwx_dump_driver_status(struct iwx_softc *);
5662ad0f7e9STom Jones static void	iwx_nic_umac_error(struct iwx_softc *);
5672ad0f7e9STom Jones static void	iwx_rx_mpdu_mq(struct iwx_softc *, struct mbuf *, void *, size_t);
5682ad0f7e9STom Jones static int	iwx_rx_pkt_valid(struct iwx_rx_packet *);
5692ad0f7e9STom Jones static void	iwx_rx_pkt(struct iwx_softc *, struct iwx_rx_data *,
5702ad0f7e9STom Jones 	    struct mbuf *);
5712ad0f7e9STom Jones static void	iwx_notif_intr(struct iwx_softc *);
5722ad0f7e9STom Jones #if 0
5732ad0f7e9STom Jones /* XXX-THJ - I don't have hardware for this */
5742ad0f7e9STom Jones static int	iwx_intr(void *);
5752ad0f7e9STom Jones #endif
5762ad0f7e9STom Jones static void	iwx_intr_msix(void *);
5772ad0f7e9STom Jones static int	iwx_preinit(struct iwx_softc *);
5782ad0f7e9STom Jones static void	iwx_attach_hook(void *);
5792ad0f7e9STom Jones static const struct iwx_device_cfg *iwx_find_device_cfg(struct iwx_softc *);
5802ad0f7e9STom Jones static int	iwx_probe(device_t);
5812ad0f7e9STom Jones static int	iwx_attach(device_t);
5822ad0f7e9STom Jones static int	iwx_detach(device_t);
5832ad0f7e9STom Jones 
5842ad0f7e9STom Jones /* FreeBSD specific glue */
5852ad0f7e9STom Jones u_int8_t etherbroadcastaddr[ETHER_ADDR_LEN] =
5862ad0f7e9STom Jones     { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
5872ad0f7e9STom Jones 
5882ad0f7e9STom Jones u_int8_t etheranyaddr[ETHER_ADDR_LEN] =
5892ad0f7e9STom Jones     { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
5902ad0f7e9STom Jones 
5912ad0f7e9STom Jones #if IWX_DEBUG
5922ad0f7e9STom Jones #define DPRINTF(x)	do { if (sc->sc_debug == IWX_DEBUG_ANY) { printf x; } } while (0)
5932ad0f7e9STom Jones #else
5942ad0f7e9STom Jones #define DPRINTF(x)	do { ; } while (0)
5952ad0f7e9STom Jones #endif
5962ad0f7e9STom Jones 
5972ad0f7e9STom Jones /* FreeBSD specific functions */
5982ad0f7e9STom Jones static struct	ieee80211vap * iwx_vap_create(struct ieee80211com *,
5992ad0f7e9STom Jones     const char[IFNAMSIZ], int, enum ieee80211_opmode, int,
6002ad0f7e9STom Jones     const uint8_t[IEEE80211_ADDR_LEN], const uint8_t[IEEE80211_ADDR_LEN]);
6012ad0f7e9STom Jones static void	iwx_vap_delete(struct ieee80211vap *);
6022ad0f7e9STom Jones static void	iwx_parent(struct ieee80211com *);
6032ad0f7e9STom Jones static void	iwx_scan_start(struct ieee80211com *);
6042ad0f7e9STom Jones static void	iwx_scan_end(struct ieee80211com *);
6052ad0f7e9STom Jones static void	iwx_update_mcast(struct ieee80211com *ic);
6062ad0f7e9STom Jones static void	iwx_scan_curchan(struct ieee80211_scan_state *, unsigned long);
6072ad0f7e9STom Jones static void	iwx_scan_mindwell(struct ieee80211_scan_state *);
6082ad0f7e9STom Jones static void	iwx_set_channel(struct ieee80211com *);
6092ad0f7e9STom Jones static void	iwx_endscan_cb(void *, int );
6102ad0f7e9STom Jones static int	iwx_wme_update(struct ieee80211com *);
6112ad0f7e9STom Jones static int	iwx_raw_xmit(struct ieee80211_node *, struct mbuf *,
6122ad0f7e9STom Jones     const struct ieee80211_bpf_params *);
6132ad0f7e9STom Jones static int	iwx_transmit(struct ieee80211com *, struct mbuf *);
6142ad0f7e9STom Jones static void	iwx_start(struct iwx_softc *);
6152ad0f7e9STom Jones static int	iwx_ampdu_rx_start(struct ieee80211_node *,
6162ad0f7e9STom Jones     struct ieee80211_rx_ampdu *, int, int, int);
6172ad0f7e9STom Jones static void	iwx_ampdu_rx_stop(struct ieee80211_node *,
6182ad0f7e9STom Jones     struct ieee80211_rx_ampdu *);
6192ad0f7e9STom Jones static int	iwx_addba_request(struct ieee80211_node *,
6202ad0f7e9STom Jones     struct ieee80211_tx_ampdu *, int, int, int);
6212ad0f7e9STom Jones static int	iwx_addba_response(struct ieee80211_node *,
6222ad0f7e9STom Jones     struct ieee80211_tx_ampdu *, int, int, int);
6232ad0f7e9STom Jones static void	iwx_key_update_begin(struct ieee80211vap *);
6242ad0f7e9STom Jones static void	iwx_key_update_end(struct ieee80211vap *);
6252ad0f7e9STom Jones static int	iwx_key_alloc(struct ieee80211vap *, struct ieee80211_key *,
6262ad0f7e9STom Jones     ieee80211_keyix *,ieee80211_keyix *);
6272ad0f7e9STom Jones static int	iwx_key_set(struct ieee80211vap *, const struct ieee80211_key *);
6282ad0f7e9STom Jones static int	iwx_key_delete(struct ieee80211vap *,
6292ad0f7e9STom Jones     const struct ieee80211_key *);
6302ad0f7e9STom Jones static int	iwx_suspend(device_t);
6312ad0f7e9STom Jones static int	iwx_resume(device_t);
6322ad0f7e9STom Jones static void	iwx_radiotap_attach(struct iwx_softc *);
6332ad0f7e9STom Jones 
6342ad0f7e9STom Jones /* OpenBSD compat defines */
6352ad0f7e9STom Jones #define IEEE80211_HTOP0_SCO_SCN 0
6362ad0f7e9STom Jones #define IEEE80211_VHTOP0_CHAN_WIDTH_HT 0
6372ad0f7e9STom Jones #define IEEE80211_VHTOP0_CHAN_WIDTH_80 1
6382ad0f7e9STom Jones 
6392ad0f7e9STom Jones #define IEEE80211_HT_RATESET_SISO 0
6402ad0f7e9STom Jones #define IEEE80211_HT_RATESET_MIMO2 2
6412ad0f7e9STom Jones 
6422ad0f7e9STom Jones const struct ieee80211_rateset ieee80211_std_rateset_11a =
6432ad0f7e9STom Jones 	{ 8, { 12, 18, 24, 36, 48, 72, 96, 108 } };
6442ad0f7e9STom Jones 
6452ad0f7e9STom Jones const struct ieee80211_rateset ieee80211_std_rateset_11b =
6462ad0f7e9STom Jones 	{ 4, { 2, 4, 11, 22 } };
6472ad0f7e9STom Jones 
6482ad0f7e9STom Jones const struct ieee80211_rateset ieee80211_std_rateset_11g =
6492ad0f7e9STom Jones 	{ 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } };
6502ad0f7e9STom Jones 
6512ad0f7e9STom Jones inline int
ieee80211_has_addr4(const struct ieee80211_frame * wh)6522ad0f7e9STom Jones ieee80211_has_addr4(const struct ieee80211_frame *wh)
6532ad0f7e9STom Jones {
6542ad0f7e9STom Jones 	return (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) ==
6552ad0f7e9STom Jones 	    IEEE80211_FC1_DIR_DSTODS;
6562ad0f7e9STom Jones }
6572ad0f7e9STom Jones 
6582ad0f7e9STom Jones static uint8_t
iwx_lookup_cmd_ver(struct iwx_softc * sc,uint8_t grp,uint8_t cmd)6592ad0f7e9STom Jones iwx_lookup_cmd_ver(struct iwx_softc *sc, uint8_t grp, uint8_t cmd)
6602ad0f7e9STom Jones {
6612ad0f7e9STom Jones 	const struct iwx_fw_cmd_version *entry;
6622ad0f7e9STom Jones 	int i;
6632ad0f7e9STom Jones 
6642ad0f7e9STom Jones 	for (i = 0; i < sc->n_cmd_versions; i++) {
6652ad0f7e9STom Jones 		entry = &sc->cmd_versions[i];
6662ad0f7e9STom Jones 		if (entry->group == grp && entry->cmd == cmd)
6672ad0f7e9STom Jones 			return entry->cmd_ver;
6682ad0f7e9STom Jones 	}
6692ad0f7e9STom Jones 
6702ad0f7e9STom Jones 	return IWX_FW_CMD_VER_UNKNOWN;
6712ad0f7e9STom Jones }
6722ad0f7e9STom Jones 
6732ad0f7e9STom Jones uint8_t
iwx_lookup_notif_ver(struct iwx_softc * sc,uint8_t grp,uint8_t cmd)6742ad0f7e9STom Jones iwx_lookup_notif_ver(struct iwx_softc *sc, uint8_t grp, uint8_t cmd)
6752ad0f7e9STom Jones {
6762ad0f7e9STom Jones 	const struct iwx_fw_cmd_version *entry;
6772ad0f7e9STom Jones 	int i;
6782ad0f7e9STom Jones 
6792ad0f7e9STom Jones 	for (i = 0; i < sc->n_cmd_versions; i++) {
6802ad0f7e9STom Jones 		entry = &sc->cmd_versions[i];
6812ad0f7e9STom Jones 		if (entry->group == grp && entry->cmd == cmd)
6822ad0f7e9STom Jones 			return entry->notif_ver;
6832ad0f7e9STom Jones 	}
6842ad0f7e9STom Jones 
6852ad0f7e9STom Jones 	return IWX_FW_CMD_VER_UNKNOWN;
6862ad0f7e9STom Jones }
6872ad0f7e9STom Jones 
6882ad0f7e9STom Jones static int
iwx_store_cscheme(struct iwx_softc * sc,const uint8_t * data,size_t dlen)6892ad0f7e9STom Jones iwx_store_cscheme(struct iwx_softc *sc, const uint8_t *data, size_t dlen)
6902ad0f7e9STom Jones {
6912ad0f7e9STom Jones 	const struct iwx_fw_cscheme_list *l = (const void *)data;
6922ad0f7e9STom Jones 
6932ad0f7e9STom Jones 	if (dlen < sizeof(*l) ||
6942ad0f7e9STom Jones 	    dlen < sizeof(l->size) + l->size * sizeof(*l->cs))
6952ad0f7e9STom Jones 		return EINVAL;
6962ad0f7e9STom Jones 
6972ad0f7e9STom Jones 	/* we don't actually store anything for now, always use s/w crypto */
6982ad0f7e9STom Jones 
6992ad0f7e9STom Jones 	return 0;
7002ad0f7e9STom Jones }
7012ad0f7e9STom Jones 
7022ad0f7e9STom Jones static int
iwx_ctxt_info_alloc_dma(struct iwx_softc * sc,const struct iwx_fw_onesect * sec,struct iwx_dma_info * dram)7032ad0f7e9STom Jones iwx_ctxt_info_alloc_dma(struct iwx_softc *sc,
7042ad0f7e9STom Jones     const struct iwx_fw_onesect *sec, struct iwx_dma_info *dram)
7052ad0f7e9STom Jones {
7062ad0f7e9STom Jones 	int err = iwx_dma_contig_alloc(sc->sc_dmat, dram, sec->fws_len, 1);
7072ad0f7e9STom Jones 	if (err) {
7082ad0f7e9STom Jones 		printf("%s: could not allocate context info DMA memory\n",
7092ad0f7e9STom Jones 		    DEVNAME(sc));
7102ad0f7e9STom Jones 		return err;
7112ad0f7e9STom Jones 	}
7122ad0f7e9STom Jones 
7132ad0f7e9STom Jones 	memcpy(dram->vaddr, sec->fws_data, sec->fws_len);
7142ad0f7e9STom Jones 
7152ad0f7e9STom Jones 	return 0;
7162ad0f7e9STom Jones }
7172ad0f7e9STom Jones 
7182ad0f7e9STom Jones static void
iwx_ctxt_info_free_paging(struct iwx_softc * sc)7192ad0f7e9STom Jones iwx_ctxt_info_free_paging(struct iwx_softc *sc)
7202ad0f7e9STom Jones {
7212ad0f7e9STom Jones 	struct iwx_self_init_dram *dram = &sc->init_dram;
7222ad0f7e9STom Jones 	int i;
7232ad0f7e9STom Jones 
7242ad0f7e9STom Jones 	if (!dram->paging)
7252ad0f7e9STom Jones 		return;
7262ad0f7e9STom Jones 
7272ad0f7e9STom Jones 	/* free paging*/
7282ad0f7e9STom Jones 	for (i = 0; i < dram->paging_cnt; i++)
7292ad0f7e9STom Jones 		iwx_dma_contig_free(&dram->paging[i]);
7302ad0f7e9STom Jones 
7312ad0f7e9STom Jones 	free(dram->paging, M_DEVBUF);
7322ad0f7e9STom Jones 	dram->paging_cnt = 0;
7332ad0f7e9STom Jones 	dram->paging = NULL;
7342ad0f7e9STom Jones }
7352ad0f7e9STom Jones 
7362ad0f7e9STom Jones static int
iwx_get_num_sections(const struct iwx_fw_sects * fws,int start)7372ad0f7e9STom Jones iwx_get_num_sections(const struct iwx_fw_sects *fws, int start)
7382ad0f7e9STom Jones {
7392ad0f7e9STom Jones 	int i = 0;
7402ad0f7e9STom Jones 
7412ad0f7e9STom Jones 	while (start < fws->fw_count &&
7422ad0f7e9STom Jones 	       fws->fw_sect[start].fws_devoff != IWX_CPU1_CPU2_SEPARATOR_SECTION &&
7432ad0f7e9STom Jones 	       fws->fw_sect[start].fws_devoff != IWX_PAGING_SEPARATOR_SECTION) {
7442ad0f7e9STom Jones 		start++;
7452ad0f7e9STom Jones 		i++;
7462ad0f7e9STom Jones 	}
7472ad0f7e9STom Jones 
7482ad0f7e9STom Jones 	return i;
7492ad0f7e9STom Jones }
7502ad0f7e9STom Jones 
7512ad0f7e9STom Jones static int
iwx_init_fw_sec(struct iwx_softc * sc,const struct iwx_fw_sects * fws,struct iwx_context_info_dram * ctxt_dram)7522ad0f7e9STom Jones iwx_init_fw_sec(struct iwx_softc *sc, const struct iwx_fw_sects *fws,
7532ad0f7e9STom Jones     struct iwx_context_info_dram *ctxt_dram)
7542ad0f7e9STom Jones {
7552ad0f7e9STom Jones 	struct iwx_self_init_dram *dram = &sc->init_dram;
7562ad0f7e9STom Jones 	int i, ret, fw_cnt = 0;
7572ad0f7e9STom Jones 
7582ad0f7e9STom Jones 	KASSERT(dram->paging == NULL, ("iwx_init_fw_sec"));
7592ad0f7e9STom Jones 
7602ad0f7e9STom Jones 	dram->lmac_cnt = iwx_get_num_sections(fws, 0);
7612ad0f7e9STom Jones 	/* add 1 due to separator */
7622ad0f7e9STom Jones 	dram->umac_cnt = iwx_get_num_sections(fws, dram->lmac_cnt + 1);
7632ad0f7e9STom Jones 	/* add 2 due to separators */
7642ad0f7e9STom Jones 	dram->paging_cnt = iwx_get_num_sections(fws,
7652ad0f7e9STom Jones 	    dram->lmac_cnt + dram->umac_cnt + 2);
7662ad0f7e9STom Jones 
7672ad0f7e9STom Jones 	IWX_UNLOCK(sc);
7682ad0f7e9STom Jones 	dram->fw = mallocarray(dram->umac_cnt + dram->lmac_cnt,
7692ad0f7e9STom Jones 	    sizeof(*dram->fw), M_DEVBUF,  M_ZERO | M_NOWAIT);
7702ad0f7e9STom Jones 	if (!dram->fw) {
7712ad0f7e9STom Jones 		printf("%s: could not allocate memory for firmware sections\n",
7722ad0f7e9STom Jones 		    DEVNAME(sc));
7732ad0f7e9STom Jones 		IWX_LOCK(sc);
7742ad0f7e9STom Jones 		return ENOMEM;
7752ad0f7e9STom Jones 	}
7762ad0f7e9STom Jones 
7772ad0f7e9STom Jones 	dram->paging = mallocarray(dram->paging_cnt, sizeof(*dram->paging),
7782ad0f7e9STom Jones 	    M_DEVBUF, M_ZERO | M_WAITOK);
7792ad0f7e9STom Jones 	IWX_LOCK(sc);
7802ad0f7e9STom Jones 	if (!dram->paging) {
7812ad0f7e9STom Jones 		printf("%s: could not allocate memory for firmware paging\n",
7822ad0f7e9STom Jones 		    DEVNAME(sc));
7832ad0f7e9STom Jones 		return ENOMEM;
7842ad0f7e9STom Jones 	}
7852ad0f7e9STom Jones 
7862ad0f7e9STom Jones 	/* initialize lmac sections */
7872ad0f7e9STom Jones 	for (i = 0; i < dram->lmac_cnt; i++) {
7882ad0f7e9STom Jones 		ret = iwx_ctxt_info_alloc_dma(sc, &fws->fw_sect[i],
7892ad0f7e9STom Jones 						   &dram->fw[fw_cnt]);
7902ad0f7e9STom Jones 		if (ret)
7912ad0f7e9STom Jones 			return ret;
7922ad0f7e9STom Jones 		ctxt_dram->lmac_img[i] =
7932ad0f7e9STom Jones 			htole64(dram->fw[fw_cnt].paddr);
7942ad0f7e9STom Jones 		IWX_DPRINTF(sc, IWX_DEBUG_FIRMWARE_TLV,
7952ad0f7e9STom Jones 		    "%s: firmware LMAC section %d at 0x%llx size %lld\n",
7962ad0f7e9STom Jones 		    __func__, i,
7972ad0f7e9STom Jones 		    (unsigned long long)dram->fw[fw_cnt].paddr,
7982ad0f7e9STom Jones 		    (unsigned long long)dram->fw[fw_cnt].size);
7992ad0f7e9STom Jones 		fw_cnt++;
8002ad0f7e9STom Jones 	}
8012ad0f7e9STom Jones 
8022ad0f7e9STom Jones 	/* initialize umac sections */
8032ad0f7e9STom Jones 	for (i = 0; i < dram->umac_cnt; i++) {
8042ad0f7e9STom Jones 		/* access FW with +1 to make up for lmac separator */
8052ad0f7e9STom Jones 		ret = iwx_ctxt_info_alloc_dma(sc,
8062ad0f7e9STom Jones 		    &fws->fw_sect[fw_cnt + 1], &dram->fw[fw_cnt]);
8072ad0f7e9STom Jones 		if (ret)
8082ad0f7e9STom Jones 			return ret;
8092ad0f7e9STom Jones 		ctxt_dram->umac_img[i] =
8102ad0f7e9STom Jones 			htole64(dram->fw[fw_cnt].paddr);
8112ad0f7e9STom Jones 		IWX_DPRINTF(sc, IWX_DEBUG_FIRMWARE_TLV,
8122ad0f7e9STom Jones 		    "%s: firmware UMAC section %d at 0x%llx size %lld\n",
8132ad0f7e9STom Jones 		    __func__, i,
8142ad0f7e9STom Jones 		    (unsigned long long)dram->fw[fw_cnt].paddr,
8152ad0f7e9STom Jones 		    (unsigned long long)dram->fw[fw_cnt].size);
8162ad0f7e9STom Jones 		fw_cnt++;
8172ad0f7e9STom Jones 	}
8182ad0f7e9STom Jones 
8192ad0f7e9STom Jones 	/*
8202ad0f7e9STom Jones 	 * Initialize paging.
8212ad0f7e9STom Jones 	 * Paging memory isn't stored in dram->fw as the umac and lmac - it is
8222ad0f7e9STom Jones 	 * stored separately.
8232ad0f7e9STom Jones 	 * This is since the timing of its release is different -
8242ad0f7e9STom Jones 	 * while fw memory can be released on alive, the paging memory can be
8252ad0f7e9STom Jones 	 * freed only when the device goes down.
8262ad0f7e9STom Jones 	 * Given that, the logic here in accessing the fw image is a bit
8272ad0f7e9STom Jones 	 * different - fw_cnt isn't changing so loop counter is added to it.
8282ad0f7e9STom Jones 	 */
8292ad0f7e9STom Jones 	for (i = 0; i < dram->paging_cnt; i++) {
8302ad0f7e9STom Jones 		/* access FW with +2 to make up for lmac & umac separators */
8312ad0f7e9STom Jones 		int fw_idx = fw_cnt + i + 2;
8322ad0f7e9STom Jones 
8332ad0f7e9STom Jones 		ret = iwx_ctxt_info_alloc_dma(sc,
8342ad0f7e9STom Jones 		    &fws->fw_sect[fw_idx], &dram->paging[i]);
8352ad0f7e9STom Jones 		if (ret)
8362ad0f7e9STom Jones 			return ret;
8372ad0f7e9STom Jones 
8382ad0f7e9STom Jones 		ctxt_dram->virtual_img[i] = htole64(dram->paging[i].paddr);
8392ad0f7e9STom Jones 		IWX_DPRINTF(sc, IWX_DEBUG_FIRMWARE_TLV,
8402ad0f7e9STom Jones 		    "%s: firmware paging section %d at 0x%llx size %lld\n",
8412ad0f7e9STom Jones 		    __func__, i,
8422ad0f7e9STom Jones 		    (unsigned long long)dram->paging[i].paddr,
8432ad0f7e9STom Jones 		    (unsigned long long)dram->paging[i].size);
8442ad0f7e9STom Jones 	}
8452ad0f7e9STom Jones 
8462ad0f7e9STom Jones 	return 0;
8472ad0f7e9STom Jones }
8482ad0f7e9STom Jones 
8492ad0f7e9STom Jones static void
iwx_fw_version_str(char * buf,size_t bufsize,uint32_t major,uint32_t minor,uint32_t api)8502ad0f7e9STom Jones iwx_fw_version_str(char *buf, size_t bufsize,
8512ad0f7e9STom Jones     uint32_t major, uint32_t minor, uint32_t api)
8522ad0f7e9STom Jones {
8532ad0f7e9STom Jones 	/*
8542ad0f7e9STom Jones 	 * Starting with major version 35 the Linux driver prints the minor
8552ad0f7e9STom Jones 	 * version in hexadecimal.
8562ad0f7e9STom Jones 	 */
8572ad0f7e9STom Jones 	if (major >= 35)
8582ad0f7e9STom Jones 		snprintf(buf, bufsize, "%u.%08x.%u", major, minor, api);
8592ad0f7e9STom Jones 	else
8602ad0f7e9STom Jones 		snprintf(buf, bufsize, "%u.%u.%u", major, minor, api);
8612ad0f7e9STom Jones }
8622ad0f7e9STom Jones #if 0
8632ad0f7e9STom Jones static int
8642ad0f7e9STom Jones iwx_alloc_fw_monitor_block(struct iwx_softc *sc, uint8_t max_power,
8652ad0f7e9STom Jones     uint8_t min_power)
8662ad0f7e9STom Jones {
8672ad0f7e9STom Jones 	struct iwx_dma_info *fw_mon = &sc->fw_mon;
8682ad0f7e9STom Jones 	uint32_t size = 0;
8692ad0f7e9STom Jones 	uint8_t power;
8702ad0f7e9STom Jones 	int err;
8712ad0f7e9STom Jones 
8722ad0f7e9STom Jones 	if (fw_mon->size)
8732ad0f7e9STom Jones 		return 0;
8742ad0f7e9STom Jones 
8752ad0f7e9STom Jones 	for (power = max_power; power >= min_power; power--) {
8762ad0f7e9STom Jones 		size = (1 << power);
8772ad0f7e9STom Jones 
8782ad0f7e9STom Jones 		err = iwx_dma_contig_alloc(sc->sc_dmat, fw_mon, size, 0);
8792ad0f7e9STom Jones 		if (err)
8802ad0f7e9STom Jones 			continue;
8812ad0f7e9STom Jones 
8822ad0f7e9STom Jones 		IWX_DPRINTF(sc, IWX_DEBUG_FIRMWARE_TLV,
8832ad0f7e9STom Jones 		    "%s: allocated 0x%08x bytes for firmware monitor.\n",
8842ad0f7e9STom Jones 		    DEVNAME(sc), size);
8852ad0f7e9STom Jones 		break;
8862ad0f7e9STom Jones 	}
8872ad0f7e9STom Jones 
8882ad0f7e9STom Jones 	if (err) {
8892ad0f7e9STom Jones 		fw_mon->size = 0;
8902ad0f7e9STom Jones 		return err;
8912ad0f7e9STom Jones 	}
8922ad0f7e9STom Jones 
8932ad0f7e9STom Jones 	if (power != max_power)
8942ad0f7e9STom Jones 		IWX_DPRINTF(sc, IWX_DEBUG_FIRMWARE_TLV,
8952ad0f7e9STom Jones 		    "%s: Sorry - debug buffer is only %luK while you requested %luK\n",
8962ad0f7e9STom Jones 		    DEVNAME(sc), (unsigned long)(1 << (power - 10)),
8972ad0f7e9STom Jones 		    (unsigned long)(1 << (max_power - 10)));
8982ad0f7e9STom Jones 
8992ad0f7e9STom Jones 	return 0;
9002ad0f7e9STom Jones }
9012ad0f7e9STom Jones 
9022ad0f7e9STom Jones static int
9032ad0f7e9STom Jones iwx_alloc_fw_monitor(struct iwx_softc *sc, uint8_t max_power)
9042ad0f7e9STom Jones {
9052ad0f7e9STom Jones 	if (!max_power) {
9062ad0f7e9STom Jones 		/* default max_power is maximum */
9072ad0f7e9STom Jones 		max_power = 26;
9082ad0f7e9STom Jones 	} else {
9092ad0f7e9STom Jones 		max_power += 11;
9102ad0f7e9STom Jones 	}
9112ad0f7e9STom Jones 
9122ad0f7e9STom Jones 	if (max_power > 26) {
9132ad0f7e9STom Jones 		 IWX_DPRINTF(sc, IWX_DEBUG_FIRMWARE_TLV,
9142ad0f7e9STom Jones 		     "%s: External buffer size for monitor is too big %d, "
9152ad0f7e9STom Jones 		     "check the FW TLV\n", DEVNAME(sc), max_power);
9162ad0f7e9STom Jones 		return 0;
9172ad0f7e9STom Jones 	}
9182ad0f7e9STom Jones 
9192ad0f7e9STom Jones 	if (sc->fw_mon.size)
9202ad0f7e9STom Jones 		return 0;
9212ad0f7e9STom Jones 
9222ad0f7e9STom Jones 	return iwx_alloc_fw_monitor_block(sc, max_power, 11);
9232ad0f7e9STom Jones }
9242ad0f7e9STom Jones #endif
9252ad0f7e9STom Jones 
9262ad0f7e9STom Jones static int
iwx_apply_debug_destination(struct iwx_softc * sc)9272ad0f7e9STom Jones iwx_apply_debug_destination(struct iwx_softc *sc)
9282ad0f7e9STom Jones {
9292ad0f7e9STom Jones #if 0
9302ad0f7e9STom Jones 	struct iwx_fw_dbg_dest_tlv_v1 *dest_v1;
9312ad0f7e9STom Jones 	int i, err;
9322ad0f7e9STom Jones 	uint8_t mon_mode, size_power, base_shift, end_shift;
9332ad0f7e9STom Jones 	uint32_t base_reg, end_reg;
9342ad0f7e9STom Jones 
9352ad0f7e9STom Jones 	dest_v1 = sc->sc_fw.dbg_dest_tlv_v1;
9362ad0f7e9STom Jones 	mon_mode = dest_v1->monitor_mode;
9372ad0f7e9STom Jones 	size_power = dest_v1->size_power;
9382ad0f7e9STom Jones 	base_reg = le32toh(dest_v1->base_reg);
9392ad0f7e9STom Jones 	end_reg = le32toh(dest_v1->end_reg);
9402ad0f7e9STom Jones 	base_shift = dest_v1->base_shift;
9412ad0f7e9STom Jones 	end_shift = dest_v1->end_shift;
9422ad0f7e9STom Jones 
9432ad0f7e9STom Jones 	DPRINTF(("%s: applying debug destination %d\n", DEVNAME(sc), mon_mode));
9442ad0f7e9STom Jones 
9452ad0f7e9STom Jones 	if (mon_mode == EXTERNAL_MODE) {
9462ad0f7e9STom Jones 		err = iwx_alloc_fw_monitor(sc, size_power);
9472ad0f7e9STom Jones 		if (err)
9482ad0f7e9STom Jones 			return err;
9492ad0f7e9STom Jones 	}
9502ad0f7e9STom Jones 
9512ad0f7e9STom Jones 	if (!iwx_nic_lock(sc))
9522ad0f7e9STom Jones 		return EBUSY;
9532ad0f7e9STom Jones 
9542ad0f7e9STom Jones 	for (i = 0; i < sc->sc_fw.n_dest_reg; i++) {
9552ad0f7e9STom Jones 		uint32_t addr, val;
9562ad0f7e9STom Jones 		uint8_t op;
9572ad0f7e9STom Jones 
9582ad0f7e9STom Jones 		addr = le32toh(dest_v1->reg_ops[i].addr);
9592ad0f7e9STom Jones 		val = le32toh(dest_v1->reg_ops[i].val);
9602ad0f7e9STom Jones 		op = dest_v1->reg_ops[i].op;
9612ad0f7e9STom Jones 
9622ad0f7e9STom Jones 		DPRINTF(("%s: op=%u addr=%u val=%u\n", __func__, op, addr, val));
9632ad0f7e9STom Jones 		switch (op) {
9642ad0f7e9STom Jones 		case CSR_ASSIGN:
9652ad0f7e9STom Jones 			IWX_WRITE(sc, addr, val);
9662ad0f7e9STom Jones 			break;
9672ad0f7e9STom Jones 		case CSR_SETBIT:
9682ad0f7e9STom Jones 			IWX_SETBITS(sc, addr, (1 << val));
9692ad0f7e9STom Jones 			break;
9702ad0f7e9STom Jones 		case CSR_CLEARBIT:
9712ad0f7e9STom Jones 			IWX_CLRBITS(sc, addr, (1 << val));
9722ad0f7e9STom Jones 			break;
9732ad0f7e9STom Jones 		case PRPH_ASSIGN:
9742ad0f7e9STom Jones 			iwx_write_prph(sc, addr, val);
9752ad0f7e9STom Jones 			break;
9762ad0f7e9STom Jones 		case PRPH_SETBIT:
9772ad0f7e9STom Jones 			err = iwx_set_bits_prph(sc, addr, (1 << val));
9782ad0f7e9STom Jones 			if (err)
9792ad0f7e9STom Jones 				return err;
9802ad0f7e9STom Jones 			break;
9812ad0f7e9STom Jones 		case PRPH_CLEARBIT:
9822ad0f7e9STom Jones 			err = iwx_clear_bits_prph(sc, addr, (1 << val));
9832ad0f7e9STom Jones 			if (err)
9842ad0f7e9STom Jones 				return err;
9852ad0f7e9STom Jones 			break;
9862ad0f7e9STom Jones 		case PRPH_BLOCKBIT:
9872ad0f7e9STom Jones 			if (iwx_read_prph(sc, addr) & (1 << val))
9882ad0f7e9STom Jones 				goto monitor;
9892ad0f7e9STom Jones 			break;
9902ad0f7e9STom Jones 		default:
9912ad0f7e9STom Jones 			DPRINTF(("%s: FW debug - unknown OP %d\n",
9922ad0f7e9STom Jones 			    DEVNAME(sc), op));
9932ad0f7e9STom Jones 			break;
9942ad0f7e9STom Jones 		}
9952ad0f7e9STom Jones 	}
9962ad0f7e9STom Jones 
9972ad0f7e9STom Jones monitor:
9982ad0f7e9STom Jones 	if (mon_mode == EXTERNAL_MODE && sc->fw_mon.size) {
9992ad0f7e9STom Jones 		iwx_write_prph(sc, le32toh(base_reg),
10002ad0f7e9STom Jones 		    sc->fw_mon.paddr >> base_shift);
10012ad0f7e9STom Jones 		iwx_write_prph(sc, end_reg,
10022ad0f7e9STom Jones 		    (sc->fw_mon.paddr + sc->fw_mon.size - 256)
10032ad0f7e9STom Jones 		    >> end_shift);
10042ad0f7e9STom Jones 	}
10052ad0f7e9STom Jones 
10062ad0f7e9STom Jones 	iwx_nic_unlock(sc);
10072ad0f7e9STom Jones 	return 0;
10082ad0f7e9STom Jones #else
10092ad0f7e9STom Jones 	return 0;
10102ad0f7e9STom Jones #endif
10112ad0f7e9STom Jones }
10122ad0f7e9STom Jones 
10132ad0f7e9STom Jones static void
iwx_set_ltr(struct iwx_softc * sc)10142ad0f7e9STom Jones iwx_set_ltr(struct iwx_softc *sc)
10152ad0f7e9STom Jones {
10162ad0f7e9STom Jones 	uint32_t ltr_val = IWX_CSR_LTR_LONG_VAL_AD_NO_SNOOP_REQ |
10172ad0f7e9STom Jones 	    ((IWX_CSR_LTR_LONG_VAL_AD_SCALE_USEC <<
10182ad0f7e9STom Jones 	    IWX_CSR_LTR_LONG_VAL_AD_NO_SNOOP_SCALE_SHIFT) &
10192ad0f7e9STom Jones 	    IWX_CSR_LTR_LONG_VAL_AD_NO_SNOOP_SCALE_MASK) |
10202ad0f7e9STom Jones 	    ((250 << IWX_CSR_LTR_LONG_VAL_AD_NO_SNOOP_VAL_SHIFT) &
10212ad0f7e9STom Jones 	    IWX_CSR_LTR_LONG_VAL_AD_NO_SNOOP_VAL_MASK) |
10222ad0f7e9STom Jones 	    IWX_CSR_LTR_LONG_VAL_AD_SNOOP_REQ |
10232ad0f7e9STom Jones 	    ((IWX_CSR_LTR_LONG_VAL_AD_SCALE_USEC <<
10242ad0f7e9STom Jones 	    IWX_CSR_LTR_LONG_VAL_AD_SNOOP_SCALE_SHIFT) &
10252ad0f7e9STom Jones 	    IWX_CSR_LTR_LONG_VAL_AD_SNOOP_SCALE_MASK) |
10262ad0f7e9STom Jones 	    (250 & IWX_CSR_LTR_LONG_VAL_AD_SNOOP_VAL);
10272ad0f7e9STom Jones 
10282ad0f7e9STom Jones 	/*
10292ad0f7e9STom Jones 	 * To workaround hardware latency issues during the boot process,
10302ad0f7e9STom Jones 	 * initialize the LTR to ~250 usec (see ltr_val above).
10312ad0f7e9STom Jones 	 * The firmware initializes this again later (to a smaller value).
10322ad0f7e9STom Jones 	 */
10332ad0f7e9STom Jones 	if (!sc->sc_integrated) {
10342ad0f7e9STom Jones 		IWX_WRITE(sc, IWX_CSR_LTR_LONG_VAL_AD, ltr_val);
10352ad0f7e9STom Jones 	} else if (sc->sc_integrated &&
10362ad0f7e9STom Jones 		   sc->sc_device_family == IWX_DEVICE_FAMILY_22000) {
10372ad0f7e9STom Jones 		iwx_write_prph(sc, IWX_HPM_MAC_LTR_CSR,
10382ad0f7e9STom Jones 		    IWX_HPM_MAC_LRT_ENABLE_ALL);
10392ad0f7e9STom Jones 		iwx_write_prph(sc, IWX_HPM_UMAC_LTR, ltr_val);
10402ad0f7e9STom Jones 	}
10412ad0f7e9STom Jones }
10422ad0f7e9STom Jones 
10432ad0f7e9STom Jones int
iwx_ctxt_info_init(struct iwx_softc * sc,const struct iwx_fw_sects * fws)10442ad0f7e9STom Jones iwx_ctxt_info_init(struct iwx_softc *sc, const struct iwx_fw_sects *fws)
10452ad0f7e9STom Jones {
10462ad0f7e9STom Jones 	struct iwx_context_info *ctxt_info;
10472ad0f7e9STom Jones 	struct iwx_context_info_rbd_cfg *rx_cfg;
10482ad0f7e9STom Jones 	uint32_t control_flags = 0;
10492ad0f7e9STom Jones 	uint64_t paddr;
10502ad0f7e9STom Jones 	int err;
10512ad0f7e9STom Jones 
10522ad0f7e9STom Jones 	ctxt_info = sc->ctxt_info_dma.vaddr;
10532ad0f7e9STom Jones 	memset(ctxt_info, 0, sizeof(*ctxt_info));
10542ad0f7e9STom Jones 
10552ad0f7e9STom Jones 	ctxt_info->version.version = 0;
10562ad0f7e9STom Jones 	ctxt_info->version.mac_id =
10572ad0f7e9STom Jones 		htole16((uint16_t)IWX_READ(sc, IWX_CSR_HW_REV));
10582ad0f7e9STom Jones 	/* size is in DWs */
10592ad0f7e9STom Jones 	ctxt_info->version.size = htole16(sizeof(*ctxt_info) / 4);
10602ad0f7e9STom Jones 
10612ad0f7e9STom Jones 	KASSERT(IWX_RX_QUEUE_CB_SIZE(IWX_MQ_RX_TABLE_SIZE) < 0xF,
10622ad0f7e9STom Jones 	    ("IWX_RX_QUEUE_CB_SIZE exceeds rate table size"));
10632ad0f7e9STom Jones 
10642ad0f7e9STom Jones 	control_flags = IWX_CTXT_INFO_TFD_FORMAT_LONG |
10652ad0f7e9STom Jones 			(IWX_RX_QUEUE_CB_SIZE(IWX_MQ_RX_TABLE_SIZE) <<
10662ad0f7e9STom Jones 			 IWX_CTXT_INFO_RB_CB_SIZE_POS) |
10672ad0f7e9STom Jones 			(IWX_CTXT_INFO_RB_SIZE_4K << IWX_CTXT_INFO_RB_SIZE_POS);
10682ad0f7e9STom Jones 	ctxt_info->control.control_flags = htole32(control_flags);
10692ad0f7e9STom Jones 
10702ad0f7e9STom Jones 	/* initialize RX default queue */
10712ad0f7e9STom Jones 	rx_cfg = &ctxt_info->rbd_cfg;
10722ad0f7e9STom Jones 	rx_cfg->free_rbd_addr = htole64(sc->rxq.free_desc_dma.paddr);
10732ad0f7e9STom Jones 	rx_cfg->used_rbd_addr = htole64(sc->rxq.used_desc_dma.paddr);
10742ad0f7e9STom Jones 	rx_cfg->status_wr_ptr = htole64(sc->rxq.stat_dma.paddr);
10752ad0f7e9STom Jones 
10762ad0f7e9STom Jones 	/* initialize TX command queue */
10772ad0f7e9STom Jones 	ctxt_info->hcmd_cfg.cmd_queue_addr =
10782ad0f7e9STom Jones 	    htole64(sc->txq[IWX_DQA_CMD_QUEUE].desc_dma.paddr);
10792ad0f7e9STom Jones 	ctxt_info->hcmd_cfg.cmd_queue_size =
10802ad0f7e9STom Jones 		IWX_TFD_QUEUE_CB_SIZE(IWX_TX_RING_COUNT);
10812ad0f7e9STom Jones 
10822ad0f7e9STom Jones 	/* allocate ucode sections in dram and set addresses */
10832ad0f7e9STom Jones 	err = iwx_init_fw_sec(sc, fws, &ctxt_info->dram);
10842ad0f7e9STom Jones 	if (err) {
10852ad0f7e9STom Jones 		iwx_ctxt_info_free_fw_img(sc);
10862ad0f7e9STom Jones 		return err;
10872ad0f7e9STom Jones 	}
10882ad0f7e9STom Jones 
10892ad0f7e9STom Jones 	/* Configure debug, if exists */
10902ad0f7e9STom Jones 	if (sc->sc_fw.dbg_dest_tlv_v1) {
10912ad0f7e9STom Jones #if 1
10922ad0f7e9STom Jones 		err = iwx_apply_debug_destination(sc);
10932ad0f7e9STom Jones 		if (err) {
10942ad0f7e9STom Jones 			iwx_ctxt_info_free_fw_img(sc);
10952ad0f7e9STom Jones 			return err;
10962ad0f7e9STom Jones 		}
10972ad0f7e9STom Jones #endif
10982ad0f7e9STom Jones 	}
10992ad0f7e9STom Jones 
11002ad0f7e9STom Jones 	/*
11012ad0f7e9STom Jones 	 * Write the context info DMA base address. The device expects a
11022ad0f7e9STom Jones 	 * 64-bit address but a simple bus_space_write_8 to this register
11032ad0f7e9STom Jones 	 * won't work on some devices, such as the AX201.
11042ad0f7e9STom Jones 	 */
11052ad0f7e9STom Jones 	paddr = sc->ctxt_info_dma.paddr;
11062ad0f7e9STom Jones 	IWX_WRITE(sc, IWX_CSR_CTXT_INFO_BA, paddr & 0xffffffff);
11072ad0f7e9STom Jones 	IWX_WRITE(sc, IWX_CSR_CTXT_INFO_BA + 4, paddr >> 32);
11082ad0f7e9STom Jones 
11092ad0f7e9STom Jones 	/* kick FW self load */
11102ad0f7e9STom Jones 	if (!iwx_nic_lock(sc)) {
11112ad0f7e9STom Jones 		iwx_ctxt_info_free_fw_img(sc);
11122ad0f7e9STom Jones 		return EBUSY;
11132ad0f7e9STom Jones 	}
11142ad0f7e9STom Jones 
11152ad0f7e9STom Jones 	iwx_set_ltr(sc);
11162ad0f7e9STom Jones 	iwx_write_prph(sc, IWX_UREG_CPU_INIT_RUN, 1);
11172ad0f7e9STom Jones 	iwx_nic_unlock(sc);
11182ad0f7e9STom Jones 
11192ad0f7e9STom Jones 	/* Context info will be released upon alive or failure to get one */
11202ad0f7e9STom Jones 
11212ad0f7e9STom Jones 	return 0;
11222ad0f7e9STom Jones }
11232ad0f7e9STom Jones 
11242ad0f7e9STom Jones static int
iwx_ctxt_info_gen3_init(struct iwx_softc * sc,const struct iwx_fw_sects * fws)11252ad0f7e9STom Jones iwx_ctxt_info_gen3_init(struct iwx_softc *sc, const struct iwx_fw_sects *fws)
11262ad0f7e9STom Jones {
11272ad0f7e9STom Jones 	struct iwx_context_info_gen3 *ctxt_info_gen3;
11282ad0f7e9STom Jones 	struct iwx_prph_scratch *prph_scratch;
11292ad0f7e9STom Jones 	struct iwx_prph_scratch_ctrl_cfg *prph_sc_ctrl;
11302ad0f7e9STom Jones 	uint16_t cb_size;
11312ad0f7e9STom Jones 	uint32_t control_flags, scratch_size;
11322ad0f7e9STom Jones 	uint64_t paddr;
11332ad0f7e9STom Jones 	int err;
11342ad0f7e9STom Jones 
11352ad0f7e9STom Jones 	if (sc->sc_fw.iml == NULL || sc->sc_fw.iml_len == 0) {
11362ad0f7e9STom Jones 		printf("%s: no image loader found in firmware file\n",
11372ad0f7e9STom Jones 		    DEVNAME(sc));
11382ad0f7e9STom Jones 		iwx_ctxt_info_free_fw_img(sc);
11392ad0f7e9STom Jones 		return EINVAL;
11402ad0f7e9STom Jones 	}
11412ad0f7e9STom Jones 
11422ad0f7e9STom Jones 	err = iwx_dma_contig_alloc(sc->sc_dmat, &sc->iml_dma,
11432ad0f7e9STom Jones 	    sc->sc_fw.iml_len, 1);
11442ad0f7e9STom Jones 	if (err) {
11452ad0f7e9STom Jones 		printf("%s: could not allocate DMA memory for "
11462ad0f7e9STom Jones 		    "firmware image loader\n", DEVNAME(sc));
11472ad0f7e9STom Jones 		iwx_ctxt_info_free_fw_img(sc);
11482ad0f7e9STom Jones 		return ENOMEM;
11492ad0f7e9STom Jones 	}
11502ad0f7e9STom Jones 
11512ad0f7e9STom Jones 	prph_scratch = sc->prph_scratch_dma.vaddr;
11522ad0f7e9STom Jones 	memset(prph_scratch, 0, sizeof(*prph_scratch));
11532ad0f7e9STom Jones 	prph_sc_ctrl = &prph_scratch->ctrl_cfg;
11542ad0f7e9STom Jones 	prph_sc_ctrl->version.version = 0;
11552ad0f7e9STom Jones 	prph_sc_ctrl->version.mac_id = htole16(IWX_READ(sc, IWX_CSR_HW_REV));
11562ad0f7e9STom Jones 	prph_sc_ctrl->version.size = htole16(sizeof(*prph_scratch) / 4);
11572ad0f7e9STom Jones 
11582ad0f7e9STom Jones 	control_flags = IWX_PRPH_SCRATCH_RB_SIZE_4K |
11592ad0f7e9STom Jones 	    IWX_PRPH_SCRATCH_MTR_MODE |
11602ad0f7e9STom Jones 	    (IWX_PRPH_MTR_FORMAT_256B & IWX_PRPH_SCRATCH_MTR_FORMAT);
11612ad0f7e9STom Jones 	if (sc->sc_imr_enabled)
11622ad0f7e9STom Jones 		control_flags |= IWX_PRPH_SCRATCH_IMR_DEBUG_EN;
11632ad0f7e9STom Jones 	prph_sc_ctrl->control.control_flags = htole32(control_flags);
11642ad0f7e9STom Jones 
11652ad0f7e9STom Jones 	/* initialize RX default queue */
11662ad0f7e9STom Jones 	prph_sc_ctrl->rbd_cfg.free_rbd_addr =
11672ad0f7e9STom Jones 	    htole64(sc->rxq.free_desc_dma.paddr);
11682ad0f7e9STom Jones 
11692ad0f7e9STom Jones 	/* allocate ucode sections in dram and set addresses */
11702ad0f7e9STom Jones 	err = iwx_init_fw_sec(sc, fws, &prph_scratch->dram);
11712ad0f7e9STom Jones 	if (err) {
11722ad0f7e9STom Jones 		iwx_dma_contig_free(&sc->iml_dma);
11732ad0f7e9STom Jones 		iwx_ctxt_info_free_fw_img(sc);
11742ad0f7e9STom Jones 		return err;
11752ad0f7e9STom Jones 	}
11762ad0f7e9STom Jones 
11772ad0f7e9STom Jones 	ctxt_info_gen3 = sc->ctxt_info_dma.vaddr;
11782ad0f7e9STom Jones 	memset(ctxt_info_gen3, 0, sizeof(*ctxt_info_gen3));
11792ad0f7e9STom Jones 	ctxt_info_gen3->prph_info_base_addr = htole64(sc->prph_info_dma.paddr);
11802ad0f7e9STom Jones 	ctxt_info_gen3->prph_scratch_base_addr =
11812ad0f7e9STom Jones 	    htole64(sc->prph_scratch_dma.paddr);
11822ad0f7e9STom Jones 	scratch_size = sizeof(*prph_scratch);
11832ad0f7e9STom Jones 	ctxt_info_gen3->prph_scratch_size = htole32(scratch_size);
11842ad0f7e9STom Jones 	ctxt_info_gen3->cr_head_idx_arr_base_addr =
11852ad0f7e9STom Jones 	    htole64(sc->rxq.stat_dma.paddr);
11862ad0f7e9STom Jones 	ctxt_info_gen3->tr_tail_idx_arr_base_addr =
11872ad0f7e9STom Jones 	    htole64(sc->prph_info_dma.paddr + PAGE_SIZE / 2);
11882ad0f7e9STom Jones 	ctxt_info_gen3->cr_tail_idx_arr_base_addr =
11892ad0f7e9STom Jones 	    htole64(sc->prph_info_dma.paddr + 3 * PAGE_SIZE / 4);
11902ad0f7e9STom Jones 	ctxt_info_gen3->mtr_base_addr =
11912ad0f7e9STom Jones 	    htole64(sc->txq[IWX_DQA_CMD_QUEUE].desc_dma.paddr);
11922ad0f7e9STom Jones 	ctxt_info_gen3->mcr_base_addr = htole64(sc->rxq.used_desc_dma.paddr);
11932ad0f7e9STom Jones 	cb_size = IWX_TFD_QUEUE_CB_SIZE(IWX_TX_RING_COUNT);
11942ad0f7e9STom Jones 	ctxt_info_gen3->mtr_size = htole16(cb_size);
11952ad0f7e9STom Jones 	cb_size = IWX_RX_QUEUE_CB_SIZE(IWX_MQ_RX_TABLE_SIZE);
11962ad0f7e9STom Jones 	ctxt_info_gen3->mcr_size = htole16(cb_size);
11972ad0f7e9STom Jones 
11982ad0f7e9STom Jones 	memcpy(sc->iml_dma.vaddr, sc->sc_fw.iml, sc->sc_fw.iml_len);
11992ad0f7e9STom Jones 
12002ad0f7e9STom Jones 	paddr = sc->ctxt_info_dma.paddr;
12012ad0f7e9STom Jones 	IWX_WRITE(sc, IWX_CSR_CTXT_INFO_ADDR, paddr & 0xffffffff);
12022ad0f7e9STom Jones 	IWX_WRITE(sc, IWX_CSR_CTXT_INFO_ADDR + 4, paddr >> 32);
12032ad0f7e9STom Jones 
12042ad0f7e9STom Jones 	paddr = sc->iml_dma.paddr;
12052ad0f7e9STom Jones 	IWX_WRITE(sc, IWX_CSR_IML_DATA_ADDR, paddr & 0xffffffff);
12062ad0f7e9STom Jones 	IWX_WRITE(sc, IWX_CSR_IML_DATA_ADDR + 4, paddr >> 32);
12072ad0f7e9STom Jones 	IWX_WRITE(sc, IWX_CSR_IML_SIZE_ADDR, sc->sc_fw.iml_len);
12082ad0f7e9STom Jones 
12092ad0f7e9STom Jones 	IWX_SETBITS(sc, IWX_CSR_CTXT_INFO_BOOT_CTRL,
12102ad0f7e9STom Jones 		    IWX_CSR_AUTO_FUNC_BOOT_ENA);
12112ad0f7e9STom Jones 
12122ad0f7e9STom Jones 	IWX_DPRINTF(sc, IWX_DEBUG_FIRMWARE_TLV,
12132ad0f7e9STom Jones 	    "%s:%d kicking fw to get going\n", __func__, __LINE__);
12142ad0f7e9STom Jones 
12152ad0f7e9STom Jones 	/* kick FW self load */
12162ad0f7e9STom Jones 	if (!iwx_nic_lock(sc)) {
12172ad0f7e9STom Jones 		iwx_dma_contig_free(&sc->iml_dma);
12182ad0f7e9STom Jones 		iwx_ctxt_info_free_fw_img(sc);
12192ad0f7e9STom Jones 		return EBUSY;
12202ad0f7e9STom Jones 	}
12212ad0f7e9STom Jones 	iwx_set_ltr(sc);
12222ad0f7e9STom Jones 	iwx_write_umac_prph(sc, IWX_UREG_CPU_INIT_RUN, 1);
12232ad0f7e9STom Jones 	iwx_nic_unlock(sc);
12242ad0f7e9STom Jones 
12252ad0f7e9STom Jones 	/* Context info will be released upon alive or failure to get one */
12262ad0f7e9STom Jones 	return 0;
12272ad0f7e9STom Jones }
12282ad0f7e9STom Jones 
12292ad0f7e9STom Jones static void
iwx_ctxt_info_free_fw_img(struct iwx_softc * sc)12302ad0f7e9STom Jones iwx_ctxt_info_free_fw_img(struct iwx_softc *sc)
12312ad0f7e9STom Jones {
12322ad0f7e9STom Jones 	struct iwx_self_init_dram *dram = &sc->init_dram;
12332ad0f7e9STom Jones 	int i;
12342ad0f7e9STom Jones 
12352ad0f7e9STom Jones 	if (!dram->fw)
12362ad0f7e9STom Jones 		return;
12372ad0f7e9STom Jones 
12382ad0f7e9STom Jones 	for (i = 0; i < dram->lmac_cnt + dram->umac_cnt; i++)
12392ad0f7e9STom Jones 		iwx_dma_contig_free(&dram->fw[i]);
12402ad0f7e9STom Jones 
12412ad0f7e9STom Jones 	free(dram->fw, M_DEVBUF);
12422ad0f7e9STom Jones 	dram->lmac_cnt = 0;
12432ad0f7e9STom Jones 	dram->umac_cnt = 0;
12442ad0f7e9STom Jones 	dram->fw = NULL;
12452ad0f7e9STom Jones }
12462ad0f7e9STom Jones 
12472ad0f7e9STom Jones static int
iwx_firmware_store_section(struct iwx_softc * sc,enum iwx_ucode_type type,const uint8_t * data,size_t dlen)12482ad0f7e9STom Jones iwx_firmware_store_section(struct iwx_softc *sc, enum iwx_ucode_type type,
12492ad0f7e9STom Jones     const uint8_t *data, size_t dlen)
12502ad0f7e9STom Jones {
12512ad0f7e9STom Jones 	struct iwx_fw_sects *fws;
12522ad0f7e9STom Jones 	struct iwx_fw_onesect *fwone;
12532ad0f7e9STom Jones 
12542ad0f7e9STom Jones 	if (type >= IWX_UCODE_TYPE_MAX)
12552ad0f7e9STom Jones 		return EINVAL;
12562ad0f7e9STom Jones 	if (dlen < sizeof(uint32_t))
12572ad0f7e9STom Jones 		return EINVAL;
12582ad0f7e9STom Jones 
12592ad0f7e9STom Jones 	fws = &sc->sc_fw.fw_sects[type];
12602ad0f7e9STom Jones 	IWX_DPRINTF(sc, IWX_DEBUG_FIRMWARE_TLV,
12612ad0f7e9STom Jones 	    "%s: ucode type %d section %d\n", DEVNAME(sc), type, fws->fw_count);
12622ad0f7e9STom Jones 	if (fws->fw_count >= IWX_UCODE_SECT_MAX)
12632ad0f7e9STom Jones 		return EINVAL;
12642ad0f7e9STom Jones 
12652ad0f7e9STom Jones 	fwone = &fws->fw_sect[fws->fw_count];
12662ad0f7e9STom Jones 
12672ad0f7e9STom Jones 	/* first 32bit are device load offset */
12682ad0f7e9STom Jones 	memcpy(&fwone->fws_devoff, data, sizeof(uint32_t));
12692ad0f7e9STom Jones 
12702ad0f7e9STom Jones 	/* rest is data */
12712ad0f7e9STom Jones 	fwone->fws_data = data + sizeof(uint32_t);
12722ad0f7e9STom Jones 	fwone->fws_len = dlen - sizeof(uint32_t);
12732ad0f7e9STom Jones 
12742ad0f7e9STom Jones 	fws->fw_count++;
12752ad0f7e9STom Jones 	fws->fw_totlen += fwone->fws_len;
12762ad0f7e9STom Jones 
12772ad0f7e9STom Jones 	return 0;
12782ad0f7e9STom Jones }
12792ad0f7e9STom Jones 
12802ad0f7e9STom Jones #define IWX_DEFAULT_SCAN_CHANNELS	40
12812ad0f7e9STom Jones /* Newer firmware might support more channels. Raise this value if needed. */
12822ad0f7e9STom Jones #define IWX_MAX_SCAN_CHANNELS		67 /* as of iwx-cc-a0-62 firmware */
12832ad0f7e9STom Jones 
12842ad0f7e9STom Jones struct iwx_tlv_calib_data {
12852ad0f7e9STom Jones 	uint32_t ucode_type;
12862ad0f7e9STom Jones 	struct iwx_tlv_calib_ctrl calib;
12872ad0f7e9STom Jones } __packed;
12882ad0f7e9STom Jones 
12892ad0f7e9STom Jones static int
iwx_set_default_calib(struct iwx_softc * sc,const void * data)12902ad0f7e9STom Jones iwx_set_default_calib(struct iwx_softc *sc, const void *data)
12912ad0f7e9STom Jones {
12922ad0f7e9STom Jones 	const struct iwx_tlv_calib_data *def_calib = data;
12932ad0f7e9STom Jones 	uint32_t ucode_type = le32toh(def_calib->ucode_type);
12942ad0f7e9STom Jones 
12952ad0f7e9STom Jones 	if (ucode_type >= IWX_UCODE_TYPE_MAX)
12962ad0f7e9STom Jones 		return EINVAL;
12972ad0f7e9STom Jones 
12982ad0f7e9STom Jones 	sc->sc_default_calib[ucode_type].flow_trigger =
12992ad0f7e9STom Jones 	    def_calib->calib.flow_trigger;
13002ad0f7e9STom Jones 	sc->sc_default_calib[ucode_type].event_trigger =
13012ad0f7e9STom Jones 	    def_calib->calib.event_trigger;
13022ad0f7e9STom Jones 
13032ad0f7e9STom Jones 	return 0;
13042ad0f7e9STom Jones }
13052ad0f7e9STom Jones 
13062ad0f7e9STom Jones static void
iwx_fw_info_free(struct iwx_fw_info * fw)13072ad0f7e9STom Jones iwx_fw_info_free(struct iwx_fw_info *fw)
13082ad0f7e9STom Jones {
13092ad0f7e9STom Jones 	free(fw->fw_rawdata, M_DEVBUF);
13102ad0f7e9STom Jones 	fw->fw_rawdata = NULL;
13112ad0f7e9STom Jones 	fw->fw_rawsize = 0;
13122ad0f7e9STom Jones 	/* don't touch fw->fw_status */
13132ad0f7e9STom Jones 	memset(fw->fw_sects, 0, sizeof(fw->fw_sects));
13142ad0f7e9STom Jones 	free(fw->iml, M_DEVBUF);
13152ad0f7e9STom Jones 	fw->iml = NULL;
13162ad0f7e9STom Jones 	fw->iml_len = 0;
13172ad0f7e9STom Jones }
13182ad0f7e9STom Jones 
13192ad0f7e9STom Jones #define IWX_FW_ADDR_CACHE_CONTROL 0xC0000000
13202ad0f7e9STom Jones 
13212ad0f7e9STom Jones static int
iwx_read_firmware(struct iwx_softc * sc)13222ad0f7e9STom Jones iwx_read_firmware(struct iwx_softc *sc)
13232ad0f7e9STom Jones {
13242ad0f7e9STom Jones 	struct iwx_fw_info *fw = &sc->sc_fw;
13252ad0f7e9STom Jones 	const struct iwx_tlv_ucode_header *uhdr;
13262ad0f7e9STom Jones 	struct iwx_ucode_tlv tlv;
13272ad0f7e9STom Jones 	uint32_t tlv_type;
13282ad0f7e9STom Jones 	const uint8_t *data;
13292ad0f7e9STom Jones 	int err = 0;
13302ad0f7e9STom Jones 	size_t len;
13312ad0f7e9STom Jones 	const struct firmware *fwp;
13322ad0f7e9STom Jones 
13332ad0f7e9STom Jones 	if (fw->fw_status == IWX_FW_STATUS_DONE)
13342ad0f7e9STom Jones 		return 0;
13352ad0f7e9STom Jones 
13362ad0f7e9STom Jones 	fw->fw_status = IWX_FW_STATUS_INPROGRESS;
13372ad0f7e9STom Jones 	fwp = firmware_get(sc->sc_fwname);
13382ad0f7e9STom Jones 	sc->sc_fwp = fwp;
13392ad0f7e9STom Jones 
13402ad0f7e9STom Jones 	if (fwp == NULL) {
13412ad0f7e9STom Jones 		printf("%s: could not read firmware %s\n",
13422ad0f7e9STom Jones 		    DEVNAME(sc), sc->sc_fwname);
13432ad0f7e9STom Jones 		err = ENOENT;
13442ad0f7e9STom Jones 		goto out;
13452ad0f7e9STom Jones 	}
13462ad0f7e9STom Jones 
13472ad0f7e9STom Jones 	IWX_DPRINTF(sc, IWX_DEBUG_FW, "%s:%d %s: using firmware %s\n",
13482ad0f7e9STom Jones 		__func__, __LINE__, DEVNAME(sc), sc->sc_fwname);
13492ad0f7e9STom Jones 
13502ad0f7e9STom Jones 
13512ad0f7e9STom Jones 	sc->sc_capaflags = 0;
13522ad0f7e9STom Jones 	sc->sc_capa_n_scan_channels = IWX_DEFAULT_SCAN_CHANNELS;
13532ad0f7e9STom Jones 	memset(sc->sc_enabled_capa, 0, sizeof(sc->sc_enabled_capa));
13542ad0f7e9STom Jones 	memset(sc->sc_ucode_api, 0, sizeof(sc->sc_ucode_api));
13552ad0f7e9STom Jones 	sc->n_cmd_versions = 0;
13562ad0f7e9STom Jones 
13572ad0f7e9STom Jones 	uhdr = (const void *)(fwp->data);
13582ad0f7e9STom Jones 	if (*(const uint32_t *)fwp->data != 0
13592ad0f7e9STom Jones 	    || le32toh(uhdr->magic) != IWX_TLV_UCODE_MAGIC) {
13602ad0f7e9STom Jones 		printf("%s: invalid firmware %s\n",
13612ad0f7e9STom Jones 		    DEVNAME(sc), sc->sc_fwname);
13622ad0f7e9STom Jones 		err = EINVAL;
13632ad0f7e9STom Jones 		goto out;
13642ad0f7e9STom Jones 	}
13652ad0f7e9STom Jones 
13662ad0f7e9STom Jones 	iwx_fw_version_str(sc->sc_fwver, sizeof(sc->sc_fwver),
13672ad0f7e9STom Jones 	    IWX_UCODE_MAJOR(le32toh(uhdr->ver)),
13682ad0f7e9STom Jones 	    IWX_UCODE_MINOR(le32toh(uhdr->ver)),
13692ad0f7e9STom Jones 	    IWX_UCODE_API(le32toh(uhdr->ver)));
13702ad0f7e9STom Jones 
13712ad0f7e9STom Jones 	data = uhdr->data;
13722ad0f7e9STom Jones 	len = fwp->datasize - sizeof(*uhdr);
13732ad0f7e9STom Jones 
13742ad0f7e9STom Jones 	while (len >= sizeof(tlv)) {
13752ad0f7e9STom Jones 		size_t tlv_len;
13762ad0f7e9STom Jones 		const void *tlv_data;
13772ad0f7e9STom Jones 
13782ad0f7e9STom Jones 		memcpy(&tlv, data, sizeof(tlv));
13792ad0f7e9STom Jones 		tlv_len = le32toh(tlv.length);
13802ad0f7e9STom Jones 		tlv_type = le32toh(tlv.type);
13812ad0f7e9STom Jones 
13822ad0f7e9STom Jones 		len -= sizeof(tlv);
13832ad0f7e9STom Jones 		data += sizeof(tlv);
13842ad0f7e9STom Jones 		tlv_data = data;
13852ad0f7e9STom Jones 
13862ad0f7e9STom Jones 		if (len < tlv_len) {
13872ad0f7e9STom Jones 			printf("%s: firmware too short: %zu bytes\n",
13882ad0f7e9STom Jones 			    DEVNAME(sc), len);
13892ad0f7e9STom Jones 			err = EINVAL;
13902ad0f7e9STom Jones 			goto parse_out;
13912ad0f7e9STom Jones 		}
13922ad0f7e9STom Jones 
13932ad0f7e9STom Jones 		switch (tlv_type) {
13942ad0f7e9STom Jones 		case IWX_UCODE_TLV_PROBE_MAX_LEN:
13952ad0f7e9STom Jones 			if (tlv_len < sizeof(uint32_t)) {
13962ad0f7e9STom Jones 				err = EINVAL;
13972ad0f7e9STom Jones 				goto parse_out;
13982ad0f7e9STom Jones 			}
13992ad0f7e9STom Jones 			sc->sc_capa_max_probe_len
14002ad0f7e9STom Jones 			    = le32toh(*(const uint32_t *)tlv_data);
14012ad0f7e9STom Jones 			if (sc->sc_capa_max_probe_len >
14022ad0f7e9STom Jones 			    IWX_SCAN_OFFLOAD_PROBE_REQ_SIZE) {
14032ad0f7e9STom Jones 				err = EINVAL;
14042ad0f7e9STom Jones 				goto parse_out;
14052ad0f7e9STom Jones 			}
14062ad0f7e9STom Jones 			break;
14072ad0f7e9STom Jones 		case IWX_UCODE_TLV_PAN:
14082ad0f7e9STom Jones 			if (tlv_len) {
14092ad0f7e9STom Jones 				err = EINVAL;
14102ad0f7e9STom Jones 				goto parse_out;
14112ad0f7e9STom Jones 			}
14122ad0f7e9STom Jones 			sc->sc_capaflags |= IWX_UCODE_TLV_FLAGS_PAN;
14132ad0f7e9STom Jones 			break;
14142ad0f7e9STom Jones 		case IWX_UCODE_TLV_FLAGS:
14152ad0f7e9STom Jones 			if (tlv_len < sizeof(uint32_t)) {
14162ad0f7e9STom Jones 				err = EINVAL;
14172ad0f7e9STom Jones 				goto parse_out;
14182ad0f7e9STom Jones 			}
14192ad0f7e9STom Jones 			/*
14202ad0f7e9STom Jones 			 * Apparently there can be many flags, but Linux driver
14212ad0f7e9STom Jones 			 * parses only the first one, and so do we.
14222ad0f7e9STom Jones 			 *
14232ad0f7e9STom Jones 			 * XXX: why does this override IWX_UCODE_TLV_PAN?
14242ad0f7e9STom Jones 			 * Intentional or a bug?  Observations from
14252ad0f7e9STom Jones 			 * current firmware file:
14262ad0f7e9STom Jones 			 *  1) TLV_PAN is parsed first
14272ad0f7e9STom Jones 			 *  2) TLV_FLAGS contains TLV_FLAGS_PAN
14282ad0f7e9STom Jones 			 * ==> this resets TLV_PAN to itself... hnnnk
14292ad0f7e9STom Jones 			 */
14302ad0f7e9STom Jones 			sc->sc_capaflags = le32toh(*(const uint32_t *)tlv_data);
14312ad0f7e9STom Jones 			break;
14322ad0f7e9STom Jones 		case IWX_UCODE_TLV_CSCHEME:
14332ad0f7e9STom Jones 			err = iwx_store_cscheme(sc, tlv_data, tlv_len);
14342ad0f7e9STom Jones 			if (err)
14352ad0f7e9STom Jones 				goto parse_out;
14362ad0f7e9STom Jones 			break;
14372ad0f7e9STom Jones 		case IWX_UCODE_TLV_NUM_OF_CPU: {
14382ad0f7e9STom Jones 			uint32_t num_cpu;
14392ad0f7e9STom Jones 			if (tlv_len != sizeof(uint32_t)) {
14402ad0f7e9STom Jones 				err = EINVAL;
14412ad0f7e9STom Jones 				goto parse_out;
14422ad0f7e9STom Jones 			}
14432ad0f7e9STom Jones 			num_cpu = le32toh(*(const uint32_t *)tlv_data);
14442ad0f7e9STom Jones 			if (num_cpu < 1 || num_cpu > 2) {
14452ad0f7e9STom Jones 				err = EINVAL;
14462ad0f7e9STom Jones 				goto parse_out;
14472ad0f7e9STom Jones 			}
14482ad0f7e9STom Jones 			break;
14492ad0f7e9STom Jones 		}
14502ad0f7e9STom Jones 		case IWX_UCODE_TLV_SEC_RT:
14512ad0f7e9STom Jones 			err = iwx_firmware_store_section(sc,
14522ad0f7e9STom Jones 			    IWX_UCODE_TYPE_REGULAR, tlv_data, tlv_len);
14532ad0f7e9STom Jones 			if (err)
14542ad0f7e9STom Jones 				goto parse_out;
14552ad0f7e9STom Jones 			break;
14562ad0f7e9STom Jones 		case IWX_UCODE_TLV_SEC_INIT:
14572ad0f7e9STom Jones 			err = iwx_firmware_store_section(sc,
14582ad0f7e9STom Jones 			    IWX_UCODE_TYPE_INIT, tlv_data, tlv_len);
14592ad0f7e9STom Jones 			if (err)
14602ad0f7e9STom Jones 				goto parse_out;
14612ad0f7e9STom Jones 			break;
14622ad0f7e9STom Jones 		case IWX_UCODE_TLV_SEC_WOWLAN:
14632ad0f7e9STom Jones 			err = iwx_firmware_store_section(sc,
14642ad0f7e9STom Jones 			    IWX_UCODE_TYPE_WOW, tlv_data, tlv_len);
14652ad0f7e9STom Jones 			if (err)
14662ad0f7e9STom Jones 				goto parse_out;
14672ad0f7e9STom Jones 			break;
14682ad0f7e9STom Jones 		case IWX_UCODE_TLV_DEF_CALIB:
14692ad0f7e9STom Jones 			if (tlv_len != sizeof(struct iwx_tlv_calib_data)) {
14702ad0f7e9STom Jones 				err = EINVAL;
14712ad0f7e9STom Jones 				goto parse_out;
14722ad0f7e9STom Jones 			}
14732ad0f7e9STom Jones 			err = iwx_set_default_calib(sc, tlv_data);
14742ad0f7e9STom Jones 			if (err)
14752ad0f7e9STom Jones 				goto parse_out;
14762ad0f7e9STom Jones 			break;
14772ad0f7e9STom Jones 		case IWX_UCODE_TLV_PHY_SKU:
14782ad0f7e9STom Jones 			if (tlv_len != sizeof(uint32_t)) {
14792ad0f7e9STom Jones 				err = EINVAL;
14802ad0f7e9STom Jones 				goto parse_out;
14812ad0f7e9STom Jones 			}
14822ad0f7e9STom Jones 			sc->sc_fw_phy_config = le32toh(*(const uint32_t *)tlv_data);
14832ad0f7e9STom Jones 			break;
14842ad0f7e9STom Jones 
14852ad0f7e9STom Jones 		case IWX_UCODE_TLV_API_CHANGES_SET: {
14862ad0f7e9STom Jones 			const struct iwx_ucode_api *api;
14872ad0f7e9STom Jones 			int idx, i;
14882ad0f7e9STom Jones 			if (tlv_len != sizeof(*api)) {
14892ad0f7e9STom Jones 				err = EINVAL;
14902ad0f7e9STom Jones 				goto parse_out;
14912ad0f7e9STom Jones 			}
14922ad0f7e9STom Jones 			api = (const struct iwx_ucode_api *)tlv_data;
14932ad0f7e9STom Jones 			idx = le32toh(api->api_index);
14942ad0f7e9STom Jones 			if (idx >= howmany(IWX_NUM_UCODE_TLV_API, 32)) {
14952ad0f7e9STom Jones 				err = EINVAL;
14962ad0f7e9STom Jones 				goto parse_out;
14972ad0f7e9STom Jones 			}
14982ad0f7e9STom Jones 			for (i = 0; i < 32; i++) {
14992ad0f7e9STom Jones 				if ((le32toh(api->api_flags) & (1 << i)) == 0)
15002ad0f7e9STom Jones 					continue;
15012ad0f7e9STom Jones 				setbit(sc->sc_ucode_api, i + (32 * idx));
15022ad0f7e9STom Jones 			}
15032ad0f7e9STom Jones 			break;
15042ad0f7e9STom Jones 		}
15052ad0f7e9STom Jones 
15062ad0f7e9STom Jones 		case IWX_UCODE_TLV_ENABLED_CAPABILITIES: {
15072ad0f7e9STom Jones 			const struct iwx_ucode_capa *capa;
15082ad0f7e9STom Jones 			int idx, i;
15092ad0f7e9STom Jones 			if (tlv_len != sizeof(*capa)) {
15102ad0f7e9STom Jones 				err = EINVAL;
15112ad0f7e9STom Jones 				goto parse_out;
15122ad0f7e9STom Jones 			}
15132ad0f7e9STom Jones 			capa = (const struct iwx_ucode_capa *)tlv_data;
15142ad0f7e9STom Jones 			idx = le32toh(capa->api_index);
15152ad0f7e9STom Jones 			if (idx >= howmany(IWX_NUM_UCODE_TLV_CAPA, 32)) {
15162ad0f7e9STom Jones 				goto parse_out;
15172ad0f7e9STom Jones 			}
15182ad0f7e9STom Jones 			for (i = 0; i < 32; i++) {
15192ad0f7e9STom Jones 				if ((le32toh(capa->api_capa) & (1 << i)) == 0)
15202ad0f7e9STom Jones 					continue;
15212ad0f7e9STom Jones 				setbit(sc->sc_enabled_capa, i + (32 * idx));
15222ad0f7e9STom Jones 			}
15232ad0f7e9STom Jones 			break;
15242ad0f7e9STom Jones 		}
15252ad0f7e9STom Jones 
15262ad0f7e9STom Jones 		case IWX_UCODE_TLV_SDIO_ADMA_ADDR:
15272ad0f7e9STom Jones 		case IWX_UCODE_TLV_FW_GSCAN_CAPA:
15282ad0f7e9STom Jones 			/* ignore, not used by current driver */
15292ad0f7e9STom Jones 			break;
15302ad0f7e9STom Jones 
15312ad0f7e9STom Jones 		case IWX_UCODE_TLV_SEC_RT_USNIFFER:
15322ad0f7e9STom Jones 			err = iwx_firmware_store_section(sc,
15332ad0f7e9STom Jones 			    IWX_UCODE_TYPE_REGULAR_USNIFFER, tlv_data,
15342ad0f7e9STom Jones 			    tlv_len);
15352ad0f7e9STom Jones 			if (err)
15362ad0f7e9STom Jones 				goto parse_out;
15372ad0f7e9STom Jones 			break;
15382ad0f7e9STom Jones 
15392ad0f7e9STom Jones 		case IWX_UCODE_TLV_PAGING:
15402ad0f7e9STom Jones 			if (tlv_len != sizeof(uint32_t)) {
15412ad0f7e9STom Jones 				err = EINVAL;
15422ad0f7e9STom Jones 				goto parse_out;
15432ad0f7e9STom Jones 			}
15442ad0f7e9STom Jones 			break;
15452ad0f7e9STom Jones 
15462ad0f7e9STom Jones 		case IWX_UCODE_TLV_N_SCAN_CHANNELS:
15472ad0f7e9STom Jones 			if (tlv_len != sizeof(uint32_t)) {
15482ad0f7e9STom Jones 				err = EINVAL;
15492ad0f7e9STom Jones 				goto parse_out;
15502ad0f7e9STom Jones 			}
15512ad0f7e9STom Jones 			sc->sc_capa_n_scan_channels =
15522ad0f7e9STom Jones 			  le32toh(*(const uint32_t *)tlv_data);
15532ad0f7e9STom Jones 			if (sc->sc_capa_n_scan_channels > IWX_MAX_SCAN_CHANNELS) {
15542ad0f7e9STom Jones 				err = ERANGE;
15552ad0f7e9STom Jones 				goto parse_out;
15562ad0f7e9STom Jones 			}
15572ad0f7e9STom Jones 			break;
15582ad0f7e9STom Jones 
15592ad0f7e9STom Jones 		case IWX_UCODE_TLV_FW_VERSION:
15602ad0f7e9STom Jones 			if (tlv_len != sizeof(uint32_t) * 3) {
15612ad0f7e9STom Jones 				err = EINVAL;
15622ad0f7e9STom Jones 				goto parse_out;
15632ad0f7e9STom Jones 			}
15642ad0f7e9STom Jones 
15652ad0f7e9STom Jones 			iwx_fw_version_str(sc->sc_fwver, sizeof(sc->sc_fwver),
15662ad0f7e9STom Jones 			    le32toh(((const uint32_t *)tlv_data)[0]),
15672ad0f7e9STom Jones 			    le32toh(((const uint32_t *)tlv_data)[1]),
15682ad0f7e9STom Jones 			    le32toh(((const uint32_t *)tlv_data)[2]));
15692ad0f7e9STom Jones 			break;
15702ad0f7e9STom Jones 
15712ad0f7e9STom Jones 		case IWX_UCODE_TLV_FW_DBG_DEST: {
15722ad0f7e9STom Jones 			const struct iwx_fw_dbg_dest_tlv_v1 *dest_v1 = NULL;
15732ad0f7e9STom Jones 
15742ad0f7e9STom Jones 			fw->dbg_dest_ver = (const uint8_t *)tlv_data;
15752ad0f7e9STom Jones 			if (*fw->dbg_dest_ver != 0) {
15762ad0f7e9STom Jones 				err = EINVAL;
15772ad0f7e9STom Jones 				goto parse_out;
15782ad0f7e9STom Jones 			}
15792ad0f7e9STom Jones 
15802ad0f7e9STom Jones 			if (fw->dbg_dest_tlv_init)
15812ad0f7e9STom Jones 				break;
15822ad0f7e9STom Jones 			fw->dbg_dest_tlv_init = true;
15832ad0f7e9STom Jones 
15842ad0f7e9STom Jones 			dest_v1 = (const void *)tlv_data;
15852ad0f7e9STom Jones 			fw->dbg_dest_tlv_v1 = dest_v1;
15862ad0f7e9STom Jones 			fw->n_dest_reg = tlv_len -
15872ad0f7e9STom Jones 			    offsetof(struct iwx_fw_dbg_dest_tlv_v1, reg_ops);
15882ad0f7e9STom Jones 			fw->n_dest_reg /= sizeof(dest_v1->reg_ops[0]);
15892ad0f7e9STom Jones 			IWX_DPRINTF(sc, IWX_DEBUG_FIRMWARE_TLV,
15902ad0f7e9STom Jones 			    "%s: found debug dest; n_dest_reg=%d\n",
15912ad0f7e9STom Jones 			    __func__, fw->n_dest_reg);
15922ad0f7e9STom Jones 			break;
15932ad0f7e9STom Jones 		}
15942ad0f7e9STom Jones 
15952ad0f7e9STom Jones 		case IWX_UCODE_TLV_FW_DBG_CONF: {
15962ad0f7e9STom Jones 			const struct iwx_fw_dbg_conf_tlv *conf = (const void *)tlv_data;
15972ad0f7e9STom Jones 
15982ad0f7e9STom Jones 			if (!fw->dbg_dest_tlv_init ||
15992ad0f7e9STom Jones 			    conf->id >= nitems(fw->dbg_conf_tlv) ||
16002ad0f7e9STom Jones 			    fw->dbg_conf_tlv[conf->id] != NULL)
16012ad0f7e9STom Jones 				break;
16022ad0f7e9STom Jones 
16032ad0f7e9STom Jones 			IWX_DPRINTF(sc, IWX_DEBUG_FIRMWARE_TLV,
16042ad0f7e9STom Jones 			    "Found debug configuration: %d\n", conf->id);
16052ad0f7e9STom Jones 			fw->dbg_conf_tlv[conf->id] = conf;
16062ad0f7e9STom Jones 			fw->dbg_conf_tlv_len[conf->id] = tlv_len;
16072ad0f7e9STom Jones 			break;
16082ad0f7e9STom Jones 		}
16092ad0f7e9STom Jones 
16102ad0f7e9STom Jones 		case IWX_UCODE_TLV_UMAC_DEBUG_ADDRS: {
16112ad0f7e9STom Jones 			const struct iwx_umac_debug_addrs *dbg_ptrs =
16122ad0f7e9STom Jones 				(const void *)tlv_data;
16132ad0f7e9STom Jones 
16142ad0f7e9STom Jones 			if (tlv_len != sizeof(*dbg_ptrs)) {
16152ad0f7e9STom Jones 				err = EINVAL;
16162ad0f7e9STom Jones 				goto parse_out;
16172ad0f7e9STom Jones 			}
16182ad0f7e9STom Jones 			if (sc->sc_device_family < IWX_DEVICE_FAMILY_22000)
16192ad0f7e9STom Jones 				break;
16202ad0f7e9STom Jones 			sc->sc_uc.uc_umac_error_event_table =
16212ad0f7e9STom Jones 				le32toh(dbg_ptrs->error_info_addr) &
16222ad0f7e9STom Jones 				~IWX_FW_ADDR_CACHE_CONTROL;
16232ad0f7e9STom Jones 			sc->sc_uc.error_event_table_tlv_status |=
16242ad0f7e9STom Jones 				IWX_ERROR_EVENT_TABLE_UMAC;
16252ad0f7e9STom Jones 			break;
16262ad0f7e9STom Jones 		}
16272ad0f7e9STom Jones 
16282ad0f7e9STom Jones 		case IWX_UCODE_TLV_LMAC_DEBUG_ADDRS: {
16292ad0f7e9STom Jones 			const struct iwx_lmac_debug_addrs *dbg_ptrs =
16302ad0f7e9STom Jones 				(const void *)tlv_data;
16312ad0f7e9STom Jones 
16322ad0f7e9STom Jones 			if (tlv_len != sizeof(*dbg_ptrs)) {
16332ad0f7e9STom Jones 				err = EINVAL;
16342ad0f7e9STom Jones 				goto parse_out;
16352ad0f7e9STom Jones 			}
16362ad0f7e9STom Jones 			if (sc->sc_device_family < IWX_DEVICE_FAMILY_22000)
16372ad0f7e9STom Jones 				break;
16382ad0f7e9STom Jones 			sc->sc_uc.uc_lmac_error_event_table[0] =
16392ad0f7e9STom Jones 				le32toh(dbg_ptrs->error_event_table_ptr) &
16402ad0f7e9STom Jones 				~IWX_FW_ADDR_CACHE_CONTROL;
16412ad0f7e9STom Jones 			sc->sc_uc.error_event_table_tlv_status |=
16422ad0f7e9STom Jones 				IWX_ERROR_EVENT_TABLE_LMAC1;
16432ad0f7e9STom Jones 			break;
16442ad0f7e9STom Jones 		}
16452ad0f7e9STom Jones 
16462ad0f7e9STom Jones 		case IWX_UCODE_TLV_FW_MEM_SEG:
16472ad0f7e9STom Jones 			break;
16482ad0f7e9STom Jones 
16492ad0f7e9STom Jones 		case IWX_UCODE_TLV_IML:
16502ad0f7e9STom Jones 			if (sc->sc_fw.iml != NULL) {
16512ad0f7e9STom Jones 				free(fw->iml, M_DEVBUF);
16522ad0f7e9STom Jones 				fw->iml_len = 0;
16532ad0f7e9STom Jones 			}
16542ad0f7e9STom Jones 			sc->sc_fw.iml = malloc(tlv_len, M_DEVBUF,
16552ad0f7e9STom Jones 			    M_WAITOK | M_ZERO);
16562ad0f7e9STom Jones 			if (sc->sc_fw.iml == NULL) {
16572ad0f7e9STom Jones 				err = ENOMEM;
16582ad0f7e9STom Jones 				goto parse_out;
16592ad0f7e9STom Jones 			}
16602ad0f7e9STom Jones 			memcpy(sc->sc_fw.iml, tlv_data, tlv_len);
16612ad0f7e9STom Jones 			sc->sc_fw.iml_len = tlv_len;
16622ad0f7e9STom Jones 			break;
16632ad0f7e9STom Jones 
16642ad0f7e9STom Jones 		case IWX_UCODE_TLV_CMD_VERSIONS:
16652ad0f7e9STom Jones 			if (tlv_len % sizeof(struct iwx_fw_cmd_version)) {
16662ad0f7e9STom Jones 				tlv_len /= sizeof(struct iwx_fw_cmd_version);
16672ad0f7e9STom Jones 				tlv_len *= sizeof(struct iwx_fw_cmd_version);
16682ad0f7e9STom Jones 			}
16692ad0f7e9STom Jones 			if (sc->n_cmd_versions != 0) {
16702ad0f7e9STom Jones 				err = EINVAL;
16712ad0f7e9STom Jones 				goto parse_out;
16722ad0f7e9STom Jones 			}
16732ad0f7e9STom Jones 			if (tlv_len > sizeof(sc->cmd_versions)) {
16742ad0f7e9STom Jones 				err = EINVAL;
16752ad0f7e9STom Jones 				goto parse_out;
16762ad0f7e9STom Jones 			}
16772ad0f7e9STom Jones 			memcpy(&sc->cmd_versions[0], tlv_data, tlv_len);
16782ad0f7e9STom Jones 			sc->n_cmd_versions = tlv_len / sizeof(struct iwx_fw_cmd_version);
16792ad0f7e9STom Jones 			break;
16802ad0f7e9STom Jones 
16812ad0f7e9STom Jones 		case IWX_UCODE_TLV_FW_RECOVERY_INFO:
16822ad0f7e9STom Jones 			break;
16832ad0f7e9STom Jones 
16842ad0f7e9STom Jones 		case IWX_UCODE_TLV_FW_FSEQ_VERSION:
16852ad0f7e9STom Jones 		case IWX_UCODE_TLV_PHY_INTEGRATION_VERSION:
16862ad0f7e9STom Jones 		case IWX_UCODE_TLV_FW_NUM_STATIONS:
16872ad0f7e9STom Jones 		case IWX_UCODE_TLV_FW_NUM_BEACONS:
16882ad0f7e9STom Jones 			break;
16892ad0f7e9STom Jones 
16902ad0f7e9STom Jones 		/* undocumented TLVs found in iwx-cc-a0-46 image */
16912ad0f7e9STom Jones 		case 58:
16922ad0f7e9STom Jones 		case 0x1000003:
16932ad0f7e9STom Jones 		case 0x1000004:
16942ad0f7e9STom Jones 			break;
16952ad0f7e9STom Jones 
16962ad0f7e9STom Jones 		/* undocumented TLVs found in iwx-cc-a0-48 image */
16972ad0f7e9STom Jones 		case 0x1000000:
16982ad0f7e9STom Jones 		case 0x1000002:
16992ad0f7e9STom Jones 			break;
17002ad0f7e9STom Jones 
17012ad0f7e9STom Jones 		case IWX_UCODE_TLV_TYPE_DEBUG_INFO:
17022ad0f7e9STom Jones 		case IWX_UCODE_TLV_TYPE_BUFFER_ALLOCATION:
17032ad0f7e9STom Jones 		case IWX_UCODE_TLV_TYPE_HCMD:
17042ad0f7e9STom Jones 		case IWX_UCODE_TLV_TYPE_REGIONS:
17052ad0f7e9STom Jones 		case IWX_UCODE_TLV_TYPE_TRIGGERS:
17062ad0f7e9STom Jones 		case IWX_UCODE_TLV_TYPE_CONF_SET:
17072ad0f7e9STom Jones 		case IWX_UCODE_TLV_SEC_TABLE_ADDR:
17082ad0f7e9STom Jones 		case IWX_UCODE_TLV_D3_KEK_KCK_ADDR:
17092ad0f7e9STom Jones 		case IWX_UCODE_TLV_CURRENT_PC:
17102ad0f7e9STom Jones 			break;
17112ad0f7e9STom Jones 
17122ad0f7e9STom Jones 		/* undocumented TLV found in iwx-cc-a0-67 image */
17132ad0f7e9STom Jones 		case 0x100000b:
17142ad0f7e9STom Jones 			break;
17152ad0f7e9STom Jones 
17162ad0f7e9STom Jones 		/* undocumented TLV found in iwx-ty-a0-gf-a0-73 image */
17172ad0f7e9STom Jones 		case 0x101:
17182ad0f7e9STom Jones 			break;
17192ad0f7e9STom Jones 
17202ad0f7e9STom Jones 		/* undocumented TLV found in iwx-ty-a0-gf-a0-77 image */
17212ad0f7e9STom Jones 		case 0x100000c:
17222ad0f7e9STom Jones 			break;
17232ad0f7e9STom Jones 
17242ad0f7e9STom Jones 		/* undocumented TLV found in iwx-ty-a0-gf-a0-89 image */
17252ad0f7e9STom Jones 		case 69:
17262ad0f7e9STom Jones 			break;
17272ad0f7e9STom Jones 
17282ad0f7e9STom Jones 		default:
17292ad0f7e9STom Jones 			err = EINVAL;
17302ad0f7e9STom Jones 			goto parse_out;
17312ad0f7e9STom Jones 		}
17322ad0f7e9STom Jones 
17332ad0f7e9STom Jones 		/*
17342ad0f7e9STom Jones 		 * Check for size_t overflow and ignore missing padding at
17352ad0f7e9STom Jones 		 * end of firmware file.
17362ad0f7e9STom Jones 		 */
17372ad0f7e9STom Jones 		if (roundup(tlv_len, 4) > len)
17382ad0f7e9STom Jones 			break;
17392ad0f7e9STom Jones 
17402ad0f7e9STom Jones 		len -= roundup(tlv_len, 4);
17412ad0f7e9STom Jones 		data += roundup(tlv_len, 4);
17422ad0f7e9STom Jones 	}
17432ad0f7e9STom Jones 
17442ad0f7e9STom Jones 	KASSERT(err == 0, ("unhandled fw parse error"));
17452ad0f7e9STom Jones 
17462ad0f7e9STom Jones parse_out:
17472ad0f7e9STom Jones 	if (err) {
17482ad0f7e9STom Jones 		printf("%s: firmware parse error %d, "
17492ad0f7e9STom Jones 		    "section type %d\n", DEVNAME(sc), err, tlv_type);
17502ad0f7e9STom Jones 	}
17512ad0f7e9STom Jones 
17522ad0f7e9STom Jones out:
17532ad0f7e9STom Jones 	if (err) {
17542ad0f7e9STom Jones 		fw->fw_status = IWX_FW_STATUS_NONE;
17552ad0f7e9STom Jones 		if (fw->fw_rawdata != NULL)
17562ad0f7e9STom Jones 			iwx_fw_info_free(fw);
17572ad0f7e9STom Jones 	} else
17582ad0f7e9STom Jones 		fw->fw_status = IWX_FW_STATUS_DONE;
17592ad0f7e9STom Jones 	return err;
17602ad0f7e9STom Jones }
17612ad0f7e9STom Jones 
17622ad0f7e9STom Jones static uint32_t
iwx_prph_addr_mask(struct iwx_softc * sc)17632ad0f7e9STom Jones iwx_prph_addr_mask(struct iwx_softc *sc)
17642ad0f7e9STom Jones {
17652ad0f7e9STom Jones 	if (sc->sc_device_family >= IWX_DEVICE_FAMILY_AX210)
17662ad0f7e9STom Jones 		return 0x00ffffff;
17672ad0f7e9STom Jones 	else
17682ad0f7e9STom Jones 		return 0x000fffff;
17692ad0f7e9STom Jones }
17702ad0f7e9STom Jones 
17712ad0f7e9STom Jones static uint32_t
iwx_read_prph_unlocked(struct iwx_softc * sc,uint32_t addr)17722ad0f7e9STom Jones iwx_read_prph_unlocked(struct iwx_softc *sc, uint32_t addr)
17732ad0f7e9STom Jones {
17742ad0f7e9STom Jones 	uint32_t mask = iwx_prph_addr_mask(sc);
17752ad0f7e9STom Jones 	IWX_WRITE(sc, IWX_HBUS_TARG_PRPH_RADDR, ((addr & mask) | (3 << 24)));
17762ad0f7e9STom Jones 	IWX_BARRIER_READ_WRITE(sc);
17772ad0f7e9STom Jones 	return IWX_READ(sc, IWX_HBUS_TARG_PRPH_RDAT);
17782ad0f7e9STom Jones }
17792ad0f7e9STom Jones 
17802ad0f7e9STom Jones uint32_t
iwx_read_prph(struct iwx_softc * sc,uint32_t addr)17812ad0f7e9STom Jones iwx_read_prph(struct iwx_softc *sc, uint32_t addr)
17822ad0f7e9STom Jones {
17832ad0f7e9STom Jones 	iwx_nic_assert_locked(sc);
17842ad0f7e9STom Jones 	return iwx_read_prph_unlocked(sc, addr);
17852ad0f7e9STom Jones }
17862ad0f7e9STom Jones 
17872ad0f7e9STom Jones static void
iwx_write_prph_unlocked(struct iwx_softc * sc,uint32_t addr,uint32_t val)17882ad0f7e9STom Jones iwx_write_prph_unlocked(struct iwx_softc *sc, uint32_t addr, uint32_t val)
17892ad0f7e9STom Jones {
17902ad0f7e9STom Jones 	uint32_t mask = iwx_prph_addr_mask(sc);
17912ad0f7e9STom Jones 	IWX_WRITE(sc, IWX_HBUS_TARG_PRPH_WADDR, ((addr & mask) | (3 << 24)));
17922ad0f7e9STom Jones 	IWX_BARRIER_WRITE(sc);
17932ad0f7e9STom Jones 	IWX_WRITE(sc, IWX_HBUS_TARG_PRPH_WDAT, val);
17942ad0f7e9STom Jones }
17952ad0f7e9STom Jones 
17962ad0f7e9STom Jones static void
iwx_write_prph(struct iwx_softc * sc,uint32_t addr,uint32_t val)17972ad0f7e9STom Jones iwx_write_prph(struct iwx_softc *sc, uint32_t addr, uint32_t val)
17982ad0f7e9STom Jones {
17992ad0f7e9STom Jones 	iwx_nic_assert_locked(sc);
18002ad0f7e9STom Jones 	iwx_write_prph_unlocked(sc, addr, val);
18012ad0f7e9STom Jones }
18022ad0f7e9STom Jones 
18032ad0f7e9STom Jones static uint32_t
iwx_read_umac_prph(struct iwx_softc * sc,uint32_t addr)18042ad0f7e9STom Jones iwx_read_umac_prph(struct iwx_softc *sc, uint32_t addr)
18052ad0f7e9STom Jones {
18062ad0f7e9STom Jones 	return iwx_read_prph(sc, addr + sc->sc_umac_prph_offset);
18072ad0f7e9STom Jones }
18082ad0f7e9STom Jones 
18092ad0f7e9STom Jones static void
iwx_write_umac_prph(struct iwx_softc * sc,uint32_t addr,uint32_t val)18102ad0f7e9STom Jones iwx_write_umac_prph(struct iwx_softc *sc, uint32_t addr, uint32_t val)
18112ad0f7e9STom Jones {
18122ad0f7e9STom Jones 	iwx_write_prph(sc, addr + sc->sc_umac_prph_offset, val);
18132ad0f7e9STom Jones }
18142ad0f7e9STom Jones 
18152ad0f7e9STom Jones static int
iwx_read_mem(struct iwx_softc * sc,uint32_t addr,void * buf,int dwords)18162ad0f7e9STom Jones iwx_read_mem(struct iwx_softc *sc, uint32_t addr, void *buf, int dwords)
18172ad0f7e9STom Jones {
18182ad0f7e9STom Jones 	int offs, err = 0;
18192ad0f7e9STom Jones 	uint32_t *vals = buf;
18202ad0f7e9STom Jones 
18212ad0f7e9STom Jones 	if (iwx_nic_lock(sc)) {
18222ad0f7e9STom Jones 		IWX_WRITE(sc, IWX_HBUS_TARG_MEM_RADDR, addr);
18232ad0f7e9STom Jones 		for (offs = 0; offs < dwords; offs++)
18242ad0f7e9STom Jones 			vals[offs] = le32toh(IWX_READ(sc, IWX_HBUS_TARG_MEM_RDAT));
18252ad0f7e9STom Jones 		iwx_nic_unlock(sc);
18262ad0f7e9STom Jones 	} else {
18272ad0f7e9STom Jones 		err = EBUSY;
18282ad0f7e9STom Jones 	}
18292ad0f7e9STom Jones 	return err;
18302ad0f7e9STom Jones }
18312ad0f7e9STom Jones 
18322ad0f7e9STom Jones static int
iwx_poll_bit(struct iwx_softc * sc,int reg,uint32_t bits,uint32_t mask,int timo)18332ad0f7e9STom Jones iwx_poll_bit(struct iwx_softc *sc, int reg, uint32_t bits, uint32_t mask,
18342ad0f7e9STom Jones     int timo)
18352ad0f7e9STom Jones {
18362ad0f7e9STom Jones 	for (;;) {
18372ad0f7e9STom Jones 		if ((IWX_READ(sc, reg) & mask) == (bits & mask)) {
18382ad0f7e9STom Jones 			return 1;
18392ad0f7e9STom Jones 		}
18402ad0f7e9STom Jones 		if (timo < 10) {
18412ad0f7e9STom Jones 			return 0;
18422ad0f7e9STom Jones 		}
18432ad0f7e9STom Jones 		timo -= 10;
18442ad0f7e9STom Jones 		DELAY(10);
18452ad0f7e9STom Jones 	}
18462ad0f7e9STom Jones }
18472ad0f7e9STom Jones 
18482ad0f7e9STom Jones static int
iwx_nic_lock(struct iwx_softc * sc)18492ad0f7e9STom Jones iwx_nic_lock(struct iwx_softc *sc)
18502ad0f7e9STom Jones {
18512ad0f7e9STom Jones 	if (sc->sc_nic_locks > 0) {
18522ad0f7e9STom Jones 		iwx_nic_assert_locked(sc);
18532ad0f7e9STom Jones 		sc->sc_nic_locks++;
18542ad0f7e9STom Jones 		return 1; /* already locked */
18552ad0f7e9STom Jones 	}
18562ad0f7e9STom Jones 
18572ad0f7e9STom Jones 	IWX_SETBITS(sc, IWX_CSR_GP_CNTRL,
18582ad0f7e9STom Jones 	    IWX_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
18592ad0f7e9STom Jones 
18602ad0f7e9STom Jones 	DELAY(2);
18612ad0f7e9STom Jones 
18622ad0f7e9STom Jones 	if (iwx_poll_bit(sc, IWX_CSR_GP_CNTRL,
18632ad0f7e9STom Jones 	    IWX_CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN,
18642ad0f7e9STom Jones 	    IWX_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY
18652ad0f7e9STom Jones 	     | IWX_CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP, 150000)) {
18662ad0f7e9STom Jones 		sc->sc_nic_locks++;
18672ad0f7e9STom Jones 		return 1;
18682ad0f7e9STom Jones 	}
18692ad0f7e9STom Jones 
18702ad0f7e9STom Jones 	printf("%s: acquiring device failed\n", DEVNAME(sc));
18712ad0f7e9STom Jones 	return 0;
18722ad0f7e9STom Jones }
18732ad0f7e9STom Jones 
18742ad0f7e9STom Jones static void
iwx_nic_assert_locked(struct iwx_softc * sc)18752ad0f7e9STom Jones iwx_nic_assert_locked(struct iwx_softc *sc)
18762ad0f7e9STom Jones {
18772ad0f7e9STom Jones 	if (sc->sc_nic_locks <= 0)
18782ad0f7e9STom Jones 		panic("%s: nic locks counter %d", DEVNAME(sc), sc->sc_nic_locks);
18792ad0f7e9STom Jones }
18802ad0f7e9STom Jones 
18812ad0f7e9STom Jones static void
iwx_nic_unlock(struct iwx_softc * sc)18822ad0f7e9STom Jones iwx_nic_unlock(struct iwx_softc *sc)
18832ad0f7e9STom Jones {
18842ad0f7e9STom Jones 	if (sc->sc_nic_locks > 0) {
18852ad0f7e9STom Jones 		if (--sc->sc_nic_locks == 0)
18862ad0f7e9STom Jones 			IWX_CLRBITS(sc, IWX_CSR_GP_CNTRL,
18872ad0f7e9STom Jones 			    IWX_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
18882ad0f7e9STom Jones 	} else
18892ad0f7e9STom Jones 		printf("%s: NIC already unlocked\n", DEVNAME(sc));
18902ad0f7e9STom Jones }
18912ad0f7e9STom Jones 
18922ad0f7e9STom Jones static int
iwx_set_bits_mask_prph(struct iwx_softc * sc,uint32_t reg,uint32_t bits,uint32_t mask)18932ad0f7e9STom Jones iwx_set_bits_mask_prph(struct iwx_softc *sc, uint32_t reg, uint32_t bits,
18942ad0f7e9STom Jones     uint32_t mask)
18952ad0f7e9STom Jones {
18962ad0f7e9STom Jones 	uint32_t val;
18972ad0f7e9STom Jones 
18982ad0f7e9STom Jones 	if (iwx_nic_lock(sc)) {
18992ad0f7e9STom Jones 		val = iwx_read_prph(sc, reg) & mask;
19002ad0f7e9STom Jones 		val |= bits;
19012ad0f7e9STom Jones 		iwx_write_prph(sc, reg, val);
19022ad0f7e9STom Jones 		iwx_nic_unlock(sc);
19032ad0f7e9STom Jones 		return 0;
19042ad0f7e9STom Jones 	}
19052ad0f7e9STom Jones 	return EBUSY;
19062ad0f7e9STom Jones }
19072ad0f7e9STom Jones 
19082ad0f7e9STom Jones static int
iwx_set_bits_prph(struct iwx_softc * sc,uint32_t reg,uint32_t bits)19092ad0f7e9STom Jones iwx_set_bits_prph(struct iwx_softc *sc, uint32_t reg, uint32_t bits)
19102ad0f7e9STom Jones {
19112ad0f7e9STom Jones 	return iwx_set_bits_mask_prph(sc, reg, bits, ~0);
19122ad0f7e9STom Jones }
19132ad0f7e9STom Jones 
19142ad0f7e9STom Jones static int
iwx_clear_bits_prph(struct iwx_softc * sc,uint32_t reg,uint32_t bits)19152ad0f7e9STom Jones iwx_clear_bits_prph(struct iwx_softc *sc, uint32_t reg, uint32_t bits)
19162ad0f7e9STom Jones {
19172ad0f7e9STom Jones 	return iwx_set_bits_mask_prph(sc, reg, 0, ~bits);
19182ad0f7e9STom Jones }
19192ad0f7e9STom Jones 
19202ad0f7e9STom Jones static void
iwx_dma_map_addr(void * arg,bus_dma_segment_t * segs,int nsegs,int error)19212ad0f7e9STom Jones iwx_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
19222ad0f7e9STom Jones {
19232ad0f7e9STom Jones         if (error != 0)
19242ad0f7e9STom Jones                 return;
19252ad0f7e9STom Jones 	KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs));
19262ad0f7e9STom Jones 	*(bus_addr_t *)arg = segs[0].ds_addr;
19272ad0f7e9STom Jones }
19282ad0f7e9STom Jones 
19292ad0f7e9STom Jones static int
iwx_dma_contig_alloc(bus_dma_tag_t tag,struct iwx_dma_info * dma,bus_size_t size,bus_size_t alignment)19302ad0f7e9STom Jones iwx_dma_contig_alloc(bus_dma_tag_t tag, struct iwx_dma_info *dma,
19312ad0f7e9STom Jones     bus_size_t size, bus_size_t alignment)
19322ad0f7e9STom Jones {
19332ad0f7e9STom Jones 	int error;
19342ad0f7e9STom Jones 
19352ad0f7e9STom Jones 	dma->tag = NULL;
19362ad0f7e9STom Jones 	dma->map = NULL;
19372ad0f7e9STom Jones 	dma->size = size;
19382ad0f7e9STom Jones 	dma->vaddr = NULL;
19392ad0f7e9STom Jones 
19402ad0f7e9STom Jones 	error = bus_dma_tag_create(tag, alignment,
19412ad0f7e9STom Jones             0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size,
19422ad0f7e9STom Jones             1, size, 0, NULL, NULL, &dma->tag);
19432ad0f7e9STom Jones         if (error != 0)
19442ad0f7e9STom Jones                 goto fail;
19452ad0f7e9STom Jones 
19462ad0f7e9STom Jones         error = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr,
19472ad0f7e9STom Jones             BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, &dma->map);
19482ad0f7e9STom Jones         if (error != 0)
19492ad0f7e9STom Jones                 goto fail;
19502ad0f7e9STom Jones 
19512ad0f7e9STom Jones         error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr, size,
19522ad0f7e9STom Jones             iwx_dma_map_addr, &dma->paddr, BUS_DMA_NOWAIT);
19532ad0f7e9STom Jones         if (error != 0) {
19542ad0f7e9STom Jones 		bus_dmamem_free(dma->tag, dma->vaddr, dma->map);
19552ad0f7e9STom Jones 		dma->vaddr = NULL;
19562ad0f7e9STom Jones 		goto fail;
19572ad0f7e9STom Jones 	}
19582ad0f7e9STom Jones 
19592ad0f7e9STom Jones 	bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE);
19602ad0f7e9STom Jones 
19612ad0f7e9STom Jones 	return 0;
19622ad0f7e9STom Jones 
19632ad0f7e9STom Jones fail:
19642ad0f7e9STom Jones 	iwx_dma_contig_free(dma);
19652ad0f7e9STom Jones 	return error;
19662ad0f7e9STom Jones }
19672ad0f7e9STom Jones 
19682ad0f7e9STom Jones static void
iwx_dma_contig_free(struct iwx_dma_info * dma)19692ad0f7e9STom Jones iwx_dma_contig_free(struct iwx_dma_info *dma)
19702ad0f7e9STom Jones {
19712ad0f7e9STom Jones 	if (dma->vaddr != NULL) {
19722ad0f7e9STom Jones 		bus_dmamap_sync(dma->tag, dma->map,
19732ad0f7e9STom Jones 		    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
19742ad0f7e9STom Jones 		bus_dmamap_unload(dma->tag, dma->map);
19752ad0f7e9STom Jones 		bus_dmamem_free(dma->tag, dma->vaddr, dma->map);
19762ad0f7e9STom Jones 		dma->vaddr = NULL;
19772ad0f7e9STom Jones 	}
19782ad0f7e9STom Jones 	if (dma->tag != NULL) {
19792ad0f7e9STom Jones 		bus_dma_tag_destroy(dma->tag);
19802ad0f7e9STom Jones 		dma->tag = NULL;
19812ad0f7e9STom Jones 	}
19822ad0f7e9STom Jones }
19832ad0f7e9STom Jones 
19842ad0f7e9STom Jones static int
iwx_alloc_rx_ring(struct iwx_softc * sc,struct iwx_rx_ring * ring)19852ad0f7e9STom Jones iwx_alloc_rx_ring(struct iwx_softc *sc, struct iwx_rx_ring *ring)
19862ad0f7e9STom Jones {
19872ad0f7e9STom Jones 	bus_size_t size;
19882ad0f7e9STom Jones 	int i, err;
19892ad0f7e9STom Jones 
19902ad0f7e9STom Jones 	ring->cur = 0;
19912ad0f7e9STom Jones 
19922ad0f7e9STom Jones 	/* Allocate RX descriptors (256-byte aligned). */
19932ad0f7e9STom Jones 	if (sc->sc_device_family >= IWX_DEVICE_FAMILY_AX210)
19942ad0f7e9STom Jones 		size = sizeof(struct iwx_rx_transfer_desc);
19952ad0f7e9STom Jones 	else
19962ad0f7e9STom Jones 		size = sizeof(uint64_t);
19972ad0f7e9STom Jones 	err = iwx_dma_contig_alloc(sc->sc_dmat, &ring->free_desc_dma,
19982ad0f7e9STom Jones 	    size * IWX_RX_MQ_RING_COUNT, 256);
19992ad0f7e9STom Jones 	if (err) {
20002ad0f7e9STom Jones 		device_printf(sc->sc_dev,
20012ad0f7e9STom Jones 		    "could not allocate RX ring DMA memory\n");
20022ad0f7e9STom Jones 		goto fail;
20032ad0f7e9STom Jones 	}
20042ad0f7e9STom Jones 	ring->desc = ring->free_desc_dma.vaddr;
20052ad0f7e9STom Jones 
20062ad0f7e9STom Jones 	/* Allocate RX status area (16-byte aligned). */
20072ad0f7e9STom Jones 	if (sc->sc_device_family >= IWX_DEVICE_FAMILY_AX210)
20082ad0f7e9STom Jones 		size = sizeof(uint16_t);
20092ad0f7e9STom Jones 	else
20102ad0f7e9STom Jones 		size = sizeof(*ring->stat);
20112ad0f7e9STom Jones 	err = iwx_dma_contig_alloc(sc->sc_dmat, &ring->stat_dma, size, 16);
20122ad0f7e9STom Jones 	if (err) {
20132ad0f7e9STom Jones 		device_printf(sc->sc_dev,
20142ad0f7e9STom Jones 		    "could not allocate RX status DMA memory\n");
20152ad0f7e9STom Jones 		goto fail;
20162ad0f7e9STom Jones 	}
20172ad0f7e9STom Jones 	ring->stat = ring->stat_dma.vaddr;
20182ad0f7e9STom Jones 
20192ad0f7e9STom Jones 	if (sc->sc_device_family >= IWX_DEVICE_FAMILY_AX210)
20202ad0f7e9STom Jones 		size = sizeof(struct iwx_rx_completion_desc);
20212ad0f7e9STom Jones 	else
20222ad0f7e9STom Jones 		size = sizeof(uint32_t);
20232ad0f7e9STom Jones 	err = iwx_dma_contig_alloc(sc->sc_dmat, &ring->used_desc_dma,
20242ad0f7e9STom Jones 	    size * IWX_RX_MQ_RING_COUNT, 256);
20252ad0f7e9STom Jones 	if (err) {
20262ad0f7e9STom Jones 		device_printf(sc->sc_dev,
20272ad0f7e9STom Jones 		    "could not allocate RX ring DMA memory\n");
20282ad0f7e9STom Jones 		goto fail;
20292ad0f7e9STom Jones 	}
20302ad0f7e9STom Jones 
20312ad0f7e9STom Jones 	err = bus_dma_tag_create(sc->sc_dmat, 1, 0, BUS_SPACE_MAXADDR_32BIT,
20322ad0f7e9STom Jones 	    BUS_SPACE_MAXADDR, NULL, NULL, IWX_RBUF_SIZE, 1, IWX_RBUF_SIZE,
20332ad0f7e9STom Jones 	    0, NULL, NULL, &ring->data_dmat);
20342ad0f7e9STom Jones 
20352ad0f7e9STom Jones 	for (i = 0; i < IWX_RX_MQ_RING_COUNT; i++) {
20362ad0f7e9STom Jones 		struct iwx_rx_data *data = &ring->data[i];
20372ad0f7e9STom Jones 
20382ad0f7e9STom Jones 		memset(data, 0, sizeof(*data));
20392ad0f7e9STom Jones 		err = bus_dmamap_create(ring->data_dmat, 0, &data->map);
20402ad0f7e9STom Jones 		if (err) {
20412ad0f7e9STom Jones 			device_printf(sc->sc_dev,
20422ad0f7e9STom Jones 			    "could not create RX buf DMA map\n");
20432ad0f7e9STom Jones 			goto fail;
20442ad0f7e9STom Jones 		}
20452ad0f7e9STom Jones 
20462ad0f7e9STom Jones 		err = iwx_rx_addbuf(sc, IWX_RBUF_SIZE, i);
20472ad0f7e9STom Jones 		if (err)
20482ad0f7e9STom Jones 			goto fail;
20492ad0f7e9STom Jones 	}
20502ad0f7e9STom Jones 	return 0;
20512ad0f7e9STom Jones 
20522ad0f7e9STom Jones fail:	iwx_free_rx_ring(sc, ring);
20532ad0f7e9STom Jones 	return err;
20542ad0f7e9STom Jones }
20552ad0f7e9STom Jones 
20562ad0f7e9STom Jones static void
iwx_disable_rx_dma(struct iwx_softc * sc)20572ad0f7e9STom Jones iwx_disable_rx_dma(struct iwx_softc *sc)
20582ad0f7e9STom Jones {
20592ad0f7e9STom Jones 	int ntries;
20602ad0f7e9STom Jones 
20612ad0f7e9STom Jones 	if (iwx_nic_lock(sc)) {
20622ad0f7e9STom Jones 		if (sc->sc_device_family >= IWX_DEVICE_FAMILY_AX210)
20632ad0f7e9STom Jones 			iwx_write_umac_prph(sc, IWX_RFH_RXF_DMA_CFG_GEN3, 0);
20642ad0f7e9STom Jones 		else
20652ad0f7e9STom Jones 			iwx_write_prph(sc, IWX_RFH_RXF_DMA_CFG, 0);
20662ad0f7e9STom Jones 		for (ntries = 0; ntries < 1000; ntries++) {
20672ad0f7e9STom Jones 			if (sc->sc_device_family >= IWX_DEVICE_FAMILY_AX210) {
20682ad0f7e9STom Jones 				if (iwx_read_umac_prph(sc,
20692ad0f7e9STom Jones 				    IWX_RFH_GEN_STATUS_GEN3) & IWX_RXF_DMA_IDLE)
20702ad0f7e9STom Jones 					break;
20712ad0f7e9STom Jones 			} else {
20722ad0f7e9STom Jones 				if (iwx_read_prph(sc, IWX_RFH_GEN_STATUS) &
20732ad0f7e9STom Jones 				    IWX_RXF_DMA_IDLE)
20742ad0f7e9STom Jones 					break;
20752ad0f7e9STom Jones 			}
20762ad0f7e9STom Jones 			DELAY(10);
20772ad0f7e9STom Jones 		}
20782ad0f7e9STom Jones 		iwx_nic_unlock(sc);
20792ad0f7e9STom Jones 	}
20802ad0f7e9STom Jones }
20812ad0f7e9STom Jones 
20822ad0f7e9STom Jones static void
iwx_reset_rx_ring(struct iwx_softc * sc,struct iwx_rx_ring * ring)20832ad0f7e9STom Jones iwx_reset_rx_ring(struct iwx_softc *sc, struct iwx_rx_ring *ring)
20842ad0f7e9STom Jones {
20852ad0f7e9STom Jones 	ring->cur = 0;
20862ad0f7e9STom Jones 	bus_dmamap_sync(sc->sc_dmat, ring->stat_dma.map,
20872ad0f7e9STom Jones 	    BUS_DMASYNC_PREWRITE);
20882ad0f7e9STom Jones 	if (sc->sc_device_family >= IWX_DEVICE_FAMILY_AX210) {
20892ad0f7e9STom Jones 		uint16_t *status = sc->rxq.stat_dma.vaddr;
20902ad0f7e9STom Jones 		*status = 0;
20912ad0f7e9STom Jones 	} else
20922ad0f7e9STom Jones 		memset(ring->stat, 0, sizeof(*ring->stat));
20932ad0f7e9STom Jones 	bus_dmamap_sync(sc->sc_dmat, ring->stat_dma.map,
20942ad0f7e9STom Jones 	    BUS_DMASYNC_POSTWRITE);
20952ad0f7e9STom Jones 
20962ad0f7e9STom Jones }
20972ad0f7e9STom Jones 
20982ad0f7e9STom Jones static void
iwx_free_rx_ring(struct iwx_softc * sc,struct iwx_rx_ring * ring)20992ad0f7e9STom Jones iwx_free_rx_ring(struct iwx_softc *sc, struct iwx_rx_ring *ring)
21002ad0f7e9STom Jones {
21012ad0f7e9STom Jones 	int i;
21022ad0f7e9STom Jones 
21032ad0f7e9STom Jones 	iwx_dma_contig_free(&ring->free_desc_dma);
21042ad0f7e9STom Jones 	iwx_dma_contig_free(&ring->stat_dma);
21052ad0f7e9STom Jones 	iwx_dma_contig_free(&ring->used_desc_dma);
21062ad0f7e9STom Jones 
21072ad0f7e9STom Jones 	for (i = 0; i < IWX_RX_MQ_RING_COUNT; i++) {
21082ad0f7e9STom Jones 		struct iwx_rx_data *data = &ring->data[i];
21092ad0f7e9STom Jones 		if (data->m != NULL) {
21102ad0f7e9STom Jones 			bus_dmamap_sync(ring->data_dmat, data->map,
21112ad0f7e9STom Jones 			    BUS_DMASYNC_POSTREAD);
21122ad0f7e9STom Jones 			bus_dmamap_unload(ring->data_dmat, data->map);
21132ad0f7e9STom Jones 			m_freem(data->m);
21142ad0f7e9STom Jones 			data->m = NULL;
21152ad0f7e9STom Jones 		}
21162ad0f7e9STom Jones 		if (data->map != NULL) {
21172ad0f7e9STom Jones 			bus_dmamap_destroy(ring->data_dmat, data->map);
21182ad0f7e9STom Jones 			data->map = NULL;
21192ad0f7e9STom Jones 		}
21202ad0f7e9STom Jones 	}
21212ad0f7e9STom Jones 	if (ring->data_dmat != NULL) {
21222ad0f7e9STom Jones 		bus_dma_tag_destroy(ring->data_dmat);
21232ad0f7e9STom Jones 		ring->data_dmat = NULL;
21242ad0f7e9STom Jones 	}
21252ad0f7e9STom Jones }
21262ad0f7e9STom Jones 
21272ad0f7e9STom Jones static int
iwx_alloc_tx_ring(struct iwx_softc * sc,struct iwx_tx_ring * ring,int qid)21282ad0f7e9STom Jones iwx_alloc_tx_ring(struct iwx_softc *sc, struct iwx_tx_ring *ring, int qid)
21292ad0f7e9STom Jones {
21302ad0f7e9STom Jones 	bus_addr_t paddr;
21312ad0f7e9STom Jones 	bus_size_t size;
21322ad0f7e9STom Jones 	int i, err;
21332ad0f7e9STom Jones 	size_t bc_tbl_size;
21342ad0f7e9STom Jones 	bus_size_t bc_align;
21352ad0f7e9STom Jones 	size_t mapsize;
21362ad0f7e9STom Jones 
21372ad0f7e9STom Jones 	ring->qid = qid;
21382ad0f7e9STom Jones 	ring->queued = 0;
21392ad0f7e9STom Jones 	ring->cur = 0;
21402ad0f7e9STom Jones 	ring->cur_hw = 0;
21412ad0f7e9STom Jones 	ring->tail = 0;
21422ad0f7e9STom Jones 	ring->tail_hw = 0;
21432ad0f7e9STom Jones 
21442ad0f7e9STom Jones 	/* Allocate TX descriptors (256-byte aligned). */
21452ad0f7e9STom Jones 	size = IWX_TX_RING_COUNT * sizeof(struct iwx_tfh_tfd);
21462ad0f7e9STom Jones 	err = iwx_dma_contig_alloc(sc->sc_dmat, &ring->desc_dma, size, 256);
21472ad0f7e9STom Jones 	if (err) {
21482ad0f7e9STom Jones 		device_printf(sc->sc_dev,
21492ad0f7e9STom Jones 		    "could not allocate TX ring DMA memory\n");
21502ad0f7e9STom Jones 		goto fail;
21512ad0f7e9STom Jones 	}
21522ad0f7e9STom Jones 	ring->desc = ring->desc_dma.vaddr;
21532ad0f7e9STom Jones 
21542ad0f7e9STom Jones 	/*
21552ad0f7e9STom Jones 	 * The hardware supports up to 512 Tx rings which is more
21562ad0f7e9STom Jones 	 * than we currently need.
21572ad0f7e9STom Jones 	 *
21582ad0f7e9STom Jones 	 * In DQA mode we use 1 command queue + 1 default queue for
21592ad0f7e9STom Jones 	 * management, control, and non-QoS data frames.
21602ad0f7e9STom Jones 	 * The command is queue sc->txq[0], our default queue is sc->txq[1].
21612ad0f7e9STom Jones 	 *
21622ad0f7e9STom Jones 	 * Tx aggregation requires additional queues, one queue per TID for
21632ad0f7e9STom Jones 	 * which aggregation is enabled. We map TID 0-7 to sc->txq[2:9].
21642ad0f7e9STom Jones 	 * Firmware may assign its own internal IDs for these queues
21652ad0f7e9STom Jones 	 * depending on which TID gets aggregation enabled first.
21662ad0f7e9STom Jones 	 * The driver maintains a table mapping driver-side queue IDs
21672ad0f7e9STom Jones 	 * to firmware-side queue IDs.
21682ad0f7e9STom Jones 	 */
21692ad0f7e9STom Jones 
21702ad0f7e9STom Jones 	if (sc->sc_device_family >= IWX_DEVICE_FAMILY_AX210) {
21712ad0f7e9STom Jones 		bc_tbl_size = sizeof(struct iwx_gen3_bc_tbl_entry) *
21722ad0f7e9STom Jones 		    IWX_TFD_QUEUE_BC_SIZE_GEN3_AX210;
21732ad0f7e9STom Jones 		bc_align = 128;
21742ad0f7e9STom Jones 	} else {
21752ad0f7e9STom Jones 		bc_tbl_size = sizeof(struct iwx_agn_scd_bc_tbl);
21762ad0f7e9STom Jones 		bc_align = 64;
21772ad0f7e9STom Jones 	}
21782ad0f7e9STom Jones 	err = iwx_dma_contig_alloc(sc->sc_dmat, &ring->bc_tbl, bc_tbl_size,
21792ad0f7e9STom Jones 	    bc_align);
21802ad0f7e9STom Jones 	if (err) {
21812ad0f7e9STom Jones 		device_printf(sc->sc_dev,
21822ad0f7e9STom Jones 		    "could not allocate byte count table DMA memory\n");
21832ad0f7e9STom Jones 		goto fail;
21842ad0f7e9STom Jones 	}
21852ad0f7e9STom Jones 
21862ad0f7e9STom Jones 	size = IWX_TX_RING_COUNT * sizeof(struct iwx_device_cmd);
21872ad0f7e9STom Jones 	err = iwx_dma_contig_alloc(sc->sc_dmat, &ring->cmd_dma, size,
21882ad0f7e9STom Jones 	    IWX_FIRST_TB_SIZE_ALIGN);
21892ad0f7e9STom Jones 	if (err) {
21902ad0f7e9STom Jones 		device_printf(sc->sc_dev,
21912ad0f7e9STom Jones 		    "could not allocate cmd DMA memory\n");
21922ad0f7e9STom Jones 		goto fail;
21932ad0f7e9STom Jones 	}
21942ad0f7e9STom Jones 	ring->cmd = ring->cmd_dma.vaddr;
21952ad0f7e9STom Jones 
21962ad0f7e9STom Jones 	/* FW commands may require more mapped space than packets. */
21972ad0f7e9STom Jones 	if (qid == IWX_DQA_CMD_QUEUE)
21982ad0f7e9STom Jones 		mapsize = (sizeof(struct iwx_cmd_header) +
21992ad0f7e9STom Jones 		    IWX_MAX_CMD_PAYLOAD_SIZE);
22002ad0f7e9STom Jones 	else
22012ad0f7e9STom Jones 		mapsize = MCLBYTES;
22022ad0f7e9STom Jones 	err = bus_dma_tag_create(sc->sc_dmat, 1, 0, BUS_SPACE_MAXADDR_32BIT,
22032ad0f7e9STom Jones 	    BUS_SPACE_MAXADDR, NULL, NULL, mapsize, IWX_TFH_NUM_TBS - 2,
22042ad0f7e9STom Jones 	    mapsize, 0, NULL, NULL, &ring->data_dmat);
22052ad0f7e9STom Jones 
22062ad0f7e9STom Jones 	paddr = ring->cmd_dma.paddr;
22072ad0f7e9STom Jones 	for (i = 0; i < IWX_TX_RING_COUNT; i++) {
22082ad0f7e9STom Jones 		struct iwx_tx_data *data = &ring->data[i];
22092ad0f7e9STom Jones 
22102ad0f7e9STom Jones 		data->cmd_paddr = paddr;
22112ad0f7e9STom Jones 		paddr += sizeof(struct iwx_device_cmd);
22122ad0f7e9STom Jones 
22132ad0f7e9STom Jones 		err = bus_dmamap_create(ring->data_dmat, 0, &data->map);
22142ad0f7e9STom Jones 		if (err) {
22152ad0f7e9STom Jones 			device_printf(sc->sc_dev,
22162ad0f7e9STom Jones 			    "could not create TX buf DMA map\n");
22172ad0f7e9STom Jones 			goto fail;
22182ad0f7e9STom Jones 		}
22192ad0f7e9STom Jones 	}
22202ad0f7e9STom Jones 	KASSERT(paddr == ring->cmd_dma.paddr + size, ("bad paddr in txr alloc"));
22212ad0f7e9STom Jones 	return 0;
22222ad0f7e9STom Jones 
22232ad0f7e9STom Jones fail:
22242ad0f7e9STom Jones 	return err;
22252ad0f7e9STom Jones }
22262ad0f7e9STom Jones 
22272ad0f7e9STom Jones static void
iwx_reset_tx_ring(struct iwx_softc * sc,struct iwx_tx_ring * ring)22282ad0f7e9STom Jones iwx_reset_tx_ring(struct iwx_softc *sc, struct iwx_tx_ring *ring)
22292ad0f7e9STom Jones {
22302ad0f7e9STom Jones 	int i;
22312ad0f7e9STom Jones 
22322ad0f7e9STom Jones 	for (i = 0; i < IWX_TX_RING_COUNT; i++) {
22332ad0f7e9STom Jones 		struct iwx_tx_data *data = &ring->data[i];
22342ad0f7e9STom Jones 
22352ad0f7e9STom Jones 		if (data->m != NULL) {
22362ad0f7e9STom Jones 			bus_dmamap_sync(ring->data_dmat, data->map,
22372ad0f7e9STom Jones 			    BUS_DMASYNC_POSTWRITE);
22382ad0f7e9STom Jones 			bus_dmamap_unload(ring->data_dmat, data->map);
22392ad0f7e9STom Jones 			m_freem(data->m);
22402ad0f7e9STom Jones 			data->m = NULL;
22412ad0f7e9STom Jones 		}
22422ad0f7e9STom Jones 	}
22432ad0f7e9STom Jones 
22442ad0f7e9STom Jones 	/* Clear byte count table. */
22452ad0f7e9STom Jones 	memset(ring->bc_tbl.vaddr, 0, ring->bc_tbl.size);
22462ad0f7e9STom Jones 
22472ad0f7e9STom Jones 	/* Clear TX descriptors. */
22482ad0f7e9STom Jones 	memset(ring->desc, 0, ring->desc_dma.size);
22492ad0f7e9STom Jones 	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
22502ad0f7e9STom Jones 	    BUS_DMASYNC_PREWRITE);
22512ad0f7e9STom Jones 	sc->qfullmsk &= ~(1 << ring->qid);
22522ad0f7e9STom Jones 	sc->qenablemsk &= ~(1 << ring->qid);
22532ad0f7e9STom Jones 	for (i = 0; i < nitems(sc->aggqid); i++) {
22542ad0f7e9STom Jones 		if (sc->aggqid[i] == ring->qid) {
22552ad0f7e9STom Jones 			sc->aggqid[i] = 0;
22562ad0f7e9STom Jones 			break;
22572ad0f7e9STom Jones 		}
22582ad0f7e9STom Jones 	}
22592ad0f7e9STom Jones 	ring->queued = 0;
22602ad0f7e9STom Jones 	ring->cur = 0;
22612ad0f7e9STom Jones 	ring->cur_hw = 0;
22622ad0f7e9STom Jones 	ring->tail = 0;
22632ad0f7e9STom Jones 	ring->tail_hw = 0;
22642ad0f7e9STom Jones 	ring->tid = 0;
22652ad0f7e9STom Jones }
22662ad0f7e9STom Jones 
22672ad0f7e9STom Jones static void
iwx_free_tx_ring(struct iwx_softc * sc,struct iwx_tx_ring * ring)22682ad0f7e9STom Jones iwx_free_tx_ring(struct iwx_softc *sc, struct iwx_tx_ring *ring)
22692ad0f7e9STom Jones {
22702ad0f7e9STom Jones 	int i;
22712ad0f7e9STom Jones 
22722ad0f7e9STom Jones 	iwx_dma_contig_free(&ring->desc_dma);
22732ad0f7e9STom Jones 	iwx_dma_contig_free(&ring->cmd_dma);
22742ad0f7e9STom Jones 	iwx_dma_contig_free(&ring->bc_tbl);
22752ad0f7e9STom Jones 
22762ad0f7e9STom Jones 	for (i = 0; i < IWX_TX_RING_COUNT; i++) {
22772ad0f7e9STom Jones 		struct iwx_tx_data *data = &ring->data[i];
22782ad0f7e9STom Jones 
22792ad0f7e9STom Jones 		if (data->m != NULL) {
22802ad0f7e9STom Jones 			bus_dmamap_sync(ring->data_dmat, data->map,
22812ad0f7e9STom Jones 			    BUS_DMASYNC_POSTWRITE);
22822ad0f7e9STom Jones 			bus_dmamap_unload(ring->data_dmat, data->map);
22832ad0f7e9STom Jones 			m_freem(data->m);
22842ad0f7e9STom Jones 			data->m = NULL;
22852ad0f7e9STom Jones 		}
22862ad0f7e9STom Jones 		if (data->map != NULL) {
22872ad0f7e9STom Jones 			bus_dmamap_destroy(ring->data_dmat, data->map);
22882ad0f7e9STom Jones 			data->map = NULL;
22892ad0f7e9STom Jones 		}
22902ad0f7e9STom Jones 	}
22912ad0f7e9STom Jones 	if (ring->data_dmat != NULL) {
22922ad0f7e9STom Jones 		bus_dma_tag_destroy(ring->data_dmat);
22932ad0f7e9STom Jones 		ring->data_dmat = NULL;
22942ad0f7e9STom Jones 	}
22952ad0f7e9STom Jones }
22962ad0f7e9STom Jones 
22972ad0f7e9STom Jones static void
iwx_enable_rfkill_int(struct iwx_softc * sc)22982ad0f7e9STom Jones iwx_enable_rfkill_int(struct iwx_softc *sc)
22992ad0f7e9STom Jones {
23002ad0f7e9STom Jones 	if (!sc->sc_msix) {
23012ad0f7e9STom Jones 		sc->sc_intmask = IWX_CSR_INT_BIT_RF_KILL;
23022ad0f7e9STom Jones 		IWX_WRITE(sc, IWX_CSR_INT_MASK, sc->sc_intmask);
23032ad0f7e9STom Jones 	} else {
23042ad0f7e9STom Jones 		IWX_WRITE(sc, IWX_CSR_MSIX_FH_INT_MASK_AD,
23052ad0f7e9STom Jones 		    sc->sc_fh_init_mask);
23062ad0f7e9STom Jones 		IWX_WRITE(sc, IWX_CSR_MSIX_HW_INT_MASK_AD,
23072ad0f7e9STom Jones 		    ~IWX_MSIX_HW_INT_CAUSES_REG_RF_KILL);
23082ad0f7e9STom Jones 		sc->sc_hw_mask = IWX_MSIX_HW_INT_CAUSES_REG_RF_KILL;
23092ad0f7e9STom Jones 	}
23102ad0f7e9STom Jones 
23112ad0f7e9STom Jones 	IWX_SETBITS(sc, IWX_CSR_GP_CNTRL,
23122ad0f7e9STom Jones 	    IWX_CSR_GP_CNTRL_REG_FLAG_RFKILL_WAKE_L1A_EN);
23132ad0f7e9STom Jones }
23142ad0f7e9STom Jones 
23152ad0f7e9STom Jones static int
iwx_check_rfkill(struct iwx_softc * sc)23162ad0f7e9STom Jones iwx_check_rfkill(struct iwx_softc *sc)
23172ad0f7e9STom Jones {
23182ad0f7e9STom Jones 	uint32_t v;
23192ad0f7e9STom Jones 	int rv;
23202ad0f7e9STom Jones 
23212ad0f7e9STom Jones 	/*
23222ad0f7e9STom Jones 	 * "documentation" is not really helpful here:
23232ad0f7e9STom Jones 	 *  27:	HW_RF_KILL_SW
23242ad0f7e9STom Jones 	 *	Indicates state of (platform's) hardware RF-Kill switch
23252ad0f7e9STom Jones 	 *
23262ad0f7e9STom Jones 	 * But apparently when it's off, it's on ...
23272ad0f7e9STom Jones 	 */
23282ad0f7e9STom Jones 	v = IWX_READ(sc, IWX_CSR_GP_CNTRL);
23292ad0f7e9STom Jones 	rv = (v & IWX_CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW) == 0;
23302ad0f7e9STom Jones 	if (rv) {
23312ad0f7e9STom Jones 		sc->sc_flags |= IWX_FLAG_RFKILL;
23322ad0f7e9STom Jones 	} else {
23332ad0f7e9STom Jones 		sc->sc_flags &= ~IWX_FLAG_RFKILL;
23342ad0f7e9STom Jones 	}
23352ad0f7e9STom Jones 
23362ad0f7e9STom Jones 	return rv;
23372ad0f7e9STom Jones }
23382ad0f7e9STom Jones 
23392ad0f7e9STom Jones static void
iwx_enable_interrupts(struct iwx_softc * sc)23402ad0f7e9STom Jones iwx_enable_interrupts(struct iwx_softc *sc)
23412ad0f7e9STom Jones {
23422ad0f7e9STom Jones 	if (!sc->sc_msix) {
23432ad0f7e9STom Jones 		sc->sc_intmask = IWX_CSR_INI_SET_MASK;
23442ad0f7e9STom Jones 		IWX_WRITE(sc, IWX_CSR_INT_MASK, sc->sc_intmask);
23452ad0f7e9STom Jones 	} else {
23462ad0f7e9STom Jones 		/*
23472ad0f7e9STom Jones 		 * fh/hw_mask keeps all the unmasked causes.
23482ad0f7e9STom Jones 		 * Unlike msi, in msix cause is enabled when it is unset.
23492ad0f7e9STom Jones 		 */
23502ad0f7e9STom Jones 		sc->sc_hw_mask = sc->sc_hw_init_mask;
23512ad0f7e9STom Jones 		sc->sc_fh_mask = sc->sc_fh_init_mask;
23522ad0f7e9STom Jones 		IWX_WRITE(sc, IWX_CSR_MSIX_FH_INT_MASK_AD,
23532ad0f7e9STom Jones 		    ~sc->sc_fh_mask);
23542ad0f7e9STom Jones 		IWX_WRITE(sc, IWX_CSR_MSIX_HW_INT_MASK_AD,
23552ad0f7e9STom Jones 		    ~sc->sc_hw_mask);
23562ad0f7e9STom Jones 	}
23572ad0f7e9STom Jones }
23582ad0f7e9STom Jones 
23592ad0f7e9STom Jones static void
iwx_enable_fwload_interrupt(struct iwx_softc * sc)23602ad0f7e9STom Jones iwx_enable_fwload_interrupt(struct iwx_softc *sc)
23612ad0f7e9STom Jones {
23622ad0f7e9STom Jones 	if (!sc->sc_msix) {
23632ad0f7e9STom Jones 		sc->sc_intmask = IWX_CSR_INT_BIT_ALIVE | IWX_CSR_INT_BIT_FH_RX;
23642ad0f7e9STom Jones 		IWX_WRITE(sc, IWX_CSR_INT_MASK, sc->sc_intmask);
23652ad0f7e9STom Jones 	} else {
23662ad0f7e9STom Jones 		IWX_WRITE(sc, IWX_CSR_MSIX_HW_INT_MASK_AD,
23672ad0f7e9STom Jones 		    ~IWX_MSIX_HW_INT_CAUSES_REG_ALIVE);
23682ad0f7e9STom Jones 		sc->sc_hw_mask = IWX_MSIX_HW_INT_CAUSES_REG_ALIVE;
23692ad0f7e9STom Jones 		/*
23702ad0f7e9STom Jones 		 * Leave all the FH causes enabled to get the ALIVE
23712ad0f7e9STom Jones 		 * notification.
23722ad0f7e9STom Jones 		 */
23732ad0f7e9STom Jones 		IWX_WRITE(sc, IWX_CSR_MSIX_FH_INT_MASK_AD,
23742ad0f7e9STom Jones 		    ~sc->sc_fh_init_mask);
23752ad0f7e9STom Jones 		sc->sc_fh_mask = sc->sc_fh_init_mask;
23762ad0f7e9STom Jones 	}
23772ad0f7e9STom Jones }
23782ad0f7e9STom Jones 
23792ad0f7e9STom Jones #if 0
23802ad0f7e9STom Jones static void
23812ad0f7e9STom Jones iwx_restore_interrupts(struct iwx_softc *sc)
23822ad0f7e9STom Jones {
23832ad0f7e9STom Jones 	IWX_WRITE(sc, IWX_CSR_INT_MASK, sc->sc_intmask);
23842ad0f7e9STom Jones }
23852ad0f7e9STom Jones #endif
23862ad0f7e9STom Jones 
23872ad0f7e9STom Jones static void
iwx_disable_interrupts(struct iwx_softc * sc)23882ad0f7e9STom Jones iwx_disable_interrupts(struct iwx_softc *sc)
23892ad0f7e9STom Jones {
23902ad0f7e9STom Jones 	if (!sc->sc_msix) {
23912ad0f7e9STom Jones 		IWX_WRITE(sc, IWX_CSR_INT_MASK, 0);
23922ad0f7e9STom Jones 
23932ad0f7e9STom Jones 		/* acknowledge all interrupts */
23942ad0f7e9STom Jones 		IWX_WRITE(sc, IWX_CSR_INT, ~0);
23952ad0f7e9STom Jones 		IWX_WRITE(sc, IWX_CSR_FH_INT_STATUS, ~0);
23962ad0f7e9STom Jones 	} else {
23972ad0f7e9STom Jones 		IWX_WRITE(sc, IWX_CSR_MSIX_FH_INT_MASK_AD,
23982ad0f7e9STom Jones 		    sc->sc_fh_init_mask);
23992ad0f7e9STom Jones 		IWX_WRITE(sc, IWX_CSR_MSIX_HW_INT_MASK_AD,
24002ad0f7e9STom Jones 		    sc->sc_hw_init_mask);
24012ad0f7e9STom Jones 	}
24022ad0f7e9STom Jones }
24032ad0f7e9STom Jones 
24042ad0f7e9STom Jones static void
iwx_ict_reset(struct iwx_softc * sc)24052ad0f7e9STom Jones iwx_ict_reset(struct iwx_softc *sc)
24062ad0f7e9STom Jones {
24072ad0f7e9STom Jones 	iwx_disable_interrupts(sc);
24082ad0f7e9STom Jones 
24092ad0f7e9STom Jones 	memset(sc->ict_dma.vaddr, 0, IWX_ICT_SIZE);
24102ad0f7e9STom Jones 	sc->ict_cur = 0;
24112ad0f7e9STom Jones 
24122ad0f7e9STom Jones 	/* Set physical address of ICT (4KB aligned). */
24132ad0f7e9STom Jones 	IWX_WRITE(sc, IWX_CSR_DRAM_INT_TBL_REG,
24142ad0f7e9STom Jones 	    IWX_CSR_DRAM_INT_TBL_ENABLE
24152ad0f7e9STom Jones 	    | IWX_CSR_DRAM_INIT_TBL_WRAP_CHECK
24162ad0f7e9STom Jones 	    | IWX_CSR_DRAM_INIT_TBL_WRITE_POINTER
24172ad0f7e9STom Jones 	    | sc->ict_dma.paddr >> IWX_ICT_PADDR_SHIFT);
24182ad0f7e9STom Jones 
24192ad0f7e9STom Jones 	/* Switch to ICT interrupt mode in driver. */
24202ad0f7e9STom Jones 	sc->sc_flags |= IWX_FLAG_USE_ICT;
24212ad0f7e9STom Jones 
24222ad0f7e9STom Jones 	IWX_WRITE(sc, IWX_CSR_INT, ~0);
24232ad0f7e9STom Jones 	iwx_enable_interrupts(sc);
24242ad0f7e9STom Jones }
24252ad0f7e9STom Jones 
24262ad0f7e9STom Jones #define IWX_HW_READY_TIMEOUT 50
24272ad0f7e9STom Jones static int
iwx_set_hw_ready(struct iwx_softc * sc)24282ad0f7e9STom Jones iwx_set_hw_ready(struct iwx_softc *sc)
24292ad0f7e9STom Jones {
24302ad0f7e9STom Jones 	int ready;
24312ad0f7e9STom Jones 
24322ad0f7e9STom Jones 	IWX_SETBITS(sc, IWX_CSR_HW_IF_CONFIG_REG,
24332ad0f7e9STom Jones 	    IWX_CSR_HW_IF_CONFIG_REG_BIT_NIC_READY);
24342ad0f7e9STom Jones 
24352ad0f7e9STom Jones 	ready = iwx_poll_bit(sc, IWX_CSR_HW_IF_CONFIG_REG,
24362ad0f7e9STom Jones 	    IWX_CSR_HW_IF_CONFIG_REG_BIT_NIC_READY,
24372ad0f7e9STom Jones 	    IWX_CSR_HW_IF_CONFIG_REG_BIT_NIC_READY,
24382ad0f7e9STom Jones 	    IWX_HW_READY_TIMEOUT);
24392ad0f7e9STom Jones 	if (ready)
24402ad0f7e9STom Jones 		IWX_SETBITS(sc, IWX_CSR_MBOX_SET_REG,
24412ad0f7e9STom Jones 		    IWX_CSR_MBOX_SET_REG_OS_ALIVE);
24422ad0f7e9STom Jones 
24432ad0f7e9STom Jones 	DPRINTF(("%s: ready=%d\n", __func__, ready));
24442ad0f7e9STom Jones 	return ready;
24452ad0f7e9STom Jones }
24462ad0f7e9STom Jones #undef IWX_HW_READY_TIMEOUT
24472ad0f7e9STom Jones 
24482ad0f7e9STom Jones static int
iwx_prepare_card_hw(struct iwx_softc * sc)24492ad0f7e9STom Jones iwx_prepare_card_hw(struct iwx_softc *sc)
24502ad0f7e9STom Jones {
24512ad0f7e9STom Jones 	int t = 0;
24522ad0f7e9STom Jones 	int ntries;
24532ad0f7e9STom Jones 
24542ad0f7e9STom Jones 	if (iwx_set_hw_ready(sc))
24552ad0f7e9STom Jones 		return 0;
24562ad0f7e9STom Jones 
24572ad0f7e9STom Jones 	IWX_SETBITS(sc, IWX_CSR_DBG_LINK_PWR_MGMT_REG,
24582ad0f7e9STom Jones 	    IWX_CSR_RESET_LINK_PWR_MGMT_DISABLED);
24592ad0f7e9STom Jones 	DELAY(1000);
24602ad0f7e9STom Jones 
24612ad0f7e9STom Jones 	for (ntries = 0; ntries < 10; ntries++) {
24622ad0f7e9STom Jones 		/* If HW is not ready, prepare the conditions to check again */
24632ad0f7e9STom Jones 		IWX_SETBITS(sc, IWX_CSR_HW_IF_CONFIG_REG,
24642ad0f7e9STom Jones 		    IWX_CSR_HW_IF_CONFIG_REG_PREPARE);
24652ad0f7e9STom Jones 
24662ad0f7e9STom Jones 		do {
24672ad0f7e9STom Jones 			if (iwx_set_hw_ready(sc))
24682ad0f7e9STom Jones 				return 0;
24692ad0f7e9STom Jones 			DELAY(200);
24702ad0f7e9STom Jones 			t += 200;
24712ad0f7e9STom Jones 		} while (t < 150000);
24722ad0f7e9STom Jones 		DELAY(25000);
24732ad0f7e9STom Jones 	}
24742ad0f7e9STom Jones 
24752ad0f7e9STom Jones 	return ETIMEDOUT;
24762ad0f7e9STom Jones }
24772ad0f7e9STom Jones 
24782ad0f7e9STom Jones static int
iwx_force_power_gating(struct iwx_softc * sc)24792ad0f7e9STom Jones iwx_force_power_gating(struct iwx_softc *sc)
24802ad0f7e9STom Jones {
24812ad0f7e9STom Jones 	int err;
24822ad0f7e9STom Jones 
24832ad0f7e9STom Jones 	err = iwx_set_bits_prph(sc, IWX_HPM_HIPM_GEN_CFG,
24842ad0f7e9STom Jones 	    IWX_HPM_HIPM_GEN_CFG_CR_FORCE_ACTIVE);
24852ad0f7e9STom Jones 	if (err)
24862ad0f7e9STom Jones 		return err;
24872ad0f7e9STom Jones 	DELAY(20);
24882ad0f7e9STom Jones 	err = iwx_set_bits_prph(sc, IWX_HPM_HIPM_GEN_CFG,
24892ad0f7e9STom Jones 	    IWX_HPM_HIPM_GEN_CFG_CR_PG_EN |
24902ad0f7e9STom Jones 	    IWX_HPM_HIPM_GEN_CFG_CR_SLP_EN);
24912ad0f7e9STom Jones 	if (err)
24922ad0f7e9STom Jones 		return err;
24932ad0f7e9STom Jones 	DELAY(20);
24942ad0f7e9STom Jones 	err = iwx_clear_bits_prph(sc, IWX_HPM_HIPM_GEN_CFG,
24952ad0f7e9STom Jones 	    IWX_HPM_HIPM_GEN_CFG_CR_FORCE_ACTIVE);
24962ad0f7e9STom Jones 	return err;
24972ad0f7e9STom Jones }
24982ad0f7e9STom Jones 
24992ad0f7e9STom Jones static void
iwx_apm_config(struct iwx_softc * sc)25002ad0f7e9STom Jones iwx_apm_config(struct iwx_softc *sc)
25012ad0f7e9STom Jones {
25022ad0f7e9STom Jones 	uint16_t lctl, cap;
25032ad0f7e9STom Jones 	int pcie_ptr;
25042ad0f7e9STom Jones 	int error;
25052ad0f7e9STom Jones 
25062ad0f7e9STom Jones 	/*
25072ad0f7e9STom Jones 	 * L0S states have been found to be unstable with our devices
25082ad0f7e9STom Jones 	 * and in newer hardware they are not officially supported at
25092ad0f7e9STom Jones 	 * all, so we must always set the L0S_DISABLED bit.
25102ad0f7e9STom Jones 	 */
25112ad0f7e9STom Jones 	IWX_SETBITS(sc, IWX_CSR_GIO_REG, IWX_CSR_GIO_REG_VAL_L0S_DISABLED);
25122ad0f7e9STom Jones 
25132ad0f7e9STom Jones 	error = pci_find_cap(sc->sc_dev, PCIY_EXPRESS, &pcie_ptr);
25142ad0f7e9STom Jones 	if (error != 0) {
25152ad0f7e9STom Jones 		printf("can't fill pcie_ptr\n");
25162ad0f7e9STom Jones 		return;
25172ad0f7e9STom Jones 	}
25182ad0f7e9STom Jones 
25192ad0f7e9STom Jones 	lctl = pci_read_config(sc->sc_dev, pcie_ptr + PCIER_LINK_CTL,
25202ad0f7e9STom Jones 	    sizeof(lctl));
25212ad0f7e9STom Jones #define PCI_PCIE_LCSR_ASPM_L0S 0x00000001
25222ad0f7e9STom Jones 	sc->sc_pm_support = !(lctl & PCI_PCIE_LCSR_ASPM_L0S);
25232ad0f7e9STom Jones #define PCI_PCIE_DCSR2 0x28
25242ad0f7e9STom Jones 	cap = pci_read_config(sc->sc_dev, pcie_ptr + PCI_PCIE_DCSR2,
25252ad0f7e9STom Jones 	    sizeof(lctl));
25262ad0f7e9STom Jones #define PCI_PCIE_DCSR2_LTREN 0x00000400
25272ad0f7e9STom Jones 	sc->sc_ltr_enabled = (cap & PCI_PCIE_DCSR2_LTREN) ? 1 : 0;
25282ad0f7e9STom Jones #define PCI_PCIE_LCSR_ASPM_L1 0x00000002
25292ad0f7e9STom Jones 	DPRINTF(("%s: L1 %sabled - LTR %sabled\n",
25302ad0f7e9STom Jones 	    DEVNAME(sc),
25312ad0f7e9STom Jones 	    (lctl & PCI_PCIE_LCSR_ASPM_L1) ? "En" : "Dis",
25322ad0f7e9STom Jones 	    sc->sc_ltr_enabled ? "En" : "Dis"));
25332ad0f7e9STom Jones #undef PCI_PCIE_LCSR_ASPM_L0S
25342ad0f7e9STom Jones #undef PCI_PCIE_DCSR2
25352ad0f7e9STom Jones #undef PCI_PCIE_DCSR2_LTREN
25362ad0f7e9STom Jones #undef PCI_PCIE_LCSR_ASPM_L1
25372ad0f7e9STom Jones }
25382ad0f7e9STom Jones 
25392ad0f7e9STom Jones /*
25402ad0f7e9STom Jones  * Start up NIC's basic functionality after it has been reset
25412ad0f7e9STom Jones  * e.g. after platform boot or shutdown.
25422ad0f7e9STom Jones  * NOTE:  This does not load uCode nor start the embedded processor
25432ad0f7e9STom Jones  */
25442ad0f7e9STom Jones static int
iwx_apm_init(struct iwx_softc * sc)25452ad0f7e9STom Jones iwx_apm_init(struct iwx_softc *sc)
25462ad0f7e9STom Jones {
25472ad0f7e9STom Jones 	int err = 0;
25482ad0f7e9STom Jones 
25492ad0f7e9STom Jones 	/*
25502ad0f7e9STom Jones 	 * Disable L0s without affecting L1;
25512ad0f7e9STom Jones 	 *  don't wait for ICH L0s (ICH bug W/A)
25522ad0f7e9STom Jones 	 */
25532ad0f7e9STom Jones 	IWX_SETBITS(sc, IWX_CSR_GIO_CHICKEN_BITS,
25542ad0f7e9STom Jones 	    IWX_CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX);
25552ad0f7e9STom Jones 
25562ad0f7e9STom Jones 	/* Set FH wait threshold to maximum (HW error during stress W/A) */
25572ad0f7e9STom Jones 	IWX_SETBITS(sc, IWX_CSR_DBG_HPET_MEM_REG, IWX_CSR_DBG_HPET_MEM_REG_VAL);
25582ad0f7e9STom Jones 
25592ad0f7e9STom Jones 	/*
25602ad0f7e9STom Jones 	 * Enable HAP INTA (interrupt from management bus) to
25612ad0f7e9STom Jones 	 * wake device's PCI Express link L1a -> L0s
25622ad0f7e9STom Jones 	 */
25632ad0f7e9STom Jones 	IWX_SETBITS(sc, IWX_CSR_HW_IF_CONFIG_REG,
25642ad0f7e9STom Jones 	    IWX_CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A);
25652ad0f7e9STom Jones 
25662ad0f7e9STom Jones 	iwx_apm_config(sc);
25672ad0f7e9STom Jones 
25682ad0f7e9STom Jones 	/*
25692ad0f7e9STom Jones 	 * Set "initialization complete" bit to move adapter from
25702ad0f7e9STom Jones 	 * D0U* --> D0A* (powered-up active) state.
25712ad0f7e9STom Jones 	 */
25722ad0f7e9STom Jones 	IWX_SETBITS(sc, IWX_CSR_GP_CNTRL, IWX_CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
25732ad0f7e9STom Jones 
25742ad0f7e9STom Jones 	/*
25752ad0f7e9STom Jones 	 * Wait for clock stabilization; once stabilized, access to
25762ad0f7e9STom Jones 	 * device-internal resources is supported, e.g. iwx_write_prph()
25772ad0f7e9STom Jones 	 * and accesses to uCode SRAM.
25782ad0f7e9STom Jones 	 */
25792ad0f7e9STom Jones 	if (!iwx_poll_bit(sc, IWX_CSR_GP_CNTRL,
25802ad0f7e9STom Jones 	    IWX_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
25812ad0f7e9STom Jones 	    IWX_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000)) {
25822ad0f7e9STom Jones 		printf("%s: timeout waiting for clock stabilization\n",
25832ad0f7e9STom Jones 		    DEVNAME(sc));
25842ad0f7e9STom Jones 		err = ETIMEDOUT;
25852ad0f7e9STom Jones 		goto out;
25862ad0f7e9STom Jones 	}
25872ad0f7e9STom Jones  out:
25882ad0f7e9STom Jones 	if (err)
25892ad0f7e9STom Jones 		printf("%s: apm init error %d\n", DEVNAME(sc), err);
25902ad0f7e9STom Jones 	return err;
25912ad0f7e9STom Jones }
25922ad0f7e9STom Jones 
25932ad0f7e9STom Jones static void
iwx_apm_stop(struct iwx_softc * sc)25942ad0f7e9STom Jones iwx_apm_stop(struct iwx_softc *sc)
25952ad0f7e9STom Jones {
25962ad0f7e9STom Jones 	IWX_SETBITS(sc, IWX_CSR_DBG_LINK_PWR_MGMT_REG,
25972ad0f7e9STom Jones 	    IWX_CSR_RESET_LINK_PWR_MGMT_DISABLED);
25982ad0f7e9STom Jones 	IWX_SETBITS(sc, IWX_CSR_HW_IF_CONFIG_REG,
25992ad0f7e9STom Jones 	    IWX_CSR_HW_IF_CONFIG_REG_PREPARE |
26002ad0f7e9STom Jones 	    IWX_CSR_HW_IF_CONFIG_REG_ENABLE_PME);
26012ad0f7e9STom Jones 	DELAY(1000);
26022ad0f7e9STom Jones 	IWX_CLRBITS(sc, IWX_CSR_DBG_LINK_PWR_MGMT_REG,
26032ad0f7e9STom Jones 	    IWX_CSR_RESET_LINK_PWR_MGMT_DISABLED);
26042ad0f7e9STom Jones 	DELAY(5000);
26052ad0f7e9STom Jones 
26062ad0f7e9STom Jones 	/* stop device's busmaster DMA activity */
26072ad0f7e9STom Jones 	IWX_SETBITS(sc, IWX_CSR_RESET, IWX_CSR_RESET_REG_FLAG_STOP_MASTER);
26082ad0f7e9STom Jones 
26092ad0f7e9STom Jones 	if (!iwx_poll_bit(sc, IWX_CSR_RESET,
26102ad0f7e9STom Jones 	    IWX_CSR_RESET_REG_FLAG_MASTER_DISABLED,
26112ad0f7e9STom Jones 	    IWX_CSR_RESET_REG_FLAG_MASTER_DISABLED, 100))
261228345b17STom Jones 		printf("%s: timeout waiting for bus master\n", DEVNAME(sc));
26132ad0f7e9STom Jones 
26142ad0f7e9STom Jones 	/*
26152ad0f7e9STom Jones 	 * Clear "initialization complete" bit to move adapter from
26162ad0f7e9STom Jones 	 * D0A* (powered-up Active) --> D0U* (Uninitialized) state.
26172ad0f7e9STom Jones 	 */
26182ad0f7e9STom Jones 	IWX_CLRBITS(sc, IWX_CSR_GP_CNTRL,
26192ad0f7e9STom Jones 	    IWX_CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
26202ad0f7e9STom Jones }
26212ad0f7e9STom Jones 
26222ad0f7e9STom Jones static void
iwx_init_msix_hw(struct iwx_softc * sc)26232ad0f7e9STom Jones iwx_init_msix_hw(struct iwx_softc *sc)
26242ad0f7e9STom Jones {
26252ad0f7e9STom Jones 	iwx_conf_msix_hw(sc, 0);
26262ad0f7e9STom Jones 
26272ad0f7e9STom Jones 	if (!sc->sc_msix)
26282ad0f7e9STom Jones 		return;
26292ad0f7e9STom Jones 
26302ad0f7e9STom Jones 	sc->sc_fh_init_mask = ~IWX_READ(sc, IWX_CSR_MSIX_FH_INT_MASK_AD);
26312ad0f7e9STom Jones 	sc->sc_fh_mask = sc->sc_fh_init_mask;
26322ad0f7e9STom Jones 	sc->sc_hw_init_mask = ~IWX_READ(sc, IWX_CSR_MSIX_HW_INT_MASK_AD);
26332ad0f7e9STom Jones 	sc->sc_hw_mask = sc->sc_hw_init_mask;
26342ad0f7e9STom Jones }
26352ad0f7e9STom Jones 
26362ad0f7e9STom Jones static void
iwx_conf_msix_hw(struct iwx_softc * sc,int stopped)26372ad0f7e9STom Jones iwx_conf_msix_hw(struct iwx_softc *sc, int stopped)
26382ad0f7e9STom Jones {
26392ad0f7e9STom Jones 	int vector = 0;
26402ad0f7e9STom Jones 
26412ad0f7e9STom Jones 	if (!sc->sc_msix) {
26422ad0f7e9STom Jones 		/* Newer chips default to MSIX. */
26432ad0f7e9STom Jones 		if (!stopped && iwx_nic_lock(sc)) {
26442ad0f7e9STom Jones 			iwx_write_umac_prph(sc, IWX_UREG_CHICK,
26452ad0f7e9STom Jones 			    IWX_UREG_CHICK_MSI_ENABLE);
26462ad0f7e9STom Jones 			iwx_nic_unlock(sc);
26472ad0f7e9STom Jones 		}
26482ad0f7e9STom Jones 		return;
26492ad0f7e9STom Jones 	}
26502ad0f7e9STom Jones 
26512ad0f7e9STom Jones 	if (!stopped && iwx_nic_lock(sc)) {
26522ad0f7e9STom Jones 		iwx_write_umac_prph(sc, IWX_UREG_CHICK,
26532ad0f7e9STom Jones 		    IWX_UREG_CHICK_MSIX_ENABLE);
26542ad0f7e9STom Jones 		iwx_nic_unlock(sc);
26552ad0f7e9STom Jones 	}
26562ad0f7e9STom Jones 
26572ad0f7e9STom Jones 	/* Disable all interrupts */
26582ad0f7e9STom Jones 	IWX_WRITE(sc, IWX_CSR_MSIX_FH_INT_MASK_AD, ~0);
26592ad0f7e9STom Jones 	IWX_WRITE(sc, IWX_CSR_MSIX_HW_INT_MASK_AD, ~0);
26602ad0f7e9STom Jones 
26612ad0f7e9STom Jones 	/* Map fallback-queue (command/mgmt) to a single vector */
26622ad0f7e9STom Jones 	IWX_WRITE_1(sc, IWX_CSR_MSIX_RX_IVAR(0),
26632ad0f7e9STom Jones 	    vector | IWX_MSIX_NON_AUTO_CLEAR_CAUSE);
26642ad0f7e9STom Jones 	/* Map RSS queue (data) to the same vector */
26652ad0f7e9STom Jones 	IWX_WRITE_1(sc, IWX_CSR_MSIX_RX_IVAR(1),
26662ad0f7e9STom Jones 	    vector | IWX_MSIX_NON_AUTO_CLEAR_CAUSE);
26672ad0f7e9STom Jones 
26682ad0f7e9STom Jones 	/* Enable the RX queues cause interrupts */
26692ad0f7e9STom Jones 	IWX_CLRBITS(sc, IWX_CSR_MSIX_FH_INT_MASK_AD,
26702ad0f7e9STom Jones 	    IWX_MSIX_FH_INT_CAUSES_Q0 | IWX_MSIX_FH_INT_CAUSES_Q1);
26712ad0f7e9STom Jones 
26722ad0f7e9STom Jones 	/* Map non-RX causes to the same vector */
26732ad0f7e9STom Jones 	IWX_WRITE_1(sc, IWX_CSR_MSIX_IVAR(IWX_MSIX_IVAR_CAUSE_D2S_CH0_NUM),
26742ad0f7e9STom Jones 	    vector | IWX_MSIX_NON_AUTO_CLEAR_CAUSE);
26752ad0f7e9STom Jones 	IWX_WRITE_1(sc, IWX_CSR_MSIX_IVAR(IWX_MSIX_IVAR_CAUSE_D2S_CH1_NUM),
26762ad0f7e9STom Jones 	    vector | IWX_MSIX_NON_AUTO_CLEAR_CAUSE);
26772ad0f7e9STom Jones 	IWX_WRITE_1(sc, IWX_CSR_MSIX_IVAR(IWX_MSIX_IVAR_CAUSE_S2D),
26782ad0f7e9STom Jones 	    vector | IWX_MSIX_NON_AUTO_CLEAR_CAUSE);
26792ad0f7e9STom Jones 	IWX_WRITE_1(sc, IWX_CSR_MSIX_IVAR(IWX_MSIX_IVAR_CAUSE_FH_ERR),
26802ad0f7e9STom Jones 	    vector | IWX_MSIX_NON_AUTO_CLEAR_CAUSE);
26812ad0f7e9STom Jones 	IWX_WRITE_1(sc, IWX_CSR_MSIX_IVAR(IWX_MSIX_IVAR_CAUSE_REG_ALIVE),
26822ad0f7e9STom Jones 	    vector | IWX_MSIX_NON_AUTO_CLEAR_CAUSE);
26832ad0f7e9STom Jones 	IWX_WRITE_1(sc, IWX_CSR_MSIX_IVAR(IWX_MSIX_IVAR_CAUSE_REG_WAKEUP),
26842ad0f7e9STom Jones 	    vector | IWX_MSIX_NON_AUTO_CLEAR_CAUSE);
26852ad0f7e9STom Jones 	IWX_WRITE_1(sc, IWX_CSR_MSIX_IVAR(IWX_MSIX_IVAR_CAUSE_REG_RESET_DONE),
26862ad0f7e9STom Jones 	    vector | IWX_MSIX_NON_AUTO_CLEAR_CAUSE);
26872ad0f7e9STom Jones 	IWX_WRITE_1(sc, IWX_CSR_MSIX_IVAR(IWX_MSIX_IVAR_CAUSE_REG_CT_KILL),
26882ad0f7e9STom Jones 	    vector | IWX_MSIX_NON_AUTO_CLEAR_CAUSE);
26892ad0f7e9STom Jones 	IWX_WRITE_1(sc, IWX_CSR_MSIX_IVAR(IWX_MSIX_IVAR_CAUSE_REG_RF_KILL),
26902ad0f7e9STom Jones 	    vector | IWX_MSIX_NON_AUTO_CLEAR_CAUSE);
26912ad0f7e9STom Jones 	IWX_WRITE_1(sc, IWX_CSR_MSIX_IVAR(IWX_MSIX_IVAR_CAUSE_REG_PERIODIC),
26922ad0f7e9STom Jones 	    vector | IWX_MSIX_NON_AUTO_CLEAR_CAUSE);
26932ad0f7e9STom Jones 	IWX_WRITE_1(sc, IWX_CSR_MSIX_IVAR(IWX_MSIX_IVAR_CAUSE_REG_SW_ERR),
26942ad0f7e9STom Jones 	    vector | IWX_MSIX_NON_AUTO_CLEAR_CAUSE);
26952ad0f7e9STom Jones 	IWX_WRITE_1(sc, IWX_CSR_MSIX_IVAR(IWX_MSIX_IVAR_CAUSE_REG_SCD),
26962ad0f7e9STom Jones 	    vector | IWX_MSIX_NON_AUTO_CLEAR_CAUSE);
26972ad0f7e9STom Jones 	IWX_WRITE_1(sc, IWX_CSR_MSIX_IVAR(IWX_MSIX_IVAR_CAUSE_REG_FH_TX),
26982ad0f7e9STom Jones 	    vector | IWX_MSIX_NON_AUTO_CLEAR_CAUSE);
26992ad0f7e9STom Jones 	IWX_WRITE_1(sc, IWX_CSR_MSIX_IVAR(IWX_MSIX_IVAR_CAUSE_REG_HW_ERR),
27002ad0f7e9STom Jones 	    vector | IWX_MSIX_NON_AUTO_CLEAR_CAUSE);
27012ad0f7e9STom Jones 	IWX_WRITE_1(sc, IWX_CSR_MSIX_IVAR(IWX_MSIX_IVAR_CAUSE_REG_HAP),
27022ad0f7e9STom Jones 	    vector | IWX_MSIX_NON_AUTO_CLEAR_CAUSE);
27032ad0f7e9STom Jones 
27042ad0f7e9STom Jones 	/* Enable non-RX causes interrupts */
27052ad0f7e9STom Jones 	IWX_CLRBITS(sc, IWX_CSR_MSIX_FH_INT_MASK_AD,
27062ad0f7e9STom Jones 	    IWX_MSIX_FH_INT_CAUSES_D2S_CH0_NUM |
27072ad0f7e9STom Jones 	    IWX_MSIX_FH_INT_CAUSES_D2S_CH1_NUM |
27082ad0f7e9STom Jones 	    IWX_MSIX_FH_INT_CAUSES_S2D |
27092ad0f7e9STom Jones 	    IWX_MSIX_FH_INT_CAUSES_FH_ERR);
27102ad0f7e9STom Jones 	IWX_CLRBITS(sc, IWX_CSR_MSIX_HW_INT_MASK_AD,
27112ad0f7e9STom Jones 	    IWX_MSIX_HW_INT_CAUSES_REG_ALIVE |
27122ad0f7e9STom Jones 	    IWX_MSIX_HW_INT_CAUSES_REG_WAKEUP |
27132ad0f7e9STom Jones 	    IWX_MSIX_HW_INT_CAUSES_REG_RESET_DONE |
27142ad0f7e9STom Jones 	    IWX_MSIX_HW_INT_CAUSES_REG_CT_KILL |
27152ad0f7e9STom Jones 	    IWX_MSIX_HW_INT_CAUSES_REG_RF_KILL |
27162ad0f7e9STom Jones 	    IWX_MSIX_HW_INT_CAUSES_REG_PERIODIC |
27172ad0f7e9STom Jones 	    IWX_MSIX_HW_INT_CAUSES_REG_SW_ERR |
27182ad0f7e9STom Jones 	    IWX_MSIX_HW_INT_CAUSES_REG_SCD |
27192ad0f7e9STom Jones 	    IWX_MSIX_HW_INT_CAUSES_REG_FH_TX |
27202ad0f7e9STom Jones 	    IWX_MSIX_HW_INT_CAUSES_REG_HW_ERR |
27212ad0f7e9STom Jones 	    IWX_MSIX_HW_INT_CAUSES_REG_HAP);
27222ad0f7e9STom Jones }
27232ad0f7e9STom Jones 
27242ad0f7e9STom Jones static int
iwx_clear_persistence_bit(struct iwx_softc * sc)27252ad0f7e9STom Jones iwx_clear_persistence_bit(struct iwx_softc *sc)
27262ad0f7e9STom Jones {
27272ad0f7e9STom Jones 	uint32_t hpm, wprot;
27282ad0f7e9STom Jones 
27292ad0f7e9STom Jones 	hpm = iwx_read_prph_unlocked(sc, IWX_HPM_DEBUG);
27302ad0f7e9STom Jones 	if (hpm != 0xa5a5a5a0 && (hpm & IWX_PERSISTENCE_BIT)) {
27312ad0f7e9STom Jones 		wprot = iwx_read_prph_unlocked(sc, IWX_PREG_PRPH_WPROT_22000);
27322ad0f7e9STom Jones 		if (wprot & IWX_PREG_WFPM_ACCESS) {
27332ad0f7e9STom Jones 			printf("%s: cannot clear persistence bit\n",
27342ad0f7e9STom Jones 			    DEVNAME(sc));
27352ad0f7e9STom Jones 			return EPERM;
27362ad0f7e9STom Jones 		}
27372ad0f7e9STom Jones 		iwx_write_prph_unlocked(sc, IWX_HPM_DEBUG,
27382ad0f7e9STom Jones 		    hpm & ~IWX_PERSISTENCE_BIT);
27392ad0f7e9STom Jones 	}
27402ad0f7e9STom Jones 
27412ad0f7e9STom Jones 	return 0;
27422ad0f7e9STom Jones }
27432ad0f7e9STom Jones 
27442ad0f7e9STom Jones static int
iwx_start_hw(struct iwx_softc * sc)27452ad0f7e9STom Jones iwx_start_hw(struct iwx_softc *sc)
27462ad0f7e9STom Jones {
27472ad0f7e9STom Jones 	int err;
27482ad0f7e9STom Jones 
27492ad0f7e9STom Jones 	err = iwx_prepare_card_hw(sc);
27502ad0f7e9STom Jones 	if (err)
27512ad0f7e9STom Jones 		return err;
27522ad0f7e9STom Jones 
27532ad0f7e9STom Jones 	if (sc->sc_device_family == IWX_DEVICE_FAMILY_22000) {
27542ad0f7e9STom Jones 		err = iwx_clear_persistence_bit(sc);
27552ad0f7e9STom Jones 		if (err)
27562ad0f7e9STom Jones 			return err;
27572ad0f7e9STom Jones 	}
27582ad0f7e9STom Jones 
27592ad0f7e9STom Jones 	/* Reset the entire device */
27602ad0f7e9STom Jones 	IWX_SETBITS(sc, IWX_CSR_RESET, IWX_CSR_RESET_REG_FLAG_SW_RESET);
27612ad0f7e9STom Jones 	DELAY(5000);
27622ad0f7e9STom Jones 
27632ad0f7e9STom Jones 	if (sc->sc_device_family == IWX_DEVICE_FAMILY_22000 &&
27642ad0f7e9STom Jones 	    sc->sc_integrated) {
27652ad0f7e9STom Jones 		IWX_SETBITS(sc, IWX_CSR_GP_CNTRL,
27662ad0f7e9STom Jones 		    IWX_CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
27672ad0f7e9STom Jones 		DELAY(20);
27682ad0f7e9STom Jones 		if (!iwx_poll_bit(sc, IWX_CSR_GP_CNTRL,
27692ad0f7e9STom Jones 		    IWX_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
27702ad0f7e9STom Jones 		    IWX_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000)) {
27712ad0f7e9STom Jones 			printf("%s: timeout waiting for clock stabilization\n",
27722ad0f7e9STom Jones 			    DEVNAME(sc));
27732ad0f7e9STom Jones 			return ETIMEDOUT;
27742ad0f7e9STom Jones 		}
27752ad0f7e9STom Jones 
27762ad0f7e9STom Jones 		err = iwx_force_power_gating(sc);
27772ad0f7e9STom Jones 		if (err)
27782ad0f7e9STom Jones 			return err;
27792ad0f7e9STom Jones 
27802ad0f7e9STom Jones 		/* Reset the entire device */
27812ad0f7e9STom Jones 		IWX_SETBITS(sc, IWX_CSR_RESET, IWX_CSR_RESET_REG_FLAG_SW_RESET);
27822ad0f7e9STom Jones 		DELAY(5000);
27832ad0f7e9STom Jones 	}
27842ad0f7e9STom Jones 
27852ad0f7e9STom Jones 	err = iwx_apm_init(sc);
27862ad0f7e9STom Jones 	if (err)
27872ad0f7e9STom Jones 		return err;
27882ad0f7e9STom Jones 
27892ad0f7e9STom Jones 	iwx_init_msix_hw(sc);
27902ad0f7e9STom Jones 
27912ad0f7e9STom Jones 	iwx_enable_rfkill_int(sc);
27922ad0f7e9STom Jones 	iwx_check_rfkill(sc);
27932ad0f7e9STom Jones 
27942ad0f7e9STom Jones 	return 0;
27952ad0f7e9STom Jones }
27962ad0f7e9STom Jones 
27972ad0f7e9STom Jones static void
iwx_stop_device(struct iwx_softc * sc)27982ad0f7e9STom Jones iwx_stop_device(struct iwx_softc *sc)
27992ad0f7e9STom Jones {
28002ad0f7e9STom Jones 	int i;
28012ad0f7e9STom Jones 
28022ad0f7e9STom Jones 	iwx_disable_interrupts(sc);
28032ad0f7e9STom Jones 	sc->sc_flags &= ~IWX_FLAG_USE_ICT;
28042ad0f7e9STom Jones 
28052ad0f7e9STom Jones 	iwx_disable_rx_dma(sc);
28062ad0f7e9STom Jones 	iwx_reset_rx_ring(sc, &sc->rxq);
28072ad0f7e9STom Jones 	for (i = 0; i < nitems(sc->txq); i++)
28082ad0f7e9STom Jones 		iwx_reset_tx_ring(sc, &sc->txq[i]);
28092ad0f7e9STom Jones #if 0
28102ad0f7e9STom Jones 	/* XXX-THJ: Tidy up BA state on stop */
28112ad0f7e9STom Jones 	for (i = 0; i < IEEE80211_NUM_TID; i++) {
28122ad0f7e9STom Jones 		struct ieee80211_tx_ba *ba = &ni->ni_tx_ba[i];
28132ad0f7e9STom Jones 		if (ba->ba_state != IEEE80211_BA_AGREED)
28142ad0f7e9STom Jones 			continue;
28152ad0f7e9STom Jones 		ieee80211_delba_request(ic, ni, 0, 1, i);
28162ad0f7e9STom Jones 	}
28172ad0f7e9STom Jones #endif
28182ad0f7e9STom Jones 	/* Make sure (redundant) we've released our request to stay awake */
28192ad0f7e9STom Jones 	IWX_CLRBITS(sc, IWX_CSR_GP_CNTRL,
28202ad0f7e9STom Jones 	    IWX_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
28212ad0f7e9STom Jones 	if (sc->sc_nic_locks > 0)
28222ad0f7e9STom Jones 		printf("%s: %d active NIC locks forcefully cleared\n",
28232ad0f7e9STom Jones 		    DEVNAME(sc), sc->sc_nic_locks);
28242ad0f7e9STom Jones 	sc->sc_nic_locks = 0;
28252ad0f7e9STom Jones 
28262ad0f7e9STom Jones 	/* Stop the device, and put it in low power state */
28272ad0f7e9STom Jones 	iwx_apm_stop(sc);
28282ad0f7e9STom Jones 
28292ad0f7e9STom Jones 	/* Reset the on-board processor. */
28302ad0f7e9STom Jones 	IWX_SETBITS(sc, IWX_CSR_RESET, IWX_CSR_RESET_REG_FLAG_SW_RESET);
28312ad0f7e9STom Jones 	DELAY(5000);
28322ad0f7e9STom Jones 
28332ad0f7e9STom Jones 	/*
28342ad0f7e9STom Jones 	 * Upon stop, the IVAR table gets erased, so msi-x won't
28352ad0f7e9STom Jones 	 * work. This causes a bug in RF-KILL flows, since the interrupt
28362ad0f7e9STom Jones 	 * that enables radio won't fire on the correct irq, and the
28372ad0f7e9STom Jones 	 * driver won't be able to handle the interrupt.
28382ad0f7e9STom Jones 	 * Configure the IVAR table again after reset.
28392ad0f7e9STom Jones 	 */
28402ad0f7e9STom Jones 	iwx_conf_msix_hw(sc, 1);
28412ad0f7e9STom Jones 
28422ad0f7e9STom Jones 	/*
28432ad0f7e9STom Jones 	 * Upon stop, the APM issues an interrupt if HW RF kill is set.
28442ad0f7e9STom Jones 	 * Clear the interrupt again.
28452ad0f7e9STom Jones 	 */
28462ad0f7e9STom Jones 	iwx_disable_interrupts(sc);
28472ad0f7e9STom Jones 
28482ad0f7e9STom Jones 	/* Even though we stop the HW we still want the RF kill interrupt. */
28492ad0f7e9STom Jones 	iwx_enable_rfkill_int(sc);
28502ad0f7e9STom Jones 	iwx_check_rfkill(sc);
28512ad0f7e9STom Jones 
28522ad0f7e9STom Jones 	iwx_prepare_card_hw(sc);
28532ad0f7e9STom Jones 
28542ad0f7e9STom Jones 	iwx_ctxt_info_free_paging(sc);
28552ad0f7e9STom Jones 	iwx_dma_contig_free(&sc->pnvm_dma);
28562ad0f7e9STom Jones }
28572ad0f7e9STom Jones 
28582ad0f7e9STom Jones static void
iwx_nic_config(struct iwx_softc * sc)28592ad0f7e9STom Jones iwx_nic_config(struct iwx_softc *sc)
28602ad0f7e9STom Jones {
28612ad0f7e9STom Jones 	uint8_t radio_cfg_type, radio_cfg_step, radio_cfg_dash;
28622ad0f7e9STom Jones 	uint32_t mask, val, reg_val = 0;
28632ad0f7e9STom Jones 
28642ad0f7e9STom Jones 	radio_cfg_type = (sc->sc_fw_phy_config & IWX_FW_PHY_CFG_RADIO_TYPE) >>
28652ad0f7e9STom Jones 	    IWX_FW_PHY_CFG_RADIO_TYPE_POS;
28662ad0f7e9STom Jones 	radio_cfg_step = (sc->sc_fw_phy_config & IWX_FW_PHY_CFG_RADIO_STEP) >>
28672ad0f7e9STom Jones 	    IWX_FW_PHY_CFG_RADIO_STEP_POS;
28682ad0f7e9STom Jones 	radio_cfg_dash = (sc->sc_fw_phy_config & IWX_FW_PHY_CFG_RADIO_DASH) >>
28692ad0f7e9STom Jones 	    IWX_FW_PHY_CFG_RADIO_DASH_POS;
28702ad0f7e9STom Jones 
28712ad0f7e9STom Jones 	reg_val |= IWX_CSR_HW_REV_STEP(sc->sc_hw_rev) <<
28722ad0f7e9STom Jones 	    IWX_CSR_HW_IF_CONFIG_REG_POS_MAC_STEP;
28732ad0f7e9STom Jones 	reg_val |= IWX_CSR_HW_REV_DASH(sc->sc_hw_rev) <<
28742ad0f7e9STom Jones 	    IWX_CSR_HW_IF_CONFIG_REG_POS_MAC_DASH;
28752ad0f7e9STom Jones 
28762ad0f7e9STom Jones 	/* radio configuration */
28772ad0f7e9STom Jones 	reg_val |= radio_cfg_type << IWX_CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE;
28782ad0f7e9STom Jones 	reg_val |= radio_cfg_step << IWX_CSR_HW_IF_CONFIG_REG_POS_PHY_STEP;
28792ad0f7e9STom Jones 	reg_val |= radio_cfg_dash << IWX_CSR_HW_IF_CONFIG_REG_POS_PHY_DASH;
28802ad0f7e9STom Jones 
28812ad0f7e9STom Jones 	mask = IWX_CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH |
28822ad0f7e9STom Jones 	    IWX_CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP |
28832ad0f7e9STom Jones 	    IWX_CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP |
28842ad0f7e9STom Jones 	    IWX_CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH |
28852ad0f7e9STom Jones 	    IWX_CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE |
28862ad0f7e9STom Jones 	    IWX_CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI |
28872ad0f7e9STom Jones 	    IWX_CSR_HW_IF_CONFIG_REG_BIT_MAC_SI;
28882ad0f7e9STom Jones 
28892ad0f7e9STom Jones 	val = IWX_READ(sc, IWX_CSR_HW_IF_CONFIG_REG);
28902ad0f7e9STom Jones 	val &= ~mask;
28912ad0f7e9STom Jones 	val |= reg_val;
28922ad0f7e9STom Jones 	IWX_WRITE(sc, IWX_CSR_HW_IF_CONFIG_REG, val);
28932ad0f7e9STom Jones }
28942ad0f7e9STom Jones 
28952ad0f7e9STom Jones static int
iwx_nic_rx_init(struct iwx_softc * sc)28962ad0f7e9STom Jones iwx_nic_rx_init(struct iwx_softc *sc)
28972ad0f7e9STom Jones {
28982ad0f7e9STom Jones 	IWX_WRITE_1(sc, IWX_CSR_INT_COALESCING, IWX_HOST_INT_TIMEOUT_DEF);
28992ad0f7e9STom Jones 
29002ad0f7e9STom Jones 	/*
29012ad0f7e9STom Jones 	 * We don't configure the RFH; the firmware will do that.
29022ad0f7e9STom Jones 	 * Rx descriptors are set when firmware sends an ALIVE interrupt.
29032ad0f7e9STom Jones 	 */
29042ad0f7e9STom Jones 	return 0;
29052ad0f7e9STom Jones }
29062ad0f7e9STom Jones 
29072ad0f7e9STom Jones static int
iwx_nic_init(struct iwx_softc * sc)29082ad0f7e9STom Jones iwx_nic_init(struct iwx_softc *sc)
29092ad0f7e9STom Jones {
29102ad0f7e9STom Jones 	int err;
29112ad0f7e9STom Jones 
29122ad0f7e9STom Jones 	iwx_apm_init(sc);
29132ad0f7e9STom Jones 	if (sc->sc_device_family < IWX_DEVICE_FAMILY_AX210)
29142ad0f7e9STom Jones 		iwx_nic_config(sc);
29152ad0f7e9STom Jones 
29162ad0f7e9STom Jones 	err = iwx_nic_rx_init(sc);
29172ad0f7e9STom Jones 	if (err)
29182ad0f7e9STom Jones 		return err;
29192ad0f7e9STom Jones 
29202ad0f7e9STom Jones 	IWX_SETBITS(sc, IWX_CSR_MAC_SHADOW_REG_CTRL, 0x800fffff);
29212ad0f7e9STom Jones 
29222ad0f7e9STom Jones 	return 0;
29232ad0f7e9STom Jones }
29242ad0f7e9STom Jones 
29252ad0f7e9STom Jones /* Map ieee80211_edca_ac categories to firmware Tx FIFO. */
29262ad0f7e9STom Jones const uint8_t iwx_ac_to_tx_fifo[] = {
29272ad0f7e9STom Jones 	IWX_GEN2_EDCA_TX_FIFO_BE,
29282ad0f7e9STom Jones 	IWX_GEN2_EDCA_TX_FIFO_BK,
29292ad0f7e9STom Jones 	IWX_GEN2_EDCA_TX_FIFO_VI,
29302ad0f7e9STom Jones 	IWX_GEN2_EDCA_TX_FIFO_VO,
29312ad0f7e9STom Jones };
29322ad0f7e9STom Jones 
29332ad0f7e9STom Jones static int
iwx_enable_txq(struct iwx_softc * sc,int sta_id,int qid,int tid,int num_slots)29342ad0f7e9STom Jones iwx_enable_txq(struct iwx_softc *sc, int sta_id, int qid, int tid,
29352ad0f7e9STom Jones     int num_slots)
29362ad0f7e9STom Jones {
29372ad0f7e9STom Jones 	struct iwx_rx_packet *pkt;
29382ad0f7e9STom Jones 	struct iwx_tx_queue_cfg_rsp *resp;
29392ad0f7e9STom Jones 	struct iwx_tx_queue_cfg_cmd cmd_v0;
29402ad0f7e9STom Jones 	struct iwx_scd_queue_cfg_cmd cmd_v3;
29412ad0f7e9STom Jones 	struct iwx_host_cmd hcmd = {
29422ad0f7e9STom Jones 		.flags = IWX_CMD_WANT_RESP,
29432ad0f7e9STom Jones 		.resp_pkt_len = sizeof(*pkt) + sizeof(*resp),
29442ad0f7e9STom Jones 	};
29452ad0f7e9STom Jones 	struct iwx_tx_ring *ring = &sc->txq[qid];
29462ad0f7e9STom Jones 	int err, fwqid, cmd_ver;
29472ad0f7e9STom Jones 	uint32_t wr_idx;
29482ad0f7e9STom Jones 	size_t resp_len;
29492ad0f7e9STom Jones 
29502ad0f7e9STom Jones 	DPRINTF(("%s: tid=%i\n", __func__, tid));
29512ad0f7e9STom Jones 	DPRINTF(("%s: qid=%i\n", __func__, qid));
29522ad0f7e9STom Jones 	iwx_reset_tx_ring(sc, ring);
29532ad0f7e9STom Jones 
29542ad0f7e9STom Jones 	cmd_ver = iwx_lookup_cmd_ver(sc, IWX_DATA_PATH_GROUP,
29552ad0f7e9STom Jones 	    IWX_SCD_QUEUE_CONFIG_CMD);
29562ad0f7e9STom Jones 	if (cmd_ver == 0 || cmd_ver == IWX_FW_CMD_VER_UNKNOWN) {
29572ad0f7e9STom Jones 		memset(&cmd_v0, 0, sizeof(cmd_v0));
29582ad0f7e9STom Jones 		cmd_v0.sta_id = sta_id;
29592ad0f7e9STom Jones 		cmd_v0.tid = tid;
29602ad0f7e9STom Jones 		cmd_v0.flags = htole16(IWX_TX_QUEUE_CFG_ENABLE_QUEUE);
29612ad0f7e9STom Jones 		cmd_v0.cb_size = htole32(IWX_TFD_QUEUE_CB_SIZE(num_slots));
29622ad0f7e9STom Jones 		cmd_v0.byte_cnt_addr = htole64(ring->bc_tbl.paddr);
29632ad0f7e9STom Jones 		cmd_v0.tfdq_addr = htole64(ring->desc_dma.paddr);
29642ad0f7e9STom Jones 		hcmd.id = IWX_SCD_QUEUE_CFG;
29652ad0f7e9STom Jones 		hcmd.data[0] = &cmd_v0;
29662ad0f7e9STom Jones 		hcmd.len[0] = sizeof(cmd_v0);
29672ad0f7e9STom Jones 	} else if (cmd_ver == 3) {
29682ad0f7e9STom Jones 		memset(&cmd_v3, 0, sizeof(cmd_v3));
29692ad0f7e9STom Jones 		cmd_v3.operation = htole32(IWX_SCD_QUEUE_ADD);
29702ad0f7e9STom Jones 		cmd_v3.u.add.tfdq_dram_addr = htole64(ring->desc_dma.paddr);
29712ad0f7e9STom Jones 		cmd_v3.u.add.bc_dram_addr = htole64(ring->bc_tbl.paddr);
29722ad0f7e9STom Jones 		cmd_v3.u.add.cb_size = htole32(IWX_TFD_QUEUE_CB_SIZE(num_slots));
29732ad0f7e9STom Jones 		cmd_v3.u.add.flags = htole32(0);
29742ad0f7e9STom Jones 		cmd_v3.u.add.sta_mask = htole32(1 << sta_id);
29752ad0f7e9STom Jones 		cmd_v3.u.add.tid = tid;
29762ad0f7e9STom Jones 		hcmd.id = IWX_WIDE_ID(IWX_DATA_PATH_GROUP,
29772ad0f7e9STom Jones 		    IWX_SCD_QUEUE_CONFIG_CMD);
29782ad0f7e9STom Jones 		hcmd.data[0] = &cmd_v3;
29792ad0f7e9STom Jones 		hcmd.len[0] = sizeof(cmd_v3);
29802ad0f7e9STom Jones 	} else {
29812ad0f7e9STom Jones 		printf("%s: unsupported SCD_QUEUE_CFG command version %d\n",
29822ad0f7e9STom Jones 		    DEVNAME(sc), cmd_ver);
29832ad0f7e9STom Jones 		return ENOTSUP;
29842ad0f7e9STom Jones 	}
29852ad0f7e9STom Jones 
29862ad0f7e9STom Jones 	err = iwx_send_cmd(sc, &hcmd);
29872ad0f7e9STom Jones 	if (err)
29882ad0f7e9STom Jones 		return err;
29892ad0f7e9STom Jones 
29902ad0f7e9STom Jones 	pkt = hcmd.resp_pkt;
29912ad0f7e9STom Jones 	if (!pkt || (pkt->hdr.flags & IWX_CMD_FAILED_MSK)) {
29922ad0f7e9STom Jones 		err = EIO;
29932ad0f7e9STom Jones 		goto out;
29942ad0f7e9STom Jones 	}
29952ad0f7e9STom Jones 
29962ad0f7e9STom Jones 	resp_len = iwx_rx_packet_payload_len(pkt);
29972ad0f7e9STom Jones 	if (resp_len != sizeof(*resp)) {
29982ad0f7e9STom Jones 		err = EIO;
29992ad0f7e9STom Jones 		goto out;
30002ad0f7e9STom Jones 	}
30012ad0f7e9STom Jones 
30022ad0f7e9STom Jones 	resp = (void *)pkt->data;
30032ad0f7e9STom Jones 	fwqid = le16toh(resp->queue_number);
30042ad0f7e9STom Jones 	wr_idx = le16toh(resp->write_pointer);
30052ad0f7e9STom Jones 
30062ad0f7e9STom Jones 	/* Unlike iwlwifi, we do not support dynamic queue ID assignment. */
30072ad0f7e9STom Jones 	if (fwqid != qid) {
30082ad0f7e9STom Jones 		DPRINTF(("%s: === fwqid != qid\n", __func__));
30092ad0f7e9STom Jones 		err = EIO;
30102ad0f7e9STom Jones 		goto out;
30112ad0f7e9STom Jones 	}
30122ad0f7e9STom Jones 
30132ad0f7e9STom Jones 	if (wr_idx != ring->cur_hw) {
30142ad0f7e9STom Jones 		DPRINTF(("%s: === (wr_idx != ring->cur_hw)\n", __func__));
30152ad0f7e9STom Jones 		err = EIO;
30162ad0f7e9STom Jones 		goto out;
30172ad0f7e9STom Jones 	}
30182ad0f7e9STom Jones 
30192ad0f7e9STom Jones 	sc->qenablemsk |= (1 << qid);
30202ad0f7e9STom Jones 	ring->tid = tid;
30212ad0f7e9STom Jones out:
30222ad0f7e9STom Jones 	iwx_free_resp(sc, &hcmd);
30232ad0f7e9STom Jones 	return err;
30242ad0f7e9STom Jones }
30252ad0f7e9STom Jones 
30262ad0f7e9STom Jones static int
iwx_disable_txq(struct iwx_softc * sc,int sta_id,int qid,uint8_t tid)30272ad0f7e9STom Jones iwx_disable_txq(struct iwx_softc *sc, int sta_id, int qid, uint8_t tid)
30282ad0f7e9STom Jones {
30292ad0f7e9STom Jones 	struct iwx_rx_packet *pkt;
30302ad0f7e9STom Jones 	struct iwx_tx_queue_cfg_rsp *resp;
30312ad0f7e9STom Jones 	struct iwx_tx_queue_cfg_cmd cmd_v0;
30322ad0f7e9STom Jones 	struct iwx_scd_queue_cfg_cmd cmd_v3;
30332ad0f7e9STom Jones 	struct iwx_host_cmd hcmd = {
30342ad0f7e9STom Jones 		.flags = IWX_CMD_WANT_RESP,
30352ad0f7e9STom Jones 		.resp_pkt_len = sizeof(*pkt) + sizeof(*resp),
30362ad0f7e9STom Jones 	};
30372ad0f7e9STom Jones 	struct iwx_tx_ring *ring = &sc->txq[qid];
30382ad0f7e9STom Jones 	int err, cmd_ver;
30392ad0f7e9STom Jones 
30402ad0f7e9STom Jones 	cmd_ver = iwx_lookup_cmd_ver(sc, IWX_DATA_PATH_GROUP,
30412ad0f7e9STom Jones 	    IWX_SCD_QUEUE_CONFIG_CMD);
30422ad0f7e9STom Jones 	if (cmd_ver == 0 || cmd_ver == IWX_FW_CMD_VER_UNKNOWN) {
30432ad0f7e9STom Jones 		memset(&cmd_v0, 0, sizeof(cmd_v0));
30442ad0f7e9STom Jones 		cmd_v0.sta_id = sta_id;
30452ad0f7e9STom Jones 		cmd_v0.tid = tid;
30462ad0f7e9STom Jones 		cmd_v0.flags = htole16(0); /* clear "queue enabled" flag */
30472ad0f7e9STom Jones 		cmd_v0.cb_size = htole32(0);
30482ad0f7e9STom Jones 		cmd_v0.byte_cnt_addr = htole64(0);
30492ad0f7e9STom Jones 		cmd_v0.tfdq_addr = htole64(0);
30502ad0f7e9STom Jones 		hcmd.id = IWX_SCD_QUEUE_CFG;
30512ad0f7e9STom Jones 		hcmd.data[0] = &cmd_v0;
30522ad0f7e9STom Jones 		hcmd.len[0] = sizeof(cmd_v0);
30532ad0f7e9STom Jones 	} else if (cmd_ver == 3) {
30542ad0f7e9STom Jones 		memset(&cmd_v3, 0, sizeof(cmd_v3));
30552ad0f7e9STom Jones 		cmd_v3.operation = htole32(IWX_SCD_QUEUE_REMOVE);
30562ad0f7e9STom Jones 		cmd_v3.u.remove.sta_mask = htole32(1 << sta_id);
30572ad0f7e9STom Jones 		cmd_v3.u.remove.tid = tid;
30582ad0f7e9STom Jones 		hcmd.id = IWX_WIDE_ID(IWX_DATA_PATH_GROUP,
30592ad0f7e9STom Jones 		    IWX_SCD_QUEUE_CONFIG_CMD);
30602ad0f7e9STom Jones 		hcmd.data[0] = &cmd_v3;
30612ad0f7e9STom Jones 		hcmd.len[0] = sizeof(cmd_v3);
30622ad0f7e9STom Jones 	} else {
30632ad0f7e9STom Jones 		printf("%s: unsupported SCD_QUEUE_CFG command version %d\n",
30642ad0f7e9STom Jones 		    DEVNAME(sc), cmd_ver);
30652ad0f7e9STom Jones 		return ENOTSUP;
30662ad0f7e9STom Jones 	}
30672ad0f7e9STom Jones 
30682ad0f7e9STom Jones 	err = iwx_send_cmd(sc, &hcmd);
30692ad0f7e9STom Jones 	if (err)
30702ad0f7e9STom Jones 		return err;
30712ad0f7e9STom Jones 
30722ad0f7e9STom Jones 	pkt = hcmd.resp_pkt;
30732ad0f7e9STom Jones 	if (!pkt || (pkt->hdr.flags & IWX_CMD_FAILED_MSK)) {
30742ad0f7e9STom Jones 		err = EIO;
30752ad0f7e9STom Jones 		goto out;
30762ad0f7e9STom Jones 	}
30772ad0f7e9STom Jones 
30782ad0f7e9STom Jones 	sc->qenablemsk &= ~(1 << qid);
30792ad0f7e9STom Jones 	iwx_reset_tx_ring(sc, ring);
30802ad0f7e9STom Jones out:
30812ad0f7e9STom Jones 	iwx_free_resp(sc, &hcmd);
30822ad0f7e9STom Jones 	return err;
30832ad0f7e9STom Jones }
30842ad0f7e9STom Jones 
30852ad0f7e9STom Jones static void
iwx_post_alive(struct iwx_softc * sc)30862ad0f7e9STom Jones iwx_post_alive(struct iwx_softc *sc)
30872ad0f7e9STom Jones {
30882ad0f7e9STom Jones 	int txcmd_ver;
30892ad0f7e9STom Jones 
30902ad0f7e9STom Jones 	iwx_ict_reset(sc);
30912ad0f7e9STom Jones 
30922ad0f7e9STom Jones 	txcmd_ver = iwx_lookup_notif_ver(sc, IWX_LONG_GROUP, IWX_TX_CMD) ;
30932ad0f7e9STom Jones 	if (txcmd_ver != IWX_FW_CMD_VER_UNKNOWN && txcmd_ver > 6)
30942ad0f7e9STom Jones 		sc->sc_rate_n_flags_version = 2;
30952ad0f7e9STom Jones 	else
30962ad0f7e9STom Jones 		sc->sc_rate_n_flags_version = 1;
30972ad0f7e9STom Jones 
30982ad0f7e9STom Jones 	txcmd_ver = iwx_lookup_cmd_ver(sc, IWX_LONG_GROUP, IWX_TX_CMD);
30992ad0f7e9STom Jones }
31002ad0f7e9STom Jones 
31012ad0f7e9STom Jones static int
iwx_schedule_session_protection(struct iwx_softc * sc,struct iwx_node * in,uint32_t duration_tu)31022ad0f7e9STom Jones iwx_schedule_session_protection(struct iwx_softc *sc, struct iwx_node *in,
31032ad0f7e9STom Jones     uint32_t duration_tu)
31042ad0f7e9STom Jones {
31052ad0f7e9STom Jones 
31062ad0f7e9STom Jones 	struct iwx_session_prot_cmd cmd = {
31072ad0f7e9STom Jones 		.id_and_color = htole32(IWX_FW_CMD_ID_AND_COLOR(in->in_id,
31082ad0f7e9STom Jones 		    in->in_color)),
31092ad0f7e9STom Jones 		.action = htole32(IWX_FW_CTXT_ACTION_ADD),
31102ad0f7e9STom Jones 		.conf_id = htole32(IWX_SESSION_PROTECT_CONF_ASSOC),
31112ad0f7e9STom Jones 		.duration_tu = htole32(duration_tu),
31122ad0f7e9STom Jones 	};
31132ad0f7e9STom Jones 	uint32_t cmd_id;
31142ad0f7e9STom Jones 	int err;
31152ad0f7e9STom Jones 
31162ad0f7e9STom Jones 	cmd_id = iwx_cmd_id(IWX_SESSION_PROTECTION_CMD, IWX_MAC_CONF_GROUP, 0);
31172ad0f7e9STom Jones 	err = iwx_send_cmd_pdu(sc, cmd_id, 0, sizeof(cmd), &cmd);
31182ad0f7e9STom Jones 	if (!err)
31192ad0f7e9STom Jones 		sc->sc_flags |= IWX_FLAG_TE_ACTIVE;
31202ad0f7e9STom Jones 	return err;
31212ad0f7e9STom Jones }
31222ad0f7e9STom Jones 
31232ad0f7e9STom Jones static void
iwx_unprotect_session(struct iwx_softc * sc,struct iwx_node * in)31242ad0f7e9STom Jones iwx_unprotect_session(struct iwx_softc *sc, struct iwx_node *in)
31252ad0f7e9STom Jones {
31262ad0f7e9STom Jones 	struct iwx_session_prot_cmd cmd = {
31272ad0f7e9STom Jones 		.id_and_color = htole32(IWX_FW_CMD_ID_AND_COLOR(in->in_id,
31282ad0f7e9STom Jones 		    in->in_color)),
31292ad0f7e9STom Jones 		.action = htole32(IWX_FW_CTXT_ACTION_REMOVE),
31302ad0f7e9STom Jones 		.conf_id = htole32(IWX_SESSION_PROTECT_CONF_ASSOC),
31312ad0f7e9STom Jones 		.duration_tu = 0,
31322ad0f7e9STom Jones 	};
31332ad0f7e9STom Jones 	uint32_t cmd_id;
31342ad0f7e9STom Jones 
31352ad0f7e9STom Jones 	/* Do nothing if the time event has already ended. */
31362ad0f7e9STom Jones 	if ((sc->sc_flags & IWX_FLAG_TE_ACTIVE) == 0)
31372ad0f7e9STom Jones 		return;
31382ad0f7e9STom Jones 
31392ad0f7e9STom Jones 	cmd_id = iwx_cmd_id(IWX_SESSION_PROTECTION_CMD, IWX_MAC_CONF_GROUP, 0);
31402ad0f7e9STom Jones 	if (iwx_send_cmd_pdu(sc, cmd_id, 0, sizeof(cmd), &cmd) == 0)
31412ad0f7e9STom Jones 		sc->sc_flags &= ~IWX_FLAG_TE_ACTIVE;
31422ad0f7e9STom Jones }
31432ad0f7e9STom Jones 
31442ad0f7e9STom Jones /*
31452ad0f7e9STom Jones  * NVM read access and content parsing.  We do not support
31462ad0f7e9STom Jones  * external NVM or writing NVM.
31472ad0f7e9STom Jones  */
31482ad0f7e9STom Jones 
31492ad0f7e9STom Jones static uint8_t
iwx_fw_valid_tx_ant(struct iwx_softc * sc)31502ad0f7e9STom Jones iwx_fw_valid_tx_ant(struct iwx_softc *sc)
31512ad0f7e9STom Jones {
31522ad0f7e9STom Jones 	uint8_t tx_ant;
31532ad0f7e9STom Jones 
31542ad0f7e9STom Jones 	tx_ant = ((sc->sc_fw_phy_config & IWX_FW_PHY_CFG_TX_CHAIN)
31552ad0f7e9STom Jones 	    >> IWX_FW_PHY_CFG_TX_CHAIN_POS);
31562ad0f7e9STom Jones 
31572ad0f7e9STom Jones 	if (sc->sc_nvm.valid_tx_ant)
31582ad0f7e9STom Jones 		tx_ant &= sc->sc_nvm.valid_tx_ant;
31592ad0f7e9STom Jones 
31602ad0f7e9STom Jones 	return tx_ant;
31612ad0f7e9STom Jones }
31622ad0f7e9STom Jones 
31632ad0f7e9STom Jones static uint8_t
iwx_fw_valid_rx_ant(struct iwx_softc * sc)31642ad0f7e9STom Jones iwx_fw_valid_rx_ant(struct iwx_softc *sc)
31652ad0f7e9STom Jones {
31662ad0f7e9STom Jones 	uint8_t rx_ant;
31672ad0f7e9STom Jones 
31682ad0f7e9STom Jones 	rx_ant = ((sc->sc_fw_phy_config & IWX_FW_PHY_CFG_RX_CHAIN)
31692ad0f7e9STom Jones 	    >> IWX_FW_PHY_CFG_RX_CHAIN_POS);
31702ad0f7e9STom Jones 
31712ad0f7e9STom Jones 	if (sc->sc_nvm.valid_rx_ant)
31722ad0f7e9STom Jones 		rx_ant &= sc->sc_nvm.valid_rx_ant;
31732ad0f7e9STom Jones 
31742ad0f7e9STom Jones 	return rx_ant;
31752ad0f7e9STom Jones }
31762ad0f7e9STom Jones 
31772ad0f7e9STom Jones static void
iwx_init_channel_map(struct ieee80211com * ic,int maxchans,int * nchans,struct ieee80211_channel chans[])31782ad0f7e9STom Jones iwx_init_channel_map(struct ieee80211com *ic, int maxchans, int *nchans,
31792ad0f7e9STom Jones     struct ieee80211_channel chans[])
31802ad0f7e9STom Jones {
31812ad0f7e9STom Jones 	struct iwx_softc *sc = ic->ic_softc;
31822ad0f7e9STom Jones 	struct iwx_nvm_data *data = &sc->sc_nvm;
31832ad0f7e9STom Jones 	uint8_t bands[IEEE80211_MODE_BYTES];
31842ad0f7e9STom Jones 	const uint8_t *nvm_channels;
31852ad0f7e9STom Jones 	uint32_t ch_flags;
31862ad0f7e9STom Jones 	int ch_idx, nchan;
31872ad0f7e9STom Jones 
31882ad0f7e9STom Jones 	if (sc->sc_uhb_supported) {
31892ad0f7e9STom Jones 		nchan = nitems(iwx_nvm_channels_uhb);
31902ad0f7e9STom Jones 		nvm_channels = iwx_nvm_channels_uhb;
31912ad0f7e9STom Jones 	} else {
31922ad0f7e9STom Jones 		nchan = nitems(iwx_nvm_channels_8000);
31932ad0f7e9STom Jones 		nvm_channels = iwx_nvm_channels_8000;
31942ad0f7e9STom Jones 	}
31952ad0f7e9STom Jones 
31962ad0f7e9STom Jones 	/* 2.4Ghz; 1-13: 11b/g channels. */
31972ad0f7e9STom Jones 	if (!data->sku_cap_band_24GHz_enable)
31982ad0f7e9STom Jones 		goto band_5;
31992ad0f7e9STom Jones 
32002ad0f7e9STom Jones 	memset(bands, 0, sizeof(bands));
32012ad0f7e9STom Jones 	setbit(bands, IEEE80211_MODE_11B);
32022ad0f7e9STom Jones 	setbit(bands, IEEE80211_MODE_11G);
32032ad0f7e9STom Jones 	setbit(bands, IEEE80211_MODE_11NG);
32042ad0f7e9STom Jones 	for (ch_idx = 0;
32052ad0f7e9STom Jones 	    ch_idx < IWX_NUM_2GHZ_CHANNELS && ch_idx < nchan;
32062ad0f7e9STom Jones 	    ch_idx++) {
32072ad0f7e9STom Jones 
32082ad0f7e9STom Jones 		uint32_t nflags = 0;
32092ad0f7e9STom Jones 		int cflags = 0;
32102ad0f7e9STom Jones 
32112ad0f7e9STom Jones 		if (sc->sc_rsp_vers == IWX_FBSD_RSP_V4) {
32122ad0f7e9STom Jones 			ch_flags = le32_to_cpup(
32132ad0f7e9STom Jones 			    sc->sc_rsp_info.rsp_v4.regulatory.channel_profile + ch_idx);
32142ad0f7e9STom Jones 		} else {
32152ad0f7e9STom Jones 			ch_flags = le16_to_cpup(
32162ad0f7e9STom Jones 			    sc->sc_rsp_info.rsp_v3.regulatory.channel_profile + ch_idx);
32172ad0f7e9STom Jones 		}
32182ad0f7e9STom Jones 		if ((ch_flags & IWX_NVM_CHANNEL_VALID) == 0)
32192ad0f7e9STom Jones 			continue;
32202ad0f7e9STom Jones 
32212ad0f7e9STom Jones 	          if ((ch_flags & IWX_NVM_CHANNEL_40MHZ) != 0)
32222ad0f7e9STom Jones                   cflags |= NET80211_CBW_FLAG_HT40;
32232ad0f7e9STom Jones 
32242ad0f7e9STom Jones 		/* XXX-BZ nflags RADAR/DFS/INDOOR */
32252ad0f7e9STom Jones 
32262ad0f7e9STom Jones 		/* error = */ ieee80211_add_channel_cbw(chans, maxchans, nchans,
32272ad0f7e9STom Jones 		nvm_channels[ch_idx],
32282ad0f7e9STom Jones 		ieee80211_ieee2mhz(nvm_channels[ch_idx], IEEE80211_CHAN_B),
32292ad0f7e9STom Jones 		/* max_power IWL_DEFAULT_MAX_TX_POWER */ 22,
32302ad0f7e9STom Jones 		nflags, bands, cflags);
32312ad0f7e9STom Jones 	}
32322ad0f7e9STom Jones 
32332ad0f7e9STom Jones band_5:
32342ad0f7e9STom Jones 	/* 5Ghz */
32352ad0f7e9STom Jones 	if (!data->sku_cap_band_52GHz_enable)
32362ad0f7e9STom Jones 		goto band_6;
32372ad0f7e9STom Jones 
32382ad0f7e9STom Jones 
32392ad0f7e9STom Jones 	memset(bands, 0, sizeof(bands));
32402ad0f7e9STom Jones 	setbit(bands, IEEE80211_MODE_11A);
32412ad0f7e9STom Jones 	setbit(bands, IEEE80211_MODE_11NA);
32422ad0f7e9STom Jones 	setbit(bands, IEEE80211_MODE_VHT_5GHZ);
32432ad0f7e9STom Jones 
32442ad0f7e9STom Jones 	for (ch_idx = IWX_NUM_2GHZ_CHANNELS;
32452ad0f7e9STom Jones 	    ch_idx < (IWX_NUM_2GHZ_CHANNELS + IWX_NUM_5GHZ_CHANNELS) && ch_idx < nchan;
32462ad0f7e9STom Jones 	    ch_idx++) {
32472ad0f7e9STom Jones 		uint32_t nflags = 0;
32482ad0f7e9STom Jones 		int cflags = 0;
32492ad0f7e9STom Jones 
32502ad0f7e9STom Jones 		if (sc->sc_rsp_vers == IWX_FBSD_RSP_V4)
32512ad0f7e9STom Jones 			ch_flags = le32_to_cpup(
32522ad0f7e9STom Jones 			    sc->sc_rsp_info.rsp_v4.regulatory.channel_profile + ch_idx);
32532ad0f7e9STom Jones 		else
32542ad0f7e9STom Jones 			ch_flags = le16_to_cpup(
32552ad0f7e9STom Jones 			    sc->sc_rsp_info.rsp_v3.regulatory.channel_profile + ch_idx);
32562ad0f7e9STom Jones 
32572ad0f7e9STom Jones 		if ((ch_flags & IWX_NVM_CHANNEL_VALID) == 0)
32582ad0f7e9STom Jones 		continue;
32592ad0f7e9STom Jones 
32602ad0f7e9STom Jones 		if ((ch_flags & IWX_NVM_CHANNEL_40MHZ) != 0)
32612ad0f7e9STom Jones 			cflags |= NET80211_CBW_FLAG_HT40;
32622ad0f7e9STom Jones 		if ((ch_flags & IWX_NVM_CHANNEL_80MHZ) != 0)
32632ad0f7e9STom Jones 			cflags |= NET80211_CBW_FLAG_VHT80;
32642ad0f7e9STom Jones 		if ((ch_flags & IWX_NVM_CHANNEL_160MHZ) != 0)
32652ad0f7e9STom Jones 			cflags |= NET80211_CBW_FLAG_VHT160;
32662ad0f7e9STom Jones 
32672ad0f7e9STom Jones 		/* XXX-BZ nflags RADAR/DFS/INDOOR */
32682ad0f7e9STom Jones 
32692ad0f7e9STom Jones 		/* error = */ ieee80211_add_channel_cbw(chans, maxchans, nchans,
32702ad0f7e9STom Jones 		nvm_channels[ch_idx],
32712ad0f7e9STom Jones 		ieee80211_ieee2mhz(nvm_channels[ch_idx], IEEE80211_CHAN_A),
32722ad0f7e9STom Jones 		/* max_power IWL_DEFAULT_MAX_TX_POWER */ 22,
32732ad0f7e9STom Jones 		nflags, bands, cflags);
32742ad0f7e9STom Jones 	}
32752ad0f7e9STom Jones band_6:
32762ad0f7e9STom Jones 	/* 6GHz one day ... */
32772ad0f7e9STom Jones 	return;
32782ad0f7e9STom Jones }
32792ad0f7e9STom Jones 
32802ad0f7e9STom Jones static int
iwx_mimo_enabled(struct iwx_softc * sc)32812ad0f7e9STom Jones iwx_mimo_enabled(struct iwx_softc *sc)
32822ad0f7e9STom Jones {
32832ad0f7e9STom Jones 
32842ad0f7e9STom Jones 	return !sc->sc_nvm.sku_cap_mimo_disable;
32852ad0f7e9STom Jones }
32862ad0f7e9STom Jones 
32872ad0f7e9STom Jones static void
iwx_init_reorder_buffer(struct iwx_reorder_buffer * reorder_buf,uint16_t ssn,uint16_t buf_size)32882ad0f7e9STom Jones iwx_init_reorder_buffer(struct iwx_reorder_buffer *reorder_buf,
32892ad0f7e9STom Jones     uint16_t ssn, uint16_t buf_size)
32902ad0f7e9STom Jones {
32912ad0f7e9STom Jones 	reorder_buf->head_sn = ssn;
32922ad0f7e9STom Jones 	reorder_buf->num_stored = 0;
32932ad0f7e9STom Jones 	reorder_buf->buf_size = buf_size;
32942ad0f7e9STom Jones 	reorder_buf->last_amsdu = 0;
32952ad0f7e9STom Jones 	reorder_buf->last_sub_index = 0;
32962ad0f7e9STom Jones 	reorder_buf->removed = 0;
32972ad0f7e9STom Jones 	reorder_buf->valid = 0;
32982ad0f7e9STom Jones 	reorder_buf->consec_oldsn_drops = 0;
32992ad0f7e9STom Jones 	reorder_buf->consec_oldsn_ampdu_gp2 = 0;
33002ad0f7e9STom Jones 	reorder_buf->consec_oldsn_prev_drop = 0;
33012ad0f7e9STom Jones }
33022ad0f7e9STom Jones 
33032ad0f7e9STom Jones static void
iwx_clear_reorder_buffer(struct iwx_softc * sc,struct iwx_rxba_data * rxba)33042ad0f7e9STom Jones iwx_clear_reorder_buffer(struct iwx_softc *sc, struct iwx_rxba_data *rxba)
33052ad0f7e9STom Jones {
33062ad0f7e9STom Jones 	struct iwx_reorder_buffer *reorder_buf = &rxba->reorder_buf;
33072ad0f7e9STom Jones 
33082ad0f7e9STom Jones 	reorder_buf->removed = 1;
33092ad0f7e9STom Jones 	rxba->baid = IWX_RX_REORDER_DATA_INVALID_BAID;
33102ad0f7e9STom Jones }
33112ad0f7e9STom Jones 
33122ad0f7e9STom Jones #define IWX_MAX_RX_BA_SESSIONS 16
33132ad0f7e9STom Jones 
33142ad0f7e9STom Jones static struct iwx_rxba_data *
iwx_find_rxba_data(struct iwx_softc * sc,uint8_t tid)33152ad0f7e9STom Jones iwx_find_rxba_data(struct iwx_softc *sc, uint8_t tid)
33162ad0f7e9STom Jones {
33172ad0f7e9STom Jones 	int i;
33182ad0f7e9STom Jones 
33192ad0f7e9STom Jones 	for (i = 0; i < nitems(sc->sc_rxba_data); i++) {
33202ad0f7e9STom Jones 		if (sc->sc_rxba_data[i].baid ==
33212ad0f7e9STom Jones 		    IWX_RX_REORDER_DATA_INVALID_BAID)
33222ad0f7e9STom Jones 			continue;
33232ad0f7e9STom Jones 		if (sc->sc_rxba_data[i].tid == tid)
33242ad0f7e9STom Jones 			return &sc->sc_rxba_data[i];
33252ad0f7e9STom Jones 	}
33262ad0f7e9STom Jones 
33272ad0f7e9STom Jones 	return NULL;
33282ad0f7e9STom Jones }
33292ad0f7e9STom Jones 
33302ad0f7e9STom Jones static int
iwx_sta_rx_agg_baid_cfg_cmd(struct iwx_softc * sc,struct ieee80211_node * ni,uint8_t tid,uint16_t ssn,uint16_t winsize,int timeout_val,int start,uint8_t * baid)33312ad0f7e9STom Jones iwx_sta_rx_agg_baid_cfg_cmd(struct iwx_softc *sc, struct ieee80211_node *ni,
33322ad0f7e9STom Jones     uint8_t tid, uint16_t ssn, uint16_t winsize, int timeout_val, int start,
33332ad0f7e9STom Jones     uint8_t *baid)
33342ad0f7e9STom Jones {
33352ad0f7e9STom Jones 	struct iwx_rx_baid_cfg_cmd cmd;
33362ad0f7e9STom Jones 	uint32_t new_baid = 0;
33372ad0f7e9STom Jones 	int err;
33382ad0f7e9STom Jones 
33392ad0f7e9STom Jones 	IWX_ASSERT_LOCKED(sc);
33402ad0f7e9STom Jones 
33412ad0f7e9STom Jones 	memset(&cmd, 0, sizeof(cmd));
33422ad0f7e9STom Jones 
33432ad0f7e9STom Jones 	if (start) {
33442ad0f7e9STom Jones 		cmd.action = IWX_RX_BAID_ACTION_ADD;
33452ad0f7e9STom Jones 		cmd.alloc.sta_id_mask = htole32(1 << IWX_STATION_ID);
33462ad0f7e9STom Jones 		cmd.alloc.tid = tid;
33472ad0f7e9STom Jones 		cmd.alloc.ssn = htole16(ssn);
33482ad0f7e9STom Jones 		cmd.alloc.win_size = htole16(winsize);
33492ad0f7e9STom Jones 	} else {
33502ad0f7e9STom Jones 		struct iwx_rxba_data *rxba;
33512ad0f7e9STom Jones 
33522ad0f7e9STom Jones 		rxba = iwx_find_rxba_data(sc, tid);
33532ad0f7e9STom Jones 		if (rxba == NULL)
33542ad0f7e9STom Jones 			return ENOENT;
33552ad0f7e9STom Jones 		*baid = rxba->baid;
33562ad0f7e9STom Jones 
33572ad0f7e9STom Jones 		cmd.action = IWX_RX_BAID_ACTION_REMOVE;
33582ad0f7e9STom Jones 		if (iwx_lookup_cmd_ver(sc, IWX_DATA_PATH_GROUP,
33592ad0f7e9STom Jones 		    IWX_RX_BAID_ALLOCATION_CONFIG_CMD) == 1) {
33602ad0f7e9STom Jones 			cmd.remove_v1.baid = rxba->baid;
33612ad0f7e9STom Jones 		} else {
33622ad0f7e9STom Jones 			cmd.remove.sta_id_mask = htole32(1 << IWX_STATION_ID);
33632ad0f7e9STom Jones 			cmd.remove.tid = tid;
33642ad0f7e9STom Jones 		}
33652ad0f7e9STom Jones 	}
33662ad0f7e9STom Jones 
33672ad0f7e9STom Jones 	err = iwx_send_cmd_pdu_status(sc, IWX_WIDE_ID(IWX_DATA_PATH_GROUP,
33682ad0f7e9STom Jones 	    IWX_RX_BAID_ALLOCATION_CONFIG_CMD), sizeof(cmd), &cmd, &new_baid);
33692ad0f7e9STom Jones 	if (err)
33702ad0f7e9STom Jones 		return err;
33712ad0f7e9STom Jones 
33722ad0f7e9STom Jones 	if (start) {
33732ad0f7e9STom Jones 		if (new_baid >= nitems(sc->sc_rxba_data))
33742ad0f7e9STom Jones 			return ERANGE;
33752ad0f7e9STom Jones 		*baid = new_baid;
33762ad0f7e9STom Jones 	}
33772ad0f7e9STom Jones 
33782ad0f7e9STom Jones 	return 0;
33792ad0f7e9STom Jones }
33802ad0f7e9STom Jones 
33812ad0f7e9STom Jones static void
iwx_sta_rx_agg(struct iwx_softc * sc,struct ieee80211_node * ni,uint8_t tid,uint16_t ssn,uint16_t winsize,int timeout_val,int start)33822ad0f7e9STom Jones iwx_sta_rx_agg(struct iwx_softc *sc, struct ieee80211_node *ni, uint8_t tid,
33832ad0f7e9STom Jones     uint16_t ssn, uint16_t winsize, int timeout_val, int start)
33842ad0f7e9STom Jones {
33852ad0f7e9STom Jones 	int err;
33862ad0f7e9STom Jones 	struct iwx_rxba_data *rxba = NULL;
33872ad0f7e9STom Jones 	uint8_t baid = 0;
33882ad0f7e9STom Jones 
33892ad0f7e9STom Jones 	if (start && sc->sc_rx_ba_sessions >= IWX_MAX_RX_BA_SESSIONS) {
33902ad0f7e9STom Jones 		return;
33912ad0f7e9STom Jones 	}
33922ad0f7e9STom Jones 
33932ad0f7e9STom Jones 	if (isset(sc->sc_enabled_capa, IWX_UCODE_TLV_CAPA_BAID_ML_SUPPORT)) {
33942ad0f7e9STom Jones 		err = iwx_sta_rx_agg_baid_cfg_cmd(sc, ni, tid, ssn, winsize,
33952ad0f7e9STom Jones 		    timeout_val, start, &baid);
33962ad0f7e9STom Jones 	} else {
33972ad0f7e9STom Jones 		panic("sta_rx_agg unsupported hw");
33982ad0f7e9STom Jones 	}
33992ad0f7e9STom Jones 	if (err) {
34002ad0f7e9STom Jones 		DPRINTF(("%s: iwx_sta_rx_agg_sta err=%i\n", __func__, err));
34012ad0f7e9STom Jones 		return;
34022ad0f7e9STom Jones 	} else
34032ad0f7e9STom Jones 		DPRINTF(("%s: iwx_sta_rx_agg_sta success\n", __func__));
34042ad0f7e9STom Jones 
34052ad0f7e9STom Jones 	rxba = &sc->sc_rxba_data[baid];
34062ad0f7e9STom Jones 
34072ad0f7e9STom Jones 	/* Deaggregation is done in hardware. */
34082ad0f7e9STom Jones 	if (start) {
34092ad0f7e9STom Jones 		if (rxba->baid != IWX_RX_REORDER_DATA_INVALID_BAID) {
34102ad0f7e9STom Jones 			return;
34112ad0f7e9STom Jones 		}
34122ad0f7e9STom Jones 		rxba->sta_id = IWX_STATION_ID;
34132ad0f7e9STom Jones 		rxba->tid = tid;
34142ad0f7e9STom Jones 		rxba->baid = baid;
34152ad0f7e9STom Jones 		rxba->timeout = timeout_val;
34162ad0f7e9STom Jones 		getmicrouptime(&rxba->last_rx);
34172ad0f7e9STom Jones 		iwx_init_reorder_buffer(&rxba->reorder_buf, ssn,
34182ad0f7e9STom Jones 		    winsize);
34192ad0f7e9STom Jones 		if (timeout_val != 0) {
34202ad0f7e9STom Jones 			DPRINTF(("%s: timeout_val != 0\n", __func__));
34212ad0f7e9STom Jones 			return;
34222ad0f7e9STom Jones 		}
34232ad0f7e9STom Jones 	} else
34242ad0f7e9STom Jones 		iwx_clear_reorder_buffer(sc, rxba);
34252ad0f7e9STom Jones 
34262ad0f7e9STom Jones 	if (start) {
34272ad0f7e9STom Jones 		sc->sc_rx_ba_sessions++;
34282ad0f7e9STom Jones 	} else if (sc->sc_rx_ba_sessions > 0)
34292ad0f7e9STom Jones 		sc->sc_rx_ba_sessions--;
34302ad0f7e9STom Jones }
34312ad0f7e9STom Jones 
34322ad0f7e9STom Jones static void
iwx_sta_tx_agg_start(struct iwx_softc * sc,struct ieee80211_node * ni,uint8_t tid)34332ad0f7e9STom Jones iwx_sta_tx_agg_start(struct iwx_softc *sc, struct ieee80211_node *ni,
34342ad0f7e9STom Jones     uint8_t tid)
34352ad0f7e9STom Jones {
34362ad0f7e9STom Jones 	int err, qid;
34372ad0f7e9STom Jones 
34382ad0f7e9STom Jones 	qid = sc->aggqid[tid];
34392ad0f7e9STom Jones 	if (qid == 0) {
34402ad0f7e9STom Jones 		/* Firmware should pick the next unused Tx queue. */
34412ad0f7e9STom Jones 		qid = fls(sc->qenablemsk);
34422ad0f7e9STom Jones 	}
34432ad0f7e9STom Jones 
34442ad0f7e9STom Jones 	DPRINTF(("%s: qid=%i\n", __func__, qid));
34452ad0f7e9STom Jones 
34462ad0f7e9STom Jones 	/*
34472ad0f7e9STom Jones 	 * Simply enable the queue.
34482ad0f7e9STom Jones 	 * Firmware handles Tx Ba session setup and teardown.
34492ad0f7e9STom Jones 	 */
34502ad0f7e9STom Jones 	if ((sc->qenablemsk & (1 << qid)) == 0) {
34512ad0f7e9STom Jones 		if (!iwx_nic_lock(sc)) {
34522ad0f7e9STom Jones 			return;
34532ad0f7e9STom Jones 		}
34542ad0f7e9STom Jones 		err = iwx_enable_txq(sc, IWX_STATION_ID, qid, tid,
34552ad0f7e9STom Jones 		    IWX_TX_RING_COUNT);
34562ad0f7e9STom Jones 		iwx_nic_unlock(sc);
34572ad0f7e9STom Jones 		if (err) {
34582ad0f7e9STom Jones 			printf("%s: could not enable Tx queue %d "
34592ad0f7e9STom Jones 			    "(error %d)\n", DEVNAME(sc), qid, err);
34602ad0f7e9STom Jones 			return;
34612ad0f7e9STom Jones 		}
34622ad0f7e9STom Jones 	}
34632ad0f7e9STom Jones 	ni->ni_tx_ampdu[tid].txa_flags = IEEE80211_AGGR_RUNNING;
34642ad0f7e9STom Jones 	DPRINTF(("%s: will set sc->aggqid[%i]=%i\n", __func__, tid, qid));
34652ad0f7e9STom Jones 	sc->aggqid[tid] = qid;
34662ad0f7e9STom Jones }
34672ad0f7e9STom Jones 
34682ad0f7e9STom Jones static void
iwx_ba_rx_task(void * arg,int npending __unused)34692ad0f7e9STom Jones iwx_ba_rx_task(void *arg, int npending __unused)
34702ad0f7e9STom Jones {
34712ad0f7e9STom Jones 	struct iwx_softc *sc = arg;
34722ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
34732ad0f7e9STom Jones 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
34742ad0f7e9STom Jones 	struct ieee80211_node *ni = vap->iv_bss;
34752ad0f7e9STom Jones 	int tid;
34762ad0f7e9STom Jones 
34772ad0f7e9STom Jones 	IWX_LOCK(sc);
34782ad0f7e9STom Jones 	for (tid = 0; tid < IWX_MAX_TID_COUNT; tid++) {
34792ad0f7e9STom Jones 		if (sc->sc_flags & IWX_FLAG_SHUTDOWN)
34802ad0f7e9STom Jones 			break;
34812ad0f7e9STom Jones 		if (sc->ba_rx.start_tidmask & (1 << tid)) {
34822ad0f7e9STom Jones 			struct iwx_rx_ba *ba = &sc->ni_rx_ba[tid];
34832ad0f7e9STom Jones 			DPRINTF(("%s: ba->ba_flags=%x\n", __func__,
34842ad0f7e9STom Jones 			    ba->ba_flags));
34852ad0f7e9STom Jones 			if (ba->ba_flags == IWX_BA_DONE) {
34862ad0f7e9STom Jones 				DPRINTF(("%s: ampdu for tid %i already added\n",
34872ad0f7e9STom Jones 				    __func__, tid));
34882ad0f7e9STom Jones 				break;
34892ad0f7e9STom Jones 			}
34902ad0f7e9STom Jones 
34912ad0f7e9STom Jones 			DPRINTF(("%s: ampdu rx start for tid %i\n", __func__,
34922ad0f7e9STom Jones 			    tid));
34932ad0f7e9STom Jones 			iwx_sta_rx_agg(sc, ni, tid, ba->ba_winstart,
34942ad0f7e9STom Jones 			    ba->ba_winsize, ba->ba_timeout_val, 1);
34952ad0f7e9STom Jones 			sc->ba_rx.start_tidmask &= ~(1 << tid);
34962ad0f7e9STom Jones 			ba->ba_flags = IWX_BA_DONE;
34972ad0f7e9STom Jones 		} else if (sc->ba_rx.stop_tidmask & (1 << tid)) {
34982ad0f7e9STom Jones 			iwx_sta_rx_agg(sc, ni, tid, 0, 0, 0, 0);
34992ad0f7e9STom Jones 			sc->ba_rx.stop_tidmask &= ~(1 << tid);
35002ad0f7e9STom Jones 		}
35012ad0f7e9STom Jones 	}
35022ad0f7e9STom Jones 	IWX_UNLOCK(sc);
35032ad0f7e9STom Jones }
35042ad0f7e9STom Jones 
35052ad0f7e9STom Jones static void
iwx_ba_tx_task(void * arg,int npending __unused)35062ad0f7e9STom Jones iwx_ba_tx_task(void *arg, int npending __unused)
35072ad0f7e9STom Jones {
35082ad0f7e9STom Jones 	struct iwx_softc *sc = arg;
35092ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
35102ad0f7e9STom Jones 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
35112ad0f7e9STom Jones 	struct ieee80211_node *ni = vap->iv_bss;
35122ad0f7e9STom Jones 	int tid;
35132ad0f7e9STom Jones 
35142ad0f7e9STom Jones 	IWX_LOCK(sc);
35152ad0f7e9STom Jones 	for (tid = 0; tid < IWX_MAX_TID_COUNT; tid++) {
35162ad0f7e9STom Jones 		if (sc->sc_flags & IWX_FLAG_SHUTDOWN)
35172ad0f7e9STom Jones 			break;
35182ad0f7e9STom Jones 		if (sc->ba_tx.start_tidmask & (1 << tid)) {
35192ad0f7e9STom Jones 			DPRINTF(("%s: ampdu tx start for tid %i\n", __func__,
35202ad0f7e9STom Jones 			    tid));
35212ad0f7e9STom Jones 			iwx_sta_tx_agg_start(sc, ni, tid);
35222ad0f7e9STom Jones 			sc->ba_tx.start_tidmask &= ~(1 << tid);
35232ad0f7e9STom Jones 			sc->sc_flags |= IWX_FLAG_AMPDUTX;
35242ad0f7e9STom Jones 		}
35252ad0f7e9STom Jones 	}
35262ad0f7e9STom Jones 
35272ad0f7e9STom Jones 	IWX_UNLOCK(sc);
35282ad0f7e9STom Jones }
35292ad0f7e9STom Jones 
35302ad0f7e9STom Jones static void
iwx_set_mac_addr_from_csr(struct iwx_softc * sc,struct iwx_nvm_data * data)35312ad0f7e9STom Jones iwx_set_mac_addr_from_csr(struct iwx_softc *sc, struct iwx_nvm_data *data)
35322ad0f7e9STom Jones {
35332ad0f7e9STom Jones 	uint32_t mac_addr0, mac_addr1;
35342ad0f7e9STom Jones 
35352ad0f7e9STom Jones 	memset(data->hw_addr, 0, sizeof(data->hw_addr));
35362ad0f7e9STom Jones 
35372ad0f7e9STom Jones 	if (!iwx_nic_lock(sc))
35382ad0f7e9STom Jones 		return;
35392ad0f7e9STom Jones 
35402ad0f7e9STom Jones 	mac_addr0 = htole32(IWX_READ(sc, IWX_CSR_MAC_ADDR0_STRAP(sc)));
35412ad0f7e9STom Jones 	mac_addr1 = htole32(IWX_READ(sc, IWX_CSR_MAC_ADDR1_STRAP(sc)));
35422ad0f7e9STom Jones 
35432ad0f7e9STom Jones 	iwx_flip_hw_address(mac_addr0, mac_addr1, data->hw_addr);
35442ad0f7e9STom Jones 
35452ad0f7e9STom Jones 	/* If OEM fused a valid address, use it instead of the one in OTP. */
35462ad0f7e9STom Jones 	if (iwx_is_valid_mac_addr(data->hw_addr)) {
35472ad0f7e9STom Jones 		iwx_nic_unlock(sc);
35482ad0f7e9STom Jones 		return;
35492ad0f7e9STom Jones 	}
35502ad0f7e9STom Jones 
35512ad0f7e9STom Jones 	mac_addr0 = htole32(IWX_READ(sc, IWX_CSR_MAC_ADDR0_OTP(sc)));
35522ad0f7e9STom Jones 	mac_addr1 = htole32(IWX_READ(sc, IWX_CSR_MAC_ADDR1_OTP(sc)));
35532ad0f7e9STom Jones 
35542ad0f7e9STom Jones 	iwx_flip_hw_address(mac_addr0, mac_addr1, data->hw_addr);
35552ad0f7e9STom Jones 
35562ad0f7e9STom Jones 	iwx_nic_unlock(sc);
35572ad0f7e9STom Jones }
35582ad0f7e9STom Jones 
35592ad0f7e9STom Jones static int
iwx_is_valid_mac_addr(const uint8_t * addr)35602ad0f7e9STom Jones iwx_is_valid_mac_addr(const uint8_t *addr)
35612ad0f7e9STom Jones {
35622ad0f7e9STom Jones 	static const uint8_t reserved_mac[] = {
35632ad0f7e9STom Jones 		0x02, 0xcc, 0xaa, 0xff, 0xee, 0x00
35642ad0f7e9STom Jones 	};
35652ad0f7e9STom Jones 
35662ad0f7e9STom Jones 	return (memcmp(reserved_mac, addr, ETHER_ADDR_LEN) != 0 &&
35672ad0f7e9STom Jones 	    memcmp(etherbroadcastaddr, addr, sizeof(etherbroadcastaddr)) != 0 &&
35682ad0f7e9STom Jones 	    memcmp(etheranyaddr, addr, sizeof(etheranyaddr)) != 0 &&
35692ad0f7e9STom Jones 	    !ETHER_IS_MULTICAST(addr));
35702ad0f7e9STom Jones }
35712ad0f7e9STom Jones 
35722ad0f7e9STom Jones static void
iwx_flip_hw_address(uint32_t mac_addr0,uint32_t mac_addr1,uint8_t * dest)35732ad0f7e9STom Jones iwx_flip_hw_address(uint32_t mac_addr0, uint32_t mac_addr1, uint8_t *dest)
35742ad0f7e9STom Jones {
35752ad0f7e9STom Jones 	const uint8_t *hw_addr;
35762ad0f7e9STom Jones 
35772ad0f7e9STom Jones 	hw_addr = (const uint8_t *)&mac_addr0;
35782ad0f7e9STom Jones 	dest[0] = hw_addr[3];
35792ad0f7e9STom Jones 	dest[1] = hw_addr[2];
35802ad0f7e9STom Jones 	dest[2] = hw_addr[1];
35812ad0f7e9STom Jones 	dest[3] = hw_addr[0];
35822ad0f7e9STom Jones 
35832ad0f7e9STom Jones 	hw_addr = (const uint8_t *)&mac_addr1;
35842ad0f7e9STom Jones 	dest[4] = hw_addr[1];
35852ad0f7e9STom Jones 	dest[5] = hw_addr[0];
35862ad0f7e9STom Jones }
35872ad0f7e9STom Jones 
35882ad0f7e9STom Jones static int
iwx_nvm_get(struct iwx_softc * sc)35892ad0f7e9STom Jones iwx_nvm_get(struct iwx_softc *sc)
35902ad0f7e9STom Jones {
35912ad0f7e9STom Jones 	struct iwx_nvm_get_info cmd = {};
35922ad0f7e9STom Jones 	struct iwx_nvm_data *nvm = &sc->sc_nvm;
35932ad0f7e9STom Jones 	struct iwx_host_cmd hcmd = {
35942ad0f7e9STom Jones 		.flags = IWX_CMD_WANT_RESP | IWX_CMD_SEND_IN_RFKILL,
35952ad0f7e9STom Jones 		.data = { &cmd, },
35962ad0f7e9STom Jones 		.len = { sizeof(cmd) },
35972ad0f7e9STom Jones 		.id = IWX_WIDE_ID(IWX_REGULATORY_AND_NVM_GROUP,
35982ad0f7e9STom Jones 		    IWX_NVM_GET_INFO)
35992ad0f7e9STom Jones 	};
36002ad0f7e9STom Jones 	int err = 0;
36012ad0f7e9STom Jones 	uint32_t mac_flags;
36022ad0f7e9STom Jones 	/*
36032ad0f7e9STom Jones 	 * All the values in iwx_nvm_get_info_rsp v4 are the same as
36042ad0f7e9STom Jones 	 * in v3, except for the channel profile part of the
36052ad0f7e9STom Jones 	 * regulatory.  So we can just access the new struct, with the
36062ad0f7e9STom Jones 	 * exception of the latter.
36072ad0f7e9STom Jones 	 */
36082ad0f7e9STom Jones 	struct iwx_nvm_get_info_rsp *rsp;
36092ad0f7e9STom Jones 	struct iwx_nvm_get_info_rsp_v3 *rsp_v3;
36102ad0f7e9STom Jones 	int v4 = isset(sc->sc_ucode_api, IWX_UCODE_TLV_API_REGULATORY_NVM_INFO);
36112ad0f7e9STom Jones 	size_t resp_len = v4 ? sizeof(*rsp) : sizeof(*rsp_v3);
36122ad0f7e9STom Jones 
36132ad0f7e9STom Jones 	hcmd.resp_pkt_len = sizeof(struct iwx_rx_packet) + resp_len;
36142ad0f7e9STom Jones 	err = iwx_send_cmd(sc, &hcmd);
36152ad0f7e9STom Jones 	if (err) {
36162ad0f7e9STom Jones 		printf("%s: failed to send cmd (error %d)", __func__, err);
36172ad0f7e9STom Jones 		return err;
36182ad0f7e9STom Jones 	}
36192ad0f7e9STom Jones 
36202ad0f7e9STom Jones 	if (iwx_rx_packet_payload_len(hcmd.resp_pkt) != resp_len) {
36212ad0f7e9STom Jones 		printf("%s: iwx_rx_packet_payload_len=%d\n", __func__,
36222ad0f7e9STom Jones 		    iwx_rx_packet_payload_len(hcmd.resp_pkt));
36232ad0f7e9STom Jones 		printf("%s: resp_len=%zu\n", __func__, resp_len);
36242ad0f7e9STom Jones 		err = EIO;
36252ad0f7e9STom Jones 		goto out;
36262ad0f7e9STom Jones 	}
36272ad0f7e9STom Jones 
36282ad0f7e9STom Jones 	memset(nvm, 0, sizeof(*nvm));
36292ad0f7e9STom Jones 
36302ad0f7e9STom Jones 	iwx_set_mac_addr_from_csr(sc, nvm);
36312ad0f7e9STom Jones 	if (!iwx_is_valid_mac_addr(nvm->hw_addr)) {
36322ad0f7e9STom Jones 		printf("%s: no valid mac address was found\n", DEVNAME(sc));
36332ad0f7e9STom Jones 		err = EINVAL;
36342ad0f7e9STom Jones 		goto out;
36352ad0f7e9STom Jones 	}
36362ad0f7e9STom Jones 
36372ad0f7e9STom Jones 	rsp = (void *)hcmd.resp_pkt->data;
36382ad0f7e9STom Jones 
36392ad0f7e9STom Jones 	/* Initialize general data */
36402ad0f7e9STom Jones 	nvm->nvm_version = le16toh(rsp->general.nvm_version);
36412ad0f7e9STom Jones 	nvm->n_hw_addrs = rsp->general.n_hw_addrs;
36422ad0f7e9STom Jones 
36432ad0f7e9STom Jones 	/* Initialize MAC sku data */
36442ad0f7e9STom Jones 	mac_flags = le32toh(rsp->mac_sku.mac_sku_flags);
36452ad0f7e9STom Jones 	nvm->sku_cap_11ac_enable =
36462ad0f7e9STom Jones 		!!(mac_flags & IWX_NVM_MAC_SKU_FLAGS_802_11AC_ENABLED);
36472ad0f7e9STom Jones 	nvm->sku_cap_11n_enable =
36482ad0f7e9STom Jones 		!!(mac_flags & IWX_NVM_MAC_SKU_FLAGS_802_11N_ENABLED);
36492ad0f7e9STom Jones 	nvm->sku_cap_11ax_enable =
36502ad0f7e9STom Jones 		!!(mac_flags & IWX_NVM_MAC_SKU_FLAGS_802_11AX_ENABLED);
36512ad0f7e9STom Jones 	nvm->sku_cap_band_24GHz_enable =
36522ad0f7e9STom Jones 		!!(mac_flags & IWX_NVM_MAC_SKU_FLAGS_BAND_2_4_ENABLED);
36532ad0f7e9STom Jones 	nvm->sku_cap_band_52GHz_enable =
36542ad0f7e9STom Jones 		!!(mac_flags & IWX_NVM_MAC_SKU_FLAGS_BAND_5_2_ENABLED);
36552ad0f7e9STom Jones 	nvm->sku_cap_mimo_disable =
36562ad0f7e9STom Jones 		!!(mac_flags & IWX_NVM_MAC_SKU_FLAGS_MIMO_DISABLED);
36572ad0f7e9STom Jones 
36582ad0f7e9STom Jones 	/* Initialize PHY sku data */
36592ad0f7e9STom Jones 	nvm->valid_tx_ant = (uint8_t)le32toh(rsp->phy_sku.tx_chains);
36602ad0f7e9STom Jones 	nvm->valid_rx_ant = (uint8_t)le32toh(rsp->phy_sku.rx_chains);
36612ad0f7e9STom Jones 
36622ad0f7e9STom Jones 	if (le32toh(rsp->regulatory.lar_enabled) &&
36632ad0f7e9STom Jones 	    isset(sc->sc_enabled_capa, IWX_UCODE_TLV_CAPA_LAR_SUPPORT)) {
36642ad0f7e9STom Jones 		nvm->lar_enabled = 1;
36652ad0f7e9STom Jones 	}
36662ad0f7e9STom Jones 
36672ad0f7e9STom Jones 	memcpy(&sc->sc_rsp_info, rsp, resp_len);
36682ad0f7e9STom Jones 	if (v4) {
36692ad0f7e9STom Jones 		sc->sc_rsp_vers = IWX_FBSD_RSP_V4;
36702ad0f7e9STom Jones 	} else {
36712ad0f7e9STom Jones 		sc->sc_rsp_vers = IWX_FBSD_RSP_V3;
36722ad0f7e9STom Jones 	}
36732ad0f7e9STom Jones out:
36742ad0f7e9STom Jones 	iwx_free_resp(sc, &hcmd);
36752ad0f7e9STom Jones 	return err;
36762ad0f7e9STom Jones }
36772ad0f7e9STom Jones 
36782ad0f7e9STom Jones static int
iwx_load_firmware(struct iwx_softc * sc)36792ad0f7e9STom Jones iwx_load_firmware(struct iwx_softc *sc)
36802ad0f7e9STom Jones {
36812ad0f7e9STom Jones 	struct iwx_fw_sects *fws;
36822ad0f7e9STom Jones 	int err;
36832ad0f7e9STom Jones 
36842ad0f7e9STom Jones 	IWX_ASSERT_LOCKED(sc)
36852ad0f7e9STom Jones 
36862ad0f7e9STom Jones 	sc->sc_uc.uc_intr = 0;
36872ad0f7e9STom Jones 	sc->sc_uc.uc_ok = 0;
36882ad0f7e9STom Jones 
36892ad0f7e9STom Jones 	fws = &sc->sc_fw.fw_sects[IWX_UCODE_TYPE_REGULAR];
36902ad0f7e9STom Jones 	if (sc->sc_device_family >= IWX_DEVICE_FAMILY_AX210)
36912ad0f7e9STom Jones 		err = iwx_ctxt_info_gen3_init(sc, fws);
36922ad0f7e9STom Jones 	else
36932ad0f7e9STom Jones 		err = iwx_ctxt_info_init(sc, fws);
36942ad0f7e9STom Jones 	if (err) {
36952ad0f7e9STom Jones 		printf("%s: could not init context info\n", DEVNAME(sc));
36962ad0f7e9STom Jones 		return err;
36972ad0f7e9STom Jones 	}
36982ad0f7e9STom Jones 
36992ad0f7e9STom Jones 	/* wait for the firmware to load */
37002ad0f7e9STom Jones 	err = msleep(&sc->sc_uc, &sc->sc_mtx, 0, "iwxuc", hz);
37012ad0f7e9STom Jones 	if (err || !sc->sc_uc.uc_ok) {
370228345b17STom Jones 		printf("%s: firmware upload failed, %d\n", DEVNAME(sc), err);
37032ad0f7e9STom Jones 		iwx_ctxt_info_free_paging(sc);
37042ad0f7e9STom Jones 	}
37052ad0f7e9STom Jones 
37062ad0f7e9STom Jones 	iwx_dma_contig_free(&sc->iml_dma);
37072ad0f7e9STom Jones 	iwx_ctxt_info_free_fw_img(sc);
37082ad0f7e9STom Jones 
37092ad0f7e9STom Jones 	if (!sc->sc_uc.uc_ok)
37102ad0f7e9STom Jones 		return EINVAL;
37112ad0f7e9STom Jones 
37122ad0f7e9STom Jones 	return err;
37132ad0f7e9STom Jones }
37142ad0f7e9STom Jones 
37152ad0f7e9STom Jones static int
iwx_start_fw(struct iwx_softc * sc)37162ad0f7e9STom Jones iwx_start_fw(struct iwx_softc *sc)
37172ad0f7e9STom Jones {
37182ad0f7e9STom Jones 	int err;
37192ad0f7e9STom Jones 
37202ad0f7e9STom Jones 	IWX_WRITE(sc, IWX_CSR_INT, ~0);
37212ad0f7e9STom Jones 
37222ad0f7e9STom Jones 	iwx_disable_interrupts(sc);
37232ad0f7e9STom Jones 
37242ad0f7e9STom Jones 	/* make sure rfkill handshake bits are cleared */
37252ad0f7e9STom Jones 	IWX_WRITE(sc, IWX_CSR_UCODE_DRV_GP1_CLR, IWX_CSR_UCODE_SW_BIT_RFKILL);
37262ad0f7e9STom Jones 	IWX_WRITE(sc, IWX_CSR_UCODE_DRV_GP1_CLR,
37272ad0f7e9STom Jones 	    IWX_CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
37282ad0f7e9STom Jones 
37292ad0f7e9STom Jones 	/* clear (again), then enable firmware load interrupt */
37302ad0f7e9STom Jones 	IWX_WRITE(sc, IWX_CSR_INT, ~0);
37312ad0f7e9STom Jones 
37322ad0f7e9STom Jones 	err = iwx_nic_init(sc);
37332ad0f7e9STom Jones 	if (err) {
37342ad0f7e9STom Jones 		printf("%s: unable to init nic\n", DEVNAME(sc));
37352ad0f7e9STom Jones 		return err;
37362ad0f7e9STom Jones 	}
37372ad0f7e9STom Jones 
37382ad0f7e9STom Jones 	iwx_enable_fwload_interrupt(sc);
37392ad0f7e9STom Jones 
37402ad0f7e9STom Jones 	return iwx_load_firmware(sc);
37412ad0f7e9STom Jones }
37422ad0f7e9STom Jones 
37432ad0f7e9STom Jones static int
iwx_pnvm_handle_section(struct iwx_softc * sc,const uint8_t * data,size_t len)37442ad0f7e9STom Jones iwx_pnvm_handle_section(struct iwx_softc *sc, const uint8_t *data,
37452ad0f7e9STom Jones     size_t len)
37462ad0f7e9STom Jones {
37472ad0f7e9STom Jones 	const struct iwx_ucode_tlv *tlv;
37482ad0f7e9STom Jones 	uint32_t sha1 = 0;
37492ad0f7e9STom Jones 	uint16_t mac_type = 0, rf_id = 0;
37502ad0f7e9STom Jones 	uint8_t *pnvm_data = NULL, *tmp;
37512ad0f7e9STom Jones 	int hw_match = 0;
37522ad0f7e9STom Jones 	uint32_t size = 0;
37532ad0f7e9STom Jones 	int err;
37542ad0f7e9STom Jones 
37552ad0f7e9STom Jones 	while (len >= sizeof(*tlv)) {
37562ad0f7e9STom Jones 		uint32_t tlv_len, tlv_type;
37572ad0f7e9STom Jones 
37582ad0f7e9STom Jones 		len -= sizeof(*tlv);
37592ad0f7e9STom Jones 		tlv = (const void *)data;
37602ad0f7e9STom Jones 
37612ad0f7e9STom Jones 		tlv_len = le32toh(tlv->length);
37622ad0f7e9STom Jones 		tlv_type = le32toh(tlv->type);
37632ad0f7e9STom Jones 
37642ad0f7e9STom Jones 		if (len < tlv_len) {
37652ad0f7e9STom Jones 			printf("%s: invalid TLV len: %zd/%u\n",
37662ad0f7e9STom Jones 			    DEVNAME(sc), len, tlv_len);
37672ad0f7e9STom Jones 			err = EINVAL;
37682ad0f7e9STom Jones 			goto out;
37692ad0f7e9STom Jones 		}
37702ad0f7e9STom Jones 
37712ad0f7e9STom Jones 		data += sizeof(*tlv);
37722ad0f7e9STom Jones 
37732ad0f7e9STom Jones 		switch (tlv_type) {
37742ad0f7e9STom Jones 		case IWX_UCODE_TLV_PNVM_VERSION:
37752ad0f7e9STom Jones 			if (tlv_len < sizeof(uint32_t))
37762ad0f7e9STom Jones 				break;
37772ad0f7e9STom Jones 
37782ad0f7e9STom Jones 			sha1 = le32_to_cpup((const uint32_t *)data);
37792ad0f7e9STom Jones 			break;
37802ad0f7e9STom Jones 		case IWX_UCODE_TLV_HW_TYPE:
37812ad0f7e9STom Jones 			if (tlv_len < 2 * sizeof(uint16_t))
37822ad0f7e9STom Jones 				break;
37832ad0f7e9STom Jones 
37842ad0f7e9STom Jones 			if (hw_match)
37852ad0f7e9STom Jones 				break;
37862ad0f7e9STom Jones 
37872ad0f7e9STom Jones 			mac_type = le16_to_cpup((const uint16_t *)data);
37882ad0f7e9STom Jones 			rf_id = le16_to_cpup((const uint16_t *)(data +
37892ad0f7e9STom Jones 			    sizeof(uint16_t)));
37902ad0f7e9STom Jones 
37912ad0f7e9STom Jones 			if (mac_type == IWX_CSR_HW_REV_TYPE(sc->sc_hw_rev) &&
37922ad0f7e9STom Jones 			    rf_id == IWX_CSR_HW_RFID_TYPE(sc->sc_hw_rf_id))
37932ad0f7e9STom Jones 				hw_match = 1;
37942ad0f7e9STom Jones 			break;
37952ad0f7e9STom Jones 		case IWX_UCODE_TLV_SEC_RT: {
37962ad0f7e9STom Jones 			const struct iwx_pnvm_section *section;
37972ad0f7e9STom Jones 			uint32_t data_len;
37982ad0f7e9STom Jones 
37992ad0f7e9STom Jones 			section = (const void *)data;
38002ad0f7e9STom Jones 			data_len = tlv_len - sizeof(*section);
38012ad0f7e9STom Jones 
38022ad0f7e9STom Jones 			/* TODO: remove, this is a deprecated separator */
38032ad0f7e9STom Jones 			if (le32_to_cpup((const uint32_t *)data) == 0xddddeeee)
38042ad0f7e9STom Jones 				break;
38052ad0f7e9STom Jones 
38062ad0f7e9STom Jones 			tmp = malloc(size + data_len, M_DEVBUF,
38072ad0f7e9STom Jones 			    M_WAITOK | M_ZERO);
38082ad0f7e9STom Jones 			if (tmp == NULL) {
38092ad0f7e9STom Jones 				err = ENOMEM;
38102ad0f7e9STom Jones 				goto out;
38112ad0f7e9STom Jones 			}
38122ad0f7e9STom Jones 			// XXX:misha pnvm_data is NULL and size is 0 at first pass
38132ad0f7e9STom Jones 			memcpy(tmp, pnvm_data, size);
38142ad0f7e9STom Jones 			memcpy(tmp + size, section->data, data_len);
38152ad0f7e9STom Jones 			free(pnvm_data, M_DEVBUF);
38162ad0f7e9STom Jones 			pnvm_data = tmp;
38172ad0f7e9STom Jones 			size += data_len;
38182ad0f7e9STom Jones 			break;
38192ad0f7e9STom Jones 		}
38202ad0f7e9STom Jones 		case IWX_UCODE_TLV_PNVM_SKU:
38212ad0f7e9STom Jones 			/* New PNVM section started, stop parsing. */
38222ad0f7e9STom Jones 			goto done;
38232ad0f7e9STom Jones 		default:
38242ad0f7e9STom Jones 			break;
38252ad0f7e9STom Jones 		}
38262ad0f7e9STom Jones 
38272ad0f7e9STom Jones 		if (roundup(tlv_len, 4) > len)
38282ad0f7e9STom Jones 			break;
38292ad0f7e9STom Jones 		len -= roundup(tlv_len, 4);
38302ad0f7e9STom Jones 		data += roundup(tlv_len, 4);
38312ad0f7e9STom Jones 	}
38322ad0f7e9STom Jones done:
38332ad0f7e9STom Jones 	if (!hw_match || size == 0) {
38342ad0f7e9STom Jones 		err = ENOENT;
38352ad0f7e9STom Jones 		goto out;
38362ad0f7e9STom Jones 	}
38372ad0f7e9STom Jones 
38382ad0f7e9STom Jones 	err = iwx_dma_contig_alloc(sc->sc_dmat, &sc->pnvm_dma, size, 1);
38392ad0f7e9STom Jones 	if (err) {
38402ad0f7e9STom Jones 		printf("%s: could not allocate DMA memory for PNVM\n",
38412ad0f7e9STom Jones 		    DEVNAME(sc));
38422ad0f7e9STom Jones 		err = ENOMEM;
38432ad0f7e9STom Jones 		goto out;
38442ad0f7e9STom Jones 	}
38452ad0f7e9STom Jones 	memcpy(sc->pnvm_dma.vaddr, pnvm_data, size);
38462ad0f7e9STom Jones 	iwx_ctxt_info_gen3_set_pnvm(sc);
38472ad0f7e9STom Jones 	sc->sc_pnvm_ver = sha1;
38482ad0f7e9STom Jones out:
38492ad0f7e9STom Jones 	free(pnvm_data, M_DEVBUF);
38502ad0f7e9STom Jones 	return err;
38512ad0f7e9STom Jones }
38522ad0f7e9STom Jones 
38532ad0f7e9STom Jones static int
iwx_pnvm_parse(struct iwx_softc * sc,const uint8_t * data,size_t len)38542ad0f7e9STom Jones iwx_pnvm_parse(struct iwx_softc *sc, const uint8_t *data, size_t len)
38552ad0f7e9STom Jones {
38562ad0f7e9STom Jones 	const struct iwx_ucode_tlv *tlv;
38572ad0f7e9STom Jones 
38582ad0f7e9STom Jones 	while (len >= sizeof(*tlv)) {
38592ad0f7e9STom Jones 		uint32_t tlv_len, tlv_type;
38602ad0f7e9STom Jones 
38612ad0f7e9STom Jones 		len -= sizeof(*tlv);
38622ad0f7e9STom Jones 		tlv = (const void *)data;
38632ad0f7e9STom Jones 
38642ad0f7e9STom Jones 		tlv_len = le32toh(tlv->length);
38652ad0f7e9STom Jones 		tlv_type = le32toh(tlv->type);
38662ad0f7e9STom Jones 
38672ad0f7e9STom Jones 		if (len < tlv_len || roundup(tlv_len, 4) > len)
38682ad0f7e9STom Jones 			return EINVAL;
38692ad0f7e9STom Jones 
38702ad0f7e9STom Jones 		if (tlv_type == IWX_UCODE_TLV_PNVM_SKU) {
38712ad0f7e9STom Jones 			const struct iwx_sku_id *sku_id =
38722ad0f7e9STom Jones 				(const void *)(data + sizeof(*tlv));
38732ad0f7e9STom Jones 
38742ad0f7e9STom Jones 			data += sizeof(*tlv) + roundup(tlv_len, 4);
38752ad0f7e9STom Jones 			len -= roundup(tlv_len, 4);
38762ad0f7e9STom Jones 
38772ad0f7e9STom Jones 			if (sc->sc_sku_id[0] == le32toh(sku_id->data[0]) &&
38782ad0f7e9STom Jones 			    sc->sc_sku_id[1] == le32toh(sku_id->data[1]) &&
38792ad0f7e9STom Jones 			    sc->sc_sku_id[2] == le32toh(sku_id->data[2]) &&
38802ad0f7e9STom Jones 			    iwx_pnvm_handle_section(sc, data, len) == 0)
38812ad0f7e9STom Jones 				return 0;
38822ad0f7e9STom Jones 		} else {
38832ad0f7e9STom Jones 			data += sizeof(*tlv) + roundup(tlv_len, 4);
38842ad0f7e9STom Jones 			len -= roundup(tlv_len, 4);
38852ad0f7e9STom Jones 		}
38862ad0f7e9STom Jones 	}
38872ad0f7e9STom Jones 
38882ad0f7e9STom Jones 	return ENOENT;
38892ad0f7e9STom Jones }
38902ad0f7e9STom Jones 
38912ad0f7e9STom Jones /* Make AX210 firmware loading context point at PNVM image in DMA memory. */
38922ad0f7e9STom Jones static void
iwx_ctxt_info_gen3_set_pnvm(struct iwx_softc * sc)38932ad0f7e9STom Jones iwx_ctxt_info_gen3_set_pnvm(struct iwx_softc *sc)
38942ad0f7e9STom Jones {
38952ad0f7e9STom Jones 	struct iwx_prph_scratch *prph_scratch;
38962ad0f7e9STom Jones 	struct iwx_prph_scratch_ctrl_cfg *prph_sc_ctrl;
38972ad0f7e9STom Jones 
38982ad0f7e9STom Jones 	prph_scratch = sc->prph_scratch_dma.vaddr;
38992ad0f7e9STom Jones 	prph_sc_ctrl = &prph_scratch->ctrl_cfg;
39002ad0f7e9STom Jones 
39012ad0f7e9STom Jones 	prph_sc_ctrl->pnvm_cfg.pnvm_base_addr = htole64(sc->pnvm_dma.paddr);
39022ad0f7e9STom Jones 	prph_sc_ctrl->pnvm_cfg.pnvm_size = htole32(sc->pnvm_dma.size);
39032ad0f7e9STom Jones 
39042ad0f7e9STom Jones 	bus_dmamap_sync(sc->sc_dmat, sc->pnvm_dma.map, BUS_DMASYNC_PREWRITE);
39052ad0f7e9STom Jones }
39062ad0f7e9STom Jones 
39072ad0f7e9STom Jones /*
39082ad0f7e9STom Jones  * Load platform-NVM (non-volatile-memory) data from the filesystem.
39092ad0f7e9STom Jones  * This data apparently contains regulatory information and affects device
39102ad0f7e9STom Jones  * channel configuration.
39112ad0f7e9STom Jones  * The SKU of AX210 devices tells us which PNVM file section is needed.
39122ad0f7e9STom Jones  * Pre-AX210 devices store NVM data onboard.
39132ad0f7e9STom Jones  */
39142ad0f7e9STom Jones static int
iwx_load_pnvm(struct iwx_softc * sc)39152ad0f7e9STom Jones iwx_load_pnvm(struct iwx_softc *sc)
39162ad0f7e9STom Jones {
39172ad0f7e9STom Jones 	const int wait_flags = IWX_PNVM_COMPLETE;
39182ad0f7e9STom Jones 	int err = 0;
39192ad0f7e9STom Jones 	const struct firmware *pnvm;
39202ad0f7e9STom Jones 
39212ad0f7e9STom Jones 	if (sc->sc_sku_id[0] == 0 &&
39222ad0f7e9STom Jones 	    sc->sc_sku_id[1] == 0 &&
39232ad0f7e9STom Jones 	    sc->sc_sku_id[2] == 0)
39242ad0f7e9STom Jones 		return 0;
39252ad0f7e9STom Jones 
39262ad0f7e9STom Jones 	if (sc->sc_pnvm_name) {
39272ad0f7e9STom Jones 		if (sc->pnvm_dma.vaddr == NULL) {
39282ad0f7e9STom Jones 			IWX_UNLOCK(sc);
39292ad0f7e9STom Jones 			pnvm = firmware_get(sc->sc_pnvm_name);
39302ad0f7e9STom Jones 			if (pnvm == NULL) {
39312ad0f7e9STom Jones 				printf("%s: could not read %s (error %d)\n",
39322ad0f7e9STom Jones 				    DEVNAME(sc), sc->sc_pnvm_name, err);
39332ad0f7e9STom Jones 				IWX_LOCK(sc);
39342ad0f7e9STom Jones 				return EINVAL;
39352ad0f7e9STom Jones 			}
39362ad0f7e9STom Jones 			sc->sc_pnvm = pnvm;
39372ad0f7e9STom Jones 
39382ad0f7e9STom Jones 			err = iwx_pnvm_parse(sc, pnvm->data, pnvm->datasize);
39392ad0f7e9STom Jones 			IWX_LOCK(sc);
39402ad0f7e9STom Jones 			if (err && err != ENOENT) {
39412ad0f7e9STom Jones 				return EINVAL;
39422ad0f7e9STom Jones 			}
39432ad0f7e9STom Jones 		} else
39442ad0f7e9STom Jones 			iwx_ctxt_info_gen3_set_pnvm(sc);
39452ad0f7e9STom Jones 	}
39462ad0f7e9STom Jones 
39472ad0f7e9STom Jones 	if (!iwx_nic_lock(sc)) {
39482ad0f7e9STom Jones 		return EBUSY;
39492ad0f7e9STom Jones 	}
39502ad0f7e9STom Jones 
39512ad0f7e9STom Jones 	/*
39522ad0f7e9STom Jones 	 * If we don't have a platform NVM file simply ask firmware
39532ad0f7e9STom Jones 	 * to proceed without it.
39542ad0f7e9STom Jones 	 */
39552ad0f7e9STom Jones 
39562ad0f7e9STom Jones 	iwx_write_umac_prph(sc, IWX_UREG_DOORBELL_TO_ISR6,
39572ad0f7e9STom Jones 	    IWX_UREG_DOORBELL_TO_ISR6_PNVM);
39582ad0f7e9STom Jones 
39592ad0f7e9STom Jones 	/* Wait for the pnvm complete notification from firmware. */
39602ad0f7e9STom Jones 	while ((sc->sc_init_complete & wait_flags) != wait_flags) {
39612ad0f7e9STom Jones 		err = msleep(&sc->sc_init_complete, &sc->sc_mtx, 0, "iwxinit", 2 * hz);
39622ad0f7e9STom Jones 		if (err)
39632ad0f7e9STom Jones 			break;
39642ad0f7e9STom Jones 	}
39652ad0f7e9STom Jones 
39662ad0f7e9STom Jones 	iwx_nic_unlock(sc);
39672ad0f7e9STom Jones 
39682ad0f7e9STom Jones 	return err;
39692ad0f7e9STom Jones }
39702ad0f7e9STom Jones 
39712ad0f7e9STom Jones static int
iwx_send_tx_ant_cfg(struct iwx_softc * sc,uint8_t valid_tx_ant)39722ad0f7e9STom Jones iwx_send_tx_ant_cfg(struct iwx_softc *sc, uint8_t valid_tx_ant)
39732ad0f7e9STom Jones {
39742ad0f7e9STom Jones 	struct iwx_tx_ant_cfg_cmd tx_ant_cmd = {
39752ad0f7e9STom Jones 		.valid = htole32(valid_tx_ant),
39762ad0f7e9STom Jones 	};
39772ad0f7e9STom Jones 
39782ad0f7e9STom Jones 	return iwx_send_cmd_pdu(sc, IWX_TX_ANT_CONFIGURATION_CMD,
39792ad0f7e9STom Jones 	    0, sizeof(tx_ant_cmd), &tx_ant_cmd);
39802ad0f7e9STom Jones }
39812ad0f7e9STom Jones 
39822ad0f7e9STom Jones static int
iwx_send_phy_cfg_cmd(struct iwx_softc * sc)39832ad0f7e9STom Jones iwx_send_phy_cfg_cmd(struct iwx_softc *sc)
39842ad0f7e9STom Jones {
39852ad0f7e9STom Jones 	struct iwx_phy_cfg_cmd phy_cfg_cmd;
39862ad0f7e9STom Jones 
39872ad0f7e9STom Jones 	phy_cfg_cmd.phy_cfg = htole32(sc->sc_fw_phy_config);
39882ad0f7e9STom Jones 	phy_cfg_cmd.calib_control.event_trigger =
39892ad0f7e9STom Jones 	    sc->sc_default_calib[IWX_UCODE_TYPE_REGULAR].event_trigger;
39902ad0f7e9STom Jones 	phy_cfg_cmd.calib_control.flow_trigger =
39912ad0f7e9STom Jones 	    sc->sc_default_calib[IWX_UCODE_TYPE_REGULAR].flow_trigger;
39922ad0f7e9STom Jones 
39932ad0f7e9STom Jones 	return iwx_send_cmd_pdu(sc, IWX_PHY_CONFIGURATION_CMD, 0,
39942ad0f7e9STom Jones 	    sizeof(phy_cfg_cmd), &phy_cfg_cmd);
39952ad0f7e9STom Jones }
39962ad0f7e9STom Jones 
39972ad0f7e9STom Jones static int
iwx_send_dqa_cmd(struct iwx_softc * sc)39982ad0f7e9STom Jones iwx_send_dqa_cmd(struct iwx_softc *sc)
39992ad0f7e9STom Jones {
40002ad0f7e9STom Jones 	struct iwx_dqa_enable_cmd dqa_cmd = {
40012ad0f7e9STom Jones 		.cmd_queue = htole32(IWX_DQA_CMD_QUEUE),
40022ad0f7e9STom Jones 	};
40032ad0f7e9STom Jones 	uint32_t cmd_id;
40042ad0f7e9STom Jones 
40052ad0f7e9STom Jones 	cmd_id = iwx_cmd_id(IWX_DQA_ENABLE_CMD, IWX_DATA_PATH_GROUP, 0);
40062ad0f7e9STom Jones 	return iwx_send_cmd_pdu(sc, cmd_id, 0, sizeof(dqa_cmd), &dqa_cmd);
40072ad0f7e9STom Jones }
40082ad0f7e9STom Jones 
40092ad0f7e9STom Jones static int
iwx_load_ucode_wait_alive(struct iwx_softc * sc)40102ad0f7e9STom Jones iwx_load_ucode_wait_alive(struct iwx_softc *sc)
40112ad0f7e9STom Jones {
40122ad0f7e9STom Jones 	int err;
40132ad0f7e9STom Jones 
40142ad0f7e9STom Jones 	IWX_UNLOCK(sc);
40152ad0f7e9STom Jones 	err = iwx_read_firmware(sc);
40162ad0f7e9STom Jones 	IWX_LOCK(sc);
40172ad0f7e9STom Jones 	if (err)
40182ad0f7e9STom Jones 		return err;
40192ad0f7e9STom Jones 
40202ad0f7e9STom Jones 	err = iwx_start_fw(sc);
40212ad0f7e9STom Jones 	if (err)
40222ad0f7e9STom Jones 		return err;
40232ad0f7e9STom Jones 
40242ad0f7e9STom Jones 	if (sc->sc_device_family >= IWX_DEVICE_FAMILY_AX210) {
40252ad0f7e9STom Jones 		err = iwx_load_pnvm(sc);
40262ad0f7e9STom Jones 		if (err)
40272ad0f7e9STom Jones 			return err;
40282ad0f7e9STom Jones 	}
40292ad0f7e9STom Jones 
40302ad0f7e9STom Jones 	iwx_post_alive(sc);
40312ad0f7e9STom Jones 
40322ad0f7e9STom Jones 	return 0;
40332ad0f7e9STom Jones }
40342ad0f7e9STom Jones 
40352ad0f7e9STom Jones static int
iwx_run_init_mvm_ucode(struct iwx_softc * sc,int readnvm)40362ad0f7e9STom Jones iwx_run_init_mvm_ucode(struct iwx_softc *sc, int readnvm)
40372ad0f7e9STom Jones {
40382ad0f7e9STom Jones 	const int wait_flags = IWX_INIT_COMPLETE;
40392ad0f7e9STom Jones 	struct iwx_nvm_access_complete_cmd nvm_complete = {};
40402ad0f7e9STom Jones 	struct iwx_init_extended_cfg_cmd init_cfg = {
40412ad0f7e9STom Jones 		.init_flags = htole32(IWX_INIT_NVM),
40422ad0f7e9STom Jones 	};
40432ad0f7e9STom Jones 
40442ad0f7e9STom Jones 	int err;
40452ad0f7e9STom Jones 
40462ad0f7e9STom Jones 	if ((sc->sc_flags & IWX_FLAG_RFKILL) && !readnvm) {
40472ad0f7e9STom Jones 		printf("%s: radio is disabled by hardware switch\n",
40482ad0f7e9STom Jones 		    DEVNAME(sc));
40492ad0f7e9STom Jones 		return EPERM;
40502ad0f7e9STom Jones 	}
40512ad0f7e9STom Jones 
40522ad0f7e9STom Jones 	sc->sc_init_complete = 0;
40532ad0f7e9STom Jones 	err = iwx_load_ucode_wait_alive(sc);
40542ad0f7e9STom Jones 	if (err) {
405528345b17STom Jones 		IWX_DPRINTF(sc, IWX_DEBUG_FIRMWARE_TLV,
405628345b17STom Jones 		    "%s: failed to load init firmware\n", DEVNAME(sc));
40572ad0f7e9STom Jones 		return err;
40582ad0f7e9STom Jones 	} else {
40592ad0f7e9STom Jones 		IWX_DPRINTF(sc, IWX_DEBUG_FIRMWARE_TLV,
40602ad0f7e9STom Jones 		    "%s: successfully loaded init firmware\n", __func__);
40612ad0f7e9STom Jones 	}
40622ad0f7e9STom Jones 
40632ad0f7e9STom Jones 	/*
40642ad0f7e9STom Jones 	 * Send init config command to mark that we are sending NVM
40652ad0f7e9STom Jones 	 * access commands
40662ad0f7e9STom Jones 	 */
40672ad0f7e9STom Jones 	err = iwx_send_cmd_pdu(sc, IWX_WIDE_ID(IWX_SYSTEM_GROUP,
40682ad0f7e9STom Jones 	    IWX_INIT_EXTENDED_CFG_CMD), 0, sizeof(init_cfg), &init_cfg);
40692ad0f7e9STom Jones 	if (err) {
40702ad0f7e9STom Jones 		printf("%s: IWX_INIT_EXTENDED_CFG_CMD error=%d\n", __func__,
40712ad0f7e9STom Jones 		    err);
40722ad0f7e9STom Jones 		return err;
40732ad0f7e9STom Jones 	}
40742ad0f7e9STom Jones 
40752ad0f7e9STom Jones 	err = iwx_send_cmd_pdu(sc, IWX_WIDE_ID(IWX_REGULATORY_AND_NVM_GROUP,
40762ad0f7e9STom Jones 	    IWX_NVM_ACCESS_COMPLETE), 0, sizeof(nvm_complete), &nvm_complete);
40772ad0f7e9STom Jones 	if (err) {
40782ad0f7e9STom Jones 		return err;
40792ad0f7e9STom Jones 	}
40802ad0f7e9STom Jones 
40812ad0f7e9STom Jones 	/* Wait for the init complete notification from the firmware. */
40822ad0f7e9STom Jones 	while ((sc->sc_init_complete & wait_flags) != wait_flags) {
40832ad0f7e9STom Jones 		err = msleep(&sc->sc_init_complete, &sc->sc_mtx, 0, "iwxinit", 2 * hz);
40842ad0f7e9STom Jones 		if (err) {
40852ad0f7e9STom Jones 			DPRINTF(("%s: will return err=%d\n", __func__, err));
40862ad0f7e9STom Jones 			return err;
40872ad0f7e9STom Jones 		} else {
40882ad0f7e9STom Jones 			DPRINTF(("%s: sc_init_complete == IWX_INIT_COMPLETE\n",
40892ad0f7e9STom Jones 			    __func__));
40902ad0f7e9STom Jones 		}
40912ad0f7e9STom Jones 	}
40922ad0f7e9STom Jones 
40932ad0f7e9STom Jones 	if (readnvm) {
40942ad0f7e9STom Jones 		err = iwx_nvm_get(sc);
40952ad0f7e9STom Jones 		DPRINTF(("%s: err=%d\n", __func__, err));
40962ad0f7e9STom Jones 		if (err) {
40972ad0f7e9STom Jones 			printf("%s: failed to read nvm (error %d)\n",
40982ad0f7e9STom Jones 			    DEVNAME(sc), err);
40992ad0f7e9STom Jones 			return err;
41002ad0f7e9STom Jones 		} else {
41012ad0f7e9STom Jones 			DPRINTF(("%s: successfully read nvm\n", DEVNAME(sc)));
41022ad0f7e9STom Jones 		}
41032ad0f7e9STom Jones 		IEEE80211_ADDR_COPY(sc->sc_ic.ic_macaddr, sc->sc_nvm.hw_addr);
41042ad0f7e9STom Jones 	}
41052ad0f7e9STom Jones 	return 0;
41062ad0f7e9STom Jones }
41072ad0f7e9STom Jones 
41082ad0f7e9STom Jones static int
iwx_config_ltr(struct iwx_softc * sc)41092ad0f7e9STom Jones iwx_config_ltr(struct iwx_softc *sc)
41102ad0f7e9STom Jones {
41112ad0f7e9STom Jones 	struct iwx_ltr_config_cmd cmd = {
41122ad0f7e9STom Jones 		.flags = htole32(IWX_LTR_CFG_FLAG_FEATURE_ENABLE),
41132ad0f7e9STom Jones 	};
41142ad0f7e9STom Jones 
41152ad0f7e9STom Jones 	if (!sc->sc_ltr_enabled)
41162ad0f7e9STom Jones 		return 0;
41172ad0f7e9STom Jones 
41182ad0f7e9STom Jones 	return iwx_send_cmd_pdu(sc, IWX_LTR_CONFIG, 0, sizeof(cmd), &cmd);
41192ad0f7e9STom Jones }
41202ad0f7e9STom Jones 
41212ad0f7e9STom Jones static void
iwx_update_rx_desc(struct iwx_softc * sc,struct iwx_rx_ring * ring,int idx,bus_dma_segment_t * seg)41222ad0f7e9STom Jones iwx_update_rx_desc(struct iwx_softc *sc, struct iwx_rx_ring *ring, int idx,
41232ad0f7e9STom Jones     bus_dma_segment_t *seg)
41242ad0f7e9STom Jones {
41252ad0f7e9STom Jones 	struct iwx_rx_data *data = &ring->data[idx];
41262ad0f7e9STom Jones 
41272ad0f7e9STom Jones 	if (sc->sc_device_family >= IWX_DEVICE_FAMILY_AX210) {
41282ad0f7e9STom Jones 		struct iwx_rx_transfer_desc *desc = ring->desc;
41292ad0f7e9STom Jones 		desc[idx].rbid = htole16(idx & 0xffff);
41302ad0f7e9STom Jones 		desc[idx].addr = htole64((*seg).ds_addr);
41312ad0f7e9STom Jones 		bus_dmamap_sync(ring->data_dmat, data->map,
41322ad0f7e9STom Jones 		    BUS_DMASYNC_PREWRITE);
41332ad0f7e9STom Jones 	} else {
41342ad0f7e9STom Jones 		((uint64_t *)ring->desc)[idx] =
41352ad0f7e9STom Jones 		    htole64((*seg).ds_addr);
41362ad0f7e9STom Jones 		bus_dmamap_sync(ring->data_dmat, data->map,
41372ad0f7e9STom Jones 		    BUS_DMASYNC_PREWRITE);
41382ad0f7e9STom Jones 	}
41392ad0f7e9STom Jones }
41402ad0f7e9STom Jones 
41412ad0f7e9STom Jones static int
iwx_rx_addbuf(struct iwx_softc * sc,int size,int idx)41422ad0f7e9STom Jones iwx_rx_addbuf(struct iwx_softc *sc, int size, int idx)
41432ad0f7e9STom Jones {
41442ad0f7e9STom Jones 	struct iwx_rx_ring *ring = &sc->rxq;
41452ad0f7e9STom Jones 	struct iwx_rx_data *data = &ring->data[idx];
41462ad0f7e9STom Jones 	struct mbuf *m;
41472ad0f7e9STom Jones 	int err;
41482ad0f7e9STom Jones 	int fatal = 0;
41492ad0f7e9STom Jones 	bus_dma_segment_t seg;
41502ad0f7e9STom Jones 	int nsegs;
41512ad0f7e9STom Jones 
41522ad0f7e9STom Jones 	m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, IWX_RBUF_SIZE);
41532ad0f7e9STom Jones 	if (m == NULL)
41542ad0f7e9STom Jones 		return ENOBUFS;
41552ad0f7e9STom Jones 
41562ad0f7e9STom Jones 	if (data->m != NULL) {
41572ad0f7e9STom Jones 		bus_dmamap_unload(ring->data_dmat, data->map);
41582ad0f7e9STom Jones 		fatal = 1;
41592ad0f7e9STom Jones 	}
41602ad0f7e9STom Jones 
41612ad0f7e9STom Jones 	m->m_len = m->m_pkthdr.len = m->m_ext.ext_size;
41622ad0f7e9STom Jones 	err = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, &seg,
41632ad0f7e9STom Jones 	    &nsegs, BUS_DMA_NOWAIT);
41642ad0f7e9STom Jones 	if (err) {
41652ad0f7e9STom Jones 		/* XXX */
41662ad0f7e9STom Jones 		if (fatal)
41672ad0f7e9STom Jones 			panic("could not load RX mbuf");
41682ad0f7e9STom Jones 		m_freem(m);
41692ad0f7e9STom Jones 		return err;
41702ad0f7e9STom Jones 	}
41712ad0f7e9STom Jones 	data->m = m;
41722ad0f7e9STom Jones 	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREREAD);
41732ad0f7e9STom Jones 
41742ad0f7e9STom Jones 	/* Update RX descriptor. */
41752ad0f7e9STom Jones 	iwx_update_rx_desc(sc, ring, idx, &seg);
41762ad0f7e9STom Jones 	return 0;
41772ad0f7e9STom Jones }
41782ad0f7e9STom Jones 
41792ad0f7e9STom Jones static int
iwx_rxmq_get_signal_strength(struct iwx_softc * sc,struct iwx_rx_mpdu_desc * desc)41802ad0f7e9STom Jones iwx_rxmq_get_signal_strength(struct iwx_softc *sc,
41812ad0f7e9STom Jones     struct iwx_rx_mpdu_desc *desc)
41822ad0f7e9STom Jones {
41832ad0f7e9STom Jones 	int energy_a, energy_b;
41842ad0f7e9STom Jones 
41852ad0f7e9STom Jones 	if (sc->sc_device_family >= IWX_DEVICE_FAMILY_AX210) {
41862ad0f7e9STom Jones 		energy_a = desc->v3.energy_a;
41872ad0f7e9STom Jones 		energy_b = desc->v3.energy_b;
41882ad0f7e9STom Jones 	} else {
41892ad0f7e9STom Jones 		energy_a = desc->v1.energy_a;
41902ad0f7e9STom Jones 		energy_b = desc->v1.energy_b;
41912ad0f7e9STom Jones 	}
41922ad0f7e9STom Jones 	energy_a = energy_a ? -energy_a : -256;
41932ad0f7e9STom Jones 	energy_b = energy_b ? -energy_b : -256;
41942ad0f7e9STom Jones 	return MAX(energy_a, energy_b);
41952ad0f7e9STom Jones }
41962ad0f7e9STom Jones 
4197*be57603cSBjoern A. Zeeb static int
iwx_rxmq_get_chains(struct iwx_softc * sc,struct iwx_rx_mpdu_desc * desc)4198*be57603cSBjoern A. Zeeb iwx_rxmq_get_chains(struct iwx_softc *sc,
4199*be57603cSBjoern A. Zeeb     struct iwx_rx_mpdu_desc *desc)
4200*be57603cSBjoern A. Zeeb {
4201*be57603cSBjoern A. Zeeb 
4202*be57603cSBjoern A. Zeeb 	if (sc->sc_device_family >= IWX_DEVICE_FAMILY_AX210)
4203*be57603cSBjoern A. Zeeb 		return ((desc->v3.rate_n_flags & IWX_RATE_MCS_ANT_AB_MSK) >>
4204*be57603cSBjoern A. Zeeb 		    IWX_RATE_MCS_ANT_POS);
4205*be57603cSBjoern A. Zeeb 	else
4206*be57603cSBjoern A. Zeeb 		return ((desc->v1.rate_n_flags & IWX_RATE_MCS_ANT_AB_MSK) >>
4207*be57603cSBjoern A. Zeeb 		    IWX_RATE_MCS_ANT_POS);
4208*be57603cSBjoern A. Zeeb }
4209*be57603cSBjoern A. Zeeb 
42102ad0f7e9STom Jones static void
iwx_rx_rx_phy_cmd(struct iwx_softc * sc,struct iwx_rx_packet * pkt,struct iwx_rx_data * data)42112ad0f7e9STom Jones iwx_rx_rx_phy_cmd(struct iwx_softc *sc, struct iwx_rx_packet *pkt,
42122ad0f7e9STom Jones     struct iwx_rx_data *data)
42132ad0f7e9STom Jones {
42142ad0f7e9STom Jones 	struct iwx_rx_phy_info *phy_info = (void *)pkt->data;
42152ad0f7e9STom Jones 	struct iwx_cmd_header *cmd_hdr = &pkt->hdr;
42162ad0f7e9STom Jones 	int qid = cmd_hdr->qid;
42172ad0f7e9STom Jones 	struct iwx_tx_ring *ring = &sc->txq[qid];
42182ad0f7e9STom Jones 
42192ad0f7e9STom Jones 	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREREAD);
42202ad0f7e9STom Jones 	memcpy(&sc->sc_last_phy_info, phy_info, sizeof(sc->sc_last_phy_info));
42212ad0f7e9STom Jones }
42222ad0f7e9STom Jones 
42232ad0f7e9STom Jones /*
42242ad0f7e9STom Jones  * Retrieve the average noise (in dBm) among receivers.
42252ad0f7e9STom Jones  */
42262ad0f7e9STom Jones static int
iwx_get_noise(const struct iwx_statistics_rx_non_phy * stats)42272ad0f7e9STom Jones iwx_get_noise(const struct iwx_statistics_rx_non_phy *stats)
42282ad0f7e9STom Jones {
42292ad0f7e9STom Jones 	int i, total, nbant, noise;
42302ad0f7e9STom Jones 
42312ad0f7e9STom Jones 	total = nbant = noise = 0;
42322ad0f7e9STom Jones 	for (i = 0; i < 3; i++) {
42332ad0f7e9STom Jones 		noise = le32toh(stats->beacon_silence_rssi[i]) & 0xff;
42342ad0f7e9STom Jones 		if (noise) {
42352ad0f7e9STom Jones 			total += noise;
42362ad0f7e9STom Jones 			nbant++;
42372ad0f7e9STom Jones 		}
42382ad0f7e9STom Jones 	}
42392ad0f7e9STom Jones 
42402ad0f7e9STom Jones 	/* There should be at least one antenna but check anyway. */
42412ad0f7e9STom Jones 	return (nbant == 0) ? -127 : (total / nbant) - 107;
42422ad0f7e9STom Jones }
42432ad0f7e9STom Jones 
42442ad0f7e9STom Jones #if 0
42452ad0f7e9STom Jones int
42462ad0f7e9STom Jones iwx_ccmp_decap(struct iwx_softc *sc, struct mbuf *m, struct ieee80211_node *ni,
42472ad0f7e9STom Jones     struct ieee80211_rxinfo *rxi)
42482ad0f7e9STom Jones {
42492ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
42502ad0f7e9STom Jones 	struct ieee80211_key *k;
42512ad0f7e9STom Jones 	struct ieee80211_frame *wh;
42522ad0f7e9STom Jones 	uint64_t pn, *prsc;
42532ad0f7e9STom Jones 	uint8_t *ivp;
42542ad0f7e9STom Jones 	uint8_t tid;
42552ad0f7e9STom Jones 	int hdrlen, hasqos;
42562ad0f7e9STom Jones 
42572ad0f7e9STom Jones 	wh = mtod(m, struct ieee80211_frame *);
42582ad0f7e9STom Jones 	hdrlen = ieee80211_get_hdrlen(wh);
42592ad0f7e9STom Jones 	ivp = (uint8_t *)wh + hdrlen;
42602ad0f7e9STom Jones 
42612ad0f7e9STom Jones 	/* find key for decryption */
42622ad0f7e9STom Jones 	k = ieee80211_get_rxkey(ic, m, ni);
42632ad0f7e9STom Jones 	if (k == NULL || k->k_cipher != IEEE80211_CIPHER_CCMP)
42642ad0f7e9STom Jones 		return 1;
42652ad0f7e9STom Jones 
42662ad0f7e9STom Jones 	/* Check that ExtIV bit is be set. */
42672ad0f7e9STom Jones 	if (!(ivp[3] & IEEE80211_WEP_EXTIV))
42682ad0f7e9STom Jones 		return 1;
42692ad0f7e9STom Jones 
42702ad0f7e9STom Jones 	hasqos = ieee80211_has_qos(wh);
42712ad0f7e9STom Jones 	tid = hasqos ? ieee80211_get_qos(wh) & IEEE80211_QOS_TID : 0;
42722ad0f7e9STom Jones 	prsc = &k->k_rsc[tid];
42732ad0f7e9STom Jones 
42742ad0f7e9STom Jones 	/* Extract the 48-bit PN from the CCMP header. */
42752ad0f7e9STom Jones 	pn = (uint64_t)ivp[0]       |
42762ad0f7e9STom Jones 	     (uint64_t)ivp[1] <<  8 |
42772ad0f7e9STom Jones 	     (uint64_t)ivp[4] << 16 |
42782ad0f7e9STom Jones 	     (uint64_t)ivp[5] << 24 |
42792ad0f7e9STom Jones 	     (uint64_t)ivp[6] << 32 |
42802ad0f7e9STom Jones 	     (uint64_t)ivp[7] << 40;
42812ad0f7e9STom Jones 	if (rxi->rxi_flags & IEEE80211_RXI_HWDEC_SAME_PN) {
42822ad0f7e9STom Jones 		if (pn < *prsc) {
42832ad0f7e9STom Jones 			ic->ic_stats.is_ccmp_replays++;
42842ad0f7e9STom Jones 			return 1;
42852ad0f7e9STom Jones 		}
42862ad0f7e9STom Jones 	} else if (pn <= *prsc) {
42872ad0f7e9STom Jones 		ic->ic_stats.is_ccmp_replays++;
42882ad0f7e9STom Jones 		return 1;
42892ad0f7e9STom Jones 	}
42902ad0f7e9STom Jones 	/* Last seen packet number is updated in ieee80211_inputm(). */
42912ad0f7e9STom Jones 
42922ad0f7e9STom Jones 	/*
42932ad0f7e9STom Jones 	 * Some firmware versions strip the MIC, and some don't. It is not
42942ad0f7e9STom Jones 	 * clear which of the capability flags could tell us what to expect.
42952ad0f7e9STom Jones 	 * For now, keep things simple and just leave the MIC in place if
42962ad0f7e9STom Jones 	 * it is present.
42972ad0f7e9STom Jones 	 *
42982ad0f7e9STom Jones 	 * The IV will be stripped by ieee80211_inputm().
42992ad0f7e9STom Jones 	 */
43002ad0f7e9STom Jones 	return 0;
43012ad0f7e9STom Jones }
43022ad0f7e9STom Jones #endif
43032ad0f7e9STom Jones 
43042ad0f7e9STom Jones static int
iwx_rx_hwdecrypt(struct iwx_softc * sc,struct mbuf * m,uint32_t rx_pkt_status)43052ad0f7e9STom Jones iwx_rx_hwdecrypt(struct iwx_softc *sc, struct mbuf *m, uint32_t rx_pkt_status)
43062ad0f7e9STom Jones {
43072ad0f7e9STom Jones 	struct ieee80211_frame *wh;
43082ad0f7e9STom Jones 	int ret = 0;
43092ad0f7e9STom Jones 	uint8_t type, subtype;
43102ad0f7e9STom Jones 
43112ad0f7e9STom Jones 	wh = mtod(m, struct ieee80211_frame *);
43122ad0f7e9STom Jones 
43132ad0f7e9STom Jones 	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
43142ad0f7e9STom Jones 	if (type == IEEE80211_FC0_TYPE_CTL) {
43152ad0f7e9STom Jones 		return 0;
43162ad0f7e9STom Jones 	}
43172ad0f7e9STom Jones 
43182ad0f7e9STom Jones 	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
43192ad0f7e9STom Jones 	if (IEEE80211_QOS_HAS_SEQ(wh) && (subtype & IEEE80211_FC0_SUBTYPE_NODATA)) {
43202ad0f7e9STom Jones 		return 0;
43212ad0f7e9STom Jones 	}
43222ad0f7e9STom Jones 
43232ad0f7e9STom Jones 
43242ad0f7e9STom Jones 	if (((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) !=
43252ad0f7e9STom Jones 	    IEEE80211_FC0_TYPE_CTL)
43262ad0f7e9STom Jones 	    && (wh->i_fc[1] & IEEE80211_FC1_PROTECTED)) {
43272ad0f7e9STom Jones 		if ((rx_pkt_status & IWX_RX_MPDU_RES_STATUS_SEC_ENC_MSK) !=
43282ad0f7e9STom Jones 		    IWX_RX_MPDU_RES_STATUS_SEC_CCM_ENC) {
43292ad0f7e9STom Jones 			DPRINTF(("%s: not IWX_RX_MPDU_RES_STATUS_SEC_CCM_ENC\n", __func__));
43302ad0f7e9STom Jones 			ret = 1;
43312ad0f7e9STom Jones 			goto out;
43322ad0f7e9STom Jones 		}
43332ad0f7e9STom Jones 		/* Check whether decryption was successful or not. */
43342ad0f7e9STom Jones 		if ((rx_pkt_status &
43352ad0f7e9STom Jones 		    (IWX_RX_MPDU_RES_STATUS_DEC_DONE |
43362ad0f7e9STom Jones 		    IWX_RX_MPDU_RES_STATUS_MIC_OK)) !=
43372ad0f7e9STom Jones 		    (IWX_RX_MPDU_RES_STATUS_DEC_DONE |
43382ad0f7e9STom Jones 		    IWX_RX_MPDU_RES_STATUS_MIC_OK)) {
43392ad0f7e9STom Jones 			DPRINTF(("%s: not IWX_RX_MPDU_RES_STATUS_MIC_OK\n", __func__));
43402ad0f7e9STom Jones 			ret = 1;
43412ad0f7e9STom Jones 			goto out;
43422ad0f7e9STom Jones 		}
43432ad0f7e9STom Jones 	}
43442ad0f7e9STom Jones 	out:
43452ad0f7e9STom Jones 	return ret;
43462ad0f7e9STom Jones }
43472ad0f7e9STom Jones 
43482ad0f7e9STom Jones static void
iwx_rx_frame(struct iwx_softc * sc,struct mbuf * m,int chanidx,uint32_t rx_pkt_status,int is_shortpre,int rate_n_flags,uint32_t device_timestamp,uint8_t rssi)43492ad0f7e9STom Jones iwx_rx_frame(struct iwx_softc *sc, struct mbuf *m, int chanidx,
43502ad0f7e9STom Jones     uint32_t rx_pkt_status, int is_shortpre, int rate_n_flags,
43512ad0f7e9STom Jones     uint32_t device_timestamp, uint8_t rssi)
43522ad0f7e9STom Jones {
43532ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
43542ad0f7e9STom Jones 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
43552ad0f7e9STom Jones 	struct ieee80211_frame *wh;
43562ad0f7e9STom Jones 	struct ieee80211_node *ni;
43572ad0f7e9STom Jones 
43582ad0f7e9STom Jones 	/*
43592ad0f7e9STom Jones 	 * We need to turn the hardware provided channel index into a channel
43602ad0f7e9STom Jones 	 * and then find it in our ic_channels array
43612ad0f7e9STom Jones 	 */
43622ad0f7e9STom Jones 	if (chanidx < 0 || chanidx >= nitems(ic->ic_channels)) {
43632ad0f7e9STom Jones 		/*
43642ad0f7e9STom Jones 		 * OpenBSD points this at the ibss chan, which it defaults to
43652ad0f7e9STom Jones 		 * channel 1 and then never touches again. Skip a step.
43662ad0f7e9STom Jones 		 */
43672ad0f7e9STom Jones 		printf("iwx: %s:%d controlling chanidx to 1 (%d)\n", __func__, __LINE__, chanidx);
43682ad0f7e9STom Jones 		chanidx = 1;
43692ad0f7e9STom Jones 	}
43702ad0f7e9STom Jones 
43712ad0f7e9STom Jones 	int channel = chanidx;
43722ad0f7e9STom Jones 	for (int i = 0; i < ic->ic_nchans; i++) {
43732ad0f7e9STom Jones 		if (ic->ic_channels[i].ic_ieee == channel) {
43742ad0f7e9STom Jones 			chanidx = i;
43752ad0f7e9STom Jones 		}
43762ad0f7e9STom Jones 	}
43772ad0f7e9STom Jones 	ic->ic_curchan = &ic->ic_channels[chanidx];
43782ad0f7e9STom Jones 
43792ad0f7e9STom Jones 	wh = mtod(m, struct ieee80211_frame *);
43802ad0f7e9STom Jones 	ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh);
43812ad0f7e9STom Jones 
43822ad0f7e9STom Jones #if 0	/* XXX hw decrypt */
43832ad0f7e9STom Jones 	if ((rxi->rxi_flags & IEEE80211_RXI_HWDEC) &&
43842ad0f7e9STom Jones 	    iwx_ccmp_decap(sc, m, ni, rxi) != 0) {
43852ad0f7e9STom Jones 		m_freem(m);
43862ad0f7e9STom Jones 		ieee80211_release_node(ic, ni);
43872ad0f7e9STom Jones 		return;
43882ad0f7e9STom Jones 	}
43892ad0f7e9STom Jones #endif
43902ad0f7e9STom Jones 	if (ieee80211_radiotap_active_vap(vap)) {
43912ad0f7e9STom Jones 		struct iwx_rx_radiotap_header *tap = &sc->sc_rxtap;
43922ad0f7e9STom Jones 		uint16_t chan_flags;
43932ad0f7e9STom Jones 		int have_legacy_rate = 1;
43942ad0f7e9STom Jones 		uint8_t mcs, rate;
43952ad0f7e9STom Jones 
43962ad0f7e9STom Jones 		tap->wr_flags = 0;
43972ad0f7e9STom Jones 		if (is_shortpre)
43982ad0f7e9STom Jones 			tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
43992ad0f7e9STom Jones 		tap->wr_chan_freq =
44002ad0f7e9STom Jones 		    htole16(ic->ic_channels[chanidx].ic_freq);
44012ad0f7e9STom Jones 		chan_flags = ic->ic_channels[chanidx].ic_flags;
44022ad0f7e9STom Jones #if 0
44032ad0f7e9STom Jones 		if (ic->ic_curmode != IEEE80211_MODE_11N &&
44042ad0f7e9STom Jones 		    ic->ic_curmode != IEEE80211_MODE_11AC) {
44052ad0f7e9STom Jones 			chan_flags &= ~IEEE80211_CHAN_HT;
44062ad0f7e9STom Jones 			chan_flags &= ~IEEE80211_CHAN_40MHZ;
44072ad0f7e9STom Jones 		}
44082ad0f7e9STom Jones 		if (ic->ic_curmode != IEEE80211_MODE_11AC)
44092ad0f7e9STom Jones 			chan_flags &= ~IEEE80211_CHAN_VHT;
44102ad0f7e9STom Jones #else
44112ad0f7e9STom Jones 		chan_flags &= ~IEEE80211_CHAN_HT;
44122ad0f7e9STom Jones #endif
44132ad0f7e9STom Jones 		tap->wr_chan_flags = htole16(chan_flags);
44142ad0f7e9STom Jones 		tap->wr_dbm_antsignal = rssi;
44152ad0f7e9STom Jones 		tap->wr_dbm_antnoise = (int8_t)sc->sc_noise;
44162ad0f7e9STom Jones 		tap->wr_tsft = device_timestamp;
44172ad0f7e9STom Jones 
44182ad0f7e9STom Jones 		if (sc->sc_rate_n_flags_version >= 2) {
44192ad0f7e9STom Jones 			uint32_t mod_type = (rate_n_flags &
44202ad0f7e9STom Jones 			    IWX_RATE_MCS_MOD_TYPE_MSK);
44212ad0f7e9STom Jones 			const struct ieee80211_rateset *rs = NULL;
44222ad0f7e9STom Jones 			uint32_t ridx;
44232ad0f7e9STom Jones 			have_legacy_rate = (mod_type == IWX_RATE_MCS_CCK_MSK ||
44242ad0f7e9STom Jones 			    mod_type == IWX_RATE_MCS_LEGACY_OFDM_MSK);
44252ad0f7e9STom Jones 			mcs = (rate_n_flags & IWX_RATE_HT_MCS_CODE_MSK);
44262ad0f7e9STom Jones 			ridx = (rate_n_flags & IWX_RATE_LEGACY_RATE_MSK);
44272ad0f7e9STom Jones 			if (mod_type == IWX_RATE_MCS_CCK_MSK)
44282ad0f7e9STom Jones 				rs = &ieee80211_std_rateset_11b;
44292ad0f7e9STom Jones 			else if (mod_type == IWX_RATE_MCS_LEGACY_OFDM_MSK)
44302ad0f7e9STom Jones 				rs = &ieee80211_std_rateset_11a;
44312ad0f7e9STom Jones 			if (rs && ridx < rs->rs_nrates) {
44322ad0f7e9STom Jones 				rate = (rs->rs_rates[ridx] &
44332ad0f7e9STom Jones 				    IEEE80211_RATE_VAL);
44342ad0f7e9STom Jones 			} else
44352ad0f7e9STom Jones 				rate = 0;
44362ad0f7e9STom Jones 		} else {
44372ad0f7e9STom Jones 			have_legacy_rate = ((rate_n_flags &
44382ad0f7e9STom Jones 			    (IWX_RATE_MCS_HT_MSK_V1 |
44392ad0f7e9STom Jones 			    IWX_RATE_MCS_VHT_MSK_V1)) == 0);
44402ad0f7e9STom Jones 			mcs = (rate_n_flags &
44412ad0f7e9STom Jones 			    (IWX_RATE_HT_MCS_RATE_CODE_MSK_V1 |
44422ad0f7e9STom Jones 			    IWX_RATE_HT_MCS_NSS_MSK_V1));
44432ad0f7e9STom Jones 			rate = (rate_n_flags & IWX_RATE_LEGACY_RATE_MSK_V1);
44442ad0f7e9STom Jones 		}
44452ad0f7e9STom Jones 		if (!have_legacy_rate) {
44462ad0f7e9STom Jones 			tap->wr_rate = (0x80 | mcs);
44472ad0f7e9STom Jones 		} else {
44482ad0f7e9STom Jones 			switch (rate) {
44492ad0f7e9STom Jones 			/* CCK rates. */
44502ad0f7e9STom Jones 			case  10: tap->wr_rate =   2; break;
44512ad0f7e9STom Jones 			case  20: tap->wr_rate =   4; break;
44522ad0f7e9STom Jones 			case  55: tap->wr_rate =  11; break;
44532ad0f7e9STom Jones 			case 110: tap->wr_rate =  22; break;
44542ad0f7e9STom Jones 			/* OFDM rates. */
44552ad0f7e9STom Jones 			case 0xd: tap->wr_rate =  12; break;
44562ad0f7e9STom Jones 			case 0xf: tap->wr_rate =  18; break;
44572ad0f7e9STom Jones 			case 0x5: tap->wr_rate =  24; break;
44582ad0f7e9STom Jones 			case 0x7: tap->wr_rate =  36; break;
44592ad0f7e9STom Jones 			case 0x9: tap->wr_rate =  48; break;
44602ad0f7e9STom Jones 			case 0xb: tap->wr_rate =  72; break;
44612ad0f7e9STom Jones 			case 0x1: tap->wr_rate =  96; break;
44622ad0f7e9STom Jones 			case 0x3: tap->wr_rate = 108; break;
44632ad0f7e9STom Jones 			/* Unknown rate: should not happen. */
44642ad0f7e9STom Jones 			default:  tap->wr_rate =   0;
44652ad0f7e9STom Jones 			}
44662ad0f7e9STom Jones 			// XXX hack - this needs rebased with the new rate stuff anyway
44672ad0f7e9STom Jones 			tap->wr_rate = rate;
44682ad0f7e9STom Jones 		}
44692ad0f7e9STom Jones 	}
44702ad0f7e9STom Jones 
44712ad0f7e9STom Jones 	IWX_UNLOCK(sc);
44722ad0f7e9STom Jones 	if (ni == NULL) {
44732ad0f7e9STom Jones 		if (ieee80211_input_mimo_all(ic, m) == -1)
44742ad0f7e9STom Jones 			printf("%s:%d input_all returned -1\n", __func__, __LINE__);
44752ad0f7e9STom Jones 	} else {
44762ad0f7e9STom Jones 
44772ad0f7e9STom Jones 		if (ieee80211_input_mimo(ni, m) == -1)
44782ad0f7e9STom Jones 			printf("%s:%d input_all returned -1\n", __func__, __LINE__);
44792ad0f7e9STom Jones 		ieee80211_free_node(ni);
44802ad0f7e9STom Jones 	}
44812ad0f7e9STom Jones 	IWX_LOCK(sc);
44822ad0f7e9STom Jones }
44832ad0f7e9STom Jones 
44842ad0f7e9STom Jones static void
iwx_rx_mpdu_mq(struct iwx_softc * sc,struct mbuf * m,void * pktdata,size_t maxlen)44852ad0f7e9STom Jones iwx_rx_mpdu_mq(struct iwx_softc *sc, struct mbuf *m, void *pktdata,
44862ad0f7e9STom Jones     size_t maxlen)
44872ad0f7e9STom Jones {
44882ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
44892ad0f7e9STom Jones 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
44902ad0f7e9STom Jones 	struct ieee80211_node *ni = vap->iv_bss;
44912ad0f7e9STom Jones 	struct ieee80211_key *k;
44922ad0f7e9STom Jones 	struct ieee80211_rx_stats rxs;
44932ad0f7e9STom Jones 	struct iwx_rx_mpdu_desc *desc;
44942ad0f7e9STom Jones 	uint32_t len, hdrlen, rate_n_flags, device_timestamp;
44952ad0f7e9STom Jones 	int rssi;
44962ad0f7e9STom Jones 	uint8_t chanidx;
44972ad0f7e9STom Jones 	uint16_t phy_info;
44982ad0f7e9STom Jones 	size_t desc_size;
44992ad0f7e9STom Jones 	int pad = 0;
45002ad0f7e9STom Jones 
45012ad0f7e9STom Jones 	if (sc->sc_device_family >= IWX_DEVICE_FAMILY_AX210)
45022ad0f7e9STom Jones 		desc_size = sizeof(*desc);
45032ad0f7e9STom Jones 	else
45042ad0f7e9STom Jones 		desc_size = IWX_RX_DESC_SIZE_V1;
45052ad0f7e9STom Jones 
45062ad0f7e9STom Jones 	if (maxlen < desc_size) {
45072ad0f7e9STom Jones 		m_freem(m);
45082ad0f7e9STom Jones 		return; /* drop */
45092ad0f7e9STom Jones 	}
45102ad0f7e9STom Jones 
45112ad0f7e9STom Jones 	desc = (struct iwx_rx_mpdu_desc *)pktdata;
45122ad0f7e9STom Jones 
45132ad0f7e9STom Jones 	if (!(desc->status & htole16(IWX_RX_MPDU_RES_STATUS_CRC_OK)) ||
45142ad0f7e9STom Jones 	    !(desc->status & htole16(IWX_RX_MPDU_RES_STATUS_OVERRUN_OK))) {
45152ad0f7e9STom Jones 		printf("%s: Bad CRC or FIFO: 0x%08X\n", __func__, desc->status);
45162ad0f7e9STom Jones 		m_freem(m);
45172ad0f7e9STom Jones 		return; /* drop */
45182ad0f7e9STom Jones 	}
45192ad0f7e9STom Jones 
45202ad0f7e9STom Jones 	len = le16toh(desc->mpdu_len);
45212ad0f7e9STom Jones 	if (ic->ic_opmode == IEEE80211_M_MONITOR) {
45222ad0f7e9STom Jones 		/* Allow control frames in monitor mode. */
45232ad0f7e9STom Jones 		if (len < sizeof(struct ieee80211_frame_cts)) {
45242ad0f7e9STom Jones 			m_freem(m);
45252ad0f7e9STom Jones 			return;
45262ad0f7e9STom Jones 		}
45272ad0f7e9STom Jones 
45282ad0f7e9STom Jones 	} else if (len < sizeof(struct ieee80211_frame)) {
45292ad0f7e9STom Jones 		m_freem(m);
45302ad0f7e9STom Jones 		return;
45312ad0f7e9STom Jones 	}
45322ad0f7e9STom Jones 	if (len > maxlen - desc_size) {
45332ad0f7e9STom Jones 		m_freem(m);
45342ad0f7e9STom Jones 		return;
45352ad0f7e9STom Jones 	}
45362ad0f7e9STom Jones 
45372ad0f7e9STom Jones 	// TODO: arithmetic on a pointer to void is a GNU extension
45382ad0f7e9STom Jones 	m->m_data = (char *)pktdata + desc_size;
45392ad0f7e9STom Jones 	m->m_pkthdr.len = m->m_len = len;
45402ad0f7e9STom Jones 
45412ad0f7e9STom Jones 	/* Account for padding following the frame header. */
45422ad0f7e9STom Jones 	if (desc->mac_flags2 & IWX_RX_MPDU_MFLG2_PAD) {
45432ad0f7e9STom Jones 		struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
45442ad0f7e9STom Jones 		int type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
45452ad0f7e9STom Jones 		if (type == IEEE80211_FC0_TYPE_CTL) {
45462ad0f7e9STom Jones 			switch (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) {
45472ad0f7e9STom Jones 			case IEEE80211_FC0_SUBTYPE_CTS:
45482ad0f7e9STom Jones 				hdrlen = sizeof(struct ieee80211_frame_cts);
45492ad0f7e9STom Jones 				break;
45502ad0f7e9STom Jones 			case IEEE80211_FC0_SUBTYPE_ACK:
45512ad0f7e9STom Jones 				hdrlen = sizeof(struct ieee80211_frame_ack);
45522ad0f7e9STom Jones 				break;
45532ad0f7e9STom Jones 			default:
45542ad0f7e9STom Jones 				hdrlen = sizeof(struct ieee80211_frame_min);
45552ad0f7e9STom Jones 				break;
45562ad0f7e9STom Jones 			}
45572ad0f7e9STom Jones 		} else
45582ad0f7e9STom Jones 			hdrlen = ieee80211_hdrsize(wh);
45592ad0f7e9STom Jones 
45602ad0f7e9STom Jones 		if ((le16toh(desc->status) &
45612ad0f7e9STom Jones 		    IWX_RX_MPDU_RES_STATUS_SEC_ENC_MSK) ==
45622ad0f7e9STom Jones 		    IWX_RX_MPDU_RES_STATUS_SEC_CCM_ENC) {
45632ad0f7e9STom Jones 			// CCMP header length
45642ad0f7e9STom Jones 			hdrlen += 8;
45652ad0f7e9STom Jones 		}
45662ad0f7e9STom Jones 
45672ad0f7e9STom Jones 		memmove(m->m_data + 2, m->m_data, hdrlen);
45682ad0f7e9STom Jones 		m_adj(m, 2);
45692ad0f7e9STom Jones 
45702ad0f7e9STom Jones 	}
45712ad0f7e9STom Jones 
45722ad0f7e9STom Jones 	if ((le16toh(desc->status) &
45732ad0f7e9STom Jones 	    IWX_RX_MPDU_RES_STATUS_SEC_ENC_MSK) ==
45742ad0f7e9STom Jones 	    IWX_RX_MPDU_RES_STATUS_SEC_CCM_ENC) {
45752ad0f7e9STom Jones 		pad = 1;
45762ad0f7e9STom Jones 	}
45772ad0f7e9STom Jones 
45782ad0f7e9STom Jones //	/*
45792ad0f7e9STom Jones //	 * Hardware de-aggregates A-MSDUs and copies the same MAC header
45802ad0f7e9STom Jones //	 * in place for each subframe. But it leaves the 'A-MSDU present'
45812ad0f7e9STom Jones //	 * bit set in the frame header. We need to clear this bit ourselves.
45822ad0f7e9STom Jones //	 * (XXX This workaround is not required on AX200/AX201 devices that
45832ad0f7e9STom Jones //	 * have been tested by me, but it's unclear when this problem was
45842ad0f7e9STom Jones //	 * fixed in the hardware. It definitely affects the 9k generation.
45852ad0f7e9STom Jones //	 * Leaving this in place for now since some 9k/AX200 hybrids seem
45862ad0f7e9STom Jones //	 * to exist that we may eventually add support for.)
45872ad0f7e9STom Jones //	 *
45882ad0f7e9STom Jones //	 * And we must allow the same CCMP PN for subframes following the
45892ad0f7e9STom Jones //	 * first subframe. Otherwise they would be discarded as replays.
45902ad0f7e9STom Jones //	 */
45912ad0f7e9STom Jones 	if (desc->mac_flags2 & IWX_RX_MPDU_MFLG2_AMSDU) {
45922ad0f7e9STom Jones 		DPRINTF(("%s: === IWX_RX_MPDU_MFLG2_AMSDU\n", __func__));
45932ad0f7e9STom Jones //		struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
45942ad0f7e9STom Jones //		uint8_t subframe_idx = (desc->amsdu_info &
45952ad0f7e9STom Jones //		    IWX_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK);
45962ad0f7e9STom Jones //		if (subframe_idx > 0)
45972ad0f7e9STom Jones //			rxi.rxi_flags |= IEEE80211_RXI_HWDEC_SAME_PN;
45982ad0f7e9STom Jones //		if (ieee80211_has_qos(wh) && ieee80211_has_addr4(wh) &&
45992ad0f7e9STom Jones //		    m->m_len >= sizeof(struct ieee80211_qosframe_addr4)) {
46002ad0f7e9STom Jones //			struct ieee80211_qosframe_addr4 *qwh4 = mtod(m,
46012ad0f7e9STom Jones //			    struct ieee80211_qosframe_addr4 *);
46022ad0f7e9STom Jones //			qwh4->i_qos[0] &= htole16(~IEEE80211_QOS_AMSDU);
46032ad0f7e9STom Jones //		} else if (ieee80211_has_qos(wh) &&
46042ad0f7e9STom Jones //		    m->m_len >= sizeof(struct ieee80211_qosframe)) {
46052ad0f7e9STom Jones //			struct ieee80211_qosframe *qwh = mtod(m,
46062ad0f7e9STom Jones //			    struct ieee80211_qosframe *);
46072ad0f7e9STom Jones //			qwh->i_qos[0] &= htole16(~IEEE80211_QOS_AMSDU);
46082ad0f7e9STom Jones //		}
46092ad0f7e9STom Jones 	}
46102ad0f7e9STom Jones 
46112ad0f7e9STom Jones 	/*
46122ad0f7e9STom Jones 	 * Verify decryption before duplicate detection. The latter uses
46132ad0f7e9STom Jones 	 * the TID supplied in QoS frame headers and this TID is implicitly
46142ad0f7e9STom Jones 	 * verified as part of the CCMP nonce.
46152ad0f7e9STom Jones 	 */
46162ad0f7e9STom Jones 	k = ieee80211_crypto_get_txkey(ni, m);
46172ad0f7e9STom Jones 	if (k != NULL &&
46182ad0f7e9STom Jones 	    (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_AES_CCM) &&
46192ad0f7e9STom Jones 	    iwx_rx_hwdecrypt(sc, m, le16toh(desc->status)/*, &rxi*/)) {
46202ad0f7e9STom Jones 		DPRINTF(("%s: iwx_rx_hwdecrypt failed\n", __func__));
46212ad0f7e9STom Jones 		m_freem(m);
46222ad0f7e9STom Jones 		return;
46232ad0f7e9STom Jones 	}
46242ad0f7e9STom Jones 
46252ad0f7e9STom Jones 	if (sc->sc_device_family >= IWX_DEVICE_FAMILY_AX210) {
46262ad0f7e9STom Jones 		rate_n_flags = le32toh(desc->v3.rate_n_flags);
46272ad0f7e9STom Jones 		chanidx = desc->v3.channel;
46282ad0f7e9STom Jones 		device_timestamp = le32toh(desc->v3.gp2_on_air_rise);
46292ad0f7e9STom Jones 	} else {
46302ad0f7e9STom Jones 		rate_n_flags = le32toh(desc->v1.rate_n_flags);
46312ad0f7e9STom Jones 		chanidx = desc->v1.channel;
46322ad0f7e9STom Jones 		device_timestamp = le32toh(desc->v1.gp2_on_air_rise);
46332ad0f7e9STom Jones 	}
46342ad0f7e9STom Jones 
46352ad0f7e9STom Jones 	phy_info = le16toh(desc->phy_info);
46362ad0f7e9STom Jones 
46372ad0f7e9STom Jones 	rssi = iwx_rxmq_get_signal_strength(sc, desc);
46382ad0f7e9STom Jones 	rssi = (0 - IWX_MIN_DBM) + rssi;		/* normalize */
46392ad0f7e9STom Jones 	rssi = MIN(rssi, (IWX_MAX_DBM - IWX_MIN_DBM));	/* clip to max. 100% */
46402ad0f7e9STom Jones 
46412ad0f7e9STom Jones 	memset(&rxs, 0, sizeof(rxs));
46422ad0f7e9STom Jones 	rxs.r_flags |= IEEE80211_R_IEEE | IEEE80211_R_FREQ;
46432ad0f7e9STom Jones 	rxs.r_flags |= IEEE80211_R_BAND;
46442ad0f7e9STom Jones 	rxs.r_flags |= IEEE80211_R_NF | IEEE80211_R_RSSI;
46452ad0f7e9STom Jones 	rxs.r_flags |= IEEE80211_R_TSF32 | IEEE80211_R_TSF_START;
46462ad0f7e9STom Jones 
46472ad0f7e9STom Jones 	rxs.c_ieee = chanidx;
46482ad0f7e9STom Jones 	rxs.c_freq = ieee80211_ieee2mhz(rxs.c_ieee,
46492ad0f7e9STom Jones 	    chanidx <= 14 ? IEEE80211_CHAN_2GHZ : IEEE80211_CHAN_5GHZ);
46502ad0f7e9STom Jones 	rxs.c_band = chanidx <= 14 ? IEEE80211_CHAN_2GHZ : IEEE80211_CHAN_5GHZ;
46512ad0f7e9STom Jones 	rxs.c_rx_tsf = device_timestamp;
4652*be57603cSBjoern A. Zeeb 	rxs.c_chain = iwx_rxmq_get_chains(sc, desc);
4653*be57603cSBjoern A. Zeeb 	if (rxs.c_chain != 0)
4654*be57603cSBjoern A. Zeeb 		rxs.r_flags |= IEEE80211_R_C_CHAIN;
46552ad0f7e9STom Jones 
46562ad0f7e9STom Jones 	/* rssi is in 1/2db units */
46572ad0f7e9STom Jones 	rxs.c_rssi = rssi * 2;
46582ad0f7e9STom Jones 	rxs.c_nf = sc->sc_noise;
46592ad0f7e9STom Jones 
46602ad0f7e9STom Jones 	if (pad) {
46612ad0f7e9STom Jones 		rxs.c_pktflags |= IEEE80211_RX_F_DECRYPTED;
46622ad0f7e9STom Jones 		rxs.c_pktflags |= IEEE80211_RX_F_IV_STRIP;
46632ad0f7e9STom Jones 	}
46642ad0f7e9STom Jones 
46652ad0f7e9STom Jones 	if (ieee80211_add_rx_params(m, &rxs) == 0) {
46662ad0f7e9STom Jones 		printf("%s: ieee80211_add_rx_params failed\n", __func__);
46672ad0f7e9STom Jones 		return;
46682ad0f7e9STom Jones 	}
46692ad0f7e9STom Jones 
46702ad0f7e9STom Jones 	ieee80211_add_rx_params(m, &rxs);
46712ad0f7e9STom Jones 
46722ad0f7e9STom Jones #if 0
46732ad0f7e9STom Jones 	if (iwx_rx_reorder(sc, m, chanidx, desc,
46742ad0f7e9STom Jones 	    (phy_info & IWX_RX_MPDU_PHY_SHORT_PREAMBLE),
46752ad0f7e9STom Jones 	    rate_n_flags, device_timestamp, &rxi, ml))
46762ad0f7e9STom Jones 		return;
46772ad0f7e9STom Jones #endif
46782ad0f7e9STom Jones 
46792ad0f7e9STom Jones 	if (pad) {
46802ad0f7e9STom Jones #define TRIM 8
46812ad0f7e9STom Jones 		struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
46822ad0f7e9STom Jones 		hdrlen = ieee80211_hdrsize(wh);
46832ad0f7e9STom Jones 		memmove(m->m_data + TRIM, m->m_data, hdrlen);
46842ad0f7e9STom Jones 		m_adj(m, TRIM);
46852ad0f7e9STom Jones #undef TRIM
46862ad0f7e9STom Jones 	}
46872ad0f7e9STom Jones 
46882ad0f7e9STom Jones 	iwx_rx_frame(sc, m, chanidx, le16toh(desc->status),
46892ad0f7e9STom Jones 	    (phy_info & IWX_RX_MPDU_PHY_SHORT_PREAMBLE),
46902ad0f7e9STom Jones 	    rate_n_flags, device_timestamp, rssi);
46912ad0f7e9STom Jones }
46922ad0f7e9STom Jones 
46932ad0f7e9STom Jones static void
iwx_clear_tx_desc(struct iwx_softc * sc,struct iwx_tx_ring * ring,int idx)46942ad0f7e9STom Jones iwx_clear_tx_desc(struct iwx_softc *sc, struct iwx_tx_ring *ring, int idx)
46952ad0f7e9STom Jones {
46962ad0f7e9STom Jones 	struct iwx_tfh_tfd *desc = &ring->desc[idx];
46972ad0f7e9STom Jones 	uint8_t num_tbs = le16toh(desc->num_tbs) & 0x1f;
46982ad0f7e9STom Jones 	int i;
46992ad0f7e9STom Jones 
47002ad0f7e9STom Jones 	/* First TB is never cleared - it is bidirectional DMA data. */
47012ad0f7e9STom Jones 	for (i = 1; i < num_tbs; i++) {
47022ad0f7e9STom Jones 		struct iwx_tfh_tb *tb = &desc->tbs[i];
47032ad0f7e9STom Jones 		memset(tb, 0, sizeof(*tb));
47042ad0f7e9STom Jones 	}
47052ad0f7e9STom Jones 	desc->num_tbs = htole16(1);
47062ad0f7e9STom Jones 
47072ad0f7e9STom Jones 	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
47082ad0f7e9STom Jones 	    BUS_DMASYNC_PREWRITE);
47092ad0f7e9STom Jones }
47102ad0f7e9STom Jones 
47112ad0f7e9STom Jones static void
iwx_txd_done(struct iwx_softc * sc,struct iwx_tx_ring * ring,struct iwx_tx_data * txd)47122ad0f7e9STom Jones iwx_txd_done(struct iwx_softc *sc, struct iwx_tx_ring *ring,
47132ad0f7e9STom Jones     struct iwx_tx_data *txd)
47142ad0f7e9STom Jones {
47152ad0f7e9STom Jones 	bus_dmamap_sync(ring->data_dmat, txd->map, BUS_DMASYNC_POSTWRITE);
47162ad0f7e9STom Jones 	bus_dmamap_unload(ring->data_dmat, txd->map);
47172ad0f7e9STom Jones 
47182ad0f7e9STom Jones 	ieee80211_tx_complete(&txd->in->in_ni, txd->m, 0);
47192ad0f7e9STom Jones 	txd->m = NULL;
47202ad0f7e9STom Jones 	txd->in = NULL;
47212ad0f7e9STom Jones }
47222ad0f7e9STom Jones 
47232ad0f7e9STom Jones static void
iwx_txq_advance(struct iwx_softc * sc,struct iwx_tx_ring * ring,uint16_t idx)47242ad0f7e9STom Jones iwx_txq_advance(struct iwx_softc *sc, struct iwx_tx_ring *ring, uint16_t idx)
47252ad0f7e9STom Jones {
47262ad0f7e9STom Jones 	struct iwx_tx_data *txd;
47272ad0f7e9STom Jones 
47282ad0f7e9STom Jones 	while (ring->tail_hw != idx) {
47292ad0f7e9STom Jones 		txd = &ring->data[ring->tail];
47302ad0f7e9STom Jones 		if (txd->m != NULL) {
47312ad0f7e9STom Jones 			iwx_clear_tx_desc(sc, ring, ring->tail);
47322ad0f7e9STom Jones 			iwx_tx_update_byte_tbl(sc, ring, ring->tail, 0, 0);
47332ad0f7e9STom Jones 			iwx_txd_done(sc, ring, txd);
47342ad0f7e9STom Jones 			ring->queued--;
47352ad0f7e9STom Jones 			if (ring->queued < 0)
47362ad0f7e9STom Jones 				panic("caught negative queue count");
47372ad0f7e9STom Jones 		}
47382ad0f7e9STom Jones 		ring->tail = (ring->tail + 1) % IWX_TX_RING_COUNT;
47392ad0f7e9STom Jones 		ring->tail_hw = (ring->tail_hw + 1) % sc->max_tfd_queue_size;
47402ad0f7e9STom Jones 	}
47412ad0f7e9STom Jones }
47422ad0f7e9STom Jones 
47432ad0f7e9STom Jones static void
iwx_rx_tx_cmd(struct iwx_softc * sc,struct iwx_rx_packet * pkt,struct iwx_rx_data * data)47442ad0f7e9STom Jones iwx_rx_tx_cmd(struct iwx_softc *sc, struct iwx_rx_packet *pkt,
47452ad0f7e9STom Jones     struct iwx_rx_data *data)
47462ad0f7e9STom Jones {
47472ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
47482ad0f7e9STom Jones 	struct ifnet *ifp = IC2IFP(ic);
47492ad0f7e9STom Jones 	struct iwx_cmd_header *cmd_hdr = &pkt->hdr;
47502ad0f7e9STom Jones 	int qid = cmd_hdr->qid, status, txfail;
47512ad0f7e9STom Jones 	struct iwx_tx_ring *ring = &sc->txq[qid];
47522ad0f7e9STom Jones 	struct iwx_tx_resp *tx_resp = (void *)pkt->data;
47532ad0f7e9STom Jones 	uint32_t ssn;
47542ad0f7e9STom Jones 	uint32_t len = iwx_rx_packet_len(pkt);
47552ad0f7e9STom Jones 	int idx = cmd_hdr->idx;
47562ad0f7e9STom Jones 	struct iwx_tx_data *txd = &ring->data[idx];
47572ad0f7e9STom Jones 	struct mbuf *m = txd->m;
47582ad0f7e9STom Jones 
47592ad0f7e9STom Jones 	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);
47602ad0f7e9STom Jones 
47612ad0f7e9STom Jones 	/* Sanity checks. */
47622ad0f7e9STom Jones 	if (sizeof(*tx_resp) > len)
47632ad0f7e9STom Jones 		return;
47642ad0f7e9STom Jones 	if (qid < IWX_FIRST_AGG_TX_QUEUE && tx_resp->frame_count > 1)
47652ad0f7e9STom Jones 		return;
47662ad0f7e9STom Jones 	if (qid >= IWX_FIRST_AGG_TX_QUEUE && sizeof(*tx_resp) + sizeof(ssn) +
47672ad0f7e9STom Jones 	    tx_resp->frame_count * sizeof(tx_resp->status) > len)
47682ad0f7e9STom Jones 		return;
47692ad0f7e9STom Jones 
47702ad0f7e9STom Jones 	sc->sc_tx_timer[qid] = 0;
47712ad0f7e9STom Jones 
47722ad0f7e9STom Jones 	if (tx_resp->frame_count > 1) /* A-MPDU */
47732ad0f7e9STom Jones 		return;
47742ad0f7e9STom Jones 
47752ad0f7e9STom Jones 	status = le16toh(tx_resp->status.status) & IWX_TX_STATUS_MSK;
47762ad0f7e9STom Jones 	txfail = (status != IWX_TX_STATUS_SUCCESS &&
47772ad0f7e9STom Jones 	    status != IWX_TX_STATUS_DIRECT_DONE);
47782ad0f7e9STom Jones 
47792ad0f7e9STom Jones #ifdef __not_yet__
47802ad0f7e9STom Jones 	/* TODO: Replace accounting below with ieee80211_tx_complete() */
47812ad0f7e9STom Jones 	ieee80211_tx_complete(&in->in_ni, m, txfail);
47822ad0f7e9STom Jones #else
47832ad0f7e9STom Jones 	if (txfail)
47842ad0f7e9STom Jones 		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
47852ad0f7e9STom Jones 	else {
47862ad0f7e9STom Jones 		if_inc_counter(ifp, IFCOUNTER_OBYTES, m->m_pkthdr.len);
47872ad0f7e9STom Jones 		if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
47882ad0f7e9STom Jones 		if (m->m_flags & M_MCAST)
47892ad0f7e9STom Jones 			if_inc_counter(ifp, IFCOUNTER_OMCASTS, 1);
47902ad0f7e9STom Jones 	}
47912ad0f7e9STom Jones #endif
47922ad0f7e9STom Jones 	/*
47932ad0f7e9STom Jones 	 * On hardware supported by iwx(4) the SSN counter corresponds
47942ad0f7e9STom Jones 	 * to a Tx ring index rather than a sequence number.
47952ad0f7e9STom Jones 	 * Frames up to this index (non-inclusive) can now be freed.
47962ad0f7e9STom Jones 	 */
47972ad0f7e9STom Jones 	memcpy(&ssn, &tx_resp->status + tx_resp->frame_count, sizeof(ssn));
47982ad0f7e9STom Jones 	ssn = le32toh(ssn);
47992ad0f7e9STom Jones 	if (ssn < sc->max_tfd_queue_size) {
48002ad0f7e9STom Jones 		iwx_txq_advance(sc, ring, ssn);
48012ad0f7e9STom Jones 		iwx_clear_oactive(sc, ring);
48022ad0f7e9STom Jones 	}
48032ad0f7e9STom Jones }
48042ad0f7e9STom Jones 
48052ad0f7e9STom Jones static void
iwx_clear_oactive(struct iwx_softc * sc,struct iwx_tx_ring * ring)48062ad0f7e9STom Jones iwx_clear_oactive(struct iwx_softc *sc, struct iwx_tx_ring *ring)
48072ad0f7e9STom Jones {
48082ad0f7e9STom Jones 	if (ring->queued < iwx_lomark) {
48092ad0f7e9STom Jones 		sc->qfullmsk &= ~(1 << ring->qid);
48102ad0f7e9STom Jones 		if (sc->qfullmsk == 0 /* && ifq_is_oactive(&ifp->if_snd) */) {
48112ad0f7e9STom Jones 			/*
48122ad0f7e9STom Jones 			 * Well, we're in interrupt context, but then again
48132ad0f7e9STom Jones 			 * I guess net80211 does all sorts of stunts in
48142ad0f7e9STom Jones 			 * interrupt context, so maybe this is no biggie.
48152ad0f7e9STom Jones 			 */
48162ad0f7e9STom Jones 			iwx_start(sc);
48172ad0f7e9STom Jones 		}
48182ad0f7e9STom Jones 	}
48192ad0f7e9STom Jones }
48202ad0f7e9STom Jones 
48212ad0f7e9STom Jones static void
iwx_rx_compressed_ba(struct iwx_softc * sc,struct iwx_rx_packet * pkt)48222ad0f7e9STom Jones iwx_rx_compressed_ba(struct iwx_softc *sc, struct iwx_rx_packet *pkt)
48232ad0f7e9STom Jones {
48242ad0f7e9STom Jones 	struct iwx_compressed_ba_notif *ba_res = (void *)pkt->data;
48252ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
48262ad0f7e9STom Jones 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
48272ad0f7e9STom Jones 	struct iwx_node *in = IWX_NODE(vap->iv_bss);
48282ad0f7e9STom Jones 	struct ieee80211_node *ni = &in->in_ni;
48292ad0f7e9STom Jones 	struct iwx_tx_ring *ring;
48302ad0f7e9STom Jones 	uint16_t i, tfd_cnt, ra_tid_cnt, idx;
48312ad0f7e9STom Jones 	int qid;
48322ad0f7e9STom Jones 
48332ad0f7e9STom Jones //	if (ic->ic_state != IEEE80211_S_RUN)
48342ad0f7e9STom Jones //		return;
48352ad0f7e9STom Jones 
48362ad0f7e9STom Jones 	if (iwx_rx_packet_payload_len(pkt) < sizeof(*ba_res))
48372ad0f7e9STom Jones 		return;
48382ad0f7e9STom Jones 
48392ad0f7e9STom Jones 	if (ba_res->sta_id != IWX_STATION_ID)
48402ad0f7e9STom Jones 		return;
48412ad0f7e9STom Jones 
48422ad0f7e9STom Jones 	in = (void *)ni;
48432ad0f7e9STom Jones 
48442ad0f7e9STom Jones 	tfd_cnt = le16toh(ba_res->tfd_cnt);
48452ad0f7e9STom Jones 	ra_tid_cnt = le16toh(ba_res->ra_tid_cnt);
48462ad0f7e9STom Jones 	if (!tfd_cnt || iwx_rx_packet_payload_len(pkt) < (sizeof(*ba_res) +
48472ad0f7e9STom Jones 	    sizeof(ba_res->ra_tid[0]) * ra_tid_cnt +
48482ad0f7e9STom Jones 	    sizeof(ba_res->tfd[0]) * tfd_cnt))
48492ad0f7e9STom Jones 		return;
48502ad0f7e9STom Jones 
48512ad0f7e9STom Jones 	for (i = 0; i < tfd_cnt; i++) {
48522ad0f7e9STom Jones 		struct iwx_compressed_ba_tfd *ba_tfd = &ba_res->tfd[i];
48532ad0f7e9STom Jones 		uint8_t tid;
48542ad0f7e9STom Jones 
48552ad0f7e9STom Jones 		tid = ba_tfd->tid;
48562ad0f7e9STom Jones 		if (tid >= nitems(sc->aggqid))
48572ad0f7e9STom Jones 			continue;
48582ad0f7e9STom Jones 
48592ad0f7e9STom Jones 		qid = sc->aggqid[tid];
48602ad0f7e9STom Jones 		if (qid != htole16(ba_tfd->q_num))
48612ad0f7e9STom Jones 			continue;
48622ad0f7e9STom Jones 
48632ad0f7e9STom Jones 		ring = &sc->txq[qid];
48642ad0f7e9STom Jones 
48652ad0f7e9STom Jones #if 0
48662ad0f7e9STom Jones 		ba = &ni->ni_tx_ba[tid];
48672ad0f7e9STom Jones 		if (ba->ba_state != IEEE80211_BA_AGREED)
48682ad0f7e9STom Jones 			continue;
48692ad0f7e9STom Jones #endif
48702ad0f7e9STom Jones 		idx = le16toh(ba_tfd->tfd_index);
48712ad0f7e9STom Jones 		sc->sc_tx_timer[qid] = 0;
48722ad0f7e9STom Jones 		iwx_txq_advance(sc, ring, idx);
48732ad0f7e9STom Jones 		iwx_clear_oactive(sc, ring);
48742ad0f7e9STom Jones 	}
48752ad0f7e9STom Jones }
48762ad0f7e9STom Jones 
48772ad0f7e9STom Jones static void
iwx_rx_bmiss(struct iwx_softc * sc,struct iwx_rx_packet * pkt,struct iwx_rx_data * data)48782ad0f7e9STom Jones iwx_rx_bmiss(struct iwx_softc *sc, struct iwx_rx_packet *pkt,
48792ad0f7e9STom Jones     struct iwx_rx_data *data)
48802ad0f7e9STom Jones {
48812ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
48822ad0f7e9STom Jones 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
48832ad0f7e9STom Jones 	struct iwx_missed_beacons_notif *mbn = (void *)pkt->data;
48842ad0f7e9STom Jones 	uint32_t missed;
48852ad0f7e9STom Jones 
48862ad0f7e9STom Jones 	if ((ic->ic_opmode != IEEE80211_M_STA) ||
48872ad0f7e9STom Jones 	    (vap->iv_state != IEEE80211_S_RUN))
48882ad0f7e9STom Jones 		return;
48892ad0f7e9STom Jones 
48902ad0f7e9STom Jones 	bus_dmamap_sync(sc->rxq.data_dmat, data->map,
48912ad0f7e9STom Jones 	    BUS_DMASYNC_POSTREAD);
48922ad0f7e9STom Jones 
48932ad0f7e9STom Jones 	missed = le32toh(mbn->consec_missed_beacons_since_last_rx);
48942ad0f7e9STom Jones 	if (missed > vap->iv_bmissthreshold) {
48952ad0f7e9STom Jones 		ieee80211_beacon_miss(ic);
48962ad0f7e9STom Jones 	}
48972ad0f7e9STom Jones 
48982ad0f7e9STom Jones }
48992ad0f7e9STom Jones 
49002ad0f7e9STom Jones static int
iwx_binding_cmd(struct iwx_softc * sc,struct iwx_node * in,uint32_t action)49012ad0f7e9STom Jones iwx_binding_cmd(struct iwx_softc *sc, struct iwx_node *in, uint32_t action)
49022ad0f7e9STom Jones {
49032ad0f7e9STom Jones 	struct iwx_binding_cmd cmd;
49042ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
49052ad0f7e9STom Jones 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
49062ad0f7e9STom Jones 	struct iwx_vap *ivp = IWX_VAP(vap);
49072ad0f7e9STom Jones 	struct iwx_phy_ctxt *phyctxt = ivp->phy_ctxt;
49082ad0f7e9STom Jones 	uint32_t mac_id = IWX_FW_CMD_ID_AND_COLOR(in->in_id, in->in_color);
49092ad0f7e9STom Jones 	int i, err, active = (sc->sc_flags & IWX_FLAG_BINDING_ACTIVE);
49102ad0f7e9STom Jones 	uint32_t status;
49112ad0f7e9STom Jones 
49122ad0f7e9STom Jones 	if (action == IWX_FW_CTXT_ACTION_ADD && active)
49132ad0f7e9STom Jones 		panic("binding already added");
49142ad0f7e9STom Jones 	if (action == IWX_FW_CTXT_ACTION_REMOVE && !active)
49152ad0f7e9STom Jones 		panic("binding already removed");
49162ad0f7e9STom Jones 
49172ad0f7e9STom Jones 	if (phyctxt == NULL) /* XXX race with iwx_stop() */
49182ad0f7e9STom Jones 		return EINVAL;
49192ad0f7e9STom Jones 
49202ad0f7e9STom Jones 	memset(&cmd, 0, sizeof(cmd));
49212ad0f7e9STom Jones 
49222ad0f7e9STom Jones 	cmd.id_and_color
49232ad0f7e9STom Jones 	    = htole32(IWX_FW_CMD_ID_AND_COLOR(phyctxt->id, phyctxt->color));
49242ad0f7e9STom Jones 	cmd.action = htole32(action);
49252ad0f7e9STom Jones 	cmd.phy = htole32(IWX_FW_CMD_ID_AND_COLOR(phyctxt->id, phyctxt->color));
49262ad0f7e9STom Jones 
49272ad0f7e9STom Jones 	cmd.macs[0] = htole32(mac_id);
49282ad0f7e9STom Jones 	for (i = 1; i < IWX_MAX_MACS_IN_BINDING; i++)
49292ad0f7e9STom Jones 		cmd.macs[i] = htole32(IWX_FW_CTXT_INVALID);
49302ad0f7e9STom Jones 
49312ad0f7e9STom Jones 	if (IEEE80211_IS_CHAN_2GHZ(phyctxt->channel) ||
49322ad0f7e9STom Jones 	    !isset(sc->sc_enabled_capa, IWX_UCODE_TLV_CAPA_CDB_SUPPORT))
49332ad0f7e9STom Jones 		cmd.lmac_id = htole32(IWX_LMAC_24G_INDEX);
49342ad0f7e9STom Jones 	else
49352ad0f7e9STom Jones 		cmd.lmac_id = htole32(IWX_LMAC_5G_INDEX);
49362ad0f7e9STom Jones 
49372ad0f7e9STom Jones 	status = 0;
49382ad0f7e9STom Jones 	err = iwx_send_cmd_pdu_status(sc, IWX_BINDING_CONTEXT_CMD, sizeof(cmd),
49392ad0f7e9STom Jones 	    &cmd, &status);
49402ad0f7e9STom Jones 	if (err == 0 && status != 0)
49412ad0f7e9STom Jones 		err = EIO;
49422ad0f7e9STom Jones 
49432ad0f7e9STom Jones 	return err;
49442ad0f7e9STom Jones }
49452ad0f7e9STom Jones 
49462ad0f7e9STom Jones static uint8_t
iwx_get_vht_ctrl_pos(struct ieee80211com * ic,struct ieee80211_channel * chan)49472ad0f7e9STom Jones iwx_get_vht_ctrl_pos(struct ieee80211com *ic, struct ieee80211_channel *chan)
49482ad0f7e9STom Jones {
49492ad0f7e9STom Jones 	int ctlchan = ieee80211_chan2ieee(ic, chan);
49502ad0f7e9STom Jones 	int midpoint = chan->ic_vht_ch_freq1;
49512ad0f7e9STom Jones 
49522ad0f7e9STom Jones 	/*
49532ad0f7e9STom Jones 	 * The FW is expected to check the control channel position only
49542ad0f7e9STom Jones 	 * when in HT/VHT and the channel width is not 20MHz. Return
49552ad0f7e9STom Jones 	 * this value as the default one:
49562ad0f7e9STom Jones 	 */
49572ad0f7e9STom Jones 	uint8_t pos = IWX_PHY_VHT_CTRL_POS_1_BELOW;
49582ad0f7e9STom Jones 
49592ad0f7e9STom Jones 	switch (ctlchan - midpoint) {
49602ad0f7e9STom Jones 	case -6:
49612ad0f7e9STom Jones 		pos = IWX_PHY_VHT_CTRL_POS_2_BELOW;
49622ad0f7e9STom Jones 		break;
49632ad0f7e9STom Jones 	case -2:
49642ad0f7e9STom Jones 		pos = IWX_PHY_VHT_CTRL_POS_1_BELOW;
49652ad0f7e9STom Jones 		break;
49662ad0f7e9STom Jones 	case 2:
49672ad0f7e9STom Jones 		pos = IWX_PHY_VHT_CTRL_POS_1_ABOVE;
49682ad0f7e9STom Jones 		break;
49692ad0f7e9STom Jones 	case 6:
49702ad0f7e9STom Jones 		pos = IWX_PHY_VHT_CTRL_POS_2_ABOVE;
49712ad0f7e9STom Jones 		break;
49722ad0f7e9STom Jones 	default:
49732ad0f7e9STom Jones 		break;
49742ad0f7e9STom Jones 	}
49752ad0f7e9STom Jones 
49762ad0f7e9STom Jones 	return pos;
49772ad0f7e9STom Jones }
49782ad0f7e9STom Jones 
49792ad0f7e9STom Jones static int
iwx_phy_ctxt_cmd_uhb_v3_v4(struct iwx_softc * sc,struct iwx_phy_ctxt * ctxt,uint8_t chains_static,uint8_t chains_dynamic,uint32_t action,uint8_t sco,uint8_t vht_chan_width,int cmdver)49802ad0f7e9STom Jones iwx_phy_ctxt_cmd_uhb_v3_v4(struct iwx_softc *sc, struct iwx_phy_ctxt *ctxt,
49812ad0f7e9STom Jones     uint8_t chains_static, uint8_t chains_dynamic, uint32_t action, uint8_t sco,
49822ad0f7e9STom Jones     uint8_t vht_chan_width, int cmdver)
49832ad0f7e9STom Jones {
49842ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
49852ad0f7e9STom Jones 	struct iwx_phy_context_cmd_uhb cmd;
49862ad0f7e9STom Jones 	uint8_t active_cnt, idle_cnt;
49872ad0f7e9STom Jones 	struct ieee80211_channel *chan = ctxt->channel;
49882ad0f7e9STom Jones 
49892ad0f7e9STom Jones 	memset(&cmd, 0, sizeof(cmd));
49902ad0f7e9STom Jones 	cmd.id_and_color = htole32(IWX_FW_CMD_ID_AND_COLOR(ctxt->id,
49912ad0f7e9STom Jones 	    ctxt->color));
49922ad0f7e9STom Jones 	cmd.action = htole32(action);
49932ad0f7e9STom Jones 
49942ad0f7e9STom Jones 	if (IEEE80211_IS_CHAN_2GHZ(chan) ||
49952ad0f7e9STom Jones 	    !isset(sc->sc_enabled_capa, IWX_UCODE_TLV_CAPA_CDB_SUPPORT))
49962ad0f7e9STom Jones 		cmd.lmac_id = htole32(IWX_LMAC_24G_INDEX);
49972ad0f7e9STom Jones 	else
49982ad0f7e9STom Jones 		cmd.lmac_id = htole32(IWX_LMAC_5G_INDEX);
49992ad0f7e9STom Jones 
50002ad0f7e9STom Jones 	cmd.ci.band = IEEE80211_IS_CHAN_2GHZ(chan) ?
50012ad0f7e9STom Jones 	    IWX_PHY_BAND_24 : IWX_PHY_BAND_5;
50022ad0f7e9STom Jones 	cmd.ci.channel = htole32(ieee80211_chan2ieee(ic, chan));
50032ad0f7e9STom Jones 
50042ad0f7e9STom Jones 	if (IEEE80211_IS_CHAN_VHT80(chan)) {
50052ad0f7e9STom Jones 		cmd.ci.ctrl_pos = iwx_get_vht_ctrl_pos(ic, chan);
50062ad0f7e9STom Jones 		cmd.ci.width = IWX_PHY_VHT_CHANNEL_MODE80;
50072ad0f7e9STom Jones 	} else if (IEEE80211_IS_CHAN_HT40(chan)) {
50082ad0f7e9STom Jones 		cmd.ci.width = IWX_PHY_VHT_CHANNEL_MODE40;
50092ad0f7e9STom Jones 		if (IEEE80211_IS_CHAN_HT40D(chan))
50102ad0f7e9STom Jones 			cmd.ci.ctrl_pos = IWX_PHY_VHT_CTRL_POS_1_ABOVE;
50112ad0f7e9STom Jones 		else
50122ad0f7e9STom Jones 			cmd.ci.ctrl_pos = IWX_PHY_VHT_CTRL_POS_1_BELOW;
50132ad0f7e9STom Jones 	} else {
50142ad0f7e9STom Jones 		cmd.ci.width = IWX_PHY_VHT_CHANNEL_MODE20;
50152ad0f7e9STom Jones 		cmd.ci.ctrl_pos = IWX_PHY_VHT_CTRL_POS_1_BELOW;
50162ad0f7e9STom Jones 	}
50172ad0f7e9STom Jones 
50182ad0f7e9STom Jones 	if (cmdver < 4 && iwx_lookup_cmd_ver(sc, IWX_DATA_PATH_GROUP,
50192ad0f7e9STom Jones 	    IWX_RLC_CONFIG_CMD) != 2) {
50202ad0f7e9STom Jones 		idle_cnt = chains_static;
50212ad0f7e9STom Jones 		active_cnt = chains_dynamic;
50222ad0f7e9STom Jones 		cmd.rxchain_info = htole32(iwx_fw_valid_rx_ant(sc) <<
50232ad0f7e9STom Jones 		    IWX_PHY_RX_CHAIN_VALID_POS);
50242ad0f7e9STom Jones 		cmd.rxchain_info |= htole32(idle_cnt <<
50252ad0f7e9STom Jones 		    IWX_PHY_RX_CHAIN_CNT_POS);
50262ad0f7e9STom Jones 		cmd.rxchain_info |= htole32(active_cnt <<
50272ad0f7e9STom Jones 		    IWX_PHY_RX_CHAIN_MIMO_CNT_POS);
50282ad0f7e9STom Jones 	}
50292ad0f7e9STom Jones 
50302ad0f7e9STom Jones 	return iwx_send_cmd_pdu(sc, IWX_PHY_CONTEXT_CMD, 0, sizeof(cmd), &cmd);
50312ad0f7e9STom Jones }
50322ad0f7e9STom Jones 
50332ad0f7e9STom Jones #if 0
50342ad0f7e9STom Jones int
50352ad0f7e9STom Jones iwx_phy_ctxt_cmd_v3_v4(struct iwx_softc *sc, struct iwx_phy_ctxt *ctxt,
50362ad0f7e9STom Jones     uint8_t chains_static, uint8_t chains_dynamic, uint32_t action, uint8_t sco,
50372ad0f7e9STom Jones     uint8_t vht_chan_width, int cmdver)
50382ad0f7e9STom Jones {
50392ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
50402ad0f7e9STom Jones 	struct iwx_phy_context_cmd cmd;
50412ad0f7e9STom Jones 	uint8_t active_cnt, idle_cnt;
50422ad0f7e9STom Jones 	struct ieee80211_channel *chan = ctxt->channel;
50432ad0f7e9STom Jones 
50442ad0f7e9STom Jones 	memset(&cmd, 0, sizeof(cmd));
50452ad0f7e9STom Jones 	cmd.id_and_color = htole32(IWX_FW_CMD_ID_AND_COLOR(ctxt->id,
50462ad0f7e9STom Jones 	    ctxt->color));
50472ad0f7e9STom Jones 	cmd.action = htole32(action);
50482ad0f7e9STom Jones 
50492ad0f7e9STom Jones 	if (IEEE80211_IS_CHAN_2GHZ(ctxt->channel) ||
50502ad0f7e9STom Jones 	    !isset(sc->sc_enabled_capa, IWX_UCODE_TLV_CAPA_CDB_SUPPORT))
50512ad0f7e9STom Jones 		cmd.lmac_id = htole32(IWX_LMAC_24G_INDEX);
50522ad0f7e9STom Jones 	else
50532ad0f7e9STom Jones 		cmd.lmac_id = htole32(IWX_LMAC_5G_INDEX);
50542ad0f7e9STom Jones 
50552ad0f7e9STom Jones 	cmd.ci.band = IEEE80211_IS_CHAN_2GHZ(chan) ?
50562ad0f7e9STom Jones 	    IWX_PHY_BAND_24 : IWX_PHY_BAND_5;
50572ad0f7e9STom Jones 	cmd.ci.channel = ieee80211_chan2ieee(ic, chan);
50582ad0f7e9STom Jones 	if (vht_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_80) {
50592ad0f7e9STom Jones 		cmd.ci.ctrl_pos = iwx_get_vht_ctrl_pos(ic, chan);
50602ad0f7e9STom Jones 		cmd.ci.width = IWX_PHY_VHT_CHANNEL_MODE80;
50612ad0f7e9STom Jones 	} else if (chan->ic_flags & IEEE80211_CHAN_40MHZ) {
50622ad0f7e9STom Jones 		if (sco == IEEE80211_HTOP0_SCO_SCA) {
50632ad0f7e9STom Jones 			/* secondary chan above -> control chan below */
50642ad0f7e9STom Jones 			cmd.ci.ctrl_pos = IWX_PHY_VHT_CTRL_POS_1_BELOW;
50652ad0f7e9STom Jones 			cmd.ci.width = IWX_PHY_VHT_CHANNEL_MODE40;
50662ad0f7e9STom Jones 		} else if (sco == IEEE80211_HTOP0_SCO_SCB) {
50672ad0f7e9STom Jones 			/* secondary chan below -> control chan above */
50682ad0f7e9STom Jones 			cmd.ci.ctrl_pos = IWX_PHY_VHT_CTRL_POS_1_ABOVE;
50692ad0f7e9STom Jones 			cmd.ci.width = IWX_PHY_VHT_CHANNEL_MODE40;
50702ad0f7e9STom Jones 		} else {
50712ad0f7e9STom Jones 			cmd.ci.width = IWX_PHY_VHT_CHANNEL_MODE20;
50722ad0f7e9STom Jones 			cmd.ci.ctrl_pos = IWX_PHY_VHT_CTRL_POS_1_BELOW;
50732ad0f7e9STom Jones 		}
50742ad0f7e9STom Jones 	} else {
50752ad0f7e9STom Jones 		cmd.ci.width = IWX_PHY_VHT_CHANNEL_MODE20;
50762ad0f7e9STom Jones 		cmd.ci.ctrl_pos = IWX_PHY_VHT_CTRL_POS_1_BELOW;
50772ad0f7e9STom Jones 	}
50782ad0f7e9STom Jones 
50792ad0f7e9STom Jones 	if (cmdver < 4 && iwx_lookup_cmd_ver(sc, IWX_DATA_PATH_GROUP,
50802ad0f7e9STom Jones 	    IWX_RLC_CONFIG_CMD) != 2) {
50812ad0f7e9STom Jones 		idle_cnt = chains_static;
50822ad0f7e9STom Jones 		active_cnt = chains_dynamic;
50832ad0f7e9STom Jones 		cmd.rxchain_info = htole32(iwx_fw_valid_rx_ant(sc) <<
50842ad0f7e9STom Jones 		    IWX_PHY_RX_CHAIN_VALID_POS);
50852ad0f7e9STom Jones 		cmd.rxchain_info |= htole32(idle_cnt <<
50862ad0f7e9STom Jones 		    IWX_PHY_RX_CHAIN_CNT_POS);
50872ad0f7e9STom Jones 		cmd.rxchain_info |= htole32(active_cnt <<
50882ad0f7e9STom Jones 		    IWX_PHY_RX_CHAIN_MIMO_CNT_POS);
50892ad0f7e9STom Jones 	}
50902ad0f7e9STom Jones 
50912ad0f7e9STom Jones 	return iwx_send_cmd_pdu(sc, IWX_PHY_CONTEXT_CMD, 0, sizeof(cmd), &cmd);
50922ad0f7e9STom Jones }
50932ad0f7e9STom Jones #endif
50942ad0f7e9STom Jones 
50952ad0f7e9STom Jones static int
iwx_phy_ctxt_cmd(struct iwx_softc * sc,struct iwx_phy_ctxt * ctxt,uint8_t chains_static,uint8_t chains_dynamic,uint32_t action,uint32_t apply_time,uint8_t sco,uint8_t vht_chan_width)50962ad0f7e9STom Jones iwx_phy_ctxt_cmd(struct iwx_softc *sc, struct iwx_phy_ctxt *ctxt,
50972ad0f7e9STom Jones     uint8_t chains_static, uint8_t chains_dynamic, uint32_t action,
50982ad0f7e9STom Jones     uint32_t apply_time, uint8_t sco, uint8_t vht_chan_width)
50992ad0f7e9STom Jones {
51002ad0f7e9STom Jones 	int cmdver;
51012ad0f7e9STom Jones 
51022ad0f7e9STom Jones 	cmdver = iwx_lookup_cmd_ver(sc, IWX_LONG_GROUP, IWX_PHY_CONTEXT_CMD);
51032ad0f7e9STom Jones 	if (cmdver != 3 && cmdver != 4) {
51042ad0f7e9STom Jones 		printf("%s: firmware does not support phy-context-cmd v3/v4\n",
51052ad0f7e9STom Jones 		    DEVNAME(sc));
51062ad0f7e9STom Jones 		return ENOTSUP;
51072ad0f7e9STom Jones 	}
51082ad0f7e9STom Jones 
51092ad0f7e9STom Jones 	/*
51102ad0f7e9STom Jones 	 * Intel increased the size of the fw_channel_info struct and neglected
51112ad0f7e9STom Jones 	 * to bump the phy_context_cmd struct, which contains an fw_channel_info
51122ad0f7e9STom Jones 	 * member in the middle.
51132ad0f7e9STom Jones 	 * To keep things simple we use a separate function to handle the larger
51142ad0f7e9STom Jones 	 * variant of the phy context command.
51152ad0f7e9STom Jones 	 */
51162ad0f7e9STom Jones 	if (isset(sc->sc_enabled_capa, IWX_UCODE_TLV_CAPA_ULTRA_HB_CHANNELS)) {
51172ad0f7e9STom Jones 		return iwx_phy_ctxt_cmd_uhb_v3_v4(sc, ctxt, chains_static,
51182ad0f7e9STom Jones 		    chains_dynamic, action, sco, vht_chan_width, cmdver);
51192ad0f7e9STom Jones 	} else
51202ad0f7e9STom Jones 		panic("Unsupported old hardware contact thj@");
51212ad0f7e9STom Jones 
51222ad0f7e9STom Jones #if 0
51232ad0f7e9STom Jones 	return iwx_phy_ctxt_cmd_v3_v4(sc, ctxt, chains_static, chains_dynamic,
51242ad0f7e9STom Jones 	    action, sco, vht_chan_width, cmdver);
51252ad0f7e9STom Jones #endif
51262ad0f7e9STom Jones }
51272ad0f7e9STom Jones 
51282ad0f7e9STom Jones static int
iwx_send_cmd(struct iwx_softc * sc,struct iwx_host_cmd * hcmd)51292ad0f7e9STom Jones iwx_send_cmd(struct iwx_softc *sc, struct iwx_host_cmd *hcmd)
51302ad0f7e9STom Jones {
51312ad0f7e9STom Jones 	struct iwx_tx_ring *ring = &sc->txq[IWX_DQA_CMD_QUEUE];
51322ad0f7e9STom Jones 	struct iwx_tfh_tfd *desc;
51332ad0f7e9STom Jones 	struct iwx_tx_data *txdata;
51342ad0f7e9STom Jones 	struct iwx_device_cmd *cmd;
51352ad0f7e9STom Jones 	struct mbuf *m;
51362ad0f7e9STom Jones 	bus_addr_t paddr;
51372ad0f7e9STom Jones 	uint64_t addr;
51382ad0f7e9STom Jones 	int err = 0, i, paylen, off/*, s*/;
51392ad0f7e9STom Jones 	int idx, code, async, group_id;
51402ad0f7e9STom Jones 	size_t hdrlen, datasz;
51412ad0f7e9STom Jones 	uint8_t *data;
51422ad0f7e9STom Jones 	int generation = sc->sc_generation;
51432ad0f7e9STom Jones 	bus_dma_segment_t seg[10];
51442ad0f7e9STom Jones 	int nsegs;
51452ad0f7e9STom Jones 
51462ad0f7e9STom Jones 	code = hcmd->id;
51472ad0f7e9STom Jones 	async = hcmd->flags & IWX_CMD_ASYNC;
51482ad0f7e9STom Jones 	idx = ring->cur;
51492ad0f7e9STom Jones 
51502ad0f7e9STom Jones 	for (i = 0, paylen = 0; i < nitems(hcmd->len); i++) {
51512ad0f7e9STom Jones 		paylen += hcmd->len[i];
51522ad0f7e9STom Jones 	}
51532ad0f7e9STom Jones 
51542ad0f7e9STom Jones 	/* If this command waits for a response, allocate response buffer. */
51552ad0f7e9STom Jones 	hcmd->resp_pkt = NULL;
51562ad0f7e9STom Jones 	if (hcmd->flags & IWX_CMD_WANT_RESP) {
51572ad0f7e9STom Jones 		uint8_t *resp_buf;
51582ad0f7e9STom Jones 		KASSERT(!async, ("async command want response"));
51592ad0f7e9STom Jones 		KASSERT(hcmd->resp_pkt_len >= sizeof(struct iwx_rx_packet),
51602ad0f7e9STom Jones 		    ("wrong pkt len 1"));
51612ad0f7e9STom Jones 		KASSERT(hcmd->resp_pkt_len <= IWX_CMD_RESP_MAX,
51622ad0f7e9STom Jones 		    ("wrong pkt len 2"));
51632ad0f7e9STom Jones 		if (sc->sc_cmd_resp_pkt[idx] != NULL)
51642ad0f7e9STom Jones 			return ENOSPC;
51652ad0f7e9STom Jones 		resp_buf = malloc(hcmd->resp_pkt_len, M_DEVBUF,
51662ad0f7e9STom Jones 		    M_NOWAIT | M_ZERO);
51672ad0f7e9STom Jones 		if (resp_buf == NULL)
51682ad0f7e9STom Jones 			return ENOMEM;
51692ad0f7e9STom Jones 		sc->sc_cmd_resp_pkt[idx] = resp_buf;
51702ad0f7e9STom Jones 		sc->sc_cmd_resp_len[idx] = hcmd->resp_pkt_len;
51712ad0f7e9STom Jones 	} else {
51722ad0f7e9STom Jones 		sc->sc_cmd_resp_pkt[idx] = NULL;
51732ad0f7e9STom Jones 	}
51742ad0f7e9STom Jones 
51752ad0f7e9STom Jones 	desc = &ring->desc[idx];
51762ad0f7e9STom Jones 	txdata = &ring->data[idx];
51772ad0f7e9STom Jones 
51782ad0f7e9STom Jones 	/*
51792ad0f7e9STom Jones 	 * XXX Intel inside (tm)
51802ad0f7e9STom Jones 	 * Firmware API versions >= 50 reject old-style commands in
51812ad0f7e9STom Jones 	 * group 0 with a "BAD_COMMAND" firmware error. We must pretend
51822ad0f7e9STom Jones 	 * that such commands were in the LONG_GROUP instead in order
51832ad0f7e9STom Jones 	 * for firmware to accept them.
51842ad0f7e9STom Jones 	 */
51852ad0f7e9STom Jones 	if (iwx_cmd_groupid(code) == 0) {
51862ad0f7e9STom Jones 		code = IWX_WIDE_ID(IWX_LONG_GROUP, code);
51872ad0f7e9STom Jones 		txdata->flags |= IWX_TXDATA_FLAG_CMD_IS_NARROW;
51882ad0f7e9STom Jones 	} else
51892ad0f7e9STom Jones 		txdata->flags &= ~IWX_TXDATA_FLAG_CMD_IS_NARROW;
51902ad0f7e9STom Jones 
51912ad0f7e9STom Jones 	group_id = iwx_cmd_groupid(code);
51922ad0f7e9STom Jones 
51932ad0f7e9STom Jones 	hdrlen = sizeof(cmd->hdr_wide);
51942ad0f7e9STom Jones 	datasz = sizeof(cmd->data_wide);
51952ad0f7e9STom Jones 
51962ad0f7e9STom Jones 	if (paylen > datasz) {
51972ad0f7e9STom Jones 		/* Command is too large to fit in pre-allocated space. */
51982ad0f7e9STom Jones 		size_t totlen = hdrlen + paylen;
51992ad0f7e9STom Jones 		if (paylen > IWX_MAX_CMD_PAYLOAD_SIZE) {
52002ad0f7e9STom Jones 			printf("%s: firmware command too long (%zd bytes)\n",
52012ad0f7e9STom Jones 			    DEVNAME(sc), totlen);
52022ad0f7e9STom Jones 			err = EINVAL;
52032ad0f7e9STom Jones 			goto out;
52042ad0f7e9STom Jones 		}
52052ad0f7e9STom Jones 		if (totlen > IWX_RBUF_SIZE)
52062ad0f7e9STom Jones 			panic("totlen > IWX_RBUF_SIZE");
52072ad0f7e9STom Jones 		m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, IWX_RBUF_SIZE);
52082ad0f7e9STom Jones 		if (m == NULL) {
52092ad0f7e9STom Jones 			printf("%s: could not get fw cmd mbuf (%i bytes)\n",
52102ad0f7e9STom Jones 			    DEVNAME(sc), IWX_RBUF_SIZE);
52112ad0f7e9STom Jones 			err = ENOMEM;
52122ad0f7e9STom Jones 			goto out;
52132ad0f7e9STom Jones 		}
52142ad0f7e9STom Jones 		m->m_len = m->m_pkthdr.len = m->m_ext.ext_size;
52152ad0f7e9STom Jones 		err = bus_dmamap_load_mbuf_sg(ring->data_dmat, txdata->map, m,
52162ad0f7e9STom Jones 		    seg, &nsegs, BUS_DMA_NOWAIT);
52172ad0f7e9STom Jones 		if (nsegs > 20)
52182ad0f7e9STom Jones 			panic("nsegs > 20");
52192ad0f7e9STom Jones 		DPRINTF(("%s: nsegs=%i\n", __func__, nsegs));
52202ad0f7e9STom Jones 		if (err) {
52212ad0f7e9STom Jones 			printf("%s: could not load fw cmd mbuf (%zd bytes)\n",
52222ad0f7e9STom Jones 			    DEVNAME(sc), totlen);
52232ad0f7e9STom Jones 			m_freem(m);
52242ad0f7e9STom Jones 			goto out;
52252ad0f7e9STom Jones 		}
52262ad0f7e9STom Jones 		txdata->m = m; /* mbuf will be freed in iwx_cmd_done() */
52272ad0f7e9STom Jones 		cmd = mtod(m, struct iwx_device_cmd *);
52282ad0f7e9STom Jones 		paddr = seg[0].ds_addr;
52292ad0f7e9STom Jones 	} else {
52302ad0f7e9STom Jones 		cmd = &ring->cmd[idx];
52312ad0f7e9STom Jones 		paddr = txdata->cmd_paddr;
52322ad0f7e9STom Jones 	}
52332ad0f7e9STom Jones 
52342ad0f7e9STom Jones 	memset(cmd, 0, sizeof(*cmd));
52352ad0f7e9STom Jones 	cmd->hdr_wide.opcode = iwx_cmd_opcode(code);
52362ad0f7e9STom Jones 	cmd->hdr_wide.group_id = group_id;
52372ad0f7e9STom Jones 	cmd->hdr_wide.qid = ring->qid;
52382ad0f7e9STom Jones 	cmd->hdr_wide.idx = idx;
52392ad0f7e9STom Jones 	cmd->hdr_wide.length = htole16(paylen);
52402ad0f7e9STom Jones 	cmd->hdr_wide.version = iwx_cmd_version(code);
52412ad0f7e9STom Jones 	data = cmd->data_wide;
52422ad0f7e9STom Jones 
52432ad0f7e9STom Jones 	for (i = 0, off = 0; i < nitems(hcmd->data); i++) {
52442ad0f7e9STom Jones 		if (hcmd->len[i] == 0)
52452ad0f7e9STom Jones 			continue;
52462ad0f7e9STom Jones 		memcpy(data + off, hcmd->data[i], hcmd->len[i]);
52472ad0f7e9STom Jones 		off += hcmd->len[i];
52482ad0f7e9STom Jones 	}
52492ad0f7e9STom Jones 	KASSERT(off == paylen, ("off %d != paylen %d", off, paylen));
52502ad0f7e9STom Jones 
52512ad0f7e9STom Jones 	desc->tbs[0].tb_len = htole16(MIN(hdrlen + paylen, IWX_FIRST_TB_SIZE));
52522ad0f7e9STom Jones 	addr = htole64(paddr);
52532ad0f7e9STom Jones 	memcpy(&desc->tbs[0].addr, &addr, sizeof(addr));
52542ad0f7e9STom Jones 	if (hdrlen + paylen > IWX_FIRST_TB_SIZE) {
52552ad0f7e9STom Jones 		DPRINTF(("%s: hdrlen=%zu paylen=%d\n", __func__, hdrlen,
52562ad0f7e9STom Jones 		    paylen));
52572ad0f7e9STom Jones 		desc->tbs[1].tb_len = htole16(hdrlen + paylen -
52582ad0f7e9STom Jones 		    IWX_FIRST_TB_SIZE);
52592ad0f7e9STom Jones 		addr = htole64(paddr + IWX_FIRST_TB_SIZE);
52602ad0f7e9STom Jones 		memcpy(&desc->tbs[1].addr, &addr, sizeof(addr));
52612ad0f7e9STom Jones 		desc->num_tbs = htole16(2);
52622ad0f7e9STom Jones 	} else
52632ad0f7e9STom Jones 		desc->num_tbs = htole16(1);
52642ad0f7e9STom Jones 
52652ad0f7e9STom Jones 	if (paylen > datasz) {
52662ad0f7e9STom Jones 		bus_dmamap_sync(ring->data_dmat, txdata->map,
52672ad0f7e9STom Jones 		    BUS_DMASYNC_PREWRITE);
52682ad0f7e9STom Jones 	} else {
52692ad0f7e9STom Jones 		bus_dmamap_sync(ring->cmd_dma.tag, ring->cmd_dma.map,
52702ad0f7e9STom Jones 		    BUS_DMASYNC_PREWRITE);
52712ad0f7e9STom Jones 	}
52722ad0f7e9STom Jones 	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
52732ad0f7e9STom Jones 	    BUS_DMASYNC_PREWRITE);
52742ad0f7e9STom Jones 
52752ad0f7e9STom Jones 	/* Kick command ring. */
52762ad0f7e9STom Jones 	ring->queued++;
52772ad0f7e9STom Jones 	ring->cur = (ring->cur + 1) % IWX_TX_RING_COUNT;
52782ad0f7e9STom Jones 	ring->cur_hw = (ring->cur_hw + 1) % sc->max_tfd_queue_size;
52792ad0f7e9STom Jones 	DPRINTF(("%s: ring->cur_hw=%i\n", __func__, ring->cur_hw));
52802ad0f7e9STom Jones 	IWX_WRITE(sc, IWX_HBUS_TARG_WRPTR, ring->qid << 16 | ring->cur_hw);
52812ad0f7e9STom Jones 
52822ad0f7e9STom Jones 	if (!async) {
52832ad0f7e9STom Jones 		err = msleep(desc, &sc->sc_mtx, PCATCH, "iwxcmd", hz);
52842ad0f7e9STom Jones 		if (err == 0) {
52852ad0f7e9STom Jones 			/* if hardware is no longer up, return error */
52862ad0f7e9STom Jones 			if (generation != sc->sc_generation) {
52872ad0f7e9STom Jones 				err = ENXIO;
52882ad0f7e9STom Jones 				goto out;
52892ad0f7e9STom Jones 			}
52902ad0f7e9STom Jones 
52912ad0f7e9STom Jones 			/* Response buffer will be freed in iwx_free_resp(). */
52922ad0f7e9STom Jones 			hcmd->resp_pkt = (void *)sc->sc_cmd_resp_pkt[idx];
52932ad0f7e9STom Jones 			sc->sc_cmd_resp_pkt[idx] = NULL;
52942ad0f7e9STom Jones 		} else if (generation == sc->sc_generation) {
52952ad0f7e9STom Jones 			free(sc->sc_cmd_resp_pkt[idx], M_DEVBUF);
52962ad0f7e9STom Jones 			sc->sc_cmd_resp_pkt[idx] = NULL;
52972ad0f7e9STom Jones 		}
52982ad0f7e9STom Jones 	}
52992ad0f7e9STom Jones out:
53002ad0f7e9STom Jones 	return err;
53012ad0f7e9STom Jones }
53022ad0f7e9STom Jones 
53032ad0f7e9STom Jones static int
iwx_send_cmd_pdu(struct iwx_softc * sc,uint32_t id,uint32_t flags,uint16_t len,const void * data)53042ad0f7e9STom Jones iwx_send_cmd_pdu(struct iwx_softc *sc, uint32_t id, uint32_t flags,
53052ad0f7e9STom Jones     uint16_t len, const void *data)
53062ad0f7e9STom Jones {
53072ad0f7e9STom Jones 	struct iwx_host_cmd cmd = {
53082ad0f7e9STom Jones 		.id = id,
53092ad0f7e9STom Jones 		.len = { len, },
53102ad0f7e9STom Jones 		.data = { data, },
53112ad0f7e9STom Jones 		.flags = flags,
53122ad0f7e9STom Jones 	};
53132ad0f7e9STom Jones 
53142ad0f7e9STom Jones 	return iwx_send_cmd(sc, &cmd);
53152ad0f7e9STom Jones }
53162ad0f7e9STom Jones 
53172ad0f7e9STom Jones static int
iwx_send_cmd_status(struct iwx_softc * sc,struct iwx_host_cmd * cmd,uint32_t * status)53182ad0f7e9STom Jones iwx_send_cmd_status(struct iwx_softc *sc, struct iwx_host_cmd *cmd,
53192ad0f7e9STom Jones     uint32_t *status)
53202ad0f7e9STom Jones {
53212ad0f7e9STom Jones 	struct iwx_rx_packet *pkt;
53222ad0f7e9STom Jones 	struct iwx_cmd_response *resp;
53232ad0f7e9STom Jones 	int err, resp_len;
53242ad0f7e9STom Jones 
53252ad0f7e9STom Jones 	KASSERT(((cmd->flags & IWX_CMD_WANT_RESP) == 0), ("IWX_CMD_WANT_RESP"));
53262ad0f7e9STom Jones 	cmd->flags |= IWX_CMD_WANT_RESP;
53272ad0f7e9STom Jones 	cmd->resp_pkt_len = sizeof(*pkt) + sizeof(*resp);
53282ad0f7e9STom Jones 
53292ad0f7e9STom Jones 	err = iwx_send_cmd(sc, cmd);
53302ad0f7e9STom Jones 	if (err)
53312ad0f7e9STom Jones 		return err;
53322ad0f7e9STom Jones 
53332ad0f7e9STom Jones 	pkt = cmd->resp_pkt;
53342ad0f7e9STom Jones 	if (pkt == NULL || (pkt->hdr.flags & IWX_CMD_FAILED_MSK))
53352ad0f7e9STom Jones 		return EIO;
53362ad0f7e9STom Jones 
53372ad0f7e9STom Jones 	resp_len = iwx_rx_packet_payload_len(pkt);
53382ad0f7e9STom Jones 	if (resp_len != sizeof(*resp)) {
53392ad0f7e9STom Jones 		iwx_free_resp(sc, cmd);
53402ad0f7e9STom Jones 		return EIO;
53412ad0f7e9STom Jones 	}
53422ad0f7e9STom Jones 
53432ad0f7e9STom Jones 	resp = (void *)pkt->data;
53442ad0f7e9STom Jones 	*status = le32toh(resp->status);
53452ad0f7e9STom Jones 	iwx_free_resp(sc, cmd);
53462ad0f7e9STom Jones 	return err;
53472ad0f7e9STom Jones }
53482ad0f7e9STom Jones 
53492ad0f7e9STom Jones static int
iwx_send_cmd_pdu_status(struct iwx_softc * sc,uint32_t id,uint16_t len,const void * data,uint32_t * status)53502ad0f7e9STom Jones iwx_send_cmd_pdu_status(struct iwx_softc *sc, uint32_t id, uint16_t len,
53512ad0f7e9STom Jones     const void *data, uint32_t *status)
53522ad0f7e9STom Jones {
53532ad0f7e9STom Jones 	struct iwx_host_cmd cmd = {
53542ad0f7e9STom Jones 		.id = id,
53552ad0f7e9STom Jones 		.len = { len, },
53562ad0f7e9STom Jones 		.data = { data, },
53572ad0f7e9STom Jones 	};
53582ad0f7e9STom Jones 
53592ad0f7e9STom Jones 	return iwx_send_cmd_status(sc, &cmd, status);
53602ad0f7e9STom Jones }
53612ad0f7e9STom Jones 
53622ad0f7e9STom Jones static void
iwx_free_resp(struct iwx_softc * sc,struct iwx_host_cmd * hcmd)53632ad0f7e9STom Jones iwx_free_resp(struct iwx_softc *sc, struct iwx_host_cmd *hcmd)
53642ad0f7e9STom Jones {
53652ad0f7e9STom Jones 	KASSERT((hcmd->flags & (IWX_CMD_WANT_RESP)) == IWX_CMD_WANT_RESP,
53662ad0f7e9STom Jones 	    ("hcmd flags !IWX_CMD_WANT_RESP"));
53672ad0f7e9STom Jones 	free(hcmd->resp_pkt, M_DEVBUF);
53682ad0f7e9STom Jones 	hcmd->resp_pkt = NULL;
53692ad0f7e9STom Jones }
53702ad0f7e9STom Jones 
53712ad0f7e9STom Jones static void
iwx_cmd_done(struct iwx_softc * sc,int qid,int idx,int code)53722ad0f7e9STom Jones iwx_cmd_done(struct iwx_softc *sc, int qid, int idx, int code)
53732ad0f7e9STom Jones {
53742ad0f7e9STom Jones 	struct iwx_tx_ring *ring = &sc->txq[IWX_DQA_CMD_QUEUE];
53752ad0f7e9STom Jones 	struct iwx_tx_data *data;
53762ad0f7e9STom Jones 
53772ad0f7e9STom Jones 	if (qid != IWX_DQA_CMD_QUEUE) {
53782ad0f7e9STom Jones 		return;	/* Not a command ack. */
53792ad0f7e9STom Jones 	}
53802ad0f7e9STom Jones 
53812ad0f7e9STom Jones 	data = &ring->data[idx];
53822ad0f7e9STom Jones 
53832ad0f7e9STom Jones 	if (data->m != NULL) {
53842ad0f7e9STom Jones 		bus_dmamap_sync(ring->data_dmat, data->map,
53852ad0f7e9STom Jones 		    BUS_DMASYNC_POSTWRITE);
53862ad0f7e9STom Jones 		bus_dmamap_unload(ring->data_dmat, data->map);
53872ad0f7e9STom Jones 		m_freem(data->m);
53882ad0f7e9STom Jones 		data->m = NULL;
53892ad0f7e9STom Jones 	}
53902ad0f7e9STom Jones 	wakeup(&ring->desc[idx]);
53912ad0f7e9STom Jones 
53922ad0f7e9STom Jones 	DPRINTF(("%s: command 0x%x done\n", __func__, code));
53932ad0f7e9STom Jones 	if (ring->queued == 0) {
53942ad0f7e9STom Jones 		DPRINTF(("%s: unexpected firmware response to command 0x%x\n",
53952ad0f7e9STom Jones 			DEVNAME(sc), code));
53962ad0f7e9STom Jones 	} else if (ring->queued > 0)
53972ad0f7e9STom Jones 		ring->queued--;
53982ad0f7e9STom Jones }
53992ad0f7e9STom Jones 
54002ad0f7e9STom Jones static uint32_t
iwx_fw_rateidx_ofdm(uint8_t rval)54012ad0f7e9STom Jones iwx_fw_rateidx_ofdm(uint8_t rval)
54022ad0f7e9STom Jones {
54032ad0f7e9STom Jones 	/* Firmware expects indices which match our 11a rate set. */
54042ad0f7e9STom Jones 	const struct ieee80211_rateset *rs = &ieee80211_std_rateset_11a;
54052ad0f7e9STom Jones 	int i;
54062ad0f7e9STom Jones 
54072ad0f7e9STom Jones 	for (i = 0; i < rs->rs_nrates; i++) {
54082ad0f7e9STom Jones 		if ((rs->rs_rates[i] & IEEE80211_RATE_VAL) == rval)
54092ad0f7e9STom Jones 			return i;
54102ad0f7e9STom Jones 	}
54112ad0f7e9STom Jones 
54122ad0f7e9STom Jones 	return 0;
54132ad0f7e9STom Jones }
54142ad0f7e9STom Jones 
54152ad0f7e9STom Jones static uint32_t
iwx_fw_rateidx_cck(uint8_t rval)54162ad0f7e9STom Jones iwx_fw_rateidx_cck(uint8_t rval)
54172ad0f7e9STom Jones {
54182ad0f7e9STom Jones 	/* Firmware expects indices which match our 11b rate set. */
54192ad0f7e9STom Jones 	const struct ieee80211_rateset *rs = &ieee80211_std_rateset_11b;
54202ad0f7e9STom Jones 	int i;
54212ad0f7e9STom Jones 
54222ad0f7e9STom Jones 	for (i = 0; i < rs->rs_nrates; i++) {
54232ad0f7e9STom Jones 		if ((rs->rs_rates[i] & IEEE80211_RATE_VAL) == rval)
54242ad0f7e9STom Jones 			return i;
54252ad0f7e9STom Jones 	}
54262ad0f7e9STom Jones 
54272ad0f7e9STom Jones 	return 0;
54282ad0f7e9STom Jones }
54292ad0f7e9STom Jones 
54302ad0f7e9STom Jones static int
iwx_min_basic_rate(struct ieee80211com * ic)54312ad0f7e9STom Jones iwx_min_basic_rate(struct ieee80211com *ic)
54322ad0f7e9STom Jones {
54332ad0f7e9STom Jones 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
54342ad0f7e9STom Jones 	struct ieee80211_node *ni = vap->iv_bss;
54352ad0f7e9STom Jones 	struct ieee80211_rateset *rs = &ni->ni_rates;
54362ad0f7e9STom Jones 	struct ieee80211_channel *c = ni->ni_chan;
54372ad0f7e9STom Jones 	int i, min, rval;
54382ad0f7e9STom Jones 
54392ad0f7e9STom Jones 	min = -1;
54402ad0f7e9STom Jones 
54412ad0f7e9STom Jones 	if (c == IEEE80211_CHAN_ANYC) {
54422ad0f7e9STom Jones 		printf("%s: channel is IEEE80211_CHAN_ANYC\n", __func__);
54432ad0f7e9STom Jones 		return -1;
54442ad0f7e9STom Jones 	}
54452ad0f7e9STom Jones 
54462ad0f7e9STom Jones 	for (i = 0; i < rs->rs_nrates; i++) {
54472ad0f7e9STom Jones 		if ((rs->rs_rates[i] & IEEE80211_RATE_BASIC) == 0)
54482ad0f7e9STom Jones 			continue;
54492ad0f7e9STom Jones 		rval = (rs->rs_rates[i] & IEEE80211_RATE_VAL);
54502ad0f7e9STom Jones 		if (min == -1)
54512ad0f7e9STom Jones 			min = rval;
54522ad0f7e9STom Jones 		else if (rval < min)
54532ad0f7e9STom Jones 			min = rval;
54542ad0f7e9STom Jones 	}
54552ad0f7e9STom Jones 
54562ad0f7e9STom Jones 	/* Default to 1 Mbit/s on 2GHz and 6 Mbit/s on 5GHz. */
54572ad0f7e9STom Jones 	if (min == -1)
54582ad0f7e9STom Jones 		min = IEEE80211_IS_CHAN_2GHZ(c) ? 2 : 12;
54592ad0f7e9STom Jones 
54602ad0f7e9STom Jones 	return min;
54612ad0f7e9STom Jones }
54622ad0f7e9STom Jones 
54632ad0f7e9STom Jones /*
54642ad0f7e9STom Jones  * Determine the Tx command flags and Tx rate+flags to use.
54652ad0f7e9STom Jones  * Return the selected Tx rate.
54662ad0f7e9STom Jones  */
54672ad0f7e9STom Jones static const struct iwx_rate *
iwx_tx_fill_cmd(struct iwx_softc * sc,struct iwx_node * in,struct ieee80211_frame * wh,uint16_t * flags,uint32_t * rate_n_flags,struct mbuf * m)54682ad0f7e9STom Jones iwx_tx_fill_cmd(struct iwx_softc *sc, struct iwx_node *in,
54692ad0f7e9STom Jones     struct ieee80211_frame *wh, uint16_t *flags, uint32_t *rate_n_flags,
54702ad0f7e9STom Jones     struct mbuf *m)
54712ad0f7e9STom Jones {
54722ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
54732ad0f7e9STom Jones 	struct ieee80211_node *ni = &in->in_ni;
54742ad0f7e9STom Jones 	struct ieee80211_rateset *rs = &ni->ni_rates;
54752ad0f7e9STom Jones 	const struct iwx_rate *rinfo = NULL;
54762ad0f7e9STom Jones 	int type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
54772ad0f7e9STom Jones 	int ridx = iwx_min_basic_rate(ic);
54782ad0f7e9STom Jones 	int min_ridx, rate_flags;
54792ad0f7e9STom Jones 	uint8_t rval;
54802ad0f7e9STom Jones 
54812ad0f7e9STom Jones 	/* We're in the process of clearing the node, no channel already */
54822ad0f7e9STom Jones 	if (ridx == -1)
54832ad0f7e9STom Jones 		return NULL;
54842ad0f7e9STom Jones 
54852ad0f7e9STom Jones 	min_ridx = iwx_rval2ridx(ridx);
54862ad0f7e9STom Jones 
54872ad0f7e9STom Jones 	*flags = 0;
54882ad0f7e9STom Jones 
54892ad0f7e9STom Jones 	if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
54902ad0f7e9STom Jones 	    type != IEEE80211_FC0_TYPE_DATA) {
54912ad0f7e9STom Jones 		/* for non-data, use the lowest supported rate */
54922ad0f7e9STom Jones 		ridx = min_ridx;
54932ad0f7e9STom Jones 		*flags |= IWX_TX_FLAGS_CMD_RATE;
54942ad0f7e9STom Jones 	} else if (ni->ni_flags & IEEE80211_NODE_HT) {
54952ad0f7e9STom Jones 		ridx = iwx_mcs2ridx[ieee80211_node_get_txrate_dot11rate(ni)
54962ad0f7e9STom Jones 		    & ~IEEE80211_RATE_MCS];
54972ad0f7e9STom Jones 	} else {
54982ad0f7e9STom Jones 		rval = (rs->rs_rates[ieee80211_node_get_txrate_dot11rate(ni)]
54992ad0f7e9STom Jones 		    & IEEE80211_RATE_VAL);
55002ad0f7e9STom Jones 		ridx = iwx_rval2ridx(rval);
55012ad0f7e9STom Jones 		if (ridx < min_ridx)
55022ad0f7e9STom Jones 			ridx = min_ridx;
55032ad0f7e9STom Jones 	}
55042ad0f7e9STom Jones 
55052ad0f7e9STom Jones 	if (m->m_flags & M_EAPOL)
55062ad0f7e9STom Jones 		*flags |= IWX_TX_FLAGS_HIGH_PRI;
55072ad0f7e9STom Jones 
55082ad0f7e9STom Jones 	rinfo = &iwx_rates[ridx];
55092ad0f7e9STom Jones 
55102ad0f7e9STom Jones 	/*
55112ad0f7e9STom Jones 	 * Do not fill rate_n_flags if firmware controls the Tx rate.
55122ad0f7e9STom Jones 	 * For data frames we rely on Tx rate scaling in firmware by default.
55132ad0f7e9STom Jones 	 */
55142ad0f7e9STom Jones 	if ((*flags & IWX_TX_FLAGS_CMD_RATE) == 0) {
55152ad0f7e9STom Jones 		*rate_n_flags = 0;
55162ad0f7e9STom Jones 		return rinfo;
55172ad0f7e9STom Jones 	}
55182ad0f7e9STom Jones 
55192ad0f7e9STom Jones 	/*
55202ad0f7e9STom Jones 	 * Forcing a CCK/OFDM legacy rate is important for management frames.
55212ad0f7e9STom Jones 	 * Association will only succeed if we do this correctly.
55222ad0f7e9STom Jones 	 */
55232ad0f7e9STom Jones 
55242ad0f7e9STom Jones 	IWX_DPRINTF(sc, IWX_DEBUG_TXRATE,"%s%d:: min_ridx=%i\n", __func__, __LINE__, min_ridx);
55252ad0f7e9STom Jones 	IWX_DPRINTF(sc, IWX_DEBUG_TXRATE, "%s:%d: ridx=%i\n", __func__, __LINE__, ridx);
55262ad0f7e9STom Jones 	rate_flags = IWX_RATE_MCS_ANT_A_MSK;
55272ad0f7e9STom Jones 	if (IWX_RIDX_IS_CCK(ridx)) {
55282ad0f7e9STom Jones 		if (sc->sc_rate_n_flags_version >= 2)
55292ad0f7e9STom Jones 			rate_flags |= IWX_RATE_MCS_CCK_MSK;
55302ad0f7e9STom Jones 		else
55312ad0f7e9STom Jones 			rate_flags |= IWX_RATE_MCS_CCK_MSK_V1;
55322ad0f7e9STom Jones 	} else if (sc->sc_rate_n_flags_version >= 2)
55332ad0f7e9STom Jones 		rate_flags |= IWX_RATE_MCS_LEGACY_OFDM_MSK;
55342ad0f7e9STom Jones 
55352ad0f7e9STom Jones 	rval = (rs->rs_rates[ieee80211_node_get_txrate_dot11rate(ni)]
55362ad0f7e9STom Jones 	    & IEEE80211_RATE_VAL);
55372ad0f7e9STom Jones 	IWX_DPRINTF(sc, IWX_DEBUG_TXRATE, "%s:%d: rval=%i dot11 %d\n", __func__, __LINE__,
55382ad0f7e9STom Jones 	    rval, rs->rs_rates[ieee80211_node_get_txrate_dot11rate(ni)]);
55392ad0f7e9STom Jones 
55402ad0f7e9STom Jones 	if (sc->sc_rate_n_flags_version >= 2) {
55412ad0f7e9STom Jones 		if (rate_flags & IWX_RATE_MCS_LEGACY_OFDM_MSK) {
55422ad0f7e9STom Jones 			rate_flags |= (iwx_fw_rateidx_ofdm(rval) &
55432ad0f7e9STom Jones 			    IWX_RATE_LEGACY_RATE_MSK);
55442ad0f7e9STom Jones 		} else {
55452ad0f7e9STom Jones 			rate_flags |= (iwx_fw_rateidx_cck(rval) &
55462ad0f7e9STom Jones 			    IWX_RATE_LEGACY_RATE_MSK);
55472ad0f7e9STom Jones 		}
55482ad0f7e9STom Jones 	} else
55492ad0f7e9STom Jones 		rate_flags |= rinfo->plcp;
55502ad0f7e9STom Jones 
55512ad0f7e9STom Jones 	*rate_n_flags = rate_flags;
55522ad0f7e9STom Jones 	IWX_DPRINTF(sc, IWX_DEBUG_TXRATE, "%s:%d flags=0x%x\n",
55532ad0f7e9STom Jones 	    __func__, __LINE__,*flags);
55542ad0f7e9STom Jones 	IWX_DPRINTF(sc, IWX_DEBUG_TXRATE, "%s:%d rate_n_flags=0x%x\n",
55552ad0f7e9STom Jones 	    __func__, __LINE__, *rate_n_flags);
55562ad0f7e9STom Jones 
55572ad0f7e9STom Jones 	if (sc->sc_debug & IWX_DEBUG_TXRATE)
55582ad0f7e9STom Jones 		print_ratenflags(__func__, __LINE__,
55592ad0f7e9STom Jones 		    *rate_n_flags, sc->sc_rate_n_flags_version);
55602ad0f7e9STom Jones 
55612ad0f7e9STom Jones 	return rinfo;
55622ad0f7e9STom Jones }
55632ad0f7e9STom Jones 
55642ad0f7e9STom Jones static void
iwx_tx_update_byte_tbl(struct iwx_softc * sc,struct iwx_tx_ring * txq,int idx,uint16_t byte_cnt,uint16_t num_tbs)55652ad0f7e9STom Jones iwx_tx_update_byte_tbl(struct iwx_softc *sc, struct iwx_tx_ring *txq,
55662ad0f7e9STom Jones     int idx, uint16_t byte_cnt, uint16_t num_tbs)
55672ad0f7e9STom Jones {
55682ad0f7e9STom Jones 	uint8_t filled_tfd_size, num_fetch_chunks;
55692ad0f7e9STom Jones 	uint16_t len = byte_cnt;
55702ad0f7e9STom Jones 	uint16_t bc_ent;
55712ad0f7e9STom Jones 
55722ad0f7e9STom Jones 	filled_tfd_size = offsetof(struct iwx_tfh_tfd, tbs) +
55732ad0f7e9STom Jones 			  num_tbs * sizeof(struct iwx_tfh_tb);
55742ad0f7e9STom Jones 	/*
55752ad0f7e9STom Jones 	 * filled_tfd_size contains the number of filled bytes in the TFD.
55762ad0f7e9STom Jones 	 * Dividing it by 64 will give the number of chunks to fetch
55772ad0f7e9STom Jones 	 * to SRAM- 0 for one chunk, 1 for 2 and so on.
55782ad0f7e9STom Jones 	 * If, for example, TFD contains only 3 TBs then 32 bytes
55792ad0f7e9STom Jones 	 * of the TFD are used, and only one chunk of 64 bytes should
55802ad0f7e9STom Jones 	 * be fetched
55812ad0f7e9STom Jones 	 */
55822ad0f7e9STom Jones 	num_fetch_chunks = howmany(filled_tfd_size, 64) - 1;
55832ad0f7e9STom Jones 
55842ad0f7e9STom Jones 	if (sc->sc_device_family >= IWX_DEVICE_FAMILY_AX210) {
55852ad0f7e9STom Jones 		struct iwx_gen3_bc_tbl_entry *scd_bc_tbl = txq->bc_tbl.vaddr;
55862ad0f7e9STom Jones 		/* Starting from AX210, the HW expects bytes */
55872ad0f7e9STom Jones 		bc_ent = htole16(len | (num_fetch_chunks << 14));
55882ad0f7e9STom Jones 		scd_bc_tbl[idx].tfd_offset = bc_ent;
55892ad0f7e9STom Jones 	} else {
55902ad0f7e9STom Jones 		struct iwx_agn_scd_bc_tbl *scd_bc_tbl = txq->bc_tbl.vaddr;
55912ad0f7e9STom Jones 		/* Before AX210, the HW expects DW */
55922ad0f7e9STom Jones 		len = howmany(len, 4);
55932ad0f7e9STom Jones 		bc_ent = htole16(len | (num_fetch_chunks << 12));
55942ad0f7e9STom Jones 		scd_bc_tbl->tfd_offset[idx] = bc_ent;
55952ad0f7e9STom Jones 	}
55962ad0f7e9STom Jones 
55972ad0f7e9STom Jones 	bus_dmamap_sync(sc->sc_dmat, txq->bc_tbl.map, BUS_DMASYNC_PREWRITE);
55982ad0f7e9STom Jones }
55992ad0f7e9STom Jones 
56002ad0f7e9STom Jones static int
iwx_tx(struct iwx_softc * sc,struct mbuf * m,struct ieee80211_node * ni)56012ad0f7e9STom Jones iwx_tx(struct iwx_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
56022ad0f7e9STom Jones {
56032ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
56042ad0f7e9STom Jones 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
56052ad0f7e9STom Jones 	struct iwx_node *in = (void *)ni;
56062ad0f7e9STom Jones 	struct iwx_tx_ring *ring;
56072ad0f7e9STom Jones 	struct iwx_tx_data *data;
56082ad0f7e9STom Jones 	struct iwx_tfh_tfd *desc;
56092ad0f7e9STom Jones 	struct iwx_device_cmd *cmd;
56102ad0f7e9STom Jones 	struct ieee80211_frame *wh;
56112ad0f7e9STom Jones 	struct ieee80211_key *k = NULL;
56122ad0f7e9STom Jones 	const struct iwx_rate *rinfo;
56132ad0f7e9STom Jones 	uint64_t paddr;
56142ad0f7e9STom Jones 	u_int hdrlen;
56152ad0f7e9STom Jones 	uint32_t rate_n_flags;
56162ad0f7e9STom Jones 	uint16_t num_tbs, flags, offload_assist = 0;
56172ad0f7e9STom Jones 	uint8_t type, subtype;
56182ad0f7e9STom Jones 	int i, totlen, err, pad, qid;
56192ad0f7e9STom Jones #define IWM_MAX_SCATTER 20
56202ad0f7e9STom Jones 	bus_dma_segment_t *seg, segs[IWM_MAX_SCATTER];
56212ad0f7e9STom Jones 	int nsegs;
56222ad0f7e9STom Jones 	struct mbuf *m1;
56232ad0f7e9STom Jones 	size_t txcmd_size;
56242ad0f7e9STom Jones 
56252ad0f7e9STom Jones 	wh = mtod(m, struct ieee80211_frame *);
56262ad0f7e9STom Jones 	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
56272ad0f7e9STom Jones 	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
56282ad0f7e9STom Jones 	hdrlen = ieee80211_anyhdrsize(wh);
56292ad0f7e9STom Jones 
56302ad0f7e9STom Jones 	qid = sc->first_data_qid;
56312ad0f7e9STom Jones 
56322ad0f7e9STom Jones 	/* Put QoS frames on the data queue which maps to their TID. */
56332ad0f7e9STom Jones 	if (IEEE80211_QOS_HAS_SEQ(wh) && (sc->sc_flags & IWX_FLAG_AMPDUTX)) {
56342ad0f7e9STom Jones 		uint16_t qos = ieee80211_gettid(wh);
56352ad0f7e9STom Jones 		uint8_t tid = qos & IEEE80211_QOS_TID;
56362ad0f7e9STom Jones #if 0
56372ad0f7e9STom Jones 		/*
56382ad0f7e9STom Jones 		 * XXX-THJ: TODO when we enable ba we need to manage the
56392ad0f7e9STom Jones 		 * mappings
56402ad0f7e9STom Jones 		 */
56412ad0f7e9STom Jones 		struct ieee80211_tx_ba *ba;
56422ad0f7e9STom Jones 		ba = &ni->ni_tx_ba[tid];
56432ad0f7e9STom Jones 
56442ad0f7e9STom Jones 		if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
56452ad0f7e9STom Jones 		    type == IEEE80211_FC0_TYPE_DATA &&
56462ad0f7e9STom Jones 		    subtype != IEEE80211_FC0_SUBTYPE_NODATA &&
56472ad0f7e9STom Jones 		    subtype != IEEE80211_FC0_SUBTYPE_BAR &&
56482ad0f7e9STom Jones 		    sc->aggqid[tid] != 0  /*&&
56492ad0f7e9STom Jones 		    ba->ba_state == IEEE80211_BA_AGREED*/) {
56502ad0f7e9STom Jones 			qid = sc->aggqid[tid];
56512ad0f7e9STom Jones #else
56522ad0f7e9STom Jones 		if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
56532ad0f7e9STom Jones 		    type == IEEE80211_FC0_TYPE_DATA &&
56542ad0f7e9STom Jones 		    subtype != IEEE80211_FC0_SUBTYPE_NODATA &&
56552ad0f7e9STom Jones 		    sc->aggqid[tid] != 0) {
56562ad0f7e9STom Jones 			qid = sc->aggqid[tid];
56572ad0f7e9STom Jones #endif
56582ad0f7e9STom Jones 		}
56592ad0f7e9STom Jones 	}
56602ad0f7e9STom Jones 
56612ad0f7e9STom Jones 	ring = &sc->txq[qid];
56622ad0f7e9STom Jones 	desc = &ring->desc[ring->cur];
56632ad0f7e9STom Jones 	memset(desc, 0, sizeof(*desc));
56642ad0f7e9STom Jones 	data = &ring->data[ring->cur];
56652ad0f7e9STom Jones 
56662ad0f7e9STom Jones 	cmd = &ring->cmd[ring->cur];
56672ad0f7e9STom Jones 	cmd->hdr.code = IWX_TX_CMD;
56682ad0f7e9STom Jones 	cmd->hdr.flags = 0;
56692ad0f7e9STom Jones 	cmd->hdr.qid = ring->qid;
56702ad0f7e9STom Jones 	cmd->hdr.idx = ring->cur;
56712ad0f7e9STom Jones 
56722ad0f7e9STom Jones 	rinfo = iwx_tx_fill_cmd(sc, in, wh, &flags, &rate_n_flags, m);
56732ad0f7e9STom Jones 	if (rinfo == NULL)
56742ad0f7e9STom Jones 		return EINVAL;
56752ad0f7e9STom Jones 
56762ad0f7e9STom Jones 	if (ieee80211_radiotap_active_vap(vap)) {
56772ad0f7e9STom Jones 		struct iwx_tx_radiotap_header *tap = &sc->sc_txtap;
56782ad0f7e9STom Jones 
56792ad0f7e9STom Jones 		tap->wt_flags = 0;
56802ad0f7e9STom Jones 		tap->wt_chan_freq = htole16(ni->ni_chan->ic_freq);
56812ad0f7e9STom Jones 		tap->wt_chan_flags = htole16(ni->ni_chan->ic_flags);
56822ad0f7e9STom Jones 		tap->wt_rate = rinfo->rate;
56832ad0f7e9STom Jones 		if (k != NULL)
56842ad0f7e9STom Jones 			tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP;
56852ad0f7e9STom Jones 		ieee80211_radiotap_tx(vap, m);
56862ad0f7e9STom Jones 	}
56872ad0f7e9STom Jones 
56882ad0f7e9STom Jones 	if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
56892ad0f7e9STom Jones 		k = ieee80211_crypto_get_txkey(ni, m);
56902ad0f7e9STom Jones 		if (k == NULL) {
56912ad0f7e9STom Jones 			printf("%s: k is NULL!\n", __func__);
56922ad0f7e9STom Jones 			m_freem(m);
56932ad0f7e9STom Jones 			return (ENOBUFS);
56942ad0f7e9STom Jones 		} else if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_AES_CCM) {
56952ad0f7e9STom Jones 			k->wk_keytsc++;
56962ad0f7e9STom Jones 		} else {
56972ad0f7e9STom Jones 			k->wk_cipher->ic_encap(k, m);
56982ad0f7e9STom Jones 
56992ad0f7e9STom Jones 			/* 802.11 headers may have moved */
57002ad0f7e9STom Jones 			wh = mtod(m, struct ieee80211_frame *);
57012ad0f7e9STom Jones 			flags |= IWX_TX_FLAGS_ENCRYPT_DIS;
57022ad0f7e9STom Jones 		}
57032ad0f7e9STom Jones 	} else
57042ad0f7e9STom Jones 		flags |= IWX_TX_FLAGS_ENCRYPT_DIS;
57052ad0f7e9STom Jones 
57062ad0f7e9STom Jones 	totlen = m->m_pkthdr.len;
57072ad0f7e9STom Jones 
57082ad0f7e9STom Jones 	if (hdrlen & 3) {
57092ad0f7e9STom Jones 		/* First segment length must be a multiple of 4. */
57102ad0f7e9STom Jones 		pad = 4 - (hdrlen & 3);
57112ad0f7e9STom Jones 		offload_assist |= IWX_TX_CMD_OFFLD_PAD;
57122ad0f7e9STom Jones 	} else
57132ad0f7e9STom Jones 		pad = 0;
57142ad0f7e9STom Jones 
57152ad0f7e9STom Jones 	if (sc->sc_device_family >= IWX_DEVICE_FAMILY_AX210) {
57162ad0f7e9STom Jones 		struct iwx_tx_cmd_gen3 *tx = (void *)cmd->data;
57172ad0f7e9STom Jones 		memset(tx, 0, sizeof(*tx));
57182ad0f7e9STom Jones 		tx->len = htole16(totlen);
57192ad0f7e9STom Jones 		tx->offload_assist = htole32(offload_assist);
57202ad0f7e9STom Jones 		tx->flags = htole16(flags);
57212ad0f7e9STom Jones 		tx->rate_n_flags = htole32(rate_n_flags);
57222ad0f7e9STom Jones 		memcpy(tx->hdr, wh, hdrlen);
57232ad0f7e9STom Jones 		txcmd_size = sizeof(*tx);
57242ad0f7e9STom Jones 	} else {
57252ad0f7e9STom Jones 		struct iwx_tx_cmd_gen2 *tx = (void *)cmd->data;
57262ad0f7e9STom Jones 		memset(tx, 0, sizeof(*tx));
57272ad0f7e9STom Jones 		tx->len = htole16(totlen);
57282ad0f7e9STom Jones 		tx->offload_assist = htole16(offload_assist);
57292ad0f7e9STom Jones 		tx->flags = htole32(flags);
57302ad0f7e9STom Jones 		tx->rate_n_flags = htole32(rate_n_flags);
57312ad0f7e9STom Jones 		memcpy(tx->hdr, wh, hdrlen);
57322ad0f7e9STom Jones 		txcmd_size = sizeof(*tx);
57332ad0f7e9STom Jones 	}
57342ad0f7e9STom Jones 
57352ad0f7e9STom Jones 	/* Trim 802.11 header. */
57362ad0f7e9STom Jones 	m_adj(m, hdrlen);
57372ad0f7e9STom Jones 
57382ad0f7e9STom Jones 	err = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, segs,
57392ad0f7e9STom Jones 	    &nsegs, BUS_DMA_NOWAIT);
57402ad0f7e9STom Jones 	if (err && err != EFBIG) {
57412ad0f7e9STom Jones 		printf("%s: can't map mbuf (error %d)\n", DEVNAME(sc), err);
57422ad0f7e9STom Jones 		m_freem(m);
57432ad0f7e9STom Jones 		return err;
57442ad0f7e9STom Jones 	}
57452ad0f7e9STom Jones 	if (err) {
57462ad0f7e9STom Jones 		/* Too many DMA segments, linearize mbuf. */
57472ad0f7e9STom Jones 		m1 = m_collapse(m, M_NOWAIT, IWM_MAX_SCATTER - 2);
57482ad0f7e9STom Jones 		if (m1 == NULL) {
57492ad0f7e9STom Jones 			printf("%s: could not defrag mbufs\n", __func__);
57502ad0f7e9STom Jones 			m_freem(m);
57512ad0f7e9STom Jones 			return (ENOBUFS);
57522ad0f7e9STom Jones 		}
57532ad0f7e9STom Jones 		m = m1;
57542ad0f7e9STom Jones 		err = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m,
57552ad0f7e9STom Jones 		    segs, &nsegs, BUS_DMA_NOWAIT);
57562ad0f7e9STom Jones 		if (err) {
57572ad0f7e9STom Jones 			printf("%s: can't map mbuf (error %d)\n", __func__,
57582ad0f7e9STom Jones 			    err);
57592ad0f7e9STom Jones 			m_freem(m);
57602ad0f7e9STom Jones 			return (err);
57612ad0f7e9STom Jones 		}
57622ad0f7e9STom Jones 	}
57632ad0f7e9STom Jones 	data->m = m;
57642ad0f7e9STom Jones 	data->in = in;
57652ad0f7e9STom Jones 
57662ad0f7e9STom Jones 	/* Fill TX descriptor. */
57672ad0f7e9STom Jones 	num_tbs = 2 + nsegs;
57682ad0f7e9STom Jones 	desc->num_tbs = htole16(num_tbs);
57692ad0f7e9STom Jones 
57702ad0f7e9STom Jones 	desc->tbs[0].tb_len = htole16(IWX_FIRST_TB_SIZE);
57712ad0f7e9STom Jones 	paddr = htole64(data->cmd_paddr);
57722ad0f7e9STom Jones 	memcpy(&desc->tbs[0].addr, &paddr, sizeof(paddr));
57732ad0f7e9STom Jones 	if (data->cmd_paddr >> 32 != (data->cmd_paddr + le32toh(desc->tbs[0].tb_len)) >> 32)
57742ad0f7e9STom Jones 		DPRINTF(("%s: TB0 crosses 32bit boundary\n", __func__));
57752ad0f7e9STom Jones 	desc->tbs[1].tb_len = htole16(sizeof(struct iwx_cmd_header) +
57762ad0f7e9STom Jones 	    txcmd_size + hdrlen + pad - IWX_FIRST_TB_SIZE);
57772ad0f7e9STom Jones 	paddr = htole64(data->cmd_paddr + IWX_FIRST_TB_SIZE);
57782ad0f7e9STom Jones 	memcpy(&desc->tbs[1].addr, &paddr, sizeof(paddr));
57792ad0f7e9STom Jones 
57802ad0f7e9STom Jones 	if (data->cmd_paddr >> 32 != (data->cmd_paddr + le32toh(desc->tbs[1].tb_len)) >> 32)
57812ad0f7e9STom Jones 		DPRINTF(("%s: TB1 crosses 32bit boundary\n", __func__));
57822ad0f7e9STom Jones 
57832ad0f7e9STom Jones 	/* Other DMA segments are for data payload. */
57842ad0f7e9STom Jones 	for (i = 0; i < nsegs; i++) {
57852ad0f7e9STom Jones 		seg = &segs[i];
57862ad0f7e9STom Jones 		desc->tbs[i + 2].tb_len = htole16(seg->ds_len);
57872ad0f7e9STom Jones 		paddr = htole64(seg->ds_addr);
57882ad0f7e9STom Jones 		memcpy(&desc->tbs[i + 2].addr, &paddr, sizeof(paddr));
57892ad0f7e9STom Jones 		if (data->cmd_paddr >> 32 != (data->cmd_paddr + le32toh(desc->tbs[i + 2].tb_len)) >> 32)
57902ad0f7e9STom Jones 			DPRINTF(("%s: TB%d crosses 32bit boundary\n", __func__, i + 2));
57912ad0f7e9STom Jones 	}
57922ad0f7e9STom Jones 
57932ad0f7e9STom Jones 	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE);
57942ad0f7e9STom Jones 	bus_dmamap_sync(ring->cmd_dma.tag, ring->cmd_dma.map,
57952ad0f7e9STom Jones 	    BUS_DMASYNC_PREWRITE);
57962ad0f7e9STom Jones 	bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
57972ad0f7e9STom Jones 	    BUS_DMASYNC_PREWRITE);
57982ad0f7e9STom Jones 
57992ad0f7e9STom Jones 	iwx_tx_update_byte_tbl(sc, ring, ring->cur, totlen, num_tbs);
58002ad0f7e9STom Jones 
58012ad0f7e9STom Jones 	/* Kick TX ring. */
58022ad0f7e9STom Jones 	ring->cur = (ring->cur + 1) % IWX_TX_RING_COUNT;
58032ad0f7e9STom Jones 	ring->cur_hw = (ring->cur_hw + 1) % sc->max_tfd_queue_size;
58042ad0f7e9STom Jones 	IWX_WRITE(sc, IWX_HBUS_TARG_WRPTR, ring->qid << 16 | ring->cur_hw);
58052ad0f7e9STom Jones 
58062ad0f7e9STom Jones 	/* Mark TX ring as full if we reach a certain threshold. */
58072ad0f7e9STom Jones 	if (++ring->queued > iwx_himark) {
58082ad0f7e9STom Jones 		sc->qfullmsk |= 1 << ring->qid;
58092ad0f7e9STom Jones 	}
58102ad0f7e9STom Jones 
58112ad0f7e9STom Jones 	sc->sc_tx_timer[ring->qid] = 15;
58122ad0f7e9STom Jones 
58132ad0f7e9STom Jones 	return 0;
58142ad0f7e9STom Jones }
58152ad0f7e9STom Jones 
58162ad0f7e9STom Jones static int
58172ad0f7e9STom Jones iwx_flush_sta_tids(struct iwx_softc *sc, int sta_id, uint16_t tids)
58182ad0f7e9STom Jones {
58192ad0f7e9STom Jones 	struct iwx_rx_packet *pkt;
58202ad0f7e9STom Jones 	struct iwx_tx_path_flush_cmd_rsp *resp;
58212ad0f7e9STom Jones 	struct iwx_tx_path_flush_cmd flush_cmd = {
58222ad0f7e9STom Jones 		.sta_id = htole32(sta_id),
58232ad0f7e9STom Jones 		.tid_mask = htole16(tids),
58242ad0f7e9STom Jones 	};
58252ad0f7e9STom Jones 	struct iwx_host_cmd hcmd = {
58262ad0f7e9STom Jones 		.id = IWX_TXPATH_FLUSH,
58272ad0f7e9STom Jones 		.len = { sizeof(flush_cmd), },
58282ad0f7e9STom Jones 		.data = { &flush_cmd, },
58292ad0f7e9STom Jones 		.flags = IWX_CMD_WANT_RESP,
58302ad0f7e9STom Jones 		.resp_pkt_len = sizeof(*pkt) + sizeof(*resp),
58312ad0f7e9STom Jones 	};
58322ad0f7e9STom Jones 	int err, resp_len, i, num_flushed_queues;
58332ad0f7e9STom Jones 
58342ad0f7e9STom Jones 	err = iwx_send_cmd(sc, &hcmd);
58352ad0f7e9STom Jones 	if (err)
58362ad0f7e9STom Jones 		return err;
58372ad0f7e9STom Jones 
58382ad0f7e9STom Jones 	pkt = hcmd.resp_pkt;
58392ad0f7e9STom Jones 	if (!pkt || (pkt->hdr.flags & IWX_CMD_FAILED_MSK)) {
58402ad0f7e9STom Jones 		err = EIO;
58412ad0f7e9STom Jones 		goto out;
58422ad0f7e9STom Jones 	}
58432ad0f7e9STom Jones 
58442ad0f7e9STom Jones 	resp_len = iwx_rx_packet_payload_len(pkt);
58452ad0f7e9STom Jones 	/* Some firmware versions don't provide a response. */
58462ad0f7e9STom Jones 	if (resp_len == 0)
58472ad0f7e9STom Jones 		goto out;
58482ad0f7e9STom Jones 	else if (resp_len != sizeof(*resp)) {
58492ad0f7e9STom Jones 		err = EIO;
58502ad0f7e9STom Jones 		goto out;
58512ad0f7e9STom Jones 	}
58522ad0f7e9STom Jones 
58532ad0f7e9STom Jones 	resp = (void *)pkt->data;
58542ad0f7e9STom Jones 
58552ad0f7e9STom Jones 	if (le16toh(resp->sta_id) != sta_id) {
58562ad0f7e9STom Jones 		err = EIO;
58572ad0f7e9STom Jones 		goto out;
58582ad0f7e9STom Jones 	}
58592ad0f7e9STom Jones 
58602ad0f7e9STom Jones 	num_flushed_queues = le16toh(resp->num_flushed_queues);
58612ad0f7e9STom Jones 	if (num_flushed_queues > IWX_TX_FLUSH_QUEUE_RSP) {
58622ad0f7e9STom Jones 		err = EIO;
58632ad0f7e9STom Jones 		goto out;
58642ad0f7e9STom Jones 	}
58652ad0f7e9STom Jones 
58662ad0f7e9STom Jones 	for (i = 0; i < num_flushed_queues; i++) {
58672ad0f7e9STom Jones 		struct iwx_flush_queue_info *queue_info = &resp->queues[i];
58682ad0f7e9STom Jones 		uint16_t tid = le16toh(queue_info->tid);
58692ad0f7e9STom Jones 		uint16_t read_after = le16toh(queue_info->read_after_flush);
58702ad0f7e9STom Jones 		uint16_t qid = le16toh(queue_info->queue_num);
58712ad0f7e9STom Jones 		struct iwx_tx_ring *txq;
58722ad0f7e9STom Jones 
58732ad0f7e9STom Jones 		if (qid >= nitems(sc->txq))
58742ad0f7e9STom Jones 			continue;
58752ad0f7e9STom Jones 
58762ad0f7e9STom Jones 		txq = &sc->txq[qid];
58772ad0f7e9STom Jones 		if (tid != txq->tid)
58782ad0f7e9STom Jones 			continue;
58792ad0f7e9STom Jones 
58802ad0f7e9STom Jones 		iwx_txq_advance(sc, txq, read_after);
58812ad0f7e9STom Jones 	}
58822ad0f7e9STom Jones out:
58832ad0f7e9STom Jones 	iwx_free_resp(sc, &hcmd);
58842ad0f7e9STom Jones 	return err;
58852ad0f7e9STom Jones }
58862ad0f7e9STom Jones 
58872ad0f7e9STom Jones #define IWX_FLUSH_WAIT_MS	2000
58882ad0f7e9STom Jones 
58892ad0f7e9STom Jones static int
58902ad0f7e9STom Jones iwx_drain_sta(struct iwx_softc *sc, struct iwx_node* in, int drain)
58912ad0f7e9STom Jones {
58922ad0f7e9STom Jones 	struct iwx_add_sta_cmd cmd;
58932ad0f7e9STom Jones 	int err;
58942ad0f7e9STom Jones 	uint32_t status;
58952ad0f7e9STom Jones 
58962ad0f7e9STom Jones 	memset(&cmd, 0, sizeof(cmd));
58972ad0f7e9STom Jones 	cmd.mac_id_n_color = htole32(IWX_FW_CMD_ID_AND_COLOR(in->in_id,
58982ad0f7e9STom Jones 	    in->in_color));
58992ad0f7e9STom Jones 	cmd.sta_id = IWX_STATION_ID;
59002ad0f7e9STom Jones 	cmd.add_modify = IWX_STA_MODE_MODIFY;
59012ad0f7e9STom Jones 	cmd.station_flags = drain ? htole32(IWX_STA_FLG_DRAIN_FLOW) : 0;
59022ad0f7e9STom Jones 	cmd.station_flags_msk = htole32(IWX_STA_FLG_DRAIN_FLOW);
59032ad0f7e9STom Jones 
59042ad0f7e9STom Jones 	status = IWX_ADD_STA_SUCCESS;
59052ad0f7e9STom Jones 	err = iwx_send_cmd_pdu_status(sc, IWX_ADD_STA,
59062ad0f7e9STom Jones 	    sizeof(cmd), &cmd, &status);
59072ad0f7e9STom Jones 	if (err) {
59082ad0f7e9STom Jones 		printf("%s: could not update sta (error %d)\n",
59092ad0f7e9STom Jones 		    DEVNAME(sc), err);
59102ad0f7e9STom Jones 		return err;
59112ad0f7e9STom Jones 	}
59122ad0f7e9STom Jones 
59132ad0f7e9STom Jones 	switch (status & IWX_ADD_STA_STATUS_MASK) {
59142ad0f7e9STom Jones 	case IWX_ADD_STA_SUCCESS:
59152ad0f7e9STom Jones 		break;
59162ad0f7e9STom Jones 	default:
59172ad0f7e9STom Jones 		err = EIO;
59182ad0f7e9STom Jones 		printf("%s: Couldn't %s draining for station\n",
59192ad0f7e9STom Jones 		    DEVNAME(sc), drain ? "enable" : "disable");
59202ad0f7e9STom Jones 		break;
59212ad0f7e9STom Jones 	}
59222ad0f7e9STom Jones 
59232ad0f7e9STom Jones 	return err;
59242ad0f7e9STom Jones }
59252ad0f7e9STom Jones 
59262ad0f7e9STom Jones static int
59272ad0f7e9STom Jones iwx_flush_sta(struct iwx_softc *sc, struct iwx_node *in)
59282ad0f7e9STom Jones {
59292ad0f7e9STom Jones 	int err;
59302ad0f7e9STom Jones 
59312ad0f7e9STom Jones 	IWX_ASSERT_LOCKED(sc);
59322ad0f7e9STom Jones 
59332ad0f7e9STom Jones 	sc->sc_flags |= IWX_FLAG_TXFLUSH;
59342ad0f7e9STom Jones 
59352ad0f7e9STom Jones 	err = iwx_drain_sta(sc, in, 1);
59362ad0f7e9STom Jones 	if (err)
59372ad0f7e9STom Jones 		goto done;
59382ad0f7e9STom Jones 
59392ad0f7e9STom Jones 	err = iwx_flush_sta_tids(sc, IWX_STATION_ID, 0xffff);
59402ad0f7e9STom Jones 	if (err) {
59412ad0f7e9STom Jones 		printf("%s: could not flush Tx path (error %d)\n",
59422ad0f7e9STom Jones 		    DEVNAME(sc), err);
59432ad0f7e9STom Jones 		goto done;
59442ad0f7e9STom Jones 	}
59452ad0f7e9STom Jones 
59462ad0f7e9STom Jones 	/*
59472ad0f7e9STom Jones 	 * XXX-THJ: iwx_wait_tx_queues_empty was here, but it was a nope in the
59482ad0f7e9STom Jones 	 * fc drive rand has has been replaced in OpenBSD.
59492ad0f7e9STom Jones 	 */
59502ad0f7e9STom Jones 
59512ad0f7e9STom Jones 	err = iwx_drain_sta(sc, in, 0);
59522ad0f7e9STom Jones done:
59532ad0f7e9STom Jones 	sc->sc_flags &= ~IWX_FLAG_TXFLUSH;
59542ad0f7e9STom Jones 	return err;
59552ad0f7e9STom Jones }
59562ad0f7e9STom Jones 
59572ad0f7e9STom Jones #define IWX_POWER_KEEP_ALIVE_PERIOD_SEC    25
59582ad0f7e9STom Jones 
59592ad0f7e9STom Jones static int
59602ad0f7e9STom Jones iwx_beacon_filter_send_cmd(struct iwx_softc *sc,
59612ad0f7e9STom Jones     struct iwx_beacon_filter_cmd *cmd)
59622ad0f7e9STom Jones {
59632ad0f7e9STom Jones 	return iwx_send_cmd_pdu(sc, IWX_REPLY_BEACON_FILTERING_CMD,
59642ad0f7e9STom Jones 	    0, sizeof(struct iwx_beacon_filter_cmd), cmd);
59652ad0f7e9STom Jones }
59662ad0f7e9STom Jones 
59672ad0f7e9STom Jones static int
59682ad0f7e9STom Jones iwx_update_beacon_abort(struct iwx_softc *sc, struct iwx_node *in, int enable)
59692ad0f7e9STom Jones {
59702ad0f7e9STom Jones 	struct iwx_beacon_filter_cmd cmd = {
59712ad0f7e9STom Jones 		IWX_BF_CMD_CONFIG_DEFAULTS,
59722ad0f7e9STom Jones 		.bf_enable_beacon_filter = htole32(1),
59732ad0f7e9STom Jones 		.ba_enable_beacon_abort = htole32(enable),
59742ad0f7e9STom Jones 	};
59752ad0f7e9STom Jones 
59762ad0f7e9STom Jones 	if (!sc->sc_bf.bf_enabled)
59772ad0f7e9STom Jones 		return 0;
59782ad0f7e9STom Jones 
59792ad0f7e9STom Jones 	sc->sc_bf.ba_enabled = enable;
59802ad0f7e9STom Jones 	return iwx_beacon_filter_send_cmd(sc, &cmd);
59812ad0f7e9STom Jones }
59822ad0f7e9STom Jones 
59832ad0f7e9STom Jones static void
59842ad0f7e9STom Jones iwx_power_build_cmd(struct iwx_softc *sc, struct iwx_node *in,
59852ad0f7e9STom Jones     struct iwx_mac_power_cmd *cmd)
59862ad0f7e9STom Jones {
59872ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
59882ad0f7e9STom Jones 	struct ieee80211_node *ni = &in->in_ni;
59892ad0f7e9STom Jones 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
59902ad0f7e9STom Jones 	int dtim_period, dtim_msec, keep_alive;
59912ad0f7e9STom Jones 
59922ad0f7e9STom Jones 	cmd->id_and_color = htole32(IWX_FW_CMD_ID_AND_COLOR(in->in_id,
59932ad0f7e9STom Jones 	    in->in_color));
59942ad0f7e9STom Jones 	if (vap->iv_dtim_period)
59952ad0f7e9STom Jones 		dtim_period = vap->iv_dtim_period;
59962ad0f7e9STom Jones 	else
59972ad0f7e9STom Jones 		dtim_period = 1;
59982ad0f7e9STom Jones 
59992ad0f7e9STom Jones 	/*
60002ad0f7e9STom Jones 	 * Regardless of power management state the driver must set
60012ad0f7e9STom Jones 	 * keep alive period. FW will use it for sending keep alive NDPs
60022ad0f7e9STom Jones 	 * immediately after association. Check that keep alive period
60032ad0f7e9STom Jones 	 * is at least 3 * DTIM.
60042ad0f7e9STom Jones 	 */
60052ad0f7e9STom Jones 	dtim_msec = dtim_period * ni->ni_intval;
60062ad0f7e9STom Jones 	keep_alive = MAX(3 * dtim_msec, 1000 * IWX_POWER_KEEP_ALIVE_PERIOD_SEC);
60072ad0f7e9STom Jones 	keep_alive = roundup(keep_alive, 1000) / 1000;
60082ad0f7e9STom Jones 	cmd->keep_alive_seconds = htole16(keep_alive);
60092ad0f7e9STom Jones 
60102ad0f7e9STom Jones 	if (ic->ic_opmode != IEEE80211_M_MONITOR)
60112ad0f7e9STom Jones 		cmd->flags = htole16(IWX_POWER_FLAGS_POWER_SAVE_ENA_MSK);
60122ad0f7e9STom Jones }
60132ad0f7e9STom Jones 
60142ad0f7e9STom Jones static int
60152ad0f7e9STom Jones iwx_power_mac_update_mode(struct iwx_softc *sc, struct iwx_node *in)
60162ad0f7e9STom Jones {
60172ad0f7e9STom Jones 	int err;
60182ad0f7e9STom Jones 	int ba_enable;
60192ad0f7e9STom Jones 	struct iwx_mac_power_cmd cmd;
60202ad0f7e9STom Jones 
60212ad0f7e9STom Jones 	memset(&cmd, 0, sizeof(cmd));
60222ad0f7e9STom Jones 
60232ad0f7e9STom Jones 	iwx_power_build_cmd(sc, in, &cmd);
60242ad0f7e9STom Jones 
60252ad0f7e9STom Jones 	err = iwx_send_cmd_pdu(sc, IWX_MAC_PM_POWER_TABLE, 0,
60262ad0f7e9STom Jones 	    sizeof(cmd), &cmd);
60272ad0f7e9STom Jones 	if (err != 0)
60282ad0f7e9STom Jones 		return err;
60292ad0f7e9STom Jones 
60302ad0f7e9STom Jones 	ba_enable = !!(cmd.flags &
60312ad0f7e9STom Jones 	    htole16(IWX_POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK));
60322ad0f7e9STom Jones 	return iwx_update_beacon_abort(sc, in, ba_enable);
60332ad0f7e9STom Jones }
60342ad0f7e9STom Jones 
60352ad0f7e9STom Jones static int
60362ad0f7e9STom Jones iwx_power_update_device(struct iwx_softc *sc)
60372ad0f7e9STom Jones {
60382ad0f7e9STom Jones 	struct iwx_device_power_cmd cmd = { };
60392ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
60402ad0f7e9STom Jones 
60412ad0f7e9STom Jones 	if (ic->ic_opmode != IEEE80211_M_MONITOR)
60422ad0f7e9STom Jones 		cmd.flags = htole16(IWX_DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK);
60432ad0f7e9STom Jones 
60442ad0f7e9STom Jones 	return iwx_send_cmd_pdu(sc,
60452ad0f7e9STom Jones 	    IWX_POWER_TABLE_CMD, 0, sizeof(cmd), &cmd);
60462ad0f7e9STom Jones }
60472ad0f7e9STom Jones #if 0
60482ad0f7e9STom Jones static int
60492ad0f7e9STom Jones iwx_enable_beacon_filter(struct iwx_softc *sc, struct iwx_node *in)
60502ad0f7e9STom Jones {
60512ad0f7e9STom Jones 	struct iwx_beacon_filter_cmd cmd = {
60522ad0f7e9STom Jones 		IWX_BF_CMD_CONFIG_DEFAULTS,
60532ad0f7e9STom Jones 		.bf_enable_beacon_filter = htole32(1),
60542ad0f7e9STom Jones 		.ba_enable_beacon_abort = htole32(sc->sc_bf.ba_enabled),
60552ad0f7e9STom Jones 	};
60562ad0f7e9STom Jones 	int err;
60572ad0f7e9STom Jones 
60582ad0f7e9STom Jones 	err = iwx_beacon_filter_send_cmd(sc, &cmd);
60592ad0f7e9STom Jones 	if (err == 0)
60602ad0f7e9STom Jones 		sc->sc_bf.bf_enabled = 1;
60612ad0f7e9STom Jones 
60622ad0f7e9STom Jones 	return err;
60632ad0f7e9STom Jones }
60642ad0f7e9STom Jones #endif
60652ad0f7e9STom Jones static int
60662ad0f7e9STom Jones iwx_disable_beacon_filter(struct iwx_softc *sc)
60672ad0f7e9STom Jones {
60682ad0f7e9STom Jones 	struct iwx_beacon_filter_cmd cmd;
60692ad0f7e9STom Jones 	int err;
60702ad0f7e9STom Jones 
60712ad0f7e9STom Jones 	memset(&cmd, 0, sizeof(cmd));
60722ad0f7e9STom Jones 
60732ad0f7e9STom Jones 	err = iwx_beacon_filter_send_cmd(sc, &cmd);
60742ad0f7e9STom Jones 	if (err == 0)
60752ad0f7e9STom Jones 		sc->sc_bf.bf_enabled = 0;
60762ad0f7e9STom Jones 
60772ad0f7e9STom Jones 	return err;
60782ad0f7e9STom Jones }
60792ad0f7e9STom Jones 
60802ad0f7e9STom Jones static int
60812ad0f7e9STom Jones iwx_add_sta_cmd(struct iwx_softc *sc, struct iwx_node *in, int update)
60822ad0f7e9STom Jones {
60832ad0f7e9STom Jones 	struct iwx_add_sta_cmd add_sta_cmd;
60842ad0f7e9STom Jones 	int err, i;
60852ad0f7e9STom Jones 	uint32_t status, aggsize;
60862ad0f7e9STom Jones 	const uint32_t max_aggsize = (IWX_STA_FLG_MAX_AGG_SIZE_64K >>
60872ad0f7e9STom Jones 		    IWX_STA_FLG_MAX_AGG_SIZE_SHIFT);
60882ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
60892ad0f7e9STom Jones 	struct ieee80211_node *ni = &in->in_ni;
60902ad0f7e9STom Jones 	struct ieee80211_htrateset *htrs = &ni->ni_htrates;
60912ad0f7e9STom Jones 
60922ad0f7e9STom Jones 	if (!update && (sc->sc_flags & IWX_FLAG_STA_ACTIVE))
60932ad0f7e9STom Jones 		panic("STA already added");
60942ad0f7e9STom Jones 
60952ad0f7e9STom Jones 	memset(&add_sta_cmd, 0, sizeof(add_sta_cmd));
60962ad0f7e9STom Jones 
60972ad0f7e9STom Jones 	if (ic->ic_opmode == IEEE80211_M_MONITOR) {
60982ad0f7e9STom Jones 		add_sta_cmd.sta_id = IWX_MONITOR_STA_ID;
60992ad0f7e9STom Jones 		add_sta_cmd.station_type = IWX_STA_GENERAL_PURPOSE;
61002ad0f7e9STom Jones 	} else {
61012ad0f7e9STom Jones 		add_sta_cmd.sta_id = IWX_STATION_ID;
61022ad0f7e9STom Jones 		add_sta_cmd.station_type = IWX_STA_LINK;
61032ad0f7e9STom Jones 	}
61042ad0f7e9STom Jones 	add_sta_cmd.mac_id_n_color
61052ad0f7e9STom Jones 	    = htole32(IWX_FW_CMD_ID_AND_COLOR(in->in_id, in->in_color));
61062ad0f7e9STom Jones 	if (!update) {
61072ad0f7e9STom Jones 		if (ic->ic_opmode == IEEE80211_M_MONITOR)
61082ad0f7e9STom Jones 			IEEE80211_ADDR_COPY(&add_sta_cmd.addr,
61092ad0f7e9STom Jones 			    etheranyaddr);
61102ad0f7e9STom Jones 		else
61112ad0f7e9STom Jones 			IEEE80211_ADDR_COPY(&add_sta_cmd.addr,
61122ad0f7e9STom Jones 			    in->in_macaddr);
61132ad0f7e9STom Jones 	}
61142ad0f7e9STom Jones 	DPRINTF(("%s: add_sta_cmd.addr=%s\n", __func__,
61152ad0f7e9STom Jones 	    ether_sprintf(add_sta_cmd.addr)));
61162ad0f7e9STom Jones 	add_sta_cmd.add_modify = update ? 1 : 0;
61172ad0f7e9STom Jones 	add_sta_cmd.station_flags_msk
61182ad0f7e9STom Jones 	    |= htole32(IWX_STA_FLG_FAT_EN_MSK | IWX_STA_FLG_MIMO_EN_MSK);
61192ad0f7e9STom Jones 
61202ad0f7e9STom Jones 	if (in->in_ni.ni_flags & IEEE80211_NODE_HT) {
61212ad0f7e9STom Jones 		add_sta_cmd.station_flags_msk
61222ad0f7e9STom Jones 		    |= htole32(IWX_STA_FLG_MAX_AGG_SIZE_MSK |
61232ad0f7e9STom Jones 		    IWX_STA_FLG_AGG_MPDU_DENS_MSK);
61242ad0f7e9STom Jones 
61252ad0f7e9STom Jones 		if (iwx_mimo_enabled(sc)) {
61262ad0f7e9STom Jones 			if (ni->ni_flags & IEEE80211_NODE_VHT) {
61272ad0f7e9STom Jones 				add_sta_cmd.station_flags |=
61282ad0f7e9STom Jones 				    htole32(IWX_STA_FLG_MIMO_EN_MIMO2);
61292ad0f7e9STom Jones 			} else {
61302ad0f7e9STom Jones 				int hasmimo = 0;
61312ad0f7e9STom Jones 				for (i = 0; i < htrs->rs_nrates; i++) {
61322ad0f7e9STom Jones 					if (htrs->rs_rates[i] > 7) {
61332ad0f7e9STom Jones 						hasmimo = 1;
61342ad0f7e9STom Jones 						break;
61352ad0f7e9STom Jones 					}
61362ad0f7e9STom Jones 				}
61372ad0f7e9STom Jones 				if (hasmimo) {
61382ad0f7e9STom Jones 					add_sta_cmd.station_flags |=
61392ad0f7e9STom Jones 					    htole32(IWX_STA_FLG_MIMO_EN_MIMO2);
61402ad0f7e9STom Jones 				}
61412ad0f7e9STom Jones 			}
61422ad0f7e9STom Jones 		}
61432ad0f7e9STom Jones 
61442ad0f7e9STom Jones 		if (ni->ni_flags & IEEE80211_NODE_HT &&
61452ad0f7e9STom Jones 		    IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
61462ad0f7e9STom Jones 			add_sta_cmd.station_flags |= htole32(
61472ad0f7e9STom Jones 			    IWX_STA_FLG_FAT_EN_40MHZ);
61482ad0f7e9STom Jones 		}
61492ad0f7e9STom Jones 
61502ad0f7e9STom Jones 
61512ad0f7e9STom Jones 		if (ni->ni_flags & IEEE80211_NODE_VHT) {
61522ad0f7e9STom Jones 			if (IEEE80211_IS_CHAN_VHT80(ni->ni_chan)) {
61532ad0f7e9STom Jones 				add_sta_cmd.station_flags |= htole32(
61542ad0f7e9STom Jones 				    IWX_STA_FLG_FAT_EN_80MHZ);
61552ad0f7e9STom Jones 			}
61562ad0f7e9STom Jones 			// XXX-misha: TODO get real ampdu size
61572ad0f7e9STom Jones 			aggsize = max_aggsize;
61582ad0f7e9STom Jones 		} else {
61592ad0f7e9STom Jones 			aggsize = _IEEE80211_MASKSHIFT(le16toh(ni->ni_htparam),
61602ad0f7e9STom Jones 			    IEEE80211_HTCAP_MAXRXAMPDU);
61612ad0f7e9STom Jones 		}
61622ad0f7e9STom Jones 
61632ad0f7e9STom Jones 		if (aggsize > max_aggsize)
61642ad0f7e9STom Jones 			aggsize = max_aggsize;
61652ad0f7e9STom Jones 		add_sta_cmd.station_flags |= htole32((aggsize <<
61662ad0f7e9STom Jones 		    IWX_STA_FLG_MAX_AGG_SIZE_SHIFT) &
61672ad0f7e9STom Jones 		    IWX_STA_FLG_MAX_AGG_SIZE_MSK);
61682ad0f7e9STom Jones 
61692ad0f7e9STom Jones 		switch (_IEEE80211_MASKSHIFT(le16toh(ni->ni_htparam),
61702ad0f7e9STom Jones 		    IEEE80211_HTCAP_MPDUDENSITY)) {
61712ad0f7e9STom Jones 		case IEEE80211_HTCAP_MPDUDENSITY_2:
61722ad0f7e9STom Jones 			add_sta_cmd.station_flags
61732ad0f7e9STom Jones 			    |= htole32(IWX_STA_FLG_AGG_MPDU_DENS_2US);
61742ad0f7e9STom Jones 			break;
61752ad0f7e9STom Jones 		case IEEE80211_HTCAP_MPDUDENSITY_4:
61762ad0f7e9STom Jones 			add_sta_cmd.station_flags
61772ad0f7e9STom Jones 			    |= htole32(IWX_STA_FLG_AGG_MPDU_DENS_4US);
61782ad0f7e9STom Jones 			break;
61792ad0f7e9STom Jones 		case IEEE80211_HTCAP_MPDUDENSITY_8:
61802ad0f7e9STom Jones 			add_sta_cmd.station_flags
61812ad0f7e9STom Jones 			    |= htole32(IWX_STA_FLG_AGG_MPDU_DENS_8US);
61822ad0f7e9STom Jones 			break;
61832ad0f7e9STom Jones 		case IEEE80211_HTCAP_MPDUDENSITY_16:
61842ad0f7e9STom Jones 			add_sta_cmd.station_flags
61852ad0f7e9STom Jones 			    |= htole32(IWX_STA_FLG_AGG_MPDU_DENS_16US);
61862ad0f7e9STom Jones 			break;
61872ad0f7e9STom Jones 		default:
61882ad0f7e9STom Jones 			break;
61892ad0f7e9STom Jones 		}
61902ad0f7e9STom Jones 	}
61912ad0f7e9STom Jones 
61922ad0f7e9STom Jones 	status = IWX_ADD_STA_SUCCESS;
61932ad0f7e9STom Jones 	err = iwx_send_cmd_pdu_status(sc, IWX_ADD_STA, sizeof(add_sta_cmd),
61942ad0f7e9STom Jones 	    &add_sta_cmd, &status);
61952ad0f7e9STom Jones 	if (!err && (status & IWX_ADD_STA_STATUS_MASK) != IWX_ADD_STA_SUCCESS)
61962ad0f7e9STom Jones 		err = EIO;
61972ad0f7e9STom Jones 
61982ad0f7e9STom Jones 	return err;
61992ad0f7e9STom Jones }
62002ad0f7e9STom Jones 
62012ad0f7e9STom Jones static int
62022ad0f7e9STom Jones iwx_rm_sta_cmd(struct iwx_softc *sc, struct iwx_node *in)
62032ad0f7e9STom Jones {
62042ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
62052ad0f7e9STom Jones 	struct iwx_rm_sta_cmd rm_sta_cmd;
62062ad0f7e9STom Jones 	int err;
62072ad0f7e9STom Jones 
62082ad0f7e9STom Jones 	if ((sc->sc_flags & IWX_FLAG_STA_ACTIVE) == 0)
62092ad0f7e9STom Jones 		panic("sta already removed");
62102ad0f7e9STom Jones 
62112ad0f7e9STom Jones 	memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd));
62122ad0f7e9STom Jones 	if (ic->ic_opmode == IEEE80211_M_MONITOR)
62132ad0f7e9STom Jones 		rm_sta_cmd.sta_id = IWX_MONITOR_STA_ID;
62142ad0f7e9STom Jones 	else
62152ad0f7e9STom Jones 		rm_sta_cmd.sta_id = IWX_STATION_ID;
62162ad0f7e9STom Jones 
62172ad0f7e9STom Jones 	err = iwx_send_cmd_pdu(sc, IWX_REMOVE_STA, 0, sizeof(rm_sta_cmd),
62182ad0f7e9STom Jones 	    &rm_sta_cmd);
62192ad0f7e9STom Jones 
62202ad0f7e9STom Jones 	return err;
62212ad0f7e9STom Jones }
62222ad0f7e9STom Jones 
62232ad0f7e9STom Jones static int
62242ad0f7e9STom Jones iwx_rm_sta(struct iwx_softc *sc, struct iwx_node *in)
62252ad0f7e9STom Jones {
62262ad0f7e9STom Jones 	int err, i, cmd_ver;
62272ad0f7e9STom Jones 
62282ad0f7e9STom Jones 	err = iwx_flush_sta(sc, in);
62292ad0f7e9STom Jones 	if (err) {
62302ad0f7e9STom Jones 		printf("%s: could not flush Tx path (error %d)\n",
62312ad0f7e9STom Jones 		    DEVNAME(sc), err);
62322ad0f7e9STom Jones 		return err;
62332ad0f7e9STom Jones 	}
62342ad0f7e9STom Jones 
62352ad0f7e9STom Jones 	/*
62362ad0f7e9STom Jones 	 * New SCD_QUEUE_CONFIG API requires explicit queue removal
62372ad0f7e9STom Jones 	 * before a station gets removed.
62382ad0f7e9STom Jones 	 */
62392ad0f7e9STom Jones 	cmd_ver = iwx_lookup_cmd_ver(sc, IWX_DATA_PATH_GROUP,
62402ad0f7e9STom Jones 	    IWX_SCD_QUEUE_CONFIG_CMD);
62412ad0f7e9STom Jones 	if (cmd_ver != 0 && cmd_ver != IWX_FW_CMD_VER_UNKNOWN) {
62422ad0f7e9STom Jones 		err = iwx_disable_mgmt_queue(sc);
62432ad0f7e9STom Jones 		if (err)
62442ad0f7e9STom Jones 			return err;
62452ad0f7e9STom Jones 		for (i = IWX_FIRST_AGG_TX_QUEUE;
62462ad0f7e9STom Jones 		    i < IWX_LAST_AGG_TX_QUEUE; i++) {
62472ad0f7e9STom Jones 			struct iwx_tx_ring *ring = &sc->txq[i];
62482ad0f7e9STom Jones 			if ((sc->qenablemsk & (1 << i)) == 0)
62492ad0f7e9STom Jones 				continue;
62502ad0f7e9STom Jones 			err = iwx_disable_txq(sc, IWX_STATION_ID,
62512ad0f7e9STom Jones 			    ring->qid, ring->tid);
62522ad0f7e9STom Jones 			if (err) {
62532ad0f7e9STom Jones 				printf("%s: could not disable Tx queue %d "
62542ad0f7e9STom Jones 				    "(error %d)\n", DEVNAME(sc), ring->qid,
62552ad0f7e9STom Jones 				    err);
62562ad0f7e9STom Jones 				return err;
62572ad0f7e9STom Jones 			}
62582ad0f7e9STom Jones 		}
62592ad0f7e9STom Jones 	}
62602ad0f7e9STom Jones 
62612ad0f7e9STom Jones 	err = iwx_rm_sta_cmd(sc, in);
62622ad0f7e9STom Jones 	if (err) {
62632ad0f7e9STom Jones 		printf("%s: could not remove STA (error %d)\n",
62642ad0f7e9STom Jones 		    DEVNAME(sc), err);
62652ad0f7e9STom Jones 		return err;
62662ad0f7e9STom Jones 	}
62672ad0f7e9STom Jones 
62682ad0f7e9STom Jones 	in->in_flags = 0;
62692ad0f7e9STom Jones 
62702ad0f7e9STom Jones 	sc->sc_rx_ba_sessions = 0;
62712ad0f7e9STom Jones 	sc->ba_rx.start_tidmask = 0;
62722ad0f7e9STom Jones 	sc->ba_rx.stop_tidmask = 0;
62732ad0f7e9STom Jones 	memset(sc->aggqid, 0, sizeof(sc->aggqid));
62742ad0f7e9STom Jones 	sc->ba_tx.start_tidmask = 0;
62752ad0f7e9STom Jones 	sc->ba_tx.stop_tidmask = 0;
62762ad0f7e9STom Jones 	for (i = IWX_FIRST_AGG_TX_QUEUE; i < IWX_LAST_AGG_TX_QUEUE; i++)
62772ad0f7e9STom Jones 		sc->qenablemsk &= ~(1 << i);
62782ad0f7e9STom Jones 
62792ad0f7e9STom Jones #if 0
62802ad0f7e9STom Jones 	for (i = 0; i < IEEE80211_NUM_TID; i++) {
62812ad0f7e9STom Jones 		struct ieee80211_tx_ba *ba = &ni->ni_tx_ba[i];
62822ad0f7e9STom Jones 		if (ba->ba_state != IEEE80211_BA_AGREED)
62832ad0f7e9STom Jones 			continue;
62842ad0f7e9STom Jones 		ieee80211_delba_request(ic, ni, 0, 1, i);
62852ad0f7e9STom Jones 	}
62862ad0f7e9STom Jones #endif
62872ad0f7e9STom Jones 	/* Clear ampdu rx state (GOS-1525) */
62882ad0f7e9STom Jones 	for (i = 0; i < IWX_MAX_TID_COUNT; i++) {
62892ad0f7e9STom Jones 		struct iwx_rx_ba *ba = &sc->ni_rx_ba[i];
62902ad0f7e9STom Jones 		ba->ba_flags = 0;
62912ad0f7e9STom Jones 	}
62922ad0f7e9STom Jones 
62932ad0f7e9STom Jones 	return 0;
62942ad0f7e9STom Jones }
62952ad0f7e9STom Jones 
62962ad0f7e9STom Jones static uint8_t
62972ad0f7e9STom Jones iwx_umac_scan_fill_channels(struct iwx_softc *sc,
62982ad0f7e9STom Jones     struct iwx_scan_channel_cfg_umac *chan, size_t chan_nitems,
62992ad0f7e9STom Jones     int n_ssids, uint32_t channel_cfg_flags)
63002ad0f7e9STom Jones {
63012ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
63022ad0f7e9STom Jones 	struct ieee80211_scan_state *ss = ic->ic_scan;
63032ad0f7e9STom Jones 	struct ieee80211_channel *c;
63042ad0f7e9STom Jones 	uint8_t nchan;
63052ad0f7e9STom Jones 	int j;
63062ad0f7e9STom Jones 
63072ad0f7e9STom Jones 	for (nchan = j = 0;
63082ad0f7e9STom Jones 	    j < ss->ss_last &&
63092ad0f7e9STom Jones 	    nchan < sc->sc_capa_n_scan_channels;
63102ad0f7e9STom Jones 	    j++) {
63112ad0f7e9STom Jones 		uint8_t channel_num;
63122ad0f7e9STom Jones 
63132ad0f7e9STom Jones 		c = ss->ss_chans[j];
63142ad0f7e9STom Jones 		channel_num = ieee80211_mhz2ieee(c->ic_freq, 0);
63152ad0f7e9STom Jones 		if (isset(sc->sc_ucode_api,
63162ad0f7e9STom Jones 		    IWX_UCODE_TLV_API_SCAN_EXT_CHAN_VER)) {
63172ad0f7e9STom Jones 			chan->v2.channel_num = channel_num;
63182ad0f7e9STom Jones 			if (IEEE80211_IS_CHAN_2GHZ(c))
63192ad0f7e9STom Jones 				chan->v2.band = IWX_PHY_BAND_24;
63202ad0f7e9STom Jones 			else
63212ad0f7e9STom Jones 				chan->v2.band = IWX_PHY_BAND_5;
63222ad0f7e9STom Jones 			chan->v2.iter_count = 1;
63232ad0f7e9STom Jones 			chan->v2.iter_interval = 0;
63242ad0f7e9STom Jones 		} else {
63252ad0f7e9STom Jones 			chan->v1.channel_num = channel_num;
63262ad0f7e9STom Jones 			chan->v1.iter_count = 1;
63272ad0f7e9STom Jones 			chan->v1.iter_interval = htole16(0);
63282ad0f7e9STom Jones 		}
63292ad0f7e9STom Jones 		chan->flags |= htole32(channel_cfg_flags);
63302ad0f7e9STom Jones 		chan++;
63312ad0f7e9STom Jones 		nchan++;
63322ad0f7e9STom Jones 	}
63332ad0f7e9STom Jones 
63342ad0f7e9STom Jones 	return nchan;
63352ad0f7e9STom Jones }
63362ad0f7e9STom Jones 
63372ad0f7e9STom Jones static int
63382ad0f7e9STom Jones iwx_fill_probe_req(struct iwx_softc *sc, struct iwx_scan_probe_req *preq)
63392ad0f7e9STom Jones {
63402ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
63412ad0f7e9STom Jones 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
63422ad0f7e9STom Jones 	struct ieee80211_frame *wh = (struct ieee80211_frame *)preq->buf;
63432ad0f7e9STom Jones 	struct ieee80211_rateset *rs;
63442ad0f7e9STom Jones 	size_t remain = sizeof(preq->buf);
63452ad0f7e9STom Jones 	uint8_t *frm, *pos;
63462ad0f7e9STom Jones 
63472ad0f7e9STom Jones 	memset(preq, 0, sizeof(*preq));
63482ad0f7e9STom Jones 
63492ad0f7e9STom Jones 	if (remain < sizeof(*wh) + 2)
63502ad0f7e9STom Jones 		return ENOBUFS;
63512ad0f7e9STom Jones 
63522ad0f7e9STom Jones 	/*
63532ad0f7e9STom Jones 	 * Build a probe request frame.  Most of the following code is a
63542ad0f7e9STom Jones 	 * copy & paste of what is done in net80211.
63552ad0f7e9STom Jones 	 */
63562ad0f7e9STom Jones 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
63572ad0f7e9STom Jones 	    IEEE80211_FC0_SUBTYPE_PROBE_REQ;
63582ad0f7e9STom Jones 	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
63592ad0f7e9STom Jones 	IEEE80211_ADDR_COPY(wh->i_addr1, etherbroadcastaddr);
63602ad0f7e9STom Jones 	IEEE80211_ADDR_COPY(wh->i_addr2, vap ? vap->iv_myaddr : ic->ic_macaddr);
63612ad0f7e9STom Jones 	IEEE80211_ADDR_COPY(wh->i_addr3, etherbroadcastaddr);
63622ad0f7e9STom Jones 	*(uint16_t *)&wh->i_dur[0] = 0;	/* filled by HW */
63632ad0f7e9STom Jones 	*(uint16_t *)&wh->i_seq[0] = 0;	/* filled by HW */
63642ad0f7e9STom Jones 
63652ad0f7e9STom Jones 	frm = (uint8_t *)(wh + 1);
63662ad0f7e9STom Jones 	*frm++ = IEEE80211_ELEMID_SSID;
63672ad0f7e9STom Jones 	*frm++ = 0;
63682ad0f7e9STom Jones 	/* hardware inserts SSID */
63692ad0f7e9STom Jones 
63702ad0f7e9STom Jones 	/* Tell the firmware where the MAC header is. */
63712ad0f7e9STom Jones 	preq->mac_header.offset = 0;
63722ad0f7e9STom Jones 	preq->mac_header.len = htole16(frm - (uint8_t *)wh);
63732ad0f7e9STom Jones 	remain -= frm - (uint8_t *)wh;
63742ad0f7e9STom Jones 
63752ad0f7e9STom Jones 	/* Fill in 2GHz IEs and tell firmware where they are. */
63762ad0f7e9STom Jones 	rs = &ic->ic_sup_rates[IEEE80211_MODE_11G];
63772ad0f7e9STom Jones 	if (rs->rs_nrates > IEEE80211_RATE_SIZE) {
63782ad0f7e9STom Jones 		if (remain < 4 + rs->rs_nrates)
63792ad0f7e9STom Jones 			return ENOBUFS;
63802ad0f7e9STom Jones 	} else if (remain < 2 + rs->rs_nrates)
63812ad0f7e9STom Jones 		return ENOBUFS;
63822ad0f7e9STom Jones 	preq->band_data[0].offset = htole16(frm - (uint8_t *)wh);
63832ad0f7e9STom Jones 	pos = frm;
63842ad0f7e9STom Jones 	frm = ieee80211_add_rates(frm, rs);
63852ad0f7e9STom Jones 	if (rs->rs_nrates > IEEE80211_RATE_SIZE)
63862ad0f7e9STom Jones 		frm = ieee80211_add_xrates(frm, rs);
63872ad0f7e9STom Jones 	remain -= frm - pos;
63882ad0f7e9STom Jones 
63892ad0f7e9STom Jones 	if (isset(sc->sc_enabled_capa,
63902ad0f7e9STom Jones 	    IWX_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT)) {
63912ad0f7e9STom Jones 		if (remain < 3)
63922ad0f7e9STom Jones 			return ENOBUFS;
63932ad0f7e9STom Jones 		*frm++ = IEEE80211_ELEMID_DSPARMS;
63942ad0f7e9STom Jones 		*frm++ = 1;
63952ad0f7e9STom Jones 		*frm++ = 0;
63962ad0f7e9STom Jones 		remain -= 3;
63972ad0f7e9STom Jones 	}
63982ad0f7e9STom Jones 	preq->band_data[0].len = htole16(frm - pos);
63992ad0f7e9STom Jones 
64002ad0f7e9STom Jones 	if (sc->sc_nvm.sku_cap_band_52GHz_enable) {
64012ad0f7e9STom Jones 		/* Fill in 5GHz IEs. */
64022ad0f7e9STom Jones 		rs = &ic->ic_sup_rates[IEEE80211_MODE_11A];
64032ad0f7e9STom Jones 		if (rs->rs_nrates > IEEE80211_RATE_SIZE) {
64042ad0f7e9STom Jones 			if (remain < 4 + rs->rs_nrates)
64052ad0f7e9STom Jones 				return ENOBUFS;
64062ad0f7e9STom Jones 		} else if (remain < 2 + rs->rs_nrates)
64072ad0f7e9STom Jones 			return ENOBUFS;
64082ad0f7e9STom Jones 		preq->band_data[1].offset = htole16(frm - (uint8_t *)wh);
64092ad0f7e9STom Jones 		pos = frm;
64102ad0f7e9STom Jones 		frm = ieee80211_add_rates(frm, rs);
64112ad0f7e9STom Jones 		if (rs->rs_nrates > IEEE80211_RATE_SIZE)
64122ad0f7e9STom Jones 			frm = ieee80211_add_xrates(frm, rs);
64132ad0f7e9STom Jones 		preq->band_data[1].len = htole16(frm - pos);
64142ad0f7e9STom Jones 		remain -= frm - pos;
64152ad0f7e9STom Jones 		if (vap->iv_vht_flags & IEEE80211_FVHT_VHT) {
64162ad0f7e9STom Jones 			if (remain < 14)
64172ad0f7e9STom Jones 				return ENOBUFS;
64182ad0f7e9STom Jones 			frm = ieee80211_add_vhtcap(frm, vap->iv_bss);
64192ad0f7e9STom Jones 			remain -= frm - pos;
64202ad0f7e9STom Jones 			preq->band_data[1].len = htole16(frm - pos);
64212ad0f7e9STom Jones 		}
64222ad0f7e9STom Jones 	}
64232ad0f7e9STom Jones 
64242ad0f7e9STom Jones 	/* Send 11n IEs on both 2GHz and 5GHz bands. */
64252ad0f7e9STom Jones 	preq->common_data.offset = htole16(frm - (uint8_t *)wh);
64262ad0f7e9STom Jones 	pos = frm;
64272ad0f7e9STom Jones 	if (vap->iv_flags_ht & IEEE80211_FHT_HT) {
64282ad0f7e9STom Jones 		if (remain < 28)
64292ad0f7e9STom Jones 			return ENOBUFS;
64302ad0f7e9STom Jones 		frm = ieee80211_add_htcap(frm, vap->iv_bss);
64312ad0f7e9STom Jones 		/* XXX add WME info? */
64322ad0f7e9STom Jones 		remain -= frm - pos;
64332ad0f7e9STom Jones 	}
64342ad0f7e9STom Jones 
64352ad0f7e9STom Jones 	preq->common_data.len = htole16(frm - pos);
64362ad0f7e9STom Jones 
64372ad0f7e9STom Jones 	return 0;
64382ad0f7e9STom Jones }
64392ad0f7e9STom Jones 
64402ad0f7e9STom Jones static int
64412ad0f7e9STom Jones iwx_config_umac_scan_reduced(struct iwx_softc *sc)
64422ad0f7e9STom Jones {
64432ad0f7e9STom Jones 	struct iwx_scan_config scan_cfg;
64442ad0f7e9STom Jones 	struct iwx_host_cmd hcmd = {
64452ad0f7e9STom Jones 		.id = iwx_cmd_id(IWX_SCAN_CFG_CMD, IWX_LONG_GROUP, 0),
64462ad0f7e9STom Jones 		.len[0] = sizeof(scan_cfg),
64472ad0f7e9STom Jones 		.data[0] = &scan_cfg,
64482ad0f7e9STom Jones 		.flags = 0,
64492ad0f7e9STom Jones 	};
64502ad0f7e9STom Jones 	int cmdver;
64512ad0f7e9STom Jones 
64522ad0f7e9STom Jones 	if (!isset(sc->sc_ucode_api, IWX_UCODE_TLV_API_REDUCED_SCAN_CONFIG)) {
64532ad0f7e9STom Jones 		printf("%s: firmware does not support reduced scan config\n",
64542ad0f7e9STom Jones 		    DEVNAME(sc));
64552ad0f7e9STom Jones 		return ENOTSUP;
64562ad0f7e9STom Jones 	}
64572ad0f7e9STom Jones 
64582ad0f7e9STom Jones 	memset(&scan_cfg, 0, sizeof(scan_cfg));
64592ad0f7e9STom Jones 
64602ad0f7e9STom Jones 	/*
64612ad0f7e9STom Jones 	 * SCAN_CFG version >= 5 implies that the broadcast
64622ad0f7e9STom Jones 	 * STA ID field is deprecated.
64632ad0f7e9STom Jones 	 */
64642ad0f7e9STom Jones 	cmdver = iwx_lookup_cmd_ver(sc, IWX_LONG_GROUP, IWX_SCAN_CFG_CMD);
64652ad0f7e9STom Jones 	if (cmdver == IWX_FW_CMD_VER_UNKNOWN || cmdver < 5)
64662ad0f7e9STom Jones 		scan_cfg.bcast_sta_id = 0xff;
64672ad0f7e9STom Jones 
64682ad0f7e9STom Jones 	scan_cfg.tx_chains = htole32(iwx_fw_valid_tx_ant(sc));
64692ad0f7e9STom Jones 	scan_cfg.rx_chains = htole32(iwx_fw_valid_rx_ant(sc));
64702ad0f7e9STom Jones 
64712ad0f7e9STom Jones 	return iwx_send_cmd(sc, &hcmd);
64722ad0f7e9STom Jones }
64732ad0f7e9STom Jones 
64742ad0f7e9STom Jones static uint16_t
64752ad0f7e9STom Jones iwx_scan_umac_flags_v2(struct iwx_softc *sc, int bgscan)
64762ad0f7e9STom Jones {
64772ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
64782ad0f7e9STom Jones 	struct ieee80211_scan_state *ss = ic->ic_scan;
64792ad0f7e9STom Jones 	uint16_t flags = 0;
64802ad0f7e9STom Jones 
64812ad0f7e9STom Jones 	if (ss->ss_nssid == 0) {
64822ad0f7e9STom Jones 		DPRINTF(("%s: Passive scan started\n", __func__));
64832ad0f7e9STom Jones 		flags |= IWX_UMAC_SCAN_GEN_FLAGS_V2_FORCE_PASSIVE;
64842ad0f7e9STom Jones 	}
64852ad0f7e9STom Jones 
64862ad0f7e9STom Jones 	flags |= IWX_UMAC_SCAN_GEN_FLAGS_V2_PASS_ALL;
64872ad0f7e9STom Jones 	flags |= IWX_UMAC_SCAN_GEN_FLAGS_V2_NTFY_ITER_COMPLETE;
64882ad0f7e9STom Jones 	flags |= IWX_UMAC_SCAN_GEN_FLAGS_V2_ADAPTIVE_DWELL;
64892ad0f7e9STom Jones 
64902ad0f7e9STom Jones 	return flags;
64912ad0f7e9STom Jones }
64922ad0f7e9STom Jones 
64932ad0f7e9STom Jones #define IWX_SCAN_DWELL_ACTIVE		10
64942ad0f7e9STom Jones #define IWX_SCAN_DWELL_PASSIVE		110
64952ad0f7e9STom Jones 
64962ad0f7e9STom Jones /* adaptive dwell max budget time [TU] for full scan */
64972ad0f7e9STom Jones #define IWX_SCAN_ADWELL_MAX_BUDGET_FULL_SCAN 300
64982ad0f7e9STom Jones /* adaptive dwell max budget time [TU] for directed scan */
64992ad0f7e9STom Jones #define IWX_SCAN_ADWELL_MAX_BUDGET_DIRECTED_SCAN 100
65002ad0f7e9STom Jones /* adaptive dwell default high band APs number */
65012ad0f7e9STom Jones #define IWX_SCAN_ADWELL_DEFAULT_HB_N_APS 8
65022ad0f7e9STom Jones /* adaptive dwell default low band APs number */
65032ad0f7e9STom Jones #define IWX_SCAN_ADWELL_DEFAULT_LB_N_APS 2
65042ad0f7e9STom Jones /* adaptive dwell default APs number in social channels (1, 6, 11) */
65052ad0f7e9STom Jones #define IWX_SCAN_ADWELL_DEFAULT_N_APS_SOCIAL 10
65062ad0f7e9STom Jones /* adaptive dwell number of APs override for p2p friendly GO channels */
65072ad0f7e9STom Jones #define IWX_SCAN_ADWELL_N_APS_GO_FRIENDLY 10
65082ad0f7e9STom Jones /* adaptive dwell number of APs override for social channels */
65092ad0f7e9STom Jones #define IWX_SCAN_ADWELL_N_APS_SOCIAL_CHS 2
65102ad0f7e9STom Jones 
65112ad0f7e9STom Jones static void
65122ad0f7e9STom Jones iwx_scan_umac_dwell_v10(struct iwx_softc *sc,
65132ad0f7e9STom Jones     struct iwx_scan_general_params_v10 *general_params, int bgscan)
65142ad0f7e9STom Jones {
65152ad0f7e9STom Jones 	uint32_t suspend_time, max_out_time;
65162ad0f7e9STom Jones 	uint8_t active_dwell, passive_dwell;
65172ad0f7e9STom Jones 
65182ad0f7e9STom Jones 	active_dwell = IWX_SCAN_DWELL_ACTIVE;
65192ad0f7e9STom Jones 	passive_dwell = IWX_SCAN_DWELL_PASSIVE;
65202ad0f7e9STom Jones 
65212ad0f7e9STom Jones 	general_params->adwell_default_social_chn =
65222ad0f7e9STom Jones 		IWX_SCAN_ADWELL_DEFAULT_N_APS_SOCIAL;
65232ad0f7e9STom Jones 	general_params->adwell_default_2g = IWX_SCAN_ADWELL_DEFAULT_LB_N_APS;
65242ad0f7e9STom Jones 	general_params->adwell_default_5g = IWX_SCAN_ADWELL_DEFAULT_HB_N_APS;
65252ad0f7e9STom Jones 
65262ad0f7e9STom Jones 	if (bgscan)
65272ad0f7e9STom Jones 		general_params->adwell_max_budget =
65282ad0f7e9STom Jones 			htole16(IWX_SCAN_ADWELL_MAX_BUDGET_DIRECTED_SCAN);
65292ad0f7e9STom Jones 	else
65302ad0f7e9STom Jones 		general_params->adwell_max_budget =
65312ad0f7e9STom Jones 			htole16(IWX_SCAN_ADWELL_MAX_BUDGET_FULL_SCAN);
65322ad0f7e9STom Jones 
65332ad0f7e9STom Jones 	general_params->scan_priority = htole32(IWX_SCAN_PRIORITY_EXT_6);
65342ad0f7e9STom Jones 	if (bgscan) {
65352ad0f7e9STom Jones 		max_out_time = htole32(120);
65362ad0f7e9STom Jones 		suspend_time = htole32(120);
65372ad0f7e9STom Jones 	} else {
65382ad0f7e9STom Jones 		max_out_time = htole32(0);
65392ad0f7e9STom Jones 		suspend_time = htole32(0);
65402ad0f7e9STom Jones 	}
65412ad0f7e9STom Jones 	general_params->max_out_of_time[IWX_SCAN_LB_LMAC_IDX] =
65422ad0f7e9STom Jones 		htole32(max_out_time);
65432ad0f7e9STom Jones 	general_params->suspend_time[IWX_SCAN_LB_LMAC_IDX] =
65442ad0f7e9STom Jones 		htole32(suspend_time);
65452ad0f7e9STom Jones 	general_params->max_out_of_time[IWX_SCAN_HB_LMAC_IDX] =
65462ad0f7e9STom Jones 		htole32(max_out_time);
65472ad0f7e9STom Jones 	general_params->suspend_time[IWX_SCAN_HB_LMAC_IDX] =
65482ad0f7e9STom Jones 		htole32(suspend_time);
65492ad0f7e9STom Jones 
65502ad0f7e9STom Jones 	general_params->active_dwell[IWX_SCAN_LB_LMAC_IDX] = active_dwell;
65512ad0f7e9STom Jones 	general_params->passive_dwell[IWX_SCAN_LB_LMAC_IDX] = passive_dwell;
65522ad0f7e9STom Jones 	general_params->active_dwell[IWX_SCAN_HB_LMAC_IDX] = active_dwell;
65532ad0f7e9STom Jones 	general_params->passive_dwell[IWX_SCAN_HB_LMAC_IDX] = passive_dwell;
65542ad0f7e9STom Jones }
65552ad0f7e9STom Jones 
65562ad0f7e9STom Jones static void
65572ad0f7e9STom Jones iwx_scan_umac_fill_general_p_v10(struct iwx_softc *sc,
65582ad0f7e9STom Jones     struct iwx_scan_general_params_v10 *gp, uint16_t gen_flags, int bgscan)
65592ad0f7e9STom Jones {
65602ad0f7e9STom Jones 	iwx_scan_umac_dwell_v10(sc, gp, bgscan);
65612ad0f7e9STom Jones 
65622ad0f7e9STom Jones 	gp->flags = htole16(gen_flags);
65632ad0f7e9STom Jones 
65642ad0f7e9STom Jones 	if (gen_flags & IWX_UMAC_SCAN_GEN_FLAGS_V2_FRAGMENTED_LMAC1)
65652ad0f7e9STom Jones 		gp->num_of_fragments[IWX_SCAN_LB_LMAC_IDX] = 3;
65662ad0f7e9STom Jones 	if (gen_flags & IWX_UMAC_SCAN_GEN_FLAGS_V2_FRAGMENTED_LMAC2)
65672ad0f7e9STom Jones 		gp->num_of_fragments[IWX_SCAN_HB_LMAC_IDX] = 3;
65682ad0f7e9STom Jones 
65692ad0f7e9STom Jones 	gp->scan_start_mac_id = 0;
65702ad0f7e9STom Jones }
65712ad0f7e9STom Jones 
65722ad0f7e9STom Jones static void
65732ad0f7e9STom Jones iwx_scan_umac_fill_ch_p_v6(struct iwx_softc *sc,
65742ad0f7e9STom Jones     struct iwx_scan_channel_params_v6 *cp, uint32_t channel_cfg_flags,
65752ad0f7e9STom Jones     int n_ssid)
65762ad0f7e9STom Jones {
65772ad0f7e9STom Jones 	cp->flags = IWX_SCAN_CHANNEL_FLAG_ENABLE_CHAN_ORDER;
65782ad0f7e9STom Jones 
65792ad0f7e9STom Jones 	cp->count = iwx_umac_scan_fill_channels(sc, cp->channel_config,
65802ad0f7e9STom Jones 	    nitems(cp->channel_config), n_ssid, channel_cfg_flags);
65812ad0f7e9STom Jones 
65822ad0f7e9STom Jones 	cp->n_aps_override[0] = IWX_SCAN_ADWELL_N_APS_GO_FRIENDLY;
65832ad0f7e9STom Jones 	cp->n_aps_override[1] = IWX_SCAN_ADWELL_N_APS_SOCIAL_CHS;
65842ad0f7e9STom Jones }
65852ad0f7e9STom Jones 
65862ad0f7e9STom Jones static int
65872ad0f7e9STom Jones iwx_umac_scan_v14(struct iwx_softc *sc, int bgscan)
65882ad0f7e9STom Jones {
65892ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
65902ad0f7e9STom Jones 	struct ieee80211_scan_state *ss = ic->ic_scan;
65912ad0f7e9STom Jones 	struct iwx_host_cmd hcmd = {
65922ad0f7e9STom Jones 		.id = iwx_cmd_id(IWX_SCAN_REQ_UMAC, IWX_LONG_GROUP, 0),
65932ad0f7e9STom Jones 		.len = { 0, },
65942ad0f7e9STom Jones 		.data = { NULL, },
65952ad0f7e9STom Jones 		.flags = 0,
65962ad0f7e9STom Jones 	};
65972ad0f7e9STom Jones 	struct iwx_scan_req_umac_v14 *cmd = &sc->sc_umac_v14_cmd;
65982ad0f7e9STom Jones 	struct iwx_scan_req_params_v14 *scan_p;
65992ad0f7e9STom Jones 	int err, async = bgscan, n_ssid = 0;
66002ad0f7e9STom Jones 	uint16_t gen_flags;
66012ad0f7e9STom Jones 	uint32_t bitmap_ssid = 0;
66022ad0f7e9STom Jones 
66032ad0f7e9STom Jones 	IWX_ASSERT_LOCKED(sc);
66042ad0f7e9STom Jones 
66052ad0f7e9STom Jones 	bzero(cmd, sizeof(struct iwx_scan_req_umac_v14));
66062ad0f7e9STom Jones 
66072ad0f7e9STom Jones 	scan_p = &cmd->scan_params;
66082ad0f7e9STom Jones 
66092ad0f7e9STom Jones 	cmd->ooc_priority = htole32(IWX_SCAN_PRIORITY_EXT_6);
66102ad0f7e9STom Jones 	cmd->uid = htole32(0);
66112ad0f7e9STom Jones 
66122ad0f7e9STom Jones 	gen_flags = iwx_scan_umac_flags_v2(sc, bgscan);
66132ad0f7e9STom Jones 	iwx_scan_umac_fill_general_p_v10(sc, &scan_p->general_params,
66142ad0f7e9STom Jones 	    gen_flags, bgscan);
66152ad0f7e9STom Jones 
66162ad0f7e9STom Jones 	scan_p->periodic_params.schedule[0].interval = htole16(0);
66172ad0f7e9STom Jones 	scan_p->periodic_params.schedule[0].iter_count = 1;
66182ad0f7e9STom Jones 
66192ad0f7e9STom Jones 	err = iwx_fill_probe_req(sc, &scan_p->probe_params.preq);
66202ad0f7e9STom Jones 	if (err) {
66212ad0f7e9STom Jones 		printf("%s: iwx_fill_probe_req failed (error %d)\n", __func__,
66222ad0f7e9STom Jones 		    err);
66232ad0f7e9STom Jones 		return err;
66242ad0f7e9STom Jones 	}
66252ad0f7e9STom Jones 
66262ad0f7e9STom Jones 	for (int i=0; i < ss->ss_nssid; i++) {
66272ad0f7e9STom Jones 		scan_p->probe_params.direct_scan[i].id = IEEE80211_ELEMID_SSID;
66282ad0f7e9STom Jones 		scan_p->probe_params.direct_scan[i].len =
66292ad0f7e9STom Jones 		    MIN(ss->ss_ssid[i].len, IEEE80211_NWID_LEN);
66302ad0f7e9STom Jones 		DPRINTF(("%s: Active scan started for ssid ", __func__));
66312ad0f7e9STom Jones 		memcpy(scan_p->probe_params.direct_scan[i].ssid,
66322ad0f7e9STom Jones 		    ss->ss_ssid[i].ssid, ss->ss_ssid[i].len);
66332ad0f7e9STom Jones 		n_ssid++;
66342ad0f7e9STom Jones 		bitmap_ssid |= (1 << i);
66352ad0f7e9STom Jones 	}
66362ad0f7e9STom Jones 	DPRINTF(("%s: bitmap_ssid=0x%x\n", __func__, bitmap_ssid));
66372ad0f7e9STom Jones 
66382ad0f7e9STom Jones 	iwx_scan_umac_fill_ch_p_v6(sc, &scan_p->channel_params, bitmap_ssid,
66392ad0f7e9STom Jones 	    n_ssid);
66402ad0f7e9STom Jones 
66412ad0f7e9STom Jones 	hcmd.len[0] = sizeof(*cmd);
66422ad0f7e9STom Jones 	hcmd.data[0] = (void *)cmd;
66432ad0f7e9STom Jones 	hcmd.flags |= async ? IWX_CMD_ASYNC : 0;
66442ad0f7e9STom Jones 
66452ad0f7e9STom Jones 	err = iwx_send_cmd(sc, &hcmd);
66462ad0f7e9STom Jones 	return err;
66472ad0f7e9STom Jones }
66482ad0f7e9STom Jones 
66492ad0f7e9STom Jones static void
66502ad0f7e9STom Jones iwx_mcc_update(struct iwx_softc *sc, struct iwx_mcc_chub_notif *notif)
66512ad0f7e9STom Jones {
66522ad0f7e9STom Jones 	char alpha2[3];
66532ad0f7e9STom Jones 
66542ad0f7e9STom Jones 	snprintf(alpha2, sizeof(alpha2), "%c%c",
66552ad0f7e9STom Jones 	    (le16toh(notif->mcc) & 0xff00) >> 8, le16toh(notif->mcc) & 0xff);
66562ad0f7e9STom Jones 
66572ad0f7e9STom Jones 	IWX_DPRINTF(sc, IWX_DEBUG_FW, "%s: firmware has detected regulatory domain '%s' "
66582ad0f7e9STom Jones 		    "(0x%x)\n", DEVNAME(sc), alpha2, le16toh(notif->mcc));
66592ad0f7e9STom Jones 
66602ad0f7e9STom Jones 	/* TODO: Schedule a task to send MCC_UPDATE_CMD? */
66612ad0f7e9STom Jones }
66622ad0f7e9STom Jones 
66632ad0f7e9STom Jones uint8_t
66642ad0f7e9STom Jones iwx_ridx2rate(struct ieee80211_rateset *rs, int ridx)
66652ad0f7e9STom Jones {
66662ad0f7e9STom Jones 	int i;
66672ad0f7e9STom Jones 	uint8_t rval;
66682ad0f7e9STom Jones 
66692ad0f7e9STom Jones 	for (i = 0; i < rs->rs_nrates; i++) {
66702ad0f7e9STom Jones 		rval = (rs->rs_rates[i] & IEEE80211_RATE_VAL);
66712ad0f7e9STom Jones 		if (rval == iwx_rates[ridx].rate)
66722ad0f7e9STom Jones 			return rs->rs_rates[i];
66732ad0f7e9STom Jones 	}
66742ad0f7e9STom Jones 
66752ad0f7e9STom Jones 	return 0;
66762ad0f7e9STom Jones }
66772ad0f7e9STom Jones 
66782ad0f7e9STom Jones static int
66792ad0f7e9STom Jones iwx_rval2ridx(int rval)
66802ad0f7e9STom Jones {
66812ad0f7e9STom Jones 	int ridx;
66822ad0f7e9STom Jones 
66832ad0f7e9STom Jones 	for (ridx = 0; ridx < nitems(iwx_rates); ridx++) {
66842ad0f7e9STom Jones 		if (iwx_rates[ridx].plcp == IWX_RATE_INVM_PLCP)
66852ad0f7e9STom Jones 			continue;
66862ad0f7e9STom Jones 		if (rval == iwx_rates[ridx].rate)
66872ad0f7e9STom Jones 			break;
66882ad0f7e9STom Jones 	}
66892ad0f7e9STom Jones 
66902ad0f7e9STom Jones        return ridx;
66912ad0f7e9STom Jones }
66922ad0f7e9STom Jones 
66932ad0f7e9STom Jones static void
66942ad0f7e9STom Jones iwx_ack_rates(struct iwx_softc *sc, struct iwx_node *in, int *cck_rates,
66952ad0f7e9STom Jones     int *ofdm_rates)
66962ad0f7e9STom Jones {
66972ad0f7e9STom Jones 	struct ieee80211_node *ni = &in->in_ni;
66982ad0f7e9STom Jones 	struct ieee80211_rateset *rs = &ni->ni_rates;
66992ad0f7e9STom Jones 	int lowest_present_ofdm = -1;
67002ad0f7e9STom Jones 	int lowest_present_cck = -1;
67012ad0f7e9STom Jones 	uint8_t cck = 0;
67022ad0f7e9STom Jones 	uint8_t ofdm = 0;
67032ad0f7e9STom Jones 	int i;
67042ad0f7e9STom Jones 
67052ad0f7e9STom Jones 	if (ni->ni_chan == IEEE80211_CHAN_ANYC ||
67062ad0f7e9STom Jones 	    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) {
67072ad0f7e9STom Jones 		for (i = IWX_FIRST_CCK_RATE; i < IWX_FIRST_OFDM_RATE; i++) {
67082ad0f7e9STom Jones 			if ((iwx_ridx2rate(rs, i) & IEEE80211_RATE_BASIC) == 0)
67092ad0f7e9STom Jones 				continue;
67102ad0f7e9STom Jones 			cck |= (1 << i);
67112ad0f7e9STom Jones 			if (lowest_present_cck == -1 || lowest_present_cck > i)
67122ad0f7e9STom Jones 				lowest_present_cck = i;
67132ad0f7e9STom Jones 		}
67142ad0f7e9STom Jones 	}
67152ad0f7e9STom Jones 	for (i = IWX_FIRST_OFDM_RATE; i <= IWX_LAST_NON_HT_RATE; i++) {
67162ad0f7e9STom Jones 		if ((iwx_ridx2rate(rs, i) & IEEE80211_RATE_BASIC) == 0)
67172ad0f7e9STom Jones 			continue;
67182ad0f7e9STom Jones 		ofdm |= (1 << (i - IWX_FIRST_OFDM_RATE));
67192ad0f7e9STom Jones 		if (lowest_present_ofdm == -1 || lowest_present_ofdm > i)
67202ad0f7e9STom Jones 			lowest_present_ofdm = i;
67212ad0f7e9STom Jones 	}
67222ad0f7e9STom Jones 
67232ad0f7e9STom Jones 	/*
67242ad0f7e9STom Jones 	 * Now we've got the basic rates as bitmaps in the ofdm and cck
67252ad0f7e9STom Jones 	 * variables. This isn't sufficient though, as there might not
67262ad0f7e9STom Jones 	 * be all the right rates in the bitmap. E.g. if the only basic
67272ad0f7e9STom Jones 	 * rates are 5.5 Mbps and 11 Mbps, we still need to add 1 Mbps
67282ad0f7e9STom Jones 	 * and 6 Mbps because the 802.11-2007 standard says in 9.6:
67292ad0f7e9STom Jones 	 *
67302ad0f7e9STom Jones 	 *    [...] a STA responding to a received frame shall transmit
67312ad0f7e9STom Jones 	 *    its Control Response frame [...] at the highest rate in the
67322ad0f7e9STom Jones 	 *    BSSBasicRateSet parameter that is less than or equal to the
67332ad0f7e9STom Jones 	 *    rate of the immediately previous frame in the frame exchange
67342ad0f7e9STom Jones 	 *    sequence ([...]) and that is of the same modulation class
67352ad0f7e9STom Jones 	 *    ([...]) as the received frame. If no rate contained in the
67362ad0f7e9STom Jones 	 *    BSSBasicRateSet parameter meets these conditions, then the
67372ad0f7e9STom Jones 	 *    control frame sent in response to a received frame shall be
67382ad0f7e9STom Jones 	 *    transmitted at the highest mandatory rate of the PHY that is
67392ad0f7e9STom Jones 	 *    less than or equal to the rate of the received frame, and
67402ad0f7e9STom Jones 	 *    that is of the same modulation class as the received frame.
67412ad0f7e9STom Jones 	 *
67422ad0f7e9STom Jones 	 * As a consequence, we need to add all mandatory rates that are
67432ad0f7e9STom Jones 	 * lower than all of the basic rates to these bitmaps.
67442ad0f7e9STom Jones 	 */
67452ad0f7e9STom Jones 
67462ad0f7e9STom Jones 	if (IWX_RATE_24M_INDEX < lowest_present_ofdm)
67472ad0f7e9STom Jones 		ofdm |= IWX_RATE_BIT_MSK(24) >> IWX_FIRST_OFDM_RATE;
67482ad0f7e9STom Jones 	if (IWX_RATE_12M_INDEX < lowest_present_ofdm)
67492ad0f7e9STom Jones 		ofdm |= IWX_RATE_BIT_MSK(12) >> IWX_FIRST_OFDM_RATE;
67502ad0f7e9STom Jones 	/* 6M already there or needed so always add */
67512ad0f7e9STom Jones 	ofdm |= IWX_RATE_BIT_MSK(6) >> IWX_FIRST_OFDM_RATE;
67522ad0f7e9STom Jones 
67532ad0f7e9STom Jones 	/*
67542ad0f7e9STom Jones 	 * CCK is a bit more complex with DSSS vs. HR/DSSS vs. ERP.
67552ad0f7e9STom Jones 	 * Note, however:
67562ad0f7e9STom Jones 	 *  - if no CCK rates are basic, it must be ERP since there must
67572ad0f7e9STom Jones 	 *    be some basic rates at all, so they're OFDM => ERP PHY
67582ad0f7e9STom Jones 	 *    (or we're in 5 GHz, and the cck bitmap will never be used)
67592ad0f7e9STom Jones 	 *  - if 11M is a basic rate, it must be ERP as well, so add 5.5M
67602ad0f7e9STom Jones 	 *  - if 5.5M is basic, 1M and 2M are mandatory
67612ad0f7e9STom Jones 	 *  - if 2M is basic, 1M is mandatory
67622ad0f7e9STom Jones 	 *  - if 1M is basic, that's the only valid ACK rate.
67632ad0f7e9STom Jones 	 * As a consequence, it's not as complicated as it sounds, just add
67642ad0f7e9STom Jones 	 * any lower rates to the ACK rate bitmap.
67652ad0f7e9STom Jones 	 */
67662ad0f7e9STom Jones 	if (IWX_RATE_11M_INDEX < lowest_present_cck)
67672ad0f7e9STom Jones 		cck |= IWX_RATE_BIT_MSK(11) >> IWX_FIRST_CCK_RATE;
67682ad0f7e9STom Jones 	if (IWX_RATE_5M_INDEX < lowest_present_cck)
67692ad0f7e9STom Jones 		cck |= IWX_RATE_BIT_MSK(5) >> IWX_FIRST_CCK_RATE;
67702ad0f7e9STom Jones 	if (IWX_RATE_2M_INDEX < lowest_present_cck)
67712ad0f7e9STom Jones 		cck |= IWX_RATE_BIT_MSK(2) >> IWX_FIRST_CCK_RATE;
67722ad0f7e9STom Jones 	/* 1M already there or needed so always add */
67732ad0f7e9STom Jones 	cck |= IWX_RATE_BIT_MSK(1) >> IWX_FIRST_CCK_RATE;
67742ad0f7e9STom Jones 
67752ad0f7e9STom Jones 	*cck_rates = cck;
67762ad0f7e9STom Jones 	*ofdm_rates = ofdm;
67772ad0f7e9STom Jones }
67782ad0f7e9STom Jones 
67792ad0f7e9STom Jones static void
67802ad0f7e9STom Jones iwx_mac_ctxt_cmd_common(struct iwx_softc *sc, struct iwx_node *in,
67812ad0f7e9STom Jones     struct iwx_mac_ctx_cmd *cmd, uint32_t action)
67822ad0f7e9STom Jones {
67832ad0f7e9STom Jones #define IWX_EXP2(x)	((1 << (x)) - 1)	/* CWmin = 2^ECWmin - 1 */
67842ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
67852ad0f7e9STom Jones 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
67862ad0f7e9STom Jones 	struct ieee80211_node *ni = vap->iv_bss;
67872ad0f7e9STom Jones 	int cck_ack_rates, ofdm_ack_rates;
67882ad0f7e9STom Jones 
67892ad0f7e9STom Jones 	cmd->id_and_color = htole32(IWX_FW_CMD_ID_AND_COLOR(in->in_id,
67902ad0f7e9STom Jones 	    in->in_color));
67912ad0f7e9STom Jones 	cmd->action = htole32(action);
67922ad0f7e9STom Jones 
67932ad0f7e9STom Jones 	if (action == IWX_FW_CTXT_ACTION_REMOVE)
67942ad0f7e9STom Jones 		return;
67952ad0f7e9STom Jones 
67962ad0f7e9STom Jones 	if (ic->ic_opmode == IEEE80211_M_MONITOR)
67972ad0f7e9STom Jones 		cmd->mac_type = htole32(IWX_FW_MAC_TYPE_LISTENER);
67982ad0f7e9STom Jones 	else if (ic->ic_opmode == IEEE80211_M_STA)
67992ad0f7e9STom Jones 		cmd->mac_type = htole32(IWX_FW_MAC_TYPE_BSS_STA);
68002ad0f7e9STom Jones 	else
68012ad0f7e9STom Jones 		panic("unsupported operating mode %d", ic->ic_opmode);
68022ad0f7e9STom Jones 	cmd->tsf_id = htole32(IWX_TSF_ID_A);
68032ad0f7e9STom Jones 
68042ad0f7e9STom Jones 	IEEE80211_ADDR_COPY(cmd->node_addr, vap->iv_myaddr);
68052ad0f7e9STom Jones 	DPRINTF(("%s: cmd->node_addr=%s\n", __func__,
68062ad0f7e9STom Jones 	    ether_sprintf(cmd->node_addr)));
68072ad0f7e9STom Jones 	if (ic->ic_opmode == IEEE80211_M_MONITOR) {
68082ad0f7e9STom Jones 		IEEE80211_ADDR_COPY(cmd->bssid_addr, etherbroadcastaddr);
68092ad0f7e9STom Jones 		return;
68102ad0f7e9STom Jones 	}
68112ad0f7e9STom Jones 
68122ad0f7e9STom Jones 	IEEE80211_ADDR_COPY(cmd->bssid_addr, in->in_macaddr);
68132ad0f7e9STom Jones 	DPRINTF(("%s: cmd->bssid_addr=%s\n", __func__,
68142ad0f7e9STom Jones 	    ether_sprintf(cmd->bssid_addr)));
68152ad0f7e9STom Jones 	iwx_ack_rates(sc, in, &cck_ack_rates, &ofdm_ack_rates);
68162ad0f7e9STom Jones 	cmd->cck_rates = htole32(cck_ack_rates);
68172ad0f7e9STom Jones 	cmd->ofdm_rates = htole32(ofdm_ack_rates);
68182ad0f7e9STom Jones 
68192ad0f7e9STom Jones 	cmd->cck_short_preamble
68202ad0f7e9STom Jones 	    = htole32((ic->ic_flags & IEEE80211_F_SHPREAMBLE)
68212ad0f7e9STom Jones 	      ? IWX_MAC_FLG_SHORT_PREAMBLE : 0);
68222ad0f7e9STom Jones 	cmd->short_slot
68232ad0f7e9STom Jones 	    = htole32((ic->ic_flags & IEEE80211_F_SHSLOT)
68242ad0f7e9STom Jones 	      ? IWX_MAC_FLG_SHORT_SLOT : 0);
68252ad0f7e9STom Jones 
68262ad0f7e9STom Jones 	struct chanAccParams chp;
68272ad0f7e9STom Jones 	ieee80211_wme_vap_getparams(vap, &chp);
68282ad0f7e9STom Jones 
68292ad0f7e9STom Jones 	for (int i = 0; i < WME_NUM_AC; i++) {
68302ad0f7e9STom Jones 		int txf = iwx_ac_to_tx_fifo[i];
68312ad0f7e9STom Jones 		cmd->ac[txf].cw_min = IWX_EXP2(chp.cap_wmeParams[i].wmep_logcwmin);
68322ad0f7e9STom Jones 		cmd->ac[txf].cw_max = IWX_EXP2(chp.cap_wmeParams[i].wmep_logcwmax);
68332ad0f7e9STom Jones 		cmd->ac[txf].aifsn = chp.cap_wmeParams[i].wmep_aifsn;
68342ad0f7e9STom Jones 		cmd->ac[txf].fifos_mask = (1 << txf);
68352ad0f7e9STom Jones 		cmd->ac[txf].edca_txop = chp.cap_wmeParams[i].wmep_txopLimit;
68362ad0f7e9STom Jones 
68372ad0f7e9STom Jones 		cmd->ac[txf].edca_txop = htole16(chp.cap_wmeParams[i].wmep_txopLimit * 32);
68382ad0f7e9STom Jones 	}
68392ad0f7e9STom Jones 
68402ad0f7e9STom Jones 	if (ni->ni_flags & IEEE80211_NODE_QOS) {
68412ad0f7e9STom Jones 		DPRINTF(("%s: === IEEE80211_NODE_QOS\n", __func__));
68422ad0f7e9STom Jones 		cmd->qos_flags |= htole32(IWX_MAC_QOS_FLG_UPDATE_EDCA);
68432ad0f7e9STom Jones 	}
68442ad0f7e9STom Jones 
68452ad0f7e9STom Jones 	if (ni->ni_flags & IEEE80211_NODE_HT) {
68462ad0f7e9STom Jones 		switch (vap->iv_curhtprotmode) {
68472ad0f7e9STom Jones 		case IEEE80211_HTINFO_OPMODE_PURE:
68482ad0f7e9STom Jones 			break;
68492ad0f7e9STom Jones 		case IEEE80211_HTINFO_OPMODE_PROTOPT:
68502ad0f7e9STom Jones 		case IEEE80211_HTINFO_OPMODE_MIXED:
68512ad0f7e9STom Jones 			cmd->protection_flags |=
68522ad0f7e9STom Jones 			    htole32(IWX_MAC_PROT_FLG_HT_PROT |
68532ad0f7e9STom Jones 			    IWX_MAC_PROT_FLG_FAT_PROT);
68542ad0f7e9STom Jones 			break;
68552ad0f7e9STom Jones 		case IEEE80211_HTINFO_OPMODE_HT20PR:
68562ad0f7e9STom Jones 			if (in->in_phyctxt &&
68572ad0f7e9STom Jones 			    (in->in_phyctxt->sco == IEEE80211_HTINFO_2NDCHAN_ABOVE ||
68582ad0f7e9STom Jones 			    in->in_phyctxt->sco == IEEE80211_HTINFO_2NDCHAN_BELOW)) {
68592ad0f7e9STom Jones 				cmd->protection_flags |=
68602ad0f7e9STom Jones 				    htole32(IWX_MAC_PROT_FLG_HT_PROT |
68612ad0f7e9STom Jones 				    IWX_MAC_PROT_FLG_FAT_PROT);
68622ad0f7e9STom Jones 			}
68632ad0f7e9STom Jones 			break;
68642ad0f7e9STom Jones 		default:
68652ad0f7e9STom Jones 			break;
68662ad0f7e9STom Jones 		}
68672ad0f7e9STom Jones 		cmd->qos_flags |= htole32(IWX_MAC_QOS_FLG_TGN);
68682ad0f7e9STom Jones 		DPRINTF(("%s: === IWX_MAC_QOS_FLG_TGN\n", __func__));
68692ad0f7e9STom Jones 	}
68702ad0f7e9STom Jones 
68712ad0f7e9STom Jones 	if (ic->ic_flags & IEEE80211_F_USEPROT)
68722ad0f7e9STom Jones 		cmd->protection_flags |= htole32(IWX_MAC_PROT_FLG_TGG_PROTECT);
68732ad0f7e9STom Jones 	cmd->filter_flags = htole32(IWX_MAC_FILTER_ACCEPT_GRP);
68742ad0f7e9STom Jones #undef IWX_EXP2
68752ad0f7e9STom Jones }
68762ad0f7e9STom Jones 
68772ad0f7e9STom Jones static void
68782ad0f7e9STom Jones iwx_mac_ctxt_cmd_fill_sta(struct iwx_softc *sc, struct iwx_node *in,
68792ad0f7e9STom Jones     struct iwx_mac_data_sta *sta, int assoc)
68802ad0f7e9STom Jones {
68812ad0f7e9STom Jones 	struct ieee80211_node *ni = &in->in_ni;
68822ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
68832ad0f7e9STom Jones 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
68842ad0f7e9STom Jones 	uint32_t dtim_off;
68852ad0f7e9STom Jones 	uint64_t tsf;
68862ad0f7e9STom Jones 	int dtim_period;
68872ad0f7e9STom Jones 
68882ad0f7e9STom Jones 	dtim_off = ni->ni_dtim_count * ni->ni_intval * IEEE80211_DUR_TU;
68892ad0f7e9STom Jones 	tsf = le64toh(ni->ni_tstamp.tsf);
68902ad0f7e9STom Jones 	dtim_period = vap->iv_dtim_period;
68912ad0f7e9STom Jones 
68922ad0f7e9STom Jones 	sta->is_assoc = htole32(assoc);
68932ad0f7e9STom Jones 
68942ad0f7e9STom Jones 	if (assoc) {
68952ad0f7e9STom Jones 		sta->dtim_time = htole32(tsf + dtim_off);
68962ad0f7e9STom Jones 		sta->dtim_tsf = htole64(tsf + dtim_off);
68972ad0f7e9STom Jones 		// XXX: unset in iwm
68982ad0f7e9STom Jones 		sta->assoc_beacon_arrive_time = 0;
68992ad0f7e9STom Jones 	}
69002ad0f7e9STom Jones 	sta->bi = htole32(ni->ni_intval);
69012ad0f7e9STom Jones 	sta->dtim_interval = htole32(ni->ni_intval * dtim_period);
69022ad0f7e9STom Jones 	sta->data_policy = htole32(0);
69032ad0f7e9STom Jones 	sta->listen_interval = htole32(10);
69042ad0f7e9STom Jones 	sta->assoc_id = htole32(ni->ni_associd);
69052ad0f7e9STom Jones }
69062ad0f7e9STom Jones 
69072ad0f7e9STom Jones static int
69082ad0f7e9STom Jones iwx_mac_ctxt_cmd(struct iwx_softc *sc, struct iwx_node *in, uint32_t action,
69092ad0f7e9STom Jones     int assoc)
69102ad0f7e9STom Jones {
69112ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
69122ad0f7e9STom Jones 	struct ieee80211_node *ni = &in->in_ni;
69132ad0f7e9STom Jones 	struct iwx_mac_ctx_cmd cmd;
69142ad0f7e9STom Jones 	int active = (sc->sc_flags & IWX_FLAG_MAC_ACTIVE);
69152ad0f7e9STom Jones 
69162ad0f7e9STom Jones 	if (action == IWX_FW_CTXT_ACTION_ADD && active)
69172ad0f7e9STom Jones 		panic("MAC already added");
69182ad0f7e9STom Jones 	if (action == IWX_FW_CTXT_ACTION_REMOVE && !active)
69192ad0f7e9STom Jones 		panic("MAC already removed");
69202ad0f7e9STom Jones 
69212ad0f7e9STom Jones 	memset(&cmd, 0, sizeof(cmd));
69222ad0f7e9STom Jones 
69232ad0f7e9STom Jones 	iwx_mac_ctxt_cmd_common(sc, in, &cmd, action);
69242ad0f7e9STom Jones 
69252ad0f7e9STom Jones 	if (action == IWX_FW_CTXT_ACTION_REMOVE) {
69262ad0f7e9STom Jones 		return iwx_send_cmd_pdu(sc, IWX_MAC_CONTEXT_CMD, 0,
69272ad0f7e9STom Jones 		    sizeof(cmd), &cmd);
69282ad0f7e9STom Jones 	}
69292ad0f7e9STom Jones 
69302ad0f7e9STom Jones 	if (ic->ic_opmode == IEEE80211_M_MONITOR) {
69312ad0f7e9STom Jones 		cmd.filter_flags |= htole32(IWX_MAC_FILTER_IN_PROMISC |
69322ad0f7e9STom Jones 		    IWX_MAC_FILTER_IN_CONTROL_AND_MGMT |
69332ad0f7e9STom Jones 		    IWX_MAC_FILTER_ACCEPT_GRP |
69342ad0f7e9STom Jones 		    IWX_MAC_FILTER_IN_BEACON |
69352ad0f7e9STom Jones 		    IWX_MAC_FILTER_IN_PROBE_REQUEST |
69362ad0f7e9STom Jones 		    IWX_MAC_FILTER_IN_CRC32);
69372ad0f7e9STom Jones 	// XXX: dtim period is in vap
69382ad0f7e9STom Jones 	} else if (!assoc || !ni->ni_associd /*|| !ni->ni_dtimperiod*/) {
69392ad0f7e9STom Jones 		/*
69402ad0f7e9STom Jones 		 * Allow beacons to pass through as long as we are not
69412ad0f7e9STom Jones 		 * associated or we do not have dtim period information.
69422ad0f7e9STom Jones 		 */
69432ad0f7e9STom Jones 		cmd.filter_flags |= htole32(IWX_MAC_FILTER_IN_BEACON);
69442ad0f7e9STom Jones 	}
69452ad0f7e9STom Jones 	iwx_mac_ctxt_cmd_fill_sta(sc, in, &cmd.sta, assoc);
69462ad0f7e9STom Jones 	return iwx_send_cmd_pdu(sc, IWX_MAC_CONTEXT_CMD, 0, sizeof(cmd), &cmd);
69472ad0f7e9STom Jones }
69482ad0f7e9STom Jones 
69492ad0f7e9STom Jones static int
69502ad0f7e9STom Jones iwx_clear_statistics(struct iwx_softc *sc)
69512ad0f7e9STom Jones {
69522ad0f7e9STom Jones 	struct iwx_statistics_cmd scmd = {
69532ad0f7e9STom Jones 		.flags = htole32(IWX_STATISTICS_FLG_CLEAR)
69542ad0f7e9STom Jones 	};
69552ad0f7e9STom Jones 	struct iwx_host_cmd cmd = {
69562ad0f7e9STom Jones 		.id = IWX_STATISTICS_CMD,
69572ad0f7e9STom Jones 		.len[0] = sizeof(scmd),
69582ad0f7e9STom Jones 		.data[0] = &scmd,
69592ad0f7e9STom Jones 		.flags = IWX_CMD_WANT_RESP,
69602ad0f7e9STom Jones 		.resp_pkt_len = sizeof(struct iwx_notif_statistics),
69612ad0f7e9STom Jones 	};
69622ad0f7e9STom Jones 	int err;
69632ad0f7e9STom Jones 
69642ad0f7e9STom Jones 	err = iwx_send_cmd(sc, &cmd);
69652ad0f7e9STom Jones 	if (err)
69662ad0f7e9STom Jones 		return err;
69672ad0f7e9STom Jones 
69682ad0f7e9STom Jones 	iwx_free_resp(sc, &cmd);
69692ad0f7e9STom Jones 	return 0;
69702ad0f7e9STom Jones }
69712ad0f7e9STom Jones 
69722ad0f7e9STom Jones static int
69732ad0f7e9STom Jones iwx_scan(struct iwx_softc *sc)
69742ad0f7e9STom Jones {
69752ad0f7e9STom Jones 	int err;
69762ad0f7e9STom Jones 	err = iwx_umac_scan_v14(sc, 0);
69772ad0f7e9STom Jones 
69782ad0f7e9STom Jones 	if (err) {
69792ad0f7e9STom Jones 		printf("%s: could not initiate scan\n", DEVNAME(sc));
69802ad0f7e9STom Jones 		return err;
69812ad0f7e9STom Jones 	}
69822ad0f7e9STom Jones 	return 0;
69832ad0f7e9STom Jones }
69842ad0f7e9STom Jones 
69852ad0f7e9STom Jones static int
69862ad0f7e9STom Jones iwx_bgscan(struct ieee80211com *ic)
69872ad0f7e9STom Jones {
69882ad0f7e9STom Jones 	struct iwx_softc *sc = ic->ic_softc;
69892ad0f7e9STom Jones 	int err;
69902ad0f7e9STom Jones 
69912ad0f7e9STom Jones 	err = iwx_umac_scan_v14(sc, 1);
69922ad0f7e9STom Jones 	if (err) {
69932ad0f7e9STom Jones 		printf("%s: could not initiate scan\n", DEVNAME(sc));
69942ad0f7e9STom Jones 		return err;
69952ad0f7e9STom Jones 	}
69962ad0f7e9STom Jones 	return 0;
69972ad0f7e9STom Jones }
69982ad0f7e9STom Jones 
69992ad0f7e9STom Jones static int
70002ad0f7e9STom Jones iwx_enable_mgmt_queue(struct iwx_softc *sc)
70012ad0f7e9STom Jones {
70022ad0f7e9STom Jones 	int err;
70032ad0f7e9STom Jones 
70042ad0f7e9STom Jones 	sc->first_data_qid = IWX_DQA_CMD_QUEUE + 1;
70052ad0f7e9STom Jones 
70062ad0f7e9STom Jones 	/*
70072ad0f7e9STom Jones 	 * Non-QoS frames use the "MGMT" TID and queue.
70082ad0f7e9STom Jones 	 * Other TIDs and data queues are reserved for QoS data frames.
70092ad0f7e9STom Jones 	 */
70102ad0f7e9STom Jones 	err = iwx_enable_txq(sc, IWX_STATION_ID, sc->first_data_qid,
70112ad0f7e9STom Jones 	    IWX_MGMT_TID, IWX_TX_RING_COUNT);
70122ad0f7e9STom Jones 	if (err) {
70132ad0f7e9STom Jones 		printf("%s: could not enable Tx queue %d (error %d)\n",
70142ad0f7e9STom Jones 		    DEVNAME(sc), sc->first_data_qid, err);
70152ad0f7e9STom Jones 		return err;
70162ad0f7e9STom Jones 	}
70172ad0f7e9STom Jones 
70182ad0f7e9STom Jones 	return 0;
70192ad0f7e9STom Jones }
70202ad0f7e9STom Jones 
70212ad0f7e9STom Jones static int
70222ad0f7e9STom Jones iwx_disable_mgmt_queue(struct iwx_softc *sc)
70232ad0f7e9STom Jones {
70242ad0f7e9STom Jones 	int err, cmd_ver;
70252ad0f7e9STom Jones 
70262ad0f7e9STom Jones 	/* Explicit removal is only required with old SCD_QUEUE_CFG command. */
70272ad0f7e9STom Jones 	cmd_ver = iwx_lookup_cmd_ver(sc, IWX_DATA_PATH_GROUP,
70282ad0f7e9STom Jones 	    IWX_SCD_QUEUE_CONFIG_CMD);
70292ad0f7e9STom Jones 	if (cmd_ver == 0 || cmd_ver == IWX_FW_CMD_VER_UNKNOWN)
70302ad0f7e9STom Jones 		return 0;
70312ad0f7e9STom Jones 
70322ad0f7e9STom Jones 	sc->first_data_qid = IWX_DQA_CMD_QUEUE + 1;
70332ad0f7e9STom Jones 
70342ad0f7e9STom Jones 	err = iwx_disable_txq(sc, IWX_STATION_ID, sc->first_data_qid,
70352ad0f7e9STom Jones 	    IWX_MGMT_TID);
70362ad0f7e9STom Jones 	if (err) {
70372ad0f7e9STom Jones 		printf("%s: could not disable Tx queue %d (error %d)\n",
70382ad0f7e9STom Jones 		    DEVNAME(sc), sc->first_data_qid, err);
70392ad0f7e9STom Jones 		return err;
70402ad0f7e9STom Jones 	}
70412ad0f7e9STom Jones 
70422ad0f7e9STom Jones 	return 0;
70432ad0f7e9STom Jones }
70442ad0f7e9STom Jones 
70452ad0f7e9STom Jones static int
70462ad0f7e9STom Jones iwx_rs_rval2idx(uint8_t rval)
70472ad0f7e9STom Jones {
70482ad0f7e9STom Jones 	/* Firmware expects indices which match our 11g rate set. */
70492ad0f7e9STom Jones 	const struct ieee80211_rateset *rs = &ieee80211_std_rateset_11g;
70502ad0f7e9STom Jones 	int i;
70512ad0f7e9STom Jones 
70522ad0f7e9STom Jones 	for (i = 0; i < rs->rs_nrates; i++) {
70532ad0f7e9STom Jones 		if ((rs->rs_rates[i] & IEEE80211_RATE_VAL) == rval)
70542ad0f7e9STom Jones 			return i;
70552ad0f7e9STom Jones 	}
70562ad0f7e9STom Jones 
70572ad0f7e9STom Jones 	return -1;
70582ad0f7e9STom Jones }
70592ad0f7e9STom Jones 
70602ad0f7e9STom Jones static uint16_t
70612ad0f7e9STom Jones iwx_rs_ht_rates(struct iwx_softc *sc, struct ieee80211_node *ni, int rsidx)
70622ad0f7e9STom Jones {
70632ad0f7e9STom Jones 	uint16_t htrates = 0;
70642ad0f7e9STom Jones 	struct ieee80211_htrateset *htrs = &ni->ni_htrates;
70652ad0f7e9STom Jones 	int i;
70662ad0f7e9STom Jones 
70672ad0f7e9STom Jones 	if (rsidx == IEEE80211_HT_RATESET_SISO) {
70682ad0f7e9STom Jones 		for (i = 0; i < htrs->rs_nrates; i++) {
70692ad0f7e9STom Jones 			if (htrs->rs_rates[i] <= 7)
70702ad0f7e9STom Jones 				htrates |= (1 << htrs->rs_rates[i]);
70712ad0f7e9STom Jones 		}
70722ad0f7e9STom Jones 	} else if (rsidx == IEEE80211_HT_RATESET_MIMO2) {
70732ad0f7e9STom Jones 		for (i = 0; i < htrs->rs_nrates; i++) {
70742ad0f7e9STom Jones 			if (htrs->rs_rates[i] > 7 && htrs->rs_rates[i] <= 15)
70752ad0f7e9STom Jones 				htrates |= (1 << (htrs->rs_rates[i] - 8));
70762ad0f7e9STom Jones 		}
70772ad0f7e9STom Jones 	} else
70782ad0f7e9STom Jones 		panic(("iwx_rs_ht_rates"));
70792ad0f7e9STom Jones 
70802ad0f7e9STom Jones 	IWX_DPRINTF(sc, IWX_DEBUG_TXRATE,
70812ad0f7e9STom Jones 	    "%s:%d rsidx=%i htrates=0x%x\n", __func__, __LINE__, rsidx, htrates);
70822ad0f7e9STom Jones 
70832ad0f7e9STom Jones 	return htrates;
70842ad0f7e9STom Jones }
70852ad0f7e9STom Jones 
70862ad0f7e9STom Jones uint16_t
70872ad0f7e9STom Jones iwx_rs_vht_rates(struct iwx_softc *sc, struct ieee80211_node *ni, int num_ss)
70882ad0f7e9STom Jones {
70892ad0f7e9STom Jones 	uint16_t rx_mcs;
70902ad0f7e9STom Jones 	int max_mcs = -1;
70912ad0f7e9STom Jones #define IEEE80211_VHT_MCS_FOR_SS_MASK(n)        (0x3 << (2*((n)-1)))
70922ad0f7e9STom Jones #define IEEE80211_VHT_MCS_FOR_SS_SHIFT(n)       (2*((n)-1))
70932ad0f7e9STom Jones 	rx_mcs = (ni->ni_vht_mcsinfo.tx_mcs_map &
70942ad0f7e9STom Jones 	    IEEE80211_VHT_MCS_FOR_SS_MASK(num_ss)) >>
70952ad0f7e9STom Jones 	    IEEE80211_VHT_MCS_FOR_SS_SHIFT(num_ss);
70962ad0f7e9STom Jones 
70972ad0f7e9STom Jones 	switch (rx_mcs) {
70982ad0f7e9STom Jones 	case IEEE80211_VHT_MCS_NOT_SUPPORTED:
70992ad0f7e9STom Jones 		break;
71002ad0f7e9STom Jones 	case IEEE80211_VHT_MCS_SUPPORT_0_7:
71012ad0f7e9STom Jones 		max_mcs = 7;
71022ad0f7e9STom Jones 		break;
71032ad0f7e9STom Jones 	case IEEE80211_VHT_MCS_SUPPORT_0_8:
71042ad0f7e9STom Jones 		max_mcs = 8;
71052ad0f7e9STom Jones 		break;
71062ad0f7e9STom Jones 	case IEEE80211_VHT_MCS_SUPPORT_0_9:
71072ad0f7e9STom Jones 		/* Disable VHT MCS 9 for 20MHz-only stations. */
71082ad0f7e9STom Jones 		if ((ni->ni_htcap & IEEE80211_HTCAP_CHWIDTH40) == 0)
71092ad0f7e9STom Jones 			max_mcs = 8;
71102ad0f7e9STom Jones 		else
71112ad0f7e9STom Jones 			max_mcs = 9;
71122ad0f7e9STom Jones 		break;
71132ad0f7e9STom Jones 	default:
71142ad0f7e9STom Jones 		/* Should not happen; Values above cover the possible range. */
71152ad0f7e9STom Jones 		panic("invalid VHT Rx MCS value %u", rx_mcs);
71162ad0f7e9STom Jones 	}
71172ad0f7e9STom Jones 
71182ad0f7e9STom Jones 	return ((1 << (max_mcs + 1)) - 1);
71192ad0f7e9STom Jones }
71202ad0f7e9STom Jones 
71212ad0f7e9STom Jones static int
71222ad0f7e9STom Jones iwx_rs_init_v3(struct iwx_softc *sc, struct iwx_node *in)
71232ad0f7e9STom Jones {
71242ad0f7e9STom Jones #if 1
71252ad0f7e9STom Jones 	panic("iwx: Trying to init rate set on untested version");
71262ad0f7e9STom Jones #else
71272ad0f7e9STom Jones 	struct ieee80211_node *ni = &in->in_ni;
71282ad0f7e9STom Jones 	struct ieee80211_rateset *rs = &ni->ni_rates;
71292ad0f7e9STom Jones 	struct iwx_tlc_config_cmd_v3 cfg_cmd;
71302ad0f7e9STom Jones 	uint32_t cmd_id;
71312ad0f7e9STom Jones 	int i;
71322ad0f7e9STom Jones 	size_t cmd_size = sizeof(cfg_cmd);
71332ad0f7e9STom Jones 
71342ad0f7e9STom Jones 	memset(&cfg_cmd, 0, sizeof(cfg_cmd));
71352ad0f7e9STom Jones 
71362ad0f7e9STom Jones 	for (i = 0; i < rs->rs_nrates; i++) {
71372ad0f7e9STom Jones 		uint8_t rval = rs->rs_rates[i] & IEEE80211_RATE_VAL;
71382ad0f7e9STom Jones 		int idx = iwx_rs_rval2idx(rval);
71392ad0f7e9STom Jones 		if (idx == -1)
71402ad0f7e9STom Jones 			return EINVAL;
71412ad0f7e9STom Jones 		cfg_cmd.non_ht_rates |= (1 << idx);
71422ad0f7e9STom Jones 	}
71432ad0f7e9STom Jones 
71442ad0f7e9STom Jones 	if (ni->ni_flags & IEEE80211_NODE_VHT) {
71452ad0f7e9STom Jones 		cfg_cmd.mode = IWX_TLC_MNG_MODE_VHT;
71462ad0f7e9STom Jones 		cfg_cmd.ht_rates[IWX_TLC_NSS_1][IWX_TLC_MCS_PER_BW_80] =
71472ad0f7e9STom Jones 		    htole16(iwx_rs_vht_rates(sc, ni, 1));
71482ad0f7e9STom Jones 		cfg_cmd.ht_rates[IWX_TLC_NSS_2][IWX_TLC_MCS_PER_BW_80] =
71492ad0f7e9STom Jones 		    htole16(iwx_rs_vht_rates(sc, ni, 2));
71502ad0f7e9STom Jones 	} else if (ni->ni_flags & IEEE80211_NODE_HT) {
71512ad0f7e9STom Jones 		cfg_cmd.mode = IWX_TLC_MNG_MODE_HT;
71522ad0f7e9STom Jones 		cfg_cmd.ht_rates[IWX_TLC_NSS_1][IWX_TLC_MCS_PER_BW_80] =
71532ad0f7e9STom Jones 		    htole16(iwx_rs_ht_rates(sc, ni,
71542ad0f7e9STom Jones 		    IEEE80211_HT_RATESET_SISO));
71552ad0f7e9STom Jones 		cfg_cmd.ht_rates[IWX_TLC_NSS_2][IWX_TLC_MCS_PER_BW_80] =
71562ad0f7e9STom Jones 		    htole16(iwx_rs_ht_rates(sc, ni,
71572ad0f7e9STom Jones 		    IEEE80211_HT_RATESET_MIMO2));
71582ad0f7e9STom Jones 	} else
71592ad0f7e9STom Jones 		cfg_cmd.mode = IWX_TLC_MNG_MODE_NON_HT;
71602ad0f7e9STom Jones 
71612ad0f7e9STom Jones 	cfg_cmd.sta_id = IWX_STATION_ID;
71622ad0f7e9STom Jones 	if (in->in_phyctxt->vht_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_80)
71632ad0f7e9STom Jones 		cfg_cmd.max_ch_width = IWX_TLC_MNG_CH_WIDTH_80MHZ;
71642ad0f7e9STom Jones 	else if (in->in_phyctxt->sco == IEEE80211_HTOP0_SCO_SCA ||
71652ad0f7e9STom Jones 	    in->in_phyctxt->sco == IEEE80211_HTOP0_SCO_SCB)
71662ad0f7e9STom Jones 		cfg_cmd.max_ch_width = IWX_TLC_MNG_CH_WIDTH_40MHZ;
71672ad0f7e9STom Jones 	else
71682ad0f7e9STom Jones 		cfg_cmd.max_ch_width = IWX_TLC_MNG_CH_WIDTH_20MHZ;
71692ad0f7e9STom Jones 	cfg_cmd.chains = IWX_TLC_MNG_CHAIN_A_MSK | IWX_TLC_MNG_CHAIN_B_MSK;
71702ad0f7e9STom Jones 	if (ni->ni_flags & IEEE80211_NODE_VHT)
71712ad0f7e9STom Jones 		cfg_cmd.max_mpdu_len = htole16(3895);
71722ad0f7e9STom Jones 	else
71732ad0f7e9STom Jones 		cfg_cmd.max_mpdu_len = htole16(3839);
71742ad0f7e9STom Jones 	if (ni->ni_flags & IEEE80211_NODE_HT) {
71752ad0f7e9STom Jones 		if (ieee80211_node_supports_ht_sgi20(ni)) {
71762ad0f7e9STom Jones 			cfg_cmd.sgi_ch_width_supp |= (1 <<
71772ad0f7e9STom Jones 			    IWX_TLC_MNG_CH_WIDTH_20MHZ);
71782ad0f7e9STom Jones 		}
71792ad0f7e9STom Jones 		if (ieee80211_node_supports_ht_sgi40(ni)) {
71802ad0f7e9STom Jones 			cfg_cmd.sgi_ch_width_supp |= (1 <<
71812ad0f7e9STom Jones 			    IWX_TLC_MNG_CH_WIDTH_40MHZ);
71822ad0f7e9STom Jones 		}
71832ad0f7e9STom Jones 	}
71842ad0f7e9STom Jones 	if ((ni->ni_flags & IEEE80211_NODE_VHT) &&
71852ad0f7e9STom Jones 	    ieee80211_node_supports_vht_sgi80(ni))
71862ad0f7e9STom Jones 		cfg_cmd.sgi_ch_width_supp |= (1 << IWX_TLC_MNG_CH_WIDTH_80MHZ);
71872ad0f7e9STom Jones 
71882ad0f7e9STom Jones 	cmd_id = iwx_cmd_id(IWX_TLC_MNG_CONFIG_CMD, IWX_DATA_PATH_GROUP, 0);
71892ad0f7e9STom Jones 	return iwx_send_cmd_pdu(sc, cmd_id, IWX_CMD_ASYNC, cmd_size, &cfg_cmd);
71902ad0f7e9STom Jones #endif
71912ad0f7e9STom Jones }
71922ad0f7e9STom Jones 
71932ad0f7e9STom Jones static int
71942ad0f7e9STom Jones iwx_rs_init_v4(struct iwx_softc *sc, struct iwx_node *in)
71952ad0f7e9STom Jones {
71962ad0f7e9STom Jones 	struct ieee80211_node *ni = &in->in_ni;
71972ad0f7e9STom Jones 	struct ieee80211_rateset *rs = &ni->ni_rates;
71982ad0f7e9STom Jones 	struct ieee80211_htrateset *htrs = &ni->ni_htrates;
71992ad0f7e9STom Jones 	struct iwx_tlc_config_cmd_v4 cfg_cmd;
72002ad0f7e9STom Jones 	uint32_t cmd_id;
72012ad0f7e9STom Jones 	int i;
72022ad0f7e9STom Jones 	int sgi80 = 0;
72032ad0f7e9STom Jones 	size_t cmd_size = sizeof(cfg_cmd);
72042ad0f7e9STom Jones 
72052ad0f7e9STom Jones 	memset(&cfg_cmd, 0, sizeof(cfg_cmd));
72062ad0f7e9STom Jones 
72072ad0f7e9STom Jones 	for (i = 0; i < rs->rs_nrates; i++) {
72082ad0f7e9STom Jones 		uint8_t rval = rs->rs_rates[i] & IEEE80211_RATE_VAL;
72092ad0f7e9STom Jones 		int idx = iwx_rs_rval2idx(rval);
72102ad0f7e9STom Jones 		if (idx == -1)
72112ad0f7e9STom Jones 			return EINVAL;
72122ad0f7e9STom Jones 		cfg_cmd.non_ht_rates |= (1 << idx);
72132ad0f7e9STom Jones 	}
72142ad0f7e9STom Jones 	for (i = 0; i < htrs->rs_nrates; i++) {
72152ad0f7e9STom Jones 		DPRINTF(("%s: htrate=%i\n", __func__, htrs->rs_rates[i]));
72162ad0f7e9STom Jones 	}
72172ad0f7e9STom Jones 
72182ad0f7e9STom Jones 	if (ni->ni_flags & IEEE80211_NODE_VHT) {
72192ad0f7e9STom Jones 		cfg_cmd.mode = IWX_TLC_MNG_MODE_VHT;
72202ad0f7e9STom Jones 		cfg_cmd.ht_rates[IWX_TLC_NSS_1][IWX_TLC_MCS_PER_BW_80] =
72212ad0f7e9STom Jones 		    htole16(iwx_rs_vht_rates(sc, ni, 1));
72222ad0f7e9STom Jones 		cfg_cmd.ht_rates[IWX_TLC_NSS_2][IWX_TLC_MCS_PER_BW_80] =
72232ad0f7e9STom Jones 		    htole16(iwx_rs_vht_rates(sc, ni, 2));
72242ad0f7e9STom Jones 
72252ad0f7e9STom Jones 		IWX_DPRINTF(sc, IWX_DEBUG_TXRATE, "%s:%d SISO=0x%x\n",
72262ad0f7e9STom Jones 		    __func__, __LINE__,
72272ad0f7e9STom Jones 		    cfg_cmd.ht_rates[IWX_TLC_NSS_1][IWX_TLC_MCS_PER_BW_80]);
72282ad0f7e9STom Jones 		IWX_DPRINTF(sc, IWX_DEBUG_TXRATE, "%s:%d MIMO2=0x%x\n",
72292ad0f7e9STom Jones 		    __func__, __LINE__,
72302ad0f7e9STom Jones 		    cfg_cmd.ht_rates[IWX_TLC_NSS_2][IWX_TLC_MCS_PER_BW_80]);
72312ad0f7e9STom Jones 	} else if (ni->ni_flags & IEEE80211_NODE_HT) {
72322ad0f7e9STom Jones 		cfg_cmd.mode = IWX_TLC_MNG_MODE_HT;
72332ad0f7e9STom Jones 		cfg_cmd.ht_rates[IWX_TLC_NSS_1][IWX_TLC_MCS_PER_BW_80] =
72342ad0f7e9STom Jones 		    htole16(iwx_rs_ht_rates(sc, ni,
72352ad0f7e9STom Jones 		    IEEE80211_HT_RATESET_SISO));
72362ad0f7e9STom Jones 		cfg_cmd.ht_rates[IWX_TLC_NSS_2][IWX_TLC_MCS_PER_BW_80] =
72372ad0f7e9STom Jones 		    htole16(iwx_rs_ht_rates(sc, ni,
72382ad0f7e9STom Jones 		    IEEE80211_HT_RATESET_MIMO2));
72392ad0f7e9STom Jones 
72402ad0f7e9STom Jones 		IWX_DPRINTF(sc, IWX_DEBUG_TXRATE, "%s:%d SISO=0x%x\n",
72412ad0f7e9STom Jones 		    __func__, __LINE__,
72422ad0f7e9STom Jones 		    cfg_cmd.ht_rates[IWX_TLC_NSS_1][IWX_TLC_MCS_PER_BW_80]);
72432ad0f7e9STom Jones 		IWX_DPRINTF(sc, IWX_DEBUG_TXRATE, "%s:%d MIMO2=0x%x\n",
72442ad0f7e9STom Jones 		    __func__, __LINE__,
72452ad0f7e9STom Jones 		    cfg_cmd.ht_rates[IWX_TLC_NSS_2][IWX_TLC_MCS_PER_BW_80]);
72462ad0f7e9STom Jones 	} else
72472ad0f7e9STom Jones 		cfg_cmd.mode = IWX_TLC_MNG_MODE_NON_HT;
72482ad0f7e9STom Jones 
72492ad0f7e9STom Jones 	cfg_cmd.sta_id = IWX_STATION_ID;
72502ad0f7e9STom Jones #if 0
72512ad0f7e9STom Jones 	if (in->in_phyctxt->vht_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_80)
72522ad0f7e9STom Jones 		cfg_cmd.max_ch_width = IWX_TLC_MNG_CH_WIDTH_80MHZ;
72532ad0f7e9STom Jones 	else if (in->in_phyctxt->sco == IEEE80211_HTOP0_SCO_SCA ||
72542ad0f7e9STom Jones 	    in->in_phyctxt->sco == IEEE80211_HTOP0_SCO_SCB)
72552ad0f7e9STom Jones 		cfg_cmd.max_ch_width = IWX_TLC_MNG_CH_WIDTH_40MHZ;
72562ad0f7e9STom Jones 	else
72572ad0f7e9STom Jones 		cfg_cmd.max_ch_width = IWX_TLC_MNG_CH_WIDTH_20MHZ;
72582ad0f7e9STom Jones #endif
72592ad0f7e9STom Jones 	if (IEEE80211_IS_CHAN_VHT80(in->in_ni.ni_chan)) {
72602ad0f7e9STom Jones 		cfg_cmd.max_ch_width = IWX_TLC_MNG_CH_WIDTH_80MHZ;
72612ad0f7e9STom Jones 	} else if (IEEE80211_IS_CHAN_HT40(in->in_ni.ni_chan)) {
72622ad0f7e9STom Jones 		cfg_cmd.max_ch_width = IWX_TLC_MNG_CH_WIDTH_40MHZ;
72632ad0f7e9STom Jones 	} else {
72642ad0f7e9STom Jones 		cfg_cmd.max_ch_width = IWX_TLC_MNG_CH_WIDTH_20MHZ;
72652ad0f7e9STom Jones 	}
72662ad0f7e9STom Jones 
72672ad0f7e9STom Jones 	cfg_cmd.chains = IWX_TLC_MNG_CHAIN_A_MSK | IWX_TLC_MNG_CHAIN_B_MSK;
72682ad0f7e9STom Jones 	if (ni->ni_flags & IEEE80211_NODE_VHT)
72692ad0f7e9STom Jones 		cfg_cmd.max_mpdu_len = htole16(3895);
72702ad0f7e9STom Jones 	else
72712ad0f7e9STom Jones 		cfg_cmd.max_mpdu_len = htole16(3839);
72722ad0f7e9STom Jones 	if (ni->ni_flags & IEEE80211_NODE_HT) {
72732ad0f7e9STom Jones 		if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20) {
72742ad0f7e9STom Jones 			cfg_cmd.sgi_ch_width_supp |= (1 <<
72752ad0f7e9STom Jones 			    IWX_TLC_MNG_CH_WIDTH_20MHZ);
72762ad0f7e9STom Jones 		}
72772ad0f7e9STom Jones 		if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40) {
72782ad0f7e9STom Jones 			cfg_cmd.sgi_ch_width_supp |= (1 <<
72792ad0f7e9STom Jones 			    IWX_TLC_MNG_CH_WIDTH_40MHZ);
72802ad0f7e9STom Jones 		}
72812ad0f7e9STom Jones 	}
72822ad0f7e9STom Jones 	sgi80 = _IEEE80211_MASKSHIFT(ni->ni_vhtcap,
72832ad0f7e9STom Jones 	    IEEE80211_VHTCAP_SHORT_GI_80);
72842ad0f7e9STom Jones 	if ((ni->ni_flags & IEEE80211_NODE_VHT) && sgi80) {
72852ad0f7e9STom Jones 		cfg_cmd.sgi_ch_width_supp |= (1 << IWX_TLC_MNG_CH_WIDTH_80MHZ);
72862ad0f7e9STom Jones 	}
72872ad0f7e9STom Jones 
72882ad0f7e9STom Jones 	cmd_id = iwx_cmd_id(IWX_TLC_MNG_CONFIG_CMD, IWX_DATA_PATH_GROUP, 0);
72892ad0f7e9STom Jones 	return iwx_send_cmd_pdu(sc, cmd_id, IWX_CMD_ASYNC, cmd_size, &cfg_cmd);
72902ad0f7e9STom Jones }
72912ad0f7e9STom Jones 
72922ad0f7e9STom Jones static int
72932ad0f7e9STom Jones iwx_rs_init(struct iwx_softc *sc, struct iwx_node *in)
72942ad0f7e9STom Jones {
72952ad0f7e9STom Jones 	int cmd_ver;
72962ad0f7e9STom Jones 
72972ad0f7e9STom Jones 	cmd_ver = iwx_lookup_cmd_ver(sc, IWX_DATA_PATH_GROUP,
72982ad0f7e9STom Jones 	    IWX_TLC_MNG_CONFIG_CMD);
72992ad0f7e9STom Jones 	if (cmd_ver == 4)
73002ad0f7e9STom Jones 		return iwx_rs_init_v4(sc, in);
73012ad0f7e9STom Jones 	else
73022ad0f7e9STom Jones 		return iwx_rs_init_v3(sc, in);
73032ad0f7e9STom Jones }
73042ad0f7e9STom Jones 
73052ad0f7e9STom Jones static void
73062ad0f7e9STom Jones iwx_rs_update(struct iwx_softc *sc, struct iwx_tlc_update_notif *notif)
73072ad0f7e9STom Jones {
73082ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
73092ad0f7e9STom Jones 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
73102ad0f7e9STom Jones 	struct ieee80211_node *ni = (void *)vap->iv_bss;
73112ad0f7e9STom Jones 
73122ad0f7e9STom Jones 	struct ieee80211_rateset *rs = &ni->ni_rates;
73132ad0f7e9STom Jones 	uint32_t rate_n_flags;
73142ad0f7e9STom Jones 	uint8_t plcp, rval;
73152ad0f7e9STom Jones 	int i, cmd_ver, rate_n_flags_ver2 = 0;
73162ad0f7e9STom Jones 
73172ad0f7e9STom Jones 	if (notif->sta_id != IWX_STATION_ID ||
73182ad0f7e9STom Jones 	    (le32toh(notif->flags) & IWX_TLC_NOTIF_FLAG_RATE) == 0)
73192ad0f7e9STom Jones 		return;
73202ad0f7e9STom Jones 
73212ad0f7e9STom Jones 	rate_n_flags = le32toh(notif->rate);
73222ad0f7e9STom Jones 
73232ad0f7e9STom Jones 	if (sc->sc_debug & IWX_DEBUG_TXRATE)
73242ad0f7e9STom Jones 		print_ratenflags(__func__, __LINE__,
73252ad0f7e9STom Jones 		    rate_n_flags, sc->sc_rate_n_flags_version);
73262ad0f7e9STom Jones 
73272ad0f7e9STom Jones 	cmd_ver = iwx_lookup_notif_ver(sc, IWX_DATA_PATH_GROUP,
73282ad0f7e9STom Jones 	    IWX_TLC_MNG_UPDATE_NOTIF);
73292ad0f7e9STom Jones 	if (cmd_ver != IWX_FW_CMD_VER_UNKNOWN && cmd_ver >= 3)
73302ad0f7e9STom Jones 		rate_n_flags_ver2 = 1;
73312ad0f7e9STom Jones 
73322ad0f7e9STom Jones 	if (rate_n_flags_ver2) {
73332ad0f7e9STom Jones 		uint32_t mod_type = (rate_n_flags & IWX_RATE_MCS_MOD_TYPE_MSK);
73342ad0f7e9STom Jones 		if (mod_type == IWX_RATE_MCS_HT_MSK) {
73352ad0f7e9STom Jones 
73362ad0f7e9STom Jones 			ieee80211_node_set_txrate_dot11rate(ni,
73372ad0f7e9STom Jones 				IWX_RATE_HT_MCS_INDEX(rate_n_flags) |
73382ad0f7e9STom Jones 				IEEE80211_RATE_MCS);
73392ad0f7e9STom Jones 			IWX_DPRINTF(sc, IWX_DEBUG_TXRATE,
73402ad0f7e9STom Jones 			    "%s:%d new MCS: %d rate_n_flags: %x\n",
73412ad0f7e9STom Jones 			    __func__, __LINE__,
73422ad0f7e9STom Jones 			    ieee80211_node_get_txrate_dot11rate(ni) & ~IEEE80211_RATE_MCS,
73432ad0f7e9STom Jones 			    rate_n_flags);
73442ad0f7e9STom Jones 			return;
73452ad0f7e9STom Jones 		}
73462ad0f7e9STom Jones 	} else {
73472ad0f7e9STom Jones 		if (rate_n_flags & IWX_RATE_MCS_HT_MSK_V1) {
73482ad0f7e9STom Jones 			ieee80211_node_set_txrate_dot11rate(ni,
73492ad0f7e9STom Jones 			    rate_n_flags & (IWX_RATE_HT_MCS_RATE_CODE_MSK_V1 |
73502ad0f7e9STom Jones 			    IWX_RATE_HT_MCS_NSS_MSK_V1));
73512ad0f7e9STom Jones 
73522ad0f7e9STom Jones 			IWX_DPRINTF(sc, IWX_DEBUG_TXRATE,
73532ad0f7e9STom Jones 			    "%s:%d new MCS idx: %d rate_n_flags: %x\n",
73542ad0f7e9STom Jones 			    __func__, __LINE__,
73552ad0f7e9STom Jones 			    ieee80211_node_get_txrate_dot11rate(ni), rate_n_flags);
73562ad0f7e9STom Jones 			return;
73572ad0f7e9STom Jones 		}
73582ad0f7e9STom Jones 	}
73592ad0f7e9STom Jones 
73602ad0f7e9STom Jones 	if (rate_n_flags_ver2) {
73612ad0f7e9STom Jones 		const struct ieee80211_rateset *rs;
73622ad0f7e9STom Jones 		uint32_t ridx = (rate_n_flags & IWX_RATE_LEGACY_RATE_MSK);
73632ad0f7e9STom Jones 		if (rate_n_flags & IWX_RATE_MCS_LEGACY_OFDM_MSK)
73642ad0f7e9STom Jones 			rs = &ieee80211_std_rateset_11a;
73652ad0f7e9STom Jones 		else
73662ad0f7e9STom Jones 			rs = &ieee80211_std_rateset_11b;
73672ad0f7e9STom Jones 		if (ridx < rs->rs_nrates)
73682ad0f7e9STom Jones 			rval = (rs->rs_rates[ridx] & IEEE80211_RATE_VAL);
73692ad0f7e9STom Jones 		else
73702ad0f7e9STom Jones 			rval = 0;
73712ad0f7e9STom Jones 	} else {
73722ad0f7e9STom Jones 		plcp = (rate_n_flags & IWX_RATE_LEGACY_RATE_MSK_V1);
73732ad0f7e9STom Jones 
73742ad0f7e9STom Jones 		rval = 0;
73752ad0f7e9STom Jones 		for (i = IWX_RATE_1M_INDEX; i < nitems(iwx_rates); i++) {
73762ad0f7e9STom Jones 			if (iwx_rates[i].plcp == plcp) {
73772ad0f7e9STom Jones 				rval = iwx_rates[i].rate;
73782ad0f7e9STom Jones 				break;
73792ad0f7e9STom Jones 			}
73802ad0f7e9STom Jones 		}
73812ad0f7e9STom Jones 	}
73822ad0f7e9STom Jones 
73832ad0f7e9STom Jones 	if (rval) {
73842ad0f7e9STom Jones 		uint8_t rv;
73852ad0f7e9STom Jones 		for (i = 0; i < rs->rs_nrates; i++) {
73862ad0f7e9STom Jones 			rv = rs->rs_rates[i] & IEEE80211_RATE_VAL;
73872ad0f7e9STom Jones 			if (rv == rval) {
73882ad0f7e9STom Jones 				ieee80211_node_set_txrate_dot11rate(ni, i);
73892ad0f7e9STom Jones 				break;
73902ad0f7e9STom Jones 			}
73912ad0f7e9STom Jones 		}
73922ad0f7e9STom Jones 		IWX_DPRINTF(sc, IWX_DEBUG_TXRATE,
73932ad0f7e9STom Jones 		    "%s:%d new rate %d\n", __func__, __LINE__,
73942ad0f7e9STom Jones 		    ieee80211_node_get_txrate_dot11rate(ni));
73952ad0f7e9STom Jones 	}
73962ad0f7e9STom Jones }
73972ad0f7e9STom Jones 
73982ad0f7e9STom Jones static int
73992ad0f7e9STom Jones iwx_phy_send_rlc(struct iwx_softc *sc, struct iwx_phy_ctxt *phyctxt,
74002ad0f7e9STom Jones     uint8_t chains_static, uint8_t chains_dynamic)
74012ad0f7e9STom Jones {
74022ad0f7e9STom Jones 	struct iwx_rlc_config_cmd cmd;
74032ad0f7e9STom Jones 	uint32_t cmd_id;
74042ad0f7e9STom Jones 	uint8_t active_cnt, idle_cnt;
74052ad0f7e9STom Jones 
74062ad0f7e9STom Jones 	memset(&cmd, 0, sizeof(cmd));
74072ad0f7e9STom Jones 
74082ad0f7e9STom Jones 	idle_cnt = chains_static;
74092ad0f7e9STom Jones 	active_cnt = chains_dynamic;
74102ad0f7e9STom Jones 
74112ad0f7e9STom Jones 	cmd.phy_id = htole32(phyctxt->id);
74122ad0f7e9STom Jones 	cmd.rlc.rx_chain_info = htole32(iwx_fw_valid_rx_ant(sc) <<
74132ad0f7e9STom Jones 	    IWX_PHY_RX_CHAIN_VALID_POS);
74142ad0f7e9STom Jones 	cmd.rlc.rx_chain_info |= htole32(idle_cnt << IWX_PHY_RX_CHAIN_CNT_POS);
74152ad0f7e9STom Jones 	cmd.rlc.rx_chain_info |= htole32(active_cnt <<
74162ad0f7e9STom Jones 	    IWX_PHY_RX_CHAIN_MIMO_CNT_POS);
74172ad0f7e9STom Jones 
74182ad0f7e9STom Jones 	cmd_id = iwx_cmd_id(IWX_RLC_CONFIG_CMD, IWX_DATA_PATH_GROUP, 2);
74192ad0f7e9STom Jones 	return iwx_send_cmd_pdu(sc, cmd_id, 0, sizeof(cmd), &cmd);
74202ad0f7e9STom Jones }
74212ad0f7e9STom Jones 
74222ad0f7e9STom Jones static int
74232ad0f7e9STom Jones iwx_phy_ctxt_update(struct iwx_softc *sc, struct iwx_phy_ctxt *phyctxt,
74242ad0f7e9STom Jones     struct ieee80211_channel *chan, uint8_t chains_static,
74252ad0f7e9STom Jones     uint8_t chains_dynamic, uint32_t apply_time, uint8_t sco,
74262ad0f7e9STom Jones     uint8_t vht_chan_width)
74272ad0f7e9STom Jones {
74282ad0f7e9STom Jones 	uint16_t band_flags = (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ);
74292ad0f7e9STom Jones 	int err;
74302ad0f7e9STom Jones 
74312ad0f7e9STom Jones 	if (chan == IEEE80211_CHAN_ANYC) {
74322ad0f7e9STom Jones 		printf("%s: GOS-3833: IEEE80211_CHAN_ANYC triggered\n",
74332ad0f7e9STom Jones 		    DEVNAME(sc));
74342ad0f7e9STom Jones 		    return EIO;
74352ad0f7e9STom Jones 	}
74362ad0f7e9STom Jones 
74372ad0f7e9STom Jones 	if (isset(sc->sc_enabled_capa,
74382ad0f7e9STom Jones 	    IWX_UCODE_TLV_CAPA_BINDING_CDB_SUPPORT) &&
74392ad0f7e9STom Jones 	    (phyctxt->channel->ic_flags & band_flags) !=
74402ad0f7e9STom Jones 	    (chan->ic_flags & band_flags)) {
74412ad0f7e9STom Jones 		err = iwx_phy_ctxt_cmd(sc, phyctxt, chains_static,
74422ad0f7e9STom Jones 		    chains_dynamic, IWX_FW_CTXT_ACTION_REMOVE, apply_time, sco,
74432ad0f7e9STom Jones 		    vht_chan_width);
74442ad0f7e9STom Jones 		if (err) {
74452ad0f7e9STom Jones 			printf("%s: could not remove PHY context "
74462ad0f7e9STom Jones 			    "(error %d)\n", DEVNAME(sc), err);
74472ad0f7e9STom Jones 			return err;
74482ad0f7e9STom Jones 		}
74492ad0f7e9STom Jones 		phyctxt->channel = chan;
74502ad0f7e9STom Jones 		err = iwx_phy_ctxt_cmd(sc, phyctxt, chains_static,
74512ad0f7e9STom Jones 		    chains_dynamic, IWX_FW_CTXT_ACTION_ADD, apply_time, sco,
74522ad0f7e9STom Jones 		    vht_chan_width);
74532ad0f7e9STom Jones 		if (err) {
74542ad0f7e9STom Jones 			printf("%s: could not add PHY context "
74552ad0f7e9STom Jones 			    "(error %d)\n", DEVNAME(sc), err);
74562ad0f7e9STom Jones 			return err;
74572ad0f7e9STom Jones 		}
74582ad0f7e9STom Jones 	} else {
74592ad0f7e9STom Jones 		phyctxt->channel = chan;
74602ad0f7e9STom Jones 		err = iwx_phy_ctxt_cmd(sc, phyctxt, chains_static,
74612ad0f7e9STom Jones 		    chains_dynamic, IWX_FW_CTXT_ACTION_MODIFY, apply_time, sco,
74622ad0f7e9STom Jones 		    vht_chan_width);
74632ad0f7e9STom Jones 		if (err) {
74642ad0f7e9STom Jones 			printf("%s: could not update PHY context (error %d)\n",
74652ad0f7e9STom Jones 			    DEVNAME(sc), err);
74662ad0f7e9STom Jones 			return err;
74672ad0f7e9STom Jones 		}
74682ad0f7e9STom Jones 	}
74692ad0f7e9STom Jones 
74702ad0f7e9STom Jones 	phyctxt->sco = sco;
74712ad0f7e9STom Jones 	phyctxt->vht_chan_width = vht_chan_width;
74722ad0f7e9STom Jones 
74732ad0f7e9STom Jones 	DPRINTF(("%s: phyctxt->channel->ic_ieee=%d\n", __func__,
74742ad0f7e9STom Jones 	    phyctxt->channel->ic_ieee));
74752ad0f7e9STom Jones 	DPRINTF(("%s: phyctxt->sco=%d\n", __func__, phyctxt->sco));
74762ad0f7e9STom Jones 	DPRINTF(("%s: phyctxt->vht_chan_width=%d\n", __func__,
74772ad0f7e9STom Jones 	    phyctxt->vht_chan_width));
74782ad0f7e9STom Jones 
74792ad0f7e9STom Jones 	if (iwx_lookup_cmd_ver(sc, IWX_DATA_PATH_GROUP,
74802ad0f7e9STom Jones 	    IWX_RLC_CONFIG_CMD) == 2)
74812ad0f7e9STom Jones 		return iwx_phy_send_rlc(sc, phyctxt,
74822ad0f7e9STom Jones 		    chains_static, chains_dynamic);
74832ad0f7e9STom Jones 
74842ad0f7e9STom Jones 	return 0;
74852ad0f7e9STom Jones }
74862ad0f7e9STom Jones 
74872ad0f7e9STom Jones static int
74882ad0f7e9STom Jones iwx_auth(struct ieee80211vap *vap, struct iwx_softc *sc)
74892ad0f7e9STom Jones {
74902ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
74912ad0f7e9STom Jones 	struct iwx_node *in;
74922ad0f7e9STom Jones 	struct iwx_vap *ivp = IWX_VAP(vap);
74932ad0f7e9STom Jones 	struct ieee80211_node *ni;
74942ad0f7e9STom Jones 	uint32_t duration;
74952ad0f7e9STom Jones 	int generation = sc->sc_generation, err;
74962ad0f7e9STom Jones 
74972ad0f7e9STom Jones 	IWX_ASSERT_LOCKED(sc);
74982ad0f7e9STom Jones 
74992ad0f7e9STom Jones 	ni = ieee80211_ref_node(vap->iv_bss);
75002ad0f7e9STom Jones 	in = IWX_NODE(ni);
75012ad0f7e9STom Jones 
75022ad0f7e9STom Jones 	if (ic->ic_opmode == IEEE80211_M_MONITOR) {
75032ad0f7e9STom Jones 		err = iwx_phy_ctxt_update(sc, &sc->sc_phyctxt[0],
75042ad0f7e9STom Jones 		    ic->ic_bsschan, 1, 1, 0, IEEE80211_HTOP0_SCO_SCN,
75052ad0f7e9STom Jones 		    IEEE80211_VHTOP0_CHAN_WIDTH_HT);
75062ad0f7e9STom Jones 		if (err)
75072ad0f7e9STom Jones 			return err;
75082ad0f7e9STom Jones 	} else {
75092ad0f7e9STom Jones 		err = iwx_phy_ctxt_update(sc, &sc->sc_phyctxt[0],
75102ad0f7e9STom Jones 		    in->in_ni.ni_chan, 1, 1, 0, IEEE80211_HTOP0_SCO_SCN,
75112ad0f7e9STom Jones 		    IEEE80211_VHTOP0_CHAN_WIDTH_HT);
75122ad0f7e9STom Jones 		if (err)
75132ad0f7e9STom Jones 			return err;
75142ad0f7e9STom Jones 	}
75152ad0f7e9STom Jones 	ivp->phy_ctxt = &sc->sc_phyctxt[0];
75162ad0f7e9STom Jones 	IEEE80211_ADDR_COPY(in->in_macaddr, in->in_ni.ni_macaddr);
75172ad0f7e9STom Jones 	DPRINTF(("%s: in-in_macaddr=%s\n", __func__,
75182ad0f7e9STom Jones 	    ether_sprintf(in->in_macaddr)));
75192ad0f7e9STom Jones 
75202ad0f7e9STom Jones 	err = iwx_mac_ctxt_cmd(sc, in, IWX_FW_CTXT_ACTION_ADD, 0);
75212ad0f7e9STom Jones 	if (err) {
75222ad0f7e9STom Jones 		printf("%s: could not add MAC context (error %d)\n",
75232ad0f7e9STom Jones 		    DEVNAME(sc), err);
75242ad0f7e9STom Jones 		return err;
75252ad0f7e9STom Jones  	}
75262ad0f7e9STom Jones 	sc->sc_flags |= IWX_FLAG_MAC_ACTIVE;
75272ad0f7e9STom Jones 
75282ad0f7e9STom Jones 	err = iwx_binding_cmd(sc, in, IWX_FW_CTXT_ACTION_ADD);
75292ad0f7e9STom Jones 	if (err) {
75302ad0f7e9STom Jones 		printf("%s: could not add binding (error %d)\n",
75312ad0f7e9STom Jones 		    DEVNAME(sc), err);
75322ad0f7e9STom Jones 		goto rm_mac_ctxt;
75332ad0f7e9STom Jones 	}
75342ad0f7e9STom Jones 	sc->sc_flags |= IWX_FLAG_BINDING_ACTIVE;
75352ad0f7e9STom Jones 
75362ad0f7e9STom Jones 	err = iwx_add_sta_cmd(sc, in, 0);
75372ad0f7e9STom Jones 	if (err) {
75382ad0f7e9STom Jones 		printf("%s: could not add sta (error %d)\n",
75392ad0f7e9STom Jones 		    DEVNAME(sc), err);
75402ad0f7e9STom Jones 		goto rm_binding;
75412ad0f7e9STom Jones 	}
75422ad0f7e9STom Jones 	sc->sc_flags |= IWX_FLAG_STA_ACTIVE;
75432ad0f7e9STom Jones 
75442ad0f7e9STom Jones 	if (ic->ic_opmode == IEEE80211_M_MONITOR) {
75452ad0f7e9STom Jones 		err = iwx_enable_txq(sc, IWX_MONITOR_STA_ID,
75462ad0f7e9STom Jones 		    IWX_DQA_INJECT_MONITOR_QUEUE, IWX_MGMT_TID,
75472ad0f7e9STom Jones 		    IWX_TX_RING_COUNT);
75482ad0f7e9STom Jones 		if (err)
75492ad0f7e9STom Jones 			goto rm_sta;
75502ad0f7e9STom Jones 		return 0;
75512ad0f7e9STom Jones 	}
75522ad0f7e9STom Jones 
75532ad0f7e9STom Jones 	err = iwx_enable_mgmt_queue(sc);
75542ad0f7e9STom Jones 	if (err)
75552ad0f7e9STom Jones 		goto rm_sta;
75562ad0f7e9STom Jones 
75572ad0f7e9STom Jones 	err = iwx_clear_statistics(sc);
75582ad0f7e9STom Jones 	if (err)
75592ad0f7e9STom Jones 		goto rm_mgmt_queue;
75602ad0f7e9STom Jones 
75612ad0f7e9STom Jones 	/*
75622ad0f7e9STom Jones 	 * Prevent the FW from wandering off channel during association
75632ad0f7e9STom Jones 	 * by "protecting" the session with a time event.
75642ad0f7e9STom Jones 	 */
75652ad0f7e9STom Jones 	if (in->in_ni.ni_intval)
75662ad0f7e9STom Jones 		duration = in->in_ni.ni_intval * 9;
75672ad0f7e9STom Jones 	else
75682ad0f7e9STom Jones 		duration = 900;
75692ad0f7e9STom Jones 	return iwx_schedule_session_protection(sc, in, duration);
75702ad0f7e9STom Jones 
75712ad0f7e9STom Jones rm_mgmt_queue:
75722ad0f7e9STom Jones 	if (generation == sc->sc_generation)
75732ad0f7e9STom Jones 		iwx_disable_mgmt_queue(sc);
75742ad0f7e9STom Jones rm_sta:
75752ad0f7e9STom Jones 	if (generation == sc->sc_generation) {
75762ad0f7e9STom Jones 		iwx_rm_sta_cmd(sc, in);
75772ad0f7e9STom Jones 		sc->sc_flags &= ~IWX_FLAG_STA_ACTIVE;
75782ad0f7e9STom Jones 	}
75792ad0f7e9STom Jones rm_binding:
75802ad0f7e9STom Jones 	if (generation == sc->sc_generation) {
75812ad0f7e9STom Jones 		iwx_binding_cmd(sc, in, IWX_FW_CTXT_ACTION_REMOVE);
75822ad0f7e9STom Jones 		sc->sc_flags &= ~IWX_FLAG_BINDING_ACTIVE;
75832ad0f7e9STom Jones 	}
75842ad0f7e9STom Jones rm_mac_ctxt:
75852ad0f7e9STom Jones 	if (generation == sc->sc_generation) {
75862ad0f7e9STom Jones 		iwx_mac_ctxt_cmd(sc, in, IWX_FW_CTXT_ACTION_REMOVE, 0);
75872ad0f7e9STom Jones 		sc->sc_flags &= ~IWX_FLAG_MAC_ACTIVE;
75882ad0f7e9STom Jones 	}
75892ad0f7e9STom Jones 	return err;
75902ad0f7e9STom Jones }
75912ad0f7e9STom Jones 
75922ad0f7e9STom Jones static int
75932ad0f7e9STom Jones iwx_deauth(struct iwx_softc *sc)
75942ad0f7e9STom Jones {
75952ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
75962ad0f7e9STom Jones 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
75972ad0f7e9STom Jones 	struct iwx_node *in = IWX_NODE(vap->iv_bss);
75982ad0f7e9STom Jones 	int err;
75992ad0f7e9STom Jones 
76002ad0f7e9STom Jones 	IWX_ASSERT_LOCKED(sc);
76012ad0f7e9STom Jones 
76022ad0f7e9STom Jones 	iwx_unprotect_session(sc, in);
76032ad0f7e9STom Jones 
76042ad0f7e9STom Jones 	if (sc->sc_flags & IWX_FLAG_STA_ACTIVE) {
76052ad0f7e9STom Jones 		err = iwx_rm_sta(sc, in);
76062ad0f7e9STom Jones 		if (err)
76072ad0f7e9STom Jones 			return err;
76082ad0f7e9STom Jones 		sc->sc_flags &= ~IWX_FLAG_STA_ACTIVE;
76092ad0f7e9STom Jones 	}
76102ad0f7e9STom Jones 
76112ad0f7e9STom Jones 	if (sc->sc_flags & IWX_FLAG_BINDING_ACTIVE) {
76122ad0f7e9STom Jones 		err = iwx_binding_cmd(sc, in, IWX_FW_CTXT_ACTION_REMOVE);
76132ad0f7e9STom Jones 		if (err) {
76142ad0f7e9STom Jones 			printf("%s: could not remove binding (error %d)\n",
76152ad0f7e9STom Jones 			    DEVNAME(sc), err);
76162ad0f7e9STom Jones 			return err;
76172ad0f7e9STom Jones 		}
76182ad0f7e9STom Jones 		sc->sc_flags &= ~IWX_FLAG_BINDING_ACTIVE;
76192ad0f7e9STom Jones 	}
76202ad0f7e9STom Jones 
76212ad0f7e9STom Jones 	DPRINTF(("%s:  IWX_FLAG_MAC_ACTIVE=%d\n", __func__, sc->sc_flags &
76222ad0f7e9STom Jones 	    IWX_FLAG_MAC_ACTIVE));
76232ad0f7e9STom Jones 	if (sc->sc_flags & IWX_FLAG_MAC_ACTIVE) {
76242ad0f7e9STom Jones 		err = iwx_mac_ctxt_cmd(sc, in, IWX_FW_CTXT_ACTION_REMOVE, 0);
76252ad0f7e9STom Jones 		if (err) {
76262ad0f7e9STom Jones 			printf("%s: could not remove MAC context (error %d)\n",
76272ad0f7e9STom Jones 			    DEVNAME(sc), err);
76282ad0f7e9STom Jones 			return err;
76292ad0f7e9STom Jones 		}
76302ad0f7e9STom Jones 		sc->sc_flags &= ~IWX_FLAG_MAC_ACTIVE;
76312ad0f7e9STom Jones 	}
76322ad0f7e9STom Jones 
76332ad0f7e9STom Jones 	/* Move unused PHY context to a default channel. */
76342ad0f7e9STom Jones 	//TODO uncommented in obsd, but stays on the way of auth->auth
76352ad0f7e9STom Jones 	err = iwx_phy_ctxt_update(sc, &sc->sc_phyctxt[0],
76362ad0f7e9STom Jones 	    &ic->ic_channels[1], 1, 1, 0, IEEE80211_HTOP0_SCO_SCN,
76372ad0f7e9STom Jones 	    IEEE80211_VHTOP0_CHAN_WIDTH_HT);
76382ad0f7e9STom Jones 	if (err)
76392ad0f7e9STom Jones 		return err;
76402ad0f7e9STom Jones 
76412ad0f7e9STom Jones 	return 0;
76422ad0f7e9STom Jones }
76432ad0f7e9STom Jones 
76442ad0f7e9STom Jones static int
76452ad0f7e9STom Jones iwx_run(struct ieee80211vap *vap, struct iwx_softc *sc)
76462ad0f7e9STom Jones {
76472ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
76482ad0f7e9STom Jones 	struct iwx_node *in = IWX_NODE(vap->iv_bss);
76492ad0f7e9STom Jones 	struct ieee80211_node *ni = &in->in_ni;
76502ad0f7e9STom Jones 	struct iwx_vap *ivp = IWX_VAP(vap);
76512ad0f7e9STom Jones 	int err;
76522ad0f7e9STom Jones 
76532ad0f7e9STom Jones 	IWX_ASSERT_LOCKED(sc);
76542ad0f7e9STom Jones 
76552ad0f7e9STom Jones 	if (ni->ni_flags & IEEE80211_NODE_HT) {
76562ad0f7e9STom Jones 		uint8_t chains = iwx_mimo_enabled(sc) ? 2 : 1;
76572ad0f7e9STom Jones 		uint8_t sco, vht_chan_width;
76582ad0f7e9STom Jones 			sco = IEEE80211_HTOP0_SCO_SCN;
76592ad0f7e9STom Jones 		if ((ni->ni_flags & IEEE80211_NODE_VHT) &&
76602ad0f7e9STom Jones 		    IEEE80211_IS_CHAN_VHT80(ni->ni_chan))
76612ad0f7e9STom Jones 			vht_chan_width = IEEE80211_VHTOP0_CHAN_WIDTH_80;
76622ad0f7e9STom Jones 		else
76632ad0f7e9STom Jones 			vht_chan_width = IEEE80211_VHTOP0_CHAN_WIDTH_HT;
76642ad0f7e9STom Jones 		err = iwx_phy_ctxt_update(sc, ivp->phy_ctxt,
76652ad0f7e9STom Jones 		    ivp->phy_ctxt->channel, chains, chains,
76662ad0f7e9STom Jones 		    0, sco, vht_chan_width);
76672ad0f7e9STom Jones 		if (err) {
76682ad0f7e9STom Jones 			printf("%s: failed to update PHY\n", DEVNAME(sc));
76692ad0f7e9STom Jones 			return err;
76702ad0f7e9STom Jones 		}
76712ad0f7e9STom Jones 	}
76722ad0f7e9STom Jones 
76732ad0f7e9STom Jones 	/* Update STA again to apply HT and VHT settings. */
76742ad0f7e9STom Jones 	err = iwx_add_sta_cmd(sc, in, 1);
76752ad0f7e9STom Jones 	if (err) {
76762ad0f7e9STom Jones 		printf("%s: could not update STA (error %d)\n",
76772ad0f7e9STom Jones 		    DEVNAME(sc), err);
76782ad0f7e9STom Jones 		return err;
76792ad0f7e9STom Jones 	}
76802ad0f7e9STom Jones 
76812ad0f7e9STom Jones 	/* We have now been assigned an associd by the AP. */
76822ad0f7e9STom Jones 	err = iwx_mac_ctxt_cmd(sc, in, IWX_FW_CTXT_ACTION_MODIFY, 1);
76832ad0f7e9STom Jones 	if (err) {
76842ad0f7e9STom Jones 		printf("%s: failed to update MAC\n", DEVNAME(sc));
76852ad0f7e9STom Jones 		return err;
76862ad0f7e9STom Jones 	}
76872ad0f7e9STom Jones 
76882ad0f7e9STom Jones 	err = iwx_sf_config(sc, IWX_SF_FULL_ON);
76892ad0f7e9STom Jones 	if (err) {
76902ad0f7e9STom Jones 		printf("%s: could not set sf full on (error %d)\n",
76912ad0f7e9STom Jones 		    DEVNAME(sc), err);
76922ad0f7e9STom Jones 		return err;
76932ad0f7e9STom Jones 	}
76942ad0f7e9STom Jones 
76952ad0f7e9STom Jones 	err = iwx_allow_mcast(sc);
76962ad0f7e9STom Jones 	if (err) {
76972ad0f7e9STom Jones 		printf("%s: could not allow mcast (error %d)\n",
76982ad0f7e9STom Jones 		    DEVNAME(sc), err);
76992ad0f7e9STom Jones 		return err;
77002ad0f7e9STom Jones 	}
77012ad0f7e9STom Jones 
77022ad0f7e9STom Jones 	err = iwx_power_update_device(sc);
77032ad0f7e9STom Jones 	if (err) {
77042ad0f7e9STom Jones 		printf("%s: could not send power command (error %d)\n",
77052ad0f7e9STom Jones 		    DEVNAME(sc), err);
77062ad0f7e9STom Jones 		return err;
77072ad0f7e9STom Jones 	}
77082ad0f7e9STom Jones #ifdef notyet
77092ad0f7e9STom Jones 	/*
77102ad0f7e9STom Jones 	 * Disabled for now. Default beacon filter settings
77112ad0f7e9STom Jones 	 * prevent net80211 from getting ERP and HT protection
77122ad0f7e9STom Jones 	 * updates from beacons.
77132ad0f7e9STom Jones 	 */
77142ad0f7e9STom Jones 	err = iwx_enable_beacon_filter(sc, in);
77152ad0f7e9STom Jones 	if (err) {
77162ad0f7e9STom Jones 		printf("%s: could not enable beacon filter\n",
77172ad0f7e9STom Jones 		    DEVNAME(sc));
77182ad0f7e9STom Jones 		return err;
77192ad0f7e9STom Jones 	}
77202ad0f7e9STom Jones #endif
77212ad0f7e9STom Jones 	err = iwx_power_mac_update_mode(sc, in);
77222ad0f7e9STom Jones 	if (err) {
77232ad0f7e9STom Jones 		printf("%s: could not update MAC power (error %d)\n",
77242ad0f7e9STom Jones 		    DEVNAME(sc), err);
77252ad0f7e9STom Jones 		return err;
77262ad0f7e9STom Jones 	}
77272ad0f7e9STom Jones 
77282ad0f7e9STom Jones 	if (ic->ic_opmode == IEEE80211_M_MONITOR)
77292ad0f7e9STom Jones 		return 0;
77302ad0f7e9STom Jones 
77312ad0f7e9STom Jones 	err = iwx_rs_init(sc, in);
77322ad0f7e9STom Jones 	if (err) {
77332ad0f7e9STom Jones 		printf("%s: could not init rate scaling (error %d)\n",
77342ad0f7e9STom Jones 		    DEVNAME(sc), err);
77352ad0f7e9STom Jones 		return err;
77362ad0f7e9STom Jones 	}
77372ad0f7e9STom Jones 
77382ad0f7e9STom Jones 	return 0;
77392ad0f7e9STom Jones }
77402ad0f7e9STom Jones 
77412ad0f7e9STom Jones static int
77422ad0f7e9STom Jones iwx_run_stop(struct iwx_softc *sc)
77432ad0f7e9STom Jones {
77442ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
77452ad0f7e9STom Jones 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
77462ad0f7e9STom Jones 	struct iwx_node *in = IWX_NODE(vap->iv_bss);
77472ad0f7e9STom Jones 	struct ieee80211_node *ni = &in->in_ni;
77482ad0f7e9STom Jones 	int err, i;
77492ad0f7e9STom Jones 
77502ad0f7e9STom Jones 	IWX_ASSERT_LOCKED(sc);
77512ad0f7e9STom Jones 
77522ad0f7e9STom Jones 	err = iwx_flush_sta(sc, in);
77532ad0f7e9STom Jones 	if (err) {
77542ad0f7e9STom Jones 		printf("%s: could not flush Tx path (error %d)\n",
77552ad0f7e9STom Jones 		    DEVNAME(sc), err);
77562ad0f7e9STom Jones 		return err;
77572ad0f7e9STom Jones 	}
77582ad0f7e9STom Jones 
77592ad0f7e9STom Jones 	/*
77602ad0f7e9STom Jones 	 * Stop Rx BA sessions now. We cannot rely on the BA task
77612ad0f7e9STom Jones 	 * for this when moving out of RUN state since it runs in a
77622ad0f7e9STom Jones 	 * separate thread.
77632ad0f7e9STom Jones 	 * Note that in->in_ni (struct ieee80211_node) already represents
77642ad0f7e9STom Jones 	 * our new access point in case we are roaming between APs.
77652ad0f7e9STom Jones 	 * This means we cannot rely on struct ieee802111_node to tell
77662ad0f7e9STom Jones 	 * us which BA sessions exist.
77672ad0f7e9STom Jones 	 */
77682ad0f7e9STom Jones 	// TODO agg
77692ad0f7e9STom Jones 	for (i = 0; i < nitems(sc->sc_rxba_data); i++) {
77702ad0f7e9STom Jones 		struct iwx_rxba_data *rxba = &sc->sc_rxba_data[i];
77712ad0f7e9STom Jones 		if (rxba->baid == IWX_RX_REORDER_DATA_INVALID_BAID)
77722ad0f7e9STom Jones 			continue;
77732ad0f7e9STom Jones 		iwx_sta_rx_agg(sc, ni, rxba->tid, 0, 0, 0, 0);
77742ad0f7e9STom Jones 	}
77752ad0f7e9STom Jones 
77762ad0f7e9STom Jones 	err = iwx_sf_config(sc, IWX_SF_INIT_OFF);
77772ad0f7e9STom Jones 	if (err)
77782ad0f7e9STom Jones 		return err;
77792ad0f7e9STom Jones 
77802ad0f7e9STom Jones 	err = iwx_disable_beacon_filter(sc);
77812ad0f7e9STom Jones 	if (err) {
77822ad0f7e9STom Jones 		printf("%s: could not disable beacon filter (error %d)\n",
77832ad0f7e9STom Jones 		    DEVNAME(sc), err);
77842ad0f7e9STom Jones 		return err;
77852ad0f7e9STom Jones 	}
77862ad0f7e9STom Jones 
77872ad0f7e9STom Jones 	/* Mark station as disassociated. */
77882ad0f7e9STom Jones 	err = iwx_mac_ctxt_cmd(sc, in, IWX_FW_CTXT_ACTION_MODIFY, 0);
77892ad0f7e9STom Jones 	if (err) {
77902ad0f7e9STom Jones 		printf("%s: failed to update MAC\n", DEVNAME(sc));
77912ad0f7e9STom Jones 		return err;
77922ad0f7e9STom Jones 	}
77932ad0f7e9STom Jones 
77942ad0f7e9STom Jones 	return 0;
77952ad0f7e9STom Jones }
77962ad0f7e9STom Jones 
77972ad0f7e9STom Jones static struct ieee80211_node *
77982ad0f7e9STom Jones iwx_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
77992ad0f7e9STom Jones {
78002ad0f7e9STom Jones 	return malloc(sizeof (struct iwx_node), M_80211_NODE,
78012ad0f7e9STom Jones 	    M_NOWAIT | M_ZERO);
78022ad0f7e9STom Jones }
78032ad0f7e9STom Jones 
78042ad0f7e9STom Jones #if 0
78052ad0f7e9STom Jones int
78062ad0f7e9STom Jones iwx_set_key(struct ieee80211com *ic, struct ieee80211_node *ni,
78072ad0f7e9STom Jones     struct ieee80211_key *k)
78082ad0f7e9STom Jones {
78092ad0f7e9STom Jones 	struct iwx_softc *sc = ic->ic_softc;
78102ad0f7e9STom Jones 	struct iwx_node *in = (void *)ni;
78112ad0f7e9STom Jones 	struct iwx_setkey_task_arg *a;
78122ad0f7e9STom Jones 	int err;
78132ad0f7e9STom Jones 
78142ad0f7e9STom Jones 	if (k->k_cipher != IEEE80211_CIPHER_CCMP) {
78152ad0f7e9STom Jones 		/* Fallback to software crypto for other ciphers. */
78162ad0f7e9STom Jones 		err = ieee80211_set_key(ic, ni, k);
78172ad0f7e9STom Jones 		if (!err && in != NULL && (k->k_flags & IEEE80211_KEY_GROUP))
78182ad0f7e9STom Jones 			in->in_flags |= IWX_NODE_FLAG_HAVE_GROUP_KEY;
78192ad0f7e9STom Jones 		return err;
78202ad0f7e9STom Jones 	}
78212ad0f7e9STom Jones 
78222ad0f7e9STom Jones 	if (sc->setkey_nkeys >= nitems(sc->setkey_arg))
78232ad0f7e9STom Jones 		return ENOSPC;
78242ad0f7e9STom Jones 
78252ad0f7e9STom Jones 	a = &sc->setkey_arg[sc->setkey_cur];
78262ad0f7e9STom Jones 	a->sta_id = IWX_STATION_ID;
78272ad0f7e9STom Jones 	a->ni = ni;
78282ad0f7e9STom Jones 	a->k = k;
78292ad0f7e9STom Jones 	sc->setkey_cur = (sc->setkey_cur + 1) % nitems(sc->setkey_arg);
78302ad0f7e9STom Jones 	sc->setkey_nkeys++;
78312ad0f7e9STom Jones 	iwx_add_task(sc, systq, &sc->setkey_task);
78322ad0f7e9STom Jones 	return EBUSY;
78332ad0f7e9STom Jones }
78342ad0f7e9STom Jones 
78352ad0f7e9STom Jones int
78362ad0f7e9STom Jones iwx_add_sta_key(struct iwx_softc *sc, int sta_id, struct ieee80211_node *ni,
78372ad0f7e9STom Jones     struct ieee80211_key *k)
78382ad0f7e9STom Jones {
78392ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
78402ad0f7e9STom Jones 	struct iwx_node *in = (void *)ni;
78412ad0f7e9STom Jones 	struct iwx_add_sta_key_cmd cmd;
78422ad0f7e9STom Jones 	uint32_t status;
78432ad0f7e9STom Jones 	const int want_keymask = (IWX_NODE_FLAG_HAVE_PAIRWISE_KEY |
78442ad0f7e9STom Jones 	    IWX_NODE_FLAG_HAVE_GROUP_KEY);
78452ad0f7e9STom Jones 	int err;
78462ad0f7e9STom Jones 
78472ad0f7e9STom Jones 	/*
78482ad0f7e9STom Jones 	 * Keys are stored in 'ni' so 'k' is valid if 'ni' is valid.
78492ad0f7e9STom Jones 	 * Currently we only implement station mode where 'ni' is always
78502ad0f7e9STom Jones 	 * ic->ic_bss so there is no need to validate arguments beyond this:
78512ad0f7e9STom Jones 	 */
78522ad0f7e9STom Jones 	KASSERT(ni == ic->ic_bss);
78532ad0f7e9STom Jones 
78542ad0f7e9STom Jones 	memset(&cmd, 0, sizeof(cmd));
78552ad0f7e9STom Jones 
78562ad0f7e9STom Jones 	cmd.common.key_flags = htole16(IWX_STA_KEY_FLG_CCM |
78572ad0f7e9STom Jones 	    IWX_STA_KEY_FLG_WEP_KEY_MAP |
78582ad0f7e9STom Jones 	    ((k->k_id << IWX_STA_KEY_FLG_KEYID_POS) &
78592ad0f7e9STom Jones 	    IWX_STA_KEY_FLG_KEYID_MSK));
78602ad0f7e9STom Jones 	if (k->k_flags & IEEE80211_KEY_GROUP) {
78612ad0f7e9STom Jones 		cmd.common.key_offset = 1;
78622ad0f7e9STom Jones 		cmd.common.key_flags |= htole16(IWX_STA_KEY_MULTICAST);
78632ad0f7e9STom Jones 	} else
78642ad0f7e9STom Jones 		cmd.common.key_offset = 0;
78652ad0f7e9STom Jones 
78662ad0f7e9STom Jones 	memcpy(cmd.common.key, k->k_key, MIN(sizeof(cmd.common.key), k->k_len));
78672ad0f7e9STom Jones 	cmd.common.sta_id = sta_id;
78682ad0f7e9STom Jones 
78692ad0f7e9STom Jones 	cmd.transmit_seq_cnt = htole64(k->k_tsc);
78702ad0f7e9STom Jones 
78712ad0f7e9STom Jones 	status = IWX_ADD_STA_SUCCESS;
78722ad0f7e9STom Jones 	err = iwx_send_cmd_pdu_status(sc, IWX_ADD_STA_KEY, sizeof(cmd), &cmd,
78732ad0f7e9STom Jones 	    &status);
78742ad0f7e9STom Jones 	if (sc->sc_flags & IWX_FLAG_SHUTDOWN)
78752ad0f7e9STom Jones 		return ECANCELED;
78762ad0f7e9STom Jones 	if (!err && (status & IWX_ADD_STA_STATUS_MASK) != IWX_ADD_STA_SUCCESS)
78772ad0f7e9STom Jones 		err = EIO;
78782ad0f7e9STom Jones 	if (err) {
78792ad0f7e9STom Jones 		IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
78802ad0f7e9STom Jones 		    IEEE80211_REASON_AUTH_LEAVE);
78812ad0f7e9STom Jones 		ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
78822ad0f7e9STom Jones 		return err;
78832ad0f7e9STom Jones 	}
78842ad0f7e9STom Jones 
78852ad0f7e9STom Jones 	if (k->k_flags & IEEE80211_KEY_GROUP)
78862ad0f7e9STom Jones 		in->in_flags |= IWX_NODE_FLAG_HAVE_GROUP_KEY;
78872ad0f7e9STom Jones 	else
78882ad0f7e9STom Jones 		in->in_flags |= IWX_NODE_FLAG_HAVE_PAIRWISE_KEY;
78892ad0f7e9STom Jones 
78902ad0f7e9STom Jones 	if ((in->in_flags & want_keymask) == want_keymask) {
78912ad0f7e9STom Jones 		DPRINTF(("marking port %s valid\n",
78922ad0f7e9STom Jones 		    ether_sprintf(ni->ni_macaddr)));
78932ad0f7e9STom Jones 		ni->ni_port_valid = 1;
78942ad0f7e9STom Jones 		ieee80211_set_link_state(ic, LINK_STATE_UP);
78952ad0f7e9STom Jones 	}
78962ad0f7e9STom Jones 
78972ad0f7e9STom Jones 	return 0;
78982ad0f7e9STom Jones }
78992ad0f7e9STom Jones 
79002ad0f7e9STom Jones void
79012ad0f7e9STom Jones iwx_setkey_task(void *arg)
79022ad0f7e9STom Jones {
79032ad0f7e9STom Jones 	struct iwx_softc *sc = arg;
79042ad0f7e9STom Jones 	struct iwx_setkey_task_arg *a;
79052ad0f7e9STom Jones 	int err = 0, s = splnet();
79062ad0f7e9STom Jones 
79072ad0f7e9STom Jones 	while (sc->setkey_nkeys > 0) {
79082ad0f7e9STom Jones 		if (err || (sc->sc_flags & IWX_FLAG_SHUTDOWN))
79092ad0f7e9STom Jones 			break;
79102ad0f7e9STom Jones 		a = &sc->setkey_arg[sc->setkey_tail];
79112ad0f7e9STom Jones 		err = iwx_add_sta_key(sc, a->sta_id, a->ni, a->k);
79122ad0f7e9STom Jones 		a->sta_id = 0;
79132ad0f7e9STom Jones 		a->ni = NULL;
79142ad0f7e9STom Jones 		a->k = NULL;
79152ad0f7e9STom Jones 		sc->setkey_tail = (sc->setkey_tail + 1) %
79162ad0f7e9STom Jones 		    nitems(sc->setkey_arg);
79172ad0f7e9STom Jones 		sc->setkey_nkeys--;
79182ad0f7e9STom Jones 	}
79192ad0f7e9STom Jones 
79202ad0f7e9STom Jones 	refcnt_rele_wake(&sc->task_refs);
79212ad0f7e9STom Jones 	splx(s);
79222ad0f7e9STom Jones }
79232ad0f7e9STom Jones 
79242ad0f7e9STom Jones void
79252ad0f7e9STom Jones iwx_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni,
79262ad0f7e9STom Jones     struct ieee80211_key *k)
79272ad0f7e9STom Jones {
79282ad0f7e9STom Jones 	struct iwx_softc *sc = ic->ic_softc;
79292ad0f7e9STom Jones 	struct iwx_add_sta_key_cmd cmd;
79302ad0f7e9STom Jones 
79312ad0f7e9STom Jones 	if (k->k_cipher != IEEE80211_CIPHER_CCMP) {
79322ad0f7e9STom Jones 		/* Fallback to software crypto for other ciphers. */
79332ad0f7e9STom Jones                 ieee80211_delete_key(ic, ni, k);
79342ad0f7e9STom Jones 		return;
79352ad0f7e9STom Jones 	}
79362ad0f7e9STom Jones 
79372ad0f7e9STom Jones 	if ((sc->sc_flags & IWX_FLAG_STA_ACTIVE) == 0)
79382ad0f7e9STom Jones 		return;
79392ad0f7e9STom Jones 
79402ad0f7e9STom Jones 	memset(&cmd, 0, sizeof(cmd));
79412ad0f7e9STom Jones 
79422ad0f7e9STom Jones 	cmd.common.key_flags = htole16(IWX_STA_KEY_NOT_VALID |
79432ad0f7e9STom Jones 	    IWX_STA_KEY_FLG_NO_ENC | IWX_STA_KEY_FLG_WEP_KEY_MAP |
79442ad0f7e9STom Jones 	    ((k->k_id << IWX_STA_KEY_FLG_KEYID_POS) &
79452ad0f7e9STom Jones 	    IWX_STA_KEY_FLG_KEYID_MSK));
79462ad0f7e9STom Jones 	memcpy(cmd.common.key, k->k_key, MIN(sizeof(cmd.common.key), k->k_len));
79472ad0f7e9STom Jones 	if (k->k_flags & IEEE80211_KEY_GROUP)
79482ad0f7e9STom Jones 		cmd.common.key_offset = 1;
79492ad0f7e9STom Jones 	else
79502ad0f7e9STom Jones 		cmd.common.key_offset = 0;
79512ad0f7e9STom Jones 	cmd.common.sta_id = IWX_STATION_ID;
79522ad0f7e9STom Jones 
79532ad0f7e9STom Jones 	iwx_send_cmd_pdu(sc, IWX_ADD_STA_KEY, IWX_CMD_ASYNC, sizeof(cmd), &cmd);
79542ad0f7e9STom Jones }
79552ad0f7e9STom Jones #endif
79562ad0f7e9STom Jones 
79572ad0f7e9STom Jones static int
79582ad0f7e9STom Jones iwx_newstate_sub(struct ieee80211vap *vap, enum ieee80211_state nstate)
79592ad0f7e9STom Jones {
79602ad0f7e9STom Jones 	struct ieee80211com *ic = vap->iv_ic;
79612ad0f7e9STom Jones 	struct iwx_softc *sc = ic->ic_softc;
79622ad0f7e9STom Jones 	enum ieee80211_state ostate = vap->iv_state;
79632ad0f7e9STom Jones 	int err = 0;
79642ad0f7e9STom Jones 
79652ad0f7e9STom Jones 	IWX_LOCK(sc);
79662ad0f7e9STom Jones 
79672ad0f7e9STom Jones 	if (nstate <= ostate || nstate > IEEE80211_S_RUN) {
79682ad0f7e9STom Jones 		switch (ostate) {
79692ad0f7e9STom Jones 		case IEEE80211_S_RUN:
79702ad0f7e9STom Jones 			err = iwx_run_stop(sc);
79712ad0f7e9STom Jones 			if (err)
79722ad0f7e9STom Jones 				goto out;
79732ad0f7e9STom Jones 			/* FALLTHROUGH */
79742ad0f7e9STom Jones 		case IEEE80211_S_ASSOC:
79752ad0f7e9STom Jones 		case IEEE80211_S_AUTH:
79762ad0f7e9STom Jones 			if (nstate <= IEEE80211_S_AUTH) {
79772ad0f7e9STom Jones 				err = iwx_deauth(sc);
79782ad0f7e9STom Jones 				if (err)
79792ad0f7e9STom Jones 					goto out;
79802ad0f7e9STom Jones 			}
79812ad0f7e9STom Jones 			/* FALLTHROUGH */
79822ad0f7e9STom Jones 		case IEEE80211_S_SCAN:
79832ad0f7e9STom Jones 		case IEEE80211_S_INIT:
79842ad0f7e9STom Jones 		default:
79852ad0f7e9STom Jones 			break;
79862ad0f7e9STom Jones 		}
79872ad0f7e9STom Jones //
79882ad0f7e9STom Jones //		/* Die now if iwx_stop() was called while we were sleeping. */
79892ad0f7e9STom Jones //		if (sc->sc_flags & IWX_FLAG_SHUTDOWN) {
79902ad0f7e9STom Jones //			refcnt_rele_wake(&sc->task_refs);
79912ad0f7e9STom Jones //			splx(s);
79922ad0f7e9STom Jones //			return;
79932ad0f7e9STom Jones //		}
79942ad0f7e9STom Jones 	}
79952ad0f7e9STom Jones 
79962ad0f7e9STom Jones 	switch (nstate) {
79972ad0f7e9STom Jones 	case IEEE80211_S_INIT:
79982ad0f7e9STom Jones 		break;
79992ad0f7e9STom Jones 
80002ad0f7e9STom Jones 	case IEEE80211_S_SCAN:
80012ad0f7e9STom Jones 			break;
80022ad0f7e9STom Jones 
80032ad0f7e9STom Jones 	case IEEE80211_S_AUTH:
80042ad0f7e9STom Jones 		err = iwx_auth(vap, sc);
80052ad0f7e9STom Jones 		break;
80062ad0f7e9STom Jones 
80072ad0f7e9STom Jones 	case IEEE80211_S_ASSOC:
80082ad0f7e9STom Jones 		break;
80092ad0f7e9STom Jones 
80102ad0f7e9STom Jones 	case IEEE80211_S_RUN:
80112ad0f7e9STom Jones 		err = iwx_run(vap, sc);
80122ad0f7e9STom Jones 		break;
80132ad0f7e9STom Jones 	default:
80142ad0f7e9STom Jones 		break;
80152ad0f7e9STom Jones 	}
80162ad0f7e9STom Jones 
80172ad0f7e9STom Jones out:
80182ad0f7e9STom Jones 	IWX_UNLOCK(sc);
80192ad0f7e9STom Jones 
80202ad0f7e9STom Jones 	return (err);
80212ad0f7e9STom Jones }
80222ad0f7e9STom Jones 
80232ad0f7e9STom Jones static int
80242ad0f7e9STom Jones iwx_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
80252ad0f7e9STom Jones {
80262ad0f7e9STom Jones 	struct iwx_vap *ivp = IWX_VAP(vap);
80272ad0f7e9STom Jones 	struct ieee80211com *ic = vap->iv_ic;
80282ad0f7e9STom Jones 	enum ieee80211_state ostate = vap->iv_state;
80292ad0f7e9STom Jones 	int err;
80302ad0f7e9STom Jones 
80312ad0f7e9STom Jones 	/*
80322ad0f7e9STom Jones 	 * Prevent attempts to transition towards the same state, unless
80332ad0f7e9STom Jones 	 * we are scanning in which case a SCAN -> SCAN transition
80342ad0f7e9STom Jones 	 * triggers another scan iteration. And AUTH -> AUTH is needed
80352ad0f7e9STom Jones 	 * to support band-steering.
80362ad0f7e9STom Jones 	 */
80372ad0f7e9STom Jones 	if (ostate == nstate && nstate != IEEE80211_S_SCAN &&
80382ad0f7e9STom Jones 	    nstate != IEEE80211_S_AUTH)
80392ad0f7e9STom Jones 		return 0;
80402ad0f7e9STom Jones 	IEEE80211_UNLOCK(ic);
80412ad0f7e9STom Jones 	err = iwx_newstate_sub(vap, nstate);
80422ad0f7e9STom Jones 	IEEE80211_LOCK(ic);
80432ad0f7e9STom Jones 	if (err == 0)
80442ad0f7e9STom Jones 		err = ivp->iv_newstate(vap, nstate, arg);
80452ad0f7e9STom Jones 
80462ad0f7e9STom Jones 	return (err);
80472ad0f7e9STom Jones }
80482ad0f7e9STom Jones 
80492ad0f7e9STom Jones static void
80502ad0f7e9STom Jones iwx_endscan(struct iwx_softc *sc)
80512ad0f7e9STom Jones {
80522ad0f7e9STom Jones         struct ieee80211com *ic = &sc->sc_ic;
80532ad0f7e9STom Jones         struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
80542ad0f7e9STom Jones 
80552ad0f7e9STom Jones         if ((sc->sc_flags & (IWX_FLAG_SCANNING | IWX_FLAG_BGSCAN)) == 0)
80562ad0f7e9STom Jones                 return;
80572ad0f7e9STom Jones 
80582ad0f7e9STom Jones         sc->sc_flags &= ~(IWX_FLAG_SCANNING | IWX_FLAG_BGSCAN);
80592ad0f7e9STom Jones 
80602ad0f7e9STom Jones         ieee80211_scan_done(TAILQ_FIRST(&ic->ic_vaps));
80612ad0f7e9STom Jones         wakeup(&vap->iv_state); /* wake up iwx_newstate */
80622ad0f7e9STom Jones }
80632ad0f7e9STom Jones 
80642ad0f7e9STom Jones /*
80652ad0f7e9STom Jones  * Aging and idle timeouts for the different possible scenarios
80662ad0f7e9STom Jones  * in default configuration
80672ad0f7e9STom Jones  */
80682ad0f7e9STom Jones static const uint32_t
80692ad0f7e9STom Jones iwx_sf_full_timeout_def[IWX_SF_NUM_SCENARIO][IWX_SF_NUM_TIMEOUT_TYPES] = {
80702ad0f7e9STom Jones 	{
80712ad0f7e9STom Jones 		htole32(IWX_SF_SINGLE_UNICAST_AGING_TIMER_DEF),
80722ad0f7e9STom Jones 		htole32(IWX_SF_SINGLE_UNICAST_IDLE_TIMER_DEF)
80732ad0f7e9STom Jones 	},
80742ad0f7e9STom Jones 	{
80752ad0f7e9STom Jones 		htole32(IWX_SF_AGG_UNICAST_AGING_TIMER_DEF),
80762ad0f7e9STom Jones 		htole32(IWX_SF_AGG_UNICAST_IDLE_TIMER_DEF)
80772ad0f7e9STom Jones 	},
80782ad0f7e9STom Jones 	{
80792ad0f7e9STom Jones 		htole32(IWX_SF_MCAST_AGING_TIMER_DEF),
80802ad0f7e9STom Jones 		htole32(IWX_SF_MCAST_IDLE_TIMER_DEF)
80812ad0f7e9STom Jones 	},
80822ad0f7e9STom Jones 	{
80832ad0f7e9STom Jones 		htole32(IWX_SF_BA_AGING_TIMER_DEF),
80842ad0f7e9STom Jones 		htole32(IWX_SF_BA_IDLE_TIMER_DEF)
80852ad0f7e9STom Jones 	},
80862ad0f7e9STom Jones 	{
80872ad0f7e9STom Jones 		htole32(IWX_SF_TX_RE_AGING_TIMER_DEF),
80882ad0f7e9STom Jones 		htole32(IWX_SF_TX_RE_IDLE_TIMER_DEF)
80892ad0f7e9STom Jones 	},
80902ad0f7e9STom Jones };
80912ad0f7e9STom Jones 
80922ad0f7e9STom Jones /*
80932ad0f7e9STom Jones  * Aging and idle timeouts for the different possible scenarios
80942ad0f7e9STom Jones  * in single BSS MAC configuration.
80952ad0f7e9STom Jones  */
80962ad0f7e9STom Jones static const uint32_t
80972ad0f7e9STom Jones iwx_sf_full_timeout[IWX_SF_NUM_SCENARIO][IWX_SF_NUM_TIMEOUT_TYPES] = {
80982ad0f7e9STom Jones 	{
80992ad0f7e9STom Jones 		htole32(IWX_SF_SINGLE_UNICAST_AGING_TIMER),
81002ad0f7e9STom Jones 		htole32(IWX_SF_SINGLE_UNICAST_IDLE_TIMER)
81012ad0f7e9STom Jones 	},
81022ad0f7e9STom Jones 	{
81032ad0f7e9STom Jones 		htole32(IWX_SF_AGG_UNICAST_AGING_TIMER),
81042ad0f7e9STom Jones 		htole32(IWX_SF_AGG_UNICAST_IDLE_TIMER)
81052ad0f7e9STom Jones 	},
81062ad0f7e9STom Jones 	{
81072ad0f7e9STom Jones 		htole32(IWX_SF_MCAST_AGING_TIMER),
81082ad0f7e9STom Jones 		htole32(IWX_SF_MCAST_IDLE_TIMER)
81092ad0f7e9STom Jones 	},
81102ad0f7e9STom Jones 	{
81112ad0f7e9STom Jones 		htole32(IWX_SF_BA_AGING_TIMER),
81122ad0f7e9STom Jones 		htole32(IWX_SF_BA_IDLE_TIMER)
81132ad0f7e9STom Jones 	},
81142ad0f7e9STom Jones 	{
81152ad0f7e9STom Jones 		htole32(IWX_SF_TX_RE_AGING_TIMER),
81162ad0f7e9STom Jones 		htole32(IWX_SF_TX_RE_IDLE_TIMER)
81172ad0f7e9STom Jones 	},
81182ad0f7e9STom Jones };
81192ad0f7e9STom Jones 
81202ad0f7e9STom Jones static void
81212ad0f7e9STom Jones iwx_fill_sf_command(struct iwx_softc *sc, struct iwx_sf_cfg_cmd *sf_cmd,
81222ad0f7e9STom Jones     struct ieee80211_node *ni)
81232ad0f7e9STom Jones {
81242ad0f7e9STom Jones 	int i, j, watermark;
81252ad0f7e9STom Jones 
81262ad0f7e9STom Jones 	sf_cmd->watermark[IWX_SF_LONG_DELAY_ON] = htole32(IWX_SF_W_MARK_SCAN);
81272ad0f7e9STom Jones 
81282ad0f7e9STom Jones 	/*
81292ad0f7e9STom Jones 	 * If we are in association flow - check antenna configuration
81302ad0f7e9STom Jones 	 * capabilities of the AP station, and choose the watermark accordingly.
81312ad0f7e9STom Jones 	 */
81322ad0f7e9STom Jones 	if (ni) {
81332ad0f7e9STom Jones 		if (ni->ni_flags & IEEE80211_NODE_HT) {
81342ad0f7e9STom Jones 			struct ieee80211_htrateset *htrs = &ni->ni_htrates;
81352ad0f7e9STom Jones 			int hasmimo = 0;
81362ad0f7e9STom Jones 			for (i = 0; i < htrs->rs_nrates; i++) {
81372ad0f7e9STom Jones 				if (htrs->rs_rates[i] > 7) {
81382ad0f7e9STom Jones 					hasmimo = 1;
81392ad0f7e9STom Jones 					break;
81402ad0f7e9STom Jones 				}
81412ad0f7e9STom Jones 			}
81422ad0f7e9STom Jones 			if (hasmimo)
81432ad0f7e9STom Jones 				watermark = IWX_SF_W_MARK_MIMO2;
81442ad0f7e9STom Jones 			else
81452ad0f7e9STom Jones 				watermark = IWX_SF_W_MARK_SISO;
81462ad0f7e9STom Jones 		} else {
81472ad0f7e9STom Jones 			watermark = IWX_SF_W_MARK_LEGACY;
81482ad0f7e9STom Jones 		}
81492ad0f7e9STom Jones 	/* default watermark value for unassociated mode. */
81502ad0f7e9STom Jones 	} else {
81512ad0f7e9STom Jones 		watermark = IWX_SF_W_MARK_MIMO2;
81522ad0f7e9STom Jones 	}
81532ad0f7e9STom Jones 	sf_cmd->watermark[IWX_SF_FULL_ON] = htole32(watermark);
81542ad0f7e9STom Jones 
81552ad0f7e9STom Jones 	for (i = 0; i < IWX_SF_NUM_SCENARIO; i++) {
81562ad0f7e9STom Jones 		for (j = 0; j < IWX_SF_NUM_TIMEOUT_TYPES; j++) {
81572ad0f7e9STom Jones 			sf_cmd->long_delay_timeouts[i][j] =
81582ad0f7e9STom Jones 					htole32(IWX_SF_LONG_DELAY_AGING_TIMER);
81592ad0f7e9STom Jones 		}
81602ad0f7e9STom Jones 	}
81612ad0f7e9STom Jones 
81622ad0f7e9STom Jones 	if (ni) {
81632ad0f7e9STom Jones 		memcpy(sf_cmd->full_on_timeouts, iwx_sf_full_timeout,
81642ad0f7e9STom Jones 		       sizeof(iwx_sf_full_timeout));
81652ad0f7e9STom Jones 	} else {
81662ad0f7e9STom Jones 		memcpy(sf_cmd->full_on_timeouts, iwx_sf_full_timeout_def,
81672ad0f7e9STom Jones 		       sizeof(iwx_sf_full_timeout_def));
81682ad0f7e9STom Jones 	}
81692ad0f7e9STom Jones 
81702ad0f7e9STom Jones }
81712ad0f7e9STom Jones 
81722ad0f7e9STom Jones static int
81732ad0f7e9STom Jones iwx_sf_config(struct iwx_softc *sc, int new_state)
81742ad0f7e9STom Jones {
81752ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
81762ad0f7e9STom Jones 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
81772ad0f7e9STom Jones 	struct ieee80211_node *ni = vap->iv_bss;
81782ad0f7e9STom Jones 	struct iwx_sf_cfg_cmd sf_cmd = {
81792ad0f7e9STom Jones 		.state = htole32(new_state),
81802ad0f7e9STom Jones 	};
81812ad0f7e9STom Jones 	int err = 0;
81822ad0f7e9STom Jones 
81832ad0f7e9STom Jones 	switch (new_state) {
81842ad0f7e9STom Jones 	case IWX_SF_UNINIT:
81852ad0f7e9STom Jones 	case IWX_SF_INIT_OFF:
81862ad0f7e9STom Jones 		iwx_fill_sf_command(sc, &sf_cmd, NULL);
81872ad0f7e9STom Jones 		break;
81882ad0f7e9STom Jones 	case IWX_SF_FULL_ON:
81892ad0f7e9STom Jones 		iwx_fill_sf_command(sc, &sf_cmd, ni);
81902ad0f7e9STom Jones 		break;
81912ad0f7e9STom Jones 	default:
81922ad0f7e9STom Jones 		return EINVAL;
81932ad0f7e9STom Jones 	}
81942ad0f7e9STom Jones 
81952ad0f7e9STom Jones 	err = iwx_send_cmd_pdu(sc, IWX_REPLY_SF_CFG_CMD, IWX_CMD_ASYNC,
81962ad0f7e9STom Jones 				   sizeof(sf_cmd), &sf_cmd);
81972ad0f7e9STom Jones 	return err;
81982ad0f7e9STom Jones }
81992ad0f7e9STom Jones 
82002ad0f7e9STom Jones static int
82012ad0f7e9STom Jones iwx_send_bt_init_conf(struct iwx_softc *sc)
82022ad0f7e9STom Jones {
82032ad0f7e9STom Jones 	struct iwx_bt_coex_cmd bt_cmd;
82042ad0f7e9STom Jones 
82052ad0f7e9STom Jones 	bzero(&bt_cmd, sizeof(struct iwx_bt_coex_cmd));
82062ad0f7e9STom Jones 
82072ad0f7e9STom Jones 	bt_cmd.mode = htole32(IWX_BT_COEX_NW);
82082ad0f7e9STom Jones 	bt_cmd.enabled_modules |= BT_COEX_SYNC2SCO_ENABLED;
82092ad0f7e9STom Jones 	bt_cmd.enabled_modules |= BT_COEX_HIGH_BAND_RET;
82102ad0f7e9STom Jones 
82112ad0f7e9STom Jones 
82122ad0f7e9STom Jones 	return iwx_send_cmd_pdu(sc, IWX_BT_CONFIG, 0, sizeof(bt_cmd),
82132ad0f7e9STom Jones 	    &bt_cmd);
82142ad0f7e9STom Jones }
82152ad0f7e9STom Jones 
82162ad0f7e9STom Jones static int
82172ad0f7e9STom Jones iwx_send_soc_conf(struct iwx_softc *sc)
82182ad0f7e9STom Jones {
82192ad0f7e9STom Jones 	struct iwx_soc_configuration_cmd cmd;
82202ad0f7e9STom Jones 	int err;
82212ad0f7e9STom Jones 	uint32_t cmd_id, flags = 0;
82222ad0f7e9STom Jones 
82232ad0f7e9STom Jones 	memset(&cmd, 0, sizeof(cmd));
82242ad0f7e9STom Jones 
82252ad0f7e9STom Jones 	/*
82262ad0f7e9STom Jones 	 * In VER_1 of this command, the discrete value is considered
82272ad0f7e9STom Jones 	 * an integer; In VER_2, it's a bitmask.  Since we have only 2
82282ad0f7e9STom Jones 	 * values in VER_1, this is backwards-compatible with VER_2,
82292ad0f7e9STom Jones 	 * as long as we don't set any other flag bits.
82302ad0f7e9STom Jones 	 */
82312ad0f7e9STom Jones 	if (!sc->sc_integrated) { /* VER_1 */
82322ad0f7e9STom Jones 		flags = IWX_SOC_CONFIG_CMD_FLAGS_DISCRETE;
82332ad0f7e9STom Jones 	} else { /* VER_2 */
82342ad0f7e9STom Jones 		uint8_t scan_cmd_ver;
82352ad0f7e9STom Jones 		if (sc->sc_ltr_delay != IWX_SOC_FLAGS_LTR_APPLY_DELAY_NONE)
82362ad0f7e9STom Jones 			flags |= (sc->sc_ltr_delay &
82372ad0f7e9STom Jones 			    IWX_SOC_FLAGS_LTR_APPLY_DELAY_MASK);
82382ad0f7e9STom Jones 		scan_cmd_ver = iwx_lookup_cmd_ver(sc, IWX_LONG_GROUP,
82392ad0f7e9STom Jones 		    IWX_SCAN_REQ_UMAC);
82402ad0f7e9STom Jones 		if (scan_cmd_ver != IWX_FW_CMD_VER_UNKNOWN &&
82412ad0f7e9STom Jones 		    scan_cmd_ver >= 2 && sc->sc_low_latency_xtal)
82422ad0f7e9STom Jones 			flags |= IWX_SOC_CONFIG_CMD_FLAGS_LOW_LATENCY;
82432ad0f7e9STom Jones 	}
82442ad0f7e9STom Jones 	cmd.flags = htole32(flags);
82452ad0f7e9STom Jones 
82462ad0f7e9STom Jones 	cmd.latency = htole32(sc->sc_xtal_latency);
82472ad0f7e9STom Jones 
82482ad0f7e9STom Jones 	cmd_id = iwx_cmd_id(IWX_SOC_CONFIGURATION_CMD, IWX_SYSTEM_GROUP, 0);
82492ad0f7e9STom Jones 	err = iwx_send_cmd_pdu(sc, cmd_id, 0, sizeof(cmd), &cmd);
82502ad0f7e9STom Jones 	if (err)
82512ad0f7e9STom Jones 		printf("%s: failed to set soc latency: %d\n", DEVNAME(sc), err);
82522ad0f7e9STom Jones 	return err;
82532ad0f7e9STom Jones }
82542ad0f7e9STom Jones 
82552ad0f7e9STom Jones static int
82562ad0f7e9STom Jones iwx_send_update_mcc_cmd(struct iwx_softc *sc, const char *alpha2)
82572ad0f7e9STom Jones {
82582ad0f7e9STom Jones 	struct iwx_mcc_update_cmd mcc_cmd;
82592ad0f7e9STom Jones 	struct iwx_host_cmd hcmd = {
82602ad0f7e9STom Jones 		.id = IWX_MCC_UPDATE_CMD,
82612ad0f7e9STom Jones 		.flags = IWX_CMD_WANT_RESP,
82622ad0f7e9STom Jones 		.data = { &mcc_cmd },
82632ad0f7e9STom Jones 	};
82642ad0f7e9STom Jones 	struct iwx_rx_packet *pkt;
82652ad0f7e9STom Jones 	struct iwx_mcc_update_resp *resp;
82662ad0f7e9STom Jones 	size_t resp_len;
82672ad0f7e9STom Jones 	int err;
82682ad0f7e9STom Jones 
82692ad0f7e9STom Jones 	memset(&mcc_cmd, 0, sizeof(mcc_cmd));
82702ad0f7e9STom Jones 	mcc_cmd.mcc = htole16(alpha2[0] << 8 | alpha2[1]);
82712ad0f7e9STom Jones 	if (isset(sc->sc_ucode_api, IWX_UCODE_TLV_API_WIFI_MCC_UPDATE) ||
82722ad0f7e9STom Jones 	    isset(sc->sc_enabled_capa, IWX_UCODE_TLV_CAPA_LAR_MULTI_MCC))
82732ad0f7e9STom Jones 		mcc_cmd.source_id = IWX_MCC_SOURCE_GET_CURRENT;
82742ad0f7e9STom Jones 	else
82752ad0f7e9STom Jones 		mcc_cmd.source_id = IWX_MCC_SOURCE_OLD_FW;
82762ad0f7e9STom Jones 
82772ad0f7e9STom Jones 	hcmd.len[0] = sizeof(struct iwx_mcc_update_cmd);
82782ad0f7e9STom Jones 	hcmd.resp_pkt_len = IWX_CMD_RESP_MAX;
82792ad0f7e9STom Jones 
82802ad0f7e9STom Jones 	err = iwx_send_cmd(sc, &hcmd);
82812ad0f7e9STom Jones 	if (err)
82822ad0f7e9STom Jones 		return err;
82832ad0f7e9STom Jones 
82842ad0f7e9STom Jones 	pkt = hcmd.resp_pkt;
82852ad0f7e9STom Jones 	if (!pkt || (pkt->hdr.flags & IWX_CMD_FAILED_MSK)) {
82862ad0f7e9STom Jones 		err = EIO;
82872ad0f7e9STom Jones 		goto out;
82882ad0f7e9STom Jones 	}
82892ad0f7e9STom Jones 
82902ad0f7e9STom Jones 	resp_len = iwx_rx_packet_payload_len(pkt);
82912ad0f7e9STom Jones 	if (resp_len < sizeof(*resp)) {
82922ad0f7e9STom Jones 		err = EIO;
82932ad0f7e9STom Jones 		goto out;
82942ad0f7e9STom Jones 	}
82952ad0f7e9STom Jones 
82962ad0f7e9STom Jones 	resp = (void *)pkt->data;
82972ad0f7e9STom Jones 	if (resp_len != sizeof(*resp) +
82982ad0f7e9STom Jones 	    resp->n_channels * sizeof(resp->channels[0])) {
82992ad0f7e9STom Jones 		err = EIO;
83002ad0f7e9STom Jones 		goto out;
83012ad0f7e9STom Jones 	}
83022ad0f7e9STom Jones 
83032ad0f7e9STom Jones 	DPRINTF(("MCC status=0x%x mcc=0x%x cap=0x%x time=0x%x geo_info=0x%x source_id=0x%d n_channels=%u\n",
83042ad0f7e9STom Jones 	    resp->status, resp->mcc, resp->cap, resp->time, resp->geo_info, resp->source_id, resp->n_channels));
83052ad0f7e9STom Jones 
83062ad0f7e9STom Jones out:
83072ad0f7e9STom Jones 	iwx_free_resp(sc, &hcmd);
83082ad0f7e9STom Jones 
83092ad0f7e9STom Jones 	return err;
83102ad0f7e9STom Jones }
83112ad0f7e9STom Jones 
83122ad0f7e9STom Jones static int
83132ad0f7e9STom Jones iwx_send_temp_report_ths_cmd(struct iwx_softc *sc)
83142ad0f7e9STom Jones {
83152ad0f7e9STom Jones 	struct iwx_temp_report_ths_cmd cmd;
83162ad0f7e9STom Jones 	int err;
83172ad0f7e9STom Jones 
83182ad0f7e9STom Jones 	/*
83192ad0f7e9STom Jones 	 * In order to give responsibility for critical-temperature-kill
83202ad0f7e9STom Jones 	 * and TX backoff to FW we need to send an empty temperature
83212ad0f7e9STom Jones 	 * reporting command at init time.
83222ad0f7e9STom Jones 	 */
83232ad0f7e9STom Jones 	memset(&cmd, 0, sizeof(cmd));
83242ad0f7e9STom Jones 
83252ad0f7e9STom Jones 	err = iwx_send_cmd_pdu(sc,
83262ad0f7e9STom Jones 	    IWX_WIDE_ID(IWX_PHY_OPS_GROUP, IWX_TEMP_REPORTING_THRESHOLDS_CMD),
83272ad0f7e9STom Jones 	    0, sizeof(cmd), &cmd);
83282ad0f7e9STom Jones 	if (err)
83292ad0f7e9STom Jones 		printf("%s: TEMP_REPORT_THS_CMD command failed (error %d)\n",
83302ad0f7e9STom Jones 		    DEVNAME(sc), err);
83312ad0f7e9STom Jones 
83322ad0f7e9STom Jones 	return err;
83332ad0f7e9STom Jones }
83342ad0f7e9STom Jones 
83352ad0f7e9STom Jones static int
83362ad0f7e9STom Jones iwx_init_hw(struct iwx_softc *sc)
83372ad0f7e9STom Jones {
83382ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
83392ad0f7e9STom Jones 	int err = 0, i;
83402ad0f7e9STom Jones 
83412ad0f7e9STom Jones 	err = iwx_run_init_mvm_ucode(sc, 0);
83422ad0f7e9STom Jones 	if (err)
83432ad0f7e9STom Jones 		return err;
83442ad0f7e9STom Jones 
83452ad0f7e9STom Jones 	if (!iwx_nic_lock(sc))
83462ad0f7e9STom Jones 		return EBUSY;
83472ad0f7e9STom Jones 
83482ad0f7e9STom Jones 	err = iwx_send_tx_ant_cfg(sc, iwx_fw_valid_tx_ant(sc));
83492ad0f7e9STom Jones 	if (err) {
83502ad0f7e9STom Jones 		printf("%s: could not init tx ant config (error %d)\n",
83512ad0f7e9STom Jones 		    DEVNAME(sc), err);
83522ad0f7e9STom Jones 		goto err;
83532ad0f7e9STom Jones 	}
83542ad0f7e9STom Jones 
83552ad0f7e9STom Jones 	if (sc->sc_tx_with_siso_diversity) {
83562ad0f7e9STom Jones 		err = iwx_send_phy_cfg_cmd(sc);
83572ad0f7e9STom Jones 		if (err) {
83582ad0f7e9STom Jones 			printf("%s: could not send phy config (error %d)\n",
83592ad0f7e9STom Jones 			    DEVNAME(sc), err);
83602ad0f7e9STom Jones 			goto err;
83612ad0f7e9STom Jones 		}
83622ad0f7e9STom Jones 	}
83632ad0f7e9STom Jones 
83642ad0f7e9STom Jones 	err = iwx_send_bt_init_conf(sc);
83652ad0f7e9STom Jones 	if (err) {
83662ad0f7e9STom Jones 		printf("%s: could not init bt coex (error %d)\n",
83672ad0f7e9STom Jones 		    DEVNAME(sc), err);
83682ad0f7e9STom Jones 		return err;
83692ad0f7e9STom Jones 	}
83702ad0f7e9STom Jones 
83712ad0f7e9STom Jones 	err = iwx_send_soc_conf(sc);
83722ad0f7e9STom Jones 	if (err) {
83732ad0f7e9STom Jones 		printf("%s: iwx_send_soc_conf failed\n", __func__);
83742ad0f7e9STom Jones 		return err;
83752ad0f7e9STom Jones 	}
83762ad0f7e9STom Jones 
83772ad0f7e9STom Jones 	if (isset(sc->sc_enabled_capa, IWX_UCODE_TLV_CAPA_DQA_SUPPORT)) {
83782ad0f7e9STom Jones 		printf("%s: === IWX_UCODE_TLV_CAPA_DQA_SUPPORT\n", __func__);
83792ad0f7e9STom Jones 		err = iwx_send_dqa_cmd(sc);
83802ad0f7e9STom Jones 		if (err) {
83812ad0f7e9STom Jones 			printf("%s: IWX_UCODE_TLV_CAPA_DQA_SUPPORT "
83822ad0f7e9STom Jones 			    "failed (error %d)\n", __func__, err);
83832ad0f7e9STom Jones 			return err;
83842ad0f7e9STom Jones 		}
83852ad0f7e9STom Jones 	}
83862ad0f7e9STom Jones 	// TODO phyctxt
83872ad0f7e9STom Jones 	for (i = 0; i < IWX_NUM_PHY_CTX; i++) {
83882ad0f7e9STom Jones 		/*
83892ad0f7e9STom Jones 		 * The channel used here isn't relevant as it's
83902ad0f7e9STom Jones 		 * going to be overwritten in the other flows.
83912ad0f7e9STom Jones 		 * For now use the first channel we have.
83922ad0f7e9STom Jones 		 */
83932ad0f7e9STom Jones 		sc->sc_phyctxt[i].id = i;
83942ad0f7e9STom Jones 		sc->sc_phyctxt[i].channel = &ic->ic_channels[1];
83952ad0f7e9STom Jones 		err = iwx_phy_ctxt_cmd(sc, &sc->sc_phyctxt[i], 1, 1,
83962ad0f7e9STom Jones 		    IWX_FW_CTXT_ACTION_ADD, 0, 0, 0);
83972ad0f7e9STom Jones 		if (err) {
83982ad0f7e9STom Jones 			printf("%s: could not add phy context %d (error %d)\n",
83992ad0f7e9STom Jones 			    DEVNAME(sc), i, err);
84002ad0f7e9STom Jones 			goto err;
84012ad0f7e9STom Jones 		}
84022ad0f7e9STom Jones 		if (iwx_lookup_cmd_ver(sc, IWX_DATA_PATH_GROUP,
84032ad0f7e9STom Jones 		    IWX_RLC_CONFIG_CMD) == 2) {
84042ad0f7e9STom Jones 			err = iwx_phy_send_rlc(sc, &sc->sc_phyctxt[i], 1, 1);
84052ad0f7e9STom Jones 			if (err) {
84062ad0f7e9STom Jones 				printf("%s: could not configure RLC for PHY "
84072ad0f7e9STom Jones 				    "%d (error %d)\n", DEVNAME(sc), i, err);
84082ad0f7e9STom Jones 				goto err;
84092ad0f7e9STom Jones 			}
84102ad0f7e9STom Jones 		}
84112ad0f7e9STom Jones 	}
84122ad0f7e9STom Jones 
84132ad0f7e9STom Jones 	err = iwx_config_ltr(sc);
84142ad0f7e9STom Jones 	if (err) {
84152ad0f7e9STom Jones 		printf("%s: PCIe LTR configuration failed (error %d)\n",
84162ad0f7e9STom Jones 		    DEVNAME(sc), err);
84172ad0f7e9STom Jones 	}
84182ad0f7e9STom Jones 
84192ad0f7e9STom Jones 	if (isset(sc->sc_enabled_capa, IWX_UCODE_TLV_CAPA_CT_KILL_BY_FW)) {
84202ad0f7e9STom Jones 		err = iwx_send_temp_report_ths_cmd(sc);
84212ad0f7e9STom Jones 		if (err) {
84222ad0f7e9STom Jones 			printf("%s: iwx_send_temp_report_ths_cmd failed\n",
84232ad0f7e9STom Jones 			    __func__);
84242ad0f7e9STom Jones 			goto err;
84252ad0f7e9STom Jones 		}
84262ad0f7e9STom Jones 	}
84272ad0f7e9STom Jones 
84282ad0f7e9STom Jones 	err = iwx_power_update_device(sc);
84292ad0f7e9STom Jones 	if (err) {
84302ad0f7e9STom Jones 		printf("%s: could not send power command (error %d)\n",
84312ad0f7e9STom Jones 		    DEVNAME(sc), err);
84322ad0f7e9STom Jones 		goto err;
84332ad0f7e9STom Jones 	}
84342ad0f7e9STom Jones 
84352ad0f7e9STom Jones 	if (sc->sc_nvm.lar_enabled) {
84362ad0f7e9STom Jones 		err = iwx_send_update_mcc_cmd(sc, "ZZ");
84372ad0f7e9STom Jones 		if (err) {
84382ad0f7e9STom Jones 			printf("%s: could not init LAR (error %d)\n",
84392ad0f7e9STom Jones 			    DEVNAME(sc), err);
84402ad0f7e9STom Jones 			goto err;
84412ad0f7e9STom Jones 		}
84422ad0f7e9STom Jones 	}
84432ad0f7e9STom Jones 
84442ad0f7e9STom Jones 	err = iwx_config_umac_scan_reduced(sc);
84452ad0f7e9STom Jones 	if (err) {
84462ad0f7e9STom Jones 		printf("%s: could not configure scan (error %d)\n",
84472ad0f7e9STom Jones 		    DEVNAME(sc), err);
84482ad0f7e9STom Jones 		goto err;
84492ad0f7e9STom Jones 	}
84502ad0f7e9STom Jones 
84512ad0f7e9STom Jones 	err = iwx_disable_beacon_filter(sc);
84522ad0f7e9STom Jones 	if (err) {
84532ad0f7e9STom Jones 		printf("%s: could not disable beacon filter (error %d)\n",
84542ad0f7e9STom Jones 		    DEVNAME(sc), err);
84552ad0f7e9STom Jones 		goto err;
84562ad0f7e9STom Jones 	}
84572ad0f7e9STom Jones 
84582ad0f7e9STom Jones err:
84592ad0f7e9STom Jones 	iwx_nic_unlock(sc);
84602ad0f7e9STom Jones 	return err;
84612ad0f7e9STom Jones }
84622ad0f7e9STom Jones 
84632ad0f7e9STom Jones /* Allow multicast from our BSSID. */
84642ad0f7e9STom Jones static int
84652ad0f7e9STom Jones iwx_allow_mcast(struct iwx_softc *sc)
84662ad0f7e9STom Jones {
84672ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
84682ad0f7e9STom Jones 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
84692ad0f7e9STom Jones 	struct iwx_node *in = IWX_NODE(vap->iv_bss);
84702ad0f7e9STom Jones 	struct iwx_mcast_filter_cmd *cmd;
84712ad0f7e9STom Jones 	size_t size;
84722ad0f7e9STom Jones 	int err;
84732ad0f7e9STom Jones 
84742ad0f7e9STom Jones 	size = roundup(sizeof(*cmd), 4);
84752ad0f7e9STom Jones 	cmd = malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO);
84762ad0f7e9STom Jones 	if (cmd == NULL)
84772ad0f7e9STom Jones 		return ENOMEM;
84782ad0f7e9STom Jones 	cmd->filter_own = 1;
84792ad0f7e9STom Jones 	cmd->port_id = 0;
84802ad0f7e9STom Jones 	cmd->count = 0;
84812ad0f7e9STom Jones 	cmd->pass_all = 1;
84822ad0f7e9STom Jones 	IEEE80211_ADDR_COPY(cmd->bssid, in->in_macaddr);
84832ad0f7e9STom Jones 
84842ad0f7e9STom Jones 	err = iwx_send_cmd_pdu(sc, IWX_MCAST_FILTER_CMD,
84852ad0f7e9STom Jones 	    0, size, cmd);
84862ad0f7e9STom Jones 	free(cmd, M_DEVBUF);
84872ad0f7e9STom Jones 	return err;
84882ad0f7e9STom Jones }
84892ad0f7e9STom Jones 
84902ad0f7e9STom Jones static int
84912ad0f7e9STom Jones iwx_init(struct iwx_softc *sc)
84922ad0f7e9STom Jones {
84932ad0f7e9STom Jones 	int err, generation;
84942ad0f7e9STom Jones 	generation = ++sc->sc_generation;
84952ad0f7e9STom Jones 	iwx_preinit(sc);
84962ad0f7e9STom Jones 
84972ad0f7e9STom Jones 	err = iwx_start_hw(sc);
84982ad0f7e9STom Jones 	if (err) {
84992ad0f7e9STom Jones 		printf("%s: iwx_start_hw failed\n", __func__);
85002ad0f7e9STom Jones 		return err;
85012ad0f7e9STom Jones 	}
85022ad0f7e9STom Jones 
85032ad0f7e9STom Jones 	err = iwx_init_hw(sc);
85042ad0f7e9STom Jones 	if (err) {
85052ad0f7e9STom Jones 		if (generation == sc->sc_generation)
85062ad0f7e9STom Jones 			iwx_stop_device(sc);
85072ad0f7e9STom Jones 		printf("%s: iwx_init_hw failed (error %d)\n", __func__, err);
85082ad0f7e9STom Jones 		return err;
85092ad0f7e9STom Jones 	}
85102ad0f7e9STom Jones 
85112ad0f7e9STom Jones 	sc->sc_flags |= IWX_FLAG_HW_INITED;
85122ad0f7e9STom Jones 	callout_reset(&sc->watchdog_to, hz, iwx_watchdog, sc);
85132ad0f7e9STom Jones 
85142ad0f7e9STom Jones 	return 0;
85152ad0f7e9STom Jones }
85162ad0f7e9STom Jones 
85172ad0f7e9STom Jones static void
85182ad0f7e9STom Jones iwx_start(struct iwx_softc *sc)
85192ad0f7e9STom Jones {
85202ad0f7e9STom Jones         struct ieee80211_node *ni;
85212ad0f7e9STom Jones         struct mbuf *m;
85222ad0f7e9STom Jones 
85232ad0f7e9STom Jones         while (sc->qfullmsk == 0 && (m = mbufq_dequeue(&sc->sc_snd)) != NULL) {
85242ad0f7e9STom Jones                 ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
85252ad0f7e9STom Jones                 if (iwx_tx(sc, m, ni) != 0) {
85262ad0f7e9STom Jones                       if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1);
85272ad0f7e9STom Jones                         continue;
85282ad0f7e9STom Jones                 }
85292ad0f7e9STom Jones         }
85302ad0f7e9STom Jones }
85312ad0f7e9STom Jones 
85322ad0f7e9STom Jones static void
85332ad0f7e9STom Jones iwx_stop(struct iwx_softc *sc)
85342ad0f7e9STom Jones {
85352ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
85362ad0f7e9STom Jones 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
85372ad0f7e9STom Jones 	struct iwx_vap *ivp = IWX_VAP(vap);
85382ad0f7e9STom Jones 
85392ad0f7e9STom Jones 	iwx_stop_device(sc);
85402ad0f7e9STom Jones 
85412ad0f7e9STom Jones 	/* Reset soft state. */
85422ad0f7e9STom Jones 	sc->sc_generation++;
85432ad0f7e9STom Jones 	ivp->phy_ctxt = NULL;
85442ad0f7e9STom Jones 
85452ad0f7e9STom Jones 	sc->sc_flags &= ~(IWX_FLAG_SCANNING | IWX_FLAG_BGSCAN);
85462ad0f7e9STom Jones 	sc->sc_flags &= ~IWX_FLAG_MAC_ACTIVE;
85472ad0f7e9STom Jones 	sc->sc_flags &= ~IWX_FLAG_BINDING_ACTIVE;
85482ad0f7e9STom Jones 	sc->sc_flags &= ~IWX_FLAG_STA_ACTIVE;
85492ad0f7e9STom Jones 	sc->sc_flags &= ~IWX_FLAG_TE_ACTIVE;
85502ad0f7e9STom Jones 	sc->sc_flags &= ~IWX_FLAG_HW_ERR;
85512ad0f7e9STom Jones 	sc->sc_flags &= ~IWX_FLAG_SHUTDOWN;
85522ad0f7e9STom Jones 	sc->sc_flags &= ~IWX_FLAG_TXFLUSH;
85532ad0f7e9STom Jones 
85542ad0f7e9STom Jones 	sc->sc_rx_ba_sessions = 0;
85552ad0f7e9STom Jones 	sc->ba_rx.start_tidmask = 0;
85562ad0f7e9STom Jones 	sc->ba_rx.stop_tidmask = 0;
85572ad0f7e9STom Jones 	memset(sc->aggqid, 0, sizeof(sc->aggqid));
85582ad0f7e9STom Jones 	sc->ba_tx.start_tidmask = 0;
85592ad0f7e9STom Jones 	sc->ba_tx.stop_tidmask = 0;
85602ad0f7e9STom Jones }
85612ad0f7e9STom Jones 
85622ad0f7e9STom Jones static void
85632ad0f7e9STom Jones iwx_watchdog(void *arg)
85642ad0f7e9STom Jones {
85652ad0f7e9STom Jones 	struct iwx_softc *sc = arg;
85662ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
85672ad0f7e9STom Jones 	int i;
85682ad0f7e9STom Jones 
85692ad0f7e9STom Jones 	/*
85702ad0f7e9STom Jones 	 * We maintain a separate timer for each Tx queue because
85712ad0f7e9STom Jones 	 * Tx aggregation queues can get "stuck" while other queues
85722ad0f7e9STom Jones 	 * keep working. The Linux driver uses a similar workaround.
85732ad0f7e9STom Jones 	 */
85742ad0f7e9STom Jones 	for (i = 0; i < nitems(sc->sc_tx_timer); i++) {
85752ad0f7e9STom Jones 		if (sc->sc_tx_timer[i] > 0) {
85762ad0f7e9STom Jones 			if (--sc->sc_tx_timer[i] == 0) {
85772ad0f7e9STom Jones 				printf("%s: device timeout\n", DEVNAME(sc));
85782ad0f7e9STom Jones 
85792ad0f7e9STom Jones 				iwx_nic_error(sc);
85802ad0f7e9STom Jones 				iwx_dump_driver_status(sc);
85812ad0f7e9STom Jones 				ieee80211_restart_all(ic);
85822ad0f7e9STom Jones 				return;
85832ad0f7e9STom Jones 			}
85842ad0f7e9STom Jones 		}
85852ad0f7e9STom Jones 	}
85862ad0f7e9STom Jones 	callout_reset(&sc->watchdog_to, hz, iwx_watchdog, sc);
85872ad0f7e9STom Jones }
85882ad0f7e9STom Jones 
85892ad0f7e9STom Jones /*
85902ad0f7e9STom Jones  * Note: This structure is read from the device with IO accesses,
85912ad0f7e9STom Jones  * and the reading already does the endian conversion. As it is
85922ad0f7e9STom Jones  * read with uint32_t-sized accesses, any members with a different size
85932ad0f7e9STom Jones  * need to be ordered correctly though!
85942ad0f7e9STom Jones  */
85952ad0f7e9STom Jones struct iwx_error_event_table {
85962ad0f7e9STom Jones 	uint32_t valid;		/* (nonzero) valid, (0) log is empty */
85972ad0f7e9STom Jones 	uint32_t error_id;		/* type of error */
85982ad0f7e9STom Jones 	uint32_t trm_hw_status0;	/* TRM HW status */
85992ad0f7e9STom Jones 	uint32_t trm_hw_status1;	/* TRM HW status */
86002ad0f7e9STom Jones 	uint32_t blink2;		/* branch link */
86012ad0f7e9STom Jones 	uint32_t ilink1;		/* interrupt link */
86022ad0f7e9STom Jones 	uint32_t ilink2;		/* interrupt link */
86032ad0f7e9STom Jones 	uint32_t data1;		/* error-specific data */
86042ad0f7e9STom Jones 	uint32_t data2;		/* error-specific data */
86052ad0f7e9STom Jones 	uint32_t data3;		/* error-specific data */
86062ad0f7e9STom Jones 	uint32_t bcon_time;		/* beacon timer */
86072ad0f7e9STom Jones 	uint32_t tsf_low;		/* network timestamp function timer */
86082ad0f7e9STom Jones 	uint32_t tsf_hi;		/* network timestamp function timer */
86092ad0f7e9STom Jones 	uint32_t gp1;		/* GP1 timer register */
86102ad0f7e9STom Jones 	uint32_t gp2;		/* GP2 timer register */
86112ad0f7e9STom Jones 	uint32_t fw_rev_type;	/* firmware revision type */
86122ad0f7e9STom Jones 	uint32_t major;		/* uCode version major */
86132ad0f7e9STom Jones 	uint32_t minor;		/* uCode version minor */
86142ad0f7e9STom Jones 	uint32_t hw_ver;		/* HW Silicon version */
86152ad0f7e9STom Jones 	uint32_t brd_ver;		/* HW board version */
86162ad0f7e9STom Jones 	uint32_t log_pc;		/* log program counter */
86172ad0f7e9STom Jones 	uint32_t frame_ptr;		/* frame pointer */
86182ad0f7e9STom Jones 	uint32_t stack_ptr;		/* stack pointer */
86192ad0f7e9STom Jones 	uint32_t hcmd;		/* last host command header */
86202ad0f7e9STom Jones 	uint32_t isr0;		/* isr status register LMPM_NIC_ISR0:
86212ad0f7e9STom Jones 				 * rxtx_flag */
86222ad0f7e9STom Jones 	uint32_t isr1;		/* isr status register LMPM_NIC_ISR1:
86232ad0f7e9STom Jones 				 * host_flag */
86242ad0f7e9STom Jones 	uint32_t isr2;		/* isr status register LMPM_NIC_ISR2:
86252ad0f7e9STom Jones 				 * enc_flag */
86262ad0f7e9STom Jones 	uint32_t isr3;		/* isr status register LMPM_NIC_ISR3:
86272ad0f7e9STom Jones 				 * time_flag */
86282ad0f7e9STom Jones 	uint32_t isr4;		/* isr status register LMPM_NIC_ISR4:
86292ad0f7e9STom Jones 				 * wico interrupt */
86302ad0f7e9STom Jones 	uint32_t last_cmd_id;	/* last HCMD id handled by the firmware */
86312ad0f7e9STom Jones 	uint32_t wait_event;		/* wait event() caller address */
86322ad0f7e9STom Jones 	uint32_t l2p_control;	/* L2pControlField */
86332ad0f7e9STom Jones 	uint32_t l2p_duration;	/* L2pDurationField */
86342ad0f7e9STom Jones 	uint32_t l2p_mhvalid;	/* L2pMhValidBits */
86352ad0f7e9STom Jones 	uint32_t l2p_addr_match;	/* L2pAddrMatchStat */
86362ad0f7e9STom Jones 	uint32_t lmpm_pmg_sel;	/* indicate which clocks are turned on
86372ad0f7e9STom Jones 				 * (LMPM_PMG_SEL) */
86382ad0f7e9STom Jones 	uint32_t u_timestamp;	/* indicate when the date and time of the
86392ad0f7e9STom Jones 				 * compilation */
86402ad0f7e9STom Jones 	uint32_t flow_handler;	/* FH read/write pointers, RX credit */
86412ad0f7e9STom Jones } __packed /* LOG_ERROR_TABLE_API_S_VER_3 */;
86422ad0f7e9STom Jones 
86432ad0f7e9STom Jones /*
86442ad0f7e9STom Jones  * UMAC error struct - relevant starting from family 8000 chip.
86452ad0f7e9STom Jones  * Note: This structure is read from the device with IO accesses,
86462ad0f7e9STom Jones  * and the reading already does the endian conversion. As it is
86472ad0f7e9STom Jones  * read with u32-sized accesses, any members with a different size
86482ad0f7e9STom Jones  * need to be ordered correctly though!
86492ad0f7e9STom Jones  */
86502ad0f7e9STom Jones struct iwx_umac_error_event_table {
86512ad0f7e9STom Jones 	uint32_t valid;		/* (nonzero) valid, (0) log is empty */
86522ad0f7e9STom Jones 	uint32_t error_id;	/* type of error */
86532ad0f7e9STom Jones 	uint32_t blink1;	/* branch link */
86542ad0f7e9STom Jones 	uint32_t blink2;	/* branch link */
86552ad0f7e9STom Jones 	uint32_t ilink1;	/* interrupt link */
86562ad0f7e9STom Jones 	uint32_t ilink2;	/* interrupt link */
86572ad0f7e9STom Jones 	uint32_t data1;		/* error-specific data */
86582ad0f7e9STom Jones 	uint32_t data2;		/* error-specific data */
86592ad0f7e9STom Jones 	uint32_t data3;		/* error-specific data */
86602ad0f7e9STom Jones 	uint32_t umac_major;
86612ad0f7e9STom Jones 	uint32_t umac_minor;
86622ad0f7e9STom Jones 	uint32_t frame_pointer;	/* core register 27*/
86632ad0f7e9STom Jones 	uint32_t stack_pointer;	/* core register 28 */
86642ad0f7e9STom Jones 	uint32_t cmd_header;	/* latest host cmd sent to UMAC */
86652ad0f7e9STom Jones 	uint32_t nic_isr_pref;	/* ISR status register */
86662ad0f7e9STom Jones } __packed;
86672ad0f7e9STom Jones 
86682ad0f7e9STom Jones #define ERROR_START_OFFSET  (1 * sizeof(uint32_t))
86692ad0f7e9STom Jones #define ERROR_ELEM_SIZE     (7 * sizeof(uint32_t))
86702ad0f7e9STom Jones 
86712ad0f7e9STom Jones static void
86722ad0f7e9STom Jones iwx_nic_umac_error(struct iwx_softc *sc)
86732ad0f7e9STom Jones {
86742ad0f7e9STom Jones 	struct iwx_umac_error_event_table table;
86752ad0f7e9STom Jones 	uint32_t base;
86762ad0f7e9STom Jones 
86772ad0f7e9STom Jones 	base = sc->sc_uc.uc_umac_error_event_table;
86782ad0f7e9STom Jones 
86792ad0f7e9STom Jones 	if (base < 0x400000) {
86802ad0f7e9STom Jones 		printf("%s: Invalid error log pointer 0x%08x\n",
86812ad0f7e9STom Jones 		    DEVNAME(sc), base);
86822ad0f7e9STom Jones 		return;
86832ad0f7e9STom Jones 	}
86842ad0f7e9STom Jones 
86852ad0f7e9STom Jones 	if (iwx_read_mem(sc, base, &table, sizeof(table)/sizeof(uint32_t))) {
86862ad0f7e9STom Jones 		printf("%s: reading errlog failed\n", DEVNAME(sc));
86872ad0f7e9STom Jones 		return;
86882ad0f7e9STom Jones 	}
86892ad0f7e9STom Jones 
86902ad0f7e9STom Jones 	if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
86912ad0f7e9STom Jones 		printf("%s: Start UMAC Error Log Dump:\n", DEVNAME(sc));
86922ad0f7e9STom Jones 		printf("%s: Status: 0x%x, count: %d\n", DEVNAME(sc),
86932ad0f7e9STom Jones 			sc->sc_flags, table.valid);
86942ad0f7e9STom Jones 	}
86952ad0f7e9STom Jones 
86962ad0f7e9STom Jones 	printf("%s: 0x%08X | %s\n", DEVNAME(sc), table.error_id,
86972ad0f7e9STom Jones 		iwx_desc_lookup(table.error_id));
86982ad0f7e9STom Jones 	printf("%s: 0x%08X | umac branchlink1\n", DEVNAME(sc), table.blink1);
86992ad0f7e9STom Jones 	printf("%s: 0x%08X | umac branchlink2\n", DEVNAME(sc), table.blink2);
87002ad0f7e9STom Jones 	printf("%s: 0x%08X | umac interruptlink1\n", DEVNAME(sc), table.ilink1);
87012ad0f7e9STom Jones 	printf("%s: 0x%08X | umac interruptlink2\n", DEVNAME(sc), table.ilink2);
87022ad0f7e9STom Jones 	printf("%s: 0x%08X | umac data1\n", DEVNAME(sc), table.data1);
87032ad0f7e9STom Jones 	printf("%s: 0x%08X | umac data2\n", DEVNAME(sc), table.data2);
87042ad0f7e9STom Jones 	printf("%s: 0x%08X | umac data3\n", DEVNAME(sc), table.data3);
87052ad0f7e9STom Jones 	printf("%s: 0x%08X | umac major\n", DEVNAME(sc), table.umac_major);
87062ad0f7e9STom Jones 	printf("%s: 0x%08X | umac minor\n", DEVNAME(sc), table.umac_minor);
87072ad0f7e9STom Jones 	printf("%s: 0x%08X | frame pointer\n", DEVNAME(sc),
87082ad0f7e9STom Jones 	    table.frame_pointer);
87092ad0f7e9STom Jones 	printf("%s: 0x%08X | stack pointer\n", DEVNAME(sc),
87102ad0f7e9STom Jones 	    table.stack_pointer);
87112ad0f7e9STom Jones 	printf("%s: 0x%08X | last host cmd\n", DEVNAME(sc), table.cmd_header);
87122ad0f7e9STom Jones 	printf("%s: 0x%08X | isr status reg\n", DEVNAME(sc),
87132ad0f7e9STom Jones 	    table.nic_isr_pref);
87142ad0f7e9STom Jones }
87152ad0f7e9STom Jones 
87162ad0f7e9STom Jones #define IWX_FW_SYSASSERT_CPU_MASK 0xf0000000
87172ad0f7e9STom Jones static struct {
87182ad0f7e9STom Jones 	const char *name;
87192ad0f7e9STom Jones 	uint8_t num;
87202ad0f7e9STom Jones } advanced_lookup[] = {
87212ad0f7e9STom Jones 	{ "NMI_INTERRUPT_WDG", 0x34 },
87222ad0f7e9STom Jones 	{ "SYSASSERT", 0x35 },
87232ad0f7e9STom Jones 	{ "UCODE_VERSION_MISMATCH", 0x37 },
87242ad0f7e9STom Jones 	{ "BAD_COMMAND", 0x38 },
87252ad0f7e9STom Jones 	{ "BAD_COMMAND", 0x39 },
87262ad0f7e9STom Jones 	{ "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C },
87272ad0f7e9STom Jones 	{ "FATAL_ERROR", 0x3D },
87282ad0f7e9STom Jones 	{ "NMI_TRM_HW_ERR", 0x46 },
87292ad0f7e9STom Jones 	{ "NMI_INTERRUPT_TRM", 0x4C },
87302ad0f7e9STom Jones 	{ "NMI_INTERRUPT_BREAK_POINT", 0x54 },
87312ad0f7e9STom Jones 	{ "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C },
87322ad0f7e9STom Jones 	{ "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 },
87332ad0f7e9STom Jones 	{ "NMI_INTERRUPT_HOST", 0x66 },
87342ad0f7e9STom Jones 	{ "NMI_INTERRUPT_LMAC_FATAL", 0x70 },
87352ad0f7e9STom Jones 	{ "NMI_INTERRUPT_UMAC_FATAL", 0x71 },
87362ad0f7e9STom Jones 	{ "NMI_INTERRUPT_OTHER_LMAC_FATAL", 0x73 },
87372ad0f7e9STom Jones 	{ "NMI_INTERRUPT_ACTION_PT", 0x7C },
87382ad0f7e9STom Jones 	{ "NMI_INTERRUPT_UNKNOWN", 0x84 },
87392ad0f7e9STom Jones 	{ "NMI_INTERRUPT_INST_ACTION_PT", 0x86 },
87402ad0f7e9STom Jones 	{ "ADVANCED_SYSASSERT", 0 },
87412ad0f7e9STom Jones };
87422ad0f7e9STom Jones 
87432ad0f7e9STom Jones static const char *
87442ad0f7e9STom Jones iwx_desc_lookup(uint32_t num)
87452ad0f7e9STom Jones {
87462ad0f7e9STom Jones 	int i;
87472ad0f7e9STom Jones 
87482ad0f7e9STom Jones 	for (i = 0; i < nitems(advanced_lookup) - 1; i++)
87492ad0f7e9STom Jones 		if (advanced_lookup[i].num ==
87502ad0f7e9STom Jones 		    (num & ~IWX_FW_SYSASSERT_CPU_MASK))
87512ad0f7e9STom Jones 			return advanced_lookup[i].name;
87522ad0f7e9STom Jones 
87532ad0f7e9STom Jones 	/* No entry matches 'num', so it is the last: ADVANCED_SYSASSERT */
87542ad0f7e9STom Jones 	return advanced_lookup[i].name;
87552ad0f7e9STom Jones }
87562ad0f7e9STom Jones 
87572ad0f7e9STom Jones /*
87582ad0f7e9STom Jones  * Support for dumping the error log seemed like a good idea ...
87592ad0f7e9STom Jones  * but it's mostly hex junk and the only sensible thing is the
87602ad0f7e9STom Jones  * hw/ucode revision (which we know anyway).  Since it's here,
87612ad0f7e9STom Jones  * I'll just leave it in, just in case e.g. the Intel guys want to
87622ad0f7e9STom Jones  * help us decipher some "ADVANCED_SYSASSERT" later.
87632ad0f7e9STom Jones  */
87642ad0f7e9STom Jones static void
87652ad0f7e9STom Jones iwx_nic_error(struct iwx_softc *sc)
87662ad0f7e9STom Jones {
87672ad0f7e9STom Jones 	struct iwx_error_event_table table;
87682ad0f7e9STom Jones 	uint32_t base;
87692ad0f7e9STom Jones 
87702ad0f7e9STom Jones 	printf("%s: dumping device error log\n", DEVNAME(sc));
87712ad0f7e9STom Jones 	printf("%s: GOS-3758: 1\n", __func__);
87722ad0f7e9STom Jones 	base = sc->sc_uc.uc_lmac_error_event_table[0];
87732ad0f7e9STom Jones 	printf("%s: GOS-3758: 2\n", __func__);
87742ad0f7e9STom Jones 	if (base < 0x400000) {
87752ad0f7e9STom Jones 		printf("%s: Invalid error log pointer 0x%08x\n",
87762ad0f7e9STom Jones 		    DEVNAME(sc), base);
87772ad0f7e9STom Jones 		return;
87782ad0f7e9STom Jones 	}
87792ad0f7e9STom Jones 
87802ad0f7e9STom Jones 	printf("%s: GOS-3758: 3\n", __func__);
87812ad0f7e9STom Jones 	if (iwx_read_mem(sc, base, &table, sizeof(table)/sizeof(uint32_t))) {
87822ad0f7e9STom Jones 		printf("%s: reading errlog failed\n", DEVNAME(sc));
87832ad0f7e9STom Jones 		return;
87842ad0f7e9STom Jones 	}
87852ad0f7e9STom Jones 
87862ad0f7e9STom Jones 	printf("%s: GOS-3758: 4\n", __func__);
87872ad0f7e9STom Jones 	if (!table.valid) {
87882ad0f7e9STom Jones 		printf("%s: errlog not found, skipping\n", DEVNAME(sc));
87892ad0f7e9STom Jones 		return;
87902ad0f7e9STom Jones 	}
87912ad0f7e9STom Jones 
87922ad0f7e9STom Jones 	printf("%s: GOS-3758: 5\n", __func__);
87932ad0f7e9STom Jones 	if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
87942ad0f7e9STom Jones 		printf("%s: Start Error Log Dump:\n", DEVNAME(sc));
87952ad0f7e9STom Jones 		printf("%s: Status: 0x%x, count: %d\n", DEVNAME(sc),
87962ad0f7e9STom Jones 		    sc->sc_flags, table.valid);
87972ad0f7e9STom Jones 	}
87982ad0f7e9STom Jones 
87992ad0f7e9STom Jones 	printf("%s: GOS-3758: 6\n", __func__);
88002ad0f7e9STom Jones 	printf("%s: 0x%08X | %-28s\n", DEVNAME(sc), table.error_id,
88012ad0f7e9STom Jones 	    iwx_desc_lookup(table.error_id));
88022ad0f7e9STom Jones 	printf("%s: %08X | trm_hw_status0\n", DEVNAME(sc),
88032ad0f7e9STom Jones 	    table.trm_hw_status0);
88042ad0f7e9STom Jones 	printf("%s: %08X | trm_hw_status1\n", DEVNAME(sc),
88052ad0f7e9STom Jones 	    table.trm_hw_status1);
88062ad0f7e9STom Jones 	printf("%s: %08X | branchlink2\n", DEVNAME(sc), table.blink2);
88072ad0f7e9STom Jones 	printf("%s: %08X | interruptlink1\n", DEVNAME(sc), table.ilink1);
88082ad0f7e9STom Jones 	printf("%s: %08X | interruptlink2\n", DEVNAME(sc), table.ilink2);
88092ad0f7e9STom Jones 	printf("%s: %08X | data1\n", DEVNAME(sc), table.data1);
88102ad0f7e9STom Jones 	printf("%s: %08X | data2\n", DEVNAME(sc), table.data2);
88112ad0f7e9STom Jones 	printf("%s: %08X | data3\n", DEVNAME(sc), table.data3);
88122ad0f7e9STom Jones 	printf("%s: %08X | beacon time\n", DEVNAME(sc), table.bcon_time);
88132ad0f7e9STom Jones 	printf("%s: %08X | tsf low\n", DEVNAME(sc), table.tsf_low);
88142ad0f7e9STom Jones 	printf("%s: %08X | tsf hi\n", DEVNAME(sc), table.tsf_hi);
88152ad0f7e9STom Jones 	printf("%s: %08X | time gp1\n", DEVNAME(sc), table.gp1);
88162ad0f7e9STom Jones 	printf("%s: %08X | time gp2\n", DEVNAME(sc), table.gp2);
88172ad0f7e9STom Jones 	printf("%s: %08X | uCode revision type\n", DEVNAME(sc),
88182ad0f7e9STom Jones 	    table.fw_rev_type);
88192ad0f7e9STom Jones 	printf("%s: %08X | uCode version major\n", DEVNAME(sc),
88202ad0f7e9STom Jones 	    table.major);
88212ad0f7e9STom Jones 	printf("%s: %08X | uCode version minor\n", DEVNAME(sc),
88222ad0f7e9STom Jones 	    table.minor);
88232ad0f7e9STom Jones 	printf("%s: %08X | hw version\n", DEVNAME(sc), table.hw_ver);
88242ad0f7e9STom Jones 	printf("%s: %08X | board version\n", DEVNAME(sc), table.brd_ver);
88252ad0f7e9STom Jones 	printf("%s: %08X | hcmd\n", DEVNAME(sc), table.hcmd);
88262ad0f7e9STom Jones 	printf("%s: %08X | isr0\n", DEVNAME(sc), table.isr0);
88272ad0f7e9STom Jones 	printf("%s: %08X | isr1\n", DEVNAME(sc), table.isr1);
88282ad0f7e9STom Jones 	printf("%s: %08X | isr2\n", DEVNAME(sc), table.isr2);
88292ad0f7e9STom Jones 	printf("%s: %08X | isr3\n", DEVNAME(sc), table.isr3);
88302ad0f7e9STom Jones 	printf("%s: %08X | isr4\n", DEVNAME(sc), table.isr4);
88312ad0f7e9STom Jones 	printf("%s: %08X | last cmd Id\n", DEVNAME(sc), table.last_cmd_id);
88322ad0f7e9STom Jones 	printf("%s: %08X | wait_event\n", DEVNAME(sc), table.wait_event);
88332ad0f7e9STom Jones 	printf("%s: %08X | l2p_control\n", DEVNAME(sc), table.l2p_control);
88342ad0f7e9STom Jones 	printf("%s: %08X | l2p_duration\n", DEVNAME(sc), table.l2p_duration);
88352ad0f7e9STom Jones 	printf("%s: %08X | l2p_mhvalid\n", DEVNAME(sc), table.l2p_mhvalid);
88362ad0f7e9STom Jones 	printf("%s: %08X | l2p_addr_match\n", DEVNAME(sc), table.l2p_addr_match);
88372ad0f7e9STom Jones 	printf("%s: %08X | lmpm_pmg_sel\n", DEVNAME(sc), table.lmpm_pmg_sel);
88382ad0f7e9STom Jones 	printf("%s: %08X | timestamp\n", DEVNAME(sc), table.u_timestamp);
88392ad0f7e9STom Jones 	printf("%s: %08X | flow_handler\n", DEVNAME(sc), table.flow_handler);
88402ad0f7e9STom Jones 
88412ad0f7e9STom Jones 	if (sc->sc_uc.uc_umac_error_event_table)
88422ad0f7e9STom Jones 		iwx_nic_umac_error(sc);
88432ad0f7e9STom Jones }
88442ad0f7e9STom Jones 
88452ad0f7e9STom Jones static void
88462ad0f7e9STom Jones iwx_dump_driver_status(struct iwx_softc *sc)
88472ad0f7e9STom Jones {
88482ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
88492ad0f7e9STom Jones 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
88502ad0f7e9STom Jones 	enum ieee80211_state state = vap->iv_state;
88512ad0f7e9STom Jones 	int i;
88522ad0f7e9STom Jones 
88532ad0f7e9STom Jones 	printf("driver status:\n");
88542ad0f7e9STom Jones 	for (i = 0; i < nitems(sc->txq); i++) {
88552ad0f7e9STom Jones 		struct iwx_tx_ring *ring = &sc->txq[i];
88562ad0f7e9STom Jones 		printf("  tx ring %2d: qid=%-2d cur=%-3d "
88572ad0f7e9STom Jones 		    "cur_hw=%-3d queued=%-3d\n",
88582ad0f7e9STom Jones 		    i, ring->qid, ring->cur, ring->cur_hw,
88592ad0f7e9STom Jones 		    ring->queued);
88602ad0f7e9STom Jones 	}
88612ad0f7e9STom Jones 	printf("  rx ring: cur=%d\n", sc->rxq.cur);
88622ad0f7e9STom Jones 	printf("  802.11 state %s\n", ieee80211_state_name[state]);
88632ad0f7e9STom Jones }
88642ad0f7e9STom Jones 
88652ad0f7e9STom Jones #define SYNC_RESP_STRUCT(_var_, _pkt_)					\
88662ad0f7e9STom Jones do {									\
88672ad0f7e9STom Jones 	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); \
88682ad0f7e9STom Jones 	_var_ = (void *)((_pkt_)+1);					\
88692ad0f7e9STom Jones } while (/*CONSTCOND*/0)
88702ad0f7e9STom Jones 
88712ad0f7e9STom Jones static int
88722ad0f7e9STom Jones iwx_rx_pkt_valid(struct iwx_rx_packet *pkt)
88732ad0f7e9STom Jones {
88742ad0f7e9STom Jones 	int qid, idx, code;
88752ad0f7e9STom Jones 
88762ad0f7e9STom Jones 	qid = pkt->hdr.qid & ~0x80;
88772ad0f7e9STom Jones 	idx = pkt->hdr.idx;
88782ad0f7e9STom Jones 	code = IWX_WIDE_ID(pkt->hdr.flags, pkt->hdr.code);
88792ad0f7e9STom Jones 
88802ad0f7e9STom Jones 	return (!(qid == 0 && idx == 0 && code == 0) &&
88812ad0f7e9STom Jones 	    pkt->len_n_flags != htole32(IWX_FH_RSCSR_FRAME_INVALID));
88822ad0f7e9STom Jones }
88832ad0f7e9STom Jones 
88842ad0f7e9STom Jones static void
88852ad0f7e9STom Jones iwx_rx_pkt(struct iwx_softc *sc, struct iwx_rx_data *data, struct mbuf *ml)
88862ad0f7e9STom Jones {
88872ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
88882ad0f7e9STom Jones 	struct iwx_rx_packet *pkt, *nextpkt;
88892ad0f7e9STom Jones 	uint32_t offset = 0, nextoff = 0, nmpdu = 0, len;
88902ad0f7e9STom Jones 	struct mbuf *m0, *m;
88912ad0f7e9STom Jones 	const size_t minsz = sizeof(pkt->len_n_flags) + sizeof(pkt->hdr);
88922ad0f7e9STom Jones 	int qid, idx, code, handled = 1;
88932ad0f7e9STom Jones 
88942ad0f7e9STom Jones 	m0 = data->m;
88952ad0f7e9STom Jones 	while (m0 && offset + minsz < IWX_RBUF_SIZE) {
88962ad0f7e9STom Jones 		pkt = (struct iwx_rx_packet *)(m0->m_data + offset);
88972ad0f7e9STom Jones 		qid = pkt->hdr.qid;
88982ad0f7e9STom Jones 		idx = pkt->hdr.idx;
88992ad0f7e9STom Jones 		code = IWX_WIDE_ID(pkt->hdr.flags, pkt->hdr.code);
89002ad0f7e9STom Jones 
89012ad0f7e9STom Jones 		if (!iwx_rx_pkt_valid(pkt))
89022ad0f7e9STom Jones 			break;
890371baab40STom Jones 
89042ad0f7e9STom Jones 		/*
89052ad0f7e9STom Jones 		 * XXX Intel inside (tm)
89062ad0f7e9STom Jones 		 * Any commands in the LONG_GROUP could actually be in the
89072ad0f7e9STom Jones 		 * LEGACY group. Firmware API versions >= 50 reject commands
89082ad0f7e9STom Jones 		 * in group 0, forcing us to use this hack.
89092ad0f7e9STom Jones 		 */
89102ad0f7e9STom Jones 		if (iwx_cmd_groupid(code) == IWX_LONG_GROUP) {
89112ad0f7e9STom Jones 			struct iwx_tx_ring *ring = &sc->txq[qid];
89122ad0f7e9STom Jones 			struct iwx_tx_data *txdata = &ring->data[idx];
89132ad0f7e9STom Jones 			if (txdata->flags & IWX_TXDATA_FLAG_CMD_IS_NARROW)
89142ad0f7e9STom Jones 				code = iwx_cmd_opcode(code);
89152ad0f7e9STom Jones 		}
89162ad0f7e9STom Jones 
89172ad0f7e9STom Jones 		len = sizeof(pkt->len_n_flags) + iwx_rx_packet_len(pkt);
89182ad0f7e9STom Jones 		if (len < minsz || len > (IWX_RBUF_SIZE - offset))
89192ad0f7e9STom Jones 			break;
89202ad0f7e9STom Jones 
89212ad0f7e9STom Jones 		// TODO ???
89222ad0f7e9STom Jones 		if (code == IWX_REPLY_RX_MPDU_CMD && ++nmpdu == 1) {
89232ad0f7e9STom Jones 			/* Take mbuf m0 off the RX ring. */
89242ad0f7e9STom Jones 			if (iwx_rx_addbuf(sc, IWX_RBUF_SIZE, sc->rxq.cur)) {
89252ad0f7e9STom Jones 				break;
89262ad0f7e9STom Jones 			}
89272ad0f7e9STom Jones 			KASSERT((data->m != m0), ("%s: data->m != m0", __func__));
89282ad0f7e9STom Jones 		}
89292ad0f7e9STom Jones 
89302ad0f7e9STom Jones 		switch (code) {
89312ad0f7e9STom Jones 		case IWX_REPLY_RX_PHY_CMD:
89322ad0f7e9STom Jones 			/* XXX-THJ: I've not managed to hit this path in testing */
89332ad0f7e9STom Jones 			iwx_rx_rx_phy_cmd(sc, pkt, data);
89342ad0f7e9STom Jones 			break;
89352ad0f7e9STom Jones 
89362ad0f7e9STom Jones 		case IWX_REPLY_RX_MPDU_CMD: {
89372ad0f7e9STom Jones 			size_t maxlen = IWX_RBUF_SIZE - offset - minsz;
89382ad0f7e9STom Jones 			nextoff = offset +
89392ad0f7e9STom Jones 			    roundup(len, IWX_FH_RSCSR_FRAME_ALIGN);
89402ad0f7e9STom Jones 			nextpkt = (struct iwx_rx_packet *)
89412ad0f7e9STom Jones 			    (m0->m_data + nextoff);
89422ad0f7e9STom Jones 			/* AX210 devices ship only one packet per Rx buffer. */
89432ad0f7e9STom Jones 			if (sc->sc_device_family >= IWX_DEVICE_FAMILY_AX210 ||
89442ad0f7e9STom Jones 			    nextoff + minsz >= IWX_RBUF_SIZE ||
89452ad0f7e9STom Jones 			    !iwx_rx_pkt_valid(nextpkt)) {
89462ad0f7e9STom Jones 				/* No need to copy last frame in buffer. */
89472ad0f7e9STom Jones 				if (offset > 0)
89482ad0f7e9STom Jones 					m_adj(m0, offset);
89492ad0f7e9STom Jones 				iwx_rx_mpdu_mq(sc, m0, pkt->data, maxlen);
89502ad0f7e9STom Jones 				m0 = NULL; /* stack owns m0 now; abort loop */
89512ad0f7e9STom Jones 			} else {
89522ad0f7e9STom Jones 				/*
89532ad0f7e9STom Jones 				 * Create an mbuf which points to the current
89542ad0f7e9STom Jones 				 * packet. Always copy from offset zero to
89552ad0f7e9STom Jones 				 * preserve m_pkthdr.
89562ad0f7e9STom Jones 				 */
89572ad0f7e9STom Jones 				m = m_copym(m0, 0, M_COPYALL, M_NOWAIT);
89582ad0f7e9STom Jones 				if (m == NULL) {
89592ad0f7e9STom Jones 					m_freem(m0);
89602ad0f7e9STom Jones 					m0 = NULL;
89612ad0f7e9STom Jones 					break;
89622ad0f7e9STom Jones 				}
89632ad0f7e9STom Jones 				m_adj(m, offset);
89642ad0f7e9STom Jones 				iwx_rx_mpdu_mq(sc, m, pkt->data, maxlen);
89652ad0f7e9STom Jones 			}
89662ad0f7e9STom Jones 			break;
89672ad0f7e9STom Jones 		}
89682ad0f7e9STom Jones 
89692ad0f7e9STom Jones //		case IWX_BAR_FRAME_RELEASE:
89702ad0f7e9STom Jones //			iwx_rx_bar_frame_release(sc, pkt, ml);
89712ad0f7e9STom Jones //			break;
89722ad0f7e9STom Jones //
89732ad0f7e9STom Jones 		case IWX_TX_CMD:
89742ad0f7e9STom Jones 			iwx_rx_tx_cmd(sc, pkt, data);
89752ad0f7e9STom Jones 			break;
89762ad0f7e9STom Jones 
89772ad0f7e9STom Jones 		case IWX_BA_NOTIF:
89782ad0f7e9STom Jones 			iwx_rx_compressed_ba(sc, pkt);
89792ad0f7e9STom Jones 			break;
89802ad0f7e9STom Jones 
89812ad0f7e9STom Jones 		case IWX_MISSED_BEACONS_NOTIFICATION:
89822ad0f7e9STom Jones 			iwx_rx_bmiss(sc, pkt, data);
89832ad0f7e9STom Jones 			DPRINTF(("%s: IWX_MISSED_BEACONS_NOTIFICATION\n",
89842ad0f7e9STom Jones 			    __func__));
89852ad0f7e9STom Jones 			ieee80211_beacon_miss(ic);
89862ad0f7e9STom Jones 			break;
89872ad0f7e9STom Jones 
89882ad0f7e9STom Jones 		case IWX_MFUART_LOAD_NOTIFICATION:
89892ad0f7e9STom Jones 			break;
89902ad0f7e9STom Jones 
89912ad0f7e9STom Jones 		case IWX_ALIVE: {
89922ad0f7e9STom Jones 			struct iwx_alive_resp_v4 *resp4;
89932ad0f7e9STom Jones 			struct iwx_alive_resp_v5 *resp5;
89942ad0f7e9STom Jones 			struct iwx_alive_resp_v6 *resp6;
89952ad0f7e9STom Jones 
89962ad0f7e9STom Jones 			DPRINTF(("%s: firmware alive\n", __func__));
89972ad0f7e9STom Jones 			sc->sc_uc.uc_ok = 0;
89982ad0f7e9STom Jones 
89992ad0f7e9STom Jones 			/*
90002ad0f7e9STom Jones 			 * For v5 and above, we can check the version, for older
90012ad0f7e9STom Jones 			 * versions we need to check the size.
90022ad0f7e9STom Jones 			 */
90032ad0f7e9STom Jones 			 if (iwx_lookup_notif_ver(sc, IWX_LEGACY_GROUP,
90042ad0f7e9STom Jones 			    IWX_ALIVE) == 6) {
90052ad0f7e9STom Jones 				SYNC_RESP_STRUCT(resp6, pkt);
90062ad0f7e9STom Jones 				if (iwx_rx_packet_payload_len(pkt) !=
90072ad0f7e9STom Jones 				    sizeof(*resp6)) {
90082ad0f7e9STom Jones 					sc->sc_uc.uc_intr = 1;
90092ad0f7e9STom Jones 					wakeup(&sc->sc_uc);
90102ad0f7e9STom Jones 					break;
90112ad0f7e9STom Jones 				}
90122ad0f7e9STom Jones 				sc->sc_uc.uc_lmac_error_event_table[0] = le32toh(
90132ad0f7e9STom Jones 				    resp6->lmac_data[0].dbg_ptrs.error_event_table_ptr);
90142ad0f7e9STom Jones 				sc->sc_uc.uc_lmac_error_event_table[1] = le32toh(
90152ad0f7e9STom Jones 				    resp6->lmac_data[1].dbg_ptrs.error_event_table_ptr);
90162ad0f7e9STom Jones 				sc->sc_uc.uc_log_event_table = le32toh(
90172ad0f7e9STom Jones 				    resp6->lmac_data[0].dbg_ptrs.log_event_table_ptr);
90182ad0f7e9STom Jones 				sc->sc_uc.uc_umac_error_event_table = le32toh(
90192ad0f7e9STom Jones 				    resp6->umac_data.dbg_ptrs.error_info_addr);
90202ad0f7e9STom Jones 				sc->sc_sku_id[0] =
90212ad0f7e9STom Jones 				    le32toh(resp6->sku_id.data[0]);
90222ad0f7e9STom Jones 				sc->sc_sku_id[1] =
90232ad0f7e9STom Jones 				    le32toh(resp6->sku_id.data[1]);
90242ad0f7e9STom Jones 				sc->sc_sku_id[2] =
90252ad0f7e9STom Jones 				    le32toh(resp6->sku_id.data[2]);
90262ad0f7e9STom Jones 				if (resp6->status == IWX_ALIVE_STATUS_OK) {
90272ad0f7e9STom Jones 					sc->sc_uc.uc_ok = 1;
90282ad0f7e9STom Jones 				}
90292ad0f7e9STom Jones 			 } else if (iwx_lookup_notif_ver(sc, IWX_LEGACY_GROUP,
90302ad0f7e9STom Jones 			    IWX_ALIVE) == 5) {
90312ad0f7e9STom Jones 				SYNC_RESP_STRUCT(resp5, pkt);
90322ad0f7e9STom Jones 				if (iwx_rx_packet_payload_len(pkt) !=
90332ad0f7e9STom Jones 				    sizeof(*resp5)) {
90342ad0f7e9STom Jones 					sc->sc_uc.uc_intr = 1;
90352ad0f7e9STom Jones 					wakeup(&sc->sc_uc);
90362ad0f7e9STom Jones 					break;
90372ad0f7e9STom Jones 				}
90382ad0f7e9STom Jones 				sc->sc_uc.uc_lmac_error_event_table[0] = le32toh(
90392ad0f7e9STom Jones 				    resp5->lmac_data[0].dbg_ptrs.error_event_table_ptr);
90402ad0f7e9STom Jones 				sc->sc_uc.uc_lmac_error_event_table[1] = le32toh(
90412ad0f7e9STom Jones 				    resp5->lmac_data[1].dbg_ptrs.error_event_table_ptr);
90422ad0f7e9STom Jones 				sc->sc_uc.uc_log_event_table = le32toh(
90432ad0f7e9STom Jones 				    resp5->lmac_data[0].dbg_ptrs.log_event_table_ptr);
90442ad0f7e9STom Jones 				sc->sc_uc.uc_umac_error_event_table = le32toh(
90452ad0f7e9STom Jones 				    resp5->umac_data.dbg_ptrs.error_info_addr);
90462ad0f7e9STom Jones 				sc->sc_sku_id[0] =
90472ad0f7e9STom Jones 				    le32toh(resp5->sku_id.data[0]);
90482ad0f7e9STom Jones 				sc->sc_sku_id[1] =
90492ad0f7e9STom Jones 				    le32toh(resp5->sku_id.data[1]);
90502ad0f7e9STom Jones 				sc->sc_sku_id[2] =
90512ad0f7e9STom Jones 				    le32toh(resp5->sku_id.data[2]);
90522ad0f7e9STom Jones 				if (resp5->status == IWX_ALIVE_STATUS_OK)
90532ad0f7e9STom Jones 					sc->sc_uc.uc_ok = 1;
90542ad0f7e9STom Jones 			} else if (iwx_rx_packet_payload_len(pkt) == sizeof(*resp4)) {
90552ad0f7e9STom Jones 				SYNC_RESP_STRUCT(resp4, pkt);
90562ad0f7e9STom Jones 				sc->sc_uc.uc_lmac_error_event_table[0] = le32toh(
90572ad0f7e9STom Jones 				    resp4->lmac_data[0].dbg_ptrs.error_event_table_ptr);
90582ad0f7e9STom Jones 				sc->sc_uc.uc_lmac_error_event_table[1] = le32toh(
90592ad0f7e9STom Jones 				    resp4->lmac_data[1].dbg_ptrs.error_event_table_ptr);
90602ad0f7e9STom Jones 				sc->sc_uc.uc_log_event_table = le32toh(
90612ad0f7e9STom Jones 				    resp4->lmac_data[0].dbg_ptrs.log_event_table_ptr);
90622ad0f7e9STom Jones 				sc->sc_uc.uc_umac_error_event_table = le32toh(
90632ad0f7e9STom Jones 				    resp4->umac_data.dbg_ptrs.error_info_addr);
90642ad0f7e9STom Jones 				if (resp4->status == IWX_ALIVE_STATUS_OK)
90652ad0f7e9STom Jones 					sc->sc_uc.uc_ok = 1;
90662ad0f7e9STom Jones 			} else
90672ad0f7e9STom Jones 				printf("unknown payload version");
90682ad0f7e9STom Jones 
90692ad0f7e9STom Jones 			sc->sc_uc.uc_intr = 1;
90702ad0f7e9STom Jones 			wakeup(&sc->sc_uc);
90712ad0f7e9STom Jones 			break;
90722ad0f7e9STom Jones 		}
90732ad0f7e9STom Jones 
90742ad0f7e9STom Jones 		case IWX_STATISTICS_NOTIFICATION: {
90752ad0f7e9STom Jones 			struct iwx_notif_statistics *stats;
90762ad0f7e9STom Jones 			SYNC_RESP_STRUCT(stats, pkt);
90772ad0f7e9STom Jones 			memcpy(&sc->sc_stats, stats, sizeof(sc->sc_stats));
90782ad0f7e9STom Jones 			sc->sc_noise = iwx_get_noise(&stats->rx.general);
90792ad0f7e9STom Jones 			break;
90802ad0f7e9STom Jones 		}
90812ad0f7e9STom Jones 
90822ad0f7e9STom Jones 		case IWX_DTS_MEASUREMENT_NOTIFICATION:
90832ad0f7e9STom Jones 		case IWX_WIDE_ID(IWX_PHY_OPS_GROUP,
90842ad0f7e9STom Jones 				 IWX_DTS_MEASUREMENT_NOTIF_WIDE):
90852ad0f7e9STom Jones 		case IWX_WIDE_ID(IWX_PHY_OPS_GROUP,
90862ad0f7e9STom Jones 				 IWX_TEMP_REPORTING_THRESHOLDS_CMD):
90872ad0f7e9STom Jones 			break;
90882ad0f7e9STom Jones 
90892ad0f7e9STom Jones 		case IWX_WIDE_ID(IWX_PHY_OPS_GROUP,
90902ad0f7e9STom Jones 		    IWX_CT_KILL_NOTIFICATION): {
90912ad0f7e9STom Jones 			struct iwx_ct_kill_notif *notif;
90922ad0f7e9STom Jones 			SYNC_RESP_STRUCT(notif, pkt);
90932ad0f7e9STom Jones 			printf("%s: device at critical temperature (%u degC), "
90942ad0f7e9STom Jones 			    "stopping device\n",
90952ad0f7e9STom Jones 			    DEVNAME(sc), le16toh(notif->temperature));
90962ad0f7e9STom Jones 			sc->sc_flags |= IWX_FLAG_HW_ERR;
90972ad0f7e9STom Jones 			ieee80211_restart_all(ic);
90982ad0f7e9STom Jones 			break;
90992ad0f7e9STom Jones 		}
91002ad0f7e9STom Jones 
91012ad0f7e9STom Jones 		case IWX_WIDE_ID(IWX_DATA_PATH_GROUP,
91022ad0f7e9STom Jones 		    IWX_SCD_QUEUE_CONFIG_CMD):
91032ad0f7e9STom Jones 		case IWX_WIDE_ID(IWX_DATA_PATH_GROUP,
91042ad0f7e9STom Jones 		    IWX_RX_BAID_ALLOCATION_CONFIG_CMD):
91052ad0f7e9STom Jones 		case IWX_WIDE_ID(IWX_MAC_CONF_GROUP,
91062ad0f7e9STom Jones 		    IWX_SESSION_PROTECTION_CMD):
91072ad0f7e9STom Jones 		case IWX_WIDE_ID(IWX_REGULATORY_AND_NVM_GROUP,
91082ad0f7e9STom Jones 		    IWX_NVM_GET_INFO):
91092ad0f7e9STom Jones 		case IWX_ADD_STA_KEY:
91102ad0f7e9STom Jones 		case IWX_PHY_CONFIGURATION_CMD:
91112ad0f7e9STom Jones 		case IWX_TX_ANT_CONFIGURATION_CMD:
91122ad0f7e9STom Jones 		case IWX_ADD_STA:
91132ad0f7e9STom Jones 		case IWX_MAC_CONTEXT_CMD:
91142ad0f7e9STom Jones 		case IWX_REPLY_SF_CFG_CMD:
91152ad0f7e9STom Jones 		case IWX_POWER_TABLE_CMD:
91162ad0f7e9STom Jones 		case IWX_LTR_CONFIG:
91172ad0f7e9STom Jones 		case IWX_PHY_CONTEXT_CMD:
91182ad0f7e9STom Jones 		case IWX_BINDING_CONTEXT_CMD:
91192ad0f7e9STom Jones 		case IWX_WIDE_ID(IWX_LONG_GROUP, IWX_SCAN_CFG_CMD):
91202ad0f7e9STom Jones 		case IWX_WIDE_ID(IWX_LONG_GROUP, IWX_SCAN_REQ_UMAC):
91212ad0f7e9STom Jones 		case IWX_WIDE_ID(IWX_LONG_GROUP, IWX_SCAN_ABORT_UMAC):
91222ad0f7e9STom Jones 		case IWX_REPLY_BEACON_FILTERING_CMD:
91232ad0f7e9STom Jones 		case IWX_MAC_PM_POWER_TABLE:
91242ad0f7e9STom Jones 		case IWX_TIME_QUOTA_CMD:
91252ad0f7e9STom Jones 		case IWX_REMOVE_STA:
91262ad0f7e9STom Jones 		case IWX_TXPATH_FLUSH:
91272ad0f7e9STom Jones 		case IWX_BT_CONFIG:
91282ad0f7e9STom Jones 		case IWX_MCC_UPDATE_CMD:
91292ad0f7e9STom Jones 		case IWX_TIME_EVENT_CMD:
91302ad0f7e9STom Jones 		case IWX_STATISTICS_CMD:
91312ad0f7e9STom Jones 		case IWX_SCD_QUEUE_CFG: {
91322ad0f7e9STom Jones 			size_t pkt_len;
91332ad0f7e9STom Jones 
91342ad0f7e9STom Jones 			if (sc->sc_cmd_resp_pkt[idx] == NULL)
91352ad0f7e9STom Jones 				break;
91362ad0f7e9STom Jones 
91372ad0f7e9STom Jones 			bus_dmamap_sync(sc->rxq.data_dmat, data->map,
91382ad0f7e9STom Jones 			    BUS_DMASYNC_POSTREAD);
91392ad0f7e9STom Jones 
91402ad0f7e9STom Jones 			pkt_len = sizeof(pkt->len_n_flags) +
91412ad0f7e9STom Jones 			    iwx_rx_packet_len(pkt);
91422ad0f7e9STom Jones 
91432ad0f7e9STom Jones 			if ((pkt->hdr.flags & IWX_CMD_FAILED_MSK) ||
91442ad0f7e9STom Jones 			    pkt_len < sizeof(*pkt) ||
91452ad0f7e9STom Jones 			    pkt_len > sc->sc_cmd_resp_len[idx]) {
91462ad0f7e9STom Jones 				free(sc->sc_cmd_resp_pkt[idx], M_DEVBUF);
91472ad0f7e9STom Jones 				sc->sc_cmd_resp_pkt[idx] = NULL;
91482ad0f7e9STom Jones 				break;
91492ad0f7e9STom Jones 			}
91502ad0f7e9STom Jones 
91512ad0f7e9STom Jones 			bus_dmamap_sync(sc->rxq.data_dmat, data->map,
91522ad0f7e9STom Jones 			    BUS_DMASYNC_POSTREAD);
91532ad0f7e9STom Jones 			memcpy(sc->sc_cmd_resp_pkt[idx], pkt, pkt_len);
91542ad0f7e9STom Jones 			break;
91552ad0f7e9STom Jones 		}
91562ad0f7e9STom Jones 
91572ad0f7e9STom Jones 		case IWX_INIT_COMPLETE_NOTIF:
91582ad0f7e9STom Jones 			sc->sc_init_complete |= IWX_INIT_COMPLETE;
91592ad0f7e9STom Jones 			wakeup(&sc->sc_init_complete);
91602ad0f7e9STom Jones 			break;
91612ad0f7e9STom Jones 
91622ad0f7e9STom Jones 		case IWX_SCAN_COMPLETE_UMAC: {
91632ad0f7e9STom Jones 			DPRINTF(("%s: >>> IWX_SCAN_COMPLETE_UMAC\n", __func__));
91642ad0f7e9STom Jones 			struct iwx_umac_scan_complete *notif __attribute__((unused));
91652ad0f7e9STom Jones 			SYNC_RESP_STRUCT(notif, pkt);
91662ad0f7e9STom Jones 			DPRINTF(("%s: scan complete notif->status=%d\n", __func__,
91672ad0f7e9STom Jones 			    notif->status));
91682ad0f7e9STom Jones 			ieee80211_runtask(&sc->sc_ic, &sc->sc_es_task);
91692ad0f7e9STom Jones 			iwx_endscan(sc);
91702ad0f7e9STom Jones 			break;
91712ad0f7e9STom Jones 		}
91722ad0f7e9STom Jones 
91732ad0f7e9STom Jones 		case IWX_SCAN_ITERATION_COMPLETE_UMAC: {
91742ad0f7e9STom Jones 			DPRINTF(("%s: >>> IWX_SCAN_ITERATION_COMPLETE_UMAC\n",
91752ad0f7e9STom Jones 			    __func__));
91762ad0f7e9STom Jones 			struct iwx_umac_scan_iter_complete_notif *notif __attribute__((unused));
91772ad0f7e9STom Jones 			SYNC_RESP_STRUCT(notif, pkt);
91782ad0f7e9STom Jones 			DPRINTF(("%s: iter scan complete notif->status=%d\n", __func__,
91792ad0f7e9STom Jones 			    notif->status));
91802ad0f7e9STom Jones 			iwx_endscan(sc);
91812ad0f7e9STom Jones 			break;
91822ad0f7e9STom Jones 		}
91832ad0f7e9STom Jones 
91842ad0f7e9STom Jones 		case IWX_MCC_CHUB_UPDATE_CMD: {
91852ad0f7e9STom Jones 			struct iwx_mcc_chub_notif *notif;
91862ad0f7e9STom Jones 			SYNC_RESP_STRUCT(notif, pkt);
91872ad0f7e9STom Jones 			iwx_mcc_update(sc, notif);
91882ad0f7e9STom Jones 			break;
91892ad0f7e9STom Jones 		}
91902ad0f7e9STom Jones 
91912ad0f7e9STom Jones 		case IWX_REPLY_ERROR: {
91922ad0f7e9STom Jones 			struct iwx_error_resp *resp;
91932ad0f7e9STom Jones 			SYNC_RESP_STRUCT(resp, pkt);
91942ad0f7e9STom Jones 			printf("%s: firmware error 0x%x, cmd 0x%x\n",
91952ad0f7e9STom Jones 				DEVNAME(sc), le32toh(resp->error_type),
91962ad0f7e9STom Jones 				resp->cmd_id);
91972ad0f7e9STom Jones 			break;
91982ad0f7e9STom Jones 		}
91992ad0f7e9STom Jones 
92002ad0f7e9STom Jones 		case IWX_TIME_EVENT_NOTIFICATION: {
92012ad0f7e9STom Jones 			struct iwx_time_event_notif *notif;
92022ad0f7e9STom Jones 			uint32_t action;
92032ad0f7e9STom Jones 			SYNC_RESP_STRUCT(notif, pkt);
92042ad0f7e9STom Jones 
92052ad0f7e9STom Jones 			if (sc->sc_time_event_uid != le32toh(notif->unique_id))
92062ad0f7e9STom Jones 				break;
92072ad0f7e9STom Jones 			action = le32toh(notif->action);
92082ad0f7e9STom Jones 			if (action & IWX_TE_V2_NOTIF_HOST_EVENT_END)
92092ad0f7e9STom Jones 				sc->sc_flags &= ~IWX_FLAG_TE_ACTIVE;
92102ad0f7e9STom Jones 			break;
92112ad0f7e9STom Jones 		}
92122ad0f7e9STom Jones 
92132ad0f7e9STom Jones 		case IWX_WIDE_ID(IWX_MAC_CONF_GROUP,
92142ad0f7e9STom Jones 		    IWX_SESSION_PROTECTION_NOTIF): {
92152ad0f7e9STom Jones 			struct iwx_session_prot_notif *notif;
92162ad0f7e9STom Jones 			uint32_t status, start, conf_id;
92172ad0f7e9STom Jones 
92182ad0f7e9STom Jones 			SYNC_RESP_STRUCT(notif, pkt);
92192ad0f7e9STom Jones 
92202ad0f7e9STom Jones 			status = le32toh(notif->status);
92212ad0f7e9STom Jones 			start = le32toh(notif->start);
92222ad0f7e9STom Jones 			conf_id = le32toh(notif->conf_id);
92232ad0f7e9STom Jones 			/* Check for end of successful PROTECT_CONF_ASSOC. */
92242ad0f7e9STom Jones 			if (status == 1 && start == 0 &&
92252ad0f7e9STom Jones 			    conf_id == IWX_SESSION_PROTECT_CONF_ASSOC)
92262ad0f7e9STom Jones 				sc->sc_flags &= ~IWX_FLAG_TE_ACTIVE;
92272ad0f7e9STom Jones 			break;
92282ad0f7e9STom Jones 		}
92292ad0f7e9STom Jones 
92302ad0f7e9STom Jones 		case IWX_WIDE_ID(IWX_SYSTEM_GROUP,
92312ad0f7e9STom Jones 		    IWX_FSEQ_VER_MISMATCH_NOTIFICATION):
92322ad0f7e9STom Jones 		    break;
92332ad0f7e9STom Jones 
92342ad0f7e9STom Jones 		/*
92352ad0f7e9STom Jones 		 * Firmware versions 21 and 22 generate some DEBUG_LOG_MSG
92362ad0f7e9STom Jones 		 * messages. Just ignore them for now.
92372ad0f7e9STom Jones 		 */
92382ad0f7e9STom Jones 		case IWX_DEBUG_LOG_MSG:
92392ad0f7e9STom Jones 			break;
92402ad0f7e9STom Jones 
92412ad0f7e9STom Jones 		case IWX_MCAST_FILTER_CMD:
92422ad0f7e9STom Jones 			break;
92432ad0f7e9STom Jones 
92442ad0f7e9STom Jones 		case IWX_WIDE_ID(IWX_DATA_PATH_GROUP, IWX_DQA_ENABLE_CMD):
92452ad0f7e9STom Jones 			break;
92462ad0f7e9STom Jones 
92472ad0f7e9STom Jones 		case IWX_WIDE_ID(IWX_SYSTEM_GROUP, IWX_SOC_CONFIGURATION_CMD):
92482ad0f7e9STom Jones 			break;
92492ad0f7e9STom Jones 
92502ad0f7e9STom Jones 		case IWX_WIDE_ID(IWX_SYSTEM_GROUP, IWX_INIT_EXTENDED_CFG_CMD):
92512ad0f7e9STom Jones 			break;
92522ad0f7e9STom Jones 
92532ad0f7e9STom Jones 		case IWX_WIDE_ID(IWX_REGULATORY_AND_NVM_GROUP,
92542ad0f7e9STom Jones 		    IWX_NVM_ACCESS_COMPLETE):
92552ad0f7e9STom Jones 			break;
92562ad0f7e9STom Jones 
92572ad0f7e9STom Jones 		case IWX_WIDE_ID(IWX_DATA_PATH_GROUP, IWX_RX_NO_DATA_NOTIF):
92582ad0f7e9STom Jones 			break; /* happens in monitor mode; ignore for now */
92592ad0f7e9STom Jones 
92602ad0f7e9STom Jones 		case IWX_WIDE_ID(IWX_DATA_PATH_GROUP, IWX_TLC_MNG_CONFIG_CMD):
92612ad0f7e9STom Jones 			break;
92622ad0f7e9STom Jones 
92632ad0f7e9STom Jones 		case IWX_WIDE_ID(IWX_DATA_PATH_GROUP,
92642ad0f7e9STom Jones 		    IWX_TLC_MNG_UPDATE_NOTIF): {
92652ad0f7e9STom Jones 			struct iwx_tlc_update_notif *notif;
92662ad0f7e9STom Jones 			SYNC_RESP_STRUCT(notif, pkt);
92672ad0f7e9STom Jones 			(void)notif;
92682ad0f7e9STom Jones 			if (iwx_rx_packet_payload_len(pkt) == sizeof(*notif))
92692ad0f7e9STom Jones 				iwx_rs_update(sc, notif);
92702ad0f7e9STom Jones 			break;
92712ad0f7e9STom Jones 		}
92722ad0f7e9STom Jones 
92732ad0f7e9STom Jones 		case IWX_WIDE_ID(IWX_DATA_PATH_GROUP, IWX_RLC_CONFIG_CMD):
92742ad0f7e9STom Jones 			break;
92752ad0f7e9STom Jones 
92762ad0f7e9STom Jones 		/* undocumented notification from iwx-ty-a0-gf-a0-77 image */
92772ad0f7e9STom Jones 		case IWX_WIDE_ID(IWX_DATA_PATH_GROUP, 0xf8):
92782ad0f7e9STom Jones 			break;
92792ad0f7e9STom Jones 
92802ad0f7e9STom Jones 		case IWX_WIDE_ID(IWX_REGULATORY_AND_NVM_GROUP,
92812ad0f7e9STom Jones 		    IWX_PNVM_INIT_COMPLETE):
92822ad0f7e9STom Jones 			DPRINTF(("%s: IWX_PNVM_INIT_COMPLETE\n", __func__));
92832ad0f7e9STom Jones 			sc->sc_init_complete |= IWX_PNVM_COMPLETE;
92842ad0f7e9STom Jones 			wakeup(&sc->sc_init_complete);
92852ad0f7e9STom Jones 			break;
92862ad0f7e9STom Jones 
92872ad0f7e9STom Jones 		default:
92882ad0f7e9STom Jones 			handled = 0;
92892ad0f7e9STom Jones 			/* XXX wulf: Get rid of bluetooth-related spam */
92902ad0f7e9STom Jones 			if ((code == 0xc2 && pkt->len_n_flags == 0x0000000c) ||
92912ad0f7e9STom Jones 			    (code == 0xce && pkt->len_n_flags == 0x2000002c))
92922ad0f7e9STom Jones 				break;
92932ad0f7e9STom Jones 			printf("%s: unhandled firmware response 0x%x/0x%x "
92942ad0f7e9STom Jones 			    "rx ring %d[%d]\n",
92952ad0f7e9STom Jones 			    DEVNAME(sc), code, pkt->len_n_flags,
92962ad0f7e9STom Jones 			    (qid & ~0x80), idx);
92972ad0f7e9STom Jones 			break;
92982ad0f7e9STom Jones 		}
92992ad0f7e9STom Jones 
93002ad0f7e9STom Jones 		/*
93012ad0f7e9STom Jones 		 * uCode sets bit 0x80 when it originates the notification,
93022ad0f7e9STom Jones 		 * i.e. when the notification is not a direct response to a
93032ad0f7e9STom Jones 		 * command sent by the driver.
93042ad0f7e9STom Jones 		 * For example, uCode issues IWX_REPLY_RX when it sends a
93052ad0f7e9STom Jones 		 * received frame to the driver.
93062ad0f7e9STom Jones 		 */
93072ad0f7e9STom Jones 		if (handled && !(qid & (1 << 7))) {
93082ad0f7e9STom Jones 			iwx_cmd_done(sc, qid, idx, code);
93092ad0f7e9STom Jones 		}
93102ad0f7e9STom Jones 
93112ad0f7e9STom Jones 		offset += roundup(len, IWX_FH_RSCSR_FRAME_ALIGN);
93122ad0f7e9STom Jones 
93132ad0f7e9STom Jones 		/* AX210 devices ship only one packet per Rx buffer. */
93142ad0f7e9STom Jones 		if (sc->sc_device_family >= IWX_DEVICE_FAMILY_AX210)
93152ad0f7e9STom Jones 			break;
93162ad0f7e9STom Jones 	}
93172ad0f7e9STom Jones 
93182ad0f7e9STom Jones 	if (m0 && m0 != data->m)
93192ad0f7e9STom Jones 		m_freem(m0);
93202ad0f7e9STom Jones }
93212ad0f7e9STom Jones 
93222ad0f7e9STom Jones static void
93232ad0f7e9STom Jones iwx_notif_intr(struct iwx_softc *sc)
93242ad0f7e9STom Jones {
93252ad0f7e9STom Jones 	struct mbuf m;
93262ad0f7e9STom Jones 	uint16_t hw;
93272ad0f7e9STom Jones 
93282ad0f7e9STom Jones 	bus_dmamap_sync(sc->rxq.stat_dma.tag, sc->rxq.stat_dma.map,
93292ad0f7e9STom Jones 	    BUS_DMASYNC_POSTREAD);
93302ad0f7e9STom Jones 
93312ad0f7e9STom Jones 	if (sc->sc_device_family >= IWX_DEVICE_FAMILY_AX210) {
93322ad0f7e9STom Jones 		uint16_t *status = sc->rxq.stat_dma.vaddr;
93332ad0f7e9STom Jones 		hw = le16toh(*status) & 0xfff;
93342ad0f7e9STom Jones 	} else
93352ad0f7e9STom Jones 		hw = le16toh(sc->rxq.stat->closed_rb_num) & 0xfff;
93362ad0f7e9STom Jones 	hw &= (IWX_RX_MQ_RING_COUNT - 1);
93372ad0f7e9STom Jones 	while (sc->rxq.cur != hw) {
93382ad0f7e9STom Jones 		struct iwx_rx_data *data = &sc->rxq.data[sc->rxq.cur];
93392ad0f7e9STom Jones 
93402ad0f7e9STom Jones 		bus_dmamap_sync(sc->rxq.data_dmat, data->map,
93412ad0f7e9STom Jones 		BUS_DMASYNC_POSTREAD);
93422ad0f7e9STom Jones 
93432ad0f7e9STom Jones 		iwx_rx_pkt(sc, data, &m);
93442ad0f7e9STom Jones 		sc->rxq.cur = (sc->rxq.cur + 1) % IWX_RX_MQ_RING_COUNT;
93452ad0f7e9STom Jones 	}
93462ad0f7e9STom Jones 
93472ad0f7e9STom Jones 	/*
93482ad0f7e9STom Jones 	 * Tell the firmware what we have processed.
93492ad0f7e9STom Jones 	 * Seems like the hardware gets upset unless we align the write by 8??
93502ad0f7e9STom Jones 	 */
93512ad0f7e9STom Jones 	hw = (hw == 0) ? IWX_RX_MQ_RING_COUNT - 1 : hw - 1;
93522ad0f7e9STom Jones 	IWX_WRITE(sc, IWX_RFH_Q0_FRBDCB_WIDX_TRG, hw & ~7);
93532ad0f7e9STom Jones }
93542ad0f7e9STom Jones 
93552ad0f7e9STom Jones #if 0
93562ad0f7e9STom Jones int
93572ad0f7e9STom Jones iwx_intr(void *arg)
93582ad0f7e9STom Jones {
93592ad0f7e9STom Jones 	struct iwx_softc *sc = arg;
93602ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
93612ad0f7e9STom Jones 	struct ifnet *ifp = IC2IFP(ic);
93622ad0f7e9STom Jones 	int r1, r2, rv = 0;
93632ad0f7e9STom Jones 
93642ad0f7e9STom Jones 	IWX_WRITE(sc, IWX_CSR_INT_MASK, 0);
93652ad0f7e9STom Jones 
93662ad0f7e9STom Jones 	if (sc->sc_flags & IWX_FLAG_USE_ICT) {
93672ad0f7e9STom Jones 		uint32_t *ict = sc->ict_dma.vaddr;
93682ad0f7e9STom Jones 		int tmp;
93692ad0f7e9STom Jones 
93702ad0f7e9STom Jones 		tmp = htole32(ict[sc->ict_cur]);
93712ad0f7e9STom Jones 		if (!tmp)
93722ad0f7e9STom Jones 			goto out_ena;
93732ad0f7e9STom Jones 
93742ad0f7e9STom Jones 		/*
93752ad0f7e9STom Jones 		 * ok, there was something.  keep plowing until we have all.
93762ad0f7e9STom Jones 		 */
93772ad0f7e9STom Jones 		r1 = r2 = 0;
93782ad0f7e9STom Jones 		while (tmp) {
93792ad0f7e9STom Jones 			r1 |= tmp;
93802ad0f7e9STom Jones 			ict[sc->ict_cur] = 0;
93812ad0f7e9STom Jones 			sc->ict_cur = (sc->ict_cur+1) % IWX_ICT_COUNT;
93822ad0f7e9STom Jones 			tmp = htole32(ict[sc->ict_cur]);
93832ad0f7e9STom Jones 		}
93842ad0f7e9STom Jones 
93852ad0f7e9STom Jones 		/* this is where the fun begins.  don't ask */
93862ad0f7e9STom Jones 		if (r1 == 0xffffffff)
93872ad0f7e9STom Jones 			r1 = 0;
93882ad0f7e9STom Jones 
93892ad0f7e9STom Jones 		/* i am not expected to understand this */
93902ad0f7e9STom Jones 		if (r1 & 0xc0000)
93912ad0f7e9STom Jones 			r1 |= 0x8000;
93922ad0f7e9STom Jones 		r1 = (0xff & r1) | ((0xff00 & r1) << 16);
93932ad0f7e9STom Jones 	} else {
93942ad0f7e9STom Jones 		r1 = IWX_READ(sc, IWX_CSR_INT);
93952ad0f7e9STom Jones 		if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0)
93962ad0f7e9STom Jones 			goto out;
93972ad0f7e9STom Jones 		r2 = IWX_READ(sc, IWX_CSR_FH_INT_STATUS);
93982ad0f7e9STom Jones 	}
93992ad0f7e9STom Jones 	if (r1 == 0 && r2 == 0) {
94002ad0f7e9STom Jones 		goto out_ena;
94012ad0f7e9STom Jones 	}
94022ad0f7e9STom Jones 
94032ad0f7e9STom Jones 	IWX_WRITE(sc, IWX_CSR_INT, r1 | ~sc->sc_intmask);
94042ad0f7e9STom Jones 
94052ad0f7e9STom Jones 	if (r1 & IWX_CSR_INT_BIT_ALIVE) {
94062ad0f7e9STom Jones #if 0
94072ad0f7e9STom Jones 		int i;
94082ad0f7e9STom Jones 		/* Firmware has now configured the RFH. */
94092ad0f7e9STom Jones 		for (i = 0; i < IWX_RX_MQ_RING_COUNT; i++)
94102ad0f7e9STom Jones 			iwx_update_rx_desc(sc, &sc->rxq, i);
94112ad0f7e9STom Jones #endif
94122ad0f7e9STom Jones 		IWX_WRITE(sc, IWX_RFH_Q0_FRBDCB_WIDX_TRG, 8);
94132ad0f7e9STom Jones 	}
94142ad0f7e9STom Jones 
94152ad0f7e9STom Jones 
94162ad0f7e9STom Jones 	if (r1 & IWX_CSR_INT_BIT_RF_KILL) {
94172ad0f7e9STom Jones 		iwx_check_rfkill(sc);
94182ad0f7e9STom Jones 		rv = 1;
94192ad0f7e9STom Jones 		goto out_ena;
94202ad0f7e9STom Jones 	}
94212ad0f7e9STom Jones 
94222ad0f7e9STom Jones 	if (r1 & IWX_CSR_INT_BIT_SW_ERR) {
94232ad0f7e9STom Jones 		if (ifp->if_flags & IFF_DEBUG) {
94242ad0f7e9STom Jones 			iwx_nic_error(sc);
94252ad0f7e9STom Jones 			iwx_dump_driver_status(sc);
94262ad0f7e9STom Jones 		}
94272ad0f7e9STom Jones 		printf("%s: fatal firmware error\n", DEVNAME(sc));
94282ad0f7e9STom Jones 		ieee80211_restart_all(ic);
94292ad0f7e9STom Jones 		rv = 1;
94302ad0f7e9STom Jones 		goto out;
94312ad0f7e9STom Jones 
94322ad0f7e9STom Jones 	}
94332ad0f7e9STom Jones 
94342ad0f7e9STom Jones 	if (r1 & IWX_CSR_INT_BIT_HW_ERR) {
94352ad0f7e9STom Jones 		printf("%s: hardware error, stopping device \n", DEVNAME(sc));
94362ad0f7e9STom Jones 		iwx_stop(sc);
94372ad0f7e9STom Jones 		rv = 1;
94382ad0f7e9STom Jones 		goto out;
94392ad0f7e9STom Jones 	}
94402ad0f7e9STom Jones 
94412ad0f7e9STom Jones 	/* firmware chunk loaded */
94422ad0f7e9STom Jones 	if (r1 & IWX_CSR_INT_BIT_FH_TX) {
94432ad0f7e9STom Jones 		IWX_WRITE(sc, IWX_CSR_FH_INT_STATUS, IWX_CSR_FH_INT_TX_MASK);
94442ad0f7e9STom Jones 
94452ad0f7e9STom Jones 		sc->sc_fw_chunk_done = 1;
94462ad0f7e9STom Jones 		wakeup(&sc->sc_fw);
94472ad0f7e9STom Jones 	}
94482ad0f7e9STom Jones 
94492ad0f7e9STom Jones 	if (r1 & (IWX_CSR_INT_BIT_FH_RX | IWX_CSR_INT_BIT_SW_RX |
94502ad0f7e9STom Jones 	    IWX_CSR_INT_BIT_RX_PERIODIC)) {
94512ad0f7e9STom Jones 		if (r1 & (IWX_CSR_INT_BIT_FH_RX | IWX_CSR_INT_BIT_SW_RX)) {
94522ad0f7e9STom Jones 			IWX_WRITE(sc, IWX_CSR_FH_INT_STATUS, IWX_CSR_FH_INT_RX_MASK);
94532ad0f7e9STom Jones 		}
94542ad0f7e9STom Jones 		if (r1 & IWX_CSR_INT_BIT_RX_PERIODIC) {
94552ad0f7e9STom Jones 			IWX_WRITE(sc, IWX_CSR_INT, IWX_CSR_INT_BIT_RX_PERIODIC);
94562ad0f7e9STom Jones 		}
94572ad0f7e9STom Jones 
94582ad0f7e9STom Jones 		/* Disable periodic interrupt; we use it as just a one-shot. */
94592ad0f7e9STom Jones 		IWX_WRITE_1(sc, IWX_CSR_INT_PERIODIC_REG, IWX_CSR_INT_PERIODIC_DIS);
94602ad0f7e9STom Jones 
94612ad0f7e9STom Jones 		/*
94622ad0f7e9STom Jones 		 * Enable periodic interrupt in 8 msec only if we received
94632ad0f7e9STom Jones 		 * real RX interrupt (instead of just periodic int), to catch
94642ad0f7e9STom Jones 		 * any dangling Rx interrupt.  If it was just the periodic
94652ad0f7e9STom Jones 		 * interrupt, there was no dangling Rx activity, and no need
94662ad0f7e9STom Jones 		 * to extend the periodic interrupt; one-shot is enough.
94672ad0f7e9STom Jones 		 */
94682ad0f7e9STom Jones 		if (r1 & (IWX_CSR_INT_BIT_FH_RX | IWX_CSR_INT_BIT_SW_RX))
94692ad0f7e9STom Jones 			IWX_WRITE_1(sc, IWX_CSR_INT_PERIODIC_REG,
94702ad0f7e9STom Jones 			    IWX_CSR_INT_PERIODIC_ENA);
94712ad0f7e9STom Jones 
94722ad0f7e9STom Jones 		iwx_notif_intr(sc);
94732ad0f7e9STom Jones 	}
94742ad0f7e9STom Jones 
94752ad0f7e9STom Jones 	rv = 1;
94762ad0f7e9STom Jones 
94772ad0f7e9STom Jones  out_ena:
94782ad0f7e9STom Jones 	iwx_restore_interrupts(sc);
94792ad0f7e9STom Jones  out:
94802ad0f7e9STom Jones 	return rv;
94812ad0f7e9STom Jones }
94822ad0f7e9STom Jones #endif
94832ad0f7e9STom Jones 
94842ad0f7e9STom Jones static void
94852ad0f7e9STom Jones iwx_intr_msix(void *arg)
94862ad0f7e9STom Jones {
94872ad0f7e9STom Jones 	struct iwx_softc *sc = arg;
94882ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
94892ad0f7e9STom Jones 	uint32_t inta_fh, inta_hw;
94902ad0f7e9STom Jones 	int vector = 0;
94912ad0f7e9STom Jones 
94922ad0f7e9STom Jones 	IWX_LOCK(sc);
94932ad0f7e9STom Jones 
94942ad0f7e9STom Jones 	inta_fh = IWX_READ(sc, IWX_CSR_MSIX_FH_INT_CAUSES_AD);
94952ad0f7e9STom Jones 	inta_hw = IWX_READ(sc, IWX_CSR_MSIX_HW_INT_CAUSES_AD);
94962ad0f7e9STom Jones 	IWX_WRITE(sc, IWX_CSR_MSIX_FH_INT_CAUSES_AD, inta_fh);
94972ad0f7e9STom Jones 	IWX_WRITE(sc, IWX_CSR_MSIX_HW_INT_CAUSES_AD, inta_hw);
94982ad0f7e9STom Jones 	inta_fh &= sc->sc_fh_mask;
94992ad0f7e9STom Jones 	inta_hw &= sc->sc_hw_mask;
95002ad0f7e9STom Jones 
95012ad0f7e9STom Jones 	if (inta_fh & IWX_MSIX_FH_INT_CAUSES_Q0 ||
95022ad0f7e9STom Jones 	    inta_fh & IWX_MSIX_FH_INT_CAUSES_Q1) {
95032ad0f7e9STom Jones 		iwx_notif_intr(sc);
95042ad0f7e9STom Jones 	}
95052ad0f7e9STom Jones 
95062ad0f7e9STom Jones 	/* firmware chunk loaded */
95072ad0f7e9STom Jones 	if (inta_fh & IWX_MSIX_FH_INT_CAUSES_D2S_CH0_NUM) {
95082ad0f7e9STom Jones 		sc->sc_fw_chunk_done = 1;
95092ad0f7e9STom Jones 		wakeup(&sc->sc_fw);
95102ad0f7e9STom Jones 	}
95112ad0f7e9STom Jones 
95122ad0f7e9STom Jones 	if ((inta_fh & IWX_MSIX_FH_INT_CAUSES_FH_ERR) ||
95132ad0f7e9STom Jones 	    (inta_hw & IWX_MSIX_HW_INT_CAUSES_REG_SW_ERR) ||
95142ad0f7e9STom Jones 	    (inta_hw & IWX_MSIX_HW_INT_CAUSES_REG_SW_ERR_V2)) {
95152ad0f7e9STom Jones 		if (sc->sc_debug) {
95162ad0f7e9STom Jones 			iwx_nic_error(sc);
95172ad0f7e9STom Jones 			iwx_dump_driver_status(sc);
95182ad0f7e9STom Jones 		}
95192ad0f7e9STom Jones 		printf("%s: fatal firmware error\n", DEVNAME(sc));
95202ad0f7e9STom Jones 		ieee80211_restart_all(ic);
95212ad0f7e9STom Jones 		goto out;
95222ad0f7e9STom Jones 	}
95232ad0f7e9STom Jones 
95242ad0f7e9STom Jones 	if (inta_hw & IWX_MSIX_HW_INT_CAUSES_REG_RF_KILL) {
95252ad0f7e9STom Jones 		iwx_check_rfkill(sc);
95262ad0f7e9STom Jones 	}
95272ad0f7e9STom Jones 
95282ad0f7e9STom Jones 	if (inta_hw & IWX_MSIX_HW_INT_CAUSES_REG_HW_ERR) {
95292ad0f7e9STom Jones 		printf("%s: hardware error, stopping device \n", DEVNAME(sc));
95302ad0f7e9STom Jones 		sc->sc_flags |= IWX_FLAG_HW_ERR;
95312ad0f7e9STom Jones 		iwx_stop(sc);
95322ad0f7e9STom Jones 		goto out;
95332ad0f7e9STom Jones 	}
95342ad0f7e9STom Jones 
95352ad0f7e9STom Jones 	if (inta_hw & IWX_MSIX_HW_INT_CAUSES_REG_ALIVE) {
95362ad0f7e9STom Jones 		IWX_DPRINTF(sc, IWX_DEBUG_TRACE,
95372ad0f7e9STom Jones 		    "%s:%d WARNING: Skipping rx desc update\n",
95382ad0f7e9STom Jones 		    __func__, __LINE__);
95392ad0f7e9STom Jones #if 0
95402ad0f7e9STom Jones 		/*
95412ad0f7e9STom Jones 		 * XXX-THJ: we don't have the dma segment handy. This is hacked
95422ad0f7e9STom Jones 		 * out in the fc release, return to it if we ever get this
95432ad0f7e9STom Jones 		 * warning.
95442ad0f7e9STom Jones 		 */
95452ad0f7e9STom Jones 		/* Firmware has now configured the RFH. */
95462ad0f7e9STom Jones 		for (int i = 0; i < IWX_RX_MQ_RING_COUNT; i++)
95472ad0f7e9STom Jones 			iwx_update_rx_desc(sc, &sc->rxq, i);
95482ad0f7e9STom Jones #endif
95492ad0f7e9STom Jones 		IWX_WRITE(sc, IWX_RFH_Q0_FRBDCB_WIDX_TRG, 8);
95502ad0f7e9STom Jones 	}
95512ad0f7e9STom Jones 
95522ad0f7e9STom Jones 	/*
95532ad0f7e9STom Jones 	 * Before sending the interrupt the HW disables it to prevent
95542ad0f7e9STom Jones 	 * a nested interrupt. This is done by writing 1 to the corresponding
95552ad0f7e9STom Jones 	 * bit in the mask register. After handling the interrupt, it should be
95562ad0f7e9STom Jones 	 * re-enabled by clearing this bit. This register is defined as
95572ad0f7e9STom Jones 	 * write 1 clear (W1C) register, meaning that it's being clear
95582ad0f7e9STom Jones 	 * by writing 1 to the bit.
95592ad0f7e9STom Jones 	 */
95602ad0f7e9STom Jones 	IWX_WRITE(sc, IWX_CSR_MSIX_AUTOMASK_ST_AD, 1 << vector);
95612ad0f7e9STom Jones out:
95622ad0f7e9STom Jones 	IWX_UNLOCK(sc);
95632ad0f7e9STom Jones 	return;
95642ad0f7e9STom Jones }
95652ad0f7e9STom Jones 
95662ad0f7e9STom Jones /*
95672ad0f7e9STom Jones  * The device info table below contains device-specific config overrides.
95682ad0f7e9STom Jones  * The most important parameter derived from this table is the name of the
95692ad0f7e9STom Jones  * firmware image to load.
95702ad0f7e9STom Jones  *
95712ad0f7e9STom Jones  * The Linux iwlwifi driver uses an "old" and a "new" device info table.
95722ad0f7e9STom Jones  * The "old" table matches devices based on PCI vendor/product IDs only.
95732ad0f7e9STom Jones  * The "new" table extends this with various device parameters derived
95742ad0f7e9STom Jones  * from MAC type, and RF type.
95752ad0f7e9STom Jones  *
95762ad0f7e9STom Jones  * In iwlwifi "old" and "new" tables share the same array, where "old"
95772ad0f7e9STom Jones  * entries contain dummy values for data defined only for "new" entries.
95782ad0f7e9STom Jones  * As of 2022, Linux developers are still in the process of moving entries
95792ad0f7e9STom Jones  * from "old" to "new" style and it looks like this effort has stalled in
95802ad0f7e9STom Jones  * in some work-in-progress state for quite a while. Linux commits moving
95812ad0f7e9STom Jones  * entries from "old" to "new" have at times been reverted due to regressions.
95822ad0f7e9STom Jones  * Part of this complexity comes from iwlwifi supporting both iwm(4) and iwx(4)
95832ad0f7e9STom Jones  * devices in the same driver.
95842ad0f7e9STom Jones  *
95852ad0f7e9STom Jones  * Our table below contains mostly "new" entries declared in iwlwifi
95862ad0f7e9STom Jones  * with the _IWL_DEV_INFO() macro (with a leading underscore).
95872ad0f7e9STom Jones  * Other devices are matched based on PCI vendor/product ID as usual,
95882ad0f7e9STom Jones  * unless matching specific PCI subsystem vendor/product IDs is required.
95892ad0f7e9STom Jones  *
95902ad0f7e9STom Jones  * Some "old"-style entries are required to identify the firmware image to use.
95912ad0f7e9STom Jones  * Others might be used to print a specific marketing name into Linux dmesg,
95922ad0f7e9STom Jones  * but we can't be sure whether the corresponding devices would be matched
95932ad0f7e9STom Jones  * correctly in the absence of their entries. So we include them just in case.
95942ad0f7e9STom Jones  */
95952ad0f7e9STom Jones 
95962ad0f7e9STom Jones struct iwx_dev_info {
95972ad0f7e9STom Jones 	uint16_t device;
95982ad0f7e9STom Jones 	uint16_t subdevice;
95992ad0f7e9STom Jones 	uint16_t mac_type;
96002ad0f7e9STom Jones 	uint16_t rf_type;
96012ad0f7e9STom Jones 	uint8_t mac_step;
96022ad0f7e9STom Jones 	uint8_t rf_id;
96032ad0f7e9STom Jones 	uint8_t no_160;
96042ad0f7e9STom Jones 	uint8_t cores;
96052ad0f7e9STom Jones 	uint8_t cdb;
96062ad0f7e9STom Jones 	uint8_t jacket;
96072ad0f7e9STom Jones 	const struct iwx_device_cfg *cfg;
96082ad0f7e9STom Jones };
96092ad0f7e9STom Jones 
96102ad0f7e9STom Jones #define _IWX_DEV_INFO(_device, _subdevice, _mac_type, _mac_step, _rf_type, \
96112ad0f7e9STom Jones 		      _rf_id, _no_160, _cores, _cdb, _jacket, _cfg) \
96122ad0f7e9STom Jones 	{ .device = (_device), .subdevice = (_subdevice), .cfg = &(_cfg),  \
96132ad0f7e9STom Jones 	  .mac_type = _mac_type, .rf_type = _rf_type,	   \
96142ad0f7e9STom Jones 	  .no_160 = _no_160, .cores = _cores, .rf_id = _rf_id,		   \
96152ad0f7e9STom Jones 	  .mac_step = _mac_step, .cdb = _cdb, .jacket = _jacket }
96162ad0f7e9STom Jones 
96172ad0f7e9STom Jones #define IWX_DEV_INFO(_device, _subdevice, _cfg) \
96182ad0f7e9STom Jones 	_IWX_DEV_INFO(_device, _subdevice, IWX_CFG_ANY, IWX_CFG_ANY,	   \
96192ad0f7e9STom Jones 		      IWX_CFG_ANY, IWX_CFG_ANY, IWX_CFG_ANY, IWX_CFG_ANY,  \
96202ad0f7e9STom Jones 		      IWX_CFG_ANY, IWX_CFG_ANY, _cfg)
96212ad0f7e9STom Jones 
96222ad0f7e9STom Jones /*
96232ad0f7e9STom Jones  * When adding entries to this table keep in mind that entries must
96242ad0f7e9STom Jones  * be listed in the same order as in the Linux driver. Code walks this
96252ad0f7e9STom Jones  * table backwards and uses the first matching entry it finds.
96262ad0f7e9STom Jones  * Device firmware must be available in fw_update(8).
96272ad0f7e9STom Jones  */
96282ad0f7e9STom Jones static const struct iwx_dev_info iwx_dev_info_table[] = {
96292ad0f7e9STom Jones 	/* So with HR */
96302ad0f7e9STom Jones 	IWX_DEV_INFO(0x2725, 0x0090, iwx_2ax_cfg_so_gf_a0),
96312ad0f7e9STom Jones 	IWX_DEV_INFO(0x2725, 0x0020, iwx_2ax_cfg_ty_gf_a0),
96322ad0f7e9STom Jones 	IWX_DEV_INFO(0x2725, 0x2020, iwx_2ax_cfg_ty_gf_a0),
96332ad0f7e9STom Jones 	IWX_DEV_INFO(0x2725, 0x0024, iwx_2ax_cfg_ty_gf_a0),
96342ad0f7e9STom Jones 	IWX_DEV_INFO(0x2725, 0x0310, iwx_2ax_cfg_ty_gf_a0),
96352ad0f7e9STom Jones 	IWX_DEV_INFO(0x2725, 0x0510, iwx_2ax_cfg_ty_gf_a0),
96362ad0f7e9STom Jones 	IWX_DEV_INFO(0x2725, 0x0A10, iwx_2ax_cfg_ty_gf_a0),
96372ad0f7e9STom Jones 	IWX_DEV_INFO(0x2725, 0xE020, iwx_2ax_cfg_ty_gf_a0),
96382ad0f7e9STom Jones 	IWX_DEV_INFO(0x2725, 0xE024, iwx_2ax_cfg_ty_gf_a0),
96392ad0f7e9STom Jones 	IWX_DEV_INFO(0x2725, 0x4020, iwx_2ax_cfg_ty_gf_a0),
96402ad0f7e9STom Jones 	IWX_DEV_INFO(0x2725, 0x6020, iwx_2ax_cfg_ty_gf_a0),
96412ad0f7e9STom Jones 	IWX_DEV_INFO(0x2725, 0x6024, iwx_2ax_cfg_ty_gf_a0),
96422ad0f7e9STom Jones 	IWX_DEV_INFO(0x2725, 0x1673, iwx_2ax_cfg_ty_gf_a0), /* killer_1675w */
96432ad0f7e9STom Jones 	IWX_DEV_INFO(0x2725, 0x1674, iwx_2ax_cfg_ty_gf_a0), /* killer_1675x */
96442ad0f7e9STom Jones 	IWX_DEV_INFO(0x51f0, 0x1691, iwx_2ax_cfg_so_gf4_a0), /* killer_1690s */
96452ad0f7e9STom Jones 	IWX_DEV_INFO(0x51f0, 0x1692, iwx_2ax_cfg_so_gf4_a0), /* killer_1690i */
96462ad0f7e9STom Jones 	IWX_DEV_INFO(0x51f1, 0x1691, iwx_2ax_cfg_so_gf4_a0),
96472ad0f7e9STom Jones 	IWX_DEV_INFO(0x51f1, 0x1692, iwx_2ax_cfg_so_gf4_a0),
96482ad0f7e9STom Jones 	IWX_DEV_INFO(0x54f0, 0x1691, iwx_2ax_cfg_so_gf4_a0), /* killer_1690s */
96492ad0f7e9STom Jones 	IWX_DEV_INFO(0x54f0, 0x1692, iwx_2ax_cfg_so_gf4_a0), /* killer_1690i */
96502ad0f7e9STom Jones 	IWX_DEV_INFO(0x7a70, 0x0090, iwx_2ax_cfg_so_gf_a0_long),
96512ad0f7e9STom Jones 	IWX_DEV_INFO(0x7a70, 0x0098, iwx_2ax_cfg_so_gf_a0_long),
96522ad0f7e9STom Jones 	IWX_DEV_INFO(0x7a70, 0x00b0, iwx_2ax_cfg_so_gf4_a0_long),
96532ad0f7e9STom Jones 	IWX_DEV_INFO(0x7a70, 0x0310, iwx_2ax_cfg_so_gf_a0_long),
96542ad0f7e9STom Jones 	IWX_DEV_INFO(0x7a70, 0x0510, iwx_2ax_cfg_so_gf_a0_long),
96552ad0f7e9STom Jones 	IWX_DEV_INFO(0x7a70, 0x0a10, iwx_2ax_cfg_so_gf_a0_long),
96562ad0f7e9STom Jones 	IWX_DEV_INFO(0x7af0, 0x0090, iwx_2ax_cfg_so_gf_a0),
96572ad0f7e9STom Jones 	IWX_DEV_INFO(0x7af0, 0x0098, iwx_2ax_cfg_so_gf_a0),
96582ad0f7e9STom Jones 	IWX_DEV_INFO(0x7af0, 0x00b0, iwx_2ax_cfg_so_gf4_a0),
96592ad0f7e9STom Jones 	IWX_DEV_INFO(0x7a70, 0x1691, iwx_2ax_cfg_so_gf4_a0), /* killer_1690s */
96602ad0f7e9STom Jones 	IWX_DEV_INFO(0x7a70, 0x1692, iwx_2ax_cfg_so_gf4_a0), /* killer_1690i */
96612ad0f7e9STom Jones 	IWX_DEV_INFO(0x7af0, 0x0310, iwx_2ax_cfg_so_gf_a0),
96622ad0f7e9STom Jones 	IWX_DEV_INFO(0x7af0, 0x0510, iwx_2ax_cfg_so_gf_a0),
96632ad0f7e9STom Jones 	IWX_DEV_INFO(0x7af0, 0x0a10, iwx_2ax_cfg_so_gf_a0),
96642ad0f7e9STom Jones 	IWX_DEV_INFO(0x7f70, 0x1691, iwx_2ax_cfg_so_gf4_a0), /* killer_1690s */
96652ad0f7e9STom Jones 	IWX_DEV_INFO(0x7f70, 0x1692, iwx_2ax_cfg_so_gf4_a0), /* killer_1690i */
96662ad0f7e9STom Jones 
96672ad0f7e9STom Jones 	/* So with GF2 */
96682ad0f7e9STom Jones 	IWX_DEV_INFO(0x2726, 0x1671, iwx_2ax_cfg_so_gf_a0), /* killer_1675s */
96692ad0f7e9STom Jones 	IWX_DEV_INFO(0x2726, 0x1672, iwx_2ax_cfg_so_gf_a0), /* killer_1675i */
96702ad0f7e9STom Jones 	IWX_DEV_INFO(0x51f0, 0x1671, iwx_2ax_cfg_so_gf_a0), /* killer_1675s */
96712ad0f7e9STom Jones 	IWX_DEV_INFO(0x51f0, 0x1672, iwx_2ax_cfg_so_gf_a0), /* killer_1675i */
96722ad0f7e9STom Jones 	IWX_DEV_INFO(0x54f0, 0x1671, iwx_2ax_cfg_so_gf_a0), /* killer_1675s */
96732ad0f7e9STom Jones 	IWX_DEV_INFO(0x54f0, 0x1672, iwx_2ax_cfg_so_gf_a0), /* killer_1675i */
96742ad0f7e9STom Jones 	IWX_DEV_INFO(0x7a70, 0x1671, iwx_2ax_cfg_so_gf_a0), /* killer_1675s */
96752ad0f7e9STom Jones 	IWX_DEV_INFO(0x7a70, 0x1672, iwx_2ax_cfg_so_gf_a0), /* killer_1675i */
96762ad0f7e9STom Jones 	IWX_DEV_INFO(0x7af0, 0x1671, iwx_2ax_cfg_so_gf_a0), /* killer_1675s */
96772ad0f7e9STom Jones 	IWX_DEV_INFO(0x7af0, 0x1672, iwx_2ax_cfg_so_gf_a0), /* killer_1675i */
96782ad0f7e9STom Jones 	IWX_DEV_INFO(0x7f70, 0x1671, iwx_2ax_cfg_so_gf_a0), /* killer_1675s */
96792ad0f7e9STom Jones 	IWX_DEV_INFO(0x7f70, 0x1672, iwx_2ax_cfg_so_gf_a0), /* killer_1675i */
96802ad0f7e9STom Jones 
96812ad0f7e9STom Jones 	/* Qu with Jf, C step */
96822ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
96832ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_QU, IWX_SILICON_C_STEP,
96842ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_JF1, IWX_CFG_RF_ID_JF1,
96852ad0f7e9STom Jones 		      IWX_CFG_160, IWX_CFG_CORES_BT, IWX_CFG_NO_CDB,
96862ad0f7e9STom Jones 		      IWX_CFG_ANY, iwx_9560_qu_c0_jf_b0_cfg), /* 9461_160 */
96872ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
96882ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_QU, IWX_SILICON_C_STEP,
96892ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_JF1, IWX_CFG_RF_ID_JF1,
96902ad0f7e9STom Jones 		      IWX_CFG_NO_160, IWX_CFG_CORES_BT, IWX_CFG_NO_CDB,
96912ad0f7e9STom Jones 		      IWX_CFG_ANY, iwx_9560_qu_c0_jf_b0_cfg), /* iwl9461 */
96922ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
96932ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_QU, IWX_SILICON_C_STEP,
96942ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_JF1, IWX_CFG_RF_ID_JF1_DIV,
96952ad0f7e9STom Jones 		      IWX_CFG_160, IWX_CFG_CORES_BT, IWX_CFG_NO_CDB,
96962ad0f7e9STom Jones 		      IWX_CFG_ANY, iwx_9560_qu_c0_jf_b0_cfg), /* 9462_160 */
96972ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
96982ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_QU, IWX_SILICON_C_STEP,
96992ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_JF1, IWX_CFG_RF_ID_JF1_DIV,
97002ad0f7e9STom Jones 		      IWX_CFG_NO_160, IWX_CFG_CORES_BT, IWX_CFG_NO_CDB,
97012ad0f7e9STom Jones 		      IWX_CFG_ANY, iwx_9560_qu_c0_jf_b0_cfg), /* 9462 */
97022ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
97032ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_QU, IWX_SILICON_C_STEP,
97042ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_JF2, IWX_CFG_RF_ID_JF,
97052ad0f7e9STom Jones 		      IWX_CFG_160, IWX_CFG_CORES_BT, IWX_CFG_NO_CDB,
97062ad0f7e9STom Jones 		      IWX_CFG_ANY, iwx_9560_qu_c0_jf_b0_cfg), /* 9560_160 */
97072ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
97082ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_QU, IWX_SILICON_C_STEP,
97092ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_JF2, IWX_CFG_RF_ID_JF,
97102ad0f7e9STom Jones 		      IWX_CFG_NO_160, IWX_CFG_CORES_BT, IWX_CFG_NO_CDB,
97112ad0f7e9STom Jones 		      IWX_CFG_ANY, iwx_9560_qu_c0_jf_b0_cfg), /* 9560 */
97122ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, 0x1551,
97132ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_QU, IWX_SILICON_C_STEP,
97142ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_JF2, IWX_CFG_RF_ID_JF,
97152ad0f7e9STom Jones 		      IWX_CFG_160, IWX_CFG_CORES_BT, IWX_CFG_NO_CDB,
97162ad0f7e9STom Jones 		      IWX_CFG_ANY,
97172ad0f7e9STom Jones 		      iwx_9560_qu_c0_jf_b0_cfg), /* 9560_killer_1550s */
97182ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, 0x1552,
97192ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_QU, IWX_SILICON_C_STEP,
97202ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_JF2, IWX_CFG_RF_ID_JF,
97212ad0f7e9STom Jones 		      IWX_CFG_NO_160, IWX_CFG_CORES_BT, IWX_CFG_NO_CDB,
97222ad0f7e9STom Jones 		      IWX_CFG_ANY,
97232ad0f7e9STom Jones 		      iwx_9560_qu_c0_jf_b0_cfg), /* 9560_killer_1550i */
97242ad0f7e9STom Jones 
97252ad0f7e9STom Jones 	/* QuZ with Jf */
97262ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
97272ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_QUZ, IWX_CFG_ANY,
97282ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_JF2, IWX_CFG_RF_ID_JF,
97292ad0f7e9STom Jones 		      IWX_CFG_160, IWX_CFG_CORES_BT, IWX_CFG_NO_CDB,
97302ad0f7e9STom Jones 		      IWX_CFG_ANY, iwx_9560_quz_a0_jf_b0_cfg), /* 9461_160 */
97312ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
97322ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_QUZ, IWX_CFG_ANY,
97332ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_JF2, IWX_CFG_RF_ID_JF,
97342ad0f7e9STom Jones 		      IWX_CFG_NO_160, IWX_CFG_CORES_BT, IWX_CFG_NO_CDB,
97352ad0f7e9STom Jones 		      IWX_CFG_ANY, iwx_9560_quz_a0_jf_b0_cfg), /* 9461 */
97362ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
97372ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_QUZ, IWX_CFG_ANY,
97382ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_JF1, IWX_CFG_RF_ID_JF1_DIV,
97392ad0f7e9STom Jones 		      IWX_CFG_160, IWX_CFG_CORES_BT, IWX_CFG_NO_CDB,
97402ad0f7e9STom Jones 		      IWX_CFG_ANY, iwx_9560_quz_a0_jf_b0_cfg), /* 9462_160 */
97412ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
97422ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_QUZ, IWX_CFG_ANY,
97432ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_JF1, IWX_CFG_RF_ID_JF1_DIV,
97442ad0f7e9STom Jones 		      IWX_CFG_NO_160, IWX_CFG_CORES_BT, IWX_CFG_NO_CDB,
97452ad0f7e9STom Jones 		      IWX_CFG_ANY, iwx_9560_quz_a0_jf_b0_cfg), /* 9462 */
97462ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, 0x1551,
97472ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_QUZ, IWX_CFG_ANY,
97482ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_JF2, IWX_CFG_RF_ID_JF,
97492ad0f7e9STom Jones 		      IWX_CFG_160, IWX_CFG_CORES_BT, IWX_CFG_NO_CDB,
97502ad0f7e9STom Jones 		      IWX_CFG_ANY,
97512ad0f7e9STom Jones 		      iwx_9560_quz_a0_jf_b0_cfg), /* killer_1550s */
97522ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, 0x1552,
97532ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_QUZ, IWX_CFG_ANY,
97542ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_JF2, IWX_CFG_RF_ID_JF,
97552ad0f7e9STom Jones 		      IWX_CFG_NO_160, IWX_CFG_CORES_BT, IWX_CFG_NO_CDB,
97562ad0f7e9STom Jones 		      IWX_CFG_ANY,
97572ad0f7e9STom Jones 		      iwx_9560_quz_a0_jf_b0_cfg), /* 9560_killer_1550i */
97582ad0f7e9STom Jones 
97592ad0f7e9STom Jones 	/* Qu with Hr, B step */
97602ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
97612ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_QU, IWX_SILICON_B_STEP,
97622ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_HR1, IWX_CFG_ANY,
97632ad0f7e9STom Jones 		      IWX_CFG_ANY, IWX_CFG_ANY, IWX_CFG_NO_CDB, IWX_CFG_ANY,
97642ad0f7e9STom Jones 		      iwx_qu_b0_hr1_b0), /* AX101 */
97652ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
97662ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_QU, IWX_SILICON_B_STEP,
97672ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_HR2, IWX_CFG_ANY,
97682ad0f7e9STom Jones 		      IWX_CFG_NO_160, IWX_CFG_ANY, IWX_CFG_NO_CDB, IWX_CFG_ANY,
97692ad0f7e9STom Jones 		      iwx_qu_b0_hr_b0), /* AX203 */
97702ad0f7e9STom Jones 
97712ad0f7e9STom Jones 	/* Qu with Hr, C step */
97722ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
97732ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_QU, IWX_SILICON_C_STEP,
97742ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_HR1, IWX_CFG_ANY,
97752ad0f7e9STom Jones 		      IWX_CFG_ANY, IWX_CFG_ANY, IWX_CFG_NO_CDB, IWX_CFG_ANY,
97762ad0f7e9STom Jones 		      iwx_qu_c0_hr1_b0), /* AX101 */
97772ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
97782ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_QU, IWX_SILICON_C_STEP,
97792ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_HR2, IWX_CFG_ANY,
97802ad0f7e9STom Jones 		      IWX_CFG_NO_160, IWX_CFG_ANY, IWX_CFG_NO_CDB, IWX_CFG_ANY,
97812ad0f7e9STom Jones 		      iwx_qu_c0_hr_b0), /* AX203 */
97822ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
97832ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_QU, IWX_SILICON_C_STEP,
97842ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_HR2, IWX_CFG_ANY,
97852ad0f7e9STom Jones 		      IWX_CFG_160, IWX_CFG_ANY, IWX_CFG_NO_CDB, IWX_CFG_ANY,
97862ad0f7e9STom Jones 		      iwx_qu_c0_hr_b0), /* AX201 */
97872ad0f7e9STom Jones 
97882ad0f7e9STom Jones 	/* QuZ with Hr */
97892ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
97902ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_QUZ, IWX_CFG_ANY,
97912ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_HR1, IWX_CFG_ANY,
97922ad0f7e9STom Jones 		      IWX_CFG_ANY, IWX_CFG_ANY, IWX_CFG_NO_CDB, IWX_CFG_ANY,
97932ad0f7e9STom Jones 		      iwx_quz_a0_hr1_b0), /* AX101 */
97942ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
97952ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_QUZ, IWX_SILICON_B_STEP,
97962ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_HR2, IWX_CFG_ANY,
97972ad0f7e9STom Jones 		      IWX_CFG_NO_160, IWX_CFG_ANY, IWX_CFG_NO_CDB, IWX_CFG_ANY,
97982ad0f7e9STom Jones 		      iwx_cfg_quz_a0_hr_b0), /* AX203 */
97992ad0f7e9STom Jones 
98002ad0f7e9STom Jones 	/* SoF with JF2 */
98012ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
98022ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_SOF, IWX_CFG_ANY,
98032ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_JF2, IWX_CFG_RF_ID_JF,
98042ad0f7e9STom Jones 		      IWX_CFG_160, IWX_CFG_CORES_BT, IWX_CFG_NO_CDB,
98052ad0f7e9STom Jones 		      IWX_CFG_ANY, iwx_2ax_cfg_so_jf_b0), /* 9560_160 */
98062ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
98072ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_SOF, IWX_CFG_ANY,
98082ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_JF2, IWX_CFG_RF_ID_JF,
98092ad0f7e9STom Jones 		      IWX_CFG_NO_160, IWX_CFG_CORES_BT, IWX_CFG_NO_CDB,
98102ad0f7e9STom Jones 		      IWX_CFG_ANY, iwx_2ax_cfg_so_jf_b0), /* 9560 */
98112ad0f7e9STom Jones 
98122ad0f7e9STom Jones 	/* SoF with JF */
98132ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
98142ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_SOF, IWX_CFG_ANY,
98152ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_JF1, IWX_CFG_RF_ID_JF1,
98162ad0f7e9STom Jones 		      IWX_CFG_160, IWX_CFG_CORES_BT, IWX_CFG_NO_CDB,
98172ad0f7e9STom Jones 		      IWX_CFG_ANY, iwx_2ax_cfg_so_jf_b0), /* 9461_160 */
98182ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
98192ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_SOF, IWX_CFG_ANY,
98202ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_JF1, IWX_CFG_RF_ID_JF1_DIV,
98212ad0f7e9STom Jones 		      IWX_CFG_160, IWX_CFG_CORES_BT, IWX_CFG_NO_CDB,
98222ad0f7e9STom Jones 		      IWX_CFG_ANY, iwx_2ax_cfg_so_jf_b0), /* 9462_160 */
98232ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
98242ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_SOF, IWX_CFG_ANY,
98252ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_JF1, IWX_CFG_RF_ID_JF1,
98262ad0f7e9STom Jones 		      IWX_CFG_NO_160, IWX_CFG_CORES_BT, IWX_CFG_NO_CDB,
98272ad0f7e9STom Jones 		      IWX_CFG_ANY, iwx_2ax_cfg_so_jf_b0), /* 9461_name */
98282ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
98292ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_SOF, IWX_CFG_ANY,
98302ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_JF1, IWX_CFG_RF_ID_JF1_DIV,
98312ad0f7e9STom Jones 		      IWX_CFG_NO_160, IWX_CFG_CORES_BT, IWX_CFG_NO_CDB,
98322ad0f7e9STom Jones 		      IWX_CFG_ANY, iwx_2ax_cfg_so_jf_b0), /* 9462 */
98332ad0f7e9STom Jones 
98342ad0f7e9STom Jones 	/* So with Hr */
98352ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
98362ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_SO, IWX_CFG_ANY,
98372ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_HR2, IWX_CFG_ANY,
98382ad0f7e9STom Jones 		      IWX_CFG_NO_160, IWX_CFG_ANY, IWX_CFG_NO_CDB, IWX_CFG_ANY,
98392ad0f7e9STom Jones 		      iwx_cfg_so_a0_hr_b0), /* AX203 */
98402ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
98412ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_SO, IWX_CFG_ANY,
98422ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_HR1, IWX_CFG_ANY,
98432ad0f7e9STom Jones 		      IWX_CFG_160, IWX_CFG_ANY, IWX_CFG_NO_CDB, IWX_CFG_ANY,
98442ad0f7e9STom Jones 		      iwx_cfg_so_a0_hr_b0), /* ax101 */
98452ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
98462ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_SO, IWX_CFG_ANY,
98472ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_HR2, IWX_CFG_ANY,
98482ad0f7e9STom Jones 		      IWX_CFG_160, IWX_CFG_ANY, IWX_CFG_NO_CDB, IWX_CFG_ANY,
98492ad0f7e9STom Jones 		      iwx_cfg_so_a0_hr_b0), /* ax201 */
98502ad0f7e9STom Jones 
98512ad0f7e9STom Jones 	/* So-F with Hr */
98522ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
98532ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_SOF, IWX_CFG_ANY,
98542ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_HR2, IWX_CFG_ANY,
98552ad0f7e9STom Jones 		      IWX_CFG_NO_160, IWX_CFG_ANY, IWX_CFG_NO_CDB, IWX_CFG_ANY,
98562ad0f7e9STom Jones 		      iwx_cfg_so_a0_hr_b0), /* AX203 */
98572ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
98582ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_SOF, IWX_CFG_ANY,
98592ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_HR1, IWX_CFG_ANY,
98602ad0f7e9STom Jones 		      IWX_CFG_160, IWX_CFG_ANY, IWX_CFG_NO_CDB, IWX_CFG_ANY,
98612ad0f7e9STom Jones 		      iwx_cfg_so_a0_hr_b0), /* AX101 */
98622ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
98632ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_SOF, IWX_CFG_ANY,
98642ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_HR2, IWX_CFG_ANY,
98652ad0f7e9STom Jones 		      IWX_CFG_160, IWX_CFG_ANY, IWX_CFG_NO_CDB, IWX_CFG_ANY,
98662ad0f7e9STom Jones 		      iwx_cfg_so_a0_hr_b0), /* AX201 */
98672ad0f7e9STom Jones 
98682ad0f7e9STom Jones 	/* So-F with GF */
98692ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
98702ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_SOF, IWX_CFG_ANY,
98712ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_GF, IWX_CFG_ANY,
98722ad0f7e9STom Jones 		      IWX_CFG_160, IWX_CFG_ANY, IWX_CFG_NO_CDB, IWX_CFG_ANY,
98732ad0f7e9STom Jones 		      iwx_2ax_cfg_so_gf_a0), /* AX211 */
98742ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
98752ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_SOF, IWX_CFG_ANY,
98762ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_GF, IWX_CFG_ANY,
98772ad0f7e9STom Jones 		      IWX_CFG_160, IWX_CFG_ANY, IWX_CFG_CDB, IWX_CFG_ANY,
98782ad0f7e9STom Jones 		      iwx_2ax_cfg_so_gf4_a0), /* AX411 */
98792ad0f7e9STom Jones 
98802ad0f7e9STom Jones 	/* So with GF */
98812ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
98822ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_SO, IWX_CFG_ANY,
98832ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_GF, IWX_CFG_ANY,
98842ad0f7e9STom Jones 		      IWX_CFG_160, IWX_CFG_ANY, IWX_CFG_NO_CDB, IWX_CFG_ANY,
98852ad0f7e9STom Jones 		      iwx_2ax_cfg_so_gf_a0), /* AX211 */
98862ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
98872ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_SO, IWX_CFG_ANY,
98882ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_GF, IWX_CFG_ANY,
98892ad0f7e9STom Jones 		      IWX_CFG_160, IWX_CFG_ANY, IWX_CFG_CDB, IWX_CFG_ANY,
98902ad0f7e9STom Jones 		      iwx_2ax_cfg_so_gf4_a0), /* AX411 */
98912ad0f7e9STom Jones 
98922ad0f7e9STom Jones 	/* So with JF2 */
98932ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
98942ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_SO, IWX_CFG_ANY,
98952ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_JF2, IWX_CFG_RF_ID_JF,
98962ad0f7e9STom Jones 		      IWX_CFG_160, IWX_CFG_CORES_BT, IWX_CFG_NO_CDB,
98972ad0f7e9STom Jones 		      IWX_CFG_ANY, iwx_2ax_cfg_so_jf_b0), /* 9560_160 */
98982ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
98992ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_SO, IWX_CFG_ANY,
99002ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_JF2, IWX_CFG_RF_ID_JF,
99012ad0f7e9STom Jones 		      IWX_CFG_NO_160, IWX_CFG_CORES_BT, IWX_CFG_NO_CDB,
99022ad0f7e9STom Jones 		      IWX_CFG_ANY, iwx_2ax_cfg_so_jf_b0), /* 9560 */
99032ad0f7e9STom Jones 
99042ad0f7e9STom Jones 	/* So with JF */
99052ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
99062ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_SO, IWX_CFG_ANY,
99072ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_JF1, IWX_CFG_RF_ID_JF1,
99082ad0f7e9STom Jones 		      IWX_CFG_160, IWX_CFG_CORES_BT, IWX_CFG_NO_CDB,
99092ad0f7e9STom Jones 		      IWX_CFG_ANY, iwx_2ax_cfg_so_jf_b0), /* 9461_160 */
99102ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
99112ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_SO, IWX_CFG_ANY,
99122ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_JF1, IWX_CFG_RF_ID_JF1_DIV,
99132ad0f7e9STom Jones 		      IWX_CFG_160, IWX_CFG_CORES_BT, IWX_CFG_NO_CDB,
99142ad0f7e9STom Jones 		      IWX_CFG_ANY, iwx_2ax_cfg_so_jf_b0), /* 9462_160 */
99152ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
99162ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_SO, IWX_CFG_ANY,
99172ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_JF1, IWX_CFG_RF_ID_JF1,
99182ad0f7e9STom Jones 		      IWX_CFG_NO_160, IWX_CFG_CORES_BT, IWX_CFG_NO_CDB,
99192ad0f7e9STom Jones 		      IWX_CFG_ANY, iwx_2ax_cfg_so_jf_b0), /* iwl9461 */
99202ad0f7e9STom Jones 	_IWX_DEV_INFO(IWX_CFG_ANY, IWX_CFG_ANY,
99212ad0f7e9STom Jones 		      IWX_CFG_MAC_TYPE_SO, IWX_CFG_ANY,
99222ad0f7e9STom Jones 		      IWX_CFG_RF_TYPE_JF1, IWX_CFG_RF_ID_JF1_DIV,
99232ad0f7e9STom Jones 		      IWX_CFG_NO_160, IWX_CFG_CORES_BT, IWX_CFG_NO_CDB,
99242ad0f7e9STom Jones 		      IWX_CFG_ANY, iwx_2ax_cfg_so_jf_b0), /* 9462 */
99252ad0f7e9STom Jones };
99262ad0f7e9STom Jones 
99272ad0f7e9STom Jones static int
99282ad0f7e9STom Jones iwx_preinit(struct iwx_softc *sc)
99292ad0f7e9STom Jones {
99302ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
99312ad0f7e9STom Jones 	int err;
99322ad0f7e9STom Jones 
99332ad0f7e9STom Jones 	err = iwx_prepare_card_hw(sc);
99342ad0f7e9STom Jones 	if (err) {
99352ad0f7e9STom Jones 		printf("%s: could not initialize hardware\n", DEVNAME(sc));
99362ad0f7e9STom Jones 		return err;
99372ad0f7e9STom Jones 	}
99382ad0f7e9STom Jones 
99392ad0f7e9STom Jones 	if (sc->attached) {
99402ad0f7e9STom Jones 		return 0;
99412ad0f7e9STom Jones 	}
99422ad0f7e9STom Jones 
99432ad0f7e9STom Jones 	err = iwx_start_hw(sc);
99442ad0f7e9STom Jones 	if (err) {
99452ad0f7e9STom Jones 		printf("%s: could not initialize hardware\n", DEVNAME(sc));
99462ad0f7e9STom Jones 		return err;
99472ad0f7e9STom Jones 	}
99482ad0f7e9STom Jones 
99492ad0f7e9STom Jones 	err = iwx_run_init_mvm_ucode(sc, 1);
99502ad0f7e9STom Jones 	iwx_stop_device(sc);
99512ad0f7e9STom Jones 	if (err) {
99522ad0f7e9STom Jones 		printf("%s: failed to stop device\n", DEVNAME(sc));
99532ad0f7e9STom Jones 		return err;
99542ad0f7e9STom Jones 	}
99552ad0f7e9STom Jones 
99562ad0f7e9STom Jones 	/* Print version info and MAC address on first successful fw load. */
99572ad0f7e9STom Jones 	sc->attached = 1;
99582ad0f7e9STom Jones 	if (sc->sc_pnvm_ver) {
99592ad0f7e9STom Jones 		printf("%s: hw rev 0x%x, fw %s, pnvm %08x, "
99602ad0f7e9STom Jones 		    "address %s\n",
99612ad0f7e9STom Jones 		    DEVNAME(sc), sc->sc_hw_rev & IWX_CSR_HW_REV_TYPE_MSK,
99622ad0f7e9STom Jones 		    sc->sc_fwver, sc->sc_pnvm_ver,
99632ad0f7e9STom Jones 		    ether_sprintf(sc->sc_nvm.hw_addr));
99642ad0f7e9STom Jones 	} else {
99652ad0f7e9STom Jones 		printf("%s: hw rev 0x%x, fw %s, address %s\n",
99662ad0f7e9STom Jones 		    DEVNAME(sc), sc->sc_hw_rev & IWX_CSR_HW_REV_TYPE_MSK,
99672ad0f7e9STom Jones 		    sc->sc_fwver, ether_sprintf(sc->sc_nvm.hw_addr));
99682ad0f7e9STom Jones 	}
99692ad0f7e9STom Jones 
99702ad0f7e9STom Jones 	/* not all hardware can do 5GHz band */
99712ad0f7e9STom Jones 	if (!sc->sc_nvm.sku_cap_band_52GHz_enable)
99722ad0f7e9STom Jones 		memset(&ic->ic_sup_rates[IEEE80211_MODE_11A], 0,
99732ad0f7e9STom Jones 		    sizeof(ic->ic_sup_rates[IEEE80211_MODE_11A]));
99742ad0f7e9STom Jones 
99752ad0f7e9STom Jones 	return 0;
99762ad0f7e9STom Jones }
99772ad0f7e9STom Jones 
99782ad0f7e9STom Jones static void
99792ad0f7e9STom Jones iwx_attach_hook(void *self)
99802ad0f7e9STom Jones {
99812ad0f7e9STom Jones 	struct iwx_softc *sc = (void *)self;
99822ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
99832ad0f7e9STom Jones 	int err;
99842ad0f7e9STom Jones 
99852ad0f7e9STom Jones 	IWX_LOCK(sc);
99862ad0f7e9STom Jones 	err = iwx_preinit(sc);
99872ad0f7e9STom Jones 	IWX_UNLOCK(sc);
99882ad0f7e9STom Jones 	if (err != 0)
99892ad0f7e9STom Jones 		goto out;
99902ad0f7e9STom Jones 
99912ad0f7e9STom Jones 	iwx_init_channel_map(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans,
99922ad0f7e9STom Jones 	    ic->ic_channels);
99932ad0f7e9STom Jones 
99942ad0f7e9STom Jones 	ieee80211_ifattach(ic);
99952ad0f7e9STom Jones 	ic->ic_vap_create = iwx_vap_create;
99962ad0f7e9STom Jones 	ic->ic_vap_delete = iwx_vap_delete;
99972ad0f7e9STom Jones 	ic->ic_raw_xmit = iwx_raw_xmit;
99982ad0f7e9STom Jones 	ic->ic_node_alloc = iwx_node_alloc;
99992ad0f7e9STom Jones 	ic->ic_scan_start = iwx_scan_start;
100002ad0f7e9STom Jones 	ic->ic_scan_end = iwx_scan_end;
100012ad0f7e9STom Jones 	ic->ic_update_mcast = iwx_update_mcast;
100022ad0f7e9STom Jones 	ic->ic_getradiocaps = iwx_init_channel_map;
100032ad0f7e9STom Jones 
100042ad0f7e9STom Jones 	ic->ic_set_channel = iwx_set_channel;
100052ad0f7e9STom Jones 	ic->ic_scan_curchan = iwx_scan_curchan;
100062ad0f7e9STom Jones 	ic->ic_scan_mindwell = iwx_scan_mindwell;
100072ad0f7e9STom Jones 	ic->ic_wme.wme_update = iwx_wme_update;
100082ad0f7e9STom Jones 	ic->ic_parent = iwx_parent;
100092ad0f7e9STom Jones 	ic->ic_transmit = iwx_transmit;
100102ad0f7e9STom Jones 
100112ad0f7e9STom Jones 	sc->sc_ampdu_rx_start = ic->ic_ampdu_rx_start;
100122ad0f7e9STom Jones 	ic->ic_ampdu_rx_start = iwx_ampdu_rx_start;
100132ad0f7e9STom Jones 	sc->sc_ampdu_rx_stop = ic->ic_ampdu_rx_stop;
100142ad0f7e9STom Jones 	ic->ic_ampdu_rx_stop = iwx_ampdu_rx_stop;
100152ad0f7e9STom Jones 
100162ad0f7e9STom Jones 	sc->sc_addba_request = ic->ic_addba_request;
100172ad0f7e9STom Jones 	ic->ic_addba_request = iwx_addba_request;
100182ad0f7e9STom Jones 	sc->sc_addba_response = ic->ic_addba_response;
100192ad0f7e9STom Jones 	ic->ic_addba_response = iwx_addba_response;
100202ad0f7e9STom Jones 
100212ad0f7e9STom Jones 	iwx_radiotap_attach(sc);
100222ad0f7e9STom Jones 	ieee80211_announce(ic);
100232ad0f7e9STom Jones out:
100242ad0f7e9STom Jones 	config_intrhook_disestablish(&sc->sc_preinit_hook);
100252ad0f7e9STom Jones }
100262ad0f7e9STom Jones 
100272ad0f7e9STom Jones const struct iwx_device_cfg *
100282ad0f7e9STom Jones iwx_find_device_cfg(struct iwx_softc *sc)
100292ad0f7e9STom Jones {
100301110ed3bSTom Jones 	uint16_t sdev_id, mac_type, rf_type;
100312ad0f7e9STom Jones 	uint8_t mac_step, cdb, jacket, rf_id, no_160, cores;
100322ad0f7e9STom Jones 	int i;
100332ad0f7e9STom Jones 
100341110ed3bSTom Jones 	sdev_id = pci_get_subdevice(sc->sc_dev);
100352ad0f7e9STom Jones 	mac_type = IWX_CSR_HW_REV_TYPE(sc->sc_hw_rev);
100362ad0f7e9STom Jones 	mac_step = IWX_CSR_HW_REV_STEP(sc->sc_hw_rev << 2);
100372ad0f7e9STom Jones 	rf_type = IWX_CSR_HW_RFID_TYPE(sc->sc_hw_rf_id);
100382ad0f7e9STom Jones 	cdb = IWX_CSR_HW_RFID_IS_CDB(sc->sc_hw_rf_id);
100392ad0f7e9STom Jones 	jacket = IWX_CSR_HW_RFID_IS_JACKET(sc->sc_hw_rf_id);
100402ad0f7e9STom Jones 
100412ad0f7e9STom Jones 	rf_id = IWX_SUBDEVICE_RF_ID(sdev_id);
100422ad0f7e9STom Jones 	no_160 = IWX_SUBDEVICE_NO_160(sdev_id);
100432ad0f7e9STom Jones 	cores = IWX_SUBDEVICE_CORES(sdev_id);
100442ad0f7e9STom Jones 
100452ad0f7e9STom Jones 	for (i = nitems(iwx_dev_info_table) - 1; i >= 0; i--) {
100462ad0f7e9STom Jones 		 const struct iwx_dev_info *dev_info = &iwx_dev_info_table[i];
100472ad0f7e9STom Jones 
100482ad0f7e9STom Jones 		if (dev_info->device != (uint16_t)IWX_CFG_ANY &&
100492ad0f7e9STom Jones 		    dev_info->device != sc->sc_pid)
100502ad0f7e9STom Jones 			continue;
100512ad0f7e9STom Jones 
100522ad0f7e9STom Jones 		if (dev_info->subdevice != (uint16_t)IWX_CFG_ANY &&
100532ad0f7e9STom Jones 		    dev_info->subdevice != sdev_id)
100542ad0f7e9STom Jones 			continue;
100552ad0f7e9STom Jones 
100562ad0f7e9STom Jones 		if (dev_info->mac_type != (uint16_t)IWX_CFG_ANY &&
100572ad0f7e9STom Jones 		    dev_info->mac_type != mac_type)
100582ad0f7e9STom Jones 			continue;
100592ad0f7e9STom Jones 
100602ad0f7e9STom Jones 		if (dev_info->mac_step != (uint8_t)IWX_CFG_ANY &&
100612ad0f7e9STom Jones 		    dev_info->mac_step != mac_step)
100622ad0f7e9STom Jones 			continue;
100632ad0f7e9STom Jones 
100642ad0f7e9STom Jones 		if (dev_info->rf_type != (uint16_t)IWX_CFG_ANY &&
100652ad0f7e9STom Jones 		    dev_info->rf_type != rf_type)
100662ad0f7e9STom Jones 			continue;
100672ad0f7e9STom Jones 
100682ad0f7e9STom Jones 		if (dev_info->cdb != (uint8_t)IWX_CFG_ANY &&
100692ad0f7e9STom Jones 		    dev_info->cdb != cdb)
100702ad0f7e9STom Jones 			continue;
100712ad0f7e9STom Jones 
100722ad0f7e9STom Jones 		if (dev_info->jacket != (uint8_t)IWX_CFG_ANY &&
100732ad0f7e9STom Jones 		    dev_info->jacket != jacket)
100742ad0f7e9STom Jones 			continue;
100752ad0f7e9STom Jones 
100762ad0f7e9STom Jones 		if (dev_info->rf_id != (uint8_t)IWX_CFG_ANY &&
100772ad0f7e9STom Jones 		    dev_info->rf_id != rf_id)
100782ad0f7e9STom Jones 			continue;
100792ad0f7e9STom Jones 
100802ad0f7e9STom Jones 		if (dev_info->no_160 != (uint8_t)IWX_CFG_ANY &&
100812ad0f7e9STom Jones 		    dev_info->no_160 != no_160)
100822ad0f7e9STom Jones 			continue;
100832ad0f7e9STom Jones 
100842ad0f7e9STom Jones 		if (dev_info->cores != (uint8_t)IWX_CFG_ANY &&
100852ad0f7e9STom Jones 		    dev_info->cores != cores)
100862ad0f7e9STom Jones 			continue;
100872ad0f7e9STom Jones 
100882ad0f7e9STom Jones 		return dev_info->cfg;
100892ad0f7e9STom Jones 	}
100902ad0f7e9STom Jones 
100912ad0f7e9STom Jones 	return NULL;
100922ad0f7e9STom Jones }
100932ad0f7e9STom Jones 
100942ad0f7e9STom Jones static int
100952ad0f7e9STom Jones iwx_probe(device_t dev)
100962ad0f7e9STom Jones {
100972ad0f7e9STom Jones 	int i;
100982ad0f7e9STom Jones 
100992ad0f7e9STom Jones 	for (i = 0; i < nitems(iwx_devices); i++) {
101002ad0f7e9STom Jones 		if (pci_get_vendor(dev) == PCI_VENDOR_INTEL &&
101012ad0f7e9STom Jones 		    pci_get_device(dev) == iwx_devices[i].device) {
101022ad0f7e9STom Jones 			device_set_desc(dev, iwx_devices[i].name);
101032ad0f7e9STom Jones 
101042ad0f7e9STom Jones 			/*
101052ad0f7e9STom Jones 			 * Due to significant existing deployments using
101062ad0f7e9STom Jones 			 * iwlwifi lower the priority of iwx.
101072ad0f7e9STom Jones 			 *
101082ad0f7e9STom Jones 			 * This inverts the advice in bus.h where drivers
101092ad0f7e9STom Jones 			 * supporting newer hardware should return
101102ad0f7e9STom Jones 			 * BUS_PROBE_DEFAULT and drivers for older devices
101112ad0f7e9STom Jones 			 * return BUS_PROBE_LOW_PRIORITY.
101122ad0f7e9STom Jones 			 *
101132ad0f7e9STom Jones 			 */
101142ad0f7e9STom Jones 			return (BUS_PROBE_LOW_PRIORITY);
101152ad0f7e9STom Jones 		}
101162ad0f7e9STom Jones 	}
101172ad0f7e9STom Jones 
101182ad0f7e9STom Jones 	return (ENXIO);
101192ad0f7e9STom Jones }
101202ad0f7e9STom Jones 
101212ad0f7e9STom Jones static int
101222ad0f7e9STom Jones iwx_attach(device_t dev)
101232ad0f7e9STom Jones {
101242ad0f7e9STom Jones 	struct iwx_softc *sc = device_get_softc(dev);
101252ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
101262ad0f7e9STom Jones 	const struct iwx_device_cfg *cfg;
101272ad0f7e9STom Jones 	int err;
101282ad0f7e9STom Jones 	int txq_i, i, j;
101292ad0f7e9STom Jones 	size_t ctxt_info_size;
101302ad0f7e9STom Jones 	int rid;
101312ad0f7e9STom Jones 	int count;
101322ad0f7e9STom Jones 	int error;
101332ad0f7e9STom Jones 	sc->sc_dev = dev;
101342ad0f7e9STom Jones 	sc->sc_pid = pci_get_device(dev);
101352ad0f7e9STom Jones 	sc->sc_dmat = bus_get_dma_tag(sc->sc_dev);
101362ad0f7e9STom Jones 
101372ad0f7e9STom Jones 	TASK_INIT(&sc->sc_es_task, 0, iwx_endscan_cb, sc);
101382ad0f7e9STom Jones 	IWX_LOCK_INIT(sc);
101392ad0f7e9STom Jones 	mbufq_init(&sc->sc_snd, ifqmaxlen);
101402ad0f7e9STom Jones 	TASK_INIT(&sc->ba_rx_task, 0, iwx_ba_rx_task, sc);
101412ad0f7e9STom Jones 	TASK_INIT(&sc->ba_tx_task, 0, iwx_ba_tx_task, sc);
101422ad0f7e9STom Jones 	sc->sc_tq = taskqueue_create("iwm_taskq", M_WAITOK,
101432ad0f7e9STom Jones 	    taskqueue_thread_enqueue, &sc->sc_tq);
101442ad0f7e9STom Jones 	error = taskqueue_start_threads(&sc->sc_tq, 1, 0, "iwm_taskq");
101452ad0f7e9STom Jones 	if (error != 0) {
101462ad0f7e9STom Jones 		device_printf(dev, "can't start taskq thread, error %d\n",
101472ad0f7e9STom Jones 		    error);
101482ad0f7e9STom Jones 		return (ENXIO);
101492ad0f7e9STom Jones 	}
101502ad0f7e9STom Jones 
101512ad0f7e9STom Jones 	pci_find_cap(dev, PCIY_EXPRESS, &sc->sc_cap_off);
101522ad0f7e9STom Jones 	if (sc->sc_cap_off == 0) {
101532ad0f7e9STom Jones 		device_printf(dev, "PCIe capability structure not found!\n");
101542ad0f7e9STom Jones 		return (ENXIO);
101552ad0f7e9STom Jones 	}
101562ad0f7e9STom Jones 
101572ad0f7e9STom Jones 	/*
101582ad0f7e9STom Jones 	 * We disable the RETRY_TIMEOUT register (0x41) to keep
101592ad0f7e9STom Jones 	 * PCI Tx retries from interfering with C3 CPU state.
101602ad0f7e9STom Jones 	 */
101612ad0f7e9STom Jones 	pci_write_config(dev, PCI_CFG_RETRY_TIMEOUT, 0x00, 1);
101622ad0f7e9STom Jones 
101632ad0f7e9STom Jones 	if (pci_msix_count(dev)) {
101642ad0f7e9STom Jones 		sc->sc_msix = 1;
101652ad0f7e9STom Jones 	} else {
101662ad0f7e9STom Jones 		device_printf(dev, "no MSI-X found\n");
101672ad0f7e9STom Jones 		return (ENXIO);
101682ad0f7e9STom Jones 	}
101692ad0f7e9STom Jones 
101702ad0f7e9STom Jones 	pci_enable_busmaster(dev);
101712ad0f7e9STom Jones 	rid = PCIR_BAR(0);
101722ad0f7e9STom Jones 	sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
101732ad0f7e9STom Jones 	    RF_ACTIVE);
101742ad0f7e9STom Jones 	if (sc->sc_mem == NULL) {
101752ad0f7e9STom Jones 		device_printf(sc->sc_dev, "can't map mem space\n");
101762ad0f7e9STom Jones 		return (ENXIO);
101772ad0f7e9STom Jones 	}
101782ad0f7e9STom Jones 	sc->sc_st = rman_get_bustag(sc->sc_mem);
101792ad0f7e9STom Jones 	sc->sc_sh = rman_get_bushandle(sc->sc_mem);
101802ad0f7e9STom Jones 
101812ad0f7e9STom Jones 	count = 1;
101822ad0f7e9STom Jones 	rid = 0;
101832ad0f7e9STom Jones 	if (pci_alloc_msix(dev, &count) == 0)
101842ad0f7e9STom Jones 		rid = 1;
101852ad0f7e9STom Jones 	DPRINTF(("%s: count=%d\n", __func__, count));
101862ad0f7e9STom Jones 	sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE |
101872ad0f7e9STom Jones 	    (rid != 0 ? 0 : RF_SHAREABLE));
101882ad0f7e9STom Jones 	if (sc->sc_irq == NULL) {
101892ad0f7e9STom Jones 		device_printf(dev, "can't map interrupt\n");
101902ad0f7e9STom Jones 		return (ENXIO);
101912ad0f7e9STom Jones 	}
101922ad0f7e9STom Jones 	error = bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_NET | INTR_MPSAFE,
101932ad0f7e9STom Jones 	    NULL, iwx_intr_msix, sc, &sc->sc_ih);
101942ad0f7e9STom Jones 	if (error != 0) {
101952ad0f7e9STom Jones 		device_printf(dev, "can't establish interrupt\n");
101962ad0f7e9STom Jones 		return (ENXIO);
101972ad0f7e9STom Jones 	}
101982ad0f7e9STom Jones 
101992ad0f7e9STom Jones 	/* Clear pending interrupts. */
102002ad0f7e9STom Jones 	IWX_WRITE(sc, IWX_CSR_INT_MASK, 0);
102012ad0f7e9STom Jones 	IWX_WRITE(sc, IWX_CSR_INT, ~0);
102022ad0f7e9STom Jones 	IWX_WRITE(sc, IWX_CSR_FH_INT_STATUS, ~0);
102032ad0f7e9STom Jones 
102042ad0f7e9STom Jones 	sc->sc_hw_rev = IWX_READ(sc, IWX_CSR_HW_REV);
102052ad0f7e9STom Jones 	DPRINTF(("%s: sc->sc_hw_rev=%d\n", __func__, sc->sc_hw_rev));
102062ad0f7e9STom Jones 	sc->sc_hw_rf_id = IWX_READ(sc, IWX_CSR_HW_RF_ID);
102072ad0f7e9STom Jones 	DPRINTF(("%s: sc->sc_hw_rf_id =%d\n", __func__, sc->sc_hw_rf_id));
102082ad0f7e9STom Jones 
102092ad0f7e9STom Jones 	/*
102102ad0f7e9STom Jones 	 * In the 8000 HW family the format of the 4 bytes of CSR_HW_REV have
102112ad0f7e9STom Jones 	 * changed, and now the revision step also includes bit 0-1 (no more
102122ad0f7e9STom Jones 	 * "dash" value). To keep hw_rev backwards compatible - we'll store it
102132ad0f7e9STom Jones 	 * in the old format.
102142ad0f7e9STom Jones 	 */
102152ad0f7e9STom Jones 	sc->sc_hw_rev = (sc->sc_hw_rev & 0xfff0) |
102162ad0f7e9STom Jones 			(IWX_CSR_HW_REV_STEP(sc->sc_hw_rev << 2) << 2);
102172ad0f7e9STom Jones 
102182ad0f7e9STom Jones 	switch (sc->sc_pid) {
102192ad0f7e9STom Jones 	case PCI_PRODUCT_INTEL_WL_22500_1:
102202ad0f7e9STom Jones 		sc->sc_fwname = IWX_CC_A_FW;
102212ad0f7e9STom Jones 		sc->sc_device_family = IWX_DEVICE_FAMILY_22000;
102222ad0f7e9STom Jones 		sc->sc_integrated = 0;
102232ad0f7e9STom Jones 		sc->sc_ltr_delay = IWX_SOC_FLAGS_LTR_APPLY_DELAY_NONE;
102242ad0f7e9STom Jones 		sc->sc_low_latency_xtal = 0;
102252ad0f7e9STom Jones 		sc->sc_xtal_latency = 0;
102262ad0f7e9STom Jones 		sc->sc_tx_with_siso_diversity = 0;
102272ad0f7e9STom Jones 		sc->sc_uhb_supported = 0;
102282ad0f7e9STom Jones 		break;
102292ad0f7e9STom Jones 	case PCI_PRODUCT_INTEL_WL_22500_2:
102302ad0f7e9STom Jones 	case PCI_PRODUCT_INTEL_WL_22500_5:
102312ad0f7e9STom Jones 		/* These devices should be QuZ only. */
102322ad0f7e9STom Jones 		if (sc->sc_hw_rev != IWX_CSR_HW_REV_TYPE_QUZ) {
102332ad0f7e9STom Jones 			device_printf(dev, "unsupported AX201 adapter\n");
102342ad0f7e9STom Jones 			return (ENXIO);
102352ad0f7e9STom Jones 		}
102362ad0f7e9STom Jones 		sc->sc_fwname = IWX_QUZ_A_HR_B_FW;
102372ad0f7e9STom Jones 		sc->sc_device_family = IWX_DEVICE_FAMILY_22000;
102382ad0f7e9STom Jones 		sc->sc_integrated = 1;
102392ad0f7e9STom Jones 		sc->sc_ltr_delay = IWX_SOC_FLAGS_LTR_APPLY_DELAY_200;
102402ad0f7e9STom Jones 		sc->sc_low_latency_xtal = 0;
102412ad0f7e9STom Jones 		sc->sc_xtal_latency = 500;
102422ad0f7e9STom Jones 		sc->sc_tx_with_siso_diversity = 0;
102432ad0f7e9STom Jones 		sc->sc_uhb_supported = 0;
102442ad0f7e9STom Jones 		break;
102452ad0f7e9STom Jones 	case PCI_PRODUCT_INTEL_WL_22500_3:
102462ad0f7e9STom Jones 		if (sc->sc_hw_rev == IWX_CSR_HW_REV_TYPE_QU_C0)
102472ad0f7e9STom Jones 			sc->sc_fwname = IWX_QU_C_HR_B_FW;
102482ad0f7e9STom Jones 		else if (sc->sc_hw_rev == IWX_CSR_HW_REV_TYPE_QUZ)
102492ad0f7e9STom Jones 			sc->sc_fwname = IWX_QUZ_A_HR_B_FW;
102502ad0f7e9STom Jones 		else
102512ad0f7e9STom Jones 			sc->sc_fwname = IWX_QU_B_HR_B_FW;
102522ad0f7e9STom Jones 		sc->sc_device_family = IWX_DEVICE_FAMILY_22000;
102532ad0f7e9STom Jones 		sc->sc_integrated = 1;
102542ad0f7e9STom Jones 		sc->sc_ltr_delay = IWX_SOC_FLAGS_LTR_APPLY_DELAY_200;
102552ad0f7e9STom Jones 		sc->sc_low_latency_xtal = 0;
102562ad0f7e9STom Jones 		sc->sc_xtal_latency = 500;
102572ad0f7e9STom Jones 		sc->sc_tx_with_siso_diversity = 0;
102582ad0f7e9STom Jones 		sc->sc_uhb_supported = 0;
102592ad0f7e9STom Jones 		break;
102602ad0f7e9STom Jones 	case PCI_PRODUCT_INTEL_WL_22500_4:
102612ad0f7e9STom Jones 	case PCI_PRODUCT_INTEL_WL_22500_7:
102622ad0f7e9STom Jones 	case PCI_PRODUCT_INTEL_WL_22500_8:
102632ad0f7e9STom Jones 		if (sc->sc_hw_rev == IWX_CSR_HW_REV_TYPE_QU_C0)
102642ad0f7e9STom Jones 			sc->sc_fwname = IWX_QU_C_HR_B_FW;
102652ad0f7e9STom Jones 		else if (sc->sc_hw_rev == IWX_CSR_HW_REV_TYPE_QUZ)
102662ad0f7e9STom Jones 			sc->sc_fwname = IWX_QUZ_A_HR_B_FW;
102672ad0f7e9STom Jones 		else
102682ad0f7e9STom Jones 			sc->sc_fwname = IWX_QU_B_HR_B_FW;
102692ad0f7e9STom Jones 		sc->sc_device_family = IWX_DEVICE_FAMILY_22000;
102702ad0f7e9STom Jones 		sc->sc_integrated = 1;
102712ad0f7e9STom Jones 		sc->sc_ltr_delay = IWX_SOC_FLAGS_LTR_APPLY_DELAY_1820;
102722ad0f7e9STom Jones 		sc->sc_low_latency_xtal = 0;
102732ad0f7e9STom Jones 		sc->sc_xtal_latency = 1820;
102742ad0f7e9STom Jones 		sc->sc_tx_with_siso_diversity = 0;
102752ad0f7e9STom Jones 		sc->sc_uhb_supported = 0;
102762ad0f7e9STom Jones 		break;
102772ad0f7e9STom Jones 	case PCI_PRODUCT_INTEL_WL_22500_6:
102782ad0f7e9STom Jones 		if (sc->sc_hw_rev == IWX_CSR_HW_REV_TYPE_QU_C0)
102792ad0f7e9STom Jones 			sc->sc_fwname = IWX_QU_C_HR_B_FW;
102802ad0f7e9STom Jones 		else if (sc->sc_hw_rev == IWX_CSR_HW_REV_TYPE_QUZ)
102812ad0f7e9STom Jones 			sc->sc_fwname = IWX_QUZ_A_HR_B_FW;
102822ad0f7e9STom Jones 		else
102832ad0f7e9STom Jones 			sc->sc_fwname = IWX_QU_B_HR_B_FW;
102842ad0f7e9STom Jones 		sc->sc_device_family = IWX_DEVICE_FAMILY_22000;
102852ad0f7e9STom Jones 		sc->sc_integrated = 1;
102862ad0f7e9STom Jones 		sc->sc_ltr_delay = IWX_SOC_FLAGS_LTR_APPLY_DELAY_2500;
102872ad0f7e9STom Jones 		sc->sc_low_latency_xtal = 1;
102882ad0f7e9STom Jones 		sc->sc_xtal_latency = 12000;
102892ad0f7e9STom Jones 		sc->sc_tx_with_siso_diversity = 0;
102902ad0f7e9STom Jones 		sc->sc_uhb_supported = 0;
102912ad0f7e9STom Jones 		break;
102922ad0f7e9STom Jones 	case PCI_PRODUCT_INTEL_WL_22500_9:
102932ad0f7e9STom Jones 	case PCI_PRODUCT_INTEL_WL_22500_10:
102942ad0f7e9STom Jones 	case PCI_PRODUCT_INTEL_WL_22500_11:
102952ad0f7e9STom Jones 	case PCI_PRODUCT_INTEL_WL_22500_13:
102962ad0f7e9STom Jones 	/* _14 is an MA device, not yet supported */
102972ad0f7e9STom Jones 	case PCI_PRODUCT_INTEL_WL_22500_15:
102982ad0f7e9STom Jones 	case PCI_PRODUCT_INTEL_WL_22500_16:
102992ad0f7e9STom Jones 		sc->sc_fwname = IWX_SO_A_GF_A_FW;
103002ad0f7e9STom Jones 		sc->sc_pnvm_name = IWX_SO_A_GF_A_PNVM;
103012ad0f7e9STom Jones 		sc->sc_device_family = IWX_DEVICE_FAMILY_AX210;
103022ad0f7e9STom Jones 		sc->sc_integrated = 0;
103032ad0f7e9STom Jones 		sc->sc_ltr_delay = IWX_SOC_FLAGS_LTR_APPLY_DELAY_NONE;
103042ad0f7e9STom Jones 		sc->sc_low_latency_xtal = 0;
103052ad0f7e9STom Jones 		sc->sc_xtal_latency = 0;
103062ad0f7e9STom Jones 		sc->sc_tx_with_siso_diversity = 0;
103072ad0f7e9STom Jones 		sc->sc_uhb_supported = 1;
103082ad0f7e9STom Jones 		break;
103092ad0f7e9STom Jones 	case PCI_PRODUCT_INTEL_WL_22500_12:
103102ad0f7e9STom Jones 	case PCI_PRODUCT_INTEL_WL_22500_17:
103112ad0f7e9STom Jones 		sc->sc_fwname = IWX_SO_A_GF_A_FW;
103122ad0f7e9STom Jones 		sc->sc_pnvm_name = IWX_SO_A_GF_A_PNVM;
103132ad0f7e9STom Jones 		sc->sc_device_family = IWX_DEVICE_FAMILY_AX210;
103142ad0f7e9STom Jones 		sc->sc_integrated = 1;
103152ad0f7e9STom Jones 		sc->sc_ltr_delay = IWX_SOC_FLAGS_LTR_APPLY_DELAY_2500;
103162ad0f7e9STom Jones 		sc->sc_low_latency_xtal = 1;
103172ad0f7e9STom Jones 		sc->sc_xtal_latency = 12000;
103182ad0f7e9STom Jones 		sc->sc_tx_with_siso_diversity = 0;
103192ad0f7e9STom Jones 		sc->sc_uhb_supported = 0;
103202ad0f7e9STom Jones 		sc->sc_imr_enabled = 1;
103212ad0f7e9STom Jones 		break;
103222ad0f7e9STom Jones 	default:
103232ad0f7e9STom Jones 		device_printf(dev, "unknown adapter type\n");
103242ad0f7e9STom Jones 		return (ENXIO);
103252ad0f7e9STom Jones 	}
103262ad0f7e9STom Jones 
103272ad0f7e9STom Jones 	cfg = iwx_find_device_cfg(sc);
103282ad0f7e9STom Jones 	DPRINTF(("%s: cfg=%p\n", __func__, cfg));
103292ad0f7e9STom Jones 	if (cfg) {
103302ad0f7e9STom Jones 		sc->sc_fwname = cfg->fw_name;
103312ad0f7e9STom Jones 		sc->sc_pnvm_name = cfg->pnvm_name;
103322ad0f7e9STom Jones 		sc->sc_tx_with_siso_diversity = cfg->tx_with_siso_diversity;
103332ad0f7e9STom Jones 		sc->sc_uhb_supported = cfg->uhb_supported;
103342ad0f7e9STom Jones 		if (cfg->xtal_latency) {
103352ad0f7e9STom Jones 			sc->sc_xtal_latency = cfg->xtal_latency;
103362ad0f7e9STom Jones 			sc->sc_low_latency_xtal = cfg->low_latency_xtal;
103372ad0f7e9STom Jones 		}
103382ad0f7e9STom Jones 	}
103392ad0f7e9STom Jones 
103402ad0f7e9STom Jones 	sc->mac_addr_from_csr = 0x380; /* differs on BZ hw generation */
103412ad0f7e9STom Jones 
103422ad0f7e9STom Jones 	if (sc->sc_device_family >= IWX_DEVICE_FAMILY_AX210) {
103432ad0f7e9STom Jones 		sc->sc_umac_prph_offset = 0x300000;
103442ad0f7e9STom Jones 		sc->max_tfd_queue_size = IWX_TFD_QUEUE_SIZE_MAX_GEN3;
103452ad0f7e9STom Jones 	} else
103462ad0f7e9STom Jones 		sc->max_tfd_queue_size = IWX_TFD_QUEUE_SIZE_MAX;
103472ad0f7e9STom Jones 
103482ad0f7e9STom Jones 	/* Allocate DMA memory for loading firmware. */
103492ad0f7e9STom Jones 	if (sc->sc_device_family >= IWX_DEVICE_FAMILY_AX210)
103502ad0f7e9STom Jones 		ctxt_info_size = sizeof(struct iwx_context_info_gen3);
103512ad0f7e9STom Jones 	else
103522ad0f7e9STom Jones 		ctxt_info_size = sizeof(struct iwx_context_info);
103532ad0f7e9STom Jones 	err = iwx_dma_contig_alloc(sc->sc_dmat, &sc->ctxt_info_dma,
103542ad0f7e9STom Jones 	    ctxt_info_size, 1);
103552ad0f7e9STom Jones 	if (err) {
103562ad0f7e9STom Jones 		device_printf(dev,
103572ad0f7e9STom Jones 		    "could not allocate memory for loading firmware\n");
103582ad0f7e9STom Jones 		return (ENXIO);
103592ad0f7e9STom Jones 	}
103602ad0f7e9STom Jones 
103612ad0f7e9STom Jones 	if (sc->sc_device_family >= IWX_DEVICE_FAMILY_AX210) {
103622ad0f7e9STom Jones 		err = iwx_dma_contig_alloc(sc->sc_dmat, &sc->prph_scratch_dma,
103632ad0f7e9STom Jones 		    sizeof(struct iwx_prph_scratch), 1);
103642ad0f7e9STom Jones 		if (err) {
103652ad0f7e9STom Jones 			device_printf(dev,
103662ad0f7e9STom Jones 			    "could not allocate prph scratch memory\n");
103672ad0f7e9STom Jones 			goto fail1;
103682ad0f7e9STom Jones 		}
103692ad0f7e9STom Jones 
103702ad0f7e9STom Jones 		/*
103712ad0f7e9STom Jones 		 * Allocate prph information. The driver doesn't use this.
103722ad0f7e9STom Jones 		 * We use the second half of this page to give the device
103732ad0f7e9STom Jones 		 * some dummy TR/CR tail pointers - which shouldn't be
103742ad0f7e9STom Jones 		 * necessary as we don't use this, but the hardware still
103752ad0f7e9STom Jones 		 * reads/writes there and we can't let it go do that with
103762ad0f7e9STom Jones 		 * a NULL pointer.
103772ad0f7e9STom Jones 		 */
103782ad0f7e9STom Jones 		KASSERT((sizeof(struct iwx_prph_info) < PAGE_SIZE / 2),
103792ad0f7e9STom Jones 		    ("iwx_prph_info has wrong size"));
103802ad0f7e9STom Jones 		err = iwx_dma_contig_alloc(sc->sc_dmat, &sc->prph_info_dma,
103812ad0f7e9STom Jones 		    PAGE_SIZE, 1);
103822ad0f7e9STom Jones 		if (err) {
103832ad0f7e9STom Jones 			device_printf(dev,
103842ad0f7e9STom Jones 			    "could not allocate prph info memory\n");
103852ad0f7e9STom Jones 			goto fail1;
103862ad0f7e9STom Jones 		}
103872ad0f7e9STom Jones 	}
103882ad0f7e9STom Jones 
103892ad0f7e9STom Jones 	/* Allocate interrupt cause table (ICT).*/
103902ad0f7e9STom Jones 	err = iwx_dma_contig_alloc(sc->sc_dmat, &sc->ict_dma,
103912ad0f7e9STom Jones 	    IWX_ICT_SIZE, 1<<IWX_ICT_PADDR_SHIFT);
103922ad0f7e9STom Jones 	if (err) {
103932ad0f7e9STom Jones 		device_printf(dev, "could not allocate ICT table\n");
103942ad0f7e9STom Jones 		goto fail1;
103952ad0f7e9STom Jones 	}
103962ad0f7e9STom Jones 
103972ad0f7e9STom Jones 	for (txq_i = 0; txq_i < nitems(sc->txq); txq_i++) {
103982ad0f7e9STom Jones 		err = iwx_alloc_tx_ring(sc, &sc->txq[txq_i], txq_i);
103992ad0f7e9STom Jones 		if (err) {
104002ad0f7e9STom Jones 			device_printf(dev, "could not allocate TX ring %d\n",
104012ad0f7e9STom Jones 			    txq_i);
104022ad0f7e9STom Jones 			goto fail4;
104032ad0f7e9STom Jones 		}
104042ad0f7e9STom Jones 	}
104052ad0f7e9STom Jones 
104062ad0f7e9STom Jones 	err = iwx_alloc_rx_ring(sc, &sc->rxq);
104072ad0f7e9STom Jones 	if (err) {
104082ad0f7e9STom Jones 		device_printf(sc->sc_dev, "could not allocate RX ring\n");
104092ad0f7e9STom Jones 		goto fail4;
104102ad0f7e9STom Jones 	}
104112ad0f7e9STom Jones 
104122ad0f7e9STom Jones #ifdef IWX_DEBUG
104132ad0f7e9STom Jones 	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
104142ad0f7e9STom Jones 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "debug",
104152ad0f7e9STom Jones 	    CTLFLAG_RWTUN, &sc->sc_debug, 0, "bitmask to control debugging");
104162ad0f7e9STom Jones 
104172ad0f7e9STom Jones 	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
104182ad0f7e9STom Jones 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "himark",
104192ad0f7e9STom Jones 	    CTLFLAG_RW, &iwx_himark, 0, "queues high watermark");
104202ad0f7e9STom Jones 	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
104212ad0f7e9STom Jones 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "lomark",
104222ad0f7e9STom Jones 	    CTLFLAG_RW, &iwx_lomark, 0, "queues low watermark");
104232ad0f7e9STom Jones 
104242ad0f7e9STom Jones 	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
104252ad0f7e9STom Jones 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "qfullmsk",
104262ad0f7e9STom Jones 	    CTLFLAG_RD, &sc->qfullmsk, 0, "queue fullmask");
104272ad0f7e9STom Jones 
104282ad0f7e9STom Jones 	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
104292ad0f7e9STom Jones 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "queue0",
104302ad0f7e9STom Jones 	    CTLFLAG_RD, &sc->txq[0].queued, 0, "queue 0");
104312ad0f7e9STom Jones 	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
104322ad0f7e9STom Jones 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "queue1",
104332ad0f7e9STom Jones 	    CTLFLAG_RD, &sc->txq[1].queued, 0, "queue 1");
104342ad0f7e9STom Jones 	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
104352ad0f7e9STom Jones 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "queue2",
104362ad0f7e9STom Jones 	    CTLFLAG_RD, &sc->txq[2].queued, 0, "queue 2");
104372ad0f7e9STom Jones 	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
104382ad0f7e9STom Jones 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "queue3",
104392ad0f7e9STom Jones 	    CTLFLAG_RD, &sc->txq[3].queued, 0, "queue 3");
104402ad0f7e9STom Jones 	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
104412ad0f7e9STom Jones 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "queue4",
104422ad0f7e9STom Jones 	    CTLFLAG_RD, &sc->txq[4].queued, 0, "queue 4");
104432ad0f7e9STom Jones 	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
104442ad0f7e9STom Jones 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "queue5",
104452ad0f7e9STom Jones 	    CTLFLAG_RD, &sc->txq[5].queued, 0, "queue 5");
104462ad0f7e9STom Jones 	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
104472ad0f7e9STom Jones 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "queue6",
104482ad0f7e9STom Jones 	    CTLFLAG_RD, &sc->txq[6].queued, 0, "queue 6");
104492ad0f7e9STom Jones 	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
104502ad0f7e9STom Jones 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "queue7",
104512ad0f7e9STom Jones 	    CTLFLAG_RD, &sc->txq[7].queued, 0, "queue 7");
104522ad0f7e9STom Jones #endif
104532ad0f7e9STom Jones 	ic->ic_softc = sc;
104542ad0f7e9STom Jones 	ic->ic_name = device_get_nameunit(sc->sc_dev);
104552ad0f7e9STom Jones 	ic->ic_phytype = IEEE80211_T_OFDM;	/* not only, but not used */
104562ad0f7e9STom Jones 	ic->ic_opmode = IEEE80211_M_STA;	/* default to BSS mode */
104572ad0f7e9STom Jones 
104582ad0f7e9STom Jones 	/* Set device capabilities. */
104592ad0f7e9STom Jones 	ic->ic_caps =
104602ad0f7e9STom Jones 	    IEEE80211_C_STA |
104612ad0f7e9STom Jones 	    IEEE80211_C_MONITOR |
104622ad0f7e9STom Jones 	    IEEE80211_C_WPA |		/* WPA/RSN */
104632ad0f7e9STom Jones 	    IEEE80211_C_WME |
104642ad0f7e9STom Jones 	    IEEE80211_C_PMGT |
104652ad0f7e9STom Jones 	    IEEE80211_C_SHSLOT |	/* short slot time supported */
104662ad0f7e9STom Jones 	    IEEE80211_C_SHPREAMBLE |	/* short preamble supported */
104672ad0f7e9STom Jones 	    IEEE80211_C_BGSCAN		/* capable of bg scanning */
104682ad0f7e9STom Jones 	    ;
104692ad0f7e9STom Jones 	ic->ic_flags_ext = IEEE80211_FEXT_SCAN_OFFLOAD;
104702ad0f7e9STom Jones 
104712ad0f7e9STom Jones 	ic->ic_txstream = 2;
104722ad0f7e9STom Jones 	ic->ic_rxstream = 2;
104732ad0f7e9STom Jones 	ic->ic_htcaps |= IEEE80211_HTC_HT
104742ad0f7e9STom Jones 			| IEEE80211_HTCAP_SMPS_OFF
104752ad0f7e9STom Jones 			| IEEE80211_HTCAP_SHORTGI20	/* short GI in 20MHz */
104762ad0f7e9STom Jones 			| IEEE80211_HTCAP_SHORTGI40	/* short GI in 40MHz */
104772ad0f7e9STom Jones 			| IEEE80211_HTCAP_CHWIDTH40	/* 40MHz channel width*/
104782ad0f7e9STom Jones 			| IEEE80211_HTC_AMPDU		/* tx A-MPDU */
104792ad0f7e9STom Jones //			| IEEE80211_HTC_RX_AMSDU_AMPDU	/* TODO: hw reorder */
104802ad0f7e9STom Jones 			| IEEE80211_HTCAP_MAXAMSDU_3839;	/* max A-MSDU length */
104812ad0f7e9STom Jones 
104822ad0f7e9STom Jones 	ic->ic_cryptocaps |= IEEE80211_CRYPTO_AES_CCM;
104832ad0f7e9STom Jones 
104842ad0f7e9STom Jones 	/*
104852ad0f7e9STom Jones 	 * XXX: setupcurchan() expects vhtcaps to be non-zero
104862ad0f7e9STom Jones 	 * https://bugs.freebsd.org/274156
104872ad0f7e9STom Jones 	 */
104882ad0f7e9STom Jones 	ic->ic_vht_cap.vht_cap_info |= IEEE80211_VHTCAP_MAX_MPDU_LENGTH_3895
104892ad0f7e9STom Jones 			| IEEE80211_VHTCAP_SHORT_GI_80
104902ad0f7e9STom Jones 			| 3 << IEEE80211_VHTCAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK_S
104912ad0f7e9STom Jones 			| IEEE80211_VHTCAP_RX_ANTENNA_PATTERN
104922ad0f7e9STom Jones 			| IEEE80211_VHTCAP_TX_ANTENNA_PATTERN;
104932ad0f7e9STom Jones 
104942ad0f7e9STom Jones 	ic->ic_flags_ext |= IEEE80211_FEXT_VHT;
104952ad0f7e9STom Jones 	int mcsmap = IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 |
104962ad0f7e9STom Jones 		  IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 |
104972ad0f7e9STom Jones 		  IEEE80211_VHT_MCS_NOT_SUPPORTED << 4 |
104982ad0f7e9STom Jones 		  IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 |
104992ad0f7e9STom Jones 		  IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 |
105002ad0f7e9STom Jones 		  IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 |
105012ad0f7e9STom Jones 		  IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 |
105022ad0f7e9STom Jones 		  IEEE80211_VHT_MCS_NOT_SUPPORTED << 14;
105032ad0f7e9STom Jones 	ic->ic_vht_cap.supp_mcs.tx_mcs_map = htole16(mcsmap);
105042ad0f7e9STom Jones 	ic->ic_vht_cap.supp_mcs.rx_mcs_map = htole16(mcsmap);
105052ad0f7e9STom Jones 
105062ad0f7e9STom Jones 	callout_init_mtx(&sc->watchdog_to, &sc->sc_mtx, 0);
105072ad0f7e9STom Jones 	for (i = 0; i < nitems(sc->sc_rxba_data); i++) {
105082ad0f7e9STom Jones 		struct iwx_rxba_data *rxba = &sc->sc_rxba_data[i];
105092ad0f7e9STom Jones 		rxba->baid = IWX_RX_REORDER_DATA_INVALID_BAID;
105102ad0f7e9STom Jones 		rxba->sc = sc;
105112ad0f7e9STom Jones 		for (j = 0; j < nitems(rxba->entries); j++)
105122ad0f7e9STom Jones 			mbufq_init(&rxba->entries[j].frames, ifqmaxlen);
105132ad0f7e9STom Jones 	}
105142ad0f7e9STom Jones 
105152ad0f7e9STom Jones 	sc->sc_preinit_hook.ich_func = iwx_attach_hook;
105162ad0f7e9STom Jones 	sc->sc_preinit_hook.ich_arg = sc;
105172ad0f7e9STom Jones 	if (config_intrhook_establish(&sc->sc_preinit_hook) != 0) {
105182ad0f7e9STom Jones 		device_printf(dev,
105192ad0f7e9STom Jones 		    "config_intrhook_establish failed\n");
105202ad0f7e9STom Jones 		goto fail4;
105212ad0f7e9STom Jones 	}
105222ad0f7e9STom Jones 
105232ad0f7e9STom Jones 	return (0);
105242ad0f7e9STom Jones 
105252ad0f7e9STom Jones fail4:
105262ad0f7e9STom Jones 	while (--txq_i >= 0)
105272ad0f7e9STom Jones 		iwx_free_tx_ring(sc, &sc->txq[txq_i]);
105282ad0f7e9STom Jones 	iwx_free_rx_ring(sc, &sc->rxq);
105292ad0f7e9STom Jones 	if (sc->ict_dma.vaddr != NULL)
105302ad0f7e9STom Jones 		iwx_dma_contig_free(&sc->ict_dma);
105312ad0f7e9STom Jones 
105322ad0f7e9STom Jones fail1:
105332ad0f7e9STom Jones 	iwx_dma_contig_free(&sc->ctxt_info_dma);
105342ad0f7e9STom Jones 	iwx_dma_contig_free(&sc->prph_scratch_dma);
105352ad0f7e9STom Jones 	iwx_dma_contig_free(&sc->prph_info_dma);
105362ad0f7e9STom Jones 	return (ENXIO);
105372ad0f7e9STom Jones }
105382ad0f7e9STom Jones 
105392ad0f7e9STom Jones static int
105402ad0f7e9STom Jones iwx_detach(device_t dev)
105412ad0f7e9STom Jones {
105422ad0f7e9STom Jones 	struct iwx_softc *sc = device_get_softc(dev);
105432ad0f7e9STom Jones 	int txq_i;
105442ad0f7e9STom Jones 
105452ad0f7e9STom Jones 	iwx_stop_device(sc);
105462ad0f7e9STom Jones 
105472ad0f7e9STom Jones 	taskqueue_drain_all(sc->sc_tq);
105482ad0f7e9STom Jones 	taskqueue_free(sc->sc_tq);
105492ad0f7e9STom Jones 
105502ad0f7e9STom Jones 	ieee80211_ifdetach(&sc->sc_ic);
105512ad0f7e9STom Jones 
105522ad0f7e9STom Jones 	callout_drain(&sc->watchdog_to);
105532ad0f7e9STom Jones 
105542ad0f7e9STom Jones 	for (txq_i = 0; txq_i < nitems(sc->txq); txq_i++)
105552ad0f7e9STom Jones 		iwx_free_tx_ring(sc, &sc->txq[txq_i]);
105562ad0f7e9STom Jones 	iwx_free_rx_ring(sc, &sc->rxq);
105572ad0f7e9STom Jones 
105582ad0f7e9STom Jones 	if (sc->sc_fwp != NULL) {
105592ad0f7e9STom Jones 		firmware_put(sc->sc_fwp, FIRMWARE_UNLOAD);
105602ad0f7e9STom Jones 		sc->sc_fwp = NULL;
105612ad0f7e9STom Jones 	}
105622ad0f7e9STom Jones 
105632ad0f7e9STom Jones 	if (sc->sc_pnvm != NULL) {
105642ad0f7e9STom Jones 		firmware_put(sc->sc_pnvm, FIRMWARE_UNLOAD);
105652ad0f7e9STom Jones 		sc->sc_pnvm = NULL;
105662ad0f7e9STom Jones 	}
105672ad0f7e9STom Jones 
105682ad0f7e9STom Jones 	if (sc->sc_irq != NULL) {
105692ad0f7e9STom Jones 		bus_teardown_intr(dev, sc->sc_irq, sc->sc_ih);
105702ad0f7e9STom Jones 		bus_release_resource(dev, SYS_RES_IRQ,
105712ad0f7e9STom Jones 		    rman_get_rid(sc->sc_irq), sc->sc_irq);
105722ad0f7e9STom Jones 		pci_release_msi(dev);
105732ad0f7e9STom Jones         }
105742ad0f7e9STom Jones 	if (sc->sc_mem != NULL)
105752ad0f7e9STom Jones 		bus_release_resource(dev, SYS_RES_MEMORY,
105762ad0f7e9STom Jones 		    rman_get_rid(sc->sc_mem), sc->sc_mem);
105772ad0f7e9STom Jones 
105782ad0f7e9STom Jones 	IWX_LOCK_DESTROY(sc);
105792ad0f7e9STom Jones 
105802ad0f7e9STom Jones 	return (0);
105812ad0f7e9STom Jones }
105822ad0f7e9STom Jones 
105832ad0f7e9STom Jones static void
105842ad0f7e9STom Jones iwx_radiotap_attach(struct iwx_softc *sc)
105852ad0f7e9STom Jones {
105862ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
105872ad0f7e9STom Jones 
105882ad0f7e9STom Jones 	IWX_DPRINTF(sc, IWX_DEBUG_RESET | IWX_DEBUG_TRACE,
105892ad0f7e9STom Jones 	    "->%s begin\n", __func__);
105902ad0f7e9STom Jones 
105912ad0f7e9STom Jones 	ieee80211_radiotap_attach(ic,
105922ad0f7e9STom Jones 	    &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap),
105932ad0f7e9STom Jones 		IWX_TX_RADIOTAP_PRESENT,
105942ad0f7e9STom Jones 	    &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap),
105952ad0f7e9STom Jones 		IWX_RX_RADIOTAP_PRESENT);
105962ad0f7e9STom Jones 
105972ad0f7e9STom Jones 	IWX_DPRINTF(sc, IWX_DEBUG_RESET | IWX_DEBUG_TRACE,
105982ad0f7e9STom Jones 	    "->%s end\n", __func__);
105992ad0f7e9STom Jones }
106002ad0f7e9STom Jones 
106012ad0f7e9STom Jones struct ieee80211vap *
106022ad0f7e9STom Jones iwx_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
106032ad0f7e9STom Jones     enum ieee80211_opmode opmode, int flags,
106042ad0f7e9STom Jones     const uint8_t bssid[IEEE80211_ADDR_LEN],
106052ad0f7e9STom Jones     const uint8_t mac[IEEE80211_ADDR_LEN])
106062ad0f7e9STom Jones {
106072ad0f7e9STom Jones 	struct iwx_vap *ivp;
106082ad0f7e9STom Jones 	struct ieee80211vap *vap;
106092ad0f7e9STom Jones 
106102ad0f7e9STom Jones 	if (!TAILQ_EMPTY(&ic->ic_vaps))         /* only one at a time */
106112ad0f7e9STom Jones 		return NULL;
106122ad0f7e9STom Jones 	ivp = malloc(sizeof(struct iwx_vap), M_80211_VAP, M_WAITOK | M_ZERO);
106132ad0f7e9STom Jones 	vap = &ivp->iv_vap;
106142ad0f7e9STom Jones 	ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid);
106152ad0f7e9STom Jones 	vap->iv_bmissthreshold = 10;            /* override default */
106162ad0f7e9STom Jones 	/* Override with driver methods. */
106172ad0f7e9STom Jones 	ivp->iv_newstate = vap->iv_newstate;
106182ad0f7e9STom Jones 	vap->iv_newstate = iwx_newstate;
106192ad0f7e9STom Jones 
106202ad0f7e9STom Jones 	ivp->id = IWX_DEFAULT_MACID;
106212ad0f7e9STom Jones 	ivp->color = IWX_DEFAULT_COLOR;
106222ad0f7e9STom Jones 
106232ad0f7e9STom Jones 	ivp->have_wme = TRUE;
106242ad0f7e9STom Jones 	ivp->ps_disabled = FALSE;
106252ad0f7e9STom Jones 
106262ad0f7e9STom Jones 	vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_64K;
106272ad0f7e9STom Jones 	vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_4;
106282ad0f7e9STom Jones 
106292ad0f7e9STom Jones 	/* h/w crypto support */
106302ad0f7e9STom Jones 	vap->iv_key_alloc = iwx_key_alloc;
106312ad0f7e9STom Jones 	vap->iv_key_delete = iwx_key_delete;
106322ad0f7e9STom Jones 	vap->iv_key_set = iwx_key_set;
106332ad0f7e9STom Jones 	vap->iv_key_update_begin = iwx_key_update_begin;
106342ad0f7e9STom Jones 	vap->iv_key_update_end = iwx_key_update_end;
106352ad0f7e9STom Jones 
106362ad0f7e9STom Jones 	ieee80211_ratectl_init(vap);
106372ad0f7e9STom Jones 	/* Complete setup. */
106382ad0f7e9STom Jones 	ieee80211_vap_attach(vap, ieee80211_media_change,
106392ad0f7e9STom Jones 	    ieee80211_media_status, mac);
106402ad0f7e9STom Jones 	ic->ic_opmode = opmode;
106412ad0f7e9STom Jones 
106422ad0f7e9STom Jones 	return vap;
106432ad0f7e9STom Jones }
106442ad0f7e9STom Jones 
106452ad0f7e9STom Jones static void
106462ad0f7e9STom Jones iwx_vap_delete(struct ieee80211vap *vap)
106472ad0f7e9STom Jones {
106482ad0f7e9STom Jones 	struct iwx_vap *ivp = IWX_VAP(vap);
106492ad0f7e9STom Jones 
106502ad0f7e9STom Jones 	ieee80211_ratectl_deinit(vap);
106512ad0f7e9STom Jones 	ieee80211_vap_detach(vap);
106522ad0f7e9STom Jones 	free(ivp, M_80211_VAP);
106532ad0f7e9STom Jones }
106542ad0f7e9STom Jones 
106552ad0f7e9STom Jones static void
106562ad0f7e9STom Jones iwx_parent(struct ieee80211com *ic)
106572ad0f7e9STom Jones {
106582ad0f7e9STom Jones 	struct iwx_softc *sc = ic->ic_softc;
106592ad0f7e9STom Jones 	IWX_LOCK(sc);
106602ad0f7e9STom Jones 
106612ad0f7e9STom Jones 	if (sc->sc_flags & IWX_FLAG_HW_INITED) {
106622ad0f7e9STom Jones 		iwx_stop(sc);
106632ad0f7e9STom Jones 		sc->sc_flags &= ~IWX_FLAG_HW_INITED;
106642ad0f7e9STom Jones 	} else {
106652ad0f7e9STom Jones 		iwx_init(sc);
106662ad0f7e9STom Jones 		ieee80211_start_all(ic);
106672ad0f7e9STom Jones 	}
106682ad0f7e9STom Jones 	IWX_UNLOCK(sc);
106692ad0f7e9STom Jones }
106702ad0f7e9STom Jones 
106712ad0f7e9STom Jones static int
106722ad0f7e9STom Jones iwx_suspend(device_t dev)
106732ad0f7e9STom Jones {
106742ad0f7e9STom Jones 	struct iwx_softc *sc = device_get_softc(dev);
10675969f6c63STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
106762ad0f7e9STom Jones 
106772ad0f7e9STom Jones 	if (sc->sc_flags & IWX_FLAG_HW_INITED) {
10678969f6c63STom Jones 		ieee80211_suspend_all(ic);
10679969f6c63STom Jones 
106802ad0f7e9STom Jones 		iwx_stop(sc);
106812ad0f7e9STom Jones 		sc->sc_flags &= ~IWX_FLAG_HW_INITED;
106822ad0f7e9STom Jones 	}
106832ad0f7e9STom Jones 	return (0);
106842ad0f7e9STom Jones }
106852ad0f7e9STom Jones 
106862ad0f7e9STom Jones static int
106872ad0f7e9STom Jones iwx_resume(device_t dev)
106882ad0f7e9STom Jones {
106892ad0f7e9STom Jones 	struct iwx_softc *sc = device_get_softc(dev);
10690969f6c63STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
106912ad0f7e9STom Jones 	int err;
106922ad0f7e9STom Jones 
10693969f6c63STom Jones 	/*
10694969f6c63STom Jones 	 * We disable the RETRY_TIMEOUT register (0x41) to keep
10695969f6c63STom Jones 	 * PCI Tx retries from interfering with C3 CPU state.
10696969f6c63STom Jones 	 */
10697969f6c63STom Jones 	pci_write_config(dev, PCI_CFG_RETRY_TIMEOUT, 0x00, 1);
106982ad0f7e9STom Jones 
10699969f6c63STom Jones 	IWX_LOCK(sc);
10700969f6c63STom Jones 
10701969f6c63STom Jones 	err = iwx_init(sc);
107022ad0f7e9STom Jones 	if (err) {
107032ad0f7e9STom Jones 		iwx_stop_device(sc);
10704969f6c63STom Jones 		IWX_UNLOCK(sc);
107052ad0f7e9STom Jones 		return err;
107062ad0f7e9STom Jones 	}
107072ad0f7e9STom Jones 
10708969f6c63STom Jones 	IWX_UNLOCK(sc);
107092ad0f7e9STom Jones 
10710969f6c63STom Jones 	ieee80211_resume_all(ic);
107112ad0f7e9STom Jones 	return (0);
107122ad0f7e9STom Jones }
107132ad0f7e9STom Jones 
107142ad0f7e9STom Jones static void
107152ad0f7e9STom Jones iwx_scan_start(struct ieee80211com *ic)
107162ad0f7e9STom Jones {
107172ad0f7e9STom Jones 	struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
107182ad0f7e9STom Jones 	struct iwx_softc *sc = ic->ic_softc;
107192ad0f7e9STom Jones 	int err;
107202ad0f7e9STom Jones 
107212ad0f7e9STom Jones 	IWX_LOCK(sc);
107222ad0f7e9STom Jones 	if ((ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) == 0)
107232ad0f7e9STom Jones 		err = iwx_scan(sc);
107242ad0f7e9STom Jones 	else
107252ad0f7e9STom Jones 		err = iwx_bgscan(ic);
107262ad0f7e9STom Jones 	IWX_UNLOCK(sc);
107272ad0f7e9STom Jones 	if (err)
107282ad0f7e9STom Jones 		ieee80211_cancel_scan(vap);
107292ad0f7e9STom Jones 
107302ad0f7e9STom Jones 	return;
107312ad0f7e9STom Jones }
107322ad0f7e9STom Jones 
107332ad0f7e9STom Jones static void
107342ad0f7e9STom Jones iwx_update_mcast(struct ieee80211com *ic)
107352ad0f7e9STom Jones {
107362ad0f7e9STom Jones }
107372ad0f7e9STom Jones 
107382ad0f7e9STom Jones static void
107392ad0f7e9STom Jones iwx_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
107402ad0f7e9STom Jones {
107412ad0f7e9STom Jones }
107422ad0f7e9STom Jones 
107432ad0f7e9STom Jones static void
107442ad0f7e9STom Jones iwx_scan_mindwell(struct ieee80211_scan_state *ss)
107452ad0f7e9STom Jones {
107462ad0f7e9STom Jones }
107472ad0f7e9STom Jones 
107482ad0f7e9STom Jones static void
107492ad0f7e9STom Jones iwx_scan_end(struct ieee80211com *ic)
107502ad0f7e9STom Jones {
107512ad0f7e9STom Jones 	iwx_endscan(ic->ic_softc);
107522ad0f7e9STom Jones }
107532ad0f7e9STom Jones 
107542ad0f7e9STom Jones static void
107552ad0f7e9STom Jones iwx_set_channel(struct ieee80211com *ic)
107562ad0f7e9STom Jones {
107572ad0f7e9STom Jones #if 0
107582ad0f7e9STom Jones         struct iwx_softc *sc = ic->ic_softc;
107592ad0f7e9STom Jones         struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
107602ad0f7e9STom Jones 
107612ad0f7e9STom Jones         IWX_DPRINTF(sc, IWX_DEBUG_NI , "%s:%d NOT IMPLEMENTED\n", __func__, __LINE__);
107622ad0f7e9STom Jones         iwx_phy_ctxt_task((void *)sc);
107632ad0f7e9STom Jones #endif
107642ad0f7e9STom Jones }
107652ad0f7e9STom Jones 
107662ad0f7e9STom Jones static void
107672ad0f7e9STom Jones iwx_endscan_cb(void *arg, int pending)
107682ad0f7e9STom Jones {
107692ad0f7e9STom Jones 	struct iwx_softc *sc = arg;
107702ad0f7e9STom Jones 	struct ieee80211com *ic = &sc->sc_ic;
107712ad0f7e9STom Jones 
107722ad0f7e9STom Jones 	DPRINTF(("scan ended\n"));
107732ad0f7e9STom Jones 	ieee80211_scan_done(TAILQ_FIRST(&ic->ic_vaps));
107742ad0f7e9STom Jones }
107752ad0f7e9STom Jones 
107762ad0f7e9STom Jones static int
107772ad0f7e9STom Jones iwx_wme_update(struct ieee80211com *ic)
107782ad0f7e9STom Jones {
107792ad0f7e9STom Jones 	return 0;
107802ad0f7e9STom Jones }
107812ad0f7e9STom Jones 
107822ad0f7e9STom Jones static int
107832ad0f7e9STom Jones iwx_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
107842ad0f7e9STom Jones     const struct ieee80211_bpf_params *params)
107852ad0f7e9STom Jones {
107862ad0f7e9STom Jones 	struct ieee80211com *ic = ni->ni_ic;
107872ad0f7e9STom Jones 	struct iwx_softc *sc = ic->ic_softc;
107882ad0f7e9STom Jones 	int err;
107892ad0f7e9STom Jones 
107902ad0f7e9STom Jones 	IWX_LOCK(sc);
107912ad0f7e9STom Jones 	if (sc->sc_flags & IWX_FLAG_STA_ACTIVE) {
107922ad0f7e9STom Jones 		err = iwx_tx(sc, m, ni);
107932ad0f7e9STom Jones 		IWX_UNLOCK(sc);
107942ad0f7e9STom Jones 		return err;
107952ad0f7e9STom Jones 	} else {
107962ad0f7e9STom Jones 		IWX_UNLOCK(sc);
107972ad0f7e9STom Jones 		return EIO;
107982ad0f7e9STom Jones 	}
107992ad0f7e9STom Jones }
108002ad0f7e9STom Jones 
108012ad0f7e9STom Jones static int
108022ad0f7e9STom Jones iwx_transmit(struct ieee80211com *ic, struct mbuf *m)
108032ad0f7e9STom Jones {
108042ad0f7e9STom Jones 	struct iwx_softc *sc = ic->ic_softc;
108052ad0f7e9STom Jones 	int error;
108062ad0f7e9STom Jones 
108072ad0f7e9STom Jones 	// TODO: mbufq_enqueue in iwm
108082ad0f7e9STom Jones 	// TODO dequeue in iwm_start, counters, locking
108092ad0f7e9STom Jones 	IWX_LOCK(sc);
108102ad0f7e9STom Jones 	error = mbufq_enqueue(&sc->sc_snd, m);
108112ad0f7e9STom Jones 	if (error) {
108122ad0f7e9STom Jones 		IWX_UNLOCK(sc);
108132ad0f7e9STom Jones 		return (error);
108142ad0f7e9STom Jones 	}
108152ad0f7e9STom Jones 
108162ad0f7e9STom Jones 	iwx_start(sc);
108172ad0f7e9STom Jones 	IWX_UNLOCK(sc);
108182ad0f7e9STom Jones 	return (0);
108192ad0f7e9STom Jones }
108202ad0f7e9STom Jones 
108212ad0f7e9STom Jones static int
108222ad0f7e9STom Jones iwx_ampdu_rx_start(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap,
108232ad0f7e9STom Jones     int baparamset, int batimeout, int baseqctl)
108242ad0f7e9STom Jones {
108252ad0f7e9STom Jones 	struct ieee80211com *ic = ni->ni_ic;
108262ad0f7e9STom Jones 	struct iwx_softc *sc = ic->ic_softc;
108272ad0f7e9STom Jones 	int tid;
108282ad0f7e9STom Jones 
108292ad0f7e9STom Jones 	tid = _IEEE80211_MASKSHIFT(le16toh(baparamset), IEEE80211_BAPS_TID);
108302ad0f7e9STom Jones 	sc->ni_rx_ba[tid].ba_winstart =
108312ad0f7e9STom Jones 	    _IEEE80211_MASKSHIFT(le16toh(baseqctl), IEEE80211_BASEQ_START);
108322ad0f7e9STom Jones 	sc->ni_rx_ba[tid].ba_winsize =
108332ad0f7e9STom Jones 	    _IEEE80211_MASKSHIFT(le16toh(baparamset), IEEE80211_BAPS_BUFSIZ);
108342ad0f7e9STom Jones 	sc->ni_rx_ba[tid].ba_timeout_val = batimeout;
108352ad0f7e9STom Jones 
108362ad0f7e9STom Jones 	if (sc->sc_rx_ba_sessions >= IWX_MAX_RX_BA_SESSIONS ||
108372ad0f7e9STom Jones 	    tid >= IWX_MAX_TID_COUNT)
108382ad0f7e9STom Jones 		return ENOSPC;
108392ad0f7e9STom Jones 
108402ad0f7e9STom Jones 	if (sc->ba_rx.start_tidmask & (1 << tid)) {
108412ad0f7e9STom Jones 		DPRINTF(("%s: tid %d already added\n", __func__, tid));
108422ad0f7e9STom Jones 		return EBUSY;
108432ad0f7e9STom Jones 	}
108442ad0f7e9STom Jones 	DPRINTF(("%s: sc->ba_rx.start_tidmask=%x\n", __func__, sc->ba_rx.start_tidmask));
108452ad0f7e9STom Jones 
108462ad0f7e9STom Jones 	sc->ba_rx.start_tidmask |= (1 << tid);
108472ad0f7e9STom Jones 	DPRINTF(("%s: tid=%i\n", __func__, tid));
108482ad0f7e9STom Jones 	DPRINTF(("%s: ba_winstart=%i\n", __func__, sc->ni_rx_ba[tid].ba_winstart));
108492ad0f7e9STom Jones 	DPRINTF(("%s: ba_winsize=%i\n", __func__, sc->ni_rx_ba[tid].ba_winsize));
108502ad0f7e9STom Jones 	DPRINTF(("%s: ba_timeout_val=%i\n", __func__, sc->ni_rx_ba[tid].ba_timeout_val));
108512ad0f7e9STom Jones 
108522ad0f7e9STom Jones 	taskqueue_enqueue(sc->sc_tq, &sc->ba_rx_task);
108532ad0f7e9STom Jones 
108542ad0f7e9STom Jones 	// TODO:misha move to ba_task (serialize)
108552ad0f7e9STom Jones 	sc->sc_ampdu_rx_start(ni, rap, baparamset, batimeout, baseqctl);
108562ad0f7e9STom Jones 
108572ad0f7e9STom Jones 	return (0);
108582ad0f7e9STom Jones }
108592ad0f7e9STom Jones 
108602ad0f7e9STom Jones static void
108612ad0f7e9STom Jones iwx_ampdu_rx_stop(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap)
108622ad0f7e9STom Jones {
108632ad0f7e9STom Jones 	return;
108642ad0f7e9STom Jones }
108652ad0f7e9STom Jones 
108662ad0f7e9STom Jones static int
108672ad0f7e9STom Jones iwx_addba_request(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
108682ad0f7e9STom Jones     int dialogtoken, int baparamset, int batimeout)
108692ad0f7e9STom Jones {
108702ad0f7e9STom Jones 	struct iwx_softc *sc = ni->ni_ic->ic_softc;
108712ad0f7e9STom Jones 	int tid;
108722ad0f7e9STom Jones 
108732ad0f7e9STom Jones 	tid = _IEEE80211_MASKSHIFT(le16toh(baparamset), IEEE80211_BAPS_TID);
108742ad0f7e9STom Jones 	DPRINTF(("%s: tid=%i\n", __func__, tid));
108752ad0f7e9STom Jones 	sc->ba_tx.start_tidmask |= (1 << tid);
108762ad0f7e9STom Jones 	taskqueue_enqueue(sc->sc_tq, &sc->ba_tx_task);
108772ad0f7e9STom Jones 	return 0;
108782ad0f7e9STom Jones }
108792ad0f7e9STom Jones 
108802ad0f7e9STom Jones 
108812ad0f7e9STom Jones static int
108822ad0f7e9STom Jones iwx_addba_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
108832ad0f7e9STom Jones     int code, int baparamset, int batimeout)
108842ad0f7e9STom Jones {
108852ad0f7e9STom Jones 	return 0;
108862ad0f7e9STom Jones }
108872ad0f7e9STom Jones 
108882ad0f7e9STom Jones static void
108892ad0f7e9STom Jones iwx_key_update_begin(struct ieee80211vap *vap)
108902ad0f7e9STom Jones {
108912ad0f7e9STom Jones 	return;
108922ad0f7e9STom Jones }
108932ad0f7e9STom Jones 
108942ad0f7e9STom Jones static void
108952ad0f7e9STom Jones iwx_key_update_end(struct ieee80211vap *vap)
108962ad0f7e9STom Jones {
108972ad0f7e9STom Jones 	return;
108982ad0f7e9STom Jones }
108992ad0f7e9STom Jones 
109002ad0f7e9STom Jones static int
109012ad0f7e9STom Jones iwx_key_alloc(struct ieee80211vap *vap, struct ieee80211_key *k,
109022ad0f7e9STom Jones 	ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
109032ad0f7e9STom Jones {
109042ad0f7e9STom Jones 
109052ad0f7e9STom Jones 	if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_AES_CCM) {
109062ad0f7e9STom Jones 		return 1;
109072ad0f7e9STom Jones 	}
109082ad0f7e9STom Jones 	if (!(&vap->iv_nw_keys[0] <= k &&
109092ad0f7e9STom Jones 	     k < &vap->iv_nw_keys[IEEE80211_WEP_NKID])) {
109102ad0f7e9STom Jones 		/*
109112ad0f7e9STom Jones 		 * Not in the global key table, the driver should handle this
109122ad0f7e9STom Jones 		 * by allocating a slot in the h/w key table/cache.  In
109132ad0f7e9STom Jones 		 * lieu of that return key slot 0 for any unicast key
109142ad0f7e9STom Jones 		 * request.  We disallow the request if this is a group key.
109152ad0f7e9STom Jones 		 * This default policy does the right thing for legacy hardware
109162ad0f7e9STom Jones 		 * with a 4 key table.  It also handles devices that pass
109172ad0f7e9STom Jones 		 * packets through untouched when marked with the WEP bit
109182ad0f7e9STom Jones 		 * and key index 0.
109192ad0f7e9STom Jones 		 */
109202ad0f7e9STom Jones 		if (k->wk_flags & IEEE80211_KEY_GROUP)
109212ad0f7e9STom Jones 			return 0;
109222ad0f7e9STom Jones 		*keyix = 0;	/* NB: use key index 0 for ucast key */
109232ad0f7e9STom Jones 	} else {
109242ad0f7e9STom Jones 		*keyix = ieee80211_crypto_get_key_wepidx(vap, k);
109252ad0f7e9STom Jones 	}
109262ad0f7e9STom Jones 	*rxkeyix = IEEE80211_KEYIX_NONE;	/* XXX maybe *keyix? */
109272ad0f7e9STom Jones 	return 1;
109282ad0f7e9STom Jones }
109292ad0f7e9STom Jones 
109302ad0f7e9STom Jones static int
109312ad0f7e9STom Jones iwx_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k)
109322ad0f7e9STom Jones {
109332ad0f7e9STom Jones 	struct ieee80211com *ic = vap->iv_ic;
109342ad0f7e9STom Jones 	struct iwx_softc *sc = ic->ic_softc;
109352ad0f7e9STom Jones 	struct iwx_add_sta_key_cmd cmd;
109362ad0f7e9STom Jones 	uint32_t status;
109372ad0f7e9STom Jones 	int err;
109382ad0f7e9STom Jones 	int id;
109392ad0f7e9STom Jones 
109402ad0f7e9STom Jones 	if (k->wk_cipher->ic_cipher != IEEE80211_CIPHER_AES_CCM) {
109412ad0f7e9STom Jones 		return 1;
109422ad0f7e9STom Jones 	}
109432ad0f7e9STom Jones 
109442ad0f7e9STom Jones 	IWX_LOCK(sc);
109452ad0f7e9STom Jones 	/*
109462ad0f7e9STom Jones 	 * Keys are stored in 'ni' so 'k' is valid if 'ni' is valid.
109472ad0f7e9STom Jones 	 * Currently we only implement station mode where 'ni' is always
109482ad0f7e9STom Jones 	 * ic->ic_bss so there is no need to validate arguments beyond this:
109492ad0f7e9STom Jones 	 */
109502ad0f7e9STom Jones 
109512ad0f7e9STom Jones 	memset(&cmd, 0, sizeof(cmd));
109522ad0f7e9STom Jones 
109532ad0f7e9STom Jones 	if (k->wk_flags & IEEE80211_KEY_GROUP) {
109542ad0f7e9STom Jones 		DPRINTF(("%s: adding group key\n", __func__));
109552ad0f7e9STom Jones 	} else {
109562ad0f7e9STom Jones 		DPRINTF(("%s: adding key\n", __func__));
109572ad0f7e9STom Jones 	}
109582ad0f7e9STom Jones 	if (k >= &vap->iv_nw_keys[0] &&
109592ad0f7e9STom Jones 	    k <  &vap->iv_nw_keys[IEEE80211_WEP_NKID])
109602ad0f7e9STom Jones 		id = (k - vap->iv_nw_keys);
109612ad0f7e9STom Jones 	else
109622ad0f7e9STom Jones 		id = (0);
109632ad0f7e9STom Jones 	DPRINTF(("%s: setting keyid=%i\n", __func__, id));
109642ad0f7e9STom Jones 	cmd.common.key_flags = htole16(IWX_STA_KEY_FLG_CCM |
109652ad0f7e9STom Jones 	    IWX_STA_KEY_FLG_WEP_KEY_MAP |
109662ad0f7e9STom Jones 	    ((id << IWX_STA_KEY_FLG_KEYID_POS) &
109672ad0f7e9STom Jones 	    IWX_STA_KEY_FLG_KEYID_MSK));
109682ad0f7e9STom Jones 	if (k->wk_flags & IEEE80211_KEY_GROUP) {
109692ad0f7e9STom Jones 		cmd.common.key_offset = 1;
109702ad0f7e9STom Jones 		cmd.common.key_flags |= htole16(IWX_STA_KEY_MULTICAST);
109712ad0f7e9STom Jones 	} else {
109722ad0f7e9STom Jones 		cmd.common.key_offset = 0;
109732ad0f7e9STom Jones 	}
109742ad0f7e9STom Jones 	memcpy(cmd.common.key, k->wk_key, MIN(sizeof(cmd.common.key),
109752ad0f7e9STom Jones 	    k->wk_keylen));
109762ad0f7e9STom Jones 	DPRINTF(("%s: wk_keylen=%i\n", __func__, k->wk_keylen));
109772ad0f7e9STom Jones 	for (int i=0; i<k->wk_keylen; i++) {
109782ad0f7e9STom Jones 		DPRINTF(("%s: key[%d]=%x\n", __func__, i, k->wk_key[i]));
109792ad0f7e9STom Jones 	}
109802ad0f7e9STom Jones 	cmd.common.sta_id = IWX_STATION_ID;
109812ad0f7e9STom Jones 
109822ad0f7e9STom Jones 	cmd.transmit_seq_cnt = htole64(k->wk_keytsc);
109832ad0f7e9STom Jones 	DPRINTF(("%s: k->wk_keytsc=%lu\n", __func__, k->wk_keytsc));
109842ad0f7e9STom Jones 
109852ad0f7e9STom Jones 	status = IWX_ADD_STA_SUCCESS;
109862ad0f7e9STom Jones 	err = iwx_send_cmd_pdu_status(sc, IWX_ADD_STA_KEY, sizeof(cmd), &cmd,
109872ad0f7e9STom Jones 	    &status);
109882ad0f7e9STom Jones 	if (!err && (status & IWX_ADD_STA_STATUS_MASK) != IWX_ADD_STA_SUCCESS)
109892ad0f7e9STom Jones 		err = EIO;
109902ad0f7e9STom Jones 	if (err) {
109912ad0f7e9STom Jones 		printf("%s: can't set wpa2 keys (error %d)\n", __func__, err);
109922ad0f7e9STom Jones 		IWX_UNLOCK(sc);
109932ad0f7e9STom Jones 		return err;
109942ad0f7e9STom Jones 	} else
109952ad0f7e9STom Jones 		DPRINTF(("%s: key added successfully\n", __func__));
109962ad0f7e9STom Jones 	IWX_UNLOCK(sc);
109972ad0f7e9STom Jones 	return 1;
109982ad0f7e9STom Jones }
109992ad0f7e9STom Jones 
110002ad0f7e9STom Jones static int
110012ad0f7e9STom Jones iwx_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
110022ad0f7e9STom Jones {
110032ad0f7e9STom Jones 	return 1;
110042ad0f7e9STom Jones }
110052ad0f7e9STom Jones 
110062ad0f7e9STom Jones static device_method_t iwx_pci_methods[] = {
110072ad0f7e9STom Jones 	/* Device interface */
110082ad0f7e9STom Jones 	DEVMETHOD(device_probe,		iwx_probe),
110092ad0f7e9STom Jones 	DEVMETHOD(device_attach,	iwx_attach),
110102ad0f7e9STom Jones 	DEVMETHOD(device_detach,	iwx_detach),
110112ad0f7e9STom Jones 	DEVMETHOD(device_suspend,	iwx_suspend),
110122ad0f7e9STom Jones 	DEVMETHOD(device_resume,	iwx_resume),
110132ad0f7e9STom Jones 
110142ad0f7e9STom Jones 	DEVMETHOD_END
110152ad0f7e9STom Jones };
110162ad0f7e9STom Jones 
110172ad0f7e9STom Jones static driver_t iwx_pci_driver = {
110182ad0f7e9STom Jones 	"iwx",
110192ad0f7e9STom Jones 	iwx_pci_methods,
110202ad0f7e9STom Jones 	sizeof (struct iwx_softc)
110212ad0f7e9STom Jones };
110222ad0f7e9STom Jones 
110232ad0f7e9STom Jones DRIVER_MODULE(iwx, pci, iwx_pci_driver, NULL, NULL);
110242ad0f7e9STom Jones MODULE_PNP_INFO("U16:device;D:#;T:vendor=0x8086", pci, iwx_pci_driver,
110252ad0f7e9STom Jones     iwx_devices, nitems(iwx_devices));
110262ad0f7e9STom Jones MODULE_DEPEND(iwx, firmware, 1, 1, 1);
110272ad0f7e9STom Jones MODULE_DEPEND(iwx, pci, 1, 1, 1);
110282ad0f7e9STom Jones MODULE_DEPEND(iwx, wlan, 1, 1, 1);
11029