ASR_BASE

Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/Makefile b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/Makefile
new file mode 100644
index 0000000..5721154
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/Makefile
@@ -0,0 +1,9 @@
+all:
+	@echo Nothing to be made.
+
+clean:
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov
+	rm -f build.wpa_supplicant build.hostapd
+
+install:
+	@echo Nothing to be made.
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/android_drv.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/android_drv.h
new file mode 100644
index 0000000..31d9440
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/android_drv.h
@@ -0,0 +1,56 @@
+/*
+ * Android driver interface
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef ANDROID_DRV_H
+#define ANDROID_DRV_H
+
+#define WPA_EVENT_DRIVER_STATE "CTRL-EVENT-DRIVER-STATE "
+
+#define MAX_SSID_LEN 32
+
+#define MAX_DRV_CMD_SIZE		248
+#define DRV_NUMBER_SEQUENTIAL_ERRORS	4
+
+#define WEXT_PNOSETUP_HEADER		"PNOSETUP "
+#define WEXT_PNOSETUP_HEADER_SIZE	9
+#define WEXT_PNO_TLV_PREFIX		'S'
+#define WEXT_PNO_TLV_VERSION		'1'
+#define WEXT_PNO_TLV_SUBVERSION		'2'
+#define WEXT_PNO_TLV_RESERVED		'0'
+#define WEXT_PNO_VERSION_SIZE		4
+#define WEXT_PNO_AMOUNT			16
+#define WEXT_PNO_SSID_SECTION		'S'
+/* SSID header size is SSID section type above + SSID length */
+#define WEXT_PNO_SSID_HEADER_SIZE	2
+#define WEXT_PNO_SCAN_INTERVAL_SECTION	'T'
+#define WEXT_PNO_SCAN_INTERVAL_LENGTH	2
+#define WEXT_PNO_SCAN_INTERVAL		30
+/* Scan interval size is scan interval section type + scan interval length
+ * above */
+#define WEXT_PNO_SCAN_INTERVAL_SIZE	(1 + WEXT_PNO_SCAN_INTERVAL_LENGTH)
+#define WEXT_PNO_REPEAT_SECTION		'R'
+#define WEXT_PNO_REPEAT_LENGTH		1
+#define WEXT_PNO_REPEAT			4
+/* Repeat section size is Repeat section type + Repeat value length above */
+#define WEXT_PNO_REPEAT_SIZE		(1 + WEXT_PNO_REPEAT_LENGTH)
+#define WEXT_PNO_MAX_REPEAT_SECTION	'M'
+#define WEXT_PNO_MAX_REPEAT_LENGTH	1
+#define WEXT_PNO_MAX_REPEAT		3
+/* Max Repeat section size is Max Repeat section type + Max Repeat value length
+ * above */
+#define WEXT_PNO_MAX_REPEAT_SIZE	(1 + WEXT_PNO_MAX_REPEAT_LENGTH)
+/* This corresponds to the size of all sections expect SSIDs */
+#define WEXT_PNO_NONSSID_SECTIONS_SIZE \
+(WEXT_PNO_SCAN_INTERVAL_SIZE + WEXT_PNO_REPEAT_SIZE + WEXT_PNO_MAX_REPEAT_SIZE)
+/* PNO Max command size is total of header, version, ssid and other sections +
+ * Null termination */
+#define WEXT_PNO_MAX_COMMAND_SIZE \
+	(WEXT_PNOSETUP_HEADER_SIZE + WEXT_PNO_VERSION_SIZE \
+	 + WEXT_PNO_AMOUNT * (WEXT_PNO_SSID_HEADER_SIZE + MAX_SSID_LEN) \
+	 + WEXT_PNO_NONSSID_SECTIONS_SIZE + 1)
+
+#endif /* ANDROID_DRV_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver.h
new file mode 100644
index 0000000..969cbda
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver.h
@@ -0,0 +1,6802 @@
+/*
+ * Driver interface definition
+ * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file defines a driver interface used by both %wpa_supplicant and
+ * hostapd. The first part of the file defines data structures used in various
+ * driver operations. This is followed by the struct wpa_driver_ops that each
+ * driver wrapper will beed to define with callback functions for requesting
+ * driver operations. After this, there are definitions for driver event
+ * reporting with wpa_supplicant_event() and some convenience helper functions
+ * that can be used to report events.
+ */
+
+#ifndef DRIVER_H
+#define DRIVER_H
+
+#define WPA_SUPPLICANT_DRIVER_VERSION 4
+
+#include "ap/sta_info.h"
+#include "common/defs.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_common.h"
+#ifdef CONFIG_MACSEC
+#include "pae/ieee802_1x_kay.h"
+#endif /* CONFIG_MACSEC */
+#include "utils/list.h"
+
+#define HOSTAPD_CHAN_DISABLED 0x00000001
+#define HOSTAPD_CHAN_NO_IR 0x00000002
+#define HOSTAPD_CHAN_RADAR 0x00000008
+#define HOSTAPD_CHAN_HT40PLUS 0x00000010
+#define HOSTAPD_CHAN_HT40MINUS 0x00000020
+#define HOSTAPD_CHAN_HT40 0x00000040
+#define HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED 0x00000080
+
+#define HOSTAPD_CHAN_DFS_UNKNOWN 0x00000000
+#define HOSTAPD_CHAN_DFS_USABLE 0x00000100
+#define HOSTAPD_CHAN_DFS_UNAVAILABLE 0x00000200
+#define HOSTAPD_CHAN_DFS_AVAILABLE 0x00000300
+#define HOSTAPD_CHAN_DFS_MASK 0x00000300
+
+#define HOSTAPD_CHAN_VHT_80MHZ_SUBCHANNEL 0x00000800
+#define HOSTAPD_CHAN_VHT_160MHZ_SUBCHANNEL 0x00001000
+
+#define HOSTAPD_CHAN_INDOOR_ONLY 0x00010000
+#define HOSTAPD_CHAN_GO_CONCURRENT 0x00020000
+
+/* Allowed bandwidth mask */
+enum hostapd_chan_width_attr {
+	HOSTAPD_CHAN_WIDTH_10   = BIT(0),
+	HOSTAPD_CHAN_WIDTH_20   = BIT(1),
+	HOSTAPD_CHAN_WIDTH_40P  = BIT(2),
+	HOSTAPD_CHAN_WIDTH_40M  = BIT(3),
+	HOSTAPD_CHAN_WIDTH_80   = BIT(4),
+	HOSTAPD_CHAN_WIDTH_160  = BIT(5),
+	HOSTAPD_CHAN_WIDTH_320  = BIT(6),
+};
+
+/* Filter gratuitous ARP */
+#define WPA_DATA_FRAME_FILTER_FLAG_ARP BIT(0)
+/* Filter unsolicited Neighbor Advertisement */
+#define WPA_DATA_FRAME_FILTER_FLAG_NA BIT(1)
+/* Filter unicast IP packets encrypted using the GTK */
+#define WPA_DATA_FRAME_FILTER_FLAG_GTK BIT(2)
+
+#define HOSTAPD_DFS_REGION_FCC	1
+#define HOSTAPD_DFS_REGION_ETSI	2
+#define HOSTAPD_DFS_REGION_JP	3
+
+/**
+ * enum reg_change_initiator - Regulatory change initiator
+ */
+enum reg_change_initiator {
+	REGDOM_SET_BY_CORE,
+	REGDOM_SET_BY_USER,
+	REGDOM_SET_BY_DRIVER,
+	REGDOM_SET_BY_COUNTRY_IE,
+	REGDOM_BEACON_HINT,
+};
+
+/**
+ * enum reg_type - Regulatory change types
+ */
+enum reg_type {
+	REGDOM_TYPE_UNKNOWN,
+	REGDOM_TYPE_COUNTRY,
+	REGDOM_TYPE_WORLD,
+	REGDOM_TYPE_CUSTOM_WORLD,
+	REGDOM_TYPE_INTERSECTION,
+};
+
+/**
+ * struct hostapd_wmm_rule - WMM regulatory rule
+ * @min_cwmin: Lower bound of CW_min value
+ * @min_cwmax: Lower bound of CW_max value
+ * @min_aifs: Lower bound of AIFS value
+ * @max_txop: Upper bound of TXOP, value in units of 32 usec
+ */
+struct hostapd_wmm_rule {
+	int min_cwmin;
+	int min_cwmax;
+	int min_aifs;
+	int max_txop;
+};
+
+/**
+ * struct hostapd_channel_data - Channel information
+ */
+struct hostapd_channel_data {
+	/**
+	 * chan - Channel number (IEEE 802.11)
+	 */
+	short chan;
+
+	/**
+	 * freq - Frequency in MHz
+	 */
+	int freq;
+
+	/**
+	 * flag - Channel flags (HOSTAPD_CHAN_*)
+	 */
+	int flag;
+
+	/**
+	 * allowed_bw - Allowed channel width bitmask
+	 *
+	 * See enum hostapd_chan_width_attr.
+	 */
+	u32 allowed_bw;
+
+	/**
+	 * max_tx_power - Regulatory transmit power limit in dBm
+	 */
+	u8 max_tx_power;
+
+	/**
+	 * survey_list - Linked list of surveys (struct freq_survey)
+	 */
+	struct dl_list survey_list;
+
+	/**
+	 * min_nf - Minimum observed noise floor, in dBm, based on all
+	 * surveyed channel data
+	 */
+	s8 min_nf;
+
+#ifdef CONFIG_ACS
+	/**
+	 * interference_factor - Computed interference factor on this
+	 * channel (used internally in src/ap/acs.c; driver wrappers do not
+	 * need to set this)
+	 */
+	long double interference_factor;
+#endif /* CONFIG_ACS */
+
+	/**
+	 * dfs_cac_ms - DFS CAC time in milliseconds
+	 */
+	unsigned int dfs_cac_ms;
+
+	/**
+	 * wmm_rules_valid - Indicates wmm_rules state
+	 */
+	int wmm_rules_valid;
+
+	/**
+	 * wmm_rules - WMM regulatory rules
+	 */
+	struct hostapd_wmm_rule wmm_rules[WMM_AC_NUM];
+
+	/**
+	 * punct_bitmap - RU puncturing bitmap
+	 */
+	u16 punct_bitmap;
+};
+
+#define HE_MAC_CAPAB_0		0
+#define HE_MAX_MAC_CAPAB_SIZE	6
+#define HE_MAX_PHY_CAPAB_SIZE	11
+#define HE_MAX_MCS_CAPAB_SIZE	12
+#define HE_MAX_PPET_CAPAB_SIZE	25
+
+/**
+ * struct he_capabilities - IEEE 802.11ax HE capabilities
+ */
+struct he_capabilities {
+	u8 he_supported;
+	u8 phy_cap[HE_MAX_PHY_CAPAB_SIZE];
+	u8 mac_cap[HE_MAX_MAC_CAPAB_SIZE];
+	u8 mcs[HE_MAX_MCS_CAPAB_SIZE];
+	u8 ppet[HE_MAX_PPET_CAPAB_SIZE];
+	u16 he_6ghz_capa;
+};
+
+/* struct eht_capabilities - IEEE 802.11be EHT capabilities */
+struct eht_capabilities {
+	bool eht_supported;
+	u16 mac_cap;
+	u8 phy_cap[EHT_PHY_CAPAB_LEN];
+	u8 mcs[EHT_MCS_NSS_CAPAB_LEN];
+	u8 ppet[EHT_PPE_THRESH_CAPAB_LEN];
+};
+
+#define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0)
+#define HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN BIT(1)
+#define HOSTAPD_MODE_FLAG_HE_INFO_KNOWN BIT(2)
+
+
+enum ieee80211_op_mode {
+	IEEE80211_MODE_INFRA = 0,
+	IEEE80211_MODE_IBSS = 1,
+	IEEE80211_MODE_AP = 2,
+	IEEE80211_MODE_MESH = 5,
+
+	/* only add new entries before IEEE80211_MODE_NUM */
+	IEEE80211_MODE_NUM
+};
+
+/**
+ * struct ieee80211_edmg_config - EDMG configuration
+ *
+ * This structure describes most essential parameters needed
+ * for IEEE 802.11ay EDMG configuration
+ *
+ * @channels: Bitmap that indicates the 2.16 GHz channel(s)
+ *	that are allowed to be used for transmissions.
+ *	Bit 0 indicates channel 1, bit 1 indicates channel 2, etc.
+ *	Set to 0 to indicate EDMG not supported.
+ * @bw_config: Channel BW Configuration subfield encodes
+ *	the allowed channel bandwidth configurations
+ */
+struct ieee80211_edmg_config {
+	u8 channels;
+	enum edmg_bw_config bw_config;
+};
+
+/**
+ * struct hostapd_hw_modes - Supported hardware mode information
+ */
+struct hostapd_hw_modes {
+	/**
+	 * mode - Hardware mode
+	 */
+	enum hostapd_hw_mode mode;
+
+	/**
+	 * num_channels - Number of entries in the channels array
+	 */
+	int num_channels;
+
+	/**
+	 * channels - Array of supported channels
+	 */
+	struct hostapd_channel_data *channels;
+
+	/**
+	 * num_rates - Number of entries in the rates array
+	 */
+	int num_rates;
+
+	/**
+	 * rates - Array of supported rates in 100 kbps units
+	 */
+	int *rates;
+
+	/**
+	 * ht_capab - HT (IEEE 802.11n) capabilities
+	 */
+	u16 ht_capab;
+
+	/**
+	 * mcs_set - MCS (IEEE 802.11n) rate parameters
+	 */
+	u8 mcs_set[16];
+
+	/**
+	 * a_mpdu_params - A-MPDU (IEEE 802.11n) parameters
+	 */
+	u8 a_mpdu_params;
+
+	/**
+	 * vht_capab - VHT (IEEE 802.11ac) capabilities
+	 */
+	u32 vht_capab;
+
+	/**
+	 * vht_mcs_set - VHT MCS (IEEE 802.11ac) rate parameters
+	 */
+	u8 vht_mcs_set[8];
+
+	unsigned int flags; /* HOSTAPD_MODE_FLAG_* */
+
+	/**
+	 * he_capab - HE (IEEE 802.11ax) capabilities
+	 */
+	struct he_capabilities he_capab[IEEE80211_MODE_NUM];
+
+	/**
+	 * This structure describes the most essential parameters needed
+	 * for IEEE 802.11ay EDMG configuration.
+	 */
+	struct ieee80211_edmg_config edmg;
+
+	/**
+	 * eht_capab - EHT (IEEE 802.11be) capabilities
+	 */
+	struct eht_capabilities eht_capab[IEEE80211_MODE_NUM];
+};
+
+
+#define IEEE80211_CAP_ESS	0x0001
+#define IEEE80211_CAP_IBSS	0x0002
+#define IEEE80211_CAP_PRIVACY	0x0010
+#define IEEE80211_CAP_RRM	0x1000
+
+/* DMG (60 GHz) IEEE 802.11ad */
+/* type - bits 0..1 */
+#define IEEE80211_CAP_DMG_MASK	0x0003
+#define IEEE80211_CAP_DMG_IBSS	0x0001 /* Tx by: STA */
+#define IEEE80211_CAP_DMG_PBSS	0x0002 /* Tx by: PCP */
+#define IEEE80211_CAP_DMG_AP	0x0003 /* Tx by: AP */
+
+#define WPA_SCAN_QUAL_INVALID		BIT(0)
+#define WPA_SCAN_NOISE_INVALID		BIT(1)
+#define WPA_SCAN_LEVEL_INVALID		BIT(2)
+#define WPA_SCAN_LEVEL_DBM		BIT(3)
+#define WPA_SCAN_ASSOCIATED		BIT(5)
+
+/**
+ * struct wpa_scan_res - Scan result for an BSS/IBSS
+ * @flags: information flags about the BSS/IBSS (WPA_SCAN_*)
+ * @bssid: BSSID
+ * @freq: frequency of the channel in MHz (e.g., 2412 = channel 1)
+ * @beacon_int: beacon interval in TUs (host byte order)
+ * @caps: capability information field in host byte order
+ * @qual: signal quality
+ * @noise: noise level
+ * @level: signal level
+ * @tsf: Timestamp
+ * @age: Age of the information in milliseconds (i.e., how many milliseconds
+ * ago the last Beacon or Probe Response frame was received)
+ * @est_throughput: Estimated throughput in kbps (this is calculated during
+ * scan result processing if left zero by the driver wrapper)
+ * @snr: Signal-to-noise ratio in dB (calculated during scan result processing)
+ * @parent_tsf: Time when the Beacon/Probe Response frame was received in terms
+ * of TSF of the BSS specified by %tsf_bssid.
+ * @tsf_bssid: The BSS that %parent_tsf TSF time refers to.
+ * @beacon_newer: Whether the Beacon frame data is known to be newer
+ * @ie_len: length of the following IE field in octets
+ * @beacon_ie_len: length of the following Beacon IE field in octets
+ *
+ * This structure is used as a generic format for scan results from the
+ * driver. Each driver interface implementation is responsible for converting
+ * the driver or OS specific scan results into this format.
+ *
+ * If the driver does not support reporting all IEs, the IE data structure is
+ * constructed of the IEs that are available. This field will also need to
+ * include SSID in IE format. All drivers are encouraged to be extended to
+ * report all IEs to make it easier to support future additions.
+ *
+ * This structure data is followed by ie_len octets of IEs from Probe Response
+ * frame (or if the driver does not indicate source of IEs, these may also be
+ * from Beacon frame). After the first set of IEs, another set of IEs may follow
+ * (with beacon_ie_len octets of data) if the driver provides both IE sets.
+ */
+struct wpa_scan_res {
+	unsigned int flags;
+	u8 bssid[ETH_ALEN];
+	int freq;
+	u16 beacon_int;
+	u16 caps;
+	int qual;
+	int noise;
+	int level;
+	u64 tsf;
+	unsigned int age;
+	unsigned int est_throughput;
+	int snr;
+	u64 parent_tsf;
+	u8 tsf_bssid[ETH_ALEN];
+	bool beacon_newer;
+	size_t ie_len;
+	size_t beacon_ie_len;
+	/* Followed by ie_len + beacon_ie_len octets of IE data */
+};
+
+/**
+ * struct wpa_scan_results - Scan results
+ * @res: Array of pointers to allocated variable length scan result entries
+ * @num: Number of entries in the scan result array
+ * @fetch_time: Time when the results were fetched from the driver
+ */
+struct wpa_scan_results {
+	struct wpa_scan_res **res;
+	size_t num;
+	struct os_reltime fetch_time;
+};
+
+/**
+ * struct wpa_interface_info - Network interface information
+ * @next: Pointer to the next interface or NULL if this is the last one
+ * @ifname: Interface name that can be used with init() or init2()
+ * @desc: Human readable adapter description (e.g., vendor/model) or NULL if
+ *	not available
+ * @drv_name: struct wpa_driver_ops::name (note: unlike other strings, this one
+ *	is not an allocated copy, i.e., get_interfaces() caller will not free
+ *	this)
+ */
+struct wpa_interface_info {
+	struct wpa_interface_info *next;
+	char *ifname;
+	char *desc;
+	const char *drv_name;
+};
+
+#define WPAS_MAX_SCAN_SSIDS 16
+
+/**
+ * struct wpa_driver_scan_ssid - SSIDs to scan for
+ * @ssid - specific SSID to scan for (ProbeReq)
+ *	%NULL or zero-length SSID is used to indicate active scan
+ *	with wildcard SSID.
+ * @ssid_len - Length of the SSID in octets
+ */
+struct wpa_driver_scan_ssid {
+	const u8 *ssid;
+	size_t ssid_len;
+};
+
+struct t2lm_mapping {
+	/**
+	 * downlink - Bitmap of TIDs mapped with a link in downlink direction
+	 */
+	u8 downlink;
+
+	/**
+	 * uplink - Bitmap of TIDs mapped with a link in uplink direction
+	 */
+	u8 uplink;
+};
+
+/**
+ * struct wpa_driver_scan_params - Scan parameters
+ * Data for struct wpa_driver_ops::scan2().
+ */
+struct wpa_driver_scan_params {
+	/**
+	 * ssids - SSIDs to scan for
+	 */
+	struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS];
+
+	/**
+	 * num_ssids - Number of entries in ssids array
+	 * Zero indicates a request for a passive scan.
+	 */
+	size_t num_ssids;
+
+	/**
+	 * extra_ies - Extra IE(s) to add into Probe Request or %NULL
+	 */
+	const u8 *extra_ies;
+
+	/**
+	 * extra_ies_len - Length of extra_ies in octets
+	 */
+	size_t extra_ies_len;
+
+	/**
+	 * freqs - Array of frequencies to scan or %NULL for all frequencies
+	 *
+	 * The frequency is set in MHz. The array is zero-terminated.
+	 */
+	int *freqs;
+
+	/**
+	 * filter_ssids - Filter for reporting SSIDs
+	 *
+	 * This optional parameter can be used to request the driver wrapper to
+	 * filter scan results to include only the specified SSIDs. %NULL
+	 * indicates that no filtering is to be done. This can be used to
+	 * reduce memory needs for scan results in environments that have large
+	 * number of APs with different SSIDs.
+	 *
+	 * The driver wrapper is allowed to take this allocated buffer into its
+	 * own use by setting the pointer to %NULL. In that case, the driver
+	 * wrapper is responsible for freeing the buffer with os_free() once it
+	 * is not needed anymore.
+	 */
+	struct wpa_driver_scan_filter {
+		u8 ssid[SSID_MAX_LEN];
+		size_t ssid_len;
+	} *filter_ssids;
+
+	/**
+	 * num_filter_ssids - Number of entries in filter_ssids array
+	 */
+	size_t num_filter_ssids;
+
+	/**
+	 * filter_rssi - Filter by RSSI
+	 *
+	 * The driver may filter scan results in firmware to reduce host
+	 * wakeups and thereby save power. Specify the RSSI threshold in s32
+	 * dBm.
+	 */
+	s32 filter_rssi;
+
+	/**
+	 * p2p_probe - Used to disable CCK (802.11b) rates for P2P probes
+	 *
+	 * When set, the driver is expected to remove rates 1, 2, 5.5, and 11
+	 * Mbps from the support rates element(s) in the Probe Request frames
+	 * and not to transmit the frames at any of those rates.
+	 */
+	unsigned int p2p_probe:1;
+
+	/**
+	 * only_new_results - Request driver to report only new results
+	 *
+	 * This is used to request the driver to report only BSSes that have
+	 * been detected after this scan request has been started, i.e., to
+	 * flush old cached BSS entries.
+	 */
+	unsigned int only_new_results:1;
+
+	/**
+	 * low_priority - Requests driver to use a lower scan priority
+	 *
+	 * This is used to request the driver to use a lower scan priority
+	 * if it supports such a thing.
+	 */
+	unsigned int low_priority:1;
+
+	/**
+	 * mac_addr_rand - Requests driver to randomize MAC address
+	 */
+	unsigned int mac_addr_rand:1;
+
+	/**
+	 * mac_addr - MAC address used with randomization. The address cannot be
+	 * a multicast one, i.e., bit 0 of byte 0 should not be set.
+	 */
+	u8 *mac_addr;
+
+	/**
+	 * mac_addr_mask - MAC address mask used with randomization.
+	 *
+	 * Bits that are 0 in the mask should be randomized. Bits that are 1 in
+	 * the mask should be taken as is from mac_addr. The mask should not
+	 * allow the generation of a multicast address, i.e., bit 0 of byte 0
+	 * must be set.
+	 */
+	const u8 *mac_addr_mask;
+
+	/**
+	 * sched_scan_plans - Scan plans for scheduled scan
+	 *
+	 * Each scan plan consists of the number of iterations to scan and the
+	 * interval between scans. When a scan plan finishes (i.e., it was run
+	 * for the specified number of iterations), the next scan plan is
+	 * executed. The scan plans are executed in the order they appear in
+	 * the array (lower index first). The last scan plan will run infinitely
+	 * (until requested to stop), thus must not specify the number of
+	 * iterations. All other scan plans must specify the number of
+	 * iterations.
+	 */
+	struct sched_scan_plan {
+		 u32 interval; /* In seconds */
+		 u32 iterations; /* Zero to run infinitely */
+	 } *sched_scan_plans;
+
+	/**
+	 * sched_scan_plans_num - Number of scan plans in sched_scan_plans array
+	 */
+	 unsigned int sched_scan_plans_num;
+
+	/**
+	 * sched_scan_start_delay - Delay to use before starting the first scan
+	 *
+	 * Delay (in seconds) before scheduling first scan plan cycle. The
+	 * driver may ignore this parameter and start immediately (or at any
+	 * other time), if this feature is not supported.
+	 */
+	 u32 sched_scan_start_delay;
+
+	/**
+	 * bssid - Specific BSSID to scan for
+	 *
+	 * This optional parameter can be used to replace the default wildcard
+	 * BSSID with a specific BSSID to scan for if results are needed from
+	 * only a single BSS.
+	 */
+	const u8 *bssid;
+
+	/**
+	 * scan_cookie - Unique identification representing the scan request
+	 *
+	 * This scan_cookie carries a unique identification representing the
+	 * scan request if the host driver/kernel supports concurrent scan
+	 * requests. This cookie is returned from the corresponding driver
+	 * interface.
+	 *
+	 * Note: Unlike other parameters in this structure, scan_cookie is used
+	 * only to return information instead of setting parameters for the
+	 * scan.
+	 */
+	u64 scan_cookie;
+
+	 /**
+	  * duration - Dwell time on each channel
+	  *
+	  * This optional parameter can be used to set the dwell time on each
+	  * channel. In TUs.
+	  */
+	 u16 duration;
+
+	 /**
+	  * duration_mandatory - Whether the specified duration is mandatory
+	  *
+	  * If this is set, the duration specified by the %duration field is
+	  * mandatory (and the driver should reject the scan request if it is
+	  * unable to comply with the specified duration), otherwise it is the
+	  * maximum duration and the actual duration may be shorter.
+	  */
+	 unsigned int duration_mandatory:1;
+
+	/**
+	 * relative_rssi_set - Whether relative RSSI parameters are set
+	 */
+	unsigned int relative_rssi_set:1;
+
+	/**
+	 * relative_rssi - Relative RSSI for reporting better BSSs
+	 *
+	 * Amount of RSSI by which a BSS should be better than the current
+	 * connected BSS to report the new BSS to user space.
+	 */
+	s8 relative_rssi;
+
+	/**
+	 * relative_adjust_band - Band to which RSSI should be adjusted
+	 *
+	 * The relative_adjust_rssi should be added to the band specified
+	 * by relative_adjust_band.
+	 */
+	enum set_band relative_adjust_band;
+
+	/**
+	 * relative_adjust_rssi - RSSI to be added to relative_adjust_band
+	 *
+	 * An amount of relative_band_rssi should be added to the BSSs that
+	 * belong to the band specified by relative_adjust_band while comparing
+	 * with other bands for BSS reporting.
+	 */
+	s8 relative_adjust_rssi;
+
+	/**
+	 * oce_scan
+	 *
+	 * Enable the following OCE scan features: (WFA OCE TechSpec v1.0)
+	 * - Accept broadcast Probe Response frame.
+	 * - Probe Request frame deferral and suppression.
+	 * - Max Channel Time - driver fills FILS request params IE with
+	 *   Maximum Channel Time.
+	 * - Send 1st Probe Request frame in rate of minimum 5.5 Mbps.
+	 */
+	unsigned int oce_scan:1;
+
+	/**
+	 * p2p_include_6ghz - Include 6 GHz channels for P2P full scan
+	 *
+	 */
+	unsigned int p2p_include_6ghz:1;
+
+	/**
+	 * non_coloc_6ghz - Force scanning of non-PSC 6 GHz channels
+	 *
+	 * If this is set, the driver should scan non-PSC channels from the
+	 * scan request even if neighbor reports from 2.4/5 GHz APs did not
+	 * report a co-located AP on these channels. The default is to scan
+	 * non-PSC channels only if a co-located AP was reported on the channel.
+	 */
+	unsigned int non_coloc_6ghz:1;
+
+	/*
+	 * NOTE: Whenever adding new parameters here, please make sure
+	 * wpa_scan_clone_params() and wpa_scan_free_params() get updated with
+	 * matching changes.
+	 */
+};
+
+/**
+ * struct wpa_driver_auth_params - Authentication parameters
+ * Data for struct wpa_driver_ops::authenticate().
+ */
+struct wpa_driver_auth_params {
+	int freq;
+	const u8 *bssid;
+	const u8 *ssid;
+	size_t ssid_len;
+	int auth_alg;
+	const u8 *ie;
+	size_t ie_len;
+	const u8 *wep_key[4];
+	size_t wep_key_len[4];
+	int wep_tx_keyidx;
+	int local_state_change;
+
+	/**
+	 * p2p - Whether this connection is a P2P group
+	 */
+	int p2p;
+
+	/**
+	 * auth_data - Additional elements for Authentication frame
+	 *
+	 * This buffer starts with the Authentication transaction sequence
+	 * number field. If no special handling of such elements is needed, this
+	 * pointer is %NULL. This is used with SAE and FILS.
+	 */
+	const u8 *auth_data;
+
+	/**
+	 * auth_data_len - Length of auth_data buffer in octets
+	 */
+	size_t auth_data_len;
+
+	/**
+	 * mld - Establish an MLD connection
+	 */
+	bool mld;
+
+	/**
+	 * mld_link_id - The link ID of the MLD AP to which we are associating
+	 */
+	u8 mld_link_id;
+
+	/**
+	 * The MLD AP address
+	 */
+	const u8 *ap_mld_addr;
+};
+
+/**
+ * enum wps_mode - WPS mode
+ */
+enum wps_mode {
+	/**
+	 * WPS_MODE_NONE - No WPS provisioning being used
+	 */
+	WPS_MODE_NONE,
+
+	/**
+	 * WPS_MODE_OPEN - WPS provisioning with AP that is in open mode
+	 */
+	WPS_MODE_OPEN,
+
+	/**
+	 * WPS_MODE_PRIVACY - WPS provisioning with AP that is using protection
+	 */
+	WPS_MODE_PRIVACY
+};
+
+/**
+ * struct hostapd_freq_params - Channel parameters
+ */
+struct hostapd_freq_params {
+	/**
+	 * mode - Mode/band (HOSTAPD_MODE_IEEE80211A, ..)
+	 */
+	enum hostapd_hw_mode mode;
+
+	/**
+	 * freq - Primary channel center frequency in MHz
+	 */
+	int freq;
+
+	/**
+	 * channel - Channel number
+	 */
+	int channel;
+
+	/**
+	 * ht_enabled - Whether HT is enabled
+	 */
+	int ht_enabled;
+
+	/**
+	 * sec_channel_offset - Secondary channel offset for HT40
+	 *
+	 * 0 = HT40 disabled,
+	 * -1 = HT40 enabled, secondary channel below primary,
+	 * 1 = HT40 enabled, secondary channel above primary
+	 */
+	int sec_channel_offset;
+
+	/**
+	 * vht_enabled - Whether VHT is enabled
+	 */
+	int vht_enabled;
+
+	/**
+	 * he_enabled - Whether HE is enabled
+	 */
+	int he_enabled;
+
+	/**
+	 * center_freq1 - Segment 0 center frequency in MHz
+	 *
+	 * Valid for both HT and VHT.
+	 */
+	int center_freq1;
+
+	/**
+	 * center_freq2 - Segment 1 center frequency in MHz
+	 *
+	 * Non-zero only for bandwidth 80 and an 80+80 channel
+	 */
+	int center_freq2;
+
+	/**
+	 * bandwidth - Channel bandwidth in MHz (20, 40, 80, 160)
+	 */
+	int bandwidth;
+
+	/**
+	 * This structure describes the most essential parameters needed
+	 * for IEEE 802.11ay EDMG configuration.
+	 */
+	struct ieee80211_edmg_config edmg;
+
+	/**
+	 * radar_background - Whether radar/CAC background is requested
+	 */
+	bool radar_background;
+
+	/**
+	 * eht_enabled - Whether EHT is enabled
+	 */
+	bool eht_enabled;
+
+	/**
+	 * link_id: If >=0 indicates the link of the AP MLD to configure
+	 */
+	int link_id;
+};
+
+/**
+ * struct wpa_driver_sta_auth_params - Authentication parameters
+ * Data for struct wpa_driver_ops::sta_auth().
+ */
+struct wpa_driver_sta_auth_params {
+
+	/**
+	 * own_addr - Source address and BSSID for authentication frame
+	 */
+	const u8 *own_addr;
+
+	/**
+	 * addr - MAC address of the station to associate
+	 */
+	const u8 *addr;
+
+	/**
+	 * seq - authentication sequence number
+	 */
+	u16 seq;
+
+	/**
+	 * status - authentication response status code
+	 */
+	u16 status;
+
+	/**
+	 * ie - authentication frame ie buffer
+	 */
+	const u8 *ie;
+
+	/**
+	 * len - ie buffer length
+	 */
+	size_t len;
+
+	/**
+	 * fils_auth - Indicates whether FILS authentication is being performed
+	 */
+	int fils_auth;
+
+	/**
+	 * fils_anonce - ANonce (required for FILS)
+	 */
+	u8 fils_anonce[WPA_NONCE_LEN];
+
+	/**
+	 * fils_snonce - SNonce (required for FILS)
+	*/
+	u8 fils_snonce[WPA_NONCE_LEN];
+
+	/**
+	 * fils_kek - key for encryption (required for FILS)
+	 */
+	u8 fils_kek[WPA_KEK_MAX_LEN];
+
+	/**
+	 * fils_kek_len - Length of the fils_kek in octets (required for FILS)
+	 */
+	size_t fils_kek_len;
+};
+
+struct wpa_driver_mld_params {
+	/**
+	 * mld_addr - AP's MLD address
+	 */
+	const u8 *mld_addr;
+
+	/**
+	 * valid_links - The valid links including the association link
+	 */
+	u16 valid_links;
+
+	/**
+	 * assoc_link_id - The link on which the association is performed
+	 */
+	u8 assoc_link_id;
+
+	/**
+	 * mld_links - Link information
+	 *
+	 * Should include information on all the requested links for association
+	 * including the link on which the association should take place.
+	 * For the association link, the ies and ies_len should be NULL and
+	 * 0 respectively.
+	 */
+	struct {
+		int freq;
+		const u8 *bssid;
+		const u8 *ies;
+		size_t ies_len;
+	} mld_links[MAX_NUM_MLD_LINKS];
+};
+
+/**
+ * struct wpa_driver_associate_params - Association parameters
+ * Data for struct wpa_driver_ops::associate().
+ */
+struct wpa_driver_associate_params {
+	/**
+	 * bssid - BSSID of the selected AP
+	 * This can be %NULL, if ap_scan=2 mode is used and the driver is
+	 * responsible for selecting with which BSS to associate. */
+	const u8 *bssid;
+
+	unsigned char rates[WLAN_SUPP_RATES_MAX];
+	int mcast_rate;
+
+	/**
+	 * bssid_hint - BSSID of a proposed AP
+	 *
+	 * This indicates which BSS has been found a suitable candidate for
+	 * initial association for drivers that use driver/firmwate-based BSS
+	 * selection. Unlike the @bssid parameter, @bssid_hint does not limit
+	 * the driver from selecting other BSSes in the ESS.
+	 */
+	const u8 *bssid_hint;
+
+	/**
+	 * ssid - The selected SSID
+	 */
+	const u8 *ssid;
+
+	/**
+	 * ssid_len - Length of the SSID (1..32)
+	 */
+	size_t ssid_len;
+
+	/**
+	 * freq - channel parameters
+	 */
+	struct hostapd_freq_params freq;
+
+	/**
+	 * freq_hint - Frequency of the channel the proposed AP is using
+	 *
+	 * This provides a channel on which a suitable BSS has been found as a
+	 * hint for the driver. Unlike the @freq parameter, @freq_hint does not
+	 * limit the driver from selecting other channels for
+	 * driver/firmware-based BSS selection.
+	 */
+	int freq_hint;
+
+	/**
+	 * bg_scan_period - Background scan period in seconds, 0 to disable
+	 * background scan, or -1 to indicate no change to default driver
+	 * configuration
+	 */
+	int bg_scan_period;
+
+	/**
+	 * beacon_int - Beacon interval for IBSS or 0 to use driver default
+	 */
+	int beacon_int;
+
+	/**
+	 * wpa_ie - WPA information element for (Re)Association Request
+	 * WPA information element to be included in (Re)Association
+	 * Request (including information element id and length). Use
+	 * of this WPA IE is optional. If the driver generates the WPA
+	 * IE, it can use pairwise_suite, group_suite, group_mgmt_suite, and
+	 * key_mgmt_suite to select proper algorithms. In this case,
+	 * the driver has to notify wpa_supplicant about the used WPA
+	 * IE by generating an event that the interface code will
+	 * convert into EVENT_ASSOCINFO data (see below).
+	 *
+	 * When using WPA2/IEEE 802.11i, wpa_ie is used for RSN IE
+	 * instead. The driver can determine which version is used by
+	 * looking at the first byte of the IE (0xdd for WPA, 0x30 for
+	 * WPA2/RSN).
+	 *
+	 * When using WPS, wpa_ie is used for WPS IE instead of WPA/RSN IE.
+	 */
+	const u8 *wpa_ie;
+
+	/**
+	 * wpa_ie_len - length of the wpa_ie
+	 */
+	size_t wpa_ie_len;
+
+	/**
+	 * wpa_proto - Bitfield of WPA_PROTO_* values to indicate WPA/WPA2
+	 */
+	unsigned int wpa_proto;
+
+	/**
+	 * pairwise_suite - Selected pairwise cipher suite (WPA_CIPHER_*)
+	 *
+	 * This is usually ignored if @wpa_ie is used.
+	 */
+	unsigned int pairwise_suite;
+
+	/**
+	 * group_suite - Selected group cipher suite (WPA_CIPHER_*)
+	 *
+	 * This is usually ignored if @wpa_ie is used.
+	 */
+	unsigned int group_suite;
+
+	/**
+	 * mgmt_group_suite - Selected group management cipher suite (WPA_CIPHER_*)
+	 *
+	 * This is usually ignored if @wpa_ie is used.
+	 */
+	unsigned int mgmt_group_suite;
+
+	/**
+	 * key_mgmt_suite - Selected key management suite (WPA_KEY_MGMT_*)
+	 *
+	 * This is usually ignored if @wpa_ie is used.
+	 */
+	unsigned int key_mgmt_suite;
+
+	/**
+	 * allowed_key_mgmts - Bitfield of allowed key management suites
+	 * (WPA_KEY_MGMT_*) other than @key_mgmt_suite for the current
+	 * connection
+	 *
+	 * SME in the driver may choose key_mgmt from this list for the initial
+	 * connection or roaming. The driver which doesn't support this
+	 * ignores this parameter.
+	 */
+	unsigned int allowed_key_mgmts;
+
+	/**
+	 * auth_alg - Allowed authentication algorithms
+	 * Bit field of WPA_AUTH_ALG_*
+	 */
+	int auth_alg;
+
+	/**
+	 * mode - Operation mode (infra/ibss) IEEE80211_MODE_*
+	 */
+	int mode;
+
+	/**
+	 * wep_key - WEP keys for static WEP configuration
+	 */
+	const u8 *wep_key[4];
+
+	/**
+	 * wep_key_len - WEP key length for static WEP configuration
+	 */
+	size_t wep_key_len[4];
+
+	/**
+	 * wep_tx_keyidx - WEP TX key index for static WEP configuration
+	 */
+	int wep_tx_keyidx;
+
+	/**
+	 * mgmt_frame_protection - IEEE 802.11w management frame protection
+	 */
+	enum mfp_options mgmt_frame_protection;
+
+	/**
+	 * passphrase - RSN passphrase for PSK
+	 *
+	 * This value is made available only for WPA/WPA2-Personal (PSK) and
+	 * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK. This
+	 * is the 8..63 character ASCII passphrase, if available. Please note
+	 * that this can be %NULL if passphrase was not used to generate the
+	 * PSK. In that case, the psk field must be used to fetch the PSK.
+	 */
+	const char *passphrase;
+
+	/**
+	 * psk - RSN PSK (alternative for passphrase for PSK)
+	 *
+	 * This value is made available only for WPA/WPA2-Personal (PSK) and
+	 * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK. This
+	 * is the 32-octet (256-bit) PSK, if available. The driver wrapper
+	 * should be prepared to handle %NULL value as an error.
+	 */
+	const u8 *psk;
+
+	/**
+	 * drop_unencrypted - Enable/disable unencrypted frame filtering
+	 *
+	 * Configure the driver to drop all non-EAPOL frames (both receive and
+	 * transmit paths). Unencrypted EAPOL frames (ethertype 0x888e) must
+	 * still be allowed for key negotiation.
+	 */
+	int drop_unencrypted;
+
+	/**
+	 * prev_bssid - Previously used BSSID in this ESS
+	 *
+	 * When not %NULL, this is a request to use reassociation instead of
+	 * association.
+	 */
+	const u8 *prev_bssid;
+
+	/**
+	 * wps - WPS mode
+	 *
+	 * If the driver needs to do special configuration for WPS association,
+	 * this variable provides more information on what type of association
+	 * is being requested. Most drivers should not need to use this.
+	 */
+	enum wps_mode wps;
+
+	/**
+	 * p2p - Whether this connection is a P2P group
+	 */
+	int p2p;
+
+	/**
+	 * uapsd - UAPSD parameters for the network
+	 * -1 = do not change defaults
+	 * AP mode: 1 = enabled, 0 = disabled
+	 * STA mode: bits 0..3 UAPSD enabled for VO,VI,BK,BE
+	 */
+	int uapsd;
+
+	/**
+	 * fixed_bssid - Whether to force this BSSID in IBSS mode
+	 * 1 = Fix this BSSID and prevent merges.
+	 * 0 = Do not fix BSSID.
+	 */
+	int fixed_bssid;
+
+	/**
+	 * fixed_freq - Fix control channel in IBSS mode
+	 * 0 = don't fix control channel (default)
+	 * 1 = fix control channel; this prevents IBSS merging with another
+	 *	channel
+	 */
+	int fixed_freq;
+
+	/**
+	 * disable_ht - Disable HT (IEEE 802.11n) for this connection
+	 */
+	int disable_ht;
+
+	/**
+	 * htcaps - HT Capabilities over-rides
+	 *
+	 * Only bits set in the mask will be used, and not all values are used
+	 * by the kernel anyway. Currently, MCS, MPDU and MSDU fields are used.
+	 *
+	 * Pointer to struct ieee80211_ht_capabilities.
+	 */
+	const u8 *htcaps;
+
+	/**
+	 * htcaps_mask - HT Capabilities over-rides mask
+	 *
+	 * Pointer to struct ieee80211_ht_capabilities.
+	 */
+	const u8 *htcaps_mask;
+
+#ifdef CONFIG_VHT_OVERRIDES
+	/**
+	 * disable_vht - Disable VHT for this connection
+	 */
+	int disable_vht;
+
+	/**
+	 * VHT capability overrides.
+	 */
+	const struct ieee80211_vht_capabilities *vhtcaps;
+	const struct ieee80211_vht_capabilities *vhtcaps_mask;
+#endif /* CONFIG_VHT_OVERRIDES */
+
+#ifdef CONFIG_HE_OVERRIDES
+	/**
+	 * disable_he - Disable HE for this connection
+	 */
+	int disable_he;
+#endif /* CONFIG_HE_OVERRIDES */
+
+	/**
+	 * req_key_mgmt_offload - Request key management offload for connection
+	 *
+	 * Request key management offload for this connection if the device
+	 * supports it.
+	 */
+	int req_key_mgmt_offload;
+
+	/**
+	 * req_handshake_offload - Request EAPOL handshake offload
+	 *
+	 * Request EAPOL handshake offload for this connection if the device
+	 * supports it.
+	 */
+	int req_handshake_offload;
+
+	/**
+	 * Flag for indicating whether this association includes support for
+	 * RRM (Radio Resource Measurements)
+	 */
+	int rrm_used;
+
+	/**
+	 * pbss - If set, connect to a PCP in a PBSS. Otherwise, connect to an
+	 * AP as usual. Valid for DMG network only.
+	 */
+	int pbss;
+
+	/**
+	 * fils_kek - KEK for FILS association frame protection (AES-SIV)
+	 */
+	const u8 *fils_kek;
+
+	/**
+	 * fils_kek_len: Length of fils_kek in bytes
+	 */
+	size_t fils_kek_len;
+
+	/**
+	 * fils_nonces - Nonces for FILS association frame protection
+	 * (AES-SIV AAD)
+	 */
+	const u8 *fils_nonces;
+
+	/**
+	 * fils_nonces_len: Length of fils_nonce in bytes
+	 */
+	size_t fils_nonces_len;
+
+	/**
+	 * fils_erp_username - Username part of keyName-NAI
+	 */
+	const u8 *fils_erp_username;
+
+	/**
+	 * fils_erp_username_len - Length of fils_erp_username in bytes
+	 */
+	size_t fils_erp_username_len;
+
+	/**
+	 * fils_erp_realm - Realm/domain name to use in FILS ERP
+	 */
+	const u8 *fils_erp_realm;
+
+	/**
+	 * fils_erp_realm_len - Length of fils_erp_realm in bytes
+	 */
+	size_t fils_erp_realm_len;
+
+	/**
+	 * fils_erp_next_seq_num - The next sequence number to use in FILS ERP
+	 * messages
+	 */
+	u16 fils_erp_next_seq_num;
+
+	/**
+	 * fils_erp_rrk - Re-authentication root key (rRK) for the keyName-NAI
+	 * specified by fils_erp_username@fils_erp_realm.
+	 */
+	const u8 *fils_erp_rrk;
+
+	/**
+	 * fils_erp_rrk_len - Length of fils_erp_rrk in bytes
+	 */
+	size_t fils_erp_rrk_len;
+
+	/**
+	 * sae_pwe - SAE mechanism for PWE derivation
+	 * 0 = hunting-and-pecking loop only
+	 * 1 = hash-to-element only
+	 * 2 = both hunting-and-pecking loop and hash-to-element enabled
+	 */
+	enum sae_pwe sae_pwe;
+
+	/**
+	 * disable_eht - Disable EHT for this connection
+	 */
+	int disable_eht;
+
+	/*
+	 * mld_params - MLD association parameters
+	 */
+	struct wpa_driver_mld_params mld_params;
+};
+
+enum hide_ssid {
+	NO_SSID_HIDING,
+	HIDDEN_SSID_ZERO_LEN,
+	HIDDEN_SSID_ZERO_CONTENTS
+};
+
+enum ch_switch_state {
+	CH_SW_STARTED,
+	CH_SW_FINISHED
+};
+
+struct wowlan_triggers {
+	u8 any;
+	u8 disconnect;
+	u8 magic_pkt;
+	u8 gtk_rekey_failure;
+	u8 eap_identity_req;
+	u8 four_way_handshake;
+	u8 rfkill_release;
+};
+
+struct wpa_driver_ap_params {
+	/**
+	 * head - Beacon head from IEEE 802.11 header to IEs before TIM IE
+	 */
+	u8 *head;
+
+	/**
+	 * head_len - Length of the head buffer in octets
+	 */
+	size_t head_len;
+
+	/**
+	 * tail - Beacon tail following TIM IE
+	 */
+	u8 *tail;
+
+	/**
+	 * tail_len - Length of the tail buffer in octets
+	 */
+	size_t tail_len;
+
+	/**
+	 * dtim_period - DTIM period
+	 */
+	int dtim_period;
+
+	/**
+	 * beacon_int - Beacon interval
+	 */
+	int beacon_int;
+
+	/**
+	 * basic_rates: -1 terminated array of basic rates in 100 kbps
+	 *
+	 * This parameter can be used to set a specific basic rate set for the
+	 * BSS. If %NULL, default basic rate set is used.
+	 */
+	int *basic_rates;
+
+	/**
+	 * beacon_rate: Beacon frame data rate
+	 *
+	 * This parameter can be used to set a specific Beacon frame data rate
+	 * for the BSS. The interpretation of this value depends on the
+	 * rate_type (legacy: in 100 kbps units, HT: HT-MCS, VHT: VHT-MCS,
+	 * HE: HE-MCS). If beacon_rate == 0 and rate_type == 0
+	 * (BEACON_RATE_LEGACY), the default Beacon frame data rate is used.
+	 */
+	unsigned int beacon_rate;
+
+	/**
+	 * beacon_rate_type: Beacon data rate type (legacy/HT/VHT/HE)
+	 */
+	enum beacon_rate_type rate_type;
+
+	/**
+	 * proberesp - Probe Response template
+	 *
+	 * This is used by drivers that reply to Probe Requests internally in
+	 * AP mode and require the full Probe Response template.
+	 */
+	u8 *proberesp;
+
+	/**
+	 * proberesp_len - Length of the proberesp buffer in octets
+	 */
+	size_t proberesp_len;
+
+	/**
+	 * ssid - The SSID to use in Beacon/Probe Response frames
+	 */
+	const u8 *ssid;
+
+	/**
+	 * ssid_len - Length of the SSID (1..32)
+	 */
+	size_t ssid_len;
+
+	/**
+	 * hide_ssid - Whether to hide the SSID
+	 */
+	enum hide_ssid hide_ssid;
+
+	/**
+	 * pairwise_ciphers - WPA_CIPHER_* bitfield
+	 */
+	unsigned int pairwise_ciphers;
+
+	/**
+	 * group_cipher - WPA_CIPHER_*
+	 */
+	unsigned int group_cipher;
+
+	/**
+	 * key_mgmt_suites - WPA_KEY_MGMT_* bitfield
+	 */
+	unsigned int key_mgmt_suites;
+
+	/**
+	 * auth_algs - WPA_AUTH_ALG_* bitfield
+	 */
+	unsigned int auth_algs;
+
+	/**
+	 * wpa_version - WPA_PROTO_* bitfield
+	 */
+	unsigned int wpa_version;
+
+	/**
+	 * privacy - Whether privacy is used in the BSS
+	 */
+	int privacy;
+
+	/**
+	 * beacon_ies - WPS/P2P IE(s) for Beacon frames
+	 *
+	 * This is used to add IEs like WPS IE and P2P IE by drivers that do
+	 * not use the full Beacon template.
+	 */
+	const struct wpabuf *beacon_ies;
+
+	/**
+	 * proberesp_ies - P2P/WPS IE(s) for Probe Response frames
+	 *
+	 * This is used to add IEs like WPS IE and P2P IE by drivers that
+	 * reply to Probe Request frames internally.
+	 */
+	const struct wpabuf *proberesp_ies;
+
+	/**
+	 * assocresp_ies - WPS IE(s) for (Re)Association Response frames
+	 *
+	 * This is used to add IEs like WPS IE by drivers that reply to
+	 * (Re)Association Request frames internally.
+	 */
+	const struct wpabuf *assocresp_ies;
+
+	/**
+	 * isolate - Whether to isolate frames between associated stations
+	 *
+	 * If this is non-zero, the AP is requested to disable forwarding of
+	 * frames between associated stations.
+	 */
+	int isolate;
+
+	/**
+	 * cts_protect - Whether CTS protection is enabled
+	 */
+	int cts_protect;
+
+	/**
+	 * preamble - Whether short preamble is enabled
+	 */
+	int preamble;
+
+	/**
+	 * short_slot_time - Whether short slot time is enabled
+	 *
+	 * 0 = short slot time disable, 1 = short slot time enabled, -1 = do
+	 * not set (e.g., when 802.11g mode is not in use)
+	 */
+	int short_slot_time;
+
+	/**
+	 * ht_opmode - HT operation mode or -1 if HT not in use
+	 */
+	int ht_opmode;
+
+	/**
+	 * interworking - Whether Interworking is enabled
+	 */
+	int interworking;
+
+	/**
+	 * hessid - Homogeneous ESS identifier or %NULL if not set
+	 */
+	const u8 *hessid;
+
+	/**
+	 * access_network_type - Access Network Type (0..15)
+	 *
+	 * This is used for filtering Probe Request frames when Interworking is
+	 * enabled.
+	 */
+	u8 access_network_type;
+
+	/**
+	 * ap_max_inactivity - Timeout in seconds to detect STA's inactivity
+	 *
+	 * This is used by driver which advertises this capability.
+	 */
+	int ap_max_inactivity;
+
+	/**
+	 * ctwindow - Client Traffic Window (in TUs)
+	 */
+	u8 p2p_go_ctwindow;
+
+	/**
+	 * disable_dgaf - Whether group-addressed frames are disabled
+	 */
+	int disable_dgaf;
+
+	/**
+	 * osen - Whether OSEN security is enabled
+	 */
+	int osen;
+
+	/**
+	 * freq - Channel parameters for dynamic bandwidth changes
+	 */
+	struct hostapd_freq_params *freq;
+
+	/**
+	 * reenable - Whether this is to re-enable beaconing
+	 */
+	int reenable;
+
+	/**
+	 * pbss - Whether to start a PCP (in PBSS) instead of an AP in
+	 * infrastructure BSS. Valid only for DMG network.
+	 */
+	int pbss;
+
+	/**
+	 * multicast_to_unicast - Whether to use multicast_to_unicast
+	 *
+	 * If this is non-zero, the AP is requested to perform multicast to
+	 * unicast conversion for ARP, IPv4, and IPv6 frames (possibly within
+	 * 802.1Q). If enabled, such frames are to be sent to each station
+	 * separately, with the DA replaced by their own MAC address rather
+	 * than the group address.
+	 *
+	 * Note that this may break certain expectations of the receiver, such
+	 * as the ability to drop unicast IP packets received within multicast
+	 * L2 frames, or the ability to not send ICMP destination unreachable
+	 * messages for packets received in L2 multicast (which is required,
+	 * but the receiver can't tell the difference if this new option is
+	 * enabled.)
+	 *
+	 * This also doesn't implement the 802.11 DMS (directed multicast
+	 * service).
+	 */
+	int multicast_to_unicast;
+
+	/**
+	 * ftm_responder - Whether FTM responder is enabled
+	 */
+	int ftm_responder;
+
+	/**
+	 * lci - Binary data, the content of an LCI report IE with type 8 as
+	 * defined in IEEE Std 802.11-2016, 9.4.2.22.10
+	 */
+	const struct wpabuf *lci;
+
+	/**
+	 * civic - Binary data, the content of a measurement report IE with
+	 * type 11 as defined in IEEE Std 802.11-2016, 9.4.2.22.13
+	 */
+	const struct wpabuf *civic;
+
+	/**
+	 * he_spr_ctrl - Spatial Reuse control field of SPR element
+	 */
+	u8 he_spr_ctrl;
+
+	/**
+	 * he_spr_non_srg_obss_pd_max_offset - Non-SRG Maximum TX power offset
+	 */
+	u8 he_spr_non_srg_obss_pd_max_offset;
+
+	/**
+	 * he_spr_srg_obss_pd_min_offset - Minimum TX power offset
+	 */
+	u8 he_spr_srg_obss_pd_min_offset;
+
+	/**
+	 * he_spr_srg_obss_pd_max_offset - Maximum TX power offset
+	 */
+	u8 he_spr_srg_obss_pd_max_offset;
+
+	/**
+	 * he_spr_bss_color_bitmap - BSS color values used by members of the
+	 * SRG.
+	 */
+	u8 he_spr_bss_color_bitmap[8];
+
+	/**
+	 * he_spr_partial_bssid_bitmap - Partial BSSID values used by members
+	 * of the SRG.
+	 */
+	u8 he_spr_partial_bssid_bitmap[8];
+
+	/**
+	 * he_bss_color - Whether the BSS Color is disabled
+	 */
+	int he_bss_color_disabled;
+
+	/**
+	 * he_bss_color_partial - The BSS Color AID equation
+	 */
+	int he_bss_color_partial;
+
+	/**
+	 * he_bss_color - The BSS Color of the AP
+	 */
+	int he_bss_color;
+
+	/**
+	 * twt_responder - Whether Target Wait Time responder is enabled
+	 */
+	int twt_responder;
+
+	/**
+	 * sae_pwe - SAE mechanism for PWE derivation
+	 * 0 = hunting-and-pecking loop only
+	 * 1 = hash-to-element only
+	 * 2 = both hunting-and-pecking loop and hash-to-element enabled
+	 */
+	enum sae_pwe sae_pwe;
+
+	/**
+	 * FILS Discovery frame minimum interval in TUs
+	 */
+	u32 fd_min_int;
+
+	/**
+	 * FILS Discovery frame maximum interval in TUs (0 = FD frame disabled)
+	 */
+	u32 fd_max_int;
+
+	/**
+	 * FILS Discovery frame template data
+	 */
+	u8 *fd_frame_tmpl;
+
+	/**
+	 * FILS Discovery frame template length
+	 */
+	size_t fd_frame_tmpl_len;
+
+	/**
+	 * Unsolicited broadcast Probe Response interval in TUs
+	 */
+	unsigned int unsol_bcast_probe_resp_interval;
+
+	/**
+	 * Unsolicited broadcast Probe Response template data
+	 */
+	u8 *unsol_bcast_probe_resp_tmpl;
+
+	/**
+	 * Unsolicited broadcast Probe Response template length
+	 */
+	size_t unsol_bcast_probe_resp_tmpl_len;
+
+	/**
+	 * mbssid_tx_iface - Transmitting interface of the MBSSID set
+	 */
+	const char *mbssid_tx_iface;
+
+	/**
+	 * mbssid_index - The index of this BSS in the MBSSID set
+	 */
+	unsigned int mbssid_index;
+
+	/**
+	 * mbssid_elem - Buffer containing all MBSSID elements
+	 */
+	u8 *mbssid_elem;
+
+	/**
+	 * mbssid_elem_len - Total length of all MBSSID elements
+	 */
+	size_t mbssid_elem_len;
+
+	/**
+	 * mbssid_elem_count - The number of MBSSID elements
+	 */
+	u8 mbssid_elem_count;
+
+	/**
+	 * mbssid_elem_offset - Offsets to elements in mbssid_elem.
+	 * Kernel will use these offsets to generate multiple BSSID beacons.
+	 */
+	u8 **mbssid_elem_offset;
+
+	/**
+	 * ema - Enhanced MBSSID advertisements support.
+	 */
+	bool ema;
+
+	/**
+	 * punct_bitmap - Preamble puncturing bitmap
+	 * Each bit corresponds to a 20 MHz subchannel, the lowest bit for the
+	 * channel with the lowest frequency. A bit set to 1 indicates that the
+	 * subchannel is punctured, otherwise active.
+	 */
+	u16 punct_bitmap;
+
+	/**
+	 * rnr_elem - This buffer contains all of reduced neighbor report (RNR)
+	 * elements
+	 */
+	u8 *rnr_elem;
+
+	/**
+	 * rnr_elem_len - Length of rnr_elem buffer
+	 */
+	size_t rnr_elem_len;
+
+	/**
+	 * rnr_elem_count - Number of RNR elements
+	 */
+	unsigned int rnr_elem_count;
+
+	/**
+	 * rnr_elem_offset - The offsets to the elements in rnr_elem.
+	 * The driver will use these to include RNR elements in EMA beacons.
+	 */
+	u8 **rnr_elem_offset;
+
+	/**
+	 * allowed_freqs - List of allowed 20 MHz channel center frequencies in
+	 * MHz for AP operation. Drivers which support this parameter will
+	 * generate a new list based on this provided list by filtering out
+	 * channels that cannot be used at that time due to regulatory or other
+	 * constraints. The resulting list is used as the list of all allowed
+	 * channels whenever performing operations like ACS and DFS.
+	 */
+	int *allowed_freqs;
+
+	/*
+	 * mld_ap - Whether operating as an AP MLD
+	 */
+	bool mld_ap;
+
+	/**
+	 * mld_link_id - Link id for MLD BSS's
+	 */
+	u8 mld_link_id;
+};
+
+struct wpa_driver_mesh_bss_params {
+#define WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS		0x00000001
+#define WPA_DRIVER_MESH_CONF_FLAG_PEER_LINK_TIMEOUT	0x00000002
+#define WPA_DRIVER_MESH_CONF_FLAG_MAX_PEER_LINKS	0x00000004
+#define WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE		0x00000008
+#define WPA_DRIVER_MESH_CONF_FLAG_RSSI_THRESHOLD	0x00000010
+#define WPA_DRIVER_MESH_CONF_FLAG_FORWARDING		0x00000020
+	/*
+	 * TODO: Other mesh configuration parameters would go here.
+	 * See NL80211_MESHCONF_* for all the mesh config parameters.
+	 */
+	unsigned int flags;
+	int auto_plinks;
+	int peer_link_timeout;
+	int max_peer_links;
+	int rssi_threshold;
+	int forwarding;
+	u16 ht_opmode;
+};
+
+struct wpa_driver_mesh_join_params {
+	const u8 *meshid;
+	int meshid_len;
+	const int *basic_rates;
+	const u8 *ies;
+	int ie_len;
+	struct hostapd_freq_params freq;
+	int beacon_int;
+	int dtim_period;
+	struct wpa_driver_mesh_bss_params conf;
+#define WPA_DRIVER_MESH_FLAG_USER_MPM	0x00000001
+#define WPA_DRIVER_MESH_FLAG_DRIVER_MPM	0x00000002
+#define WPA_DRIVER_MESH_FLAG_SAE_AUTH	0x00000004
+#define WPA_DRIVER_MESH_FLAG_AMPE	0x00000008
+	unsigned int flags;
+	bool handle_dfs;
+	int mcast_rate;
+};
+
+struct wpa_driver_set_key_params {
+	/**
+	 * ifname - Interface name (for multi-SSID/VLAN support) */
+	const char *ifname;
+
+	/**
+	 * alg - Encryption algorithm
+	 *
+	 * (%WPA_ALG_NONE, %WPA_ALG_WEP, %WPA_ALG_TKIP, %WPA_ALG_CCMP,
+	 * %WPA_ALG_BIP_AES_CMAC_128, %WPA_ALG_GCMP, %WPA_ALG_GCMP_256,
+	 * %WPA_ALG_CCMP_256, %WPA_ALG_BIP_GMAC_128, %WPA_ALG_BIP_GMAC_256,
+	 * %WPA_ALG_BIP_CMAC_256);
+	 * %WPA_ALG_NONE clears the key. */
+	enum wpa_alg alg;
+
+	/**
+	 * addr - Address of the peer STA
+	 *
+	 * (BSSID of the current AP when setting pairwise key in station mode),
+	 * ff:ff:ff:ff:ff:ff for broadcast keys, %NULL for default keys that
+	 * are used both for broadcast and unicast; when clearing keys, %NULL
+	 * is used to indicate that both the broadcast-only and default key of
+	 * the specified key index is to be cleared */
+	const u8 *addr;
+
+	/**
+	 * key_idx - Key index
+	 *
+	 * (0..3), usually 0 for unicast keys; 4..5 for IGTK; 6..7 for BIGTK */
+	int key_idx;
+
+	/**
+	 * set_tx - Configure this key as the default Tx key
+	 *
+	 * Only used when driver does not support separate unicast/individual
+	 * key */
+	int set_tx;
+
+	/**
+	 * seq - Sequence number/packet number
+	 *
+	 * seq_len octets, the next packet number to be used for in replay
+	 * protection; configured for Rx keys (in most cases, this is only used
+	 * with broadcast keys and set to zero for unicast keys); %NULL if not
+	 * set */
+	const u8 *seq;
+
+	/**
+	 * seq_len - Length of the seq, depends on the algorithm
+	 *
+	 * TKIP: 6 octets, CCMP/GCMP: 6 octets, IGTK: 6 octets */
+	size_t seq_len;
+
+	/**
+	 * key - Key buffer
+	 *
+	 * TKIP: 16-byte temporal key, 8-byte Tx Mic key, 8-byte Rx Mic Key */
+	const u8 *key;
+
+	/**
+	 * key_len - Length of the key buffer in octets
+	 *
+	 * WEP: 5 or 13, TKIP: 32, CCMP/GCMP: 16, IGTK: 16 */
+	size_t key_len;
+
+	/**
+	 * vlan_id - VLAN index (0..4095) for VLAN offload cases */
+	int vlan_id;
+
+	/**
+	 * key_flag - Additional key flags
+	 *
+	 * %KEY_FLAG_MODIFY
+	 *  Set when an already installed key must be updated.
+	 *  So far the only use-case is changing RX/TX status for
+	 *  pairwise keys. Must not be set when deleting a key.
+	 * %KEY_FLAG_DEFAULT
+	 *  Set when the key is also a default key. Must not be set when
+	 *  deleting a key.
+	 * %KEY_FLAG_RX
+	 *  The key is valid for RX. Must not be set when deleting a key.
+	 * %KEY_FLAG_TX
+	 *  The key is valid for TX. Must not be set when deleting a key.
+	 * %KEY_FLAG_GROUP
+	 *  The key is a broadcast or group key.
+	 * %KEY_FLAG_PAIRWISE
+	 *  The key is a pairwise key.
+	 * %KEY_FLAG_PMK
+	 *  The key is a Pairwise Master Key (PMK).
+	 *
+	 * Valid and pre-defined combinations are:
+	 * %KEY_FLAG_GROUP_RX_TX
+	 *  WEP key not to be installed as default key.
+	 * %KEY_FLAG_GROUP_RX_TX_DEFAULT
+	 *  Default WEP or WPA-NONE key.
+	 * %KEY_FLAG_GROUP_RX
+	 *  GTK key valid for RX only.
+	 * %KEY_FLAG_GROUP_TX_DEFAULT
+	 *  GTK key valid for TX only, immediately taking over TX.
+	 * %KEY_FLAG_PAIRWISE_RX_TX
+	 *  Pairwise key immediately becoming the active pairwise key.
+	 * %KEY_FLAG_PAIRWISE_RX
+	 *  Pairwise key not yet valid for TX. (Only usable when Extended
+	 *  Key ID is supported by the driver.)
+	 * %KEY_FLAG_PAIRWISE_RX_TX_MODIFY
+	 *  Enable TX for a pairwise key installed with
+	 *  KEY_FLAG_PAIRWISE_RX.
+	 *
+	 * Not a valid standalone key type but pre-defined to be combined
+	 * with other key_flags:
+	 * %KEY_FLAG_RX_TX
+	 *  RX/TX key. */
+	enum key_flag key_flag;
+
+	/**
+	 * link_id - MLO Link ID
+	 *
+	 * Set to a valid Link ID (0-14) when applicable, otherwise -1. */
+	int link_id;
+};
+
+enum wpa_driver_if_type {
+	/**
+	 * WPA_IF_STATION - Station mode interface
+	 */
+	WPA_IF_STATION,
+
+	/**
+	 * WPA_IF_AP_VLAN - AP mode VLAN interface
+	 *
+	 * This interface shares its address and Beacon frame with the main
+	 * BSS.
+	 */
+	WPA_IF_AP_VLAN,
+
+	/**
+	 * WPA_IF_AP_BSS - AP mode BSS interface
+	 *
+	 * This interface has its own address and Beacon frame.
+	 */
+	WPA_IF_AP_BSS,
+
+	/**
+	 * WPA_IF_P2P_GO - P2P Group Owner
+	 */
+	WPA_IF_P2P_GO,
+
+	/**
+	 * WPA_IF_P2P_CLIENT - P2P Client
+	 */
+	WPA_IF_P2P_CLIENT,
+
+	/**
+	 * WPA_IF_P2P_GROUP - P2P Group interface (will become either
+	 * WPA_IF_P2P_GO or WPA_IF_P2P_CLIENT, but the role is not yet known)
+	 */
+	WPA_IF_P2P_GROUP,
+
+	/**
+	 * WPA_IF_P2P_DEVICE - P2P Device interface is used to identify the
+	 * abstracted P2P Device function in the driver
+	 */
+	WPA_IF_P2P_DEVICE,
+
+	/*
+	 * WPA_IF_MESH - Mesh interface
+	 */
+	WPA_IF_MESH,
+
+	/*
+	 * WPA_IF_TDLS - TDLS offchannel interface (used for pref freq only)
+	 */
+	WPA_IF_TDLS,
+
+	/*
+	 * WPA_IF_IBSS - IBSS interface (used for pref freq only)
+	 */
+	WPA_IF_IBSS,
+
+	/*
+	 * WPA_IF_NAN - NAN Device
+	 */
+	WPA_IF_NAN,
+
+	/* keep last */
+	WPA_IF_MAX
+};
+
+/**
+ * struct wpa_driver_capa - Driver capability information
+ */
+struct wpa_driver_capa {
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA		0x00000001
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA2		0x00000002
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK	0x00000004
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK	0x00000008
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE	0x00000010
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT		0x00000020
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK		0x00000040
+#define WPA_DRIVER_CAPA_KEY_MGMT_WAPI_PSK	0x00000080
+#define WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B	0x00000100
+#define WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192	0x00000200
+#define WPA_DRIVER_CAPA_KEY_MGMT_OWE		0x00000400
+#define WPA_DRIVER_CAPA_KEY_MGMT_DPP		0x00000800
+#define WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256    0x00001000
+#define WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384    0x00002000
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 0x00004000
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384 0x00008000
+#define WPA_DRIVER_CAPA_KEY_MGMT_SAE 		0x00010000
+#define WPA_DRIVER_CAPA_KEY_MGMT_802_1X_SHA256	0x00020000
+#define WPA_DRIVER_CAPA_KEY_MGMT_PSK_SHA256	0x00040000
+#define WPA_DRIVER_CAPA_KEY_MGMT_TPK_HANDSHAKE	0x00080000
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE		0x00100000
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT_802_1X_SHA384	0x00200000
+#define WPA_DRIVER_CAPA_KEY_MGMT_CCKM		0x00400000
+#define WPA_DRIVER_CAPA_KEY_MGMT_OSEN		0x00800000
+#define WPA_DRIVER_CAPA_KEY_MGMT_SAE_EXT_KEY	0x01000000
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE_EXT_KEY	0x02000000
+	/** Bitfield of supported key management suites */
+	unsigned int key_mgmt;
+	unsigned int key_mgmt_iftype[WPA_IF_MAX];
+
+#define WPA_DRIVER_CAPA_ENC_WEP40	0x00000001
+#define WPA_DRIVER_CAPA_ENC_WEP104	0x00000002
+#define WPA_DRIVER_CAPA_ENC_TKIP	0x00000004
+#define WPA_DRIVER_CAPA_ENC_CCMP	0x00000008
+#define WPA_DRIVER_CAPA_ENC_WEP128	0x00000010
+#define WPA_DRIVER_CAPA_ENC_GCMP	0x00000020
+#define WPA_DRIVER_CAPA_ENC_GCMP_256	0x00000040
+#define WPA_DRIVER_CAPA_ENC_CCMP_256	0x00000080
+#define WPA_DRIVER_CAPA_ENC_BIP		0x00000100
+#define WPA_DRIVER_CAPA_ENC_BIP_GMAC_128	0x00000200
+#define WPA_DRIVER_CAPA_ENC_BIP_GMAC_256	0x00000400
+#define WPA_DRIVER_CAPA_ENC_BIP_CMAC_256	0x00000800
+#define WPA_DRIVER_CAPA_ENC_GTK_NOT_USED	0x00001000
+	/** Bitfield of supported cipher suites */
+	unsigned int enc;
+
+#define WPA_DRIVER_AUTH_OPEN		0x00000001
+#define WPA_DRIVER_AUTH_SHARED		0x00000002
+#define WPA_DRIVER_AUTH_LEAP		0x00000004
+	/** Bitfield of supported IEEE 802.11 authentication algorithms */
+	unsigned int auth;
+
+/** Driver generated WPA/RSN IE */
+#define WPA_DRIVER_FLAGS_DRIVER_IE	0x00000001
+/** Driver needs static WEP key setup after association command */
+#define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC 0x00000002
+/** Driver takes care of all DFS operations */
+#define WPA_DRIVER_FLAGS_DFS_OFFLOAD			0x00000004
+/** Driver takes care of RSN 4-way handshake internally; PMK is configured with
+ * struct wpa_driver_ops::set_key using key_flag = KEY_FLAG_PMK */
+#define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X		0x00000008
+/** Driver is for a wired Ethernet interface */
+#define WPA_DRIVER_FLAGS_WIRED		0x00000010
+/** Driver provides separate commands for authentication and association (SME in
+ * wpa_supplicant). */
+#define WPA_DRIVER_FLAGS_SME		0x00000020
+/** Driver supports AP mode */
+#define WPA_DRIVER_FLAGS_AP		0x00000040
+/** Driver needs static WEP key setup after association has been completed */
+#define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE	0x00000080
+/** Driver supports dynamic HT 20/40 MHz channel changes during BSS lifetime */
+#define WPA_DRIVER_FLAGS_HT_2040_COEX			0x00000100
+/** Driver supports concurrent P2P operations */
+#define WPA_DRIVER_FLAGS_P2P_CONCURRENT	0x00000200
+/**
+ * Driver uses the initial interface as a dedicated management interface, i.e.,
+ * it cannot be used for P2P group operations or non-P2P purposes.
+ */
+#define WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE	0x00000400
+/** This interface is P2P capable (P2P GO or P2P Client) */
+#define WPA_DRIVER_FLAGS_P2P_CAPABLE	0x00000800
+/** Driver supports station and key removal when stopping an AP */
+#define WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT		0x00001000
+/**
+ * Driver uses the initial interface for P2P management interface and non-P2P
+ * purposes (e.g., connect to infra AP), but this interface cannot be used for
+ * P2P group operations.
+ */
+#define WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P		0x00002000
+/**
+ * Driver is known to use valid error codes, i.e., when it indicates that
+ * something (e.g., association) fails, there was indeed a failure and the
+ * operation does not end up getting completed successfully later.
+ */
+#define WPA_DRIVER_FLAGS_VALID_ERROR_CODES		0x00004000
+/** Driver supports off-channel TX */
+#define WPA_DRIVER_FLAGS_OFFCHANNEL_TX			0x00008000
+/** Driver indicates TX status events for EAPOL Data frames */
+#define WPA_DRIVER_FLAGS_EAPOL_TX_STATUS		0x00010000
+/** Driver indicates TX status events for Deauth/Disassoc frames */
+#define WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS		0x00020000
+/** Driver supports roaming (BSS selection) in firmware */
+#define WPA_DRIVER_FLAGS_BSS_SELECTION			0x00040000
+/** Driver supports operating as a TDLS peer */
+#define WPA_DRIVER_FLAGS_TDLS_SUPPORT			0x00080000
+/** Driver requires external TDLS setup/teardown/discovery */
+#define WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP		0x00100000
+/** Driver indicates support for Probe Response offloading in AP mode */
+#define WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD		0x00200000
+/** Driver supports U-APSD in AP mode */
+#define WPA_DRIVER_FLAGS_AP_UAPSD			0x00400000
+/** Driver supports inactivity timer in AP mode */
+#define WPA_DRIVER_FLAGS_INACTIVITY_TIMER		0x00800000
+/** Driver expects user space implementation of MLME in AP mode */
+#define WPA_DRIVER_FLAGS_AP_MLME			0x01000000
+/** Driver supports SAE with user space SME */
+#define WPA_DRIVER_FLAGS_SAE				0x02000000
+/** Driver makes use of OBSS scan mechanism in wpa_supplicant */
+#define WPA_DRIVER_FLAGS_OBSS_SCAN			0x04000000
+/** Driver supports IBSS (Ad-hoc) mode */
+#define WPA_DRIVER_FLAGS_IBSS				0x08000000
+/** Driver supports radar detection */
+#define WPA_DRIVER_FLAGS_RADAR				0x10000000
+/** Driver supports a dedicated interface for P2P Device */
+#define WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE		0x20000000
+/** Driver supports QoS Mapping */
+#define WPA_DRIVER_FLAGS_QOS_MAPPING			0x40000000
+/** Driver supports CSA in AP mode */
+#define WPA_DRIVER_FLAGS_AP_CSA				0x80000000
+/** Driver supports mesh */
+#define WPA_DRIVER_FLAGS_MESH			0x0000000100000000ULL
+/** Driver support ACS offload */
+#define WPA_DRIVER_FLAGS_ACS_OFFLOAD		0x0000000200000000ULL
+/** Driver supports key management offload */
+#define WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD	0x0000000400000000ULL
+/** Driver supports TDLS channel switching */
+#define WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH	0x0000000800000000ULL
+/** Driver supports IBSS with HT datarates */
+#define WPA_DRIVER_FLAGS_HT_IBSS		0x0000001000000000ULL
+/** Driver supports IBSS with VHT datarates */
+#define WPA_DRIVER_FLAGS_VHT_IBSS		0x0000002000000000ULL
+/** Driver supports automatic band selection */
+#define WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY	0x0000004000000000ULL
+/** Driver supports simultaneous off-channel operations */
+#define WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS	0x0000008000000000ULL
+/** Driver supports full AP client state */
+#define WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE	0x0000010000000000ULL
+/** Driver supports P2P Listen offload */
+#define WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD     0x0000020000000000ULL
+/** Driver supports FILS */
+#define WPA_DRIVER_FLAGS_SUPPORT_FILS		0x0000040000000000ULL
+/** Driver supports Beacon frame TX rate configuration (legacy rates) */
+#define WPA_DRIVER_FLAGS_BEACON_RATE_LEGACY	0x0000080000000000ULL
+/** Driver supports Beacon frame TX rate configuration (HT rates) */
+#define WPA_DRIVER_FLAGS_BEACON_RATE_HT		0x0000100000000000ULL
+/** Driver supports Beacon frame TX rate configuration (VHT rates) */
+#define WPA_DRIVER_FLAGS_BEACON_RATE_VHT	0x0000200000000000ULL
+/** Driver supports mgmt_tx with random TX address in non-connected state */
+#define WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA	0x0000400000000000ULL
+/** Driver supports mgmt_tx with random TX addr in connected state */
+#define WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA_CONNECTED	0x0000800000000000ULL
+/** Driver supports better BSS reporting with sched_scan in connected mode */
+#define WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI	0x0001000000000000ULL
+/** Driver supports HE capabilities */
+#define WPA_DRIVER_FLAGS_HE_CAPABILITIES	0x0002000000000000ULL
+/** Driver supports FILS shared key offload */
+#define WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD	0x0004000000000000ULL
+/** Driver supports all OCE STA specific mandatory features */
+#define WPA_DRIVER_FLAGS_OCE_STA		0x0008000000000000ULL
+/** Driver supports all OCE AP specific mandatory features */
+#define WPA_DRIVER_FLAGS_OCE_AP			0x0010000000000000ULL
+/**
+ * Driver supports all OCE STA-CFON specific mandatory features only.
+ * If a driver sets this bit but not the %WPA_DRIVER_FLAGS_OCE_AP, the
+ * userspace shall assume that this driver may not support all OCE AP
+ * functionality but can support only OCE STA-CFON functionality.
+ */
+#define WPA_DRIVER_FLAGS_OCE_STA_CFON		0x0020000000000000ULL
+/** Driver supports MFP-optional in the connect command */
+#define WPA_DRIVER_FLAGS_MFP_OPTIONAL		0x0040000000000000ULL
+/** Driver is a self-managed regulatory device */
+#define WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY       0x0080000000000000ULL
+/** Driver supports FTM responder functionality */
+#define WPA_DRIVER_FLAGS_FTM_RESPONDER		0x0100000000000000ULL
+/** Driver support 4-way handshake offload for WPA-Personal */
+#define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK	0x0200000000000000ULL
+/** Driver supports a separate control port TX for EAPOL frames */
+#define WPA_DRIVER_FLAGS_CONTROL_PORT		0x0400000000000000ULL
+/** Driver supports VLAN offload */
+#define WPA_DRIVER_FLAGS_VLAN_OFFLOAD		0x0800000000000000ULL
+/** Driver supports UPDATE_FT_IES command */
+#define WPA_DRIVER_FLAGS_UPDATE_FT_IES		0x1000000000000000ULL
+/** Driver can correctly rekey PTKs without Extended Key ID */
+#define WPA_DRIVER_FLAGS_SAFE_PTK0_REKEYS	0x2000000000000000ULL
+/** Driver supports Beacon protection */
+#define WPA_DRIVER_FLAGS_BEACON_PROTECTION	0x4000000000000000ULL
+/** Driver supports Extended Key ID */
+#define WPA_DRIVER_FLAGS_EXTENDED_KEY_ID	0x8000000000000000ULL
+	u64 flags;
+
+/** Driver supports a separate control port RX for EAPOL frames */
+#define WPA_DRIVER_FLAGS2_CONTROL_PORT_RX	0x0000000000000001ULL
+/** Driver supports TX status reports for EAPOL frames through control port */
+#define WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS 0x0000000000000002ULL
+/** Driver supports secure LTF in AP mode */
+#define WPA_DRIVER_FLAGS2_SEC_LTF_AP		0x0000000000000004ULL
+/** Driver supports secure RTT measurement exchange in AP mode */
+#define WPA_DRIVER_FLAGS2_SEC_RTT_AP		0x0000000000000008ULL
+/**
+ * Driver supports protection of range negotiation and measurement management
+ * frames in AP mode
+ */
+#define WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP	0x0000000000000010ULL
+/** Driver supports Beacon frame TX rate configuration (HE rates) */
+#define WPA_DRIVER_FLAGS2_BEACON_RATE_HE	0x0000000000000020ULL
+/** Driver supports Beacon protection only in client mode */
+#define WPA_DRIVER_FLAGS2_BEACON_PROTECTION_CLIENT 0x0000000000000040ULL
+/** Driver supports Operating Channel Validation */
+#define WPA_DRIVER_FLAGS2_OCV			0x0000000000000080ULL
+/** Driver expects user space implementation of SME in AP mode */
+#define WPA_DRIVER_FLAGS2_AP_SME		0x0000000000000100ULL
+/** Driver handles SA Query procedures in AP mode */
+#define WPA_DRIVER_FLAGS2_SA_QUERY_OFFLOAD_AP	0x0000000000000200ULL
+/** Driver supports background radar/CAC detection */
+#define WPA_DRIVER_RADAR_BACKGROUND		0x0000000000000400ULL
+/** Driver supports secure LTF in STA mode */
+#define WPA_DRIVER_FLAGS2_SEC_LTF_STA		0x0000000000000800ULL
+/** Driver supports secure RTT measurement exchange in STA mode */
+#define WPA_DRIVER_FLAGS2_SEC_RTT_STA		0x0000000000001000ULL
+/**
+ * Driver supports protection of range negotiation and measurement management
+ * frames in STA mode
+ */
+#define WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA	0x0000000000002000ULL
+/** Driver supports MLO in station/AP mode */
+#define WPA_DRIVER_FLAGS2_MLO			0x0000000000004000ULL
+	u64 flags2;
+
+#define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
+	(drv_flags & WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE)
+
+	unsigned int wmm_ac_supported:1;
+
+	unsigned int mac_addr_rand_scan_supported:1;
+	unsigned int mac_addr_rand_sched_scan_supported:1;
+
+	/** Maximum number of supported active probe SSIDs */
+	int max_scan_ssids;
+
+	/** Maximum number of supported active probe SSIDs for sched_scan */
+	int max_sched_scan_ssids;
+
+	/** Maximum number of supported scan plans for scheduled scan */
+	unsigned int max_sched_scan_plans;
+
+	/** Maximum interval in a scan plan. In seconds */
+	u32 max_sched_scan_plan_interval;
+
+	/** Maximum number of iterations in a single scan plan */
+	u32 max_sched_scan_plan_iterations;
+
+	/** Whether sched_scan (offloaded scanning) is supported */
+	int sched_scan_supported;
+
+	/** Maximum number of supported match sets for sched_scan */
+	int max_match_sets;
+
+	/**
+	 * max_remain_on_chan - Maximum remain-on-channel duration in msec
+	 */
+	unsigned int max_remain_on_chan;
+
+	/**
+	 * max_stations - Maximum number of associated stations the driver
+	 * supports in AP mode
+	 */
+	unsigned int max_stations;
+
+	/**
+	 * probe_resp_offloads - Bitmap of supported protocols by the driver
+	 * for Probe Response offloading.
+	 */
+/** Driver Probe Response offloading support for WPS ver. 1 */
+#define WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS		0x00000001
+/** Driver Probe Response offloading support for WPS ver. 2 */
+#define WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2		0x00000002
+/** Driver Probe Response offloading support for P2P */
+#define WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P		0x00000004
+/** Driver Probe Response offloading support for IEEE 802.11u (Interworking) */
+#define WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING	0x00000008
+	unsigned int probe_resp_offloads;
+
+	unsigned int max_acl_mac_addrs;
+
+	/**
+	 * Number of supported concurrent channels
+	 */
+	unsigned int num_multichan_concurrent;
+
+	/**
+	 * extended_capa - extended capabilities in driver/device
+	 *
+	 * Must be allocated and freed by driver and the pointers must be
+	 * valid for the lifetime of the driver, i.e., freed in deinit()
+	 */
+	const u8 *extended_capa, *extended_capa_mask;
+	unsigned int extended_capa_len;
+
+	struct wowlan_triggers wowlan_triggers;
+
+/** Driver adds the DS Params Set IE in Probe Request frames */
+#define WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES	0x00000001
+/** Driver adds the WFA TPC IE in Probe Request frames */
+#define WPA_DRIVER_FLAGS_WFA_TPC_IE_IN_PROBES		0x00000002
+/** Driver handles quiet period requests */
+#define WPA_DRIVER_FLAGS_QUIET				0x00000004
+/**
+ * Driver is capable of inserting the current TX power value into the body of
+ * transmitted frames.
+ * Background: Some Action frames include a TPC Report IE. This IE contains a
+ * TX power field, which has to be updated by lower layers. One such Action
+ * frame is Link Measurement Report (part of RRM). Another is TPC Report (part
+ * of spectrum management). Note that this insertion takes place at a fixed
+ * offset, namely the 6th byte in the Action frame body.
+ */
+#define WPA_DRIVER_FLAGS_TX_POWER_INSERTION		0x00000008
+/**
+ * Driver supports RRM. With this support, the driver will accept to use RRM in
+ * (Re)Association Request frames, without supporting quiet period.
+ */
+#define WPA_DRIVER_FLAGS_SUPPORT_RRM			0x00000010
+
+/** Driver supports setting the scan dwell time */
+#define WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL		0x00000020
+/** Driver supports Beacon Report Measurement */
+#define WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT		0x00000040
+
+	u32 rrm_flags;
+
+	/* Driver concurrency capabilities */
+	unsigned int conc_capab;
+	/* Maximum number of concurrent channels on 2.4 GHz */
+	unsigned int max_conc_chan_2_4;
+	/* Maximum number of concurrent channels on 5 GHz */
+	unsigned int max_conc_chan_5_0;
+
+	/* Maximum number of supported CSA counters */
+	u16 max_csa_counters;
+
+	/* Maximum number of supported AKM suites in commands */
+	unsigned int max_num_akms;
+
+	/* Maximum number of interfaces supported for MBSSID advertisement */
+	unsigned int mbssid_max_interfaces;
+	/* Maximum profile periodicity for enhanced MBSSID advertisement */
+	unsigned int ema_max_periodicity;
+};
+
+
+struct hostapd_data;
+
+enum guard_interval {
+	GUARD_INTERVAL_0_4 = 1,
+	GUARD_INTERVAL_0_8 = 2,
+	GUARD_INTERVAL_1_6 = 3,
+	GUARD_INTERVAL_3_2 = 4,
+};
+
+#define STA_DRV_DATA_TX_MCS BIT(0)
+#define STA_DRV_DATA_RX_MCS BIT(1)
+#define STA_DRV_DATA_TX_VHT_MCS BIT(2)
+#define STA_DRV_DATA_RX_VHT_MCS BIT(3)
+#define STA_DRV_DATA_TX_VHT_NSS BIT(4)
+#define STA_DRV_DATA_RX_VHT_NSS BIT(5)
+#define STA_DRV_DATA_TX_SHORT_GI BIT(6)
+#define STA_DRV_DATA_RX_SHORT_GI BIT(7)
+#define STA_DRV_DATA_LAST_ACK_RSSI BIT(8)
+#define STA_DRV_DATA_CONN_TIME BIT(9)
+#define STA_DRV_DATA_TX_HE_MCS BIT(10)
+#define STA_DRV_DATA_RX_HE_MCS BIT(11)
+#define STA_DRV_DATA_TX_HE_NSS BIT(12)
+#define STA_DRV_DATA_RX_HE_NSS BIT(13)
+#define STA_DRV_DATA_TX_HE_DCM BIT(14)
+#define STA_DRV_DATA_RX_HE_DCM BIT(15)
+#define STA_DRV_DATA_TX_HE_GI BIT(16)
+#define STA_DRV_DATA_RX_HE_GI BIT(17)
+
+struct hostap_sta_driver_data {
+	unsigned long rx_packets, tx_packets;
+	unsigned long long rx_bytes, tx_bytes;
+	unsigned long long rx_airtime, tx_airtime;
+	unsigned long long beacons_count;
+	int bytes_64bit; /* whether 64-bit byte counters are supported */
+	unsigned long current_tx_rate; /* in kbps */
+	unsigned long current_rx_rate; /* in kbps */
+	unsigned long inactive_msec;
+	unsigned long connected_sec;
+	unsigned long flags; /* bitfield of STA_DRV_DATA_* */
+	unsigned long num_ps_buf_frames;
+	unsigned long tx_retry_failed;
+	unsigned long tx_retry_count;
+	s8 last_ack_rssi;
+	unsigned long backlog_packets;
+	unsigned long backlog_bytes;
+	unsigned long fcs_error_count;
+	unsigned long beacon_loss_count;
+	unsigned long expected_throughput;
+	unsigned long rx_drop_misc;
+	unsigned long rx_mpdus;
+	int signal; /* dBm; or -WPA_INVALID_NOISE */
+	u8 rx_hemcs;
+	u8 tx_hemcs;
+	u8 rx_vhtmcs;
+	u8 tx_vhtmcs;
+	u8 rx_mcs;
+	u8 tx_mcs;
+	u8 rx_he_nss;
+	u8 tx_he_nss;
+	u8 rx_vht_nss;
+	u8 tx_vht_nss;
+	s8 avg_signal; /* dBm */
+	s8 avg_beacon_signal; /* dBm */
+	s8 avg_ack_signal; /* dBm */
+	enum guard_interval rx_guard_interval, tx_guard_interval;
+	u8 rx_dcm, tx_dcm;
+};
+
+struct hostapd_sta_add_params {
+	const u8 *addr;
+	u16 aid;
+	u16 capability;
+	const u8 *supp_rates;
+	size_t supp_rates_len;
+	u16 listen_interval;
+	const struct ieee80211_ht_capabilities *ht_capabilities;
+	const struct ieee80211_vht_capabilities *vht_capabilities;
+	int vht_opmode_enabled;
+	u8 vht_opmode;
+	const struct ieee80211_he_capabilities *he_capab;
+	size_t he_capab_len;
+	const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab;
+	const struct ieee80211_eht_capabilities *eht_capab;
+	size_t eht_capab_len;
+	u32 flags; /* bitmask of WPA_STA_* flags */
+	u32 flags_mask; /* unset bits in flags */
+#ifdef CONFIG_MESH
+	enum mesh_plink_state plink_state;
+	u16 peer_aid;
+#endif /* CONFIG_MESH */
+	int set; /* Set STA parameters instead of add */
+	u8 qosinfo;
+	const u8 *ext_capab;
+	size_t ext_capab_len;
+	const u8 *supp_channels;
+	size_t supp_channels_len;
+	const u8 *supp_oper_classes;
+	size_t supp_oper_classes_len;
+	int support_p2p_ps;
+
+	bool mld_link_sta;
+	s8 mld_link_id;
+	const u8 *mld_link_addr;
+};
+
+struct mac_address {
+	u8 addr[ETH_ALEN];
+};
+
+struct hostapd_acl_params {
+	u8 acl_policy;
+	unsigned int num_mac_acl;
+	struct mac_address mac_acl[0];
+};
+
+struct wpa_init_params {
+	void *global_priv;
+	const u8 *bssid;
+	const char *ifname;
+	const char *driver_params;
+	int use_pae_group_addr;
+	char **bridge;
+	size_t num_bridge;
+
+	u8 *own_addr; /* buffer for writing own MAC address */
+};
+
+
+struct wpa_bss_params {
+	/** Interface name (for multi-SSID/VLAN support) */
+	const char *ifname;
+	/** Whether IEEE 802.1X or WPA/WPA2 is enabled */
+	int enabled;
+
+	int wpa;
+	int ieee802_1x;
+	int wpa_group;
+	int wpa_pairwise;
+	int wpa_key_mgmt;
+	int rsn_preauth;
+	enum mfp_options ieee80211w;
+};
+
+#define WPA_STA_AUTHORIZED BIT(0)
+#define WPA_STA_WMM BIT(1)
+#define WPA_STA_SHORT_PREAMBLE BIT(2)
+#define WPA_STA_MFP BIT(3)
+#define WPA_STA_TDLS_PEER BIT(4)
+#define WPA_STA_AUTHENTICATED BIT(5)
+#define WPA_STA_ASSOCIATED BIT(6)
+
+enum tdls_oper {
+	TDLS_DISCOVERY_REQ,
+	TDLS_SETUP,
+	TDLS_TEARDOWN,
+	TDLS_ENABLE_LINK,
+	TDLS_DISABLE_LINK,
+	TDLS_ENABLE,
+	TDLS_DISABLE
+};
+
+enum wnm_oper {
+	WNM_SLEEP_ENTER_CONFIRM,
+	WNM_SLEEP_ENTER_FAIL,
+	WNM_SLEEP_EXIT_CONFIRM,
+	WNM_SLEEP_EXIT_FAIL,
+	WNM_SLEEP_TFS_REQ_IE_ADD,   /* STA requests driver to add TFS req IE */
+	WNM_SLEEP_TFS_REQ_IE_NONE,  /* STA requests empty TFS req IE */
+	WNM_SLEEP_TFS_REQ_IE_SET,   /* AP requests driver to set TFS req IE for
+				     * a STA */
+	WNM_SLEEP_TFS_RESP_IE_ADD,  /* AP requests driver to add TFS resp IE
+				     * for a STA */
+	WNM_SLEEP_TFS_RESP_IE_NONE, /* AP requests empty TFS resp IE */
+	WNM_SLEEP_TFS_RESP_IE_SET,  /* AP requests driver to set TFS resp IE
+				     * for a STA */
+	WNM_SLEEP_TFS_IE_DEL        /* AP delete the TFS IE */
+};
+
+/* enum smps_mode - SMPS mode definitions */
+enum smps_mode {
+	SMPS_AUTOMATIC,
+	SMPS_OFF,
+	SMPS_DYNAMIC,
+	SMPS_STATIC,
+
+	/* Keep last */
+	SMPS_INVALID,
+};
+
+#define WPA_INVALID_NOISE 9999
+
+/**
+ * struct wpa_signal_info - Information about channel signal quality
+ * @frequency: control frequency
+ * @above_threshold: true if the above threshold was crossed
+ *	(relevant for a CQM event)
+ * @data: STA information
+ * @current_noise: %WPA_INVALID_NOISE if not supported
+ * @chanwidth: channel width
+ * @center_frq1: center frequency for the first segment
+ * @center_frq2: center frequency for the second segment (if relevant)
+ */
+struct wpa_signal_info {
+	u32 frequency;
+	int above_threshold;
+	struct hostap_sta_driver_data data;
+	int current_noise;
+	enum chan_width chanwidth;
+	int center_frq1;
+	int center_frq2;
+};
+
+struct wpa_mlo_signal_info {
+	u16 valid_links;
+	struct wpa_signal_info links[MAX_NUM_MLD_LINKS];
+};
+
+/**
+ * struct wpa_channel_info - Information about the current channel
+ * @frequency: Center frequency of the primary 20 MHz channel
+ * @chanwidth: Width of the current operating channel
+ * @sec_channel: Location of the secondary 20 MHz channel (either +1 or -1).
+ *	This field is only filled in when using a 40 MHz channel.
+ * @center_frq1: Center frequency of frequency segment 0
+ * @center_frq2: Center frequency of frequency segment 1 (for 80+80 channels)
+ * @seg1_idx: Frequency segment 1 index when using a 80+80 channel. This is
+ *	derived from center_frq2 for convenience.
+ */
+struct wpa_channel_info {
+	u32 frequency;
+	enum chan_width chanwidth;
+	int sec_channel;
+	int center_frq1;
+	int center_frq2;
+	u8 seg1_idx;
+};
+
+/**
+ * struct beacon_data - Beacon data
+ * @head: Head portion of Beacon frame (before TIM IE)
+ * @tail: Tail portion of Beacon frame (after TIM IE)
+ * @beacon_ies: Extra information element(s) to add into Beacon frames or %NULL
+ * @proberesp_ies: Extra information element(s) to add into Probe Response
+ *	frames or %NULL
+ * @assocresp_ies: Extra information element(s) to add into (Re)Association
+ *	Response frames or %NULL
+ * @probe_resp: Probe Response frame template
+ * @head_len: Length of @head
+ * @tail_len: Length of @tail
+ * @beacon_ies_len: Length of beacon_ies in octets
+ * @proberesp_ies_len: Length of proberesp_ies in octets
+ * @proberesp_ies_len: Length of proberesp_ies in octets
+ * @probe_resp_len: Length of probe response template (@probe_resp)
+ */
+struct beacon_data {
+	u8 *head, *tail;
+	u8 *beacon_ies;
+	u8 *proberesp_ies;
+	u8 *assocresp_ies;
+	u8 *probe_resp;
+
+	size_t head_len, tail_len;
+	size_t beacon_ies_len;
+	size_t proberesp_ies_len;
+	size_t assocresp_ies_len;
+	size_t probe_resp_len;
+};
+
+/**
+ * struct csa_settings - Settings for channel switch command
+ * @cs_count: Count in Beacon frames (TBTT) to perform the switch
+ * @block_tx: 1 - block transmission for CSA period
+ * @freq_params: Next channel frequency parameter
+ * @beacon_csa: Beacon/probe resp/asooc resp info for CSA period
+ * @beacon_after: Next beacon/probe resp/asooc resp info
+ * @counter_offset_beacon: Offset to the count field in beacon's tail
+ * @counter_offset_presp: Offset to the count field in probe resp.
+ * @punct_bitmap - Preamble puncturing bitmap
+ */
+struct csa_settings {
+	u8 cs_count;
+	u8 block_tx;
+
+	struct hostapd_freq_params freq_params;
+	struct beacon_data beacon_csa;
+	struct beacon_data beacon_after;
+
+	u16 counter_offset_beacon[2];
+	u16 counter_offset_presp[2];
+
+	u16 punct_bitmap;
+};
+
+/**
+ * struct cca_settings - Settings for color switch command
+ * @cca_count: Count in Beacon frames (TBTT) to perform the switch
+ * @cca_color: The new color that we are switching to
+ * @beacon_cca: Beacon/Probe Response/(Re)Association Response frame info for
+ * color switch period
+ * @beacon_after: Next Beacon/Probe Response/(Re)Association Response frame info
+ * @counter_offset_beacon: Offset to the count field in Beacon frame tail
+ * @counter_offset_presp: Offset to the count field in Probe Response frame
+ */
+struct cca_settings {
+	u8 cca_count;
+	u8 cca_color;
+
+	struct beacon_data beacon_cca;
+	struct beacon_data beacon_after;
+
+	u16 counter_offset_beacon;
+	u16 counter_offset_presp;
+};
+
+/* TDLS peer capabilities for send_tdls_mgmt() */
+enum tdls_peer_capability {
+	TDLS_PEER_HT = BIT(0),
+	TDLS_PEER_VHT = BIT(1),
+	TDLS_PEER_WMM = BIT(2),
+	TDLS_PEER_HE = BIT(3),
+};
+
+/* valid info in the wmm_params struct */
+enum wmm_params_valid_info {
+	WMM_PARAMS_UAPSD_QUEUES_INFO = BIT(0),
+};
+
+/**
+ * struct wmm_params - WMM parameterss configured for this association
+ * @info_bitmap: Bitmap of valid wmm_params info; indicates what fields
+ *	of the struct contain valid information.
+ * @uapsd_queues: Bitmap of ACs configured for uapsd (valid only if
+ *	%WMM_PARAMS_UAPSD_QUEUES_INFO is set)
+ */
+struct wmm_params {
+	u8 info_bitmap;
+	u8 uapsd_queues;
+};
+
+#ifdef CONFIG_MACSEC
+struct macsec_init_params {
+	bool always_include_sci;
+	bool use_es;
+	bool use_scb;
+};
+#endif /* CONFIG_MACSEC */
+
+enum drv_br_port_attr {
+	DRV_BR_PORT_ATTR_PROXYARP,
+	DRV_BR_PORT_ATTR_HAIRPIN_MODE,
+	DRV_BR_PORT_ATTR_MCAST2UCAST,
+};
+
+enum drv_br_net_param {
+	DRV_BR_NET_PARAM_GARP_ACCEPT,
+	DRV_BR_MULTICAST_SNOOPING,
+};
+
+struct drv_acs_params {
+	/* Selected mode (HOSTAPD_MODE_*) */
+	enum hostapd_hw_mode hw_mode;
+
+	/* Indicates whether HT is enabled */
+	int ht_enabled;
+
+	/* Indicates whether HT40 is enabled */
+	int ht40_enabled;
+
+	/* Indicates whether VHT is enabled */
+	int vht_enabled;
+
+	/* Configured ACS channel width */
+	u16 ch_width;
+
+	/* ACS frequency list info */
+	const int *freq_list;
+
+	/* Indicates whether EDMG is enabled */
+	int edmg_enabled;
+
+	/* Indicates whether EHT is enabled */
+	bool eht_enabled;
+};
+
+struct wpa_bss_trans_info {
+	u8 mbo_transition_reason;
+	u8 n_candidates;
+	u8 *bssid;
+};
+
+struct wpa_bss_candidate_info {
+	u8 num;
+	struct candidate_list {
+		u8 bssid[ETH_ALEN];
+		u8 is_accept;
+		u32 reject_reason;
+	} *candidates;
+};
+
+struct wpa_pmkid_params {
+	const u8 *bssid;
+	const u8 *ssid;
+	size_t ssid_len;
+	const u8 *fils_cache_id;
+	const u8 *pmkid;
+	const u8 *pmk;
+	size_t pmk_len;
+	u32 pmk_lifetime;
+	u8 pmk_reauth_threshold;
+};
+
+/* Mask used to specify which connection parameters have to be updated */
+enum wpa_drv_update_connect_params_mask {
+	WPA_DRV_UPDATE_ASSOC_IES	= BIT(0),
+	WPA_DRV_UPDATE_FILS_ERP_INFO	= BIT(1),
+	WPA_DRV_UPDATE_AUTH_TYPE	= BIT(2),
+};
+
+/**
+ * struct external_auth - External authentication trigger parameters
+ *
+ * These are used across the external authentication request and event
+ * interfaces.
+ * @action: Action type / trigger for external authentication. Only significant
+ *	for the event interface.
+ * @bssid: BSSID of the peer with which the authentication has to happen. Used
+ *	by both the request and event interface.
+ * @ssid: SSID of the AP. Used by both the request and event interface.
+ * @ssid_len: SSID length in octets.
+ * @key_mgmt_suite: AKM suite of the respective authentication. Optional for
+ *	the request interface.
+ * @status: Status code, %WLAN_STATUS_SUCCESS for successful authentication,
+ *	use %WLAN_STATUS_UNSPECIFIED_FAILURE if wpa_supplicant cannot give
+ *	the real status code for failures. Used only for the request interface
+ *	from user space to the driver.
+ * @pmkid: Generated PMKID as part of external auth exchange (e.g., SAE).
+ * @mld_addr: AP's MLD address or %NULL if MLO is not used
+ */
+struct external_auth {
+	enum {
+		EXT_AUTH_START,
+		EXT_AUTH_ABORT,
+	} action;
+	const u8 *bssid;
+	const u8 *ssid;
+	size_t ssid_len;
+	unsigned int key_mgmt_suite;
+	u16 status;
+	const u8 *pmkid;
+	const u8 *mld_addr;
+};
+
+#define WPAS_MAX_PASN_PEERS 10
+
+enum pasn_status {
+	PASN_STATUS_SUCCESS = 0,
+	PASN_STATUS_FAILURE = 1,
+};
+
+/**
+ * struct pasn_peer - PASN peer parameters
+ *
+ * Used to process the PASN authentication event from the driver to
+ * userspace and to send a response back.
+ * @own_addr: Own MAC address specified by the driver to use for PASN
+ *	handshake.
+ * @peer_addr: MAC address of the peer with which PASN authentication is to be
+ *	performed.
+ * @network_id: Unique id for the network.
+ *	This identifier is used as a unique identifier for each network
+ *	block when using the control interface. Each network is allocated an
+ *	id when it is being created, either when reading the configuration
+ *	file or when a new network is added through the control interface.
+ * @akmp: Authentication key management protocol type supported.
+ * @cipher: Cipher suite.
+ * @group: Finite cyclic group. Default group used is 19 (ECC).
+ * @ltf_keyseed_required: Indicates whether LTF keyseed generation is required
+ * @status: PASN response status, %PASN_STATUS_SUCCESS for successful
+ *	authentication, use %PASN_STATUS_FAILURE if PASN authentication
+ *	fails or if wpa_supplicant fails to set the security ranging context to
+ *	the driver
+ */
+struct pasn_peer {
+	u8 own_addr[ETH_ALEN];
+	u8 peer_addr[ETH_ALEN];
+	int network_id;
+	int akmp;
+	int cipher;
+	int group;
+	bool ltf_keyseed_required;
+	enum pasn_status status;
+};
+
+/**
+ * struct pasn_auth - PASN authentication trigger parameters
+ *
+ * These are used across the PASN authentication event from the driver to
+ * userspace and to send a response to it.
+ * @action: Action type. Only significant for the event interface.
+ * @num_peers: The number of peers for which the PASN handshake is requested
+ *	for.
+ * @peer: Holds the peer details.
+ */
+struct pasn_auth {
+	enum {
+		PASN_ACTION_AUTH,
+		PASN_ACTION_DELETE_SECURE_RANGING_CONTEXT,
+	} action;
+	unsigned int num_peers;
+	struct pasn_peer peer[WPAS_MAX_PASN_PEERS];
+};
+
+/**
+ * struct secure_ranging_params - Parameters required to set secure ranging
+ *	context for a peer.
+ *
+ * @action: Add or delete a security context to the driver.
+ * @own_addr: Own MAC address used during key derivation.
+ * @peer_addr: Address of the peer device.
+ * @cipher: Cipher suite.
+ * @tk_len: Length of temporal key.
+ * @tk: Temporal key buffer.
+ * @ltf_keyseed_len: Length of LTF keyseed.
+ * @ltf_keyeed: LTF keyseed buffer.
+ */
+struct secure_ranging_params {
+	u32 action;
+	const u8 *own_addr;
+	const u8 *peer_addr;
+	u32 cipher;
+	u8 tk_len;
+	const u8 *tk;
+	u8 ltf_keyseed_len;
+	const u8 *ltf_keyseed;
+};
+
+/* enum nested_attr - Used to specify if subcommand uses nested attributes */
+enum nested_attr {
+	NESTED_ATTR_NOT_USED = 0,
+	NESTED_ATTR_USED = 1,
+	NESTED_ATTR_UNSPECIFIED = 2,
+};
+
+/* Preferred channel list information */
+
+/* GO role */
+#define WEIGHTED_PCL_GO BIT(0)
+/* P2P Client role */
+#define WEIGHTED_PCL_CLI BIT(1)
+/* Must be considered for operating channel */
+#define WEIGHTED_PCL_MUST_CONSIDER BIT(2)
+/* Should be excluded in GO negotiation */
+#define WEIGHTED_PCL_EXCLUDE BIT(3)
+
+/* Preferred channel list with weight */
+struct weighted_pcl {
+	u32 freq; /* MHz */
+	u8 weight;
+	u32 flag; /* bitmap for WEIGHTED_PCL_* */
+};
+
+struct driver_sta_mlo_info {
+	bool default_map;
+	u16 req_links; /* bitmap of requested link IDs */
+	u16 valid_links; /* bitmap of accepted link IDs */
+	u8 assoc_link_id;
+	u8 ap_mld_addr[ETH_ALEN];
+	struct {
+		u8 addr[ETH_ALEN];
+		u8 bssid[ETH_ALEN];
+		unsigned int freq;
+		struct t2lm_mapping t2lmap;
+	} links[MAX_NUM_MLD_LINKS];
+};
+
+/**
+ * struct wpa_driver_ops - Driver interface API definition
+ *
+ * This structure defines the API that each driver interface needs to implement
+ * for core wpa_supplicant code. All driver specific functionality is captured
+ * in this wrapper.
+ */
+struct wpa_driver_ops {
+	/** Name of the driver interface */
+	const char *name;
+	/** One line description of the driver interface */
+	const char *desc;
+
+	/**
+	 * get_bssid - Get the current BSSID
+	 * @priv: private driver interface data
+	 * @bssid: buffer for BSSID (ETH_ALEN = 6 bytes)
+	 *
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * Query kernel driver for the current BSSID and copy it to bssid.
+	 * Setting bssid to 00:00:00:00:00:00 is recommended if the STA is not
+	 * associated.
+	 */
+	int (*get_bssid)(void *priv, u8 *bssid);
+
+	/**
+	 * get_ssid - Get the current SSID
+	 * @priv: private driver interface data
+	 * @ssid: buffer for SSID (at least 32 bytes)
+	 *
+	 * Returns: Length of the SSID on success, -1 on failure
+	 *
+	 * Query kernel driver for the current SSID and copy it to ssid.
+	 * Returning zero is recommended if the STA is not associated.
+	 *
+	 * Note: SSID is an array of octets, i.e., it is not nul terminated and
+	 * can, at least in theory, contain control characters (including nul)
+	 * and as such, should be processed as binary data, not a printable
+	 * string.
+	 */
+	int (*get_ssid)(void *priv, u8 *ssid);
+
+	/**
+	 * set_key - Configure encryption key
+	 * @priv: private driver interface data
+	 * @params: Key parameters
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * Configure the given key for the kernel driver. If the driver
+	 * supports separate individual keys (4 default keys + 1 individual),
+	 * addr can be used to determine whether the key is default or
+	 * individual. If only 4 keys are supported, the default key with key
+	 * index 0 is used as the individual key. STA must be configured to use
+	 * it as the default Tx key (set_tx is set) and accept Rx for all the
+	 * key indexes. In most cases, WPA uses only key indexes 1 and 2 for
+	 * broadcast keys, so key index 0 is available for this kind of
+	 * configuration.
+	 *
+	 * Please note that TKIP keys include separate TX and RX MIC keys and
+	 * some drivers may expect them in different order than wpa_supplicant
+	 * is using. If the TX/RX keys are swapped, all TKIP encrypted packets
+	 * will trigger Michael MIC errors. This can be fixed by changing the
+	 * order of MIC keys by swapping the bytes 16..23 and 24..31 of the key
+	 * in driver_*.c set_key() implementation, see driver_ndis.c for an
+	 * example on how this can be done.
+	 */
+	int (*set_key)(void *priv, struct wpa_driver_set_key_params *params);
+
+	/**
+	 * init - Initialize driver interface
+	 * @ctx: context to be used when calling wpa_supplicant functions,
+	 * e.g., wpa_supplicant_event()
+	 * @ifname: interface name, e.g., wlan0
+	 *
+	 * Returns: Pointer to private data, %NULL on failure
+	 *
+	 * Initialize driver interface, including event processing for kernel
+	 * driver events (e.g., associated, scan results, Michael MIC failure).
+	 * This function can allocate a private configuration data area for
+	 * @ctx, file descriptor, interface name, etc. information that may be
+	 * needed in future driver operations. If this is not used, non-NULL
+	 * value will need to be returned because %NULL is used to indicate
+	 * failure. The returned value will be used as 'void *priv' data for
+	 * all other driver_ops functions.
+	 *
+	 * The main event loop (eloop.c) of wpa_supplicant can be used to
+	 * register callback for read sockets (eloop_register_read_sock()).
+	 *
+	 * See below for more information about events and
+	 * wpa_supplicant_event() function.
+	 */
+	void * (*init)(void *ctx, const char *ifname);
+
+	/**
+	 * deinit - Deinitialize driver interface
+	 * @priv: private driver interface data from init()
+	 *
+	 * Shut down driver interface and processing of driver events. Free
+	 * private data buffer if one was allocated in init() handler.
+	 */
+	void (*deinit)(void *priv);
+
+	/**
+	 * set_param - Set driver configuration parameters
+	 * @priv: private driver interface data from init()
+	 * @param: driver specific configuration parameters
+	 *
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * Optional handler for notifying driver interface about configuration
+	 * parameters (driver_param).
+	 */
+	int (*set_param)(void *priv, const char *param);
+
+	/**
+	 * set_countermeasures - Enable/disable TKIP countermeasures
+	 * @priv: private driver interface data
+	 * @enabled: 1 = countermeasures enabled, 0 = disabled
+	 *
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * Configure TKIP countermeasures. When these are enabled, the driver
+	 * should drop all received and queued frames that are using TKIP.
+	 */
+	int (*set_countermeasures)(void *priv, int enabled);
+
+	/**
+	 * deauthenticate - Request driver to deauthenticate
+	 * @priv: private driver interface data
+	 * @addr: peer address (BSSID of the AP)
+	 * @reason_code: 16-bit reason code to be sent in the deauthentication
+	 *	frame
+	 *
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*deauthenticate)(void *priv, const u8 *addr, u16 reason_code);
+
+	/**
+	 * associate - Request driver to associate
+	 * @priv: private driver interface data
+	 * @params: association parameters
+	 *
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*associate)(void *priv,
+			 struct wpa_driver_associate_params *params);
+
+	/**
+	 * add_pmkid - Add PMKSA cache entry to the driver
+	 * @priv: private driver interface data
+	 * @params: PMKSA parameters
+	 *
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function is called when a new PMK is received, as a result of
+	 * either normal authentication or RSN pre-authentication. The PMKSA
+	 * parameters are either a set of bssid, pmkid, and pmk; or a set of
+	 * ssid, fils_cache_id, pmkid, and pmk.
+	 *
+	 * If the driver generates RSN IE, i.e., it does not use wpa_ie in
+	 * associate(), add_pmkid() can be used to add new PMKSA cache entries
+	 * in the driver. If the driver uses wpa_ie from wpa_supplicant, this
+	 * driver_ops function does not need to be implemented. Likewise, if
+	 * the driver does not support WPA, this function is not needed.
+	 */
+	int (*add_pmkid)(void *priv, struct wpa_pmkid_params *params);
+
+	/**
+	 * remove_pmkid - Remove PMKSA cache entry to the driver
+	 * @priv: private driver interface data
+	 * @params: PMKSA parameters
+	 *
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function is called when the supplicant drops a PMKSA cache
+	 * entry for any reason. The PMKSA parameters are either a set of
+	 * bssid and pmkid; or a set of ssid, fils_cache_id, and pmkid.
+	 *
+	 * If the driver generates RSN IE, i.e., it does not use wpa_ie in
+	 * associate(), remove_pmkid() can be used to synchronize PMKSA caches
+	 * between the driver and wpa_supplicant. If the driver uses wpa_ie
+	 * from wpa_supplicant, this driver_ops function does not need to be
+	 * implemented. Likewise, if the driver does not support WPA, this
+	 * function is not needed.
+	 */
+	int (*remove_pmkid)(void *priv, struct wpa_pmkid_params *params);
+
+	/**
+	 * flush_pmkid - Flush PMKSA cache
+	 * @priv: private driver interface data
+	 *
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function is called when the supplicant drops all PMKSA cache
+	 * entries for any reason.
+	 *
+	 * If the driver generates RSN IE, i.e., it does not use wpa_ie in
+	 * associate(), remove_pmkid() can be used to synchronize PMKSA caches
+	 * between the driver and wpa_supplicant. If the driver uses wpa_ie
+	 * from wpa_supplicant, this driver_ops function does not need to be
+	 * implemented. Likewise, if the driver does not support WPA, this
+	 * function is not needed.
+	 */
+	int (*flush_pmkid)(void *priv);
+
+	/**
+	 * get_capa - Get driver capabilities
+	 * @priv: private driver interface data
+	 *
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * Get driver/firmware/hardware capabilities.
+	 */
+	int (*get_capa)(void *priv, struct wpa_driver_capa *capa);
+
+	/**
+	 * poll - Poll driver for association information
+	 * @priv: private driver interface data
+	 *
+	 * This is an optional callback that can be used when the driver does
+	 * not provide event mechanism for association events. This is called
+	 * when receiving WPA/RSN EAPOL-Key messages that require association
+	 * information. The driver interface is supposed to generate associnfo
+	 * event before returning from this callback function. In addition, the
+	 * driver interface should generate an association event after having
+	 * sent out associnfo.
+	 */
+	void (*poll)(void *priv);
+
+	/**
+	 * get_ifindex - Get interface index
+	 * @priv: private driver interface data
+	 *
+	 * Returns: Interface index
+	 */
+	unsigned int (*get_ifindex)(void *priv);
+
+	/**
+	 * get_ifname - Get interface name
+	 * @priv: private driver interface data
+	 *
+	 * Returns: Pointer to the interface name. This can differ from the
+	 * interface name used in init() call. Init() is called first.
+	 *
+	 * This optional function can be used to allow the driver interface to
+	 * replace the interface name with something else, e.g., based on an
+	 * interface mapping from a more descriptive name.
+	 */
+	const char * (*get_ifname)(void *priv);
+
+	/**
+	 * get_mac_addr - Get own MAC address
+	 * @priv: private driver interface data
+	 *
+	 * Returns: Pointer to own MAC address or %NULL on failure
+	 *
+	 * This optional function can be used to get the own MAC address of the
+	 * device from the driver interface code. This is only needed if the
+	 * l2_packet implementation for the OS does not provide easy access to
+	 * a MAC address. */
+	const u8 * (*get_mac_addr)(void *priv);
+
+	/**
+	 * set_operstate - Sets device operating state to DORMANT or UP
+	 * @priv: private driver interface data
+	 * @state: 0 = dormant, 1 = up
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This is an optional function that can be used on operating systems
+	 * that support a concept of controlling network device state from user
+	 * space applications. This function, if set, gets called with
+	 * state = 1 when authentication has been completed and with state = 0
+	 * when connection is lost.
+	 */
+	int (*set_operstate)(void *priv, int state);
+
+	/**
+	 * mlme_setprotection - MLME-SETPROTECTION.request primitive
+	 * @priv: Private driver interface data
+	 * @addr: Address of the station for which to set protection (may be
+	 * %NULL for group keys)
+	 * @protect_type: MLME_SETPROTECTION_PROTECT_TYPE_*
+	 * @key_type: MLME_SETPROTECTION_KEY_TYPE_*
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This is an optional function that can be used to set the driver to
+	 * require protection for Tx and/or Rx frames. This uses the layer
+	 * interface defined in IEEE 802.11i-2004 clause 10.3.22.1
+	 * (MLME-SETPROTECTION.request). Many drivers do not use explicit
+	 * set protection operation; instead, they set protection implicitly
+	 * based on configured keys.
+	 */
+	int (*mlme_setprotection)(void *priv, const u8 *addr, int protect_type,
+				  int key_type);
+
+	/**
+	 * get_hw_feature_data - Get hardware support data (channels and rates)
+	 * @priv: Private driver interface data
+	 * @num_modes: Variable for returning the number of returned modes
+	 * flags: Variable for returning hardware feature flags
+	 * @dfs: Variable for returning DFS region (HOSTAPD_DFS_REGION_*)
+	 * Returns: Pointer to allocated hardware data on success or %NULL on
+	 * failure. Caller is responsible for freeing this.
+	 */
+	struct hostapd_hw_modes * (*get_hw_feature_data)(void *priv,
+							 u16 *num_modes,
+							 u16 *flags, u8 *dfs);
+
+	/**
+	 * send_mlme - Send management frame from MLME
+	 * @priv: Private driver interface data
+	 * @data: IEEE 802.11 management frame with IEEE 802.11 header
+	 * @data_len: Size of the management frame
+	 * @noack: Do not wait for this frame to be acked (disable retries)
+	 * @freq: Frequency (in MHz) to send the frame on, or 0 to let the
+	 * driver decide
+	 * @csa_offs: Array of CSA offsets or %NULL
+	 * @csa_offs_len: Number of elements in csa_offs
+	 * @no_encrypt: Do not encrypt frame even if appropriate key exists
+	 *	(used only for testing purposes)
+	 * @wait: Time to wait off-channel for a response (in ms), or zero
+	 * @link_id: Link ID to use for TX, or -1 if not set
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*send_mlme)(void *priv, const u8 *data, size_t data_len,
+			 int noack, unsigned int freq, const u16 *csa_offs,
+			 size_t csa_offs_len, int no_encrypt,
+			 unsigned int wait, int link_id);
+
+	/**
+	 * update_ft_ies - Update FT (IEEE 802.11r) IEs
+	 * @priv: Private driver interface data
+	 * @md: Mobility domain (2 octets) (also included inside ies)
+	 * @ies: FT IEs (MDIE, FTIE, ...) or %NULL to remove IEs
+	 * @ies_len: Length of FT IEs in bytes
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * The supplicant uses this callback to let the driver know that keying
+	 * material for FT is available and that the driver can use the
+	 * provided IEs in the next message in FT authentication sequence.
+	 *
+	 * This function is only needed for driver that support IEEE 802.11r
+	 * (Fast BSS Transition).
+	 */
+	int (*update_ft_ies)(void *priv, const u8 *md, const u8 *ies,
+			     size_t ies_len);
+
+	/**
+	 * get_scan_results2 - Fetch the latest scan results
+	 * @priv: private driver interface data
+	 *
+	 * Returns: Allocated buffer of scan results (caller is responsible for
+	 * freeing the data structure) on success, NULL on failure
+	 */
+	 struct wpa_scan_results * (*get_scan_results2)(void *priv);
+
+	/**
+	 * set_country - Set country
+	 * @priv: Private driver interface data
+	 * @alpha2: country to which to switch to
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function is for drivers which support some form
+	 * of setting a regulatory domain.
+	 */
+	int (*set_country)(void *priv, const char *alpha2);
+
+	/**
+	 * get_country - Get country
+	 * @priv: Private driver interface data
+	 * @alpha2: Buffer for returning country code (at least 3 octets)
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*get_country)(void *priv, char *alpha2);
+
+	/**
+	 * global_init - Global driver initialization
+	 * @ctx: wpa_global pointer
+	 * Returns: Pointer to private data (global), %NULL on failure
+	 *
+	 * This optional function is called to initialize the driver wrapper
+	 * for global data, i.e., data that applies to all interfaces. If this
+	 * function is implemented, global_deinit() will also need to be
+	 * implemented to free the private data. The driver will also likely
+	 * use init2() function instead of init() to get the pointer to global
+	 * data available to per-interface initializer.
+	 */
+	void * (*global_init)(void *ctx);
+
+	/**
+	 * global_deinit - Global driver deinitialization
+	 * @priv: private driver global data from global_init()
+	 *
+	 * Terminate any global driver related functionality and free the
+	 * global data structure.
+	 */
+	void (*global_deinit)(void *priv);
+
+	/**
+	 * init2 - Initialize driver interface (with global data)
+	 * @ctx: context to be used when calling wpa_supplicant functions,
+	 * e.g., wpa_supplicant_event()
+	 * @ifname: interface name, e.g., wlan0
+	 * @global_priv: private driver global data from global_init()
+	 * Returns: Pointer to private data, %NULL on failure
+	 *
+	 * This function can be used instead of init() if the driver wrapper
+	 * uses global data.
+	 */
+	void * (*init2)(void *ctx, const char *ifname, void *global_priv);
+
+	/**
+	 * get_interfaces - Get information about available interfaces
+	 * @global_priv: private driver global data from global_init()
+	 * Returns: Allocated buffer of interface information (caller is
+	 * responsible for freeing the data structure) on success, NULL on
+	 * failure
+	 */
+	struct wpa_interface_info * (*get_interfaces)(void *global_priv);
+
+	/**
+	 * scan2 - Request the driver to initiate scan
+	 * @priv: private driver interface data
+	 * @params: Scan parameters
+	 *
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * Once the scan results are ready, the driver should report scan
+	 * results event for wpa_supplicant which will eventually request the
+	 * results with wpa_driver_get_scan_results2().
+	 */
+	int (*scan2)(void *priv, struct wpa_driver_scan_params *params);
+
+	/**
+	 * authenticate - Request driver to authenticate
+	 * @priv: private driver interface data
+	 * @params: authentication parameters
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This is an optional function that can be used with drivers that
+	 * support separate authentication and association steps, i.e., when
+	 * wpa_supplicant can act as the SME. If not implemented, associate()
+	 * function is expected to take care of IEEE 802.11 authentication,
+	 * too.
+	 */
+	int (*authenticate)(void *priv,
+			    struct wpa_driver_auth_params *params);
+
+	/**
+	 * set_ap - Set Beacon and Probe Response information for AP mode
+	 * @priv: Private driver interface data
+	 * @params: Parameters to use in AP mode
+	 *
+	 * This function is used to configure Beacon template and/or extra IEs
+	 * to add for Beacon and Probe Response frames for the driver in
+	 * AP mode. The driver is responsible for building the full Beacon
+	 * frame by concatenating the head part with TIM IE generated by the
+	 * driver/firmware and finishing with the tail part. Depending on the
+	 * driver architectue, this can be done either by using the full
+	 * template or the set of additional IEs (e.g., WPS and P2P IE).
+	 * Similarly, Probe Response processing depends on the driver design.
+	 * If the driver (or firmware) takes care of replying to Probe Request
+	 * frames, the extra IEs provided here needs to be added to the Probe
+	 * Response frames.
+	 *
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*set_ap)(void *priv, struct wpa_driver_ap_params *params);
+
+	/**
+	 * set_acl - Set ACL in AP mode
+	 * @priv: Private driver interface data
+	 * @params: Parameters to configure ACL
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This is used only for the drivers which support MAC address ACL.
+	 */
+	int (*set_acl)(void *priv, struct hostapd_acl_params *params);
+
+	/**
+	 * hapd_init - Initialize driver interface (hostapd only)
+	 * @hapd: Pointer to hostapd context
+	 * @params: Configuration for the driver wrapper
+	 * Returns: Pointer to private data, %NULL on failure
+	 *
+	 * This function is used instead of init() or init2() when the driver
+	 * wrapper is used with hostapd.
+	 */
+	void * (*hapd_init)(struct hostapd_data *hapd,
+			    struct wpa_init_params *params);
+
+	/**
+	 * hapd_deinit - Deinitialize driver interface (hostapd only)
+	 * @priv: Private driver interface data from hapd_init()
+	 */
+	void (*hapd_deinit)(void *priv);
+
+	/**
+	 * set_ieee8021x - Enable/disable IEEE 802.1X support (AP only)
+	 * @priv: Private driver interface data
+	 * @params: BSS parameters
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This is an optional function to configure the kernel driver to
+	 * enable/disable IEEE 802.1X support and set WPA/WPA2 parameters. This
+	 * can be left undefined (set to %NULL) if IEEE 802.1X support is
+	 * always enabled and the driver uses set_ap() to set WPA/RSN IE
+	 * for Beacon frames.
+	 *
+	 * DEPRECATED - use set_ap() instead
+	 */
+	int (*set_ieee8021x)(void *priv, struct wpa_bss_params *params);
+
+	/**
+	 * set_privacy - Enable/disable privacy (AP only)
+	 * @priv: Private driver interface data
+	 * @enabled: 1 = privacy enabled, 0 = disabled
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This is an optional function to configure privacy field in the
+	 * kernel driver for Beacon frames. This can be left undefined (set to
+	 * %NULL) if the driver uses the Beacon template from set_ap().
+	 *
+	 * DEPRECATED - use set_ap() instead
+	 */
+	int (*set_privacy)(void *priv, int enabled);
+
+	/**
+	 * get_seqnum - Fetch the current TSC/packet number (AP only)
+	 * @ifname: The interface name (main or virtual)
+	 * @priv: Private driver interface data
+	 * @addr: MAC address of the station or %NULL for group keys
+	 * @idx: Key index
+	 * @link_id: Link ID for a group key, or -1 if not set
+	 * @seq: Buffer for returning the latest used TSC/packet number
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function is used to fetch the last used TSC/packet number for
+	 * a TKIP, CCMP, GCMP, or BIP/IGTK key. It is mainly used with group
+	 * keys, so there is no strict requirement on implementing support for
+	 * unicast keys (i.e., addr != %NULL).
+	 */
+	int (*get_seqnum)(const char *ifname, void *priv, const u8 *addr,
+			  int idx, int link_id, u8 *seq);
+
+	/**
+	 * flush - Flush all association stations (AP only)
+	 * @priv: Private driver interface data
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function requests the driver to disassociate all associated
+	 * stations. This function does not need to be implemented if the
+	 * driver does not process association frames internally.
+	 */
+	int (*flush)(void *priv);
+
+	/**
+	 * set_generic_elem - Add IEs into Beacon/Probe Response frames (AP)
+	 * @priv: Private driver interface data
+	 * @elem: Information elements
+	 * @elem_len: Length of the elem buffer in octets
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This is an optional function to add information elements in the
+	 * kernel driver for Beacon and Probe Response frames. This can be left
+	 * undefined (set to %NULL) if the driver uses the Beacon template from
+	 * set_ap().
+	 *
+	 * DEPRECATED - use set_ap() instead
+	 */
+	int (*set_generic_elem)(void *priv, const u8 *elem, size_t elem_len);
+
+	/**
+	 * read_sta_data - Fetch station data
+	 * @priv: Private driver interface data
+	 * @data: Buffer for returning station information
+	 * @addr: MAC address of the station
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*read_sta_data)(void *priv, struct hostap_sta_driver_data *data,
+			     const u8 *addr);
+
+	/**
+	 * tx_control_port - Send a frame over the 802.1X controlled port
+	 * @priv: Private driver interface data
+	 * @dest: Destination MAC address
+	 * @proto: Ethertype in host byte order
+	 * @buf: Frame payload starting from IEEE 802.1X header
+	 * @len: Frame payload length
+	 * @no_encrypt: Do not encrypt frame
+	 * @link_id: Link ID to use for TX, or -1 if not set
+	 *
+	 * Returns 0 on success, else an error
+	 *
+	 * This is like a normal Ethernet send except that the driver is aware
+	 * (by other means than the Ethertype) that this frame is special,
+	 * and more importantly it gains an ordering between the transmission of
+	 * the frame and other driver management operations such as key
+	 * installations. This can be used to work around known limitations in
+	 * IEEE 802.11 protocols such as race conditions between rekeying 4-way
+	 * handshake message 4/4 and a PTK being overwritten.
+	 *
+	 * This function is only used for a given interface if the driver
+	 * instance reports WPA_DRIVER_FLAGS_CONTROL_PORT capability. Otherwise,
+	 * API users will fall back to sending the frame via a normal socket.
+	 */
+	int (*tx_control_port)(void *priv, const u8 *dest,
+			       u16 proto, const u8 *buf, size_t len,
+			       int no_encrypt, int link_id);
+
+	/**
+	 * hapd_send_eapol - Send an EAPOL packet (AP only)
+	 * @priv: private driver interface data
+	 * @addr: Destination MAC address
+	 * @data: EAPOL packet starting with IEEE 802.1X header
+	 * @data_len: Length of the EAPOL packet in octets
+	 * @encrypt: Whether the frame should be encrypted
+	 * @own_addr: Source MAC address
+	 * @flags: WPA_STA_* flags for the destination station
+	 * @link_id: Link ID to use for TX, or -1 if not set
+	 *
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*hapd_send_eapol)(void *priv, const u8 *addr, const u8 *data,
+			       size_t data_len, int encrypt,
+			       const u8 *own_addr, u32 flags, int link_id);
+
+	/**
+	 * sta_deauth - Deauthenticate a station (AP only)
+	 * @priv: Private driver interface data
+	 * @own_addr: Source address and BSSID for the Deauthentication frame
+	 * @addr: MAC address of the station to deauthenticate
+	 * @reason: Reason code for the Deauthentication frame
+	 * @link_id: Link ID to use for Deauthentication frame, or -1 if not set
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function requests a specific station to be deauthenticated and
+	 * a Deauthentication frame to be sent to it.
+	 */
+	int (*sta_deauth)(void *priv, const u8 *own_addr, const u8 *addr,
+			  u16 reason, int link_id);
+
+	/**
+	 * sta_disassoc - Disassociate a station (AP only)
+	 * @priv: Private driver interface data
+	 * @own_addr: Source address and BSSID for the Disassociation frame
+	 * @addr: MAC address of the station to disassociate
+	 * @reason: Reason code for the Disassociation frame
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function requests a specific station to be disassociated and
+	 * a Disassociation frame to be sent to it.
+	 */
+	int (*sta_disassoc)(void *priv, const u8 *own_addr, const u8 *addr,
+			    u16 reason);
+
+	/**
+	 * sta_remove - Remove a station entry (AP only)
+	 * @priv: Private driver interface data
+	 * @addr: MAC address of the station to be removed
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*sta_remove)(void *priv, const u8 *addr);
+
+	/**
+	 * hapd_get_ssid - Get the current SSID (AP only)
+	 * @priv: Private driver interface data
+	 * @buf: Buffer for returning the SSID
+	 * @len: Maximum length of the buffer
+	 * Returns: Length of the SSID on success, -1 on failure
+	 *
+	 * This function need not be implemented if the driver uses Beacon
+	 * template from set_ap() and does not reply to Probe Request frames.
+	 */
+	int (*hapd_get_ssid)(void *priv, u8 *buf, int len);
+
+	/**
+	 * hapd_set_ssid - Set SSID (AP only)
+	 * @priv: Private driver interface data
+	 * @buf: SSID
+	 * @len: Length of the SSID in octets
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * DEPRECATED - use set_ap() instead
+	 */
+	int (*hapd_set_ssid)(void *priv, const u8 *buf, int len);
+
+	/**
+	 * hapd_set_countermeasures - Enable/disable TKIP countermeasures (AP)
+	 * @priv: Private driver interface data
+	 * @enabled: 1 = countermeasures enabled, 0 = disabled
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This need not be implemented if the driver does not take care of
+	 * association processing.
+	 */
+	int (*hapd_set_countermeasures)(void *priv, int enabled);
+
+	/**
+	 * sta_add - Add a station entry
+	 * @priv: Private driver interface data
+	 * @params: Station parameters
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function is used to add or set (params->set 1) a station
+	 * entry in the driver. Adding STA entries is used only if the driver
+	 * does not take care of association processing.
+	 *
+	 * With drivers that don't support full AP client state, this function
+	 * is used to add a station entry to the driver once the station has
+	 * completed association.
+	 *
+	 * With TDLS, this function is used to add or set (params->set 1)
+	 * TDLS peer entries (even with drivers that do not support full AP
+	 * client state).
+	 */
+	int (*sta_add)(void *priv, struct hostapd_sta_add_params *params);
+
+	/**
+	 * get_inact_sec - Get station inactivity duration (AP only)
+	 * @priv: Private driver interface data
+	 * @addr: Station address
+	 * Returns: Number of seconds station has been inactive, -1 on failure
+	 */
+	int (*get_inact_sec)(void *priv, const u8 *addr);
+
+	/**
+	 * sta_clear_stats - Clear station statistics (AP only)
+	 * @priv: Private driver interface data
+	 * @addr: Station address
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*sta_clear_stats)(void *priv, const u8 *addr);
+
+	/**
+	 * set_freq - Set channel/frequency (AP only)
+	 * @priv: Private driver interface data
+	 * @freq: Channel parameters
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*set_freq)(void *priv, struct hostapd_freq_params *freq);
+
+	/**
+	 * set_rts - Set RTS threshold
+	 * @priv: Private driver interface data
+	 * @rts: RTS threshold in octets
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*set_rts)(void *priv, int rts);
+
+	/**
+	 * set_frag - Set fragmentation threshold
+	 * @priv: Private driver interface data
+	 * @frag: Fragmentation threshold in octets
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*set_frag)(void *priv, int frag);
+
+	/**
+	 * sta_set_flags - Set station flags (AP only)
+	 * @priv: Private driver interface data
+	 * @addr: Station address
+	 * @total_flags: Bitmap of all WPA_STA_* flags currently set
+	 * @flags_or: Bitmap of WPA_STA_* flags to add
+	 * @flags_and: Bitmap of WPA_STA_* flags to us as a mask
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*sta_set_flags)(void *priv, const u8 *addr,
+			     unsigned int total_flags, unsigned int flags_or,
+			     unsigned int flags_and);
+
+	/**
+	 * sta_set_airtime_weight - Set station airtime weight (AP only)
+	 * @priv: Private driver interface data
+	 * @addr: Station address
+	 * @weight: New weight for station airtime assignment
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*sta_set_airtime_weight)(void *priv, const u8 *addr,
+				      unsigned int weight);
+
+	/**
+	 * set_tx_queue_params - Set TX queue parameters
+	 * @priv: Private driver interface data
+	 * @queue: Queue number (0 = VO, 1 = VI, 2 = BE, 3 = BK)
+	 * @aifs: AIFS
+	 * @cw_min: cwMin
+	 * @cw_max: cwMax
+	 * @burst_time: Maximum length for bursting in 0.1 msec units
+	 * @link_id: Link ID to use, or -1 for non MLD.
+	 */
+	int (*set_tx_queue_params)(void *priv, int queue, int aifs, int cw_min,
+				   int cw_max, int burst_time, int link_id);
+
+	/**
+	 * if_add - Add a virtual interface
+	 * @priv: Private driver interface data
+	 * @type: Interface type
+	 * @ifname: Interface name for the new virtual interface
+	 * @addr: Local address to use for the interface or %NULL to use the
+	 *	parent interface address
+	 * @bss_ctx: BSS context for %WPA_IF_AP_BSS interfaces
+	 * @drv_priv: Pointer for overwriting the driver context or %NULL if
+	 *	not allowed (applies only to %WPA_IF_AP_BSS type)
+	 * @force_ifname: Buffer for returning an interface name that the
+	 *	driver ended up using if it differs from the requested ifname
+	 * @if_addr: Buffer for returning the allocated interface address
+	 *	(this may differ from the requested addr if the driver cannot
+	 *	change interface address)
+	 * @bridge: Bridge interface to use or %NULL if no bridge configured
+	 * @use_existing: Whether to allow existing interface to be used
+	 * @setup_ap: Whether to setup AP for %WPA_IF_AP_BSS interfaces
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*if_add)(void *priv, enum wpa_driver_if_type type,
+		      const char *ifname, const u8 *addr, void *bss_ctx,
+		      void **drv_priv, char *force_ifname, u8 *if_addr,
+		      const char *bridge, int use_existing, int setup_ap);
+
+	/**
+	 * if_remove - Remove a virtual interface
+	 * @priv: Private driver interface data
+	 * @type: Interface type
+	 * @ifname: Interface name of the virtual interface to be removed
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*if_remove)(void *priv, enum wpa_driver_if_type type,
+			 const char *ifname);
+
+	/**
+	 * set_sta_vlan - Bind a station into a specific interface (AP only)
+	 * @priv: Private driver interface data
+	 * @ifname: Interface (main or virtual BSS or VLAN)
+	 * @addr: MAC address of the associated station
+	 * @vlan_id: VLAN ID
+	 * @link_id: The link ID or -1 for non-MLO
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function is used to bind a station to a specific virtual
+	 * interface. It is only used if when virtual interfaces are supported,
+	 * e.g., to assign stations to different VLAN interfaces based on
+	 * information from a RADIUS server. This allows separate broadcast
+	 * domains to be used with a single BSS.
+	 */
+	int (*set_sta_vlan)(void *priv, const u8 *addr, const char *ifname,
+			    int vlan_id, int link_id);
+
+	/**
+	 * commit - Optional commit changes handler (AP only)
+	 * @priv: driver private data
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This optional handler function can be registered if the driver
+	 * interface implementation needs to commit changes (e.g., by setting
+	 * network interface up) at the end of initial configuration. If set,
+	 * this handler will be called after initial setup has been completed.
+	 */
+	int (*commit)(void *priv);
+
+	/**
+	 * set_radius_acl_auth - Notification of RADIUS ACL change
+	 * @priv: Private driver interface data
+	 * @mac: MAC address of the station
+	 * @accepted: Whether the station was accepted
+	 * @session_timeout: Session timeout for the station
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*set_radius_acl_auth)(void *priv, const u8 *mac, int accepted,
+				   u32 session_timeout);
+
+	/**
+	 * set_radius_acl_expire - Notification of RADIUS ACL expiration
+	 * @priv: Private driver interface data
+	 * @mac: MAC address of the station
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*set_radius_acl_expire)(void *priv, const u8 *mac);
+
+	/**
+	 * set_ap_wps_ie - Add WPS IE(s) into Beacon/Probe Response frames (AP)
+	 * @priv: Private driver interface data
+	 * @beacon: WPS IE(s) for Beacon frames or %NULL to remove extra IE(s)
+	 * @proberesp: WPS IE(s) for Probe Response frames or %NULL to remove
+	 *	extra IE(s)
+	 * @assocresp: WPS IE(s) for (Re)Association Response frames or %NULL
+	 *	to remove extra IE(s)
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This is an optional function to add WPS IE in the kernel driver for
+	 * Beacon and Probe Response frames. This can be left undefined (set
+	 * to %NULL) if the driver uses the Beacon template from set_ap()
+	 * and does not process Probe Request frames. If the driver takes care
+	 * of (Re)Association frame processing, the assocresp buffer includes
+	 * WPS IE(s) that need to be added to (Re)Association Response frames
+	 * whenever a (Re)Association Request frame indicated use of WPS.
+	 *
+	 * This will also be used to add P2P IE(s) into Beacon/Probe Response
+	 * frames when operating as a GO. The driver is responsible for adding
+	 * timing related attributes (e.g., NoA) in addition to the IEs
+	 * included here by appending them after these buffers. This call is
+	 * also used to provide Probe Response IEs for P2P Listen state
+	 * operations for drivers that generate the Probe Response frames
+	 * internally.
+	 *
+	 * DEPRECATED - use set_ap() instead
+	 */
+	int (*set_ap_wps_ie)(void *priv, const struct wpabuf *beacon,
+			     const struct wpabuf *proberesp,
+			     const struct wpabuf *assocresp);
+
+	/**
+	 * set_supp_port - Set IEEE 802.1X Supplicant Port status
+	 * @priv: Private driver interface data
+	 * @authorized: Whether the port is authorized
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*set_supp_port)(void *priv, int authorized);
+
+	/**
+	 * set_wds_sta - Bind a station into a 4-address WDS (AP only)
+	 * @priv: Private driver interface data
+	 * @addr: MAC address of the associated station
+	 * @aid: Association ID
+	 * @val: 1 = bind to 4-address WDS; 0 = unbind
+	 * @bridge_ifname: Bridge interface to use for the WDS station or %NULL
+	 *	to indicate that bridge is not to be used
+	 * @ifname_wds: Buffer to return the interface name for the new WDS
+	 *	station or %NULL to indicate name is not returned.
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val,
+			   const char *bridge_ifname, char *ifname_wds);
+
+	/**
+	 * send_action - Transmit an Action frame
+	 * @priv: Private driver interface data
+	 * @freq: Frequency (in MHz) of the channel
+	 * @wait: Time to wait off-channel for a response (in ms), or zero
+	 * @dst: Destination MAC address (Address 1)
+	 * @src: Source MAC address (Address 2)
+	 * @bssid: BSSID (Address 3)
+	 * @data: Frame body
+	 * @data_len: data length in octets
+	 @ @no_cck: Whether CCK rates must not be used to transmit this frame
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This command can be used to request the driver to transmit an action
+	 * frame to the specified destination.
+	 *
+	 * If the %WPA_DRIVER_FLAGS_OFFCHANNEL_TX flag is set, the frame will
+	 * be transmitted on the given channel and the device will wait for a
+	 * response on that channel for the given wait time.
+	 *
+	 * If the flag is not set, the wait time will be ignored. In this case,
+	 * if a remain-on-channel duration is in progress, the frame must be
+	 * transmitted on that channel; alternatively the frame may be sent on
+	 * the current operational channel (if in associated state in station
+	 * mode or while operating as an AP.)
+	 *
+	 * If @src differs from the device MAC address, use of a random
+	 * transmitter address is requested for this message exchange.
+	 */
+	int (*send_action)(void *priv, unsigned int freq, unsigned int wait,
+			   const u8 *dst, const u8 *src, const u8 *bssid,
+			   const u8 *data, size_t data_len, int no_cck);
+
+	/**
+	 * send_action_cancel_wait - Cancel action frame TX wait
+	 * @priv: Private driver interface data
+	 *
+	 * This command cancels the wait time associated with sending an action
+	 * frame. It is only available when %WPA_DRIVER_FLAGS_OFFCHANNEL_TX is
+	 * set in the driver flags.
+	 */
+	void (*send_action_cancel_wait)(void *priv);
+
+	/**
+	 * remain_on_channel - Remain awake on a channel
+	 * @priv: Private driver interface data
+	 * @freq: Frequency (in MHz) of the channel
+	 * @duration: Duration in milliseconds
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This command is used to request the driver to remain awake on the
+	 * specified channel for the specified duration and report received
+	 * Action frames with EVENT_RX_MGMT events. Optionally, received
+	 * Probe Request frames may also be requested to be reported by calling
+	 * probe_req_report(). These will be reported with EVENT_RX_PROBE_REQ.
+	 *
+	 * The driver may not be at the requested channel when this function
+	 * returns, i.e., the return code is only indicating whether the
+	 * request was accepted. The caller will need to wait until the
+	 * EVENT_REMAIN_ON_CHANNEL event indicates that the driver has
+	 * completed the channel change. This may take some time due to other
+	 * need for the radio and the caller should be prepared to timing out
+	 * its wait since there are no guarantees on when this request can be
+	 * executed.
+	 */
+	int (*remain_on_channel)(void *priv, unsigned int freq,
+				 unsigned int duration);
+
+	/**
+	 * cancel_remain_on_channel - Cancel remain-on-channel operation
+	 * @priv: Private driver interface data
+	 *
+	 * This command can be used to cancel a remain-on-channel operation
+	 * before its originally requested duration has passed. This could be
+	 * used, e.g., when remain_on_channel() is used to request extra time
+	 * to receive a response to an Action frame and the response is
+	 * received when there is still unneeded time remaining on the
+	 * remain-on-channel operation.
+	 */
+	int (*cancel_remain_on_channel)(void *priv);
+
+	/**
+	 * probe_req_report - Request Probe Request frames to be indicated
+	 * @priv: Private driver interface data
+	 * @report: Whether to report received Probe Request frames
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 *
+	 * This command can be used to request the driver to indicate when
+	 * Probe Request frames are received with EVENT_RX_PROBE_REQ events.
+	 * Since this operation may require extra resources, e.g., due to less
+	 * optimal hardware/firmware RX filtering, many drivers may disable
+	 * Probe Request reporting at least in station mode. This command is
+	 * used to notify the driver when the Probe Request frames need to be
+	 * reported, e.g., during remain-on-channel operations.
+	 */
+	int (*probe_req_report)(void *priv, int report);
+
+	/**
+	 * deinit_ap - Deinitialize AP mode
+	 * @priv: Private driver interface data
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 *
+	 * This optional function can be used to disable AP mode related
+	 * configuration. If the interface was not dynamically added,
+	 * change the driver mode to station mode to allow normal station
+	 * operations like scanning to be completed.
+	 */
+	int (*deinit_ap)(void *priv);
+
+	/**
+	 * deinit_p2p_cli - Deinitialize P2P client mode
+	 * @priv: Private driver interface data
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 *
+	 * This optional function can be used to disable P2P client mode. If the
+	 * interface was not dynamically added, change the interface type back
+	 * to station mode.
+	 */
+	int (*deinit_p2p_cli)(void *priv);
+
+	/**
+	 * suspend - Notification on system suspend/hibernate event
+	 * @priv: Private driver interface data
+	 */
+	void (*suspend)(void *priv);
+
+	/**
+	 * resume - Notification on system resume/thaw event
+	 * @priv: Private driver interface data
+	 */
+	void (*resume)(void *priv);
+
+	/**
+	 * signal_monitor - Set signal monitoring parameters
+	 * @priv: Private driver interface data
+	 * @threshold: Threshold value for signal change events; 0 = disabled
+	 * @hysteresis: Minimum change in signal strength before indicating a
+	 *	new event
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 *
+	 * This function can be used to configure monitoring of signal strength
+	 * with the current AP. Whenever signal strength drops below the
+	 * %threshold value or increases above it, EVENT_SIGNAL_CHANGE event
+	 * should be generated assuming the signal strength has changed at
+	 * least %hysteresis from the previously indicated signal change event.
+	 */
+	int (*signal_monitor)(void *priv, int threshold, int hysteresis);
+
+	/**
+	 * get_noa - Get current Notice of Absence attribute payload
+	 * @priv: Private driver interface data
+	 * @buf: Buffer for returning NoA
+	 * @buf_len: Buffer length in octets
+	 * Returns: Number of octets used in buf, 0 to indicate no NoA is being
+	 * advertized, or -1 on failure
+	 *
+	 * This function is used to fetch the current Notice of Absence
+	 * attribute value from GO.
+	 */
+	int (*get_noa)(void *priv, u8 *buf, size_t buf_len);
+
+	/**
+	 * set_noa - Set Notice of Absence parameters for GO (testing)
+	 * @priv: Private driver interface data
+	 * @count: Count
+	 * @start: Start time in ms from next TBTT
+	 * @duration: Duration in ms
+	 * Returns: 0 on success or -1 on failure
+	 *
+	 * This function is used to set Notice of Absence parameters for GO. It
+	 * is used only for testing. To disable NoA, all parameters are set to
+	 * 0.
+	 */
+	int (*set_noa)(void *priv, u8 count, int start, int duration);
+
+	/**
+	 * set_p2p_powersave - Set P2P power save options
+	 * @priv: Private driver interface data
+	 * @legacy_ps: 0 = disable, 1 = enable, 2 = maximum PS, -1 = no change
+	 * @opp_ps: 0 = disable, 1 = enable, -1 = no change
+	 * @ctwindow: 0.. = change (msec), -1 = no change
+	 * Returns: 0 on success or -1 on failure
+	 */
+	int (*set_p2p_powersave)(void *priv, int legacy_ps, int opp_ps,
+				 int ctwindow);
+
+	/**
+	 * ampdu - Enable/disable aggregation
+	 * @priv: Private driver interface data
+	 * @ampdu: 1/0 = enable/disable A-MPDU aggregation
+	 * Returns: 0 on success or -1 on failure
+	 */
+	int (*ampdu)(void *priv, int ampdu);
+
+	/**
+	 * get_radio_name - Get physical radio name for the device
+	 * @priv: Private driver interface data
+	 * Returns: Radio name or %NULL if not known
+	 *
+	 * The returned data must not be modified by the caller. It is assumed
+	 * that any interface that has the same radio name as another is
+	 * sharing the same physical radio. This information can be used to
+	 * share scan results etc. information between the virtual interfaces
+	 * to speed up various operations.
+	 */
+	const char * (*get_radio_name)(void *priv);
+
+	/**
+	 * send_tdls_mgmt - for sending TDLS management packets
+	 * @priv: private driver interface data
+	 * @dst: Destination (peer) MAC address
+	 * @action_code: TDLS action code for the mssage
+	 * @dialog_token: Dialog Token to use in the message (if needed)
+	 * @status_code: Status Code or Reason Code to use (if needed)
+	 * @peer_capab: TDLS peer capability (TDLS_PEER_* bitfield)
+	 * @initiator: Is the current end the TDLS link initiator
+	 * @buf: TDLS IEs to add to the message
+	 * @len: Length of buf in octets
+	 * Returns: 0 on success, negative (<0) on failure
+	 *
+	 * This optional function can be used to send packet to driver which is
+	 * responsible for receiving and sending all TDLS packets.
+	 */
+	int (*send_tdls_mgmt)(void *priv, const u8 *dst, u8 action_code,
+			      u8 dialog_token, u16 status_code, u32 peer_capab,
+			      int initiator, const u8 *buf, size_t len);
+
+	/**
+	 * tdls_oper - Ask the driver to perform high-level TDLS operations
+	 * @priv: Private driver interface data
+	 * @oper: TDLS high-level operation. See %enum tdls_oper
+	 * @peer: Destination (peer) MAC address
+	 * Returns: 0 on success, negative (<0) on failure
+	 *
+	 * This optional function can be used to send high-level TDLS commands
+	 * to the driver.
+	 */
+	int (*tdls_oper)(void *priv, enum tdls_oper oper, const u8 *peer);
+
+	/**
+	 * wnm_oper - Notify driver of the WNM frame reception
+	 * @priv: Private driver interface data
+	 * @oper: WNM operation. See %enum wnm_oper
+	 * @peer: Destination (peer) MAC address
+	 * @buf: Buffer for the driver to fill in (for getting IE)
+	 * @buf_len: Return the len of buf
+	 * Returns: 0 on success, negative (<0) on failure
+	 */
+	int (*wnm_oper)(void *priv, enum wnm_oper oper, const u8 *peer,
+			u8 *buf, u16 *buf_len);
+
+	/**
+	 * set_qos_map - Set QoS Map
+	 * @priv: Private driver interface data
+	 * @qos_map_set: QoS Map
+	 * @qos_map_set_len: Length of QoS Map
+	 */
+	int (*set_qos_map)(void *priv, const u8 *qos_map_set,
+			   u8 qos_map_set_len);
+
+	/**
+	 * br_add_ip_neigh - Add a neigh to the bridge ip neigh table
+	 * @priv: Private driver interface data
+	 * @version: IP version of the IP address, 4 or 6
+	 * @ipaddr: IP address for the neigh entry
+	 * @prefixlen: IP address prefix length
+	 * @addr: Corresponding MAC address
+	 * Returns: 0 on success, negative (<0) on failure
+	 */
+	int (*br_add_ip_neigh)(void *priv, u8 version, const u8 *ipaddr,
+			       int prefixlen, const u8 *addr);
+
+	/**
+	 * br_delete_ip_neigh - Remove a neigh from the bridge ip neigh table
+	 * @priv: Private driver interface data
+	 * @version: IP version of the IP address, 4 or 6
+	 * @ipaddr: IP address for the neigh entry
+	 * Returns: 0 on success, negative (<0) on failure
+	 */
+	int (*br_delete_ip_neigh)(void *priv, u8 version, const u8 *ipaddr);
+
+	/**
+	 * br_port_set_attr - Set a bridge port attribute
+	 * @attr: Bridge port attribute to set
+	 * @val: Value to be set
+	 * Returns: 0 on success, negative (<0) on failure
+	 */
+	int (*br_port_set_attr)(void *priv, enum drv_br_port_attr attr,
+				unsigned int val);
+
+	/**
+	 * br_port_set_attr - Set a bridge network parameter
+	 * @param: Bridge parameter to set
+	 * @val: Value to be set
+	 * Returns: 0 on success, negative (<0) on failure
+	 */
+	int (*br_set_net_param)(void *priv, enum drv_br_net_param param,
+				unsigned int val);
+
+	/**
+	 * get_wowlan - Get wake-on-wireless status
+	 * @priv: Private driver interface data
+	 */
+	int (*get_wowlan)(void *priv);
+
+	/**
+	 * set_wowlan - Set wake-on-wireless triggers
+	 * @priv: Private driver interface data
+	 * @triggers: wowlan triggers
+	 */
+	int (*set_wowlan)(void *priv, const struct wowlan_triggers *triggers);
+
+	/**
+	 * signal_poll - Get current connection information
+	 * @priv: Private driver interface data
+	 * @signal_info: Connection info structure
+	 */
+	int (*signal_poll)(void *priv, struct wpa_signal_info *signal_info);
+
+	/**
+	 * mlo_signal_poll - Get current MLO connection information
+	 * @priv: Private driver interface data
+	 * @mlo_signal_info: MLO connection info structure
+	 */
+	int (*mlo_signal_poll)(void *priv,
+			       struct wpa_mlo_signal_info *mlo_signal_info);
+
+	/**
+	 * channel_info - Get parameters of the current operating channel
+	 * @priv: Private driver interface data
+	 * @channel_info: Channel info structure
+	 * Returns: 0 on success, negative (<0) on failure
+	 */
+	int (*channel_info)(void *priv, struct wpa_channel_info *channel_info);
+
+	/**
+	 * set_authmode - Set authentication algorithm(s) for static WEP
+	 * @priv: Private driver interface data
+	 * @authmode: 1=Open System, 2=Shared Key, 3=both
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function can be used to set authentication algorithms for AP
+	 * mode when static WEP is used. If the driver uses user space MLME/SME
+	 * implementation, there is no need to implement this function.
+	 *
+	 * DEPRECATED - use set_ap() instead
+	 */
+	int (*set_authmode)(void *priv, int authmode);
+
+#ifdef ANDROID
+	/**
+	 * driver_cmd - Execute driver-specific command
+	 * @priv: Private driver interface data
+	 * @cmd: Command to execute
+	 * @buf: Return buffer
+	 * @buf_len: Buffer length
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*driver_cmd)(void *priv, char *cmd, char *buf, size_t buf_len);
+#endif /* ANDROID */
+
+	/**
+	 * vendor_cmd - Execute vendor specific command
+	 * @priv: Private driver interface data
+	 * @vendor_id: Vendor id
+	 * @subcmd: Vendor command id
+	 * @nested_attr_flag: Specifies if vendor subcommand uses nested
+	 *	attributes or not
+	 * @data: Vendor command parameters (%NULL if no parameters)
+	 * @data_len: Data length
+	 * @buf: Return buffer (%NULL to ignore reply)
+	 * Returns: 0 on success, negative (<0) on failure
+	 *
+	 * This function handles vendor specific commands that are passed to
+	 * the driver/device. The command is identified by vendor id and
+	 * command id. The nested_attr_flag specifies whether the subcommand
+	 * uses nested attributes or not. Parameters can be passed
+	 * as argument to the command in the data buffer. Reply (if any) will be
+	 * filled in the supplied return buffer.
+	 *
+	 * The exact driver behavior is driver interface and vendor specific. As
+	 * an example, this will be converted to a vendor specific cfg80211
+	 * command in case of the nl80211 driver interface.
+	 */
+	int (*vendor_cmd)(void *priv, unsigned int vendor_id,
+			  unsigned int subcmd, const u8 *data, size_t data_len,
+			  enum nested_attr nested_attr_flag,
+			  struct wpabuf *buf);
+
+	/**
+	 * set_rekey_info - Set rekey information
+	 * @priv: Private driver interface data
+	 * @kek: Current KEK
+	 * @kek_len: KEK length in octets
+	 * @kck: Current KCK
+	 * @kck_len: KCK length in octets
+	 * @replay_ctr: Current EAPOL-Key Replay Counter
+	 *
+	 * This optional function can be used to provide information for the
+	 * driver/firmware to process EAPOL-Key frames in Group Key Handshake
+	 * while the host (including wpa_supplicant) is sleeping.
+	 */
+	void (*set_rekey_info)(void *priv, const u8 *kek, size_t kek_len,
+			       const u8 *kck, size_t kck_len,
+			       const u8 *replay_ctr);
+
+	/**
+	 * sta_assoc - Station association indication
+	 * @priv: Private driver interface data
+	 * @own_addr: Source address and BSSID for association frame
+	 * @addr: MAC address of the station to associate
+	 * @reassoc: flag to indicate re-association
+	 * @status: association response status code
+	 * @ie: assoc response ie buffer
+	 * @len: ie buffer length
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function indicates the driver to send (Re)Association
+	 * Response frame to the station.
+	 */
+	 int (*sta_assoc)(void *priv, const u8 *own_addr, const u8 *addr,
+			  int reassoc, u16 status, const u8 *ie, size_t len);
+
+	/**
+	 * sta_auth - Station authentication indication
+	 * @priv: private driver interface data
+	 * @params: Station authentication parameters
+	 *
+	 * Returns: 0 on success, -1 on failure
+	 */
+	 int (*sta_auth)(void *priv,
+			 struct wpa_driver_sta_auth_params *params);
+
+	/**
+	 * add_tspec - Add traffic stream
+	 * @priv: Private driver interface data
+	 * @addr: MAC address of the station to associate
+	 * @tspec_ie: tspec ie buffer
+	 * @tspec_ielen: tspec ie length
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function adds the traffic steam for the station
+	 * and fills the medium_time in tspec_ie.
+	 */
+	 int (*add_tspec)(void *priv, const u8 *addr, u8 *tspec_ie,
+			  size_t tspec_ielen);
+
+	/**
+	 * add_sta_node - Add a station node in the driver
+	 * @priv: Private driver interface data
+	 * @addr: MAC address of the station to add
+	 * @auth_alg: authentication algorithm used by the station
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function adds the station node in the driver, when
+	 * the station gets added by FT-over-DS.
+	 */
+	int (*add_sta_node)(void *priv, const u8 *addr, u16 auth_alg);
+
+	/**
+	 * sched_scan - Request the driver to initiate scheduled scan
+	 * @priv: Private driver interface data
+	 * @params: Scan parameters
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This operation should be used for scheduled scan offload to
+	 * the hardware. Every time scan results are available, the
+	 * driver should report scan results event for wpa_supplicant
+	 * which will eventually request the results with
+	 * wpa_driver_get_scan_results2(). This operation is optional
+	 * and if not provided or if it returns -1, we fall back to
+	 * normal host-scheduled scans.
+	 */
+	int (*sched_scan)(void *priv, struct wpa_driver_scan_params *params);
+
+	/**
+	 * stop_sched_scan - Request the driver to stop a scheduled scan
+	 * @priv: Private driver interface data
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This should cause the scheduled scan to be stopped and
+	 * results should stop being sent. Must be supported if
+	 * sched_scan is supported.
+	 */
+	int (*stop_sched_scan)(void *priv);
+
+	/**
+	 * poll_client - Probe (null data or such) the given station
+	 * @priv: Private driver interface data
+	 * @own_addr: MAC address of sending interface
+	 * @addr: MAC address of the station to probe
+	 * @qos: Indicates whether station is QoS station
+	 *
+	 * This function is used to verify whether an associated station is
+	 * still present. This function does not need to be implemented if the
+	 * driver provides such inactivity polling mechanism.
+	 */
+	void (*poll_client)(void *priv, const u8 *own_addr,
+			    const u8 *addr, int qos);
+
+	/**
+	 * radio_disable - Disable/enable radio
+	 * @priv: Private driver interface data
+	 * @disabled: 1=disable 0=enable radio
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This optional command is for testing purposes. It can be used to
+	 * disable the radio on a testbed device to simulate out-of-radio-range
+	 * conditions.
+	 */
+	int (*radio_disable)(void *priv, int disabled);
+
+	/**
+	 * switch_channel - Announce channel switch and migrate the GO to the
+	 * given frequency
+	 * @priv: Private driver interface data
+	 * @settings: Settings for CSA period and new channel
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function is used to move the GO to the legacy STA channel to
+	 * avoid frequency conflict in single channel concurrency.
+	 */
+	int (*switch_channel)(void *priv, struct csa_settings *settings);
+
+	/**
+	 * switch_color - Announce color switch and migrate the BSS to the
+	 * given color
+	 * @priv: Private driver interface data
+	 * @settings: Settings for CCA period and new color
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function is used to move the BSS to its new color.
+	 */
+	int (*switch_color)(void *priv, struct cca_settings *settings);
+
+	/**
+	 * add_tx_ts - Add traffic stream
+	 * @priv: Private driver interface data
+	 * @tsid: Traffic stream ID
+	 * @addr: Receiver address
+	 * @user_prio: User priority of the traffic stream
+	 * @admitted_time: Admitted time for this TS in units of
+	 *	32 microsecond periods (per second).
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*add_tx_ts)(void *priv, u8 tsid, const u8 *addr, u8 user_prio,
+			 u16 admitted_time);
+
+	/**
+	 * del_tx_ts - Delete traffic stream
+	 * @priv: Private driver interface data
+	 * @tsid: Traffic stream ID
+	 * @addr: Receiver address
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*del_tx_ts)(void *priv, u8 tsid, const u8 *addr);
+
+	/**
+	 * Enable channel-switching with TDLS peer
+	 * @priv: Private driver interface data
+	 * @addr: MAC address of the TDLS peer
+	 * @oper_class: Operating class of the switch channel
+	 * @params: Channel specification
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * The function indicates to driver that it can start switching to a
+	 * different channel with a specified TDLS peer. The switching is
+	 * assumed on until canceled with tdls_disable_channel_switch().
+	 */
+	int (*tdls_enable_channel_switch)(
+		void *priv, const u8 *addr, u8 oper_class,
+		const struct hostapd_freq_params *params);
+
+	/**
+	 * Disable channel switching with TDLS peer
+	 * @priv: Private driver interface data
+	 * @addr: MAC address of the TDLS peer
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function indicates to the driver that it should stop switching
+	 * with a given TDLS peer.
+	 */
+	int (*tdls_disable_channel_switch)(void *priv, const u8 *addr);
+
+	/**
+	 * start_dfs_cac - Listen for radar interference on the channel
+	 * @priv: Private driver interface data
+	 * @freq: Channel parameters
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*start_dfs_cac)(void *priv, struct hostapd_freq_params *freq);
+
+	/**
+	 * stop_ap - Removes beacon from AP
+	 * @priv: Private driver interface data
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 *
+	 * This optional function can be used to disable AP mode related
+	 * configuration. Unlike deinit_ap, it does not change to station
+	 * mode.
+	 */
+	int (*stop_ap)(void *priv);
+
+	/**
+	 * get_survey - Retrieve survey data
+	 * @priv: Private driver interface data
+	 * @freq: If set, survey data for the specified frequency is only
+	 *	being requested. If not set, all survey data is requested.
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * Use this to retrieve:
+	 *
+	 * - the observed channel noise floor
+	 * - the amount of time we have spent on the channel
+	 * - the amount of time during which we have spent on the channel that
+	 *   the radio has determined the medium is busy and we cannot
+	 *   transmit
+	 * - the amount of time we have spent receiving data
+	 * - the amount of time we have spent transmitting data
+	 *
+	 * This data can be used for spectrum heuristics. One example is
+	 * Automatic Channel Selection (ACS). The channel survey data is
+	 * kept on a linked list on the channel data, one entry is added
+	 * for each survey. The min_nf of the channel is updated for each
+	 * survey.
+	 */
+	int (*get_survey)(void *priv, unsigned int freq);
+
+	/**
+	 * status - Get driver interface status information
+	 * @priv: Private driver interface data
+	 * @buf: Buffer for printing the status information
+	 * @buflen: Maximum length of the buffer
+	 * Returns: Length of written status information or -1 on failure
+	 */
+	int (*status)(void *priv, char *buf, size_t buflen);
+
+	/**
+	 * roaming - Set roaming policy for driver-based BSS selection
+	 * @priv: Private driver interface data
+	 * @allowed: Whether roaming within ESS is allowed
+	 * @bssid: Forced BSSID if roaming is disabled or %NULL if not set
+	 * Returns: Length of written status information or -1 on failure
+	 *
+	 * This optional callback can be used to update roaming policy from the
+	 * associate() command (bssid being set there indicates that the driver
+	 * should not roam before getting this roaming() call to allow roaming.
+	 * If the driver does not indicate WPA_DRIVER_FLAGS_BSS_SELECTION
+	 * capability, roaming policy is handled within wpa_supplicant and there
+	 * is no need to implement or react to this callback.
+	 */
+	int (*roaming)(void *priv, int allowed, const u8 *bssid);
+
+	/**
+	 * disable_fils - Enable/disable FILS feature
+	 * @priv: Private driver interface data
+	 * @disable: 0-enable and 1-disable FILS feature
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This callback can be used to configure driver and below layers to
+	 * enable/disable all FILS features.
+	 */
+	int (*disable_fils)(void *priv, int disable);
+
+	/**
+	 * set_mac_addr - Set MAC address
+	 * @priv: Private driver interface data
+	 * @addr: MAC address to use or %NULL for setting back to permanent
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*set_mac_addr)(void *priv, const u8 *addr);
+
+#ifdef CONFIG_MACSEC
+	int (*macsec_init)(void *priv, struct macsec_init_params *params);
+
+	int (*macsec_deinit)(void *priv);
+
+	/**
+	 * macsec_get_capability - Inform MKA of this driver's capability
+	 * @priv: Private driver interface data
+	 * @cap: Driver's capability
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*macsec_get_capability)(void *priv, enum macsec_cap *cap);
+
+	/**
+	 * enable_protect_frames - Set protect frames status
+	 * @priv: Private driver interface data
+	 * @enabled: true = protect frames enabled
+	 *           false = protect frames disabled
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 */
+	int (*enable_protect_frames)(void *priv, bool enabled);
+
+	/**
+	 * enable_encrypt - Set encryption status
+	 * @priv: Private driver interface data
+	 * @enabled: true = encrypt outgoing traffic
+	 *           false = integrity-only protection on outgoing traffic
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 */
+	int (*enable_encrypt)(void *priv, bool enabled);
+
+	/**
+	 * set_replay_protect - Set replay protect status and window size
+	 * @priv: Private driver interface data
+	 * @enabled: true = replay protect enabled
+	 *           false = replay protect disabled
+	 * @window: replay window size, valid only when replay protect enabled
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 */
+	int (*set_replay_protect)(void *priv, bool enabled, u32 window);
+
+	/**
+	 * set_offload - Set MACsec hardware offload
+	 * @priv: Private driver interface data
+	 * @offload: 0 = MACSEC_OFFLOAD_OFF
+	 *           1 = MACSEC_OFFLOAD_PHY
+	 *           2 = MACSEC_OFFLOAD_MAC
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 */
+	int (*set_offload)(void *priv, u8 offload);
+
+	/**
+	 * set_current_cipher_suite - Set current cipher suite
+	 * @priv: Private driver interface data
+	 * @cs: EUI64 identifier
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 */
+	int (*set_current_cipher_suite)(void *priv, u64 cs);
+
+	/**
+	 * enable_controlled_port - Set controlled port status
+	 * @priv: Private driver interface data
+	 * @enabled: true = controlled port enabled
+	 *           false = controlled port disabled
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 */
+	int (*enable_controlled_port)(void *priv, bool enabled);
+
+	/**
+	 * get_receive_lowest_pn - Get receive lowest pn
+	 * @priv: Private driver interface data
+	 * @sa: secure association
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 */
+	int (*get_receive_lowest_pn)(void *priv, struct receive_sa *sa);
+
+	/**
+	 * get_transmit_next_pn - Get transmit next pn
+	 * @priv: Private driver interface data
+	 * @sa: secure association
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 */
+	int (*get_transmit_next_pn)(void *priv, struct transmit_sa *sa);
+
+	/**
+	 * set_transmit_next_pn - Set transmit next pn
+	 * @priv: Private driver interface data
+	 * @sa: secure association
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 */
+	int (*set_transmit_next_pn)(void *priv, struct transmit_sa *sa);
+
+	/**
+	 * set_receive_lowest_pn - Set receive lowest PN
+	 * @priv: Private driver interface data
+	 * @sa: secure association
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 */
+	int (*set_receive_lowest_pn)(void *priv, struct receive_sa *sa);
+
+	/**
+	 * create_receive_sc - create secure channel for receiving
+	 * @priv: Private driver interface data
+	 * @sc: secure channel
+	 * @conf_offset: confidentiality offset (0, 30, or 50)
+	 * @validation: frame validation policy (0 = Disabled, 1 = Checked,
+	 *	2 = Strict)
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 */
+	int (*create_receive_sc)(void *priv, struct receive_sc *sc,
+				 unsigned int conf_offset,
+				 int validation);
+
+	/**
+	 * delete_receive_sc - delete secure connection for receiving
+	 * @priv: private driver interface data from init()
+	 * @sc: secure channel
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*delete_receive_sc)(void *priv, struct receive_sc *sc);
+
+	/**
+	 * create_receive_sa - create secure association for receive
+	 * @priv: private driver interface data from init()
+	 * @sa: secure association
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*create_receive_sa)(void *priv, struct receive_sa *sa);
+
+	/**
+	 * delete_receive_sa - Delete secure association for receive
+	 * @priv: Private driver interface data from init()
+	 * @sa: Secure association
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*delete_receive_sa)(void *priv, struct receive_sa *sa);
+
+	/**
+	 * enable_receive_sa - enable the SA for receive
+	 * @priv: private driver interface data from init()
+	 * @sa: secure association
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*enable_receive_sa)(void *priv, struct receive_sa *sa);
+
+	/**
+	 * disable_receive_sa - disable SA for receive
+	 * @priv: private driver interface data from init()
+	 * @sa: secure association
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*disable_receive_sa)(void *priv, struct receive_sa *sa);
+
+	/**
+	 * create_transmit_sc - create secure connection for transmit
+	 * @priv: private driver interface data from init()
+	 * @sc: secure channel
+	 * @conf_offset: confidentiality offset (0, 30, or 50)
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*create_transmit_sc)(void *priv, struct transmit_sc *sc,
+				  unsigned int conf_offset);
+
+	/**
+	 * delete_transmit_sc - delete secure connection for transmit
+	 * @priv: private driver interface data from init()
+	 * @sc: secure channel
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*delete_transmit_sc)(void *priv, struct transmit_sc *sc);
+
+	/**
+	 * create_transmit_sa - create secure association for transmit
+	 * @priv: private driver interface data from init()
+	 * @sa: secure association
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*create_transmit_sa)(void *priv, struct transmit_sa *sa);
+
+	/**
+	 * delete_transmit_sa - Delete secure association for transmit
+	 * @priv: Private driver interface data from init()
+	 * @sa: Secure association
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*delete_transmit_sa)(void *priv, struct transmit_sa *sa);
+
+	/**
+	 * enable_transmit_sa - enable SA for transmit
+	 * @priv: private driver interface data from init()
+	 * @sa: secure association
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*enable_transmit_sa)(void *priv, struct transmit_sa *sa);
+
+	/**
+	 * disable_transmit_sa - disable SA for transmit
+	 * @priv: private driver interface data from init()
+	 * @sa: secure association
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*disable_transmit_sa)(void *priv, struct transmit_sa *sa);
+#endif /* CONFIG_MACSEC */
+
+	/**
+	 * init_mesh - Driver specific initialization for mesh
+	 * @priv: Private driver interface data
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*init_mesh)(void *priv);
+
+	/**
+	 * join_mesh - Join a mesh network
+	 * @priv: Private driver interface data
+	 * @params: Mesh configuration parameters
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*join_mesh)(void *priv,
+			 struct wpa_driver_mesh_join_params *params);
+
+	/**
+	 * leave_mesh - Leave a mesh network
+	 * @priv: Private driver interface data
+	 * Returns 0 on success, -1 on failure
+	 */
+	int (*leave_mesh)(void *priv);
+
+	/**
+	 * probe_mesh_link - Inject a frame over direct mesh link to a given
+	 *	peer skipping the next_hop lookup from mpath table.
+	 * @priv: Private driver interface data
+	 * @addr: Peer MAC address
+	 * @eth: Ethernet frame to be sent
+	 * @len: Ethernet frame lengtn in bytes
+	 * Returns 0 on success, -1 on failure
+	 */
+	int (*probe_mesh_link)(void *priv, const u8 *addr, const u8 *eth,
+			       size_t len);
+
+	/**
+	 * do_acs - Automatically select channel
+	 * @priv: Private driver interface data
+	 * @params: Parameters for ACS
+	 * Returns 0 on success, -1 on failure
+	 *
+	 * This command can be used to offload ACS to the driver if the driver
+	 * indicates support for such offloading (WPA_DRIVER_FLAGS_ACS_OFFLOAD).
+	 */
+	int (*do_acs)(void *priv, struct drv_acs_params *params);
+
+	/**
+	 * set_band - Notify driver of band(s) selection
+	 * @priv: Private driver interface data
+	 * @band_mask: The selected band(s) bit mask (from enum set_band)
+	 * Returns 0 on success, -1 on failure
+	 */
+	int (*set_band)(void *priv, u32 band_mask);
+
+	/**
+	 * get_pref_freq_list - Get preferred frequency list for an interface
+	 * @priv: Private driver interface data
+	 * @if_type: Interface type
+	 * @num: Number of channels
+	 * @freq_list: Weighted preferred channel list
+	 * Returns 0 on success, -1 on failure
+	 *
+	 * This command can be used to query the preferred frequency list from
+	 * the driver specific to a particular interface type. The freq_list
+	 * array needs to have room for *num entries. *num will be updated to
+	 * indicate the number of entries fetched from the driver.
+	 */
+	int (*get_pref_freq_list)(void *priv, enum wpa_driver_if_type if_type,
+				  unsigned int *num,
+				  struct weighted_pcl *freq_list);
+
+	/**
+	 * set_prob_oper_freq - Indicate probable P2P operating channel
+	 * @priv: Private driver interface data
+	 * @freq: Channel frequency in MHz
+	 * Returns 0 on success, -1 on failure
+	 *
+	 * This command can be used to inform the driver of the operating
+	 * frequency that an ongoing P2P group formation is likely to come up
+	 * on. Local device is assuming P2P Client role.
+	 */
+	int (*set_prob_oper_freq)(void *priv, unsigned int freq);
+
+	/**
+	 * abort_scan - Request the driver to abort an ongoing scan
+	 * @priv: Private driver interface data
+	 * @scan_cookie: Cookie identifying the scan request. This is used only
+	 *	when the vendor interface QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN
+	 *	was used to trigger scan. Otherwise, 0 is used.
+	 * Returns 0 on success, -1 on failure
+	 */
+	int (*abort_scan)(void *priv, u64 scan_cookie);
+
+	/**
+	 * configure_data_frame_filters - Request to configure frame filters
+	 * @priv: Private driver interface data
+	 * @filter_flags: The type of frames to filter (bitfield of
+	 * WPA_DATA_FRAME_FILTER_FLAG_*)
+	 * Returns: 0 on success or -1 on failure
+	 */
+	int (*configure_data_frame_filters)(void *priv, u32 filter_flags);
+
+	/**
+	 * get_ext_capab - Get extended capabilities for the specified interface
+	 * @priv: Private driver interface data
+	 * @type: Interface type for which to get extended capabilities
+	 * @ext_capab: Extended capabilities fetched
+	 * @ext_capab_mask: Extended capabilities mask
+	 * @ext_capab_len: Length of the extended capabilities
+	 * Returns: 0 on success or -1 on failure
+	 */
+	int (*get_ext_capab)(void *priv, enum wpa_driver_if_type type,
+			     const u8 **ext_capab, const u8 **ext_capab_mask,
+			     unsigned int *ext_capab_len);
+
+	/**
+	 * get_mld_capab - Get MLD capabilities for the specified interface
+	 * @priv: Private driver interface data
+	 * @type: Interface type for which to get MLD capabilities
+	 * @eml_capa: EML capabilities
+	 * @mld_capa_and_ops: MLD Capabilities and Operations
+	 * Returns: 0 on success or -1 on failure
+	 */
+	int (*get_mld_capab)(void *priv, enum wpa_driver_if_type type,
+			     u16 *eml_capa, u16 *mld_capa_and_ops);
+
+	/**
+	 * p2p_lo_start - Start offloading P2P listen to device
+	 * @priv: Private driver interface data
+	 * @freq: Listening frequency (MHz) for P2P listen
+	 * @period: Length of the listen operation in milliseconds
+	 * @interval: Interval for running the listen operation in milliseconds
+	 * @count: Number of times to run the listen operation
+	 * @device_types: Device primary and secondary types
+	 * @dev_types_len: Number of bytes for device_types
+	 * @ies: P2P IE and WSC IE for Probe Response frames
+	 * @ies_len: Length of ies in bytes
+	 * Returns: 0 on success or -1 on failure
+	 */
+	int (*p2p_lo_start)(void *priv, unsigned int freq,
+			    unsigned int period, unsigned int interval,
+			    unsigned int count,
+			    const u8 *device_types, size_t dev_types_len,
+			    const u8 *ies, size_t ies_len);
+
+	/**
+	 * p2p_lo_stop - Stop P2P listen offload
+	 * @priv: Private driver interface data
+	 * Returns: 0 on success or -1 on failure
+	 */
+	int (*p2p_lo_stop)(void *priv);
+
+	/**
+	 * set_default_scan_ies - Set default scan IEs
+	 * @priv: Private driver interface data
+	 * @ies: Scan default IEs buffer
+	 * @ies_len: Length of IEs in bytes
+	 * Returns: 0 on success or -1 on failure
+	 *
+	 * The driver can use these by default when there are no scan IEs coming
+	 * in the subsequent scan requests. Also in case of one or more of IEs
+	 * given in set_default_scan_ies() are missing in the subsequent scan
+	 * request, the driver should merge the missing scan IEs in the scan
+	 * request from the IEs set by set_default_scan_ies() in the Probe
+	 * Request frames sent.
+	 */
+	int (*set_default_scan_ies)(void *priv, const u8 *ies, size_t ies_len);
+
+	/**
+	 * set_tdls_mode - Set TDLS trigger mode to the host driver
+	 * @priv: Private driver interface data
+	 * @tdls_external_control: Represents if TDLS external trigger control
+	 *  mode is enabled/disabled.
+	 *
+	 * This optional callback can be used to configure the TDLS external
+	 * trigger control mode to the host driver.
+	 */
+	int (*set_tdls_mode)(void *priv, int tdls_external_control);
+
+	/**
+	 * get_bss_transition_status - Get candidate BSS's transition status
+	 * @priv: Private driver interface data
+	 * @params: Candidate BSS list
+	 *
+	 * Get the accept or reject reason code for a list of BSS transition
+	 * candidates.
+	 */
+	struct wpa_bss_candidate_info *
+	(*get_bss_transition_status)(void *priv,
+				     struct wpa_bss_trans_info *params);
+	/**
+	 * ignore_assoc_disallow - Configure driver to ignore assoc_disallow
+	 * @priv: Private driver interface data
+	 * @ignore_disallow: 0 to not ignore, 1 to ignore
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*ignore_assoc_disallow)(void *priv, int ignore_disallow);
+
+	/**
+	 * set_bssid_tmp_disallow - Set disallowed BSSIDs to the driver
+	 * @priv: Private driver interface data
+	 * @num_bssid: Number of temporarily disallowed BSSIDs
+	 * @bssids: List of temporarily disallowed BSSIDs
+	 */
+	int (*set_bssid_tmp_disallow)(void *priv, unsigned int num_bssid,
+				      const u8 *bssid);
+
+	/**
+	 * update_connect_params - Update the connection parameters
+	 * @priv: Private driver interface data
+	 * @params: Association parameters
+	 * @mask: Bit mask indicating which parameters in @params have to be
+	 *	updated
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * Update the connection parameters when in connected state so that the
+	 * driver uses the updated parameters for subsequent roaming. This is
+	 * used only with drivers that implement internal BSS selection and
+	 * roaming.
+	 */
+	int (*update_connect_params)(
+		void *priv, struct wpa_driver_associate_params *params,
+		enum wpa_drv_update_connect_params_mask mask);
+
+	/**
+	 * send_external_auth_status - Indicate the status of external
+	 * authentication processing to the host driver.
+	 * @priv: Private driver interface data
+	 * @params: Status of authentication processing.
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*send_external_auth_status)(void *priv,
+					 struct external_auth *params);
+
+	/**
+	 * set_4addr_mode - Set 4-address mode
+	 * @priv: Private driver interface data
+	 * @bridge_ifname: Bridge interface name
+	 * @val: 0 - disable 4addr mode, 1 - enable 4addr mode
+	 * Returns: 0 on success, < 0 on failure
+	 */
+	int (*set_4addr_mode)(void *priv, const char *bridge_ifname, int val);
+
+	/**
+	 * update_dh_ie - Update DH IE
+	 * @priv: Private driver interface data
+	 * @peer_mac: Peer MAC address
+	 * @reason_code: Reacon code
+	 * @ie: DH IE
+	 * @ie_len: DH IE length in bytes
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This callback is used to let the driver know the DH processing result
+	 * and DH IE for a pending association.
+	 */
+	int (*update_dh_ie)(void *priv, const u8 *peer_mac, u16 reason_code,
+			    const u8 *ie, size_t ie_len);
+
+	/**
+	 * dpp_listen - Notify driver about start/stop of DPP listen
+	 * @priv: Private driver interface data
+	 * @enable: Whether listen state is enabled (or disabled)
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This optional callback can be used to update RX frame filtering to
+	 * explicitly allow reception of broadcast Public Action frames.
+	 */
+	int (*dpp_listen)(void *priv, bool enable);
+
+	/**
+	 * set_secure_ranging_ctx - Add or delete secure ranging parameters of
+	 * the specified peer to the driver.
+	 * @priv: Private driver interface data
+	 * @params: Secure ranging parameters
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 */
+	int (*set_secure_ranging_ctx)(void *priv,
+				      struct secure_ranging_params *params);
+
+	/**
+	 * send_pasn_resp - Send PASN response for a set of peers to the
+	 * driver.
+	 * @priv: Private driver interface data
+	 * @params: Parameters holding peers and respective status.
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*send_pasn_resp)(void *priv, struct pasn_auth *params);
+
+	/**
+	 * get_sta_mlo_info - Get the current multi-link association info
+	 * @priv: Private driver interface data
+	 * @mlo: Pointer to fill multi-link association info
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This callback is used to fetch multi-link of the current association.
+	 */
+	int (*get_sta_mlo_info)(void *priv,
+				struct driver_sta_mlo_info *mlo_info);
+
+	/**
+	 * link_add - Add a link to the AP MLD interface
+	 * @priv: Private driver interface data
+	 * @link_id: The link ID
+	 * @addr: The MAC address to use for the link
+	 * Returns: 0 on success, negative value on failure
+	 */
+	int (*link_add)(void *priv, u8 link_id, const u8 *addr);
+
+#ifdef CONFIG_TESTING_OPTIONS
+	int (*register_frame)(void *priv, u16 type,
+			      const u8 *match, size_t match_len,
+			      bool multicast);
+#endif /* CONFIG_TESTING_OPTIONS */
+};
+
+/**
+ * enum wpa_event_type - Event type for wpa_supplicant_event() calls
+ */
+enum wpa_event_type {
+	/**
+	 * EVENT_ASSOC - Association completed
+	 *
+	 * This event needs to be delivered when the driver completes IEEE
+	 * 802.11 association or reassociation successfully.
+	 * wpa_driver_ops::get_bssid() is expected to provide the current BSSID
+	 * after this event has been generated. In addition, optional
+	 * EVENT_ASSOCINFO may be generated just before EVENT_ASSOC to provide
+	 * more information about the association. If the driver interface gets
+	 * both of these events at the same time, it can also include the
+	 * assoc_info data in EVENT_ASSOC call.
+	 */
+	EVENT_ASSOC,
+
+	/**
+	 * EVENT_DISASSOC - Association lost
+	 *
+	 * This event should be called when association is lost either due to
+	 * receiving deauthenticate or disassociate frame from the AP or when
+	 * sending either of these frames to the current AP. If the driver
+	 * supports separate deauthentication event, EVENT_DISASSOC should only
+	 * be used for disassociation and EVENT_DEAUTH for deauthentication.
+	 * In AP mode, union wpa_event_data::disassoc_info is required.
+	 */
+	EVENT_DISASSOC,
+
+	/**
+	 * EVENT_MICHAEL_MIC_FAILURE - Michael MIC (TKIP) detected
+	 *
+	 * This event must be delivered when a Michael MIC error is detected by
+	 * the local driver. Additional data for event processing is
+	 * provided with union wpa_event_data::michael_mic_failure. This
+	 * information is used to request new encryption key and to initiate
+	 * TKIP countermeasures if needed.
+	 */
+	EVENT_MICHAEL_MIC_FAILURE,
+
+	/**
+	 * EVENT_SCAN_RESULTS - Scan results available
+	 *
+	 * This event must be called whenever scan results are available to be
+	 * fetched with struct wpa_driver_ops::get_scan_results(). This event
+	 * is expected to be used some time after struct wpa_driver_ops::scan()
+	 * is called. If the driver provides an unsolicited event when the scan
+	 * has been completed, this event can be used to trigger
+	 * EVENT_SCAN_RESULTS call. If such event is not available from the
+	 * driver, the driver wrapper code is expected to use a registered
+	 * timeout to generate EVENT_SCAN_RESULTS call after the time that the
+	 * scan is expected to be completed. Optional information about
+	 * completed scan can be provided with union wpa_event_data::scan_info.
+	 */
+	EVENT_SCAN_RESULTS,
+
+	/**
+	 * EVENT_ASSOCINFO - Report optional extra information for association
+	 *
+	 * This event can be used to report extra association information for
+	 * EVENT_ASSOC processing. This extra information includes IEs from
+	 * association frames and Beacon/Probe Response frames in union
+	 * wpa_event_data::assoc_info. EVENT_ASSOCINFO must be send just before
+	 * EVENT_ASSOC. Alternatively, the driver interface can include
+	 * assoc_info data in the EVENT_ASSOC call if it has all the
+	 * information available at the same point.
+	 */
+	EVENT_ASSOCINFO,
+
+	/**
+	 * EVENT_INTERFACE_STATUS - Report interface status changes
+	 *
+	 * This optional event can be used to report changes in interface
+	 * status (interface added/removed) using union
+	 * wpa_event_data::interface_status. This can be used to trigger
+	 * wpa_supplicant to stop and re-start processing for the interface,
+	 * e.g., when a cardbus card is ejected/inserted.
+	 */
+	EVENT_INTERFACE_STATUS,
+
+	/**
+	 * EVENT_PMKID_CANDIDATE - Report a candidate AP for pre-authentication
+	 *
+	 * This event can be used to inform wpa_supplicant about candidates for
+	 * RSN (WPA2) pre-authentication. If wpa_supplicant is not responsible
+	 * for scan request (ap_scan=2 mode), this event is required for
+	 * pre-authentication. If wpa_supplicant is performing scan request
+	 * (ap_scan=1), this event is optional since scan results can be used
+	 * to add pre-authentication candidates. union
+	 * wpa_event_data::pmkid_candidate is used to report the BSSID of the
+	 * candidate and priority of the candidate, e.g., based on the signal
+	 * strength, in order to try to pre-authenticate first with candidates
+	 * that are most likely targets for re-association.
+	 *
+	 * EVENT_PMKID_CANDIDATE can be called whenever the driver has updates
+	 * on the candidate list. In addition, it can be called for the current
+	 * AP and APs that have existing PMKSA cache entries. wpa_supplicant
+	 * will automatically skip pre-authentication in cases where a valid
+	 * PMKSA exists. When more than one candidate exists, this event should
+	 * be generated once for each candidate.
+	 *
+	 * Driver will be notified about successful pre-authentication with
+	 * struct wpa_driver_ops::add_pmkid() calls.
+	 */
+	EVENT_PMKID_CANDIDATE,
+
+	/**
+	 * EVENT_TDLS - Request TDLS operation
+	 *
+	 * This event can be used to request a TDLS operation to be performed.
+	 */
+	EVENT_TDLS,
+
+	/**
+	 * EVENT_FT_RESPONSE - Report FT (IEEE 802.11r) response IEs
+	 *
+	 * The driver is expected to report the received FT IEs from
+	 * FT authentication sequence from the AP. The FT IEs are included in
+	 * the extra information in union wpa_event_data::ft_ies.
+	 */
+	EVENT_FT_RESPONSE,
+
+	/**
+	 * EVENT_IBSS_RSN_START - Request RSN authentication in IBSS
+	 *
+	 * The driver can use this event to inform wpa_supplicant about a STA
+	 * in an IBSS with which protected frames could be exchanged. This
+	 * event starts RSN authentication with the other STA to authenticate
+	 * the STA and set up encryption keys with it.
+	 */
+	EVENT_IBSS_RSN_START,
+
+	/**
+	 * EVENT_AUTH - Authentication result
+	 *
+	 * This event should be called when authentication attempt has been
+	 * completed. This is only used if the driver supports separate
+	 * authentication step (struct wpa_driver_ops::authenticate).
+	 * Information about authentication result is included in
+	 * union wpa_event_data::auth.
+	 */
+	EVENT_AUTH,
+
+	/**
+	 * EVENT_DEAUTH - Authentication lost
+	 *
+	 * This event should be called when authentication is lost either due
+	 * to receiving deauthenticate frame from the AP or when sending that
+	 * frame to the current AP.
+	 * In AP mode, union wpa_event_data::deauth_info is required.
+	 */
+	EVENT_DEAUTH,
+
+	/**
+	 * EVENT_ASSOC_REJECT - Association rejected
+	 *
+	 * This event should be called when (re)association attempt has been
+	 * rejected by the AP. Information about the association response is
+	 * included in union wpa_event_data::assoc_reject.
+	 */
+	EVENT_ASSOC_REJECT,
+
+	/**
+	 * EVENT_AUTH_TIMED_OUT - Authentication timed out
+	 */
+	EVENT_AUTH_TIMED_OUT,
+
+	/**
+	 * EVENT_ASSOC_TIMED_OUT - Association timed out
+	 */
+	EVENT_ASSOC_TIMED_OUT,
+
+	/**
+	 * EVENT_WPS_BUTTON_PUSHED - Report hardware push button press for WPS
+	 */
+	EVENT_WPS_BUTTON_PUSHED,
+
+	/**
+	 * EVENT_TX_STATUS - Report TX status
+	 */
+	EVENT_TX_STATUS,
+
+	/**
+	 * EVENT_RX_FROM_UNKNOWN - Report RX from unknown STA
+	 */
+	EVENT_RX_FROM_UNKNOWN,
+
+	/**
+	 * EVENT_RX_MGMT - Report RX of a management frame
+	 */
+	EVENT_RX_MGMT,
+
+	/**
+	 * EVENT_REMAIN_ON_CHANNEL - Remain-on-channel duration started
+	 *
+	 * This event is used to indicate when the driver has started the
+	 * requested remain-on-channel duration. Information about the
+	 * operation is included in union wpa_event_data::remain_on_channel.
+	 */
+	EVENT_REMAIN_ON_CHANNEL,
+
+	/**
+	 * EVENT_CANCEL_REMAIN_ON_CHANNEL - Remain-on-channel timed out
+	 *
+	 * This event is used to indicate when the driver has completed
+	 * remain-on-channel duration, i.e., may noot be available on the
+	 * requested channel anymore. Information about the
+	 * operation is included in union wpa_event_data::remain_on_channel.
+	 */
+	EVENT_CANCEL_REMAIN_ON_CHANNEL,
+
+	/**
+	 * EVENT_RX_PROBE_REQ - Indicate received Probe Request frame
+	 *
+	 * This event is used to indicate when a Probe Request frame has been
+	 * received. Information about the received frame is included in
+	 * union wpa_event_data::rx_probe_req. The driver is required to report
+	 * these events only after successfully completed probe_req_report()
+	 * commands to request the events (i.e., report parameter is non-zero)
+	 * in station mode. In AP mode, Probe Request frames should always be
+	 * reported.
+	 */
+	EVENT_RX_PROBE_REQ,
+
+	/**
+	 * EVENT_NEW_STA - New wired device noticed
+	 *
+	 * This event is used to indicate that a new device has been detected
+	 * in a network that does not use association-like functionality (i.e.,
+	 * mainly wired Ethernet). This can be used to start EAPOL
+	 * authenticator when receiving a frame from a device. The address of
+	 * the device is included in union wpa_event_data::new_sta.
+	 */
+	EVENT_NEW_STA,
+
+	/**
+	 * EVENT_EAPOL_RX - Report received EAPOL frame
+	 *
+	 * When in AP mode with hostapd, this event is required to be used to
+	 * deliver the receive EAPOL frames from the driver.
+	 */
+	EVENT_EAPOL_RX,
+
+	/**
+	 * EVENT_SIGNAL_CHANGE - Indicate change in signal strength
+	 *
+	 * This event is used to indicate changes in the signal strength
+	 * observed in frames received from the current AP if signal strength
+	 * monitoring has been enabled with signal_monitor().
+	 */
+	EVENT_SIGNAL_CHANGE,
+
+	/**
+	 * EVENT_INTERFACE_ENABLED - Notify that interface was enabled
+	 *
+	 * This event is used to indicate that the interface was enabled after
+	 * having been previously disabled, e.g., due to rfkill.
+	 */
+	EVENT_INTERFACE_ENABLED,
+
+	/**
+	 * EVENT_INTERFACE_DISABLED - Notify that interface was disabled
+	 *
+	 * This event is used to indicate that the interface was disabled,
+	 * e.g., due to rfkill.
+	 */
+	EVENT_INTERFACE_DISABLED,
+
+	/**
+	 * EVENT_CHANNEL_LIST_CHANGED - Channel list changed
+	 *
+	 * This event is used to indicate that the channel list has changed,
+	 * e.g., because of a regulatory domain change triggered by scan
+	 * results including an AP advertising a country code.
+	 */
+	EVENT_CHANNEL_LIST_CHANGED,
+
+	/**
+	 * EVENT_INTERFACE_UNAVAILABLE - Notify that interface is unavailable
+	 *
+	 * This event is used to indicate that the driver cannot maintain this
+	 * interface in its operation mode anymore. The most likely use for
+	 * this is to indicate that AP mode operation is not available due to
+	 * operating channel would need to be changed to a DFS channel when
+	 * the driver does not support radar detection and another virtual
+	 * interfaces caused the operating channel to change. Other similar
+	 * resource conflicts could also trigger this for station mode
+	 * interfaces. This event can be propagated when channel switching
+	 * fails.
+	 */
+	EVENT_INTERFACE_UNAVAILABLE,
+
+	/**
+	 * EVENT_BEST_CHANNEL
+	 *
+	 * Driver generates this event whenever it detects a better channel
+	 * (e.g., based on RSSI or channel use). This information can be used
+	 * to improve channel selection for a new AP/P2P group.
+	 */
+	EVENT_BEST_CHANNEL,
+
+	/**
+	 * EVENT_UNPROT_DEAUTH - Unprotected Deauthentication frame received
+	 *
+	 * This event should be called when a Deauthentication frame is dropped
+	 * due to it not being protected (MFP/IEEE 802.11w).
+	 * union wpa_event_data::unprot_deauth is required to provide more
+	 * details of the frame.
+	 */
+	EVENT_UNPROT_DEAUTH,
+
+	/**
+	 * EVENT_UNPROT_DISASSOC - Unprotected Disassociation frame received
+	 *
+	 * This event should be called when a Disassociation frame is dropped
+	 * due to it not being protected (MFP/IEEE 802.11w).
+	 * union wpa_event_data::unprot_disassoc is required to provide more
+	 * details of the frame.
+	 */
+	EVENT_UNPROT_DISASSOC,
+
+	/**
+	 * EVENT_STATION_LOW_ACK
+	 *
+	 * Driver generates this event whenever it detected that a particular
+	 * station was lost. Detection can be through massive transmission
+	 * failures for example.
+	 */
+	EVENT_STATION_LOW_ACK,
+
+	/**
+	 * EVENT_IBSS_PEER_LOST - IBSS peer not reachable anymore
+	 */
+	EVENT_IBSS_PEER_LOST,
+
+	/**
+	 * EVENT_DRIVER_GTK_REKEY - Device/driver did GTK rekey
+	 *
+	 * This event carries the new replay counter to notify wpa_supplicant
+	 * of the current EAPOL-Key Replay Counter in case the driver/firmware
+	 * completed Group Key Handshake while the host (including
+	 * wpa_supplicant was sleeping).
+	 */
+	EVENT_DRIVER_GTK_REKEY,
+
+	/**
+	 * EVENT_SCHED_SCAN_STOPPED - Scheduled scan was stopped
+	 */
+	EVENT_SCHED_SCAN_STOPPED,
+
+	/**
+	 * EVENT_DRIVER_CLIENT_POLL_OK - Station responded to poll
+	 *
+	 * This event indicates that the station responded to the poll
+	 * initiated with @poll_client.
+	 */
+	EVENT_DRIVER_CLIENT_POLL_OK,
+
+	/**
+	 * EVENT_EAPOL_TX_STATUS - notify of EAPOL TX status
+	 */
+	EVENT_EAPOL_TX_STATUS,
+
+	/**
+	 * EVENT_CH_SWITCH - AP or GO decided to switch channels
+	 *
+	 * Described in wpa_event_data.ch_switch
+	 * */
+	EVENT_CH_SWITCH,
+
+	/**
+	 * EVENT_CH_SWITCH_STARTED - AP or GO started to switch channels
+	 *
+	 * This is a pre-switch event indicating the shortly following switch
+	 * of operating channels.
+	 *
+	 * Described in wpa_event_data.ch_switch
+	 */
+	EVENT_CH_SWITCH_STARTED,
+	/**
+	 * EVENT_WNM - Request WNM operation
+	 *
+	 * This event can be used to request a WNM operation to be performed.
+	 */
+	EVENT_WNM,
+
+	/**
+	 * EVENT_CONNECT_FAILED_REASON - Connection failure reason in AP mode
+	 *
+	 * This event indicates that the driver reported a connection failure
+	 * with the specified client (for example, max client reached, etc.) in
+	 * AP mode.
+	 */
+	EVENT_CONNECT_FAILED_REASON,
+
+	/**
+	 * EVENT_DFS_RADAR_DETECTED - Notify of radar detection
+	 *
+	 * A radar has been detected on the supplied frequency, hostapd should
+	 * react accordingly (e.g., change channel).
+	 */
+	EVENT_DFS_RADAR_DETECTED,
+
+	/**
+	 * EVENT_DFS_CAC_FINISHED - Notify that channel availability check has been completed
+	 *
+	 * After a successful CAC, the channel can be marked clear and used.
+	 */
+	EVENT_DFS_CAC_FINISHED,
+
+	/**
+	 * EVENT_DFS_CAC_ABORTED - Notify that channel availability check has been aborted
+	 *
+	 * The CAC was not successful, and the channel remains in the previous
+	 * state. This may happen due to a radar being detected or other
+	 * external influences.
+	 */
+	EVENT_DFS_CAC_ABORTED,
+
+	/**
+	 * EVENT_DFS_NOP_FINISHED - Notify that non-occupancy period is over
+	 *
+	 * The channel which was previously unavailable is now available again.
+	 */
+	EVENT_DFS_NOP_FINISHED,
+
+	/**
+	 * EVENT_SURVEY - Received survey data
+	 *
+	 * This event gets triggered when a driver query is issued for survey
+	 * data and the requested data becomes available. The returned data is
+	 * stored in struct survey_results. The results provide at most one
+	 * survey entry for each frequency and at minimum will provide one
+	 * survey entry for one frequency. The survey data can be os_malloc()'d
+	 * and then os_free()'d, so the event callback must only copy data.
+	 */
+	EVENT_SURVEY,
+
+	/**
+	 * EVENT_SCAN_STARTED - Scan started
+	 *
+	 * This indicates that driver has started a scan operation either based
+	 * on a request from wpa_supplicant/hostapd or from another application.
+	 * EVENT_SCAN_RESULTS is used to indicate when the scan has been
+	 * completed (either successfully or by getting cancelled).
+	 */
+	EVENT_SCAN_STARTED,
+
+	/**
+	 * EVENT_AVOID_FREQUENCIES - Received avoid frequency range
+	 *
+	 * This event indicates a set of frequency ranges that should be avoided
+	 * to reduce issues due to interference or internal co-existence
+	 * information in the driver.
+	 */
+	EVENT_AVOID_FREQUENCIES,
+
+	/**
+	 * EVENT_NEW_PEER_CANDIDATE - new (unknown) mesh peer notification
+	 */
+	EVENT_NEW_PEER_CANDIDATE,
+
+	/**
+	 * EVENT_ACS_CHANNEL_SELECTED - Received selected channels by ACS
+	 *
+	 * Indicates a pair of primary and secondary channels chosen by ACS
+	 * in device.
+	 */
+	EVENT_ACS_CHANNEL_SELECTED,
+
+	/**
+	 * EVENT_DFS_CAC_STARTED - Notify that channel availability check has
+	 * been started.
+	 *
+	 * This event indicates that channel availability check has been started
+	 * on a DFS frequency by a driver that supports DFS Offload.
+	 */
+	EVENT_DFS_CAC_STARTED,
+
+	/**
+	 * EVENT_P2P_LO_STOP - Notify that P2P listen offload is stopped
+	 */
+	EVENT_P2P_LO_STOP,
+
+	/**
+	 * EVENT_BEACON_LOSS - Beacon loss detected
+	 *
+	 * This event indicates that no Beacon frames has been received from
+	 * the current AP. This may indicate that the AP is not anymore in
+	 * range.
+	 */
+	EVENT_BEACON_LOSS,
+
+	/**
+	 * EVENT_DFS_PRE_CAC_EXPIRED - Notify that channel availability check
+	 * done previously (Pre-CAC) on the channel has expired. This would
+	 * normally be on a non-ETSI DFS regulatory domain. DFS state of the
+	 * channel will be moved from available to usable. A new CAC has to be
+	 * performed before start operating on this channel.
+	 */
+	EVENT_DFS_PRE_CAC_EXPIRED,
+
+	/**
+	 * EVENT_EXTERNAL_AUTH - This event interface is used by host drivers
+	 * that do not define separate commands for authentication and
+	 * association (~WPA_DRIVER_FLAGS_SME) but offload the 802.11
+	 * authentication to wpa_supplicant. This event carries all the
+	 * necessary information from the host driver for the authentication to
+	 * happen.
+	 */
+	EVENT_EXTERNAL_AUTH,
+
+	/**
+	 * EVENT_PORT_AUTHORIZED - Notification that a connection is authorized
+	 *
+	 * This event should be indicated when the driver completes the 4-way
+	 * handshake. This event should be preceded by an EVENT_ASSOC that
+	 * indicates the completion of IEEE 802.11 association.
+	 */
+	EVENT_PORT_AUTHORIZED,
+
+	/**
+	 * EVENT_STATION_OPMODE_CHANGED - Notify STA's HT/VHT operation mode
+	 * change event.
+	 */
+	EVENT_STATION_OPMODE_CHANGED,
+
+	/**
+	 * EVENT_INTERFACE_MAC_CHANGED - Notify that interface MAC changed
+	 *
+	 * This event is emitted when the MAC changes while the interface is
+	 * enabled. When an interface was disabled and becomes enabled, it
+	 * must be always assumed that the MAC possibly changed.
+	 */
+	EVENT_INTERFACE_MAC_CHANGED,
+
+	/**
+	 * EVENT_WDS_STA_INTERFACE_STATUS - Notify WDS STA interface status
+	 *
+	 * This event is emitted when an interface is added/removed for WDS STA.
+	 */
+	EVENT_WDS_STA_INTERFACE_STATUS,
+
+	/**
+	  * EVENT_UPDATE_DH - Notification of updated DH information
+	  */
+	EVENT_UPDATE_DH,
+
+	/**
+	 * EVENT_UNPROT_BEACON - Unprotected Beacon frame received
+	 *
+	 * This event should be called when a Beacon frame is dropped due to it
+	 * not being protected correctly. union wpa_event_data::unprot_beacon
+	 * is required to provide more details of the frame.
+	 */
+	EVENT_UNPROT_BEACON,
+
+	/**
+	 * EVENT_TX_WAIT_EXPIRE - TX wait timed out
+	 *
+	 * This event is used to indicate when the driver has completed
+	 * wait for a response frame based on a TX request that specified a
+	 * non-zero wait time and that has not been explicitly cancelled.
+	 */
+	EVENT_TX_WAIT_EXPIRE,
+
+	/**
+	  * EVENT_BSS_COLOR_COLLISION - Notification of a BSS color collision
+	  */
+	EVENT_BSS_COLOR_COLLISION,
+
+	/**
+	 * EVENT_CCA_STARTED_NOTIFY - Notification that CCA has started
+	 */
+	EVENT_CCA_STARTED_NOTIFY,
+
+	/**
+	 * EVENT_CCA_ABORTED_NOTIFY - Notification that CCA has aborted
+	 */
+	EVENT_CCA_ABORTED_NOTIFY,
+
+	/**
+	 * EVENT_CCA_NOTIFY - Notification that CCA has completed
+	 */
+	EVENT_CCA_NOTIFY,
+
+	/**
+	 * EVENT_PASN_AUTH - This event is used by the driver that requests
+	 * PASN authentication and secure ranging context for multiple peers.
+	 */
+	EVENT_PASN_AUTH,
+
+	/**
+	 * EVENT_LINK_CH_SWITCH - MLD AP link decided to switch channels
+	 *
+	 * Described in wpa_event_data.ch_switch.
+	 *
+	 */
+	EVENT_LINK_CH_SWITCH,
+
+	/**
+	 * EVENT_LINK_CH_SWITCH_STARTED - MLD AP link started to switch channels
+	 *
+	 * This is a pre-switch event indicating the shortly following switch
+	 * of operating channels.
+	 *
+	 * Described in wpa_event_data.ch_switch.
+	 */
+	EVENT_LINK_CH_SWITCH_STARTED,
+
+	/**
+	 * EVENT_TID_LINK_MAP - MLD event to set TID-to-link mapping
+	 *
+	 * This event is used by the driver to indicate the received TID-to-link
+	 * mapping response from the associated AP MLD.
+	 *
+	 * Described in wpa_event_data.t2l_map_info.
+	 */
+	EVENT_TID_LINK_MAP,
+
+	/**
+	 * EVENT_LINK_RECONFIG - Notification that AP links removed
+	 */
+	EVENT_LINK_RECONFIG,
+};
+
+
+/**
+ * struct freq_survey - Channel survey info
+ *
+ * @ifidx: Interface index in which this survey was observed
+ * @freq: Center of frequency of the surveyed channel
+ * @nf: Channel noise floor in dBm
+ * @channel_time: Amount of time in ms the radio spent on the channel
+ * @channel_time_busy: Amount of time in ms the radio detected some signal
+ *     that indicated to the radio the channel was not clear
+ * @channel_time_rx: Amount of time the radio spent receiving data
+ * @channel_time_tx: Amount of time the radio spent transmitting data
+ * @filled: bitmask indicating which fields have been reported, see
+ *     SURVEY_HAS_* defines.
+ * @list: Internal list pointers
+ */
+struct freq_survey {
+	u32 ifidx;
+	unsigned int freq;
+	s8 nf;
+	u64 channel_time;
+	u64 channel_time_busy;
+	u64 channel_time_rx;
+	u64 channel_time_tx;
+	unsigned int filled;
+	struct dl_list list;
+};
+
+#define SURVEY_HAS_NF BIT(0)
+#define SURVEY_HAS_CHAN_TIME BIT(1)
+#define SURVEY_HAS_CHAN_TIME_BUSY BIT(2)
+#define SURVEY_HAS_CHAN_TIME_RX BIT(3)
+#define SURVEY_HAS_CHAN_TIME_TX BIT(4)
+
+/**
+ * enum sta_connect_fail_reason_codes - STA connect failure reason code values
+ * @STA_CONNECT_FAIL_REASON_UNSPECIFIED: No reason code specified for
+ *	connection failure.
+ * @STA_CONNECT_FAIL_REASON_NO_BSS_FOUND: No Probe Response frame received
+ *	for unicast Probe Request frame.
+ * @STA_CONNECT_FAIL_REASON_AUTH_TX_FAIL: STA failed to send auth request.
+ * @STA_CONNECT_FAIL_REASON_AUTH_NO_ACK_RECEIVED: AP didn't send ACK for
+ *	auth request.
+ * @STA_CONNECT_FAIL_REASON_AUTH_NO_RESP_RECEIVED: Auth response is not
+ *	received from AP.
+ * @STA_CONNECT_FAIL_REASON_ASSOC_REQ_TX_FAIL: STA failed to send
+ *	Association Request frame.
+ * @STA_CONNECT_FAIL_REASON_ASSOC_NO_ACK_RECEIVED: AP didn't send ACK for
+ *	Association Request frame.
+ * @STA_CONNECT_FAIL_REASON_ASSOC_NO_RESP_RECEIVED: Association Response
+ *	frame is not received from AP.
+ */
+enum sta_connect_fail_reason_codes {
+	STA_CONNECT_FAIL_REASON_UNSPECIFIED = 0,
+	STA_CONNECT_FAIL_REASON_NO_BSS_FOUND = 1,
+	STA_CONNECT_FAIL_REASON_AUTH_TX_FAIL = 2,
+	STA_CONNECT_FAIL_REASON_AUTH_NO_ACK_RECEIVED = 3,
+	STA_CONNECT_FAIL_REASON_AUTH_NO_RESP_RECEIVED = 4,
+	STA_CONNECT_FAIL_REASON_ASSOC_REQ_TX_FAIL = 5,
+	STA_CONNECT_FAIL_REASON_ASSOC_NO_ACK_RECEIVED = 6,
+	STA_CONNECT_FAIL_REASON_ASSOC_NO_RESP_RECEIVED = 7,
+};
+
+/**
+ * union wpa_event_data - Additional data for wpa_supplicant_event() calls
+ */
+union wpa_event_data {
+	/**
+	 * struct assoc_info - Data for EVENT_ASSOC and EVENT_ASSOCINFO events
+	 *
+	 * This structure is optional for EVENT_ASSOC calls and required for
+	 * EVENT_ASSOCINFO calls. By using EVENT_ASSOC with this data, the
+	 * driver interface does not need to generate separate EVENT_ASSOCINFO
+	 * calls.
+	 */
+	struct assoc_info {
+		/**
+		 * reassoc - Flag to indicate association or reassociation
+		 */
+		int reassoc;
+
+		/**
+		 * req_ies - (Re)Association Request IEs
+		 *
+		 * If the driver generates WPA/RSN IE, this event data must be
+		 * returned for WPA handshake to have needed information. If
+		 * wpa_supplicant-generated WPA/RSN IE is used, this
+		 * information event is optional.
+		 *
+		 * This should start with the first IE (fixed fields before IEs
+		 * are not included).
+		 */
+		const u8 *req_ies;
+
+		/**
+		 * req_ies_len - Length of req_ies in bytes
+		 */
+		size_t req_ies_len;
+
+		/**
+		 * resp_ies - (Re)Association Response IEs
+		 *
+		 * Optional association data from the driver. This data is not
+		 * required WPA, but may be useful for some protocols and as
+		 * such, should be reported if this is available to the driver
+		 * interface.
+		 *
+		 * This should start with the first IE (fixed fields before IEs
+		 * are not included).
+		 */
+		const u8 *resp_ies;
+
+		/**
+		 * resp_ies_len - Length of resp_ies in bytes
+		 */
+		size_t resp_ies_len;
+
+		/**
+		 * resp_frame - (Re)Association Response frame
+		 */
+		const u8 *resp_frame;
+
+		/**
+		 * resp_frame_len - (Re)Association Response frame length
+		 */
+		size_t resp_frame_len;
+
+		/**
+		 * beacon_ies - Beacon or Probe Response IEs
+		 *
+		 * Optional Beacon/ProbeResp data: IEs included in Beacon or
+		 * Probe Response frames from the current AP (i.e., the one
+		 * that the client just associated with). This information is
+		 * used to update WPA/RSN IE for the AP. If this field is not
+		 * set, the results from previous scan will be used. If no
+		 * data for the new AP is found, scan results will be requested
+		 * again (without scan request). At this point, the driver is
+		 * expected to provide WPA/RSN IE for the AP (if WPA/WPA2 is
+		 * used).
+		 *
+		 * This should start with the first IE (fixed fields before IEs
+		 * are not included).
+		 */
+		const u8 *beacon_ies;
+
+		/**
+		 * beacon_ies_len - Length of beacon_ies */
+		size_t beacon_ies_len;
+
+		/**
+		 * freq - Frequency of the operational channel in MHz
+		 */
+		unsigned int freq;
+
+		/**
+		 * wmm_params - WMM parameters used in this association.
+		 */
+		struct wmm_params wmm_params;
+
+		/**
+		 * addr - Station address (for AP mode)
+		 */
+		const u8 *addr;
+
+		/**
+		 * The following is the key management offload information
+		 * @authorized
+		 * @key_replay_ctr
+		 * @key_replay_ctr_len
+		 * @ptk_kck
+		 * @ptk_kek_len
+		 * @ptk_kek
+		 * @ptk_kek_len
+		 */
+
+		/**
+		 * authorized - Status of key management offload,
+		 * 1 = successful
+		 */
+		int authorized;
+
+		/**
+		 * key_replay_ctr - Key replay counter value last used
+		 * in a valid EAPOL-Key frame
+		 */
+		const u8 *key_replay_ctr;
+
+		/**
+		 * key_replay_ctr_len - The length of key_replay_ctr
+		 */
+		size_t key_replay_ctr_len;
+
+		/**
+		 * ptk_kck - The derived PTK KCK
+		 */
+		const u8 *ptk_kck;
+
+		/**
+		 * ptk_kek_len - The length of ptk_kck
+		 */
+		size_t ptk_kck_len;
+
+		/**
+		 * ptk_kek - The derived PTK KEK
+		 * This is used in key management offload and also in FILS SK
+		 * offload.
+		 */
+		const u8 *ptk_kek;
+
+		/**
+		 * ptk_kek_len - The length of ptk_kek
+		 */
+		size_t ptk_kek_len;
+
+		/**
+		 * subnet_status - The subnet status:
+		 * 0 = unknown, 1 = unchanged, 2 = changed
+		 */
+		u8 subnet_status;
+
+		/**
+		 * The following information is used in FILS SK offload
+		 * @fils_erp_next_seq_num
+		 * @fils_pmk
+		 * @fils_pmk_len
+		 * @fils_pmkid
+		 */
+
+		/**
+		 * fils_erp_next_seq_num - The next sequence number to use in
+		 * FILS ERP messages
+		 */
+		u16 fils_erp_next_seq_num;
+
+		/**
+		 * fils_pmk - A new PMK if generated in case of FILS
+		 * authentication
+		 */
+		const u8 *fils_pmk;
+
+		/**
+		 * fils_pmk_len - Length of fils_pmk
+		 */
+		size_t fils_pmk_len;
+
+		/**
+		 * fils_pmkid - PMKID used or generated in FILS authentication
+		 */
+		const u8 *fils_pmkid;
+	} assoc_info;
+
+	/**
+	 * struct disassoc_info - Data for EVENT_DISASSOC events
+	 */
+	struct disassoc_info {
+		/**
+		 * addr - Station address (for AP mode)
+		 */
+		const u8 *addr;
+
+		/**
+		 * reason_code - Reason Code (host byte order) used in
+		 *	Deauthentication frame
+		 */
+		u16 reason_code;
+
+		/**
+		 * ie - Optional IE(s) in Disassociation frame
+		 */
+		const u8 *ie;
+
+		/**
+		 * ie_len - Length of ie buffer in octets
+		 */
+		size_t ie_len;
+
+		/**
+		 * locally_generated - Whether the frame was locally generated
+		 */
+		int locally_generated;
+	} disassoc_info;
+
+	/**
+	 * struct deauth_info - Data for EVENT_DEAUTH events
+	 */
+	struct deauth_info {
+		/**
+		 * addr - Station address (for AP mode)
+		 */
+		const u8 *addr;
+
+		/**
+		 * reason_code - Reason Code (host byte order) used in
+		 *	Deauthentication frame
+		 */
+		u16 reason_code;
+
+		/**
+		 * ie - Optional IE(s) in Deauthentication frame
+		 */
+		const u8 *ie;
+
+		/**
+		 * ie_len - Length of ie buffer in octets
+		 */
+		size_t ie_len;
+
+		/**
+		 * locally_generated - Whether the frame was locally generated
+		 */
+		int locally_generated;
+	} deauth_info;
+
+	/**
+	 * struct michael_mic_failure - Data for EVENT_MICHAEL_MIC_FAILURE
+	 */
+	struct michael_mic_failure {
+		int unicast;
+		const u8 *src;
+	} michael_mic_failure;
+
+	/**
+	 * struct interface_status - Data for EVENT_INTERFACE_STATUS
+	 */
+	struct interface_status {
+		unsigned int ifindex;
+		char ifname[100];
+		enum {
+			EVENT_INTERFACE_ADDED, EVENT_INTERFACE_REMOVED
+		} ievent;
+	} interface_status;
+
+	/**
+	 * struct pmkid_candidate - Data for EVENT_PMKID_CANDIDATE
+	 */
+	struct pmkid_candidate {
+		/** BSSID of the PMKID candidate */
+		u8 bssid[ETH_ALEN];
+		/** Smaller the index, higher the priority */
+		int index;
+		/** Whether RSN IE includes pre-authenticate flag */
+		int preauth;
+	} pmkid_candidate;
+
+	/**
+	 * struct tdls - Data for EVENT_TDLS
+	 */
+	struct tdls {
+		u8 peer[ETH_ALEN];
+		enum {
+			TDLS_REQUEST_SETUP,
+			TDLS_REQUEST_TEARDOWN,
+			TDLS_REQUEST_DISCOVER,
+		} oper;
+		u16 reason_code; /* for teardown */
+	} tdls;
+
+	/**
+	 * struct wnm - Data for EVENT_WNM
+	 */
+	struct wnm {
+		u8 addr[ETH_ALEN];
+		enum {
+			WNM_OPER_SLEEP,
+		} oper;
+		enum {
+			WNM_SLEEP_ENTER,
+			WNM_SLEEP_EXIT
+		} sleep_action;
+		int sleep_intval;
+		u16 reason_code;
+		u8 *buf;
+		u16 buf_len;
+	} wnm;
+
+	/**
+	 * struct ft_ies - FT information elements (EVENT_FT_RESPONSE)
+	 *
+	 * During FT (IEEE 802.11r) authentication sequence, the driver is
+	 * expected to use this event to report received FT IEs (MDIE, FTIE,
+	 * RSN IE, TIE, possible resource request) to the supplicant. The FT
+	 * IEs for the next message will be delivered through the
+	 * struct wpa_driver_ops::update_ft_ies() callback.
+	 */
+	struct ft_ies {
+		const u8 *ies;
+		size_t ies_len;
+		int ft_action;
+		u8 target_ap[ETH_ALEN];
+		/** Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request */
+		const u8 *ric_ies;
+		/** Length of ric_ies buffer in octets */
+		size_t ric_ies_len;
+	} ft_ies;
+
+	/**
+	 * struct ibss_rsn_start - Data for EVENT_IBSS_RSN_START
+	 */
+	struct ibss_rsn_start {
+		u8 peer[ETH_ALEN];
+	} ibss_rsn_start;
+
+	/**
+	 * struct auth_info - Data for EVENT_AUTH events
+	 */
+	struct auth_info {
+		u8 peer[ETH_ALEN];
+		u8 bssid[ETH_ALEN];
+		u16 auth_type;
+		u16 auth_transaction;
+		u16 status_code;
+		const u8 *ies;
+		size_t ies_len;
+	} auth;
+
+	/**
+	 * struct assoc_reject - Data for EVENT_ASSOC_REJECT events
+	 */
+	struct assoc_reject {
+		/**
+		 * bssid - BSSID of the AP that rejected association
+		 */
+		const u8 *bssid;
+
+		/**
+		 * resp_ies - (Re)Association Response IEs
+		 *
+		 * Optional association data from the driver. This data is not
+		 * required WPA, but may be useful for some protocols and as
+		 * such, should be reported if this is available to the driver
+		 * interface.
+		 *
+		 * This should start with the first IE (fixed fields before IEs
+		 * are not included).
+		 */
+		const u8 *resp_ies;
+
+		/**
+		 * resp_ies_len - Length of resp_ies in bytes
+		 */
+		size_t resp_ies_len;
+
+		/**
+		 * status_code - Status Code from (Re)association Response
+		 */
+		u16 status_code;
+
+		/**
+		 * timed_out - Whether failure is due to timeout (etc.) rather
+		 * than explicit rejection response from the AP.
+		 */
+		int timed_out;
+
+		/**
+		 * timeout_reason - Reason for the timeout
+		 */
+		const char *timeout_reason;
+
+		/**
+		 * fils_erp_next_seq_num - The next sequence number to use in
+		 * FILS ERP messages
+		 */
+		u16 fils_erp_next_seq_num;
+
+		/**
+		 * reason_code - Connection failure reason code from the driver
+		 */
+		enum sta_connect_fail_reason_codes reason_code;
+	} assoc_reject;
+
+	struct timeout_event {
+		u8 addr[ETH_ALEN];
+	} timeout_event;
+
+	/**
+	 * struct tx_status - Data for EVENT_TX_STATUS events
+	 */
+	struct tx_status {
+		u16 type;
+		u16 stype;
+		const u8 *dst;
+		const u8 *data;
+		size_t data_len;
+		int ack;
+		int link_id;
+	} tx_status;
+
+	/**
+	 * struct rx_from_unknown - Data for EVENT_RX_FROM_UNKNOWN events
+	 */
+	struct rx_from_unknown {
+		const u8 *bssid;
+		const u8 *addr;
+		int wds;
+	} rx_from_unknown;
+
+	/**
+	 * struct rx_mgmt - Data for EVENT_RX_MGMT events
+	 */
+	struct rx_mgmt {
+		const u8 *frame;
+		size_t frame_len;
+		u32 datarate;
+
+		/**
+		 * drv_priv - Pointer to store driver private BSS information
+		 *
+		 * If not set to NULL, this is used for comparison with
+		 * hostapd_data->drv_priv to determine which BSS should process
+		 * the frame.
+		 */
+		void *drv_priv;
+
+		/**
+		 * freq - Frequency (in MHz) on which the frame was received
+		 */
+		int freq;
+
+		/**
+		 * ssi_signal - Signal strength in dBm (or 0 if not available)
+		 */
+		int ssi_signal;
+
+		/**
+		 * link_id - MLO link on which the frame was received or -1 for
+		 * non MLD.
+		 */
+		int link_id;
+	} rx_mgmt;
+
+	/**
+	 * struct remain_on_channel - Data for EVENT_REMAIN_ON_CHANNEL events
+	 *
+	 * This is also used with EVENT_CANCEL_REMAIN_ON_CHANNEL events.
+	 */
+	struct remain_on_channel {
+		/**
+		 * freq - Channel frequency in MHz
+		 */
+		unsigned int freq;
+
+		/**
+		 * duration - Duration to remain on the channel in milliseconds
+		 */
+		unsigned int duration;
+	} remain_on_channel;
+
+	/**
+	 * struct scan_info - Optional data for EVENT_SCAN_RESULTS events
+	 * @aborted: Whether the scan was aborted
+	 * @freqs: Scanned frequencies in MHz (%NULL = all channels scanned)
+	 * @num_freqs: Number of entries in freqs array
+	 * @ssids: Scanned SSIDs (%NULL or zero-length SSID indicates wildcard
+	 *	SSID)
+	 * @num_ssids: Number of entries in ssids array
+	 * @external_scan: Whether the scan info is for an external scan
+	 * @nl_scan_event: 1 if the source of this scan event is a normal scan,
+	 * 	0 if the source of the scan event is a vendor scan
+	 * @scan_start_tsf: Time when the scan started in terms of TSF of the
+	 *	BSS that the interface that requested the scan is connected to
+	 *	(if available).
+	 * @scan_start_tsf_bssid: The BSSID according to which %scan_start_tsf
+	 *	is set.
+	 */
+	struct scan_info {
+		int aborted;
+		const int *freqs;
+		size_t num_freqs;
+		struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS];
+		size_t num_ssids;
+		int external_scan;
+		int nl_scan_event;
+		u64 scan_start_tsf;
+		u8 scan_start_tsf_bssid[ETH_ALEN];
+	} scan_info;
+
+	/**
+	 * struct rx_probe_req - Data for EVENT_RX_PROBE_REQ events
+	 */
+	struct rx_probe_req {
+		/**
+		 * sa - Source address of the received Probe Request frame
+		 */
+		const u8 *sa;
+
+		/**
+		 * da - Destination address of the received Probe Request frame
+		 *	or %NULL if not available
+		 */
+		const u8 *da;
+
+		/**
+		 * bssid - BSSID of the received Probe Request frame or %NULL
+		 *	if not available
+		 */
+		const u8 *bssid;
+
+		/**
+		 * ie - IEs from the Probe Request body
+		 */
+		const u8 *ie;
+
+		/**
+		 * ie_len - Length of ie buffer in octets
+		 */
+		size_t ie_len;
+
+		/**
+		 * signal - signal strength in dBm (or 0 if not available)
+		 */
+		int ssi_signal;
+	} rx_probe_req;
+
+	/**
+	 * struct new_sta - Data for EVENT_NEW_STA events
+	 */
+	struct new_sta {
+		const u8 *addr;
+	} new_sta;
+
+	/**
+	 * struct eapol_rx - Data for EVENT_EAPOL_RX events
+	 */
+	struct eapol_rx {
+		const u8 *src;
+		const u8 *data;
+		size_t data_len;
+		enum frame_encryption encrypted;
+		int link_id;
+	} eapol_rx;
+
+	/**
+	 * signal_change - Data for EVENT_SIGNAL_CHANGE events
+	 */
+	struct wpa_signal_info signal_change;
+
+	/**
+	 * struct best_channel - Data for EVENT_BEST_CHANNEL events
+	 * @freq_24: Best 2.4 GHz band channel frequency in MHz
+	 * @freq_5: Best 5 GHz band channel frequency in MHz
+	 * @freq_overall: Best channel frequency in MHz
+	 *
+	 * 0 can be used to indicate no preference in either band.
+	 */
+	struct best_channel {
+		int freq_24;
+		int freq_5;
+		int freq_overall;
+	} best_chan;
+
+	struct unprot_deauth {
+		const u8 *sa;
+		const u8 *da;
+		u16 reason_code;
+	} unprot_deauth;
+
+	struct unprot_disassoc {
+		const u8 *sa;
+		const u8 *da;
+		u16 reason_code;
+	} unprot_disassoc;
+
+	/**
+	 * struct low_ack - Data for EVENT_STATION_LOW_ACK events
+	 * @addr: station address
+	 * @num_packets: Number of packets lost (consecutive packets not
+	 * acknowledged)
+	 */
+	struct low_ack {
+		u8 addr[ETH_ALEN];
+		u32 num_packets;
+	} low_ack;
+
+	/**
+	 * struct ibss_peer_lost - Data for EVENT_IBSS_PEER_LOST
+	 */
+	struct ibss_peer_lost {
+		u8 peer[ETH_ALEN];
+	} ibss_peer_lost;
+
+	/**
+	 * struct driver_gtk_rekey - Data for EVENT_DRIVER_GTK_REKEY
+	 */
+	struct driver_gtk_rekey {
+		const u8 *bssid;
+		const u8 *replay_ctr;
+	} driver_gtk_rekey;
+
+	/**
+	 * struct client_poll - Data for EVENT_DRIVER_CLIENT_POLL_OK events
+	 * @addr: station address
+	 */
+	struct client_poll {
+		u8 addr[ETH_ALEN];
+	} client_poll;
+
+	/**
+	 * struct eapol_tx_status
+	 * @dst: Original destination
+	 * @data: Data starting with IEEE 802.1X header (!)
+	 * @data_len: Length of data
+	 * @ack: Indicates ack or lost frame
+	 * @link_id: MLD link id used to transmit the frame or -1 for non MLO
+	 *
+	 * This corresponds to hapd_send_eapol if the frame sent
+	 * there isn't just reported as EVENT_TX_STATUS.
+	 */
+	struct eapol_tx_status {
+		const u8 *dst;
+		const u8 *data;
+		int data_len;
+		int ack;
+		int link_id;
+	} eapol_tx_status;
+
+	/**
+	 * struct ch_switch
+	 * @count: Count until channel switch activates
+	 * @freq: Frequency of new channel in MHz
+	 * @ht_enabled: Whether this is an HT channel
+	 * @ch_offset: Secondary channel offset
+	 * @ch_width: Channel width
+	 * @cf1: Center frequency 1
+	 * @cf2: Center frequency 2
+	 * @link_id: Link ID of the MLO link
+	 * @punct_bitmap: Puncturing bitmap
+	 */
+	struct ch_switch {
+		int count;
+		int freq;
+		int ht_enabled;
+		int ch_offset;
+		enum chan_width ch_width;
+		int cf1;
+		int cf2;
+		int link_id;
+		u16 punct_bitmap;
+	} ch_switch;
+
+	/**
+	 * struct connect_failed - Data for EVENT_CONNECT_FAILED_REASON
+	 * @addr: Remote client address
+	 * @code: Reason code for connection failure
+	 */
+	struct connect_failed_reason {
+		u8 addr[ETH_ALEN];
+		enum {
+			MAX_CLIENT_REACHED,
+			BLOCKED_CLIENT
+		} code;
+	} connect_failed_reason;
+
+	/**
+	 * struct dfs_event - Data for radar detected events
+	 * @freq: Frequency of the channel in MHz
+	 */
+	struct dfs_event {
+		int freq;
+		int ht_enabled;
+		int chan_offset;
+		enum chan_width chan_width;
+		int cf1;
+		int cf2;
+	} dfs_event;
+
+	/**
+	 * survey_results - Survey result data for EVENT_SURVEY
+	 * @freq_filter: Requested frequency survey filter, 0 if request
+	 *	was for all survey data
+	 * @survey_list: Linked list of survey data (struct freq_survey)
+	 */
+	struct survey_results {
+		unsigned int freq_filter;
+		struct dl_list survey_list; /* struct freq_survey */
+	} survey_results;
+
+	/**
+	 * channel_list_changed - Data for EVENT_CHANNEL_LIST_CHANGED
+	 * @initiator: Initiator of the regulatory change
+	 * @type: Regulatory change type
+	 * @alpha2: Country code (or "" if not available)
+	 */
+	struct channel_list_changed {
+		enum reg_change_initiator initiator;
+		enum reg_type type;
+		char alpha2[3];
+	} channel_list_changed;
+
+	/**
+	 * freq_range - List of frequency ranges
+	 *
+	 * This is used as the data with EVENT_AVOID_FREQUENCIES.
+	 */
+	struct wpa_freq_range_list freq_range;
+
+	/**
+	 * struct mesh_peer
+	 *
+	 * @peer: Peer address
+	 * @ies: Beacon IEs
+	 * @ie_len: Length of @ies
+	 *
+	 * Notification of new candidate mesh peer.
+	 */
+	struct mesh_peer {
+		const u8 *peer;
+		const u8 *ies;
+		size_t ie_len;
+	} mesh_peer;
+
+	/**
+	 * struct acs_selected_channels - Data for EVENT_ACS_CHANNEL_SELECTED
+	 * @pri_freq: Selected primary frequency
+	 * @sec_freq: Selected secondary frequency
+	 * @edmg_channel: Selected EDMG channel
+	 * @vht_seg0_center_ch: VHT mode Segment0 center channel
+	 *	The value is the index of the channel center frequency for
+	 *	20 MHz, 40 MHz, and 80 MHz channels. The value is the center
+	 *	frequency index of the primary 80 MHz segment for 160 MHz and
+	 *	80+80 MHz channels.
+	 * @vht_seg1_center_ch: VHT mode Segment1 center channel
+	 *	The value is zero for 20 MHz, 40 MHz, and 80 MHz channels. The
+	 *	value is the index of the channel center frequency for 160 MHz
+	 *	channels and the center frequency index of the secondary 80 MHz
+	 *	segment for 80+80 MHz channels.
+	 * @ch_width: Selected Channel width by driver. Driver may choose to
+	 *	change hostapd configured ACS channel width due driver internal
+	 *	channel restrictions.
+	 * hw_mode: Selected band (used with hw_mode=any)
+	 */
+	struct acs_selected_channels {
+		unsigned int pri_freq;
+		unsigned int sec_freq;
+		u8 edmg_channel;
+		u8 vht_seg0_center_ch;
+		u8 vht_seg1_center_ch;
+		u16 ch_width;
+		enum hostapd_hw_mode hw_mode;
+		u16 puncture_bitmap;
+	} acs_selected_channels;
+
+	/**
+	 * struct p2p_lo_stop - Reason code for P2P Listen offload stop event
+	 * @reason_code: Reason for stopping offload
+	 *	P2P_LO_STOPPED_REASON_COMPLETE: Listen offload finished as
+	 *	scheduled.
+	 *	P2P_LO_STOPPED_REASON_RECV_STOP_CMD: Host requested offload to
+	 *	be stopped.
+	 *	P2P_LO_STOPPED_REASON_INVALID_PARAM: Invalid listen offload
+	 *	parameters.
+	 *	P2P_LO_STOPPED_REASON_NOT_SUPPORTED: Listen offload not
+	 *	supported by device.
+	 */
+	struct p2p_lo_stop {
+		enum {
+			P2P_LO_STOPPED_REASON_COMPLETE = 0,
+			P2P_LO_STOPPED_REASON_RECV_STOP_CMD,
+			P2P_LO_STOPPED_REASON_INVALID_PARAM,
+			P2P_LO_STOPPED_REASON_NOT_SUPPORTED,
+		} reason_code;
+	} p2p_lo_stop;
+
+	/* For EVENT_EXTERNAL_AUTH */
+	struct external_auth external_auth;
+
+	/**
+	 * struct sta_opmode - Station's operation mode change event
+	 * @addr: The station MAC address
+	 * @smps_mode: SMPS mode of the station
+	 * @chan_width: Channel width of the station
+	 * @rx_nss: RX_NSS of the station
+	 *
+	 * This is used as data with EVENT_STATION_OPMODE_CHANGED.
+	 */
+	struct sta_opmode {
+		const u8 *addr;
+		enum smps_mode smps_mode;
+		enum chan_width chan_width;
+		u8 rx_nss;
+	} sta_opmode;
+
+	/**
+	 * struct wds_sta_interface - Data for EVENT_WDS_STA_INTERFACE_STATUS.
+	 */
+	struct wds_sta_interface {
+		const u8 *sta_addr;
+		const char *ifname;
+		enum {
+			INTERFACE_ADDED,
+			INTERFACE_REMOVED
+		} istatus;
+	} wds_sta_interface;
+
+	/**
+	 * struct update_dh - Data for EVENT_UPDATE_DH
+	 */
+	struct update_dh {
+		const u8 *peer;
+		const u8 *ie;
+		size_t ie_len;
+	} update_dh;
+
+	/**
+	 * struct unprot_beacon - Data for EVENT_UNPROT_BEACON
+	 */
+	struct unprot_beacon {
+		const u8 *sa;
+	} unprot_beacon;
+
+	/**
+	 * struct bss_color_collision - Data for EVENT_BSS_COLOR_COLLISION
+	 */
+	struct bss_color_collision {
+		u64 bitmap;
+	} bss_color_collision;
+
+	/**
+	 * struct pasn_auth - Data for EVENT_PASN_AUTH
+	 */
+	struct pasn_auth pasn_auth;
+
+	/**
+	 * struct port_authorized - Data for EVENT_PORT_AUTHORIZED
+	 */
+	struct port_authorized {
+		const u8 *td_bitmap;
+		size_t td_bitmap_len;
+	} port_authorized;
+
+	/**
+	 * struct tid_link_map_info - Data for EVENT_TID_LINK_MAP
+	 */
+	struct tid_link_map_info {
+		bool default_map;
+		u8 valid_links;
+		struct t2lm_mapping t2lmap[MAX_NUM_MLD_LINKS];
+	} t2l_map_info;
+};
+
+/**
+ * wpa_supplicant_event - Report a driver event for wpa_supplicant
+ * @ctx: Context pointer (wpa_s); this is the ctx variable registered
+ *	with struct wpa_driver_ops::init()
+ * @event: event type (defined above)
+ * @data: possible extra data for the event
+ *
+ * Driver wrapper code should call this function whenever an event is received
+ * from the driver.
+ */
+extern void (*wpa_supplicant_event)(void *ctx, enum wpa_event_type event,
+				    union wpa_event_data *data);
+
+/**
+ * wpa_supplicant_event_global - Report a driver event for wpa_supplicant
+ * @ctx: Context pointer (wpa_s); this is the ctx variable registered
+ *	with struct wpa_driver_ops::init()
+ * @event: event type (defined above)
+ * @data: possible extra data for the event
+ *
+ * Same as wpa_supplicant_event(), but we search for the interface in
+ * wpa_global.
+ */
+extern void (*wpa_supplicant_event_global)(void *ctx, enum wpa_event_type event,
+				 union wpa_event_data *data);
+
+/*
+ * The following inline functions are provided for convenience to simplify
+ * event indication for some of the common events.
+ */
+
+static inline void drv_event_assoc(void *ctx, const u8 *addr, const u8 *ie,
+				   size_t ielen, int reassoc)
+{
+	union wpa_event_data event;
+	os_memset(&event, 0, sizeof(event));
+	event.assoc_info.reassoc = reassoc;
+	event.assoc_info.req_ies = ie;
+	event.assoc_info.req_ies_len = ielen;
+	event.assoc_info.addr = addr;
+	wpa_supplicant_event(ctx, EVENT_ASSOC, &event);
+}
+
+static inline void drv_event_disassoc(void *ctx, const u8 *addr)
+{
+	union wpa_event_data event;
+	os_memset(&event, 0, sizeof(event));
+	event.disassoc_info.addr = addr;
+	wpa_supplicant_event(ctx, EVENT_DISASSOC, &event);
+}
+
+static inline void drv_event_eapol_rx(void *ctx, const u8 *src, const u8 *data,
+				      size_t data_len)
+{
+	union wpa_event_data event;
+	os_memset(&event, 0, sizeof(event));
+	event.eapol_rx.src = src;
+	event.eapol_rx.data = data;
+	event.eapol_rx.data_len = data_len;
+	event.eapol_rx.encrypted = FRAME_ENCRYPTION_UNKNOWN;
+	event.eapol_rx.link_id = -1;
+	wpa_supplicant_event(ctx, EVENT_EAPOL_RX, &event);
+}
+
+static inline void drv_event_eapol_rx2(void *ctx, const u8 *src, const u8 *data,
+				       size_t data_len,
+				       enum frame_encryption encrypted,
+				       int link_id)
+{
+	union wpa_event_data event;
+	os_memset(&event, 0, sizeof(event));
+	event.eapol_rx.src = src;
+	event.eapol_rx.data = data;
+	event.eapol_rx.data_len = data_len;
+	event.eapol_rx.encrypted = encrypted;
+	event.eapol_rx.link_id = link_id;
+	wpa_supplicant_event(ctx, EVENT_EAPOL_RX, &event);
+}
+
+/* driver_common.c */
+void wpa_scan_results_free(struct wpa_scan_results *res);
+
+/* Convert wpa_event_type to a string for logging */
+const char * event_to_string(enum wpa_event_type event);
+
+/* Convert chan_width to a string for logging and control interfaces */
+const char * channel_width_to_string(enum chan_width width);
+
+int channel_width_to_int(enum chan_width width);
+
+int ht_supported(const struct hostapd_hw_modes *mode);
+int vht_supported(const struct hostapd_hw_modes *mode);
+bool he_supported(const struct hostapd_hw_modes *hw_mode,
+		  enum ieee80211_op_mode op_mode);
+
+struct wowlan_triggers *
+wpa_get_wowlan_triggers(const char *wowlan_triggers,
+			const struct wpa_driver_capa *capa);
+/* Convert driver flag to string */
+const char * driver_flag_to_string(u64 flag);
+const char * driver_flag2_to_string(u64 flag2);
+
+/* NULL terminated array of linked in driver wrappers */
+extern const struct wpa_driver_ops *const wpa_drivers[];
+
+
+/* Available drivers */
+
+#ifdef CONFIG_DRIVER_WEXT
+extern const struct wpa_driver_ops wpa_driver_wext_ops; /* driver_wext.c */
+#endif /* CONFIG_DRIVER_WEXT */
+#ifdef CONFIG_DRIVER_NL80211
+/* driver_nl80211.c */
+extern const struct wpa_driver_ops wpa_driver_nl80211_ops;
+#endif /* CONFIG_DRIVER_NL80211 */
+#ifdef CONFIG_DRIVER_HOSTAP
+extern const struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */
+#endif /* CONFIG_DRIVER_HOSTAP */
+#ifdef CONFIG_DRIVER_BSD
+extern const struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */
+#endif /* CONFIG_DRIVER_BSD */
+#ifdef CONFIG_DRIVER_OPENBSD
+/* driver_openbsd.c */
+extern const struct wpa_driver_ops wpa_driver_openbsd_ops;
+#endif /* CONFIG_DRIVER_OPENBSD */
+#ifdef CONFIG_DRIVER_NDIS
+extern struct wpa_driver_ops wpa_driver_ndis_ops; /* driver_ndis.c */
+#endif /* CONFIG_DRIVER_NDIS */
+#ifdef CONFIG_DRIVER_WIRED
+extern const struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */
+#endif /* CONFIG_DRIVER_WIRED */
+#ifdef CONFIG_DRIVER_MACSEC_QCA
+/* driver_macsec_qca.c */
+extern const struct wpa_driver_ops wpa_driver_macsec_qca_ops;
+#endif /* CONFIG_DRIVER_MACSEC_QCA */
+#ifdef CONFIG_DRIVER_MACSEC_LINUX
+/* driver_macsec_linux.c */
+extern const struct wpa_driver_ops wpa_driver_macsec_linux_ops;
+#endif /* CONFIG_DRIVER_MACSEC_LINUX */
+#ifdef CONFIG_DRIVER_ROBOSWITCH
+/* driver_roboswitch.c */
+extern const struct wpa_driver_ops wpa_driver_roboswitch_ops;
+#endif /* CONFIG_DRIVER_ROBOSWITCH */
+#ifdef CONFIG_DRIVER_ATHEROS
+/* driver_atheros.c */
+extern const struct wpa_driver_ops wpa_driver_atheros_ops;
+#endif /* CONFIG_DRIVER_ATHEROS */
+#ifdef CONFIG_DRIVER_NONE
+extern const struct wpa_driver_ops wpa_driver_none_ops; /* driver_none.c */
+#endif /* CONFIG_DRIVER_NONE */
+
+#endif /* DRIVER_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_atheros.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_atheros.c
new file mode 100644
index 0000000..08ff915
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_atheros.c
@@ -0,0 +1,2284 @@
+/*
+ * hostapd / Driver interaction with Atheros driver
+ * Copyright (c) 2004, Sam Leffler <sam@errno.com>
+ * Copyright (c) 2004, Video54 Technologies
+ * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <net/if.h>
+#include <sys/ioctl.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "l2_packet/l2_packet.h"
+
+#include "common.h"
+#ifndef _BYTE_ORDER
+#ifdef WORDS_BIGENDIAN
+#define _BYTE_ORDER _BIG_ENDIAN
+#else
+#define _BYTE_ORDER _LITTLE_ENDIAN
+#endif
+#endif /* _BYTE_ORDER */
+
+/*
+ * Note, the ATH_WPS_IE setting must match with the driver build.. If the
+ * driver does not include this, the IEEE80211_IOCTL_GETWPAIE ioctl will fail.
+ */
+#define ATH_WPS_IE
+
+#include "ieee80211_external.h"
+
+/* Avoid conflicting definition from the driver header files with
+ * common/wpa_common.h */
+#undef WPA_OUI_TYPE
+
+
+#ifdef CONFIG_WPS
+#include <netpacket/packet.h>
+#endif /* CONFIG_WPS */
+
+#ifndef ETH_P_80211_RAW
+#define ETH_P_80211_RAW 0x0019
+#endif
+
+#include "linux_wext.h"
+
+#include "driver.h"
+#include "eloop.h"
+#include "priv_netlink.h"
+#include "l2_packet/l2_packet.h"
+#include "common/ieee802_11_defs.h"
+#include "netlink.h"
+#include "linux_ioctl.h"
+
+
+struct atheros_driver_data {
+	struct hostapd_data *hapd;		/* back pointer */
+
+	char	iface[IFNAMSIZ + 1];
+	int     ifindex;
+	struct l2_packet_data *sock_xmit;	/* raw packet xmit socket */
+	struct l2_packet_data *sock_recv;	/* raw packet recv socket */
+	int	ioctl_sock;			/* socket for ioctl() use */
+	struct netlink_data *netlink;
+	int	we_version;
+	int fils_en;			/* FILS enable/disable in driver */
+	u8	acct_mac[ETH_ALEN];
+	struct hostap_sta_driver_data acct_data;
+
+	struct l2_packet_data *sock_raw; /* raw 802.11 management frames */
+	struct wpabuf *wpa_ie;
+	struct wpabuf *wps_beacon_ie;
+	struct wpabuf *wps_probe_resp_ie;
+	u8	own_addr[ETH_ALEN];
+};
+
+static int atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+			      u16 reason_code, int link_id);
+static int atheros_set_privacy(void *priv, int enabled);
+
+static const char * athr_get_ioctl_name(int op)
+{
+	switch (op) {
+	case IEEE80211_IOCTL_SETPARAM:
+		return "SETPARAM";
+	case IEEE80211_IOCTL_GETPARAM:
+		return "GETPARAM";
+	case IEEE80211_IOCTL_SETKEY:
+		return "SETKEY";
+	case IEEE80211_IOCTL_SETWMMPARAMS:
+		return "SETWMMPARAMS";
+	case IEEE80211_IOCTL_DELKEY:
+		return "DELKEY";
+	case IEEE80211_IOCTL_GETWMMPARAMS:
+		return "GETWMMPARAMS";
+	case IEEE80211_IOCTL_SETMLME:
+		return "SETMLME";
+	case IEEE80211_IOCTL_GETCHANINFO:
+		return "GETCHANINFO";
+	case IEEE80211_IOCTL_SETOPTIE:
+		return "SETOPTIE";
+	case IEEE80211_IOCTL_GETOPTIE:
+		return "GETOPTIE";
+	case IEEE80211_IOCTL_ADDMAC:
+		return "ADDMAC";
+	case IEEE80211_IOCTL_DELMAC:
+		return "DELMAC";
+	case IEEE80211_IOCTL_GETCHANLIST:
+		return "GETCHANLIST";
+	case IEEE80211_IOCTL_SETCHANLIST:
+		return "SETCHANLIST";
+	case IEEE80211_IOCTL_KICKMAC:
+		return "KICKMAC";
+	case IEEE80211_IOCTL_CHANSWITCH:
+		return "CHANSWITCH";
+	case IEEE80211_IOCTL_GETMODE:
+		return "GETMODE";
+	case IEEE80211_IOCTL_SETMODE:
+		return "SETMODE";
+	case IEEE80211_IOCTL_GET_APPIEBUF:
+		return "GET_APPIEBUF";
+	case IEEE80211_IOCTL_SET_APPIEBUF:
+		return "SET_APPIEBUF";
+	case IEEE80211_IOCTL_SET_ACPARAMS:
+		return "SET_ACPARAMS";
+	case IEEE80211_IOCTL_FILTERFRAME:
+		return "FILTERFRAME";
+	case IEEE80211_IOCTL_SET_RTPARAMS:
+		return "SET_RTPARAMS";
+	case IEEE80211_IOCTL_SET_MEDENYENTRY:
+		return "SET_MEDENYENTRY";
+	case IEEE80211_IOCTL_GET_MACADDR:
+		return "GET_MACADDR";
+	case IEEE80211_IOCTL_SET_HBRPARAMS:
+		return "SET_HBRPARAMS";
+	case IEEE80211_IOCTL_SET_RXTIMEOUT:
+		return "SET_RXTIMEOUT";
+	case IEEE80211_IOCTL_STA_STATS:
+		return "STA_STATS";
+	case IEEE80211_IOCTL_GETWPAIE:
+		return "GETWPAIE";
+	default:
+		return "??";
+	}
+}
+
+
+static const char * athr_get_param_name(int op)
+{
+	switch (op) {
+	case IEEE80211_IOC_MCASTCIPHER:
+		return "MCASTCIPHER";
+	case IEEE80211_PARAM_MCASTKEYLEN:
+		return "MCASTKEYLEN";
+	case IEEE80211_PARAM_UCASTCIPHERS:
+		return "UCASTCIPHERS";
+	case IEEE80211_PARAM_KEYMGTALGS:
+		return "KEYMGTALGS";
+	case IEEE80211_PARAM_RSNCAPS:
+		return "RSNCAPS";
+	case IEEE80211_PARAM_WPA:
+		return "WPA";
+	case IEEE80211_PARAM_AUTHMODE:
+		return "AUTHMODE";
+	case IEEE80211_PARAM_PRIVACY:
+		return "PRIVACY";
+	case IEEE80211_PARAM_COUNTERMEASURES:
+		return "COUNTERMEASURES";
+	default:
+		return "??";
+	}
+}
+
+
+#ifdef CONFIG_FILS
+static int
+get80211param(struct atheros_driver_data *drv, int op, int *data)
+{
+	struct iwreq iwr;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+	iwr.u.mode = op;
+
+	if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_GETPARAM, &iwr) < 0)
+		return -1;
+
+	*data = iwr.u.mode;
+	return 0;
+}
+#endif /* CONFIG_FILS */
+
+
+static int
+set80211priv(struct atheros_driver_data *drv, int op, void *data, int len)
+{
+	struct iwreq iwr;
+	int do_inline = len < IFNAMSIZ;
+
+	/* Certain ioctls must use the non-inlined method */
+	if (op == IEEE80211_IOCTL_SET_APPIEBUF ||
+	    op == IEEE80211_IOCTL_FILTERFRAME)
+		do_inline = 0;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+	if (do_inline) {
+		/*
+		 * Argument data fits inline; put it there.
+		 */
+		os_memcpy(iwr.u.name, data, len);
+	} else {
+		/*
+		 * Argument data too big for inline transfer; setup a
+		 * parameter block instead; the kernel will transfer
+		 * the data for the driver.
+		 */
+		iwr.u.data.pointer = data;
+		iwr.u.data.length = len;
+	}
+
+	if (ioctl(drv->ioctl_sock, op, &iwr) < 0) {
+		wpa_printf(MSG_DEBUG, "atheros: %s: %s: ioctl op=0x%x "
+			   "(%s) len=%d failed: %d (%s)",
+			   __func__, drv->iface, op,
+			   athr_get_ioctl_name(op),
+			   len, errno, strerror(errno));
+		return -1;
+	}
+	return 0;
+}
+
+static int
+set80211param(struct atheros_driver_data *drv, int op, int arg)
+{
+	struct iwreq iwr;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+	iwr.u.mode = op;
+	os_memcpy(iwr.u.name + sizeof(__u32), &arg, sizeof(arg));
+
+	if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) {
+		wpa_printf(MSG_INFO,
+			   "%s: %s: Failed to set parameter (op %d (%s) arg %d): ioctl[IEEE80211_IOCTL_SETPARAM]: %s",
+			   __func__, drv->iface, op, athr_get_param_name(op),
+			   arg, strerror(errno));
+		return -1;
+	}
+	return 0;
+}
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static const char *
+ether_sprintf(const u8 *addr)
+{
+	static char buf[sizeof(MACSTR)];
+
+	if (addr != NULL)
+		os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
+	else
+		os_snprintf(buf, sizeof(buf), MACSTR, 0, 0, 0, 0, 0, 0);
+	return buf;
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+/*
+ * Configure WPA parameters.
+ */
+static int
+atheros_configure_wpa(struct atheros_driver_data *drv,
+		      struct wpa_bss_params *params)
+{
+	int v;
+
+	switch (params->wpa_group) {
+	case WPA_CIPHER_CCMP:
+		v = IEEE80211_CIPHER_AES_CCM;
+		break;
+#ifdef ATH_GCM_SUPPORT
+	case WPA_CIPHER_CCMP_256:
+		v = IEEE80211_CIPHER_AES_CCM_256;
+		break;
+	case WPA_CIPHER_GCMP:
+		v = IEEE80211_CIPHER_AES_GCM;
+		break;
+	case WPA_CIPHER_GCMP_256:
+		v = IEEE80211_CIPHER_AES_GCM_256;
+		break;
+#endif /* ATH_GCM_SUPPORT */
+	case WPA_CIPHER_TKIP:
+		v = IEEE80211_CIPHER_TKIP;
+		break;
+	case WPA_CIPHER_WEP104:
+		v = IEEE80211_CIPHER_WEP;
+		break;
+	case WPA_CIPHER_WEP40:
+		v = IEEE80211_CIPHER_WEP;
+		break;
+	case WPA_CIPHER_NONE:
+		v = IEEE80211_CIPHER_NONE;
+		break;
+	default:
+		wpa_printf(MSG_ERROR, "Unknown group key cipher %u",
+			   params->wpa_group);
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "%s: group key cipher=%d", __func__, v);
+	if (set80211param(drv, IEEE80211_PARAM_MCASTCIPHER, v)) {
+		wpa_printf(MSG_INFO, "Unable to set group key cipher to %u", v);
+		return -1;
+	}
+	if (v == IEEE80211_CIPHER_WEP) {
+		/* key length is done only for specific ciphers */
+		v = (params->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5);
+		if (set80211param(drv, IEEE80211_PARAM_MCASTKEYLEN, v)) {
+			wpa_printf(MSG_INFO,
+				   "Unable to set group key length to %u", v);
+			return -1;
+		}
+	}
+
+	v = 0;
+	if (params->wpa_pairwise & WPA_CIPHER_CCMP)
+		v |= 1<<IEEE80211_CIPHER_AES_CCM;
+#ifdef ATH_GCM_SUPPORT
+	if (params->wpa_pairwise & WPA_CIPHER_CCMP_256)
+		v |= 1<<IEEE80211_CIPHER_AES_CCM_256;
+	if (params->wpa_pairwise & WPA_CIPHER_GCMP)
+		v |= 1<<IEEE80211_CIPHER_AES_GCM;
+	if (params->wpa_pairwise & WPA_CIPHER_GCMP_256)
+		v |= 1<<IEEE80211_CIPHER_AES_GCM_256;
+#endif /* ATH_GCM_SUPPORT */
+	if (params->wpa_pairwise & WPA_CIPHER_TKIP)
+		v |= 1<<IEEE80211_CIPHER_TKIP;
+	if (params->wpa_pairwise & WPA_CIPHER_NONE)
+		v |= 1<<IEEE80211_CIPHER_NONE;
+	wpa_printf(MSG_DEBUG, "%s: pairwise key ciphers=0x%x", __func__, v);
+	if (set80211param(drv, IEEE80211_PARAM_UCASTCIPHERS, v)) {
+		wpa_printf(MSG_INFO,
+			   "Unable to set pairwise key ciphers to 0x%x", v);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "%s: key management algorithms=0x%x",
+		   __func__, params->wpa_key_mgmt);
+	if (set80211param(drv, IEEE80211_PARAM_KEYMGTALGS,
+			  params->wpa_key_mgmt)) {
+		wpa_printf(MSG_INFO,
+			   "Unable to set key management algorithms to 0x%x",
+			   params->wpa_key_mgmt);
+		return -1;
+	}
+
+	v = 0;
+	if (params->rsn_preauth)
+		v |= BIT(0);
+	if (params->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+		v |= BIT(7);
+		if (params->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
+			v |= BIT(6);
+	}
+
+	wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", __func__, v);
+	if (set80211param(drv, IEEE80211_PARAM_RSNCAPS, v)) {
+		wpa_printf(MSG_INFO, "Unable to set RSN capabilities to 0x%x",
+			   v);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "%s: enable WPA=0x%x", __func__, params->wpa);
+	if (set80211param(drv, IEEE80211_PARAM_WPA, params->wpa)) {
+		wpa_printf(MSG_INFO, "Unable to set WPA to %u", params->wpa);
+		return -1;
+	}
+	return 0;
+}
+
+static int
+atheros_set_ieee8021x(void *priv, struct wpa_bss_params *params)
+{
+	struct atheros_driver_data *drv = priv;
+
+	wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled);
+
+	if (!params->enabled) {
+		/* XXX restore state */
+		if (set80211param(priv, IEEE80211_PARAM_AUTHMODE,
+				  IEEE80211_AUTH_AUTO) < 0)
+			return -1;
+		/* IEEE80211_AUTH_AUTO ends up enabling Privacy; clear that */
+		return atheros_set_privacy(drv, 0);
+	}
+	if (!params->wpa && !params->ieee802_1x) {
+		wpa_printf(MSG_WARNING, "No 802.1X or WPA enabled!");
+		return -1;
+	}
+	if (params->wpa && atheros_configure_wpa(drv, params) != 0) {
+		wpa_printf(MSG_WARNING, "Error configuring WPA state!");
+		return -1;
+	}
+	if (set80211param(priv, IEEE80211_PARAM_AUTHMODE,
+		(params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) {
+		wpa_printf(MSG_WARNING, "Error enabling WPA/802.1X!");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+atheros_set_privacy(void *priv, int enabled)
+{
+	struct atheros_driver_data *drv = priv;
+
+	wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
+
+	return set80211param(drv, IEEE80211_PARAM_PRIVACY, enabled);
+}
+
+static int
+atheros_set_sta_authorized(void *priv, const u8 *addr, int authorized)
+{
+	struct atheros_driver_data *drv = priv;
+	struct ieee80211req_mlme mlme;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "%s: addr=%s authorized=%d",
+		   __func__, ether_sprintf(addr), authorized);
+
+	if (authorized)
+		mlme.im_op = IEEE80211_MLME_AUTHORIZE;
+	else
+		mlme.im_op = IEEE80211_MLME_UNAUTHORIZE;
+	mlme.im_reason = 0;
+	os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+	ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme));
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "%s: Failed to %sauthorize STA " MACSTR,
+			   __func__, authorized ? "" : "un", MAC2STR(addr));
+	}
+
+	return ret;
+}
+
+static int
+atheros_sta_set_flags(void *priv, const u8 *addr,
+		      unsigned int total_flags, unsigned int flags_or,
+		      unsigned int flags_and)
+{
+	/* For now, only support setting Authorized flag */
+	if (flags_or & WPA_STA_AUTHORIZED)
+		return atheros_set_sta_authorized(priv, addr, 1);
+	if (!(flags_and & WPA_STA_AUTHORIZED))
+		return atheros_set_sta_authorized(priv, addr, 0);
+	return 0;
+}
+
+static int
+atheros_del_key(void *priv, const u8 *addr, int key_idx)
+{
+	struct atheros_driver_data *drv = priv;
+	struct ieee80211req_del_key wk;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "%s: addr=%s key_idx=%d",
+		   __func__, ether_sprintf(addr), key_idx);
+
+	os_memset(&wk, 0, sizeof(wk));
+	if (addr != NULL) {
+		os_memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN);
+		wk.idk_keyix = (u8) IEEE80211_KEYIX_NONE;
+	} else {
+		wk.idk_keyix = key_idx;
+	}
+
+	ret = set80211priv(drv, IEEE80211_IOCTL_DELKEY, &wk, sizeof(wk));
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "%s: Failed to delete key (addr %s"
+			   " key_idx %d)", __func__, ether_sprintf(addr),
+			   key_idx);
+	}
+
+	return ret;
+}
+
+static int
+atheros_set_key(void *priv, struct wpa_driver_set_key_params *params)
+{
+	struct atheros_driver_data *drv = priv;
+	struct ieee80211req_key wk;
+	u_int8_t cipher;
+	int ret;
+	enum wpa_alg alg = params->alg;
+	const u8 *addr = params->addr;
+	int key_idx = params->key_idx;
+	int set_tx = params->set_tx;
+	const u8 *key = params->key;
+	size_t key_len = params->key_len;
+
+	if (alg == WPA_ALG_NONE)
+		return atheros_del_key(drv, addr, key_idx);
+
+	wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%s key_idx=%d",
+		   __func__, alg, ether_sprintf(addr), key_idx);
+
+	switch (alg) {
+	case WPA_ALG_WEP:
+		cipher = IEEE80211_CIPHER_WEP;
+		break;
+	case WPA_ALG_TKIP:
+		cipher = IEEE80211_CIPHER_TKIP;
+		break;
+	case WPA_ALG_CCMP:
+		cipher = IEEE80211_CIPHER_AES_CCM;
+		break;
+#ifdef ATH_GCM_SUPPORT
+	case WPA_ALG_CCMP_256:
+		cipher = IEEE80211_CIPHER_AES_CCM_256;
+		break;
+	case WPA_ALG_GCMP:
+		cipher = IEEE80211_CIPHER_AES_GCM;
+		break;
+	case WPA_ALG_GCMP_256:
+		cipher = IEEE80211_CIPHER_AES_GCM_256;
+		break;
+#endif /* ATH_GCM_SUPPORT */
+	case WPA_ALG_BIP_CMAC_128:
+		cipher = IEEE80211_CIPHER_AES_CMAC;
+		break;
+#ifdef ATH_GCM_SUPPORT
+	case WPA_ALG_BIP_CMAC_256:
+		cipher = IEEE80211_CIPHER_AES_CMAC_256;
+		break;
+	case WPA_ALG_BIP_GMAC_128:
+		cipher = IEEE80211_CIPHER_AES_GMAC;
+		break;
+	case WPA_ALG_BIP_GMAC_256:
+		cipher = IEEE80211_CIPHER_AES_GMAC_256;
+		break;
+#endif /* ATH_GCM_SUPPORT */
+	default:
+		wpa_printf(MSG_INFO, "%s: unknown/unsupported algorithm %d",
+			   __func__, alg);
+		return -1;
+	}
+
+	if (key_len > sizeof(wk.ik_keydata)) {
+		wpa_printf(MSG_INFO, "%s: key length %lu too big", __func__,
+			   (unsigned long) key_len);
+		return -3;
+	}
+
+	os_memset(&wk, 0, sizeof(wk));
+	wk.ik_type = cipher;
+	wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT;
+	if (addr == NULL || is_broadcast_ether_addr(addr)) {
+		os_memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
+		wk.ik_keyix = key_idx;
+		if (set_tx)
+			wk.ik_flags |= IEEE80211_KEY_DEFAULT;
+	} else {
+		os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
+		wk.ik_keyix = IEEE80211_KEYIX_NONE;
+	}
+	wk.ik_keylen = key_len;
+	os_memcpy(wk.ik_keydata, key, key_len);
+
+	ret = set80211priv(drv, IEEE80211_IOCTL_SETKEY, &wk, sizeof(wk));
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "%s: Failed to set key (addr %s"
+			   " key_idx %d alg %d key_len %lu set_tx %d)",
+			   __func__, ether_sprintf(wk.ik_macaddr), key_idx,
+			   alg, (unsigned long) key_len, set_tx);
+	}
+
+	return ret;
+}
+
+
+static int
+atheros_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
+		   int link_id, u8 *seq)
+{
+	struct atheros_driver_data *drv = priv;
+	struct ieee80211req_key wk;
+
+	wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d",
+		   __func__, ether_sprintf(addr), idx);
+
+	os_memset(&wk, 0, sizeof(wk));
+	if (addr == NULL)
+		os_memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
+	else
+		os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
+	wk.ik_keyix = idx;
+
+	if (set80211priv(drv, IEEE80211_IOCTL_GETKEY, &wk, sizeof(wk))) {
+		wpa_printf(MSG_DEBUG, "%s: Failed to get encryption data "
+			   "(addr " MACSTR " key_idx %d)",
+			   __func__, MAC2STR(wk.ik_macaddr), idx);
+		return -1;
+	}
+
+#ifdef WORDS_BIGENDIAN
+	{
+		/*
+		 * wk.ik_keytsc is in host byte order (big endian), need to
+		 * swap it to match with the byte order used in WPA.
+		 */
+		int i;
+#ifndef WPA_KEY_RSC_LEN
+#define WPA_KEY_RSC_LEN 8
+#endif
+		u8 tmp[WPA_KEY_RSC_LEN];
+		os_memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc));
+		for (i = 0; i < WPA_KEY_RSC_LEN; i++) {
+			seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1];
+		}
+	}
+#else /* WORDS_BIGENDIAN */
+	os_memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc));
+#endif /* WORDS_BIGENDIAN */
+	return 0;
+}
+
+
+static int
+atheros_flush(void *priv)
+{
+	u8 allsta[IEEE80211_ADDR_LEN];
+	os_memset(allsta, 0xff, IEEE80211_ADDR_LEN);
+	return atheros_sta_deauth(priv, NULL, allsta,
+				  IEEE80211_REASON_AUTH_LEAVE, -1);
+}
+
+
+static int
+atheros_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data,
+			     const u8 *addr)
+{
+	struct atheros_driver_data *drv = priv;
+	struct ieee80211req_sta_stats stats;
+
+	os_memset(data, 0, sizeof(*data));
+
+	/*
+	 * Fetch statistics for station from the system.
+	 */
+	os_memset(&stats, 0, sizeof(stats));
+	os_memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN);
+	if (set80211priv(drv, IEEE80211_IOCTL_STA_STATS,
+			 &stats, sizeof(stats))) {
+		wpa_printf(MSG_DEBUG, "%s: Failed to fetch STA stats (addr "
+			   MACSTR ")", __func__, MAC2STR(addr));
+		if (os_memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) {
+			os_memcpy(data, &drv->acct_data, sizeof(*data));
+			return 0;
+		}
+
+		wpa_printf(MSG_INFO,
+			   "Failed to get station stats information element");
+		return -1;
+	}
+
+	data->rx_packets = stats.is_stats.ns_rx_data;
+	data->rx_bytes = stats.is_stats.ns_rx_bytes;
+	data->tx_packets = stats.is_stats.ns_tx_data;
+	data->tx_bytes = stats.is_stats.ns_tx_bytes;
+	return 0;
+}
+
+
+static int
+atheros_sta_clear_stats(void *priv, const u8 *addr)
+{
+	struct atheros_driver_data *drv = priv;
+	struct ieee80211req_mlme mlme;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "%s: addr=%s", __func__, ether_sprintf(addr));
+
+	mlme.im_op = IEEE80211_MLME_CLEAR_STATS;
+	os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+	ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme,
+			   sizeof(mlme));
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "%s: Failed to clear STA stats (addr "
+			   MACSTR ")", __func__, MAC2STR(addr));
+	}
+
+	return ret;
+}
+
+
+static int
+atheros_set_opt_ie(void *priv, const u8 *ie, size_t ie_len)
+{
+	struct atheros_driver_data *drv = priv;
+	u8 buf[512];
+	struct ieee80211req_getset_appiebuf *app_ie;
+
+	wpa_printf(MSG_DEBUG, "%s buflen = %lu", __func__,
+		   (unsigned long) ie_len);
+	wpa_hexdump(MSG_DEBUG, "atheros: set_generic_elem", ie, ie_len);
+
+	wpabuf_free(drv->wpa_ie);
+	if (ie)
+		drv->wpa_ie = wpabuf_alloc_copy(ie, ie_len);
+	else
+		drv->wpa_ie = NULL;
+
+	app_ie = (struct ieee80211req_getset_appiebuf *) buf;
+	if (ie)
+		os_memcpy(&(app_ie->app_buf[0]), ie, ie_len);
+	app_ie->app_buflen = ie_len;
+
+	app_ie->app_frmtype = IEEE80211_APPIE_FRAME_BEACON;
+
+	/* append WPS IE for Beacon */
+	if (drv->wps_beacon_ie != NULL) {
+		os_memcpy(&(app_ie->app_buf[ie_len]),
+			  wpabuf_head(drv->wps_beacon_ie),
+			  wpabuf_len(drv->wps_beacon_ie));
+		app_ie->app_buflen = ie_len + wpabuf_len(drv->wps_beacon_ie);
+	}
+	wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF(Beacon)",
+		    app_ie->app_buf, app_ie->app_buflen);
+	set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, app_ie,
+		     sizeof(struct ieee80211req_getset_appiebuf) +
+		     app_ie->app_buflen);
+
+	/* append WPS IE for Probe Response */
+	app_ie->app_frmtype = IEEE80211_APPIE_FRAME_PROBE_RESP;
+	if (drv->wps_probe_resp_ie != NULL) {
+		os_memcpy(&(app_ie->app_buf[ie_len]),
+			  wpabuf_head(drv->wps_probe_resp_ie),
+			  wpabuf_len(drv->wps_probe_resp_ie));
+		app_ie->app_buflen = ie_len +
+			wpabuf_len(drv->wps_probe_resp_ie);
+	} else
+		app_ie->app_buflen = ie_len;
+	wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF(ProbeResp)",
+		    app_ie->app_buf, app_ie->app_buflen);
+	set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, app_ie,
+		     sizeof(struct ieee80211req_getset_appiebuf) +
+		     app_ie->app_buflen);
+	return 0;
+}
+
+static int
+atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+		   u16 reason_code, int link_id)
+{
+	struct atheros_driver_data *drv = priv;
+	struct ieee80211req_mlme mlme;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d",
+		   __func__, ether_sprintf(addr), reason_code);
+
+	mlme.im_op = IEEE80211_MLME_DEAUTH;
+	mlme.im_reason = reason_code;
+	os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+	ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme));
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "%s: Failed to deauth STA (addr " MACSTR
+			   " reason %d)",
+			   __func__, MAC2STR(addr), reason_code);
+	}
+
+	return ret;
+}
+
+static int
+atheros_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
+		     u16 reason_code)
+{
+	struct atheros_driver_data *drv = priv;
+	struct ieee80211req_mlme mlme;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d",
+		   __func__, ether_sprintf(addr), reason_code);
+
+	mlme.im_op = IEEE80211_MLME_DISASSOC;
+	mlme.im_reason = reason_code;
+	os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+	ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme));
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "%s: Failed to disassoc STA (addr "
+			   MACSTR " reason %d)",
+			   __func__, MAC2STR(addr), reason_code);
+	}
+
+	return ret;
+}
+
+static int atheros_set_qos_map(void *ctx, const u8 *qos_map_set,
+			       u8 qos_map_set_len)
+{
+#ifdef CONFIG_ATHEROS_QOS_MAP
+	struct atheros_driver_data *drv = ctx;
+	struct ieee80211req_athdbg req;
+	struct ieee80211_qos_map *qos_map = &req.data.qos_map;
+	struct iwreq iwr;
+	int i, up_start;
+
+	if (qos_map_set_len < 16 || qos_map_set_len > 58 ||
+	    qos_map_set_len & 1) {
+		wpa_printf(MSG_ERROR, "Invalid QoS Map");
+		return -1;
+	} else {
+		os_memset(&req, 0, sizeof(struct ieee80211req_athdbg));
+		req.cmd = IEEE80211_DBGREQ_SETQOSMAPCONF;
+		os_memset(&iwr, 0, sizeof(iwr));
+		os_strlcpy(iwr.ifr_name, drv->iface, sizeof(iwr.ifr_name));
+		iwr.u.data.pointer = (void *) &req;
+		iwr.u.data.length = sizeof(struct ieee80211req_athdbg);
+	}
+
+	qos_map->valid = 1;
+	qos_map->num_dscp_except = (qos_map_set_len - 16) / 2;
+	if (qos_map->num_dscp_except) {
+		for (i = 0; i < qos_map->num_dscp_except; i++) {
+			qos_map->dscp_exception[i].dscp	= qos_map_set[i * 2];
+			qos_map->dscp_exception[i].up =	qos_map_set[i * 2 + 1];
+		}
+	}
+
+	up_start = qos_map_set_len - 16;
+	for (i = 0; i < IEEE80211_MAX_QOS_UP_RANGE; i++) {
+		qos_map->up[i].low = qos_map_set[up_start + (i * 2)];
+		qos_map->up[i].high = qos_map_set[up_start + (i * 2) + 1];
+	}
+
+	if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_DBGREQ, &iwr) < 0) {
+		wpa_printf(MSG_ERROR,
+			   "%s: %s: Failed to set QoS Map: ioctl[IEEE80211_IOCTL_DBGREQ]: %s",
+			   __func__, drv->iface, strerror(errno));
+		return -1;
+	}
+#endif /* CONFIG_ATHEROS_QOS_MAP */
+
+	return 0;
+}
+
+
+static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
+				size_t len)
+{
+	struct atheros_driver_data *drv = ctx;
+	const struct ieee80211_mgmt *mgmt;
+	union wpa_event_data event;
+	u16 fc, stype;
+	int ielen;
+	const u8 *iebuf;
+
+	if (len < IEEE80211_HDRLEN)
+		return;
+
+	mgmt = (const struct ieee80211_mgmt *) buf;
+
+	fc = le_to_host16(mgmt->frame_control);
+
+	if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT)
+		return;
+
+	stype = WLAN_FC_GET_STYPE(fc);
+
+	wpa_printf(MSG_DEBUG, "%s: subtype 0x%x len %d", __func__, stype,
+		   (int) len);
+
+	if (stype == WLAN_FC_STYPE_PROBE_REQ) {
+		if (len < IEEE80211_HDRLEN)
+			return;
+
+		os_memset(&event, 0, sizeof(event));
+		event.rx_probe_req.sa = mgmt->sa;
+		event.rx_probe_req.da = mgmt->da;
+		event.rx_probe_req.bssid = mgmt->bssid;
+		event.rx_probe_req.ie = buf + IEEE80211_HDRLEN;
+		event.rx_probe_req.ie_len = len - IEEE80211_HDRLEN;
+		wpa_supplicant_event(drv->hapd, EVENT_RX_PROBE_REQ, &event);
+		return;
+	}
+
+	if (stype == WLAN_FC_STYPE_ACTION &&
+	    (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) == 0 ||
+	     is_broadcast_ether_addr(mgmt->bssid))) {
+		os_memset(&event, 0, sizeof(event));
+		event.rx_mgmt.frame = buf;
+		event.rx_mgmt.frame_len = len;
+		wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event);
+		return;
+	}
+
+	if (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "%s: BSSID does not match - ignore",
+			   __func__);
+		return;
+	}
+
+	switch (stype) {
+	case WLAN_FC_STYPE_ASSOC_REQ:
+		if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req))
+			break;
+		ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req));
+		iebuf = mgmt->u.assoc_req.variable;
+		drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 0);
+		break;
+	case WLAN_FC_STYPE_REASSOC_REQ:
+		if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req))
+			break;
+		ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req));
+		iebuf = mgmt->u.reassoc_req.variable;
+		drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 1);
+		break;
+	case WLAN_FC_STYPE_AUTH:
+		if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth))
+			break;
+		os_memset(&event, 0, sizeof(event));
+		if (le_to_host16(mgmt->u.auth.auth_alg) == WLAN_AUTH_SAE) {
+			event.rx_mgmt.frame = buf;
+			event.rx_mgmt.frame_len = len;
+			wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event);
+			break;
+		}
+		os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN);
+		os_memcpy(event.auth.bssid, mgmt->bssid, ETH_ALEN);
+		event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg);
+		event.auth.status_code =
+			le_to_host16(mgmt->u.auth.status_code);
+		event.auth.auth_transaction =
+			le_to_host16(mgmt->u.auth.auth_transaction);
+		event.auth.ies = mgmt->u.auth.variable;
+		event.auth.ies_len = len - IEEE80211_HDRLEN -
+			sizeof(mgmt->u.auth);
+		wpa_supplicant_event(drv->hapd, EVENT_AUTH, &event);
+		break;
+	default:
+		break;
+	}
+}
+
+
+static int atheros_receive_pkt(struct atheros_driver_data *drv)
+{
+	int ret = 0;
+	struct ieee80211req_set_filter filt;
+
+	wpa_printf(MSG_DEBUG, "%s Enter", __func__);
+	filt.app_filterype = 0;
+#ifdef CONFIG_WPS
+	filt.app_filterype |= IEEE80211_FILTER_TYPE_PROBE_REQ;
+#endif /* CONFIG_WPS */
+	filt.app_filterype |= (IEEE80211_FILTER_TYPE_ASSOC_REQ |
+			       IEEE80211_FILTER_TYPE_AUTH |
+			       IEEE80211_FILTER_TYPE_ACTION);
+#ifdef CONFIG_WNM
+	filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION;
+#endif /* CONFIG_WNM */
+#ifdef CONFIG_HS20
+	filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION;
+#endif /* CONFIG_HS20 */
+	if (filt.app_filterype) {
+		ret = set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt,
+				   sizeof(struct ieee80211req_set_filter));
+		if (ret)
+			return ret;
+	}
+
+#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_FILS)
+	drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW,
+				       atheros_raw_receive, drv, 1);
+	if (drv->sock_raw == NULL)
+		return -1;
+#endif /* CONFIG_WPS || CONFIG_IEEE80211R || CONFIG_FILS */
+	return ret;
+}
+
+static int atheros_reset_appfilter(struct atheros_driver_data *drv)
+{
+	struct ieee80211req_set_filter filt;
+	filt.app_filterype = 0;
+	return set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt,
+			    sizeof(struct ieee80211req_set_filter));
+}
+
+#ifdef CONFIG_WPS
+static int
+atheros_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype)
+{
+	struct atheros_driver_data *drv = priv;
+	u8 buf[512];
+	struct ieee80211req_getset_appiebuf *beac_ie;
+
+	wpa_printf(MSG_DEBUG, "%s buflen = %lu frametype=%u", __func__,
+		   (unsigned long) len, frametype);
+	wpa_hexdump(MSG_DEBUG, "atheros: IE", ie, len);
+
+	beac_ie = (struct ieee80211req_getset_appiebuf *) buf;
+	beac_ie->app_frmtype = frametype;
+	beac_ie->app_buflen = len;
+	if (ie)
+		os_memcpy(&(beac_ie->app_buf[0]), ie, len);
+
+	/* append the WPA/RSN IE if it is set already */
+	if (((frametype == IEEE80211_APPIE_FRAME_BEACON) ||
+	     (frametype == IEEE80211_APPIE_FRAME_PROBE_RESP)) &&
+	    (drv->wpa_ie != NULL)) {
+		wpa_hexdump_buf(MSG_DEBUG, "atheros: Append WPA/RSN IE",
+				drv->wpa_ie);
+		os_memcpy(&(beac_ie->app_buf[len]), wpabuf_head(drv->wpa_ie),
+			  wpabuf_len(drv->wpa_ie));
+		beac_ie->app_buflen += wpabuf_len(drv->wpa_ie);
+	}
+
+	wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF",
+		    beac_ie->app_buf, beac_ie->app_buflen);
+	return set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, beac_ie,
+			    sizeof(struct ieee80211req_getset_appiebuf) +
+			    beac_ie->app_buflen);
+}
+
+static int
+atheros_set_ap_wps_ie(void *priv, const struct wpabuf *beacon,
+		      const struct wpabuf *proberesp,
+		      const struct wpabuf *assocresp)
+{
+	struct atheros_driver_data *drv = priv;
+
+	wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - beacon", beacon);
+	wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - proberesp",
+			proberesp);
+	wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - assocresp",
+			assocresp);
+	wpabuf_free(drv->wps_beacon_ie);
+	drv->wps_beacon_ie = beacon ? wpabuf_dup(beacon) : NULL;
+	wpabuf_free(drv->wps_probe_resp_ie);
+	drv->wps_probe_resp_ie = proberesp ? wpabuf_dup(proberesp) : NULL;
+
+	atheros_set_wps_ie(priv, assocresp ? wpabuf_head(assocresp) : NULL,
+			   assocresp ? wpabuf_len(assocresp) : 0,
+			   IEEE80211_APPIE_FRAME_ASSOC_RESP);
+	if (atheros_set_wps_ie(priv, beacon ? wpabuf_head(beacon) : NULL,
+			       beacon ? wpabuf_len(beacon) : 0,
+			       IEEE80211_APPIE_FRAME_BEACON))
+		return -1;
+	return atheros_set_wps_ie(priv,
+				  proberesp ? wpabuf_head(proberesp) : NULL,
+				  proberesp ? wpabuf_len(proberesp): 0,
+				  IEEE80211_APPIE_FRAME_PROBE_RESP);
+}
+#else /* CONFIG_WPS */
+#define atheros_set_ap_wps_ie NULL
+#endif /* CONFIG_WPS */
+
+static int
+atheros_sta_auth(void *priv, struct wpa_driver_sta_auth_params *params)
+{
+	struct atheros_driver_data *drv = priv;
+	struct ieee80211req_mlme mlme;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "%s: addr=%s status_code=%d",
+		   __func__, ether_sprintf(params->addr), params->status);
+
+#ifdef CONFIG_FILS
+	/* Copy FILS AAD parameters if the driver supports FILS */
+	if (params->fils_auth && drv->fils_en) {
+		wpa_printf(MSG_DEBUG, "%s: im_op IEEE80211_MLME_AUTH_FILS",
+			   __func__);
+		os_memcpy(mlme.fils_aad.ANonce, params->fils_anonce,
+			  IEEE80211_FILS_NONCE_LEN);
+		os_memcpy(mlme.fils_aad.SNonce, params->fils_snonce,
+			  IEEE80211_FILS_NONCE_LEN);
+		os_memcpy(mlme.fils_aad.kek, params->fils_kek,
+			  IEEE80211_MAX_WPA_KEK_LEN);
+		mlme.fils_aad.kek_len = params->fils_kek_len;
+		mlme.im_op = IEEE80211_MLME_AUTH_FILS;
+		wpa_hexdump(MSG_DEBUG, "FILS: ANonce",
+			    mlme.fils_aad.ANonce, FILS_NONCE_LEN);
+		wpa_hexdump(MSG_DEBUG, "FILS: SNonce",
+			    mlme.fils_aad.SNonce, FILS_NONCE_LEN);
+		wpa_hexdump_key(MSG_DEBUG, "FILS: KEK",
+				mlme.fils_aad.kek, mlme.fils_aad.kek_len);
+	} else {
+		mlme.im_op = IEEE80211_MLME_AUTH;
+	}
+#else /* CONFIG_FILS */
+	mlme.im_op = IEEE80211_MLME_AUTH;
+#endif /* CONFIG_FILS */
+
+	mlme.im_reason = params->status;
+	mlme.im_seq = params->seq;
+	os_memcpy(mlme.im_macaddr, params->addr, IEEE80211_ADDR_LEN);
+	mlme.im_optie_len = params->len;
+	if (params->len) {
+		if (params->len < IEEE80211_MAX_OPT_IE) {
+			os_memcpy(mlme.im_optie, params->ie, params->len);
+		} else {
+			wpa_printf(MSG_DEBUG, "%s: Not enough space to copy "
+				   "opt_ie STA (addr " MACSTR " reason %d, "
+				   "ie_len %d)",
+				   __func__, MAC2STR(params->addr),
+				   params->status, (int) params->len);
+			return -1;
+		}
+	}
+	ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme));
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "%s: Failed to auth STA (addr " MACSTR
+			   " reason %d)",
+			   __func__, MAC2STR(params->addr), params->status);
+	}
+	return ret;
+}
+
+static int
+atheros_sta_assoc(void *priv, const u8 *own_addr, const u8 *addr,
+		  int reassoc, u16 status_code, const u8 *ie, size_t len)
+{
+	struct atheros_driver_data *drv = priv;
+	struct ieee80211req_mlme mlme;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "%s: addr=%s status_code=%d reassoc %d",
+		   __func__, ether_sprintf(addr), status_code, reassoc);
+
+	if (reassoc)
+		mlme.im_op = IEEE80211_MLME_REASSOC;
+	else
+		mlme.im_op = IEEE80211_MLME_ASSOC;
+	mlme.im_reason = status_code;
+	os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+	mlme.im_optie_len = len;
+	if (len) {
+		if (len < IEEE80211_MAX_OPT_IE) {
+			os_memcpy(mlme.im_optie, ie, len);
+		} else {
+			wpa_printf(MSG_DEBUG, "%s: Not enough space to copy "
+				   "opt_ie STA (addr " MACSTR " reason %d, "
+				   "ie_len %d)",
+				   __func__, MAC2STR(addr), status_code,
+				   (int) len);
+			return -1;
+		}
+	}
+	ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme));
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "%s: Failed to assoc STA (addr " MACSTR
+			   " reason %d)",
+			   __func__, MAC2STR(addr), status_code);
+	}
+	return ret;
+}
+
+
+static void
+atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN])
+{
+	struct hostapd_data *hapd = drv->hapd;
+	struct ieee80211req_wpaie ie;
+	int ielen = 0;
+	u8 *iebuf = NULL;
+
+	/*
+	 * Fetch negotiated WPA/RSN parameters from the system.
+	 */
+	os_memset(&ie, 0, sizeof(ie));
+	os_memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN);
+	if (set80211priv(drv, IEEE80211_IOCTL_GETWPAIE, &ie, sizeof(ie))) {
+		/*
+		 * See ATH_WPS_IE comment in the beginning of the file for a
+		 * possible cause for the failure..
+		 */
+		wpa_printf(MSG_DEBUG, "%s: Failed to get WPA/RSN IE: %s",
+			   __func__, strerror(errno));
+		goto no_ie;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "atheros req WPA IE",
+		    ie.wpa_ie, IEEE80211_MAX_OPT_IE);
+	wpa_hexdump(MSG_MSGDUMP, "atheros req RSN IE",
+		    ie.rsn_ie, IEEE80211_MAX_OPT_IE);
+#ifdef ATH_WPS_IE
+	wpa_hexdump(MSG_MSGDUMP, "atheros req WPS IE",
+		    ie.wps_ie, IEEE80211_MAX_OPT_IE);
+#endif /* ATH_WPS_IE */
+	iebuf = ie.wpa_ie;
+	/* atheros seems to return some random data if WPA/RSN IE is not set.
+	 * Assume the IE was not included if the IE type is unknown. */
+	if (iebuf[0] != WLAN_EID_VENDOR_SPECIFIC)
+		iebuf[1] = 0;
+	if (iebuf[1] == 0 && ie.rsn_ie[1] > 0) {
+		/* atheros-ng svn #1453 added rsn_ie. Use it, if wpa_ie was not
+		 * set. This is needed for WPA2. */
+		iebuf = ie.rsn_ie;
+		if (iebuf[0] != WLAN_EID_RSN)
+			iebuf[1] = 0;
+	}
+
+	ielen = iebuf[1];
+
+#ifdef ATH_WPS_IE
+	/* if WPS IE is present, preference is given to WPS */
+	if (ie.wps_ie[0] == WLAN_EID_VENDOR_SPECIFIC && ie.wps_ie[1] > 0) {
+		iebuf = ie.wps_ie;
+		ielen = ie.wps_ie[1];
+	}
+#endif /* ATH_WPS_IE */
+
+	if (ielen == 0)
+		iebuf = NULL;
+	else
+		ielen += 2;
+
+no_ie:
+	drv_event_assoc(hapd, addr, iebuf, ielen, 0);
+
+	if (os_memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) {
+		/* Cached accounting data is not valid anymore. */
+		os_memset(drv->acct_mac, 0, ETH_ALEN);
+		os_memset(&drv->acct_data, 0, sizeof(drv->acct_data));
+	}
+}
+
+static void
+atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv,
+				       char *custom, char *end)
+{
+#define MGMT_FRAM_TAG_SIZE 30 /* hardcoded in driver */
+	wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom);
+
+	if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) {
+		char *pos;
+		u8 addr[ETH_ALEN];
+		pos = os_strstr(custom, "addr=");
+		if (pos == NULL) {
+			wpa_printf(MSG_DEBUG,
+				   "MLME-MICHAELMICFAILURE.indication "
+				   "without sender address ignored");
+			return;
+		}
+		pos += 5;
+		if (hwaddr_aton(pos, addr) == 0) {
+			union wpa_event_data data;
+			os_memset(&data, 0, sizeof(data));
+			data.michael_mic_failure.unicast = 1;
+			data.michael_mic_failure.src = addr;
+			wpa_supplicant_event(drv->hapd,
+					     EVENT_MICHAEL_MIC_FAILURE, &data);
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "MLME-MICHAELMICFAILURE.indication "
+				   "with invalid MAC address");
+		}
+	} else if (strncmp(custom, "STA-TRAFFIC-STAT", 16) == 0) {
+		char *key, *value;
+		u32 val;
+		key = custom;
+		while ((key = os_strchr(key, '\n')) != NULL) {
+			key++;
+			value = os_strchr(key, '=');
+			if (value == NULL)
+				continue;
+			*value++ = '\0';
+			val = strtoul(value, NULL, 10);
+			if (os_strcmp(key, "mac") == 0)
+				hwaddr_aton(value, drv->acct_mac);
+			else if (os_strcmp(key, "rx_packets") == 0)
+				drv->acct_data.rx_packets = val;
+			else if (os_strcmp(key, "tx_packets") == 0)
+				drv->acct_data.tx_packets = val;
+			else if (os_strcmp(key, "rx_bytes") == 0)
+				drv->acct_data.rx_bytes = val;
+			else if (os_strcmp(key, "tx_bytes") == 0)
+				drv->acct_data.tx_bytes = val;
+			key = value;
+		}
+#ifdef CONFIG_WPS
+	} else if (os_strncmp(custom, "PUSH-BUTTON.indication", 22) == 0) {
+		/* Some atheros kernels send push button as a wireless event */
+		/* PROBLEM! this event is received for ALL BSSs ...
+		 * so all are enabled for WPS... ugh.
+		 */
+		wpa_supplicant_event(drv->hapd, EVENT_WPS_BUTTON_PUSHED, NULL);
+	} else if (os_strncmp(custom, "Manage.prob_req ", 16) == 0) {
+		/*
+		 * Atheros driver uses a hack to pass Probe Request frames as a
+		 * binary data in the custom wireless event. The old way (using
+		 * packet sniffing) didn't work when bridging.
+		 * Format: "Manage.prob_req <frame len>" | zero padding | frame
+		 */
+		int len = atoi(custom + 16);
+		if (len < 0 || MGMT_FRAM_TAG_SIZE + len > end - custom) {
+			wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req event "
+				   "length %d", len);
+			return;
+		}
+		atheros_raw_receive(drv, NULL,
+				    (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
+#endif /* CONFIG_WPS */
+	} else if (os_strncmp(custom, "Manage.assoc_req ", 17) == 0) {
+		/* Format: "Manage.assoc_req <frame len>" | zero padding |
+		 * frame */
+		int len = atoi(custom + 17);
+		if (len < 0 || MGMT_FRAM_TAG_SIZE + len > end - custom) {
+			wpa_printf(MSG_DEBUG,
+				   "Invalid Manage.assoc_req event length %d",
+				   len);
+			return;
+		}
+		atheros_raw_receive(drv, NULL,
+				    (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
+	} else if (os_strncmp(custom, "Manage.auth ", 12) == 0) {
+		/* Format: "Manage.auth <frame len>" | zero padding | frame */
+		int len = atoi(custom + 12);
+		if (len < 0 ||
+		    MGMT_FRAM_TAG_SIZE + len > end - custom) {
+			wpa_printf(MSG_DEBUG,
+				   "Invalid Manage.auth event length %d", len);
+			return;
+		}
+		atheros_raw_receive(drv, NULL,
+				    (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
+	} else if (os_strncmp(custom, "Manage.action ", 14) == 0) {
+		/* Format: "Manage.assoc_req <frame len>" | zero padding | frame
+		 */
+		int len = atoi(custom + 14);
+		if (len < 0 || MGMT_FRAM_TAG_SIZE + len > end - custom) {
+			wpa_printf(MSG_DEBUG,
+				   "Invalid Manage.action event length %d",
+				   len);
+			return;
+		}
+		atheros_raw_receive(drv, NULL,
+				    (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
+	}
+}
+
+
+static void send_action_cb_event(struct atheros_driver_data *drv,
+				 char *data, size_t data_len)
+{
+	union wpa_event_data event;
+	struct ieee80211_send_action_cb *sa;
+	const struct ieee80211_hdr *hdr;
+	u16 fc;
+
+	if (data_len < sizeof(*sa) + 24) {
+		wpa_printf(MSG_DEBUG,
+			   "athr: Too short event message (data_len=%d sizeof(*sa)=%d)",
+			   (int) data_len, (int) sizeof(*sa));
+		wpa_hexdump(MSG_DEBUG, "athr: Short event message",
+			    data, data_len);
+		return;
+	}
+
+	sa = (struct ieee80211_send_action_cb *) data;
+
+	hdr = (const struct ieee80211_hdr *) (sa + 1);
+	fc = le_to_host16(hdr->frame_control);
+
+	os_memset(&event, 0, sizeof(event));
+	event.tx_status.type = WLAN_FC_GET_TYPE(fc);
+	event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
+	event.tx_status.dst = sa->dst_addr;
+	event.tx_status.data = (const u8 *) hdr;
+	event.tx_status.data_len = data_len - sizeof(*sa);
+	event.tx_status.ack = sa->ack;
+	wpa_supplicant_event(drv->hapd, EVENT_TX_STATUS, &event);
+}
+
+
+/*
+* Handle size of data problem. WEXT only allows data of 256 bytes for custom
+* events, and p2p data can be much bigger. So the athr driver sends a small
+* event telling me to collect the big data with an ioctl.
+* On the first event, send all pending events to supplicant.
+*/
+static void fetch_pending_big_events(struct atheros_driver_data *drv)
+{
+	union wpa_event_data event;
+	const struct ieee80211_mgmt *mgmt;
+	u8 tbuf[IW_PRIV_SIZE_MASK]; /* max size is 2047 bytes */
+	u16 fc, stype;
+	struct iwreq iwr;
+	size_t data_len;
+	u32 freq, frame_type;
+
+	while (1) {
+		os_memset(&iwr, 0, sizeof(iwr));
+		os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+
+		iwr.u.data.pointer = (void *) tbuf;
+		iwr.u.data.length = sizeof(tbuf);
+		iwr.u.data.flags = IEEE80211_IOC_P2P_FETCH_FRAME;
+
+		if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_P2P_BIG_PARAM, &iwr)
+		    < 0) {
+			if (errno == ENOSPC) {
+				wpa_printf(MSG_DEBUG, "%s:%d exit",
+					   __func__, __LINE__);
+				return;
+			}
+			wpa_printf(MSG_DEBUG, "athr: %s: P2P_BIG_PARAM["
+				   "P2P_FETCH_FRAME] failed: %s",
+				   __func__, strerror(errno));
+			return;
+		}
+		data_len = iwr.u.data.length;
+		wpa_hexdump(MSG_DEBUG, "athr: P2P_FETCH_FRAME data",
+			    (u8 *) tbuf, data_len);
+		if (data_len < sizeof(freq) + sizeof(frame_type) + 24) {
+			wpa_printf(MSG_DEBUG, "athr: frame too short");
+			continue;
+		}
+		os_memcpy(&freq, tbuf, sizeof(freq));
+		os_memcpy(&frame_type, &tbuf[sizeof(freq)],
+			  sizeof(frame_type));
+		mgmt = (void *) &tbuf[sizeof(freq) + sizeof(frame_type)];
+		data_len -= sizeof(freq) + sizeof(frame_type);
+
+		if (frame_type == IEEE80211_EV_RX_MGMT) {
+			fc = le_to_host16(mgmt->frame_control);
+			stype = WLAN_FC_GET_STYPE(fc);
+
+			wpa_printf(MSG_DEBUG, "athr: EV_RX_MGMT stype=%u "
+				"freq=%u len=%u", stype, freq, (int) data_len);
+
+			if (stype == WLAN_FC_STYPE_ACTION) {
+				os_memset(&event, 0, sizeof(event));
+				event.rx_mgmt.frame = (const u8 *) mgmt;
+				event.rx_mgmt.frame_len = data_len;
+				wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT,
+						     &event);
+				continue;
+			}
+		} else if (frame_type == IEEE80211_EV_P2P_SEND_ACTION_CB) {
+			wpa_printf(MSG_DEBUG,
+				   "%s: ACTION_CB frame_type=%u len=%zu",
+				   __func__, frame_type, data_len);
+			send_action_cb_event(drv, (void *) mgmt, data_len);
+		} else {
+			wpa_printf(MSG_DEBUG, "athr: %s unknown type %d",
+				   __func__, frame_type);
+			continue;
+		}
+	}
+}
+
+static void
+atheros_wireless_event_atheros_custom(struct atheros_driver_data *drv,
+				      int opcode, char *buf, int len)
+{
+	switch (opcode) {
+	case IEEE80211_EV_P2P_SEND_ACTION_CB:
+		wpa_printf(MSG_DEBUG, "WEXT: EV_P2P_SEND_ACTION_CB");
+		fetch_pending_big_events(drv);
+		break;
+	case IEEE80211_EV_RX_MGMT:
+		wpa_printf(MSG_DEBUG, "WEXT: EV_RX_MGMT");
+		fetch_pending_big_events(drv);
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+atheros_wireless_event_wireless(struct atheros_driver_data *drv,
+				char *data, unsigned int len)
+{
+	struct iw_event iwe_buf, *iwe = &iwe_buf;
+	char *pos, *end, *custom, *buf;
+
+	pos = data;
+	end = data + len;
+
+	while ((size_t) (end - pos) >= IW_EV_LCP_LEN) {
+		/* Event data may be unaligned, so make a local, aligned copy
+		 * before processing. */
+		os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
+		wpa_printf(MSG_MSGDUMP, "Wireless event: cmd=0x%x len=%d",
+			   iwe->cmd, iwe->len);
+		if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos)
+			return;
+
+		custom = pos + IW_EV_POINT_LEN;
+		if (drv->we_version > 18 &&
+		    (iwe->cmd == IWEVMICHAELMICFAILURE ||
+		     iwe->cmd == IWEVASSOCREQIE ||
+		     iwe->cmd == IWEVCUSTOM)) {
+			/* WE-19 removed the pointer from struct iw_point */
+			char *dpos = (char *) &iwe_buf.u.data.length;
+			int dlen = dpos - (char *) &iwe_buf;
+			os_memcpy(dpos, pos + IW_EV_LCP_LEN,
+				  sizeof(struct iw_event) - dlen);
+		} else {
+			os_memcpy(&iwe_buf, pos, sizeof(struct iw_event));
+			custom += IW_EV_POINT_OFF;
+		}
+
+		switch (iwe->cmd) {
+		case IWEVEXPIRED:
+			drv_event_disassoc(drv->hapd,
+					   (u8 *) iwe->u.addr.sa_data);
+			break;
+		case IWEVREGISTERED:
+			atheros_new_sta(drv, (u8 *) iwe->u.addr.sa_data);
+			break;
+		case IWEVASSOCREQIE:
+			/* Driver hack.. Use IWEVASSOCREQIE to bypass
+			 * IWEVCUSTOM size limitations. Need to handle this
+			 * just like IWEVCUSTOM.
+			 */
+		case IWEVCUSTOM:
+			if (iwe->u.data.length > end - custom)
+				return;
+			buf = os_malloc(iwe->u.data.length + 1);
+			if (buf == NULL)
+				return;		/* XXX */
+			os_memcpy(buf, custom, iwe->u.data.length);
+			buf[iwe->u.data.length] = '\0';
+
+			if (iwe->u.data.flags != 0) {
+				atheros_wireless_event_atheros_custom(
+					drv, (int) iwe->u.data.flags,
+					buf, len);
+			} else {
+				atheros_wireless_event_wireless_custom(
+					drv, buf, buf + iwe->u.data.length);
+			}
+			os_free(buf);
+			break;
+		}
+
+		pos += iwe->len;
+	}
+}
+
+
+static void
+atheros_wireless_event_rtm_newlink(void *ctx,
+				   struct ifinfomsg *ifi, u8 *buf, size_t len)
+{
+	struct atheros_driver_data *drv = ctx;
+	int attrlen, rta_len;
+	struct rtattr *attr;
+
+	if (ifi->ifi_index != drv->ifindex)
+		return;
+
+	attrlen = len;
+	attr = (struct rtattr *) buf;
+
+	rta_len = RTA_ALIGN(sizeof(struct rtattr));
+	while (RTA_OK(attr, attrlen)) {
+		if (attr->rta_type == IFLA_WIRELESS) {
+			atheros_wireless_event_wireless(
+				drv, ((char *) attr) + rta_len,
+				attr->rta_len - rta_len);
+		}
+		attr = RTA_NEXT(attr, attrlen);
+	}
+}
+
+
+static int
+atheros_get_we_version(struct atheros_driver_data *drv)
+{
+	struct iw_range *range;
+	struct iwreq iwr;
+	int minlen;
+	size_t buflen;
+
+	drv->we_version = 0;
+
+	/*
+	 * Use larger buffer than struct iw_range in order to allow the
+	 * structure to grow in the future.
+	 */
+	buflen = sizeof(struct iw_range) + 500;
+	range = os_zalloc(buflen);
+	if (range == NULL)
+		return -1;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+	iwr.u.data.pointer = (caddr_t) range;
+	iwr.u.data.length = buflen;
+
+	minlen = ((char *) &range->enc_capa) - (char *) range +
+		sizeof(range->enc_capa);
+
+	if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIWRANGE]: %s",
+			   strerror(errno));
+		os_free(range);
+		return -1;
+	} else if (iwr.u.data.length >= minlen &&
+		   range->we_version_compiled >= 18) {
+		wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d "
+			   "WE(source)=%d enc_capa=0x%x",
+			   range->we_version_compiled,
+			   range->we_version_source,
+			   range->enc_capa);
+		drv->we_version = range->we_version_compiled;
+	}
+
+	os_free(range);
+	return 0;
+}
+
+
+static int
+atheros_wireless_event_init(struct atheros_driver_data *drv)
+{
+	struct netlink_config *cfg;
+
+	atheros_get_we_version(drv);
+
+	cfg = os_zalloc(sizeof(*cfg));
+	if (cfg == NULL)
+		return -1;
+	cfg->ctx = drv;
+	cfg->newlink_cb = atheros_wireless_event_rtm_newlink;
+	drv->netlink = netlink_init(cfg);
+	if (drv->netlink == NULL) {
+		os_free(cfg);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int
+atheros_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len,
+		   int encrypt, const u8 *own_addr, u32 flags, int link_id)
+{
+	struct atheros_driver_data *drv = priv;
+	unsigned char buf[3000];
+	unsigned char *bp = buf;
+	struct l2_ethhdr *eth;
+	size_t len;
+	int status;
+
+	/*
+	 * Prepend the Ethernet header.  If the caller left us
+	 * space at the front we could just insert it but since
+	 * we don't know we copy to a local buffer.  Given the frequency
+	 * and size of frames this probably doesn't matter.
+	 */
+	len = data_len + sizeof(struct l2_ethhdr);
+	if (len > sizeof(buf)) {
+		bp = os_malloc(len);
+		if (bp == NULL) {
+			wpa_printf(MSG_INFO,
+				   "EAPOL frame discarded, cannot malloc temp buffer of size %lu!",
+				   (unsigned long) len);
+			return -1;
+		}
+	}
+	eth = (struct l2_ethhdr *) bp;
+	os_memcpy(eth->h_dest, addr, ETH_ALEN);
+	os_memcpy(eth->h_source, own_addr, ETH_ALEN);
+	eth->h_proto = host_to_be16(ETH_P_EAPOL);
+	os_memcpy(eth + 1, data, data_len);
+
+	wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", bp, len);
+
+	status = l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, bp, len);
+
+	if (bp != buf)
+		os_free(bp);
+	return status;
+}
+
+static void
+handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
+{
+	struct atheros_driver_data *drv = ctx;
+	drv_event_eapol_rx(drv->hapd, src_addr, buf + sizeof(struct l2_ethhdr),
+			   len - sizeof(struct l2_ethhdr));
+}
+
+
+static void atheros_read_fils_cap(struct atheros_driver_data *drv)
+{
+	int fils = 0;
+
+#ifdef CONFIG_FILS
+	/* TODO: Would be better to have #ifdef on the IEEE80211_PARAM_* value
+	 * to automatically check this against the driver header files. */
+	if (get80211param(drv, IEEE80211_PARAM_ENABLE_FILS, &fils) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "%s: Failed to get FILS capability from driver",
+			   __func__);
+		/* Assume driver does not support FILS */
+		fils = 0;
+	}
+#endif /* CONFIG_FILS */
+	drv->fils_en = fils;
+	wpa_printf(MSG_DEBUG, "atheros: fils_en=%d", drv->fils_en);
+}
+
+
+static void *
+atheros_init(struct hostapd_data *hapd, struct wpa_init_params *params)
+{
+	struct atheros_driver_data *drv;
+	struct ifreq ifr;
+	struct iwreq iwr;
+	char brname[IFNAMSIZ];
+
+	drv = os_zalloc(sizeof(struct atheros_driver_data));
+	if (drv == NULL) {
+		wpa_printf(MSG_INFO,
+			   "Could not allocate memory for atheros driver data");
+		return NULL;
+	}
+
+	drv->hapd = hapd;
+	drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
+	if (drv->ioctl_sock < 0) {
+		wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s",
+			   strerror(errno));
+		goto bad;
+	}
+	os_memcpy(drv->iface, params->ifname, sizeof(drv->iface));
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name));
+	if (ioctl(drv->ioctl_sock, SIOCGIFINDEX, &ifr) != 0) {
+		wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s",
+			   strerror(errno));
+		goto bad;
+	}
+	drv->ifindex = ifr.ifr_ifindex;
+
+	drv->sock_xmit = l2_packet_init(drv->iface, NULL, ETH_P_EAPOL,
+					handle_read, drv, 1);
+	if (drv->sock_xmit == NULL)
+		goto bad;
+	if (l2_packet_get_own_addr(drv->sock_xmit, params->own_addr))
+		goto bad;
+	os_memcpy(drv->own_addr, params->own_addr, ETH_ALEN);
+	if (params->bridge[0]) {
+		wpa_printf(MSG_DEBUG, "Configure bridge %s for EAPOL traffic.",
+			   params->bridge[0]);
+		drv->sock_recv = l2_packet_init(params->bridge[0], NULL,
+						ETH_P_EAPOL, handle_read, drv,
+						1);
+		if (drv->sock_recv == NULL)
+			goto bad;
+	} else if (linux_br_get(brname, drv->iface) == 0) {
+		wpa_printf(MSG_DEBUG, "Interface in bridge %s; configure for "
+			   "EAPOL receive", brname);
+		drv->sock_recv = l2_packet_init(brname, NULL, ETH_P_EAPOL,
+						handle_read, drv, 1);
+		if (drv->sock_recv == NULL)
+			goto bad;
+	} else
+		drv->sock_recv = drv->sock_xmit;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+
+	iwr.u.mode = IW_MODE_MASTER;
+
+	if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0) {
+		wpa_printf(MSG_ERROR,
+			   "Could not set interface to master mode! ioctl[SIOCSIWMODE]: %s",
+			   strerror(errno));
+		goto bad;
+	}
+
+	/* mark down during setup */
+	linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0);
+	atheros_set_privacy(drv, 0); /* default to no privacy */
+
+	if (atheros_receive_pkt(drv))
+		goto bad;
+
+	if (atheros_wireless_event_init(drv))
+		goto bad;
+
+	/* Read FILS capability from the driver */
+	atheros_read_fils_cap(drv);
+
+	return drv;
+bad:
+	atheros_reset_appfilter(drv);
+	if (drv->sock_raw)
+		l2_packet_deinit(drv->sock_raw);
+	if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit)
+		l2_packet_deinit(drv->sock_recv);
+	if (drv->sock_xmit != NULL)
+		l2_packet_deinit(drv->sock_xmit);
+	if (drv->ioctl_sock >= 0)
+		close(drv->ioctl_sock);
+	os_free(drv);
+	return NULL;
+}
+
+
+static void
+atheros_deinit(void *priv)
+{
+	struct atheros_driver_data *drv = priv;
+
+	atheros_reset_appfilter(drv);
+
+	if (drv->wpa_ie || drv->wps_beacon_ie || drv->wps_probe_resp_ie) {
+		atheros_set_opt_ie(priv, NULL, 0);
+		wpabuf_free(drv->wpa_ie);
+		wpabuf_free(drv->wps_beacon_ie);
+		wpabuf_free(drv->wps_probe_resp_ie);
+	}
+	netlink_deinit(drv->netlink);
+	(void) linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0);
+	if (drv->ioctl_sock >= 0)
+		close(drv->ioctl_sock);
+	if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit)
+		l2_packet_deinit(drv->sock_recv);
+	if (drv->sock_xmit != NULL)
+		l2_packet_deinit(drv->sock_xmit);
+	if (drv->sock_raw)
+		l2_packet_deinit(drv->sock_raw);
+	os_free(drv);
+}
+
+static int
+atheros_set_ssid(void *priv, const u8 *buf, int len)
+{
+	struct atheros_driver_data *drv = priv;
+	struct iwreq iwr;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+	iwr.u.essid.flags = 1; /* SSID active */
+	iwr.u.essid.pointer = (caddr_t) buf;
+	iwr.u.essid.length = len;
+
+	if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID,len=%d]: %s",
+			   len, strerror(errno));
+		return -1;
+	}
+	return 0;
+}
+
+static int
+atheros_get_ssid(void *priv, u8 *buf, int len)
+{
+	struct atheros_driver_data *drv = priv;
+	struct iwreq iwr;
+	int ret = 0;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+	iwr.u.essid.pointer = (caddr_t) buf;
+	iwr.u.essid.length = (len > IW_ESSID_MAX_SIZE) ?
+		IW_ESSID_MAX_SIZE : len;
+
+	if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIWESSID]: %s",
+			   strerror(errno));
+		ret = -1;
+	} else
+		ret = iwr.u.essid.length;
+
+	return ret;
+}
+
+static int
+atheros_set_countermeasures(void *priv, int enabled)
+{
+	struct atheros_driver_data *drv = priv;
+	wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
+	return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled);
+}
+
+static int
+atheros_commit(void *priv)
+{
+	struct atheros_driver_data *drv = priv;
+	return linux_set_iface_flags(drv->ioctl_sock, drv->iface, 1);
+}
+
+static int atheros_set_authmode(void *priv, int auth_algs)
+{
+	int authmode;
+
+	if ((auth_algs & WPA_AUTH_ALG_OPEN) &&
+	    (auth_algs & WPA_AUTH_ALG_SHARED))
+		authmode = IEEE80211_AUTH_AUTO;
+	else if (auth_algs & WPA_AUTH_ALG_OPEN)
+		authmode = IEEE80211_AUTH_OPEN;
+	else if (auth_algs & WPA_AUTH_ALG_SHARED)
+		authmode = IEEE80211_AUTH_SHARED;
+	else
+		return -1;
+
+	return set80211param(priv, IEEE80211_PARAM_AUTHMODE, authmode);
+}
+
+static int atheros_set_ap(void *priv, struct wpa_driver_ap_params *params)
+{
+	/*
+	 * TODO: Use this to replace set_authmode, set_privacy, set_ieee8021x,
+	 * set_generic_elem, and hapd_set_ssid.
+	 */
+
+	wpa_printf(MSG_DEBUG, "atheros: set_ap - pairwise_ciphers=0x%x "
+		   "group_cipher=0x%x key_mgmt_suites=0x%x auth_algs=0x%x "
+		   "wpa_version=0x%x privacy=%d interworking=%d",
+		   params->pairwise_ciphers, params->group_cipher,
+		   params->key_mgmt_suites, params->auth_algs,
+		   params->wpa_version, params->privacy, params->interworking);
+	wpa_hexdump_ascii(MSG_DEBUG, "atheros: SSID",
+			  params->ssid, params->ssid_len);
+	if (params->hessid)
+		wpa_printf(MSG_DEBUG, "atheros: HESSID " MACSTR,
+			   MAC2STR(params->hessid));
+	wpa_hexdump_buf(MSG_DEBUG, "atheros: beacon_ies",
+			params->beacon_ies);
+	wpa_hexdump_buf(MSG_DEBUG, "atheros: proberesp_ies",
+			params->proberesp_ies);
+	wpa_hexdump_buf(MSG_DEBUG, "atheros: assocresp_ies",
+			params->assocresp_ies);
+
+#if defined(CONFIG_HS20) && (defined(IEEE80211_PARAM_OSEN) || defined(CONFIG_ATHEROS_OSEN))
+	if (params->osen) {
+		struct wpa_bss_params bss_params;
+
+		os_memset(&bss_params, 0, sizeof(struct wpa_bss_params));
+		bss_params.enabled = 1;
+		bss_params.wpa = 2;
+		bss_params.wpa_pairwise = WPA_CIPHER_CCMP;
+		bss_params.wpa_group = WPA_CIPHER_CCMP;
+		bss_params.ieee802_1x = 1;
+
+		if (atheros_set_privacy(priv, 1) ||
+		    set80211param(priv, IEEE80211_PARAM_OSEN, 1))
+			return -1;
+
+		return atheros_set_ieee8021x(priv, &bss_params);
+	}
+#endif /* CONFIG_HS20 && IEEE80211_PARAM_OSEN */
+
+	return 0;
+}
+
+
+static int atheros_send_mgmt(void *priv, const u8 *frm, size_t data_len,
+			     int noack, unsigned int freq,
+			     const u16 *csa_offs, size_t csa_offs_len,
+			     int no_encrypt, unsigned int wait, int link_id)
+{
+	struct atheros_driver_data *drv = priv;
+	u8 buf[1510];
+	const struct ieee80211_mgmt *mgmt;
+	struct ieee80211req_mgmtbuf *mgmt_frm;
+
+	mgmt = (const struct ieee80211_mgmt *) frm;
+	wpa_printf(MSG_DEBUG, "%s frmlen = %lu " MACSTR, __func__,
+		   (unsigned long) data_len, MAC2STR(mgmt->da));
+	mgmt_frm = (struct ieee80211req_mgmtbuf *) buf;
+	os_memcpy(mgmt_frm->macaddr, (u8 *)mgmt->da, IEEE80211_ADDR_LEN);
+	mgmt_frm->buflen = data_len;
+	if (&mgmt_frm->buf[0] + data_len > buf + sizeof(buf)) {
+		wpa_printf(MSG_INFO, "atheros: Too long frame for "
+			   "atheros_send_mgmt (%u)", (unsigned int) data_len);
+		return -1;
+	}
+	os_memcpy(&mgmt_frm->buf[0], frm, data_len);
+	return set80211priv(drv, IEEE80211_IOCTL_SEND_MGMT, mgmt_frm,
+			    sizeof(struct ieee80211req_mgmtbuf) + data_len);
+}
+
+
+#ifdef CONFIG_IEEE80211R
+
+static int atheros_add_tspec(void *priv, const u8 *addr, u8 *tspec_ie,
+			     size_t tspec_ielen)
+{
+	struct atheros_driver_data *drv = priv;
+	int retv;
+	struct ieee80211req_res req;
+	struct ieee80211req_res_addts *addts = &req.u.addts;
+
+	wpa_printf(MSG_DEBUG, "%s", __func__);
+	req.type = IEEE80211_RESREQ_ADDTS;
+	os_memcpy(&req.macaddr[0], addr, IEEE80211_ADDR_LEN);
+	os_memcpy(addts->tspecie, tspec_ie, tspec_ielen);
+	retv = set80211priv(drv, IEEE80211_IOCTL_RES_REQ, &req,
+			    sizeof(struct ieee80211req_res));
+	if (retv < 0) {
+		wpa_printf(MSG_DEBUG, "%s IEEE80211_IOCTL_RES_REQ FAILED "
+			   "retv = %d", __func__, retv);
+		return -1;
+	}
+	os_memcpy(tspec_ie, addts->tspecie, tspec_ielen);
+	return addts->status;
+}
+
+
+static int atheros_add_sta_node(void *priv, const u8 *addr, u16 auth_alg)
+{
+	struct atheros_driver_data *drv = priv;
+	struct ieee80211req_res req;
+	struct ieee80211req_res_addnode *addnode = &req.u.addnode;
+
+	wpa_printf(MSG_DEBUG, "%s", __func__);
+	req.type = IEEE80211_RESREQ_ADDNODE;
+	os_memcpy(&req.macaddr[0], addr, IEEE80211_ADDR_LEN);
+	addnode->auth_alg = auth_alg;
+	return set80211priv(drv, IEEE80211_IOCTL_RES_REQ, &req,
+			    sizeof(struct ieee80211req_res));
+}
+
+#endif /* CONFIG_IEEE80211R */
+
+
+/* Use only to set a big param, get will not work. */
+static int
+set80211big(struct atheros_driver_data *drv, int op, const void *data, int len)
+{
+	struct iwreq iwr;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+
+	iwr.u.data.pointer = (void *) data;
+	iwr.u.data.length = len;
+	iwr.u.data.flags = op;
+	wpa_printf(MSG_DEBUG, "%s: op=0x%x=%d (%s) len=0x%x",
+		   __func__, op, op, athr_get_param_name(op), len);
+
+	if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_P2P_BIG_PARAM, &iwr) < 0) {
+		wpa_printf(MSG_DEBUG, "%s: op=0x%x (%s) subop=0x%x=%d "
+			   "value=0x%x,0x%x failed: %d (%s)",
+			   __func__, op, athr_get_ioctl_name(op), iwr.u.mode,
+			   iwr.u.mode, iwr.u.data.length,
+			   iwr.u.data.flags, errno, strerror(errno));
+		return -1;
+	}
+	return 0;
+}
+
+
+static int atheros_send_action(void *priv, unsigned int freq,
+			       unsigned int wait,
+			       const u8 *dst, const u8 *src,
+			       const u8 *bssid,
+			       const u8 *data, size_t data_len, int no_cck)
+{
+	struct atheros_driver_data *drv = priv;
+	struct ieee80211_p2p_send_action *act;
+	int res;
+
+	act = os_zalloc(sizeof(*act) + data_len);
+	if (act == NULL)
+		return -1;
+	act->freq = freq;
+	os_memcpy(act->dst_addr, dst, ETH_ALEN);
+	os_memcpy(act->src_addr, src, ETH_ALEN);
+	os_memcpy(act->bssid, bssid, ETH_ALEN);
+	os_memcpy(act + 1, data, data_len);
+	wpa_printf(MSG_DEBUG, "%s: freq=%d, wait=%u, dst=" MACSTR ", src="
+		   MACSTR ", bssid=" MACSTR,
+		   __func__, act->freq, wait, MAC2STR(act->dst_addr),
+		   MAC2STR(act->src_addr), MAC2STR(act->bssid));
+	wpa_hexdump(MSG_MSGDUMP, "athr: act", (u8 *) act, sizeof(*act));
+	wpa_hexdump(MSG_MSGDUMP, "athr: data", data, data_len);
+
+	res = set80211big(drv, IEEE80211_IOC_P2P_SEND_ACTION,
+			  act, sizeof(*act) + data_len);
+	os_free(act);
+	return res;
+}
+
+
+#if defined(CONFIG_WNM) && defined(IEEE80211_APPIE_FRAME_WNM)
+static int athr_wnm_tfs(struct atheros_driver_data *drv, const u8* peer,
+			u8 *ie, u16 *len, enum wnm_oper oper)
+{
+#define IEEE80211_APPIE_MAX    1024 /* max appie buffer size */
+	u8 buf[IEEE80211_APPIE_MAX];
+	struct ieee80211req_getset_appiebuf *tfs_ie;
+	u16 val;
+
+	wpa_printf(MSG_DEBUG, "atheros: ifname=%s, WNM TFS IE oper=%d " MACSTR,
+		   drv->iface, oper, MAC2STR(peer));
+
+	switch (oper) {
+	case WNM_SLEEP_TFS_REQ_IE_SET:
+		if (*len > IEEE80211_APPIE_MAX -
+		    sizeof(struct ieee80211req_getset_appiebuf)) {
+			wpa_printf(MSG_DEBUG, "TFS Req IE(s) too large");
+			return -1;
+		}
+		tfs_ie = (struct ieee80211req_getset_appiebuf *) buf;
+		tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM;
+		tfs_ie->app_buflen = ETH_ALEN + 2 + 2 + *len;
+
+		/* Command header for driver */
+		os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN);
+		val = oper;
+		os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2);
+		val = *len;
+		os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2);
+
+		/* copy the ie */
+		os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2 + 2, ie, *len);
+
+		if (set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, tfs_ie,
+				 IEEE80211_APPIE_MAX)) {
+			wpa_printf(MSG_DEBUG, "%s: Failed to set WNM TFS IE: "
+				   "%s", __func__, strerror(errno));
+			return -1;
+		}
+		break;
+	case WNM_SLEEP_TFS_RESP_IE_ADD:
+		tfs_ie = (struct ieee80211req_getset_appiebuf *) buf;
+		tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM;
+		tfs_ie->app_buflen = IEEE80211_APPIE_MAX -
+			sizeof(struct ieee80211req_getset_appiebuf);
+		/* Command header for driver */
+		os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN);
+		val = oper;
+		os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2);
+		val = 0;
+		os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2);
+
+		if (set80211priv(drv, IEEE80211_IOCTL_GET_APPIEBUF, tfs_ie,
+				 IEEE80211_APPIE_MAX)) {
+			wpa_printf(MSG_DEBUG, "%s: Failed to get WNM TFS IE: "
+				   "%s", __func__, strerror(errno));
+			return -1;
+		}
+
+		*len = tfs_ie->app_buflen;
+		os_memcpy(ie, &(tfs_ie->app_buf[0]), *len);
+		wpa_printf(MSG_DEBUG, "atheros: %c len=%d", tfs_ie->app_buf[0],
+			   *len);
+		break;
+	case WNM_SLEEP_TFS_RESP_IE_NONE:
+		*len = 0;
+		break;
+	case WNM_SLEEP_TFS_IE_DEL:
+		tfs_ie = (struct ieee80211req_getset_appiebuf *) buf;
+		tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM;
+		tfs_ie->app_buflen = IEEE80211_APPIE_MAX -
+			sizeof(struct ieee80211req_getset_appiebuf);
+		/* Command header for driver */
+		os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN);
+		val = oper;
+		os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2);
+		val = 0;
+		os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2);
+
+		if (set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, tfs_ie,
+				 IEEE80211_APPIE_MAX)) {
+			wpa_printf(MSG_DEBUG, "%s: Failed to set WNM TFS IE: "
+				   "%s", __func__, strerror(errno));
+			return -1;
+		}
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "Unsupported TFS oper %d", oper);
+		break;
+	}
+
+	return 0;
+}
+
+
+static int atheros_wnm_sleep(struct atheros_driver_data *drv,
+			     const u8 *peer, enum wnm_oper oper)
+{
+	u8 *data, *pos;
+	size_t dlen;
+	int ret;
+	u16 val;
+
+	wpa_printf(MSG_DEBUG, "atheros: WNM-Sleep Oper %d, " MACSTR,
+		   oper, MAC2STR(peer));
+
+	dlen = ETH_ALEN + 2 + 2;
+	data = os_malloc(dlen);
+	if (data == NULL)
+		return -1;
+
+	/* Command header for driver */
+	pos = data;
+	os_memcpy(pos, peer, ETH_ALEN);
+	pos += ETH_ALEN;
+
+	val = oper;
+	os_memcpy(pos, &val, 2);
+	pos += 2;
+
+	val = 0;
+	os_memcpy(pos, &val, 2);
+
+	ret = atheros_set_wps_ie(drv, data, dlen, IEEE80211_APPIE_FRAME_WNM);
+
+	os_free(data);
+
+	return ret;
+}
+
+
+static int atheros_wnm_oper(void *priv, enum wnm_oper oper, const u8 *peer,
+			    u8 *buf, u16 *buf_len)
+{
+	struct atheros_driver_data *drv = priv;
+
+	switch (oper) {
+	case WNM_SLEEP_ENTER_CONFIRM:
+	case WNM_SLEEP_ENTER_FAIL:
+	case WNM_SLEEP_EXIT_CONFIRM:
+	case WNM_SLEEP_EXIT_FAIL:
+		return atheros_wnm_sleep(drv, peer, oper);
+	case WNM_SLEEP_TFS_REQ_IE_SET:
+	case WNM_SLEEP_TFS_RESP_IE_ADD:
+	case WNM_SLEEP_TFS_RESP_IE_NONE:
+	case WNM_SLEEP_TFS_IE_DEL:
+		return athr_wnm_tfs(drv, peer, buf, buf_len, oper);
+	default:
+		wpa_printf(MSG_DEBUG, "atheros: Unsupported WNM operation %d",
+			   oper);
+		return -1;
+	}
+}
+#endif /* CONFIG_WNM && IEEE80211_APPIE_FRAME_WNM */
+
+
+const struct wpa_driver_ops wpa_driver_atheros_ops = {
+	.name			= "atheros",
+	.hapd_init		= atheros_init,
+	.hapd_deinit		= atheros_deinit,
+	.set_ieee8021x		= atheros_set_ieee8021x,
+	.set_privacy		= atheros_set_privacy,
+	.set_key		= atheros_set_key,
+	.get_seqnum		= atheros_get_seqnum,
+	.flush			= atheros_flush,
+	.set_generic_elem	= atheros_set_opt_ie,
+	.sta_set_flags		= atheros_sta_set_flags,
+	.read_sta_data		= atheros_read_sta_driver_data,
+	.hapd_send_eapol	= atheros_send_eapol,
+	.sta_disassoc		= atheros_sta_disassoc,
+	.sta_deauth		= atheros_sta_deauth,
+	.hapd_set_ssid		= atheros_set_ssid,
+	.hapd_get_ssid		= atheros_get_ssid,
+	.set_countermeasures	= atheros_set_countermeasures,
+	.sta_clear_stats	= atheros_sta_clear_stats,
+	.commit			= atheros_commit,
+	.set_ap_wps_ie		= atheros_set_ap_wps_ie,
+	.set_authmode		= atheros_set_authmode,
+	.set_ap			= atheros_set_ap,
+	.sta_assoc              = atheros_sta_assoc,
+	.sta_auth               = atheros_sta_auth,
+	.send_mlme       	= atheros_send_mgmt,
+#ifdef CONFIG_IEEE80211R
+	.add_tspec      	= atheros_add_tspec,
+	.add_sta_node    	= atheros_add_sta_node,
+#endif /* CONFIG_IEEE80211R */
+	.send_action		= atheros_send_action,
+#if defined(CONFIG_WNM) && defined(IEEE80211_APPIE_FRAME_WNM)
+	.wnm_oper		= atheros_wnm_oper,
+#endif /* CONFIG_WNM && IEEE80211_APPIE_FRAME_WNM */
+	.set_qos_map		= atheros_set_qos_map,
+};
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_bsd.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_bsd.c
new file mode 100644
index 0000000..c32d05e
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_bsd.c
@@ -0,0 +1,1769 @@
+/*
+ * WPA Supplicant - driver interaction with BSD net80211 layer
+ * Copyright (c) 2004, Sam Leffler <sam@errno.com>
+ * Copyright (c) 2004, 2Wire, Inc
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+
+#include "common.h"
+#include "driver.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_common.h"
+
+#include <ifaddrs.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+
+#ifdef __NetBSD__
+#include <net/if_ether.h>
+#else
+#include <net/ethernet.h>
+#endif
+#include <net/route.h>
+
+#ifdef __DragonFly__
+#include <netproto/802_11/ieee80211_ioctl.h>
+#include <netproto/802_11/ieee80211_dragonfly.h>
+#else /* __DragonFly__ */
+#ifdef __GLIBC__
+#include <netinet/ether.h>
+#endif /* __GLIBC__ */
+#include <net80211/ieee80211.h>
+#include <net80211/ieee80211_ioctl.h>
+#include <net80211/ieee80211_crypto.h>
+#endif /* __DragonFly__ || __GLIBC__ */
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#include <net80211/ieee80211_freebsd.h>
+#endif
+#if __NetBSD__
+#include <net80211/ieee80211_netbsd.h>
+#endif
+
+#include "l2_packet/l2_packet.h"
+
+struct bsd_driver_global {
+	void		*ctx;
+	int		sock;			/* socket for 802.11 ioctls */
+	int		route;			/* routing socket for events */
+	struct dl_list	ifaces;			/* list of interfaces */
+};
+
+struct bsd_driver_data {
+	struct dl_list	list;
+	struct bsd_driver_global *global;
+	void	*ctx;
+
+	struct l2_packet_data *sock_xmit;/* raw packet xmit socket */
+	char	ifname[IFNAMSIZ+1];	/* interface name */
+	int	flags;
+	unsigned int ifindex;		/* interface index */
+	int	if_removed;		/* has the interface been removed? */
+	struct wpa_driver_capa capa;	/* driver capability */
+	int	is_ap;			/* Access point mode */
+	int	prev_roaming;	/* roaming state to restore on deinit */
+	int	prev_privacy;	/* privacy state to restore on deinit */
+	int	prev_wpa;	/* wpa state to restore on deinit */
+	enum ieee80211_opmode opmode;	/* operation mode */
+};
+
+/* Generic functions for hostapd and wpa_supplicant */
+
+static struct bsd_driver_data *
+bsd_get_drvindex(void *priv, unsigned int ifindex)
+{
+	struct bsd_driver_global *global = priv;
+	struct bsd_driver_data *drv;
+
+	dl_list_for_each(drv, &global->ifaces, struct bsd_driver_data, list) {
+		if (drv->ifindex == ifindex)
+			return drv;
+	}
+	return NULL;
+}
+
+static struct bsd_driver_data *
+bsd_get_drvname(void *priv, const char *ifname)
+{
+	struct bsd_driver_global *global = priv;
+	struct bsd_driver_data *drv;
+
+	dl_list_for_each(drv, &global->ifaces, struct bsd_driver_data, list) {
+		if (os_strcmp(drv->ifname, ifname) == 0)
+			return drv;
+	}
+	return NULL;
+}
+
+static int
+bsd_set80211(void *priv, int op, int val, const void *arg, int arg_len)
+{
+	struct bsd_driver_data *drv = priv;
+	struct ieee80211req ireq;
+
+	if (drv->ifindex == 0 || drv->if_removed)
+		return -1;
+
+	os_memset(&ireq, 0, sizeof(ireq));
+	os_strlcpy(ireq.i_name, drv->ifname, sizeof(ireq.i_name));
+	ireq.i_type = op;
+	ireq.i_val = val;
+	ireq.i_data = (void *) arg;
+	ireq.i_len = arg_len;
+
+	if (ioctl(drv->global->sock, SIOCS80211, &ireq) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, val=%u, "
+			   "arg_len=%u]: %s", op, val, arg_len,
+			   strerror(errno));
+		return -1;
+	}
+	return 0;
+}
+
+static int
+bsd_get80211(void *priv, struct ieee80211req *ireq, int op, void *arg,
+	     int arg_len)
+{
+	struct bsd_driver_data *drv = priv;
+
+	os_memset(ireq, 0, sizeof(*ireq));
+	os_strlcpy(ireq->i_name, drv->ifname, sizeof(ireq->i_name));
+	ireq->i_type = op;
+	ireq->i_len = arg_len;
+	ireq->i_data = arg;
+
+	if (ioctl(drv->global->sock, SIOCG80211, ireq) < 0) {
+		int level = drv->if_removed ? MSG_DEBUG : MSG_ERROR;
+
+		wpa_printf(level, "ioctl[SIOCG80211, op=%u, "
+			   "arg_len=%u]: %s", op, arg_len, strerror(errno));
+		return -1;
+	}
+	return 0;
+}
+
+static int
+get80211var(struct bsd_driver_data *drv, int op, void *arg, int arg_len)
+{
+	struct ieee80211req ireq;
+
+	if (bsd_get80211(drv, &ireq, op, arg, arg_len) < 0)
+		return -1;
+	return ireq.i_len;
+}
+
+static int
+set80211var(struct bsd_driver_data *drv, int op, const void *arg, int arg_len)
+{
+	return bsd_set80211(drv, op, 0, arg, arg_len);
+}
+
+static int
+set80211param(struct bsd_driver_data *drv, int op, int arg)
+{
+	return bsd_set80211(drv, op, arg, NULL, 0);
+}
+
+static int
+bsd_get_ssid(void *priv, u8 *ssid, int len)
+{
+	struct bsd_driver_data *drv = priv;
+#ifdef SIOCG80211NWID
+	struct ieee80211_nwid nwid;
+	struct ifreq ifr;
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+	ifr.ifr_data = (void *)&nwid;
+	if (ioctl(drv->global->sock, SIOCG80211NWID, &ifr) < 0 ||
+	    nwid.i_len > IEEE80211_NWID_LEN)
+		return -1;
+	os_memcpy(ssid, nwid.i_nwid, nwid.i_len);
+	return nwid.i_len;
+#else
+	return get80211var(drv, IEEE80211_IOC_SSID, ssid, IEEE80211_NWID_LEN);
+#endif
+}
+
+static int
+bsd_set_ssid(void *priv, const u8 *ssid, int ssid_len)
+{
+	struct bsd_driver_data *drv = priv;
+#ifdef SIOCS80211NWID
+	struct ieee80211_nwid nwid;
+	struct ifreq ifr;
+
+	os_memcpy(nwid.i_nwid, ssid, ssid_len);
+	nwid.i_len = ssid_len;
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+	ifr.ifr_data = (void *)&nwid;
+	return ioctl(drv->global->sock, SIOCS80211NWID, &ifr);
+#else
+	return set80211var(drv, IEEE80211_IOC_SSID, ssid, ssid_len);
+#endif
+}
+
+static int
+bsd_get_if_media(void *priv)
+{
+	struct bsd_driver_data *drv = priv;
+	struct ifmediareq ifmr;
+
+	os_memset(&ifmr, 0, sizeof(ifmr));
+	os_strlcpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name));
+
+	if (ioctl(drv->global->sock, SIOCGIFMEDIA, &ifmr) < 0) {
+		wpa_printf(MSG_ERROR, "%s: SIOCGIFMEDIA %s", __func__,
+			   strerror(errno));
+		return -1;
+	}
+
+	return ifmr.ifm_current;
+}
+
+static int
+bsd_set_if_media(void *priv, int media)
+{
+	struct bsd_driver_data *drv = priv;
+	struct ifreq ifr;
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+	ifr.ifr_media = media;
+
+	if (ioctl(drv->global->sock, SIOCSIFMEDIA, &ifr) < 0) {
+		wpa_printf(MSG_ERROR, "%s: SIOCSIFMEDIA %s", __func__,
+			   strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+bsd_set_mediaopt(void *priv, uint32_t mask, uint32_t mode)
+{
+	int media = bsd_get_if_media(priv);
+
+	if (media < 0)
+		return -1;
+	media &= ~mask;
+	media |= mode;
+	if (bsd_set_if_media(priv, media) < 0)
+		return -1;
+	return 0;
+}
+
+static int
+bsd_del_key(void *priv, const u8 *addr, int key_idx)
+{
+	struct ieee80211req_del_key wk;
+
+	os_memset(&wk, 0, sizeof(wk));
+	if (addr == NULL) {
+		wpa_printf(MSG_DEBUG, "%s: key_idx=%d", __func__, key_idx);
+		wk.idk_keyix = key_idx;
+	} else {
+		wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__,
+			   MAC2STR(addr));
+		os_memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN);
+		wk.idk_keyix = (u_int8_t) IEEE80211_KEYIX_NONE;	/* XXX */
+	}
+
+	return set80211var(priv, IEEE80211_IOC_DELKEY, &wk, sizeof(wk));
+}
+
+static int
+bsd_send_mlme_param(void *priv, const u8 op, const u16 reason, const u8 *addr)
+{
+	struct ieee80211req_mlme mlme;
+
+	os_memset(&mlme, 0, sizeof(mlme));
+	mlme.im_op = op;
+	mlme.im_reason = reason;
+	os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+	return set80211var(priv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme));
+}
+
+static int
+bsd_get_iface_flags(struct bsd_driver_data *drv)
+{
+	struct ifreq ifr;
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+
+	if (ioctl(drv->global->sock, SIOCGIFFLAGS, &ifr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s",
+			   strerror(errno));
+		return -1;
+	}
+	drv->flags = ifr.ifr_flags;
+	return 0;
+}
+
+static int
+bsd_set_key(void *priv, struct wpa_driver_set_key_params *params)
+{
+	struct ieee80211req_key wk;
+#ifdef IEEE80211_KEY_NOREPLAY
+	struct bsd_driver_data *drv = priv;
+#endif /* IEEE80211_KEY_NOREPLAY */
+	enum wpa_alg alg = params->alg;
+	const u8 *addr = params->addr;
+	int key_idx = params->key_idx;
+	int set_tx = params->set_tx;
+	const u8 *seq = params->seq;
+	size_t seq_len = params->seq_len;
+	const u8 *key = params->key;
+	size_t key_len = params->key_len;
+
+	wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%p key_idx=%d set_tx=%d "
+		   "seq_len=%zu key_len=%zu", __func__, alg, addr, key_idx,
+		   set_tx, seq_len, key_len);
+
+	if (alg == WPA_ALG_NONE) {
+#ifndef HOSTAPD
+		if (addr == NULL || is_broadcast_ether_addr(addr))
+			return bsd_del_key(priv, NULL, key_idx);
+		else
+#endif /* HOSTAPD */
+			return bsd_del_key(priv, addr, key_idx);
+	}
+
+	os_memset(&wk, 0, sizeof(wk));
+	switch (alg) {
+	case WPA_ALG_WEP:
+		wk.ik_type = IEEE80211_CIPHER_WEP;
+		break;
+	case WPA_ALG_TKIP:
+		wk.ik_type = IEEE80211_CIPHER_TKIP;
+		break;
+	case WPA_ALG_CCMP:
+		wk.ik_type = IEEE80211_CIPHER_AES_CCM;
+		break;
+	default:
+		wpa_printf(MSG_ERROR, "%s: unknown alg=%d", __func__, alg);
+		return -1;
+	}
+
+	wk.ik_flags = IEEE80211_KEY_RECV;
+	if (set_tx)
+		wk.ik_flags |= IEEE80211_KEY_XMIT;
+
+	if (addr == NULL) {
+		os_memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
+		wk.ik_keyix = key_idx;
+	} else {
+		os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
+		/*
+		 * Deduce whether group/global or unicast key by checking
+		 * the address (yech).  Note also that we can only mark global
+		 * keys default; doing this for a unicast key is an error.
+		 */
+		if (is_broadcast_ether_addr(addr)) {
+			wk.ik_flags |= IEEE80211_KEY_GROUP;
+			wk.ik_keyix = key_idx;
+		} else {
+			wk.ik_keyix = key_idx == 0 ? IEEE80211_KEYIX_NONE :
+				key_idx;
+		}
+	}
+	if (wk.ik_keyix != IEEE80211_KEYIX_NONE && set_tx)
+		wk.ik_flags |= IEEE80211_KEY_DEFAULT;
+#ifndef HOSTAPD
+#ifdef IEEE80211_KEY_NOREPLAY
+	/*
+	 * Ignore replay failures in IBSS and AHDEMO mode.
+	 */
+	if (drv->opmode == IEEE80211_M_IBSS ||
+	    drv->opmode == IEEE80211_M_AHDEMO)
+		wk.ik_flags |= IEEE80211_KEY_NOREPLAY;
+#endif /* IEEE80211_KEY_NOREPLAY */
+#endif /* HOSTAPD */
+	wk.ik_keylen = key_len;
+	if (seq) {
+#ifdef WORDS_BIGENDIAN
+		/*
+		 * wk.ik_keyrsc is in host byte order (big endian), need to
+		 * swap it to match with the byte order used in WPA.
+		 */
+		int i;
+		u8 *keyrsc = (u8 *) &wk.ik_keyrsc;
+		for (i = 0; i < seq_len; i++)
+			keyrsc[WPA_KEY_RSC_LEN - i - 1] = seq[i];
+#else /* WORDS_BIGENDIAN */
+		os_memcpy(&wk.ik_keyrsc, seq, seq_len);
+#endif /* WORDS_BIGENDIAN */
+	}
+	os_memcpy(wk.ik_keydata, key, key_len);
+
+	return set80211var(priv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk));
+}
+
+static int
+bsd_configure_wpa(void *priv, struct wpa_bss_params *params)
+{
+#ifndef IEEE80211_IOC_APPIE
+	static const char *ciphernames[] =
+		{ "WEP", "TKIP", "AES-OCB", "AES-CCM", "CKIP", "NONE" };
+	int v;
+
+	switch (params->wpa_group) {
+	case WPA_CIPHER_CCMP:
+		v = IEEE80211_CIPHER_AES_CCM;
+		break;
+	case WPA_CIPHER_TKIP:
+		v = IEEE80211_CIPHER_TKIP;
+		break;
+	case WPA_CIPHER_WEP104:
+		v = IEEE80211_CIPHER_WEP;
+		break;
+	case WPA_CIPHER_WEP40:
+		v = IEEE80211_CIPHER_WEP;
+		break;
+	case WPA_CIPHER_NONE:
+		v = IEEE80211_CIPHER_NONE;
+		break;
+	default:
+		wpa_printf(MSG_INFO, "Unknown group key cipher %u",
+			   params->wpa_group);
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "%s: group key cipher=%s (%u)",
+		   __func__, ciphernames[v], v);
+	if (set80211param(priv, IEEE80211_IOC_MCASTCIPHER, v)) {
+		wpa_printf(MSG_INFO,
+			   "Unable to set group key cipher to %u (%s)",
+			   v, ciphernames[v]);
+		return -1;
+	}
+	if (v == IEEE80211_CIPHER_WEP) {
+		/* key length is done only for specific ciphers */
+		v = (params->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5);
+		if (set80211param(priv, IEEE80211_IOC_MCASTKEYLEN, v)) {
+			wpa_printf(MSG_INFO,
+				   "Unable to set group key length to %u", v);
+			return -1;
+		}
+	}
+
+	v = 0;
+	if (params->wpa_pairwise & WPA_CIPHER_CCMP)
+		v |= 1<<IEEE80211_CIPHER_AES_CCM;
+	if (params->wpa_pairwise & WPA_CIPHER_TKIP)
+		v |= 1<<IEEE80211_CIPHER_TKIP;
+	if (params->wpa_pairwise & WPA_CIPHER_NONE)
+		v |= 1<<IEEE80211_CIPHER_NONE;
+	wpa_printf(MSG_DEBUG, "%s: pairwise key ciphers=0x%x", __func__, v);
+	if (set80211param(priv, IEEE80211_IOC_UCASTCIPHERS, v)) {
+		wpa_printf(MSG_INFO,
+			   "Unable to set pairwise key ciphers to 0x%x", v);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "%s: key management algorithms=0x%x",
+		   __func__, params->wpa_key_mgmt);
+	if (set80211param(priv, IEEE80211_IOC_KEYMGTALGS,
+			  params->wpa_key_mgmt)) {
+		wpa_printf(MSG_INFO,
+			   "Unable to set key management algorithms to 0x%x",
+			   params->wpa_key_mgmt);
+		return -1;
+	}
+
+	v = 0;
+	if (params->rsn_preauth)
+		v |= BIT(0);
+	wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x",
+		   __func__, params->rsn_preauth);
+	if (set80211param(priv, IEEE80211_IOC_RSNCAPS, v)) {
+		wpa_printf(MSG_INFO, "Unable to set RSN capabilities to 0x%x",
+			   v);
+		return -1;
+	}
+#endif /* IEEE80211_IOC_APPIE */
+
+	wpa_printf(MSG_DEBUG, "%s: enable WPA= 0x%x", __func__, params->wpa);
+	if (set80211param(priv, IEEE80211_IOC_WPA, params->wpa)) {
+		wpa_printf(MSG_INFO, "Unable to set WPA to %u", params->wpa);
+		return -1;
+	}
+	return 0;
+}
+
+static int
+bsd_set_ieee8021x(void *priv, struct wpa_bss_params *params)
+{
+	wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled);
+
+	if (!params->enabled) {
+		/* XXX restore state */
+		return set80211param(priv, IEEE80211_IOC_AUTHMODE,
+				     IEEE80211_AUTH_AUTO);
+	}
+	if (!params->wpa && !params->ieee802_1x) {
+		wpa_printf(MSG_ERROR, "%s: No 802.1X or WPA enabled",
+			   __func__);
+		return -1;
+	}
+	if (params->wpa && bsd_configure_wpa(priv, params) != 0) {
+		wpa_printf(MSG_ERROR, "%s: Failed to configure WPA state",
+			   __func__);
+		return -1;
+	}
+	if (set80211param(priv, IEEE80211_IOC_AUTHMODE,
+		(params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) {
+		wpa_printf(MSG_ERROR, "%s: Failed to enable WPA/802.1X",
+			   __func__);
+		return -1;
+	}
+	return 0;
+}
+
+static void
+bsd_new_sta(void *priv, void *ctx, u8 addr[IEEE80211_ADDR_LEN])
+{
+	struct ieee80211req_wpaie ie;
+	int ielen = 0;
+	u8 *iebuf = NULL;
+
+	/*
+	 * Fetch and validate any negotiated WPA/RSN parameters.
+	 */
+	memset(&ie, 0, sizeof(ie));
+	memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN);
+	if (get80211var(priv, IEEE80211_IOC_WPAIE, &ie, sizeof(ie)) < 0) {
+		wpa_printf(MSG_INFO,
+			   "Failed to get WPA/RSN information element");
+		goto no_ie;
+	}
+	iebuf = ie.wpa_ie;
+	ielen = ie.wpa_ie[1];
+	if (ielen == 0)
+		iebuf = NULL;
+	else
+		ielen += 2;
+
+no_ie:
+	drv_event_assoc(ctx, addr, iebuf, ielen, 0);
+}
+
+static int
+bsd_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len,
+	       int encrypt, const u8 *own_addr, u32 flags, int link_id)
+{
+	struct bsd_driver_data *drv = priv;
+
+	wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", data, data_len);
+
+	return l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, data,
+			      data_len);
+}
+
+static int
+bsd_set_freq(void *priv, struct hostapd_freq_params *freq)
+{
+	struct bsd_driver_data *drv = priv;
+#ifdef SIOCS80211CHANNEL
+	struct ieee80211chanreq creq;
+#endif /* SIOCS80211CHANNEL */
+	u32 mode;
+	int channel = freq->channel;
+
+	if (channel < 14) {
+		mode =
+			freq->ht_enabled ? IFM_IEEE80211_11NG :
+			IFM_IEEE80211_11G;
+	} else if (channel == 14) {
+		mode = IFM_IEEE80211_11B;
+	} else {
+		mode =
+			freq->ht_enabled ? IFM_IEEE80211_11NA :
+			IFM_IEEE80211_11A;
+	}
+	if (bsd_set_mediaopt(drv, IFM_MMASK, mode) < 0) {
+		wpa_printf(MSG_ERROR, "%s: failed to set modulation mode",
+			   __func__);
+		return -1;
+	}
+
+#ifdef SIOCS80211CHANNEL
+	os_memset(&creq, 0, sizeof(creq));
+	os_strlcpy(creq.i_name, drv->ifname, sizeof(creq.i_name));
+	creq.i_channel = (u_int16_t)channel;
+	return ioctl(drv->global->sock, SIOCS80211CHANNEL, &creq);
+#else /* SIOCS80211CHANNEL */
+	return set80211param(priv, IEEE80211_IOC_CHANNEL, channel);
+#endif /* SIOCS80211CHANNEL */
+}
+
+static int
+bsd_set_opt_ie(void *priv, const u8 *ie, size_t ie_len)
+{
+#ifdef IEEE80211_IOC_APPIE
+	wpa_printf(MSG_DEBUG, "%s: set WPA+RSN ie (len %lu)", __func__,
+		   (unsigned long)ie_len);
+	return bsd_set80211(priv, IEEE80211_IOC_APPIE, IEEE80211_APPIE_WPA,
+			    ie, ie_len);
+#endif /* IEEE80211_IOC_APPIE */
+	return 0;
+}
+
+#ifdef SO_RERROR
+static void
+bsd_route_overflow(int sock, void *ctx, struct bsd_driver_global *global)
+{
+	char event_buf[2048]; /* max size of a single route(4) msg */
+	int n;
+	struct ifaddrs *ifaddrs, *ifa;
+	struct bsd_driver_data *drv;
+	struct sockaddr_dl *sdl;
+	union wpa_event_data event;
+
+	/* We need to match the system state, so drain the route
+	 * socket to avoid stale messages. */
+	do {
+		n = read(sock, event_buf, sizeof(event_buf));
+	} while (n != -1 || errno == ENOBUFS);
+
+	if (getifaddrs(&ifaddrs) == -1) {
+		wpa_printf(MSG_ERROR, "%s getifaddrs() failed: %s",
+			   __func__, strerror(errno));
+		return;
+	}
+
+	/* add or update existing interfaces */
+	for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
+		if (ifa->ifa_addr == NULL ||
+		    ifa->ifa_addr->sa_family != AF_LINK)
+			continue;
+		sdl = (struct sockaddr_dl *) (void *) ifa->ifa_addr;
+		drv = bsd_get_drvname(global, ifa->ifa_name);
+		if (drv != NULL &&
+		    (drv->ifindex != sdl->sdl_index || drv->if_removed)) {
+			wpa_printf(MSG_DEBUG,
+				   "RTM_IFANNOUNCE: Interface '%s' added",
+				   drv->ifname);
+			drv->ifindex = sdl->sdl_index;
+			drv->if_removed = 0;
+			event.interface_status.ievent = EVENT_INTERFACE_ADDED;
+			os_strlcpy(event.interface_status.ifname, ifa->ifa_name,
+				   sizeof(event.interface_status.ifname));
+			wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS,
+					     &event);
+		}
+		if (!drv &&
+		    (drv = bsd_get_drvindex(global, sdl->sdl_index)) != NULL) {
+			/* Driver name is invalid */
+			wpa_printf(MSG_DEBUG,
+				   "RTM_IFANNOUNCE: Interface '%s' removed",
+				   drv->ifname);
+			drv->if_removed = 1;
+			event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
+			os_strlcpy(event.interface_status.ifname, drv->ifname,
+				   sizeof(event.interface_status.ifname));
+			wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS,
+					     &event);
+		}
+	}
+
+	/* punt missing interfaces and update flags */
+	dl_list_for_each(drv, &global->ifaces, struct bsd_driver_data, list) {
+		for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
+			if (ifa->ifa_addr == NULL ||
+			    ifa->ifa_addr->sa_family != AF_LINK)
+				continue;
+			sdl = (struct sockaddr_dl *) (void *) ifa->ifa_addr;
+			if (os_strcmp(drv->ifname, ifa->ifa_name) == 0)
+				break;
+		}
+		if (ifa == NULL && !drv->if_removed) {
+			wpa_printf(MSG_DEBUG,
+				   "RTM_IFANNOUNCE: Interface '%s' removed",
+				   drv->ifname);
+			drv->if_removed = 1;
+			event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
+			os_strlcpy(event.interface_status.ifname, drv->ifname,
+				   sizeof(event.interface_status.ifname));
+			wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS,
+					     &event);
+		}
+		if (!ifa)
+			continue;
+
+		if ((ifa->ifa_flags & IFF_UP) == 0 &&
+		    (drv->flags & IFF_UP) != 0) {
+			wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' DOWN",
+				   drv->ifname);
+			wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED,
+					     NULL);
+		} else if ((ifa->ifa_flags & IFF_UP) != 0 &&
+			   (drv->flags & IFF_UP) == 0) {
+			wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' UP",
+				   drv->ifname);
+			wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED,
+					     NULL);
+		}
+		drv->flags = ifa->ifa_flags;
+	}
+
+	freeifaddrs(ifaddrs);
+}
+#endif /* SO_RERROR */
+
+static void
+bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx)
+{
+	char event_buf[2048]; /* max size of a single route(4) msg */
+	struct bsd_driver_global *global = sock_ctx;
+	struct bsd_driver_data *drv;
+	struct if_announcemsghdr *ifan;
+	struct if_msghdr *ifm;
+	struct rt_msghdr *rtm;
+	union wpa_event_data event;
+	struct ieee80211_michael_event *mic;
+	struct ieee80211_leave_event *leave;
+	struct ieee80211_join_event *join;
+	int n;
+
+	n = read(sock, event_buf, sizeof(event_buf));
+	if (n < 0) {
+		if (errno != EINTR && errno != EAGAIN)
+			wpa_printf(MSG_ERROR, "%s read() failed: %s",
+				   __func__, strerror(errno));
+#ifdef SO_RERROR
+		if (errno == ENOBUFS)
+			bsd_route_overflow(sock, ctx, sock_ctx);
+#endif /* SO_RERROR */
+		return;
+	}
+
+	rtm = (struct rt_msghdr *) event_buf;
+	if (rtm->rtm_version != RTM_VERSION) {
+		wpa_printf(MSG_DEBUG, "Invalid routing message version=%d",
+			   rtm->rtm_version);
+		return;
+	}
+	os_memset(&event, 0, sizeof(event));
+	switch (rtm->rtm_type) {
+	case RTM_IEEE80211:
+		ifan = (struct if_announcemsghdr *) rtm;
+		drv = bsd_get_drvindex(global, ifan->ifan_index);
+		if (drv == NULL)
+			return;
+		switch (ifan->ifan_what) {
+		case RTM_IEEE80211_ASSOC:
+		case RTM_IEEE80211_REASSOC:
+			if (drv->is_ap)
+				break;
+			wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
+			break;
+		case RTM_IEEE80211_DISASSOC:
+			if (drv->is_ap)
+				break;
+			wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+			break;
+		case RTM_IEEE80211_SCAN:
+			if (drv->is_ap)
+				break;
+			wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS,
+					     NULL);
+			break;
+		case RTM_IEEE80211_LEAVE:
+			leave = (struct ieee80211_leave_event *) &ifan[1];
+			drv_event_disassoc(drv->ctx, leave->iev_addr);
+			break;
+		case RTM_IEEE80211_JOIN:
+#ifdef RTM_IEEE80211_REJOIN
+		case RTM_IEEE80211_REJOIN:
+#endif
+			join = (struct ieee80211_join_event *) &ifan[1];
+			bsd_new_sta(drv, drv->ctx, join->iev_addr);
+			break;
+		case RTM_IEEE80211_REPLAY:
+			/* ignore */
+			break;
+		case RTM_IEEE80211_MICHAEL:
+			mic = (struct ieee80211_michael_event *) &ifan[1];
+			wpa_printf(MSG_DEBUG,
+				"Michael MIC failure wireless event: "
+				"keyix=%u src_addr=" MACSTR, mic->iev_keyix,
+				MAC2STR(mic->iev_src));
+			os_memset(&event, 0, sizeof(event));
+			event.michael_mic_failure.unicast =
+				!IEEE80211_IS_MULTICAST(mic->iev_dst);
+			event.michael_mic_failure.src = mic->iev_src;
+			wpa_supplicant_event(drv->ctx,
+					     EVENT_MICHAEL_MIC_FAILURE, &event);
+			break;
+		}
+		break;
+	case RTM_IFANNOUNCE:
+		ifan = (struct if_announcemsghdr *) rtm;
+		switch (ifan->ifan_what) {
+		case IFAN_DEPARTURE:
+			drv = bsd_get_drvindex(global, ifan->ifan_index);
+			if (drv)
+				drv->if_removed = 1;
+			event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
+			break;
+		case IFAN_ARRIVAL:
+			drv = bsd_get_drvname(global, ifan->ifan_name);
+			if (drv) {
+				drv->ifindex = ifan->ifan_index;
+				drv->if_removed = 0;
+			}
+			event.interface_status.ievent = EVENT_INTERFACE_ADDED;
+			break;
+		default:
+			wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: unknown action");
+			return;
+		}
+		wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: Interface '%s' %s",
+			   ifan->ifan_name,
+			   ifan->ifan_what == IFAN_DEPARTURE ?
+				"removed" : "added");
+		os_strlcpy(event.interface_status.ifname, ifan->ifan_name,
+			   sizeof(event.interface_status.ifname));
+		if (drv) {
+			wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS,
+					     &event);
+			/*
+			 * Set ifindex to zero after sending the event as the
+			 * event might query the driver to ensure a match.
+			 */
+			if (ifan->ifan_what == IFAN_DEPARTURE)
+				drv->ifindex = 0;
+		} else {
+			wpa_supplicant_event_global(global->ctx,
+						    EVENT_INTERFACE_STATUS,
+						    &event);
+		}
+		break;
+	case RTM_IFINFO:
+		ifm = (struct if_msghdr *) rtm;
+		drv = bsd_get_drvindex(global, ifm->ifm_index);
+		if (drv == NULL)
+			return;
+		if ((ifm->ifm_flags & IFF_UP) == 0 &&
+		    (drv->flags & IFF_UP) != 0) {
+			wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' DOWN",
+				   drv->ifname);
+			wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED,
+					     NULL);
+		} else if ((ifm->ifm_flags & IFF_UP) != 0 &&
+		    (drv->flags & IFF_UP) == 0) {
+			wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' UP",
+				   drv->ifname);
+			wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED,
+					     NULL);
+		}
+		drv->flags = ifm->ifm_flags;
+		break;
+	}
+}
+
+#ifdef HOSTAPD
+
+/*
+ * Avoid conflicts with hostapd definitions by undefining couple of defines
+ * from net80211 header files.
+ */
+#undef RSN_VERSION
+#undef WPA_VERSION
+#undef WPA_OUI_TYPE
+
+static int bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+			  u16 reason_code, int link_id);
+
+static const char *
+ether_sprintf(const u8 *addr)
+{
+	static char buf[sizeof(MACSTR)];
+
+	if (addr != NULL)
+		snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
+	else
+		snprintf(buf, sizeof(buf), MACSTR, 0,0,0,0,0,0);
+	return buf;
+}
+
+static int
+bsd_set_privacy(void *priv, int enabled)
+{
+	wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
+
+	return set80211param(priv, IEEE80211_IOC_PRIVACY, enabled);
+}
+
+static int
+bsd_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
+	       int link_id, u8 *seq)
+{
+	struct ieee80211req_key wk;
+
+	wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d",
+		   __func__, ether_sprintf(addr), idx);
+
+	memset(&wk, 0, sizeof(wk));
+	if (addr == NULL)
+		memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
+	else
+		memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
+	wk.ik_keyix = idx;
+
+	if (get80211var(priv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk)) < 0) {
+		wpa_printf(MSG_INFO, "Failed to get encryption");
+		return -1;
+	}
+
+#ifdef WORDS_BIGENDIAN
+	{
+		/*
+		 * wk.ik_keytsc is in host byte order (big endian), need to
+		 * swap it to match with the byte order used in WPA.
+		 */
+		int i;
+		u8 tmp[WPA_KEY_RSC_LEN];
+		memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc));
+		for (i = 0; i < WPA_KEY_RSC_LEN; i++) {
+			seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1];
+		}
+	}
+#else /* WORDS_BIGENDIAN */
+	memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc));
+#endif /* WORDS_BIGENDIAN */
+	return 0;
+}
+
+
+static int
+bsd_flush(void *priv)
+{
+	u8 allsta[IEEE80211_ADDR_LEN];
+
+	memset(allsta, 0xff, IEEE80211_ADDR_LEN);
+	return bsd_sta_deauth(priv, NULL, allsta, IEEE80211_REASON_AUTH_LEAVE,
+			      -1);
+}
+
+
+static int
+bsd_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data,
+			 const u8 *addr)
+{
+	struct ieee80211req_sta_stats stats;
+
+	memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN);
+	if (get80211var(priv, IEEE80211_IOC_STA_STATS, &stats, sizeof(stats))
+	    > 0) {
+		/* XXX? do packets counts include non-data frames? */
+		data->rx_packets = stats.is_stats.ns_rx_data;
+		data->rx_bytes = stats.is_stats.ns_rx_bytes;
+		data->tx_packets = stats.is_stats.ns_tx_data;
+		data->tx_bytes = stats.is_stats.ns_tx_bytes;
+	}
+	return 0;
+}
+
+static int
+bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, u16 reason_code,
+	       int link_id)
+{
+	return bsd_send_mlme_param(priv, IEEE80211_MLME_DEAUTH, reason_code,
+				   addr);
+}
+
+static int
+bsd_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
+		 u16 reason_code)
+{
+	return bsd_send_mlme_param(priv, IEEE80211_MLME_DISASSOC, reason_code,
+				   addr);
+}
+
+static void
+handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
+{
+	struct bsd_driver_data *drv = ctx;
+	drv_event_eapol_rx(drv->ctx, src_addr, buf, len);
+}
+
+static void *
+bsd_init(struct hostapd_data *hapd, struct wpa_init_params *params)
+{
+	struct bsd_driver_data *drv;
+
+	drv = os_zalloc(sizeof(struct bsd_driver_data));
+	if (drv == NULL) {
+		wpa_printf(MSG_ERROR, "Could not allocate memory for bsd driver data");
+		return NULL;
+	}
+
+	drv->ifindex = if_nametoindex(params->ifname);
+	if (drv->ifindex == 0) {
+		wpa_printf(MSG_DEBUG, "%s: interface %s does not exist",
+			   __func__, params->ifname);
+		goto bad;
+	}
+
+	drv->ctx = hapd;
+	drv->is_ap = 1;
+	drv->global = params->global_priv;
+	os_strlcpy(drv->ifname, params->ifname, sizeof(drv->ifname));
+
+	drv->sock_xmit = l2_packet_init(drv->ifname, NULL, ETH_P_EAPOL,
+					handle_read, drv, 0);
+	if (drv->sock_xmit == NULL)
+		goto bad;
+	if (l2_packet_get_own_addr(drv->sock_xmit, params->own_addr))
+		goto bad;
+
+	if (bsd_get_iface_flags(drv) < 0)
+		goto bad;
+
+	if (bsd_set_mediaopt(drv, IFM_OMASK, IFM_IEEE80211_HOSTAP) < 0) {
+		wpa_printf(MSG_ERROR, "%s: failed to set operation mode",
+			   __func__);
+		goto bad;
+	}
+
+	dl_list_add(&drv->global->ifaces, &drv->list);
+
+	return drv;
+bad:
+	if (drv->sock_xmit != NULL)
+		l2_packet_deinit(drv->sock_xmit);
+	os_free(drv);
+	return NULL;
+}
+
+
+static void
+bsd_deinit(void *priv)
+{
+	struct bsd_driver_data *drv = priv;
+
+	if (drv->sock_xmit != NULL)
+		l2_packet_deinit(drv->sock_xmit);
+	os_free(drv);
+}
+
+
+static int
+bsd_set_sta_authorized(void *priv, const u8 *addr,
+		       unsigned int total_flags, unsigned int flags_or,
+		       unsigned int flags_and)
+{
+	int authorized = -1;
+
+	/* For now, only support setting Authorized flag */
+	if (flags_or & WPA_STA_AUTHORIZED)
+		authorized = 1;
+	if (!(flags_and & WPA_STA_AUTHORIZED))
+		authorized = 0;
+
+	if (authorized < 0)
+		return 0;
+
+	return bsd_send_mlme_param(priv, authorized ?
+				   IEEE80211_MLME_AUTHORIZE :
+				   IEEE80211_MLME_UNAUTHORIZE, 0, addr);
+}
+#else /* HOSTAPD */
+
+static int
+get80211param(struct bsd_driver_data *drv, int op)
+{
+	struct ieee80211req ireq;
+
+	if (bsd_get80211(drv, &ireq, op, NULL, 0) < 0)
+		return -1;
+	return ireq.i_val;
+}
+
+static int
+wpa_driver_bsd_get_bssid(void *priv, u8 *bssid)
+{
+	struct bsd_driver_data *drv = priv;
+#ifdef SIOCG80211BSSID
+	struct ieee80211_bssid bs;
+
+	os_strlcpy(bs.i_name, drv->ifname, sizeof(bs.i_name));
+	if (ioctl(drv->global->sock, SIOCG80211BSSID, &bs) < 0)
+		return -1;
+	os_memcpy(bssid, bs.i_bssid, sizeof(bs.i_bssid));
+	return 0;
+#else
+	return get80211var(drv, IEEE80211_IOC_BSSID,
+		bssid, IEEE80211_ADDR_LEN) < 0 ? -1 : 0;
+#endif
+}
+
+static int
+wpa_driver_bsd_get_ssid(void *priv, u8 *ssid)
+{
+	struct bsd_driver_data *drv = priv;
+	return bsd_get_ssid(drv, ssid, 0);
+}
+
+static int
+wpa_driver_bsd_set_wpa_ie(struct bsd_driver_data *drv, const u8 *wpa_ie,
+			  size_t wpa_ie_len)
+{
+#ifdef IEEE80211_IOC_APPIE
+	return bsd_set_opt_ie(drv, wpa_ie, wpa_ie_len);
+#else /* IEEE80211_IOC_APPIE */
+	return set80211var(drv, IEEE80211_IOC_OPTIE, wpa_ie, wpa_ie_len);
+#endif /* IEEE80211_IOC_APPIE */
+}
+
+static int
+wpa_driver_bsd_set_wpa_internal(void *priv, int wpa, int privacy)
+{
+	int ret = 0;
+
+	wpa_printf(MSG_DEBUG, "%s: wpa=%d privacy=%d",
+		__func__, wpa, privacy);
+
+	if (!wpa && wpa_driver_bsd_set_wpa_ie(priv, NULL, 0) < 0)
+		ret = -1;
+	if (set80211param(priv, IEEE80211_IOC_PRIVACY, privacy) < 0)
+		ret = -1;
+	if (set80211param(priv, IEEE80211_IOC_WPA, wpa) < 0)
+		ret = -1;
+
+	return ret;
+}
+
+static int
+wpa_driver_bsd_set_wpa(void *priv, int enabled)
+{
+	wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
+
+	return wpa_driver_bsd_set_wpa_internal(priv, enabled ? 3 : 0, enabled);
+}
+
+static int
+wpa_driver_bsd_set_countermeasures(void *priv, int enabled)
+{
+	wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
+	return set80211param(priv, IEEE80211_IOC_COUNTERMEASURES, enabled);
+}
+
+
+static int
+wpa_driver_bsd_set_drop_unencrypted(void *priv, int enabled)
+{
+	wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
+	return set80211param(priv, IEEE80211_IOC_DROPUNENCRYPTED, enabled);
+}
+
+static int
+wpa_driver_bsd_deauthenticate(void *priv, const u8 *addr, u16 reason_code)
+{
+	return bsd_send_mlme_param(priv, IEEE80211_MLME_DEAUTH, reason_code,
+				   addr);
+}
+
+static int
+wpa_driver_bsd_set_auth_alg(void *priv, int auth_alg)
+{
+	int authmode;
+
+	if ((auth_alg & WPA_AUTH_ALG_OPEN) &&
+	    (auth_alg & WPA_AUTH_ALG_SHARED))
+		authmode = IEEE80211_AUTH_AUTO;
+	else if (auth_alg & WPA_AUTH_ALG_SHARED)
+		authmode = IEEE80211_AUTH_SHARED;
+	else
+		authmode = IEEE80211_AUTH_OPEN;
+
+	return set80211param(priv, IEEE80211_IOC_AUTHMODE, authmode);
+}
+
+static void
+handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
+{
+	struct bsd_driver_data *drv = ctx;
+
+	drv_event_eapol_rx(drv->ctx, src_addr, buf, len);
+}
+
+static int
+wpa_driver_bsd_associate(void *priv, struct wpa_driver_associate_params *params)
+{
+	struct bsd_driver_data *drv = priv;
+	struct ieee80211req_mlme mlme;
+	u32 mode;
+	int privacy;
+	int ret = 0;
+
+	wpa_printf(MSG_DEBUG,
+		"%s: ssid '%.*s' wpa ie len %u pairwise %u group %u key mgmt %u"
+		, __func__
+		   , (unsigned int) params->ssid_len, params->ssid
+		, (unsigned int) params->wpa_ie_len
+		, params->pairwise_suite
+		, params->group_suite
+		, params->key_mgmt_suite
+	);
+
+	switch (params->mode) {
+	case IEEE80211_MODE_INFRA:
+		mode = 0 /* STA */;
+		break;
+	case IEEE80211_MODE_IBSS:
+		mode = IFM_IEEE80211_IBSS;
+		break;
+	case IEEE80211_MODE_AP:
+		mode = IFM_IEEE80211_HOSTAP;
+		break;
+	default:
+		wpa_printf(MSG_ERROR, "%s: unknown operation mode", __func__);
+		return -1;
+	}
+	if (bsd_set_mediaopt(drv, IFM_OMASK, mode) < 0) {
+		wpa_printf(MSG_ERROR, "%s: failed to set operation mode",
+			   __func__);
+		return -1;
+	}
+
+	if (params->mode == IEEE80211_MODE_AP) {
+		drv->sock_xmit = l2_packet_init(drv->ifname, NULL, ETH_P_EAPOL,
+						handle_read, drv, 0);
+		if (drv->sock_xmit == NULL)
+			return -1;
+		drv->is_ap = 1;
+		return 0;
+	}
+
+	if (wpa_driver_bsd_set_drop_unencrypted(drv, params->drop_unencrypted)
+	    < 0)
+		ret = -1;
+	if (wpa_driver_bsd_set_auth_alg(drv, params->auth_alg) < 0)
+		ret = -1;
+	/* XXX error handling is wrong but unclear what to do... */
+	if (wpa_driver_bsd_set_wpa_ie(drv, params->wpa_ie, params->wpa_ie_len) < 0)
+		return -1;
+
+	privacy = !(params->pairwise_suite == WPA_CIPHER_NONE &&
+	    params->group_suite == WPA_CIPHER_NONE &&
+	    params->key_mgmt_suite == WPA_KEY_MGMT_NONE &&
+	    params->wpa_ie_len == 0);
+	wpa_printf(MSG_DEBUG, "%s: set PRIVACY %u", __func__, privacy);
+
+	if (set80211param(drv, IEEE80211_IOC_PRIVACY, privacy) < 0)
+		return -1;
+
+	if (params->wpa_ie_len &&
+	    set80211param(drv, IEEE80211_IOC_WPA,
+			  params->wpa_ie[0] == WLAN_EID_RSN ? 2 : 1) < 0)
+		return -1;
+
+	os_memset(&mlme, 0, sizeof(mlme));
+	mlme.im_op = IEEE80211_MLME_ASSOC;
+	if (params->ssid != NULL)
+		os_memcpy(mlme.im_ssid, params->ssid, params->ssid_len);
+	mlme.im_ssid_len = params->ssid_len;
+	if (params->bssid != NULL)
+		os_memcpy(mlme.im_macaddr, params->bssid, IEEE80211_ADDR_LEN);
+	if (set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)) < 0)
+		return -1;
+	return ret;
+}
+
+static int
+wpa_driver_bsd_scan(void *priv, struct wpa_driver_scan_params *params)
+{
+	struct bsd_driver_data *drv = priv;
+#ifdef IEEE80211_IOC_SCAN_MAX_SSID
+	struct ieee80211_scan_req sr;
+	int i;
+#endif /* IEEE80211_IOC_SCAN_MAX_SSID */
+
+	if (bsd_set_mediaopt(drv, IFM_OMASK, 0 /* STA */) < 0) {
+		wpa_printf(MSG_ERROR, "%s: failed to set operation mode",
+			   __func__);
+		return -1;
+	}
+
+	if (set80211param(drv, IEEE80211_IOC_ROAMING,
+			  IEEE80211_ROAMING_MANUAL) < 0) {
+		wpa_printf(MSG_ERROR, "%s: failed to set "
+			   "wpa_supplicant-based roaming: %s", __func__,
+			   strerror(errno));
+		return -1;
+	}
+
+	if (wpa_driver_bsd_set_wpa(drv, 1) < 0) {
+		wpa_printf(MSG_ERROR, "%s: failed to set wpa: %s", __func__,
+			   strerror(errno));
+		return -1;
+	}
+
+	/* NB: interface must be marked UP to do a scan */
+	if (!(drv->flags & IFF_UP)) {
+		wpa_printf(MSG_DEBUG, "%s: interface is not up, cannot scan",
+			   __func__);
+		return -1;
+	}
+
+#ifdef IEEE80211_IOC_SCAN_MAX_SSID
+	os_memset(&sr, 0, sizeof(sr));
+	sr.sr_flags = IEEE80211_IOC_SCAN_ACTIVE | IEEE80211_IOC_SCAN_ONCE |
+		IEEE80211_IOC_SCAN_NOJOIN;
+	sr.sr_duration = IEEE80211_IOC_SCAN_FOREVER;
+	if (params->num_ssids > 0) {
+		sr.sr_nssid = params->num_ssids;
+#if 0
+		/* Boundary check is done by upper layer */
+		if (sr.sr_nssid > IEEE80211_IOC_SCAN_MAX_SSID)
+			sr.sr_nssid = IEEE80211_IOC_SCAN_MAX_SSID;
+#endif
+
+		/* NB: check scan cache first */
+		sr.sr_flags |= IEEE80211_IOC_SCAN_CHECK;
+	}
+	for (i = 0; i < sr.sr_nssid; i++) {
+		sr.sr_ssid[i].len = params->ssids[i].ssid_len;
+		os_memcpy(sr.sr_ssid[i].ssid, params->ssids[i].ssid,
+			  sr.sr_ssid[i].len);
+	}
+
+	/* NB: net80211 delivers a scan complete event so no need to poll */
+	return set80211var(drv, IEEE80211_IOC_SCAN_REQ, &sr, sizeof(sr));
+#else /* IEEE80211_IOC_SCAN_MAX_SSID */
+	/* set desired ssid before scan */
+	if (bsd_set_ssid(drv, params->ssids[0].ssid,
+			 params->ssids[0].ssid_len) < 0)
+		return -1;
+
+	/* NB: net80211 delivers a scan complete event so no need to poll */
+	return set80211param(drv, IEEE80211_IOC_SCAN_REQ, 0);
+#endif /* IEEE80211_IOC_SCAN_MAX_SSID */
+}
+
+static void
+wpa_driver_bsd_add_scan_entry(struct wpa_scan_results *res,
+			      struct ieee80211req_scan_result *sr)
+{
+	struct wpa_scan_res *result, **tmp;
+	size_t extra_len;
+	u8 *pos;
+
+	extra_len = 2 + sr->isr_ssid_len;
+	extra_len += 2 + sr->isr_nrates;
+	extra_len += 3; /* ERP IE */
+	extra_len += sr->isr_ie_len;
+
+	result = os_zalloc(sizeof(*result) + extra_len);
+	if (result == NULL)
+		return;
+	os_memcpy(result->bssid, sr->isr_bssid, ETH_ALEN);
+	result->freq = sr->isr_freq;
+	result->beacon_int = sr->isr_intval;
+	result->caps = sr->isr_capinfo;
+	result->qual = sr->isr_rssi;
+	result->noise = sr->isr_noise;
+
+#ifdef __FreeBSD__
+	/*
+	 * the rssi value reported by the kernel is in 0.5dB steps relative to
+	 * the reported noise floor. see ieee80211_node.h for details.
+	 */
+	result->level = sr->isr_rssi / 2 + sr->isr_noise;
+#else
+	result->level = sr->isr_rssi;
+#endif
+
+	pos = (u8 *)(result + 1);
+
+	*pos++ = WLAN_EID_SSID;
+	*pos++ = sr->isr_ssid_len;
+	os_memcpy(pos, sr + 1, sr->isr_ssid_len);
+	pos += sr->isr_ssid_len;
+
+	/*
+	 * Deal all rates as supported rate.
+	 * Because net80211 doesn't report extended supported rate or not.
+	 */
+	*pos++ = WLAN_EID_SUPP_RATES;
+	*pos++ = sr->isr_nrates;
+	os_memcpy(pos, sr->isr_rates, sr->isr_nrates);
+	pos += sr->isr_nrates;
+
+	*pos++ = WLAN_EID_ERP_INFO;
+	*pos++ = 1;
+	*pos++ = sr->isr_erp;
+
+#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+	os_memcpy(pos, (u8 *)(sr + 1) + sr->isr_ssid_len + sr->isr_meshid_len,
+		  sr->isr_ie_len);
+#else
+	os_memcpy(pos, (u8 *)(sr + 1) + sr->isr_ssid_len, sr->isr_ie_len);
+#endif
+	pos += sr->isr_ie_len;
+
+	result->ie_len = pos - (u8 *)(result + 1);
+
+	tmp = os_realloc_array(res->res, res->num + 1,
+			       sizeof(struct wpa_scan_res *));
+	if (tmp == NULL) {
+		os_free(result);
+		return;
+	}
+	tmp[res->num++] = result;
+	res->res = tmp;
+}
+
+struct wpa_scan_results *
+wpa_driver_bsd_get_scan_results2(void *priv)
+{
+	struct ieee80211req_scan_result *sr;
+	struct wpa_scan_results *res;
+	int len, rest;
+	uint8_t buf[24*1024], *pos;
+
+	len = get80211var(priv, IEEE80211_IOC_SCAN_RESULTS, buf, 24*1024);
+	if (len < 0)
+		return NULL;
+
+	res = os_zalloc(sizeof(*res));
+	if (res == NULL)
+		return NULL;
+
+	pos = buf;
+	rest = len;
+	while (rest >= sizeof(struct ieee80211req_scan_result)) {
+		sr = (struct ieee80211req_scan_result *)pos;
+		wpa_driver_bsd_add_scan_entry(res, sr);
+		pos += sr->isr_len;
+		rest -= sr->isr_len;
+	}
+
+	wpa_printf(MSG_DEBUG, "Received %d bytes of scan results (%lu BSSes)",
+		   len, (unsigned long)res->num);
+
+	return res;
+}
+
+static int wpa_driver_bsd_capa(struct bsd_driver_data *drv)
+{
+#ifdef IEEE80211_IOC_DEVCAPS
+/* kernel definitions copied from net80211/ieee80211_var.h */
+#define IEEE80211_CIPHER_WEP            0
+#define IEEE80211_CIPHER_TKIP           1
+#define IEEE80211_CIPHER_AES_CCM        3
+#define IEEE80211_CRYPTO_WEP            (1<<IEEE80211_CIPHER_WEP)
+#define IEEE80211_CRYPTO_TKIP           (1<<IEEE80211_CIPHER_TKIP)
+#define IEEE80211_CRYPTO_AES_CCM        (1<<IEEE80211_CIPHER_AES_CCM)
+#define IEEE80211_C_HOSTAP      0x00000400      /* CAPABILITY: HOSTAP avail */
+#define IEEE80211_C_WPA1        0x00800000      /* CAPABILITY: WPA1 avail */
+#define IEEE80211_C_WPA2        0x01000000      /* CAPABILITY: WPA2 avail */
+	struct ieee80211_devcaps_req devcaps;
+
+	if (get80211var(drv, IEEE80211_IOC_DEVCAPS, &devcaps,
+			sizeof(devcaps)) < 0) {
+		wpa_printf(MSG_ERROR, "failed to IEEE80211_IOC_DEVCAPS: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "%s: drivercaps=0x%08x,cryptocaps=0x%08x",
+		   __func__, devcaps.dc_drivercaps, devcaps.dc_cryptocaps);
+
+	if (devcaps.dc_drivercaps & IEEE80211_C_WPA1)
+		drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+			WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK;
+	if (devcaps.dc_drivercaps & IEEE80211_C_WPA2)
+		drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+			WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+
+	if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_WEP)
+		drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 |
+			WPA_DRIVER_CAPA_ENC_WEP104;
+	if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_TKIP)
+		drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP;
+	if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_AES_CCM)
+		drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP;
+
+	if (devcaps.dc_drivercaps & IEEE80211_C_HOSTAP)
+		drv->capa.flags |= WPA_DRIVER_FLAGS_AP;
+#undef IEEE80211_CIPHER_WEP
+#undef IEEE80211_CIPHER_TKIP
+#undef IEEE80211_CIPHER_AES_CCM
+#undef IEEE80211_CRYPTO_WEP
+#undef IEEE80211_CRYPTO_TKIP
+#undef IEEE80211_CRYPTO_AES_CCM
+#undef IEEE80211_C_HOSTAP
+#undef IEEE80211_C_WPA1
+#undef IEEE80211_C_WPA2
+#else /* IEEE80211_IOC_DEVCAPS */
+	/* For now, assume TKIP, CCMP, WPA, WPA2 are supported */
+	drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+		WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+		WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+		WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+	drv->capa.enc = WPA_DRIVER_CAPA_ENC_WEP40 |
+		WPA_DRIVER_CAPA_ENC_WEP104 |
+		WPA_DRIVER_CAPA_ENC_TKIP |
+		WPA_DRIVER_CAPA_ENC_CCMP;
+	drv->capa.flags |= WPA_DRIVER_FLAGS_AP;
+#endif /* IEEE80211_IOC_DEVCAPS */
+#ifdef IEEE80211_IOC_SCAN_MAX_SSID
+	drv->capa.max_scan_ssids = IEEE80211_IOC_SCAN_MAX_SSID;
+#else /* IEEE80211_IOC_SCAN_MAX_SSID */
+	drv->capa.max_scan_ssids = 1;
+#endif /* IEEE80211_IOC_SCAN_MAX_SSID */
+	drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
+		WPA_DRIVER_AUTH_SHARED |
+		WPA_DRIVER_AUTH_LEAP;
+	return 0;
+}
+
+static enum ieee80211_opmode
+get80211opmode(struct bsd_driver_data *drv)
+{
+	struct ifmediareq ifmr;
+
+	(void) memset(&ifmr, 0, sizeof(ifmr));
+	(void) os_strlcpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name));
+
+	if (ioctl(drv->global->sock, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) {
+		if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) {
+			if (ifmr.ifm_current & IFM_FLAG0)
+				return IEEE80211_M_AHDEMO;
+			else
+				return IEEE80211_M_IBSS;
+		}
+		if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP)
+			return IEEE80211_M_HOSTAP;
+		if (ifmr.ifm_current & IFM_IEEE80211_MONITOR)
+			return IEEE80211_M_MONITOR;
+#ifdef IEEE80211_M_MBSS
+		if (ifmr.ifm_current & IFM_IEEE80211_MBSS)
+			return IEEE80211_M_MBSS;
+#endif /* IEEE80211_M_MBSS */
+	}
+	return IEEE80211_M_STA;
+}
+
+static void *
+wpa_driver_bsd_init(void *ctx, const char *ifname, void *priv)
+{
+#define	GETPARAM(drv, param, v) \
+	(((v) = get80211param(drv, param)) != -1)
+	struct bsd_driver_data *drv;
+	int i;
+
+	drv = os_zalloc(sizeof(*drv));
+	if (drv == NULL)
+		return NULL;
+
+	drv->ifindex = if_nametoindex(ifname);
+	if (drv->ifindex == 0) {
+		wpa_printf(MSG_DEBUG, "%s: interface %s does not exist",
+			   __func__, ifname);
+		goto fail;
+	}
+
+	drv->ctx = ctx;
+	drv->global = priv;
+	os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+
+	/* Set the interface as removed until proven to work. */
+	drv->if_removed = 1;
+
+	if (!GETPARAM(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming)) {
+		wpa_printf(MSG_DEBUG, "%s: failed to get roaming state: %s",
+			__func__, strerror(errno));
+		goto fail;
+	}
+	if (!GETPARAM(drv, IEEE80211_IOC_PRIVACY, drv->prev_privacy)) {
+		wpa_printf(MSG_DEBUG, "%s: failed to get privacy state: %s",
+			__func__, strerror(errno));
+		goto fail;
+	}
+	if (!GETPARAM(drv, IEEE80211_IOC_WPA, drv->prev_wpa)) {
+		wpa_printf(MSG_DEBUG, "%s: failed to get wpa state: %s",
+			__func__, strerror(errno));
+		goto fail;
+	}
+
+	if (wpa_driver_bsd_capa(drv))
+		goto fail;
+
+	/* Update per interface supported AKMs */
+	for (i = 0; i < WPA_IF_MAX; i++)
+		drv->capa.key_mgmt_iftype[i] = drv->capa.key_mgmt;
+
+	/* Down interface during setup. */
+	if (bsd_get_iface_flags(drv) < 0)
+		goto fail;
+
+	/* Proven to work, lets go! */
+	drv->if_removed = 0;
+
+	drv->opmode = get80211opmode(drv);
+	dl_list_add(&drv->global->ifaces, &drv->list);
+
+	return drv;
+fail:
+	os_free(drv);
+	return NULL;
+#undef GETPARAM
+}
+
+static void
+wpa_driver_bsd_deinit(void *priv)
+{
+	struct bsd_driver_data *drv = priv;
+
+	if (drv->ifindex != 0 && !drv->if_removed) {
+		wpa_driver_bsd_set_wpa(drv, 0);
+
+		wpa_driver_bsd_set_wpa_internal(drv, drv->prev_wpa,
+						drv->prev_privacy);
+
+		if (set80211param(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming)
+		    < 0)
+			wpa_printf(MSG_DEBUG,
+				   "%s: failed to restore roaming state",
+				   __func__);
+	}
+
+	if (drv->sock_xmit != NULL)
+		l2_packet_deinit(drv->sock_xmit);
+	dl_list_del(&drv->list);
+	os_free(drv);
+}
+
+static int
+wpa_driver_bsd_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+	struct bsd_driver_data *drv = priv;
+
+	os_memcpy(capa, &drv->capa, sizeof(*capa));
+	return 0;
+}
+#endif /* HOSTAPD */
+
+static void *
+bsd_global_init(void *ctx)
+{
+	struct bsd_driver_global *global;
+#if defined(RO_MSGFILTER) || defined(ROUTE_MSGFILTER)
+	unsigned char msgfilter[] = {
+		RTM_IEEE80211,
+		RTM_IFINFO, RTM_IFANNOUNCE,
+	};
+#endif
+#ifdef ROUTE_MSGFILTER
+	unsigned int i, msgfilter_mask;
+#endif
+
+	global = os_zalloc(sizeof(*global));
+	if (global == NULL)
+		return NULL;
+
+	global->ctx = ctx;
+	dl_list_init(&global->ifaces);
+
+	global->sock = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+	if (global->sock < 0) {
+		wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s",
+			   strerror(errno));
+		goto fail1;
+	}
+
+	global->route = socket(PF_ROUTE,
+			       SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
+	if (global->route < 0) {
+		wpa_printf(MSG_ERROR, "socket[PF_ROUTE,SOCK_RAW]: %s",
+			   strerror(errno));
+		goto fail;
+	}
+
+#if defined(RO_MSGFILTER)
+	if (setsockopt(global->route, PF_ROUTE, RO_MSGFILTER,
+	    &msgfilter, sizeof(msgfilter)) < 0)
+		wpa_printf(MSG_ERROR, "socket[PF_ROUTE,RO_MSGFILTER]: %s",
+			   strerror(errno));
+#elif defined(ROUTE_MSGFILTER)
+	msgfilter_mask = 0;
+	for (i = 0; i < (sizeof(msgfilter) / sizeof(msgfilter[0])); i++)
+		msgfilter_mask |= ROUTE_FILTER(msgfilter[i]);
+	if (setsockopt(global->route, PF_ROUTE, ROUTE_MSGFILTER,
+	    &msgfilter_mask, sizeof(msgfilter_mask)) < 0)
+		wpa_printf(MSG_ERROR, "socket[PF_ROUTE,ROUTE_MSGFILTER]: %s",
+			   strerror(errno));
+#endif
+
+	eloop_register_read_sock(global->route, bsd_wireless_event_receive,
+				 NULL, global);
+
+	return global;
+
+fail:
+	close(global->sock);
+fail1:
+	os_free(global);
+	return NULL;
+}
+
+static void
+bsd_global_deinit(void *priv)
+{
+	struct bsd_driver_global *global = priv;
+
+	eloop_unregister_read_sock(global->route);
+	(void) close(global->route);
+	(void) close(global->sock);
+	os_free(global);
+}
+
+
+const struct wpa_driver_ops wpa_driver_bsd_ops = {
+	.name			= "bsd",
+	.desc			= "BSD 802.11 support",
+	.global_init		= bsd_global_init,
+	.global_deinit		= bsd_global_deinit,
+#ifdef HOSTAPD
+	.hapd_init		= bsd_init,
+	.hapd_deinit		= bsd_deinit,
+	.set_privacy		= bsd_set_privacy,
+	.get_seqnum		= bsd_get_seqnum,
+	.flush			= bsd_flush,
+	.read_sta_data		= bsd_read_sta_driver_data,
+	.sta_disassoc		= bsd_sta_disassoc,
+	.sta_deauth		= bsd_sta_deauth,
+	.sta_set_flags		= bsd_set_sta_authorized,
+#else /* HOSTAPD */
+	.init2			= wpa_driver_bsd_init,
+	.deinit			= wpa_driver_bsd_deinit,
+	.get_bssid		= wpa_driver_bsd_get_bssid,
+	.get_ssid		= wpa_driver_bsd_get_ssid,
+	.set_countermeasures	= wpa_driver_bsd_set_countermeasures,
+	.scan2			= wpa_driver_bsd_scan,
+	.get_scan_results2	= wpa_driver_bsd_get_scan_results2,
+	.deauthenticate		= wpa_driver_bsd_deauthenticate,
+	.associate		= wpa_driver_bsd_associate,
+	.get_capa		= wpa_driver_bsd_get_capa,
+#endif /* HOSTAPD */
+	.set_freq		= bsd_set_freq,
+	.set_key		= bsd_set_key,
+	.set_ieee8021x		= bsd_set_ieee8021x,
+	.hapd_set_ssid		= bsd_set_ssid,
+	.hapd_get_ssid		= bsd_get_ssid,
+	.hapd_send_eapol	= bsd_send_eapol,
+	.set_generic_elem	= bsd_set_opt_ie,
+};
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_common.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_common.c
new file mode 100644
index 0000000..f3625e8
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_common.c
@@ -0,0 +1,364 @@
+/*
+ * Common driver-related functions
+ * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include "utils/common.h"
+#include "driver.h"
+
+void wpa_scan_results_free(struct wpa_scan_results *res)
+{
+	size_t i;
+
+	if (res == NULL)
+		return;
+
+	for (i = 0; i < res->num; i++)
+		os_free(res->res[i]);
+	os_free(res->res);
+	os_free(res);
+}
+
+
+const char * event_to_string(enum wpa_event_type event)
+{
+#define E2S(n) case EVENT_ ## n: return #n
+	switch (event) {
+	E2S(ASSOC);
+	E2S(DISASSOC);
+	E2S(MICHAEL_MIC_FAILURE);
+	E2S(SCAN_RESULTS);
+	E2S(ASSOCINFO);
+	E2S(INTERFACE_STATUS);
+	E2S(PMKID_CANDIDATE);
+	E2S(TDLS);
+	E2S(FT_RESPONSE);
+	E2S(IBSS_RSN_START);
+	E2S(AUTH);
+	E2S(DEAUTH);
+	E2S(ASSOC_REJECT);
+	E2S(AUTH_TIMED_OUT);
+	E2S(ASSOC_TIMED_OUT);
+	E2S(WPS_BUTTON_PUSHED);
+	E2S(TX_STATUS);
+	E2S(RX_FROM_UNKNOWN);
+	E2S(RX_MGMT);
+	E2S(REMAIN_ON_CHANNEL);
+	E2S(CANCEL_REMAIN_ON_CHANNEL);
+	E2S(RX_PROBE_REQ);
+	E2S(NEW_STA);
+	E2S(EAPOL_RX);
+	E2S(SIGNAL_CHANGE);
+	E2S(INTERFACE_ENABLED);
+	E2S(INTERFACE_DISABLED);
+	E2S(CHANNEL_LIST_CHANGED);
+	E2S(INTERFACE_UNAVAILABLE);
+	E2S(BEST_CHANNEL);
+	E2S(UNPROT_DEAUTH);
+	E2S(UNPROT_DISASSOC);
+	E2S(STATION_LOW_ACK);
+	E2S(IBSS_PEER_LOST);
+	E2S(DRIVER_GTK_REKEY);
+	E2S(SCHED_SCAN_STOPPED);
+	E2S(DRIVER_CLIENT_POLL_OK);
+	E2S(EAPOL_TX_STATUS);
+	E2S(CH_SWITCH);
+	E2S(CH_SWITCH_STARTED);
+	E2S(WNM);
+	E2S(CONNECT_FAILED_REASON);
+	E2S(DFS_RADAR_DETECTED);
+	E2S(DFS_CAC_FINISHED);
+	E2S(DFS_CAC_ABORTED);
+	E2S(DFS_NOP_FINISHED);
+	E2S(SURVEY);
+	E2S(SCAN_STARTED);
+	E2S(AVOID_FREQUENCIES);
+	E2S(NEW_PEER_CANDIDATE);
+	E2S(ACS_CHANNEL_SELECTED);
+	E2S(DFS_CAC_STARTED);
+	E2S(P2P_LO_STOP);
+	E2S(BEACON_LOSS);
+	E2S(DFS_PRE_CAC_EXPIRED);
+	E2S(EXTERNAL_AUTH);
+	E2S(PORT_AUTHORIZED);
+	E2S(STATION_OPMODE_CHANGED);
+	E2S(INTERFACE_MAC_CHANGED);
+	E2S(WDS_STA_INTERFACE_STATUS);
+	E2S(UPDATE_DH);
+	E2S(UNPROT_BEACON);
+	E2S(TX_WAIT_EXPIRE);
+	E2S(BSS_COLOR_COLLISION);
+	E2S(CCA_STARTED_NOTIFY);
+	E2S(CCA_ABORTED_NOTIFY);
+	E2S(CCA_NOTIFY);
+	E2S(PASN_AUTH);
+	E2S(LINK_CH_SWITCH);
+	E2S(LINK_CH_SWITCH_STARTED);
+	E2S(TID_LINK_MAP);
+	E2S(LINK_RECONFIG);
+	}
+
+	return "UNKNOWN";
+#undef E2S
+}
+
+
+const char * channel_width_to_string(enum chan_width width)
+{
+	switch (width) {
+	case CHAN_WIDTH_20_NOHT:
+		return "20 MHz (no HT)";
+	case CHAN_WIDTH_20:
+		return "20 MHz";
+	case CHAN_WIDTH_40:
+		return "40 MHz";
+	case CHAN_WIDTH_80:
+		return "80 MHz";
+	case CHAN_WIDTH_80P80:
+		return "80+80 MHz";
+	case CHAN_WIDTH_160:
+		return "160 MHz";
+	case CHAN_WIDTH_320:
+		return "320 MHz";
+	default:
+		return "unknown";
+	}
+}
+
+
+int channel_width_to_int(enum chan_width width)
+{
+	switch (width) {
+	case CHAN_WIDTH_20_NOHT:
+	case CHAN_WIDTH_20:
+		return 20;
+	case CHAN_WIDTH_40:
+		return 40;
+	case CHAN_WIDTH_80:
+		return 80;
+	case CHAN_WIDTH_80P80:
+	case CHAN_WIDTH_160:
+		return 160;
+	case CHAN_WIDTH_320:
+		return 320;
+	default:
+		return 0;
+	}
+}
+
+
+int ht_supported(const struct hostapd_hw_modes *mode)
+{
+	if (!(mode->flags & HOSTAPD_MODE_FLAG_HT_INFO_KNOWN)) {
+		/*
+		 * The driver did not indicate whether it supports HT. Assume
+		 * it does to avoid connection issues.
+		 */
+		return 1;
+	}
+
+	/*
+	 * IEEE Std 802.11n-2009 20.1.1:
+	 * An HT non-AP STA shall support all EQM rates for one spatial stream.
+	 */
+	return mode->mcs_set[0] == 0xff;
+}
+
+
+int vht_supported(const struct hostapd_hw_modes *mode)
+{
+	if (!(mode->flags & HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN)) {
+		/*
+		 * The driver did not indicate whether it supports VHT. Assume
+		 * it does to avoid connection issues.
+		 */
+		return 1;
+	}
+
+	/*
+	 * A VHT non-AP STA shall support MCS 0-7 for one spatial stream.
+	 * TODO: Verify if this complies with the standard
+	 */
+	return (mode->vht_mcs_set[0] & 0x3) != 3;
+}
+
+
+bool he_supported(const struct hostapd_hw_modes *hw_mode,
+		  enum ieee80211_op_mode op_mode)
+{
+	if (!(hw_mode->flags & HOSTAPD_MODE_FLAG_HE_INFO_KNOWN)) {
+		/*
+		 * The driver did not indicate whether it supports HE. Assume
+		 * it does to avoid connection issues.
+		 */
+		return true;
+	}
+
+	return hw_mode->he_capab[op_mode].he_supported;
+}
+
+
+static int wpa_check_wowlan_trigger(const char *start, const char *trigger,
+				    int capa_trigger, u8 *param_trigger)
+{
+	if (os_strcmp(start, trigger) != 0)
+		return 0;
+	if (!capa_trigger)
+		return 0;
+
+	*param_trigger = 1;
+	return 1;
+}
+
+
+struct wowlan_triggers *
+wpa_get_wowlan_triggers(const char *wowlan_triggers,
+			const struct wpa_driver_capa *capa)
+{
+	struct wowlan_triggers *triggers;
+	char *start, *end, *buf;
+	int last;
+
+	if (!wowlan_triggers)
+		return NULL;
+
+	buf = os_strdup(wowlan_triggers);
+	if (buf == NULL)
+		return NULL;
+
+	triggers = os_zalloc(sizeof(*triggers));
+	if (triggers == NULL)
+		goto out;
+
+#define CHECK_TRIGGER(trigger) \
+	wpa_check_wowlan_trigger(start, #trigger,			\
+				  capa->wowlan_triggers.trigger,	\
+				  &triggers->trigger)
+
+	start = buf;
+	while (*start != '\0') {
+		while (isblank((unsigned char) *start))
+			start++;
+		if (*start == '\0')
+			break;
+		end = start;
+		while (!isblank((unsigned char) *end) && *end != '\0')
+			end++;
+		last = *end == '\0';
+		*end = '\0';
+
+		if (!CHECK_TRIGGER(any) &&
+		    !CHECK_TRIGGER(disconnect) &&
+		    !CHECK_TRIGGER(magic_pkt) &&
+		    !CHECK_TRIGGER(gtk_rekey_failure) &&
+		    !CHECK_TRIGGER(eap_identity_req) &&
+		    !CHECK_TRIGGER(four_way_handshake) &&
+		    !CHECK_TRIGGER(rfkill_release)) {
+			wpa_printf(MSG_DEBUG,
+				   "Unknown/unsupported wowlan trigger '%s'",
+				   start);
+			os_free(triggers);
+			triggers = NULL;
+			goto out;
+		}
+
+		if (last)
+			break;
+		start = end + 1;
+	}
+#undef CHECK_TRIGGER
+
+out:
+	os_free(buf);
+	return triggers;
+}
+
+
+const char * driver_flag_to_string(u64 flag)
+{
+#define DF2S(x) case WPA_DRIVER_FLAGS_ ## x: return #x
+	switch (flag) {
+	DF2S(DRIVER_IE);
+	DF2S(SET_KEYS_AFTER_ASSOC);
+	DF2S(DFS_OFFLOAD);
+	DF2S(4WAY_HANDSHAKE_PSK);
+	DF2S(4WAY_HANDSHAKE_8021X);
+	DF2S(WIRED);
+	DF2S(SME);
+	DF2S(AP);
+	DF2S(SET_KEYS_AFTER_ASSOC_DONE);
+	DF2S(HT_2040_COEX);
+	DF2S(P2P_CONCURRENT);
+	DF2S(P2P_DEDICATED_INTERFACE);
+	DF2S(P2P_CAPABLE);
+	DF2S(AP_TEARDOWN_SUPPORT);
+	DF2S(P2P_MGMT_AND_NON_P2P);
+	DF2S(VALID_ERROR_CODES);
+	DF2S(OFFCHANNEL_TX);
+	DF2S(EAPOL_TX_STATUS);
+	DF2S(DEAUTH_TX_STATUS);
+	DF2S(BSS_SELECTION);
+	DF2S(TDLS_SUPPORT);
+	DF2S(TDLS_EXTERNAL_SETUP);
+	DF2S(PROBE_RESP_OFFLOAD);
+	DF2S(AP_UAPSD);
+	DF2S(INACTIVITY_TIMER);
+	DF2S(AP_MLME);
+	DF2S(SAE);
+	DF2S(OBSS_SCAN);
+	DF2S(IBSS);
+	DF2S(RADAR);
+	DF2S(DEDICATED_P2P_DEVICE);
+	DF2S(QOS_MAPPING);
+	DF2S(AP_CSA);
+	DF2S(MESH);
+	DF2S(ACS_OFFLOAD);
+	DF2S(KEY_MGMT_OFFLOAD);
+	DF2S(TDLS_CHANNEL_SWITCH);
+	DF2S(HT_IBSS);
+	DF2S(VHT_IBSS);
+	DF2S(SUPPORT_HW_MODE_ANY);
+	DF2S(OFFCHANNEL_SIMULTANEOUS);
+	DF2S(FULL_AP_CLIENT_STATE);
+	DF2S(P2P_LISTEN_OFFLOAD);
+	DF2S(SUPPORT_FILS);
+	DF2S(BEACON_RATE_LEGACY);
+	DF2S(BEACON_RATE_HT);
+	DF2S(BEACON_RATE_VHT);
+	DF2S(MGMT_TX_RANDOM_TA);
+	DF2S(MGMT_TX_RANDOM_TA_CONNECTED);
+	DF2S(SCHED_SCAN_RELATIVE_RSSI);
+	DF2S(HE_CAPABILITIES);
+	DF2S(FILS_SK_OFFLOAD);
+	DF2S(OCE_STA);
+	DF2S(OCE_AP);
+	DF2S(OCE_STA_CFON);
+	DF2S(MFP_OPTIONAL);
+	DF2S(SELF_MANAGED_REGULATORY);
+	DF2S(FTM_RESPONDER);
+	DF2S(CONTROL_PORT);
+	DF2S(VLAN_OFFLOAD);
+	DF2S(UPDATE_FT_IES);
+	DF2S(SAFE_PTK0_REKEYS);
+	DF2S(BEACON_PROTECTION);
+	DF2S(EXTENDED_KEY_ID);
+	}
+	return "UNKNOWN";
+#undef DF2S
+}
+
+
+const char * driver_flag2_to_string(u64 flag2)
+{
+#define DF2S(x) case WPA_DRIVER_FLAGS2_ ## x: return #x
+	switch (flag2) {
+	DF2S(CONTROL_PORT_RX);
+	DF2S(CONTROL_PORT_TX_STATUS);
+	}
+	return "UNKNOWN";
+#undef DF2S
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_hostap.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_hostap.c
new file mode 100644
index 0000000..d3520aa
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_hostap.c
@@ -0,0 +1,1208 @@
+/*
+ * Driver interaction with Linux Host AP driver
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+
+#include "linux_wext.h"
+#include "common.h"
+#include "driver.h"
+#include "driver_wext.h"
+#include "eloop.h"
+#include "driver_hostap.h"
+
+
+#include <net/if_arp.h>
+#include <netpacket/packet.h>
+
+#include "priv_netlink.h"
+#include "netlink.h"
+#include "linux_ioctl.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+
+
+/* MTU to be set for the wlan#ap device; this is mainly needed for IEEE 802.1X
+ * frames that might be longer than normal default MTU and they are not
+ * fragmented */
+#define HOSTAPD_MTU 2290
+
+static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+
+struct hostap_driver_data {
+	struct hostapd_data *hapd;
+
+	char iface[IFNAMSIZ + 1];
+	int sock; /* raw packet socket for driver access */
+	int ioctl_sock; /* socket for ioctl() use */
+	struct netlink_data *netlink;
+
+	int we_version;
+
+	u8 *generic_ie;
+	size_t generic_ie_len;
+	u8 *wps_ie;
+	size_t wps_ie_len;
+};
+
+
+static int hostapd_ioctl(void *priv, struct prism2_hostapd_param *param,
+			 int len);
+static int hostap_set_iface_flags(void *priv, int dev_up);
+
+static void handle_data(struct hostap_driver_data *drv, u8 *buf, size_t len,
+			u16 stype)
+{
+	struct ieee80211_hdr *hdr;
+	u16 fc, ethertype;
+	u8 *pos, *sa;
+	size_t left;
+	union wpa_event_data event;
+
+	if (len < sizeof(struct ieee80211_hdr))
+		return;
+
+	hdr = (struct ieee80211_hdr *) buf;
+	fc = le_to_host16(hdr->frame_control);
+
+	if ((fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) != WLAN_FC_TODS) {
+		printf("Not ToDS data frame (fc=0x%04x)\n", fc);
+		return;
+	}
+
+	sa = hdr->addr2;
+	os_memset(&event, 0, sizeof(event));
+	event.rx_from_unknown.bssid = get_hdr_bssid(hdr, len);
+	event.rx_from_unknown.addr = sa;
+	wpa_supplicant_event(drv->hapd, EVENT_RX_FROM_UNKNOWN, &event);
+
+	pos = (u8 *) (hdr + 1);
+	left = len - sizeof(*hdr);
+
+	if (left < sizeof(rfc1042_header)) {
+		printf("Too short data frame\n");
+		return;
+	}
+
+	if (memcmp(pos, rfc1042_header, sizeof(rfc1042_header)) != 0) {
+		printf("Data frame with no RFC1042 header\n");
+		return;
+	}
+	pos += sizeof(rfc1042_header);
+	left -= sizeof(rfc1042_header);
+
+	if (left < 2) {
+		printf("No ethertype in data frame\n");
+		return;
+	}
+
+	ethertype = WPA_GET_BE16(pos);
+	pos += 2;
+	left -= 2;
+	switch (ethertype) {
+	case ETH_P_PAE:
+		drv_event_eapol_rx(drv->hapd, sa, pos, left);
+		break;
+
+	default:
+		printf("Unknown ethertype 0x%04x in data frame\n", ethertype);
+		break;
+	}
+}
+
+
+static void handle_tx_callback(struct hostap_driver_data *drv, u8 *buf,
+			       size_t len, int ok)
+{
+	struct ieee80211_hdr *hdr;
+	u16 fc;
+	union wpa_event_data event;
+
+	hdr = (struct ieee80211_hdr *) buf;
+	fc = le_to_host16(hdr->frame_control);
+
+	os_memset(&event, 0, sizeof(event));
+	event.tx_status.type = WLAN_FC_GET_TYPE(fc);
+	event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
+	event.tx_status.dst = hdr->addr1;
+	event.tx_status.data = buf;
+	event.tx_status.data_len = len;
+	event.tx_status.ack = ok;
+	wpa_supplicant_event(drv->hapd, EVENT_TX_STATUS, &event);
+}
+
+
+static void handle_frame(struct hostap_driver_data *drv, u8 *buf, size_t len)
+{
+	struct ieee80211_hdr *hdr;
+	u16 fc, type, stype;
+	size_t data_len = len;
+	int ver;
+	union wpa_event_data event;
+
+	/* PSPOLL is only 16 bytes, but driver does not (at least yet) pass
+	 * these to user space */
+	if (len < 24) {
+		wpa_printf(MSG_MSGDUMP, "handle_frame: too short (%lu)",
+			   (unsigned long) len);
+		return;
+	}
+
+	hdr = (struct ieee80211_hdr *) buf;
+	fc = le_to_host16(hdr->frame_control);
+	type = WLAN_FC_GET_TYPE(fc);
+	stype = WLAN_FC_GET_STYPE(fc);
+
+	if (type != WLAN_FC_TYPE_MGMT || stype != WLAN_FC_STYPE_BEACON) {
+		wpa_hexdump(MSG_MSGDUMP, "Received management frame",
+			    buf, len);
+	}
+
+	ver = fc & WLAN_FC_PVER;
+
+	/* protocol version 2 is reserved for indicating ACKed frame (TX
+	 * callbacks), and version 1 for indicating failed frame (no ACK, TX
+	 * callbacks) */
+	if (ver == 1 || ver == 2) {
+		handle_tx_callback(drv, buf, data_len, ver == 2 ? 1 : 0);
+		return;
+	} else if (ver != 0) {
+		printf("unknown protocol version %d\n", ver);
+		return;
+	}
+
+	switch (type) {
+	case WLAN_FC_TYPE_MGMT:
+		os_memset(&event, 0, sizeof(event));
+		event.rx_mgmt.frame = buf;
+		event.rx_mgmt.frame_len = data_len;
+		wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event);
+		break;
+	case WLAN_FC_TYPE_CTRL:
+		wpa_printf(MSG_DEBUG, "CTRL");
+		break;
+	case WLAN_FC_TYPE_DATA:
+		wpa_printf(MSG_DEBUG, "DATA");
+		handle_data(drv, buf, data_len, stype);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "unknown frame type %d", type);
+		break;
+	}
+}
+
+
+static void handle_read(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct hostap_driver_data *drv = eloop_ctx;
+	int len;
+	unsigned char buf[3000];
+
+	len = recv(sock, buf, sizeof(buf), 0);
+	if (len < 0) {
+		wpa_printf(MSG_ERROR, "recv: %s", strerror(errno));
+		return;
+	}
+
+	handle_frame(drv, buf, len);
+}
+
+
+static int hostap_init_sockets(struct hostap_driver_data *drv, u8 *own_addr)
+{
+	struct ifreq ifr;
+	struct sockaddr_ll addr;
+
+	drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+	if (drv->sock < 0) {
+		wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	if (eloop_register_read_sock(drv->sock, handle_read, drv, NULL)) {
+		wpa_printf(MSG_ERROR, "Could not register read socket");
+		return -1;
+	}
+
+        memset(&ifr, 0, sizeof(ifr));
+	if (os_snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%sap",
+			drv->iface) >= (int) sizeof(ifr.ifr_name)) {
+		wpa_printf(MSG_ERROR, "hostap: AP interface name truncated");
+		return -1;
+	}
+        if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) {
+		wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s",
+			   strerror(errno));
+		return -1;
+        }
+
+	if (hostap_set_iface_flags(drv, 1)) {
+		return -1;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sll_family = AF_PACKET;
+	addr.sll_ifindex = ifr.ifr_ifindex;
+	wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d",
+		   addr.sll_ifindex);
+
+	if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		wpa_printf(MSG_ERROR, "bind: %s", strerror(errno));
+		return -1;
+	}
+
+	return linux_get_ifhwaddr(drv->sock, drv->iface, own_addr);
+}
+
+
+static int hostap_send_mlme(void *priv, const u8 *msg, size_t len, int noack,
+			    unsigned int freq,
+			    const u16 *csa_offs, size_t csa_offs_len,
+			    int no_encrypt, unsigned int wait, int link_id)
+{
+	struct hostap_driver_data *drv = priv;
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) msg;
+	int res;
+
+	/* Request TX callback */
+	hdr->frame_control |= host_to_le16(BIT(1));
+	res = send(drv->sock, msg, len, 0);
+	hdr->frame_control &= ~host_to_le16(BIT(1));
+
+	return res;
+}
+
+
+static int hostap_send_eapol(void *priv, const u8 *addr, const u8 *data,
+			     size_t data_len, int encrypt, const u8 *own_addr,
+			     u32 flags, int link_id)
+{
+	struct hostap_driver_data *drv = priv;
+	struct ieee80211_hdr *hdr;
+	size_t len;
+	u8 *pos;
+	int res;
+
+	len = sizeof(*hdr) + sizeof(rfc1042_header) + 2 + data_len;
+	hdr = os_zalloc(len);
+	if (hdr == NULL) {
+		printf("malloc() failed for hostapd_send_data(len=%lu)\n",
+		       (unsigned long) len);
+		return -1;
+	}
+
+	hdr->frame_control =
+		IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA);
+	hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS);
+	if (encrypt)
+		hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
+	memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN);
+	memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
+	memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
+
+	pos = (u8 *) (hdr + 1);
+	memcpy(pos, rfc1042_header, sizeof(rfc1042_header));
+	pos += sizeof(rfc1042_header);
+	*((u16 *) pos) = htons(ETH_P_PAE);
+	pos += 2;
+	memcpy(pos, data, data_len);
+
+	res = hostap_send_mlme(drv, (u8 *) hdr, len, 0, 0, NULL, 0, 0, 0, -1);
+	if (res < 0) {
+		wpa_printf(MSG_ERROR, "hostap_send_eapol - packet len: %lu - "
+			   "failed: %d (%s)",
+			   (unsigned long) len, errno, strerror(errno));
+	}
+	os_free(hdr);
+
+	return res;
+}
+
+
+static int hostap_sta_set_flags(void *priv, const u8 *addr,
+				unsigned int total_flags, unsigned int flags_or,
+				unsigned int flags_and)
+{
+	struct hostap_driver_data *drv = priv;
+	struct prism2_hostapd_param param;
+
+	if (flags_or & WPA_STA_AUTHORIZED)
+		flags_or = BIT(5); /* WLAN_STA_AUTHORIZED */
+	if (!(flags_and & WPA_STA_AUTHORIZED))
+		flags_and = ~BIT(5);
+	else
+		flags_and = ~0;
+	memset(&param, 0, sizeof(param));
+	param.cmd = PRISM2_HOSTAPD_SET_FLAGS_STA;
+	memcpy(param.sta_addr, addr, ETH_ALEN);
+	param.u.set_flags_sta.flags_or = flags_or;
+	param.u.set_flags_sta.flags_and = flags_and;
+	return hostapd_ioctl(drv, &param, sizeof(param));
+}
+
+
+static int hostap_set_iface_flags(void *priv, int dev_up)
+{
+	struct hostap_driver_data *drv = priv;
+	struct ifreq ifr;
+	char ifname[IFNAMSIZ];
+
+	if (os_snprintf(ifname, IFNAMSIZ, "%sap", drv->iface) >= IFNAMSIZ) {
+		wpa_printf(MSG_ERROR, "hostap: AP interface name truncated");
+		return -1;
+	}
+	if (linux_set_iface_flags(drv->ioctl_sock, ifname, dev_up) < 0)
+		return -1;
+
+	if (dev_up) {
+		memset(&ifr, 0, sizeof(ifr));
+		os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+		ifr.ifr_mtu = HOSTAPD_MTU;
+		if (ioctl(drv->ioctl_sock, SIOCSIFMTU, &ifr) != 0) {
+			wpa_printf(MSG_INFO,
+				   "Setting MTU failed - trying to survive with current value: ioctl[SIOCSIFMTU]: %s",
+				   strerror(errno));
+		}
+	}
+
+	return 0;
+}
+
+
+static int hostapd_ioctl(void *priv, struct prism2_hostapd_param *param,
+			 int len)
+{
+	struct hostap_driver_data *drv = priv;
+	struct iwreq iwr;
+
+	memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+	iwr.u.data.pointer = (caddr_t) param;
+	iwr.u.data.length = len;
+
+	if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_HOSTAPD, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[PRISM2_IOCTL_HOSTAPD]: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int wpa_driver_hostap_set_key(void *priv,
+				     struct wpa_driver_set_key_params *params)
+{
+	struct hostap_driver_data *drv = priv;
+	struct prism2_hostapd_param *param;
+	u8 *buf;
+	size_t blen;
+	int ret = 0;
+	enum wpa_alg alg = params->alg;
+	const u8 *addr = params->addr;
+	int key_idx = params->key_idx;
+	int set_tx = params->set_tx;
+	const u8 *key = params->key;
+	size_t key_len = params->key_len;
+
+	blen = sizeof(*param) + key_len;
+	buf = os_zalloc(blen);
+	if (buf == NULL)
+		return -1;
+
+	param = (struct prism2_hostapd_param *) buf;
+	param->cmd = PRISM2_SET_ENCRYPTION;
+	if (addr == NULL)
+		memset(param->sta_addr, 0xff, ETH_ALEN);
+	else
+		memcpy(param->sta_addr, addr, ETH_ALEN);
+	switch (alg) {
+	case WPA_ALG_NONE:
+		os_strlcpy((char *) param->u.crypt.alg, "NONE",
+			   HOSTAP_CRYPT_ALG_NAME_LEN);
+		break;
+	case WPA_ALG_WEP:
+		os_strlcpy((char *) param->u.crypt.alg, "WEP",
+			   HOSTAP_CRYPT_ALG_NAME_LEN);
+		break;
+	case WPA_ALG_TKIP:
+		os_strlcpy((char *) param->u.crypt.alg, "TKIP",
+			   HOSTAP_CRYPT_ALG_NAME_LEN);
+		break;
+	case WPA_ALG_CCMP:
+		os_strlcpy((char *) param->u.crypt.alg, "CCMP",
+			   HOSTAP_CRYPT_ALG_NAME_LEN);
+		break;
+	default:
+		os_free(buf);
+		return -1;
+	}
+	param->u.crypt.flags = set_tx ? HOSTAP_CRYPT_FLAG_SET_TX_KEY : 0;
+	param->u.crypt.idx = key_idx;
+	param->u.crypt.key_len = key_len;
+	memcpy((u8 *) (param + 1), key, key_len);
+
+	if (hostapd_ioctl(drv, param, blen)) {
+		printf("Failed to set encryption.\n");
+		ret = -1;
+	}
+	free(buf);
+
+	return ret;
+}
+
+
+static int hostap_get_seqnum(const char *ifname, void *priv, const u8 *addr,
+			     int idx, int link_id, u8 *seq)
+{
+	struct hostap_driver_data *drv = priv;
+	struct prism2_hostapd_param *param;
+	u8 *buf;
+	size_t blen;
+	int ret = 0;
+
+	blen = sizeof(*param) + 32;
+	buf = os_zalloc(blen);
+	if (buf == NULL)
+		return -1;
+
+	param = (struct prism2_hostapd_param *) buf;
+	param->cmd = PRISM2_GET_ENCRYPTION;
+	if (addr == NULL)
+		os_memset(param->sta_addr, 0xff, ETH_ALEN);
+	else
+		os_memcpy(param->sta_addr, addr, ETH_ALEN);
+	param->u.crypt.idx = idx;
+
+	if (hostapd_ioctl(drv, param, blen)) {
+		printf("Failed to get encryption.\n");
+		ret = -1;
+	} else {
+		os_memcpy(seq, param->u.crypt.seq, 8);
+	}
+	os_free(buf);
+
+	return ret;
+}
+
+
+static int hostap_ioctl_prism2param(void *priv, int param, int value)
+{
+	struct hostap_driver_data *drv = priv;
+	struct iwreq iwr;
+	int *i;
+
+	memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+	i = (int *) iwr.u.name;
+	*i++ = param;
+	*i++ = value;
+
+	if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_PRISM2_PARAM, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[PRISM2_IOCTL_PRISM2_PARAM]: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int hostap_set_ieee8021x(void *priv, struct wpa_bss_params *params)
+{
+	struct hostap_driver_data *drv = priv;
+	int enabled = params->enabled;
+
+	/* enable kernel driver support for IEEE 802.1X */
+	if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_IEEE_802_1X, enabled)) {
+		printf("Could not setup IEEE 802.1X support in kernel driver."
+		       "\n");
+		return -1;
+	}
+
+	if (!enabled)
+		return 0;
+
+	/* use host driver implementation of encryption to allow
+	 * individual keys and passing plaintext EAPOL frames */
+	if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOST_DECRYPT, 1) ||
+	    hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOST_ENCRYPT, 1)) {
+		printf("Could not setup host-based encryption in kernel "
+		       "driver.\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int hostap_set_privacy(void *priv, int enabled)
+{
+	struct hostap_drvier_data *drv = priv;
+
+	return hostap_ioctl_prism2param(drv, PRISM2_PARAM_PRIVACY_INVOKED,
+					enabled);
+}
+
+
+static int hostap_set_ssid(void *priv, const u8 *buf, int len)
+{
+	struct hostap_driver_data *drv = priv;
+	struct iwreq iwr;
+
+	memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+	iwr.u.essid.flags = 1; /* SSID active */
+	iwr.u.essid.pointer = (caddr_t) buf;
+	iwr.u.essid.length = len + 1;
+
+	if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID,len=%d]: %s",
+			   len, strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int hostap_flush(void *priv)
+{
+	struct hostap_driver_data *drv = priv;
+	struct prism2_hostapd_param param;
+
+	memset(&param, 0, sizeof(param));
+	param.cmd = PRISM2_HOSTAPD_FLUSH;
+	return hostapd_ioctl(drv, &param, sizeof(param));
+}
+
+
+static int hostap_read_sta_data(void *priv,
+				struct hostap_sta_driver_data *data,
+				const u8 *addr)
+{
+	struct hostap_driver_data *drv = priv;
+	char buf[1024], line[128], *pos;
+	FILE *f;
+	unsigned long val;
+
+	memset(data, 0, sizeof(*data));
+	snprintf(buf, sizeof(buf), "/proc/net/hostap/%s/" MACSTR,
+		 drv->iface, MAC2STR(addr));
+
+	f = fopen(buf, "r");
+	if (!f)
+		return -1;
+	/* Need to read proc file with in one piece, so use large enough
+	 * buffer. */
+	setbuffer(f, buf, sizeof(buf));
+
+	while (fgets(line, sizeof(line), f)) {
+		pos = strchr(line, '=');
+		if (!pos)
+			continue;
+		*pos++ = '\0';
+		val = strtoul(pos, NULL, 10);
+		if (strcmp(line, "rx_packets") == 0)
+			data->rx_packets = val;
+		else if (strcmp(line, "tx_packets") == 0)
+			data->tx_packets = val;
+		else if (strcmp(line, "rx_bytes") == 0)
+			data->rx_bytes = val;
+		else if (strcmp(line, "tx_bytes") == 0)
+			data->tx_bytes = val;
+	}
+
+	fclose(f);
+
+	return 0;
+}
+
+
+static int hostap_sta_add(void *priv, struct hostapd_sta_add_params *params)
+{
+	struct hostap_driver_data *drv = priv;
+	struct prism2_hostapd_param param;
+	int tx_supp_rates = 0;
+	size_t i;
+
+#define WLAN_RATE_1M BIT(0)
+#define WLAN_RATE_2M BIT(1)
+#define WLAN_RATE_5M5 BIT(2)
+#define WLAN_RATE_11M BIT(3)
+
+	for (i = 0; i < params->supp_rates_len; i++) {
+		if ((params->supp_rates[i] & 0x7f) == 2)
+			tx_supp_rates |= WLAN_RATE_1M;
+		if ((params->supp_rates[i] & 0x7f) == 4)
+			tx_supp_rates |= WLAN_RATE_2M;
+		if ((params->supp_rates[i] & 0x7f) == 11)
+			tx_supp_rates |= WLAN_RATE_5M5;
+		if ((params->supp_rates[i] & 0x7f) == 22)
+			tx_supp_rates |= WLAN_RATE_11M;
+	}
+
+	memset(&param, 0, sizeof(param));
+	param.cmd = PRISM2_HOSTAPD_ADD_STA;
+	memcpy(param.sta_addr, params->addr, ETH_ALEN);
+	param.u.add_sta.aid = params->aid;
+	param.u.add_sta.capability = params->capability;
+	param.u.add_sta.tx_supp_rates = tx_supp_rates;
+	return hostapd_ioctl(drv, &param, sizeof(param));
+}
+
+
+static int hostap_sta_remove(void *priv, const u8 *addr)
+{
+	struct hostap_driver_data *drv = priv;
+	struct prism2_hostapd_param param;
+
+	hostap_sta_set_flags(drv, addr, 0, 0, ~WPA_STA_AUTHORIZED);
+
+	memset(&param, 0, sizeof(param));
+	param.cmd = PRISM2_HOSTAPD_REMOVE_STA;
+	memcpy(param.sta_addr, addr, ETH_ALEN);
+	if (hostapd_ioctl(drv, &param, sizeof(param))) {
+		printf("Could not remove station from kernel driver.\n");
+		return -1;
+	}
+	return 0;
+}
+
+
+static int hostap_get_inact_sec(void *priv, const u8 *addr)
+{
+	struct hostap_driver_data *drv = priv;
+	struct prism2_hostapd_param param;
+
+	memset(&param, 0, sizeof(param));
+	param.cmd = PRISM2_HOSTAPD_GET_INFO_STA;
+	memcpy(param.sta_addr, addr, ETH_ALEN);
+	if (hostapd_ioctl(drv, &param, sizeof(param))) {
+		return -1;
+	}
+
+	return param.u.get_info_sta.inactive_sec;
+}
+
+
+static int hostap_sta_clear_stats(void *priv, const u8 *addr)
+{
+	struct hostap_driver_data *drv = priv;
+	struct prism2_hostapd_param param;
+
+	memset(&param, 0, sizeof(param));
+	param.cmd = PRISM2_HOSTAPD_STA_CLEAR_STATS;
+	memcpy(param.sta_addr, addr, ETH_ALEN);
+	if (hostapd_ioctl(drv, &param, sizeof(param))) {
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int hostapd_ioctl_set_generic_elem(struct hostap_driver_data *drv)
+{
+	struct prism2_hostapd_param *param;
+	int res;
+	size_t blen, elem_len;
+
+	elem_len = drv->generic_ie_len + drv->wps_ie_len;
+	blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN + elem_len;
+	if (blen < sizeof(*param))
+		blen = sizeof(*param);
+
+	param = os_zalloc(blen);
+	if (param == NULL)
+		return -1;
+
+	param->cmd = PRISM2_HOSTAPD_SET_GENERIC_ELEMENT;
+	param->u.generic_elem.len = elem_len;
+	if (drv->generic_ie) {
+		os_memcpy(param->u.generic_elem.data, drv->generic_ie,
+			  drv->generic_ie_len);
+	}
+	if (drv->wps_ie) {
+		os_memcpy(&param->u.generic_elem.data[drv->generic_ie_len],
+			  drv->wps_ie, drv->wps_ie_len);
+	}
+	wpa_hexdump(MSG_DEBUG, "hostap: Set generic IE",
+		    param->u.generic_elem.data, elem_len);
+	res = hostapd_ioctl(drv, param, blen);
+
+	os_free(param);
+
+	return res;
+}
+
+
+static int hostap_set_generic_elem(void *priv,
+				   const u8 *elem, size_t elem_len)
+{
+	struct hostap_driver_data *drv = priv;
+
+	os_free(drv->generic_ie);
+	drv->generic_ie = NULL;
+	drv->generic_ie_len = 0;
+	if (elem) {
+		drv->generic_ie = os_memdup(elem, elem_len);
+		if (drv->generic_ie == NULL)
+			return -1;
+		drv->generic_ie_len = elem_len;
+	}
+
+	return hostapd_ioctl_set_generic_elem(drv);
+}
+
+
+static int hostap_set_ap_wps_ie(void *priv, const struct wpabuf *beacon,
+				const struct wpabuf *proberesp,
+				const struct wpabuf *assocresp)
+{
+	struct hostap_driver_data *drv = priv;
+
+	/*
+	 * Host AP driver supports only one set of extra IEs, so we need to
+	 * use the Probe Response IEs also for Beacon frames since they include
+	 * more information.
+	 */
+
+	os_free(drv->wps_ie);
+	drv->wps_ie = NULL;
+	drv->wps_ie_len = 0;
+	if (proberesp) {
+		drv->wps_ie = os_memdup(wpabuf_head(proberesp),
+					wpabuf_len(proberesp));
+		if (drv->wps_ie == NULL)
+			return -1;
+		drv->wps_ie_len = wpabuf_len(proberesp);
+	}
+
+	return hostapd_ioctl_set_generic_elem(drv);
+}
+
+
+static void
+hostapd_wireless_event_wireless_custom(struct hostap_driver_data *drv,
+				       char *custom)
+{
+	wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom);
+
+	if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) {
+		char *pos;
+		u8 addr[ETH_ALEN];
+		pos = strstr(custom, "addr=");
+		if (pos == NULL) {
+			wpa_printf(MSG_DEBUG,
+				   "MLME-MICHAELMICFAILURE.indication "
+				   "without sender address ignored");
+			return;
+		}
+		pos += 5;
+		if (hwaddr_aton(pos, addr) == 0) {
+			union wpa_event_data data;
+			os_memset(&data, 0, sizeof(data));
+			data.michael_mic_failure.unicast = 1;
+			data.michael_mic_failure.src = addr;
+			wpa_supplicant_event(drv->hapd,
+					     EVENT_MICHAEL_MIC_FAILURE, &data);
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "MLME-MICHAELMICFAILURE.indication "
+				   "with invalid MAC address");
+		}
+	}
+}
+
+
+static void hostapd_wireless_event_wireless(struct hostap_driver_data *drv,
+					    char *data, unsigned int len)
+{
+	struct iw_event iwe_buf, *iwe = &iwe_buf;
+	char *pos, *end, *custom, *buf;
+
+	pos = data;
+	end = data + len;
+
+	while ((size_t) (end - pos) >= IW_EV_LCP_LEN) {
+		/* Event data may be unaligned, so make a local, aligned copy
+		 * before processing. */
+		memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
+		wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d",
+			   iwe->cmd, iwe->len);
+		if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos)
+			return;
+
+		custom = pos + IW_EV_POINT_LEN;
+		if (drv->we_version > 18 &&
+		    (iwe->cmd == IWEVMICHAELMICFAILURE ||
+		     iwe->cmd == IWEVCUSTOM)) {
+			/* WE-19 removed the pointer from struct iw_point */
+			char *dpos = (char *) &iwe_buf.u.data.length;
+			int dlen = dpos - (char *) &iwe_buf;
+			memcpy(dpos, pos + IW_EV_LCP_LEN,
+			       sizeof(struct iw_event) - dlen);
+		} else {
+			memcpy(&iwe_buf, pos, sizeof(struct iw_event));
+			custom += IW_EV_POINT_OFF;
+		}
+
+		switch (iwe->cmd) {
+		case IWEVCUSTOM:
+			if (iwe->u.data.length > end - custom)
+				return;
+			buf = malloc(iwe->u.data.length + 1);
+			if (buf == NULL)
+				return;
+			memcpy(buf, custom, iwe->u.data.length);
+			buf[iwe->u.data.length] = '\0';
+			hostapd_wireless_event_wireless_custom(drv, buf);
+			free(buf);
+			break;
+		}
+
+		pos += iwe->len;
+	}
+}
+
+
+static void hostapd_wireless_event_rtm_newlink(void *ctx,
+					       struct ifinfomsg *ifi,
+					       u8 *buf, size_t len)
+{
+	struct hostap_driver_data *drv = ctx;
+	int attrlen, rta_len;
+	struct rtattr *attr;
+
+	/* TODO: use ifi->ifi_index to filter out wireless events from other
+	 * interfaces */
+
+	attrlen = len;
+	attr = (struct rtattr *) buf;
+
+	rta_len = RTA_ALIGN(sizeof(struct rtattr));
+	while (RTA_OK(attr, attrlen)) {
+		if (attr->rta_type == IFLA_WIRELESS) {
+			hostapd_wireless_event_wireless(
+				drv, ((char *) attr) + rta_len,
+				attr->rta_len - rta_len);
+		}
+		attr = RTA_NEXT(attr, attrlen);
+	}
+}
+
+
+static int hostap_get_we_version(struct hostap_driver_data *drv)
+{
+	struct iw_range *range;
+	struct iwreq iwr;
+	int minlen;
+	size_t buflen;
+
+	drv->we_version = 0;
+
+	/*
+	 * Use larger buffer than struct iw_range in order to allow the
+	 * structure to grow in the future.
+	 */
+	buflen = sizeof(struct iw_range) + 500;
+	range = os_zalloc(buflen);
+	if (range == NULL)
+		return -1;
+
+	memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+	iwr.u.data.pointer = (caddr_t) range;
+	iwr.u.data.length = buflen;
+
+	minlen = ((char *) &range->enc_capa) - (char *) range +
+		sizeof(range->enc_capa);
+
+	if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIWRANGE]: %s",
+			   strerror(errno));
+		os_free(range);
+		return -1;
+	} else if (iwr.u.data.length >= minlen &&
+		   range->we_version_compiled >= 18) {
+		wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d "
+			   "WE(source)=%d enc_capa=0x%x",
+			   range->we_version_compiled,
+			   range->we_version_source,
+			   range->enc_capa);
+		drv->we_version = range->we_version_compiled;
+	}
+
+	free(range);
+	return 0;
+}
+
+
+static int hostap_wireless_event_init(struct hostap_driver_data *drv)
+{
+	struct netlink_config *cfg;
+
+	hostap_get_we_version(drv);
+
+	cfg = os_zalloc(sizeof(*cfg));
+	if (cfg == NULL)
+		return -1;
+	cfg->ctx = drv;
+	cfg->newlink_cb = hostapd_wireless_event_rtm_newlink;
+	drv->netlink = netlink_init(cfg);
+	if (drv->netlink == NULL) {
+		os_free(cfg);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static void * hostap_init(struct hostapd_data *hapd,
+			  struct wpa_init_params *params)
+{
+	struct hostap_driver_data *drv;
+
+	drv = os_zalloc(sizeof(struct hostap_driver_data));
+	if (drv == NULL) {
+		printf("Could not allocate memory for hostapd driver data\n");
+		return NULL;
+	}
+
+	drv->hapd = hapd;
+	drv->ioctl_sock = drv->sock = -1;
+	memcpy(drv->iface, params->ifname, sizeof(drv->iface));
+
+	drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
+	if (drv->ioctl_sock < 0) {
+		wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s",
+			   strerror(errno));
+		os_free(drv);
+		return NULL;
+	}
+
+	if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD, 1)) {
+		wpa_printf(MSG_ERROR,
+			   "Could not enable hostapd mode for interface %s",
+			   drv->iface);
+		close(drv->ioctl_sock);
+		os_free(drv);
+		return NULL;
+	}
+
+	if (hostap_init_sockets(drv, params->own_addr) ||
+	    hostap_wireless_event_init(drv)) {
+		close(drv->ioctl_sock);
+		os_free(drv);
+		return NULL;
+	}
+
+	return drv;
+}
+
+
+static void hostap_driver_deinit(void *priv)
+{
+	struct hostap_driver_data *drv = priv;
+
+	netlink_deinit(drv->netlink);
+	(void) hostap_set_iface_flags(drv, 0);
+	(void) hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD, 0);
+	(void) hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD_STA, 0);
+
+	if (drv->ioctl_sock >= 0)
+		close(drv->ioctl_sock);
+
+	if (drv->sock >= 0)
+		close(drv->sock);
+
+	os_free(drv->generic_ie);
+	os_free(drv->wps_ie);
+
+	free(drv);
+}
+
+
+static int hostap_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+			     u16 reason, int link_id)
+{
+	struct hostap_driver_data *drv = priv;
+	struct ieee80211_mgmt mgmt;
+
+	if (is_broadcast_ether_addr(addr)) {
+		/*
+		 * New Prism2.5/3 STA firmware versions seem to have issues
+		 * with this broadcast deauth frame. This gets the firmware in
+		 * odd state where nothing works correctly, so let's skip
+		 * sending this for the hostap driver.
+		 */
+		return 0;
+	}
+
+	memset(&mgmt, 0, sizeof(mgmt));
+	mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					  WLAN_FC_STYPE_DEAUTH);
+	memcpy(mgmt.da, addr, ETH_ALEN);
+	memcpy(mgmt.sa, own_addr, ETH_ALEN);
+	memcpy(mgmt.bssid, own_addr, ETH_ALEN);
+	mgmt.u.deauth.reason_code = host_to_le16(reason);
+	return hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN +
+				sizeof(mgmt.u.deauth), 0, 0, NULL, 0, 0, 0, -1);
+}
+
+
+static int hostap_set_freq(void *priv, struct hostapd_freq_params *freq)
+{
+	struct hostap_driver_data *drv = priv;
+	struct iwreq iwr;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+	iwr.u.freq.m = freq->channel;
+	iwr.u.freq.e = 0;
+
+	if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWFREQ]: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int hostap_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
+			       u16 reason)
+{
+	struct hostap_driver_data *drv = priv;
+	struct ieee80211_mgmt mgmt;
+
+	memset(&mgmt, 0, sizeof(mgmt));
+	mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					  WLAN_FC_STYPE_DISASSOC);
+	memcpy(mgmt.da, addr, ETH_ALEN);
+	memcpy(mgmt.sa, own_addr, ETH_ALEN);
+	memcpy(mgmt.bssid, own_addr, ETH_ALEN);
+	mgmt.u.disassoc.reason_code = host_to_le16(reason);
+	return  hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN +
+				 sizeof(mgmt.u.disassoc), 0, 0, NULL, 0, 0, 0,
+				 -1);
+}
+
+
+static struct hostapd_hw_modes * hostap_get_hw_feature_data(void *priv,
+							    u16 *num_modes,
+							    u16 *flags, u8 *dfs)
+{
+	struct hostapd_hw_modes *mode;
+	int i, clen, rlen;
+	const short chan2freq[14] = {
+		2412, 2417, 2422, 2427, 2432, 2437, 2442,
+		2447, 2452, 2457, 2462, 2467, 2472, 2484
+	};
+
+	mode = os_zalloc(sizeof(struct hostapd_hw_modes));
+	if (mode == NULL)
+		return NULL;
+
+	*num_modes = 1;
+	*flags = 0;
+	*dfs = 0;
+
+	mode->mode = HOSTAPD_MODE_IEEE80211B;
+	mode->num_channels = 14;
+	mode->num_rates = 4;
+
+	clen = mode->num_channels * sizeof(struct hostapd_channel_data);
+	rlen = mode->num_rates * sizeof(int);
+
+	mode->channels = os_zalloc(clen);
+	mode->rates = os_zalloc(rlen);
+	if (mode->channels == NULL || mode->rates == NULL) {
+		os_free(mode->channels);
+		os_free(mode->rates);
+		os_free(mode);
+		return NULL;
+	}
+
+	for (i = 0; i < 14; i++) {
+		mode->channels[i].chan = i + 1;
+		mode->channels[i].freq = chan2freq[i];
+		mode->channels[i].allowed_bw = HOSTAPD_CHAN_WIDTH_20;
+		/* TODO: Get allowed channel list from the driver */
+		if (i >= 11)
+			mode->channels[i].flag = HOSTAPD_CHAN_DISABLED;
+	}
+
+	mode->rates[0] = 10;
+	mode->rates[1] = 20;
+	mode->rates[2] = 55;
+	mode->rates[3] = 110;
+
+	return mode;
+}
+
+
+static void wpa_driver_hostap_poll_client(void *priv, const u8 *own_addr,
+					  const u8 *addr, int qos)
+{
+	struct ieee80211_hdr hdr;
+
+	os_memset(&hdr, 0, sizeof(hdr));
+
+	/*
+	 * WLAN_FC_STYPE_NULLFUNC would be more appropriate,
+	 * but it is apparently not retried so TX Exc events
+	 * are not received for it.
+	 * This is the reason the driver overrides the default
+	 * handling.
+	 */
+	hdr.frame_control = IEEE80211_FC(WLAN_FC_TYPE_DATA,
+					 WLAN_FC_STYPE_DATA);
+
+	hdr.frame_control |=
+		host_to_le16(WLAN_FC_FROMDS);
+	os_memcpy(hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN);
+	os_memcpy(hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
+	os_memcpy(hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
+
+	hostap_send_mlme(priv, (u8 *)&hdr, sizeof(hdr), 0, 0, NULL, 0, 0, 0,
+			 -1);
+}
+
+
+const struct wpa_driver_ops wpa_driver_hostap_ops = {
+	.name = "hostap",
+	.desc = "Host AP driver (Intersil Prism2/2.5/3)",
+	.set_key = wpa_driver_hostap_set_key,
+	.hapd_init = hostap_init,
+	.hapd_deinit = hostap_driver_deinit,
+	.set_ieee8021x = hostap_set_ieee8021x,
+	.set_privacy = hostap_set_privacy,
+	.get_seqnum = hostap_get_seqnum,
+	.flush = hostap_flush,
+	.set_generic_elem = hostap_set_generic_elem,
+	.read_sta_data = hostap_read_sta_data,
+	.hapd_send_eapol = hostap_send_eapol,
+	.sta_set_flags = hostap_sta_set_flags,
+	.sta_deauth = hostap_sta_deauth,
+	.sta_disassoc = hostap_sta_disassoc,
+	.sta_remove = hostap_sta_remove,
+	.hapd_set_ssid = hostap_set_ssid,
+	.send_mlme = hostap_send_mlme,
+	.sta_add = hostap_sta_add,
+	.get_inact_sec = hostap_get_inact_sec,
+	.sta_clear_stats = hostap_sta_clear_stats,
+	.get_hw_feature_data = hostap_get_hw_feature_data,
+	.set_ap_wps_ie = hostap_set_ap_wps_ie,
+	.set_freq = hostap_set_freq,
+	.poll_client = wpa_driver_hostap_poll_client,
+};
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_hostap.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_hostap.h
new file mode 100644
index 0000000..ac0b83a
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_hostap.h
@@ -0,0 +1,208 @@
+/*
+ * Driver interaction with Linux Host AP driver
+ * Copyright (c) 2002-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HOSTAP_DRIVER_H
+#define HOSTAP_DRIVER_H
+
+/* netdevice private ioctls (used, e.g., with iwpriv from user space) */
+
+/* New wireless extensions API - SET/GET convention (even ioctl numbers are
+ * root only)
+ */
+#define PRISM2_IOCTL_PRISM2_PARAM (SIOCIWFIRSTPRIV + 0)
+#define PRISM2_IOCTL_GET_PRISM2_PARAM (SIOCIWFIRSTPRIV + 1)
+#define PRISM2_IOCTL_WRITEMIF (SIOCIWFIRSTPRIV + 2)
+#define PRISM2_IOCTL_READMIF (SIOCIWFIRSTPRIV + 3)
+#define PRISM2_IOCTL_MONITOR (SIOCIWFIRSTPRIV + 4)
+#define PRISM2_IOCTL_RESET (SIOCIWFIRSTPRIV + 6)
+#define PRISM2_IOCTL_INQUIRE (SIOCIWFIRSTPRIV + 8)
+#define PRISM2_IOCTL_WDS_ADD (SIOCIWFIRSTPRIV + 10)
+#define PRISM2_IOCTL_WDS_DEL (SIOCIWFIRSTPRIV + 12)
+#define PRISM2_IOCTL_SET_RID_WORD (SIOCIWFIRSTPRIV + 14)
+#define PRISM2_IOCTL_MACCMD (SIOCIWFIRSTPRIV + 16)
+#define PRISM2_IOCTL_ADDMAC (SIOCIWFIRSTPRIV + 18)
+#define PRISM2_IOCTL_DELMAC (SIOCIWFIRSTPRIV + 20)
+#define PRISM2_IOCTL_KICKMAC (SIOCIWFIRSTPRIV + 22)
+
+/* following are not in SIOCGIWPRIV list; check permission in the driver code
+ */
+#define PRISM2_IOCTL_DOWNLOAD (SIOCDEVPRIVATE + 13)
+#define PRISM2_IOCTL_HOSTAPD (SIOCDEVPRIVATE + 14)
+
+
+/* PRISM2_IOCTL_PRISM2_PARAM ioctl() subtypes: */
+enum {
+	/* PRISM2_PARAM_PTYPE = 1, */ /* REMOVED 2003-10-22 */
+	PRISM2_PARAM_TXRATECTRL = 2,
+	PRISM2_PARAM_BEACON_INT = 3,
+	PRISM2_PARAM_PSEUDO_IBSS = 4,
+	PRISM2_PARAM_ALC = 5,
+	/* PRISM2_PARAM_TXPOWER = 6, */ /* REMOVED 2003-10-22 */
+	PRISM2_PARAM_DUMP = 7,
+	PRISM2_PARAM_OTHER_AP_POLICY = 8,
+	PRISM2_PARAM_AP_MAX_INACTIVITY = 9,
+	PRISM2_PARAM_AP_BRIDGE_PACKETS = 10,
+	PRISM2_PARAM_DTIM_PERIOD = 11,
+	PRISM2_PARAM_AP_NULLFUNC_ACK = 12,
+	PRISM2_PARAM_MAX_WDS = 13,
+	PRISM2_PARAM_AP_AUTOM_AP_WDS = 14,
+	PRISM2_PARAM_AP_AUTH_ALGS = 15,
+	PRISM2_PARAM_MONITOR_ALLOW_FCSERR = 16,
+	PRISM2_PARAM_HOST_ENCRYPT = 17,
+	PRISM2_PARAM_HOST_DECRYPT = 18,
+	PRISM2_PARAM_HOST_ROAMING = 21,
+	PRISM2_PARAM_BCRX_STA_KEY = 22,
+	PRISM2_PARAM_IEEE_802_1X = 23,
+	PRISM2_PARAM_ANTSEL_TX = 24,
+	PRISM2_PARAM_ANTSEL_RX = 25,
+	PRISM2_PARAM_MONITOR_TYPE = 26,
+	PRISM2_PARAM_WDS_TYPE = 27,
+	PRISM2_PARAM_HOSTSCAN = 28,
+	PRISM2_PARAM_AP_SCAN = 29,
+	PRISM2_PARAM_ENH_SEC = 30,
+	PRISM2_PARAM_IO_DEBUG = 31,
+	PRISM2_PARAM_BASIC_RATES = 32,
+	PRISM2_PARAM_OPER_RATES = 33,
+	PRISM2_PARAM_HOSTAPD = 34,
+	PRISM2_PARAM_HOSTAPD_STA = 35,
+	PRISM2_PARAM_WPA = 36,
+	PRISM2_PARAM_PRIVACY_INVOKED = 37,
+	PRISM2_PARAM_TKIP_COUNTERMEASURES = 38,
+	PRISM2_PARAM_DROP_UNENCRYPTED = 39,
+	PRISM2_PARAM_SCAN_CHANNEL_MASK = 40,
+};
+
+enum { HOSTAP_ANTSEL_DO_NOT_TOUCH = 0, HOSTAP_ANTSEL_DIVERSITY = 1,
+       HOSTAP_ANTSEL_LOW = 2, HOSTAP_ANTSEL_HIGH = 3 };
+
+
+/* PRISM2_IOCTL_MACCMD ioctl() subcommands: */
+enum { AP_MAC_CMD_POLICY_OPEN = 0, AP_MAC_CMD_POLICY_ALLOW = 1,
+       AP_MAC_CMD_POLICY_DENY = 2, AP_MAC_CMD_FLUSH = 3,
+       AP_MAC_CMD_KICKALL = 4 };
+
+
+/* PRISM2_IOCTL_DOWNLOAD ioctl() dl_cmd: */
+enum {
+	PRISM2_DOWNLOAD_VOLATILE = 1 /* RAM */,
+	/* Note! Old versions of prism2_srec have a fatal error in CRC-16
+	 * calculation, which will corrupt all non-volatile downloads.
+	 * PRISM2_DOWNLOAD_NON_VOLATILE used to be 2, but it is now 3 to
+	 * prevent use of old versions of prism2_srec for non-volatile
+	 * download. */
+	PRISM2_DOWNLOAD_NON_VOLATILE = 3 /* FLASH */,
+	PRISM2_DOWNLOAD_VOLATILE_GENESIS = 4 /* RAM in Genesis mode */,
+	/* Persistent versions of volatile download commands (keep firmware
+	 * data in memory and automatically re-download after hw_reset */
+	PRISM2_DOWNLOAD_VOLATILE_PERSISTENT = 5,
+	PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT = 6,
+};
+
+struct prism2_download_param {
+	u32 dl_cmd;
+	u32 start_addr;
+	u32 num_areas;
+	struct prism2_download_area {
+		u32 addr; /* wlan card address */
+		u32 len;
+		caddr_t ptr; /* pointer to data in user space */
+	} data[0];
+};
+
+#define PRISM2_MAX_DOWNLOAD_AREA_LEN 131072
+#define PRISM2_MAX_DOWNLOAD_LEN 262144
+
+
+/* PRISM2_IOCTL_HOSTAPD ioctl() cmd: */
+enum {
+	PRISM2_HOSTAPD_FLUSH = 1,
+	PRISM2_HOSTAPD_ADD_STA = 2,
+	PRISM2_HOSTAPD_REMOVE_STA = 3,
+	PRISM2_HOSTAPD_GET_INFO_STA = 4,
+	/* REMOVED: PRISM2_HOSTAPD_RESET_TXEXC_STA = 5, */
+	PRISM2_SET_ENCRYPTION = 6,
+	PRISM2_GET_ENCRYPTION = 7,
+	PRISM2_HOSTAPD_SET_FLAGS_STA = 8,
+	PRISM2_HOSTAPD_GET_RID = 9,
+	PRISM2_HOSTAPD_SET_RID = 10,
+	PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR = 11,
+	PRISM2_HOSTAPD_SET_GENERIC_ELEMENT = 12,
+	PRISM2_HOSTAPD_MLME = 13,
+	PRISM2_HOSTAPD_SCAN_REQ = 14,
+	PRISM2_HOSTAPD_STA_CLEAR_STATS = 15,
+};
+
+#define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024
+#define PRISM2_HOSTAPD_RID_HDR_LEN \
+((size_t) (&((struct prism2_hostapd_param *) 0)->u.rid.data))
+#define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \
+((size_t) (&((struct prism2_hostapd_param *) 0)->u.generic_elem.data))
+
+/* Maximum length for algorithm names (-1 for nul termination) used in ioctl()
+ */
+#define HOSTAP_CRYPT_ALG_NAME_LEN 16
+
+
+struct prism2_hostapd_param {
+	u32 cmd;
+	u8 sta_addr[ETH_ALEN];
+	union {
+		struct {
+			u16 aid;
+			u16 capability;
+			u8 tx_supp_rates;
+		} add_sta;
+		struct {
+			u32 inactive_sec;
+		} get_info_sta;
+		struct {
+			u8 alg[HOSTAP_CRYPT_ALG_NAME_LEN];
+			u32 flags;
+			u32 err;
+			u8 idx;
+			u8 seq[8]; /* sequence counter (set: RX, get: TX) */
+			u16 key_len;
+			u8 key[0];
+		} crypt;
+		struct {
+			u32 flags_and;
+			u32 flags_or;
+		} set_flags_sta;
+		struct {
+			u16 rid;
+			u16 len;
+			u8 data[0];
+		} rid;
+		struct {
+			u8 len;
+			u8 data[0];
+		} generic_elem;
+		struct {
+#define MLME_STA_DEAUTH 0
+#define MLME_STA_DISASSOC 1
+			u16 cmd;
+			u16 reason_code;
+		} mlme;
+		struct {
+			u8 ssid_len;
+			u8 ssid[SSID_MAX_LEN];
+		} scan_req;
+	} u;
+};
+
+#define HOSTAP_CRYPT_FLAG_SET_TX_KEY BIT(0)
+#define HOSTAP_CRYPT_FLAG_PERMANENT BIT(1)
+
+#define HOSTAP_CRYPT_ERR_UNKNOWN_ALG 2
+#define HOSTAP_CRYPT_ERR_UNKNOWN_ADDR 3
+#define HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED 4
+#define HOSTAP_CRYPT_ERR_KEY_SET_FAILED 5
+#define HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED 6
+#define HOSTAP_CRYPT_ERR_CARD_CONF_FAILED 7
+
+#endif /* HOSTAP_DRIVER_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_macsec_linux.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_macsec_linux.c
new file mode 100644
index 0000000..c867154
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_macsec_linux.c
@@ -0,0 +1,1720 @@
+/*
+ * Driver interaction with Linux MACsec kernel module
+ * Copyright (c) 2016, Sabrina Dubroca <sd@queasysnail.net> and Red Hat, Inc.
+ * Copyright (c) 2019, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <netpacket/packet.h>
+#include <net/if_arp.h>
+#include <net/if.h>
+#include <netlink/netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/route/link.h>
+#include <netlink/route/link/macsec.h>
+#include <linux/if_macsec.h>
+#include <inttypes.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/eapol_common.h"
+#include "pae/ieee802_1x_kay.h"
+#include "driver.h"
+#include "driver_wired_common.h"
+
+#define DRV_PREFIX "macsec_linux: "
+
+#define UNUSED_SCI 0xffffffffffffffff
+
+#if LIBNL_VER_NUM >= LIBNL_VER(3, 6)
+#define LIBNL_HAS_OFFLOAD
+#endif
+
+struct cb_arg {
+	struct macsec_drv_data *drv;
+	u32 *pn;
+	int ifindex;
+	u8 txsa;
+	u8 rxsa;
+	u64 rxsci;
+};
+
+struct macsec_genl_ctx {
+	struct nl_sock *sk;
+	int macsec_genl_id;
+	struct cb_arg cb_arg;
+};
+
+struct macsec_drv_data {
+	struct driver_wired_common_data common;
+	struct rtnl_link *link;
+	struct nl_cache *link_cache;
+	struct nl_sock *sk;
+	struct macsec_genl_ctx ctx;
+
+	char ifname[IFNAMSIZ + 1];
+	int ifi;
+	int parent_ifi;
+	int use_pae_group_addr;
+
+	bool created_link;
+
+	bool controlled_port_enabled;
+	bool controlled_port_enabled_set;
+
+	bool protect_frames;
+	bool protect_frames_set;
+
+	bool encrypt;
+	bool encrypt_set;
+
+	bool replay_protect;
+	bool replay_protect_set;
+
+#ifdef LIBNL_HAS_OFFLOAD
+	enum macsec_offload offload;
+	bool offload_set;
+#endif /* LIBNL_HAS_OFFLOAD */
+
+	u32 replay_window;
+
+	u8 encoding_sa;
+	bool encoding_sa_set;
+
+	u64 cipher_suite;
+	bool cipher_suite_set;
+};
+
+
+static int dump_callback(struct nl_msg *msg, void *argp);
+
+
+static struct nl_msg * msg_prepare(enum macsec_nl_commands cmd,
+				   const struct macsec_genl_ctx *ctx,
+				   unsigned int ifindex)
+{
+	struct nl_msg *msg;
+
+	msg = nlmsg_alloc();
+	if (!msg) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX "failed to alloc message");
+		return NULL;
+	}
+
+	if (!genlmsg_put(msg, 0, 0, ctx->macsec_genl_id, 0, 0, cmd, 0)) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX "failed to put header");
+		goto nla_put_failure;
+	}
+
+	NLA_PUT_U32(msg, MACSEC_ATTR_IFINDEX, ifindex);
+
+	return msg;
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return NULL;
+}
+
+
+static int nla_put_rxsc_config(struct nl_msg *msg, u64 sci)
+{
+	struct nlattr *nest = nla_nest_start(msg, MACSEC_ATTR_RXSC_CONFIG);
+
+	if (!nest)
+		return -1;
+
+	NLA_PUT_U64(msg, MACSEC_RXSC_ATTR_SCI, sci);
+
+	nla_nest_end(msg, nest);
+
+	return 0;
+
+nla_put_failure:
+	return -1;
+}
+
+
+static int init_genl_ctx(struct macsec_drv_data *drv)
+{
+	struct macsec_genl_ctx *ctx = &drv->ctx;
+
+	ctx->sk = nl_socket_alloc();
+	if (!ctx->sk) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX "failed to alloc genl socket");
+		return -1;
+	}
+
+	if (genl_connect(ctx->sk) < 0) {
+		wpa_printf(MSG_ERROR,
+			   DRV_PREFIX "connection to genl socket failed");
+		goto out_free;
+	}
+
+	ctx->macsec_genl_id = genl_ctrl_resolve(ctx->sk, "macsec");
+	if (ctx->macsec_genl_id < 0) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX "genl resolve failed");
+		goto out_free;
+	}
+
+	memset(&ctx->cb_arg, 0, sizeof(ctx->cb_arg));
+	ctx->cb_arg.drv = drv;
+
+	nl_socket_modify_cb(ctx->sk, NL_CB_VALID, NL_CB_CUSTOM, dump_callback,
+			    &ctx->cb_arg);
+
+	return 0;
+
+out_free:
+	nl_socket_free(ctx->sk);
+	ctx->sk = NULL;
+	return -1;
+}
+
+
+static int try_commit(struct macsec_drv_data *drv)
+{
+	int err;
+
+	if (!drv->sk)
+		return 0;
+
+	if (!drv->link)
+		return 0;
+
+	if (drv->controlled_port_enabled_set) {
+		struct rtnl_link *change = rtnl_link_alloc();
+
+		wpa_printf(MSG_DEBUG, DRV_PREFIX
+			   "%s: try_commit controlled_port_enabled=%d",
+			   drv->ifname, drv->controlled_port_enabled);
+		if (!change)
+			return -1;
+
+		rtnl_link_set_name(change, drv->ifname);
+
+		if (drv->controlled_port_enabled)
+			rtnl_link_set_flags(change, IFF_UP);
+		else
+			rtnl_link_unset_flags(change, IFF_UP);
+
+		err = rtnl_link_change(drv->sk, change, change, 0);
+		if (err < 0)
+			return err;
+
+		rtnl_link_put(change);
+
+		drv->controlled_port_enabled_set = false;
+	}
+
+	if (drv->protect_frames_set) {
+		wpa_printf(MSG_DEBUG, DRV_PREFIX
+			   "%s: try_commit protect_frames=%d",
+			   drv->ifname, drv->protect_frames);
+		rtnl_link_macsec_set_protect(drv->link, drv->protect_frames);
+	}
+
+	if (drv->encrypt_set) {
+		wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: try_commit encrypt=%d",
+			   drv->ifname, drv->encrypt);
+		rtnl_link_macsec_set_encrypt(drv->link, drv->encrypt);
+	}
+
+	if (drv->replay_protect_set) {
+		wpa_printf(MSG_DEBUG, DRV_PREFIX
+			   "%s: try_commit replay_protect=%d replay_window=%d",
+			   drv->ifname, drv->replay_protect,
+			   drv->replay_window);
+		rtnl_link_macsec_set_replay_protect(drv->link,
+						    drv->replay_protect);
+		if (drv->replay_protect)
+			rtnl_link_macsec_set_window(drv->link,
+						    drv->replay_window);
+	}
+
+#ifdef LIBNL_HAS_OFFLOAD
+	if (drv->offload_set) {
+		wpa_printf(MSG_DEBUG, DRV_PREFIX
+			   "%s: try_commit offload=%d",
+			   drv->ifname, drv->offload);
+		rtnl_link_macsec_set_offload(drv->link, drv->offload);
+	}
+#endif /* LIBNL_HAS_OFFLOAD */
+
+	if (drv->encoding_sa_set) {
+		wpa_printf(MSG_DEBUG, DRV_PREFIX
+			   "%s: try_commit encoding_sa=%d",
+			   drv->ifname, drv->encoding_sa);
+		rtnl_link_macsec_set_encoding_sa(drv->link, drv->encoding_sa);
+	}
+
+	err = rtnl_link_add(drv->sk, drv->link, 0);
+	if (err < 0)
+		return err;
+
+	drv->protect_frames_set = false;
+	drv->encrypt_set = false;
+	drv->replay_protect_set = false;
+
+	return 0;
+}
+
+
+static void macsec_drv_wpa_deinit(void *priv)
+{
+	struct macsec_drv_data *drv = priv;
+
+	driver_wired_deinit_common(&drv->common);
+	os_free(drv);
+}
+
+
+static int macsec_check_macsec(void)
+{
+	struct nl_sock *sk;
+	int err = -1;
+
+	sk = nl_socket_alloc();
+	if (!sk) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX "failed to alloc genl socket");
+		return -1;
+	}
+
+	if (genl_connect(sk) < 0) {
+		wpa_printf(MSG_ERROR,
+			   DRV_PREFIX "connection to genl socket failed");
+		goto out_free;
+	}
+
+	if (genl_ctrl_resolve(sk, "macsec") < 0) {
+		wpa_printf(MSG_ERROR,
+			   DRV_PREFIX "genl resolve failed - macsec kernel module not present?");
+		goto out_free;
+	}
+
+	err = 0;
+
+out_free:
+	nl_socket_free(sk);
+	return err;
+}
+
+
+static void * macsec_drv_wpa_init(void *ctx, const char *ifname)
+{
+	struct macsec_drv_data *drv;
+
+	if (macsec_check_macsec() < 0)
+		return NULL;
+
+	drv = os_zalloc(sizeof(*drv));
+	if (!drv)
+		return NULL;
+
+	if (driver_wired_init_common(&drv->common, ifname, ctx) < 0) {
+		os_free(drv);
+		return NULL;
+	}
+
+	return drv;
+}
+
+
+static int macsec_drv_macsec_init(void *priv, struct macsec_init_params *params)
+{
+	struct macsec_drv_data *drv = priv;
+	int err;
+
+	wpa_printf(MSG_DEBUG, "%s", __func__);
+
+	drv->sk = nl_socket_alloc();
+	if (!drv->sk)
+		return -1;
+
+	err = nl_connect(drv->sk, NETLINK_ROUTE);
+	if (err < 0) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX
+			   "Unable to connect NETLINK_ROUTE socket: %s",
+			   nl_geterror(err));
+		goto sock;
+	}
+
+	err = rtnl_link_alloc_cache(drv->sk, AF_UNSPEC, &drv->link_cache);
+	if (err < 0) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX "Unable to get link cache: %s",
+			   nl_geterror(err));
+		goto sock;
+	}
+
+	drv->parent_ifi = rtnl_link_name2i(drv->link_cache, drv->common.ifname);
+	if (drv->parent_ifi == 0) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX
+			   "couldn't find ifindex for interface %s",
+			   drv->common.ifname);
+		goto cache;
+	}
+	wpa_printf(MSG_DEBUG, DRV_PREFIX "ifname=%s parent_ifi=%d",
+		   drv->common.ifname, drv->parent_ifi);
+
+	err = init_genl_ctx(drv);
+	if (err < 0)
+		goto cache;
+
+	return 0;
+
+cache:
+	nl_cache_free(drv->link_cache);
+	drv->link_cache = NULL;
+sock:
+	nl_socket_free(drv->sk);
+	drv->sk = NULL;
+	return -1;
+}
+
+
+static int macsec_drv_macsec_deinit(void *priv)
+{
+	struct macsec_drv_data *drv = priv;
+
+	wpa_printf(MSG_DEBUG, "%s", __func__);
+
+	if (drv->sk)
+		nl_socket_free(drv->sk);
+	drv->sk = NULL;
+
+	if (drv->link_cache)
+		nl_cache_free(drv->link_cache);
+	drv->link_cache = NULL;
+
+	if (drv->ctx.sk)
+		nl_socket_free(drv->ctx.sk);
+
+	return 0;
+}
+
+
+static int macsec_drv_get_capability(void *priv, enum macsec_cap *cap)
+{
+	wpa_printf(MSG_DEBUG, "%s", __func__);
+
+	*cap = MACSEC_CAP_INTEG_AND_CONF;
+
+	return 0;
+}
+
+
+/**
+ * macsec_drv_enable_protect_frames - Set protect frames status
+ * @priv: Private driver interface data
+ * @enabled: true = protect frames enabled
+ *           false = protect frames disabled
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_enable_protect_frames(void *priv, bool enabled)
+{
+	struct macsec_drv_data *drv = priv;
+
+	wpa_printf(MSG_DEBUG, "%s -> %s", __func__, enabled ? "TRUE" : "FALSE");
+
+	drv->protect_frames_set = true;
+	drv->protect_frames = enabled;
+
+	return try_commit(drv);
+}
+
+
+/**
+ * macsec_drv_enable_encrypt - Set protect frames status
+ * @priv: Private driver interface data
+ * @enabled: true = protect frames enabled
+ *           false = protect frames disabled
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_enable_encrypt(void *priv, bool enabled)
+{
+	struct macsec_drv_data *drv = priv;
+
+	wpa_printf(MSG_DEBUG, "%s -> %s", __func__, enabled ? "TRUE" : "FALSE");
+
+	drv->encrypt_set = true;
+	drv->encrypt = enabled;
+
+	return try_commit(drv);
+}
+
+
+/**
+ * macsec_drv_set_replay_protect - Set replay protect status and window size
+ * @priv: Private driver interface data
+ * @enabled: true = replay protect enabled
+ *           false = replay protect disabled
+ * @window: replay window size, valid only when replay protect enabled
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_set_replay_protect(void *priv, bool enabled,
+					 u32 window)
+{
+	struct macsec_drv_data *drv = priv;
+
+	wpa_printf(MSG_DEBUG, "%s -> %s, %u", __func__,
+		   enabled ? "TRUE" : "FALSE", window);
+
+	drv->replay_protect_set = true;
+	drv->replay_protect = enabled;
+	if (enabled)
+		drv->replay_window = window;
+
+	return try_commit(drv);
+}
+
+
+/**
+ * macsec_drv_set_offload - Set offload status
+ * @priv: Private driver interface data
+ * @offload: 0 = MACSEC_OFFLOAD_OFF
+ *           1 = MACSEC_OFFLOAD_PHY
+ *           2 = MACSEC_OFFLOAD_MAC
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_set_offload(void *priv, u8 offload)
+{
+#ifdef LIBNL_HAS_OFFLOAD
+	struct macsec_drv_data *drv = priv;
+
+	wpa_printf(MSG_DEBUG, "%s -> %02" PRIx8, __func__, offload);
+
+	drv->offload_set = true;
+	drv->offload = offload;
+
+	return try_commit(drv);
+#else /* LIBNL_HAS_OFFLOAD */
+	if (offload == 0)
+		return 0;
+	wpa_printf(MSG_INFO,
+		   "%s: libnl version does not include support for MACsec offload",
+		   __func__);
+	return -1;
+#endif /* LIBNL_HAS_OFFLOAD */
+}
+
+
+/**
+ * macsec_drv_set_current_cipher_suite - Set current cipher suite
+ * @priv: Private driver interface data
+ * @cs: EUI64 identifier
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_set_current_cipher_suite(void *priv, u64 cs)
+{
+	struct macsec_drv_data *drv = priv;
+
+	wpa_printf(MSG_DEBUG, "%s -> %016" PRIx64, __func__, cs);
+
+	drv->cipher_suite_set = true;
+	drv->cipher_suite = cs;
+
+	return try_commit(drv);
+}
+
+
+/**
+ * macsec_drv_enable_controlled_port - Set controlled port status
+ * @priv: Private driver interface data
+ * @enabled: true = controlled port enabled
+ *           false = controlled port disabled
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_enable_controlled_port(void *priv, bool enabled)
+{
+	struct macsec_drv_data *drv = priv;
+
+	wpa_printf(MSG_DEBUG, "%s -> %s", __func__, enabled ? "TRUE" : "FALSE");
+
+	drv->controlled_port_enabled = enabled;
+	drv->controlled_port_enabled_set = true;
+
+	return try_commit(drv);
+}
+
+
+static struct nla_policy sa_policy[MACSEC_SA_ATTR_MAX + 1] = {
+	[MACSEC_SA_ATTR_AN] = { .type = NLA_U8 },
+	[MACSEC_SA_ATTR_ACTIVE] = { .type = NLA_U8 },
+	[MACSEC_SA_ATTR_PN] = { .type = NLA_U32 },
+	[MACSEC_SA_ATTR_KEYID] = { .type = NLA_BINARY },
+};
+
+static struct nla_policy sc_policy[MACSEC_RXSC_ATTR_MAX + 1] = {
+	[MACSEC_RXSC_ATTR_SCI] = { .type = NLA_U64 },
+	[MACSEC_RXSC_ATTR_ACTIVE] = { .type = NLA_U8 },
+	[MACSEC_RXSC_ATTR_SA_LIST] = { .type = NLA_NESTED },
+};
+
+static struct nla_policy main_policy[MACSEC_ATTR_MAX + 1] = {
+	[MACSEC_ATTR_IFINDEX] = { .type = NLA_U32 },
+	[MACSEC_ATTR_SECY] = { .type = NLA_NESTED },
+	[MACSEC_ATTR_TXSA_LIST] = { .type = NLA_NESTED },
+	[MACSEC_ATTR_RXSC_LIST] = { .type = NLA_NESTED },
+};
+
+static int dump_callback(struct nl_msg *msg, void *argp)
+{
+	struct nlmsghdr *ret_hdr = nlmsg_hdr(msg);
+	struct nlattr *tb_msg[MACSEC_ATTR_MAX + 1];
+	struct cb_arg *arg = (struct cb_arg *) argp;
+	struct genlmsghdr *gnlh = (struct genlmsghdr *) nlmsg_data(ret_hdr);
+	int err;
+
+	if (ret_hdr->nlmsg_type != arg->drv->ctx.macsec_genl_id)
+		return 0;
+
+	err = nla_parse(tb_msg, MACSEC_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+			genlmsg_attrlen(gnlh, 0), main_policy);
+	if (err < 0)
+		return 0;
+
+	if (!tb_msg[MACSEC_ATTR_IFINDEX])
+		return 0;
+
+	if (nla_get_u32(tb_msg[MACSEC_ATTR_IFINDEX]) != (u32) arg->ifindex)
+		return 0;
+
+	if (arg->txsa < 4 && !tb_msg[MACSEC_ATTR_TXSA_LIST]) {
+		return 0;
+	} else if (arg->txsa < 4) {
+		struct nlattr *nla;
+		int rem;
+
+		nla_for_each_nested(nla, tb_msg[MACSEC_ATTR_TXSA_LIST], rem) {
+			struct nlattr *tb[MACSEC_SA_ATTR_MAX + 1];
+
+			err = nla_parse_nested(tb, MACSEC_SA_ATTR_MAX, nla,
+					       sa_policy);
+			if (err < 0)
+				continue;
+			if (!tb[MACSEC_SA_ATTR_AN])
+				continue;
+			if (nla_get_u8(tb[MACSEC_SA_ATTR_AN]) != arg->txsa)
+				continue;
+			if (!tb[MACSEC_SA_ATTR_PN])
+				return 0;
+			*arg->pn = nla_get_u32(tb[MACSEC_SA_ATTR_PN]);
+			return 0;
+		}
+
+		return 0;
+	}
+
+	if (arg->rxsci == UNUSED_SCI)
+		return 0;
+
+	if (tb_msg[MACSEC_ATTR_RXSC_LIST]) {
+		struct nlattr *nla;
+		int rem;
+
+		nla_for_each_nested(nla, tb_msg[MACSEC_ATTR_RXSC_LIST], rem) {
+			struct nlattr *tb[MACSEC_RXSC_ATTR_MAX + 1];
+
+			err = nla_parse_nested(tb, MACSEC_RXSC_ATTR_MAX, nla,
+					       sc_policy);
+			if (err < 0)
+				return 0;
+			if (!tb[MACSEC_RXSC_ATTR_SCI])
+				continue;
+			if (nla_get_u64(tb[MACSEC_RXSC_ATTR_SCI]) != arg->rxsci)
+				continue;
+			if (!tb[MACSEC_RXSC_ATTR_SA_LIST])
+				return 0;
+
+			nla_for_each_nested(nla, tb[MACSEC_RXSC_ATTR_SA_LIST],
+					    rem) {
+				struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1];
+
+				err = nla_parse_nested(tb_sa,
+						       MACSEC_SA_ATTR_MAX, nla,
+						       sa_policy);
+				if (err < 0)
+					continue;
+				if (!tb_sa[MACSEC_SA_ATTR_AN])
+					continue;
+				if (nla_get_u8(tb_sa[MACSEC_SA_ATTR_AN]) !=
+				    arg->rxsa)
+					continue;
+				if (!tb_sa[MACSEC_SA_ATTR_PN])
+					return 0;
+				*arg->pn =
+					nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]);
+
+				return 0;
+			}
+
+			return 0;
+		}
+
+		return 0;
+	}
+
+	return 0;
+}
+
+
+static int nl_send_recv(struct nl_sock *sk, struct nl_msg *msg)
+{
+	int ret;
+
+	ret = nl_send_auto_complete(sk, msg);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to send: %d (%s)",
+			   __func__, ret, nl_geterror(-ret));
+		return ret;
+	}
+
+	ret = nl_recvmsgs_default(sk);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to recv: %d (%s)",
+			   __func__, ret, nl_geterror(-ret));
+	}
+
+	return ret;
+}
+
+
+static int do_dump(struct macsec_drv_data *drv, u8 txsa, u64 rxsci, u8 rxsa,
+		   u32 *pn)
+{
+	struct macsec_genl_ctx *ctx = &drv->ctx;
+	struct nl_msg *msg;
+	int ret = 1;
+
+	ctx->cb_arg.ifindex = drv->ifi;
+	ctx->cb_arg.rxsci = rxsci;
+	ctx->cb_arg.rxsa = rxsa;
+	ctx->cb_arg.txsa = txsa;
+	ctx->cb_arg.pn = pn;
+
+	msg = nlmsg_alloc();
+	if (!msg) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to alloc message",
+			   __func__);
+		return 1;
+	}
+
+	if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, ctx->macsec_genl_id, 0,
+			 NLM_F_DUMP, MACSEC_CMD_GET_TXSC, 0)) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to put header",
+			   __func__);
+		goto out_free_msg;
+	}
+
+	ret = nl_send_recv(ctx->sk, msg);
+	if (ret < 0)
+		wpa_printf(MSG_ERROR,
+			   DRV_PREFIX "failed to communicate: %d (%s)",
+			   ret, nl_geterror(-ret));
+
+	ctx->cb_arg.pn = NULL;
+
+out_free_msg:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+/**
+ * macsec_drv_get_receive_lowest_pn - Get receive lowest PN
+ * @priv: Private driver interface data
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_get_receive_lowest_pn(void *priv, struct receive_sa *sa)
+{
+	struct macsec_drv_data *drv = priv;
+	int err;
+
+	wpa_printf(MSG_DEBUG, DRV_PREFIX "%s", __func__);
+
+	err = do_dump(drv, 0xff, mka_sci_u64(&sa->sc->sci), sa->an,
+		      &sa->lowest_pn);
+	wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: result %d", __func__,
+		   sa->lowest_pn);
+
+	return err;
+}
+
+
+/**
+ * macsec_drv_set_receive_lowest_pn - Set receive lowest PN
+ * @priv: Private driver interface data
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_set_receive_lowest_pn(void *priv, struct receive_sa *sa)
+{
+	struct macsec_drv_data *drv = priv;
+	struct macsec_genl_ctx *ctx = &drv->ctx;
+	struct nl_msg *msg;
+	struct nlattr *nest;
+	int ret = -1;
+
+	wpa_printf(MSG_DEBUG,
+		   DRV_PREFIX "%s: set_receive_lowest_pn -> %d: %d",
+		   drv->ifname, sa->an, sa->next_pn);
+
+	msg = msg_prepare(MACSEC_CMD_UPD_RXSA, ctx, drv->ifi);
+	if (!msg)
+		return ret;
+
+	if (nla_put_rxsc_config(msg, mka_sci_u64(&sa->sc->sci)))
+		goto nla_put_failure;
+
+	nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+	if (!nest)
+		goto nla_put_failure;
+
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
+	NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn);
+
+	nla_nest_end(msg, nest);
+
+	ret = nl_send_recv(ctx->sk, msg);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR,
+			   DRV_PREFIX "failed to communicate: %d (%s)",
+			   ret, nl_geterror(-ret));
+	}
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+/**
+ * macsec_drv_get_transmit_next_pn - Get transmit next PN
+ * @priv: Private driver interface data
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_get_transmit_next_pn(void *priv, struct transmit_sa *sa)
+{
+	struct macsec_drv_data *drv = priv;
+	int err;
+
+	wpa_printf(MSG_DEBUG, "%s", __func__);
+
+	err = do_dump(drv, sa->an, UNUSED_SCI, 0xff, &sa->next_pn);
+	wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: err %d result %d", __func__, err,
+		   sa->next_pn);
+	return err;
+}
+
+
+/**
+ * macsec_drv_set_transmit_next_pn - Set transmit next pn
+ * @priv: Private driver interface data
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_set_transmit_next_pn(void *priv, struct transmit_sa *sa)
+{
+	struct macsec_drv_data *drv = priv;
+	struct macsec_genl_ctx *ctx = &drv->ctx;
+	struct nl_msg *msg;
+	struct nlattr *nest;
+	int ret = -1;
+
+	wpa_printf(MSG_DEBUG, "%s -> %d: %d", __func__, sa->an, sa->next_pn);
+
+	msg = msg_prepare(MACSEC_CMD_UPD_TXSA, ctx, drv->ifi);
+	if (!msg)
+		return ret;
+
+	nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+	if (!nest)
+		goto nla_put_failure;
+
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
+	NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn);
+
+	nla_nest_end(msg, nest);
+
+	ret = nl_send_recv(ctx->sk, msg);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR,
+			   DRV_PREFIX "failed to communicate: %d (%s)",
+			   ret, nl_geterror(-ret));
+	}
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+#define SCISTR MACSTR "::%hx"
+#define SCI2STR(addr, port) MAC2STR(addr), htons(port)
+
+/**
+ * macsec_drv_create_receive_sc - Create secure channel for receiving
+ * @priv: Private driver interface data
+ * @sc: secure channel
+ * @sci_addr: secure channel identifier - address
+ * @sci_port: secure channel identifier - port
+ * @conf_offset: confidentiality offset (0, 30, or 50)
+ * @validation: frame validation policy (0 = Disabled, 1 = Checked,
+ *	2 = Strict)
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_create_receive_sc(void *priv, struct receive_sc *sc,
+					unsigned int conf_offset,
+					int validation)
+{
+	struct macsec_drv_data *drv = priv;
+	struct macsec_genl_ctx *ctx = &drv->ctx;
+	struct nl_msg *msg;
+	int ret = -1;
+
+	wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: create_receive_sc -> " SCISTR
+		   " (conf_offset=%u validation=%d)",
+		   drv->ifname, SCI2STR(sc->sci.addr, sc->sci.port),
+		   conf_offset, validation);
+
+	msg = msg_prepare(MACSEC_CMD_ADD_RXSC, ctx, drv->ifi);
+	if (!msg)
+		return ret;
+
+	if (nla_put_rxsc_config(msg, mka_sci_u64(&sc->sci)))
+		goto nla_put_failure;
+
+	ret = nl_send_recv(ctx->sk, msg);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR,
+			   DRV_PREFIX "%s: failed to communicate: %d (%s)",
+			   __func__, ret, nl_geterror(-ret));
+	}
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+/**
+ * macsec_drv_delete_receive_sc - Delete secure connection for receiving
+ * @priv: private driver interface data from init()
+ * @sc: secure channel
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_delete_receive_sc(void *priv, struct receive_sc *sc)
+{
+	struct macsec_drv_data *drv = priv;
+	struct macsec_genl_ctx *ctx = &drv->ctx;
+	struct nl_msg *msg;
+	int ret = -1;
+
+	wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: delete_receive_sc -> " SCISTR,
+		   drv->ifname, SCI2STR(sc->sci.addr, sc->sci.port));
+
+	msg = msg_prepare(MACSEC_CMD_DEL_RXSC, ctx, drv->ifi);
+	if (!msg)
+		return ret;
+
+	if (nla_put_rxsc_config(msg, mka_sci_u64(&sc->sci)))
+		goto nla_put_failure;
+
+	ret = nl_send_recv(ctx->sk, msg);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR,
+			   DRV_PREFIX "%s: failed to communicate: %d (%s)",
+			   __func__, ret, nl_geterror(-ret));
+	}
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+/**
+ * macsec_drv_create_receive_sa - Create secure association for receive
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_create_receive_sa(void *priv, struct receive_sa *sa)
+{
+	struct macsec_drv_data *drv = priv;
+	struct macsec_genl_ctx *ctx = &drv->ctx;
+	struct nl_msg *msg;
+	struct nlattr *nest;
+	int ret = -1;
+
+	wpa_printf(MSG_DEBUG,
+		   DRV_PREFIX "%s: create_receive_sa -> %d on " SCISTR
+		   " (enable_receive=%d next_pn=%u)",
+		   drv->ifname, sa->an,
+		   SCI2STR(sa->sc->sci.addr, sa->sc->sci.port),
+		   sa->enable_receive, sa->next_pn);
+	wpa_hexdump(MSG_DEBUG, DRV_PREFIX "SA keyid",
+		    &sa->pkey->key_identifier,
+		    sizeof(sa->pkey->key_identifier));
+	wpa_hexdump_key(MSG_DEBUG, DRV_PREFIX "SA key",
+			sa->pkey->key, sa->pkey->key_len);
+
+	msg = msg_prepare(MACSEC_CMD_ADD_RXSA, ctx, drv->ifi);
+	if (!msg)
+		return ret;
+
+	if (nla_put_rxsc_config(msg, mka_sci_u64(&sa->sc->sci)))
+		goto nla_put_failure;
+
+	nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+	if (!nest)
+		goto nla_put_failure;
+
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, sa->enable_receive);
+	NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn);
+	NLA_PUT(msg, MACSEC_SA_ATTR_KEYID, sizeof(sa->pkey->key_identifier),
+		&sa->pkey->key_identifier);
+	NLA_PUT(msg, MACSEC_SA_ATTR_KEY, sa->pkey->key_len, sa->pkey->key);
+
+	nla_nest_end(msg, nest);
+
+	ret = nl_send_recv(ctx->sk, msg);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR,
+			   DRV_PREFIX "%s: failed to communicate: %d (%s)",
+			   __func__, ret, nl_geterror(-ret));
+	}
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+/**
+ * macsec_drv_delete_receive_sa - Delete secure association for receive
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_delete_receive_sa(void *priv, struct receive_sa *sa)
+{
+	struct macsec_drv_data *drv = priv;
+	struct macsec_genl_ctx *ctx = &drv->ctx;
+	struct nl_msg *msg;
+	struct nlattr *nest;
+	int ret = -1;
+
+	wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: delete_receive_sa -> %d on "
+		   SCISTR, drv->ifname, sa->an,
+		   SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
+
+	msg = msg_prepare(MACSEC_CMD_DEL_RXSA, ctx, drv->ifi);
+	if (!msg)
+		return ret;
+
+	if (nla_put_rxsc_config(msg, mka_sci_u64(&sa->sc->sci)))
+		goto nla_put_failure;
+
+	nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+	if (!nest)
+		goto nla_put_failure;
+
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
+
+	nla_nest_end(msg, nest);
+
+	ret = nl_send_recv(ctx->sk, msg);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR,
+			   DRV_PREFIX "%s: failed to communicate: %d (%s)",
+			   __func__, ret, nl_geterror(-ret));
+	}
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+static int set_active_rx_sa(const struct macsec_genl_ctx *ctx, int ifindex,
+			    u64 sci, unsigned char an, bool state)
+{
+	struct nl_msg *msg;
+	struct nlattr *nest;
+	int ret = -1;
+
+	msg = msg_prepare(MACSEC_CMD_UPD_RXSA, ctx, ifindex);
+	if (!msg)
+		return ret;
+
+	if (nla_put_rxsc_config(msg, sci))
+		goto nla_put_failure;
+
+	nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+	if (!nest)
+		goto nla_put_failure;
+
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, an);
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, !!state);
+
+	nla_nest_end(msg, nest);
+
+	ret = nl_send_recv(ctx->sk, msg);
+	if (ret < 0)
+		wpa_printf(MSG_ERROR,
+			   DRV_PREFIX "%s: failed to communicate: %d (%s)",
+			   __func__, ret, nl_geterror(-ret));
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+/**
+ * macsec_drv_enable_receive_sa - Enable the SA for receive
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_enable_receive_sa(void *priv, struct receive_sa *sa)
+{
+	struct macsec_drv_data *drv = priv;
+	struct macsec_genl_ctx *ctx = &drv->ctx;
+
+	wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: enable_receive_sa -> %d on "
+		   SCISTR, drv->ifname, sa->an,
+		   SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
+
+	return set_active_rx_sa(ctx, drv->ifi, mka_sci_u64(&sa->sc->sci),
+				sa->an, true);
+}
+
+
+/**
+ * macsec_drv_disable_receive_sa - Disable SA for receive
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_disable_receive_sa(void *priv, struct receive_sa *sa)
+{
+	struct macsec_drv_data *drv = priv;
+	struct macsec_genl_ctx *ctx = &drv->ctx;
+
+	wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: disable_receive_sa -> %d on "
+		   SCISTR, drv->ifname, sa->an,
+		   SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
+
+	return set_active_rx_sa(ctx, drv->ifi, mka_sci_u64(&sa->sc->sci),
+				sa->an, false);
+}
+
+
+static struct rtnl_link * lookup_sc(struct nl_cache *cache, int parent, u64 sci,
+				    u64 cs)
+{
+	struct rtnl_link *needle;
+	void *match;
+
+	needle = rtnl_link_macsec_alloc();
+	if (!needle)
+		return NULL;
+
+	rtnl_link_set_link(needle, parent);
+	rtnl_link_macsec_set_sci(needle, sci);
+	if (cs)
+		rtnl_link_macsec_set_cipher_suite(needle, cs);
+
+	match = nl_cache_find(cache, (struct nl_object *) needle);
+	rtnl_link_put(needle);
+
+	return (struct rtnl_link *) match;
+}
+
+
+/**
+ * macsec_drv_create_transmit_sc - Create secure connection for transmit
+ * @priv: private driver interface data from init()
+ * @sc: secure channel
+ * @conf_offset: confidentiality offset
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_create_transmit_sc(
+	void *priv, struct transmit_sc *sc,
+	unsigned int conf_offset)
+{
+	struct macsec_drv_data *drv = priv;
+	struct rtnl_link *link;
+	char *ifname;
+	u64 sci;
+	int err;
+	u64 cs = 0;
+
+	wpa_printf(MSG_DEBUG, DRV_PREFIX
+		   "%s: create_transmit_sc -> " SCISTR " (conf_offset=%d)",
+		   drv->common.ifname, SCI2STR(sc->sci.addr, sc->sci.port),
+		   conf_offset);
+
+	if (!drv->sk) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX "NULL rtnl socket");
+		return -1;
+	}
+
+	link = rtnl_link_macsec_alloc();
+	if (!link) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't allocate link");
+		return -1;
+	}
+
+	rtnl_link_set_link(link, drv->parent_ifi);
+
+	sci = mka_sci_u64(&sc->sci);
+	rtnl_link_macsec_set_sci(link, sci);
+
+	drv->created_link = true;
+
+	if (drv->cipher_suite_set) {
+		cs = drv->cipher_suite;
+		drv->cipher_suite_set = false;
+		rtnl_link_macsec_set_cipher_suite(link, cs);
+	}
+
+	err = rtnl_link_add(drv->sk, link, NLM_F_CREATE);
+	if (err == -NLE_BUSY) {
+		wpa_printf(MSG_INFO,
+			   DRV_PREFIX "link already exists, using it");
+		drv->created_link = false;
+	} else if (err < 0) {
+		rtnl_link_put(link);
+		wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't create link: err %d",
+			   err);
+		return err;
+	}
+
+	rtnl_link_put(link);
+
+	nl_cache_refill(drv->sk, drv->link_cache);
+	link = lookup_sc(drv->link_cache, drv->parent_ifi, sci, cs);
+	if (!link) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't find link");
+		return -1;
+	}
+
+	drv->ifi = rtnl_link_get_ifindex(link);
+	ifname = rtnl_link_get_name(link);
+	wpa_printf(MSG_DEBUG,
+		   DRV_PREFIX "%s: create_transmit_sc: ifi=%d ifname=%s",
+		   drv->common.ifname, drv->ifi, ifname);
+	os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+	rtnl_link_put(link);
+
+	drv->link = rtnl_link_macsec_alloc();
+	if (!drv->link) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't allocate link");
+		return -1;
+	}
+
+	rtnl_link_set_name(drv->link, drv->ifname);
+
+	/* In case some settings have already been done but we couldn't apply
+	 * them. */
+	return try_commit(drv);
+}
+
+
+/**
+ * macsec_drv_delete_transmit_sc - Delete secure connection for transmit
+ * @priv: private driver interface data from init()
+ * @sc: secure channel
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_delete_transmit_sc(void *priv, struct transmit_sc *sc)
+{
+	struct macsec_drv_data *drv = priv;
+	int err;
+
+	wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: delete_transmit_sc -> " SCISTR,
+		   drv->ifname, SCI2STR(sc->sci.addr, sc->sci.port));
+
+	if (!drv->sk)
+		return 0;
+
+	if (!drv->created_link) {
+		rtnl_link_put(drv->link);
+		drv->link = NULL;
+		wpa_printf(MSG_DEBUG, DRV_PREFIX
+			   "we didn't create the link, leave it alone");
+		return 0;
+	}
+
+	err = rtnl_link_delete(drv->sk, drv->link);
+	if (err < 0)
+		wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't delete link");
+	rtnl_link_put(drv->link);
+	drv->link = NULL;
+
+	return err;
+}
+
+
+/**
+ * macsec_drv_create_transmit_sa - Create secure association for transmit
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_create_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+	struct macsec_drv_data *drv = priv;
+	struct macsec_genl_ctx *ctx = &drv->ctx;
+	struct nl_msg *msg;
+	struct nlattr *nest;
+	int ret = -1;
+
+	wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: create_transmit_sa -> %d on "
+		   SCISTR " (enable_transmit=%d next_pn=%u)",
+		   drv->ifname, sa->an,
+		   SCI2STR(sa->sc->sci.addr, sa->sc->sci.port),
+		   sa->enable_transmit, sa->next_pn);
+	wpa_hexdump(MSG_DEBUG, DRV_PREFIX "SA keyid",
+		    &sa->pkey->key_identifier,
+		    sizeof(sa->pkey->key_identifier));
+	wpa_hexdump_key(MSG_DEBUG, DRV_PREFIX "SA key",
+			sa->pkey->key, sa->pkey->key_len);
+
+	msg = msg_prepare(MACSEC_CMD_ADD_TXSA, ctx, drv->ifi);
+	if (!msg)
+		return ret;
+
+	nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+	if (!nest)
+		goto nla_put_failure;
+
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
+	NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn);
+	NLA_PUT(msg, MACSEC_SA_ATTR_KEYID, sizeof(sa->pkey->key_identifier),
+		&sa->pkey->key_identifier);
+	NLA_PUT(msg, MACSEC_SA_ATTR_KEY, sa->pkey->key_len, sa->pkey->key);
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, sa->enable_transmit);
+
+	nla_nest_end(msg, nest);
+
+	ret = nl_send_recv(ctx->sk, msg);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR,
+			   DRV_PREFIX "%s: failed to communicate: %d (%s)",
+			   __func__, ret, nl_geterror(-ret));
+	}
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+/**
+ * macsec_drv_delete_transmit_sa - Delete secure association for transmit
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_delete_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+	struct macsec_drv_data *drv = priv;
+	struct macsec_genl_ctx *ctx = &drv->ctx;
+	struct nl_msg *msg;
+	struct nlattr *nest;
+	int ret = -1;
+
+	wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: delete_transmit_sa -> %d on "
+		   SCISTR, drv->ifname, sa->an,
+		   SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
+
+	msg = msg_prepare(MACSEC_CMD_DEL_TXSA, ctx, drv->ifi);
+	if (!msg)
+		return ret;
+
+	nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+	if (!nest)
+		goto nla_put_failure;
+
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
+
+	nla_nest_end(msg, nest);
+
+	ret = nl_send_recv(ctx->sk, msg);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR,
+			   DRV_PREFIX "%s: failed to communicate: %d (%s)",
+			   __func__, ret, nl_geterror(-ret));
+	}
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+static int set_active_tx_sa(const struct macsec_genl_ctx *ctx, int ifindex,
+			    unsigned char an, bool state)
+{
+	struct nl_msg *msg;
+	struct nlattr *nest;
+	int ret = -1;
+
+	msg = msg_prepare(MACSEC_CMD_UPD_TXSA, ctx, ifindex);
+	if (!msg)
+		return ret;
+
+	nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+	if (!nest)
+		goto nla_put_failure;
+
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, an);
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, !!state);
+
+	nla_nest_end(msg, nest);
+
+	ret = nl_send_recv(ctx->sk, msg);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR,
+			   DRV_PREFIX "%s: failed to communicate: %d (%s)",
+			   __func__, ret, nl_geterror(-ret));
+	}
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+/**
+ * macsec_drv_enable_transmit_sa - Enable SA for transmit
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_enable_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+	struct macsec_drv_data *drv = priv;
+	struct macsec_genl_ctx *ctx = &drv->ctx;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: enable_transmit_sa -> %d on "
+		   SCISTR, drv->ifname, sa->an,
+		   SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
+
+	ret = set_active_tx_sa(ctx, drv->ifi, sa->an, true);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX "failed to enable txsa");
+		return ret;
+	}
+
+	drv->encoding_sa_set = true;
+	drv->encoding_sa = sa->an;
+
+	return try_commit(drv);
+}
+
+
+/**
+ * macsec_drv_disable_transmit_sa - Disable SA for transmit
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_disable_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+	struct macsec_drv_data *drv = priv;
+	struct macsec_genl_ctx *ctx = &drv->ctx;
+
+	wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: disable_transmit_sa -> %d on "
+		   SCISTR, drv->ifname, sa->an,
+		   SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
+
+	return set_active_tx_sa(ctx, drv->ifi, sa->an, false);
+}
+
+
+static int macsec_drv_status(void *priv, char *buf, size_t buflen)
+{
+	struct macsec_drv_data *drv = priv;
+	int res;
+	char *pos, *end;
+
+	pos = buf;
+	end = buf + buflen;
+
+	res = os_snprintf(pos, end - pos,
+			  "ifname=%s\n"
+			  "ifi=%d\n"
+			  "parent_ifname=%s\n"
+			  "parent_ifi=%d\n",
+			  drv->common.ifname, drv->ifi,
+			  drv->ifname, drv->parent_ifi);
+	if (os_snprintf_error(end - pos, res))
+		return pos - buf;
+	pos += res;
+
+	return pos - buf;
+}
+
+
+#ifdef __linux__
+
+static void macsec_drv_handle_data(void *ctx, unsigned char *buf, size_t len)
+{
+#ifdef HOSTAPD
+	struct ieee8023_hdr *hdr;
+	u8 *pos, *sa;
+	size_t left;
+	union wpa_event_data event;
+
+	/* must contain at least ieee8023_hdr 6 byte source, 6 byte dest,
+	 * 2 byte ethertype */
+	if (len < 14) {
+		wpa_printf(MSG_MSGDUMP, "%s: too short (%lu)",
+			   __func__, (unsigned long) len);
+		return;
+	}
+
+	hdr = (struct ieee8023_hdr *) buf;
+
+	switch (ntohs(hdr->ethertype)) {
+	case ETH_P_PAE:
+		wpa_printf(MSG_MSGDUMP, "Received EAPOL packet");
+		sa = hdr->src;
+		os_memset(&event, 0, sizeof(event));
+		event.new_sta.addr = sa;
+		wpa_supplicant_event(ctx, EVENT_NEW_STA, &event);
+
+		pos = (u8 *) (hdr + 1);
+		left = len - sizeof(*hdr);
+		drv_event_eapol_rx(ctx, sa, pos, left);
+		break;
+
+	default:
+		wpa_printf(MSG_DEBUG, "Unknown ethertype 0x%04x in data frame",
+			   ntohs(hdr->ethertype));
+		break;
+	}
+#endif /* HOSTAPD */
+}
+
+
+static void macsec_drv_handle_read(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	int len;
+	unsigned char buf[3000];
+
+	len = recv(sock, buf, sizeof(buf), 0);
+	if (len < 0) {
+		wpa_printf(MSG_ERROR, "macsec_linux: recv: %s",
+			   strerror(errno));
+		return;
+	}
+
+	macsec_drv_handle_data(eloop_ctx, buf, len);
+}
+
+#endif /* __linux__ */
+
+
+static int macsec_drv_init_sockets(struct macsec_drv_data *drv, u8 *own_addr)
+{
+#ifdef __linux__
+	struct ifreq ifr;
+	struct sockaddr_ll addr;
+
+	drv->common.sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE));
+	if (drv->common.sock < 0) {
+		wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	if (eloop_register_read_sock(drv->common.sock, macsec_drv_handle_read,
+				     drv->common.ctx, NULL)) {
+		wpa_printf(MSG_INFO, "Could not register read socket");
+		return -1;
+	}
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name));
+	if (ioctl(drv->common.sock, SIOCGIFINDEX, &ifr) != 0) {
+		wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	os_memset(&addr, 0, sizeof(addr));
+	addr.sll_family = AF_PACKET;
+	addr.sll_ifindex = ifr.ifr_ifindex;
+	wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d",
+		   addr.sll_ifindex);
+
+	if (bind(drv->common.sock, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+	{
+		wpa_printf(MSG_ERROR, "bind: %s", strerror(errno));
+		return -1;
+	}
+
+	/* filter multicast address */
+	if (wired_multicast_membership(drv->common.sock, ifr.ifr_ifindex,
+				       pae_group_addr, 1) < 0) {
+		wpa_printf(MSG_ERROR, "wired: Failed to add multicast group "
+			   "membership");
+		return -1;
+	}
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name));
+	if (ioctl(drv->common.sock, SIOCGIFHWADDR, &ifr) != 0) {
+		wpa_printf(MSG_ERROR, "ioctl(SIOCGIFHWADDR): %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
+		wpa_printf(MSG_INFO, "Invalid HW-addr family 0x%04x",
+			   ifr.ifr_hwaddr.sa_family);
+		return -1;
+	}
+	os_memcpy(own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+	return 0;
+#else /* __linux__ */
+	return -1;
+#endif /* __linux__ */
+}
+
+
+static void * macsec_drv_hapd_init(struct hostapd_data *hapd,
+				   struct wpa_init_params *params)
+{
+	struct macsec_drv_data *drv;
+
+	drv = os_zalloc(sizeof(struct macsec_drv_data));
+	if (drv == NULL) {
+		wpa_printf(MSG_INFO,
+			   "Could not allocate memory for wired driver data");
+		return NULL;
+	}
+
+	drv->common.ctx = hapd;
+	os_strlcpy(drv->common.ifname, params->ifname,
+		   sizeof(drv->common.ifname));
+	drv->use_pae_group_addr = params->use_pae_group_addr;
+
+	if (macsec_drv_init_sockets(drv, params->own_addr)) {
+		os_free(drv);
+		return NULL;
+	}
+
+	return drv;
+}
+
+
+static void macsec_drv_hapd_deinit(void *priv)
+{
+	struct macsec_drv_data *drv = priv;
+
+	if (drv->common.sock >= 0) {
+		eloop_unregister_read_sock(drv->common.sock);
+		close(drv->common.sock);
+	}
+
+	os_free(drv);
+}
+
+
+static int macsec_drv_send_eapol(void *priv, const u8 *addr,
+				 const u8 *data, size_t data_len, int encrypt,
+				 const u8 *own_addr, u32 flags, int link_id)
+{
+	struct macsec_drv_data *drv = priv;
+	struct ieee8023_hdr *hdr;
+	size_t len;
+	u8 *pos;
+	int res;
+
+	len = sizeof(*hdr) + data_len;
+	hdr = os_zalloc(len);
+	if (hdr == NULL) {
+		wpa_printf(MSG_INFO,
+			   "%s: malloc() failed (len=%lu)",
+			   __func__, (unsigned long) len);
+		return -1;
+	}
+
+	os_memcpy(hdr->dest, drv->use_pae_group_addr ? pae_group_addr : addr,
+		  ETH_ALEN);
+	os_memcpy(hdr->src, own_addr, ETH_ALEN);
+	hdr->ethertype = htons(ETH_P_PAE);
+
+	pos = (u8 *) (hdr + 1);
+	os_memcpy(pos, data, data_len);
+
+	res = send(drv->common.sock, (u8 *) hdr, len, 0);
+	os_free(hdr);
+
+	if (res < 0) {
+		wpa_printf(MSG_ERROR,
+			   "%s: packet len: %lu - failed: send: %s",
+			   __func__, (unsigned long) len, strerror(errno));
+	}
+
+	return res;
+}
+
+
+const struct wpa_driver_ops wpa_driver_macsec_linux_ops = {
+	.name = "macsec_linux",
+	.desc = "MACsec Ethernet driver for Linux",
+	.get_ssid = driver_wired_get_ssid,
+	.get_bssid = driver_wired_get_bssid,
+	.get_capa = driver_wired_get_capa,
+	.init = macsec_drv_wpa_init,
+	.deinit = macsec_drv_wpa_deinit,
+	.hapd_init = macsec_drv_hapd_init,
+	.hapd_deinit = macsec_drv_hapd_deinit,
+	.hapd_send_eapol = macsec_drv_send_eapol,
+
+	.macsec_init = macsec_drv_macsec_init,
+	.macsec_deinit = macsec_drv_macsec_deinit,
+	.macsec_get_capability = macsec_drv_get_capability,
+	.enable_protect_frames = macsec_drv_enable_protect_frames,
+	.enable_encrypt = macsec_drv_enable_encrypt,
+	.set_replay_protect = macsec_drv_set_replay_protect,
+	.set_offload = macsec_drv_set_offload,
+	.set_current_cipher_suite = macsec_drv_set_current_cipher_suite,
+	.enable_controlled_port = macsec_drv_enable_controlled_port,
+	.get_receive_lowest_pn = macsec_drv_get_receive_lowest_pn,
+	.set_receive_lowest_pn = macsec_drv_set_receive_lowest_pn,
+	.get_transmit_next_pn = macsec_drv_get_transmit_next_pn,
+	.set_transmit_next_pn = macsec_drv_set_transmit_next_pn,
+	.create_receive_sc = macsec_drv_create_receive_sc,
+	.delete_receive_sc = macsec_drv_delete_receive_sc,
+	.create_receive_sa = macsec_drv_create_receive_sa,
+	.delete_receive_sa = macsec_drv_delete_receive_sa,
+	.enable_receive_sa = macsec_drv_enable_receive_sa,
+	.disable_receive_sa = macsec_drv_disable_receive_sa,
+	.create_transmit_sc = macsec_drv_create_transmit_sc,
+	.delete_transmit_sc = macsec_drv_delete_transmit_sc,
+	.create_transmit_sa = macsec_drv_create_transmit_sa,
+	.delete_transmit_sa = macsec_drv_delete_transmit_sa,
+	.enable_transmit_sa = macsec_drv_enable_transmit_sa,
+	.disable_transmit_sa = macsec_drv_disable_transmit_sa,
+
+	.status = macsec_drv_status,
+};
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_macsec_qca.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_macsec_qca.c
new file mode 100644
index 0000000..58c48c2
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_macsec_qca.c
@@ -0,0 +1,1071 @@
+/*
+ * Wired Ethernet driver interface for QCA MACsec driver
+ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004, Gunter Burchardt <tira@isx.de>
+ * Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
+ * Copyright (c) 2019, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <inttypes.h>
+#ifdef __linux__
+#include <netpacket/packet.h>
+#include <net/if_arp.h>
+#include <net/if.h>
+#endif /* __linux__ */
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */
+#ifdef __sun__
+#include <sys/sockio.h>
+#endif /* __sun__ */
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/defs.h"
+#include "common/ieee802_1x_defs.h"
+#include "common/eapol_common.h"
+#include "pae/ieee802_1x_kay.h"
+#include "driver.h"
+#include "driver_wired_common.h"
+
+#include "nss_macsec_secy.h"
+#include "nss_macsec_secy_rx.h"
+#include "nss_macsec_secy_tx.h"
+
+#define MAXSC 16
+
+#define SAK_128_LEN	16
+#define SAK_256_LEN	32
+
+/* TCI field definition */
+#define TCI_ES                0x40
+#define TCI_SC                0x20
+#define TCI_SCB               0x10
+#define TCI_E                 0x08
+#define TCI_C                 0x04
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+struct channel_map {
+	struct ieee802_1x_mka_sci sci;
+};
+
+struct macsec_qca_data {
+	struct driver_wired_common_data common;
+
+	int use_pae_group_addr;
+	u32 secy_id;
+
+	/* shadow */
+	bool always_include_sci;
+	bool use_es;
+	bool use_scb;
+	bool protect_frames;
+	bool replay_protect;
+	u32 replay_window;
+
+	struct channel_map receive_channel_map[MAXSC];
+	struct channel_map transmit_channel_map[MAXSC];
+};
+
+
+static void __macsec_drv_init(struct macsec_qca_data *drv)
+{
+	int ret = 0;
+	fal_rx_ctl_filt_t rx_ctl_filt;
+	fal_tx_ctl_filt_t tx_ctl_filt;
+
+	wpa_printf(MSG_INFO, "%s: secy_id=%d", __func__, drv->secy_id);
+
+	/* Enable Secy and Let EAPoL bypass */
+	ret = nss_macsec_secy_en_set(drv->secy_id, true);
+	if (ret)
+		wpa_printf(MSG_ERROR, "nss_macsec_secy_en_set: FAIL");
+
+	ret = nss_macsec_secy_sc_sa_mapping_mode_set(drv->secy_id,
+						     FAL_SC_SA_MAP_1_4);
+	if (ret)
+		wpa_printf(MSG_ERROR,
+			   "nss_macsec_secy_sc_sa_mapping_mode_set: FAIL");
+
+	os_memset(&rx_ctl_filt, 0, sizeof(rx_ctl_filt));
+	rx_ctl_filt.bypass = 1;
+	rx_ctl_filt.match_type = IG_CTL_COMPARE_ETHER_TYPE;
+	rx_ctl_filt.match_mask = 0xffff;
+	rx_ctl_filt.ether_type_da_range = 0x888e;
+	ret = nss_macsec_secy_rx_ctl_filt_set(drv->secy_id, 0, &rx_ctl_filt);
+	if (ret)
+		wpa_printf(MSG_ERROR, "nss_macsec_secy_rx_ctl_filt_set: FAIL");
+
+	os_memset(&tx_ctl_filt, 0, sizeof(tx_ctl_filt));
+	tx_ctl_filt.bypass = 1;
+	tx_ctl_filt.match_type = EG_CTL_COMPARE_ETHER_TYPE;
+	tx_ctl_filt.match_mask = 0xffff;
+	tx_ctl_filt.ether_type_da_range = 0x888e;
+	ret = nss_macsec_secy_tx_ctl_filt_set(drv->secy_id, 0, &tx_ctl_filt);
+	if (ret)
+		wpa_printf(MSG_ERROR, "nss_macsec_secy_tx_ctl_filt_set: FAIL");
+}
+
+
+static void __macsec_drv_deinit(struct macsec_qca_data *drv)
+{
+	nss_macsec_secy_en_set(drv->secy_id, false);
+	nss_macsec_secy_rx_sc_del_all(drv->secy_id);
+	nss_macsec_secy_tx_sc_del_all(drv->secy_id);
+}
+
+
+#ifdef __linux__
+
+static void macsec_qca_handle_data(void *ctx, unsigned char *buf, size_t len)
+{
+#ifdef HOSTAPD
+	struct ieee8023_hdr *hdr;
+	u8 *pos, *sa;
+	size_t left;
+	union wpa_event_data event;
+
+	/* at least 6 bytes src macaddress, 6 bytes dst macaddress
+	 * and 2 bytes ethertype
+	*/
+	if (len < 14) {
+		wpa_printf(MSG_MSGDUMP,
+			   "macsec_qca_handle_data: too short (%lu)",
+			   (unsigned long) len);
+		return;
+	}
+	hdr = (struct ieee8023_hdr *) buf;
+
+	switch (ntohs(hdr->ethertype)) {
+	case ETH_P_PAE:
+		wpa_printf(MSG_MSGDUMP, "Received EAPOL packet");
+		sa = hdr->src;
+		os_memset(&event, 0, sizeof(event));
+		event.new_sta.addr = sa;
+		wpa_supplicant_event(ctx, EVENT_NEW_STA, &event);
+
+		pos = (u8 *) (hdr + 1);
+		left = len - sizeof(*hdr);
+		drv_event_eapol_rx(ctx, sa, pos, left);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "Unknown ethertype 0x%04x in data frame",
+			   ntohs(hdr->ethertype));
+		break;
+	}
+#endif /* HOSTAPD */
+}
+
+
+static void macsec_qca_handle_read(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	int len;
+	unsigned char buf[3000];
+
+	len = recv(sock, buf, sizeof(buf), 0);
+	if (len < 0) {
+		wpa_printf(MSG_ERROR, "macsec_qca: recv: %s", strerror(errno));
+		return;
+	}
+
+	macsec_qca_handle_data(eloop_ctx, buf, len);
+}
+
+#endif /* __linux__ */
+
+
+static int macsec_qca_init_sockets(struct macsec_qca_data *drv, u8 *own_addr)
+{
+#ifdef __linux__
+	struct ifreq ifr;
+	struct sockaddr_ll addr;
+
+	drv->common.sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE));
+	if (drv->common.sock < 0) {
+		wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	if (eloop_register_read_sock(drv->common.sock, macsec_qca_handle_read,
+				     drv->common.ctx, NULL)) {
+		wpa_printf(MSG_INFO, "Could not register read socket");
+		return -1;
+	}
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name));
+	if (ioctl(drv->common.sock, SIOCGIFINDEX, &ifr) != 0) {
+		wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	os_memset(&addr, 0, sizeof(addr));
+	addr.sll_family = AF_PACKET;
+	addr.sll_ifindex = ifr.ifr_ifindex;
+	wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d",
+		   addr.sll_ifindex);
+
+	if (bind(drv->common.sock, (struct sockaddr *) &addr,
+		 sizeof(addr)) < 0) {
+		wpa_printf(MSG_ERROR, "macsec_qca: bind: %s", strerror(errno));
+		return -1;
+	}
+
+	/* filter multicast address */
+	if (wired_multicast_membership(drv->common.sock, ifr.ifr_ifindex,
+				       pae_group_addr, 1) < 0) {
+		wpa_printf(MSG_ERROR,
+			"macsec_qca_init_sockets: Failed to add multicast group membership");
+		return -1;
+	}
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name));
+	if (ioctl(drv->common.sock, SIOCGIFHWADDR, &ifr) != 0) {
+		wpa_printf(MSG_ERROR, "ioctl(SIOCGIFHWADDR): %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
+		wpa_printf(MSG_INFO, "Invalid HW-addr family 0x%04x",
+			   ifr.ifr_hwaddr.sa_family);
+		return -1;
+	}
+	os_memcpy(own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+	return 0;
+#else /* __linux__ */
+	return -1;
+#endif /* __linux__ */
+}
+
+
+static int macsec_qca_secy_id_get(const char *ifname, u32 *secy_id)
+{
+#ifdef NSS_MACSEC_SECY_ID_GET_FUNC
+	/* Get secy id from nss macsec driver */
+	return nss_macsec_secy_id_get((u8 *) ifname, secy_id);
+#else /* NSS_MACSEC_SECY_ID_GET_FUNC */
+	/* Board specific settings */
+	if (os_strcmp(ifname, "eth2") == 0) {
+		*secy_id = 1;
+	} else if (os_strcmp(ifname, "eth3") == 0) {
+		*secy_id = 2;
+	} else if (os_strcmp(ifname, "eth4") == 0 ||
+		   os_strcmp(ifname, "eth0") == 0) {
+		*secy_id = 0;
+	} else if (os_strcmp(ifname, "eth5") == 0 ||
+		   os_strcmp(ifname, "eth1") == 0) {
+		*secy_id = 1;
+	} else {
+		*secy_id = -1;
+		return -1;
+	}
+
+	return 0;
+#endif /* NSS_MACSEC_SECY_ID_GET_FUNC */
+}
+
+
+static void * macsec_qca_init(void *ctx, const char *ifname)
+{
+	struct macsec_qca_data *drv;
+
+	drv = os_zalloc(sizeof(*drv));
+	if (drv == NULL)
+		return NULL;
+
+	if (macsec_qca_secy_id_get(ifname, &drv->secy_id)) {
+		wpa_printf(MSG_ERROR,
+			   "macsec_qca: Failed to get secy_id for %s", ifname);
+		os_free(drv);
+		return NULL;
+	}
+
+	if (driver_wired_init_common(&drv->common, ifname, ctx) < 0) {
+		os_free(drv);
+		return NULL;
+	}
+
+	return drv;
+}
+
+
+static void macsec_qca_deinit(void *priv)
+{
+	struct macsec_qca_data *drv = priv;
+
+	driver_wired_deinit_common(&drv->common);
+	os_free(drv);
+}
+
+
+static void * macsec_qca_hapd_init(struct hostapd_data *hapd,
+				   struct wpa_init_params *params)
+{
+	struct macsec_qca_data *drv;
+
+	drv = os_zalloc(sizeof(struct macsec_qca_data));
+	if (!drv) {
+		wpa_printf(MSG_INFO,
+			   "Could not allocate memory for macsec_qca driver data");
+		return NULL;
+	}
+
+	if (macsec_qca_secy_id_get(params->ifname, &drv->secy_id)) {
+		wpa_printf(MSG_ERROR,
+			   "macsec_qca: Failed to get secy_id for %s",
+			   params->ifname);
+		os_free(drv);
+		return NULL;
+	}
+
+	drv->common.ctx = hapd;
+	os_strlcpy(drv->common.ifname, params->ifname,
+		   sizeof(drv->common.ifname));
+	drv->use_pae_group_addr = params->use_pae_group_addr;
+
+	if (macsec_qca_init_sockets(drv, params->own_addr)) {
+		os_free(drv);
+		return NULL;
+	}
+
+	return drv;
+}
+
+
+static void macsec_qca_hapd_deinit(void *priv)
+{
+	struct macsec_qca_data *drv = priv;
+
+	if (drv->common.sock >= 0) {
+		eloop_unregister_read_sock(drv->common.sock);
+		close(drv->common.sock);
+	}
+
+	os_free(drv);
+}
+
+
+static int macsec_qca_send_eapol(void *priv, const u8 *addr,
+				 const u8 *data, size_t data_len, int encrypt,
+				 const u8 *own_addr, u32 flags, int link_id)
+{
+	struct macsec_qca_data *drv = priv;
+	struct ieee8023_hdr *hdr;
+	size_t len;
+	u8 *pos;
+	int res;
+
+	len = sizeof(*hdr) + data_len;
+	hdr = os_zalloc(len);
+	if (!hdr) {
+		wpa_printf(MSG_INFO,
+			   "malloc() failed for macsec_qca_send_eapol(len=%lu)",
+			   (unsigned long) len);
+		return -1;
+	}
+
+	os_memcpy(hdr->dest, drv->use_pae_group_addr ? pae_group_addr : addr,
+		  ETH_ALEN);
+	os_memcpy(hdr->src, own_addr, ETH_ALEN);
+	hdr->ethertype = htons(ETH_P_PAE);
+
+	pos = (u8 *) (hdr + 1);
+	os_memcpy(pos, data, data_len);
+
+	res = send(drv->common.sock, (u8 *) hdr, len, 0);
+	os_free(hdr);
+
+	if (res < 0) {
+		wpa_printf(MSG_ERROR,
+			   "macsec_qca_send_eapol - packet len: %lu - failed: send: %s",
+			   (unsigned long) len, strerror(errno));
+	}
+
+	return res;
+}
+
+
+static int macsec_qca_macsec_init(void *priv, struct macsec_init_params *params)
+{
+	struct macsec_qca_data *drv = priv;
+
+	drv->always_include_sci = params->always_include_sci;
+	drv->use_es = params->use_es;
+	drv->use_scb = params->use_scb;
+
+	wpa_printf(MSG_DEBUG, "%s: es=%d, scb=%d, sci=%d",
+		   __func__, drv->use_es, drv->use_scb,
+		   drv->always_include_sci);
+
+	__macsec_drv_init(drv);
+
+	return 0;
+}
+
+
+static int macsec_qca_macsec_deinit(void *priv)
+{
+	struct macsec_qca_data *drv = priv;
+
+	wpa_printf(MSG_DEBUG, "%s", __func__);
+
+	__macsec_drv_deinit(drv);
+
+	return 0;
+}
+
+
+static int macsec_qca_get_capability(void *priv, enum macsec_cap *cap)
+{
+	wpa_printf(MSG_DEBUG, "%s", __func__);
+
+	*cap = MACSEC_CAP_INTEG_AND_CONF_0_30_50;
+
+	return 0;
+}
+
+
+static int macsec_qca_enable_protect_frames(void *priv, bool enabled)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret = 0;
+
+	wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
+
+	drv->protect_frames = enabled;
+
+	return ret;
+}
+
+
+static int macsec_qca_set_replay_protect(void *priv, bool enabled,
+					 unsigned int window)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret = 0;
+
+	wpa_printf(MSG_DEBUG, "%s: enabled=%d, win=%u",
+		   __func__, enabled, window);
+
+	drv->replay_protect = enabled;
+	drv->replay_window = window;
+
+	return ret;
+}
+
+
+static fal_cipher_suite_e macsec_qca_cs_type_get(u64 cs)
+{
+	if (cs == CS_ID_GCM_AES_128)
+		return FAL_CIPHER_SUITE_AES_GCM_128;
+	if (cs == CS_ID_GCM_AES_256)
+		return FAL_CIPHER_SUITE_AES_GCM_256;
+	return FAL_CIPHER_SUITE_MAX;
+}
+
+
+static int macsec_qca_set_current_cipher_suite(void *priv, u64 cs)
+{
+	struct macsec_qca_data *drv = priv;
+	fal_cipher_suite_e cs_type;
+
+	if (cs != CS_ID_GCM_AES_128 && cs != CS_ID_GCM_AES_256) {
+		wpa_printf(MSG_ERROR,
+			   "%s: NOT supported CipherSuite: %016" PRIx64,
+			   __func__, cs);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "%s: CipherSuite: %016" PRIx64, __func__, cs);
+
+	cs_type = macsec_qca_cs_type_get(cs);
+	return nss_macsec_secy_cipher_suite_set(drv->secy_id, cs_type);
+}
+
+
+static int macsec_qca_enable_controlled_port(void *priv, bool enabled)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret = 0;
+
+	wpa_printf(MSG_DEBUG, "%s: enable=%d", __func__, enabled);
+
+	ret += nss_macsec_secy_controlled_port_en_set(drv->secy_id, enabled);
+
+	return ret;
+}
+
+
+static int macsec_qca_lookup_channel(struct channel_map *map,
+				     struct ieee802_1x_mka_sci *sci,
+				     u32 *channel)
+{
+	u32 i;
+
+	for (i = 0; i < MAXSC; i++) {
+		if (os_memcmp(&map[i].sci, sci,
+			      sizeof(struct ieee802_1x_mka_sci)) == 0) {
+			*channel = i;
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+
+static void macsec_qca_register_channel(struct channel_map *map,
+					struct ieee802_1x_mka_sci *sci,
+					u32 channel)
+{
+	os_memcpy(&map[channel].sci, sci, sizeof(struct ieee802_1x_mka_sci));
+}
+
+
+static int macsec_qca_lookup_receive_channel(struct macsec_qca_data *drv,
+					     struct receive_sc *sc,
+					     u32 *channel)
+{
+	return macsec_qca_lookup_channel(drv->receive_channel_map, &sc->sci,
+					 channel);
+}
+
+
+static void macsec_qca_register_receive_channel(struct macsec_qca_data *drv,
+						struct receive_sc *sc,
+						u32 channel)
+{
+	macsec_qca_register_channel(drv->receive_channel_map, &sc->sci,
+				    channel);
+}
+
+
+static int macsec_qca_lookup_transmit_channel(struct macsec_qca_data *drv,
+					      struct transmit_sc *sc,
+					      u32 *channel)
+{
+	return macsec_qca_lookup_channel(drv->transmit_channel_map, &sc->sci,
+					 channel);
+}
+
+
+static void macsec_qca_register_transmit_channel(struct macsec_qca_data *drv,
+						 struct transmit_sc *sc,
+						 u32 channel)
+{
+	macsec_qca_register_channel(drv->transmit_channel_map, &sc->sci,
+				    channel);
+}
+
+
+static int macsec_qca_get_receive_lowest_pn(void *priv, struct receive_sa *sa)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret = 0;
+	u32 next_pn = 0;
+	bool enabled = false;
+	u32 win;
+	u32 channel;
+
+	ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel);
+	if (ret != 0)
+		return ret;
+
+	ret += nss_macsec_secy_rx_sa_next_pn_get(drv->secy_id, channel, sa->an,
+						 &next_pn);
+	ret += nss_macsec_secy_rx_sc_replay_protect_get(drv->secy_id, channel,
+							&enabled);
+	ret += nss_macsec_secy_rx_sc_anti_replay_window_get(drv->secy_id,
+							    channel, &win);
+
+	if (enabled)
+		sa->lowest_pn = (next_pn > win) ? (next_pn - win) : 1;
+	else
+		sa->lowest_pn = next_pn;
+
+	wpa_printf(MSG_DEBUG, "%s: lpn=0x%x", __func__, sa->lowest_pn);
+
+	return ret;
+}
+
+
+static int macsec_qca_get_transmit_next_pn(void *priv, struct transmit_sa *sa)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret = 0;
+	u32 channel;
+
+	ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
+	if (ret != 0)
+		return ret;
+
+	ret += nss_macsec_secy_tx_sa_next_pn_get(drv->secy_id, channel, sa->an,
+						 &sa->next_pn);
+
+	wpa_printf(MSG_DEBUG, "%s: npn=0x%x", __func__, sa->next_pn);
+
+	return ret;
+}
+
+
+static int macsec_qca_set_transmit_next_pn(void *priv, struct transmit_sa *sa)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret = 0;
+	u32 channel;
+
+	ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
+	if (ret != 0)
+		return ret;
+
+	ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, sa->an,
+						 sa->next_pn);
+
+	wpa_printf(MSG_INFO, "%s: npn=0x%x", __func__, sa->next_pn);
+
+	return ret;
+}
+
+
+static int macsec_qca_get_available_receive_sc(void *priv, u32 *channel)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret = 0;
+	u32 sc_ch = 0;
+	bool in_use = false;
+
+	for (sc_ch = 0; sc_ch < MAXSC; sc_ch++) {
+		ret = nss_macsec_secy_rx_sc_in_used_get(drv->secy_id, sc_ch,
+							&in_use);
+		if (ret)
+			continue;
+
+		if (!in_use) {
+			*channel = sc_ch;
+			wpa_printf(MSG_DEBUG, "%s: channel=%d",
+				   __func__, *channel);
+			return 0;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "%s: no available channel", __func__);
+
+	return -1;
+}
+
+
+static int macsec_qca_create_receive_sc(void *priv, struct receive_sc *sc,
+					unsigned int conf_offset,
+					int validation)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret = 0;
+	fal_rx_prc_lut_t entry;
+	fal_rx_sc_validate_frame_e vf;
+	enum validate_frames validate_frames = validation;
+	u32 channel;
+	const u8 *sci_addr = sc->sci.addr;
+	u16 sci_port = be_to_host16(sc->sci.port);
+
+	ret = macsec_qca_get_available_receive_sc(priv, &channel);
+	if (ret != 0)
+		return ret;
+
+	wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
+
+	/* rx prc lut */
+	os_memset(&entry, 0, sizeof(entry));
+
+	os_memcpy(entry.sci, sci_addr, ETH_ALEN);
+	entry.sci[6] = (sci_port >> 8) & 0xff;
+	entry.sci[7] = sci_port & 0xff;
+	entry.sci_mask = 0xf;
+
+	entry.valid = 1;
+	entry.channel = channel;
+	entry.action = FAL_RX_PRC_ACTION_PROCESS;
+	entry.offset = conf_offset;
+
+	/* rx validate frame  */
+	if (validate_frames == Strict)
+		vf = FAL_RX_SC_VALIDATE_FRAME_STRICT;
+	else if (validate_frames == Checked)
+		vf = FAL_RX_SC_VALIDATE_FRAME_CHECK;
+	else
+		vf = FAL_RX_SC_VALIDATE_FRAME_DISABLED;
+
+	ret += nss_macsec_secy_rx_prc_lut_set(drv->secy_id, channel, &entry);
+	ret += nss_macsec_secy_rx_sc_create(drv->secy_id, channel);
+	ret += nss_macsec_secy_rx_sc_validate_frame_set(drv->secy_id, channel,
+							vf);
+	ret += nss_macsec_secy_rx_sc_replay_protect_set(drv->secy_id, channel,
+							drv->replay_protect);
+	ret += nss_macsec_secy_rx_sc_anti_replay_window_set(drv->secy_id,
+							    channel,
+							    drv->replay_window);
+
+	macsec_qca_register_receive_channel(drv, sc, channel);
+
+	return ret;
+}
+
+
+static int macsec_qca_delete_receive_sc(void *priv, struct receive_sc *sc)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret;
+	fal_rx_prc_lut_t entry;
+	u32 channel;
+
+	ret = macsec_qca_lookup_receive_channel(priv, sc, &channel);
+	if (ret != 0)
+		return ret;
+
+	wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
+
+	/* rx prc lut */
+	os_memset(&entry, 0, sizeof(entry));
+
+	ret += nss_macsec_secy_rx_sc_del(drv->secy_id, channel);
+	ret += nss_macsec_secy_rx_prc_lut_set(drv->secy_id, channel, &entry);
+
+	return ret;
+}
+
+
+static int macsec_qca_create_receive_sa(void *priv, struct receive_sa *sa)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret;
+	fal_rx_sak_t rx_sak;
+	int i = 0;
+	u32 channel;
+	fal_rx_prc_lut_t entry;
+	u32 offset;
+
+	ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel);
+	if (ret != 0)
+		return ret;
+
+	wpa_printf(MSG_DEBUG, "%s, channel=%d, an=%d, lpn=0x%x",
+		   __func__, channel, sa->an, sa->lowest_pn);
+
+	os_memset(&rx_sak, 0, sizeof(rx_sak));
+	rx_sak.sak_len = sa->pkey->key_len;
+	if (sa->pkey->key_len == SAK_128_LEN) {
+		for (i = 0; i < 16; i++)
+			rx_sak.sak[i] = sa->pkey->key[15 - i];
+	} else if (sa->pkey->key_len == SAK_256_LEN) {
+		for (i = 0; i < 16; i++) {
+			rx_sak.sak1[i] = sa->pkey->key[15 - i];
+			rx_sak.sak[i] = sa->pkey->key[31 - i];
+		}
+	} else {
+		return -1;
+	}
+
+	if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_0)
+		offset = 0;
+	else if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_30)
+		offset = 30;
+	else if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_50)
+		offset = 50;
+	else
+		return -1;
+	ret += nss_macsec_secy_rx_prc_lut_get(drv->secy_id, channel, &entry);
+	entry.offset = offset;
+	ret += nss_macsec_secy_rx_prc_lut_set(drv->secy_id, channel, &entry);
+	ret += nss_macsec_secy_rx_sa_create(drv->secy_id, channel, sa->an);
+	ret += nss_macsec_secy_rx_sak_set(drv->secy_id, channel, sa->an,
+					  &rx_sak);
+
+	return ret;
+}
+
+
+static int macsec_qca_enable_receive_sa(void *priv, struct receive_sa *sa)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret;
+	u32 channel;
+
+	ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel);
+	if (ret != 0)
+		return ret;
+
+	wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel,
+		   sa->an);
+
+	ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, sa->an,
+					    true);
+
+	return ret;
+}
+
+
+static int macsec_qca_disable_receive_sa(void *priv, struct receive_sa *sa)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret;
+	u32 channel;
+
+	ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel);
+	if (ret != 0)
+		return ret;
+
+	wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel,
+		   sa->an);
+
+	ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, sa->an,
+					    false);
+
+	return ret;
+}
+
+
+static int macsec_qca_get_available_transmit_sc(void *priv, u32 *channel)
+{
+	struct macsec_qca_data *drv = priv;
+	u32 sc_ch = 0;
+	bool in_use = false;
+
+	for (sc_ch = 0; sc_ch < MAXSC; sc_ch++) {
+		if (nss_macsec_secy_tx_sc_in_used_get(drv->secy_id, sc_ch,
+						      &in_use))
+			continue;
+
+		if (!in_use) {
+			*channel = sc_ch;
+			wpa_printf(MSG_DEBUG, "%s: channel=%d",
+				   __func__, *channel);
+			return 0;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "%s: no available channel", __func__);
+
+	return -1;
+}
+
+
+static int macsec_qca_create_transmit_sc(void *priv, struct transmit_sc *sc,
+					 unsigned int conf_offset)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret;
+	fal_tx_class_lut_t entry;
+	u8 psci[ETH_ALEN + 2];
+	u32 channel;
+	u16 sci_port = be_to_host16(sc->sci.port);
+
+	ret = macsec_qca_get_available_transmit_sc(priv, &channel);
+	if (ret != 0)
+		return ret;
+
+	wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
+
+	/* class lut */
+	os_memset(&entry, 0, sizeof(entry));
+
+	entry.valid = 1;
+	entry.action = FAL_TX_CLASS_ACTION_FORWARD;
+	entry.channel = channel;
+
+	os_memcpy(psci, sc->sci.addr, ETH_ALEN);
+	psci[6] = (sci_port >> 8) & 0xff;
+	psci[7] = sci_port & 0xff;
+
+	ret += nss_macsec_secy_tx_class_lut_set(drv->secy_id, channel, &entry);
+	ret += nss_macsec_secy_tx_sc_create(drv->secy_id, channel, psci, 8);
+	ret += nss_macsec_secy_tx_sc_protect_set(drv->secy_id, channel,
+						 drv->protect_frames);
+	ret += nss_macsec_secy_tx_sc_confidentiality_offset_set(drv->secy_id,
+								channel,
+								conf_offset);
+
+	macsec_qca_register_transmit_channel(drv, sc, channel);
+
+	return ret;
+}
+
+
+static int macsec_qca_delete_transmit_sc(void *priv, struct transmit_sc *sc)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret;
+	fal_tx_class_lut_t entry;
+	u32 channel;
+
+	ret = macsec_qca_lookup_transmit_channel(priv, sc, &channel);
+	if (ret != 0)
+		return ret;
+
+	wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
+
+	/* class lut */
+	os_memset(&entry, 0, sizeof(entry));
+
+	ret += nss_macsec_secy_tx_class_lut_set(drv->secy_id, channel, &entry);
+	ret += nss_macsec_secy_tx_sc_del(drv->secy_id, channel);
+
+	return ret;
+}
+
+
+static int macsec_qca_create_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret;
+	u8 tci = 0;
+	fal_tx_sak_t tx_sak;
+	int i;
+	u32 channel;
+	u32 offset;
+
+	ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
+	if (ret != 0)
+		return ret;
+
+	wpa_printf(MSG_DEBUG,
+		   "%s: channel=%d, an=%d, next_pn=0x%x, confidentiality=%d",
+		   __func__, channel, sa->an, sa->next_pn, sa->confidentiality);
+
+	if (drv->always_include_sci)
+		tci |= TCI_SC;
+	else if (drv->use_es)
+		tci |= TCI_ES;
+	else if (drv->use_scb)
+		tci |= TCI_SCB;
+
+	if (sa->confidentiality)
+		tci |= TCI_E | TCI_C;
+
+	os_memset(&tx_sak, 0, sizeof(tx_sak));
+	tx_sak.sak_len = sa->pkey->key_len;
+	if (sa->pkey->key_len == SAK_128_LEN) {
+		for (i = 0; i < 16; i++)
+			tx_sak.sak[i] = sa->pkey->key[15 - i];
+	} else if (sa->pkey->key_len == SAK_256_LEN) {
+		for (i = 0; i < 16; i++) {
+			tx_sak.sak1[i] = sa->pkey->key[15 - i];
+			tx_sak.sak[i] = sa->pkey->key[31 - i];
+		}
+	} else {
+		return -1;
+	}
+
+	if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_0)
+		offset = 0;
+	else if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_30)
+		offset = 30;
+	else if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_50)
+		offset = 50;
+	else
+		return -1;
+	ret += nss_macsec_secy_tx_sc_confidentiality_offset_set(drv->secy_id,
+								channel,
+								offset);
+	ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, sa->an,
+						 sa->next_pn);
+	ret += nss_macsec_secy_tx_sak_set(drv->secy_id, channel, sa->an,
+					  &tx_sak);
+	ret += nss_macsec_secy_tx_sc_tci_7_2_set(drv->secy_id, channel,
+						 (tci >> 2));
+	ret += nss_macsec_secy_tx_sc_an_set(drv->secy_id, channel, sa->an);
+
+	return ret;
+}
+
+
+static int macsec_qca_enable_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret;
+	u32 channel;
+
+	ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
+	if (ret != 0)
+		return ret;
+
+	wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel,
+		   sa->an);
+
+	ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, sa->an,
+					    true);
+
+	return ret;
+}
+
+
+static int macsec_qca_disable_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+	struct macsec_qca_data *drv = priv;
+	int ret;
+	u32 channel;
+
+	ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
+	if (ret != 0)
+		return ret;
+
+	wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel,
+		   sa->an);
+
+	ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, sa->an,
+					    false);
+
+	return ret;
+}
+
+
+const struct wpa_driver_ops wpa_driver_macsec_qca_ops = {
+	.name = "macsec_qca",
+	.desc = "QCA MACsec Ethernet driver",
+	.get_ssid = driver_wired_get_ssid,
+	.get_bssid = driver_wired_get_bssid,
+	.get_capa = driver_wired_get_capa,
+	.init = macsec_qca_init,
+	.deinit = macsec_qca_deinit,
+	.hapd_init = macsec_qca_hapd_init,
+	.hapd_deinit = macsec_qca_hapd_deinit,
+	.hapd_send_eapol = macsec_qca_send_eapol,
+
+	.macsec_init = macsec_qca_macsec_init,
+	.macsec_deinit = macsec_qca_macsec_deinit,
+	.macsec_get_capability = macsec_qca_get_capability,
+	.enable_protect_frames = macsec_qca_enable_protect_frames,
+	.set_replay_protect = macsec_qca_set_replay_protect,
+	.set_current_cipher_suite = macsec_qca_set_current_cipher_suite,
+	.enable_controlled_port = macsec_qca_enable_controlled_port,
+	.get_receive_lowest_pn = macsec_qca_get_receive_lowest_pn,
+	.get_transmit_next_pn = macsec_qca_get_transmit_next_pn,
+	.set_transmit_next_pn = macsec_qca_set_transmit_next_pn,
+	.create_receive_sc = macsec_qca_create_receive_sc,
+	.delete_receive_sc = macsec_qca_delete_receive_sc,
+	.create_receive_sa = macsec_qca_create_receive_sa,
+	.enable_receive_sa = macsec_qca_enable_receive_sa,
+	.disable_receive_sa = macsec_qca_disable_receive_sa,
+	.create_transmit_sc = macsec_qca_create_transmit_sc,
+	.delete_transmit_sc = macsec_qca_delete_transmit_sc,
+	.create_transmit_sa = macsec_qca_create_transmit_sa,
+	.enable_transmit_sa = macsec_qca_enable_transmit_sa,
+	.disable_transmit_sa = macsec_qca_disable_transmit_sa,
+};
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_ndis.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_ndis.c
new file mode 100644
index 0000000..b32e009
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_ndis.c
@@ -0,0 +1,3232 @@
+/*
+ * WPA Supplicant - Windows/NDIS driver interface
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifdef __CYGWIN__
+/* Avoid some header file conflicts by not including standard headers for
+ * cygwin builds when Packet32.h is included. */
+#include "build_config.h"
+int close(int fd);
+#else /* __CYGWIN__ */
+#include "includes.h"
+#endif /* __CYGWIN__ */
+#ifdef CONFIG_USE_NDISUIO
+#include <winsock2.h>
+#else /* CONFIG_USE_NDISUIO */
+#include <Packet32.h>
+#endif /* CONFIG_USE_NDISUIO */
+#ifdef __MINGW32_VERSION
+#include <ddk/ntddndis.h>
+#else /* __MINGW32_VERSION */
+#include <ntddndis.h>
+#endif /* __MINGW32_VERSION */
+
+#ifdef _WIN32_WCE
+#include <winioctl.h>
+#include <nuiouser.h>
+#include <devload.h>
+#endif /* _WIN32_WCE */
+
+#include "common.h"
+#include "driver.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "driver_ndis.h"
+
+int wpa_driver_register_event_cb(struct wpa_driver_ndis_data *drv);
+#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+void wpa_driver_ndis_event_pipe_cb(void *eloop_data, void *user_data);
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+
+static void wpa_driver_ndis_deinit(void *priv);
+static void wpa_driver_ndis_poll(void *drv);
+static void wpa_driver_ndis_poll_timeout(void *eloop_ctx, void *timeout_ctx);
+static int wpa_driver_ndis_adapter_init(struct wpa_driver_ndis_data *drv);
+static int wpa_driver_ndis_adapter_open(struct wpa_driver_ndis_data *drv);
+static void wpa_driver_ndis_adapter_close(struct wpa_driver_ndis_data *drv);
+
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+
+/* FIX: to be removed once this can be compiled with the complete NDIS
+ * header files */
+#ifndef OID_802_11_BSSID
+#define OID_802_11_BSSID 			0x0d010101
+#define OID_802_11_SSID 			0x0d010102
+#define OID_802_11_INFRASTRUCTURE_MODE		0x0d010108
+#define OID_802_11_ADD_WEP			0x0D010113
+#define OID_802_11_REMOVE_WEP			0x0D010114
+#define OID_802_11_DISASSOCIATE			0x0D010115
+#define OID_802_11_BSSID_LIST 			0x0d010217
+#define OID_802_11_AUTHENTICATION_MODE		0x0d010118
+#define OID_802_11_PRIVACY_FILTER		0x0d010119
+#define OID_802_11_BSSID_LIST_SCAN 		0x0d01011A
+#define OID_802_11_WEP_STATUS	 		0x0d01011B
+#define OID_802_11_ENCRYPTION_STATUS OID_802_11_WEP_STATUS
+#define OID_802_11_ADD_KEY 			0x0d01011D
+#define OID_802_11_REMOVE_KEY 			0x0d01011E
+#define OID_802_11_ASSOCIATION_INFORMATION	0x0d01011F
+#define OID_802_11_TEST 			0x0d010120
+#define OID_802_11_CAPABILITY 			0x0d010122
+#define OID_802_11_PMKID 			0x0d010123
+
+#define NDIS_802_11_LENGTH_SSID 32
+#define NDIS_802_11_LENGTH_RATES 8
+#define NDIS_802_11_LENGTH_RATES_EX 16
+
+typedef UCHAR NDIS_802_11_MAC_ADDRESS[6];
+
+typedef struct NDIS_802_11_SSID {
+	ULONG SsidLength;
+	UCHAR Ssid[NDIS_802_11_LENGTH_SSID];
+} NDIS_802_11_SSID;
+
+typedef LONG NDIS_802_11_RSSI;
+
+typedef enum NDIS_802_11_NETWORK_TYPE {
+	Ndis802_11FH,
+	Ndis802_11DS,
+	Ndis802_11OFDM5,
+	Ndis802_11OFDM24,
+	Ndis802_11NetworkTypeMax
+} NDIS_802_11_NETWORK_TYPE;
+
+typedef struct NDIS_802_11_CONFIGURATION_FH {
+	ULONG Length;
+	ULONG HopPattern;
+	ULONG HopSet;
+	ULONG DwellTime;
+} NDIS_802_11_CONFIGURATION_FH;
+
+typedef struct NDIS_802_11_CONFIGURATION {
+	ULONG Length;
+	ULONG BeaconPeriod;
+	ULONG ATIMWindow;
+	ULONG DSConfig;
+	NDIS_802_11_CONFIGURATION_FH FHConfig;
+} NDIS_802_11_CONFIGURATION;
+
+typedef enum NDIS_802_11_NETWORK_INFRASTRUCTURE {
+	Ndis802_11IBSS,
+	Ndis802_11Infrastructure,
+	Ndis802_11AutoUnknown,
+	Ndis802_11InfrastructureMax
+} NDIS_802_11_NETWORK_INFRASTRUCTURE;
+
+typedef enum NDIS_802_11_AUTHENTICATION_MODE {
+	Ndis802_11AuthModeOpen,
+	Ndis802_11AuthModeShared,
+	Ndis802_11AuthModeAutoSwitch,
+	Ndis802_11AuthModeWPA,
+	Ndis802_11AuthModeWPAPSK,
+	Ndis802_11AuthModeWPANone,
+	Ndis802_11AuthModeWPA2,
+	Ndis802_11AuthModeWPA2PSK,
+	Ndis802_11AuthModeMax
+} NDIS_802_11_AUTHENTICATION_MODE;
+
+typedef enum NDIS_802_11_WEP_STATUS {
+	Ndis802_11WEPEnabled,
+	Ndis802_11Encryption1Enabled = Ndis802_11WEPEnabled,
+	Ndis802_11WEPDisabled,
+	Ndis802_11EncryptionDisabled = Ndis802_11WEPDisabled,
+	Ndis802_11WEPKeyAbsent,
+	Ndis802_11Encryption1KeyAbsent = Ndis802_11WEPKeyAbsent,
+	Ndis802_11WEPNotSupported,
+	Ndis802_11EncryptionNotSupported = Ndis802_11WEPNotSupported,
+	Ndis802_11Encryption2Enabled,
+	Ndis802_11Encryption2KeyAbsent,
+	Ndis802_11Encryption3Enabled,
+	Ndis802_11Encryption3KeyAbsent
+} NDIS_802_11_WEP_STATUS, NDIS_802_11_ENCRYPTION_STATUS;
+
+typedef enum NDIS_802_11_PRIVACY_FILTER {
+	Ndis802_11PrivFilterAcceptAll,
+	Ndis802_11PrivFilter8021xWEP
+} NDIS_802_11_PRIVACY_FILTER;
+
+typedef UCHAR NDIS_802_11_RATES[NDIS_802_11_LENGTH_RATES];
+typedef UCHAR NDIS_802_11_RATES_EX[NDIS_802_11_LENGTH_RATES_EX];
+
+typedef struct NDIS_WLAN_BSSID_EX {
+	ULONG Length;
+	NDIS_802_11_MAC_ADDRESS MacAddress; /* BSSID */
+	UCHAR Reserved[2];
+	NDIS_802_11_SSID Ssid;
+	ULONG Privacy;
+	NDIS_802_11_RSSI Rssi;
+	NDIS_802_11_NETWORK_TYPE NetworkTypeInUse;
+	NDIS_802_11_CONFIGURATION Configuration;
+	NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode;
+	NDIS_802_11_RATES_EX SupportedRates;
+	ULONG IELength;
+	UCHAR IEs[1];
+} NDIS_WLAN_BSSID_EX;
+
+typedef struct NDIS_802_11_BSSID_LIST_EX {
+	ULONG NumberOfItems;
+	NDIS_WLAN_BSSID_EX Bssid[1];
+} NDIS_802_11_BSSID_LIST_EX;
+
+typedef struct NDIS_802_11_FIXED_IEs {
+	UCHAR Timestamp[8];
+	USHORT BeaconInterval;
+	USHORT Capabilities;
+} NDIS_802_11_FIXED_IEs;
+
+typedef struct NDIS_802_11_WEP {
+	ULONG Length;
+	ULONG KeyIndex;
+	ULONG KeyLength;
+	UCHAR KeyMaterial[1];
+} NDIS_802_11_WEP;
+
+typedef ULONG NDIS_802_11_KEY_INDEX;
+typedef ULONGLONG NDIS_802_11_KEY_RSC;
+
+typedef struct NDIS_802_11_KEY {
+	ULONG Length;
+	ULONG KeyIndex;
+	ULONG KeyLength;
+	NDIS_802_11_MAC_ADDRESS BSSID;
+	NDIS_802_11_KEY_RSC KeyRSC;
+	UCHAR KeyMaterial[1];
+} NDIS_802_11_KEY;
+
+typedef struct NDIS_802_11_REMOVE_KEY {
+	ULONG Length;
+	ULONG KeyIndex;
+	NDIS_802_11_MAC_ADDRESS BSSID;
+} NDIS_802_11_REMOVE_KEY;
+
+typedef struct NDIS_802_11_AI_REQFI {
+	USHORT Capabilities;
+	USHORT ListenInterval;
+	NDIS_802_11_MAC_ADDRESS CurrentAPAddress;
+} NDIS_802_11_AI_REQFI;
+
+typedef struct NDIS_802_11_AI_RESFI {
+	USHORT Capabilities;
+	USHORT StatusCode;
+	USHORT AssociationId;
+} NDIS_802_11_AI_RESFI;
+
+typedef struct NDIS_802_11_ASSOCIATION_INFORMATION {
+	ULONG Length;
+	USHORT AvailableRequestFixedIEs;
+	NDIS_802_11_AI_REQFI RequestFixedIEs;
+	ULONG RequestIELength;
+	ULONG OffsetRequestIEs;
+	USHORT AvailableResponseFixedIEs;
+	NDIS_802_11_AI_RESFI ResponseFixedIEs;
+	ULONG ResponseIELength;
+	ULONG OffsetResponseIEs;
+} NDIS_802_11_ASSOCIATION_INFORMATION;
+
+typedef struct NDIS_802_11_AUTHENTICATION_ENCRYPTION {
+	NDIS_802_11_AUTHENTICATION_MODE AuthModeSupported;
+	NDIS_802_11_ENCRYPTION_STATUS EncryptStatusSupported;
+} NDIS_802_11_AUTHENTICATION_ENCRYPTION;
+
+typedef struct NDIS_802_11_CAPABILITY {
+	ULONG Length;
+	ULONG Version;
+	ULONG NoOfPMKIDs;
+	ULONG NoOfAuthEncryptPairsSupported;
+	NDIS_802_11_AUTHENTICATION_ENCRYPTION
+		AuthenticationEncryptionSupported[1];
+} NDIS_802_11_CAPABILITY;
+
+typedef UCHAR NDIS_802_11_PMKID_VALUE[16];
+
+typedef struct BSSID_INFO {
+	NDIS_802_11_MAC_ADDRESS BSSID;
+	NDIS_802_11_PMKID_VALUE PMKID;
+} BSSID_INFO;
+
+typedef struct NDIS_802_11_PMKID {
+	ULONG Length;
+	ULONG BSSIDInfoCount;
+	BSSID_INFO BSSIDInfo[1];
+} NDIS_802_11_PMKID;
+
+typedef enum NDIS_802_11_STATUS_TYPE {
+	Ndis802_11StatusType_Authentication,
+	Ndis802_11StatusType_PMKID_CandidateList = 2,
+	Ndis802_11StatusTypeMax
+} NDIS_802_11_STATUS_TYPE;
+
+typedef struct NDIS_802_11_STATUS_INDICATION {
+	NDIS_802_11_STATUS_TYPE StatusType;
+} NDIS_802_11_STATUS_INDICATION;
+
+typedef struct PMKID_CANDIDATE {
+	NDIS_802_11_MAC_ADDRESS BSSID;
+	ULONG Flags;
+} PMKID_CANDIDATE;
+
+#define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED 0x01
+
+typedef struct NDIS_802_11_PMKID_CANDIDATE_LIST {
+	ULONG Version;
+	ULONG NumCandidates;
+	PMKID_CANDIDATE CandidateList[1];
+} NDIS_802_11_PMKID_CANDIDATE_LIST;
+
+typedef struct NDIS_802_11_AUTHENTICATION_REQUEST {
+	ULONG Length;
+	NDIS_802_11_MAC_ADDRESS Bssid;
+	ULONG Flags;
+} NDIS_802_11_AUTHENTICATION_REQUEST;
+
+#define NDIS_802_11_AUTH_REQUEST_REAUTH			0x01
+#define NDIS_802_11_AUTH_REQUEST_KEYUPDATE		0x02
+#define NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR		0x06
+#define NDIS_802_11_AUTH_REQUEST_GROUP_ERROR		0x0E
+
+#endif /* OID_802_11_BSSID */
+
+
+#ifndef OID_802_11_PMKID
+/* Platform SDK for XP did not include WPA2, so add needed definitions */
+
+#define OID_802_11_CAPABILITY 			0x0d010122
+#define OID_802_11_PMKID 			0x0d010123
+
+#define Ndis802_11AuthModeWPA2 6
+#define Ndis802_11AuthModeWPA2PSK 7
+
+#define Ndis802_11StatusType_PMKID_CandidateList 2
+
+typedef struct NDIS_802_11_AUTHENTICATION_ENCRYPTION {
+	NDIS_802_11_AUTHENTICATION_MODE AuthModeSupported;
+	NDIS_802_11_ENCRYPTION_STATUS EncryptStatusSupported;
+} NDIS_802_11_AUTHENTICATION_ENCRYPTION;
+
+typedef struct NDIS_802_11_CAPABILITY {
+	ULONG Length;
+	ULONG Version;
+	ULONG NoOfPMKIDs;
+	ULONG NoOfAuthEncryptPairsSupported;
+	NDIS_802_11_AUTHENTICATION_ENCRYPTION
+		AuthenticationEncryptionSupported[1];
+} NDIS_802_11_CAPABILITY;
+
+typedef UCHAR NDIS_802_11_PMKID_VALUE[16];
+
+typedef struct BSSID_INFO {
+	NDIS_802_11_MAC_ADDRESS BSSID;
+	NDIS_802_11_PMKID_VALUE PMKID;
+} BSSID_INFO;
+
+typedef struct NDIS_802_11_PMKID {
+	ULONG Length;
+	ULONG BSSIDInfoCount;
+	BSSID_INFO BSSIDInfo[1];
+} NDIS_802_11_PMKID;
+
+typedef struct PMKID_CANDIDATE {
+	NDIS_802_11_MAC_ADDRESS BSSID;
+	ULONG Flags;
+} PMKID_CANDIDATE;
+
+#define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED 0x01
+
+typedef struct NDIS_802_11_PMKID_CANDIDATE_LIST {
+	ULONG Version;
+	ULONG NumCandidates;
+	PMKID_CANDIDATE CandidateList[1];
+} NDIS_802_11_PMKID_CANDIDATE_LIST;
+
+#endif /* OID_802_11_CAPABILITY */
+
+
+#ifndef OID_DOT11_CURRENT_OPERATION_MODE
+/* Native 802.11 OIDs */
+#define OID_DOT11_NDIS_START 0x0D010300
+#define OID_DOT11_CURRENT_OPERATION_MODE (OID_DOT11_NDIS_START + 8)
+#define OID_DOT11_SCAN_REQUEST (OID_DOT11_NDIS_START + 11)
+
+typedef enum _DOT11_BSS_TYPE {
+	dot11_BSS_type_infrastructure = 1,
+	dot11_BSS_type_independent = 2,
+	dot11_BSS_type_any = 3
+} DOT11_BSS_TYPE, * PDOT11_BSS_TYPE;
+
+typedef UCHAR DOT11_MAC_ADDRESS[6];
+typedef DOT11_MAC_ADDRESS * PDOT11_MAC_ADDRESS;
+
+typedef enum _DOT11_SCAN_TYPE {
+	dot11_scan_type_active = 1,
+	dot11_scan_type_passive = 2,
+	dot11_scan_type_auto = 3,
+	dot11_scan_type_forced = 0x80000000
+} DOT11_SCAN_TYPE, * PDOT11_SCAN_TYPE;
+
+typedef struct _DOT11_SCAN_REQUEST_V2 {
+	DOT11_BSS_TYPE dot11BSSType;
+	DOT11_MAC_ADDRESS dot11BSSID;
+	DOT11_SCAN_TYPE dot11ScanType;
+	BOOLEAN bRestrictedScan;
+	ULONG udot11SSIDsOffset;
+	ULONG uNumOfdot11SSIDs;
+	BOOLEAN bUseRequestIE;
+	ULONG uRequestIDsOffset;
+	ULONG uNumOfRequestIDs;
+	ULONG uPhyTypeInfosOffset;
+	ULONG uNumOfPhyTypeInfos;
+	ULONG uIEsOffset;
+	ULONG uIEsLength;
+	UCHAR ucBuffer[1];
+} DOT11_SCAN_REQUEST_V2, * PDOT11_SCAN_REQUEST_V2;
+
+#endif /* OID_DOT11_CURRENT_OPERATION_MODE */
+
+#ifdef CONFIG_USE_NDISUIO
+#ifndef _WIN32_WCE
+#ifdef __MINGW32_VERSION
+typedef ULONG NDIS_OID;
+#endif /* __MINGW32_VERSION */
+/* from nuiouser.h */
+#define FSCTL_NDISUIO_BASE      FILE_DEVICE_NETWORK
+
+#define _NDISUIO_CTL_CODE(_Function, _Method, _Access) \
+	CTL_CODE(FSCTL_NDISUIO_BASE, _Function, _Method, _Access)
+
+#define IOCTL_NDISUIO_OPEN_DEVICE \
+	_NDISUIO_CTL_CODE(0x200, METHOD_BUFFERED, \
+			  FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_NDISUIO_QUERY_OID_VALUE \
+	_NDISUIO_CTL_CODE(0x201, METHOD_BUFFERED, \
+			  FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_NDISUIO_SET_OID_VALUE \
+	_NDISUIO_CTL_CODE(0x205, METHOD_BUFFERED, \
+			  FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_NDISUIO_SET_ETHER_TYPE \
+	_NDISUIO_CTL_CODE(0x202, METHOD_BUFFERED, \
+			  FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_NDISUIO_QUERY_BINDING \
+	_NDISUIO_CTL_CODE(0x203, METHOD_BUFFERED, \
+			  FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_NDISUIO_BIND_WAIT \
+	_NDISUIO_CTL_CODE(0x204, METHOD_BUFFERED, \
+			  FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+typedef struct _NDISUIO_QUERY_OID
+{
+    NDIS_OID Oid;
+    UCHAR Data[sizeof(ULONG)];
+} NDISUIO_QUERY_OID, *PNDISUIO_QUERY_OID;
+
+typedef struct _NDISUIO_SET_OID
+{
+    NDIS_OID Oid;
+    UCHAR Data[sizeof(ULONG)];
+} NDISUIO_SET_OID, *PNDISUIO_SET_OID;
+
+typedef struct _NDISUIO_QUERY_BINDING
+{
+	ULONG BindingIndex;
+	ULONG DeviceNameOffset;
+	ULONG DeviceNameLength;
+	ULONG DeviceDescrOffset;
+	ULONG DeviceDescrLength;
+} NDISUIO_QUERY_BINDING, *PNDISUIO_QUERY_BINDING;
+#endif /* _WIN32_WCE */
+#endif /* CONFIG_USE_NDISUIO */
+
+
+static int ndis_get_oid(struct wpa_driver_ndis_data *drv, unsigned int oid,
+			char *data, size_t len)
+{
+#ifdef CONFIG_USE_NDISUIO
+	NDISUIO_QUERY_OID *o;
+	size_t buflen = sizeof(*o) + len;
+	DWORD written;
+	int ret;
+	size_t hdrlen;
+
+	o = os_zalloc(buflen);
+	if (o == NULL)
+		return -1;
+	o->Oid = oid;
+#ifdef _WIN32_WCE
+	o->ptcDeviceName = drv->adapter_name;
+#endif /* _WIN32_WCE */
+	if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_QUERY_OID_VALUE,
+			     o, sizeof(NDISUIO_QUERY_OID), o, buflen, &written,
+			     NULL)) {
+		wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDISUIO_QUERY_OID_VALUE "
+			   "failed (oid=%08x): %d", oid, (int) GetLastError());
+		os_free(o);
+		return -1;
+	}
+	hdrlen = sizeof(NDISUIO_QUERY_OID) - sizeof(o->Data);
+	if (written < hdrlen) {
+		wpa_printf(MSG_DEBUG, "NDIS: query oid=%08x written (%d); "
+			   "too short", oid, (unsigned int) written);
+		os_free(o);
+		return -1;
+	}
+	written -= hdrlen;
+	if (written > len) {
+		wpa_printf(MSG_DEBUG, "NDIS: query oid=%08x written (%d) > "
+			   "len (%d)",oid, (unsigned int) written, len);
+		os_free(o);
+		return -1;
+	}
+	os_memcpy(data, o->Data, written);
+	ret = written;
+	os_free(o);
+	return ret;
+#else /* CONFIG_USE_NDISUIO */
+	char *buf;
+	PACKET_OID_DATA *o;
+	int ret;
+
+	buf = os_zalloc(sizeof(*o) + len);
+	if (buf == NULL)
+		return -1;
+	o = (PACKET_OID_DATA *) buf;
+	o->Oid = oid;
+	o->Length = len;
+
+	if (!PacketRequest(drv->adapter, FALSE, o)) {
+		wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%d) failed",
+			   __func__, oid, len);
+		os_free(buf);
+		return -1;
+	}
+	if (o->Length > len) {
+		wpa_printf(MSG_DEBUG, "%s: oid=0x%x Length (%d) > len (%d)",
+			   __func__, oid, (unsigned int) o->Length, len);
+		os_free(buf);
+		return -1;
+	}
+	os_memcpy(data, o->Data, o->Length);
+	ret = o->Length;
+	os_free(buf);
+	return ret;
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+static int ndis_set_oid(struct wpa_driver_ndis_data *drv, unsigned int oid,
+			const char *data, size_t len)
+{
+#ifdef CONFIG_USE_NDISUIO
+	NDISUIO_SET_OID *o;
+	size_t buflen, reallen;
+	DWORD written;
+	char txt[50];
+
+	os_snprintf(txt, sizeof(txt), "NDIS: Set OID %08x", oid);
+	wpa_hexdump_key(MSG_MSGDUMP, txt, (const u8 *) data, len);
+
+	buflen = sizeof(*o) + len;
+	reallen = buflen - sizeof(o->Data);
+	o = os_zalloc(buflen);
+	if (o == NULL)
+		return -1;
+	o->Oid = oid;
+#ifdef _WIN32_WCE
+	o->ptcDeviceName = drv->adapter_name;
+#endif /* _WIN32_WCE */
+	if (data)
+		os_memcpy(o->Data, data, len);
+	if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_SET_OID_VALUE,
+			     o, reallen, NULL, 0, &written, NULL)) {
+		wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDISUIO_SET_OID_VALUE "
+			   "(oid=%08x) failed: %d", oid, (int) GetLastError());
+		os_free(o);
+		return -1;
+	}
+	os_free(o);
+	return 0;
+#else /* CONFIG_USE_NDISUIO */
+	char *buf;
+	PACKET_OID_DATA *o;
+	char txt[50];
+
+	os_snprintf(txt, sizeof(txt), "NDIS: Set OID %08x", oid);
+	wpa_hexdump_key(MSG_MSGDUMP, txt, (const u8 *) data, len);
+
+	buf = os_zalloc(sizeof(*o) + len);
+	if (buf == NULL)
+		return -1;
+	o = (PACKET_OID_DATA *) buf;
+	o->Oid = oid;
+	o->Length = len;
+	if (data)
+		os_memcpy(o->Data, data, len);
+
+	if (!PacketRequest(drv->adapter, TRUE, o)) {
+		wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%d) failed",
+			   __func__, oid, len);
+		os_free(buf);
+		return -1;
+	}
+	os_free(buf);
+	return 0;
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+static int ndis_set_auth_mode(struct wpa_driver_ndis_data *drv, int mode)
+{
+	u32 auth_mode = mode;
+	if (ndis_set_oid(drv, OID_802_11_AUTHENTICATION_MODE,
+			 (char *) &auth_mode, sizeof(auth_mode)) < 0) {
+		wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
+			   "OID_802_11_AUTHENTICATION_MODE (%d)",
+			   (int) auth_mode);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int ndis_get_auth_mode(struct wpa_driver_ndis_data *drv)
+{
+	u32 auth_mode;
+	int res;
+	res = ndis_get_oid(drv, OID_802_11_AUTHENTICATION_MODE,
+			   (char *) &auth_mode, sizeof(auth_mode));
+	if (res != sizeof(auth_mode)) {
+		wpa_printf(MSG_DEBUG, "NDIS: Failed to get "
+			   "OID_802_11_AUTHENTICATION_MODE");
+		return -1;
+	}
+	return auth_mode;
+}
+
+
+static int ndis_set_encr_status(struct wpa_driver_ndis_data *drv, int encr)
+{
+	u32 encr_status = encr;
+	if (ndis_set_oid(drv, OID_802_11_ENCRYPTION_STATUS,
+			 (char *) &encr_status, sizeof(encr_status)) < 0) {
+		wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
+			   "OID_802_11_ENCRYPTION_STATUS (%d)", encr);
+		return -1;
+	}
+	return 0;
+}
+
+
+static int ndis_get_encr_status(struct wpa_driver_ndis_data *drv)
+{
+	u32 encr;
+	int res;
+	res = ndis_get_oid(drv, OID_802_11_ENCRYPTION_STATUS,
+			   (char *) &encr, sizeof(encr));
+	if (res != sizeof(encr)) {
+		wpa_printf(MSG_DEBUG, "NDIS: Failed to get "
+			   "OID_802_11_ENCRYPTION_STATUS");
+		return -1;
+	}
+	return encr;
+}
+
+
+static int wpa_driver_ndis_get_bssid(void *priv, u8 *bssid)
+{
+	struct wpa_driver_ndis_data *drv = priv;
+
+	if (drv->wired) {
+		/*
+		 * Report PAE group address as the "BSSID" for wired
+		 * connection.
+		 */
+		os_memcpy(bssid, pae_group_addr, ETH_ALEN);
+		return 0;
+	}
+
+	return ndis_get_oid(drv, OID_802_11_BSSID, (char *) bssid, ETH_ALEN) <
+		0 ? -1 : 0;
+}
+
+
+static int wpa_driver_ndis_get_ssid(void *priv, u8 *ssid)
+{
+	struct wpa_driver_ndis_data *drv = priv;
+	NDIS_802_11_SSID buf;
+	int res;
+
+	res = ndis_get_oid(drv, OID_802_11_SSID, (char *) &buf, sizeof(buf));
+	if (res < 4) {
+		wpa_printf(MSG_DEBUG, "NDIS: Failed to get SSID");
+		if (drv->wired) {
+			wpa_printf(MSG_DEBUG, "NDIS: Allow get_ssid failure "
+				   "with a wired interface");
+			return 0;
+		}
+		return -1;
+	}
+	os_memcpy(ssid, buf.Ssid, buf.SsidLength);
+	return buf.SsidLength;
+}
+
+
+static int wpa_driver_ndis_set_ssid(struct wpa_driver_ndis_data *drv,
+				    const u8 *ssid, size_t ssid_len)
+{
+	NDIS_802_11_SSID buf;
+
+	os_memset(&buf, 0, sizeof(buf));
+	buf.SsidLength = ssid_len;
+	os_memcpy(buf.Ssid, ssid, ssid_len);
+	/*
+	 * Make sure radio is marked enabled here so that scan request will not
+	 * force SSID to be changed to a random one in order to enable radio at
+	 * that point.
+	 */
+	drv->radio_enabled = 1;
+	return ndis_set_oid(drv, OID_802_11_SSID, (char *) &buf, sizeof(buf));
+}
+
+
+/* Disconnect using OID_802_11_DISASSOCIATE. This will also turn the radio off.
+ */
+static int wpa_driver_ndis_radio_off(struct wpa_driver_ndis_data *drv)
+{
+	drv->radio_enabled = 0;
+	return ndis_set_oid(drv, OID_802_11_DISASSOCIATE, "    ", 4);
+}
+
+
+/* Disconnect by setting SSID to random (i.e., likely not used). */
+static int wpa_driver_ndis_disconnect(struct wpa_driver_ndis_data *drv)
+{
+	char ssid[SSID_MAX_LEN];
+	int i;
+	for (i = 0; i < SSID_MAX_LEN; i++)
+		ssid[i] = rand() & 0xff;
+	return wpa_driver_ndis_set_ssid(drv, (u8 *) ssid, SSID_MAX_LEN);
+}
+
+
+static int wpa_driver_ndis_deauthenticate(void *priv, const u8 *addr,
+					  u16 reason_code)
+{
+	struct wpa_driver_ndis_data *drv = priv;
+	return wpa_driver_ndis_disconnect(drv);
+}
+
+
+static void wpa_driver_ndis_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
+	wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
+}
+
+
+static int wpa_driver_ndis_scan_native80211(
+	struct wpa_driver_ndis_data *drv,
+	struct wpa_driver_scan_params *params)
+{
+	DOT11_SCAN_REQUEST_V2 req;
+	int res;
+
+	os_memset(&req, 0, sizeof(req));
+	req.dot11BSSType = dot11_BSS_type_any;
+	os_memset(req.dot11BSSID, 0xff, ETH_ALEN);
+	req.dot11ScanType = dot11_scan_type_auto;
+	res = ndis_set_oid(drv, OID_DOT11_SCAN_REQUEST, (char *) &req,
+			   sizeof(req));
+	eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx);
+	eloop_register_timeout(7, 0, wpa_driver_ndis_scan_timeout, drv,
+			       drv->ctx);
+	return res;
+}
+
+
+static int wpa_driver_ndis_scan(void *priv,
+				struct wpa_driver_scan_params *params)
+{
+	struct wpa_driver_ndis_data *drv = priv;
+	int res;
+
+	if (drv->native80211)
+		return wpa_driver_ndis_scan_native80211(drv, params);
+
+	if (!drv->radio_enabled) {
+		wpa_printf(MSG_DEBUG, "NDIS: turning radio on before the first"
+			   " scan");
+		if (wpa_driver_ndis_disconnect(drv) < 0) {
+			wpa_printf(MSG_DEBUG, "NDIS: failed to enable radio");
+		}
+		drv->radio_enabled = 1;
+	}
+
+	res = ndis_set_oid(drv, OID_802_11_BSSID_LIST_SCAN, "    ", 4);
+	eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx);
+	eloop_register_timeout(7, 0, wpa_driver_ndis_scan_timeout, drv,
+			       drv->ctx);
+	return res;
+}
+
+
+static const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie)
+{
+	return get_ie((const u8 *) (res + 1), res->ie_len, ie);
+}
+
+
+static struct wpa_scan_res * wpa_driver_ndis_add_scan_ssid(
+	struct wpa_scan_res *r, NDIS_802_11_SSID *ssid)
+{
+	struct wpa_scan_res *nr;
+	u8 *pos;
+
+	if (wpa_scan_get_ie(r, WLAN_EID_SSID))
+		return r; /* SSID IE already present */
+
+	if (ssid->SsidLength == 0 || ssid->SsidLength > SSID_MAX_LEN)
+		return r; /* No valid SSID inside scan data */
+
+	nr = os_realloc(r, sizeof(*r) + r->ie_len + 2 + ssid->SsidLength);
+	if (nr == NULL)
+		return r;
+
+	pos = ((u8 *) (nr + 1)) + nr->ie_len;
+	*pos++ = WLAN_EID_SSID;
+	*pos++ = ssid->SsidLength;
+	os_memcpy(pos, ssid->Ssid, ssid->SsidLength);
+	nr->ie_len += 2 + ssid->SsidLength;
+
+	return nr;
+}
+
+
+static struct wpa_scan_results * wpa_driver_ndis_get_scan_results(void *priv)
+{
+	struct wpa_driver_ndis_data *drv = priv;
+	NDIS_802_11_BSSID_LIST_EX *b;
+	size_t blen, count, i;
+	int len;
+	char *pos;
+	struct wpa_scan_results *results;
+	struct wpa_scan_res *r;
+
+	blen = 65535;
+	b = os_zalloc(blen);
+	if (b == NULL)
+		return NULL;
+	len = ndis_get_oid(drv, OID_802_11_BSSID_LIST, (char *) b, blen);
+	if (len < 0) {
+		wpa_printf(MSG_DEBUG, "NDIS: failed to get scan results");
+		os_free(b);
+		return NULL;
+	}
+	count = b->NumberOfItems;
+
+	results = os_zalloc(sizeof(*results));
+	if (results == NULL) {
+		os_free(b);
+		return NULL;
+	}
+	results->res = os_calloc(count, sizeof(struct wpa_scan_res *));
+	if (results->res == NULL) {
+		os_free(results);
+		os_free(b);
+		return NULL;
+	}
+
+	pos = (char *) &b->Bssid[0];
+	for (i = 0; i < count; i++) {
+		NDIS_WLAN_BSSID_EX *bss = (NDIS_WLAN_BSSID_EX *) pos;
+		NDIS_802_11_FIXED_IEs *fixed;
+
+		if (bss->IELength < sizeof(NDIS_802_11_FIXED_IEs)) {
+			wpa_printf(MSG_DEBUG, "NDIS: too small IELength=%d",
+				   (int) bss->IELength);
+			break;
+		}
+		if (((char *) bss->IEs) + bss->IELength  > (char *) b + blen) {
+			/*
+			 * Some NDIS drivers have been reported to include an
+			 * entry with an invalid IELength in scan results and
+			 * this has crashed wpa_supplicant, so validate the
+			 * returned value before using it.
+			 */
+			wpa_printf(MSG_DEBUG, "NDIS: skipped invalid scan "
+				   "result IE (BSSID=" MACSTR ") IELength=%d",
+				   MAC2STR(bss->MacAddress),
+				   (int) bss->IELength);
+			break;
+		}
+
+		r = os_zalloc(sizeof(*r) + bss->IELength -
+			      sizeof(NDIS_802_11_FIXED_IEs));
+		if (r == NULL)
+			break;
+
+		os_memcpy(r->bssid, bss->MacAddress, ETH_ALEN);
+		r->level = (int) bss->Rssi;
+		r->freq = bss->Configuration.DSConfig / 1000;
+		fixed = (NDIS_802_11_FIXED_IEs *) bss->IEs;
+		r->beacon_int = WPA_GET_LE16((u8 *) &fixed->BeaconInterval);
+		r->caps = WPA_GET_LE16((u8 *) &fixed->Capabilities);
+		r->tsf = WPA_GET_LE64(fixed->Timestamp);
+		os_memcpy(r + 1, bss->IEs + sizeof(NDIS_802_11_FIXED_IEs),
+			  bss->IELength - sizeof(NDIS_802_11_FIXED_IEs));
+		r->ie_len = bss->IELength - sizeof(NDIS_802_11_FIXED_IEs);
+		r = wpa_driver_ndis_add_scan_ssid(r, &bss->Ssid);
+
+		results->res[results->num++] = r;
+
+		pos += bss->Length;
+		if (pos > (char *) b + blen)
+			break;
+	}
+
+	os_free(b);
+
+	return results;
+}
+
+
+static int wpa_driver_ndis_remove_key(struct wpa_driver_ndis_data *drv,
+				      int key_idx, const u8 *addr,
+				      const u8 *bssid, int pairwise)
+{
+	NDIS_802_11_REMOVE_KEY rkey;
+	NDIS_802_11_KEY_INDEX index;
+	int res, res2;
+
+	os_memset(&rkey, 0, sizeof(rkey));
+
+	rkey.Length = sizeof(rkey);
+	rkey.KeyIndex = key_idx;
+	if (pairwise)
+		rkey.KeyIndex |= 1 << 30;
+	os_memcpy(rkey.BSSID, bssid, ETH_ALEN);
+
+	res = ndis_set_oid(drv, OID_802_11_REMOVE_KEY, (char *) &rkey,
+			   sizeof(rkey));
+	if (!pairwise) {
+		index = key_idx;
+		res2 = ndis_set_oid(drv, OID_802_11_REMOVE_WEP,
+				    (char *) &index, sizeof(index));
+	} else
+		res2 = 0;
+
+	if (res < 0 && res2 < 0)
+		return -1;
+	return 0;
+}
+
+
+static int wpa_driver_ndis_add_wep(struct wpa_driver_ndis_data *drv,
+				   int pairwise, int key_idx, int set_tx,
+				   const u8 *key, size_t key_len)
+{
+	NDIS_802_11_WEP *wep;
+	size_t len;
+	int res;
+
+	len = 12 + key_len;
+	wep = os_zalloc(len);
+	if (wep == NULL)
+		return -1;
+	wep->Length = len;
+	wep->KeyIndex = key_idx;
+	if (set_tx)
+		wep->KeyIndex |= 1 << 31;
+#if 0 /* Setting bit30 does not seem to work with some NDIS drivers */
+	if (pairwise)
+		wep->KeyIndex |= 1 << 30;
+#endif
+	wep->KeyLength = key_len;
+	os_memcpy(wep->KeyMaterial, key, key_len);
+
+	wpa_hexdump_key(MSG_MSGDUMP, "NDIS: OID_802_11_ADD_WEP",
+			(u8 *) wep, len);
+	res = ndis_set_oid(drv, OID_802_11_ADD_WEP, (char *) wep, len);
+
+	os_free(wep);
+
+	return res;
+}
+
+
+static int wpa_driver_ndis_set_key(const char *ifname, void *priv,
+				   enum wpa_alg alg, const u8 *addr,
+				   int key_idx, int set_tx,
+				   const u8 *seq, size_t seq_len,
+				   const u8 *key, size_t key_len)
+{
+	struct wpa_driver_ndis_data *drv = priv;
+	size_t len, i;
+	NDIS_802_11_KEY *nkey;
+	int res, pairwise;
+	u8 bssid[ETH_ALEN];
+
+	if (addr == NULL || is_broadcast_ether_addr(addr)) {
+		/* Group Key */
+		pairwise = 0;
+		if (wpa_driver_ndis_get_bssid(drv, bssid) < 0)
+			os_memset(bssid, 0xff, ETH_ALEN);
+	} else {
+		/* Pairwise Key */
+		pairwise = 1;
+		os_memcpy(bssid, addr, ETH_ALEN);
+	}
+
+	if (alg == WPA_ALG_NONE || key_len == 0) {
+		return wpa_driver_ndis_remove_key(drv, key_idx, addr, bssid,
+						  pairwise);
+	}
+
+	if (alg == WPA_ALG_WEP) {
+		return wpa_driver_ndis_add_wep(drv, pairwise, key_idx, set_tx,
+					       key, key_len);
+	}
+
+	len = 12 + 6 + 6 + 8 + key_len;
+
+	nkey = os_zalloc(len);
+	if (nkey == NULL)
+		return -1;
+
+	nkey->Length = len;
+	nkey->KeyIndex = key_idx;
+	if (set_tx)
+		nkey->KeyIndex |= 1 << 31;
+	if (pairwise)
+		nkey->KeyIndex |= 1 << 30;
+	if (seq && seq_len)
+		nkey->KeyIndex |= 1 << 29;
+	nkey->KeyLength = key_len;
+	os_memcpy(nkey->BSSID, bssid, ETH_ALEN);
+	if (seq && seq_len) {
+		for (i = 0; i < seq_len; i++)
+			nkey->KeyRSC |= (ULONGLONG) seq[i] << (i * 8);
+	}
+	if (alg == WPA_ALG_TKIP && key_len == 32) {
+		os_memcpy(nkey->KeyMaterial, key, 16);
+		os_memcpy(nkey->KeyMaterial + 16, key + 24, 8);
+		os_memcpy(nkey->KeyMaterial + 24, key + 16, 8);
+	} else {
+		os_memcpy(nkey->KeyMaterial, key, key_len);
+	}
+
+	wpa_hexdump_key(MSG_MSGDUMP, "NDIS: OID_802_11_ADD_KEY",
+			(u8 *) nkey, len);
+	res = ndis_set_oid(drv, OID_802_11_ADD_KEY, (char *) nkey, len);
+	os_free(nkey);
+
+	return res;
+}
+
+
+static int
+wpa_driver_ndis_set_key_wrapper(void *priv,
+				struct wpa_driver_set_key_params *params)
+{
+	return wpa_driver_ndis_set_key(params->ifname, priv,
+				       params->alg, params->addr,
+				       params->key_idx, params->set_tx,
+				       params->seq, params->seq_len,
+				       params->key, params->key_len);
+}
+
+
+static int
+wpa_driver_ndis_associate(void *priv,
+			  struct wpa_driver_associate_params *params)
+{
+	struct wpa_driver_ndis_data *drv = priv;
+	u32 auth_mode, encr, priv_mode, mode;
+	u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+	drv->mode = params->mode;
+
+	/* Note: Setting OID_802_11_INFRASTRUCTURE_MODE clears current keys,
+	 * so static WEP keys needs to be set again after this. */
+	if (params->mode == IEEE80211_MODE_IBSS) {
+		mode = Ndis802_11IBSS;
+		/* Need to make sure that BSSID polling is enabled for
+		 * IBSS mode. */
+		eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL);
+		eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout,
+				       drv, NULL);
+	} else
+		mode = Ndis802_11Infrastructure;
+	if (ndis_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE,
+			 (char *) &mode, sizeof(mode)) < 0) {
+		wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
+			   "OID_802_11_INFRASTRUCTURE_MODE (%d)",
+			   (int) mode);
+		/* Try to continue anyway */
+	}
+
+	if (params->key_mgmt_suite == WPA_KEY_MGMT_NONE ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+		/* Re-set WEP keys if static WEP configuration is used. */
+		int i;
+		for (i = 0; i < 4; i++) {
+			if (!params->wep_key[i])
+				continue;
+			wpa_printf(MSG_DEBUG, "NDIS: Re-setting static WEP "
+				   "key %d", i);
+			wpa_driver_ndis_set_key(drv->ifname, drv, WPA_ALG_WEP,
+						bcast, i,
+						i == params->wep_tx_keyidx,
+						NULL, 0, params->wep_key[i],
+						params->wep_key_len[i]);
+		}
+	}
+
+	if (params->wpa_ie == NULL || params->wpa_ie_len == 0) {
+		if (params->auth_alg & WPA_AUTH_ALG_SHARED) {
+			if (params->auth_alg & WPA_AUTH_ALG_OPEN)
+				auth_mode = Ndis802_11AuthModeAutoSwitch;
+			else
+				auth_mode = Ndis802_11AuthModeShared;
+		} else
+			auth_mode = Ndis802_11AuthModeOpen;
+		priv_mode = Ndis802_11PrivFilterAcceptAll;
+	} else if (params->wpa_ie[0] == WLAN_EID_RSN) {
+		priv_mode = Ndis802_11PrivFilter8021xWEP;
+		if (params->key_mgmt_suite == WPA_KEY_MGMT_PSK)
+			auth_mode = Ndis802_11AuthModeWPA2PSK;
+		else
+			auth_mode = Ndis802_11AuthModeWPA2;
+#ifdef CONFIG_WPS
+	} else if (params->key_mgmt_suite == WPA_KEY_MGMT_WPS) {
+		auth_mode = Ndis802_11AuthModeOpen;
+		priv_mode = Ndis802_11PrivFilterAcceptAll;
+		if (params->wps == WPS_MODE_PRIVACY) {
+			u8 stub_key[5] = { 0x11, 0x22, 0x33, 0x44, 0x55 };
+			/*
+			 * Some NDIS drivers refuse to associate in open mode
+			 * configuration due to Privacy field mismatch, so use
+			 * a workaround to make the configuration look like
+			 * matching one for WPS provisioning.
+			 */
+			wpa_printf(MSG_DEBUG, "NDIS: Set stub WEP key as a "
+				   "workaround to allow driver to associate "
+				   "for WPS");
+			wpa_driver_ndis_set_key(drv->ifname, drv, WPA_ALG_WEP,
+						bcast, 0, 1,
+						NULL, 0, stub_key,
+						sizeof(stub_key));
+		}
+#endif /* CONFIG_WPS */
+	} else {
+		priv_mode = Ndis802_11PrivFilter8021xWEP;
+		if (params->key_mgmt_suite == WPA_KEY_MGMT_WPA_NONE)
+			auth_mode = Ndis802_11AuthModeWPANone;
+		else if (params->key_mgmt_suite == WPA_KEY_MGMT_PSK)
+			auth_mode = Ndis802_11AuthModeWPAPSK;
+		else
+			auth_mode = Ndis802_11AuthModeWPA;
+	}
+
+	switch (params->pairwise_suite) {
+	case WPA_CIPHER_CCMP:
+		encr = Ndis802_11Encryption3Enabled;
+		break;
+	case WPA_CIPHER_TKIP:
+		encr = Ndis802_11Encryption2Enabled;
+		break;
+	case WPA_CIPHER_WEP40:
+	case WPA_CIPHER_WEP104:
+		encr = Ndis802_11Encryption1Enabled;
+		break;
+	case WPA_CIPHER_NONE:
+#ifdef CONFIG_WPS
+		if (params->wps == WPS_MODE_PRIVACY) {
+			encr = Ndis802_11Encryption1Enabled;
+			break;
+		}
+#endif /* CONFIG_WPS */
+		if (params->group_suite == WPA_CIPHER_CCMP)
+			encr = Ndis802_11Encryption3Enabled;
+		else if (params->group_suite == WPA_CIPHER_TKIP)
+			encr = Ndis802_11Encryption2Enabled;
+		else
+			encr = Ndis802_11EncryptionDisabled;
+		break;
+	default:
+#ifdef CONFIG_WPS
+		if (params->wps == WPS_MODE_PRIVACY) {
+			encr = Ndis802_11Encryption1Enabled;
+			break;
+		}
+#endif /* CONFIG_WPS */
+		encr = Ndis802_11EncryptionDisabled;
+		break;
+	};
+
+	if (ndis_set_oid(drv, OID_802_11_PRIVACY_FILTER,
+			 (char *) &priv_mode, sizeof(priv_mode)) < 0) {
+		wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
+			   "OID_802_11_PRIVACY_FILTER (%d)",
+			   (int) priv_mode);
+		/* Try to continue anyway */
+	}
+
+	ndis_set_auth_mode(drv, auth_mode);
+	ndis_set_encr_status(drv, encr);
+
+	if (params->bssid) {
+		ndis_set_oid(drv, OID_802_11_BSSID, (char *) params->bssid,
+			     ETH_ALEN);
+		drv->oid_bssid_set = 1;
+	} else if (drv->oid_bssid_set) {
+		ndis_set_oid(drv, OID_802_11_BSSID, "\xff\xff\xff\xff\xff\xff",
+			     ETH_ALEN);
+		drv->oid_bssid_set = 0;
+	}
+
+	return wpa_driver_ndis_set_ssid(drv, params->ssid, params->ssid_len);
+}
+
+
+static int wpa_driver_ndis_set_pmkid(struct wpa_driver_ndis_data *drv)
+{
+	int len, count, i, ret;
+	struct ndis_pmkid_entry *entry;
+	NDIS_802_11_PMKID *p;
+
+	count = 0;
+	entry = drv->pmkid;
+	while (entry) {
+		count++;
+		if (count >= drv->no_of_pmkid)
+			break;
+		entry = entry->next;
+	}
+	len = 8 + count * sizeof(BSSID_INFO);
+	p = os_zalloc(len);
+	if (p == NULL)
+		return -1;
+
+	p->Length = len;
+	p->BSSIDInfoCount = count;
+	entry = drv->pmkid;
+	for (i = 0; i < count; i++) {
+		os_memcpy(&p->BSSIDInfo[i].BSSID, entry->bssid, ETH_ALEN);
+		os_memcpy(&p->BSSIDInfo[i].PMKID, entry->pmkid, 16);
+		entry = entry->next;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID", (u8 *) p, len);
+	ret = ndis_set_oid(drv, OID_802_11_PMKID, (char *) p, len);
+	os_free(p);
+	return ret;
+}
+
+
+static int wpa_driver_ndis_add_pmkid(void *priv,
+				     struct wpa_pmkid_params *params)
+{
+	struct wpa_driver_ndis_data *drv = priv;
+	struct ndis_pmkid_entry *entry, *prev;
+	const u8 *bssid = params->bssid;
+	const u8 *pmkid = params->pmkid;
+
+	if (!bssid || !pmkid)
+		return -1;
+	if (drv->no_of_pmkid == 0)
+		return 0;
+
+	prev = NULL;
+	entry = drv->pmkid;
+	while (entry) {
+		if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0)
+			break;
+		prev = entry;
+		entry = entry->next;
+	}
+
+	if (entry) {
+		/* Replace existing entry for this BSSID and move it into the
+		 * beginning of the list. */
+		os_memcpy(entry->pmkid, pmkid, 16);
+		if (prev) {
+			prev->next = entry->next;
+			entry->next = drv->pmkid;
+			drv->pmkid = entry;
+		}
+	} else {
+		entry = os_malloc(sizeof(*entry));
+		if (entry) {
+			os_memcpy(entry->bssid, bssid, ETH_ALEN);
+			os_memcpy(entry->pmkid, pmkid, 16);
+			entry->next = drv->pmkid;
+			drv->pmkid = entry;
+		}
+	}
+
+	return wpa_driver_ndis_set_pmkid(drv);
+}
+
+
+static int wpa_driver_ndis_remove_pmkid(void *priv,
+					struct wpa_pmkid_params *params)
+{
+	struct wpa_driver_ndis_data *drv = priv;
+	struct ndis_pmkid_entry *entry, *prev;
+	const u8 *bssid = params->bssid;
+	const u8 *pmkid = params->pmkid;
+
+	if (!bssid || !pmkid)
+		return -1;
+	if (drv->no_of_pmkid == 0)
+		return 0;
+
+	entry = drv->pmkid;
+	prev = NULL;
+	while (entry) {
+		if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0 &&
+		    os_memcmp(entry->pmkid, pmkid, 16) == 0) {
+			if (prev)
+				prev->next = entry->next;
+			else
+				drv->pmkid = entry->next;
+			os_free(entry);
+			break;
+		}
+		prev = entry;
+		entry = entry->next;
+	}
+	return wpa_driver_ndis_set_pmkid(drv);
+}
+
+
+static int wpa_driver_ndis_flush_pmkid(void *priv)
+{
+	struct wpa_driver_ndis_data *drv = priv;
+	NDIS_802_11_PMKID p;
+	struct ndis_pmkid_entry *pmkid, *prev;
+	int prev_authmode, ret;
+
+	if (drv->no_of_pmkid == 0)
+		return 0;
+
+	pmkid = drv->pmkid;
+	drv->pmkid = NULL;
+	while (pmkid) {
+		prev = pmkid;
+		pmkid = pmkid->next;
+		os_free(prev);
+	}
+
+	/*
+	 * Some drivers may refuse OID_802_11_PMKID if authMode is not set to
+	 * WPA2, so change authMode temporarily, if needed.
+	 */
+	prev_authmode = ndis_get_auth_mode(drv);
+	if (prev_authmode != Ndis802_11AuthModeWPA2)
+		ndis_set_auth_mode(drv, Ndis802_11AuthModeWPA2);
+
+	os_memset(&p, 0, sizeof(p));
+	p.Length = 8;
+	p.BSSIDInfoCount = 0;
+	wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID (flush)",
+		    (u8 *) &p, 8);
+	ret = ndis_set_oid(drv, OID_802_11_PMKID, (char *) &p, 8);
+
+	if (prev_authmode != Ndis802_11AuthModeWPA2)
+		ndis_set_auth_mode(drv, prev_authmode);
+
+	return ret;
+}
+
+
+static int wpa_driver_ndis_get_associnfo(struct wpa_driver_ndis_data *drv)
+{
+	char buf[512], *pos;
+	NDIS_802_11_ASSOCIATION_INFORMATION *ai;
+	int len;
+	union wpa_event_data data;
+	NDIS_802_11_BSSID_LIST_EX *b;
+	size_t blen, i;
+
+	len = ndis_get_oid(drv, OID_802_11_ASSOCIATION_INFORMATION, buf,
+			   sizeof(buf));
+	if (len < 0) {
+		wpa_printf(MSG_DEBUG, "NDIS: failed to get association "
+			   "information");
+		return -1;
+	}
+	if (len > sizeof(buf)) {
+		/* Some drivers seem to be producing incorrect length for this
+		 * data. Limit the length to the current buffer size to avoid
+		 * crashing in hexdump. The data seems to be otherwise valid,
+		 * so better try to use it. */
+		wpa_printf(MSG_DEBUG, "NDIS: ignored bogus association "
+			   "information length %d", len);
+		len = ndis_get_oid(drv, OID_802_11_ASSOCIATION_INFORMATION,
+				   buf, sizeof(buf));
+		if (len < -1) {
+			wpa_printf(MSG_DEBUG, "NDIS: re-reading association "
+				   "information failed");
+			return -1;
+		}
+		if (len > sizeof(buf)) {
+			wpa_printf(MSG_DEBUG, "NDIS: ignored bogus association"
+				   " information length %d (re-read)", len);
+			len = sizeof(buf);
+		}
+	}
+	wpa_hexdump(MSG_MSGDUMP, "NDIS: association information",
+		    (u8 *) buf, len);
+	if (len < sizeof(*ai)) {
+		wpa_printf(MSG_DEBUG, "NDIS: too short association "
+			   "information");
+		return -1;
+	}
+	ai = (NDIS_802_11_ASSOCIATION_INFORMATION *) buf;
+	wpa_printf(MSG_DEBUG, "NDIS: ReqFixed=0x%x RespFixed=0x%x off_req=%d "
+		   "off_resp=%d len_req=%d len_resp=%d",
+		   ai->AvailableRequestFixedIEs, ai->AvailableResponseFixedIEs,
+		   (int) ai->OffsetRequestIEs, (int) ai->OffsetResponseIEs,
+		   (int) ai->RequestIELength, (int) ai->ResponseIELength);
+
+	if (ai->OffsetRequestIEs + ai->RequestIELength > (unsigned) len ||
+	    ai->OffsetResponseIEs + ai->ResponseIELength > (unsigned) len) {
+		wpa_printf(MSG_DEBUG, "NDIS: association information - "
+			   "IE overflow");
+		return -1;
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "NDIS: Request IEs",
+		    (u8 *) buf + ai->OffsetRequestIEs, ai->RequestIELength);
+	wpa_hexdump(MSG_MSGDUMP, "NDIS: Response IEs",
+		    (u8 *) buf + ai->OffsetResponseIEs, ai->ResponseIELength);
+
+	os_memset(&data, 0, sizeof(data));
+	data.assoc_info.req_ies = (u8 *) buf + ai->OffsetRequestIEs;
+	data.assoc_info.req_ies_len = ai->RequestIELength;
+	data.assoc_info.resp_ies = (u8 *) buf + ai->OffsetResponseIEs;
+	data.assoc_info.resp_ies_len = ai->ResponseIELength;
+
+	blen = 65535;
+	b = os_zalloc(blen);
+	if (b == NULL)
+		goto skip_scan_results;
+	len = ndis_get_oid(drv, OID_802_11_BSSID_LIST, (char *) b, blen);
+	if (len < 0) {
+		wpa_printf(MSG_DEBUG, "NDIS: failed to get scan results");
+		os_free(b);
+		b = NULL;
+		goto skip_scan_results;
+	}
+	wpa_printf(MSG_DEBUG, "NDIS: %d BSSID items to process for AssocInfo",
+		   (unsigned int) b->NumberOfItems);
+
+	pos = (char *) &b->Bssid[0];
+	for (i = 0; i < b->NumberOfItems; i++) {
+		NDIS_WLAN_BSSID_EX *bss = (NDIS_WLAN_BSSID_EX *) pos;
+		if (os_memcmp(drv->bssid, bss->MacAddress, ETH_ALEN) == 0 &&
+		    bss->IELength > sizeof(NDIS_802_11_FIXED_IEs)) {
+			data.assoc_info.beacon_ies =
+				((u8 *) bss->IEs) +
+				sizeof(NDIS_802_11_FIXED_IEs);
+			data.assoc_info.beacon_ies_len =
+				bss->IELength - sizeof(NDIS_802_11_FIXED_IEs);
+			wpa_hexdump(MSG_MSGDUMP, "NDIS: Beacon IEs",
+				    data.assoc_info.beacon_ies,
+				    data.assoc_info.beacon_ies_len);
+			break;
+		}
+		pos += bss->Length;
+		if (pos > (char *) b + blen)
+			break;
+	}
+
+skip_scan_results:
+	wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data);
+
+	os_free(b);
+
+	return 0;
+}
+
+
+static void wpa_driver_ndis_poll_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_driver_ndis_data *drv = eloop_ctx;
+	u8 bssid[ETH_ALEN];
+	int poll;
+
+	if (drv->wired)
+		return;
+
+	if (wpa_driver_ndis_get_bssid(drv, bssid)) {
+		/* Disconnected */
+		if (!is_zero_ether_addr(drv->bssid)) {
+			os_memset(drv->bssid, 0, ETH_ALEN);
+			wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+		}
+	} else {
+		/* Connected */
+		if (os_memcmp(drv->bssid, bssid, ETH_ALEN) != 0) {
+			os_memcpy(drv->bssid, bssid, ETH_ALEN);
+			wpa_driver_ndis_get_associnfo(drv);
+			wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
+		}
+	}
+
+	/* When using integrated NDIS event receiver, we can skip BSSID
+	 * polling when using infrastructure network. However, when using
+	 * IBSS mode, many driver do not seem to generate connection event,
+	 * so we need to enable BSSID polling to figure out when IBSS network
+	 * has been formed.
+	 */
+	poll = drv->mode == IEEE80211_MODE_IBSS;
+#ifndef CONFIG_NDIS_EVENTS_INTEGRATED
+#ifndef _WIN32_WCE
+	poll = 1;
+#endif /* _WIN32_WCE */
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+
+	if (poll) {
+		eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout,
+					drv, NULL);
+	}
+}
+
+
+static void wpa_driver_ndis_poll(void *priv)
+{
+	struct wpa_driver_ndis_data *drv = priv;
+	eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL);
+	wpa_driver_ndis_poll_timeout(drv, NULL);
+}
+
+
+/* Called when driver generates Media Connect Event by calling
+ * NdisMIndicateStatus() with NDIS_STATUS_MEDIA_CONNECT */
+void wpa_driver_ndis_event_connect(struct wpa_driver_ndis_data *drv)
+{
+	wpa_printf(MSG_DEBUG, "NDIS: Media Connect Event");
+	if (wpa_driver_ndis_get_bssid(drv, drv->bssid) == 0) {
+		wpa_driver_ndis_get_associnfo(drv);
+		wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
+	}
+}
+
+
+/* Called when driver generates Media Disconnect Event by calling
+ * NdisMIndicateStatus() with NDIS_STATUS_MEDIA_DISCONNECT */
+void wpa_driver_ndis_event_disconnect(struct wpa_driver_ndis_data *drv)
+{
+	wpa_printf(MSG_DEBUG, "NDIS: Media Disconnect Event");
+	os_memset(drv->bssid, 0, ETH_ALEN);
+	wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+}
+
+
+static void wpa_driver_ndis_event_auth(struct wpa_driver_ndis_data *drv,
+				       const u8 *data, size_t data_len)
+{
+	NDIS_802_11_AUTHENTICATION_REQUEST *req;
+	int pairwise = 0, group = 0;
+	union wpa_event_data event;
+
+	if (data_len < sizeof(*req)) {
+		wpa_printf(MSG_DEBUG, "NDIS: Too short Authentication Request "
+			   "Event (len=%d)", data_len);
+		return;
+	}
+	req = (NDIS_802_11_AUTHENTICATION_REQUEST *) data;
+
+	wpa_printf(MSG_DEBUG, "NDIS: Authentication Request Event: "
+		   "Bssid " MACSTR " Flags 0x%x",
+		   MAC2STR(req->Bssid), (int) req->Flags);
+
+	if ((req->Flags & NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR) ==
+	    NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR)
+		pairwise = 1;
+	else if ((req->Flags & NDIS_802_11_AUTH_REQUEST_GROUP_ERROR) ==
+	    NDIS_802_11_AUTH_REQUEST_GROUP_ERROR)
+		group = 1;
+
+	if (pairwise || group) {
+		os_memset(&event, 0, sizeof(event));
+		event.michael_mic_failure.unicast = pairwise;
+		wpa_supplicant_event(drv->ctx, EVENT_MICHAEL_MIC_FAILURE,
+				     &event);
+	}
+}
+
+
+static void wpa_driver_ndis_event_pmkid(struct wpa_driver_ndis_data *drv,
+					const u8 *data, size_t data_len)
+{
+	NDIS_802_11_PMKID_CANDIDATE_LIST *pmkid;
+	size_t i;
+	union wpa_event_data event;
+
+	if (data_len < 8) {
+		wpa_printf(MSG_DEBUG, "NDIS: Too short PMKID Candidate List "
+			   "Event (len=%d)", data_len);
+		return;
+	}
+	pmkid = (NDIS_802_11_PMKID_CANDIDATE_LIST *) data;
+	wpa_printf(MSG_DEBUG, "NDIS: PMKID Candidate List Event - Version %d "
+		   "NumCandidates %d",
+		   (int) pmkid->Version, (int) pmkid->NumCandidates);
+
+	if (pmkid->Version != 1) {
+		wpa_printf(MSG_DEBUG, "NDIS: Unsupported PMKID Candidate List "
+			   "Version %d", (int) pmkid->Version);
+		return;
+	}
+
+	if (data_len < 8 + pmkid->NumCandidates * sizeof(PMKID_CANDIDATE)) {
+		wpa_printf(MSG_DEBUG, "NDIS: PMKID Candidate List underflow");
+		return;
+	}
+
+	os_memset(&event, 0, sizeof(event));
+	for (i = 0; i < pmkid->NumCandidates; i++) {
+		PMKID_CANDIDATE *p = &pmkid->CandidateList[i];
+		wpa_printf(MSG_DEBUG, "NDIS: %d: " MACSTR " Flags 0x%x",
+			   i, MAC2STR(p->BSSID), (int) p->Flags);
+		os_memcpy(event.pmkid_candidate.bssid, p->BSSID, ETH_ALEN);
+		event.pmkid_candidate.index = i;
+		event.pmkid_candidate.preauth =
+			p->Flags & NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED;
+		wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE,
+				     &event);
+	}
+}
+
+
+/* Called when driver calls NdisMIndicateStatus() with
+ * NDIS_STATUS_MEDIA_SPECIFIC_INDICATION */
+void wpa_driver_ndis_event_media_specific(struct wpa_driver_ndis_data *drv,
+					  const u8 *data, size_t data_len)
+{
+	NDIS_802_11_STATUS_INDICATION *status;
+
+	if (data == NULL || data_len < sizeof(*status))
+		return;
+
+	wpa_hexdump(MSG_DEBUG, "NDIS: Media Specific Indication",
+		    data, data_len);
+
+	status = (NDIS_802_11_STATUS_INDICATION *) data;
+	data += sizeof(status);
+	data_len -= sizeof(status);
+
+	switch (status->StatusType) {
+	case Ndis802_11StatusType_Authentication:
+		wpa_driver_ndis_event_auth(drv, data, data_len);
+		break;
+	case Ndis802_11StatusType_PMKID_CandidateList:
+		wpa_driver_ndis_event_pmkid(drv, data, data_len);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "NDIS: Unknown StatusType %d",
+			   (int) status->StatusType);
+		break;
+	}
+}
+
+
+/* Called when an adapter is added */
+void wpa_driver_ndis_event_adapter_arrival(struct wpa_driver_ndis_data *drv)
+{
+	union wpa_event_data event;
+	int i;
+
+	wpa_printf(MSG_DEBUG, "NDIS: Notify Adapter Arrival");
+
+	for (i = 0; i < 30; i++) {
+		/* Re-open Packet32/NDISUIO connection */
+		wpa_driver_ndis_adapter_close(drv);
+		if (wpa_driver_ndis_adapter_init(drv) < 0 ||
+		    wpa_driver_ndis_adapter_open(drv) < 0) {
+			wpa_printf(MSG_DEBUG, "NDIS: Driver re-initialization "
+				   "(%d) failed", i);
+			os_sleep(1, 0);
+		} else {
+			wpa_printf(MSG_DEBUG, "NDIS: Driver re-initialized");
+			break;
+		}
+	}
+
+	os_memset(&event, 0, sizeof(event));
+	os_strlcpy(event.interface_status.ifname, drv->ifname,
+		   sizeof(event.interface_status.ifname));
+	event.interface_status.ievent = EVENT_INTERFACE_ADDED;
+	wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+}
+
+
+/* Called when an adapter is removed */
+void wpa_driver_ndis_event_adapter_removal(struct wpa_driver_ndis_data *drv)
+{
+	union wpa_event_data event;
+
+	wpa_printf(MSG_DEBUG, "NDIS: Notify Adapter Removal");
+	os_memset(&event, 0, sizeof(event));
+	os_strlcpy(event.interface_status.ifname, drv->ifname,
+		   sizeof(event.interface_status.ifname));
+	event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
+	wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+}
+
+
+static void
+wpa_driver_ndis_get_wpa_capability(struct wpa_driver_ndis_data *drv)
+{
+	wpa_printf(MSG_DEBUG, "NDIS: verifying driver WPA capability");
+
+	if (ndis_set_auth_mode(drv, Ndis802_11AuthModeWPA) == 0 &&
+	    ndis_get_auth_mode(drv) == Ndis802_11AuthModeWPA) {
+		wpa_printf(MSG_DEBUG, "NDIS: WPA key management supported");
+		drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA;
+	}
+
+	if (ndis_set_auth_mode(drv, Ndis802_11AuthModeWPAPSK) == 0 &&
+	    ndis_get_auth_mode(drv) == Ndis802_11AuthModeWPAPSK) {
+		wpa_printf(MSG_DEBUG, "NDIS: WPA-PSK key management "
+			   "supported");
+		drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK;
+	}
+
+	if (ndis_set_encr_status(drv, Ndis802_11Encryption3Enabled) == 0 &&
+	    ndis_get_encr_status(drv) == Ndis802_11Encryption3KeyAbsent) {
+		wpa_printf(MSG_DEBUG, "NDIS: CCMP encryption supported");
+		drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP;
+	}
+
+	if (ndis_set_encr_status(drv, Ndis802_11Encryption2Enabled) == 0 &&
+	    ndis_get_encr_status(drv) == Ndis802_11Encryption2KeyAbsent) {
+		wpa_printf(MSG_DEBUG, "NDIS: TKIP encryption supported");
+		drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP;
+	}
+
+	if (ndis_set_encr_status(drv, Ndis802_11Encryption1Enabled) == 0 &&
+	    ndis_get_encr_status(drv) == Ndis802_11Encryption1KeyAbsent) {
+		wpa_printf(MSG_DEBUG, "NDIS: WEP encryption supported");
+		drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 |
+			WPA_DRIVER_CAPA_ENC_WEP104;
+	}
+
+	if (ndis_set_auth_mode(drv, Ndis802_11AuthModeShared) == 0 &&
+	    ndis_get_auth_mode(drv) == Ndis802_11AuthModeShared) {
+		drv->capa.auth |= WPA_DRIVER_AUTH_SHARED;
+	}
+
+	if (ndis_set_auth_mode(drv, Ndis802_11AuthModeOpen) == 0 &&
+	    ndis_get_auth_mode(drv) == Ndis802_11AuthModeOpen) {
+		drv->capa.auth |= WPA_DRIVER_AUTH_OPEN;
+	}
+
+	ndis_set_encr_status(drv, Ndis802_11EncryptionDisabled);
+
+	/* Could also verify OID_802_11_ADD_KEY error reporting and
+	 * support for OID_802_11_ASSOCIATION_INFORMATION. */
+
+	if (drv->capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA &&
+	    drv->capa.enc & (WPA_DRIVER_CAPA_ENC_TKIP |
+			     WPA_DRIVER_CAPA_ENC_CCMP)) {
+		wpa_printf(MSG_DEBUG, "NDIS: driver supports WPA");
+		drv->has_capability = 1;
+	} else {
+		wpa_printf(MSG_DEBUG, "NDIS: no WPA support found");
+	}
+
+	wpa_printf(MSG_DEBUG, "NDIS: driver capabilities: key_mgmt 0x%x "
+		   "enc 0x%x auth 0x%x",
+		   drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth);
+}
+
+
+static void wpa_driver_ndis_get_capability(struct wpa_driver_ndis_data *drv)
+{
+	char buf[512];
+	int len;
+	size_t i;
+	NDIS_802_11_CAPABILITY *c;
+
+	drv->capa.flags = WPA_DRIVER_FLAGS_DRIVER_IE;
+
+	len = ndis_get_oid(drv, OID_802_11_CAPABILITY, buf, sizeof(buf));
+	if (len < 0) {
+		wpa_driver_ndis_get_wpa_capability(drv);
+		return;
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "OID_802_11_CAPABILITY", (u8 *) buf, len);
+	c = (NDIS_802_11_CAPABILITY *) buf;
+	if (len < sizeof(*c) || c->Version != 2) {
+		wpa_printf(MSG_DEBUG, "NDIS: unsupported "
+			   "OID_802_11_CAPABILITY data");
+		return;
+	}
+	wpa_printf(MSG_DEBUG, "NDIS: Driver supports OID_802_11_CAPABILITY - "
+		   "NoOfPMKIDs %d NoOfAuthEncrPairs %d",
+		   (int) c->NoOfPMKIDs,
+		   (int) c->NoOfAuthEncryptPairsSupported);
+	drv->has_capability = 1;
+	drv->no_of_pmkid = c->NoOfPMKIDs;
+	for (i = 0; i < c->NoOfAuthEncryptPairsSupported; i++) {
+		NDIS_802_11_AUTHENTICATION_ENCRYPTION *ae;
+		ae = &c->AuthenticationEncryptionSupported[i];
+		if ((char *) (ae + 1) > buf + len) {
+			wpa_printf(MSG_DEBUG, "NDIS: auth/encr pair list "
+				   "overflow");
+			break;
+		}
+		wpa_printf(MSG_MSGDUMP, "NDIS: %d - auth %d encr %d",
+			   i, (int) ae->AuthModeSupported,
+			   (int) ae->EncryptStatusSupported);
+		switch (ae->AuthModeSupported) {
+		case Ndis802_11AuthModeOpen:
+			drv->capa.auth |= WPA_DRIVER_AUTH_OPEN;
+			break;
+		case Ndis802_11AuthModeShared:
+			drv->capa.auth |= WPA_DRIVER_AUTH_SHARED;
+			break;
+		case Ndis802_11AuthModeWPA:
+			drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA;
+			break;
+		case Ndis802_11AuthModeWPAPSK:
+			drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK;
+			break;
+		case Ndis802_11AuthModeWPA2:
+			drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA2;
+			break;
+		case Ndis802_11AuthModeWPA2PSK:
+			drv->capa.key_mgmt |=
+				WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+			break;
+		case Ndis802_11AuthModeWPANone:
+			drv->capa.key_mgmt |=
+				WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE;
+			break;
+		default:
+			break;
+		}
+		switch (ae->EncryptStatusSupported) {
+		case Ndis802_11Encryption1Enabled:
+			drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40;
+			drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP104;
+			break;
+		case Ndis802_11Encryption2Enabled:
+			drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP;
+			break;
+		case Ndis802_11Encryption3Enabled:
+			drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP;
+			break;
+		default:
+			break;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "NDIS: driver capabilities: key_mgmt 0x%x "
+		   "enc 0x%x auth 0x%x",
+		   drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth);
+}
+
+
+static int wpa_driver_ndis_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+	struct wpa_driver_ndis_data *drv = priv;
+	if (!drv->has_capability)
+		return -1;
+	os_memcpy(capa, &drv->capa, sizeof(*capa));
+	return 0;
+}
+
+
+static const char * wpa_driver_ndis_get_ifname(void *priv)
+{
+	struct wpa_driver_ndis_data *drv = priv;
+	return drv->ifname;
+}
+
+
+static const u8 * wpa_driver_ndis_get_mac_addr(void *priv)
+{
+	struct wpa_driver_ndis_data *drv = priv;
+	return drv->own_addr;
+}
+
+
+#ifdef _WIN32_WCE
+
+#define NDISUIO_MSG_SIZE (sizeof(NDISUIO_DEVICE_NOTIFICATION) + 512)
+
+static void ndisuio_notification_receive(void *eloop_data, void *user_ctx)
+{
+	struct wpa_driver_ndis_data *drv = eloop_data;
+	NDISUIO_DEVICE_NOTIFICATION *hdr;
+	u8 buf[NDISUIO_MSG_SIZE];
+	DWORD len, flags;
+
+	if (!ReadMsgQueue(drv->event_queue, buf, NDISUIO_MSG_SIZE, &len, 0,
+			  &flags)) {
+		wpa_printf(MSG_DEBUG, "ndisuio_notification_receive: "
+			   "ReadMsgQueue failed: %d", (int) GetLastError());
+		return;
+	}
+
+	if (len < sizeof(NDISUIO_DEVICE_NOTIFICATION)) {
+		wpa_printf(MSG_DEBUG, "ndisuio_notification_receive: "
+			   "Too short message (len=%d)", (int) len);
+		return;
+	}
+
+	hdr = (NDISUIO_DEVICE_NOTIFICATION *) buf;
+	wpa_printf(MSG_DEBUG, "NDIS: Notification received: len=%d type=0x%x",
+		   (int) len, hdr->dwNotificationType);
+
+	switch (hdr->dwNotificationType) {
+#ifdef NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL
+	case NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL:
+		wpa_printf(MSG_DEBUG, "NDIS: ADAPTER_ARRIVAL");
+		wpa_driver_ndis_event_adapter_arrival(drv);
+		break;
+#endif
+#ifdef NDISUIO_NOTIFICATION_ADAPTER_REMOVAL
+	case NDISUIO_NOTIFICATION_ADAPTER_REMOVAL:
+		wpa_printf(MSG_DEBUG, "NDIS: ADAPTER_REMOVAL");
+		wpa_driver_ndis_event_adapter_removal(drv);
+		break;
+#endif
+	case NDISUIO_NOTIFICATION_MEDIA_CONNECT:
+		wpa_printf(MSG_DEBUG, "NDIS: MEDIA_CONNECT");
+		SetEvent(drv->connected_event);
+		wpa_driver_ndis_event_connect(drv);
+		break;
+	case NDISUIO_NOTIFICATION_MEDIA_DISCONNECT:
+		ResetEvent(drv->connected_event);
+		wpa_printf(MSG_DEBUG, "NDIS: MEDIA_DISCONNECT");
+		wpa_driver_ndis_event_disconnect(drv);
+		break;
+	case NDISUIO_NOTIFICATION_MEDIA_SPECIFIC_NOTIFICATION:
+		wpa_printf(MSG_DEBUG, "NDIS: MEDIA_SPECIFIC_NOTIFICATION");
+#if _WIN32_WCE == 420 || _WIN32_WCE == 0x420
+		wpa_driver_ndis_event_media_specific(
+			drv, hdr->pvStatusBuffer, hdr->uiStatusBufferSize);
+#else
+		wpa_driver_ndis_event_media_specific(
+			drv, ((const u8 *) hdr) + hdr->uiOffsetToStatusBuffer,
+			(size_t) hdr->uiStatusBufferSize);
+#endif
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "NDIS: Unknown notification type 0x%x",
+			   hdr->dwNotificationType);
+		break;
+	}
+}
+
+
+static void ndisuio_notification_deinit(struct wpa_driver_ndis_data *drv)
+{
+	NDISUIO_REQUEST_NOTIFICATION req;
+
+	memset(&req, 0, sizeof(req));
+	req.hMsgQueue = drv->event_queue;
+	req.dwNotificationTypes = 0;
+
+	if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_REQUEST_NOTIFICATION,
+			     &req, sizeof(req), NULL, 0, NULL, NULL)) {
+		wpa_printf(MSG_INFO, "ndisuio_notification_deinit: "
+			   "IOCTL_NDISUIO_REQUEST_NOTIFICATION failed: %d",
+			   (int) GetLastError());
+	}
+
+	if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_CANCEL_NOTIFICATION,
+			     NULL, 0, NULL, 0, NULL, NULL)) {
+		wpa_printf(MSG_INFO, "ndisuio_notification_deinit: "
+			   "IOCTL_NDISUIO_CANCEL_NOTIFICATION failed: %d",
+			   (int) GetLastError());
+	}
+
+	if (drv->event_queue) {
+		eloop_unregister_event(drv->event_queue,
+				       sizeof(drv->event_queue));
+		CloseHandle(drv->event_queue);
+		drv->event_queue = NULL;
+	}
+
+	if (drv->connected_event) {
+		CloseHandle(drv->connected_event);
+		drv->connected_event = NULL;
+	}
+}
+
+
+static int ndisuio_notification_init(struct wpa_driver_ndis_data *drv)
+{
+	MSGQUEUEOPTIONS opt;
+	NDISUIO_REQUEST_NOTIFICATION req;
+
+	drv->connected_event =
+		CreateEvent(NULL, TRUE, FALSE, TEXT("WpaSupplicantConnected"));
+	if (drv->connected_event == NULL) {
+		wpa_printf(MSG_INFO, "ndisuio_notification_init: "
+			   "CreateEvent failed: %d",
+			   (int) GetLastError());
+		return -1;
+	}
+
+	memset(&opt, 0, sizeof(opt));
+	opt.dwSize = sizeof(opt);
+	opt.dwMaxMessages = 5;
+	opt.cbMaxMessage = NDISUIO_MSG_SIZE;
+	opt.bReadAccess = TRUE;
+
+	drv->event_queue = CreateMsgQueue(NULL, &opt);
+	if (drv->event_queue == NULL) {
+		wpa_printf(MSG_INFO, "ndisuio_notification_init: "
+			   "CreateMsgQueue failed: %d",
+			   (int) GetLastError());
+		ndisuio_notification_deinit(drv);
+		return -1;
+	}
+
+	memset(&req, 0, sizeof(req));
+	req.hMsgQueue = drv->event_queue;
+	req.dwNotificationTypes =
+#ifdef NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL
+		NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL |
+#endif
+#ifdef NDISUIO_NOTIFICATION_ADAPTER_REMOVAL
+		NDISUIO_NOTIFICATION_ADAPTER_REMOVAL |
+#endif
+		NDISUIO_NOTIFICATION_MEDIA_CONNECT |
+		NDISUIO_NOTIFICATION_MEDIA_DISCONNECT |
+		NDISUIO_NOTIFICATION_MEDIA_SPECIFIC_NOTIFICATION;
+
+	if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_REQUEST_NOTIFICATION,
+			     &req, sizeof(req), NULL, 0, NULL, NULL)) {
+		wpa_printf(MSG_INFO, "ndisuio_notification_init: "
+			   "IOCTL_NDISUIO_REQUEST_NOTIFICATION failed: %d",
+			   (int) GetLastError());
+		ndisuio_notification_deinit(drv);
+		return -1;
+	}
+
+	eloop_register_event(drv->event_queue, sizeof(drv->event_queue),
+			     ndisuio_notification_receive, drv, NULL);
+
+	return 0;
+}
+#endif /* _WIN32_WCE */
+
+
+static int wpa_driver_ndis_get_names(struct wpa_driver_ndis_data *drv)
+{
+#ifdef CONFIG_USE_NDISUIO
+	NDISUIO_QUERY_BINDING *b;
+	size_t blen = sizeof(*b) + 1024;
+	int i, error, found = 0;
+	DWORD written;
+	char name[256], desc[256], *dpos;
+	WCHAR *pos;
+	size_t j, len, dlen;
+
+	b = os_malloc(blen);
+	if (b == NULL)
+		return -1;
+
+	for (i = 0; ; i++) {
+		os_memset(b, 0, blen);
+		b->BindingIndex = i;
+		if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_QUERY_BINDING,
+				     b, sizeof(NDISUIO_QUERY_BINDING), b, blen,
+				     &written, NULL)) {
+			error = (int) GetLastError();
+			if (error == ERROR_NO_MORE_ITEMS)
+				break;
+			wpa_printf(MSG_DEBUG, "IOCTL_NDISUIO_QUERY_BINDING "
+				   "failed: %d", error);
+			break;
+		}
+
+		pos = (WCHAR *) ((char *) b + b->DeviceNameOffset);
+		len = b->DeviceNameLength;
+		if (len >= sizeof(name))
+			len = sizeof(name) - 1;
+		for (j = 0; j < len; j++)
+			name[j] = (char) pos[j];
+		name[len] = '\0';
+
+		pos = (WCHAR *) ((char *) b + b->DeviceDescrOffset);
+		len = b->DeviceDescrLength;
+		if (len >= sizeof(desc))
+			len = sizeof(desc) - 1;
+		for (j = 0; j < len; j++)
+			desc[j] = (char) pos[j];
+		desc[len] = '\0';
+
+		wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s", i, name, desc);
+
+		if (os_strstr(name, drv->ifname)) {
+			wpa_printf(MSG_DEBUG, "NDIS: Interface name match");
+			found = 1;
+			break;
+		}
+
+		if (os_strncmp(desc, drv->ifname, os_strlen(drv->ifname)) == 0)
+		{
+			wpa_printf(MSG_DEBUG, "NDIS: Interface description "
+				   "match");
+			found = 1;
+			break;
+		}
+	}
+
+	if (!found) {
+		wpa_printf(MSG_DEBUG, "NDIS: Could not find interface '%s'",
+			   drv->ifname);
+		os_free(b);
+		return -1;
+	}
+
+	os_strlcpy(drv->ifname,
+		   os_strncmp(name, "\\DEVICE\\", 8) == 0 ? name + 8 : name,
+		   sizeof(drv->ifname));
+#ifdef _WIN32_WCE
+	drv->adapter_name = wpa_strdup_tchar(drv->ifname);
+	if (drv->adapter_name == NULL) {
+		wpa_printf(MSG_ERROR, "NDIS: Failed to allocate memory for "
+			   "adapter name");
+		os_free(b);
+		return -1;
+	}
+#endif /* _WIN32_WCE */
+
+	dpos = os_strstr(desc, " - ");
+	if (dpos)
+		dlen = dpos - desc;
+	else
+		dlen = os_strlen(desc);
+	drv->adapter_desc = dup_binstr(desc, dlen);
+	os_free(b);
+	if (drv->adapter_desc == NULL)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "NDIS: Adapter description prefix '%s'",
+		   drv->adapter_desc);
+
+	return 0;
+#else /* CONFIG_USE_NDISUIO */
+	PTSTR _names;
+	char *names, *pos, *pos2;
+	ULONG len;
+	BOOLEAN res;
+#define MAX_ADAPTERS 32
+	char *name[MAX_ADAPTERS];
+	char *desc[MAX_ADAPTERS];
+	int num_name, num_desc, i, found_name, found_desc;
+	size_t dlen;
+
+	wpa_printf(MSG_DEBUG, "NDIS: Packet.dll version: %s",
+		   PacketGetVersion());
+
+	len = 8192;
+	_names = os_zalloc(len);
+	if (_names == NULL)
+		return -1;
+
+	res = PacketGetAdapterNames(_names, &len);
+	if (!res && len > 8192) {
+		os_free(_names);
+		_names = os_zalloc(len);
+		if (_names == NULL)
+			return -1;
+		res = PacketGetAdapterNames(_names, &len);
+	}
+
+	if (!res) {
+		wpa_printf(MSG_ERROR, "NDIS: Failed to get adapter list "
+			   "(PacketGetAdapterNames)");
+		os_free(_names);
+		return -1;
+	}
+
+	names = (char *) _names;
+	if (names[0] && names[1] == '\0' && names[2] && names[3] == '\0') {
+		wpa_printf(MSG_DEBUG, "NDIS: Looks like adapter names are in "
+			   "UNICODE");
+		/* Convert to ASCII */
+		pos2 = pos = names;
+		while (pos2 < names + len) {
+			if (pos2[0] == '\0' && pos2[1] == '\0' &&
+			    pos2[2] == '\0' && pos2[3] == '\0') {
+				pos2 += 4;
+				break;
+			}
+			*pos++ = pos2[0];
+			pos2 += 2;
+		}
+		os_memcpy(pos + 2, names, pos - names);
+		pos += 2;
+	} else
+		pos = names;
+
+	num_name = 0;
+	while (pos < names + len) {
+		name[num_name] = pos;
+		while (*pos && pos < names + len)
+			pos++;
+		if (pos + 1 >= names + len) {
+			os_free(names);
+			return -1;
+		}
+		pos++;
+		num_name++;
+		if (num_name >= MAX_ADAPTERS) {
+			wpa_printf(MSG_DEBUG, "NDIS: Too many adapters");
+			os_free(names);
+			return -1;
+		}
+		if (*pos == '\0') {
+			wpa_printf(MSG_DEBUG, "NDIS: %d adapter names found",
+				   num_name);
+			pos++;
+			break;
+		}
+	}
+
+	num_desc = 0;
+	while (pos < names + len) {
+		desc[num_desc] = pos;
+		while (*pos && pos < names + len)
+			pos++;
+		if (pos + 1 >= names + len) {
+			os_free(names);
+			return -1;
+		}
+		pos++;
+		num_desc++;
+		if (num_desc >= MAX_ADAPTERS) {
+			wpa_printf(MSG_DEBUG, "NDIS: Too many adapter "
+				   "descriptions");
+			os_free(names);
+			return -1;
+		}
+		if (*pos == '\0') {
+			wpa_printf(MSG_DEBUG, "NDIS: %d adapter descriptions "
+				   "found", num_name);
+			pos++;
+			break;
+		}
+	}
+
+	/*
+	 * Windows 98 with Packet.dll 3.0 alpha3 does not include adapter
+	 * descriptions. Fill in stub descriptors to work around this.
+	 */
+	while (num_desc < num_name)
+		desc[num_desc++] = "stub description";
+
+	if (num_name != num_desc) {
+		wpa_printf(MSG_DEBUG, "NDIS: mismatch in adapter name and "
+			   "description counts (%d != %d)",
+			   num_name, num_desc);
+		os_free(names);
+		return -1;
+	}
+
+	found_name = found_desc = -1;
+	for (i = 0; i < num_name; i++) {
+		wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s",
+			   i, name[i], desc[i]);
+		if (found_name == -1 && os_strstr(name[i], drv->ifname))
+			found_name = i;
+		if (found_desc == -1 &&
+		    os_strncmp(desc[i], drv->ifname, os_strlen(drv->ifname)) ==
+		    0)
+			found_desc = i;
+	}
+
+	if (found_name < 0 && found_desc >= 0) {
+		wpa_printf(MSG_DEBUG, "NDIS: Matched interface '%s' based on "
+			   "description '%s'",
+			   name[found_desc], desc[found_desc]);
+		found_name = found_desc;
+		os_strlcpy(drv->ifname,
+			   os_strncmp(name[found_desc], "\\Device\\NPF_", 12)
+			   == 0 ? name[found_desc] + 12 : name[found_desc],
+			   sizeof(drv->ifname));
+	}
+
+	if (found_name < 0) {
+		wpa_printf(MSG_DEBUG, "NDIS: Could not find interface '%s'",
+			   drv->ifname);
+		os_free(names);
+		return -1;
+	}
+
+	i = found_name;
+	pos = os_strrchr(desc[i], '(');
+	if (pos) {
+		dlen = pos - desc[i];
+		pos--;
+		if (pos > desc[i] && *pos == ' ')
+			dlen--;
+	} else {
+		dlen = os_strlen(desc[i]);
+	}
+	drv->adapter_desc = dup_binstr(desc[i], dlen);
+	os_free(names);
+	if (drv->adapter_desc == NULL)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "NDIS: Adapter description prefix '%s'",
+		   drv->adapter_desc);
+
+	return 0;
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+#if defined(CONFIG_NATIVE_WINDOWS) || defined(__CYGWIN__)
+#ifndef _WIN32_WCE
+/*
+ * These structures are undocumented for WinXP; only WinCE version is
+ * documented. These would be included wzcsapi.h if it were available. Some
+ * changes here have been needed to make the structures match with WinXP SP2.
+ * It is unclear whether these work with any other version.
+ */
+
+typedef struct {
+	LPWSTR wszGuid;
+} INTF_KEY_ENTRY, *PINTF_KEY_ENTRY;
+
+typedef struct {
+	DWORD dwNumIntfs;
+	PINTF_KEY_ENTRY pIntfs;
+} INTFS_KEY_TABLE, *PINTFS_KEY_TABLE;
+
+typedef struct {
+	DWORD dwDataLen;
+	LPBYTE pData;
+} RAW_DATA, *PRAW_DATA;
+
+typedef struct {
+	LPWSTR wszGuid;
+	LPWSTR wszDescr;
+	ULONG ulMediaState;
+	ULONG ulMediaType;
+	ULONG ulPhysicalMediaType;
+	INT nInfraMode;
+	INT nAuthMode;
+	INT nWepStatus;
+#ifndef _WIN32_WCE
+	u8 pad[2]; /* why is this needed? */
+#endif /* _WIN32_WCE */
+	DWORD dwCtlFlags;
+	DWORD dwCapabilities; /* something added for WinXP SP2(?) */
+	RAW_DATA rdSSID;
+	RAW_DATA rdBSSID;
+	RAW_DATA rdBSSIDList;
+	RAW_DATA rdStSSIDList;
+	RAW_DATA rdCtrlData;
+#ifdef UNDER_CE
+	BOOL bInitialized;
+#endif
+	DWORD nWPAMCastCipher;
+	/* add some extra buffer for later additions since this interface is
+	 * far from stable */
+	u8 later_additions[100];
+} INTF_ENTRY, *PINTF_ENTRY;
+
+#define INTF_ALL 0xffffffff
+#define INTF_ALL_FLAGS 0x0000ffff
+#define INTF_CTLFLAGS 0x00000010
+#define INTFCTL_ENABLED 0x8000
+#endif /* _WIN32_WCE */
+
+
+#ifdef _WIN32_WCE
+static int wpa_driver_ndis_rebind_adapter(struct wpa_driver_ndis_data *drv)
+{
+	HANDLE ndis;
+	TCHAR multi[100];
+	int len;
+
+	len = _tcslen(drv->adapter_name);
+	if (len > 80)
+		return -1;
+
+	ndis = CreateFile(DD_NDIS_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE,
+			  0, NULL, OPEN_EXISTING, 0, NULL);
+	if (ndis == INVALID_HANDLE_VALUE) {
+		wpa_printf(MSG_DEBUG, "NDIS: Failed to open file to NDIS "
+			   "device: %d", (int) GetLastError());
+		return -1;
+	}
+
+	len++;
+	memcpy(multi, drv->adapter_name, len * sizeof(TCHAR));
+	memcpy(&multi[len], TEXT("NDISUIO\0"), 9 * sizeof(TCHAR));
+	len += 9;
+
+	if (!DeviceIoControl(ndis, IOCTL_NDIS_REBIND_ADAPTER,
+			     multi, len * sizeof(TCHAR), NULL, 0, NULL, NULL))
+	{
+		wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDIS_REBIND_ADAPTER "
+			   "failed: 0x%x", (int) GetLastError());
+		wpa_hexdump_ascii(MSG_DEBUG, "NDIS: rebind multi_sz",
+				  (u8 *) multi, len * sizeof(TCHAR));
+		CloseHandle(ndis);
+		return -1;
+	}
+
+	CloseHandle(ndis);
+
+	wpa_printf(MSG_DEBUG, "NDIS: Requested NDIS rebind of NDISUIO "
+		   "protocol");
+
+	return 0;
+}
+#endif /* _WIN32_WCE */
+
+
+static int wpa_driver_ndis_set_wzc(struct wpa_driver_ndis_data *drv,
+				   int enable)
+{
+#ifdef _WIN32_WCE
+	HKEY hk, hk2;
+	LONG ret;
+	DWORD i, hnd, len;
+	TCHAR keyname[256], devname[256];
+
+#define WZC_DRIVER TEXT("Drivers\\BuiltIn\\ZeroConfig")
+
+	if (enable) {
+		HANDLE h;
+		h = ActivateDeviceEx(WZC_DRIVER, NULL, 0, NULL);
+		if (h == INVALID_HANDLE_VALUE || h == 0) {
+			wpa_printf(MSG_DEBUG, "NDIS: Failed to re-enable WZC "
+				   "- ActivateDeviceEx failed: %d",
+				   (int) GetLastError());
+			return -1;
+		}
+
+		wpa_printf(MSG_DEBUG, "NDIS: WZC re-enabled");
+		return wpa_driver_ndis_rebind_adapter(drv);
+	}
+
+	/*
+	 * Unfortunately, just disabling the WZC for an interface is not enough
+	 * to free NDISUIO for us, so need to disable and unload WZC completely
+	 * for now when using WinCE with NDISUIO. In addition, must request
+	 * NDISUIO protocol to be rebound to the adapter in order to free the
+	 * NDISUIO binding that WZC hold before us.
+	 */
+
+	/* Enumerate HKLM\Drivers\Active\* to find a handle to WZC. */
+	ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, DEVLOAD_ACTIVE_KEY, 0, 0, &hk);
+	if (ret != ERROR_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "NDIS: RegOpenKeyEx(DEVLOAD_ACTIVE_KEY) "
+			   "failed: %d %d", (int) ret, (int) GetLastError());
+		return -1;
+	}
+
+	for (i = 0; ; i++) {
+		len = sizeof(keyname);
+		ret = RegEnumKeyEx(hk, i, keyname, &len, NULL, NULL, NULL,
+				   NULL);
+		if (ret != ERROR_SUCCESS) {
+			wpa_printf(MSG_DEBUG, "NDIS: Could not find active "
+				   "WZC - assuming it is not running.");
+			RegCloseKey(hk);
+			return -1;
+		}
+
+		ret = RegOpenKeyEx(hk, keyname, 0, 0, &hk2);
+		if (ret != ERROR_SUCCESS) {
+			wpa_printf(MSG_DEBUG, "NDIS: RegOpenKeyEx(active dev) "
+				   "failed: %d %d",
+				   (int) ret, (int) GetLastError());
+			continue;
+		}
+
+		len = sizeof(devname);
+		ret = RegQueryValueEx(hk2, DEVLOAD_DEVKEY_VALNAME, NULL, NULL,
+				      (LPBYTE) devname, &len);
+		if (ret != ERROR_SUCCESS) {
+			wpa_printf(MSG_DEBUG, "NDIS: RegQueryValueEx("
+				   "DEVKEY_VALNAME) failed: %d %d",
+				   (int) ret, (int) GetLastError());
+			RegCloseKey(hk2);
+			continue;
+		}
+
+		if (_tcscmp(devname, WZC_DRIVER) == 0)
+			break;
+
+		RegCloseKey(hk2);
+	}
+
+	RegCloseKey(hk);
+
+	/* Found WZC - get handle to it. */
+	len = sizeof(hnd);
+	ret = RegQueryValueEx(hk2, DEVLOAD_HANDLE_VALNAME, NULL, NULL,
+			      (PUCHAR) &hnd, &len);
+	if (ret != ERROR_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "NDIS: RegQueryValueEx(HANDLE_VALNAME) "
+			   "failed: %d %d", (int) ret, (int) GetLastError());
+		RegCloseKey(hk2);
+		return -1;
+	}
+
+	RegCloseKey(hk2);
+
+	/* Deactivate WZC */
+	if (!DeactivateDevice((HANDLE) hnd)) {
+		wpa_printf(MSG_DEBUG, "NDIS: DeactivateDevice failed: %d",
+			   (int) GetLastError());
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "NDIS: Disabled WZC temporarily");
+	drv->wzc_disabled = 1;
+	return wpa_driver_ndis_rebind_adapter(drv);
+
+#else /* _WIN32_WCE */
+
+	HMODULE hm;
+	DWORD (WINAPI *wzc_enum_interf)(LPWSTR pSrvAddr,
+					PINTFS_KEY_TABLE pIntfs);
+	DWORD (WINAPI *wzc_query_interf)(LPWSTR pSrvAddr, DWORD dwInFlags,
+					 PINTF_ENTRY pIntf,
+					 LPDWORD pdwOutFlags);
+	DWORD (WINAPI *wzc_set_interf)(LPWSTR pSrvAddr, DWORD dwInFlags,
+				       PINTF_ENTRY pIntf, LPDWORD pdwOutFlags);
+	int ret = -1, j;
+	DWORD res;
+	INTFS_KEY_TABLE guids;
+	INTF_ENTRY intf;
+	char guid[128];
+	WCHAR *pos;
+	DWORD flags, i;
+
+	hm = LoadLibrary(TEXT("wzcsapi.dll"));
+	if (hm == NULL) {
+		wpa_printf(MSG_DEBUG, "NDIS: Failed to load wzcsapi.dll (%u) "
+			   "- WZC probably not running",
+			   (unsigned int) GetLastError());
+		return -1;
+	}
+
+#ifdef _WIN32_WCE
+	wzc_enum_interf = (void *) GetProcAddressA(hm, "WZCEnumInterfaces");
+	wzc_query_interf = (void *) GetProcAddressA(hm, "WZCQueryInterface");
+	wzc_set_interf = (void *) GetProcAddressA(hm, "WZCSetInterface");
+#else /* _WIN32_WCE */
+	wzc_enum_interf = (void *) GetProcAddress(hm, "WZCEnumInterfaces");
+	wzc_query_interf = (void *) GetProcAddress(hm, "WZCQueryInterface");
+	wzc_set_interf = (void *) GetProcAddress(hm, "WZCSetInterface");
+#endif /* _WIN32_WCE */
+
+	if (wzc_enum_interf == NULL || wzc_query_interf == NULL ||
+	    wzc_set_interf == NULL) {
+		wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces, "
+			   "WZCQueryInterface, or WZCSetInterface not found "
+			   "in wzcsapi.dll");
+		goto fail;
+	}
+
+	os_memset(&guids, 0, sizeof(guids));
+	res = wzc_enum_interf(NULL, &guids);
+	if (res != 0) {
+		wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces failed: %d; "
+			   "WZC service is apparently not running",
+			   (int) res);
+		goto fail;
+	}
+
+	wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces: %d interfaces",
+		   (int) guids.dwNumIntfs);
+
+	for (i = 0; i < guids.dwNumIntfs; i++) {
+		pos = guids.pIntfs[i].wszGuid;
+		for (j = 0; j < sizeof(guid); j++) {
+			guid[j] = (char) *pos;
+			if (*pos == 0)
+				break;
+			pos++;
+		}
+		guid[sizeof(guid) - 1] = '\0';
+		wpa_printf(MSG_DEBUG, "NDIS: intfs %d GUID '%s'",
+			   (int) i, guid);
+		if (os_strstr(drv->ifname, guid) == NULL)
+			continue;
+
+		wpa_printf(MSG_DEBUG, "NDIS: Current interface found from "
+			   "WZC");
+		break;
+	}
+
+	if (i >= guids.dwNumIntfs) {
+		wpa_printf(MSG_DEBUG, "NDIS: Current interface not found from "
+			   "WZC");
+		goto fail;
+	}
+
+	os_memset(&intf, 0, sizeof(intf));
+	intf.wszGuid = guids.pIntfs[i].wszGuid;
+	/* Set flags to verify that the structure has not changed. */
+	intf.dwCtlFlags = -1;
+	flags = 0;
+	res = wzc_query_interf(NULL, INTFCTL_ENABLED, &intf, &flags);
+	if (res != 0) {
+		wpa_printf(MSG_DEBUG, "NDIS: Could not query flags for the "
+			   "WZC interface: %d (0x%x)",
+			   (int) res, (int) res);
+		wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u",
+			   (unsigned int) GetLastError());
+		goto fail;
+	}
+
+	wpa_printf(MSG_DEBUG, "NDIS: WZC interface flags 0x%x dwCtlFlags 0x%x",
+		   (int) flags, (int) intf.dwCtlFlags);
+
+	if (intf.dwCtlFlags == -1) {
+		wpa_printf(MSG_DEBUG, "NDIS: Looks like wzcsapi has changed "
+			   "again - could not disable WZC");
+		wpa_hexdump(MSG_MSGDUMP, "NDIS: intf",
+			    (u8 *) &intf, sizeof(intf));
+		goto fail;
+	}
+
+	if (enable) {
+		if (!(intf.dwCtlFlags & INTFCTL_ENABLED)) {
+			wpa_printf(MSG_DEBUG, "NDIS: Enabling WZC for this "
+				   "interface");
+			intf.dwCtlFlags |= INTFCTL_ENABLED;
+			res = wzc_set_interf(NULL, INTFCTL_ENABLED, &intf,
+					     &flags);
+			if (res != 0) {
+				wpa_printf(MSG_DEBUG, "NDIS: Failed to enable "
+					   "WZC: %d (0x%x)",
+					   (int) res, (int) res);
+				wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u",
+					   (unsigned int) GetLastError());
+				goto fail;
+			}
+			wpa_printf(MSG_DEBUG, "NDIS: Re-enabled WZC for this "
+				   "interface");
+			drv->wzc_disabled = 0;
+		}
+	} else {
+		if (intf.dwCtlFlags & INTFCTL_ENABLED) {
+			wpa_printf(MSG_DEBUG, "NDIS: Disabling WZC for this "
+				   "interface");
+			intf.dwCtlFlags &= ~INTFCTL_ENABLED;
+			res = wzc_set_interf(NULL, INTFCTL_ENABLED, &intf,
+					     &flags);
+			if (res != 0) {
+				wpa_printf(MSG_DEBUG, "NDIS: Failed to "
+					   "disable WZC: %d (0x%x)",
+					   (int) res, (int) res);
+				wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u",
+					   (unsigned int) GetLastError());
+				goto fail;
+			}
+			wpa_printf(MSG_DEBUG, "NDIS: Disabled WZC temporarily "
+				   "for this interface");
+			drv->wzc_disabled = 1;
+		} else {
+			wpa_printf(MSG_DEBUG, "NDIS: WZC was not enabled for "
+				   "this interface");
+		}
+	}
+
+	ret = 0;
+
+fail:
+	FreeLibrary(hm);
+
+	return ret;
+#endif /* _WIN32_WCE */
+}
+
+#else /* CONFIG_NATIVE_WINDOWS || __CYGWIN__ */
+
+static int wpa_driver_ndis_set_wzc(struct wpa_driver_ndis_data *drv,
+				   int enable)
+{
+	return 0;
+}
+
+#endif /* CONFIG_NATIVE_WINDOWS || __CYGWIN__ */
+
+
+#ifdef CONFIG_USE_NDISUIO
+/*
+ * l2_packet_ndis.c is sharing the same handle to NDISUIO, so we must be able
+ * to export this handle. This is somewhat ugly, but there is no better
+ * mechanism available to pass data from driver interface to l2_packet wrapper.
+ */
+static HANDLE driver_ndis_ndisuio_handle = INVALID_HANDLE_VALUE;
+
+HANDLE driver_ndis_get_ndisuio_handle(void)
+{
+	return driver_ndis_ndisuio_handle;
+}
+#endif /* CONFIG_USE_NDISUIO */
+
+
+static int wpa_driver_ndis_adapter_init(struct wpa_driver_ndis_data *drv)
+{
+#ifdef CONFIG_USE_NDISUIO
+#ifndef _WIN32_WCE
+#define NDISUIO_DEVICE_NAME TEXT("\\\\.\\\\Ndisuio")
+	DWORD written;
+#endif /* _WIN32_WCE */
+	drv->ndisuio = CreateFile(NDISUIO_DEVICE_NAME,
+				  GENERIC_READ | GENERIC_WRITE, 0, NULL,
+				  OPEN_EXISTING,
+				  FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
+				  INVALID_HANDLE_VALUE);
+	if (drv->ndisuio == INVALID_HANDLE_VALUE) {
+		wpa_printf(MSG_ERROR, "NDIS: Failed to open connection to "
+			   "NDISUIO: %d", (int) GetLastError());
+		return -1;
+	}
+	driver_ndis_ndisuio_handle = drv->ndisuio;
+
+#ifndef _WIN32_WCE
+	if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_BIND_WAIT, NULL, 0,
+			     NULL, 0, &written, NULL)) {
+		wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_BIND_WAIT failed: "
+			   "%d", (int) GetLastError());
+		CloseHandle(drv->ndisuio);
+		drv->ndisuio = INVALID_HANDLE_VALUE;
+		return -1;
+	}
+#endif /* _WIN32_WCE */
+
+	return 0;
+#else /* CONFIG_USE_NDISUIO */
+	return 0;
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+static int wpa_driver_ndis_adapter_open(struct wpa_driver_ndis_data *drv)
+{
+#ifdef CONFIG_USE_NDISUIO
+	DWORD written;
+#define MAX_NDIS_DEVICE_NAME_LEN 256
+	WCHAR ifname[MAX_NDIS_DEVICE_NAME_LEN];
+	size_t len, i, pos;
+	const char *prefix = "\\DEVICE\\";
+
+#ifdef _WIN32_WCE
+	pos = 0;
+#else /* _WIN32_WCE */
+	pos = 8;
+#endif /* _WIN32_WCE */
+	len = pos + os_strlen(drv->ifname);
+	if (len >= MAX_NDIS_DEVICE_NAME_LEN)
+		return -1;
+	for (i = 0; i < pos; i++)
+		ifname[i] = (WCHAR) prefix[i];
+	for (i = pos; i < len; i++)
+		ifname[i] = (WCHAR) drv->ifname[i - pos];
+	ifname[i] = L'\0';
+
+	if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_OPEN_DEVICE,
+			     ifname, len * sizeof(WCHAR), NULL, 0, &written,
+			     NULL)) {
+		wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_OPEN_DEVICE "
+			   "failed: %d", (int) GetLastError());
+		wpa_hexdump_ascii(MSG_DEBUG, "NDIS: ifname",
+				  (const u8 *) ifname, len * sizeof(WCHAR));
+		CloseHandle(drv->ndisuio);
+		drv->ndisuio = INVALID_HANDLE_VALUE;
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "NDIS: Opened NDISUIO device successfully");
+
+	return 0;
+#else /* CONFIG_USE_NDISUIO */
+	char ifname[128];
+	os_snprintf(ifname, sizeof(ifname), "\\Device\\NPF_%s", drv->ifname);
+	drv->adapter = PacketOpenAdapter(ifname);
+	if (drv->adapter == NULL) {
+		wpa_printf(MSG_DEBUG, "NDIS: PacketOpenAdapter failed for "
+			   "'%s'", ifname);
+		return -1;
+	}
+	return 0;
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+static void wpa_driver_ndis_adapter_close(struct wpa_driver_ndis_data *drv)
+{
+#ifdef CONFIG_USE_NDISUIO
+	driver_ndis_ndisuio_handle = INVALID_HANDLE_VALUE;
+	if (drv->ndisuio != INVALID_HANDLE_VALUE)
+		CloseHandle(drv->ndisuio);
+#else /* CONFIG_USE_NDISUIO */
+	if (drv->adapter)
+		PacketCloseAdapter(drv->adapter);
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+static int ndis_add_multicast(struct wpa_driver_ndis_data *drv)
+{
+	if (ndis_set_oid(drv, OID_802_3_MULTICAST_LIST,
+			 (const char *) pae_group_addr, ETH_ALEN) < 0) {
+		wpa_printf(MSG_DEBUG, "NDIS: Failed to add PAE group address "
+			   "to the multicast list");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static void * wpa_driver_ndis_init(void *ctx, const char *ifname)
+{
+	struct wpa_driver_ndis_data *drv;
+	u32 mode;
+	int i;
+
+	drv = os_zalloc(sizeof(*drv));
+	if (drv == NULL)
+		return NULL;
+	drv->ctx = ctx;
+	/*
+	 * Compatibility code to strip possible prefix from the GUID. Previous
+	 * versions include \Device\NPF_ prefix for all names, but the internal
+	 * interface name is now only the GUI. Both Packet32 and NDISUIO
+	 * prefixes are supported.
+	 */
+	if (os_strncmp(ifname, "\\Device\\NPF_", 12) == 0)
+		ifname += 12;
+	else if (os_strncmp(ifname, "\\DEVICE\\", 8) == 0)
+		ifname += 8;
+	os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+
+	if (wpa_driver_ndis_adapter_init(drv) < 0) {
+		os_free(drv);
+		return NULL;
+	}
+
+	if (wpa_driver_ndis_get_names(drv) < 0) {
+		wpa_driver_ndis_adapter_close(drv);
+		os_free(drv);
+		return NULL;
+	}
+
+	wpa_driver_ndis_set_wzc(drv, 0);
+
+	if (wpa_driver_ndis_adapter_open(drv) < 0) {
+		wpa_driver_ndis_adapter_close(drv);
+		os_free(drv);
+		return NULL;
+	}
+
+	if (ndis_get_oid(drv, OID_802_3_CURRENT_ADDRESS,
+			 (char *) drv->own_addr, ETH_ALEN) < 0) {
+		wpa_printf(MSG_DEBUG, "NDIS: Get OID_802_3_CURRENT_ADDRESS "
+			   "failed");
+		wpa_driver_ndis_adapter_close(drv);
+		os_free(drv);
+		return NULL;
+	}
+	wpa_driver_ndis_get_capability(drv);
+
+	/* Update per interface supported AKMs */
+	for (i = 0; i < WPA_IF_MAX; i++)
+		drv->capa.key_mgmt_iftype[i] = drv->capa.key_mgmt;
+
+
+	/* Make sure that the driver does not have any obsolete PMKID entries.
+	 */
+	wpa_driver_ndis_flush_pmkid(drv);
+
+	/*
+	 * Disconnect to make sure that driver re-associates if it was
+	 * connected.
+	 */
+	wpa_driver_ndis_disconnect(drv);
+
+	eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout, drv, NULL);
+
+#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+	drv->events = ndis_events_init(&drv->events_pipe, &drv->event_avail,
+				       drv->ifname, drv->adapter_desc);
+	if (drv->events == NULL) {
+		wpa_driver_ndis_deinit(drv);
+		return NULL;
+	}
+	eloop_register_event(drv->event_avail, sizeof(drv->event_avail),
+			     wpa_driver_ndis_event_pipe_cb, drv, NULL);
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+
+#ifdef _WIN32_WCE
+	if (ndisuio_notification_init(drv) < 0) {
+		wpa_driver_ndis_deinit(drv);
+		return NULL;
+	}
+#endif /* _WIN32_WCE */
+
+	/* Set mode here in case card was configured for ad-hoc mode
+	 * previously. */
+	mode = Ndis802_11Infrastructure;
+	if (ndis_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE,
+			 (char *) &mode, sizeof(mode)) < 0) {
+		char buf[8];
+		int res;
+		wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
+			   "OID_802_11_INFRASTRUCTURE_MODE (%d)",
+			   (int) mode);
+		/* Try to continue anyway */
+
+		res = ndis_get_oid(drv, OID_DOT11_CURRENT_OPERATION_MODE, buf,
+				   sizeof(buf));
+		if (res > 0) {
+			wpa_printf(MSG_INFO, "NDIS: The driver seems to use "
+				   "Native 802.11 OIDs. These are not yet "
+				   "fully supported.");
+			drv->native80211 = 1;
+		} else if (!drv->has_capability || drv->capa.enc == 0) {
+			/*
+			 * Note: This will also happen with NDIS 6 drivers with
+			 * Vista.
+			 */
+			wpa_printf(MSG_DEBUG, "NDIS: Driver did not provide "
+				   "any wireless capabilities - assume it is "
+				   "a wired interface");
+			drv->wired = 1;
+			drv->capa.flags |= WPA_DRIVER_FLAGS_WIRED;
+			drv->has_capability = 1;
+			ndis_add_multicast(drv);
+		}
+	}
+
+	return drv;
+}
+
+
+static void wpa_driver_ndis_deinit(void *priv)
+{
+	struct wpa_driver_ndis_data *drv = priv;
+
+#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+	if (drv->events) {
+		eloop_unregister_event(drv->event_avail,
+				       sizeof(drv->event_avail));
+		ndis_events_deinit(drv->events);
+	}
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+
+#ifdef _WIN32_WCE
+	ndisuio_notification_deinit(drv);
+#endif /* _WIN32_WCE */
+
+	eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx);
+	eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL);
+	wpa_driver_ndis_flush_pmkid(drv);
+	wpa_driver_ndis_disconnect(drv);
+	if (wpa_driver_ndis_radio_off(drv) < 0) {
+		wpa_printf(MSG_DEBUG, "NDIS: failed to disassociate and turn "
+			   "radio off");
+	}
+
+	wpa_driver_ndis_adapter_close(drv);
+
+	if (drv->wzc_disabled)
+		wpa_driver_ndis_set_wzc(drv, 1);
+
+#ifdef _WIN32_WCE
+	os_free(drv->adapter_name);
+#endif /* _WIN32_WCE */
+	os_free(drv->adapter_desc);
+	os_free(drv);
+}
+
+
+static struct wpa_interface_info *
+wpa_driver_ndis_get_interfaces(void *global_priv)
+{
+	struct wpa_interface_info *iface = NULL, *niface;
+
+#ifdef CONFIG_USE_NDISUIO
+	NDISUIO_QUERY_BINDING *b;
+	size_t blen = sizeof(*b) + 1024;
+	int i, error;
+	DWORD written;
+	char name[256], desc[256];
+	WCHAR *pos;
+	size_t j, len;
+	HANDLE ndisuio;
+
+	ndisuio = CreateFile(NDISUIO_DEVICE_NAME,
+			     GENERIC_READ | GENERIC_WRITE, 0, NULL,
+			     OPEN_EXISTING,
+			     FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
+			     INVALID_HANDLE_VALUE);
+	if (ndisuio == INVALID_HANDLE_VALUE) {
+		wpa_printf(MSG_ERROR, "NDIS: Failed to open connection to "
+			   "NDISUIO: %d", (int) GetLastError());
+		return NULL;
+	}
+
+#ifndef _WIN32_WCE
+	if (!DeviceIoControl(ndisuio, IOCTL_NDISUIO_BIND_WAIT, NULL, 0,
+			     NULL, 0, &written, NULL)) {
+		wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_BIND_WAIT failed: "
+			   "%d", (int) GetLastError());
+		CloseHandle(ndisuio);
+		return NULL;
+	}
+#endif /* _WIN32_WCE */
+
+	b = os_malloc(blen);
+	if (b == NULL) {
+		CloseHandle(ndisuio);
+		return NULL;
+	}
+
+	for (i = 0; ; i++) {
+		os_memset(b, 0, blen);
+		b->BindingIndex = i;
+		if (!DeviceIoControl(ndisuio, IOCTL_NDISUIO_QUERY_BINDING,
+				     b, sizeof(NDISUIO_QUERY_BINDING), b, blen,
+				     &written, NULL)) {
+			error = (int) GetLastError();
+			if (error == ERROR_NO_MORE_ITEMS)
+				break;
+			wpa_printf(MSG_DEBUG, "IOCTL_NDISUIO_QUERY_BINDING "
+				   "failed: %d", error);
+			break;
+		}
+
+		pos = (WCHAR *) ((char *) b + b->DeviceNameOffset);
+		len = b->DeviceNameLength;
+		if (len >= sizeof(name))
+			len = sizeof(name) - 1;
+		for (j = 0; j < len; j++)
+			name[j] = (char) pos[j];
+		name[len] = '\0';
+
+		pos = (WCHAR *) ((char *) b + b->DeviceDescrOffset);
+		len = b->DeviceDescrLength;
+		if (len >= sizeof(desc))
+			len = sizeof(desc) - 1;
+		for (j = 0; j < len; j++)
+			desc[j] = (char) pos[j];
+		desc[len] = '\0';
+
+		wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s", i, name, desc);
+
+		niface = os_zalloc(sizeof(*niface));
+		if (niface == NULL)
+			break;
+		niface->drv_name = "ndis";
+		if (os_strncmp(name, "\\DEVICE\\", 8) == 0)
+			niface->ifname = os_strdup(name + 8);
+		else
+			niface->ifname = os_strdup(name);
+		if (niface->ifname == NULL) {
+			os_free(niface);
+			break;
+		}
+		niface->desc = os_strdup(desc);
+		niface->next = iface;
+		iface = niface;
+	}
+
+	os_free(b);
+	CloseHandle(ndisuio);
+#else /* CONFIG_USE_NDISUIO */
+	PTSTR _names;
+	char *names, *pos, *pos2;
+	ULONG len;
+	BOOLEAN res;
+	char *name[MAX_ADAPTERS];
+	char *desc[MAX_ADAPTERS];
+	int num_name, num_desc, i;
+
+	wpa_printf(MSG_DEBUG, "NDIS: Packet.dll version: %s",
+		   PacketGetVersion());
+
+	len = 8192;
+	_names = os_zalloc(len);
+	if (_names == NULL)
+		return NULL;
+
+	res = PacketGetAdapterNames(_names, &len);
+	if (!res && len > 8192) {
+		os_free(_names);
+		_names = os_zalloc(len);
+		if (_names == NULL)
+			return NULL;
+		res = PacketGetAdapterNames(_names, &len);
+	}
+
+	if (!res) {
+		wpa_printf(MSG_ERROR, "NDIS: Failed to get adapter list "
+			   "(PacketGetAdapterNames)");
+		os_free(_names);
+		return NULL;
+	}
+
+	names = (char *) _names;
+	if (names[0] && names[1] == '\0' && names[2] && names[3] == '\0') {
+		wpa_printf(MSG_DEBUG, "NDIS: Looks like adapter names are in "
+			   "UNICODE");
+		/* Convert to ASCII */
+		pos2 = pos = names;
+		while (pos2 < names + len) {
+			if (pos2[0] == '\0' && pos2[1] == '\0' &&
+			    pos2[2] == '\0' && pos2[3] == '\0') {
+				pos2 += 4;
+				break;
+			}
+			*pos++ = pos2[0];
+			pos2 += 2;
+		}
+		os_memcpy(pos + 2, names, pos - names);
+		pos += 2;
+	} else
+		pos = names;
+
+	num_name = 0;
+	while (pos < names + len) {
+		name[num_name] = pos;
+		while (*pos && pos < names + len)
+			pos++;
+		if (pos + 1 >= names + len) {
+			os_free(names);
+			return NULL;
+		}
+		pos++;
+		num_name++;
+		if (num_name >= MAX_ADAPTERS) {
+			wpa_printf(MSG_DEBUG, "NDIS: Too many adapters");
+			os_free(names);
+			return NULL;
+		}
+		if (*pos == '\0') {
+			wpa_printf(MSG_DEBUG, "NDIS: %d adapter names found",
+				   num_name);
+			pos++;
+			break;
+		}
+	}
+
+	num_desc = 0;
+	while (pos < names + len) {
+		desc[num_desc] = pos;
+		while (*pos && pos < names + len)
+			pos++;
+		if (pos + 1 >= names + len) {
+			os_free(names);
+			return NULL;
+		}
+		pos++;
+		num_desc++;
+		if (num_desc >= MAX_ADAPTERS) {
+			wpa_printf(MSG_DEBUG, "NDIS: Too many adapter "
+				   "descriptions");
+			os_free(names);
+			return NULL;
+		}
+		if (*pos == '\0') {
+			wpa_printf(MSG_DEBUG, "NDIS: %d adapter descriptions "
+				   "found", num_name);
+			pos++;
+			break;
+		}
+	}
+
+	/*
+	 * Windows 98 with Packet.dll 3.0 alpha3 does not include adapter
+	 * descriptions. Fill in stub descriptors to work around this.
+	 */
+	while (num_desc < num_name)
+		desc[num_desc++] = "stub description";
+
+	if (num_name != num_desc) {
+		wpa_printf(MSG_DEBUG, "NDIS: mismatch in adapter name and "
+			   "description counts (%d != %d)",
+			   num_name, num_desc);
+		os_free(names);
+		return NULL;
+	}
+
+	for (i = 0; i < num_name; i++) {
+		niface = os_zalloc(sizeof(*niface));
+		if (niface == NULL)
+			break;
+		niface->drv_name = "ndis";
+		if (os_strncmp(name[i], "\\Device\\NPF_", 12) == 0)
+			niface->ifname = os_strdup(name[i] + 12);
+		else
+			niface->ifname = os_strdup(name[i]);
+		if (niface->ifname == NULL) {
+			os_free(niface);
+			break;
+		}
+		niface->desc = os_strdup(desc[i]);
+		niface->next = iface;
+		iface = niface;
+	}
+
+#endif /* CONFIG_USE_NDISUIO */
+
+	return iface;
+}
+
+
+static const char *ndis_drv_name = "ndis";
+static const char *ndis_drv_desc = "Windows NDIS driver";
+
+struct wpa_driver_ops wpa_driver_ndis_ops;
+
+void driver_ndis_init_ops(void)
+{
+	os_memset(&wpa_driver_ndis_ops, 0, sizeof(wpa_driver_ndis_ops));
+	wpa_driver_ndis_ops.name = ndis_drv_name;
+	wpa_driver_ndis_ops.desc = ndis_drv_desc;
+	wpa_driver_ndis_ops.get_bssid = wpa_driver_ndis_get_bssid;
+	wpa_driver_ndis_ops.get_ssid = wpa_driver_ndis_get_ssid;
+	wpa_driver_ndis_ops.set_key = wpa_driver_ndis_set_key_wrapper;
+	wpa_driver_ndis_ops.init = wpa_driver_ndis_init;
+	wpa_driver_ndis_ops.deinit = wpa_driver_ndis_deinit;
+	wpa_driver_ndis_ops.deauthenticate = wpa_driver_ndis_deauthenticate;
+	wpa_driver_ndis_ops.associate = wpa_driver_ndis_associate;
+	wpa_driver_ndis_ops.add_pmkid = wpa_driver_ndis_add_pmkid;
+	wpa_driver_ndis_ops.remove_pmkid = wpa_driver_ndis_remove_pmkid;
+	wpa_driver_ndis_ops.flush_pmkid = wpa_driver_ndis_flush_pmkid;
+	wpa_driver_ndis_ops.get_capa = wpa_driver_ndis_get_capa;
+	wpa_driver_ndis_ops.poll = wpa_driver_ndis_poll;
+	wpa_driver_ndis_ops.get_ifname = wpa_driver_ndis_get_ifname;
+	wpa_driver_ndis_ops.get_mac_addr = wpa_driver_ndis_get_mac_addr;
+	wpa_driver_ndis_ops.get_scan_results2 =
+		wpa_driver_ndis_get_scan_results;
+	wpa_driver_ndis_ops.get_interfaces = wpa_driver_ndis_get_interfaces;
+	wpa_driver_ndis_ops.scan2 = wpa_driver_ndis_scan;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_ndis.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_ndis.h
new file mode 100644
index 0000000..89d136d
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_ndis.h
@@ -0,0 +1,59 @@
+/*
+ * WPA Supplicant - Windows/NDIS driver interface
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DRIVER_NDIS_H
+#define DRIVER_NDIS_H
+
+#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+struct ndis_events_data;
+struct ndis_events_data * ndis_events_init(HANDLE *read_pipe, HANDLE *event,
+					   const char *ifname,
+					   const char *desc);
+void ndis_events_deinit(struct ndis_events_data *events);
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+
+struct ndis_pmkid_entry {
+	struct ndis_pmkid_entry *next;
+	u8 bssid[ETH_ALEN];
+	u8 pmkid[16];
+};
+
+struct wpa_driver_ndis_data {
+	void *ctx;
+	char ifname[100]; /* GUID: {7EE3EFE5-C165-472F-986D-F6FBEDFE8C8D} */
+#ifdef _WIN32_WCE
+	TCHAR *adapter_name;
+	HANDLE event_queue; /* NDISUIO notifier MsgQueue */
+	HANDLE connected_event; /* WpaSupplicantConnected event */
+#endif /* _WIN32_WCE */
+	u8 own_addr[ETH_ALEN];
+#ifdef CONFIG_USE_NDISUIO
+	HANDLE ndisuio;
+#else /* CONFIG_USE_NDISUIO */
+	LPADAPTER adapter;
+#endif /* CONFIG_USE_NDISUIO */
+	u8 bssid[ETH_ALEN];
+
+	int has_capability;
+	int no_of_pmkid;
+	int radio_enabled;
+	struct wpa_driver_capa capa;
+	struct ndis_pmkid_entry *pmkid;
+	char *adapter_desc;
+	int wired;
+	int native80211;
+	int mode;
+	int wzc_disabled;
+	int oid_bssid_set;
+#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+	HANDLE events_pipe, event_avail;
+	struct ndis_events_data *events;
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+};
+
+#endif /* DRIVER_NDIS_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_ndis_.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_ndis_.c
new file mode 100644
index 0000000..4d23001
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_ndis_.c
@@ -0,0 +1,99 @@
+/*
+ * WPA Supplicant - Windows/NDIS driver interface - event processing
+ * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "driver.h"
+#include "eloop.h"
+
+/* Keep this event processing in a separate file and without WinPcap headers to
+ * avoid conflicts with some of the header files. */
+struct _ADAPTER;
+typedef struct _ADAPTER * LPADAPTER;
+#include "driver_ndis.h"
+
+
+void wpa_driver_ndis_event_connect(struct wpa_driver_ndis_data *drv);
+void wpa_driver_ndis_event_disconnect(struct wpa_driver_ndis_data *drv);
+void wpa_driver_ndis_event_media_specific(struct wpa_driver_ndis_data *drv,
+					  const u8 *data, size_t data_len);
+void wpa_driver_ndis_event_adapter_arrival(struct wpa_driver_ndis_data *drv);
+void wpa_driver_ndis_event_adapter_removal(struct wpa_driver_ndis_data *drv);
+
+
+enum event_types { EVENT_CONNECT, EVENT_DISCONNECT,
+		   EVENT_MEDIA_SPECIFIC, EVENT_ADAPTER_ARRIVAL,
+		   EVENT_ADAPTER_REMOVAL };
+
+/* Event data:
+ * enum event_types (as int, i.e., 4 octets)
+ * data length (2 octets (big endian), optional)
+ * data (variable len, optional)
+ */
+
+
+static void wpa_driver_ndis_event_process(struct wpa_driver_ndis_data *drv,
+					  u8 *buf, size_t len)
+{
+	u8 *pos, *data = NULL;
+	enum event_types type;
+	size_t data_len = 0;
+
+	wpa_hexdump(MSG_MSGDUMP, "NDIS: received event data", buf, len);
+	if (len < sizeof(int))
+		return;
+	type = *((int *) buf);
+	pos = buf + sizeof(int);
+	wpa_printf(MSG_DEBUG, "NDIS: event - type %d", type);
+
+	if (buf + len - pos > 2) {
+		data_len = (int) *pos++ << 8;
+		data_len += *pos++;
+		if (data_len > (size_t) (buf + len - pos)) {
+			wpa_printf(MSG_DEBUG, "NDIS: event data overflow");
+			return;
+		}
+		data = pos;
+		wpa_hexdump(MSG_MSGDUMP, "NDIS: event data", data, data_len);
+	}
+
+	switch (type) {
+	case EVENT_CONNECT:
+		wpa_driver_ndis_event_connect(drv);
+		break;
+	case EVENT_DISCONNECT:
+		wpa_driver_ndis_event_disconnect(drv);
+		break;
+	case EVENT_MEDIA_SPECIFIC:
+		wpa_driver_ndis_event_media_specific(drv, data, data_len);
+		break;
+	case EVENT_ADAPTER_ARRIVAL:
+		wpa_driver_ndis_event_adapter_arrival(drv);
+		break;
+	case EVENT_ADAPTER_REMOVAL:
+		wpa_driver_ndis_event_adapter_removal(drv);
+		break;
+	}
+}
+
+
+void wpa_driver_ndis_event_pipe_cb(void *eloop_data, void *user_data)
+{
+	struct wpa_driver_ndis_data *drv = eloop_data;
+	u8 buf[512];
+	DWORD len;
+
+	ResetEvent(drv->event_avail);
+	if (ReadFile(drv->events_pipe, buf, sizeof(buf), &len, NULL))
+		wpa_driver_ndis_event_process(drv, buf, len);
+	else {
+		wpa_printf(MSG_DEBUG, "%s: ReadFile() failed: %d", __func__,
+			   (int) GetLastError());
+	}
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211.c
new file mode 100644
index 0000000..eb46e4d
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211.c
@@ -0,0 +1,13795 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211
+ * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2004, Instant802 Networks, Inc.
+ * Copyright (c) 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/types.h>
+#include <fcntl.h>
+#include <net/if.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/ctrl.h>
+#include <linux/rtnetlink.h>
+#include <netpacket/packet.h>
+#include <linux/errqueue.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "common/qca-vendor.h"
+#include "common/qca-vendor-attr.h"
+#include "common/brcm_vendor.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_common.h"
+#include "crypto/sha256.h"
+#include "crypto/sha384.h"
+#include "netlink.h"
+#include "linux_defines.h"
+#include "linux_ioctl.h"
+#include "radiotap.h"
+#include "radiotap_iter.h"
+#include "rfkill.h"
+#include "driver_nl80211.h"
+
+
+#ifndef NETLINK_CAP_ACK
+#define NETLINK_CAP_ACK 10
+#endif /* NETLINK_CAP_ACK */
+/* support for extack if compilation headers are too old */
+#ifndef NETLINK_EXT_ACK
+#define NETLINK_EXT_ACK 11
+enum nlmsgerr_attrs {
+	NLMSGERR_ATTR_UNUSED,
+	NLMSGERR_ATTR_MSG,
+	NLMSGERR_ATTR_OFFS,
+	NLMSGERR_ATTR_COOKIE,
+
+	__NLMSGERR_ATTR_MAX,
+	NLMSGERR_ATTR_MAX = __NLMSGERR_ATTR_MAX - 1
+};
+#endif
+#ifndef NLM_F_CAPPED
+#define NLM_F_CAPPED 0x100
+#endif
+#ifndef NLM_F_ACK_TLVS
+#define NLM_F_ACK_TLVS 0x200
+#endif
+#ifndef SOL_NETLINK
+#define SOL_NETLINK 270
+#endif
+
+
+#ifdef ANDROID
+/* system/core/libnl_2 does not include nl_socket_set_nonblocking() */
+#undef nl_socket_set_nonblocking
+#define nl_socket_set_nonblocking(h) android_nl_socket_set_nonblocking(h)
+
+#endif /* ANDROID */
+
+
+static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
+{
+	struct nl_sock *handle;
+
+	handle = nl_socket_alloc_cb(cb);
+	if (handle == NULL) {
+		wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
+			   "callbacks (%s)", dbg);
+		return NULL;
+	}
+
+	if (genl_connect(handle)) {
+		wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic "
+			   "netlink (%s)", dbg);
+		nl_socket_free(handle);
+		return NULL;
+	}
+
+	return handle;
+}
+
+
+static void nl_destroy_handles(struct nl_sock **handle)
+{
+	if (*handle == NULL)
+		return;
+	nl_socket_free(*handle);
+	*handle = NULL;
+}
+
+
+#if __WORDSIZE == 64
+#define ELOOP_SOCKET_INVALID	(intptr_t) 0x8888888888888889ULL
+#else
+#define ELOOP_SOCKET_INVALID	(intptr_t) 0x88888889ULL
+#endif
+
+static void nl80211_register_eloop_read(struct nl_sock **handle,
+					eloop_sock_handler handler,
+					void *eloop_data, int persist)
+{
+	/*
+	 * libnl uses a pretty small buffer (32 kB that gets converted to 64 kB)
+	 * by default. It is possible to hit that limit in some cases where
+	 * operations are blocked, e.g., with a burst of Deauthentication frames
+	 * to hostapd and STA entry deletion. Try to increase the buffer to make
+	 * this less likely to occur.
+	 */
+	int err;
+
+	err = nl_socket_set_buffer_size(*handle, 262144, 0);
+	if (err < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Could not set nl_socket RX buffer size: %s",
+			   nl_geterror(err));
+		/* continue anyway with the default (smaller) buffer */
+	}
+
+	nl_socket_set_nonblocking(*handle);
+	eloop_register_read_sock(nl_socket_get_fd(*handle), handler,
+				 eloop_data, *handle);
+	if (!persist)
+		*handle = (void *) (((intptr_t) *handle) ^
+				    ELOOP_SOCKET_INVALID);
+}
+
+
+static void nl80211_destroy_eloop_handle(struct nl_sock **handle, int persist)
+{
+	if (!persist)
+		*handle = (void *) (((intptr_t) *handle) ^
+				    ELOOP_SOCKET_INVALID);
+	eloop_unregister_read_sock(nl_socket_get_fd(*handle));
+	nl_destroy_handles(handle);
+}
+
+
+static void nl80211_global_deinit(void *priv);
+static void nl80211_check_global(struct nl80211_global *global);
+
+static void wpa_driver_nl80211_deinit(struct i802_bss *bss);
+static int wpa_driver_nl80211_set_mode_ibss(struct i802_bss *bss,
+					    struct hostapd_freq_params *freq);
+
+static int
+wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv,
+				   const u8 *set_addr, int first,
+				   const char *driver_params);
+static int nl80211_send_frame_cmd(struct i802_bss *bss,
+				  unsigned int freq, unsigned int wait,
+				  const u8 *buf, size_t buf_len,
+				  int save_cookie,
+				  int no_cck, int no_ack, int offchanok,
+				  const u16 *csa_offs, size_t csa_offs_len);
+static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss,
+					       int report);
+
+#define IFIDX_ANY -1
+
+static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
+		      int ifidx_reason);
+static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
+		      int ifidx_reason);
+static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
+		      int ifidx_reason);
+
+static int nl80211_set_channel(struct i802_bss *bss,
+			       struct hostapd_freq_params *freq, int set_chan);
+static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
+				     int ifindex, int disabled);
+
+static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv,
+			      int reset_mode);
+
+static int i802_set_iface_flags(struct i802_bss *bss, int up);
+static int nl80211_set_param(void *priv, const char *param);
+#ifdef CONFIG_MESH
+static int nl80211_put_mesh_config(struct nl_msg *msg,
+				   struct wpa_driver_mesh_bss_params *params);
+#endif /* CONFIG_MESH */
+static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
+			     u16 reason);
+
+
+/* Converts nl80211_chan_width to a common format */
+enum chan_width convert2width(int width)
+{
+	switch (width) {
+	case NL80211_CHAN_WIDTH_20_NOHT:
+		return CHAN_WIDTH_20_NOHT;
+	case NL80211_CHAN_WIDTH_20:
+		return CHAN_WIDTH_20;
+	case NL80211_CHAN_WIDTH_40:
+		return CHAN_WIDTH_40;
+	case NL80211_CHAN_WIDTH_80:
+		return CHAN_WIDTH_80;
+	case NL80211_CHAN_WIDTH_80P80:
+		return CHAN_WIDTH_80P80;
+	case NL80211_CHAN_WIDTH_160:
+		return CHAN_WIDTH_160;
+	case NL80211_CHAN_WIDTH_320:
+		return CHAN_WIDTH_320;
+	default:
+		return CHAN_WIDTH_UNKNOWN;
+	}
+}
+
+
+int is_ap_interface(enum nl80211_iftype nlmode)
+{
+	return nlmode == NL80211_IFTYPE_AP ||
+		nlmode == NL80211_IFTYPE_P2P_GO;
+}
+
+
+int is_sta_interface(enum nl80211_iftype nlmode)
+{
+	return nlmode == NL80211_IFTYPE_STATION ||
+		nlmode == NL80211_IFTYPE_P2P_CLIENT;
+}
+
+
+static int is_p2p_net_interface(enum nl80211_iftype nlmode)
+{
+	return nlmode == NL80211_IFTYPE_P2P_CLIENT ||
+		nlmode == NL80211_IFTYPE_P2P_GO;
+}
+
+
+struct i802_bss * get_bss_ifindex(struct wpa_driver_nl80211_data *drv,
+				  int ifindex)
+{
+	struct i802_bss *bss;
+
+	for (bss = drv->first_bss; bss; bss = bss->next) {
+		if (bss->ifindex == ifindex)
+			return bss;
+	}
+
+	return NULL;
+}
+
+
+static int is_mesh_interface(enum nl80211_iftype nlmode)
+{
+	return nlmode == NL80211_IFTYPE_MESH_POINT;
+}
+
+
+void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv)
+{
+	if (drv->associated)
+		os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN);
+	drv->associated = 0;
+	os_memset(&drv->sta_mlo_info, 0, sizeof(drv->sta_mlo_info));
+	os_memset(drv->bssid, 0, ETH_ALEN);
+	drv->first_bss->flink->freq = 0;
+#ifdef CONFIG_DRIVER_NL80211_QCA
+	os_free(drv->pending_roam_data);
+	drv->pending_roam_data = NULL;
+	os_free(drv->pending_t2lm_data);
+	drv->pending_t2lm_data = NULL;
+	os_free(drv->pending_link_reconfig_data);
+	drv->pending_link_reconfig_data = NULL;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+	drv->auth_mld = false;
+	drv->auth_mld_link_id = -1;
+	os_memset(drv->auth_ap_mld_addr, 0, ETH_ALEN);
+}
+
+
+/* nl80211 code */
+static int ack_handler(struct nl_msg *msg, void *arg)
+{
+	int *err = arg;
+	*err = 0;
+	return NL_STOP;
+}
+
+
+struct nl80211_ack_ext_arg {
+	int *err;
+	void *ext_data;
+};
+
+
+static int ack_handler_cookie(struct nl_msg *msg, void *arg)
+{
+	struct nl80211_ack_ext_arg *ext_arg = arg;
+	struct nlattr *tb[NLMSGERR_ATTR_MAX + 1];
+	u64 *cookie = ext_arg->ext_data;
+	struct nlattr *attrs;
+	size_t ack_len, attr_len;
+
+	*ext_arg->err = 0;
+	ack_len = sizeof(struct nlmsghdr) + sizeof(int) +
+		sizeof(struct nlmsghdr);
+	attrs = (struct nlattr *)
+		((u8 *) nlmsg_data(nlmsg_hdr(msg)) + sizeof(struct nlmsghdr) +
+		 sizeof(int));
+	if (nlmsg_hdr(msg)->nlmsg_len <= ack_len)
+		return NL_STOP;
+
+	attr_len = nlmsg_hdr(msg)->nlmsg_len - ack_len;
+
+	if(!(nlmsg_hdr(msg)->nlmsg_flags & NLM_F_ACK_TLVS))
+		return NL_STOP;
+
+	nla_parse(tb, NLMSGERR_ATTR_MAX, attrs, attr_len, NULL);
+	if (tb[NLMSGERR_ATTR_COOKIE])
+		*cookie = nla_get_u64(tb[NLMSGERR_ATTR_COOKIE]);
+
+	return NL_STOP;
+}
+
+
+static int finish_handler(struct nl_msg *msg, void *arg)
+{
+	int *ret = arg;
+	*ret = 0;
+	return NL_SKIP;
+}
+
+static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
+			 void *arg)
+{
+	struct nlmsghdr *nlh = (struct nlmsghdr *) err - 1;
+	int len = nlh->nlmsg_len;
+	struct nlattr *attrs;
+	struct nlattr *tb[NLMSGERR_ATTR_MAX + 1];
+	int *ret = arg;
+	int ack_len = sizeof(*nlh) + sizeof(int) + sizeof(*nlh);
+
+	*ret = err->error;
+
+	if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS))
+		return NL_SKIP;
+
+	if (!(nlh->nlmsg_flags & NLM_F_CAPPED))
+		ack_len += err->msg.nlmsg_len - sizeof(*nlh);
+
+	if (len <= ack_len)
+		return NL_STOP;
+
+	attrs = (void *) ((unsigned char *) nlh + ack_len);
+	len -= ack_len;
+
+	nla_parse(tb, NLMSGERR_ATTR_MAX, attrs, len, NULL);
+	if (tb[NLMSGERR_ATTR_MSG]) {
+		len = strnlen((char *) nla_data(tb[NLMSGERR_ATTR_MSG]),
+			      nla_len(tb[NLMSGERR_ATTR_MSG]));
+		wpa_printf(MSG_ERROR, "nl80211: kernel reports: %*s",
+			   len, (char *) nla_data(tb[NLMSGERR_ATTR_MSG]));
+	}
+
+	return NL_SKIP;
+}
+
+
+static int no_seq_check(struct nl_msg *msg, void *arg)
+{
+	return NL_OK;
+}
+
+
+static void nl80211_nlmsg_clear(struct nl_msg *msg)
+{
+	/*
+	 * Clear nlmsg data, e.g., to make sure key material is not left in
+	 * heap memory for unnecessarily long time.
+	 */
+	if (msg) {
+		struct nlmsghdr *hdr = nlmsg_hdr(msg);
+		void *data = nlmsg_data(hdr);
+		/*
+		 * This would use nlmsg_datalen() or the older nlmsg_len() if
+		 * only libnl were to maintain a stable API.. Neither will work
+		 * with all released versions, so just calculate the length
+		 * here.
+		 */
+		int len = hdr->nlmsg_len - NLMSG_HDRLEN;
+
+		os_memset(data, 0, len);
+	}
+}
+
+
+static int send_and_recv(struct nl80211_global *global,
+			 struct nl_sock *nl_handle, struct nl_msg *msg,
+			 int (*valid_handler)(struct nl_msg *, void *),
+			 void *valid_data,
+			 int (*ack_handler_custom)(struct nl_msg *, void *),
+			 void *ack_data)
+{
+	struct nl_cb *cb;
+	int err = -ENOMEM, opt;
+
+	if (!msg)
+		return -ENOMEM;
+
+	cb = nl_cb_clone(global->nl_cb);
+	if (!cb)
+		goto out;
+
+	/* try to set NETLINK_EXT_ACK to 1, ignoring errors */
+	opt = 1;
+	setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK,
+		   NETLINK_EXT_ACK, &opt, sizeof(opt));
+
+	/* try to set NETLINK_CAP_ACK to 1, ignoring errors */
+	opt = 1;
+	setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK,
+		   NETLINK_CAP_ACK, &opt, sizeof(opt));
+
+	err = nl_send_auto_complete(nl_handle, msg);
+	if (err < 0) {
+		wpa_printf(MSG_INFO,
+			   "nl80211: nl_send_auto_complete() failed: %s",
+			   nl_geterror(err));
+		/* Need to convert libnl error code to an errno value. For now,
+		 * just hardcode this to EBADF; the real error reason is shown
+		 * in that error print above. */
+		err = -EBADF;
+		goto out;
+	}
+
+	err = 1;
+
+	nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
+	nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);
+	if (ack_handler_custom) {
+		struct nl80211_ack_ext_arg *ext_arg = ack_data;
+
+		ext_arg->err = &err;
+		nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM,
+			  ack_handler_custom, ack_data);
+	} else {
+		nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
+	}
+
+	if (valid_handler)
+		nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM,
+			  valid_handler, valid_data);
+
+	while (err > 0) {
+		int res = nl_recvmsgs(nl_handle, cb);
+
+		if (res == -NLE_DUMP_INTR) {
+			/* Most likely one of the nl80211 dump routines hit a
+			 * case where internal results changed while the dump
+			 * was being sent. The most common known case for this
+			 * is scan results fetching while associated were every
+			 * received Beacon frame from the AP may end up
+			 * incrementing bss_generation. This
+			 * NL80211_CMD_GET_SCAN case tries again in the caller;
+			 * other cases (of which there are no known common ones)
+			 * will stop and return an error. */
+			wpa_printf(MSG_DEBUG, "nl80211: %s; convert to -EAGAIN",
+				   nl_geterror(res));
+			err = -EAGAIN;
+		} else if (res < 0) {
+			wpa_printf(MSG_INFO,
+				   "nl80211: %s->nl_recvmsgs failed: %d (%s)",
+				   __func__, res, nl_geterror(res));
+		}
+	}
+ out:
+	nl_cb_put(cb);
+	/* Always clear the message as it can potentially contain keys */
+	nl80211_nlmsg_clear(msg);
+	nlmsg_free(msg);
+	return err;
+}
+
+
+int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv,
+		       struct nl_msg *msg,
+		       int (*valid_handler)(struct nl_msg *, void *),
+		       void *valid_data,
+		       int (*ack_handler_custom)(struct nl_msg *, void *),
+		       void *ack_data)
+{
+	return send_and_recv(drv->global, drv->global->nl, msg,
+			     valid_handler, valid_data,
+			     ack_handler_custom, ack_data);
+}
+
+
+/* Use this method to mark that it is necessary to own the connection/interface
+ * for this operation.
+ * handle may be set to NULL, to get the same behavior as send_and_recv_msgs().
+ * set_owner can be used to mark this socket for receiving control port frames.
+ */
+static int send_and_recv_msgs_owner(struct wpa_driver_nl80211_data *drv,
+				    struct nl_msg *msg,
+				    struct nl_sock *handle, int set_owner,
+				    int (*valid_handler)(struct nl_msg *,
+							 void *),
+				    void *valid_data,
+				    int (*ack_handler_custom)(struct nl_msg *,
+							      void *),
+				    void *ack_data)
+{
+	if (!msg)
+		return -ENOMEM;
+
+	/* Control port over nl80211 needs the flags and attributes below.
+	 *
+	 * The Linux kernel has initial checks for them (in nl80211.c) like:
+	 *     validate_pae_over_nl80211(...)
+	 * or final checks like:
+	 *     dev->ieee80211_ptr->conn_owner_nlportid != info->snd_portid
+	 *
+	 * Final operations (e.g., disassociate) don't need to set these
+	 * attributes, but they have to be performed on the socket, which has
+	 * the connection owner property set in the kernel.
+	 */
+	if ((drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) &&
+	    handle && set_owner &&
+	    (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_OVER_NL80211) ||
+	     nla_put_flag(msg, NL80211_ATTR_SOCKET_OWNER) ||
+	     nla_put_u16(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, ETH_P_PAE) ||
+	     nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_PREAUTH)))
+		return -1;
+
+	return send_and_recv(drv->global, handle ? handle : drv->global->nl,
+			     msg, valid_handler, valid_data,
+			     ack_handler_custom, ack_data);
+}
+
+
+static int
+send_and_recv_msgs_connect_handle(struct wpa_driver_nl80211_data *drv,
+				  struct nl_msg *msg, struct i802_bss *bss,
+				  int set_owner)
+{
+	struct nl_sock *nl_connect = get_connect_handle(bss);
+
+	if (nl_connect)
+		return send_and_recv_msgs_owner(drv, msg, nl_connect, set_owner,
+						process_bss_event, bss, NULL,
+						NULL);
+	else
+		return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+}
+
+
+struct nl_sock * get_connect_handle(struct i802_bss *bss)
+{
+	if ((bss->drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) ||
+	    bss->use_nl_connect)
+		return bss->nl_connect;
+
+	return NULL;
+}
+
+
+struct family_data {
+	const char *group;
+	int id;
+};
+
+
+static int family_handler(struct nl_msg *msg, void *arg)
+{
+	struct family_data *res = arg;
+	struct nlattr *tb[CTRL_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *mcgrp;
+	int i;
+
+	nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+	if (!tb[CTRL_ATTR_MCAST_GROUPS])
+		return NL_SKIP;
+
+	nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], i) {
+		struct nlattr *tb2[CTRL_ATTR_MCAST_GRP_MAX + 1];
+		nla_parse(tb2, CTRL_ATTR_MCAST_GRP_MAX, nla_data(mcgrp),
+			  nla_len(mcgrp), NULL);
+		if (!tb2[CTRL_ATTR_MCAST_GRP_NAME] ||
+		    !tb2[CTRL_ATTR_MCAST_GRP_ID] ||
+		    os_strncmp(nla_data(tb2[CTRL_ATTR_MCAST_GRP_NAME]),
+			       res->group,
+			       nla_len(tb2[CTRL_ATTR_MCAST_GRP_NAME])) != 0)
+			continue;
+		res->id = nla_get_u32(tb2[CTRL_ATTR_MCAST_GRP_ID]);
+		break;
+	};
+
+	return NL_SKIP;
+}
+
+
+static int nl_get_multicast_id(struct nl80211_global *global,
+			       const char *family, const char *group)
+{
+	struct nl_msg *msg;
+	int ret;
+	struct family_data res = { group, -ENOENT };
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return -ENOMEM;
+	if (!genlmsg_put(msg, 0, 0, global->nlctrl_id,
+			 0, 0, CTRL_CMD_GETFAMILY, 0) ||
+	    nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, family)) {
+		nlmsg_free(msg);
+		return -1;
+	}
+
+	ret = send_and_recv(global, global->nl, msg, family_handler, &res,
+			    NULL, NULL);
+	if (ret == 0)
+		ret = res.id;
+	return ret;
+}
+
+
+void * nl80211_cmd(struct wpa_driver_nl80211_data *drv,
+		   struct nl_msg *msg, int flags, uint8_t cmd)
+{
+	if (TEST_FAIL())
+		return NULL;
+	return genlmsg_put(msg, 0, 0, drv->global->nl80211_id,
+			   0, flags, cmd, 0);
+}
+
+
+static int nl80211_set_iface_id(struct nl_msg *msg, struct i802_bss *bss)
+{
+	if (bss->wdev_id_set)
+		return nla_put_u64(msg, NL80211_ATTR_WDEV, bss->wdev_id);
+	return nla_put_u32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
+}
+
+
+struct nl_msg * nl80211_cmd_msg(struct i802_bss *bss, int flags, uint8_t cmd)
+{
+	struct nl_msg *msg;
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return NULL;
+
+	if (!nl80211_cmd(bss->drv, msg, flags, cmd) ||
+	    nl80211_set_iface_id(msg, bss) < 0) {
+		nlmsg_free(msg);
+		return NULL;
+	}
+
+	return msg;
+}
+
+
+static struct nl_msg *
+nl80211_ifindex_msg_build(struct wpa_driver_nl80211_data *drv,
+			  struct nl_msg *msg, int ifindex, int flags,
+			  uint8_t cmd)
+{
+	if (!msg)
+		return NULL;
+
+	if (!nl80211_cmd(drv, msg, flags, cmd) ||
+	    nla_put_u32(msg, NL80211_ATTR_IFINDEX, ifindex)) {
+		nlmsg_free(msg);
+		return NULL;
+	}
+
+	return msg;
+}
+
+
+static struct nl_msg *
+nl80211_ifindex_msg(struct wpa_driver_nl80211_data *drv, int ifindex,
+		    int flags, uint8_t cmd)
+{
+	return nl80211_ifindex_msg_build(drv, nlmsg_alloc(), ifindex, flags,
+					 cmd);
+}
+
+
+struct nl_msg * nl80211_drv_msg(struct wpa_driver_nl80211_data *drv, int flags,
+				uint8_t cmd)
+{
+	return nl80211_ifindex_msg(drv, drv->ifindex, flags, cmd);
+}
+
+
+struct nl_msg * nl80211_bss_msg(struct i802_bss *bss, int flags, uint8_t cmd)
+{
+	return nl80211_ifindex_msg(bss->drv, bss->ifindex, flags, cmd);
+}
+
+
+struct wiphy_idx_data {
+	int wiphy_idx;
+	enum nl80211_iftype nlmode;
+	u8 *macaddr;
+	u8 use_4addr;
+};
+
+
+static int netdev_info_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct wiphy_idx_data *info = arg;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (tb[NL80211_ATTR_WIPHY])
+		info->wiphy_idx = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
+
+	if (tb[NL80211_ATTR_IFTYPE])
+		info->nlmode = nla_get_u32(tb[NL80211_ATTR_IFTYPE]);
+
+	if (tb[NL80211_ATTR_MAC] && info->macaddr)
+		os_memcpy(info->macaddr, nla_data(tb[NL80211_ATTR_MAC]),
+			  ETH_ALEN);
+
+	if (tb[NL80211_ATTR_4ADDR])
+		info->use_4addr = nla_get_u8(tb[NL80211_ATTR_4ADDR]);
+
+	return NL_SKIP;
+}
+
+
+int nl80211_get_wiphy_index(struct i802_bss *bss)
+{
+	struct nl_msg *msg;
+	struct wiphy_idx_data data = {
+		.wiphy_idx = -1,
+		.macaddr = NULL,
+	};
+
+	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE)))
+		return -1;
+
+	if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data,
+			       NULL, NULL) == 0)
+		return data.wiphy_idx;
+	return -1;
+}
+
+
+static enum nl80211_iftype nl80211_get_ifmode(struct i802_bss *bss)
+{
+	struct nl_msg *msg;
+	struct wiphy_idx_data data = {
+		.nlmode = NL80211_IFTYPE_UNSPECIFIED,
+		.macaddr = NULL,
+	};
+
+	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE)))
+		return NL80211_IFTYPE_UNSPECIFIED;
+
+	if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data,
+			       NULL, NULL) == 0)
+		return data.nlmode;
+	return NL80211_IFTYPE_UNSPECIFIED;
+}
+
+
+static int nl80211_get_macaddr(struct i802_bss *bss)
+{
+	struct nl_msg *msg;
+	struct wiphy_idx_data data = {
+		.macaddr = bss->addr,
+	};
+
+	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE)))
+		return -1;
+
+	return send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data,
+				  NULL, NULL);
+}
+
+
+static int nl80211_get_4addr(struct i802_bss *bss)
+{
+	struct nl_msg *msg;
+	struct wiphy_idx_data data = {
+		.use_4addr = 0,
+	};
+
+	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE)) ||
+	    send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data,
+			       NULL, NULL))
+		return -1;
+	return data.use_4addr;
+}
+
+
+static int nl80211_register_beacons(struct wpa_driver_nl80211_data *drv,
+				    struct nl80211_wiphy_data *w)
+{
+	struct nl_msg *msg;
+	int ret;
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return -1;
+
+	if (!nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_BEACONS) ||
+	    nla_put_u32(msg, NL80211_ATTR_WIPHY, w->wiphy_idx)) {
+		nlmsg_free(msg);
+		return -1;
+	}
+
+	ret = send_and_recv(drv->global, w->nl_beacons, msg, NULL, NULL,
+			    NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Register beacons command "
+			   "failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+	}
+	return ret;
+}
+
+
+static void nl80211_recv_beacons(int sock, void *eloop_ctx, void *handle)
+{
+	struct nl80211_wiphy_data *w = eloop_ctx;
+	int res;
+
+	wpa_printf(MSG_EXCESSIVE, "nl80211: Beacon event message available");
+
+	res = nl_recvmsgs(handle, w->nl_cb);
+	if (res < 0) {
+		wpa_printf(MSG_INFO, "nl80211: %s->nl_recvmsgs failed: %d",
+			   __func__, res);
+	}
+}
+
+
+static int process_beacon_event(struct nl_msg *msg, void *arg)
+{
+	struct nl80211_wiphy_data *w = arg;
+	struct wpa_driver_nl80211_data *drv;
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	union wpa_event_data event;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (gnlh->cmd != NL80211_CMD_FRAME) {
+		wpa_printf(MSG_DEBUG, "nl80211: Unexpected beacon event? (%d)",
+			   gnlh->cmd);
+		return NL_SKIP;
+	}
+
+	if (!tb[NL80211_ATTR_FRAME])
+		return NL_SKIP;
+
+	dl_list_for_each(drv, &w->drvs, struct wpa_driver_nl80211_data,
+			 wiphy_list) {
+		os_memset(&event, 0, sizeof(event));
+		event.rx_mgmt.frame = nla_data(tb[NL80211_ATTR_FRAME]);
+		event.rx_mgmt.frame_len = nla_len(tb[NL80211_ATTR_FRAME]);
+
+		// Add RSSI and freq info for Beacon frame handling. Added by Liangyu Chu
+		if (tb[NL80211_ATTR_WIPHY_FREQ])
+			event.rx_mgmt.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
+		if (tb[NL80211_ATTR_RX_SIGNAL_DBM])
+			event.rx_mgmt.ssi_signal = (s32) nla_get_u32(tb[NL80211_ATTR_RX_SIGNAL_DBM]);
+			
+		wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
+	}
+
+	return NL_SKIP;
+}
+
+
+static struct nl80211_wiphy_data *
+nl80211_get_wiphy_data_ap(struct i802_bss *bss)
+{
+	static DEFINE_DL_LIST(nl80211_wiphys);
+	struct nl80211_wiphy_data *w;
+	int wiphy_idx, found = 0;
+	struct i802_bss *tmp_bss;
+	u8 channel;
+
+	if (bss->wiphy_data != NULL)
+		return bss->wiphy_data;
+
+	wiphy_idx = nl80211_get_wiphy_index(bss);
+
+	dl_list_for_each(w, &nl80211_wiphys, struct nl80211_wiphy_data, list) {
+		if (w->wiphy_idx == wiphy_idx)
+			goto add;
+	}
+
+	/* alloc new one */
+	w = os_zalloc(sizeof(*w));
+	if (w == NULL)
+		return NULL;
+	w->wiphy_idx = wiphy_idx;
+	dl_list_init(&w->bsss);
+	dl_list_init(&w->drvs);
+
+	/* Beacon frames not supported in IEEE 802.11ad */
+	if (ieee80211_freq_to_chan(bss->flink->freq, &channel) !=
+	    HOSTAPD_MODE_IEEE80211AD) {
+		w->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
+		if (!w->nl_cb) {
+			os_free(w);
+			return NULL;
+		}
+		nl_cb_set(w->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
+			  no_seq_check, NULL);
+		nl_cb_set(w->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
+			  process_beacon_event, w);
+
+		w->nl_beacons = nl_create_handle(bss->drv->global->nl_cb,
+						 "wiphy beacons");
+		if (w->nl_beacons == NULL) {
+			os_free(w);
+			return NULL;
+		}
+
+		if (nl80211_register_beacons(bss->drv, w)) {
+			nl_destroy_handles(&w->nl_beacons);
+			os_free(w);
+			return NULL;
+		}
+
+		nl80211_register_eloop_read(&w->nl_beacons,
+					    nl80211_recv_beacons, w, 0);
+	}
+
+	dl_list_add(&nl80211_wiphys, &w->list);
+
+add:
+	/* drv entry for this bss already there? */
+	dl_list_for_each(tmp_bss, &w->bsss, struct i802_bss, wiphy_list) {
+		if (tmp_bss->drv == bss->drv) {
+			found = 1;
+			break;
+		}
+	}
+	/* if not add it */
+	if (!found)
+		dl_list_add(&w->drvs, &bss->drv->wiphy_list);
+
+	dl_list_add(&w->bsss, &bss->wiphy_list);
+	bss->wiphy_data = w;
+	return w;
+}
+
+
+static void nl80211_put_wiphy_data_ap(struct i802_bss *bss)
+{
+	struct nl80211_wiphy_data *w = bss->wiphy_data;
+	struct i802_bss *tmp_bss;
+	int found = 0;
+
+	if (w == NULL)
+		return;
+	bss->wiphy_data = NULL;
+	dl_list_del(&bss->wiphy_list);
+
+	/* still any for this drv present? */
+	dl_list_for_each(tmp_bss, &w->bsss, struct i802_bss, wiphy_list) {
+		if (tmp_bss->drv == bss->drv) {
+			found = 1;
+			break;
+		}
+	}
+	/* if not remove it */
+	if (!found)
+		dl_list_del(&bss->drv->wiphy_list);
+
+	if (!dl_list_empty(&w->bsss))
+		return;
+
+	if (w->nl_beacons)
+		nl80211_destroy_eloop_handle(&w->nl_beacons, 0);
+
+	nl_cb_put(w->nl_cb);
+	dl_list_del(&w->list);
+	os_free(w);
+}
+
+
+static unsigned int nl80211_get_ifindex(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	return drv->ifindex;
+}
+
+
+static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	if (!drv->associated)
+		return -1;
+	os_memcpy(bssid, drv->bssid, ETH_ALEN);
+	return 0;
+}
+
+
+static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	if (!drv->associated)
+		return -1;
+	os_memcpy(ssid, drv->ssid, drv->ssid_len);
+	return drv->ssid_len;
+}
+
+
+static int get_mlo_info(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *link_attr, *link_data[NL80211_ATTR_MAX + 1];
+	static struct nla_policy link_policy[NL80211_ATTR_MAX + 1] = {
+		[NL80211_ATTR_MLO_LINK_ID] = { .type = NLA_U8 },
+		[NL80211_ATTR_MAC] = { .minlen = ETH_ALEN, .maxlen = ETH_ALEN },
+	};
+	struct driver_sta_mlo_info *info = arg;
+	int rem;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (!tb[NL80211_ATTR_MLO_LINKS])
+		return NL_SKIP;
+
+	info->valid_links = 0;
+	nla_for_each_nested(link_attr, tb[NL80211_ATTR_MLO_LINKS], rem) {
+		u8 link_id;
+
+		if (nla_parse_nested(link_data, NL80211_ATTR_MAX,
+				     link_attr, link_policy) != 0)
+			continue;
+
+		if (!link_data[NL80211_ATTR_MLO_LINK_ID] ||
+		    !link_data[NL80211_ATTR_MAC])
+			continue;
+
+		link_id = nla_get_u8(link_data[NL80211_ATTR_MLO_LINK_ID]);
+		if (link_id >= MAX_NUM_MLD_LINKS)
+			continue;
+		info->valid_links |= BIT(link_id);
+		os_memcpy(info->links[link_id].addr,
+			  nla_data(link_data[NL80211_ATTR_MAC]), ETH_ALEN);
+		if (link_data[NL80211_ATTR_WIPHY_FREQ])
+			info->links[link_id].freq =
+				nla_get_u32(link_data[NL80211_ATTR_WIPHY_FREQ]);
+	}
+
+	return NL_SKIP;
+}
+
+
+static int nl80211_get_sta_mlo_info(void *priv,
+				    struct driver_sta_mlo_info *mlo_info)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	if (!drv->associated)
+		return -1;
+
+	if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
+		struct nl_msg *msg;
+
+		msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
+		if (send_and_recv_msgs(drv, msg, get_mlo_info,
+				       &drv->sta_mlo_info, NULL, NULL))
+			return -1;
+	}
+
+	os_memcpy(mlo_info, &drv->sta_mlo_info, sizeof(*mlo_info));
+	return 0;
+}
+
+
+static void wpa_driver_nl80211_event_newlink(
+	struct nl80211_global *global, struct wpa_driver_nl80211_data *drv,
+	int ifindex, const char *ifname)
+{
+	union wpa_event_data event;
+
+	if (drv && os_strcmp(drv->first_bss->ifname, ifname) == 0) {
+		if (if_nametoindex(drv->first_bss->ifname) == 0) {
+			wpa_printf(MSG_DEBUG, "nl80211: Interface %s does not exist - ignore RTM_NEWLINK",
+				   drv->first_bss->ifname);
+			return;
+		}
+		if (!drv->if_removed)
+			return;
+		wpa_printf(MSG_DEBUG, "nl80211: Mark if_removed=0 for %s based on RTM_NEWLINK event",
+			   drv->first_bss->ifname);
+		drv->if_removed = 0;
+	}
+
+	os_memset(&event, 0, sizeof(event));
+	event.interface_status.ifindex = ifindex;
+	os_strlcpy(event.interface_status.ifname, ifname,
+		   sizeof(event.interface_status.ifname));
+	event.interface_status.ievent = EVENT_INTERFACE_ADDED;
+	if (drv)
+		wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+	else
+		wpa_supplicant_event_global(global->ctx, EVENT_INTERFACE_STATUS,
+					    &event);
+}
+
+
+static void wpa_driver_nl80211_event_dellink(
+	struct nl80211_global *global, struct wpa_driver_nl80211_data *drv,
+	int ifindex, const char *ifname)
+{
+	union wpa_event_data event;
+
+	if (drv && os_strcmp(drv->first_bss->ifname, ifname) == 0) {
+		if (drv->if_removed) {
+			wpa_printf(MSG_DEBUG, "nl80211: if_removed already set - ignore RTM_DELLINK event for %s",
+				   ifname);
+			return;
+		}
+		wpa_printf(MSG_DEBUG, "RTM_DELLINK: Interface '%s' removed - mark if_removed=1",
+			   ifname);
+		drv->if_removed = 1;
+	} else {
+		wpa_printf(MSG_DEBUG, "RTM_DELLINK: Interface '%s' removed",
+			   ifname);
+	}
+
+	os_memset(&event, 0, sizeof(event));
+	event.interface_status.ifindex = ifindex;
+	os_strlcpy(event.interface_status.ifname, ifname,
+		   sizeof(event.interface_status.ifname));
+	event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
+	if (drv)
+		wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+	else
+		wpa_supplicant_event_global(global->ctx, EVENT_INTERFACE_STATUS,
+					    &event);
+}
+
+
+static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv,
+					 u8 *buf, size_t len)
+{
+	int attrlen, rta_len;
+	struct rtattr *attr;
+
+	attrlen = len;
+	attr = (struct rtattr *) buf;
+
+	rta_len = RTA_ALIGN(sizeof(struct rtattr));
+	while (RTA_OK(attr, attrlen)) {
+		if (attr->rta_type == IFLA_IFNAME) {
+			if (os_strcmp(((char *) attr) + rta_len,
+				      drv->first_bss->ifname) == 0)
+				return 1;
+			else
+				break;
+		}
+		attr = RTA_NEXT(attr, attrlen);
+	}
+
+	return 0;
+}
+
+
+static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv,
+					  int ifindex, u8 *buf, size_t len)
+{
+	if (drv->ifindex == ifindex)
+		return 1;
+
+	if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, buf, len)) {
+		nl80211_check_global(drv->global);
+		wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed "
+			   "interface");
+		if (wpa_driver_nl80211_finish_drv_init(drv, NULL, 0, NULL) < 0)
+			return -1;
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static struct wpa_driver_nl80211_data *
+nl80211_find_drv(struct nl80211_global *global, int idx, u8 *buf, size_t len,
+		 int *init_failed)
+{
+	struct wpa_driver_nl80211_data *drv;
+	int res;
+
+	if (init_failed)
+		*init_failed = 0;
+	dl_list_for_each(drv, &global->interfaces,
+			 struct wpa_driver_nl80211_data, list) {
+		res = wpa_driver_nl80211_own_ifindex(drv, idx, buf, len);
+		if (res < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Found matching own interface, but failed to complete reinitialization");
+			if (init_failed)
+				*init_failed = 1;
+			return drv;
+		}
+		if (res > 0 || have_ifidx(drv, idx, IFIDX_ANY))
+			return drv;
+	}
+	return NULL;
+}
+
+
+static void nl80211_refresh_mac(struct wpa_driver_nl80211_data *drv,
+				int ifindex, int notify)
+{
+	struct i802_bss *bss;
+	u8 addr[ETH_ALEN];
+
+	bss = get_bss_ifindex(drv, ifindex);
+	if (bss &&
+	    linux_get_ifhwaddr(drv->global->ioctl_sock,
+			       bss->ifname, addr) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: %s: failed to re-read MAC address",
+			   bss->ifname);
+	} else if (bss && os_memcmp(addr, bss->addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Own MAC address on ifindex %d (%s) changed from "
+			   MACSTR " to " MACSTR,
+			   ifindex, bss->ifname,
+			   MAC2STR(bss->addr), MAC2STR(addr));
+		os_memcpy(bss->prev_addr, bss->addr, ETH_ALEN);
+		os_memcpy(bss->addr, addr, ETH_ALEN);
+		if (notify)
+			wpa_supplicant_event(drv->ctx,
+					     EVENT_INTERFACE_MAC_CHANGED, NULL);
+	}
+}
+
+
+static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
+						 struct ifinfomsg *ifi,
+						 u8 *buf, size_t len)
+{
+	struct nl80211_global *global = ctx;
+	struct wpa_driver_nl80211_data *drv;
+	int attrlen;
+	struct rtattr *attr;
+	u32 brid = 0;
+	char namebuf[IFNAMSIZ];
+	char ifname[IFNAMSIZ + 1];
+	char extra[100], *pos, *end;
+	int init_failed;
+
+	extra[0] = '\0';
+	pos = extra;
+	end = pos + sizeof(extra);
+	ifname[0] = '\0';
+
+	attrlen = len;
+	attr = (struct rtattr *) buf;
+	while (RTA_OK(attr, attrlen)) {
+		switch (attr->rta_type) {
+		case IFLA_IFNAME:
+			if (RTA_PAYLOAD(attr) > IFNAMSIZ)
+				break;
+			os_memcpy(ifname, RTA_DATA(attr), RTA_PAYLOAD(attr));
+			ifname[RTA_PAYLOAD(attr)] = '\0';
+			break;
+		case IFLA_MASTER:
+			brid = nla_get_u32((struct nlattr *) attr);
+			pos += os_snprintf(pos, end - pos, " master=%u", brid);
+			break;
+		case IFLA_WIRELESS:
+			pos += os_snprintf(pos, end - pos, " wext");
+			break;
+		case IFLA_OPERSTATE:
+			pos += os_snprintf(pos, end - pos, " operstate=%u",
+					   nla_get_u32((struct nlattr *) attr));
+			break;
+		case IFLA_LINKMODE:
+			pos += os_snprintf(pos, end - pos, " linkmode=%u",
+					   nla_get_u32((struct nlattr *) attr));
+			break;
+		}
+		attr = RTA_NEXT(attr, attrlen);
+	}
+	extra[sizeof(extra) - 1] = '\0';
+
+	wpa_printf(MSG_DEBUG, "RTM_NEWLINK: ifi_index=%d ifname=%s%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)",
+		   ifi->ifi_index, ifname, extra, ifi->ifi_family,
+		   ifi->ifi_flags,
+		   (ifi->ifi_flags & IFF_UP) ? "[UP]" : "",
+		   (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
+		   (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
+		   (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
+
+	drv = nl80211_find_drv(global, ifi->ifi_index, buf, len, &init_failed);
+	if (!drv)
+		goto event_newlink;
+	if (init_failed)
+		return; /* do not update interface state */
+
+	if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) {
+		namebuf[0] = '\0';
+		if (if_indextoname(ifi->ifi_index, namebuf) &&
+		    linux_iface_up(drv->global->ioctl_sock, namebuf) > 0) {
+			wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
+				   "event since interface %s is up", namebuf);
+			drv->ignore_if_down_event = 0;
+			/* Re-read MAC address as it may have changed */
+			nl80211_refresh_mac(drv, ifi->ifi_index, 1);
+			return;
+		}
+		wpa_printf(MSG_DEBUG, "nl80211: Interface down (%s/%s)",
+			   namebuf, ifname);
+		if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Not the main interface (%s) - do not indicate interface down",
+				   drv->first_bss->ifname);
+		} else if (drv->ignore_if_down_event) {
+			wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
+				   "event generated by mode change");
+			drv->ignore_if_down_event = 0;
+		} else {
+			drv->if_disabled = 1;
+			wpa_supplicant_event(drv->ctx,
+					     EVENT_INTERFACE_DISABLED, NULL);
+
+			/*
+			 * Try to get drv again, since it may be removed as
+			 * part of the EVENT_INTERFACE_DISABLED handling for
+			 * dynamic interfaces
+			 */
+			drv = nl80211_find_drv(global, ifi->ifi_index,
+					       buf, len, NULL);
+			if (!drv)
+				return;
+		}
+	}
+
+	if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) {
+		namebuf[0] = '\0';
+		if (if_indextoname(ifi->ifi_index, namebuf) &&
+		    linux_iface_up(drv->global->ioctl_sock, namebuf) == 0) {
+			wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
+				   "event since interface %s is down",
+				   namebuf);
+			return;
+		}
+		wpa_printf(MSG_DEBUG, "nl80211: Interface up (%s/%s)",
+			   namebuf, ifname);
+		if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Not the main interface (%s) - do not indicate interface up",
+				   drv->first_bss->ifname);
+		} else if (if_nametoindex(drv->first_bss->ifname) == 0) {
+			wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
+				   "event since interface %s does not exist",
+				   drv->first_bss->ifname);
+		} else if (drv->if_removed) {
+			wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
+				   "event since interface %s is marked "
+				   "removed", drv->first_bss->ifname);
+		} else {
+			/* Re-read MAC address as it may have changed */
+			nl80211_refresh_mac(drv, ifi->ifi_index, 0);
+
+			drv->if_disabled = 0;
+			wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED,
+					     NULL);
+		}
+	}
+
+	/*
+	 * Some drivers send the association event before the operup event--in
+	 * this case, lifting operstate in wpa_driver_nl80211_set_operstate()
+	 * fails. This will hit us when wpa_supplicant does not need to do
+	 * IEEE 802.1X authentication
+	 */
+	if (drv->operstate == 1 &&
+	    (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP &&
+	    !(ifi->ifi_flags & IFF_RUNNING)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Set IF_OPER_UP again based on ifi_flags and expected operstate");
+		netlink_send_oper_ifla(drv->global->netlink, drv->ifindex,
+				       -1, IF_OPER_UP);
+	}
+
+event_newlink:
+	if (ifname[0])
+		wpa_driver_nl80211_event_newlink(global, drv, ifi->ifi_index,
+						 ifname);
+
+	if (ifi->ifi_family == AF_BRIDGE && brid && drv) {
+		struct i802_bss *bss;
+
+		/* device has been added to bridge */
+		if (!if_indextoname(brid, namebuf)) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Could not find bridge ifname for ifindex %u",
+				   brid);
+			return;
+		}
+		wpa_printf(MSG_DEBUG, "nl80211: Add ifindex %u for bridge %s",
+			   brid, namebuf);
+		add_ifidx(drv, brid, ifi->ifi_index);
+
+		for (bss = drv->first_bss; bss; bss = bss->next) {
+			if (os_strcmp(ifname, bss->ifname) == 0) {
+				os_strlcpy(bss->brname, namebuf, IFNAMSIZ);
+				break;
+			}
+		}
+	}
+}
+
+
+static void wpa_driver_nl80211_event_rtm_dellink(void *ctx,
+						 struct ifinfomsg *ifi,
+						 u8 *buf, size_t len)
+{
+	struct nl80211_global *global = ctx;
+	struct wpa_driver_nl80211_data *drv;
+	int attrlen;
+	struct rtattr *attr;
+	u32 brid = 0;
+	char ifname[IFNAMSIZ + 1];
+	char extra[100], *pos, *end;
+
+	extra[0] = '\0';
+	pos = extra;
+	end = pos + sizeof(extra);
+	ifname[0] = '\0';
+
+	attrlen = len;
+	attr = (struct rtattr *) buf;
+	while (RTA_OK(attr, attrlen)) {
+		switch (attr->rta_type) {
+		case IFLA_IFNAME:
+			if (RTA_PAYLOAD(attr) > IFNAMSIZ)
+				break;
+			os_memcpy(ifname, RTA_DATA(attr), RTA_PAYLOAD(attr));
+			ifname[RTA_PAYLOAD(attr)] = '\0';
+			break;
+		case IFLA_MASTER:
+			brid = nla_get_u32((struct nlattr *) attr);
+			pos += os_snprintf(pos, end - pos, " master=%u", brid);
+			break;
+		case IFLA_OPERSTATE:
+			pos += os_snprintf(pos, end - pos, " operstate=%u",
+					   nla_get_u32((struct nlattr *) attr));
+			break;
+		case IFLA_LINKMODE:
+			pos += os_snprintf(pos, end - pos, " linkmode=%u",
+					   nla_get_u32((struct nlattr *) attr));
+			break;
+		}
+		attr = RTA_NEXT(attr, attrlen);
+	}
+	extra[sizeof(extra) - 1] = '\0';
+
+	wpa_printf(MSG_DEBUG, "RTM_DELLINK: ifi_index=%d ifname=%s%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)",
+		   ifi->ifi_index, ifname, extra, ifi->ifi_family,
+		   ifi->ifi_flags,
+		   (ifi->ifi_flags & IFF_UP) ? "[UP]" : "",
+		   (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
+		   (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
+		   (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
+
+	drv = nl80211_find_drv(global, ifi->ifi_index, buf, len, NULL);
+
+	if (ifi->ifi_family == AF_BRIDGE && brid && drv) {
+		/* device has been removed from bridge */
+		char namebuf[IFNAMSIZ];
+
+		if (!if_indextoname(brid, namebuf)) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Could not find bridge ifname for ifindex %u",
+				   brid);
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Remove ifindex %u for bridge %s",
+				   brid, namebuf);
+		}
+		del_ifidx(drv, brid, ifi->ifi_index);
+	}
+
+	if (ifi->ifi_family != AF_BRIDGE || !brid)
+		wpa_driver_nl80211_event_dellink(global, drv, ifi->ifi_index,
+						 ifname);
+}
+
+
+struct nl80211_get_assoc_freq_arg {
+	struct wpa_driver_nl80211_data *drv;
+	unsigned int assoc_freq;
+	unsigned int ibss_freq;
+	u8 assoc_bssid[ETH_ALEN];
+	u8 assoc_ssid[SSID_MAX_LEN];
+	u8 assoc_ssid_len;
+	u8 bssid[MAX_NUM_MLD_LINKS][ETH_ALEN];
+	unsigned int freq[MAX_NUM_MLD_LINKS];
+};
+
+static int nl80211_get_assoc_freq_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *bss[NL80211_BSS_MAX + 1];
+	static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
+		[NL80211_BSS_BSSID] = { .type = NLA_UNSPEC },
+		[NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
+		[NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC },
+		[NL80211_BSS_STATUS] = { .type = NLA_U32 },
+		[NL80211_BSS_MLO_LINK_ID] = { .type = NLA_U8 },
+	};
+	struct nl80211_get_assoc_freq_arg *ctx = arg;
+	enum nl80211_bss_status status;
+	struct wpa_driver_nl80211_data *drv = ctx->drv;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+	if (!tb[NL80211_ATTR_BSS] ||
+	    nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
+			     bss_policy) ||
+	    !bss[NL80211_BSS_STATUS])
+		return NL_SKIP;
+
+	status = nla_get_u32(bss[NL80211_BSS_STATUS]);
+	if (status == NL80211_BSS_STATUS_ASSOCIATED &&
+	    bss[NL80211_BSS_FREQUENCY]) {
+		int link_id = -1;
+		u32 freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+
+		if (bss[NL80211_BSS_MLO_LINK_ID])
+			link_id = nla_get_u8(bss[NL80211_BSS_MLO_LINK_ID]);
+
+		if (link_id >= 0 && link_id < MAX_NUM_MLD_LINKS) {
+			ctx->freq[link_id] = freq;
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: MLO link %d associated on %u MHz",
+				   link_id, ctx->freq[link_id]);
+		}
+
+		if (!drv->sta_mlo_info.valid_links ||
+		    drv->sta_mlo_info.assoc_link_id == link_id) {
+			ctx->assoc_freq = freq;
+			wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz",
+				   ctx->assoc_freq);
+		}
+	}
+	if (status == NL80211_BSS_STATUS_IBSS_JOINED &&
+	    bss[NL80211_BSS_FREQUENCY]) {
+		ctx->ibss_freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+		wpa_printf(MSG_DEBUG, "nl80211: IBSS-joined on %u MHz",
+			   ctx->ibss_freq);
+	}
+	if (status == NL80211_BSS_STATUS_ASSOCIATED &&
+	    bss[NL80211_BSS_BSSID]) {
+		int link_id = -1;
+		const u8 *bssid = nla_data(bss[NL80211_BSS_BSSID]);
+
+		if (bss[NL80211_BSS_MLO_LINK_ID])
+			link_id = nla_get_u8(bss[NL80211_BSS_MLO_LINK_ID]);
+
+		if (link_id >= 0 && link_id < MAX_NUM_MLD_LINKS) {
+			os_memcpy(ctx->bssid[link_id], bssid, ETH_ALEN);
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: MLO link %d associated with "
+				   MACSTR, link_id, MAC2STR(bssid));
+		}
+
+		if (!drv->sta_mlo_info.valid_links ||
+		    drv->sta_mlo_info.assoc_link_id == link_id) {
+			os_memcpy(ctx->assoc_bssid, bssid, ETH_ALEN);
+			wpa_printf(MSG_DEBUG, "nl80211: Associated with "
+				   MACSTR, MAC2STR(bssid));
+		}
+
+	}
+
+	if (status == NL80211_BSS_STATUS_ASSOCIATED &&
+	    bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
+		const u8 *ie, *ssid;
+		size_t ie_len;
+
+		ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
+		ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
+		ssid = get_ie(ie, ie_len, WLAN_EID_SSID);
+		if (ssid && ssid[1] > 0 && ssid[1] <= SSID_MAX_LEN) {
+			ctx->assoc_ssid_len = ssid[1];
+			os_memcpy(ctx->assoc_ssid, ssid + 2, ssid[1]);
+		}
+	}
+
+	return NL_SKIP;
+}
+
+
+int nl80211_get_assoc_ssid(struct wpa_driver_nl80211_data *drv, u8 *ssid)
+{
+	struct nl_msg *msg;
+	int ret;
+	struct nl80211_get_assoc_freq_arg arg;
+	int count = 0;
+
+try_again:
+	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
+	os_memset(&arg, 0, sizeof(arg));
+	arg.drv = drv;
+	ret = send_and_recv_msgs(drv, msg, nl80211_get_assoc_freq_handler,
+				 &arg, NULL, NULL);
+	if (ret == -EAGAIN) {
+		count++;
+		if (count >= 10) {
+			wpa_printf(MSG_INFO,
+				   "nl80211: Failed to receive consistent scan result dump for get_assoc_ssid");
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Failed to receive consistent scan result dump for get_assoc_ssid - try again");
+			goto try_again;
+		}
+	}
+	if (ret == 0) {
+		os_memcpy(ssid, arg.assoc_ssid, arg.assoc_ssid_len);
+		return arg.assoc_ssid_len;
+	}
+	wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d (%s)",
+		   ret, strerror(-ret));
+	return ret;
+}
+
+
+unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv)
+{
+	struct nl_msg *msg;
+	int ret;
+	struct nl80211_get_assoc_freq_arg arg;
+	int count = 0;
+
+try_again:
+	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
+	os_memset(&arg, 0, sizeof(arg));
+	arg.drv = drv;
+	ret = send_and_recv_msgs(drv, msg, nl80211_get_assoc_freq_handler,
+				 &arg, NULL, NULL);
+	if (ret == -EAGAIN) {
+		count++;
+		if (count >= 10) {
+			wpa_printf(MSG_INFO,
+				   "nl80211: Failed to receive consistent scan result dump for get_assoc_freq");
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Failed to receive consistent scan result dump for get_assoc_freq - try again");
+			goto try_again;
+		}
+	}
+	if (ret == 0) {
+		unsigned int freq = drv->nlmode == NL80211_IFTYPE_ADHOC ?
+			arg.ibss_freq : arg.assoc_freq;
+		wpa_printf(MSG_DEBUG, "nl80211: Operating frequency for the "
+			   "associated BSS from scan results: %u MHz", freq);
+		if (freq)
+			drv->assoc_freq = freq;
+
+		if (drv->sta_mlo_info.valid_links) {
+			int i;
+
+			for (i = 0; i < MAX_NUM_MLD_LINKS; i++)
+				drv->sta_mlo_info.links[i].freq = arg.freq[i];
+		}
+
+		return drv->assoc_freq;
+	}
+	wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
+		   "(%s)", ret, strerror(-ret));
+	return drv->assoc_freq;
+}
+
+
+static int get_link_noise(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1];
+	static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
+		[NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
+		[NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
+	};
+	struct wpa_signal_info *sig_change = arg;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (!tb[NL80211_ATTR_SURVEY_INFO]) {
+		wpa_printf(MSG_DEBUG, "nl80211: survey data missing!");
+		return NL_SKIP;
+	}
+
+	if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
+			     tb[NL80211_ATTR_SURVEY_INFO],
+			     survey_policy)) {
+		wpa_printf(MSG_DEBUG, "nl80211: failed to parse nested "
+			   "attributes!");
+		return NL_SKIP;
+	}
+
+	if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY])
+		return NL_SKIP;
+
+	if (nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) !=
+	    sig_change->frequency)
+		return NL_SKIP;
+
+	if (!sinfo[NL80211_SURVEY_INFO_NOISE])
+		return NL_SKIP;
+
+	sig_change->current_noise =
+		(s8) nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
+
+	return NL_SKIP;
+}
+
+
+int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv,
+			   struct wpa_signal_info *sig_change)
+{
+	struct nl_msg *msg;
+
+	sig_change->current_noise = WPA_INVALID_NOISE;
+	sig_change->frequency = drv->assoc_freq;
+
+	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
+	return send_and_recv_msgs(drv, msg, get_link_noise, sig_change,
+				  NULL, NULL);
+}
+
+
+static int get_channel_info(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1] = { 0 };
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct wpa_channel_info *chan_info = arg;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	os_memset(chan_info, 0, sizeof(struct wpa_channel_info));
+	chan_info->chanwidth = CHAN_WIDTH_UNKNOWN;
+
+	if (tb[NL80211_ATTR_WIPHY_FREQ])
+		chan_info->frequency =
+			nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
+	if (tb[NL80211_ATTR_CHANNEL_WIDTH])
+		chan_info->chanwidth = convert2width(
+			nla_get_u32(tb[NL80211_ATTR_CHANNEL_WIDTH]));
+	if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
+		enum nl80211_channel_type ct =
+			nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+
+		switch (ct) {
+		case NL80211_CHAN_HT40MINUS:
+			chan_info->sec_channel = -1;
+			break;
+		case NL80211_CHAN_HT40PLUS:
+			chan_info->sec_channel = 1;
+			break;
+		default:
+			chan_info->sec_channel = 0;
+			break;
+		}
+	}
+	if (tb[NL80211_ATTR_CENTER_FREQ1])
+		chan_info->center_frq1 =
+			nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]);
+	if (tb[NL80211_ATTR_CENTER_FREQ2])
+		chan_info->center_frq2 =
+			nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]);
+
+	if (chan_info->center_frq2) {
+		u8 seg1_idx = 0;
+
+		if (ieee80211_freq_to_chan(chan_info->center_frq2, &seg1_idx) !=
+		    NUM_HOSTAPD_MODES)
+			chan_info->seg1_idx = seg1_idx;
+	}
+
+	return NL_SKIP;
+}
+
+
+static int nl80211_channel_info(void *priv, struct wpa_channel_info *ci)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
+	return send_and_recv_msgs(drv, msg, get_channel_info, ci, NULL, NULL);
+}
+
+
+static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx,
+					     void *handle)
+{
+	struct nl_cb *cb = eloop_ctx;
+	int res;
+
+	wpa_printf(MSG_MSGDUMP, "nl80211: Event message available");
+
+	res = nl_recvmsgs(handle, cb);
+	if (res < 0) {
+		wpa_printf(MSG_INFO, "nl80211: %s->nl_recvmsgs failed: %d",
+			   __func__, res);
+	}
+}
+
+
+/**
+ * wpa_driver_nl80211_set_country - ask nl80211 to set the regulatory domain
+ * @priv: driver_nl80211 private data
+ * @alpha2_arg: country to which to switch to
+ * Returns: 0 on success, -1 on failure
+ *
+ * This asks nl80211 to set the regulatory domain for given
+ * country ISO / IEC alpha2.
+ */
+static int wpa_driver_nl80211_set_country(void *priv, const char *alpha2_arg)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	char alpha2[3];
+	struct nl_msg *msg;
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return -ENOMEM;
+
+	alpha2[0] = alpha2_arg[0];
+	alpha2[1] = alpha2_arg[1];
+	alpha2[2] = '\0';
+
+	if (!nl80211_cmd(drv, msg, 0, NL80211_CMD_REQ_SET_REG) ||
+	    nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, alpha2)) {
+		nlmsg_free(msg);
+		return -EINVAL;
+	}
+	if (send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL))
+		return -EINVAL;
+	return 0;
+}
+
+
+static int nl80211_get_country(struct nl_msg *msg, void *arg)
+{
+	char *alpha2 = arg;
+	struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+
+	nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+	if (!tb_msg[NL80211_ATTR_REG_ALPHA2]) {
+		wpa_printf(MSG_DEBUG, "nl80211: No country information available");
+		return NL_SKIP;
+	}
+	os_strlcpy(alpha2, nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]), 3);
+	return NL_SKIP;
+}
+
+
+static int wpa_driver_nl80211_get_country(void *priv, char *alpha2)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return -ENOMEM;
+
+	nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG);
+
+	if (drv->capa.flags & WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY) {
+		/* put wiphy idx to get the interface specific country code
+		 * instead of the global one. */
+		if (nla_put_u32(msg, NL80211_ATTR_WIPHY, drv->wiphy_idx)) {
+			nlmsg_free(msg);
+			return -1;
+		}
+	}
+
+	alpha2[0] = '\0';
+	ret = send_and_recv_msgs(drv, msg, nl80211_get_country, alpha2,
+				 NULL, NULL);
+	if (!alpha2[0])
+		ret = -1;
+
+	return ret;
+}
+
+
+static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global)
+{
+	int ret;
+
+	global->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
+	if (global->nl_cb == NULL) {
+		wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
+			   "callbacks");
+		return -1;
+	}
+
+	global->nl = nl_create_handle(global->nl_cb, "nl");
+	if (global->nl == NULL)
+		goto err;
+
+	global->nl80211_id = genl_ctrl_resolve(global->nl, "nl80211");
+	if (global->nl80211_id < 0) {
+		wpa_printf(MSG_ERROR, "nl80211: 'nl80211' generic netlink not "
+			   "found");
+		goto err;
+	}
+
+	global->nlctrl_id = genl_ctrl_resolve(global->nl, "nlctrl");
+	if (global->nlctrl_id < 0) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: 'nlctrl' generic netlink not found");
+		goto err;
+	}
+
+	global->nl_event = nl_create_handle(global->nl_cb, "event");
+	if (global->nl_event == NULL)
+		goto err;
+
+	ret = nl_get_multicast_id(global, "nl80211", "scan");
+	if (ret >= 0)
+		ret = nl_socket_add_membership(global->nl_event, ret);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "nl80211: Could not add multicast "
+			   "membership for scan events: %d (%s)",
+			   ret, nl_geterror(ret));
+		goto err;
+	}
+
+	ret = nl_get_multicast_id(global, "nl80211", "mlme");
+	if (ret >= 0)
+		ret = nl_socket_add_membership(global->nl_event, ret);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "nl80211: Could not add multicast "
+			   "membership for mlme events: %d (%s)",
+			   ret, nl_geterror(ret));
+		goto err;
+	}
+
+	ret = nl_get_multicast_id(global, "nl80211", "regulatory");
+	if (ret >= 0)
+		ret = nl_socket_add_membership(global->nl_event, ret);
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "nl80211: Could not add multicast "
+			   "membership for regulatory events: %d (%s)",
+			   ret, nl_geterror(ret));
+		/* Continue without regulatory events */
+	}
+
+	ret = nl_get_multicast_id(global, "nl80211", "vendor");
+	if (ret >= 0)
+		ret = nl_socket_add_membership(global->nl_event, ret);
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "nl80211: Could not add multicast "
+			   "membership for vendor events: %d (%s)",
+			   ret, nl_geterror(ret));
+		/* Continue without vendor events */
+	}
+
+	nl_cb_set(global->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
+		  no_seq_check, NULL);
+	nl_cb_set(global->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
+		  process_global_event, global);
+
+	nl80211_register_eloop_read(&global->nl_event,
+				    wpa_driver_nl80211_event_receive,
+				    global->nl_cb, 0);
+
+	return 0;
+
+err:
+	nl_destroy_handles(&global->nl_event);
+	nl_destroy_handles(&global->nl);
+	nl_cb_put(global->nl_cb);
+	global->nl_cb = NULL;
+	return -1;
+}
+
+
+static void nl80211_check_global(struct nl80211_global *global)
+{
+	struct nl_sock *handle;
+	const char *groups[] = { "scan", "mlme", "regulatory", "vendor", NULL };
+	int ret;
+	unsigned int i;
+
+	/*
+	 * Try to re-add memberships to handle case of cfg80211 getting reloaded
+	 * and all registration having been cleared.
+	 */
+	handle = (void *) (((intptr_t) global->nl_event) ^
+			   ELOOP_SOCKET_INVALID);
+
+	for (i = 0; groups[i]; i++) {
+		ret = nl_get_multicast_id(global, "nl80211", groups[i]);
+		if (ret >= 0)
+			ret = nl_socket_add_membership(handle, ret);
+		if (ret < 0) {
+			wpa_printf(MSG_INFO,
+				   "nl80211: Could not re-add multicast membership for %s events: %d (%s)",
+				   groups[i], ret, nl_geterror(ret));
+		}
+	}
+}
+
+
+static void wpa_driver_nl80211_rfkill_blocked(void *ctx)
+{
+	struct wpa_driver_nl80211_data *drv = ctx;
+
+	wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked");
+
+	/*
+	 * rtnetlink ifdown handler will report interfaces other than the P2P
+	 * Device interface as disabled.
+	 */
+	if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE)
+		wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, NULL);
+}
+
+
+static void wpa_driver_nl80211_rfkill_unblocked(void *ctx)
+{
+	struct wpa_driver_nl80211_data *drv = ctx;
+	wpa_printf(MSG_DEBUG, "nl80211: RFKILL unblocked");
+	if (i802_set_iface_flags(drv->first_bss, 1)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Could not set interface UP "
+			   "after rfkill unblock");
+		return;
+	}
+
+	if (is_p2p_net_interface(drv->nlmode))
+		nl80211_disable_11b_rates(drv, drv->ifindex, 1);
+
+	/*
+	 * rtnetlink ifup handler will report interfaces other than the P2P
+	 * Device interface as enabled.
+	 */
+	if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE)
+		wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, NULL);
+}
+
+
+static void wpa_driver_nl80211_handle_eapol_tx_status(int sock,
+						      void *eloop_ctx,
+						      void *handle)
+{
+	struct wpa_driver_nl80211_data *drv = eloop_ctx;
+	u8 data[2048];
+	struct msghdr msg;
+	struct iovec entry;
+	u8 control[512];
+	struct cmsghdr *cmsg;
+	int res, found_ee = 0, found_wifi = 0, acked = 0;
+	union wpa_event_data event;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_iov = &entry;
+	msg.msg_iovlen = 1;
+	entry.iov_base = data;
+	entry.iov_len = sizeof(data);
+	msg.msg_control = &control;
+	msg.msg_controllen = sizeof(control);
+
+	res = recvmsg(sock, &msg, MSG_ERRQUEUE);
+	/* if error or not fitting 802.3 header, return */
+	if (res < 14)
+		return;
+
+	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg))
+	{
+		if (cmsg->cmsg_level == SOL_SOCKET &&
+		    cmsg->cmsg_type == SCM_WIFI_STATUS) {
+			int *ack;
+
+			found_wifi = 1;
+			ack = (void *)CMSG_DATA(cmsg);
+			acked = *ack;
+		}
+
+		if (cmsg->cmsg_level == SOL_PACKET &&
+		    cmsg->cmsg_type == PACKET_TX_TIMESTAMP) {
+			struct sock_extended_err *err =
+				(struct sock_extended_err *)CMSG_DATA(cmsg);
+
+			if (err->ee_origin == SO_EE_ORIGIN_TXSTATUS)
+				found_ee = 1;
+		}
+	}
+
+	if (!found_ee || !found_wifi)
+		return;
+
+	memset(&event, 0, sizeof(event));
+	event.eapol_tx_status.dst = data;
+	event.eapol_tx_status.data = data + 14;
+	event.eapol_tx_status.data_len = res - 14;
+	event.eapol_tx_status.ack = acked;
+	wpa_supplicant_event(drv->ctx, EVENT_EAPOL_TX_STATUS, &event);
+}
+
+
+static int nl80211_init_connect_handle(struct i802_bss *bss)
+{
+	if (bss->nl_connect) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Connect handle already created (nl_connect=%p)",
+			   bss->nl_connect);
+		return -1;
+	}
+
+	bss->nl_connect = nl_create_handle(bss->nl_cb, "connect");
+	if (!bss->nl_connect)
+		return -1;
+	nl80211_register_eloop_read(&bss->nl_connect,
+				    wpa_driver_nl80211_event_receive,
+				    bss->nl_cb, 1);
+	return 0;
+}
+
+
+static int nl80211_init_bss(struct i802_bss *bss)
+{
+	bss->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
+	if (!bss->nl_cb)
+		return -1;
+
+	nl_cb_set(bss->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
+		  no_seq_check, NULL);
+	nl_cb_set(bss->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
+		  process_bss_event, bss);
+
+	nl80211_init_connect_handle(bss);
+
+	return 0;
+}
+
+
+static void nl80211_destroy_bss(struct i802_bss *bss)
+{
+	nl_cb_put(bss->nl_cb);
+	bss->nl_cb = NULL;
+
+	if (bss->nl_connect)
+		nl80211_destroy_eloop_handle(&bss->nl_connect, 1);
+}
+
+
+static void
+wpa_driver_nl80211_drv_init_rfkill(struct wpa_driver_nl80211_data *drv)
+{
+	struct rfkill_config *rcfg;
+
+	if (drv->rfkill)
+		return;
+
+	rcfg = os_zalloc(sizeof(*rcfg));
+	if (!rcfg)
+		return;
+
+	rcfg->ctx = drv;
+
+	/* rfkill uses netdev sysfs for initialization. However, P2P Device is
+	 * not associated with a netdev, so use the name of some other interface
+	 * sharing the same wiphy as the P2P Device interface.
+	 *
+	 * Note: This is valid, as a P2P Device interface is always dynamically
+	 * created and is created only once another wpa_s interface was added.
+	 */
+	if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) {
+		struct nl80211_global *global = drv->global;
+		struct wpa_driver_nl80211_data *tmp1;
+
+		dl_list_for_each(tmp1, &global->interfaces,
+				 struct wpa_driver_nl80211_data, list) {
+			if (drv == tmp1 || drv->wiphy_idx != tmp1->wiphy_idx ||
+			    !tmp1->rfkill)
+				continue;
+
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Use (%s) to initialize P2P Device rfkill",
+				   tmp1->first_bss->ifname);
+			os_strlcpy(rcfg->ifname, tmp1->first_bss->ifname,
+				   sizeof(rcfg->ifname));
+			break;
+		}
+	} else {
+		os_strlcpy(rcfg->ifname, drv->first_bss->ifname,
+			   sizeof(rcfg->ifname));
+	}
+
+	rcfg->blocked_cb = wpa_driver_nl80211_rfkill_blocked;
+	rcfg->unblocked_cb = wpa_driver_nl80211_rfkill_unblocked;
+	drv->rfkill = rfkill_init(rcfg);
+	if (!drv->rfkill) {
+		wpa_printf(MSG_DEBUG, "nl80211: RFKILL status not available");
+		os_free(rcfg);
+	}
+}
+
+
+static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname,
+					  void *global_priv, int hostapd,
+					  const u8 *set_addr,
+					  const char *driver_params)
+{
+	struct wpa_driver_nl80211_data *drv;
+	struct i802_bss *bss;
+	unsigned int i;
+
+	if (global_priv == NULL)
+		return NULL;
+	drv = os_zalloc(sizeof(*drv));
+	if (drv == NULL)
+		return NULL;
+	drv->global = global_priv;
+	drv->ctx = ctx;
+	drv->hostapd = !!hostapd;
+	drv->eapol_sock = -1;
+
+	/*
+	 * There is no driver capability flag for this, so assume it is
+	 * supported and disable this on first attempt to use if the driver
+	 * rejects the command due to missing support.
+	 */
+	drv->set_rekey_offload = 1;
+
+	drv->num_if_indices = ARRAY_SIZE(drv->default_if_indices);
+	drv->if_indices = drv->default_if_indices;
+
+	drv->first_bss = os_zalloc(sizeof(*drv->first_bss));
+	if (!drv->first_bss) {
+		os_free(drv);
+		return NULL;
+	}
+	bss = drv->first_bss;
+	bss->drv = drv;
+	bss->ctx = ctx;
+
+	os_strlcpy(bss->ifname, ifname, sizeof(bss->ifname));
+	drv->monitor_ifidx = -1;
+	drv->monitor_sock = -1;
+	drv->eapol_tx_sock = -1;
+	drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
+
+	if (nl80211_init_bss(bss))
+		goto failed;
+
+	if (wpa_driver_nl80211_finish_drv_init(drv, set_addr, 1, driver_params))
+		goto failed;
+
+	if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS) {
+		drv->control_port_ap = 1;
+		goto skip_wifi_status;
+	}
+
+	drv->eapol_tx_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
+	if (drv->eapol_tx_sock < 0)
+		goto failed;
+
+	if (drv->data_tx_status) {
+		int enabled = 1;
+
+		if (setsockopt(drv->eapol_tx_sock, SOL_SOCKET, SO_WIFI_STATUS,
+			       &enabled, sizeof(enabled)) < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: wifi status sockopt failed: %s",
+				   strerror(errno));
+			drv->data_tx_status = 0;
+			if (!drv->use_monitor)
+				drv->capa.flags &=
+					~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
+		} else {
+			eloop_register_read_sock(
+				drv->eapol_tx_sock,
+				wpa_driver_nl80211_handle_eapol_tx_status,
+				drv, NULL);
+		}
+	}
+skip_wifi_status:
+
+	if (drv->global) {
+		nl80211_check_global(drv->global);
+		dl_list_add(&drv->global->interfaces, &drv->list);
+		drv->in_interface_list = 1;
+	}
+
+	/*
+	 * Set the default link to be the first one, and set its address to that
+	 * of the interface.
+	 */
+	bss->flink = &bss->links[0];
+	bss->n_links = 1;
+	os_memcpy(bss->flink->addr, bss->addr, ETH_ALEN);
+
+	for (i = 0; i < MAX_NUM_MLD_LINKS; i++)
+		bss->links[i].link_id = NL80211_DRV_LINK_ID_NA;
+
+	return bss;
+
+failed:
+	wpa_driver_nl80211_deinit(bss);
+	return NULL;
+}
+
+
+/**
+ * wpa_driver_nl80211_init - Initialize nl80211 driver interface
+ * @ctx: context to be used when calling wpa_supplicant functions,
+ * e.g., wpa_supplicant_event()
+ * @ifname: interface name, e.g., wlan0
+ * @global_priv: private driver global data from global_init()
+ * Returns: Pointer to private data, %NULL on failure
+ */
+static void * wpa_driver_nl80211_init(void *ctx, const char *ifname,
+				      void *global_priv)
+{
+	return wpa_driver_nl80211_drv_init(ctx, ifname, global_priv, 0, NULL,
+					   NULL);
+}
+
+
+static int nl80211_register_frame(struct i802_bss *bss,
+				  struct nl_sock *nl_handle,
+				  u16 type, const u8 *match, size_t match_len,
+				  bool multicast)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+	char buf[30];
+
+	buf[0] = '\0';
+	wpa_snprintf_hex(buf, sizeof(buf), match, match_len);
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Register frame type=0x%x (%s) nl_handle=%p match=%s multicast=%d",
+		   type, fc2str(type), nl_handle, buf, multicast);
+
+	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_REGISTER_FRAME)) ||
+	    (multicast && nla_put_flag(msg, NL80211_ATTR_RECEIVE_MULTICAST)) ||
+	    nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE, type) ||
+	    nla_put(msg, NL80211_ATTR_FRAME_MATCH, match_len, match)) {
+		nlmsg_free(msg);
+		return -1;
+	}
+
+	ret = send_and_recv(drv->global, nl_handle, msg, NULL, NULL,
+			    NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Register frame command "
+			   "failed (type=%u): ret=%d (%s)",
+			   type, ret, strerror(-ret));
+		wpa_hexdump(MSG_DEBUG, "nl80211: Register frame match",
+			    match, match_len);
+	}
+	return ret;
+}
+
+
+static int nl80211_alloc_mgmt_handle(struct i802_bss *bss)
+{
+	if (bss->nl_mgmt) {
+		wpa_printf(MSG_DEBUG, "nl80211: Mgmt reporting "
+			   "already on! (nl_mgmt=%p)", bss->nl_mgmt);
+		return -1;
+	}
+
+	bss->nl_mgmt = nl_create_handle(bss->nl_cb, "mgmt");
+	if (bss->nl_mgmt == NULL)
+		return -1;
+
+	return 0;
+}
+
+
+static void nl80211_mgmt_handle_register_eloop(struct i802_bss *bss)
+{
+	nl80211_register_eloop_read(&bss->nl_mgmt,
+				    wpa_driver_nl80211_event_receive,
+				    bss->nl_cb, 0);
+}
+
+
+static int nl80211_register_action_frame(struct i802_bss *bss,
+					 const u8 *match, size_t match_len)
+{
+	u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_ACTION << 4);
+	return nl80211_register_frame(bss, bss->nl_mgmt,
+				      type, match, match_len, false);
+}
+
+
+static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_AUTH << 4);
+	int ret = 0;
+
+	if (nl80211_alloc_mgmt_handle(bss))
+		return -1;
+	wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with non-AP "
+		   "handle %p", bss->nl_mgmt);
+
+	if (drv->nlmode == NL80211_IFTYPE_ADHOC) {
+		/* register for any AUTH message */
+		nl80211_register_frame(bss, bss->nl_mgmt, type, NULL, 0, false);
+	} else if ((drv->capa.flags & WPA_DRIVER_FLAGS_SAE) &&
+		   !(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) {
+		/* register for SAE Authentication frames */
+		nl80211_register_frame(bss, bss->nl_mgmt, type,
+				       (u8 *) "\x03\x00", 2, false);
+	}
+
+#ifdef CONFIG_PASN
+	/* register for PASN Authentication frames */
+	if (nl80211_register_frame(bss, bss->nl_mgmt, type,
+				   (u8 *) "\x07\x00", 2, false))
+		ret = -1;
+#endif /* CONFIG_PASN */
+
+#ifdef CONFIG_INTERWORKING
+	/* QoS Map Configure */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x01\x04", 2) < 0)
+		ret = -1;
+#endif /* CONFIG_INTERWORKING */
+#if defined(CONFIG_P2P) || defined(CONFIG_INTERWORKING) || defined(CONFIG_DPP)
+	/* GAS Initial Request */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0a", 2) < 0)
+		ret = -1;
+	/* GAS Initial Response */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0b", 2) < 0)
+		ret = -1;
+	/* GAS Comeback Request */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0c", 2) < 0)
+		ret = -1;
+	/* GAS Comeback Response */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0d", 2) < 0)
+		ret = -1;
+	/* Protected GAS Initial Request */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0a", 2) < 0)
+		ret = -1;
+	/* Protected GAS Initial Response */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0b", 2) < 0)
+		ret = -1;
+	/* Protected GAS Comeback Request */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0c", 2) < 0)
+		ret = -1;
+	/* Protected GAS Comeback Response */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0d", 2) < 0)
+		ret = -1;
+#endif /* CONFIG_P2P || CONFIG_INTERWORKING || CONFIG_DPP */
+#ifdef CONFIG_P2P
+	/* P2P Public Action */
+	if (nl80211_register_action_frame(bss,
+					  (u8 *) "\x04\x09\x50\x6f\x9a\x09",
+					  6) < 0)
+		ret = -1;
+	/* P2P Action */
+	if (nl80211_register_action_frame(bss,
+					  (u8 *) "\x7f\x50\x6f\x9a\x09",
+					  5) < 0)
+		ret = -1;
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_DPP
+	/* DPP Public Action */
+	if (nl80211_register_action_frame(bss,
+					  (u8 *) "\x04\x09\x50\x6f\x9a\x1a",
+					  6) < 0)
+		ret = -1;
+#endif /* CONFIG_DPP */
+#ifdef CONFIG_OCV
+	/* SA Query Request */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x08\x00", 2) < 0)
+		ret = -1;
+#endif /* CONFIG_OCV */
+	/* SA Query Response */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x08\x01", 2) < 0)
+		ret = -1;
+#ifdef CONFIG_TDLS
+	if ((drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) {
+		/* TDLS Discovery Response */
+		if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0e", 2) <
+		    0)
+			ret = -1;
+	}
+#endif /* CONFIG_TDLS */
+#ifdef CONFIG_FST
+	/* FST Action frames */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x12", 1) < 0)
+		ret = -1;
+#endif /* CONFIG_FST */
+
+	/* FT Action frames */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x06", 1) < 0)
+		ret = -1;
+	else if (!drv->has_driver_key_mgmt) {
+		int i;
+
+		/* Update supported AKMs only if the driver doesn't advertize
+		 * any AKM capabilities. */
+		drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT |
+			WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK;
+
+		/* Update per interface supported AKMs */
+		for (i = 0; i < WPA_IF_MAX; i++)
+			drv->capa.key_mgmt_iftype[i] = drv->capa.key_mgmt;
+	}
+
+	/* WNM - BSS Transition Management Request */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x07", 2) < 0)
+		ret = -1;
+	/* WNM-Sleep Mode Response */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x11", 2) < 0)
+		ret = -1;
+#ifdef CONFIG_WNM
+	/* WNM - Collocated Interference Request */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x0b", 2) < 0)
+		ret = -1;
+#endif /* CONFIG_WNM */
+
+#ifdef CONFIG_HS20
+	/* WNM-Notification */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x1a", 2) < 0)
+		ret = -1;
+#endif /* CONFIG_HS20 */
+
+	/* WMM-AC ADDTS Response */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x11\x01", 2) < 0)
+		ret = -1;
+
+	/* WMM-AC DELTS */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x11\x02", 2) < 0)
+		ret = -1;
+
+	/* Radio Measurement - Neighbor Report Response */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x05\x05", 2) < 0)
+		ret = -1;
+
+	/* Radio Measurement - Radio Measurement Request */
+	if  (!drv->no_rrm &&
+	     nl80211_register_action_frame(bss, (u8 *) "\x05\x00", 2) < 0)
+		ret = -1;
+
+	/* Radio Measurement - Link Measurement Request */
+	if ((drv->capa.rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION) &&
+	    (nl80211_register_action_frame(bss, (u8 *) "\x05\x02", 2) < 0))
+		ret = -1;
+
+	/* Robust AV SCS Response */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x13\x01", 2) < 0)
+		ret = -1;
+
+	/* Robust AV MSCS Response */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x13\x05", 2) < 0)
+		ret = -1;
+
+	/* Protected QoS Management Action frame */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x7e\x50\x6f\x9a\x1a",
+					  5) < 0)
+		ret = -1;
+
+	nl80211_mgmt_handle_register_eloop(bss);
+
+	return ret;
+}
+
+
+static int nl80211_mgmt_subscribe_mesh(struct i802_bss *bss)
+{
+	int ret = 0;
+
+	if (nl80211_alloc_mgmt_handle(bss))
+		return -1;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Subscribe to mgmt frames with mesh handle %p",
+		   bss->nl_mgmt);
+
+	/* Auth frames for mesh SAE */
+	if (nl80211_register_frame(bss, bss->nl_mgmt,
+				   (WLAN_FC_TYPE_MGMT << 2) |
+				   (WLAN_FC_STYPE_AUTH << 4),
+				   NULL, 0, false) < 0)
+		ret = -1;
+
+	/* Mesh peering open */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x0f\x01", 2) < 0)
+		ret = -1;
+	/* Mesh peering confirm */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x0f\x02", 2) < 0)
+		ret = -1;
+	/* Mesh peering close */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x0f\x03", 2) < 0)
+		ret = -1;
+
+	nl80211_mgmt_handle_register_eloop(bss);
+
+	return ret;
+}
+
+
+static int nl80211_register_spurious_class3(struct i802_bss *bss)
+{
+	struct nl_msg *msg;
+	int ret;
+
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_UNEXPECTED_FRAME);
+	ret = send_and_recv(bss->drv->global, bss->nl_mgmt, msg, NULL, NULL,
+			    NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Register spurious class3 "
+			   "failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+	}
+	return ret;
+}
+
+
+static int nl80211_action_subscribe_ap(struct i802_bss *bss)
+{
+	int ret = 0;
+
+	/* Public Action frames */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x04", 1) < 0)
+		ret = -1;
+	/* RRM Measurement Report */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x05\x01", 2) < 0)
+		ret = -1;
+	/* RRM Link Measurement Report */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x05\x03", 2) < 0)
+		ret = -1;
+	/* RRM Neighbor Report Request */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x05\x04", 2) < 0)
+		ret = -1;
+	/* FT Action frames */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x06", 1) < 0)
+		ret = -1;
+	/* SA Query */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x08", 1) < 0)
+		ret = -1;
+	/* Protected Dual of Public Action */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x09", 1) < 0)
+		ret = -1;
+	/* WNM */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x0a", 1) < 0)
+		ret = -1;
+	/* WMM */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x11", 1) < 0)
+		ret = -1;
+#ifdef CONFIG_FST
+	/* FST Action frames */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x12", 1) < 0)
+		ret = -1;
+#endif /* CONFIG_FST */
+	/* Vendor-specific */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x7f", 1) < 0)
+		ret = -1;
+
+	return ret;
+}
+
+
+static int nl80211_mgmt_subscribe_ap(struct i802_bss *bss)
+{
+	static const int stypes[] = {
+		WLAN_FC_STYPE_AUTH,
+		WLAN_FC_STYPE_ASSOC_REQ,
+		WLAN_FC_STYPE_REASSOC_REQ,
+		WLAN_FC_STYPE_DISASSOC,
+		WLAN_FC_STYPE_DEAUTH,
+		WLAN_FC_STYPE_PROBE_REQ,
+/* Beacon doesn't work as mac80211 doesn't currently allow
+ * it, but it wouldn't really be the right thing anyway as
+ * it isn't per interface ... maybe just dump the scan
+ * results periodically for OLBC?
+ */
+		/* WLAN_FC_STYPE_BEACON, */
+	};
+	unsigned int i;
+
+	if (nl80211_alloc_mgmt_handle(bss))
+		return -1;
+	wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP "
+		   "handle %p", bss->nl_mgmt);
+
+	for (i = 0; i < ARRAY_SIZE(stypes); i++) {
+		if (nl80211_register_frame(bss, bss->nl_mgmt,
+					   (WLAN_FC_TYPE_MGMT << 2) |
+					   (stypes[i] << 4),
+					   NULL, 0, false) < 0) {
+			goto out_err;
+		}
+	}
+
+	if (nl80211_action_subscribe_ap(bss))
+		goto out_err;
+
+	if (nl80211_register_spurious_class3(bss))
+		goto out_err;
+
+	nl80211_mgmt_handle_register_eloop(bss);
+	return 0;
+
+out_err:
+	nl_destroy_handles(&bss->nl_mgmt);
+	return -1;
+}
+
+
+static int nl80211_mgmt_subscribe_ap_dev_sme(struct i802_bss *bss)
+{
+	if (nl80211_alloc_mgmt_handle(bss))
+		return -1;
+	wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP "
+		   "handle %p (device SME)", bss->nl_mgmt);
+
+	if (nl80211_action_subscribe_ap(bss))
+		goto out_err;
+
+	if (bss->drv->device_ap_sme) {
+		u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_AUTH << 4);
+
+		/* Register for all Authentication frames */
+		if (nl80211_register_frame(bss, bss->nl_mgmt, type, NULL, 0,
+					   false) < 0)
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Failed to subscribe to handle Authentication frames - SAE offload may not work");
+	}
+
+	nl80211_mgmt_handle_register_eloop(bss);
+	return 0;
+
+out_err:
+	nl_destroy_handles(&bss->nl_mgmt);
+	return -1;
+}
+
+
+static void nl80211_mgmt_unsubscribe(struct i802_bss *bss, const char *reason)
+{
+	if (bss->nl_mgmt == NULL)
+		return;
+	wpa_printf(MSG_DEBUG, "nl80211: Unsubscribe mgmt frames handle %p "
+		   "(%s)", bss->nl_mgmt, reason);
+	nl80211_destroy_eloop_handle(&bss->nl_mgmt, 0);
+
+	nl80211_put_wiphy_data_ap(bss);
+}
+
+
+static void wpa_driver_nl80211_send_rfkill(void *eloop_ctx, void *timeout_ctx)
+{
+	wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL);
+}
+
+
+static void nl80211_del_p2pdev(struct i802_bss *bss)
+{
+	struct nl_msg *msg;
+	int ret;
+
+	msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_DEL_INTERFACE);
+	ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
+
+	wpa_printf(MSG_DEBUG, "nl80211: Delete P2P Device %s (0x%llx): %s",
+		   bss->ifname, (long long unsigned int) bss->wdev_id,
+		   strerror(-ret));
+}
+
+
+static int nl80211_set_p2pdev(struct i802_bss *bss, int start)
+{
+	struct nl_msg *msg;
+	int ret;
+
+	msg = nl80211_cmd_msg(bss, 0, start ? NL80211_CMD_START_P2P_DEVICE :
+			      NL80211_CMD_STOP_P2P_DEVICE);
+	ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
+
+	wpa_printf(MSG_DEBUG, "nl80211: %s P2P Device %s (0x%llx): %s",
+		   start ? "Start" : "Stop",
+		   bss->ifname, (long long unsigned int) bss->wdev_id,
+		   strerror(-ret));
+	return ret;
+}
+
+
+static int i802_set_iface_flags(struct i802_bss *bss, int up)
+{
+	enum nl80211_iftype nlmode;
+
+	nlmode = nl80211_get_ifmode(bss);
+	if (nlmode != NL80211_IFTYPE_P2P_DEVICE) {
+		return linux_set_iface_flags(bss->drv->global->ioctl_sock,
+					     bss->ifname, up);
+	}
+
+	/* P2P Device has start/stop which is equivalent */
+	return nl80211_set_p2pdev(bss, up);
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+static int qca_vendor_test_cmd_handler(struct nl_msg *msg, void *arg)
+{
+	/* struct wpa_driver_nl80211_data *drv = arg; */
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: QCA vendor test command response received");
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+	if (!tb[NL80211_ATTR_VENDOR_DATA]) {
+		wpa_printf(MSG_DEBUG, "nl80211: No vendor data attribute");
+		return NL_SKIP;
+	}
+
+	wpa_hexdump(MSG_DEBUG,
+		    "nl80211: Received QCA vendor test command response",
+		    nla_data(tb[NL80211_ATTR_VENDOR_DATA]),
+		    nla_len(tb[NL80211_ATTR_VENDOR_DATA]));
+
+	return NL_SKIP;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+static void qca_vendor_test(struct wpa_driver_nl80211_data *drv)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+	struct nl_msg *msg;
+	struct nlattr *params;
+	int ret;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_TEST) ||
+	    !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+	    nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_TEST, 123)) {
+		nlmsg_free(msg);
+		return;
+	}
+	nla_nest_end(msg, params);
+
+	ret = send_and_recv_msgs(drv, msg, qca_vendor_test_cmd_handler, drv,
+				 NULL, NULL);
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: QCA vendor test command returned %d (%s)",
+		   ret, strerror(-ret));
+#endif /* CONFIG_TESTING_OPTIONS */
+}
+
+
+static int
+wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv,
+				   const u8 *set_addr, int first,
+				   const char *driver_params)
+{
+	struct i802_bss *bss = drv->first_bss;
+	int send_rfkill_event = 0;
+	enum nl80211_iftype nlmode;
+
+	drv->ifindex = if_nametoindex(bss->ifname);
+	bss->ifindex = drv->ifindex;
+	bss->wdev_id = drv->global->if_add_wdevid;
+	bss->wdev_id_set = drv->global->if_add_wdevid_set;
+
+	bss->if_dynamic = drv->ifindex == drv->global->if_add_ifindex;
+	bss->if_dynamic = bss->if_dynamic || drv->global->if_add_wdevid_set;
+	drv->global->if_add_wdevid_set = 0;
+
+	if (!bss->if_dynamic && nl80211_get_ifmode(bss) == NL80211_IFTYPE_AP)
+		bss->static_ap = 1;
+
+	if (first &&
+	    nl80211_get_ifmode(bss) != NL80211_IFTYPE_P2P_DEVICE &&
+	    linux_iface_up(drv->global->ioctl_sock, bss->ifname) > 0)
+		drv->start_iface_up = 1;
+
+	if (wpa_driver_nl80211_capa(drv))
+		return -1;
+
+	if (driver_params && nl80211_set_param(bss, driver_params) < 0)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "nl80211: interface %s in phy %s",
+		   bss->ifname, drv->phyname);
+
+	if (set_addr &&
+	    (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0) ||
+	     linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+				set_addr)))
+		return -1;
+
+	if (first && nl80211_get_ifmode(bss) == NL80211_IFTYPE_STATION)
+		drv->start_mode_sta = 1;
+
+	if (drv->hostapd || bss->static_ap)
+		nlmode = NL80211_IFTYPE_AP;
+	else if (bss->if_dynamic ||
+		 nl80211_get_ifmode(bss) == NL80211_IFTYPE_MESH_POINT)
+		nlmode = nl80211_get_ifmode(bss);
+	else
+		nlmode = NL80211_IFTYPE_STATION;
+
+	if (wpa_driver_nl80211_set_mode(bss, nlmode) < 0) {
+		wpa_printf(MSG_ERROR, "nl80211: Could not configure driver mode");
+		return -1;
+	}
+
+	if (nlmode == NL80211_IFTYPE_P2P_DEVICE)
+		nl80211_get_macaddr(bss);
+
+	wpa_driver_nl80211_drv_init_rfkill(drv);
+
+	if (!rfkill_is_blocked(drv->rfkill)) {
+		int ret = i802_set_iface_flags(bss, 1);
+		if (ret) {
+			wpa_printf(MSG_ERROR, "nl80211: Could not set "
+				   "interface '%s' UP", bss->ifname);
+			return ret;
+		}
+
+		if (is_p2p_net_interface(nlmode))
+			nl80211_disable_11b_rates(bss->drv,
+						  bss->drv->ifindex, 1);
+
+		if (nlmode == NL80211_IFTYPE_P2P_DEVICE)
+			return ret;
+	} else {
+		wpa_printf(MSG_DEBUG, "nl80211: Could not yet enable "
+			   "interface '%s' due to rfkill", bss->ifname);
+		if (nlmode != NL80211_IFTYPE_P2P_DEVICE)
+			drv->if_disabled = 1;
+
+		send_rfkill_event = 1;
+	}
+
+	if (!drv->hostapd && nlmode != NL80211_IFTYPE_P2P_DEVICE)
+		netlink_send_oper_ifla(drv->global->netlink, drv->ifindex,
+				       1, IF_OPER_DORMANT);
+
+	if (nlmode != NL80211_IFTYPE_P2P_DEVICE) {
+		if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+				       bss->addr))
+			return -1;
+		os_memcpy(drv->perm_addr, bss->addr, ETH_ALEN);
+	}
+
+	if (send_rfkill_event) {
+		eloop_register_timeout(0, 0, wpa_driver_nl80211_send_rfkill,
+				       drv, drv->ctx);
+	}
+
+	if (drv->vendor_cmd_test_avail)
+		qca_vendor_test(drv);
+
+	return 0;
+}
+
+
+static int wpa_driver_nl80211_del_beacon(struct i802_bss *bss,
+					 struct i802_link *link)
+{
+	struct nl_msg *msg;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	if (!link->beacon_set)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Remove beacon (ifindex=%d)",
+		   bss->ifindex);
+	link->beacon_set = 0;
+	link->freq = 0;
+
+	nl80211_put_wiphy_data_ap(bss);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_BEACON);
+	if (!msg)
+		return -ENOBUFS;
+
+	if (link->link_id != NL80211_DRV_LINK_ID_NA) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: MLD: stop beaconing on link=%u",
+			   link->link_id);
+
+		if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
+			       link->link_id)) {
+			nlmsg_free(msg);
+			return -ENOBUFS;
+		}
+	}
+
+	return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+}
+
+
+static void wpa_driver_nl80211_del_beacon_all(struct i802_bss *bss)
+{
+	unsigned int i;
+
+	for (i = 0; i < bss->n_links; i++)
+		wpa_driver_nl80211_del_beacon(bss, &bss->links[i]);
+}
+
+
+/**
+ * wpa_driver_nl80211_deinit - Deinitialize nl80211 driver interface
+ * @bss: Pointer to private nl80211 data from wpa_driver_nl80211_init()
+ *
+ * Shut down driver interface and processing of driver events. Free
+ * private data buffer if one was allocated in wpa_driver_nl80211_init().
+ */
+static void wpa_driver_nl80211_deinit(struct i802_bss *bss)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	unsigned int i;
+
+	wpa_printf(MSG_INFO, "nl80211: deinit ifname=%s disabled_11b_rates=%d",
+		   bss->ifname, drv->disabled_11b_rates);
+
+	bss->in_deinit = 1;
+	if (drv->data_tx_status)
+		eloop_unregister_read_sock(drv->eapol_tx_sock);
+	if (drv->eapol_tx_sock >= 0)
+		close(drv->eapol_tx_sock);
+
+	if (bss->nl_preq)
+		wpa_driver_nl80211_probe_req_report(bss, 0);
+	if (bss->added_if_into_bridge) {
+		if (linux_br_del_if(drv->global->ioctl_sock, bss->brname,
+				    bss->ifname) < 0)
+			wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+				   "interface %s from bridge %s: %s",
+				   bss->ifname, bss->brname, strerror(errno));
+	}
+
+	if (drv->rtnl_sk)
+		nl_socket_free(drv->rtnl_sk);
+
+	if (bss->added_bridge) {
+		if (linux_set_iface_flags(drv->global->ioctl_sock, bss->brname,
+					  0) < 0)
+			wpa_printf(MSG_INFO,
+				   "nl80211: Could not set bridge %s down",
+				   bss->brname);
+		if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0)
+			wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+				   "bridge %s: %s",
+				   bss->brname, strerror(errno));
+	}
+
+	nl80211_remove_monitor_interface(drv);
+
+	if (is_ap_interface(drv->nlmode))
+		wpa_driver_nl80211_del_beacon_all(bss);
+
+	if (drv->eapol_sock >= 0) {
+		eloop_unregister_read_sock(drv->eapol_sock);
+		close(drv->eapol_sock);
+	}
+
+	if (drv->if_indices != drv->default_if_indices)
+		os_free(drv->if_indices);
+
+	if (drv->disabled_11b_rates)
+		nl80211_disable_11b_rates(drv, drv->ifindex, 0);
+
+	netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, 0,
+			       IF_OPER_UP);
+	eloop_cancel_timeout(wpa_driver_nl80211_send_rfkill, drv, drv->ctx);
+	rfkill_deinit(drv->rfkill);
+
+	eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
+
+	if (!drv->start_iface_up)
+		(void) i802_set_iface_flags(bss, 0);
+
+	if (drv->addr_changed) {
+		if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname,
+					  0) < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Could not set interface down to restore permanent MAC address");
+		}
+		if (linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+				       drv->perm_addr) < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Could not restore permanent MAC address");
+		}
+	}
+
+	if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE) {
+		if (drv->start_mode_sta)
+			wpa_driver_nl80211_set_mode(bss,
+						    NL80211_IFTYPE_STATION);
+		nl80211_mgmt_unsubscribe(bss, "deinit");
+	} else {
+		nl80211_mgmt_unsubscribe(bss, "deinit");
+		nl80211_del_p2pdev(bss);
+	}
+
+	nl80211_destroy_bss(drv->first_bss);
+
+	os_free(drv->filter_ssids);
+
+	os_free(drv->auth_ie);
+	os_free(drv->auth_data);
+
+	if (drv->in_interface_list)
+		dl_list_del(&drv->list);
+
+	os_free(drv->extended_capa);
+	os_free(drv->extended_capa_mask);
+	for (i = 0; i < drv->num_iface_capa; i++) {
+		os_free(drv->iface_capa[i].ext_capa);
+		os_free(drv->iface_capa[i].ext_capa_mask);
+	}
+	os_free(drv->first_bss);
+#ifdef CONFIG_DRIVER_NL80211_QCA
+	os_free(drv->pending_roam_data);
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+	os_free(drv);
+}
+
+
+static u32 wpa_alg_to_cipher_suite(enum wpa_alg alg, size_t key_len)
+{
+	switch (alg) {
+	case WPA_ALG_WEP:
+		if (key_len == 5)
+			return RSN_CIPHER_SUITE_WEP40;
+		return RSN_CIPHER_SUITE_WEP104;
+	case WPA_ALG_TKIP:
+		return RSN_CIPHER_SUITE_TKIP;
+	case WPA_ALG_CCMP:
+		return RSN_CIPHER_SUITE_CCMP;
+	case WPA_ALG_GCMP:
+		return RSN_CIPHER_SUITE_GCMP;
+	case WPA_ALG_CCMP_256:
+		return RSN_CIPHER_SUITE_CCMP_256;
+	case WPA_ALG_GCMP_256:
+		return RSN_CIPHER_SUITE_GCMP_256;
+	case WPA_ALG_BIP_CMAC_128:
+		return RSN_CIPHER_SUITE_AES_128_CMAC;
+	case WPA_ALG_BIP_GMAC_128:
+		return RSN_CIPHER_SUITE_BIP_GMAC_128;
+	case WPA_ALG_BIP_GMAC_256:
+		return RSN_CIPHER_SUITE_BIP_GMAC_256;
+	case WPA_ALG_BIP_CMAC_256:
+		return RSN_CIPHER_SUITE_BIP_CMAC_256;
+	case WPA_ALG_SMS4:
+		return RSN_CIPHER_SUITE_SMS4;
+	case WPA_ALG_KRK:
+		return RSN_CIPHER_SUITE_KRK;
+	case WPA_ALG_NONE:
+		wpa_printf(MSG_ERROR, "nl80211: Unexpected encryption algorithm %d",
+			   alg);
+		return 0;
+	}
+
+	wpa_printf(MSG_ERROR, "nl80211: Unsupported encryption algorithm %d",
+		   alg);
+	return 0;
+}
+
+
+static u32 wpa_cipher_to_cipher_suite(unsigned int cipher)
+{
+	switch (cipher) {
+	case WPA_CIPHER_CCMP_256:
+		return RSN_CIPHER_SUITE_CCMP_256;
+	case WPA_CIPHER_GCMP_256:
+		return RSN_CIPHER_SUITE_GCMP_256;
+	case WPA_CIPHER_CCMP:
+		return RSN_CIPHER_SUITE_CCMP;
+	case WPA_CIPHER_GCMP:
+		return RSN_CIPHER_SUITE_GCMP;
+	case WPA_CIPHER_TKIP:
+		return RSN_CIPHER_SUITE_TKIP;
+	case WPA_CIPHER_WEP104:
+		return RSN_CIPHER_SUITE_WEP104;
+	case WPA_CIPHER_WEP40:
+		return RSN_CIPHER_SUITE_WEP40;
+	case WPA_CIPHER_GTK_NOT_USED:
+		return RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED;
+	default:
+		return 0;
+	}
+}
+
+
+static int wpa_cipher_to_cipher_suites(unsigned int ciphers, u32 suites[],
+				       int max_suites)
+{
+	int num_suites = 0;
+
+	if (num_suites < max_suites && ciphers & WPA_CIPHER_CCMP_256)
+		suites[num_suites++] = RSN_CIPHER_SUITE_CCMP_256;
+	if (num_suites < max_suites && ciphers & WPA_CIPHER_GCMP_256)
+		suites[num_suites++] = RSN_CIPHER_SUITE_GCMP_256;
+	if (num_suites < max_suites && ciphers & WPA_CIPHER_CCMP)
+		suites[num_suites++] = RSN_CIPHER_SUITE_CCMP;
+	if (num_suites < max_suites && ciphers & WPA_CIPHER_GCMP)
+		suites[num_suites++] = RSN_CIPHER_SUITE_GCMP;
+	if (num_suites < max_suites && ciphers & WPA_CIPHER_TKIP)
+		suites[num_suites++] = RSN_CIPHER_SUITE_TKIP;
+	if (num_suites < max_suites && ciphers & WPA_CIPHER_WEP104)
+		suites[num_suites++] = RSN_CIPHER_SUITE_WEP104;
+	if (num_suites < max_suites && ciphers & WPA_CIPHER_WEP40)
+		suites[num_suites++] = RSN_CIPHER_SUITE_WEP40;
+
+	return num_suites;
+}
+
+
+static int wpa_key_mgmt_to_suites(unsigned int key_mgmt_suites, u32 suites[],
+				  int max_suites)
+{
+	int num_suites = 0;
+
+#define __AKM(a, b) \
+	if (num_suites < max_suites && \
+	    (key_mgmt_suites & (WPA_KEY_MGMT_ ## a))) \
+		suites[num_suites++] = (RSN_AUTH_KEY_MGMT_ ## b)
+	__AKM(IEEE8021X, UNSPEC_802_1X);
+	__AKM(PSK, PSK_OVER_802_1X);
+	__AKM(FT_IEEE8021X, FT_802_1X);
+	__AKM(FT_PSK, FT_PSK);
+	__AKM(IEEE8021X_SHA256, 802_1X_SHA256);
+	__AKM(PSK_SHA256, PSK_SHA256);
+	__AKM(SAE, SAE);
+	__AKM(SAE_EXT_KEY, SAE_EXT_KEY);
+	__AKM(FT_SAE, FT_SAE);
+	__AKM(FT_SAE_EXT_KEY, FT_SAE_EXT_KEY);
+	__AKM(CCKM, CCKM);
+	__AKM(OSEN, OSEN);
+	__AKM(IEEE8021X_SUITE_B, 802_1X_SUITE_B);
+	__AKM(IEEE8021X_SUITE_B_192, 802_1X_SUITE_B_192);
+	__AKM(FILS_SHA256, FILS_SHA256);
+	__AKM(FILS_SHA384, FILS_SHA384);
+	__AKM(FT_FILS_SHA256, FT_FILS_SHA256);
+	__AKM(FT_FILS_SHA384, FT_FILS_SHA384);
+	__AKM(OWE, OWE);
+	__AKM(DPP, DPP);
+	__AKM(FT_IEEE8021X_SHA384, FT_802_1X_SHA384);
+#undef __AKM
+
+	return num_suites;
+}
+
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+static int issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv,
+				  const u8 *key, size_t key_len)
+{
+	struct nl_msg *msg;
+	int ret;
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD))
+		return 0;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY) ||
+	    nla_put(msg, NL80211_ATTR_VENDOR_DATA, key_len, key)) {
+		nl80211_nlmsg_clear(msg);
+		nlmsg_free(msg);
+		return -1;
+	}
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Key management set key failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+	}
+
+	return ret;
+}
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
+static int nl80211_set_pmk(struct wpa_driver_nl80211_data *drv,
+			   const u8 *key, size_t key_len,
+			   const u8 *addr)
+{
+	struct nl_msg *msg = NULL;
+	int ret;
+
+	/*
+	 * If the authenticator address is not set, assume it is
+	 * the current BSSID.
+	 */
+	if (!addr && drv->associated)
+		addr = drv->bssid;
+	else if (!addr)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Set PMK to the driver for " MACSTR,
+		   MAC2STR(addr));
+	wpa_hexdump_key(MSG_DEBUG, "nl80211: PMK", key, key_len);
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_PMK);
+	if (!msg ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+	    nla_put(msg, NL80211_ATTR_PMK, key_len, key)) {
+		nl80211_nlmsg_clear(msg);
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Set PMK failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+	}
+
+	return ret;
+}
+
+
+static int wpa_driver_nl80211_set_key(struct i802_bss *bss,
+				      struct wpa_driver_set_key_params *params)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int ifindex;
+	struct nl_msg *msg;
+	struct nl_msg *key_msg;
+	int ret;
+	int skip_set_key = 1;
+	const char *ifname = params->ifname;
+	enum wpa_alg alg = params->alg;
+	const u8 *addr = params->addr;
+	int key_idx = params->key_idx;
+	int set_tx = params->set_tx;
+	const u8 *seq = params->seq;
+	size_t seq_len = params->seq_len;
+	const u8 *key = params->key;
+	size_t key_len = params->key_len;
+	int vlan_id = params->vlan_id;
+	enum key_flag key_flag = params->key_flag;
+	int link_id = params->link_id;
+
+	/* Ignore for P2P Device */
+	if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE)
+		return 0;
+
+	ifindex = if_nametoindex(ifname);
+	wpa_printf(MSG_DEBUG, "%s: ifindex=%d (%s) alg=%d addr=%p key_idx=%d "
+		   "set_tx=%d seq_len=%lu key_len=%lu key_flag=0x%x link_id=%d",
+		   __func__, ifindex, ifname, alg, addr, key_idx, set_tx,
+		   (unsigned long) seq_len, (unsigned long) key_len, key_flag,
+		   link_id);
+
+	if (check_key_flag(key_flag)) {
+		wpa_printf(MSG_DEBUG, "%s: invalid key_flag", __func__);
+		return -EINVAL;
+	}
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+	if ((key_flag & KEY_FLAG_PMK) &&
+	    (drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) {
+		wpa_printf(MSG_DEBUG, "%s: calling issue_key_mgmt_set_key",
+			   __func__);
+		ret = issue_key_mgmt_set_key(drv, key, key_len);
+		return ret;
+	}
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+	if (key_flag & KEY_FLAG_PMK) {
+		if (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X)
+			return nl80211_set_pmk(drv, key, key_len, addr);
+		/* The driver does not have any offload mechanism for PMK, so
+		 * there is no need to configure this key. */
+		return 0;
+	}
+
+	ret = -ENOBUFS;
+	key_msg = nlmsg_alloc();
+	if (!key_msg)
+		return ret;
+
+	if ((key_flag & KEY_FLAG_PAIRWISE_MASK) ==
+	    KEY_FLAG_PAIRWISE_RX_TX_MODIFY) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: SET_KEY (pairwise RX/TX modify)");
+		msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_SET_KEY);
+		if (!msg)
+			goto fail2;
+	} else if (alg == WPA_ALG_NONE && (key_flag & KEY_FLAG_RX_TX)) {
+		wpa_printf(MSG_DEBUG, "%s: invalid key_flag to delete key",
+			   __func__);
+		ret = -EINVAL;
+		goto fail2;
+	} else if (alg == WPA_ALG_NONE) {
+		wpa_printf(MSG_DEBUG, "nl80211: DEL_KEY");
+		msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_DEL_KEY);
+		if (!msg)
+			goto fail2;
+	} else {
+		u32 suite;
+
+		suite = wpa_alg_to_cipher_suite(alg, key_len);
+		if (!suite) {
+			ret = -EINVAL;
+			goto fail2;
+		}
+		wpa_printf(MSG_DEBUG, "nl80211: NEW_KEY");
+		msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_NEW_KEY);
+		if (!msg)
+			goto fail2;
+		if (nla_put(key_msg, NL80211_KEY_DATA, key_len, key) ||
+		    nla_put_u32(key_msg, NL80211_KEY_CIPHER, suite))
+			goto fail;
+		wpa_hexdump_key(MSG_DEBUG, "nl80211: KEY_DATA", key, key_len);
+
+		if (seq && seq_len) {
+			if (nla_put(key_msg, NL80211_KEY_SEQ, seq_len, seq))
+				goto fail;
+			wpa_hexdump(MSG_DEBUG, "nl80211: KEY_SEQ",
+				    seq, seq_len);
+		}
+	}
+
+	if (addr && !is_broadcast_ether_addr(addr)) {
+		wpa_printf(MSG_DEBUG, "   addr=" MACSTR, MAC2STR(addr));
+		if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
+			goto fail;
+
+		if ((key_flag & KEY_FLAG_PAIRWISE_MASK) ==
+		    KEY_FLAG_PAIRWISE_RX ||
+		    (key_flag & KEY_FLAG_PAIRWISE_MASK) ==
+		    KEY_FLAG_PAIRWISE_RX_TX_MODIFY) {
+			if (nla_put_u8(key_msg, NL80211_KEY_MODE,
+				       key_flag == KEY_FLAG_PAIRWISE_RX ?
+				       NL80211_KEY_NO_TX : NL80211_KEY_SET_TX))
+				goto fail;
+		} else if ((key_flag & KEY_FLAG_GROUP_MASK) ==
+			   KEY_FLAG_GROUP_RX) {
+			wpa_printf(MSG_DEBUG, "   RSN IBSS RX GTK");
+			if (nla_put_u32(key_msg, NL80211_KEY_TYPE,
+					NL80211_KEYTYPE_GROUP))
+				goto fail;
+		} else if (!(key_flag & KEY_FLAG_PAIRWISE)) {
+			wpa_printf(MSG_DEBUG,
+				   "   key_flag missing PAIRWISE when setting a pairwise key");
+			ret = -EINVAL;
+			goto fail;
+		} else if (alg == WPA_ALG_WEP &&
+			   (key_flag & KEY_FLAG_RX_TX) == KEY_FLAG_RX_TX) {
+			wpa_printf(MSG_DEBUG, "   unicast WEP key");
+			skip_set_key = 0;
+		} else {
+			wpa_printf(MSG_DEBUG, "   pairwise key");
+		}
+	} else if ((key_flag & KEY_FLAG_PAIRWISE) ||
+		   !(key_flag & KEY_FLAG_GROUP)) {
+		wpa_printf(MSG_DEBUG,
+			   "   invalid key_flag for a broadcast key");
+		ret = -EINVAL;
+		goto fail;
+	} else {
+		wpa_printf(MSG_DEBUG, "   broadcast key");
+		if (key_flag & KEY_FLAG_DEFAULT)
+			skip_set_key = 0;
+	}
+	if (nla_put_u8(key_msg, NL80211_KEY_IDX, key_idx) ||
+	    nla_put_nested(msg, NL80211_ATTR_KEY, key_msg))
+		goto fail;
+	nl80211_nlmsg_clear(key_msg);
+	nlmsg_free(key_msg);
+	key_msg = NULL;
+
+	if (vlan_id && (drv->capa.flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD)) {
+		wpa_printf(MSG_DEBUG, "nl80211: VLAN ID %d", vlan_id);
+		if (nla_put_u16(msg, NL80211_ATTR_VLAN_ID, vlan_id))
+			goto fail;
+	}
+
+	if (link_id != -1) {
+		wpa_printf(MSG_DEBUG, "nl80211: Link ID %d", link_id);
+		if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))
+			goto fail;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if ((ret == -ENOENT || ret == -ENOLINK) && alg == WPA_ALG_NONE)
+		ret = 0;
+	if (ret)
+		wpa_printf(MSG_DEBUG, "nl80211: set_key failed; err=%d %s",
+			   ret, strerror(-ret));
+
+	/*
+	 * If we failed or don't need to set the key as default (below),
+	 * we're done here.
+	 */
+	if (ret || skip_set_key)
+		return ret;
+	wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_SET_KEY - default key");
+
+	ret = -ENOBUFS;
+	key_msg = nlmsg_alloc();
+	if (!key_msg)
+		return ret;
+
+	msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_SET_KEY);
+	if (!msg)
+		goto fail2;
+	if (!key_msg ||
+	    nla_put_u8(key_msg, NL80211_KEY_IDX, key_idx) ||
+	    nla_put_flag(key_msg, wpa_alg_bip(alg) ?
+			 (key_idx == 6 || key_idx == 7 ?
+			  NL80211_KEY_DEFAULT_BEACON :
+			  NL80211_KEY_DEFAULT_MGMT) :
+			 NL80211_KEY_DEFAULT))
+		goto fail;
+	if (addr && is_broadcast_ether_addr(addr)) {
+		struct nlattr *types;
+
+		types = nla_nest_start(key_msg, NL80211_KEY_DEFAULT_TYPES);
+		if (!types ||
+		    nla_put_flag(key_msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST))
+			goto fail;
+		nla_nest_end(key_msg, types);
+	} else if (addr) {
+		struct nlattr *types;
+
+		types = nla_nest_start(key_msg, NL80211_KEY_DEFAULT_TYPES);
+		if (!types ||
+		    nla_put_flag(key_msg, NL80211_KEY_DEFAULT_TYPE_UNICAST))
+			goto fail;
+		nla_nest_end(key_msg, types);
+	}
+
+	if (nla_put_nested(msg, NL80211_ATTR_KEY, key_msg))
+		goto fail;
+	nl80211_nlmsg_clear(key_msg);
+	nlmsg_free(key_msg);
+	key_msg = NULL;
+
+	if (vlan_id && (drv->capa.flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD)) {
+		wpa_printf(MSG_DEBUG, "nl80211: set_key default - VLAN ID %d",
+			   vlan_id);
+		if (nla_put_u16(msg, NL80211_ATTR_VLAN_ID, vlan_id))
+			goto fail;
+	}
+
+	if (link_id != -1) {
+		wpa_printf(MSG_DEBUG, "nl80211: set_key default - Link ID %d",
+			   link_id);
+		if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))
+			goto fail;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret)
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: set_key default failed; err=%d %s",
+			   ret, strerror(-ret));
+	return ret;
+
+fail:
+	nl80211_nlmsg_clear(msg);
+	nlmsg_free(msg);
+fail2:
+	nl80211_nlmsg_clear(key_msg);
+	nlmsg_free(key_msg);
+	return ret;
+}
+
+
+static int nl_add_key(struct nl_msg *msg, enum wpa_alg alg,
+		      int key_idx, int defkey,
+		      const u8 *seq, size_t seq_len,
+		      const u8 *key, size_t key_len)
+{
+	struct nlattr *key_attr = nla_nest_start(msg, NL80211_ATTR_KEY);
+	u32 suite;
+
+	if (!key_attr)
+		return -1;
+
+	suite = wpa_alg_to_cipher_suite(alg, key_len);
+	if (!suite)
+		return -1;
+
+	if (defkey && wpa_alg_bip(alg)) {
+		if (nla_put_flag(msg, NL80211_KEY_DEFAULT_MGMT))
+			return -1;
+	} else if (defkey) {
+		if (nla_put_flag(msg, NL80211_KEY_DEFAULT))
+			return -1;
+	}
+
+	if (nla_put_u8(msg, NL80211_KEY_IDX, key_idx) ||
+	    nla_put_u32(msg, NL80211_KEY_CIPHER, suite) ||
+	    (seq && seq_len &&
+	     nla_put(msg, NL80211_KEY_SEQ, seq_len, seq)) ||
+	    nla_put(msg, NL80211_KEY_DATA, key_len, key))
+		return -1;
+
+	nla_nest_end(msg, key_attr);
+
+	return 0;
+}
+
+
+static int nl80211_set_conn_keys(struct wpa_driver_associate_params *params,
+				 struct nl_msg *msg)
+{
+	int i, privacy = 0;
+	struct nlattr *nl_keys, *nl_key;
+
+	for (i = 0; i < 4; i++) {
+		if (!params->wep_key[i])
+			continue;
+		privacy = 1;
+		break;
+	}
+	if (params->wps == WPS_MODE_PRIVACY)
+		privacy = 1;
+	if (params->pairwise_suite &&
+	    params->pairwise_suite != WPA_CIPHER_NONE)
+		privacy = 1;
+
+	if (!privacy)
+		return 0;
+
+	if (nla_put_flag(msg, NL80211_ATTR_PRIVACY))
+		return -ENOBUFS;
+
+	nl_keys = nla_nest_start(msg, NL80211_ATTR_KEYS);
+	if (!nl_keys)
+		return -ENOBUFS;
+
+	for (i = 0; i < 4; i++) {
+		if (!params->wep_key[i])
+			continue;
+
+		nl_key = nla_nest_start(msg, i);
+		if (!nl_key ||
+		    nla_put(msg, NL80211_KEY_DATA, params->wep_key_len[i],
+			    params->wep_key[i]) ||
+		    nla_put_u32(msg, NL80211_KEY_CIPHER,
+				params->wep_key_len[i] == 5 ?
+				RSN_CIPHER_SUITE_WEP40 :
+				RSN_CIPHER_SUITE_WEP104) ||
+		    nla_put_u8(msg, NL80211_KEY_IDX, i) ||
+		    (i == params->wep_tx_keyidx &&
+		     nla_put_flag(msg, NL80211_KEY_DEFAULT)))
+			return -ENOBUFS;
+
+		nla_nest_end(msg, nl_key);
+	}
+	nla_nest_end(msg, nl_keys);
+
+	return 0;
+}
+
+
+int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
+			    const u8 *addr, int cmd, u16 reason_code,
+			    int local_state_change,
+			    struct i802_bss *bss)
+{
+	int ret;
+	struct nl_msg *msg;
+	struct nl_sock *nl_connect = get_connect_handle(bss);
+
+	if (!(msg = nl80211_drv_msg(drv, 0, cmd)) ||
+	    nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code) ||
+	    (addr && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) ||
+	    (local_state_change &&
+	     nla_put_flag(msg, NL80211_ATTR_LOCAL_STATE_CHANGE))) {
+		nlmsg_free(msg);
+		return -1;
+	}
+
+	if (nl_connect)
+		ret = send_and_recv(drv->global, nl_connect, msg,
+				    process_bss_event, bss, NULL, NULL);
+	else
+		ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret) {
+		wpa_dbg(drv->ctx, MSG_DEBUG,
+			"nl80211: MLME command failed: reason=%u ret=%d (%s)",
+			reason_code, ret, strerror(-ret));
+	}
+	return ret;
+}
+
+
+static int wpa_driver_nl80211_disconnect(struct wpa_driver_nl80211_data *drv,
+					 u16 reason_code,
+					 struct i802_bss *bss)
+{
+	int ret;
+	int drv_associated = drv->associated;
+
+	wpa_printf(MSG_DEBUG, "%s(reason_code=%d)", __func__, reason_code);
+	nl80211_mark_disconnected(drv);
+	/* Disconnect command doesn't need BSSID - it uses cached value */
+	ret = wpa_driver_nl80211_mlme(drv, NULL, NL80211_CMD_DISCONNECT,
+				      reason_code, 0, bss);
+	/*
+	 * For locally generated disconnect, supplicant already generates a
+	 * DEAUTH event, so ignore the event from NL80211.
+	 */
+	drv->ignore_next_local_disconnect = drv_associated && (ret == 0);
+
+	return ret;
+}
+
+
+static int wpa_driver_nl80211_deauthenticate(struct i802_bss *bss,
+					     const u8 *addr, u16 reason_code)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int ret;
+	int drv_associated = drv->associated;
+
+	if (drv->nlmode == NL80211_IFTYPE_ADHOC) {
+		nl80211_mark_disconnected(drv);
+		return nl80211_leave_ibss(drv, 1);
+	}
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) {
+		return wpa_driver_nl80211_disconnect(drv, reason_code, bss);
+	}
+	wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)",
+		   __func__, MAC2STR(addr), reason_code);
+	nl80211_mark_disconnected(drv);
+	ret = wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE,
+				      reason_code, 0, bss);
+	/*
+	 * For locally generated deauthenticate, supplicant already generates a
+	 * DEAUTH event, so ignore the event from NL80211.
+	 */
+	drv->ignore_next_local_deauth = drv_associated && (ret == 0);
+
+	return ret;
+}
+
+
+static void nl80211_copy_auth_params(struct wpa_driver_nl80211_data *drv,
+				     struct wpa_driver_auth_params *params)
+{
+	int i;
+
+	drv->auth_freq = params->freq;
+	drv->auth_alg = params->auth_alg;
+	drv->auth_wep_tx_keyidx = params->wep_tx_keyidx;
+	drv->auth_local_state_change = params->local_state_change;
+	drv->auth_p2p = params->p2p;
+
+	if (params->bssid)
+		os_memcpy(drv->auth_bssid_, params->bssid, ETH_ALEN);
+	else
+		os_memset(drv->auth_bssid_, 0, ETH_ALEN);
+
+	if (params->ssid) {
+		os_memcpy(drv->auth_ssid, params->ssid, params->ssid_len);
+		drv->auth_ssid_len = params->ssid_len;
+	} else
+		drv->auth_ssid_len = 0;
+
+
+	os_free(drv->auth_ie);
+	drv->auth_ie = NULL;
+	drv->auth_ie_len = 0;
+	if (params->ie) {
+		drv->auth_ie = os_malloc(params->ie_len);
+		if (drv->auth_ie) {
+			os_memcpy(drv->auth_ie, params->ie, params->ie_len);
+			drv->auth_ie_len = params->ie_len;
+		}
+	}
+
+	if (params->mld && params->ap_mld_addr) {
+		drv->auth_mld = params->mld;
+		drv->auth_mld_link_id = params->mld_link_id;
+		os_memcpy(drv->auth_ap_mld_addr, params->ap_mld_addr, ETH_ALEN);
+	} else {
+		drv->auth_mld = false;
+		drv->auth_mld_link_id = -1;
+	}
+
+	os_free(drv->auth_data);
+	drv->auth_data = NULL;
+	drv->auth_data_len = 0;
+	if (params->auth_data) {
+		drv->auth_data = os_memdup(params->auth_data,
+					   params->auth_data_len);
+		if (drv->auth_data)
+			drv->auth_data_len = params->auth_data_len;
+	}
+
+	for (i = 0; i < 4; i++) {
+		if (params->wep_key[i] && params->wep_key_len[i] &&
+		    params->wep_key_len[i] <= 16) {
+			os_memcpy(drv->auth_wep_key[i], params->wep_key[i],
+				  params->wep_key_len[i]);
+			drv->auth_wep_key_len[i] = params->wep_key_len[i];
+		} else
+			drv->auth_wep_key_len[i] = 0;
+	}
+}
+
+
+static void nl80211_unmask_11b_rates(struct i802_bss *bss)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	if (is_p2p_net_interface(drv->nlmode) || !drv->disabled_11b_rates)
+		return;
+
+	/*
+	 * Looks like we failed to unmask 11b rates previously. This could
+	 * happen, e.g., if the interface was down at the point in time when a
+	 * P2P group was terminated.
+	 */
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Interface %s mode is for non-P2P, but 11b rates were disabled - re-enable them",
+		   bss->ifname);
+	nl80211_disable_11b_rates(drv, drv->ifindex, 0);
+}
+
+
+static enum nl80211_auth_type get_nl_auth_type(int wpa_auth_alg)
+{
+	if (wpa_auth_alg & WPA_AUTH_ALG_OPEN)
+		return NL80211_AUTHTYPE_OPEN_SYSTEM;
+	if (wpa_auth_alg & WPA_AUTH_ALG_SHARED)
+		return NL80211_AUTHTYPE_SHARED_KEY;
+	if (wpa_auth_alg & WPA_AUTH_ALG_LEAP)
+		return NL80211_AUTHTYPE_NETWORK_EAP;
+	if (wpa_auth_alg & WPA_AUTH_ALG_FT)
+		return NL80211_AUTHTYPE_FT;
+	if (wpa_auth_alg & WPA_AUTH_ALG_SAE)
+		return NL80211_AUTHTYPE_SAE;
+	if (wpa_auth_alg & WPA_AUTH_ALG_FILS)
+		return NL80211_AUTHTYPE_FILS_SK;
+	if (wpa_auth_alg & WPA_AUTH_ALG_FILS_SK_PFS)
+		return NL80211_AUTHTYPE_FILS_SK_PFS;
+
+	return NL80211_AUTHTYPE_MAX;
+}
+
+
+static int wpa_driver_nl80211_authenticate(
+	struct i802_bss *bss, struct wpa_driver_auth_params *params)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int ret = -1, i;
+	struct nl_msg *msg;
+	enum nl80211_auth_type type;
+	enum nl80211_iftype nlmode;
+	int count = 0;
+	int is_retry;
+	struct wpa_driver_set_key_params p;
+
+	nl80211_unmask_11b_rates(bss);
+
+	is_retry = drv->retry_auth;
+	drv->retry_auth = 0;
+	drv->ignore_deauth_event = 0;
+
+	nl80211_mark_disconnected(drv);
+	os_memset(drv->auth_bssid, 0, ETH_ALEN);
+	if (params->bssid)
+		os_memcpy(drv->auth_attempt_bssid, params->bssid, ETH_ALEN);
+	else
+		os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN);
+	/* FIX: IBSS mode */
+	nlmode = params->p2p ?
+		NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION;
+	if (drv->nlmode != nlmode &&
+	    wpa_driver_nl80211_set_mode(bss, nlmode) < 0)
+		return -1;
+
+retry:
+	wpa_printf(MSG_DEBUG, "nl80211: Authenticate (ifindex=%d)",
+		   drv->ifindex);
+
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_AUTHENTICATE);
+	if (!msg)
+		goto fail;
+
+	os_memset(&p, 0, sizeof(p));
+	p.ifname = bss->ifname;
+	p.alg = WPA_ALG_WEP;
+	p.link_id = -1;
+	for (i = 0; i < 4; i++) {
+		if (!params->wep_key[i])
+			continue;
+		p.key_idx = i;
+		p.set_tx = i == params->wep_tx_keyidx;
+		p.key = params->wep_key[i];
+		p.key_len = params->wep_key_len[i];
+		p.key_flag = i == params->wep_tx_keyidx ?
+			KEY_FLAG_GROUP_RX_TX_DEFAULT :
+			KEY_FLAG_GROUP_RX_TX;
+		wpa_driver_nl80211_set_key(bss, &p);
+		if (params->wep_tx_keyidx != i)
+			continue;
+		if (nl_add_key(msg, WPA_ALG_WEP, i, 1, NULL, 0,
+			       params->wep_key[i], params->wep_key_len[i]))
+			goto fail;
+	}
+
+	if (params->bssid) {
+		wpa_printf(MSG_DEBUG, "  * bssid=" MACSTR,
+			   MAC2STR(params->bssid));
+		if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid))
+			goto fail;
+	}
+	if (params->freq) {
+		wpa_printf(MSG_DEBUG, "  * freq=%d", params->freq);
+		if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq))
+			goto fail;
+	}
+	if (params->ssid) {
+		wpa_printf(MSG_DEBUG, "  * SSID=%s",
+			   wpa_ssid_txt(params->ssid, params->ssid_len));
+		if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len,
+			    params->ssid))
+			goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "  * IEs", params->ie, params->ie_len);
+	if (params->ie &&
+	    nla_put(msg, NL80211_ATTR_IE, params->ie_len, params->ie))
+		goto fail;
+	if (params->auth_data) {
+		wpa_hexdump(MSG_DEBUG, "  * auth_data", params->auth_data,
+			    params->auth_data_len);
+		if (nla_put(msg, NL80211_ATTR_SAE_DATA, params->auth_data_len,
+			    params->auth_data))
+			goto fail;
+	}
+	type = get_nl_auth_type(params->auth_alg);
+	wpa_printf(MSG_DEBUG, "  * Auth Type %d", type);
+	if (type == NL80211_AUTHTYPE_MAX ||
+	    nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
+		goto fail;
+	if (params->local_state_change) {
+		wpa_printf(MSG_DEBUG, "  * Local state change only");
+		if (nla_put_flag(msg, NL80211_ATTR_LOCAL_STATE_CHANGE))
+			goto fail;
+	}
+
+	if (params->mld && params->ap_mld_addr) {
+		wpa_printf(MSG_DEBUG, "  * MLD: link_id=%u, MLD addr=" MACSTR,
+			   params->mld_link_id, MAC2STR(params->ap_mld_addr));
+
+		if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
+			       params->mld_link_id) ||
+		    nla_put(msg, NL80211_ATTR_MLD_ADDR, ETH_ALEN,
+			    params->ap_mld_addr))
+			goto fail;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_dbg(drv->ctx, MSG_DEBUG,
+			"nl80211: MLME command failed (auth): count=%d ret=%d (%s)",
+			count, ret, strerror(-ret));
+		count++;
+		if ((ret == -EALREADY || ret == -EEXIST) && count == 1 &&
+		    params->bssid && !params->local_state_change) {
+			/*
+			 * mac80211 does not currently accept new
+			 * authentication if we are already authenticated. As a
+			 * workaround, force deauthentication and try again.
+			 */
+			wpa_printf(MSG_DEBUG, "nl80211: Retry authentication "
+				   "after forced deauthentication");
+			drv->ignore_deauth_event = 1;
+			wpa_driver_nl80211_deauthenticate(
+				bss, params->bssid,
+				WLAN_REASON_PREV_AUTH_NOT_VALID);
+			nlmsg_free(msg);
+			goto retry;
+		}
+
+		if (ret == -ENOENT && params->freq && !is_retry) {
+			/*
+			 * cfg80211 has likely expired the BSS entry even
+			 * though it was previously available in our internal
+			 * BSS table. To recover quickly, start a single
+			 * channel scan on the specified channel.
+			 */
+			struct wpa_driver_scan_params scan;
+			int freqs[2];
+
+			os_memset(&scan, 0, sizeof(scan));
+			scan.num_ssids = 1;
+			if (params->ssid) {
+				scan.ssids[0].ssid = params->ssid;
+				scan.ssids[0].ssid_len = params->ssid_len;
+			}
+			freqs[0] = params->freq;
+			freqs[1] = 0;
+			scan.freqs = freqs;
+			wpa_printf(MSG_DEBUG, "nl80211: Trigger single "
+				   "channel scan to refresh cfg80211 BSS "
+				   "entry");
+			ret = wpa_driver_nl80211_scan(bss, &scan);
+			if (ret == 0) {
+				nl80211_copy_auth_params(drv, params);
+				drv->scan_for_auth = 1;
+			}
+		} else if (is_retry) {
+			/*
+			 * Need to indicate this with an event since the return
+			 * value from the retry is not delivered to core code.
+			 */
+			union wpa_event_data event;
+			wpa_printf(MSG_DEBUG, "nl80211: Authentication retry "
+				   "failed");
+			os_memset(&event, 0, sizeof(event));
+			os_memcpy(event.timeout_event.addr, drv->auth_bssid_,
+				  ETH_ALEN);
+			wpa_supplicant_event(drv->ctx, EVENT_AUTH_TIMED_OUT,
+					     &event);
+		}
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Authentication request send successfully");
+	}
+
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+int wpa_driver_nl80211_authenticate_retry(struct wpa_driver_nl80211_data *drv)
+{
+	struct wpa_driver_auth_params params;
+	struct i802_bss *bss = drv->first_bss;
+	int i;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Try to authenticate again");
+
+	os_memset(&params, 0, sizeof(params));
+	params.freq = drv->auth_freq;
+	params.auth_alg = drv->auth_alg;
+	params.wep_tx_keyidx = drv->auth_wep_tx_keyidx;
+	params.local_state_change = drv->auth_local_state_change;
+	params.p2p = drv->auth_p2p;
+
+	if (!is_zero_ether_addr(drv->auth_bssid_))
+		params.bssid = drv->auth_bssid_;
+
+	if (drv->auth_ssid_len) {
+		params.ssid = drv->auth_ssid;
+		params.ssid_len = drv->auth_ssid_len;
+	}
+
+	params.ie = drv->auth_ie;
+	params.ie_len = drv->auth_ie_len;
+	params.auth_data = drv->auth_data;
+	params.auth_data_len = drv->auth_data_len;
+	params.mld = drv->auth_mld;
+	params.mld_link_id = drv->auth_mld_link_id;
+	if (drv->auth_mld)
+		params.ap_mld_addr = drv->auth_ap_mld_addr;
+
+	for (i = 0; i < 4; i++) {
+		if (drv->auth_wep_key_len[i]) {
+			params.wep_key[i] = drv->auth_wep_key[i];
+			params.wep_key_len[i] = drv->auth_wep_key_len[i];
+		}
+	}
+
+	drv->retry_auth = 1;
+	return wpa_driver_nl80211_authenticate(bss, &params);
+}
+
+
+struct i802_link * nl80211_get_link(struct i802_bss *bss, s8 link_id)
+{
+	unsigned int i;
+
+	for (i = 0; i < bss->n_links; i++) {
+		if (bss->links[i].link_id != link_id)
+			continue;
+
+		return &bss->links[i];
+	}
+
+	return bss->flink;
+}
+
+
+static void nl80211_link_set_freq(struct i802_bss *bss, s8 link_id, int freq)
+{
+	struct i802_link *link = nl80211_get_link(bss, link_id);
+
+	link->freq = freq;
+}
+
+
+static int nl80211_get_link_freq(struct i802_bss *bss, const u8 *addr,
+				 bool bss_freq_debug)
+{
+	size_t i;
+
+	for (i = 0; i < bss->n_links; i++) {
+		if (os_memcmp(bss->links[i].addr, addr, ETH_ALEN) == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Use link freq=%d for address "
+				   MACSTR,
+				   bss->links[i].freq, MAC2STR(addr));
+			return bss->links[i].freq;
+		}
+	}
+
+	if (bss_freq_debug)
+		wpa_printf(MSG_DEBUG, "nl80211: Use bss->freq=%d",
+			   bss->flink->freq);
+
+	return bss->flink->freq;
+}
+
+
+static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data,
+					size_t data_len, int noack,
+					unsigned int freq, int no_cck,
+					int offchanok,
+					unsigned int wait_time,
+					const u16 *csa_offs,
+					size_t csa_offs_len, int no_encrypt,
+					int link_id)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct ieee80211_mgmt *mgmt;
+	int encrypt = !no_encrypt;
+	u16 fc;
+	int use_cookie = 1;
+	int res;
+	struct i802_link *link = nl80211_get_link(bss, link_id);
+
+	mgmt = (struct ieee80211_mgmt *) data;
+	fc = le_to_host16(mgmt->frame_control);
+	wpa_printf(MSG_DEBUG, "nl80211: send_mlme - da=" MACSTR
+		   " noack=%d freq=%u no_cck=%d offchanok=%d wait_time=%u no_encrypt=%d fc=0x%x (%s) nlmode=%d",
+		   MAC2STR(mgmt->da), noack, freq, no_cck, offchanok, wait_time,
+		   no_encrypt, fc, fc2str(fc), drv->nlmode);
+
+	if ((is_sta_interface(drv->nlmode) ||
+	     drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) &&
+	    WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+	    WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_RESP) {
+		/*
+		 * The use of last_mgmt_freq is a bit of a hack,
+		 * but it works due to the single-threaded nature
+		 * of wpa_supplicant.
+		 */
+		if (freq == 0) {
+			wpa_printf(MSG_DEBUG, "nl80211: Use last_mgmt_freq=%d",
+				   drv->last_mgmt_freq);
+			freq = drv->last_mgmt_freq;
+		}
+		wait_time = 0;
+		use_cookie = 0;
+		no_cck = 1;
+		offchanok = 1;
+		goto send_frame_cmd;
+	}
+
+	if (drv->device_ap_sme && is_ap_interface(drv->nlmode)) {
+		unsigned int link_freq = nl80211_get_link_freq(bss, mgmt->sa,
+							       !freq);
+
+		if (!freq)
+			freq = link_freq;
+
+		if (freq == link_freq)
+			wait_time = 0;
+
+		goto send_frame_cmd;
+	}
+
+	if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+	    WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) {
+		/*
+		 * Only one of the authentication frame types is encrypted.
+		 * In order for static WEP encryption to work properly (i.e.,
+		 * to not encrypt the frame), we need to tell mac80211 about
+		 * the frames that must not be encrypted.
+		 */
+		u16 auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
+		u16 auth_trans = le_to_host16(mgmt->u.auth.auth_transaction);
+		if (auth_alg != WLAN_AUTH_SHARED_KEY || auth_trans != 3)
+			encrypt = 0;
+	}
+
+	if (is_sta_interface(drv->nlmode) &&
+	    WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+	    WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) {
+		if (freq == 0 &&
+		    (drv->capa.flags & WPA_DRIVER_FLAGS_SAE) &&
+		    !(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) {
+			freq = nl80211_get_assoc_freq(drv);
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: send_mlme - Use assoc_freq=%u for external auth",
+				   freq);
+		}
+
+		/* Allow off channel for PASN authentication */
+		if (data_len >= IEEE80211_HDRLEN + 2 &&
+		    WPA_GET_LE16(data + IEEE80211_HDRLEN) == WLAN_AUTH_PASN &&
+		    !offchanok) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: send_mlme: allow off channel for PASN");
+			offchanok = 1;
+		}
+	}
+
+#ifdef CONFIG_PASN
+	if (is_sta_interface(drv->nlmode) &&
+	    WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+	     WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_DEAUTH) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: send_mlme: allow Deauthentication frame for PASN");
+
+		use_cookie = 0;
+		offchanok = 1;
+		goto send_frame_cmd;
+	}
+#endif /* CONFIG_PASN */
+
+	if (freq == 0 && drv->nlmode == NL80211_IFTYPE_ADHOC) {
+		freq = nl80211_get_assoc_freq(drv);
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: send_mlme - Use assoc_freq=%u for IBSS",
+			   freq);
+	}
+	if (freq == 0) {
+		wpa_printf(MSG_DEBUG, "nl80211: send_mlme - Use bss->freq=%u",
+			   link->freq);
+		freq = link->freq;
+	}
+
+	if (drv->use_monitor && is_ap_interface(drv->nlmode)) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: send_frame(freq=%u bss->freq=%u) -> send_monitor",
+			   freq, link->freq);
+		return nl80211_send_monitor(drv, data, data_len, encrypt,
+					    noack);
+	}
+
+	if ((noack || WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT ||
+	     WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION) &&
+	    link_id == NL80211_DRV_LINK_ID_NA)
+		use_cookie = 0;
+send_frame_cmd:
+#ifdef CONFIG_TESTING_OPTIONS
+	if (no_encrypt && !encrypt && !drv->use_monitor) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Request to send an unencrypted frame - use a monitor interface for this");
+		if (nl80211_create_monitor_interface(drv) < 0)
+			return -1;
+		res = nl80211_send_monitor(drv, data, data_len, encrypt,
+					   noack);
+		nl80211_remove_monitor_interface(drv);
+		return res;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	wpa_printf(MSG_DEBUG, "nl80211: send_mlme -> send_frame_cmd");
+	res = nl80211_send_frame_cmd(bss, freq, wait_time, data, data_len,
+				     use_cookie, no_cck, noack, offchanok,
+				     csa_offs, csa_offs_len);
+	if (!res)
+		drv->send_frame_link_id = link_id;
+
+	return res;
+}
+
+
+static int nl80211_put_basic_rates(struct nl_msg *msg, const int *basic_rates)
+{
+	u8 rates[NL80211_MAX_SUPP_RATES];
+	u8 rates_len = 0;
+	int i;
+
+	if (!basic_rates)
+		return 0;
+
+	for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] >= 0; i++)
+		rates[rates_len++] = basic_rates[i] / 5;
+
+	return nla_put(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, rates);
+}
+
+
+static int nl80211_set_bss(struct i802_bss *bss, int cts, int preamble,
+			   int slot, int ht_opmode, int ap_isolate,
+			   const int *basic_rates)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_BSS)) ||
+	    (cts >= 0 &&
+	     nla_put_u8(msg, NL80211_ATTR_BSS_CTS_PROT, cts)) ||
+	    (preamble >= 0 &&
+	     nla_put_u8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble)) ||
+	    (slot >= 0 &&
+	     nla_put_u8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot)) ||
+	    (ht_opmode >= 0 &&
+	     nla_put_u16(msg, NL80211_ATTR_BSS_HT_OPMODE, ht_opmode)) ||
+	    (ap_isolate >= 0 &&
+	     nla_put_u8(msg, NL80211_ATTR_AP_ISOLATE, ap_isolate)) ||
+	    nl80211_put_basic_rates(msg, basic_rates)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+}
+
+
+static int wpa_driver_nl80211_set_acl(void *priv,
+				      struct hostapd_acl_params *params)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nl_msg *acl;
+	unsigned int i;
+	int ret;
+	size_t acl_nla_sz, acl_nlmsg_sz, nla_sz, nlmsg_sz;
+
+	if (!(drv->capa.max_acl_mac_addrs))
+		return -ENOTSUP;
+
+	if (params->num_mac_acl > drv->capa.max_acl_mac_addrs)
+		return -ENOTSUP;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Set %s ACL (num_mac_acl=%u)",
+		   params->acl_policy ? "Accept" : "Deny", params->num_mac_acl);
+
+	acl_nla_sz = nla_total_size(ETH_ALEN) * params->num_mac_acl;
+	acl_nlmsg_sz = nlmsg_total_size(acl_nla_sz);
+	acl = nlmsg_alloc_size(acl_nlmsg_sz);
+	if (!acl)
+		return -ENOMEM;
+	for (i = 0; i < params->num_mac_acl; i++) {
+		if (nla_put(acl, i + 1, ETH_ALEN, params->mac_acl[i].addr)) {
+			nlmsg_free(acl);
+			return -ENOMEM;
+		}
+	}
+
+	/*
+	 * genetlink message header (Length of user header is 0) +
+	 * u32 attr: NL80211_ATTR_IFINDEX +
+	 * u32 attr: NL80211_ATTR_ACL_POLICY +
+	 * nested acl attr
+	 */
+	nla_sz = GENL_HDRLEN +
+		nla_total_size(4) * 2 +
+		nla_total_size(acl_nla_sz);
+	nlmsg_sz = nlmsg_total_size(nla_sz);
+	if (!(msg = nl80211_ifindex_msg_build(drv, nlmsg_alloc_size(nlmsg_sz),
+					      drv->ifindex, 0,
+					      NL80211_CMD_SET_MAC_ACL)) ||
+	    nla_put_u32(msg, NL80211_ATTR_ACL_POLICY, params->acl_policy ?
+			NL80211_ACL_POLICY_DENY_UNLESS_LISTED :
+			NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED) ||
+	    nla_put_nested(msg, NL80211_ATTR_MAC_ADDRS, acl)) {
+		nlmsg_free(msg);
+		nlmsg_free(acl);
+		return -ENOMEM;
+	}
+	nlmsg_free(acl);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Failed to set MAC ACL: %d (%s)",
+			   ret, strerror(-ret));
+	}
+
+	return ret;
+}
+
+
+static int nl80211_put_beacon_int(struct nl_msg *msg, int beacon_int)
+{
+	if (beacon_int > 0) {
+		wpa_printf(MSG_DEBUG, "  * beacon_int=%d", beacon_int);
+		return nla_put_u32(msg, NL80211_ATTR_BEACON_INTERVAL,
+				   beacon_int);
+	}
+
+	return 0;
+}
+
+
+static int nl80211_put_dtim_period(struct nl_msg *msg, int dtim_period)
+{
+	if (dtim_period > 0) {
+		wpa_printf(MSG_DEBUG, "  * dtim_period=%d", dtim_period);
+		return nla_put_u32(msg, NL80211_ATTR_DTIM_PERIOD, dtim_period);
+	}
+
+	return 0;
+}
+
+
+#ifdef CONFIG_MESH
+static int nl80211_set_mesh_config(void *priv,
+				   struct wpa_driver_mesh_bss_params *params)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_MESH_CONFIG);
+	if (!msg)
+		return -1;
+
+	ret = nl80211_put_mesh_config(msg, params);
+	if (ret < 0) {
+		nlmsg_free(msg);
+		return ret;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Mesh config set failed: %d (%s)",
+			   ret, strerror(-ret));
+		return ret;
+	}
+	return 0;
+}
+#endif /* CONFIG_MESH */
+
+
+static int nl80211_put_beacon_rate(struct nl_msg *msg, u64 flags, u64 flags2,
+				   struct wpa_driver_ap_params *params)
+{
+	struct nlattr *bands, *band;
+	struct nl80211_txrate_vht vht_rate;
+	struct nl80211_txrate_he he_rate;
+
+	if (!params->freq ||
+	    (params->beacon_rate == 0 &&
+	     params->rate_type == BEACON_RATE_LEGACY))
+		return 0;
+
+	bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES);
+	if (!bands)
+		return -1;
+
+	switch (params->freq->mode) {
+	case HOSTAPD_MODE_IEEE80211B:
+	case HOSTAPD_MODE_IEEE80211G:
+		band = nla_nest_start(msg, NL80211_BAND_2GHZ);
+		break;
+	case HOSTAPD_MODE_IEEE80211A:
+		if (is_6ghz_freq(params->freq->freq))
+			band = nla_nest_start(msg, NL80211_BAND_6GHZ);
+		else
+			band = nla_nest_start(msg, NL80211_BAND_5GHZ);
+		break;
+	case HOSTAPD_MODE_IEEE80211AD:
+		band = nla_nest_start(msg, NL80211_BAND_60GHZ);
+		break;
+	default:
+		return 0;
+	}
+
+	if (!band)
+		return -1;
+
+	os_memset(&vht_rate, 0, sizeof(vht_rate));
+	os_memset(&he_rate, 0, sizeof(he_rate));
+
+	switch (params->rate_type) {
+	case BEACON_RATE_LEGACY:
+		if (!(flags & WPA_DRIVER_FLAGS_BEACON_RATE_LEGACY)) {
+			wpa_printf(MSG_INFO,
+				   "nl80211: Driver does not support setting Beacon frame rate (legacy)");
+			return -1;
+		}
+
+		if (nla_put_u8(msg, NL80211_TXRATE_LEGACY,
+			       (u8) params->beacon_rate / 5) ||
+		    nla_put(msg, NL80211_TXRATE_HT, 0, NULL) ||
+		    (params->freq->vht_enabled &&
+		     nla_put(msg, NL80211_TXRATE_VHT, sizeof(vht_rate),
+			     &vht_rate)))
+			return -1;
+
+		wpa_printf(MSG_DEBUG, " * beacon_rate = legacy:%u (* 100 kbps)",
+			   params->beacon_rate);
+		break;
+	case BEACON_RATE_HT:
+		if (!(flags & WPA_DRIVER_FLAGS_BEACON_RATE_HT)) {
+			wpa_printf(MSG_INFO,
+				   "nl80211: Driver does not support setting Beacon frame rate (HT)");
+			return -1;
+		}
+		if (nla_put(msg, NL80211_TXRATE_LEGACY, 0, NULL) ||
+		    nla_put_u8(msg, NL80211_TXRATE_HT, params->beacon_rate) ||
+		    (params->freq->vht_enabled &&
+		     nla_put(msg, NL80211_TXRATE_VHT, sizeof(vht_rate),
+			     &vht_rate)))
+			return -1;
+		wpa_printf(MSG_DEBUG, " * beacon_rate = HT-MCS %u",
+			   params->beacon_rate);
+		break;
+	case BEACON_RATE_VHT:
+		if (!(flags & WPA_DRIVER_FLAGS_BEACON_RATE_VHT)) {
+			wpa_printf(MSG_INFO,
+				   "nl80211: Driver does not support setting Beacon frame rate (VHT)");
+			return -1;
+		}
+		vht_rate.mcs[0] = BIT(params->beacon_rate);
+		if (nla_put(msg, NL80211_TXRATE_LEGACY, 0, NULL))
+			return -1;
+		if (nla_put(msg, NL80211_TXRATE_HT, 0, NULL))
+			return -1;
+		if (nla_put(msg, NL80211_TXRATE_VHT, sizeof(vht_rate),
+			    &vht_rate))
+			return -1;
+		wpa_printf(MSG_DEBUG, " * beacon_rate = VHT-MCS %u",
+			   params->beacon_rate);
+		break;
+	case BEACON_RATE_HE:
+		if (!(flags2 & WPA_DRIVER_FLAGS2_BEACON_RATE_HE)) {
+			wpa_printf(MSG_INFO,
+				   "nl80211: Driver does not support setting Beacon frame rate (HE)");
+			return -1;
+		}
+		he_rate.mcs[0] = BIT(params->beacon_rate);
+		if (nla_put(msg, NL80211_TXRATE_LEGACY, 0, NULL) ||
+		    nla_put(msg, NL80211_TXRATE_HT, 0, NULL) ||
+		    nla_put(msg, NL80211_TXRATE_VHT, sizeof(vht_rate),
+			    &vht_rate) ||
+		    nla_put(msg, NL80211_TXRATE_HE, sizeof(he_rate), &he_rate))
+			return -1;
+		wpa_printf(MSG_DEBUG, " * beacon_rate = HE-MCS %u",
+			   params->beacon_rate);
+		break;
+	}
+
+	nla_nest_end(msg, band);
+	nla_nest_end(msg, bands);
+
+	return 0;
+}
+
+
+static int nl80211_set_multicast_to_unicast(struct i802_bss *bss,
+					    int multicast_to_unicast)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_MULTICAST_TO_UNICAST);
+	if (!msg ||
+	    (multicast_to_unicast &&
+	     nla_put_flag(msg, NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED))) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Failed to build NL80211_CMD_SET_MULTICAST_TO_UNICAST msg for %s",
+			   bss->ifname);
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+
+	switch (ret) {
+	case 0:
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: multicast to unicast %s on interface %s",
+			   multicast_to_unicast ? "enabled" : "disabled",
+			   bss->ifname);
+		break;
+	case -EOPNOTSUPP:
+		if (!multicast_to_unicast)
+			break;
+		wpa_printf(MSG_INFO,
+			   "nl80211: multicast to unicast not supported on interface %s",
+			   bss->ifname);
+		break;
+	default:
+		wpa_printf(MSG_ERROR,
+			   "nl80211: %s multicast to unicast failed with %d (%s) on interface %s",
+			   multicast_to_unicast ? "enabling" : "disabling",
+			   ret, strerror(-ret), bss->ifname);
+		break;
+	}
+
+	return ret;
+}
+
+
+#ifdef CONFIG_SAE
+static int nl80211_put_sae_pwe(struct nl_msg *msg, enum sae_pwe pwe)
+{
+	u8 sae_pwe;
+
+	wpa_printf(MSG_DEBUG, "nl802111: sae_pwe=%d", pwe);
+	if (pwe == SAE_PWE_HUNT_AND_PECK)
+		sae_pwe = NL80211_SAE_PWE_HUNT_AND_PECK;
+	else if (pwe == SAE_PWE_HASH_TO_ELEMENT)
+		sae_pwe = NL80211_SAE_PWE_HASH_TO_ELEMENT;
+	else if (pwe == SAE_PWE_BOTH)
+		sae_pwe = NL80211_SAE_PWE_BOTH;
+	else if (pwe == SAE_PWE_FORCE_HUNT_AND_PECK)
+		return 0; /* special test mode */
+	else
+		return -1;
+	if (nla_put_u8(msg, NL80211_ATTR_SAE_PWE, sae_pwe))
+		return -1;
+
+	return 0;
+}
+#endif /* CONFIG_SAE */
+
+
+#ifdef CONFIG_FILS
+static int nl80211_fils_discovery(struct i802_bss *bss, struct nl_msg *msg,
+				  struct wpa_driver_ap_params *params)
+{
+	struct nlattr *attr;
+
+	if (!bss->drv->fils_discovery) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Driver does not support FILS Discovery frame transmission for %s",
+			   bss->ifname);
+		return -1;
+	}
+
+	attr = nla_nest_start(msg, NL80211_ATTR_FILS_DISCOVERY);
+	if (!attr ||
+	    nla_put_u32(msg, NL80211_FILS_DISCOVERY_ATTR_INT_MIN,
+			params->fd_min_int) ||
+	    nla_put_u32(msg, NL80211_FILS_DISCOVERY_ATTR_INT_MAX,
+			params->fd_max_int) ||
+	    (params->fd_frame_tmpl &&
+	     nla_put(msg, NL80211_FILS_DISCOVERY_ATTR_TMPL,
+		     params->fd_frame_tmpl_len, params->fd_frame_tmpl)))
+		return -1;
+
+	nla_nest_end(msg, attr);
+	return 0;
+}
+#endif /* CONFIG_FILS */
+
+
+#ifdef CONFIG_IEEE80211AX
+
+static int nl80211_unsol_bcast_probe_resp(struct i802_bss *bss,
+					  struct nl_msg *msg,
+					  struct wpa_driver_ap_params *params)
+{
+	struct nlattr *attr;
+
+	if (!bss->drv->unsol_bcast_probe_resp) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Driver does not support unsolicited broadcast Probe Response frame transmission for %s",
+			   bss->ifname);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Unsolicited broadcast Probe Response frame interval: %u",
+		   params->unsol_bcast_probe_resp_interval);
+	attr = nla_nest_start(msg, NL80211_ATTR_UNSOL_BCAST_PROBE_RESP);
+	if (!attr ||
+	    nla_put_u32(msg, NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT,
+			params->unsol_bcast_probe_resp_interval) ||
+	    (params->unsol_bcast_probe_resp_tmpl &&
+	     nla_put(msg, NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL,
+		     params->unsol_bcast_probe_resp_tmpl_len,
+		     params->unsol_bcast_probe_resp_tmpl)))
+		return -1;
+
+	nla_nest_end(msg, attr);
+	return 0;
+}
+
+
+static int nl80211_mbssid(struct nl_msg *msg,
+			 struct wpa_driver_ap_params *params)
+{
+	struct nlattr *config, *elems;
+	int ifidx;
+
+	if (!params->mbssid_tx_iface)
+		return 0;
+
+	config = nla_nest_start(msg, NL80211_ATTR_MBSSID_CONFIG);
+	if (!config ||
+	    nla_put_u8(msg, NL80211_MBSSID_CONFIG_ATTR_INDEX,
+		       params->mbssid_index))
+		return -1;
+
+	if (params->mbssid_tx_iface) {
+		ifidx = if_nametoindex(params->mbssid_tx_iface);
+		if (ifidx <= 0 ||
+		    nla_put_u32(msg, NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX,
+				ifidx))
+			return -1;
+	}
+
+	if (params->ema && nla_put_flag(msg, NL80211_MBSSID_CONFIG_ATTR_EMA))
+		return -1;
+
+	nla_nest_end(msg, config);
+
+	if (params->mbssid_elem_count && params->mbssid_elem_len &&
+	    params->mbssid_elem_offset && *params->mbssid_elem_offset) {
+		u8 i, **offs = params->mbssid_elem_offset;
+
+		elems = nla_nest_start(msg, NL80211_ATTR_MBSSID_ELEMS);
+		if (!elems)
+			return -1;
+
+		for (i = 0; i < params->mbssid_elem_count - 1; i++) {
+			if (nla_put(msg, i + 1, offs[i + 1] - offs[i], offs[i]))
+				return -1;
+		}
+
+		if (nla_put(msg, i + 1,
+			    *offs + params->mbssid_elem_len - offs[i],
+			    offs[i]))
+			return -1;
+
+		nla_nest_end(msg, elems);
+	}
+
+	if (!params->ema)
+		return 0;
+
+	if (params->rnr_elem_count && params->rnr_elem_len &&
+	    params->rnr_elem_offset && *params->rnr_elem_offset) {
+		u8 i, **offs = params->rnr_elem_offset;
+
+		elems = nla_nest_start(msg, NL80211_ATTR_EMA_RNR_ELEMS);
+		if (!elems)
+			return -1;
+
+		for (i = 0; i < params->rnr_elem_count - 1; i++) {
+			if (nla_put(msg, i + 1, offs[i + 1] - offs[i], offs[i]))
+				return -1;
+		}
+
+		if (nla_put(msg, i + 1, *offs + params->rnr_elem_len - offs[i],
+			    offs[i]))
+			return -1;
+		nla_nest_end(msg, elems);
+	}
+
+	return 0;
+}
+
+#endif /* CONFIG_IEEE80211AX */
+
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+static void qca_set_allowed_ap_freqs(struct wpa_driver_nl80211_data *drv,
+				    const int *freqs, int num_freqs)
+{
+	struct nl_msg *msg;
+	struct nlattr *params, *freqs_list;
+	int i, ret;
+
+	if (!drv->set_wifi_conf_vendor_cmd_avail || !drv->qca_ap_allowed_freqs)
+		return;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Set AP allowed frequency list");
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION) ||
+	    !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)))
+		goto err;
+
+	freqs_list = nla_nest_start(
+		msg, QCA_WLAN_VENDOR_ATTR_CONFIG_AP_ALLOWED_FREQ_LIST);
+	if (!freqs_list)
+		goto err;
+
+	for (i = 0; i < num_freqs; i++) {
+		if (nla_put_u32(msg, i, freqs[i]))
+			goto err;
+	}
+
+	nla_nest_end(msg, freqs_list);
+	nla_nest_end(msg, params);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret)
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Failed set AP alllowed frequency list: %d (%s)",
+			   ret, strerror(-ret));
+
+	return;
+err:
+	nlmsg_free(msg);
+}
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
+static int nl80211_put_freq_params(struct nl_msg *msg,
+				   const struct hostapd_freq_params *freq)
+{
+	enum hostapd_hw_mode hw_mode;
+	int is_24ghz;
+	u8 channel;
+
+	wpa_printf(MSG_DEBUG, "  * freq=%d", freq->freq);
+	if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq))
+		return -ENOBUFS;
+
+	wpa_printf(MSG_DEBUG, "  * eht_enabled=%d", freq->eht_enabled);
+	wpa_printf(MSG_DEBUG, "  * he_enabled=%d", freq->he_enabled);
+	wpa_printf(MSG_DEBUG, "  * vht_enabled=%d", freq->vht_enabled);
+	wpa_printf(MSG_DEBUG, "  * ht_enabled=%d", freq->ht_enabled);
+	wpa_printf(MSG_DEBUG, "  * radar_background=%d",
+		   freq->radar_background);
+
+	hw_mode = ieee80211_freq_to_chan(freq->freq, &channel);
+	is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G ||
+		hw_mode == HOSTAPD_MODE_IEEE80211B;
+
+	if (freq->vht_enabled ||
+	    ((freq->he_enabled || freq->eht_enabled) && !is_24ghz)) {
+		enum nl80211_chan_width cw;
+
+		wpa_printf(MSG_DEBUG, "  * bandwidth=%d", freq->bandwidth);
+		switch (freq->bandwidth) {
+		case 20:
+			cw = NL80211_CHAN_WIDTH_20;
+			break;
+		case 40:
+			cw = NL80211_CHAN_WIDTH_40;
+			break;
+		case 80:
+			if (freq->center_freq2)
+				cw = NL80211_CHAN_WIDTH_80P80;
+			else
+				cw = NL80211_CHAN_WIDTH_80;
+			break;
+		case 160:
+			cw = NL80211_CHAN_WIDTH_160;
+			break;
+		case 320:
+			cw = NL80211_CHAN_WIDTH_320;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		wpa_printf(MSG_DEBUG, "  * channel_width=%d", cw);
+		wpa_printf(MSG_DEBUG, "  * center_freq1=%d",
+			   freq->center_freq1);
+		wpa_printf(MSG_DEBUG, "  * center_freq2=%d",
+			   freq->center_freq2);
+		if (nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, cw) ||
+		    nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1,
+				freq->center_freq1) ||
+		    (freq->center_freq2 &&
+		     nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2,
+				 freq->center_freq2)))
+			return -ENOBUFS;
+	} else if (freq->ht_enabled) {
+		enum nl80211_channel_type ct;
+
+		wpa_printf(MSG_DEBUG, "  * sec_channel_offset=%d",
+			   freq->sec_channel_offset);
+		switch (freq->sec_channel_offset) {
+		case -1:
+			ct = NL80211_CHAN_HT40MINUS;
+			break;
+		case 1:
+			ct = NL80211_CHAN_HT40PLUS;
+			break;
+		default:
+			ct = NL80211_CHAN_HT20;
+			break;
+		}
+
+		wpa_printf(MSG_DEBUG, "  * channel_type=%d", ct);
+		if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, ct))
+			return -ENOBUFS;
+	} else if (freq->edmg.channels && freq->edmg.bw_config) {
+		wpa_printf(MSG_DEBUG,
+			   "  * EDMG configuration: channels=0x%x bw_config=%d",
+			   freq->edmg.channels, freq->edmg.bw_config);
+		if (nla_put_u8(msg, NL80211_ATTR_WIPHY_EDMG_CHANNELS,
+			       freq->edmg.channels) ||
+		    nla_put_u8(msg, NL80211_ATTR_WIPHY_EDMG_BW_CONFIG,
+			       freq->edmg.bw_config))
+			return -1;
+	} else {
+		wpa_printf(MSG_DEBUG, "  * channel_type=%d",
+			   NL80211_CHAN_NO_HT);
+		if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+				NL80211_CHAN_NO_HT))
+			return -ENOBUFS;
+	}
+	if (freq->radar_background &&
+	    nla_put_flag(msg, NL80211_ATTR_RADAR_BACKGROUND))
+		return -ENOBUFS;
+
+	return 0;
+}
+
+
+static int wpa_driver_nl80211_set_ap(void *priv,
+				     struct wpa_driver_ap_params *params)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct i802_link *link = bss->flink;
+	struct nl_msg *msg;
+	u8 cmd = NL80211_CMD_NEW_BEACON;
+	int ret = -ENOBUFS;
+	int beacon_set;
+	int num_suites;
+	u32 suites[20], suite;
+	u32 ver;
+#ifdef CONFIG_MESH
+	struct wpa_driver_mesh_bss_params mesh_params;
+#endif /* CONFIG_MESH */
+
+	if (params->mld_ap) {
+		size_t i;
+
+		for (i = 0; i < bss->n_links; i++) {
+			if (bss->links[i].link_id == params->mld_link_id) {
+				link = &bss->links[i];
+				break;
+			}
+		}
+
+		if (i == bss->n_links) {
+			wpa_printf(MSG_DEBUG, "nl80211: Link ID=%u not found",
+				   params->mld_link_id);
+			return -EINVAL;
+		}
+	}
+
+	beacon_set = params->reenable ? 0 : link->beacon_set;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Set beacon (beacon_set=%d)",
+		   beacon_set);
+	if (beacon_set)
+		cmd = NL80211_CMD_SET_BEACON;
+	else if (!drv->device_ap_sme && !drv->use_monitor &&
+		 !nl80211_get_wiphy_data_ap(bss))
+		return -ENOBUFS;
+
+	wpa_hexdump(MSG_DEBUG, "nl80211: Beacon head",
+		    params->head, params->head_len);
+	wpa_hexdump(MSG_DEBUG, "nl80211: Beacon tail",
+		    params->tail, params->tail_len);
+	wpa_printf(MSG_DEBUG, "nl80211: ifindex=%d", bss->ifindex);
+	wpa_printf(MSG_DEBUG, "nl80211: beacon_int=%d", params->beacon_int);
+	wpa_printf(MSG_DEBUG, "nl80211: beacon_rate=%u", params->beacon_rate);
+	wpa_printf(MSG_DEBUG, "nl80211: rate_type=%d", params->rate_type);
+	wpa_printf(MSG_DEBUG, "nl80211: dtim_period=%d", params->dtim_period);
+	wpa_printf(MSG_DEBUG, "nl80211: ssid=%s",
+		   wpa_ssid_txt(params->ssid, params->ssid_len));
+	if (!(msg = nl80211_bss_msg(bss, 0, cmd)) ||
+	    nla_put(msg, NL80211_ATTR_BEACON_HEAD, params->head_len,
+		    params->head) ||
+	    nla_put(msg, NL80211_ATTR_BEACON_TAIL, params->tail_len,
+		    params->tail) ||
+	    nl80211_put_beacon_int(msg, params->beacon_int) ||
+	    nl80211_put_beacon_rate(msg, drv->capa.flags, drv->capa.flags2,
+				    params) ||
+	    nl80211_put_dtim_period(msg, params->dtim_period) ||
+	    nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid))
+		goto fail;
+
+	if (params->mld_ap) {
+		wpa_printf(MSG_DEBUG, "nl80211: link_id=%u",
+			   params->mld_link_id);
+
+		if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
+			       params->mld_link_id) ||
+		    (params->freq &&
+		     nl80211_put_freq_params(msg, params->freq) < 0))
+			goto fail;
+
+		nl80211_link_set_freq(bss, params->mld_link_id,
+				      params->freq->freq);
+	}
+
+	if (params->proberesp && params->proberesp_len) {
+		wpa_hexdump(MSG_DEBUG, "nl80211: proberesp (offload)",
+			    params->proberesp, params->proberesp_len);
+		if (nla_put(msg, NL80211_ATTR_PROBE_RESP, params->proberesp_len,
+			    params->proberesp))
+			goto fail;
+	}
+	switch (params->hide_ssid) {
+	case NO_SSID_HIDING:
+		wpa_printf(MSG_DEBUG, "nl80211: hidden SSID not in use");
+		if (nla_put_u32(msg, NL80211_ATTR_HIDDEN_SSID,
+				NL80211_HIDDEN_SSID_NOT_IN_USE))
+			goto fail;
+		break;
+	case HIDDEN_SSID_ZERO_LEN:
+		wpa_printf(MSG_DEBUG, "nl80211: hidden SSID zero len");
+		if (nla_put_u32(msg, NL80211_ATTR_HIDDEN_SSID,
+				NL80211_HIDDEN_SSID_ZERO_LEN))
+			goto fail;
+		break;
+	case HIDDEN_SSID_ZERO_CONTENTS:
+		wpa_printf(MSG_DEBUG, "nl80211: hidden SSID zero contents");
+		if (nla_put_u32(msg, NL80211_ATTR_HIDDEN_SSID,
+				NL80211_HIDDEN_SSID_ZERO_CONTENTS))
+			goto fail;
+		break;
+	}
+	wpa_printf(MSG_DEBUG, "nl80211: privacy=%d", params->privacy);
+	if (params->privacy &&
+	    nla_put_flag(msg, NL80211_ATTR_PRIVACY))
+		goto fail;
+	wpa_printf(MSG_DEBUG, "nl80211: auth_algs=0x%x", params->auth_algs);
+	if ((params->auth_algs & (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) ==
+	    (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) {
+		/* Leave out the attribute */
+	} else if (params->auth_algs & WPA_AUTH_ALG_SHARED) {
+		if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE,
+				NL80211_AUTHTYPE_SHARED_KEY))
+			goto fail;
+	} else {
+		if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE,
+				NL80211_AUTHTYPE_OPEN_SYSTEM))
+			goto fail;
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: wpa_version=0x%x", params->wpa_version);
+	ver = 0;
+	if (params->wpa_version & WPA_PROTO_WPA)
+		ver |= NL80211_WPA_VERSION_1;
+	if (params->wpa_version & WPA_PROTO_RSN)
+		ver |= NL80211_WPA_VERSION_2;
+	if (ver &&
+	    nla_put_u32(msg, NL80211_ATTR_WPA_VERSIONS, ver))
+		goto fail;
+
+	wpa_printf(MSG_DEBUG, "nl80211: key_mgmt_suites=0x%x",
+		   params->key_mgmt_suites);
+	num_suites = wpa_key_mgmt_to_suites(params->key_mgmt_suites,
+					    suites, ARRAY_SIZE(suites));
+	if (num_suites > NL80211_MAX_NR_AKM_SUITES)
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Not enough room for all AKM suites (num_suites=%d > NL80211_MAX_NR_AKM_SUITES)",
+			   num_suites);
+	else if (num_suites &&
+		 nla_put(msg, NL80211_ATTR_AKM_SUITES, num_suites * sizeof(u32),
+			 suites))
+		goto fail;
+
+	if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X_NO_WPA &&
+	    (!params->pairwise_ciphers ||
+	     params->pairwise_ciphers & (WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40)) &&
+	    (nla_put_u16(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, ETH_P_PAE) ||
+	     nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT)))
+		goto fail;
+
+	if (drv->device_ap_sme) {
+		u32 flags = 0;
+
+		if (params->key_mgmt_suites & (WPA_KEY_MGMT_SAE |
+					       WPA_KEY_MGMT_SAE_EXT_KEY)) {
+			/* Add the previously used flag attribute to support
+			 * older kernel versions and the newer flag bit for
+			 * newer kernels. */
+			if (nla_put_flag(msg,
+					 NL80211_ATTR_EXTERNAL_AUTH_SUPPORT))
+				goto fail;
+			flags |= NL80211_AP_SETTINGS_EXTERNAL_AUTH_SUPPORT;
+		}
+
+		flags |= NL80211_AP_SETTINGS_SA_QUERY_OFFLOAD_SUPPORT;
+
+		if (nla_put_u32(msg, NL80211_ATTR_AP_SETTINGS_FLAGS, flags))
+			goto fail;
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: pairwise_ciphers=0x%x",
+		   params->pairwise_ciphers);
+	num_suites = wpa_cipher_to_cipher_suites(params->pairwise_ciphers,
+						 suites, ARRAY_SIZE(suites));
+	if (num_suites &&
+	    nla_put(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE,
+		    num_suites * sizeof(u32), suites))
+		goto fail;
+
+	wpa_printf(MSG_DEBUG, "nl80211: group_cipher=0x%x",
+		   params->group_cipher);
+	suite = wpa_cipher_to_cipher_suite(params->group_cipher);
+	if (suite &&
+	    nla_put_u32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, suite))
+		goto fail;
+
+	if (params->beacon_ies) {
+		wpa_hexdump_buf(MSG_DEBUG, "nl80211: beacon_ies",
+				params->beacon_ies);
+		if (nla_put(msg, NL80211_ATTR_IE,
+			    wpabuf_len(params->beacon_ies),
+			    wpabuf_head(params->beacon_ies)))
+			goto fail;
+	}
+	if (params->proberesp_ies) {
+		wpa_hexdump_buf(MSG_DEBUG, "nl80211: proberesp_ies",
+				params->proberesp_ies);
+		if (nla_put(msg, NL80211_ATTR_IE_PROBE_RESP,
+			    wpabuf_len(params->proberesp_ies),
+			    wpabuf_head(params->proberesp_ies)))
+			goto fail;
+	}
+	if (params->assocresp_ies) {
+		wpa_hexdump_buf(MSG_DEBUG, "nl80211: assocresp_ies",
+				params->assocresp_ies);
+		if (nla_put(msg, NL80211_ATTR_IE_ASSOC_RESP,
+			    wpabuf_len(params->assocresp_ies),
+			    wpabuf_head(params->assocresp_ies)))
+			goto fail;
+	}
+
+	if (drv->capa.flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)  {
+		wpa_printf(MSG_DEBUG, "nl80211: ap_max_inactivity=%d",
+			   params->ap_max_inactivity);
+		if (nla_put_u16(msg, NL80211_ATTR_INACTIVITY_TIMEOUT,
+				params->ap_max_inactivity))
+			goto fail;
+	}
+
+#ifdef CONFIG_P2P
+	if (params->p2p_go_ctwindow > 0) {
+		if (drv->p2p_go_ctwindow_supported) {
+			wpa_printf(MSG_DEBUG, "nl80211: P2P GO ctwindow=%d",
+				   params->p2p_go_ctwindow);
+			if (nla_put_u8(msg, NL80211_ATTR_P2P_CTWINDOW,
+				       params->p2p_go_ctwindow))
+				goto fail;
+		} else {
+			wpa_printf(MSG_INFO,
+				   "nl80211: Driver does not support CTWindow configuration - ignore this parameter");
+		}
+	}
+#endif /* CONFIG_P2P */
+
+	if (params->pbss) {
+		wpa_printf(MSG_DEBUG, "nl80211: PBSS");
+		if (nla_put_flag(msg, NL80211_ATTR_PBSS))
+			goto fail;
+	}
+
+	if (params->ftm_responder) {
+		struct nlattr *ftm;
+
+		if (!(drv->capa.flags & WPA_DRIVER_FLAGS_FTM_RESPONDER)) {
+			ret = -ENOTSUP;
+			goto fail;
+		}
+
+		ftm = nla_nest_start(msg, NL80211_ATTR_FTM_RESPONDER);
+		if (!ftm ||
+		    nla_put_flag(msg, NL80211_FTM_RESP_ATTR_ENABLED) ||
+		    (params->lci &&
+		     nla_put(msg, NL80211_FTM_RESP_ATTR_LCI,
+			     wpabuf_len(params->lci),
+			     wpabuf_head(params->lci))) ||
+		    (params->civic &&
+		     nla_put(msg, NL80211_FTM_RESP_ATTR_CIVICLOC,
+			     wpabuf_len(params->civic),
+			     wpabuf_head(params->civic))))
+			goto fail;
+		nla_nest_end(msg, ftm);
+	}
+
+#ifdef CONFIG_IEEE80211AX
+	if (params->he_spr_ctrl) {
+		struct nlattr *spr;
+
+		spr = nla_nest_start(msg, NL80211_ATTR_HE_OBSS_PD);
+		wpa_printf(MSG_DEBUG, "nl80211: he_spr_ctrl=0x%x",
+			   params->he_spr_ctrl);
+
+		if (!spr ||
+		    nla_put_u8(msg, NL80211_HE_OBSS_PD_ATTR_SR_CTRL,
+			       params->he_spr_ctrl) ||
+		    ((params->he_spr_ctrl &
+		      SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT) &&
+		     nla_put_u8(msg, NL80211_HE_OBSS_PD_ATTR_NON_SRG_MAX_OFFSET,
+				params->he_spr_non_srg_obss_pd_max_offset)))
+			goto fail;
+
+		if ((params->he_spr_ctrl &
+		     SPATIAL_REUSE_SRG_INFORMATION_PRESENT) &&
+		    (nla_put_u8(msg, NL80211_HE_OBSS_PD_ATTR_MIN_OFFSET,
+				params->he_spr_srg_obss_pd_min_offset) ||
+		     nla_put_u8(msg, NL80211_HE_OBSS_PD_ATTR_MAX_OFFSET,
+				params->he_spr_srg_obss_pd_max_offset) ||
+		     nla_put(msg, NL80211_HE_OBSS_PD_ATTR_BSS_COLOR_BITMAP,
+			     sizeof(params->he_spr_bss_color_bitmap),
+			     params->he_spr_bss_color_bitmap) ||
+		     nla_put(msg, NL80211_HE_OBSS_PD_ATTR_PARTIAL_BSSID_BITMAP,
+			     sizeof(params->he_spr_partial_bssid_bitmap),
+			     params->he_spr_partial_bssid_bitmap)))
+			goto fail;
+
+		nla_nest_end(msg, spr);
+	}
+
+	if (params->freq && nl80211_put_freq_params(msg, params->freq) < 0)
+		goto fail;
+
+	if (params->freq && params->freq->he_enabled) {
+		struct nlattr *bss_color;
+
+		bss_color = nla_nest_start(msg, NL80211_ATTR_HE_BSS_COLOR);
+		if (!bss_color ||
+		    (params->he_bss_color_disabled &&
+		     nla_put_flag(msg, NL80211_HE_BSS_COLOR_ATTR_DISABLED)) ||
+		    (params->he_bss_color_partial &&
+		     nla_put_flag(msg, NL80211_HE_BSS_COLOR_ATTR_PARTIAL)) ||
+		    nla_put_u8(msg, NL80211_HE_BSS_COLOR_ATTR_COLOR,
+			       params->he_bss_color))
+			goto fail;
+		nla_nest_end(msg, bss_color);
+	}
+
+	if (params->twt_responder) {
+		wpa_printf(MSG_DEBUG, "nl80211: twt_responder=%d",
+			   params->twt_responder);
+		if (nla_put_flag(msg, NL80211_ATTR_TWT_RESPONDER))
+			goto fail;
+	}
+
+	if (params->unsol_bcast_probe_resp_interval &&
+	    nl80211_unsol_bcast_probe_resp(bss, msg, params) < 0)
+		goto fail;
+
+	if (nl80211_mbssid(msg, params) < 0)
+		goto fail;
+#endif /* CONFIG_IEEE80211AX */
+
+#ifdef CONFIG_SAE
+	if (wpa_key_mgmt_sae(params->key_mgmt_suites) &&
+	    nl80211_put_sae_pwe(msg, params->sae_pwe) < 0)
+		goto fail;
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_FILS
+	if (params->fd_max_int && nl80211_fils_discovery(bss, msg, params) < 0)
+		goto fail;
+#endif /* CONFIG_FILS */
+
+	if (params->punct_bitmap) {
+		wpa_printf(MSG_DEBUG, "nl80211: Puncturing bitmap=0x%04x",
+			   params->punct_bitmap);
+		if (nla_put_u32(msg, NL80211_ATTR_PUNCT_BITMAP,
+				params->punct_bitmap))
+			goto fail;
+	}
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+	if (cmd == NL80211_CMD_NEW_BEACON && params->allowed_freqs)
+		qca_set_allowed_ap_freqs(drv, params->allowed_freqs,
+					 int_array_len(params->allowed_freqs));
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+	ret = send_and_recv_msgs_connect_handle(drv, msg, bss, 1);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)",
+			   ret, strerror(-ret));
+		if (!bss->flink->beacon_set)
+			ret = 0;
+		bss->flink->beacon_set = 0;
+	} else {
+		link->beacon_set = 1;
+		nl80211_set_bss(bss, params->cts_protect, params->preamble,
+				params->short_slot_time, params->ht_opmode,
+				params->isolate, params->basic_rates);
+		nl80211_set_multicast_to_unicast(bss,
+						 params->multicast_to_unicast);
+		if (beacon_set && params->freq &&
+		    params->freq->bandwidth != link->bandwidth) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Update BSS %s bandwidth: %d -> %d",
+				   bss->ifname, link->bandwidth,
+				   params->freq->bandwidth);
+			ret = nl80211_set_channel(bss, params->freq, 1);
+			if (ret) {
+				wpa_printf(MSG_DEBUG,
+					   "nl80211: Frequency set failed: %d (%s)",
+					   ret, strerror(-ret));
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "nl80211: Frequency set succeeded for ht2040 coex");
+				link->bandwidth = params->freq->bandwidth;
+			}
+		} else if (!beacon_set && params->freq) {
+			/*
+			 * cfg80211 updates the driver on frequence change in AP
+			 * mode only at the point when beaconing is started, so
+			 * set the initial value here.
+			 */
+			link->bandwidth = params->freq->bandwidth;
+		}
+	}
+
+#ifdef CONFIG_MESH
+	if (is_mesh_interface(drv->nlmode) && params->ht_opmode != -1) {
+		os_memset(&mesh_params, 0, sizeof(mesh_params));
+		mesh_params.flags |= WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE;
+		mesh_params.ht_opmode = params->ht_opmode;
+		ret = nl80211_set_mesh_config(priv, &mesh_params);
+		if (ret < 0)
+			return ret;
+	}
+#endif /* CONFIG_MESH */
+
+	return ret;
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+static bool nl80211_link_valid(struct i802_bss *bss, s8 link_id)
+{
+	unsigned int i;
+
+	if (link_id < 0)
+		return false;
+
+	for (i = 0; i < bss->n_links; i++) {
+		wpa_printf(MSG_DEBUG, "nl80211: %s - i=%u, link_id=%u",
+			   __func__, i, bss->links[i].link_id);
+		if (bss->links[i].link_id == NL80211_DRV_LINK_ID_NA)
+			continue;
+
+		if (bss->links[i].link_id == link_id)
+			return true;
+	}
+
+	return false;
+}
+
+
+static int nl80211_set_channel(struct i802_bss *bss,
+			       struct hostapd_freq_params *freq, int set_chan)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Set freq %d, channel %d(ht_enabled=%d, vht_enabled=%d, he_enabled=%d, eht_enabled=%d, bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)",
+		   freq->freq, freq->channel, freq->ht_enabled, freq->vht_enabled,
+		   freq->he_enabled, freq->eht_enabled, freq->bandwidth,
+		   freq->center_freq1, freq->center_freq2);
+
+	msg = nl80211_bss_msg(bss, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
+			      NL80211_CMD_SET_WIPHY);
+	if (!msg || nl80211_put_freq_params(msg, freq) < 0) {
+		nlmsg_free(msg);
+		return -1;
+	}
+
+	if (nl80211_link_valid(bss, freq->link_id)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Set link_id=%u for freq",
+			   freq->link_id);
+
+		if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, freq->link_id)) {
+			nlmsg_free(msg);
+			return -ENOBUFS;
+		}
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret == 0) {
+		nl80211_link_set_freq(bss, freq->link_id, freq->freq);
+		return 0;
+	}
+	wpa_printf(MSG_DEBUG, "nl80211: Failed to set channel (freq=%d): "
+		   "%d (%s)", freq->freq, ret, strerror(-ret));
+	return -1;
+}
+
+
+static u32 sta_flags_nl80211(int flags)
+{
+	u32 f = 0;
+
+	if (flags & WPA_STA_AUTHORIZED)
+		f |= BIT(NL80211_STA_FLAG_AUTHORIZED);
+	if (flags & WPA_STA_WMM)
+		f |= BIT(NL80211_STA_FLAG_WME);
+	if (flags & WPA_STA_SHORT_PREAMBLE)
+		f |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE);
+	if (flags & WPA_STA_MFP)
+		f |= BIT(NL80211_STA_FLAG_MFP);
+	if (flags & WPA_STA_TDLS_PEER)
+		f |= BIT(NL80211_STA_FLAG_TDLS_PEER);
+	if (flags & WPA_STA_AUTHENTICATED)
+		f |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
+	if (flags & WPA_STA_ASSOCIATED)
+		f |= BIT(NL80211_STA_FLAG_ASSOCIATED);
+
+	return f;
+}
+
+
+#ifdef CONFIG_MESH
+static u32 sta_plink_state_nl80211(enum mesh_plink_state state)
+{
+	switch (state) {
+	case PLINK_IDLE:
+		return NL80211_PLINK_LISTEN;
+	case PLINK_OPN_SNT:
+		return NL80211_PLINK_OPN_SNT;
+	case PLINK_OPN_RCVD:
+		return NL80211_PLINK_OPN_RCVD;
+	case PLINK_CNF_RCVD:
+		return NL80211_PLINK_CNF_RCVD;
+	case PLINK_ESTAB:
+		return NL80211_PLINK_ESTAB;
+	case PLINK_HOLDING:
+		return NL80211_PLINK_HOLDING;
+	case PLINK_BLOCKED:
+		return NL80211_PLINK_BLOCKED;
+	default:
+		wpa_printf(MSG_ERROR, "nl80211: Invalid mesh plink state %d",
+			   state);
+	}
+	return -1;
+}
+#endif /* CONFIG_MESH */
+
+
+static int wpa_driver_nl80211_sta_add(void *priv,
+				      struct hostapd_sta_add_params *params)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nl80211_sta_flag_update upd;
+	int ret = -ENOBUFS;
+	u8 cmd;
+	const char *cmd_string;
+
+	if ((params->flags & WPA_STA_TDLS_PEER) &&
+	    !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
+		return -EOPNOTSUPP;
+
+	if (params->mld_link_sta) {
+		cmd = params->set ? NL80211_CMD_MODIFY_LINK_STA :
+			NL80211_CMD_ADD_LINK_STA;
+		cmd_string = params->set ? "NL80211_CMD_MODIFY_LINK_STA" :
+			"NL80211_CMD_ADD_LINK_STA";
+	} else {
+		cmd = params->set ? NL80211_CMD_SET_STATION :
+			NL80211_CMD_NEW_STATION;
+		cmd_string = params->set ? "NL80211_CMD_SET_STATION" :
+			"NL80211_CMD_NEW_STATION";
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: %s STA " MACSTR,
+		   cmd_string, MAC2STR(params->addr));
+	msg = nl80211_bss_msg(bss, 0, cmd);
+	if (!msg)
+		goto fail;
+
+	/*
+	 * Set the below properties only in one of the following cases:
+	 * 1. New station is added, already associated.
+	 * 2. Set WPA_STA_TDLS_PEER station.
+	 * 3. Set an already added unassociated station, if driver supports
+	 * full AP client state. (Set these properties after station became
+	 * associated will be rejected by the driver).
+	 */
+	if (!params->set || (params->flags & WPA_STA_TDLS_PEER) ||
+	    (params->set && FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags) &&
+	     (params->flags & WPA_STA_ASSOCIATED))) {
+		wpa_hexdump(MSG_DEBUG, "  * supported rates",
+			    params->supp_rates, params->supp_rates_len);
+		wpa_printf(MSG_DEBUG, "  * capability=0x%x",
+			   params->capability);
+		if (nla_put(msg, NL80211_ATTR_STA_SUPPORTED_RATES,
+			    params->supp_rates_len, params->supp_rates) ||
+		    nla_put_u16(msg, NL80211_ATTR_STA_CAPABILITY,
+				params->capability))
+			goto fail;
+
+		if (params->ht_capabilities) {
+			wpa_hexdump(MSG_DEBUG, "  * ht_capabilities",
+				    (u8 *) params->ht_capabilities,
+				    sizeof(*params->ht_capabilities));
+			if (nla_put(msg, NL80211_ATTR_HT_CAPABILITY,
+				    sizeof(*params->ht_capabilities),
+				    params->ht_capabilities))
+				goto fail;
+		}
+
+		if (params->vht_capabilities) {
+			wpa_hexdump(MSG_DEBUG, "  * vht_capabilities",
+				    (u8 *) params->vht_capabilities,
+				    sizeof(*params->vht_capabilities));
+			if (nla_put(msg, NL80211_ATTR_VHT_CAPABILITY,
+				    sizeof(*params->vht_capabilities),
+				    params->vht_capabilities))
+				goto fail;
+		}
+
+		if (params->he_capab) {
+			wpa_hexdump(MSG_DEBUG, "  * he_capab",
+				    params->he_capab, params->he_capab_len);
+			if (nla_put(msg, NL80211_ATTR_HE_CAPABILITY,
+				    params->he_capab_len, params->he_capab))
+				goto fail;
+		}
+
+		if (params->he_6ghz_capab) {
+			wpa_hexdump(MSG_DEBUG, "  * he_6ghz_capab",
+				    params->he_6ghz_capab,
+				    sizeof(*params->he_6ghz_capab));
+			if (nla_put(msg, NL80211_ATTR_HE_6GHZ_CAPABILITY,
+				    sizeof(*params->he_6ghz_capab),
+				    params->he_6ghz_capab))
+				goto fail;
+		}
+
+		if (params->eht_capab) {
+			wpa_hexdump(MSG_DEBUG, "  * eht_capab",
+				    params->eht_capab, params->eht_capab_len);
+			if (nla_put(msg, NL80211_ATTR_EHT_CAPABILITY,
+				    params->eht_capab_len, params->eht_capab))
+				goto fail;
+		}
+
+		if (params->ext_capab) {
+			wpa_hexdump(MSG_DEBUG, "  * ext_capab",
+				    params->ext_capab, params->ext_capab_len);
+			if (nla_put(msg, NL80211_ATTR_STA_EXT_CAPABILITY,
+				    params->ext_capab_len, params->ext_capab))
+				goto fail;
+		}
+
+		if (is_ap_interface(drv->nlmode) &&
+		    nla_put_u8(msg, NL80211_ATTR_STA_SUPPORT_P2P_PS,
+			       params->support_p2p_ps ?
+			       NL80211_P2P_PS_SUPPORTED :
+			       NL80211_P2P_PS_UNSUPPORTED))
+			goto fail;
+	}
+	if (!params->set) {
+		if (params->aid) {
+			wpa_printf(MSG_DEBUG, "  * aid=%u", params->aid);
+			if (nla_put_u16(msg, NL80211_ATTR_STA_AID, params->aid))
+				goto fail;
+		} else {
+			/*
+			 * cfg80211 validates that AID is non-zero, so we have
+			 * to make this a non-zero value for the TDLS case where
+			 * a stub STA entry is used for now and for a station
+			 * that is still not associated.
+			 */
+			wpa_printf(MSG_DEBUG, "  * aid=1 (%s workaround)",
+				   (params->flags & WPA_STA_TDLS_PEER) ?
+				   "TDLS" : "UNASSOC_STA");
+			if (nla_put_u16(msg, NL80211_ATTR_STA_AID, 1))
+				goto fail;
+		}
+		wpa_printf(MSG_DEBUG, "  * listen_interval=%u",
+			   params->listen_interval);
+		if (nla_put_u16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL,
+				params->listen_interval))
+			goto fail;
+	} else if (params->aid && (params->flags & WPA_STA_TDLS_PEER)) {
+		wpa_printf(MSG_DEBUG, "  * peer_aid=%u", params->aid);
+		if (nla_put_u16(msg, NL80211_ATTR_PEER_AID, params->aid))
+			goto fail;
+	} else if (FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags) &&
+		   (params->flags & WPA_STA_ASSOCIATED)) {
+		wpa_printf(MSG_DEBUG, "  * aid=%u", params->aid);
+		wpa_printf(MSG_DEBUG, "  * listen_interval=%u",
+			   params->listen_interval);
+		if (nla_put_u16(msg, NL80211_ATTR_STA_AID, params->aid) ||
+		    nla_put_u16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL,
+				params->listen_interval))
+			goto fail;
+	}
+
+	if (params->vht_opmode_enabled) {
+		wpa_printf(MSG_DEBUG, "  * opmode=%u", params->vht_opmode);
+		if (nla_put_u8(msg, NL80211_ATTR_OPMODE_NOTIF,
+			       params->vht_opmode))
+			goto fail;
+	}
+
+	if (params->supp_channels) {
+		wpa_hexdump(MSG_DEBUG, "  * supported channels",
+			    params->supp_channels, params->supp_channels_len);
+		if (nla_put(msg, NL80211_ATTR_STA_SUPPORTED_CHANNELS,
+			    params->supp_channels_len, params->supp_channels))
+			goto fail;
+	}
+
+	if (params->supp_oper_classes) {
+		wpa_hexdump(MSG_DEBUG, "  * supported operating classes",
+			    params->supp_oper_classes,
+			    params->supp_oper_classes_len);
+		if (nla_put(msg, NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES,
+			    params->supp_oper_classes_len,
+			    params->supp_oper_classes))
+			goto fail;
+	}
+
+	os_memset(&upd, 0, sizeof(upd));
+	upd.set = sta_flags_nl80211(params->flags);
+	upd.mask = upd.set | sta_flags_nl80211(params->flags_mask);
+
+	/*
+	 * If the driver doesn't support full AP client state, ignore ASSOC/AUTH
+	 * flags, as nl80211 driver moves a new station, by default, into
+	 * associated state.
+	 *
+	 * On the other hand, if the driver supports that feature and the
+	 * station is added in unauthenticated state, set the
+	 * authenticated/associated bits in the mask to prevent moving this
+	 * station to associated state before it is actually associated.
+	 *
+	 * This is irrelevant for mesh mode where the station is added to the
+	 * driver as authenticated already, and ASSOCIATED isn't part of the
+	 * nl80211 API.
+	 */
+	if (!is_mesh_interface(drv->nlmode)) {
+		if (!FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags)) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Ignore ASSOC/AUTH flags since driver doesn't support full AP client state");
+			upd.mask &= ~(BIT(NL80211_STA_FLAG_ASSOCIATED) |
+				      BIT(NL80211_STA_FLAG_AUTHENTICATED));
+		} else if (!params->set &&
+			   !(params->flags & WPA_STA_TDLS_PEER)) {
+			if (!(params->flags & WPA_STA_AUTHENTICATED))
+				upd.mask |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
+			if (!(params->flags & WPA_STA_ASSOCIATED))
+				upd.mask |= BIT(NL80211_STA_FLAG_ASSOCIATED);
+		}
+#ifdef CONFIG_MESH
+	} else {
+		if (params->plink_state == PLINK_ESTAB && params->peer_aid) {
+			ret = nla_put_u16(msg, NL80211_ATTR_MESH_PEER_AID,
+					  params->peer_aid);
+			if (ret)
+				goto fail;
+		}
+#endif /* CONFIG_MESH */
+	}
+
+	wpa_printf(MSG_DEBUG, "  * flags set=0x%x mask=0x%x",
+		   upd.set, upd.mask);
+	if (nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd))
+		goto fail;
+
+#ifdef CONFIG_MESH
+	if (params->plink_state &&
+	    nla_put_u8(msg, NL80211_ATTR_STA_PLINK_STATE,
+		       sta_plink_state_nl80211(params->plink_state)))
+		goto fail;
+#endif /* CONFIG_MESH */
+
+	if ((!params->set || (params->flags & WPA_STA_TDLS_PEER) ||
+	     FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags)) &&
+	     (params->flags & WPA_STA_WMM)) {
+		struct nlattr *wme = nla_nest_start(msg, NL80211_ATTR_STA_WME);
+
+		wpa_printf(MSG_DEBUG, "  * qosinfo=0x%x", params->qosinfo);
+		if (!wme ||
+		    nla_put_u8(msg, NL80211_STA_WME_UAPSD_QUEUES,
+			       params->qosinfo & WMM_QOSINFO_STA_AC_MASK) ||
+		    nla_put_u8(msg, NL80211_STA_WME_MAX_SP,
+			       (params->qosinfo >> WMM_QOSINFO_STA_SP_SHIFT) &
+			       WMM_QOSINFO_STA_SP_MASK))
+			goto fail;
+		nla_nest_end(msg, wme);
+	}
+
+	/* In case we are an AP MLD need to always specify the link ID */
+	if (params->mld_link_id >= 0) {
+		wpa_printf(MSG_DEBUG, "  * mld_link_id=%d",
+			   params->mld_link_id);
+		if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
+			       params->mld_link_id))
+			goto fail;
+
+		/*
+		 * If the link address is specified the station is a non-AP MLD
+		 * and thus need to provide the MLD address as the station
+		 * address, and the non-AP MLD link address as the link address.
+		 */
+		if (params->mld_link_addr) {
+			wpa_printf(MSG_DEBUG, "  * mld_link_addr=" MACSTR,
+				   MAC2STR(params->mld_link_addr));
+
+			if (nla_put(msg, NL80211_ATTR_MLD_ADDR,
+				    ETH_ALEN, params->addr) ||
+			    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
+				    params->mld_link_addr))
+				goto fail;
+		} else {
+			if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
+				    params->addr))
+				goto fail;
+		}
+	} else {
+		if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr))
+			goto fail;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	msg = NULL;
+	if (ret)
+		wpa_printf(MSG_DEBUG, "nl80211: %s result: %d (%s)",
+			   cmd_string, ret, strerror(-ret));
+	if (ret == -EEXIST)
+		ret = 0;
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+static void rtnl_neigh_delete_fdb_entry(struct i802_bss *bss, const u8 *addr)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct ndmsg nhdr = {
+		.ndm_state = NUD_PERMANENT,
+		.ndm_ifindex = bss->ifindex,
+		.ndm_family = AF_BRIDGE,
+	};
+	struct nl_msg *msg;
+	int err;
+
+	msg = nlmsg_alloc_simple(RTM_DELNEIGH, NLM_F_CREATE);
+	if (!msg)
+		return;
+
+	if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0)
+		goto errout;
+
+	if (nla_put(msg, NDA_LLADDR, ETH_ALEN, (void *)addr))
+		goto errout;
+
+	if (nl_send_auto_complete(drv->rtnl_sk, msg) < 0)
+		goto errout;
+
+	err = nl_wait_for_ack(drv->rtnl_sk);
+	if (err < 0) {
+		wpa_printf(MSG_DEBUG, "nl80211: bridge FDB entry delete for "
+			   MACSTR " ifindex=%d failed: %s", MAC2STR(addr),
+			   bss->ifindex, nl_geterror(err));
+	} else {
+		wpa_printf(MSG_DEBUG, "nl80211: deleted bridge FDB entry for "
+			   MACSTR, MAC2STR(addr));
+	}
+
+errout:
+	nlmsg_free(msg);
+}
+
+
+static int wpa_driver_nl80211_sta_remove(struct i802_bss *bss, const u8 *addr,
+					 int deauth, u16 reason_code)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_STATION)) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+	    (deauth == 0 &&
+	     nla_put_u8(msg, NL80211_ATTR_MGMT_SUBTYPE,
+			WLAN_FC_STYPE_DISASSOC)) ||
+	    (deauth == 1 &&
+	     nla_put_u8(msg, NL80211_ATTR_MGMT_SUBTYPE,
+			WLAN_FC_STYPE_DEAUTH)) ||
+	    (reason_code &&
+	     nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code))) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	wpa_printf(MSG_DEBUG, "nl80211: sta_remove -> DEL_STATION %s " MACSTR
+		   " --> %d (%s)",
+		   bss->ifname, MAC2STR(addr), ret, strerror(-ret));
+
+	if (drv->rtnl_sk)
+		rtnl_neigh_delete_fdb_entry(bss, addr);
+
+	if (ret == -ENOENT)
+		return 0;
+	return ret;
+}
+
+
+void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+	struct nl_msg *msg;
+	struct wpa_driver_nl80211_data *drv2;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Remove interface ifindex=%d", ifidx);
+
+	/* stop listening for EAPOL on this interface */
+	dl_list_for_each(drv2, &drv->global->interfaces,
+			 struct wpa_driver_nl80211_data, list)
+	{
+		del_ifidx(drv2, ifidx, IFIDX_ANY);
+		/* Remove all bridges learned for this iface */
+		del_ifidx(drv2, IFIDX_ANY, ifidx);
+	}
+
+	msg = nl80211_ifindex_msg(drv, ifidx, 0, NL80211_CMD_DEL_INTERFACE);
+	if (send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL) == 0)
+		return;
+	wpa_printf(MSG_ERROR, "Failed to remove interface (ifidx=%d)", ifidx);
+}
+
+
+const char * nl80211_iftype_str(enum nl80211_iftype mode)
+{
+	switch (mode) {
+	case NL80211_IFTYPE_ADHOC:
+		return "ADHOC";
+	case NL80211_IFTYPE_STATION:
+		return "STATION";
+	case NL80211_IFTYPE_AP:
+		return "AP";
+	case NL80211_IFTYPE_AP_VLAN:
+		return "AP_VLAN";
+	case NL80211_IFTYPE_WDS:
+		return "WDS";
+	case NL80211_IFTYPE_MONITOR:
+		return "MONITOR";
+	case NL80211_IFTYPE_MESH_POINT:
+		return "MESH_POINT";
+	case NL80211_IFTYPE_P2P_CLIENT:
+		return "P2P_CLIENT";
+	case NL80211_IFTYPE_P2P_GO:
+		return "P2P_GO";
+	case NL80211_IFTYPE_P2P_DEVICE:
+		return "P2P_DEVICE";
+	case NL80211_IFTYPE_OCB:
+		return "OCB";
+	case NL80211_IFTYPE_NAN:
+		return "NAN";
+	default:
+		return "unknown";
+	}
+}
+
+
+static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
+				     const char *ifname,
+				     enum nl80211_iftype iftype,
+				     const u8 *addr, int wds,
+				     int (*handler)(struct nl_msg *, void *),
+				     void *arg)
+{
+	struct nl_msg *msg;
+	int ifidx;
+	int ret = -ENOBUFS;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Create interface iftype %d (%s)",
+		   iftype, nl80211_iftype_str(iftype));
+
+	msg = nl80211_cmd_msg(drv->first_bss, 0, NL80211_CMD_NEW_INTERFACE);
+	if (!msg ||
+	    nla_put_string(msg, NL80211_ATTR_IFNAME, ifname) ||
+	    nla_put_u32(msg, NL80211_ATTR_IFTYPE, iftype))
+		goto fail;
+
+	if (iftype == NL80211_IFTYPE_MONITOR) {
+		struct nlattr *flags;
+
+		flags = nla_nest_start(msg, NL80211_ATTR_MNTR_FLAGS);
+		if (!flags ||
+		    nla_put_flag(msg, NL80211_MNTR_FLAG_COOK_FRAMES))
+			goto fail;
+
+		nla_nest_end(msg, flags);
+	} else if (wds) {
+		if (nla_put_u8(msg, NL80211_ATTR_4ADDR, wds))
+			goto fail;
+	}
+
+	/*
+	 * Tell cfg80211 that the interface belongs to the socket that created
+	 * it, and the interface should be deleted when the socket is closed.
+	 */
+	if (nla_put_flag(msg, NL80211_ATTR_IFACE_SOCKET_OWNER))
+		goto fail;
+
+	if ((addr && iftype == NL80211_IFTYPE_P2P_DEVICE) &&
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
+		goto fail;
+
+	ret = send_and_recv_msgs(drv, msg, handler, arg, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+	fail:
+		nlmsg_free(msg);
+		wpa_printf(MSG_ERROR, "Failed to create interface %s: %d (%s)",
+			   ifname, ret, strerror(-ret));
+		return ret;
+	}
+
+	if (iftype == NL80211_IFTYPE_P2P_DEVICE)
+		return 0;
+
+	ifidx = if_nametoindex(ifname);
+	wpa_printf(MSG_DEBUG, "nl80211: New interface %s created: ifindex=%d",
+		   ifname, ifidx);
+
+	if (ifidx <= 0)
+		return -1;
+
+	/*
+	 * Some virtual interfaces need to process EAPOL packets and events on
+	 * the parent interface. This is used mainly with hostapd.
+	 */
+	if (drv->hostapd ||
+	    iftype == NL80211_IFTYPE_AP_VLAN ||
+	    iftype == NL80211_IFTYPE_WDS ||
+	    iftype == NL80211_IFTYPE_MONITOR) {
+		/* start listening for EAPOL on this interface */
+		add_ifidx(drv, ifidx, IFIDX_ANY);
+	}
+
+	if (addr && iftype != NL80211_IFTYPE_MONITOR &&
+	    linux_set_ifhwaddr(drv->global->ioctl_sock, ifname, addr)) {
+		nl80211_remove_iface(drv, ifidx);
+		return -1;
+	}
+
+	return ifidx;
+}
+
+
+int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
+			 const char *ifname, enum nl80211_iftype iftype,
+			 const u8 *addr, int wds,
+			 int (*handler)(struct nl_msg *, void *),
+			 void *arg, int use_existing)
+{
+	int ret;
+
+	ret = nl80211_create_iface_once(drv, ifname, iftype, addr, wds, handler,
+					arg);
+
+	/* if error occurred and interface exists already */
+	if (ret == -ENFILE && if_nametoindex(ifname)) {
+		if (use_existing) {
+			wpa_printf(MSG_DEBUG, "nl80211: Continue using existing interface %s",
+				   ifname);
+			if (addr && iftype != NL80211_IFTYPE_MONITOR &&
+			    linux_set_ifhwaddr(drv->global->ioctl_sock, ifname,
+					       addr) < 0 &&
+			    (linux_set_iface_flags(drv->global->ioctl_sock,
+						   ifname, 0) < 0 ||
+			     linux_set_ifhwaddr(drv->global->ioctl_sock, ifname,
+						addr) < 0 ||
+			     linux_set_iface_flags(drv->global->ioctl_sock,
+						   ifname, 1) < 0))
+					return -1;
+			return -ENFILE;
+		}
+		wpa_printf(MSG_INFO, "Try to remove and re-create %s", ifname);
+
+		/* Try to remove the interface that was already there. */
+		nl80211_remove_iface(drv, if_nametoindex(ifname));
+
+		/* Try to create the interface again */
+		ret = nl80211_create_iface_once(drv, ifname, iftype, addr,
+						wds, handler, arg);
+	}
+
+	if (ret >= 0 && is_p2p_net_interface(iftype)) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Interface %s created for P2P - disable 11b rates",
+			   ifname);
+		nl80211_disable_11b_rates(drv, ret, 1);
+	}
+
+	return ret;
+}
+
+
+static int nl80211_setup_ap(struct i802_bss *bss)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Setup AP(%s) - device_ap_sme=%d use_monitor=%d",
+		   bss->ifname, drv->device_ap_sme, drv->use_monitor);
+
+	/*
+	 * Disable Probe Request reporting unless we need it in this way for
+	 * devices that include the AP SME, in the other case (unless using
+	 * monitor iface) we'll get it through the nl_mgmt socket instead.
+	 */
+	if (!drv->device_ap_sme)
+		wpa_driver_nl80211_probe_req_report(bss, 0);
+
+	if (!drv->device_ap_sme && !drv->use_monitor)
+		if (nl80211_mgmt_subscribe_ap(bss))
+			return -1;
+
+	if (drv->device_ap_sme && !drv->use_monitor)
+		if (nl80211_mgmt_subscribe_ap_dev_sme(bss))
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Failed to subscribe for mgmt frames from SME driver - trying to run without it");
+
+	if (!drv->device_ap_sme && drv->use_monitor &&
+	    nl80211_create_monitor_interface(drv) &&
+	    !drv->device_ap_sme)
+		return -1;
+
+	if (drv->device_ap_sme &&
+	    wpa_driver_nl80211_probe_req_report(bss, 1) < 0) {
+		wpa_printf(MSG_DEBUG, "nl80211: Failed to enable "
+			   "Probe Request frame reporting in AP mode");
+		/* Try to survive without this */
+	}
+
+	return 0;
+}
+
+
+static void nl80211_teardown_ap(struct i802_bss *bss)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Teardown AP(%s) - device_ap_sme=%d use_monitor=%d",
+		   bss->ifname, drv->device_ap_sme, drv->use_monitor);
+	if (drv->device_ap_sme) {
+		wpa_driver_nl80211_probe_req_report(bss, 0);
+		if (!drv->use_monitor)
+			nl80211_mgmt_unsubscribe(bss, "AP teardown (dev SME)");
+	} else if (drv->use_monitor)
+		nl80211_remove_monitor_interface(drv);
+	else
+		nl80211_mgmt_unsubscribe(bss, "AP teardown");
+
+	nl80211_put_wiphy_data_ap(bss);
+	wpa_driver_nl80211_del_beacon_all(bss);
+}
+
+
+static int nl80211_tx_control_port(void *priv, const u8 *dest,
+				   u16 proto, const u8 *buf, size_t len,
+				   int no_encrypt, int link_id)
+{
+	struct nl80211_ack_ext_arg ext_arg;
+	struct i802_bss *bss = priv;
+	struct nl_msg *msg;
+	u64 cookie = 0;
+	int ret;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Send over control port dest=" MACSTR
+		   " proto=0x%04x len=%u no_encrypt=%d",
+		   MAC2STR(dest), proto, (unsigned int) len, no_encrypt);
+
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_CONTROL_PORT_FRAME);
+	if (!msg ||
+	    nla_put_u16(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, proto) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, dest) ||
+	    nla_put(msg, NL80211_ATTR_FRAME, len, buf) ||
+	    (no_encrypt &&
+	     nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT)) ||
+	    (link_id != NL80211_DRV_LINK_ID_NA &&
+	     nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	os_memset(&ext_arg, 0, sizeof(struct nl80211_ack_ext_arg));
+	ext_arg.ext_data = &cookie;
+	ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL,
+				 ack_handler_cookie, &ext_arg);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: tx_control_port failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+	} else {
+		struct wpa_driver_nl80211_data *drv = bss->drv;
+
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: tx_control_port cookie=0x%llx",
+			   (long long unsigned int) cookie);
+		drv->eapol_tx_cookie = cookie;
+		drv->eapol_tx_link_id = link_id;
+	}
+
+	return ret;
+}
+
+
+static int nl80211_send_eapol_data(struct i802_bss *bss,
+				   const u8 *addr, const u8 *data,
+				   size_t data_len)
+{
+	struct sockaddr_ll ll;
+	int ret;
+
+	if (bss->drv->eapol_tx_sock < 0) {
+		wpa_printf(MSG_DEBUG, "nl80211: No socket to send EAPOL");
+		return -1;
+	}
+
+	os_memset(&ll, 0, sizeof(ll));
+	ll.sll_family = AF_PACKET;
+	ll.sll_ifindex = bss->ifindex;
+	ll.sll_protocol = htons(ETH_P_PAE);
+	ll.sll_halen = ETH_ALEN;
+	os_memcpy(ll.sll_addr, addr, ETH_ALEN);
+	ret = sendto(bss->drv->eapol_tx_sock, data, data_len, 0,
+		     (struct sockaddr *) &ll, sizeof(ll));
+	if (ret < 0)
+		wpa_printf(MSG_ERROR, "nl80211: EAPOL TX: %s",
+			   strerror(errno));
+
+	return ret;
+}
+
+
+static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+
+static int wpa_driver_nl80211_hapd_send_eapol(
+	void *priv, const u8 *addr, const u8 *data,
+	size_t data_len, int encrypt, const u8 *own_addr, u32 flags,
+	int link_id)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct ieee80211_hdr *hdr;
+	size_t len;
+	u8 *pos;
+	int res;
+	int qos = flags & WPA_STA_WMM;
+
+	/* For now, disable EAPOL TX over control port in AP mode by default
+	 * since it does not provide TX status notifications. */
+	if (drv->control_port_ap &&
+	    (drv->capa.flags & WPA_DRIVER_FLAGS_CONTROL_PORT))
+		return nl80211_tx_control_port(bss, addr, ETH_P_EAPOL,
+					       data, data_len, !encrypt,
+					       link_id);
+
+	if (drv->device_ap_sme || !drv->use_monitor)
+		return nl80211_send_eapol_data(bss, addr, data, data_len);
+
+	len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 +
+		data_len;
+	hdr = os_zalloc(len);
+	if (hdr == NULL) {
+		wpa_printf(MSG_INFO, "nl80211: Failed to allocate EAPOL buffer(len=%lu)",
+			   (unsigned long) len);
+		return -1;
+	}
+
+	hdr->frame_control =
+		IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA);
+	hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS);
+	if (encrypt)
+		hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
+	if (qos) {
+		hdr->frame_control |=
+			host_to_le16(WLAN_FC_STYPE_QOS_DATA << 4);
+	}
+
+	memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN);
+	memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
+	memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
+	pos = (u8 *) (hdr + 1);
+
+	if (qos) {
+		/* Set highest priority in QoS header */
+		pos[0] = 7;
+		pos[1] = 0;
+		pos += 2;
+	}
+
+	memcpy(pos, rfc1042_header, sizeof(rfc1042_header));
+	pos += sizeof(rfc1042_header);
+	WPA_PUT_BE16(pos, ETH_P_PAE);
+	pos += 2;
+	memcpy(pos, data, data_len);
+
+	res = nl80211_send_monitor(drv, hdr, len, encrypt, 0);
+	if (res < 0) {
+		wpa_printf(MSG_ERROR,
+			   "hapd_send_eapol - packet len: %lu - failed",
+			   (unsigned long) len);
+	}
+	os_free(hdr);
+
+	return res;
+}
+
+
+static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr,
+					    unsigned int total_flags,
+					    unsigned int flags_or,
+					    unsigned int flags_and)
+{
+	struct i802_bss *bss = priv;
+	struct nl_msg *msg;
+	struct nlattr *flags;
+	struct nl80211_sta_flag_update upd;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Set STA flags - ifname=%s addr=" MACSTR
+		   " total_flags=0x%x flags_or=0x%x flags_and=0x%x authorized=%d",
+		   bss->ifname, MAC2STR(addr), total_flags, flags_or, flags_and,
+		   !!(total_flags & WPA_STA_AUTHORIZED));
+
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
+		goto fail;
+
+	/*
+	 * Backwards compatibility version using NL80211_ATTR_STA_FLAGS. This
+	 * can be removed eventually.
+	 */
+	flags = nla_nest_start(msg, NL80211_ATTR_STA_FLAGS);
+	if (!flags ||
+	    ((total_flags & WPA_STA_AUTHORIZED) &&
+	     nla_put_flag(msg, NL80211_STA_FLAG_AUTHORIZED)) ||
+	    ((total_flags & WPA_STA_WMM) &&
+	     nla_put_flag(msg, NL80211_STA_FLAG_WME)) ||
+	    ((total_flags & WPA_STA_SHORT_PREAMBLE) &&
+	     nla_put_flag(msg, NL80211_STA_FLAG_SHORT_PREAMBLE)) ||
+	    ((total_flags & WPA_STA_MFP) &&
+	     nla_put_flag(msg, NL80211_STA_FLAG_MFP)) ||
+	    ((total_flags & WPA_STA_TDLS_PEER) &&
+	     nla_put_flag(msg, NL80211_STA_FLAG_TDLS_PEER)))
+		goto fail;
+
+	nla_nest_end(msg, flags);
+
+	os_memset(&upd, 0, sizeof(upd));
+	upd.mask = sta_flags_nl80211(flags_or | ~flags_and);
+	upd.set = sta_flags_nl80211(flags_or);
+	if (nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd))
+		goto fail;
+
+	return send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
+fail:
+	nlmsg_free(msg);
+	return -ENOBUFS;
+}
+
+
+static int driver_nl80211_sta_set_airtime_weight(void *priv, const u8 *addr,
+						 unsigned int weight)
+{
+	struct i802_bss *bss = priv;
+	struct nl_msg *msg;
+	int ret;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Set STA airtime weight - ifname=%s addr=" MACSTR
+		   " weight=%u", bss->ifname, MAC2STR(addr), weight);
+
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+	    nla_put_u16(msg, NL80211_ATTR_AIRTIME_WEIGHT, weight))
+		goto fail;
+
+	ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: SET_STATION[AIRTIME_WEIGHT] failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+	}
+	return ret;
+fail:
+	nlmsg_free(msg);
+	return -ENOBUFS;
+}
+
+
+static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv,
+				 struct wpa_driver_associate_params *params)
+{
+	enum nl80211_iftype nlmode, old_mode;
+
+	if (params->p2p) {
+		wpa_printf(MSG_DEBUG, "nl80211: Setup AP operations for P2P "
+			   "group (GO)");
+		nlmode = NL80211_IFTYPE_P2P_GO;
+	} else
+		nlmode = NL80211_IFTYPE_AP;
+
+	old_mode = drv->nlmode;
+	if (wpa_driver_nl80211_set_mode(drv->first_bss, nlmode)) {
+		nl80211_remove_monitor_interface(drv);
+		return -1;
+	}
+
+	if (params->freq.freq &&
+	    nl80211_set_channel(drv->first_bss, &params->freq, 0)) {
+		if (old_mode != nlmode)
+			wpa_driver_nl80211_set_mode(drv->first_bss, old_mode);
+		nl80211_remove_monitor_interface(drv);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv,
+			      int reset_mode)
+{
+	struct nl_msg *msg;
+	int ret;
+
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_IBSS);
+	ret = send_and_recv_msgs_connect_handle(drv, msg, drv->first_bss, 1);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS failed: ret=%d "
+			   "(%s)", ret, strerror(-ret));
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Leave IBSS request sent successfully");
+	}
+
+	if (reset_mode &&
+	    wpa_driver_nl80211_set_mode(drv->first_bss,
+					NL80211_IFTYPE_STATION)) {
+		wpa_printf(MSG_INFO, "nl80211: Failed to set interface into "
+			   "station mode");
+	}
+
+	return ret;
+}
+
+
+static int nl80211_ht_vht_overrides(struct nl_msg *msg,
+				    struct wpa_driver_associate_params *params)
+{
+	if (params->disable_ht && nla_put_flag(msg, NL80211_ATTR_DISABLE_HT))
+		return -1;
+
+	if (params->htcaps && params->htcaps_mask) {
+		int sz = sizeof(struct ieee80211_ht_capabilities);
+		wpa_hexdump(MSG_DEBUG, "  * htcaps", params->htcaps, sz);
+		wpa_hexdump(MSG_DEBUG, "  * htcaps_mask",
+			    params->htcaps_mask, sz);
+		if (nla_put(msg, NL80211_ATTR_HT_CAPABILITY, sz,
+			    params->htcaps) ||
+		    nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz,
+			    params->htcaps_mask))
+			return -1;
+	}
+
+#ifdef CONFIG_VHT_OVERRIDES
+	if (params->disable_vht) {
+		wpa_printf(MSG_DEBUG, "  * VHT disabled");
+		if (nla_put_flag(msg, NL80211_ATTR_DISABLE_VHT))
+			return -1;
+	}
+
+	if (params->vhtcaps && params->vhtcaps_mask) {
+		int sz = sizeof(struct ieee80211_vht_capabilities);
+		wpa_hexdump(MSG_DEBUG, "  * vhtcaps", params->vhtcaps, sz);
+		wpa_hexdump(MSG_DEBUG, "  * vhtcaps_mask",
+			    params->vhtcaps_mask, sz);
+		if (nla_put(msg, NL80211_ATTR_VHT_CAPABILITY, sz,
+			    params->vhtcaps) ||
+		    nla_put(msg, NL80211_ATTR_VHT_CAPABILITY_MASK, sz,
+			    params->vhtcaps_mask))
+			return -1;
+	}
+#endif /* CONFIG_VHT_OVERRIDES */
+
+#ifdef CONFIG_HE_OVERRIDES
+	if (params->disable_he) {
+		wpa_printf(MSG_DEBUG, "  * HE disabled");
+		if (nla_put_flag(msg, NL80211_ATTR_DISABLE_HE))
+			return -1;
+	}
+#endif /* CONFIG_HE_OVERRIDES */
+
+	if (params->disable_eht) {
+		wpa_printf(MSG_DEBUG, "  * EHT disabled");
+		if (nla_put_flag(msg, NL80211_ATTR_DISABLE_EHT))
+			return -1;
+	}
+
+	return 0;
+}
+
+
+static int wpa_driver_nl80211_ibss(struct wpa_driver_nl80211_data *drv,
+				   struct wpa_driver_associate_params *params)
+{
+	struct nl_msg *msg;
+	int ret = -1;
+	int count = 0;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Join IBSS (ifindex=%d)", drv->ifindex);
+
+	if (wpa_driver_nl80211_set_mode_ibss(drv->first_bss, &params->freq)) {
+		wpa_printf(MSG_INFO, "nl80211: Failed to set interface into "
+			   "IBSS mode");
+		return -1;
+	}
+
+retry:
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_JOIN_IBSS)) ||
+	    params->ssid == NULL || params->ssid_len > sizeof(drv->ssid))
+		goto fail;
+
+	wpa_printf(MSG_DEBUG, "  * SSID=%s",
+		   wpa_ssid_txt(params->ssid, params->ssid_len));
+	if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid))
+		goto fail;
+	os_memcpy(drv->ssid, params->ssid, params->ssid_len);
+	drv->ssid_len = params->ssid_len;
+
+	if (nl80211_put_freq_params(msg, &params->freq) < 0 ||
+	    nl80211_put_beacon_int(msg, params->beacon_int))
+		goto fail;
+
+	ret = nl80211_set_conn_keys(params, msg);
+	if (ret)
+		goto fail;
+
+	if (params->bssid && params->fixed_bssid) {
+		wpa_printf(MSG_DEBUG, "  * BSSID=" MACSTR,
+			   MAC2STR(params->bssid));
+		if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid))
+			goto fail;
+	}
+
+	if (params->fixed_freq) {
+		wpa_printf(MSG_DEBUG, "  * fixed_freq");
+		if (nla_put_flag(msg, NL80211_ATTR_FREQ_FIXED))
+			goto fail;
+	}
+
+	if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_PSK ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256) {
+		wpa_printf(MSG_DEBUG, "  * control port");
+		if (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT))
+			goto fail;
+	}
+
+	if (params->wpa_ie) {
+		wpa_hexdump(MSG_DEBUG,
+			    "  * Extra IEs for Beacon/Probe Response frames",
+			    params->wpa_ie, params->wpa_ie_len);
+		if (nla_put(msg, NL80211_ATTR_IE, params->wpa_ie_len,
+			    params->wpa_ie))
+			goto fail;
+	}
+
+	ret = nl80211_ht_vht_overrides(msg, params);
+	if (ret < 0)
+		goto fail;
+
+	ret = send_and_recv_msgs_connect_handle(drv, msg, drv->first_bss, 1);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Join IBSS failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+		count++;
+		if (ret == -EALREADY && count == 1) {
+			wpa_printf(MSG_DEBUG, "nl80211: Retry IBSS join after "
+				   "forced leave");
+			nl80211_leave_ibss(drv, 0);
+			nlmsg_free(msg);
+			goto retry;
+		}
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Join IBSS request sent successfully");
+	}
+
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+static int nl80211_put_fils_connect_params(struct wpa_driver_nl80211_data *drv,
+					   struct wpa_driver_associate_params *params,
+					   struct nl_msg *msg)
+{
+	if (params->fils_erp_username_len) {
+		wpa_hexdump_ascii(MSG_DEBUG, "  * FILS ERP EMSKname/username",
+				  params->fils_erp_username,
+				  params->fils_erp_username_len);
+		if (nla_put(msg, NL80211_ATTR_FILS_ERP_USERNAME,
+			    params->fils_erp_username_len,
+			    params->fils_erp_username))
+			return -1;
+	}
+
+	if (params->fils_erp_realm_len) {
+		wpa_hexdump_ascii(MSG_DEBUG, "  * FILS ERP Realm",
+				  params->fils_erp_realm,
+				  params->fils_erp_realm_len);
+		if (nla_put(msg, NL80211_ATTR_FILS_ERP_REALM,
+			    params->fils_erp_realm_len, params->fils_erp_realm))
+			return -1;
+	}
+
+	if (params->fils_erp_rrk_len) {
+		wpa_printf(MSG_DEBUG, "  * FILS ERP next seq %u",
+			   params->fils_erp_next_seq_num);
+		if (nla_put_u16(msg, NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM,
+				params->fils_erp_next_seq_num))
+			return -1;
+
+		wpa_printf(MSG_DEBUG, "  * FILS ERP rRK (len=%lu)",
+			   (unsigned long) params->fils_erp_rrk_len);
+		if (nla_put(msg, NL80211_ATTR_FILS_ERP_RRK,
+			    params->fils_erp_rrk_len, params->fils_erp_rrk))
+			return -1;
+	}
+
+	return 0;
+}
+
+
+static unsigned int num_bits_set(u32 val)
+{
+	unsigned int c;
+
+	for (c = 0; val; c++)
+		val &= val - 1;
+
+	return c;
+}
+
+
+static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
+				  struct wpa_driver_associate_params *params,
+				  struct nl_msg *msg)
+{
+	if (params->mld_params.mld_addr && params->mld_params.valid_links > 0) {
+		struct wpa_driver_mld_params *mld_params = &params->mld_params;
+		struct nlattr *links, *attr;
+		int i;
+		u8 link_id;
+
+		wpa_printf(MSG_DEBUG, "  * MLD: MLD addr=" MACSTR,
+			   MAC2STR(mld_params->mld_addr));
+
+		if (nla_put(msg, NL80211_ATTR_MLD_ADDR, ETH_ALEN,
+			    mld_params->mld_addr) ||
+		    nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
+			       mld_params->assoc_link_id))
+			return -1;
+
+		links = nla_nest_start(msg, NL80211_ATTR_MLO_LINKS);
+		if (!links)
+			return -1;
+
+		attr = nla_nest_start(msg, 0);
+		if (!attr)
+			return -1;
+
+		/* First add the association link ID */
+		link_id = mld_params->assoc_link_id;
+		if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id) ||
+		    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
+			    mld_params->mld_links[link_id].bssid) ||
+		    nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
+				mld_params->mld_links[link_id].freq))
+			return -1;
+
+		os_memcpy(drv->sta_mlo_info.links[link_id].bssid,
+			  mld_params->mld_links[link_id].bssid, ETH_ALEN);
+
+		nla_nest_end(msg, attr);
+
+		for (i = 1, link_id = 0; link_id < MAX_NUM_MLD_LINKS;
+		     link_id++) {
+			if (!(mld_params->valid_links & BIT(link_id)) ||
+			    link_id == mld_params->assoc_link_id)
+				continue;
+
+			attr = nla_nest_start(msg, i);
+			if (!attr)
+				return -1;
+
+			if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
+				       link_id) ||
+			    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
+				    mld_params->mld_links[link_id].bssid) ||
+			    nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
+					mld_params->mld_links[link_id].freq) ||
+			    (mld_params->mld_links[link_id].ies &&
+			     mld_params->mld_links[i].ies_len &&
+			     nla_put(msg, NL80211_ATTR_IE,
+				     mld_params->mld_links[link_id].ies_len,
+				     mld_params->mld_links[link_id].ies)))
+				return -1;
+
+			os_memcpy(drv->sta_mlo_info.links[link_id].bssid,
+				  mld_params->mld_links[link_id].bssid,
+				  ETH_ALEN);
+			nla_nest_end(msg, attr);
+			i++;
+		}
+
+		nla_nest_end(msg, links);
+
+		os_memcpy(drv->sta_mlo_info.ap_mld_addr,
+			  params->mld_params.mld_addr, ETH_ALEN);
+		drv->sta_mlo_info.assoc_link_id = mld_params->assoc_link_id;
+		drv->sta_mlo_info.req_links = mld_params->valid_links;
+	}
+
+	if (nla_put_flag(msg, NL80211_ATTR_IFACE_SOCKET_OWNER))
+		return -1;
+
+	if (params->bssid && !params->mld_params.mld_addr) {
+		wpa_printf(MSG_DEBUG, "  * bssid=" MACSTR,
+			   MAC2STR(params->bssid));
+		if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid))
+			return -1;
+	}
+
+	if (params->bssid_hint) {
+		wpa_printf(MSG_DEBUG, "  * bssid_hint=" MACSTR,
+			   MAC2STR(params->bssid_hint));
+		if (nla_put(msg, NL80211_ATTR_MAC_HINT, ETH_ALEN,
+			    params->bssid_hint))
+			return -1;
+	}
+
+	if (params->freq.freq && !params->mld_params.mld_addr) {
+		wpa_printf(MSG_DEBUG, "  * freq=%d", params->freq.freq);
+		if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
+				params->freq.freq))
+			return -1;
+		drv->assoc_freq = params->freq.freq;
+	} else
+		drv->assoc_freq = 0;
+
+	if (params->freq_hint) {
+		wpa_printf(MSG_DEBUG, "  * freq_hint=%d", params->freq_hint);
+		if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ_HINT,
+				params->freq_hint))
+			return -1;
+	}
+
+	if (params->freq.edmg.channels && params->freq.edmg.bw_config) {
+		wpa_printf(MSG_DEBUG,
+			   "  * EDMG configuration: channels=0x%x bw_config=%d",
+			   params->freq.edmg.channels,
+			   params->freq.edmg.bw_config);
+		if (nla_put_u8(msg, NL80211_ATTR_WIPHY_EDMG_CHANNELS,
+			       params->freq.edmg.channels) ||
+		    nla_put_u8(msg, NL80211_ATTR_WIPHY_EDMG_BW_CONFIG,
+			       params->freq.edmg.bw_config))
+			return -1;
+	}
+
+	if (params->bg_scan_period >= 0) {
+		wpa_printf(MSG_DEBUG, "  * bg scan period=%d",
+			   params->bg_scan_period);
+		if (nla_put_u16(msg, NL80211_ATTR_BG_SCAN_PERIOD,
+				params->bg_scan_period))
+			return -1;
+	}
+
+	if (params->ssid) {
+		wpa_printf(MSG_DEBUG, "  * SSID=%s",
+			   wpa_ssid_txt(params->ssid, params->ssid_len));
+		if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len,
+			    params->ssid))
+			return -1;
+		if (params->ssid_len > sizeof(drv->ssid))
+			return -1;
+		os_memcpy(drv->ssid, params->ssid, params->ssid_len);
+		drv->ssid_len = params->ssid_len;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "  * IEs", params->wpa_ie, params->wpa_ie_len);
+	if (params->wpa_ie &&
+	    nla_put(msg, NL80211_ATTR_IE, params->wpa_ie_len, params->wpa_ie))
+		return -1;
+
+	if (params->wpa_proto) {
+		enum nl80211_wpa_versions ver = 0;
+
+		if (params->wpa_proto & WPA_PROTO_WPA)
+			ver |= NL80211_WPA_VERSION_1;
+		if (params->wpa_proto & WPA_PROTO_RSN)
+			ver |= NL80211_WPA_VERSION_2;
+
+		wpa_printf(MSG_DEBUG, "  * WPA Versions 0x%x", ver);
+		if (nla_put_u32(msg, NL80211_ATTR_WPA_VERSIONS, ver))
+			return -1;
+	}
+
+	if (params->pairwise_suite != WPA_CIPHER_NONE) {
+		u32 cipher = wpa_cipher_to_cipher_suite(params->pairwise_suite);
+		wpa_printf(MSG_DEBUG, "  * pairwise=0x%x", cipher);
+		if (nla_put_u32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE,
+				cipher))
+			return -1;
+	}
+
+	if (params->group_suite == WPA_CIPHER_GTK_NOT_USED &&
+	    !(drv->capa.enc & WPA_DRIVER_CAPA_ENC_GTK_NOT_USED)) {
+		/*
+		 * This is likely to work even though many drivers do not
+		 * advertise support for operations without GTK.
+		 */
+		wpa_printf(MSG_DEBUG, "  * skip group cipher configuration for GTK_NOT_USED due to missing driver support advertisement");
+	} else if (params->group_suite != WPA_CIPHER_NONE) {
+		u32 cipher = wpa_cipher_to_cipher_suite(params->group_suite);
+		wpa_printf(MSG_DEBUG, "  * group=0x%x", cipher);
+		if (nla_put_u32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher))
+			return -1;
+	}
+
+	if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_PSK ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_FT_IEEE8021X ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_FT_PSK ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_CCKM ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_OSEN ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_SAE ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_SAE_EXT_KEY ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE_EXT_KEY ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_FT_IEEE8021X_SHA384 ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_FILS_SHA256 ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_FILS_SHA384 ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA256 ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA384 ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_OWE ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_DPP) {
+		u32 *mgmt;
+		unsigned int akm_count = 1, i;
+
+		/*
+		 * Make sure the driver has capability to handle default AKM in
+		 * key_mgmt_suite plus allowed AKMs in allowed_key_mgmts.
+		 */
+		if (drv->capa.max_num_akms <=
+		    num_bits_set(params->allowed_key_mgmts)) {
+			wpa_printf(MSG_INFO,
+				   "nl80211: Not enough support for the allowed AKMs (max_num_akms=%u <= num_bits_set=%u)",
+				   drv->capa.max_num_akms,
+				   num_bits_set(params->allowed_key_mgmts));
+			return -1;
+		}
+
+		mgmt = os_malloc(sizeof(u32) * drv->capa.max_num_akms);
+		if (!mgmt)
+			return -1;
+
+		mgmt[0] = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
+
+		switch (params->key_mgmt_suite) {
+		case WPA_KEY_MGMT_CCKM:
+			mgmt[0] = RSN_AUTH_KEY_MGMT_CCKM;
+			break;
+		case WPA_KEY_MGMT_IEEE8021X:
+			mgmt[0] = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
+			break;
+		case WPA_KEY_MGMT_FT_IEEE8021X:
+			mgmt[0] = RSN_AUTH_KEY_MGMT_FT_802_1X;
+			break;
+		case WPA_KEY_MGMT_FT_PSK:
+			mgmt[0] = RSN_AUTH_KEY_MGMT_FT_PSK;
+			break;
+		case WPA_KEY_MGMT_IEEE8021X_SHA256:
+			mgmt[0] = RSN_AUTH_KEY_MGMT_802_1X_SHA256;
+			break;
+		case WPA_KEY_MGMT_PSK_SHA256:
+			mgmt[0] = RSN_AUTH_KEY_MGMT_PSK_SHA256;
+			break;
+		case WPA_KEY_MGMT_OSEN:
+			mgmt[0] = RSN_AUTH_KEY_MGMT_OSEN;
+			break;
+		case WPA_KEY_MGMT_SAE:
+			mgmt[0] = RSN_AUTH_KEY_MGMT_SAE;
+			break;
+		case WPA_KEY_MGMT_SAE_EXT_KEY:
+			mgmt[0] = RSN_AUTH_KEY_MGMT_SAE_EXT_KEY;
+			break;
+		case WPA_KEY_MGMT_FT_SAE:
+			mgmt[0] = RSN_AUTH_KEY_MGMT_FT_SAE;
+			break;
+		case WPA_KEY_MGMT_FT_SAE_EXT_KEY:
+			mgmt[0] = RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY;
+			break;
+		case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
+			mgmt[0] = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
+			break;
+		case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
+			mgmt[0] = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
+			break;
+		case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+			mgmt[0] = RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384;
+			break;
+		case WPA_KEY_MGMT_FILS_SHA256:
+			mgmt[0] = RSN_AUTH_KEY_MGMT_FILS_SHA256;
+			break;
+		case WPA_KEY_MGMT_FILS_SHA384:
+			mgmt[0] = RSN_AUTH_KEY_MGMT_FILS_SHA384;
+			break;
+		case WPA_KEY_MGMT_FT_FILS_SHA256:
+			mgmt[0] = RSN_AUTH_KEY_MGMT_FT_FILS_SHA256;
+			break;
+		case WPA_KEY_MGMT_FT_FILS_SHA384:
+			mgmt[0] = RSN_AUTH_KEY_MGMT_FT_FILS_SHA384;
+			break;
+		case WPA_KEY_MGMT_OWE:
+			mgmt[0] = RSN_AUTH_KEY_MGMT_OWE;
+			break;
+		case WPA_KEY_MGMT_DPP:
+			mgmt[0] = RSN_AUTH_KEY_MGMT_DPP;
+			break;
+		case WPA_KEY_MGMT_PSK:
+		default:
+			mgmt[0] = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
+			break;
+		}
+
+		if (drv->capa.max_num_akms > 1) {
+			akm_count += wpa_key_mgmt_to_suites(
+				params->allowed_key_mgmts, &mgmt[1],
+				drv->capa.max_num_akms - 1);
+		}
+
+		for (i = 0; i < akm_count; i++)
+			wpa_printf(MSG_DEBUG, "  * akm[%d]=0x%x", i, mgmt[i]);
+
+		if (nla_put(msg, NL80211_ATTR_AKM_SUITES,
+			    akm_count * sizeof(u32), mgmt)) {
+			os_free(mgmt);
+			return -1;
+		}
+
+		os_free(mgmt);
+	}
+
+	if (params->req_handshake_offload &&
+	    (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X)) {
+		    wpa_printf(MSG_DEBUG, "  * WANT_1X_4WAY_HS");
+		    if (nla_put_flag(msg, NL80211_ATTR_WANT_1X_4WAY_HS))
+			    return -1;
+	    }
+
+	/* Add PSK in case of 4-way handshake offload */
+	if (params->psk &&
+	    (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK)) {
+		wpa_hexdump_key(MSG_DEBUG, "  * PSK", params->psk, 32);
+		if (nla_put(msg, NL80211_ATTR_PMK, 32, params->psk))
+			return -1;
+	}
+
+	if (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT))
+		return -1;
+
+	if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_NO_WPA &&
+	    (params->pairwise_suite == WPA_CIPHER_NONE ||
+	     params->pairwise_suite == WPA_CIPHER_WEP104 ||
+	     params->pairwise_suite == WPA_CIPHER_WEP40) &&
+	    (nla_put_u16(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, ETH_P_PAE) ||
+	     nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT)))
+		return -1;
+
+	if (params->rrm_used) {
+		u32 drv_rrm_flags = drv->capa.rrm_flags;
+		if ((!((drv_rrm_flags &
+			WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES) &&
+		       (drv_rrm_flags & WPA_DRIVER_FLAGS_QUIET)) &&
+		     !(drv_rrm_flags & WPA_DRIVER_FLAGS_SUPPORT_RRM)) ||
+		    nla_put_flag(msg, NL80211_ATTR_USE_RRM))
+			return -1;
+	}
+
+	if (nl80211_ht_vht_overrides(msg, params) < 0)
+		return -1;
+
+	if (params->p2p)
+		wpa_printf(MSG_DEBUG, "  * P2P group");
+
+	if (params->pbss) {
+		wpa_printf(MSG_DEBUG, "  * PBSS");
+		if (nla_put_flag(msg, NL80211_ATTR_PBSS))
+			return -1;
+	}
+
+	drv->connect_reassoc = 0;
+	if (params->prev_bssid) {
+		wpa_printf(MSG_DEBUG, "  * prev_bssid=" MACSTR,
+			   MAC2STR(params->prev_bssid));
+		if (nla_put(msg, NL80211_ATTR_PREV_BSSID, ETH_ALEN,
+			    params->prev_bssid))
+			return -1;
+		drv->connect_reassoc = 1;
+	}
+
+	if ((params->auth_alg & WPA_AUTH_ALG_FILS) &&
+	    nl80211_put_fils_connect_params(drv, params, msg) != 0)
+		return -1;
+
+	if ((wpa_key_mgmt_sae(params->key_mgmt_suite) ||
+	     wpa_key_mgmt_sae(params->allowed_key_mgmts)) &&
+	    (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) &&
+	    nla_put_flag(msg, NL80211_ATTR_EXTERNAL_AUTH_SUPPORT))
+		return -1;
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
+	    nla_put_flag(msg, NL80211_ATTR_MLO_SUPPORT))
+		return -1;
+
+	return 0;
+}
+
+
+static int wpa_driver_nl80211_try_connect(
+	struct wpa_driver_nl80211_data *drv,
+	struct wpa_driver_associate_params *params,
+	struct i802_bss *bss)
+{
+	struct nl_msg *msg;
+	enum nl80211_auth_type type;
+	int ret;
+	int algs;
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+	if (params->req_key_mgmt_offload && params->psk &&
+	    (wpa_key_mgmt_wpa_psk_no_sae(params->key_mgmt_suite) ||
+	     wpa_key_mgmt_wpa_psk_no_sae(params->allowed_key_mgmts))) {
+		wpa_printf(MSG_DEBUG, "nl80211: Key management set PSK");
+		ret = issue_key_mgmt_set_key(drv, params->psk, 32);
+		if (ret)
+			return ret;
+	}
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+	wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex);
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_CONNECT);
+	if (!msg)
+		return -1;
+
+	ret = nl80211_connect_common(drv, params, msg);
+	if (ret)
+		goto fail;
+
+	if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED &&
+	    nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED))
+		goto fail;
+
+	if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_OPTIONAL &&
+	    (drv->capa.flags & WPA_DRIVER_FLAGS_MFP_OPTIONAL) &&
+	    nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_OPTIONAL))
+		goto fail;
+
+#ifdef CONFIG_SAE
+	if ((wpa_key_mgmt_sae(params->key_mgmt_suite) ||
+	     wpa_key_mgmt_sae(params->allowed_key_mgmts)) &&
+	    nl80211_put_sae_pwe(msg, params->sae_pwe) < 0)
+		goto fail;
+#endif /* CONFIG_SAE */
+
+	algs = 0;
+	if (params->auth_alg & WPA_AUTH_ALG_OPEN)
+		algs++;
+	if (params->auth_alg & WPA_AUTH_ALG_SHARED)
+		algs++;
+	if (params->auth_alg & WPA_AUTH_ALG_LEAP)
+		algs++;
+	if (params->auth_alg & WPA_AUTH_ALG_FILS)
+		algs++;
+	if (params->auth_alg & WPA_AUTH_ALG_FT)
+		algs++;
+	if (algs > 1) {
+		wpa_printf(MSG_DEBUG, "  * Leave out Auth Type for automatic "
+			   "selection");
+		goto skip_auth_type;
+	}
+
+	type = get_nl_auth_type(params->auth_alg);
+	wpa_printf(MSG_DEBUG, "  * Auth Type %d", type);
+	if (type == NL80211_AUTHTYPE_MAX ||
+	    nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
+		goto fail;
+
+skip_auth_type:
+	ret = nl80211_set_conn_keys(params, msg);
+	if (ret)
+		goto fail;
+
+	ret = send_and_recv_msgs_connect_handle(drv, msg, bss, 1);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d "
+			   "(%s)", ret, strerror(-ret));
+	} else {
+#ifdef CONFIG_DRIVER_NL80211_QCA
+		drv->roam_indication_done = false;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Connect request send successfully");
+	}
+
+fail:
+	nl80211_nlmsg_clear(msg);
+	nlmsg_free(msg);
+	return ret;
+
+}
+
+
+static int wpa_driver_nl80211_connect(
+	struct wpa_driver_nl80211_data *drv,
+	struct wpa_driver_associate_params *params,
+	struct i802_bss *bss)
+{
+	int ret;
+
+	/* Store the connection attempted bssid for future use */
+	if (params->bssid)
+		os_memcpy(drv->auth_attempt_bssid, params->bssid, ETH_ALEN);
+	else
+		os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN);
+
+	ret = wpa_driver_nl80211_try_connect(drv, params, bss);
+	if (ret == -EALREADY) {
+		/*
+		 * cfg80211 does not currently accept new connections if
+		 * we are already connected. As a workaround, force
+		 * disconnection and try again.
+		 */
+		wpa_printf(MSG_DEBUG, "nl80211: Explicitly "
+			   "disconnecting before reassociation "
+			   "attempt");
+		if (wpa_driver_nl80211_disconnect(
+			    drv, WLAN_REASON_PREV_AUTH_NOT_VALID, bss))
+			return -1;
+		ret = wpa_driver_nl80211_try_connect(drv, params, bss);
+	}
+	return ret;
+}
+
+
+static int wpa_driver_nl80211_associate(
+	void *priv, struct wpa_driver_associate_params *params)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int ret = -1;
+	struct nl_msg *msg;
+
+	nl80211_unmask_11b_rates(bss);
+
+	if (params->mode == IEEE80211_MODE_AP)
+		return wpa_driver_nl80211_ap(drv, params);
+
+	if (params->mode == IEEE80211_MODE_IBSS)
+		return wpa_driver_nl80211_ibss(drv, params);
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) {
+		enum nl80211_iftype nlmode = params->p2p ?
+			NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION;
+
+		if (wpa_driver_nl80211_set_mode(priv, nlmode) < 0)
+			return -1;
+		if (wpa_key_mgmt_sae(params->key_mgmt_suite) ||
+		    wpa_key_mgmt_sae(params->allowed_key_mgmts))
+			bss->use_nl_connect = 1;
+		else
+			bss->use_nl_connect = 0;
+
+		return wpa_driver_nl80211_connect(drv, params, bss);
+	}
+
+	nl80211_mark_disconnected(drv);
+
+	wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)",
+		   drv->ifindex);
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_ASSOCIATE);
+	if (!msg)
+		return -1;
+
+	ret = nl80211_connect_common(drv, params, msg);
+	if (ret)
+		goto fail;
+
+	if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED &&
+	    nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED))
+		goto fail;
+
+	if (params->fils_kek) {
+		wpa_printf(MSG_DEBUG, "  * FILS KEK (len=%u)",
+			   (unsigned int) params->fils_kek_len);
+		if (nla_put(msg, NL80211_ATTR_FILS_KEK, params->fils_kek_len,
+			    params->fils_kek))
+			goto fail;
+	}
+	if (params->fils_nonces) {
+		wpa_hexdump(MSG_DEBUG, "  * FILS nonces (for AAD)",
+			    params->fils_nonces,
+			    params->fils_nonces_len);
+		if (nla_put(msg, NL80211_ATTR_FILS_NONCES,
+			    params->fils_nonces_len, params->fils_nonces))
+			goto fail;
+	}
+
+	ret = send_and_recv_msgs_connect_handle(drv, msg, drv->first_bss, 1);
+	msg = NULL;
+	if (ret) {
+		wpa_dbg(drv->ctx, MSG_DEBUG,
+			"nl80211: MLME command failed (assoc): ret=%d (%s)",
+			ret, strerror(-ret));
+		nl80211_dump_scan(drv);
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Association request send successfully");
+	}
+
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+static int nl80211_set_mode(struct wpa_driver_nl80211_data *drv,
+			    int ifindex, enum nl80211_iftype mode)
+{
+	struct nl_msg *msg;
+	int ret = -ENOBUFS;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Set mode ifindex %d iftype %d (%s)",
+		   ifindex, mode, nl80211_iftype_str(mode));
+
+	msg = nl80211_cmd_msg(drv->first_bss, 0, NL80211_CMD_SET_INTERFACE);
+	if (!msg || nla_put_u32(msg, NL80211_ATTR_IFTYPE, mode))
+		goto fail;
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	msg = NULL;
+	if (!ret)
+		return 0;
+fail:
+	nlmsg_free(msg);
+	wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface %d to mode %d:"
+		   " %d (%s)", ifindex, mode, ret, strerror(-ret));
+	return ret;
+}
+
+
+static int wpa_driver_nl80211_set_mode_impl(
+		struct i802_bss *bss,
+		enum nl80211_iftype nlmode,
+		struct hostapd_freq_params *desired_freq_params)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int ret = -1;
+	int i;
+	int was_ap = is_ap_interface(drv->nlmode);
+	int res;
+	int mode_switch_res;
+
+	if (TEST_FAIL())
+		return -1;
+
+	mode_switch_res = nl80211_set_mode(drv, drv->ifindex, nlmode);
+	if (mode_switch_res && nlmode == nl80211_get_ifmode(bss))
+		mode_switch_res = 0;
+
+	if (mode_switch_res == 0) {
+		drv->nlmode = nlmode;
+		ret = 0;
+		goto done;
+	}
+
+	if (mode_switch_res == -ENODEV)
+		return -1;
+
+	if (nlmode == drv->nlmode) {
+		wpa_printf(MSG_DEBUG, "nl80211: Interface already in "
+			   "requested mode - ignore error");
+		ret = 0;
+		goto done; /* Already in the requested mode */
+	}
+
+	/* mac80211 doesn't allow mode changes while the device is up, so
+	 * take the device down, try to set the mode again, and bring the
+	 * device back up.
+	 */
+	wpa_printf(MSG_DEBUG, "nl80211: Try mode change after setting "
+		   "interface down");
+	for (i = 0; i < 10; i++) {
+		res = i802_set_iface_flags(bss, 0);
+		if (res == -EACCES || res == -ENODEV)
+			break;
+		if (res != 0) {
+			wpa_printf(MSG_DEBUG, "nl80211: Failed to set "
+				   "interface down");
+			os_sleep(0, 100000);
+			continue;
+		}
+
+		/*
+		 * Setting the mode will fail for some drivers if the phy is
+		 * on a frequency that the mode is disallowed in.
+		 */
+		if (desired_freq_params) {
+			res = nl80211_set_channel(bss, desired_freq_params, 0);
+			if (res) {
+				wpa_printf(MSG_DEBUG,
+					   "nl80211: Failed to set frequency on interface");
+			}
+		}
+
+		if (i == 0 && was_ap && !is_ap_interface(nlmode) &&
+		    bss->brname[0] &&
+		    (bss->added_if_into_bridge || bss->already_in_bridge)) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Remove AP interface %s temporarily from the bridge %s to allow its mode to be set to STATION",
+				   bss->ifname, bss->brname);
+			if (linux_br_del_if(drv->global->ioctl_sock,
+					    bss->brname, bss->ifname) < 0)
+				wpa_printf(MSG_INFO,
+					   "nl80211: Failed to remove interface %s from bridge %s: %s",
+					   bss->ifname, bss->brname,
+					   strerror(errno));
+		}
+
+		/* Try to set the mode again while the interface is down */
+		mode_switch_res = nl80211_set_mode(drv, drv->ifindex, nlmode);
+		if (mode_switch_res == -EBUSY) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Delaying mode set while interface going down");
+			os_sleep(0, 100000);
+			continue;
+		}
+		ret = mode_switch_res;
+		break;
+	}
+
+	if (!ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Mode change succeeded while "
+			   "interface is down");
+		drv->nlmode = nlmode;
+		drv->ignore_if_down_event = 1;
+	}
+
+	/* Bring the interface back up */
+	res = linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1);
+	if (res != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Failed to set interface up after switching mode");
+		ret = -1;
+	}
+
+done:
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Interface mode change to %d "
+			   "from %d failed", nlmode, drv->nlmode);
+		return ret;
+	}
+
+	if (is_p2p_net_interface(nlmode)) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Interface %s mode change to P2P - disable 11b rates",
+			   bss->ifname);
+		nl80211_disable_11b_rates(drv, drv->ifindex, 1);
+	} else if (drv->disabled_11b_rates) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Interface %s mode changed to non-P2P - re-enable 11b rates",
+			   bss->ifname);
+		nl80211_disable_11b_rates(drv, drv->ifindex, 0);
+	}
+
+	if (is_ap_interface(nlmode)) {
+		nl80211_mgmt_unsubscribe(bss, "start AP");
+		/* Setup additional AP mode functionality if needed */
+		if (nl80211_setup_ap(bss))
+			return -1;
+	} else if (was_ap) {
+		/* Remove additional AP mode functionality */
+		nl80211_teardown_ap(bss);
+	} else {
+		nl80211_mgmt_unsubscribe(bss, "mode change");
+	}
+
+	if (is_mesh_interface(nlmode) &&
+	    nl80211_mgmt_subscribe_mesh(bss))
+		return -1;
+
+	if (!bss->in_deinit && !is_ap_interface(nlmode) &&
+	    !is_mesh_interface(nlmode) &&
+	    nl80211_mgmt_subscribe_non_ap(bss) < 0)
+		wpa_printf(MSG_DEBUG, "nl80211: Failed to register Action "
+			   "frame processing - ignore for now");
+
+	return 0;
+}
+
+
+void nl80211_restore_ap_mode(struct i802_bss *bss)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int was_ap = is_ap_interface(drv->nlmode);
+
+	wpa_driver_nl80211_set_mode(bss, drv->ap_scan_as_station);
+	if (!was_ap && is_ap_interface(drv->ap_scan_as_station) &&
+	    bss->brname[0] &&
+	    (bss->added_if_into_bridge || bss->already_in_bridge)) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Add AP interface %s back into the bridge %s",
+			   bss->ifname, bss->brname);
+		if (linux_br_add_if(drv->global->ioctl_sock, bss->brname,
+				    bss->ifname) < 0) {
+			wpa_printf(MSG_WARNING,
+				   "nl80211: Failed to add interface %s into bridge %s: %s",
+				   bss->ifname, bss->brname, strerror(errno));
+		}
+	}
+	drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
+}
+
+
+int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
+				enum nl80211_iftype nlmode)
+{
+	return wpa_driver_nl80211_set_mode_impl(bss, nlmode, NULL);
+}
+
+
+static int wpa_driver_nl80211_set_mode_ibss(struct i802_bss *bss,
+					    struct hostapd_freq_params *freq)
+{
+	return wpa_driver_nl80211_set_mode_impl(bss, NL80211_IFTYPE_ADHOC,
+						freq);
+}
+
+
+static int wpa_driver_nl80211_get_capa(void *priv,
+				       struct wpa_driver_capa *capa)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	if (!drv->has_capability)
+		return -1;
+	os_memcpy(capa, &drv->capa, sizeof(*capa));
+	if (drv->extended_capa && drv->extended_capa_mask) {
+		capa->extended_capa = drv->extended_capa;
+		capa->extended_capa_mask = drv->extended_capa_mask;
+		capa->extended_capa_len = drv->extended_capa_len;
+	}
+
+	return 0;
+}
+
+
+static int wpa_driver_nl80211_set_operstate(void *priv, int state)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Set %s operstate %d->%d (%s)",
+		   bss->ifname, drv->operstate, state,
+		   state ? "UP" : "DORMANT");
+	drv->operstate = state;
+	return netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, -1,
+				      state ? IF_OPER_UP : IF_OPER_DORMANT);
+}
+
+
+static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nl80211_sta_flag_update upd;
+	int ret;
+	const u8 *connected_addr = drv->sta_mlo_info.valid_links ?
+		drv->sta_mlo_info.ap_mld_addr : drv->bssid;
+
+	if (!drv->associated && is_zero_ether_addr(connected_addr) &&
+	    !authorized) {
+		wpa_printf(MSG_DEBUG, "nl80211: Skip set_supp_port(unauthorized) while not associated");
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: Set supplicant port %sauthorized for "
+		   MACSTR, authorized ? "" : "un", MAC2STR(connected_addr));
+
+	os_memset(&upd, 0, sizeof(upd));
+	upd.mask = BIT(NL80211_STA_FLAG_AUTHORIZED);
+	if (authorized)
+		upd.set = BIT(NL80211_STA_FLAG_AUTHORIZED);
+
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, connected_addr) ||
+	    nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (!ret)
+		return 0;
+	wpa_printf(MSG_DEBUG, "nl80211: Failed to set STA flag: %d (%s)",
+		   ret, strerror(-ret));
+	return ret;
+}
+
+
+/* Set kernel driver on given frequency (MHz) */
+static int i802_set_freq(void *priv, struct hostapd_freq_params *freq)
+{
+	struct i802_bss *bss = priv;
+	return nl80211_set_channel(bss, freq, 0);
+}
+
+
+static inline int min_int(int a, int b)
+{
+	if (a < b)
+		return a;
+	return b;
+}
+
+
+static int get_key_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	/*
+	 * TODO: validate the key index and mac address!
+	 * Otherwise, there's a race condition as soon as
+	 * the kernel starts sending key notifications.
+	 */
+
+	if (tb[NL80211_ATTR_KEY_SEQ])
+		memcpy(arg, nla_data(tb[NL80211_ATTR_KEY_SEQ]),
+		       min_int(nla_len(tb[NL80211_ATTR_KEY_SEQ]), 6));
+	nl80211_nlmsg_clear(msg);
+	return NL_SKIP;
+}
+
+
+static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr,
+			   int idx, int link_id, u8 *seq)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int res;
+
+	msg = nl80211_ifindex_msg(drv, if_nametoindex(iface), 0,
+				  NL80211_CMD_GET_KEY);
+	if (!msg ||
+	    (addr && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) ||
+	    (link_id != NL80211_DRV_LINK_ID_NA &&
+	     nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) ||
+	    nla_put_u8(msg, NL80211_ATTR_KEY_IDX, idx)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	os_memset(seq, 0, 6);
+	res = send_and_recv_msgs(drv, msg, get_key_handler, seq, NULL, NULL);
+	if (res) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Failed to get current TX sequence for a key (link_id=%d idx=%d): %d (%s)",
+			   link_id, idx, res, strerror(-res));
+	}
+
+	return res;
+}
+
+
+static int i802_set_rts(void *priv, int rts)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+	u32 val;
+
+	if (rts >= 2347 || rts == -1)
+		val = (u32) -1;
+	else
+		val = rts;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_WIPHY)) ||
+	    nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, val)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (!ret)
+		return 0;
+	wpa_printf(MSG_DEBUG, "nl80211: Failed to set RTS threshold %d: "
+		   "%d (%s)", rts, ret, strerror(-ret));
+	return ret;
+}
+
+
+static int i802_set_frag(void *priv, int frag)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+	u32 val;
+
+	if (frag >= 2346 || frag == -1)
+		val = (u32) -1;
+	else
+		val = frag;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_WIPHY)) ||
+	    nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, val)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (!ret)
+		return 0;
+	wpa_printf(MSG_DEBUG, "nl80211: Failed to set fragmentation threshold "
+		   "%d: %d (%s)", frag, ret, strerror(-ret));
+	return ret;
+}
+
+
+static int i802_flush(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct nl_msg *msg;
+	int res;
+
+	wpa_printf(MSG_DEBUG, "nl80211: flush -> DEL_STATION %s (all)",
+		   bss->ifname);
+
+	/*
+	 * XXX: FIX! this needs to flush all VLANs too
+	 */
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_STATION);
+	res = send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
+	if (res) {
+		wpa_printf(MSG_DEBUG, "nl80211: Station flush failed: ret=%d "
+			   "(%s)", res, strerror(-res));
+	}
+	return res;
+}
+
+
+static void get_sta_tid_stats(struct hostap_sta_driver_data *data,
+			      struct nlattr *attr)
+{
+	struct nlattr *tid_stats[NL80211_TID_STATS_MAX + 1], *tidattr;
+	struct nlattr *txq_stats[NL80211_TXQ_STATS_MAX + 1];
+	static struct nla_policy txq_stats_policy[NL80211_TXQ_STATS_MAX + 1] = {
+		[NL80211_TXQ_STATS_BACKLOG_BYTES] = { .type = NLA_U32 },
+		[NL80211_TXQ_STATS_BACKLOG_PACKETS] = { .type = NLA_U32 },
+	};
+	int rem;
+
+	nla_for_each_nested(tidattr, attr, rem) {
+		if (nla_parse_nested(tid_stats, NL80211_TID_STATS_MAX,
+				     tidattr, NULL) != 0 ||
+		    !tid_stats[NL80211_TID_STATS_TXQ_STATS] ||
+		    nla_parse_nested(txq_stats, NL80211_TXQ_STATS_MAX,
+				     tid_stats[NL80211_TID_STATS_TXQ_STATS],
+				     txq_stats_policy) != 0)
+			continue;
+		/* sum the backlogs over all TIDs for station */
+		if (txq_stats[NL80211_TXQ_STATS_BACKLOG_BYTES])
+			data->backlog_bytes += nla_get_u32(
+				txq_stats[NL80211_TXQ_STATS_BACKLOG_BYTES]);
+		if (txq_stats[NL80211_TXQ_STATS_BACKLOG_PACKETS])
+			data->backlog_bytes += nla_get_u32(
+				txq_stats[NL80211_TXQ_STATS_BACKLOG_PACKETS]);
+	}
+}
+
+
+static int get_sta_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct hostap_sta_driver_data *data = arg;
+	struct nlattr *stats[NL80211_STA_INFO_MAX + 1];
+	static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
+		[NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
+		[NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_TX_RETRIES] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_SIGNAL_AVG] = { .type = NLA_U8 },
+		[NL80211_STA_INFO_CONNECTED_TIME] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_BEACON_LOSS] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_RX_BYTES64] = { .type = NLA_U64 },
+		[NL80211_STA_INFO_TX_BYTES64] = { .type = NLA_U64 },
+		[NL80211_STA_INFO_EXPECTED_THROUGHPUT] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_RX_DROP_MISC] = { .type = NLA_U64 },
+		[NL80211_STA_INFO_BEACON_RX] = { .type = NLA_U64 },
+		[NL80211_STA_INFO_BEACON_SIGNAL_AVG] = { .type = NLA_U8},
+		[NL80211_STA_INFO_RX_DURATION] = { .type = NLA_U64 },
+		[NL80211_STA_INFO_ACK_SIGNAL] = { .type = NLA_U8 },
+		[NL80211_STA_INFO_ACK_SIGNAL_AVG] = { .type = NLA_S8 },
+		[NL80211_STA_INFO_RX_MPDUS] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_FCS_ERROR_COUNT] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_TX_DURATION] = { .type = NLA_U64 },
+	};
+	struct nlattr *rate[NL80211_RATE_INFO_MAX + 1];
+	static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
+		[NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
+		[NL80211_RATE_INFO_BITRATE32] = { .type = NLA_U32 },
+		[NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
+		[NL80211_RATE_INFO_VHT_MCS] = { .type = NLA_U8 },
+		[NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
+		[NL80211_RATE_INFO_VHT_NSS] = { .type = NLA_U8 },
+		[NL80211_RATE_INFO_HE_MCS] = { .type = NLA_U8 },
+		[NL80211_RATE_INFO_HE_NSS] = { .type = NLA_U8 },
+		[NL80211_RATE_INFO_HE_GI] = { .type = NLA_U8 },
+		[NL80211_RATE_INFO_HE_DCM] = { .type = NLA_U8 },
+	};
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	/*
+	 * TODO: validate the interface and mac address!
+	 * Otherwise, there's a race condition as soon as
+	 * the kernel starts sending station notifications.
+	 */
+
+	if (!tb[NL80211_ATTR_STA_INFO]) {
+		wpa_printf(MSG_DEBUG, "sta stats missing!");
+		return NL_SKIP;
+	}
+	if (nla_parse_nested(stats, NL80211_STA_INFO_MAX,
+			     tb[NL80211_ATTR_STA_INFO],
+			     stats_policy)) {
+		wpa_printf(MSG_DEBUG, "failed to parse nested attributes!");
+		return NL_SKIP;
+	}
+
+	if (stats[NL80211_STA_INFO_INACTIVE_TIME])
+		data->inactive_msec =
+			nla_get_u32(stats[NL80211_STA_INFO_INACTIVE_TIME]);
+	/* For backwards compatibility, fetch the 32-bit counters first. */
+	if (stats[NL80211_STA_INFO_RX_BYTES])
+		data->rx_bytes = nla_get_u32(stats[NL80211_STA_INFO_RX_BYTES]);
+	if (stats[NL80211_STA_INFO_TX_BYTES])
+		data->tx_bytes = nla_get_u32(stats[NL80211_STA_INFO_TX_BYTES]);
+	if (stats[NL80211_STA_INFO_RX_BYTES64] &&
+	    stats[NL80211_STA_INFO_TX_BYTES64]) {
+		/*
+		 * The driver supports 64-bit counters, so use them to override
+		 * the 32-bit values.
+		 */
+		data->rx_bytes =
+			nla_get_u64(stats[NL80211_STA_INFO_RX_BYTES64]);
+		data->tx_bytes =
+			nla_get_u64(stats[NL80211_STA_INFO_TX_BYTES64]);
+		data->bytes_64bit = 1;
+	}
+	if (stats[NL80211_STA_INFO_SIGNAL])
+		data->signal = (s8) nla_get_u8(stats[NL80211_STA_INFO_SIGNAL]);
+	if (stats[NL80211_STA_INFO_RX_PACKETS])
+		data->rx_packets =
+			nla_get_u32(stats[NL80211_STA_INFO_RX_PACKETS]);
+	if (stats[NL80211_STA_INFO_TX_PACKETS])
+		data->tx_packets =
+			nla_get_u32(stats[NL80211_STA_INFO_TX_PACKETS]);
+	if (stats[NL80211_STA_INFO_TX_RETRIES])
+		data->tx_retry_count =
+			nla_get_u32(stats[NL80211_STA_INFO_TX_RETRIES]);
+	if (stats[NL80211_STA_INFO_TX_FAILED])
+		data->tx_retry_failed =
+			nla_get_u32(stats[NL80211_STA_INFO_TX_FAILED]);
+	if (stats[NL80211_STA_INFO_SIGNAL_AVG])
+		data->avg_signal =
+			(s8) nla_get_u8(stats[NL80211_STA_INFO_SIGNAL_AVG]);
+	if (stats[NL80211_STA_INFO_CONNECTED_TIME]) {
+		data->connected_sec =
+			nla_get_u32(stats[NL80211_STA_INFO_CONNECTED_TIME]);
+		data->flags |= STA_DRV_DATA_CONN_TIME;
+	}
+	if (stats[NL80211_STA_INFO_BEACON_LOSS])
+		data->beacon_loss_count =
+			nla_get_u32(stats[NL80211_STA_INFO_BEACON_LOSS]);
+	if (stats[NL80211_STA_INFO_EXPECTED_THROUGHPUT])
+		data->expected_throughput =
+			nla_get_u32(stats[NL80211_STA_INFO_EXPECTED_THROUGHPUT]);
+	if (stats[NL80211_STA_INFO_RX_DROP_MISC])
+		data->rx_drop_misc =
+			nla_get_u64(stats[NL80211_STA_INFO_RX_DROP_MISC]);
+	if (stats[NL80211_STA_INFO_BEACON_RX])
+		data->beacons_count =
+			nla_get_u64(stats[NL80211_STA_INFO_BEACON_RX]);
+	if (stats[NL80211_STA_INFO_BEACON_SIGNAL_AVG])
+		data->avg_beacon_signal =
+			(s8) nla_get_u8(stats[NL80211_STA_INFO_BEACON_SIGNAL_AVG]);
+	if (stats[NL80211_STA_INFO_RX_DURATION])
+		data->rx_airtime =
+			nla_get_u64(stats[NL80211_STA_INFO_RX_DURATION]);
+	if (stats[NL80211_STA_INFO_ACK_SIGNAL]) {
+		data->last_ack_rssi =
+			nla_get_u8(stats[NL80211_STA_INFO_ACK_SIGNAL]);
+		data->flags |= STA_DRV_DATA_LAST_ACK_RSSI;
+	}
+	if (stats[NL80211_STA_INFO_ACK_SIGNAL_AVG])
+		data->avg_ack_signal =
+			nla_get_s8(stats[NL80211_STA_INFO_ACK_SIGNAL_AVG]);
+	if (stats[NL80211_STA_INFO_RX_MPDUS])
+		data->rx_mpdus = nla_get_u32(stats[NL80211_STA_INFO_RX_MPDUS]);
+	if (stats[NL80211_STA_INFO_FCS_ERROR_COUNT])
+		data->fcs_error_count =
+			nla_get_u32(stats[NL80211_STA_INFO_FCS_ERROR_COUNT]);
+	if (stats[NL80211_STA_INFO_TX_DURATION])
+		data->tx_airtime =
+			nla_get_u64(stats[NL80211_STA_INFO_TX_DURATION]);
+
+	if (stats[NL80211_STA_INFO_TX_BITRATE] &&
+	    nla_parse_nested(rate, NL80211_RATE_INFO_MAX,
+			     stats[NL80211_STA_INFO_TX_BITRATE],
+			     rate_policy) == 0) {
+		if (rate[NL80211_RATE_INFO_BITRATE32])
+			data->current_tx_rate =
+				nla_get_u32(rate[NL80211_RATE_INFO_BITRATE32]);
+		else if (rate[NL80211_RATE_INFO_BITRATE])
+			data->current_tx_rate =
+				nla_get_u16(rate[NL80211_RATE_INFO_BITRATE]);
+
+		/* Convert from 100 kbps to kbps; it's a more convenient unit.
+		 * It's also safe up until ~1Tbps. */
+		data->current_tx_rate = data->current_tx_rate * 100;
+
+		if (rate[NL80211_RATE_INFO_MCS]) {
+			data->tx_mcs = nla_get_u8(rate[NL80211_RATE_INFO_MCS]);
+			data->flags |= STA_DRV_DATA_TX_MCS;
+		}
+		if (rate[NL80211_RATE_INFO_VHT_MCS]) {
+			data->tx_vhtmcs =
+				nla_get_u8(rate[NL80211_RATE_INFO_VHT_MCS]);
+			data->flags |= STA_DRV_DATA_TX_VHT_MCS;
+		}
+		if (rate[NL80211_RATE_INFO_SHORT_GI]) {
+			data->tx_guard_interval = GUARD_INTERVAL_0_4;
+			data->flags |= STA_DRV_DATA_TX_SHORT_GI;
+		}
+		if (rate[NL80211_RATE_INFO_VHT_NSS]) {
+			data->tx_vht_nss =
+				nla_get_u8(rate[NL80211_RATE_INFO_VHT_NSS]);
+			data->flags |= STA_DRV_DATA_TX_VHT_NSS;
+		}
+		if (rate[NL80211_RATE_INFO_HE_MCS]) {
+			data->tx_hemcs =
+				nla_get_u8(rate[NL80211_RATE_INFO_HE_MCS]);
+			data->flags |= STA_DRV_DATA_TX_HE_MCS;
+		}
+		if (rate[NL80211_RATE_INFO_HE_NSS]) {
+			data->tx_he_nss =
+				nla_get_u8(rate[NL80211_RATE_INFO_HE_NSS]);
+			data->flags |= STA_DRV_DATA_TX_HE_NSS;
+		}
+		if (rate[NL80211_RATE_INFO_HE_GI]) {
+			switch (nla_get_u8(rate[NL80211_RATE_INFO_HE_GI])) {
+			case NL80211_RATE_INFO_HE_GI_0_8:
+				data->tx_guard_interval = GUARD_INTERVAL_0_8;
+				break;
+			case NL80211_RATE_INFO_HE_GI_1_6:
+				data->tx_guard_interval = GUARD_INTERVAL_1_6;
+				break;
+			case NL80211_RATE_INFO_HE_GI_3_2:
+				data->tx_guard_interval = GUARD_INTERVAL_3_2;
+				break;
+			}
+			data->flags |= STA_DRV_DATA_TX_HE_GI;
+		}
+		if (rate[NL80211_RATE_INFO_HE_DCM]) {
+			data->tx_dcm =
+				nla_get_u8(rate[NL80211_RATE_INFO_HE_DCM]);
+			data->flags |= STA_DRV_DATA_TX_HE_DCM;
+		}
+	}
+
+	if (stats[NL80211_STA_INFO_RX_BITRATE] &&
+	    nla_parse_nested(rate, NL80211_RATE_INFO_MAX,
+			     stats[NL80211_STA_INFO_RX_BITRATE],
+			     rate_policy) == 0) {
+		if (rate[NL80211_RATE_INFO_BITRATE32])
+			data->current_rx_rate =
+				nla_get_u32(rate[NL80211_RATE_INFO_BITRATE32]);
+		else if (rate[NL80211_RATE_INFO_BITRATE])
+			data->current_rx_rate =
+				nla_get_u16(rate[NL80211_RATE_INFO_BITRATE]);
+
+		/* Convert from 100 kbps to kbps; it's a more convenient unit.
+		 * It's also safe up until ~1Tbps. */
+		data->current_rx_rate = data->current_rx_rate * 100;
+
+		if (rate[NL80211_RATE_INFO_MCS]) {
+			data->rx_mcs = nla_get_u8(rate[NL80211_RATE_INFO_MCS]);
+			data->flags |= STA_DRV_DATA_RX_MCS;
+		}
+		if (rate[NL80211_RATE_INFO_VHT_MCS]) {
+			data->rx_vhtmcs =
+				nla_get_u8(rate[NL80211_RATE_INFO_VHT_MCS]);
+			data->flags |= STA_DRV_DATA_RX_VHT_MCS;
+		}
+		if (rate[NL80211_RATE_INFO_SHORT_GI]) {
+			data->rx_guard_interval = GUARD_INTERVAL_0_4;
+			data->flags |= STA_DRV_DATA_RX_SHORT_GI;
+		}
+		if (rate[NL80211_RATE_INFO_VHT_NSS]) {
+			data->rx_vht_nss =
+				nla_get_u8(rate[NL80211_RATE_INFO_VHT_NSS]);
+			data->flags |= STA_DRV_DATA_RX_VHT_NSS;
+		}
+		if (rate[NL80211_RATE_INFO_HE_MCS]) {
+			data->rx_hemcs =
+				nla_get_u8(rate[NL80211_RATE_INFO_HE_MCS]);
+			data->flags |= STA_DRV_DATA_RX_HE_MCS;
+		}
+		if (rate[NL80211_RATE_INFO_HE_NSS]) {
+			data->rx_he_nss =
+				nla_get_u8(rate[NL80211_RATE_INFO_HE_NSS]);
+			data->flags |= STA_DRV_DATA_RX_HE_NSS;
+		}
+		if (rate[NL80211_RATE_INFO_HE_GI]) {
+			switch (nla_get_u8(rate[NL80211_RATE_INFO_HE_GI])) {
+			case NL80211_RATE_INFO_HE_GI_0_8:
+				data->rx_guard_interval = GUARD_INTERVAL_0_8;
+				break;
+			case NL80211_RATE_INFO_HE_GI_1_6:
+				data->rx_guard_interval = GUARD_INTERVAL_1_6;
+				break;
+			case NL80211_RATE_INFO_HE_GI_3_2:
+				data->rx_guard_interval = GUARD_INTERVAL_3_2;
+				break;
+			}
+			data->flags |= STA_DRV_DATA_RX_HE_GI;
+		}
+		if (rate[NL80211_RATE_INFO_HE_DCM]) {
+			data->rx_dcm =
+				nla_get_u8(rate[NL80211_RATE_INFO_HE_DCM]);
+			data->flags |= STA_DRV_DATA_RX_HE_DCM;
+		}
+	}
+
+	if (stats[NL80211_STA_INFO_TID_STATS])
+		get_sta_tid_stats(data, stats[NL80211_STA_INFO_TID_STATS]);
+
+	return NL_SKIP;
+}
+
+
+int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
+			    const u8 *bssid,
+			    struct hostap_sta_driver_data *data)
+{
+	struct nl_msg *msg;
+
+	data->signal = -WPA_INVALID_NOISE;
+	data->current_tx_rate = 0;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_STATION)) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	return send_and_recv_msgs(drv, msg, get_sta_handler, data, NULL, NULL);
+}
+
+
+static int i802_read_sta_data(struct i802_bss *bss,
+			      struct hostap_sta_driver_data *data,
+			      const u8 *addr)
+{
+	struct nl_msg *msg;
+
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_STATION)) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	return send_and_recv_msgs(bss->drv, msg, get_sta_handler, data,
+				  NULL, NULL);
+}
+
+
+static int i802_set_tx_queue_params(void *priv, int queue, int aifs,
+				    int cw_min, int cw_max, int burst_time,
+				    int link_id)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *txq, *params;
+	int res;
+
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_WIPHY);
+	if (!msg)
+		return -1;
+
+	txq = nla_nest_start(msg, NL80211_ATTR_WIPHY_TXQ_PARAMS);
+	if (!txq)
+		goto fail;
+
+	/* We are only sending parameters for a single TXQ at a time */
+	params = nla_nest_start(msg, 1);
+	if (!params)
+		goto fail;
+
+	switch (queue) {
+	case 0:
+		if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VO))
+			goto fail;
+		break;
+	case 1:
+		if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VI))
+			goto fail;
+		break;
+	case 2:
+		if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BE))
+			goto fail;
+		break;
+	case 3:
+		if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BK))
+			goto fail;
+		break;
+	}
+	/* Burst time is configured in units of 0.1 msec and TXOP parameter in
+	 * 32 usec, so need to convert the value here. */
+	if (nla_put_u16(msg, NL80211_TXQ_ATTR_TXOP,
+			(burst_time * 100 + 16) / 32) ||
+	    nla_put_u16(msg, NL80211_TXQ_ATTR_CWMIN, cw_min) ||
+	    nla_put_u16(msg, NL80211_TXQ_ATTR_CWMAX, cw_max) ||
+	    nla_put_u8(msg, NL80211_TXQ_ATTR_AIFS, aifs))
+		goto fail;
+
+	nla_nest_end(msg, params);
+
+	nla_nest_end(msg, txq);
+
+	if (link_id != NL80211_DRV_LINK_ID_NA &&
+	    nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))
+		goto fail;
+
+	res = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: TX queue param set: queue=%d aifs=%d cw_min=%d cw_max=%d burst_time=%d --> res=%d",
+		   queue, aifs, cw_min, cw_max, burst_time, res);
+	if (res == 0)
+		return 0;
+	msg = NULL;
+fail:
+	nlmsg_free(msg);
+	return -1;
+}
+
+
+static int i802_set_sta_vlan(struct i802_bss *bss, const u8 *addr,
+			     const char *ifname, int vlan_id, int link_id)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "nl80211: %s[%d]: set_sta_vlan(" MACSTR
+		   ", ifname=%s[%d], vlan_id=%d)",
+		   bss->ifname, if_nametoindex(bss->ifname),
+		   MAC2STR(addr), ifname, if_nametoindex(ifname), vlan_id);
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+	    (vlan_id && (drv->capa.flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD) &&
+	     nla_put_u16(msg, NL80211_ATTR_VLAN_ID, vlan_id)) ||
+	    (link_id != NL80211_DRV_LINK_ID_NA &&
+	     nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) ||
+	    nla_put_u32(msg, NL80211_ATTR_STA_VLAN, if_nametoindex(ifname))) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "nl80211: NL80211_ATTR_STA_VLAN (addr="
+			   MACSTR " ifname=%s vlan_id=%d) failed: %d (%s)",
+			   MAC2STR(addr), ifname, vlan_id, ret,
+			   strerror(-ret));
+	}
+	return ret;
+}
+
+
+static int i802_get_inact_sec(void *priv, const u8 *addr)
+{
+	struct hostap_sta_driver_data data;
+	int ret;
+
+	os_memset(&data, 0, sizeof(data));
+	data.inactive_msec = (unsigned long) -1;
+	ret = i802_read_sta_data(priv, &data, addr);
+	if (ret == -ENOENT)
+		return -ENOENT;
+	if (ret || data.inactive_msec == (unsigned long) -1)
+		return -1;
+	return data.inactive_msec / 1000;
+}
+
+
+static int i802_sta_clear_stats(void *priv, const u8 *addr)
+{
+#if 0
+	/* TODO */
+#endif
+	return 0;
+}
+
+
+static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+			   u16 reason, int link_id)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct ieee80211_mgmt mgmt;
+	u8 channel;
+	struct i802_link *link = nl80211_get_link(bss, link_id);
+
+	if (ieee80211_freq_to_chan(link->freq, &channel) ==
+	    HOSTAPD_MODE_IEEE80211AD) {
+		/* Deauthentication is not used in DMG/IEEE 802.11ad;
+		 * disassociate the STA instead. */
+		return i802_sta_disassoc(priv, own_addr, addr, reason);
+	}
+
+	if (is_mesh_interface(drv->nlmode))
+		return -1;
+
+	if (drv->device_ap_sme)
+		return wpa_driver_nl80211_sta_remove(bss, addr, 1, reason);
+
+	memset(&mgmt, 0, sizeof(mgmt));
+	mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					  WLAN_FC_STYPE_DEAUTH);
+	memcpy(mgmt.da, addr, ETH_ALEN);
+	memcpy(mgmt.sa, own_addr, ETH_ALEN);
+	memcpy(mgmt.bssid, own_addr, ETH_ALEN);
+	mgmt.u.deauth.reason_code = host_to_le16(reason);
+	return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
+					    IEEE80211_HDRLEN +
+					    sizeof(mgmt.u.deauth), 0, 0, 0, 0,
+					    0, NULL, 0, 0, -1);
+}
+
+
+static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
+			     u16 reason)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct ieee80211_mgmt mgmt;
+
+	if (is_mesh_interface(drv->nlmode))
+		return -1;
+
+	if (drv->device_ap_sme)
+		return wpa_driver_nl80211_sta_remove(bss, addr, 0, reason);
+
+	memset(&mgmt, 0, sizeof(mgmt));
+	mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					  WLAN_FC_STYPE_DISASSOC);
+	memcpy(mgmt.da, addr, ETH_ALEN);
+	memcpy(mgmt.sa, own_addr, ETH_ALEN);
+	memcpy(mgmt.bssid, own_addr, ETH_ALEN);
+	mgmt.u.disassoc.reason_code = host_to_le16(reason);
+	return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
+					    IEEE80211_HDRLEN +
+					    sizeof(mgmt.u.disassoc), 0, 0, 0, 0,
+					    0, NULL, 0, 0, -1);
+}
+
+
+static void dump_ifidx(struct wpa_driver_nl80211_data *drv)
+{
+	char buf[200], *pos, *end;
+	int i, res;
+
+	pos = buf;
+	end = pos + sizeof(buf);
+
+	for (i = 0; i < drv->num_if_indices; i++) {
+		if (!drv->if_indices[i].ifindex)
+			continue;
+		res = os_snprintf(pos, end - pos, " %d(%d)",
+				  drv->if_indices[i].ifindex,
+				  drv->if_indices[i].reason);
+		if (os_snprintf_error(end - pos, res))
+			break;
+		pos += res;
+	}
+	*pos = '\0';
+
+	wpa_printf(MSG_DEBUG, "nl80211: if_indices[%d]:%s",
+		   drv->num_if_indices, buf);
+}
+
+
+static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
+		      int ifidx_reason)
+{
+	int i;
+	struct drv_nl80211_if_info *old;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Add own interface ifindex %d (ifidx_reason %d)",
+		   ifidx, ifidx_reason);
+	if (have_ifidx(drv, ifidx, ifidx_reason)) {
+		wpa_printf(MSG_DEBUG, "nl80211: ifindex %d already in the list",
+			   ifidx);
+		return;
+	}
+	for (i = 0; i < drv->num_if_indices; i++) {
+		if (drv->if_indices[i].ifindex == 0) {
+			drv->if_indices[i].ifindex = ifidx;
+			drv->if_indices[i].reason = ifidx_reason;
+			dump_ifidx(drv);
+			return;
+		}
+	}
+
+	if (drv->if_indices != drv->default_if_indices)
+		old = drv->if_indices;
+	else
+		old = NULL;
+
+	drv->if_indices = os_realloc_array(old, drv->num_if_indices + 1,
+					   sizeof(*old));
+	if (!drv->if_indices) {
+		if (!old)
+			drv->if_indices = drv->default_if_indices;
+		else
+			drv->if_indices = old;
+		wpa_printf(MSG_ERROR, "Failed to reallocate memory for "
+			   "interfaces");
+		wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx);
+		return;
+	}
+	if (!old)
+		os_memcpy(drv->if_indices, drv->default_if_indices,
+			  sizeof(drv->default_if_indices));
+	drv->if_indices[drv->num_if_indices].ifindex = ifidx;
+	drv->if_indices[drv->num_if_indices].reason = ifidx_reason;
+	drv->num_if_indices++;
+	dump_ifidx(drv);
+}
+
+
+static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
+		      int ifidx_reason)
+{
+	int i;
+
+	for (i = 0; i < drv->num_if_indices; i++) {
+		if ((drv->if_indices[i].ifindex == ifidx ||
+		     ifidx == IFIDX_ANY) &&
+		    (drv->if_indices[i].reason == ifidx_reason ||
+		     ifidx_reason == IFIDX_ANY)) {
+			drv->if_indices[i].ifindex = 0;
+			drv->if_indices[i].reason = 0;
+			break;
+		}
+	}
+	dump_ifidx(drv);
+}
+
+
+static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
+		      int ifidx_reason)
+{
+	int i;
+
+	for (i = 0; i < drv->num_if_indices; i++)
+		if (drv->if_indices[i].ifindex == ifidx &&
+		    (drv->if_indices[i].reason == ifidx_reason ||
+		     ifidx_reason == IFIDX_ANY))
+			return 1;
+
+	return 0;
+}
+
+
+static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val,
+			    const char *bridge_ifname, char *ifname_wds)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	char name[IFNAMSIZ + 1];
+	union wpa_event_data event;
+	int ret;
+
+	ret = os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid);
+	if (ret >= (int) sizeof(name))
+		wpa_printf(MSG_WARNING,
+			   "nl80211: WDS interface name was truncated");
+	else if (ret < 0)
+		return ret;
+
+	if (ifname_wds)
+		os_strlcpy(ifname_wds, name, IFNAMSIZ + 1);
+
+	wpa_printf(MSG_DEBUG, "nl80211: Set WDS STA addr=" MACSTR
+		   " aid=%d val=%d name=%s", MAC2STR(addr), aid, val, name);
+	if (val) {
+		if (!if_nametoindex(name)) {
+			if (nl80211_create_iface(drv, name,
+						 NL80211_IFTYPE_AP_VLAN,
+						 bss->addr, 1, NULL, NULL, 0) <
+			    0)
+				return -1;
+			if (bridge_ifname &&
+			    linux_br_add_if(drv->global->ioctl_sock,
+					    bridge_ifname, name) < 0)
+				return -1;
+
+			os_memset(&event, 0, sizeof(event));
+			event.wds_sta_interface.sta_addr = addr;
+			event.wds_sta_interface.ifname = name;
+			event.wds_sta_interface.istatus = INTERFACE_ADDED;
+			wpa_supplicant_event(bss->ctx,
+					     EVENT_WDS_STA_INTERFACE_STATUS,
+					     &event);
+		}
+		if (linux_set_iface_flags(drv->global->ioctl_sock, name, 1)) {
+			wpa_printf(MSG_ERROR, "nl80211: Failed to set WDS STA "
+				   "interface %s up", name);
+		}
+		return i802_set_sta_vlan(priv, addr, name, 0,
+					 NL80211_DRV_LINK_ID_NA);
+	} else {
+		if (bridge_ifname &&
+		    linux_br_del_if(drv->global->ioctl_sock, bridge_ifname,
+				    name) < 0)
+			wpa_printf(MSG_INFO,
+				   "nl80211: Failed to remove interface %s from bridge %s: %s",
+				   name, bridge_ifname, strerror(errno));
+
+		i802_set_sta_vlan(priv, addr, bss->ifname, 0,
+				  NL80211_DRV_LINK_ID_NA);
+		nl80211_remove_iface(drv, if_nametoindex(name));
+		os_memset(&event, 0, sizeof(event));
+		event.wds_sta_interface.sta_addr = addr;
+		event.wds_sta_interface.ifname = name;
+		event.wds_sta_interface.istatus = INTERFACE_REMOVED;
+		wpa_supplicant_event(bss->ctx, EVENT_WDS_STA_INTERFACE_STATUS,
+				     &event);
+		return 0;
+	}
+}
+
+
+static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct wpa_driver_nl80211_data *drv = eloop_ctx;
+	struct sockaddr_ll lladdr;
+	unsigned char buf[3000];
+	int len;
+	socklen_t fromlen = sizeof(lladdr);
+
+	len = recvfrom(sock, buf, sizeof(buf), 0,
+		       (struct sockaddr *)&lladdr, &fromlen);
+	if (len < 0) {
+		wpa_printf(MSG_ERROR, "nl80211: EAPOL recv failed: %s",
+			   strerror(errno));
+		return;
+	}
+
+	if (have_ifidx(drv, lladdr.sll_ifindex, IFIDX_ANY))
+		drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len);
+}
+
+
+static int i802_check_bridge(struct wpa_driver_nl80211_data *drv,
+			     struct i802_bss *bss,
+			     const char *brname, const char *ifname)
+{
+	int br_ifindex;
+	char in_br[IFNAMSIZ];
+
+	os_strlcpy(bss->brname, brname, IFNAMSIZ);
+	br_ifindex = if_nametoindex(brname);
+	if (br_ifindex == 0) {
+		/*
+		 * Bridge was configured, but the bridge device does
+		 * not exist. Try to add it now.
+		 */
+		if (linux_br_add(drv->global->ioctl_sock, brname) < 0) {
+			wpa_printf(MSG_ERROR, "nl80211: Failed to add the "
+				   "bridge interface %s: %s",
+				   brname, strerror(errno));
+			return -1;
+		}
+		bss->added_bridge = 1;
+		br_ifindex = if_nametoindex(brname);
+		add_ifidx(drv, br_ifindex, drv->ifindex);
+	}
+	bss->br_ifindex = br_ifindex;
+
+	if (linux_br_get(in_br, ifname) == 0) {
+		if (os_strcmp(in_br, brname) == 0) {
+			bss->already_in_bridge = 1;
+			return 0; /* already in the bridge */
+		}
+
+		wpa_printf(MSG_DEBUG, "nl80211: Removing interface %s from "
+			   "bridge %s", ifname, in_br);
+		if (linux_br_del_if(drv->global->ioctl_sock, in_br, ifname) <
+		    0) {
+			wpa_printf(MSG_ERROR, "nl80211: Failed to "
+				   "remove interface %s from bridge "
+				   "%s: %s",
+				   ifname, in_br, strerror(errno));
+			return -1;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: Adding interface %s into bridge %s",
+		   ifname, brname);
+	if (linux_br_add_if(drv->global->ioctl_sock, brname, ifname) < 0) {
+		wpa_printf(MSG_WARNING,
+			   "nl80211: Failed to add interface %s into bridge %s: %s",
+			   ifname, brname, strerror(errno));
+		/* Try to continue without the interface being in a bridge. This
+		 * may be needed for some cases, e.g., with Open vSwitch, where
+		 * an external component will need to handle bridge
+		 * configuration. */
+		return 0;
+	}
+	bss->added_if_into_bridge = 1;
+
+	return 0;
+}
+
+
+static void *i802_init(struct hostapd_data *hapd,
+		       struct wpa_init_params *params)
+{
+	struct wpa_driver_nl80211_data *drv;
+	struct i802_bss *bss;
+	size_t i;
+	char master_ifname[IFNAMSIZ];
+	int ifindex, br_ifindex = 0;
+	int br_added = 0;
+
+	bss = wpa_driver_nl80211_drv_init(hapd, params->ifname,
+					  params->global_priv, 1,
+					  params->bssid, params->driver_params);
+	if (bss == NULL)
+		return NULL;
+
+	drv = bss->drv;
+
+	if (linux_br_get(master_ifname, params->ifname) == 0) {
+		wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s",
+			   params->ifname, master_ifname);
+		br_ifindex = if_nametoindex(master_ifname);
+		os_strlcpy(bss->brname, master_ifname, IFNAMSIZ);
+	} else if ((params->num_bridge == 0 || !params->bridge[0]) &&
+		   linux_master_get(master_ifname, params->ifname) == 0) {
+		wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in master %s",
+			params->ifname, master_ifname);
+		/* start listening for EAPOL on the master interface */
+		add_ifidx(drv, if_nametoindex(master_ifname), drv->ifindex);
+
+		/* check if master itself is under bridge */
+		if (linux_br_get(master_ifname, master_ifname) == 0) {
+			wpa_printf(MSG_DEBUG, "nl80211: which is in bridge %s",
+				   master_ifname);
+			br_ifindex = if_nametoindex(master_ifname);
+			os_strlcpy(bss->brname, master_ifname, IFNAMSIZ);
+		}
+	} else {
+		master_ifname[0] = '\0';
+	}
+
+	bss->br_ifindex = br_ifindex;
+
+	for (i = 0; i < params->num_bridge; i++) {
+		if (params->bridge[i]) {
+			ifindex = if_nametoindex(params->bridge[i]);
+			if (ifindex)
+				add_ifidx(drv, ifindex, drv->ifindex);
+			if (ifindex == br_ifindex)
+				br_added = 1;
+		}
+	}
+
+	/* start listening for EAPOL on the default AP interface */
+	add_ifidx(drv, drv->ifindex, IFIDX_ANY);
+
+	if (params->num_bridge && params->bridge[0]) {
+		if (i802_check_bridge(drv, bss, params->bridge[0],
+				      params->ifname) < 0)
+			goto failed;
+		if (os_strcmp(params->bridge[0], master_ifname) != 0)
+			br_added = 1;
+	}
+
+	if (!br_added && br_ifindex &&
+	    (params->num_bridge == 0 || !params->bridge[0]))
+		add_ifidx(drv, br_ifindex, drv->ifindex);
+
+	if (bss->added_if_into_bridge || bss->already_in_bridge) {
+		int err;
+
+		drv->rtnl_sk = nl_socket_alloc();
+		if (drv->rtnl_sk == NULL) {
+			wpa_printf(MSG_ERROR, "nl80211: Failed to allocate nl_sock");
+			goto failed;
+		}
+
+		err = nl_connect(drv->rtnl_sk, NETLINK_ROUTE);
+		if (err) {
+			wpa_printf(MSG_ERROR, "nl80211: Failed to connect nl_sock to NETLINK_ROUTE: %s",
+				   nl_geterror(err));
+			goto failed;
+		}
+	}
+
+	if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Do not open EAPOL RX socket - using control port for RX");
+		goto skip_eapol_sock;
+	}
+
+	drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE));
+	if (drv->eapol_sock < 0) {
+		wpa_printf(MSG_ERROR, "nl80211: socket(PF_PACKET, SOCK_DGRAM, ETH_P_PAE) failed: %s",
+			   strerror(errno));
+		goto failed;
+	}
+
+	if (eloop_register_read_sock(drv->eapol_sock, handle_eapol, drv, NULL))
+	{
+		wpa_printf(MSG_INFO, "nl80211: Could not register read socket for eapol");
+		goto failed;
+	}
+skip_eapol_sock:
+
+	if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+			       params->own_addr))
+		goto failed;
+	os_memcpy(drv->perm_addr, params->own_addr, ETH_ALEN);
+
+	memcpy(bss->addr, params->own_addr, ETH_ALEN);
+
+	return bss;
+
+failed:
+	wpa_driver_nl80211_deinit(bss);
+	return NULL;
+}
+
+
+static void i802_deinit(void *priv)
+{
+	struct i802_bss *bss = priv;
+	wpa_driver_nl80211_deinit(bss);
+}
+
+
+static enum nl80211_iftype wpa_driver_nl80211_if_type(
+	enum wpa_driver_if_type type)
+{
+	switch (type) {
+	case WPA_IF_STATION:
+		return NL80211_IFTYPE_STATION;
+	case WPA_IF_P2P_CLIENT:
+	case WPA_IF_P2P_GROUP:
+		return NL80211_IFTYPE_P2P_CLIENT;
+	case WPA_IF_AP_VLAN:
+		return NL80211_IFTYPE_AP_VLAN;
+	case WPA_IF_AP_BSS:
+		return NL80211_IFTYPE_AP;
+	case WPA_IF_P2P_GO:
+		return NL80211_IFTYPE_P2P_GO;
+	case WPA_IF_P2P_DEVICE:
+		return NL80211_IFTYPE_P2P_DEVICE;
+	case WPA_IF_MESH:
+		return NL80211_IFTYPE_MESH_POINT;
+	default:
+		return -1;
+	}
+}
+
+
+static int nl80211_addr_in_use(struct nl80211_global *global, const u8 *addr)
+{
+	struct wpa_driver_nl80211_data *drv;
+	dl_list_for_each(drv, &global->interfaces,
+			 struct wpa_driver_nl80211_data, list) {
+		if (os_memcmp(addr, drv->first_bss->addr, ETH_ALEN) == 0)
+			return 1;
+	}
+	return 0;
+}
+
+
+static int nl80211_vif_addr(struct wpa_driver_nl80211_data *drv, u8 *new_addr)
+{
+	unsigned int idx;
+
+	if (!drv->global)
+		return -1;
+
+	os_memcpy(new_addr, drv->first_bss->addr, ETH_ALEN);
+	for (idx = 0; idx < 64; idx++) {
+		new_addr[0] = drv->first_bss->addr[0] | 0x02;
+		new_addr[0] ^= idx << 2;
+		if (!nl80211_addr_in_use(drv->global, new_addr))
+			break;
+	}
+	if (idx == 64)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Assigned new virtual interface address "
+		   MACSTR, MAC2STR(new_addr));
+
+	return 0;
+}
+
+
+struct wdev_info {
+	u64 wdev_id;
+	int wdev_id_set;
+	u8 macaddr[ETH_ALEN];
+};
+
+static int nl80211_wdev_handler(struct nl_msg *msg, void *arg)
+{
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct wdev_info *wi = arg;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+	if (tb[NL80211_ATTR_WDEV]) {
+		wi->wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]);
+		wi->wdev_id_set = 1;
+	}
+
+	if (tb[NL80211_ATTR_MAC])
+		os_memcpy(wi->macaddr, nla_data(tb[NL80211_ATTR_MAC]),
+			  ETH_ALEN);
+
+	return NL_SKIP;
+}
+
+
+static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
+				     const char *ifname, const u8 *addr,
+				     void *bss_ctx, void **drv_priv,
+				     char *force_ifname, u8 *if_addr,
+				     const char *bridge, int use_existing,
+				     int setup_ap)
+{
+	enum nl80211_iftype nlmode;
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int ifidx;
+	int added = 1;
+
+	if (addr)
+		os_memcpy(if_addr, addr, ETH_ALEN);
+	nlmode = wpa_driver_nl80211_if_type(type);
+	if (nlmode == NL80211_IFTYPE_P2P_DEVICE) {
+		struct wdev_info p2pdev_info;
+
+		os_memset(&p2pdev_info, 0, sizeof(p2pdev_info));
+		ifidx = nl80211_create_iface(drv, ifname, nlmode, addr,
+					     0, nl80211_wdev_handler,
+					     &p2pdev_info, use_existing);
+		if (!p2pdev_info.wdev_id_set || ifidx != 0) {
+			wpa_printf(MSG_ERROR, "nl80211: Failed to create a P2P Device interface %s",
+				   ifname);
+			return -1;
+		}
+
+		drv->global->if_add_wdevid = p2pdev_info.wdev_id;
+		drv->global->if_add_wdevid_set = p2pdev_info.wdev_id_set;
+		if (!is_zero_ether_addr(p2pdev_info.macaddr))
+			os_memcpy(if_addr, p2pdev_info.macaddr, ETH_ALEN);
+		wpa_printf(MSG_DEBUG, "nl80211: New P2P Device interface %s (0x%llx) created",
+			   ifname,
+			   (long long unsigned int) p2pdev_info.wdev_id);
+	} else {
+		ifidx = nl80211_create_iface(drv, ifname, nlmode, addr,
+					     0, NULL, NULL, use_existing);
+		if (use_existing && ifidx == -ENFILE) {
+			added = 0;
+			ifidx = if_nametoindex(ifname);
+		} else if (ifidx < 0) {
+			return -1;
+		}
+	}
+
+	if (!addr) {
+		if (nlmode == NL80211_IFTYPE_P2P_DEVICE)
+			os_memcpy(if_addr, bss->addr, ETH_ALEN);
+		else if (linux_get_ifhwaddr(drv->global->ioctl_sock,
+					    ifname, if_addr) < 0) {
+			if (added)
+				nl80211_remove_iface(drv, ifidx);
+			return -1;
+		}
+	}
+
+	if (!addr &&
+	    (type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP ||
+	     type == WPA_IF_P2P_GO || type == WPA_IF_MESH ||
+	     type == WPA_IF_STATION || type == WPA_IF_AP_BSS)) {
+		/* Enforce unique address */
+		u8 new_addr[ETH_ALEN];
+
+		if (linux_get_ifhwaddr(drv->global->ioctl_sock, ifname,
+				       new_addr) < 0) {
+			if (added)
+				nl80211_remove_iface(drv, ifidx);
+			return -1;
+		}
+		if (nl80211_addr_in_use(drv->global, new_addr)) {
+			wpa_printf(MSG_DEBUG, "nl80211: Allocate new address "
+				   "for interface %s type %d", ifname, type);
+			if (nl80211_vif_addr(drv, new_addr) < 0) {
+				if (added)
+					nl80211_remove_iface(drv, ifidx);
+				return -1;
+			}
+			if (linux_set_ifhwaddr(drv->global->ioctl_sock, ifname,
+					       new_addr) < 0) {
+				if (added)
+					nl80211_remove_iface(drv, ifidx);
+				return -1;
+			}
+		}
+		os_memcpy(if_addr, new_addr, ETH_ALEN);
+	}
+
+	if (type == WPA_IF_AP_BSS && setup_ap) {
+		struct i802_bss *new_bss = os_zalloc(sizeof(*new_bss));
+		unsigned int i;
+
+		if (new_bss == NULL) {
+			if (added)
+				nl80211_remove_iface(drv, ifidx);
+			return -1;
+		}
+
+		/* Initialize here before any failure path */
+		for (i = 0; i < MAX_NUM_MLD_LINKS; i++)
+			new_bss->links[i].link_id = NL80211_DRV_LINK_ID_NA;
+
+		if (bridge &&
+		    i802_check_bridge(drv, new_bss, bridge, ifname) < 0) {
+			wpa_printf(MSG_ERROR, "nl80211: Failed to add the new "
+				   "interface %s to a bridge %s",
+				   ifname, bridge);
+			if (added)
+				nl80211_remove_iface(drv, ifidx);
+			os_free(new_bss);
+			return -1;
+		}
+
+		if (linux_set_iface_flags(drv->global->ioctl_sock, ifname, 1))
+		{
+			if (added)
+				nl80211_remove_iface(drv, ifidx);
+			os_free(new_bss);
+			return -1;
+		}
+		os_strlcpy(new_bss->ifname, ifname, IFNAMSIZ);
+		os_memcpy(new_bss->addr, if_addr, ETH_ALEN);
+		new_bss->ifindex = ifidx;
+		new_bss->drv = drv;
+		new_bss->next = drv->first_bss->next;
+		new_bss->flink = &new_bss->links[0];
+		new_bss->n_links = 1;
+		os_memcpy(new_bss->flink->addr, new_bss->addr, ETH_ALEN);
+
+		new_bss->flink->freq = drv->first_bss->flink->freq;
+		new_bss->ctx = bss_ctx;
+		new_bss->added_if = added;
+		drv->first_bss->next = new_bss;
+		if (drv_priv)
+			*drv_priv = new_bss;
+		nl80211_init_bss(new_bss);
+
+		/* Subscribe management frames for this WPA_IF_AP_BSS */
+		if (nl80211_setup_ap(new_bss))
+			return -1;
+	}
+
+	if (drv->global)
+		drv->global->if_add_ifindex = ifidx;
+
+	/*
+	 * Some virtual interfaces need to process EAPOL packets and events on
+	 * the parent interface. This is used mainly with hostapd.
+	 */
+	if (ifidx > 0 &&
+	    (drv->hostapd ||
+	     nlmode == NL80211_IFTYPE_AP_VLAN ||
+	     nlmode == NL80211_IFTYPE_WDS ||
+	     nlmode == NL80211_IFTYPE_MONITOR))
+		add_ifidx(drv, ifidx, IFIDX_ANY);
+
+	return 0;
+}
+
+
+static int wpa_driver_nl80211_if_remove(struct i802_bss *bss,
+					enum wpa_driver_if_type type,
+					const char *ifname)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int ifindex = if_nametoindex(ifname);
+
+	wpa_printf(MSG_DEBUG, "nl80211: %s(type=%d ifname=%s) ifindex=%d added_if=%d",
+		   __func__, type, ifname, ifindex, bss->added_if);
+	if (ifindex > 0 && (bss->added_if || bss->ifindex != ifindex))
+		nl80211_remove_iface(drv, ifindex);
+	else if (ifindex > 0 && !bss->added_if) {
+		struct wpa_driver_nl80211_data *drv2;
+		dl_list_for_each(drv2, &drv->global->interfaces,
+				 struct wpa_driver_nl80211_data, list) {
+			del_ifidx(drv2, ifindex, IFIDX_ANY);
+			del_ifidx(drv2, IFIDX_ANY, ifindex);
+		}
+	}
+
+	if (type != WPA_IF_AP_BSS)
+		return 0;
+
+	if (bss->added_if_into_bridge) {
+		if (linux_br_del_if(drv->global->ioctl_sock, bss->brname,
+				    bss->ifname) < 0)
+			wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+				   "interface %s from bridge %s: %s",
+				   bss->ifname, bss->brname, strerror(errno));
+	}
+	if (bss->added_bridge) {
+		if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0)
+			wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+				   "bridge %s: %s",
+				   bss->brname, strerror(errno));
+	}
+
+	if (bss != drv->first_bss) {
+		struct i802_bss *tbss;
+
+		wpa_printf(MSG_DEBUG, "nl80211: Not the first BSS - remove it");
+		for (tbss = drv->first_bss; tbss; tbss = tbss->next) {
+			if (tbss->next == bss) {
+				tbss->next = bss->next;
+				/* Unsubscribe management frames */
+				nl80211_teardown_ap(bss);
+				nl80211_destroy_bss(bss);
+				if (!bss->added_if)
+					i802_set_iface_flags(bss, 0);
+				os_free(bss);
+				bss = NULL;
+				break;
+			}
+		}
+		if (bss)
+			wpa_printf(MSG_INFO, "nl80211: %s - could not find "
+				   "BSS %p in the list", __func__, bss);
+	} else {
+		wpa_printf(MSG_DEBUG, "nl80211: First BSS - reassign context");
+		nl80211_teardown_ap(bss);
+		nl80211_destroy_bss(bss);
+		if (!bss->added_if)
+			i802_set_iface_flags(bss, 0);
+		if (drv->first_bss->next) {
+			drv->first_bss = drv->first_bss->next;
+			drv->ctx = drv->first_bss->ctx;
+			os_free(bss);
+		} else {
+			wpa_printf(MSG_DEBUG, "nl80211: No second BSS to reassign context to");
+		}
+	}
+
+	return 0;
+}
+
+
+static int cookie_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	u64 *cookie = arg;
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+	if (tb[NL80211_ATTR_COOKIE])
+		*cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]);
+	return NL_SKIP;
+}
+
+
+static int nl80211_send_frame_cmd(struct i802_bss *bss,
+				  unsigned int freq, unsigned int wait,
+				  const u8 *buf, size_t buf_len,
+				  int save_cookie, int no_cck, int no_ack,
+				  int offchanok, const u16 *csa_offs,
+				  size_t csa_offs_len)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	u64 cookie;
+	int ret = -1;
+
+	wpa_printf(MSG_MSGDUMP, "nl80211: CMD_FRAME freq=%u wait=%u no_cck=%d "
+		   "no_ack=%d offchanok=%d",
+		   freq, wait, no_cck, no_ack, offchanok);
+	wpa_hexdump(MSG_MSGDUMP, "CMD_FRAME", buf, buf_len);
+
+	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_FRAME)) ||
+	    (freq && nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) ||
+	    (wait && nla_put_u32(msg, NL80211_ATTR_DURATION, wait)) ||
+	    (offchanok && ((drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) ||
+			   drv->test_use_roc_tx) &&
+	     nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK)) ||
+	    (no_cck && nla_put_flag(msg, NL80211_ATTR_TX_NO_CCK_RATE)) ||
+	    (no_ack && nla_put_flag(msg, NL80211_ATTR_DONT_WAIT_FOR_ACK)) ||
+	    (csa_offs && nla_put(msg, NL80211_ATTR_CSA_C_OFFSETS_TX,
+				 csa_offs_len * sizeof(u16), csa_offs)) ||
+	    nla_put(msg, NL80211_ATTR_FRAME, buf_len, buf))
+		goto fail;
+
+	cookie = 0;
+	ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Frame command failed: ret=%d "
+			   "(%s) (freq=%u wait=%u)", ret, strerror(-ret),
+			   freq, wait);
+	} else {
+		wpa_printf(MSG_MSGDUMP, "nl80211: Frame TX command accepted%s; "
+			   "cookie 0x%llx", no_ack ? " (no ACK)" : "",
+			   (long long unsigned int) cookie);
+
+		if (save_cookie)
+			drv->send_frame_cookie = no_ack ? (u64) -1 : cookie;
+
+		if (!wait) {
+			 /* There is no need to store this cookie since there
+			  * is no wait that could be canceled later.  */
+			goto fail;
+		}
+		if (drv->num_send_frame_cookies == MAX_SEND_FRAME_COOKIES) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Drop oldest pending send frame cookie 0x%llx",
+				   (long long unsigned int)
+				   drv->send_frame_cookies[0]);
+			os_memmove(&drv->send_frame_cookies[0],
+				   &drv->send_frame_cookies[1],
+				   (MAX_SEND_FRAME_COOKIES - 1) *
+				   sizeof(u64));
+			drv->num_send_frame_cookies--;
+		}
+		drv->send_frame_cookies[drv->num_send_frame_cookies] = cookie;
+		drv->num_send_frame_cookies++;
+	}
+
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+static int wpa_driver_nl80211_send_action(struct i802_bss *bss,
+					  unsigned int freq,
+					  unsigned int wait_time,
+					  const u8 *dst, const u8 *src,
+					  const u8 *bssid,
+					  const u8 *data, size_t data_len,
+					  int no_cck)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int ret = -1;
+	u8 *buf;
+	struct ieee80211_hdr *hdr;
+	int offchanok = 1;
+
+	if (is_ap_interface(drv->nlmode) && (int) freq == bss->flink->freq &&
+	    bss->flink->beacon_set)
+		offchanok = 0;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d, "
+		   "freq=%u MHz wait=%d ms no_cck=%d offchanok=%d)",
+		   drv->ifindex, freq, wait_time, no_cck, offchanok);
+
+	buf = os_zalloc(24 + data_len);
+	if (buf == NULL)
+		return ret;
+	os_memcpy(buf + 24, data, data_len);
+	hdr = (struct ieee80211_hdr *) buf;
+	hdr->frame_control =
+		IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION);
+	os_memcpy(hdr->addr1, dst, ETH_ALEN);
+	os_memcpy(hdr->addr2, src, ETH_ALEN);
+	os_memcpy(hdr->addr3, bssid, ETH_ALEN);
+
+	if (os_memcmp(bss->addr, src, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "nl80211: Use random TA " MACSTR,
+			   MAC2STR(src));
+		os_memcpy(bss->rand_addr, src, ETH_ALEN);
+	} else {
+		os_memset(bss->rand_addr, 0, ETH_ALEN);
+	}
+
+#ifdef CONFIG_MESH
+	if (is_mesh_interface(drv->nlmode)) {
+		struct hostapd_hw_modes *modes;
+		u16 num_modes, flags;
+		u8 dfs_domain;
+		int i;
+
+		modes = nl80211_get_hw_feature_data(bss, &num_modes,
+						    &flags, &dfs_domain);
+		if (dfs_domain != HOSTAPD_DFS_REGION_ETSI &&
+		    ieee80211_is_dfs(bss->flink->freq, modes, num_modes))
+			offchanok = 0;
+		if (modes) {
+			for (i = 0; i < num_modes; i++) {
+				os_free(modes[i].channels);
+				os_free(modes[i].rates);
+			}
+			os_free(modes);
+		}
+	}
+#endif /* CONFIG_MESH */
+
+	if (is_ap_interface(drv->nlmode) &&
+	    (!(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) ||
+	     (int) freq == bss->flink->freq || drv->device_ap_sme ||
+	     !drv->use_monitor))
+		ret = wpa_driver_nl80211_send_mlme(bss, buf, 24 + data_len,
+						   0, freq, no_cck, offchanok,
+						   wait_time, NULL, 0, 0, -1);
+	else
+		ret = nl80211_send_frame_cmd(bss, freq, wait_time, buf,
+					     24 + data_len,
+					     1, no_cck, 0, offchanok, NULL, 0);
+
+	os_free(buf);
+	return ret;
+}
+
+
+static void nl80211_frame_wait_cancel(struct i802_bss *bss, u64 cookie)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Cancel TX frame wait: cookie=0x%llx",
+		   (long long unsigned int) cookie);
+	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_FRAME_WAIT_CANCEL)) ||
+	    nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie)) {
+		nlmsg_free(msg);
+		return;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret)
+		wpa_printf(MSG_DEBUG, "nl80211: wait cancel failed: ret=%d "
+			   "(%s)", ret, strerror(-ret));
+}
+
+
+static void wpa_driver_nl80211_send_action_cancel_wait(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	unsigned int i;
+	u64 cookie;
+
+	/* Cancel the last pending TX cookie */
+	if (drv->send_frame_cookie != (u64) -1)
+		nl80211_frame_wait_cancel(bss, drv->send_frame_cookie);
+
+	/*
+	 * Cancel the other pending TX cookies, if any. This is needed since
+	 * the driver may keep a list of all pending offchannel TX operations
+	 * and free up the radio only once they have expired or cancelled.
+	 */
+	for (i = drv->num_send_frame_cookies; i > 0; i--) {
+		cookie = drv->send_frame_cookies[i - 1];
+		if (cookie != drv->send_frame_cookie)
+			nl80211_frame_wait_cancel(bss, cookie);
+	}
+	drv->num_send_frame_cookies = 0;
+}
+
+
+static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq,
+						unsigned int duration)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+	u64 cookie;
+
+	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_REMAIN_ON_CHANNEL)) ||
+	    nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) ||
+	    nla_put_u32(msg, NL80211_ATTR_DURATION, duration)) {
+		nlmsg_free(msg);
+		return -1;
+	}
+
+	cookie = 0;
+	ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie, NULL, NULL);
+	if (ret == 0) {
+		wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel cookie "
+			   "0x%llx for freq=%u MHz duration=%u",
+			   (long long unsigned int) cookie, freq, duration);
+		drv->remain_on_chan_cookie = cookie;
+		drv->pending_remain_on_chan = 1;
+		return 0;
+	}
+	wpa_printf(MSG_DEBUG, "nl80211: Failed to request remain-on-channel "
+		   "(freq=%d duration=%u): %d (%s)",
+		   freq, duration, ret, strerror(-ret));
+	return -1;
+}
+
+
+static int wpa_driver_nl80211_cancel_remain_on_channel(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	if (!drv->pending_remain_on_chan) {
+		wpa_printf(MSG_DEBUG, "nl80211: No pending remain-on-channel "
+			   "to cancel");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: Cancel remain-on-channel with cookie "
+		   "0x%llx",
+		   (long long unsigned int) drv->remain_on_chan_cookie);
+
+	msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL);
+	if (!msg ||
+	    nla_put_u64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie)) {
+		nlmsg_free(msg);
+		return -1;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret == 0)
+		return 0;
+	wpa_printf(MSG_DEBUG, "nl80211: Failed to cancel remain-on-channel: "
+		   "%d (%s)", ret, strerror(-ret));
+	return -1;
+}
+
+
+static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss, int report)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	if (!report) {
+		if (bss->nl_preq && drv->device_ap_sme &&
+		    is_ap_interface(drv->nlmode) && !bss->in_deinit &&
+		    !bss->static_ap) {
+			/*
+			 * Do not disable Probe Request reporting that was
+			 * enabled in nl80211_setup_ap().
+			 */
+			wpa_printf(MSG_DEBUG, "nl80211: Skip disabling of "
+				   "Probe Request reporting nl_preq=%p while "
+				   "in AP mode", bss->nl_preq);
+		} else if (bss->nl_preq) {
+			wpa_printf(MSG_DEBUG, "nl80211: Disable Probe Request "
+				   "reporting nl_preq=%p", bss->nl_preq);
+			nl80211_destroy_eloop_handle(&bss->nl_preq, 0);
+		}
+		return 0;
+	}
+
+	if (bss->nl_preq) {
+		wpa_printf(MSG_DEBUG, "nl80211: Probe Request reporting "
+			   "already on! nl_preq=%p", bss->nl_preq);
+		return 0;
+	}
+
+	bss->nl_preq = nl_create_handle(drv->global->nl_cb, "preq");
+	if (bss->nl_preq == NULL)
+		return -1;
+	wpa_printf(MSG_DEBUG, "nl80211: Enable Probe Request "
+		   "reporting nl_preq=%p", bss->nl_preq);
+
+	if (nl80211_register_frame(bss, bss->nl_preq,
+				   (WLAN_FC_TYPE_MGMT << 2) |
+				   (WLAN_FC_STYPE_PROBE_REQ << 4),
+				   NULL, 0, false) < 0)
+		goto out_err;
+
+	nl80211_register_eloop_read(&bss->nl_preq,
+				    wpa_driver_nl80211_event_receive,
+				    bss->nl_cb, 0);
+
+	return 0;
+
+ out_err:
+	nl_destroy_handles(&bss->nl_preq);
+	return -1;
+}
+
+
+static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
+				     int ifindex, int disabled)
+{
+	struct nl_msg *msg;
+	struct nlattr *bands, *band;
+	int ret;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: NL80211_CMD_SET_TX_BITRATE_MASK (ifindex=%d %s)",
+		   ifindex, disabled ? "NL80211_TXRATE_LEGACY=OFDM-only" :
+		   "no NL80211_TXRATE_LEGACY constraint");
+
+	msg = nl80211_ifindex_msg(drv, ifindex, 0,
+				  NL80211_CMD_SET_TX_BITRATE_MASK);
+	if (!msg)
+		return -1;
+
+	bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES);
+	if (!bands)
+		goto fail;
+
+	/*
+	 * Disable 2 GHz rates 1, 2, 5.5, 11 Mbps by masking out everything
+	 * else apart from 6, 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS
+	 * rates. All 5 GHz rates are left enabled.
+	 */
+	band = nla_nest_start(msg, NL80211_BAND_2GHZ);
+	if (!band ||
+	    (disabled && nla_put(msg, NL80211_TXRATE_LEGACY, 8,
+				 "\x0c\x12\x18\x24\x30\x48\x60\x6c")))
+		goto fail;
+	nla_nest_end(msg, band);
+
+	nla_nest_end(msg, bands);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Set TX rates failed: ret=%d "
+			   "(%s)", ret, strerror(-ret));
+	} else
+		drv->disabled_11b_rates = disabled;
+
+	return ret;
+
+fail:
+	nlmsg_free(msg);
+	return -1;
+}
+
+
+static void nl80211_remove_links(struct i802_bss *bss)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+	u8 link_id;
+
+	while (bss->links[0].link_id != NL80211_DRV_LINK_ID_NA) {
+		struct i802_link *link = &bss->links[0];
+
+		wpa_printf(MSG_DEBUG, "nl80211: MLD: remove link_id=%u",
+			   link->link_id);
+
+		wpa_driver_nl80211_del_beacon(bss, link);
+
+		link_id = link->link_id;
+
+		/* First remove the link locally */
+		if (bss->n_links == 1) {
+			bss->flink->link_id = NL80211_DRV_LINK_ID_NA;
+			os_memcpy(bss->flink->addr, bss->addr, ETH_ALEN);
+		} else {
+			struct i802_link *other = &bss->links[bss->n_links - 1];
+
+			os_memcpy(link, other, sizeof(*link));
+			other->link_id = NL80211_DRV_LINK_ID_NA;
+			os_memset(other->addr, 0, ETH_ALEN);
+
+			bss->n_links--;
+		}
+
+		/* Remove the link from the kernel */
+		msg = nl80211_drv_msg(drv, 0, NL80211_CMD_REMOVE_LINK);
+		if (!msg ||
+		    nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) {
+			nlmsg_free(msg);
+			wpa_printf(MSG_ERROR,
+				   "nl80211: remove link (%d) failed",
+				   link_id);
+			return;
+		}
+
+		ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+		if (ret) {
+			wpa_printf(MSG_ERROR,
+				   "nl80211: remove link (%d) failed. ret=%d (%s)",
+				   link_id, ret, strerror(-ret));
+			return;
+		}
+	}
+}
+
+
+static int wpa_driver_nl80211_deinit_ap(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	if (!is_ap_interface(drv->nlmode))
+		return -1;
+
+	/* Stop beaconing */
+	wpa_driver_nl80211_del_beacon(bss, bss->flink);
+
+	nl80211_remove_links(bss);
+
+	/*
+	 * If the P2P GO interface was dynamically added, then it is
+	 * possible that the interface change to station is not possible.
+	 */
+	if (drv->nlmode == NL80211_IFTYPE_P2P_GO && bss->if_dynamic)
+		return 0;
+
+	return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION);
+}
+
+
+static int wpa_driver_nl80211_stop_ap(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	if (!is_ap_interface(drv->nlmode))
+		return -1;
+
+	wpa_driver_nl80211_del_beacon_all(bss);
+
+	return 0;
+}
+
+
+static int wpa_driver_nl80211_deinit_p2p_cli(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	if (drv->nlmode != NL80211_IFTYPE_P2P_CLIENT)
+		return -1;
+
+	/*
+	 * If the P2P Client interface was dynamically added, then it is
+	 * possible that the interface change to station is not possible.
+	 */
+	if (bss->if_dynamic)
+		return 0;
+
+	return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION);
+}
+
+
+static void wpa_driver_nl80211_resume(void *priv)
+{
+	struct i802_bss *bss = priv;
+	enum nl80211_iftype nlmode = nl80211_get_ifmode(bss);
+
+	if (i802_set_iface_flags(bss, 1))
+		wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface up on resume event");
+
+	if (is_p2p_net_interface(nlmode))
+		nl80211_disable_11b_rates(bss->drv, bss->drv->ifindex, 1);
+}
+
+
+static int nl80211_signal_monitor(void *priv, int threshold, int hysteresis)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *cqm;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Signal monitor threshold=%d "
+		   "hysteresis=%d", threshold, hysteresis);
+
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_CQM)) ||
+	    !(cqm = nla_nest_start(msg, NL80211_ATTR_CQM)) ||
+	    nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THOLD, threshold) ||
+	    nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_HYST, hysteresis)) {
+		nlmsg_free(msg);
+		return -1;
+	}
+	nla_nest_end(msg, cqm);
+
+	return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+}
+
+
+static int get_channel_width(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct wpa_signal_info *sig_change = arg;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	sig_change->center_frq1 = -1;
+	sig_change->center_frq2 = -1;
+	sig_change->chanwidth = CHAN_WIDTH_UNKNOWN;
+
+	if (tb[NL80211_ATTR_CHANNEL_WIDTH]) {
+		sig_change->chanwidth = convert2width(
+			nla_get_u32(tb[NL80211_ATTR_CHANNEL_WIDTH]));
+		if (tb[NL80211_ATTR_CENTER_FREQ1])
+			sig_change->center_frq1 =
+				nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]);
+		if (tb[NL80211_ATTR_CENTER_FREQ2])
+			sig_change->center_frq2 =
+				nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]);
+	}
+
+	return NL_SKIP;
+}
+
+
+static int nl80211_get_channel_width(struct wpa_driver_nl80211_data *drv,
+				     struct wpa_signal_info *sig)
+{
+	struct nl_msg *msg;
+
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
+	return send_and_recv_msgs(drv, msg, get_channel_width, sig, NULL, NULL);
+}
+
+
+static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int res;
+
+	os_memset(si, 0, sizeof(*si));
+	res = nl80211_get_link_signal(drv, drv->bssid, &si->data);
+	if (res) {
+		if (drv->nlmode != NL80211_IFTYPE_ADHOC &&
+		    drv->nlmode != NL80211_IFTYPE_MESH_POINT)
+			return res;
+		si->data.signal = 0;
+	}
+
+	res = nl80211_get_channel_width(drv, si);
+	if (res != 0)
+		return res;
+
+	return nl80211_get_link_noise(drv, si);
+}
+
+
+static int get_links_noise(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1];
+	static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
+		[NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
+		[NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
+	};
+	struct wpa_mlo_signal_info *mlo_sig = arg;
+	int i;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (!tb[NL80211_ATTR_SURVEY_INFO]) {
+		wpa_printf(MSG_DEBUG, "nl80211: Survey data missing");
+		return NL_SKIP;
+	}
+
+	if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
+			     tb[NL80211_ATTR_SURVEY_INFO],
+			     survey_policy)) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Failed to parse nested attributes");
+		return NL_SKIP;
+	}
+
+	if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY])
+		return NL_SKIP;
+
+	if (!sinfo[NL80211_SURVEY_INFO_NOISE])
+		return NL_SKIP;
+
+	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+		if (!(mlo_sig->valid_links & BIT(i)))
+			continue;
+
+		if (nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) !=
+		    mlo_sig->links[i].frequency)
+			continue;
+
+		mlo_sig->links[i].current_noise =
+			(s8) nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
+		break;
+	}
+
+	return NL_SKIP;
+}
+
+
+static int nl80211_get_links_noise(struct wpa_driver_nl80211_data *drv,
+				   struct wpa_mlo_signal_info *mlo_sig)
+{
+	struct nl_msg *msg;
+
+	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
+	return send_and_recv_msgs(drv, msg, get_links_noise, mlo_sig,
+				  NULL, NULL);
+}
+
+
+static int get_links_channel_width(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct wpa_mlo_signal_info *mlo_sig = arg;
+	struct nlattr *link;
+	int rem_links;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (!tb[NL80211_ATTR_MLO_LINKS])
+		return NL_SKIP;
+
+	nla_for_each_nested(link, tb[NL80211_ATTR_MLO_LINKS], rem_links) {
+		struct nlattr *tb2[NL80211_ATTR_MAX + 1];
+		int link_id;
+
+		nla_parse(tb2, NL80211_ATTR_MAX, nla_data(link), nla_len(link),
+			  NULL);
+
+		if (!tb2[NL80211_ATTR_MLO_LINK_ID])
+			continue;
+
+		link_id = nla_get_u8(tb2[NL80211_ATTR_MLO_LINK_ID]);
+		if (link_id >= MAX_NUM_MLD_LINKS)
+			continue;
+
+		if (!tb2[NL80211_ATTR_CHANNEL_WIDTH])
+			continue;
+		mlo_sig->links[link_id].chanwidth = convert2width(
+			nla_get_u32(tb2[NL80211_ATTR_CHANNEL_WIDTH]));
+		if (tb2[NL80211_ATTR_CENTER_FREQ1])
+			mlo_sig->links[link_id].center_frq1 =
+				nla_get_u32(tb2[NL80211_ATTR_CENTER_FREQ1]);
+		if (tb2[NL80211_ATTR_CENTER_FREQ2])
+			mlo_sig->links[link_id].center_frq2 =
+				nla_get_u32(tb2[NL80211_ATTR_CENTER_FREQ2]);
+	}
+
+	return NL_SKIP;
+}
+
+
+static int nl80211_get_links_channel_width(struct wpa_driver_nl80211_data *drv,
+					   struct wpa_mlo_signal_info *mlo_sig)
+{
+	struct nl_msg *msg;
+
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
+	return send_and_recv_msgs(drv, msg, get_links_channel_width, mlo_sig,
+				  NULL, NULL);
+}
+
+
+static int nl80211_mlo_signal_poll(void *priv,
+				   struct wpa_mlo_signal_info *mlo_si)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int res;
+	int i;
+
+	if (drv->nlmode != NL80211_IFTYPE_STATION ||
+	    !drv->sta_mlo_info.valid_links)
+		return -1;
+
+	os_memset(mlo_si, 0, sizeof(*mlo_si));
+	mlo_si->valid_links = drv->sta_mlo_info.valid_links;
+
+	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+		if (!(mlo_si->valid_links & BIT(i)))
+			continue;
+
+		res = nl80211_get_link_signal(drv,
+					      drv->sta_mlo_info.links[i].bssid,
+					      &mlo_si->links[i].data);
+		if (res != 0)
+			return res;
+
+		mlo_si->links[i].center_frq1 = -1;
+		mlo_si->links[i].center_frq2 = -1;
+		mlo_si->links[i].chanwidth = CHAN_WIDTH_UNKNOWN;
+		mlo_si->links[i].current_noise = WPA_INVALID_NOISE;
+		mlo_si->links[i].frequency = drv->sta_mlo_info.links[i].freq;
+	}
+
+	res = nl80211_get_links_channel_width(drv, mlo_si);
+	if (res != 0)
+		return res;
+
+	return nl80211_get_links_noise(drv, mlo_si);
+}
+
+
+static int nl80211_set_param(void *priv, const char *param)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	if (param == NULL)
+		return 0;
+	wpa_printf(MSG_DEBUG, "nl80211: driver param='%s'", param);
+
+#ifdef CONFIG_P2P
+	if (os_strstr(param, "use_p2p_group_interface=1")) {
+		wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group "
+			   "interface");
+		drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
+		drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P;
+	}
+#endif /* CONFIG_P2P */
+
+	if (os_strstr(param, "use_monitor=1"))
+		drv->use_monitor = 1;
+
+	if (os_strstr(param, "force_connect_cmd=1")) {
+		drv->capa.flags &= ~WPA_DRIVER_FLAGS_SME;
+		drv->force_connect_cmd = 1;
+	}
+
+	if (os_strstr(param, "force_bss_selection=1"))
+		drv->capa.flags |= WPA_DRIVER_FLAGS_BSS_SELECTION;
+
+	if (os_strstr(param, "no_offchannel_tx=1")) {
+		drv->capa.flags &= ~WPA_DRIVER_FLAGS_OFFCHANNEL_TX;
+		drv->test_use_roc_tx = 1;
+	}
+
+	if (os_strstr(param, "control_port=0")) {
+		drv->capa.flags &= ~WPA_DRIVER_FLAGS_CONTROL_PORT;
+		drv->capa.flags2 &= ~(WPA_DRIVER_FLAGS2_CONTROL_PORT_RX |
+				      WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS);
+		drv->control_port_ap = 0;
+	}
+
+	if (os_strstr(param, "control_port_ap=1"))
+		drv->control_port_ap = 1;
+
+	if (os_strstr(param, "control_port_ap=0")) {
+		drv->capa.flags2 &= ~WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS;
+		drv->control_port_ap = 0;
+	}
+
+	if (os_strstr(param, "full_ap_client_state=0"))
+		drv->capa.flags &= ~WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE;
+
+	if (os_strstr(param, "no_rrm=1")) {
+		drv->no_rrm = 1;
+
+		if (!bss->in_deinit && !is_ap_interface(drv->nlmode) &&
+		    !is_mesh_interface(drv->nlmode)) {
+			nl80211_mgmt_unsubscribe(bss, "no_rrm=1");
+			if (nl80211_mgmt_subscribe_non_ap(bss) < 0)
+				wpa_printf(MSG_DEBUG,
+					   "nl80211: Failed to re-register Action frame processing - ignore for now");
+		}
+	}
+
+	if (os_strstr(param, "secure_ltf=1")) {
+		drv->capa.flags2 |= WPA_DRIVER_FLAGS2_SEC_LTF_STA |
+			WPA_DRIVER_FLAGS2_SEC_LTF_AP;
+	}
+
+	return 0;
+}
+
+
+static void * nl80211_global_init(void *ctx)
+{
+	struct nl80211_global *global;
+	struct netlink_config *cfg;
+
+	global = os_zalloc(sizeof(*global));
+	if (global == NULL)
+		return NULL;
+	global->ctx = ctx;
+	global->ioctl_sock = -1;
+	dl_list_init(&global->interfaces);
+	global->if_add_ifindex = -1;
+
+	cfg = os_zalloc(sizeof(*cfg));
+	if (cfg == NULL)
+		goto err;
+
+	cfg->ctx = global;
+	cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink;
+	cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink;
+	global->netlink = netlink_init(cfg);
+	if (global->netlink == NULL) {
+		os_free(cfg);
+		goto err;
+	}
+
+	if (wpa_driver_nl80211_init_nl_global(global) < 0)
+		goto err;
+
+	global->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
+	if (global->ioctl_sock < 0) {
+		wpa_printf(MSG_ERROR, "nl80211: socket(PF_INET,SOCK_DGRAM) failed: %s",
+			   strerror(errno));
+		goto err;
+	}
+
+	return global;
+
+err:
+	nl80211_global_deinit(global);
+	return NULL;
+}
+
+
+static void nl80211_global_deinit(void *priv)
+{
+	struct nl80211_global *global = priv;
+	if (global == NULL)
+		return;
+	if (!dl_list_empty(&global->interfaces)) {
+		wpa_printf(MSG_ERROR, "nl80211: %u interface(s) remain at "
+			   "nl80211_global_deinit",
+			   dl_list_len(&global->interfaces));
+	}
+
+	if (global->netlink)
+		netlink_deinit(global->netlink);
+
+	nl_destroy_handles(&global->nl);
+
+	if (global->nl_event)
+		nl80211_destroy_eloop_handle(&global->nl_event, 0);
+
+	nl_cb_put(global->nl_cb);
+
+	if (global->ioctl_sock >= 0)
+		close(global->ioctl_sock);
+
+	os_free(global);
+}
+
+
+static const char * nl80211_get_radio_name(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	return drv->phyname;
+}
+
+
+static int nl80211_pmkid(struct i802_bss *bss, int cmd,
+			 struct wpa_pmkid_params *params, bool skip_pmk)
+{
+	struct nl_msg *msg;
+
+	if (cmd == NL80211_CMD_SET_PMKSA)
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: NL80211_CMD_SET_PMKSA with skip_pmk=%s pmk_len=%zu",
+			   skip_pmk ? "true" : "false", params->pmk_len);
+
+	if (!(msg = nl80211_bss_msg(bss, 0, cmd)) ||
+	    (params->pmkid &&
+	     nla_put(msg, NL80211_ATTR_PMKID, 16, params->pmkid)) ||
+	    (params->bssid &&
+	     nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid)) ||
+	    (params->ssid_len &&
+	     nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) ||
+	    (params->fils_cache_id &&
+	     nla_put(msg, NL80211_ATTR_FILS_CACHE_ID, 2,
+		     params->fils_cache_id)) ||
+	    (params->pmk_lifetime &&
+	     nla_put_u32(msg, NL80211_ATTR_PMK_LIFETIME,
+			 params->pmk_lifetime)) ||
+	    (params->pmk_reauth_threshold &&
+	     nla_put_u8(msg, NL80211_ATTR_PMK_REAUTH_THRESHOLD,
+			params->pmk_reauth_threshold)) ||
+	    (cmd != NL80211_CMD_DEL_PMKSA &&
+	     params->pmk_len && !skip_pmk &&
+	     nla_put(msg, NL80211_ATTR_PMK, params->pmk_len, params->pmk))) {
+		nl80211_nlmsg_clear(msg);
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	return send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
+}
+
+
+static int nl80211_add_pmkid(void *priv, struct wpa_pmkid_params *params)
+{
+	struct i802_bss *bss = priv;
+	const size_t PMK_MAX_LEN = 64; /* current cfg80211 limit */
+	const size_t LEGACY_PMK_MAX_LEN = 48; /* old cfg80211 limit */
+	bool skip_pmk = params->pmk_len > PMK_MAX_LEN;
+	int ret;
+
+	if (params->bssid)
+		wpa_printf(MSG_DEBUG, "nl80211: Add PMKID for " MACSTR,
+			   MAC2STR(params->bssid));
+	else if (params->fils_cache_id && params->ssid_len) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Add PMKSA for cache id %02x%02x SSID %s",
+			   params->fils_cache_id[0], params->fils_cache_id[1],
+			   wpa_ssid_txt(params->ssid, params->ssid_len));
+	}
+
+	ret = nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, params, skip_pmk);
+	/*
+	 * Try again by skipping PMK if the first attempt failed with ERANGE
+	 * error, PMK was not skipped, and PMK length is greater than the
+	 * legacy kernel maximum allowed limit.
+	 */
+	if (ret == -ERANGE && !skip_pmk &&
+	    params->pmk_len > LEGACY_PMK_MAX_LEN)
+		ret = nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, params, true);
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: NL80211_CMD_SET_PMKSA failed: %d (%s)",
+			   ret, strerror(-ret));
+	}
+
+	return ret;
+}
+
+
+static int nl80211_remove_pmkid(void *priv, struct wpa_pmkid_params *params)
+{
+	struct i802_bss *bss = priv;
+	int ret;
+
+	if (params->bssid)
+		wpa_printf(MSG_DEBUG, "nl80211: Delete PMKID for " MACSTR,
+			   MAC2STR(params->bssid));
+	else if (params->fils_cache_id && params->ssid_len) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Delete PMKSA for cache id %02x%02x SSID %s",
+			   params->fils_cache_id[0], params->fils_cache_id[1],
+			   wpa_ssid_txt(params->ssid, params->ssid_len));
+	}
+
+	ret = nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, params, true);
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: NL80211_CMD_DEL_PMKSA failed: %d (%s)",
+			   ret, strerror(-ret));
+	}
+
+	return ret;
+}
+
+
+static int nl80211_flush_pmkid(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct nl_msg *msg;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Flush PMKIDs");
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_FLUSH_PMKSA);
+	if (!msg)
+		return -ENOBUFS;
+	return send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
+}
+
+
+static void clean_survey_results(struct survey_results *survey_results)
+{
+	struct freq_survey *survey, *tmp;
+
+	if (dl_list_empty(&survey_results->survey_list))
+		return;
+
+	dl_list_for_each_safe(survey, tmp, &survey_results->survey_list,
+			      struct freq_survey, list) {
+		dl_list_del(&survey->list);
+		os_free(survey);
+	}
+}
+
+
+static void add_survey(struct nlattr **sinfo, u32 ifidx,
+		       struct dl_list *survey_list)
+{
+	struct freq_survey *survey;
+
+	survey = os_zalloc(sizeof(struct freq_survey));
+	if  (!survey)
+		return;
+
+	survey->ifidx = ifidx;
+	survey->freq = nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]);
+	survey->filled = 0;
+
+	if (sinfo[NL80211_SURVEY_INFO_NOISE]) {
+		survey->nf = (int8_t)
+			nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
+		survey->filled |= SURVEY_HAS_NF;
+	}
+
+	if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME]) {
+		survey->channel_time =
+			nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME]);
+		survey->filled |= SURVEY_HAS_CHAN_TIME;
+	}
+
+	if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY]) {
+		survey->channel_time_busy =
+			nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY]);
+		survey->filled |= SURVEY_HAS_CHAN_TIME_BUSY;
+	}
+
+	if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX]) {
+		survey->channel_time_rx =
+			nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX]);
+		survey->filled |= SURVEY_HAS_CHAN_TIME_RX;
+	}
+
+	if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX]) {
+		survey->channel_time_tx =
+			nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX]);
+		survey->filled |= SURVEY_HAS_CHAN_TIME_TX;
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: Freq survey dump event (freq=%d MHz noise=%d channel_time=%ld busy_time=%ld tx_time=%ld rx_time=%ld filled=%04x)",
+		   survey->freq,
+		   survey->nf,
+		   (unsigned long int) survey->channel_time,
+		   (unsigned long int) survey->channel_time_busy,
+		   (unsigned long int) survey->channel_time_tx,
+		   (unsigned long int) survey->channel_time_rx,
+		   survey->filled);
+
+	dl_list_add_tail(survey_list, &survey->list);
+}
+
+
+static int check_survey_ok(struct nlattr **sinfo, u32 surveyed_freq,
+			   unsigned int freq_filter)
+{
+	if (!freq_filter)
+		return 1;
+
+	return freq_filter == surveyed_freq;
+}
+
+
+static int survey_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1];
+	struct survey_results *survey_results;
+	u32 surveyed_freq = 0;
+	u32 ifidx;
+
+	static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
+		[NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
+		[NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
+	};
+
+	survey_results = (struct survey_results *) arg;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (!tb[NL80211_ATTR_IFINDEX])
+		return NL_SKIP;
+
+	ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
+
+	if (!tb[NL80211_ATTR_SURVEY_INFO])
+		return NL_SKIP;
+
+	if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
+			     tb[NL80211_ATTR_SURVEY_INFO],
+			     survey_policy))
+		return NL_SKIP;
+
+	if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY]) {
+		wpa_printf(MSG_ERROR, "nl80211: Invalid survey data");
+		return NL_SKIP;
+	}
+
+	surveyed_freq = nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]);
+
+	if (!check_survey_ok(sinfo, surveyed_freq,
+			     survey_results->freq_filter))
+		return NL_SKIP;
+
+	if (survey_results->freq_filter &&
+	    survey_results->freq_filter != surveyed_freq) {
+		wpa_printf(MSG_EXCESSIVE, "nl80211: Ignoring survey data for freq %d MHz",
+			   surveyed_freq);
+		return NL_SKIP;
+	}
+
+	add_survey(sinfo, ifidx, &survey_results->survey_list);
+
+	return NL_SKIP;
+}
+
+
+static int wpa_driver_nl80211_get_survey(void *priv, unsigned int freq)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int err;
+	union wpa_event_data data;
+	struct survey_results *survey_results;
+
+	os_memset(&data, 0, sizeof(data));
+	survey_results = &data.survey_results;
+
+	dl_list_init(&survey_results->survey_list);
+
+	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
+	if (!msg)
+		return -ENOBUFS;
+
+	if (freq)
+		data.survey_results.freq_filter = freq;
+
+	do {
+		wpa_printf(MSG_DEBUG, "nl80211: Fetch survey data");
+		err = send_and_recv_msgs(drv, msg, survey_handler,
+					 survey_results, NULL, NULL);
+	} while (err > 0);
+
+	if (err)
+		wpa_printf(MSG_ERROR, "nl80211: Failed to process survey data");
+	else
+		wpa_supplicant_event(drv->ctx, EVENT_SURVEY, &data);
+
+	clean_survey_results(survey_results);
+	return err;
+}
+
+
+static void nl80211_set_rekey_info(void *priv, const u8 *kek, size_t kek_len,
+				   const u8 *kck, size_t kck_len,
+				   const u8 *replay_ctr)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nlattr *replay_nested;
+	struct nl_msg *msg;
+	int ret;
+
+	if (!drv->set_rekey_offload)
+		return;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Set rekey offload");
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_REKEY_OFFLOAD)) ||
+	    !(replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA)) ||
+	    nla_put(msg, NL80211_REKEY_DATA_KEK, kek_len, kek) ||
+	    (kck_len && nla_put(msg, NL80211_REKEY_DATA_KCK, kck_len, kck)) ||
+	    nla_put(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN,
+		    replay_ctr)) {
+		nl80211_nlmsg_clear(msg);
+		nlmsg_free(msg);
+		return;
+	}
+
+	nla_nest_end(msg, replay_nested);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret == -EOPNOTSUPP) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Driver does not support rekey offload");
+		drv->set_rekey_offload = 0;
+	}
+}
+
+
+static void nl80211_send_null_frame(struct i802_bss *bss, const u8 *own_addr,
+				    const u8 *addr, int qos)
+{
+	/* send data frame to poll STA and check whether
+	 * this frame is ACKed */
+	struct {
+		struct ieee80211_hdr hdr;
+		u16 qos_ctl;
+	} STRUCT_PACKED nulldata;
+	size_t size;
+
+	/* Send data frame to poll STA and check whether this frame is ACKed */
+
+	os_memset(&nulldata, 0, sizeof(nulldata));
+
+	if (qos) {
+		nulldata.hdr.frame_control =
+			IEEE80211_FC(WLAN_FC_TYPE_DATA,
+				     WLAN_FC_STYPE_QOS_NULL);
+		size = sizeof(nulldata);
+	} else {
+		nulldata.hdr.frame_control =
+			IEEE80211_FC(WLAN_FC_TYPE_DATA,
+				     WLAN_FC_STYPE_NULLFUNC);
+		size = sizeof(struct ieee80211_hdr);
+	}
+
+	nulldata.hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS);
+	os_memcpy(nulldata.hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN);
+	os_memcpy(nulldata.hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
+	os_memcpy(nulldata.hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
+
+	if (wpa_driver_nl80211_send_mlme(bss, (u8 *) &nulldata, size, 0, 0, 0,
+					 0, 0, NULL, 0, 0, -1) < 0)
+		wpa_printf(MSG_DEBUG, "nl80211_send_null_frame: Failed to "
+			   "send poll frame");
+}
+
+static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr,
+				int qos)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	u64 cookie;
+	int ret;
+
+	if (!drv->poll_command_supported) {
+		nl80211_send_null_frame(bss, own_addr, addr, qos);
+		return;
+	}
+
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_PROBE_CLIENT)) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
+		nlmsg_free(msg);
+		return;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie, NULL, NULL);
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "nl80211: Client probe request for "
+			   MACSTR " failed: ret=%d (%s)",
+			   MAC2STR(addr), ret, strerror(-ret));
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Client probe request addr=" MACSTR
+			   " cookie=%llu", MAC2STR(addr),
+			   (long long unsigned int) cookie);
+	}
+}
+
+
+static int nl80211_set_power_save(struct i802_bss *bss, int enabled)
+{
+	struct nl_msg *msg;
+	int ret;
+
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_POWER_SAVE)) ||
+	    nla_put_u32(msg, NL80211_ATTR_PS_STATE,
+			enabled ? NL80211_PS_ENABLED : NL80211_PS_DISABLED)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Setting PS state %s failed: %d (%s)",
+			   enabled ? "enabled" : "disabled",
+			   ret, strerror(-ret));
+	}
+	return ret;
+}
+
+
+static int nl80211_set_p2p_powersave(void *priv, int legacy_ps, int opp_ps,
+				     int ctwindow)
+{
+	struct i802_bss *bss = priv;
+
+	wpa_printf(MSG_DEBUG, "nl80211: set_p2p_powersave (legacy_ps=%d "
+		   "opp_ps=%d ctwindow=%d)", legacy_ps, opp_ps, ctwindow);
+
+	if (opp_ps != -1 || ctwindow != -1) {
+#ifdef ANDROID_P2P
+		wpa_driver_set_p2p_ps(priv, legacy_ps, opp_ps, ctwindow);
+#else /* ANDROID_P2P */
+		return -1; /* Not yet supported */
+#endif /* ANDROID_P2P */
+	}
+
+	if (legacy_ps == -1)
+		return 0;
+	if (legacy_ps != 0 && legacy_ps != 1)
+		return -1; /* Not yet supported */
+
+	return nl80211_set_power_save(bss, legacy_ps);
+}
+
+
+static int nl80211_start_radar_detection(void *priv,
+					 struct hostapd_freq_params *freq)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Start radar detection (CAC) %d MHz (ht_enabled=%d, vht_enabled=%d, he_enabled=%d, bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)",
+		   freq->freq, freq->ht_enabled, freq->vht_enabled, freq->he_enabled,
+		   freq->bandwidth, freq->center_freq1, freq->center_freq2);
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_RADAR)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Driver does not support radar "
+			   "detection");
+		return -1;
+	}
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_RADAR_DETECT)) ||
+	    nl80211_put_freq_params(msg, freq) < 0) {
+		nlmsg_free(msg);
+		return -1;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret == 0)
+		return 0;
+	wpa_printf(MSG_DEBUG, "nl80211: Failed to start radar detection: "
+		   "%d (%s)", ret, strerror(-ret));
+	return -1;
+}
+
+#ifdef CONFIG_TDLS
+
+static int nl80211_add_peer_capab(struct nl_msg *msg,
+				  enum tdls_peer_capability capa)
+{
+	u32 peer_capab = 0;
+
+	if (!capa)
+		return 0;
+
+	if (capa & TDLS_PEER_HT)
+		peer_capab |= NL80211_TDLS_PEER_HT;
+	if (capa & TDLS_PEER_VHT)
+		peer_capab |= NL80211_TDLS_PEER_VHT;
+	if (capa & TDLS_PEER_WMM)
+		peer_capab |= NL80211_TDLS_PEER_WMM;
+	if (capa & TDLS_PEER_HE)
+		peer_capab |= NL80211_TDLS_PEER_HE;
+
+	return nla_put_u32(msg, NL80211_ATTR_TDLS_PEER_CAPABILITY,
+			   peer_capab);
+}
+
+
+static int nl80211_send_tdls_mgmt(void *priv, const u8 *dst, u8 action_code,
+				  u8 dialog_token, u16 status_code,
+				  u32 peer_capab, int initiator, const u8 *buf,
+				  size_t len)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
+		return -EOPNOTSUPP;
+
+	if (!dst)
+		return -EINVAL;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_TDLS_MGMT)) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, dst) ||
+	    nla_put_u8(msg, NL80211_ATTR_TDLS_ACTION, action_code) ||
+	    nla_put_u8(msg, NL80211_ATTR_TDLS_DIALOG_TOKEN, dialog_token) ||
+	    nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, status_code) ||
+	    nl80211_add_peer_capab(msg, peer_capab) ||
+	    (initiator && nla_put_flag(msg, NL80211_ATTR_TDLS_INITIATOR)) ||
+	    nla_put(msg, NL80211_ATTR_IE, len, buf))
+		goto fail;
+
+	return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+
+fail:
+	nlmsg_free(msg);
+	return -ENOBUFS;
+}
+
+
+static int nl80211_tdls_oper(void *priv, enum tdls_oper oper, const u8 *peer)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	enum nl80211_tdls_operation nl80211_oper;
+	int res;
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
+		return -EOPNOTSUPP;
+
+	switch (oper) {
+	case TDLS_DISCOVERY_REQ:
+		nl80211_oper = NL80211_TDLS_DISCOVERY_REQ;
+		break;
+	case TDLS_SETUP:
+		nl80211_oper = NL80211_TDLS_SETUP;
+		break;
+	case TDLS_TEARDOWN:
+		nl80211_oper = NL80211_TDLS_TEARDOWN;
+		break;
+	case TDLS_ENABLE_LINK:
+		nl80211_oper = NL80211_TDLS_ENABLE_LINK;
+		break;
+	case TDLS_DISABLE_LINK:
+		nl80211_oper = NL80211_TDLS_DISABLE_LINK;
+		break;
+	case TDLS_ENABLE:
+		return 0;
+	case TDLS_DISABLE:
+		return 0;
+	default:
+		return -EINVAL;
+	}
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_TDLS_OPER)) ||
+	    nla_put_u8(msg, NL80211_ATTR_TDLS_OPERATION, nl80211_oper) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	res = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	wpa_printf(MSG_DEBUG, "nl80211: TDLS_OPER: oper=%d mac=" MACSTR
+		   " --> res=%d (%s)", nl80211_oper, MAC2STR(peer), res,
+		   strerror(-res));
+	return res;
+}
+
+
+static int
+nl80211_tdls_enable_channel_switch(void *priv, const u8 *addr, u8 oper_class,
+				   const struct hostapd_freq_params *params)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret = -ENOBUFS;
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT) ||
+	    !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH))
+		return -EOPNOTSUPP;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Enable TDLS channel switch " MACSTR
+		   " oper_class=%u freq=%u",
+		   MAC2STR(addr), oper_class, params->freq);
+	msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_TDLS_CHANNEL_SWITCH);
+	if (!msg ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+	    nla_put_u8(msg, NL80211_ATTR_OPER_CLASS, oper_class) ||
+	    (ret = nl80211_put_freq_params(msg, params))) {
+		nlmsg_free(msg);
+		wpa_printf(MSG_DEBUG, "nl80211: Could not build TDLS chan switch");
+		return ret;
+	}
+
+	return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+}
+
+
+static int
+nl80211_tdls_disable_channel_switch(void *priv, const u8 *addr)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT) ||
+	    !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH))
+		return -EOPNOTSUPP;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Disable TDLS channel switch " MACSTR,
+		   MAC2STR(addr));
+	msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH);
+	if (!msg ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
+		nlmsg_free(msg);
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Could not build TDLS cancel chan switch");
+		return -ENOBUFS;
+	}
+
+	return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+}
+
+#endif /* CONFIG TDLS */
+
+
+static int driver_nl80211_set_key(void *priv,
+				  struct wpa_driver_set_key_params *params)
+{
+	struct i802_bss *bss = priv;
+
+	return wpa_driver_nl80211_set_key(bss, params);
+}
+
+
+static int driver_nl80211_scan2(void *priv,
+				struct wpa_driver_scan_params *params)
+{
+	struct i802_bss *bss = priv;
+#ifdef CONFIG_DRIVER_NL80211_QCA
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	/*
+	 * Do a vendor specific scan if possible. If only_new_results is
+	 * set, do a normal scan since a kernel (cfg80211) BSS cache flush
+	 * cannot be achieved through a vendor scan. The below condition may
+	 * need to be modified if new scan flags are added in the future whose
+	 * functionality can only be achieved through a normal scan.
+	 */
+	if (drv->scan_vendor_cmd_avail && !params->only_new_results)
+		return wpa_driver_nl80211_vendor_scan(bss, params);
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+	return wpa_driver_nl80211_scan(bss, params);
+}
+
+
+static int driver_nl80211_deauthenticate(void *priv, const u8 *addr,
+					 u16 reason_code)
+{
+	struct i802_bss *bss = priv;
+	return wpa_driver_nl80211_deauthenticate(bss, addr, reason_code);
+}
+
+
+static int driver_nl80211_authenticate(void *priv,
+				       struct wpa_driver_auth_params *params)
+{
+	struct i802_bss *bss = priv;
+	return wpa_driver_nl80211_authenticate(bss, params);
+}
+
+
+static void driver_nl80211_deinit(void *priv)
+{
+	struct i802_bss *bss = priv;
+	wpa_driver_nl80211_deinit(bss);
+}
+
+
+static int driver_nl80211_if_remove(void *priv, enum wpa_driver_if_type type,
+				    const char *ifname)
+{
+	struct i802_bss *bss = priv;
+	return wpa_driver_nl80211_if_remove(bss, type, ifname);
+}
+
+
+static int driver_nl80211_send_mlme(void *priv, const u8 *data,
+				    size_t data_len, int noack,
+				    unsigned int freq,
+				    const u16 *csa_offs, size_t csa_offs_len,
+				    int no_encrypt, unsigned int wait,
+				    int link_id)
+{
+	struct i802_bss *bss = priv;
+	return wpa_driver_nl80211_send_mlme(bss, data, data_len, noack,
+					    freq, 0, 0, wait, csa_offs,
+					    csa_offs_len, no_encrypt, link_id);
+}
+
+
+static int driver_nl80211_sta_remove(void *priv, const u8 *addr)
+{
+	struct i802_bss *bss = priv;
+	return wpa_driver_nl80211_sta_remove(bss, addr, -1, 0);
+}
+
+
+static int driver_nl80211_set_sta_vlan(void *priv, const u8 *addr,
+				       const char *ifname, int vlan_id,
+				       int link_id)
+{
+	struct i802_bss *bss = priv;
+	return i802_set_sta_vlan(bss, addr, ifname, vlan_id, link_id);
+}
+
+
+static int driver_nl80211_read_sta_data(void *priv,
+					struct hostap_sta_driver_data *data,
+					const u8 *addr)
+{
+	struct i802_bss *bss = priv;
+
+	os_memset(data, 0, sizeof(*data));
+	return i802_read_sta_data(bss, data, addr);
+}
+
+
+static int driver_nl80211_send_action(void *priv, unsigned int freq,
+				      unsigned int wait_time,
+				      const u8 *dst, const u8 *src,
+				      const u8 *bssid,
+				      const u8 *data, size_t data_len,
+				      int no_cck)
+{
+	struct i802_bss *bss = priv;
+	return wpa_driver_nl80211_send_action(bss, freq, wait_time, dst, src,
+					      bssid, data, data_len, no_cck);
+}
+
+
+static int driver_nl80211_probe_req_report(void *priv, int report)
+{
+	struct i802_bss *bss = priv;
+	return wpa_driver_nl80211_probe_req_report(bss, report);
+}
+
+
+static int wpa_driver_nl80211_update_ft_ies(void *priv, const u8 *md,
+					    const u8 *ies, size_t ies_len)
+{
+	int ret;
+	struct nl_msg *msg;
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	u16 mdid = WPA_GET_LE16(md);
+
+	wpa_printf(MSG_DEBUG, "nl80211: Updating FT IEs");
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_UPDATE_FT_IES)) ||
+	    nla_put(msg, NL80211_ATTR_IE, ies_len, ies) ||
+	    nla_put_u16(msg, NL80211_ATTR_MDID, mdid)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: update_ft_ies failed "
+			   "err=%d (%s)", ret, strerror(-ret));
+	}
+
+	return ret;
+}
+
+
+static int nl80211_update_dh_ie(void *priv, const u8 *peer_mac,
+				u16 reason_code, const u8 *ie, size_t ie_len)
+{
+	int ret;
+	struct nl_msg *msg;
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Updating DH IE peer: " MACSTR
+		   " reason %u", MAC2STR(peer_mac), reason_code);
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_UPDATE_OWE_INFO)) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer_mac) ||
+	    nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, reason_code) ||
+	    (ie && nla_put(msg, NL80211_ATTR_IE, ie_len, ie))) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: update_dh_ie failed err=%d (%s)",
+			   ret, strerror(-ret));
+	}
+
+	return ret;
+}
+
+
+static const u8 * wpa_driver_nl80211_get_macaddr(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE)
+		return NULL;
+
+	return bss->addr;
+}
+
+
+static const char * scan_state_str(enum scan_states scan_state)
+{
+	switch (scan_state) {
+	case NO_SCAN:
+		return "NO_SCAN";
+	case SCAN_REQUESTED:
+		return "SCAN_REQUESTED";
+	case SCAN_STARTED:
+		return "SCAN_STARTED";
+	case SCAN_COMPLETED:
+		return "SCAN_COMPLETED";
+	case SCAN_ABORTED:
+		return "SCAN_ABORTED";
+	case SCHED_SCAN_STARTED:
+		return "SCHED_SCAN_STARTED";
+	case SCHED_SCAN_STOPPED:
+		return "SCHED_SCAN_STOPPED";
+	case SCHED_SCAN_RESULTS:
+		return "SCHED_SCAN_RESULTS";
+	}
+
+	return "??";
+}
+
+
+static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int res;
+	char *pos, *end;
+	struct nl_msg *msg;
+	char alpha2[3] = { 0, 0, 0 };
+
+	pos = buf;
+	end = buf + buflen;
+
+	res = os_snprintf(pos, end - pos,
+			  "ifindex=%d\n"
+			  "ifname=%s\n"
+			  "brname=%s\n"
+			  "addr=" MACSTR "\n"
+			  "freq=%d\n"
+			  "%s%s%s%s%s%s",
+			  bss->ifindex,
+			  bss->ifname,
+			  bss->brname,
+			  MAC2STR(bss->addr),
+			  bss->flink->freq,
+			  bss->flink->beacon_set ? "beacon_set=1\n" : "",
+			  bss->added_if_into_bridge ?
+			  "added_if_into_bridge=1\n" : "",
+			  bss->already_in_bridge ? "already_in_bridge=1\n" : "",
+			  bss->added_bridge ? "added_bridge=1\n" : "",
+			  bss->in_deinit ? "in_deinit=1\n" : "",
+			  bss->if_dynamic ? "if_dynamic=1\n" : "");
+	if (os_snprintf_error(end - pos, res))
+		return pos - buf;
+	pos += res;
+
+	if (bss->wdev_id_set) {
+		res = os_snprintf(pos, end - pos, "wdev_id=%llu\n",
+				  (unsigned long long) bss->wdev_id);
+		if (os_snprintf_error(end - pos, res))
+			return pos - buf;
+		pos += res;
+	}
+
+	res = os_snprintf(pos, end - pos,
+			  "phyname=%s\n"
+			  "perm_addr=" MACSTR "\n"
+			  "drv_ifindex=%d\n"
+			  "operstate=%d\n"
+			  "scan_state=%s\n"
+			  "auth_bssid=" MACSTR "\n"
+			  "auth_attempt_bssid=" MACSTR "\n"
+			  "bssid=" MACSTR "\n"
+			  "prev_bssid=" MACSTR "\n"
+			  "associated=%d\n"
+			  "assoc_freq=%u\n"
+			  "monitor_sock=%d\n"
+			  "monitor_ifidx=%d\n"
+			  "monitor_refcount=%d\n"
+			  "last_mgmt_freq=%u\n"
+			  "eapol_tx_sock=%d\n"
+			  "%s%s%s%s%s%s%s%s%s%s%s%s%s",
+			  drv->phyname,
+			  MAC2STR(drv->perm_addr),
+			  drv->ifindex,
+			  drv->operstate,
+			  scan_state_str(drv->scan_state),
+			  MAC2STR(drv->auth_bssid),
+			  MAC2STR(drv->auth_attempt_bssid),
+			  MAC2STR(drv->bssid),
+			  MAC2STR(drv->prev_bssid),
+			  drv->associated,
+			  drv->assoc_freq,
+			  drv->monitor_sock,
+			  drv->monitor_ifidx,
+			  drv->monitor_refcount,
+			  drv->last_mgmt_freq,
+			  drv->eapol_tx_sock,
+			  drv->ignore_if_down_event ?
+			  "ignore_if_down_event=1\n" : "",
+			  drv->scan_complete_events ?
+			  "scan_complete_events=1\n" : "",
+			  drv->disabled_11b_rates ?
+			  "disabled_11b_rates=1\n" : "",
+			  drv->pending_remain_on_chan ?
+			  "pending_remain_on_chan=1\n" : "",
+			  drv->in_interface_list ? "in_interface_list=1\n" : "",
+			  drv->device_ap_sme ? "device_ap_sme=1\n" : "",
+			  drv->poll_command_supported ?
+			  "poll_command_supported=1\n" : "",
+			  drv->data_tx_status ? "data_tx_status=1\n" : "",
+			  drv->scan_for_auth ? "scan_for_auth=1\n" : "",
+			  drv->retry_auth ? "retry_auth=1\n" : "",
+			  drv->use_monitor ? "use_monitor=1\n" : "",
+			  drv->ignore_next_local_disconnect ?
+			  "ignore_next_local_disconnect=1\n" : "",
+			  drv->ignore_next_local_deauth ?
+			  "ignore_next_local_deauth=1\n" : "");
+	if (os_snprintf_error(end - pos, res))
+		return pos - buf;
+	pos += res;
+
+	if (drv->sta_mlo_info.valid_links) {
+		int i;
+		struct driver_sta_mlo_info *mlo = &drv->sta_mlo_info;
+
+		res = os_snprintf(pos, end - pos,
+				  "ap_mld_addr=" MACSTR "\n"
+				  "default_map=%d\n",
+				   MAC2STR(mlo->ap_mld_addr),
+				   mlo->default_map);
+		if (os_snprintf_error(end - pos, res))
+			return pos - buf;
+		pos += res;
+
+		for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+			if (!(mlo->valid_links & BIT(i)))
+				continue;
+
+			res = os_snprintf(pos, end - pos,
+					  "link_addr[%u]=" MACSTR "\n"
+					  "link_bssid[%u]=" MACSTR "\n"
+					  "link_freq[%u]=%u\n",
+					  i, MAC2STR(mlo->links[i].addr),
+					  i, MAC2STR(mlo->links[i].bssid),
+					  i, mlo->links[i].freq);
+			if (os_snprintf_error(end - pos, res))
+				return pos - buf;
+			pos += res;
+
+			if (!mlo->default_map) {
+				res = os_snprintf(
+					pos, end - pos,
+					"uplink_map[%u]=%x\n"
+					"downlink_map[%u]=%x\n",
+					i, mlo->links[i].t2lmap.uplink,
+					i, mlo->links[i].t2lmap.downlink);
+				if (os_snprintf_error(end - pos, res))
+					return pos - buf;
+				pos += res;
+			}
+		}
+	}
+
+	if (drv->has_capability) {
+		res = os_snprintf(pos, end - pos,
+				  "capa.key_mgmt=0x%x\n"
+				  "capa.enc=0x%x\n"
+				  "capa.auth=0x%x\n"
+				  "capa.flags=0x%llx\n"
+				  "capa.rrm_flags=0x%x\n"
+				  "capa.max_scan_ssids=%d\n"
+				  "capa.max_sched_scan_ssids=%d\n"
+				  "capa.sched_scan_supported=%d\n"
+				  "capa.max_match_sets=%d\n"
+				  "capa.max_remain_on_chan=%u\n"
+				  "capa.max_stations=%u\n"
+				  "capa.probe_resp_offloads=0x%x\n"
+				  "capa.max_acl_mac_addrs=%u\n"
+				  "capa.num_multichan_concurrent=%u\n"
+				  "capa.mac_addr_rand_sched_scan_supported=%d\n"
+				  "capa.mac_addr_rand_scan_supported=%d\n"
+				  "capa.conc_capab=%u\n"
+				  "capa.max_conc_chan_2_4=%u\n"
+				  "capa.max_conc_chan_5_0=%u\n"
+				  "capa.max_sched_scan_plans=%u\n"
+				  "capa.max_sched_scan_plan_interval=%u\n"
+				  "capa.max_sched_scan_plan_iterations=%u\n"
+				  "capa.mbssid_max_interfaces=%u\n"
+				  "capa.ema_max_periodicity=%u\n",
+				  drv->capa.key_mgmt,
+				  drv->capa.enc,
+				  drv->capa.auth,
+				  (unsigned long long) drv->capa.flags,
+				  drv->capa.rrm_flags,
+				  drv->capa.max_scan_ssids,
+				  drv->capa.max_sched_scan_ssids,
+				  drv->capa.sched_scan_supported,
+				  drv->capa.max_match_sets,
+				  drv->capa.max_remain_on_chan,
+				  drv->capa.max_stations,
+				  drv->capa.probe_resp_offloads,
+				  drv->capa.max_acl_mac_addrs,
+				  drv->capa.num_multichan_concurrent,
+				  drv->capa.mac_addr_rand_sched_scan_supported,
+				  drv->capa.mac_addr_rand_scan_supported,
+				  drv->capa.conc_capab,
+				  drv->capa.max_conc_chan_2_4,
+				  drv->capa.max_conc_chan_5_0,
+				  drv->capa.max_sched_scan_plans,
+				  drv->capa.max_sched_scan_plan_interval,
+				  drv->capa.max_sched_scan_plan_iterations,
+				  drv->capa.mbssid_max_interfaces,
+				  drv->capa.ema_max_periodicity);
+		if (os_snprintf_error(end - pos, res))
+			return pos - buf;
+		pos += res;
+	}
+
+	msg = nlmsg_alloc();
+	if (msg &&
+	    nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG) &&
+	    nla_put_u32(msg, NL80211_ATTR_WIPHY, drv->wiphy_idx) == 0) {
+		if (send_and_recv_msgs(drv, msg, nl80211_get_country,
+				       alpha2, NULL, NULL) == 0 &&
+		    alpha2[0]) {
+			res = os_snprintf(pos, end - pos, "country=%s\n",
+					  alpha2);
+			if (os_snprintf_error(end - pos, res))
+				return pos - buf;
+			pos += res;
+		}
+	} else {
+		nlmsg_free(msg);
+	}
+
+	return pos - buf;
+}
+
+
+static int set_beacon_data(struct nl_msg *msg, struct beacon_data *settings)
+{
+	if ((settings->head &&
+	     nla_put(msg, NL80211_ATTR_BEACON_HEAD,
+		     settings->head_len, settings->head)) ||
+	    (settings->tail &&
+	     nla_put(msg, NL80211_ATTR_BEACON_TAIL,
+		     settings->tail_len, settings->tail)) ||
+	    (settings->beacon_ies &&
+	     nla_put(msg, NL80211_ATTR_IE,
+		     settings->beacon_ies_len, settings->beacon_ies)) ||
+	    (settings->proberesp_ies &&
+	     nla_put(msg, NL80211_ATTR_IE_PROBE_RESP,
+		     settings->proberesp_ies_len, settings->proberesp_ies)) ||
+	    (settings->assocresp_ies &&
+	     nla_put(msg, NL80211_ATTR_IE_ASSOC_RESP,
+		     settings->assocresp_ies_len, settings->assocresp_ies)) ||
+	    (settings->probe_resp &&
+	     nla_put(msg, NL80211_ATTR_PROBE_RESP,
+		     settings->probe_resp_len, settings->probe_resp)))
+		return -ENOBUFS;
+
+	return 0;
+}
+
+
+static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
+{
+	struct nl_msg *msg;
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nlattr *beacon_csa;
+	int ret = -ENOBUFS;
+	int csa_off_len = 0;
+	int i;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Channel switch request (cs_count=%u block_tx=%u freq=%d channel=%d sec_channel_offset=%d width=%d cf1=%d cf2=%d puncturing_bitmap=0x%04x%s%s%s)",
+		   settings->cs_count, settings->block_tx,
+		   settings->freq_params.freq,
+		   settings->freq_params.channel,
+		   settings->freq_params.sec_channel_offset,
+		   settings->freq_params.bandwidth,
+		   settings->freq_params.center_freq1,
+		   settings->freq_params.center_freq2,
+		   settings->punct_bitmap,
+		   settings->freq_params.ht_enabled ? " ht" : "",
+		   settings->freq_params.vht_enabled ? " vht" : "",
+		   settings->freq_params.he_enabled ? " he" : "");
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_AP_CSA)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Driver does not support channel switch command");
+		return -EOPNOTSUPP;
+	}
+
+	if (drv->nlmode != NL80211_IFTYPE_AP &&
+	    drv->nlmode != NL80211_IFTYPE_P2P_GO &&
+	    drv->nlmode != NL80211_IFTYPE_MESH_POINT)
+		return -EOPNOTSUPP;
+
+	/*
+	 * Remove empty counters, assuming Probe Response and Beacon frame
+	 * counters match. This implementation assumes that there are only two
+	 * counters.
+	 */
+	if (settings->counter_offset_beacon[0] &&
+	    !settings->counter_offset_beacon[1]) {
+		csa_off_len = 1;
+	} else if (settings->counter_offset_beacon[1] &&
+		   !settings->counter_offset_beacon[0]) {
+		csa_off_len = 1;
+		settings->counter_offset_beacon[0] =
+			settings->counter_offset_beacon[1];
+		settings->counter_offset_presp[0] =
+			settings->counter_offset_presp[1];
+	} else if (settings->counter_offset_beacon[1] &&
+		   settings->counter_offset_beacon[0]) {
+		csa_off_len = 2;
+	} else {
+		wpa_printf(MSG_ERROR, "nl80211: No CSA counters provided");
+		return -EINVAL;
+	}
+
+	/* Check CSA counters validity */
+	if (drv->capa.max_csa_counters &&
+	    csa_off_len > drv->capa.max_csa_counters) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Too many CSA counters provided");
+		return -EINVAL;
+	}
+
+	if (!settings->beacon_csa.tail)
+		return -EINVAL;
+
+	for (i = 0; i < csa_off_len; i++) {
+		u16 csa_c_off_bcn = settings->counter_offset_beacon[i];
+		u16 csa_c_off_presp = settings->counter_offset_presp[i];
+
+		if ((settings->beacon_csa.tail_len <= csa_c_off_bcn) ||
+		    (settings->beacon_csa.tail[csa_c_off_bcn] !=
+		     settings->cs_count))
+			return -EINVAL;
+
+		if (settings->beacon_csa.probe_resp &&
+		    ((settings->beacon_csa.probe_resp_len <=
+		      csa_c_off_presp) ||
+		     (settings->beacon_csa.probe_resp[csa_c_off_presp] !=
+		      settings->cs_count)))
+			return -EINVAL;
+	}
+
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_CHANNEL_SWITCH)) ||
+	    nla_put_u32(msg, NL80211_ATTR_CH_SWITCH_COUNT,
+			settings->cs_count) ||
+	    (ret = nl80211_put_freq_params(msg, &settings->freq_params)) ||
+	    (settings->block_tx &&
+	     nla_put_flag(msg, NL80211_ATTR_CH_SWITCH_BLOCK_TX)) ||
+	    (settings->punct_bitmap &&
+	     nla_put_u32(msg, NL80211_ATTR_PUNCT_BITMAP,
+			 settings->punct_bitmap)))
+		goto error;
+
+	/* beacon_after params */
+	ret = set_beacon_data(msg, &settings->beacon_after);
+	if (ret)
+		goto error;
+
+	if (drv->nlmode == NL80211_IFTYPE_MESH_POINT) {
+		nla_put_flag(msg, NL80211_ATTR_HANDLE_DFS);
+	}
+
+	/* beacon_csa params */
+	beacon_csa = nla_nest_start(msg, NL80211_ATTR_CSA_IES);
+	if (!beacon_csa)
+		goto fail;
+
+	ret = set_beacon_data(msg, &settings->beacon_csa);
+	if (ret)
+		goto error;
+
+	if (nla_put(msg, NL80211_ATTR_CSA_C_OFF_BEACON,
+		    csa_off_len * sizeof(u16),
+		    settings->counter_offset_beacon) ||
+	    (settings->beacon_csa.probe_resp &&
+	     nla_put(msg, NL80211_ATTR_CSA_C_OFF_PRESP,
+		     csa_off_len * sizeof(u16),
+		     settings->counter_offset_presp)))
+		goto fail;
+
+	nla_nest_end(msg, beacon_csa);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: switch_channel failed err=%d (%s)",
+			   ret, strerror(-ret));
+	}
+	return ret;
+
+fail:
+	ret = -ENOBUFS;
+error:
+	nlmsg_free(msg);
+	wpa_printf(MSG_DEBUG, "nl80211: Could not build channel switch request");
+	return ret;
+}
+
+
+#ifdef CONFIG_IEEE80211AX
+static int nl80211_switch_color(void *priv, struct cca_settings *settings)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nlattr *beacon_cca;
+	struct nl_msg *msg;
+	int ret = -ENOBUFS;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Color change request (cca_count=%u color=%d)",
+		   settings->cca_count, settings->cca_color);
+
+	if (drv->nlmode != NL80211_IFTYPE_AP)
+		return -EOPNOTSUPP;
+
+	if (!settings->beacon_cca.tail)
+		return -EINVAL;
+
+	if (settings->beacon_cca.tail_len <= settings->counter_offset_beacon ||
+	    settings->beacon_cca.tail[settings->counter_offset_beacon] !=
+	    settings->cca_count)
+		return -EINVAL;
+
+	if (settings->beacon_cca.probe_resp &&
+	    (settings->beacon_cca.probe_resp_len <=
+	     settings->counter_offset_presp ||
+	     settings->beacon_cca.probe_resp[settings->counter_offset_presp] !=
+	     settings->cca_count))
+		return -EINVAL;
+
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_COLOR_CHANGE_REQUEST);
+	if (!msg ||
+	    nla_put_u8(msg, NL80211_ATTR_COLOR_CHANGE_COUNT,
+		       settings->cca_count) ||
+	    nla_put_u8(msg, NL80211_ATTR_COLOR_CHANGE_COLOR,
+		       settings->cca_color))
+		goto error;
+
+	/* beacon_after params */
+	ret = set_beacon_data(msg, &settings->beacon_after);
+	if (ret)
+		goto error;
+
+	/* beacon_csa params */
+	beacon_cca = nla_nest_start(msg, NL80211_ATTR_COLOR_CHANGE_ELEMS);
+	if (!beacon_cca) {
+		ret = -ENOBUFS;
+		goto error;
+	}
+
+	ret = set_beacon_data(msg, &settings->beacon_cca);
+	if (ret)
+		goto error;
+
+	if (nla_put_u16(msg, NL80211_ATTR_CNTDWN_OFFS_BEACON,
+			settings->counter_offset_beacon) ||
+	    (settings->beacon_cca.probe_resp &&
+	     nla_put_u16(msg, NL80211_ATTR_CNTDWN_OFFS_PRESP,
+			 settings->counter_offset_presp))) {
+		ret = -ENOBUFS;
+		goto error;
+	}
+
+	nla_nest_end(msg, beacon_cca);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: switch_color failed err=%d (%s)",
+			   ret, strerror(-ret));
+	}
+	return ret;
+
+error:
+	nlmsg_free(msg);
+	wpa_printf(MSG_DEBUG, "nl80211: Could not build color switch request");
+	return ret;
+}
+#endif /* CONFIG_IEEE80211AX */
+
+
+static int nl80211_add_ts(void *priv, u8 tsid, const u8 *addr,
+			  u8 user_priority, u16 admitted_time)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: add_ts request: tsid=%u admitted_time=%u up=%d",
+		   tsid, admitted_time, user_priority);
+
+	if (!is_sta_interface(drv->nlmode))
+		return -ENOTSUP;
+
+	msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_ADD_TX_TS);
+	if (!msg ||
+	    nla_put_u8(msg, NL80211_ATTR_TSID, tsid) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+	    nla_put_u8(msg, NL80211_ATTR_USER_PRIO, user_priority) ||
+	    nla_put_u16(msg, NL80211_ATTR_ADMITTED_TIME, admitted_time)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret)
+		wpa_printf(MSG_DEBUG, "nl80211: add_ts failed err=%d (%s)",
+			   ret, strerror(-ret));
+	return ret;
+}
+
+
+static int nl80211_del_ts(void *priv, u8 tsid, const u8 *addr)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "nl80211: del_ts request: tsid=%u", tsid);
+
+	if (!is_sta_interface(drv->nlmode))
+		return -ENOTSUP;
+
+	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_DEL_TX_TS)) ||
+	    nla_put_u8(msg, NL80211_ATTR_TSID, tsid) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret)
+		wpa_printf(MSG_DEBUG, "nl80211: del_ts failed err=%d (%s)",
+			   ret, strerror(-ret));
+	return ret;
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+static int cmd_reply_handler(struct nl_msg *msg, void *arg)
+{
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct wpabuf *buf = arg;
+
+	if (!buf)
+		return NL_SKIP;
+
+	if ((size_t) genlmsg_attrlen(gnlh, 0) > wpabuf_tailroom(buf)) {
+		wpa_printf(MSG_INFO, "nl80211: insufficient buffer space for reply");
+		return NL_SKIP;
+	}
+
+	wpabuf_put_data(buf, genlmsg_attrdata(gnlh, 0),
+			genlmsg_attrlen(gnlh, 0));
+
+	return NL_SKIP;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+static int vendor_reply_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct nlattr *nl_vendor_reply, *nl;
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct wpabuf *buf = arg;
+	int rem;
+
+	if (!buf)
+		return NL_SKIP;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+	nl_vendor_reply = tb[NL80211_ATTR_VENDOR_DATA];
+
+	if (!nl_vendor_reply)
+		return NL_SKIP;
+
+	if ((size_t) nla_len(nl_vendor_reply) > wpabuf_tailroom(buf)) {
+		wpa_printf(MSG_INFO, "nl80211: Vendor command: insufficient buffer space for reply");
+		return NL_SKIP;
+	}
+
+	nla_for_each_nested(nl, nl_vendor_reply, rem) {
+		wpabuf_put_data(buf, nla_data(nl), nla_len(nl));
+	}
+
+	return NL_SKIP;
+}
+
+
+static bool is_cmd_with_nested_attrs(unsigned int vendor_id,
+				     unsigned int subcmd)
+{
+	if (vendor_id != OUI_QCA)
+		return true;
+
+	switch (subcmd) {
+	case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY:
+	case QCA_NL80211_VENDOR_SUBCMD_STATS_EXT:
+	case QCA_NL80211_VENDOR_SUBCMD_SCANNING_MAC_OUI:
+	case QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY:
+	case QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_STATUS:
+	case QCA_NL80211_VENDOR_SUBCMD_NAN:
+		return false;
+	default:
+		return true;
+	}
+}
+
+
+static int nl80211_vendor_cmd(void *priv, unsigned int vendor_id,
+			      unsigned int subcmd, const u8 *data,
+			      size_t data_len, enum nested_attr nested_attr,
+			      struct wpabuf *buf)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret, nla_flag;
+
+#ifdef CONFIG_TESTING_OPTIONS
+	if (vendor_id == 0xffffffff) {
+		msg = nlmsg_alloc();
+		if (!msg)
+			return -ENOMEM;
+
+		nl80211_cmd(drv, msg, 0, subcmd);
+		if (nlmsg_append(msg, (void *) data, data_len, NLMSG_ALIGNTO) <
+		    0)
+			goto fail;
+		/* This test vendor_cmd can be used with nl80211 commands that
+		 * need the connect nl_sock, so use the owner-setting variant
+		 * of send_and_recv_msgs(). */
+		ret = send_and_recv_msgs_owner(drv, msg,
+					       get_connect_handle(bss), 0,
+					       cmd_reply_handler, buf,
+					       NULL, NULL);
+		if (ret)
+			wpa_printf(MSG_DEBUG, "nl80211: command failed err=%d",
+				   ret);
+		return ret;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	if (nested_attr == NESTED_ATTR_USED)
+		nla_flag = NLA_F_NESTED;
+	else if (nested_attr == NESTED_ATTR_UNSPECIFIED &&
+		 is_cmd_with_nested_attrs(vendor_id, subcmd))
+		nla_flag = NLA_F_NESTED;
+	else
+		nla_flag = 0;
+
+	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, vendor_id) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, subcmd) ||
+	    (data &&
+	     nla_put(msg, nla_flag | NL80211_ATTR_VENDOR_DATA,
+		     data_len, data)))
+		goto fail;
+
+	ret = send_and_recv_msgs(drv, msg, vendor_reply_handler, buf,
+				 NULL, NULL);
+	if (ret)
+		wpa_printf(MSG_DEBUG, "nl80211: vendor command failed err=%d",
+			   ret);
+	return ret;
+
+fail:
+	nlmsg_free(msg);
+	return -ENOBUFS;
+}
+
+
+static int nl80211_set_qos_map(void *priv, const u8 *qos_map_set,
+			       u8 qos_map_set_len)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	wpa_hexdump(MSG_DEBUG, "nl80211: Setting QoS Map",
+		    qos_map_set, qos_map_set_len);
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_QOS_MAP)) ||
+	    nla_put(msg, NL80211_ATTR_QOS_MAP, qos_map_set_len, qos_map_set)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret)
+		wpa_printf(MSG_DEBUG, "nl80211: Setting QoS Map failed");
+
+	return ret;
+}
+
+
+static int get_wowlan_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	int *wowlan_enabled = arg;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	*wowlan_enabled = !!tb[NL80211_ATTR_WOWLAN_TRIGGERS];
+
+	return NL_SKIP;
+}
+
+
+static int nl80211_get_wowlan(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int wowlan_enabled;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Getting wowlan status");
+
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_WOWLAN);
+
+	ret = send_and_recv_msgs(drv, msg, get_wowlan_handler, &wowlan_enabled,
+				 NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Getting wowlan status failed");
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: wowlan is %s",
+		   wowlan_enabled ? "enabled" : "disabled");
+
+	return wowlan_enabled;
+}
+
+
+static int nl80211_set_wowlan(void *priv,
+			      const struct wowlan_triggers *triggers)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *wowlan_triggers;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Setting wowlan");
+
+	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_SET_WOWLAN)) ||
+	    !(wowlan_triggers = nla_nest_start(msg,
+					       NL80211_ATTR_WOWLAN_TRIGGERS)) ||
+	    (triggers->any &&
+	     nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
+	    (triggers->disconnect &&
+	     nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
+	    (triggers->magic_pkt &&
+	     nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
+	    (triggers->gtk_rekey_failure &&
+	     nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
+	    (triggers->eap_identity_req &&
+	     nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
+	    (triggers->four_way_handshake &&
+	     nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
+	    (triggers->rfkill_release &&
+	     nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE))) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	nla_nest_end(msg, wowlan_triggers);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret)
+		wpa_printf(MSG_DEBUG, "nl80211: Setting wowlan failed");
+
+	return ret;
+}
+
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+static int nl80211_roaming(void *priv, int allowed, const u8 *bssid)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *params;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Roaming policy: allowed=%d", allowed);
+
+	if (!drv->roaming_vendor_cmd_avail) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Ignore roaming policy change since driver does not provide command for setting it");
+		return -1;
+	}
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_ROAMING) ||
+	    !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+	    nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY,
+			allowed ? QCA_ROAMING_ALLOWED_WITHIN_ESS :
+			QCA_ROAMING_NOT_ALLOWED) ||
+	    (bssid &&
+	     nla_put(msg, QCA_WLAN_VENDOR_ATTR_MAC_ADDR, ETH_ALEN, bssid))) {
+		nlmsg_free(msg);
+		return -1;
+	}
+	nla_nest_end(msg, params);
+
+	return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+}
+
+
+static int nl80211_disable_fils(void *priv, int disable)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *params;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Disable FILS=%d", disable);
+
+	if (!drv->set_wifi_conf_vendor_cmd_avail)
+		return -1;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION) ||
+	    !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+	    nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_CONFIG_DISABLE_FILS,
+		       disable)) {
+		nlmsg_free(msg);
+		return -1;
+	}
+	nla_nest_end(msg, params);
+
+	return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+}
+
+
+/* Reserved QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID value for wpa_supplicant */
+#define WPA_SUPPLICANT_CLIENT_ID 1
+
+static int nl80211_set_bssid_tmp_disallow(void *priv, unsigned int num_bssid,
+					  const u8 *bssid)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *params, *nlbssids, *attr;
+	unsigned int i;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Set temporarily disallowed BSSIDs (num=%u)",
+		   num_bssid);
+
+	if (!drv->roam_vendor_cmd_avail)
+		return -1;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_ROAM) ||
+	    !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+	    nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD,
+			QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BLACKLIST_BSSID) ||
+	    nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID,
+			WPA_SUPPLICANT_CLIENT_ID) ||
+	    nla_put_u32(msg,
+			QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID,
+			num_bssid))
+		goto fail;
+
+	nlbssids = nla_nest_start(
+		msg, QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS);
+	if (!nlbssids)
+		goto fail;
+
+	for (i = 0; i < num_bssid; i++) {
+		attr = nla_nest_start(msg, i);
+		if (!attr)
+			goto fail;
+		if (nla_put(msg,
+			    QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID,
+			    ETH_ALEN, &bssid[i * ETH_ALEN]))
+			goto fail;
+		wpa_printf(MSG_DEBUG, "nl80211:   BSSID[%u]: " MACSTR, i,
+			   MAC2STR(&bssid[i * ETH_ALEN]));
+		nla_nest_end(msg, attr);
+	}
+	nla_nest_end(msg, nlbssids);
+	nla_nest_end(msg, params);
+
+	return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+
+fail:
+	nlmsg_free(msg);
+	return -1;
+}
+
+
+static int nl80211_add_sta_node(void *priv, const u8 *addr, u16 auth_alg)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *params;
+
+	if (!drv->add_sta_node_vendor_cmd_avail)
+		return -EOPNOTSUPP;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Add STA node");
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_ADD_STA_NODE) ||
+	    !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+	    (addr &&
+	     nla_put(msg, QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_MAC_ADDR, ETH_ALEN,
+		     addr)) ||
+	    nla_put_u16(msg, QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_AUTH_ALGO,
+			auth_alg)) {
+		nlmsg_free(msg);
+		wpa_printf(MSG_ERROR,
+			   "%s: err in adding vendor_cmd and vendor_data",
+			   __func__);
+		return -1;
+	}
+	nla_nest_end(msg, params);
+
+	return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+}
+
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
+static int nl80211_set_mac_addr(void *priv, const u8 *addr)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int new_addr = addr != NULL;
+
+	if (TEST_FAIL())
+		return -1;
+
+	if (!addr)
+		addr = drv->perm_addr;
+
+	if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0) < 0)
+		return -1;
+
+	if (linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname, addr) < 0)
+	{
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: failed to set_mac_addr for %s to " MACSTR,
+			   bss->ifname, MAC2STR(addr));
+		if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname,
+					  1) < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Could not restore interface UP after failed set_mac_addr");
+		}
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: set_mac_addr for %s to " MACSTR,
+		   bss->ifname, MAC2STR(addr));
+	drv->addr_changed = new_addr;
+	os_memcpy(bss->prev_addr, bss->addr, ETH_ALEN);
+	os_memcpy(bss->addr, addr, ETH_ALEN);
+
+	if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1) < 0)
+	{
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Could not restore interface UP after set_mac_addr");
+	}
+
+	return 0;
+}
+
+
+#ifdef CONFIG_MESH
+
+static int wpa_driver_nl80211_init_mesh(void *priv)
+{
+	if (wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_MESH_POINT)) {
+		wpa_printf(MSG_INFO,
+			   "nl80211: Failed to set interface into mesh mode");
+		return -1;
+	}
+	return 0;
+}
+
+
+static int nl80211_put_mesh_id(struct nl_msg *msg, const u8 *mesh_id,
+			       size_t mesh_id_len)
+{
+	if (mesh_id) {
+		wpa_printf(MSG_DEBUG, "  * Mesh ID (SSID)=%s",
+			   wpa_ssid_txt(mesh_id, mesh_id_len));
+		return nla_put(msg, NL80211_ATTR_MESH_ID, mesh_id_len, mesh_id);
+	}
+
+	return 0;
+}
+
+
+static int nl80211_put_mcast_rate(struct nl_msg *msg, int mcast_rate)
+{
+	if (mcast_rate > 0) {
+		wpa_printf(MSG_DEBUG, "  * mcast_rate=%.1f",
+			   (double)mcast_rate / 10);
+		return nla_put_u32(msg, NL80211_ATTR_MCAST_RATE, mcast_rate);
+	}
+
+	return 0;
+}
+
+
+static int nl80211_put_mesh_config(struct nl_msg *msg,
+				   struct wpa_driver_mesh_bss_params *params)
+{
+	struct nlattr *container;
+
+	container = nla_nest_start(msg, NL80211_ATTR_MESH_CONFIG);
+	if (!container)
+		return -1;
+
+	if (((params->flags & WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS) &&
+	     nla_put_u8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
+			params->auto_plinks)) ||
+	    ((params->flags & WPA_DRIVER_MESH_CONF_FLAG_FORWARDING) &&
+	     nla_put_u8(msg, NL80211_MESHCONF_FORWARDING,
+			params->forwarding)) ||
+	    ((params->flags & WPA_DRIVER_MESH_CONF_FLAG_MAX_PEER_LINKS) &&
+	     nla_put_u16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
+			 params->max_peer_links)) ||
+	    ((params->flags & WPA_DRIVER_MESH_CONF_FLAG_RSSI_THRESHOLD) &&
+	     nla_put_u32(msg, NL80211_MESHCONF_RSSI_THRESHOLD,
+			 params->rssi_threshold)))
+		return -1;
+
+	/*
+	 * Set NL80211_MESHCONF_PLINK_TIMEOUT even if user mpm is used because
+	 * the timer could disconnect stations even in that case.
+	 */
+	if ((params->flags & WPA_DRIVER_MESH_CONF_FLAG_PEER_LINK_TIMEOUT) &&
+	    nla_put_u32(msg, NL80211_MESHCONF_PLINK_TIMEOUT,
+			params->peer_link_timeout)) {
+		wpa_printf(MSG_ERROR, "nl80211: Failed to set PLINK_TIMEOUT");
+		return -1;
+	}
+
+	if ((params->flags & WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE) &&
+	    nla_put_u16(msg, NL80211_MESHCONF_HT_OPMODE, params->ht_opmode)) {
+		wpa_printf(MSG_ERROR, "nl80211: Failed to set HT_OP_MODE");
+		return -1;
+	}
+
+	nla_nest_end(msg, container);
+
+	return 0;
+}
+
+
+static int nl80211_join_mesh(struct i802_bss *bss,
+			     struct wpa_driver_mesh_join_params *params)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *container;
+	int ret = -1;
+
+	wpa_printf(MSG_DEBUG, "nl80211: mesh join (ifindex=%d)", drv->ifindex);
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_JOIN_MESH);
+	if (!msg ||
+	    nl80211_put_freq_params(msg, &params->freq) ||
+	    nl80211_put_basic_rates(msg, params->basic_rates) ||
+	    nl80211_put_mesh_id(msg, params->meshid, params->meshid_len) ||
+	    nl80211_put_beacon_int(msg, params->beacon_int) ||
+	    nl80211_put_mcast_rate(msg, params->mcast_rate) ||
+	    nl80211_put_dtim_period(msg, params->dtim_period))
+		goto fail;
+
+	wpa_printf(MSG_DEBUG, "  * flags=%08X", params->flags);
+
+	if (params->handle_dfs && nla_put_flag(msg, NL80211_ATTR_HANDLE_DFS))
+		goto fail;
+
+	container = nla_nest_start(msg, NL80211_ATTR_MESH_SETUP);
+	if (!container)
+		goto fail;
+
+	if (params->ies) {
+		wpa_hexdump(MSG_DEBUG, "  * IEs", params->ies, params->ie_len);
+		if (nla_put(msg, NL80211_MESH_SETUP_IE, params->ie_len,
+			    params->ies))
+			goto fail;
+	}
+	/* WPA_DRIVER_MESH_FLAG_OPEN_AUTH is treated as default by nl80211 */
+	if (params->flags & WPA_DRIVER_MESH_FLAG_SAE_AUTH) {
+		if (nla_put_u8(msg, NL80211_MESH_SETUP_AUTH_PROTOCOL, 0x1) ||
+		    nla_put_flag(msg, NL80211_MESH_SETUP_USERSPACE_AUTH))
+			goto fail;
+	}
+	if ((params->flags & WPA_DRIVER_MESH_FLAG_AMPE) &&
+	    nla_put_flag(msg, NL80211_MESH_SETUP_USERSPACE_AMPE))
+		goto fail;
+	if ((params->flags & WPA_DRIVER_MESH_FLAG_USER_MPM) &&
+	    nla_put_flag(msg, NL80211_MESH_SETUP_USERSPACE_MPM))
+		goto fail;
+	nla_nest_end(msg, container);
+
+	params->conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS;
+	params->conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_PEER_LINK_TIMEOUT;
+	params->conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_MAX_PEER_LINKS;
+	if (nl80211_put_mesh_config(msg, &params->conf) < 0)
+		goto fail;
+
+	ret = send_and_recv_msgs_connect_handle(drv, msg, bss, 1);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: mesh join failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+		goto fail;
+	}
+	ret = 0;
+	drv->assoc_freq = bss->flink->freq = params->freq.freq;
+	wpa_printf(MSG_DEBUG, "nl80211: mesh join request send successfully");
+
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+static int
+wpa_driver_nl80211_join_mesh(void *priv,
+			     struct wpa_driver_mesh_join_params *params)
+{
+	struct i802_bss *bss = priv;
+	int ret, timeout;
+
+	timeout = params->conf.peer_link_timeout;
+
+	/* Disable kernel inactivity timer */
+	if (params->flags & WPA_DRIVER_MESH_FLAG_USER_MPM)
+		params->conf.peer_link_timeout = 0;
+
+	ret = nl80211_join_mesh(bss, params);
+	if (ret == -EINVAL && params->conf.peer_link_timeout == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Mesh join retry for peer_link_timeout");
+		/*
+		 * Old kernel does not support setting
+		 * NL80211_MESHCONF_PLINK_TIMEOUT to zero, so set 60 seconds
+		 * into future from peer_link_timeout.
+		 */
+		params->conf.peer_link_timeout = timeout + 60;
+		ret = nl80211_join_mesh(priv, params);
+	}
+
+	params->conf.peer_link_timeout = timeout;
+	return ret;
+}
+
+
+static int wpa_driver_nl80211_leave_mesh(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "nl80211: mesh leave (ifindex=%d)", drv->ifindex);
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_MESH);
+	ret = send_and_recv_msgs_connect_handle(drv, msg, bss, 0);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: mesh leave failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: mesh leave request send successfully");
+		drv->first_bss->flink->freq = 0;
+	}
+
+	if (drv->start_mode_sta &&
+	    wpa_driver_nl80211_set_mode(drv->first_bss,
+					NL80211_IFTYPE_STATION)) {
+		wpa_printf(MSG_INFO,
+			   "nl80211: Failed to set interface into station mode");
+	}
+	return ret;
+}
+
+
+static int nl80211_probe_mesh_link(void *priv, const u8 *addr, const u8 *eth,
+				   size_t len)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_PROBE_MESH_LINK);
+	if (!msg ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+	    nla_put(msg, NL80211_ATTR_FRAME, len, eth)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: mesh link probe to " MACSTR
+			   " failed: ret=%d (%s)",
+			   MAC2STR(addr), ret, strerror(-ret));
+	} else {
+		wpa_printf(MSG_DEBUG, "nl80211: Mesh link to " MACSTR
+			   " probed successfully", MAC2STR(addr));
+	}
+
+	return ret;
+}
+
+#endif /* CONFIG_MESH */
+
+
+static int wpa_driver_br_add_ip_neigh(void *priv, u8 version,
+				      const u8 *ipaddr, int prefixlen,
+				      const u8 *addr)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct ndmsg nhdr = {
+		.ndm_state = NUD_PERMANENT,
+		.ndm_ifindex = bss->br_ifindex,
+	};
+	struct nl_msg *msg;
+	int addrsize;
+	int res;
+
+	if (!ipaddr || prefixlen == 0 || !addr)
+		return -EINVAL;
+
+	if (bss->br_ifindex == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: bridge must be set before adding an ip neigh to it");
+		return -1;
+	}
+
+	if (!drv->rtnl_sk) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: nl_sock for NETLINK_ROUTE is not initialized");
+		return -1;
+	}
+
+	if (version == 4) {
+		nhdr.ndm_family = AF_INET;
+		addrsize = 4;
+	} else if (version == 6) {
+		nhdr.ndm_family = AF_INET6;
+		addrsize = 16;
+	} else {
+		return -EINVAL;
+	}
+
+	msg = nlmsg_alloc_simple(RTM_NEWNEIGH, NLM_F_CREATE);
+	if (!msg)
+		return -ENOMEM;
+
+	res = -ENOMEM;
+	if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0)
+		goto errout;
+
+	if (nla_put(msg, NDA_DST, addrsize, (void *)ipaddr))
+		goto errout;
+
+	if (nla_put(msg, NDA_LLADDR, ETH_ALEN, (void *)addr))
+		goto errout;
+
+	res = nl_send_auto_complete(drv->rtnl_sk, msg);
+	if (res < 0)
+		goto errout;
+
+	res = nl_wait_for_ack(drv->rtnl_sk);
+	if (res) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Adding bridge ip neigh failed: %s",
+			   nl_geterror(res));
+	}
+errout:
+	nlmsg_free(msg);
+	return res;
+}
+
+
+static int wpa_driver_br_delete_ip_neigh(void *priv, u8 version,
+					 const u8 *ipaddr)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct ndmsg nhdr = {
+		.ndm_state = NUD_PERMANENT,
+		.ndm_ifindex = bss->br_ifindex,
+	};
+	struct nl_msg *msg;
+	int addrsize;
+	int res;
+
+	if (!ipaddr)
+		return -EINVAL;
+
+	if (version == 4) {
+		nhdr.ndm_family = AF_INET;
+		addrsize = 4;
+	} else if (version == 6) {
+		nhdr.ndm_family = AF_INET6;
+		addrsize = 16;
+	} else {
+		return -EINVAL;
+	}
+
+	if (bss->br_ifindex == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: bridge must be set to delete an ip neigh");
+		return -1;
+	}
+
+	if (!drv->rtnl_sk) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: nl_sock for NETLINK_ROUTE is not initialized");
+		return -1;
+	}
+
+	msg = nlmsg_alloc_simple(RTM_DELNEIGH, NLM_F_CREATE);
+	if (!msg)
+		return -ENOMEM;
+
+	res = -ENOMEM;
+	if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0)
+		goto errout;
+
+	if (nla_put(msg, NDA_DST, addrsize, (void *)ipaddr))
+		goto errout;
+
+	res = nl_send_auto_complete(drv->rtnl_sk, msg);
+	if (res < 0)
+		goto errout;
+
+	res = nl_wait_for_ack(drv->rtnl_sk);
+	if (res) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Deleting bridge ip neigh failed: %s",
+			   nl_geterror(res));
+	}
+errout:
+	nlmsg_free(msg);
+	return res;
+}
+
+
+static int linux_write_system_file(const char *path, unsigned int val)
+{
+	char buf[50];
+	int fd, len;
+
+	len = os_snprintf(buf, sizeof(buf), "%u\n", val);
+	if (os_snprintf_error(sizeof(buf), len))
+		return -1;
+
+	fd = open(path, O_WRONLY);
+	if (fd < 0)
+		return -1;
+
+	if (write(fd, buf, len) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Failed to write Linux system file: %s with the value of %d",
+			   path, val);
+		close(fd);
+		return -1;
+	}
+	close(fd);
+
+	return 0;
+}
+
+
+static const char * drv_br_port_attr_str(enum drv_br_port_attr attr)
+{
+	switch (attr) {
+	case DRV_BR_PORT_ATTR_PROXYARP:
+		return "proxyarp_wifi";
+	case DRV_BR_PORT_ATTR_HAIRPIN_MODE:
+		return "hairpin_mode";
+	case DRV_BR_PORT_ATTR_MCAST2UCAST:
+		return "multicast_to_unicast";
+	}
+
+	return NULL;
+}
+
+
+static int wpa_driver_br_port_set_attr(void *priv, enum drv_br_port_attr attr,
+				       unsigned int val)
+{
+	struct i802_bss *bss = priv;
+	char path[128];
+	const char *attr_txt;
+
+	attr_txt = drv_br_port_attr_str(attr);
+	if (attr_txt == NULL)
+		return -EINVAL;
+
+	os_snprintf(path, sizeof(path), "/sys/class/net/%s/brport/%s",
+		    bss->ifname, attr_txt);
+
+	if (linux_write_system_file(path, val))
+		return -1;
+
+	return 0;
+}
+
+
+static const char * drv_br_net_param_str(enum drv_br_net_param param)
+{
+	switch (param) {
+	case DRV_BR_NET_PARAM_GARP_ACCEPT:
+		return "arp_accept";
+	default:
+		return NULL;
+	}
+}
+
+
+static int wpa_driver_br_set_net_param(void *priv, enum drv_br_net_param param,
+				       unsigned int val)
+{
+	struct i802_bss *bss = priv;
+	char path[128];
+	const char *param_txt;
+	int ip_version = 4;
+
+	if (param == DRV_BR_MULTICAST_SNOOPING) {
+		os_snprintf(path, sizeof(path),
+			    "/sys/devices/virtual/net/%s/bridge/multicast_snooping",
+			    bss->brname);
+		goto set_val;
+	}
+
+	param_txt = drv_br_net_param_str(param);
+	if (param_txt == NULL)
+		return -EINVAL;
+
+	switch (param) {
+		case DRV_BR_NET_PARAM_GARP_ACCEPT:
+			ip_version = 4;
+			break;
+		default:
+			return -EINVAL;
+	}
+
+	os_snprintf(path, sizeof(path), "/proc/sys/net/ipv%d/conf/%s/%s",
+		    ip_version, bss->brname, param_txt);
+
+set_val:
+	if (linux_write_system_file(path, val))
+		return -1;
+
+	return 0;
+}
+
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+
+static int hw_mode_to_qca_acs(enum hostapd_hw_mode hw_mode)
+{
+	switch (hw_mode) {
+	case HOSTAPD_MODE_IEEE80211B:
+		return QCA_ACS_MODE_IEEE80211B;
+	case HOSTAPD_MODE_IEEE80211G:
+		return QCA_ACS_MODE_IEEE80211G;
+	case HOSTAPD_MODE_IEEE80211A:
+		return QCA_ACS_MODE_IEEE80211A;
+	case HOSTAPD_MODE_IEEE80211AD:
+		return QCA_ACS_MODE_IEEE80211AD;
+	case HOSTAPD_MODE_IEEE80211ANY:
+		return QCA_ACS_MODE_IEEE80211ANY;
+	default:
+		return -1;
+	}
+}
+
+
+static int add_acs_ch_list(struct nl_msg *msg, const int *freq_list)
+{
+	int num_channels = 0, num_freqs;
+	u8 *ch_list;
+	enum hostapd_hw_mode hw_mode;
+	int ret = 0;
+	int i;
+
+	if (!freq_list)
+		return 0;
+
+	num_freqs = int_array_len(freq_list);
+	ch_list = os_malloc(sizeof(u8) * num_freqs);
+	if (!ch_list)
+		return -1;
+
+	for (i = 0; i < num_freqs; i++) {
+		const int freq = freq_list[i];
+
+		if (freq == 0)
+			break;
+		/* Send 2.4 GHz and 5 GHz channels with
+		 * QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST to maintain backwards
+		 * compatibility.
+		 */
+		if (!(freq >= 2412 && freq <= 2484) &&
+		    !(freq >= 5180 && freq <= 5900) &&
+		    !(freq >= 5945 && freq <= 7115))
+			continue;
+		hw_mode = ieee80211_freq_to_chan(freq, &ch_list[num_channels]);
+		if (hw_mode != NUM_HOSTAPD_MODES)
+			num_channels++;
+	}
+
+	if (num_channels)
+		ret = nla_put(msg, QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST,
+			      num_channels, ch_list);
+
+	os_free(ch_list);
+	return ret;
+}
+
+
+static int add_acs_freq_list(struct nl_msg *msg, const int *freq_list)
+{
+	int i, len, ret;
+	u32 *freqs;
+
+	if (!freq_list)
+		return 0;
+	len = int_array_len(freq_list);
+	freqs = os_malloc(sizeof(u32) * len);
+	if (!freqs)
+		return -1;
+	for (i = 0; i < len; i++)
+		freqs[i] = freq_list[i];
+	ret = nla_put(msg, QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST,
+		      sizeof(u32) * len, freqs);
+	os_free(freqs);
+	return ret;
+}
+
+
+static int nl80211_qca_do_acs(struct wpa_driver_nl80211_data *drv,
+			      struct drv_acs_params *params)
+{
+	struct nl_msg *msg;
+	struct nlattr *data;
+	int ret;
+	int mode;
+
+	mode = hw_mode_to_qca_acs(params->hw_mode);
+	if (mode < 0)
+		return -1;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_DO_ACS) ||
+	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+	    nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE, mode) ||
+	    (params->ht_enabled &&
+	     nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED)) ||
+	    (params->ht40_enabled &&
+	     nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED)) ||
+	    (params->vht_enabled &&
+	     nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED)) ||
+	    (params->eht_enabled &&
+	     nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_EHT_ENABLED)) ||
+	    nla_put_u16(msg, QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH,
+			params->ch_width) ||
+	    add_acs_ch_list(msg, params->freq_list) ||
+	    add_acs_freq_list(msg, params->freq_list) ||
+	    (params->edmg_enabled &&
+	     nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_EDMG_ENABLED))) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+	nla_nest_end(msg, data);
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: ACS Params: HW_MODE: %d HT: %d HT40: %d VHT: %d EHT: %d BW: %d EDMG: %d",
+		   params->hw_mode, params->ht_enabled, params->ht40_enabled,
+		   params->vht_enabled, params->eht_enabled, params->ch_width,
+		   params->edmg_enabled);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Failed to invoke driver ACS function: %s",
+			   strerror(-ret));
+	}
+	return ret;
+}
+
+
+static int nl80211_set_band(void *priv, u32 band_mask)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *data;
+	int ret;
+	enum qca_set_band qca_band_value;
+	u32 qca_band_mask = QCA_SETBAND_AUTO;
+
+	if (!drv->setband_vendor_cmd_avail ||
+	    (band_mask > (WPA_SETBAND_2G | WPA_SETBAND_5G | WPA_SETBAND_6G)))
+		return -1;
+
+	if (band_mask & WPA_SETBAND_5G)
+		qca_band_mask |= QCA_SETBAND_5G;
+	if (band_mask & WPA_SETBAND_2G)
+		qca_band_mask |= QCA_SETBAND_2G;
+	if (band_mask & WPA_SETBAND_6G)
+		qca_band_mask |= QCA_SETBAND_6G;
+
+	/*
+	 * QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE is a legacy interface hence make
+	 * it suite to its values (AUTO/5G/2G) for backwards compatibility.
+	 */
+	qca_band_value = ((qca_band_mask & QCA_SETBAND_5G) &&
+			  (qca_band_mask & QCA_SETBAND_2G)) ?
+				QCA_SETBAND_AUTO :
+				qca_band_mask & ~QCA_SETBAND_6G;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: QCA_BAND_MASK = 0x%x, QCA_BAND_VALUE = %d",
+		   qca_band_mask, qca_band_value);
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_SETBAND) ||
+	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+	    nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE,
+			qca_band_value) ||
+	    nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SETBAND_MASK,
+			qca_band_mask)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+	nla_nest_end(msg, data);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Driver setband function failed: %s",
+			   strerror(-ret));
+	}
+	return ret;
+}
+
+
+struct nl80211_pcl {
+	unsigned int num;
+	struct weighted_pcl *freq_list;
+};
+
+static void get_pcl_attr_values(struct weighted_pcl *wpcl, struct nlattr *nl[])
+{
+	if (nl[QCA_WLAN_VENDOR_ATTR_PCL_FREQ])
+		wpcl->freq = nla_get_u32(nl[QCA_WLAN_VENDOR_ATTR_PCL_FREQ]);
+	if (nl[QCA_WLAN_VENDOR_ATTR_PCL_WEIGHT])
+		wpcl->weight = nla_get_u8(nl[QCA_WLAN_VENDOR_ATTR_PCL_WEIGHT]);
+	if (nl[QCA_WLAN_VENDOR_ATTR_PCL_FLAG]) {
+		u32 flags = nla_get_u32(nl[QCA_WLAN_VENDOR_ATTR_PCL_FLAG]);
+
+		wpcl->flag = 0;
+		if (flags & BIT(0))
+			wpcl->flag |= WEIGHTED_PCL_GO;
+		if (flags & BIT(1))
+			wpcl->flag |= WEIGHTED_PCL_CLI;
+		if (flags & BIT(2))
+			wpcl->flag |= WEIGHTED_PCL_MUST_CONSIDER;
+		if (flags & BIT(3))
+			wpcl->flag |= WEIGHTED_PCL_EXCLUDE;
+	} else {
+		wpcl->flag = WEIGHTED_PCL_GO | WEIGHTED_PCL_CLI;
+	}
+}
+
+
+static int preferred_freq_info_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nl80211_pcl *param = arg;
+	struct nlattr *nl_vend, *attr;
+	enum qca_iface_type iface_type;
+	struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+	struct nlattr *nl_pcl[QCA_WLAN_VENDOR_ATTR_PCL_MAX + 1];
+	unsigned int num, max_num;
+	u32 *freqs;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
+	if (!nl_vend)
+		return NL_SKIP;
+
+	nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX,
+		  nla_data(nl_vend), nla_len(nl_vend), NULL);
+
+	attr = tb_vendor[
+		QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE];
+	if (!attr) {
+		wpa_printf(MSG_ERROR, "nl80211: iface_type couldn't be found");
+		param->num = 0;
+		return NL_SKIP;
+	}
+
+	iface_type = (enum qca_iface_type) nla_get_u32(attr);
+	wpa_printf(MSG_DEBUG, "nl80211: Driver returned iface_type=%d",
+		   iface_type);
+
+	attr = tb_vendor[
+		QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_WEIGHED_PCL];
+	if (attr) {
+		int rem;
+		struct nlattr *wpcl = attr;
+		unsigned int i;
+
+		num = 0;
+		nla_for_each_nested(attr, wpcl, rem) {
+			if (num == param->num)
+				break; /* not enough room for all entries */
+			if (nla_parse(nl_pcl, QCA_WLAN_VENDOR_ATTR_PCL_MAX,
+				      nla_data(attr), nla_len(attr), NULL)) {
+				wpa_printf(MSG_ERROR,
+					   "nl80211: Failed to parse PCL info");
+				param->num = 0;
+				return NL_SKIP;
+			}
+			get_pcl_attr_values(&param->freq_list[num], nl_pcl);
+			num++;
+		}
+		param->num = num;
+
+		/* Sort frequencies based on their weight */
+		for (i = 0; i < num; i++) {
+			unsigned int j;
+
+			for (j = i + 1; j < num; j++) {
+				if (param->freq_list[i].weight <
+				    param->freq_list[j].weight) {
+					struct weighted_pcl tmp;
+
+					tmp = param->freq_list[i];
+					param->freq_list[i] =
+						param->freq_list[j];
+					param->freq_list[j] = tmp;
+				}
+			}
+		}
+	} else if (tb_vendor[QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST]) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Driver does not provide weighted PCL; use the non-weighted variant");
+		attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST];
+		/*
+		 * param->num has the maximum number of entries for which there
+		 * is room in the freq_list provided by the caller.
+		 */
+		freqs = nla_data(attr);
+		max_num = nla_len(attr) / sizeof(u32);
+		if (max_num > param->num)
+			max_num = param->num;
+		for (num = 0; num < max_num; num++) {
+			param->freq_list[num].freq = freqs[num];
+			param->freq_list[num].flag =
+				WEIGHTED_PCL_GO | WEIGHTED_PCL_CLI;
+		}
+		param->num = num;
+	} else {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: preferred_freq_list couldn't be found");
+		param->num = 0;
+		return NL_SKIP;
+	}
+	return NL_SKIP;
+}
+
+
+static int nl80211_get_pref_freq_list(void *priv,
+				      enum wpa_driver_if_type if_type,
+				      unsigned int *num,
+				      struct weighted_pcl *freq_list)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+	unsigned int i;
+	struct nlattr *params;
+	struct nl80211_pcl param;
+	enum qca_iface_type iface_type;
+
+	if (!drv->get_pref_freq_list)
+		return -1;
+
+	switch (if_type) {
+	case WPA_IF_STATION:
+		iface_type = QCA_IFACE_TYPE_STA;
+		break;
+	case WPA_IF_AP_BSS:
+		iface_type = QCA_IFACE_TYPE_AP;
+		break;
+	case WPA_IF_P2P_GO:
+		iface_type = QCA_IFACE_TYPE_P2P_GO;
+		break;
+	case WPA_IF_P2P_CLIENT:
+		iface_type = QCA_IFACE_TYPE_P2P_CLIENT;
+		break;
+	case WPA_IF_IBSS:
+		iface_type = QCA_IFACE_TYPE_IBSS;
+		break;
+	case WPA_IF_TDLS:
+		iface_type = QCA_IFACE_TYPE_TDLS;
+		break;
+	default:
+		return -1;
+	}
+
+	param.num = *num;
+	param.freq_list = freq_list;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_IFINDEX, drv->ifindex) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST) ||
+	    !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+	    nla_put_u32(msg,
+			QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE,
+			iface_type)) {
+		wpa_printf(MSG_ERROR,
+			   "%s: err in adding vendor_cmd and vendor_data",
+			   __func__);
+		nlmsg_free(msg);
+		return -1;
+	}
+	nla_nest_end(msg, params);
+
+	if (freq_list)
+		os_memset(freq_list, 0, *num * sizeof(struct weighted_pcl));
+	ret = send_and_recv_msgs(drv, msg, preferred_freq_info_handler, &param,
+				 NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_ERROR,
+			   "%s: err in send_and_recv_msgs", __func__);
+		return ret;
+	}
+
+	*num = param.num;
+
+	for (i = 0; i < *num; i++) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: preferred_channel_list[%d]=%d[%d]:0x%x",
+			   i, freq_list[i].freq, freq_list[i].weight,
+			   freq_list[i].flag);
+	}
+
+	return 0;
+}
+
+
+static int nl80211_set_prob_oper_freq(void *priv, unsigned int freq)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+	struct nlattr *params;
+
+	if (!drv->set_prob_oper_freq)
+		return -1;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Set P2P probable operating freq %u for ifindex %d",
+		   freq, bss->ifindex);
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL) ||
+	    !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+	    nla_put_u32(msg,
+			QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_IFACE_TYPE,
+			QCA_IFACE_TYPE_P2P_CLIENT) ||
+	    nla_put_u32(msg,
+			QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_FREQ,
+			freq)) {
+		wpa_printf(MSG_ERROR,
+			   "%s: err in adding vendor_cmd and vendor_data",
+			   __func__);
+		nlmsg_free(msg);
+		return -1;
+	}
+	nla_nest_end(msg, params);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_ERROR, "%s: err in send_and_recv_msgs",
+			   __func__);
+		return ret;
+	}
+	nlmsg_free(msg);
+	return 0;
+}
+
+
+static int nl80211_p2p_lo_start(void *priv, unsigned int freq,
+				unsigned int period, unsigned int interval,
+				unsigned int count, const u8 *device_types,
+				size_t dev_types_len,
+				const u8 *ies, size_t ies_len)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *container;
+	int ret;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Start P2P Listen offload: freq=%u, period=%u, interval=%u, count=%u",
+		   freq, period, interval, count);
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD))
+		return -1;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START))
+		goto fail;
+
+	container = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+	if (!container)
+		goto fail;
+
+	if (nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CHANNEL,
+			freq) ||
+	    nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_PERIOD,
+			period) ||
+	    nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_INTERVAL,
+			interval) ||
+	    nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_COUNT,
+			count) ||
+	    nla_put(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_DEVICE_TYPES,
+		    dev_types_len, device_types) ||
+	    nla_put(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_VENDOR_IE,
+		    ies_len, ies))
+		goto fail;
+
+	nla_nest_end(msg, container);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Failed to send P2P Listen offload vendor command");
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	nlmsg_free(msg);
+	return -1;
+}
+
+
+static int nl80211_p2p_lo_stop(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Stop P2P Listen offload");
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD))
+		return -1;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP)) {
+		nlmsg_free(msg);
+		return -1;
+	}
+
+	return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+}
+
+
+static int nl80211_set_tdls_mode(void *priv, int tdls_external_control)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *params;
+	int ret;
+	u32 tdls_mode;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Set TDKS mode: tdls_external_control=%d",
+		   tdls_external_control);
+
+	if (tdls_external_control == 1)
+		tdls_mode = QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT |
+			QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL;
+	else
+		tdls_mode = QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_CONFIGURE_TDLS))
+		goto fail;
+
+	params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+	if (!params)
+		goto fail;
+
+	if (nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE,
+			tdls_mode))
+		goto fail;
+
+	nla_nest_end(msg, params);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Set TDLS mode failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+		goto fail;
+	}
+	return 0;
+fail:
+	nlmsg_free(msg);
+	return -1;
+}
+
+
+#ifdef CONFIG_MBO
+
+static enum mbo_transition_reject_reason
+nl80211_mbo_reject_reason_mapping(enum qca_wlan_btm_candidate_status status)
+{
+	switch (status) {
+	case QCA_STATUS_REJECT_EXCESSIVE_FRAME_LOSS_EXPECTED:
+		return MBO_TRANSITION_REJECT_REASON_FRAME_LOSS;
+	case QCA_STATUS_REJECT_EXCESSIVE_DELAY_EXPECTED:
+		return MBO_TRANSITION_REJECT_REASON_DELAY;
+	case QCA_STATUS_REJECT_INSUFFICIENT_QOS_CAPACITY:
+		return MBO_TRANSITION_REJECT_REASON_QOS_CAPACITY;
+	case QCA_STATUS_REJECT_LOW_RSSI:
+		return MBO_TRANSITION_REJECT_REASON_RSSI;
+	case QCA_STATUS_REJECT_HIGH_INTERFERENCE:
+		return MBO_TRANSITION_REJECT_REASON_INTERFERENCE;
+	case QCA_STATUS_REJECT_UNKNOWN:
+	default:
+		return MBO_TRANSITION_REJECT_REASON_UNSPECIFIED;
+	}
+}
+
+
+static void nl80211_parse_btm_candidate_info(struct candidate_list *candidate,
+					     struct nlattr *tb[], int num)
+{
+	enum qca_wlan_btm_candidate_status status;
+	char buf[50];
+
+	os_memcpy(candidate->bssid,
+		  nla_data(tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID]),
+		  ETH_ALEN);
+
+	status = nla_get_u32(
+		tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS]);
+	candidate->is_accept = status == QCA_STATUS_ACCEPT;
+	candidate->reject_reason = nl80211_mbo_reject_reason_mapping(status);
+
+	if (candidate->is_accept)
+		os_snprintf(buf, sizeof(buf), "Accepted");
+	else
+		os_snprintf(buf, sizeof(buf),
+			    "Rejected, Reject_reason: %d",
+			    candidate->reject_reason);
+	wpa_printf(MSG_DEBUG, "nl80211:   BSSID[%d]: " MACSTR " %s",
+		   num, MAC2STR(candidate->bssid), buf);
+}
+
+
+static int
+nl80211_get_bss_transition_status_handler(struct nl_msg *msg, void *arg)
+{
+	struct wpa_bss_candidate_info *info = arg;
+	struct candidate_list *candidate = info->candidates;
+	struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+	struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX + 1];
+	static struct nla_policy policy[
+		QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX + 1] = {
+		[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID] = {
+			.minlen = ETH_ALEN
+		},
+		[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS] = {
+			.type = NLA_U32,
+		},
+	};
+	struct nlattr *attr;
+	int rem;
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	u8 num;
+
+	num = info->num; /* number of candidates sent to driver */
+	info->num = 0;
+	nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (!tb_msg[NL80211_ATTR_VENDOR_DATA] ||
+	    nla_parse_nested(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX,
+			     tb_msg[NL80211_ATTR_VENDOR_DATA], NULL) ||
+	    !tb_vendor[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO])
+		return NL_SKIP;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: WNM Candidate list received from driver");
+	nla_for_each_nested(attr,
+			    tb_vendor[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO],
+			    rem) {
+		if (info->num >= num ||
+		    nla_parse_nested(
+			    tb, QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX,
+			    attr, policy) ||
+		    !tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID] ||
+		    !tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS])
+			break;
+
+		nl80211_parse_btm_candidate_info(candidate, tb, info->num);
+
+		candidate++;
+		info->num++;
+	}
+
+	return NL_SKIP;
+}
+
+
+static struct wpa_bss_candidate_info *
+nl80211_get_bss_transition_status(void *priv, struct wpa_bss_trans_info *params)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *attr, *attr1, *attr2;
+	struct wpa_bss_candidate_info *info;
+	u8 i;
+	int ret;
+	u8 *pos;
+
+	if (!drv->fetch_bss_trans_status)
+		return NULL;
+
+	info = os_zalloc(sizeof(*info));
+	if (!info)
+		return NULL;
+	/* Allocate memory for number of candidates sent to driver */
+	info->candidates = os_calloc(params->n_candidates,
+				     sizeof(*info->candidates));
+	if (!info->candidates) {
+		os_free(info);
+		return NULL;
+	}
+
+	/* Copy the number of candidates being sent to driver. This is used in
+	 * nl80211_get_bss_transition_status_handler() to limit the number of
+	 * candidates that can be populated in info->candidates and will be
+	 * later overwritten with the actual number of candidates received from
+	 * the driver.
+	 */
+	info->num = params->n_candidates;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS))
+		goto fail;
+
+	attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+	if (!attr)
+		goto fail;
+
+	if (nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON,
+		       params->mbo_transition_reason))
+		goto fail;
+
+	attr1 = nla_nest_start(msg, QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO);
+	if (!attr1)
+		goto fail;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: WNM Candidate list info sending to driver: mbo_transition_reason: %d n_candidates: %d",
+		   params->mbo_transition_reason, params->n_candidates);
+	pos = params->bssid;
+	for (i = 0; i < params->n_candidates; i++) {
+		wpa_printf(MSG_DEBUG, "nl80211:   BSSID[%d]: " MACSTR, i,
+			   MAC2STR(pos));
+		attr2 = nla_nest_start(msg, i);
+		if (!attr2 ||
+		    nla_put(msg, QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID,
+			    ETH_ALEN, pos))
+			goto fail;
+		pos += ETH_ALEN;
+		nla_nest_end(msg, attr2);
+	}
+
+	nla_nest_end(msg, attr1);
+	nla_nest_end(msg, attr);
+
+	ret = send_and_recv_msgs(drv, msg,
+				 nl80211_get_bss_transition_status_handler,
+				 info, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: WNM Get BSS transition status failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+		goto fail;
+	}
+	return info;
+
+fail:
+	nlmsg_free(msg);
+	os_free(info->candidates);
+	os_free(info);
+	return NULL;
+}
+
+
+/**
+ * nl80211_ignore_assoc_disallow - Configure driver to ignore assoc_disallow
+ * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
+ * @ignore_assoc_disallow: 0 to not ignore, 1 to ignore
+ * Returns: 0 on success, -1 on failure
+ */
+static int nl80211_ignore_assoc_disallow(void *priv, int ignore_disallow)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *attr;
+	int ret = -1;
+
+	if (!drv->set_wifi_conf_vendor_cmd_avail)
+		return -1;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION))
+		goto fail;
+
+	attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+	if (!attr)
+		goto fail;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Set ignore_assoc_disallow %d",
+		   ignore_disallow);
+	if (nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_CONFIG_IGNORE_ASSOC_DISALLOWED,
+		       ignore_disallow))
+		goto fail;
+
+	nla_nest_end(msg, attr);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Set ignore_assoc_disallow failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+		goto fail;
+	}
+
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+
+#endif /* CONFIG_MBO */
+
+
+#ifdef CONFIG_PASN
+
+static int nl80211_send_pasn_resp(void *priv, struct pasn_auth *params)
+{
+	unsigned int i;
+	struct i802_bss *bss = priv;
+	struct nl_msg *msg = NULL;
+	struct nlattr *nlpeers, *attr, *attr1;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	wpa_dbg(drv->ctx, MSG_DEBUG,
+		"nl80211: PASN authentication response for %d entries",
+		params->num_peers);
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+	if (!msg ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_PASN))
+		goto fail;
+
+	attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+	if (!attr)
+		goto fail;
+
+	nlpeers = nla_nest_start(msg, QCA_WLAN_VENDOR_ATTR_PASN_PEERS);
+	if (!nlpeers)
+		goto fail;
+
+	for (i = 0; i < params->num_peers; i++) {
+		attr1 = nla_nest_start(msg, i);
+		if (!attr1 ||
+		    nla_put(msg, QCA_WLAN_VENDOR_ATTR_PASN_PEER_SRC_ADDR,
+			    ETH_ALEN, params->peer[i].own_addr) ||
+		    nla_put(msg, QCA_WLAN_VENDOR_ATTR_PASN_PEER_MAC_ADDR,
+			    ETH_ALEN, params->peer[i].peer_addr))
+			goto fail;
+
+		if (params->peer[i].status == 0 &&
+		    nla_put_flag(msg,
+				 QCA_WLAN_VENDOR_ATTR_PASN_PEER_STATUS_SUCCESS))
+			goto fail;
+
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Own address[%u]: " MACSTR
+			   " Peer address[%u]: " MACSTR " Status: %s",
+			   i, MAC2STR(params->peer[i].own_addr), i,
+			   MAC2STR(params->peer[i].peer_addr),
+			   params->peer[i].status ? "Fail" : "Success");
+		nla_nest_end(msg, attr1);
+	}
+
+	nla_nest_end(msg, nlpeers);
+	nla_nest_end(msg, attr);
+
+	return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+
+fail:
+	nlmsg_free(msg);
+	return -1;
+}
+
+
+static u32 wpa_ltf_keyseed_len_to_sha_type(size_t len)
+{
+	if (len == SHA384_MAC_LEN)
+		return QCA_WLAN_VENDOR_SHA_384;
+	if (len == SHA256_MAC_LEN)
+		return QCA_WLAN_VENDOR_SHA_256;
+
+	wpa_printf(MSG_ERROR, "nl80211: Unexpected LTF keyseed len %zu", len);
+	return (u32) -1;
+}
+
+
+static int nl80211_set_secure_ranging_ctx(void *priv,
+					  struct secure_ranging_params *params)
+{
+	int ret;
+	u32 suite;
+	struct nlattr *attr;
+	struct nl_msg *msg = NULL;
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	/* Configure secure ranging context only to the drivers that support it.
+	 */
+	if (!drv->secure_ranging_ctx_vendor_cmd_avail)
+		return 0;
+
+	if (!params->peer_addr || !params->own_addr)
+		return -1;
+
+	wpa_dbg(drv->ctx, MSG_DEBUG,
+		"nl80211: Secure ranging context for " MACSTR,
+		MAC2STR(params->peer_addr));
+
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+	if (!msg ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_SECURE_RANGING_CONTEXT))
+		goto fail;
+
+	attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+	if (!attr)
+		goto fail;
+
+	if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_PEER_MAC_ADDR,
+		    ETH_ALEN, params->peer_addr) ||
+	    nla_put(msg, QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_SRC_ADDR,
+		    ETH_ALEN, params->own_addr) ||
+	    nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_ACTION,
+			params->action))
+		goto fail;
+
+	if (params->cipher) {
+		suite = wpa_cipher_to_cipher_suite(params->cipher);
+		if (!suite ||
+		    nla_put_u32(msg,
+				QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_CIPHER,
+				suite))
+			goto fail;
+	}
+
+	if (params->tk_len && params->tk) {
+		if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_TK,
+			    params->tk_len, params->tk))
+			goto fail;
+		wpa_hexdump_key(MSG_DEBUG, "nl80211: TK",
+				params->tk, params->tk_len);
+	}
+
+	if (params->ltf_keyseed_len && params->ltf_keyseed) {
+		u32 sha_type = wpa_ltf_keyseed_len_to_sha_type(
+			params->ltf_keyseed_len);
+
+		if (sha_type == (u32) -1 ||
+		    nla_put_u32(
+			    msg,
+			    QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_SHA_TYPE,
+			    sha_type) ||
+		    nla_put(msg,
+			    QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_LTF_KEYSEED,
+			    params->ltf_keyseed_len, params->ltf_keyseed))
+			goto fail;
+		wpa_hexdump_key(MSG_DEBUG, "nl80211: LTF keyseed",
+				params->ltf_keyseed, params->ltf_keyseed_len);
+	}
+	nla_nest_end(msg, attr);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret)
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Set secure ranging context failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+	return ret;
+fail:
+	nlmsg_free(msg);
+	return -1;
+}
+
+#endif /* CONFIG_PASN */
+
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
+#ifdef CONFIG_DRIVER_NL80211_BRCM
+static int wpa_driver_do_broadcom_acs(struct wpa_driver_nl80211_data *drv,
+				      struct drv_acs_params *params)
+{
+	struct nl_msg *msg;
+	struct nlattr *data;
+	int freq_list_len;
+	int ret = -1;
+
+	freq_list_len = int_array_len(params->freq_list);
+	wpa_printf(MSG_DEBUG, "%s: freq_list_len=%d",
+		   __func__, freq_list_len);
+
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+	if (!msg ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_BRCM) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			BRCM_VENDOR_SCMD_ACS) ||
+	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+	    nla_put_u8(msg, BRCM_VENDOR_ATTR_ACS_HW_MODE, params->hw_mode) ||
+	    nla_put_u8(msg, BRCM_VENDOR_ATTR_ACS_HT_ENABLED,
+		       params->ht_enabled) ||
+	    nla_put_u8(msg, BRCM_VENDOR_ATTR_ACS_HT40_ENABLED,
+		       params->ht40_enabled) ||
+	    nla_put_u8(msg, BRCM_VENDOR_ATTR_ACS_VHT_ENABLED,
+		       params->vht_enabled) ||
+	    nla_put_u16(msg, BRCM_VENDOR_ATTR_ACS_CHWIDTH, params->ch_width) ||
+	    (freq_list_len > 0 &&
+	     nla_put(msg, BRCM_VENDOR_ATTR_ACS_FREQ_LIST,
+		     sizeof(int) * freq_list_len, params->freq_list)))
+		goto fail;
+	nla_nest_end(msg, data);
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: ACS Params: HW_MODE: %d HT: %d HT40: %d VHT: %d BW: %d",
+		   params->hw_mode, params->ht_enabled, params->ht40_enabled,
+		   params->vht_enabled, params->ch_width);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: BRCM Failed to invoke driver ACS function: %s",
+			   strerror(errno));
+	}
+
+	msg = NULL;
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+#endif /* CONFIG_DRIVER_NL80211_BRCM */
+
+
+static int nl80211_do_acs(void *priv, struct drv_acs_params *params)
+{
+#if defined(CONFIG_DRIVER_NL80211_QCA) || defined(CONFIG_DRIVER_NL80211_BRCM)
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+#endif /* CONFIG_DRIVER_NL80211_QCA || CONFIG_DRIVER_NL80211_BRCM */
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+	if (drv->qca_do_acs)
+		return nl80211_qca_do_acs(drv, params);
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+#ifdef CONFIG_DRIVER_NL80211_BRCM
+	if (drv->brcm_do_acs)
+		return wpa_driver_do_broadcom_acs(drv, params);
+#endif /* CONFIG_DRIVER_NL80211_BRCM */
+
+	return -1;
+}
+
+
+static int nl80211_write_to_file(const char *name, unsigned int val)
+{
+	int fd, len;
+	char tmp[128];
+	int ret = 0;
+
+	fd = open(name, O_RDWR);
+	if (fd < 0) {
+		int level;
+		/*
+		 * Flags may not exist on older kernels, or while we're tearing
+		 * down a disappearing device.
+		 */
+		if (errno == ENOENT) {
+			ret = 0;
+			level = MSG_DEBUG;
+		} else {
+			ret = -1;
+			level = MSG_ERROR;
+		}
+		wpa_printf(level, "nl80211: Failed to open %s: %s",
+			   name, strerror(errno));
+		return ret;
+	}
+
+	len = os_snprintf(tmp, sizeof(tmp), "%u\n", val);
+	len = write(fd, tmp, len);
+	if (len < 0) {
+		ret = -1;
+		wpa_printf(MSG_ERROR, "nl80211: Failed to write to %s: %s",
+			   name, strerror(errno));
+	}
+	close(fd);
+
+	return ret;
+}
+
+
+static int nl80211_configure_data_frame_filters(void *priv, u32 filter_flags)
+{
+	struct i802_bss *bss = priv;
+	char path[128];
+	int ret;
+
+	/* P2P-Device has no netdev that can (or should) be configured here */
+	if (nl80211_get_ifmode(bss) == NL80211_IFTYPE_P2P_DEVICE)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Data frame filter flags=0x%x",
+		   filter_flags);
+
+	/* Configure filtering of unicast frame encrypted using GTK */
+	ret = os_snprintf(path, sizeof(path),
+			  "/proc/sys/net/ipv4/conf/%s/drop_unicast_in_l2_multicast",
+			  bss->ifname);
+	if (os_snprintf_error(sizeof(path), ret))
+		return -1;
+
+	ret = nl80211_write_to_file(path,
+				    !!(filter_flags &
+				       WPA_DATA_FRAME_FILTER_FLAG_GTK));
+	if (ret) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Failed to set IPv4 unicast in multicast filter");
+		return ret;
+	}
+
+	os_snprintf(path, sizeof(path),
+		    "/proc/sys/net/ipv6/conf/%s/drop_unicast_in_l2_multicast",
+		    bss->ifname);
+	ret = nl80211_write_to_file(path,
+				    !!(filter_flags &
+				       WPA_DATA_FRAME_FILTER_FLAG_GTK));
+
+	if (ret) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Failed to set IPv6 unicast in multicast filter");
+		return ret;
+	}
+
+	/* Configure filtering of unicast frame encrypted using GTK */
+	os_snprintf(path, sizeof(path),
+		    "/proc/sys/net/ipv4/conf/%s/drop_gratuitous_arp",
+		    bss->ifname);
+	ret = nl80211_write_to_file(path,
+				    !!(filter_flags &
+				       WPA_DATA_FRAME_FILTER_FLAG_ARP));
+	if (ret) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Failed set gratuitous ARP filter");
+		return ret;
+	}
+
+	/* Configure filtering of IPv6 NA frames */
+	os_snprintf(path, sizeof(path),
+		    "/proc/sys/net/ipv6/conf/%s/drop_unsolicited_na",
+		    bss->ifname);
+	ret = nl80211_write_to_file(path,
+				    !!(filter_flags &
+				       WPA_DATA_FRAME_FILTER_FLAG_NA));
+	if (ret) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Failed to set unsolicited NA filter");
+		return ret;
+	}
+
+	return 0;
+}
+
+
+static int nl80211_get_ext_capab(void *priv, enum wpa_driver_if_type type,
+				 const u8 **ext_capa, const u8 **ext_capa_mask,
+				 unsigned int *ext_capa_len)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	enum nl80211_iftype nlmode;
+	unsigned int i;
+
+	if (!ext_capa || !ext_capa_mask || !ext_capa_len)
+		return -1;
+
+	nlmode = wpa_driver_nl80211_if_type(type);
+
+	/* By default, use the per-radio values */
+	*ext_capa = drv->extended_capa;
+	*ext_capa_mask = drv->extended_capa_mask;
+	*ext_capa_len = drv->extended_capa_len;
+
+	/* Replace the default value if a per-interface type value exists */
+	for (i = 0; i < drv->num_iface_capa; i++) {
+		if (nlmode == drv->iface_capa[i].iftype) {
+			*ext_capa = drv->iface_capa[i].ext_capa;
+			*ext_capa_mask = drv->iface_capa[i].ext_capa_mask;
+			*ext_capa_len = drv->iface_capa[i].ext_capa_len;
+			break;
+		}
+	}
+
+	return 0;
+}
+
+
+static int nl80211_get_mld_capab(void *priv, enum wpa_driver_if_type type,
+				 u16 *eml_capa, u16 *mld_capa_and_ops)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	enum nl80211_iftype nlmode;
+	unsigned int i;
+
+	if (!eml_capa || !mld_capa_and_ops)
+		return -1;
+
+	nlmode = wpa_driver_nl80211_if_type(type);
+
+	/* By default, set to zero */
+	*eml_capa = 0;
+	*mld_capa_and_ops = 0;
+
+	/* Replace the default value if a per-interface type value exists */
+	for (i = 0; i < drv->num_iface_capa; i++) {
+		if (nlmode == drv->iface_capa[i].iftype) {
+			*eml_capa = drv->iface_capa[i].eml_capa;
+			*mld_capa_and_ops =
+				drv->iface_capa[i].mld_capa_and_ops;
+			break;
+		}
+	}
+
+	return 0;
+}
+
+
+static int nl80211_update_connection_params(
+	void *priv, struct wpa_driver_associate_params *params,
+	enum wpa_drv_update_connect_params_mask mask)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret = -1;
+	enum nl80211_auth_type type;
+
+	/* Update Connection Params is intended for drivers that implement
+	 * internal SME and expect these updated connection params from
+	 * wpa_supplicant. Do not send this request for the drivers using
+	 * SME from wpa_supplicant.
+	 */
+	if (drv->capa.flags & WPA_DRIVER_FLAGS_SME)
+		return 0;
+
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_UPDATE_CONNECT_PARAMS);
+	if (!msg)
+		goto fail;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Update connection params (ifindex=%d)",
+		   drv->ifindex);
+
+	if ((mask & WPA_DRV_UPDATE_ASSOC_IES) && params->wpa_ie) {
+		if (nla_put(msg, NL80211_ATTR_IE, params->wpa_ie_len,
+			    params->wpa_ie))
+			goto fail;
+		wpa_hexdump(MSG_DEBUG, "  * IEs", params->wpa_ie,
+			    params->wpa_ie_len);
+	}
+
+	if (mask & WPA_DRV_UPDATE_AUTH_TYPE) {
+		type = get_nl_auth_type(params->auth_alg);
+		if (type == NL80211_AUTHTYPE_MAX ||
+		    nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
+			goto fail;
+		wpa_printf(MSG_DEBUG, "  * Auth Type %d", type);
+	}
+
+	if ((mask & WPA_DRV_UPDATE_FILS_ERP_INFO) &&
+	    nl80211_put_fils_connect_params(drv, params, msg))
+		goto fail;
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	msg = NULL;
+	if (ret)
+		wpa_dbg(drv->ctx, MSG_DEBUG,
+			"nl80211: Update connect params command failed: ret=%d (%s)",
+			ret, strerror(-ret));
+
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+static int nl80211_send_external_auth_status(void *priv,
+					     struct external_auth *params)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg = NULL;
+	int ret = -1;
+
+	/* External auth command/status is intended for drivers that implement
+	 * internal SME but want to offload authentication processing (e.g.,
+	 * SAE) to hostapd/wpa_supplicant. Do not send the status to drivers
+	 * which do not support AP SME or use wpa_supplicant/hostapd SME.
+	 */
+	if ((is_ap_interface(drv->nlmode) && !bss->drv->device_ap_sme) ||
+	    (drv->capa.flags & WPA_DRIVER_FLAGS_SME))
+		return -1;
+
+	wpa_dbg(drv->ctx, MSG_DEBUG,
+		"nl80211: External auth status: %u", params->status);
+
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_EXTERNAL_AUTH);
+	if (!msg ||
+	    nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, params->status) ||
+	    (params->ssid && params->ssid_len &&
+	     nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) ||
+	    (params->pmkid &&
+	     nla_put(msg, NL80211_ATTR_PMKID, PMKID_LEN, params->pmkid)) ||
+	    (params->bssid &&
+	     nla_put(msg, NL80211_ATTR_BSSID, ETH_ALEN, params->bssid)))
+		goto fail;
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: External Auth status update failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+		goto fail;
+	}
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+static int nl80211_set_4addr_mode(void *priv, const char *bridge_ifname,
+				  int val)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret = -ENOBUFS;
+
+	wpa_printf(MSG_DEBUG, "nl80211: %s 4addr mode (bridge_ifname: %s)",
+		   val ? "Enable" : "Disable", bridge_ifname);
+
+	msg = nl80211_cmd_msg(drv->first_bss, 0, NL80211_CMD_SET_INTERFACE);
+	if (!msg || nla_put_u8(msg, NL80211_ATTR_4ADDR, val))
+		goto fail;
+
+	if (bridge_ifname[0] && bss->added_if_into_bridge && !val) {
+		if (linux_br_del_if(drv->global->ioctl_sock,
+				    bridge_ifname, bss->ifname)) {
+			wpa_printf(MSG_ERROR,
+				   "nl80211: Failed to remove interface %s from bridge %s",
+				   bss->ifname, bridge_ifname);
+			return -1;
+		}
+		bss->added_if_into_bridge = 0;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	msg = NULL;
+	if (ret && val && nl80211_get_4addr(bss) == 1) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: 4addr mode was already enabled");
+		ret = 0;
+	}
+	if (!ret) {
+		if (bridge_ifname[0] && val &&
+		    i802_check_bridge(drv, bss, bridge_ifname, bss->ifname) < 0)
+			return -1;
+		return 0;
+	}
+
+fail:
+	nlmsg_free(msg);
+	wpa_printf(MSG_ERROR, "nl80211: Failed to enable/disable 4addr");
+
+	return ret;
+}
+
+
+#ifdef CONFIG_DPP
+static int nl80211_dpp_listen(void *priv, bool enable)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_ACTION << 4);
+	struct nl_sock *handle;
+
+	if (!drv->multicast_registrations || !bss->nl_mgmt)
+		return 0; /* cannot do more than hope broadcast RX works */
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Update DPP Public Action frame registration (%s multicast RX)",
+		   enable ? "enable" : "disable");
+	handle = (void *) (((intptr_t) bss->nl_mgmt) ^ ELOOP_SOCKET_INVALID);
+	return nl80211_register_frame(bss, handle, type,
+				      (u8 *) "\x04\x09\x50\x6f\x9a\x1a", 6,
+				      enable);
+}
+#endif /* CONFIG_DPP */
+
+
+static int nl80211_link_add(void *priv, u8 link_id, const u8 *addr)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	unsigned int idx, i;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "nl80211: MLD: add link_id=%u, addr=" MACSTR,
+		   link_id, MAC2STR(addr));
+
+	if (drv->nlmode != NL80211_IFTYPE_AP) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: MLD: cannot add link to iftype=%u",
+			   drv->nlmode);
+		return -EINVAL;
+	}
+
+	if (bss->n_links >= MAX_NUM_MLD_LINKS) {
+		wpa_printf(MSG_DEBUG, "nl80211: MLD: already have n_links=%zu",
+			   bss->n_links);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < bss->n_links; i++) {
+		if (bss->links[i].link_id == link_id &&
+		    bss->links[i].beacon_set) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: MLD: link already set");
+			return -EINVAL;
+		}
+	}
+
+	/* try using the first link entry, assuming it is not beaconing yet */
+	if (bss->n_links == 1 &&
+	    bss->flink->link_id == NL80211_DRV_LINK_ID_NA) {
+		if (bss->flink->beacon_set) {
+			wpa_printf(MSG_DEBUG, "nl80211: BSS already beaconing");
+			return -EINVAL;
+		}
+
+		idx = 0;
+	} else {
+		idx = bss->n_links;
+	}
+
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_ADD_LINK);
+	if (!msg ||
+	    nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: add link failed. ret=%d (%s)",
+			   ret, strerror(-ret));
+		return ret;
+	}
+
+	bss->links[idx].link_id = link_id;
+	os_memcpy(bss->links[idx].addr, addr, ETH_ALEN);
+
+	bss->n_links = idx + 1;
+
+	wpa_printf(MSG_DEBUG, "nl80211: MLD: n_links=%zu", bss->n_links);
+	return 0;
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+
+static int testing_nl80211_register_frame(void *priv, u16 type,
+					  const u8 *match, size_t match_len,
+					  bool multicast)
+{
+	struct i802_bss *bss = priv;
+	struct nl_sock *handle;
+
+	if (!bss->nl_mgmt)
+		return -1;
+	handle = (void *) (((intptr_t) bss->nl_mgmt) ^ ELOOP_SOCKET_INVALID);
+	return nl80211_register_frame(bss, handle, type, match, match_len,
+				      multicast);
+}
+
+
+static int testing_nl80211_radio_disable(void *priv, int disabled)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	/* For now, this is supported only partially in station mode with
+	 * SME-in-wpa_supplicant case where the NL80211_ATTR_LOCAL_STATE_CHANGE
+	 * attribute can be used to avoid sending out the Deauthentication frame
+	 * to the currently associated AP. */
+
+	if (!disabled)
+		return 0;
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME))
+		return -1;
+
+	if (!drv->associated)
+		return 0;
+
+	return wpa_driver_nl80211_mlme(drv, drv->bssid,
+				       NL80211_CMD_DEAUTHENTICATE,
+				       WLAN_REASON_PREV_AUTH_NOT_VALID, 1,
+				       drv->first_bss);
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+	.name = "nl80211",
+	.desc = "Linux nl80211/cfg80211",
+	.get_bssid = wpa_driver_nl80211_get_bssid,
+	.get_ssid = wpa_driver_nl80211_get_ssid,
+	.set_key = driver_nl80211_set_key,
+	.scan2 = driver_nl80211_scan2,
+	.sched_scan = wpa_driver_nl80211_sched_scan,
+	.stop_sched_scan = wpa_driver_nl80211_stop_sched_scan,
+	.get_scan_results2 = wpa_driver_nl80211_get_scan_results,
+	.abort_scan = wpa_driver_nl80211_abort_scan,
+	.deauthenticate = driver_nl80211_deauthenticate,
+	.authenticate = driver_nl80211_authenticate,
+	.associate = wpa_driver_nl80211_associate,
+	.global_init = nl80211_global_init,
+	.global_deinit = nl80211_global_deinit,
+	.init2 = wpa_driver_nl80211_init,
+	.deinit = driver_nl80211_deinit,
+	.get_capa = wpa_driver_nl80211_get_capa,
+	.set_operstate = wpa_driver_nl80211_set_operstate,
+	.set_supp_port = wpa_driver_nl80211_set_supp_port,
+	.set_country = wpa_driver_nl80211_set_country,
+	.get_country = wpa_driver_nl80211_get_country,
+	.set_ap = wpa_driver_nl80211_set_ap,
+	.set_acl = wpa_driver_nl80211_set_acl,
+	.if_add = wpa_driver_nl80211_if_add,
+	.if_remove = driver_nl80211_if_remove,
+	.send_mlme = driver_nl80211_send_mlme,
+	.get_hw_feature_data = nl80211_get_hw_feature_data,
+	.sta_add = wpa_driver_nl80211_sta_add,
+	.sta_remove = driver_nl80211_sta_remove,
+	.tx_control_port = nl80211_tx_control_port,
+	.hapd_send_eapol = wpa_driver_nl80211_hapd_send_eapol,
+	.sta_set_flags = wpa_driver_nl80211_sta_set_flags,
+	.sta_set_airtime_weight = driver_nl80211_sta_set_airtime_weight,
+	.hapd_init = i802_init,
+	.hapd_deinit = i802_deinit,
+	.set_wds_sta = i802_set_wds_sta,
+	.get_seqnum = i802_get_seqnum,
+	.flush = i802_flush,
+	.get_inact_sec = i802_get_inact_sec,
+	.sta_clear_stats = i802_sta_clear_stats,
+	.set_rts = i802_set_rts,
+	.set_frag = i802_set_frag,
+	.set_tx_queue_params = i802_set_tx_queue_params,
+	.set_sta_vlan = driver_nl80211_set_sta_vlan,
+	.sta_deauth = i802_sta_deauth,
+	.sta_disassoc = i802_sta_disassoc,
+	.read_sta_data = driver_nl80211_read_sta_data,
+	.set_freq = i802_set_freq,
+	.send_action = driver_nl80211_send_action,
+	.send_action_cancel_wait = wpa_driver_nl80211_send_action_cancel_wait,
+	.remain_on_channel = wpa_driver_nl80211_remain_on_channel,
+	.cancel_remain_on_channel =
+	wpa_driver_nl80211_cancel_remain_on_channel,
+	.probe_req_report = driver_nl80211_probe_req_report,
+	.deinit_ap = wpa_driver_nl80211_deinit_ap,
+	.deinit_p2p_cli = wpa_driver_nl80211_deinit_p2p_cli,
+	.resume = wpa_driver_nl80211_resume,
+	.signal_monitor = nl80211_signal_monitor,
+	.signal_poll = nl80211_signal_poll,
+	.mlo_signal_poll = nl80211_mlo_signal_poll,
+	.channel_info = nl80211_channel_info,
+	.set_param = nl80211_set_param,
+	.get_radio_name = nl80211_get_radio_name,
+	.add_pmkid = nl80211_add_pmkid,
+	.remove_pmkid = nl80211_remove_pmkid,
+	.flush_pmkid = nl80211_flush_pmkid,
+	.set_rekey_info = nl80211_set_rekey_info,
+	.poll_client = nl80211_poll_client,
+	.set_p2p_powersave = nl80211_set_p2p_powersave,
+	.start_dfs_cac = nl80211_start_radar_detection,
+	.stop_ap = wpa_driver_nl80211_stop_ap,
+#ifdef CONFIG_TDLS
+	.send_tdls_mgmt = nl80211_send_tdls_mgmt,
+	.tdls_oper = nl80211_tdls_oper,
+	.tdls_enable_channel_switch = nl80211_tdls_enable_channel_switch,
+	.tdls_disable_channel_switch = nl80211_tdls_disable_channel_switch,
+#endif /* CONFIG_TDLS */
+	.update_ft_ies = wpa_driver_nl80211_update_ft_ies,
+	.update_dh_ie = nl80211_update_dh_ie,
+	.get_mac_addr = wpa_driver_nl80211_get_macaddr,
+	.get_survey = wpa_driver_nl80211_get_survey,
+	.status = wpa_driver_nl80211_status,
+	.switch_channel = nl80211_switch_channel,
+#ifdef CONFIG_IEEE80211AX
+	.switch_color = nl80211_switch_color,
+#endif /* CONFIG_IEEE80211AX */
+#ifdef ANDROID_P2P
+	.set_noa = wpa_driver_set_p2p_noa,
+	.get_noa = wpa_driver_get_p2p_noa,
+	.set_ap_wps_ie = wpa_driver_set_ap_wps_p2p_ie,
+#endif /* ANDROID_P2P */
+#ifdef ANDROID
+#ifndef ANDROID_LIB_STUB
+	.driver_cmd = wpa_driver_nl80211_driver_cmd,
+#endif /* !ANDROID_LIB_STUB */
+#endif /* ANDROID */
+	.vendor_cmd = nl80211_vendor_cmd,
+	.set_qos_map = nl80211_set_qos_map,
+	.get_wowlan = nl80211_get_wowlan,
+	.set_wowlan = nl80211_set_wowlan,
+	.set_mac_addr = nl80211_set_mac_addr,
+#ifdef CONFIG_MESH
+	.init_mesh = wpa_driver_nl80211_init_mesh,
+	.join_mesh = wpa_driver_nl80211_join_mesh,
+	.leave_mesh = wpa_driver_nl80211_leave_mesh,
+	.probe_mesh_link = nl80211_probe_mesh_link,
+#endif /* CONFIG_MESH */
+	.br_add_ip_neigh = wpa_driver_br_add_ip_neigh,
+	.br_delete_ip_neigh = wpa_driver_br_delete_ip_neigh,
+	.br_port_set_attr = wpa_driver_br_port_set_attr,
+	.br_set_net_param = wpa_driver_br_set_net_param,
+	.add_tx_ts = nl80211_add_ts,
+	.del_tx_ts = nl80211_del_ts,
+	.get_ifindex = nl80211_get_ifindex,
+#ifdef CONFIG_DRIVER_NL80211_QCA
+	.roaming = nl80211_roaming,
+	.disable_fils = nl80211_disable_fils,
+	.set_band = nl80211_set_band,
+	.get_pref_freq_list = nl80211_get_pref_freq_list,
+	.set_prob_oper_freq = nl80211_set_prob_oper_freq,
+	.p2p_lo_start = nl80211_p2p_lo_start,
+	.p2p_lo_stop = nl80211_p2p_lo_stop,
+	.set_default_scan_ies = nl80211_set_default_scan_ies,
+	.set_tdls_mode = nl80211_set_tdls_mode,
+#ifdef CONFIG_MBO
+	.get_bss_transition_status = nl80211_get_bss_transition_status,
+	.ignore_assoc_disallow = nl80211_ignore_assoc_disallow,
+#endif /* CONFIG_MBO */
+	.set_bssid_tmp_disallow = nl80211_set_bssid_tmp_disallow,
+	.add_sta_node = nl80211_add_sta_node,
+#ifdef CONFIG_PASN
+	.send_pasn_resp = nl80211_send_pasn_resp,
+	.set_secure_ranging_ctx = nl80211_set_secure_ranging_ctx,
+#endif /* CONFIG_PASN */
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+	.do_acs = nl80211_do_acs,
+	.configure_data_frame_filters = nl80211_configure_data_frame_filters,
+	.get_ext_capab = nl80211_get_ext_capab,
+	.get_mld_capab = nl80211_get_mld_capab,
+	.update_connect_params = nl80211_update_connection_params,
+	.send_external_auth_status = nl80211_send_external_auth_status,
+	.set_4addr_mode = nl80211_set_4addr_mode,
+#ifdef CONFIG_DPP
+	.dpp_listen = nl80211_dpp_listen,
+#endif /* CONFIG_DPP */
+	.get_sta_mlo_info = nl80211_get_sta_mlo_info,
+	.link_add = nl80211_link_add,
+#ifdef CONFIG_TESTING_OPTIONS
+	.register_frame = testing_nl80211_register_frame,
+	.radio_disable = testing_nl80211_radio_disable,
+#endif /* CONFIG_TESTING_OPTIONS */
+};
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211.h
new file mode 100644
index 0000000..aee8c45
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211.h
@@ -0,0 +1,368 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211 - definitions
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2004, Instant802 Networks, Inc.
+ * Copyright (c) 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DRIVER_NL80211_H
+#define DRIVER_NL80211_H
+
+#include "nl80211_copy.h"
+#include "utils/list.h"
+#include "driver.h"
+
+#ifndef NL_CAPABILITY_VERSION_3_5_0
+#define nla_nest_start(msg, attrtype) \
+	nla_nest_start(msg, NLA_F_NESTED | (attrtype))
+#endif
+
+struct nl80211_global {
+	void *ctx;
+	struct dl_list interfaces;
+	int if_add_ifindex;
+	u64 if_add_wdevid;
+	int if_add_wdevid_set;
+	struct netlink_data *netlink;
+	struct nl_cb *nl_cb;
+	struct nl_sock *nl;
+	int nl80211_id;
+	int nlctrl_id;
+	int ioctl_sock; /* socket for ioctl() use */
+
+	struct nl_sock *nl_event;
+};
+
+struct nl80211_wiphy_data {
+	struct dl_list list;
+	struct dl_list bsss;
+	struct dl_list drvs;
+
+	struct nl_sock *nl_beacons;
+	struct nl_cb *nl_cb;
+
+	int wiphy_idx;
+};
+
+#define NL80211_DRV_LINK_ID_NA (-1)
+
+struct i802_link {
+	unsigned int beacon_set:1;
+
+	s8 link_id;
+	int freq;
+	int bandwidth;
+	u8 addr[ETH_ALEN];
+	void *ctx;
+};
+
+struct i802_bss {
+	struct wpa_driver_nl80211_data *drv;
+	struct i802_bss *next;
+
+	size_t n_links;
+	struct i802_link links[MAX_NUM_MLD_LINKS];
+	struct i802_link *flink;
+
+	int ifindex;
+	int br_ifindex;
+	u64 wdev_id;
+	char ifname[IFNAMSIZ + 1];
+	char brname[IFNAMSIZ];
+	unsigned int added_if_into_bridge:1;
+	unsigned int already_in_bridge:1;
+	unsigned int added_bridge:1;
+	unsigned int in_deinit:1;
+	unsigned int wdev_id_set:1;
+	unsigned int added_if:1;
+	unsigned int static_ap:1;
+	unsigned int use_nl_connect:1;
+
+	u8 addr[ETH_ALEN];
+	u8 prev_addr[ETH_ALEN];
+
+	int if_dynamic;
+
+	void *ctx;
+	struct nl_sock *nl_preq, *nl_mgmt, *nl_connect;
+	struct nl_cb *nl_cb;
+
+	struct nl80211_wiphy_data *wiphy_data;
+	struct dl_list wiphy_list;
+	u8 rand_addr[ETH_ALEN];
+};
+
+struct drv_nl80211_if_info {
+	int ifindex;
+	/* the AP/AP_VLAN iface that is in this bridge */
+	int reason;
+};
+
+struct wpa_driver_nl80211_data {
+	struct nl80211_global *global;
+	struct dl_list list;
+	struct dl_list wiphy_list;
+	char phyname[32];
+	unsigned int wiphy_idx;
+	u8 perm_addr[ETH_ALEN];
+	void *ctx;
+	int ifindex;
+	int if_removed;
+	int if_disabled;
+	int ignore_if_down_event;
+	struct rfkill_data *rfkill;
+	struct wpa_driver_capa capa;
+	u8 *extended_capa, *extended_capa_mask;
+	unsigned int extended_capa_len;
+	struct drv_nl80211_iface_capa {
+		enum nl80211_iftype iftype;
+		u8 *ext_capa, *ext_capa_mask;
+		unsigned int ext_capa_len;
+		u16 eml_capa;
+		u16 mld_capa_and_ops;
+	} iface_capa[NL80211_IFTYPE_MAX];
+	unsigned int num_iface_capa;
+
+	int has_capability;
+	int has_driver_key_mgmt;
+
+	int operstate;
+
+	int scan_complete_events;
+	enum scan_states {
+		NO_SCAN, SCAN_REQUESTED, SCAN_STARTED, SCAN_COMPLETED,
+		SCAN_ABORTED, SCHED_SCAN_STARTED, SCHED_SCAN_STOPPED,
+		SCHED_SCAN_RESULTS
+	} scan_state;
+
+	u8 auth_bssid[ETH_ALEN];
+	u8 auth_attempt_bssid[ETH_ALEN];
+	u8 bssid[ETH_ALEN];
+	u8 prev_bssid[ETH_ALEN];
+	int associated;
+	struct driver_sta_mlo_info sta_mlo_info;
+	u8 ssid[SSID_MAX_LEN];
+	size_t ssid_len;
+	enum nl80211_iftype nlmode;
+	enum nl80211_iftype ap_scan_as_station;
+	unsigned int assoc_freq;
+
+	int monitor_sock;
+	int monitor_ifidx;
+	int monitor_refcount;
+
+	unsigned int disabled_11b_rates:1;
+	unsigned int pending_remain_on_chan:1;
+	unsigned int in_interface_list:1;
+	unsigned int device_ap_sme:1;
+	unsigned int poll_command_supported:1;
+	unsigned int data_tx_status:1;
+	unsigned int scan_for_auth:1;
+	unsigned int retry_auth:1;
+	unsigned int use_monitor:1;
+	unsigned int ignore_next_local_disconnect:1;
+	unsigned int ignore_next_local_deauth:1;
+	unsigned int hostapd:1;
+	unsigned int start_mode_sta:1;
+	unsigned int start_iface_up:1;
+	unsigned int test_use_roc_tx:1;
+	unsigned int ignore_deauth_event:1;
+	unsigned int vendor_cmd_test_avail:1;
+	unsigned int roaming_vendor_cmd_avail:1;
+	unsigned int dfs_vendor_cmd_avail:1;
+	unsigned int have_low_prio_scan:1;
+	unsigned int force_connect_cmd:1;
+	unsigned int addr_changed:1;
+	unsigned int get_features_vendor_cmd_avail:1;
+	unsigned int set_rekey_offload:1;
+	unsigned int p2p_go_ctwindow_supported:1;
+	unsigned int setband_vendor_cmd_avail:1;
+	unsigned int get_pref_freq_list:1;
+	unsigned int set_prob_oper_freq:1;
+	unsigned int scan_vendor_cmd_avail:1;
+	unsigned int connect_reassoc:1;
+	unsigned int set_wifi_conf_vendor_cmd_avail:1;
+	unsigned int fetch_bss_trans_status:1;
+	unsigned int roam_vendor_cmd_avail:1;
+	unsigned int add_sta_node_vendor_cmd_avail:1;
+	unsigned int control_port_ap:1;
+	unsigned int multicast_registrations:1;
+	unsigned int no_rrm:1;
+	unsigned int get_sta_info_vendor_cmd_avail:1;
+	unsigned int fils_discovery:1;
+	unsigned int unsol_bcast_probe_resp:1;
+	unsigned int qca_do_acs:1;
+	unsigned int brcm_do_acs:1;
+	unsigned int uses_6ghz:1;
+	unsigned int secure_ranging_ctx_vendor_cmd_avail:1;
+	unsigned int puncturing:1;
+	unsigned int qca_ap_allowed_freqs:1;
+
+	u64 vendor_scan_cookie;
+	u64 remain_on_chan_cookie;
+	u64 send_frame_cookie;
+	int send_frame_link_id;
+#define MAX_SEND_FRAME_COOKIES 20
+	u64 send_frame_cookies[MAX_SEND_FRAME_COOKIES];
+	unsigned int num_send_frame_cookies;
+	u64 eapol_tx_cookie;
+	int eapol_tx_link_id;
+
+	unsigned int last_mgmt_freq;
+
+	struct wpa_driver_scan_filter *filter_ssids;
+	size_t num_filter_ssids;
+
+	struct i802_bss *first_bss;
+
+	int eapol_tx_sock;
+
+	int eapol_sock; /* socket for EAPOL frames */
+
+	struct nl_sock *rtnl_sk; /* nl_sock for NETLINK_ROUTE */
+
+	struct drv_nl80211_if_info default_if_indices[16];
+	struct drv_nl80211_if_info *if_indices;
+	int num_if_indices;
+
+	/* From failed authentication command */
+	int auth_freq;
+	u8 auth_bssid_[ETH_ALEN];
+	u8 auth_ssid[SSID_MAX_LEN];
+	size_t auth_ssid_len;
+	int auth_alg;
+	u8 *auth_ie;
+	size_t auth_ie_len;
+	u8 *auth_data;
+	size_t auth_data_len;
+	u8 auth_wep_key[4][16];
+	size_t auth_wep_key_len[4];
+	int auth_wep_tx_keyidx;
+	int auth_local_state_change;
+	int auth_p2p;
+	bool auth_mld;
+	u8 auth_mld_link_id;
+	u8 auth_ap_mld_addr[ETH_ALEN];
+
+	/*
+	 * Tells whether the last scan issued from wpa_supplicant was a normal
+	 * scan (NL80211_CMD_TRIGGER_SCAN) or a vendor scan
+	 * (NL80211_CMD_VENDOR). 0 if no pending scan request.
+	 */
+	int last_scan_cmd;
+#ifdef CONFIG_DRIVER_NL80211_QCA
+	bool roam_indication_done;
+	u8 *pending_roam_data;
+	size_t pending_roam_data_len;
+	u8 *pending_t2lm_data;
+	size_t pending_t2lm_data_len;
+	u8 *pending_link_reconfig_data;
+	size_t pending_link_reconfig_data_len;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+};
+
+struct nl_msg;
+
+void * nl80211_cmd(struct wpa_driver_nl80211_data *drv,
+		   struct nl_msg *msg, int flags, uint8_t cmd);
+struct nl_msg * nl80211_cmd_msg(struct i802_bss *bss, int flags, uint8_t cmd);
+struct nl_msg * nl80211_drv_msg(struct wpa_driver_nl80211_data *drv, int flags,
+				uint8_t cmd);
+struct nl_msg * nl80211_bss_msg(struct i802_bss *bss, int flags, uint8_t cmd);
+int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, struct nl_msg *msg,
+		       int (*valid_handler)(struct nl_msg *, void *),
+		       void *valid_data,
+		       int (*ack_handler_custom)(struct nl_msg *, void *),
+		       void *ack_data);
+struct nl_sock * get_connect_handle(struct i802_bss *bss);
+int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
+			 const char *ifname, enum nl80211_iftype iftype,
+			 const u8 *addr, int wds,
+			 int (*handler)(struct nl_msg *, void *),
+			 void *arg, int use_existing);
+void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, int ifidx);
+unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv);
+int nl80211_get_assoc_ssid(struct wpa_driver_nl80211_data *drv, u8 *ssid);
+enum chan_width convert2width(int width);
+void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv);
+struct i802_bss * get_bss_ifindex(struct wpa_driver_nl80211_data *drv,
+				  int ifindex);
+int is_ap_interface(enum nl80211_iftype nlmode);
+int is_sta_interface(enum nl80211_iftype nlmode);
+int wpa_driver_nl80211_authenticate_retry(struct wpa_driver_nl80211_data *drv);
+int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
+			    const u8 *bssid,
+			    struct hostap_sta_driver_data *data);
+int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv,
+			   struct wpa_signal_info *sig_change);
+int nl80211_get_wiphy_index(struct i802_bss *bss);
+int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
+				enum nl80211_iftype nlmode);
+int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
+			    const u8 *addr, int cmd, u16 reason_code,
+			    int local_state_change,
+			    struct i802_bss *bss);
+
+int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv);
+void nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data *drv);
+int nl80211_send_monitor(struct wpa_driver_nl80211_data *drv,
+			 const void *data, size_t len,
+			 int encrypt, int noack);
+
+int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv);
+struct hostapd_hw_modes *
+nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags,
+			    u8 *dfs_domain);
+
+int process_global_event(struct nl_msg *msg, void *arg);
+int process_bss_event(struct nl_msg *msg, void *arg);
+
+const char * nl80211_iftype_str(enum nl80211_iftype mode);
+
+void nl80211_restore_ap_mode(struct i802_bss *bss);
+struct i802_link * nl80211_get_link(struct i802_bss *bss, s8 link_id);
+
+#ifdef ANDROID
+int android_nl_socket_set_nonblocking(struct nl_sock *handle);
+int android_pno_start(struct i802_bss *bss,
+		      struct wpa_driver_scan_params *params);
+int android_pno_stop(struct i802_bss *bss);
+extern int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf,
+					 size_t buf_len);
+extern int wpa_driver_nl80211_driver_event(struct wpa_driver_nl80211_data *drv,
+					   u32 vendor_id, u32 subcmd,
+					   u8 *data, size_t len);
+
+
+#ifdef ANDROID_P2P
+int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration);
+int wpa_driver_get_p2p_noa(void *priv, u8 *buf, size_t len);
+int wpa_driver_set_p2p_ps(void *priv, int legacy_ps, int opp_ps, int ctwindow);
+int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon,
+				 const struct wpabuf *proberesp,
+				 const struct wpabuf *assocresp);
+#endif /* ANDROID_P2P */
+#endif /* ANDROID */
+
+
+/* driver_nl80211_scan.c */
+
+void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx);
+int wpa_driver_nl80211_scan(struct i802_bss *bss,
+			    struct wpa_driver_scan_params *params);
+int wpa_driver_nl80211_sched_scan(void *priv,
+				  struct wpa_driver_scan_params *params);
+int wpa_driver_nl80211_stop_sched_scan(void *priv);
+struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv);
+void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv);
+int wpa_driver_nl80211_abort_scan(void *priv, u64 scan_cookie);
+int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss,
+				   struct wpa_driver_scan_params *params);
+int nl80211_set_default_scan_ies(void *priv, const u8 *ies, size_t ies_len);
+
+#endif /* DRIVER_NL80211_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211_android.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211_android.c
new file mode 100644
index 0000000..9431a12
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211_android.c
@@ -0,0 +1,188 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211 - Android specific
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/family.h>
+#include <netlink/genl/ctrl.h>
+#include <fcntl.h>
+
+#include "utils/common.h"
+#include "driver_nl80211.h"
+#include "android_drv.h"
+
+
+typedef struct android_wifi_priv_cmd {
+	char *buf;
+	int used_len;
+	int total_len;
+} android_wifi_priv_cmd;
+
+static int drv_errors = 0;
+
+static void wpa_driver_send_hang_msg(struct wpa_driver_nl80211_data *drv)
+{
+	drv_errors++;
+	if (drv_errors > DRV_NUMBER_SEQUENTIAL_ERRORS) {
+		drv_errors = 0;
+		wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED");
+	}
+}
+
+
+static int android_priv_cmd(struct i802_bss *bss, const char *cmd)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct ifreq ifr;
+	android_wifi_priv_cmd priv_cmd;
+	char buf[MAX_DRV_CMD_SIZE];
+	int ret;
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_memset(&priv_cmd, 0, sizeof(priv_cmd));
+	os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ);
+
+	os_memset(buf, 0, sizeof(buf));
+	os_strlcpy(buf, cmd, sizeof(buf));
+
+	priv_cmd.buf = buf;
+	priv_cmd.used_len = sizeof(buf);
+	priv_cmd.total_len = sizeof(buf);
+	ifr.ifr_data = &priv_cmd;
+
+	ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: failed to issue private commands",
+			   __func__);
+		wpa_driver_send_hang_msg(drv);
+		return ret;
+	}
+
+	drv_errors = 0;
+	return 0;
+}
+
+
+int android_pno_start(struct i802_bss *bss,
+		      struct wpa_driver_scan_params *params)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct ifreq ifr;
+	android_wifi_priv_cmd priv_cmd;
+	int ret = 0, i = 0, bp;
+	char buf[WEXT_PNO_MAX_COMMAND_SIZE];
+
+	bp = WEXT_PNOSETUP_HEADER_SIZE;
+	os_memcpy(buf, WEXT_PNOSETUP_HEADER, bp);
+	buf[bp++] = WEXT_PNO_TLV_PREFIX;
+	buf[bp++] = WEXT_PNO_TLV_VERSION;
+	buf[bp++] = WEXT_PNO_TLV_SUBVERSION;
+	buf[bp++] = WEXT_PNO_TLV_RESERVED;
+
+	while (i < WEXT_PNO_AMOUNT && (size_t) i < params->num_ssids) {
+		/* Check that there is enough space needed for 1 more SSID, the
+		 * other sections and null termination */
+		if ((bp + WEXT_PNO_SSID_HEADER_SIZE + MAX_SSID_LEN +
+		     WEXT_PNO_NONSSID_SECTIONS_SIZE + 1) >= (int) sizeof(buf))
+			break;
+		wpa_hexdump_ascii(MSG_DEBUG, "For PNO Scan",
+				  params->ssids[i].ssid,
+				  params->ssids[i].ssid_len);
+		buf[bp++] = WEXT_PNO_SSID_SECTION;
+		buf[bp++] = params->ssids[i].ssid_len;
+		os_memcpy(&buf[bp], params->ssids[i].ssid,
+			  params->ssids[i].ssid_len);
+		bp += params->ssids[i].ssid_len;
+		i++;
+	}
+
+	buf[bp++] = WEXT_PNO_SCAN_INTERVAL_SECTION;
+	os_snprintf(&buf[bp], WEXT_PNO_SCAN_INTERVAL_LENGTH + 1, "%x",
+		    WEXT_PNO_SCAN_INTERVAL);
+	bp += WEXT_PNO_SCAN_INTERVAL_LENGTH;
+
+	buf[bp++] = WEXT_PNO_REPEAT_SECTION;
+	os_snprintf(&buf[bp], WEXT_PNO_REPEAT_LENGTH + 1, "%x",
+		    WEXT_PNO_REPEAT);
+	bp += WEXT_PNO_REPEAT_LENGTH;
+
+	buf[bp++] = WEXT_PNO_MAX_REPEAT_SECTION;
+	os_snprintf(&buf[bp], WEXT_PNO_MAX_REPEAT_LENGTH + 1, "%x",
+		    WEXT_PNO_MAX_REPEAT);
+	bp += WEXT_PNO_MAX_REPEAT_LENGTH + 1;
+
+	memset(&ifr, 0, sizeof(ifr));
+	memset(&priv_cmd, 0, sizeof(priv_cmd));
+	os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ);
+
+	priv_cmd.buf = buf;
+	priv_cmd.used_len = bp;
+	priv_cmd.total_len = bp;
+	ifr.ifr_data = &priv_cmd;
+
+	ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr);
+
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (pnosetup): %d",
+			   ret);
+		wpa_driver_send_hang_msg(drv);
+		return ret;
+	}
+
+	drv_errors = 0;
+
+	return android_priv_cmd(bss, "PNOFORCE 1");
+}
+
+
+int android_pno_stop(struct i802_bss *bss)
+{
+	return android_priv_cmd(bss, "PNOFORCE 0");
+}
+
+
+#ifdef ANDROID_P2P
+#ifdef ANDROID_LIB_STUB
+
+int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration)
+{
+	return 0;
+}
+
+
+int wpa_driver_get_p2p_noa(void *priv, u8 *buf, size_t len)
+{
+	return 0;
+}
+
+
+int wpa_driver_set_p2p_ps(void *priv, int legacy_ps, int opp_ps, int ctwindow)
+{
+	return -1;
+}
+
+
+int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon,
+				 const struct wpabuf *proberesp,
+				 const struct wpabuf *assocresp)
+{
+	return 0;
+}
+
+#endif /* ANDROID_LIB_STUB */
+#endif /* ANDROID_P2P */
+
+
+int android_nl_socket_set_nonblocking(struct nl_sock *handle)
+{
+	return fcntl(nl_socket_get_fd(handle), F_SETFL, O_NONBLOCK);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211_capa.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211_capa.c
new file mode 100644
index 0000000..04393ef
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211_capa.c
@@ -0,0 +1,2646 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211 - Capabilities
+ * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <netlink/genl/genl.h>
+
+#include "utils/common.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_common.h"
+#include "common/qca-vendor.h"
+#include "common/qca-vendor-attr.h"
+#include "common/brcm_vendor.h"
+#include "driver_nl80211.h"
+
+
+static int protocol_feature_handler(struct nl_msg *msg, void *arg)
+{
+	u32 *feat = arg;
+	struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+
+	nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (tb_msg[NL80211_ATTR_PROTOCOL_FEATURES])
+		*feat = nla_get_u32(tb_msg[NL80211_ATTR_PROTOCOL_FEATURES]);
+
+	return NL_SKIP;
+}
+
+
+static u32 get_nl80211_protocol_features(struct wpa_driver_nl80211_data *drv)
+{
+	u32 feat = 0;
+	struct nl_msg *msg;
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return 0;
+
+	if (!nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_PROTOCOL_FEATURES)) {
+		nlmsg_free(msg);
+		return 0;
+	}
+
+	if (send_and_recv_msgs(drv, msg, protocol_feature_handler, &feat,
+			       NULL, NULL) == 0)
+		return feat;
+
+	return 0;
+}
+
+
+struct wiphy_info_data {
+	struct wpa_driver_nl80211_data *drv;
+	struct wpa_driver_capa *capa;
+
+	unsigned int num_multichan_concurrent;
+
+	unsigned int error:1;
+	unsigned int device_ap_sme:1;
+	unsigned int poll_command_supported:1;
+	unsigned int data_tx_status:1;
+	unsigned int auth_supported:1;
+	unsigned int connect_supported:1;
+	unsigned int p2p_go_supported:1;
+	unsigned int p2p_client_supported:1;
+	unsigned int p2p_go_ctwindow_supported:1;
+	unsigned int p2p_concurrent:1;
+	unsigned int channel_switch_supported:1;
+	unsigned int set_qos_map_supported:1;
+	unsigned int have_low_prio_scan:1;
+	unsigned int wmm_ac_supported:1;
+	unsigned int mac_addr_rand_scan_supported:1;
+	unsigned int mac_addr_rand_sched_scan_supported:1;
+	unsigned int update_ft_ies_supported:1;
+	unsigned int has_key_mgmt:1;
+	unsigned int has_key_mgmt_iftype:1;
+};
+
+
+static unsigned int probe_resp_offload_support(int supp_protocols)
+{
+	unsigned int prot = 0;
+
+	if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS)
+		prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS;
+	if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2)
+		prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2;
+	if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P)
+		prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P;
+	if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U)
+		prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING;
+
+	return prot;
+}
+
+
+static void wiphy_info_supported_iftypes(struct wiphy_info_data *info,
+					 struct nlattr *tb)
+{
+	struct nlattr *nl_mode;
+	int i;
+
+	if (tb == NULL)
+		return;
+
+	nla_for_each_nested(nl_mode, tb, i) {
+		switch (nla_type(nl_mode)) {
+		case NL80211_IFTYPE_AP:
+			info->capa->flags |= WPA_DRIVER_FLAGS_AP;
+			break;
+		case NL80211_IFTYPE_MESH_POINT:
+			info->capa->flags |= WPA_DRIVER_FLAGS_MESH;
+			break;
+		case NL80211_IFTYPE_ADHOC:
+			info->capa->flags |= WPA_DRIVER_FLAGS_IBSS;
+			break;
+		case NL80211_IFTYPE_P2P_DEVICE:
+			info->capa->flags |=
+				WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE;
+			break;
+		case NL80211_IFTYPE_P2P_GO:
+			info->p2p_go_supported = 1;
+			break;
+		case NL80211_IFTYPE_P2P_CLIENT:
+			info->p2p_client_supported = 1;
+			break;
+		}
+	}
+}
+
+
+static int wiphy_info_iface_comb_process(struct wiphy_info_data *info,
+					 struct nlattr *nl_combi)
+{
+	struct nlattr *tb_comb[NUM_NL80211_IFACE_COMB];
+	struct nlattr *tb_limit[NUM_NL80211_IFACE_LIMIT];
+	struct nlattr *nl_limit, *nl_mode;
+	int err, rem_limit, rem_mode;
+	int combination_has_p2p = 0, combination_has_mgd = 0;
+	static struct nla_policy
+	iface_combination_policy[NUM_NL80211_IFACE_COMB] = {
+		[NL80211_IFACE_COMB_LIMITS] = { .type = NLA_NESTED },
+		[NL80211_IFACE_COMB_MAXNUM] = { .type = NLA_U32 },
+		[NL80211_IFACE_COMB_STA_AP_BI_MATCH] = { .type = NLA_FLAG },
+		[NL80211_IFACE_COMB_NUM_CHANNELS] = { .type = NLA_U32 },
+		[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS] = { .type = NLA_U32 },
+	},
+	iface_limit_policy[NUM_NL80211_IFACE_LIMIT] = {
+		[NL80211_IFACE_LIMIT_TYPES] = { .type = NLA_NESTED },
+		[NL80211_IFACE_LIMIT_MAX] = { .type = NLA_U32 },
+	};
+
+	err = nla_parse_nested(tb_comb, MAX_NL80211_IFACE_COMB,
+			       nl_combi, iface_combination_policy);
+	if (err || !tb_comb[NL80211_IFACE_COMB_LIMITS] ||
+	    !tb_comb[NL80211_IFACE_COMB_MAXNUM] ||
+	    !tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS])
+		return 0; /* broken combination */
+
+	if (tb_comb[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS])
+		info->capa->flags |= WPA_DRIVER_FLAGS_RADAR;
+
+	nla_for_each_nested(nl_limit, tb_comb[NL80211_IFACE_COMB_LIMITS],
+			    rem_limit) {
+		err = nla_parse_nested(tb_limit, MAX_NL80211_IFACE_LIMIT,
+				       nl_limit, iface_limit_policy);
+		if (err || !tb_limit[NL80211_IFACE_LIMIT_TYPES])
+			return 0; /* broken combination */
+
+		nla_for_each_nested(nl_mode,
+				    tb_limit[NL80211_IFACE_LIMIT_TYPES],
+				    rem_mode) {
+			int ift = nla_type(nl_mode);
+			if (ift == NL80211_IFTYPE_P2P_GO ||
+			    ift == NL80211_IFTYPE_P2P_CLIENT)
+				combination_has_p2p = 1;
+			if (ift == NL80211_IFTYPE_STATION)
+				combination_has_mgd = 1;
+		}
+		if (combination_has_p2p && combination_has_mgd)
+			break;
+	}
+
+	if (combination_has_p2p && combination_has_mgd) {
+		unsigned int num_channels =
+			nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]);
+
+		info->p2p_concurrent = 1;
+		if (info->num_multichan_concurrent < num_channels)
+			info->num_multichan_concurrent = num_channels;
+	}
+
+	return 0;
+}
+
+
+static void wiphy_info_iface_comb(struct wiphy_info_data *info,
+				  struct nlattr *tb)
+{
+	struct nlattr *nl_combi;
+	int rem_combi;
+
+	if (tb == NULL)
+		return;
+
+	nla_for_each_nested(nl_combi, tb, rem_combi) {
+		if (wiphy_info_iface_comb_process(info, nl_combi) > 0)
+			break;
+	}
+}
+
+
+static void wiphy_info_supp_cmds(struct wiphy_info_data *info,
+				 struct nlattr *tb)
+{
+	struct nlattr *nl_cmd;
+	int i;
+
+	if (tb == NULL)
+		return;
+
+	nla_for_each_nested(nl_cmd, tb, i) {
+		switch (nla_get_u32(nl_cmd)) {
+		case NL80211_CMD_AUTHENTICATE:
+			info->auth_supported = 1;
+			break;
+		case NL80211_CMD_CONNECT:
+			info->connect_supported = 1;
+			break;
+		case NL80211_CMD_START_SCHED_SCAN:
+			info->capa->sched_scan_supported = 1;
+			break;
+		case NL80211_CMD_PROBE_CLIENT:
+			info->poll_command_supported = 1;
+			break;
+		case NL80211_CMD_CHANNEL_SWITCH:
+			info->channel_switch_supported = 1;
+			break;
+		case NL80211_CMD_SET_QOS_MAP:
+			info->set_qos_map_supported = 1;
+			break;
+		case NL80211_CMD_UPDATE_FT_IES:
+			info->update_ft_ies_supported = 1;
+			break;
+		}
+	}
+}
+
+
+static unsigned int get_akm_suites_info(struct nlattr *tb)
+{
+	int i, num;
+	unsigned int key_mgmt = 0;
+	u32 *akms;
+
+	if (!tb)
+		return 0;
+
+	num = nla_len(tb) / sizeof(u32);
+	akms = nla_data(tb);
+	for (i = 0; i < num; i++) {
+		switch (akms[i]) {
+		case RSN_AUTH_KEY_MGMT_UNSPEC_802_1X:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+				WPA_DRIVER_CAPA_KEY_MGMT_WPA2;
+			break;
+		case RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+				WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+			break;
+		case RSN_AUTH_KEY_MGMT_FT_802_1X:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT;
+			break;
+		case RSN_AUTH_KEY_MGMT_FT_PSK:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK;
+			break;
+		case RSN_AUTH_KEY_MGMT_802_1X_SHA256:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_802_1X_SHA256;
+			break;
+		case RSN_AUTH_KEY_MGMT_PSK_SHA256:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_PSK_SHA256;
+			break;
+		case RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_TPK_HANDSHAKE;
+			break;
+		case RSN_AUTH_KEY_MGMT_FT_SAE:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE;
+			break;
+		case RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE_EXT_KEY;
+			break;
+		case RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_802_1X_SHA384;
+			break;
+		case RSN_AUTH_KEY_MGMT_CCKM:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_CCKM;
+			break;
+		case RSN_AUTH_KEY_MGMT_OSEN:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_OSEN;
+			break;
+		case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B;
+			break;
+		case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192;
+			break;
+		case RSN_AUTH_KEY_MGMT_OWE:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_OWE;
+			break;
+		case RSN_AUTH_KEY_MGMT_DPP:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_DPP;
+			break;
+		case RSN_AUTH_KEY_MGMT_FILS_SHA256:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256;
+			break;
+		case RSN_AUTH_KEY_MGMT_FILS_SHA384:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384;
+			break;
+		case RSN_AUTH_KEY_MGMT_FT_FILS_SHA256:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256;
+			break;
+		case RSN_AUTH_KEY_MGMT_FT_FILS_SHA384:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384;
+			break;
+		case RSN_AUTH_KEY_MGMT_SAE:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SAE;
+			break;
+		case RSN_AUTH_KEY_MGMT_SAE_EXT_KEY:
+			key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SAE_EXT_KEY;
+			break;
+		}
+	}
+
+	return key_mgmt;
+}
+
+
+static void get_iface_akm_suites_info(struct wiphy_info_data *info,
+					struct nlattr *nl_akms)
+{
+	struct nlattr *tb[NL80211_IFTYPE_AKM_ATTR_MAX + 1];
+	struct nlattr *nl_iftype;
+	unsigned int key_mgmt;
+	int i;
+
+	if (!nl_akms)
+		return;
+
+	nla_parse(tb, NL80211_IFTYPE_AKM_ATTR_MAX,
+		  nla_data(nl_akms), nla_len(nl_akms), NULL);
+
+	if (!tb[NL80211_IFTYPE_AKM_ATTR_IFTYPES] ||
+	    !tb[NL80211_IFTYPE_AKM_ATTR_SUITES])
+		return;
+
+	info->has_key_mgmt_iftype = 1;
+	key_mgmt = get_akm_suites_info(tb[NL80211_IFTYPE_AKM_ATTR_SUITES]);
+
+	nla_for_each_nested(nl_iftype, tb[NL80211_IFTYPE_AKM_ATTR_IFTYPES], i) {
+		switch (nla_type(nl_iftype)) {
+		case NL80211_IFTYPE_ADHOC:
+			info->drv->capa.key_mgmt_iftype[WPA_IF_IBSS] = key_mgmt;
+			break;
+		case NL80211_IFTYPE_STATION:
+			info->drv->capa.key_mgmt_iftype[WPA_IF_STATION] =
+				key_mgmt;
+			break;
+		case NL80211_IFTYPE_AP:
+			info->drv->capa.key_mgmt_iftype[WPA_IF_AP_BSS] =
+				key_mgmt;
+			break;
+		case NL80211_IFTYPE_AP_VLAN:
+			info->drv->capa.key_mgmt_iftype[WPA_IF_AP_VLAN] =
+				key_mgmt;
+			break;
+		case NL80211_IFTYPE_MESH_POINT:
+			info->drv->capa.key_mgmt_iftype[WPA_IF_MESH] = key_mgmt;
+			break;
+		case NL80211_IFTYPE_P2P_CLIENT:
+			info->drv->capa.key_mgmt_iftype[WPA_IF_P2P_CLIENT] =
+				key_mgmt;
+			break;
+		case NL80211_IFTYPE_P2P_GO:
+			info->drv->capa.key_mgmt_iftype[WPA_IF_P2P_GO] =
+				key_mgmt;
+			break;
+		case NL80211_IFTYPE_P2P_DEVICE:
+			info->drv->capa.key_mgmt_iftype[WPA_IF_P2P_DEVICE] =
+				key_mgmt;
+			break;
+		case NL80211_IFTYPE_NAN:
+			info->drv->capa.key_mgmt_iftype[WPA_IF_NAN] = key_mgmt;
+			break;
+		}
+		wpa_printf(MSG_DEBUG, "nl80211: %s supported key_mgmt 0x%x",
+			   nl80211_iftype_str(nla_type(nl_iftype)),
+			   key_mgmt);
+	}
+}
+
+
+static void wiphy_info_iftype_akm_suites(struct wiphy_info_data *info,
+					 struct nlattr *tb)
+{
+	struct nlattr *nl_if;
+	int rem_if;
+
+	if (!tb)
+		return;
+
+	nla_for_each_nested(nl_if, tb, rem_if)
+		get_iface_akm_suites_info(info, nl_if);
+}
+
+
+static void wiphy_info_akm_suites(struct wiphy_info_data *info,
+				  struct nlattr *tb)
+{
+	if (!tb)
+		return;
+
+	info->has_key_mgmt = 1;
+	info->capa->key_mgmt = get_akm_suites_info(tb);
+	wpa_printf(MSG_DEBUG, "nl80211: wiphy supported key_mgmt 0x%x",
+		   info->capa->key_mgmt);
+}
+
+
+static void wiphy_info_cipher_suites(struct wiphy_info_data *info,
+				     struct nlattr *tb)
+{
+	int i, num;
+	u32 *ciphers;
+
+	if (tb == NULL)
+		return;
+
+	num = nla_len(tb) / sizeof(u32);
+	ciphers = nla_data(tb);
+	for (i = 0; i < num; i++) {
+		u32 c = ciphers[i];
+
+		wpa_printf(MSG_DEBUG, "nl80211: Supported cipher %02x-%02x-%02x:%d",
+			   c >> 24, (c >> 16) & 0xff,
+			   (c >> 8) & 0xff, c & 0xff);
+		switch (c) {
+		case RSN_CIPHER_SUITE_CCMP_256:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP_256;
+			break;
+		case RSN_CIPHER_SUITE_GCMP_256:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP_256;
+			break;
+		case RSN_CIPHER_SUITE_CCMP:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP;
+			break;
+		case RSN_CIPHER_SUITE_GCMP:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP;
+			break;
+		case RSN_CIPHER_SUITE_TKIP:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_TKIP;
+			break;
+		case RSN_CIPHER_SUITE_WEP104:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP104;
+			break;
+		case RSN_CIPHER_SUITE_WEP40:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP40;
+			break;
+		case RSN_CIPHER_SUITE_AES_128_CMAC:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP;
+			break;
+		case RSN_CIPHER_SUITE_BIP_GMAC_128:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_128;
+			break;
+		case RSN_CIPHER_SUITE_BIP_GMAC_256:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_256;
+			break;
+		case RSN_CIPHER_SUITE_BIP_CMAC_256:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_CMAC_256;
+			break;
+		case RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_GTK_NOT_USED;
+			break;
+		}
+	}
+}
+
+
+static void wiphy_info_max_roc(struct wpa_driver_capa *capa,
+			       struct nlattr *tb)
+{
+	if (tb)
+		capa->max_remain_on_chan = nla_get_u32(tb);
+}
+
+
+static void wiphy_info_tdls(struct wpa_driver_capa *capa, struct nlattr *tdls,
+			    struct nlattr *ext_setup)
+{
+	if (tdls == NULL)
+		return;
+
+	wpa_printf(MSG_DEBUG, "nl80211: TDLS supported");
+	capa->flags |= WPA_DRIVER_FLAGS_TDLS_SUPPORT;
+
+	if (ext_setup) {
+		wpa_printf(MSG_DEBUG, "nl80211: TDLS external setup");
+		capa->flags |= WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP;
+	}
+}
+
+
+static int ext_feature_isset(const u8 *ext_features, int ext_features_len,
+			     enum nl80211_ext_feature_index ftidx)
+{
+	u8 ft_byte;
+
+	if ((int) ftidx / 8 >= ext_features_len)
+		return 0;
+
+	ft_byte = ext_features[ftidx / 8];
+	return (ft_byte & BIT(ftidx % 8)) != 0;
+}
+
+
+static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info,
+					 struct nlattr *tb)
+{
+	struct wpa_driver_capa *capa = info->capa;
+	u8 *ext_features;
+	int len;
+
+	if (tb == NULL)
+		return;
+
+	ext_features = nla_data(tb);
+	len = nla_len(tb);
+
+	if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_VHT_IBSS))
+		capa->flags |= WPA_DRIVER_FLAGS_VHT_IBSS;
+
+	if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_RRM))
+		capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_RRM;
+
+	if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_FILS_STA))
+		capa->flags |= WPA_DRIVER_FLAGS_SUPPORT_FILS;
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_BEACON_RATE_LEGACY))
+		capa->flags |= WPA_DRIVER_FLAGS_BEACON_RATE_LEGACY;
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_BEACON_RATE_HT))
+		capa->flags |= WPA_DRIVER_FLAGS_BEACON_RATE_HT;
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_BEACON_RATE_VHT))
+		capa->flags |= WPA_DRIVER_FLAGS_BEACON_RATE_VHT;
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_BEACON_RATE_HE))
+		capa->flags2 |= WPA_DRIVER_FLAGS2_BEACON_RATE_HE;
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_SET_SCAN_DWELL))
+		capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL;
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_SCAN_START_TIME) &&
+	    ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_BSS_PARENT_TSF) &&
+	    ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_SET_SCAN_DWELL))
+		capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT;
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA))
+		capa->flags |= WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA;
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED))
+		capa->flags |= WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA_CONNECTED;
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI))
+		capa->flags |= WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI;
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_FILS_SK_OFFLOAD))
+		capa->flags |= WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD;
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK))
+		capa->flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK;
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X))
+		capa->flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X;
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_MFP_OPTIONAL))
+		capa->flags |= WPA_DRIVER_FLAGS_MFP_OPTIONAL;
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_DFS_OFFLOAD))
+		capa->flags |= WPA_DRIVER_FLAGS_DFS_OFFLOAD;
+
+#ifdef CONFIG_MBO
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME) &&
+	    ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP) &&
+	    ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE) &&
+	    ext_feature_isset(
+		    ext_features, len,
+		    NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION))
+		capa->flags |= WPA_DRIVER_FLAGS_OCE_STA;
+#endif /* CONFIG_MBO */
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER))
+		capa->flags |= WPA_DRIVER_FLAGS_FTM_RESPONDER;
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211))
+		capa->flags |= WPA_DRIVER_FLAGS_CONTROL_PORT;
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_CONTROL_PORT_NO_PREAUTH))
+		capa->flags2 |= WPA_DRIVER_FLAGS2_CONTROL_PORT_RX;
+	if (ext_feature_isset(
+		    ext_features, len,
+		    NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211_TX_STATUS))
+		capa->flags2 |= WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS;
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_VLAN_OFFLOAD))
+		capa->flags |= WPA_DRIVER_FLAGS_VLAN_OFFLOAD;
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_CAN_REPLACE_PTK0))
+		capa->flags |= WPA_DRIVER_FLAGS_SAFE_PTK0_REKEYS;
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_BEACON_PROTECTION))
+		capa->flags |= WPA_DRIVER_FLAGS_BEACON_PROTECTION;
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_EXT_KEY_ID))
+		capa->flags |= WPA_DRIVER_FLAGS_EXTENDED_KEY_ID;
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS))
+		info->drv->multicast_registrations = 1;
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_FILS_DISCOVERY))
+		info->drv->fils_discovery = 1;
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP))
+		info->drv->unsol_bcast_probe_resp = 1;
+
+	if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_PUNCT))
+		info->drv->puncturing = 1;
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT))
+		capa->flags2 |= WPA_DRIVER_FLAGS2_BEACON_PROTECTION_CLIENT;
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION))
+		capa->flags2 |= WPA_DRIVER_FLAGS2_OCV;
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_RADAR_BACKGROUND))
+		capa->flags2 |= WPA_DRIVER_RADAR_BACKGROUND;
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_SECURE_LTF)) {
+		capa->flags2 |= WPA_DRIVER_FLAGS2_SEC_LTF_STA;
+		capa->flags2 |= WPA_DRIVER_FLAGS2_SEC_LTF_AP;
+	}
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_SECURE_RTT)) {
+		capa->flags2 |= WPA_DRIVER_FLAGS2_SEC_RTT_STA;
+		capa->flags2 |= WPA_DRIVER_FLAGS2_SEC_RTT_AP;
+	}
+
+	if (ext_feature_isset(
+		    ext_features, len,
+		    NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE)) {
+		capa->flags2 |= WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA;
+		capa->flags2 |= WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP;
+	}
+}
+
+
+static void wiphy_info_feature_flags(struct wiphy_info_data *info,
+				     struct nlattr *tb)
+{
+	u32 flags;
+	struct wpa_driver_capa *capa = info->capa;
+
+	if (tb == NULL)
+		return;
+
+	flags = nla_get_u32(tb);
+
+	if (flags & NL80211_FEATURE_SK_TX_STATUS)
+		info->data_tx_status = 1;
+
+	if (flags & NL80211_FEATURE_INACTIVITY_TIMER)
+		capa->flags |= WPA_DRIVER_FLAGS_INACTIVITY_TIMER;
+
+	if (flags & NL80211_FEATURE_SAE)
+		capa->flags |= WPA_DRIVER_FLAGS_SAE;
+
+	if (flags & NL80211_FEATURE_NEED_OBSS_SCAN)
+		capa->flags |= WPA_DRIVER_FLAGS_OBSS_SCAN;
+
+	if (flags & NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE)
+		capa->flags |= WPA_DRIVER_FLAGS_HT_2040_COEX;
+
+	if (flags & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) {
+		wpa_printf(MSG_DEBUG, "nl80211: TDLS channel switch");
+		capa->flags |= WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH;
+	}
+
+	if (flags & NL80211_FEATURE_P2P_GO_CTWIN)
+		info->p2p_go_ctwindow_supported = 1;
+
+	if (flags & NL80211_FEATURE_LOW_PRIORITY_SCAN)
+		info->have_low_prio_scan = 1;
+
+	if (flags & NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR)
+		info->mac_addr_rand_scan_supported = 1;
+
+	if (flags & NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR)
+		info->mac_addr_rand_sched_scan_supported = 1;
+
+	if (flags & NL80211_FEATURE_SUPPORTS_WMM_ADMISSION)
+		info->wmm_ac_supported = 1;
+
+	if (flags & NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES)
+		capa->rrm_flags |= WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES;
+
+	if (flags & NL80211_FEATURE_WFA_TPC_IE_IN_PROBES)
+		capa->rrm_flags |= WPA_DRIVER_FLAGS_WFA_TPC_IE_IN_PROBES;
+
+	if (flags & NL80211_FEATURE_QUIET)
+		capa->rrm_flags |= WPA_DRIVER_FLAGS_QUIET;
+
+	if (flags & NL80211_FEATURE_TX_POWER_INSERTION)
+		capa->rrm_flags |= WPA_DRIVER_FLAGS_TX_POWER_INSERTION;
+
+	if (flags & NL80211_FEATURE_HT_IBSS)
+		capa->flags |= WPA_DRIVER_FLAGS_HT_IBSS;
+
+	if (flags & NL80211_FEATURE_FULL_AP_CLIENT_STATE)
+		capa->flags |= WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE;
+}
+
+
+static void wiphy_info_probe_resp_offload(struct wpa_driver_capa *capa,
+					  struct nlattr *tb)
+{
+	u32 protocols;
+
+	if (tb == NULL)
+		return;
+
+	protocols = nla_get_u32(tb);
+	wpa_printf(MSG_DEBUG, "nl80211: Supports Probe Response offload in AP "
+		   "mode");
+	capa->flags |= WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD;
+	capa->probe_resp_offloads = probe_resp_offload_support(protocols);
+}
+
+
+static void wiphy_info_wowlan_triggers(struct wpa_driver_capa *capa,
+				       struct nlattr *tb)
+{
+	struct nlattr *triggers[MAX_NL80211_WOWLAN_TRIG + 1];
+
+	if (tb == NULL)
+		return;
+
+	if (nla_parse_nested(triggers, MAX_NL80211_WOWLAN_TRIG,
+			     tb, NULL))
+		return;
+
+	if (triggers[NL80211_WOWLAN_TRIG_ANY])
+		capa->wowlan_triggers.any = 1;
+	if (triggers[NL80211_WOWLAN_TRIG_DISCONNECT])
+		capa->wowlan_triggers.disconnect = 1;
+	if (triggers[NL80211_WOWLAN_TRIG_MAGIC_PKT])
+		capa->wowlan_triggers.magic_pkt = 1;
+	if (triggers[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE])
+		capa->wowlan_triggers.gtk_rekey_failure = 1;
+	if (triggers[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST])
+		capa->wowlan_triggers.eap_identity_req = 1;
+	if (triggers[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE])
+		capa->wowlan_triggers.four_way_handshake = 1;
+	if (triggers[NL80211_WOWLAN_TRIG_RFKILL_RELEASE])
+		capa->wowlan_triggers.rfkill_release = 1;
+}
+
+
+static void wiphy_info_extended_capab(struct wpa_driver_nl80211_data *drv,
+				      struct nlattr *tb)
+{
+	int rem = 0, i;
+	struct nlattr *tb1[NL80211_ATTR_MAX + 1], *attr;
+
+	if (!tb || drv->num_iface_capa == NL80211_IFTYPE_MAX)
+		return;
+
+	nla_for_each_nested(attr, tb, rem) {
+		unsigned int len;
+		struct drv_nl80211_iface_capa *capa;
+
+		nla_parse(tb1, NL80211_ATTR_MAX, nla_data(attr),
+			  nla_len(attr), NULL);
+
+		if (!tb1[NL80211_ATTR_IFTYPE] ||
+		    !tb1[NL80211_ATTR_EXT_CAPA] ||
+		    !tb1[NL80211_ATTR_EXT_CAPA_MASK])
+			continue;
+
+		capa = &drv->iface_capa[drv->num_iface_capa];
+		capa->iftype = nla_get_u32(tb1[NL80211_ATTR_IFTYPE]);
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Driver-advertised extended capabilities for interface type %s",
+			   nl80211_iftype_str(capa->iftype));
+
+		len = nla_len(tb1[NL80211_ATTR_EXT_CAPA]);
+		capa->ext_capa = os_memdup(nla_data(tb1[NL80211_ATTR_EXT_CAPA]),
+					   len);
+		if (!capa->ext_capa)
+			goto err;
+
+		capa->ext_capa_len = len;
+		wpa_hexdump(MSG_DEBUG, "nl80211: Extended capabilities",
+			    capa->ext_capa, capa->ext_capa_len);
+
+		len = nla_len(tb1[NL80211_ATTR_EXT_CAPA_MASK]);
+		capa->ext_capa_mask =
+			os_memdup(nla_data(tb1[NL80211_ATTR_EXT_CAPA_MASK]),
+				  len);
+		if (!capa->ext_capa_mask)
+			goto err;
+
+		wpa_hexdump(MSG_DEBUG, "nl80211: Extended capabilities mask",
+			    capa->ext_capa_mask, capa->ext_capa_len);
+
+		if (tb1[NL80211_ATTR_EML_CAPABILITY] &&
+		    tb1[NL80211_ATTR_MLD_CAPA_AND_OPS]) {
+			capa->eml_capa =
+				nla_get_u16(tb1[NL80211_ATTR_EML_CAPABILITY]);
+			capa->mld_capa_and_ops =
+				nla_get_u16(tb1[NL80211_ATTR_MLD_CAPA_AND_OPS]);
+		}
+
+		drv->num_iface_capa++;
+		if (drv->num_iface_capa == NL80211_IFTYPE_MAX)
+			break;
+	}
+
+	return;
+
+err:
+	/* Cleanup allocated memory on error */
+	for (i = 0; i < NL80211_IFTYPE_MAX; i++) {
+		os_free(drv->iface_capa[i].ext_capa);
+		drv->iface_capa[i].ext_capa = NULL;
+		os_free(drv->iface_capa[i].ext_capa_mask);
+		drv->iface_capa[i].ext_capa_mask = NULL;
+		drv->iface_capa[i].ext_capa_len = 0;
+	}
+	drv->num_iface_capa = 0;
+}
+
+
+static void wiphy_info_mbssid(struct wpa_driver_capa *cap, struct nlattr *attr)
+{
+	struct nlattr *config[NL80211_MBSSID_CONFIG_ATTR_MAX + 1];
+
+	if (nla_parse_nested(config, NL80211_MBSSID_CONFIG_ATTR_MAX, attr,
+			     NULL))
+		return;
+
+	if (!config[NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES])
+		return;
+
+	cap->mbssid_max_interfaces =
+		nla_get_u8(config[NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES]);
+
+	if (config[NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY])
+		cap->ema_max_periodicity =
+			nla_get_u8(config[NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY]);
+
+	wpa_printf(MSG_DEBUG,
+		   "mbssid: max interfaces %u, max profile periodicity %u",
+		   cap->mbssid_max_interfaces, cap->ema_max_periodicity);
+}
+
+
+static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct wiphy_info_data *info = arg;
+	struct wpa_driver_capa *capa = info->capa;
+	struct wpa_driver_nl80211_data *drv = info->drv;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (tb[NL80211_ATTR_WIPHY])
+		drv->wiphy_idx = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
+
+	if (tb[NL80211_ATTR_WIPHY_NAME])
+		os_strlcpy(drv->phyname,
+			   nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]),
+			   sizeof(drv->phyname));
+	if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS])
+		capa->max_scan_ssids =
+			nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]);
+
+	if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS])
+		capa->max_sched_scan_ssids =
+			nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]);
+
+	if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS] &&
+	    tb[NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL] &&
+	    tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]) {
+		capa->max_sched_scan_plans =
+			nla_get_u32(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS]);
+
+		capa->max_sched_scan_plan_interval =
+			nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL]);
+
+		capa->max_sched_scan_plan_iterations =
+			nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]);
+	}
+
+	if (tb[NL80211_ATTR_MAX_MATCH_SETS])
+		capa->max_match_sets =
+			nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]);
+
+	if (tb[NL80211_ATTR_MAC_ACL_MAX])
+		capa->max_acl_mac_addrs =
+			nla_get_u32(tb[NL80211_ATTR_MAC_ACL_MAX]);
+
+	wiphy_info_supported_iftypes(info, tb[NL80211_ATTR_SUPPORTED_IFTYPES]);
+	wiphy_info_iface_comb(info, tb[NL80211_ATTR_INTERFACE_COMBINATIONS]);
+	wiphy_info_supp_cmds(info, tb[NL80211_ATTR_SUPPORTED_COMMANDS]);
+	wiphy_info_cipher_suites(info, tb[NL80211_ATTR_CIPHER_SUITES]);
+	wiphy_info_akm_suites(info, tb[NL80211_ATTR_AKM_SUITES]);
+	wiphy_info_iftype_akm_suites(info, tb[NL80211_ATTR_IFTYPE_AKM_SUITES]);
+
+	if (tb[NL80211_ATTR_OFFCHANNEL_TX_OK]) {
+		wpa_printf(MSG_DEBUG, "nl80211: Using driver-based "
+			   "off-channel TX");
+		capa->flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_TX;
+	}
+
+	if (tb[NL80211_ATTR_ROAM_SUPPORT]) {
+		wpa_printf(MSG_DEBUG, "nl80211: Using driver-based roaming");
+		capa->flags |= WPA_DRIVER_FLAGS_BSS_SELECTION;
+	}
+
+	wiphy_info_max_roc(capa,
+			   tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]);
+
+	if (tb[NL80211_ATTR_SUPPORT_AP_UAPSD])
+		capa->flags |= WPA_DRIVER_FLAGS_AP_UAPSD;
+
+	wiphy_info_tdls(capa, tb[NL80211_ATTR_TDLS_SUPPORT],
+			tb[NL80211_ATTR_TDLS_EXTERNAL_SETUP]);
+
+	if (tb[NL80211_ATTR_DEVICE_AP_SME]) {
+		u32 ap_sme_features_flags =
+			nla_get_u32(tb[NL80211_ATTR_DEVICE_AP_SME]);
+
+		if (ap_sme_features_flags & NL80211_AP_SME_SA_QUERY_OFFLOAD)
+			capa->flags2 |= WPA_DRIVER_FLAGS2_SA_QUERY_OFFLOAD_AP;
+
+		info->device_ap_sme = 1;
+	}
+
+	wiphy_info_feature_flags(info, tb[NL80211_ATTR_FEATURE_FLAGS]);
+	wiphy_info_ext_feature_flags(info, tb[NL80211_ATTR_EXT_FEATURES]);
+	wiphy_info_probe_resp_offload(capa,
+				      tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]);
+
+	if (tb[NL80211_ATTR_EXT_CAPA] && tb[NL80211_ATTR_EXT_CAPA_MASK] &&
+	    drv->extended_capa == NULL) {
+		drv->extended_capa =
+			os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA]));
+		if (drv->extended_capa) {
+			os_memcpy(drv->extended_capa,
+				  nla_data(tb[NL80211_ATTR_EXT_CAPA]),
+				  nla_len(tb[NL80211_ATTR_EXT_CAPA]));
+			drv->extended_capa_len =
+				nla_len(tb[NL80211_ATTR_EXT_CAPA]);
+			wpa_hexdump(MSG_DEBUG,
+				    "nl80211: Driver-advertised extended capabilities (default)",
+				    drv->extended_capa, drv->extended_capa_len);
+		}
+		drv->extended_capa_mask =
+			os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK]));
+		if (drv->extended_capa_mask) {
+			os_memcpy(drv->extended_capa_mask,
+				  nla_data(tb[NL80211_ATTR_EXT_CAPA_MASK]),
+				  nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK]));
+			wpa_hexdump(MSG_DEBUG,
+				    "nl80211: Driver-advertised extended capabilities mask (default)",
+				    drv->extended_capa_mask,
+				    drv->extended_capa_len);
+		} else {
+			os_free(drv->extended_capa);
+			drv->extended_capa = NULL;
+			drv->extended_capa_len = 0;
+		}
+	}
+
+	wiphy_info_extended_capab(drv, tb[NL80211_ATTR_IFTYPE_EXT_CAPA]);
+
+	if (tb[NL80211_ATTR_VENDOR_DATA]) {
+		struct nlattr *nl;
+		int rem;
+
+		nla_for_each_nested(nl, tb[NL80211_ATTR_VENDOR_DATA], rem) {
+			struct nl80211_vendor_cmd_info *vinfo;
+			if (nla_len(nl) != sizeof(*vinfo)) {
+				wpa_printf(MSG_DEBUG, "nl80211: Unexpected vendor data info");
+				continue;
+			}
+			vinfo = nla_data(nl);
+			if (vinfo->vendor_id == OUI_QCA) {
+				switch (vinfo->subcmd) {
+				case QCA_NL80211_VENDOR_SUBCMD_TEST:
+					drv->vendor_cmd_test_avail = 1;
+					break;
+#ifdef CONFIG_DRIVER_NL80211_QCA
+				case QCA_NL80211_VENDOR_SUBCMD_ROAMING:
+					drv->roaming_vendor_cmd_avail = 1;
+					break;
+				case QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY:
+					drv->dfs_vendor_cmd_avail = 1;
+					break;
+				case QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES:
+					drv->get_features_vendor_cmd_avail = 1;
+					break;
+				case QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST:
+					drv->get_pref_freq_list = 1;
+					break;
+				case QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL:
+					drv->set_prob_oper_freq = 1;
+					break;
+				case QCA_NL80211_VENDOR_SUBCMD_DO_ACS:
+					drv->capa.flags |=
+						WPA_DRIVER_FLAGS_ACS_OFFLOAD;
+					drv->qca_do_acs = 1;
+					break;
+				case QCA_NL80211_VENDOR_SUBCMD_SETBAND:
+					drv->setband_vendor_cmd_avail = 1;
+					break;
+				case QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN:
+					drv->scan_vendor_cmd_avail = 1;
+					break;
+				case QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION:
+					drv->set_wifi_conf_vendor_cmd_avail = 1;
+					break;
+				case QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS:
+					drv->fetch_bss_trans_status = 1;
+					break;
+				case QCA_NL80211_VENDOR_SUBCMD_ROAM:
+					drv->roam_vendor_cmd_avail = 1;
+					break;
+				case QCA_NL80211_VENDOR_SUBCMD_ADD_STA_NODE:
+					drv->add_sta_node_vendor_cmd_avail = 1;
+					break;
+				case QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO:
+					drv->get_sta_info_vendor_cmd_avail = 1;
+					break;
+				case QCA_NL80211_VENDOR_SUBCMD_SECURE_RANGING_CONTEXT:
+					drv->secure_ranging_ctx_vendor_cmd_avail = 1;
+					break;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+				}
+#ifdef CONFIG_DRIVER_NL80211_BRCM
+			} else if (vinfo->vendor_id == OUI_BRCM) {
+				switch (vinfo->subcmd) {
+				case BRCM_VENDOR_SCMD_ACS:
+					drv->capa.flags |=
+						WPA_DRIVER_FLAGS_ACS_OFFLOAD;
+					wpa_printf(MSG_DEBUG,
+						   "Enabled BRCM ACS");
+					drv->brcm_do_acs = 1;
+					break;
+				}
+#endif /* CONFIG_DRIVER_NL80211_BRCM */
+			}
+
+			wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u",
+				   vinfo->vendor_id, vinfo->subcmd);
+		}
+	}
+
+	if (tb[NL80211_ATTR_VENDOR_EVENTS]) {
+		struct nlattr *nl;
+		int rem;
+
+		nla_for_each_nested(nl, tb[NL80211_ATTR_VENDOR_EVENTS], rem) {
+			struct nl80211_vendor_cmd_info *vinfo;
+			if (nla_len(nl) != sizeof(*vinfo)) {
+				wpa_printf(MSG_DEBUG, "nl80211: Unexpected vendor data info");
+				continue;
+			}
+			vinfo = nla_data(nl);
+			wpa_printf(MSG_DEBUG, "nl80211: Supported vendor event: vendor_id=0x%x subcmd=%u",
+				   vinfo->vendor_id, vinfo->subcmd);
+		}
+	}
+
+	wiphy_info_wowlan_triggers(capa,
+				   tb[NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED]);
+
+	if (tb[NL80211_ATTR_MAX_AP_ASSOC_STA])
+		capa->max_stations =
+			nla_get_u32(tb[NL80211_ATTR_MAX_AP_ASSOC_STA]);
+
+	if (tb[NL80211_ATTR_MAX_CSA_COUNTERS])
+		capa->max_csa_counters =
+			nla_get_u8(tb[NL80211_ATTR_MAX_CSA_COUNTERS]);
+
+	if (tb[NL80211_ATTR_WIPHY_SELF_MANAGED_REG])
+		capa->flags |= WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY;
+
+	if (tb[NL80211_ATTR_MAX_NUM_AKM_SUITES])
+		capa->max_num_akms =
+			nla_get_u16(tb[NL80211_ATTR_MAX_NUM_AKM_SUITES]);
+
+	if (tb[NL80211_ATTR_MBSSID_CONFIG])
+		wiphy_info_mbssid(capa, tb[NL80211_ATTR_MBSSID_CONFIG]);
+
+	if (tb[NL80211_ATTR_MLO_SUPPORT])
+		capa->flags2 |= WPA_DRIVER_FLAGS2_MLO;
+
+	return NL_SKIP;
+}
+
+
+static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv,
+				       struct wiphy_info_data *info)
+{
+	u32 feat;
+	struct nl_msg *msg;
+	int flags = 0;
+
+	os_memset(info, 0, sizeof(*info));
+	info->capa = &drv->capa;
+	info->drv = drv;
+
+	feat = get_nl80211_protocol_features(drv);
+	if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)
+		flags = NLM_F_DUMP;
+	msg = nl80211_cmd_msg(drv->first_bss, flags, NL80211_CMD_GET_WIPHY);
+	if (!msg || nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP)) {
+		nlmsg_free(msg);
+		return -1;
+	}
+
+	if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info, NULL, NULL))
+		return -1;
+
+	if (info->auth_supported)
+		drv->capa.flags |= WPA_DRIVER_FLAGS_SME;
+	else if (!info->connect_supported) {
+		wpa_printf(MSG_INFO, "nl80211: Driver does not support "
+			   "authentication/association or connect commands");
+		info->error = 1;
+	}
+
+	if (info->p2p_go_supported && info->p2p_client_supported)
+		drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE;
+	if (info->p2p_concurrent) {
+		wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group "
+			   "interface (driver advertised support)");
+		drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
+		drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P;
+	}
+	if (info->num_multichan_concurrent > 1) {
+		wpa_printf(MSG_DEBUG, "nl80211: Enable multi-channel "
+			   "concurrent (driver advertised support)");
+		drv->capa.num_multichan_concurrent =
+			info->num_multichan_concurrent;
+	}
+	if (drv->capa.flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)
+		wpa_printf(MSG_DEBUG, "nl80211: use P2P_DEVICE support");
+
+	/* default to 5000 since early versions of mac80211 don't set it */
+	if (!drv->capa.max_remain_on_chan)
+		drv->capa.max_remain_on_chan = 5000;
+
+	drv->capa.wmm_ac_supported = info->wmm_ac_supported;
+
+	drv->capa.mac_addr_rand_sched_scan_supported =
+		info->mac_addr_rand_sched_scan_supported;
+	drv->capa.mac_addr_rand_scan_supported =
+		info->mac_addr_rand_scan_supported;
+
+	if (info->channel_switch_supported) {
+		drv->capa.flags |= WPA_DRIVER_FLAGS_AP_CSA;
+		if (!drv->capa.max_csa_counters)
+			drv->capa.max_csa_counters = 1;
+	}
+
+	if (!drv->capa.max_sched_scan_plans) {
+		drv->capa.max_sched_scan_plans = 1;
+		drv->capa.max_sched_scan_plan_interval = UINT32_MAX;
+		drv->capa.max_sched_scan_plan_iterations = 0;
+	}
+
+	if (info->update_ft_ies_supported)
+		drv->capa.flags |= WPA_DRIVER_FLAGS_UPDATE_FT_IES;
+
+	if (!drv->capa.max_num_akms)
+		drv->capa.max_num_akms = NL80211_MAX_NR_AKM_SUITES;
+
+	return 0;
+}
+
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+
+static int dfs_info_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	int *dfs_capability_ptr = arg;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (tb[NL80211_ATTR_VENDOR_DATA]) {
+		struct nlattr *nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
+		struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+
+		nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX,
+			  nla_data(nl_vend), nla_len(nl_vend), NULL);
+
+		if (tb_vendor[QCA_WLAN_VENDOR_ATTR_DFS]) {
+			u32 val;
+			val = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_DFS]);
+			wpa_printf(MSG_DEBUG, "nl80211: DFS offload capability: %u",
+				   val);
+			*dfs_capability_ptr = val;
+		}
+	}
+
+	return NL_SKIP;
+}
+
+
+static void qca_nl80211_check_dfs_capa(struct wpa_driver_nl80211_data *drv)
+{
+	struct nl_msg *msg;
+	int dfs_capability = 0;
+	int ret;
+
+	if (!drv->dfs_vendor_cmd_avail)
+		return;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY)) {
+		nlmsg_free(msg);
+		return;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, dfs_info_handler, &dfs_capability,
+				 NULL, NULL);
+	if (!ret && dfs_capability)
+		drv->capa.flags |= WPA_DRIVER_FLAGS_DFS_OFFLOAD;
+}
+
+
+struct features_info {
+	u8 *flags;
+	size_t flags_len;
+	struct wpa_driver_capa *capa;
+};
+
+
+static int features_info_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct features_info *info = arg;
+	struct nlattr *nl_vend, *attr;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
+	if (nl_vend) {
+		struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+
+		nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX,
+			  nla_data(nl_vend), nla_len(nl_vend), NULL);
+
+		attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS];
+		if (attr) {
+			int len = nla_len(attr);
+			info->flags = os_malloc(len);
+			if (info->flags != NULL) {
+				os_memcpy(info->flags, nla_data(attr), len);
+				info->flags_len = len;
+			}
+		}
+		attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_CONCURRENCY_CAPA];
+		if (attr)
+			info->capa->conc_capab = nla_get_u32(attr);
+
+		attr = tb_vendor[
+			QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_2_4_BAND];
+		if (attr)
+			info->capa->max_conc_chan_2_4 = nla_get_u32(attr);
+
+		attr = tb_vendor[
+			QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_5_0_BAND];
+		if (attr)
+			info->capa->max_conc_chan_5_0 = nla_get_u32(attr);
+	}
+
+	return NL_SKIP;
+}
+
+
+static int check_feature(enum qca_wlan_vendor_features feature,
+			 struct features_info *info)
+{
+	size_t idx = feature / 8;
+
+	return (idx < info->flags_len) &&
+		(info->flags[idx] & BIT(feature % 8));
+}
+
+
+static void qca_nl80211_get_features(struct wpa_driver_nl80211_data *drv)
+{
+	struct nl_msg *msg;
+	struct features_info info;
+	int ret;
+
+	if (!drv->get_features_vendor_cmd_avail)
+		return;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES)) {
+		nlmsg_free(msg);
+		return;
+	}
+
+	os_memset(&info, 0, sizeof(info));
+	info.capa = &drv->capa;
+	ret = send_and_recv_msgs(drv, msg, features_info_handler, &info,
+				 NULL, NULL);
+	if (ret || !info.flags)
+		return;
+
+	if (check_feature(QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD, &info))
+		drv->capa.flags |= WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD;
+
+	if (check_feature(QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY, &info))
+		drv->capa.flags |= WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY;
+
+	if (check_feature(QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS,
+			  &info))
+		drv->capa.flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS;
+	if (check_feature(QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD, &info))
+		drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD;
+	if (check_feature(QCA_WLAN_VENDOR_FEATURE_OCE_STA, &info))
+		drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_STA;
+	if (check_feature(QCA_WLAN_VENDOR_FEATURE_OCE_AP, &info))
+		drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_AP;
+	if (check_feature(QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON, &info))
+		drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_STA_CFON;
+	if (check_feature(QCA_WLAN_VENDOR_FEATURE_SECURE_LTF_STA, &info))
+		drv->capa.flags2 |= WPA_DRIVER_FLAGS2_SEC_LTF_STA;
+	if (check_feature(QCA_WLAN_VENDOR_FEATURE_SECURE_LTF_AP, &info))
+		drv->capa.flags2 |= WPA_DRIVER_FLAGS2_SEC_LTF_AP;
+	if (check_feature(QCA_WLAN_VENDOR_FEATURE_SECURE_RTT_STA, &info))
+		drv->capa.flags2 |= WPA_DRIVER_FLAGS2_SEC_RTT_STA;
+	if (check_feature(QCA_WLAN_VENDOR_FEATURE_SECURE_RTT_AP, &info))
+		drv->capa.flags2 |= WPA_DRIVER_FLAGS2_SEC_RTT_AP;
+	if (check_feature(
+		    QCA_WLAN_VENDOR_FEATURE_PROT_RANGE_NEGO_AND_MEASURE_STA,
+		    &info))
+		drv->capa.flags2 |= WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA;
+	if (check_feature(
+		    QCA_WLAN_VENDOR_FEATURE_PROT_RANGE_NEGO_AND_MEASURE_AP,
+		    &info))
+		drv->capa.flags2 |= WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP;
+	if (check_feature(QCA_WLAN_VENDOR_FEATURE_AP_ALLOWED_FREQ_LIST,
+			  &info))
+		drv->qca_ap_allowed_freqs = 1;
+	os_free(info.flags);
+}
+
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
+int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
+{
+	struct wiphy_info_data info;
+	int i;
+
+	if (wpa_driver_nl80211_get_info(drv, &info))
+		return -1;
+
+	if (info.error)
+		return -1;
+
+	drv->has_capability = 1;
+	drv->has_driver_key_mgmt = info.has_key_mgmt | info.has_key_mgmt_iftype;
+
+	/* Fallback to hardcoded defaults if the driver does not advertise any
+	 * AKM capabilities. */
+	if (!drv->has_driver_key_mgmt) {
+		drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+			WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+			WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+			WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK |
+			WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B |
+			WPA_DRIVER_CAPA_KEY_MGMT_OWE |
+			WPA_DRIVER_CAPA_KEY_MGMT_DPP;
+
+		if (drv->capa.enc & (WPA_DRIVER_CAPA_ENC_CCMP_256 |
+				     WPA_DRIVER_CAPA_ENC_GCMP_256))
+			drv->capa.key_mgmt |=
+				WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192;
+
+		if (drv->capa.flags & WPA_DRIVER_FLAGS_SME)
+			drv->capa.key_mgmt |=
+				WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 |
+				WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 |
+				WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 |
+				WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384 |
+				WPA_DRIVER_CAPA_KEY_MGMT_SAE;
+		else if (drv->capa.flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD)
+			drv->capa.key_mgmt |=
+				WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 |
+				WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384;
+	}
+
+	if (!info.has_key_mgmt_iftype) {
+		/* If the driver does not advertize per interface AKM
+		 * capabilities, consider all interfaces to support default AKMs
+		 * in key_mgmt. */
+		for (i = 0; i < WPA_IF_MAX; i++)
+			drv->capa.key_mgmt_iftype[i] = drv->capa.key_mgmt;
+	} else if (info.has_key_mgmt_iftype && !info.has_key_mgmt) {
+		/* If the driver advertizes only per interface supported AKMs
+		 * but does not advertize per wiphy AKM capabilities, consider
+		 * the default key_mgmt as a mask of per interface supported
+		 * AKMs. */
+		drv->capa.key_mgmt = 0;
+		for (i = 0; i < WPA_IF_MAX; i++)
+			drv->capa.key_mgmt |= drv->capa.key_mgmt_iftype[i];
+	} else if (info.has_key_mgmt_iftype && info.has_key_mgmt) {
+		/* If the driver advertizes AKM capabilities both per wiphy and
+		 * per interface, consider the interfaces for which per
+		 * interface AKM capabilities were not received to support the
+		 * default key_mgmt capabilities.
+		 */
+		for (i = 0; i < WPA_IF_MAX; i++)
+			if (!drv->capa.key_mgmt_iftype[i])
+				drv->capa.key_mgmt_iftype[i] =
+					drv->capa.key_mgmt;
+	}
+
+	drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
+		WPA_DRIVER_AUTH_SHARED |
+		WPA_DRIVER_AUTH_LEAP;
+
+	drv->capa.flags |= WPA_DRIVER_FLAGS_VALID_ERROR_CODES;
+	drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE;
+	drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
+
+	/*
+	 * As all cfg80211 drivers must support cases where the AP interface is
+	 * removed without the knowledge of wpa_supplicant/hostapd, e.g., in
+	 * case that the user space daemon has crashed, they must be able to
+	 * cleanup all stations and key entries in the AP tear down flow. Thus,
+	 * this flag can/should always be set for cfg80211 drivers.
+	 */
+	drv->capa.flags |= WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT;
+
+	if (!info.device_ap_sme) {
+		drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS;
+		drv->capa.flags2 |= WPA_DRIVER_FLAGS2_AP_SME;
+
+		/*
+		 * No AP SME is currently assumed to also indicate no AP MLME
+		 * in the driver/firmware.
+		 */
+		drv->capa.flags |= WPA_DRIVER_FLAGS_AP_MLME;
+	}
+
+	drv->device_ap_sme = info.device_ap_sme;
+	drv->poll_command_supported = info.poll_command_supported;
+	drv->data_tx_status = info.data_tx_status;
+	drv->p2p_go_ctwindow_supported = info.p2p_go_ctwindow_supported;
+	if (info.set_qos_map_supported)
+		drv->capa.flags |= WPA_DRIVER_FLAGS_QOS_MAPPING;
+	drv->have_low_prio_scan = info.have_low_prio_scan;
+
+	/*
+	 * If poll command and tx status are supported, mac80211 is new enough
+	 * to have everything we need to not need monitor interfaces.
+	 */
+	drv->use_monitor = !info.device_ap_sme &&
+		(!info.poll_command_supported || !info.data_tx_status);
+
+	/*
+	 * If we aren't going to use monitor interfaces, but the
+	 * driver doesn't support data TX status, we won't get TX
+	 * status for EAPOL frames.
+	 */
+	if (!drv->use_monitor && !info.data_tx_status)
+		drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+	if (!(info.capa->flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD))
+		qca_nl80211_check_dfs_capa(drv);
+	qca_nl80211_get_features(drv);
+
+	/*
+	 * To enable offchannel simultaneous support in wpa_supplicant, the
+	 * underlying driver needs to support the same along with offchannel TX.
+	 * Offchannel TX support is needed since remain_on_channel and
+	 * action_tx use some common data structures and hence cannot be
+	 * scheduled simultaneously.
+	 */
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX))
+		drv->capa.flags &= ~WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: key_mgmt=0x%x enc=0x%x auth=0x%x flags=0x%llx rrm_flags=0x%x probe_resp_offloads=0x%x max_stations=%u max_remain_on_chan=%u max_scan_ssids=%d",
+		   drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth,
+		   (unsigned long long) drv->capa.flags, drv->capa.rrm_flags,
+		   drv->capa.probe_resp_offloads, drv->capa.max_stations,
+		   drv->capa.max_remain_on_chan, drv->capa.max_scan_ssids);
+	return 0;
+}
+
+
+struct phy_info_arg {
+	u16 *num_modes;
+	struct hostapd_hw_modes *modes;
+	int last_mode, last_chan_idx;
+	int failed;
+	u8 dfs_domain;
+};
+
+static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa,
+			     struct nlattr *ampdu_factor,
+			     struct nlattr *ampdu_density,
+			     struct nlattr *mcs_set)
+{
+	if (capa)
+		mode->ht_capab = nla_get_u16(capa);
+
+	if (ampdu_factor)
+		mode->a_mpdu_params |= nla_get_u8(ampdu_factor) & 0x03;
+
+	if (ampdu_density)
+		mode->a_mpdu_params |= nla_get_u8(ampdu_density) << 2;
+
+	if (mcs_set && nla_len(mcs_set) >= 16) {
+		u8 *mcs;
+		mcs = nla_data(mcs_set);
+		os_memcpy(mode->mcs_set, mcs, 16);
+	}
+}
+
+
+static void phy_info_vht_capa(struct hostapd_hw_modes *mode,
+			      struct nlattr *capa,
+			      struct nlattr *mcs_set)
+{
+	if (capa)
+		mode->vht_capab = nla_get_u32(capa);
+
+	if (mcs_set && nla_len(mcs_set) >= 8) {
+		u8 *mcs;
+		mcs = nla_data(mcs_set);
+		os_memcpy(mode->vht_mcs_set, mcs, 8);
+	}
+}
+
+
+static int phy_info_edmg_capa(struct hostapd_hw_modes *mode,
+			      struct nlattr *bw_config,
+			      struct nlattr *channels)
+{
+	if (!bw_config || !channels)
+		return NL_OK;
+
+	mode->edmg.bw_config = nla_get_u8(bw_config);
+	mode->edmg.channels = nla_get_u8(channels);
+
+	if (!mode->edmg.channels || !mode->edmg.bw_config)
+		return NL_STOP;
+
+	return NL_OK;
+}
+
+
+static int cw2ecw(unsigned int cw)
+{
+	int bit;
+
+	if (cw == 0)
+		return 0;
+
+	for (bit = 1; cw != 1; bit++)
+		cw >>= 1;
+
+	return bit;
+}
+
+
+static void phy_info_freq(struct hostapd_hw_modes *mode,
+			  struct hostapd_channel_data *chan,
+			  struct nlattr *tb_freq[])
+{
+	u8 channel;
+
+	os_memset(chan, 0, sizeof(*chan));
+	chan->freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
+	chan->flag = 0;
+	chan->allowed_bw = ~0;
+	chan->dfs_cac_ms = 0;
+	if (ieee80211_freq_to_chan(chan->freq, &channel) != NUM_HOSTAPD_MODES)
+		chan->chan = channel;
+	else
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: No channel number found for frequency %u MHz",
+			   chan->freq);
+
+	if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
+		chan->flag |= HOSTAPD_CHAN_DISABLED;
+	if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IR])
+		chan->flag |= HOSTAPD_CHAN_NO_IR;
+	if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR])
+		chan->flag |= HOSTAPD_CHAN_RADAR;
+	if (tb_freq[NL80211_FREQUENCY_ATTR_INDOOR_ONLY])
+		chan->flag |= HOSTAPD_CHAN_INDOOR_ONLY;
+	if (tb_freq[NL80211_FREQUENCY_ATTR_GO_CONCURRENT])
+		chan->flag |= HOSTAPD_CHAN_GO_CONCURRENT;
+
+	if (tb_freq[NL80211_FREQUENCY_ATTR_NO_10MHZ])
+		chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_10;
+	if (tb_freq[NL80211_FREQUENCY_ATTR_NO_20MHZ])
+		chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_20;
+	if (tb_freq[NL80211_FREQUENCY_ATTR_NO_HT40_PLUS])
+		chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_40P;
+	if (tb_freq[NL80211_FREQUENCY_ATTR_NO_HT40_MINUS])
+		chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_40M;
+	if (tb_freq[NL80211_FREQUENCY_ATTR_NO_80MHZ])
+		chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_80;
+	if (tb_freq[NL80211_FREQUENCY_ATTR_NO_160MHZ])
+		chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_160;
+
+	if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) {
+		enum nl80211_dfs_state state =
+			nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]);
+
+		switch (state) {
+		case NL80211_DFS_USABLE:
+			chan->flag |= HOSTAPD_CHAN_DFS_USABLE;
+			break;
+		case NL80211_DFS_AVAILABLE:
+			chan->flag |= HOSTAPD_CHAN_DFS_AVAILABLE;
+			break;
+		case NL80211_DFS_UNAVAILABLE:
+			chan->flag |= HOSTAPD_CHAN_DFS_UNAVAILABLE;
+			break;
+		}
+	}
+
+	if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]) {
+		chan->dfs_cac_ms = nla_get_u32(
+			tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]);
+	}
+
+	chan->wmm_rules_valid = 0;
+	if (tb_freq[NL80211_FREQUENCY_ATTR_WMM]) {
+		static struct nla_policy wmm_policy[NL80211_WMMR_MAX + 1] = {
+			[NL80211_WMMR_CW_MIN] = { .type = NLA_U16 },
+			[NL80211_WMMR_CW_MAX] = { .type = NLA_U16 },
+			[NL80211_WMMR_AIFSN] = { .type = NLA_U8 },
+			[NL80211_WMMR_TXOP] = { .type = NLA_U16 },
+		};
+		static const u8 wmm_map[4] = {
+			[NL80211_AC_BE] = WMM_AC_BE,
+			[NL80211_AC_BK] = WMM_AC_BK,
+			[NL80211_AC_VI] = WMM_AC_VI,
+			[NL80211_AC_VO] = WMM_AC_VO,
+		};
+		struct nlattr *nl_wmm;
+		struct nlattr *tb_wmm[NL80211_WMMR_MAX + 1];
+		int rem_wmm, ac, count = 0;
+
+		nla_for_each_nested(nl_wmm, tb_freq[NL80211_FREQUENCY_ATTR_WMM],
+				    rem_wmm) {
+			if (nla_parse_nested(tb_wmm, NL80211_WMMR_MAX, nl_wmm,
+					     wmm_policy)) {
+				wpa_printf(MSG_DEBUG,
+					   "nl80211: Failed to parse WMM rules attribute");
+				return;
+			}
+			if (!tb_wmm[NL80211_WMMR_CW_MIN] ||
+			    !tb_wmm[NL80211_WMMR_CW_MAX] ||
+			    !tb_wmm[NL80211_WMMR_AIFSN] ||
+			    !tb_wmm[NL80211_WMMR_TXOP]) {
+				wpa_printf(MSG_DEBUG,
+					   "nl80211: Channel is missing WMM rule attribute");
+				return;
+			}
+			ac = nl_wmm->nla_type;
+			if ((unsigned int) ac >= ARRAY_SIZE(wmm_map)) {
+				wpa_printf(MSG_DEBUG,
+					   "nl80211: Invalid AC value %d", ac);
+				return;
+			}
+
+			ac = wmm_map[ac];
+			chan->wmm_rules[ac].min_cwmin =
+				cw2ecw(nla_get_u16(
+					       tb_wmm[NL80211_WMMR_CW_MIN]));
+			chan->wmm_rules[ac].min_cwmax =
+				cw2ecw(nla_get_u16(
+					       tb_wmm[NL80211_WMMR_CW_MAX]));
+			chan->wmm_rules[ac].min_aifs =
+				nla_get_u8(tb_wmm[NL80211_WMMR_AIFSN]);
+			chan->wmm_rules[ac].max_txop =
+				nla_get_u16(tb_wmm[NL80211_WMMR_TXOP]) / 32;
+			count++;
+		}
+
+		/* Set valid flag if all the AC rules are present */
+		if (count == WMM_AC_NUM)
+			chan->wmm_rules_valid = 1;
+	}
+}
+
+
+static int phy_info_freqs(struct phy_info_arg *phy_info,
+			  struct hostapd_hw_modes *mode, struct nlattr *tb)
+{
+	static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
+		[NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
+		[NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG },
+		[NL80211_FREQUENCY_ATTR_NO_IR] = { .type = NLA_FLAG },
+		[NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG },
+		[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
+		[NL80211_FREQUENCY_ATTR_DFS_STATE] = { .type = NLA_U32 },
+		[NL80211_FREQUENCY_ATTR_NO_10MHZ] = { .type = NLA_FLAG },
+		[NL80211_FREQUENCY_ATTR_NO_20MHZ] = { .type = NLA_FLAG },
+		[NL80211_FREQUENCY_ATTR_NO_HT40_PLUS] = { .type = NLA_FLAG },
+		[NL80211_FREQUENCY_ATTR_NO_HT40_MINUS] = { .type = NLA_FLAG },
+		[NL80211_FREQUENCY_ATTR_NO_80MHZ] = { .type = NLA_FLAG },
+		[NL80211_FREQUENCY_ATTR_NO_160MHZ] = { .type = NLA_FLAG },
+	};
+	int new_channels = 0;
+	struct hostapd_channel_data *channel;
+	struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
+	struct nlattr *nl_freq;
+	int rem_freq, idx;
+
+	if (tb == NULL)
+		return NL_OK;
+
+	nla_for_each_nested(nl_freq, tb, rem_freq) {
+		nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX,
+			  nla_data(nl_freq), nla_len(nl_freq), freq_policy);
+		if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
+			continue;
+		new_channels++;
+	}
+
+	channel = os_realloc_array(mode->channels,
+				   mode->num_channels + new_channels,
+				   sizeof(struct hostapd_channel_data));
+	if (!channel)
+		return NL_STOP;
+
+	mode->channels = channel;
+	mode->num_channels += new_channels;
+
+	idx = phy_info->last_chan_idx;
+
+	nla_for_each_nested(nl_freq, tb, rem_freq) {
+		nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX,
+			  nla_data(nl_freq), nla_len(nl_freq), freq_policy);
+		if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
+			continue;
+		phy_info_freq(mode, &mode->channels[idx], tb_freq);
+		idx++;
+	}
+	phy_info->last_chan_idx = idx;
+
+	return NL_OK;
+}
+
+
+static int phy_info_rates(struct hostapd_hw_modes *mode, struct nlattr *tb)
+{
+	static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = {
+		[NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 },
+		[NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] =
+		{ .type = NLA_FLAG },
+	};
+	struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1];
+	struct nlattr *nl_rate;
+	int rem_rate, idx;
+
+	if (tb == NULL)
+		return NL_OK;
+
+	nla_for_each_nested(nl_rate, tb, rem_rate) {
+		nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX,
+			  nla_data(nl_rate), nla_len(nl_rate),
+			  rate_policy);
+		if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
+			continue;
+		mode->num_rates++;
+	}
+
+	mode->rates = os_calloc(mode->num_rates, sizeof(int));
+	if (!mode->rates)
+		return NL_STOP;
+
+	idx = 0;
+
+	nla_for_each_nested(nl_rate, tb, rem_rate) {
+		nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX,
+			  nla_data(nl_rate), nla_len(nl_rate),
+			  rate_policy);
+		if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
+			continue;
+		mode->rates[idx] = nla_get_u32(
+			tb_rate[NL80211_BITRATE_ATTR_RATE]);
+		idx++;
+	}
+
+	return NL_OK;
+}
+
+
+static void phy_info_iftype_copy(struct hostapd_hw_modes *mode,
+				 enum ieee80211_op_mode opmode,
+				 struct nlattr **tb, struct nlattr **tb_flags)
+{
+	enum nl80211_iftype iftype;
+	size_t len;
+	struct he_capabilities *he_capab = &mode->he_capab[opmode];
+	struct eht_capabilities *eht_capab = &mode->eht_capab[opmode];
+
+	switch (opmode) {
+	case IEEE80211_MODE_INFRA:
+		iftype = NL80211_IFTYPE_STATION;
+		break;
+	case IEEE80211_MODE_IBSS:
+		iftype = NL80211_IFTYPE_ADHOC;
+		break;
+	case IEEE80211_MODE_AP:
+		iftype = NL80211_IFTYPE_AP;
+		break;
+	case IEEE80211_MODE_MESH:
+		iftype = NL80211_IFTYPE_MESH_POINT;
+		break;
+	default:
+		return;
+	}
+
+	if (!nla_get_flag(tb_flags[iftype]))
+		return;
+
+	he_capab->he_supported = 1;
+
+	if (tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY]) {
+		len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY]);
+
+		if (len > sizeof(he_capab->phy_cap))
+			len = sizeof(he_capab->phy_cap);
+		os_memcpy(he_capab->phy_cap,
+			  nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY]),
+			  len);
+	}
+
+	if (tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC]) {
+		len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC]);
+
+		if (len > sizeof(he_capab->mac_cap))
+			len = sizeof(he_capab->mac_cap);
+		os_memcpy(he_capab->mac_cap,
+			  nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC]),
+			  len);
+	}
+
+	if (tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET]) {
+		len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET]);
+
+		if (len > sizeof(he_capab->mcs))
+			len = sizeof(he_capab->mcs);
+		os_memcpy(he_capab->mcs,
+			  nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET]),
+			  len);
+	}
+
+	if (tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE]) {
+		len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE]);
+
+		if (len > sizeof(he_capab->ppet))
+			len = sizeof(he_capab->ppet);
+		os_memcpy(&he_capab->ppet,
+			  nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE]),
+			  len);
+	}
+
+	if (tb[NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA]) {
+		u16 capa;
+
+		capa = nla_get_u16(tb[NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA]);
+		he_capab->he_6ghz_capa = le_to_host16(capa);
+	}
+
+	if (!tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC] ||
+	    !tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY])
+		return;
+
+	eht_capab->eht_supported = true;
+
+	if (tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC] &&
+	    nla_len(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC]) >= 2) {
+		    const u8 *pos;
+
+		    pos = nla_data(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC]);
+		    eht_capab->mac_cap = WPA_GET_LE16(pos);
+	}
+
+	if (tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY]) {
+		len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY]);
+		if (len > sizeof(eht_capab->phy_cap))
+			len = sizeof(eht_capab->phy_cap);
+		os_memcpy(eht_capab->phy_cap,
+			  nla_data(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY]),
+			  len);
+	}
+
+	if (tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET]) {
+		len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET]);
+		if (len > sizeof(eht_capab->mcs))
+			len = sizeof(eht_capab->mcs);
+		os_memcpy(eht_capab->mcs,
+			  nla_data(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET]),
+			  len);
+	}
+
+	if (tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE]) {
+		len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE]);
+		if (len > sizeof(eht_capab->ppet))
+			len = sizeof(eht_capab->ppet);
+		os_memcpy(&eht_capab->ppet,
+			  nla_data(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE]),
+			  len);
+	}
+}
+
+
+static int phy_info_iftype(struct hostapd_hw_modes *mode,
+			   struct nlattr *nl_iftype)
+{
+	struct nlattr *tb[NL80211_BAND_IFTYPE_ATTR_MAX + 1];
+	struct nlattr *tb_flags[NL80211_IFTYPE_MAX + 1];
+	unsigned int i;
+
+	nla_parse(tb, NL80211_BAND_IFTYPE_ATTR_MAX,
+		  nla_data(nl_iftype), nla_len(nl_iftype), NULL);
+
+	if (!tb[NL80211_BAND_IFTYPE_ATTR_IFTYPES])
+		return NL_STOP;
+
+	if (nla_parse_nested(tb_flags, NL80211_IFTYPE_MAX,
+			     tb[NL80211_BAND_IFTYPE_ATTR_IFTYPES], NULL))
+		return NL_STOP;
+
+	for (i = 0; i < IEEE80211_MODE_NUM; i++)
+		phy_info_iftype_copy(mode, i, tb, tb_flags);
+
+	return NL_OK;
+}
+
+
+static int phy_info_band(struct phy_info_arg *phy_info, struct nlattr *nl_band)
+{
+	struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1];
+	struct hostapd_hw_modes *mode;
+	int ret;
+
+	if (phy_info->last_mode != nl_band->nla_type) {
+		mode = os_realloc_array(phy_info->modes,
+					*phy_info->num_modes + 1,
+					sizeof(*mode));
+		if (!mode) {
+			phy_info->failed = 1;
+			return NL_STOP;
+		}
+		phy_info->modes = mode;
+
+		mode = &phy_info->modes[*(phy_info->num_modes)];
+		os_memset(mode, 0, sizeof(*mode));
+		mode->mode = NUM_HOSTAPD_MODES;
+		mode->flags = HOSTAPD_MODE_FLAG_HT_INFO_KNOWN |
+			HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN |
+			HOSTAPD_MODE_FLAG_HE_INFO_KNOWN;
+
+		/*
+		 * Unsupported VHT MCS stream is defined as value 3, so the VHT
+		 * MCS RX/TX map must be initialized with 0xffff to mark all 8
+		 * possible streams as unsupported. This will be overridden if
+		 * driver advertises VHT support.
+		 */
+		mode->vht_mcs_set[0] = 0xff;
+		mode->vht_mcs_set[1] = 0xff;
+		mode->vht_mcs_set[4] = 0xff;
+		mode->vht_mcs_set[5] = 0xff;
+
+		*(phy_info->num_modes) += 1;
+		phy_info->last_mode = nl_band->nla_type;
+		phy_info->last_chan_idx = 0;
+	} else
+		mode = &phy_info->modes[*(phy_info->num_modes) - 1];
+
+	nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band),
+		  nla_len(nl_band), NULL);
+
+	phy_info_ht_capa(mode, tb_band[NL80211_BAND_ATTR_HT_CAPA],
+			 tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR],
+			 tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY],
+			 tb_band[NL80211_BAND_ATTR_HT_MCS_SET]);
+	phy_info_vht_capa(mode, tb_band[NL80211_BAND_ATTR_VHT_CAPA],
+			  tb_band[NL80211_BAND_ATTR_VHT_MCS_SET]);
+	ret = phy_info_edmg_capa(mode,
+				 tb_band[NL80211_BAND_ATTR_EDMG_BW_CONFIG],
+				 tb_band[NL80211_BAND_ATTR_EDMG_CHANNELS]);
+	if (ret == NL_OK)
+		ret = phy_info_freqs(phy_info, mode,
+				     tb_band[NL80211_BAND_ATTR_FREQS]);
+	if (ret == NL_OK)
+		ret = phy_info_rates(mode, tb_band[NL80211_BAND_ATTR_RATES]);
+	if (ret != NL_OK) {
+		phy_info->failed = 1;
+		return ret;
+	}
+
+	if (tb_band[NL80211_BAND_ATTR_IFTYPE_DATA]) {
+		struct nlattr *nl_iftype;
+		int rem_band;
+
+		nla_for_each_nested(nl_iftype,
+				    tb_band[NL80211_BAND_ATTR_IFTYPE_DATA],
+				    rem_band) {
+			ret = phy_info_iftype(mode, nl_iftype);
+			if (ret != NL_OK)
+				return ret;
+		}
+	}
+
+	return NL_OK;
+}
+
+
+static int phy_info_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct phy_info_arg *phy_info = arg;
+	struct nlattr *nl_band;
+	int rem_band;
+
+	nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (!tb_msg[NL80211_ATTR_WIPHY_BANDS])
+		return NL_SKIP;
+
+	nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band)
+	{
+		int res = phy_info_band(phy_info, nl_band);
+		if (res != NL_OK)
+			return res;
+	}
+
+	return NL_SKIP;
+}
+
+
+static struct hostapd_hw_modes *
+wpa_driver_nl80211_postprocess_modes(struct hostapd_hw_modes *modes,
+				     u16 *num_modes)
+{
+	u16 m;
+	struct hostapd_hw_modes *mode11g = NULL, *nmodes, *mode;
+	int i, mode11g_idx = -1;
+
+	/* heuristic to set up modes */
+	for (m = 0; m < *num_modes; m++) {
+		if (!modes[m].num_channels)
+			continue;
+		if (modes[m].channels[0].freq < 2000) {
+			modes[m].num_channels = 0;
+			continue;
+		} else if (modes[m].channels[0].freq < 4000) {
+			modes[m].mode = HOSTAPD_MODE_IEEE80211B;
+			for (i = 0; i < modes[m].num_rates; i++) {
+				if (modes[m].rates[i] > 200) {
+					modes[m].mode = HOSTAPD_MODE_IEEE80211G;
+					break;
+				}
+			}
+		} else if (modes[m].channels[0].freq > 50000)
+			modes[m].mode = HOSTAPD_MODE_IEEE80211AD;
+		else
+			modes[m].mode = HOSTAPD_MODE_IEEE80211A;
+	}
+
+	/* Remove unsupported bands */
+	m = 0;
+	while (m < *num_modes) {
+		if (modes[m].mode == NUM_HOSTAPD_MODES) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Remove unsupported mode");
+			os_free(modes[m].channels);
+			os_free(modes[m].rates);
+			if (m + 1 < *num_modes)
+				os_memmove(&modes[m], &modes[m + 1],
+					   sizeof(struct hostapd_hw_modes) *
+					   (*num_modes - (m + 1)));
+			(*num_modes)--;
+			continue;
+		}
+		m++;
+	}
+
+	/* If only 802.11g mode is included, use it to construct matching
+	 * 802.11b mode data. */
+
+	for (m = 0; m < *num_modes; m++) {
+		if (modes[m].mode == HOSTAPD_MODE_IEEE80211B)
+			return modes; /* 802.11b already included */
+		if (modes[m].mode == HOSTAPD_MODE_IEEE80211G)
+			mode11g_idx = m;
+	}
+
+	if (mode11g_idx < 0)
+		return modes; /* 2.4 GHz band not supported at all */
+
+	nmodes = os_realloc_array(modes, *num_modes + 1, sizeof(*nmodes));
+	if (nmodes == NULL)
+		return modes; /* Could not add 802.11b mode */
+
+	mode = &nmodes[*num_modes];
+	os_memset(mode, 0, sizeof(*mode));
+	(*num_modes)++;
+	modes = nmodes;
+
+	mode->mode = HOSTAPD_MODE_IEEE80211B;
+
+	mode11g = &modes[mode11g_idx];
+	mode->num_channels = mode11g->num_channels;
+	mode->channels = os_memdup(mode11g->channels,
+				   mode11g->num_channels *
+				   sizeof(struct hostapd_channel_data));
+	if (mode->channels == NULL) {
+		(*num_modes)--;
+		return modes; /* Could not add 802.11b mode */
+	}
+
+	mode->num_rates = 0;
+	mode->rates = os_malloc(4 * sizeof(int));
+	if (mode->rates == NULL) {
+		os_free(mode->channels);
+		(*num_modes)--;
+		return modes; /* Could not add 802.11b mode */
+	}
+
+	for (i = 0; i < mode11g->num_rates; i++) {
+		if (mode11g->rates[i] != 10 && mode11g->rates[i] != 20 &&
+		    mode11g->rates[i] != 55 && mode11g->rates[i] != 110)
+			continue;
+		mode->rates[mode->num_rates] = mode11g->rates[i];
+		mode->num_rates++;
+		if (mode->num_rates == 4)
+			break;
+	}
+
+	if (mode->num_rates == 0) {
+		os_free(mode->channels);
+		os_free(mode->rates);
+		(*num_modes)--;
+		return modes; /* No 802.11b rates */
+	}
+
+	/* channel 14 is only for IEEE 802.11b */
+	for (i = 0; i < mode11g->num_channels; i++) {
+		if (mode11g->channels[i].freq == 2484) {
+			mode11g->channels[i].flag |= HOSTAPD_CHAN_DISABLED;
+			break;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: Added 802.11b mode based on 802.11g "
+		   "information");
+
+	return modes;
+}
+
+
+static void nl80211_set_ht40_mode(struct hostapd_hw_modes *mode, int start,
+				  int end)
+{
+	int c;
+
+	for (c = 0; c < mode->num_channels; c++) {
+		struct hostapd_channel_data *chan = &mode->channels[c];
+		if (chan->freq - 10 >= start && chan->freq + 10 <= end)
+			chan->flag |= HOSTAPD_CHAN_HT40;
+	}
+}
+
+
+static void nl80211_set_ht40_mode_sec(struct hostapd_hw_modes *mode, int start,
+				      int end)
+{
+	int c;
+
+	for (c = 0; c < mode->num_channels; c++) {
+		struct hostapd_channel_data *chan = &mode->channels[c];
+		if (!(chan->flag & HOSTAPD_CHAN_HT40))
+			continue;
+		if (chan->freq - 30 >= start && chan->freq - 10 <= end)
+			chan->flag |= HOSTAPD_CHAN_HT40MINUS;
+		if (chan->freq + 10 >= start && chan->freq + 30 <= end)
+			chan->flag |= HOSTAPD_CHAN_HT40PLUS;
+	}
+}
+
+
+static void nl80211_reg_rule_max_eirp(u32 start, u32 end, u32 max_eirp,
+				      struct phy_info_arg *results)
+{
+	u16 m;
+
+	for (m = 0; m < *results->num_modes; m++) {
+		int c;
+		struct hostapd_hw_modes *mode = &results->modes[m];
+
+		for (c = 0; c < mode->num_channels; c++) {
+			struct hostapd_channel_data *chan = &mode->channels[c];
+			if ((u32) chan->freq - 10 >= start &&
+			    (u32) chan->freq + 10 <= end)
+				chan->max_tx_power = max_eirp;
+		}
+	}
+}
+
+
+static void nl80211_reg_rule_ht40(u32 start, u32 end,
+				  struct phy_info_arg *results)
+{
+	u16 m;
+
+	for (m = 0; m < *results->num_modes; m++) {
+		if (!(results->modes[m].ht_capab &
+		      HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+			continue;
+		nl80211_set_ht40_mode(&results->modes[m], start, end);
+	}
+}
+
+
+static void nl80211_reg_rule_sec(struct nlattr *tb[],
+				 struct phy_info_arg *results)
+{
+	u32 start, end, max_bw;
+	u16 m;
+
+	if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL ||
+	    tb[NL80211_ATTR_FREQ_RANGE_END] == NULL ||
+	    tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL)
+		return;
+
+	start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000;
+	end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000;
+	max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000;
+
+	if (max_bw < 20)
+		return;
+
+	for (m = 0; m < *results->num_modes; m++) {
+		if (!(results->modes[m].ht_capab &
+		      HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+			continue;
+		nl80211_set_ht40_mode_sec(&results->modes[m], start, end);
+	}
+}
+
+
+static void nl80211_set_vht_mode(struct hostapd_hw_modes *mode, int start,
+				 int end, int max_bw)
+{
+	int c;
+
+	for (c = 0; c < mode->num_channels; c++) {
+		struct hostapd_channel_data *chan = &mode->channels[c];
+
+		if (chan->freq - 10 < start || chan->freq + 10 > end)
+			continue;
+
+		if (max_bw >= 80)
+			chan->flag |= HOSTAPD_CHAN_VHT_80MHZ_SUBCHANNEL;
+
+		if (max_bw >= 160)
+			chan->flag |= HOSTAPD_CHAN_VHT_160MHZ_SUBCHANNEL;
+	}
+}
+
+
+static void nl80211_reg_rule_vht(struct nlattr *tb[],
+				 struct phy_info_arg *results)
+{
+	u32 start, end, max_bw;
+	u16 m;
+
+	if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL ||
+	    tb[NL80211_ATTR_FREQ_RANGE_END] == NULL ||
+	    tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL)
+		return;
+
+	start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000;
+	end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000;
+	max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000;
+
+	if (max_bw < 80)
+		return;
+
+	for (m = 0; m < *results->num_modes; m++) {
+		if (!(results->modes[m].ht_capab &
+		      HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+			continue;
+		/* TODO: use a real VHT support indication */
+		if (!results->modes[m].vht_capab)
+			continue;
+
+		nl80211_set_vht_mode(&results->modes[m], start, end, max_bw);
+	}
+}
+
+
+static void nl80211_set_dfs_domain(enum nl80211_dfs_regions region,
+				   u8 *dfs_domain)
+{
+	if (region == NL80211_DFS_FCC)
+		*dfs_domain = HOSTAPD_DFS_REGION_FCC;
+	else if (region == NL80211_DFS_ETSI)
+		*dfs_domain = HOSTAPD_DFS_REGION_ETSI;
+	else if (region == NL80211_DFS_JP)
+		*dfs_domain = HOSTAPD_DFS_REGION_JP;
+	else
+		*dfs_domain = 0;
+}
+
+
+static const char * dfs_domain_name(enum nl80211_dfs_regions region)
+{
+	switch (region) {
+	case NL80211_DFS_UNSET:
+		return "DFS-UNSET";
+	case NL80211_DFS_FCC:
+		return "DFS-FCC";
+	case NL80211_DFS_ETSI:
+		return "DFS-ETSI";
+	case NL80211_DFS_JP:
+		return "DFS-JP";
+	default:
+		return "DFS-invalid";
+	}
+}
+
+
+static int nl80211_get_reg(struct nl_msg *msg, void *arg)
+{
+	struct phy_info_arg *results = arg;
+	struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *nl_rule;
+	struct nlattr *tb_rule[NL80211_FREQUENCY_ATTR_MAX + 1];
+	int rem_rule;
+	static struct nla_policy reg_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
+		[NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
+		[NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
+		[NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
+		[NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
+		[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
+		[NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
+	};
+
+	nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+	if (!tb_msg[NL80211_ATTR_REG_ALPHA2] ||
+	    !tb_msg[NL80211_ATTR_REG_RULES]) {
+		wpa_printf(MSG_DEBUG, "nl80211: No regulatory information "
+			   "available");
+		return NL_SKIP;
+	}
+
+	if (tb_msg[NL80211_ATTR_DFS_REGION]) {
+		enum nl80211_dfs_regions dfs_domain;
+		dfs_domain = nla_get_u8(tb_msg[NL80211_ATTR_DFS_REGION]);
+		nl80211_set_dfs_domain(dfs_domain, &results->dfs_domain);
+		wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s (%s)",
+			   (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]),
+			   dfs_domain_name(dfs_domain));
+	} else {
+		wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s",
+			   (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]));
+	}
+
+	nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule)
+	{
+		u32 start, end, max_eirp = 0, max_bw = 0, flags = 0;
+		nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
+			  nla_data(nl_rule), nla_len(nl_rule), reg_policy);
+		if (tb_rule[NL80211_ATTR_FREQ_RANGE_START] == NULL ||
+		    tb_rule[NL80211_ATTR_FREQ_RANGE_END] == NULL)
+			continue;
+		start = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_START]) / 1000;
+		end = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_END]) / 1000;
+		if (tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP])
+			max_eirp = nla_get_u32(tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP]) / 100;
+		if (tb_rule[NL80211_ATTR_FREQ_RANGE_MAX_BW])
+			max_bw = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000;
+		if (tb_rule[NL80211_ATTR_REG_RULE_FLAGS])
+			flags = nla_get_u32(tb_rule[NL80211_ATTR_REG_RULE_FLAGS]);
+
+		wpa_printf(MSG_DEBUG, "nl80211: %u-%u @ %u MHz %u mBm%s%s%s%s%s%s%s%s",
+			   start, end, max_bw, max_eirp,
+			   flags & NL80211_RRF_NO_OFDM ? " (no OFDM)" : "",
+			   flags & NL80211_RRF_NO_CCK ? " (no CCK)" : "",
+			   flags & NL80211_RRF_NO_INDOOR ? " (no indoor)" : "",
+			   flags & NL80211_RRF_NO_OUTDOOR ? " (no outdoor)" :
+			   "",
+			   flags & NL80211_RRF_DFS ? " (DFS)" : "",
+			   flags & NL80211_RRF_PTP_ONLY ? " (PTP only)" : "",
+			   flags & NL80211_RRF_PTMP_ONLY ? " (PTMP only)" : "",
+			   flags & NL80211_RRF_NO_IR ? " (no IR)" : "");
+		if (max_bw >= 40)
+			nl80211_reg_rule_ht40(start, end, results);
+		if (tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP])
+			nl80211_reg_rule_max_eirp(start, end, max_eirp,
+						  results);
+	}
+
+	nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule)
+	{
+		nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
+			  nla_data(nl_rule), nla_len(nl_rule), reg_policy);
+		nl80211_reg_rule_sec(tb_rule, results);
+	}
+
+	nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule)
+	{
+		nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
+			  nla_data(nl_rule), nla_len(nl_rule), reg_policy);
+		nl80211_reg_rule_vht(tb_rule, results);
+	}
+
+	return NL_SKIP;
+}
+
+
+static int nl80211_set_regulatory_flags(struct wpa_driver_nl80211_data *drv,
+					struct phy_info_arg *results)
+{
+	struct nl_msg *msg;
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return -ENOMEM;
+
+	nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG);
+	if (drv->capa.flags & WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY) {
+		if (nla_put_u32(msg, NL80211_ATTR_WIPHY, drv->wiphy_idx)) {
+			nlmsg_free(msg);
+			return -1;
+		}
+	}
+
+	return send_and_recv_msgs(drv, msg, nl80211_get_reg, results,
+				  NULL, NULL);
+}
+
+
+static const char * modestr(enum hostapd_hw_mode mode)
+{
+	switch (mode) {
+	case HOSTAPD_MODE_IEEE80211B:
+		return "802.11b";
+	case HOSTAPD_MODE_IEEE80211G:
+		return "802.11g";
+	case HOSTAPD_MODE_IEEE80211A:
+		return "802.11a";
+	case HOSTAPD_MODE_IEEE80211AD:
+		return "802.11ad";
+	default:
+		return "?";
+	}
+}
+
+
+static void nl80211_dump_chan_list(struct wpa_driver_nl80211_data *drv,
+				   struct hostapd_hw_modes *modes,
+				   u16 num_modes)
+{
+	int i;
+
+	if (!modes)
+		return;
+
+	for (i = 0; i < num_modes; i++) {
+		struct hostapd_hw_modes *mode = &modes[i];
+		char str[1000];
+		char *pos = str;
+		char *end = pos + sizeof(str);
+		int j, res;
+
+		for (j = 0; j < mode->num_channels; j++) {
+			struct hostapd_channel_data *chan = &mode->channels[j];
+
+			if (chan->freq >= 5925 && chan->freq <= 7125 &&
+			    !(chan->flag & HOSTAPD_CHAN_DISABLED))
+				drv->uses_6ghz = true;
+			res = os_snprintf(pos, end - pos, " %d%s%s%s",
+					  chan->freq,
+					  (chan->flag & HOSTAPD_CHAN_DISABLED) ?
+					  "[DISABLED]" : "",
+					  (chan->flag & HOSTAPD_CHAN_NO_IR) ?
+					  "[NO_IR]" : "",
+					  (chan->flag & HOSTAPD_CHAN_RADAR) ?
+					  "[RADAR]" : "");
+			if (os_snprintf_error(end - pos, res))
+				break;
+			pos += res;
+		}
+
+		*pos = '\0';
+		wpa_printf(MSG_DEBUG, "nl80211: Mode IEEE %s:%s",
+			   modestr(mode->mode), str);
+	}
+}
+
+
+struct hostapd_hw_modes *
+nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags,
+			    u8 *dfs_domain)
+{
+	u32 feat;
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int nl_flags = 0;
+	struct nl_msg *msg;
+	struct phy_info_arg result = {
+		.num_modes = num_modes,
+		.modes = NULL,
+		.last_mode = -1,
+		.failed = 0,
+		.dfs_domain = 0,
+	};
+
+	*num_modes = 0;
+	*flags = 0;
+	*dfs_domain = 0;
+
+	feat = get_nl80211_protocol_features(drv);
+	if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)
+		nl_flags = NLM_F_DUMP;
+	if (!(msg = nl80211_cmd_msg(bss, nl_flags, NL80211_CMD_GET_WIPHY)) ||
+	    nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP)) {
+		nlmsg_free(msg);
+		return NULL;
+	}
+
+	if (send_and_recv_msgs(drv, msg, phy_info_handler, &result,
+			       NULL, NULL) == 0) {
+		struct hostapd_hw_modes *modes;
+
+		nl80211_set_regulatory_flags(drv, &result);
+		if (result.failed) {
+			int i;
+
+			for (i = 0; result.modes && i < *num_modes; i++) {
+				os_free(result.modes[i].channels);
+				os_free(result.modes[i].rates);
+			}
+			os_free(result.modes);
+			*num_modes = 0;
+			return NULL;
+		}
+
+		*dfs_domain = result.dfs_domain;
+
+		modes = wpa_driver_nl80211_postprocess_modes(result.modes,
+							     num_modes);
+		nl80211_dump_chan_list(drv, modes, *num_modes);
+		return modes;
+	}
+
+	return NULL;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211_event.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211_event.c
new file mode 100644
index 0000000..3e8ace0
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211_event.c
@@ -0,0 +1,4077 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211 - Event processing
+ * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <netlink/genl/genl.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/qca-vendor.h"
+#include "common/qca-vendor-attr.h"
+#include "common/brcm_vendor.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "driver_nl80211.h"
+
+
+static void
+nl80211_control_port_frame_tx_status(struct wpa_driver_nl80211_data *drv,
+				     const u8 *frame, size_t len,
+				     struct nlattr *ack, struct nlattr *cookie);
+
+
+static const char * nl80211_command_to_string(enum nl80211_commands cmd)
+{
+#define C2S(x) case x: return #x;
+	switch (cmd) {
+	C2S(NL80211_CMD_UNSPEC)
+	C2S(NL80211_CMD_GET_WIPHY)
+	C2S(NL80211_CMD_SET_WIPHY)
+	C2S(NL80211_CMD_NEW_WIPHY)
+	C2S(NL80211_CMD_DEL_WIPHY)
+	C2S(NL80211_CMD_GET_INTERFACE)
+	C2S(NL80211_CMD_SET_INTERFACE)
+	C2S(NL80211_CMD_NEW_INTERFACE)
+	C2S(NL80211_CMD_DEL_INTERFACE)
+	C2S(NL80211_CMD_GET_KEY)
+	C2S(NL80211_CMD_SET_KEY)
+	C2S(NL80211_CMD_NEW_KEY)
+	C2S(NL80211_CMD_DEL_KEY)
+	C2S(NL80211_CMD_GET_BEACON)
+	C2S(NL80211_CMD_SET_BEACON)
+	C2S(NL80211_CMD_START_AP)
+	C2S(NL80211_CMD_STOP_AP)
+	C2S(NL80211_CMD_GET_STATION)
+	C2S(NL80211_CMD_SET_STATION)
+	C2S(NL80211_CMD_NEW_STATION)
+	C2S(NL80211_CMD_DEL_STATION)
+	C2S(NL80211_CMD_GET_MPATH)
+	C2S(NL80211_CMD_SET_MPATH)
+	C2S(NL80211_CMD_NEW_MPATH)
+	C2S(NL80211_CMD_DEL_MPATH)
+	C2S(NL80211_CMD_SET_BSS)
+	C2S(NL80211_CMD_SET_REG)
+	C2S(NL80211_CMD_REQ_SET_REG)
+	C2S(NL80211_CMD_GET_MESH_CONFIG)
+	C2S(NL80211_CMD_SET_MESH_CONFIG)
+	C2S(NL80211_CMD_SET_MGMT_EXTRA_IE)
+	C2S(NL80211_CMD_GET_REG)
+	C2S(NL80211_CMD_GET_SCAN)
+	C2S(NL80211_CMD_TRIGGER_SCAN)
+	C2S(NL80211_CMD_NEW_SCAN_RESULTS)
+	C2S(NL80211_CMD_SCAN_ABORTED)
+	C2S(NL80211_CMD_REG_CHANGE)
+	C2S(NL80211_CMD_AUTHENTICATE)
+	C2S(NL80211_CMD_ASSOCIATE)
+	C2S(NL80211_CMD_DEAUTHENTICATE)
+	C2S(NL80211_CMD_DISASSOCIATE)
+	C2S(NL80211_CMD_MICHAEL_MIC_FAILURE)
+	C2S(NL80211_CMD_REG_BEACON_HINT)
+	C2S(NL80211_CMD_JOIN_IBSS)
+	C2S(NL80211_CMD_LEAVE_IBSS)
+	C2S(NL80211_CMD_TESTMODE)
+	C2S(NL80211_CMD_CONNECT)
+	C2S(NL80211_CMD_ROAM)
+	C2S(NL80211_CMD_DISCONNECT)
+	C2S(NL80211_CMD_SET_WIPHY_NETNS)
+	C2S(NL80211_CMD_GET_SURVEY)
+	C2S(NL80211_CMD_NEW_SURVEY_RESULTS)
+	C2S(NL80211_CMD_SET_PMKSA)
+	C2S(NL80211_CMD_DEL_PMKSA)
+	C2S(NL80211_CMD_FLUSH_PMKSA)
+	C2S(NL80211_CMD_REMAIN_ON_CHANNEL)
+	C2S(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL)
+	C2S(NL80211_CMD_SET_TX_BITRATE_MASK)
+	C2S(NL80211_CMD_REGISTER_FRAME)
+	C2S(NL80211_CMD_FRAME)
+	C2S(NL80211_CMD_FRAME_TX_STATUS)
+	C2S(NL80211_CMD_SET_POWER_SAVE)
+	C2S(NL80211_CMD_GET_POWER_SAVE)
+	C2S(NL80211_CMD_SET_CQM)
+	C2S(NL80211_CMD_NOTIFY_CQM)
+	C2S(NL80211_CMD_SET_CHANNEL)
+	C2S(NL80211_CMD_SET_WDS_PEER)
+	C2S(NL80211_CMD_FRAME_WAIT_CANCEL)
+	C2S(NL80211_CMD_JOIN_MESH)
+	C2S(NL80211_CMD_LEAVE_MESH)
+	C2S(NL80211_CMD_UNPROT_DEAUTHENTICATE)
+	C2S(NL80211_CMD_UNPROT_DISASSOCIATE)
+	C2S(NL80211_CMD_NEW_PEER_CANDIDATE)
+	C2S(NL80211_CMD_GET_WOWLAN)
+	C2S(NL80211_CMD_SET_WOWLAN)
+	C2S(NL80211_CMD_START_SCHED_SCAN)
+	C2S(NL80211_CMD_STOP_SCHED_SCAN)
+	C2S(NL80211_CMD_SCHED_SCAN_RESULTS)
+	C2S(NL80211_CMD_SCHED_SCAN_STOPPED)
+	C2S(NL80211_CMD_SET_REKEY_OFFLOAD)
+	C2S(NL80211_CMD_PMKSA_CANDIDATE)
+	C2S(NL80211_CMD_TDLS_OPER)
+	C2S(NL80211_CMD_TDLS_MGMT)
+	C2S(NL80211_CMD_UNEXPECTED_FRAME)
+	C2S(NL80211_CMD_PROBE_CLIENT)
+	C2S(NL80211_CMD_REGISTER_BEACONS)
+	C2S(NL80211_CMD_UNEXPECTED_4ADDR_FRAME)
+	C2S(NL80211_CMD_SET_NOACK_MAP)
+	C2S(NL80211_CMD_CH_SWITCH_NOTIFY)
+	C2S(NL80211_CMD_START_P2P_DEVICE)
+	C2S(NL80211_CMD_STOP_P2P_DEVICE)
+	C2S(NL80211_CMD_CONN_FAILED)
+	C2S(NL80211_CMD_SET_MCAST_RATE)
+	C2S(NL80211_CMD_SET_MAC_ACL)
+	C2S(NL80211_CMD_RADAR_DETECT)
+	C2S(NL80211_CMD_GET_PROTOCOL_FEATURES)
+	C2S(NL80211_CMD_UPDATE_FT_IES)
+	C2S(NL80211_CMD_FT_EVENT)
+	C2S(NL80211_CMD_CRIT_PROTOCOL_START)
+	C2S(NL80211_CMD_CRIT_PROTOCOL_STOP)
+	C2S(NL80211_CMD_GET_COALESCE)
+	C2S(NL80211_CMD_SET_COALESCE)
+	C2S(NL80211_CMD_CHANNEL_SWITCH)
+	C2S(NL80211_CMD_VENDOR)
+	C2S(NL80211_CMD_SET_QOS_MAP)
+	C2S(NL80211_CMD_ADD_TX_TS)
+	C2S(NL80211_CMD_DEL_TX_TS)
+	C2S(NL80211_CMD_GET_MPP)
+	C2S(NL80211_CMD_JOIN_OCB)
+	C2S(NL80211_CMD_LEAVE_OCB)
+	C2S(NL80211_CMD_CH_SWITCH_STARTED_NOTIFY)
+	C2S(NL80211_CMD_TDLS_CHANNEL_SWITCH)
+	C2S(NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH)
+	C2S(NL80211_CMD_WIPHY_REG_CHANGE)
+	C2S(NL80211_CMD_ABORT_SCAN)
+	C2S(NL80211_CMD_START_NAN)
+	C2S(NL80211_CMD_STOP_NAN)
+	C2S(NL80211_CMD_ADD_NAN_FUNCTION)
+	C2S(NL80211_CMD_DEL_NAN_FUNCTION)
+	C2S(NL80211_CMD_CHANGE_NAN_CONFIG)
+	C2S(NL80211_CMD_NAN_MATCH)
+	C2S(NL80211_CMD_SET_MULTICAST_TO_UNICAST)
+	C2S(NL80211_CMD_UPDATE_CONNECT_PARAMS)
+	C2S(NL80211_CMD_SET_PMK)
+	C2S(NL80211_CMD_DEL_PMK)
+	C2S(NL80211_CMD_PORT_AUTHORIZED)
+	C2S(NL80211_CMD_RELOAD_REGDB)
+	C2S(NL80211_CMD_EXTERNAL_AUTH)
+	C2S(NL80211_CMD_STA_OPMODE_CHANGED)
+	C2S(NL80211_CMD_CONTROL_PORT_FRAME)
+	C2S(NL80211_CMD_GET_FTM_RESPONDER_STATS)
+	C2S(NL80211_CMD_PEER_MEASUREMENT_START)
+	C2S(NL80211_CMD_PEER_MEASUREMENT_RESULT)
+	C2S(NL80211_CMD_PEER_MEASUREMENT_COMPLETE)
+	C2S(NL80211_CMD_NOTIFY_RADAR)
+	C2S(NL80211_CMD_UPDATE_OWE_INFO)
+	C2S(NL80211_CMD_PROBE_MESH_LINK)
+	C2S(NL80211_CMD_SET_TID_CONFIG)
+	C2S(NL80211_CMD_UNPROT_BEACON)
+	C2S(NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS)
+	C2S(NL80211_CMD_SET_SAR_SPECS)
+	C2S(NL80211_CMD_OBSS_COLOR_COLLISION)
+	C2S(NL80211_CMD_COLOR_CHANGE_REQUEST)
+	C2S(NL80211_CMD_COLOR_CHANGE_STARTED)
+	C2S(NL80211_CMD_COLOR_CHANGE_ABORTED)
+	C2S(NL80211_CMD_COLOR_CHANGE_COMPLETED)
+	C2S(NL80211_CMD_SET_FILS_AAD)
+	C2S(NL80211_CMD_ASSOC_COMEBACK)
+	C2S(NL80211_CMD_ADD_LINK)
+	C2S(NL80211_CMD_REMOVE_LINK)
+	C2S(NL80211_CMD_ADD_LINK_STA)
+	C2S(NL80211_CMD_MODIFY_LINK_STA)
+	C2S(NL80211_CMD_REMOVE_LINK_STA)
+	C2S(NL80211_CMD_SET_HW_TIMESTAMP)
+	C2S(__NL80211_CMD_AFTER_LAST)
+	}
+#undef C2S
+
+	return "NL80211_CMD_UNKNOWN";
+}
+
+
+static void mlme_event_auth(struct wpa_driver_nl80211_data *drv,
+			    const u8 *frame, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	union wpa_event_data event;
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
+	    drv->force_connect_cmd) {
+		/*
+		 * Avoid reporting two association events that would confuse
+		 * the core code.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Ignore auth event when using driver SME");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: Authenticate event");
+	mgmt = (const struct ieee80211_mgmt *) frame;
+	if (len < 24 + sizeof(mgmt->u.auth)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Too short association event "
+			   "frame");
+		return;
+	}
+
+	os_memcpy(drv->auth_bssid, mgmt->sa, ETH_ALEN);
+	os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN);
+	os_memset(&event, 0, sizeof(event));
+	os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN);
+	event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg);
+	event.auth.auth_transaction =
+		le_to_host16(mgmt->u.auth.auth_transaction);
+	event.auth.status_code = le_to_host16(mgmt->u.auth.status_code);
+	if (len > 24 + sizeof(mgmt->u.auth)) {
+		event.auth.ies = mgmt->u.auth.variable;
+		event.auth.ies_len = len - 24 - sizeof(mgmt->u.auth);
+	}
+
+	wpa_supplicant_event(drv->ctx, EVENT_AUTH, &event);
+}
+
+
+static void nl80211_parse_wmm_params(struct nlattr *wmm_attr,
+				     struct wmm_params *wmm_params)
+{
+	struct nlattr *wmm_info[NL80211_STA_WME_MAX + 1];
+	static struct nla_policy wme_policy[NL80211_STA_WME_MAX + 1] = {
+		[NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 },
+	};
+
+	if (!wmm_attr ||
+	    nla_parse_nested(wmm_info, NL80211_STA_WME_MAX, wmm_attr,
+			     wme_policy) ||
+	    !wmm_info[NL80211_STA_WME_UAPSD_QUEUES])
+		return;
+
+	wmm_params->uapsd_queues =
+		nla_get_u8(wmm_info[NL80211_STA_WME_UAPSD_QUEUES]);
+	wmm_params->info_bitmap |= WMM_PARAMS_UAPSD_QUEUES_INFO;
+}
+
+
+static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
+			     const u8 *frame, size_t len, struct nlattr *wmm,
+			     struct nlattr *req_ie)
+{
+	const struct ieee80211_mgmt *mgmt;
+	union wpa_event_data event;
+	u16 status;
+	int ssid_len;
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
+	    drv->force_connect_cmd) {
+		/*
+		 * Avoid reporting two association events that would confuse
+		 * the core code.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Ignore assoc event when using driver SME");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: Associate event");
+	mgmt = (const struct ieee80211_mgmt *) frame;
+	if (len < 24 + sizeof(mgmt->u.assoc_resp)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Too short association event "
+			   "frame");
+		return;
+	}
+
+	status = le_to_host16(mgmt->u.assoc_resp.status_code);
+	if (status != WLAN_STATUS_SUCCESS) {
+		os_memset(&event, 0, sizeof(event));
+		event.assoc_reject.bssid = mgmt->bssid;
+		if (len > 24 + sizeof(mgmt->u.assoc_resp)) {
+			event.assoc_reject.resp_ies =
+				(u8 *) mgmt->u.assoc_resp.variable;
+			event.assoc_reject.resp_ies_len =
+				len - 24 - sizeof(mgmt->u.assoc_resp);
+		}
+		event.assoc_reject.status_code = status;
+
+		wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event);
+		return;
+	}
+
+	drv->associated = 1;
+	os_memcpy(drv->bssid, mgmt->sa, ETH_ALEN);
+	os_memcpy(drv->prev_bssid, mgmt->sa, ETH_ALEN);
+
+	os_memset(&event, 0, sizeof(event));
+	event.assoc_info.resp_frame = frame;
+	event.assoc_info.resp_frame_len = len;
+	if (len > 24 + sizeof(mgmt->u.assoc_resp)) {
+		event.assoc_info.resp_ies = (u8 *) mgmt->u.assoc_resp.variable;
+		event.assoc_info.resp_ies_len =
+			len - 24 - sizeof(mgmt->u.assoc_resp);
+	}
+
+	if (req_ie) {
+		event.assoc_info.req_ies = nla_data(req_ie);
+		event.assoc_info.req_ies_len = nla_len(req_ie);
+	}
+
+	/* When this association was initiated outside of wpa_supplicant,
+	 * drv->ssid needs to be set here to satisfy later checking. */
+	ssid_len = nl80211_get_assoc_ssid(drv, drv->ssid);
+	if (ssid_len > 0) {
+		drv->ssid_len = ssid_len;
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Set drv->ssid based on scan res info to '%s'",
+			   wpa_ssid_txt(drv->ssid, drv->ssid_len));
+	}
+
+	event.assoc_info.freq = drv->assoc_freq;
+	drv->first_bss->flink->freq = drv->assoc_freq;
+
+	nl80211_parse_wmm_params(wmm, &event.assoc_info.wmm_params);
+
+	wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
+}
+
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+
+static int qca_drv_connect_fail_reason_code_handler(struct nl_msg *msg,
+						    void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct nlattr *tb_sta_info[QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	u32 *reason_code = arg;
+
+	*reason_code = 0;
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (!tb[NL80211_ATTR_VENDOR_DATA]) {
+		wpa_printf(MSG_ERROR, "%s: Vendor data not found", __func__);
+		return NL_SKIP;
+	}
+
+	nla_parse(tb_sta_info, QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAX,
+		  nla_data(tb[NL80211_ATTR_VENDOR_DATA]),
+		  nla_len(tb[NL80211_ATTR_VENDOR_DATA]), NULL);
+
+	if (!tb_sta_info[QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_CONNECT_FAIL_REASON_CODE]) {
+		wpa_printf(MSG_INFO, "%s: Vendor attr not found", __func__);
+		return NL_SKIP;
+	}
+
+	*reason_code = nla_get_u32(tb_sta_info[QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_CONNECT_FAIL_REASON_CODE]);
+
+	return NL_SKIP;
+}
+
+
+static enum qca_sta_connect_fail_reason_codes
+drv_get_connect_fail_reason_code(struct wpa_driver_nl80211_data *drv)
+{
+	enum qca_sta_connect_fail_reason_codes reason_code;
+	struct nl_msg *msg;
+	int ret;
+
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+	if (!msg || nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO)) {
+		nlmsg_free(msg);
+		return 0;
+	}
+
+	ret = send_and_recv_msgs(drv, msg,
+				 qca_drv_connect_fail_reason_code_handler,
+				 &reason_code, NULL, NULL);
+	if (ret)
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Get connect fail reason_code failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+
+	return reason_code;
+}
+
+
+static enum sta_connect_fail_reason_codes
+convert_connect_fail_reason_codes(enum qca_sta_connect_fail_reason_codes
+				  reason_code)
+{
+	switch (reason_code) {
+	case QCA_STA_CONNECT_FAIL_REASON_NO_BSS_FOUND:
+		return STA_CONNECT_FAIL_REASON_NO_BSS_FOUND;
+	case QCA_STA_CONNECT_FAIL_REASON_AUTH_TX_FAIL:
+		return STA_CONNECT_FAIL_REASON_AUTH_TX_FAIL;
+	case QCA_STA_CONNECT_FAIL_REASON_AUTH_NO_ACK_RECEIVED:
+		return STA_CONNECT_FAIL_REASON_AUTH_NO_ACK_RECEIVED;
+	case QCA_STA_CONNECT_FAIL_REASON_AUTH_NO_RESP_RECEIVED:
+		return STA_CONNECT_FAIL_REASON_AUTH_NO_RESP_RECEIVED;
+	case QCA_STA_CONNECT_FAIL_REASON_ASSOC_REQ_TX_FAIL:
+		return STA_CONNECT_FAIL_REASON_ASSOC_REQ_TX_FAIL;
+	case QCA_STA_CONNECT_FAIL_REASON_ASSOC_NO_ACK_RECEIVED:
+		return STA_CONNECT_FAIL_REASON_ASSOC_NO_ACK_RECEIVED;
+	case QCA_STA_CONNECT_FAIL_REASON_ASSOC_NO_RESP_RECEIVED:
+		return STA_CONNECT_FAIL_REASON_ASSOC_NO_RESP_RECEIVED;
+	default:
+		return STA_CONNECT_FAIL_REASON_UNSPECIFIED;
+	}
+}
+
+
+static void qca_nl80211_link_reconfig_event(struct wpa_driver_nl80211_data *drv,
+					    u8 *data, size_t len)
+{
+	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_LINK_RECONFIG_MAX + 1];
+	u16 removed_links;
+	u8 *ap_mld;
+	int i;
+
+	if (!data)
+		return;
+
+	if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_LINK_RECONFIG_MAX,
+		      (struct nlattr *) data, len, NULL) ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_LINK_RECONFIG_AP_MLD_ADDR])
+		return;
+
+	ap_mld = nla_data(tb[QCA_WLAN_VENDOR_ATTR_LINK_RECONFIG_AP_MLD_ADDR]);
+	wpa_printf(MSG_DEBUG, "nl80211: AP MLD address " MACSTR
+		   " received in link reconfig event", MAC2STR(ap_mld));
+	if (!drv->sta_mlo_info.valid_links ||
+	    os_memcmp(drv->sta_mlo_info.ap_mld_addr, ap_mld, ETH_ALEN) != 0) {
+		if (drv->pending_link_reconfig_data == data) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Drop pending link reconfig event since AP MLD not matched even after new connect/roam event");
+			os_free(drv->pending_link_reconfig_data);
+			drv->pending_link_reconfig_data = NULL;
+			return;
+		}
+
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Cache new link reconfig event till next connect/roam event");
+		if (drv->pending_link_reconfig_data) {
+			wpa_printf(MSG_DEBUG, "nl80211: Override old link reconfig event data");
+			os_free(drv->pending_link_reconfig_data);
+		}
+		drv->pending_link_reconfig_data = os_memdup(data, len);
+		if (!drv->pending_link_reconfig_data)
+			return;
+		drv->pending_link_reconfig_data_len = len;
+		return;
+	}
+
+	if (!tb[QCA_WLAN_VENDOR_ATTR_LINK_RECONFIG_REMOVED_LINKS])
+		return;
+	removed_links = nla_get_u16(
+		tb[QCA_WLAN_VENDOR_ATTR_LINK_RECONFIG_REMOVED_LINKS]);
+
+	drv->sta_mlo_info.valid_links &= ~removed_links;
+
+	/*
+	 * Set default BSSID to the BSSID of the lowest link ID of remaining
+	 * links when the link used for (re)association is removed.
+	 */
+	if (removed_links & BIT(drv->sta_mlo_info.assoc_link_id)) {
+		for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+			if (!(drv->sta_mlo_info.valid_links & BIT(i)))
+				continue;
+
+			os_memcpy(drv->bssid, drv->sta_mlo_info.links[i].bssid,
+				  ETH_ALEN);
+			drv->sta_mlo_info.assoc_link_id = i;
+			break;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: Removed MLO links bitmap: 0x%x",
+		   removed_links);
+
+	wpa_supplicant_event(drv->ctx, EVENT_LINK_RECONFIG, NULL);
+}
+
+
+static void
+nl80211_parse_qca_vendor_mlo_link_info(struct driver_sta_mlo_info *mlo,
+				       struct nlattr *mlo_links)
+{
+	struct nlattr *link;
+	int rem_links;
+
+	nla_for_each_nested(link, mlo_links, rem_links) {
+		struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MLO_LINK_MAX + 1];
+		int link_id;
+
+		nla_parse(tb,QCA_WLAN_VENDOR_ATTR_MLO_LINK_MAX, nla_data(link),
+			  nla_len(link), NULL);
+
+		if (!tb[QCA_WLAN_VENDOR_ATTR_MLO_LINK_ID] ||
+		    !tb[QCA_WLAN_VENDOR_ATTR_MLO_LINK_MAC_ADDR] ||
+		    !tb[QCA_WLAN_VENDOR_ATTR_MLO_LINK_BSSID])
+			continue;
+
+		link_id = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_MLO_LINK_ID]);
+		if (link_id >= MAX_NUM_MLD_LINKS)
+			continue;
+
+		mlo->valid_links |= BIT(link_id);
+		os_memcpy(mlo->links[link_id].addr,
+			  nla_data(tb[QCA_WLAN_VENDOR_ATTR_MLO_LINK_MAC_ADDR]),
+			  ETH_ALEN);
+		os_memcpy(mlo->links[link_id].bssid,
+			  nla_data(tb[QCA_WLAN_VENDOR_ATTR_MLO_LINK_BSSID]),
+			  ETH_ALEN);
+		wpa_printf(MSG_DEBUG, "nl80211: MLO link[%u] addr " MACSTR
+			   " bssid " MACSTR,
+			   link_id, MAC2STR(mlo->links[link_id].addr),
+			   MAC2STR(mlo->links[link_id].bssid));
+	}
+}
+
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
+static void nl80211_parse_mlo_link_info(struct driver_sta_mlo_info *mlo,
+					struct nlattr *mlo_links)
+{
+	struct nlattr *link;
+	int rem_links;
+
+	nla_for_each_nested(link, mlo_links, rem_links) {
+		struct nlattr *tb[NL80211_ATTR_MAX + 1];
+		int link_id;
+
+		nla_parse(tb, NL80211_ATTR_MAX, nla_data(link), nla_len(link),
+			  NULL);
+
+		if (!tb[NL80211_ATTR_MLO_LINK_ID] || !tb[NL80211_ATTR_MAC] ||
+		    !tb[NL80211_ATTR_BSSID])
+			continue;
+
+		link_id = nla_get_u8(tb[NL80211_ATTR_MLO_LINK_ID]);
+		if (link_id >= MAX_NUM_MLD_LINKS)
+			continue;
+
+		if (tb[NL80211_ATTR_STATUS_CODE]) {
+			/* Set requested links only when status indicated */
+			mlo->req_links |= BIT(link_id);
+			if (nla_get_u16(tb[NL80211_ATTR_STATUS_CODE]) ==
+			    WLAN_STATUS_SUCCESS)
+				mlo->valid_links |= BIT(link_id);
+		} else {
+			mlo->valid_links |= BIT(link_id);
+		}
+
+		os_memcpy(mlo->links[link_id].addr,
+			  nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+		os_memcpy(mlo->links[link_id].bssid,
+			  nla_data(tb[NL80211_ATTR_BSSID]), ETH_ALEN);
+		wpa_printf(MSG_DEBUG, "nl80211: MLO link[%u] addr " MACSTR
+			   " bssid " MACSTR,
+			   link_id, MAC2STR(mlo->links[link_id].addr),
+			   MAC2STR(mlo->links[link_id].bssid));
+	}
+}
+
+
+struct links_info {
+	/* bitmap of link IDs in Per-STA profile subelements */
+	u16 non_assoc_links;
+	u8 addr[MAX_NUM_MLD_LINKS][ETH_ALEN];
+};
+
+
+static void nl80211_get_basic_mle_links_info(const u8 *mle, size_t mle_len,
+					     struct links_info *info)
+{
+	size_t rem_len;
+	const u8 *pos;
+
+	if (mle_len < MULTI_LINK_CONTROL_LEN + 1 ||
+	    mle_len - MULTI_LINK_CONTROL_LEN < mle[MULTI_LINK_CONTROL_LEN])
+		return;
+
+	/* Skip Common Info */
+	pos = mle + MULTI_LINK_CONTROL_LEN + mle[MULTI_LINK_CONTROL_LEN];
+	rem_len = mle_len -
+		(MULTI_LINK_CONTROL_LEN + mle[MULTI_LINK_CONTROL_LEN]);
+
+	/* Parse Subelements */
+	while (rem_len > 2) {
+		size_t ie_len = 2 + pos[1];
+
+		if (rem_len < ie_len)
+			break;
+
+		if (pos[0] == MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE) {
+			u8 link_id;
+			const u8 *sta_profile;
+			u16 sta_ctrl;
+
+			if (pos[1] < BASIC_MLE_STA_PROF_STA_MAC_IDX + ETH_ALEN)
+				goto next_subelem;
+
+			sta_profile = &pos[2];
+			sta_ctrl = WPA_GET_LE16(sta_profile);
+			link_id = sta_ctrl & BASIC_MLE_STA_CTRL_LINK_ID_MASK;
+			if (link_id >= MAX_NUM_MLD_LINKS)
+				goto next_subelem;
+
+			if (!(sta_ctrl & BASIC_MLE_STA_CTRL_PRES_STA_MAC))
+				goto next_subelem;
+
+			info->non_assoc_links |= BIT(link_id);
+			os_memcpy(info->addr[link_id],
+				  &sta_profile[BASIC_MLE_STA_PROF_STA_MAC_IDX],
+				  ETH_ALEN);
+		}
+next_subelem:
+		pos += ie_len;
+		rem_len -= ie_len;
+	}
+}
+
+
+static int nl80211_update_rejected_links_info(struct driver_sta_mlo_info *mlo,
+					      struct nlattr *req_ie,
+					      struct nlattr *resp_ie)
+{
+	int i;
+	struct wpabuf *mle;
+	struct ieee802_11_elems req_elems, resp_elems;
+	struct links_info req_info, resp_info;
+
+	if (!req_ie || !resp_ie) {
+		wpa_printf(MSG_INFO,
+			   "nl80211: MLO: (Re)Association Request/Response frame elements not available");
+		return -1;
+	}
+
+	if (ieee802_11_parse_elems(nla_data(req_ie), nla_len(req_ie),
+				   &req_elems, 0) == ParseFailed ||
+	    ieee802_11_parse_elems(nla_data(resp_ie), nla_len(resp_ie),
+				   &resp_elems, 0) == ParseFailed) {
+		wpa_printf(MSG_INFO,
+			   "nl80211: MLO: Failed to parse (Re)Association Request/Response elements");
+		return -1;
+	}
+
+	mle = ieee802_11_defrag_mle(&req_elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+	if (!mle) {
+		wpa_printf(MSG_INFO,
+			   "nl80211: MLO: Basic Multi-Link element not found in Association Request");
+		return -1;
+	}
+	os_memset(&req_info, 0, sizeof(req_info));
+	nl80211_get_basic_mle_links_info(wpabuf_head(mle), wpabuf_len(mle),
+					 &req_info);
+	wpabuf_free(mle);
+
+	mle = ieee802_11_defrag_mle(&resp_elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+	if (!mle) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: MLO: Basic Multi-Link element not found in Association Response");
+		return -1;
+	}
+	os_memset(&resp_info, 0, sizeof(resp_info));
+	nl80211_get_basic_mle_links_info(wpabuf_head(mle), wpabuf_len(mle),
+					 &resp_info);
+	wpabuf_free(mle);
+
+	if (req_info.non_assoc_links != resp_info.non_assoc_links) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: MLO: Association Request and Response links bitmaps not equal (0x%x != 0x%x)",
+			   req_info.non_assoc_links,
+			   resp_info.non_assoc_links);
+		return -1;
+	}
+
+	mlo->req_links = BIT(mlo->assoc_link_id) | req_info.non_assoc_links;
+	if ((mlo->req_links & mlo->valid_links) != mlo->valid_links) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: MLO: Accepted links are not a subset of requested links (req_links=0x%x valid_links=0x%x non_assoc_links=0x%x assoc_link_id=0x%x)",
+			   mlo->req_links, mlo->valid_links,
+			   req_info.non_assoc_links, BIT(mlo->assoc_link_id));
+		return -1;
+	}
+
+	/* Get MLO links info for rejected links */
+	for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+		if (!((mlo->req_links & ~mlo->valid_links) & BIT(i)))
+			continue;
+
+		os_memcpy(mlo->links[i].bssid, resp_info.addr[i], ETH_ALEN);
+		os_memcpy(mlo->links[i].addr, req_info.addr[i], ETH_ALEN);
+	}
+
+	return 0;
+}
+
+
+static int nl80211_get_assoc_link_id(const u8 *data, u8 len)
+{
+	u16 control;
+
+	if (len < 2)
+		return -1;
+
+	control = WPA_GET_LE16(data);
+	if (!(control & BASIC_MULTI_LINK_CTRL_PRES_LINK_ID))
+		return -1;
+
+#define BASIC_ML_IE_COMMON_INFO_LINK_ID_IDX \
+		(2 + /* Multi-Link Control field */ \
+		 1 + /* Common Info Length field (Basic) */ \
+		 ETH_ALEN) /* MLD MAC Address field (Basic) */
+	if (len <= BASIC_ML_IE_COMMON_INFO_LINK_ID_IDX)
+		return -1;
+
+	return data[BASIC_ML_IE_COMMON_INFO_LINK_ID_IDX] & 0x0F;
+}
+
+
+static void nl80211_parse_mlo_info(struct wpa_driver_nl80211_data *drv,
+				   bool qca_roam_auth,
+				   struct nlattr *addr,
+				   struct nlattr *mlo_links,
+				   struct nlattr *req_ie,
+				   struct nlattr *resp_ie)
+{
+	const u8 *ml_ie;
+	struct driver_sta_mlo_info *mlo = &drv->sta_mlo_info;
+	int res;
+
+	if (!addr || !mlo_links || !resp_ie)
+		return;
+
+	ml_ie = get_ml_ie(nla_data(resp_ie), nla_len(resp_ie),
+			  MULTI_LINK_CONTROL_TYPE_BASIC);
+	if (!ml_ie)
+		return;
+
+	res = nl80211_get_assoc_link_id(&ml_ie[3], ml_ie[1] - 1);
+	if (res < 0 || res >= MAX_NUM_MLD_LINKS) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Could not find a valid association Link ID (res=%d)",
+			   res);
+		return;
+	}
+	drv->sta_mlo_info.assoc_link_id = res;
+
+	os_memcpy(mlo->ap_mld_addr, nla_data(addr), ETH_ALEN);
+	wpa_printf(MSG_DEBUG, "nl80211: AP MLD MAC Address " MACSTR,
+		   MAC2STR(mlo->ap_mld_addr));
+
+	if (!qca_roam_auth)
+		nl80211_parse_mlo_link_info(mlo, mlo_links);
+#ifdef CONFIG_DRIVER_NL80211_QCA
+	if (qca_roam_auth)
+		nl80211_parse_qca_vendor_mlo_link_info(mlo, mlo_links);
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+	if (!(mlo->valid_links & BIT(mlo->assoc_link_id)) ||
+	    (!mlo->req_links &&
+	     nl80211_update_rejected_links_info(mlo, req_ie, resp_ie))) {
+		wpa_printf(MSG_INFO, "nl80211: Invalid MLO connection info");
+		mlo->valid_links = 0;
+		return;
+	}
+
+	os_memcpy(drv->bssid, mlo->links[drv->sta_mlo_info.assoc_link_id].bssid,
+		  ETH_ALEN);
+	os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN);
+}
+
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+static void
+qca_nl80211_tid_to_link_map_event(struct wpa_driver_nl80211_data *drv,
+				  u8 *data, size_t len)
+{
+	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_TID_TO_LINK_MAP_MAX + 1];
+	struct nlattr *tids;
+	union wpa_event_data event;
+	u8 *ap_mld;
+	int i, rem, tidnum = 0;
+
+	os_memset(&event, 0, sizeof(event));
+
+	if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_TID_TO_LINK_MAP_MAX,
+		      (struct nlattr *) data, len, NULL) ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_TID_TO_LINK_MAP_AP_MLD_ADDR])
+		return;
+
+	ap_mld = nla_data(tb[QCA_WLAN_VENDOR_ATTR_TID_TO_LINK_MAP_AP_MLD_ADDR]);
+
+	wpa_printf(MSG_DEBUG, "nl80211: AP MLD address " MACSTR
+		   " received in TID to link mapping event", MAC2STR(ap_mld));
+	if (!drv->sta_mlo_info.valid_links ||
+	    os_memcmp(drv->sta_mlo_info.ap_mld_addr, ap_mld, ETH_ALEN) != 0) {
+		if (drv->pending_t2lm_data == data) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Drop pending TID-to-link mapping event since AP MLD not matched even after new connect/roam event");
+			os_free(drv->pending_t2lm_data);
+			drv->pending_t2lm_data = NULL;
+			return;
+		}
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Cache new TID-to-link map event until the next connect/roam event");
+		if (drv->pending_t2lm_data) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Override old TID-to-link map event data");
+			os_free(drv->pending_t2lm_data);
+		}
+		drv->pending_t2lm_data = os_memdup(data, len);
+		if (!drv->pending_t2lm_data)
+			return;
+		drv->pending_t2lm_data_len = len;
+		return;
+	}
+
+	if (!tb[QCA_WLAN_VENDOR_ATTR_TID_TO_LINK_MAP_STATUS]) {
+		wpa_printf(MSG_DEBUG, "nl80211: Default TID-to-link map");
+		event.t2l_map_info.default_map = true;
+		goto out;
+	}
+
+	event.t2l_map_info.default_map = false;
+
+	nla_for_each_nested(tids,
+			    tb[QCA_WLAN_VENDOR_ATTR_TID_TO_LINK_MAP_STATUS],
+			    rem) {
+		u16 uplink, downlink;
+		struct nlattr *tid[QCA_WLAN_VENDOR_ATTR_LINK_TID_MAP_STATUS_MAX + 1];
+
+		if (nla_parse_nested(
+			    tid, QCA_WLAN_VENDOR_ATTR_LINK_TID_MAP_STATUS_MAX,
+			    tids,  NULL)) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: TID-to-link: nla_parse_nested() failed");
+			return;
+		}
+
+		if (!tid[QCA_WLAN_VENDOR_ATTR_LINK_TID_MAP_STATUS_UPLINK]) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: TID-to-link: uplink not present for tid: %d",
+				   tidnum);
+			return;
+		}
+		uplink = nla_get_u16(tid[QCA_WLAN_VENDOR_ATTR_LINK_TID_MAP_STATUS_UPLINK]);
+
+		if (!tid[QCA_WLAN_VENDOR_ATTR_LINK_TID_MAP_STATUS_DOWNLINK]) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: TID-to-link: downlink not present for tid: %d",
+				   tidnum);
+			return;
+		}
+		downlink = nla_get_u16(tid[QCA_WLAN_VENDOR_ATTR_LINK_TID_MAP_STATUS_DOWNLINK]);
+
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: TID-to-link: Received uplink %x downlink %x",
+			   uplink, downlink);
+		for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+			if (!(drv->sta_mlo_info.valid_links & BIT(i)))
+				continue;
+			if (uplink & BIT(i))
+				event.t2l_map_info.t2lmap[i].uplink |=
+					BIT(tidnum);
+			if (downlink & BIT(i))
+				event.t2l_map_info.t2lmap[i].downlink |=
+					BIT(tidnum);
+		}
+
+		tidnum++;
+	}
+
+out:
+	drv->sta_mlo_info.default_map = event.t2l_map_info.default_map;
+
+	event.t2l_map_info.valid_links = drv->sta_mlo_info.valid_links;
+	for (i = 0; i < MAX_NUM_MLD_LINKS && !drv->sta_mlo_info.default_map;
+	     i++) {
+		if (!(drv->sta_mlo_info.valid_links & BIT(i)))
+			continue;
+
+		drv->sta_mlo_info.links[i].t2lmap.uplink =
+			event.t2l_map_info.t2lmap[i].uplink;
+		drv->sta_mlo_info.links[i].t2lmap.downlink =
+			event.t2l_map_info.t2lmap[i].downlink;
+	}
+
+	wpa_supplicant_event(drv->ctx, EVENT_TID_LINK_MAP, &event);
+}
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
+static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
+			       enum nl80211_commands cmd, bool qca_roam_auth,
+			       struct nlattr *status,
+			       struct nlattr *addr, struct nlattr *req_ie,
+			       struct nlattr *resp_ie,
+			       struct nlattr *timed_out,
+			       struct nlattr *timeout_reason,
+			       struct nlattr *authorized,
+			       struct nlattr *key_replay_ctr,
+			       struct nlattr *ptk_kck,
+			       struct nlattr *ptk_kek,
+			       struct nlattr *subnet_status,
+			       struct nlattr *fils_erp_next_seq_num,
+			       struct nlattr *fils_pmk,
+			       struct nlattr *fils_pmkid,
+			       struct nlattr *mlo_links)
+{
+	union wpa_event_data event;
+	const u8 *ssid = NULL;
+	u16 status_code;
+	int ssid_len;
+
+	if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
+		/*
+		 * Avoid reporting two association events that would confuse
+		 * the core code.
+		 */
+		wpa_printf(MSG_DEBUG, "nl80211: Ignore connect event (cmd=%d) "
+			   "when using userspace SME", cmd);
+		return;
+	}
+
+	drv->connect_reassoc = 0;
+
+	status_code = status ? nla_get_u16(status) : WLAN_STATUS_SUCCESS;
+
+	if (cmd == NL80211_CMD_CONNECT) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Connect event (status=%u ignore_next_local_disconnect=%d)",
+			   status_code, drv->ignore_next_local_disconnect);
+	} else if (cmd == NL80211_CMD_ROAM) {
+		wpa_printf(MSG_DEBUG, "nl80211: Roam event");
+	}
+
+	os_memset(&event, 0, sizeof(event));
+	if (cmd == NL80211_CMD_CONNECT && status_code != WLAN_STATUS_SUCCESS) {
+		if (addr)
+			event.assoc_reject.bssid = nla_data(addr);
+		if (drv->ignore_next_local_disconnect) {
+			drv->ignore_next_local_disconnect = 0;
+			if (!event.assoc_reject.bssid ||
+			    (os_memcmp(event.assoc_reject.bssid,
+				       drv->auth_attempt_bssid,
+				       ETH_ALEN) != 0)) {
+				/*
+				 * Ignore the event that came without a BSSID or
+				 * for the old connection since this is likely
+				 * not relevant to the new Connect command.
+				 */
+				wpa_printf(MSG_DEBUG,
+					   "nl80211: Ignore connection failure event triggered during reassociation");
+				return;
+			}
+		}
+		if (resp_ie) {
+			event.assoc_reject.resp_ies = nla_data(resp_ie);
+			event.assoc_reject.resp_ies_len = nla_len(resp_ie);
+		}
+		event.assoc_reject.status_code = status_code;
+		event.assoc_reject.timed_out = timed_out != NULL;
+		if (timed_out && timeout_reason) {
+			enum nl80211_timeout_reason reason;
+
+			reason = nla_get_u32(timeout_reason);
+			switch (reason) {
+			case NL80211_TIMEOUT_SCAN:
+				event.assoc_reject.timeout_reason = "scan";
+				break;
+			case NL80211_TIMEOUT_AUTH:
+				event.assoc_reject.timeout_reason = "auth";
+				break;
+			case NL80211_TIMEOUT_ASSOC:
+				event.assoc_reject.timeout_reason = "assoc";
+				break;
+			default:
+				break;
+			}
+		}
+		if (fils_erp_next_seq_num)
+			event.assoc_reject.fils_erp_next_seq_num =
+				nla_get_u16(fils_erp_next_seq_num);
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+		if (drv->get_sta_info_vendor_cmd_avail) {
+			enum qca_sta_connect_fail_reason_codes reason_code;
+
+			reason_code = drv_get_connect_fail_reason_code(drv);
+			event.assoc_reject.reason_code =
+				convert_connect_fail_reason_codes(reason_code);
+		}
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+		wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event);
+		return;
+	}
+
+	drv->associated = 1;
+	os_memset(&drv->sta_mlo_info, 0, sizeof(drv->sta_mlo_info));
+	nl80211_parse_mlo_info(drv, qca_roam_auth, addr, mlo_links, req_ie,
+			       resp_ie);
+	if (!drv->sta_mlo_info.valid_links && addr) {
+		os_memcpy(drv->bssid, nla_data(addr), ETH_ALEN);
+		os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN);
+	}
+
+	if (req_ie) {
+		event.assoc_info.req_ies = nla_data(req_ie);
+		event.assoc_info.req_ies_len = nla_len(req_ie);
+
+		if (cmd == NL80211_CMD_ROAM) {
+			ssid = get_ie(event.assoc_info.req_ies,
+				      event.assoc_info.req_ies_len,
+				      WLAN_EID_SSID);
+			if (ssid && ssid[1] > 0 && ssid[1] <= 32) {
+				drv->ssid_len = ssid[1];
+				os_memcpy(drv->ssid, ssid + 2, ssid[1]);
+				wpa_printf(MSG_DEBUG,
+					   "nl80211: Set drv->ssid based on req_ie to '%s'",
+					   wpa_ssid_txt(drv->ssid,
+							drv->ssid_len));
+			}
+		}
+	}
+	if (resp_ie) {
+		event.assoc_info.resp_ies = nla_data(resp_ie);
+		event.assoc_info.resp_ies_len = nla_len(resp_ie);
+	}
+
+	event.assoc_info.freq = nl80211_get_assoc_freq(drv);
+	drv->first_bss->flink->freq = drv->assoc_freq;
+
+	if ((!ssid || ssid[1] == 0 || ssid[1] > 32) &&
+	    (ssid_len = nl80211_get_assoc_ssid(drv, drv->ssid)) > 0) {
+		/* When this connection was initiated outside of wpa_supplicant,
+		 * drv->ssid needs to be set here to satisfy later checking. */
+		drv->ssid_len = ssid_len;
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Set drv->ssid based on scan res info to '%s'",
+			   wpa_ssid_txt(drv->ssid, drv->ssid_len));
+	}
+
+	if (authorized && nla_get_u8(authorized)) {
+		event.assoc_info.authorized = 1;
+		wpa_printf(MSG_DEBUG, "nl80211: connection authorized");
+	}
+	if (key_replay_ctr) {
+		event.assoc_info.key_replay_ctr = nla_data(key_replay_ctr);
+		event.assoc_info.key_replay_ctr_len = nla_len(key_replay_ctr);
+	}
+	if (ptk_kck) {
+		event.assoc_info.ptk_kck = nla_data(ptk_kck);
+		event.assoc_info.ptk_kck_len = nla_len(ptk_kck);
+	}
+	if (ptk_kek) {
+		event.assoc_info.ptk_kek = nla_data(ptk_kek);
+		event.assoc_info.ptk_kek_len = nla_len(ptk_kek);
+	}
+
+	if (subnet_status) {
+		/*
+		 * At least for now, this is only available from
+		 * QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS and that
+		 * attribute has the same values 0, 1, 2 as are used in the
+		 * variable here, so no mapping between different values are
+		 * needed.
+		 */
+		event.assoc_info.subnet_status = nla_get_u8(subnet_status);
+	}
+
+	if (fils_erp_next_seq_num)
+		event.assoc_info.fils_erp_next_seq_num =
+			nla_get_u16(fils_erp_next_seq_num);
+
+	if (fils_pmk) {
+		event.assoc_info.fils_pmk = nla_data(fils_pmk);
+		event.assoc_info.fils_pmk_len = nla_len(fils_pmk);
+	}
+
+	if (fils_pmkid)
+		event.assoc_info.fils_pmkid = nla_data(fils_pmkid);
+
+	wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
+
+	/* Avoid a race condition by stopping to ignore any following
+	 * disconnection events now that the driver has indicated it is
+	 * connected since that connection could have been triggered by a roam
+	 * operation that happened in parallel with the disconnection request.
+	 */
+	drv->ignore_next_local_disconnect = 0;
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+	if (drv->pending_t2lm_data)
+		qca_nl80211_tid_to_link_map_event(drv, drv->pending_t2lm_data,
+						  drv->pending_t2lm_data_len);
+	else
+		drv->sta_mlo_info.default_map = true;
+
+	if (drv->pending_link_reconfig_data)
+		qca_nl80211_link_reconfig_event(
+			drv, drv->pending_link_reconfig_data,
+			drv->pending_link_reconfig_data_len);
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+}
+
+
+static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv,
+				  struct nlattr *reason, struct nlattr *addr,
+				  struct nlattr *by_ap)
+{
+	union wpa_event_data data;
+	unsigned int locally_generated = by_ap == NULL;
+
+	if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
+		/*
+		 * Avoid reporting two disassociation events that could
+		 * confuse the core code.
+		 */
+		wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect "
+			   "event when using userspace SME");
+		return;
+	}
+
+	if (drv->ignore_next_local_disconnect) {
+		drv->ignore_next_local_disconnect = 0;
+		if (locally_generated) {
+			wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect "
+				   "event triggered during reassociation");
+			return;
+		}
+		wpa_printf(MSG_WARNING, "nl80211: Was expecting local "
+			   "disconnect but got another disconnect "
+			   "event first");
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: Disconnect event");
+	nl80211_mark_disconnected(drv);
+	os_memset(&data, 0, sizeof(data));
+	if (reason)
+		data.deauth_info.reason_code = nla_get_u16(reason);
+	data.deauth_info.locally_generated = by_ap == NULL;
+	wpa_supplicant_event(drv->ctx, EVENT_DEAUTH, &data);
+}
+
+
+static int calculate_chan_offset(int width, int freq, int cf1, int cf2)
+{
+	int freq1 = 0;
+
+	switch (convert2width(width)) {
+	case CHAN_WIDTH_20_NOHT:
+	case CHAN_WIDTH_20:
+		return 0;
+	case CHAN_WIDTH_40:
+		freq1 = cf1 - 10;
+		break;
+	case CHAN_WIDTH_80:
+		freq1 = cf1 - 30;
+		break;
+	case CHAN_WIDTH_160:
+		freq1 = cf1 - 70;
+		break;
+	case CHAN_WIDTH_80P80:
+		freq1 = cf1 - 30;
+		break;
+	case CHAN_WIDTH_320:
+		freq1 = cf1 - 150;
+		break;
+	case CHAN_WIDTH_UNKNOWN:
+	case CHAN_WIDTH_2160:
+	case CHAN_WIDTH_4320:
+	case CHAN_WIDTH_6480:
+	case CHAN_WIDTH_8640:
+		/* FIXME: implement this */
+		return 0;
+	}
+
+	return (abs(freq - freq1) / 20) % 2 == 0 ? 1 : -1;
+}
+
+
+static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
+				 struct nlattr *ifindex, struct nlattr *link,
+				 struct nlattr *freq, struct nlattr *type,
+				 struct nlattr *bw, struct nlattr *cf1,
+				 struct nlattr *cf2,
+				 struct nlattr *punct_bitmap,
+				 struct nlattr *count,
+				 int finished)
+{
+	struct i802_bss *bss;
+	union wpa_event_data data;
+	int ht_enabled = 1;
+	int chan_offset = 0;
+	int ifidx;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Channel switch%s event",
+		   finished ? "" : " started");
+
+	if (!freq)
+		return;
+
+	ifidx = nla_get_u32(ifindex);
+	bss = get_bss_ifindex(drv, ifidx);
+	if (bss == NULL) {
+		wpa_printf(MSG_WARNING, "nl80211: Unknown ifindex (%d) for channel switch, ignoring",
+			   ifidx);
+		return;
+	}
+
+	if (type) {
+		enum nl80211_channel_type ch_type = nla_get_u32(type);
+
+		wpa_printf(MSG_DEBUG, "nl80211: Channel type: %d", ch_type);
+		switch (ch_type) {
+		case NL80211_CHAN_NO_HT:
+			ht_enabled = 0;
+			break;
+		case NL80211_CHAN_HT20:
+			break;
+		case NL80211_CHAN_HT40PLUS:
+			chan_offset = 1;
+			break;
+		case NL80211_CHAN_HT40MINUS:
+			chan_offset = -1;
+			break;
+		}
+	} else if (bw && cf1) {
+		/* This can happen for example with VHT80 ch switch */
+		chan_offset = calculate_chan_offset(nla_get_u32(bw),
+						    nla_get_u32(freq),
+						    nla_get_u32(cf1),
+						    cf2 ? nla_get_u32(cf2) : 0);
+		wpa_printf(MSG_DEBUG, "nl80211: Calculated channel offset: %d",
+			   chan_offset);
+	} else {
+		wpa_printf(MSG_WARNING, "nl80211: Unknown secondary channel information - following channel definition calculations may fail");
+	}
+
+	os_memset(&data, 0, sizeof(data));
+	data.ch_switch.freq = nla_get_u32(freq);
+	data.ch_switch.ht_enabled = ht_enabled;
+	data.ch_switch.ch_offset = chan_offset;
+	if (punct_bitmap)
+		data.ch_switch.punct_bitmap = (u16) nla_get_u32(punct_bitmap);
+	if (bw)
+		data.ch_switch.ch_width = convert2width(nla_get_u32(bw));
+	if (cf1)
+		data.ch_switch.cf1 = nla_get_u32(cf1);
+	if (cf2)
+		data.ch_switch.cf2 = nla_get_u32(cf2);
+	if (count)
+		data.ch_switch.count = nla_get_u32(count);
+
+	if (finished)
+		bss->flink->freq = data.ch_switch.freq;
+
+	if (link) {
+		u8 link_id = nla_get_u8(link);
+
+		if (link_id < MAX_NUM_MLD_LINKS &&
+		    drv->sta_mlo_info.valid_links & BIT(link_id)) {
+			data.ch_switch.link_id = link_id;
+			drv->sta_mlo_info.links[link_id].freq =
+				data.ch_switch.freq;
+			wpa_supplicant_event(
+				bss->ctx,
+				finished ? EVENT_LINK_CH_SWITCH :
+				EVENT_LINK_CH_SWITCH_STARTED, &data);
+		}
+
+		if (link_id != drv->sta_mlo_info.assoc_link_id)
+			return;
+	}
+
+	drv->assoc_freq = data.ch_switch.freq;
+
+	wpa_supplicant_event(bss->ctx, finished ?
+			     EVENT_CH_SWITCH : EVENT_CH_SWITCH_STARTED, &data);
+}
+
+
+static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv,
+			       enum nl80211_commands cmd, struct nlattr *addr)
+{
+	union wpa_event_data event;
+	enum wpa_event_type ev;
+
+	if (nla_len(addr) != ETH_ALEN)
+		return;
+
+	wpa_printf(MSG_DEBUG, "nl80211: MLME event %d; timeout with " MACSTR,
+		   cmd, MAC2STR((u8 *) nla_data(addr)));
+
+	if (cmd == NL80211_CMD_AUTHENTICATE)
+		ev = EVENT_AUTH_TIMED_OUT;
+	else if (cmd == NL80211_CMD_ASSOCIATE)
+		ev = EVENT_ASSOC_TIMED_OUT;
+	else
+		return;
+
+	os_memset(&event, 0, sizeof(event));
+	os_memcpy(event.timeout_event.addr, nla_data(addr), ETH_ALEN);
+	wpa_supplicant_event(drv->ctx, ev, &event);
+}
+
+
+static void mlme_event_mgmt(struct i802_bss *bss,
+			    struct nlattr *freq, struct nlattr *sig,
+			    const u8 *frame, size_t len,
+			    int link_id)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	const struct ieee80211_mgmt *mgmt;
+	union wpa_event_data event;
+	u16 fc, stype;
+	int ssi_signal = 0;
+	int rx_freq = 0;
+
+	wpa_printf(MSG_MSGDUMP, "nl80211: Frame event");
+	mgmt = (const struct ieee80211_mgmt *) frame;
+	if (len < 24) {
+		wpa_printf(MSG_DEBUG, "nl80211: Too short management frame");
+		return;
+	}
+
+	fc = le_to_host16(mgmt->frame_control);
+	stype = WLAN_FC_GET_STYPE(fc);
+
+	if (sig)
+		ssi_signal = (s32) nla_get_u32(sig);
+
+	os_memset(&event, 0, sizeof(event));
+	if (freq) {
+		event.rx_mgmt.freq = nla_get_u32(freq);
+		rx_freq = drv->last_mgmt_freq = event.rx_mgmt.freq;
+	}
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: RX frame da=" MACSTR " sa=" MACSTR " bssid=" MACSTR
+		   " freq=%d ssi_signal=%d fc=0x%x seq_ctrl=0x%x stype=%u (%s) len=%u",
+		   MAC2STR(mgmt->da), MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid),
+		   rx_freq, ssi_signal, fc,
+		   le_to_host16(mgmt->seq_ctrl), stype, fc2str(fc),
+		   (unsigned int) len);
+	event.rx_mgmt.frame = frame;
+	event.rx_mgmt.frame_len = len;
+	event.rx_mgmt.ssi_signal = ssi_signal;
+	event.rx_mgmt.drv_priv = bss;
+	event.rx_mgmt.link_id = link_id;
+
+	wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
+}
+
+
+static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv,
+				      struct nlattr *cookie, const u8 *frame,
+				      size_t len, struct nlattr *ack)
+{
+	union wpa_event_data event;
+	const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *) frame;
+	u16 fc = le_to_host16(hdr->frame_control);
+	u64 cookie_val = 0;
+
+	if (cookie)
+		cookie_val = nla_get_u64(cookie);
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Frame TX status event A1=" MACSTR
+		   " %sstype=%d cookie=0x%llx%s ack=%d",
+		   MAC2STR(hdr->addr1),
+		   WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT ? "not-mgmt " : "",
+		   WLAN_FC_GET_STYPE(fc), (long long unsigned int) cookie_val,
+		   cookie ? "" : "(N/A)", ack != NULL);
+
+	if (cookie_val && cookie_val == drv->eapol_tx_cookie &&
+	    len >= ETH_HLEN &&
+	    WPA_GET_BE16(frame + 2 * ETH_ALEN) == ETH_P_PAE) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Work around misdelivered control port TX status for EAPOL");
+		nl80211_control_port_frame_tx_status(drv, frame, len, ack,
+						     cookie);
+		return;
+	}
+
+	if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT)
+		return;
+
+	if (!is_ap_interface(drv->nlmode) &&
+	    WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) {
+		if (!cookie)
+			return;
+
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Frame TX status: cookie=0x%llx%s (ack=%d)",
+			   (long long unsigned int) cookie_val,
+			   cookie_val == drv->send_frame_cookie ?
+			   " (match)" : " (unknown)", ack != NULL);
+		if (cookie_val != drv->send_frame_cookie)
+			return;
+	} else if (!is_ap_interface(drv->nlmode) &&
+		   WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Authentication frame TX status: ack=%d",
+			   !!ack);
+	}
+
+	os_memset(&event, 0, sizeof(event));
+	event.tx_status.type = WLAN_FC_GET_TYPE(fc);
+	event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
+	event.tx_status.dst = hdr->addr1;
+	event.tx_status.data = frame;
+	event.tx_status.data_len = len;
+	event.tx_status.ack = ack != NULL;
+	event.tx_status.link_id = cookie_val == drv->send_frame_cookie ?
+		drv->send_frame_link_id : NL80211_DRV_LINK_ID_NA;
+	wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event);
+}
+
+
+static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv,
+				       enum wpa_event_type type,
+				       const u8 *frame, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	union wpa_event_data event;
+	const u8 *bssid = NULL;
+	u16 reason_code = 0;
+
+	if (type == EVENT_DEAUTH)
+		wpa_printf(MSG_DEBUG, "nl80211: Deauthenticate event");
+	else
+		wpa_printf(MSG_DEBUG, "nl80211: Disassociate event");
+
+	mgmt = (const struct ieee80211_mgmt *) frame;
+	if (len >= 24) {
+		bssid = mgmt->bssid;
+
+		if ((drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
+		    !drv->associated &&
+		    os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0 &&
+		    os_memcmp(bssid, drv->auth_attempt_bssid, ETH_ALEN) != 0 &&
+		    os_memcmp(bssid, drv->prev_bssid, ETH_ALEN) == 0) {
+			/*
+			 * Avoid issues with some roaming cases where
+			 * disconnection event for the old AP may show up after
+			 * we have started connection with the new AP.
+			 * In case of locally generated event clear
+			 * ignore_next_local_deauth as well, to avoid next local
+			 * deauth event be wrongly ignored.
+			 */
+			if (os_memcmp(mgmt->sa, drv->first_bss->addr,
+				      ETH_ALEN) == 0 ||
+			    (!is_zero_ether_addr(drv->first_bss->prev_addr) &&
+			     os_memcmp(mgmt->sa, drv->first_bss->prev_addr,
+				       ETH_ALEN) == 0)) {
+				wpa_printf(MSG_DEBUG,
+					   "nl80211: Received a locally generated deauth event. Clear ignore_next_local_deauth flag");
+				drv->ignore_next_local_deauth = 0;
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "nl80211: Ignore deauth/disassoc event from old AP " MACSTR " when already authenticating with " MACSTR,
+					   MAC2STR(bssid),
+					   MAC2STR(drv->auth_attempt_bssid));
+			}
+			return;
+		}
+
+		if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
+		    drv->connect_reassoc && drv->associated &&
+		    os_memcmp(bssid, drv->prev_bssid, ETH_ALEN) == 0 &&
+		    os_memcmp(bssid, drv->auth_attempt_bssid, ETH_ALEN) != 0) {
+			/*
+			 * Avoid issues with some roaming cases where
+			 * disconnection event for the old AP may show up after
+			 * we have started connection with the new AP.
+			 */
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Ignore deauth/disassoc event from old AP "
+				   MACSTR
+				   " when already connecting with " MACSTR,
+				   MAC2STR(bssid),
+				   MAC2STR(drv->auth_attempt_bssid));
+			return;
+		}
+
+		if (drv->associated != 0 &&
+		    os_memcmp(bssid, drv->bssid, ETH_ALEN) != 0 &&
+		    os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0) {
+			/*
+			 * We have presumably received this deauth as a
+			 * response to a clear_state_mismatch() outgoing
+			 * deauth.  Don't let it take us offline!
+			 */
+			wpa_printf(MSG_DEBUG, "nl80211: Deauth received "
+				   "from Unknown BSSID " MACSTR " -- ignoring",
+				   MAC2STR(bssid));
+			return;
+		}
+	}
+
+	nl80211_mark_disconnected(drv);
+	os_memset(&event, 0, sizeof(event));
+
+	/* Note: Same offset for Reason Code in both frame subtypes */
+	if (len >= 24 + sizeof(mgmt->u.deauth))
+		reason_code = le_to_host16(mgmt->u.deauth.reason_code);
+
+	if (type == EVENT_DISASSOC) {
+		event.disassoc_info.locally_generated =
+			!os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN);
+		event.disassoc_info.addr = bssid;
+		event.disassoc_info.reason_code = reason_code;
+		if (frame + len > mgmt->u.disassoc.variable) {
+			event.disassoc_info.ie = mgmt->u.disassoc.variable;
+			event.disassoc_info.ie_len = frame + len -
+				mgmt->u.disassoc.variable;
+		}
+	} else {
+		event.deauth_info.locally_generated =
+			!os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN);
+		if (drv->ignore_deauth_event) {
+			wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth event due to previous forced deauth-during-auth");
+			drv->ignore_deauth_event = 0;
+			if (event.deauth_info.locally_generated)
+				drv->ignore_next_local_deauth = 0;
+			return;
+		}
+		if (drv->ignore_next_local_deauth) {
+			drv->ignore_next_local_deauth = 0;
+			if (event.deauth_info.locally_generated) {
+				wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth event triggered due to own deauth request");
+				return;
+			}
+			wpa_printf(MSG_WARNING, "nl80211: Was expecting local deauth but got another disconnect event first");
+		}
+		event.deauth_info.addr = bssid;
+		event.deauth_info.reason_code = reason_code;
+		if (frame + len > mgmt->u.deauth.variable) {
+			event.deauth_info.ie = mgmt->u.deauth.variable;
+			event.deauth_info.ie_len = frame + len -
+				mgmt->u.deauth.variable;
+		}
+	}
+
+	wpa_supplicant_event(drv->ctx, type, &event);
+}
+
+
+static void mlme_event_unprot_disconnect(struct wpa_driver_nl80211_data *drv,
+					 enum wpa_event_type type,
+					 const u8 *frame, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	union wpa_event_data event;
+	u16 reason_code = 0;
+
+	if (type == EVENT_UNPROT_DEAUTH)
+		wpa_printf(MSG_DEBUG, "nl80211: Unprot Deauthenticate event");
+	else
+		wpa_printf(MSG_DEBUG, "nl80211: Unprot Disassociate event");
+
+	if (len < 24)
+		return;
+
+	mgmt = (const struct ieee80211_mgmt *) frame;
+
+	os_memset(&event, 0, sizeof(event));
+	/* Note: Same offset for Reason Code in both frame subtypes */
+	if (len >= 24 + sizeof(mgmt->u.deauth))
+		reason_code = le_to_host16(mgmt->u.deauth.reason_code);
+
+	if (type == EVENT_UNPROT_DISASSOC) {
+		event.unprot_disassoc.sa = mgmt->sa;
+		event.unprot_disassoc.da = mgmt->da;
+		event.unprot_disassoc.reason_code = reason_code;
+	} else {
+		event.unprot_deauth.sa = mgmt->sa;
+		event.unprot_deauth.da = mgmt->da;
+		event.unprot_deauth.reason_code = reason_code;
+	}
+
+	wpa_supplicant_event(drv->ctx, type, &event);
+}
+
+
+static void mlme_event_unprot_beacon(struct wpa_driver_nl80211_data *drv,
+				     const u8 *frame, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	union wpa_event_data event;
+
+	if (len < 24)
+		return;
+
+	mgmt = (const struct ieee80211_mgmt *) frame;
+
+	os_memset(&event, 0, sizeof(event));
+	event.unprot_beacon.sa = mgmt->sa;
+	wpa_supplicant_event(drv->ctx, EVENT_UNPROT_BEACON, &event);
+}
+
+
+static struct i802_link *
+nl80211_get_mld_link_by_freq(struct i802_bss *bss, unsigned int freq)
+{
+	unsigned int i;
+
+	for (i = 0; i < bss->n_links; i++) {
+		if ((unsigned int) bss->links[i].freq == freq &&
+		    bss->links[i].link_id != -1)
+			return &bss->links[i];
+	}
+
+	return NULL;
+}
+
+
+static void mlme_event(struct i802_bss *bss,
+		       enum nl80211_commands cmd, struct nlattr *frame,
+		       struct nlattr *addr, struct nlattr *timed_out,
+		       struct nlattr *freq, struct nlattr *ack,
+		       struct nlattr *cookie, struct nlattr *sig,
+		       struct nlattr *wmm, struct nlattr *req_ie,
+		       struct nlattr *link)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	u16 stype = 0, auth_type = 0;
+	const u8 *data;
+	size_t len;
+	int link_id = -1;
+	struct i802_link *mld_link = NULL;
+
+	if (timed_out && addr) {
+		mlme_timeout_event(drv, cmd, addr);
+		return;
+	}
+
+	if (frame == NULL) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: MLME event %d (%s) without frame data",
+			   cmd, nl80211_command_to_string(cmd));
+		return;
+	}
+
+	/* Determine the MLD link either by an explicitly provided link id or
+	 * finding a match based on the frequency. */
+	if (link)
+		mld_link = nl80211_get_link(bss, nla_get_u8(link));
+	else if (freq)
+		mld_link = nl80211_get_mld_link_by_freq(bss, nla_get_u32(freq));
+
+	if (mld_link)
+		link_id = mld_link->link_id;
+
+	data = nla_data(frame);
+	len = nla_len(frame);
+	if (len < 4 + 2 * ETH_ALEN) {
+		wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s("
+			   MACSTR ") - too short",
+			   cmd, nl80211_command_to_string(cmd), bss->ifname,
+			   MAC2STR(bss->addr));
+		return;
+	}
+	wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s(" MACSTR
+		   ") A1=" MACSTR " A2=" MACSTR " on link_id=%d", cmd,
+		   nl80211_command_to_string(cmd), bss->ifname,
+		   MAC2STR(bss->addr), MAC2STR(data + 4),
+		   MAC2STR(data + 4 + ETH_ALEN), link_id);
+
+	/* PASN Authentication frame can be received with a different source MAC
+	 * address. Allow NL80211_CMD_FRAME event with foreign addresses also.
+	 */
+	if (cmd == NL80211_CMD_FRAME && len >= 24) {
+		const struct ieee80211_mgmt *mgmt;
+		u16 fc;
+
+		mgmt = (const struct ieee80211_mgmt *) data;
+		fc = le_to_host16(mgmt->frame_control);
+		stype = WLAN_FC_GET_STYPE(fc);
+		auth_type = le_to_host16(mgmt->u.auth.auth_alg);
+	}
+
+	if (cmd == NL80211_CMD_FRAME && stype == WLAN_FC_STYPE_AUTH &&
+	    auth_type == host_to_le16(WLAN_AUTH_PASN)) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: %s: Allow PASN frame for foreign address",
+			   bss->ifname);
+	} else if (cmd != NL80211_CMD_FRAME_TX_STATUS  &&
+		   !(data[4] & 0x01) &&
+		   os_memcmp(bss->addr, data + 4, ETH_ALEN) != 0 &&
+		   (is_zero_ether_addr(bss->rand_addr) ||
+		    os_memcmp(bss->rand_addr, data + 4, ETH_ALEN) != 0) &&
+		   os_memcmp(bss->addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0 &&
+		   (is_zero_ether_addr(drv->first_bss->prev_addr) ||
+		    os_memcmp(bss->prev_addr, data + 4 + ETH_ALEN,
+			      ETH_ALEN) != 0) &&
+		   (!mld_link ||
+		    os_memcmp(mld_link->addr, data + 4, ETH_ALEN) != 0)) {
+		wpa_printf(MSG_MSGDUMP, "nl80211: %s: Ignore MLME frame event "
+			   "for foreign address", bss->ifname);
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "nl80211: MLME event frame",
+		    nla_data(frame), nla_len(frame));
+
+	switch (cmd) {
+	case NL80211_CMD_AUTHENTICATE:
+		mlme_event_auth(drv, nla_data(frame), nla_len(frame));
+		break;
+	case NL80211_CMD_ASSOCIATE:
+		mlme_event_assoc(drv, nla_data(frame), nla_len(frame), wmm,
+				 req_ie);
+		break;
+	case NL80211_CMD_DEAUTHENTICATE:
+		mlme_event_deauth_disassoc(drv, EVENT_DEAUTH,
+					   nla_data(frame), nla_len(frame));
+		break;
+	case NL80211_CMD_DISASSOCIATE:
+		mlme_event_deauth_disassoc(drv, EVENT_DISASSOC,
+					   nla_data(frame), nla_len(frame));
+		break;
+	case NL80211_CMD_FRAME:
+		mlme_event_mgmt(bss, freq, sig, nla_data(frame),
+				nla_len(frame), link_id);
+		break;
+	case NL80211_CMD_FRAME_TX_STATUS:
+		mlme_event_mgmt_tx_status(drv, cookie, nla_data(frame),
+					  nla_len(frame), ack);
+		break;
+	case NL80211_CMD_UNPROT_DEAUTHENTICATE:
+		mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DEAUTH,
+					     nla_data(frame), nla_len(frame));
+		break;
+	case NL80211_CMD_UNPROT_DISASSOCIATE:
+		mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DISASSOC,
+					     nla_data(frame), nla_len(frame));
+		break;
+	case NL80211_CMD_UNPROT_BEACON:
+		mlme_event_unprot_beacon(drv, nla_data(frame), nla_len(frame));
+		break;
+	default:
+		break;
+	}
+}
+
+
+static void mlme_event_michael_mic_failure(struct i802_bss *bss,
+					   struct nlattr *tb[])
+{
+	union wpa_event_data data;
+
+	wpa_printf(MSG_DEBUG, "nl80211: MLME event Michael MIC failure");
+	os_memset(&data, 0, sizeof(data));
+	if (tb[NL80211_ATTR_MAC]) {
+		wpa_hexdump(MSG_DEBUG, "nl80211: Source MAC address",
+			    nla_data(tb[NL80211_ATTR_MAC]),
+			    nla_len(tb[NL80211_ATTR_MAC]));
+		data.michael_mic_failure.src = nla_data(tb[NL80211_ATTR_MAC]);
+	}
+	if (tb[NL80211_ATTR_KEY_SEQ]) {
+		wpa_hexdump(MSG_DEBUG, "nl80211: TSC",
+			    nla_data(tb[NL80211_ATTR_KEY_SEQ]),
+			    nla_len(tb[NL80211_ATTR_KEY_SEQ]));
+	}
+	if (tb[NL80211_ATTR_KEY_TYPE]) {
+		enum nl80211_key_type key_type =
+			nla_get_u32(tb[NL80211_ATTR_KEY_TYPE]);
+		wpa_printf(MSG_DEBUG, "nl80211: Key Type %d", key_type);
+		if (key_type == NL80211_KEYTYPE_PAIRWISE)
+			data.michael_mic_failure.unicast = 1;
+	} else
+		data.michael_mic_failure.unicast = 1;
+
+	if (tb[NL80211_ATTR_KEY_IDX]) {
+		u8 key_id = nla_get_u8(tb[NL80211_ATTR_KEY_IDX]);
+		wpa_printf(MSG_DEBUG, "nl80211: Key Id %d", key_id);
+	}
+
+	wpa_supplicant_event(bss->ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+}
+
+
+static void mlme_event_join_ibss(struct wpa_driver_nl80211_data *drv,
+				 struct nlattr *tb[])
+{
+	unsigned int freq;
+	union wpa_event_data event;
+
+	if (tb[NL80211_ATTR_MAC] == NULL) {
+		wpa_printf(MSG_DEBUG, "nl80211: No address in IBSS joined "
+			   "event");
+		return;
+	}
+	os_memcpy(drv->bssid, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+
+	drv->associated = 1;
+	wpa_printf(MSG_DEBUG, "nl80211: IBSS " MACSTR " joined",
+		   MAC2STR(drv->bssid));
+
+	freq = nl80211_get_assoc_freq(drv);
+	if (freq) {
+		wpa_printf(MSG_DEBUG, "nl80211: IBSS on frequency %u MHz",
+			   freq);
+		drv->first_bss->flink->freq = freq;
+	}
+
+	os_memset(&event, 0, sizeof(event));
+	event.assoc_info.freq = freq;
+
+	wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
+}
+
+
+static void mlme_event_remain_on_channel(struct wpa_driver_nl80211_data *drv,
+					 int cancel_event, struct nlattr *tb[])
+{
+	unsigned int freq, chan_type, duration;
+	union wpa_event_data data;
+	u64 cookie;
+
+	if (tb[NL80211_ATTR_WIPHY_FREQ])
+		freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
+	else
+		freq = 0;
+
+	if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])
+		chan_type = nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+	else
+		chan_type = 0;
+
+	if (tb[NL80211_ATTR_DURATION])
+		duration = nla_get_u32(tb[NL80211_ATTR_DURATION]);
+	else
+		duration = 0;
+
+	if (tb[NL80211_ATTR_COOKIE])
+		cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]);
+	else
+		cookie = 0;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel event (cancel=%d "
+		   "freq=%u channel_type=%u duration=%u cookie=0x%llx (%s))",
+		   cancel_event, freq, chan_type, duration,
+		   (long long unsigned int) cookie,
+		   cookie == drv->remain_on_chan_cookie ? "match" : "unknown");
+
+	if (cookie != drv->remain_on_chan_cookie)
+		return; /* not for us */
+
+	if (cancel_event)
+		drv->pending_remain_on_chan = 0;
+
+	os_memset(&data, 0, sizeof(data));
+	data.remain_on_channel.freq = freq;
+	data.remain_on_channel.duration = duration;
+	wpa_supplicant_event(drv->ctx, cancel_event ?
+			     EVENT_CANCEL_REMAIN_ON_CHANNEL :
+			     EVENT_REMAIN_ON_CHANNEL, &data);
+}
+
+
+static void mlme_event_ft_event(struct wpa_driver_nl80211_data *drv,
+				struct nlattr *tb[])
+{
+	union wpa_event_data data;
+
+	os_memset(&data, 0, sizeof(data));
+
+	if (tb[NL80211_ATTR_IE]) {
+		data.ft_ies.ies = nla_data(tb[NL80211_ATTR_IE]);
+		data.ft_ies.ies_len = nla_len(tb[NL80211_ATTR_IE]);
+	}
+
+	if (tb[NL80211_ATTR_IE_RIC]) {
+		data.ft_ies.ric_ies = nla_data(tb[NL80211_ATTR_IE_RIC]);
+		data.ft_ies.ric_ies_len = nla_len(tb[NL80211_ATTR_IE_RIC]);
+	}
+
+	if (tb[NL80211_ATTR_MAC])
+		os_memcpy(data.ft_ies.target_ap,
+			  nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+
+	wpa_printf(MSG_DEBUG, "nl80211: FT event target_ap " MACSTR,
+		   MAC2STR(data.ft_ies.target_ap));
+
+	wpa_supplicant_event(drv->ctx, EVENT_FT_RESPONSE, &data);
+}
+
+
+static void mlme_event_dh_event(struct wpa_driver_nl80211_data *drv,
+				struct i802_bss *bss,
+				struct nlattr *tb[])
+{
+	union wpa_event_data data;
+
+	if (!is_ap_interface(drv->nlmode))
+		return;
+	if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_IE])
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	data.update_dh.peer = nla_data(tb[NL80211_ATTR_MAC]);
+	data.update_dh.ie = nla_data(tb[NL80211_ATTR_IE]);
+	data.update_dh.ie_len = nla_len(tb[NL80211_ATTR_IE]);
+
+	wpa_printf(MSG_DEBUG, "nl80211: DH event - peer " MACSTR,
+		   MAC2STR(data.update_dh.peer));
+
+	wpa_supplicant_event(bss->ctx, EVENT_UPDATE_DH, &data);
+}
+
+
+static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
+			    struct nlattr *tb[], int external_scan)
+{
+	union wpa_event_data event;
+	struct nlattr *nl;
+	int rem;
+	struct scan_info *info;
+#define MAX_REPORT_FREQS 110
+	int freqs[MAX_REPORT_FREQS];
+	int num_freqs = 0;
+
+	if (!external_scan && drv->scan_for_auth) {
+		drv->scan_for_auth = 0;
+		wpa_printf(MSG_DEBUG, "nl80211: Scan results for missing "
+			   "cfg80211 BSS entry");
+		wpa_driver_nl80211_authenticate_retry(drv);
+		return;
+	}
+
+	os_memset(&event, 0, sizeof(event));
+	info = &event.scan_info;
+	info->aborted = aborted;
+	info->external_scan = external_scan;
+	info->nl_scan_event = 1;
+
+	if (tb[NL80211_ATTR_SCAN_SSIDS]) {
+		nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_SSIDS], rem) {
+			struct wpa_driver_scan_ssid *s =
+				&info->ssids[info->num_ssids];
+			s->ssid = nla_data(nl);
+			s->ssid_len = nla_len(nl);
+			wpa_printf(MSG_DEBUG, "nl80211: Scan probed for SSID '%s'",
+				   wpa_ssid_txt(s->ssid, s->ssid_len));
+			info->num_ssids++;
+			if (info->num_ssids == WPAS_MAX_SCAN_SSIDS)
+				break;
+		}
+	}
+	if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) {
+		char msg[MAX_REPORT_FREQS * 5 + 1], *pos, *end;
+		int res;
+
+		pos = msg;
+		end = pos + sizeof(msg);
+		*pos = '\0';
+
+		nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_FREQUENCIES], rem)
+		{
+			freqs[num_freqs] = nla_get_u32(nl);
+			res = os_snprintf(pos, end - pos, " %d",
+					  freqs[num_freqs]);
+			if (!os_snprintf_error(end - pos, res))
+				pos += res;
+			num_freqs++;
+			if (num_freqs == MAX_REPORT_FREQS)
+				break;
+		}
+		info->freqs = freqs;
+		info->num_freqs = num_freqs;
+		msg[sizeof(msg) - 1] = '\0';
+		wpa_printf(MSG_DEBUG, "nl80211: Scan included frequencies:%s",
+			   msg);
+	}
+
+	if (tb[NL80211_ATTR_SCAN_START_TIME_TSF] &&
+	    tb[NL80211_ATTR_SCAN_START_TIME_TSF_BSSID]) {
+		info->scan_start_tsf =
+			nla_get_u64(tb[NL80211_ATTR_SCAN_START_TIME_TSF]);
+		os_memcpy(info->scan_start_tsf_bssid,
+			  nla_data(tb[NL80211_ATTR_SCAN_START_TIME_TSF_BSSID]),
+			  ETH_ALEN);
+	}
+
+	wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event);
+}
+
+
+static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv,
+			      struct nlattr *tb[])
+{
+	static struct nla_policy cqm_policy[NL80211_ATTR_CQM_MAX + 1] = {
+		[NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
+		[NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 },
+		[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
+		[NL80211_ATTR_CQM_PKT_LOSS_EVENT] = { .type = NLA_U32 },
+		[NL80211_ATTR_CQM_TXE_RATE] = { .type = NLA_U32 },
+		[NL80211_ATTR_CQM_TXE_PKTS] = { .type = NLA_U32 },
+		[NL80211_ATTR_CQM_TXE_INTVL] = { .type = NLA_U32 },
+		[NL80211_ATTR_CQM_BEACON_LOSS_EVENT] = { .type = NLA_FLAG },
+	};
+	struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1];
+	enum nl80211_cqm_rssi_threshold_event event;
+	union wpa_event_data ed;
+	int res;
+
+	if (tb[NL80211_ATTR_CQM] == NULL ||
+	    nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, tb[NL80211_ATTR_CQM],
+			     cqm_policy)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid CQM event");
+		return;
+	}
+
+	os_memset(&ed, 0, sizeof(ed));
+
+	if (cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]) {
+		if (!tb[NL80211_ATTR_MAC])
+			return;
+		os_memcpy(ed.low_ack.addr, nla_data(tb[NL80211_ATTR_MAC]),
+			  ETH_ALEN);
+		ed.low_ack.num_packets =
+			nla_get_u32(cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]);
+		wpa_printf(MSG_DEBUG, "nl80211: Packet loss event for " MACSTR
+			   " (num_packets %u)",
+			   MAC2STR(ed.low_ack.addr), ed.low_ack.num_packets);
+		wpa_supplicant_event(drv->ctx, EVENT_STATION_LOW_ACK, &ed);
+		return;
+	}
+
+	if (cqm[NL80211_ATTR_CQM_BEACON_LOSS_EVENT]) {
+		wpa_printf(MSG_DEBUG, "nl80211: Beacon loss event");
+		wpa_supplicant_event(drv->ctx, EVENT_BEACON_LOSS, NULL);
+		return;
+	}
+
+	if (cqm[NL80211_ATTR_CQM_TXE_RATE] &&
+	    cqm[NL80211_ATTR_CQM_TXE_PKTS] &&
+	    cqm[NL80211_ATTR_CQM_TXE_INTVL] &&
+	    cqm[NL80211_ATTR_MAC]) {
+		wpa_printf(MSG_DEBUG, "nl80211: CQM TXE event for " MACSTR
+			   " (rate: %u pkts: %u interval: %u)",
+			   MAC2STR((u8 *) nla_data(cqm[NL80211_ATTR_MAC])),
+			   nla_get_u32(cqm[NL80211_ATTR_CQM_TXE_RATE]),
+			   nla_get_u32(cqm[NL80211_ATTR_CQM_TXE_PKTS]),
+			   nla_get_u32(cqm[NL80211_ATTR_CQM_TXE_INTVL]));
+		return;
+	}
+
+	if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Not a CQM RSSI threshold event");
+		return;
+	}
+	event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]);
+
+	if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) {
+		wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
+			   "event: RSSI high");
+		ed.signal_change.above_threshold = 1;
+	} else if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW) {
+		wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
+			   "event: RSSI low");
+		ed.signal_change.above_threshold = 0;
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Unknown CQM RSSI threshold event: %d",
+			   event);
+		return;
+	}
+
+	/*
+	 * nl80211_get_link_signal() and nl80211_get_link_noise() set default
+	 * values in case querying the driver fails.
+	 */
+	res = nl80211_get_link_signal(drv, drv->bssid, &ed.signal_change.data);
+	if (res == 0) {
+		wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm  txrate: %lu",
+			   ed.signal_change.data.signal,
+			   ed.signal_change.data.current_tx_rate);
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Querying the driver for signal info failed");
+	}
+
+	res = nl80211_get_link_noise(drv, &ed.signal_change);
+	if (res == 0) {
+		wpa_printf(MSG_DEBUG, "nl80211: Noise: %d dBm",
+			   ed.signal_change.current_noise);
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Querying the driver for noise info failed");
+	}
+
+	wpa_supplicant_event(drv->ctx, EVENT_SIGNAL_CHANGE, &ed);
+}
+
+
+static void nl80211_new_peer_candidate(struct wpa_driver_nl80211_data *drv,
+				       struct nlattr **tb)
+{
+	const u8 *addr;
+	union wpa_event_data data;
+
+	if (drv->nlmode != NL80211_IFTYPE_MESH_POINT ||
+	    !tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_IE])
+		return;
+
+	addr = nla_data(tb[NL80211_ATTR_MAC]);
+	wpa_printf(MSG_DEBUG, "nl80211: New peer candidate " MACSTR,
+		   MAC2STR(addr));
+
+	os_memset(&data, 0, sizeof(data));
+	data.mesh_peer.peer = addr;
+	data.mesh_peer.ies = nla_data(tb[NL80211_ATTR_IE]);
+	data.mesh_peer.ie_len = nla_len(tb[NL80211_ATTR_IE]);
+	wpa_supplicant_event(drv->ctx, EVENT_NEW_PEER_CANDIDATE, &data);
+}
+
+
+static void nl80211_new_station_event(struct wpa_driver_nl80211_data *drv,
+				      struct i802_bss *bss,
+				      struct nlattr **tb)
+{
+	u8 *addr;
+	union wpa_event_data data;
+
+	if (tb[NL80211_ATTR_MAC] == NULL)
+		return;
+	addr = nla_data(tb[NL80211_ATTR_MAC]);
+	wpa_printf(MSG_DEBUG, "nl80211: New station " MACSTR, MAC2STR(addr));
+
+	if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) {
+		u8 *ies = NULL;
+		size_t ies_len = 0;
+		if (tb[NL80211_ATTR_IE]) {
+			ies = nla_data(tb[NL80211_ATTR_IE]);
+			ies_len = nla_len(tb[NL80211_ATTR_IE]);
+		}
+		wpa_hexdump(MSG_DEBUG, "nl80211: Assoc Req IEs", ies, ies_len);
+		drv_event_assoc(bss->ctx, addr, ies, ies_len, 0);
+		return;
+	}
+
+	if (drv->nlmode != NL80211_IFTYPE_ADHOC)
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	os_memcpy(data.ibss_rsn_start.peer, addr, ETH_ALEN);
+	wpa_supplicant_event(bss->ctx, EVENT_IBSS_RSN_START, &data);
+}
+
+
+static void nl80211_del_station_event(struct wpa_driver_nl80211_data *drv,
+				      struct i802_bss *bss,
+				      struct nlattr **tb)
+{
+	u8 *addr;
+	union wpa_event_data data;
+
+	if (tb[NL80211_ATTR_MAC] == NULL)
+		return;
+	addr = nla_data(tb[NL80211_ATTR_MAC]);
+	wpa_printf(MSG_DEBUG, "nl80211: Delete station " MACSTR,
+		   MAC2STR(addr));
+
+	if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) {
+		drv_event_disassoc(bss->ctx, addr);
+		return;
+	}
+
+	if (drv->nlmode != NL80211_IFTYPE_ADHOC)
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	os_memcpy(data.ibss_peer_lost.peer, addr, ETH_ALEN);
+	wpa_supplicant_event(bss->ctx, EVENT_IBSS_PEER_LOST, &data);
+}
+
+
+static void nl80211_rekey_offload_event(struct wpa_driver_nl80211_data *drv,
+					struct nlattr **tb)
+{
+	struct nlattr *rekey_info[NUM_NL80211_REKEY_DATA];
+	static struct nla_policy rekey_policy[NUM_NL80211_REKEY_DATA] = {
+		[NL80211_REKEY_DATA_KEK] = {
+			.minlen = NL80211_KEK_LEN,
+			.maxlen = NL80211_KEK_LEN,
+		},
+		[NL80211_REKEY_DATA_KCK] = {
+			.minlen = NL80211_KCK_LEN,
+			.maxlen = NL80211_KCK_LEN,
+		},
+		[NL80211_REKEY_DATA_REPLAY_CTR] = {
+			.minlen = NL80211_REPLAY_CTR_LEN,
+			.maxlen = NL80211_REPLAY_CTR_LEN,
+		},
+	};
+	union wpa_event_data data;
+
+	if (!tb[NL80211_ATTR_MAC] ||
+	    !tb[NL80211_ATTR_REKEY_DATA] ||
+	    nla_parse_nested(rekey_info, MAX_NL80211_REKEY_DATA,
+			     tb[NL80211_ATTR_REKEY_DATA], rekey_policy) ||
+	    !rekey_info[NL80211_REKEY_DATA_REPLAY_CTR])
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	data.driver_gtk_rekey.bssid = nla_data(tb[NL80211_ATTR_MAC]);
+	wpa_printf(MSG_DEBUG, "nl80211: Rekey offload event for BSSID " MACSTR,
+		   MAC2STR(data.driver_gtk_rekey.bssid));
+	data.driver_gtk_rekey.replay_ctr =
+		nla_data(rekey_info[NL80211_REKEY_DATA_REPLAY_CTR]);
+	wpa_hexdump(MSG_DEBUG, "nl80211: Rekey offload - Replay Counter",
+		    data.driver_gtk_rekey.replay_ctr, NL80211_REPLAY_CTR_LEN);
+	wpa_supplicant_event(drv->ctx, EVENT_DRIVER_GTK_REKEY, &data);
+}
+
+
+static void nl80211_pmksa_candidate_event(struct wpa_driver_nl80211_data *drv,
+					  struct nlattr **tb)
+{
+	struct nlattr *cand[NUM_NL80211_PMKSA_CANDIDATE];
+	static struct nla_policy cand_policy[NUM_NL80211_PMKSA_CANDIDATE] = {
+		[NL80211_PMKSA_CANDIDATE_INDEX] = { .type = NLA_U32 },
+		[NL80211_PMKSA_CANDIDATE_BSSID] = {
+			.minlen = ETH_ALEN,
+			.maxlen = ETH_ALEN,
+		},
+		[NL80211_PMKSA_CANDIDATE_PREAUTH] = { .type = NLA_FLAG },
+	};
+	union wpa_event_data data;
+
+	wpa_printf(MSG_DEBUG, "nl80211: PMKSA candidate event");
+
+	if (!tb[NL80211_ATTR_PMKSA_CANDIDATE] ||
+	    nla_parse_nested(cand, MAX_NL80211_PMKSA_CANDIDATE,
+			     tb[NL80211_ATTR_PMKSA_CANDIDATE], cand_policy) ||
+	    !cand[NL80211_PMKSA_CANDIDATE_INDEX] ||
+	    !cand[NL80211_PMKSA_CANDIDATE_BSSID])
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	os_memcpy(data.pmkid_candidate.bssid,
+		  nla_data(cand[NL80211_PMKSA_CANDIDATE_BSSID]), ETH_ALEN);
+	data.pmkid_candidate.index =
+		nla_get_u32(cand[NL80211_PMKSA_CANDIDATE_INDEX]);
+	data.pmkid_candidate.preauth =
+		cand[NL80211_PMKSA_CANDIDATE_PREAUTH] != NULL;
+	wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data);
+}
+
+
+static void nl80211_client_probe_event(struct wpa_driver_nl80211_data *drv,
+				       struct nlattr **tb)
+{
+	union wpa_event_data data;
+	const u8 *addr;
+	u64 cookie = 0;
+
+	addr = nla_data(tb[NL80211_ATTR_MAC]);
+	if (!addr)
+		return;
+	if (tb[NL80211_ATTR_COOKIE])
+		cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]);
+	wpa_printf(MSG_DEBUG, "nl80211: Probe client event (addr=" MACSTR
+		   " ack=%d cookie=%llu)", MAC2STR(addr),
+		   tb[NL80211_ATTR_ACK] != NULL,
+		   (long long unsigned int) cookie);
+	if (!tb[NL80211_ATTR_ACK])
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	os_memcpy(data.client_poll.addr, addr, ETH_ALEN);
+	wpa_supplicant_event(drv->ctx, EVENT_DRIVER_CLIENT_POLL_OK, &data);
+}
+
+
+static void nl80211_tdls_oper_event(struct wpa_driver_nl80211_data *drv,
+				    struct nlattr **tb)
+{
+	union wpa_event_data data;
+
+	wpa_printf(MSG_DEBUG, "nl80211: TDLS operation event");
+
+	if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_TDLS_OPERATION])
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	os_memcpy(data.tdls.peer, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+	switch (nla_get_u8(tb[NL80211_ATTR_TDLS_OPERATION])) {
+	case NL80211_TDLS_SETUP:
+		wpa_printf(MSG_DEBUG, "nl80211: TDLS setup request for peer "
+			   MACSTR, MAC2STR(data.tdls.peer));
+		data.tdls.oper = TDLS_REQUEST_SETUP;
+		break;
+	case NL80211_TDLS_TEARDOWN:
+		wpa_printf(MSG_DEBUG, "nl80211: TDLS teardown request for peer "
+			   MACSTR, MAC2STR(data.tdls.peer));
+		data.tdls.oper = TDLS_REQUEST_TEARDOWN;
+		break;
+	case NL80211_TDLS_DISCOVERY_REQ:
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: TDLS discovery request for peer " MACSTR,
+			   MAC2STR(data.tdls.peer));
+		data.tdls.oper = TDLS_REQUEST_DISCOVER;
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "nl80211: Unsupported TDLS operatione "
+			   "event");
+		return;
+	}
+	if (tb[NL80211_ATTR_REASON_CODE]) {
+		data.tdls.reason_code =
+			nla_get_u16(tb[NL80211_ATTR_REASON_CODE]);
+	}
+
+	wpa_supplicant_event(drv->ctx, EVENT_TDLS, &data);
+}
+
+
+static void nl80211_stop_ap(struct wpa_driver_nl80211_data *drv,
+			    struct nlattr **tb)
+{
+	wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_UNAVAILABLE, NULL);
+}
+
+
+static void nl80211_connect_failed_event(struct wpa_driver_nl80211_data *drv,
+					 struct nlattr **tb)
+{
+	union wpa_event_data data;
+	u32 reason;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Connect failed event");
+
+	if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_CONN_FAILED_REASON])
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	os_memcpy(data.connect_failed_reason.addr,
+		  nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+
+	reason = nla_get_u32(tb[NL80211_ATTR_CONN_FAILED_REASON]);
+	switch (reason) {
+	case NL80211_CONN_FAIL_MAX_CLIENTS:
+		wpa_printf(MSG_DEBUG, "nl80211: Max client reached");
+		data.connect_failed_reason.code = MAX_CLIENT_REACHED;
+		break;
+	case NL80211_CONN_FAIL_BLOCKED_CLIENT:
+		wpa_printf(MSG_DEBUG, "nl80211: Blocked client " MACSTR
+			   " tried to connect",
+			   MAC2STR(data.connect_failed_reason.addr));
+		data.connect_failed_reason.code = BLOCKED_CLIENT;
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "nl8021l: Unknown connect failed reason "
+			   "%u", reason);
+		return;
+	}
+
+	wpa_supplicant_event(drv->ctx, EVENT_CONNECT_FAILED_REASON, &data);
+}
+
+
+static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
+				struct nlattr **tb)
+{
+	union wpa_event_data data;
+	enum nl80211_radar_event event_type;
+
+	if (!tb[NL80211_ATTR_WIPHY_FREQ] || !tb[NL80211_ATTR_RADAR_EVENT])
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
+	event_type = nla_get_u32(tb[NL80211_ATTR_RADAR_EVENT]);
+
+	/* Check HT params */
+	if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
+		data.dfs_event.ht_enabled = 1;
+		data.dfs_event.chan_offset = 0;
+
+		switch (nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])) {
+		case NL80211_CHAN_NO_HT:
+			data.dfs_event.ht_enabled = 0;
+			break;
+		case NL80211_CHAN_HT20:
+			break;
+		case NL80211_CHAN_HT40PLUS:
+			data.dfs_event.chan_offset = 1;
+			break;
+		case NL80211_CHAN_HT40MINUS:
+			data.dfs_event.chan_offset = -1;
+			break;
+		}
+	}
+
+	/* Get VHT params */
+	if (tb[NL80211_ATTR_CHANNEL_WIDTH])
+		data.dfs_event.chan_width =
+			convert2width(nla_get_u32(
+					      tb[NL80211_ATTR_CHANNEL_WIDTH]));
+	if (tb[NL80211_ATTR_CENTER_FREQ1])
+		data.dfs_event.cf1 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]);
+	if (tb[NL80211_ATTR_CENTER_FREQ2])
+		data.dfs_event.cf2 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]);
+
+	wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz, ht: %d, offset: %d, width: %d, cf1: %dMHz, cf2: %dMHz",
+		   data.dfs_event.freq, data.dfs_event.ht_enabled,
+		   data.dfs_event.chan_offset, data.dfs_event.chan_width,
+		   data.dfs_event.cf1, data.dfs_event.cf2);
+
+	switch (event_type) {
+	case NL80211_RADAR_DETECTED:
+		wpa_supplicant_event(drv->ctx, EVENT_DFS_RADAR_DETECTED, &data);
+		break;
+	case NL80211_RADAR_CAC_FINISHED:
+		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_FINISHED, &data);
+		break;
+	case NL80211_RADAR_CAC_ABORTED:
+		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_ABORTED, &data);
+		break;
+	case NL80211_RADAR_NOP_FINISHED:
+		wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data);
+		break;
+	case NL80211_RADAR_PRE_CAC_EXPIRED:
+		wpa_supplicant_event(drv->ctx, EVENT_DFS_PRE_CAC_EXPIRED,
+				     &data);
+		break;
+	case NL80211_RADAR_CAC_STARTED:
+		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d "
+			   "received", event_type);
+		break;
+	}
+}
+
+
+static void nl80211_spurious_frame(struct i802_bss *bss, struct nlattr **tb,
+				   int wds)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	union wpa_event_data event;
+
+	if (!tb[NL80211_ATTR_MAC])
+		return;
+
+	os_memset(&event, 0, sizeof(event));
+	event.rx_from_unknown.bssid = bss->addr;
+	event.rx_from_unknown.addr = nla_data(tb[NL80211_ATTR_MAC]);
+	event.rx_from_unknown.wds = wds;
+
+	wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event);
+}
+
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+
+static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv,
+				   const u8 *data, size_t len)
+{
+	u32 i, count;
+	union wpa_event_data event;
+	struct wpa_freq_range *range = NULL;
+	const struct qca_avoid_freq_list *freq_range;
+
+	freq_range = (const struct qca_avoid_freq_list *) data;
+	if (len < sizeof(freq_range->count))
+		return;
+
+	count = freq_range->count;
+	if (len < sizeof(freq_range->count) +
+	    count * sizeof(struct qca_avoid_freq_range)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Ignored too short avoid frequency list (len=%u)",
+			   (unsigned int) len);
+		return;
+	}
+
+	if (count > 0) {
+		range = os_calloc(count, sizeof(struct wpa_freq_range));
+		if (range == NULL)
+			return;
+	}
+
+	os_memset(&event, 0, sizeof(event));
+	for (i = 0; i < count; i++) {
+		unsigned int idx = event.freq_range.num;
+		range[idx].min = freq_range->range[i].start_freq;
+		range[idx].max = freq_range->range[i].end_freq;
+		wpa_printf(MSG_DEBUG, "nl80211: Avoid frequency range: %u-%u",
+			   range[idx].min, range[idx].max);
+		if (range[idx].min > range[idx].max) {
+			wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid frequency range");
+			continue;
+		}
+		event.freq_range.num++;
+	}
+	event.freq_range.range = range;
+
+	wpa_supplicant_event(drv->ctx, EVENT_AVOID_FREQUENCIES, &event);
+
+	os_free(range);
+}
+
+
+static enum hostapd_hw_mode get_qca_hw_mode(u8 hw_mode)
+{
+	switch (hw_mode) {
+	case QCA_ACS_MODE_IEEE80211B:
+		return HOSTAPD_MODE_IEEE80211B;
+	case QCA_ACS_MODE_IEEE80211G:
+		return HOSTAPD_MODE_IEEE80211G;
+	case QCA_ACS_MODE_IEEE80211A:
+		return HOSTAPD_MODE_IEEE80211A;
+	case QCA_ACS_MODE_IEEE80211AD:
+		return HOSTAPD_MODE_IEEE80211AD;
+	case QCA_ACS_MODE_IEEE80211ANY:
+		return HOSTAPD_MODE_IEEE80211ANY;
+	default:
+		return NUM_HOSTAPD_MODES;
+	}
+}
+
+
+static unsigned int chan_to_freq(struct wpa_driver_nl80211_data *drv,
+				 u8 chan, enum hostapd_hw_mode hw_mode)
+{
+	if (hw_mode == NUM_HOSTAPD_MODES) {
+		/* For drivers that do not report ACS_HW_MODE */
+		u16 num_modes, flags;
+		struct hostapd_hw_modes *modes;
+		u8 dfs_domain;
+		int i;
+
+		modes = nl80211_get_hw_feature_data(drv->first_bss, &num_modes,
+						    &flags, &dfs_domain);
+		if (!modes) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Fetching hardware mode failed");
+			goto try_2_4_or_5;
+		}
+		if (num_modes == 1)
+			hw_mode = modes[0].mode;
+
+		for (i = 0; i < num_modes; i++) {
+			os_free(modes[i].channels);
+			os_free(modes[i].rates);
+		}
+
+		os_free(modes);
+	}
+
+	if (hw_mode == HOSTAPD_MODE_IEEE80211AD) {
+		if (chan >= 1 && chan <= 6)
+			return 56160 + (2160 * chan);
+		return 0;
+	}
+
+try_2_4_or_5:
+	if (chan >= 1 && chan <= 13)
+		return 2407 + 5 * chan;
+	if (chan == 14)
+		return 2484;
+	if (chan >= 36 && chan <= 177)
+		return 5000 + 5 * chan;
+
+	return 0;
+}
+
+
+static void qca_nl80211_acs_select_ch(struct wpa_driver_nl80211_data *drv,
+				   const u8 *data, size_t len)
+{
+	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ACS_MAX + 1];
+	union wpa_event_data event;
+	u8 chan;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: ACS channel selection vendor event received");
+
+	if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ACS_MAX,
+		      (struct nlattr *) data, len, NULL) ||
+	    (!tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_FREQUENCY] &&
+	     !tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL]) ||
+	    (!tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_FREQUENCY] &&
+	     !tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL]))
+		return;
+
+	os_memset(&event, 0, sizeof(event));
+	event.acs_selected_channels.hw_mode = NUM_HOSTAPD_MODES;
+
+	if (tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]) {
+		u8 hw_mode = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]);
+
+		event.acs_selected_channels.hw_mode = get_qca_hw_mode(hw_mode);
+		if (event.acs_selected_channels.hw_mode == NUM_HOSTAPD_MODES ||
+		    event.acs_selected_channels.hw_mode ==
+		    HOSTAPD_MODE_IEEE80211ANY) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Invalid hw_mode %d in ACS selection event",
+				   hw_mode);
+			return;
+		}
+	}
+
+	if (tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_FREQUENCY]) {
+		event.acs_selected_channels.pri_freq = nla_get_u32(
+			tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_FREQUENCY]);
+	} else {
+		chan = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL]);
+		event.acs_selected_channels.pri_freq =
+			chan_to_freq(drv, chan,
+				     event.acs_selected_channels.hw_mode);
+	}
+
+	if (tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_FREQUENCY]) {
+		event.acs_selected_channels.sec_freq = nla_get_u32(
+			tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_FREQUENCY]);
+	} else {
+		chan = nla_get_u8(
+			tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL]);
+		event.acs_selected_channels.sec_freq =
+			chan_to_freq(drv, chan,
+				     event.acs_selected_channels.hw_mode);
+	}
+
+	if (tb[QCA_WLAN_VENDOR_ATTR_ACS_EDMG_CHANNEL])
+		event.acs_selected_channels.edmg_channel =
+			nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_EDMG_CHANNEL]);
+	if (tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL])
+		event.acs_selected_channels.vht_seg0_center_ch =
+			nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]);
+	if (tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL])
+		event.acs_selected_channels.vht_seg1_center_ch =
+			nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL]);
+	if (tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH])
+		event.acs_selected_channels.ch_width =
+			nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH]);
+	if (tb[QCA_WLAN_VENDOR_ATTR_ACS_PUNCTURE_BITMAP])
+		event.acs_selected_channels.puncture_bitmap =
+			nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_ACS_PUNCTURE_BITMAP]);
+	wpa_printf(MSG_INFO,
+		   "nl80211: ACS Results: PFreq: %d SFreq: %d BW: %d VHT0: %d VHT1: %d HW_MODE: %d EDMGCH: %d PUNCBITMAP: 0x%x",
+		   event.acs_selected_channels.pri_freq,
+		   event.acs_selected_channels.sec_freq,
+		   event.acs_selected_channels.ch_width,
+		   event.acs_selected_channels.vht_seg0_center_ch,
+		   event.acs_selected_channels.vht_seg1_center_ch,
+		   event.acs_selected_channels.hw_mode,
+		   event.acs_selected_channels.edmg_channel,
+		   event.acs_selected_channels.puncture_bitmap);
+
+	/* Ignore ACS channel list check for backwards compatibility */
+
+	wpa_supplicant_event(drv->ctx, EVENT_ACS_CHANNEL_SELECTED, &event);
+}
+
+
+static void qca_nl80211_key_mgmt_auth(struct wpa_driver_nl80211_data *drv,
+				      const u8 *data, size_t len)
+{
+	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX + 1];
+	u8 *bssid;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Key management roam+auth vendor event received");
+
+	if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX,
+		      (struct nlattr *) data, len, NULL) ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID] ||
+	    nla_len(tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID]) != ETH_ALEN ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE] ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE] ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED])
+		return;
+
+	bssid = nla_data(tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID]);
+	wpa_printf(MSG_DEBUG, "  * roam BSSID " MACSTR, MAC2STR(bssid));
+
+	mlme_event_connect(drv, NL80211_CMD_ROAM, true, NULL,
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID],
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE],
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE],
+			   NULL, NULL,
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED],
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR],
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK],
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK],
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS],
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_FILS_ERP_NEXT_SEQ_NUM],
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMK],
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMKID],
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MLO_LINKS]);
+
+#ifdef ANDROID
+#ifdef ANDROID_LIB_EVENT
+	wpa_driver_nl80211_driver_event(
+		drv, OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH,
+		data, len);
+#endif /* ANDROID_LIB_EVENT */
+#endif /* ANDROID */
+}
+
+
+static void
+qca_nl80211_key_mgmt_auth_handler(struct wpa_driver_nl80211_data *drv,
+				  const u8 *data, size_t len)
+{
+	if (!drv->roam_indication_done) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Pending roam indication, delay processing roam+auth vendor event");
+
+		os_free(drv->pending_roam_data);
+		drv->pending_roam_data = os_memdup(data, len);
+		if (!drv->pending_roam_data)
+			return;
+		drv->pending_roam_data_len = len;
+		return;
+	}
+	drv->roam_indication_done = false;
+	qca_nl80211_key_mgmt_auth(drv, data, len);
+}
+
+
+static void qca_nl80211_dfs_offload_radar_event(
+	struct wpa_driver_nl80211_data *drv, u32 subcmd, u8 *msg, int length)
+{
+	union wpa_event_data data;
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: DFS offload radar vendor event received");
+
+	if (nla_parse(tb, NL80211_ATTR_MAX,
+		      (struct nlattr *) msg, length, NULL))
+		return;
+
+	if (!tb[NL80211_ATTR_WIPHY_FREQ]) {
+		wpa_printf(MSG_INFO,
+			   "nl80211: Error parsing WIPHY_FREQ in FS offload radar vendor event");
+		return;
+	}
+
+	os_memset(&data, 0, sizeof(data));
+	data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
+
+	wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz",
+		   data.dfs_event.freq);
+
+	/* Check HT params */
+	if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
+		data.dfs_event.ht_enabled = 1;
+		data.dfs_event.chan_offset = 0;
+
+		switch (nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])) {
+		case NL80211_CHAN_NO_HT:
+			data.dfs_event.ht_enabled = 0;
+			break;
+		case NL80211_CHAN_HT20:
+			break;
+		case NL80211_CHAN_HT40PLUS:
+			data.dfs_event.chan_offset = 1;
+			break;
+		case NL80211_CHAN_HT40MINUS:
+			data.dfs_event.chan_offset = -1;
+			break;
+		}
+	}
+
+	/* Get VHT params */
+	if (tb[NL80211_ATTR_CHANNEL_WIDTH])
+		data.dfs_event.chan_width =
+			convert2width(nla_get_u32(
+					      tb[NL80211_ATTR_CHANNEL_WIDTH]));
+	if (tb[NL80211_ATTR_CENTER_FREQ1])
+		data.dfs_event.cf1 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]);
+	if (tb[NL80211_ATTR_CENTER_FREQ2])
+		data.dfs_event.cf2 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]);
+
+	wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz, ht: %d, "
+		    "offset: %d, width: %d, cf1: %dMHz, cf2: %dMHz",
+		    data.dfs_event.freq, data.dfs_event.ht_enabled,
+		    data.dfs_event.chan_offset, data.dfs_event.chan_width,
+		    data.dfs_event.cf1, data.dfs_event.cf2);
+
+	switch (subcmd) {
+	case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED:
+		wpa_supplicant_event(drv->ctx, EVENT_DFS_RADAR_DETECTED, &data);
+		break;
+	case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED:
+		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data);
+		break;
+	case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED:
+		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_FINISHED, &data);
+		break;
+	case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED:
+		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_ABORTED, &data);
+		break;
+	case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED:
+		wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Unknown DFS offload radar event %d received",
+			   subcmd);
+		break;
+	}
+}
+
+
+static void qca_nl80211_scan_trigger_event(struct wpa_driver_nl80211_data *drv,
+					   u8 *data, size_t len)
+{
+	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1];
+	u64 cookie = 0;
+	union wpa_event_data event;
+	struct scan_info *info;
+
+	if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX,
+		      (struct nlattr *) data, len, NULL) ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE])
+		return;
+
+	cookie = nla_get_u64(tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]);
+	if (cookie != drv->vendor_scan_cookie) {
+		/* External scan trigger event, ignore */
+		return;
+	}
+
+	/* Cookie match, own scan */
+	os_memset(&event, 0, sizeof(event));
+	info = &event.scan_info;
+	info->external_scan = 0;
+	info->nl_scan_event = 0;
+
+	drv->scan_state = SCAN_STARTED;
+	wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, &event);
+}
+
+
+static void send_vendor_scan_event(struct wpa_driver_nl80211_data *drv,
+				   int aborted, struct nlattr *tb[],
+				   int external_scan)
+{
+	union wpa_event_data event;
+	struct nlattr *nl;
+	int rem;
+	struct scan_info *info;
+	int freqs[MAX_REPORT_FREQS];
+	int num_freqs = 0;
+
+	os_memset(&event, 0, sizeof(event));
+	info = &event.scan_info;
+	info->aborted = aborted;
+	info->external_scan = external_scan;
+
+	if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS]) {
+		nla_for_each_nested(nl,
+				    tb[QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS], rem) {
+			struct wpa_driver_scan_ssid *s =
+				&info->ssids[info->num_ssids];
+			s->ssid = nla_data(nl);
+			s->ssid_len = nla_len(nl);
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Scan probed for SSID '%s'",
+				   wpa_ssid_txt(s->ssid, s->ssid_len));
+			info->num_ssids++;
+			if (info->num_ssids == WPAS_MAX_SCAN_SSIDS)
+				break;
+		}
+	}
+
+	if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES]) {
+		char msg[500], *pos, *end;
+		int res;
+
+		pos = msg;
+		end = pos + sizeof(msg);
+		*pos = '\0';
+
+		nla_for_each_nested(nl,
+				    tb[QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES],
+				    rem) {
+			freqs[num_freqs] = nla_get_u32(nl);
+			res = os_snprintf(pos, end - pos, " %d",
+					  freqs[num_freqs]);
+			if (!os_snprintf_error(end - pos, res))
+				pos += res;
+			num_freqs++;
+			if (num_freqs == MAX_REPORT_FREQS - 1)
+				break;
+		}
+
+		info->freqs = freqs;
+		info->num_freqs = num_freqs;
+		wpa_printf(MSG_DEBUG, "nl80211: Scan included frequencies:%s",
+			   msg);
+	}
+	wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event);
+}
+
+
+static void qca_nl80211_scan_done_event(struct wpa_driver_nl80211_data *drv,
+					u8 *data, size_t len)
+{
+	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1];
+	u64 cookie = 0;
+	enum scan_status status;
+	int external_scan;
+
+	if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX,
+		      (struct nlattr *) data, len, NULL) ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_SCAN_STATUS] ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE])
+		return;
+
+	status = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_SCAN_STATUS]);
+	if (status >= VENDOR_SCAN_STATUS_MAX)
+		return; /* invalid status */
+
+	cookie = nla_get_u64(tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]);
+	if (cookie != drv->vendor_scan_cookie) {
+		/* Event from an external scan, get scan results */
+		external_scan = 1;
+	} else {
+		external_scan = 0;
+		if (status == VENDOR_SCAN_STATUS_NEW_RESULTS)
+			drv->scan_state = SCAN_COMPLETED;
+		else
+			drv->scan_state = SCAN_ABORTED;
+
+		eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
+				     drv->ctx);
+		drv->vendor_scan_cookie = 0;
+		drv->last_scan_cmd = 0;
+	}
+
+	send_vendor_scan_event(drv, (status == VENDOR_SCAN_STATUS_ABORTED), tb,
+			       external_scan);
+}
+
+
+static void qca_nl80211_p2p_lo_stop_event(struct wpa_driver_nl80211_data *drv,
+					  u8 *data, size_t len)
+{
+	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_MAX + 1];
+	union wpa_event_data event;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: P2P listen offload stop vendor event received");
+
+	if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_MAX,
+		      (struct nlattr *) data, len, NULL) ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_STOP_REASON])
+		return;
+
+	os_memset(&event, 0, sizeof(event));
+	event.p2p_lo_stop.reason_code =
+		nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_STOP_REASON]);
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: P2P Listen offload stop reason: %d",
+		   event.p2p_lo_stop.reason_code);
+	wpa_supplicant_event(drv->ctx, EVENT_P2P_LO_STOP, &event);
+}
+
+
+#ifdef CONFIG_PASN
+
+static void qca_nl80211_pasn_auth(struct wpa_driver_nl80211_data *drv,
+				  u8 *data, size_t len)
+{
+	int ret = -EINVAL;
+	struct nlattr *attr;
+	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+	struct nlattr *cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_MAX + 1];
+	unsigned int n_peers = 0, idx = 0;
+	int rem_conf;
+	enum qca_wlan_vendor_pasn_action action;
+	union wpa_event_data event;
+
+	if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_PASN_MAX,
+		      (struct nlattr *) data, len, NULL) ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_PASN_PEERS] ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_PASN_ACTION]) {
+		return;
+	}
+
+	os_memset(&event, 0, sizeof(event));
+	action = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_PASN_ACTION]);
+	switch (action) {
+	case QCA_WLAN_VENDOR_PASN_ACTION_AUTH:
+		event.pasn_auth.action = PASN_ACTION_AUTH;
+		break;
+	case QCA_WLAN_VENDOR_PASN_ACTION_DELETE_SECURE_RANGING_CONTEXT:
+		event.pasn_auth.action =
+			PASN_ACTION_DELETE_SECURE_RANGING_CONTEXT;
+		break;
+	default:
+		return;
+	}
+
+	nla_for_each_nested(attr, tb[QCA_WLAN_VENDOR_ATTR_PASN_PEERS], rem_conf)
+		n_peers++;
+
+	if (n_peers > WPAS_MAX_PASN_PEERS) {
+		wpa_printf(MSG_DEBUG, "nl80211: PASN auth: too many peers (%d)",
+			    n_peers);
+		return;
+	}
+
+	nla_for_each_nested(attr, tb[QCA_WLAN_VENDOR_ATTR_PASN_PEERS],
+			    rem_conf) {
+		struct nlattr *nl_src, *nl_peer;
+
+		ret = nla_parse_nested(cfg, QCA_WLAN_VENDOR_ATTR_PASN_PEER_MAX,
+				       attr, NULL);
+		if (ret)
+			return;
+		nl_src = cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_SRC_ADDR];
+		nl_peer = cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_MAC_ADDR];
+		if (nl_src)
+			os_memcpy(event.pasn_auth.peer[idx].own_addr,
+				  nla_data(nl_src), ETH_ALEN);
+		if (nl_peer)
+			os_memcpy(event.pasn_auth.peer[idx].peer_addr,
+				  nla_data(nl_peer), ETH_ALEN);
+		if (cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_LTF_KEYSEED_REQUIRED])
+			event.pasn_auth.peer[idx].ltf_keyseed_required = true;
+		idx++;
+	}
+	event.pasn_auth.num_peers = n_peers;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: PASN auth action: %u, num_bssids: %d",
+		   event.pasn_auth.action,
+		   event.pasn_auth.num_peers);
+	wpa_supplicant_event(drv->ctx, EVENT_PASN_AUTH, &event);
+}
+
+#endif /* CONFIG_PASN */
+
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
+static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv,
+				     u32 subcmd, u8 *data, size_t len)
+{
+	switch (subcmd) {
+	case QCA_NL80211_VENDOR_SUBCMD_TEST:
+		wpa_hexdump(MSG_DEBUG, "nl80211: QCA test event", data, len);
+		break;
+#ifdef CONFIG_DRIVER_NL80211_QCA
+	case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY:
+		qca_nl80211_avoid_freq(drv, data, len);
+		break;
+	case QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH:
+		qca_nl80211_key_mgmt_auth_handler(drv, data, len);
+		break;
+	case QCA_NL80211_VENDOR_SUBCMD_DO_ACS:
+		qca_nl80211_acs_select_ch(drv, data, len);
+		break;
+	case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED:
+	case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED:
+	case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED:
+	case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED:
+	case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED:
+		qca_nl80211_dfs_offload_radar_event(drv, subcmd, data, len);
+		break;
+	case QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN:
+		qca_nl80211_scan_trigger_event(drv, data, len);
+		break;
+	case QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE:
+		qca_nl80211_scan_done_event(drv, data, len);
+		break;
+	case QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP:
+		qca_nl80211_p2p_lo_stop_event(drv, data, len);
+		break;
+#ifdef CONFIG_PASN
+	case QCA_NL80211_VENDOR_SUBCMD_PASN:
+		qca_nl80211_pasn_auth(drv, data, len);
+		break;
+#endif /* CONFIG_PASN */
+	case QCA_NL80211_VENDOR_SUBCMD_TID_TO_LINK_MAP:
+		qca_nl80211_tid_to_link_map_event(drv, data, len);
+		break;
+	case QCA_NL80211_VENDOR_SUBCMD_LINK_RECONFIG:
+		qca_nl80211_link_reconfig_event(drv, data, len);
+		break;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+	default:
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Ignore unsupported QCA vendor event %u",
+			   subcmd);
+		break;
+	}
+}
+
+
+#ifdef CONFIG_DRIVER_NL80211_BRCM
+
+static void brcm_nl80211_acs_select_ch(struct wpa_driver_nl80211_data *drv,
+				       const u8 *data, size_t len)
+{
+	struct nlattr *tb[BRCM_VENDOR_ATTR_ACS_LAST + 1];
+	union wpa_event_data event;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: BRCM ACS channel selection vendor event received");
+
+	if (nla_parse(tb, BRCM_VENDOR_ATTR_ACS_LAST, (struct nlattr *) data,
+		      len, NULL) ||
+	    !tb[BRCM_VENDOR_ATTR_ACS_PRIMARY_FREQ] ||
+	    !tb[BRCM_VENDOR_ATTR_ACS_SECONDARY_FREQ])
+		return;
+
+	os_memset(&event, 0, sizeof(event));
+	if (tb[BRCM_VENDOR_ATTR_ACS_PRIMARY_FREQ])
+		event.acs_selected_channels.pri_freq =
+			nla_get_u32(tb[BRCM_VENDOR_ATTR_ACS_PRIMARY_FREQ]);
+	if (tb[BRCM_VENDOR_ATTR_ACS_SECONDARY_FREQ])
+		event.acs_selected_channels.sec_freq =
+			nla_get_u32(tb[BRCM_VENDOR_ATTR_ACS_SECONDARY_FREQ]);
+	if (tb[BRCM_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL])
+		event.acs_selected_channels.vht_seg0_center_ch =
+			nla_get_u8(tb[BRCM_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]);
+	if (tb[BRCM_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL])
+		event.acs_selected_channels.vht_seg1_center_ch =
+			nla_get_u8(tb[BRCM_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL]);
+	if (tb[BRCM_VENDOR_ATTR_ACS_CHWIDTH])
+		event.acs_selected_channels.ch_width =
+			nla_get_u16(tb[BRCM_VENDOR_ATTR_ACS_CHWIDTH]);
+	if (tb[BRCM_VENDOR_ATTR_ACS_HW_MODE]) {
+		event.acs_selected_channels.hw_mode = nla_get_u8(tb[BRCM_VENDOR_ATTR_ACS_HW_MODE]);
+		if (event.acs_selected_channels.hw_mode == NUM_HOSTAPD_MODES ||
+		    event.acs_selected_channels.hw_mode ==
+		    HOSTAPD_MODE_IEEE80211ANY) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Invalid hw_mode %d in ACS selection event",
+				   event.acs_selected_channels.hw_mode);
+			return;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: ACS Results: PCH: %d SCH: %d BW: %d VHT0: %d VHT1: %d HW_MODE: %d",
+		   event.acs_selected_channels.pri_freq,
+		   event.acs_selected_channels.sec_freq,
+		   event.acs_selected_channels.ch_width,
+		   event.acs_selected_channels.vht_seg0_center_ch,
+		   event.acs_selected_channels.vht_seg1_center_ch,
+		   event.acs_selected_channels.hw_mode);
+	wpa_supplicant_event(drv->ctx, EVENT_ACS_CHANNEL_SELECTED, &event);
+}
+
+
+static void nl80211_vendor_event_brcm(struct wpa_driver_nl80211_data *drv,
+				      u32 subcmd, u8 *data, size_t len)
+{
+	wpa_printf(MSG_DEBUG, "nl80211: Got BRCM vendor event %u", subcmd);
+	switch (subcmd) {
+	case BRCM_VENDOR_EVENT_PRIV_STR:
+	case BRCM_VENDOR_EVENT_HANGED:
+		/* Dump the event on to the console */
+		wpa_msg(NULL, MSG_INFO, "%s", data);
+		break;
+	case BRCM_VENDOR_EVENT_ACS:
+		brcm_nl80211_acs_select_ch(drv, data, len);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG,
+			   "%s: Ignore unsupported BRCM vendor event %u",
+			   __func__, subcmd);
+		break;
+	}
+}
+
+#endif /* CONFIG_DRIVER_NL80211_BRCM */
+
+
+static void nl80211_vendor_event(struct wpa_driver_nl80211_data *drv,
+				 struct nlattr **tb)
+{
+	u32 vendor_id, subcmd, wiphy = 0;
+	int wiphy_idx;
+	u8 *data = NULL;
+	size_t len = 0;
+
+	if (!tb[NL80211_ATTR_VENDOR_ID] ||
+	    !tb[NL80211_ATTR_VENDOR_SUBCMD])
+		return;
+
+	vendor_id = nla_get_u32(tb[NL80211_ATTR_VENDOR_ID]);
+	subcmd = nla_get_u32(tb[NL80211_ATTR_VENDOR_SUBCMD]);
+
+	if (tb[NL80211_ATTR_WIPHY])
+		wiphy = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
+
+	wpa_printf(MSG_DEBUG, "nl80211: Vendor event: wiphy=%u vendor_id=0x%x subcmd=%u",
+		   wiphy, vendor_id, subcmd);
+
+	if (tb[NL80211_ATTR_VENDOR_DATA]) {
+		data = nla_data(tb[NL80211_ATTR_VENDOR_DATA]);
+		len = nla_len(tb[NL80211_ATTR_VENDOR_DATA]);
+		wpa_hexdump(MSG_MSGDUMP, "nl80211: Vendor data", data, len);
+	}
+
+	wiphy_idx = nl80211_get_wiphy_index(drv->first_bss);
+	if (wiphy_idx >= 0 && wiphy_idx != (int) wiphy) {
+		wpa_printf(MSG_DEBUG, "nl80211: Ignore vendor event for foreign wiphy %u (own: %d)",
+			   wiphy, wiphy_idx);
+		return;
+	}
+
+#ifdef ANDROID
+#ifdef ANDROID_LIB_EVENT
+	/* Postpone QCA roam+auth event indication to the point when both that
+	 * and the NL80211_CMD_ROAM event have been received (see calls to
+	 * qca_nl80211_key_mgmt_auth() and drv->pending_roam_data). */
+	if (!(vendor_id == OUI_QCA &&
+	      subcmd == QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH))
+		wpa_driver_nl80211_driver_event(drv, vendor_id, subcmd, data,
+						len);
+#endif /* ANDROID_LIB_EVENT */
+#endif /* ANDROID */
+
+	switch (vendor_id) {
+	case OUI_QCA:
+		nl80211_vendor_event_qca(drv, subcmd, data, len);
+		break;
+#ifdef CONFIG_DRIVER_NL80211_BRCM
+	case OUI_BRCM:
+		nl80211_vendor_event_brcm(drv, subcmd, data, len);
+		break;
+#endif /* CONFIG_DRIVER_NL80211_BRCM */
+	default:
+		wpa_printf(MSG_DEBUG, "nl80211: Ignore unsupported vendor event");
+		break;
+	}
+}
+
+
+static void nl80211_reg_change_event(struct wpa_driver_nl80211_data *drv,
+				     struct nlattr *tb[])
+{
+	union wpa_event_data data;
+	enum nl80211_reg_initiator init;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Regulatory domain change");
+
+	if (tb[NL80211_ATTR_REG_INITIATOR] == NULL)
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	init = nla_get_u8(tb[NL80211_ATTR_REG_INITIATOR]);
+	wpa_printf(MSG_DEBUG, " * initiator=%d", init);
+	switch (init) {
+	case NL80211_REGDOM_SET_BY_CORE:
+		data.channel_list_changed.initiator = REGDOM_SET_BY_CORE;
+		break;
+	case NL80211_REGDOM_SET_BY_USER:
+		data.channel_list_changed.initiator = REGDOM_SET_BY_USER;
+		break;
+	case NL80211_REGDOM_SET_BY_DRIVER:
+		data.channel_list_changed.initiator = REGDOM_SET_BY_DRIVER;
+		break;
+	case NL80211_REGDOM_SET_BY_COUNTRY_IE:
+		data.channel_list_changed.initiator = REGDOM_SET_BY_COUNTRY_IE;
+		break;
+	}
+
+	if (tb[NL80211_ATTR_REG_TYPE]) {
+		enum nl80211_reg_type type;
+		type = nla_get_u8(tb[NL80211_ATTR_REG_TYPE]);
+		wpa_printf(MSG_DEBUG, " * type=%d", type);
+		switch (type) {
+		case NL80211_REGDOM_TYPE_COUNTRY:
+			data.channel_list_changed.type = REGDOM_TYPE_COUNTRY;
+			break;
+		case NL80211_REGDOM_TYPE_WORLD:
+			data.channel_list_changed.type = REGDOM_TYPE_WORLD;
+			break;
+		case NL80211_REGDOM_TYPE_CUSTOM_WORLD:
+			data.channel_list_changed.type =
+				REGDOM_TYPE_CUSTOM_WORLD;
+			break;
+		case NL80211_REGDOM_TYPE_INTERSECTION:
+			data.channel_list_changed.type =
+				REGDOM_TYPE_INTERSECTION;
+			break;
+		}
+	}
+
+	if (tb[NL80211_ATTR_REG_ALPHA2]) {
+		os_strlcpy(data.channel_list_changed.alpha2,
+			   nla_get_string(tb[NL80211_ATTR_REG_ALPHA2]),
+			   sizeof(data.channel_list_changed.alpha2));
+		wpa_printf(MSG_DEBUG, " * alpha2=%s",
+			   data.channel_list_changed.alpha2);
+	}
+
+	wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, &data);
+}
+
+
+static void nl80211_dump_freq(const char *title, struct nlattr *nl_freq)
+{
+	static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
+		[NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
+		[NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG },
+		[NL80211_FREQUENCY_ATTR_NO_IR] = { .type = NLA_FLAG },
+		[NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG },
+		[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
+	};
+	struct nlattr *tb[NL80211_FREQUENCY_ATTR_MAX + 1];
+	u32 freq = 0, max_tx_power = 0;
+
+	nla_parse(tb, NL80211_FREQUENCY_ATTR_MAX,
+		  nla_data(nl_freq), nla_len(nl_freq), freq_policy);
+
+	if (tb[NL80211_FREQUENCY_ATTR_FREQ])
+		freq = nla_get_u32(tb[NL80211_FREQUENCY_ATTR_FREQ]);
+	if (tb[NL80211_FREQUENCY_ATTR_MAX_TX_POWER])
+		max_tx_power =
+			nla_get_u32(tb[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]);
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Channel (%s): freq=%u max_tx_power=%u%s%s%s",
+		   title, freq, max_tx_power,
+		   tb[NL80211_FREQUENCY_ATTR_DISABLED] ? " disabled" : "",
+		   tb[NL80211_FREQUENCY_ATTR_NO_IR] ? " no-IR" : "",
+		   tb[NL80211_FREQUENCY_ATTR_RADAR] ? " radar" : "");
+}
+
+
+static void nl80211_reg_beacon_hint_event(struct wpa_driver_nl80211_data *drv,
+					   struct nlattr *tb[])
+{
+	union wpa_event_data data;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Regulatory beacon hint");
+	os_memset(&data, 0, sizeof(data));
+	data.channel_list_changed.initiator = REGDOM_BEACON_HINT;
+
+	if (tb[NL80211_ATTR_FREQ_BEFORE])
+		nl80211_dump_freq("before", tb[NL80211_ATTR_FREQ_BEFORE]);
+	if (tb[NL80211_ATTR_FREQ_AFTER])
+		nl80211_dump_freq("after", tb[NL80211_ATTR_FREQ_AFTER]);
+
+	wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, &data);
+}
+
+
+static void nl80211_external_auth(struct wpa_driver_nl80211_data *drv,
+				  struct nlattr **tb)
+{
+	union wpa_event_data event;
+	enum nl80211_external_auth_action act;
+	char mld_addr[50];
+
+	if (!tb[NL80211_ATTR_AKM_SUITES] ||
+	    !tb[NL80211_ATTR_EXTERNAL_AUTH_ACTION] ||
+	    !tb[NL80211_ATTR_BSSID] ||
+	    !tb[NL80211_ATTR_SSID])
+		return;
+
+	os_memset(&event, 0, sizeof(event));
+	act = nla_get_u32(tb[NL80211_ATTR_EXTERNAL_AUTH_ACTION]);
+	switch (act) {
+	case NL80211_EXTERNAL_AUTH_START:
+		event.external_auth.action = EXT_AUTH_START;
+		break;
+	case NL80211_EXTERNAL_AUTH_ABORT:
+		event.external_auth.action = EXT_AUTH_ABORT;
+		break;
+	default:
+		return;
+	}
+
+	event.external_auth.key_mgmt_suite =
+		nla_get_u32(tb[NL80211_ATTR_AKM_SUITES]);
+
+	event.external_auth.ssid_len = nla_len(tb[NL80211_ATTR_SSID]);
+	if (event.external_auth.ssid_len > SSID_MAX_LEN)
+		return;
+	event.external_auth.ssid = nla_data(tb[NL80211_ATTR_SSID]);
+
+	event.external_auth.bssid = nla_data(tb[NL80211_ATTR_BSSID]);
+
+	mld_addr[0] = '\0';
+	if (tb[NL80211_ATTR_MLD_ADDR]) {
+		event.external_auth.mld_addr =
+			nla_data(tb[NL80211_ATTR_MLD_ADDR]);
+		os_snprintf(mld_addr, sizeof(mld_addr), ", MLD ADDR: " MACSTR,
+			    MAC2STR(event.external_auth.mld_addr));
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: External auth action: %u, AKM: 0x%x, SSID: %s, BSSID: " MACSTR "%s",
+		   event.external_auth.action,
+		   event.external_auth.key_mgmt_suite,
+		   wpa_ssid_txt(event.external_auth.ssid,
+				event.external_auth.ssid_len),
+		   MAC2STR(event.external_auth.bssid), mld_addr);
+	wpa_supplicant_event(drv->ctx, EVENT_EXTERNAL_AUTH, &event);
+}
+
+
+static void nl80211_port_authorized(struct wpa_driver_nl80211_data *drv,
+				    struct nlattr **tb)
+{
+	const u8 *addr;
+	union wpa_event_data event;
+
+	os_memset(&event, 0, sizeof(event));
+
+	if (!tb[NL80211_ATTR_MAC] ||
+	    nla_len(tb[NL80211_ATTR_MAC]) != ETH_ALEN) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Ignore port authorized event without BSSID");
+		return;
+	}
+
+	addr = nla_data(tb[NL80211_ATTR_MAC]);
+	if (os_memcmp(addr, drv->bssid, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Ignore port authorized event for " MACSTR
+			   " (not the currently connected BSSID " MACSTR ")",
+			   MAC2STR(addr), MAC2STR(drv->bssid));
+		return;
+	}
+
+	if (tb[NL80211_ATTR_TD_BITMAP]) {
+		event.port_authorized.td_bitmap_len =
+			nla_len(tb[NL80211_ATTR_TD_BITMAP]);
+		if (event.port_authorized.td_bitmap_len > 0)
+			event.port_authorized.td_bitmap =
+				nla_data(tb[NL80211_ATTR_TD_BITMAP]);
+	}
+
+	wpa_supplicant_event(drv->ctx, EVENT_PORT_AUTHORIZED, &event);
+}
+
+
+static void nl80211_sta_opmode_change_event(struct wpa_driver_nl80211_data *drv,
+					    struct nlattr **tb)
+{
+	union wpa_event_data ed;
+	u8 smps_mode, max_bw;
+
+	if (!tb[NL80211_ATTR_MAC] ||
+	    (!tb[NL80211_ATTR_CHANNEL_WIDTH] &&
+	     !tb[NL80211_ATTR_SMPS_MODE] &&
+	     !tb[NL80211_ATTR_NSS]))
+		return;
+
+	ed.sta_opmode.smps_mode = SMPS_INVALID;
+	ed.sta_opmode.chan_width = CHAN_WIDTH_UNKNOWN;
+	ed.sta_opmode.rx_nss = 0xff;
+	ed.sta_opmode.addr = nla_data(tb[NL80211_ATTR_MAC]);
+
+	if (tb[NL80211_ATTR_SMPS_MODE]) {
+		smps_mode = nla_get_u8(tb[NL80211_ATTR_SMPS_MODE]);
+		switch (smps_mode) {
+		case NL80211_SMPS_OFF:
+			ed.sta_opmode.smps_mode = SMPS_OFF;
+			break;
+		case NL80211_SMPS_STATIC:
+			ed.sta_opmode.smps_mode = SMPS_STATIC;
+			break;
+		case NL80211_SMPS_DYNAMIC:
+			ed.sta_opmode.smps_mode = SMPS_DYNAMIC;
+			break;
+		default:
+			ed.sta_opmode.smps_mode = SMPS_INVALID;
+			break;
+		}
+	}
+
+	if (tb[NL80211_ATTR_CHANNEL_WIDTH]) {
+		max_bw = nla_get_u32(tb[NL80211_ATTR_CHANNEL_WIDTH]);
+		switch (max_bw) {
+		case NL80211_CHAN_WIDTH_20_NOHT:
+			ed.sta_opmode.chan_width = CHAN_WIDTH_20_NOHT;
+			break;
+		case NL80211_CHAN_WIDTH_20:
+			ed.sta_opmode.chan_width = CHAN_WIDTH_20;
+			break;
+		case NL80211_CHAN_WIDTH_40:
+			ed.sta_opmode.chan_width = CHAN_WIDTH_40;
+			break;
+		case NL80211_CHAN_WIDTH_80:
+			ed.sta_opmode.chan_width = CHAN_WIDTH_80;
+			break;
+		case NL80211_CHAN_WIDTH_80P80:
+			ed.sta_opmode.chan_width = CHAN_WIDTH_80P80;
+			break;
+		case NL80211_CHAN_WIDTH_160:
+			ed.sta_opmode.chan_width = CHAN_WIDTH_160;
+			break;
+		case NL80211_CHAN_WIDTH_320:
+			ed.sta_opmode.chan_width = CHAN_WIDTH_320;
+			break;
+		default:
+			ed.sta_opmode.chan_width = CHAN_WIDTH_UNKNOWN;
+			break;
+
+		}
+	}
+
+	if (tb[NL80211_ATTR_NSS])
+		ed.sta_opmode.rx_nss = nla_get_u8(tb[NL80211_ATTR_NSS]);
+
+	wpa_supplicant_event(drv->ctx, EVENT_STATION_OPMODE_CHANGED, &ed);
+}
+
+
+static void nl80211_control_port_frame(struct wpa_driver_nl80211_data *drv,
+				       struct nlattr **tb)
+{
+	u8 *src_addr;
+	u16 ethertype;
+	enum frame_encryption encrypted;
+	int link_id;
+
+	if (!tb[NL80211_ATTR_MAC] ||
+	    !tb[NL80211_ATTR_FRAME] ||
+	    !tb[NL80211_ATTR_CONTROL_PORT_ETHERTYPE])
+		return;
+
+	src_addr = nla_data(tb[NL80211_ATTR_MAC]);
+	ethertype = nla_get_u16(tb[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]);
+	encrypted = nla_get_flag(tb[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT]) ?
+		FRAME_NOT_ENCRYPTED : FRAME_ENCRYPTED;
+
+	if (tb[NL80211_ATTR_MLO_LINK_ID])
+		link_id = nla_get_u8(tb[NL80211_ATTR_MLO_LINK_ID]);
+	else
+		link_id = -1;
+
+	switch (ethertype) {
+	case ETH_P_RSN_PREAUTH:
+		wpa_printf(MSG_INFO, "nl80211: Got pre-auth frame from "
+			   MACSTR " over control port unexpectedly",
+			   MAC2STR(src_addr));
+		break;
+	case ETH_P_PAE:
+		drv_event_eapol_rx2(drv->ctx, src_addr,
+				    nla_data(tb[NL80211_ATTR_FRAME]),
+				    nla_len(tb[NL80211_ATTR_FRAME]),
+				    encrypted, link_id);
+		break;
+	default:
+		wpa_printf(MSG_INFO,
+			   "nl80211: Unexpected ethertype 0x%04x from "
+			   MACSTR " over control port",
+			   ethertype, MAC2STR(src_addr));
+		break;
+	}
+}
+
+
+static void
+nl80211_control_port_frame_tx_status(struct wpa_driver_nl80211_data *drv,
+				     const u8 *frame, size_t len,
+				     struct nlattr *ack, struct nlattr *cookie)
+{
+	union wpa_event_data event;
+
+	if (!cookie || len < ETH_HLEN)
+		return;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Control port TX status (ack=%d), cookie=%llu",
+		   ack != NULL, (long long unsigned int) nla_get_u64(cookie));
+
+	os_memset(&event, 0, sizeof(event));
+	event.eapol_tx_status.dst = frame;
+	event.eapol_tx_status.data = frame + ETH_HLEN;
+	event.eapol_tx_status.data_len = len - ETH_HLEN;
+	event.eapol_tx_status.ack = ack != NULL;
+	event.eapol_tx_status.link_id =
+		nla_get_u64(cookie) == drv->eapol_tx_cookie ?
+		drv->eapol_tx_link_id : NL80211_DRV_LINK_ID_NA;
+
+	wpa_supplicant_event(drv->ctx, EVENT_EAPOL_TX_STATUS, &event);
+}
+
+
+static void nl80211_frame_wait_cancel(struct wpa_driver_nl80211_data *drv,
+				      struct nlattr *cookie_attr)
+{
+	unsigned int i;
+	u64 cookie;
+	bool match = false;
+
+	if (!cookie_attr)
+		return;
+	cookie = nla_get_u64(cookie_attr);
+
+	for (i = 0; i < drv->num_send_frame_cookies; i++) {
+		if (cookie == drv->send_frame_cookies[i]) {
+			match = true;
+			break;
+		}
+	}
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: TX frame wait expired for cookie 0x%llx%s%s",
+		   (long long unsigned int) cookie,
+		   match ? " (match)" : "",
+		   drv->send_frame_cookie == cookie ? " (match-saved)" : "");
+	if (drv->send_frame_cookie == cookie)
+		drv->send_frame_cookie = (u64) -1;
+	if (!match)
+		return;
+
+	if (i < drv->num_send_frame_cookies - 1)
+		os_memmove(&drv->send_frame_cookies[i],
+			   &drv->send_frame_cookies[i + 1],
+			   (drv->num_send_frame_cookies - i - 1) * sizeof(u64));
+	drv->num_send_frame_cookies--;
+
+	wpa_supplicant_event(drv->ctx, EVENT_TX_WAIT_EXPIRE, NULL);
+}
+
+
+static void nl80211_assoc_comeback(struct wpa_driver_nl80211_data *drv,
+				   struct nlattr *mac, struct nlattr *timeout)
+{
+	if (!mac || !timeout)
+		return;
+	wpa_printf(MSG_DEBUG, "nl80211: Association comeback requested by "
+		   MACSTR " (timeout: %u ms)",
+		   MAC2STR((u8 *) nla_data(mac)), nla_get_u32(timeout));
+}
+
+
+#ifdef CONFIG_IEEE80211AX
+
+static void nl80211_obss_color_collision(struct wpa_driver_nl80211_data *drv,
+					 struct nlattr *tb[])
+{
+	union wpa_event_data data;
+
+	if (!tb[NL80211_ATTR_OBSS_COLOR_BITMAP])
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	data.bss_color_collision.bitmap =
+		nla_get_u64(tb[NL80211_ATTR_OBSS_COLOR_BITMAP]);
+
+	wpa_printf(MSG_DEBUG, "nl80211: BSS color collision - bitmap %08llx",
+		   (long long unsigned int) data.bss_color_collision.bitmap);
+	wpa_supplicant_event(drv->ctx, EVENT_BSS_COLOR_COLLISION, &data);
+}
+
+
+static void
+nl80211_color_change_announcement_started(struct wpa_driver_nl80211_data *drv)
+{
+	union wpa_event_data data = {};
+
+	wpa_printf(MSG_DEBUG, "nl80211: CCA started");
+	wpa_supplicant_event(drv->ctx, EVENT_CCA_STARTED_NOTIFY, &data);
+}
+
+
+static void
+nl80211_color_change_announcement_aborted(struct wpa_driver_nl80211_data *drv)
+{
+	union wpa_event_data data = {};
+
+	wpa_printf(MSG_DEBUG, "nl80211: CCA aborted");
+	wpa_supplicant_event(drv->ctx, EVENT_CCA_ABORTED_NOTIFY, &data);
+}
+
+
+static void
+nl80211_color_change_announcement_completed(struct wpa_driver_nl80211_data *drv)
+{
+	union wpa_event_data data = {};
+
+	wpa_printf(MSG_DEBUG, "nl80211: CCA completed");
+	wpa_supplicant_event(drv->ctx, EVENT_CCA_NOTIFY, &data);
+}
+
+#endif /* CONFIG_IEEE80211AX */
+
+
+static void do_process_drv_event(struct i802_bss *bss, int cmd,
+				 struct nlattr **tb)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int external_scan_event = 0;
+	struct nlattr *frame = tb[NL80211_ATTR_FRAME];
+
+	wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s",
+		   cmd, nl80211_command_to_string(cmd), bss->ifname);
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+	if (cmd == NL80211_CMD_ROAM &&
+	    (drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) {
+		if (drv->pending_roam_data) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Process pending roam+auth vendor event");
+			qca_nl80211_key_mgmt_auth(drv, drv->pending_roam_data,
+						  drv->pending_roam_data_len);
+			os_free(drv->pending_roam_data);
+			drv->pending_roam_data = NULL;
+			return;
+		}
+		/*
+		 * Device will use roam+auth vendor event to indicate
+		 * roaming, so ignore the regular roam event.
+		 */
+		drv->roam_indication_done = true;
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Ignore roam event (cmd=%d), device will use vendor event roam+auth",
+			   cmd);
+		return;
+	}
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+	if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED &&
+	    (cmd == NL80211_CMD_NEW_SCAN_RESULTS ||
+	     cmd == NL80211_CMD_SCAN_ABORTED))
+		nl80211_restore_ap_mode(bss);
+
+	switch (cmd) {
+	case NL80211_CMD_TRIGGER_SCAN:
+		wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan trigger");
+		drv->scan_state = SCAN_STARTED;
+		if (drv->scan_for_auth) {
+			/*
+			 * Cannot indicate EVENT_SCAN_STARTED here since we skip
+			 * EVENT_SCAN_RESULTS in scan_for_auth case and the
+			 * upper layer implementation could get confused about
+			 * scanning state.
+			 */
+			wpa_printf(MSG_DEBUG, "nl80211: Do not indicate scan-start event due to internal scan_for_auth");
+			break;
+		}
+		wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, NULL);
+		break;
+	case NL80211_CMD_START_SCHED_SCAN:
+		wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan started");
+		drv->scan_state = SCHED_SCAN_STARTED;
+		break;
+	case NL80211_CMD_SCHED_SCAN_STOPPED:
+		wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan stopped");
+		drv->scan_state = SCHED_SCAN_STOPPED;
+		wpa_supplicant_event(drv->ctx, EVENT_SCHED_SCAN_STOPPED, NULL);
+		break;
+	case NL80211_CMD_NEW_SCAN_RESULTS:
+		wpa_dbg(drv->ctx, MSG_DEBUG,
+			"nl80211: New scan results available");
+		if (drv->last_scan_cmd != NL80211_CMD_VENDOR)
+			drv->scan_state = SCAN_COMPLETED;
+		drv->scan_complete_events = 1;
+		if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) {
+			eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout,
+					     drv, drv->ctx);
+			drv->last_scan_cmd = 0;
+		} else {
+			external_scan_event = 1;
+		}
+		send_scan_event(drv, 0, tb, external_scan_event);
+		break;
+	case NL80211_CMD_SCHED_SCAN_RESULTS:
+		wpa_dbg(drv->ctx, MSG_DEBUG,
+			"nl80211: New sched scan results available");
+		drv->scan_state = SCHED_SCAN_RESULTS;
+		send_scan_event(drv, 0, tb, 0);
+		break;
+	case NL80211_CMD_SCAN_ABORTED:
+		wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan aborted");
+		if (drv->last_scan_cmd != NL80211_CMD_VENDOR)
+			drv->scan_state = SCAN_ABORTED;
+		if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) {
+			/*
+			 * Need to indicate that scan results are available in
+			 * order not to make wpa_supplicant stop its scanning.
+			 */
+			eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout,
+					     drv, drv->ctx);
+			drv->last_scan_cmd = 0;
+		} else {
+			external_scan_event = 1;
+		}
+		send_scan_event(drv, 1, tb, external_scan_event);
+		break;
+	case NL80211_CMD_AUTHENTICATE:
+	case NL80211_CMD_ASSOCIATE:
+	case NL80211_CMD_DEAUTHENTICATE:
+	case NL80211_CMD_DISASSOCIATE:
+	case NL80211_CMD_FRAME_TX_STATUS:
+	case NL80211_CMD_UNPROT_DEAUTHENTICATE:
+	case NL80211_CMD_UNPROT_DISASSOCIATE:
+		mlme_event(bss, cmd, tb[NL80211_ATTR_FRAME],
+			   tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT],
+			   tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
+			   tb[NL80211_ATTR_COOKIE],
+			   tb[NL80211_ATTR_RX_SIGNAL_DBM],
+			   tb[NL80211_ATTR_STA_WME],
+			   tb[NL80211_ATTR_REQ_IE],
+			   tb[NL80211_ATTR_MLO_LINK_ID]);
+		break;
+	case NL80211_CMD_CONNECT:
+	case NL80211_CMD_ROAM:
+		mlme_event_connect(drv, cmd, false,
+				   tb[NL80211_ATTR_STATUS_CODE],
+				   tb[NL80211_ATTR_MAC],
+				   tb[NL80211_ATTR_REQ_IE],
+				   tb[NL80211_ATTR_RESP_IE],
+				   tb[NL80211_ATTR_TIMED_OUT],
+				   tb[NL80211_ATTR_TIMEOUT_REASON],
+				   NULL, NULL, NULL,
+				   tb[NL80211_ATTR_FILS_KEK],
+				   NULL,
+				   tb[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM],
+				   tb[NL80211_ATTR_PMK],
+				   tb[NL80211_ATTR_PMKID],
+				   tb[NL80211_ATTR_MLO_LINKS]);
+		break;
+	case NL80211_CMD_CH_SWITCH_STARTED_NOTIFY:
+		mlme_event_ch_switch(drv,
+				     tb[NL80211_ATTR_IFINDEX],
+				     tb[NL80211_ATTR_MLO_LINK_ID],
+				     tb[NL80211_ATTR_WIPHY_FREQ],
+				     tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE],
+				     tb[NL80211_ATTR_CHANNEL_WIDTH],
+				     tb[NL80211_ATTR_CENTER_FREQ1],
+				     tb[NL80211_ATTR_CENTER_FREQ2],
+				     tb[NL80211_ATTR_PUNCT_BITMAP],
+				     tb[NL80211_ATTR_CH_SWITCH_COUNT],
+				     0);
+		break;
+	case NL80211_CMD_CH_SWITCH_NOTIFY:
+		mlme_event_ch_switch(drv,
+				     tb[NL80211_ATTR_IFINDEX],
+				     tb[NL80211_ATTR_MLO_LINK_ID],
+				     tb[NL80211_ATTR_WIPHY_FREQ],
+				     tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE],
+				     tb[NL80211_ATTR_CHANNEL_WIDTH],
+				     tb[NL80211_ATTR_CENTER_FREQ1],
+				     tb[NL80211_ATTR_CENTER_FREQ2],
+				     tb[NL80211_ATTR_PUNCT_BITMAP],
+				     NULL,
+				     1);
+		break;
+	case NL80211_CMD_DISCONNECT:
+		mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE],
+				      tb[NL80211_ATTR_MAC],
+				      tb[NL80211_ATTR_DISCONNECTED_BY_AP]);
+		break;
+	case NL80211_CMD_MICHAEL_MIC_FAILURE:
+		mlme_event_michael_mic_failure(bss, tb);
+		break;
+	case NL80211_CMD_JOIN_IBSS:
+		mlme_event_join_ibss(drv, tb);
+		break;
+	case NL80211_CMD_REMAIN_ON_CHANNEL:
+		mlme_event_remain_on_channel(drv, 0, tb);
+		break;
+	case NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL:
+		mlme_event_remain_on_channel(drv, 1, tb);
+		break;
+	case NL80211_CMD_NOTIFY_CQM:
+		nl80211_cqm_event(drv, tb);
+		break;
+	case NL80211_CMD_REG_CHANGE:
+	case NL80211_CMD_WIPHY_REG_CHANGE:
+		nl80211_reg_change_event(drv, tb);
+		break;
+	case NL80211_CMD_REG_BEACON_HINT:
+		nl80211_reg_beacon_hint_event(drv, tb);
+		break;
+	case NL80211_CMD_NEW_STATION:
+		nl80211_new_station_event(drv, bss, tb);
+		break;
+	case NL80211_CMD_DEL_STATION:
+		nl80211_del_station_event(drv, bss, tb);
+		break;
+	case NL80211_CMD_SET_REKEY_OFFLOAD:
+		nl80211_rekey_offload_event(drv, tb);
+		break;
+	case NL80211_CMD_PMKSA_CANDIDATE:
+		nl80211_pmksa_candidate_event(drv, tb);
+		break;
+	case NL80211_CMD_PROBE_CLIENT:
+		nl80211_client_probe_event(drv, tb);
+		break;
+	case NL80211_CMD_TDLS_OPER:
+		nl80211_tdls_oper_event(drv, tb);
+		break;
+	case NL80211_CMD_CONN_FAILED:
+		nl80211_connect_failed_event(drv, tb);
+		break;
+	case NL80211_CMD_FT_EVENT:
+		mlme_event_ft_event(drv, tb);
+		break;
+	case NL80211_CMD_RADAR_DETECT:
+		nl80211_radar_event(drv, tb);
+		break;
+	case NL80211_CMD_STOP_AP:
+		nl80211_stop_ap(drv, tb);
+		break;
+	case NL80211_CMD_VENDOR:
+		nl80211_vendor_event(drv, tb);
+		break;
+	case NL80211_CMD_NEW_PEER_CANDIDATE:
+		nl80211_new_peer_candidate(drv, tb);
+		break;
+	case NL80211_CMD_PORT_AUTHORIZED:
+		nl80211_port_authorized(drv, tb);
+		break;
+	case NL80211_CMD_STA_OPMODE_CHANGED:
+		nl80211_sta_opmode_change_event(drv, tb);
+		break;
+	case NL80211_CMD_UPDATE_OWE_INFO:
+		mlme_event_dh_event(drv, bss, tb);
+		break;
+	case NL80211_CMD_UNPROT_BEACON:
+		if (frame)
+			mlme_event_unprot_beacon(drv, nla_data(frame),
+						 nla_len(frame));
+		break;
+	case NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS:
+		if (!frame)
+			break;
+		nl80211_control_port_frame_tx_status(drv,
+						     nla_data(frame),
+						     nla_len(frame),
+						     tb[NL80211_ATTR_ACK],
+						     tb[NL80211_ATTR_COOKIE]);
+		break;
+	case NL80211_CMD_FRAME_WAIT_CANCEL:
+		nl80211_frame_wait_cancel(drv, tb[NL80211_ATTR_COOKIE]);
+		break;
+	case NL80211_CMD_ASSOC_COMEBACK:
+		nl80211_assoc_comeback(drv, tb[NL80211_ATTR_MAC],
+				       tb[NL80211_ATTR_TIMEOUT]);
+		break;
+#ifdef CONFIG_IEEE80211AX
+	case NL80211_CMD_OBSS_COLOR_COLLISION:
+		nl80211_obss_color_collision(drv, tb);
+		break;
+	case NL80211_CMD_COLOR_CHANGE_STARTED:
+		nl80211_color_change_announcement_started(drv);
+		break;
+	case NL80211_CMD_COLOR_CHANGE_ABORTED:
+		nl80211_color_change_announcement_aborted(drv);
+		break;
+	case NL80211_CMD_COLOR_CHANGE_COMPLETED:
+		nl80211_color_change_announcement_completed(drv);
+		break;
+#endif /* CONFIG_IEEE80211AX */
+	default:
+		wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event "
+			"(cmd=%d)", cmd);
+		break;
+	}
+}
+
+
+int process_global_event(struct nl_msg *msg, void *arg)
+{
+	struct nl80211_global *global = arg;
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct wpa_driver_nl80211_data *drv, *tmp;
+	int ifidx = -1, wiphy_idx = -1, wiphy_idx_rx = -1;
+	struct i802_bss *bss;
+	u64 wdev_id = 0;
+	int wdev_id_set = 0;
+	int wiphy_idx_set = 0;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (tb[NL80211_ATTR_IFINDEX])
+		ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
+	else if (tb[NL80211_ATTR_WDEV]) {
+		wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]);
+		wdev_id_set = 1;
+	} else if (tb[NL80211_ATTR_WIPHY]) {
+		wiphy_idx_rx = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
+		wiphy_idx_set = 1;
+	}
+
+	dl_list_for_each_safe(drv, tmp, &global->interfaces,
+			      struct wpa_driver_nl80211_data, list) {
+		for (bss = drv->first_bss; bss; bss = bss->next) {
+			if (wiphy_idx_set)
+				wiphy_idx = nl80211_get_wiphy_index(bss);
+			if ((ifidx == -1 && !wiphy_idx_set && !wdev_id_set) ||
+			    ifidx == bss->ifindex ||
+			    (wiphy_idx_set && wiphy_idx == wiphy_idx_rx) ||
+			    (wdev_id_set && bss->wdev_id_set &&
+			     wdev_id == bss->wdev_id)) {
+				do_process_drv_event(bss, gnlh->cmd, tb);
+				return NL_SKIP;
+			}
+		}
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Ignored event %d (%s) for foreign interface (ifindex %d wdev 0x%llx)",
+			   gnlh->cmd, nl80211_command_to_string(gnlh->cmd),
+			   ifidx, (long long unsigned int) wdev_id);
+	}
+
+	return NL_SKIP;
+}
+
+
+int process_bss_event(struct nl_msg *msg, void *arg)
+{
+	struct i802_bss *bss = arg;
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	wpa_printf(MSG_DEBUG, "nl80211: BSS Event %d (%s) received for %s",
+		   gnlh->cmd, nl80211_command_to_string(gnlh->cmd),
+		   bss->ifname);
+
+	switch (gnlh->cmd) {
+	case NL80211_CMD_FRAME:
+	case NL80211_CMD_FRAME_TX_STATUS:
+		mlme_event(bss, gnlh->cmd, tb[NL80211_ATTR_FRAME],
+			   tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT],
+			   tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
+			   tb[NL80211_ATTR_COOKIE],
+			   tb[NL80211_ATTR_RX_SIGNAL_DBM],
+			   tb[NL80211_ATTR_STA_WME], NULL,
+			   tb[NL80211_ATTR_MLO_LINK_ID]);
+		break;
+	case NL80211_CMD_UNEXPECTED_FRAME:
+		nl80211_spurious_frame(bss, tb, 0);
+		break;
+	case NL80211_CMD_UNEXPECTED_4ADDR_FRAME:
+		nl80211_spurious_frame(bss, tb, 1);
+		break;
+	case NL80211_CMD_EXTERNAL_AUTH:
+		nl80211_external_auth(bss->drv, tb);
+		break;
+	case NL80211_CMD_CONTROL_PORT_FRAME:
+		nl80211_control_port_frame(bss->drv, tb);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
+			   "(cmd=%d)", gnlh->cmd);
+		break;
+	}
+
+	return NL_SKIP;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211_monitor.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211_monitor.c
new file mode 100644
index 0000000..7ff55f1
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211_monitor.c
@@ -0,0 +1,503 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211 - AP monitor interface
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2004, Instant802 Networks, Inc.
+ * Copyright (c) 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <netpacket/packet.h>
+#include <linux/filter.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "linux_ioctl.h"
+#include "radiotap_iter.h"
+#include "driver_nl80211.h"
+
+
+static void handle_tx_callback(void *ctx, u8 *buf, size_t len, int ok)
+{
+	struct ieee80211_hdr *hdr;
+	u16 fc;
+	union wpa_event_data event;
+
+	hdr = (struct ieee80211_hdr *) buf;
+	fc = le_to_host16(hdr->frame_control);
+
+	os_memset(&event, 0, sizeof(event));
+	event.tx_status.type = WLAN_FC_GET_TYPE(fc);
+	event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
+	event.tx_status.dst = hdr->addr1;
+	event.tx_status.data = buf;
+	event.tx_status.data_len = len;
+	event.tx_status.ack = ok;
+	wpa_supplicant_event(ctx, EVENT_TX_STATUS, &event);
+}
+
+
+static void from_unknown_sta(struct wpa_driver_nl80211_data *drv,
+			     u8 *buf, size_t len)
+{
+	struct ieee80211_hdr *hdr = (void *)buf;
+	u16 fc;
+	union wpa_event_data event;
+
+	if (len < sizeof(*hdr))
+		return;
+
+	fc = le_to_host16(hdr->frame_control);
+
+	os_memset(&event, 0, sizeof(event));
+	event.rx_from_unknown.bssid = get_hdr_bssid(hdr, len);
+	event.rx_from_unknown.addr = hdr->addr2;
+	event.rx_from_unknown.wds = (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) ==
+		(WLAN_FC_FROMDS | WLAN_FC_TODS);
+	wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event);
+}
+
+
+static void handle_frame(struct wpa_driver_nl80211_data *drv,
+			 u8 *buf, size_t len, int datarate, int ssi_signal)
+{
+	struct ieee80211_hdr *hdr;
+	u16 fc;
+	union wpa_event_data event;
+
+	if (!drv->use_monitor)
+		return;
+
+	hdr = (struct ieee80211_hdr *) buf;
+	fc = le_to_host16(hdr->frame_control);
+
+	switch (WLAN_FC_GET_TYPE(fc)) {
+	case WLAN_FC_TYPE_MGMT:
+		os_memset(&event, 0, sizeof(event));
+		event.rx_mgmt.frame = buf;
+		event.rx_mgmt.frame_len = len;
+		event.rx_mgmt.datarate = datarate;
+		event.rx_mgmt.ssi_signal = ssi_signal;
+		wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
+		break;
+	case WLAN_FC_TYPE_CTRL:
+		/* can only get here with PS-Poll frames */
+		wpa_printf(MSG_DEBUG, "CTRL");
+		from_unknown_sta(drv, buf, len);
+		break;
+	case WLAN_FC_TYPE_DATA:
+		from_unknown_sta(drv, buf, len);
+		break;
+	}
+}
+
+
+static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct wpa_driver_nl80211_data *drv = eloop_ctx;
+	int len;
+	unsigned char buf[3000];
+	struct ieee80211_radiotap_iterator iter;
+	int ret;
+	int datarate = 0, ssi_signal = 0;
+	int injected = 0, failed = 0, rxflags = 0;
+
+	len = recv(sock, buf, sizeof(buf), 0);
+	if (len < 0) {
+		wpa_printf(MSG_ERROR, "nl80211: Monitor socket recv failed: %s",
+			   strerror(errno));
+		return;
+	}
+
+	if (ieee80211_radiotap_iterator_init(&iter, (void *) buf, len, NULL)) {
+		wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame");
+		return;
+	}
+
+	while (1) {
+		ret = ieee80211_radiotap_iterator_next(&iter);
+		if (ret == -ENOENT)
+			break;
+		if (ret) {
+			wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame (%d)",
+				   ret);
+			return;
+		}
+		switch (iter.this_arg_index) {
+		case IEEE80211_RADIOTAP_FLAGS:
+			if (*iter.this_arg & IEEE80211_RADIOTAP_F_FCS)
+				len -= 4;
+			break;
+		case IEEE80211_RADIOTAP_RX_FLAGS:
+			rxflags = 1;
+			break;
+		case IEEE80211_RADIOTAP_TX_FLAGS:
+			injected = 1;
+			failed = le_to_host16((*(le16 *) iter.this_arg)) &
+					IEEE80211_RADIOTAP_F_TX_FAIL;
+			break;
+		case IEEE80211_RADIOTAP_DATA_RETRIES:
+			break;
+		case IEEE80211_RADIOTAP_CHANNEL:
+			/* TODO: convert from freq/flags to channel number */
+			break;
+		case IEEE80211_RADIOTAP_RATE:
+			datarate = *iter.this_arg * 5;
+			break;
+		case IEEE80211_RADIOTAP_DBM_ANTSIGNAL:
+			ssi_signal = (s8) *iter.this_arg;
+			break;
+		}
+	}
+
+	if (rxflags && injected)
+		return;
+
+	if (!injected)
+		handle_frame(drv, buf + iter._max_length,
+			     len - iter._max_length, datarate, ssi_signal);
+	else
+		handle_tx_callback(drv->ctx, buf + iter._max_length,
+				   len - iter._max_length, !failed);
+}
+
+
+/*
+ * we post-process the filter code later and rewrite
+ * this to the offset to the last instruction
+ */
+#define PASS	0xFF
+#define FAIL	0xFE
+
+static struct sock_filter msock_filter_insns[] = {
+	/*
+	 * do a little-endian load of the radiotap length field
+	 */
+	/* load lower byte into A */
+	BPF_STMT(BPF_LD  | BPF_B | BPF_ABS, 2),
+	/* put it into X (== index register) */
+	BPF_STMT(BPF_MISC| BPF_TAX, 0),
+	/* load upper byte into A */
+	BPF_STMT(BPF_LD  | BPF_B | BPF_ABS, 3),
+	/* left-shift it by 8 */
+	BPF_STMT(BPF_ALU | BPF_LSH | BPF_K, 8),
+	/* or with X */
+	BPF_STMT(BPF_ALU | BPF_OR | BPF_X, 0),
+	/* put result into X */
+	BPF_STMT(BPF_MISC| BPF_TAX, 0),
+
+	/*
+	 * Allow management frames through, this also gives us those
+	 * management frames that we sent ourselves with status
+	 */
+	/* load the lower byte of the IEEE 802.11 frame control field */
+	BPF_STMT(BPF_LD  | BPF_B | BPF_IND, 0),
+	/* mask off frame type and version */
+	BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xF),
+	/* accept frame if it's both 0, fall through otherwise */
+	BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, PASS, 0),
+
+	/*
+	 * TODO: add a bit to radiotap RX flags that indicates
+	 * that the sending station is not associated, then
+	 * add a filter here that filters on our DA and that flag
+	 * to allow us to deauth frames to that bad station.
+	 *
+	 * For now allow all To DS data frames through.
+	 */
+	/* load the IEEE 802.11 frame control field */
+	BPF_STMT(BPF_LD  | BPF_H | BPF_IND, 0),
+	/* mask off frame type, version and DS status */
+	BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0F03),
+	/* accept frame if version 0, type 2 and To DS, fall through otherwise
+	 */
+	BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0801, PASS, 0),
+
+#if 0
+	/*
+	 * drop non-data frames
+	 */
+	/* load the lower byte of the frame control field */
+	BPF_STMT(BPF_LD   | BPF_B | BPF_IND, 0),
+	/* mask off QoS bit */
+	BPF_STMT(BPF_ALU  | BPF_AND | BPF_K, 0x0c),
+	/* drop non-data frames */
+	BPF_JUMP(BPF_JMP  | BPF_JEQ | BPF_K, 8, 0, FAIL),
+#endif
+	/* load the upper byte of the frame control field */
+	BPF_STMT(BPF_LD   | BPF_B | BPF_IND, 1),
+	/* mask off toDS/fromDS */
+	BPF_STMT(BPF_ALU  | BPF_AND | BPF_K, 0x03),
+	/* accept WDS frames */
+	BPF_JUMP(BPF_JMP  | BPF_JEQ | BPF_K, 3, PASS, 0),
+
+	/*
+	 * add header length to index
+	 */
+	/* load the lower byte of the frame control field */
+	BPF_STMT(BPF_LD   | BPF_B | BPF_IND, 0),
+	/* mask off QoS bit */
+	BPF_STMT(BPF_ALU  | BPF_AND | BPF_K, 0x80),
+	/* right shift it by 6 to give 0 or 2 */
+	BPF_STMT(BPF_ALU  | BPF_RSH | BPF_K, 6),
+	/* add data frame header length */
+	BPF_STMT(BPF_ALU  | BPF_ADD | BPF_K, 24),
+	/* add index, was start of 802.11 header */
+	BPF_STMT(BPF_ALU  | BPF_ADD | BPF_X, 0),
+	/* move to index, now start of LL header */
+	BPF_STMT(BPF_MISC | BPF_TAX, 0),
+
+	/*
+	 * Accept empty data frames, we use those for
+	 * polling activity.
+	 */
+	BPF_STMT(BPF_LD  | BPF_W | BPF_LEN, 0),
+	BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_X, 0, PASS, 0),
+
+	/*
+	 * Accept EAPOL frames
+	 */
+	BPF_STMT(BPF_LD  | BPF_W | BPF_IND, 0),
+	BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0xAAAA0300, 0, FAIL),
+	BPF_STMT(BPF_LD  | BPF_W | BPF_IND, 4),
+	BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0000888E, PASS, FAIL),
+
+	/* keep these last two statements or change the code below */
+	/* return 0 == "DROP" */
+	BPF_STMT(BPF_RET | BPF_K, 0),
+	/* return ~0 == "keep all" */
+	BPF_STMT(BPF_RET | BPF_K, ~0),
+};
+
+static struct sock_fprog msock_filter = {
+	.len = ARRAY_SIZE(msock_filter_insns),
+	.filter = msock_filter_insns,
+};
+
+
+static int add_monitor_filter(int s)
+{
+	int idx;
+
+	/* rewrite all PASS/FAIL jump offsets */
+	for (idx = 0; idx < msock_filter.len; idx++) {
+		struct sock_filter *insn = &msock_filter_insns[idx];
+
+		if (BPF_CLASS(insn->code) == BPF_JMP) {
+			if (insn->code == (BPF_JMP|BPF_JA)) {
+				if (insn->k == PASS)
+					insn->k = msock_filter.len - idx - 2;
+				else if (insn->k == FAIL)
+					insn->k = msock_filter.len - idx - 3;
+			}
+
+			if (insn->jt == PASS)
+				insn->jt = msock_filter.len - idx - 2;
+			else if (insn->jt == FAIL)
+				insn->jt = msock_filter.len - idx - 3;
+
+			if (insn->jf == PASS)
+				insn->jf = msock_filter.len - idx - 2;
+			else if (insn->jf == FAIL)
+				insn->jf = msock_filter.len - idx - 3;
+		}
+	}
+
+	if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER,
+		       &msock_filter, sizeof(msock_filter))) {
+		wpa_printf(MSG_ERROR, "nl80211: setsockopt(SO_ATTACH_FILTER) failed: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data *drv)
+{
+	if (drv->monitor_refcount > 0)
+		drv->monitor_refcount--;
+	wpa_printf(MSG_DEBUG, "nl80211: Remove monitor interface: refcount=%d",
+		   drv->monitor_refcount);
+	if (drv->monitor_refcount > 0)
+		return;
+
+	if (drv->monitor_ifidx >= 0) {
+		nl80211_remove_iface(drv, drv->monitor_ifidx);
+		drv->monitor_ifidx = -1;
+	}
+	if (drv->monitor_sock >= 0) {
+		eloop_unregister_read_sock(drv->monitor_sock);
+		close(drv->monitor_sock);
+		drv->monitor_sock = -1;
+	}
+}
+
+
+int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv)
+{
+	char buf[IFNAMSIZ];
+	struct sockaddr_ll ll;
+	int optval;
+	socklen_t optlen;
+
+	if (drv->monitor_ifidx >= 0) {
+		drv->monitor_refcount++;
+		wpa_printf(MSG_DEBUG, "nl80211: Re-use existing monitor interface: refcount=%d",
+			   drv->monitor_refcount);
+		return 0;
+	}
+
+	if (os_strncmp(drv->first_bss->ifname, "p2p-", 4) == 0) {
+		/*
+		 * P2P interface name is of the format p2p-%s-%d. For monitor
+		 * interface name corresponding to P2P GO, replace "p2p-" with
+		 * "mon-" to retain the same interface name length and to
+		 * indicate that it is a monitor interface.
+		 */
+		snprintf(buf, IFNAMSIZ, "mon-%s", drv->first_bss->ifname + 4);
+	} else {
+		int ret;
+
+		/* Non-P2P interface with AP functionality. */
+		ret = os_snprintf(buf, IFNAMSIZ, "mon.%s",
+				  drv->first_bss->ifname);
+		if (ret >= (int) sizeof(buf))
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Monitor interface name has been truncated to %s",
+				   buf);
+		else if (ret < 0)
+			return ret;
+	}
+
+	buf[IFNAMSIZ - 1] = '\0';
+
+	drv->monitor_ifidx =
+		nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL,
+				     0, NULL, NULL, 0);
+
+	if (drv->monitor_ifidx == -EOPNOTSUPP) {
+		/*
+		 * This is backward compatibility for a few versions of
+		 * the kernel only that didn't advertise the right
+		 * attributes for the only driver that then supported
+		 * AP mode w/o monitor -- ath6kl.
+		 */
+		wpa_printf(MSG_DEBUG, "nl80211: Driver does not support "
+			   "monitor interface type - try to run without it");
+		drv->device_ap_sme = 1;
+	}
+
+	if (drv->monitor_ifidx < 0)
+		return -1;
+
+	if (linux_set_iface_flags(drv->global->ioctl_sock, buf, 1))
+		goto error;
+
+	memset(&ll, 0, sizeof(ll));
+	ll.sll_family = AF_PACKET;
+	ll.sll_ifindex = drv->monitor_ifidx;
+	drv->monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+	if (drv->monitor_sock < 0) {
+		wpa_printf(MSG_ERROR, "nl80211: socket[PF_PACKET,SOCK_RAW] failed: %s",
+			   strerror(errno));
+		goto error;
+	}
+
+	if (add_monitor_filter(drv->monitor_sock)) {
+		wpa_printf(MSG_INFO, "Failed to set socket filter for monitor "
+			   "interface; do filtering in user space");
+		/* This works, but will cost in performance. */
+	}
+
+	if (bind(drv->monitor_sock, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
+		wpa_printf(MSG_ERROR, "nl80211: monitor socket bind failed: %s",
+			   strerror(errno));
+		goto error;
+	}
+
+	optlen = sizeof(optval);
+	optval = 20;
+	if (setsockopt
+	    (drv->monitor_sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) {
+		wpa_printf(MSG_ERROR, "nl80211: Failed to set socket priority: %s",
+			   strerror(errno));
+		goto error;
+	}
+
+	if (eloop_register_read_sock(drv->monitor_sock, handle_monitor_read,
+				     drv, NULL)) {
+		wpa_printf(MSG_INFO, "nl80211: Could not register monitor read socket");
+		goto error;
+	}
+
+	drv->monitor_refcount++;
+	return 0;
+ error:
+	nl80211_remove_monitor_interface(drv);
+	return -1;
+}
+
+
+int nl80211_send_monitor(struct wpa_driver_nl80211_data *drv,
+			 const void *data, size_t len,
+			 int encrypt, int noack)
+{
+	__u8 rtap_hdr[] = {
+		0x00, 0x00, /* radiotap version */
+		0x0e, 0x00, /* radiotap length */
+		0x02, 0xc0, 0x00, 0x00, /* bmap: flags, tx and rx flags */
+		IEEE80211_RADIOTAP_F_FRAG, /* F_FRAG (fragment if required) */
+		0x00,       /* padding */
+		0x00, 0x00, /* RX and TX flags to indicate that */
+		0x00, 0x00, /* this is the injected frame directly */
+	};
+	struct iovec iov[2] = {
+		{
+			.iov_base = &rtap_hdr,
+			.iov_len = sizeof(rtap_hdr),
+		},
+		{
+			.iov_base = (void *) data,
+			.iov_len = len,
+		}
+	};
+	struct msghdr msg = {
+		.msg_name = NULL,
+		.msg_namelen = 0,
+		.msg_iov = iov,
+		.msg_iovlen = 2,
+		.msg_control = NULL,
+		.msg_controllen = 0,
+		.msg_flags = 0,
+	};
+	int res;
+	u16 txflags = 0;
+
+	if (encrypt)
+		rtap_hdr[8] |= IEEE80211_RADIOTAP_F_WEP;
+
+	if (drv->monitor_sock < 0) {
+		wpa_printf(MSG_DEBUG, "nl80211: No monitor socket available "
+			   "for %s", __func__);
+		return -1;
+	}
+
+	if (noack)
+		txflags |= IEEE80211_RADIOTAP_F_TX_NOACK;
+	WPA_PUT_LE16(&rtap_hdr[12], txflags);
+
+	res = sendmsg(drv->monitor_sock, &msg, 0);
+	if (res < 0) {
+		wpa_printf(MSG_INFO, "nl80211: sendmsg: %s", strerror(errno));
+		return -1;
+	}
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211_scan.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211_scan.c
new file mode 100644
index 0000000..461d688
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_nl80211_scan.c
@@ -0,0 +1,1335 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211 - Scanning
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <time.h>
+#include <netlink/genl/genl.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/qca-vendor.h"
+#include "driver_nl80211.h"
+
+
+#define MAX_NL80211_NOISE_FREQS 100
+
+struct nl80211_noise_info {
+	u32 freq[MAX_NL80211_NOISE_FREQS];
+	s8 noise[MAX_NL80211_NOISE_FREQS];
+	unsigned int count;
+};
+
+static int get_noise_for_scan_results(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1];
+	static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
+		[NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
+		[NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
+	};
+	struct nl80211_noise_info *info = arg;
+
+	if (info->count >= MAX_NL80211_NOISE_FREQS)
+		return NL_SKIP;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (!tb[NL80211_ATTR_SURVEY_INFO]) {
+		wpa_printf(MSG_DEBUG, "nl80211: Survey data missing");
+		return NL_SKIP;
+	}
+
+	if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
+			     tb[NL80211_ATTR_SURVEY_INFO],
+			     survey_policy)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Failed to parse nested "
+			   "attributes");
+		return NL_SKIP;
+	}
+
+	if (!sinfo[NL80211_SURVEY_INFO_NOISE])
+		return NL_SKIP;
+
+	if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY])
+		return NL_SKIP;
+
+	info->freq[info->count] =
+		nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]);
+	info->noise[info->count] =
+		(s8) nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
+	info->count++;
+
+	return NL_SKIP;
+}
+
+
+static int nl80211_get_noise_for_scan_results(
+	struct wpa_driver_nl80211_data *drv, struct nl80211_noise_info *info)
+{
+	struct nl_msg *msg;
+
+	os_memset(info, 0, sizeof(*info));
+	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
+	return send_and_recv_msgs(drv, msg, get_noise_for_scan_results, info,
+				  NULL, NULL);
+}
+
+
+static int nl80211_abort_scan(struct i802_bss *bss)
+{
+	int ret;
+	struct nl_msg *msg;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Abort scan");
+	msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_ABORT_SCAN);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Abort scan failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+	}
+	return ret;
+}
+
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+static int nl80211_abort_vendor_scan(struct wpa_driver_nl80211_data *drv,
+				     u64 scan_cookie)
+{
+	struct nl_msg *msg;
+	struct nlattr *params;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Abort vendor scan with cookie 0x%llx",
+		   (long long unsigned int) scan_cookie);
+
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+	if (!msg ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_ABORT_SCAN) ||
+	    !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+	    nla_put_u64(msg, QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE, scan_cookie))
+		goto fail;
+
+	nla_nest_end(msg, params);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_INFO,
+			   "nl80211: Aborting vendor scan with cookie 0x%llx failed: ret=%d (%s)",
+			   (long long unsigned int) scan_cookie, ret,
+			   strerror(-ret));
+		goto fail;
+	}
+	return 0;
+fail:
+	nlmsg_free(msg);
+	return -1;
+}
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
+/**
+ * wpa_driver_nl80211_scan_timeout - Scan timeout to report scan completion
+ * @eloop_ctx: Driver private data
+ * @timeout_ctx: ctx argument given to wpa_driver_nl80211_init()
+ *
+ * This function can be used as registered timeout when starting a scan to
+ * generate a scan completed event if the driver does not report this.
+ */
+void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_driver_nl80211_data *drv = eloop_ctx;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Scan timeout - try to abort it");
+#ifdef CONFIG_DRIVER_NL80211_QCA
+	if (drv->vendor_scan_cookie &&
+	    nl80211_abort_vendor_scan(drv, drv->vendor_scan_cookie) == 0)
+		return;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+	if (!drv->vendor_scan_cookie &&
+	    nl80211_abort_scan(drv->first_bss) == 0)
+		return;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Failed to abort scan");
+
+	if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED)
+		nl80211_restore_ap_mode(drv->first_bss);
+
+	wpa_printf(MSG_DEBUG, "nl80211: Try to get scan results");
+	wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
+}
+
+
+static struct nl_msg *
+nl80211_scan_common(struct i802_bss *bss, u8 cmd,
+		    struct wpa_driver_scan_params *params)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	size_t i;
+	u32 scan_flags = 0;
+
+	msg = nl80211_cmd_msg(bss, 0, cmd);
+	if (!msg)
+		return NULL;
+
+	if (params->num_ssids) {
+		struct nlattr *ssids;
+
+		ssids = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS);
+		if (ssids == NULL)
+			goto fail;
+		for (i = 0; i < params->num_ssids; i++) {
+			wpa_printf(MSG_MSGDUMP, "nl80211: Scan SSID %s",
+				   wpa_ssid_txt(params->ssids[i].ssid,
+						params->ssids[i].ssid_len));
+			if (nla_put(msg, i + 1, params->ssids[i].ssid_len,
+				    params->ssids[i].ssid))
+				goto fail;
+		}
+		nla_nest_end(msg, ssids);
+
+		/*
+		 * If allowed, scan for 6 GHz APs that are reported by other
+		 * APs. Note that if the flag is not set and 6 GHz channels are
+		 * to be scanned, it is highly likely that non-PSC channels
+		 * would be scanned passively (due to the Probe Request frame
+		 * transmission restrictions mandated in IEEE Std 802.11ax-2021,
+		 * 26.17.2.3 (Scanning in the 6 GHz band). Passive scanning of
+		 * all non-PSC channels would take a significant amount of time.
+		 */
+		if (!params->non_coloc_6ghz) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Scan co-located APs on 6 GHz");
+			scan_flags |= NL80211_SCAN_FLAG_COLOCATED_6GHZ;
+		}
+	} else {
+		wpa_printf(MSG_DEBUG, "nl80211: Passive scan requested");
+	}
+
+	if (params->extra_ies) {
+		wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs",
+			    params->extra_ies, params->extra_ies_len);
+		if (nla_put(msg, NL80211_ATTR_IE, params->extra_ies_len,
+			    params->extra_ies))
+			goto fail;
+	}
+
+	if (params->freqs) {
+		struct nlattr *freqs;
+		freqs = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
+		if (freqs == NULL)
+			goto fail;
+		for (i = 0; params->freqs[i]; i++) {
+			wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u "
+				   "MHz", params->freqs[i]);
+			if (nla_put_u32(msg, i + 1, params->freqs[i]))
+				goto fail;
+		}
+		nla_nest_end(msg, freqs);
+	}
+
+	os_free(drv->filter_ssids);
+	drv->filter_ssids = params->filter_ssids;
+	params->filter_ssids = NULL;
+	drv->num_filter_ssids = params->num_filter_ssids;
+
+	if (!drv->hostapd && is_ap_interface(drv->nlmode)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Add NL80211_SCAN_FLAG_AP");
+		scan_flags |= NL80211_SCAN_FLAG_AP;
+	}
+
+	if (params->only_new_results) {
+		wpa_printf(MSG_DEBUG, "nl80211: Add NL80211_SCAN_FLAG_FLUSH");
+		scan_flags |= NL80211_SCAN_FLAG_FLUSH;
+	}
+
+	if (params->low_priority && drv->have_low_prio_scan) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Add NL80211_SCAN_FLAG_LOW_PRIORITY");
+		scan_flags |= NL80211_SCAN_FLAG_LOW_PRIORITY;
+	}
+
+	if (params->mac_addr_rand) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Add NL80211_SCAN_FLAG_RANDOM_ADDR");
+		scan_flags |= NL80211_SCAN_FLAG_RANDOM_ADDR;
+
+		if (params->mac_addr) {
+			wpa_printf(MSG_DEBUG, "nl80211: MAC address: " MACSTR,
+				   MAC2STR(params->mac_addr));
+			if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
+				    params->mac_addr))
+				goto fail;
+		}
+
+		if (params->mac_addr_mask) {
+			wpa_printf(MSG_DEBUG, "nl80211: MAC address mask: "
+				   MACSTR, MAC2STR(params->mac_addr_mask));
+			if (nla_put(msg, NL80211_ATTR_MAC_MASK, ETH_ALEN,
+				    params->mac_addr_mask))
+				goto fail;
+		}
+	}
+
+	if (params->duration) {
+		if (!(drv->capa.rrm_flags &
+		      WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL) ||
+		    nla_put_u16(msg, NL80211_ATTR_MEASUREMENT_DURATION,
+				params->duration))
+			goto fail;
+
+		if (params->duration_mandatory &&
+		    nla_put_flag(msg,
+				 NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY))
+			goto fail;
+	}
+
+	if (params->oce_scan) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Add NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME");
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Add NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP");
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Add NL80211_SCAN_FLAG_OCE_PROBE_REQ_MIN_TX_RATE");
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Add NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION");
+		scan_flags |= NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME |
+			NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP |
+			NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE |
+			NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION;
+	}
+
+	if (scan_flags &&
+	    nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, scan_flags))
+		goto fail;
+
+	return msg;
+
+fail:
+	nlmsg_free(msg);
+	return NULL;
+}
+
+
+/**
+ * wpa_driver_nl80211_scan - Request the driver to initiate scan
+ * @bss: Pointer to private driver data from wpa_driver_nl80211_init()
+ * @params: Scan parameters
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_nl80211_scan(struct i802_bss *bss,
+			    struct wpa_driver_scan_params *params)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int ret = -1, timeout;
+	struct nl_msg *msg = NULL;
+
+	wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: scan request");
+	drv->scan_for_auth = 0;
+
+	if (TEST_FAIL())
+		return -1;
+
+	msg = nl80211_scan_common(bss, NL80211_CMD_TRIGGER_SCAN, params);
+	if (!msg)
+		return -1;
+
+	if (params->p2p_probe) {
+		struct nlattr *rates;
+
+		wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates");
+
+		rates = nla_nest_start(msg, NL80211_ATTR_SCAN_SUPP_RATES);
+		if (rates == NULL)
+			goto fail;
+
+		/*
+		 * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates
+		 * by masking out everything else apart from the OFDM rates 6,
+		 * 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS rates. All 5 GHz
+		 * rates are left enabled.
+		 */
+		if (nla_put(msg, NL80211_BAND_2GHZ, 8,
+			    "\x0c\x12\x18\x24\x30\x48\x60\x6c"))
+			goto fail;
+		nla_nest_end(msg, rates);
+
+		if (nla_put_flag(msg, NL80211_ATTR_TX_NO_CCK_RATE))
+			goto fail;
+	}
+
+	if (params->bssid) {
+		wpa_printf(MSG_DEBUG, "nl80211: Scan for a specific BSSID: "
+			   MACSTR, MAC2STR(params->bssid));
+		if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid))
+			goto fail;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d "
+			   "(%s)", ret, strerror(-ret));
+		if (drv->hostapd && is_ap_interface(drv->nlmode)) {
+			enum nl80211_iftype old_mode = drv->nlmode;
+
+			/*
+			 * mac80211 does not allow scan requests in AP mode, so
+			 * try to do this in station mode.
+			 */
+			if (wpa_driver_nl80211_set_mode(
+				    bss, NL80211_IFTYPE_STATION))
+				goto fail;
+
+			if (wpa_driver_nl80211_scan(bss, params)) {
+				wpa_driver_nl80211_set_mode(bss, old_mode);
+				goto fail;
+			}
+
+			/* Restore AP mode when processing scan results */
+			drv->ap_scan_as_station = old_mode;
+			ret = 0;
+		} else
+			goto fail;
+	}
+
+	drv->scan_state = SCAN_REQUESTED;
+	/* Not all drivers generate "scan completed" wireless event, so try to
+	 * read results after a timeout. */
+	timeout = drv->uses_6ghz ? 15 : 10;
+	if (drv->scan_complete_events) {
+		/*
+		 * The driver seems to deliver events to notify when scan is
+		 * complete, so use longer timeout to avoid race conditions
+		 * with scanning and following association request.
+		 */
+		timeout = 30;
+	}
+	wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d "
+		   "seconds", ret, timeout);
+	eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
+	eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout,
+			       drv, drv->ctx);
+	drv->last_scan_cmd = NL80211_CMD_TRIGGER_SCAN;
+
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+static int
+nl80211_sched_scan_add_scan_plans(struct wpa_driver_nl80211_data *drv,
+				  struct nl_msg *msg,
+				  struct wpa_driver_scan_params *params)
+{
+	struct nlattr *plans;
+	struct sched_scan_plan *scan_plans = params->sched_scan_plans;
+	unsigned int i;
+
+	plans = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_PLANS);
+	if (!plans)
+		return -1;
+
+	for (i = 0; i < params->sched_scan_plans_num; i++) {
+		struct nlattr *plan = nla_nest_start(msg, i + 1);
+
+		if (!plan)
+			return -1;
+
+		if (!scan_plans[i].interval ||
+		    scan_plans[i].interval >
+		    drv->capa.max_sched_scan_plan_interval) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: sched scan plan no. %u: Invalid interval: %u",
+				   i, scan_plans[i].interval);
+			return -1;
+		}
+
+		if (nla_put_u32(msg, NL80211_SCHED_SCAN_PLAN_INTERVAL,
+				scan_plans[i].interval))
+			return -1;
+
+		if (scan_plans[i].iterations >
+		    drv->capa.max_sched_scan_plan_iterations) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: sched scan plan no. %u: Invalid number of iterations: %u",
+				   i, scan_plans[i].iterations);
+			return -1;
+		}
+
+		if (scan_plans[i].iterations &&
+		    nla_put_u32(msg, NL80211_SCHED_SCAN_PLAN_ITERATIONS,
+				scan_plans[i].iterations))
+			return -1;
+
+		nla_nest_end(msg, plan);
+
+		/*
+		 * All the scan plans must specify the number of iterations
+		 * except the last plan, which will run infinitely. So if the
+		 * number of iterations is not specified, this ought to be the
+		 * last scan plan.
+		 */
+		if (!scan_plans[i].iterations)
+			break;
+	}
+
+	if (i != params->sched_scan_plans_num - 1) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: All sched scan plans but the last must specify number of iterations");
+		return -1;
+	}
+
+	nla_nest_end(msg, plans);
+	return 0;
+}
+
+
+/**
+ * wpa_driver_nl80211_sched_scan - Initiate a scheduled scan
+ * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
+ * @params: Scan parameters
+ * Returns: 0 on success, -1 on failure or if not supported
+ */
+int wpa_driver_nl80211_sched_scan(void *priv,
+				  struct wpa_driver_scan_params *params)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int ret = -1;
+	struct nl_msg *msg;
+	size_t i;
+
+	wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: sched_scan request");
+
+#ifdef ANDROID
+	if (!drv->capa.sched_scan_supported)
+		return android_pno_start(bss, params);
+#endif /* ANDROID */
+
+	if (!params->sched_scan_plans_num ||
+	    params->sched_scan_plans_num > drv->capa.max_sched_scan_plans) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Invalid number of sched scan plans: %u",
+			   params->sched_scan_plans_num);
+		return -1;
+	}
+
+	msg = nl80211_scan_common(bss, NL80211_CMD_START_SCHED_SCAN, params);
+	if (!msg)
+		goto fail;
+
+	if (drv->capa.max_sched_scan_plan_iterations) {
+		if (nl80211_sched_scan_add_scan_plans(drv, msg, params))
+			goto fail;
+	} else {
+		if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL,
+				params->sched_scan_plans[0].interval * 1000))
+			goto fail;
+	}
+
+	if ((drv->num_filter_ssids &&
+	    (int) drv->num_filter_ssids <= drv->capa.max_match_sets) ||
+	    params->filter_rssi) {
+		struct nlattr *match_sets;
+		match_sets = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_MATCH);
+		if (match_sets == NULL)
+			goto fail;
+
+		for (i = 0; i < drv->num_filter_ssids; i++) {
+			struct nlattr *match_set_ssid;
+			wpa_printf(MSG_MSGDUMP,
+				   "nl80211: Sched scan filter SSID %s",
+				   wpa_ssid_txt(drv->filter_ssids[i].ssid,
+						drv->filter_ssids[i].ssid_len));
+
+			match_set_ssid = nla_nest_start(msg, i + 1);
+			if (match_set_ssid == NULL ||
+			    nla_put(msg, NL80211_ATTR_SCHED_SCAN_MATCH_SSID,
+				    drv->filter_ssids[i].ssid_len,
+				    drv->filter_ssids[i].ssid) ||
+			    (params->filter_rssi &&
+			     nla_put_u32(msg,
+					 NL80211_SCHED_SCAN_MATCH_ATTR_RSSI,
+					 params->filter_rssi)))
+				goto fail;
+
+			nla_nest_end(msg, match_set_ssid);
+		}
+
+		/*
+		 * Due to backward compatibility code, newer kernels treat this
+		 * matchset (with only an RSSI filter) as the default for all
+		 * other matchsets, unless it's the only one, in which case the
+		 * matchset will actually allow all SSIDs above the RSSI.
+		 */
+		if (params->filter_rssi) {
+			struct nlattr *match_set_rssi;
+			match_set_rssi = nla_nest_start(msg, 0);
+			if (match_set_rssi == NULL ||
+			    nla_put_u32(msg, NL80211_SCHED_SCAN_MATCH_ATTR_RSSI,
+					params->filter_rssi))
+				goto fail;
+			wpa_printf(MSG_MSGDUMP,
+				   "nl80211: Sched scan RSSI filter %d dBm",
+				   params->filter_rssi);
+			nla_nest_end(msg, match_set_rssi);
+		}
+
+		nla_nest_end(msg, match_sets);
+	}
+
+	if (params->relative_rssi_set) {
+		struct nl80211_bss_select_rssi_adjust rssi_adjust;
+
+		os_memset(&rssi_adjust, 0, sizeof(rssi_adjust));
+		wpa_printf(MSG_DEBUG, "nl80211: Relative RSSI: %d",
+			   params->relative_rssi);
+		if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI,
+				params->relative_rssi))
+			goto fail;
+
+		if (params->relative_adjust_rssi) {
+			int pref_band_set = 1;
+
+			switch (params->relative_adjust_band) {
+			case WPA_SETBAND_5G:
+				rssi_adjust.band = NL80211_BAND_5GHZ;
+				break;
+			case WPA_SETBAND_2G:
+				rssi_adjust.band = NL80211_BAND_2GHZ;
+				break;
+			default:
+				pref_band_set = 0;
+				break;
+			}
+			rssi_adjust.delta = params->relative_adjust_rssi;
+
+			if (pref_band_set &&
+			    nla_put(msg, NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST,
+				    sizeof(rssi_adjust), &rssi_adjust))
+				goto fail;
+		}
+	}
+
+	if (params->sched_scan_start_delay &&
+	    nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_DELAY,
+			params->sched_scan_start_delay))
+		goto fail;
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+
+	/* TODO: if we get an error here, we should fall back to normal scan */
+
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Sched scan start failed: "
+			   "ret=%d (%s)", ret, strerror(-ret));
+		goto fail;
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d)", ret);
+
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+/**
+ * wpa_driver_nl80211_stop_sched_scan - Stop a scheduled scan
+ * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
+ * Returns: 0 on success, -1 on failure or if not supported
+ */
+int wpa_driver_nl80211_stop_sched_scan(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int ret;
+	struct nl_msg *msg;
+
+#ifdef ANDROID
+	if (!drv->capa.sched_scan_supported)
+		return android_pno_stop(bss);
+#endif /* ANDROID */
+
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_STOP_SCHED_SCAN);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Sched scan stop failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Sched scan stop sent");
+	}
+
+	return ret;
+}
+
+
+static int nl80211_scan_filtered(struct wpa_driver_nl80211_data *drv,
+				 const u8 *ie, size_t ie_len)
+{
+	const u8 *ssid;
+	size_t i;
+
+	if (drv->filter_ssids == NULL)
+		return 0;
+
+	ssid = get_ie(ie, ie_len, WLAN_EID_SSID);
+	if (ssid == NULL)
+		return 1;
+
+	for (i = 0; i < drv->num_filter_ssids; i++) {
+		if (ssid[1] == drv->filter_ssids[i].ssid_len &&
+		    os_memcmp(ssid + 2, drv->filter_ssids[i].ssid, ssid[1]) ==
+		    0)
+			return 0;
+	}
+
+	return 1;
+}
+
+
+static struct wpa_scan_res *
+nl80211_parse_bss_info(struct wpa_driver_nl80211_data *drv,
+		       struct nl_msg *msg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *bss[NL80211_BSS_MAX + 1];
+	static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
+		[NL80211_BSS_BSSID] = { .type = NLA_UNSPEC },
+		[NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
+		[NL80211_BSS_TSF] = { .type = NLA_U64 },
+		[NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
+		[NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
+		[NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC },
+		[NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 },
+		[NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
+		[NL80211_BSS_STATUS] = { .type = NLA_U32 },
+		[NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 },
+		[NL80211_BSS_BEACON_IES] = { .type = NLA_UNSPEC },
+		[NL80211_BSS_BEACON_TSF] = { .type = NLA_U64 },
+		[NL80211_BSS_PARENT_TSF] = { .type = NLA_U64 },
+		[NL80211_BSS_PARENT_BSSID] = { .type = NLA_UNSPEC },
+		[NL80211_BSS_LAST_SEEN_BOOTTIME] = { .type = NLA_U64 },
+	};
+	struct wpa_scan_res *r;
+	const u8 *ie, *beacon_ie;
+	size_t ie_len, beacon_ie_len;
+	u8 *pos;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+	if (!tb[NL80211_ATTR_BSS])
+		return NULL;
+	if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
+			     bss_policy))
+		return NULL;
+	if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
+		ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
+		ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
+	} else {
+		ie = NULL;
+		ie_len = 0;
+	}
+	if (bss[NL80211_BSS_BEACON_IES]) {
+		beacon_ie = nla_data(bss[NL80211_BSS_BEACON_IES]);
+		beacon_ie_len = nla_len(bss[NL80211_BSS_BEACON_IES]);
+	} else {
+		beacon_ie = NULL;
+		beacon_ie_len = 0;
+	}
+
+	if (nl80211_scan_filtered(drv, ie ? ie : beacon_ie,
+				  ie ? ie_len : beacon_ie_len))
+		return NULL;
+
+	r = os_zalloc(sizeof(*r) + ie_len + beacon_ie_len);
+	if (r == NULL)
+		return NULL;
+	if (bss[NL80211_BSS_BSSID])
+		os_memcpy(r->bssid, nla_data(bss[NL80211_BSS_BSSID]),
+			  ETH_ALEN);
+	if (bss[NL80211_BSS_FREQUENCY])
+		r->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+	if (bss[NL80211_BSS_BEACON_INTERVAL])
+		r->beacon_int = nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]);
+	if (bss[NL80211_BSS_CAPABILITY])
+		r->caps = nla_get_u16(bss[NL80211_BSS_CAPABILITY]);
+	r->flags |= WPA_SCAN_NOISE_INVALID;
+	if (bss[NL80211_BSS_SIGNAL_MBM]) {
+		r->level = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]);
+		r->level /= 100; /* mBm to dBm */
+		r->flags |= WPA_SCAN_LEVEL_DBM | WPA_SCAN_QUAL_INVALID;
+	} else if (bss[NL80211_BSS_SIGNAL_UNSPEC]) {
+		r->level = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);
+		r->flags |= WPA_SCAN_QUAL_INVALID;
+	} else
+		r->flags |= WPA_SCAN_LEVEL_INVALID | WPA_SCAN_QUAL_INVALID;
+	if (bss[NL80211_BSS_TSF])
+		r->tsf = nla_get_u64(bss[NL80211_BSS_TSF]);
+	if (bss[NL80211_BSS_BEACON_TSF]) {
+		u64 tsf = nla_get_u64(bss[NL80211_BSS_BEACON_TSF]);
+		if (tsf > r->tsf) {
+			r->tsf = tsf;
+			r->beacon_newer = true;
+		}
+	}
+	if (bss[NL80211_BSS_SEEN_MS_AGO])
+		r->age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]);
+	if (bss[NL80211_BSS_LAST_SEEN_BOOTTIME]) {
+		u64 boottime;
+		struct timespec ts;
+
+#ifndef CLOCK_BOOTTIME
+#define CLOCK_BOOTTIME 7
+#endif
+		if (clock_gettime(CLOCK_BOOTTIME, &ts) == 0) {
+			/* Use more accurate boottime information to update the
+			 * scan result age since the driver reports this and
+			 * CLOCK_BOOTTIME is available. */
+			boottime = nla_get_u64(
+				bss[NL80211_BSS_LAST_SEEN_BOOTTIME]);
+			r->age = ((u64) ts.tv_sec * 1000000000 +
+				  ts.tv_nsec - boottime) / 1000000;
+		}
+	}
+	r->ie_len = ie_len;
+	pos = (u8 *) (r + 1);
+	if (ie) {
+		os_memcpy(pos, ie, ie_len);
+		pos += ie_len;
+	}
+	r->beacon_ie_len = beacon_ie_len;
+	if (beacon_ie)
+		os_memcpy(pos, beacon_ie, beacon_ie_len);
+
+	if (bss[NL80211_BSS_STATUS]) {
+		enum nl80211_bss_status status;
+		status = nla_get_u32(bss[NL80211_BSS_STATUS]);
+		switch (status) {
+		case NL80211_BSS_STATUS_ASSOCIATED:
+			r->flags |= WPA_SCAN_ASSOCIATED;
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (bss[NL80211_BSS_PARENT_TSF] && bss[NL80211_BSS_PARENT_BSSID]) {
+		r->parent_tsf = nla_get_u64(bss[NL80211_BSS_PARENT_TSF]);
+		os_memcpy(r->tsf_bssid, nla_data(bss[NL80211_BSS_PARENT_BSSID]),
+			  ETH_ALEN);
+	}
+
+	return r;
+}
+
+
+struct nl80211_bss_info_arg {
+	struct wpa_driver_nl80211_data *drv;
+	struct wpa_scan_results *res;
+};
+
+static int bss_info_handler(struct nl_msg *msg, void *arg)
+{
+	struct nl80211_bss_info_arg *_arg = arg;
+	struct wpa_scan_results *res = _arg->res;
+	struct wpa_scan_res **tmp;
+	struct wpa_scan_res *r;
+
+	r = nl80211_parse_bss_info(_arg->drv, msg);
+	if (!r)
+		return NL_SKIP;
+
+	if (!res) {
+		os_free(r);
+		return NL_SKIP;
+	}
+	tmp = os_realloc_array(res->res, res->num + 1,
+			       sizeof(struct wpa_scan_res *));
+	if (tmp == NULL) {
+		os_free(r);
+		return NL_SKIP;
+	}
+	tmp[res->num++] = r;
+	res->res = tmp;
+
+	return NL_SKIP;
+}
+
+
+static void clear_state_mismatch(struct wpa_driver_nl80211_data *drv,
+				 const u8 *addr)
+{
+	if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
+		wpa_printf(MSG_DEBUG, "nl80211: Clear possible state "
+			   "mismatch (" MACSTR ")", MAC2STR(addr));
+		wpa_driver_nl80211_mlme(drv, addr,
+					NL80211_CMD_DEAUTHENTICATE,
+					WLAN_REASON_PREV_AUTH_NOT_VALID, 1,
+					drv->first_bss);
+	}
+}
+
+
+static void nl80211_check_bss_status(struct wpa_driver_nl80211_data *drv,
+				     struct wpa_scan_res *r)
+{
+	if (!(r->flags & WPA_SCAN_ASSOCIATED))
+		return;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Scan results indicate BSS status with "
+		   MACSTR " as associated", MAC2STR(r->bssid));
+	if (is_sta_interface(drv->nlmode) && !drv->associated) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Local state (not associated) does not match with BSS state");
+		clear_state_mismatch(drv, r->bssid);
+	} else if (is_sta_interface(drv->nlmode) &&
+		   os_memcmp(drv->bssid, r->bssid, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Local state (associated with " MACSTR
+			   ") does not match with BSS state",
+			   MAC2STR(drv->bssid));
+
+		if (os_memcmp(drv->sta_mlo_info.ap_mld_addr, drv->bssid,
+			      ETH_ALEN) != 0) {
+			clear_state_mismatch(drv, r->bssid);
+
+			if (!is_zero_ether_addr(drv->sta_mlo_info.ap_mld_addr))
+				clear_state_mismatch(
+					drv, drv->sta_mlo_info.ap_mld_addr);
+			else
+				clear_state_mismatch(drv, drv->bssid);
+
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: BSSID is the MLD address");
+		}
+	}
+}
+
+
+static void wpa_driver_nl80211_check_bss_status(
+	struct wpa_driver_nl80211_data *drv, struct wpa_scan_results *res)
+{
+	size_t i;
+
+	for (i = 0; i < res->num; i++)
+		nl80211_check_bss_status(drv, res->res[i]);
+}
+
+
+static void nl80211_update_scan_res_noise(struct wpa_scan_res *res,
+					  struct nl80211_noise_info *info)
+{
+	unsigned int i;
+
+	for (i = 0; res && i < info->count; i++) {
+		if ((int) info->freq[i] != res->freq ||
+		    !(res->flags & WPA_SCAN_NOISE_INVALID))
+			continue;
+		res->noise = info->noise[i];
+		res->flags &= ~WPA_SCAN_NOISE_INVALID;
+	}
+}
+
+
+static struct wpa_scan_results *
+nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv)
+{
+	struct nl_msg *msg;
+	struct wpa_scan_results *res;
+	int ret;
+	struct nl80211_bss_info_arg arg;
+	int count = 0;
+
+try_again:
+	res = os_zalloc(sizeof(*res));
+	if (res == NULL)
+		return NULL;
+	if (!(msg = nl80211_cmd_msg(drv->first_bss, NLM_F_DUMP,
+				    NL80211_CMD_GET_SCAN))) {
+		wpa_scan_results_free(res);
+		return NULL;
+	}
+
+	arg.drv = drv;
+	arg.res = res;
+	ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg, NULL, NULL);
+	if (ret == -EAGAIN) {
+		count++;
+		if (count >= 10) {
+			wpa_printf(MSG_INFO,
+				   "nl80211: Failed to receive consistent scan result dump");
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Failed to receive consistent scan result dump - try again");
+			wpa_scan_results_free(res);
+			goto try_again;
+		}
+	}
+	if (ret == 0) {
+		struct nl80211_noise_info info;
+
+		wpa_printf(MSG_DEBUG, "nl80211: Received scan results (%lu "
+			   "BSSes)", (unsigned long) res->num);
+		if (nl80211_get_noise_for_scan_results(drv, &info) == 0) {
+			size_t i;
+
+			for (i = 0; i < res->num; ++i)
+				nl80211_update_scan_res_noise(res->res[i],
+							      &info);
+		}
+		return res;
+	}
+	wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
+		   "(%s)", ret, strerror(-ret));
+	wpa_scan_results_free(res);
+	return NULL;
+}
+
+
+/**
+ * wpa_driver_nl80211_get_scan_results - Fetch the latest scan results
+ * @priv: Pointer to private wext data from wpa_driver_nl80211_init()
+ * Returns: Scan results on success, -1 on failure
+ */
+struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct wpa_scan_results *res;
+
+	res = nl80211_get_scan_results(drv);
+	if (res)
+		wpa_driver_nl80211_check_bss_status(drv, res);
+	return res;
+}
+
+
+struct nl80211_dump_scan_ctx {
+	struct wpa_driver_nl80211_data *drv;
+	int idx;
+};
+
+static int nl80211_dump_scan_handler(struct nl_msg *msg, void *arg)
+{
+	struct nl80211_dump_scan_ctx *ctx = arg;
+	struct wpa_scan_res *r;
+
+	r = nl80211_parse_bss_info(ctx->drv, msg);
+	if (!r)
+		return NL_SKIP;
+	wpa_printf(MSG_DEBUG, "nl80211: %d " MACSTR " %d%s",
+		   ctx->idx, MAC2STR(r->bssid), r->freq,
+		   r->flags & WPA_SCAN_ASSOCIATED ? " [assoc]" : "");
+	ctx->idx++;
+	os_free(r);
+	return NL_SKIP;
+}
+
+
+void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv)
+{
+	struct nl_msg *msg;
+	struct nl80211_dump_scan_ctx ctx;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Scan result dump");
+	ctx.drv = drv;
+	ctx.idx = 0;
+	msg = nl80211_cmd_msg(drv->first_bss, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
+	if (msg)
+		send_and_recv_msgs(drv, msg, nl80211_dump_scan_handler, &ctx,
+				   NULL, NULL);
+}
+
+
+int wpa_driver_nl80211_abort_scan(void *priv, u64 scan_cookie)
+{
+	struct i802_bss *bss = priv;
+#ifdef CONFIG_DRIVER_NL80211_QCA
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	/*
+	 * If scan_cookie is zero, a normal scan through kernel (cfg80211)
+	 * was triggered, hence abort the cfg80211 scan instead of the vendor
+	 * scan.
+	 */
+	if (drv->scan_vendor_cmd_avail && scan_cookie)
+		return nl80211_abort_vendor_scan(drv, scan_cookie);
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+	return nl80211_abort_scan(bss);
+}
+
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+
+static int scan_cookie_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	u64 *cookie = arg;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (tb[NL80211_ATTR_VENDOR_DATA]) {
+		struct nlattr *nl_vendor = tb[NL80211_ATTR_VENDOR_DATA];
+		struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1];
+
+		nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_SCAN_MAX,
+			  nla_data(nl_vendor), nla_len(nl_vendor), NULL);
+
+		if (tb_vendor[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE])
+			*cookie = nla_get_u64(
+				tb_vendor[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]);
+	}
+
+	return NL_SKIP;
+}
+
+
+/**
+ * wpa_driver_nl80211_vendor_scan - Request the driver to initiate a vendor scan
+ * @bss: Pointer to private driver data from wpa_driver_nl80211_init()
+ * @params: Scan parameters
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss,
+				   struct wpa_driver_scan_params *params)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg = NULL;
+	struct nlattr *attr;
+	size_t i;
+	u32 scan_flags = 0;
+	int ret = -1;
+	u64 cookie = 0;
+
+	wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: vendor scan request");
+	drv->scan_for_auth = 0;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN) )
+		goto fail;
+
+	attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+	if (attr == NULL)
+		goto fail;
+
+	if (params->num_ssids) {
+		struct nlattr *ssids;
+
+		ssids = nla_nest_start(msg, QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS);
+		if (ssids == NULL)
+			goto fail;
+		for (i = 0; i < params->num_ssids; i++) {
+			wpa_printf(MSG_MSGDUMP, "nl80211: Scan SSID %s",
+				   wpa_ssid_txt(params->ssids[i].ssid,
+						params->ssids[i].ssid_len));
+			if (nla_put(msg, i + 1, params->ssids[i].ssid_len,
+				    params->ssids[i].ssid))
+				goto fail;
+		}
+		nla_nest_end(msg, ssids);
+	}
+
+	if (params->extra_ies) {
+		wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs",
+			    params->extra_ies, params->extra_ies_len);
+		if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SCAN_IE,
+			    params->extra_ies_len, params->extra_ies))
+			goto fail;
+	}
+
+	if (params->freqs) {
+		struct nlattr *freqs;
+
+		freqs = nla_nest_start(msg,
+				       QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES);
+		if (freqs == NULL)
+			goto fail;
+		for (i = 0; params->freqs[i]; i++) {
+			wpa_printf(MSG_MSGDUMP,
+				   "nl80211: Scan frequency %u MHz",
+				   params->freqs[i]);
+			if (nla_put_u32(msg, i + 1, params->freqs[i]))
+				goto fail;
+		}
+		nla_nest_end(msg, freqs);
+	}
+
+	os_free(drv->filter_ssids);
+	drv->filter_ssids = params->filter_ssids;
+	params->filter_ssids = NULL;
+	drv->num_filter_ssids = params->num_filter_ssids;
+
+	if (params->low_priority && drv->have_low_prio_scan) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Add NL80211_SCAN_FLAG_LOW_PRIORITY");
+		scan_flags |= NL80211_SCAN_FLAG_LOW_PRIORITY;
+	}
+
+	if (params->mac_addr_rand) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Add NL80211_SCAN_FLAG_RANDOM_ADDR");
+		scan_flags |= NL80211_SCAN_FLAG_RANDOM_ADDR;
+
+		if (params->mac_addr) {
+			wpa_printf(MSG_DEBUG, "nl80211: MAC address: " MACSTR,
+				   MAC2STR(params->mac_addr));
+			if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SCAN_MAC,
+				    ETH_ALEN, params->mac_addr))
+				goto fail;
+		}
+
+		if (params->mac_addr_mask) {
+			wpa_printf(MSG_DEBUG, "nl80211: MAC address mask: "
+				   MACSTR, MAC2STR(params->mac_addr_mask));
+			if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK,
+				    ETH_ALEN, params->mac_addr_mask))
+				goto fail;
+		}
+	}
+
+	if (scan_flags &&
+	    nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS, scan_flags))
+		goto fail;
+
+	if (params->p2p_probe) {
+		struct nlattr *rates;
+
+		wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates");
+
+		rates = nla_nest_start(msg,
+				       QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES);
+		if (rates == NULL)
+			goto fail;
+
+		/*
+		 * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates
+		 * by masking out everything else apart from the OFDM rates 6,
+		 * 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS rates. All 5 GHz
+		 * rates are left enabled.
+		 */
+		if (nla_put(msg, NL80211_BAND_2GHZ, 8,
+			    "\x0c\x12\x18\x24\x30\x48\x60\x6c"))
+			goto fail;
+		nla_nest_end(msg, rates);
+
+		if (nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE))
+			goto fail;
+	}
+
+	if (params->bssid) {
+		wpa_printf(MSG_DEBUG, "nl80211: Scan for a specific BSSID: "
+			   MACSTR, MAC2STR(params->bssid));
+		if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SCAN_BSSID, ETH_ALEN,
+			    params->bssid))
+			goto fail;
+	}
+
+	nla_nest_end(msg, attr);
+
+	ret = send_and_recv_msgs(drv, msg, scan_cookie_handler, &cookie,
+				 NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Vendor scan trigger failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+		goto fail;
+	}
+
+	drv->vendor_scan_cookie = cookie;
+	drv->scan_state = SCAN_REQUESTED;
+	/* Pass the cookie to the caller to help distinguish the scans. */
+	params->scan_cookie = cookie;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Vendor scan requested (ret=%d) - scan timeout 30 seconds, scan cookie:0x%llx",
+		   ret, (long long unsigned int) cookie);
+	eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
+	eloop_register_timeout(30, 0, wpa_driver_nl80211_scan_timeout,
+			       drv, drv->ctx);
+	drv->last_scan_cmd = NL80211_CMD_VENDOR;
+
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+/**
+ * nl80211_set_default_scan_ies - Set the scan default IEs to the driver
+ * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
+ * @ies: Pointer to IEs buffer
+ * @ies_len: Length of IEs in bytes
+ * Returns: 0 on success, -1 on failure
+ */
+int nl80211_set_default_scan_ies(void *priv, const u8 *ies, size_t ies_len)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg = NULL;
+	struct nlattr *attr;
+	int ret = -1;
+
+	if (!drv->set_wifi_conf_vendor_cmd_avail)
+		return -1;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION))
+		goto fail;
+
+	attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+	if (attr == NULL)
+		goto fail;
+
+	wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan default IEs", ies, ies_len);
+	if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_DEFAULT_IES,
+		    ies_len, ies))
+		goto fail;
+
+	nla_nest_end(msg, attr);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Set scan default IEs failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+		goto fail;
+	}
+
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+
+#endif /* CONFIG_DRIVER_NL80211_QCA */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_none.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_none.c
new file mode 100644
index 0000000..ccd2d9d
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_none.c
@@ -0,0 +1,77 @@
+/*
+ * Driver interface for RADIUS server or WPS ER only (no driver)
+ * Copyright (c) 2008, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "driver.h"
+
+
+struct none_driver_data {
+	struct hostapd_data *hapd;
+	void *ctx;
+};
+
+
+static void * none_driver_hapd_init(struct hostapd_data *hapd,
+				    struct wpa_init_params *params)
+{
+	struct none_driver_data *drv;
+
+	drv = os_zalloc(sizeof(struct none_driver_data));
+	if (drv == NULL) {
+		wpa_printf(MSG_ERROR, "Could not allocate memory for none "
+			   "driver data");
+		return NULL;
+	}
+	drv->hapd = hapd;
+
+	return drv;
+}
+
+
+static void none_driver_hapd_deinit(void *priv)
+{
+	struct none_driver_data *drv = priv;
+
+	os_free(drv);
+}
+
+
+static void * none_driver_init(void *ctx, const char *ifname)
+{
+	struct none_driver_data *drv;
+
+	drv = os_zalloc(sizeof(struct none_driver_data));
+	if (drv == NULL) {
+		wpa_printf(MSG_ERROR, "Could not allocate memory for none "
+			   "driver data");
+		return NULL;
+	}
+	drv->ctx = ctx;
+
+	return drv;
+}
+
+
+static void none_driver_deinit(void *priv)
+{
+	struct none_driver_data *drv = priv;
+
+	os_free(drv);
+}
+
+
+const struct wpa_driver_ops wpa_driver_none_ops = {
+	.name = "none",
+	.desc = "no driver (RADIUS server/WPS ER)",
+	.hapd_init = none_driver_hapd_init,
+	.hapd_deinit = none_driver_hapd_deinit,
+	.init = none_driver_init,
+	.deinit = none_driver_deinit,
+};
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_openbsd.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_openbsd.c
new file mode 100644
index 0000000..bfc2311
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_openbsd.c
@@ -0,0 +1,139 @@
+/*
+ * Driver interaction with OpenBSD net80211 layer
+ * Copyright (c) 2013, Mark Kettenis
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+
+#include <net/if.h>
+#include <net80211/ieee80211.h>
+#include <net80211/ieee80211_crypto.h>
+#include <net80211/ieee80211_ioctl.h>
+
+#include "common.h"
+#include "driver.h"
+
+struct openbsd_driver_data {
+	char ifname[IFNAMSIZ + 1];
+	void *ctx;
+
+	int sock;			/* open socket for 802.11 ioctls */
+};
+
+
+static int
+wpa_driver_openbsd_get_ssid(void *priv, u8 *ssid)
+{
+	struct openbsd_driver_data *drv = priv;
+	struct ieee80211_nwid nwid;
+	struct ifreq ifr;
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+	ifr.ifr_data = (void *)&nwid;
+	if (ioctl(drv->sock, SIOCG80211NWID, &ifr) < 0 ||
+	    nwid.i_len > IEEE80211_NWID_LEN)
+		return -1;
+
+	os_memcpy(ssid, nwid.i_nwid, nwid.i_len);
+	return nwid.i_len;
+}
+
+static int
+wpa_driver_openbsd_get_bssid(void *priv, u8 *bssid)
+{
+	struct openbsd_driver_data *drv = priv;
+	struct ieee80211_bssid id;
+
+	os_strlcpy(id.i_name, drv->ifname, sizeof(id.i_name));
+	if (ioctl(drv->sock, SIOCG80211BSSID, &id) < 0)
+		return -1;
+
+	os_memcpy(bssid, id.i_bssid, IEEE80211_ADDR_LEN);
+	return 0;
+}
+
+
+static int
+wpa_driver_openbsd_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+	os_memset(capa, 0, sizeof(*capa));
+	capa->flags = WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK |
+		      WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X;
+	return 0;
+}
+
+
+static int
+wpa_driver_openbsd_set_key(void *priv, struct wpa_driver_set_key_params *params)
+{
+	struct openbsd_driver_data *drv = priv;
+	struct ieee80211_keyavail keyavail;
+	enum key_flag key_flag = params->key_flag;
+	const u8 *key = params->key;
+	size_t key_len = params->key_len;
+
+	if (key_len > IEEE80211_PMK_LEN ||
+	    (key_flag & KEY_FLAG_PMK_MASK) != KEY_FLAG_PMK) {
+		return -1;
+
+	memset(&keyavail, 0, sizeof(keyavail));
+	os_strlcpy(keyavail.i_name, drv->ifname, sizeof(keyavail.i_name));
+	if (wpa_driver_openbsd_get_bssid(priv, keyavail.i_macaddr) < 0)
+		return -1;
+	memcpy(keyavail.i_key, key, key_len);
+
+	if (ioctl(drv->sock, SIOCS80211KEYAVAIL, &keyavail) < 0)
+		return -1;
+
+	return 0;
+}
+
+static void *
+wpa_driver_openbsd_init(void *ctx, const char *ifname)
+{
+	struct openbsd_driver_data *drv;
+
+	drv = os_zalloc(sizeof(*drv));
+	if (drv == NULL)
+		return NULL;
+
+	drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
+	if (drv->sock < 0)
+		goto fail;
+
+	drv->ctx = ctx;
+	os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+
+	return drv;
+
+fail:
+	os_free(drv);
+	return NULL;
+}
+
+
+static void
+wpa_driver_openbsd_deinit(void *priv)
+{
+	struct openbsd_driver_data *drv = priv;
+
+	close(drv->sock);
+	os_free(drv);
+}
+
+
+const struct wpa_driver_ops wpa_driver_openbsd_ops = {
+	.name = "openbsd",
+	.desc = "OpenBSD 802.11 support",
+	.get_ssid = wpa_driver_openbsd_get_ssid,
+	.get_bssid = wpa_driver_openbsd_get_bssid,
+	.get_capa = wpa_driver_openbsd_get_capa,
+	.set_key = wpa_driver_openbsd_set_key,
+	.init = wpa_driver_openbsd_init,
+	.deinit = wpa_driver_openbsd_deinit,
+};
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_privsep.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_privsep.c
new file mode 100644
index 0000000..d6735b4
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_privsep.c
@@ -0,0 +1,847 @@
+/*
+ * WPA Supplicant - privilege separated driver interface
+ * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/un.h>
+
+#include "common.h"
+#include "driver.h"
+#include "eloop.h"
+#include "common/privsep_commands.h"
+
+
+struct wpa_driver_privsep_data {
+	void *ctx;
+	u8 own_addr[ETH_ALEN];
+	int priv_socket;
+	char *own_socket_path;
+	int cmd_socket;
+	char *own_cmd_path;
+	struct sockaddr_un priv_addr;
+	char ifname[16];
+};
+
+
+static int wpa_priv_reg_cmd(struct wpa_driver_privsep_data *drv, int cmd)
+{
+	int res;
+
+	res = sendto(drv->priv_socket, &cmd, sizeof(cmd), 0,
+		     (struct sockaddr *) &drv->priv_addr,
+		     sizeof(drv->priv_addr));
+	if (res < 0)
+		wpa_printf(MSG_ERROR, "sendto: %s", strerror(errno));
+	return res < 0 ? -1 : 0;
+}
+
+
+static int wpa_priv_cmd(struct wpa_driver_privsep_data *drv, int cmd,
+			const void *data, size_t data_len,
+			void *reply, size_t *reply_len)
+{
+	struct msghdr msg;
+	struct iovec io[2];
+
+	io[0].iov_base = &cmd;
+	io[0].iov_len = sizeof(cmd);
+	io[1].iov_base = (u8 *) data;
+	io[1].iov_len = data_len;
+
+	os_memset(&msg, 0, sizeof(msg));
+	msg.msg_iov = io;
+	msg.msg_iovlen = data ? 2 : 1;
+	msg.msg_name = &drv->priv_addr;
+	msg.msg_namelen = sizeof(drv->priv_addr);
+
+	if (sendmsg(drv->cmd_socket, &msg, 0) < 0) {
+		wpa_printf(MSG_ERROR, "sendmsg(cmd_socket): %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	if (reply) {
+		fd_set rfds;
+		struct timeval tv;
+		int res;
+
+		FD_ZERO(&rfds);
+		FD_SET(drv->cmd_socket, &rfds);
+		tv.tv_sec = 5;
+		tv.tv_usec = 0;
+		res = select(drv->cmd_socket + 1, &rfds, NULL, NULL, &tv);
+		if (res < 0 && errno != EINTR) {
+			wpa_printf(MSG_ERROR, "select: %s", strerror(errno));
+			return -1;
+		}
+
+		if (FD_ISSET(drv->cmd_socket, &rfds)) {
+			res = recv(drv->cmd_socket, reply, *reply_len, 0);
+			if (res < 0) {
+				wpa_printf(MSG_ERROR, "recv: %s",
+					   strerror(errno));
+				return -1;
+			}
+			*reply_len = res;
+		} else {
+			wpa_printf(MSG_DEBUG, "PRIVSEP: Timeout while waiting "
+				   "for reply (cmd=%d)", cmd);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+static int wpa_driver_privsep_scan(void *priv,
+				   struct wpa_driver_scan_params *params)
+{
+	struct wpa_driver_privsep_data *drv = priv;
+	struct privsep_cmd_scan scan;
+	size_t i;
+
+	wpa_printf(MSG_DEBUG, "%s: priv=%p", __func__, priv);
+	os_memset(&scan, 0, sizeof(scan));
+	scan.num_ssids = params->num_ssids;
+	for (i = 0; i < params->num_ssids; i++) {
+		if (!params->ssids[i].ssid)
+			continue;
+		scan.ssid_lens[i] = params->ssids[i].ssid_len;
+		os_memcpy(scan.ssids[i], params->ssids[i].ssid,
+			  scan.ssid_lens[i]);
+	}
+
+	for (i = 0; i < PRIVSEP_MAX_SCAN_FREQS &&
+		     params->freqs && params->freqs[i]; i++)
+		scan.freqs[i] = params->freqs[i];
+	scan.num_freqs = i;
+
+	return wpa_priv_cmd(drv, PRIVSEP_CMD_SCAN, &scan, sizeof(scan),
+			    NULL, NULL);
+}
+
+
+static struct wpa_scan_results *
+wpa_driver_privsep_get_scan_results2(void *priv)
+{
+	struct wpa_driver_privsep_data *drv = priv;
+	int res, num;
+	u8 *buf, *pos, *end;
+	size_t reply_len = 60000;
+	struct wpa_scan_results *results;
+	struct wpa_scan_res *r;
+
+	buf = os_malloc(reply_len);
+	if (buf == NULL)
+		return NULL;
+	res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_SCAN_RESULTS,
+			   NULL, 0, buf, &reply_len);
+	if (res < 0) {
+		os_free(buf);
+		return NULL;
+	}
+
+	wpa_printf(MSG_DEBUG, "privsep: Received %lu bytes of scan results",
+		   (unsigned long) reply_len);
+	if (reply_len < sizeof(int)) {
+		wpa_printf(MSG_DEBUG, "privsep: Invalid scan result len %lu",
+			   (unsigned long) reply_len);
+		os_free(buf);
+		return NULL;
+	}
+
+	pos = buf;
+	end = buf + reply_len;
+	os_memcpy(&num, pos, sizeof(int));
+	if (num < 0 || num > 1000) {
+		os_free(buf);
+		return NULL;
+	}
+	pos += sizeof(int);
+
+	results = os_zalloc(sizeof(*results));
+	if (results == NULL) {
+		os_free(buf);
+		return NULL;
+	}
+
+	results->res = os_calloc(num, sizeof(struct wpa_scan_res *));
+	if (results->res == NULL) {
+		os_free(results);
+		os_free(buf);
+		return NULL;
+	}
+
+	while (results->num < (size_t) num && end - pos > (int) sizeof(int)) {
+		int len;
+		os_memcpy(&len, pos, sizeof(int));
+		pos += sizeof(int);
+		if (len < 0 || len > 10000 || len > end - pos)
+			break;
+
+		r = os_memdup(pos, len);
+		if (r == NULL)
+			break;
+		pos += len;
+		if (sizeof(*r) + r->ie_len + r->beacon_ie_len > (size_t) len) {
+			wpa_printf(MSG_ERROR,
+				   "privsep: Invalid scan result len (%d + %d + %d > %d)",
+				   (int) sizeof(*r), (int) r->ie_len,
+				   (int) r->beacon_ie_len, len);
+			os_free(r);
+			break;
+		}
+
+		results->res[results->num++] = r;
+	}
+
+	os_free(buf);
+	return results;
+}
+
+
+static int wpa_driver_privsep_set_key(void *priv,
+				      struct wpa_driver_set_key_params *params)
+{
+	struct wpa_driver_privsep_data *drv = priv;
+	struct privsep_cmd_set_key cmd;
+	enum wpa_alg alg = params->alg;
+	const u8 *addr = params->addr;
+	int key_idx = params->key_idx;
+	int set_tx = params->set_tx;
+	const u8 *seq = params->seq;
+	size_t seq_len = params->seq_len;
+	const u8 *key = params->key;
+	size_t key_len = params->key_len;
+
+	wpa_printf(MSG_DEBUG, "%s: priv=%p alg=%d key_idx=%d set_tx=%d",
+		   __func__, priv, alg, key_idx, set_tx);
+
+	os_memset(&cmd, 0, sizeof(cmd));
+	cmd.alg = alg;
+	if (addr)
+		os_memcpy(cmd.addr, addr, ETH_ALEN);
+	else
+		os_memset(cmd.addr, 0xff, ETH_ALEN);
+	cmd.key_idx = key_idx;
+	cmd.set_tx = set_tx;
+	cmd.key_flag = params->key_flag;
+	if (seq && seq_len > 0 && seq_len < sizeof(cmd.seq)) {
+		os_memcpy(cmd.seq, seq, seq_len);
+		cmd.seq_len = seq_len;
+	}
+	if (key && key_len > 0 && key_len < sizeof(cmd.key)) {
+		os_memcpy(cmd.key, key, key_len);
+		cmd.key_len = key_len;
+	}
+
+	return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_KEY, &cmd, sizeof(cmd),
+			    NULL, NULL);
+}
+
+
+static int wpa_driver_privsep_authenticate(
+	void *priv, struct wpa_driver_auth_params *params)
+{
+	struct wpa_driver_privsep_data *drv = priv;
+	struct privsep_cmd_authenticate *data;
+	int i, res;
+	size_t buflen;
+	u8 *pos;
+
+	wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d bssid=" MACSTR
+		   " auth_alg=%d local_state_change=%d p2p=%d",
+		   __func__, priv, params->freq, MAC2STR(params->bssid),
+		   params->auth_alg, params->local_state_change, params->p2p);
+
+	buflen = sizeof(*data) + params->ie_len + params->auth_data_len;
+	data = os_zalloc(buflen);
+	if (data == NULL)
+		return -1;
+
+	data->freq = params->freq;
+	os_memcpy(data->bssid, params->bssid, ETH_ALEN);
+	os_memcpy(data->ssid, params->ssid, params->ssid_len);
+	data->ssid_len = params->ssid_len;
+	data->auth_alg = params->auth_alg;
+	data->ie_len = params->ie_len;
+	for (i = 0; i < 4; i++) {
+		if (params->wep_key[i])
+			os_memcpy(data->wep_key[i], params->wep_key[i],
+				  params->wep_key_len[i]);
+		data->wep_key_len[i] = params->wep_key_len[i];
+	}
+	data->wep_tx_keyidx = params->wep_tx_keyidx;
+	data->local_state_change = params->local_state_change;
+	data->p2p = params->p2p;
+	pos = (u8 *) (data + 1);
+	if (params->ie_len) {
+		os_memcpy(pos, params->ie, params->ie_len);
+		pos += params->ie_len;
+	}
+	if (params->auth_data_len)
+		os_memcpy(pos, params->auth_data, params->auth_data_len);
+
+	res = wpa_priv_cmd(drv, PRIVSEP_CMD_AUTHENTICATE, data, buflen,
+			   NULL, NULL);
+	os_free(data);
+
+	return res;
+}
+
+
+static int wpa_driver_privsep_associate(
+	void *priv, struct wpa_driver_associate_params *params)
+{
+	struct wpa_driver_privsep_data *drv = priv;
+	struct privsep_cmd_associate *data;
+	int res;
+	size_t buflen;
+
+	wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d pairwise_suite=%d "
+		   "group_suite=%d key_mgmt_suite=%d auth_alg=%d mode=%d",
+		   __func__, priv, params->freq.freq, params->pairwise_suite,
+		   params->group_suite, params->key_mgmt_suite,
+		   params->auth_alg, params->mode);
+
+	buflen = sizeof(*data) + params->wpa_ie_len;
+	data = os_zalloc(buflen);
+	if (data == NULL)
+		return -1;
+
+	if (params->bssid)
+		os_memcpy(data->bssid, params->bssid, ETH_ALEN);
+	os_memcpy(data->ssid, params->ssid, params->ssid_len);
+	data->ssid_len = params->ssid_len;
+	data->hwmode = params->freq.mode;
+	data->freq = params->freq.freq;
+	data->channel = params->freq.channel;
+	data->pairwise_suite = params->pairwise_suite;
+	data->group_suite = params->group_suite;
+	data->key_mgmt_suite = params->key_mgmt_suite;
+	data->auth_alg = params->auth_alg;
+	data->mode = params->mode;
+	data->wpa_ie_len = params->wpa_ie_len;
+	if (params->wpa_ie)
+		os_memcpy(data + 1, params->wpa_ie, params->wpa_ie_len);
+	/* TODO: add support for other assoc parameters */
+
+	res = wpa_priv_cmd(drv, PRIVSEP_CMD_ASSOCIATE, data, buflen,
+			   NULL, NULL);
+	os_free(data);
+
+	return res;
+}
+
+
+static int wpa_driver_privsep_get_bssid(void *priv, u8 *bssid)
+{
+	struct wpa_driver_privsep_data *drv = priv;
+	int res;
+	size_t len = ETH_ALEN;
+
+	res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_BSSID, NULL, 0, bssid, &len);
+	if (res < 0 || len != ETH_ALEN)
+		return -1;
+	return 0;
+}
+
+
+static int wpa_driver_privsep_get_ssid(void *priv, u8 *ssid)
+{
+	struct wpa_driver_privsep_data *drv = priv;
+	int res, ssid_len;
+	u8 reply[sizeof(int) + SSID_MAX_LEN];
+	size_t len = sizeof(reply);
+
+	res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_SSID, NULL, 0, reply, &len);
+	if (res < 0 || len < sizeof(int))
+		return -1;
+	os_memcpy(&ssid_len, reply, sizeof(int));
+	if (ssid_len < 0 || ssid_len > SSID_MAX_LEN ||
+	    sizeof(int) + ssid_len > len) {
+		wpa_printf(MSG_DEBUG, "privsep: Invalid get SSID reply");
+		return -1;
+	}
+	os_memcpy(ssid, &reply[sizeof(int)], ssid_len);
+	return ssid_len;
+}
+
+
+static int wpa_driver_privsep_deauthenticate(void *priv, const u8 *addr,
+					     u16 reason_code)
+{
+	//struct wpa_driver_privsep_data *drv = priv;
+	wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d",
+		   __func__, MAC2STR(addr), reason_code);
+	wpa_printf(MSG_DEBUG, "%s - TODO", __func__);
+	return 0;
+}
+
+
+static void wpa_driver_privsep_event_auth(void *ctx, u8 *buf, size_t len)
+{
+	union wpa_event_data data;
+	struct privsep_event_auth *auth;
+
+	os_memset(&data, 0, sizeof(data));
+	if (len < sizeof(*auth))
+		return;
+	auth = (struct privsep_event_auth *) buf;
+	if (len < sizeof(*auth) + auth->ies_len)
+		return;
+
+	os_memcpy(data.auth.peer, auth->peer, ETH_ALEN);
+	os_memcpy(data.auth.bssid, auth->bssid, ETH_ALEN);
+	data.auth.auth_type = auth->auth_type;
+	data.auth.auth_transaction = auth->auth_transaction;
+	data.auth.status_code = auth->status_code;
+	if (auth->ies_len) {
+		data.auth.ies = (u8 *) (auth + 1);
+		data.auth.ies_len = auth->ies_len;
+	}
+
+	wpa_supplicant_event(ctx, EVENT_AUTH, &data);
+}
+
+
+static void wpa_driver_privsep_event_assoc(void *ctx,
+					   enum wpa_event_type event,
+					   u8 *buf, size_t len)
+{
+	union wpa_event_data data;
+	int inc_data = 0;
+	u8 *pos, *end;
+	int ie_len;
+
+	os_memset(&data, 0, sizeof(data));
+
+	pos = buf;
+	end = buf + len;
+
+	if (end - pos < (int) sizeof(int))
+		return;
+	os_memcpy(&ie_len, pos, sizeof(int));
+	pos += sizeof(int);
+	if (ie_len < 0 || ie_len > end - pos)
+		return;
+	if (ie_len) {
+		data.assoc_info.req_ies = pos;
+		data.assoc_info.req_ies_len = ie_len;
+		pos += ie_len;
+		inc_data = 1;
+	}
+
+	wpa_supplicant_event(ctx, event, inc_data ? &data : NULL);
+}
+
+
+static void wpa_driver_privsep_event_interface_status(void *ctx, u8 *buf,
+						      size_t len)
+{
+	union wpa_event_data data;
+	int ievent;
+
+	if (len < sizeof(int) ||
+	    len - sizeof(int) > sizeof(data.interface_status.ifname))
+		return;
+
+	os_memcpy(&ievent, buf, sizeof(int));
+
+	os_memset(&data, 0, sizeof(data));
+	data.interface_status.ievent = ievent;
+	os_memcpy(data.interface_status.ifname, buf + sizeof(int),
+		  len - sizeof(int));
+	wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &data);
+}
+
+
+static void wpa_driver_privsep_event_michael_mic_failure(
+	void *ctx, u8 *buf, size_t len)
+{
+	union wpa_event_data data;
+
+	if (len != sizeof(int))
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	os_memcpy(&data.michael_mic_failure.unicast, buf, sizeof(int));
+	wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+}
+
+
+static void wpa_driver_privsep_event_pmkid_candidate(void *ctx, u8 *buf,
+						     size_t len)
+{
+	union wpa_event_data data;
+
+	if (len != sizeof(struct pmkid_candidate))
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	os_memcpy(&data.pmkid_candidate, buf, len);
+	wpa_supplicant_event(ctx, EVENT_PMKID_CANDIDATE, &data);
+}
+
+
+static void wpa_driver_privsep_event_ft_response(void *ctx, u8 *buf,
+						 size_t len)
+{
+	union wpa_event_data data;
+
+	if (len < sizeof(int) + ETH_ALEN)
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	os_memcpy(&data.ft_ies.ft_action, buf, sizeof(int));
+	os_memcpy(data.ft_ies.target_ap, buf + sizeof(int), ETH_ALEN);
+	data.ft_ies.ies = buf + sizeof(int) + ETH_ALEN;
+	data.ft_ies.ies_len = len - sizeof(int) - ETH_ALEN;
+	wpa_supplicant_event(ctx, EVENT_FT_RESPONSE, &data);
+}
+
+
+static void wpa_driver_privsep_event_rx_eapol(void *ctx, u8 *buf, size_t len)
+{
+	if (len < ETH_ALEN)
+		return;
+	drv_event_eapol_rx(ctx, buf, buf + ETH_ALEN, len - ETH_ALEN);
+}
+
+
+static void wpa_driver_privsep_receive(int sock, void *eloop_ctx,
+				       void *sock_ctx)
+{
+	struct wpa_driver_privsep_data *drv = eloop_ctx;
+	u8 *buf, *event_buf;
+	size_t event_len;
+	int res, event;
+	enum privsep_event e;
+	struct sockaddr_un from;
+	socklen_t fromlen = sizeof(from);
+	const size_t buflen = 2000;
+
+	buf = os_malloc(buflen);
+	if (buf == NULL)
+		return;
+	res = recvfrom(sock, buf, buflen, 0,
+		       (struct sockaddr *) &from, &fromlen);
+	if (res < 0) {
+		wpa_printf(MSG_ERROR, "recvfrom(priv_socket): %s",
+			   strerror(errno));
+		os_free(buf);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "privsep_driver: received %u bytes", res);
+
+	if (res < (int) sizeof(int)) {
+		wpa_printf(MSG_DEBUG, "Too short event message (len=%d)", res);
+		return;
+	}
+
+	os_memcpy(&event, buf, sizeof(int));
+	event_buf = &buf[sizeof(int)];
+	event_len = res - sizeof(int);
+	wpa_printf(MSG_DEBUG, "privsep: Event %d received (len=%lu)",
+		   event, (unsigned long) event_len);
+
+	e = event;
+	switch (e) {
+	case PRIVSEP_EVENT_SCAN_RESULTS:
+		wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL);
+		break;
+	case PRIVSEP_EVENT_SCAN_STARTED:
+		wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, NULL);
+		break;
+	case PRIVSEP_EVENT_ASSOC:
+		wpa_driver_privsep_event_assoc(drv->ctx, EVENT_ASSOC,
+					       event_buf, event_len);
+		break;
+	case PRIVSEP_EVENT_DISASSOC:
+		wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+		break;
+	case PRIVSEP_EVENT_ASSOCINFO:
+		wpa_driver_privsep_event_assoc(drv->ctx, EVENT_ASSOCINFO,
+					       event_buf, event_len);
+		break;
+	case PRIVSEP_EVENT_MICHAEL_MIC_FAILURE:
+		wpa_driver_privsep_event_michael_mic_failure(
+			drv->ctx, event_buf, event_len);
+		break;
+	case PRIVSEP_EVENT_INTERFACE_STATUS:
+		wpa_driver_privsep_event_interface_status(drv->ctx, event_buf,
+							  event_len);
+		break;
+	case PRIVSEP_EVENT_PMKID_CANDIDATE:
+		wpa_driver_privsep_event_pmkid_candidate(drv->ctx, event_buf,
+							 event_len);
+		break;
+	case PRIVSEP_EVENT_FT_RESPONSE:
+		wpa_driver_privsep_event_ft_response(drv->ctx, event_buf,
+						     event_len);
+		break;
+	case PRIVSEP_EVENT_RX_EAPOL:
+		wpa_driver_privsep_event_rx_eapol(drv->ctx, event_buf,
+						  event_len);
+		break;
+	case PRIVSEP_EVENT_AUTH:
+		wpa_driver_privsep_event_auth(drv->ctx, event_buf, event_len);
+		break;
+	}
+
+	os_free(buf);
+}
+
+
+static void * wpa_driver_privsep_init(void *ctx, const char *ifname)
+{
+	struct wpa_driver_privsep_data *drv;
+
+	drv = os_zalloc(sizeof(*drv));
+	if (drv == NULL)
+		return NULL;
+	drv->ctx = ctx;
+	drv->priv_socket = -1;
+	drv->cmd_socket = -1;
+	os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+
+	return drv;
+}
+
+
+static void wpa_driver_privsep_deinit(void *priv)
+{
+	struct wpa_driver_privsep_data *drv = priv;
+
+	if (drv->priv_socket >= 0) {
+		wpa_priv_reg_cmd(drv, PRIVSEP_CMD_UNREGISTER);
+		eloop_unregister_read_sock(drv->priv_socket);
+		close(drv->priv_socket);
+	}
+
+	if (drv->own_socket_path) {
+		unlink(drv->own_socket_path);
+		os_free(drv->own_socket_path);
+	}
+
+	if (drv->cmd_socket >= 0) {
+		eloop_unregister_read_sock(drv->cmd_socket);
+		close(drv->cmd_socket);
+	}
+
+	if (drv->own_cmd_path) {
+		unlink(drv->own_cmd_path);
+		os_free(drv->own_cmd_path);
+	}
+
+	os_free(drv);
+}
+
+
+static int wpa_driver_privsep_set_param(void *priv, const char *param)
+{
+	struct wpa_driver_privsep_data *drv = priv;
+	const char *pos;
+	char *own_dir, *priv_dir;
+	static unsigned int counter = 0;
+	size_t len;
+	struct sockaddr_un addr;
+
+	wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param);
+	if (param == NULL)
+		pos = NULL;
+	else
+		pos = os_strstr(param, "own_dir=");
+	if (pos) {
+		char *end;
+		own_dir = os_strdup(pos + 8);
+		if (own_dir == NULL)
+			return -1;
+		end = os_strchr(own_dir, ' ');
+		if (end)
+			*end = '\0';
+	} else {
+		own_dir = os_strdup("/tmp");
+		if (own_dir == NULL)
+			return -1;
+	}
+
+	if (param == NULL)
+		pos = NULL;
+	else
+		pos = os_strstr(param, "priv_dir=");
+	if (pos) {
+		char *end;
+		priv_dir = os_strdup(pos + 9);
+		if (priv_dir == NULL) {
+			os_free(own_dir);
+			return -1;
+		}
+		end = os_strchr(priv_dir, ' ');
+		if (end)
+			*end = '\0';
+	} else {
+		priv_dir = os_strdup("/var/run/wpa_priv");
+		if (priv_dir == NULL) {
+			os_free(own_dir);
+			return -1;
+		}
+	}
+
+	len = os_strlen(own_dir) + 50;
+	drv->own_socket_path = os_malloc(len);
+	if (drv->own_socket_path == NULL) {
+		os_free(priv_dir);
+		os_free(own_dir);
+		return -1;
+	}
+	os_snprintf(drv->own_socket_path, len, "%s/wpa_privsep-%d-%d",
+		    own_dir, getpid(), counter++);
+
+	len = os_strlen(own_dir) + 50;
+	drv->own_cmd_path = os_malloc(len);
+	if (drv->own_cmd_path == NULL) {
+		os_free(drv->own_socket_path);
+		drv->own_socket_path = NULL;
+		os_free(priv_dir);
+		os_free(own_dir);
+		return -1;
+	}
+	os_snprintf(drv->own_cmd_path, len, "%s/wpa_privsep-%d-%d",
+		    own_dir, getpid(), counter++);
+
+	os_free(own_dir);
+
+	drv->priv_addr.sun_family = AF_UNIX;
+	os_snprintf(drv->priv_addr.sun_path, sizeof(drv->priv_addr.sun_path),
+		    "%s/%s", priv_dir, drv->ifname);
+	os_free(priv_dir);
+
+	drv->priv_socket = socket(PF_UNIX, SOCK_DGRAM, 0);
+	if (drv->priv_socket < 0) {
+		wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
+		os_free(drv->own_socket_path);
+		drv->own_socket_path = NULL;
+		return -1;
+	}
+
+	os_memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path));
+	if (bind(drv->priv_socket, (struct sockaddr *) &addr, sizeof(addr)) <
+	    0) {
+		wpa_printf(MSG_ERROR,
+			   "privsep-set-params priv-sock: bind(PF_UNIX): %s",
+			   strerror(errno));
+		close(drv->priv_socket);
+		drv->priv_socket = -1;
+		unlink(drv->own_socket_path);
+		os_free(drv->own_socket_path);
+		drv->own_socket_path = NULL;
+		return -1;
+	}
+
+	eloop_register_read_sock(drv->priv_socket, wpa_driver_privsep_receive,
+				 drv, NULL);
+
+	drv->cmd_socket = socket(PF_UNIX, SOCK_DGRAM, 0);
+	if (drv->cmd_socket < 0) {
+		wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
+		os_free(drv->own_cmd_path);
+		drv->own_cmd_path = NULL;
+		return -1;
+	}
+
+	os_memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	os_strlcpy(addr.sun_path, drv->own_cmd_path, sizeof(addr.sun_path));
+	if (bind(drv->cmd_socket, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+	{
+		wpa_printf(MSG_ERROR,
+			   "privsep-set-params cmd-sock: bind(PF_UNIX): %s",
+			   strerror(errno));
+		close(drv->cmd_socket);
+		drv->cmd_socket = -1;
+		unlink(drv->own_cmd_path);
+		os_free(drv->own_cmd_path);
+		drv->own_cmd_path = NULL;
+		return -1;
+	}
+
+	if (wpa_priv_reg_cmd(drv, PRIVSEP_CMD_REGISTER) < 0) {
+		wpa_printf(MSG_ERROR, "Failed to register with wpa_priv");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int wpa_driver_privsep_get_capa(void *priv,
+				       struct wpa_driver_capa *capa)
+{
+	struct wpa_driver_privsep_data *drv = priv;
+	int res;
+	size_t len = sizeof(*capa);
+
+	res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_CAPA, NULL, 0, capa, &len);
+	if (res < 0 || len != sizeof(*capa))
+		return -1;
+	/* For now, no support for passing extended_capa pointers */
+	capa->extended_capa = NULL;
+	capa->extended_capa_mask = NULL;
+	capa->extended_capa_len = 0;
+	/* Control port is not yet supported */
+	capa->flags &= ~WPA_DRIVER_FLAGS_CONTROL_PORT;
+	return 0;
+}
+
+
+static const u8 * wpa_driver_privsep_get_mac_addr(void *priv)
+{
+	struct wpa_driver_privsep_data *drv = priv;
+	wpa_printf(MSG_DEBUG, "%s", __func__);
+	return drv->own_addr;
+}
+
+
+static int wpa_driver_privsep_set_country(void *priv, const char *alpha2)
+{
+	struct wpa_driver_privsep_data *drv = priv;
+	wpa_printf(MSG_DEBUG, "%s country='%s'", __func__, alpha2);
+	return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_COUNTRY, alpha2,
+			    os_strlen(alpha2), NULL, NULL);
+}
+
+
+struct wpa_driver_ops wpa_driver_privsep_ops = {
+	"privsep",
+	"wpa_supplicant privilege separated driver",
+	.get_bssid = wpa_driver_privsep_get_bssid,
+	.get_ssid = wpa_driver_privsep_get_ssid,
+	.set_key = wpa_driver_privsep_set_key,
+	.init = wpa_driver_privsep_init,
+	.deinit = wpa_driver_privsep_deinit,
+	.set_param = wpa_driver_privsep_set_param,
+	.scan2 = wpa_driver_privsep_scan,
+	.deauthenticate = wpa_driver_privsep_deauthenticate,
+	.authenticate = wpa_driver_privsep_authenticate,
+	.associate = wpa_driver_privsep_associate,
+	.get_capa = wpa_driver_privsep_get_capa,
+	.get_mac_addr = wpa_driver_privsep_get_mac_addr,
+	.get_scan_results2 = wpa_driver_privsep_get_scan_results2,
+	.set_country = wpa_driver_privsep_set_country,
+};
+
+
+const struct wpa_driver_ops *const wpa_drivers[] =
+{
+	&wpa_driver_privsep_ops,
+	NULL
+};
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_roboswitch.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_roboswitch.c
new file mode 100644
index 0000000..9beb6c4
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_roboswitch.c
@@ -0,0 +1,487 @@
+/*
+ * WPA Supplicant - roboswitch driver interface
+ * Copyright (c) 2008-2012 Jouke Witteveen
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+#include <linux/sockios.h>
+#include <linux/if_ether.h>
+#include <linux/mii.h>
+#include <net/if.h>
+
+#include "common.h"
+#include "driver.h"
+#include "l2_packet/l2_packet.h"
+
+#define ROBO_PHY_ADDR		0x1e	/* RoboSwitch PHY address */
+
+/* MII access registers */
+#define ROBO_MII_PAGE		0x10	/* MII page register */
+#define ROBO_MII_ADDR		0x11	/* MII address register */
+#define ROBO_MII_DATA_OFFSET	0x18	/* Start of MII data registers */
+
+#define ROBO_MII_PAGE_ENABLE	0x01	/* MII page op code */
+#define ROBO_MII_ADDR_WRITE	0x01	/* MII address write op code */
+#define ROBO_MII_ADDR_READ	0x02	/* MII address read op code */
+#define ROBO_MII_DATA_MAX	   4	/* Consecutive MII data registers */
+#define ROBO_MII_RETRY_MAX	  10	/* Read attempts before giving up */
+
+/* Page numbers */
+#define ROBO_ARLCTRL_PAGE	0x04	/* ARL control page */
+#define ROBO_VLAN_PAGE		0x34	/* VLAN page */
+
+/* ARL control page registers */
+#define ROBO_ARLCTRL_CONF	0x00	/* ARL configuration register */
+#define ROBO_ARLCTRL_ADDR_1	0x10	/* Multiport address 1 */
+#define ROBO_ARLCTRL_VEC_1	0x16	/* Multiport vector 1 */
+#define ROBO_ARLCTRL_ADDR_2	0x20	/* Multiport address 2 */
+#define ROBO_ARLCTRL_VEC_2	0x26	/* Multiport vector 2 */
+
+/* VLAN page registers */
+#define ROBO_VLAN_ACCESS	0x08	/* VLAN table access register */
+#define ROBO_VLAN_ACCESS_5350	0x06	/* VLAN table access register (5350) */
+#define ROBO_VLAN_READ		0x0c	/* VLAN read register */
+#define ROBO_VLAN_MAX		0xff	/* Maximum number of VLANs */
+
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+
+struct wpa_driver_roboswitch_data {
+	void *ctx;
+	struct l2_packet_data *l2;
+	char ifname[IFNAMSIZ + 1];
+	u8 own_addr[ETH_ALEN];
+	struct ifreq ifr;
+	int fd, is_5350;
+	u16 ports;
+};
+
+
+/* Copied from the kernel-only part of mii.h. */
+static inline struct mii_ioctl_data *if_mii(struct ifreq *rq)
+{
+	return (struct mii_ioctl_data *) &rq->ifr_ifru;
+}
+
+
+/*
+ * RoboSwitch uses 16-bit Big Endian addresses.
+ * The ordering of the words is reversed in the MII registers.
+ */
+static void wpa_driver_roboswitch_addr_be16(const u8 addr[ETH_ALEN], u16 *be)
+{
+	int i;
+	for (i = 0; i < ETH_ALEN; i += 2)
+		be[(ETH_ALEN - i) / 2 - 1] = WPA_GET_BE16(addr + i);
+}
+
+
+static u16 wpa_driver_roboswitch_mdio_read(
+	struct wpa_driver_roboswitch_data *drv, u8 reg)
+{
+	struct mii_ioctl_data *mii = if_mii(&drv->ifr);
+
+	mii->phy_id = ROBO_PHY_ADDR;
+	mii->reg_num = reg;
+
+	if (ioctl(drv->fd, SIOCGMIIREG, &drv->ifr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGMIIREG]: %s",
+			   strerror(errno));
+		return 0x00;
+	}
+	return mii->val_out;
+}
+
+
+static void wpa_driver_roboswitch_mdio_write(
+	struct wpa_driver_roboswitch_data *drv, u8 reg, u16 val)
+{
+	struct mii_ioctl_data *mii = if_mii(&drv->ifr);
+
+	mii->phy_id = ROBO_PHY_ADDR;
+	mii->reg_num = reg;
+	mii->val_in = val;
+
+	if (ioctl(drv->fd, SIOCSMIIREG, &drv->ifr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSMIIREG]: %s",
+			   strerror(errno));
+	}
+}
+
+
+static int wpa_driver_roboswitch_reg(struct wpa_driver_roboswitch_data *drv,
+				     u8 page, u8 reg, u8 op)
+{
+	int i;
+
+	/* set page number */
+	wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_PAGE,
+					 (page << 8) | ROBO_MII_PAGE_ENABLE);
+	/* set register address */
+	wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_ADDR, (reg << 8) | op);
+
+	/* check if operation completed */
+	for (i = 0; i < ROBO_MII_RETRY_MAX; ++i) {
+		if ((wpa_driver_roboswitch_mdio_read(drv, ROBO_MII_ADDR) & 3)
+		    == 0)
+			return 0;
+	}
+	/* timeout */
+	return -1;
+}
+
+
+static int wpa_driver_roboswitch_read(struct wpa_driver_roboswitch_data *drv,
+				      u8 page, u8 reg, u16 *val, int len)
+{
+	int i;
+
+	if (len > ROBO_MII_DATA_MAX ||
+	    wpa_driver_roboswitch_reg(drv, page, reg, ROBO_MII_ADDR_READ) < 0)
+		return -1;
+
+	for (i = 0; i < len; ++i) {
+		val[i] = wpa_driver_roboswitch_mdio_read(
+			drv, ROBO_MII_DATA_OFFSET + i);
+	}
+
+	return 0;
+}
+
+
+static int wpa_driver_roboswitch_write(struct wpa_driver_roboswitch_data *drv,
+				       u8 page, u8 reg, u16 *val, int len)
+{
+	int i;
+
+	if (len > ROBO_MII_DATA_MAX) return -1;
+	for (i = 0; i < len; ++i) {
+		wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_DATA_OFFSET + i,
+						 val[i]);
+	}
+	return wpa_driver_roboswitch_reg(drv, page, reg, ROBO_MII_ADDR_WRITE);
+}
+
+
+static void wpa_driver_roboswitch_receive(void *priv, const u8 *src_addr,
+					  const u8 *buf, size_t len)
+{
+	struct wpa_driver_roboswitch_data *drv = priv;
+
+	if (len > 14 && WPA_GET_BE16(buf + 12) == ETH_P_EAPOL &&
+	    os_memcmp(buf, drv->own_addr, ETH_ALEN) == 0)
+		drv_event_eapol_rx(drv->ctx, src_addr, buf + 14, len - 14);
+}
+
+
+static int wpa_driver_roboswitch_get_ssid(void *priv, u8 *ssid)
+{
+	ssid[0] = 0;
+	return 0;
+}
+
+
+static int wpa_driver_roboswitch_get_bssid(void *priv, u8 *bssid)
+{
+	/* Report PAE group address as the "BSSID" for wired connection. */
+	os_memcpy(bssid, pae_group_addr, ETH_ALEN);
+	return 0;
+}
+
+
+static int wpa_driver_roboswitch_get_capa(void *priv,
+					  struct wpa_driver_capa *capa)
+{
+	os_memset(capa, 0, sizeof(*capa));
+	capa->flags = WPA_DRIVER_FLAGS_WIRED;
+	return 0;
+}
+
+
+static int wpa_driver_roboswitch_set_param(void *priv, const char *param)
+{
+	struct wpa_driver_roboswitch_data *drv = priv;
+	char *sep;
+
+	if (param == NULL || os_strstr(param, "multicast_only=1") == NULL) {
+		sep = drv->ifname + os_strlen(drv->ifname);
+		*sep = '.';
+		drv->l2 = l2_packet_init(drv->ifname, NULL, ETH_P_ALL,
+					 wpa_driver_roboswitch_receive, drv,
+					 1);
+		if (drv->l2 == NULL) {
+			wpa_printf(MSG_INFO, "%s: Unable to listen on %s",
+				   __func__, drv->ifname);
+			return -1;
+		}
+		*sep = '\0';
+		l2_packet_get_own_addr(drv->l2, drv->own_addr);
+	} else {
+		wpa_printf(MSG_DEBUG, "%s: Ignoring unicast frames", __func__);
+		drv->l2 = NULL;
+	}
+	return 0;
+}
+
+
+static const char * wpa_driver_roboswitch_get_ifname(void *priv)
+{
+	struct wpa_driver_roboswitch_data *drv = priv;
+	return drv->ifname;
+}
+
+
+static int wpa_driver_roboswitch_join(struct wpa_driver_roboswitch_data *drv,
+				      u16 ports, const u8 *addr)
+{
+	u16 read1[3], read2[3], addr_be16[3];
+
+	wpa_driver_roboswitch_addr_be16(addr, addr_be16);
+
+	if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+				       ROBO_ARLCTRL_CONF, read1, 1) < 0)
+		return -1;
+	if (!(read1[0] & (1 << 4))) {
+		/* multiport addresses are not yet enabled */
+		read1[0] |= 1 << 4;
+		wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+					    ROBO_ARLCTRL_ADDR_1, addr_be16, 3);
+		wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+					    ROBO_ARLCTRL_VEC_1, &ports, 1);
+		wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+					    ROBO_ARLCTRL_ADDR_2, addr_be16, 3);
+		wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+					    ROBO_ARLCTRL_VEC_2, &ports, 1);
+		wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+					    ROBO_ARLCTRL_CONF, read1, 1);
+	} else {
+		/* if both multiport addresses are the same we can add */
+		if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+					       ROBO_ARLCTRL_ADDR_1, read1, 3) ||
+		    wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+					       ROBO_ARLCTRL_ADDR_2, read2, 3) ||
+		    os_memcmp(read1, read2, 6) != 0)
+			return -1;
+		if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+					       ROBO_ARLCTRL_VEC_1, read1, 1) ||
+		    wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+					       ROBO_ARLCTRL_VEC_2, read2, 1) ||
+		    read1[0] != read2[0])
+			return -1;
+		wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+					    ROBO_ARLCTRL_ADDR_1, addr_be16, 3);
+		wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+					    ROBO_ARLCTRL_VEC_1, &ports, 1);
+	}
+	return 0;
+}
+
+
+static int wpa_driver_roboswitch_leave(struct wpa_driver_roboswitch_data *drv,
+				       u16 ports, const u8 *addr)
+{
+	u16 _read, addr_be16[3], addr_read[3], ports_read;
+
+	wpa_driver_roboswitch_addr_be16(addr, addr_be16);
+
+	if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+				       ROBO_ARLCTRL_CONF, &_read, 1) < 0)
+		return -1;
+	/* If ARL control is disabled, there is nothing to leave. */
+	if (!(_read & (1 << 4))) return -1;
+
+	if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+				       ROBO_ARLCTRL_ADDR_1, addr_read, 3) < 0 ||
+	    wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+				       ROBO_ARLCTRL_VEC_1, &ports_read, 1) < 0)
+		return -1;
+	/* check if we occupy multiport address 1 */
+	if (os_memcmp(addr_read, addr_be16, 6) == 0 && ports_read == ports) {
+		if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+					       ROBO_ARLCTRL_ADDR_2, addr_read,
+					       3) < 0 ||
+		    wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+					       ROBO_ARLCTRL_VEC_2, &ports_read,
+					       1) < 0)
+			return -1;
+		/* and multiport address 2 */
+		if (os_memcmp(addr_read, addr_be16, 6) == 0 &&
+		    ports_read == ports) {
+			_read &= ~(1 << 4);
+			wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+						    ROBO_ARLCTRL_CONF, &_read,
+						    1);
+		} else {
+			wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+						   ROBO_ARLCTRL_ADDR_1,
+						   addr_read, 3);
+			wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+						   ROBO_ARLCTRL_VEC_1,
+						   &ports_read, 1);
+			wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+						    ROBO_ARLCTRL_ADDR_2,
+						    addr_read, 3);
+			wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+						    ROBO_ARLCTRL_VEC_2,
+						    &ports_read, 1);
+		}
+	} else {
+		if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+					       ROBO_ARLCTRL_ADDR_2, addr_read,
+					       3) < 0 ||
+		    wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+					       ROBO_ARLCTRL_VEC_2, &ports_read,
+					       1) < 0)
+			return -1;
+		/* or multiport address 2 */
+		if (os_memcmp(addr_read, addr_be16, 6) == 0 &&
+		    ports_read == ports) {
+			wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+						    ROBO_ARLCTRL_ADDR_1,
+						    addr_read, 3);
+			wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+						    ROBO_ARLCTRL_VEC_1,
+						    &ports_read, 1);
+		} else return -1;
+	}
+	return 0;
+}
+
+
+static void * wpa_driver_roboswitch_init(void *ctx, const char *ifname)
+{
+	struct wpa_driver_roboswitch_data *drv;
+	char *sep;
+	u16 vlan = 0, _read[2];
+
+	drv = os_zalloc(sizeof(*drv));
+	if (drv == NULL) return NULL;
+	drv->ctx = ctx;
+	drv->own_addr[0] = '\0';
+
+	/* copy ifname and take a pointer to the second to last character */
+	sep = drv->ifname +
+	      os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)) - 2;
+	/* find the '.' separating <interface> and <vlan> */
+	while (sep > drv->ifname && *sep != '.') sep--;
+	if (sep <= drv->ifname) {
+		wpa_printf(MSG_INFO, "%s: No <interface>.<vlan> pair in "
+			   "interface name %s", __func__, drv->ifname);
+		os_free(drv);
+		return NULL;
+	}
+	*sep = '\0';
+	while (*++sep) {
+		if (*sep < '0' || *sep > '9') {
+			wpa_printf(MSG_INFO, "%s: Invalid vlan specification "
+				   "in interface name %s", __func__, ifname);
+			os_free(drv);
+			return NULL;
+		}
+		vlan *= 10;
+		vlan += *sep - '0';
+		if (vlan > ROBO_VLAN_MAX) {
+			wpa_printf(MSG_INFO, "%s: VLAN out of range in "
+				   "interface name %s", __func__, ifname);
+			os_free(drv);
+			return NULL;
+		}
+	}
+
+	drv->fd = socket(PF_INET, SOCK_DGRAM, 0);
+	if (drv->fd < 0) {
+		wpa_printf(MSG_INFO, "%s: Unable to create socket", __func__);
+		os_free(drv);
+		return NULL;
+	}
+
+	os_memset(&drv->ifr, 0, sizeof(drv->ifr));
+	os_strlcpy(drv->ifr.ifr_name, drv->ifname, IFNAMSIZ);
+	if (ioctl(drv->fd, SIOCGMIIPHY, &drv->ifr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGMIIPHY]: %s",
+			   strerror(errno));
+		os_free(drv);
+		return NULL;
+	}
+	/* BCM63xx devices provide 0 here */
+	if (if_mii(&drv->ifr)->phy_id != ROBO_PHY_ADDR &&
+	    if_mii(&drv->ifr)->phy_id != 0) {
+		wpa_printf(MSG_INFO, "%s: Invalid phy address (not a "
+			   "RoboSwitch?)", __func__);
+		os_free(drv);
+		return NULL;
+	}
+
+	/* set and read back to see if the register can be used */
+	_read[0] = ROBO_VLAN_MAX;
+	wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE, ROBO_VLAN_ACCESS_5350,
+				    _read, 1);
+	wpa_driver_roboswitch_read(drv, ROBO_VLAN_PAGE, ROBO_VLAN_ACCESS_5350,
+				   _read + 1, 1);
+	drv->is_5350 = _read[0] == _read[1];
+
+	/* set the read bit */
+	vlan |= 1 << 13;
+	wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE,
+				    drv->is_5350 ? ROBO_VLAN_ACCESS_5350
+						 : ROBO_VLAN_ACCESS,
+				    &vlan, 1);
+	wpa_driver_roboswitch_read(drv, ROBO_VLAN_PAGE, ROBO_VLAN_READ, _read,
+				   drv->is_5350 ? 2 : 1);
+	if (!(drv->is_5350 ? _read[1] & (1 << 4) : _read[0] & (1 << 14))) {
+		wpa_printf(MSG_INFO, "%s: Could not get port information for "
+				     "VLAN %d", __func__, vlan & ~(1 << 13));
+		os_free(drv);
+		return NULL;
+	}
+	drv->ports = _read[0] & 0x001F;
+	/* add the MII port */
+	drv->ports |= 1 << 8;
+	if (wpa_driver_roboswitch_join(drv, drv->ports, pae_group_addr) < 0) {
+		wpa_printf(MSG_INFO, "%s: Unable to join PAE group", __func__);
+		os_free(drv);
+		return NULL;
+	} else {
+		wpa_printf(MSG_DEBUG, "%s: Added PAE group address to "
+			   "RoboSwitch ARL", __func__);
+	}
+
+	return drv;
+}
+
+
+static void wpa_driver_roboswitch_deinit(void *priv)
+{
+	struct wpa_driver_roboswitch_data *drv = priv;
+
+	if (drv->l2) {
+		l2_packet_deinit(drv->l2);
+		drv->l2 = NULL;
+	}
+	if (wpa_driver_roboswitch_leave(drv, drv->ports, pae_group_addr) < 0) {
+		wpa_printf(MSG_DEBUG, "%s: Unable to leave PAE group",
+			   __func__);
+	}
+
+	close(drv->fd);
+	os_free(drv);
+}
+
+
+const struct wpa_driver_ops wpa_driver_roboswitch_ops = {
+	.name = "roboswitch",
+	.desc = "wpa_supplicant roboswitch driver",
+	.get_ssid = wpa_driver_roboswitch_get_ssid,
+	.get_bssid = wpa_driver_roboswitch_get_bssid,
+	.get_capa = wpa_driver_roboswitch_get_capa,
+	.init = wpa_driver_roboswitch_init,
+	.deinit = wpa_driver_roboswitch_deinit,
+	.set_param = wpa_driver_roboswitch_set_param,
+	.get_ifname = wpa_driver_roboswitch_get_ifname,
+};
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_wext.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_wext.c
new file mode 100644
index 0000000..cf201fe
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_wext.c
@@ -0,0 +1,2499 @@
+/*
+ * Driver interaction with generic Linux Wireless Extensions
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file implements a driver interface for the Linux Wireless Extensions.
+ * When used with WE-18 or newer, this interface can be used as-is with number
+ * of drivers. In addition to this, some of the common functions in this file
+ * can be used by other driver interface implementations that use generic WE
+ * ioctls, but require private ioctls for some of the functionality.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <net/if_arp.h>
+#include <dirent.h>
+
+#include "linux_wext.h"
+#include "common.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_common.h"
+#include "priv_netlink.h"
+#include "netlink.h"
+#include "linux_ioctl.h"
+#include "rfkill.h"
+#include "driver.h"
+#include "driver_wext.h"
+
+static int wpa_driver_wext_flush_pmkid(void *priv);
+static int wpa_driver_wext_get_range(void *priv);
+static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv);
+static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv);
+static int wpa_driver_wext_set_auth_alg(void *priv, int auth_alg);
+
+
+int wpa_driver_wext_set_auth_param(struct wpa_driver_wext_data *drv,
+				   int idx, u32 value)
+{
+	struct iwreq iwr;
+	int ret = 0;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+	iwr.u.param.flags = idx & IW_AUTH_INDEX;
+	iwr.u.param.value = value;
+
+	if (ioctl(drv->ioctl_sock, SIOCSIWAUTH, &iwr) < 0) {
+		if (errno != EOPNOTSUPP) {
+			wpa_printf(MSG_DEBUG, "WEXT: SIOCSIWAUTH(param %d "
+				   "value 0x%x) failed: %s)",
+				   idx, value, strerror(errno));
+		}
+		ret = errno == EOPNOTSUPP ? -2 : -1;
+	}
+
+	return ret;
+}
+
+
+/**
+ * wpa_driver_wext_get_bssid - Get BSSID, SIOCGIWAP
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @bssid: Buffer for BSSID
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_wext_get_bssid(void *priv, u8 *bssid)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	struct iwreq iwr;
+	int ret = 0;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+
+	if (ioctl(drv->ioctl_sock, SIOCGIWAP, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIWAP]: %s", strerror(errno));
+		ret = -1;
+	}
+	os_memcpy(bssid, iwr.u.ap_addr.sa_data, ETH_ALEN);
+
+	return ret;
+}
+
+
+/**
+ * wpa_driver_wext_set_bssid - Set BSSID, SIOCSIWAP
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @bssid: BSSID
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_wext_set_bssid(void *priv, const u8 *bssid)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	struct iwreq iwr;
+	int ret = 0;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+	iwr.u.ap_addr.sa_family = ARPHRD_ETHER;
+	if (bssid)
+		os_memcpy(iwr.u.ap_addr.sa_data, bssid, ETH_ALEN);
+	else
+		os_memset(iwr.u.ap_addr.sa_data, 0, ETH_ALEN);
+
+	if (ioctl(drv->ioctl_sock, SIOCSIWAP, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWAP]: %s", strerror(errno));
+		ret = -1;
+	}
+
+	return ret;
+}
+
+
+/**
+ * wpa_driver_wext_get_ssid - Get SSID, SIOCGIWESSID
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @ssid: Buffer for the SSID; must be at least 32 bytes long
+ * Returns: SSID length on success, -1 on failure
+ */
+int wpa_driver_wext_get_ssid(void *priv, u8 *ssid)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	struct iwreq iwr;
+	int ret = 0;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+	iwr.u.essid.pointer = (caddr_t) ssid;
+	iwr.u.essid.length = SSID_MAX_LEN;
+
+	if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIWESSID]: %s",
+			   strerror(errno));
+		ret = -1;
+	} else {
+		ret = iwr.u.essid.length;
+		if (ret > SSID_MAX_LEN)
+			ret = SSID_MAX_LEN;
+		/* Some drivers include nul termination in the SSID, so let's
+		 * remove it here before further processing. WE-21 changes this
+		 * to explicitly require the length _not_ to include nul
+		 * termination. */
+		if (ret > 0 && ssid[ret - 1] == '\0' &&
+		    drv->we_version_compiled < 21)
+			ret--;
+	}
+
+	return ret;
+}
+
+
+/**
+ * wpa_driver_wext_set_ssid - Set SSID, SIOCSIWESSID
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @ssid: SSID
+ * @ssid_len: Length of SSID (0..32)
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_wext_set_ssid(void *priv, const u8 *ssid, size_t ssid_len)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	struct iwreq iwr;
+	int ret = 0;
+	char buf[33];
+
+	if (ssid_len > SSID_MAX_LEN)
+		return -1;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+	/* flags: 1 = ESSID is active, 0 = not (promiscuous) */
+	iwr.u.essid.flags = (ssid_len != 0);
+	os_memset(buf, 0, sizeof(buf));
+	os_memcpy(buf, ssid, ssid_len);
+	iwr.u.essid.pointer = (caddr_t) buf;
+	if (drv->we_version_compiled < 21) {
+		/* For historic reasons, set SSID length to include one extra
+		 * character, C string nul termination, even though SSID is
+		 * really an octet string that should not be presented as a C
+		 * string. Some Linux drivers decrement the length by one and
+		 * can thus end up missing the last octet of the SSID if the
+		 * length is not incremented here. WE-21 changes this to
+		 * explicitly require the length _not_ to include nul
+		 * termination. */
+		if (ssid_len)
+			ssid_len++;
+	}
+	iwr.u.essid.length = ssid_len;
+
+	if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID]: %s",
+			   strerror(errno));
+		ret = -1;
+	}
+
+	return ret;
+}
+
+
+/**
+ * wpa_driver_wext_set_freq - Set frequency/channel, SIOCSIWFREQ
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @freq: Frequency in MHz
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_wext_set_freq(void *priv, int freq)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	struct iwreq iwr;
+	int ret = 0;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+	iwr.u.freq.m = freq * 100000;
+	iwr.u.freq.e = 1;
+
+	if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWFREQ]: %s",
+			   strerror(errno));
+		ret = -1;
+	}
+
+	return ret;
+}
+
+
+static void
+wpa_driver_wext_event_wireless_custom(void *ctx, char *custom)
+{
+	union wpa_event_data data;
+
+	wpa_printf(MSG_MSGDUMP, "WEXT: Custom wireless event: '%s'",
+		   custom);
+
+	os_memset(&data, 0, sizeof(data));
+	/* Host AP driver */
+	if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) {
+		data.michael_mic_failure.unicast =
+			os_strstr(custom, " unicast ") != NULL;
+		/* TODO: parse parameters(?) */
+		wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+	} else if (os_strncmp(custom, "ASSOCINFO(ReqIEs=", 17) == 0) {
+		char *spos;
+		int bytes;
+		u8 *req_ies = NULL, *resp_ies = NULL;
+
+		spos = custom + 17;
+
+		bytes = strspn(spos, "0123456789abcdefABCDEF");
+		if (!bytes || (bytes & 1))
+			return;
+		bytes /= 2;
+
+		req_ies = os_malloc(bytes);
+		if (req_ies == NULL ||
+		    hexstr2bin(spos, req_ies, bytes) < 0)
+			goto done;
+		data.assoc_info.req_ies = req_ies;
+		data.assoc_info.req_ies_len = bytes;
+
+		spos += bytes * 2;
+
+		data.assoc_info.resp_ies = NULL;
+		data.assoc_info.resp_ies_len = 0;
+
+		if (os_strncmp(spos, " RespIEs=", 9) == 0) {
+			spos += 9;
+
+			bytes = strspn(spos, "0123456789abcdefABCDEF");
+			if (!bytes || (bytes & 1))
+				goto done;
+			bytes /= 2;
+
+			resp_ies = os_malloc(bytes);
+			if (resp_ies == NULL ||
+			    hexstr2bin(spos, resp_ies, bytes) < 0)
+				goto done;
+			data.assoc_info.resp_ies = resp_ies;
+			data.assoc_info.resp_ies_len = bytes;
+		}
+
+		wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data);
+
+	done:
+		os_free(resp_ies);
+		os_free(req_ies);
+	}
+}
+
+
+static int wpa_driver_wext_event_wireless_michaelmicfailure(
+	void *ctx, const char *ev, size_t len)
+{
+	const struct iw_michaelmicfailure *mic;
+	union wpa_event_data data;
+
+	if (len < sizeof(*mic))
+		return -1;
+
+	mic = (const struct iw_michaelmicfailure *) ev;
+
+	wpa_printf(MSG_DEBUG, "Michael MIC failure wireless event: "
+		   "flags=0x%x src_addr=" MACSTR, mic->flags,
+		   MAC2STR(mic->src_addr.sa_data));
+
+	os_memset(&data, 0, sizeof(data));
+	data.michael_mic_failure.unicast = !(mic->flags & IW_MICFAILURE_GROUP);
+	wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+
+	return 0;
+}
+
+
+static int wpa_driver_wext_event_wireless_pmkidcand(
+	struct wpa_driver_wext_data *drv, const char *ev, size_t len)
+{
+	const struct iw_pmkid_cand *cand;
+	union wpa_event_data data;
+	const u8 *addr;
+
+	if (len < sizeof(*cand))
+		return -1;
+
+	cand = (const struct iw_pmkid_cand *) ev;
+	addr = (const u8 *) cand->bssid.sa_data;
+
+	wpa_printf(MSG_DEBUG, "PMKID candidate wireless event: "
+		   "flags=0x%x index=%d bssid=" MACSTR, cand->flags,
+		   cand->index, MAC2STR(addr));
+
+	os_memset(&data, 0, sizeof(data));
+	os_memcpy(data.pmkid_candidate.bssid, addr, ETH_ALEN);
+	data.pmkid_candidate.index = cand->index;
+	data.pmkid_candidate.preauth = cand->flags & IW_PMKID_CAND_PREAUTH;
+	wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data);
+
+	return 0;
+}
+
+
+static int wpa_driver_wext_event_wireless_assocreqie(
+	struct wpa_driver_wext_data *drv, const char *ev, int len)
+{
+	if (len < 0)
+		return -1;
+
+	wpa_hexdump(MSG_DEBUG, "AssocReq IE wireless event", (const u8 *) ev,
+		    len);
+	os_free(drv->assoc_req_ies);
+	drv->assoc_req_ies = os_memdup(ev, len);
+	if (drv->assoc_req_ies == NULL) {
+		drv->assoc_req_ies_len = 0;
+		return -1;
+	}
+	drv->assoc_req_ies_len = len;
+
+	return 0;
+}
+
+
+static int wpa_driver_wext_event_wireless_assocrespie(
+	struct wpa_driver_wext_data *drv, const char *ev, int len)
+{
+	if (len < 0)
+		return -1;
+
+	wpa_hexdump(MSG_DEBUG, "AssocResp IE wireless event", (const u8 *) ev,
+		    len);
+	os_free(drv->assoc_resp_ies);
+	drv->assoc_resp_ies = os_memdup(ev, len);
+	if (drv->assoc_resp_ies == NULL) {
+		drv->assoc_resp_ies_len = 0;
+		return -1;
+	}
+	drv->assoc_resp_ies_len = len;
+
+	return 0;
+}
+
+
+static void wpa_driver_wext_event_assoc_ies(struct wpa_driver_wext_data *drv)
+{
+	union wpa_event_data data;
+
+	if (drv->assoc_req_ies == NULL && drv->assoc_resp_ies == NULL)
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	if (drv->assoc_req_ies) {
+		data.assoc_info.req_ies = drv->assoc_req_ies;
+		data.assoc_info.req_ies_len = drv->assoc_req_ies_len;
+	}
+	if (drv->assoc_resp_ies) {
+		data.assoc_info.resp_ies = drv->assoc_resp_ies;
+		data.assoc_info.resp_ies_len = drv->assoc_resp_ies_len;
+	}
+
+	wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data);
+
+	os_free(drv->assoc_req_ies);
+	drv->assoc_req_ies = NULL;
+	os_free(drv->assoc_resp_ies);
+	drv->assoc_resp_ies = NULL;
+}
+
+
+static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv,
+					   char *data, unsigned int len)
+{
+	struct iw_event iwe_buf, *iwe = &iwe_buf;
+	char *pos, *end, *custom, *buf;
+
+	pos = data;
+	end = data + len;
+
+	while ((size_t) (end - pos) >= IW_EV_LCP_LEN) {
+		/* Event data may be unaligned, so make a local, aligned copy
+		 * before processing. */
+		os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
+		wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d",
+			   iwe->cmd, iwe->len);
+		if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos)
+			return;
+
+		custom = pos + IW_EV_POINT_LEN;
+		if (drv->we_version_compiled > 18 &&
+		    (iwe->cmd == IWEVMICHAELMICFAILURE ||
+		     iwe->cmd == IWEVCUSTOM ||
+		     iwe->cmd == IWEVASSOCREQIE ||
+		     iwe->cmd == IWEVASSOCRESPIE ||
+		     iwe->cmd == IWEVPMKIDCAND)) {
+			/* WE-19 removed the pointer from struct iw_point */
+			char *dpos = (char *) &iwe_buf.u.data.length;
+			int dlen = dpos - (char *) &iwe_buf;
+			os_memcpy(dpos, pos + IW_EV_LCP_LEN,
+				  sizeof(struct iw_event) - dlen);
+		} else {
+			os_memcpy(&iwe_buf, pos, sizeof(struct iw_event));
+			custom += IW_EV_POINT_OFF;
+		}
+
+		switch (iwe->cmd) {
+		case SIOCGIWAP:
+			wpa_printf(MSG_DEBUG, "Wireless event: new AP: "
+				   MACSTR,
+				   MAC2STR((u8 *) iwe->u.ap_addr.sa_data));
+			if (is_zero_ether_addr(
+				    (const u8 *) iwe->u.ap_addr.sa_data) ||
+			    os_memcmp(iwe->u.ap_addr.sa_data,
+				      "\x44\x44\x44\x44\x44\x44", ETH_ALEN) ==
+			    0) {
+				os_free(drv->assoc_req_ies);
+				drv->assoc_req_ies = NULL;
+				os_free(drv->assoc_resp_ies);
+				drv->assoc_resp_ies = NULL;
+				wpa_supplicant_event(drv->ctx, EVENT_DISASSOC,
+						     NULL);
+
+			} else {
+				wpa_driver_wext_event_assoc_ies(drv);
+				wpa_supplicant_event(drv->ctx, EVENT_ASSOC,
+						     NULL);
+			}
+			break;
+		case IWEVMICHAELMICFAILURE:
+			if (iwe->u.data.length > end - custom) {
+				wpa_printf(MSG_DEBUG, "WEXT: Invalid "
+					   "IWEVMICHAELMICFAILURE length");
+				return;
+			}
+			wpa_driver_wext_event_wireless_michaelmicfailure(
+				drv->ctx, custom, iwe->u.data.length);
+			break;
+		case IWEVCUSTOM:
+			if (iwe->u.data.length > end - custom) {
+				wpa_printf(MSG_DEBUG, "WEXT: Invalid "
+					   "IWEVCUSTOM length");
+				return;
+			}
+			buf = dup_binstr(custom, iwe->u.data.length);
+			if (buf == NULL)
+				return;
+			wpa_driver_wext_event_wireless_custom(drv->ctx, buf);
+			os_free(buf);
+			break;
+		case SIOCGIWSCAN:
+			drv->scan_complete_events = 1;
+			eloop_cancel_timeout(wpa_driver_wext_scan_timeout,
+					     drv, drv->ctx);
+			wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS,
+					     NULL);
+			break;
+		case IWEVASSOCREQIE:
+			if (iwe->u.data.length > end - custom) {
+				wpa_printf(MSG_DEBUG, "WEXT: Invalid "
+					   "IWEVASSOCREQIE length");
+				return;
+			}
+			wpa_driver_wext_event_wireless_assocreqie(
+				drv, custom, iwe->u.data.length);
+			break;
+		case IWEVASSOCRESPIE:
+			if (iwe->u.data.length > end - custom) {
+				wpa_printf(MSG_DEBUG, "WEXT: Invalid "
+					   "IWEVASSOCRESPIE length");
+				return;
+			}
+			wpa_driver_wext_event_wireless_assocrespie(
+				drv, custom, iwe->u.data.length);
+			break;
+		case IWEVPMKIDCAND:
+			if (iwe->u.data.length > end - custom) {
+				wpa_printf(MSG_DEBUG, "WEXT: Invalid "
+					   "IWEVPMKIDCAND length");
+				return;
+			}
+			wpa_driver_wext_event_wireless_pmkidcand(
+				drv, custom, iwe->u.data.length);
+			break;
+		}
+
+		pos += iwe->len;
+	}
+}
+
+
+static void wpa_driver_wext_event_link(struct wpa_driver_wext_data *drv,
+				       char *buf, size_t len, int del)
+{
+	union wpa_event_data event;
+
+	os_memset(&event, 0, sizeof(event));
+	if (len > sizeof(event.interface_status.ifname))
+		len = sizeof(event.interface_status.ifname) - 1;
+	os_memcpy(event.interface_status.ifname, buf, len);
+	event.interface_status.ievent = del ? EVENT_INTERFACE_REMOVED :
+		EVENT_INTERFACE_ADDED;
+
+	wpa_printf(MSG_DEBUG, "RTM_%sLINK, IFLA_IFNAME: Interface '%s' %s",
+		   del ? "DEL" : "NEW",
+		   event.interface_status.ifname,
+		   del ? "removed" : "added");
+
+	if (os_strcmp(drv->ifname, event.interface_status.ifname) == 0) {
+		if (del) {
+			if (drv->if_removed) {
+				wpa_printf(MSG_DEBUG, "WEXT: if_removed "
+					   "already set - ignore event");
+				return;
+			}
+			drv->if_removed = 1;
+		} else {
+			if (if_nametoindex(drv->ifname) == 0) {
+				wpa_printf(MSG_DEBUG, "WEXT: Interface %s "
+					   "does not exist - ignore "
+					   "RTM_NEWLINK",
+					   drv->ifname);
+				return;
+			}
+			if (!drv->if_removed) {
+				wpa_printf(MSG_DEBUG, "WEXT: if_removed "
+					   "already cleared - ignore event");
+				return;
+			}
+			drv->if_removed = 0;
+		}
+	}
+
+	wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+}
+
+
+static int wpa_driver_wext_own_ifname(struct wpa_driver_wext_data *drv,
+				      u8 *buf, size_t len)
+{
+	int attrlen, rta_len;
+	struct rtattr *attr;
+
+	attrlen = len;
+	attr = (struct rtattr *) buf;
+
+	rta_len = RTA_ALIGN(sizeof(struct rtattr));
+	while (RTA_OK(attr, attrlen)) {
+		if (attr->rta_type == IFLA_IFNAME) {
+			if (os_strcmp(((char *) attr) + rta_len, drv->ifname)
+			    == 0)
+				return 1;
+			else
+				break;
+		}
+		attr = RTA_NEXT(attr, attrlen);
+	}
+
+	return 0;
+}
+
+
+static int wpa_driver_wext_own_ifindex(struct wpa_driver_wext_data *drv,
+				       int ifindex, u8 *buf, size_t len)
+{
+	if (drv->ifindex == ifindex || drv->ifindex2 == ifindex)
+		return 1;
+
+	if (drv->if_removed && wpa_driver_wext_own_ifname(drv, buf, len)) {
+		drv->ifindex = if_nametoindex(drv->ifname);
+		wpa_printf(MSG_DEBUG, "WEXT: Update ifindex for a removed "
+			   "interface");
+		wpa_driver_wext_finish_drv_init(drv);
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static void wpa_driver_wext_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi,
+					      u8 *buf, size_t len)
+{
+	struct wpa_driver_wext_data *drv = ctx;
+	int attrlen, rta_len;
+	struct rtattr *attr;
+	char namebuf[IFNAMSIZ];
+
+	if (!wpa_driver_wext_own_ifindex(drv, ifi->ifi_index, buf, len)) {
+		wpa_printf(MSG_DEBUG, "Ignore event for foreign ifindex %d",
+			   ifi->ifi_index);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "RTM_NEWLINK: operstate=%d ifi_flags=0x%x "
+		   "(%s%s%s%s)",
+		   drv->operstate, ifi->ifi_flags,
+		   (ifi->ifi_flags & IFF_UP) ? "[UP]" : "",
+		   (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
+		   (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
+		   (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
+
+	if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) {
+		wpa_printf(MSG_DEBUG, "WEXT: Interface down");
+		drv->if_disabled = 1;
+		wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, NULL);
+	}
+
+	if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) {
+		if (if_indextoname(ifi->ifi_index, namebuf) &&
+		    linux_iface_up(drv->ioctl_sock, drv->ifname) == 0) {
+			wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up "
+				   "event since interface %s is down",
+				   namebuf);
+		} else if (if_nametoindex(drv->ifname) == 0) {
+			wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up "
+				   "event since interface %s does not exist",
+				   drv->ifname);
+		} else if (drv->if_removed) {
+			wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up "
+				   "event since interface %s is marked "
+				   "removed", drv->ifname);
+		} else {
+			wpa_printf(MSG_DEBUG, "WEXT: Interface up");
+			drv->if_disabled = 0;
+			wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED,
+					     NULL);
+		}
+	}
+
+	/*
+	 * Some drivers send the association event before the operup event--in
+	 * this case, lifting operstate in wpa_driver_wext_set_operstate()
+	 * fails. This will hit us when wpa_supplicant does not need to do
+	 * IEEE 802.1X authentication
+	 */
+	if (drv->operstate == 1 &&
+	    (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP &&
+	    !(ifi->ifi_flags & IFF_RUNNING))
+		netlink_send_oper_ifla(drv->netlink, drv->ifindex,
+				       -1, IF_OPER_UP);
+
+	attrlen = len;
+	attr = (struct rtattr *) buf;
+
+	rta_len = RTA_ALIGN(sizeof(struct rtattr));
+	while (RTA_OK(attr, attrlen)) {
+		if (attr->rta_type == IFLA_WIRELESS) {
+			wpa_driver_wext_event_wireless(
+				drv, ((char *) attr) + rta_len,
+				attr->rta_len - rta_len);
+		} else if (attr->rta_type == IFLA_IFNAME) {
+			wpa_driver_wext_event_link(drv,
+						   ((char *) attr) + rta_len,
+						   attr->rta_len - rta_len, 0);
+		}
+		attr = RTA_NEXT(attr, attrlen);
+	}
+}
+
+
+static void wpa_driver_wext_event_rtm_dellink(void *ctx, struct ifinfomsg *ifi,
+					      u8 *buf, size_t len)
+{
+	struct wpa_driver_wext_data *drv = ctx;
+	int attrlen, rta_len;
+	struct rtattr *attr;
+
+	attrlen = len;
+	attr = (struct rtattr *) buf;
+
+	rta_len = RTA_ALIGN(sizeof(struct rtattr));
+	while (RTA_OK(attr, attrlen)) {
+		if (attr->rta_type == IFLA_IFNAME) {
+			wpa_driver_wext_event_link(drv,
+						   ((char *) attr) + rta_len,
+						   attr->rta_len - rta_len, 1);
+		}
+		attr = RTA_NEXT(attr, attrlen);
+	}
+}
+
+
+static void wpa_driver_wext_rfkill_blocked(void *ctx)
+{
+	wpa_printf(MSG_DEBUG, "WEXT: RFKILL blocked");
+	/*
+	 * This may be for any interface; use ifdown event to disable
+	 * interface.
+	 */
+}
+
+
+static void wpa_driver_wext_rfkill_unblocked(void *ctx)
+{
+	struct wpa_driver_wext_data *drv = ctx;
+	wpa_printf(MSG_DEBUG, "WEXT: RFKILL unblocked");
+	if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1)) {
+		wpa_printf(MSG_DEBUG, "WEXT: Could not set interface UP "
+			   "after rfkill unblock");
+		return;
+	}
+	/* rtnetlink ifup handler will report interface as enabled */
+}
+
+
+static void wext_get_phy_name(struct wpa_driver_wext_data *drv)
+{
+	/* Find phy (radio) to which this interface belongs */
+	char buf[90], *pos;
+	int f, rv;
+
+	drv->phyname[0] = '\0';
+	snprintf(buf, sizeof(buf) - 1, "/sys/class/net/%s/phy80211/name",
+		 drv->ifname);
+	f = open(buf, O_RDONLY);
+	if (f < 0) {
+		wpa_printf(MSG_DEBUG, "Could not open file %s: %s",
+			   buf, strerror(errno));
+		return;
+	}
+
+	rv = read(f, drv->phyname, sizeof(drv->phyname) - 1);
+	close(f);
+	if (rv < 0) {
+		wpa_printf(MSG_DEBUG, "Could not read file %s: %s",
+			   buf, strerror(errno));
+		return;
+	}
+
+	drv->phyname[rv] = '\0';
+	pos = os_strchr(drv->phyname, '\n');
+	if (pos)
+		*pos = '\0';
+	wpa_printf(MSG_DEBUG, "wext: interface %s phy: %s",
+		   drv->ifname, drv->phyname);
+}
+
+
+/**
+ * wpa_driver_wext_init - Initialize WE driver interface
+ * @ctx: context to be used when calling wpa_supplicant functions,
+ * e.g., wpa_supplicant_event()
+ * @ifname: interface name, e.g., wlan0
+ * Returns: Pointer to private data, %NULL on failure
+ */
+void * wpa_driver_wext_init(void *ctx, const char *ifname)
+{
+	struct wpa_driver_wext_data *drv;
+	struct netlink_config *cfg;
+	struct rfkill_config *rcfg;
+	char path[128];
+	struct stat buf;
+
+	drv = os_zalloc(sizeof(*drv));
+	if (drv == NULL)
+		return NULL;
+	drv->ctx = ctx;
+	os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+
+	os_snprintf(path, sizeof(path), "/sys/class/net/%s/phy80211", ifname);
+	if (stat(path, &buf) == 0) {
+		wpa_printf(MSG_DEBUG, "WEXT: cfg80211-based driver detected");
+		drv->cfg80211 = 1;
+		wext_get_phy_name(drv);
+	}
+
+	drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
+	if (drv->ioctl_sock < 0) {
+		wpa_printf(MSG_ERROR, "socket(PF_INET,SOCK_DGRAM): %s",
+			   strerror(errno));
+		goto err1;
+	}
+
+	cfg = os_zalloc(sizeof(*cfg));
+	if (cfg == NULL)
+		goto err1;
+	cfg->ctx = drv;
+	cfg->newlink_cb = wpa_driver_wext_event_rtm_newlink;
+	cfg->dellink_cb = wpa_driver_wext_event_rtm_dellink;
+	drv->netlink = netlink_init(cfg);
+	if (drv->netlink == NULL) {
+		os_free(cfg);
+		goto err2;
+	}
+
+	rcfg = os_zalloc(sizeof(*rcfg));
+	if (rcfg == NULL)
+		goto err3;
+	rcfg->ctx = drv;
+	os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname));
+	rcfg->blocked_cb = wpa_driver_wext_rfkill_blocked;
+	rcfg->unblocked_cb = wpa_driver_wext_rfkill_unblocked;
+	drv->rfkill = rfkill_init(rcfg);
+	if (drv->rfkill == NULL) {
+		wpa_printf(MSG_DEBUG, "WEXT: RFKILL status not available");
+		os_free(rcfg);
+	}
+
+	drv->mlme_sock = -1;
+
+	if (wpa_driver_wext_finish_drv_init(drv) < 0)
+		goto err3;
+
+	wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_ENABLED, 1);
+
+	return drv;
+
+err3:
+	rfkill_deinit(drv->rfkill);
+	netlink_deinit(drv->netlink);
+err2:
+	close(drv->ioctl_sock);
+err1:
+	os_free(drv);
+	return NULL;
+}
+
+
+static void wpa_driver_wext_send_rfkill(void *eloop_ctx, void *timeout_ctx)
+{
+	wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL);
+}
+
+
+static int wext_hostap_ifname(struct wpa_driver_wext_data *drv,
+			      const char *ifname)
+{
+	char buf[200], *res;
+	int type, ret;
+	FILE *f;
+
+	if (strcmp(ifname, ".") == 0 || strcmp(ifname, "..") == 0)
+		return -1;
+
+	ret = snprintf(buf, sizeof(buf), "/sys/class/net/%s/device/net/%s/type",
+		       drv->ifname, ifname);
+	if (os_snprintf_error(sizeof(buf), ret))
+		return -1;
+
+	f = fopen(buf, "r");
+	if (!f)
+		return -1;
+	res = fgets(buf, sizeof(buf), f);
+	fclose(f);
+
+	type = res ? atoi(res) : -1;
+	wpa_printf(MSG_DEBUG, "WEXT: hostap ifname %s type %d", ifname, type);
+
+	if (type == ARPHRD_IEEE80211) {
+		wpa_printf(MSG_DEBUG,
+			   "WEXT: Found hostap driver wifi# interface (%s)",
+			   ifname);
+		wpa_driver_wext_alternative_ifindex(drv, ifname);
+		return 0;
+	}
+	return -1;
+}
+
+
+static int wext_add_hostap(struct wpa_driver_wext_data *drv)
+{
+	char buf[200];
+	int n;
+	struct dirent **names;
+	int ret = -1;
+
+	snprintf(buf, sizeof(buf), "/sys/class/net/%s/device/net", drv->ifname);
+	n = scandir(buf, &names, NULL, alphasort);
+	if (n < 0)
+		return -1;
+
+	while (n--) {
+		if (ret < 0 && wext_hostap_ifname(drv, names[n]->d_name) == 0)
+			ret = 0;
+		free(names[n]);
+	}
+	free(names);
+
+	return ret;
+}
+
+
+static void wext_check_hostap(struct wpa_driver_wext_data *drv)
+{
+	char path[200], buf[200], *pos;
+	ssize_t res;
+
+	/*
+	 * Host AP driver may use both wlan# and wifi# interface in wireless
+	 * events. Since some of the versions included WE-18 support, let's add
+	 * the alternative ifindex also from driver_wext.c for the time being.
+	 * This may be removed at some point once it is believed that old
+	 * versions of the driver are not in use anymore. However, it looks like
+	 * the wifi# interface is still used in the current kernel tree, so it
+	 * may not really be possible to remove this before the Host AP driver
+	 * gets removed from the kernel.
+	 */
+
+	/* First, try to see if driver information is available from sysfs */
+	snprintf(path, sizeof(path), "/sys/class/net/%s/device/driver",
+		 drv->ifname);
+	res = readlink(path, buf, sizeof(buf) - 1);
+	if (res > 0) {
+		buf[res] = '\0';
+		pos = strrchr(buf, '/');
+		if (pos)
+			pos++;
+		else
+			pos = buf;
+		wpa_printf(MSG_DEBUG, "WEXT: Driver: %s", pos);
+		if (os_strncmp(pos, "hostap", 6) == 0 &&
+		    wext_add_hostap(drv) == 0)
+			return;
+	}
+
+	/* Second, use the old design with hardcoded ifname */
+	if (os_strncmp(drv->ifname, "wlan", 4) == 0) {
+		char ifname2[IFNAMSIZ + 1];
+		os_strlcpy(ifname2, drv->ifname, sizeof(ifname2));
+		os_memcpy(ifname2, "wifi", 4);
+		wpa_driver_wext_alternative_ifindex(drv, ifname2);
+	}
+}
+
+
+static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv)
+{
+	int send_rfkill_event = 0;
+	int i;
+
+	if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1) < 0) {
+		if (rfkill_is_blocked(drv->rfkill)) {
+			wpa_printf(MSG_DEBUG, "WEXT: Could not yet enable "
+				   "interface '%s' due to rfkill",
+				   drv->ifname);
+			drv->if_disabled = 1;
+			send_rfkill_event = 1;
+		} else {
+			wpa_printf(MSG_ERROR, "WEXT: Could not set "
+				   "interface '%s' UP", drv->ifname);
+			return -1;
+		}
+	}
+
+	/*
+	 * Make sure that the driver does not have any obsolete PMKID entries.
+	 */
+	wpa_driver_wext_flush_pmkid(drv);
+
+	if (wpa_driver_wext_set_mode(drv, 0) < 0) {
+		wpa_printf(MSG_DEBUG, "Could not configure driver to use "
+			   "managed mode");
+		/* Try to use it anyway */
+	}
+
+	wpa_driver_wext_get_range(drv);
+
+	/* Update per interface supported AKMs */
+	for (i = 0; i < WPA_IF_MAX; i++)
+		drv->capa.key_mgmt_iftype[i] = drv->capa.key_mgmt;
+
+	/*
+	 * Unlock the driver's BSSID and force to a random SSID to clear any
+	 * previous association the driver might have when the supplicant
+	 * starts up.
+	 */
+	wpa_driver_wext_disconnect(drv);
+
+	drv->ifindex = if_nametoindex(drv->ifname);
+
+	wext_check_hostap(drv);
+
+	netlink_send_oper_ifla(drv->netlink, drv->ifindex,
+			       1, IF_OPER_DORMANT);
+
+	if (send_rfkill_event) {
+		eloop_register_timeout(0, 0, wpa_driver_wext_send_rfkill,
+				       drv, drv->ctx);
+	}
+
+	return 0;
+}
+
+
+/**
+ * wpa_driver_wext_deinit - Deinitialize WE driver interface
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ *
+ * Shut down driver interface and processing of driver events. Free
+ * private data buffer if one was allocated in wpa_driver_wext_init().
+ */
+void wpa_driver_wext_deinit(void *priv)
+{
+	struct wpa_driver_wext_data *drv = priv;
+
+	wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_ENABLED, 0);
+
+	eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx);
+	eloop_cancel_timeout(wpa_driver_wext_send_rfkill, drv, drv->ctx);
+
+	/*
+	 * Clear possibly configured driver parameters in order to make it
+	 * easier to use the driver after wpa_supplicant has been terminated.
+	 */
+	wpa_driver_wext_disconnect(drv);
+
+	netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP);
+	netlink_deinit(drv->netlink);
+	rfkill_deinit(drv->rfkill);
+
+	if (drv->mlme_sock >= 0)
+		eloop_unregister_read_sock(drv->mlme_sock);
+
+	(void) linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0);
+
+	close(drv->ioctl_sock);
+	if (drv->mlme_sock >= 0)
+		close(drv->mlme_sock);
+	os_free(drv->assoc_req_ies);
+	os_free(drv->assoc_resp_ies);
+	os_free(drv);
+}
+
+
+/**
+ * wpa_driver_wext_scan_timeout - Scan timeout to report scan completion
+ * @eloop_ctx: Unused
+ * @timeout_ctx: ctx argument given to wpa_driver_wext_init()
+ *
+ * This function can be used as registered timeout when starting a scan to
+ * generate a scan completed event if the driver does not report this.
+ */
+void wpa_driver_wext_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
+	wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
+}
+
+
+/**
+ * wpa_driver_wext_scan - Request the driver to initiate scan
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @param: Scan parameters (specific SSID to scan for (ProbeReq), etc.)
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_wext_scan(void *priv, struct wpa_driver_scan_params *params)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	struct iwreq iwr;
+	int ret = 0, timeout;
+	struct iw_scan_req req;
+	const u8 *ssid = params->ssids[0].ssid;
+	size_t ssid_len = params->ssids[0].ssid_len;
+
+	if (ssid_len > IW_ESSID_MAX_SIZE) {
+		wpa_printf(MSG_DEBUG, "%s: too long SSID (%lu)",
+			   __FUNCTION__, (unsigned long) ssid_len);
+		return -1;
+	}
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+
+	if (ssid && ssid_len) {
+		os_memset(&req, 0, sizeof(req));
+		req.essid_len = ssid_len;
+		req.bssid.sa_family = ARPHRD_ETHER;
+		os_memset(req.bssid.sa_data, 0xff, ETH_ALEN);
+		os_memcpy(req.essid, ssid, ssid_len);
+		iwr.u.data.pointer = (caddr_t) &req;
+		iwr.u.data.length = sizeof(req);
+		iwr.u.data.flags = IW_SCAN_THIS_ESSID;
+	}
+
+	if (ioctl(drv->ioctl_sock, SIOCSIWSCAN, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWSCAN]: %s",
+			   strerror(errno));
+		ret = -1;
+	}
+
+	/* Not all drivers generate "scan completed" wireless event, so try to
+	 * read results after a timeout. */
+	timeout = 10;
+	if (drv->scan_complete_events) {
+		/*
+		 * The driver seems to deliver SIOCGIWSCAN events to notify
+		 * when scan is complete, so use longer timeout to avoid race
+		 * conditions with scanning and following association request.
+		 */
+		timeout = 30;
+	}
+	wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d "
+		   "seconds", ret, timeout);
+	eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx);
+	eloop_register_timeout(timeout, 0, wpa_driver_wext_scan_timeout, drv,
+			       drv->ctx);
+
+	return ret;
+}
+
+
+static u8 * wpa_driver_wext_giwscan(struct wpa_driver_wext_data *drv,
+				    size_t *len)
+{
+	struct iwreq iwr;
+	u8 *res_buf;
+	size_t res_buf_len;
+
+	res_buf_len = IW_SCAN_MAX_DATA;
+	for (;;) {
+		res_buf = os_malloc(res_buf_len);
+		if (res_buf == NULL)
+			return NULL;
+		os_memset(&iwr, 0, sizeof(iwr));
+		os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+		iwr.u.data.pointer = res_buf;
+		iwr.u.data.length = res_buf_len;
+
+		if (ioctl(drv->ioctl_sock, SIOCGIWSCAN, &iwr) == 0)
+			break;
+
+		if (errno == E2BIG && res_buf_len < 65535) {
+			os_free(res_buf);
+			res_buf = NULL;
+			res_buf_len *= 2;
+			if (res_buf_len > 65535)
+				res_buf_len = 65535; /* 16-bit length field */
+			wpa_printf(MSG_DEBUG, "Scan results did not fit - "
+				   "trying larger buffer (%lu bytes)",
+				   (unsigned long) res_buf_len);
+		} else {
+			wpa_printf(MSG_ERROR, "ioctl[SIOCGIWSCAN]: %s",
+				   strerror(errno));
+			os_free(res_buf);
+			return NULL;
+		}
+	}
+
+	if (iwr.u.data.length > res_buf_len) {
+		os_free(res_buf);
+		return NULL;
+	}
+	*len = iwr.u.data.length;
+
+	return res_buf;
+}
+
+
+/*
+ * Data structure for collecting WEXT scan results. This is needed to allow
+ * the various methods of reporting IEs to be combined into a single IE buffer.
+ */
+struct wext_scan_data {
+	struct wpa_scan_res res;
+	u8 *ie;
+	size_t ie_len;
+	u8 ssid[SSID_MAX_LEN];
+	size_t ssid_len;
+	int maxrate;
+};
+
+
+static void wext_get_scan_mode(struct iw_event *iwe,
+			       struct wext_scan_data *res)
+{
+	if (iwe->u.mode == IW_MODE_ADHOC)
+		res->res.caps |= IEEE80211_CAP_IBSS;
+	else if (iwe->u.mode == IW_MODE_MASTER || iwe->u.mode == IW_MODE_INFRA)
+		res->res.caps |= IEEE80211_CAP_ESS;
+}
+
+
+static void wext_get_scan_ssid(struct iw_event *iwe,
+			       struct wext_scan_data *res, char *custom,
+			       char *end)
+{
+	int ssid_len = iwe->u.essid.length;
+	if (ssid_len > end - custom)
+		return;
+	if (iwe->u.essid.flags &&
+	    ssid_len > 0 &&
+	    ssid_len <= IW_ESSID_MAX_SIZE) {
+		os_memcpy(res->ssid, custom, ssid_len);
+		res->ssid_len = ssid_len;
+	}
+}
+
+
+static void wext_get_scan_freq(struct iw_event *iwe,
+			       struct wext_scan_data *res)
+{
+	int divi = 1000000, i;
+
+	if (iwe->u.freq.e == 0) {
+		/*
+		 * Some drivers do not report frequency, but a channel.
+		 * Try to map this to frequency by assuming they are using
+		 * IEEE 802.11b/g.  But don't overwrite a previously parsed
+		 * frequency if the driver sends both frequency and channel,
+		 * since the driver may be sending an A-band channel that we
+		 * don't handle here.
+		 */
+
+		if (res->res.freq)
+			return;
+
+		if (iwe->u.freq.m >= 1 && iwe->u.freq.m <= 13) {
+			res->res.freq = 2407 + 5 * iwe->u.freq.m;
+			return;
+		} else if (iwe->u.freq.m == 14) {
+			res->res.freq = 2484;
+			return;
+		}
+	}
+
+	if (iwe->u.freq.e > 6) {
+		wpa_printf(MSG_DEBUG, "Invalid freq in scan results (BSSID="
+			   MACSTR " m=%d e=%d)",
+			   MAC2STR(res->res.bssid), iwe->u.freq.m,
+			   iwe->u.freq.e);
+		return;
+	}
+
+	for (i = 0; i < iwe->u.freq.e; i++)
+		divi /= 10;
+	res->res.freq = iwe->u.freq.m / divi;
+}
+
+
+static void wext_get_scan_qual(struct wpa_driver_wext_data *drv,
+			       struct iw_event *iwe,
+			       struct wext_scan_data *res)
+{
+	res->res.qual = iwe->u.qual.qual;
+	res->res.noise = iwe->u.qual.noise;
+	res->res.level = iwe->u.qual.level;
+	if (iwe->u.qual.updated & IW_QUAL_QUAL_INVALID)
+		res->res.flags |= WPA_SCAN_QUAL_INVALID;
+	if (iwe->u.qual.updated & IW_QUAL_LEVEL_INVALID)
+		res->res.flags |= WPA_SCAN_LEVEL_INVALID;
+	if (iwe->u.qual.updated & IW_QUAL_NOISE_INVALID)
+		res->res.flags |= WPA_SCAN_NOISE_INVALID;
+	if (iwe->u.qual.updated & IW_QUAL_DBM)
+		res->res.flags |= WPA_SCAN_LEVEL_DBM;
+	if ((iwe->u.qual.updated & IW_QUAL_DBM) ||
+	    ((iwe->u.qual.level != 0) &&
+	     (iwe->u.qual.level > drv->max_level))) {
+		if (iwe->u.qual.level >= 64)
+			res->res.level -= 0x100;
+		if (iwe->u.qual.noise >= 64)
+			res->res.noise -= 0x100;
+	}
+}
+
+
+static void wext_get_scan_encode(struct iw_event *iwe,
+				 struct wext_scan_data *res)
+{
+	if (!(iwe->u.data.flags & IW_ENCODE_DISABLED))
+		res->res.caps |= IEEE80211_CAP_PRIVACY;
+}
+
+
+static void wext_get_scan_rate(struct iw_event *iwe,
+			       struct wext_scan_data *res, char *pos,
+			       char *end)
+{
+	int maxrate;
+	char *custom = pos + IW_EV_LCP_LEN;
+	struct iw_param p;
+	size_t clen;
+
+	clen = iwe->len;
+	if (clen > (size_t) (end - custom))
+		return;
+	maxrate = 0;
+	while (((ssize_t) clen) >= (ssize_t) sizeof(struct iw_param)) {
+		/* Note: may be misaligned, make a local, aligned copy */
+		os_memcpy(&p, custom, sizeof(struct iw_param));
+		if (p.value > maxrate)
+			maxrate = p.value;
+		clen -= sizeof(struct iw_param);
+		custom += sizeof(struct iw_param);
+	}
+
+	/* Convert the maxrate from WE-style (b/s units) to
+	 * 802.11 rates (500000 b/s units).
+	 */
+	res->maxrate = maxrate / 500000;
+}
+
+
+static void wext_get_scan_iwevgenie(struct iw_event *iwe,
+				    struct wext_scan_data *res, char *custom,
+				    char *end)
+{
+	char *genie, *gpos, *gend;
+	u8 *tmp;
+
+	if (iwe->u.data.length == 0)
+		return;
+
+	gpos = genie = custom;
+	gend = genie + iwe->u.data.length;
+	if (gend > end) {
+		wpa_printf(MSG_INFO, "IWEVGENIE overflow");
+		return;
+	}
+
+	tmp = os_realloc(res->ie, res->ie_len + gend - gpos);
+	if (tmp == NULL)
+		return;
+	os_memcpy(tmp + res->ie_len, gpos, gend - gpos);
+	res->ie = tmp;
+	res->ie_len += gend - gpos;
+}
+
+
+static void wext_get_scan_custom(struct iw_event *iwe,
+				 struct wext_scan_data *res, char *custom,
+				 char *end)
+{
+	size_t clen;
+	u8 *tmp;
+
+	clen = iwe->u.data.length;
+	if (clen > (size_t) (end - custom))
+		return;
+
+	if (clen > 7 && os_strncmp(custom, "wpa_ie=", 7) == 0) {
+		char *spos;
+		int bytes;
+		spos = custom + 7;
+		bytes = custom + clen - spos;
+		if (bytes & 1 || bytes == 0)
+			return;
+		bytes /= 2;
+		tmp = os_realloc(res->ie, res->ie_len + bytes);
+		if (tmp == NULL)
+			return;
+		res->ie = tmp;
+		if (hexstr2bin(spos, tmp + res->ie_len, bytes) < 0)
+			return;
+		res->ie_len += bytes;
+	} else if (clen > 7 && os_strncmp(custom, "rsn_ie=", 7) == 0) {
+		char *spos;
+		int bytes;
+		spos = custom + 7;
+		bytes = custom + clen - spos;
+		if (bytes & 1 || bytes == 0)
+			return;
+		bytes /= 2;
+		tmp = os_realloc(res->ie, res->ie_len + bytes);
+		if (tmp == NULL)
+			return;
+		res->ie = tmp;
+		if (hexstr2bin(spos, tmp + res->ie_len, bytes) < 0)
+			return;
+		res->ie_len += bytes;
+	} else if (clen > 4 && os_strncmp(custom, "tsf=", 4) == 0) {
+		char *spos;
+		int bytes;
+		u8 bin[8];
+		spos = custom + 4;
+		bytes = custom + clen - spos;
+		if (bytes != 16) {
+			wpa_printf(MSG_INFO, "Invalid TSF length (%d)", bytes);
+			return;
+		}
+		bytes /= 2;
+		if (hexstr2bin(spos, bin, bytes) < 0) {
+			wpa_printf(MSG_DEBUG, "WEXT: Invalid TSF value");
+			return;
+		}
+		res->res.tsf += WPA_GET_BE64(bin);
+	}
+}
+
+
+static int wext_19_iw_point(struct wpa_driver_wext_data *drv, u16 cmd)
+{
+	return drv->we_version_compiled > 18 &&
+		(cmd == SIOCGIWESSID || cmd == SIOCGIWENCODE ||
+		 cmd == IWEVGENIE || cmd == IWEVCUSTOM);
+}
+
+
+static void wpa_driver_wext_add_scan_entry(struct wpa_scan_results *res,
+					   struct wext_scan_data *data)
+{
+	struct wpa_scan_res **tmp;
+	struct wpa_scan_res *r;
+	size_t extra_len;
+	u8 *pos, *end, *ssid_ie = NULL, *rate_ie = NULL;
+
+	/* Figure out whether we need to fake any IEs */
+	pos = data->ie;
+	end = pos + data->ie_len;
+	while (pos && end - pos > 1) {
+		if (2 + pos[1] > end - pos)
+			break;
+		if (pos[0] == WLAN_EID_SSID)
+			ssid_ie = pos;
+		else if (pos[0] == WLAN_EID_SUPP_RATES)
+			rate_ie = pos;
+		else if (pos[0] == WLAN_EID_EXT_SUPP_RATES)
+			rate_ie = pos;
+		pos += 2 + pos[1];
+	}
+
+	extra_len = 0;
+	if (ssid_ie == NULL)
+		extra_len += 2 + data->ssid_len;
+	if (rate_ie == NULL && data->maxrate)
+		extra_len += 3;
+
+	r = os_zalloc(sizeof(*r) + extra_len + data->ie_len);
+	if (r == NULL)
+		return;
+	os_memcpy(r, &data->res, sizeof(*r));
+	r->ie_len = extra_len + data->ie_len;
+	pos = (u8 *) (r + 1);
+	if (ssid_ie == NULL) {
+		/*
+		 * Generate a fake SSID IE since the driver did not report
+		 * a full IE list.
+		 */
+		*pos++ = WLAN_EID_SSID;
+		*pos++ = data->ssid_len;
+		os_memcpy(pos, data->ssid, data->ssid_len);
+		pos += data->ssid_len;
+	}
+	if (rate_ie == NULL && data->maxrate) {
+		/*
+		 * Generate a fake Supported Rates IE since the driver did not
+		 * report a full IE list.
+		 */
+		*pos++ = WLAN_EID_SUPP_RATES;
+		*pos++ = 1;
+		*pos++ = data->maxrate;
+	}
+	if (data->ie)
+		os_memcpy(pos, data->ie, data->ie_len);
+
+	tmp = os_realloc_array(res->res, res->num + 1,
+			       sizeof(struct wpa_scan_res *));
+	if (tmp == NULL) {
+		os_free(r);
+		return;
+	}
+	tmp[res->num++] = r;
+	res->res = tmp;
+}
+
+
+/**
+ * wpa_driver_wext_get_scan_results - Fetch the latest scan results
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * Returns: Scan results on success, -1 on failure
+ */
+struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	size_t len;
+	int first;
+	u8 *res_buf;
+	struct iw_event iwe_buf, *iwe = &iwe_buf;
+	char *pos, *end, *custom;
+	struct wpa_scan_results *res;
+	struct wext_scan_data data;
+
+	res_buf = wpa_driver_wext_giwscan(drv, &len);
+	if (res_buf == NULL)
+		return NULL;
+
+	first = 1;
+
+	res = os_zalloc(sizeof(*res));
+	if (res == NULL) {
+		os_free(res_buf);
+		return NULL;
+	}
+
+	pos = (char *) res_buf;
+	end = (char *) res_buf + len;
+	os_memset(&data, 0, sizeof(data));
+
+	while ((size_t) (end - pos) >= IW_EV_LCP_LEN) {
+		/* Event data may be unaligned, so make a local, aligned copy
+		 * before processing. */
+		os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
+		if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos)
+			break;
+
+		custom = pos + IW_EV_POINT_LEN;
+		if (wext_19_iw_point(drv, iwe->cmd)) {
+			/* WE-19 removed the pointer from struct iw_point */
+			char *dpos = (char *) &iwe_buf.u.data.length;
+			int dlen = dpos - (char *) &iwe_buf;
+			os_memcpy(dpos, pos + IW_EV_LCP_LEN,
+				  sizeof(struct iw_event) - dlen);
+		} else {
+			os_memcpy(&iwe_buf, pos, sizeof(struct iw_event));
+			custom += IW_EV_POINT_OFF;
+		}
+
+		switch (iwe->cmd) {
+		case SIOCGIWAP:
+			if (!first)
+				wpa_driver_wext_add_scan_entry(res, &data);
+			first = 0;
+			os_free(data.ie);
+			os_memset(&data, 0, sizeof(data));
+			os_memcpy(data.res.bssid,
+				  iwe->u.ap_addr.sa_data, ETH_ALEN);
+			break;
+		case SIOCGIWMODE:
+			wext_get_scan_mode(iwe, &data);
+			break;
+		case SIOCGIWESSID:
+			wext_get_scan_ssid(iwe, &data, custom, end);
+			break;
+		case SIOCGIWFREQ:
+			wext_get_scan_freq(iwe, &data);
+			break;
+		case IWEVQUAL:
+			wext_get_scan_qual(drv, iwe, &data);
+			break;
+		case SIOCGIWENCODE:
+			wext_get_scan_encode(iwe, &data);
+			break;
+		case SIOCGIWRATE:
+			wext_get_scan_rate(iwe, &data, pos, end);
+			break;
+		case IWEVGENIE:
+			wext_get_scan_iwevgenie(iwe, &data, custom, end);
+			break;
+		case IWEVCUSTOM:
+			wext_get_scan_custom(iwe, &data, custom, end);
+			break;
+		}
+
+		pos += iwe->len;
+	}
+	os_free(res_buf);
+	res_buf = NULL;
+	if (!first)
+		wpa_driver_wext_add_scan_entry(res, &data);
+	os_free(data.ie);
+
+	wpa_printf(MSG_DEBUG, "Received %lu bytes of scan results (%lu BSSes)",
+		   (unsigned long) len, (unsigned long) res->num);
+
+	return res;
+}
+
+
+static int wpa_driver_wext_get_range(void *priv)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	struct iw_range *range;
+	struct iwreq iwr;
+	int minlen;
+	size_t buflen;
+
+	/*
+	 * Use larger buffer than struct iw_range in order to allow the
+	 * structure to grow in the future.
+	 */
+	buflen = sizeof(struct iw_range) + 500;
+	range = os_zalloc(buflen);
+	if (range == NULL)
+		return -1;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+	iwr.u.data.pointer = (caddr_t) range;
+	iwr.u.data.length = buflen;
+
+	minlen = ((char *) &range->enc_capa) - (char *) range +
+		sizeof(range->enc_capa);
+
+	if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIWRANGE]: %s",
+			   strerror(errno));
+		os_free(range);
+		return -1;
+	} else if (iwr.u.data.length >= minlen &&
+		   range->we_version_compiled >= 18) {
+		wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d "
+			   "WE(source)=%d enc_capa=0x%x",
+			   range->we_version_compiled,
+			   range->we_version_source,
+			   range->enc_capa);
+		drv->has_capability = 1;
+		drv->we_version_compiled = range->we_version_compiled;
+		if (range->enc_capa & IW_ENC_CAPA_WPA) {
+			drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+				WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK;
+		}
+		if (range->enc_capa & IW_ENC_CAPA_WPA2) {
+			drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+				WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+		}
+		drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 |
+			WPA_DRIVER_CAPA_ENC_WEP104;
+		drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP128;
+		if (range->enc_capa & IW_ENC_CAPA_CIPHER_TKIP)
+			drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP;
+		if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP)
+			drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP;
+		if (range->enc_capa & IW_ENC_CAPA_4WAY_HANDSHAKE)
+			drv->capa.flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK |
+				WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X;
+		drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
+			WPA_DRIVER_AUTH_SHARED |
+			WPA_DRIVER_AUTH_LEAP;
+		drv->capa.max_scan_ssids = 1;
+
+		wpa_printf(MSG_DEBUG, "  capabilities: key_mgmt 0x%x enc 0x%x "
+			   "flags 0x%llx",
+			   drv->capa.key_mgmt, drv->capa.enc,
+			   (unsigned long long) drv->capa.flags);
+	} else {
+		wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: too old (short) data - "
+			   "assuming WPA is not supported");
+	}
+
+	drv->max_level = range->max_qual.level;
+
+	os_free(range);
+	return 0;
+}
+
+
+static int wpa_driver_wext_set_psk(struct wpa_driver_wext_data *drv,
+				   const u8 *psk)
+{
+	struct iw_encode_ext *ext;
+	struct iwreq iwr;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X))
+		return 0;
+
+	if (!psk)
+		return 0;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+
+	ext = os_zalloc(sizeof(*ext) + PMK_LEN);
+	if (ext == NULL)
+		return -1;
+
+	iwr.u.encoding.pointer = (caddr_t) ext;
+	iwr.u.encoding.length = sizeof(*ext) + PMK_LEN;
+	ext->key_len = PMK_LEN;
+	os_memcpy(&ext->key, psk, ext->key_len);
+	ext->alg = IW_ENCODE_ALG_PMK;
+
+	ret = ioctl(drv->ioctl_sock, SIOCSIWENCODEEXT, &iwr);
+	if (ret < 0)
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWENCODEEXT] PMK: %s",
+			   strerror(errno));
+	os_free(ext);
+
+	return ret;
+}
+
+
+static int wpa_driver_wext_set_key_ext(void *priv, enum wpa_alg alg,
+				       const u8 *addr, int key_idx,
+				       int set_tx, const u8 *seq,
+				       size_t seq_len,
+				       const u8 *key, size_t key_len,
+				       enum key_flag key_flag)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	struct iwreq iwr;
+	int ret = 0;
+	struct iw_encode_ext *ext;
+
+	if (seq_len > IW_ENCODE_SEQ_MAX_SIZE) {
+		wpa_printf(MSG_DEBUG, "%s: Invalid seq_len %lu",
+			   __FUNCTION__, (unsigned long) seq_len);
+		return -1;
+	}
+
+	ext = os_zalloc(sizeof(*ext) + key_len);
+	if (ext == NULL)
+		return -1;
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+	iwr.u.encoding.flags = key_idx + 1;
+	iwr.u.encoding.flags |= IW_ENCODE_TEMP;
+	if (alg == WPA_ALG_NONE)
+		iwr.u.encoding.flags |= IW_ENCODE_DISABLED;
+	iwr.u.encoding.pointer = (caddr_t) ext;
+	iwr.u.encoding.length = sizeof(*ext) + key_len;
+
+	if (addr == NULL || is_broadcast_ether_addr(addr))
+		ext->ext_flags |= IW_ENCODE_EXT_GROUP_KEY;
+	if (set_tx)
+		ext->ext_flags |= IW_ENCODE_EXT_SET_TX_KEY;
+
+	ext->addr.sa_family = ARPHRD_ETHER;
+	if (addr)
+		os_memcpy(ext->addr.sa_data, addr, ETH_ALEN);
+	else
+		os_memset(ext->addr.sa_data, 0xff, ETH_ALEN);
+	if (key && key_len) {
+		os_memcpy(ext + 1, key, key_len);
+		ext->key_len = key_len;
+	}
+	if (key_flag & KEY_FLAG_PMK) {
+		ext->alg = IW_ENCODE_ALG_PMK;
+	} else {
+		switch (alg) {
+		case WPA_ALG_NONE:
+			ext->alg = IW_ENCODE_ALG_NONE;
+			break;
+		case WPA_ALG_WEP:
+			ext->alg = IW_ENCODE_ALG_WEP;
+			break;
+		case WPA_ALG_TKIP:
+			ext->alg = IW_ENCODE_ALG_TKIP;
+			break;
+		case WPA_ALG_CCMP:
+			ext->alg = IW_ENCODE_ALG_CCMP;
+			break;
+		case WPA_ALG_BIP_CMAC_128:
+			ext->alg = IW_ENCODE_ALG_AES_CMAC;
+			break;
+		default:
+			wpa_printf(MSG_DEBUG, "%s: Unknown algorithm %d",
+				   __FUNCTION__, alg);
+			os_free(ext);
+			return -1;
+		}
+	}
+
+	if (seq && seq_len) {
+		ext->ext_flags |= IW_ENCODE_EXT_RX_SEQ_VALID;
+		os_memcpy(ext->rx_seq, seq, seq_len);
+	}
+
+	if (ioctl(drv->ioctl_sock, SIOCSIWENCODEEXT, &iwr) < 0) {
+		ret = errno == EOPNOTSUPP ? -2 : -1;
+		if (errno == ENODEV) {
+			/*
+			 * ndiswrapper seems to be returning incorrect error
+			 * code.. */
+			ret = -2;
+		}
+
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWENCODEEXT]: %s",
+			   strerror(errno));
+	}
+
+	os_free(ext);
+	return ret;
+}
+
+
+/**
+ * wpa_driver_wext_set_key - Configure encryption key
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @params: Key parameters
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function uses SIOCSIWENCODEEXT by default, but tries to use
+ * SIOCSIWENCODE if the extended ioctl fails when configuring a WEP key.
+ */
+static int wpa_driver_wext_set_key(void *priv,
+				   struct wpa_driver_set_key_params *params)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	struct iwreq iwr;
+	int ret = 0;
+	enum wpa_alg alg = params->alg;
+	enum key_flag key_flag = params->key_flag;
+	const u8 *addr = params->addr;
+	int key_idx = params->key_idx;
+	int set_tx = params->set_tx;
+	const u8 *seq = params->seq;
+	size_t seq_len = params->seq_len;
+	const u8 *key = params->key;
+	size_t key_len = params->key_len;
+
+	wpa_printf(MSG_DEBUG, "%s: alg=%d key_idx=%d set_tx=%d seq_len=%lu "
+		   "key_len=%lu",
+		   __FUNCTION__, alg, key_idx, set_tx,
+		   (unsigned long) seq_len, (unsigned long) key_len);
+
+	ret = wpa_driver_wext_set_key_ext(drv, alg, addr, key_idx, set_tx,
+					  seq, seq_len, key, key_len, key_flag);
+	if (ret == 0)
+		return 0;
+
+	if (ret == -2 &&
+	    (alg == WPA_ALG_NONE || alg == WPA_ALG_WEP)) {
+		wpa_printf(MSG_DEBUG, "Driver did not support "
+			   "SIOCSIWENCODEEXT, trying SIOCSIWENCODE");
+		ret = 0;
+	} else {
+		wpa_printf(MSG_DEBUG, "Driver did not support "
+			   "SIOCSIWENCODEEXT");
+		return ret;
+	}
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+	iwr.u.encoding.flags = key_idx + 1;
+	iwr.u.encoding.flags |= IW_ENCODE_TEMP;
+	if (alg == WPA_ALG_NONE)
+		iwr.u.encoding.flags |= IW_ENCODE_DISABLED;
+	iwr.u.encoding.pointer = (caddr_t) key;
+	iwr.u.encoding.length = key_len;
+
+	if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWENCODE]: %s",
+			   strerror(errno));
+		ret = -1;
+	}
+
+	if (set_tx && alg != WPA_ALG_NONE) {
+		os_memset(&iwr, 0, sizeof(iwr));
+		os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+		iwr.u.encoding.flags = key_idx + 1;
+		iwr.u.encoding.flags |= IW_ENCODE_TEMP;
+		iwr.u.encoding.pointer = (caddr_t) NULL;
+		iwr.u.encoding.length = 0;
+		if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) {
+			wpa_printf(MSG_ERROR,
+				   "ioctl[SIOCSIWENCODE] (set_tx): %s",
+				   strerror(errno));
+			ret = -1;
+		}
+	}
+
+	return ret;
+}
+
+
+static int wpa_driver_wext_set_countermeasures(void *priv,
+					       int enabled)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+	return wpa_driver_wext_set_auth_param(drv,
+					      IW_AUTH_TKIP_COUNTERMEASURES,
+					      enabled);
+}
+
+
+static int wpa_driver_wext_set_drop_unencrypted(void *priv,
+						int enabled)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+	drv->use_crypt = enabled;
+	return wpa_driver_wext_set_auth_param(drv, IW_AUTH_DROP_UNENCRYPTED,
+					      enabled);
+}
+
+
+static int wpa_driver_wext_mlme(struct wpa_driver_wext_data *drv,
+				const u8 *addr, int cmd, u16 reason_code)
+{
+	struct iwreq iwr;
+	struct iw_mlme mlme;
+	int ret = 0;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+	os_memset(&mlme, 0, sizeof(mlme));
+	mlme.cmd = cmd;
+	mlme.reason_code = reason_code;
+	mlme.addr.sa_family = ARPHRD_ETHER;
+	os_memcpy(mlme.addr.sa_data, addr, ETH_ALEN);
+	iwr.u.data.pointer = (caddr_t) &mlme;
+	iwr.u.data.length = sizeof(mlme);
+
+	if (ioctl(drv->ioctl_sock, SIOCSIWMLME, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWMLME]: %s",
+			   strerror(errno));
+		ret = -1;
+	}
+
+	return ret;
+}
+
+
+static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv)
+{
+	struct iwreq iwr;
+	const u8 null_bssid[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+	u8 ssid[SSID_MAX_LEN];
+	int i;
+
+	/*
+	 * Only force-disconnect when the card is in infrastructure mode,
+	 * otherwise the driver might interpret the cleared BSSID and random
+	 * SSID as an attempt to create a new ad-hoc network.
+	 */
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+	if (ioctl(drv->ioctl_sock, SIOCGIWMODE, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIWMODE]: %s",
+			   strerror(errno));
+		iwr.u.mode = IW_MODE_INFRA;
+	}
+
+	if (iwr.u.mode == IW_MODE_INFRA) {
+		/* Clear the BSSID selection */
+		if (wpa_driver_wext_set_bssid(drv, null_bssid) < 0) {
+			wpa_printf(MSG_DEBUG, "WEXT: Failed to clear BSSID "
+				   "selection on disconnect");
+		}
+
+		if (drv->cfg80211) {
+			/*
+			 * cfg80211 supports SIOCSIWMLME commands, so there is
+			 * no need for the random SSID hack, but clear the
+			 * SSID.
+			 */
+			if (wpa_driver_wext_set_ssid(drv, (u8 *) "", 0) < 0) {
+				wpa_printf(MSG_DEBUG, "WEXT: Failed to clear "
+					   "SSID on disconnect");
+			}
+			return;
+		}
+
+		/*
+		 * Set a random SSID to make sure the driver will not be trying
+		 * to associate with something even if it does not understand
+		 * SIOCSIWMLME commands (or tries to associate automatically
+		 * after deauth/disassoc).
+		 */
+		for (i = 0; i < SSID_MAX_LEN; i++)
+			ssid[i] = rand() & 0xFF;
+		if (wpa_driver_wext_set_ssid(drv, ssid, SSID_MAX_LEN) < 0) {
+			wpa_printf(MSG_DEBUG, "WEXT: Failed to set bogus "
+				   "SSID to disconnect");
+		}
+	}
+}
+
+
+static int wpa_driver_wext_deauthenticate(void *priv, const u8 *addr,
+					  u16 reason_code)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	int ret;
+	wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+	ret = wpa_driver_wext_mlme(drv, addr, IW_MLME_DEAUTH, reason_code);
+	wpa_driver_wext_disconnect(drv);
+	return ret;
+}
+
+
+static int wpa_driver_wext_set_gen_ie(void *priv, const u8 *ie,
+				      size_t ie_len)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	struct iwreq iwr;
+	int ret = 0;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+	iwr.u.data.pointer = (caddr_t) ie;
+	iwr.u.data.length = ie_len;
+
+	if (ioctl(drv->ioctl_sock, SIOCSIWGENIE, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWGENIE]: %s",
+			   strerror(errno));
+		ret = -1;
+	}
+
+	return ret;
+}
+
+
+int wpa_driver_wext_cipher2wext(int cipher)
+{
+	switch (cipher) {
+	case WPA_CIPHER_NONE:
+		return IW_AUTH_CIPHER_NONE;
+	case WPA_CIPHER_WEP40:
+		return IW_AUTH_CIPHER_WEP40;
+	case WPA_CIPHER_TKIP:
+		return IW_AUTH_CIPHER_TKIP;
+	case WPA_CIPHER_CCMP:
+		return IW_AUTH_CIPHER_CCMP;
+	case WPA_CIPHER_WEP104:
+		return IW_AUTH_CIPHER_WEP104;
+	default:
+		return 0;
+	}
+}
+
+
+int wpa_driver_wext_keymgmt2wext(int keymgmt)
+{
+	switch (keymgmt) {
+	case WPA_KEY_MGMT_IEEE8021X:
+	case WPA_KEY_MGMT_IEEE8021X_NO_WPA:
+		return IW_AUTH_KEY_MGMT_802_1X;
+	case WPA_KEY_MGMT_PSK:
+		return IW_AUTH_KEY_MGMT_PSK;
+	default:
+		return 0;
+	}
+}
+
+
+static int
+wpa_driver_wext_auth_alg_fallback(struct wpa_driver_wext_data *drv,
+				  struct wpa_driver_associate_params *params)
+{
+	struct iwreq iwr;
+	int ret = 0;
+
+	wpa_printf(MSG_DEBUG, "WEXT: Driver did not support "
+		   "SIOCSIWAUTH for AUTH_ALG, trying SIOCSIWENCODE");
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+	/* Just changing mode, not actual keys */
+	iwr.u.encoding.flags = 0;
+	iwr.u.encoding.pointer = (caddr_t) NULL;
+	iwr.u.encoding.length = 0;
+
+	/*
+	 * Note: IW_ENCODE_{OPEN,RESTRICTED} can be interpreted to mean two
+	 * different things. Here they are used to indicate Open System vs.
+	 * Shared Key authentication algorithm. However, some drivers may use
+	 * them to select between open/restricted WEP encrypted (open = allow
+	 * both unencrypted and encrypted frames; restricted = only allow
+	 * encrypted frames).
+	 */
+
+	if (!drv->use_crypt) {
+		iwr.u.encoding.flags |= IW_ENCODE_DISABLED;
+	} else {
+		if (params->auth_alg & WPA_AUTH_ALG_OPEN)
+			iwr.u.encoding.flags |= IW_ENCODE_OPEN;
+		if (params->auth_alg & WPA_AUTH_ALG_SHARED)
+			iwr.u.encoding.flags |= IW_ENCODE_RESTRICTED;
+	}
+
+	if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWENCODE]: %s",
+			   strerror(errno));
+		ret = -1;
+	}
+
+	return ret;
+}
+
+
+int wpa_driver_wext_associate(void *priv,
+			      struct wpa_driver_associate_params *params)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	int ret = 0;
+	int allow_unencrypted_eapol;
+	int value;
+
+	wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+	if (drv->cfg80211) {
+		/*
+		 * Stop cfg80211 from trying to associate before we are done
+		 * with all parameters.
+		 */
+		if (wpa_driver_wext_set_ssid(drv, (u8 *) "", 0) < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "WEXT: Failed to clear SSID to stop pending cfg80211 association attempts (if any)");
+			/* continue anyway */
+		}
+	}
+
+	if (wpa_driver_wext_set_drop_unencrypted(drv, params->drop_unencrypted)
+	    < 0)
+		ret = -1;
+	if (wpa_driver_wext_set_auth_alg(drv, params->auth_alg) < 0)
+		ret = -1;
+	if (wpa_driver_wext_set_mode(drv, params->mode) < 0)
+		ret = -1;
+
+	/*
+	 * If the driver did not support SIOCSIWAUTH, fallback to
+	 * SIOCSIWENCODE here.
+	 */
+	if (drv->auth_alg_fallback &&
+	    wpa_driver_wext_auth_alg_fallback(drv, params) < 0)
+		ret = -1;
+
+	if (!params->bssid &&
+	    wpa_driver_wext_set_bssid(drv, NULL) < 0)
+		ret = -1;
+
+	/* TODO: should consider getting wpa version and cipher/key_mgmt suites
+	 * from configuration, not from here, where only the selected suite is
+	 * available */
+	if (wpa_driver_wext_set_gen_ie(drv, params->wpa_ie, params->wpa_ie_len)
+	    < 0)
+		ret = -1;
+	if (params->wpa_proto & WPA_PROTO_RSN)
+		value = IW_AUTH_WPA_VERSION_WPA2;
+	else if (params->wpa_proto & WPA_PROTO_WPA)
+		value = IW_AUTH_WPA_VERSION_WPA;
+	else
+		value = IW_AUTH_WPA_VERSION_DISABLED;
+	if (wpa_driver_wext_set_auth_param(drv,
+					   IW_AUTH_WPA_VERSION, value) < 0)
+		ret = -1;
+	value = wpa_driver_wext_cipher2wext(params->pairwise_suite);
+	if (wpa_driver_wext_set_auth_param(drv,
+					   IW_AUTH_CIPHER_PAIRWISE, value) < 0)
+		ret = -1;
+	value = wpa_driver_wext_cipher2wext(params->group_suite);
+	if (wpa_driver_wext_set_auth_param(drv,
+					   IW_AUTH_CIPHER_GROUP, value) < 0)
+		ret = -1;
+	value = wpa_driver_wext_keymgmt2wext(params->key_mgmt_suite);
+	if (wpa_driver_wext_set_auth_param(drv,
+					   IW_AUTH_KEY_MGMT, value) < 0)
+		ret = -1;
+	value = params->key_mgmt_suite != WPA_KEY_MGMT_NONE ||
+		params->pairwise_suite != WPA_CIPHER_NONE ||
+		params->group_suite != WPA_CIPHER_NONE ||
+		(params->wpa_proto & (WPA_PROTO_RSN | WPA_PROTO_WPA));
+	if (wpa_driver_wext_set_auth_param(drv,
+					   IW_AUTH_PRIVACY_INVOKED, value) < 0)
+		ret = -1;
+
+	/* Allow unencrypted EAPOL messages even if pairwise keys are set when
+	 * not using WPA. IEEE 802.1X specifies that these frames are not
+	 * encrypted, but WPA encrypts them when pairwise keys are in use. */
+	if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_PSK)
+		allow_unencrypted_eapol = 0;
+	else
+		allow_unencrypted_eapol = 1;
+
+	if (wpa_driver_wext_set_psk(drv, params->psk) < 0)
+		ret = -1;
+	if (wpa_driver_wext_set_auth_param(drv,
+					   IW_AUTH_RX_UNENCRYPTED_EAPOL,
+					   allow_unencrypted_eapol) < 0)
+		ret = -1;
+	switch (params->mgmt_frame_protection) {
+	case NO_MGMT_FRAME_PROTECTION:
+		value = IW_AUTH_MFP_DISABLED;
+		break;
+	case MGMT_FRAME_PROTECTION_OPTIONAL:
+		value = IW_AUTH_MFP_OPTIONAL;
+		break;
+	case MGMT_FRAME_PROTECTION_REQUIRED:
+		value = IW_AUTH_MFP_REQUIRED;
+		break;
+	};
+	if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_MFP, value) < 0)
+		ret = -1;
+	if (params->freq.freq &&
+	    wpa_driver_wext_set_freq(drv, params->freq.freq) < 0)
+		ret = -1;
+	if (!drv->cfg80211 &&
+	    wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len) < 0)
+		ret = -1;
+	if (params->bssid &&
+	    wpa_driver_wext_set_bssid(drv, params->bssid) < 0)
+		ret = -1;
+	if (drv->cfg80211 &&
+	    wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len) < 0)
+		ret = -1;
+
+	return ret;
+}
+
+
+static int wpa_driver_wext_set_auth_alg(void *priv, int auth_alg)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	int algs = 0, res;
+
+	if (auth_alg & WPA_AUTH_ALG_OPEN)
+		algs |= IW_AUTH_ALG_OPEN_SYSTEM;
+	if (auth_alg & WPA_AUTH_ALG_SHARED)
+		algs |= IW_AUTH_ALG_SHARED_KEY;
+	if (auth_alg & WPA_AUTH_ALG_LEAP)
+		algs |= IW_AUTH_ALG_LEAP;
+	if (algs == 0) {
+		/* at least one algorithm should be set */
+		algs = IW_AUTH_ALG_OPEN_SYSTEM;
+	}
+
+	res = wpa_driver_wext_set_auth_param(drv, IW_AUTH_80211_AUTH_ALG,
+					     algs);
+	drv->auth_alg_fallback = res == -2;
+	return res;
+}
+
+
+/**
+ * wpa_driver_wext_set_mode - Set wireless mode (infra/adhoc), SIOCSIWMODE
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @mode: 0 = infra/BSS (associate with an AP), 1 = adhoc/IBSS
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_wext_set_mode(void *priv, int mode)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	struct iwreq iwr;
+	int ret = -1;
+	unsigned int new_mode = mode ? IW_MODE_ADHOC : IW_MODE_INFRA;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+	iwr.u.mode = new_mode;
+	if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) == 0) {
+		ret = 0;
+		goto done;
+	}
+
+	if (errno != EBUSY) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWMODE]: %s",
+			   strerror(errno));
+		goto done;
+	}
+
+	/* mac80211 doesn't allow mode changes while the device is up, so if
+	 * the device isn't in the mode we're about to change to, take device
+	 * down, try to set the mode again, and bring it back up.
+	 */
+	if (ioctl(drv->ioctl_sock, SIOCGIWMODE, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIWMODE]: %s",
+			   strerror(errno));
+		goto done;
+	}
+
+	if (iwr.u.mode == new_mode) {
+		ret = 0;
+		goto done;
+	}
+
+	if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0) == 0) {
+		/* Try to set the mode again while the interface is down */
+		iwr.u.mode = new_mode;
+		if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0)
+			wpa_printf(MSG_ERROR, "ioctl[SIOCSIWMODE]: %s",
+				   strerror(errno));
+		else
+			ret = 0;
+
+		(void) linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1);
+	}
+
+done:
+	return ret;
+}
+
+
+static int wpa_driver_wext_pmksa(struct wpa_driver_wext_data *drv,
+				 u32 cmd, const u8 *bssid, const u8 *pmkid)
+{
+	struct iwreq iwr;
+	struct iw_pmksa pmksa;
+	int ret = 0;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+	os_memset(&pmksa, 0, sizeof(pmksa));
+	pmksa.cmd = cmd;
+	pmksa.bssid.sa_family = ARPHRD_ETHER;
+	if (bssid)
+		os_memcpy(pmksa.bssid.sa_data, bssid, ETH_ALEN);
+	if (pmkid)
+		os_memcpy(pmksa.pmkid, pmkid, IW_PMKID_LEN);
+	iwr.u.data.pointer = (caddr_t) &pmksa;
+	iwr.u.data.length = sizeof(pmksa);
+
+	if (ioctl(drv->ioctl_sock, SIOCSIWPMKSA, &iwr) < 0) {
+		if (errno != EOPNOTSUPP)
+			wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPMKSA]: %s",
+				   strerror(errno));
+		ret = -1;
+	}
+
+	return ret;
+}
+
+
+static int wpa_driver_wext_add_pmkid(void *priv,
+				     struct wpa_pmkid_params *params)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	return wpa_driver_wext_pmksa(drv, IW_PMKSA_ADD, params->bssid,
+				     params->pmkid);
+}
+
+
+static int wpa_driver_wext_remove_pmkid(void *priv,
+					struct wpa_pmkid_params *params)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	return wpa_driver_wext_pmksa(drv, IW_PMKSA_REMOVE, params->bssid,
+				     params->pmkid);
+}
+
+
+static int wpa_driver_wext_flush_pmkid(void *priv)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	return wpa_driver_wext_pmksa(drv, IW_PMKSA_FLUSH, NULL, NULL);
+}
+
+
+int wpa_driver_wext_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	if (!drv->has_capability)
+		return -1;
+	os_memcpy(capa, &drv->capa, sizeof(*capa));
+	return 0;
+}
+
+
+int wpa_driver_wext_alternative_ifindex(struct wpa_driver_wext_data *drv,
+					const char *ifname)
+{
+	if (ifname == NULL) {
+		drv->ifindex2 = -1;
+		return 0;
+	}
+
+	drv->ifindex2 = if_nametoindex(ifname);
+	if (drv->ifindex2 <= 0)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "Added alternative ifindex %d (%s) for "
+		   "wireless events", drv->ifindex2, ifname);
+
+	return 0;
+}
+
+
+int wpa_driver_wext_set_operstate(void *priv, int state)
+{
+	struct wpa_driver_wext_data *drv = priv;
+
+	wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)",
+		   __func__, drv->operstate, state, state ? "UP" : "DORMANT");
+	drv->operstate = state;
+	return netlink_send_oper_ifla(drv->netlink, drv->ifindex, -1,
+				      state ? IF_OPER_UP : IF_OPER_DORMANT);
+}
+
+
+int wpa_driver_wext_get_version(struct wpa_driver_wext_data *drv)
+{
+	return drv->we_version_compiled;
+}
+
+
+static const char * wext_get_radio_name(void *priv)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	return drv->phyname;
+}
+
+
+static int wpa_driver_wext_signal_poll(void *priv, struct wpa_signal_info *si)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	struct iw_statistics stats;
+	struct iwreq iwr;
+
+	os_memset(si, 0, sizeof(*si));
+	si->data.signal = -WPA_INVALID_NOISE;
+	si->current_noise = WPA_INVALID_NOISE;
+	si->chanwidth = CHAN_WIDTH_UNKNOWN;
+
+	os_memset(&iwr, 0, sizeof(iwr));
+	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+	iwr.u.data.pointer = (caddr_t) &stats;
+	iwr.u.data.length = sizeof(stats);
+	iwr.u.data.flags = 1;
+
+	if (ioctl(drv->ioctl_sock, SIOCGIWSTATS, &iwr) < 0) {
+		wpa_printf(MSG_ERROR, "WEXT: SIOCGIWSTATS: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	si->data.signal = stats.qual.level -
+		((stats.qual.updated & IW_QUAL_DBM) ? 0x100 : 0);
+	si->current_noise = stats.qual.noise -
+		((stats.qual.updated & IW_QUAL_DBM) ? 0x100 : 0);
+	return 0;
+}
+
+
+static int wpa_driver_wext_status(void *priv, char *buf, size_t buflen)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	int res;
+	char *pos, *end;
+	unsigned char addr[ETH_ALEN];
+
+	pos = buf;
+	end = buf + buflen;
+
+	if (linux_get_ifhwaddr(drv->ioctl_sock, drv->ifname, addr))
+		return -1;
+
+	res = os_snprintf(pos, end - pos,
+			  "ifindex=%d\n"
+			  "ifname=%s\n"
+			  "addr=" MACSTR "\n",
+			  drv->ifindex,
+			  drv->ifname,
+			  MAC2STR(addr));
+	if (os_snprintf_error(end - pos, res))
+		return pos - buf;
+	pos += res;
+
+	return pos - buf;
+}
+
+const struct wpa_driver_ops wpa_driver_wext_ops = {
+	.name = "wext",
+	.desc = "Linux wireless extensions (generic)",
+	.get_bssid = wpa_driver_wext_get_bssid,
+	.get_ssid = wpa_driver_wext_get_ssid,
+	.set_key = wpa_driver_wext_set_key,
+	.set_countermeasures = wpa_driver_wext_set_countermeasures,
+	.scan2 = wpa_driver_wext_scan,
+	.get_scan_results2 = wpa_driver_wext_get_scan_results,
+	.deauthenticate = wpa_driver_wext_deauthenticate,
+	.associate = wpa_driver_wext_associate,
+	.init = wpa_driver_wext_init,
+	.deinit = wpa_driver_wext_deinit,
+	.add_pmkid = wpa_driver_wext_add_pmkid,
+	.remove_pmkid = wpa_driver_wext_remove_pmkid,
+	.flush_pmkid = wpa_driver_wext_flush_pmkid,
+	.get_capa = wpa_driver_wext_get_capa,
+	.set_operstate = wpa_driver_wext_set_operstate,
+	.get_radio_name = wext_get_radio_name,
+	.signal_poll = wpa_driver_wext_signal_poll,
+	.status = wpa_driver_wext_status,
+};
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_wext.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_wext.h
new file mode 100644
index 0000000..6214cdf
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_wext.h
@@ -0,0 +1,77 @@
+/*
+ * WPA Supplicant - driver_wext exported functions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DRIVER_WEXT_H
+#define DRIVER_WEXT_H
+
+#include <net/if.h>
+
+struct wpa_driver_wext_data {
+	void *ctx;
+	struct netlink_data *netlink;
+	int ioctl_sock;
+	int mlme_sock;
+	char ifname[IFNAMSIZ + 1];
+	char phyname[32];
+	int ifindex;
+	int ifindex2;
+	int if_removed;
+	int if_disabled;
+	struct rfkill_data *rfkill;
+	u8 *assoc_req_ies;
+	size_t assoc_req_ies_len;
+	u8 *assoc_resp_ies;
+	size_t assoc_resp_ies_len;
+	struct wpa_driver_capa capa;
+	int has_capability;
+	int we_version_compiled;
+
+	/* for set_auth_alg fallback */
+	int use_crypt;
+	int auth_alg_fallback;
+
+	int operstate;
+
+	char mlmedev[IFNAMSIZ + 1];
+
+	int scan_complete_events;
+
+	int cfg80211; /* whether driver is using cfg80211 */
+
+	u8 max_level;
+};
+
+int wpa_driver_wext_get_bssid(void *priv, u8 *bssid);
+int wpa_driver_wext_set_bssid(void *priv, const u8 *bssid);
+int wpa_driver_wext_get_ssid(void *priv, u8 *ssid);
+int wpa_driver_wext_set_ssid(void *priv, const u8 *ssid, size_t ssid_len);
+int wpa_driver_wext_set_freq(void *priv, int freq);
+int wpa_driver_wext_set_mode(void *priv, int mode);
+int wpa_driver_wext_scan(void *priv, struct wpa_driver_scan_params *params);
+struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv);
+
+void wpa_driver_wext_scan_timeout(void *eloop_ctx, void *timeout_ctx);
+
+int wpa_driver_wext_alternative_ifindex(struct wpa_driver_wext_data *drv,
+					const char *ifname);
+
+void * wpa_driver_wext_init(void *ctx, const char *ifname);
+void wpa_driver_wext_deinit(void *priv);
+
+int wpa_driver_wext_set_operstate(void *priv, int state);
+int wpa_driver_wext_get_version(struct wpa_driver_wext_data *drv);
+
+int wpa_driver_wext_associate(void *priv,
+			      struct wpa_driver_associate_params *params);
+int wpa_driver_wext_get_capa(void *priv, struct wpa_driver_capa *capa);
+int wpa_driver_wext_set_auth_param(struct wpa_driver_wext_data *drv,
+				   int idx, u32 value);
+int wpa_driver_wext_cipher2wext(int cipher);
+int wpa_driver_wext_keymgmt2wext(int keymgmt);
+
+#endif /* DRIVER_WEXT_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_wired.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_wired.c
new file mode 100644
index 0000000..03366b8
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_wired.c
@@ -0,0 +1,407 @@
+/*
+ * Wired Ethernet driver interface
+ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004, Gunter Burchardt <tira@isx.de>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "driver.h"
+#include "driver_wired_common.h"
+
+#include <sys/ioctl.h>
+#undef IFNAMSIZ
+#include <net/if.h>
+#ifdef __linux__
+#include <netpacket/packet.h>
+#include <net/if_arp.h>
+#endif /* __linux__ */
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */
+#ifdef __sun__
+#include <sys/sockio.h>
+#endif /* __sun__ */
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct ieee8023_hdr {
+	u8 dest[6];
+	u8 src[6];
+	u16 ethertype;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+struct wpa_driver_wired_data {
+	struct driver_wired_common_data common;
+
+	int dhcp_sock; /* socket for dhcp packets */
+	int use_pae_group_addr;
+};
+
+
+/* TODO: detecting new devices should eventually be changed from using DHCP
+ * snooping to trigger on any packet from a new layer 2 MAC address, e.g.,
+ * based on ebtables, etc. */
+
+struct dhcp_message {
+	u_int8_t op;
+	u_int8_t htype;
+	u_int8_t hlen;
+	u_int8_t hops;
+	u_int32_t xid;
+	u_int16_t secs;
+	u_int16_t flags;
+	u_int32_t ciaddr;
+	u_int32_t yiaddr;
+	u_int32_t siaddr;
+	u_int32_t giaddr;
+	u_int8_t chaddr[16];
+	u_int8_t sname[64];
+	u_int8_t file[128];
+	u_int32_t cookie;
+	u_int8_t options[308]; /* 312 - cookie */
+};
+
+
+#ifdef __linux__
+static void handle_data(void *ctx, unsigned char *buf, size_t len)
+{
+#ifdef HOSTAPD
+	struct ieee8023_hdr *hdr;
+	u8 *pos, *sa;
+	size_t left;
+	union wpa_event_data event;
+
+	/* must contain at least ieee8023_hdr 6 byte source, 6 byte dest,
+	 * 2 byte ethertype */
+	if (len < 14) {
+		wpa_printf(MSG_MSGDUMP, "handle_data: too short (%lu)",
+			   (unsigned long) len);
+		return;
+	}
+
+	hdr = (struct ieee8023_hdr *) buf;
+
+	switch (ntohs(hdr->ethertype)) {
+	case ETH_P_PAE:
+		wpa_printf(MSG_MSGDUMP, "Received EAPOL packet");
+		sa = hdr->src;
+		os_memset(&event, 0, sizeof(event));
+		event.new_sta.addr = sa;
+		wpa_supplicant_event(ctx, EVENT_NEW_STA, &event);
+
+		pos = (u8 *) (hdr + 1);
+		left = len - sizeof(*hdr);
+		drv_event_eapol_rx(ctx, sa, pos, left);
+		break;
+
+	default:
+		wpa_printf(MSG_DEBUG, "Unknown ethertype 0x%04x in data frame",
+			   ntohs(hdr->ethertype));
+		break;
+	}
+#endif /* HOSTAPD */
+}
+
+
+static void handle_read(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	int len;
+	unsigned char buf[3000];
+
+	len = recv(sock, buf, sizeof(buf), 0);
+	if (len < 0) {
+		wpa_printf(MSG_ERROR, "recv: %s", strerror(errno));
+		return;
+	}
+
+	handle_data(eloop_ctx, buf, len);
+}
+
+
+static void handle_dhcp(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	int len;
+	unsigned char buf[3000];
+	struct dhcp_message *msg;
+	u8 *mac_address;
+	union wpa_event_data event;
+
+	len = recv(sock, buf, sizeof(buf), 0);
+	if (len < 0) {
+		wpa_printf(MSG_ERROR, "recv: %s", strerror(errno));
+		return;
+	}
+
+	/* must contain at least dhcp_message->chaddr */
+	if (len < 44) {
+		wpa_printf(MSG_MSGDUMP, "handle_dhcp: too short (%d)", len);
+		return;
+	}
+
+	msg = (struct dhcp_message *) buf;
+	mac_address = (u8 *) &(msg->chaddr);
+
+	wpa_printf(MSG_MSGDUMP, "Got DHCP broadcast packet from " MACSTR,
+		   MAC2STR(mac_address));
+
+	os_memset(&event, 0, sizeof(event));
+	event.new_sta.addr = mac_address;
+	wpa_supplicant_event(eloop_ctx, EVENT_NEW_STA, &event);
+}
+#endif /* __linux__ */
+
+
+static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr)
+{
+#ifdef __linux__
+	struct ifreq ifr;
+	struct sockaddr_ll addr;
+	struct sockaddr_in addr2;
+	int n = 1;
+
+	drv->common.sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE));
+	if (drv->common.sock < 0) {
+		wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	if (eloop_register_read_sock(drv->common.sock, handle_read,
+				     drv->common.ctx, NULL)) {
+		wpa_printf(MSG_INFO, "Could not register read socket");
+		return -1;
+	}
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name));
+	if (ioctl(drv->common.sock, SIOCGIFINDEX, &ifr) != 0) {
+		wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	os_memset(&addr, 0, sizeof(addr));
+	addr.sll_family = AF_PACKET;
+	addr.sll_ifindex = ifr.ifr_ifindex;
+	wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d",
+		   addr.sll_ifindex);
+
+	if (bind(drv->common.sock, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+	{
+		wpa_printf(MSG_ERROR, "bind: %s", strerror(errno));
+		return -1;
+	}
+
+	/* filter multicast address */
+	if (wired_multicast_membership(drv->common.sock, ifr.ifr_ifindex,
+				       pae_group_addr, 1) < 0) {
+		wpa_printf(MSG_ERROR, "wired: Failed to add multicast group "
+			   "membership");
+		return -1;
+	}
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name));
+	if (ioctl(drv->common.sock, SIOCGIFHWADDR, &ifr) != 0) {
+		wpa_printf(MSG_ERROR, "ioctl(SIOCGIFHWADDR): %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
+		wpa_printf(MSG_INFO, "Invalid HW-addr family 0x%04x",
+			   ifr.ifr_hwaddr.sa_family);
+		return -1;
+	}
+	os_memcpy(own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+	/* setup dhcp listen socket for sta detection */
+	if ((drv->dhcp_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+		wpa_printf(MSG_ERROR, "socket call failed for dhcp: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	if (eloop_register_read_sock(drv->dhcp_sock, handle_dhcp,
+				     drv->common.ctx, NULL)) {
+		wpa_printf(MSG_INFO, "Could not register read socket");
+		return -1;
+	}
+
+	os_memset(&addr2, 0, sizeof(addr2));
+	addr2.sin_family = AF_INET;
+	addr2.sin_port = htons(67);
+	addr2.sin_addr.s_addr = INADDR_ANY;
+
+	if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_REUSEADDR, (char *) &n,
+		       sizeof(n)) == -1) {
+		wpa_printf(MSG_ERROR, "setsockopt[SOL_SOCKET,SO_REUSEADDR]: %s",
+			   strerror(errno));
+		return -1;
+	}
+	if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BROADCAST, (char *) &n,
+		       sizeof(n)) == -1) {
+		wpa_printf(MSG_ERROR, "setsockopt[SOL_SOCKET,SO_BROADCAST]: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_ifrn.ifrn_name, drv->common.ifname, IFNAMSIZ);
+	if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BINDTODEVICE,
+		       (char *) &ifr, sizeof(ifr)) < 0) {
+		wpa_printf(MSG_ERROR,
+			   "setsockopt[SOL_SOCKET,SO_BINDTODEVICE]: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	if (bind(drv->dhcp_sock, (struct sockaddr *) &addr2,
+		 sizeof(struct sockaddr)) == -1) {
+		wpa_printf(MSG_ERROR, "bind: %s", strerror(errno));
+		return -1;
+	}
+
+	return 0;
+#else /* __linux__ */
+	return -1;
+#endif /* __linux__ */
+}
+
+
+static int wired_send_eapol(void *priv, const u8 *addr,
+			    const u8 *data, size_t data_len, int encrypt,
+			    const u8 *own_addr, u32 flags, int link_id)
+{
+	struct wpa_driver_wired_data *drv = priv;
+	struct ieee8023_hdr *hdr;
+	size_t len;
+	u8 *pos;
+	int res;
+
+	len = sizeof(*hdr) + data_len;
+	hdr = os_zalloc(len);
+	if (hdr == NULL) {
+		wpa_printf(MSG_INFO,
+			   "malloc() failed for wired_send_eapol(len=%lu)",
+			   (unsigned long) len);
+		return -1;
+	}
+
+	os_memcpy(hdr->dest, drv->use_pae_group_addr ? pae_group_addr : addr,
+		  ETH_ALEN);
+	os_memcpy(hdr->src, own_addr, ETH_ALEN);
+	hdr->ethertype = htons(ETH_P_PAE);
+
+	pos = (u8 *) (hdr + 1);
+	os_memcpy(pos, data, data_len);
+
+	res = send(drv->common.sock, (u8 *) hdr, len, 0);
+	os_free(hdr);
+
+	if (res < 0) {
+		wpa_printf(MSG_ERROR,
+			   "wired_send_eapol - packet len: %lu - failed: send: %s",
+			   (unsigned long) len, strerror(errno));
+	}
+
+	return res;
+}
+
+
+static void * wired_driver_hapd_init(struct hostapd_data *hapd,
+				     struct wpa_init_params *params)
+{
+	struct wpa_driver_wired_data *drv;
+
+	drv = os_zalloc(sizeof(struct wpa_driver_wired_data));
+	if (drv == NULL) {
+		wpa_printf(MSG_INFO,
+			   "Could not allocate memory for wired driver data");
+		return NULL;
+	}
+
+	drv->common.ctx = hapd;
+	os_strlcpy(drv->common.ifname, params->ifname,
+		   sizeof(drv->common.ifname));
+	drv->use_pae_group_addr = params->use_pae_group_addr;
+
+	if (wired_init_sockets(drv, params->own_addr)) {
+		os_free(drv);
+		return NULL;
+	}
+
+	return drv;
+}
+
+
+static void wired_driver_hapd_deinit(void *priv)
+{
+	struct wpa_driver_wired_data *drv = priv;
+
+	if (drv->common.sock >= 0) {
+		eloop_unregister_read_sock(drv->common.sock);
+		close(drv->common.sock);
+	}
+
+	if (drv->dhcp_sock >= 0) {
+		eloop_unregister_read_sock(drv->dhcp_sock);
+		close(drv->dhcp_sock);
+	}
+
+	os_free(drv);
+}
+
+
+static void * wpa_driver_wired_init(void *ctx, const char *ifname)
+{
+	struct wpa_driver_wired_data *drv;
+
+	drv = os_zalloc(sizeof(*drv));
+	if (drv == NULL)
+		return NULL;
+
+	if (driver_wired_init_common(&drv->common, ifname, ctx) < 0) {
+		os_free(drv);
+		return NULL;
+	}
+
+	return drv;
+}
+
+
+static void wpa_driver_wired_deinit(void *priv)
+{
+	struct wpa_driver_wired_data *drv = priv;
+
+	driver_wired_deinit_common(&drv->common);
+	os_free(drv);
+}
+
+
+const struct wpa_driver_ops wpa_driver_wired_ops = {
+	.name = "wired",
+	.desc = "Wired Ethernet driver",
+	.hapd_init = wired_driver_hapd_init,
+	.hapd_deinit = wired_driver_hapd_deinit,
+	.hapd_send_eapol = wired_send_eapol,
+	.get_ssid = driver_wired_get_ssid,
+	.get_bssid = driver_wired_get_bssid,
+	.get_capa = driver_wired_get_capa,
+	.init = wpa_driver_wired_init,
+	.deinit = wpa_driver_wired_deinit,
+};
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_wired_common.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_wired_common.c
new file mode 100644
index 0000000..a860b1c
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_wired_common.c
@@ -0,0 +1,322 @@
+/*
+ * Common functions for Wired Ethernet driver interfaces
+ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004, Gunter Burchardt <tira@isx.de>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "driver.h"
+#include "driver_wired_common.h"
+
+#include <sys/ioctl.h>
+#include <net/if.h>
+#ifdef __linux__
+#include <netpacket/packet.h>
+#include <net/if_arp.h>
+#include <net/if.h>
+#endif /* __linux__ */
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */
+#ifdef __sun__
+#include <sys/sockio.h>
+#endif /* __sun__ */
+
+
+static int driver_wired_get_ifflags(const char *ifname, int *flags)
+{
+	struct ifreq ifr;
+	int s;
+
+	s = socket(PF_INET, SOCK_DGRAM, 0);
+	if (s < 0) {
+		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+		return -1;
+	}
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+	if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s",
+			   strerror(errno));
+		close(s);
+		return -1;
+	}
+	close(s);
+	*flags = ifr.ifr_flags & 0xffff;
+	return 0;
+}
+
+
+static int driver_wired_set_ifflags(const char *ifname, int flags)
+{
+	struct ifreq ifr;
+	int s;
+
+	s = socket(PF_INET, SOCK_DGRAM, 0);
+	if (s < 0) {
+		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+		return -1;
+	}
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+	ifr.ifr_flags = flags & 0xffff;
+	if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s",
+			   strerror(errno));
+		close(s);
+		return -1;
+	}
+	close(s);
+	return 0;
+}
+
+
+static int driver_wired_multi(const char *ifname, const u8 *addr, int add)
+{
+	struct ifreq ifr;
+	int s;
+
+#ifdef __sun__
+	return -1;
+#endif /* __sun__ */
+
+	s = socket(PF_INET, SOCK_DGRAM, 0);
+	if (s < 0) {
+		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+		return -1;
+	}
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+#ifdef __linux__
+	ifr.ifr_hwaddr.sa_family = AF_UNSPEC;
+	os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN);
+#endif /* __linux__ */
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+	{
+		struct sockaddr_dl *dlp;
+
+		dlp = (struct sockaddr_dl *) &ifr.ifr_addr;
+		dlp->sdl_len = sizeof(struct sockaddr_dl);
+		dlp->sdl_family = AF_LINK;
+		dlp->sdl_index = 0;
+		dlp->sdl_nlen = 0;
+		dlp->sdl_alen = ETH_ALEN;
+		dlp->sdl_slen = 0;
+		os_memcpy(LLADDR(dlp), addr, ETH_ALEN);
+	}
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
+#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
+	{
+		struct sockaddr *sap;
+
+		sap = (struct sockaddr *) &ifr.ifr_addr;
+		sap->sa_len = sizeof(struct sockaddr);
+		sap->sa_family = AF_UNSPEC;
+		os_memcpy(sap->sa_data, addr, ETH_ALEN);
+	}
+#endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */
+
+	if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOC{ADD/DEL}MULTI]: %s",
+			   strerror(errno));
+		close(s);
+		return -1;
+	}
+	close(s);
+	return 0;
+}
+
+
+int wired_multicast_membership(int sock, int ifindex, const u8 *addr, int add)
+{
+#ifdef __linux__
+	struct packet_mreq mreq;
+
+	if (sock < 0)
+		return -1;
+
+	os_memset(&mreq, 0, sizeof(mreq));
+	mreq.mr_ifindex = ifindex;
+	mreq.mr_type = PACKET_MR_MULTICAST;
+	mreq.mr_alen = ETH_ALEN;
+	os_memcpy(mreq.mr_address, addr, ETH_ALEN);
+
+	if (setsockopt(sock, SOL_PACKET,
+		       add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP,
+		       &mreq, sizeof(mreq)) < 0) {
+		wpa_printf(MSG_ERROR, "setsockopt: %s", strerror(errno));
+		return -1;
+	}
+	return 0;
+#else /* __linux__ */
+	return -1;
+#endif /* __linux__ */
+}
+
+
+int driver_wired_get_ssid(void *priv, u8 *ssid)
+{
+	ssid[0] = 0;
+	return 0;
+}
+
+
+int driver_wired_get_bssid(void *priv, u8 *bssid)
+{
+	/* Report PAE group address as the "BSSID" for wired connection. */
+	os_memcpy(bssid, pae_group_addr, ETH_ALEN);
+	return 0;
+}
+
+
+int driver_wired_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+	os_memset(capa, 0, sizeof(*capa));
+	capa->flags = WPA_DRIVER_FLAGS_WIRED;
+	return 0;
+}
+
+
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+static int driver_wired_get_ifstatus(const char *ifname, int *status)
+{
+	struct ifmediareq ifmr;
+	int s;
+
+	s = socket(PF_INET, SOCK_DGRAM, 0);
+	if (s < 0) {
+		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+		return -1;
+	}
+
+	os_memset(&ifmr, 0, sizeof(ifmr));
+	os_strlcpy(ifmr.ifm_name, ifname, IFNAMSIZ);
+	if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIFMEDIA]: %s",
+			   strerror(errno));
+		close(s);
+		return -1;
+	}
+	close(s);
+	*status = (ifmr.ifm_status & (IFM_ACTIVE | IFM_AVALID)) ==
+		(IFM_ACTIVE | IFM_AVALID);
+
+	return 0;
+}
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
+
+
+int driver_wired_init_common(struct driver_wired_common_data *common,
+			     const char *ifname, void *ctx)
+{
+	int flags;
+
+	os_strlcpy(common->ifname, ifname, sizeof(common->ifname));
+	common->ctx = ctx;
+
+#ifdef __linux__
+	common->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
+	if (common->pf_sock < 0)
+		wpa_printf(MSG_ERROR, "socket(PF_PACKET): %s", strerror(errno));
+#else /* __linux__ */
+	common->pf_sock = -1;
+#endif /* __linux__ */
+
+	if (driver_wired_get_ifflags(ifname, &flags) == 0 &&
+	    !(flags & IFF_UP) &&
+	    driver_wired_set_ifflags(ifname, flags | IFF_UP) == 0)
+		common->iff_up = 1;
+
+	if (wired_multicast_membership(common->pf_sock,
+				       if_nametoindex(common->ifname),
+				       pae_group_addr, 1) == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "%s: Added multicast membership with packet socket",
+			   __func__);
+		common->membership = 1;
+	} else if (driver_wired_multi(ifname, pae_group_addr, 1) == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "%s: Added multicast membership with SIOCADDMULTI",
+			   __func__);
+		common->multi = 1;
+	} else if (driver_wired_get_ifflags(ifname, &flags) < 0) {
+		wpa_printf(MSG_INFO, "%s: Could not get interface flags",
+			   __func__);
+		return -1;
+	} else if (flags & IFF_ALLMULTI) {
+		wpa_printf(MSG_DEBUG,
+			   "%s: Interface is already configured for multicast",
+			   __func__);
+	} else if (driver_wired_set_ifflags(ifname,
+						flags | IFF_ALLMULTI) < 0) {
+		wpa_printf(MSG_INFO, "%s: Failed to enable allmulti", __func__);
+		return -1;
+	} else {
+		wpa_printf(MSG_DEBUG, "%s: Enabled allmulti mode", __func__);
+		common->iff_allmulti = 1;
+	}
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+	{
+		int status;
+
+		wpa_printf(MSG_DEBUG, "%s: waiting for link to become active",
+			   __func__);
+		while (driver_wired_get_ifstatus(ifname, &status) == 0 &&
+		       status == 0)
+			sleep(1);
+	}
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
+
+	return 0;
+}
+
+
+void driver_wired_deinit_common(struct driver_wired_common_data *common)
+{
+	int flags;
+
+	if (common->membership &&
+	    wired_multicast_membership(common->pf_sock,
+				       if_nametoindex(common->ifname),
+				       pae_group_addr, 0) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "%s: Failed to remove PAE multicast group (PACKET)",
+			   __func__);
+	}
+
+	if (common->multi &&
+	    driver_wired_multi(common->ifname, pae_group_addr, 0) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "%s: Failed to remove PAE multicast group (SIOCDELMULTI)",
+			   __func__);
+	}
+
+	if (common->iff_allmulti &&
+	    (driver_wired_get_ifflags(common->ifname, &flags) < 0 ||
+	     driver_wired_set_ifflags(common->ifname,
+				      flags & ~IFF_ALLMULTI) < 0)) {
+		wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode",
+			   __func__);
+	}
+
+	if (common->iff_up &&
+	    driver_wired_get_ifflags(common->ifname, &flags) == 0 &&
+	    (flags & IFF_UP) &&
+	    driver_wired_set_ifflags(common->ifname, flags & ~IFF_UP) < 0) {
+		wpa_printf(MSG_DEBUG, "%s: Failed to set the interface down",
+			   __func__);
+	}
+
+	if (common->pf_sock != -1)
+		close(common->pf_sock);
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_wired_common.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_wired_common.h
new file mode 100644
index 0000000..2bb0710
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/driver_wired_common.h
@@ -0,0 +1,34 @@
+/*
+ * Common definitions for Wired Ethernet driver interfaces
+ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004, Gunter Burchardt <tira@isx.de>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DRIVER_WIRED_COMMON_H
+#define DRIVER_WIRED_COMMON_H
+
+struct driver_wired_common_data {
+	char ifname[IFNAMSIZ + 1];
+	void *ctx;
+
+	int sock; /* raw packet socket for driver access */
+	int pf_sock;
+	int membership, multi, iff_allmulti, iff_up;
+};
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+int wired_multicast_membership(int sock, int ifindex, const u8 *addr, int add);
+int driver_wired_get_ssid(void *priv, u8 *ssid);
+int driver_wired_get_bssid(void *priv, u8 *bssid);
+int driver_wired_get_capa(void *priv, struct wpa_driver_capa *capa);
+
+int driver_wired_init_common(struct driver_wired_common_data *common,
+			     const char *ifname, void *ctx);
+void driver_wired_deinit_common(struct driver_wired_common_data *common);
+
+#endif /* DRIVER_WIRED_COMMON_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/drivers.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/drivers.c
new file mode 100644
index 0000000..9071da3
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/drivers.c
@@ -0,0 +1,57 @@
+/*
+ * Driver interface list
+ * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "driver.h"
+
+void (*wpa_supplicant_event)(void *ctx, enum wpa_event_type event,
+			     union wpa_event_data *data);
+void (*wpa_supplicant_event_global)(void *ctx, enum wpa_event_type event,
+			     union wpa_event_data *data);
+
+const struct wpa_driver_ops *const wpa_drivers[] =
+{
+#ifdef CONFIG_DRIVER_NL80211
+	&wpa_driver_nl80211_ops,
+#endif /* CONFIG_DRIVER_NL80211 */
+#ifdef CONFIG_DRIVER_WEXT
+	&wpa_driver_wext_ops,
+#endif /* CONFIG_DRIVER_WEXT */
+#ifdef CONFIG_DRIVER_HOSTAP
+	&wpa_driver_hostap_ops,
+#endif /* CONFIG_DRIVER_HOSTAP */
+#ifdef CONFIG_DRIVER_BSD
+	&wpa_driver_bsd_ops,
+#endif /* CONFIG_DRIVER_BSD */
+#ifdef CONFIG_DRIVER_OPENBSD
+	&wpa_driver_openbsd_ops,
+#endif /* CONFIG_DRIVER_OPENBSD */
+#ifdef CONFIG_DRIVER_NDIS
+	&wpa_driver_ndis_ops,
+#endif /* CONFIG_DRIVER_NDIS */
+#ifdef CONFIG_DRIVER_WIRED
+	&wpa_driver_wired_ops,
+#endif /* CONFIG_DRIVER_WIRED */
+#ifdef CONFIG_DRIVER_MACSEC_LINUX
+	&wpa_driver_macsec_linux_ops,
+#endif /* CONFIG_DRIVER_MACSEC_LINUX */
+#ifdef CONFIG_DRIVER_MACSEC_QCA
+	&wpa_driver_macsec_qca_ops,
+#endif /* CONFIG_DRIVER_MACSEC_QCA */
+#ifdef CONFIG_DRIVER_ROBOSWITCH
+	&wpa_driver_roboswitch_ops,
+#endif /* CONFIG_DRIVER_ROBOSWITCH */
+#ifdef CONFIG_DRIVER_ATHEROS
+	&wpa_driver_atheros_ops,
+#endif /* CONFIG_DRIVER_ATHEROS */
+#ifdef CONFIG_DRIVER_NONE
+	&wpa_driver_none_ops,
+#endif /* CONFIG_DRIVER_NONE */
+	NULL
+};
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/drivers.mak b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/drivers.mak
new file mode 100644
index 0000000..8da44d9
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/drivers.mak
@@ -0,0 +1,218 @@
+##### CLEAR VARS
+
+DRV_CFLAGS =
+DRV_WPA_CFLAGS =
+DRV_AP_CFLAGS =
+DRV_OBJS =
+DRV_WPA_OBJS =
+DRV_AP_OBJS =
+DRV_LIBS =
+DRV_WPA_LIBS =
+DRV_AP_LIBS =
+
+##### COMMON DRIVERS
+
+ifdef CONFIG_DRIVER_WIRED
+DRV_CFLAGS += -DCONFIG_DRIVER_WIRED
+DRV_OBJS += ../src/drivers/driver_wired.o
+NEED_DRV_WIRED_COMMON=1
+endif
+
+ifdef CONFIG_DRIVER_MACSEC_LINUX
+DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_LINUX
+DRV_OBJS += ../src/drivers/driver_macsec_linux.o
+NEED_DRV_WIRED_COMMON=1
+NEED_LIBNL=y
+CONFIG_LIBNL3_ROUTE=y
+endif
+
+ifdef CONFIG_DRIVER_NL80211_BRCM
+DRV_CFLAGS += -DCONFIG_DRIVER_NL80211_BRCM
+endif
+
+ifdef CONFIG_DRIVER_MACSEC_QCA
+DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_QCA
+DRV_OBJS += ../src/drivers/driver_macsec_qca.o
+NEED_DRV_WIRED_COMMON=1
+endif
+
+ifdef NEED_DRV_WIRED_COMMON
+DRV_OBJS += ../src/drivers/driver_wired_common.o
+endif
+
+ifdef CONFIG_DRIVER_NL80211
+DRV_CFLAGS += -DCONFIG_DRIVER_NL80211
+DRV_OBJS += ../src/drivers/driver_nl80211.o
+DRV_OBJS += ../src/drivers/driver_nl80211_capa.o
+DRV_OBJS += ../src/drivers/driver_nl80211_event.o
+DRV_OBJS += ../src/drivers/driver_nl80211_monitor.o
+DRV_OBJS += ../src/drivers/driver_nl80211_scan.o
+ifdef CONFIG_DRIVER_NL80211_QCA
+DRV_CFLAGS += -DCONFIG_DRIVER_NL80211_QCA
+endif
+NEED_SME=y
+NEED_AP_MLME=y
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+NEED_RADIOTAP=y
+NEED_LIBNL=y
+endif
+
+ifdef CONFIG_DRIVER_BSD
+ifndef CONFIG_L2_PACKET
+CONFIG_L2_PACKET=freebsd
+endif
+DRV_CFLAGS += -DCONFIG_DRIVER_BSD
+DRV_OBJS += ../src/drivers/driver_bsd.o
+CONFIG_L2_FREEBSD=y
+CONFIG_DNET_PCAP=y
+endif
+
+ifdef CONFIG_DRIVER_OPENBSD
+ifndef CONFIG_L2_PACKET
+CONFIG_L2_PACKET=freebsd
+endif
+DRV_CFLAGS += -DCONFIG_DRIVER_OPENBSD
+DRV_OBJS += ../src/drivers/driver_openbsd.o
+endif
+
+ifdef CONFIG_DRIVER_NONE
+DRV_CFLAGS += -DCONFIG_DRIVER_NONE
+DRV_OBJS += ../src/drivers/driver_none.o
+endif
+
+##### PURE AP DRIVERS
+
+ifdef CONFIG_DRIVER_HOSTAP
+DRV_AP_CFLAGS += -DCONFIG_DRIVER_HOSTAP
+DRV_AP_OBJS += ../src/drivers/driver_hostap.o
+CONFIG_WIRELESS_EXTENSION=y
+NEED_AP_MLME=y
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+endif
+
+ifdef CONFIG_DRIVER_ATHEROS
+DRV_AP_CFLAGS += -DCONFIG_DRIVER_ATHEROS
+DRV_AP_OBJS += ../src/drivers/driver_atheros.o
+CONFIG_L2_PACKET=linux
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+ifdef ATH_GCM_SUPPORT
+CFLAGS += -DATH_GCM_SUPPORT
+endif
+endif
+
+##### PURE CLIENT DRIVERS
+
+ifdef CONFIG_DRIVER_WEXT
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_WEXT
+CONFIG_WIRELESS_EXTENSION=y
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+endif
+
+ifdef CONFIG_DRIVER_NDIS
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_NDIS
+DRV_WPA_OBJS += ../src/drivers/driver_ndis.o
+ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+DRV_WPA_OBJS += ../src/drivers/driver_ndis_.o
+endif
+ifndef CONFIG_L2_PACKET
+CONFIG_L2_PACKET=pcap
+endif
+CONFIG_WINPCAP=y
+ifdef CONFIG_USE_NDISUIO
+DRV_WPA_CFLAGS += -DCONFIG_USE_NDISUIO
+endif
+endif
+
+ifdef CONFIG_DRIVER_ROBOSWITCH
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_ROBOSWITCH
+DRV_WPA_OBJS += ../src/drivers/driver_roboswitch.o
+endif
+
+ifdef CONFIG_WIRELESS_EXTENSION
+DRV_WPA_CFLAGS += -DCONFIG_WIRELESS_EXTENSION
+DRV_WPA_OBJS += ../src/drivers/driver_wext.o
+endif
+
+ifdef NEED_NETLINK
+DRV_OBJS += ../src/drivers/netlink.o
+endif
+
+ifdef NEED_RFKILL
+DRV_OBJS += ../src/drivers/rfkill.o
+DRV_WPA_CFLAGS += -DCONFIG_RFKILL
+endif
+
+ifdef NEED_RADIOTAP
+DRV_OBJS += ../src/utils/radiotap.o
+endif
+
+ifdef CONFIG_FULL_DYNAMIC_VLAN
+NEED_LINUX_IOCTL=y
+ifdef CONFIG_VLAN_NETLINK
+NEED_LIBNL=y
+CONFIG_LIBNL3_ROUTE=y
+endif
+endif
+
+ifdef NEED_LINUX_IOCTL
+DRV_OBJS += ../src/drivers/linux_ioctl.o
+endif
+
+ifdef NEED_LIBNL
+ifndef CONFIG_LIBNL32
+ifndef CONFIG_LIBNL20
+ifndef CONFIG_LIBNL_TINY
+PKG_CONFIG ?= pkg-config
+HAVE_LIBNL3 := $(shell $(PKG_CONFIG) --exists libnl-3.0; echo $$?)
+ifeq ($(HAVE_LIBNL3),0)
+CONFIG_LIBNL32=y
+endif
+endif
+endif
+endif
+
+ifdef CONFIG_LIBNL32
+  DRV_LIBS += -lnl-3
+  DRV_LIBS += -lnl-genl-3
+  ifdef LIBNL_INC
+    DRV_CFLAGS += -I$(LIBNL_INC)
+  else
+    PKG_CONFIG ?= pkg-config
+    DRV_CFLAGS += $(shell $(PKG_CONFIG) --cflags libnl-3.0)
+  endif
+  ifdef CONFIG_LIBNL3_ROUTE
+    DRV_LIBS += -lnl-route-3
+    DRV_CFLAGS += -DCONFIG_LIBNL3_ROUTE
+  endif
+else
+  ifdef CONFIG_LIBNL_TINY
+    DRV_LIBS += -lnl-tiny
+  else
+    ifndef CONFIG_OSX
+      DRV_LIBS += -lnl
+      DRV_LIBS += -lnl-genl
+    endif
+  endif
+endif
+endif
+
+##### COMMON VARS
+DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS)
+DRV_WPA_CFLAGS += $(DRV_CFLAGS)
+DRV_AP_CFLAGS += $(DRV_CFLAGS)
+
+DRV_BOTH_LIBS := $(DRV_LIBS) $(DRV_WPA_LIBS) $(DRV_AP_LIBS)
+DRV_WPA_LIBS += $(DRV_LIBS)
+DRV_AP_LIBS += $(DRV_LIBS)
+
+DRV_BOTH_OBJS := $(DRV_OBJS) $(DRV_WPA_OBJS) $(DRV_AP_OBJS)
+DRV_WPA_OBJS += $(DRV_OBJS)
+DRV_AP_OBJS += $(DRV_OBJS)
+
+DRV_BOTH_LDFLAGS := $(DRV_LDFLAGS) $(DRV_WPA_LDFLAGS) $(DRV_AP_LDFLAGS)
+DRV_WPA_LDFLAGS += $(DRV_LDFLAGS)
+DRV_AP_LDFLAGS += $(DRV_LDFLAGS)
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/drivers.mk b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/drivers.mk
new file mode 100644
index 0000000..10eab6a
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/drivers.mk
@@ -0,0 +1,196 @@
+##### CLEAR VARS
+
+DRV_CFLAGS =
+DRV_WPA_CFLAGS =
+DRV_AP_CFLAGS =
+DRV_OBJS =
+DRV_WPA_OBJS =
+DRV_AP_OBJS =
+DRV_LIBS =
+DRV_WPA_LIBS =
+DRV_AP_LIBS =
+
+##### COMMON DRIVERS
+
+ifdef CONFIG_DRIVER_WIRED
+DRV_CFLAGS += -DCONFIG_DRIVER_WIRED
+DRV_OBJS += src/drivers/driver_wired.c
+NEED_DRV_WIRED_COMMON=1
+endif
+
+ifdef CONFIG_DRIVER_MACSEC_LINUX
+DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_LINUX
+DRV_OBJS += src/drivers/driver_macsec_linux.c
+NEED_DRV_WIRED_COMMON=1
+CONFIG_LIBNL3_ROUTE=y
+NEED_LIBNL=y
+endif
+
+ifdef NEED_DRV_WIRED_COMMON
+DRV_OBJS += src/drivers/driver_wired_common.c
+endif
+
+ifdef CONFIG_DRIVER_NL80211
+DRV_CFLAGS += -DCONFIG_DRIVER_NL80211
+DRV_OBJS += src/drivers/driver_nl80211.c
+DRV_OBJS += src/drivers/driver_nl80211_android.c
+DRV_OBJS += src/drivers/driver_nl80211_capa.c
+DRV_OBJS += src/drivers/driver_nl80211_event.c
+DRV_OBJS += src/drivers/driver_nl80211_monitor.c
+DRV_OBJS += src/drivers/driver_nl80211_scan.c
+ifdef CONFIG_DRIVER_NL80211_QCA
+DRV_CFLAGS += -DCONFIG_DRIVER_NL80211_QCA
+endif
+ifdef CONFIG_DRIVER_NL80211_BRCM
+DRV_CFLAGS += -DCONFIG_DRIVER_NL80211_BRCM
+endif
+NEED_SME=y
+NEED_AP_MLME=y
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+NEED_RFKILL=y
+NEED_RADIOTAP=y
+NEED_LIBNL=y
+endif
+
+ifdef CONFIG_DRIVER_BSD
+ifndef CONFIG_L2_PACKET
+CONFIG_L2_PACKET=freebsd
+endif
+DRV_CFLAGS += -DCONFIG_DRIVER_BSD
+DRV_OBJS += src/drivers/driver_bsd.c
+CONFIG_L2_FREEBSD=y
+CONFIG_DNET_PCAP=y
+endif
+
+ifdef CONFIG_DRIVER_OPENBSD
+ifndef CONFIG_L2_PACKET
+CONFIG_L2_PACKET=freebsd
+endif
+DRV_CFLAGS += -DCONFIG_DRIVER_OPENBSD
+DRV_OBJS += src/drivers/driver_openbsd.c
+endif
+
+ifdef CONFIG_DRIVER_NONE
+DRV_CFLAGS += -DCONFIG_DRIVER_NONE
+DRV_OBJS += src/drivers/driver_none.c
+endif
+
+##### PURE AP DRIVERS
+
+ifdef CONFIG_DRIVER_HOSTAP
+DRV_AP_CFLAGS += -DCONFIG_DRIVER_HOSTAP
+DRV_AP_OBJS += src/drivers/driver_hostap.c
+CONFIG_WIRELESS_EXTENSION=y
+NEED_AP_MLME=y
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+endif
+
+ifdef CONFIG_DRIVER_ATHEROS
+DRV_AP_CFLAGS += -DCONFIG_DRIVER_ATHEROS
+DRV_AP_OBJS += src/drivers/driver_atheros.c
+CONFIG_L2_PACKET=linux
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+endif
+
+##### PURE CLIENT DRIVERS
+
+ifdef CONFIG_DRIVER_WEXT
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_WEXT
+CONFIG_WIRELESS_EXTENSION=y
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+NEED_RFKILL=y
+endif
+
+ifdef CONFIG_DRIVER_NDIS
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_NDIS
+DRV_WPA_OBJS += src/drivers/driver_ndis.c
+ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+DRV_WPA_OBJS += src/drivers/driver_ndis_.c
+endif
+ifndef CONFIG_L2_PACKET
+CONFIG_L2_PACKET=pcap
+endif
+CONFIG_WINPCAP=y
+ifdef CONFIG_USE_NDISUIO
+DRV_WPA_CFLAGS += -DCONFIG_USE_NDISUIO
+endif
+endif
+
+ifdef CONFIG_DRIVER_ROBOSWITCH
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_ROBOSWITCH
+DRV_WPA_OBJS += src/drivers/driver_roboswitch.c
+endif
+
+ifdef CONFIG_WIRELESS_EXTENSION
+DRV_WPA_CFLAGS += -DCONFIG_WIRELESS_EXTENSION
+DRV_WPA_OBJS += src/drivers/driver_wext.c
+NEED_RFKILL=y
+endif
+
+ifdef NEED_NETLINK
+DRV_OBJS += src/drivers/netlink.c
+endif
+
+ifdef NEED_RFKILL
+DRV_OBJS += src/drivers/rfkill.c
+endif
+
+ifdef NEED_RADIOTAP
+DRV_OBJS += src/utils/radiotap.c
+endif
+
+ifdef CONFIG_DRIVER_CUSTOM
+DRV_CFLAGS += -DCONFIG_DRIVER_CUSTOM
+endif
+
+ifdef CONFIG_FULL_DYNAMIC_VLAN
+NEED_LINUX_IOCTL=y
+ifdef CONFIG_VLAN_NETLINK
+NEED_LIBNL=y
+CONFIG_LIBNL3_ROUTE=y
+endif
+endif
+
+ifdef NEED_LINUX_IOCTL
+DRV_OBJS += src/drivers/linux_ioctl.c
+endif
+
+ifdef NEED_LIBNL
+ifdef CONFIG_LIBNL32
+  DRV_LIBS += -lnl-3
+  DRV_LIBS += -lnl-genl-3
+  DRV_CFLAGS += -I/usr/include/libnl3
+ifdef CONFIG_LIBNL3_ROUTE
+  DRV_LIBS += -lnl-route-3
+  DRV_CFLAGS += -DCONFIG_LIBNL3_ROUTE
+endif
+else
+  ifdef CONFIG_LIBNL_TINY
+    DRV_LIBS += -lnl-tiny
+  else
+    DRV_LIBS += -lnl
+    DRV_LIBS += -lnl-genl
+  endif
+endif
+endif
+
+##### COMMON VARS
+DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS)
+DRV_WPA_CFLAGS += $(DRV_CFLAGS)
+DRV_AP_CFLAGS += $(DRV_CFLAGS)
+
+DRV_BOTH_LIBS := $(DRV_LIBS) $(DRV_WPA_LIBS) $(DRV_AP_LIBS)
+DRV_WPA_LIBS += $(DRV_LIBS)
+DRV_AP_LIBS += $(DRV_LIBS)
+
+DRV_BOTH_OBJS := $(DRV_OBJS) $(DRV_WPA_OBJS) $(DRV_AP_OBJS)
+DRV_WPA_OBJS += $(DRV_OBJS)
+DRV_AP_OBJS += $(DRV_OBJS)
+
+DRV_BOTH_LDFLAGS := $(DRV_LDFLAGS) $(DRV_WPA_LDFLAGS) $(DRV_AP_LDFLAGS)
+DRV_WPA_LDFLAGS += $(DRV_LDFLAGS)
+DRV_AP_LDFLAGS += $(DRV_LDFLAGS)
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/linux_defines.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/linux_defines.h
new file mode 100644
index 0000000..a107479
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/linux_defines.h
@@ -0,0 +1,46 @@
+/*
+ * Linux defines for values that are not yet included in common C libraries
+ * Copyright (c) 2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef LINUX_DEFINES_H
+#define LINUX_DEFINES_H
+
+#ifndef SO_WIFI_STATUS
+# if defined(__sparc__)
+#  define SO_WIFI_STATUS	0x0025
+# elif defined(__parisc__)
+#  define SO_WIFI_STATUS	0x4022
+# else
+#  define SO_WIFI_STATUS	41
+# endif
+
+# define SCM_WIFI_STATUS	SO_WIFI_STATUS
+#endif
+
+#ifndef SO_EE_ORIGIN_TXSTATUS
+#define SO_EE_ORIGIN_TXSTATUS	4
+#endif
+
+#ifndef PACKET_TX_TIMESTAMP
+#define PACKET_TX_TIMESTAMP	16
+#endif
+
+#ifndef IFF_LOWER_UP
+#define IFF_LOWER_UP   0x10000         /* driver signals L1 up         */
+#endif
+#ifndef IFF_DORMANT
+#define IFF_DORMANT    0x20000         /* driver signals dormant       */
+#endif
+
+#ifndef IF_OPER_DORMANT
+#define IF_OPER_DORMANT 5
+#endif
+#ifndef IF_OPER_UP
+#define IF_OPER_UP 6
+#endif
+
+#endif /* LINUX_DEFINES_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/linux_ioctl.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/linux_ioctl.c
new file mode 100644
index 0000000..7edb9df
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/linux_ioctl.c
@@ -0,0 +1,237 @@
+/*
+ * Linux ioctl helper functions for driver wrappers
+ * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+
+#include "utils/common.h"
+#include "common/linux_bridge.h"
+#include "linux_ioctl.h"
+
+
+int linux_set_iface_flags(int sock, const char *ifname, int dev_up)
+{
+	struct ifreq ifr;
+	int ret;
+
+	if (sock < 0)
+		return -1;
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+
+	if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) {
+		ret = errno ? -errno : -999;
+		wpa_printf(MSG_ERROR, "Could not read interface %s flags: %s",
+			   ifname, strerror(errno));
+		return ret;
+	}
+
+	if (dev_up) {
+		if (ifr.ifr_flags & IFF_UP)
+			return 0;
+		ifr.ifr_flags |= IFF_UP;
+	} else {
+		if (!(ifr.ifr_flags & IFF_UP))
+			return 0;
+		ifr.ifr_flags &= ~IFF_UP;
+	}
+
+	if (ioctl(sock, SIOCSIFFLAGS, &ifr) != 0) {
+		ret = errno ? -errno : -999;
+		wpa_printf(MSG_ERROR, "Could not set interface %s flags (%s): "
+			   "%s",
+			   ifname, dev_up ? "UP" : "DOWN", strerror(errno));
+		return ret;
+	}
+
+	return 0;
+}
+
+
+int linux_iface_up(int sock, const char *ifname)
+{
+	struct ifreq ifr;
+	int ret;
+
+	if (sock < 0)
+		return -1;
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+
+	if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) {
+		ret = errno ? -errno : -999;
+		wpa_printf(MSG_ERROR, "Could not read interface %s flags: %s",
+			   ifname, strerror(errno));
+		return ret;
+	}
+
+	return !!(ifr.ifr_flags & IFF_UP);
+}
+
+
+int linux_get_ifhwaddr(int sock, const char *ifname, u8 *addr)
+{
+	struct ifreq ifr;
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+	if (ioctl(sock, SIOCGIFHWADDR, &ifr)) {
+		wpa_printf(MSG_ERROR, "Could not get interface %s hwaddr: %s",
+			   ifname, strerror(errno));
+		return -1;
+	}
+
+	if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
+		wpa_printf(MSG_ERROR, "%s: Invalid HW-addr family 0x%04x",
+			   ifname, ifr.ifr_hwaddr.sa_family);
+		return -1;
+	}
+	os_memcpy(addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+	return 0;
+}
+
+
+int linux_set_ifhwaddr(int sock, const char *ifname, const u8 *addr)
+{
+	struct ifreq ifr;
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+	os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN);
+	ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
+
+	if (ioctl(sock, SIOCSIFHWADDR, &ifr)) {
+		wpa_printf(MSG_DEBUG, "Could not set interface %s hwaddr: %s",
+			   ifname, strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int linux_br_add(int sock, const char *brname)
+{
+	if (ioctl(sock, SIOCBRADDBR, brname) < 0) {
+		int saved_errno = errno;
+
+		wpa_printf(MSG_DEBUG, "Could not add bridge %s: %s",
+			   brname, strerror(errno));
+		errno = saved_errno;
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int linux_br_del(int sock, const char *brname)
+{
+	if (ioctl(sock, SIOCBRDELBR, brname) < 0) {
+		wpa_printf(MSG_DEBUG, "Could not remove bridge %s: %s",
+			   brname, strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int linux_br_add_if(int sock, const char *brname, const char *ifname)
+{
+	struct ifreq ifr;
+	int ifindex;
+
+	ifindex = if_nametoindex(ifname);
+	if (ifindex == 0)
+		return -1;
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, brname, IFNAMSIZ);
+	ifr.ifr_ifindex = ifindex;
+	if (ioctl(sock, SIOCBRADDIF, &ifr) < 0) {
+		int saved_errno = errno;
+
+		wpa_printf(MSG_DEBUG, "Could not add interface %s into bridge "
+			   "%s: %s", ifname, brname, strerror(errno));
+		errno = saved_errno;
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int linux_br_del_if(int sock, const char *brname, const char *ifname)
+{
+	struct ifreq ifr;
+	int ifindex;
+
+	ifindex = if_nametoindex(ifname);
+	if (ifindex == 0)
+		return -1;
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, brname, IFNAMSIZ);
+	ifr.ifr_ifindex = ifindex;
+	if (ioctl(sock, SIOCBRDELIF, &ifr) < 0) {
+		wpa_printf(MSG_DEBUG, "Could not remove interface %s from "
+			   "bridge %s: %s", ifname, brname, strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int linux_br_get(char *brname, const char *ifname)
+{
+	char path[128], brlink[128], *pos;
+	ssize_t res;
+
+	os_snprintf(path, sizeof(path), "/sys/class/net/%s/brport/bridge",
+		    ifname);
+	res = readlink(path, brlink, sizeof(brlink));
+	if (res < 0 || (size_t) res >= sizeof(brlink))
+		return -1;
+	brlink[res] = '\0';
+	pos = os_strrchr(brlink, '/');
+	if (pos == NULL)
+		return -1;
+	pos++;
+	os_strlcpy(brname, pos, IFNAMSIZ);
+	return 0;
+}
+
+
+int linux_master_get(char *master_ifname, const char *ifname)
+{
+	char buf[128], masterlink[128], *pos;
+	ssize_t res;
+
+	/* check whether there is a master */
+	os_snprintf(buf, sizeof(buf), "/sys/class/net/%s/master", ifname);
+
+	res = readlink(buf, masterlink, sizeof(masterlink));
+	if (res < 0 || (size_t) res >= sizeof(masterlink))
+		return -1;
+
+	masterlink[res] = '\0';
+
+	pos = os_strrchr(masterlink, '/');
+	if (pos == NULL)
+		return -1;
+	pos++;
+	os_strlcpy(master_ifname, pos, IFNAMSIZ);
+	return 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/linux_ioctl.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/linux_ioctl.h
new file mode 100644
index 0000000..6de4d9b
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/linux_ioctl.h
@@ -0,0 +1,23 @@
+/*
+ * Linux ioctl helper functions for driver wrappers
+ * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef LINUX_IOCTL_H
+#define LINUX_IOCTL_H
+
+int linux_set_iface_flags(int sock, const char *ifname, int dev_up);
+int linux_iface_up(int sock, const char *ifname);
+int linux_get_ifhwaddr(int sock, const char *ifname, u8 *addr);
+int linux_set_ifhwaddr(int sock, const char *ifname, const u8 *addr);
+int linux_br_add(int sock, const char *brname);
+int linux_br_del(int sock, const char *brname);
+int linux_br_add_if(int sock, const char *brname, const char *ifname);
+int linux_br_del_if(int sock, const char *brname, const char *ifname);
+int linux_br_get(char *brname, const char *ifname);
+int linux_master_get(char *master_ifname, const char *ifname);
+
+#endif /* LINUX_IOCTL_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/linux_wext.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/linux_wext.h
new file mode 100644
index 0000000..e7c7001
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/linux_wext.h
@@ -0,0 +1,45 @@
+/*
+ * Driver interaction with generic Linux Wireless Extensions
+ * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef LINUX_WEXT_H
+#define LINUX_WEXT_H
+
+#ifndef ANDROID
+
+/*
+ * Avoid including other kernel header to avoid conflicts with C library
+ * headers.
+ */
+#define _LINUX_TYPES_H
+#define _LINUX_SOCKET_H
+#define _LINUX_IF_H
+
+#include <stdint.h>
+#include <net/if.h>
+typedef uint32_t __u32;
+typedef int32_t __s32;
+typedef uint16_t __u16;
+typedef int16_t __s16;
+typedef uint8_t __u8;
+#ifndef __user
+#define __user
+#endif /* __user */
+
+#endif /* ANDROID */
+
+#include <linux/wireless.h>
+
+#ifndef IW_ENCODE_ALG_PMK
+#define IW_ENCODE_ALG_PMK 4
+#endif
+
+#ifndef IW_ENC_CAPA_4WAY_HANDSHAKE
+#define IW_ENC_CAPA_4WAY_HANDSHAKE 0x00000010
+#endif
+
+#endif /* LINUX_WEXT_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/ndis_events.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/ndis_events.c
new file mode 100644
index 0000000..4d4ec81
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/ndis_events.c
@@ -0,0 +1,803 @@
+/*
+ * ndis_events - Receive NdisMIndicateStatus() events using WMI
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#define _WIN32_WINNT    0x0400
+
+#include "includes.h"
+
+#ifndef COBJMACROS
+#define COBJMACROS
+#endif /* COBJMACROS */
+#include <wbemidl.h>
+
+#include "common.h"
+
+
+static int wmi_refcnt = 0;
+static int wmi_first = 1;
+
+struct ndis_events_data {
+	IWbemObjectSink sink;
+	IWbemObjectSinkVtbl sink_vtbl;
+
+	IWbemServices *pSvc;
+	IWbemLocator *pLoc;
+
+	HANDLE read_pipe, write_pipe, event_avail;
+	UINT ref;
+	int terminating;
+	char *ifname; /* {GUID..} */
+	WCHAR *adapter_desc;
+};
+
+#define BstrAlloc(x) (x) ? SysAllocString(x) : NULL
+#define BstrFree(x) if (x) SysFreeString(x)
+
+/* WBEM / WMI wrapper functions, to perform in-place conversion of WCHARs to
+ * BSTRs */
+HRESULT STDMETHODCALLTYPE call_IWbemServices_ExecQuery(
+	IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery,
+	long lFlags, IWbemContext *pCtx, IEnumWbemClassObject **ppEnum)
+{
+	BSTR bsQueryLanguage, bsQuery;
+	HRESULT hr;
+
+	bsQueryLanguage = BstrAlloc(strQueryLanguage);
+	bsQuery = BstrAlloc(strQuery);
+
+	hr = IWbemServices_ExecQuery(pSvc, bsQueryLanguage, bsQuery, lFlags,
+				     pCtx, ppEnum);
+
+	BstrFree(bsQueryLanguage);
+	BstrFree(bsQuery);
+
+	return hr;
+}
+
+
+HRESULT STDMETHODCALLTYPE call_IWbemServices_ExecNotificationQueryAsync(
+	IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery,
+	long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler)
+{
+	BSTR bsQueryLanguage, bsQuery;
+	HRESULT hr;
+
+	bsQueryLanguage = BstrAlloc(strQueryLanguage);
+	bsQuery = BstrAlloc(strQuery);
+
+	hr = IWbemServices_ExecNotificationQueryAsync(pSvc, bsQueryLanguage,
+						      bsQuery, lFlags, pCtx,
+						      pResponseHandler);
+
+	BstrFree(bsQueryLanguage);
+	BstrFree(bsQuery);
+
+	return hr;
+}
+
+
+HRESULT STDMETHODCALLTYPE call_IWbemLocator_ConnectServer(
+	IWbemLocator *pLoc, LPCWSTR strNetworkResource, LPCWSTR strUser,
+	LPCWSTR strPassword, LPCWSTR strLocale, long lSecurityFlags,
+	LPCWSTR strAuthority, IWbemContext *pCtx, IWbemServices **ppNamespace)
+{
+	BSTR bsNetworkResource, bsUser, bsPassword, bsLocale, bsAuthority;
+	HRESULT hr;
+
+	bsNetworkResource = BstrAlloc(strNetworkResource);
+	bsUser = BstrAlloc(strUser);
+	bsPassword = BstrAlloc(strPassword);
+	bsLocale = BstrAlloc(strLocale);
+	bsAuthority = BstrAlloc(strAuthority);
+
+	hr = IWbemLocator_ConnectServer(pLoc, bsNetworkResource, bsUser,
+					bsPassword, bsLocale, lSecurityFlags,
+					bsAuthority, pCtx, ppNamespace);
+
+	BstrFree(bsNetworkResource);
+	BstrFree(bsUser);
+	BstrFree(bsPassword);
+	BstrFree(bsLocale);
+	BstrFree(bsAuthority);
+
+	return hr;
+}
+
+
+enum event_types { EVENT_CONNECT, EVENT_DISCONNECT, EVENT_MEDIA_SPECIFIC,
+		   EVENT_ADAPTER_ARRIVAL, EVENT_ADAPTER_REMOVAL };
+
+static int ndis_events_get_adapter(struct ndis_events_data *events,
+				   const char *ifname, const char *desc);
+
+
+static int ndis_events_constructor(struct ndis_events_data *events)
+{
+	events->ref = 1;
+
+	if (!CreatePipe(&events->read_pipe, &events->write_pipe, NULL, 512)) {
+		wpa_printf(MSG_ERROR, "CreatePipe() failed: %d",
+			   (int) GetLastError());
+		return -1;
+	}
+	events->event_avail = CreateEvent(NULL, TRUE, FALSE, NULL);
+	if (events->event_avail == NULL) {
+		wpa_printf(MSG_ERROR, "CreateEvent() failed: %d",
+			   (int) GetLastError());
+		CloseHandle(events->read_pipe);
+		CloseHandle(events->write_pipe);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static void ndis_events_destructor(struct ndis_events_data *events)
+{
+	CloseHandle(events->read_pipe);
+	CloseHandle(events->write_pipe);
+	CloseHandle(events->event_avail);
+	IWbemServices_Release(events->pSvc);
+	IWbemLocator_Release(events->pLoc);
+	if (--wmi_refcnt == 0)
+		CoUninitialize();
+}
+
+
+static HRESULT STDMETHODCALLTYPE
+ndis_events_query_interface(IWbemObjectSink *this, REFIID riid, void **obj)
+{
+	*obj = NULL;
+
+	if (IsEqualIID(riid, &IID_IUnknown) ||
+	    IsEqualIID(riid, &IID_IWbemObjectSink)) {
+		*obj = this;
+		IWbemObjectSink_AddRef(this);
+		return NOERROR;
+	}
+
+	return E_NOINTERFACE;
+}
+
+
+static ULONG STDMETHODCALLTYPE ndis_events_add_ref(IWbemObjectSink *this)
+{
+	struct ndis_events_data *events = (struct ndis_events_data *) this;
+	return ++events->ref;
+}
+
+
+static ULONG STDMETHODCALLTYPE ndis_events_release(IWbemObjectSink *this)
+{
+	struct ndis_events_data *events = (struct ndis_events_data *) this;
+
+	if (--events->ref != 0)
+		return events->ref;
+
+	ndis_events_destructor(events);
+	wpa_printf(MSG_DEBUG, "ndis_events: terminated");
+	os_free(events->adapter_desc);
+	os_free(events->ifname);
+	os_free(events);
+	return 0;
+}
+
+
+static int ndis_events_send_event(struct ndis_events_data *events,
+				  enum event_types type,
+				  char *data, size_t data_len)
+{
+	char buf[512], *pos, *end;
+	int _type;
+	DWORD written;
+
+	end = buf + sizeof(buf);
+	_type = (int) type;
+	os_memcpy(buf, &_type, sizeof(_type));
+	pos = buf + sizeof(_type);
+
+	if (data) {
+		if (2 + data_len > (size_t) (end - pos)) {
+			wpa_printf(MSG_DEBUG, "Not enough room for send_event "
+				   "data (%d)", data_len);
+			return -1;
+		}
+		*pos++ = data_len >> 8;
+		*pos++ = data_len & 0xff;
+		os_memcpy(pos, data, data_len);
+		pos += data_len;
+	}
+
+	if (WriteFile(events->write_pipe, buf, pos - buf, &written, NULL)) {
+		SetEvent(events->event_avail);
+		return 0;
+	}
+	wpa_printf(MSG_INFO, "WriteFile() failed: %d", (int) GetLastError());
+	return -1;
+}
+
+
+static void ndis_events_media_connect(struct ndis_events_data *events)
+{
+	wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaConnect");
+	ndis_events_send_event(events, EVENT_CONNECT, NULL, 0);
+}
+
+
+static void ndis_events_media_disconnect(struct ndis_events_data *events)
+{
+	wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaDisconnect");
+	ndis_events_send_event(events, EVENT_DISCONNECT, NULL, 0);
+}
+
+
+static void ndis_events_media_specific(struct ndis_events_data *events,
+				       IWbemClassObject *pObj)
+{
+	VARIANT vt;
+	HRESULT hr;
+	LONG lower, upper, k;
+	UCHAR ch;
+	char *data, *pos;
+	size_t data_len;
+
+	wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaSpecificIndication");
+
+	/* This is the StatusBuffer from NdisMIndicateStatus() call */
+	hr = IWbemClassObject_Get(pObj, L"NdisStatusMediaSpecificIndication",
+				  0, &vt, NULL, NULL);
+	if (FAILED(hr)) {
+		wpa_printf(MSG_DEBUG, "Could not get "
+			   "NdisStatusMediaSpecificIndication from "
+			   "the object?!");
+		return;
+	}
+
+	SafeArrayGetLBound(V_ARRAY(&vt), 1, &lower);
+	SafeArrayGetUBound(V_ARRAY(&vt), 1, &upper);
+	data_len = upper - lower + 1;
+	data = os_malloc(data_len);
+	if (data == NULL) {
+		wpa_printf(MSG_DEBUG, "Failed to allocate buffer for event "
+			   "data");
+		VariantClear(&vt);
+		return;
+	}
+
+	pos = data;
+	for (k = lower; k <= upper; k++) {
+		SafeArrayGetElement(V_ARRAY(&vt), &k, &ch);
+		*pos++ = ch;
+	}
+	wpa_hexdump(MSG_DEBUG, "MediaSpecificEvent", (u8 *) data, data_len);
+
+	VariantClear(&vt);
+
+	ndis_events_send_event(events, EVENT_MEDIA_SPECIFIC, data, data_len);
+
+	os_free(data);
+}
+
+
+static void ndis_events_adapter_arrival(struct ndis_events_data *events)
+{
+	wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterArrival");
+	ndis_events_send_event(events, EVENT_ADAPTER_ARRIVAL, NULL, 0);
+}
+
+
+static void ndis_events_adapter_removal(struct ndis_events_data *events)
+{
+	wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterRemoval");
+	ndis_events_send_event(events, EVENT_ADAPTER_REMOVAL, NULL, 0);
+}
+
+
+static HRESULT STDMETHODCALLTYPE
+ndis_events_indicate(IWbemObjectSink *this, long lObjectCount,
+		     IWbemClassObject __RPC_FAR *__RPC_FAR *ppObjArray)
+{
+	struct ndis_events_data *events = (struct ndis_events_data *) this;
+	long i;
+
+	if (events->terminating) {
+		wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore "
+			   "indication - terminating");
+		return WBEM_NO_ERROR;
+	}
+	/* wpa_printf(MSG_DEBUG, "Notification received - %d object(s)",
+	   lObjectCount); */
+
+	for (i = 0; i < lObjectCount; i++) {
+		IWbemClassObject *pObj = ppObjArray[i];
+		HRESULT hr;
+		VARIANT vtClass, vt;
+
+		hr = IWbemClassObject_Get(pObj, L"__CLASS", 0, &vtClass, NULL,
+					  NULL);
+		if (FAILED(hr)) {
+			wpa_printf(MSG_DEBUG, "Failed to get __CLASS from "
+				   "event.");
+			break;
+		}
+		/* wpa_printf(MSG_DEBUG, "CLASS: '%S'", vtClass.bstrVal); */
+
+		hr = IWbemClassObject_Get(pObj, L"InstanceName", 0, &vt, NULL,
+					  NULL);
+		if (FAILED(hr)) {
+			wpa_printf(MSG_DEBUG, "Failed to get InstanceName "
+				   "from event.");
+			VariantClear(&vtClass);
+			break;
+		}
+
+		if (wcscmp(vtClass.bstrVal,
+			   L"MSNdis_NotifyAdapterArrival") == 0) {
+			wpa_printf(MSG_DEBUG, "ndis_events_indicate: Try to "
+				   "update adapter description since it may "
+				   "have changed with new adapter instance");
+			ndis_events_get_adapter(events, events->ifname, NULL);
+		}
+
+		if (wcscmp(events->adapter_desc, vt.bstrVal) != 0) {
+			wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore "
+				   "indication for foreign adapter: "
+				   "InstanceName: '%S' __CLASS: '%S'",
+				   vt.bstrVal, vtClass.bstrVal);
+			VariantClear(&vtClass);
+			VariantClear(&vt);
+			continue;
+		}
+		VariantClear(&vt);
+
+		if (wcscmp(vtClass.bstrVal,
+			   L"MSNdis_StatusMediaSpecificIndication") == 0) {
+			ndis_events_media_specific(events, pObj);
+		} else if (wcscmp(vtClass.bstrVal,
+				  L"MSNdis_StatusMediaConnect") == 0) {
+			ndis_events_media_connect(events);
+		} else if (wcscmp(vtClass.bstrVal,
+				  L"MSNdis_StatusMediaDisconnect") == 0) {
+			ndis_events_media_disconnect(events);
+		} else if (wcscmp(vtClass.bstrVal,
+				  L"MSNdis_NotifyAdapterArrival") == 0) {
+			ndis_events_adapter_arrival(events);
+		} else if (wcscmp(vtClass.bstrVal,
+				  L"MSNdis_NotifyAdapterRemoval") == 0) {
+			ndis_events_adapter_removal(events);
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "Unexpected event - __CLASS: '%S'",
+				   vtClass.bstrVal);
+		}
+
+		VariantClear(&vtClass);
+	}
+
+	return WBEM_NO_ERROR;
+}
+
+
+static HRESULT STDMETHODCALLTYPE
+ndis_events_set_status(IWbemObjectSink *this, long lFlags, HRESULT hResult,
+		       BSTR strParam, IWbemClassObject __RPC_FAR *pObjParam)
+{
+	return WBEM_NO_ERROR;
+}
+
+
+static int notification_query(IWbemObjectSink *pDestSink,
+			      IWbemServices *pSvc, const char *class_name)
+{
+	HRESULT hr;
+	WCHAR query[256];
+
+	_snwprintf(query, 256,
+		  L"SELECT * FROM %S", class_name);
+	wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
+	hr = call_IWbemServices_ExecNotificationQueryAsync(
+		pSvc, L"WQL", query, 0, 0, pDestSink);
+	if (FAILED(hr)) {
+		wpa_printf(MSG_DEBUG, "ExecNotificationQueryAsync for %s "
+			   "failed with hresult of 0x%x",
+			   class_name, (int) hr);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int register_async_notification(IWbemObjectSink *pDestSink,
+				       IWbemServices *pSvc)
+{
+	int i;
+	const char *class_list[] = {
+		"MSNdis_StatusMediaConnect",
+		"MSNdis_StatusMediaDisconnect",
+		"MSNdis_StatusMediaSpecificIndication",
+		"MSNdis_NotifyAdapterArrival",
+		"MSNdis_NotifyAdapterRemoval",
+		NULL
+	};
+
+	for (i = 0; class_list[i]; i++) {
+		if (notification_query(pDestSink, pSvc, class_list[i]) < 0)
+			return -1;
+	}
+
+	return 0;
+}
+
+
+void ndis_events_deinit(struct ndis_events_data *events)
+{
+	events->terminating = 1;
+	IWbemServices_CancelAsyncCall(events->pSvc, &events->sink);
+	IWbemObjectSink_Release(&events->sink);
+	/*
+	 * Rest of deinitialization is done in ndis_events_destructor() once
+	 * all reference count drops to zero.
+	 */
+}
+
+
+static int ndis_events_use_desc(struct ndis_events_data *events,
+				const char *desc)
+{
+	char *tmp, *pos;
+	size_t len;
+
+	if (desc == NULL) {
+		if (events->adapter_desc == NULL)
+			return -1;
+		/* Continue using old description */
+		return 0;
+	}
+
+	tmp = os_strdup(desc);
+	if (tmp == NULL)
+		return -1;
+
+	pos = os_strstr(tmp, " (Microsoft's Packet Scheduler)");
+	if (pos)
+		*pos = '\0';
+
+	len = os_strlen(tmp);
+	events->adapter_desc = os_malloc((len + 1) * sizeof(WCHAR));
+	if (events->adapter_desc == NULL) {
+		os_free(tmp);
+		return -1;
+	}
+	_snwprintf(events->adapter_desc, len + 1, L"%S", tmp);
+	os_free(tmp);
+	return 0;
+}
+
+
+static int ndis_events_get_adapter(struct ndis_events_data *events,
+				   const char *ifname, const char *desc)
+{
+	HRESULT hr;
+	IWbemServices *pSvc;
+#define MAX_QUERY_LEN 256
+	WCHAR query[MAX_QUERY_LEN];
+	IEnumWbemClassObject *pEnumerator;
+	IWbemClassObject *pObj;
+	ULONG uReturned;
+	VARIANT vt;
+	int len, pos;
+
+	/*
+	 * Try to get adapter descriptor through WMI CIMv2 Win32_NetworkAdapter
+	 * to have better probability of matching with InstanceName from
+	 * MSNdis events. If this fails, use the provided description.
+	 */
+
+	os_free(events->adapter_desc);
+	events->adapter_desc = NULL;
+
+	hr = call_IWbemLocator_ConnectServer(
+		events->pLoc, L"ROOT\\CIMV2", NULL, NULL, 0, 0, 0, 0, &pSvc);
+	if (FAILED(hr)) {
+		wpa_printf(MSG_ERROR, "ndis_events: Could not connect to WMI "
+			   "server (ROOT\\CIMV2) - error 0x%x", (int) hr);
+		return ndis_events_use_desc(events, desc);
+	}
+	wpa_printf(MSG_DEBUG, "ndis_events: Connected to ROOT\\CIMV2.");
+
+	_snwprintf(query, MAX_QUERY_LEN,
+		  L"SELECT Index FROM Win32_NetworkAdapterConfiguration "
+		  L"WHERE SettingID='%S'", ifname);
+	wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
+
+	hr = call_IWbemServices_ExecQuery(
+		pSvc, L"WQL", query,
+		WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
+		NULL, &pEnumerator);
+	if (!SUCCEEDED(hr)) {
+		wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
+			   "GUID from Win32_NetworkAdapterConfiguration: "
+			   "0x%x", (int) hr);
+		IWbemServices_Release(pSvc);
+		return ndis_events_use_desc(events, desc);
+	}
+
+	uReturned = 0;
+	hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
+				       &pObj, &uReturned);
+	if (!SUCCEEDED(hr) || uReturned == 0) {
+		wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
+			   "GUID from Win32_NetworkAdapterConfiguration: "
+			   "0x%x", (int) hr);
+		IEnumWbemClassObject_Release(pEnumerator);
+		IWbemServices_Release(pSvc);
+		return ndis_events_use_desc(events, desc);
+	}
+	IEnumWbemClassObject_Release(pEnumerator);
+
+	VariantInit(&vt);
+	hr = IWbemClassObject_Get(pObj, L"Index", 0, &vt, NULL, NULL);
+	if (!SUCCEEDED(hr)) {
+		wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Index from "
+			   "Win32_NetworkAdapterConfiguration: 0x%x",
+			   (int) hr);
+		IWbemServices_Release(pSvc);
+		return ndis_events_use_desc(events, desc);
+	}
+
+	_snwprintf(query, MAX_QUERY_LEN,
+		  L"SELECT Name,PNPDeviceID FROM Win32_NetworkAdapter WHERE "
+		  L"Index=%d",
+		  vt.uintVal);
+	wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
+	VariantClear(&vt);
+	IWbemClassObject_Release(pObj);
+
+	hr = call_IWbemServices_ExecQuery(
+		pSvc, L"WQL", query,
+		WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
+		NULL, &pEnumerator);
+	if (!SUCCEEDED(hr)) {
+		wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
+			   "from Win32_NetworkAdapter: 0x%x", (int) hr);
+		IWbemServices_Release(pSvc);
+		return ndis_events_use_desc(events, desc);
+	}
+
+	uReturned = 0;
+	hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
+				       &pObj, &uReturned);
+	if (!SUCCEEDED(hr) || uReturned == 0) {
+		wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
+			   "from Win32_NetworkAdapter: 0x%x", (int) hr);
+		IEnumWbemClassObject_Release(pEnumerator);
+		IWbemServices_Release(pSvc);
+		return ndis_events_use_desc(events, desc);
+	}
+	IEnumWbemClassObject_Release(pEnumerator);
+
+	hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL);
+	if (!SUCCEEDED(hr)) {
+		wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from "
+			   "Win32_NetworkAdapter: 0x%x", (int) hr);
+		IWbemClassObject_Release(pObj);
+		IWbemServices_Release(pSvc);
+		return ndis_events_use_desc(events, desc);
+	}
+
+	wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::Name='%S'",
+		   vt.bstrVal);
+	events->adapter_desc = _wcsdup(vt.bstrVal);
+	VariantClear(&vt);
+
+	/*
+	 * Try to get even better candidate for matching with InstanceName
+	 * from Win32_PnPEntity. This is needed at least for some USB cards
+	 * that can change the InstanceName whenever being unplugged and
+	 * plugged again.
+	 */
+
+	hr = IWbemClassObject_Get(pObj, L"PNPDeviceID", 0, &vt, NULL, NULL);
+	if (!SUCCEEDED(hr)) {
+		wpa_printf(MSG_DEBUG, "ndis_events: Failed to get PNPDeviceID "
+			   "from Win32_NetworkAdapter: 0x%x", (int) hr);
+		IWbemClassObject_Release(pObj);
+		IWbemServices_Release(pSvc);
+		if (events->adapter_desc == NULL)
+			return ndis_events_use_desc(events, desc);
+		return 0; /* use Win32_NetworkAdapter::Name */
+	}
+
+	wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::PNPDeviceID="
+		   "'%S'", vt.bstrVal);
+
+	len = _snwprintf(query, MAX_QUERY_LEN,
+			L"SELECT Name FROM Win32_PnPEntity WHERE DeviceID='");
+	if (len < 0 || len >= MAX_QUERY_LEN - 1) {
+		VariantClear(&vt);
+		IWbemClassObject_Release(pObj);
+		IWbemServices_Release(pSvc);
+		if (events->adapter_desc == NULL)
+			return ndis_events_use_desc(events, desc);
+		return 0; /* use Win32_NetworkAdapter::Name */
+	}
+
+	/* Escape \ as \\ */
+	for (pos = 0; vt.bstrVal[pos] && len < MAX_QUERY_LEN - 2; pos++) {
+		if (vt.bstrVal[pos] == '\\') {
+			if (len >= MAX_QUERY_LEN - 3)
+				break;
+			query[len++] = '\\';
+		}
+		query[len++] = vt.bstrVal[pos];
+	}
+	query[len++] = L'\'';
+	query[len] = L'\0';
+	VariantClear(&vt);
+	IWbemClassObject_Release(pObj);
+	wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
+
+	hr = call_IWbemServices_ExecQuery(
+		pSvc, L"WQL", query,
+		WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
+		NULL, &pEnumerator);
+	if (!SUCCEEDED(hr)) {
+		wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
+			   "Name from Win32_PnPEntity: 0x%x", (int) hr);
+		IWbemServices_Release(pSvc);
+		if (events->adapter_desc == NULL)
+			return ndis_events_use_desc(events, desc);
+		return 0; /* use Win32_NetworkAdapter::Name */
+	}
+
+	uReturned = 0;
+	hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
+				       &pObj, &uReturned);
+	if (!SUCCEEDED(hr) || uReturned == 0) {
+		wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
+			   "from Win32_PnPEntity: 0x%x", (int) hr);
+		IEnumWbemClassObject_Release(pEnumerator);
+		IWbemServices_Release(pSvc);
+		if (events->adapter_desc == NULL)
+			return ndis_events_use_desc(events, desc);
+		return 0; /* use Win32_NetworkAdapter::Name */
+	}
+	IEnumWbemClassObject_Release(pEnumerator);
+
+	hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL);
+	if (!SUCCEEDED(hr)) {
+		wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from "
+			   "Win32_PnPEntity: 0x%x", (int) hr);
+		IWbemClassObject_Release(pObj);
+		IWbemServices_Release(pSvc);
+		if (events->adapter_desc == NULL)
+			return ndis_events_use_desc(events, desc);
+		return 0; /* use Win32_NetworkAdapter::Name */
+	}
+
+	wpa_printf(MSG_DEBUG, "ndis_events: Win32_PnPEntity::Name='%S'",
+		   vt.bstrVal);
+	os_free(events->adapter_desc);
+	events->adapter_desc = _wcsdup(vt.bstrVal);
+	VariantClear(&vt);
+
+	IWbemClassObject_Release(pObj);
+
+	IWbemServices_Release(pSvc);
+
+	if (events->adapter_desc == NULL)
+		return ndis_events_use_desc(events, desc);
+
+	return 0;
+}
+
+
+struct ndis_events_data *
+ndis_events_init(HANDLE *read_pipe, HANDLE *event_avail,
+		 const char *ifname, const char *desc)
+{
+	HRESULT hr;
+	IWbemObjectSink *pSink;
+	struct ndis_events_data *events;
+
+	events = os_zalloc(sizeof(*events));
+	if (events == NULL) {
+		wpa_printf(MSG_ERROR, "Could not allocate sink for events.");
+		return NULL;
+	}
+	events->ifname = os_strdup(ifname);
+	if (events->ifname == NULL) {
+		os_free(events);
+		return NULL;
+	}
+
+	if (wmi_refcnt++ == 0) {
+		hr = CoInitializeEx(0, COINIT_MULTITHREADED);
+		if (FAILED(hr)) {
+			wpa_printf(MSG_ERROR, "CoInitializeEx() failed - "
+				   "returned 0x%x", (int) hr);
+			os_free(events);
+			return NULL;
+		}
+	}
+
+	if (wmi_first) {
+		/* CoInitializeSecurity() must be called once and only once
+		 * per process, so let's use wmi_first flag to protect against
+		 * multiple calls. */
+		wmi_first = 0;
+
+		hr = CoInitializeSecurity(NULL, -1, NULL, NULL,
+					  RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
+					  RPC_C_IMP_LEVEL_IMPERSONATE,
+					  NULL, EOAC_SECURE_REFS, NULL);
+		if (FAILED(hr)) {
+			wpa_printf(MSG_ERROR, "CoInitializeSecurity() failed "
+				   "- returned 0x%x", (int) hr);
+			os_free(events);
+			return NULL;
+		}
+	}
+
+	hr = CoCreateInstance(&CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER,
+			      &IID_IWbemLocator,
+			      (LPVOID *) (void *) &events->pLoc);
+	if (FAILED(hr)) {
+		wpa_printf(MSG_ERROR, "CoCreateInstance() failed - returned "
+			   "0x%x", (int) hr);
+		CoUninitialize();
+		os_free(events);
+		return NULL;
+	}
+
+	if (ndis_events_get_adapter(events, ifname, desc) < 0) {
+		CoUninitialize();
+		os_free(events);
+		return NULL;
+	}
+	wpa_printf(MSG_DEBUG, "ndis_events: use adapter descriptor '%S'",
+		   events->adapter_desc);
+
+	hr = call_IWbemLocator_ConnectServer(
+		events->pLoc, L"ROOT\\WMI", NULL, NULL,
+		0, 0, 0, 0, &events->pSvc);
+	if (FAILED(hr)) {
+		wpa_printf(MSG_ERROR, "Could not connect to server - error "
+			   "0x%x", (int) hr);
+		CoUninitialize();
+		os_free(events->adapter_desc);
+		os_free(events);
+		return NULL;
+	}
+	wpa_printf(MSG_DEBUG, "Connected to ROOT\\WMI.");
+
+	ndis_events_constructor(events);
+	pSink = &events->sink;
+	pSink->lpVtbl = &events->sink_vtbl;
+	events->sink_vtbl.QueryInterface = ndis_events_query_interface;
+	events->sink_vtbl.AddRef = ndis_events_add_ref;
+	events->sink_vtbl.Release = ndis_events_release;
+	events->sink_vtbl.Indicate = ndis_events_indicate;
+	events->sink_vtbl.SetStatus = ndis_events_set_status;
+
+	if (register_async_notification(pSink, events->pSvc) < 0) {
+		wpa_printf(MSG_DEBUG, "Failed to register async "
+			   "notifications");
+		ndis_events_destructor(events);
+		os_free(events->adapter_desc);
+		os_free(events);
+		return NULL;
+	}
+
+	*read_pipe = events->read_pipe;
+	*event_avail = events->event_avail;
+
+	return events;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/netlink.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/netlink.c
new file mode 100644
index 0000000..7780479
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/netlink.c
@@ -0,0 +1,228 @@
+/*
+ * Netlink helper functions for driver wrappers
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "priv_netlink.h"
+#include "netlink.h"
+
+
+struct netlink_data {
+	struct netlink_config *cfg;
+	int sock;
+};
+
+
+static void netlink_receive_link(struct netlink_data *netlink,
+				 void (*cb)(void *ctx, struct ifinfomsg *ifi,
+					    u8 *buf, size_t len),
+				 struct nlmsghdr *h)
+{
+	if (cb == NULL || NLMSG_PAYLOAD(h, 0) < sizeof(struct ifinfomsg))
+		return;
+	cb(netlink->cfg->ctx, NLMSG_DATA(h),
+	   (u8 *) NLMSG_DATA(h) + NLMSG_ALIGN(sizeof(struct ifinfomsg)),
+	   NLMSG_PAYLOAD(h, sizeof(struct ifinfomsg)));
+}
+
+
+static void netlink_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct netlink_data *netlink = eloop_ctx;
+	char buf[8192];
+	int left;
+	struct sockaddr_nl from;
+	socklen_t fromlen;
+	struct nlmsghdr *h;
+	int max_events = 10;
+
+try_again:
+	fromlen = sizeof(from);
+	left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
+			(struct sockaddr *) &from, &fromlen);
+	if (left < 0) {
+		if (errno != EINTR && errno != EAGAIN)
+			wpa_printf(MSG_INFO, "netlink: recvfrom failed: %s",
+				   strerror(errno));
+		return;
+	}
+
+	h = (struct nlmsghdr *) buf;
+	while (NLMSG_OK(h, left)) {
+		switch (h->nlmsg_type) {
+		case RTM_NEWLINK:
+			netlink_receive_link(netlink, netlink->cfg->newlink_cb,
+					     h);
+			break;
+		case RTM_DELLINK:
+			netlink_receive_link(netlink, netlink->cfg->dellink_cb,
+					     h);
+			break;
+		}
+
+		h = NLMSG_NEXT(h, left);
+	}
+
+	if (left > 0) {
+		wpa_printf(MSG_DEBUG, "netlink: %d extra bytes in the end of "
+			   "netlink message", left);
+	}
+
+	if (--max_events > 0) {
+		/*
+		 * Try to receive all events in one eloop call in order to
+		 * limit race condition on cases where AssocInfo event, Assoc
+		 * event, and EAPOL frames are received more or less at the
+		 * same time. We want to process the event messages first
+		 * before starting EAPOL processing.
+		 */
+		goto try_again;
+	}
+}
+
+
+struct netlink_data * netlink_init(struct netlink_config *cfg)
+{
+	struct netlink_data *netlink;
+	struct sockaddr_nl local;
+
+	netlink = os_zalloc(sizeof(*netlink));
+	if (netlink == NULL)
+		return NULL;
+
+	netlink->sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+	if (netlink->sock < 0) {
+		wpa_printf(MSG_ERROR, "netlink: Failed to open netlink "
+			   "socket: %s", strerror(errno));
+		netlink_deinit(netlink);
+		return NULL;
+	}
+
+	os_memset(&local, 0, sizeof(local));
+	local.nl_family = AF_NETLINK;
+	local.nl_groups = RTMGRP_LINK;
+	if (bind(netlink->sock, (struct sockaddr *) &local, sizeof(local)) < 0)
+	{
+		wpa_printf(MSG_ERROR, "netlink: Failed to bind netlink "
+			   "socket: %s", strerror(errno));
+		netlink_deinit(netlink);
+		return NULL;
+	}
+
+	eloop_register_read_sock(netlink->sock, netlink_receive, netlink,
+				 NULL);
+
+	netlink->cfg = cfg;
+
+	return netlink;
+}
+
+
+void netlink_deinit(struct netlink_data *netlink)
+{
+	if (netlink == NULL)
+		return;
+	if (netlink->sock >= 0) {
+		eloop_unregister_read_sock(netlink->sock);
+		close(netlink->sock);
+	}
+	os_free(netlink->cfg);
+	os_free(netlink);
+}
+
+
+static const char * linkmode_str(int mode)
+{
+	switch (mode) {
+	case -1:
+		return "no change";
+	case 0:
+		return "kernel-control";
+	case 1:
+		return "userspace-control";
+	default:
+		return "?";
+	}
+}
+
+
+static const char * operstate_str(int state)
+{
+	switch (state) {
+	case -1:
+		return "no change";
+	case IF_OPER_DORMANT:
+		return "IF_OPER_DORMANT";
+	case IF_OPER_UP:
+		return "IF_OPER_UP";
+	default:
+		return "?";
+	}
+}
+
+
+int netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex,
+			   int linkmode, int operstate)
+{
+	struct {
+		struct nlmsghdr hdr;
+		struct ifinfomsg ifinfo;
+		char opts[16];
+	} req;
+	struct rtattr *rta;
+	static int nl_seq;
+	ssize_t ret;
+
+	os_memset(&req, 0, sizeof(req));
+
+	req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+	req.hdr.nlmsg_type = RTM_SETLINK;
+	req.hdr.nlmsg_flags = NLM_F_REQUEST;
+	req.hdr.nlmsg_seq = ++nl_seq;
+	req.hdr.nlmsg_pid = 0;
+
+	req.ifinfo.ifi_family = AF_UNSPEC;
+	req.ifinfo.ifi_type = 0;
+	req.ifinfo.ifi_index = ifindex;
+	req.ifinfo.ifi_flags = 0;
+	req.ifinfo.ifi_change = 0;
+
+	if (linkmode != -1) {
+		rta = aliasing_hide_typecast(
+			((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)),
+			struct rtattr);
+		rta->rta_type = IFLA_LINKMODE;
+		rta->rta_len = RTA_LENGTH(sizeof(char));
+		*((char *) RTA_DATA(rta)) = linkmode;
+		req.hdr.nlmsg_len += RTA_SPACE(sizeof(char));
+	}
+	if (operstate != -1) {
+		rta = aliasing_hide_typecast(
+			((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)),
+			struct rtattr);
+		rta->rta_type = IFLA_OPERSTATE;
+		rta->rta_len = RTA_LENGTH(sizeof(char));
+		*((char *) RTA_DATA(rta)) = operstate;
+		req.hdr.nlmsg_len += RTA_SPACE(sizeof(char));
+	}
+
+	wpa_printf(MSG_DEBUG, "netlink: Operstate: ifindex=%d linkmode=%d (%s), operstate=%d (%s)",
+		   ifindex, linkmode, linkmode_str(linkmode),
+		   operstate, operstate_str(operstate));
+
+	ret = send(netlink->sock, &req, req.hdr.nlmsg_len, 0);
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "netlink: Sending operstate IFLA "
+			   "failed: %s (assume operstate is not supported)",
+			   strerror(errno));
+	}
+
+	return ret < 0 ? -1 : 0;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/netlink.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/netlink.h
new file mode 100644
index 0000000..3a7340e
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/netlink.h
@@ -0,0 +1,28 @@
+/*
+ * Netlink helper functions for driver wrappers
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef NETLINK_H
+#define NETLINK_H
+
+struct netlink_data;
+struct ifinfomsg;
+
+struct netlink_config {
+	void *ctx;
+	void (*newlink_cb)(void *ctx, struct ifinfomsg *ifi, u8 *buf,
+			   size_t len);
+	void (*dellink_cb)(void *ctx, struct ifinfomsg *ifi, u8 *buf,
+			   size_t len);
+};
+
+struct netlink_data * netlink_init(struct netlink_config *cfg);
+void netlink_deinit(struct netlink_data *netlink);
+int netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex,
+			   int linkmode, int operstate);
+
+#endif /* NETLINK_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/nl80211_copy.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/nl80211_copy.h
new file mode 100644
index 0000000..c59fec4
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/nl80211_copy.h
@@ -0,0 +1,7815 @@
+#ifndef __LINUX_NL80211_H
+#define __LINUX_NL80211_H
+/*
+ * 802.11 netlink interface public header
+ *
+ * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2008 Michael Wu <flamingice@sourmilk.net>
+ * Copyright 2008 Luis Carlos Cobo <luisca@cozybit.com>
+ * Copyright 2008 Michael Buesch <m@bues.ch>
+ * Copyright 2008, 2009 Luis R. Rodriguez <lrodriguez@atheros.com>
+ * Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
+ * Copyright 2008 Colin McCabe <colin@cozybit.com>
+ * Copyright 2015-2017	Intel Deutschland GmbH
+ * Copyright (C) 2018-2022 Intel Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+/*
+ * This header file defines the userspace API to the wireless stack. Please
+ * be careful not to break things - i.e. don't move anything around or so
+ * unless you can demonstrate that it breaks neither API nor ABI.
+ *
+ * Additions to the API should be accompanied by actual implementations in
+ * an upstream driver, so that example implementations exist in case there
+ * are ever concerns about the precise semantics of the API or changes are
+ * needed, and to ensure that code for dead (no longer implemented) API
+ * can actually be identified and removed.
+ * Nonetheless, semantics should also be documented carefully in this file.
+ */
+
+#include <linux/types.h>
+
+#define NL80211_GENL_NAME "nl80211"
+
+#define NL80211_MULTICAST_GROUP_CONFIG		"config"
+#define NL80211_MULTICAST_GROUP_SCAN		"scan"
+#define NL80211_MULTICAST_GROUP_REG		"regulatory"
+#define NL80211_MULTICAST_GROUP_MLME		"mlme"
+#define NL80211_MULTICAST_GROUP_VENDOR		"vendor"
+#define NL80211_MULTICAST_GROUP_NAN		"nan"
+#define NL80211_MULTICAST_GROUP_TESTMODE	"testmode"
+
+#define NL80211_EDMG_BW_CONFIG_MIN	4
+#define NL80211_EDMG_BW_CONFIG_MAX	15
+#define NL80211_EDMG_CHANNELS_MIN	1
+#define NL80211_EDMG_CHANNELS_MAX	0x3c /* 0b00111100 */
+
+/**
+ * DOC: Station handling
+ *
+ * Stations are added per interface, but a special case exists with VLAN
+ * interfaces. When a station is bound to an AP interface, it may be moved
+ * into a VLAN identified by a VLAN interface index (%NL80211_ATTR_STA_VLAN).
+ * The station is still assumed to belong to the AP interface it was added
+ * to.
+ *
+ * Station handling varies per interface type and depending on the driver's
+ * capabilities.
+ *
+ * For drivers supporting TDLS with external setup (WIPHY_FLAG_SUPPORTS_TDLS
+ * and WIPHY_FLAG_TDLS_EXTERNAL_SETUP), the station lifetime is as follows:
+ *  - a setup station entry is added, not yet authorized, without any rate
+ *    or capability information, this just exists to avoid race conditions
+ *  - when the TDLS setup is done, a single NL80211_CMD_SET_STATION is valid
+ *    to add rate and capability information to the station and at the same
+ *    time mark it authorized.
+ *  - %NL80211_TDLS_ENABLE_LINK is then used
+ *  - after this, the only valid operation is to remove it by tearing down
+ *    the TDLS link (%NL80211_TDLS_DISABLE_LINK)
+ *
+ * TODO: need more info for other interface types
+ */
+
+/**
+ * DOC: Frame transmission/registration support
+ *
+ * Frame transmission and registration support exists to allow userspace
+ * management entities such as wpa_supplicant react to management frames
+ * that are not being handled by the kernel. This includes, for example,
+ * certain classes of action frames that cannot be handled in the kernel
+ * for various reasons.
+ *
+ * Frame registration is done on a per-interface basis and registrations
+ * cannot be removed other than by closing the socket. It is possible to
+ * specify a registration filter to register, for example, only for a
+ * certain type of action frame. In particular with action frames, those
+ * that userspace registers for will not be returned as unhandled by the
+ * driver, so that the registered application has to take responsibility
+ * for doing that.
+ *
+ * The type of frame that can be registered for is also dependent on the
+ * driver and interface type. The frame types are advertised in wiphy
+ * attributes so applications know what to expect.
+ *
+ * NOTE: When an interface changes type while registrations are active,
+ *       these registrations are ignored until the interface type is
+ *       changed again. This means that changing the interface type can
+ *       lead to a situation that couldn't otherwise be produced, but
+ *       any such registrations will be dormant in the sense that they
+ *       will not be serviced, i.e. they will not receive any frames.
+ *
+ * Frame transmission allows userspace to send for example the required
+ * responses to action frames. It is subject to some sanity checking,
+ * but many frames can be transmitted. When a frame was transmitted, its
+ * status is indicated to the sending socket.
+ *
+ * For more technical details, see the corresponding command descriptions
+ * below.
+ */
+
+/**
+ * DOC: Virtual interface / concurrency capabilities
+ *
+ * Some devices are able to operate with virtual MACs, they can have
+ * more than one virtual interface. The capability handling for this
+ * is a bit complex though, as there may be a number of restrictions
+ * on the types of concurrency that are supported.
+ *
+ * To start with, each device supports the interface types listed in
+ * the %NL80211_ATTR_SUPPORTED_IFTYPES attribute, but by listing the
+ * types there no concurrency is implied.
+ *
+ * Once concurrency is desired, more attributes must be observed:
+ * To start with, since some interface types are purely managed in
+ * software, like the AP-VLAN type in mac80211 for example, there's
+ * an additional list of these, they can be added at any time and
+ * are only restricted by some semantic restrictions (e.g. AP-VLAN
+ * cannot be added without a corresponding AP interface). This list
+ * is exported in the %NL80211_ATTR_SOFTWARE_IFTYPES attribute.
+ *
+ * Further, the list of supported combinations is exported. This is
+ * in the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute. Basically,
+ * it exports a list of "groups", and at any point in time the
+ * interfaces that are currently active must fall into any one of
+ * the advertised groups. Within each group, there are restrictions
+ * on the number of interfaces of different types that are supported
+ * and also the number of different channels, along with potentially
+ * some other restrictions. See &enum nl80211_if_combination_attrs.
+ *
+ * All together, these attributes define the concurrency of virtual
+ * interfaces that a given device supports.
+ */
+
+/**
+ * DOC: packet coalesce support
+ *
+ * In most cases, host that receives IPv4 and IPv6 multicast/broadcast
+ * packets does not do anything with these packets. Therefore the
+ * reception of these unwanted packets causes unnecessary processing
+ * and power consumption.
+ *
+ * Packet coalesce feature helps to reduce number of received interrupts
+ * to host by buffering these packets in firmware/hardware for some
+ * predefined time. Received interrupt will be generated when one of the
+ * following events occur.
+ * a) Expiration of hardware timer whose expiration time is set to maximum
+ * coalescing delay of matching coalesce rule.
+ * b) Coalescing buffer in hardware reaches it's limit.
+ * c) Packet doesn't match any of the configured coalesce rules.
+ *
+ * User needs to configure following parameters for creating a coalesce
+ * rule.
+ * a) Maximum coalescing delay
+ * b) List of packet patterns which needs to be matched
+ * c) Condition for coalescence. pattern 'match' or 'no match'
+ * Multiple such rules can be created.
+ */
+
+/**
+ * DOC: WPA/WPA2 EAPOL handshake offload
+ *
+ * By setting @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK flag drivers
+ * can indicate they support offloading EAPOL handshakes for WPA/WPA2
+ * preshared key authentication in station mode. In %NL80211_CMD_CONNECT
+ * the preshared key should be specified using %NL80211_ATTR_PMK. Drivers
+ * supporting this offload may reject the %NL80211_CMD_CONNECT when no
+ * preshared key material is provided, for example when that driver does
+ * not support setting the temporal keys through %NL80211_CMD_NEW_KEY.
+ *
+ * Similarly @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X flag can be
+ * set by drivers indicating offload support of the PTK/GTK EAPOL
+ * handshakes during 802.1X authentication in station mode. In order to
+ * use the offload the %NL80211_CMD_CONNECT should have
+ * %NL80211_ATTR_WANT_1X_4WAY_HS attribute flag. Drivers supporting this
+ * offload may reject the %NL80211_CMD_CONNECT when the attribute flag is
+ * not present.
+ *
+ * By setting @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_AP_PSK flag drivers
+ * can indicate they support offloading EAPOL handshakes for WPA/WPA2
+ * preshared key authentication in AP mode. In %NL80211_CMD_START_AP
+ * the preshared key should be specified using %NL80211_ATTR_PMK. Drivers
+ * supporting this offload may reject the %NL80211_CMD_START_AP when no
+ * preshared key material is provided, for example when that driver does
+ * not support setting the temporal keys through %NL80211_CMD_NEW_KEY.
+ *
+ * For 802.1X the PMK or PMK-R0 are set by providing %NL80211_ATTR_PMK
+ * using %NL80211_CMD_SET_PMK. For offloaded FT support also
+ * %NL80211_ATTR_PMKR0_NAME must be provided.
+ */
+
+/**
+ * DOC: FILS shared key authentication offload
+ *
+ * FILS shared key authentication offload can be advertized by drivers by
+ * setting @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD flag. The drivers that support
+ * FILS shared key authentication offload should be able to construct the
+ * authentication and association frames for FILS shared key authentication and
+ * eventually do a key derivation as per IEEE 802.11ai. The below additional
+ * parameters should be given to driver in %NL80211_CMD_CONNECT and/or in
+ * %NL80211_CMD_UPDATE_CONNECT_PARAMS.
+ *	%NL80211_ATTR_FILS_ERP_USERNAME - used to construct keyname_nai
+ *	%NL80211_ATTR_FILS_ERP_REALM - used to construct keyname_nai
+ *	%NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM - used to construct erp message
+ *	%NL80211_ATTR_FILS_ERP_RRK - used to generate the rIK and rMSK
+ * rIK should be used to generate an authentication tag on the ERP message and
+ * rMSK should be used to derive a PMKSA.
+ * rIK, rMSK should be generated and keyname_nai, sequence number should be used
+ * as specified in IETF RFC 6696.
+ *
+ * When FILS shared key authentication is completed, driver needs to provide the
+ * below additional parameters to userspace, which can be either after setting
+ * up a connection or after roaming.
+ *	%NL80211_ATTR_FILS_KEK - used for key renewal
+ *	%NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM - used in further EAP-RP exchanges
+ *	%NL80211_ATTR_PMKID - used to identify the PMKSA used/generated
+ *	%Nl80211_ATTR_PMK - used to update PMKSA cache in userspace
+ * The PMKSA can be maintained in userspace persistently so that it can be used
+ * later after reboots or wifi turn off/on also.
+ *
+ * %NL80211_ATTR_FILS_CACHE_ID is the cache identifier advertized by a FILS
+ * capable AP supporting PMK caching. It specifies the scope within which the
+ * PMKSAs are cached in an ESS. %NL80211_CMD_SET_PMKSA and
+ * %NL80211_CMD_DEL_PMKSA are enhanced to allow support for PMKSA caching based
+ * on FILS cache identifier. Additionally %NL80211_ATTR_PMK is used with
+ * %NL80211_SET_PMKSA to specify the PMK corresponding to a PMKSA for driver to
+ * use in a FILS shared key connection with PMKSA caching.
+ */
+
+/**
+ * DOC: SAE authentication offload
+ *
+ * By setting @NL80211_EXT_FEATURE_SAE_OFFLOAD flag drivers can indicate they
+ * support offloading SAE authentication for WPA3-Personal networks in station
+ * mode. Similarly @NL80211_EXT_FEATURE_SAE_OFFLOAD_AP flag can be set by
+ * drivers indicating the offload support in AP mode.
+ *
+ * The password for SAE should be specified using %NL80211_ATTR_SAE_PASSWORD in
+ * %NL80211_CMD_CONNECT and %NL80211_CMD_START_AP for station and AP mode
+ * respectively.
+ */
+
+/**
+ * DOC: VLAN offload support for setting group keys and binding STAs to VLANs
+ *
+ * By setting @NL80211_EXT_FEATURE_VLAN_OFFLOAD flag drivers can indicate they
+ * support offloading VLAN functionality in a manner where the driver exposes a
+ * single netdev that uses VLAN tagged frames and separate VLAN-specific netdevs
+ * can then be added using RTM_NEWLINK/IFLA_VLAN_ID similarly to the Ethernet
+ * case. Frames received from stations that are not assigned to any VLAN are
+ * delivered on the main netdev and frames to such stations can be sent through
+ * that main netdev.
+ *
+ * %NL80211_CMD_NEW_KEY (for group keys), %NL80211_CMD_NEW_STATION, and
+ * %NL80211_CMD_SET_STATION will optionally specify vlan_id using
+ * %NL80211_ATTR_VLAN_ID.
+ */
+
+/**
+ * DOC: TID configuration
+ *
+ * TID config support can be checked in the %NL80211_ATTR_TID_CONFIG
+ * attribute given in wiphy capabilities.
+ *
+ * The necessary configuration parameters are mentioned in
+ * &enum nl80211_tid_config_attr and it will be passed to the
+ * %NL80211_CMD_SET_TID_CONFIG command in %NL80211_ATTR_TID_CONFIG.
+ *
+ * If the configuration needs to be applied for specific peer then the MAC
+ * address of the peer needs to be passed in %NL80211_ATTR_MAC, otherwise the
+ * configuration will be applied for all the connected peers in the vif except
+ * any peers that have peer specific configuration for the TID by default; if
+ * the %NL80211_TID_CONFIG_ATTR_OVERRIDE flag is set, peer specific values
+ * will be overwritten.
+ *
+ * All this configuration is valid only for STA's current connection
+ * i.e. the configuration will be reset to default when the STA connects back
+ * after disconnection/roaming, and this configuration will be cleared when
+ * the interface goes down.
+ */
+
+/**
+ * DOC: FILS shared key crypto offload
+ *
+ * This feature is applicable to drivers running in AP mode.
+ *
+ * FILS shared key crypto offload can be advertised by drivers by setting
+ * @NL80211_EXT_FEATURE_FILS_CRYPTO_OFFLOAD flag. The drivers that support
+ * FILS shared key crypto offload should be able to encrypt and decrypt
+ * association frames for FILS shared key authentication as per IEEE 802.11ai.
+ * With this capability, for FILS key derivation, drivers depend on userspace.
+ *
+ * After FILS key derivation, userspace shares the FILS AAD details with the
+ * driver and the driver stores the same to use in decryption of association
+ * request and in encryption of association response. The below parameters
+ * should be given to the driver in %NL80211_CMD_SET_FILS_AAD.
+ *	%NL80211_ATTR_MAC - STA MAC address, used for storing FILS AAD per STA
+ *	%NL80211_ATTR_FILS_KEK - Used for encryption or decryption
+ *	%NL80211_ATTR_FILS_NONCES - Used for encryption or decryption
+ *			(STA Nonce 16 bytes followed by AP Nonce 16 bytes)
+ *
+ * Once the association is done, the driver cleans the FILS AAD data.
+ */
+
+/**
+ * DOC: Multi-Link Operation
+ *
+ * In Multi-Link Operation, a connection between to MLDs utilizes multiple
+ * links. To use this in nl80211, various commands and responses now need
+ * to or will include the new %NL80211_ATTR_MLO_LINKS attribute.
+ * Additionally, various commands that need to operate on a specific link
+ * now need to be given the %NL80211_ATTR_MLO_LINK_ID attribute, e.g. to
+ * use %NL80211_CMD_START_AP or similar functions.
+ */
+
+/**
+ * enum nl80211_commands - supported nl80211 commands
+ *
+ * @NL80211_CMD_UNSPEC: unspecified command to catch errors
+ *
+ * @NL80211_CMD_GET_WIPHY: request information about a wiphy or dump request
+ *	to get a list of all present wiphys.
+ * @NL80211_CMD_SET_WIPHY: set wiphy parameters, needs %NL80211_ATTR_WIPHY or
+ *	%NL80211_ATTR_IFINDEX; can be used to set %NL80211_ATTR_WIPHY_NAME,
+ *	%NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ,
+ *	%NL80211_ATTR_WIPHY_FREQ_OFFSET (and the attributes determining the
+ *	channel width; this is used for setting monitor mode channel),
+ *	%NL80211_ATTR_WIPHY_RETRY_SHORT, %NL80211_ATTR_WIPHY_RETRY_LONG,
+ *	%NL80211_ATTR_WIPHY_FRAG_THRESHOLD, and/or
+ *	%NL80211_ATTR_WIPHY_RTS_THRESHOLD.  However, for setting the channel,
+ *	see %NL80211_CMD_SET_CHANNEL instead, the support here is for backward
+ *	compatibility only.
+ * @NL80211_CMD_NEW_WIPHY: Newly created wiphy, response to get request
+ *	or rename notification. Has attributes %NL80211_ATTR_WIPHY and
+ *	%NL80211_ATTR_WIPHY_NAME.
+ * @NL80211_CMD_DEL_WIPHY: Wiphy deleted. Has attributes
+ *	%NL80211_ATTR_WIPHY and %NL80211_ATTR_WIPHY_NAME.
+ *
+ * @NL80211_CMD_GET_INTERFACE: Request an interface's configuration;
+ *	either a dump request for all interfaces or a specific get with a
+ *	single %NL80211_ATTR_IFINDEX is supported.
+ * @NL80211_CMD_SET_INTERFACE: Set type of a virtual interface, requires
+ *	%NL80211_ATTR_IFINDEX and %NL80211_ATTR_IFTYPE.
+ * @NL80211_CMD_NEW_INTERFACE: Newly created virtual interface or response
+ *	to %NL80211_CMD_GET_INTERFACE. Has %NL80211_ATTR_IFINDEX,
+ *	%NL80211_ATTR_WIPHY and %NL80211_ATTR_IFTYPE attributes. Can also
+ *	be sent from userspace to request creation of a new virtual interface,
+ *	then requires attributes %NL80211_ATTR_WIPHY, %NL80211_ATTR_IFTYPE and
+ *	%NL80211_ATTR_IFNAME.
+ * @NL80211_CMD_DEL_INTERFACE: Virtual interface was deleted, has attributes
+ *	%NL80211_ATTR_IFINDEX and %NL80211_ATTR_WIPHY. Can also be sent from
+ *	userspace to request deletion of a virtual interface, then requires
+ *	attribute %NL80211_ATTR_IFINDEX. If multiple BSSID advertisements are
+ *	enabled using %NL80211_ATTR_MBSSID_CONFIG, %NL80211_ATTR_MBSSID_ELEMS,
+ *	and if this command is used for the transmitting interface, then all
+ *	the non-transmitting interfaces are deleted as well.
+ *
+ * @NL80211_CMD_GET_KEY: Get sequence counter information for a key specified
+ *	by %NL80211_ATTR_KEY_IDX and/or %NL80211_ATTR_MAC. %NL80211_ATTR_MAC
+ *	represents peer's MLD address for MLO pairwise key. For MLO group key,
+ *	the link is identified by %NL80211_ATTR_MLO_LINK_ID.
+ * @NL80211_CMD_SET_KEY: Set key attributes %NL80211_ATTR_KEY_DEFAULT,
+ *	%NL80211_ATTR_KEY_DEFAULT_MGMT, or %NL80211_ATTR_KEY_THRESHOLD.
+ *	For MLO connection, the link to set default key is identified by
+ *	%NL80211_ATTR_MLO_LINK_ID.
+ * @NL80211_CMD_NEW_KEY: add a key with given %NL80211_ATTR_KEY_DATA,
+ *	%NL80211_ATTR_KEY_IDX, %NL80211_ATTR_MAC, %NL80211_ATTR_KEY_CIPHER,
+ *	and %NL80211_ATTR_KEY_SEQ attributes. %NL80211_ATTR_MAC represents
+ *	peer's MLD address for MLO pairwise key. The link to add MLO
+ *	group key is identified by %NL80211_ATTR_MLO_LINK_ID.
+ * @NL80211_CMD_DEL_KEY: delete a key identified by %NL80211_ATTR_KEY_IDX
+ *	or %NL80211_ATTR_MAC. %NL80211_ATTR_MAC represents peer's MLD address
+ *	for MLO pairwise key. The link to delete group key is identified by
+ *	%NL80211_ATTR_MLO_LINK_ID.
+ *
+ * @NL80211_CMD_GET_BEACON: (not used)
+ * @NL80211_CMD_SET_BEACON: change the beacon on an access point interface
+ *	using the %NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL
+ *	attributes. For drivers that generate the beacon and probe responses
+ *	internally, the following attributes must be provided: %NL80211_ATTR_IE,
+ *	%NL80211_ATTR_IE_PROBE_RESP and %NL80211_ATTR_IE_ASSOC_RESP.
+ * @NL80211_CMD_START_AP: Start AP operation on an AP interface, parameters
+ *	are like for %NL80211_CMD_SET_BEACON, and additionally parameters that
+ *	do not change are used, these include %NL80211_ATTR_BEACON_INTERVAL,
+ *	%NL80211_ATTR_DTIM_PERIOD, %NL80211_ATTR_SSID,
+ *	%NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHERS_PAIRWISE,
+ *	%NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS,
+ *	%NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY,
+ *	%NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_INACTIVITY_TIMEOUT,
+ *	%NL80211_ATTR_ACL_POLICY and %NL80211_ATTR_MAC_ADDRS.
+ *	The channel to use can be set on the interface or be given using the
+ *	%NL80211_ATTR_WIPHY_FREQ and %NL80211_ATTR_WIPHY_FREQ_OFFSET, and the
+ *	attributes determining channel width.
+ * @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP
+ * @NL80211_CMD_STOP_AP: Stop AP operation on the given interface
+ * @NL80211_CMD_DEL_BEACON: old alias for %NL80211_CMD_STOP_AP
+ *
+ * @NL80211_CMD_GET_STATION: Get station attributes for station identified by
+ *	%NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_SET_STATION: Set station attributes for station identified by
+ *	%NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_NEW_STATION: Add a station with given attributes to the
+ *	interface identified by %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_DEL_STATION: Remove a station identified by %NL80211_ATTR_MAC
+ *	or, if no MAC address given, all stations, on the interface identified
+ *	by %NL80211_ATTR_IFINDEX. For MLD station, MLD address is used in
+ *	%NL80211_ATTR_MAC. %NL80211_ATTR_MGMT_SUBTYPE and
+ *	%NL80211_ATTR_REASON_CODE can optionally be used to specify which type
+ *	of disconnection indication should be sent to the station
+ *	(Deauthentication or Disassociation frame and reason code for that
+ *	frame).
+ *
+ * @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to
+ * 	destination %NL80211_ATTR_MAC on the interface identified by
+ * 	%NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_SET_MPATH:  Set mesh path attributes for mesh path to
+ * 	destination %NL80211_ATTR_MAC on the interface identified by
+ * 	%NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_NEW_MPATH: Create a new mesh path for the destination given by
+ *	%NL80211_ATTR_MAC via %NL80211_ATTR_MPATH_NEXT_HOP.
+ * @NL80211_CMD_DEL_MPATH: Delete a mesh path to the destination given by
+ *	%NL80211_ATTR_MAC.
+ * @NL80211_CMD_NEW_PATH: Add a mesh path with given attributes to the
+ *	interface identified by %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_DEL_PATH: Remove a mesh path identified by %NL80211_ATTR_MAC
+ *	or, if no MAC address given, all mesh paths, on the interface identified
+ *	by %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_SET_BSS: Set BSS attributes for BSS identified by
+ *	%NL80211_ATTR_IFINDEX.
+ *
+ * @NL80211_CMD_GET_REG: ask the wireless core to send us its currently set
+ *	regulatory domain. If %NL80211_ATTR_WIPHY is specified and the device
+ *	has a private regulatory domain, it will be returned. Otherwise, the
+ *	global regdomain will be returned.
+ *	A device will have a private regulatory domain if it uses the
+ *	regulatory_hint() API. Even when a private regdomain is used the channel
+ *	information will still be mended according to further hints from
+ *	the regulatory core to help with compliance. A dump version of this API
+ *	is now available which will returns the global regdomain as well as
+ *	all private regdomains of present wiphys (for those that have it).
+ *	If a wiphy is self-managed (%NL80211_ATTR_WIPHY_SELF_MANAGED_REG), then
+ *	its private regdomain is the only valid one for it. The regulatory
+ *	core is not used to help with compliance in this case.
+ * @NL80211_CMD_SET_REG: Set current regulatory domain. CRDA sends this command
+ *	after being queried by the kernel. CRDA replies by sending a regulatory
+ *	domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our
+ *	current alpha2 if it found a match. It also provides
+ * 	NL80211_ATTR_REG_RULE_FLAGS, and a set of regulatory rules. Each
+ * 	regulatory rule is a nested set of attributes  given by
+ * 	%NL80211_ATTR_REG_RULE_FREQ_[START|END] and
+ * 	%NL80211_ATTR_FREQ_RANGE_MAX_BW with an attached power rule given by
+ * 	%NL80211_ATTR_REG_RULE_POWER_MAX_ANT_GAIN and
+ * 	%NL80211_ATTR_REG_RULE_POWER_MAX_EIRP.
+ * @NL80211_CMD_REQ_SET_REG: ask the wireless core to set the regulatory domain
+ * 	to the specified ISO/IEC 3166-1 alpha2 country code. The core will
+ * 	store this as a valid request and then query userspace for it.
+ *
+ * @NL80211_CMD_GET_MESH_CONFIG: Get mesh networking properties for the
+ *	interface identified by %NL80211_ATTR_IFINDEX
+ *
+ * @NL80211_CMD_SET_MESH_CONFIG: Set mesh networking properties for the
+ *      interface identified by %NL80211_ATTR_IFINDEX
+ *
+ * @NL80211_CMD_SET_MGMT_EXTRA_IE: Set extra IEs for management frames. The
+ *	interface is identified with %NL80211_ATTR_IFINDEX and the management
+ *	frame subtype with %NL80211_ATTR_MGMT_SUBTYPE. The extra IE data to be
+ *	added to the end of the specified management frame is specified with
+ *	%NL80211_ATTR_IE. If the command succeeds, the requested data will be
+ *	added to all specified management frames generated by
+ *	kernel/firmware/driver.
+ *	Note: This command has been removed and it is only reserved at this
+ *	point to avoid re-using existing command number. The functionality this
+ *	command was planned for has been provided with cleaner design with the
+ *	option to specify additional IEs in NL80211_CMD_TRIGGER_SCAN,
+ *	NL80211_CMD_AUTHENTICATE, NL80211_CMD_ASSOCIATE,
+ *	NL80211_CMD_DEAUTHENTICATE, and NL80211_CMD_DISASSOCIATE.
+ *
+ * @NL80211_CMD_GET_SCAN: get scan results
+ * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters
+ *	%NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the
+ *	probe requests at CCK rate or not. %NL80211_ATTR_BSSID can be used to
+ *	specify a BSSID to scan for; if not included, the wildcard BSSID will
+ *	be used.
+ * @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to
+ *	NL80211_CMD_GET_SCAN and on the "scan" multicast group)
+ * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons,
+ *	partial scan results may be available
+ *
+ * @NL80211_CMD_START_SCHED_SCAN: start a scheduled scan at certain
+ *	intervals and certain number of cycles, as specified by
+ *	%NL80211_ATTR_SCHED_SCAN_PLANS. If %NL80211_ATTR_SCHED_SCAN_PLANS is
+ *	not specified and only %NL80211_ATTR_SCHED_SCAN_INTERVAL is specified,
+ *	scheduled scan will run in an infinite loop with the specified interval.
+ *	These attributes are mutually exculsive,
+ *	i.e. NL80211_ATTR_SCHED_SCAN_INTERVAL must not be passed if
+ *	NL80211_ATTR_SCHED_SCAN_PLANS is defined.
+ *	If for some reason scheduled scan is aborted by the driver, all scan
+ *	plans are canceled (including scan plans that did not start yet).
+ *	Like with normal scans, if SSIDs (%NL80211_ATTR_SCAN_SSIDS)
+ *	are passed, they are used in the probe requests.  For
+ *	broadcast, a broadcast SSID must be passed (ie. an empty
+ *	string).  If no SSID is passed, no probe requests are sent and
+ *	a passive scan is performed.  %NL80211_ATTR_SCAN_FREQUENCIES,
+ *	if passed, define which channels should be scanned; if not
+ *	passed, all channels allowed for the current regulatory domain
+ *	are used.  Extra IEs can also be passed from the userspace by
+ *	using the %NL80211_ATTR_IE attribute.  The first cycle of the
+ *	scheduled scan can be delayed by %NL80211_ATTR_SCHED_SCAN_DELAY
+ *	is supplied. If the device supports multiple concurrent scheduled
+ *	scans, it will allow such when the caller provides the flag attribute
+ *	%NL80211_ATTR_SCHED_SCAN_MULTI to indicate user-space support for it.
+ * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT if
+ *	scheduled scan is not running. The caller may assume that as soon
+ *	as the call returns, it is safe to start a new scheduled scan again.
+ * @NL80211_CMD_SCHED_SCAN_RESULTS: indicates that there are scheduled scan
+ *	results available.
+ * @NL80211_CMD_SCHED_SCAN_STOPPED: indicates that the scheduled scan has
+ *	stopped.  The driver may issue this event at any time during a
+ *	scheduled scan.  One reason for stopping the scan is if the hardware
+ *	does not support starting an association or a normal scan while running
+ *	a scheduled scan.  This event is also sent when the
+ *	%NL80211_CMD_STOP_SCHED_SCAN command is received or when the interface
+ *	is brought down while a scheduled scan was running.
+ *
+ * @NL80211_CMD_GET_SURVEY: get survey resuls, e.g. channel occupation
+ *      or noise level
+ * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to
+ *	NL80211_CMD_GET_SURVEY and on the "scan" multicast group)
+ *
+ * @NL80211_CMD_SET_PMKSA: Add a PMKSA cache entry using %NL80211_ATTR_MAC
+ *	(for the BSSID), %NL80211_ATTR_PMKID, and optionally %NL80211_ATTR_PMK
+ *	(PMK is used for PTKSA derivation in case of FILS shared key offload) or
+ *	using %NL80211_ATTR_SSID, %NL80211_ATTR_FILS_CACHE_ID,
+ *	%NL80211_ATTR_PMKID, and %NL80211_ATTR_PMK in case of FILS
+ *	authentication where %NL80211_ATTR_FILS_CACHE_ID is the identifier
+ *	advertized by a FILS capable AP identifying the scope of PMKSA in an
+ *	ESS.
+ * @NL80211_CMD_DEL_PMKSA: Delete a PMKSA cache entry, using %NL80211_ATTR_MAC
+ *	(for the BSSID) and %NL80211_ATTR_PMKID or using %NL80211_ATTR_SSID,
+ *	%NL80211_ATTR_FILS_CACHE_ID, and %NL80211_ATTR_PMKID in case of FILS
+ *	authentication.
+ * @NL80211_CMD_FLUSH_PMKSA: Flush all PMKSA cache entries.
+ *
+ * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain
+ * 	has been changed and provides details of the request information
+ * 	that caused the change such as who initiated the regulatory request
+ * 	(%NL80211_ATTR_REG_INITIATOR), the wiphy_idx
+ * 	(%NL80211_ATTR_REG_ALPHA2) on which the request was made from if
+ * 	the initiator was %NL80211_REGDOM_SET_BY_COUNTRY_IE or
+ * 	%NL80211_REGDOM_SET_BY_DRIVER, the type of regulatory domain
+ * 	set (%NL80211_ATTR_REG_TYPE), if the type of regulatory domain is
+ * 	%NL80211_REG_TYPE_COUNTRY the alpha2 to which we have moved on
+ * 	to (%NL80211_ATTR_REG_ALPHA2).
+ * @NL80211_CMD_REG_BEACON_HINT: indicates to userspace that an AP beacon
+ * 	has been found while world roaming thus enabling active scan or
+ * 	any mode of operation that initiates TX (beacons) on a channel
+ * 	where we would not have been able to do either before. As an example
+ * 	if you are world roaming (regulatory domain set to world or if your
+ * 	driver is using a custom world roaming regulatory domain) and while
+ * 	doing a passive scan on the 5 GHz band you find an AP there (if not
+ * 	on a DFS channel) you will now be able to actively scan for that AP
+ * 	or use AP mode on your card on that same channel. Note that this will
+ * 	never be used for channels 1-11 on the 2 GHz band as they are always
+ * 	enabled world wide. This beacon hint is only sent if your device had
+ * 	either disabled active scanning or beaconing on a channel. We send to
+ * 	userspace the wiphy on which we removed a restriction from
+ * 	(%NL80211_ATTR_WIPHY) and the channel on which this occurred
+ * 	before (%NL80211_ATTR_FREQ_BEFORE) and after (%NL80211_ATTR_FREQ_AFTER)
+ * 	the beacon hint was processed.
+ *
+ * @NL80211_CMD_AUTHENTICATE: authentication request and notification.
+ *	This command is used both as a command (request to authenticate) and
+ *	as an event on the "mlme" multicast group indicating completion of the
+ *	authentication process.
+ *	When used as a command, %NL80211_ATTR_IFINDEX is used to identify the
+ *	interface. %NL80211_ATTR_MAC is used to specify PeerSTAAddress (and
+ *	BSSID in case of station mode). %NL80211_ATTR_SSID is used to specify
+ *	the SSID (mainly for association, but is included in authentication
+ *	request, too, to help BSS selection. %NL80211_ATTR_WIPHY_FREQ +
+ *	%NL80211_ATTR_WIPHY_FREQ_OFFSET is used to specify the frequence of the
+ *	channel in MHz. %NL80211_ATTR_AUTH_TYPE is used to specify the
+ *	authentication type. %NL80211_ATTR_IE is used to define IEs
+ *	(VendorSpecificInfo, but also including RSN IE and FT IEs) to be added
+ *	to the frame.
+ *	When used as an event, this reports reception of an Authentication
+ *	frame in station and IBSS modes when the local MLME processed the
+ *	frame, i.e., it was for the local STA and was received in correct
+ *	state. This is similar to MLME-AUTHENTICATE.confirm primitive in the
+ *	MLME SAP interface (kernel providing MLME, userspace SME). The
+ *	included %NL80211_ATTR_FRAME attribute contains the management frame
+ *	(including both the header and frame body, but not FCS). This event is
+ *	also used to indicate if the authentication attempt timed out. In that
+ *	case the %NL80211_ATTR_FRAME attribute is replaced with a
+ *	%NL80211_ATTR_TIMED_OUT flag (and %NL80211_ATTR_MAC to indicate which
+ *	pending authentication timed out).
+ * @NL80211_CMD_ASSOCIATE: association request and notification; like
+ *	NL80211_CMD_AUTHENTICATE but for Association and Reassociation
+ *	(similar to MLME-ASSOCIATE.request, MLME-REASSOCIATE.request,
+ *	MLME-ASSOCIATE.confirm or MLME-REASSOCIATE.confirm primitives). The
+ *	%NL80211_ATTR_PREV_BSSID attribute is used to specify whether the
+ *	request is for the initial association to an ESS (that attribute not
+ *	included) or for reassociation within the ESS (that attribute is
+ *	included).
+ * @NL80211_CMD_DEAUTHENTICATE: deauthentication request and notification; like
+ *	NL80211_CMD_AUTHENTICATE but for Deauthentication frames (similar to
+ *	MLME-DEAUTHENTICATION.request and MLME-DEAUTHENTICATE.indication
+ *	primitives).
+ * @NL80211_CMD_DISASSOCIATE: disassociation request and notification; like
+ *	NL80211_CMD_AUTHENTICATE but for Disassociation frames (similar to
+ *	MLME-DISASSOCIATE.request and MLME-DISASSOCIATE.indication primitives).
+ *
+ * @NL80211_CMD_MICHAEL_MIC_FAILURE: notification of a locally detected Michael
+ *	MIC (part of TKIP) failure; sent on the "mlme" multicast group; the
+ *	event includes %NL80211_ATTR_MAC to describe the source MAC address of
+ *	the frame with invalid MIC, %NL80211_ATTR_KEY_TYPE to show the key
+ *	type, %NL80211_ATTR_KEY_IDX to indicate the key identifier, and
+ *	%NL80211_ATTR_KEY_SEQ to indicate the TSC value of the frame; this
+ *	event matches with MLME-MICHAELMICFAILURE.indication() primitive
+ *
+ * @NL80211_CMD_JOIN_IBSS: Join a new IBSS -- given at least an SSID and a
+ *	FREQ attribute (for the initial frequency if no peer can be found)
+ *	and optionally a MAC (as BSSID) and FREQ_FIXED attribute if those
+ *	should be fixed rather than automatically determined. Can only be
+ *	executed on a network interface that is UP, and fixed BSSID/FREQ
+ *	may be rejected. Another optional parameter is the beacon interval,
+ *	given in the %NL80211_ATTR_BEACON_INTERVAL attribute, which if not
+ *	given defaults to 100 TU (102.4ms).
+ * @NL80211_CMD_LEAVE_IBSS: Leave the IBSS -- no special arguments, the IBSS is
+ *	determined by the network interface.
+ *
+ * @NL80211_CMD_TESTMODE: testmode command, takes a wiphy (or ifindex) attribute
+ *	to identify the device, and the TESTDATA blob attribute to pass through
+ *	to the driver.
+ *
+ * @NL80211_CMD_CONNECT: connection request and notification; this command
+ *	requests to connect to a specified network but without separating
+ *	auth and assoc steps. For this, you need to specify the SSID in a
+ *	%NL80211_ATTR_SSID attribute, and can optionally specify the association
+ *	IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE,
+ *	%NL80211_ATTR_USE_MFP, %NL80211_ATTR_MAC, %NL80211_ATTR_WIPHY_FREQ,
+ *	%NL80211_ATTR_WIPHY_FREQ_OFFSET, %NL80211_ATTR_CONTROL_PORT,
+ *	%NL80211_ATTR_CONTROL_PORT_ETHERTYPE,
+ *	%NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT,
+ *	%NL80211_ATTR_CONTROL_PORT_OVER_NL80211, %NL80211_ATTR_MAC_HINT, and
+ *	%NL80211_ATTR_WIPHY_FREQ_HINT.
+ *	If included, %NL80211_ATTR_MAC and %NL80211_ATTR_WIPHY_FREQ are
+ *	restrictions on BSS selection, i.e., they effectively prevent roaming
+ *	within the ESS. %NL80211_ATTR_MAC_HINT and %NL80211_ATTR_WIPHY_FREQ_HINT
+ *	can be included to provide a recommendation of the initial BSS while
+ *	allowing the driver to roam to other BSSes within the ESS and also to
+ *	ignore this recommendation if the indicated BSS is not ideal. Only one
+ *	set of BSSID,frequency parameters is used (i.e., either the enforcing
+ *	%NL80211_ATTR_MAC,%NL80211_ATTR_WIPHY_FREQ or the less strict
+ *	%NL80211_ATTR_MAC_HINT and %NL80211_ATTR_WIPHY_FREQ_HINT).
+ *	Driver shall not modify the IEs specified through %NL80211_ATTR_IE if
+ *	%NL80211_ATTR_MAC is included. However, if %NL80211_ATTR_MAC_HINT is
+ *	included, these IEs through %NL80211_ATTR_IE are specified by the user
+ *	space based on the best possible BSS selected. Thus, if the driver ends
+ *	up selecting a different BSS, it can modify these IEs accordingly (e.g.
+ *	userspace asks the driver to perform PMKSA caching with BSS1 and the
+ *	driver ends up selecting BSS2 with different PMKSA cache entry; RSNIE
+ *	has to get updated with the apt PMKID).
+ *	%NL80211_ATTR_PREV_BSSID can be used to request a reassociation within
+ *	the ESS in case the device is already associated and an association with
+ *	a different BSS is desired.
+ *	Background scan period can optionally be
+ *	specified in %NL80211_ATTR_BG_SCAN_PERIOD,
+ *	if not specified default background scan configuration
+ *	in driver is used and if period value is 0, bg scan will be disabled.
+ *	This attribute is ignored if driver does not support roam scan.
+ *	It is also sent as an event, with the BSSID and response IEs when the
+ *	connection is established or failed to be established. This can be
+ *	determined by the %NL80211_ATTR_STATUS_CODE attribute (0 = success,
+ *	non-zero = failure). If %NL80211_ATTR_TIMED_OUT is included in the
+ *	event, the connection attempt failed due to not being able to initiate
+ *	authentication/association or not receiving a response from the AP.
+ *	Non-zero %NL80211_ATTR_STATUS_CODE value is indicated in that case as
+ *	well to remain backwards compatible.
+ * @NL80211_CMD_ROAM: Notification indicating the card/driver roamed by itself.
+ *	When a security association was established on an 802.1X network using
+ *	fast transition, this event should be followed by an
+ *	%NL80211_CMD_PORT_AUTHORIZED event.
+ *	Following a %NL80211_CMD_ROAM event userspace can issue
+ *	%NL80211_CMD_GET_SCAN in order to obtain the scan information for the
+ *	new BSS the card/driver roamed to.
+ * @NL80211_CMD_DISCONNECT: drop a given connection; also used to notify
+ *	userspace that a connection was dropped by the AP or due to other
+ *	reasons, for this the %NL80211_ATTR_DISCONNECTED_BY_AP and
+ *	%NL80211_ATTR_REASON_CODE attributes are used.
+ *
+ * @NL80211_CMD_SET_WIPHY_NETNS: Set a wiphy's netns. Note that all devices
+ *	associated with this wiphy must be down and will follow.
+ *
+ * @NL80211_CMD_REMAIN_ON_CHANNEL: Request to remain awake on the specified
+ *	channel for the specified amount of time. This can be used to do
+ *	off-channel operations like transmit a Public Action frame and wait for
+ *	a response while being associated to an AP on another channel.
+ *	%NL80211_ATTR_IFINDEX is used to specify which interface (and thus
+ *	radio) is used. %NL80211_ATTR_WIPHY_FREQ is used to specify the
+ *	frequency for the operation.
+ *	%NL80211_ATTR_DURATION is used to specify the duration in milliseconds
+ *	to remain on the channel. This command is also used as an event to
+ *	notify when the requested duration starts (it may take a while for the
+ *	driver to schedule this time due to other concurrent needs for the
+ *	radio).
+ *	When called, this operation returns a cookie (%NL80211_ATTR_COOKIE)
+ *	that will be included with any events pertaining to this request;
+ *	the cookie is also used to cancel the request.
+ * @NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL: This command can be used to cancel a
+ *	pending remain-on-channel duration if the desired operation has been
+ *	completed prior to expiration of the originally requested duration.
+ *	%NL80211_ATTR_WIPHY or %NL80211_ATTR_IFINDEX is used to specify the
+ *	radio. The %NL80211_ATTR_COOKIE attribute must be given as well to
+ *	uniquely identify the request.
+ *	This command is also used as an event to notify when a requested
+ *	remain-on-channel duration has expired.
+ *
+ * @NL80211_CMD_SET_TX_BITRATE_MASK: Set the mask of rates to be used in TX
+ *	rate selection. %NL80211_ATTR_IFINDEX is used to specify the interface
+ *	and @NL80211_ATTR_TX_RATES the set of allowed rates.
+ *
+ * @NL80211_CMD_REGISTER_FRAME: Register for receiving certain mgmt frames
+ *	(via @NL80211_CMD_FRAME) for processing in userspace. This command
+ *	requires an interface index, a frame type attribute (optional for
+ *	backward compatibility reasons, if not given assumes action frames)
+ *	and a match attribute containing the first few bytes of the frame
+ *	that should match, e.g. a single byte for only a category match or
+ *	four bytes for vendor frames including the OUI. The registration
+ *	cannot be dropped, but is removed automatically when the netlink
+ *	socket is closed. Multiple registrations can be made.
+ *	The %NL80211_ATTR_RECEIVE_MULTICAST flag attribute can be given if
+ *	%NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS is available, in which
+ *	case the registration can also be modified to include/exclude the
+ *	flag, rather than requiring unregistration to change it.
+ * @NL80211_CMD_REGISTER_ACTION: Alias for @NL80211_CMD_REGISTER_FRAME for
+ *	backward compatibility
+ * @NL80211_CMD_FRAME: Management frame TX request and RX notification. This
+ *	command is used both as a request to transmit a management frame and
+ *	as an event indicating reception of a frame that was not processed in
+ *	kernel code, but is for us (i.e., which may need to be processed in a
+ *	user space application). %NL80211_ATTR_FRAME is used to specify the
+ *	frame contents (including header). %NL80211_ATTR_WIPHY_FREQ is used
+ *	to indicate on which channel the frame is to be transmitted or was
+ *	received. If this channel is not the current channel (remain-on-channel
+ *	or the operational channel) the device will switch to the given channel
+ *	and transmit the frame, optionally waiting for a response for the time
+ *	specified using %NL80211_ATTR_DURATION. When called, this operation
+ *	returns a cookie (%NL80211_ATTR_COOKIE) that will be included with the
+ *	TX status event pertaining to the TX request.
+ *	%NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the
+ *	management frames at CCK rate or not in 2GHz band.
+ *	%NL80211_ATTR_CSA_C_OFFSETS_TX is an array of offsets to CSA
+ *	counters which will be updated to the current value. This attribute
+ *	is used during CSA period.
+ *	For TX on an MLD, the frequency can be omitted and the link ID be
+ *	specified, or if transmitting to a known peer MLD (with MLD addresses
+ *	in the frame) both can be omitted and the link will be selected by
+ *	lower layers.
+ *	For RX notification, %NL80211_ATTR_RX_HW_TIMESTAMP may be included to
+ *	indicate the frame RX timestamp and %NL80211_ATTR_TX_HW_TIMESTAMP may
+ *	be included to indicate the ack TX timestamp.
+ * @NL80211_CMD_FRAME_WAIT_CANCEL: When an off-channel TX was requested, this
+ *	command may be used with the corresponding cookie to cancel the wait
+ *	time if it is known that it is no longer necessary.  This command is
+ *	also sent as an event whenever the driver has completed the off-channel
+ *	wait time.
+ * @NL80211_CMD_ACTION: Alias for @NL80211_CMD_FRAME for backward compatibility.
+ * @NL80211_CMD_FRAME_TX_STATUS: Report TX status of a management frame
+ *	transmitted with %NL80211_CMD_FRAME. %NL80211_ATTR_COOKIE identifies
+ *	the TX command and %NL80211_ATTR_FRAME includes the contents of the
+ *	frame. %NL80211_ATTR_ACK flag is included if the recipient acknowledged
+ *	the frame. %NL80211_ATTR_TX_HW_TIMESTAMP may be included to indicate the
+ *	tx timestamp and %NL80211_ATTR_RX_HW_TIMESTAMP may be included to
+ *	indicate the ack RX timestamp.
+ * @NL80211_CMD_ACTION_TX_STATUS: Alias for @NL80211_CMD_FRAME_TX_STATUS for
+ *	backward compatibility.
+ *
+ * @NL80211_CMD_SET_POWER_SAVE: Set powersave, using %NL80211_ATTR_PS_STATE
+ * @NL80211_CMD_GET_POWER_SAVE: Get powersave status in %NL80211_ATTR_PS_STATE
+ *
+ * @NL80211_CMD_SET_CQM: Connection quality monitor configuration. This command
+ *	is used to configure connection quality monitoring notification trigger
+ *	levels.
+ * @NL80211_CMD_NOTIFY_CQM: Connection quality monitor notification. This
+ *	command is used as an event to indicate the that a trigger level was
+ *	reached.
+ * @NL80211_CMD_SET_CHANNEL: Set the channel (using %NL80211_ATTR_WIPHY_FREQ
+ *	and the attributes determining channel width) the given interface
+ *	(identifed by %NL80211_ATTR_IFINDEX) shall operate on.
+ *	In case multiple channels are supported by the device, the mechanism
+ *	with which it switches channels is implementation-defined.
+ *	When a monitor interface is given, it can only switch channel while
+ *	no other interfaces are operating to avoid disturbing the operation
+ *	of any other interfaces, and other interfaces will again take
+ *	precedence when they are used.
+ *
+ * @NL80211_CMD_SET_WDS_PEER: Set the MAC address of the peer on a WDS interface
+ *	(no longer supported).
+ *
+ * @NL80211_CMD_SET_MULTICAST_TO_UNICAST: Configure if this AP should perform
+ *	multicast to unicast conversion. When enabled, all multicast packets
+ *	with ethertype ARP, IPv4 or IPv6 (possibly within an 802.1Q header)
+ *	will be sent out to each station once with the destination (multicast)
+ *	MAC address replaced by the station's MAC address. Note that this may
+ *	break certain expectations of the receiver, e.g. the ability to drop
+ *	unicast IP packets encapsulated in multicast L2 frames, or the ability
+ *	to not send destination unreachable messages in such cases.
+ *	This can only be toggled per BSS. Configure this on an interface of
+ *	type %NL80211_IFTYPE_AP. It applies to all its VLAN interfaces
+ *	(%NL80211_IFTYPE_AP_VLAN), except for those in 4addr (WDS) mode.
+ *	If %NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED is not present with this
+ *	command, the feature is disabled.
+ *
+ * @NL80211_CMD_JOIN_MESH: Join a mesh. The mesh ID must be given, and initial
+ *	mesh config parameters may be given.
+ * @NL80211_CMD_LEAVE_MESH: Leave the mesh network -- no special arguments, the
+ *	network is determined by the network interface.
+ *
+ * @NL80211_CMD_UNPROT_DEAUTHENTICATE: Unprotected deauthentication frame
+ *	notification. This event is used to indicate that an unprotected
+ *	deauthentication frame was dropped when MFP is in use.
+ * @NL80211_CMD_UNPROT_DISASSOCIATE: Unprotected disassociation frame
+ *	notification. This event is used to indicate that an unprotected
+ *	disassociation frame was dropped when MFP is in use.
+ *
+ * @NL80211_CMD_NEW_PEER_CANDIDATE: Notification on the reception of a
+ *      beacon or probe response from a compatible mesh peer.  This is only
+ *      sent while no station information (sta_info) exists for the new peer
+ *      candidate and when @NL80211_MESH_SETUP_USERSPACE_AUTH,
+ *      @NL80211_MESH_SETUP_USERSPACE_AMPE, or
+ *      @NL80211_MESH_SETUP_USERSPACE_MPM is set.  On reception of this
+ *      notification, userspace may decide to create a new station
+ *      (@NL80211_CMD_NEW_STATION).  To stop this notification from
+ *      reoccurring, the userspace authentication daemon may want to create the
+ *      new station with the AUTHENTICATED flag unset and maybe change it later
+ *      depending on the authentication result.
+ *
+ * @NL80211_CMD_GET_WOWLAN: get Wake-on-Wireless-LAN (WoWLAN) settings.
+ * @NL80211_CMD_SET_WOWLAN: set Wake-on-Wireless-LAN (WoWLAN) settings.
+ *	Since wireless is more complex than wired ethernet, it supports
+ *	various triggers. These triggers can be configured through this
+ *	command with the %NL80211_ATTR_WOWLAN_TRIGGERS attribute. For
+ *	more background information, see
+ *	https://wireless.wiki.kernel.org/en/users/Documentation/WoWLAN.
+ *	The @NL80211_CMD_SET_WOWLAN command can also be used as a notification
+ *	from the driver reporting the wakeup reason. In this case, the
+ *	@NL80211_ATTR_WOWLAN_TRIGGERS attribute will contain the reason
+ *	for the wakeup, if it was caused by wireless. If it is not present
+ *	in the wakeup notification, the wireless device didn't cause the
+ *	wakeup but reports that it was woken up.
+ *
+ * @NL80211_CMD_SET_REKEY_OFFLOAD: This command is used give the driver
+ *	the necessary information for supporting GTK rekey offload. This
+ *	feature is typically used during WoWLAN. The configuration data
+ *	is contained in %NL80211_ATTR_REKEY_DATA (which is nested and
+ *	contains the data in sub-attributes). After rekeying happened,
+ *	this command may also be sent by the driver as an MLME event to
+ *	inform userspace of the new replay counter.
+ *
+ * @NL80211_CMD_PMKSA_CANDIDATE: This is used as an event to inform userspace
+ *	of PMKSA caching dandidates.
+ *
+ * @NL80211_CMD_TDLS_OPER: Perform a high-level TDLS command (e.g. link setup).
+ *	In addition, this can be used as an event to request userspace to take
+ *	actions on TDLS links (set up a new link or tear down an existing one).
+ *	In such events, %NL80211_ATTR_TDLS_OPERATION indicates the requested
+ *	operation, %NL80211_ATTR_MAC contains the peer MAC address, and
+ *	%NL80211_ATTR_REASON_CODE the reason code to be used (only with
+ *	%NL80211_TDLS_TEARDOWN).
+ * @NL80211_CMD_TDLS_MGMT: Send a TDLS management frame. The
+ *	%NL80211_ATTR_TDLS_ACTION attribute determines the type of frame to be
+ *	sent. Public Action codes (802.11-2012 8.1.5.1) will be sent as
+ *	802.11 management frames, while TDLS action codes (802.11-2012
+ *	8.5.13.1) will be encapsulated and sent as data frames. The currently
+ *	supported Public Action code is %WLAN_PUB_ACTION_TDLS_DISCOVER_RES
+ *	and the currently supported TDLS actions codes are given in
+ *	&enum ieee80211_tdls_actioncode.
+ *
+ * @NL80211_CMD_UNEXPECTED_FRAME: Used by an application controlling an AP
+ *	(or GO) interface (i.e. hostapd) to ask for unexpected frames to
+ *	implement sending deauth to stations that send unexpected class 3
+ *	frames. Also used as the event sent by the kernel when such a frame
+ *	is received.
+ *	For the event, the %NL80211_ATTR_MAC attribute carries the TA and
+ *	other attributes like the interface index are present.
+ *	If used as the command it must have an interface index and you can
+ *	only unsubscribe from the event by closing the socket. Subscription
+ *	is also for %NL80211_CMD_UNEXPECTED_4ADDR_FRAME events.
+ *
+ * @NL80211_CMD_UNEXPECTED_4ADDR_FRAME: Sent as an event indicating that the
+ *	associated station identified by %NL80211_ATTR_MAC sent a 4addr frame
+ *	and wasn't already in a 4-addr VLAN. The event will be sent similarly
+ *	to the %NL80211_CMD_UNEXPECTED_FRAME event, to the same listener.
+ *
+ * @NL80211_CMD_PROBE_CLIENT: Probe an associated station on an AP interface
+ *	by sending a null data frame to it and reporting when the frame is
+ *	acknowleged. This is used to allow timing out inactive clients. Uses
+ *	%NL80211_ATTR_IFINDEX and %NL80211_ATTR_MAC. The command returns a
+ *	direct reply with an %NL80211_ATTR_COOKIE that is later used to match
+ *	up the event with the request. The event includes the same data and
+ *	has %NL80211_ATTR_ACK set if the frame was ACKed.
+ *
+ * @NL80211_CMD_REGISTER_BEACONS: Register this socket to receive beacons from
+ *	other BSSes when any interfaces are in AP mode. This helps implement
+ *	OLBC handling in hostapd. Beacons are reported in %NL80211_CMD_FRAME
+ *	messages. Note that per PHY only one application may register.
+ *
+ * @NL80211_CMD_SET_NOACK_MAP: sets a bitmap for the individual TIDs whether
+ *      No Acknowledgement Policy should be applied.
+ *
+ * @NL80211_CMD_CH_SWITCH_NOTIFY: An AP or GO may decide to switch channels
+ *	independently of the userspace SME, send this event indicating
+ *	%NL80211_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ and the
+ *	attributes determining channel width.  This indication may also be
+ *	sent when a remotely-initiated switch (e.g., when a STA receives a CSA
+ *	from the remote AP) is completed;
+ *
+ * @NL80211_CMD_CH_SWITCH_STARTED_NOTIFY: Notify that a channel switch
+ *	has been started on an interface, regardless of the initiator
+ *	(ie. whether it was requested from a remote device or
+ *	initiated on our own).  It indicates that
+ *	%NL80211_ATTR_IFINDEX will be on %NL80211_ATTR_WIPHY_FREQ
+ *	after %NL80211_ATTR_CH_SWITCH_COUNT TBTT's.  The userspace may
+ *	decide to react to this indication by requesting other
+ *	interfaces to change channel as well.
+ *
+ * @NL80211_CMD_START_P2P_DEVICE: Start the given P2P Device, identified by
+ *	its %NL80211_ATTR_WDEV identifier. It must have been created with
+ *	%NL80211_CMD_NEW_INTERFACE previously. After it has been started, the
+ *	P2P Device can be used for P2P operations, e.g. remain-on-channel and
+ *	public action frame TX.
+ * @NL80211_CMD_STOP_P2P_DEVICE: Stop the given P2P Device, identified by
+ *	its %NL80211_ATTR_WDEV identifier.
+ *
+ * @NL80211_CMD_CONN_FAILED: connection request to an AP failed; used to
+ *	notify userspace that AP has rejected the connection request from a
+ *	station, due to particular reason. %NL80211_ATTR_CONN_FAILED_REASON
+ *	is used for this.
+ *
+ * @NL80211_CMD_SET_MCAST_RATE: Change the rate used to send multicast frames
+ *	for IBSS or MESH vif.
+ *
+ * @NL80211_CMD_SET_MAC_ACL: sets ACL for MAC address based access control.
+ *	This is to be used with the drivers advertising the support of MAC
+ *	address based access control. List of MAC addresses is passed in
+ *	%NL80211_ATTR_MAC_ADDRS and ACL policy is passed in
+ *	%NL80211_ATTR_ACL_POLICY. Driver will enable ACL with this list, if it
+ *	is not already done. The new list will replace any existing list. Driver
+ *	will clear its ACL when the list of MAC addresses passed is empty. This
+ *	command is used in AP/P2P GO mode. Driver has to make sure to clear its
+ *	ACL list during %NL80211_CMD_STOP_AP.
+ *
+ * @NL80211_CMD_RADAR_DETECT: Start a Channel availability check (CAC). Once
+ *	a radar is detected or the channel availability scan (CAC) has finished
+ *	or was aborted, or a radar was detected, usermode will be notified with
+ *	this event. This command is also used to notify userspace about radars
+ *	while operating on this channel.
+ *	%NL80211_ATTR_RADAR_EVENT is used to inform about the type of the
+ *	event.
+ *
+ * @NL80211_CMD_GET_PROTOCOL_FEATURES: Get global nl80211 protocol features,
+ *	i.e. features for the nl80211 protocol rather than device features.
+ *	Returns the features in the %NL80211_ATTR_PROTOCOL_FEATURES bitmap.
+ *
+ * @NL80211_CMD_UPDATE_FT_IES: Pass down the most up-to-date Fast Transition
+ *	Information Element to the WLAN driver
+ *
+ * @NL80211_CMD_FT_EVENT: Send a Fast transition event from the WLAN driver
+ *	to the supplicant. This will carry the target AP's MAC address along
+ *	with the relevant Information Elements. This event is used to report
+ *	received FT IEs (MDIE, FTIE, RSN IE, TIE, RICIE).
+ *
+ * @NL80211_CMD_CRIT_PROTOCOL_START: Indicates user-space will start running
+ *	a critical protocol that needs more reliability in the connection to
+ *	complete.
+ *
+ * @NL80211_CMD_CRIT_PROTOCOL_STOP: Indicates the connection reliability can
+ *	return back to normal.
+ *
+ * @NL80211_CMD_GET_COALESCE: Get currently supported coalesce rules.
+ * @NL80211_CMD_SET_COALESCE: Configure coalesce rules or clear existing rules.
+ *
+ * @NL80211_CMD_CHANNEL_SWITCH: Perform a channel switch by announcing the
+ *	new channel information (Channel Switch Announcement - CSA)
+ *	in the beacon for some time (as defined in the
+ *	%NL80211_ATTR_CH_SWITCH_COUNT parameter) and then change to the
+ *	new channel. Userspace provides the new channel information (using
+ *	%NL80211_ATTR_WIPHY_FREQ and the attributes determining channel
+ *	width). %NL80211_ATTR_CH_SWITCH_BLOCK_TX may be supplied to inform
+ *	other station that transmission must be blocked until the channel
+ *	switch is complete.
+ *
+ * @NL80211_CMD_VENDOR: Vendor-specified command/event. The command is specified
+ *	by the %NL80211_ATTR_VENDOR_ID attribute and a sub-command in
+ *	%NL80211_ATTR_VENDOR_SUBCMD. Parameter(s) can be transported in
+ *	%NL80211_ATTR_VENDOR_DATA.
+ *	For feature advertisement, the %NL80211_ATTR_VENDOR_DATA attribute is
+ *	used in the wiphy data as a nested attribute containing descriptions
+ *	(&struct nl80211_vendor_cmd_info) of the supported vendor commands.
+ *	This may also be sent as an event with the same attributes.
+ *
+ * @NL80211_CMD_SET_QOS_MAP: Set Interworking QoS mapping for IP DSCP values.
+ *	The QoS mapping information is included in %NL80211_ATTR_QOS_MAP. If
+ *	that attribute is not included, QoS mapping is disabled. Since this
+ *	QoS mapping is relevant for IP packets, it is only valid during an
+ *	association. This is cleared on disassociation and AP restart.
+ *
+ * @NL80211_CMD_ADD_TX_TS: Ask the kernel to add a traffic stream for the given
+ *	%NL80211_ATTR_TSID and %NL80211_ATTR_MAC with %NL80211_ATTR_USER_PRIO
+ *	and %NL80211_ATTR_ADMITTED_TIME parameters.
+ *	Note that the action frame handshake with the AP shall be handled by
+ *	userspace via the normal management RX/TX framework, this only sets
+ *	up the TX TS in the driver/device.
+ *	If the admitted time attribute is not added then the request just checks
+ *	if a subsequent setup could be successful, the intent is to use this to
+ *	avoid setting up a session with the AP when local restrictions would
+ *	make that impossible. However, the subsequent "real" setup may still
+ *	fail even if the check was successful.
+ * @NL80211_CMD_DEL_TX_TS: Remove an existing TS with the %NL80211_ATTR_TSID
+ *	and %NL80211_ATTR_MAC parameters. It isn't necessary to call this
+ *	before removing a station entry entirely, or before disassociating
+ *	or similar, cleanup will happen in the driver/device in this case.
+ *
+ * @NL80211_CMD_GET_MPP: Get mesh path attributes for mesh proxy path to
+ *	destination %NL80211_ATTR_MAC on the interface identified by
+ *	%NL80211_ATTR_IFINDEX.
+ *
+ * @NL80211_CMD_JOIN_OCB: Join the OCB network. The center frequency and
+ *	bandwidth of a channel must be given.
+ * @NL80211_CMD_LEAVE_OCB: Leave the OCB network -- no special arguments, the
+ *	network is determined by the network interface.
+ *
+ * @NL80211_CMD_TDLS_CHANNEL_SWITCH: Start channel-switching with a TDLS peer,
+ *	identified by the %NL80211_ATTR_MAC parameter. A target channel is
+ *	provided via %NL80211_ATTR_WIPHY_FREQ and other attributes determining
+ *	channel width/type. The target operating class is given via
+ *	%NL80211_ATTR_OPER_CLASS.
+ *	The driver is responsible for continually initiating channel-switching
+ *	operations and returning to the base channel for communication with the
+ *	AP.
+ * @NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH: Stop channel-switching with a TDLS
+ *	peer given by %NL80211_ATTR_MAC. Both peers must be on the base channel
+ *	when this command completes.
+ *
+ * @NL80211_CMD_WIPHY_REG_CHANGE: Similar to %NL80211_CMD_REG_CHANGE, but used
+ *	as an event to indicate changes for devices with wiphy-specific regdom
+ *	management.
+ *
+ * @NL80211_CMD_ABORT_SCAN: Stop an ongoing scan. Returns -ENOENT if a scan is
+ *	not running. The driver indicates the status of the scan through
+ *	cfg80211_scan_done().
+ *
+ * @NL80211_CMD_START_NAN: Start NAN operation, identified by its
+ *	%NL80211_ATTR_WDEV interface. This interface must have been
+ *	previously created with %NL80211_CMD_NEW_INTERFACE. After it
+ *	has been started, the NAN interface will create or join a
+ *	cluster. This command must have a valid
+ *	%NL80211_ATTR_NAN_MASTER_PREF attribute and optional
+ *	%NL80211_ATTR_BANDS attributes.  If %NL80211_ATTR_BANDS is
+ *	omitted or set to 0, it means don't-care and the device will
+ *	decide what to use.  After this command NAN functions can be
+ *	added.
+ * @NL80211_CMD_STOP_NAN: Stop the NAN operation, identified by
+ *	its %NL80211_ATTR_WDEV interface.
+ * @NL80211_CMD_ADD_NAN_FUNCTION: Add a NAN function. The function is defined
+ *	with %NL80211_ATTR_NAN_FUNC nested attribute. When called, this
+ *	operation returns the strictly positive and unique instance id
+ *	(%NL80211_ATTR_NAN_FUNC_INST_ID) and a cookie (%NL80211_ATTR_COOKIE)
+ *	of the function upon success.
+ *	Since instance ID's can be re-used, this cookie is the right
+ *	way to identify the function. This will avoid races when a termination
+ *	event is handled by the user space after it has already added a new
+ *	function that got the same instance id from the kernel as the one
+ *	which just terminated.
+ *	This cookie may be used in NAN events even before the command
+ *	returns, so userspace shouldn't process NAN events until it processes
+ *	the response to this command.
+ *	Look at %NL80211_ATTR_SOCKET_OWNER as well.
+ * @NL80211_CMD_DEL_NAN_FUNCTION: Delete a NAN function by cookie.
+ *	This command is also used as a notification sent when a NAN function is
+ *	terminated. This will contain a %NL80211_ATTR_NAN_FUNC_INST_ID
+ *	and %NL80211_ATTR_COOKIE attributes.
+ * @NL80211_CMD_CHANGE_NAN_CONFIG: Change current NAN
+ *	configuration. NAN must be operational (%NL80211_CMD_START_NAN
+ *	was executed).  It must contain at least one of the following
+ *	attributes: %NL80211_ATTR_NAN_MASTER_PREF,
+ *	%NL80211_ATTR_BANDS.  If %NL80211_ATTR_BANDS is omitted, the
+ *	current configuration is not changed.  If it is present but
+ *	set to zero, the configuration is changed to don't-care
+ *	(i.e. the device can decide what to do).
+ * @NL80211_CMD_NAN_FUNC_MATCH: Notification sent when a match is reported.
+ *	This will contain a %NL80211_ATTR_NAN_MATCH nested attribute and
+ *	%NL80211_ATTR_COOKIE.
+ *
+ * @NL80211_CMD_UPDATE_CONNECT_PARAMS: Update one or more connect parameters
+ *	for subsequent roaming cases if the driver or firmware uses internal
+ *	BSS selection. This command can be issued only while connected and it
+ *	does not result in a change for the current association. Currently,
+ *	only the %NL80211_ATTR_IE data is used and updated with this command.
+ *
+ * @NL80211_CMD_SET_PMK: For offloaded 4-Way handshake, set the PMK or PMK-R0
+ *	for the given authenticator address (specified with %NL80211_ATTR_MAC).
+ *	When %NL80211_ATTR_PMKR0_NAME is set, %NL80211_ATTR_PMK specifies the
+ *	PMK-R0, otherwise it specifies the PMK.
+ * @NL80211_CMD_DEL_PMK: For offloaded 4-Way handshake, delete the previously
+ *	configured PMK for the authenticator address identified by
+ *	%NL80211_ATTR_MAC.
+ * @NL80211_CMD_PORT_AUTHORIZED: An event that indicates an 802.1X FT roam was
+ *	completed successfully. Drivers that support 4 way handshake offload
+ *	should send this event after indicating 802.1X FT assocation with
+ *	%NL80211_CMD_ROAM. If the 4 way handshake failed %NL80211_CMD_DISCONNECT
+ *	should be indicated instead.
+ * @NL80211_CMD_CONTROL_PORT_FRAME: Control Port (e.g. PAE) frame TX request
+ *	and RX notification.  This command is used both as a request to transmit
+ *	a control port frame and as a notification that a control port frame
+ *	has been received. %NL80211_ATTR_FRAME is used to specify the
+ *	frame contents.  The frame is the raw EAPoL data, without ethernet or
+ *	802.11 headers.
+ *	For an MLD transmitter, the %NL80211_ATTR_MLO_LINK_ID may be given and
+ *	its effect will depend on the destination: If the destination is known
+ *	to be an MLD, this will be used as a hint to select the link to transmit
+ *	the frame on. If the destination is not an MLD, this will select both
+ *	the link to transmit on and the source address will be set to the link
+ *	address of that link.
+ *	When used as an event indication %NL80211_ATTR_CONTROL_PORT_ETHERTYPE,
+ *	%NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT and %NL80211_ATTR_MAC are added
+ *	indicating the protocol type of the received frame; whether the frame
+ *	was received unencrypted and the MAC address of the peer respectively.
+ *
+ * @NL80211_CMD_RELOAD_REGDB: Request that the regdb firmware file is reloaded.
+ *
+ * @NL80211_CMD_EXTERNAL_AUTH: This interface is exclusively defined for host
+ *	drivers that do not define separate commands for authentication and
+ *	association, but rely on user space for the authentication to happen.
+ *	This interface acts both as the event request (driver to user space)
+ *	to trigger the authentication and command response (userspace to
+ *	driver) to indicate the authentication status.
+ *
+ *	User space uses the %NL80211_CMD_CONNECT command to the host driver to
+ *	trigger a connection. The host driver selects a BSS and further uses
+ *	this interface to offload only the authentication part to the user
+ *	space. Authentication frames are passed between the driver and user
+ *	space through the %NL80211_CMD_FRAME interface. Host driver proceeds
+ *	further with the association after getting successful authentication
+ *	status. User space indicates the authentication status through
+ *	%NL80211_ATTR_STATUS_CODE attribute in %NL80211_CMD_EXTERNAL_AUTH
+ *	command interface.
+ *
+ *	Host driver sends MLD address of the AP with %NL80211_ATTR_MLD_ADDR in
+ *	%NL80211_CMD_EXTERNAL_AUTH event to indicate user space to enable MLO
+ *	during the authentication offload in STA mode while connecting to MLD
+ *	APs. Host driver should check %NL80211_ATTR_MLO_SUPPORT flag capability
+ *	in %NL80211_CMD_CONNECT to know whether the user space supports enabling
+ *	MLO during the authentication offload or not.
+ *	User space should enable MLO during the authentication only when it
+ *	receives the AP MLD address in authentication offload request. User
+ *	space shouldn't enable MLO when the authentication offload request
+ *	doesn't indicate the AP MLD address even if the AP is MLO capable.
+ *	User space should use %NL80211_ATTR_MLD_ADDR as peer's MLD address and
+ *	interface address identified by %NL80211_ATTR_IFINDEX as self MLD
+ *	address. User space and host driver to use MLD addresses in RA, TA and
+ *	BSSID fields of the frames between them, and host driver translates the
+ *	MLD addresses to/from link addresses based on the link chosen for the
+ *	authentication.
+ *
+ *	Host driver reports this status on an authentication failure to the
+ *	user space through the connect result as the user space would have
+ *	initiated the connection through the connect request.
+ *
+ * @NL80211_CMD_STA_OPMODE_CHANGED: An event that notify station's
+ *	ht opmode or vht opmode changes using any of %NL80211_ATTR_SMPS_MODE,
+ *	%NL80211_ATTR_CHANNEL_WIDTH,%NL80211_ATTR_NSS attributes with its
+ *	address(specified in %NL80211_ATTR_MAC).
+ *
+ * @NL80211_CMD_GET_FTM_RESPONDER_STATS: Retrieve FTM responder statistics, in
+ *	the %NL80211_ATTR_FTM_RESPONDER_STATS attribute.
+ *
+ * @NL80211_CMD_PEER_MEASUREMENT_START: start a (set of) peer measurement(s)
+ *	with the given parameters, which are encapsulated in the nested
+ *	%NL80211_ATTR_PEER_MEASUREMENTS attribute. Optionally, MAC address
+ *	randomization may be enabled and configured by specifying the
+ *	%NL80211_ATTR_MAC and %NL80211_ATTR_MAC_MASK attributes.
+ *	If a timeout is requested, use the %NL80211_ATTR_TIMEOUT attribute.
+ *	A u64 cookie for further %NL80211_ATTR_COOKIE use is returned in
+ *	the netlink extended ack message.
+ *
+ *	To cancel a measurement, close the socket that requested it.
+ *
+ *	Measurement results are reported to the socket that requested the
+ *	measurement using @NL80211_CMD_PEER_MEASUREMENT_RESULT when they
+ *	become available, so applications must ensure a large enough socket
+ *	buffer size.
+ *
+ *	Depending on driver support it may or may not be possible to start
+ *	multiple concurrent measurements.
+ * @NL80211_CMD_PEER_MEASUREMENT_RESULT: This command number is used for the
+ *	result notification from the driver to the requesting socket.
+ * @NL80211_CMD_PEER_MEASUREMENT_COMPLETE: Notification only, indicating that
+ *	the measurement completed, using the measurement cookie
+ *	(%NL80211_ATTR_COOKIE).
+ *
+ * @NL80211_CMD_NOTIFY_RADAR: Notify the kernel that a radar signal was
+ *	detected and reported by a neighboring device on the channel
+ *	indicated by %NL80211_ATTR_WIPHY_FREQ and other attributes
+ *	determining the width and type.
+ *
+ * @NL80211_CMD_UPDATE_OWE_INFO: This interface allows the host driver to
+ *	offload OWE processing to user space. This intends to support
+ *	OWE AKM by the host drivers that implement SME but rely
+ *	on the user space for the cryptographic/DH IE processing in AP mode.
+ *
+ * @NL80211_CMD_PROBE_MESH_LINK: The requirement for mesh link metric
+ *	refreshing, is that from one mesh point we be able to send some data
+ *	frames to other mesh points which are not currently selected as a
+ *	primary traffic path, but which are only 1 hop away. The absence of
+ *	the primary path to the chosen node makes it necessary to apply some
+ *	form of marking on a chosen packet stream so that the packets can be
+ *	properly steered to the selected node for testing, and not by the
+ *	regular mesh path lookup. Further, the packets must be of type data
+ *	so that the rate control (often embedded in firmware) is used for
+ *	rate selection.
+ *
+ *	Here attribute %NL80211_ATTR_MAC is used to specify connected mesh
+ *	peer MAC address and %NL80211_ATTR_FRAME is used to specify the frame
+ *	content. The frame is ethernet data.
+ *
+ * @NL80211_CMD_SET_TID_CONFIG: Data frame TID specific configuration
+ *	is passed using %NL80211_ATTR_TID_CONFIG attribute.
+ *
+ * @NL80211_CMD_UNPROT_BEACON: Unprotected or incorrectly protected Beacon
+ *	frame. This event is used to indicate that a received Beacon frame was
+ *	dropped because it did not include a valid MME MIC while beacon
+ *	protection was enabled (BIGTK configured in station mode).
+ *
+ * @NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS: Report TX status of a control
+ *	port frame transmitted with %NL80211_CMD_CONTROL_PORT_FRAME.
+ *	%NL80211_ATTR_COOKIE identifies the TX command and %NL80211_ATTR_FRAME
+ *	includes the contents of the frame. %NL80211_ATTR_ACK flag is included
+ *	if the recipient acknowledged the frame.
+ *
+ * @NL80211_CMD_SET_SAR_SPECS: SAR power limitation configuration is
+ *	passed using %NL80211_ATTR_SAR_SPEC. %NL80211_ATTR_WIPHY is used to
+ *	specify the wiphy index to be applied to.
+ *
+ * @NL80211_CMD_OBSS_COLOR_COLLISION: This notification is sent out whenever
+ *	mac80211/drv detects a bss color collision.
+ *
+ * @NL80211_CMD_COLOR_CHANGE_REQUEST: This command is used to indicate that
+ *	userspace wants to change the BSS color.
+ *
+ * @NL80211_CMD_COLOR_CHANGE_STARTED: Notify userland, that a color change has
+ *	started
+ *
+ * @NL80211_CMD_COLOR_CHANGE_ABORTED: Notify userland, that the color change has
+ *	been aborted
+ *
+ * @NL80211_CMD_COLOR_CHANGE_COMPLETED: Notify userland that the color change
+ *	has completed
+ *
+ * @NL80211_CMD_SET_FILS_AAD: Set FILS AAD data to the driver using -
+ *	&NL80211_ATTR_MAC - for STA MAC address
+ *	&NL80211_ATTR_FILS_KEK - for KEK
+ *	&NL80211_ATTR_FILS_NONCES - for FILS Nonces
+ *		(STA Nonce 16 bytes followed by AP Nonce 16 bytes)
+ *
+ * @NL80211_CMD_ASSOC_COMEBACK: notification about an association
+ *      temporal rejection with comeback. The event includes %NL80211_ATTR_MAC
+ *      to describe the BSSID address of the AP and %NL80211_ATTR_TIMEOUT to
+ *      specify the timeout value.
+ *
+ * @NL80211_CMD_ADD_LINK: Add a new link to an interface. The
+ *	%NL80211_ATTR_MLO_LINK_ID attribute is used for the new link.
+ * @NL80211_CMD_REMOVE_LINK: Remove a link from an interface. This may come
+ *	without %NL80211_ATTR_MLO_LINK_ID as an easy way to remove all links
+ *	in preparation for e.g. roaming to a regular (non-MLO) AP.
+ *
+ * @NL80211_CMD_ADD_LINK_STA: Add a link to an MLD station
+ * @NL80211_CMD_MODIFY_LINK_STA: Modify a link of an MLD station
+ * @NL80211_CMD_REMOVE_LINK_STA: Remove a link of an MLD station
+ *
+ * @NL80211_CMD_SET_HW_TIMESTAMP: Enable/disable HW timestamping of Timing
+ *	measurement and Fine timing measurement frames. If %NL80211_ATTR_MAC
+ *	is included, enable/disable HW timestamping only for frames to/from the
+ *	specified MAC address. Otherwise enable/disable HW timestamping for
+ *	all TM/FTM frames (including ones that were enabled with specific MAC
+ *	address). If %NL80211_ATTR_HW_TIMESTAMP_ENABLED is not included, disable
+ *	HW timestamping.
+ *	The number of peers that HW timestamping can be enabled for concurrently
+ *	is indicated by %NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS.
+ *
+ * @NL80211_CMD_MAX: highest used command number
+ * @__NL80211_CMD_AFTER_LAST: internal use
+ */
+enum nl80211_commands {
+/* don't change the order or add anything between, this is ABI! */
+	NL80211_CMD_UNSPEC,
+
+	NL80211_CMD_GET_WIPHY,		/* can dump */
+	NL80211_CMD_SET_WIPHY,
+	NL80211_CMD_NEW_WIPHY,
+	NL80211_CMD_DEL_WIPHY,
+
+	NL80211_CMD_GET_INTERFACE,	/* can dump */
+	NL80211_CMD_SET_INTERFACE,
+	NL80211_CMD_NEW_INTERFACE,
+	NL80211_CMD_DEL_INTERFACE,
+
+	NL80211_CMD_GET_KEY,
+	NL80211_CMD_SET_KEY,
+	NL80211_CMD_NEW_KEY,
+	NL80211_CMD_DEL_KEY,
+
+	NL80211_CMD_GET_BEACON,
+	NL80211_CMD_SET_BEACON,
+	NL80211_CMD_START_AP,
+	NL80211_CMD_NEW_BEACON = NL80211_CMD_START_AP,
+	NL80211_CMD_STOP_AP,
+	NL80211_CMD_DEL_BEACON = NL80211_CMD_STOP_AP,
+
+	NL80211_CMD_GET_STATION,
+	NL80211_CMD_SET_STATION,
+	NL80211_CMD_NEW_STATION,
+	NL80211_CMD_DEL_STATION,
+
+	NL80211_CMD_GET_MPATH,
+	NL80211_CMD_SET_MPATH,
+	NL80211_CMD_NEW_MPATH,
+	NL80211_CMD_DEL_MPATH,
+
+	NL80211_CMD_SET_BSS,
+
+	NL80211_CMD_SET_REG,
+	NL80211_CMD_REQ_SET_REG,
+
+	NL80211_CMD_GET_MESH_CONFIG,
+	NL80211_CMD_SET_MESH_CONFIG,
+
+	NL80211_CMD_SET_MGMT_EXTRA_IE /* reserved; not used */,
+
+	NL80211_CMD_GET_REG,
+
+	NL80211_CMD_GET_SCAN,
+	NL80211_CMD_TRIGGER_SCAN,
+	NL80211_CMD_NEW_SCAN_RESULTS,
+	NL80211_CMD_SCAN_ABORTED,
+
+	NL80211_CMD_REG_CHANGE,
+
+	NL80211_CMD_AUTHENTICATE,
+	NL80211_CMD_ASSOCIATE,
+	NL80211_CMD_DEAUTHENTICATE,
+	NL80211_CMD_DISASSOCIATE,
+
+	NL80211_CMD_MICHAEL_MIC_FAILURE,
+
+	NL80211_CMD_REG_BEACON_HINT,
+
+	NL80211_CMD_JOIN_IBSS,
+	NL80211_CMD_LEAVE_IBSS,
+
+	NL80211_CMD_TESTMODE,
+
+	NL80211_CMD_CONNECT,
+	NL80211_CMD_ROAM,
+	NL80211_CMD_DISCONNECT,
+
+	NL80211_CMD_SET_WIPHY_NETNS,
+
+	NL80211_CMD_GET_SURVEY,
+	NL80211_CMD_NEW_SURVEY_RESULTS,
+
+	NL80211_CMD_SET_PMKSA,
+	NL80211_CMD_DEL_PMKSA,
+	NL80211_CMD_FLUSH_PMKSA,
+
+	NL80211_CMD_REMAIN_ON_CHANNEL,
+	NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
+
+	NL80211_CMD_SET_TX_BITRATE_MASK,
+
+	NL80211_CMD_REGISTER_FRAME,
+	NL80211_CMD_REGISTER_ACTION = NL80211_CMD_REGISTER_FRAME,
+	NL80211_CMD_FRAME,
+	NL80211_CMD_ACTION = NL80211_CMD_FRAME,
+	NL80211_CMD_FRAME_TX_STATUS,
+	NL80211_CMD_ACTION_TX_STATUS = NL80211_CMD_FRAME_TX_STATUS,
+
+	NL80211_CMD_SET_POWER_SAVE,
+	NL80211_CMD_GET_POWER_SAVE,
+
+	NL80211_CMD_SET_CQM,
+	NL80211_CMD_NOTIFY_CQM,
+
+	NL80211_CMD_SET_CHANNEL,
+	NL80211_CMD_SET_WDS_PEER,
+
+	NL80211_CMD_FRAME_WAIT_CANCEL,
+
+	NL80211_CMD_JOIN_MESH,
+	NL80211_CMD_LEAVE_MESH,
+
+	NL80211_CMD_UNPROT_DEAUTHENTICATE,
+	NL80211_CMD_UNPROT_DISASSOCIATE,
+
+	NL80211_CMD_NEW_PEER_CANDIDATE,
+
+	NL80211_CMD_GET_WOWLAN,
+	NL80211_CMD_SET_WOWLAN,
+
+	NL80211_CMD_START_SCHED_SCAN,
+	NL80211_CMD_STOP_SCHED_SCAN,
+	NL80211_CMD_SCHED_SCAN_RESULTS,
+	NL80211_CMD_SCHED_SCAN_STOPPED,
+
+	NL80211_CMD_SET_REKEY_OFFLOAD,
+
+	NL80211_CMD_PMKSA_CANDIDATE,
+
+	NL80211_CMD_TDLS_OPER,
+	NL80211_CMD_TDLS_MGMT,
+
+	NL80211_CMD_UNEXPECTED_FRAME,
+
+	NL80211_CMD_PROBE_CLIENT,
+
+	NL80211_CMD_REGISTER_BEACONS,
+
+	NL80211_CMD_UNEXPECTED_4ADDR_FRAME,
+
+	NL80211_CMD_SET_NOACK_MAP,
+
+	NL80211_CMD_CH_SWITCH_NOTIFY,
+
+	NL80211_CMD_START_P2P_DEVICE,
+	NL80211_CMD_STOP_P2P_DEVICE,
+
+	NL80211_CMD_CONN_FAILED,
+
+	NL80211_CMD_SET_MCAST_RATE,
+
+	NL80211_CMD_SET_MAC_ACL,
+
+	NL80211_CMD_RADAR_DETECT,
+
+	NL80211_CMD_GET_PROTOCOL_FEATURES,
+
+	NL80211_CMD_UPDATE_FT_IES,
+	NL80211_CMD_FT_EVENT,
+
+	NL80211_CMD_CRIT_PROTOCOL_START,
+	NL80211_CMD_CRIT_PROTOCOL_STOP,
+
+	NL80211_CMD_GET_COALESCE,
+	NL80211_CMD_SET_COALESCE,
+
+	NL80211_CMD_CHANNEL_SWITCH,
+
+	NL80211_CMD_VENDOR,
+
+	NL80211_CMD_SET_QOS_MAP,
+
+	NL80211_CMD_ADD_TX_TS,
+	NL80211_CMD_DEL_TX_TS,
+
+	NL80211_CMD_GET_MPP,
+
+	NL80211_CMD_JOIN_OCB,
+	NL80211_CMD_LEAVE_OCB,
+
+	NL80211_CMD_CH_SWITCH_STARTED_NOTIFY,
+
+	NL80211_CMD_TDLS_CHANNEL_SWITCH,
+	NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH,
+
+	NL80211_CMD_WIPHY_REG_CHANGE,
+
+	NL80211_CMD_ABORT_SCAN,
+
+	NL80211_CMD_START_NAN,
+	NL80211_CMD_STOP_NAN,
+	NL80211_CMD_ADD_NAN_FUNCTION,
+	NL80211_CMD_DEL_NAN_FUNCTION,
+	NL80211_CMD_CHANGE_NAN_CONFIG,
+	NL80211_CMD_NAN_MATCH,
+
+	NL80211_CMD_SET_MULTICAST_TO_UNICAST,
+
+	NL80211_CMD_UPDATE_CONNECT_PARAMS,
+
+	NL80211_CMD_SET_PMK,
+	NL80211_CMD_DEL_PMK,
+
+	NL80211_CMD_PORT_AUTHORIZED,
+
+	NL80211_CMD_RELOAD_REGDB,
+
+	NL80211_CMD_EXTERNAL_AUTH,
+
+	NL80211_CMD_STA_OPMODE_CHANGED,
+
+	NL80211_CMD_CONTROL_PORT_FRAME,
+
+	NL80211_CMD_GET_FTM_RESPONDER_STATS,
+
+	NL80211_CMD_PEER_MEASUREMENT_START,
+	NL80211_CMD_PEER_MEASUREMENT_RESULT,
+	NL80211_CMD_PEER_MEASUREMENT_COMPLETE,
+
+	NL80211_CMD_NOTIFY_RADAR,
+
+	NL80211_CMD_UPDATE_OWE_INFO,
+
+	NL80211_CMD_PROBE_MESH_LINK,
+
+	NL80211_CMD_SET_TID_CONFIG,
+
+	NL80211_CMD_UNPROT_BEACON,
+
+	NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS,
+
+	NL80211_CMD_SET_SAR_SPECS,
+
+	NL80211_CMD_OBSS_COLOR_COLLISION,
+
+	NL80211_CMD_COLOR_CHANGE_REQUEST,
+
+	NL80211_CMD_COLOR_CHANGE_STARTED,
+	NL80211_CMD_COLOR_CHANGE_ABORTED,
+	NL80211_CMD_COLOR_CHANGE_COMPLETED,
+
+	NL80211_CMD_SET_FILS_AAD,
+
+	NL80211_CMD_ASSOC_COMEBACK,
+
+	NL80211_CMD_ADD_LINK,
+	NL80211_CMD_REMOVE_LINK,
+
+	NL80211_CMD_ADD_LINK_STA,
+	NL80211_CMD_MODIFY_LINK_STA,
+	NL80211_CMD_REMOVE_LINK_STA,
+
+	NL80211_CMD_SET_HW_TIMESTAMP,
+
+	/* add new commands above here */
+
+	/* used to define NL80211_CMD_MAX below */
+	__NL80211_CMD_AFTER_LAST,
+	NL80211_CMD_MAX = __NL80211_CMD_AFTER_LAST - 1
+};
+
+/*
+ * Allow user space programs to use #ifdef on new commands by defining them
+ * here
+ */
+#define NL80211_CMD_SET_BSS NL80211_CMD_SET_BSS
+#define NL80211_CMD_SET_MGMT_EXTRA_IE NL80211_CMD_SET_MGMT_EXTRA_IE
+#define NL80211_CMD_REG_CHANGE NL80211_CMD_REG_CHANGE
+#define NL80211_CMD_AUTHENTICATE NL80211_CMD_AUTHENTICATE
+#define NL80211_CMD_ASSOCIATE NL80211_CMD_ASSOCIATE
+#define NL80211_CMD_DEAUTHENTICATE NL80211_CMD_DEAUTHENTICATE
+#define NL80211_CMD_DISASSOCIATE NL80211_CMD_DISASSOCIATE
+#define NL80211_CMD_REG_BEACON_HINT NL80211_CMD_REG_BEACON_HINT
+
+#define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS
+
+/* source-level API compatibility */
+#define NL80211_CMD_GET_MESH_PARAMS NL80211_CMD_GET_MESH_CONFIG
+#define NL80211_CMD_SET_MESH_PARAMS NL80211_CMD_SET_MESH_CONFIG
+#define NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE NL80211_MESH_SETUP_IE
+
+/**
+ * enum nl80211_attrs - nl80211 netlink attributes
+ *
+ * @NL80211_ATTR_UNSPEC: unspecified attribute to catch errors
+ *
+ * @NL80211_ATTR_WIPHY: index of wiphy to operate on, cf.
+ *	/sys/class/ieee80211/<phyname>/index
+ * @NL80211_ATTR_WIPHY_NAME: wiphy name (used for renaming)
+ * @NL80211_ATTR_WIPHY_TXQ_PARAMS: a nested array of TX queue parameters
+ * @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz,
+ *	defines the channel together with the (deprecated)
+ *	%NL80211_ATTR_WIPHY_CHANNEL_TYPE attribute or the attributes
+ *	%NL80211_ATTR_CHANNEL_WIDTH and if needed %NL80211_ATTR_CENTER_FREQ1
+ *	and %NL80211_ATTR_CENTER_FREQ2
+ * @NL80211_ATTR_CHANNEL_WIDTH: u32 attribute containing one of the values
+ *	of &enum nl80211_chan_width, describing the channel width. See the
+ *	documentation of the enum for more information.
+ * @NL80211_ATTR_CENTER_FREQ1: Center frequency of the first part of the
+ *	channel, used for anything but 20 MHz bandwidth. In S1G this is the
+ *	operating channel center frequency.
+ * @NL80211_ATTR_CENTER_FREQ2: Center frequency of the second part of the
+ *	channel, used only for 80+80 MHz bandwidth
+ * @NL80211_ATTR_WIPHY_CHANNEL_TYPE: included with NL80211_ATTR_WIPHY_FREQ
+ *	if HT20 or HT40 are to be used (i.e., HT disabled if not included):
+ *	NL80211_CHAN_NO_HT = HT not allowed (i.e., same as not including
+ *		this attribute)
+ *	NL80211_CHAN_HT20 = HT20 only
+ *	NL80211_CHAN_HT40MINUS = secondary channel is below the primary channel
+ *	NL80211_CHAN_HT40PLUS = secondary channel is above the primary channel
+ *	This attribute is now deprecated.
+ * @NL80211_ATTR_WIPHY_RETRY_SHORT: TX retry limit for frames whose length is
+ *	less than or equal to the RTS threshold; allowed range: 1..255;
+ *	dot11ShortRetryLimit; u8
+ * @NL80211_ATTR_WIPHY_RETRY_LONG: TX retry limit for frames whose length is
+ *	greater than the RTS threshold; allowed range: 1..255;
+ *	dot11ShortLongLimit; u8
+ * @NL80211_ATTR_WIPHY_FRAG_THRESHOLD: fragmentation threshold, i.e., maximum
+ *	length in octets for frames; allowed range: 256..8000, disable
+ *	fragmentation with (u32)-1; dot11FragmentationThreshold; u32
+ * @NL80211_ATTR_WIPHY_RTS_THRESHOLD: RTS threshold (TX frames with length
+ *	larger than or equal to this use RTS/CTS handshake); allowed range:
+ *	0..65536, disable with (u32)-1; dot11RTSThreshold; u32
+ * @NL80211_ATTR_WIPHY_COVERAGE_CLASS: Coverage Class as defined by IEEE 802.11
+ *	section 7.3.2.9; dot11CoverageClass; u8
+ *
+ * @NL80211_ATTR_IFINDEX: network interface index of the device to operate on
+ * @NL80211_ATTR_IFNAME: network interface name
+ * @NL80211_ATTR_IFTYPE: type of virtual interface, see &enum nl80211_iftype
+ *
+ * @NL80211_ATTR_WDEV: wireless device identifier, used for pseudo-devices
+ *	that don't have a netdev (u64)
+ *
+ * @NL80211_ATTR_MAC: MAC address (various uses)
+ *
+ * @NL80211_ATTR_KEY_DATA: (temporal) key data; for TKIP this consists of
+ *	16 bytes encryption key followed by 8 bytes each for TX and RX MIC
+ *	keys
+ * @NL80211_ATTR_KEY_IDX: key ID (u8, 0-3)
+ * @NL80211_ATTR_KEY_CIPHER: key cipher suite (u32, as defined by IEEE 802.11
+ *	section 7.3.2.25.1, e.g. 0x000FAC04)
+ * @NL80211_ATTR_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and
+ *	CCMP keys, each six bytes in little endian
+ * @NL80211_ATTR_KEY_DEFAULT: Flag attribute indicating the key is default key
+ * @NL80211_ATTR_KEY_DEFAULT_MGMT: Flag attribute indicating the key is the
+ *	default management key
+ * @NL80211_ATTR_CIPHER_SUITES_PAIRWISE: For crypto settings for connect or
+ *	other commands, indicates which pairwise cipher suites are used
+ * @NL80211_ATTR_CIPHER_SUITE_GROUP: For crypto settings for connect or
+ *	other commands, indicates which group cipher suite is used
+ *
+ * @NL80211_ATTR_BEACON_INTERVAL: beacon interval in TU
+ * @NL80211_ATTR_DTIM_PERIOD: DTIM period for beaconing
+ * @NL80211_ATTR_BEACON_HEAD: portion of the beacon before the TIM IE
+ * @NL80211_ATTR_BEACON_TAIL: portion of the beacon after the TIM IE
+ *
+ * @NL80211_ATTR_STA_AID: Association ID for the station (u16)
+ * @NL80211_ATTR_STA_FLAGS: flags, nested element with NLA_FLAG attributes of
+ *	&enum nl80211_sta_flags (deprecated, use %NL80211_ATTR_STA_FLAGS2)
+ * @NL80211_ATTR_STA_LISTEN_INTERVAL: listen interval as defined by
+ *	IEEE 802.11 7.3.1.6 (u16).
+ * @NL80211_ATTR_STA_SUPPORTED_RATES: supported rates, array of supported
+ *	rates as defined by IEEE 802.11 7.3.2.2 but without the length
+ *	restriction (at most %NL80211_MAX_SUPP_RATES).
+ * @NL80211_ATTR_STA_VLAN: interface index of VLAN interface to move station
+ *	to, or the AP interface the station was originally added to.
+ * @NL80211_ATTR_STA_INFO: information about a station, part of station info
+ *	given for %NL80211_CMD_GET_STATION, nested attribute containing
+ *	info as possible, see &enum nl80211_sta_info.
+ *
+ * @NL80211_ATTR_WIPHY_BANDS: Information about an operating bands,
+ *	consisting of a nested array.
+ *
+ * @NL80211_ATTR_MESH_ID: mesh id (1-32 bytes).
+ * @NL80211_ATTR_STA_PLINK_ACTION: action to perform on the mesh peer link
+ *	(see &enum nl80211_plink_action).
+ * @NL80211_ATTR_MPATH_NEXT_HOP: MAC address of the next hop for a mesh path.
+ * @NL80211_ATTR_MPATH_INFO: information about a mesh_path, part of mesh path
+ * 	info given for %NL80211_CMD_GET_MPATH, nested attribute described at
+ *	&enum nl80211_mpath_info.
+ *
+ * @NL80211_ATTR_MNTR_FLAGS: flags, nested element with NLA_FLAG attributes of
+ *      &enum nl80211_mntr_flags.
+ *
+ * @NL80211_ATTR_REG_ALPHA2: an ISO-3166-alpha2 country code for which the
+ * 	current regulatory domain should be set to or is already set to.
+ * 	For example, 'CR', for Costa Rica. This attribute is used by the kernel
+ * 	to query the CRDA to retrieve one regulatory domain. This attribute can
+ * 	also be used by userspace to query the kernel for the currently set
+ * 	regulatory domain. We chose an alpha2 as that is also used by the
+ * 	IEEE-802.11 country information element to identify a country.
+ * 	Users can also simply ask the wireless core to set regulatory domain
+ * 	to a specific alpha2.
+ * @NL80211_ATTR_REG_RULES: a nested array of regulatory domain regulatory
+ *	rules.
+ *
+ * @NL80211_ATTR_BSS_CTS_PROT: whether CTS protection is enabled (u8, 0 or 1)
+ * @NL80211_ATTR_BSS_SHORT_PREAMBLE: whether short preamble is enabled
+ *	(u8, 0 or 1)
+ * @NL80211_ATTR_BSS_SHORT_SLOT_TIME: whether short slot time enabled
+ *	(u8, 0 or 1)
+ * @NL80211_ATTR_BSS_BASIC_RATES: basic rates, array of basic
+ *	rates in format defined by IEEE 802.11 7.3.2.2 but without the length
+ *	restriction (at most %NL80211_MAX_SUPP_RATES).
+ *
+ * @NL80211_ATTR_HT_CAPABILITY: HT Capability information element (from
+ *	association request when used with NL80211_CMD_NEW_STATION)
+ *
+ * @NL80211_ATTR_SUPPORTED_IFTYPES: nested attribute containing all
+ *	supported interface types, each a flag attribute with the number
+ *	of the interface mode.
+ *
+ * @NL80211_ATTR_MGMT_SUBTYPE: Management frame subtype for
+ *	%NL80211_CMD_SET_MGMT_EXTRA_IE.
+ *
+ * @NL80211_ATTR_IE: Information element(s) data (used, e.g., with
+ *	%NL80211_CMD_SET_MGMT_EXTRA_IE).
+ *
+ * @NL80211_ATTR_MAX_NUM_SCAN_SSIDS: number of SSIDs you can scan with
+ *	a single scan request, a wiphy attribute.
+ * @NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS: number of SSIDs you can
+ *	scan with a single scheduled scan request, a wiphy attribute.
+ * @NL80211_ATTR_MAX_SCAN_IE_LEN: maximum length of information elements
+ *	that can be added to a scan request
+ * @NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN: maximum length of information
+ *	elements that can be added to a scheduled scan request
+ * @NL80211_ATTR_MAX_MATCH_SETS: maximum number of sets that can be
+ *	used with @NL80211_ATTR_SCHED_SCAN_MATCH, a wiphy attribute.
+ *
+ * @NL80211_ATTR_SCAN_FREQUENCIES: nested attribute with frequencies (in MHz)
+ * @NL80211_ATTR_SCAN_SSIDS: nested attribute with SSIDs, leave out for passive
+ *	scanning and include a zero-length SSID (wildcard) for wildcard scan
+ * @NL80211_ATTR_BSS: scan result BSS
+ *
+ * @NL80211_ATTR_REG_INITIATOR: indicates who requested the regulatory domain
+ * 	currently in effect. This could be any of the %NL80211_REGDOM_SET_BY_*
+ * @NL80211_ATTR_REG_TYPE: indicates the type of the regulatory domain currently
+ * 	set. This can be one of the nl80211_reg_type (%NL80211_REGDOM_TYPE_*)
+ *
+ * @NL80211_ATTR_SUPPORTED_COMMANDS: wiphy attribute that specifies
+ *	an array of command numbers (i.e. a mapping index to command number)
+ *	that the driver for the given wiphy supports.
+ *
+ * @NL80211_ATTR_FRAME: frame data (binary attribute), including frame header
+ *	and body, but not FCS; used, e.g., with NL80211_CMD_AUTHENTICATE and
+ *	NL80211_CMD_ASSOCIATE events
+ * @NL80211_ATTR_SSID: SSID (binary attribute, 0..32 octets)
+ * @NL80211_ATTR_AUTH_TYPE: AuthenticationType, see &enum nl80211_auth_type,
+ *	represented as a u32
+ * @NL80211_ATTR_REASON_CODE: ReasonCode for %NL80211_CMD_DEAUTHENTICATE and
+ *	%NL80211_CMD_DISASSOCIATE, u16
+ *
+ * @NL80211_ATTR_KEY_TYPE: Key Type, see &enum nl80211_key_type, represented as
+ *	a u32
+ *
+ * @NL80211_ATTR_FREQ_BEFORE: A channel which has suffered a regulatory change
+ * 	due to considerations from a beacon hint. This attribute reflects
+ * 	the state of the channel _before_ the beacon hint processing. This
+ * 	attributes consists of a nested attribute containing
+ * 	NL80211_FREQUENCY_ATTR_*
+ * @NL80211_ATTR_FREQ_AFTER: A channel which has suffered a regulatory change
+ * 	due to considerations from a beacon hint. This attribute reflects
+ * 	the state of the channel _after_ the beacon hint processing. This
+ * 	attributes consists of a nested attribute containing
+ * 	NL80211_FREQUENCY_ATTR_*
+ *
+ * @NL80211_ATTR_CIPHER_SUITES: a set of u32 values indicating the supported
+ *	cipher suites
+ *
+ * @NL80211_ATTR_FREQ_FIXED: a flag indicating the IBSS should not try to look
+ *	for other networks on different channels
+ *
+ * @NL80211_ATTR_TIMED_OUT: a flag indicating than an operation timed out; this
+ *	is used, e.g., with %NL80211_CMD_AUTHENTICATE event
+ *
+ * @NL80211_ATTR_USE_MFP: Whether management frame protection (IEEE 802.11w) is
+ *	used for the association (&enum nl80211_mfp, represented as a u32);
+ *	this attribute can be used with %NL80211_CMD_ASSOCIATE and
+ *	%NL80211_CMD_CONNECT requests. %NL80211_MFP_OPTIONAL is not allowed for
+ *	%NL80211_CMD_ASSOCIATE since user space SME is expected and hence, it
+ *	must have decided whether to use management frame protection or not.
+ *	Setting %NL80211_MFP_OPTIONAL with a %NL80211_CMD_CONNECT request will
+ *	let the driver (or the firmware) decide whether to use MFP or not.
+ *
+ * @NL80211_ATTR_STA_FLAGS2: Attribute containing a
+ *	&struct nl80211_sta_flag_update.
+ *
+ * @NL80211_ATTR_CONTROL_PORT: A flag indicating whether user space controls
+ *	IEEE 802.1X port, i.e., sets/clears %NL80211_STA_FLAG_AUTHORIZED, in
+ *	station mode. If the flag is included in %NL80211_CMD_ASSOCIATE
+ *	request, the driver will assume that the port is unauthorized until
+ *	authorized by user space. Otherwise, port is marked authorized by
+ *	default in station mode.
+ * @NL80211_ATTR_CONTROL_PORT_ETHERTYPE: A 16-bit value indicating the
+ *	ethertype that will be used for key negotiation. It can be
+ *	specified with the associate and connect commands. If it is not
+ *	specified, the value defaults to 0x888E (PAE, 802.1X). This
+ *	attribute is also used as a flag in the wiphy information to
+ *	indicate that protocols other than PAE are supported.
+ * @NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT: When included along with
+ *	%NL80211_ATTR_CONTROL_PORT_ETHERTYPE, indicates that the custom
+ *	ethertype frames used for key negotiation must not be encrypted.
+ * @NL80211_ATTR_CONTROL_PORT_OVER_NL80211: A flag indicating whether control
+ *	port frames (e.g. of type given in %NL80211_ATTR_CONTROL_PORT_ETHERTYPE)
+ *	will be sent directly to the network interface or sent via the NL80211
+ *	socket.  If this attribute is missing, then legacy behavior of sending
+ *	control port frames directly to the network interface is used.  If the
+ *	flag is included, then control port frames are sent over NL80211 instead
+ *	using %CMD_CONTROL_PORT_FRAME.  If control port routing over NL80211 is
+ *	to be used then userspace must also use the %NL80211_ATTR_SOCKET_OWNER
+ *	flag. When used with %NL80211_ATTR_CONTROL_PORT_NO_PREAUTH, pre-auth
+ *	frames are not forwared over the control port.
+ *
+ * @NL80211_ATTR_TESTDATA: Testmode data blob, passed through to the driver.
+ *	We recommend using nested, driver-specific attributes within this.
+ *
+ * @NL80211_ATTR_DISCONNECTED_BY_AP: A flag indicating that the DISCONNECT
+ *	event was due to the AP disconnecting the station, and not due to
+ *	a local disconnect request.
+ * @NL80211_ATTR_STATUS_CODE: StatusCode for the %NL80211_CMD_CONNECT
+ *	event (u16)
+ * @NL80211_ATTR_PRIVACY: Flag attribute, used with connect(), indicating
+ *	that protected APs should be used. This is also used with NEW_BEACON to
+ *	indicate that the BSS is to use protection.
+ *
+ * @NL80211_ATTR_CIPHERS_PAIRWISE: Used with CONNECT, ASSOCIATE, and NEW_BEACON
+ *	to indicate which unicast key ciphers will be used with the connection
+ *	(an array of u32).
+ * @NL80211_ATTR_CIPHER_GROUP: Used with CONNECT, ASSOCIATE, and NEW_BEACON to
+ *	indicate which group key cipher will be used with the connection (a
+ *	u32).
+ * @NL80211_ATTR_WPA_VERSIONS: Used with CONNECT, ASSOCIATE, and NEW_BEACON to
+ *	indicate which WPA version(s) the AP we want to associate with is using
+ *	(a u32 with flags from &enum nl80211_wpa_versions).
+ * @NL80211_ATTR_AKM_SUITES: Used with CONNECT, ASSOCIATE, and NEW_BEACON to
+ *	indicate which key management algorithm(s) to use (an array of u32).
+ *	This attribute is also sent in response to @NL80211_CMD_GET_WIPHY,
+ *	indicating the supported AKM suites, intended for specific drivers which
+ *	implement SME and have constraints on which AKMs are supported and also
+ *	the cases where an AKM support is offloaded to the driver/firmware.
+ *	If there is no such notification from the driver, user space should
+ *	assume the driver supports all the AKM suites.
+ *
+ * @NL80211_ATTR_REQ_IE: (Re)association request information elements as
+ *	sent out by the card, for ROAM and successful CONNECT events.
+ * @NL80211_ATTR_RESP_IE: (Re)association response information elements as
+ *	sent by peer, for ROAM and successful CONNECT events.
+ *
+ * @NL80211_ATTR_PREV_BSSID: previous BSSID, to be used in ASSOCIATE and CONNECT
+ *	commands to specify a request to reassociate within an ESS, i.e., to use
+ *	Reassociate Request frame (with the value of this attribute in the
+ *	Current AP address field) instead of Association Request frame which is
+ *	used for the initial association to an ESS.
+ *
+ * @NL80211_ATTR_KEY: key information in a nested attribute with
+ *	%NL80211_KEY_* sub-attributes
+ * @NL80211_ATTR_KEYS: array of keys for static WEP keys for connect()
+ *	and join_ibss(), key information is in a nested attribute each
+ *	with %NL80211_KEY_* sub-attributes
+ *
+ * @NL80211_ATTR_PID: Process ID of a network namespace.
+ *
+ * @NL80211_ATTR_GENERATION: Used to indicate consistent snapshots for
+ *	dumps. This number increases whenever the object list being
+ *	dumped changes, and as such userspace can verify that it has
+ *	obtained a complete and consistent snapshot by verifying that
+ *	all dump messages contain the same generation number. If it
+ *	changed then the list changed and the dump should be repeated
+ *	completely from scratch.
+ *
+ * @NL80211_ATTR_4ADDR: Use 4-address frames on a virtual interface
+ *
+ * @NL80211_ATTR_SURVEY_INFO: survey information about a channel, part of
+ *      the survey response for %NL80211_CMD_GET_SURVEY, nested attribute
+ *      containing info as possible, see &enum survey_info.
+ *
+ * @NL80211_ATTR_PMKID: PMK material for PMKSA caching.
+ * @NL80211_ATTR_MAX_NUM_PMKIDS: maximum number of PMKIDs a firmware can
+ *	cache, a wiphy attribute.
+ *
+ * @NL80211_ATTR_DURATION: Duration of an operation in milliseconds, u32.
+ * @NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION: Device attribute that
+ *	specifies the maximum duration that can be requested with the
+ *	remain-on-channel operation, in milliseconds, u32.
+ *
+ * @NL80211_ATTR_COOKIE: Generic 64-bit cookie to identify objects.
+ *
+ * @NL80211_ATTR_TX_RATES: Nested set of attributes
+ *	(enum nl80211_tx_rate_attributes) describing TX rates per band. The
+ *	enum nl80211_band value is used as the index (nla_type() of the nested
+ *	data. If a band is not included, it will be configured to allow all
+ *	rates based on negotiated supported rates information. This attribute
+ *	is used with %NL80211_CMD_SET_TX_BITRATE_MASK and with starting AP,
+ *	and joining mesh networks (not IBSS yet). In the later case, it must
+ *	specify just a single bitrate, which is to be used for the beacon.
+ *	The driver must also specify support for this with the extended
+ *	features NL80211_EXT_FEATURE_BEACON_RATE_LEGACY,
+ *	NL80211_EXT_FEATURE_BEACON_RATE_HT,
+ *	NL80211_EXT_FEATURE_BEACON_RATE_VHT and
+ *	NL80211_EXT_FEATURE_BEACON_RATE_HE.
+ *
+ * @NL80211_ATTR_FRAME_MATCH: A binary attribute which typically must contain
+ *	at least one byte, currently used with @NL80211_CMD_REGISTER_FRAME.
+ * @NL80211_ATTR_FRAME_TYPE: A u16 indicating the frame type/subtype for the
+ *	@NL80211_CMD_REGISTER_FRAME command.
+ * @NL80211_ATTR_TX_FRAME_TYPES: wiphy capability attribute, which is a
+ *	nested attribute of %NL80211_ATTR_FRAME_TYPE attributes, containing
+ *	information about which frame types can be transmitted with
+ *	%NL80211_CMD_FRAME.
+ * @NL80211_ATTR_RX_FRAME_TYPES: wiphy capability attribute, which is a
+ *	nested attribute of %NL80211_ATTR_FRAME_TYPE attributes, containing
+ *	information about which frame types can be registered for RX.
+ *
+ * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was
+ *	acknowledged by the recipient.
+ *
+ * @NL80211_ATTR_PS_STATE: powersave state, using &enum nl80211_ps_state values.
+ *
+ * @NL80211_ATTR_CQM: connection quality monitor configuration in a
+ *	nested attribute with %NL80211_ATTR_CQM_* sub-attributes.
+ *
+ * @NL80211_ATTR_LOCAL_STATE_CHANGE: Flag attribute to indicate that a command
+ *	is requesting a local authentication/association state change without
+ *	invoking actual management frame exchange. This can be used with
+ *	NL80211_CMD_AUTHENTICATE, NL80211_CMD_DEAUTHENTICATE,
+ *	NL80211_CMD_DISASSOCIATE.
+ *
+ * @NL80211_ATTR_AP_ISOLATE: (AP mode) Do not forward traffic between stations
+ *	connected to this BSS.
+ *
+ * @NL80211_ATTR_WIPHY_TX_POWER_SETTING: Transmit power setting type. See
+ *      &enum nl80211_tx_power_setting for possible values.
+ * @NL80211_ATTR_WIPHY_TX_POWER_LEVEL: Transmit power level in signed mBm units.
+ *      This is used in association with @NL80211_ATTR_WIPHY_TX_POWER_SETTING
+ *      for non-automatic settings.
+ *
+ * @NL80211_ATTR_SUPPORT_IBSS_RSN: The device supports IBSS RSN, which mostly
+ *	means support for per-station GTKs.
+ *
+ * @NL80211_ATTR_WIPHY_ANTENNA_TX: Bitmap of allowed antennas for transmitting.
+ *	This can be used to mask out antennas which are not attached or should
+ *	not be used for transmitting. If an antenna is not selected in this
+ *	bitmap the hardware is not allowed to transmit on this antenna.
+ *
+ *	Each bit represents one antenna, starting with antenna 1 at the first
+ *	bit. Depending on which antennas are selected in the bitmap, 802.11n
+ *	drivers can derive which chainmasks to use (if all antennas belonging to
+ *	a particular chain are disabled this chain should be disabled) and if
+ *	a chain has diversity antennas wether diversity should be used or not.
+ *	HT capabilities (STBC, TX Beamforming, Antenna selection) can be
+ *	derived from the available chains after applying the antenna mask.
+ *	Non-802.11n drivers can derive wether to use diversity or not.
+ *	Drivers may reject configurations or RX/TX mask combinations they cannot
+ *	support by returning -EINVAL.
+ *
+ * @NL80211_ATTR_WIPHY_ANTENNA_RX: Bitmap of allowed antennas for receiving.
+ *	This can be used to mask out antennas which are not attached or should
+ *	not be used for receiving. If an antenna is not selected in this bitmap
+ *	the hardware should not be configured to receive on this antenna.
+ *	For a more detailed description see @NL80211_ATTR_WIPHY_ANTENNA_TX.
+ *
+ * @NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX: Bitmap of antennas which are available
+ *	for configuration as TX antennas via the above parameters.
+ *
+ * @NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX: Bitmap of antennas which are available
+ *	for configuration as RX antennas via the above parameters.
+ *
+ * @NL80211_ATTR_MCAST_RATE: Multicast tx rate (in 100 kbps) for IBSS
+ *
+ * @NL80211_ATTR_OFFCHANNEL_TX_OK: For management frame TX, the frame may be
+ *	transmitted on another channel when the channel given doesn't match
+ *	the current channel. If the current channel doesn't match and this
+ *	flag isn't set, the frame will be rejected. This is also used as an
+ *	nl80211 capability flag.
+ *
+ * @NL80211_ATTR_BSS_HT_OPMODE: HT operation mode (u16)
+ *
+ * @NL80211_ATTR_KEY_DEFAULT_TYPES: A nested attribute containing flags
+ *	attributes, specifying what a key should be set as default as.
+ *	See &enum nl80211_key_default_types.
+ *
+ * @NL80211_ATTR_MESH_SETUP: Optional mesh setup parameters.  These cannot be
+ *	changed once the mesh is active.
+ * @NL80211_ATTR_MESH_CONFIG: Mesh configuration parameters, a nested attribute
+ *	containing attributes from &enum nl80211_meshconf_params.
+ * @NL80211_ATTR_SUPPORT_MESH_AUTH: Currently, this means the underlying driver
+ *	allows auth frames in a mesh to be passed to userspace for processing via
+ *	the @NL80211_MESH_SETUP_USERSPACE_AUTH flag.
+ * @NL80211_ATTR_STA_PLINK_STATE: The state of a mesh peer link as defined in
+ *	&enum nl80211_plink_state. Used when userspace is driving the peer link
+ *	management state machine.  @NL80211_MESH_SETUP_USERSPACE_AMPE or
+ *	@NL80211_MESH_SETUP_USERSPACE_MPM must be enabled.
+ *
+ * @NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED: indicates, as part of the wiphy
+ *	capabilities, the supported WoWLAN triggers
+ * @NL80211_ATTR_WOWLAN_TRIGGERS: used by %NL80211_CMD_SET_WOWLAN to
+ *	indicate which WoW triggers should be enabled. This is also
+ *	used by %NL80211_CMD_GET_WOWLAN to get the currently enabled WoWLAN
+ *	triggers.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_INTERVAL: Interval between scheduled scan
+ *	cycles, in msecs.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_MATCH: Nested attribute with one or more
+ *	sets of attributes to match during scheduled scans.  Only BSSs
+ *	that match any of the sets will be reported.  These are
+ *	pass-thru filter rules.
+ *	For a match to succeed, the BSS must match all attributes of a
+ *	set.  Since not every hardware supports matching all types of
+ *	attributes, there is no guarantee that the reported BSSs are
+ *	fully complying with the match sets and userspace needs to be
+ *	able to ignore them by itself.
+ *	Thus, the implementation is somewhat hardware-dependent, but
+ *	this is only an optimization and the userspace application
+ *	needs to handle all the non-filtered results anyway.
+ *	If the match attributes don't make sense when combined with
+ *	the values passed in @NL80211_ATTR_SCAN_SSIDS (eg. if an SSID
+ *	is included in the probe request, but the match attributes
+ *	will never let it go through), -EINVAL may be returned.
+ *	If omitted, no filtering is done.
+ *
+ * @NL80211_ATTR_INTERFACE_COMBINATIONS: Nested attribute listing the supported
+ *	interface combinations. In each nested item, it contains attributes
+ *	defined in &enum nl80211_if_combination_attrs.
+ * @NL80211_ATTR_SOFTWARE_IFTYPES: Nested attribute (just like
+ *	%NL80211_ATTR_SUPPORTED_IFTYPES) containing the interface types that
+ *	are managed in software: interfaces of these types aren't subject to
+ *	any restrictions in their number or combinations.
+ *
+ * @NL80211_ATTR_REKEY_DATA: nested attribute containing the information
+ *	necessary for GTK rekeying in the device, see &enum nl80211_rekey_data.
+ *
+ * @NL80211_ATTR_SCAN_SUPP_RATES: rates per to be advertised as supported in scan,
+ *	nested array attribute containing an entry for each band, with the entry
+ *	being a list of supported rates as defined by IEEE 802.11 7.3.2.2 but
+ *	without the length restriction (at most %NL80211_MAX_SUPP_RATES).
+ *
+ * @NL80211_ATTR_HIDDEN_SSID: indicates whether SSID is to be hidden from Beacon
+ *	and Probe Response (when response to wildcard Probe Request); see
+ *	&enum nl80211_hidden_ssid, represented as a u32
+ *
+ * @NL80211_ATTR_IE_PROBE_RESP: Information element(s) for Probe Response frame.
+ *	This is used with %NL80211_CMD_NEW_BEACON and %NL80211_CMD_SET_BEACON to
+ *	provide extra IEs (e.g., WPS/P2P IE) into Probe Response frames when the
+ *	driver (or firmware) replies to Probe Request frames.
+ * @NL80211_ATTR_IE_ASSOC_RESP: Information element(s) for (Re)Association
+ *	Response frames. This is used with %NL80211_CMD_NEW_BEACON and
+ *	%NL80211_CMD_SET_BEACON to provide extra IEs (e.g., WPS/P2P IE) into
+ *	(Re)Association Response frames when the driver (or firmware) replies to
+ *	(Re)Association Request frames.
+ *
+ * @NL80211_ATTR_STA_WME: Nested attribute containing the wme configuration
+ *	of the station, see &enum nl80211_sta_wme_attr.
+ * @NL80211_ATTR_SUPPORT_AP_UAPSD: the device supports uapsd when working
+ *	as AP.
+ *
+ * @NL80211_ATTR_ROAM_SUPPORT: Indicates whether the firmware is capable of
+ *	roaming to another AP in the same ESS if the signal lever is low.
+ *
+ * @NL80211_ATTR_PMKSA_CANDIDATE: Nested attribute containing the PMKSA caching
+ *	candidate information, see &enum nl80211_pmksa_candidate_attr.
+ *
+ * @NL80211_ATTR_TX_NO_CCK_RATE: Indicates whether to use CCK rate or not
+ *	for management frames transmission. In order to avoid p2p probe/action
+ *	frames are being transmitted at CCK rate in 2GHz band, the user space
+ *	applications use this attribute.
+ *	This attribute is used with %NL80211_CMD_TRIGGER_SCAN and
+ *	%NL80211_CMD_FRAME commands.
+ *
+ * @NL80211_ATTR_TDLS_ACTION: Low level TDLS action code (e.g. link setup
+ *	request, link setup confirm, link teardown, etc.). Values are
+ *	described in the TDLS (802.11z) specification.
+ * @NL80211_ATTR_TDLS_DIALOG_TOKEN: Non-zero token for uniquely identifying a
+ *	TDLS conversation between two devices.
+ * @NL80211_ATTR_TDLS_OPERATION: High level TDLS operation; see
+ *	&enum nl80211_tdls_operation, represented as a u8.
+ * @NL80211_ATTR_TDLS_SUPPORT: A flag indicating the device can operate
+ *	as a TDLS peer sta.
+ * @NL80211_ATTR_TDLS_EXTERNAL_SETUP: The TDLS discovery/setup and teardown
+ *	procedures should be performed by sending TDLS packets via
+ *	%NL80211_CMD_TDLS_MGMT. Otherwise %NL80211_CMD_TDLS_OPER should be
+ *	used for asking the driver to perform a TDLS operation.
+ *
+ * @NL80211_ATTR_DEVICE_AP_SME: This u32 attribute may be listed for devices
+ *	that have AP support to indicate that they have the AP SME integrated
+ *	with support for the features listed in this attribute, see
+ *	&enum nl80211_ap_sme_features.
+ *
+ * @NL80211_ATTR_DONT_WAIT_FOR_ACK: Used with %NL80211_CMD_FRAME, this tells
+ *	the driver to not wait for an acknowledgement. Note that due to this,
+ *	it will also not give a status callback nor return a cookie. This is
+ *	mostly useful for probe responses to save airtime.
+ *
+ * @NL80211_ATTR_FEATURE_FLAGS: This u32 attribute contains flags from
+ *	&enum nl80211_feature_flags and is advertised in wiphy information.
+ * @NL80211_ATTR_PROBE_RESP_OFFLOAD: Indicates that the HW responds to probe
+ *	requests while operating in AP-mode.
+ *	This attribute holds a bitmap of the supported protocols for
+ *	offloading (see &enum nl80211_probe_resp_offload_support_attr).
+ *
+ * @NL80211_ATTR_PROBE_RESP: Probe Response template data. Contains the entire
+ *	probe-response frame. The DA field in the 802.11 header is zero-ed out,
+ *	to be filled by the FW.
+ * @NL80211_ATTR_DISABLE_HT: Force HT capable interfaces to disable
+ *      this feature during association. This is a flag attribute.
+ *	Currently only supported in mac80211 drivers.
+ * @NL80211_ATTR_DISABLE_VHT: Force VHT capable interfaces to disable
+ *      this feature during association. This is a flag attribute.
+ *	Currently only supported in mac80211 drivers.
+ * @NL80211_ATTR_DISABLE_HE: Force HE capable interfaces to disable
+ *      this feature during association. This is a flag attribute.
+ *	Currently only supported in mac80211 drivers.
+ * @NL80211_ATTR_HT_CAPABILITY_MASK: Specify which bits of the
+ *      ATTR_HT_CAPABILITY to which attention should be paid.
+ *      Currently, only mac80211 NICs support this feature.
+ *      The values that may be configured are:
+ *       MCS rates, MAX-AMSDU, HT-20-40 and HT_CAP_SGI_40
+ *       AMPDU density and AMPDU factor.
+ *      All values are treated as suggestions and may be ignored
+ *      by the driver as required.  The actual values may be seen in
+ *      the station debugfs ht_caps file.
+ *
+ * @NL80211_ATTR_DFS_REGION: region for regulatory rules which this country
+ *    abides to when initiating radiation on DFS channels. A country maps
+ *    to one DFS region.
+ *
+ * @NL80211_ATTR_NOACK_MAP: This u16 bitmap contains the No Ack Policy of
+ *      up to 16 TIDs.
+ *
+ * @NL80211_ATTR_INACTIVITY_TIMEOUT: timeout value in seconds, this can be
+ *	used by the drivers which has MLME in firmware and does not have support
+ *	to report per station tx/rx activity to free up the station entry from
+ *	the list. This needs to be used when the driver advertises the
+ *	capability to timeout the stations.
+ *
+ * @NL80211_ATTR_RX_SIGNAL_DBM: signal strength in dBm (as a 32-bit int);
+ *	this attribute is (depending on the driver capabilities) added to
+ *	received frames indicated with %NL80211_CMD_FRAME.
+ *
+ * @NL80211_ATTR_BG_SCAN_PERIOD: Background scan period in seconds
+ *      or 0 to disable background scan.
+ *
+ * @NL80211_ATTR_USER_REG_HINT_TYPE: type of regulatory hint passed from
+ *	userspace. If unset it is assumed the hint comes directly from
+ *	a user. If set code could specify exactly what type of source
+ *	was used to provide the hint. For the different types of
+ *	allowed user regulatory hints see nl80211_user_reg_hint_type.
+ *
+ * @NL80211_ATTR_CONN_FAILED_REASON: The reason for which AP has rejected
+ *	the connection request from a station. nl80211_connect_failed_reason
+ *	enum has different reasons of connection failure.
+ *
+ * @NL80211_ATTR_AUTH_DATA: Fields and elements in Authentication frames.
+ *	This contains the authentication frame body (non-IE and IE data),
+ *	excluding the Authentication algorithm number, i.e., starting at the
+ *	Authentication transaction sequence number field. It is used with
+ *	authentication algorithms that need special fields to be added into
+ *	the frames (SAE and FILS). Currently, only the SAE cases use the
+ *	initial two fields (Authentication transaction sequence number and
+ *	Status code). However, those fields are included in the attribute data
+ *	for all authentication algorithms to keep the attribute definition
+ *	consistent.
+ *
+ * @NL80211_ATTR_VHT_CAPABILITY: VHT Capability information element (from
+ *	association request when used with NL80211_CMD_NEW_STATION)
+ *
+ * @NL80211_ATTR_SCAN_FLAGS: scan request control flags (u32)
+ *
+ * @NL80211_ATTR_P2P_CTWINDOW: P2P GO Client Traffic Window (u8), used with
+ *	the START_AP and SET_BSS commands
+ * @NL80211_ATTR_P2P_OPPPS: P2P GO opportunistic PS (u8), used with the
+ *	START_AP and SET_BSS commands. This can have the values 0 or 1;
+ *	if not given in START_AP 0 is assumed, if not given in SET_BSS
+ *	no change is made.
+ *
+ * @NL80211_ATTR_LOCAL_MESH_POWER_MODE: local mesh STA link-specific power mode
+ *	defined in &enum nl80211_mesh_power_mode.
+ *
+ * @NL80211_ATTR_ACL_POLICY: ACL policy, see &enum nl80211_acl_policy,
+ *	carried in a u32 attribute
+ *
+ * @NL80211_ATTR_MAC_ADDRS: Array of nested MAC addresses, used for
+ *	MAC ACL.
+ *
+ * @NL80211_ATTR_MAC_ACL_MAX: u32 attribute to advertise the maximum
+ *	number of MAC addresses that a device can support for MAC
+ *	ACL.
+ *
+ * @NL80211_ATTR_RADAR_EVENT: Type of radar event for notification to userspace,
+ *	contains a value of enum nl80211_radar_event (u32).
+ *
+ * @NL80211_ATTR_EXT_CAPA: 802.11 extended capabilities that the kernel driver
+ *	has and handles. The format is the same as the IE contents. See
+ *	802.11-2012 8.4.2.29 for more information.
+ * @NL80211_ATTR_EXT_CAPA_MASK: Extended capabilities that the kernel driver
+ *	has set in the %NL80211_ATTR_EXT_CAPA value, for multibit fields.
+ *
+ * @NL80211_ATTR_STA_CAPABILITY: Station capabilities (u16) are advertised to
+ *	the driver, e.g., to enable TDLS power save (PU-APSD).
+ *
+ * @NL80211_ATTR_STA_EXT_CAPABILITY: Station extended capabilities are
+ *	advertised to the driver, e.g., to enable TDLS off channel operations
+ *	and PU-APSD.
+ *
+ * @NL80211_ATTR_PROTOCOL_FEATURES: global nl80211 feature flags, see
+ *	&enum nl80211_protocol_features, the attribute is a u32.
+ *
+ * @NL80211_ATTR_SPLIT_WIPHY_DUMP: flag attribute, userspace supports
+ *	receiving the data for a single wiphy split across multiple
+ *	messages, given with wiphy dump message
+ *
+ * @NL80211_ATTR_MDID: Mobility Domain Identifier
+ *
+ * @NL80211_ATTR_IE_RIC: Resource Information Container Information
+ *	Element
+ *
+ * @NL80211_ATTR_CRIT_PROT_ID: critical protocol identifier requiring increased
+ *	reliability, see &enum nl80211_crit_proto_id (u16).
+ * @NL80211_ATTR_MAX_CRIT_PROT_DURATION: duration in milliseconds in which
+ *      the connection should have increased reliability (u16).
+ *
+ * @NL80211_ATTR_PEER_AID: Association ID for the peer TDLS station (u16).
+ *	This is similar to @NL80211_ATTR_STA_AID but with a difference of being
+ *	allowed to be used with the first @NL80211_CMD_SET_STATION command to
+ *	update a TDLS peer STA entry.
+ *
+ * @NL80211_ATTR_COALESCE_RULE: Coalesce rule information.
+ *
+ * @NL80211_ATTR_CH_SWITCH_COUNT: u32 attribute specifying the number of TBTT's
+ *	until the channel switch event.
+ * @NL80211_ATTR_CH_SWITCH_BLOCK_TX: flag attribute specifying that transmission
+ *	must be blocked on the current channel (before the channel switch
+ *	operation). Also included in the channel switch started event if quiet
+ *	was requested by the AP.
+ * @NL80211_ATTR_CSA_IES: Nested set of attributes containing the IE information
+ *	for the time while performing a channel switch.
+ * @NL80211_ATTR_CNTDWN_OFFS_BEACON: An array of offsets (u16) to the channel
+ *	switch or color change counters in the beacons tail (%NL80211_ATTR_BEACON_TAIL).
+ * @NL80211_ATTR_CNTDWN_OFFS_PRESP: An array of offsets (u16) to the channel
+ *	switch or color change counters in the probe response (%NL80211_ATTR_PROBE_RESP).
+ *
+ * @NL80211_ATTR_RXMGMT_FLAGS: flags for nl80211_send_mgmt(), u32.
+ *	As specified in the &enum nl80211_rxmgmt_flags.
+ *
+ * @NL80211_ATTR_STA_SUPPORTED_CHANNELS: array of supported channels.
+ *
+ * @NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES: array of supported
+ *      operating classes.
+ *
+ * @NL80211_ATTR_HANDLE_DFS: A flag indicating whether user space
+ *	controls DFS operation in IBSS mode. If the flag is included in
+ *	%NL80211_CMD_JOIN_IBSS request, the driver will allow use of DFS
+ *	channels and reports radar events to userspace. Userspace is required
+ *	to react to radar events, e.g. initiate a channel switch or leave the
+ *	IBSS network.
+ *
+ * @NL80211_ATTR_SUPPORT_5_MHZ: A flag indicating that the device supports
+ *	5 MHz channel bandwidth.
+ * @NL80211_ATTR_SUPPORT_10_MHZ: A flag indicating that the device supports
+ *	10 MHz channel bandwidth.
+ *
+ * @NL80211_ATTR_OPMODE_NOTIF: Operating mode field from Operating Mode
+ *	Notification Element based on association request when used with
+ *	%NL80211_CMD_NEW_STATION or %NL80211_CMD_SET_STATION (only when
+ *	%NL80211_FEATURE_FULL_AP_CLIENT_STATE is supported, or with TDLS);
+ *	u8 attribute.
+ *
+ * @NL80211_ATTR_VENDOR_ID: The vendor ID, either a 24-bit OUI or, if
+ *	%NL80211_VENDOR_ID_IS_LINUX is set, a special Linux ID (not used yet)
+ * @NL80211_ATTR_VENDOR_SUBCMD: vendor sub-command
+ * @NL80211_ATTR_VENDOR_DATA: data for the vendor command, if any; this
+ *	attribute is also used for vendor command feature advertisement
+ * @NL80211_ATTR_VENDOR_EVENTS: used for event list advertising in the wiphy
+ *	info, containing a nested array of possible events
+ *
+ * @NL80211_ATTR_QOS_MAP: IP DSCP mapping for Interworking QoS mapping. This
+ *	data is in the format defined for the payload of the QoS Map Set element
+ *	in IEEE Std 802.11-2012, 8.4.2.97.
+ *
+ * @NL80211_ATTR_MAC_HINT: MAC address recommendation as initial BSS
+ * @NL80211_ATTR_WIPHY_FREQ_HINT: frequency of the recommended initial BSS
+ *
+ * @NL80211_ATTR_MAX_AP_ASSOC_STA: Device attribute that indicates how many
+ *	associated stations are supported in AP mode (including P2P GO); u32.
+ *	Since drivers may not have a fixed limit on the maximum number (e.g.,
+ *	other concurrent operations may affect this), drivers are allowed to
+ *	advertise values that cannot always be met. In such cases, an attempt
+ *	to add a new station entry with @NL80211_CMD_NEW_STATION may fail.
+ *
+ * @NL80211_ATTR_CSA_C_OFFSETS_TX: An array of csa counter offsets (u16) which
+ *	should be updated when the frame is transmitted.
+ * @NL80211_ATTR_MAX_CSA_COUNTERS: U8 attribute used to advertise the maximum
+ *	supported number of csa counters.
+ *
+ * @NL80211_ATTR_TDLS_PEER_CAPABILITY: flags for TDLS peer capabilities, u32.
+ *	As specified in the &enum nl80211_tdls_peer_capability.
+ *
+ * @NL80211_ATTR_SOCKET_OWNER: Flag attribute, if set during interface
+ *	creation then the new interface will be owned by the netlink socket
+ *	that created it and will be destroyed when the socket is closed.
+ *	If set during scheduled scan start then the new scan req will be
+ *	owned by the netlink socket that created it and the scheduled scan will
+ *	be stopped when the socket is closed.
+ *	If set during configuration of regulatory indoor operation then the
+ *	regulatory indoor configuration would be owned by the netlink socket
+ *	that configured the indoor setting, and the indoor operation would be
+ *	cleared when the socket is closed.
+ *	If set during NAN interface creation, the interface will be destroyed
+ *	if the socket is closed just like any other interface. Moreover, NAN
+ *	notifications will be sent in unicast to that socket. Without this
+ *	attribute, the notifications will be sent to the %NL80211_MCGRP_NAN
+ *	multicast group.
+ *	If set during %NL80211_CMD_ASSOCIATE or %NL80211_CMD_CONNECT the
+ *	station will deauthenticate when the socket is closed.
+ *	If set during %NL80211_CMD_JOIN_IBSS the IBSS will be automatically
+ *	torn down when the socket is closed.
+ *	If set during %NL80211_CMD_JOIN_MESH the mesh setup will be
+ *	automatically torn down when the socket is closed.
+ *	If set during %NL80211_CMD_START_AP the AP will be automatically
+ *	disabled when the socket is closed.
+ *
+ * @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is
+ *	the TDLS link initiator.
+ *
+ * @NL80211_ATTR_USE_RRM: flag for indicating whether the current connection
+ *	shall support Radio Resource Measurements (11k). This attribute can be
+ *	used with %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests.
+ *	User space applications are expected to use this flag only if the
+ *	underlying device supports these minimal RRM features:
+ *		%NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES,
+ *		%NL80211_FEATURE_QUIET,
+ *	Or, if global RRM is supported, see:
+ *		%NL80211_EXT_FEATURE_RRM
+ *	If this flag is used, driver must add the Power Capabilities IE to the
+ *	association request. In addition, it must also set the RRM capability
+ *	flag in the association request's Capability Info field.
+ *
+ * @NL80211_ATTR_WIPHY_DYN_ACK: flag attribute used to enable ACK timeout
+ *	estimation algorithm (dynack). In order to activate dynack
+ *	%NL80211_FEATURE_ACKTO_ESTIMATION feature flag must be set by lower
+ *	drivers to indicate dynack capability. Dynack is automatically disabled
+ *	setting valid value for coverage class.
+ *
+ * @NL80211_ATTR_TSID: a TSID value (u8 attribute)
+ * @NL80211_ATTR_USER_PRIO: user priority value (u8 attribute)
+ * @NL80211_ATTR_ADMITTED_TIME: admitted time in units of 32 microseconds
+ *	(per second) (u16 attribute)
+ *
+ * @NL80211_ATTR_SMPS_MODE: SMPS mode to use (ap mode). see
+ *	&enum nl80211_smps_mode.
+ *
+ * @NL80211_ATTR_OPER_CLASS: operating class
+ *
+ * @NL80211_ATTR_MAC_MASK: MAC address mask
+ *
+ * @NL80211_ATTR_WIPHY_SELF_MANAGED_REG: flag attribute indicating this device
+ *	is self-managing its regulatory information and any regulatory domain
+ *	obtained from it is coming from the device's wiphy and not the global
+ *	cfg80211 regdomain.
+ *
+ * @NL80211_ATTR_EXT_FEATURES: extended feature flags contained in a byte
+ *	array. The feature flags are identified by their bit index (see &enum
+ *	nl80211_ext_feature_index). The bit index is ordered starting at the
+ *	least-significant bit of the first byte in the array, ie. bit index 0
+ *	is located at bit 0 of byte 0. bit index 25 would be located at bit 1
+ *	of byte 3 (u8 array).
+ *
+ * @NL80211_ATTR_SURVEY_RADIO_STATS: Request overall radio statistics to be
+ *	returned along with other survey data. If set, @NL80211_CMD_GET_SURVEY
+ *	may return a survey entry without a channel indicating global radio
+ *	statistics (only some values are valid and make sense.)
+ *	For devices that don't return such an entry even then, the information
+ *	should be contained in the result as the sum of the respective counters
+ *	over all channels.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_DELAY: delay before the first cycle of a
+ *	scheduled scan is started.  Or the delay before a WoWLAN
+ *	net-detect scan is started, counting from the moment the
+ *	system is suspended.  This value is a u32, in seconds.
+
+ * @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device
+ *      is operating in an indoor environment.
+ *
+ * @NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS: maximum number of scan plans for
+ *	scheduled scan supported by the device (u32), a wiphy attribute.
+ * @NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL: maximum interval (in seconds) for
+ *	a scan plan (u32), a wiphy attribute.
+ * @NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS: maximum number of iterations in
+ *	a scan plan (u32), a wiphy attribute.
+ * @NL80211_ATTR_SCHED_SCAN_PLANS: a list of scan plans for scheduled scan.
+ *	Each scan plan defines the number of scan iterations and the interval
+ *	between scans. The last scan plan will always run infinitely,
+ *	thus it must not specify the number of iterations, only the interval
+ *	between scans. The scan plans are executed sequentially.
+ *	Each scan plan is a nested attribute of &enum nl80211_sched_scan_plan.
+ * @NL80211_ATTR_PBSS: flag attribute. If set it means operate
+ *	in a PBSS. Specified in %NL80211_CMD_CONNECT to request
+ *	connecting to a PCP, and in %NL80211_CMD_START_AP to start
+ *	a PCP instead of AP. Relevant for DMG networks only.
+ * @NL80211_ATTR_BSS_SELECT: nested attribute for driver supporting the
+ *	BSS selection feature. When used with %NL80211_CMD_GET_WIPHY it contains
+ *	attributes according &enum nl80211_bss_select_attr to indicate what
+ *	BSS selection behaviours are supported. When used with %NL80211_CMD_CONNECT
+ *	it contains the behaviour-specific attribute containing the parameters for
+ *	BSS selection to be done by driver and/or firmware.
+ *
+ * @NL80211_ATTR_STA_SUPPORT_P2P_PS: whether P2P PS mechanism supported
+ *	or not. u8, one of the values of &enum nl80211_sta_p2p_ps_status
+ *
+ * @NL80211_ATTR_PAD: attribute used for padding for 64-bit alignment
+ *
+ * @NL80211_ATTR_IFTYPE_EXT_CAPA: Nested attribute of the following attributes:
+ *	%NL80211_ATTR_IFTYPE, %NL80211_ATTR_EXT_CAPA,
+ *	%NL80211_ATTR_EXT_CAPA_MASK, to specify the extended capabilities and
+ *	other interface-type specific capabilities per interface type. For MLO,
+ *	%NL80211_ATTR_EML_CAPABILITY and %NL80211_ATTR_MLD_CAPA_AND_OPS are
+ *	present.
+ *
+ * @NL80211_ATTR_MU_MIMO_GROUP_DATA: array of 24 bytes that defines a MU-MIMO
+ *	groupID for monitor mode.
+ *	The first 8 bytes are a mask that defines the membership in each
+ *	group (there are 64 groups, group 0 and 63 are reserved),
+ *	each bit represents a group and set to 1 for being a member in
+ *	that group and 0 for not being a member.
+ *	The remaining 16 bytes define the position in each group: 2 bits for
+ *	each group.
+ *	(smaller group numbers represented on most significant bits and bigger
+ *	group numbers on least significant bits.)
+ *	This attribute is used only if all interfaces are in monitor mode.
+ *	Set this attribute in order to monitor packets using the given MU-MIMO
+ *	groupID data.
+ *	to turn off that feature set all the bits of the groupID to zero.
+ * @NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR: mac address for the sniffer to follow
+ *	when using MU-MIMO air sniffer.
+ *	to turn that feature off set an invalid mac address
+ *	(e.g. FF:FF:FF:FF:FF:FF)
+ *
+ * @NL80211_ATTR_SCAN_START_TIME_TSF: The time at which the scan was actually
+ *	started (u64). The time is the TSF of the BSS the interface that
+ *	requested the scan is connected to (if available, otherwise this
+ *	attribute must not be included).
+ * @NL80211_ATTR_SCAN_START_TIME_TSF_BSSID: The BSS according to which
+ *	%NL80211_ATTR_SCAN_START_TIME_TSF is set.
+ * @NL80211_ATTR_MEASUREMENT_DURATION: measurement duration in TUs (u16). If
+ *	%NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY is not set, this is the
+ *	maximum measurement duration allowed. This attribute is used with
+ *	measurement requests. It can also be used with %NL80211_CMD_TRIGGER_SCAN
+ *	if the scan is used for beacon report radio measurement.
+ * @NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY: flag attribute that indicates
+ *	that the duration specified with %NL80211_ATTR_MEASUREMENT_DURATION is
+ *	mandatory. If this flag is not set, the duration is the maximum duration
+ *	and the actual measurement duration may be shorter.
+ *
+ * @NL80211_ATTR_MESH_PEER_AID: Association ID for the mesh peer (u16). This is
+ *	used to pull the stored data for mesh peer in power save state.
+ *
+ * @NL80211_ATTR_NAN_MASTER_PREF: the master preference to be used by
+ *	%NL80211_CMD_START_NAN and optionally with
+ *	%NL80211_CMD_CHANGE_NAN_CONFIG. Its type is u8 and it can't be 0.
+ *	Also, values 1 and 255 are reserved for certification purposes and
+ *	should not be used during a normal device operation.
+ * @NL80211_ATTR_BANDS: operating bands configuration.  This is a u32
+ *	bitmask of BIT(NL80211_BAND_*) as described in %enum
+ *	nl80211_band.  For instance, for NL80211_BAND_2GHZ, bit 0
+ *	would be set.  This attribute is used with
+ *	%NL80211_CMD_START_NAN and %NL80211_CMD_CHANGE_NAN_CONFIG, and
+ *	it is optional.  If no bands are set, it means don't-care and
+ *	the device will decide what to use.
+ * @NL80211_ATTR_NAN_FUNC: a function that can be added to NAN. See
+ *	&enum nl80211_nan_func_attributes for description of this nested
+ *	attribute.
+ * @NL80211_ATTR_NAN_MATCH: used to report a match. This is a nested attribute.
+ *	See &enum nl80211_nan_match_attributes.
+ * @NL80211_ATTR_FILS_KEK: KEK for FILS (Re)Association Request/Response frame
+ *	protection.
+ * @NL80211_ATTR_FILS_NONCES: Nonces (part of AAD) for FILS (Re)Association
+ *	Request/Response frame protection. This attribute contains the 16 octet
+ *	STA Nonce followed by 16 octets of AP Nonce.
+ *
+ * @NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED: Indicates whether or not multicast
+ *	packets should be send out as unicast to all stations (flag attribute).
+ *
+ * @NL80211_ATTR_BSSID: The BSSID of the AP. Note that %NL80211_ATTR_MAC is also
+ *	used in various commands/events for specifying the BSSID.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI: Relative RSSI threshold by which
+ *	other BSSs has to be better or slightly worse than the current
+ *	connected BSS so that they get reported to user space.
+ *	This will give an opportunity to userspace to consider connecting to
+ *	other matching BSSs which have better or slightly worse RSSI than
+ *	the current connected BSS by using an offloaded operation to avoid
+ *	unnecessary wakeups.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST: When present the RSSI level for BSSs in
+ *	the specified band is to be adjusted before doing
+ *	%NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI based comparison to figure out
+ *	better BSSs. The attribute value is a packed structure
+ *	value as specified by &struct nl80211_bss_select_rssi_adjust.
+ *
+ * @NL80211_ATTR_TIMEOUT_REASON: The reason for which an operation timed out.
+ *	u32 attribute with an &enum nl80211_timeout_reason value. This is used,
+ *	e.g., with %NL80211_CMD_CONNECT event.
+ *
+ * @NL80211_ATTR_FILS_ERP_USERNAME: EAP Re-authentication Protocol (ERP)
+ *	username part of NAI used to refer keys rRK and rIK. This is used with
+ *	%NL80211_CMD_CONNECT.
+ *
+ * @NL80211_ATTR_FILS_ERP_REALM: EAP Re-authentication Protocol (ERP) realm part
+ *	of NAI specifying the domain name of the ER server. This is used with
+ *	%NL80211_CMD_CONNECT.
+ *
+ * @NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM: Unsigned 16-bit ERP next sequence number
+ *	to use in ERP messages. This is used in generating the FILS wrapped data
+ *	for FILS authentication and is used with %NL80211_CMD_CONNECT.
+ *
+ * @NL80211_ATTR_FILS_ERP_RRK: ERP re-authentication Root Key (rRK) for the
+ *	NAI specified by %NL80211_ATTR_FILS_ERP_USERNAME and
+ *	%NL80211_ATTR_FILS_ERP_REALM. This is used for generating rIK and rMSK
+ *	from successful FILS authentication and is used with
+ *	%NL80211_CMD_CONNECT.
+ *
+ * @NL80211_ATTR_FILS_CACHE_ID: A 2-octet identifier advertized by a FILS AP
+ *	identifying the scope of PMKSAs. This is used with
+ *	@NL80211_CMD_SET_PMKSA and @NL80211_CMD_DEL_PMKSA.
+ *
+ * @NL80211_ATTR_PMK: attribute for passing PMK key material. Used with
+ *	%NL80211_CMD_SET_PMKSA for the PMKSA identified by %NL80211_ATTR_PMKID.
+ *	For %NL80211_CMD_CONNECT and %NL80211_CMD_START_AP it is used to provide
+ *	PSK for offloading 4-way handshake for WPA/WPA2-PSK networks. For 802.1X
+ *	authentication it is used with %NL80211_CMD_SET_PMK. For offloaded FT
+ *	support this attribute specifies the PMK-R0 if NL80211_ATTR_PMKR0_NAME
+ *	is included as well.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_MULTI: flag attribute which user-space shall use to
+ *	indicate that it supports multiple active scheduled scan requests.
+ * @NL80211_ATTR_SCHED_SCAN_MAX_REQS: indicates maximum number of scheduled
+ *	scan request that may be active for the device (u32).
+ *
+ * @NL80211_ATTR_WANT_1X_4WAY_HS: flag attribute which user-space can include
+ *	in %NL80211_CMD_CONNECT to indicate that for 802.1X authentication it
+ *	wants to use the supported offload of the 4-way handshake.
+ * @NL80211_ATTR_PMKR0_NAME: PMK-R0 Name for offloaded FT.
+ * @NL80211_ATTR_PORT_AUTHORIZED: (reserved)
+ *
+ * @NL80211_ATTR_EXTERNAL_AUTH_ACTION: Identify the requested external
+ *     authentication operation (u32 attribute with an
+ *     &enum nl80211_external_auth_action value). This is used with the
+ *     %NL80211_CMD_EXTERNAL_AUTH request event.
+ * @NL80211_ATTR_EXTERNAL_AUTH_SUPPORT: Flag attribute indicating that the user
+ *	space supports external authentication. This attribute shall be used
+ *	with %NL80211_CMD_CONNECT and %NL80211_CMD_START_AP request. The driver
+ *	may offload authentication processing to user space if this capability
+ *	is indicated in the respective requests from the user space. (This flag
+ *	attribute deprecated for %NL80211_CMD_START_AP, use
+ *	%NL80211_ATTR_AP_SETTINGS_FLAGS)
+ *
+ * @NL80211_ATTR_NSS: Station's New/updated  RX_NSS value notified using this
+ *	u8 attribute. This is used with %NL80211_CMD_STA_OPMODE_CHANGED.
+ *
+ * @NL80211_ATTR_TXQ_STATS: TXQ statistics (nested attribute, see &enum
+ *      nl80211_txq_stats)
+ * @NL80211_ATTR_TXQ_LIMIT: Total packet limit for the TXQ queues for this phy.
+ *      The smaller of this and the memory limit is enforced.
+ * @NL80211_ATTR_TXQ_MEMORY_LIMIT: Total memory limit (in bytes) for the
+ *      TXQ queues for this phy. The smaller of this and the packet limit is
+ *      enforced.
+ * @NL80211_ATTR_TXQ_QUANTUM: TXQ scheduler quantum (bytes). Number of bytes
+ *      a flow is assigned on each round of the DRR scheduler.
+ * @NL80211_ATTR_HE_CAPABILITY: HE Capability information element (from
+ *	association request when used with NL80211_CMD_NEW_STATION). Can be set
+ *	only if %NL80211_STA_FLAG_WME is set.
+ *
+ * @NL80211_ATTR_FTM_RESPONDER: nested attribute which user-space can include
+ *	in %NL80211_CMD_START_AP or %NL80211_CMD_SET_BEACON for fine timing
+ *	measurement (FTM) responder functionality and containing parameters as
+ *	possible, see &enum nl80211_ftm_responder_attr
+ *
+ * @NL80211_ATTR_FTM_RESPONDER_STATS: Nested attribute with FTM responder
+ *	statistics, see &enum nl80211_ftm_responder_stats.
+ *
+ * @NL80211_ATTR_TIMEOUT: Timeout for the given operation in milliseconds (u32),
+ *	if the attribute is not given no timeout is requested. Note that 0 is an
+ *	invalid value.
+ *
+ * @NL80211_ATTR_PEER_MEASUREMENTS: peer measurements request (and result)
+ *	data, uses nested attributes specified in
+ *	&enum nl80211_peer_measurement_attrs.
+ *	This is also used for capability advertisement in the wiphy information,
+ *	with the appropriate sub-attributes.
+ *
+ * @NL80211_ATTR_AIRTIME_WEIGHT: Station's weight when scheduled by the airtime
+ *	scheduler.
+ *
+ * @NL80211_ATTR_STA_TX_POWER_SETTING: Transmit power setting type (u8) for
+ *	station associated with the AP. See &enum nl80211_tx_power_setting for
+ *	possible values.
+ * @NL80211_ATTR_STA_TX_POWER: Transmit power level (s16) in dBm units. This
+ *	allows to set Tx power for a station. If this attribute is not included,
+ *	the default per-interface tx power setting will be overriding. Driver
+ *	should be picking up the lowest tx power, either tx power per-interface
+ *	or per-station.
+ *
+ * @NL80211_ATTR_SAE_PASSWORD: attribute for passing SAE password material. It
+ *	is used with %NL80211_CMD_CONNECT to provide password for offloading
+ *	SAE authentication for WPA3-Personal networks.
+ *
+ * @NL80211_ATTR_TWT_RESPONDER: Enable target wait time responder support.
+ *
+ * @NL80211_ATTR_HE_OBSS_PD: nested attribute for OBSS Packet Detection
+ *	functionality.
+ *
+ * @NL80211_ATTR_WIPHY_EDMG_CHANNELS: bitmap that indicates the 2.16 GHz
+ *	channel(s) that are allowed to be used for EDMG transmissions.
+ *	Defined by IEEE P802.11ay/D4.0 section 9.4.2.251. (u8 attribute)
+ * @NL80211_ATTR_WIPHY_EDMG_BW_CONFIG: Channel BW Configuration subfield encodes
+ *	the allowed channel bandwidth configurations. (u8 attribute)
+ *	Defined by IEEE P802.11ay/D4.0 section 9.4.2.251, Table 13.
+ *
+ * @NL80211_ATTR_VLAN_ID: VLAN ID (1..4094) for the station and VLAN group key
+ *	(u16).
+ *
+ * @NL80211_ATTR_HE_BSS_COLOR: nested attribute for BSS Color Settings.
+ *
+ * @NL80211_ATTR_IFTYPE_AKM_SUITES: nested array attribute, with each entry
+ *	using attributes from &enum nl80211_iftype_akm_attributes. This
+ *	attribute is sent in a response to %NL80211_CMD_GET_WIPHY indicating
+ *	supported AKM suites capability per interface. AKMs advertised in
+ *	%NL80211_ATTR_AKM_SUITES are default capabilities if AKM suites not
+ *	advertised for a specific interface type.
+ *
+ * @NL80211_ATTR_TID_CONFIG: TID specific configuration in a
+ *	nested attribute with &enum nl80211_tid_config_attr sub-attributes;
+ *	on output (in wiphy attributes) it contains only the feature sub-
+ *	attributes.
+ *
+ * @NL80211_ATTR_CONTROL_PORT_NO_PREAUTH: disable preauth frame rx on control
+ *	port in order to forward/receive them as ordinary data frames.
+ *
+ * @NL80211_ATTR_PMK_LIFETIME: Maximum lifetime for PMKSA in seconds (u32,
+ *	dot11RSNAConfigPMKReauthThreshold; 0 is not a valid value).
+ *	An optional parameter configured through %NL80211_CMD_SET_PMKSA.
+ *	Drivers that trigger roaming need to know the lifetime of the
+ *	configured PMKSA for triggering the full vs. PMKSA caching based
+ *	authentication. This timeout helps authentication methods like SAE,
+ *	where PMK gets updated only by going through a full (new SAE)
+ *	authentication instead of getting updated during an association for EAP
+ *	authentication. No new full authentication within the PMK expiry shall
+ *	result in a disassociation at the end of the lifetime.
+ *
+ * @NL80211_ATTR_PMK_REAUTH_THRESHOLD: Reauthentication threshold time, in
+ *	terms of percentage of %NL80211_ATTR_PMK_LIFETIME
+ *	(u8, dot11RSNAConfigPMKReauthThreshold, 1..100). This is an optional
+ *	parameter configured through %NL80211_CMD_SET_PMKSA. Requests the
+ *	driver to trigger a full authentication roam (without PMKSA caching)
+ *	after the reauthentication threshold time, but before the PMK lifetime
+ *	has expired.
+ *
+ *	Authentication methods like SAE need to be able to generate a new PMKSA
+ *	entry without having to force a disconnection after the PMK timeout. If
+ *	no roaming occurs between the reauth threshold and PMK expiration,
+ *	disassociation is still forced.
+ * @NL80211_ATTR_RECEIVE_MULTICAST: multicast flag for the
+ *	%NL80211_CMD_REGISTER_FRAME command, see the description there.
+ * @NL80211_ATTR_WIPHY_FREQ_OFFSET: offset of the associated
+ *	%NL80211_ATTR_WIPHY_FREQ in positive KHz. Only valid when supplied with
+ *	an %NL80211_ATTR_WIPHY_FREQ_OFFSET.
+ * @NL80211_ATTR_CENTER_FREQ1_OFFSET: Center frequency offset in KHz for the
+ *	first channel segment specified in %NL80211_ATTR_CENTER_FREQ1.
+ * @NL80211_ATTR_SCAN_FREQ_KHZ: nested attribute with KHz frequencies
+ *
+ * @NL80211_ATTR_HE_6GHZ_CAPABILITY: HE 6 GHz Band Capability element (from
+ *	association request when used with NL80211_CMD_NEW_STATION).
+ *
+ * @NL80211_ATTR_FILS_DISCOVERY: Optional parameter to configure FILS
+ *	discovery. It is a nested attribute, see
+ *	&enum nl80211_fils_discovery_attributes.
+ *
+ * @NL80211_ATTR_UNSOL_BCAST_PROBE_RESP: Optional parameter to configure
+ *	unsolicited broadcast probe response. It is a nested attribute, see
+ *	&enum nl80211_unsol_bcast_probe_resp_attributes.
+ *
+ * @NL80211_ATTR_S1G_CAPABILITY: S1G Capability information element (from
+ *	association request when used with NL80211_CMD_NEW_STATION)
+ * @NL80211_ATTR_S1G_CAPABILITY_MASK: S1G Capability Information element
+ *	override mask. Used with NL80211_ATTR_S1G_CAPABILITY in
+ *	NL80211_CMD_ASSOCIATE or NL80211_CMD_CONNECT.
+ *
+ * @NL80211_ATTR_SAE_PWE: Indicates the mechanism(s) allowed for SAE PWE
+ *	derivation in WPA3-Personal networks which are using SAE authentication.
+ *	This is a u8 attribute that encapsulates one of the values from
+ *	&enum nl80211_sae_pwe_mechanism.
+ *
+ * @NL80211_ATTR_SAR_SPEC: SAR power limitation specification when
+ *	used with %NL80211_CMD_SET_SAR_SPECS. The message contains fields
+ *	of %nl80211_sar_attrs which specifies the sar type and related
+ *	sar specs. Sar specs contains array of %nl80211_sar_specs_attrs.
+ *
+ * @NL80211_ATTR_RECONNECT_REQUESTED: flag attribute, used with deauth and
+ *	disassoc events to indicate that an immediate reconnect to the AP
+ *	is desired.
+ *
+ * @NL80211_ATTR_OBSS_COLOR_BITMAP: bitmap of the u64 BSS colors for the
+ *	%NL80211_CMD_OBSS_COLOR_COLLISION event.
+ *
+ * @NL80211_ATTR_COLOR_CHANGE_COUNT: u8 attribute specifying the number of TBTT's
+ *	until the color switch event.
+ * @NL80211_ATTR_COLOR_CHANGE_COLOR: u8 attribute specifying the color that we are
+ *	switching to
+ * @NL80211_ATTR_COLOR_CHANGE_ELEMS: Nested set of attributes containing the IE
+ *	information for the time while performing a color switch.
+ *
+ * @NL80211_ATTR_MBSSID_CONFIG: Nested attribute for multiple BSSID
+ *	advertisements (MBSSID) parameters in AP mode.
+ *	Kernel uses this attribute to indicate the driver's support for MBSSID
+ *	and enhanced multi-BSSID advertisements (EMA AP) to the userspace.
+ *	Userspace should use this attribute to configure per interface MBSSID
+ *	parameters.
+ *	See &enum nl80211_mbssid_config_attributes for details.
+ *
+ * @NL80211_ATTR_MBSSID_ELEMS: Nested parameter to pass multiple BSSID elements.
+ *	Mandatory parameter for the transmitting interface to enable MBSSID.
+ *	Optional for the non-transmitting interfaces.
+ *
+ * @NL80211_ATTR_RADAR_BACKGROUND: Configure dedicated offchannel chain
+ *	available for radar/CAC detection on some hw. This chain can't be used
+ *	to transmit or receive frames and it is bounded to a running wdev.
+ *	Background radar/CAC detection allows to avoid the CAC downtime
+ *	switching on a different channel during CAC detection on the selected
+ *	radar channel.
+ *
+ * @NL80211_ATTR_AP_SETTINGS_FLAGS: u32 attribute contains ap settings flags,
+ *	enumerated in &enum nl80211_ap_settings_flags. This attribute shall be
+ *	used with %NL80211_CMD_START_AP request.
+ *
+ * @NL80211_ATTR_EHT_CAPABILITY: EHT Capability information element (from
+ *	association request when used with NL80211_CMD_NEW_STATION). Can be set
+ *	only if %NL80211_STA_FLAG_WME is set.
+ *
+ * @NL80211_ATTR_MLO_LINK_ID: A (u8) link ID for use with MLO, to be used with
+ *	various commands that need a link ID to operate.
+ * @NL80211_ATTR_MLO_LINKS: A nested array of links, each containing some
+ *	per-link information and a link ID.
+ * @NL80211_ATTR_MLD_ADDR: An MLD address, used with various commands such as
+ *	authenticate/associate.
+ *
+ * @NL80211_ATTR_MLO_SUPPORT: Flag attribute to indicate user space supports MLO
+ *	connection. Used with %NL80211_CMD_CONNECT. If this attribute is not
+ *	included in NL80211_CMD_CONNECT drivers must not perform MLO connection.
+ *
+ * @NL80211_ATTR_MAX_NUM_AKM_SUITES: U16 attribute. Indicates maximum number of
+ *	AKM suites allowed for %NL80211_CMD_CONNECT, %NL80211_CMD_ASSOCIATE and
+ *	%NL80211_CMD_START_AP in %NL80211_CMD_GET_WIPHY response. If this
+ *	attribute is not present userspace shall consider maximum number of AKM
+ *	suites allowed as %NL80211_MAX_NR_AKM_SUITES which is the legacy maximum
+ *	number prior to the introduction of this attribute.
+ *
+ * @NL80211_ATTR_EML_CAPABILITY: EML Capability information (u16)
+ * @NL80211_ATTR_MLD_CAPA_AND_OPS: MLD Capabilities and Operations (u16)
+ *
+ * @NL80211_ATTR_TX_HW_TIMESTAMP: Hardware timestamp for TX operation in
+ *	nanoseconds (u64). This is the device clock timestamp so it will
+ *	probably reset when the device is stopped or the firmware is reset.
+ *	When used with %NL80211_CMD_FRAME_TX_STATUS, indicates the frame TX
+ *	timestamp. When used with %NL80211_CMD_FRAME RX notification, indicates
+ *	the ack TX timestamp.
+ * @NL80211_ATTR_RX_HW_TIMESTAMP: Hardware timestamp for RX operation in
+ *	nanoseconds (u64). This is the device clock timestamp so it will
+ *	probably reset when the device is stopped or the firmware is reset.
+ *	When used with %NL80211_CMD_FRAME_TX_STATUS, indicates the ack RX
+ *	timestamp. When used with %NL80211_CMD_FRAME RX notification, indicates
+ *	the incoming frame RX timestamp.
+ * @NL80211_ATTR_TD_BITMAP: Transition Disable bitmap, for subsequent
+ *	(re)associations.
+ *
+ * @NL80211_ATTR_PUNCT_BITMAP: (u32) Preamble puncturing bitmap, lowest
+ *	bit corresponds to the lowest 20 MHz channel. Each bit set to 1
+ *	indicates that the sub-channel is punctured. Higher 16 bits are
+ *	reserved.
+ *
+ * @NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS: Maximum number of peers that HW
+ *	timestamping can be enabled for concurrently (u16), a wiphy attribute.
+ *	A value of 0xffff indicates setting for all peers (i.e. not specifying
+ *	an address with %NL80211_CMD_SET_HW_TIMESTAMP) is supported.
+ * @NL80211_ATTR_HW_TIMESTAMP_ENABLED: Indicates whether HW timestamping should
+ *	be enabled or not (flag attribute).
+ *
+ * @NL80211_ATTR_EMA_RNR_ELEMS: Optional nested attribute for
+ *	reduced neighbor report (RNR) elements. This attribute can be used
+ *	only when NL80211_MBSSID_CONFIG_ATTR_EMA is enabled.
+ *	Userspace is responsible for splitting the RNR into multiple
+ *	elements such that each element excludes the non-transmitting
+ *	profiles already included in the MBSSID element
+ *	(%NL80211_ATTR_MBSSID_ELEMS) at the same index. Each EMA beacon
+ *	will be generated by adding MBSSID and RNR elements at the same
+ *	index. If the userspace includes more RNR elements than number of
+ *	MBSSID elements then these will be added in every EMA beacon.
+ *
+ * @NUM_NL80211_ATTR: total number of nl80211_attrs available
+ * @NL80211_ATTR_MAX: highest attribute number currently defined
+ * @__NL80211_ATTR_AFTER_LAST: internal use
+ */
+enum nl80211_attrs {
+/* don't change the order or add anything between, this is ABI! */
+	NL80211_ATTR_UNSPEC,
+
+	NL80211_ATTR_WIPHY,
+	NL80211_ATTR_WIPHY_NAME,
+
+	NL80211_ATTR_IFINDEX,
+	NL80211_ATTR_IFNAME,
+	NL80211_ATTR_IFTYPE,
+
+	NL80211_ATTR_MAC,
+
+	NL80211_ATTR_KEY_DATA,
+	NL80211_ATTR_KEY_IDX,
+	NL80211_ATTR_KEY_CIPHER,
+	NL80211_ATTR_KEY_SEQ,
+	NL80211_ATTR_KEY_DEFAULT,
+
+	NL80211_ATTR_BEACON_INTERVAL,
+	NL80211_ATTR_DTIM_PERIOD,
+	NL80211_ATTR_BEACON_HEAD,
+	NL80211_ATTR_BEACON_TAIL,
+
+	NL80211_ATTR_STA_AID,
+	NL80211_ATTR_STA_FLAGS,
+	NL80211_ATTR_STA_LISTEN_INTERVAL,
+	NL80211_ATTR_STA_SUPPORTED_RATES,
+	NL80211_ATTR_STA_VLAN,
+	NL80211_ATTR_STA_INFO,
+
+	NL80211_ATTR_WIPHY_BANDS,
+
+	NL80211_ATTR_MNTR_FLAGS,
+
+	NL80211_ATTR_MESH_ID,
+	NL80211_ATTR_STA_PLINK_ACTION,
+	NL80211_ATTR_MPATH_NEXT_HOP,
+	NL80211_ATTR_MPATH_INFO,
+
+	NL80211_ATTR_BSS_CTS_PROT,
+	NL80211_ATTR_BSS_SHORT_PREAMBLE,
+	NL80211_ATTR_BSS_SHORT_SLOT_TIME,
+
+	NL80211_ATTR_HT_CAPABILITY,
+
+	NL80211_ATTR_SUPPORTED_IFTYPES,
+
+	NL80211_ATTR_REG_ALPHA2,
+	NL80211_ATTR_REG_RULES,
+
+	NL80211_ATTR_MESH_CONFIG,
+
+	NL80211_ATTR_BSS_BASIC_RATES,
+
+	NL80211_ATTR_WIPHY_TXQ_PARAMS,
+	NL80211_ATTR_WIPHY_FREQ,
+	NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+
+	NL80211_ATTR_KEY_DEFAULT_MGMT,
+
+	NL80211_ATTR_MGMT_SUBTYPE,
+	NL80211_ATTR_IE,
+
+	NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
+
+	NL80211_ATTR_SCAN_FREQUENCIES,
+	NL80211_ATTR_SCAN_SSIDS,
+	NL80211_ATTR_GENERATION, /* replaces old SCAN_GENERATION */
+	NL80211_ATTR_BSS,
+
+	NL80211_ATTR_REG_INITIATOR,
+	NL80211_ATTR_REG_TYPE,
+
+	NL80211_ATTR_SUPPORTED_COMMANDS,
+
+	NL80211_ATTR_FRAME,
+	NL80211_ATTR_SSID,
+	NL80211_ATTR_AUTH_TYPE,
+	NL80211_ATTR_REASON_CODE,
+
+	NL80211_ATTR_KEY_TYPE,
+
+	NL80211_ATTR_MAX_SCAN_IE_LEN,
+	NL80211_ATTR_CIPHER_SUITES,
+
+	NL80211_ATTR_FREQ_BEFORE,
+	NL80211_ATTR_FREQ_AFTER,
+
+	NL80211_ATTR_FREQ_FIXED,
+
+
+	NL80211_ATTR_WIPHY_RETRY_SHORT,
+	NL80211_ATTR_WIPHY_RETRY_LONG,
+	NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
+	NL80211_ATTR_WIPHY_RTS_THRESHOLD,
+
+	NL80211_ATTR_TIMED_OUT,
+
+	NL80211_ATTR_USE_MFP,
+
+	NL80211_ATTR_STA_FLAGS2,
+
+	NL80211_ATTR_CONTROL_PORT,
+
+	NL80211_ATTR_TESTDATA,
+
+	NL80211_ATTR_PRIVACY,
+
+	NL80211_ATTR_DISCONNECTED_BY_AP,
+	NL80211_ATTR_STATUS_CODE,
+
+	NL80211_ATTR_CIPHER_SUITES_PAIRWISE,
+	NL80211_ATTR_CIPHER_SUITE_GROUP,
+	NL80211_ATTR_WPA_VERSIONS,
+	NL80211_ATTR_AKM_SUITES,
+
+	NL80211_ATTR_REQ_IE,
+	NL80211_ATTR_RESP_IE,
+
+	NL80211_ATTR_PREV_BSSID,
+
+	NL80211_ATTR_KEY,
+	NL80211_ATTR_KEYS,
+
+	NL80211_ATTR_PID,
+
+	NL80211_ATTR_4ADDR,
+
+	NL80211_ATTR_SURVEY_INFO,
+
+	NL80211_ATTR_PMKID,
+	NL80211_ATTR_MAX_NUM_PMKIDS,
+
+	NL80211_ATTR_DURATION,
+
+	NL80211_ATTR_COOKIE,
+
+	NL80211_ATTR_WIPHY_COVERAGE_CLASS,
+
+	NL80211_ATTR_TX_RATES,
+
+	NL80211_ATTR_FRAME_MATCH,
+
+	NL80211_ATTR_ACK,
+
+	NL80211_ATTR_PS_STATE,
+
+	NL80211_ATTR_CQM,
+
+	NL80211_ATTR_LOCAL_STATE_CHANGE,
+
+	NL80211_ATTR_AP_ISOLATE,
+
+	NL80211_ATTR_WIPHY_TX_POWER_SETTING,
+	NL80211_ATTR_WIPHY_TX_POWER_LEVEL,
+
+	NL80211_ATTR_TX_FRAME_TYPES,
+	NL80211_ATTR_RX_FRAME_TYPES,
+	NL80211_ATTR_FRAME_TYPE,
+
+	NL80211_ATTR_CONTROL_PORT_ETHERTYPE,
+	NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT,
+
+	NL80211_ATTR_SUPPORT_IBSS_RSN,
+
+	NL80211_ATTR_WIPHY_ANTENNA_TX,
+	NL80211_ATTR_WIPHY_ANTENNA_RX,
+
+	NL80211_ATTR_MCAST_RATE,
+
+	NL80211_ATTR_OFFCHANNEL_TX_OK,
+
+	NL80211_ATTR_BSS_HT_OPMODE,
+
+	NL80211_ATTR_KEY_DEFAULT_TYPES,
+
+	NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
+
+	NL80211_ATTR_MESH_SETUP,
+
+	NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
+	NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
+
+	NL80211_ATTR_SUPPORT_MESH_AUTH,
+	NL80211_ATTR_STA_PLINK_STATE,
+
+	NL80211_ATTR_WOWLAN_TRIGGERS,
+	NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED,
+
+	NL80211_ATTR_SCHED_SCAN_INTERVAL,
+
+	NL80211_ATTR_INTERFACE_COMBINATIONS,
+	NL80211_ATTR_SOFTWARE_IFTYPES,
+
+	NL80211_ATTR_REKEY_DATA,
+
+	NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS,
+	NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
+
+	NL80211_ATTR_SCAN_SUPP_RATES,
+
+	NL80211_ATTR_HIDDEN_SSID,
+
+	NL80211_ATTR_IE_PROBE_RESP,
+	NL80211_ATTR_IE_ASSOC_RESP,
+
+	NL80211_ATTR_STA_WME,
+	NL80211_ATTR_SUPPORT_AP_UAPSD,
+
+	NL80211_ATTR_ROAM_SUPPORT,
+
+	NL80211_ATTR_SCHED_SCAN_MATCH,
+	NL80211_ATTR_MAX_MATCH_SETS,
+
+	NL80211_ATTR_PMKSA_CANDIDATE,
+
+	NL80211_ATTR_TX_NO_CCK_RATE,
+
+	NL80211_ATTR_TDLS_ACTION,
+	NL80211_ATTR_TDLS_DIALOG_TOKEN,
+	NL80211_ATTR_TDLS_OPERATION,
+	NL80211_ATTR_TDLS_SUPPORT,
+	NL80211_ATTR_TDLS_EXTERNAL_SETUP,
+
+	NL80211_ATTR_DEVICE_AP_SME,
+
+	NL80211_ATTR_DONT_WAIT_FOR_ACK,
+
+	NL80211_ATTR_FEATURE_FLAGS,
+
+	NL80211_ATTR_PROBE_RESP_OFFLOAD,
+
+	NL80211_ATTR_PROBE_RESP,
+
+	NL80211_ATTR_DFS_REGION,
+
+	NL80211_ATTR_DISABLE_HT,
+	NL80211_ATTR_HT_CAPABILITY_MASK,
+
+	NL80211_ATTR_NOACK_MAP,
+
+	NL80211_ATTR_INACTIVITY_TIMEOUT,
+
+	NL80211_ATTR_RX_SIGNAL_DBM,
+
+	NL80211_ATTR_BG_SCAN_PERIOD,
+
+	NL80211_ATTR_WDEV,
+
+	NL80211_ATTR_USER_REG_HINT_TYPE,
+
+	NL80211_ATTR_CONN_FAILED_REASON,
+
+	NL80211_ATTR_AUTH_DATA,
+
+	NL80211_ATTR_VHT_CAPABILITY,
+
+	NL80211_ATTR_SCAN_FLAGS,
+
+	NL80211_ATTR_CHANNEL_WIDTH,
+	NL80211_ATTR_CENTER_FREQ1,
+	NL80211_ATTR_CENTER_FREQ2,
+
+	NL80211_ATTR_P2P_CTWINDOW,
+	NL80211_ATTR_P2P_OPPPS,
+
+	NL80211_ATTR_LOCAL_MESH_POWER_MODE,
+
+	NL80211_ATTR_ACL_POLICY,
+
+	NL80211_ATTR_MAC_ADDRS,
+
+	NL80211_ATTR_MAC_ACL_MAX,
+
+	NL80211_ATTR_RADAR_EVENT,
+
+	NL80211_ATTR_EXT_CAPA,
+	NL80211_ATTR_EXT_CAPA_MASK,
+
+	NL80211_ATTR_STA_CAPABILITY,
+	NL80211_ATTR_STA_EXT_CAPABILITY,
+
+	NL80211_ATTR_PROTOCOL_FEATURES,
+	NL80211_ATTR_SPLIT_WIPHY_DUMP,
+
+	NL80211_ATTR_DISABLE_VHT,
+	NL80211_ATTR_VHT_CAPABILITY_MASK,
+
+	NL80211_ATTR_MDID,
+	NL80211_ATTR_IE_RIC,
+
+	NL80211_ATTR_CRIT_PROT_ID,
+	NL80211_ATTR_MAX_CRIT_PROT_DURATION,
+
+	NL80211_ATTR_PEER_AID,
+
+	NL80211_ATTR_COALESCE_RULE,
+
+	NL80211_ATTR_CH_SWITCH_COUNT,
+	NL80211_ATTR_CH_SWITCH_BLOCK_TX,
+	NL80211_ATTR_CSA_IES,
+	NL80211_ATTR_CNTDWN_OFFS_BEACON,
+	NL80211_ATTR_CNTDWN_OFFS_PRESP,
+
+	NL80211_ATTR_RXMGMT_FLAGS,
+
+	NL80211_ATTR_STA_SUPPORTED_CHANNELS,
+
+	NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES,
+
+	NL80211_ATTR_HANDLE_DFS,
+
+	NL80211_ATTR_SUPPORT_5_MHZ,
+	NL80211_ATTR_SUPPORT_10_MHZ,
+
+	NL80211_ATTR_OPMODE_NOTIF,
+
+	NL80211_ATTR_VENDOR_ID,
+	NL80211_ATTR_VENDOR_SUBCMD,
+	NL80211_ATTR_VENDOR_DATA,
+	NL80211_ATTR_VENDOR_EVENTS,
+
+	NL80211_ATTR_QOS_MAP,
+
+	NL80211_ATTR_MAC_HINT,
+	NL80211_ATTR_WIPHY_FREQ_HINT,
+
+	NL80211_ATTR_MAX_AP_ASSOC_STA,
+
+	NL80211_ATTR_TDLS_PEER_CAPABILITY,
+
+	NL80211_ATTR_SOCKET_OWNER,
+
+	NL80211_ATTR_CSA_C_OFFSETS_TX,
+	NL80211_ATTR_MAX_CSA_COUNTERS,
+
+	NL80211_ATTR_TDLS_INITIATOR,
+
+	NL80211_ATTR_USE_RRM,
+
+	NL80211_ATTR_WIPHY_DYN_ACK,
+
+	NL80211_ATTR_TSID,
+	NL80211_ATTR_USER_PRIO,
+	NL80211_ATTR_ADMITTED_TIME,
+
+	NL80211_ATTR_SMPS_MODE,
+
+	NL80211_ATTR_OPER_CLASS,
+
+	NL80211_ATTR_MAC_MASK,
+
+	NL80211_ATTR_WIPHY_SELF_MANAGED_REG,
+
+	NL80211_ATTR_EXT_FEATURES,
+
+	NL80211_ATTR_SURVEY_RADIO_STATS,
+
+	NL80211_ATTR_NETNS_FD,
+
+	NL80211_ATTR_SCHED_SCAN_DELAY,
+
+	NL80211_ATTR_REG_INDOOR,
+
+	NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS,
+	NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL,
+	NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS,
+	NL80211_ATTR_SCHED_SCAN_PLANS,
+
+	NL80211_ATTR_PBSS,
+
+	NL80211_ATTR_BSS_SELECT,
+
+	NL80211_ATTR_STA_SUPPORT_P2P_PS,
+
+	NL80211_ATTR_PAD,
+
+	NL80211_ATTR_IFTYPE_EXT_CAPA,
+
+	NL80211_ATTR_MU_MIMO_GROUP_DATA,
+	NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR,
+
+	NL80211_ATTR_SCAN_START_TIME_TSF,
+	NL80211_ATTR_SCAN_START_TIME_TSF_BSSID,
+	NL80211_ATTR_MEASUREMENT_DURATION,
+	NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY,
+
+	NL80211_ATTR_MESH_PEER_AID,
+
+	NL80211_ATTR_NAN_MASTER_PREF,
+	NL80211_ATTR_BANDS,
+	NL80211_ATTR_NAN_FUNC,
+	NL80211_ATTR_NAN_MATCH,
+
+	NL80211_ATTR_FILS_KEK,
+	NL80211_ATTR_FILS_NONCES,
+
+	NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED,
+
+	NL80211_ATTR_BSSID,
+
+	NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI,
+	NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST,
+
+	NL80211_ATTR_TIMEOUT_REASON,
+
+	NL80211_ATTR_FILS_ERP_USERNAME,
+	NL80211_ATTR_FILS_ERP_REALM,
+	NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM,
+	NL80211_ATTR_FILS_ERP_RRK,
+	NL80211_ATTR_FILS_CACHE_ID,
+
+	NL80211_ATTR_PMK,
+
+	NL80211_ATTR_SCHED_SCAN_MULTI,
+	NL80211_ATTR_SCHED_SCAN_MAX_REQS,
+
+	NL80211_ATTR_WANT_1X_4WAY_HS,
+	NL80211_ATTR_PMKR0_NAME,
+	NL80211_ATTR_PORT_AUTHORIZED,
+
+	NL80211_ATTR_EXTERNAL_AUTH_ACTION,
+	NL80211_ATTR_EXTERNAL_AUTH_SUPPORT,
+
+	NL80211_ATTR_NSS,
+	NL80211_ATTR_ACK_SIGNAL,
+
+	NL80211_ATTR_CONTROL_PORT_OVER_NL80211,
+
+	NL80211_ATTR_TXQ_STATS,
+	NL80211_ATTR_TXQ_LIMIT,
+	NL80211_ATTR_TXQ_MEMORY_LIMIT,
+	NL80211_ATTR_TXQ_QUANTUM,
+
+	NL80211_ATTR_HE_CAPABILITY,
+
+	NL80211_ATTR_FTM_RESPONDER,
+
+	NL80211_ATTR_FTM_RESPONDER_STATS,
+
+	NL80211_ATTR_TIMEOUT,
+
+	NL80211_ATTR_PEER_MEASUREMENTS,
+
+	NL80211_ATTR_AIRTIME_WEIGHT,
+	NL80211_ATTR_STA_TX_POWER_SETTING,
+	NL80211_ATTR_STA_TX_POWER,
+
+	NL80211_ATTR_SAE_PASSWORD,
+
+	NL80211_ATTR_TWT_RESPONDER,
+
+	NL80211_ATTR_HE_OBSS_PD,
+
+	NL80211_ATTR_WIPHY_EDMG_CHANNELS,
+	NL80211_ATTR_WIPHY_EDMG_BW_CONFIG,
+
+	NL80211_ATTR_VLAN_ID,
+
+	NL80211_ATTR_HE_BSS_COLOR,
+
+	NL80211_ATTR_IFTYPE_AKM_SUITES,
+
+	NL80211_ATTR_TID_CONFIG,
+
+	NL80211_ATTR_CONTROL_PORT_NO_PREAUTH,
+
+	NL80211_ATTR_PMK_LIFETIME,
+	NL80211_ATTR_PMK_REAUTH_THRESHOLD,
+
+	NL80211_ATTR_RECEIVE_MULTICAST,
+	NL80211_ATTR_WIPHY_FREQ_OFFSET,
+	NL80211_ATTR_CENTER_FREQ1_OFFSET,
+	NL80211_ATTR_SCAN_FREQ_KHZ,
+
+	NL80211_ATTR_HE_6GHZ_CAPABILITY,
+
+	NL80211_ATTR_FILS_DISCOVERY,
+
+	NL80211_ATTR_UNSOL_BCAST_PROBE_RESP,
+
+	NL80211_ATTR_S1G_CAPABILITY,
+	NL80211_ATTR_S1G_CAPABILITY_MASK,
+
+	NL80211_ATTR_SAE_PWE,
+
+	NL80211_ATTR_RECONNECT_REQUESTED,
+
+	NL80211_ATTR_SAR_SPEC,
+
+	NL80211_ATTR_DISABLE_HE,
+
+	NL80211_ATTR_OBSS_COLOR_BITMAP,
+
+	NL80211_ATTR_COLOR_CHANGE_COUNT,
+	NL80211_ATTR_COLOR_CHANGE_COLOR,
+	NL80211_ATTR_COLOR_CHANGE_ELEMS,
+
+	NL80211_ATTR_MBSSID_CONFIG,
+	NL80211_ATTR_MBSSID_ELEMS,
+
+	NL80211_ATTR_RADAR_BACKGROUND,
+
+	NL80211_ATTR_AP_SETTINGS_FLAGS,
+
+	NL80211_ATTR_EHT_CAPABILITY,
+
+	NL80211_ATTR_DISABLE_EHT,
+
+	NL80211_ATTR_MLO_LINKS,
+	NL80211_ATTR_MLO_LINK_ID,
+	NL80211_ATTR_MLD_ADDR,
+
+	NL80211_ATTR_MLO_SUPPORT,
+
+	NL80211_ATTR_MAX_NUM_AKM_SUITES,
+
+	NL80211_ATTR_EML_CAPABILITY,
+	NL80211_ATTR_MLD_CAPA_AND_OPS,
+
+	NL80211_ATTR_TX_HW_TIMESTAMP,
+	NL80211_ATTR_RX_HW_TIMESTAMP,
+	NL80211_ATTR_TD_BITMAP,
+
+	NL80211_ATTR_PUNCT_BITMAP,
+
+	NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS,
+	NL80211_ATTR_HW_TIMESTAMP_ENABLED,
+
+	NL80211_ATTR_EMA_RNR_ELEMS,
+
+	/* add attributes here, update the policy in nl80211.c */
+
+	__NL80211_ATTR_AFTER_LAST,
+	NUM_NL80211_ATTR = __NL80211_ATTR_AFTER_LAST,
+	NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1
+};
+
+/* source-level API compatibility */
+#define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION
+#define	NL80211_ATTR_MESH_PARAMS NL80211_ATTR_MESH_CONFIG
+#define NL80211_ATTR_IFACE_SOCKET_OWNER NL80211_ATTR_SOCKET_OWNER
+#define NL80211_ATTR_SAE_DATA NL80211_ATTR_AUTH_DATA
+#define NL80211_ATTR_CSA_C_OFF_BEACON NL80211_ATTR_CNTDWN_OFFS_BEACON
+#define NL80211_ATTR_CSA_C_OFF_PRESP NL80211_ATTR_CNTDWN_OFFS_PRESP
+
+/*
+ * Allow user space programs to use #ifdef on new attributes by defining them
+ * here
+ */
+#define NL80211_CMD_CONNECT NL80211_CMD_CONNECT
+#define NL80211_ATTR_HT_CAPABILITY NL80211_ATTR_HT_CAPABILITY
+#define NL80211_ATTR_BSS_BASIC_RATES NL80211_ATTR_BSS_BASIC_RATES
+#define NL80211_ATTR_WIPHY_TXQ_PARAMS NL80211_ATTR_WIPHY_TXQ_PARAMS
+#define NL80211_ATTR_WIPHY_FREQ NL80211_ATTR_WIPHY_FREQ
+#define NL80211_ATTR_WIPHY_CHANNEL_TYPE NL80211_ATTR_WIPHY_CHANNEL_TYPE
+#define NL80211_ATTR_MGMT_SUBTYPE NL80211_ATTR_MGMT_SUBTYPE
+#define NL80211_ATTR_IE NL80211_ATTR_IE
+#define NL80211_ATTR_REG_INITIATOR NL80211_ATTR_REG_INITIATOR
+#define NL80211_ATTR_REG_TYPE NL80211_ATTR_REG_TYPE
+#define NL80211_ATTR_FRAME NL80211_ATTR_FRAME
+#define NL80211_ATTR_SSID NL80211_ATTR_SSID
+#define NL80211_ATTR_AUTH_TYPE NL80211_ATTR_AUTH_TYPE
+#define NL80211_ATTR_REASON_CODE NL80211_ATTR_REASON_CODE
+#define NL80211_ATTR_CIPHER_SUITES_PAIRWISE NL80211_ATTR_CIPHER_SUITES_PAIRWISE
+#define NL80211_ATTR_CIPHER_SUITE_GROUP NL80211_ATTR_CIPHER_SUITE_GROUP
+#define NL80211_ATTR_WPA_VERSIONS NL80211_ATTR_WPA_VERSIONS
+#define NL80211_ATTR_AKM_SUITES NL80211_ATTR_AKM_SUITES
+#define NL80211_ATTR_KEY NL80211_ATTR_KEY
+#define NL80211_ATTR_KEYS NL80211_ATTR_KEYS
+#define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS
+
+#define NL80211_WIPHY_NAME_MAXLEN		64
+
+#define NL80211_MAX_SUPP_RATES			32
+#define NL80211_MAX_SUPP_HT_RATES		77
+#define NL80211_MAX_SUPP_REG_RULES		128
+#define NL80211_TKIP_DATA_OFFSET_ENCR_KEY	0
+#define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY	16
+#define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY	24
+#define NL80211_HT_CAPABILITY_LEN		26
+#define NL80211_VHT_CAPABILITY_LEN		12
+#define NL80211_HE_MIN_CAPABILITY_LEN           16
+#define NL80211_HE_MAX_CAPABILITY_LEN           54
+#define NL80211_MAX_NR_CIPHER_SUITES		5
+
+/*
+ * NL80211_MAX_NR_AKM_SUITES is obsolete when %NL80211_ATTR_MAX_NUM_AKM_SUITES
+ * present in %NL80211_CMD_GET_WIPHY response.
+ */
+#define NL80211_MAX_NR_AKM_SUITES		2
+#define NL80211_EHT_MIN_CAPABILITY_LEN          13
+#define NL80211_EHT_MAX_CAPABILITY_LEN          51
+
+#define NL80211_MIN_REMAIN_ON_CHANNEL_TIME	10
+
+/* default RSSI threshold for scan results if none specified. */
+#define NL80211_SCAN_RSSI_THOLD_OFF		-300
+
+#define NL80211_CQM_TXE_MAX_INTVL		1800
+
+/**
+ * enum nl80211_iftype - (virtual) interface types
+ *
+ * @NL80211_IFTYPE_UNSPECIFIED: unspecified type, driver decides
+ * @NL80211_IFTYPE_ADHOC: independent BSS member
+ * @NL80211_IFTYPE_STATION: managed BSS member
+ * @NL80211_IFTYPE_AP: access point
+ * @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points; VLAN interfaces
+ *	are a bit special in that they must always be tied to a pre-existing
+ *	AP type interface.
+ * @NL80211_IFTYPE_WDS: wireless distribution interface
+ * @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames
+ * @NL80211_IFTYPE_MESH_POINT: mesh point
+ * @NL80211_IFTYPE_P2P_CLIENT: P2P client
+ * @NL80211_IFTYPE_P2P_GO: P2P group owner
+ * @NL80211_IFTYPE_P2P_DEVICE: P2P device interface type, this is not a netdev
+ *	and therefore can't be created in the normal ways, use the
+ *	%NL80211_CMD_START_P2P_DEVICE and %NL80211_CMD_STOP_P2P_DEVICE
+ *	commands to create and destroy one
+ * @NL80211_IFTYPE_OCB: Outside Context of a BSS
+ *	This mode corresponds to the MIB variable dot11OCBActivated=true
+ * @NL80211_IFTYPE_NAN: NAN device interface type (not a netdev)
+ * @NL80211_IFTYPE_MAX: highest interface type number currently defined
+ * @NUM_NL80211_IFTYPES: number of defined interface types
+ *
+ * These values are used with the %NL80211_ATTR_IFTYPE
+ * to set the type of an interface.
+ *
+ */
+enum nl80211_iftype {
+	NL80211_IFTYPE_UNSPECIFIED,
+	NL80211_IFTYPE_ADHOC,
+	NL80211_IFTYPE_STATION,
+	NL80211_IFTYPE_AP,
+	NL80211_IFTYPE_AP_VLAN,
+	NL80211_IFTYPE_WDS,
+	NL80211_IFTYPE_MONITOR,
+	NL80211_IFTYPE_MESH_POINT,
+	NL80211_IFTYPE_P2P_CLIENT,
+	NL80211_IFTYPE_P2P_GO,
+	NL80211_IFTYPE_P2P_DEVICE,
+	NL80211_IFTYPE_OCB,
+	NL80211_IFTYPE_NAN,
+
+	/* keep last */
+	NUM_NL80211_IFTYPES,
+	NL80211_IFTYPE_MAX = NUM_NL80211_IFTYPES - 1
+};
+
+/**
+ * enum nl80211_sta_flags - station flags
+ *
+ * Station flags. When a station is added to an AP interface, it is
+ * assumed to be already associated (and hence authenticated.)
+ *
+ * @__NL80211_STA_FLAG_INVALID: attribute number 0 is reserved
+ * @NL80211_STA_FLAG_AUTHORIZED: station is authorized (802.1X)
+ * @NL80211_STA_FLAG_SHORT_PREAMBLE: station is capable of receiving frames
+ *	with short barker preamble
+ * @NL80211_STA_FLAG_WME: station is WME/QoS capable
+ * @NL80211_STA_FLAG_MFP: station uses management frame protection
+ * @NL80211_STA_FLAG_AUTHENTICATED: station is authenticated
+ * @NL80211_STA_FLAG_TDLS_PEER: station is a TDLS peer -- this flag should
+ *	only be used in managed mode (even in the flags mask). Note that the
+ *	flag can't be changed, it is only valid while adding a station, and
+ *	attempts to change it will silently be ignored (rather than rejected
+ *	as errors.)
+ * @NL80211_STA_FLAG_ASSOCIATED: station is associated; used with drivers
+ *	that support %NL80211_FEATURE_FULL_AP_CLIENT_STATE to transition a
+ *	previously added station into associated state
+ * @NL80211_STA_FLAG_MAX: highest station flag number currently defined
+ * @__NL80211_STA_FLAG_AFTER_LAST: internal use
+ */
+enum nl80211_sta_flags {
+	__NL80211_STA_FLAG_INVALID,
+	NL80211_STA_FLAG_AUTHORIZED,
+	NL80211_STA_FLAG_SHORT_PREAMBLE,
+	NL80211_STA_FLAG_WME,
+	NL80211_STA_FLAG_MFP,
+	NL80211_STA_FLAG_AUTHENTICATED,
+	NL80211_STA_FLAG_TDLS_PEER,
+	NL80211_STA_FLAG_ASSOCIATED,
+
+	/* keep last */
+	__NL80211_STA_FLAG_AFTER_LAST,
+	NL80211_STA_FLAG_MAX = __NL80211_STA_FLAG_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_sta_p2p_ps_status - station support of P2P PS
+ *
+ * @NL80211_P2P_PS_UNSUPPORTED: station doesn't support P2P PS mechanism
+ * @@NL80211_P2P_PS_SUPPORTED: station supports P2P PS mechanism
+ * @NUM_NL80211_P2P_PS_STATUS: number of values
+ */
+enum nl80211_sta_p2p_ps_status {
+	NL80211_P2P_PS_UNSUPPORTED = 0,
+	NL80211_P2P_PS_SUPPORTED,
+
+	NUM_NL80211_P2P_PS_STATUS,
+};
+
+#define NL80211_STA_FLAG_MAX_OLD_API	NL80211_STA_FLAG_TDLS_PEER
+
+/**
+ * struct nl80211_sta_flag_update - station flags mask/set
+ * @mask: mask of station flags to set
+ * @set: which values to set them to
+ *
+ * Both mask and set contain bits as per &enum nl80211_sta_flags.
+ */
+struct nl80211_sta_flag_update {
+	__u32 mask;
+	__u32 set;
+} __attribute__((packed));
+
+/**
+ * enum nl80211_he_gi - HE guard interval
+ * @NL80211_RATE_INFO_HE_GI_0_8: 0.8 usec
+ * @NL80211_RATE_INFO_HE_GI_1_6: 1.6 usec
+ * @NL80211_RATE_INFO_HE_GI_3_2: 3.2 usec
+ */
+enum nl80211_he_gi {
+	NL80211_RATE_INFO_HE_GI_0_8,
+	NL80211_RATE_INFO_HE_GI_1_6,
+	NL80211_RATE_INFO_HE_GI_3_2,
+};
+
+/**
+ * enum nl80211_he_ltf - HE long training field
+ * @NL80211_RATE_INFO_HE_1xLTF: 3.2 usec
+ * @NL80211_RATE_INFO_HE_2xLTF: 6.4 usec
+ * @NL80211_RATE_INFO_HE_4xLTF: 12.8 usec
+ */
+enum nl80211_he_ltf {
+	NL80211_RATE_INFO_HE_1XLTF,
+	NL80211_RATE_INFO_HE_2XLTF,
+	NL80211_RATE_INFO_HE_4XLTF,
+};
+
+/**
+ * enum nl80211_he_ru_alloc - HE RU allocation values
+ * @NL80211_RATE_INFO_HE_RU_ALLOC_26: 26-tone RU allocation
+ * @NL80211_RATE_INFO_HE_RU_ALLOC_52: 52-tone RU allocation
+ * @NL80211_RATE_INFO_HE_RU_ALLOC_106: 106-tone RU allocation
+ * @NL80211_RATE_INFO_HE_RU_ALLOC_242: 242-tone RU allocation
+ * @NL80211_RATE_INFO_HE_RU_ALLOC_484: 484-tone RU allocation
+ * @NL80211_RATE_INFO_HE_RU_ALLOC_996: 996-tone RU allocation
+ * @NL80211_RATE_INFO_HE_RU_ALLOC_2x996: 2x996-tone RU allocation
+ */
+enum nl80211_he_ru_alloc {
+	NL80211_RATE_INFO_HE_RU_ALLOC_26,
+	NL80211_RATE_INFO_HE_RU_ALLOC_52,
+	NL80211_RATE_INFO_HE_RU_ALLOC_106,
+	NL80211_RATE_INFO_HE_RU_ALLOC_242,
+	NL80211_RATE_INFO_HE_RU_ALLOC_484,
+	NL80211_RATE_INFO_HE_RU_ALLOC_996,
+	NL80211_RATE_INFO_HE_RU_ALLOC_2x996,
+};
+
+/**
+ * enum nl80211_eht_gi - EHT guard interval
+ * @NL80211_RATE_INFO_EHT_GI_0_8: 0.8 usec
+ * @NL80211_RATE_INFO_EHT_GI_1_6: 1.6 usec
+ * @NL80211_RATE_INFO_EHT_GI_3_2: 3.2 usec
+ */
+enum nl80211_eht_gi {
+	NL80211_RATE_INFO_EHT_GI_0_8,
+	NL80211_RATE_INFO_EHT_GI_1_6,
+	NL80211_RATE_INFO_EHT_GI_3_2,
+};
+
+/**
+ * enum nl80211_eht_ru_alloc - EHT RU allocation values
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_26: 26-tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_52: 52-tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_52P26: 52+26-tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_106: 106-tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_106P26: 106+26 tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_242: 242-tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_484: 484-tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_484P242: 484+242 tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_996: 996-tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_996P484: 996+484 tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_996P484P242: 996+484+242 tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_2x996: 2x996-tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_2x996P484: 2x996+484 tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_3x996: 3x996-tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_3x996P484: 3x996+484 tone RU allocation
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC_4x996: 4x996-tone RU allocation
+ */
+enum nl80211_eht_ru_alloc {
+	NL80211_RATE_INFO_EHT_RU_ALLOC_26,
+	NL80211_RATE_INFO_EHT_RU_ALLOC_52,
+	NL80211_RATE_INFO_EHT_RU_ALLOC_52P26,
+	NL80211_RATE_INFO_EHT_RU_ALLOC_106,
+	NL80211_RATE_INFO_EHT_RU_ALLOC_106P26,
+	NL80211_RATE_INFO_EHT_RU_ALLOC_242,
+	NL80211_RATE_INFO_EHT_RU_ALLOC_484,
+	NL80211_RATE_INFO_EHT_RU_ALLOC_484P242,
+	NL80211_RATE_INFO_EHT_RU_ALLOC_996,
+	NL80211_RATE_INFO_EHT_RU_ALLOC_996P484,
+	NL80211_RATE_INFO_EHT_RU_ALLOC_996P484P242,
+	NL80211_RATE_INFO_EHT_RU_ALLOC_2x996,
+	NL80211_RATE_INFO_EHT_RU_ALLOC_2x996P484,
+	NL80211_RATE_INFO_EHT_RU_ALLOC_3x996,
+	NL80211_RATE_INFO_EHT_RU_ALLOC_3x996P484,
+	NL80211_RATE_INFO_EHT_RU_ALLOC_4x996,
+};
+
+/**
+ * enum nl80211_rate_info - bitrate information
+ *
+ * These attribute types are used with %NL80211_STA_INFO_TXRATE
+ * when getting information about the bitrate of a station.
+ * There are 2 attributes for bitrate, a legacy one that represents
+ * a 16-bit value, and new one that represents a 32-bit value.
+ * If the rate value fits into 16 bit, both attributes are reported
+ * with the same value. If the rate is too high to fit into 16 bits
+ * (>6.5535Gbps) only 32-bit attribute is included.
+ * User space tools encouraged to use the 32-bit attribute and fall
+ * back to the 16-bit one for compatibility with older kernels.
+ *
+ * @__NL80211_RATE_INFO_INVALID: attribute number 0 is reserved
+ * @NL80211_RATE_INFO_BITRATE: total bitrate (u16, 100kbit/s)
+ * @NL80211_RATE_INFO_MCS: mcs index for 802.11n (u8)
+ * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 MHz dualchannel bitrate
+ * @NL80211_RATE_INFO_SHORT_GI: 400ns guard interval
+ * @NL80211_RATE_INFO_BITRATE32: total bitrate (u32, 100kbit/s)
+ * @NL80211_RATE_INFO_MAX: highest rate_info number currently defined
+ * @NL80211_RATE_INFO_VHT_MCS: MCS index for VHT (u8)
+ * @NL80211_RATE_INFO_VHT_NSS: number of streams in VHT (u8)
+ * @NL80211_RATE_INFO_80_MHZ_WIDTH: 80 MHz VHT rate
+ * @NL80211_RATE_INFO_80P80_MHZ_WIDTH: unused - 80+80 is treated the
+ *	same as 160 for purposes of the bitrates
+ * @NL80211_RATE_INFO_160_MHZ_WIDTH: 160 MHz VHT rate
+ * @NL80211_RATE_INFO_10_MHZ_WIDTH: 10 MHz width - note that this is
+ *	a legacy rate and will be reported as the actual bitrate, i.e.
+ *	half the base (20 MHz) rate
+ * @NL80211_RATE_INFO_5_MHZ_WIDTH: 5 MHz width - note that this is
+ *	a legacy rate and will be reported as the actual bitrate, i.e.
+ *	a quarter of the base (20 MHz) rate
+ * @NL80211_RATE_INFO_HE_MCS: HE MCS index (u8, 0-11)
+ * @NL80211_RATE_INFO_HE_NSS: HE NSS value (u8, 1-8)
+ * @NL80211_RATE_INFO_HE_GI: HE guard interval identifier
+ *	(u8, see &enum nl80211_he_gi)
+ * @NL80211_RATE_INFO_HE_DCM: HE DCM value (u8, 0/1)
+ * @NL80211_RATE_INFO_RU_ALLOC: HE RU allocation, if not present then
+ *	non-OFDMA was used (u8, see &enum nl80211_he_ru_alloc)
+ * @NL80211_RATE_INFO_320_MHZ_WIDTH: 320 MHz bitrate
+ * @NL80211_RATE_INFO_EHT_MCS: EHT MCS index (u8, 0-15)
+ * @NL80211_RATE_INFO_EHT_NSS: EHT NSS value (u8, 1-8)
+ * @NL80211_RATE_INFO_EHT_GI: EHT guard interval identifier
+ *	(u8, see &enum nl80211_eht_gi)
+ * @NL80211_RATE_INFO_EHT_RU_ALLOC: EHT RU allocation, if not present then
+ *	non-OFDMA was used (u8, see &enum nl80211_eht_ru_alloc)
+ * @__NL80211_RATE_INFO_AFTER_LAST: internal use
+ */
+enum nl80211_rate_info {
+	__NL80211_RATE_INFO_INVALID,
+	NL80211_RATE_INFO_BITRATE,
+	NL80211_RATE_INFO_MCS,
+	NL80211_RATE_INFO_40_MHZ_WIDTH,
+	NL80211_RATE_INFO_SHORT_GI,
+	NL80211_RATE_INFO_BITRATE32,
+	NL80211_RATE_INFO_VHT_MCS,
+	NL80211_RATE_INFO_VHT_NSS,
+	NL80211_RATE_INFO_80_MHZ_WIDTH,
+	NL80211_RATE_INFO_80P80_MHZ_WIDTH,
+	NL80211_RATE_INFO_160_MHZ_WIDTH,
+	NL80211_RATE_INFO_10_MHZ_WIDTH,
+	NL80211_RATE_INFO_5_MHZ_WIDTH,
+	NL80211_RATE_INFO_HE_MCS,
+	NL80211_RATE_INFO_HE_NSS,
+	NL80211_RATE_INFO_HE_GI,
+	NL80211_RATE_INFO_HE_DCM,
+	NL80211_RATE_INFO_HE_RU_ALLOC,
+	NL80211_RATE_INFO_320_MHZ_WIDTH,
+	NL80211_RATE_INFO_EHT_MCS,
+	NL80211_RATE_INFO_EHT_NSS,
+	NL80211_RATE_INFO_EHT_GI,
+	NL80211_RATE_INFO_EHT_RU_ALLOC,
+
+	/* keep last */
+	__NL80211_RATE_INFO_AFTER_LAST,
+	NL80211_RATE_INFO_MAX = __NL80211_RATE_INFO_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_sta_bss_param - BSS information collected by STA
+ *
+ * These attribute types are used with %NL80211_STA_INFO_BSS_PARAM
+ * when getting information about the bitrate of a station.
+ *
+ * @__NL80211_STA_BSS_PARAM_INVALID: attribute number 0 is reserved
+ * @NL80211_STA_BSS_PARAM_CTS_PROT: whether CTS protection is enabled (flag)
+ * @NL80211_STA_BSS_PARAM_SHORT_PREAMBLE:  whether short preamble is enabled
+ *	(flag)
+ * @NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME:  whether short slot time is enabled
+ *	(flag)
+ * @NL80211_STA_BSS_PARAM_DTIM_PERIOD: DTIM period for beaconing (u8)
+ * @NL80211_STA_BSS_PARAM_BEACON_INTERVAL: Beacon interval (u16)
+ * @NL80211_STA_BSS_PARAM_MAX: highest sta_bss_param number currently defined
+ * @__NL80211_STA_BSS_PARAM_AFTER_LAST: internal use
+ */
+enum nl80211_sta_bss_param {
+	__NL80211_STA_BSS_PARAM_INVALID,
+	NL80211_STA_BSS_PARAM_CTS_PROT,
+	NL80211_STA_BSS_PARAM_SHORT_PREAMBLE,
+	NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME,
+	NL80211_STA_BSS_PARAM_DTIM_PERIOD,
+	NL80211_STA_BSS_PARAM_BEACON_INTERVAL,
+
+	/* keep last */
+	__NL80211_STA_BSS_PARAM_AFTER_LAST,
+	NL80211_STA_BSS_PARAM_MAX = __NL80211_STA_BSS_PARAM_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_sta_info - station information
+ *
+ * These attribute types are used with %NL80211_ATTR_STA_INFO
+ * when getting information about a station.
+ *
+ * @__NL80211_STA_INFO_INVALID: attribute number 0 is reserved
+ * @NL80211_STA_INFO_INACTIVE_TIME: time since last activity (u32, msecs)
+ * @NL80211_STA_INFO_RX_BYTES: total received bytes (MPDU length)
+ *	(u32, from this station)
+ * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (MPDU length)
+ *	(u32, to this station)
+ * @NL80211_STA_INFO_RX_BYTES64: total received bytes (MPDU length)
+ *	(u64, from this station)
+ * @NL80211_STA_INFO_TX_BYTES64: total transmitted bytes (MPDU length)
+ *	(u64, to this station)
+ * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm)
+ * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute
+ * 	containing info as possible, see &enum nl80211_rate_info
+ * @NL80211_STA_INFO_RX_PACKETS: total received packet (MSDUs and MMPDUs)
+ *	(u32, from this station)
+ * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (MSDUs and MMPDUs)
+ *	(u32, to this station)
+ * @NL80211_STA_INFO_TX_RETRIES: total retries (MPDUs) (u32, to this station)
+ * @NL80211_STA_INFO_TX_FAILED: total failed packets (MPDUs)
+ *	(u32, to this station)
+ * @NL80211_STA_INFO_SIGNAL_AVG: signal strength average (u8, dBm)
+ * @NL80211_STA_INFO_LLID: the station's mesh LLID
+ * @NL80211_STA_INFO_PLID: the station's mesh PLID
+ * @NL80211_STA_INFO_PLINK_STATE: peer link state for the station
+ *	(see %enum nl80211_plink_state)
+ * @NL80211_STA_INFO_RX_BITRATE: last unicast data frame rx rate, nested
+ *	attribute, like NL80211_STA_INFO_TX_BITRATE.
+ * @NL80211_STA_INFO_BSS_PARAM: current station's view of BSS, nested attribute
+ *     containing info as possible, see &enum nl80211_sta_bss_param
+ * @NL80211_STA_INFO_CONNECTED_TIME: time since the station is last connected
+ * @NL80211_STA_INFO_STA_FLAGS: Contains a struct nl80211_sta_flag_update.
+ * @NL80211_STA_INFO_BEACON_LOSS: count of times beacon loss was detected (u32)
+ * @NL80211_STA_INFO_T_OFFSET: timing offset with respect to this STA (s64)
+ * @NL80211_STA_INFO_LOCAL_PM: local mesh STA link-specific power mode
+ * @NL80211_STA_INFO_PEER_PM: peer mesh STA link-specific power mode
+ * @NL80211_STA_INFO_NONPEER_PM: neighbor mesh STA power save mode towards
+ *	non-peer STA
+ * @NL80211_STA_INFO_CHAIN_SIGNAL: per-chain signal strength of last PPDU
+ *	Contains a nested array of signal strength attributes (u8, dBm)
+ * @NL80211_STA_INFO_CHAIN_SIGNAL_AVG: per-chain signal strength average
+ *	Same format as NL80211_STA_INFO_CHAIN_SIGNAL.
+ * @NL80211_STA_EXPECTED_THROUGHPUT: expected throughput considering also the
+ *	802.11 header (u32, kbps)
+ * @NL80211_STA_INFO_RX_DROP_MISC: RX packets dropped for unspecified reasons
+ *	(u64)
+ * @NL80211_STA_INFO_BEACON_RX: number of beacons received from this peer (u64)
+ * @NL80211_STA_INFO_BEACON_SIGNAL_AVG: signal strength average
+ *	for beacons only (u8, dBm)
+ * @NL80211_STA_INFO_TID_STATS: per-TID statistics (see &enum nl80211_tid_stats)
+ *	This is a nested attribute where each the inner attribute number is the
+ *	TID+1 and the special TID 16 (i.e. value 17) is used for non-QoS frames;
+ *	each one of those is again nested with &enum nl80211_tid_stats
+ *	attributes carrying the actual values.
+ * @NL80211_STA_INFO_RX_DURATION: aggregate PPDU duration for all frames
+ *	received from the station (u64, usec)
+ * @NL80211_STA_INFO_PAD: attribute used for padding for 64-bit alignment
+ * @NL80211_STA_INFO_ACK_SIGNAL: signal strength of the last ACK frame(u8, dBm)
+ * @NL80211_STA_INFO_ACK_SIGNAL_AVG: avg signal strength of ACK frames (s8, dBm)
+ * @NL80211_STA_INFO_RX_MPDUS: total number of received packets (MPDUs)
+ *	(u32, from this station)
+ * @NL80211_STA_INFO_FCS_ERROR_COUNT: total number of packets (MPDUs) received
+ *	with an FCS error (u32, from this station). This count may not include
+ *	some packets with an FCS error due to TA corruption. Hence this counter
+ *	might not be fully accurate.
+ * @NL80211_STA_INFO_CONNECTED_TO_GATE: set to true if STA has a path to a
+ *	mesh gate (u8, 0 or 1)
+ * @NL80211_STA_INFO_TX_DURATION: aggregate PPDU duration for all frames
+ *	sent to the station (u64, usec)
+ * @NL80211_STA_INFO_AIRTIME_WEIGHT: current airtime weight for station (u16)
+ * @NL80211_STA_INFO_AIRTIME_LINK_METRIC: airtime link metric for mesh station
+ * @NL80211_STA_INFO_ASSOC_AT_BOOTTIME: Timestamp (CLOCK_BOOTTIME, nanoseconds)
+ *	of STA's association
+ * @NL80211_STA_INFO_CONNECTED_TO_AS: set to true if STA has a path to a
+ *	authentication server (u8, 0 or 1)
+ * @__NL80211_STA_INFO_AFTER_LAST: internal
+ * @NL80211_STA_INFO_MAX: highest possible station info attribute
+ */
+enum nl80211_sta_info {
+	__NL80211_STA_INFO_INVALID,
+	NL80211_STA_INFO_INACTIVE_TIME,
+	NL80211_STA_INFO_RX_BYTES,
+	NL80211_STA_INFO_TX_BYTES,
+	NL80211_STA_INFO_LLID,
+	NL80211_STA_INFO_PLID,
+	NL80211_STA_INFO_PLINK_STATE,
+	NL80211_STA_INFO_SIGNAL,
+	NL80211_STA_INFO_TX_BITRATE,
+	NL80211_STA_INFO_RX_PACKETS,
+	NL80211_STA_INFO_TX_PACKETS,
+	NL80211_STA_INFO_TX_RETRIES,
+	NL80211_STA_INFO_TX_FAILED,
+	NL80211_STA_INFO_SIGNAL_AVG,
+	NL80211_STA_INFO_RX_BITRATE,
+	NL80211_STA_INFO_BSS_PARAM,
+	NL80211_STA_INFO_CONNECTED_TIME,
+	NL80211_STA_INFO_STA_FLAGS,
+	NL80211_STA_INFO_BEACON_LOSS,
+	NL80211_STA_INFO_T_OFFSET,
+	NL80211_STA_INFO_LOCAL_PM,
+	NL80211_STA_INFO_PEER_PM,
+	NL80211_STA_INFO_NONPEER_PM,
+	NL80211_STA_INFO_RX_BYTES64,
+	NL80211_STA_INFO_TX_BYTES64,
+	NL80211_STA_INFO_CHAIN_SIGNAL,
+	NL80211_STA_INFO_CHAIN_SIGNAL_AVG,
+	NL80211_STA_INFO_EXPECTED_THROUGHPUT,
+	NL80211_STA_INFO_RX_DROP_MISC,
+	NL80211_STA_INFO_BEACON_RX,
+	NL80211_STA_INFO_BEACON_SIGNAL_AVG,
+	NL80211_STA_INFO_TID_STATS,
+	NL80211_STA_INFO_RX_DURATION,
+	NL80211_STA_INFO_PAD,
+	NL80211_STA_INFO_ACK_SIGNAL,
+	NL80211_STA_INFO_ACK_SIGNAL_AVG,
+	NL80211_STA_INFO_RX_MPDUS,
+	NL80211_STA_INFO_FCS_ERROR_COUNT,
+	NL80211_STA_INFO_CONNECTED_TO_GATE,
+	NL80211_STA_INFO_TX_DURATION,
+	NL80211_STA_INFO_AIRTIME_WEIGHT,
+	NL80211_STA_INFO_AIRTIME_LINK_METRIC,
+	NL80211_STA_INFO_ASSOC_AT_BOOTTIME,
+	NL80211_STA_INFO_CONNECTED_TO_AS,
+
+	/* keep last */
+	__NL80211_STA_INFO_AFTER_LAST,
+	NL80211_STA_INFO_MAX = __NL80211_STA_INFO_AFTER_LAST - 1
+};
+
+/* we renamed this - stay compatible */
+#define NL80211_STA_INFO_DATA_ACK_SIGNAL_AVG NL80211_STA_INFO_ACK_SIGNAL_AVG
+
+
+/**
+ * enum nl80211_tid_stats - per TID statistics attributes
+ * @__NL80211_TID_STATS_INVALID: attribute number 0 is reserved
+ * @NL80211_TID_STATS_RX_MSDU: number of MSDUs received (u64)
+ * @NL80211_TID_STATS_TX_MSDU: number of MSDUs transmitted (or
+ *	attempted to transmit; u64)
+ * @NL80211_TID_STATS_TX_MSDU_RETRIES: number of retries for
+ *	transmitted MSDUs (not counting the first attempt; u64)
+ * @NL80211_TID_STATS_TX_MSDU_FAILED: number of failed transmitted
+ *	MSDUs (u64)
+ * @NL80211_TID_STATS_PAD: attribute used for padding for 64-bit alignment
+ * @NL80211_TID_STATS_TXQ_STATS: TXQ stats (nested attribute)
+ * @NUM_NL80211_TID_STATS: number of attributes here
+ * @NL80211_TID_STATS_MAX: highest numbered attribute here
+ */
+enum nl80211_tid_stats {
+	__NL80211_TID_STATS_INVALID,
+	NL80211_TID_STATS_RX_MSDU,
+	NL80211_TID_STATS_TX_MSDU,
+	NL80211_TID_STATS_TX_MSDU_RETRIES,
+	NL80211_TID_STATS_TX_MSDU_FAILED,
+	NL80211_TID_STATS_PAD,
+	NL80211_TID_STATS_TXQ_STATS,
+
+	/* keep last */
+	NUM_NL80211_TID_STATS,
+	NL80211_TID_STATS_MAX = NUM_NL80211_TID_STATS - 1
+};
+
+/**
+ * enum nl80211_txq_stats - per TXQ statistics attributes
+ * @__NL80211_TXQ_STATS_INVALID: attribute number 0 is reserved
+ * @NUM_NL80211_TXQ_STATS: number of attributes here
+ * @NL80211_TXQ_STATS_BACKLOG_BYTES: number of bytes currently backlogged
+ * @NL80211_TXQ_STATS_BACKLOG_PACKETS: number of packets currently
+ *      backlogged
+ * @NL80211_TXQ_STATS_FLOWS: total number of new flows seen
+ * @NL80211_TXQ_STATS_DROPS: total number of packet drops
+ * @NL80211_TXQ_STATS_ECN_MARKS: total number of packet ECN marks
+ * @NL80211_TXQ_STATS_OVERLIMIT: number of drops due to queue space overflow
+ * @NL80211_TXQ_STATS_OVERMEMORY: number of drops due to memory limit overflow
+ *      (only for per-phy stats)
+ * @NL80211_TXQ_STATS_COLLISIONS: number of hash collisions
+ * @NL80211_TXQ_STATS_TX_BYTES: total number of bytes dequeued from TXQ
+ * @NL80211_TXQ_STATS_TX_PACKETS: total number of packets dequeued from TXQ
+ * @NL80211_TXQ_STATS_MAX_FLOWS: number of flow buckets for PHY
+ * @NL80211_TXQ_STATS_MAX: highest numbered attribute here
+ */
+enum nl80211_txq_stats {
+	__NL80211_TXQ_STATS_INVALID,
+	NL80211_TXQ_STATS_BACKLOG_BYTES,
+	NL80211_TXQ_STATS_BACKLOG_PACKETS,
+	NL80211_TXQ_STATS_FLOWS,
+	NL80211_TXQ_STATS_DROPS,
+	NL80211_TXQ_STATS_ECN_MARKS,
+	NL80211_TXQ_STATS_OVERLIMIT,
+	NL80211_TXQ_STATS_OVERMEMORY,
+	NL80211_TXQ_STATS_COLLISIONS,
+	NL80211_TXQ_STATS_TX_BYTES,
+	NL80211_TXQ_STATS_TX_PACKETS,
+	NL80211_TXQ_STATS_MAX_FLOWS,
+
+	/* keep last */
+	NUM_NL80211_TXQ_STATS,
+	NL80211_TXQ_STATS_MAX = NUM_NL80211_TXQ_STATS - 1
+};
+
+/**
+ * enum nl80211_mpath_flags - nl80211 mesh path flags
+ *
+ * @NL80211_MPATH_FLAG_ACTIVE: the mesh path is active
+ * @NL80211_MPATH_FLAG_RESOLVING: the mesh path discovery process is running
+ * @NL80211_MPATH_FLAG_SN_VALID: the mesh path contains a valid SN
+ * @NL80211_MPATH_FLAG_FIXED: the mesh path has been manually set
+ * @NL80211_MPATH_FLAG_RESOLVED: the mesh path discovery process succeeded
+ */
+enum nl80211_mpath_flags {
+	NL80211_MPATH_FLAG_ACTIVE =	1<<0,
+	NL80211_MPATH_FLAG_RESOLVING =	1<<1,
+	NL80211_MPATH_FLAG_SN_VALID =	1<<2,
+	NL80211_MPATH_FLAG_FIXED =	1<<3,
+	NL80211_MPATH_FLAG_RESOLVED =	1<<4,
+};
+
+/**
+ * enum nl80211_mpath_info - mesh path information
+ *
+ * These attribute types are used with %NL80211_ATTR_MPATH_INFO when getting
+ * information about a mesh path.
+ *
+ * @__NL80211_MPATH_INFO_INVALID: attribute number 0 is reserved
+ * @NL80211_MPATH_INFO_FRAME_QLEN: number of queued frames for this destination
+ * @NL80211_MPATH_INFO_SN: destination sequence number
+ * @NL80211_MPATH_INFO_METRIC: metric (cost) of this mesh path
+ * @NL80211_MPATH_INFO_EXPTIME: expiration time for the path, in msec from now
+ * @NL80211_MPATH_INFO_FLAGS: mesh path flags, enumerated in
+ * 	&enum nl80211_mpath_flags;
+ * @NL80211_MPATH_INFO_DISCOVERY_TIMEOUT: total path discovery timeout, in msec
+ * @NL80211_MPATH_INFO_DISCOVERY_RETRIES: mesh path discovery retries
+ * @NL80211_MPATH_INFO_HOP_COUNT: hop count to destination
+ * @NL80211_MPATH_INFO_PATH_CHANGE: total number of path changes to destination
+ * @NL80211_MPATH_INFO_MAX: highest mesh path information attribute number
+ *	currently defined
+ * @__NL80211_MPATH_INFO_AFTER_LAST: internal use
+ */
+enum nl80211_mpath_info {
+	__NL80211_MPATH_INFO_INVALID,
+	NL80211_MPATH_INFO_FRAME_QLEN,
+	NL80211_MPATH_INFO_SN,
+	NL80211_MPATH_INFO_METRIC,
+	NL80211_MPATH_INFO_EXPTIME,
+	NL80211_MPATH_INFO_FLAGS,
+	NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
+	NL80211_MPATH_INFO_DISCOVERY_RETRIES,
+	NL80211_MPATH_INFO_HOP_COUNT,
+	NL80211_MPATH_INFO_PATH_CHANGE,
+
+	/* keep last */
+	__NL80211_MPATH_INFO_AFTER_LAST,
+	NL80211_MPATH_INFO_MAX = __NL80211_MPATH_INFO_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_band_iftype_attr - Interface type data attributes
+ *
+ * @__NL80211_BAND_IFTYPE_ATTR_INVALID: attribute number 0 is reserved
+ * @NL80211_BAND_IFTYPE_ATTR_IFTYPES: nested attribute containing a flag attribute
+ *     for each interface type that supports the band data
+ * @NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC: HE MAC capabilities as in HE
+ *     capabilities IE
+ * @NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY: HE PHY capabilities as in HE
+ *     capabilities IE
+ * @NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET: HE supported NSS/MCS as in HE
+ *     capabilities IE
+ * @NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE: HE PPE thresholds information as
+ *     defined in HE capabilities IE
+ * @NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA: HE 6GHz band capabilities (__le16),
+ *	given for all 6 GHz band channels
+ * @NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS: vendor element capabilities that are
+ *	advertised on this band/for this iftype (binary)
+ * @NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC: EHT MAC capabilities as in EHT
+ *	capabilities element
+ * @NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY: EHT PHY capabilities as in EHT
+ *	capabilities element
+ * @NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET: EHT supported NSS/MCS as in EHT
+ *	capabilities element
+ * @NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE: EHT PPE thresholds information as
+ *	defined in EHT capabilities element
+ * @__NL80211_BAND_IFTYPE_ATTR_AFTER_LAST: internal use
+ * @NL80211_BAND_IFTYPE_ATTR_MAX: highest band attribute currently defined
+ */
+enum nl80211_band_iftype_attr {
+	__NL80211_BAND_IFTYPE_ATTR_INVALID,
+
+	NL80211_BAND_IFTYPE_ATTR_IFTYPES,
+	NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC,
+	NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY,
+	NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET,
+	NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE,
+	NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA,
+	NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS,
+	NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC,
+	NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY,
+	NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET,
+	NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE,
+
+	/* keep last */
+	__NL80211_BAND_IFTYPE_ATTR_AFTER_LAST,
+	NL80211_BAND_IFTYPE_ATTR_MAX = __NL80211_BAND_IFTYPE_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_band_attr - band attributes
+ * @__NL80211_BAND_ATTR_INVALID: attribute number 0 is reserved
+ * @NL80211_BAND_ATTR_FREQS: supported frequencies in this band,
+ *	an array of nested frequency attributes
+ * @NL80211_BAND_ATTR_RATES: supported bitrates in this band,
+ *	an array of nested bitrate attributes
+ * @NL80211_BAND_ATTR_HT_MCS_SET: 16-byte attribute containing the MCS set as
+ *	defined in 802.11n
+ * @NL80211_BAND_ATTR_HT_CAPA: HT capabilities, as in the HT information IE
+ * @NL80211_BAND_ATTR_HT_AMPDU_FACTOR: A-MPDU factor, as in 11n
+ * @NL80211_BAND_ATTR_HT_AMPDU_DENSITY: A-MPDU density, as in 11n
+ * @NL80211_BAND_ATTR_VHT_MCS_SET: 32-byte attribute containing the MCS set as
+ *	defined in 802.11ac
+ * @NL80211_BAND_ATTR_VHT_CAPA: VHT capabilities, as in the HT information IE
+ * @NL80211_BAND_ATTR_IFTYPE_DATA: nested array attribute, with each entry using
+ *	attributes from &enum nl80211_band_iftype_attr
+ * @NL80211_BAND_ATTR_EDMG_CHANNELS: bitmap that indicates the 2.16 GHz
+ *	channel(s) that are allowed to be used for EDMG transmissions.
+ *	Defined by IEEE P802.11ay/D4.0 section 9.4.2.251.
+ * @NL80211_BAND_ATTR_EDMG_BW_CONFIG: Channel BW Configuration subfield encodes
+ *	the allowed channel bandwidth configurations.
+ *	Defined by IEEE P802.11ay/D4.0 section 9.4.2.251, Table 13.
+ * @NL80211_BAND_ATTR_S1G_MCS_NSS_SET: S1G capabilities, supported S1G-MCS and NSS
+ *	set subfield, as in the S1G information IE, 5 bytes
+ * @NL80211_BAND_ATTR_S1G_CAPA: S1G capabilities information subfield as in the
+ *	S1G information IE, 10 bytes
+ * @NL80211_BAND_ATTR_MAX: highest band attribute currently defined
+ * @__NL80211_BAND_ATTR_AFTER_LAST: internal use
+ */
+enum nl80211_band_attr {
+	__NL80211_BAND_ATTR_INVALID,
+	NL80211_BAND_ATTR_FREQS,
+	NL80211_BAND_ATTR_RATES,
+
+	NL80211_BAND_ATTR_HT_MCS_SET,
+	NL80211_BAND_ATTR_HT_CAPA,
+	NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
+	NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
+
+	NL80211_BAND_ATTR_VHT_MCS_SET,
+	NL80211_BAND_ATTR_VHT_CAPA,
+	NL80211_BAND_ATTR_IFTYPE_DATA,
+
+	NL80211_BAND_ATTR_EDMG_CHANNELS,
+	NL80211_BAND_ATTR_EDMG_BW_CONFIG,
+
+	NL80211_BAND_ATTR_S1G_MCS_NSS_SET,
+	NL80211_BAND_ATTR_S1G_CAPA,
+
+	/* keep last */
+	__NL80211_BAND_ATTR_AFTER_LAST,
+	NL80211_BAND_ATTR_MAX = __NL80211_BAND_ATTR_AFTER_LAST - 1
+};
+
+#define NL80211_BAND_ATTR_HT_CAPA NL80211_BAND_ATTR_HT_CAPA
+
+/**
+ * enum nl80211_wmm_rule - regulatory wmm rule
+ *
+ * @__NL80211_WMMR_INVALID: attribute number 0 is reserved
+ * @NL80211_WMMR_CW_MIN: Minimum contention window slot.
+ * @NL80211_WMMR_CW_MAX: Maximum contention window slot.
+ * @NL80211_WMMR_AIFSN: Arbitration Inter Frame Space.
+ * @NL80211_WMMR_TXOP: Maximum allowed tx operation time.
+ * @nl80211_WMMR_MAX: highest possible wmm rule.
+ * @__NL80211_WMMR_LAST: Internal use.
+ */
+enum nl80211_wmm_rule {
+	__NL80211_WMMR_INVALID,
+	NL80211_WMMR_CW_MIN,
+	NL80211_WMMR_CW_MAX,
+	NL80211_WMMR_AIFSN,
+	NL80211_WMMR_TXOP,
+
+	/* keep last */
+	__NL80211_WMMR_LAST,
+	NL80211_WMMR_MAX = __NL80211_WMMR_LAST - 1
+};
+
+/**
+ * enum nl80211_frequency_attr - frequency attributes
+ * @__NL80211_FREQUENCY_ATTR_INVALID: attribute number 0 is reserved
+ * @NL80211_FREQUENCY_ATTR_FREQ: Frequency in MHz
+ * @NL80211_FREQUENCY_ATTR_DISABLED: Channel is disabled in current
+ *	regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_NO_IR: no mechanisms that initiate radiation
+ * 	are permitted on this channel, this includes sending probe
+ * 	requests, or modes of operation that require beaconing.
+ * @NL80211_FREQUENCY_ATTR_RADAR: Radar detection is mandatory
+ *	on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_MAX_TX_POWER: Maximum transmission power in mBm
+ *	(100 * dBm).
+ * @NL80211_FREQUENCY_ATTR_DFS_STATE: current state for DFS
+ *	(enum nl80211_dfs_state)
+ * @NL80211_FREQUENCY_ATTR_DFS_TIME: time in miliseconds for how long
+ *	this channel is in this DFS state.
+ * @NL80211_FREQUENCY_ATTR_NO_HT40_MINUS: HT40- isn't possible with this
+ *	channel as the control channel
+ * @NL80211_FREQUENCY_ATTR_NO_HT40_PLUS: HT40+ isn't possible with this
+ *	channel as the control channel
+ * @NL80211_FREQUENCY_ATTR_NO_80MHZ: any 80 MHz channel using this channel
+ *	as the primary or any of the secondary channels isn't possible,
+ *	this includes 80+80 channels
+ * @NL80211_FREQUENCY_ATTR_NO_160MHZ: any 160 MHz (but not 80+80) channel
+ *	using this channel as the primary or any of the secondary channels
+ *	isn't possible
+ * @NL80211_FREQUENCY_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds.
+ * @NL80211_FREQUENCY_ATTR_INDOOR_ONLY: Only indoor use is permitted on this
+ *	channel. A channel that has the INDOOR_ONLY attribute can only be
+ *	used when there is a clear assessment that the device is operating in
+ *	an indoor surroundings, i.e., it is connected to AC power (and not
+ *	through portable DC inverters) or is under the control of a master
+ *	that is acting as an AP and is connected to AC power.
+ * @NL80211_FREQUENCY_ATTR_IR_CONCURRENT: IR operation is allowed on this
+ *	channel if it's connected concurrently to a BSS on the same channel on
+ *	the 2 GHz band or to a channel in the same UNII band (on the 5 GHz
+ *	band), and IEEE80211_CHAN_RADAR is not set. Instantiating a GO or TDLS
+ *	off-channel on a channel that has the IR_CONCURRENT attribute set can be
+ *	done when there is a clear assessment that the device is operating under
+ *	the guidance of an authorized master, i.e., setting up a GO or TDLS
+ *	off-channel while the device is also connected to an AP with DFS and
+ *	radar detection on the UNII band (it is up to user-space, i.e.,
+ *	wpa_supplicant to perform the required verifications). Using this
+ *	attribute for IR is disallowed for master interfaces (IBSS, AP).
+ * @NL80211_FREQUENCY_ATTR_NO_20MHZ: 20 MHz operation is not allowed
+ *	on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_NO_10MHZ: 10 MHz operation is not allowed
+ *	on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_WMM: this channel has wmm limitations.
+ *	This is a nested attribute that contains the wmm limitation per AC.
+ *	(see &enum nl80211_wmm_rule)
+ * @NL80211_FREQUENCY_ATTR_NO_HE: HE operation is not allowed on this channel
+ *	in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_OFFSET: frequency offset in KHz
+ * @NL80211_FREQUENCY_ATTR_1MHZ: 1 MHz operation is allowed
+ *	on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_2MHZ: 2 MHz operation is allowed
+ *	on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_4MHZ: 4 MHz operation is allowed
+ *	on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_8MHZ: 8 MHz operation is allowed
+ *	on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_16MHZ: 16 MHz operation is allowed
+ *	on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_NO_320MHZ: any 320 MHz channel using this channel
+ *	as the primary or any of the secondary channels isn't possible
+ * @NL80211_FREQUENCY_ATTR_NO_EHT: EHT operation is not allowed on this channel
+ *	in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
+ *	currently defined
+ * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
+ *
+ * See https://apps.fcc.gov/eas/comments/GetPublishedDocument.html?id=327&tn=528122
+ * for more information on the FCC description of the relaxations allowed
+ * by NL80211_FREQUENCY_ATTR_INDOOR_ONLY and
+ * NL80211_FREQUENCY_ATTR_IR_CONCURRENT.
+ */
+enum nl80211_frequency_attr {
+	__NL80211_FREQUENCY_ATTR_INVALID,
+	NL80211_FREQUENCY_ATTR_FREQ,
+	NL80211_FREQUENCY_ATTR_DISABLED,
+	NL80211_FREQUENCY_ATTR_NO_IR,
+	__NL80211_FREQUENCY_ATTR_NO_IBSS,
+	NL80211_FREQUENCY_ATTR_RADAR,
+	NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
+	NL80211_FREQUENCY_ATTR_DFS_STATE,
+	NL80211_FREQUENCY_ATTR_DFS_TIME,
+	NL80211_FREQUENCY_ATTR_NO_HT40_MINUS,
+	NL80211_FREQUENCY_ATTR_NO_HT40_PLUS,
+	NL80211_FREQUENCY_ATTR_NO_80MHZ,
+	NL80211_FREQUENCY_ATTR_NO_160MHZ,
+	NL80211_FREQUENCY_ATTR_DFS_CAC_TIME,
+	NL80211_FREQUENCY_ATTR_INDOOR_ONLY,
+	NL80211_FREQUENCY_ATTR_IR_CONCURRENT,
+	NL80211_FREQUENCY_ATTR_NO_20MHZ,
+	NL80211_FREQUENCY_ATTR_NO_10MHZ,
+	NL80211_FREQUENCY_ATTR_WMM,
+	NL80211_FREQUENCY_ATTR_NO_HE,
+	NL80211_FREQUENCY_ATTR_OFFSET,
+	NL80211_FREQUENCY_ATTR_1MHZ,
+	NL80211_FREQUENCY_ATTR_2MHZ,
+	NL80211_FREQUENCY_ATTR_4MHZ,
+	NL80211_FREQUENCY_ATTR_8MHZ,
+	NL80211_FREQUENCY_ATTR_16MHZ,
+	NL80211_FREQUENCY_ATTR_NO_320MHZ,
+	NL80211_FREQUENCY_ATTR_NO_EHT,
+
+	/* keep last */
+	__NL80211_FREQUENCY_ATTR_AFTER_LAST,
+	NL80211_FREQUENCY_ATTR_MAX = __NL80211_FREQUENCY_ATTR_AFTER_LAST - 1
+};
+
+#define NL80211_FREQUENCY_ATTR_MAX_TX_POWER NL80211_FREQUENCY_ATTR_MAX_TX_POWER
+#define NL80211_FREQUENCY_ATTR_PASSIVE_SCAN	NL80211_FREQUENCY_ATTR_NO_IR
+#define NL80211_FREQUENCY_ATTR_NO_IBSS		NL80211_FREQUENCY_ATTR_NO_IR
+#define NL80211_FREQUENCY_ATTR_NO_IR		NL80211_FREQUENCY_ATTR_NO_IR
+#define NL80211_FREQUENCY_ATTR_GO_CONCURRENT \
+					NL80211_FREQUENCY_ATTR_IR_CONCURRENT
+
+/**
+ * enum nl80211_bitrate_attr - bitrate attributes
+ * @__NL80211_BITRATE_ATTR_INVALID: attribute number 0 is reserved
+ * @NL80211_BITRATE_ATTR_RATE: Bitrate in units of 100 kbps
+ * @NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE: Short preamble supported
+ *	in 2.4 GHz band.
+ * @NL80211_BITRATE_ATTR_MAX: highest bitrate attribute number
+ *	currently defined
+ * @__NL80211_BITRATE_ATTR_AFTER_LAST: internal use
+ */
+enum nl80211_bitrate_attr {
+	__NL80211_BITRATE_ATTR_INVALID,
+	NL80211_BITRATE_ATTR_RATE,
+	NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE,
+
+	/* keep last */
+	__NL80211_BITRATE_ATTR_AFTER_LAST,
+	NL80211_BITRATE_ATTR_MAX = __NL80211_BITRATE_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_initiator - Indicates the initiator of a reg domain request
+ * @NL80211_REGDOM_SET_BY_CORE: Core queried CRDA for a dynamic world
+ * 	regulatory domain.
+ * @NL80211_REGDOM_SET_BY_USER: User asked the wireless core to set the
+ * 	regulatory domain.
+ * @NL80211_REGDOM_SET_BY_DRIVER: a wireless drivers has hinted to the
+ * 	wireless core it thinks its knows the regulatory domain we should be in.
+ * @NL80211_REGDOM_SET_BY_COUNTRY_IE: the wireless core has received an
+ * 	802.11 country information element with regulatory information it
+ * 	thinks we should consider. cfg80211 only processes the country
+ *	code from the IE, and relies on the regulatory domain information
+ *	structure passed by userspace (CRDA) from our wireless-regdb.
+ *	If a channel is enabled but the country code indicates it should
+ *	be disabled we disable the channel and re-enable it upon disassociation.
+ */
+enum nl80211_reg_initiator {
+	NL80211_REGDOM_SET_BY_CORE,
+	NL80211_REGDOM_SET_BY_USER,
+	NL80211_REGDOM_SET_BY_DRIVER,
+	NL80211_REGDOM_SET_BY_COUNTRY_IE,
+};
+
+/**
+ * enum nl80211_reg_type - specifies the type of regulatory domain
+ * @NL80211_REGDOM_TYPE_COUNTRY: the regulatory domain set is one that pertains
+ *	to a specific country. When this is set you can count on the
+ *	ISO / IEC 3166 alpha2 country code being valid.
+ * @NL80211_REGDOM_TYPE_WORLD: the regulatory set domain is the world regulatory
+ * 	domain.
+ * @NL80211_REGDOM_TYPE_CUSTOM_WORLD: the regulatory domain set is a custom
+ * 	driver specific world regulatory domain. These do not apply system-wide
+ * 	and are only applicable to the individual devices which have requested
+ * 	them to be applied.
+ * @NL80211_REGDOM_TYPE_INTERSECTION: the regulatory domain set is the product
+ *	of an intersection between two regulatory domains -- the previously
+ *	set regulatory domain on the system and the last accepted regulatory
+ *	domain request to be processed.
+ */
+enum nl80211_reg_type {
+	NL80211_REGDOM_TYPE_COUNTRY,
+	NL80211_REGDOM_TYPE_WORLD,
+	NL80211_REGDOM_TYPE_CUSTOM_WORLD,
+	NL80211_REGDOM_TYPE_INTERSECTION,
+};
+
+/**
+ * enum nl80211_reg_rule_attr - regulatory rule attributes
+ * @__NL80211_REG_RULE_ATTR_INVALID: attribute number 0 is reserved
+ * @NL80211_ATTR_REG_RULE_FLAGS: a set of flags which specify additional
+ * 	considerations for a given frequency range. These are the
+ * 	&enum nl80211_reg_rule_flags.
+ * @NL80211_ATTR_FREQ_RANGE_START: starting frequencry for the regulatory
+ * 	rule in KHz. This is not a center of frequency but an actual regulatory
+ * 	band edge.
+ * @NL80211_ATTR_FREQ_RANGE_END: ending frequency for the regulatory rule
+ * 	in KHz. This is not a center a frequency but an actual regulatory
+ * 	band edge.
+ * @NL80211_ATTR_FREQ_RANGE_MAX_BW: maximum allowed bandwidth for this
+ *	frequency range, in KHz.
+ * @NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN: the maximum allowed antenna gain
+ * 	for a given frequency range. The value is in mBi (100 * dBi).
+ * 	If you don't have one then don't send this.
+ * @NL80211_ATTR_POWER_RULE_MAX_EIRP: the maximum allowed EIRP for
+ * 	a given frequency range. The value is in mBm (100 * dBm).
+ * @NL80211_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds.
+ *	If not present or 0 default CAC time will be used.
+ * @NL80211_REG_RULE_ATTR_MAX: highest regulatory rule attribute number
+ *	currently defined
+ * @__NL80211_REG_RULE_ATTR_AFTER_LAST: internal use
+ */
+enum nl80211_reg_rule_attr {
+	__NL80211_REG_RULE_ATTR_INVALID,
+	NL80211_ATTR_REG_RULE_FLAGS,
+
+	NL80211_ATTR_FREQ_RANGE_START,
+	NL80211_ATTR_FREQ_RANGE_END,
+	NL80211_ATTR_FREQ_RANGE_MAX_BW,
+
+	NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
+	NL80211_ATTR_POWER_RULE_MAX_EIRP,
+
+	NL80211_ATTR_DFS_CAC_TIME,
+
+	/* keep last */
+	__NL80211_REG_RULE_ATTR_AFTER_LAST,
+	NL80211_REG_RULE_ATTR_MAX = __NL80211_REG_RULE_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_sched_scan_match_attr - scheduled scan match attributes
+ * @__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: attribute number 0 is reserved
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_SSID: SSID to be used for matching,
+ *	only report BSS with matching SSID.
+ *	(This cannot be used together with BSSID.)
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI: RSSI threshold (in dBm) for reporting a
+ *	BSS in scan results. Filtering is turned off if not specified. Note that
+ *	if this attribute is in a match set of its own, then it is treated as
+ *	the default value for all matchsets with an SSID, rather than being a
+ *	matchset of its own without an RSSI filter. This is due to problems with
+ *	how this API was implemented in the past. Also, due to the same problem,
+ *	the only way to create a matchset with only an RSSI filter (with this
+ *	attribute) is if there's only a single matchset with the RSSI attribute.
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI: Flag indicating whether
+ *	%NL80211_SCHED_SCAN_MATCH_ATTR_RSSI to be used as absolute RSSI or
+ *	relative to current bss's RSSI.
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST: When present the RSSI level for
+ *	BSS-es in the specified band is to be adjusted before doing
+ *	RSSI-based BSS selection. The attribute value is a packed structure
+ *	value as specified by &struct nl80211_bss_select_rssi_adjust.
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_BSSID: BSSID to be used for matching
+ *	(this cannot be used together with SSID).
+ * @NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI: Nested attribute that carries the
+ *	band specific minimum rssi thresholds for the bands defined in
+ *	enum nl80211_band. The minimum rssi threshold value(s32) specific to a
+ *	band shall be encapsulated in attribute with type value equals to one
+ *	of the NL80211_BAND_* defined in enum nl80211_band. For example, the
+ *	minimum rssi threshold value for 2.4GHZ band shall be encapsulated
+ *	within an attribute of type NL80211_BAND_2GHZ. And one or more of such
+ *	attributes will be nested within this attribute.
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter
+ *	attribute number currently defined
+ * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use
+ */
+enum nl80211_sched_scan_match_attr {
+	__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID,
+
+	NL80211_SCHED_SCAN_MATCH_ATTR_SSID,
+	NL80211_SCHED_SCAN_MATCH_ATTR_RSSI,
+	NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI,
+	NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST,
+	NL80211_SCHED_SCAN_MATCH_ATTR_BSSID,
+	NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI,
+
+	/* keep last */
+	__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST,
+	NL80211_SCHED_SCAN_MATCH_ATTR_MAX =
+		__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST - 1
+};
+
+/* only for backward compatibility */
+#define NL80211_ATTR_SCHED_SCAN_MATCH_SSID NL80211_SCHED_SCAN_MATCH_ATTR_SSID
+
+/**
+ * enum nl80211_reg_rule_flags - regulatory rule flags
+ *
+ * @NL80211_RRF_NO_OFDM: OFDM modulation not allowed
+ * @NL80211_RRF_NO_CCK: CCK modulation not allowed
+ * @NL80211_RRF_NO_INDOOR: indoor operation not allowed
+ * @NL80211_RRF_NO_OUTDOOR: outdoor operation not allowed
+ * @NL80211_RRF_DFS: DFS support is required to be used
+ * @NL80211_RRF_PTP_ONLY: this is only for Point To Point links
+ * @NL80211_RRF_PTMP_ONLY: this is only for Point To Multi Point links
+ * @NL80211_RRF_NO_IR: no mechanisms that initiate radiation are allowed,
+ * 	this includes probe requests or modes of operation that require
+ * 	beaconing.
+ * @NL80211_RRF_AUTO_BW: maximum available bandwidth should be calculated
+ *	base on contiguous rules and wider channels will be allowed to cross
+ *	multiple contiguous/overlapping frequency ranges.
+ * @NL80211_RRF_IR_CONCURRENT: See %NL80211_FREQUENCY_ATTR_IR_CONCURRENT
+ * @NL80211_RRF_NO_HT40MINUS: channels can't be used in HT40- operation
+ * @NL80211_RRF_NO_HT40PLUS: channels can't be used in HT40+ operation
+ * @NL80211_RRF_NO_80MHZ: 80MHz operation not allowed
+ * @NL80211_RRF_NO_160MHZ: 160MHz operation not allowed
+ * @NL80211_RRF_NO_HE: HE operation not allowed
+ * @NL80211_RRF_NO_320MHZ: 320MHz operation not allowed
+ */
+enum nl80211_reg_rule_flags {
+	NL80211_RRF_NO_OFDM		= 1<<0,
+	NL80211_RRF_NO_CCK		= 1<<1,
+	NL80211_RRF_NO_INDOOR		= 1<<2,
+	NL80211_RRF_NO_OUTDOOR		= 1<<3,
+	NL80211_RRF_DFS			= 1<<4,
+	NL80211_RRF_PTP_ONLY		= 1<<5,
+	NL80211_RRF_PTMP_ONLY		= 1<<6,
+	NL80211_RRF_NO_IR		= 1<<7,
+	__NL80211_RRF_NO_IBSS		= 1<<8,
+	NL80211_RRF_AUTO_BW		= 1<<11,
+	NL80211_RRF_IR_CONCURRENT	= 1<<12,
+	NL80211_RRF_NO_HT40MINUS	= 1<<13,
+	NL80211_RRF_NO_HT40PLUS		= 1<<14,
+	NL80211_RRF_NO_80MHZ		= 1<<15,
+	NL80211_RRF_NO_160MHZ		= 1<<16,
+	NL80211_RRF_NO_HE		= 1<<17,
+	NL80211_RRF_NO_320MHZ		= 1<<18,
+};
+
+#define NL80211_RRF_PASSIVE_SCAN	NL80211_RRF_NO_IR
+#define NL80211_RRF_NO_IBSS		NL80211_RRF_NO_IR
+#define NL80211_RRF_NO_IR		NL80211_RRF_NO_IR
+#define NL80211_RRF_NO_HT40		(NL80211_RRF_NO_HT40MINUS |\
+					 NL80211_RRF_NO_HT40PLUS)
+#define NL80211_RRF_GO_CONCURRENT	NL80211_RRF_IR_CONCURRENT
+
+/* For backport compatibility with older userspace */
+#define NL80211_RRF_NO_IR_ALL		(NL80211_RRF_NO_IR | __NL80211_RRF_NO_IBSS)
+
+/**
+ * enum nl80211_dfs_regions - regulatory DFS regions
+ *
+ * @NL80211_DFS_UNSET: Country has no DFS master region specified
+ * @NL80211_DFS_FCC: Country follows DFS master rules from FCC
+ * @NL80211_DFS_ETSI: Country follows DFS master rules from ETSI
+ * @NL80211_DFS_JP: Country follows DFS master rules from JP/MKK/Telec
+ */
+enum nl80211_dfs_regions {
+	NL80211_DFS_UNSET	= 0,
+	NL80211_DFS_FCC		= 1,
+	NL80211_DFS_ETSI	= 2,
+	NL80211_DFS_JP		= 3,
+};
+
+/**
+ * enum nl80211_user_reg_hint_type - type of user regulatory hint
+ *
+ * @NL80211_USER_REG_HINT_USER: a user sent the hint. This is always
+ *	assumed if the attribute is not set.
+ * @NL80211_USER_REG_HINT_CELL_BASE: the hint comes from a cellular
+ *	base station. Device drivers that have been tested to work
+ *	properly to support this type of hint can enable these hints
+ *	by setting the NL80211_FEATURE_CELL_BASE_REG_HINTS feature
+ *	capability on the struct wiphy. The wireless core will
+ *	ignore all cell base station hints until at least one device
+ *	present has been registered with the wireless core that
+ *	has listed NL80211_FEATURE_CELL_BASE_REG_HINTS as a
+ *	supported feature.
+ * @NL80211_USER_REG_HINT_INDOOR: a user sent an hint indicating that the
+ *	platform is operating in an indoor environment.
+ */
+enum nl80211_user_reg_hint_type {
+	NL80211_USER_REG_HINT_USER	= 0,
+	NL80211_USER_REG_HINT_CELL_BASE = 1,
+	NL80211_USER_REG_HINT_INDOOR    = 2,
+};
+
+/**
+ * enum nl80211_survey_info - survey information
+ *
+ * These attribute types are used with %NL80211_ATTR_SURVEY_INFO
+ * when getting information about a survey.
+ *
+ * @__NL80211_SURVEY_INFO_INVALID: attribute number 0 is reserved
+ * @NL80211_SURVEY_INFO_FREQUENCY: center frequency of channel
+ * @NL80211_SURVEY_INFO_NOISE: noise level of channel (u8, dBm)
+ * @NL80211_SURVEY_INFO_IN_USE: channel is currently being used
+ * @NL80211_SURVEY_INFO_TIME: amount of time (in ms) that the radio
+ *	was turned on (on channel or globally)
+ * @NL80211_SURVEY_INFO_TIME_BUSY: amount of the time the primary
+ *	channel was sensed busy (either due to activity or energy detect)
+ * @NL80211_SURVEY_INFO_TIME_EXT_BUSY: amount of time the extension
+ *	channel was sensed busy
+ * @NL80211_SURVEY_INFO_TIME_RX: amount of time the radio spent
+ *	receiving data (on channel or globally)
+ * @NL80211_SURVEY_INFO_TIME_TX: amount of time the radio spent
+ *	transmitting data (on channel or globally)
+ * @NL80211_SURVEY_INFO_TIME_SCAN: time the radio spent for scan
+ *	(on this channel or globally)
+ * @NL80211_SURVEY_INFO_PAD: attribute used for padding for 64-bit alignment
+ * @NL80211_SURVEY_INFO_TIME_BSS_RX: amount of time the radio spent
+ *	receiving frames destined to the local BSS
+ * @NL80211_SURVEY_INFO_MAX: highest survey info attribute number
+ *	currently defined
+ * @NL80211_SURVEY_INFO_FREQUENCY_OFFSET: center frequency offset in KHz
+ * @__NL80211_SURVEY_INFO_AFTER_LAST: internal use
+ */
+enum nl80211_survey_info {
+	__NL80211_SURVEY_INFO_INVALID,
+	NL80211_SURVEY_INFO_FREQUENCY,
+	NL80211_SURVEY_INFO_NOISE,
+	NL80211_SURVEY_INFO_IN_USE,
+	NL80211_SURVEY_INFO_TIME,
+	NL80211_SURVEY_INFO_TIME_BUSY,
+	NL80211_SURVEY_INFO_TIME_EXT_BUSY,
+	NL80211_SURVEY_INFO_TIME_RX,
+	NL80211_SURVEY_INFO_TIME_TX,
+	NL80211_SURVEY_INFO_TIME_SCAN,
+	NL80211_SURVEY_INFO_PAD,
+	NL80211_SURVEY_INFO_TIME_BSS_RX,
+	NL80211_SURVEY_INFO_FREQUENCY_OFFSET,
+
+	/* keep last */
+	__NL80211_SURVEY_INFO_AFTER_LAST,
+	NL80211_SURVEY_INFO_MAX = __NL80211_SURVEY_INFO_AFTER_LAST - 1
+};
+
+/* keep old names for compatibility */
+#define NL80211_SURVEY_INFO_CHANNEL_TIME		NL80211_SURVEY_INFO_TIME
+#define NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY		NL80211_SURVEY_INFO_TIME_BUSY
+#define NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY	NL80211_SURVEY_INFO_TIME_EXT_BUSY
+#define NL80211_SURVEY_INFO_CHANNEL_TIME_RX		NL80211_SURVEY_INFO_TIME_RX
+#define NL80211_SURVEY_INFO_CHANNEL_TIME_TX		NL80211_SURVEY_INFO_TIME_TX
+
+/**
+ * enum nl80211_mntr_flags - monitor configuration flags
+ *
+ * Monitor configuration flags.
+ *
+ * @__NL80211_MNTR_FLAG_INVALID: reserved
+ *
+ * @NL80211_MNTR_FLAG_FCSFAIL: pass frames with bad FCS
+ * @NL80211_MNTR_FLAG_PLCPFAIL: pass frames with bad PLCP
+ * @NL80211_MNTR_FLAG_CONTROL: pass control frames
+ * @NL80211_MNTR_FLAG_OTHER_BSS: disable BSSID filtering
+ * @NL80211_MNTR_FLAG_COOK_FRAMES: report frames after processing.
+ *	overrides all other flags.
+ * @NL80211_MNTR_FLAG_ACTIVE: use the configured MAC address
+ *	and ACK incoming unicast packets.
+ *
+ * @__NL80211_MNTR_FLAG_AFTER_LAST: internal use
+ * @NL80211_MNTR_FLAG_MAX: highest possible monitor flag
+ */
+enum nl80211_mntr_flags {
+	__NL80211_MNTR_FLAG_INVALID,
+	NL80211_MNTR_FLAG_FCSFAIL,
+	NL80211_MNTR_FLAG_PLCPFAIL,
+	NL80211_MNTR_FLAG_CONTROL,
+	NL80211_MNTR_FLAG_OTHER_BSS,
+	NL80211_MNTR_FLAG_COOK_FRAMES,
+	NL80211_MNTR_FLAG_ACTIVE,
+
+	/* keep last */
+	__NL80211_MNTR_FLAG_AFTER_LAST,
+	NL80211_MNTR_FLAG_MAX = __NL80211_MNTR_FLAG_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_mesh_power_mode - mesh power save modes
+ *
+ * @NL80211_MESH_POWER_UNKNOWN: The mesh power mode of the mesh STA is
+ *	not known or has not been set yet.
+ * @NL80211_MESH_POWER_ACTIVE: Active mesh power mode. The mesh STA is
+ *	in Awake state all the time.
+ * @NL80211_MESH_POWER_LIGHT_SLEEP: Light sleep mode. The mesh STA will
+ *	alternate between Active and Doze states, but will wake up for
+ *	neighbor's beacons.
+ * @NL80211_MESH_POWER_DEEP_SLEEP: Deep sleep mode. The mesh STA will
+ *	alternate between Active and Doze states, but may not wake up
+ *	for neighbor's beacons.
+ *
+ * @__NL80211_MESH_POWER_AFTER_LAST - internal use
+ * @NL80211_MESH_POWER_MAX - highest possible power save level
+ */
+
+enum nl80211_mesh_power_mode {
+	NL80211_MESH_POWER_UNKNOWN,
+	NL80211_MESH_POWER_ACTIVE,
+	NL80211_MESH_POWER_LIGHT_SLEEP,
+	NL80211_MESH_POWER_DEEP_SLEEP,
+
+	__NL80211_MESH_POWER_AFTER_LAST,
+	NL80211_MESH_POWER_MAX = __NL80211_MESH_POWER_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_meshconf_params - mesh configuration parameters
+ *
+ * Mesh configuration parameters. These can be changed while the mesh is
+ * active.
+ *
+ * @__NL80211_MESHCONF_INVALID: internal use
+ *
+ * @NL80211_MESHCONF_RETRY_TIMEOUT: specifies the initial retry timeout in
+ *	millisecond units, used by the Peer Link Open message
+ *
+ * @NL80211_MESHCONF_CONFIRM_TIMEOUT: specifies the initial confirm timeout, in
+ *	millisecond units, used by the peer link management to close a peer link
+ *
+ * @NL80211_MESHCONF_HOLDING_TIMEOUT: specifies the holding timeout, in
+ *	millisecond units
+ *
+ * @NL80211_MESHCONF_MAX_PEER_LINKS: maximum number of peer links allowed
+ *	on this mesh interface
+ *
+ * @NL80211_MESHCONF_MAX_RETRIES: specifies the maximum number of peer link
+ *	open retries that can be sent to establish a new peer link instance in a
+ *	mesh
+ *
+ * @NL80211_MESHCONF_TTL: specifies the value of TTL field set at a source mesh
+ *	point.
+ *
+ * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically open
+ *	peer links when we detect compatible mesh peers. Disabled if
+ *	@NL80211_MESH_SETUP_USERSPACE_MPM or @NL80211_MESH_SETUP_USERSPACE_AMPE are
+ *	set.
+ *
+ * @NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES: the number of action frames
+ *	containing a PREQ that an MP can send to a particular destination (path
+ *	target)
+ *
+ * @NL80211_MESHCONF_PATH_REFRESH_TIME: how frequently to refresh mesh paths
+ *	(in milliseconds)
+ *
+ * @NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT: minimum length of time to wait
+ *	until giving up on a path discovery (in milliseconds)
+ *
+ * @NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT: The time (in TUs) for which mesh
+ *	points receiving a PREQ shall consider the forwarding information from
+ *	the root to be valid. (TU = time unit)
+ *
+ * @NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL: The minimum interval of time (in
+ *	TUs) during which an MP can send only one action frame containing a PREQ
+ *	reference element
+ *
+ * @NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME: The interval of time (in TUs)
+ *	that it takes for an HWMP information element to propagate across the
+ *	mesh
+ *
+ * @NL80211_MESHCONF_HWMP_ROOTMODE: whether root mode is enabled or not
+ *
+ * @NL80211_MESHCONF_ELEMENT_TTL: specifies the value of TTL field set at a
+ *	source mesh point for path selection elements.
+ *
+ * @NL80211_MESHCONF_HWMP_RANN_INTERVAL:  The interval of time (in TUs) between
+ *	root announcements are transmitted.
+ *
+ * @NL80211_MESHCONF_GATE_ANNOUNCEMENTS: Advertise that this mesh station has
+ *	access to a broader network beyond the MBSS.  This is done via Root
+ *	Announcement frames.
+ *
+ * @NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL: The minimum interval of time (in
+ *	TUs) during which a mesh STA can send only one Action frame containing a
+ *	PERR element.
+ *
+ * @NL80211_MESHCONF_FORWARDING: set Mesh STA as forwarding or non-forwarding
+ *	or forwarding entity (default is TRUE - forwarding entity)
+ *
+ * @NL80211_MESHCONF_RSSI_THRESHOLD: RSSI threshold in dBm. This specifies the
+ *	threshold for average signal strength of candidate station to establish
+ *	a peer link.
+ *
+ * @NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR: maximum number of neighbors
+ *	to synchronize to for 11s default synchronization method
+ *	(see 11C.12.2.2)
+ *
+ * @NL80211_MESHCONF_HT_OPMODE: set mesh HT protection mode.
+ *
+ * @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute
+ *
+ * @NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT: The time (in TUs) for
+ *	which mesh STAs receiving a proactive PREQ shall consider the forwarding
+ *	information to the root mesh STA to be valid.
+ *
+ * @NL80211_MESHCONF_HWMP_ROOT_INTERVAL: The interval of time (in TUs) between
+ *	proactive PREQs are transmitted.
+ *
+ * @NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL: The minimum interval of time
+ *	(in TUs) during which a mesh STA can send only one Action frame
+ *	containing a PREQ element for root path confirmation.
+ *
+ * @NL80211_MESHCONF_POWER_MODE: Default mesh power mode for new peer links.
+ *	type &enum nl80211_mesh_power_mode (u32)
+ *
+ * @NL80211_MESHCONF_AWAKE_WINDOW: awake window duration (in TUs)
+ *
+ * @NL80211_MESHCONF_PLINK_TIMEOUT: If no tx activity is seen from a STA we've
+ *	established peering with for longer than this time (in seconds), then
+ *	remove it from the STA's list of peers. You may set this to 0 to disable
+ *	the removal of the STA. Default is 30 minutes.
+ *
+ * @NL80211_MESHCONF_CONNECTED_TO_GATE: If set to true then this mesh STA
+ *	will advertise that it is connected to a gate in the mesh formation
+ *	field.  If left unset then the mesh formation field will only
+ *	advertise such if there is an active root mesh path.
+ *
+ * @NL80211_MESHCONF_NOLEARN: Try to avoid multi-hop path discovery (e.g.
+ *      PREQ/PREP for HWMP) if the destination is a direct neighbor. Note that
+ *      this might not be the optimal decision as a multi-hop route might be
+ *      better. So if using this setting you will likely also want to disable
+ *      dot11MeshForwarding and use another mesh routing protocol on top.
+ *
+ * @NL80211_MESHCONF_CONNECTED_TO_AS: If set to true then this mesh STA
+ *	will advertise that it is connected to a authentication server
+ *	in the mesh formation field.
+ *
+ * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use
+ */
+enum nl80211_meshconf_params {
+	__NL80211_MESHCONF_INVALID,
+	NL80211_MESHCONF_RETRY_TIMEOUT,
+	NL80211_MESHCONF_CONFIRM_TIMEOUT,
+	NL80211_MESHCONF_HOLDING_TIMEOUT,
+	NL80211_MESHCONF_MAX_PEER_LINKS,
+	NL80211_MESHCONF_MAX_RETRIES,
+	NL80211_MESHCONF_TTL,
+	NL80211_MESHCONF_AUTO_OPEN_PLINKS,
+	NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
+	NL80211_MESHCONF_PATH_REFRESH_TIME,
+	NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
+	NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
+	NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
+	NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
+	NL80211_MESHCONF_HWMP_ROOTMODE,
+	NL80211_MESHCONF_ELEMENT_TTL,
+	NL80211_MESHCONF_HWMP_RANN_INTERVAL,
+	NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
+	NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL,
+	NL80211_MESHCONF_FORWARDING,
+	NL80211_MESHCONF_RSSI_THRESHOLD,
+	NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR,
+	NL80211_MESHCONF_HT_OPMODE,
+	NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT,
+	NL80211_MESHCONF_HWMP_ROOT_INTERVAL,
+	NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
+	NL80211_MESHCONF_POWER_MODE,
+	NL80211_MESHCONF_AWAKE_WINDOW,
+	NL80211_MESHCONF_PLINK_TIMEOUT,
+	NL80211_MESHCONF_CONNECTED_TO_GATE,
+	NL80211_MESHCONF_NOLEARN,
+	NL80211_MESHCONF_CONNECTED_TO_AS,
+
+	/* keep last */
+	__NL80211_MESHCONF_ATTR_AFTER_LAST,
+	NL80211_MESHCONF_ATTR_MAX = __NL80211_MESHCONF_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_mesh_setup_params - mesh setup parameters
+ *
+ * Mesh setup parameters.  These are used to start/join a mesh and cannot be
+ * changed while the mesh is active.
+ *
+ * @__NL80211_MESH_SETUP_INVALID: Internal use
+ *
+ * @NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL: Enable this option to use a
+ *	vendor specific path selection algorithm or disable it to use the
+ *	default HWMP.
+ *
+ * @NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC: Enable this option to use a
+ *	vendor specific path metric or disable it to use the default Airtime
+ *	metric.
+ *
+ * @NL80211_MESH_SETUP_IE: Information elements for this mesh, for instance, a
+ *	robust security network ie, or a vendor specific information element
+ *	that vendors will use to identify the path selection methods and
+ *	metrics in use.
+ *
+ * @NL80211_MESH_SETUP_USERSPACE_AUTH: Enable this option if an authentication
+ *	daemon will be authenticating mesh candidates.
+ *
+ * @NL80211_MESH_SETUP_USERSPACE_AMPE: Enable this option if an authentication
+ *	daemon will be securing peer link frames.  AMPE is a secured version of
+ *	Mesh Peering Management (MPM) and is implemented with the assistance of
+ *	a userspace daemon.  When this flag is set, the kernel will send peer
+ *	management frames to a userspace daemon that will implement AMPE
+ *	functionality (security capabilities selection, key confirmation, and
+ *	key management).  When the flag is unset (default), the kernel can
+ *	autonomously complete (unsecured) mesh peering without the need of a
+ *	userspace daemon.
+ *
+ * @NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC: Enable this option to use a
+ *	vendor specific synchronization method or disable it to use the default
+ *	neighbor offset synchronization
+ *
+ * @NL80211_MESH_SETUP_USERSPACE_MPM: Enable this option if userspace will
+ *	implement an MPM which handles peer allocation and state.
+ *
+ * @NL80211_MESH_SETUP_AUTH_PROTOCOL: Inform the kernel of the authentication
+ *	method (u8, as defined in IEEE 8.4.2.100.6, e.g. 0x1 for SAE).
+ *	Default is no authentication method required.
+ *
+ * @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number
+ *
+ * @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use
+ */
+enum nl80211_mesh_setup_params {
+	__NL80211_MESH_SETUP_INVALID,
+	NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL,
+	NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC,
+	NL80211_MESH_SETUP_IE,
+	NL80211_MESH_SETUP_USERSPACE_AUTH,
+	NL80211_MESH_SETUP_USERSPACE_AMPE,
+	NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC,
+	NL80211_MESH_SETUP_USERSPACE_MPM,
+	NL80211_MESH_SETUP_AUTH_PROTOCOL,
+
+	/* keep last */
+	__NL80211_MESH_SETUP_ATTR_AFTER_LAST,
+	NL80211_MESH_SETUP_ATTR_MAX = __NL80211_MESH_SETUP_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_txq_attr - TX queue parameter attributes
+ * @__NL80211_TXQ_ATTR_INVALID: Attribute number 0 is reserved
+ * @NL80211_TXQ_ATTR_AC: AC identifier (NL80211_AC_*)
+ * @NL80211_TXQ_ATTR_TXOP: Maximum burst time in units of 32 usecs, 0 meaning
+ *	disabled
+ * @NL80211_TXQ_ATTR_CWMIN: Minimum contention window [a value of the form
+ *	2^n-1 in the range 1..32767]
+ * @NL80211_TXQ_ATTR_CWMAX: Maximum contention window [a value of the form
+ *	2^n-1 in the range 1..32767]
+ * @NL80211_TXQ_ATTR_AIFS: Arbitration interframe space [0..255]
+ * @__NL80211_TXQ_ATTR_AFTER_LAST: Internal
+ * @NL80211_TXQ_ATTR_MAX: Maximum TXQ attribute number
+ */
+enum nl80211_txq_attr {
+	__NL80211_TXQ_ATTR_INVALID,
+	NL80211_TXQ_ATTR_AC,
+	NL80211_TXQ_ATTR_TXOP,
+	NL80211_TXQ_ATTR_CWMIN,
+	NL80211_TXQ_ATTR_CWMAX,
+	NL80211_TXQ_ATTR_AIFS,
+
+	/* keep last */
+	__NL80211_TXQ_ATTR_AFTER_LAST,
+	NL80211_TXQ_ATTR_MAX = __NL80211_TXQ_ATTR_AFTER_LAST - 1
+};
+
+enum nl80211_ac {
+	NL80211_AC_VO,
+	NL80211_AC_VI,
+	NL80211_AC_BE,
+	NL80211_AC_BK,
+	NL80211_NUM_ACS
+};
+
+/* backward compat */
+#define NL80211_TXQ_ATTR_QUEUE	NL80211_TXQ_ATTR_AC
+#define NL80211_TXQ_Q_VO	NL80211_AC_VO
+#define NL80211_TXQ_Q_VI	NL80211_AC_VI
+#define NL80211_TXQ_Q_BE	NL80211_AC_BE
+#define NL80211_TXQ_Q_BK	NL80211_AC_BK
+
+/**
+ * enum nl80211_channel_type - channel type
+ * @NL80211_CHAN_NO_HT: 20 MHz, non-HT channel
+ * @NL80211_CHAN_HT20: 20 MHz HT channel
+ * @NL80211_CHAN_HT40MINUS: HT40 channel, secondary channel
+ *	below the control channel
+ * @NL80211_CHAN_HT40PLUS: HT40 channel, secondary channel
+ *	above the control channel
+ */
+enum nl80211_channel_type {
+	NL80211_CHAN_NO_HT,
+	NL80211_CHAN_HT20,
+	NL80211_CHAN_HT40MINUS,
+	NL80211_CHAN_HT40PLUS
+};
+
+/**
+ * enum nl80211_key_mode - Key mode
+ *
+ * @NL80211_KEY_RX_TX: (Default)
+ *	Key can be used for Rx and Tx immediately
+ *
+ * The following modes can only be selected for unicast keys and when the
+ * driver supports @NL80211_EXT_FEATURE_EXT_KEY_ID:
+ *
+ * @NL80211_KEY_NO_TX: Only allowed in combination with @NL80211_CMD_NEW_KEY:
+ *	Unicast key can only be used for Rx, Tx not allowed, yet
+ * @NL80211_KEY_SET_TX: Only allowed in combination with @NL80211_CMD_SET_KEY:
+ *	The unicast key identified by idx and mac is cleared for Tx and becomes
+ *	the preferred Tx key for the station.
+ */
+enum nl80211_key_mode {
+	NL80211_KEY_RX_TX,
+	NL80211_KEY_NO_TX,
+	NL80211_KEY_SET_TX
+};
+
+/**
+ * enum nl80211_chan_width - channel width definitions
+ *
+ * These values are used with the %NL80211_ATTR_CHANNEL_WIDTH
+ * attribute.
+ *
+ * @NL80211_CHAN_WIDTH_20_NOHT: 20 MHz, non-HT channel
+ * @NL80211_CHAN_WIDTH_20: 20 MHz HT channel
+ * @NL80211_CHAN_WIDTH_40: 40 MHz channel, the %NL80211_ATTR_CENTER_FREQ1
+ *	attribute must be provided as well
+ * @NL80211_CHAN_WIDTH_80: 80 MHz channel, the %NL80211_ATTR_CENTER_FREQ1
+ *	attribute must be provided as well
+ * @NL80211_CHAN_WIDTH_80P80: 80+80 MHz channel, the %NL80211_ATTR_CENTER_FREQ1
+ *	and %NL80211_ATTR_CENTER_FREQ2 attributes must be provided as well
+ * @NL80211_CHAN_WIDTH_160: 160 MHz channel, the %NL80211_ATTR_CENTER_FREQ1
+ *	attribute must be provided as well
+ * @NL80211_CHAN_WIDTH_5: 5 MHz OFDM channel
+ * @NL80211_CHAN_WIDTH_10: 10 MHz OFDM channel
+ * @NL80211_CHAN_WIDTH_1: 1 MHz OFDM channel
+ * @NL80211_CHAN_WIDTH_2: 2 MHz OFDM channel
+ * @NL80211_CHAN_WIDTH_4: 4 MHz OFDM channel
+ * @NL80211_CHAN_WIDTH_8: 8 MHz OFDM channel
+ * @NL80211_CHAN_WIDTH_16: 16 MHz OFDM channel
+ * @NL80211_CHAN_WIDTH_320: 320 MHz channel, the %NL80211_ATTR_CENTER_FREQ1
+ *	attribute must be provided as well
+ */
+enum nl80211_chan_width {
+	NL80211_CHAN_WIDTH_20_NOHT,
+	NL80211_CHAN_WIDTH_20,
+	NL80211_CHAN_WIDTH_40,
+	NL80211_CHAN_WIDTH_80,
+	NL80211_CHAN_WIDTH_80P80,
+	NL80211_CHAN_WIDTH_160,
+	NL80211_CHAN_WIDTH_5,
+	NL80211_CHAN_WIDTH_10,
+	NL80211_CHAN_WIDTH_1,
+	NL80211_CHAN_WIDTH_2,
+	NL80211_CHAN_WIDTH_4,
+	NL80211_CHAN_WIDTH_8,
+	NL80211_CHAN_WIDTH_16,
+	NL80211_CHAN_WIDTH_320,
+};
+
+/**
+ * enum nl80211_bss_scan_width - control channel width for a BSS
+ *
+ * These values are used with the %NL80211_BSS_CHAN_WIDTH attribute.
+ *
+ * @NL80211_BSS_CHAN_WIDTH_20: control channel is 20 MHz wide or compatible
+ * @NL80211_BSS_CHAN_WIDTH_10: control channel is 10 MHz wide
+ * @NL80211_BSS_CHAN_WIDTH_5: control channel is 5 MHz wide
+ * @NL80211_BSS_CHAN_WIDTH_1: control channel is 1 MHz wide
+ * @NL80211_BSS_CHAN_WIDTH_2: control channel is 2 MHz wide
+ */
+enum nl80211_bss_scan_width {
+	NL80211_BSS_CHAN_WIDTH_20,
+	NL80211_BSS_CHAN_WIDTH_10,
+	NL80211_BSS_CHAN_WIDTH_5,
+	NL80211_BSS_CHAN_WIDTH_1,
+	NL80211_BSS_CHAN_WIDTH_2,
+};
+
+/**
+ * enum nl80211_bss - netlink attributes for a BSS
+ *
+ * @__NL80211_BSS_INVALID: invalid
+ * @NL80211_BSS_BSSID: BSSID of the BSS (6 octets)
+ * @NL80211_BSS_FREQUENCY: frequency in MHz (u32)
+ * @NL80211_BSS_TSF: TSF of the received probe response/beacon (u64)
+ *	(if @NL80211_BSS_PRESP_DATA is present then this is known to be
+ *	from a probe response, otherwise it may be from the same beacon
+ *	that the NL80211_BSS_BEACON_TSF will be from)
+ * @NL80211_BSS_BEACON_INTERVAL: beacon interval of the (I)BSS (u16)
+ * @NL80211_BSS_CAPABILITY: capability field (CPU order, u16)
+ * @NL80211_BSS_INFORMATION_ELEMENTS: binary attribute containing the
+ *	raw information elements from the probe response/beacon (bin);
+ *	if the %NL80211_BSS_BEACON_IES attribute is present and the data is
+ *	different then the IEs here are from a Probe Response frame; otherwise
+ *	they are from a Beacon frame.
+ *	However, if the driver does not indicate the source of the IEs, these
+ *	IEs may be from either frame subtype.
+ *	If present, the @NL80211_BSS_PRESP_DATA attribute indicates that the
+ *	data here is known to be from a probe response, without any heuristics.
+ * @NL80211_BSS_SIGNAL_MBM: signal strength of probe response/beacon
+ *	in mBm (100 * dBm) (s32)
+ * @NL80211_BSS_SIGNAL_UNSPEC: signal strength of the probe response/beacon
+ *	in unspecified units, scaled to 0..100 (u8)
+ * @NL80211_BSS_STATUS: status, if this BSS is "used"
+ * @NL80211_BSS_SEEN_MS_AGO: age of this BSS entry in ms
+ * @NL80211_BSS_BEACON_IES: binary attribute containing the raw information
+ *	elements from a Beacon frame (bin); not present if no Beacon frame has
+ *	yet been received
+ * @NL80211_BSS_CHAN_WIDTH: channel width of the control channel
+ *	(u32, enum nl80211_bss_scan_width)
+ * @NL80211_BSS_BEACON_TSF: TSF of the last received beacon (u64)
+ *	(not present if no beacon frame has been received yet)
+ * @NL80211_BSS_PRESP_DATA: the data in @NL80211_BSS_INFORMATION_ELEMENTS and
+ *	@NL80211_BSS_TSF is known to be from a probe response (flag attribute)
+ * @NL80211_BSS_LAST_SEEN_BOOTTIME: CLOCK_BOOTTIME timestamp when this entry
+ *	was last updated by a received frame. The value is expected to be
+ *	accurate to about 10ms. (u64, nanoseconds)
+ * @NL80211_BSS_PAD: attribute used for padding for 64-bit alignment
+ * @NL80211_BSS_PARENT_TSF: the time at the start of reception of the first
+ *	octet of the timestamp field of the last beacon/probe received for
+ *	this BSS. The time is the TSF of the BSS specified by
+ *	@NL80211_BSS_PARENT_BSSID. (u64).
+ * @NL80211_BSS_PARENT_BSSID: the BSS according to which @NL80211_BSS_PARENT_TSF
+ *	is set.
+ * @NL80211_BSS_CHAIN_SIGNAL: per-chain signal strength of last BSS update.
+ *	Contains a nested array of signal strength attributes (u8, dBm),
+ *	using the nesting index as the antenna number.
+ * @NL80211_BSS_FREQUENCY_OFFSET: frequency offset in KHz
+ * @NL80211_BSS_MLO_LINK_ID: MLO link ID of the BSS (u8).
+ * @NL80211_BSS_MLD_ADDR: MLD address of this BSS if connected to it.
+ * @__NL80211_BSS_AFTER_LAST: internal
+ * @NL80211_BSS_MAX: highest BSS attribute
+ */
+enum nl80211_bss {
+	__NL80211_BSS_INVALID,
+	NL80211_BSS_BSSID,
+	NL80211_BSS_FREQUENCY,
+	NL80211_BSS_TSF,
+	NL80211_BSS_BEACON_INTERVAL,
+	NL80211_BSS_CAPABILITY,
+	NL80211_BSS_INFORMATION_ELEMENTS,
+	NL80211_BSS_SIGNAL_MBM,
+	NL80211_BSS_SIGNAL_UNSPEC,
+	NL80211_BSS_STATUS,
+	NL80211_BSS_SEEN_MS_AGO,
+	NL80211_BSS_BEACON_IES,
+	NL80211_BSS_CHAN_WIDTH,
+	NL80211_BSS_BEACON_TSF,
+	NL80211_BSS_PRESP_DATA,
+	NL80211_BSS_LAST_SEEN_BOOTTIME,
+	NL80211_BSS_PAD,
+	NL80211_BSS_PARENT_TSF,
+	NL80211_BSS_PARENT_BSSID,
+	NL80211_BSS_CHAIN_SIGNAL,
+	NL80211_BSS_FREQUENCY_OFFSET,
+	NL80211_BSS_MLO_LINK_ID,
+	NL80211_BSS_MLD_ADDR,
+
+	/* keep last */
+	__NL80211_BSS_AFTER_LAST,
+	NL80211_BSS_MAX = __NL80211_BSS_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_bss_status - BSS "status"
+ * @NL80211_BSS_STATUS_AUTHENTICATED: Authenticated with this BSS.
+ *	Note that this is no longer used since cfg80211 no longer
+ *	keeps track of whether or not authentication was done with
+ *	a given BSS.
+ * @NL80211_BSS_STATUS_ASSOCIATED: Associated with this BSS.
+ * @NL80211_BSS_STATUS_IBSS_JOINED: Joined to this IBSS.
+ *
+ * The BSS status is a BSS attribute in scan dumps, which
+ * indicates the status the interface has wrt. this BSS.
+ */
+enum nl80211_bss_status {
+	NL80211_BSS_STATUS_AUTHENTICATED,
+	NL80211_BSS_STATUS_ASSOCIATED,
+	NL80211_BSS_STATUS_IBSS_JOINED,
+};
+
+/**
+ * enum nl80211_auth_type - AuthenticationType
+ *
+ * @NL80211_AUTHTYPE_OPEN_SYSTEM: Open System authentication
+ * @NL80211_AUTHTYPE_SHARED_KEY: Shared Key authentication (WEP only)
+ * @NL80211_AUTHTYPE_FT: Fast BSS Transition (IEEE 802.11r)
+ * @NL80211_AUTHTYPE_NETWORK_EAP: Network EAP (some Cisco APs and mainly LEAP)
+ * @NL80211_AUTHTYPE_SAE: Simultaneous authentication of equals
+ * @NL80211_AUTHTYPE_FILS_SK: Fast Initial Link Setup shared key
+ * @NL80211_AUTHTYPE_FILS_SK_PFS: Fast Initial Link Setup shared key with PFS
+ * @NL80211_AUTHTYPE_FILS_PK: Fast Initial Link Setup public key
+ * @__NL80211_AUTHTYPE_NUM: internal
+ * @NL80211_AUTHTYPE_MAX: maximum valid auth algorithm
+ * @NL80211_AUTHTYPE_AUTOMATIC: determine automatically (if necessary by
+ *	trying multiple times); this is invalid in netlink -- leave out
+ *	the attribute for this on CONNECT commands.
+ */
+enum nl80211_auth_type {
+	NL80211_AUTHTYPE_OPEN_SYSTEM,
+	NL80211_AUTHTYPE_SHARED_KEY,
+	NL80211_AUTHTYPE_FT,
+	NL80211_AUTHTYPE_NETWORK_EAP,
+	NL80211_AUTHTYPE_SAE,
+	NL80211_AUTHTYPE_FILS_SK,
+	NL80211_AUTHTYPE_FILS_SK_PFS,
+	NL80211_AUTHTYPE_FILS_PK,
+
+	/* keep last */
+	__NL80211_AUTHTYPE_NUM,
+	NL80211_AUTHTYPE_MAX = __NL80211_AUTHTYPE_NUM - 1,
+	NL80211_AUTHTYPE_AUTOMATIC
+};
+
+/**
+ * enum nl80211_key_type - Key Type
+ * @NL80211_KEYTYPE_GROUP: Group (broadcast/multicast) key
+ * @NL80211_KEYTYPE_PAIRWISE: Pairwise (unicast/individual) key
+ * @NL80211_KEYTYPE_PEERKEY: PeerKey (DLS)
+ * @NUM_NL80211_KEYTYPES: number of defined key types
+ */
+enum nl80211_key_type {
+	NL80211_KEYTYPE_GROUP,
+	NL80211_KEYTYPE_PAIRWISE,
+	NL80211_KEYTYPE_PEERKEY,
+
+	NUM_NL80211_KEYTYPES
+};
+
+/**
+ * enum nl80211_mfp - Management frame protection state
+ * @NL80211_MFP_NO: Management frame protection not used
+ * @NL80211_MFP_REQUIRED: Management frame protection required
+ * @NL80211_MFP_OPTIONAL: Management frame protection is optional
+ */
+enum nl80211_mfp {
+	NL80211_MFP_NO,
+	NL80211_MFP_REQUIRED,
+	NL80211_MFP_OPTIONAL,
+};
+
+enum nl80211_wpa_versions {
+	NL80211_WPA_VERSION_1 = 1 << 0,
+	NL80211_WPA_VERSION_2 = 1 << 1,
+	NL80211_WPA_VERSION_3 = 1 << 2,
+};
+
+/**
+ * enum nl80211_key_default_types - key default types
+ * @__NL80211_KEY_DEFAULT_TYPE_INVALID: invalid
+ * @NL80211_KEY_DEFAULT_TYPE_UNICAST: key should be used as default
+ *	unicast key
+ * @NL80211_KEY_DEFAULT_TYPE_MULTICAST: key should be used as default
+ *	multicast key
+ * @NUM_NL80211_KEY_DEFAULT_TYPES: number of default types
+ */
+enum nl80211_key_default_types {
+	__NL80211_KEY_DEFAULT_TYPE_INVALID,
+	NL80211_KEY_DEFAULT_TYPE_UNICAST,
+	NL80211_KEY_DEFAULT_TYPE_MULTICAST,
+
+	NUM_NL80211_KEY_DEFAULT_TYPES
+};
+
+/**
+ * enum nl80211_key_attributes - key attributes
+ * @__NL80211_KEY_INVALID: invalid
+ * @NL80211_KEY_DATA: (temporal) key data; for TKIP this consists of
+ *	16 bytes encryption key followed by 8 bytes each for TX and RX MIC
+ *	keys
+ * @NL80211_KEY_IDX: key ID (u8, 0-3)
+ * @NL80211_KEY_CIPHER: key cipher suite (u32, as defined by IEEE 802.11
+ *	section 7.3.2.25.1, e.g. 0x000FAC04)
+ * @NL80211_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and
+ *	CCMP keys, each six bytes in little endian
+ * @NL80211_KEY_DEFAULT: flag indicating default key
+ * @NL80211_KEY_DEFAULT_MGMT: flag indicating default management key
+ * @NL80211_KEY_TYPE: the key type from enum nl80211_key_type, if not
+ *	specified the default depends on whether a MAC address was
+ *	given with the command using the key or not (u32)
+ * @NL80211_KEY_DEFAULT_TYPES: A nested attribute containing flags
+ *	attributes, specifying what a key should be set as default as.
+ *	See &enum nl80211_key_default_types.
+ * @NL80211_KEY_MODE: the mode from enum nl80211_key_mode.
+ *	Defaults to @NL80211_KEY_RX_TX.
+ * @NL80211_KEY_DEFAULT_BEACON: flag indicating default Beacon frame key
+ *
+ * @__NL80211_KEY_AFTER_LAST: internal
+ * @NL80211_KEY_MAX: highest key attribute
+ */
+enum nl80211_key_attributes {
+	__NL80211_KEY_INVALID,
+	NL80211_KEY_DATA,
+	NL80211_KEY_IDX,
+	NL80211_KEY_CIPHER,
+	NL80211_KEY_SEQ,
+	NL80211_KEY_DEFAULT,
+	NL80211_KEY_DEFAULT_MGMT,
+	NL80211_KEY_TYPE,
+	NL80211_KEY_DEFAULT_TYPES,
+	NL80211_KEY_MODE,
+	NL80211_KEY_DEFAULT_BEACON,
+
+	/* keep last */
+	__NL80211_KEY_AFTER_LAST,
+	NL80211_KEY_MAX = __NL80211_KEY_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_tx_rate_attributes - TX rate set attributes
+ * @__NL80211_TXRATE_INVALID: invalid
+ * @NL80211_TXRATE_LEGACY: Legacy (non-MCS) rates allowed for TX rate selection
+ *	in an array of rates as defined in IEEE 802.11 7.3.2.2 (u8 values with
+ *	1 = 500 kbps) but without the IE length restriction (at most
+ *	%NL80211_MAX_SUPP_RATES in a single array).
+ * @NL80211_TXRATE_HT: HT (MCS) rates allowed for TX rate selection
+ *	in an array of MCS numbers.
+ * @NL80211_TXRATE_VHT: VHT rates allowed for TX rate selection,
+ *	see &struct nl80211_txrate_vht
+ * @NL80211_TXRATE_GI: configure GI, see &enum nl80211_txrate_gi
+ * @NL80211_TXRATE_HE: HE rates allowed for TX rate selection,
+ *	see &struct nl80211_txrate_he
+ * @NL80211_TXRATE_HE_GI: configure HE GI, 0.8us, 1.6us and 3.2us.
+ * @NL80211_TXRATE_HE_LTF: configure HE LTF, 1XLTF, 2XLTF and 4XLTF.
+ * @__NL80211_TXRATE_AFTER_LAST: internal
+ * @NL80211_TXRATE_MAX: highest TX rate attribute
+ */
+enum nl80211_tx_rate_attributes {
+	__NL80211_TXRATE_INVALID,
+	NL80211_TXRATE_LEGACY,
+	NL80211_TXRATE_HT,
+	NL80211_TXRATE_VHT,
+	NL80211_TXRATE_GI,
+	NL80211_TXRATE_HE,
+	NL80211_TXRATE_HE_GI,
+	NL80211_TXRATE_HE_LTF,
+
+	/* keep last */
+	__NL80211_TXRATE_AFTER_LAST,
+	NL80211_TXRATE_MAX = __NL80211_TXRATE_AFTER_LAST - 1
+};
+
+#define NL80211_TXRATE_MCS NL80211_TXRATE_HT
+#define NL80211_VHT_NSS_MAX		8
+
+/**
+ * struct nl80211_txrate_vht - VHT MCS/NSS txrate bitmap
+ * @mcs: MCS bitmap table for each NSS (array index 0 for 1 stream, etc.)
+ */
+struct nl80211_txrate_vht {
+	__u16 mcs[NL80211_VHT_NSS_MAX];
+};
+
+#define NL80211_HE_NSS_MAX		8
+/**
+ * struct nl80211_txrate_he - HE MCS/NSS txrate bitmap
+ * @mcs: MCS bitmap table for each NSS (array index 0 for 1 stream, etc.)
+ */
+struct nl80211_txrate_he {
+	__u16 mcs[NL80211_HE_NSS_MAX];
+};
+
+enum nl80211_txrate_gi {
+	NL80211_TXRATE_DEFAULT_GI,
+	NL80211_TXRATE_FORCE_SGI,
+	NL80211_TXRATE_FORCE_LGI,
+};
+
+/**
+ * enum nl80211_band - Frequency band
+ * @NL80211_BAND_2GHZ: 2.4 GHz ISM band
+ * @NL80211_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz)
+ * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 69.12 GHz)
+ * @NL80211_BAND_6GHZ: around 6 GHz band (5.9 - 7.2 GHz)
+ * @NL80211_BAND_S1GHZ: around 900MHz, supported by S1G PHYs
+ * @NL80211_BAND_LC: light communication band (placeholder)
+ * @NUM_NL80211_BANDS: number of bands, avoid using this in userspace
+ *	since newer kernel versions may support more bands
+ */
+enum nl80211_band {
+	NL80211_BAND_2GHZ,
+	NL80211_BAND_5GHZ,
+	NL80211_BAND_60GHZ,
+	NL80211_BAND_6GHZ,
+	NL80211_BAND_S1GHZ,
+	NL80211_BAND_LC,
+
+	NUM_NL80211_BANDS,
+};
+
+/**
+ * enum nl80211_ps_state - powersave state
+ * @NL80211_PS_DISABLED: powersave is disabled
+ * @NL80211_PS_ENABLED: powersave is enabled
+ */
+enum nl80211_ps_state {
+	NL80211_PS_DISABLED,
+	NL80211_PS_ENABLED,
+};
+
+/**
+ * enum nl80211_attr_cqm - connection quality monitor attributes
+ * @__NL80211_ATTR_CQM_INVALID: invalid
+ * @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm. This value specifies
+ *	the threshold for the RSSI level at which an event will be sent. Zero
+ *	to disable.  Alternatively, if %NL80211_EXT_FEATURE_CQM_RSSI_LIST is
+ *	set, multiple values can be supplied as a low-to-high sorted array of
+ *	threshold values in dBm.  Events will be sent when the RSSI value
+ *	crosses any of the thresholds.
+ * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm. This value specifies
+ *	the minimum amount the RSSI level must change after an event before a
+ *	new event may be issued (to reduce effects of RSSI oscillation).
+ * @NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: RSSI threshold event
+ * @NL80211_ATTR_CQM_PKT_LOSS_EVENT: a u32 value indicating that this many
+ *	consecutive packets were not acknowledged by the peer
+ * @NL80211_ATTR_CQM_TXE_RATE: TX error rate in %. Minimum % of TX failures
+ *	during the given %NL80211_ATTR_CQM_TXE_INTVL before an
+ *	%NL80211_CMD_NOTIFY_CQM with reported %NL80211_ATTR_CQM_TXE_RATE and
+ *	%NL80211_ATTR_CQM_TXE_PKTS is generated.
+ * @NL80211_ATTR_CQM_TXE_PKTS: number of attempted packets in a given
+ *	%NL80211_ATTR_CQM_TXE_INTVL before %NL80211_ATTR_CQM_TXE_RATE is
+ *	checked.
+ * @NL80211_ATTR_CQM_TXE_INTVL: interval in seconds. Specifies the periodic
+ *	interval in which %NL80211_ATTR_CQM_TXE_PKTS and
+ *	%NL80211_ATTR_CQM_TXE_RATE must be satisfied before generating an
+ *	%NL80211_CMD_NOTIFY_CQM. Set to 0 to turn off TX error reporting.
+ * @NL80211_ATTR_CQM_BEACON_LOSS_EVENT: flag attribute that's set in a beacon
+ *	loss event
+ * @NL80211_ATTR_CQM_RSSI_LEVEL: the RSSI value in dBm that triggered the
+ *	RSSI threshold event.
+ * @__NL80211_ATTR_CQM_AFTER_LAST: internal
+ * @NL80211_ATTR_CQM_MAX: highest key attribute
+ */
+enum nl80211_attr_cqm {
+	__NL80211_ATTR_CQM_INVALID,
+	NL80211_ATTR_CQM_RSSI_THOLD,
+	NL80211_ATTR_CQM_RSSI_HYST,
+	NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
+	NL80211_ATTR_CQM_PKT_LOSS_EVENT,
+	NL80211_ATTR_CQM_TXE_RATE,
+	NL80211_ATTR_CQM_TXE_PKTS,
+	NL80211_ATTR_CQM_TXE_INTVL,
+	NL80211_ATTR_CQM_BEACON_LOSS_EVENT,
+	NL80211_ATTR_CQM_RSSI_LEVEL,
+
+	/* keep last */
+	__NL80211_ATTR_CQM_AFTER_LAST,
+	NL80211_ATTR_CQM_MAX = __NL80211_ATTR_CQM_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_cqm_rssi_threshold_event - RSSI threshold event
+ * @NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW: The RSSI level is lower than the
+ *      configured threshold
+ * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH: The RSSI is higher than the
+ *      configured threshold
+ * @NL80211_CQM_RSSI_BEACON_LOSS_EVENT: (reserved, never sent)
+ */
+enum nl80211_cqm_rssi_threshold_event {
+	NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
+	NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+	NL80211_CQM_RSSI_BEACON_LOSS_EVENT,
+};
+
+
+/**
+ * enum nl80211_tx_power_setting - TX power adjustment
+ * @NL80211_TX_POWER_AUTOMATIC: automatically determine transmit power
+ * @NL80211_TX_POWER_LIMITED: limit TX power by the mBm parameter
+ * @NL80211_TX_POWER_FIXED: fix TX power to the mBm parameter
+ */
+enum nl80211_tx_power_setting {
+	NL80211_TX_POWER_AUTOMATIC,
+	NL80211_TX_POWER_LIMITED,
+	NL80211_TX_POWER_FIXED,
+};
+
+/**
+ * enum nl80211_tid_config - TID config state
+ * @NL80211_TID_CONFIG_ENABLE: Enable config for the TID
+ * @NL80211_TID_CONFIG_DISABLE: Disable config for the TID
+ */
+enum nl80211_tid_config {
+	NL80211_TID_CONFIG_ENABLE,
+	NL80211_TID_CONFIG_DISABLE,
+};
+
+/* enum nl80211_tx_rate_setting - TX rate configuration type
+ * @NL80211_TX_RATE_AUTOMATIC: automatically determine TX rate
+ * @NL80211_TX_RATE_LIMITED: limit the TX rate by the TX rate parameter
+ * @NL80211_TX_RATE_FIXED: fix TX rate to the TX rate parameter
+ */
+enum nl80211_tx_rate_setting {
+	NL80211_TX_RATE_AUTOMATIC,
+	NL80211_TX_RATE_LIMITED,
+	NL80211_TX_RATE_FIXED,
+};
+
+/* enum nl80211_tid_config_attr - TID specific configuration.
+ * @NL80211_TID_CONFIG_ATTR_PAD: pad attribute for 64-bit values
+ * @NL80211_TID_CONFIG_ATTR_VIF_SUPP: a bitmap (u64) of attributes supported
+ *	for per-vif configuration; doesn't list the ones that are generic
+ *	(%NL80211_TID_CONFIG_ATTR_TIDS, %NL80211_TID_CONFIG_ATTR_OVERRIDE).
+ * @NL80211_TID_CONFIG_ATTR_PEER_SUPP: same as the previous per-vif one, but
+ *	per peer instead.
+ * @NL80211_TID_CONFIG_ATTR_OVERRIDE: flag attribue, if set indicates
+ *	that the new configuration overrides all previous peer
+ *	configurations, otherwise previous peer specific configurations
+ *	should be left untouched.
+ * @NL80211_TID_CONFIG_ATTR_TIDS: a bitmask value of TIDs (bit 0 to 7)
+ *	Its type is u16.
+ * @NL80211_TID_CONFIG_ATTR_NOACK: Configure ack policy for the TID.
+ *	specified in %NL80211_TID_CONFIG_ATTR_TID. see %enum nl80211_tid_config.
+ *	Its type is u8.
+ * @NL80211_TID_CONFIG_ATTR_RETRY_SHORT: Number of retries used with data frame
+ *	transmission, user-space sets this configuration in
+ *	&NL80211_CMD_SET_TID_CONFIG. It is u8 type, min value is 1 and
+ *	the max value is advertised by the driver in this attribute on
+ *	output in wiphy capabilities.
+ * @NL80211_TID_CONFIG_ATTR_RETRY_LONG: Number of retries used with data frame
+ *	transmission, user-space sets this configuration in
+ *	&NL80211_CMD_SET_TID_CONFIG. Its type is u8, min value is 1 and
+ *	the max value is advertised by the driver in this attribute on
+ *	output in wiphy capabilities.
+ * @NL80211_TID_CONFIG_ATTR_AMPDU_CTRL: Enable/Disable MPDU aggregation
+ *	for the TIDs specified in %NL80211_TID_CONFIG_ATTR_TIDS.
+ *	Its type is u8, using the values from &nl80211_tid_config.
+ * @NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL: Enable/Disable RTS_CTS for the TIDs
+ *	specified in %NL80211_TID_CONFIG_ATTR_TIDS. It is u8 type, using
+ *	the values from &nl80211_tid_config.
+ * @NL80211_TID_CONFIG_ATTR_AMSDU_CTRL: Enable/Disable MSDU aggregation
+ *	for the TIDs specified in %NL80211_TID_CONFIG_ATTR_TIDS.
+ *	Its type is u8, using the values from &nl80211_tid_config.
+ * @NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE: This attribute will be useful
+ *	to notfiy the driver that what type of txrate should be used
+ *	for the TIDs specified in %NL80211_TID_CONFIG_ATTR_TIDS. using
+ *	the values form &nl80211_tx_rate_setting.
+ * @NL80211_TID_CONFIG_ATTR_TX_RATE: Data frame TX rate mask should be applied
+ *	with the parameters passed through %NL80211_ATTR_TX_RATES.
+ *	configuration is applied to the data frame for the tid to that connected
+ *	station.
+ */
+enum nl80211_tid_config_attr {
+	__NL80211_TID_CONFIG_ATTR_INVALID,
+	NL80211_TID_CONFIG_ATTR_PAD,
+	NL80211_TID_CONFIG_ATTR_VIF_SUPP,
+	NL80211_TID_CONFIG_ATTR_PEER_SUPP,
+	NL80211_TID_CONFIG_ATTR_OVERRIDE,
+	NL80211_TID_CONFIG_ATTR_TIDS,
+	NL80211_TID_CONFIG_ATTR_NOACK,
+	NL80211_TID_CONFIG_ATTR_RETRY_SHORT,
+	NL80211_TID_CONFIG_ATTR_RETRY_LONG,
+	NL80211_TID_CONFIG_ATTR_AMPDU_CTRL,
+	NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL,
+	NL80211_TID_CONFIG_ATTR_AMSDU_CTRL,
+	NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE,
+	NL80211_TID_CONFIG_ATTR_TX_RATE,
+
+	/* keep last */
+	__NL80211_TID_CONFIG_ATTR_AFTER_LAST,
+	NL80211_TID_CONFIG_ATTR_MAX = __NL80211_TID_CONFIG_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_packet_pattern_attr - packet pattern attribute
+ * @__NL80211_PKTPAT_INVALID: invalid number for nested attribute
+ * @NL80211_PKTPAT_PATTERN: the pattern, values where the mask has
+ *	a zero bit are ignored
+ * @NL80211_PKTPAT_MASK: pattern mask, must be long enough to have
+ *	a bit for each byte in the pattern. The lowest-order bit corresponds
+ *	to the first byte of the pattern, but the bytes of the pattern are
+ *	in a little-endian-like format, i.e. the 9th byte of the pattern
+ *	corresponds to the lowest-order bit in the second byte of the mask.
+ *	For example: The match 00:xx:00:00:xx:00:00:00:00:xx:xx:xx (where
+ *	xx indicates "don't care") would be represented by a pattern of
+ *	twelve zero bytes, and a mask of "0xed,0x01".
+ *	Note that the pattern matching is done as though frames were not
+ *	802.11 frames but 802.3 frames, i.e. the frame is fully unpacked
+ *	first (including SNAP header unpacking) and then matched.
+ * @NL80211_PKTPAT_OFFSET: packet offset, pattern is matched after
+ *	these fixed number of bytes of received packet
+ * @NUM_NL80211_PKTPAT: number of attributes
+ * @MAX_NL80211_PKTPAT: max attribute number
+ */
+enum nl80211_packet_pattern_attr {
+	__NL80211_PKTPAT_INVALID,
+	NL80211_PKTPAT_MASK,
+	NL80211_PKTPAT_PATTERN,
+	NL80211_PKTPAT_OFFSET,
+
+	NUM_NL80211_PKTPAT,
+	MAX_NL80211_PKTPAT = NUM_NL80211_PKTPAT - 1,
+};
+
+/**
+ * struct nl80211_pattern_support - packet pattern support information
+ * @max_patterns: maximum number of patterns supported
+ * @min_pattern_len: minimum length of each pattern
+ * @max_pattern_len: maximum length of each pattern
+ * @max_pkt_offset: maximum Rx packet offset
+ *
+ * This struct is carried in %NL80211_WOWLAN_TRIG_PKT_PATTERN when
+ * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED or in
+ * %NL80211_ATTR_COALESCE_RULE_PKT_PATTERN when that is part of
+ * %NL80211_ATTR_COALESCE_RULE in the capability information given
+ * by the kernel to userspace.
+ */
+struct nl80211_pattern_support {
+	__u32 max_patterns;
+	__u32 min_pattern_len;
+	__u32 max_pattern_len;
+	__u32 max_pkt_offset;
+} __attribute__((packed));
+
+/* only for backward compatibility */
+#define __NL80211_WOWLAN_PKTPAT_INVALID __NL80211_PKTPAT_INVALID
+#define NL80211_WOWLAN_PKTPAT_MASK NL80211_PKTPAT_MASK
+#define NL80211_WOWLAN_PKTPAT_PATTERN NL80211_PKTPAT_PATTERN
+#define NL80211_WOWLAN_PKTPAT_OFFSET NL80211_PKTPAT_OFFSET
+#define NUM_NL80211_WOWLAN_PKTPAT NUM_NL80211_PKTPAT
+#define MAX_NL80211_WOWLAN_PKTPAT MAX_NL80211_PKTPAT
+#define nl80211_wowlan_pattern_support nl80211_pattern_support
+
+/**
+ * enum nl80211_wowlan_triggers - WoWLAN trigger definitions
+ * @__NL80211_WOWLAN_TRIG_INVALID: invalid number for nested attributes
+ * @NL80211_WOWLAN_TRIG_ANY: wake up on any activity, do not really put
+ *	the chip into a special state -- works best with chips that have
+ *	support for low-power operation already (flag)
+ *	Note that this mode is incompatible with all of the others, if
+ *	any others are even supported by the device.
+ * @NL80211_WOWLAN_TRIG_DISCONNECT: wake up on disconnect, the way disconnect
+ *	is detected is implementation-specific (flag)
+ * @NL80211_WOWLAN_TRIG_MAGIC_PKT: wake up on magic packet (6x 0xff, followed
+ *	by 16 repetitions of MAC addr, anywhere in payload) (flag)
+ * @NL80211_WOWLAN_TRIG_PKT_PATTERN: wake up on the specified packet patterns
+ *	which are passed in an array of nested attributes, each nested attribute
+ *	defining a with attributes from &struct nl80211_wowlan_trig_pkt_pattern.
+ *	Each pattern defines a wakeup packet. Packet offset is associated with
+ *	each pattern which is used while matching the pattern. The matching is
+ *	done on the MSDU, i.e. as though the packet was an 802.3 packet, so the
+ *	pattern matching is done after the packet is converted to the MSDU.
+ *
+ *	In %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, it is a binary attribute
+ *	carrying a &struct nl80211_pattern_support.
+ *
+ *	When reporting wakeup. it is a u32 attribute containing the 0-based
+ *	index of the pattern that caused the wakeup, in the patterns passed
+ *	to the kernel when configuring.
+ * @NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED: Not a real trigger, and cannot be
+ *	used when setting, used only to indicate that GTK rekeying is supported
+ *	by the device (flag)
+ * @NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE: wake up on GTK rekey failure (if
+ *	done by the device) (flag)
+ * @NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST: wake up on EAP Identity Request
+ *	packet (flag)
+ * @NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE: wake up on 4-way handshake (flag)
+ * @NL80211_WOWLAN_TRIG_RFKILL_RELEASE: wake up when rfkill is released
+ *	(on devices that have rfkill in the device) (flag)
+ * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211: For wakeup reporting only, contains
+ *	the 802.11 packet that caused the wakeup, e.g. a deauth frame. The frame
+ *	may be truncated, the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN
+ *	attribute contains the original length.
+ * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN: Original length of the 802.11
+ *	packet, may be bigger than the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211
+ *	attribute if the packet was truncated somewhere.
+ * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023: For wakeup reporting only, contains the
+ *	802.11 packet that caused the wakeup, e.g. a magic packet. The frame may
+ *	be truncated, the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN attribute
+ *	contains the original length.
+ * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN: Original length of the 802.3
+ *	packet, may be bigger than the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023
+ *	attribute if the packet was truncated somewhere.
+ * @NL80211_WOWLAN_TRIG_TCP_CONNECTION: TCP connection wake, see DOC section
+ *	"TCP connection wakeup" for more details. This is a nested attribute
+ *	containing the exact information for establishing and keeping alive
+ *	the TCP connection.
+ * @NL80211_WOWLAN_TRIG_TCP_WAKEUP_MATCH: For wakeup reporting only, the
+ *	wakeup packet was received on the TCP connection
+ * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST: For wakeup reporting only, the
+ *	TCP connection was lost or failed to be established
+ * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS: For wakeup reporting only,
+ *	the TCP connection ran out of tokens to use for data to send to the
+ *	service
+ * @NL80211_WOWLAN_TRIG_NET_DETECT: wake up when a configured network
+ *	is detected.  This is a nested attribute that contains the
+ *	same attributes used with @NL80211_CMD_START_SCHED_SCAN.  It
+ *	specifies how the scan is performed (e.g. the interval, the
+ *	channels to scan and the initial delay) as well as the scan
+ *	results that will trigger a wake (i.e. the matchsets).  This
+ *	attribute is also sent in a response to
+ *	@NL80211_CMD_GET_WIPHY, indicating the number of match sets
+ *	supported by the driver (u32).
+ * @NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS: nested attribute
+ *	containing an array with information about what triggered the
+ *	wake up.  If no elements are present in the array, it means
+ *	that the information is not available.  If more than one
+ *	element is present, it means that more than one match
+ *	occurred.
+ *	Each element in the array is a nested attribute that contains
+ *	one optional %NL80211_ATTR_SSID attribute and one optional
+ *	%NL80211_ATTR_SCAN_FREQUENCIES attribute.  At least one of
+ *	these attributes must be present.  If
+ *	%NL80211_ATTR_SCAN_FREQUENCIES contains more than one
+ *	frequency, it means that the match occurred in more than one
+ *	channel.
+ * @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers
+ * @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number
+ *
+ * These nested attributes are used to configure the wakeup triggers and
+ * to report the wakeup reason(s).
+ */
+enum nl80211_wowlan_triggers {
+	__NL80211_WOWLAN_TRIG_INVALID,
+	NL80211_WOWLAN_TRIG_ANY,
+	NL80211_WOWLAN_TRIG_DISCONNECT,
+	NL80211_WOWLAN_TRIG_MAGIC_PKT,
+	NL80211_WOWLAN_TRIG_PKT_PATTERN,
+	NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED,
+	NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE,
+	NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST,
+	NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE,
+	NL80211_WOWLAN_TRIG_RFKILL_RELEASE,
+	NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211,
+	NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN,
+	NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023,
+	NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN,
+	NL80211_WOWLAN_TRIG_TCP_CONNECTION,
+	NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH,
+	NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST,
+	NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS,
+	NL80211_WOWLAN_TRIG_NET_DETECT,
+	NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS,
+
+	/* keep last */
+	NUM_NL80211_WOWLAN_TRIG,
+	MAX_NL80211_WOWLAN_TRIG = NUM_NL80211_WOWLAN_TRIG - 1
+};
+
+/**
+ * DOC: TCP connection wakeup
+ *
+ * Some devices can establish a TCP connection in order to be woken up by a
+ * packet coming in from outside their network segment, or behind NAT. If
+ * configured, the device will establish a TCP connection to the given
+ * service, and periodically send data to that service. The first data
+ * packet is usually transmitted after SYN/ACK, also ACKing the SYN/ACK.
+ * The data packets can optionally include a (little endian) sequence
+ * number (in the TCP payload!) that is generated by the device, and, also
+ * optionally, a token from a list of tokens. This serves as a keep-alive
+ * with the service, and for NATed connections, etc.
+ *
+ * During this keep-alive period, the server doesn't send any data to the
+ * client. When receiving data, it is compared against the wakeup pattern
+ * (and mask) and if it matches, the host is woken up. Similarly, if the
+ * connection breaks or cannot be established to start with, the host is
+ * also woken up.
+ *
+ * Developer's note: ARP offload is required for this, otherwise TCP
+ * response packets might not go through correctly.
+ */
+
+/**
+ * struct nl80211_wowlan_tcp_data_seq - WoWLAN TCP data sequence
+ * @start: starting value
+ * @offset: offset of sequence number in packet
+ * @len: length of the sequence value to write, 1 through 4
+ *
+ * Note: don't confuse with the TCP sequence number(s), this is for the
+ * keepalive packet payload. The actual value is written into the packet
+ * in little endian.
+ */
+struct nl80211_wowlan_tcp_data_seq {
+	__u32 start, offset, len;
+};
+
+/**
+ * struct nl80211_wowlan_tcp_data_token - WoWLAN TCP data token config
+ * @offset: offset of token in packet
+ * @len: length of each token
+ * @token_stream: stream of data to be used for the tokens, the length must
+ *	be a multiple of @len for this to make sense
+ */
+struct nl80211_wowlan_tcp_data_token {
+	__u32 offset, len;
+	__u8 token_stream[];
+};
+
+/**
+ * struct nl80211_wowlan_tcp_data_token_feature - data token features
+ * @min_len: minimum token length
+ * @max_len: maximum token length
+ * @bufsize: total available token buffer size (max size of @token_stream)
+ */
+struct nl80211_wowlan_tcp_data_token_feature {
+	__u32 min_len, max_len, bufsize;
+};
+
+/**
+ * enum nl80211_wowlan_tcp_attrs - WoWLAN TCP connection parameters
+ * @__NL80211_WOWLAN_TCP_INVALID: invalid number for nested attributes
+ * @NL80211_WOWLAN_TCP_SRC_IPV4: source IPv4 address (in network byte order)
+ * @NL80211_WOWLAN_TCP_DST_IPV4: destination IPv4 address
+ *	(in network byte order)
+ * @NL80211_WOWLAN_TCP_DST_MAC: destination MAC address, this is given because
+ *	route lookup when configured might be invalid by the time we suspend,
+ *	and doing a route lookup when suspending is no longer possible as it
+ *	might require ARP querying.
+ * @NL80211_WOWLAN_TCP_SRC_PORT: source port (u16); optional, if not given a
+ *	socket and port will be allocated
+ * @NL80211_WOWLAN_TCP_DST_PORT: destination port (u16)
+ * @NL80211_WOWLAN_TCP_DATA_PAYLOAD: data packet payload, at least one byte.
+ *	For feature advertising, a u32 attribute holding the maximum length
+ *	of the data payload.
+ * @NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ: data packet sequence configuration
+ *	(if desired), a &struct nl80211_wowlan_tcp_data_seq. For feature
+ *	advertising it is just a flag
+ * @NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN: data packet token configuration,
+ *	see &struct nl80211_wowlan_tcp_data_token and for advertising see
+ *	&struct nl80211_wowlan_tcp_data_token_feature.
+ * @NL80211_WOWLAN_TCP_DATA_INTERVAL: data interval in seconds, maximum
+ *	interval in feature advertising (u32)
+ * @NL80211_WOWLAN_TCP_WAKE_PAYLOAD: wake packet payload, for advertising a
+ *	u32 attribute holding the maximum length
+ * @NL80211_WOWLAN_TCP_WAKE_MASK: Wake packet payload mask, not used for
+ *	feature advertising. The mask works like @NL80211_PKTPAT_MASK
+ *	but on the TCP payload only.
+ * @NUM_NL80211_WOWLAN_TCP: number of TCP attributes
+ * @MAX_NL80211_WOWLAN_TCP: highest attribute number
+ */
+enum nl80211_wowlan_tcp_attrs {
+	__NL80211_WOWLAN_TCP_INVALID,
+	NL80211_WOWLAN_TCP_SRC_IPV4,
+	NL80211_WOWLAN_TCP_DST_IPV4,
+	NL80211_WOWLAN_TCP_DST_MAC,
+	NL80211_WOWLAN_TCP_SRC_PORT,
+	NL80211_WOWLAN_TCP_DST_PORT,
+	NL80211_WOWLAN_TCP_DATA_PAYLOAD,
+	NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ,
+	NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN,
+	NL80211_WOWLAN_TCP_DATA_INTERVAL,
+	NL80211_WOWLAN_TCP_WAKE_PAYLOAD,
+	NL80211_WOWLAN_TCP_WAKE_MASK,
+
+	/* keep last */
+	NUM_NL80211_WOWLAN_TCP,
+	MAX_NL80211_WOWLAN_TCP = NUM_NL80211_WOWLAN_TCP - 1
+};
+
+/**
+ * struct nl80211_coalesce_rule_support - coalesce rule support information
+ * @max_rules: maximum number of rules supported
+ * @pat: packet pattern support information
+ * @max_delay: maximum supported coalescing delay in msecs
+ *
+ * This struct is carried in %NL80211_ATTR_COALESCE_RULE in the
+ * capability information given by the kernel to userspace.
+ */
+struct nl80211_coalesce_rule_support {
+	__u32 max_rules;
+	struct nl80211_pattern_support pat;
+	__u32 max_delay;
+} __attribute__((packed));
+
+/**
+ * enum nl80211_attr_coalesce_rule - coalesce rule attribute
+ * @__NL80211_COALESCE_RULE_INVALID: invalid number for nested attribute
+ * @NL80211_ATTR_COALESCE_RULE_DELAY: delay in msecs used for packet coalescing
+ * @NL80211_ATTR_COALESCE_RULE_CONDITION: condition for packet coalescence,
+ *	see &enum nl80211_coalesce_condition.
+ * @NL80211_ATTR_COALESCE_RULE_PKT_PATTERN: packet offset, pattern is matched
+ *	after these fixed number of bytes of received packet
+ * @NUM_NL80211_ATTR_COALESCE_RULE: number of attributes
+ * @NL80211_ATTR_COALESCE_RULE_MAX: max attribute number
+ */
+enum nl80211_attr_coalesce_rule {
+	__NL80211_COALESCE_RULE_INVALID,
+	NL80211_ATTR_COALESCE_RULE_DELAY,
+	NL80211_ATTR_COALESCE_RULE_CONDITION,
+	NL80211_ATTR_COALESCE_RULE_PKT_PATTERN,
+
+	/* keep last */
+	NUM_NL80211_ATTR_COALESCE_RULE,
+	NL80211_ATTR_COALESCE_RULE_MAX = NUM_NL80211_ATTR_COALESCE_RULE - 1
+};
+
+/**
+ * enum nl80211_coalesce_condition - coalesce rule conditions
+ * @NL80211_COALESCE_CONDITION_MATCH: coalaesce Rx packets when patterns
+ *	in a rule are matched.
+ * @NL80211_COALESCE_CONDITION_NO_MATCH: coalesce Rx packets when patterns
+ *	in a rule are not matched.
+ */
+enum nl80211_coalesce_condition {
+	NL80211_COALESCE_CONDITION_MATCH,
+	NL80211_COALESCE_CONDITION_NO_MATCH
+};
+
+/**
+ * enum nl80211_iface_limit_attrs - limit attributes
+ * @NL80211_IFACE_LIMIT_UNSPEC: (reserved)
+ * @NL80211_IFACE_LIMIT_MAX: maximum number of interfaces that
+ *	can be chosen from this set of interface types (u32)
+ * @NL80211_IFACE_LIMIT_TYPES: nested attribute containing a
+ *	flag attribute for each interface type in this set
+ * @NUM_NL80211_IFACE_LIMIT: number of attributes
+ * @MAX_NL80211_IFACE_LIMIT: highest attribute number
+ */
+enum nl80211_iface_limit_attrs {
+	NL80211_IFACE_LIMIT_UNSPEC,
+	NL80211_IFACE_LIMIT_MAX,
+	NL80211_IFACE_LIMIT_TYPES,
+
+	/* keep last */
+	NUM_NL80211_IFACE_LIMIT,
+	MAX_NL80211_IFACE_LIMIT = NUM_NL80211_IFACE_LIMIT - 1
+};
+
+/**
+ * enum nl80211_if_combination_attrs -- interface combination attributes
+ *
+ * @NL80211_IFACE_COMB_UNSPEC: (reserved)
+ * @NL80211_IFACE_COMB_LIMITS: Nested attributes containing the limits
+ *	for given interface types, see &enum nl80211_iface_limit_attrs.
+ * @NL80211_IFACE_COMB_MAXNUM: u32 attribute giving the total number of
+ *	interfaces that can be created in this group. This number doesn't
+ *	apply to interfaces purely managed in software, which are listed
+ *	in a separate attribute %NL80211_ATTR_INTERFACES_SOFTWARE.
+ * @NL80211_IFACE_COMB_STA_AP_BI_MATCH: flag attribute specifying that
+ *	beacon intervals within this group must be all the same even for
+ *	infrastructure and AP/GO combinations, i.e. the GO(s) must adopt
+ *	the infrastructure network's beacon interval.
+ * @NL80211_IFACE_COMB_NUM_CHANNELS: u32 attribute specifying how many
+ *	different channels may be used within this group.
+ * @NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS: u32 attribute containing the bitmap
+ *	of supported channel widths for radar detection.
+ * @NL80211_IFACE_COMB_RADAR_DETECT_REGIONS: u32 attribute containing the bitmap
+ *	of supported regulatory regions for radar detection.
+ * @NL80211_IFACE_COMB_BI_MIN_GCD: u32 attribute specifying the minimum GCD of
+ *	different beacon intervals supported by all the interface combinations
+ *	in this group (if not present, all beacon intervals be identical).
+ * @NUM_NL80211_IFACE_COMB: number of attributes
+ * @MAX_NL80211_IFACE_COMB: highest attribute number
+ *
+ * Examples:
+ *	limits = [ #{STA} <= 1, #{AP} <= 1 ], matching BI, channels = 1, max = 2
+ *	=> allows an AP and a STA that must match BIs
+ *
+ *	numbers = [ #{AP, P2P-GO} <= 8 ], BI min gcd, channels = 1, max = 8,
+ *	=> allows 8 of AP/GO that can have BI gcd >= min gcd
+ *
+ *	numbers = [ #{STA} <= 2 ], channels = 2, max = 2
+ *	=> allows two STAs on the same or on different channels
+ *
+ *	numbers = [ #{STA} <= 1, #{P2P-client,P2P-GO} <= 3 ], max = 4
+ *	=> allows a STA plus three P2P interfaces
+ *
+ * The list of these four possibilities could completely be contained
+ * within the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute to indicate
+ * that any of these groups must match.
+ *
+ * "Combinations" of just a single interface will not be listed here,
+ * a single interface of any valid interface type is assumed to always
+ * be possible by itself. This means that implicitly, for each valid
+ * interface type, the following group always exists:
+ *	numbers = [ #{<type>} <= 1 ], channels = 1, max = 1
+ */
+enum nl80211_if_combination_attrs {
+	NL80211_IFACE_COMB_UNSPEC,
+	NL80211_IFACE_COMB_LIMITS,
+	NL80211_IFACE_COMB_MAXNUM,
+	NL80211_IFACE_COMB_STA_AP_BI_MATCH,
+	NL80211_IFACE_COMB_NUM_CHANNELS,
+	NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS,
+	NL80211_IFACE_COMB_RADAR_DETECT_REGIONS,
+	NL80211_IFACE_COMB_BI_MIN_GCD,
+
+	/* keep last */
+	NUM_NL80211_IFACE_COMB,
+	MAX_NL80211_IFACE_COMB = NUM_NL80211_IFACE_COMB - 1
+};
+
+
+/**
+ * enum nl80211_plink_state - state of a mesh peer link finite state machine
+ *
+ * @NL80211_PLINK_LISTEN: initial state, considered the implicit
+ *	state of non existent mesh peer links
+ * @NL80211_PLINK_OPN_SNT: mesh plink open frame has been sent to
+ *	this mesh peer
+ * @NL80211_PLINK_OPN_RCVD: mesh plink open frame has been received
+ *	from this mesh peer
+ * @NL80211_PLINK_CNF_RCVD: mesh plink confirm frame has been
+ *	received from this mesh peer
+ * @NL80211_PLINK_ESTAB: mesh peer link is established
+ * @NL80211_PLINK_HOLDING: mesh peer link is being closed or cancelled
+ * @NL80211_PLINK_BLOCKED: all frames transmitted from this mesh
+ *	plink are discarded, except for authentication frames
+ * @NUM_NL80211_PLINK_STATES: number of peer link states
+ * @MAX_NL80211_PLINK_STATES: highest numerical value of plink states
+ */
+enum nl80211_plink_state {
+	NL80211_PLINK_LISTEN,
+	NL80211_PLINK_OPN_SNT,
+	NL80211_PLINK_OPN_RCVD,
+	NL80211_PLINK_CNF_RCVD,
+	NL80211_PLINK_ESTAB,
+	NL80211_PLINK_HOLDING,
+	NL80211_PLINK_BLOCKED,
+
+	/* keep last */
+	NUM_NL80211_PLINK_STATES,
+	MAX_NL80211_PLINK_STATES = NUM_NL80211_PLINK_STATES - 1
+};
+
+/**
+ * enum nl80211_plink_action - actions to perform in mesh peers
+ *
+ * @NL80211_PLINK_ACTION_NO_ACTION: perform no action
+ * @NL80211_PLINK_ACTION_OPEN: start mesh peer link establishment
+ * @NL80211_PLINK_ACTION_BLOCK: block traffic from this mesh peer
+ * @NUM_NL80211_PLINK_ACTIONS: number of possible actions
+ */
+enum plink_actions {
+	NL80211_PLINK_ACTION_NO_ACTION,
+	NL80211_PLINK_ACTION_OPEN,
+	NL80211_PLINK_ACTION_BLOCK,
+
+	NUM_NL80211_PLINK_ACTIONS,
+};
+
+
+#define NL80211_KCK_LEN			16
+#define NL80211_KEK_LEN			16
+#define NL80211_KCK_EXT_LEN		24
+#define NL80211_KEK_EXT_LEN		32
+#define NL80211_KCK_EXT_LEN_32		32
+#define NL80211_REPLAY_CTR_LEN		8
+
+/**
+ * enum nl80211_rekey_data - attributes for GTK rekey offload
+ * @__NL80211_REKEY_DATA_INVALID: invalid number for nested attributes
+ * @NL80211_REKEY_DATA_KEK: key encryption key (binary)
+ * @NL80211_REKEY_DATA_KCK: key confirmation key (binary)
+ * @NL80211_REKEY_DATA_REPLAY_CTR: replay counter (binary)
+ * @NL80211_REKEY_DATA_AKM: AKM data (OUI, suite type)
+ * @NUM_NL80211_REKEY_DATA: number of rekey attributes (internal)
+ * @MAX_NL80211_REKEY_DATA: highest rekey attribute (internal)
+ */
+enum nl80211_rekey_data {
+	__NL80211_REKEY_DATA_INVALID,
+	NL80211_REKEY_DATA_KEK,
+	NL80211_REKEY_DATA_KCK,
+	NL80211_REKEY_DATA_REPLAY_CTR,
+	NL80211_REKEY_DATA_AKM,
+
+	/* keep last */
+	NUM_NL80211_REKEY_DATA,
+	MAX_NL80211_REKEY_DATA = NUM_NL80211_REKEY_DATA - 1
+};
+
+/**
+ * enum nl80211_hidden_ssid - values for %NL80211_ATTR_HIDDEN_SSID
+ * @NL80211_HIDDEN_SSID_NOT_IN_USE: do not hide SSID (i.e., broadcast it in
+ *	Beacon frames)
+ * @NL80211_HIDDEN_SSID_ZERO_LEN: hide SSID by using zero-length SSID element
+ *	in Beacon frames
+ * @NL80211_HIDDEN_SSID_ZERO_CONTENTS: hide SSID by using correct length of SSID
+ *	element in Beacon frames but zero out each byte in the SSID
+ */
+enum nl80211_hidden_ssid {
+	NL80211_HIDDEN_SSID_NOT_IN_USE,
+	NL80211_HIDDEN_SSID_ZERO_LEN,
+	NL80211_HIDDEN_SSID_ZERO_CONTENTS
+};
+
+/**
+ * enum nl80211_sta_wme_attr - station WME attributes
+ * @__NL80211_STA_WME_INVALID: invalid number for nested attribute
+ * @NL80211_STA_WME_UAPSD_QUEUES: bitmap of uapsd queues. the format
+ *	is the same as the AC bitmap in the QoS info field.
+ * @NL80211_STA_WME_MAX_SP: max service period. the format is the same
+ *	as the MAX_SP field in the QoS info field (but already shifted down).
+ * @__NL80211_STA_WME_AFTER_LAST: internal
+ * @NL80211_STA_WME_MAX: highest station WME attribute
+ */
+enum nl80211_sta_wme_attr {
+	__NL80211_STA_WME_INVALID,
+	NL80211_STA_WME_UAPSD_QUEUES,
+	NL80211_STA_WME_MAX_SP,
+
+	/* keep last */
+	__NL80211_STA_WME_AFTER_LAST,
+	NL80211_STA_WME_MAX = __NL80211_STA_WME_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_pmksa_candidate_attr - attributes for PMKSA caching candidates
+ * @__NL80211_PMKSA_CANDIDATE_INVALID: invalid number for nested attributes
+ * @NL80211_PMKSA_CANDIDATE_INDEX: candidate index (u32; the smaller, the higher
+ *	priority)
+ * @NL80211_PMKSA_CANDIDATE_BSSID: candidate BSSID (6 octets)
+ * @NL80211_PMKSA_CANDIDATE_PREAUTH: RSN pre-authentication supported (flag)
+ * @NUM_NL80211_PMKSA_CANDIDATE: number of PMKSA caching candidate attributes
+ *	(internal)
+ * @MAX_NL80211_PMKSA_CANDIDATE: highest PMKSA caching candidate attribute
+ *	(internal)
+ */
+enum nl80211_pmksa_candidate_attr {
+	__NL80211_PMKSA_CANDIDATE_INVALID,
+	NL80211_PMKSA_CANDIDATE_INDEX,
+	NL80211_PMKSA_CANDIDATE_BSSID,
+	NL80211_PMKSA_CANDIDATE_PREAUTH,
+
+	/* keep last */
+	NUM_NL80211_PMKSA_CANDIDATE,
+	MAX_NL80211_PMKSA_CANDIDATE = NUM_NL80211_PMKSA_CANDIDATE - 1
+};
+
+/**
+ * enum nl80211_tdls_operation - values for %NL80211_ATTR_TDLS_OPERATION
+ * @NL80211_TDLS_DISCOVERY_REQ: Send a TDLS discovery request
+ * @NL80211_TDLS_SETUP: Setup TDLS link
+ * @NL80211_TDLS_TEARDOWN: Teardown a TDLS link which is already established
+ * @NL80211_TDLS_ENABLE_LINK: Enable TDLS link
+ * @NL80211_TDLS_DISABLE_LINK: Disable TDLS link
+ */
+enum nl80211_tdls_operation {
+	NL80211_TDLS_DISCOVERY_REQ,
+	NL80211_TDLS_SETUP,
+	NL80211_TDLS_TEARDOWN,
+	NL80211_TDLS_ENABLE_LINK,
+	NL80211_TDLS_DISABLE_LINK,
+};
+
+/**
+ * enum nl80211_ap_sme_features - device-integrated AP features
+ * @NL80211_AP_SME_SA_QUERY_OFFLOAD: SA Query procedures offloaded to driver
+ *	when user space indicates support for SA Query procedures offload during
+ *	"start ap" with %NL80211_AP_SETTINGS_SA_QUERY_OFFLOAD_SUPPORT.
+ */
+enum nl80211_ap_sme_features {
+	NL80211_AP_SME_SA_QUERY_OFFLOAD		= 1 << 0,
+};
+
+/**
+ * enum nl80211_feature_flags - device/driver features
+ * @NL80211_FEATURE_SK_TX_STATUS: This driver supports reflecting back
+ *	TX status to the socket error queue when requested with the
+ *	socket option.
+ * @NL80211_FEATURE_HT_IBSS: This driver supports IBSS with HT datarates.
+ * @NL80211_FEATURE_INACTIVITY_TIMER: This driver takes care of freeing up
+ *	the connected inactive stations in AP mode.
+ * @NL80211_FEATURE_CELL_BASE_REG_HINTS: This driver has been tested
+ *	to work properly to support receiving regulatory hints from
+ *	cellular base stations.
+ * @NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL: (no longer available, only
+ *	here to reserve the value for API/ABI compatibility)
+ * @NL80211_FEATURE_SAE: This driver supports simultaneous authentication of
+ *	equals (SAE) with user space SME (NL80211_CMD_AUTHENTICATE) in station
+ *	mode
+ * @NL80211_FEATURE_LOW_PRIORITY_SCAN: This driver supports low priority scan
+ * @NL80211_FEATURE_SCAN_FLUSH: Scan flush is supported
+ * @NL80211_FEATURE_AP_SCAN: Support scanning using an AP vif
+ * @NL80211_FEATURE_VIF_TXPOWER: The driver supports per-vif TX power setting
+ * @NL80211_FEATURE_NEED_OBSS_SCAN: The driver expects userspace to perform
+ *	OBSS scans and generate 20/40 BSS coex reports. This flag is used only
+ *	for drivers implementing the CONNECT API, for AUTH/ASSOC it is implied.
+ * @NL80211_FEATURE_P2P_GO_CTWIN: P2P GO implementation supports CT Window
+ *	setting
+ * @NL80211_FEATURE_P2P_GO_OPPPS: P2P GO implementation supports opportunistic
+ *	powersave
+ * @NL80211_FEATURE_FULL_AP_CLIENT_STATE: The driver supports full state
+ *	transitions for AP clients. Without this flag (and if the driver
+ *	doesn't have the AP SME in the device) the driver supports adding
+ *	stations only when they're associated and adds them in associated
+ *	state (to later be transitioned into authorized), with this flag
+ *	they should be added before even sending the authentication reply
+ *	and then transitioned into authenticated, associated and authorized
+ *	states using station flags.
+ *	Note that even for drivers that support this, the default is to add
+ *	stations in authenticated/associated state, so to add unauthenticated
+ *	stations the authenticated/associated bits have to be set in the mask.
+ * @NL80211_FEATURE_ADVERTISE_CHAN_LIMITS: cfg80211 advertises channel limits
+ *	(HT40, VHT 80/160 MHz) if this flag is set
+ * @NL80211_FEATURE_USERSPACE_MPM: This driver supports a userspace Mesh
+ *	Peering Management entity which may be implemented by registering for
+ *	beacons or NL80211_CMD_NEW_PEER_CANDIDATE events. The mesh beacon is
+ *	still generated by the driver.
+ * @NL80211_FEATURE_ACTIVE_MONITOR: This driver supports an active monitor
+ *	interface. An active monitor interface behaves like a normal monitor
+ *	interface, but gets added to the driver. It ensures that incoming
+ *	unicast packets directed at the configured interface address get ACKed.
+ * @NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE: This driver supports dynamic
+ *	channel bandwidth change (e.g., HT 20 <-> 40 MHz channel) during the
+ *	lifetime of a BSS.
+ * @NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES: This device adds a DS Parameter
+ *	Set IE to probe requests.
+ * @NL80211_FEATURE_WFA_TPC_IE_IN_PROBES: This device adds a WFA TPC Report IE
+ *	to probe requests.
+ * @NL80211_FEATURE_QUIET: This device, in client mode, supports Quiet Period
+ *	requests sent to it by an AP.
+ * @NL80211_FEATURE_TX_POWER_INSERTION: This device is capable of inserting the
+ *	current tx power value into the TPC Report IE in the spectrum
+ *	management TPC Report action frame, and in the Radio Measurement Link
+ *	Measurement Report action frame.
+ * @NL80211_FEATURE_ACKTO_ESTIMATION: This driver supports dynamic ACK timeout
+ *	estimation (dynack). %NL80211_ATTR_WIPHY_DYN_ACK flag attribute is used
+ *	to enable dynack.
+ * @NL80211_FEATURE_STATIC_SMPS: Device supports static spatial
+ *	multiplexing powersave, ie. can turn off all but one chain
+ *	even on HT connections that should be using more chains.
+ * @NL80211_FEATURE_DYNAMIC_SMPS: Device supports dynamic spatial
+ *	multiplexing powersave, ie. can turn off all but one chain
+ *	and then wake the rest up as required after, for example,
+ *	rts/cts handshake.
+ * @NL80211_FEATURE_SUPPORTS_WMM_ADMISSION: the device supports setting up WMM
+ *	TSPEC sessions (TID aka TSID 0-7) with the %NL80211_CMD_ADD_TX_TS
+ *	command. Standard IEEE 802.11 TSPEC setup is not yet supported, it
+ *	needs to be able to handle Block-Ack agreements and other things.
+ * @NL80211_FEATURE_MAC_ON_CREATE: Device supports configuring
+ *	the vif's MAC address upon creation.
+ *	See 'macaddr' field in the vif_params (cfg80211.h).
+ * @NL80211_FEATURE_TDLS_CHANNEL_SWITCH: Driver supports channel switching when
+ *	operating as a TDLS peer.
+ * @NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR: This device/driver supports using a
+ *	random MAC address during scan (if the device is unassociated); the
+ *	%NL80211_SCAN_FLAG_RANDOM_ADDR flag may be set for scans and the MAC
+ *	address mask/value will be used.
+ * @NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR: This device/driver supports
+ *	using a random MAC address for every scan iteration during scheduled
+ *	scan (while not associated), the %NL80211_SCAN_FLAG_RANDOM_ADDR may
+ *	be set for scheduled scan and the MAC address mask/value will be used.
+ * @NL80211_FEATURE_ND_RANDOM_MAC_ADDR: This device/driver supports using a
+ *	random MAC address for every scan iteration during "net detect", i.e.
+ *	scan in unassociated WoWLAN, the %NL80211_SCAN_FLAG_RANDOM_ADDR may
+ *	be set for scheduled scan and the MAC address mask/value will be used.
+ */
+enum nl80211_feature_flags {
+	NL80211_FEATURE_SK_TX_STATUS			= 1 << 0,
+	NL80211_FEATURE_HT_IBSS				= 1 << 1,
+	NL80211_FEATURE_INACTIVITY_TIMER		= 1 << 2,
+	NL80211_FEATURE_CELL_BASE_REG_HINTS		= 1 << 3,
+	NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL	= 1 << 4,
+	NL80211_FEATURE_SAE				= 1 << 5,
+	NL80211_FEATURE_LOW_PRIORITY_SCAN		= 1 << 6,
+	NL80211_FEATURE_SCAN_FLUSH			= 1 << 7,
+	NL80211_FEATURE_AP_SCAN				= 1 << 8,
+	NL80211_FEATURE_VIF_TXPOWER			= 1 << 9,
+	NL80211_FEATURE_NEED_OBSS_SCAN			= 1 << 10,
+	NL80211_FEATURE_P2P_GO_CTWIN			= 1 << 11,
+	NL80211_FEATURE_P2P_GO_OPPPS			= 1 << 12,
+	/* bit 13 is reserved */
+	NL80211_FEATURE_ADVERTISE_CHAN_LIMITS		= 1 << 14,
+	NL80211_FEATURE_FULL_AP_CLIENT_STATE		= 1 << 15,
+	NL80211_FEATURE_USERSPACE_MPM			= 1 << 16,
+	NL80211_FEATURE_ACTIVE_MONITOR			= 1 << 17,
+	NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE	= 1 << 18,
+	NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES	= 1 << 19,
+	NL80211_FEATURE_WFA_TPC_IE_IN_PROBES		= 1 << 20,
+	NL80211_FEATURE_QUIET				= 1 << 21,
+	NL80211_FEATURE_TX_POWER_INSERTION		= 1 << 22,
+	NL80211_FEATURE_ACKTO_ESTIMATION		= 1 << 23,
+	NL80211_FEATURE_STATIC_SMPS			= 1 << 24,
+	NL80211_FEATURE_DYNAMIC_SMPS			= 1 << 25,
+	NL80211_FEATURE_SUPPORTS_WMM_ADMISSION		= 1 << 26,
+	NL80211_FEATURE_MAC_ON_CREATE			= 1 << 27,
+	NL80211_FEATURE_TDLS_CHANNEL_SWITCH		= 1 << 28,
+	NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR		= 1 << 29,
+	NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR	= 1 << 30,
+	NL80211_FEATURE_ND_RANDOM_MAC_ADDR		= 1U << 31,
+};
+
+/**
+ * enum nl80211_ext_feature_index - bit index of extended features.
+ * @NL80211_EXT_FEATURE_VHT_IBSS: This driver supports IBSS with VHT datarates.
+ * @NL80211_EXT_FEATURE_RRM: This driver supports RRM. When featured, user can
+ *	request to use RRM (see %NL80211_ATTR_USE_RRM) with
+ *	%NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests, which will set
+ *	the ASSOC_REQ_USE_RRM flag in the association request even if
+ *	NL80211_FEATURE_QUIET is not advertized.
+ * @NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER: This device supports MU-MIMO air
+ *	sniffer which means that it can be configured to hear packets from
+ *	certain groups which can be configured by the
+ *	%NL80211_ATTR_MU_MIMO_GROUP_DATA attribute,
+ *	or can be configured to follow a station by configuring the
+ *	%NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR attribute.
+ * @NL80211_EXT_FEATURE_SCAN_START_TIME: This driver includes the actual
+ *	time the scan started in scan results event. The time is the TSF of
+ *	the BSS that the interface that requested the scan is connected to
+ *	(if available).
+ * @NL80211_EXT_FEATURE_BSS_PARENT_TSF: Per BSS, this driver reports the
+ *	time the last beacon/probe was received. The time is the TSF of the
+ *	BSS that the interface that requested the scan is connected to
+ *	(if available).
+ * @NL80211_EXT_FEATURE_SET_SCAN_DWELL: This driver supports configuration of
+ *	channel dwell time.
+ * @NL80211_EXT_FEATURE_BEACON_RATE_LEGACY: Driver supports beacon rate
+ *	configuration (AP/mesh), supporting a legacy (non HT/VHT) rate.
+ * @NL80211_EXT_FEATURE_BEACON_RATE_HT: Driver supports beacon rate
+ *	configuration (AP/mesh) with HT rates.
+ * @NL80211_EXT_FEATURE_BEACON_RATE_VHT: Driver supports beacon rate
+ *	configuration (AP/mesh) with VHT rates.
+ * @NL80211_EXT_FEATURE_FILS_STA: This driver supports Fast Initial Link Setup
+ *	with user space SME (NL80211_CMD_AUTHENTICATE) in station mode.
+ * @NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA: This driver supports randomized TA
+ *	in @NL80211_CMD_FRAME while not associated.
+ * @NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED: This driver supports
+ *	randomized TA in @NL80211_CMD_FRAME while associated.
+ * @NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI: The driver supports sched_scan
+ *	for reporting BSSs with better RSSI than the current connected BSS
+ *	(%NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI).
+ * @NL80211_EXT_FEATURE_CQM_RSSI_LIST: With this driver the
+ *	%NL80211_ATTR_CQM_RSSI_THOLD attribute accepts a list of zero or more
+ *	RSSI threshold values to monitor rather than exactly one threshold.
+ * @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD: Driver SME supports FILS shared key
+ *	authentication with %NL80211_CMD_CONNECT.
+ * @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK: Device wants to do 4-way
+ *	handshake with PSK in station mode (PSK is passed as part of the connect
+ *	and associate commands), doing it in the host might not be supported.
+ * @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X: Device wants to do doing 4-way
+ *	handshake with 802.1X in station mode (will pass EAP frames to the host
+ *	and accept the set_pmk/del_pmk commands), doing it in the host might not
+ *	be supported.
+ * @NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME: Driver is capable of overriding
+ *	the max channel attribute in the FILS request params IE with the
+ *	actual dwell time.
+ * @NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP: Driver accepts broadcast probe
+ *	response
+ * @NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE: Driver supports sending
+ *	the first probe request in each channel at rate of at least 5.5Mbps.
+ * @NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION: Driver supports
+ *	probe request tx deferral and suppression
+ * @NL80211_EXT_FEATURE_MFP_OPTIONAL: Driver supports the %NL80211_MFP_OPTIONAL
+ *	value in %NL80211_ATTR_USE_MFP.
+ * @NL80211_EXT_FEATURE_LOW_SPAN_SCAN: Driver supports low span scan.
+ * @NL80211_EXT_FEATURE_LOW_POWER_SCAN: Driver supports low power scan.
+ * @NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN: Driver supports high accuracy scan.
+ * @NL80211_EXT_FEATURE_DFS_OFFLOAD: HW/driver will offload DFS actions.
+ *	Device or driver will do all DFS-related actions by itself,
+ *	informing user-space about CAC progress, radar detection event,
+ *	channel change triggered by radar detection event.
+ *	No need to start CAC from user-space, no need to react to
+ *	"radar detected" event.
+ * @NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211: Driver supports sending and
+ *	receiving control port frames over nl80211 instead of the netdevice.
+ * @NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT: This driver/device supports
+ *	(average) ACK signal strength reporting.
+ * @NL80211_EXT_FEATURE_TXQS: Driver supports FQ-CoDel-enabled intermediate
+ *      TXQs.
+ * @NL80211_EXT_FEATURE_SCAN_RANDOM_SN: Driver/device supports randomizing the
+ *	SN in probe request frames if requested by %NL80211_SCAN_FLAG_RANDOM_SN.
+ * @NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT: Driver/device can omit all data
+ *	except for supported rates from the probe request content if requested
+ *	by the %NL80211_SCAN_FLAG_MIN_PREQ_CONTENT flag.
+ * @NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER: Driver supports enabling fine
+ *	timing measurement responder role.
+ *
+ * @NL80211_EXT_FEATURE_CAN_REPLACE_PTK0: Driver/device confirm that they are
+ *      able to rekey an in-use key correctly. Userspace must not rekey PTK keys
+ *      if this flag is not set. Ignoring this can leak clear text packets and/or
+ *      freeze the connection.
+ * @NL80211_EXT_FEATURE_EXT_KEY_ID: Driver supports "Extended Key ID for
+ *      Individually Addressed Frames" from IEEE802.11-2016.
+ *
+ * @NL80211_EXT_FEATURE_AIRTIME_FAIRNESS: Driver supports getting airtime
+ *	fairness for transmitted packets and has enabled airtime fairness
+ *	scheduling.
+ *
+ * @NL80211_EXT_FEATURE_AP_PMKSA_CACHING: Driver/device supports PMKSA caching
+ *	(set/del PMKSA operations) in AP mode.
+ *
+ * @NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD: Driver supports
+ *	filtering of sched scan results using band specific RSSI thresholds.
+ *
+ * @NL80211_EXT_FEATURE_STA_TX_PWR: This driver supports controlling tx power
+ *	to a station.
+ *
+ * @NL80211_EXT_FEATURE_SAE_OFFLOAD: Device wants to do SAE authentication in
+ *	station mode (SAE password is passed as part of the connect command).
+ *
+ * @NL80211_EXT_FEATURE_VLAN_OFFLOAD: The driver supports a single netdev
+ *	with VLAN tagged frames and separate VLAN-specific netdevs added using
+ *	vconfig similarly to the Ethernet case.
+ *
+ * @NL80211_EXT_FEATURE_AQL: The driver supports the Airtime Queue Limit (AQL)
+ *	feature, which prevents bufferbloat by using the expected transmission
+ *	time to limit the amount of data buffered in the hardware.
+ *
+ * @NL80211_EXT_FEATURE_BEACON_PROTECTION: The driver supports Beacon protection
+ *	and can receive key configuration for BIGTK using key indexes 6 and 7.
+ * @NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT: The driver supports Beacon
+ *	protection as a client only and cannot transmit protected beacons.
+ *
+ * @NL80211_EXT_FEATURE_CONTROL_PORT_NO_PREAUTH: The driver can disable the
+ *	forwarding of preauth frames over the control port. They are then
+ *	handled as ordinary data frames.
+ *
+ * @NL80211_EXT_FEATURE_PROTECTED_TWT: Driver supports protected TWT frames
+ *
+ * @NL80211_EXT_FEATURE_DEL_IBSS_STA: The driver supports removing stations
+ *      in IBSS mode, essentially by dropping their state.
+ *
+ * @NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS: management frame registrations
+ *	are possible for multicast frames and those will be reported properly.
+ *
+ * @NL80211_EXT_FEATURE_SCAN_FREQ_KHZ: This driver supports receiving and
+ *	reporting scan request with %NL80211_ATTR_SCAN_FREQ_KHZ. In order to
+ *	report %NL80211_ATTR_SCAN_FREQ_KHZ, %NL80211_SCAN_FLAG_FREQ_KHZ must be
+ *	included in the scan request.
+ *
+ * @NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211_TX_STATUS: The driver
+ *	can report tx status for control port over nl80211 tx operations.
+ *
+ * @NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION: Driver supports Operating
+ *	Channel Validation (OCV) when using driver's SME for RSNA handshakes.
+ *
+ * @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_AP_PSK: Device wants to do 4-way
+ *	handshake with PSK in AP mode (PSK is passed as part of the start AP
+ *	command).
+ *
+ * @NL80211_EXT_FEATURE_SAE_OFFLOAD_AP: Device wants to do SAE authentication
+ *	in AP mode (SAE password is passed as part of the start AP command).
+ *
+ * @NL80211_EXT_FEATURE_FILS_DISCOVERY: Driver/device supports FILS discovery
+ *	frames transmission
+ *
+ * @NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP: Driver/device supports
+ *	unsolicited broadcast probe response transmission
+ *
+ * @NL80211_EXT_FEATURE_BEACON_RATE_HE: Driver supports beacon rate
+ *	configuration (AP/mesh) with HE rates.
+ *
+ * @NL80211_EXT_FEATURE_SECURE_LTF: Device supports secure LTF measurement
+ *      exchange protocol.
+ *
+ * @NL80211_EXT_FEATURE_SECURE_RTT: Device supports secure RTT measurement
+ *      exchange protocol.
+ *
+ * @NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE: Device supports management
+ *      frame protection for all management frames exchanged during the
+ *      negotiation and range measurement procedure.
+ *
+ * @NL80211_EXT_FEATURE_BSS_COLOR: The driver supports BSS color collision
+ *	detection and change announcemnts.
+ *
+ * @NL80211_EXT_FEATURE_FILS_CRYPTO_OFFLOAD: Driver running in AP mode supports
+ *	FILS encryption and decryption for (Re)Association Request and Response
+ *	frames. Userspace has to share FILS AAD details to the driver by using
+ *	@NL80211_CMD_SET_FILS_AAD.
+ *
+ * @NL80211_EXT_FEATURE_RADAR_BACKGROUND: Device supports background radar/CAC
+ *	detection.
+ *
+ * @NL80211_EXT_FEATURE_POWERED_ADDR_CHANGE: Device can perform a MAC address
+ *	change without having to bring the underlying network device down
+ *	first. For example, in station mode this can be used to vary the
+ *	origin MAC address prior to a connection to a new AP for privacy
+ *	or other reasons. Note that certain driver specific restrictions
+ *	might apply, e.g. no scans in progress, no offchannel operations
+ *	in progress, and no active connections.
+ *
+ * @NL80211_EXT_FEATURE_PUNCT: Driver supports preamble puncturing in AP mode.
+ *
+ * @NL80211_EXT_FEATURE_SECURE_NAN: Device supports NAN Pairing which enables
+ *	authentication, data encryption and message integrity.
+ *
+ * @NL80211_EXT_FEATURE_AUTH_AND_DEAUTH_RANDOM_TA: Device supports randomized TA
+ *	in authentication and deauthentication frames sent to unassociated peer
+ *	using @NL80211_CMD_FRAME.
+ *
+ * @NUM_NL80211_EXT_FEATURES: number of extended features.
+ * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
+ */
+enum nl80211_ext_feature_index {
+	NL80211_EXT_FEATURE_VHT_IBSS,
+	NL80211_EXT_FEATURE_RRM,
+	NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER,
+	NL80211_EXT_FEATURE_SCAN_START_TIME,
+	NL80211_EXT_FEATURE_BSS_PARENT_TSF,
+	NL80211_EXT_FEATURE_SET_SCAN_DWELL,
+	NL80211_EXT_FEATURE_BEACON_RATE_LEGACY,
+	NL80211_EXT_FEATURE_BEACON_RATE_HT,
+	NL80211_EXT_FEATURE_BEACON_RATE_VHT,
+	NL80211_EXT_FEATURE_FILS_STA,
+	NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA,
+	NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED,
+	NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI,
+	NL80211_EXT_FEATURE_CQM_RSSI_LIST,
+	NL80211_EXT_FEATURE_FILS_SK_OFFLOAD,
+	NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK,
+	NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X,
+	NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME,
+	NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP,
+	NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE,
+	NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION,
+	NL80211_EXT_FEATURE_MFP_OPTIONAL,
+	NL80211_EXT_FEATURE_LOW_SPAN_SCAN,
+	NL80211_EXT_FEATURE_LOW_POWER_SCAN,
+	NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN,
+	NL80211_EXT_FEATURE_DFS_OFFLOAD,
+	NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211,
+	NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT,
+	/* we renamed this - stay compatible */
+	NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT = NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT,
+	NL80211_EXT_FEATURE_TXQS,
+	NL80211_EXT_FEATURE_SCAN_RANDOM_SN,
+	NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT,
+	NL80211_EXT_FEATURE_CAN_REPLACE_PTK0,
+	NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER,
+	NL80211_EXT_FEATURE_AIRTIME_FAIRNESS,
+	NL80211_EXT_FEATURE_AP_PMKSA_CACHING,
+	NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD,
+	NL80211_EXT_FEATURE_EXT_KEY_ID,
+	NL80211_EXT_FEATURE_STA_TX_PWR,
+	NL80211_EXT_FEATURE_SAE_OFFLOAD,
+	NL80211_EXT_FEATURE_VLAN_OFFLOAD,
+	NL80211_EXT_FEATURE_AQL,
+	NL80211_EXT_FEATURE_BEACON_PROTECTION,
+	NL80211_EXT_FEATURE_CONTROL_PORT_NO_PREAUTH,
+	NL80211_EXT_FEATURE_PROTECTED_TWT,
+	NL80211_EXT_FEATURE_DEL_IBSS_STA,
+	NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS,
+	NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT,
+	NL80211_EXT_FEATURE_SCAN_FREQ_KHZ,
+	NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211_TX_STATUS,
+	NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION,
+	NL80211_EXT_FEATURE_4WAY_HANDSHAKE_AP_PSK,
+	NL80211_EXT_FEATURE_SAE_OFFLOAD_AP,
+	NL80211_EXT_FEATURE_FILS_DISCOVERY,
+	NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP,
+	NL80211_EXT_FEATURE_BEACON_RATE_HE,
+	NL80211_EXT_FEATURE_SECURE_LTF,
+	NL80211_EXT_FEATURE_SECURE_RTT,
+	NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE,
+	NL80211_EXT_FEATURE_BSS_COLOR,
+	NL80211_EXT_FEATURE_FILS_CRYPTO_OFFLOAD,
+	NL80211_EXT_FEATURE_RADAR_BACKGROUND,
+	NL80211_EXT_FEATURE_POWERED_ADDR_CHANGE,
+	NL80211_EXT_FEATURE_PUNCT,
+	NL80211_EXT_FEATURE_SECURE_NAN,
+	NL80211_EXT_FEATURE_AUTH_AND_DEAUTH_RANDOM_TA,
+
+	/* add new features before the definition below */
+	NUM_NL80211_EXT_FEATURES,
+	MAX_NL80211_EXT_FEATURES = NUM_NL80211_EXT_FEATURES - 1
+};
+
+/**
+ * enum nl80211_probe_resp_offload_support_attr - optional supported
+ *	protocols for probe-response offloading by the driver/FW.
+ *	To be used with the %NL80211_ATTR_PROBE_RESP_OFFLOAD attribute.
+ *	Each enum value represents a bit in the bitmap of supported
+ *	protocols. Typically a subset of probe-requests belonging to a
+ *	supported protocol will be excluded from offload and uploaded
+ *	to the host.
+ *
+ * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS: Support for WPS ver. 1
+ * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2: Support for WPS ver. 2
+ * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P: Support for P2P
+ * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U: Support for 802.11u
+ */
+enum nl80211_probe_resp_offload_support_attr {
+	NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS =	1<<0,
+	NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 =	1<<1,
+	NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P =	1<<2,
+	NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U =	1<<3,
+};
+
+/**
+ * enum nl80211_connect_failed_reason - connection request failed reasons
+ * @NL80211_CONN_FAIL_MAX_CLIENTS: Maximum number of clients that can be
+ *	handled by the AP is reached.
+ * @NL80211_CONN_FAIL_BLOCKED_CLIENT: Connection request is rejected due to ACL.
+ */
+enum nl80211_connect_failed_reason {
+	NL80211_CONN_FAIL_MAX_CLIENTS,
+	NL80211_CONN_FAIL_BLOCKED_CLIENT,
+};
+
+/**
+ * enum nl80211_timeout_reason - timeout reasons
+ *
+ * @NL80211_TIMEOUT_UNSPECIFIED: Timeout reason unspecified.
+ * @NL80211_TIMEOUT_SCAN: Scan (AP discovery) timed out.
+ * @NL80211_TIMEOUT_AUTH: Authentication timed out.
+ * @NL80211_TIMEOUT_ASSOC: Association timed out.
+ */
+enum nl80211_timeout_reason {
+	NL80211_TIMEOUT_UNSPECIFIED,
+	NL80211_TIMEOUT_SCAN,
+	NL80211_TIMEOUT_AUTH,
+	NL80211_TIMEOUT_ASSOC,
+};
+
+/**
+ * enum nl80211_scan_flags -  scan request control flags
+ *
+ * Scan request control flags are used to control the handling
+ * of NL80211_CMD_TRIGGER_SCAN and NL80211_CMD_START_SCHED_SCAN
+ * requests.
+ *
+ * NL80211_SCAN_FLAG_LOW_SPAN, NL80211_SCAN_FLAG_LOW_POWER, and
+ * NL80211_SCAN_FLAG_HIGH_ACCURACY flags are exclusive of each other, i.e., only
+ * one of them can be used in the request.
+ *
+ * @NL80211_SCAN_FLAG_LOW_PRIORITY: scan request has low priority
+ * @NL80211_SCAN_FLAG_FLUSH: flush cache before scanning
+ * @NL80211_SCAN_FLAG_AP: force a scan even if the interface is configured
+ *	as AP and the beaconing has already been configured. This attribute is
+ *	dangerous because will destroy stations performance as a lot of frames
+ *	will be lost while scanning off-channel, therefore it must be used only
+ *	when really needed
+ * @NL80211_SCAN_FLAG_RANDOM_ADDR: use a random MAC address for this scan (or
+ *	for scheduled scan: a different one for every scan iteration). When the
+ *	flag is set, depending on device capabilities the @NL80211_ATTR_MAC and
+ *	@NL80211_ATTR_MAC_MASK attributes may also be given in which case only
+ *	the masked bits will be preserved from the MAC address and the remainder
+ *	randomised. If the attributes are not given full randomisation (46 bits,
+ *	locally administered 1, multicast 0) is assumed.
+ *	This flag must not be requested when the feature isn't supported, check
+ *	the nl80211 feature flags for the device.
+ * @NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME: fill the dwell time in the FILS
+ *	request parameters IE in the probe request
+ * @NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP: accept broadcast probe responses
+ * @NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE: send probe request frames at
+ *	rate of at least 5.5M. In case non OCE AP is discovered in the channel,
+ *	only the first probe req in the channel will be sent in high rate.
+ * @NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION: allow probe request
+ *	tx deferral (dot11FILSProbeDelay shall be set to 15ms)
+ *	and suppression (if it has received a broadcast Probe Response frame,
+ *	Beacon frame or FILS Discovery frame from an AP that the STA considers
+ *	a suitable candidate for (re-)association - suitable in terms of
+ *	SSID and/or RSSI.
+ * @NL80211_SCAN_FLAG_LOW_SPAN: Span corresponds to the total time taken to
+ *	accomplish the scan. Thus, this flag intends the driver to perform the
+ *	scan request with lesser span/duration. It is specific to the driver
+ *	implementations on how this is accomplished. Scan accuracy may get
+ *	impacted with this flag.
+ * @NL80211_SCAN_FLAG_LOW_POWER: This flag intends the scan attempts to consume
+ *	optimal possible power. Drivers can resort to their specific means to
+ *	optimize the power. Scan accuracy may get impacted with this flag.
+ * @NL80211_SCAN_FLAG_HIGH_ACCURACY: Accuracy here intends to the extent of scan
+ *	results obtained. Thus HIGH_ACCURACY scan flag aims to get maximum
+ *	possible scan results. This flag hints the driver to use the best
+ *	possible scan configuration to improve the accuracy in scanning.
+ *	Latency and power use may get impacted with this flag.
+ * @NL80211_SCAN_FLAG_RANDOM_SN: randomize the sequence number in probe
+ *	request frames from this scan to avoid correlation/tracking being
+ *	possible.
+ * @NL80211_SCAN_FLAG_MIN_PREQ_CONTENT: minimize probe request content to
+ *	only have supported rates and no additional capabilities (unless
+ *	added by userspace explicitly.)
+ * @NL80211_SCAN_FLAG_FREQ_KHZ: report scan results with
+ *	%NL80211_ATTR_SCAN_FREQ_KHZ. This also means
+ *	%NL80211_ATTR_SCAN_FREQUENCIES will not be included.
+ * @NL80211_SCAN_FLAG_COLOCATED_6GHZ: scan for collocated APs reported by
+ *	2.4/5 GHz APs. When the flag is set, the scan logic will use the
+ *	information from the RNR element found in beacons/probe responses
+ *	received on the 2.4/5 GHz channels to actively scan only the 6GHz
+ *	channels on which APs are expected to be found. Note that when not set,
+ *	the scan logic would scan all 6GHz channels, but since transmission of
+ *	probe requests on non PSC channels is limited, it is highly likely that
+ *	these channels would passively be scanned. Also note that when the flag
+ *	is set, in addition to the colocated APs, PSC channels would also be
+ *	scanned if the user space has asked for it.
+ */
+enum nl80211_scan_flags {
+	NL80211_SCAN_FLAG_LOW_PRIORITY				= 1<<0,
+	NL80211_SCAN_FLAG_FLUSH					= 1<<1,
+	NL80211_SCAN_FLAG_AP					= 1<<2,
+	NL80211_SCAN_FLAG_RANDOM_ADDR				= 1<<3,
+	NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME			= 1<<4,
+	NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP		= 1<<5,
+	NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE		= 1<<6,
+	NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION	= 1<<7,
+	NL80211_SCAN_FLAG_LOW_SPAN				= 1<<8,
+	NL80211_SCAN_FLAG_LOW_POWER				= 1<<9,
+	NL80211_SCAN_FLAG_HIGH_ACCURACY				= 1<<10,
+	NL80211_SCAN_FLAG_RANDOM_SN				= 1<<11,
+	NL80211_SCAN_FLAG_MIN_PREQ_CONTENT			= 1<<12,
+	NL80211_SCAN_FLAG_FREQ_KHZ				= 1<<13,
+	NL80211_SCAN_FLAG_COLOCATED_6GHZ			= 1<<14,
+};
+
+/**
+ * enum nl80211_acl_policy - access control policy
+ *
+ * Access control policy is applied on a MAC list set by
+ * %NL80211_CMD_START_AP and %NL80211_CMD_SET_MAC_ACL, to
+ * be used with %NL80211_ATTR_ACL_POLICY.
+ *
+ * @NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED: Deny stations which are
+ *	listed in ACL, i.e. allow all the stations which are not listed
+ *	in ACL to authenticate.
+ * @NL80211_ACL_POLICY_DENY_UNLESS_LISTED: Allow the stations which are listed
+ *	in ACL, i.e. deny all the stations which are not listed in ACL.
+ */
+enum nl80211_acl_policy {
+	NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED,
+	NL80211_ACL_POLICY_DENY_UNLESS_LISTED,
+};
+
+/**
+ * enum nl80211_smps_mode - SMPS mode
+ *
+ * Requested SMPS mode (for AP mode)
+ *
+ * @NL80211_SMPS_OFF: SMPS off (use all antennas).
+ * @NL80211_SMPS_STATIC: static SMPS (use a single antenna)
+ * @NL80211_SMPS_DYNAMIC: dynamic smps (start with a single antenna and
+ *	turn on other antennas after CTS/RTS).
+ */
+enum nl80211_smps_mode {
+	NL80211_SMPS_OFF,
+	NL80211_SMPS_STATIC,
+	NL80211_SMPS_DYNAMIC,
+
+	__NL80211_SMPS_AFTER_LAST,
+	NL80211_SMPS_MAX = __NL80211_SMPS_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_radar_event - type of radar event for DFS operation
+ *
+ * Type of event to be used with NL80211_ATTR_RADAR_EVENT to inform userspace
+ * about detected radars or success of the channel available check (CAC)
+ *
+ * @NL80211_RADAR_DETECTED: A radar pattern has been detected. The channel is
+ *	now unusable.
+ * @NL80211_RADAR_CAC_FINISHED: Channel Availability Check has been finished,
+ *	the channel is now available.
+ * @NL80211_RADAR_CAC_ABORTED: Channel Availability Check has been aborted, no
+ *	change to the channel status.
+ * @NL80211_RADAR_NOP_FINISHED: The Non-Occupancy Period for this channel is
+ *	over, channel becomes usable.
+ * @NL80211_RADAR_PRE_CAC_EXPIRED: Channel Availability Check done on this
+ *	non-operating channel is expired and no longer valid. New CAC must
+ *	be done on this channel before starting the operation. This is not
+ *	applicable for ETSI dfs domain where pre-CAC is valid for ever.
+ * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started,
+ *	should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled.
+ */
+enum nl80211_radar_event {
+	NL80211_RADAR_DETECTED,
+	NL80211_RADAR_CAC_FINISHED,
+	NL80211_RADAR_CAC_ABORTED,
+	NL80211_RADAR_NOP_FINISHED,
+	NL80211_RADAR_PRE_CAC_EXPIRED,
+	NL80211_RADAR_CAC_STARTED,
+};
+
+/**
+ * enum nl80211_dfs_state - DFS states for channels
+ *
+ * Channel states used by the DFS code.
+ *
+ * @NL80211_DFS_USABLE: The channel can be used, but channel availability
+ *	check (CAC) must be performed before using it for AP or IBSS.
+ * @NL80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it
+ *	is therefore marked as not available.
+ * @NL80211_DFS_AVAILABLE: The channel has been CAC checked and is available.
+ */
+enum nl80211_dfs_state {
+	NL80211_DFS_USABLE,
+	NL80211_DFS_UNAVAILABLE,
+	NL80211_DFS_AVAILABLE,
+};
+
+/**
+ * enum nl80211_protocol_features - nl80211 protocol features
+ * @NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP: nl80211 supports splitting
+ *	wiphy dumps (if requested by the application with the attribute
+ *	%NL80211_ATTR_SPLIT_WIPHY_DUMP. Also supported is filtering the
+ *	wiphy dump by %NL80211_ATTR_WIPHY, %NL80211_ATTR_IFINDEX or
+ *	%NL80211_ATTR_WDEV.
+ */
+enum nl80211_protocol_features {
+	NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP =	1 << 0,
+};
+
+/**
+ * enum nl80211_crit_proto_id - nl80211 critical protocol identifiers
+ *
+ * @NL80211_CRIT_PROTO_UNSPEC: protocol unspecified.
+ * @NL80211_CRIT_PROTO_DHCP: BOOTP or DHCPv6 protocol.
+ * @NL80211_CRIT_PROTO_EAPOL: EAPOL protocol.
+ * @NL80211_CRIT_PROTO_APIPA: APIPA protocol.
+ * @NUM_NL80211_CRIT_PROTO: must be kept last.
+ */
+enum nl80211_crit_proto_id {
+	NL80211_CRIT_PROTO_UNSPEC,
+	NL80211_CRIT_PROTO_DHCP,
+	NL80211_CRIT_PROTO_EAPOL,
+	NL80211_CRIT_PROTO_APIPA,
+	/* add other protocols before this one */
+	NUM_NL80211_CRIT_PROTO
+};
+
+/* maximum duration for critical protocol measures */
+#define NL80211_CRIT_PROTO_MAX_DURATION		5000 /* msec */
+
+/**
+ * enum nl80211_rxmgmt_flags - flags for received management frame.
+ *
+ * Used by cfg80211_rx_mgmt()
+ *
+ * @NL80211_RXMGMT_FLAG_ANSWERED: frame was answered by device/driver.
+ * @NL80211_RXMGMT_FLAG_EXTERNAL_AUTH: Host driver intends to offload
+ *	the authentication. Exclusively defined for host drivers that
+ *	advertises the SME functionality but would like the userspace
+ *	to handle certain authentication algorithms (e.g. SAE).
+ */
+enum nl80211_rxmgmt_flags {
+	NL80211_RXMGMT_FLAG_ANSWERED = 1 << 0,
+	NL80211_RXMGMT_FLAG_EXTERNAL_AUTH = 1 << 1,
+};
+
+/*
+ * If this flag is unset, the lower 24 bits are an OUI, if set
+ * a Linux nl80211 vendor ID is used (no such IDs are allocated
+ * yet, so that's not valid so far)
+ */
+#define NL80211_VENDOR_ID_IS_LINUX	0x80000000
+
+/**
+ * struct nl80211_vendor_cmd_info - vendor command data
+ * @vendor_id: If the %NL80211_VENDOR_ID_IS_LINUX flag is clear, then the
+ *	value is a 24-bit OUI; if it is set then a separately allocated ID
+ *	may be used, but no such IDs are allocated yet. New IDs should be
+ *	added to this file when needed.
+ * @subcmd: sub-command ID for the command
+ */
+struct nl80211_vendor_cmd_info {
+	__u32 vendor_id;
+	__u32 subcmd;
+};
+
+/**
+ * enum nl80211_tdls_peer_capability - TDLS peer flags.
+ *
+ * Used by tdls_mgmt() to determine which conditional elements need
+ * to be added to TDLS Setup frames.
+ *
+ * @NL80211_TDLS_PEER_HT: TDLS peer is HT capable.
+ * @NL80211_TDLS_PEER_VHT: TDLS peer is VHT capable.
+ * @NL80211_TDLS_PEER_WMM: TDLS peer is WMM capable.
+ * @NL80211_TDLS_PEER_HE: TDLS peer is HE capable.
+ */
+enum nl80211_tdls_peer_capability {
+	NL80211_TDLS_PEER_HT = 1<<0,
+	NL80211_TDLS_PEER_VHT = 1<<1,
+	NL80211_TDLS_PEER_WMM = 1<<2,
+	NL80211_TDLS_PEER_HE = 1<<3,
+};
+
+/**
+ * enum nl80211_sched_scan_plan - scanning plan for scheduled scan
+ * @__NL80211_SCHED_SCAN_PLAN_INVALID: attribute number 0 is reserved
+ * @NL80211_SCHED_SCAN_PLAN_INTERVAL: interval between scan iterations. In
+ *	seconds (u32).
+ * @NL80211_SCHED_SCAN_PLAN_ITERATIONS: number of scan iterations in this
+ *	scan plan (u32). The last scan plan must not specify this attribute
+ *	because it will run infinitely. A value of zero is invalid as it will
+ *	make the scan plan meaningless.
+ * @NL80211_SCHED_SCAN_PLAN_MAX: highest scheduled scan plan attribute number
+ *	currently defined
+ * @__NL80211_SCHED_SCAN_PLAN_AFTER_LAST: internal use
+ */
+enum nl80211_sched_scan_plan {
+	__NL80211_SCHED_SCAN_PLAN_INVALID,
+	NL80211_SCHED_SCAN_PLAN_INTERVAL,
+	NL80211_SCHED_SCAN_PLAN_ITERATIONS,
+
+	/* keep last */
+	__NL80211_SCHED_SCAN_PLAN_AFTER_LAST,
+	NL80211_SCHED_SCAN_PLAN_MAX =
+		__NL80211_SCHED_SCAN_PLAN_AFTER_LAST - 1
+};
+
+/**
+ * struct nl80211_bss_select_rssi_adjust - RSSI adjustment parameters.
+ *
+ * @band: band of BSS that must match for RSSI value adjustment. The value
+ *	of this field is according to &enum nl80211_band.
+ * @delta: value used to adjust the RSSI value of matching BSS in dB.
+ */
+struct nl80211_bss_select_rssi_adjust {
+	__u8 band;
+	__s8 delta;
+} __attribute__((packed));
+
+/**
+ * enum nl80211_bss_select_attr - attributes for bss selection.
+ *
+ * @__NL80211_BSS_SELECT_ATTR_INVALID: reserved.
+ * @NL80211_BSS_SELECT_ATTR_RSSI: Flag indicating only RSSI-based BSS selection
+ *	is requested.
+ * @NL80211_BSS_SELECT_ATTR_BAND_PREF: attribute indicating BSS
+ *	selection should be done such that the specified band is preferred.
+ *	When there are multiple BSS-es in the preferred band, the driver
+ *	shall use RSSI-based BSS selection as a second step. The value of
+ *	this attribute is according to &enum nl80211_band (u32).
+ * @NL80211_BSS_SELECT_ATTR_RSSI_ADJUST: When present the RSSI level for
+ *	BSS-es in the specified band is to be adjusted before doing
+ *	RSSI-based BSS selection. The attribute value is a packed structure
+ *	value as specified by &struct nl80211_bss_select_rssi_adjust.
+ * @NL80211_BSS_SELECT_ATTR_MAX: highest bss select attribute number.
+ * @__NL80211_BSS_SELECT_ATTR_AFTER_LAST: internal use.
+ *
+ * One and only one of these attributes are found within %NL80211_ATTR_BSS_SELECT
+ * for %NL80211_CMD_CONNECT. It specifies the required BSS selection behaviour
+ * which the driver shall use.
+ */
+enum nl80211_bss_select_attr {
+	__NL80211_BSS_SELECT_ATTR_INVALID,
+	NL80211_BSS_SELECT_ATTR_RSSI,
+	NL80211_BSS_SELECT_ATTR_BAND_PREF,
+	NL80211_BSS_SELECT_ATTR_RSSI_ADJUST,
+
+	/* keep last */
+	__NL80211_BSS_SELECT_ATTR_AFTER_LAST,
+	NL80211_BSS_SELECT_ATTR_MAX = __NL80211_BSS_SELECT_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_nan_function_type - NAN function type
+ *
+ * Defines the function type of a NAN function
+ *
+ * @NL80211_NAN_FUNC_PUBLISH: function is publish
+ * @NL80211_NAN_FUNC_SUBSCRIBE: function is subscribe
+ * @NL80211_NAN_FUNC_FOLLOW_UP: function is follow-up
+ */
+enum nl80211_nan_function_type {
+	NL80211_NAN_FUNC_PUBLISH,
+	NL80211_NAN_FUNC_SUBSCRIBE,
+	NL80211_NAN_FUNC_FOLLOW_UP,
+
+	/* keep last */
+	__NL80211_NAN_FUNC_TYPE_AFTER_LAST,
+	NL80211_NAN_FUNC_MAX_TYPE = __NL80211_NAN_FUNC_TYPE_AFTER_LAST - 1,
+};
+
+/**
+ * enum nl80211_nan_publish_type - NAN publish tx type
+ *
+ * Defines how to send publish Service Discovery Frames
+ *
+ * @NL80211_NAN_SOLICITED_PUBLISH: publish function is solicited
+ * @NL80211_NAN_UNSOLICITED_PUBLISH: publish function is unsolicited
+ */
+enum nl80211_nan_publish_type {
+	NL80211_NAN_SOLICITED_PUBLISH = 1 << 0,
+	NL80211_NAN_UNSOLICITED_PUBLISH = 1 << 1,
+};
+
+/**
+ * enum nl80211_nan_func_term_reason - NAN functions termination reason
+ *
+ * Defines termination reasons of a NAN function
+ *
+ * @NL80211_NAN_FUNC_TERM_REASON_USER_REQUEST: requested by user
+ * @NL80211_NAN_FUNC_TERM_REASON_TTL_EXPIRED: timeout
+ * @NL80211_NAN_FUNC_TERM_REASON_ERROR: errored
+ */
+enum nl80211_nan_func_term_reason {
+	NL80211_NAN_FUNC_TERM_REASON_USER_REQUEST,
+	NL80211_NAN_FUNC_TERM_REASON_TTL_EXPIRED,
+	NL80211_NAN_FUNC_TERM_REASON_ERROR,
+};
+
+#define NL80211_NAN_FUNC_SERVICE_ID_LEN 6
+#define NL80211_NAN_FUNC_SERVICE_SPEC_INFO_MAX_LEN 0xff
+#define NL80211_NAN_FUNC_SRF_MAX_LEN 0xff
+
+/**
+ * enum nl80211_nan_func_attributes - NAN function attributes
+ * @__NL80211_NAN_FUNC_INVALID: invalid
+ * @NL80211_NAN_FUNC_TYPE: &enum nl80211_nan_function_type (u8).
+ * @NL80211_NAN_FUNC_SERVICE_ID: 6 bytes of the service ID hash as
+ *	specified in NAN spec. This is a binary attribute.
+ * @NL80211_NAN_FUNC_PUBLISH_TYPE: relevant if the function's type is
+ *	publish. Defines the transmission type for the publish Service Discovery
+ *	Frame, see &enum nl80211_nan_publish_type. Its type is u8.
+ * @NL80211_NAN_FUNC_PUBLISH_BCAST: relevant if the function is a solicited
+ *	publish. Should the solicited publish Service Discovery Frame be sent to
+ *	the NAN Broadcast address. This is a flag.
+ * @NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE: relevant if the function's type is
+ *	subscribe. Is the subscribe active. This is a flag.
+ * @NL80211_NAN_FUNC_FOLLOW_UP_ID: relevant if the function's type is follow up.
+ *	The instance ID for the follow up Service Discovery Frame. This is u8.
+ * @NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID: relevant if the function's type
+ *	is follow up. This is a u8.
+ *	The requestor instance ID for the follow up Service Discovery Frame.
+ * @NL80211_NAN_FUNC_FOLLOW_UP_DEST: the MAC address of the recipient of the
+ *	follow up Service Discovery Frame. This is a binary attribute.
+ * @NL80211_NAN_FUNC_CLOSE_RANGE: is this function limited for devices in a
+ *	close range. The range itself (RSSI) is defined by the device.
+ *	This is a flag.
+ * @NL80211_NAN_FUNC_TTL: strictly positive number of DWs this function should
+ *	stay active. If not present infinite TTL is assumed. This is a u32.
+ * @NL80211_NAN_FUNC_SERVICE_INFO: array of bytes describing the service
+ *	specific info. This is a binary attribute.
+ * @NL80211_NAN_FUNC_SRF: Service Receive Filter. This is a nested attribute.
+ *	See &enum nl80211_nan_srf_attributes.
+ * @NL80211_NAN_FUNC_RX_MATCH_FILTER: Receive Matching filter. This is a nested
+ *	attribute. It is a list of binary values.
+ * @NL80211_NAN_FUNC_TX_MATCH_FILTER: Transmit Matching filter. This is a
+ *	nested attribute. It is a list of binary values.
+ * @NL80211_NAN_FUNC_INSTANCE_ID: The instance ID of the function.
+ *	Its type is u8 and it cannot be 0.
+ * @NL80211_NAN_FUNC_TERM_REASON: NAN function termination reason.
+ *	See &enum nl80211_nan_func_term_reason.
+ *
+ * @NUM_NL80211_NAN_FUNC_ATTR: internal
+ * @NL80211_NAN_FUNC_ATTR_MAX: highest NAN function attribute
+ */
+enum nl80211_nan_func_attributes {
+	__NL80211_NAN_FUNC_INVALID,
+	NL80211_NAN_FUNC_TYPE,
+	NL80211_NAN_FUNC_SERVICE_ID,
+	NL80211_NAN_FUNC_PUBLISH_TYPE,
+	NL80211_NAN_FUNC_PUBLISH_BCAST,
+	NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE,
+	NL80211_NAN_FUNC_FOLLOW_UP_ID,
+	NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID,
+	NL80211_NAN_FUNC_FOLLOW_UP_DEST,
+	NL80211_NAN_FUNC_CLOSE_RANGE,
+	NL80211_NAN_FUNC_TTL,
+	NL80211_NAN_FUNC_SERVICE_INFO,
+	NL80211_NAN_FUNC_SRF,
+	NL80211_NAN_FUNC_RX_MATCH_FILTER,
+	NL80211_NAN_FUNC_TX_MATCH_FILTER,
+	NL80211_NAN_FUNC_INSTANCE_ID,
+	NL80211_NAN_FUNC_TERM_REASON,
+
+	/* keep last */
+	NUM_NL80211_NAN_FUNC_ATTR,
+	NL80211_NAN_FUNC_ATTR_MAX = NUM_NL80211_NAN_FUNC_ATTR - 1
+};
+
+/**
+ * enum nl80211_nan_srf_attributes - NAN Service Response filter attributes
+ * @__NL80211_NAN_SRF_INVALID: invalid
+ * @NL80211_NAN_SRF_INCLUDE: present if the include bit of the SRF set.
+ *	This is a flag.
+ * @NL80211_NAN_SRF_BF: Bloom Filter. Present if and only if
+ *	%NL80211_NAN_SRF_MAC_ADDRS isn't present. This attribute is binary.
+ * @NL80211_NAN_SRF_BF_IDX: index of the Bloom Filter. Mandatory if
+ *	%NL80211_NAN_SRF_BF is present. This is a u8.
+ * @NL80211_NAN_SRF_MAC_ADDRS: list of MAC addresses for the SRF. Present if
+ *	and only if %NL80211_NAN_SRF_BF isn't present. This is a nested
+ *	attribute. Each nested attribute is a MAC address.
+ * @NUM_NL80211_NAN_SRF_ATTR: internal
+ * @NL80211_NAN_SRF_ATTR_MAX: highest NAN SRF attribute
+ */
+enum nl80211_nan_srf_attributes {
+	__NL80211_NAN_SRF_INVALID,
+	NL80211_NAN_SRF_INCLUDE,
+	NL80211_NAN_SRF_BF,
+	NL80211_NAN_SRF_BF_IDX,
+	NL80211_NAN_SRF_MAC_ADDRS,
+
+	/* keep last */
+	NUM_NL80211_NAN_SRF_ATTR,
+	NL80211_NAN_SRF_ATTR_MAX = NUM_NL80211_NAN_SRF_ATTR - 1,
+};
+
+/**
+ * enum nl80211_nan_match_attributes - NAN match attributes
+ * @__NL80211_NAN_MATCH_INVALID: invalid
+ * @NL80211_NAN_MATCH_FUNC_LOCAL: the local function that had the
+ *	match. This is a nested attribute.
+ *	See &enum nl80211_nan_func_attributes.
+ * @NL80211_NAN_MATCH_FUNC_PEER: the peer function
+ *	that caused the match. This is a nested attribute.
+ *	See &enum nl80211_nan_func_attributes.
+ *
+ * @NUM_NL80211_NAN_MATCH_ATTR: internal
+ * @NL80211_NAN_MATCH_ATTR_MAX: highest NAN match attribute
+ */
+enum nl80211_nan_match_attributes {
+	__NL80211_NAN_MATCH_INVALID,
+	NL80211_NAN_MATCH_FUNC_LOCAL,
+	NL80211_NAN_MATCH_FUNC_PEER,
+
+	/* keep last */
+	NUM_NL80211_NAN_MATCH_ATTR,
+	NL80211_NAN_MATCH_ATTR_MAX = NUM_NL80211_NAN_MATCH_ATTR - 1
+};
+
+/**
+ * nl80211_external_auth_action - Action to perform with external
+ *     authentication request. Used by NL80211_ATTR_EXTERNAL_AUTH_ACTION.
+ * @NL80211_EXTERNAL_AUTH_START: Start the authentication.
+ * @NL80211_EXTERNAL_AUTH_ABORT: Abort the ongoing authentication.
+ */
+enum nl80211_external_auth_action {
+	NL80211_EXTERNAL_AUTH_START,
+	NL80211_EXTERNAL_AUTH_ABORT,
+};
+
+/**
+ * enum nl80211_ftm_responder_attributes - fine timing measurement
+ *	responder attributes
+ * @__NL80211_FTM_RESP_ATTR_INVALID: Invalid
+ * @NL80211_FTM_RESP_ATTR_ENABLED: FTM responder is enabled
+ * @NL80211_FTM_RESP_ATTR_LCI: The content of Measurement Report Element
+ *	(9.4.2.22 in 802.11-2016) with type 8 - LCI (9.4.2.22.10),
+ *	i.e. starting with the measurement token
+ * @NL80211_FTM_RESP_ATTR_CIVIC: The content of Measurement Report Element
+ *	(9.4.2.22 in 802.11-2016) with type 11 - Civic (Section 9.4.2.22.13),
+ *	i.e. starting with the measurement token
+ * @__NL80211_FTM_RESP_ATTR_LAST: Internal
+ * @NL80211_FTM_RESP_ATTR_MAX: highest FTM responder attribute.
+ */
+enum nl80211_ftm_responder_attributes {
+	__NL80211_FTM_RESP_ATTR_INVALID,
+
+	NL80211_FTM_RESP_ATTR_ENABLED,
+	NL80211_FTM_RESP_ATTR_LCI,
+	NL80211_FTM_RESP_ATTR_CIVICLOC,
+
+	/* keep last */
+	__NL80211_FTM_RESP_ATTR_LAST,
+	NL80211_FTM_RESP_ATTR_MAX = __NL80211_FTM_RESP_ATTR_LAST - 1,
+};
+
+/*
+ * enum nl80211_ftm_responder_stats - FTM responder statistics
+ *
+ * These attribute types are used with %NL80211_ATTR_FTM_RESPONDER_STATS
+ * when getting FTM responder statistics.
+ *
+ * @__NL80211_FTM_STATS_INVALID: attribute number 0 is reserved
+ * @NL80211_FTM_STATS_SUCCESS_NUM: number of FTM sessions in which all frames
+ *	were ssfully answered (u32)
+ * @NL80211_FTM_STATS_PARTIAL_NUM: number of FTM sessions in which part of the
+ *	frames were successfully answered (u32)
+ * @NL80211_FTM_STATS_FAILED_NUM: number of failed FTM sessions (u32)
+ * @NL80211_FTM_STATS_ASAP_NUM: number of ASAP sessions (u32)
+ * @NL80211_FTM_STATS_NON_ASAP_NUM: number of non-ASAP sessions (u32)
+ * @NL80211_FTM_STATS_TOTAL_DURATION_MSEC: total sessions durations - gives an
+ *	indication of how much time the responder was busy (u64, msec)
+ * @NL80211_FTM_STATS_UNKNOWN_TRIGGERS_NUM: number of unknown FTM triggers -
+ *	triggers from initiators that didn't finish successfully the negotiation
+ *	phase with the responder (u32)
+ * @NL80211_FTM_STATS_RESCHEDULE_REQUESTS_NUM: number of FTM reschedule requests
+ *	- initiator asks for a new scheduling although it already has scheduled
+ *	FTM slot (u32)
+ * @NL80211_FTM_STATS_OUT_OF_WINDOW_TRIGGERS_NUM: number of FTM triggers out of
+ *	scheduled window (u32)
+ * @NL80211_FTM_STATS_PAD: used for padding, ignore
+ * @__NL80211_TXQ_ATTR_AFTER_LAST: Internal
+ * @NL80211_FTM_STATS_MAX: highest possible FTM responder stats attribute
+ */
+enum nl80211_ftm_responder_stats {
+	__NL80211_FTM_STATS_INVALID,
+	NL80211_FTM_STATS_SUCCESS_NUM,
+	NL80211_FTM_STATS_PARTIAL_NUM,
+	NL80211_FTM_STATS_FAILED_NUM,
+	NL80211_FTM_STATS_ASAP_NUM,
+	NL80211_FTM_STATS_NON_ASAP_NUM,
+	NL80211_FTM_STATS_TOTAL_DURATION_MSEC,
+	NL80211_FTM_STATS_UNKNOWN_TRIGGERS_NUM,
+	NL80211_FTM_STATS_RESCHEDULE_REQUESTS_NUM,
+	NL80211_FTM_STATS_OUT_OF_WINDOW_TRIGGERS_NUM,
+	NL80211_FTM_STATS_PAD,
+
+	/* keep last */
+	__NL80211_FTM_STATS_AFTER_LAST,
+	NL80211_FTM_STATS_MAX = __NL80211_FTM_STATS_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_preamble - frame preamble types
+ * @NL80211_PREAMBLE_LEGACY: legacy (HR/DSSS, OFDM, ERP PHY) preamble
+ * @NL80211_PREAMBLE_HT: HT preamble
+ * @NL80211_PREAMBLE_VHT: VHT preamble
+ * @NL80211_PREAMBLE_DMG: DMG preamble
+ * @NL80211_PREAMBLE_HE: HE preamble
+ */
+enum nl80211_preamble {
+	NL80211_PREAMBLE_LEGACY,
+	NL80211_PREAMBLE_HT,
+	NL80211_PREAMBLE_VHT,
+	NL80211_PREAMBLE_DMG,
+	NL80211_PREAMBLE_HE,
+};
+
+/**
+ * enum nl80211_peer_measurement_type - peer measurement types
+ * @NL80211_PMSR_TYPE_INVALID: invalid/unused, needed as we use
+ *	these numbers also for attributes
+ *
+ * @NL80211_PMSR_TYPE_FTM: flight time measurement
+ *
+ * @NUM_NL80211_PMSR_TYPES: internal
+ * @NL80211_PMSR_TYPE_MAX: highest type number
+ */
+enum nl80211_peer_measurement_type {
+	NL80211_PMSR_TYPE_INVALID,
+
+	NL80211_PMSR_TYPE_FTM,
+
+	NUM_NL80211_PMSR_TYPES,
+	NL80211_PMSR_TYPE_MAX = NUM_NL80211_PMSR_TYPES - 1
+};
+
+/**
+ * enum nl80211_peer_measurement_status - peer measurement status
+ * @NL80211_PMSR_STATUS_SUCCESS: measurement completed successfully
+ * @NL80211_PMSR_STATUS_REFUSED: measurement was locally refused
+ * @NL80211_PMSR_STATUS_TIMEOUT: measurement timed out
+ * @NL80211_PMSR_STATUS_FAILURE: measurement failed, a type-dependent
+ *	reason may be available in the response data
+ */
+enum nl80211_peer_measurement_status {
+	NL80211_PMSR_STATUS_SUCCESS,
+	NL80211_PMSR_STATUS_REFUSED,
+	NL80211_PMSR_STATUS_TIMEOUT,
+	NL80211_PMSR_STATUS_FAILURE,
+};
+
+/**
+ * enum nl80211_peer_measurement_req - peer measurement request attributes
+ * @__NL80211_PMSR_REQ_ATTR_INVALID: invalid
+ *
+ * @NL80211_PMSR_REQ_ATTR_DATA: This is a nested attribute with measurement
+ *	type-specific request data inside. The attributes used are from the
+ *	enums named nl80211_peer_measurement_<type>_req.
+ * @NL80211_PMSR_REQ_ATTR_GET_AP_TSF: include AP TSF timestamp, if supported
+ *	(flag attribute)
+ *
+ * @NUM_NL80211_PMSR_REQ_ATTRS: internal
+ * @NL80211_PMSR_REQ_ATTR_MAX: highest attribute number
+ */
+enum nl80211_peer_measurement_req {
+	__NL80211_PMSR_REQ_ATTR_INVALID,
+
+	NL80211_PMSR_REQ_ATTR_DATA,
+	NL80211_PMSR_REQ_ATTR_GET_AP_TSF,
+
+	/* keep last */
+	NUM_NL80211_PMSR_REQ_ATTRS,
+	NL80211_PMSR_REQ_ATTR_MAX = NUM_NL80211_PMSR_REQ_ATTRS - 1
+};
+
+/**
+ * enum nl80211_peer_measurement_resp - peer measurement response attributes
+ * @__NL80211_PMSR_RESP_ATTR_INVALID: invalid
+ *
+ * @NL80211_PMSR_RESP_ATTR_DATA: This is a nested attribute with measurement
+ *	type-specific results inside. The attributes used are from the enums
+ *	named nl80211_peer_measurement_<type>_resp.
+ * @NL80211_PMSR_RESP_ATTR_STATUS: u32 value with the measurement status
+ *	(using values from &enum nl80211_peer_measurement_status.)
+ * @NL80211_PMSR_RESP_ATTR_HOST_TIME: host time (%CLOCK_BOOTTIME) when the
+ *	result was measured; this value is not expected to be accurate to
+ *	more than 20ms. (u64, nanoseconds)
+ * @NL80211_PMSR_RESP_ATTR_AP_TSF: TSF of the AP that the interface
+ *	doing the measurement is connected to when the result was measured.
+ *	This shall be accurately reported if supported and requested
+ *	(u64, usec)
+ * @NL80211_PMSR_RESP_ATTR_FINAL: If results are sent to the host partially
+ *	(*e.g. with FTM per-burst data) this flag will be cleared on all but
+ *	the last result; if all results are combined it's set on the single
+ *	result.
+ * @NL80211_PMSR_RESP_ATTR_PAD: padding for 64-bit attributes, ignore
+ *
+ * @NUM_NL80211_PMSR_RESP_ATTRS: internal
+ * @NL80211_PMSR_RESP_ATTR_MAX: highest attribute number
+ */
+enum nl80211_peer_measurement_resp {
+	__NL80211_PMSR_RESP_ATTR_INVALID,
+
+	NL80211_PMSR_RESP_ATTR_DATA,
+	NL80211_PMSR_RESP_ATTR_STATUS,
+	NL80211_PMSR_RESP_ATTR_HOST_TIME,
+	NL80211_PMSR_RESP_ATTR_AP_TSF,
+	NL80211_PMSR_RESP_ATTR_FINAL,
+	NL80211_PMSR_RESP_ATTR_PAD,
+
+	/* keep last */
+	NUM_NL80211_PMSR_RESP_ATTRS,
+	NL80211_PMSR_RESP_ATTR_MAX = NUM_NL80211_PMSR_RESP_ATTRS - 1
+};
+
+/**
+ * enum nl80211_peer_measurement_peer_attrs - peer attributes for measurement
+ * @__NL80211_PMSR_PEER_ATTR_INVALID: invalid
+ *
+ * @NL80211_PMSR_PEER_ATTR_ADDR: peer's MAC address
+ * @NL80211_PMSR_PEER_ATTR_CHAN: channel definition, nested, using top-level
+ *	attributes like %NL80211_ATTR_WIPHY_FREQ etc.
+ * @NL80211_PMSR_PEER_ATTR_REQ: This is a nested attribute indexed by
+ *	measurement type, with attributes from the
+ *	&enum nl80211_peer_measurement_req inside.
+ * @NL80211_PMSR_PEER_ATTR_RESP: This is a nested attribute indexed by
+ *	measurement type, with attributes from the
+ *	&enum nl80211_peer_measurement_resp inside.
+ *
+ * @NUM_NL80211_PMSR_PEER_ATTRS: internal
+ * @NL80211_PMSR_PEER_ATTR_MAX: highest attribute number
+ */
+enum nl80211_peer_measurement_peer_attrs {
+	__NL80211_PMSR_PEER_ATTR_INVALID,
+
+	NL80211_PMSR_PEER_ATTR_ADDR,
+	NL80211_PMSR_PEER_ATTR_CHAN,
+	NL80211_PMSR_PEER_ATTR_REQ,
+	NL80211_PMSR_PEER_ATTR_RESP,
+
+	/* keep last */
+	NUM_NL80211_PMSR_PEER_ATTRS,
+	NL80211_PMSR_PEER_ATTR_MAX = NUM_NL80211_PMSR_PEER_ATTRS - 1,
+};
+
+/**
+ * enum nl80211_peer_measurement_attrs - peer measurement attributes
+ * @__NL80211_PMSR_ATTR_INVALID: invalid
+ *
+ * @NL80211_PMSR_ATTR_MAX_PEERS: u32 attribute used for capability
+ *	advertisement only, indicates the maximum number of peers
+ *	measurements can be done with in a single request
+ * @NL80211_PMSR_ATTR_REPORT_AP_TSF: flag attribute in capability
+ *	indicating that the connected AP's TSF can be reported in
+ *	measurement results
+ * @NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR: flag attribute in capability
+ *	indicating that MAC address randomization is supported.
+ * @NL80211_PMSR_ATTR_TYPE_CAPA: capabilities reported by the device,
+ *	this contains a nesting indexed by measurement type, and
+ *	type-specific capabilities inside, which are from the enums
+ *	named nl80211_peer_measurement_<type>_capa.
+ * @NL80211_PMSR_ATTR_PEERS: nested attribute, the nesting index is
+ *	meaningless, just a list of peers to measure with, with the
+ *	sub-attributes taken from
+ *	&enum nl80211_peer_measurement_peer_attrs.
+ *
+ * @NUM_NL80211_PMSR_ATTR: internal
+ * @NL80211_PMSR_ATTR_MAX: highest attribute number
+ */
+enum nl80211_peer_measurement_attrs {
+	__NL80211_PMSR_ATTR_INVALID,
+
+	NL80211_PMSR_ATTR_MAX_PEERS,
+	NL80211_PMSR_ATTR_REPORT_AP_TSF,
+	NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR,
+	NL80211_PMSR_ATTR_TYPE_CAPA,
+	NL80211_PMSR_ATTR_PEERS,
+
+	/* keep last */
+	NUM_NL80211_PMSR_ATTR,
+	NL80211_PMSR_ATTR_MAX = NUM_NL80211_PMSR_ATTR - 1
+};
+
+/**
+ * enum nl80211_peer_measurement_ftm_capa - FTM capabilities
+ * @__NL80211_PMSR_FTM_CAPA_ATTR_INVALID: invalid
+ *
+ * @NL80211_PMSR_FTM_CAPA_ATTR_ASAP: flag attribute indicating ASAP mode
+ *	is supported
+ * @NL80211_PMSR_FTM_CAPA_ATTR_NON_ASAP: flag attribute indicating non-ASAP
+ *	mode is supported
+ * @NL80211_PMSR_FTM_CAPA_ATTR_REQ_LCI: flag attribute indicating if LCI
+ *	data can be requested during the measurement
+ * @NL80211_PMSR_FTM_CAPA_ATTR_REQ_CIVICLOC: flag attribute indicating if civic
+ *	location data can be requested during the measurement
+ * @NL80211_PMSR_FTM_CAPA_ATTR_PREAMBLES: u32 bitmap attribute of bits
+ *	from &enum nl80211_preamble.
+ * @NL80211_PMSR_FTM_CAPA_ATTR_BANDWIDTHS: bitmap of values from
+ *	&enum nl80211_chan_width indicating the supported channel
+ *	bandwidths for FTM. Note that a higher channel bandwidth may be
+ *	configured to allow for other measurements types with different
+ *	bandwidth requirement in the same measurement.
+ * @NL80211_PMSR_FTM_CAPA_ATTR_MAX_BURSTS_EXPONENT: u32 attribute indicating
+ *	the maximum bursts exponent that can be used (if not present anything
+ *	is valid)
+ * @NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST: u32 attribute indicating
+ *	the maximum FTMs per burst (if not present anything is valid)
+ * @NL80211_PMSR_FTM_CAPA_ATTR_TRIGGER_BASED: flag attribute indicating if
+ *	trigger based ranging measurement is supported
+ * @NL80211_PMSR_FTM_CAPA_ATTR_NON_TRIGGER_BASED: flag attribute indicating
+ *	if non trigger based ranging measurement is supported
+ *
+ * @NUM_NL80211_PMSR_FTM_CAPA_ATTR: internal
+ * @NL80211_PMSR_FTM_CAPA_ATTR_MAX: highest attribute number
+ */
+enum nl80211_peer_measurement_ftm_capa {
+	__NL80211_PMSR_FTM_CAPA_ATTR_INVALID,
+
+	NL80211_PMSR_FTM_CAPA_ATTR_ASAP,
+	NL80211_PMSR_FTM_CAPA_ATTR_NON_ASAP,
+	NL80211_PMSR_FTM_CAPA_ATTR_REQ_LCI,
+	NL80211_PMSR_FTM_CAPA_ATTR_REQ_CIVICLOC,
+	NL80211_PMSR_FTM_CAPA_ATTR_PREAMBLES,
+	NL80211_PMSR_FTM_CAPA_ATTR_BANDWIDTHS,
+	NL80211_PMSR_FTM_CAPA_ATTR_MAX_BURSTS_EXPONENT,
+	NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST,
+	NL80211_PMSR_FTM_CAPA_ATTR_TRIGGER_BASED,
+	NL80211_PMSR_FTM_CAPA_ATTR_NON_TRIGGER_BASED,
+
+	/* keep last */
+	NUM_NL80211_PMSR_FTM_CAPA_ATTR,
+	NL80211_PMSR_FTM_CAPA_ATTR_MAX = NUM_NL80211_PMSR_FTM_CAPA_ATTR - 1
+};
+
+/**
+ * enum nl80211_peer_measurement_ftm_req - FTM request attributes
+ * @__NL80211_PMSR_FTM_REQ_ATTR_INVALID: invalid
+ *
+ * @NL80211_PMSR_FTM_REQ_ATTR_ASAP: ASAP mode requested (flag)
+ * @NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE: preamble type (see
+ *	&enum nl80211_preamble), optional for DMG (u32)
+ * @NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP: number of bursts exponent as in
+ *	802.11-2016 9.4.2.168 "Fine Timing Measurement Parameters element"
+ *	(u8, 0-15, optional with default 15 i.e. "no preference")
+ * @NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD: interval between bursts in units
+ *	of 100ms (u16, optional with default 0)
+ * @NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION: burst duration, as in 802.11-2016
+ *	Table 9-257 "Burst Duration field encoding" (u8, 0-15, optional with
+ *	default 15 i.e. "no preference")
+ * @NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST: number of successful FTM frames
+ *	requested per burst
+ *	(u8, 0-31, optional with default 0 i.e. "no preference")
+ * @NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES: number of FTMR frame retries
+ *	(u8, default 3)
+ * @NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI: request LCI data (flag)
+ * @NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC: request civic location data
+ *	(flag)
+ * @NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED: request trigger based ranging
+ *	measurement (flag).
+ *	This attribute and %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED are
+ *	mutually exclusive.
+ *      if neither %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED nor
+ *	%NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set, EDCA based
+ *	ranging will be used.
+ * @NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED: request non trigger based
+ *	ranging measurement (flag)
+ *	This attribute and %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED are
+ *	mutually exclusive.
+ *      if neither %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED nor
+ *	%NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set, EDCA based
+ *	ranging will be used.
+ * @NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK: negotiate for LMR feedback. Only
+ *	valid if either %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED or
+ *	%NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set.
+ * @NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR: optional. The BSS color of the
+ *	responder. Only valid if %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED
+ *	or %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED is set.
+ *
+ * @NUM_NL80211_PMSR_FTM_REQ_ATTR: internal
+ * @NL80211_PMSR_FTM_REQ_ATTR_MAX: highest attribute number
+ */
+enum nl80211_peer_measurement_ftm_req {
+	__NL80211_PMSR_FTM_REQ_ATTR_INVALID,
+
+	NL80211_PMSR_FTM_REQ_ATTR_ASAP,
+	NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE,
+	NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP,
+	NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD,
+	NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION,
+	NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST,
+	NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES,
+	NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI,
+	NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC,
+	NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED,
+	NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED,
+	NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK,
+	NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR,
+
+	/* keep last */
+	NUM_NL80211_PMSR_FTM_REQ_ATTR,
+	NL80211_PMSR_FTM_REQ_ATTR_MAX = NUM_NL80211_PMSR_FTM_REQ_ATTR - 1
+};
+
+/**
+ * enum nl80211_peer_measurement_ftm_failure_reasons - FTM failure reasons
+ * @NL80211_PMSR_FTM_FAILURE_UNSPECIFIED: unspecified failure, not used
+ * @NL80211_PMSR_FTM_FAILURE_NO_RESPONSE: no response from the FTM responder
+ * @NL80211_PMSR_FTM_FAILURE_REJECTED: FTM responder rejected measurement
+ * @NL80211_PMSR_FTM_FAILURE_WRONG_CHANNEL: we already know the peer is
+ *	on a different channel, so can't measure (if we didn't know, we'd
+ *	try and get no response)
+ * @NL80211_PMSR_FTM_FAILURE_PEER_NOT_CAPABLE: peer can't actually do FTM
+ * @NL80211_PMSR_FTM_FAILURE_INVALID_TIMESTAMP: invalid T1/T4 timestamps
+ *	received
+ * @NL80211_PMSR_FTM_FAILURE_PEER_BUSY: peer reports busy, you may retry
+ *	later (see %NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME)
+ * @NL80211_PMSR_FTM_FAILURE_BAD_CHANGED_PARAMS: parameters were changed
+ *	by the peer and are no longer supported
+ */
+enum nl80211_peer_measurement_ftm_failure_reasons {
+	NL80211_PMSR_FTM_FAILURE_UNSPECIFIED,
+	NL80211_PMSR_FTM_FAILURE_NO_RESPONSE,
+	NL80211_PMSR_FTM_FAILURE_REJECTED,
+	NL80211_PMSR_FTM_FAILURE_WRONG_CHANNEL,
+	NL80211_PMSR_FTM_FAILURE_PEER_NOT_CAPABLE,
+	NL80211_PMSR_FTM_FAILURE_INVALID_TIMESTAMP,
+	NL80211_PMSR_FTM_FAILURE_PEER_BUSY,
+	NL80211_PMSR_FTM_FAILURE_BAD_CHANGED_PARAMS,
+};
+
+/**
+ * enum nl80211_peer_measurement_ftm_resp - FTM response attributes
+ * @__NL80211_PMSR_FTM_RESP_ATTR_INVALID: invalid
+ *
+ * @NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON: FTM-specific failure reason
+ *	(u32, optional)
+ * @NL80211_PMSR_FTM_RESP_ATTR_BURST_INDEX: optional, if bursts are reported
+ *	as separate results then it will be the burst index 0...(N-1) and
+ *	the top level will indicate partial results (u32)
+ * @NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_ATTEMPTS: number of FTM Request frames
+ *	transmitted (u32, optional)
+ * @NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_SUCCESSES: number of FTM Request frames
+ *	that were acknowleged (u32, optional)
+ * @NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME: retry time received from the
+ *	busy peer (u32, seconds)
+ * @NL80211_PMSR_FTM_RESP_ATTR_NUM_BURSTS_EXP: actual number of bursts exponent
+ *	used by the responder (similar to request, u8)
+ * @NL80211_PMSR_FTM_RESP_ATTR_BURST_DURATION: actual burst duration used by
+ *	the responder (similar to request, u8)
+ * @NL80211_PMSR_FTM_RESP_ATTR_FTMS_PER_BURST: actual FTMs per burst used
+ *	by the responder (similar to request, u8)
+ * @NL80211_PMSR_FTM_RESP_ATTR_RSSI_AVG: average RSSI across all FTM action
+ *	frames (optional, s32, 1/2 dBm)
+ * @NL80211_PMSR_FTM_RESP_ATTR_RSSI_SPREAD: RSSI spread across all FTM action
+ *	frames (optional, s32, 1/2 dBm)
+ * @NL80211_PMSR_FTM_RESP_ATTR_TX_RATE: bitrate we used for the response to the
+ *	FTM action frame (optional, nested, using &enum nl80211_rate_info
+ *	attributes)
+ * @NL80211_PMSR_FTM_RESP_ATTR_RX_RATE: bitrate the responder used for the FTM
+ *	action frame (optional, nested, using &enum nl80211_rate_info attrs)
+ * @NL80211_PMSR_FTM_RESP_ATTR_RTT_AVG: average RTT (s64, picoseconds, optional
+ *	but one of RTT/DIST must be present)
+ * @NL80211_PMSR_FTM_RESP_ATTR_RTT_VARIANCE: RTT variance (u64, ps^2, note that
+ *	standard deviation is the square root of variance, optional)
+ * @NL80211_PMSR_FTM_RESP_ATTR_RTT_SPREAD: RTT spread (u64, picoseconds,
+ *	optional)
+ * @NL80211_PMSR_FTM_RESP_ATTR_DIST_AVG: average distance (s64, mm, optional
+ *	but one of RTT/DIST must be present)
+ * @NL80211_PMSR_FTM_RESP_ATTR_DIST_VARIANCE: distance variance (u64, mm^2, note
+ *	that standard deviation is the square root of variance, optional)
+ * @NL80211_PMSR_FTM_RESP_ATTR_DIST_SPREAD: distance spread (u64, mm, optional)
+ * @NL80211_PMSR_FTM_RESP_ATTR_LCI: LCI data from peer (binary, optional);
+ *	this is the contents of the Measurement Report Element (802.11-2016
+ *	9.4.2.22.1) starting with the Measurement Token, with Measurement
+ *	Type 8.
+ * @NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC: civic location data from peer
+ *	(binary, optional);
+ *	this is the contents of the Measurement Report Element (802.11-2016
+ *	9.4.2.22.1) starting with the Measurement Token, with Measurement
+ *	Type 11.
+ * @NL80211_PMSR_FTM_RESP_ATTR_PAD: ignore, for u64/s64 padding only
+ *
+ * @NUM_NL80211_PMSR_FTM_RESP_ATTR: internal
+ * @NL80211_PMSR_FTM_RESP_ATTR_MAX: highest attribute number
+ */
+enum nl80211_peer_measurement_ftm_resp {
+	__NL80211_PMSR_FTM_RESP_ATTR_INVALID,
+
+	NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON,
+	NL80211_PMSR_FTM_RESP_ATTR_BURST_INDEX,
+	NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_ATTEMPTS,
+	NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_SUCCESSES,
+	NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME,
+	NL80211_PMSR_FTM_RESP_ATTR_NUM_BURSTS_EXP,
+	NL80211_PMSR_FTM_RESP_ATTR_BURST_DURATION,
+	NL80211_PMSR_FTM_RESP_ATTR_FTMS_PER_BURST,
+	NL80211_PMSR_FTM_RESP_ATTR_RSSI_AVG,
+	NL80211_PMSR_FTM_RESP_ATTR_RSSI_SPREAD,
+	NL80211_PMSR_FTM_RESP_ATTR_TX_RATE,
+	NL80211_PMSR_FTM_RESP_ATTR_RX_RATE,
+	NL80211_PMSR_FTM_RESP_ATTR_RTT_AVG,
+	NL80211_PMSR_FTM_RESP_ATTR_RTT_VARIANCE,
+	NL80211_PMSR_FTM_RESP_ATTR_RTT_SPREAD,
+	NL80211_PMSR_FTM_RESP_ATTR_DIST_AVG,
+	NL80211_PMSR_FTM_RESP_ATTR_DIST_VARIANCE,
+	NL80211_PMSR_FTM_RESP_ATTR_DIST_SPREAD,
+	NL80211_PMSR_FTM_RESP_ATTR_LCI,
+	NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC,
+	NL80211_PMSR_FTM_RESP_ATTR_PAD,
+
+	/* keep last */
+	NUM_NL80211_PMSR_FTM_RESP_ATTR,
+	NL80211_PMSR_FTM_RESP_ATTR_MAX = NUM_NL80211_PMSR_FTM_RESP_ATTR - 1
+};
+
+/**
+ * enum nl80211_obss_pd_attributes - OBSS packet detection attributes
+ * @__NL80211_HE_OBSS_PD_ATTR_INVALID: Invalid
+ *
+ * @NL80211_HE_OBSS_PD_ATTR_MIN_OFFSET: the OBSS PD minimum tx power offset.
+ * @NL80211_HE_OBSS_PD_ATTR_MAX_OFFSET: the OBSS PD maximum tx power offset.
+ * @NL80211_HE_OBSS_PD_ATTR_NON_SRG_MAX_OFFSET: the non-SRG OBSS PD maximum
+ *	tx power offset.
+ * @NL80211_HE_OBSS_PD_ATTR_BSS_COLOR_BITMAP: bitmap that indicates the BSS color
+ *	values used by members of the SRG.
+ * @NL80211_HE_OBSS_PD_ATTR_PARTIAL_BSSID_BITMAP: bitmap that indicates the partial
+ *	BSSID values used by members of the SRG.
+ * @NL80211_HE_OBSS_PD_ATTR_SR_CTRL: The SR Control field of SRP element.
+ *
+ * @__NL80211_HE_OBSS_PD_ATTR_LAST: Internal
+ * @NL80211_HE_OBSS_PD_ATTR_MAX: highest OBSS PD attribute.
+ */
+enum nl80211_obss_pd_attributes {
+	__NL80211_HE_OBSS_PD_ATTR_INVALID,
+
+	NL80211_HE_OBSS_PD_ATTR_MIN_OFFSET,
+	NL80211_HE_OBSS_PD_ATTR_MAX_OFFSET,
+	NL80211_HE_OBSS_PD_ATTR_NON_SRG_MAX_OFFSET,
+	NL80211_HE_OBSS_PD_ATTR_BSS_COLOR_BITMAP,
+	NL80211_HE_OBSS_PD_ATTR_PARTIAL_BSSID_BITMAP,
+	NL80211_HE_OBSS_PD_ATTR_SR_CTRL,
+
+	/* keep last */
+	__NL80211_HE_OBSS_PD_ATTR_LAST,
+	NL80211_HE_OBSS_PD_ATTR_MAX = __NL80211_HE_OBSS_PD_ATTR_LAST - 1,
+};
+
+/**
+ * enum nl80211_bss_color_attributes - BSS Color attributes
+ * @__NL80211_HE_BSS_COLOR_ATTR_INVALID: Invalid
+ *
+ * @NL80211_HE_BSS_COLOR_ATTR_COLOR: the current BSS Color.
+ * @NL80211_HE_BSS_COLOR_ATTR_DISABLED: is BSS coloring disabled.
+ * @NL80211_HE_BSS_COLOR_ATTR_PARTIAL: the AID equation to be used..
+ *
+ * @__NL80211_HE_BSS_COLOR_ATTR_LAST: Internal
+ * @NL80211_HE_BSS_COLOR_ATTR_MAX: highest BSS Color attribute.
+ */
+enum nl80211_bss_color_attributes {
+	__NL80211_HE_BSS_COLOR_ATTR_INVALID,
+
+	NL80211_HE_BSS_COLOR_ATTR_COLOR,
+	NL80211_HE_BSS_COLOR_ATTR_DISABLED,
+	NL80211_HE_BSS_COLOR_ATTR_PARTIAL,
+
+	/* keep last */
+	__NL80211_HE_BSS_COLOR_ATTR_LAST,
+	NL80211_HE_BSS_COLOR_ATTR_MAX = __NL80211_HE_BSS_COLOR_ATTR_LAST - 1,
+};
+
+/**
+ * enum nl80211_iftype_akm_attributes - interface type AKM attributes
+ * @__NL80211_IFTYPE_AKM_ATTR_INVALID: Invalid
+ *
+ * @NL80211_IFTYPE_AKM_ATTR_IFTYPES: nested attribute containing a flag
+ *	attribute for each interface type that supports AKM suites specified in
+ *	%NL80211_IFTYPE_AKM_ATTR_SUITES
+ * @NL80211_IFTYPE_AKM_ATTR_SUITES: an array of u32. Used to indicate supported
+ *	AKM suites for the specified interface types.
+ *
+ * @__NL80211_IFTYPE_AKM_ATTR_LAST: Internal
+ * @NL80211_IFTYPE_AKM_ATTR_MAX: highest interface type AKM attribute.
+ */
+enum nl80211_iftype_akm_attributes {
+	__NL80211_IFTYPE_AKM_ATTR_INVALID,
+
+	NL80211_IFTYPE_AKM_ATTR_IFTYPES,
+	NL80211_IFTYPE_AKM_ATTR_SUITES,
+
+	/* keep last */
+	__NL80211_IFTYPE_AKM_ATTR_LAST,
+	NL80211_IFTYPE_AKM_ATTR_MAX = __NL80211_IFTYPE_AKM_ATTR_LAST - 1,
+};
+
+/**
+ * enum nl80211_fils_discovery_attributes - FILS discovery configuration
+ * from IEEE Std 802.11ai-2016, Annex C.3 MIB detail.
+ *
+ * @__NL80211_FILS_DISCOVERY_ATTR_INVALID: Invalid
+ *
+ * @NL80211_FILS_DISCOVERY_ATTR_INT_MIN: Minimum packet interval (u32, TU).
+ *	Allowed range: 0..10000 (TU = Time Unit)
+ * @NL80211_FILS_DISCOVERY_ATTR_INT_MAX: Maximum packet interval (u32, TU).
+ *	Allowed range: 0..10000 (TU = Time Unit)
+ * @NL80211_FILS_DISCOVERY_ATTR_TMPL: Template data for FILS discovery action
+ *	frame including the headers.
+ *
+ * @__NL80211_FILS_DISCOVERY_ATTR_LAST: Internal
+ * @NL80211_FILS_DISCOVERY_ATTR_MAX: highest attribute
+ */
+enum nl80211_fils_discovery_attributes {
+	__NL80211_FILS_DISCOVERY_ATTR_INVALID,
+
+	NL80211_FILS_DISCOVERY_ATTR_INT_MIN,
+	NL80211_FILS_DISCOVERY_ATTR_INT_MAX,
+	NL80211_FILS_DISCOVERY_ATTR_TMPL,
+
+	/* keep last */
+	__NL80211_FILS_DISCOVERY_ATTR_LAST,
+	NL80211_FILS_DISCOVERY_ATTR_MAX = __NL80211_FILS_DISCOVERY_ATTR_LAST - 1
+};
+
+/*
+ * FILS discovery template minimum length with action frame headers and
+ * mandatory fields.
+ */
+#define NL80211_FILS_DISCOVERY_TMPL_MIN_LEN 42
+
+/**
+ * enum nl80211_unsol_bcast_probe_resp_attributes - Unsolicited broadcast probe
+ *	response configuration. Applicable only in 6GHz.
+ *
+ * @__NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INVALID: Invalid
+ *
+ * @NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT: Maximum packet interval (u32, TU).
+ *	Allowed range: 0..20 (TU = Time Unit). IEEE P802.11ax/D6.0
+ *	26.17.2.3.2 (AP behavior for fast passive scanning).
+ * @NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL: Unsolicited broadcast probe response
+ *	frame template (binary).
+ *
+ * @__NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_LAST: Internal
+ * @NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_MAX: highest attribute
+ */
+enum nl80211_unsol_bcast_probe_resp_attributes {
+	__NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INVALID,
+
+	NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT,
+	NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL,
+
+	/* keep last */
+	__NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_LAST,
+	NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_MAX =
+		__NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_LAST - 1
+};
+
+/**
+ * enum nl80211_sae_pwe_mechanism - The mechanism(s) allowed for SAE PWE
+ *	derivation. Applicable only when WPA3-Personal SAE authentication is
+ *	used.
+ *
+ * @NL80211_SAE_PWE_UNSPECIFIED: not specified, used internally to indicate that
+ *	attribute is not present from userspace.
+ * @NL80211_SAE_PWE_HUNT_AND_PECK: hunting-and-pecking loop only
+ * @NL80211_SAE_PWE_HASH_TO_ELEMENT: hash-to-element only
+ * @NL80211_SAE_PWE_BOTH: both hunting-and-pecking loop and hash-to-element
+ *	can be used.
+ */
+enum nl80211_sae_pwe_mechanism {
+	NL80211_SAE_PWE_UNSPECIFIED,
+	NL80211_SAE_PWE_HUNT_AND_PECK,
+	NL80211_SAE_PWE_HASH_TO_ELEMENT,
+	NL80211_SAE_PWE_BOTH,
+};
+
+/**
+ * enum nl80211_sar_type - type of SAR specs
+ *
+ * @NL80211_SAR_TYPE_POWER: power limitation specified in 0.25dBm unit
+ *
+ */
+enum nl80211_sar_type {
+	NL80211_SAR_TYPE_POWER,
+
+	/* add new type here */
+
+	/* Keep last */
+	NUM_NL80211_SAR_TYPE,
+};
+
+/**
+ * enum nl80211_sar_attrs - Attributes for SAR spec
+ *
+ * @NL80211_SAR_ATTR_TYPE: the SAR type as defined in &enum nl80211_sar_type.
+ *
+ * @NL80211_SAR_ATTR_SPECS: Nested array of SAR power
+ *	limit specifications. Each specification contains a set
+ *	of %nl80211_sar_specs_attrs.
+ *
+ *	For SET operation, it contains array of %NL80211_SAR_ATTR_SPECS_POWER
+ *	and %NL80211_SAR_ATTR_SPECS_RANGE_INDEX.
+ *
+ *	For sar_capa dump, it contains array of
+ *	%NL80211_SAR_ATTR_SPECS_START_FREQ
+ *	and %NL80211_SAR_ATTR_SPECS_END_FREQ.
+ *
+ * @__NL80211_SAR_ATTR_LAST: Internal
+ * @NL80211_SAR_ATTR_MAX: highest sar attribute
+ *
+ * These attributes are used with %NL80211_CMD_SET_SAR_SPEC
+ */
+enum nl80211_sar_attrs {
+	__NL80211_SAR_ATTR_INVALID,
+
+	NL80211_SAR_ATTR_TYPE,
+	NL80211_SAR_ATTR_SPECS,
+
+	__NL80211_SAR_ATTR_LAST,
+	NL80211_SAR_ATTR_MAX = __NL80211_SAR_ATTR_LAST - 1,
+};
+
+/**
+ * enum nl80211_sar_specs_attrs - Attributes for SAR power limit specs
+ *
+ * @NL80211_SAR_ATTR_SPECS_POWER: Required (s32)value to specify the actual
+ *	power limit value in units of 0.25 dBm if type is
+ *	NL80211_SAR_TYPE_POWER. (i.e., a value of 44 represents 11 dBm).
+ *	0 means userspace doesn't have SAR limitation on this associated range.
+ *
+ * @NL80211_SAR_ATTR_SPECS_RANGE_INDEX: Required (u32) value to specify the
+ *	index of exported freq range table and the associated power limitation
+ *	is applied to this range.
+ *
+ *	Userspace isn't required to set all the ranges advertised by WLAN driver,
+ *	and userspace can skip some certain ranges. These skipped ranges don't
+ *	have SAR limitations, and they are same as setting the
+ *	%NL80211_SAR_ATTR_SPECS_POWER to any unreasonable high value because any
+ *	value higher than regulatory allowed value just means SAR power
+ *	limitation is removed, but it's required to set at least one range.
+ *	It's not allowed to set duplicated range in one SET operation.
+ *
+ *	Every SET operation overwrites previous SET operation.
+ *
+ * @NL80211_SAR_ATTR_SPECS_START_FREQ: Required (u32) value to specify the start
+ *	frequency of this range edge when registering SAR capability to wiphy.
+ *	It's not a channel center frequency. The unit is kHz.
+ *
+ * @NL80211_SAR_ATTR_SPECS_END_FREQ: Required (u32) value to specify the end
+ *	frequency of this range edge when registering SAR capability to wiphy.
+ *	It's not a channel center frequency. The unit is kHz.
+ *
+ * @__NL80211_SAR_ATTR_SPECS_LAST: Internal
+ * @NL80211_SAR_ATTR_SPECS_MAX: highest sar specs attribute
+ */
+enum nl80211_sar_specs_attrs {
+	__NL80211_SAR_ATTR_SPECS_INVALID,
+
+	NL80211_SAR_ATTR_SPECS_POWER,
+	NL80211_SAR_ATTR_SPECS_RANGE_INDEX,
+	NL80211_SAR_ATTR_SPECS_START_FREQ,
+	NL80211_SAR_ATTR_SPECS_END_FREQ,
+
+	__NL80211_SAR_ATTR_SPECS_LAST,
+	NL80211_SAR_ATTR_SPECS_MAX = __NL80211_SAR_ATTR_SPECS_LAST - 1,
+};
+
+/**
+ * enum nl80211_mbssid_config_attributes - multiple BSSID (MBSSID) and enhanced
+ * multi-BSSID advertisements (EMA) in AP mode.
+ * Kernel uses some of these attributes to advertise driver's support for
+ * MBSSID and EMA.
+ * Remaining attributes should be used by the userspace to configure the
+ * features.
+ *
+ * @__NL80211_MBSSID_CONFIG_ATTR_INVALID: Invalid
+ *
+ * @NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES: Used by the kernel to advertise
+ *	the maximum number of MBSSID interfaces supported by the driver.
+ *	Driver should indicate MBSSID support by setting
+ *	wiphy->mbssid_max_interfaces to a value more than or equal to 2.
+ *
+ * @NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY: Used by the kernel
+ *	to advertise the maximum profile periodicity supported by the driver
+ *	if EMA is enabled. Driver should indicate EMA support to the userspace
+ *	by setting wiphy->ema_max_profile_periodicity to
+ *	a non-zero value.
+ *
+ * @NL80211_MBSSID_CONFIG_ATTR_INDEX: Mandatory parameter to pass the index of
+ *	this BSS (u8) in the multiple BSSID set.
+ *	Value must be set to 0 for the transmitting interface and non-zero for
+ *	all non-transmitting interfaces. The userspace will be responsible
+ *	for using unique indices for the interfaces.
+ *	Range: 0 to wiphy->mbssid_max_interfaces-1.
+ *
+ * @NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX: Mandatory parameter for
+ *	a non-transmitted profile which provides the interface index (u32) of
+ *	the transmitted profile. The value must match one of the interface
+ *	indices advertised by the kernel. Optional if the interface being set up
+ *	is the transmitting one, however, if provided then the value must match
+ *	the interface index of the same.
+ *
+ * @NL80211_MBSSID_CONFIG_ATTR_EMA: Flag used to enable EMA AP feature.
+ *	Setting this flag is permitted only if the driver advertises EMA support
+ *	by setting wiphy->ema_max_profile_periodicity to non-zero.
+ *
+ * @__NL80211_MBSSID_CONFIG_ATTR_LAST: Internal
+ * @NL80211_MBSSID_CONFIG_ATTR_MAX: highest attribute
+ */
+enum nl80211_mbssid_config_attributes {
+	__NL80211_MBSSID_CONFIG_ATTR_INVALID,
+
+	NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES,
+	NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY,
+	NL80211_MBSSID_CONFIG_ATTR_INDEX,
+	NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX,
+	NL80211_MBSSID_CONFIG_ATTR_EMA,
+
+	/* keep last */
+	__NL80211_MBSSID_CONFIG_ATTR_LAST,
+	NL80211_MBSSID_CONFIG_ATTR_MAX = __NL80211_MBSSID_CONFIG_ATTR_LAST - 1,
+};
+
+/**
+ * enum nl80211_ap_settings_flags - AP settings flags
+ *
+ * @NL80211_AP_SETTINGS_EXTERNAL_AUTH_SUPPORT: AP supports external
+ *	authentication.
+ * @NL80211_AP_SETTINGS_SA_QUERY_OFFLOAD_SUPPORT: Userspace supports SA Query
+ *	procedures offload to driver. If driver advertises
+ *	%NL80211_AP_SME_SA_QUERY_OFFLOAD in AP SME features, userspace shall
+ *	ignore SA Query procedures and validations when this flag is set by
+ *	userspace.
+ */
+enum nl80211_ap_settings_flags {
+	NL80211_AP_SETTINGS_EXTERNAL_AUTH_SUPPORT	= 1 << 0,
+	NL80211_AP_SETTINGS_SA_QUERY_OFFLOAD_SUPPORT	= 1 << 1,
+};
+
+#endif /* __LINUX_NL80211_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/priv_netlink.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/priv_netlink.h
new file mode 100644
index 0000000..d3f091c
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/priv_netlink.h
@@ -0,0 +1,109 @@
+/*
+ * wpa_supplicant - Private copy of Linux netlink/rtnetlink definitions.
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef PRIV_NETLINK_H
+#define PRIV_NETLINK_H
+
+/*
+ * This should be replaced with user space header once one is available with C
+ * library, etc..
+ */
+
+#ifndef IFF_LOWER_UP
+#define IFF_LOWER_UP   0x10000         /* driver signals L1 up         */
+#endif
+#ifndef IFF_DORMANT
+#define IFF_DORMANT    0x20000         /* driver signals dormant       */
+#endif
+
+#ifndef IFLA_IFNAME
+#define IFLA_IFNAME 3
+#endif
+#ifndef IFLA_WIRELESS
+#define IFLA_WIRELESS 11
+#endif
+#ifndef IFLA_OPERSTATE
+#define IFLA_OPERSTATE 16
+#endif
+#ifndef IFLA_LINKMODE
+#define IFLA_LINKMODE 17
+#define IF_OPER_DORMANT 5
+#define IF_OPER_UP 6
+#endif
+
+#define NLM_F_REQUEST 1
+
+#define NETLINK_ROUTE 0
+#define RTMGRP_LINK 1
+#define RTM_BASE 0x10
+#define RTM_NEWLINK (RTM_BASE + 0)
+#define RTM_DELLINK (RTM_BASE + 1)
+#define RTM_SETLINK (RTM_BASE + 3)
+
+#define NLMSG_ALIGNTO 4
+#define NLMSG_ALIGN(len) (((len) + NLMSG_ALIGNTO - 1) & ~(NLMSG_ALIGNTO - 1))
+#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
+#define NLMSG_LENGTH(len) ((len) + NLMSG_ALIGN(sizeof(struct nlmsghdr)))
+#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
+#define NLMSG_DATA(nlh) ((void*) (((char*) nlh) + NLMSG_LENGTH(0)))
+#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
+			     (struct nlmsghdr *) \
+			     (((char *)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
+#define NLMSG_OK(nlh,len) ((len) >= (int) sizeof(struct nlmsghdr) && \
+			   (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \
+			   (int) (nlh)->nlmsg_len <= (len))
+#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))
+
+#define RTA_ALIGNTO 4
+#define RTA_ALIGN(len) (((len) + RTA_ALIGNTO - 1) & ~(RTA_ALIGNTO - 1))
+#define RTA_OK(rta,len) \
+((len) > 0 && (rta)->rta_len >= sizeof(struct rtattr) && \
+(rta)->rta_len <= (len))
+#define RTA_NEXT(rta,attrlen) \
+((attrlen) -= RTA_ALIGN((rta)->rta_len), \
+(struct rtattr *) (((char *)(rta)) + RTA_ALIGN((rta)->rta_len)))
+#define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len))
+#define RTA_SPACE(len) RTA_ALIGN(RTA_LENGTH(len))
+#define RTA_DATA(rta) ((void *) (((char *) (rta)) + RTA_LENGTH(0)))
+#define RTA_PAYLOAD(rta) ((int) ((rta)->rta_len) - RTA_LENGTH(0))
+
+
+struct sockaddr_nl
+{
+	sa_family_t nl_family;
+	unsigned short nl_pad;
+	u32 nl_pid;
+	u32 nl_groups;
+};
+
+struct nlmsghdr
+{
+	u32 nlmsg_len;
+	u16 nlmsg_type;
+	u16 nlmsg_flags;
+	u32 nlmsg_seq;
+	u32 nlmsg_pid;
+};
+
+struct ifinfomsg
+{
+	unsigned char ifi_family;
+	unsigned char __ifi_pad;
+	unsigned short ifi_type;
+	int ifi_index;
+	unsigned ifi_flags;
+	unsigned ifi_change;
+};
+
+struct rtattr
+{
+	unsigned short rta_len;
+	unsigned short rta_type;
+};
+
+#endif /* PRIV_NETLINK_H */
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/rfkill.c b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/rfkill.c
new file mode 100644
index 0000000..4d4d1b4
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/rfkill.c
@@ -0,0 +1,224 @@
+/*
+ * Linux rfkill helper functions for driver wrappers
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <fcntl.h>
+#include <limits.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "rfkill.h"
+
+#define RFKILL_EVENT_SIZE_V1 8
+
+struct rfkill_event {
+	u32 idx;
+	u8 type;
+	u8 op;
+	u8 soft;
+	u8 hard;
+} STRUCT_PACKED;
+
+enum rfkill_operation {
+	RFKILL_OP_ADD = 0,
+	RFKILL_OP_DEL,
+	RFKILL_OP_CHANGE,
+	RFKILL_OP_CHANGE_ALL,
+};
+
+enum rfkill_type {
+	RFKILL_TYPE_ALL = 0,
+	RFKILL_TYPE_WLAN,
+	RFKILL_TYPE_BLUETOOTH,
+	RFKILL_TYPE_UWB,
+	RFKILL_TYPE_WIMAX,
+	RFKILL_TYPE_WWAN,
+	RFKILL_TYPE_GPS,
+	RFKILL_TYPE_FM,
+	NUM_RFKILL_TYPES,
+};
+
+
+struct rfkill_data {
+	struct rfkill_config *cfg;
+	int fd;
+	int blocked;
+	uint32_t idx;
+};
+
+
+static void rfkill_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct rfkill_data *rfkill = eloop_ctx;
+	struct rfkill_event event;
+	ssize_t len;
+	int new_blocked;
+
+	len = read(rfkill->fd, &event, sizeof(event));
+	if (len < 0) {
+		wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s",
+			   strerror(errno));
+		return;
+	}
+	if (len != RFKILL_EVENT_SIZE_V1) {
+		wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size "
+			   "%d (expected %d)",
+			   (int) len, RFKILL_EVENT_SIZE_V1);
+		return;
+	}
+	if (event.op != RFKILL_OP_CHANGE || event.idx != rfkill->idx)
+		return;
+
+	wpa_printf(MSG_DEBUG, "rfkill: event: idx=%u type=%d "
+		   "op=%u soft=%u hard=%u",
+		   event.idx, event.type, event.op, event.soft,
+		   event.hard);
+
+	if (event.hard) {
+		wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked");
+		new_blocked = 1;
+	} else if (event.soft) {
+		wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked");
+		new_blocked = 1;
+	} else {
+		wpa_printf(MSG_INFO, "rfkill: WLAN unblocked");
+		new_blocked = 0;
+	}
+
+	if (new_blocked != rfkill->blocked) {
+		rfkill->blocked = new_blocked;
+		if (new_blocked)
+			rfkill->cfg->blocked_cb(rfkill->cfg->ctx);
+		else
+			rfkill->cfg->unblocked_cb(rfkill->cfg->ctx);
+	}
+}
+
+
+struct rfkill_data * rfkill_init(struct rfkill_config *cfg)
+{
+	struct rfkill_data *rfkill;
+	struct rfkill_event event;
+	ssize_t len;
+	char *phy = NULL, *rfk_phy;
+	char buf[24 + IFNAMSIZ + 1];
+	char buf2[31 + 11 + 1];
+	int found = 0;
+
+	rfkill = os_zalloc(sizeof(*rfkill));
+	if (rfkill == NULL)
+		return NULL;
+
+	os_snprintf(buf, sizeof(buf), "/sys/class/net/%s/phy80211",
+		    cfg->ifname);
+	phy = realpath(buf, NULL);
+	if (!phy) {
+		wpa_printf(MSG_INFO, "rfkill: Cannot get wiphy information");
+		goto fail;
+	}
+
+	rfkill->cfg = cfg;
+	rfkill->fd = open("/dev/rfkill", O_RDONLY);
+	if (rfkill->fd < 0) {
+		wpa_printf(MSG_INFO, "rfkill: Cannot open RFKILL control "
+			   "device");
+		goto fail;
+	}
+
+	if (fcntl(rfkill->fd, F_SETFL, O_NONBLOCK) < 0) {
+		wpa_printf(MSG_ERROR, "rfkill: Cannot set non-blocking mode: "
+			   "%s", strerror(errno));
+		goto fail2;
+	}
+
+	for (;;) {
+		len = read(rfkill->fd, &event, sizeof(event));
+		if (len < 0) {
+			if (errno == EAGAIN)
+				break; /* No more entries */
+			wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s",
+				   strerror(errno));
+			break;
+		}
+		if (len != RFKILL_EVENT_SIZE_V1) {
+			wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size "
+				   "%d (expected %d)",
+				   (int) len, RFKILL_EVENT_SIZE_V1);
+			continue;
+		}
+		if (event.op != RFKILL_OP_ADD ||
+		    event.type != RFKILL_TYPE_WLAN)
+			continue;
+
+		os_snprintf(buf2, sizeof(buf2),
+			    "/sys/class/rfkill/rfkill%d/device", event.idx);
+		rfk_phy = realpath(buf2, NULL);
+		if (!rfk_phy)
+			goto fail2;
+		found = os_strcmp(phy, rfk_phy) == 0;
+		free(rfk_phy);
+
+		if (!found)
+			continue;
+
+		wpa_printf(MSG_DEBUG, "rfkill: initial event: idx=%u type=%d "
+			   "op=%u soft=%u hard=%u",
+			   event.idx, event.type, event.op, event.soft,
+			   event.hard);
+
+		rfkill->idx = event.idx;
+		if (event.hard) {
+			wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked");
+			rfkill->blocked = 1;
+		} else if (event.soft) {
+			wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked");
+			rfkill->blocked = 1;
+		}
+		break;
+	}
+
+	if (!found)
+		goto fail2;
+
+	free(phy);
+	eloop_register_read_sock(rfkill->fd, rfkill_receive, rfkill, NULL);
+
+	return rfkill;
+
+fail2:
+	close(rfkill->fd);
+fail:
+	os_free(rfkill);
+	/* use standard free function to match realpath() */
+	free(phy);
+	return NULL;
+}
+
+
+void rfkill_deinit(struct rfkill_data *rfkill)
+{
+	if (rfkill == NULL)
+		return;
+
+	if (rfkill->fd >= 0) {
+		eloop_unregister_read_sock(rfkill->fd);
+		close(rfkill->fd);
+	}
+
+	os_free(rfkill->cfg);
+	os_free(rfkill);
+}
+
+
+int rfkill_is_blocked(struct rfkill_data *rfkill)
+{
+	if (rfkill == NULL)
+		return 0;
+
+	return rfkill->blocked;
+}
diff --git a/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/rfkill.h b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/rfkill.h
new file mode 100644
index 0000000..e275653
--- /dev/null
+++ b/package/kernel/asr-wl/asr-hostapd/asr-hostapd-2023-06-22/src/drivers/rfkill.h
@@ -0,0 +1,41 @@
+/*
+ * Linux rfkill helper functions for driver wrappers
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef RFKILL_H
+#define RFKILL_H
+
+struct rfkill_data;
+
+struct rfkill_config {
+	void *ctx;
+	char ifname[IFNAMSIZ];
+	void (*blocked_cb)(void *ctx);
+	void (*unblocked_cb)(void *ctx);
+};
+
+#ifdef CONFIG_RFKILL
+struct rfkill_data * rfkill_init(struct rfkill_config *cfg);
+void rfkill_deinit(struct rfkill_data *rfkill);
+int rfkill_is_blocked(struct rfkill_data *rfkill);
+#else
+static inline struct rfkill_data * rfkill_init(struct rfkill_config *cfg)
+{
+	return (void *) 1;
+}
+
+static inline void rfkill_deinit(struct rfkill_data *rfkill)
+{
+}
+
+static inline int rfkill_is_blocked(struct rfkill_data *rfkill)
+{
+	return 0;
+}
+#endif
+
+#endif /* RFKILL_H */