From 6b4cac814e32f3b307720d6b880939fb1f21f3ac Mon Sep 17 00:00:00 2001 From: "Bjoern A. Zeeb" Date: Sun, 26 Dec 2021 18:52:51 +0000 Subject: [PATCH] LinuxKPI: add 802.11 compat code Add 802.11 compat code for mac80211 and to a minimal degree cfg80211. This allows us to compile and use basic functionality of wireless drivers such as iwlwifi. This is a constant work in progress but having it in the tree will allow others to test and more easy to track changes and avoid having snapshots no longer applying to branches. Sponsored by: The FreeBSD Foundation MFC after: 3 days --- .../linuxkpi/common/include/linux/ieee80211.h | 511 +++ .../linuxkpi/common/include/linux/nl80211.h | 308 ++ .../linuxkpi/common/include/net/cfg80211.h | 1489 +++++++ .../common/include/net/ieee80211_radiotap.h | 2 - .../linuxkpi/common/include/net/mac80211.h | 2028 ++++++++++ sys/compat/linuxkpi/common/src/linux_80211.c | 3576 +++++++++++++++++ sys/compat/linuxkpi/common/src/linux_80211.h | 235 ++ .../linuxkpi/common/src/linux_80211_macops.c | 615 +++ sys/conf/files | 4 + sys/modules/Makefile | 1 + sys/modules/linuxkpi_wlan/Makefile | 15 + 11 files changed, 8782 insertions(+), 2 deletions(-) create mode 100644 sys/compat/linuxkpi/common/include/linux/ieee80211.h create mode 100644 sys/compat/linuxkpi/common/include/linux/nl80211.h create mode 100644 sys/compat/linuxkpi/common/include/net/cfg80211.h create mode 100644 sys/compat/linuxkpi/common/include/net/mac80211.h create mode 100644 sys/compat/linuxkpi/common/src/linux_80211.c create mode 100644 sys/compat/linuxkpi/common/src/linux_80211.h create mode 100644 sys/compat/linuxkpi/common/src/linux_80211_macops.c create mode 100644 sys/modules/linuxkpi_wlan/Makefile diff --git a/sys/compat/linuxkpi/common/include/linux/ieee80211.h b/sys/compat/linuxkpi/common/include/linux/ieee80211.h new file mode 100644 index 00000000000..714a682013e --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/ieee80211.h @@ -0,0 +1,511 @@ +/*- + * Copyright (c) 2020-2021 The FreeBSD Foundation + * + * This software was developed by Björn Zeeb under sponsorship from + * the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _LINUXKPI_LINUX_IEEE80211_H +#define _LINUXKPI_LINUX_IEEE80211_H + +#include +#include + +#include +#include +#include + +/* linux_80211.c */ +extern int debug_80211; + + +#define IEEE80211_CCMP_HDR_LEN 8 /* 802.11i .. net80211 comment */ +#define IEEE80211_CCMP_PN_LEN 6 +#define IEEE80211_CCMP_MIC_LEN 8 /* || 16 */ +#define IEEE80211_GCMP_HDR_LEN 8 +#define IEEE80211_GCMP_PN_LEN 6 +#define IEEE80211_GMAC_PN_LEN 6 + +#define IEEE80211_MAX_PN_LEN 16 + +#define IEEE80211_INVAL_HW_QUEUE ((uint8_t)-1) + +#define IEEE80211_MAX_AMPDU_BUF_HT 0x40 +#define IEEE80211_MAX_AMPDU_BUF 256 /* for HE? */ + +#define IEEE80211_MAX_DATA_LEN (2300 + IEEE80211_CRC_LEN) + +#define IEEE80211_MAX_MPDU_LEN_HT_BA 4095 /* 9.3.2.1 Format of Data frames; non-VHT non-DMG STA */ +#define IEEE80211_MAX_MPDU_LEN_HT_3839 3839 +#define IEEE80211_MAX_MPDU_LEN_VHT_3895 3895 +#define IEEE80211_MAX_MPDU_LEN_VHT_7991 7991 +#define IEEE80211_MAX_MPDU_LEN_VHT_11454 11454 + +#define IEEE80211_MAX_RTS_THRESHOLD 2346 /* net80211::IEEE80211_RTS_MAX */ + +#define IEEE80211_MIN_ACTION_SIZE 23 /* ? */ + +/* Wi-Fi Peer-to-Peer (P2P) Technical Specification */ +#define IEEE80211_P2P_OPPPS_CTWINDOW_MASK 0x7f +#define IEEE80211_P2P_OPPPS_ENABLE_BIT BIT(7) + +#define IEEE80211_QOS_CTL_A_MSDU_PRESENT 0x0080 /* 9.2.4.5.1, Table 9-6 QoS Control Field */ + +#define IEEE80211_RATE_SHORT_PREAMBLE BIT(0) + +enum ieee80211_rate_control_changed_flags { + IEEE80211_RC_BW_CHANGED = BIT(0), + IEEE80211_RC_NSS_CHANGED = BIT(1), + IEEE80211_RC_SUPP_RATES_CHANGED = BIT(2), +}; + +#define IEEE80211_SCTL_FRAG IEEE80211_SEQ_FRAG_MASK +#define IEEE80211_SCTL_SEQ IEEE80211_SEQ_SEQ_MASK + +#define IEEE80211_TKIP_ICV_LEN 4 +#define IEEE80211_TKIP_IV_LEN 8 /* WEP + KID + EXT */ + +#define IEEE80211_VHT_EXT_NSS_BW_CAPABLE (1 << 13) /* assigned to tx_highest */ + +#define IEEE80211_VHT_MAX_AMPDU_1024K 7 /* 9.4.2.56.3 A-MPDU Parameters field, Table 9-163 */ + +#define IEEE80211_WEP_IV_LEN 3 /* net80211: IEEE80211_WEP_IVLEN */ +#define IEEE80211_WEP_ICV_LEN 4 + +#define WLAN_AUTH_OPEN __LINE__ /* TODO FIXME brcmfmac */ +#define WLAN_CAPABILITY_IBSS __LINE__ /* TODO FIXME no longer used? */ +#define WLAN_CAPABILITY_SHORT_PREAMBLE __LINE__ /* TODO FIXME brcmfmac */ +#define WLAN_CAPABILITY_SHORT_SLOT_TIME __LINE__ /* TODO FIXME brcmfmac */ + + +#define WLAN_MAX_KEY_LEN 32 /* TODO FIXME brcmfmac */ +#define WLAN_PMKID_LEN 16 /* TODO FIXME brcmfmac */ + +#define WLAN_KEY_LEN_CCMP 16 +#define WLAN_KEY_LEN_GCMP_256 32 + +/* 9.4.2.56.3, Table 9-163 Subfields of the A-MPDU Parameters field */ +enum ieee80211_min_mpdu_start_spacing { + IEEE80211_HT_MPDU_DENSITY_4 = 5, /* 4us */ + IEEE80211_HT_MPDU_DENSITY_16 = 7, /* 16us */ +}; + +/* 9.4.2.57, Table 9-168, HT Operation element fields and subfields */ +#define IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT 0x0080 /* B24.. */ + +#define IEEE80211_FCTL_STYPE IEEE80211_FC0_SUBTYPE_MASK + +#define IEEE80211_STYPE_ASSOC_REQ IEEE80211_FC0_SUBTYPE_ASSOC_REQ +#define IEEE80211_STYPE_REASSOC_REQ IEEE80211_FC0_SUBTYPE_REASSOC_REQ +#define IEEE80211_STYPE_PROBE_REQ IEEE80211_FC0_SUBTYPE_PROBE_REQ +#define IEEE80211_STYPE_DISASSOC IEEE80211_FC0_SUBTYPE_DISASSOC +#define IEEE80211_STYPE_AUTH IEEE80211_FC0_SUBTYPE_AUTH +#define IEEE80211_STYPE_DEAUTH IEEE80211_FC0_SUBTYPE_DEAUTH +#define IEEE80211_STYPE_ACTION IEEE80211_FC0_SUBTYPE_ACTION + +#define IEEE80211_NUM_ACS 4 /* net8021::WME_NUM_AC */ + +#define IEEE80211_MAX_SSID_LEN 32 /* 9.4.2.2 SSID element, net80211: IEEE80211_NWID_LEN */ + + +/* Figure 9-27, BAR Control field */ +#define IEEE80211_BAR_CTRL_TID_INFO_MASK 0xf000 +#define IEEE80211_BAR_CTRL_TID_INFO_SHIFT 12 + +#define IEEE80211_PPE_THRES_INFO_PPET_SIZE 1 /* TODO FIXME ax? */ +#define IEEE80211_PPE_THRES_NSS_MASK 2 /* TODO FIXME ax? */ +#define IEEE80211_PPE_THRES_RU_INDEX_BITMASK_POS 3 /* TODO FIXME ax? */ +#define IEEE80211_PPE_THRES_RU_INDEX_BITMASK_MASK 8 /* TODO FIXME ax? */ + +#define IEEE80211_HT_OP_MODE_PROTECTION 0x03 /* MASK */ +#define IEEE80211_HT_OP_MODE_PROTECTION_NONE 0x00 +#define IEEE80211_HT_OP_MODE_PROTECTION_20MHZ 0x01 +#define IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED 0x02 +#define IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER 0x03 + + +/* 9.6.13.1, Table 9-342 TDLS Action field values. */ +enum ieee80211_tdls_action_code { + WLAN_TDLS_SETUP_REQUEST = 0, + WLAN_TDLS_SETUP_RESPONSE = 1, + WLAN_TDLS_SETUP_CONFIRM = 2, + WLAN_TDLS_TEARDOWN = 3, + WLAN_TDLS_PEER_TRAFFIC_INDICATION = 4, + WLAN_TDLS_CHANNEL_SWITCH_REQUEST = 5, + WLAN_TDLS_CHANNEL_SWITCH_RESPONSE = 6, + WLAN_TDLS_PEER_PSM_REQUEST = 7, + WLAN_TDLS_PEER_PSM_RESPONSE = 8, + WLAN_TDLS_PEER_TRAFFIC_RESPONSE = 9, + WLAN_TDLS_DISCOVERY_REQUEST = 10, + /* 11-255 reserved */ +}; + +/* 9.4.2.27, Table 9-135. Extended Capabilities field. */ +/* This is split up into octets CAPA1 = octet 1, ... */ +#define WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING BIT(2 % 8) +#define WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT BIT(22 % 8) +#define WLAN_EXT_CAPA8_OPMODE_NOTIF BIT(62 % 8) +#define WLAN_EXT_CAPA10_TWT_REQUESTER_SUPPORT BIT(5) /* XXX */ +#define WLAN_EXT_CAPA10_OBSS_NARROW_BW_RU_TOLERANCE_SUPPORT BIT(7) /* XXX */ + + +/* iwlwifi/mvm/utils:: for (ac = IEEE80211_AC_VO; ac <= IEEE80211_AC_VI; ac++) */ +/* Would be so much easier if we'd define constants to the same. */ +enum ieee80211_ac_numbers { + IEEE80211_AC_VO = 0, /* net80211::WME_AC_VO */ + IEEE80211_AC_VI = 1, /* net80211::WME_AC_VI */ + IEEE80211_AC_BE = 2, /* net80211::WME_AC_BE */ + IEEE80211_AC_BK = 3, /* net80211::WME_AC_BK */ +}; + +#define IEEE80211_MAX_QUEUES 16 /* Assume IEEE80211_NUM_TIDS for the moment. */ + +#define IEEE80211_WMM_IE_STA_QOSINFO_AC_VO 1 +#define IEEE80211_WMM_IE_STA_QOSINFO_AC_VI 2 +#define IEEE80211_WMM_IE_STA_QOSINFO_AC_BK 4 +#define IEEE80211_WMM_IE_STA_QOSINFO_AC_BE 8 +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL 0xf + +struct vht_mcs { + uint16_t rx_mcs_map; + uint16_t rx_highest; + uint16_t tx_mcs_map; + uint16_t tx_highest; +}; + +struct ieee80211_vht_cap { + struct vht_mcs supp_mcs;; + __le32 vht_cap_info; +}; + +enum ieee80211_ht_max_ampdu_len { + IEEE80211_HT_MAX_AMPDU_64K +}; + +enum ieee80211_ampdu_mlme_action { + IEEE80211_AMPDU_RX_START, + IEEE80211_AMPDU_RX_STOP, + IEEE80211_AMPDU_TX_OPERATIONAL, + IEEE80211_AMPDU_TX_START, + IEEE80211_AMPDU_TX_START_DELAY_ADDBA, + IEEE80211_AMPDU_TX_START_IMMEDIATE, + IEEE80211_AMPDU_TX_STOP_CONT, + IEEE80211_AMPDU_TX_STOP_FLUSH, + IEEE80211_AMPDU_TX_STOP_FLUSH_CONT +}; + +enum ieee80211_chanctx_switch_mode { + CHANCTX_SWMODE_REASSIGN_VIF, + CHANCTX_SWMODE_SWAP_CONTEXTS, +}; + +enum ieee80211_chanctx_change_flags { + IEEE80211_CHANCTX_CHANGE_MIN_WIDTH = BIT(0), + IEEE80211_CHANCTX_CHANGE_RADAR = BIT(1), + IEEE80211_CHANCTX_CHANGE_RX_CHAINS = BIT(2), + IEEE80211_CHANCTX_CHANGE_WIDTH = BIT(3), +}; + +enum ieee80211_frame_release_type { + IEEE80211_FRAME_RELEASE_PSPOLL = 1, + IEEE80211_FRAME_RELEASE_UAPSD = 2, +}; + +enum ieee80211_p2p_attr_ids { + IEEE80211_P2P_ATTR_DEVICE_ID, + IEEE80211_P2P_ATTR_DEVICE_INFO, + IEEE80211_P2P_ATTR_GROUP_ID, + IEEE80211_P2P_ATTR_LISTEN_CHANNEL, +}; + +enum ieee80211_reconfig_type { + IEEE80211_RECONFIG_TYPE_RESTART, + IEEE80211_RECONFIG_TYPE_SUSPEND, +}; + +enum ieee80211_roc_type { + IEEE80211_ROC_TYPE_MGMT_TX, + IEEE80211_ROC_TYPE_NORMAL, +}; + +enum ieee80211_smps_mode { + IEEE80211_SMPS_OFF, + IEEE80211_SMPS_STATIC, + IEEE80211_SMPS_DYNAMIC, + IEEE80211_SMPS_AUTOMATIC, + IEEE80211_SMPS_NUM_MODES, +}; + +/* net80211::IEEE80211_S_* different but represents the state machine. */ +/* Note: order here is important! */ +enum ieee80211_sta_state { + IEEE80211_STA_NOTEXIST, + IEEE80211_STA_NONE, + IEEE80211_STA_AUTH, + IEEE80211_STA_ASSOC, + IEEE80211_STA_AUTHORIZED, /* 802.1x */ +}; + +enum ieee80211_sta_rx_bw { + IEEE80211_STA_RX_BW_20, + IEEE80211_STA_RX_BW_40, + IEEE80211_STA_RX_BW_80, + IEEE80211_STA_RX_BW_160, +}; + +enum ieee80211_tx_info_flags { + /* XXX TODO .. right shift numbers - not sure where that came from? */ + IEEE80211_TX_CTL_AMPDU = BIT(0), + IEEE80211_TX_CTL_ASSIGN_SEQ = BIT(1), + IEEE80211_TX_CTL_NO_ACK = BIT(2), + IEEE80211_TX_CTL_SEND_AFTER_DTIM = BIT(3), + IEEE80211_TX_CTL_TX_OFFCHAN = BIT(4), + IEEE80211_TX_CTL_REQ_TX_STATUS = BIT(5), + IEEE80211_TX_STATUS_EOSP = BIT(6), + IEEE80211_TX_STAT_ACK = BIT(7), + IEEE80211_TX_STAT_AMPDU = BIT(8), + IEEE80211_TX_STAT_AMPDU_NO_BACK = BIT(9), + IEEE80211_TX_STAT_TX_FILTERED = BIT(10), + IEEE80211_TX_STAT_NOACK_TRANSMITTED = BIT(11), +}; + +enum ieee80211_tx_control_flags { + /* XXX TODO .. right shift numbers */ + IEEE80211_TX_CTRL_PORT_CTRL_PROTO = BIT(0), +}; + +enum ieee80211_tx_rate_flags { + /* XXX TODO .. right shift numbers */ + IEEE80211_TX_RC_40_MHZ_WIDTH = BIT(0), + IEEE80211_TX_RC_80_MHZ_WIDTH = BIT(1), + IEEE80211_TX_RC_160_MHZ_WIDTH = BIT(2), + IEEE80211_TX_RC_GREEN_FIELD = BIT(3), + IEEE80211_TX_RC_MCS = BIT(4), + IEEE80211_TX_RC_SHORT_GI = BIT(5), + IEEE80211_TX_RC_VHT_MCS = BIT(6), +}; + +struct ieee80211_hdr { /* net80211::ieee80211_frame */ + __le16 frame_control; + __le16 duration_id; + uint8_t addr1[ETH_ALEN]; + uint8_t addr2[ETH_ALEN]; + uint8_t addr3[ETH_ALEN]; + __le16 seq_ctrl; + uint8_t addr4[ETH_ALEN]; +}; + +struct ieee80211_vendor_ie { +}; + +/* 9.3.3.2 Format of Management frames */ +struct ieee80211_mgmt { + __le16 frame_control; + __le16 duration_id; + uint8_t da[ETH_ALEN]; + uint8_t sa[ETH_ALEN]; + uint8_t bssid[ETH_ALEN]; + __le16 seq_ctrl; + union { + /* 9.3.3.3 Beacon frame format */ + struct { + uint64_t timestamp; + uint16_t beacon_int; + uint16_t capab_info; + uint8_t variable[0]; + } beacon; + /* 9.3.3.10 Probe Request frame format */ + struct { + uint8_t variable[0]; + } probe_req; + /* 9.3.3.11 Probe Response frame format */ + struct { + uint64_t timestamp; + uint16_t beacon_int; + uint16_t capab_info; + uint8_t variable[0]; + } probe_resp; + /* 9.3.3.14 Action frame format */ + struct { + /* 9.4.1.11 Action field */ + uint8_t category; + /* 9.6.8 Public Action details */ + union { + /* 9.6.8.33 Fine Timing Measurement frame format */ + struct { + uint8_t dialog_token; + uint8_t follow_up; + uint8_t tod[6]; + uint8_t toa[6]; + uint16_t tod_error; + uint16_t toa_error; + uint8_t variable[0]; + } ftm; + } u; + } action; + } u; +}; + +#define MHZ_TO_KHZ(_f) ((_f) * 1000) +#define DBI_TO_MBI(_g) ((_g) * 100) +#define MBI_TO_DBI(_x) ((_x) / 100) +#define DBM_TO_MBM(_g) ((_g) * 100) +#define MBM_TO_DBM(_x) ((_x) / 100) + +#define IEEE80211_SEQ_TO_SN(_seqn) (((_seqn) & IEEE80211_SEQ_SEQ_MASK) >> \ + IEEE80211_SEQ_SEQ_SHIFT) + +/* Time unit (TU) to .. See net80211: IEEE80211_DUR_TU */ +#define TU_TO_JIFFIES(_tu) (usecs_to_jiffies(_tu) * 1024) +#define TU_TO_EXP_TIME(_tu) (jiffies + TU_TO_JIFFIES(_tu)) + +/* 9.4.2.21.1, Table 9-82. */ +#define IEEE80211_SPCT_MSR_RPRT_TYPE_LCI 8 +#define IEEE80211_SPCT_MSR_RPRT_TYPE_CIVIC 11 + +/* 9.4.2.1, Table 9-77. Element IDs. */ +enum ieee80211_eid { + WLAN_EID_SSID = 0, + WLAN_EID_SUPP_RATES = 1, + WLAN_EID_DS_PARAMS = 3, + WLAN_EID_TIM = 5, + WLAN_EID_COUNTRY = 7, /* IEEE80211_ELEMID_COUNTRY */ + WLAN_EID_REQUEST = 10, + WLAN_EID_CHANNEL_SWITCH = 37, + WLAN_EID_MEASURE_REPORT = 39, + WLAN_EID_RSN = 48, /* IEEE80211_ELEMID_RSN */ + WLAN_EID_EXT_SUPP_RATES = 50, + WLAN_EID_EXT_CHANSWITCH_ANN = 60, + WLAN_EID_EXT_CAPABILITY = 127, + WLAN_EID_VENDOR_SPECIFIC = 221, +}; + +/* 9.4.1.7, Table 9-45. Reason codes. */ +enum ieee80211_reason_code { + /* reserved = 0, */ + WLAN_REASON_UNSPECIFIED = 1, + WLAN_REASON_DEAUTH_LEAVING = 3, /* LEAVING_NETWORK_DEAUTH */ + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED = 26, +}; + +/* 9.4.1.9, Table 9-46. Status codes. */ +enum ieee80211_status_code { + WLAN_STATUS_SUCCESS = 0, + WLAN_STATUS_AUTH_TIMEOUT = 16, /* REJECTED_SEQUENCE_TIMEOUT */ +}; + +/* net80211: IEEE80211_IS_CTL() */ +static __inline bool +ieee80211_is_ctl(__le16 fc) +{ + __le16 v; + + fc &= htole16(IEEE80211_FC0_TYPE_MASK); + v = htole16(IEEE80211_FC0_TYPE_CTL); + + return (fc == v); +} + +/* net80211: IEEE80211_IS_DATA() */ +static __inline bool +ieee80211_is_data(__le16 fc) +{ + __le16 v; + + fc &= htole16(IEEE80211_FC0_TYPE_MASK); + v = htole16(IEEE80211_FC0_TYPE_DATA); + + return (fc == v); +} + +/* net80211: IEEE80211_IS_QOSDATA() */ +static __inline bool +ieee80211_is_data_qos(__le16 fc) +{ + __le16 v; + + fc &= htole16(IEEE80211_FC0_SUBTYPE_QOS | IEEE80211_FC0_TYPE_MASK | + IEEE80211_FC0_VERSION_MASK); + v = htole16(IEEE80211_FC0_QOSDATA); + + return (fc == v); +} + +/* net80211: IEEE80211_IS_MGMT() */ +static __inline bool +ieee80211_is_mgmt(__le16 fc) +{ + __le16 v; + + fc &= htole16(IEEE80211_FC0_TYPE_MASK); + v = htole16(IEEE80211_FC0_TYPE_MGT); + + return (fc == v); +} + + +/* Derived from net80211::ieee80211_anyhdrsize. */ +static __inline unsigned int +ieee80211_hdrlen(__le16 fc) +{ + unsigned int size; + + if (ieee80211_is_ctl(fc)) { + switch (fc & htole16(IEEE80211_FC0_SUBTYPE_MASK)) { + case htole16(IEEE80211_FC0_SUBTYPE_CTS): + case htole16(IEEE80211_FC0_SUBTYPE_ACK): + return sizeof(struct ieee80211_frame_ack); + case htole16(IEEE80211_FC0_SUBTYPE_BAR): + return sizeof(struct ieee80211_frame_bar); + } + return (sizeof(struct ieee80211_frame_min)); + } + + size = sizeof(struct ieee80211_frame); + if (ieee80211_is_data(fc)) { + if ((fc & htole16(IEEE80211_FC1_DIR_MASK << 8)) == + htole16(IEEE80211_FC1_DIR_DSTODS << 8)) + size += IEEE80211_ADDR_LEN; + if ((fc & htole16(IEEE80211_FC0_SUBTYPE_QOS | + IEEE80211_FC0_TYPE_MASK)) == + htole16(IEEE80211_FC0_SUBTYPE_QOS | + IEEE80211_FC0_TYPE_DATA)) + size += sizeof(uint16_t); + } + + if (ieee80211_is_mgmt(fc)) { +#ifdef __notyet__ + if (debug_80211 > 0) + printf("XXX-BZ %s: TODO? fc %#04x size %u\n", + __func__, fc, size); +#endif + ; + } + + return (size); +} + +#endif /* _LINUXKPI_LINUX_IEEE80211_H */ diff --git a/sys/compat/linuxkpi/common/include/linux/nl80211.h b/sys/compat/linuxkpi/common/include/linux/nl80211.h new file mode 100644 index 00000000000..4f0a55c9dbd --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/nl80211.h @@ -0,0 +1,308 @@ +/*- + * Copyright (c) 2020-2021 The FreeBSD Foundation + * + * This software was developed by Björn Zeeb under sponsorship from + * the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _LINUXKPI_LINUX_NL80211_H +#define _LINUXKPI_LINUX_NL80211_H + +#include + +enum nl80211_feature_flags { + NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE = BIT(0), + NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES = BIT(1), + NL80211_FEATURE_HT_IBSS = BIT(2), + NL80211_FEATURE_LOW_PRIORITY_SCAN = BIT(3), + NL80211_FEATURE_ND_RANDOM_MAC_ADDR = BIT(4), + NL80211_FEATURE_P2P_GO_CTWIN = BIT(5), + NL80211_FEATURE_P2P_GO_OPPPS = BIT(6), + NL80211_FEATURE_QUIET = BIT(7), + NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR = BIT(8), + NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR = BIT(9), + NL80211_FEATURE_DYNAMIC_SMPS = BIT(10), + NL80211_FEATURE_STATIC_SMPS = BIT(11), + NL80211_FEATURE_SUPPORTS_WMM_ADMISSION = BIT(12), + NL80211_FEATURE_TDLS_CHANNEL_SWITCH = BIT(13), + NL80211_FEATURE_TX_POWER_INSERTION = BIT(14), + NL80211_FEATURE_WFA_TPC_IE_IN_PROBES = BIT(15), +}; + +enum nl80211_pmsr_ftm_failure_flags { + NL80211_PMSR_FTM_FAILURE_NO_RESPONSE = BIT(0), + NL80211_PMSR_FTM_FAILURE_PEER_BUSY = BIT(1), + NL80211_PMSR_FTM_FAILURE_UNSPECIFIED = BIT(2), +}; + +enum nl80211_pmsr_status_flags { + NL80211_PMSR_STATUS_FAILURE = BIT(0), + NL80211_PMSR_STATUS_SUCCESS = BIT(1), + NL80211_PMSR_STATUS_TIMEOUT = BIT(2), +}; + +#define NL80211_PMSR_TYPE_FTM 1 + +enum nl80211_reg_rule_flags { + NL80211_RRF_AUTO_BW = BIT(0), + NL80211_RRF_DFS = BIT(1), + NL80211_RRF_GO_CONCURRENT = BIT(2), + NL80211_RRF_NO_IR = BIT(3), + NL80211_RRF_NO_OUTDOOR = BIT(4), + NL80211_RRF_NO_HT40MINUS = BIT(5), + NL80211_RRF_NO_HT40PLUS = BIT(6), + NL80211_RRF_NO_80MHZ = BIT(7), + NL80211_RRF_NO_160MHZ = BIT(8), + NL80211_RRF_NO_HE = BIT(9), +}; +#define NL80211_RRF_NO_HT40 (NL80211_RRF_NO_HT40MINUS|NL80211_RRF_NO_HT40PLUS) + +enum nl80211_scan_flags { + NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME = BIT(0), + NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION = BIT(1), + NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE = BIT(2), + NL80211_SCAN_FLAG_RANDOM_ADDR = BIT(3), + NL80211_SCAN_FLAG_COLOCATED_6GHZ = BIT(4), +}; + +#define NL80211_REGDOM_SET_BY_USER 1 +#define NL80211_REGDOM_SET_BY_DRIVER 2 + +#define NL80211_MAX_SUPP_REG_RULES 512 /* TODO FIXME, random */ + +#define NL80211_BSS_CHAN_WIDTH_20 __LINE__ /* TODO FIXME, brcmfmac */ + +enum nl80211_wpa_versions { + NL80211_WPA_VERSION_1 = 1, + NL80211_WPA_VERSION_2, + NL80211_WPA_VERSION_3, +}; + +enum nl80211_bss_select_attr { + __NL80211_BSS_SELECT_ATTR_INVALID = 0, + NL80211_BSS_SELECT_ATTR_BAND_PREF, + NL80211_BSS_SELECT_ATTR_RSSI, + NL80211_BSS_SELECT_ATTR_RSSI_ADJUST, +}; + +enum nl80211_sta_flag { + /* XXX TODO */ + NL80211_STA_FLAG_ASSOCIATED, + NL80211_STA_FLAG_AUTHENTICATED, + NL80211_STA_FLAG_AUTHORIZED, + NL80211_STA_FLAG_TDLS_PEER, + NL80211_STA_FLAG_WME, +}; + +enum nl80211_band { + /* XXX TODO */ + NL80211_BAND_2GHZ = 0, + NL80211_BAND_5GHZ, + NL80211_BAND_60GHZ, + NL80211_BAND_6GHZ, + + /* Keep this last. */ + NUM_NL80211_BANDS +}; + +enum nl80211_chan_flags { + /* XXX TODO */ + NL80211_CHAN_NO_HT, +}; + +enum nl80211_chan_width { + /* XXX TODO */ + 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, +}; + +enum nl80211_iftype { + /* XXX TODO */ + NL80211_IFTYPE_UNSPECIFIED, + NL80211_IFTYPE_ADHOC, + NL80211_IFTYPE_STATION, + NL80211_IFTYPE_AP, + NL80211_IFTYPE_AP_VLAN, + NL80211_IFTYPE_MONITOR, + NL80211_IFTYPE_P2P_CLIENT, + NL80211_IFTYPE_P2P_DEVICE, + NL80211_IFTYPE_P2P_GO, + NL80211_IFTYPE_MESH_POINT, + NL80211_IFTYPE_WDS, + + /* Keep this last. */ + NUM_NL80211_IFTYPES +}; + +enum nl80211_preamble { + /* XXX TODO */ + NL80211_PREAMBLE_LEGACY, + NL80211_PREAMBLE_HT, + NL80211_PREAMBLE_VHT, + NL80211_PREAMBLE_HE, +}; + +enum nl80211_tdls_operation { + /* XXX TODO */ + NL80211_TDLS_SETUP, + NL80211_TDLS_TEARDOWN, + NL80211_TDLS_ENABLE_LINK, + NL80211_TDLS_DISABLE_LINK, + NL80211_TDLS_DISCOVERY_REQ, + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY, + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY, +}; + +enum nl80211_cqm_rssi_threshold_event { + /* XXX TODO */ + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, +}; + +enum nl80211_ext_feature { + /* XXX TODO */ + NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP, + NL80211_EXT_FEATURE_BSS_PARENT_TSF, + NL80211_EXT_FEATURE_CAN_REPLACE_PTK0, + NL80211_EXT_FEATURE_DFS_OFFLOAD, + NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER, + NL80211_EXT_FEATURE_EXT_KEY_ID, + NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME, + NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER, + NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION, + NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE, + NL80211_EXT_FEATURE_PROTECTED_TWT, + NL80211_EXT_FEATURE_SAE_OFFLOAD, + NL80211_EXT_FEATURE_SCAN_START_TIME, + NL80211_EXT_FEATURE_SET_SCAN_DWELL, + NL80211_EXT_FEATURE_VHT_IBSS, + NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X, + NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK, + NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT, + + /* Keep this last. */ + NUM_NL80211_EXT_FEATURES +}; + +enum nl80211_sta_info { + /* XXX TODO */ + NL80211_STA_INFO_BEACON_RX, + NL80211_STA_INFO_BEACON_SIGNAL_AVG, + NL80211_STA_INFO_BSS_PARAM, + NL80211_STA_INFO_CHAIN_SIGNAL, + NL80211_STA_INFO_CONNECTED_TIME, + NL80211_STA_INFO_INACTIVE_TIME, + NL80211_STA_INFO_SIGNAL, + NL80211_STA_INFO_SIGNAL_AVG, + NL80211_STA_INFO_STA_FLAGS, + NL80211_STA_INFO_RX_BITRATE, + NL80211_STA_INFO_RX_PACKETS, + NL80211_STA_INFO_RX_BYTES, + NL80211_STA_INFO_RX_DROP_MISC, + NL80211_STA_INFO_TX_BITRATE, + NL80211_STA_INFO_TX_PACKETS, + NL80211_STA_INFO_TX_BYTES, + NL80211_STA_INFO_TX_FAILED, +}; + +enum nl80211_ftm_stats { + /* XXX TODO */ + NL80211_FTM_STATS_ASAP_NUM, + NL80211_FTM_STATS_FAILED_NUM, + NL80211_FTM_STATS_NON_ASAP_NUM, + NL80211_FTM_STATS_OUT_OF_WINDOW_TRIGGERS_NUM, + NL80211_FTM_STATS_PARTIAL_NUM, + NL80211_FTM_STATS_RESCHEDULE_REQUESTS_NUM, + NL80211_FTM_STATS_SUCCESS_NUM, + NL80211_FTM_STATS_TOTAL_DURATION_MSEC, + NL80211_FTM_STATS_UNKNOWN_TRIGGERS_NUM, +}; + +enum nl80211_reg_initiator { + /* XXX TODO */ + _XXX_FXIME_RTW88_USES_ENUM, +}; + +struct nl80211_sta_flag_update { + /* XXX TODO */ + int mask, set; + +}; + +enum nl80211_tx_power_setting { + /* XXX TODO */ + NL80211_TX_POWER_AUTOMATIC, + NL80211_TX_POWER_FIXED, + NL80211_TX_POWER_LIMITED, +}; + +enum nl80211_crit_proto_id { + /* XXX TODO */ + NL80211_CRIT_PROTO_DHCP, +}; + +enum nl80211_auth_type { + NL80211_AUTHTYPE_AUTOMATIC, + NL80211_AUTHTYPE_OPEN_SYSTEM, + NL80211_AUTHTYPE_SHARED_KEY, + NL80211_AUTHTYPE_SAE, +}; + +enum nl80211_key_type { + NL80211_KEYTYPE_GROUP, + NL80211_KEYTYPE_PAIRWISE, +}; + +enum nl80211_rate_info_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_rate_info_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_dfs_regions { + NL80211_DFS_UNSET, + NL80211_DFS_FCC, + NL80211_DFS_ETSI, + NL80211_DFS_JP, +}; + +#endif /* _LINUXKPI_LINUX_NL80211_H */ diff --git a/sys/compat/linuxkpi/common/include/net/cfg80211.h b/sys/compat/linuxkpi/common/include/net/cfg80211.h new file mode 100644 index 00000000000..595d65a6ad7 --- /dev/null +++ b/sys/compat/linuxkpi/common/include/net/cfg80211.h @@ -0,0 +1,1489 @@ +/*- + * Copyright (c) 2020-2021 The FreeBSD Foundation + * Copyright (c) 2021 Bjoern A. Zeeb + * + * This software was developed by Björn Zeeb under sponsorship from + * the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _LINUXKPI_NET_CFG80211_H +#define _LINUXKPI_NET_CFG80211_H + +#include +#include +#include +#include +#include +#include +#include +#include + +/* linux_80211.c */ +extern int debug_80211; +#ifndef D80211_TODO +#define D80211_TODO 0x1 +#endif +#ifndef D80211_IMPROVE +#define D80211_IMPROVE 0x2 +#endif +#define TODO() if (debug_80211 & D80211_TODO) \ + printf("%s:%d: XXX LKPI80211 TODO\n", __func__, __LINE__) +#define IMPROVE(...) if (debug_80211 & D80211_IMPROVE) \ + printf("%s:%d: XXX LKPI80211 IMPROVE\n", __func__, __LINE__) + +#define WIPHY_PARAM_FRAG_THRESHOLD __LINE__ /* TODO FIXME brcmfmac */ +#define WIPHY_PARAM_RETRY_LONG __LINE__ /* TODO FIXME brcmfmac */ +#define WIPHY_PARAM_RETRY_SHORT __LINE__ /* TODO FIXME brcmfmac */ +#define WIPHY_PARAM_RTS_THRESHOLD __LINE__ /* TODO FIXME brcmfmac */ + +#define CFG80211_SIGNAL_TYPE_MBM __LINE__ /* TODO FIXME brcmfmac */ + +#define UPDATE_ASSOC_IES 1 + +#define IEEE80211_MAX_CHAINS 4 /* net80211: IEEE80211_MAX_CHAINS copied */ + +enum cfg80211_rate_info_flags { + RATE_INFO_FLAGS_SHORT_GI = BIT(0), + RATE_INFO_FLAGS_MCS = BIT(1), + RATE_INFO_FLAGS_VHT_MCS = BIT(2), + RATE_INFO_FLAGS_HE_MCS = BIT(3), +}; + +extern const uint8_t rfc1042_header[6]; + +enum cfg80211_bss_ftypes { + CFG80211_BSS_FTYPE_UNKNOWN, +}; + +enum ieee80211_channel_flags { + IEEE80211_CHAN_DISABLED = 1, + IEEE80211_CHAN_INDOOR_ONLY, + IEEE80211_CHAN_IR_CONCURRENT, + IEEE80211_CHAN_RADAR, + IEEE80211_CHAN_NO_IR, + IEEE80211_CHAN_NO_HT40MINUS, + IEEE80211_CHAN_NO_HT40PLUS, + IEEE80211_CHAN_NO_80MHZ, + IEEE80211_CHAN_NO_160MHZ, +}; +#define IEEE80211_CHAN_NO_HT40 (IEEE80211_CHAN_NO_HT40MINUS|IEEE80211_CHAN_NO_HT40PLUS) + +struct ieee80211_txrx_stypes { + uint16_t tx; + uint16_t rx; +}; + +/* XXX net80211 has an ieee80211_channel as well. */ +struct linuxkpi_ieee80211_channel { + /* TODO FIXME */ + uint32_t hw_value; /* ic_ieee */ + uint32_t center_freq; /* ic_freq */ + enum ieee80211_channel_flags flags; /* ic_flags */ + enum nl80211_band band; + int8_t max_power; /* ic_maxpower */ + bool beacon_found; + int max_antenna_gain, max_reg_power; + int orig_flags; +}; + +enum ieee80211_vht_mcs_support { + LKPI_IEEE80211_VHT_MCS_SUPPORT_0_7, + LKPI_IEEE80211_VHT_MCS_SUPPORT_0_8, + LKPI_IEEE80211_VHT_MCS_SUPPORT_0_9, +}; + +struct cfg80211_bitrate_mask { + /* TODO FIXME */ + /* This is so weird but nothing else works out...*/ + struct { + uint64_t legacy; /* XXX? */ + uint8_t ht_mcs[16]; /* XXX? */ + uint16_t vht_mcs[16]; /* XXX? */ + uint8_t gi; /* NL80211_TXRATE_FORCE_LGI enum? */ + } control[NUM_NL80211_BANDS]; +}; + +struct rate_info { + /* TODO FIXME */ + int bw, flags, he_dcm, he_gi, he_ru_alloc, legacy, mcs, nss; +}; + +struct ieee80211_rate { + /* TODO FIXME */ + uint32_t bitrate; + uint32_t hw_value; + uint32_t hw_value_short; + uint32_t flags; +}; + +/* XXX net80211 calls these IEEE80211_HTCAP_* */ +#define IEEE80211_HT_CAP_LDPC_CODING 0x0001 /* IEEE80211_HTCAP_LDPC */ +#define IEEE80211_HT_CAP_SUP_WIDTH_20_40 0x0002 /* IEEE80211_HTCAP_CHWIDTH40 */ +#define IEEE80211_HT_CAP_GRN_FLD 0x0010 /* IEEE80211_HTCAP_GREENFIELD */ +#define IEEE80211_HT_CAP_SGI_20 0x0020 /* IEEE80211_HTCAP_SHORTGI20 */ +#define IEEE80211_HT_CAP_SGI_40 0x0040 /* IEEE80211_HTCAP_SHORTGI40 */ +#define IEEE80211_HT_CAP_TX_STBC 0x0080 /* IEEE80211_HTCAP_TXSTBC */ +#define IEEE80211_HT_CAP_RX_STBC 0x0100 /* IEEE80211_HTCAP_RXSTBC */ +#define IEEE80211_HT_CAP_RX_STBC_SHIFT 8 /* IEEE80211_HTCAP_RXSTBC_S */ +#define IEEE80211_HT_CAP_MAX_AMSDU 0x0800 /* IEEE80211_HTCAP_MAXAMSDU */ +#define IEEE80211_HT_CAP_DSSSCCK40 0x1000 /* IEEE80211_HTCAP_DSSSCCK40 */ + +#define IEEE80211_HT_MCS_TX_DEFINED 0x0001 +#define IEEE80211_HT_MCS_TX_RX_DIFF 0x0002 +#define IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT 2 +#define IEEE80211_HT_MCS_RX_HIGHEST_MASK 0x3FF + +struct ieee80211_sta_ht_cap { + /* TODO FIXME */ + int ampdu_density, ampdu_factor; + bool ht_supported; + uint16_t cap; + struct mcs { + uint16_t rx_mask[16]; /* XXX ? > 4 (rtw88) */ + int rx_highest; + uint32_t tx_params; + } mcs; +}; + +/* XXX net80211 calls these IEEE80211_VHTCAP_* */ +#define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895 0x00000000 /* IEEE80211_VHTCAP_MAX_MPDU_LENGTH_3895 */ +#define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991 0x00000001 /* IEEE80211_VHTCAP_MAX_MPDU_LENGTH_7991 */ +#define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 0x00000002 /* IEEE80211_VHTCAP_MAX_MPDU_LENGTH_11454 */ +#define IEEE80211_VHT_CAP_MAX_MPDU_MASK 0x00000003 /* IEEE80211_VHTCAP_MAX_MPDU_MASK */ + +#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ (IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_160MHZ << IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK_S) + +#define IEEE80211_VHT_CAP_RXLDPC 0x00000010 /* IEEE80211_VHTCAP_RXLDPC */ + +#define IEEE80211_VHT_CAP_SHORT_GI_80 0x00000020 /* IEEE80211_VHTCAP_SHORT_GI_80 */ +#define IEEE80211_VHT_CAP_SHORT_GI_160 0x00000040 /* IEEE80211_VHTCAP_SHORT_GI_160 */ + +#define IEEE80211_VHT_CAP_TXSTBC 0x00000080 /* IEEE80211_VHTCAP_TXSTBC */ + +#define IEEE80211_VHT_CAP_RXSTBC_1 0x00000100 /* IEEE80211_VHTCAP_RXSTBC_1 */ +#define IEEE80211_VHT_CAP_RXSTBC_MASK 0x00000700 /* IEEE80211_VHTCAP_RXSTBC_MASK */ + +#define IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE 0x00000800 /* IEEE80211_VHTCAP_SU_BEAMFORMER_CAPABLE */ + +#define IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE 0x00001000 /* IEEE80211_VHTCAP_SU_BEAMFORMEE_CAPABLE */ + +#define IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE 0x00080000 /* IEEE80211_VHTCAP_MU_BEAMFORMER_CAPABLE */ + +#define IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE 0x00100000 /* IEEE80211_VHTCAP_MU_BEAMFORMEE_CAPABLE */ + +#define IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT 13 /* IEEE80211_VHTCAP_BEAMFORMEE_STS_SHIFT */ + +#define IEEE80211_VHT_CAP_HTC_VHT 0x00400000 /* IEEE80211_VHTCAP_HTC_VHT */ + +#define IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN 0x10000000 /* IEEE80211_VHTCAP_RX_ANTENNA_PATTERN */ +#define IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN 0x20000000 /* IEEE80211_VHTCAP_TX_ANTENNA_PATTERN */ + +#define IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB 0x0c000000 /* IEEE80211_VHTCAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB */ + +#define IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT 16 /* IEEE80211_VHTCAP_SOUNDING_DIMENSIONS_SHIFT */ +#define IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK \ + (7 << IEEE80211_VHTCAP_SOUNDING_DIMENSIONS_SHIFT) /* IEEE80211_VHTCAP_SOUNDING_DIMENSIONS_MASK */ + +#define IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT 23 /* IEEE80211_VHTCAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT */ +#define IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK \ + (7 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT) /* IEEE80211_VHTCAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK */ + +struct ieee80211_sta_vht_cap { + /* TODO FIXME */ + bool vht_supported; + uint32_t cap; + struct vht_mcs vht_mcs; +}; + +struct cfg80211_connect_resp_params { + /* XXX TODO */ + uint8_t *bssid; + const uint8_t *req_ie; + const uint8_t *resp_ie; + uint32_t req_ie_len; + uint32_t resp_ie_len; + int status; +}; + +struct cfg80211_inform_bss { + /* XXX TODO */ + int boottime_ns, scan_width, signal; + struct linuxkpi_ieee80211_channel *chan; +}; + +struct cfg80211_roam_info { + /* XXX TODO */ + uint8_t *bssid; + const uint8_t *req_ie; + const uint8_t *resp_ie; + uint32_t req_ie_len; + uint32_t resp_ie_len; + struct linuxkpi_ieee80211_channel *channel; +}; + +struct cfg80211_bss_ies { + /* XXX TODO, type is best guess. Fix if more info. */ + uint8_t *data; + int len; +}; + +struct cfg80211_bss { + /* XXX TODO */ + struct cfg80211_bss_ies *ies; +}; + +struct cfg80211_chan_def { + /* XXX TODO */ + struct linuxkpi_ieee80211_channel *chan; + enum nl80211_chan_width width; + uint32_t center_freq1; + uint32_t center_freq2; +}; + +struct cfg80211_ftm_responder_stats { + /* XXX TODO */ + int asap_num, failed_num, filled, non_asap_num, out_of_window_triggers_num, partial_num, reschedule_requests_num, success_num, total_duration_ms, unknown_triggers_num; +}; + +struct cfg80211_pmsr_capabilities { + /* XXX TODO */ + int max_peers, randomize_mac_addr, report_ap_tsf; + struct { + int asap, bandwidths, max_bursts_exponent, max_ftms_per_burst, non_asap, non_trigger_based, preambles, request_civicloc, request_lci, supported, trigger_based; + } ftm; +}; + +struct cfg80211_pmsr_ftm_request { + /* XXX TODO */ + int asap, burst_period, ftmr_retries, ftms_per_burst, non_trigger_based, num_bursts_exp, request_civicloc, request_lci, trigger_based; + uint8_t bss_color; + bool lmr_feedback; +}; + +struct cfg80211_pmsr_request_peer { + /* XXX TODO */ + struct cfg80211_chan_def chandef; + struct cfg80211_pmsr_ftm_request ftm; + uint8_t addr[ETH_ALEN]; + int report_ap_tsf; +}; + +struct cfg80211_pmsr_request { + /* XXX TODO */ + int cookie, n_peers, timeout; + uint8_t mac_addr[ETH_ALEN], mac_addr_mask[ETH_ALEN]; + struct cfg80211_pmsr_request_peer peers[]; +}; + +struct cfg80211_pmsr_ftm_result { + /* XXX TODO */ + int burst_index, busy_retry_time, failure_reason; + int num_ftmr_successes, rssi_avg, rssi_avg_valid, rssi_spread, rssi_spread_valid, rtt_avg, rtt_avg_valid, rtt_spread, rtt_spread_valid, rtt_variance, rtt_variance_valid; + uint8_t *lci; + uint8_t *civicloc; + int lci_len; + int civicloc_len; +}; + +struct cfg80211_pmsr_result { + /* XXX TODO */ + int ap_tsf, ap_tsf_valid, final, host_time, status, type; + uint8_t addr[ETH_ALEN]; + struct cfg80211_pmsr_ftm_result ftm; +}; + +struct cfg80211_ssid { + int ssid_len; + uint8_t ssid[IEEE80211_MAX_SSID_LEN]; +}; + +struct cfg80211_scan_6ghz_params { + /* XXX TODO */ + uint8_t *bssid; + int channel_idx, psc_no_listen, short_ssid, short_ssid_valid, unsolicited_probe; +}; + +struct cfg80211_match_set { + uint8_t bssid[ETH_ALEN]; + struct cfg80211_ssid ssid; + int rssi_thold; +}; + +struct cfg80211_scan_request { + /* XXX TODO */ + int duration, duration_mandatory, flags; + bool no_cck; + bool scan_6ghz; + struct wireless_dev *wdev; + struct wiphy *wiphy; + int ie_len; + uint8_t *ie; + uint8_t mac_addr[ETH_ALEN], mac_addr_mask[ETH_ALEN]; + int n_ssids; + int n_6ghz_params; + int n_channels; + struct cfg80211_ssid *ssids; + struct cfg80211_scan_6ghz_params *scan_6ghz_params; + struct linuxkpi_ieee80211_channel *channels[0]; +}; + +struct cfg80211_sched_scan_plan { + /* XXX TODO */ + int interval, iterations; +}; + +struct cfg80211_sched_scan_request { + /* XXX TODO */ + int delay, flags; + uint8_t mac_addr[ETH_ALEN], mac_addr_mask[ETH_ALEN]; + uint64_t reqid; + int n_match_sets; + int n_scan_plans; + int n_ssids; + int n_channels; + struct cfg80211_match_set *match_sets; + struct cfg80211_sched_scan_plan *scan_plans; + struct cfg80211_ssid *ssids; + struct linuxkpi_ieee80211_channel *channels[0]; +}; + +struct cfg80211_scan_info { + uint64_t scan_start_tsf; + uint8_t tsf_bssid[ETH_ALEN]; + bool aborted; +}; + +struct cfg80211_beacon_data { + /* XXX TODO */ + const uint8_t *head; + const uint8_t *tail; + uint32_t head_len; + uint32_t tail_len; + const uint8_t *proberesp_ies; + const uint8_t *assocresp_ies; + uint32_t proberesp_ies_len; + uint32_t assocresp_ies_len; +}; + +struct cfg80211_ap_settings { + /* XXX TODO */ + int auth_type, beacon_interval, dtim_period, hidden_ssid, inactivity_timeout; + const uint8_t *ssid; + size_t ssid_len; + struct cfg80211_beacon_data beacon; + struct cfg80211_chan_def chandef; +}; + +struct cfg80211_bss_selection { + /* XXX TODO */ + enum nl80211_bss_select_attr behaviour; + union { + enum nl80211_band band_pref; + struct { + enum nl80211_band band; + uint8_t delta; + } adjust; + } param; +}; + +struct cfg80211_crypto { /* XXX made up name */ + /* XXX TODO */ + enum nl80211_wpa_versions wpa_versions; + uint32_t cipher_group; /* WLAN_CIPHER_SUITE_* */ + uint32_t *akm_suites; + uint32_t *ciphers_pairwise; + const uint8_t *sae_pwd; + const uint8_t *psk; + int n_akm_suites; + int n_ciphers_pairwise; + int sae_pwd_len; +}; + +struct cfg80211_connect_params { + /* XXX TODO */ + struct linuxkpi_ieee80211_channel *channel; + uint8_t *bssid; + const uint8_t *ie; + const uint8_t *ssid; + uint32_t ie_len; + uint32_t ssid_len; + const void *key; + uint32_t key_len; + int auth_type, key_idx, privacy, want_1x; + struct cfg80211_bss_selection bss_select; + struct cfg80211_crypto crypto; +}; + +enum bss_param_flags { /* Used as bitflags. XXX FIXME values? */ + BSS_PARAM_FLAGS_CTS_PROT = 0x01, + BSS_PARAM_FLAGS_SHORT_PREAMBLE = 0x02, + BSS_PARAM_FLAGS_SHORT_SLOT_TIME = 0x04, +}; + +struct cfg80211_ibss_params { + /* XXX TODO */ + int basic_rates, beacon_interval; + int channel_fixed, ie, ie_len, privacy; + int dtim_period; + uint8_t *ssid; + uint8_t *bssid; + int ssid_len; + struct cfg80211_chan_def chandef; + enum bss_param_flags flags; +}; + +struct cfg80211_mgmt_tx_params { + /* XXX TODO */ + struct linuxkpi_ieee80211_channel *chan; + const uint8_t *buf; + size_t len; + int wait; +}; + +struct cfg80211_pmk_conf { + /* XXX TODO */ + const uint8_t *pmk; + uint8_t pmk_len; +}; + +struct cfg80211_pmksa { + /* XXX TODO */ + const uint8_t *bssid; + const uint8_t *pmkid; +}; + +struct cfg80211_wowlan_nd_match { + /* XXX TODO */ + struct cfg80211_ssid ssid; + int n_channels; + uint32_t channels[0]; /* freq! = ieee80211_channel_to_frequency() */ +}; + +struct cfg80211_wowlan_nd_info { + /* XXX TODO */ + int n_matches; + struct cfg80211_wowlan_nd_match *matches[0]; +}; + +enum wiphy_wowlan_support_flags { + WIPHY_WOWLAN_DISCONNECT, + WIPHY_WOWLAN_GTK_REKEY_FAILURE, + WIPHY_WOWLAN_MAGIC_PKT, + WIPHY_WOWLAN_SUPPORTS_GTK_REKEY, + WIPHY_WOWLAN_NET_DETECT, +}; + +struct wiphy_wowlan_support { + /* XXX TODO */ + enum wiphy_wowlan_support_flags flags; + int max_nd_match_sets, max_pkt_offset, n_patterns, pattern_max_len, pattern_min_len; +}; + +struct station_del_parameters { + /* XXX TODO */ + const uint8_t *mac; + uint32_t reason_code; /* elsewhere uint16_t? */ +}; + +struct station_info { + /* TODO FIXME */ + int assoc_req_ies_len, connected_time; + int generation, inactive_time, rx_bytes, rx_dropped_misc, rx_packets, signal, tx_bytes, tx_packets; + int filled, rx_beacon, rx_beacon_signal_avg, signal_avg; + int rx_duration, tx_failed, tx_retries; + + int chains; + uint8_t chain_signal[IEEE80211_MAX_CHAINS]; + uint8_t chain_signal_avg[IEEE80211_MAX_CHAINS]; + + uint8_t *assoc_req_ies; + struct rate_info rxrate; + struct rate_info txrate; + struct cfg80211_ibss_params bss_param; + struct nl80211_sta_flag_update sta_flags; +}; + +struct station_parameters { + /* XXX TODO */ + int sta_flags_mask, sta_flags_set; +}; + +struct key_params { + /* XXX TODO */ + const uint8_t *key; + const uint8_t *seq; + int key_len; + int seq_len; + uint32_t cipher; /* WLAN_CIPHER_SUITE_* */ +}; + +struct mgmt_frame_regs { + /* XXX TODO */ + int interface_stypes; +}; + +struct vif_params { + /* XXX TODO */ + uint8_t macaddr[ETH_ALEN]; +}; + +/* That the world needs so many different structs for this is amazing. */ +struct mac_address { + uint8_t addr[ETH_ALEN]; +}; + +#define REG_RULE(_begin, _end, _bw, _gain, _eirp, _x) \ +{ \ + .freq_range.start_freq_khz = (_begin) * 1000, \ + .freq_range.end_freq_khz = (_end) * 1000, \ + .freq_range.max_bandwidth_khz = (_bw) * 1000, \ + .power_rule.max_antenna_gain = DBI_TO_MBI(_gain), \ + .power_rule.max_eirp = DBM_TO_MBM(_eirp), \ + .flags = (_x), /* ? */ \ + /* XXX TODO FIXME */ \ +} + +struct ieee80211_reg_rule { + /* TODO FIXME */ + uint32_t flags; + struct freq_range { + int start_freq_khz; + int end_freq_khz; + int max_bandwidth_khz; + } freq_range; + struct power_rule { + int max_antenna_gain; + int max_eirp; + } power_rule; +}; + +struct linuxkpi_ieee80211_regdomain { + /* TODO FIXME */ + uint8_t alpha2[2]; + int n_reg_rules; + struct ieee80211_reg_rule reg_rules[]; +}; + +/* XXX-BZ this are insensible values probably ... */ +#define IEEE80211_HE_MAC_CAP0_HTC_HE 0x1 +#define IEEE80211_HE_MAC_CAP0_TWT_REQ 0x2 + +#define IEEE80211_HE_MAC_CAP1_LINK_ADAPTATION 0x1 +#define IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8 0x2 +#define IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US 0x4 + +#define IEEE80211_HE_MAC_CAP2_32BIT_BA_BITMAP 0x1 +#define IEEE80211_HE_MAC_CAP2_ACK_EN 0x2 +#define IEEE80211_HE_MAC_CAP2_BSR 0x4 +#define IEEE80211_HE_MAC_CAP2_LINK_ADAPTATION 0x8 +#define IEEE80211_HE_MAC_CAP2_BCAST_TWT 0x10 + +#define IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_VHT_2 0x1 +#define IEEE80211_HE_MAC_CAP3_OMI_CONTROL 0x2 +#define IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_1 0x10 +#define IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3 0x20 +#define IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_MASK 0x30 +#define IEEE80211_HE_MAC_CAP3_RX_CTRL_FRAME_TO_MULTIBSS 0x80 + +#define IEEE80211_HE_MAC_CAP4_AMDSU_IN_AMPDU 0x1 +#define IEEE80211_HE_MAC_CAP4_BQR 0x2 +#define IEEE80211_HE_MAC_CAP4_MULTI_TID_AGG_TX_QOS_B39 0x4 +#define IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU 0x8 + +#define IEEE80211_HE_MAC_CAP5_HE_DYNAMIC_SM_PS 0x1 +#define IEEE80211_HE_MAC_CAP5_HT_VHT_TRIG_FRAME_RX 0x2 +#define IEEE80211_HE_MAC_CAP5_MULTI_TID_AGG_TX_QOS_B40 0x4 +#define IEEE80211_HE_MAC_CAP5_MULTI_TID_AGG_TX_QOS_B41 0x8 +#define IEEE80211_HE_MAC_CAP5_UL_2x996_TONE_RU 0x10 + +#define IEEE80211_HE_MCS_NOT_SUPPORTED 0x0 +#define IEEE80211_HE_MCS_SUPPORT_0_7 0x1 +#define IEEE80211_HE_MCS_SUPPORT_0_9 0x2 +#define IEEE80211_HE_MCS_SUPPORT_0_11 0x4 + +#define IEEE80211_HE_6GHZ_CAP_TX_ANTPAT_CONS 0x01 +#define IEEE80211_HE_6GHZ_CAP_RX_ANTPAT_CONS 0x02 +#define IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START 0x04 +#define IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN 0x08 +#define IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP 0x10 + +#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G 0x1 +#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G 0x2 +#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G 0x4 + +#define IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A 0x1 +#define IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD 0x2 +#define IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS 0x4 +#define IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK 0x8 + +#define IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_TX_MAX_NSTS 0x1 +#define IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US 0x2 +#define IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ 0x4 +#define IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ 0x8 + +#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_MASK 0x1 +#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_NO_DCM 0x2 +#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_NO_DCM 0x4 +#define IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1 0x8 +#define IEEE80211_HE_PHY_CAP3_DCM_MAX_TX_NSS_1 0x10 + +#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_8 0x1 +#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_8 0x2 +#define IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE 0x4 + +#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_2 0x1 +#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_2 0x2 + +#define IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT 0x1 +#define IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMER_FB 0x2 +#define IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMER_FB 0x4 +#define IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB 0x10 +#define IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB 0x20 + +#define IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI 0x1 +#define IEEE80211_HE_PHY_CAP7_MAX_NC_1 0x2 +#define IEEE80211_HE_PHY_CAP7_MAX_NC_2 0x4 +#define IEEE80211_HE_PHY_CAP7_MAX_NC_MASK 0x6 +#define IEEE80211_HE_PHY_CAP7_POWER_BOOST_FACTOR_AR 0x8 +#define IEEE80211_HE_PHY_CAP7_POWER_BOOST_FACTOR_SUPP 0x10 +#define IEEE80211_HE_PHY_CAP7_STBC_RX_ABOVE_80MHZ 0x20 + +#define IEEE80211_HE_PHY_CAP8_20MHZ_IN_160MHZ_HE_PPDU 0x1 +#define IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G 0x2 +#define IEEE80211_HE_PHY_CAP8_80MHZ_IN_160MHZ_HE_PPDU 0x4 +#define IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_2x996 0x8 +#define IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_242 0x10 +#define IEEE80211_HE_PHY_CAP8_HE_ER_SU_PPDU_4XLTF_AND_08_US_GI 0x20 + +#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_0US 0x1 +#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_16US 0x2 +#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_8US 0x4 +#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_MASK 0x8 +#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_RESERVED 0x10 +#define IEEE80211_HE_PHY_CAP9_NON_TRIGGERED_CQI_FEEDBACK 0x20 +#define IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB 0x40 +#define IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB 0x80 +#define IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU 0x100 +#define IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU 0x200 + +#define IEEE80211_HE_PHY_CAP10_HE_MU_M1RU_MAX_LTF 0x1 + +#define VENDOR_CMD_RAW_DATA (void *)(uintptr_t)(-ENOENT) + +struct ieee80211_he_cap_elem { + u8 mac_cap_info[6]; + u8 phy_cap_info[11]; +} __packed; + +struct ieee80211_he_mcs_nss_supp { + /* TODO FIXME */ + uint32_t rx_mcs_80; + uint32_t tx_mcs_80; + uint32_t rx_mcs_160; + uint32_t tx_mcs_160; + uint32_t rx_mcs_80p80; + uint32_t tx_mcs_80p80; +}; + +#define IEEE80211_STA_HE_CAP_PPE_THRES_MAX 32 +struct ieee80211_sta_he_cap { + /* TODO FIXME */ + int has_he; + struct ieee80211_he_cap_elem he_cap_elem; + struct ieee80211_he_mcs_nss_supp he_mcs_nss_supp; + uint8_t ppe_thres[IEEE80211_STA_HE_CAP_PPE_THRES_MAX]; +}; + +struct ieee80211_sta_he_6ghz_capa { + /* TODO FIXME */ + int capa; +}; + +struct ieee80211_sband_iftype_data { + /* TODO FIXME */ + enum nl80211_iftype types_mask; + struct ieee80211_sta_he_cap he_cap; + struct ieee80211_sta_he_6ghz_capa he_6ghz_capa; + struct { + const uint8_t *data; + size_t len; + } vendor_elems; +}; + +struct ieee80211_supported_band { + /* TODO FIXME */ + struct linuxkpi_ieee80211_channel *channels; + struct ieee80211_rate *bitrates; + struct ieee80211_sband_iftype_data *iftype_data; + int n_channels; + int n_bitrates; + int n_iftype_data; + enum nl80211_band band; + struct ieee80211_sta_ht_cap ht_cap; + struct ieee80211_sta_vht_cap vht_cap; +}; + +struct cfg80211_pkt_pattern { + /* XXX TODO */ + uint8_t *mask; + uint8_t *pattern; + int pattern_len; + int pkt_offset; +}; + +struct cfg80211_wowlan { + /* XXX TODO */ + int disconnect, gtk_rekey_failure, magic_pkt; + int n_patterns; + struct cfg80211_sched_scan_request *nd_config; + struct cfg80211_pkt_pattern *patterns; +}; + +struct cfg80211_gtk_rekey_data { + /* XXX TODO */ + int kck, kek, replay_ctr; +}; + +struct ieee80211_iface_limit { + /* TODO FIXME */ + int max, types; +}; + +struct ieee80211_iface_combination { + /* TODO FIXME */ + const struct ieee80211_iface_limit *limits; + int n_limits; + int max_interfaces, num_different_channels; + int beacon_int_infra_match, beacon_int_min_gcd; +}; + +struct iface_combination_params { + int num_different_channels; + int iftype_num[NUM_NL80211_IFTYPES]; +}; + +struct regulatory_request { + /* XXX TODO */ + uint8_t alpha2[2]; + int initiator, dfs_region; +}; + +enum wiphy_vendor_cmd_need_flags { + WIPHY_VENDOR_CMD_NEED_NETDEV = 0x01, + WIPHY_VENDOR_CMD_NEED_RUNNING = 0x02, + WIPHY_VENDOR_CMD_NEED_WDEV = 0x04, +}; + +struct wiphy_vendor_command { + struct { + uint32_t vendor_id; + uint32_t subcmd; + }; + uint32_t flags; + void *policy; + int (*doit)(struct wiphy *, struct wireless_dev *, const void *, int); +}; + +struct wiphy_iftype_ext_capab { + /* TODO FIXME */ + enum nl80211_iftype iftype; + const uint8_t *extended_capabilities; + const uint8_t *extended_capabilities_mask; + uint8_t extended_capabilities_len; + +}; + +enum cfg80211_regulatory { + REGULATORY_CUSTOM_REG = BIT(0), + REGULATORY_STRICT_REG = BIT(1), + REGULATORY_DISABLE_BEACON_HINTS = BIT(2), + REGULATORY_ENABLE_RELAX_NO_IR = BIT(3), + REGULATORY_WIPHY_SELF_MANAGED = BIT(4), + REGULATORY_COUNTRY_IE_IGNORE = BIT(5), +}; + +#define WIPHY_FLAG_AP_UAPSD 0x00000001 +#define WIPHY_FLAG_HAS_CHANNEL_SWITCH 0x00000002 +#define WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL 0x00000004 +#define WIPHY_FLAG_HAVE_AP_SME 0x00000008 +#define WIPHY_FLAG_IBSS_RSN 0x00000010 +#define WIPHY_FLAG_NETNS_OK 0x00000020 +#define WIPHY_FLAG_OFFCHAN_TX 0x00000040 +#define WIPHY_FLAG_PS_ON_BY_DEFAULT 0x00000080 +#define WIPHY_FLAG_SPLIT_SCAN_6GHZ 0x00000100 +#define WIPHY_FLAG_SUPPORTS_EXT_KEK_KCK 0x00000200 +#define WIPHY_FLAG_SUPPORTS_FW_ROAM 0x00000400 +#define WIPHY_FLAG_SUPPORTS_TDLS 0x00000800 +#define WIPHY_FLAG_TDLS_EXTERNAL_SETUP 0x00001000 + +struct wiphy { + + struct device *dev; + struct mac_address *addresses; + int n_addresses; + uint32_t flags; + struct ieee80211_supported_band *bands[NUM_NL80211_BANDS]; + uint8_t perm_addr[ETH_ALEN]; + + /* XXX TODO */ + const struct cfg80211_pmsr_capabilities *pmsr_capa; + const struct wiphy_iftype_ext_capab *iftype_ext_capab; + const struct linuxkpi_ieee80211_regdomain *regd; + char fw_version[64]; /* XXX TODO */ + const struct ieee80211_iface_combination *iface_combinations; + const uint32_t *cipher_suites; + int n_iface_combinations; + int n_cipher_suites; + void(*reg_notifier)(struct wiphy *, struct regulatory_request *); + enum cfg80211_regulatory regulatory_flags; + int n_vendor_commands; + const struct wiphy_vendor_command *vendor_commands; + const struct ieee80211_txrx_stypes *mgmt_stypes; + uint32_t rts_threshold; + uint32_t frag_threshold; + + int available_antennas_rx, available_antennas_tx; + int features, hw_version; + int interface_modes, max_match_sets, max_remain_on_channel_duration, max_scan_ie_len, max_scan_ssids, max_sched_scan_ie_len, max_sched_scan_plan_interval, max_sched_scan_plan_iterations, max_sched_scan_plans, max_sched_scan_reqs, max_sched_scan_ssids; + int num_iftype_ext_capab; + int max_ap_assoc_sta, probe_resp_offload, software_iftypes; + int bss_select_support, max_num_pmkids, retry_long, retry_short, signal_type; + + unsigned long ext_features[BITS_TO_LONGS(NUM_NL80211_EXT_FEATURES)]; + struct dentry *debugfsdir; + struct cfg80211_wowlan_support *wowlan; + /* Lower layer (driver/mac80211) specific data. */ + /* Must stay last. */ + uint8_t priv[0] __aligned(CACHE_LINE_SIZE); +}; + +struct wireless_dev { + /* XXX TODO, like ic? */ + int iftype; + int address; + struct net_device *netdev; + struct wiphy *wiphy; +}; + +struct cfg80211_ops { + /* XXX TODO */ + struct wireless_dev *(*add_virtual_intf)(struct wiphy *, const char *, unsigned char, enum nl80211_iftype, struct vif_params *); + int (*del_virtual_intf)(struct wiphy *, struct wireless_dev *); + s32 (*change_virtual_intf)(struct wiphy *, struct net_device *, enum nl80211_iftype, struct vif_params *); + s32 (*scan)(struct wiphy *, struct cfg80211_scan_request *); + s32 (*set_wiphy_params)(struct wiphy *, u32); + s32 (*join_ibss)(struct wiphy *, struct net_device *, struct cfg80211_ibss_params *); + s32 (*leave_ibss)(struct wiphy *, struct net_device *); + s32 (*get_station)(struct wiphy *, struct net_device *, const u8 *, struct station_info *); + int (*dump_station)(struct wiphy *, struct net_device *, int, u8 *, struct station_info *); + s32 (*set_tx_power)(struct wiphy *, struct wireless_dev *, enum nl80211_tx_power_setting, s32); + s32 (*get_tx_power)(struct wiphy *, struct wireless_dev *, s32 *); + s32 (*add_key)(struct wiphy *, struct net_device *, u8, bool, const u8 *, struct key_params *); + s32 (*del_key)(struct wiphy *, struct net_device *, u8, bool, const u8 *); + s32 (*get_key)(struct wiphy *, struct net_device *, u8, bool, const u8 *, void *, void(*)(void *, struct key_params *)); + s32 (*set_default_key)(struct wiphy *, struct net_device *, u8, bool, bool); + s32 (*set_default_mgmt_key)(struct wiphy *, struct net_device *, u8); + s32 (*set_power_mgmt)(struct wiphy *, struct net_device *, bool, s32); + s32 (*connect)(struct wiphy *, struct net_device *, struct cfg80211_connect_params *); + s32 (*disconnect)(struct wiphy *, struct net_device *, u16); + s32 (*suspend)(struct wiphy *, struct cfg80211_wowlan *); + s32 (*resume)(struct wiphy *); + s32 (*set_pmksa)(struct wiphy *, struct net_device *, struct cfg80211_pmksa *); + s32 (*del_pmksa)(struct wiphy *, struct net_device *, struct cfg80211_pmksa *); + s32 (*flush_pmksa)(struct wiphy *, struct net_device *); + s32 (*start_ap)(struct wiphy *, struct net_device *, struct cfg80211_ap_settings *); + int (*stop_ap)(struct wiphy *, struct net_device *); + s32 (*change_beacon)(struct wiphy *, struct net_device *, struct cfg80211_beacon_data *); + int (*del_station)(struct wiphy *, struct net_device *, struct station_del_parameters *); + int (*change_station)(struct wiphy *, struct net_device *, const u8 *, struct station_parameters *); + int (*sched_scan_start)(struct wiphy *, struct net_device *, struct cfg80211_sched_scan_request *); + int (*sched_scan_stop)(struct wiphy *, struct net_device *, u64); + void (*update_mgmt_frame_registrations)(struct wiphy *, struct wireless_dev *, struct mgmt_frame_regs *); + int (*mgmt_tx)(struct wiphy *, struct wireless_dev *, struct cfg80211_mgmt_tx_params *, u64 *); + int (*cancel_remain_on_channel)(struct wiphy *, struct wireless_dev *, u64); + int (*get_channel)(struct wiphy *, struct wireless_dev *, struct cfg80211_chan_def *); + int (*crit_proto_start)(struct wiphy *, struct wireless_dev *, enum nl80211_crit_proto_id, u16); + void (*crit_proto_stop)(struct wiphy *, struct wireless_dev *); + int (*tdls_oper)(struct wiphy *, struct net_device *, const u8 *, enum nl80211_tdls_operation); + int (*update_connect_params)(struct wiphy *, struct net_device *, struct cfg80211_connect_params *, u32); + int (*set_pmk)(struct wiphy *, struct net_device *, const struct cfg80211_pmk_conf *); + int (*del_pmk)(struct wiphy *, struct net_device *, const u8 *); + int (*remain_on_channel)(struct wiphy *, struct wireless_dev *, struct linuxkpi_ieee80211_channel *, unsigned int, u64 *); + int (*start_p2p_device)(struct wiphy *, struct wireless_dev *); + void (*stop_p2p_device)(struct wiphy *, struct wireless_dev *); +}; + + +/* -------------------------------------------------------------------------- */ + +/* linux_80211.c */ + +struct wiphy *linuxkpi_wiphy_new(const struct cfg80211_ops *, size_t); +void linuxkpi_wiphy_free(struct wiphy *wiphy); + +int linuxkpi_regulatory_set_wiphy_regd_sync(struct wiphy *wiphy, + struct linuxkpi_ieee80211_regdomain *regd); +uint32_t linuxkpi_ieee80211_channel_to_frequency(uint32_t, enum nl80211_band); +uint32_t linuxkpi_ieee80211_frequency_to_channel(uint32_t, uint32_t); + +/* -------------------------------------------------------------------------- */ + +static __inline struct wiphy * +wiphy_new(const struct cfg80211_ops *ops, size_t priv_len) +{ + + return (linuxkpi_wiphy_new(ops, priv_len)); +} + +static __inline void +wiphy_free(struct wiphy *wiphy) +{ + + linuxkpi_wiphy_free(wiphy); +} + +static __inline void * +wiphy_priv(struct wiphy *wiphy) +{ + + return (wiphy->priv); +} + +static __inline void +set_wiphy_dev(struct wiphy *wiphy, struct device *dev) +{ + + wiphy->dev = dev; +} + +static __inline struct device * +wiphy_dev(struct wiphy *wiphy) +{ + + return (wiphy->dev); +} + +#define wiphy_err(_wiphy, _fmt, ...) \ + dev_err((_wiphy)->dev, _fmt, __VA_ARGS__) + +static __inline const struct linuxkpi_ieee80211_regdomain * +wiphy_dereference(struct wiphy *wiphy, + const struct linuxkpi_ieee80211_regdomain *regd) +{ + TODO(); + return (NULL); +} + +/* -------------------------------------------------------------------------- */ + +static __inline int +reg_query_regdb_wmm(uint8_t *alpha2, uint32_t center_freq, + struct ieee80211_reg_rule *rule) +{ + + /* ETSI has special rules. FreeBSD regdb needs to learn about them. */ + TODO(); + + return (-ENXIO); +} + +static __inline const u8 * +cfg80211_find_ie_match(uint32_t f, const u8 *ies, size_t ies_len, + const u8 *match, int x, int y) +{ + TODO(); + return (NULL); +} + +static __inline const u8 * +cfg80211_find_ie(uint8_t eid, uint8_t *variable, uint32_t frame_size) +{ + TODO(); + return (NULL); +} + +static __inline void +cfg80211_pmsr_complete(struct wireless_dev *wdev, + struct cfg80211_pmsr_request *req, gfp_t gfp) +{ + TODO(); +} + +static __inline void +cfg80211_pmsr_report(struct wireless_dev *wdev, + struct cfg80211_pmsr_request *req, + struct cfg80211_pmsr_result *result, gfp_t gfp) +{ + TODO(); +} + +static __inline void +cfg80211_chandef_create(struct cfg80211_chan_def *chandef, + struct linuxkpi_ieee80211_channel *chan, enum nl80211_chan_flags chan_flag) +{ + + KASSERT(chandef != NULL, ("%s: chandef is NULL\n", __func__)); + KASSERT(chan != NULL, ("%s: chan is NULL\n", __func__)); + + chandef->chan = chan; + chandef->center_freq2 = 0; /* Set here and only overwrite if needed. */ + chandef->chan = chan; + + switch (chan_flag) { + case NL80211_CHAN_NO_HT: + chandef->width = NL80211_CHAN_WIDTH_20_NOHT; + chandef->center_freq1 = chan->center_freq; + break; + default: + printf("%s: unsupported chan_flag %#0x\n", __func__, chan_flag); + /* XXX-BZ should we panic instead? */ + chandef->width = NL80211_CHAN_WIDTH_20; + chandef->center_freq1 = chan->center_freq; + break; + }; +} + +static __inline void +cfg80211_bss_iter(struct wiphy *wiphy, struct cfg80211_chan_def *chandef, + void (*iterfunc)(struct wiphy *, struct cfg80211_bss *, void *), void *data) +{ + TODO(); +} + +struct element { + uint8_t id; + uint8_t datalen; + uint8_t data[0]; +}; + +static __inline const struct element * +cfg80211_find_elem(enum ieee80211_eid eid, uint8_t *data, size_t len) +{ + TODO(); + return (NULL); +} + +static __inline uint32_t +cfg80211_calculate_bitrate(struct rate_info *rate) +{ + TODO(); + return (-1); +} + +static __inline uint32_t +ieee80211_channel_to_frequency(uint32_t channel, enum nl80211_band band) +{ + + return (linuxkpi_ieee80211_channel_to_frequency(channel, band)); +} + +static __inline uint32_t +ieee80211_frequency_to_channel(uint32_t freq) +{ + + return (linuxkpi_ieee80211_frequency_to_channel(freq, 0)); +} + +static __inline int +regulatory_set_wiphy_regd_sync(struct wiphy *wiphy, + struct linuxkpi_ieee80211_regdomain *regd) +{ + IMPROVE(); + return (linuxkpi_regulatory_set_wiphy_regd_sync(wiphy, regd)); +} + +static __inline int +regulatory_set_wiphy_regd_sync_rtnl(struct wiphy *wiphy, + struct linuxkpi_ieee80211_regdomain *regd) +{ + + IMPROVE(); + return (linuxkpi_regulatory_set_wiphy_regd_sync(wiphy, regd)); +} + +static __inline int +regulatory_set_wiphy_regd(struct wiphy *wiphy, + struct linuxkpi_ieee80211_regdomain *regd) +{ + + IMPROVE(); + if (regd == NULL) + return (EINVAL); + + /* XXX-BZ wild guessing here based on brcmfmac. */ + if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) + wiphy->regd = regd; + else + return (EPERM); + + /* XXX FIXME, do we have to do anything with reg_notifier? */ + return (0); +} + +static __inline int +regulatory_hint(struct wiphy *wiphy, uint8_t *alpha2) +{ + TODO(); + return (-ENXIO); +} + +static __inline struct linuxkpi_ieee80211_regdomain * +rtnl_dereference(const struct linuxkpi_ieee80211_regdomain *regd) +{ + TODO(); + return (NULL); +} + +static __inline struct ieee80211_reg_rule * +freq_reg_info(struct wiphy *wiphy, uint32_t center_freq) +{ + TODO(); + return (NULL); +} + +static __inline struct cfg80211_bss * +cfg80211_get_bss(struct wiphy *wiphy, struct linuxkpi_ieee80211_channel *chan, + uint8_t *bssid, void *p, int x, uint32_t f1, uint32_t f2) +{ + TODO(); + return (NULL); +} + +static __inline void +cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *bss) +{ + TODO(); +} + +static __inline void +wiphy_apply_custom_regulatory(struct wiphy *wiphy, + const struct linuxkpi_ieee80211_regdomain *regd) +{ + TODO(); +} + +static __inline char * +wiphy_name(struct wiphy *wiphy) +{ + if (wiphy != NULL && wiphy->dev != NULL) + return dev_name(wiphy->dev); + else + return ("wlanNA"); +} + +static __inline void +wiphy_read_of_freq_limits(struct wiphy *wiphy) +{ +#ifdef FDT + TODO(); +#endif +} + +static __inline uint8_t * +cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type, + uint8_t *data, size_t len) +{ + TODO(); + return (NULL); +} + +static __inline void +wiphy_ext_feature_set(struct wiphy *wiphy, enum nl80211_ext_feature ef) +{ + + set_bit(ef, wiphy->ext_features); +} + +static __inline void * +wiphy_net(struct wiphy *wiphy) +{ + TODO(); + return (NULL); /* XXX passed to dev_net_set() */ +} + +static __inline int +wiphy_register(struct wiphy *wiphy) +{ + TODO(); + return (0); +} + +static __inline void +wiphy_unregister(struct wiphy *wiphy) +{ + TODO(); +} + +static __inline void +wiphy_warn(struct wiphy *wiphy, const char *fmt, ...) +{ + TODO(); +} + +static __inline int +cfg80211_check_combinations(struct wiphy *wiphy, + struct iface_combination_params *params) +{ + TODO(); + return (-ENOENT); +} + +static __inline uint8_t +cfg80211_classify8021d(struct sk_buff *skb, void *p) +{ + TODO(); + return (0); +} + +static __inline void +cfg80211_connect_done(struct net_device *ndev, + struct cfg80211_connect_resp_params *conn_params, gfp_t gfp) +{ + TODO(); +} + +static __inline void +cfg80211_crit_proto_stopped(struct wireless_dev *wdev, gfp_t gfp) +{ + TODO(); +} + +static __inline void +cfg80211_disconnected(struct net_device *ndev, uint16_t reason, + void *p, int x, bool locally_generated, gfp_t gfp) +{ + TODO(); +} + +static __inline int +cfg80211_get_p2p_attr(const u8 *ie, u32 ie_len, + enum ieee80211_p2p_attr_ids attr, u8 *p, size_t p_len) +{ + TODO(); + return (-1); +} + +static __inline void +cfg80211_ibss_joined(struct net_device *ndev, const uint8_t *addr, + struct linuxkpi_ieee80211_channel *chan, gfp_t gfp) +{ + TODO(); +} + +static __inline struct cfg80211_bss * +cfg80211_inform_bss(struct wiphy *wiphy, + struct linuxkpi_ieee80211_channel *channel, + enum cfg80211_bss_ftypes bss_ftype, const uint8_t *bss, int _x, + uint16_t cap, uint16_t intvl, const uint8_t *ie, size_t ie_len, + int signal, gfp_t gfp) +{ + TODO(); + return (NULL); +} + +static __inline struct cfg80211_bss * +cfg80211_inform_bss_data(struct wiphy *wiphy, + struct cfg80211_inform_bss *bss_data, + enum cfg80211_bss_ftypes bss_ftype, const uint8_t *bss, int _x, + uint16_t cap, uint16_t intvl, const uint8_t *ie, size_t ie_len, gfp_t gfp) +{ + TODO(); + return (NULL); +} + +static __inline void +cfg80211_mgmt_tx_status(struct wireless_dev *wdev, uint64_t cookie, + const u8 *buf, size_t len, bool ack, gfp_t gfp) +{ + TODO(); +} + +static __inline void +cfg80211_michael_mic_failure(struct net_device *ndev, const uint8_t *addr, + enum nl80211_key_type key_type, int _x, void *p, gfp_t gfp) +{ + TODO(); +} + +static __inline void +cfg80211_new_sta(struct net_device *ndev, const uint8_t *addr, + struct station_info *sinfo, gfp_t gfp) +{ + TODO(); +} + +static __inline void +cfg80211_del_sta(struct net_device *ndev, const uint8_t *addr, gfp_t gfp) +{ + TODO(); +} + +static __inline void +cfg80211_port_authorized(struct net_device *ndev, const uint8_t *bssid, + gfp_t gfp) +{ + TODO(); +} + +static __inline void +cfg80211_ready_on_channel(struct wireless_dev *wdev, uint64_t cookie, + struct linuxkpi_ieee80211_channel *channel, unsigned int duration, + gfp_t gfp) +{ + TODO(); +} + +static __inline void +cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, + uint64_t cookie, struct linuxkpi_ieee80211_channel *channel, gfp_t gfp) +{ + TODO(); +} + +static __inline void +cfg80211_report_wowlan_wakeup(void) +{ + TODO(); +} + +static __inline void +cfg80211_roamed(struct net_device *ndev, struct cfg80211_roam_info *roam_info, + gfp_t gfp) +{ + TODO(); +} + +static __inline void +cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int _x, + uint8_t *p, size_t p_len, int _x2) +{ + TODO(); +} + +static __inline void +cfg80211_scan_done(struct cfg80211_scan_request *scan_request, + struct cfg80211_scan_info *info) +{ + TODO(); +} + +static __inline void +cfg80211_sched_scan_results(struct wiphy *wiphy, uint64_t reqid) +{ + TODO(); +} + +static __inline void +cfg80211_sched_scan_stopped(struct wiphy *wiphy, int _x) +{ + TODO(); +} + +static __inline void +cfg80211_unregister_wdev(struct wireless_dev *wdev) +{ + TODO(); +} + +static __inline struct sk_buff * +cfg80211_vendor_cmd_alloc_reply_skb(struct wiphy *wiphy, unsigned int len) +{ + TODO(); + return (NULL); +} + +static __inline int +cfg80211_vendor_cmd_reply(struct sk_buff *skb) +{ + TODO(); + return (-ENXIO); +} + +static __inline struct linuxkpi_ieee80211_channel * +ieee80211_get_channel(struct wiphy *wiphy, uint32_t freq) +{ + TODO(); + return (NULL); +} + +static __inline size_t +ieee80211_get_hdrlen_from_skb(struct sk_buff *skb) +{ + + TODO(); + return (-1); +} + +static __inline void +cfg80211_bss_flush(struct wiphy *wiphy) +{ + TODO(); +} + +static __inline bool +cfg80211_channel_is_psc(struct linuxkpi_ieee80211_channel *channel) +{ + + /* Only 6Ghz. */ + if (channel->band != NL80211_BAND_6GHZ) + return (false); + + TODO(); + return (false); +} + + +/* Used for scanning at least. */ +static __inline void +get_random_mask_addr(uint8_t *dst, const uint8_t *addr, const uint8_t *mask) +{ + int i; + + /* Get a completely random address and then overlay what we want. */ + get_random_bytes(dst, ETH_ALEN); + for (i = 0; i < ETH_ALEN; i++) + dst[i] = (dst[i] & ~(mask[i])) | (addr[i] & mask[i]); +} + +#ifndef LINUXKPI_NET80211 +#define ieee80211_channel linuxkpi_ieee80211_channel +#define ieee80211_regdomain linuxkpi_ieee80211_regdomain +/* net80211::IEEE80211_VHT_MCS_SUPPORT_0_n() conflicts */ +#if defined(IEEE80211_VHT_MCS_SUPPORT_0_7) +#undef IEEE80211_VHT_MCS_SUPPORT_0_7 +#endif +#if defined(IEEE80211_VHT_MCS_SUPPORT_0_8) +#undef IEEE80211_VHT_MCS_SUPPORT_0_8 +#endif +#if defined(IEEE80211_VHT_MCS_SUPPORT_0_9) +#undef IEEE80211_VHT_MCS_SUPPORT_0_9 +#endif +#define IEEE80211_VHT_MCS_SUPPORT_0_7 LKPI_IEEE80211_VHT_MCS_SUPPORT_0_7 +#define IEEE80211_VHT_MCS_SUPPORT_0_8 LKPI_IEEE80211_VHT_MCS_SUPPORT_0_8 +#define IEEE80211_VHT_MCS_SUPPORT_0_9 LKPI_IEEE80211_VHT_MCS_SUPPORT_0_9 +#endif + +#endif /* _LINUXKPI_NET_CFG80211_H */ diff --git a/sys/compat/linuxkpi/common/include/net/ieee80211_radiotap.h b/sys/compat/linuxkpi/common/include/net/ieee80211_radiotap.h index 9c22e3e0698..675ac1da659 100644 --- a/sys/compat/linuxkpi/common/include/net/ieee80211_radiotap.h +++ b/sys/compat/linuxkpi/common/include/net/ieee80211_radiotap.h @@ -1,6 +1,4 @@ /*- - * SPDX-License-Identifier: BSD-2-Clause - * * Copyright (c) 2020-2021 The FreeBSD Foundation * * This software was developed by Björn Zeeb under sponsorship from diff --git a/sys/compat/linuxkpi/common/include/net/mac80211.h b/sys/compat/linuxkpi/common/include/net/mac80211.h new file mode 100644 index 00000000000..ffba78be2fe --- /dev/null +++ b/sys/compat/linuxkpi/common/include/net/mac80211.h @@ -0,0 +1,2028 @@ +/*- + * Copyright (c) 2020-2021 The FreeBSD Foundation + * Copyright (c) 2020-2021 Bjoern A. Zeeb + * + * This software was developed by Björn Zeeb under sponsorship from + * the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _LINUXKPI_NET_MAC80211_H +#define _LINUXKPI_NET_MAC80211_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define ARPHRD_IEEE80211_RADIOTAP __LINE__ /* XXX TODO brcmfmac */ + +#define WLAN_OUI_MICROSOFT (0x0050F2) +#define WLAN_OUI_TYPE_MICROSOFT_TPC (8) +#define WLAN_OUI_TYPE_WFA_P2P (9) +#define WLAN_OUI_WFA (0x506F9A) + +/* hw->conf.flags */ +enum ieee80211_hw_conf_flags { + IEEE80211_CONF_IDLE = BIT(0), + IEEE80211_CONF_PS = BIT(1), +}; + +/* (*ops->config()) */ +enum ieee80211_hw_conf_changed_flags { + IEEE80211_CONF_CHANGE_CHANNEL = BIT(0), + IEEE80211_CONF_CHANGE_IDLE = BIT(1), + IEEE80211_CONF_CHANGE_PS = BIT(2), +}; + +#define CFG80211_TESTMODE_CMD(_x) /* XXX TODO */ + +#define FCS_LEN 4 + +/* ops.configure_filter() */ +enum mcast_filter_flags { + FIF_ALLMULTI = BIT(0), + FIF_PROBE_REQ = BIT(1), + FIF_BCN_PRBRESP_PROMISC = BIT(2), + FIF_FCSFAIL = BIT(3), + FIF_OTHER_BSS = BIT(4), +}; + +enum ieee80211_bss_changed { + BSS_CHANGED_ARP_FILTER = BIT(0), + BSS_CHANGED_ASSOC = BIT(1), + BSS_CHANGED_BANDWIDTH = BIT(2), + BSS_CHANGED_BEACON = BIT(3), + BSS_CHANGED_BEACON_ENABLED = BIT(4), + BSS_CHANGED_BEACON_INFO = BIT(5), + BSS_CHANGED_BEACON_INT = BIT(6), + BSS_CHANGED_BSSID = BIT(7), + BSS_CHANGED_CQM = BIT(8), + BSS_CHANGED_ERP_CTS_PROT = BIT(9), + BSS_CHANGED_ERP_SLOT = BIT(10), + BSS_CHANGED_FTM_RESPONDER = BIT(11), + BSS_CHANGED_HT = BIT(12), + BSS_CHANGED_IDLE = BIT(13), + BSS_CHANGED_MU_GROUPS = BIT(14), + BSS_CHANGED_P2P_PS = BIT(15), + BSS_CHANGED_PS = BIT(16), + BSS_CHANGED_QOS = BIT(17), + BSS_CHANGED_TXPOWER = BIT(18), +}; + +/* 802.11 Figure 9-256 Suite selector format. [OUI(3), SUITE TYPE(1)] */ +#define WLAN_CIPHER_SUITE_OUI(_oui, _x) ((_oui) << 8 | (_x) & 0xff) + +/* 802.11 Table 9-131 Cipher suite selectors. */ +/* 802.1x suite B 11 */ +#define WLAN_CIPHER_SUITE(_x) WLAN_CIPHER_SUITE_OUI(0x000fac, _x) +/* Use group 0 */ +#define WLAN_CIPHER_SUITE_WEP40 WLAN_CIPHER_SUITE(1) +#define WLAN_CIPHER_SUITE_TKIP WLAN_CIPHER_SUITE(2) +/* Reserved 3 */ +#define WLAN_CIPHER_SUITE_CCMP WLAN_CIPHER_SUITE(4) /* CCMP-128 */ +#define WLAN_CIPHER_SUITE_WEP104 WLAN_CIPHER_SUITE(5) +#define WLAN_CIPHER_SUITE_AES_CMAC WLAN_CIPHER_SUITE(6) /* BIP-CMAC-128 */ +/* Group addressed traffic not allowed 7 */ +#define WLAN_CIPHER_SUITE_GCMP WLAN_CIPHER_SUITE(8) +#define WLAN_CIPHER_SUITE_GCMP_256 WLAN_CIPHER_SUITE(9) +#define WLAN_CIPHER_SUITE_CCMP_256 WLAN_CIPHER_SUITE(10) +#define WLAN_CIPHER_SUITE_BIP_GMAC_128 WLAN_CIPHER_SUITE(11) +#define WLAN_CIPHER_SUITE_BIP_GMAC_256 WLAN_CIPHER_SUITE(12) +#define WLAN_CIPHER_SUITE_BIP_CMAC_256 WLAN_CIPHER_SUITE(13) +/* Reserved 14-255 */ + + +/* 802.11 Table 9-133 AKM suite selectors. */ +#define WLAN_AKM_SUITE(_x) WLAN_CIPHER_SUITE_OUI(0x000fac, _x) +/* Reserved 0 */ +#define WLAN_AKM_SUITE_8021X WLAN_AKM_SUITE(1) +#define WLAN_AKM_SUITE_PSK WLAN_AKM_SUITE(2) +#define WLAN_AKM_SUITE_FT_8021X WLAN_AKM_SUITE(3) +#define WLAN_AKM_SUITE_FT_PSK WLAN_AKM_SUITE(4) +#define WLAN_AKM_SUITE_8021X_SHA256 WLAN_AKM_SUITE(5) +#define WLAN_AKM_SUITE_PSK_SHA256 WLAN_AKM_SUITE(6) +/* TDLS 7 */ +#define WLAN_AKM_SUITE_SAE WLAN_AKM_SUITE(8) +/* FToSAE 9 */ +/* AP peer key 10 */ +/* 802.1x suite B 11 */ +/* 802.1x suite B 384 12 */ +/* FTo802.1x 384 13 */ +/* Reserved 14-255 */ +/* Apparently 11ax defines more. Seen (19,20) mentioned. */ + + +struct ieee80211_sta; + +struct ieee80211_ampdu_params { + /* TODO FIXME */ + struct ieee80211_sta *sta; + uint8_t tid; + uint16_t ssn; + int action, amsdu, buf_size, timeout; +}; + +struct ieee80211_bar { + /* TODO FIXME */ + int control, start_seq_num; + uint8_t *ra; + uint16_t frame_control; +}; + +struct ieee80211_p2p_noa_attr { + /* TODO FIXME */ + int oppps_ctwindow; + int desc, index; +}; + +#define WLAN_MEMBERSHIP_LEN (8) +#define WLAN_USER_POSITION_LEN (16) + +struct ieee80211_bss_conf { + /* TODO FIXME */ + uint8_t bssid[ETH_ALEN]; + uint8_t transmitter_bssid[ETH_ALEN]; + struct ieee80211_ftm_responder_params *ftmr_params; + struct ieee80211_p2p_noa_attr p2p_noa_attr; + struct cfg80211_chan_def chandef; + __be32 arp_addr_list[1]; /* XXX TODO */ + struct ieee80211_rate *beacon_rate; + struct { + uint8_t membership[WLAN_MEMBERSHIP_LEN]; + uint8_t position[WLAN_USER_POSITION_LEN]; + } mu_group; + struct { + int color; + } he_bss_color; + size_t ssid_len; + uint8_t ssid[IEEE80211_NWID_LEN]; + uint16_t aid; + uint16_t beacon_int; + uint16_t ht_operation_mode; + int arp_addr_cnt; + uint8_t dtim_period; + bool assoc; + bool idle; + bool qos; + bool ps; + bool twt_broadcast; + bool use_cts_prot; + bool use_short_preamble; + bool use_short_slot; + + int txpower; + int ack_enabled, bssid_index, bssid_indicator, cqm_rssi_hyst, cqm_rssi_thold, ema_ap, frame_time_rts_th, ftm_responder; + int htc_trig_based_pkt_ext; + int multi_sta_back_32bit, nontransmitted; + int profile_periodicity, sync_device_ts, sync_dtim_count, sync_tsf; + int twt_requester, uora_exists, uora_ocw_range; + int assoc_capability, enable_beacon, hidden_ssid, ibss_joined, mcast_rate, twt_protected; + unsigned long basic_rates; + bool he_support; +}; + +struct ieee80211_chanctx_conf { + /* TODO FIXME */ + int rx_chains_dynamic, rx_chains_static; + bool radar_enabled; + struct cfg80211_chan_def def; + struct cfg80211_chan_def min_def; + + /* Must stay last. */ + uint8_t drv_priv[0] __aligned(CACHE_LINE_SIZE); +}; + +struct ieee80211_channel_switch { + /* TODO FIXME */ + int block_tx, count, delay, device_timestamp, timestamp; + struct cfg80211_chan_def chandef; +}; + +struct ieee80211_cipher_scheme { + uint32_t cipher; + uint8_t iftype; /* We do not know the size of this. */ + uint8_t hdr_len; + uint8_t pn_len; + uint8_t pn_off; + uint8_t key_idx_off; + uint8_t key_idx_mask; + uint8_t key_idx_shift; + uint8_t mic_len; +}; + +enum ieee80211_event_type { + BA_FRAME_TIMEOUT, + BAR_RX_EVENT, + MLME_EVENT, + RSSI_EVENT, +}; + +enum ieee80211_rssi_event_data { + RSSI_EVENT_LOW, + RSSI_EVENT_HIGH, +}; + +enum ieee80211_mlme_event_data { + ASSOC_EVENT, + AUTH_EVENT, + DEAUTH_RX_EVENT, + DEAUTH_TX_EVENT, +}; + +enum ieee80211_mlme_event_status { + MLME_DENIED, + MLME_TIMEOUT, +}; + +struct ieee80211_mlme_event { + enum ieee80211_mlme_event_data data; + enum ieee80211_mlme_event_status status; + int reason; +}; + +struct ieee80211_event { + /* TODO FIXME */ + enum ieee80211_event_type type; + union { + struct { + int ssn; + struct ieee80211_sta *sta; + uint8_t tid; + } ba; + struct ieee80211_mlme_event mlme; + } u; +}; + +struct ieee80211_ftm_responder_params { + /* TODO FIXME */ + uint8_t *lci; + uint8_t *civicloc; + int lci_len; + int civicloc_len; +}; + +struct ieee80211_he_mu_edca_param_ac_rec { + /* TODO FIXME */ + int aifsn, ecw_min_max, mu_edca_timer; +}; + +enum ieee80211_hw_flags { + IEEE80211_HW_AMPDU_AGGREGATION, + IEEE80211_HW_AP_LINK_PS, + IEEE80211_HW_BUFF_MMPDU_TXQ, + IEEE80211_HW_CHANCTX_STA_CSA, + IEEE80211_HW_CONNECTION_MONITOR, + IEEE80211_HW_DEAUTH_NEED_MGD_TX_PREP, + IEEE80211_HW_HAS_RATE_CONTROL, + IEEE80211_HW_MFP_CAPABLE, + IEEE80211_HW_NEEDS_UNIQUE_STA_ADDR, + IEEE80211_HW_REPORTS_TX_ACK_STATUS, + IEEE80211_HW_RX_INCLUDES_FCS, + IEEE80211_HW_SIGNAL_DBM, + IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS, + IEEE80211_HW_SPECTRUM_MGMT, + IEEE80211_HW_STA_MMPDU_TXQ, + IEEE80211_HW_SUPPORTS_AMSDU_IN_AMPDU, + IEEE80211_HW_SUPPORTS_CLONED_SKBS, + IEEE80211_HW_SUPPORTS_DYNAMIC_PS, + IEEE80211_HW_SUPPORTS_MULTI_BSSID, + IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID, + IEEE80211_HW_SUPPORTS_PS, + IEEE80211_HW_SUPPORTS_REORDERING_BUFFER, + IEEE80211_HW_SUPPORTS_VHT_EXT_NSS_BW, + IEEE80211_HW_SUPPORT_FAST_XMIT, + IEEE80211_HW_TDLS_WIDER_BW, + IEEE80211_HW_TIMING_BEACON_ONLY, + IEEE80211_HW_TX_AMPDU_SETUP_IN_HW, + IEEE80211_HW_TX_AMSDU, + IEEE80211_HW_TX_FRAG_LIST, + IEEE80211_HW_USES_RSS, + IEEE80211_HW_WANT_MONITOR_VIF, + + /* Keep last. */ + NUM_IEEE80211_HW_FLAGS +}; + +struct ieee80211_hw { + + struct wiphy *wiphy; + + /* TODO FIXME */ + int max_rx_aggregation_subframes, max_tx_aggregation_subframes; + int extra_tx_headroom, weight_multiplier; + int max_rate_tries, max_rates, max_report_rates; + struct ieee80211_cipher_scheme *cipher_schemes; + int n_cipher_schemes; + const char *rate_control_algorithm; + struct { + uint16_t units_pos; /* radiotap "spec" is .. inconsistent. */ + uint16_t accuracy; + } radiotap_timestamp; + size_t sta_data_size; + size_t vif_data_size; + size_t chanctx_data_size; + size_t txq_data_size; + uint16_t radiotap_mcs_details; + uint16_t radiotap_vht_details; + uint16_t queues; + uint16_t offchannel_tx_hw_queue; + uint16_t uapsd_max_sp_len; + uint16_t uapsd_queues; + uint16_t max_tx_fragments; + uint16_t max_listen_interval; + netdev_features_t netdev_features; + unsigned long flags[BITS_TO_LONGS(NUM_IEEE80211_HW_FLAGS)]; + struct { + uint32_t listen_interval; + enum ieee80211_hw_conf_flags flags; + struct cfg80211_chan_def chandef; + } conf; + +#if 0 /* leave here for documentation purposes. This does NOT work. */ + /* Must stay last. */ + uint8_t priv[0] __aligned(CACHE_LINE_SIZE); +#else + void *priv; +#endif +}; + +enum ieee802111_key_flag { + IEEE80211_KEY_FLAG_GENERATE_IV = BIT(0), + IEEE80211_KEY_FLAG_GENERATE_MMIC = BIT(1), + IEEE80211_KEY_FLAG_PAIRWISE = BIT(2), + IEEE80211_KEY_FLAG_PUT_IV_SPACE = BIT(3), + IEEE80211_KEY_FLAG_PUT_MIC_SPACE = BIT(4), + IEEE80211_KEY_FLAG_SW_MGMT_TX = BIT(5), +}; + +struct ieee80211_key_conf { + atomic64_t tx_pn; + uint32_t cipher; + uint8_t icv_len; /* __unused nowadays? */ + uint8_t iv_len; + uint8_t hw_key_idx; /* Set by drv. */ + uint8_t keyidx; + uint16_t flags; + uint8_t keylen; + uint8_t key[0]; +}; + +struct ieee80211_key_seq { + /* TODO FIXME */ + union { + struct { + uint8_t seq[IEEE80211_MAX_PN_LEN]; + uint8_t seq_len; + } hw; + struct { + uint8_t pn[IEEE80211_CCMP_PN_LEN]; + } ccmp; + struct { + uint8_t pn[IEEE80211_CCMP_PN_LEN]; + } aes_cmac; + struct { + uint32_t iv32; + uint16_t iv16; + } tkip; + }; +}; + + +struct ieee80211_p2p_noa_desc { + /* TODO FIXME */ +}; + +enum ieee80211_rx_status_flags { + RX_FLAG_ALLOW_SAME_PN = BIT(0), + RX_FLAG_AMPDU_DETAILS = BIT(1), + RX_FLAG_AMPDU_EOF_BIT = BIT(2), + RX_FLAG_AMPDU_EOF_BIT_KNOWN = BIT(3), + RX_FLAG_DECRYPTED = BIT(4), + RX_FLAG_DUP_VALIDATED = BIT(5), + RX_FLAG_FAILED_FCS_CRC = BIT(6), + RX_FLAG_ICV_STRIPPED = BIT(7), + RX_FLAG_MACTIME_PLCP_START = BIT(8), + RX_FLAG_MACTIME_START = BIT(9), + RX_FLAG_MIC_STRIPPED = BIT(10), + RX_FLAG_MMIC_ERROR = BIT(11), + RX_FLAG_MMIC_STRIPPED = BIT(12), + RX_FLAG_NO_PSDU = BIT(13), + RX_FLAG_PN_VALIDATED = BIT(14), + RX_FLAG_RADIOTAP_HE = BIT(15), + RX_FLAG_RADIOTAP_HE_MU = BIT(16), + RX_FLAG_RADIOTAP_LSIG = BIT(17), + RX_FLAG_RADIOTAP_VENDOR_DATA = BIT(18), + RX_FLAG_NO_SIGNAL_VAL = BIT(19), +}; + +struct ieee80211_rx_status { + /* TODO FIXME, this is too large. Over-reduce types to u8 where possible. */ + u8 boottime_ns; + u8 mactime; + u8 device_timestamp; + enum ieee80211_rx_status_flags flag; + u16 freq; + u8 bw; +#define RATE_INFO_BW_20 0x01 +#define RATE_INFO_BW_40 0x02 +#define RATE_INFO_BW_80 0x04 +#define RATE_INFO_BW_160 0x08 +#define RATE_INFO_BW_HE_RU 0x10 + u8 encoding; +#define RX_ENC_HE 0x01 +#define RX_ENC_HT 0x02 +#define RX_ENC_VHT 0x04 + u8 ampdu_reference; + u8 band; + u8 chains; + u8 chain_signal[3]; + u8 signal; + u8 enc_flags; + u8 he_dcm; + u8 he_gi; + u8 he_ru; + u8 zero_length_psdu_type; + uint8_t nss; + uint8_t rate_idx; +}; + +struct ieee80211_scan_ies { + /* TODO FIXME */ + int common_ie_len; + int len[NUM_NL80211_BANDS]; + uint8_t *common_ies; + uint8_t *ies[NUM_NL80211_BANDS]; +}; + +struct ieee80211_scan_request { + struct ieee80211_scan_ies ies; + struct cfg80211_scan_request req; +}; + +struct ieee80211_txq { + struct ieee80211_sta *sta; + struct ieee80211_vif *vif; + int ac; + uint8_t tid; + + /* Must stay last. */ + uint8_t drv_priv[0] __aligned(CACHE_LINE_SIZE); +}; + +struct ieee80211_sta_rates { + /* XXX TODO */ + /* XXX some _rcu thing */ + struct { + int idx; + int flags; + } rate[1]; /* XXX what is the real number? */ +}; + +#define IEEE80211_NUM_TIDS 16 /* net80211::WME_NUM_TID */ +struct ieee80211_sta { + /* TODO FIXME */ + int max_amsdu_len, max_amsdu_subframes, max_rc_amsdu_len, max_sp; + int mfp, rx_nss, smps_mode, tdls, tdls_initiator, uapsd_queues, wme, txpwr; + enum ieee80211_sta_rx_bw bandwidth; + struct ieee80211_sta_ht_cap ht_cap; + struct ieee80211_sta_vht_cap vht_cap; + struct ieee80211_sta_he_cap he_cap; + struct ieee80211_sta_he_6ghz_capa he_6ghz_capa; + struct ieee80211_txq *txq[IEEE80211_NUM_TIDS + 1]; /* iwlwifi: 8 and adds +1 to tid_data, net80211::IEEE80211_TID_SIZE */ + struct ieee80211_sta_rates *rates; /* some rcu thing? */ + uint32_t max_tid_amsdu_len[IEEE80211_NUM_TIDS]; + uint32_t supp_rates[NUM_NL80211_BANDS]; + uint8_t addr[ETH_ALEN]; + uint16_t aid; + + /* Must stay last. */ + uint8_t drv_priv[0] __aligned(CACHE_LINE_SIZE); +}; + +struct ieee80211_tdls_ch_sw_params { + /* TODO FIXME */ + int action_code, ch_sw_tm_ie, status, switch_time, switch_timeout, timestamp; + struct ieee80211_sta *sta; + struct cfg80211_chan_def *chandef; + struct sk_buff *tmpl_skb; +}; + +struct ieee80211_tx_control { + /* TODO FIXME */ + struct ieee80211_sta *sta; +}; + +struct ieee80211_tx_queue_params { + /* These types are based on iwlwifi FW structs. */ + uint16_t cw_min; + uint16_t cw_max; + uint16_t txop; + uint8_t aifs; + + /* TODO FIXME */ + int acm, mu_edca, uapsd; + struct ieee80211_he_mu_edca_param_ac_rec mu_edca_param_rec; +}; + +struct ieee80211_tx_rate { + uint8_t idx; + uint16_t count:5, + flags:11; +}; + +enum ieee80211_vif_driver_flags { + IEEE80211_VIF_BEACON_FILTER = BIT(0), + IEEE80211_VIF_SUPPORTS_CQM_RSSI = BIT(1), + IEEE80211_VIF_SUPPORTS_UAPSD = BIT(2), +}; + +struct ieee80211_vif { + /* TODO FIXME */ + enum nl80211_iftype type; + int csa_active, mu_mimo_owner; + int cab_queue, hw_queue; + enum ieee80211_vif_driver_flags driver_flags; + bool p2p; + bool probe_req_reg; + uint8_t addr[ETH_ALEN]; + struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_txq *txq; + struct ieee80211_bss_conf bss_conf; + + /* Must stay last. */ + uint8_t drv_priv[0] __aligned(CACHE_LINE_SIZE); +}; + +struct ieee80211_vif_chanctx_switch { + struct ieee80211_chanctx_conf *old_ctx, *new_ctx; + struct ieee80211_vif *vif; +}; + +struct ieee80211_prep_tx_info { + u16 duration; + bool success; +}; + +/* XXX-BZ too big, over-reduce size to u8, and array sizes to minuimum to fit in skb->cb. */ +/* Also warning: some sizes change by pointer size! This is 64bit only. */ +struct ieee80211_tx_info { + enum ieee80211_tx_info_flags flags; + /* TODO FIXME */ + u8 band; + u8 hw_queue; + bool tx_time_est; + union { + struct { + struct ieee80211_tx_rate rates[4]; + bool use_rts; + struct ieee80211_vif *vif; + struct ieee80211_key_conf *hw_key; + enum ieee80211_tx_control_flags flags; + } control; + struct { + struct ieee80211_tx_rate rates[4]; + uint32_t ack_signal; + uint8_t ampdu_ack_len; + uint8_t ampdu_len; + uint8_t antenna; + uint16_t tx_time; + bool is_valid_ack_signal; + void *status_driver_data[2]; /* XXX TODO */ + } status; + void *driver_data[5]; /* XXX TODO */ + }; +}; + +/* net80211 conflict */ +#ifdef FIXME_TODO +struct ieee80211_tim_ie { + /* TODO FIXME */ + uint8_t dtim_count; + uint8_t dtim_period; + uint8_t bitmap_ctrl; + uint8_t virtual_map; +}; +#endif + +struct survey_info { /* net80211::struct ieee80211_channel_survey */ + /* TODO FIXME */ + uint32_t filled; +#define SURVEY_INFO_TIME 0x0001 +#define SURVEY_INFO_TIME_RX 0x0002 +#define SURVEY_INFO_TIME_SCAN 0x0004 +#define SURVEY_INFO_TIME_TX 0x0008 +#define SURVEY_INFO_TIME_BSS_RX 0x0010 +#define SURVEY_INFO_TIME_BUSY 0x0020 +#define SURVEY_INFO_IN_USE 0x0040 +#define SURVEY_INFO_NOISE_DBM 0x0080 + uint32_t noise; + uint64_t time; + uint64_t time_bss_rx; + uint64_t time_busy; + uint64_t time_rx; + uint64_t time_scan; + uint64_t time_tx; + struct ieee80211_channel *channel; +}; + +enum ieee80211_iface_iter { + IEEE80211_IFACE_ITER_NORMAL = BIT(0), + IEEE80211_IFACE_ITER_RESUME_ALL = BIT(1), + + /* Internal flags only. */ + /* ieee80211_iterate_active_interfaces*(). */ + IEEE80211_IFACE_ITER__ATOMIC = BIT(6), + IEEE80211_IFACE_ITER__ACTIVE = BIT(7), +}; + +enum set_key_cmd { + SET_KEY, + DISABLE_KEY, +}; + +enum rx_enc_flags { + RX_ENC_FLAG_SHORTPRE = BIT(0), + RX_ENC_FLAG_SHORT_GI = BIT(1), + RX_ENC_FLAG_HT_GF = BIT(2), + RX_ENC_FLAG_LDPC = BIT(3), + RX_ENC_FLAG_BF = BIT(4), +#define RX_ENC_FLAG_STBC_SHIFT 6 +}; + +enum sta_notify_cmd { + STA_NOTIFY_AWAKE, + STA_NOTIFY_SLEEP, +}; + +struct ieee80211_ops { + /* TODO FIXME */ + int (*start)(struct ieee80211_hw *); + void (*stop)(struct ieee80211_hw *); + + int (*config)(struct ieee80211_hw *, u32); + void (*reconfig_complete)(struct ieee80211_hw *, enum ieee80211_reconfig_type); + + int (*add_interface)(struct ieee80211_hw *, struct ieee80211_vif *); + void (*remove_interface)(struct ieee80211_hw *, struct ieee80211_vif *); + int (*change_interface)(struct ieee80211_hw *, struct ieee80211_vif *, enum nl80211_iftype, bool); + + void (*sw_scan_start)(struct ieee80211_hw *, struct ieee80211_vif *, const u8 *); + void (*sw_scan_complete)(struct ieee80211_hw *, struct ieee80211_vif *); + int (*sched_scan_start)(struct ieee80211_hw *, struct ieee80211_vif *, struct cfg80211_sched_scan_request *, struct ieee80211_scan_ies *); + int (*sched_scan_stop)(struct ieee80211_hw *, struct ieee80211_vif *); + int (*hw_scan)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_scan_request *); + void (*cancel_hw_scan)(struct ieee80211_hw *, struct ieee80211_vif *); + + int (*conf_tx)(struct ieee80211_hw *, struct ieee80211_vif *, u16, const struct ieee80211_tx_queue_params *); + void (*tx)(struct ieee80211_hw *, struct ieee80211_tx_control *, struct sk_buff *); + int (*tx_last_beacon)(struct ieee80211_hw *); + void (*wake_tx_queue)(struct ieee80211_hw *, struct ieee80211_txq *); + + void (*mgd_prepare_tx)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_prep_tx_info *); + void (*mgd_complete_tx)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_prep_tx_info *); + void (*mgd_protect_tdls_discover)(struct ieee80211_hw *, struct ieee80211_vif *); + + void (*flush)(struct ieee80211_hw *, struct ieee80211_vif *, u32, bool); + + int (*set_frag_threshold)(struct ieee80211_hw *, u32); + + void (*sync_rx_queues)(struct ieee80211_hw *); + + void (*allow_buffered_frames)(struct ieee80211_hw *, struct ieee80211_sta *, u16, int, enum ieee80211_frame_release_type, bool); + void (*release_buffered_frames)(struct ieee80211_hw *, struct ieee80211_sta *, u16, int, enum ieee80211_frame_release_type, bool); + + int (*sta_add)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *); + int (*sta_remove)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *); + int (*sta_set_txpwr)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *); + void (*sta_statistics)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *, struct station_info *); + void (*sta_pre_rcu_remove)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *); + int (*sta_state)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *, enum ieee80211_sta_state, enum ieee80211_sta_state); + void (*sta_notify)(struct ieee80211_hw *, struct ieee80211_vif *, enum sta_notify_cmd, struct ieee80211_sta *); + void (*sta_rc_update)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *, u32); + void (*sta_rate_tbl_update)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *); + + u64 (*prepare_multicast)(struct ieee80211_hw *, struct netdev_hw_addr_list *); + + int (*ampdu_action)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_ampdu_params *); + + bool (*can_aggregate_in_amsdu)(struct ieee80211_hw *, struct sk_buff *, struct sk_buff *); + + int (*pre_channel_switch)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_channel_switch *); + int (*post_channel_switch)(struct ieee80211_hw *, struct ieee80211_vif *); + void (*channel_switch)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_channel_switch *); + void (*abort_channel_switch)(struct ieee80211_hw *, struct ieee80211_vif *); + void (*channel_switch_rx_beacon)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_channel_switch *); + int (*tdls_channel_switch)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *, u8, struct cfg80211_chan_def *, struct sk_buff *, u32); + void (*tdls_cancel_channel_switch)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_sta *); + void (*tdls_recv_channel_switch)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_tdls_ch_sw_params *); + + int (*add_chanctx)(struct ieee80211_hw *, struct ieee80211_chanctx_conf *); + void (*remove_chanctx)(struct ieee80211_hw *, struct ieee80211_chanctx_conf *); + void (*change_chanctx)(struct ieee80211_hw *, struct ieee80211_chanctx_conf *, u32); + int (*assign_vif_chanctx)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_chanctx_conf *); + void (*unassign_vif_chanctx)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_chanctx_conf *); + int (*switch_vif_chanctx)(struct ieee80211_hw *, struct ieee80211_vif_chanctx_switch *, int, enum ieee80211_chanctx_switch_mode); + + int (*get_antenna)(struct ieee80211_hw *, u32 *, u32 *); + int (*set_antenna)(struct ieee80211_hw *, u32, u32); + + int (*remain_on_channel)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_channel *, int, enum ieee80211_roc_type); + int (*cancel_remain_on_channel)(struct ieee80211_hw *, struct ieee80211_vif *); + + void (*configure_filter)(struct ieee80211_hw *, unsigned int, unsigned int *, u64); + void (*config_iface_filter)(struct ieee80211_hw *, struct ieee80211_vif *, unsigned int, unsigned int); + + void (*bss_info_changed)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_bss_conf *, u32); + int (*set_rts_threshold)(struct ieee80211_hw *, u32); + void (*event_callback)(struct ieee80211_hw *, struct ieee80211_vif *, const struct ieee80211_event *); + int (*get_survey)(struct ieee80211_hw *, int, struct survey_info *); + int (*get_ftm_responder_stats)(struct ieee80211_hw *, struct ieee80211_vif *, struct cfg80211_ftm_responder_stats *); + void (*offset_tsf)(struct ieee80211_hw *, struct ieee80211_vif *, s64); + int (*set_bitrate_mask)(struct ieee80211_hw *, struct ieee80211_vif *, const struct cfg80211_bitrate_mask *); + void (*set_coverage_class)(struct ieee80211_hw *, s16); + int (*set_tim)(struct ieee80211_hw *, struct ieee80211_sta *, bool); + + int (*set_key)(struct ieee80211_hw *, enum set_key_cmd, struct ieee80211_vif *, struct ieee80211_sta *, struct ieee80211_key_conf *); + void (*set_default_unicast_key)(struct ieee80211_hw *, struct ieee80211_vif *, int); + void (*update_tkip_key)(struct ieee80211_hw *, struct ieee80211_vif *, struct ieee80211_key_conf *, struct ieee80211_sta *, u32, u16 *); + + int (*start_pmsr)(struct ieee80211_hw *, struct ieee80211_vif *, struct cfg80211_pmsr_request *); + void (*abort_pmsr)(struct ieee80211_hw *, struct ieee80211_vif *, struct cfg80211_pmsr_request *); + + int (*start_ap)(struct ieee80211_hw *, struct ieee80211_vif *); + void (*stop_ap)(struct ieee80211_hw *, struct ieee80211_vif *); + int (*join_ibss)(struct ieee80211_hw *, struct ieee80211_vif *); + void (*leave_ibss)(struct ieee80211_hw *, struct ieee80211_vif *); + + /* XXX TODO: get_et_sset_count, get_et_stats, get_et_strings */ +}; + + +/* -------------------------------------------------------------------------- */ + +/* linux_80211.c */ +extern const struct cfg80211_ops linuxkpi_mac80211cfgops; + +struct ieee80211_hw *linuxkpi_ieee80211_alloc_hw(size_t, + const struct ieee80211_ops *); +void linuxkpi_ieee80211_iffree(struct ieee80211_hw *); +void linuxkpi_set_ieee80211_dev(struct ieee80211_hw *, char *); +void linuxkpi_ieee80211_ifattach(struct ieee80211_hw *); +void linuxkpi_ieee80211_ifdetach(struct ieee80211_hw *); +struct ieee80211_hw * linuxkpi_wiphy_to_ieee80211_hw(struct wiphy *); +void linuxkpi_ieee80211_iterate_interfaces( + struct ieee80211_hw *hw, enum ieee80211_iface_iter flags, + void(*iterfunc)(void *, uint8_t *, struct ieee80211_vif *), + void *); +void linuxkpi_ieee80211_iterate_keys(struct ieee80211_hw *, + struct ieee80211_vif *, + void(*iterfunc)(struct ieee80211_hw *, struct ieee80211_vif *, + struct ieee80211_sta *, struct ieee80211_key_conf *, void *), + void *); +void linuxkpi_ieee80211_iterate_chan_contexts(struct ieee80211_hw *, + void(*iterfunc)(struct ieee80211_hw *, + struct ieee80211_chanctx_conf *, void *), + void *); +void linuxkpi_ieee80211_iterate_stations_atomic(struct ieee80211_hw *, + void (*iterfunc)(void *, struct ieee80211_sta *), void *); +void linuxkpi_ieee80211_scan_completed(struct ieee80211_hw *, + struct cfg80211_scan_info *); +void linuxkpi_ieee80211_rx(struct ieee80211_hw *, struct sk_buff *, + struct ieee80211_sta *, struct napi_struct *); +uint8_t linuxkpi_ieee80211_get_tid(struct ieee80211_hdr *); +struct ieee80211_sta *linuxkpi_ieee80211_find_sta(struct ieee80211_vif *, + const u8 *); +struct ieee80211_sta *linuxkpi_ieee80211_find_sta_by_ifaddr( + struct ieee80211_hw *, uint8_t *, uint8_t *); +struct sk_buff *linuxkpi_ieee80211_tx_dequeue(struct ieee80211_hw *, + struct ieee80211_txq *); +bool linuxkpi_ieee80211_is_ie_id_in_ie_buf(const u8, const u8 *, size_t); +bool linuxkpi_ieee80211_ie_advance(size_t *, const u8 *, size_t); +void linuxkpi_ieee80211_free_txskb(struct ieee80211_hw *, struct sk_buff *, + int); +void linuxkpi_ieee80211_queue_delayed_work(struct ieee80211_hw *, + struct delayed_work *, int); +void linuxkpi_ieee80211_queue_work(struct ieee80211_hw *, struct work_struct *); +struct sk_buff *linuxkpi_ieee80211_pspoll_get(struct ieee80211_hw *, + struct ieee80211_vif *); +struct sk_buff *linuxkpi_ieee80211_nullfunc_get(struct ieee80211_hw *, + struct ieee80211_vif *, bool); +void linuxkpi_ieee80211_txq_get_depth(struct ieee80211_txq *, uint64_t *, + uint64_t *); +struct wireless_dev *linuxkpi_ieee80211_vif_to_wdev(struct ieee80211_vif *); +void linuxkpi_ieee80211_connection_loss(struct ieee80211_vif *); + +/* -------------------------------------------------------------------------- */ + +static __inline void +_ieee80211_hw_set(struct ieee80211_hw *hw, enum ieee80211_hw_flags flag) +{ + + set_bit(flag, hw->flags); +} + +static __inline bool +__ieee80211_hw_check(struct ieee80211_hw *hw, enum ieee80211_hw_flags flag) +{ + + return (test_bit(flag, hw->flags)); +} + +/* They pass in shortened flag names; how confusingly inconsistent. */ +#define ieee80211_hw_set(_hw, _flag) \ + _ieee80211_hw_set(_hw, IEEE80211_HW_ ## _flag) +#define ieee80211_hw_check(_hw, _flag) \ + __ieee80211_hw_check(_hw, IEEE80211_HW_ ## _flag) + +/* XXX-BZ add CTASSERTS that size of struct is <= sizeof skb->cb. */ +CTASSERT(sizeof(struct ieee80211_tx_info) <= sizeof(((struct sk_buff *)0)->cb)); +#define IEEE80211_SKB_CB(_skb) \ + ((struct ieee80211_tx_info *)((_skb)->cb)) + +CTASSERT(sizeof(struct ieee80211_rx_status) <= sizeof(((struct sk_buff *)0)->cb)); +#define IEEE80211_SKB_RXCB(_skb) \ + ((struct ieee80211_rx_status *)((_skb)->cb)) + +static __inline void +ieee80211_free_hw(struct ieee80211_hw *hw) +{ + + linuxkpi_ieee80211_iffree(hw); + + if (hw->wiphy != NULL) + wiphy_free(hw->wiphy); + /* Note that *hw is not valid any longer after this. */ + + IMPROVE(); +} + +static __inline struct ieee80211_hw * +ieee80211_alloc_hw(size_t priv_len, const struct ieee80211_ops *ops) +{ + + return (linuxkpi_ieee80211_alloc_hw(priv_len, ops)); +} + +static __inline void +SET_IEEE80211_DEV(struct ieee80211_hw *hw, struct device *dev) +{ + + set_wiphy_dev(hw->wiphy, dev); + linuxkpi_set_ieee80211_dev(hw, dev_name(dev)); + + IMPROVE(); +} + +static __inline int +ieee80211_register_hw(struct ieee80211_hw *hw) +{ + int error; + + error = wiphy_register(hw->wiphy); + if (error != 0) + return (error); + + /* + * At this point the driver has set all the options, flags, bands, + * ciphers, hw address(es), ... basically mac80211/cfg80211 hw/wiphy + * setup is done. + * We need to replicate a lot of information from here into net80211. + */ + linuxkpi_ieee80211_ifattach(hw); + + IMPROVE(); + + return (0); +} + +static __inline void +ieee80211_unregister_hw(struct ieee80211_hw *hw) +{ + + wiphy_unregister(hw->wiphy); + linuxkpi_ieee80211_ifdetach(hw); + + IMPROVE(); +} + +static __inline struct ieee80211_hw * +wiphy_to_ieee80211_hw(struct wiphy *wiphy) +{ + + return (linuxkpi_wiphy_to_ieee80211_hw(wiphy)); +} + +/* -------------------------------------------------------------------------- */ + +static __inline bool +ieee80211_is_action(__le16 fc) +{ + __le16 v; + + fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); + v = htole16(IEEE80211_FC0_SUBTYPE_ACTION | IEEE80211_FC0_TYPE_MGT); + + return (fc == v); +} + +static __inline bool +ieee80211_is_probe_resp(__le16 fc) +{ + __le16 v; + + fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); + v = htole16(IEEE80211_FC0_SUBTYPE_PROBE_RESP | IEEE80211_FC0_TYPE_MGT); + + return (fc == v); +} + +static __inline bool +ieee80211_is_auth(__le16 fc) +{ + __le16 v; + + fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); + v = htole16(IEEE80211_FC0_SUBTYPE_AUTH | IEEE80211_FC0_TYPE_MGT); + + return (fc == v); +} + +static __inline bool +ieee80211_is_assoc_req(__le16 fc) +{ + __le16 v; + + fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); + v = htole16(IEEE80211_FC0_SUBTYPE_ASSOC_REQ | IEEE80211_FC0_TYPE_MGT); + + return (fc == v); +} + +static __inline bool +ieee80211_is_assoc_resp(__le16 fc) +{ + __le16 v; + + fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); + v = htole16(IEEE80211_FC0_SUBTYPE_ASSOC_RESP | IEEE80211_FC0_TYPE_MGT); + + return (fc == v); +} + +static __inline bool +ieee80211_is_reassoc_req(__le16 fc) +{ + __le16 v; + + fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); + v = htole16(IEEE80211_FC0_SUBTYPE_REASSOC_REQ | IEEE80211_FC0_TYPE_MGT); + + return (fc == v); +} + +static __inline bool +ieee80211_is_reassoc_resp(__le16 fc) +{ + __le16 v; + + fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); + v = htole16(IEEE80211_FC0_SUBTYPE_REASSOC_RESP | IEEE80211_FC0_TYPE_MGT); + + return (fc == v); +} + +static __inline bool +ieee80211_is_disassoc(__le16 fc) +{ + __le16 v; + + fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); + v = htole16(IEEE80211_FC0_SUBTYPE_DISASSOC | IEEE80211_FC0_TYPE_MGT); + + return (fc == v); +} + +static __inline bool +ieee80211_is_data_present(__le16 fc) +{ + TODO(); + return (false); +} + +static __inline bool +ieee80211_is_deauth(__le16 fc) +{ + __le16 v; + + fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); + v = htole16(IEEE80211_FC0_SUBTYPE_DEAUTH | IEEE80211_FC0_TYPE_MGT); + + return (fc == v); +} + +static __inline bool +ieee80211_is_beacon(__le16 fc) +{ + __le16 v; + + /* + * For as much as I get it this comes in LE and unlike FreeBSD + * where we get the entire frame header and u8[], here we get the + * 9.2.4.1 Frame Control field only. Mask and compare. + */ + fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); + v = htole16(IEEE80211_FC0_SUBTYPE_BEACON | IEEE80211_FC0_TYPE_MGT); + + return (fc == v); +} + + +static __inline bool +ieee80211_is_probe_req(__le16 fc) +{ + __le16 v; + + fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); + v = htole16(IEEE80211_FC0_SUBTYPE_PROBE_REQ | IEEE80211_FC0_TYPE_MGT); + + return (fc == v); +} + +static __inline bool +ieee80211_has_protected(__le16 fc) +{ + + return (fc & htole16(IEEE80211_FC1_PROTECTED << 8)); +} + +static __inline bool +ieee80211_is_back_req(__le16 fc) +{ + __le16 v; + + fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); + v = htole16(IEEE80211_FC0_SUBTYPE_BAR | IEEE80211_FC0_TYPE_CTL); + + return (fc == v); +} + +static __inline bool +ieee80211_is_bufferable_mmpdu(__le16 fc) +{ + TODO(); + return (false); +} + +static __inline bool +ieee80211_is_nullfunc(__le16 fc) +{ + __le16 v; + + fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); + v = htole16(IEEE80211_FC0_SUBTYPE_NODATA | IEEE80211_FC0_TYPE_DATA); + + return (fc == v); +} + +static __inline bool +ieee80211_is_qos_nullfunc(__le16 fc) +{ + __le16 v; + + fc &= htole16(IEEE80211_FC0_SUBTYPE_MASK | IEEE80211_FC0_TYPE_MASK); + v = htole16(IEEE80211_FC0_SUBTYPE_QOS_NULL | IEEE80211_FC0_TYPE_DATA); + + return (fc == v); +} + +static __inline bool +ieee80211_is_any_nullfunc(__le16 fc) +{ + + return (ieee80211_is_nullfunc(fc) || ieee80211_is_qos_nullfunc(fc)); +} + +static __inline bool +ieee80211_vif_is_mesh(struct ieee80211_vif *vif) +{ + TODO(); + return (false); +} + +static __inline bool +ieee80211_has_a4(__le16 fc) +{ + __le16 v; + + fc &= htole16((IEEE80211_FC1_DIR_TODS | IEEE80211_FC1_DIR_FROMDS) << 8); + v = htole16((IEEE80211_FC1_DIR_TODS | IEEE80211_FC1_DIR_FROMDS) << 8); + + return (fc == v); +} + +static __inline bool +ieee80211_has_order(__le16 fc) +{ + + return (fc & htole16(IEEE80211_FC1_ORDER << 8)); +} + +static __inline bool +ieee80211_has_retry(__le16 fc) +{ + + return (fc & htole16(IEEE80211_FC1_RETRY << 8)); +} + + +static __inline bool +ieee80211_has_fromds(__le16 fc) +{ + + return (fc & htole16(IEEE80211_FC1_DIR_FROMDS << 8)); +} + +static __inline bool +ieee80211_has_tods(__le16 fc) +{ + + return (fc & htole16(IEEE80211_FC1_DIR_TODS << 8)); +} + +static __inline uint8_t * +ieee80211_get_SA(struct ieee80211_hdr *hdr) +{ + + if (ieee80211_has_a4(hdr->frame_control)) + return (hdr->addr4); + if (ieee80211_has_fromds(hdr->frame_control)) + return (hdr->addr3); + return (hdr->addr2); +} + +static __inline uint8_t * +ieee80211_get_DA(struct ieee80211_hdr *hdr) +{ + + if (ieee80211_has_tods(hdr->frame_control)) + return (hdr->addr3); + return (hdr->addr1); +} + +static __inline bool +ieee80211_has_morefrags(__le16 fc) +{ + + fc &= htole16(IEEE80211_FC1_MORE_FRAG << 8); + return (fc != 0); +} + +static __inline u8 * +ieee80211_get_qos_ctl(struct ieee80211_hdr *hdr) +{ + if (ieee80211_has_a4(hdr->frame_control)) + return (u8 *)hdr + 30; + else + return (u8 *)hdr + 24; +} + +/* -------------------------------------------------------------------------- */ +/* Receive functions (air/driver to mac80211/net80211). */ + + +static __inline void +ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *sta, + struct sk_buff *skb, struct napi_struct *napi) +{ + + linuxkpi_ieee80211_rx(hw, skb, sta, napi); +} + +static __inline void +ieee80211_rx_ni(struct ieee80211_hw *hw, struct sk_buff *skb) +{ + + linuxkpi_ieee80211_rx(hw, skb, NULL, NULL); +} + +static __inline void +ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb) +{ + + linuxkpi_ieee80211_rx(hw, skb, NULL, NULL); +} + +/* -------------------------------------------------------------------------- */ + +static __inline uint8_t +ieee80211_get_tid(struct ieee80211_hdr *hdr) +{ + + return (linuxkpi_ieee80211_get_tid(hdr)); +} + +static __inline void +ieee80211_iterate_active_interfaces_atomic(struct ieee80211_hw *hw, + enum ieee80211_iface_iter flags, + void(*iterfunc)(void *, uint8_t *, struct ieee80211_vif *), + void *arg) +{ + + flags |= IEEE80211_IFACE_ITER__ATOMIC; + flags |= IEEE80211_IFACE_ITER__ACTIVE; + linuxkpi_ieee80211_iterate_interfaces(hw, flags, iterfunc, arg); +} + +static __inline void +ieee80211_iterate_active_interfaces(struct ieee80211_hw *hw, + enum ieee80211_iface_iter flags, + void(*iterfunc)(void *, uint8_t *, struct ieee80211_vif *), + void *arg) +{ + + flags |= IEEE80211_IFACE_ITER__ACTIVE; + linuxkpi_ieee80211_iterate_interfaces(hw, flags, iterfunc, arg); +} + +static __inline void +ieee80211_iterate_interfaces(struct ieee80211_hw *hw, + enum ieee80211_iface_iter flags, + void (*iterfunc)(void *, uint8_t *, struct ieee80211_vif *), + void *arg) +{ + + linuxkpi_ieee80211_iterate_interfaces(hw, flags, iterfunc, arg); +} + +static __inline void +ieee80211_iter_keys(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + void(*iterfunc)(struct ieee80211_hw *, struct ieee80211_vif *, + struct ieee80211_sta *, struct ieee80211_key_conf *, void *), + void *arg) +{ + + linuxkpi_ieee80211_iterate_keys(hw, vif, iterfunc, arg); +} + +static __inline void +ieee80211_iter_keys_rcu(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + void(*iterfunc)(struct ieee80211_hw *, struct ieee80211_vif *, + struct ieee80211_sta *, struct ieee80211_key_conf *, void *), + void *arg) +{ + + IMPROVE(); /* "rcu" */ + linuxkpi_ieee80211_iterate_keys(hw, vif, iterfunc, arg); +} + +static __inline void +ieee80211_iter_chan_contexts_atomic(struct ieee80211_hw *hw, + void(*iterfunc)(struct ieee80211_hw *, struct ieee80211_chanctx_conf *, void *), + void *arg) +{ + + linuxkpi_ieee80211_iterate_chan_contexts(hw, iterfunc, arg); +} + +static __inline void +ieee80211_iterate_stations_atomic(struct ieee80211_hw *hw, + void (*iterfunc)(void *, struct ieee80211_sta *), void *arg) +{ + + linuxkpi_ieee80211_iterate_stations_atomic(hw, iterfunc, arg); +} + +static __inline struct wireless_dev * +ieee80211_vif_to_wdev(struct ieee80211_vif *vif) +{ + + return (linuxkpi_ieee80211_vif_to_wdev(vif)); +} + +static __inline struct sk_buff * +ieee80211_beacon_get_template(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, void *p /* XXX TODO */) +{ + TODO(); + return (NULL); +} + +static __inline void +ieee80211_beacon_loss(struct ieee80211_vif *vif) +{ + TODO(); +} + +static __inline void +ieee80211_chswitch_done(struct ieee80211_vif *vif, bool t) +{ + TODO(); +} + +static __inline bool +ieee80211_csa_is_complete(struct ieee80211_vif *vif) +{ + TODO(); + return (false); +} + +static __inline void +ieee80211_csa_set_counter(struct ieee80211_vif *vif, uint8_t counter) +{ + TODO(); +} + +static __inline int +ieee80211_csa_update_counter(struct ieee80211_vif *vif) +{ + TODO(); + return (-1); +} + +static __inline void +ieee80211_csa_finish(struct ieee80211_vif *vif) +{ + TODO(); +} + +static __inline enum nl80211_iftype +ieee80211_vif_type_p2p(struct ieee80211_vif *vif) +{ + + /* If we are not p2p enabled, just return the type. */ + if (!vif->p2p) + return (vif->type); + + /* If we are p2p, depending on side, return type. */ + switch (vif->type) { + case NL80211_IFTYPE_AP: + return (NL80211_IFTYPE_P2P_GO); + case NL80211_IFTYPE_STATION: + return (NL80211_IFTYPE_P2P_CLIENT); + default: + fallthrough; + } + return (vif->type); +} + +static __inline unsigned long +ieee80211_tu_to_usec(unsigned long tu) +{ + + return (tu * IEEE80211_DUR_TU); +} + + +static __inline int +ieee80211_action_contains_tpc(struct sk_buff *skb) +{ + TODO(); + return (0); +} + +static __inline void +ieee80211_connection_loss(struct ieee80211_vif *vif) +{ + + linuxkpi_ieee80211_connection_loss(vif); +} + +static __inline struct ieee80211_sta * +ieee80211_find_sta(struct ieee80211_vif *vif, const u8 *peer) +{ + + return (linuxkpi_ieee80211_find_sta(vif, peer)); +} + +static __inline struct ieee80211_sta * +ieee80211_find_sta_by_ifaddr(struct ieee80211_hw *hw, uint8_t *addr, + uint8_t *ourvifaddr) +{ + + return (linuxkpi_ieee80211_find_sta_by_ifaddr(hw, addr, ourvifaddr)); +} + + +static __inline void +ieee80211_get_tkip_p2k(struct ieee80211_key_conf *keyconf, + struct sk_buff *skb_frag, u8 *key) +{ + TODO(); +} + +static __inline void +ieee80211_get_tkip_rx_p1k(struct ieee80211_key_conf *keyconf, + const u8 *addr, uint32_t iv32, u16 *p1k) +{ + TODO(); +} + +static __inline size_t +ieee80211_ie_split(const u8 *ies, size_t ies_len, + const u8 *ie_ids, size_t ie_ids_len, size_t start) +{ + size_t x; + + x = start; + + /* XXX FIXME, we need to deal with "Element ID Extension" */ + while (x < ies_len) { + + /* Is this IE[s] one of the ie_ids? */ + if (!linuxkpi_ieee80211_is_ie_id_in_ie_buf(ies[x], + ie_ids, ie_ids_len)) + break; + + if (!linuxkpi_ieee80211_ie_advance(&x, ies, ies_len)) + break; + } + + return (x); +} + +static __inline void +ieee80211_request_smps(struct ieee80211_vif *vif, enum ieee80211_smps_mode smps) +{ + + TODO(); +} + +static __inline void +ieee80211_tdls_oper_request(struct ieee80211_vif *vif, uint8_t *addr, + enum nl80211_tdls_operation oper, enum ieee80211_reason_code code, + gfp_t gfp) +{ + TODO(); +} + +static __inline void +ieee80211_stop_queues(struct ieee80211_hw *hw) +{ + TODO(); +} + +static __inline void +ieee80211_wake_queues(struct ieee80211_hw *hw) +{ + TODO(); +} + +static __inline void +wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool state) +{ + TODO(); +} + +static __inline void +ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb) +{ + IMPROVE(); + + /* + * This is called on transmit failure. + * Use a not-so-random random high status error so we can distinguish + * it from normal low values flying around in net80211 ("ETX"). + */ + linuxkpi_ieee80211_free_txskb(hw, skb, 0x455458); +} + +static __inline void +ieee80211_restart_hw(struct ieee80211_hw *hw) +{ + TODO(); +} + +static __inline void +ieee80211_ready_on_channel(struct ieee80211_hw *hw) +{ + TODO(); +/* XXX-BZ We need to see that. */ +} + +static __inline void +ieee80211_remain_on_channel_expired(struct ieee80211_hw *hw) +{ + TODO(); +} + +static __inline void +ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif, + enum nl80211_cqm_rssi_threshold_event crte, int sig, gfp_t gfp) +{ + TODO(); +} + +static __inline void +ieee80211_mark_rx_ba_filtered_frames(struct ieee80211_sta *sta, uint8_t tid, + uint32_t ssn, uint64_t bitmap, uint16_t received_mpdu) +{ + TODO(); +} + +static __inline bool +ieee80211_sn_less(uint16_t sn1, uint16_t sn2) +{ + TODO(); + return (false); +} + +static __inline uint16_t +ieee80211_sn_inc(uint16_t sn) +{ + TODO(); + return (sn + 1); +} + +static __inline uint16_t +ieee80211_sn_add(uint16_t sn, uint16_t a) +{ + TODO(); + return (sn + a); +} + +static __inline void +ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, uint32_t x, uint8_t *addr) +{ + TODO(); +} + +static __inline void +ieee80211_rate_set_vht(struct ieee80211_tx_rate *r, uint32_t f1, uint32_t f2) +{ + TODO(); +} + +static __inline void +ieee80211_reserve_tid(struct ieee80211_sta *sta, uint8_t tid) +{ + TODO(); +} + +static __inline void +ieee80211_unreserve_tid(struct ieee80211_sta *sta, uint8_t tid) +{ + TODO(); +} + +static __inline void +ieee80211_rx_ba_timer_expired(struct ieee80211_vif *vif, uint8_t *addr, + uint8_t tid) +{ + TODO(); +} + +static __inline void +ieee80211_send_eosp_nullfunc(struct ieee80211_sta *sta, uint8_t tid) +{ + TODO(); +} + +static __inline uint16_t +ieee80211_sn_sub(uint16_t sn, uint16_t n) +{ + TODO(); + + return (0); +} + +static __inline void +ieee80211_sta_block_awake(struct ieee80211_hw *hw, struct ieee80211_sta *sta, + bool disable) +{ + TODO(); +} + +static __inline void +ieee80211_sta_ps_transition(struct ieee80211_sta *sta, bool sleeping) +{ + TODO(); +} + +static __inline void +ieee80211_sta_pspoll(struct ieee80211_sta *sta) +{ + TODO(); +} + +static __inline void +ieee80211_sta_uapsd_trigger(struct ieee80211_sta *sta, int ntids) +{ + TODO(); +} + +static __inline void +ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_vif *vif, uint8_t *addr, + uint8_t tid) +{ + TODO(); +} + +static __inline void +ieee80211_tkip_add_iv(u8 *crypto_hdr, struct ieee80211_key_conf *keyconf, + uint64_t pn) +{ + TODO(); +} + +static __inline struct sk_buff * +ieee80211_tx_dequeue(struct ieee80211_hw *hw, struct ieee80211_txq *txq) +{ + + return (linuxkpi_ieee80211_tx_dequeue(hw, txq)); +} + +static __inline void +ieee80211_update_mu_groups(struct ieee80211_vif *vif, uint8_t *ms, uint8_t *up) +{ + TODO(); +} + +static __inline void +ieee80211_sta_set_buffered(struct ieee80211_sta *sta, uint8_t tid, bool t) +{ + TODO(); +} + +static __inline void +ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) +{ + struct ieee80211_tx_info *info; + int status; + + info = IEEE80211_SKB_CB(skb); + + /* XXX-BZ this check is probably over-simplified? */ + /* XXX-BZ but then we have no full feedback in net80211 yet. */ + if (info->flags & IEEE80211_TX_STAT_ACK) + status = 0; /* No error. */ + else + status = 1; +#if 0 + printf("XXX-BZ: %s: hw %p skb %p status %d : flags %#x " + "band %u hw_queue %u tx_time_est %d : " + "rates [ %u %u %#x, %u %u %#x, %u %u %#x, %u %u %#x ] " + "ack_signal %u ampdu_ack_len %u ampdu_len %u antenna %u tx_time %u " + "is_valid_ack_signal %u status_driver_data [ %p %p ]\n", + __func__, hw, skb, status, info->flags, + info->band, info->hw_queue, info->tx_time_est, + info->status.rates[0].idx, info->status.rates[0].count, + info->status.rates[0].flags, + info->status.rates[1].idx, info->status.rates[1].count, + info->status.rates[1].flags, + info->status.rates[2].idx, info->status.rates[2].count, + info->status.rates[2].flags, + info->status.rates[3].idx, info->status.rates[3].count, + info->status.rates[3].flags, + info->status.ack_signal, info->status.ampdu_ack_len, + info->status.ampdu_len, info->status.antenna, + info->status.tx_time, info->status.is_valid_ack_signal, + info->status.status_driver_data[0], + info->status.status_driver_data[1]); +#endif + IMPROVE(); + linuxkpi_ieee80211_free_txskb(hw, skb, status); +} + +static __inline void +ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf, uint8_t tid, + struct ieee80211_key_seq *seq) +{ + TODO(); +} + +static __inline void +ieee80211_sched_scan_results(struct ieee80211_hw *hw) +{ + TODO(); +} + +static __inline void +ieee80211_sta_eosp(struct ieee80211_sta *sta) +{ + TODO(); +} + +static __inline void +ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_vif *vif, uint8_t *addr, + uint8_t tid) +{ + TODO(); +} + +static __inline void +ieee80211_sched_scan_stopped(struct ieee80211_hw *hw) +{ + TODO(); +} + +static __inline void +ieee80211_scan_completed(struct ieee80211_hw *hw, + struct cfg80211_scan_info *info) +{ + + linuxkpi_ieee80211_scan_completed(hw, info); +} + +static __inline struct sk_buff * +ieee80211_beacon_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + TODO(); + return (NULL); +} + +static __inline struct sk_buff * +ieee80211_pspoll_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + + /* Only STA needs this. Otherwise return NULL and panic bad drivers. */ + if (vif->type != NL80211_IFTYPE_STATION) + return (NULL); + + return (linuxkpi_ieee80211_pspoll_get(hw, vif)); +} + +static __inline struct sk_buff * +ieee80211_proberesp_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + TODO(); + return (NULL); +} + +static __inline struct sk_buff * +ieee80211_nullfunc_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + bool qos) +{ + + /* Only STA needs this. Otherwise return NULL and panic bad drivers. */ + if (vif->type != NL80211_IFTYPE_STATION) + return (NULL); + + return (linuxkpi_ieee80211_nullfunc_get(hw, vif, qos)); +} + +static __inline struct sk_buff * +ieee80211_probereq_get(struct ieee80211_hw *hw, uint8_t *addr, + uint8_t *ssid, size_t ssid_len, int _x) +{ + TODO(); + return (NULL); +} + +static __inline void +ieee80211_queue_delayed_work(struct ieee80211_hw *hw, struct delayed_work *w, + int delay) +{ + + linuxkpi_ieee80211_queue_delayed_work(hw, w, delay); +} + +static __inline void +ieee80211_queue_work(struct ieee80211_hw *hw, struct work_struct *w) +{ + + linuxkpi_ieee80211_queue_work(hw, w); +} + +static __inline void +ieee80211_stop_queue(struct ieee80211_hw *hw, uint16_t q) +{ + TODO(); +} + +static __inline void +ieee80211_wake_queue(struct ieee80211_hw *hw, uint16_t q) +{ + TODO(); +} + +static __inline void +ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb) +{ + IMPROVE(); + ieee80211_tx_status(hw, skb); +} + +static __inline int +ieee80211_start_tx_ba_session(struct ieee80211_sta *sta, uint8_t tid, int x) +{ + TODO(); + return (ENXIO); +} + +static __inline void +ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info) +{ + int i; + + /* + * Apparently clearing flags and some other fields is not right. + * Given the function is called "status" we work on that part of + * the union. + */ + for (i = 0; i < nitems(info->status.rates); i++) + info->status.rates[i].count = 0; + /* + * Unclear if ack_signal should be included or not but we clear the + * "valid" bool so this field is no longer valid. + */ + memset(&info->status.ack_signal, 0, sizeof(*info) - + offsetof(struct ieee80211_tx_info, status.ack_signal)); +} + +static __inline void +ieee80211_txq_get_depth(struct ieee80211_txq *txq, uint64_t *frame_cnt, uint64_t *byte_cnt) +{ + + if (frame_cnt == NULL && byte_cnt == NULL) + return; + + linuxkpi_ieee80211_txq_get_depth(txq, frame_cnt, byte_cnt); +} + +static __inline int +rate_lowest_index(struct ieee80211_supported_band *band, + struct ieee80211_sta *sta) +{ + IMPROVE(); + return (0); +} + + +static __inline void +SET_IEEE80211_PERM_ADDR (struct ieee80211_hw *hw, uint8_t *addr) +{ + + ether_addr_copy(hw->wiphy->perm_addr, addr); +} + +static __inline uint8_t * +ieee80211_bss_get_ie(struct cfg80211_bss *bss, uint32_t x) +{ + TODO(); + return (NULL); +} + +static __inline void +ieee80211_report_low_ack(struct ieee80211_sta *sta, int x) +{ + TODO(); +} + +static __inline void +ieee80211_start_rx_ba_session_offl(struct ieee80211_vif *vif, uint8_t *addr, + uint8_t tid) +{ + TODO(); +} + +static __inline void +ieee80211_stop_rx_ba_session_offl(struct ieee80211_vif *vif, uint8_t *addr, + uint8_t tid) +{ + TODO(); +} + +static __inline struct sk_buff * +ieee80211_tx_dequeue_ni(struct ieee80211_hw *hw, struct ieee80211_txq *txq) +{ + TODO(); + return (NULL); +} + +static __inline void +ieee80211_tx_rate_update(struct ieee80211_hw *hw, struct ieee80211_sta *sta, + struct ieee80211_tx_info *info) +{ + TODO(); +} + +static __inline bool +ieee80211_txq_may_transmit(struct ieee80211_hw *hw, struct ieee80211_txq *txq) +{ + TODO(); + return (false); +} + +static __inline struct ieee80211_txq * +ieee80211_next_txq(struct ieee80211_hw *hw, uint32_t ac) +{ + TODO(); + return (NULL); +} + +static __inline void +ieee80211_radar_detected(struct ieee80211_hw *hw) +{ + TODO(); +} + +static __inline void +ieee80211_sta_register_airtime(struct ieee80211_sta *sta, + uint8_t tid, uint32_t duration, int x) +{ + TODO(); +} + + +static __inline void +ieee80211_return_txq(struct ieee80211_hw *hw, + struct ieee80211_txq *txq, bool _t) +{ + TODO(); +} + +static __inline void +ieee80211_txq_schedule_end(struct ieee80211_hw *hw, uint32_t ac) +{ + TODO(); +} + +static __inline void +ieee80211_txq_schedule_start(struct ieee80211_hw *hw, uint32_t ac) +{ + TODO(); +} + +static __inline void +ieee80211_beacon_set_cntdwn(struct ieee80211_vif *vif, u8 counter) +{ + TODO(); +} + +static __inline int +ieee80211_beacon_update_cntdwn(struct ieee80211_vif *vif) +{ + TODO(); + return (-1); +} + +static __inline int +ieee80211_get_vht_max_nss(struct ieee80211_vht_cap *vht_cap, uint32_t chanwidth, + int x, bool t, int nss) +{ + TODO(); + return (-1); +} + +static __inline bool +ieee80211_beacon_cntdwn_is_complete(struct ieee80211_vif *vif) +{ + TODO(); + return (true); +} + +static __inline void +ieee80211_disconnect(struct ieee80211_vif *vif, bool _x) +{ + TODO(); +} + +static __inline const struct ieee80211_sta_he_cap * +ieee80211_get_he_iftype_cap(const struct ieee80211_supported_band *band, + enum nl80211_iftype type) +{ + TODO(); + return (NULL); +} + +static __inline void +ieee80211_key_mic_failure(struct ieee80211_key_conf *key) +{ + TODO(); +} + +static __inline void +ieee80211_key_replay(struct ieee80211_key_conf *key) +{ + TODO(); +} + +#endif /* _LINUXKPI_NET_MAC80211_H */ diff --git a/sys/compat/linuxkpi/common/src/linux_80211.c b/sys/compat/linuxkpi/common/src/linux_80211.c new file mode 100644 index 00000000000..f3899722518 --- /dev/null +++ b/sys/compat/linuxkpi/common/src/linux_80211.c @@ -0,0 +1,3576 @@ +/*- + * Copyright (c) 2020-2021 The FreeBSD Foundation + * Copyright (c) 2020-2021 Bjoern A. Zeeb + * + * This software was developed by Björn Zeeb under sponsorship from + * the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Public functions are called linuxkpi_*(). + * Internal (static) functions are called lkpi_*(). + * + * The internal structures holding metadata over public structures are also + * called lkpi_xxx (usually with a member at the end called xxx). + * Note: we do not replicate the structure names but the general variable names + * for these (e.g., struct hw -> struct lkpi_hw, struct sta -> struct lkpi_sta). + * There are macros to access one from the other. + * We call the internal versions lxxx (e.g., hw -> lhw, sta -> lsta). + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#define LINUXKPI_NET80211 +#include + +#include +#include "linux_80211.h" + +static MALLOC_DEFINE(M_LKPI80211, "lkpi80211", "Linux KPI 80211 compat"); + +/* -------------------------------------------------------------------------- */ +/* These are unrelated to 802.11 sysctl bug debugging during 802.11 work so * + * keep them here rather than in a more general file. */ + +int debug_skb; +SYSCTL_INT(_compat_linuxkpi, OID_AUTO, debug_skb, CTLFLAG_RWTUN, + &debug_skb, 0, "SKB debug level"); + +/* -------------------------------------------------------------------------- */ + +int debug_80211; +SYSCTL_INT(_compat_linuxkpi, OID_AUTO, debug_80211, CTLFLAG_RWTUN, + &debug_80211, 0, "80211 debug Level"); + +#define LINUXKPI_DEBUG_80211 +#ifdef LINUXKPI_DEBUG_80211 +#ifndef D80211_TODO +#define D80211_TODO 0x1 +#endif +#ifndef D80211_IMPROVE +#define D80211_IMPROVE 0x2 +#endif +#define D80211_TRACE 0x10 +#define D80211_TRACEOK 0x20 +#define D80211_TRACE_TX 0x100 +#define D80211_TRACE_TX_DUMP 0x200 +#define D80211_TRACE_RX 0x1000 +#define D80211_TRACE_RX_DUMP 0x2000 +#define D80211_TRACE_RX_BEACONS 0x4000 +#define D80211_TRACEX (D80211_TRACE_TX|D80211_TRACE_RX) +#define D80211_TRACEX_DUMP (D80211_TRACE_TX_DUMP|D80211_TRACE_RX_DUMP) +#define UNIMPLEMENTED if (debug_80211 & D80211_TODO) \ + printf("XXX-TODO %s:%d: UNIMPLEMENTED\n", __func__, __LINE__) +#define TRACEOK() if (debug_80211 & D80211_TRACEOK) \ + printf("XXX-TODO %s:%d: TRACEPOINT\n", __func__, __LINE__) +#else +#define UNIMPLEMENTED do { } while (0) +#define TRACEOK() do { } while (0) +#endif + +/* #define PREP_TX_INFO_DURATION (IEEE80211_TRANS_WAIT * 1000) */ +#ifndef PREP_TX_INFO_DURATION +#define PREP_TX_INFO_DURATION 0 /* Let the driver do its thing. */ +#endif + +/* This is DSAP | SSAP | CTRL | ProtoID/OrgCode{3}. */ +const uint8_t rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; + +const struct cfg80211_ops linuxkpi_mac80211cfgops = { + /* + * XXX TODO need a "glue layer" to link cfg80211 ops to + * mac80211 and to the driver or net80211. + * Can we pass some on 1:1? Need to compare the (*f)(). + */ +}; + +static struct lkpi_sta *lkpi_find_lsta_by_ni(struct lkpi_vif *, + struct ieee80211_node *); +static void lkpi_80211_txq_task(void *, int); +static void lkpi_ieee80211_free_skb_mbuf(void *); + +static enum nl80211_band +lkpi_net80211_chan_to_nl80211_band(struct ieee80211_channel *c) +{ + + if (IEEE80211_IS_CHAN_2GHZ(c)) + return (NL80211_BAND_2GHZ); + else if (IEEE80211_IS_CHAN_5GHZ(c)) + return (NL80211_BAND_5GHZ); +#ifdef __notyet__ + else if () + return (NL80211_BAND_6GHZ); + else if () + return (NL80211_BAND_60GHZ); + else if (IEEE80211_IS_CHAN_GSM(c)) + return (NL80211_BAND_XXX); +#endif + else + panic("%s: unsupported band. c %p flags %#x\n", + __func__, c, c->ic_flags); +} + +static uint32_t +lkpi_nl80211_band_to_net80211_band(enum nl80211_band band) +{ + + /* XXX-BZ this is just silly; net80211 is too convoluted. */ + /* IEEE80211_CHAN_A / _G / .. doesn't really work either. */ + switch (band) { + case NL80211_BAND_2GHZ: + return (IEEE80211_CHAN_2GHZ); + break; + case NL80211_BAND_5GHZ: + return (IEEE80211_CHAN_5GHZ); + break; + case NL80211_BAND_60GHZ: + break; + case NL80211_BAND_6GHZ: + break; + default: + panic("%s: unsupported band %u\n", __func__, band); + break; + } + + IMPROVE(); + return (0x00); +} + +static enum ieee80211_ac_numbers +lkpi_ac_net_to_l80211(int ac) +{ + + switch (ac) { + case WME_AC_VO: + return (IEEE80211_AC_VO); + case WME_AC_VI: + return (IEEE80211_AC_VI); + case WME_AC_BE: + return (IEEE80211_AC_BE); + case WME_AC_BK: + return (IEEE80211_AC_BK); + default: + printf("%s: invalid WME_AC_* input: ac = %d\n", __func__, ac); + return (IEEE80211_AC_BE); + } +} + +static enum nl80211_iftype +lkpi_opmode_to_vif_type(enum ieee80211_opmode opmode) +{ + + switch (opmode) { + case IEEE80211_M_IBSS: + return (NL80211_IFTYPE_ADHOC); + break; + case IEEE80211_M_STA: + return (NL80211_IFTYPE_STATION); + break; + case IEEE80211_M_WDS: + return (NL80211_IFTYPE_WDS); + break; + case IEEE80211_M_HOSTAP: + return (NL80211_IFTYPE_AP); + break; + case IEEE80211_M_MONITOR: + return (NL80211_IFTYPE_MONITOR); + break; + case IEEE80211_M_MBSS: + return (NL80211_IFTYPE_MESH_POINT); + break; + case IEEE80211_M_AHDEMO: + /* FALLTHROUGH */ + default: + printf("ERROR: %s: unsupported opmode %d\n", __func__, opmode); + /* FALLTHROUGH */ + } + return (NL80211_IFTYPE_UNSPECIFIED); +} + +#ifdef __notyet__ +static uint32_t +lkpi_l80211_to_net80211_cyphers(uint32_t wlan_cipher_suite) +{ + + switch (wlan_cipher_suite) { + case WLAN_CIPHER_SUITE_WEP40: + return (IEEE80211_CRYPTO_WEP); + case WLAN_CIPHER_SUITE_TKIP: + return (IEEE80211_CRYPTO_TKIP); + case WLAN_CIPHER_SUITE_CCMP: + return (IEEE80211_CIPHER_AES_CCM); + case WLAN_CIPHER_SUITE_WEP104: + return (IEEE80211_CRYPTO_WEP); + case WLAN_CIPHER_SUITE_AES_CMAC: + case WLAN_CIPHER_SUITE_GCMP: + case WLAN_CIPHER_SUITE_GCMP_256: + case WLAN_CIPHER_SUITE_CCMP_256: + case WLAN_CIPHER_SUITE_BIP_GMAC_128: + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + case WLAN_CIPHER_SUITE_BIP_CMAC_256: + printf("%s: unsupported WLAN Cipher Suite %#08x | %u\n", __func__, + wlan_cipher_suite >> 8, wlan_cipher_suite & 0xff); + break; + default: + printf("%s: unknown WLAN Cipher Suite %#08x | %u\n", __func__, + wlan_cipher_suite >> 8, wlan_cipher_suite & 0xff); + } + + return (0); +} +#endif + +#ifdef TRY_HW_CRYPTO +static uint32_t +lkpi_net80211_to_l80211_cipher_suite(uint32_t cipher, uint8_t keylen) +{ + + switch (cipher) { + case IEEE80211_CIPHER_TKIP: + return (WLAN_CIPHER_SUITE_TKIP); + case IEEE80211_CIPHER_AES_CCM: + return (WLAN_CIPHER_SUITE_CCMP); + case IEEE80211_CIPHER_WEP: + if (keylen < 8) + return (WLAN_CIPHER_SUITE_WEP40); + else + return (WLAN_CIPHER_SUITE_WEP104); + break; + case IEEE80211_CIPHER_AES_OCB: + case IEEE80211_CIPHER_TKIPMIC: + case IEEE80211_CIPHER_CKIP: + case IEEE80211_CIPHER_NONE: + printf("%s: unsupported cipher %#010x\n", __func__, cipher); + break; + default: + printf("%s: unknown cipher %#010x\n", __func__, cipher); + }; + return (0); +} +#endif + +#ifdef __notyet__ +static enum ieee80211_sta_state +lkpi_net80211_state_to_sta_state(enum ieee80211_state state) +{ + + /* + * XXX-BZ The net80211 states are "try to ..", the lkpi8011 states are + * "done". Also ASSOC/AUTHORIZED are both "RUN" then? + */ + switch (state) { + case IEEE80211_S_INIT: + return (IEEE80211_STA_NOTEXIST); + case IEEE80211_S_SCAN: + return (IEEE80211_STA_NONE); + case IEEE80211_S_AUTH: + return (IEEE80211_STA_AUTH); + case IEEE80211_S_ASSOC: + return (IEEE80211_STA_ASSOC); + case IEEE80211_S_RUN: + return (IEEE80211_STA_AUTHORIZED); + case IEEE80211_S_CAC: + case IEEE80211_S_CSA: + case IEEE80211_S_SLEEP: + default: + UNIMPLEMENTED; + }; + + return (IEEE80211_STA_NOTEXIST); +} +#endif + +static struct linuxkpi_ieee80211_channel * +lkpi_find_lkpi80211_chan(struct lkpi_hw *lhw, + struct ieee80211_channel *c) +{ + struct ieee80211_hw *hw; + struct linuxkpi_ieee80211_channel *channels; + enum nl80211_band band; + int i, nchans; + + hw = LHW_TO_HW(lhw); + band = lkpi_net80211_chan_to_nl80211_band(c); + if (hw->wiphy->bands[band] == NULL) + return (NULL); + + nchans = hw->wiphy->bands[band]->n_channels; + if (nchans <= 0) + return (NULL); + + channels = hw->wiphy->bands[band]->channels; + for (i = 0; i < nchans; i++) { + if (channels[i].hw_value == c->ic_ieee) + return (&channels[i]); + } + + return (NULL); +} + +static struct linuxkpi_ieee80211_channel * +lkpi_get_lkpi80211_chan(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + struct linuxkpi_ieee80211_channel *chan; + struct ieee80211_channel *c; + struct lkpi_hw *lhw; + + chan = NULL; + if (ni != NULL && ni->ni_chan != IEEE80211_CHAN_ANYC) + c = ni->ni_chan; + else if (ic->ic_bsschan != IEEE80211_CHAN_ANYC) + c = ic->ic_bsschan; + else if (ic->ic_curchan != IEEE80211_CHAN_ANYC) + c = ic->ic_curchan; + else + c = NULL; + + if (c != NULL && c != IEEE80211_CHAN_ANYC) { + lhw = ic->ic_softc; + chan = lkpi_find_lkpi80211_chan(lhw, c); + } + + return (chan); +} + +#ifdef TRY_HW_CRYPTO +static int +_lkpi_iv_key_set_delete(struct ieee80211vap *vap, const struct ieee80211_key *k, + enum set_key_cmd cmd) +{ + struct ieee80211com *ic; + struct lkpi_hw *lhw; + struct ieee80211_hw *hw; + struct lkpi_vif *lvif; + struct ieee80211_vif *vif; + struct ieee80211_sta *sta; + struct ieee80211_node *ni; + struct ieee80211_key_conf *kc; + int error; + + /* XXX TODO Check (k->wk_flags & IEEE80211_KEY_SWENCRYPT) and don't upload to driver/hw? */ + + ic = vap->iv_ic; + lhw = ic->ic_softc; + hw = LHW_TO_HW(lhw); + lvif = VAP_TO_LVIF(vap); + vif = LVIF_TO_VIF(lvif); + + memset(&kc, 0, sizeof(kc)); + kc = malloc(sizeof(*kc) + k->wk_keylen, M_LKPI80211, M_WAITOK | M_ZERO); + kc->cipher = lkpi_net80211_to_l80211_cipher_suite( + k->wk_cipher->ic_cipher, k->wk_keylen); + kc->keyidx = k->wk_keyix; +#if 0 + kc->hw_key_idx = /* set by hw and needs to be passed for TX */; +#endif + atomic64_set(&kc->tx_pn, k->wk_keytsc); + kc->keylen = k->wk_keylen; + memcpy(kc->key, k->wk_key, k->wk_keylen); + + switch (kc->cipher) { + case WLAN_CIPHER_SUITE_CCMP: + kc->iv_len = k->wk_cipher->ic_header; + kc->icv_len = k->wk_cipher->ic_trailer; + break; + case WLAN_CIPHER_SUITE_TKIP: + default: + IMPROVE(); + return (0); + }; + + ni = vap->iv_bss; + sta = ieee80211_find_sta(vif, ni->ni_bssid); + if (sta != NULL) { + struct lkpi_sta *lsta; + + lsta = STA_TO_LSTA(sta); + lsta->kc = kc; + } + + error = lkpi_80211_mo_set_key(hw, cmd, vif, sta, kc); + if (error != 0) { + /* XXX-BZ leaking kc currently */ + ic_printf(ic, "%s: set_key failed: %d\n", __func__, error); + return (0); + } else { + ic_printf(ic, "%s: set_key succeeded: keyidx %u hw_key_idx %u " + "flags %#10x\n", __func__, + kc->keyidx, kc->hw_key_idx, kc->flags); + return (1); + } +} + +static int +lkpi_iv_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k) +{ + + /* XXX-BZ one day we should replace this iterating over VIFs, or node list? */ + return (_lkpi_iv_key_set_delete(vap, k, DISABLE_KEY)); +} +static int +lkpi_iv_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k) +{ + + return (_lkpi_iv_key_set_delete(vap, k, SET_KEY)); +} +#endif + +static u_int +lkpi_ic_update_mcast_copy(void *arg, struct sockaddr_dl *sdl, u_int cnt) +{ + struct netdev_hw_addr_list *mc_list; + struct netdev_hw_addr *addr; + + KASSERT(arg != NULL && sdl != NULL, ("%s: arg %p sdl %p cnt %u\n", + __func__, arg, sdl, cnt)); + + mc_list = arg; + /* If it is on the list already skip it. */ + netdev_hw_addr_list_for_each(addr, mc_list) { + if (!memcmp(addr->addr, LLADDR(sdl), sdl->sdl_alen)) + return (0); + } + + addr = malloc(sizeof(*addr), M_LKPI80211, M_NOWAIT | M_ZERO); + if (addr == NULL) + return (0); + + INIT_LIST_HEAD(&addr->addr_list); + memcpy(addr->addr, LLADDR(sdl), sdl->sdl_alen); + /* XXX this should be a netdev function? */ + list_add(&addr->addr_list, &mc_list->addr_list); + mc_list->count++; + + if (debug_80211 & D80211_TRACE) + printf("%s:%d: mc_list count %d: added %6D\n", + __func__, __LINE__, mc_list->count, addr->addr, ":"); + + return (1); +} + +static void +lkpi_update_mcast_filter(struct ieee80211com *ic, bool force) +{ + struct lkpi_hw *lhw; + struct ieee80211_hw *hw; + struct netdev_hw_addr_list mc_list; + struct list_head *le, *next; + struct netdev_hw_addr *addr; + struct ieee80211vap *vap; + u64 mc; + unsigned int changed_flags, total_flags; + + lhw = ic->ic_softc; + + if (lhw->ops->prepare_multicast == NULL || + lhw->ops->configure_filter == NULL) + return; + + if (!lhw->update_mc && !force) + return; + + changed_flags = total_flags = 0; + mc_list.count = 0; + INIT_LIST_HEAD(&mc_list.addr_list); + if (ic->ic_allmulti == 0) { + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) + if_foreach_llmaddr(vap->iv_ifp, + lkpi_ic_update_mcast_copy, &mc_list); + } else { + changed_flags |= FIF_ALLMULTI; + } + + hw = LHW_TO_HW(lhw); + mc = lkpi_80211_mo_prepare_multicast(hw, &mc_list); + /* + * XXX-BZ make sure to get this sorted what is a change, + * what gets all set; what was already set? + */ + total_flags = changed_flags; + lkpi_80211_mo_configure_filter(hw, changed_flags, &total_flags, mc); + + if (debug_80211 & D80211_TRACE) + printf("%s: changed_flags %#06x count %d total_flags %#010x\n", + __func__, changed_flags, mc_list.count, total_flags); + + if (mc_list.count != 0) { + list_for_each_safe(le, next, &mc_list.addr_list) { + addr = list_entry(le, struct netdev_hw_addr, addr_list); + free(addr, M_LKPI80211); + mc_list.count--; + } + } + KASSERT(mc_list.count == 0, ("%s: mc_list %p count %d != 0\n", + __func__, &mc_list, mc_list.count)); +} + +const uint8_t tid_to_mac80211_ac[] = { + IEEE80211_AC_BE, + IEEE80211_AC_BK, + IEEE80211_AC_BK, + IEEE80211_AC_BE, + IEEE80211_AC_VI, + IEEE80211_AC_VI, + IEEE80211_AC_VO, + IEEE80211_AC_VO, +#if 0 + IEEE80211_AC_VO, /* We treat MGMT as TID 8, which is set as AC_VO */ +#endif +}; + +static void +lkpi_stop_hw_scan(struct lkpi_hw *lhw, struct ieee80211_vif *vif) +{ + struct ieee80211_hw *hw; + int error; + + if ((lhw->scan_flags & LKPI_SCAN_RUNNING) == 0) + return; + + hw = LHW_TO_HW(lhw); + + /* Need to cancel the scan. */ + lkpi_80211_mo_cancel_hw_scan(hw, vif); + + /* Need to make sure we see ieee80211_scan_completed. */ + error = msleep(lhw, &lhw->mtx, 0, "lhwscanstop", hz/2); + + if ((lhw->scan_flags & LKPI_SCAN_RUNNING) != 0) + ic_printf(lhw->ic, "%s: failed to cancel scan: %d (%p, %p)\n", + __func__, error, lhw, vif); +} + +static void +lkpi_disassoc(struct ieee80211_sta *sta, struct ieee80211_vif *vif, + struct lkpi_hw *lhw) +{ + sta->aid = 0; + if (vif->bss_conf.assoc) { + struct ieee80211_hw *hw; + enum ieee80211_bss_changed changed; + + lhw->update_mc = true; + lkpi_update_mcast_filter(lhw->ic, true); + + changed = 0; + vif->bss_conf.assoc = false; + vif->bss_conf.aid = 0; + changed |= BSS_CHANGED_ASSOC; + IMPROVE(); + hw = LHW_TO_HW(lhw); + lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, + changed); + } +} + +static void +lkpi_wake_tx_queues(struct ieee80211_hw *hw, struct ieee80211_sta *sta, + bool dequeue_seen, bool no_emptyq) +{ + struct lkpi_txq *ltxq; + int tid; + + /* Wake up all queues to know they are allocated in the driver. */ + for (tid = 0; tid < nitems(sta->txq); tid++) { + + if (tid == IEEE80211_NUM_TIDS) { + IMPROVE("station specific?"); + if (!ieee80211_hw_check(hw, STA_MMPDU_TXQ)) + continue; + } else if (tid >= hw->queues) + continue; + + if (sta->txq[tid] == NULL) + continue; + + ltxq = TXQ_TO_LTXQ(sta->txq[tid]); + if (dequeue_seen && !ltxq->seen_dequeue) + continue; + + if (no_emptyq && skb_queue_empty(<xq->skbq)) + continue; + + lkpi_80211_mo_wake_tx_queue(hw, sta->txq[tid]); + } +} + +/* -------------------------------------------------------------------------- */ + +static int +lkpi_sta_state_do_nada(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + + return (0); +} + +/* lkpi_iv_newstate() handles the stop scan case generally. */ +#define lkpi_sta_scan_to_init(_v, _n, _a) lkpi_sta_state_do_nada(_v, _n, _a) + +static int +lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct linuxkpi_ieee80211_channel *chan; + struct ieee80211_chanctx_conf *conf; + struct lkpi_hw *lhw; + struct ieee80211_hw *hw; + struct lkpi_vif *lvif; + struct ieee80211_vif *vif; + struct ieee80211_node *ni; + struct lkpi_sta *lsta; + struct ieee80211_sta *sta; + enum ieee80211_bss_changed bss_changed; + struct ieee80211_prep_tx_info prep_tx_info; + uint32_t changed; + int error; + + chan = lkpi_get_lkpi80211_chan(vap->iv_ic, vap->iv_bss); + if (chan == NULL) { + ic_printf(vap->iv_ic, "%s: failed to get channel\n", __func__); + return (ESRCH); + } + + lhw = vap->iv_ic->ic_softc; + hw = LHW_TO_HW(lhw); + lvif = VAP_TO_LVIF(vap); + vif = LVIF_TO_VIF(lvif); + + IEEE80211_UNLOCK(vap->iv_ic); + + /* Add chanctx (or if exists, change it). */ + if (vif->chanctx_conf != NULL) { + conf = vif->chanctx_conf; + IMPROVE("diff changes for changed, working on live copy, rcu"); + } else { + /* Keep separate alloc as in Linux this is rcu managed? */ + conf = malloc(sizeof(*conf) + hw->chanctx_data_size, + M_LKPI80211, M_WAITOK | M_ZERO); + } + + conf->rx_chains_dynamic = 1; + conf->rx_chains_static = 1; + conf->radar_enabled = + (chan->flags & IEEE80211_CHAN_RADAR) ? true : false; + conf->def.chan = chan; + conf->def.width = NL80211_CHAN_WIDTH_20_NOHT; + conf->def.center_freq1 = chan->center_freq; + conf->def.center_freq2 = 0; + /* Responder ... */ + conf->min_def.chan = chan; + conf->min_def.width = NL80211_CHAN_WIDTH_20_NOHT; + conf->min_def.center_freq1 = chan->center_freq; + conf->min_def.center_freq2 = 0; + IMPROVE("currently 20_NOHT only"); + + ni = NULL; + error = 0; + if (vif->chanctx_conf != NULL) { + changed = IEEE80211_CHANCTX_CHANGE_MIN_WIDTH; + changed |= IEEE80211_CHANCTX_CHANGE_RADAR; + changed |= IEEE80211_CHANCTX_CHANGE_RX_CHAINS; + changed |= IEEE80211_CHANCTX_CHANGE_WIDTH; + lkpi_80211_mo_change_chanctx(hw, conf, changed); + } else { + error = lkpi_80211_mo_add_chanctx(hw, conf); + if (error == 0 || error == EOPNOTSUPP) { + vif->bss_conf.chandef.chan = conf->def.chan; + vif->bss_conf.chandef.width = conf->def.width; + vif->bss_conf.chandef.center_freq1 = + conf->def.center_freq1; + vif->bss_conf.chandef.center_freq2 = + conf->def.center_freq2; + } else { + goto out; + } + /* Assign vif chanctx. */ + if (error == 0) + error = lkpi_80211_mo_assign_vif_chanctx(hw, vif, conf); + if (error == EOPNOTSUPP) + error = 0; + if (error != 0) { + lkpi_80211_mo_remove_chanctx(hw, conf); + free(conf, M_LKPI80211); + goto out; + } + } + IMPROVE("update radiotap chan fields too"); + + ni = ieee80211_ref_node(vap->iv_bss); + + /* Set bss info (bss_info_changed). */ + bss_changed = 0; + IEEE80211_ADDR_COPY(vif->bss_conf.bssid, ni->ni_bssid); + bss_changed |= BSS_CHANGED_BSSID; + vif->bss_conf.txpower = ni->ni_txpower; + bss_changed |= BSS_CHANGED_TXPOWER; + vif->bss_conf.idle = false; + bss_changed |= BSS_CHANGED_IDLE; + vif->bss_conf.beacon_int = ni->ni_intval; + /* iwlwifi FW bug workaround; iwl_mvm_mac_sta_state. */ + if (vif->bss_conf.beacon_int < 16) + vif->bss_conf.beacon_int = 16; + bss_changed |= BSS_CHANGED_BEACON_INT; + /* Should almost assert it is this. */ + vif->bss_conf.assoc = false; + vif->bss_conf.aid = 0; + /* RATES */ + IMPROVE("bss info: not all needs to come now and rates are missing"); + lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed); + + /* Add (or adjust) sta and change state (from NOTEXIST) to NONE. */ + lsta = ni->ni_drv_data; + KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni)); + KASSERT(lsta->state == IEEE80211_STA_NOTEXIST, ("%s: lsta %p state not " + "NOTEXIST: %#x\n", __func__, lsta, lsta->state)); + sta = LSTA_TO_STA(lsta); + error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_NONE); + if (error != 0) { + IMPROVE("do we need to undo the chan ctx?"); + goto out; + } +#if 0 + lsta->added_to_drv = true; /* mo manages. */ +#endif + + /* + * Wakeup all queues now that sta is there so we have as much time to + * possibly prepare the queue in the driver to be ready for the 1st + * packet; lkpi_80211_txq_tx_one() still has a workaround as there + * is no guarantee or way to check. + */ + lkpi_wake_tx_queues(hw, sta, false, false); + + { + int i, count; + + for (i = 3; i > 0; i++) { + struct lkpi_txq *ltxq; + int tid; + + count = 0; + /* Wake up all queues to know they are allocated in the driver. */ + for (tid = 0; tid < nitems(sta->txq); tid++) { + + if (tid == IEEE80211_NUM_TIDS) { + IMPROVE("station specific?"); + if (!ieee80211_hw_check(hw, STA_MMPDU_TXQ)) + continue; + } else if (tid >= hw->queues) + continue; + + if (sta->txq[tid] == NULL) + continue; + + ltxq = TXQ_TO_LTXQ(sta->txq[tid]); + if (!ltxq->seen_dequeue) + count++; + } + if (count == 0) + break; +#ifdef LINUXKPI_DEBUG_80211 + if (count > 0) + ic_printf(vap->iv_ic, "%s: waiting for %d quuees " + "to be allocated by driver\n", __func__, count); +#endif + DELAY(100); + } +#ifdef LINUXKPI_DEBUG_80211 + if (count > 0) + ic_printf(vap->iv_ic, "%s: %d quuees still not " + "allocated by driver\n", __func__, count); +#endif + } + + /* Start mgd_prepare_tx. */ + memset(&prep_tx_info, 0, sizeof(prep_tx_info)); + prep_tx_info.duration = PREP_TX_INFO_DURATION; + lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info); + lsta->in_mgd = true; + + /* + * What is going to happen next: + * - .. we should end up in "auth_to_assoc" + * - event_callback + * - update sta_state (NONE to AUTH) + * - mgd_complete_tx + * (ideally we'd do that on a callback for something else ...) + */ + +out: + IEEE80211_LOCK(vap->iv_ic); + if (ni != NULL) + ieee80211_free_node(ni); + return (error); +} + +static int +lkpi_sta_auth_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct lkpi_hw *lhw; + struct ieee80211_hw *hw; + struct lkpi_vif *lvif; + struct ieee80211_vif *vif; + struct ieee80211_node *ni; + struct lkpi_sta *lsta; + struct ieee80211_sta *sta; + struct ieee80211_prep_tx_info prep_tx_info; + int error; + + lhw = vap->iv_ic->ic_softc; + hw = LHW_TO_HW(lhw); + lvif = VAP_TO_LVIF(vap); + vif = LVIF_TO_VIF(lvif); + + /* Keep ni around. */ + ni = ieee80211_ref_node(vap->iv_bss); + + IEEE80211_UNLOCK(vap->iv_ic); + lsta = ni->ni_drv_data; + sta = LSTA_TO_STA(lsta); + + /* flush, drop. */ + lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), true); + + IEEE80211_LOCK(vap->iv_ic); + + /* Call iv_newstate first so we get potential deauth packet out. */ + error = lvif->iv_newstate(vap, nstate, arg); + if (error != 0) + goto outni; + + IEEE80211_UNLOCK(vap->iv_ic); + + /* Wake tx queues to get packet(s) out. */ + lkpi_wake_tx_queues(hw, sta, true, true); + + /* flush, no drop */ + lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), false); + + /* Take the station and chan ctx down again. */ + + IMPROVE("event callback with failure?"); + + /* End mgd_complete_tx. */ + if (lsta->in_mgd) { + memset(&prep_tx_info, 0, sizeof(prep_tx_info)); + prep_tx_info.success = false; + lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info); + lsta->in_mgd = false; + } + +#ifdef __not_yet__ + /* sync_rx_queues */ + lkpi_80211_mo_sync_rx_queues(hw); + + /* sta_pre_rcu_remove */ + lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta); +#endif + + /* Adjust sta and change state (from NONE) to NOTEXIST. */ + KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni)); + KASSERT(lsta->state == IEEE80211_STA_NONE, ("%s: lsta %p state not " + "NONE: %#x\n", __func__, lsta, lsta->state)); + error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_NOTEXIST); + if (error != 0) { + IMPROVE("do we need to undo the chan ctx?"); + goto out; + } +#if 0 + lsta->added_to_drv = false; /* mo manages. */ +#endif + + IMPROVE("Any bss_info changes to announce?"); + + if (vif->chanctx_conf != NULL) { + struct ieee80211_chanctx_conf *conf; + + conf = vif->chanctx_conf; + /* Remove vif context. */ + lkpi_80211_mo_unassign_vif_chanctx(hw, vif, &vif->chanctx_conf); + /* NB: vif->chanctx_conf is NULL now. */ + + /* Remove chan ctx. */ + lkpi_80211_mo_remove_chanctx(hw, conf); + free(conf, M_LKPI80211); + } + + /* No need to start a scan; ic_scan_start should do. */ + + error = EALREADY; +out: + IEEE80211_LOCK(vap->iv_ic); +outni: + if (ni != NULL) + ieee80211_free_node(ni); + return (error); +} + +static int +lkpi_sta_auth_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + int error; + + error = lkpi_sta_auth_to_scan(vap, nstate, arg); + if (error == 0) + error = lkpi_sta_scan_to_init(vap, nstate, arg); + return (error); +} + +static int +lkpi_sta_auth_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct lkpi_hw *lhw; + struct ieee80211_hw *hw; + struct lkpi_vif *lvif; + struct ieee80211_vif *vif; + struct ieee80211_node *ni; + struct lkpi_sta *lsta; + struct ieee80211_sta *sta; + struct ieee80211_prep_tx_info prep_tx_info; + int error; + + lhw = vap->iv_ic->ic_softc; + hw = LHW_TO_HW(lhw); + lvif = VAP_TO_LVIF(vap); + vif = LVIF_TO_VIF(lvif); + + IEEE80211_UNLOCK(vap->iv_ic); + ni = NULL; + + /* Finish auth. */ + IMPROVE("event callback"); + + /* Update sta_state (NONE to AUTH). */ + ni = ieee80211_ref_node(vap->iv_bss); + lsta = ni->ni_drv_data; + KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni)); + KASSERT(lsta->state == IEEE80211_STA_NONE, ("%s: lsta %p state not " + "NONE: %#x\n", __func__, lsta, lsta->state)); + sta = LSTA_TO_STA(lsta); + error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_AUTH); + if (error != 0) + goto out; + + /* End mgd_complete_tx. */ + if (lsta->in_mgd) { + memset(&prep_tx_info, 0, sizeof(prep_tx_info)); + prep_tx_info.success = true; + lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info); + lsta->in_mgd = false; + } + + /* Now start assoc. */ + + /* Start mgd_prepare_tx. */ + if (!lsta->in_mgd) { + memset(&prep_tx_info, 0, sizeof(prep_tx_info)); + prep_tx_info.duration = PREP_TX_INFO_DURATION; + lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info); + lsta->in_mgd = true; + } + + /* Wake tx queue to get packet out. */ + lkpi_wake_tx_queues(hw, sta, true, true); + + /* + * .. we end up in "assoc_to_run" + * - update sta_state (AUTH to ASSOC) + * - conf_tx [all] + * - bss_info_changed (assoc, aid, ssid, ..) + * - change_chanctx (if needed) + * - event_callback + * - mgd_complete_tx + */ + +out: + IEEE80211_LOCK(vap->iv_ic); + if (ni != NULL) + ieee80211_free_node(ni); + return (error); +} + +/* auth_to_auth, assoc_to_assoc. */ +static int +lkpi_sta_a_to_a(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct lkpi_hw *lhw; + struct ieee80211_hw *hw; + struct lkpi_vif *lvif; + struct ieee80211_vif *vif; + struct ieee80211_node *ni; + struct lkpi_sta *lsta; + struct ieee80211_prep_tx_info prep_tx_info; + + lhw = vap->iv_ic->ic_softc; + hw = LHW_TO_HW(lhw); + lvif = VAP_TO_LVIF(vap); + vif = LVIF_TO_VIF(lvif); + + ni = ieee80211_ref_node(vap->iv_bss); + + IEEE80211_UNLOCK(vap->iv_ic); + lsta = ni->ni_drv_data; + + IMPROVE("event callback?"); + + /* End mgd_complete_tx. */ + if (lsta->in_mgd) { + memset(&prep_tx_info, 0, sizeof(prep_tx_info)); + prep_tx_info.success = false; + lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info); + lsta->in_mgd = false; + } + + /* Now start assoc. */ + + /* Start mgd_prepare_tx. */ + if (!lsta->in_mgd) { + memset(&prep_tx_info, 0, sizeof(prep_tx_info)); + prep_tx_info.duration = PREP_TX_INFO_DURATION; + lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info); + lsta->in_mgd = true; + } + + IEEE80211_LOCK(vap->iv_ic); + if (ni != NULL) + ieee80211_free_node(ni); + + return (0); +} + +static int +lkpi_sta_assoc_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct lkpi_hw *lhw; + struct ieee80211_hw *hw; + struct lkpi_vif *lvif; + struct ieee80211_vif *vif; + struct ieee80211_node *ni; + struct lkpi_sta *lsta; + struct ieee80211_sta *sta; + struct ieee80211_prep_tx_info prep_tx_info; + int error; + + lhw = vap->iv_ic->ic_softc; + hw = LHW_TO_HW(lhw); + lvif = VAP_TO_LVIF(vap); + vif = LVIF_TO_VIF(lvif); + + /* Keep ni around. */ + ni = ieee80211_ref_node(vap->iv_bss); + + IEEE80211_UNLOCK(vap->iv_ic); + lsta = ni->ni_drv_data; + sta = LSTA_TO_STA(lsta); + + /* End mgd_complete_tx. */ + if (lsta->in_mgd) { + memset(&prep_tx_info, 0, sizeof(prep_tx_info)); + prep_tx_info.success = false; + lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info); + lsta->in_mgd = false; + } + + /* Update sta and change state (from AUTH) to NONE. */ + KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni)); + KASSERT(lsta->state == IEEE80211_STA_AUTH, ("%s: lsta %p state not " + "AUTH: %#x\n", __func__, lsta, lsta->state)); + error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_NONE); + if (error != 0) + goto out; + + IMPROVE("anything else?"); + +out: + IEEE80211_LOCK(vap->iv_ic); + if (ni != NULL) + ieee80211_free_node(ni); + return (error); +} + +static int +lkpi_sta_assoc_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + int error; + + error = lkpi_sta_assoc_to_auth(vap, nstate, arg); + if (error == 0) + error = lkpi_sta_auth_to_scan(vap, nstate, arg); + return (error); +} + +static int +lkpi_sta_assoc_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + int error; + + error = lkpi_sta_assoc_to_scan(vap, nstate, arg); + if (error == 0) + error = lkpi_sta_scan_to_init(vap, nstate, arg); + return (error); +} + +static int +lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct lkpi_hw *lhw; + struct ieee80211_hw *hw; + struct lkpi_vif *lvif; + struct ieee80211_vif *vif; + struct ieee80211_node *ni; + struct lkpi_sta *lsta; + struct ieee80211_sta *sta; + struct ieee80211_prep_tx_info prep_tx_info; + enum ieee80211_bss_changed bss_changed; + int error; + + lhw = vap->iv_ic->ic_softc; + hw = LHW_TO_HW(lhw); + lvif = VAP_TO_LVIF(vap); + vif = LVIF_TO_VIF(lvif); + + IEEE80211_UNLOCK(vap->iv_ic); + ni = NULL; + + IMPROVE("ponder some of this moved to ic_newassoc, scan_assoc_success, " + "and to lesser extend ieee80211_notify_node_join"); + + /* Finish assoc. */ + /* Update sta_state (AUTH to ASSOC) and set aid. */ + ni = ieee80211_ref_node(vap->iv_bss); + lsta = ni->ni_drv_data; + KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni)); + KASSERT(lsta->state == IEEE80211_STA_AUTH, ("%s: lsta %p state not " + "AUTH: %#x\n", __func__, lsta, lsta->state)); + sta = LSTA_TO_STA(lsta); + sta->aid = IEEE80211_NODE_AID(ni); + error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_ASSOC); + if (error != 0) + goto out; + + IMPROVE("wme / conf_tx [all]"); + + /* Update bss info (bss_info_changed) (assoc, aid, ..). */ + bss_changed = 0; + if (!vif->bss_conf.assoc || vif->bss_conf.aid != IEEE80211_NODE_AID(ni)) { + vif->bss_conf.assoc = true; + vif->bss_conf.aid = IEEE80211_NODE_AID(ni); + bss_changed |= BSS_CHANGED_ASSOC; + } + /* We set SSID but this is not BSSID! */ + vif->bss_conf.ssid_len = ni->ni_esslen; + memcpy(vif->bss_conf.ssid, ni->ni_essid, ni->ni_esslen); + if ((vap->iv_flags & IEEE80211_F_SHPREAMBLE) != + vif->bss_conf.use_short_preamble) { + vif->bss_conf.use_short_preamble ^= 1; + /* bss_changed |= BSS_CHANGED_??? */ + } + if ((vap->iv_flags & IEEE80211_F_SHSLOT) != + vif->bss_conf.use_short_slot) { + vif->bss_conf.use_short_slot ^= 1; + /* bss_changed |= BSS_CHANGED_??? */ + } + if ((ni->ni_flags & IEEE80211_NODE_QOS) != + vif->bss_conf.qos) { + vif->bss_conf.qos ^= 1; + bss_changed |= BSS_CHANGED_QOS; + } + if (vif->bss_conf.beacon_int != ni->ni_intval) { + vif->bss_conf.beacon_int = ni->ni_intval; + /* iwlwifi FW bug workaround; iwl_mvm_mac_sta_state. */ + if (vif->bss_conf.beacon_int < 16) + vif->bss_conf.beacon_int = 16; + bss_changed |= BSS_CHANGED_BEACON_INT; + } + if (vif->bss_conf.dtim_period != vap->iv_dtim_period && + vap->iv_dtim_period > 0) { + vif->bss_conf.dtim_period = vap->iv_dtim_period; + bss_changed |= BSS_CHANGED_BEACON_INFO; + } + lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed); + + /* - change_chanctx (if needed) + * - event_callback + */ + + /* End mgd_complete_tx. */ + if (lsta->in_mgd) { + memset(&prep_tx_info, 0, sizeof(prep_tx_info)); + prep_tx_info.success = true; + lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info); + lsta->in_mgd = false; + } + + /* + * And then: + * - (more packets)? + * - set_key + * - set_default_unicast_key + * - set_key (?) + * - ipv6_addr_change (?) + */ + /* Prepare_multicast && configure_filter. */ + lhw->update_mc = true; + lkpi_update_mcast_filter(vap->iv_ic, true); + + if (!ieee80211_node_is_authorized(ni)) { + IMPROVE("net80211 does not consider node authorized"); + } + + /* Update sta_state (ASSOC to AUTHORIZED). */ + ni = ieee80211_ref_node(vap->iv_bss); + lsta = ni->ni_drv_data; + KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni)); + KASSERT(lsta->state == IEEE80211_STA_ASSOC, ("%s: lsta %p state not " + "ASSOC: %#x\n", __func__, lsta, lsta->state)); + sta = LSTA_TO_STA(lsta); + error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_AUTHORIZED); + if (error != 0) { + IMPROVE("undo some changes?"); + goto out; + } + + /* - drv_config (?) + * - bss_info_changed + * - set_rekey_data (?) + * + * And now we should be passing packets. + */ + IMPROVE("Need that bssid setting, and the keys"); + +out: + IEEE80211_LOCK(vap->iv_ic); + if (ni != NULL) + ieee80211_free_node(ni); + return (error); +} + +static int +lkpi_sta_auth_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + int error; + + error = lkpi_sta_auth_to_assoc(vap, nstate, arg); + if (error == 0) + error = lkpi_sta_assoc_to_run(vap, nstate, arg); + return (error); +} + +static int +lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct lkpi_hw *lhw; + struct ieee80211_hw *hw; + struct lkpi_vif *lvif; + struct ieee80211_vif *vif; + struct ieee80211_node *ni; + struct lkpi_sta *lsta; + struct ieee80211_sta *sta; + int error; + + lhw = vap->iv_ic->ic_softc; + hw = LHW_TO_HW(lhw); + lvif = VAP_TO_LVIF(vap); + vif = LVIF_TO_VIF(lvif); + + /* Keep ni around. */ + ni = ieee80211_ref_node(vap->iv_bss); + + IEEE80211_UNLOCK(vap->iv_ic); + lsta = ni->ni_drv_data; + sta = LSTA_TO_STA(lsta); + + /* Adjust sta and change state (from AUTHORIZED) to ASSOC. */ + KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni)); + KASSERT(lsta->state == IEEE80211_STA_AUTHORIZED, ("%s: lsta %p state not " + "AUTHORIZED: %#x\n", __func__, lsta, lsta->state)); + error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_ASSOC); + if (error != 0) + goto out; + + /* Update bss info (bss_info_changed) (assoc, aid, ..). */ + lkpi_disassoc(sta, vif, lhw); + + /* Update sta_state (ASSOC to AUTH). */ + KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni)); + KASSERT(lsta->state == IEEE80211_STA_ASSOC, ("%s: lsta %p state not " + "ASSOC: %#x\n", __func__, lsta, lsta->state)); + sta = LSTA_TO_STA(lsta); + sta->aid = 0; + error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_AUTH); + if (error != 0) + goto out; + + IMPROVE("if ASSOC is final state, prep_tx_info?"); + +out: + IEEE80211_LOCK(vap->iv_ic); + if (ni != NULL) + ieee80211_free_node(ni); + return (error); +} + +static int +lkpi_sta_run_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + int error; + + error = lkpi_sta_run_to_assoc(vap, nstate, arg); + if (error == 0) + error = lkpi_sta_assoc_to_auth(vap, nstate, arg); + return (error); +} + +static int +lkpi_sta_run_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + int error; + + error = lkpi_sta_run_to_auth(vap, nstate, arg); + if (error == 0) + error = lkpi_sta_auth_to_scan(vap, nstate, arg); + return (error); +} + +static int +lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + int error; + + error = lkpi_sta_run_to_scan(vap, nstate, arg); + if (error == 0) + error = lkpi_sta_scan_to_init(vap, nstate, arg); + return (error); +} + +/* + * The matches the documented state changes in net80211::sta_newstate(). + * XXX (1) without CSA and SLEEP yet, * XXX (2) not all unhandled cases + * there are "invalid" (so there is a room for failure here). + */ +struct fsm_state { + /* INIT, SCAN, AUTH, ASSOC, CAC, RUN, CSA, SLEEP */ + enum ieee80211_state ostate; + enum ieee80211_state nstate; + int (*handler)(struct ieee80211vap *, enum ieee80211_state, int); +} sta_state_fsm[] = { + { IEEE80211_S_INIT, IEEE80211_S_INIT, lkpi_sta_state_do_nada }, + { IEEE80211_S_SCAN, IEEE80211_S_INIT, lkpi_sta_state_do_nada }, /* scan_to_init */ + { IEEE80211_S_AUTH, IEEE80211_S_INIT, lkpi_sta_auth_to_init }, /* not explicitly in sta_newstate() */ + { IEEE80211_S_ASSOC, IEEE80211_S_INIT, lkpi_sta_assoc_to_init }, + { IEEE80211_S_RUN, IEEE80211_S_INIT, lkpi_sta_run_to_init }, + + { IEEE80211_S_INIT, IEEE80211_S_SCAN, lkpi_sta_state_do_nada }, + { IEEE80211_S_SCAN, IEEE80211_S_SCAN, lkpi_sta_state_do_nada }, + { IEEE80211_S_AUTH, IEEE80211_S_SCAN, lkpi_sta_auth_to_scan }, + { IEEE80211_S_ASSOC, IEEE80211_S_SCAN, lkpi_sta_assoc_to_scan }, + { IEEE80211_S_RUN, IEEE80211_S_SCAN, lkpi_sta_run_to_scan }, + + { IEEE80211_S_INIT, IEEE80211_S_AUTH, lkpi_sta_scan_to_auth }, + { IEEE80211_S_SCAN, IEEE80211_S_AUTH, lkpi_sta_scan_to_auth }, + { IEEE80211_S_AUTH, IEEE80211_S_AUTH, lkpi_sta_a_to_a }, + { IEEE80211_S_ASSOC, IEEE80211_S_AUTH, lkpi_sta_assoc_to_auth }, + { IEEE80211_S_RUN, IEEE80211_S_AUTH, lkpi_sta_run_to_auth }, + + { IEEE80211_S_AUTH, IEEE80211_S_ASSOC, lkpi_sta_auth_to_assoc }, + { IEEE80211_S_ASSOC, IEEE80211_S_ASSOC, lkpi_sta_a_to_a }, + { IEEE80211_S_RUN, IEEE80211_S_ASSOC, lkpi_sta_run_to_assoc }, + + { IEEE80211_S_AUTH, IEEE80211_S_RUN, lkpi_sta_auth_to_run }, + { IEEE80211_S_ASSOC, IEEE80211_S_RUN, lkpi_sta_assoc_to_run }, + { IEEE80211_S_RUN, IEEE80211_S_RUN, lkpi_sta_state_do_nada }, + + /* Dummy at the end without handler. */ + { IEEE80211_S_INIT, IEEE80211_S_INIT, NULL }, +}; + +static int +lkpi_iv_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct ieee80211com *ic; + struct lkpi_hw *lhw; + struct lkpi_vif *lvif; + struct ieee80211_vif *vif; + struct fsm_state *s; + enum ieee80211_state ostate; + int error; + + ic = vap->iv_ic; + IEEE80211_LOCK_ASSERT(ic); + ostate = vap->iv_state; + + if (debug_80211 & D80211_TRACE) + ic_printf(vap->iv_ic, "%s:%d: vap %p nstate %#x arg %#x\n", + __func__, __LINE__, vap, nstate, arg); + + if (vap->iv_opmode == IEEE80211_M_STA) { + + lhw = ic->ic_softc; + lvif = VAP_TO_LVIF(vap); + vif = LVIF_TO_VIF(lvif); + + /* No need to replicate this in most state handlers. */ + if (ostate == IEEE80211_S_SCAN && nstate != IEEE80211_S_SCAN) + lkpi_stop_hw_scan(lhw, vif); + + s = sta_state_fsm; + + } else { + ic_printf(vap->iv_ic, "%s: only station mode currently supported: " + "cap %p iv_opmode %d\n", __func__, vap, vap->iv_opmode); + return (ENOSYS); + } + + error = 0; + for (; s->handler != NULL; s++) { + if (ostate == s->ostate && nstate == s->nstate) { + error = s->handler(vap, nstate, arg); + break; + } + } + IEEE80211_LOCK_ASSERT(vap->iv_ic); + + if (s->handler == NULL) { + IMPROVE("thurn this into a KASSERT\n"); + ic_printf(vap->iv_ic, "%s: unsupported state transition " + "%d (%s) -> %d (%s)\n", __func__, + ostate, ieee80211_state_name[ostate], + nstate, ieee80211_state_name[nstate]); + return (ENOSYS); + } + + if (error == EALREADY) { + IMPROVE("make this a debug log later"); + ic_printf(vap->iv_ic, "%s: error %d during state transition " + "%d (%s) -> %d (%s): iv_newstate already handled.\n", + __func__, error, + ostate, ieee80211_state_name[ostate], + nstate, ieee80211_state_name[nstate]); + return (0); + } + + if (error != 0) { + /* XXX-BZ currently expected so ignore. */ + ic_printf(vap->iv_ic, "%s: error %d during state transition " + "%d (%s) -> %d (%s)\n", __func__, error, + ostate, ieee80211_state_name[ostate], + nstate, ieee80211_state_name[nstate]); + /* return (error); */ + } + + if (debug_80211 & D80211_TRACE) + ic_printf(vap->iv_ic, "%s:%d: vap %p nstate %#x arg %#x calling net80211 parent\n", + __func__, __LINE__, vap, nstate, arg); + + return (lvif->iv_newstate(vap, nstate, arg)); +} + +/* -------------------------------------------------------------------------- */ + +static int +lkpi_ic_wme_update(struct ieee80211com *ic) +{ + /* This needs queuing and go at the right moment. */ +#ifdef WITH_WME_UPDATE + struct ieee80211vap *vap; + struct lkpi_hw *lhw; + struct ieee80211_hw *hw; + struct lkpi_vif *lvif; + struct ieee80211_vif *vif; + struct chanAccParams chp; + struct wmeParams wmeparr[WME_NUM_AC]; + struct ieee80211_tx_queue_params txqp; + enum ieee80211_bss_changed changed; + int error; + uint16_t ac; +#endif + + IMPROVE(); + KASSERT(WME_NUM_AC == IEEE80211_NUM_ACS, ("%s: WME_NUM_AC %d != " + "IEEE80211_NUM_ACS %d\n", __func__, WME_NUM_AC, IEEE80211_NUM_ACS)); + +#ifdef WITH_WME_UPDATE + vap = TAILQ_FIRST(&ic->ic_vaps); + if (vap == NULL) + return (0); + + /* We should factor this out into per-vap (*wme_update). */ + lhw = ic->ic_softc; + if (lhw->ops->conf_tx == NULL) + return (0); + + /* XXX-BZ check amount of hw queues */ + hw = LHW_TO_HW(lhw); + lvif = VAP_TO_LVIF(vap); + vif = LVIF_TO_VIF(lvif); + + ieee80211_wme_ic_getparams(ic, &chp); + IEEE80211_LOCK(ic); + for (ac = 0; ac < WME_NUM_AC; ac++) + wmeparr[ac] = chp.cap_wmeParams[ac]; + IEEE80211_UNLOCK(ic); + + /* Configure tx queues (conf_tx) & send BSS_CHANGED_QOS. */ + LKPI_80211_LHW_LOCK(lhw); + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + struct wmeParams *wmep; + + /* XXX-BZ should keep this in lvif? */ + wmep = &wmeparr[ac]; + bzero(&txqp, sizeof(txqp)); + txqp.cw_min = wmep->wmep_logcwmin; + txqp.cw_max = wmep->wmep_logcwmax; + txqp.txop = wmep->wmep_txopLimit; + txqp.aifs = wmep->wmep_aifsn; + error = lkpi_80211_mo_conf_tx(hw, vif, ac, &txqp); + if (error != 0) + printf("%s: conf_tx ac %u failed %d\n", + __func__, ac, error); + } + LKPI_80211_LHW_UNLOCK(lhw); + changed = BSS_CHANGED_QOS; + lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, changed); +#endif + + return (0); +} + +static struct ieee80211vap * +lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], + int unit, enum ieee80211_opmode opmode, int flags, + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + struct lkpi_hw *lhw; + struct ieee80211_hw *hw; + struct lkpi_vif *lvif; + struct ieee80211vap *vap; + struct ieee80211_vif *vif; + enum ieee80211_bss_changed changed; + size_t len; + int error; + + if (!TAILQ_EMPTY(&ic->ic_vaps)) /* 1 so far. Add once this works. */ + return (NULL); + + lhw = ic->ic_softc; + hw = LHW_TO_HW(lhw); + + len = sizeof(*lvif); + len += hw->vif_data_size; /* vif->drv_priv */ + + lvif = malloc(len, M_80211_VAP, M_WAITOK | M_ZERO); + mtx_init(&lvif->mtx, "lvif", NULL, MTX_DEF); + TAILQ_INIT(&lvif->lsta_head); + vap = LVIF_TO_VAP(lvif); + + vif = LVIF_TO_VIF(lvif); + memcpy(vif->addr, mac, IEEE80211_ADDR_LEN); + vif->p2p = false; + vif->probe_req_reg = false; + vif->type = lkpi_opmode_to_vif_type(opmode); + lvif->wdev.iftype = vif->type; + /* Need to fill in other fields as well. */ + IMPROVE(); + + /* XXX-BZ hardcoded for now! */ +#if 1 + vif->chanctx_conf = NULL; + vif->bss_conf.idle = true; + vif->bss_conf.ps = false; + vif->bss_conf.chandef.width = NL80211_CHAN_WIDTH_20_NOHT; + vif->bss_conf.use_short_preamble = false; /* vap->iv_flags IEEE80211_F_SHPREAMBLE */ + vif->bss_conf.use_short_slot = false; /* vap->iv_flags IEEE80211_F_SHSLOT */ + vif->bss_conf.qos = false; + vif->bss_conf.use_cts_prot = false; /* vap->iv_protmode */ + vif->bss_conf.ht_operation_mode = IEEE80211_HT_OP_MODE_PROTECTION_NONE; + vif->bss_conf.assoc = false; + vif->bss_conf.aid = 0; +#endif +#if 0 + vif->bss_conf.dtim_period = 0; /* IEEE80211_DTIM_DEFAULT ; must stay 0. */ + IEEE80211_ADDR_COPY(vif->bss_conf.bssid, bssid); + vif->bss_conf.beacon_int = ic->ic_bintval; + /* iwlwifi bug. */ + if (vif->bss_conf.beacon_int < 16) + vif->bss_conf.beacon_int = 16; +#endif + IMPROVE(); + + error = lkpi_80211_mo_start(hw); + if (error != 0) { + printf("%s: failed to start hw: %d\n", __func__, error); + mtx_destroy(&lvif->mtx); + free(lvif, M_80211_VAP); + return (NULL); + } + + error = lkpi_80211_mo_add_interface(hw, vif); + if (error != 0) { + IMPROVE(); /* XXX-BZ mo_stop()? */ + printf("%s: failed to add interface: %d\n", __func__, error); + mtx_destroy(&lvif->mtx); + free(lvif, M_80211_VAP); + return (NULL); + } + + LKPI_80211_LHW_LOCK(lhw); + TAILQ_INSERT_TAIL(&lhw->lvif_head, lvif, lvif_entry); + LKPI_80211_LHW_UNLOCK(lhw); + + /* Set bss_info. */ + changed = 0; + lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, changed); + + /* conf_tx setup; default WME? */ + + /* Force MC init. */ + lkpi_update_mcast_filter(ic, true); + + IMPROVE(); + + ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid); + + /* Override with LinuxKPI method so we can drive mac80211/cfg80211. */ + lvif->iv_newstate = vap->iv_newstate; + vap->iv_newstate = lkpi_iv_newstate; + + /* Key management. */ + if (lhw->ops->set_key != NULL) { +#ifdef TRY_HW_CRYPTO + vap->iv_key_set = lkpi_iv_key_set; + vap->iv_key_delete = lkpi_iv_key_delete; +#endif + } + + ieee80211_ratectl_init(vap); + + /* Complete setup. */ + ieee80211_vap_attach(vap, ieee80211_media_change, + ieee80211_media_status, mac); + + if (hw->max_listen_interval == 0) + hw->max_listen_interval = 7 * (ic->ic_lintval / ic->ic_bintval); + hw->conf.listen_interval = hw->max_listen_interval; + ic->ic_set_channel(ic); + + /* XXX-BZ do we need to be able to update these? */ + hw->wiphy->frag_threshold = vap->iv_fragthreshold; + lkpi_80211_mo_set_frag_threshold(hw, vap->iv_fragthreshold); + hw->wiphy->rts_threshold = vap->iv_rtsthreshold; + lkpi_80211_mo_set_rts_threshold(hw, vap->iv_rtsthreshold); + /* any others? */ + IMPROVE(); + + return (vap); +} + +static void +lkpi_ic_vap_delete(struct ieee80211vap *vap) +{ + struct ieee80211com *ic; + struct lkpi_hw *lhw; + struct ieee80211_hw *hw; + struct lkpi_vif *lvif; + struct ieee80211_vif *vif; + + lvif = VAP_TO_LVIF(vap); + vif = LVIF_TO_VIF(lvif); + ic = vap->iv_ic; + lhw = ic->ic_softc; + hw = LHW_TO_HW(lhw); + + LKPI_80211_LHW_LOCK(lhw); + TAILQ_REMOVE(&lhw->lvif_head, lvif, lvif_entry); + LKPI_80211_LHW_UNLOCK(lhw); + lkpi_80211_mo_remove_interface(hw, vif); + + ieee80211_ratectl_deinit(vap); + ieee80211_vap_detach(vap); + mtx_destroy(&lvif->mtx); + free(lvif, M_80211_VAP); +} + +static void +lkpi_ic_update_mcast(struct ieee80211com *ic) +{ + + lkpi_update_mcast_filter(ic, false); + TRACEOK(); +} + +static void +lkpi_ic_update_promisc(struct ieee80211com *ic) +{ + + UNIMPLEMENTED; +} + +static void +lkpi_ic_update_chw(struct ieee80211com *ic) +{ + + UNIMPLEMENTED; +} + +/* Start / stop device. */ +static void +lkpi_ic_parent(struct ieee80211com *ic) +{ + struct lkpi_hw *lhw; + struct ieee80211_hw *hw; + int error; + bool start_all; + + IMPROVE(); + + lhw = ic->ic_softc; + hw = LHW_TO_HW(lhw); + start_all = false; + + if (ic->ic_nrunning > 0) { + error = lkpi_80211_mo_start(hw); + if (error == 0) + start_all = true; + } else { + lkpi_80211_mo_stop(hw); + } + + if (start_all) + ieee80211_start_all(ic); +} + +bool +linuxkpi_ieee80211_is_ie_id_in_ie_buf(const u8 ie, const u8 *ie_ids, + size_t ie_ids_len) +{ + int i; + + for (i = 0; i < ie_ids_len; i++) { + if (ie == *ie_ids) + return (true); + } + + return (false); +} + +/* Return true if skipped; false if error. */ +bool +linuxkpi_ieee80211_ie_advance(size_t *xp, const u8 *ies, size_t ies_len) +{ + size_t x; + uint8_t l; + + x = *xp; + + KASSERT(x < ies_len, ("%s: x %zu ies_len %zu ies %p\n", + __func__, x, ies_len, ies)); + l = ies[x + 1]; + x += 2 + l; + + if (x > ies_len) + return (false); + + *xp = x; + return (true); +} + +static int +lkpi_ieee80211_probereq_ie_alloc(struct ieee80211vap *vap, + struct ieee80211com *ic, struct ieee80211_scan_ies *scan_ies, + const uint8_t *ssid, size_t ssidlen) +{ + + return (ieee80211_probereq_ie(vap, ic, + &scan_ies->common_ies, &scan_ies->common_ie_len, + ssid, ssidlen, true)); +} + +static void +lkpi_ic_scan_start(struct ieee80211com *ic) +{ + struct lkpi_hw *lhw; + struct ieee80211_hw *hw; + struct lkpi_vif *lvif; + struct ieee80211_vif *vif; + struct ieee80211_scan_state *ss; + struct ieee80211vap *vap; + int error; + + lhw = ic->ic_softc; + if ((lhw->scan_flags & LKPI_SCAN_RUNNING) != 0) { + /* A scan is still running. */ + return; + } + + ss = ic->ic_scan; + vap = ss->ss_vap; + if (vap->iv_state != IEEE80211_S_SCAN) { + /* Do not start a scan for now. */ + return; + } + + hw = LHW_TO_HW(lhw); + if ((ic->ic_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD) == 0) { + + lvif = VAP_TO_LVIF(vap); + vif = LVIF_TO_VIF(lvif); + lkpi_80211_mo_sw_scan_start(hw, vif, vif->addr); + /* net80211::scan_start() handled PS for us. */ + IMPROVE(); + /* XXX Also means it is too late to flush queues? + * need to check iv_sta_ps or overload? */ + /* XXX want to adjust ss end time/ maxdwell? */ + + } else { + struct ieee80211_channel *c; + struct ieee80211_scan_request *hw_req; + struct linuxkpi_ieee80211_channel *lc, **cpp; + struct cfg80211_ssid *ssids; + struct cfg80211_scan_6ghz_params *s6gp; + size_t chan_len, nchan, ssids_len, s6ghzlen; + int i; + + ssids_len = ss->ss_nssid * sizeof(*ssids);; + s6ghzlen = 0 * (sizeof(*s6gp)); /* XXX-BZ */ + + nchan = 0; + for (i = ss->ss_next; i < ss->ss_last; i++) + nchan++; + chan_len = nchan * (sizeof(lc) + sizeof(*lc)); + + KASSERT(lhw->hw_req == NULL, ("%s: ic %p lhw %p hw_req %p " + "!= NULL\n", __func__, ic, lhw, lhw->hw_req)); + lhw->hw_req = hw_req = malloc(sizeof(*hw_req) + ssids_len + + s6ghzlen + chan_len, M_LKPI80211, M_WAITOK | M_ZERO); + + error = lkpi_ieee80211_probereq_ie_alloc(vap, ic, + &hw_req->ies, NULL, -1); + if (error != 0) + ic_printf(ic, "ERROR: %s: probereq_ie returned %d\n", + __func__, error); + + hw_req->req.flags = 0; /* XXX ??? */ + /* hw_req->req.wdev */ + hw_req->req.wiphy = hw->wiphy; + hw_req->req.no_cck = false; /* XXX */ +#if 0 + /* This seems to pessimise default scanning behaviour. */ + hw_req->req.duration_mandatory = TICKS_2_USEC(ss->ss_mindwell); + hw_req->req.duration = TICKS_2_USEC(ss->ss_maxdwell); +#endif +#ifdef __notyet__ + hw_req->req.flags |= NL80211_SCAN_FLAG_RANDOM_ADDR; + memcpy(hw_req->req.mac_addr, xxx, IEEE80211_ADDR_LEN); + memset(hw_req->req.mac_addr_mask, 0xxx, IEEE80211_ADDR_LEN); +#endif +#if 0 + hw_req->req.ie_len = ; + hw_req->req.ie = ; +#endif + + hw_req->req.n_channels = nchan; + cpp = (struct linuxkpi_ieee80211_channel **)(hw_req + 1); + lc = (struct linuxkpi_ieee80211_channel *)(cpp + nchan); + for (i = 0; i < nchan; i++) { + *(cpp + i) = + (struct linuxkpi_ieee80211_channel *)(lc + i); + } + for (i = 0; i < nchan; i++) { + c = ss->ss_chans[ss->ss_next + i]; + + lc->hw_value = c->ic_ieee; + lc->center_freq = c->ic_freq; + /* lc->flags */ + lc->band = lkpi_net80211_chan_to_nl80211_band(c); + lc->max_power = c->ic_maxpower; + /* lc-> ... */ + lc++; + } + + hw_req->req.n_ssids = ss->ss_nssid; + if (hw_req->req.n_ssids > 0) { + ssids = (struct cfg80211_ssid *)lc; + hw_req->req.ssids = ssids; + for (i = 0; i < ss->ss_nssid; i++) { + ssids->ssid_len = ss->ss_ssid[i].len; + memcpy(ssids->ssid, ss->ss_ssid[i].ssid, + ss->ss_ssid[i].len); + ssids++; + } + s6gp = (struct cfg80211_scan_6ghz_params *)ssids; + } else { + s6gp = (struct cfg80211_scan_6ghz_params *)lc; + } + + /* 6GHz one day. */ + hw_req->req.n_6ghz_params = 0; + hw_req->req.scan_6ghz_params = NULL; + hw_req->req.scan_6ghz = false; /* Weird boolean; not what you think. */ + /* s6gp->... */ + + lvif = VAP_TO_LVIF(vap); + vif = LVIF_TO_VIF(lvif); + error = lkpi_80211_mo_hw_scan(hw, vif, hw_req); + if (error != 0) { + ic_printf(ic, "ERROR: %s: hw_scan returned %d\n", + __func__, error); + ieee80211_cancel_scan(vap); + free(hw_req->ies.common_ies, M_80211_VAP); + free(hw_req, M_LKPI80211); + lhw->hw_req = NULL; + } + } +} + +static void +lkpi_ic_scan_end(struct ieee80211com *ic) +{ + struct lkpi_hw *lhw; + + lhw = ic->ic_softc; + if ((lhw->scan_flags & LKPI_SCAN_RUNNING) == 0) { + return; + } + + if (ic->ic_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD) { + /* Nothing to do. */ + } else { + struct ieee80211_hw *hw; + struct lkpi_vif *lvif; + struct ieee80211_vif *vif; + struct ieee80211_scan_state *ss; + struct ieee80211vap *vap; + + hw = LHW_TO_HW(lhw); + ss = ic->ic_scan; + vap = ss->ss_vap; + lvif = VAP_TO_LVIF(vap); + vif = LVIF_TO_VIF(lvif); + lkpi_80211_mo_sw_scan_complete(hw, vif); + + /* Send PS to stop buffering if n80211 does not for us? */ + } +} + +static void +lkpi_ic_scan_curchan_nada(struct ieee80211_scan_state *ss __unused, + unsigned long maxdwell __unused) +{ +} + +static void +lkpi_ic_set_channel(struct ieee80211com *ic) +{ + struct lkpi_hw *lhw; + struct ieee80211_hw *hw; + int error; + + lhw = ic->ic_softc; +#ifdef __no_longer__ + /* For now only be concerned if scanning. */ + if ((lhw->scan_flags & LKPI_SCAN_RUNNING) == 0) { + IMPROVE(); + return; + } +#endif + + if (ic->ic_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD) { + /* + * AP scanning is taken care of by firmware, so only switch + * channels in monitor mode (maybe, maybe not; to be + * investigated at the right time). + */ + if (ic->ic_opmode == IEEE80211_M_MONITOR) { + UNIMPLEMENTED; + } + } else { + struct ieee80211_channel *c = ic->ic_curchan; + struct linuxkpi_ieee80211_channel *chan; + struct cfg80211_chan_def chandef; + + if (c == NULL || c == IEEE80211_CHAN_ANYC || + lhw->ops->config == NULL) { + ic_printf(ic, "%s: c %p ops->config %p\n", __func__, + c, lhw->ops->config); + return; + } + + chan = lkpi_find_lkpi80211_chan(lhw, c); + if (chan == NULL) { + ic_printf(ic, "%s: c %p chan %p\n", __func__, + c, chan); + return; + } + + memset(&chandef, 0, sizeof(chandef)); + chandef.chan = chan; + chandef.width = NL80211_CHAN_WIDTH_20_NOHT; + chandef.center_freq1 = chandef.chan->center_freq; + + /* XXX max power for scanning? */ + IMPROVE(); + + hw = LHW_TO_HW(lhw); + hw->conf.chandef = chandef; + + hw->conf.flags &= ~IEEE80211_CONF_IDLE; + error = lkpi_80211_mo_config(hw, IEEE80211_CONF_CHANGE_IDLE); + if (error != 0 && error != EOPNOTSUPP) + ic_printf(ic, "ERROR: %s: config %#0x returned %d\n", + __func__, IEEE80211_CONF_CHANGE_IDLE, error); + + error = lkpi_80211_mo_config(hw, IEEE80211_CONF_CHANGE_CHANNEL); + if (error != 0 && error != EOPNOTSUPP) { + ic_printf(ic, "ERROR: %s: config %#0x returned %d\n", + __func__, IEEE80211_CONF_CHANGE_CHANNEL, error); + /* XXX should we unroll to the previous chandef? */ + IMPROVE(); + } else { + /* Update radiotap channels as well. */ + lhw->rtap_tx.wt_chan_freq = htole16(c->ic_freq); + lhw->rtap_tx.wt_chan_flags = htole16(c->ic_flags); + lhw->rtap_rx.wr_chan_freq = htole16(c->ic_freq); + lhw->rtap_rx.wr_chan_flags = htole16(c->ic_flags); + } + + /* Currently PS is hard coded off! Not sure it belongs here. */ + IMPROVE(); + if (ieee80211_hw_check(hw, SUPPORTS_PS) && + (hw->conf.flags & IEEE80211_CONF_PS) != 0) { + hw->conf.flags &= ~IEEE80211_CONF_PS; + error = lkpi_80211_mo_config(hw, IEEE80211_CONF_CHANGE_PS); + if (error != 0 && error != EOPNOTSUPP) + ic_printf(ic, "ERROR: %s: config %#0x returned " + "%d\n", __func__, IEEE80211_CONF_CHANGE_PS, + error); + } + } +} + +static struct ieee80211_node * +lkpi_ic_node_alloc(struct ieee80211vap *vap, + const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + struct ieee80211com *ic; + struct lkpi_hw *lhw; + struct ieee80211_hw *hw; + struct lkpi_sta *lsta; + struct ieee80211_sta *sta; + struct lkpi_vif *lvif; + struct ieee80211_vif *vif; + struct ieee80211_node *ni; + int tid; + + ic = vap->iv_ic; + lhw = ic->ic_softc; + + /* We keep allocations de-coupled so we can deal with the two worlds. */ + if (lhw->ic_node_alloc != NULL) { + ni = lhw->ic_node_alloc(vap, mac); + if (ni == NULL) + return (NULL); + } + + hw = LHW_TO_HW(lhw); + lsta = malloc(sizeof(*lsta) + hw->sta_data_size, M_LKPI80211, + M_NOWAIT | M_ZERO); + if (lsta == NULL) { + if (lhw->ic_node_free != NULL) + lhw->ic_node_free(ni); + return (NULL); + } + + lsta->added_to_drv = false; + lsta->state = IEEE80211_STA_NOTEXIST; +#if 0 + /* + * This needs to be done in node_init() as ieee80211_alloc_node() + * will initialise the refcount after us. + */ + lsta->ni = ieee80211_ref_node(ni); +#endif + /* The back-pointer "drv_data" to net80211_node let's us get lsta. */ + ni->ni_drv_data = lsta; + + lvif = VAP_TO_LVIF(vap); + vif = LVIF_TO_VIF(lvif); + sta = LSTA_TO_STA(lsta); + + IEEE80211_ADDR_COPY(sta->addr, mac); + for (tid = 0; tid < nitems(sta->txq); tid++) { + struct lkpi_txq *ltxq; + + /* + * We are neither limiting ourselves to hw.queues here, + * nor do we check if driver wants IEEE80211_NUM_TIDS queue. + */ + + ltxq = malloc(sizeof(*ltxq) + hw->txq_data_size, + M_LKPI80211, M_NOWAIT | M_ZERO); + if (ltxq == NULL) + goto cleanup; + ltxq->seen_dequeue = false; + skb_queue_head_init(<xq->skbq); + /* iwlwifi//mvm/sta.c::tid_to_mac80211_ac[] */ + if (tid == IEEE80211_NUM_TIDS) { + IMPROVE(); + ltxq->txq.ac = IEEE80211_AC_VO; + } else { + ltxq->txq.ac = tid_to_mac80211_ac[tid & 7]; + } + ltxq->txq.tid = tid; + ltxq->txq.sta = sta; + ltxq->txq.vif = vif; + sta->txq[tid] = <xq->txq; + } + + /* Deferred TX path. */ + mtx_init(&lsta->txq_mtx, "lsta_txq", NULL, MTX_DEF); + TASK_INIT(&lsta->txq_task, 0, lkpi_80211_txq_task, lsta); + mbufq_init(&lsta->txq, IFQ_MAXLEN); + + return (ni); + +cleanup: + for (; tid >= 0; tid--) + free(sta->txq[tid], M_LKPI80211); + free(lsta, M_LKPI80211); + if (lhw->ic_node_free != NULL) + lhw->ic_node_free(ni); + return (NULL); +} + +static int +lkpi_ic_node_init(struct ieee80211_node *ni) +{ + struct ieee80211com *ic; + struct lkpi_hw *lhw; + struct lkpi_sta *lsta; + struct lkpi_vif *lvif; + int error; + + ic = ni->ni_ic; + lhw = ic->ic_softc; + + if (lhw->ic_node_init != NULL) { + error = lhw->ic_node_init(ni); + if (error != 0) + return (error); + } + + lvif = VAP_TO_LVIF(ni->ni_vap); + + lsta = ni->ni_drv_data; + + /* Now take the reference before linking it to the table. */ + lsta->ni = ieee80211_ref_node(ni); + + LKPI_80211_LVIF_LOCK(lvif); + TAILQ_INSERT_TAIL(&lvif->lsta_head, lsta, lsta_entry); + LKPI_80211_LVIF_UNLOCK(lvif); + + /* XXX-BZ Sync other state over. */ + IMPROVE(); + + return (0); +} + +static void +lkpi_ic_node_cleanup(struct ieee80211_node *ni) +{ + struct ieee80211com *ic; + struct lkpi_hw *lhw; + + ic = ni->ni_ic; + lhw = ic->ic_softc; + + /* XXX-BZ remove from driver, ... */ + IMPROVE(); + + if (lhw->ic_node_cleanup != NULL) + lhw->ic_node_cleanup(ni); +} + +static void +lkpi_ic_node_free(struct ieee80211_node *ni) +{ + struct ieee80211com *ic; + struct lkpi_hw *lhw; + struct lkpi_sta *lsta; + + ic = ni->ni_ic; + lhw = ic->ic_softc; + lsta = ni->ni_drv_data; + + /* XXX-BZ free resources, ... */ + IMPROVE(); + + /* Flush mbufq (make sure to release ni refs!). */ +#ifdef __notyet__ + KASSERT(mbufq_len(&lsta->txq) == 0, ("%s: lsta %p has txq len %d != 0\n", + __func__, lsta, mbufq_len(&lsta->txq))); +#endif + /* Drain taskq. */ + + /* Drain sta->txq[] */ + mtx_destroy(&lsta->txq_mtx); + + /* Remove lsta if added_to_drv. */ + /* Remove lsta from vif */ + + /* remove ref from lsta node... */ + + if (lhw->ic_node_free != NULL) + lhw->ic_node_free(ni); + + /* Free lsta. */ +} + +static int +lkpi_ic_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, + const struct ieee80211_bpf_params *params __unused) +{ + struct lkpi_sta *lsta; + + lsta = ni->ni_drv_data; + + /* Queue the packet and enqueue the task to handle it. */ + LKPI_80211_LSTA_LOCK(lsta); + mbufq_enqueue(&lsta->txq, m); + LKPI_80211_LSTA_UNLOCK(lsta); + + if (debug_80211 & D80211_TRACE_TX) + printf("%s:%d lsta %p ni %p %6D mbuf_qlen %d\n", + __func__, __LINE__, lsta, ni, ni->ni_macaddr, ":", + mbufq_len(&lsta->txq)); + + taskqueue_enqueue(taskqueue_thread, &lsta->txq_task); + return (0); +} + +static void +lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m) +{ + struct ieee80211_node *ni; + struct ieee80211_frame *wh; + struct ieee80211_key *k; + struct sk_buff *skb; + struct ieee80211com *ic; + struct lkpi_hw *lhw; + struct ieee80211_hw *hw; + struct lkpi_vif *lvif; + struct ieee80211_vif *vif; + struct ieee80211_channel *c; + struct ieee80211_tx_control control; + struct ieee80211_tx_info *info; + struct ieee80211_sta *sta; + void *buf; + int ac; + + M_ASSERTPKTHDR(m); +#ifdef LINUXKPI_DEBUG_80211 + if (debug_80211 & D80211_TRACE_TX_DUMP) + hexdump(mtod(m, const void *), m->m_len, "RAW TX (plain) ", 0); +#endif + + ni = lsta->ni; +#ifndef TRY_HW_CRYPTO + /* Encrypt the frame if need be; XXX-BZ info->control.hw_key. */ + wh = mtod(m, struct ieee80211_frame *); + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + /* Retrieve key for TX && do software encryption. */ + k = ieee80211_crypto_encap(ni, m); + if (k == NULL) { + ieee80211_free_node(ni); + m_freem(m); + return; + } + } +#endif + + ic = ni->ni_ic; + lhw = ic->ic_softc; + hw = LHW_TO_HW(lhw); + c = ni->ni_chan; + + if (ieee80211_radiotap_active_vap(ni->ni_vap)) { + struct lkpi_radiotap_tx_hdr *rtap; + + rtap = &lhw->rtap_tx; + rtap->wt_flags = 0; + if (k != NULL) + rtap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; + if (m->m_flags & M_FRAG) + rtap->wt_flags |= IEEE80211_RADIOTAP_F_FRAG; + IMPROVE(); + rtap->wt_rate = 0; + if (c != NULL && c != IEEE80211_CHAN_ANYC) { + rtap->wt_chan_freq = htole16(c->ic_freq); + rtap->wt_chan_flags = htole16(c->ic_flags); + } + + ieee80211_radiotap_tx(ni->ni_vap, m); + } + + /* + * net80211 should handle hw->extra_tx_headroom. + * Though for as long as we are copying we don't mind. + */ + skb = dev_alloc_skb(hw->extra_tx_headroom + m->m_pkthdr.len); + if (skb == NULL) { + printf("XXX ERROR %s: skb alloc failed\n", __func__); + ieee80211_free_node(ni); + m_freem(m); + return; + } + skb_reserve(skb, hw->extra_tx_headroom); + + /* XXX-BZ we need a SKB version understanding mbuf. */ + /* Save the mbuf for ieee80211_tx_complete(). */ + skb->m_free_func = lkpi_ieee80211_free_skb_mbuf; + skb->m = m; +#if 0 + skb_put_data(skb, m->m_data, m->m_pkthdr.len); +#else + buf = skb_put(skb, m->m_pkthdr.len); + m_copydata(m, 0, m->m_pkthdr.len, buf); +#endif + /* Save the ni. */ + m->m_pkthdr.PH_loc.ptr = ni; + + lvif = VAP_TO_LVIF(ni->ni_vap); + vif = LVIF_TO_VIF(lvif); + + /* XXX-BZ review this at some point [4] vs. [8] vs. [16](TID). */ + ac = M_WME_GETAC(m); + skb->priority = WME_AC_TO_TID(ac); + ac = lkpi_ac_net_to_l80211(ac); + skb_set_queue_mapping(skb, ac); + + info = IEEE80211_SKB_CB(skb); + info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; + /* Slight delay; probably only happens on scanning so fine? */ + if (c == NULL || c == IEEE80211_CHAN_ANYC) + c = ic->ic_curchan; + info->band = lkpi_net80211_chan_to_nl80211_band(c); + info->hw_queue = ac; /* XXX-BZ is this correct? */ + if (m->m_flags & M_EAPOL) + info->control.flags |= IEEE80211_TX_CTRL_PORT_CTRL_PROTO; + info->control.vif = vif; + /* XXX-BZ info->control.rates */ + + lsta = lkpi_find_lsta_by_ni(lvif, ni); + if (lsta != NULL) { + sta = LSTA_TO_STA(lsta); +#ifdef TRY_HW_CRYPTO + info->control.hw_key = lsta->kc; +#endif + } else { + sta = NULL; + } + + IMPROVE(); + + if (sta != NULL) { + struct lkpi_txq *ltxq; + + ltxq = TXQ_TO_LTXQ(sta->txq[ac]); /* XXX-BZ re-check */ + /* + * We currently do not use queues but do direct TX. + * The exception to the rule is initial packets, as we cannot + * TX until queues are allocated (at least for iwlwifi). + * So we wake_tx_queue in newstate and register any dequeue + * calls. In the time until then we queue packets and + * let the driver deal with them. + */ + if (!ltxq->seen_dequeue) { + + /* Prevent an ordering problem, likely other issues. */ + while (!skb_queue_empty(<xq->skbq)) { + struct sk_buff *skb2; + + skb2 = skb_dequeue(<xq->skbq); + if (skb2 != NULL) { + memset(&control, 0, sizeof(control)); + control.sta = sta; + lkpi_80211_mo_tx(hw, &control, skb2); + } + } + goto ops_tx; + } + if (0 && ltxq->seen_dequeue && skb_queue_empty(<xq->skbq)) + goto ops_tx; + + skb_queue_tail(<xq->skbq, skb); + if (debug_80211 & D80211_TRACE_TX) + printf("%s:%d lsta %p sta %p ni %p %6D skb %p lxtq %p " + "qlen %u WAKE_TX_Q ac %d prio %u qmap %u\n", + __func__, __LINE__, lsta, sta, ni, + ni->ni_macaddr, ":", skb, ltxq, + skb_queue_len(<xq->skbq), ac, + skb->priority, skb->qmap); + lkpi_80211_mo_wake_tx_queue(hw, sta->txq[ac]); /* XXX-BZ */ + return; + } + +ops_tx: + if (debug_80211 & D80211_TRACE_TX) + printf("%s:%d lsta %p sta %p ni %p %6D skb %p TX ac %d prio %u qmap %u\n", + __func__, __LINE__, lsta, sta, ni, ni->ni_macaddr, ":", + skb, ac, skb->priority, skb->qmap); + memset(&control, 0, sizeof(control)); + control.sta = sta; + + lkpi_80211_mo_tx(hw, &control, skb); + return; +} + +static void +lkpi_80211_txq_task(void *ctx, int pending) +{ + struct lkpi_sta *lsta; + struct ieee80211_node *ni; + struct mbufq mq; + struct mbuf *m; + + lsta = ctx; + ni = lsta->ni; + + if (debug_80211 & D80211_TRACE_TX) + printf("%s:%d lsta %p ni %p %6D pending %d mbuf_qlen %d\n", + __func__, __LINE__, lsta, ni, ni->ni_macaddr, ":", + pending, mbufq_len(&lsta->txq)); + + mbufq_init(&mq, IFQ_MAXLEN); + + LKPI_80211_LSTA_LOCK(lsta); + mbufq_concat(&mq, &lsta->txq); + LKPI_80211_LSTA_UNLOCK(lsta); + + m = mbufq_dequeue(&mq); + while (m != NULL) { + lkpi_80211_txq_tx_one(lsta, m); + m = mbufq_dequeue(&mq); + } +} + +static int +lkpi_ic_transmit(struct ieee80211com *ic, struct mbuf *m) +{ + + /* XXX TODO */ + IMPROVE(); + + /* Quick and dirty cheating hack. */ + struct ieee80211_node *ni; + + ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; + return (lkpi_ic_raw_xmit(ni, m, NULL)); +} + +static void +lkpi_ic_getradiocaps(struct ieee80211com *ic, int maxchan, + int *n, struct ieee80211_channel *c) +{ + struct lkpi_hw *lhw; + struct ieee80211_hw *hw; + struct linuxkpi_ieee80211_channel *channels; + uint8_t bands[IEEE80211_MODE_BYTES]; + int chan_flags, error, i, nchans; + + /* Channels */ + lhw = ic->ic_softc; + hw = LHW_TO_HW(lhw); + + /* NL80211_BAND_2GHZ */ + nchans = 0; + if (hw->wiphy->bands[NL80211_BAND_2GHZ] != NULL) + nchans = hw->wiphy->bands[NL80211_BAND_2GHZ]->n_channels; + if (nchans > 0) { + memset(bands, 0, sizeof(bands)); + chan_flags = 0; + setbit(bands, IEEE80211_MODE_11B); + /* XXX-BZ unclear how to check for 11g. */ + setbit(bands, IEEE80211_MODE_11G); +#ifdef __notyet__ + if (hw->wiphy->bands[NL80211_BAND_2GHZ]->ht_cap.ht_supported) { + setbit(bands, IEEE80211_MODE_11NG); + chan_flags |= NET80211_CBW_FLAG_HT40; + } +#endif + + channels = hw->wiphy->bands[NL80211_BAND_2GHZ]->channels; + for (i = 0; i < nchans; i++) { + uint32_t nflags = 0; + int cflags = chan_flags; + + if (channels[i].flags & IEEE80211_CHAN_DISABLED) { + printf("%s: %s: Skipping disabled chan " + "[%u/%u/%#x]\n", ic->ic_name, __func__, + channels[i].hw_value, + channels[i].center_freq, channels[i].flags); + continue; + } + if (channels[i].flags & IEEE80211_CHAN_NO_IR) + nflags |= (IEEE80211_CHAN_NOADHOC|IEEE80211_CHAN_PASSIVE); + if (channels[i].flags & IEEE80211_CHAN_RADAR) + nflags |= IEEE80211_CHAN_DFS; + if (channels[i].flags & IEEE80211_CHAN_NO_160MHZ) + cflags &= ~(NET80211_CBW_FLAG_VHT160|NET80211_CBW_FLAG_VHT80P80); + if (channels[i].flags & IEEE80211_CHAN_NO_80MHZ) + cflags &= ~NET80211_CBW_FLAG_VHT80; + /* XXX how to map the remaining enum ieee80211_channel_flags? */ + if (channels[i].flags & IEEE80211_CHAN_NO_HT40) + cflags &= ~NET80211_CBW_FLAG_HT40; + + error = ieee80211_add_channel_cbw(c, maxchan, n, + channels[i].hw_value, channels[i].center_freq, + channels[i].max_power, + nflags, bands, chan_flags); + if (error != 0) { + printf("%s: %s: Adding chan %u/%u/%#x/%#x/%#x/%#x " + "returned error %d\n", ic->ic_name, + __func__, channels[i].hw_value, + channels[i].center_freq, channels[i].flags, + nflags, chan_flags, cflags, error); + break; + } + } + } + + /* NL80211_BAND_5GHZ */ + nchans = 0; + if (hw->wiphy->bands[NL80211_BAND_5GHZ] != NULL) + nchans = hw->wiphy->bands[NL80211_BAND_5GHZ]->n_channels; + if (nchans > 0) { + memset(bands, 0, sizeof(bands)); + chan_flags = 0; + setbit(bands, IEEE80211_MODE_11A); +#ifdef __not_yet__ + if (hw->wiphy->bands[NL80211_BAND_5GHZ]->ht_cap.ht_supported) { + setbit(bands, IEEE80211_MODE_11NA); + chan_flags |= NET80211_CBW_FLAG_HT40; + } + if (hw->wiphy->bands[NL80211_BAND_5GHZ]->vht_cap.vht_supported){ + + ic->ic_flags_ext |= IEEE80211_FEXT_VHT; + ic->ic_vhtcaps = + hw->wiphy->bands[NL80211_BAND_5GHZ]->vht_cap.cap; + + setbit(bands, IEEE80211_MODE_VHT_5GHZ); + chan_flags |= NET80211_CBW_FLAG_VHT80; + if (IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_IS_160MHZ( + ic->ic_vhtcaps)) + chan_flags |= NET80211_CBW_FLAG_VHT160; + if (IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_IS_160_80P80MHZ( + ic->ic_vhtcaps)) + chan_flags |= NET80211_CBW_FLAG_VHT80P80; + } +#endif + + channels = hw->wiphy->bands[NL80211_BAND_5GHZ]->channels; + for (i = 0; i < nchans; i++) { + uint32_t nflags = 0; + int cflags = chan_flags; + + if (channels[i].flags & IEEE80211_CHAN_DISABLED) { + printf("%s: %s: Skipping disabled chan " + "[%u/%u/%#x]\n", ic->ic_name, __func__, + channels[i].hw_value, + channels[i].center_freq, channels[i].flags); + continue; + } + if (channels[i].flags & IEEE80211_CHAN_NO_IR) + nflags |= (IEEE80211_CHAN_NOADHOC|IEEE80211_CHAN_PASSIVE); + if (channels[i].flags & IEEE80211_CHAN_RADAR) + nflags |= IEEE80211_CHAN_DFS; + if (channels[i].flags & IEEE80211_CHAN_NO_160MHZ) + cflags &= ~(NET80211_CBW_FLAG_VHT160|NET80211_CBW_FLAG_VHT80P80); + if (channels[i].flags & IEEE80211_CHAN_NO_80MHZ) + cflags &= ~NET80211_CBW_FLAG_VHT80; + /* XXX hwo to map the remaining enum ieee80211_channel_flags? */ + if (channels[i].flags & IEEE80211_CHAN_NO_HT40) + cflags &= ~NET80211_CBW_FLAG_HT40; + + error = ieee80211_add_channel_cbw(c, maxchan, n, + channels[i].hw_value, channels[i].center_freq, + channels[i].max_power, + nflags, bands, chan_flags); + if (error != 0) { + printf("%s: %s: Adding chan %u/%u/%#x/%#x/%#x/%#x " + "returned error %d\n", ic->ic_name, + __func__, channels[i].hw_value, + channels[i].center_freq, channels[i].flags, + nflags, chan_flags, cflags, error); + break; + } + } + } +} + +static void * +lkpi_ieee80211_ifalloc(void) +{ + struct ieee80211com *ic; + + ic = malloc(sizeof(*ic), M_LKPI80211, M_WAITOK | M_ZERO); + if (ic == NULL) + return (NULL); + + /* Setting these happens later when we have device information. */ + ic->ic_softc = NULL; + ic->ic_name = "linuxkpi"; + + return (ic); +} + +struct ieee80211_hw * +linuxkpi_ieee80211_alloc_hw(size_t priv_len, const struct ieee80211_ops *ops) +{ + struct ieee80211_hw *hw; + struct lkpi_hw *lhw; + struct wiphy *wiphy; + + /* Get us and the driver data also allocated. */ + wiphy = wiphy_new(&linuxkpi_mac80211cfgops, sizeof(*lhw) + priv_len); + if (wiphy == NULL) + return (NULL); + + lhw = wiphy_priv(wiphy); + lhw->ops = ops; + lhw->workq = alloc_ordered_workqueue(wiphy_name(wiphy), 0); + if (lhw->workq == NULL) { + wiphy_free(wiphy); + return (NULL); + } + mtx_init(&lhw->mtx, "lhw", NULL, MTX_DEF | MTX_RECURSE); + TAILQ_INIT(&lhw->lvif_head); + + /* + * XXX-BZ TODO make sure there is a "_null" function to all ops + * not initialized. + */ + hw = LHW_TO_HW(lhw); + hw->wiphy = wiphy; + hw->priv = (void *)(lhw + 1); + + /* BSD Specific. */ + lhw->ic = lkpi_ieee80211_ifalloc(); + if (lhw->ic == NULL) { + ieee80211_free_hw(hw); + return (NULL); + } + + IMPROVE(); + + return (hw); +} + +void +linuxkpi_ieee80211_iffree(struct ieee80211_hw *hw) +{ + struct lkpi_hw *lhw; + + lhw = HW_TO_LHW(hw); + free(lhw->ic, M_LKPI80211); + lhw->ic = NULL; + + /* Cleanup more of lhw here or in wiphy_free()? */ + mtx_destroy(&lhw->mtx); + IMPROVE(); +} + +void +linuxkpi_set_ieee80211_dev(struct ieee80211_hw *hw, char *name) +{ + struct lkpi_hw *lhw; + struct ieee80211com *ic; + + lhw = HW_TO_LHW(hw); + ic = lhw->ic; + + /* Now set a proper name before ieee80211_ifattach(). */ + ic->ic_softc = lhw; + ic->ic_name = name; + + /* XXX-BZ do we also need to set wiphy name? */ +} + +struct ieee80211_hw * +linuxkpi_wiphy_to_ieee80211_hw(struct wiphy *wiphy) +{ + struct lkpi_hw *lhw; + + lhw = wiphy_priv(wiphy); + return (LHW_TO_HW(lhw)); +} + +static void +lkpi_radiotap_attach(struct lkpi_hw *lhw) +{ + struct ieee80211com *ic; + + ic = lhw->ic; + ieee80211_radiotap_attach(ic, + &lhw->rtap_tx.wt_ihdr, sizeof(lhw->rtap_tx), + LKPI_RTAP_TX_FLAGS_PRESENT, + &lhw->rtap_rx.wr_ihdr, sizeof(lhw->rtap_rx), + LKPI_RTAP_RX_FLAGS_PRESENT); +} + +void +linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw) +{ + struct ieee80211com *ic; + struct lkpi_hw *lhw; +#ifdef TRY_HW_CRYPTO + int i; +#endif + + lhw = HW_TO_LHW(hw); + ic = lhw->ic; + + /* XXX-BZ figure this out how they count his... */ + if (!is_zero_ether_addr(hw->wiphy->perm_addr)) { + IEEE80211_ADDR_COPY(ic->ic_macaddr, + hw->wiphy->perm_addr); + } else if (hw->wiphy->n_addresses > 0) { + /* We take the first one. */ + IEEE80211_ADDR_COPY(ic->ic_macaddr, + hw->wiphy->addresses[0].addr); + } else { + ic_printf(ic, "%s: warning, no hardware address!\n", __func__); + } + + ic->ic_headroom = hw->extra_tx_headroom; + + ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ + ic->ic_opmode = IEEE80211_M_STA; + + /* Set device capabilities. */ + /* XXX-BZ we need to get these from linux80211/drivers and convert. */ + ic->ic_caps = + IEEE80211_C_STA | + IEEE80211_C_MONITOR | + IEEE80211_C_WPA | /* WPA/RSN */ + IEEE80211_C_WME | +#if 0 + IEEE80211_C_PMGT | +#endif + IEEE80211_C_SHSLOT | /* short slot time supported */ + IEEE80211_C_SHPREAMBLE /* short preamble supported */ + ; +#if 0 + /* Scanning is a different kind of beast to re-work. */ + ic->ic_caps |= IEEE80211_C_BGSCAN; +#endif + if (lhw->ops->hw_scan && + ieee80211_hw_check(hw, SINGLE_SCAN_ON_ALL_BANDS)) { + /* Advertise full-offload scanning */ + ic->ic_flags_ext |= IEEE80211_FEXT_SCAN_OFFLOAD; + } + +#ifdef __notyet__ + ic->ic_htcaps = IEEE80211_HTC_HT /* HT operation */ + | IEEE80211_HTC_AMPDU /* A-MPDU tx/rx */ + | IEEE80211_HTC_AMSDU /* A-MSDU tx/rx */ + | IEEE80211_HTCAP_MAXAMSDU_3839 + /* max A-MSDU length */ + | IEEE80211_HTCAP_SMPS_OFF; /* SM power save off */ + ic->ic_htcaps |= IEEE80211_HTCAP_SHORTGI20; + ic->ic_htcaps |= IEEE80211_HTCAP_CHWIDTH40 | IEEE80211_HTCAP_SHORTGI40; + ic->ic_htcaps |= IEEE80211_HTCAP_TXSTBC; +#endif + + ic->ic_cryptocaps = 0; +#ifdef TRY_HW_CRYPTO + if (hw->wiphy->n_cipher_suites > 0) { + for (i = 0; i < hw->wiphy->n_cipher_suites; i++) + ic->ic_cryptocaps |= lkpi_l80211_to_net80211_cyphers( + hw->wiphy->cipher_suites[i]); + } +#endif + + lkpi_ic_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans, + ic->ic_channels); + + ieee80211_ifattach(ic); + + ic->ic_update_mcast = lkpi_ic_update_mcast; + ic->ic_update_promisc = lkpi_ic_update_promisc; + ic->ic_update_chw = lkpi_ic_update_chw; + ic->ic_parent = lkpi_ic_parent; + ic->ic_scan_start = lkpi_ic_scan_start; + ic->ic_scan_end = lkpi_ic_scan_end; + if (lhw->ops->hw_scan && + ieee80211_hw_check(hw, SINGLE_SCAN_ON_ALL_BANDS)) + ic->ic_scan_curchan = lkpi_ic_scan_curchan_nada; + ic->ic_set_channel = lkpi_ic_set_channel; + ic->ic_transmit = lkpi_ic_transmit; + ic->ic_raw_xmit = lkpi_ic_raw_xmit; + ic->ic_vap_create = lkpi_ic_vap_create; + ic->ic_vap_delete = lkpi_ic_vap_delete; + ic->ic_getradiocaps = lkpi_ic_getradiocaps; + ic->ic_wme.wme_update = lkpi_ic_wme_update; + + lhw->ic_node_alloc = ic->ic_node_alloc; + ic->ic_node_alloc = lkpi_ic_node_alloc; + lhw->ic_node_init = ic->ic_node_init; + ic->ic_node_init = lkpi_ic_node_init; + lhw->ic_node_cleanup = ic->ic_node_cleanup; + ic->ic_node_cleanup = lkpi_ic_node_cleanup; + lhw->ic_node_free = ic->ic_node_free; + ic->ic_node_free = lkpi_ic_node_free; + + lkpi_radiotap_attach(lhw); + + if (bootverbose) + ieee80211_announce(ic); +} + +void +linuxkpi_ieee80211_ifdetach(struct ieee80211_hw *hw) +{ + struct lkpi_hw *lhw; + struct ieee80211com *ic; + + lhw = HW_TO_LHW(hw); + ic = lhw->ic; + ieee80211_ifdetach(ic); +} + +void +linuxkpi_ieee80211_iterate_interfaces(struct ieee80211_hw *hw, + enum ieee80211_iface_iter flags, + void(*iterfunc)(void *, uint8_t *, struct ieee80211_vif *), + void *arg) +{ + struct lkpi_hw *lhw; + struct lkpi_vif *lvif; + struct ieee80211_vif *vif; + bool active, atomic; + + lhw = HW_TO_LHW(hw); + + if (flags & ~(IEEE80211_IFACE_ITER_NORMAL| + IEEE80211_IFACE_ITER_RESUME_ALL| + IEEE80211_IFACE_ITER__ACTIVE|IEEE80211_IFACE_ITER__ATOMIC)) { + ic_printf(lhw->ic, "XXX TODO %s flags(%#x) not yet supported.\n", + __func__, flags); + } + + active = (flags & IEEE80211_IFACE_ITER__ACTIVE) != 0; + atomic = (flags & IEEE80211_IFACE_ITER__ATOMIC) != 0; + + if (atomic) + LKPI_80211_LHW_LOCK(lhw); + TAILQ_FOREACH(lvif, &lhw->lvif_head, lvif_entry) { + struct ieee80211vap *vap; + + vif = LVIF_TO_VIF(lvif); + + /* + * If we want "active" interfaces, we need to distinguish on + * whether the driver knows about them or not to be able to + * handle the "resume" case correctly. Skip the ones the + * driver does not know about. + */ + if (active && !lvif->added_to_drv && + (flags & IEEE80211_IFACE_ITER_RESUME_ALL) != 0) + continue; + + /* + * Run the iterator function if we are either not asking + * asking for active only or if the VAP is "running". + */ + /* XXX-BZ probably should have state in the lvif as well. */ + vap = LVIF_TO_VAP(lvif); + if (!active || (vap->iv_state != IEEE80211_S_INIT)) + iterfunc(arg, vif->addr, vif); + } + if (atomic) + LKPI_80211_LHW_UNLOCK(lhw); +} + +void +linuxkpi_ieee80211_iterate_keys(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + void(*iterfunc)(struct ieee80211_hw *, struct ieee80211_vif *, + struct ieee80211_sta *, struct ieee80211_key_conf *, void *), + void *arg) +{ + + UNIMPLEMENTED; +} + +void +linuxkpi_ieee80211_iterate_chan_contexts(struct ieee80211_hw *hw, + void(*iterfunc)(struct ieee80211_hw *, struct ieee80211_chanctx_conf *, + void *), + void *arg) +{ + + UNIMPLEMENTED; +} + +void +linuxkpi_ieee80211_iterate_stations_atomic(struct ieee80211_hw *hw, + void (*iterfunc)(void *, struct ieee80211_sta *), void *arg) +{ + struct lkpi_hw *lhw; + struct lkpi_vif *lvif; + struct lkpi_sta *lsta; + struct ieee80211_sta *sta; + + KASSERT(hw != NULL && iterfunc != NULL, + ("%s: hw %p iterfunc %p arg %p\n", __func__, hw, iterfunc, arg)); + + lhw = HW_TO_LHW(hw); + + LKPI_80211_LHW_LOCK(lhw); + TAILQ_FOREACH(lvif, &lhw->lvif_head, lvif_entry) { + + LKPI_80211_LVIF_LOCK(lvif); + TAILQ_FOREACH(lsta, &lvif->lsta_head, lsta_entry) { + if (!lsta->added_to_drv) + continue; + sta = LSTA_TO_STA(lsta); + iterfunc(arg, sta); + } + LKPI_80211_LVIF_UNLOCK(lvif); + } + LKPI_80211_LHW_UNLOCK(lhw); +} + +int +linuxkpi_regulatory_set_wiphy_regd_sync(struct wiphy *wiphy, + struct linuxkpi_ieee80211_regdomain *regd) +{ + struct lkpi_hw *lhw; + struct ieee80211com *ic; + struct ieee80211_regdomain *rd; + + lhw = wiphy_priv(wiphy); + ic = lhw->ic; + + rd = &ic->ic_regdomain; + if (rd->isocc[0] == '\0') { + rd->isocc[0] = regd->alpha2[0]; + rd->isocc[1] = regd->alpha2[1]; + } + + TODO(); + /* XXX-BZ finish the rest. */ + + return (0); +} + +void +linuxkpi_ieee80211_scan_completed(struct ieee80211_hw *hw, + struct cfg80211_scan_info *info) +{ + struct lkpi_hw *lhw; + struct ieee80211com *ic; + struct ieee80211_scan_state *ss; + + lhw = wiphy_priv(hw->wiphy); + ic = lhw->ic; + ss = ic->ic_scan; + + ieee80211_scan_done(ss->ss_vap); + + LKPI_80211_LHW_LOCK(lhw); + free(lhw->hw_req->ies.common_ies, M_80211_VAP); + free(lhw->hw_req, M_LKPI80211); + lhw->hw_req = NULL; + lhw->scan_flags &= ~LKPI_SCAN_RUNNING; + wakeup(lhw); + LKPI_80211_LHW_UNLOCK(lhw); + + return; +} + +void +linuxkpi_ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ieee80211_sta *sta, struct napi_struct *napi __unused) +{ + struct epoch_tracker et; + struct lkpi_hw *lhw; + struct ieee80211com *ic; + struct mbuf *m; + struct skb_shared_info *shinfo; + struct ieee80211_rx_status *rx_status; + struct ieee80211_rx_stats rx_stats; + struct ieee80211_node *ni; + struct ieee80211vap *vap; + struct ieee80211_frame_min *wh; + struct ieee80211_hdr *hdr; + struct lkpi_sta *lsta; + int i, offset, ok, type; + + if (skb->len < 2) { + /* Need 80211 stats here. */ + IMPROVE(); + goto err; + } + + /* + * For now do the data copy; we can later improve things. Might even + * have an mbuf backing the skb data then? + */ + m = m_get2(skb->len, M_NOWAIT, MT_DATA, M_PKTHDR); + if (m == NULL) + goto err; + m_copyback(m, 0, skb->tail - skb->data, skb->data); + + shinfo = skb_shinfo(skb); + offset = m->m_len; + for (i = 0; i < shinfo->nr_frags; i++) { + m_copyback(m, offset, shinfo->frags[i].size, + (uint8_t *)linux_page_address(shinfo->frags[i].page) + + shinfo->frags[i].offset); + offset += shinfo->frags[i].size; + } + + rx_status = IEEE80211_SKB_RXCB(skb); + +#ifdef LINUXKPI_DEBUG_80211 + hdr = (void *)skb->data; + if ((debug_80211 & D80211_TRACE_RX_BEACONS) == 0 && + ieee80211_is_beacon(hdr->frame_control)) + goto no_trace_beacons; + + if (debug_80211 & D80211_TRACE_RX) + printf("TRACE-RX: %s: skb %p a/l/d/t-len (%u/%u/%u/%u) " + "h %p d %p t %p e %p sh %p (%u) m %p plen %u len %u\n", + __func__, skb, skb->_alloc_len, skb->len, skb->data_len, + skb->truesize, skb->head, skb->data, skb->tail, skb->end, + shinfo, shinfo->nr_frags, + m, m->m_pkthdr.len, m->m_len); + + if (debug_80211 & D80211_TRACE_RX_DUMP) + hexdump(mtod(m, const void *), m->m_len, "RX (raw) ", 0); + + /* Implement a dump_rxcb() !!! */ + if (debug_80211 & D80211_TRACE_RX) + printf("TRACE %s: RXCB: %u %u %u, %#0x, %u, %#0x, %#0x, " + "%u band %u, %u %u %u %u, %u, %#x %#x %#x %#x %u %u %u\n", + __func__, + rx_status->boottime_ns, + rx_status->mactime, + rx_status->device_timestamp, + rx_status->flag, + rx_status->freq, + rx_status->bw, + rx_status->encoding, + rx_status->ampdu_reference, + rx_status->band, + rx_status->chains, + rx_status->chain_signal[0], + rx_status->chain_signal[1], + rx_status->chain_signal[2], + rx_status->signal, + rx_status->enc_flags, + rx_status->he_dcm, + rx_status->he_gi, + rx_status->he_ru, + rx_status->zero_length_psdu_type, + rx_status->nss, + rx_status->rate_idx); +no_trace_beacons: +#endif + + memset(&rx_stats, 0, sizeof(rx_stats)); + rx_stats.r_flags = IEEE80211_R_NF | IEEE80211_R_RSSI; + if (ieee80211_hw_check(hw, SIGNAL_DBM) && + !(rx_status->flag & RX_FLAG_NO_SIGNAL_VAL)) + rx_stats.c_rssi = rx_status->signal; + else + rx_stats.c_rssi = 0; /* XXX */ + rx_stats.c_nf = -96; /* XXX */ + rx_stats.r_flags |= IEEE80211_R_BAND; + rx_stats.c_band = + lkpi_nl80211_band_to_net80211_band(rx_status->band); + rx_stats.r_flags |= IEEE80211_R_FREQ | IEEE80211_R_IEEE; + rx_stats.c_freq = rx_status->freq; + rx_stats.c_ieee = ieee80211_mhz2ieee(rx_stats.c_freq, rx_stats.c_band); + + /* XXX-BZ correct hardcoded rssi and noise floor. */ + /* XXX (*sta_statistics)() to get to some of that? */ + /* XXX-BZ dump the FreeBSD version of rx_stats as well! */ + + lhw = HW_TO_LHW(hw); + ic = lhw->ic; + + ok = ieee80211_add_rx_params(m, &rx_stats); + if (ok == 0) { + counter_u64_add(ic->ic_ierrors, 1); + goto err; + } + + if (sta != NULL) { + lsta = STA_TO_LSTA(sta); + ni = ieee80211_ref_node(lsta->ni); + } else { + wh = mtod(m, struct ieee80211_frame_min *); + ni = ieee80211_find_rxnode(ic, wh); + if (ni != NULL) + lsta = ni->ni_drv_data; + } + + if (ni != NULL) + vap = ni->ni_vap; + else + /* + * XXX-BZ can we improve this by looking at the frame hdr + * or other meta-data passed up? + */ + vap = TAILQ_FIRST(&ic->ic_vaps); + + if (debug_80211 & D80211_TRACE_RX) + printf("TRACE %s: sta %p lsta %p ni %p vap %p\n", __func__, sta, lsta, ni, vap); + + if (vap != NULL && vap->iv_state > IEEE80211_S_INIT && + ieee80211_radiotap_active_vap(vap)) { + struct lkpi_radiotap_rx_hdr *rtap; + + rtap = &lhw->rtap_rx; + rtap->wr_tsft = rx_status->device_timestamp; + rtap->wr_flags = 0; + if (rx_status->enc_flags & RX_ENC_FLAG_SHORTPRE) + rtap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; + if (rx_status->enc_flags & RX_ENC_FLAG_SHORT_GI) + rtap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTGI; +#if 0 /* .. or it does not given we strip it below. */ + if (ieee80211_hw_check(hw, RX_INCLUDES_FCS)) + rtap->wr_flags |= IEEE80211_RADIOTAP_F_FCS; +#endif + if (rx_status->flag & RX_FLAG_FAILED_FCS_CRC) + rtap->wr_flags |= IEEE80211_RADIOTAP_F_BADFCS; + rtap->wr_rate = 0; + IMPROVE(); + /* XXX TODO status->encoding / rate_index / bw */ + rtap->wr_chan_freq = htole16(rx_stats.c_freq); + if (ic->ic_curchan->ic_ieee == rx_stats.c_ieee) + rtap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); + rtap->wr_dbm_antsignal = rx_stats.c_rssi; + rtap->wr_dbm_antnoise = rx_stats.c_nf; + } + + if (ieee80211_hw_check(hw, RX_INCLUDES_FCS)) + m_adj(m, -IEEE80211_CRC_LEN); + + NET_EPOCH_ENTER(et); + if (ni != NULL) { + type = ieee80211_input_mimo(ni, m); + ieee80211_free_node(ni); + } else { + type = ieee80211_input_mimo_all(ic, m); + } + NET_EPOCH_EXIT(et); + + if (debug_80211 & D80211_TRACE_RX) + printf("TRACE %s: handled frame type %#0x\n", __func__, type); + + IMPROVE(); + +err: + /* The skb is ours so we can free it :-) */ + kfree_skb(skb); +} + +uint8_t +linuxkpi_ieee80211_get_tid(struct ieee80211_hdr *hdr) +{ + const struct ieee80211_frame *wh; + + wh = (const struct ieee80211_frame *)hdr; + return (ieee80211_gettid(wh)); +} + +struct wiphy * +linuxkpi_wiphy_new(const struct cfg80211_ops *ops, size_t priv_len) +{ + struct lkpi_wiphy *lwiphy; + + lwiphy = kzalloc(sizeof(*lwiphy) + priv_len, GFP_KERNEL); + if (lwiphy == NULL) + return (NULL); + lwiphy->ops = ops; + + /* XXX TODO */ + return (LWIPHY_TO_WIPHY(lwiphy)); +} + +void +linuxkpi_wiphy_free(struct wiphy *wiphy) +{ + struct lkpi_wiphy *lwiphy; + + if (wiphy == NULL) + return; + + lwiphy = WIPHY_TO_LWIPHY(wiphy); + kfree(lwiphy); +} + +uint32_t +linuxkpi_ieee80211_channel_to_frequency(uint32_t channel, + enum nl80211_band band) +{ + + switch (band) { + case NL80211_BAND_2GHZ: + return (ieee80211_ieee2mhz(channel, IEEE80211_CHAN_2GHZ)); + break; + case NL80211_BAND_5GHZ: + return (ieee80211_ieee2mhz(channel, IEEE80211_CHAN_5GHZ)); + break; + default: + /* XXX abort, retry, error, panic? */ + break; + } + + return (0); +} + +uint32_t +linuxkpi_ieee80211_frequency_to_channel(uint32_t freq, uint32_t flags __unused) +{ + + return (ieee80211_mhz2ieee(freq, 0)); +} + +static struct lkpi_sta * +lkpi_find_lsta_by_ni(struct lkpi_vif *lvif, struct ieee80211_node *ni) +{ + struct lkpi_sta *lsta, *temp; + + LKPI_80211_LVIF_LOCK(lvif); + TAILQ_FOREACH_SAFE(lsta, &lvif->lsta_head, lsta_entry, temp) { + if (lsta->ni == ni) { + LKPI_80211_LVIF_UNLOCK(lvif); + return (lsta); + } + } + LKPI_80211_LVIF_UNLOCK(lvif); + + return (NULL); +} + +struct ieee80211_sta * +linuxkpi_ieee80211_find_sta(struct ieee80211_vif *vif, const u8 *peer) +{ + struct lkpi_vif *lvif; + struct lkpi_sta *lsta, *temp; + struct ieee80211_sta *sta; + + lvif = VIF_TO_LVIF(vif); + + LKPI_80211_LVIF_LOCK(lvif); + TAILQ_FOREACH_SAFE(lsta, &lvif->lsta_head, lsta_entry, temp) { + sta = LSTA_TO_STA(lsta); + if (IEEE80211_ADDR_EQ(sta->addr, peer)) { + LKPI_80211_LVIF_UNLOCK(lvif); + return (sta); + } + } + LKPI_80211_LVIF_UNLOCK(lvif); + return (NULL); +} + +struct ieee80211_sta * +linuxkpi_ieee80211_find_sta_by_ifaddr(struct ieee80211_hw *hw, uint8_t *addr, + uint8_t *ourvifaddr) +{ + struct lkpi_hw *lhw; + struct lkpi_vif *lvif; + struct lkpi_sta *lsta; + struct ieee80211_vif *vif; + struct ieee80211_sta *sta; + + lhw = wiphy_priv(hw->wiphy); + sta = NULL; + + LKPI_80211_LHW_LOCK(lhw); + TAILQ_FOREACH(lvif, &lhw->lvif_head, lvif_entry) { + + /* XXX-BZ check our address from the vif. */ + + vif = LVIF_TO_VIF(lvif); + if (ourvifaddr != NULL && + !IEEE80211_ADDR_EQ(vif->addr, ourvifaddr)) + continue; + sta = linuxkpi_ieee80211_find_sta(vif, addr); + if (sta != NULL) + break; + } + LKPI_80211_LHW_UNLOCK(lhw); + + if (sta != NULL) { + lsta = STA_TO_LSTA(sta); + if (!lsta->added_to_drv) + return (NULL); + } + + return (sta); +} + +struct sk_buff * +linuxkpi_ieee80211_tx_dequeue(struct ieee80211_hw *hw, + struct ieee80211_txq *txq) +{ + struct lkpi_txq *ltxq; + struct sk_buff *skb; + + ltxq = TXQ_TO_LTXQ(txq); + ltxq->seen_dequeue = true; + + skb = skb_dequeue(<xq->skbq); + + return (skb); +} + +void +linuxkpi_ieee80211_txq_get_depth(struct ieee80211_txq *txq, + uint64_t *frame_cnt, uint64_t *byte_cnt) +{ + struct lkpi_txq *ltxq; + struct sk_buff *skb; + uint64_t fc, bc; + + ltxq = TXQ_TO_LTXQ(txq); + + fc = bc = 0; + skb_queue_walk(<xq->skbq, skb) { + fc++; + bc += skb->len; + } + if (frame_cnt) + *frame_cnt = fc; + if (byte_cnt) + *byte_cnt = bc; + + /* Validate that this is doing the correct thing. */ + /* Should we keep track on en/dequeue? */ + IMPROVE(); +} + +/* + * We are called from ieee80211_free_txskb() or ieee80211_tx_status(). + * The latter tries to derive the success status from the info flags + * passed back from the driver. rawx_mit() saves the ni on the m and the + * m on the skb for us to be able to give feedback to net80211. + */ +void +linuxkpi_ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb, + int status) +{ + struct ieee80211_node *ni; + struct mbuf *m; + + m = skb->m; + skb->m = NULL; + + if (m != NULL) { + ni = m->m_pkthdr.PH_loc.ptr; + /* Status: 0 is ok, != 0 is error. */ + ieee80211_tx_complete(ni, m, status); + /* ni & mbuf were consumed. */ + } + + kfree_skb(skb); +} + +/* + * This is an internal bandaid for the moment for the way we glue + * skbs and mbufs together for TX. Once we have skbs backed by + * mbufs this should go away. + * This is a public function but kept on the private KPI (lkpi_) + * and is not exposed by a header file. + */ +static void +lkpi_ieee80211_free_skb_mbuf(void *p) +{ + struct ieee80211_node *ni; + struct mbuf *m; + + if (p == NULL) + return; + + m = (struct mbuf *)p; + M_ASSERTPKTHDR(m); + + ni = m->m_pkthdr.PH_loc.ptr; + m->m_pkthdr.PH_loc.ptr = NULL; + if (ni != NULL) + ieee80211_free_node(ni); + m_freem(m); +} + +void +linuxkpi_ieee80211_queue_delayed_work(struct ieee80211_hw *hw, + struct delayed_work *w, int delay) +{ + struct lkpi_hw *lhw; + + /* Need to make sure hw is in a stable (non-suspended) state. */ + IMPROVE(); + + lhw = HW_TO_LHW(hw); + queue_delayed_work(lhw->workq, w, delay); +} + +void +linuxkpi_ieee80211_queue_work(struct ieee80211_hw *hw, + struct work_struct *w) +{ + struct lkpi_hw *lhw; + + /* Need to make sure hw is in a stable (non-suspended) state. */ + IMPROVE(); + + lhw = HW_TO_LHW(hw); + queue_work(lhw->workq, w); +} + +struct sk_buff * +linuxkpi_ieee80211_pspoll_get(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct lkpi_vif *lvif; + struct ieee80211vap *vap; + struct sk_buff *skb; + struct ieee80211_frame_pspoll *psp; + uint16_t v; + + skb = dev_alloc_skb(hw->extra_tx_headroom + sizeof(*psp)); + if (skb == NULL) + return (NULL); + + skb_reserve(skb, hw->extra_tx_headroom); + + lvif = VIF_TO_LVIF(vif); + vap = LVIF_TO_VAP(lvif); + + psp = skb_put_zero(skb, sizeof(*psp)); + psp->i_fc[0] = IEEE80211_FC0_VERSION_0; + psp->i_fc[0] |= IEEE80211_FC0_SUBTYPE_PS_POLL | IEEE80211_FC0_TYPE_CTL; + v = htole16(vif->bss_conf.aid | 1<<15 | 1<<16); + memcpy(&psp->i_aid, &v, sizeof(v)); + IEEE80211_ADDR_COPY(psp->i_bssid, vap->iv_bss->ni_macaddr); + IEEE80211_ADDR_COPY(psp->i_ta, vif->addr); + + return (skb); +} + +struct sk_buff * +linuxkpi_ieee80211_nullfunc_get(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, bool qos) +{ + struct lkpi_vif *lvif; + struct ieee80211vap *vap; + struct sk_buff *skb; + struct ieee80211_frame *nullf; + + skb = dev_alloc_skb(hw->extra_tx_headroom + sizeof(*nullf)); + if (skb == NULL) + return (NULL); + + skb_reserve(skb, hw->extra_tx_headroom); + + lvif = VIF_TO_LVIF(vif); + vap = LVIF_TO_VAP(lvif); + + nullf = skb_put_zero(skb, sizeof(*nullf)); + nullf->i_fc[0] = IEEE80211_FC0_VERSION_0; + nullf->i_fc[0] |= IEEE80211_FC0_SUBTYPE_NODATA | IEEE80211_FC0_TYPE_DATA; + nullf->i_fc[1] = IEEE80211_FC1_DIR_TODS; + + IEEE80211_ADDR_COPY(nullf->i_addr1, vap->iv_bss->ni_bssid); + IEEE80211_ADDR_COPY(nullf->i_addr2, vif->addr); + IEEE80211_ADDR_COPY(nullf->i_addr3, vap->iv_bss->ni_macaddr); + + return (skb); +} + +struct wireless_dev * +linuxkpi_ieee80211_vif_to_wdev(struct ieee80211_vif *vif) +{ + struct lkpi_vif *lvif; + + lvif = VIF_TO_LVIF(vif); + return (&lvif->wdev); +} + +void +linuxkpi_ieee80211_connection_loss(struct ieee80211_vif *vif) +{ + struct lkpi_vif *lvif; + struct ieee80211vap *vap; + enum ieee80211_state nstate; + int arg; + + lvif = VIF_TO_LVIF(vif); + vap = LVIF_TO_VAP(lvif); + + /* + * Go to scan; otherwise we need to elaborately check state and + * handle accordingly, e.g., if in RUN we could call iv_bmiss. + * Let the statemachine handle all neccessary changes. + */ + nstate = IEEE80211_S_SCAN; + arg = 0; + + if (debug_80211 & D80211_TRACE) + ic_printf(vap->iv_ic, "%s: vif %p\n", __func__, vif); + ieee80211_new_state(vap, nstate, arg); +} + +MODULE_VERSION(linuxkpi_wlan, 1); +MODULE_DEPEND(linuxkpi_wlan, linuxkpi, 1, 1, 1); +MODULE_DEPEND(linuxkpi_wlan, wlan, 1, 1, 1); diff --git a/sys/compat/linuxkpi/common/src/linux_80211.h b/sys/compat/linuxkpi/common/src/linux_80211.h new file mode 100644 index 00000000000..9538a128420 --- /dev/null +++ b/sys/compat/linuxkpi/common/src/linux_80211.h @@ -0,0 +1,235 @@ +/*- + * Copyright (c) 2020-2021 The FreeBSD Foundation + * Copyright (c) 2020-2021 Bjoern A. Zeeb + * + * This software was developed by Björn Zeeb under sponsorship from + * the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Public functions are called linuxkpi_*(). + * Internal (static) functions are called lkpi_*(). + * + * The internal structures holding metadata over public structures are also + * called lkpi_xxx (usually with a member at the end called xxx). + * Note: we do not replicate the structure names but the general variable names + * for these (e.g., struct hw -> struct lkpi_hw, struct sta -> struct lkpi_sta). + * There are macros to access one from the other. + * We call the internal versions lxxx (e.g., hw -> lhw, sta -> lsta). + */ + +#ifndef _LKPI_SRC_LINUX_80211_H +#define _LKPI_SRC_LINUX_80211_H + +struct lkpi_radiotap_tx_hdr { + struct ieee80211_radiotap_header wt_ihdr; + uint8_t wt_flags; + uint8_t wt_rate; + uint16_t wt_chan_freq; + uint16_t wt_chan_flags; +} __packed; +#define LKPI_RTAP_TX_FLAGS_PRESENT \ + ((1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL)) + +struct lkpi_radiotap_rx_hdr { + struct ieee80211_radiotap_header wr_ihdr; + uint64_t wr_tsft; + uint8_t wr_flags; + uint8_t wr_rate; + uint16_t wr_chan_freq; + uint16_t wr_chan_flags; + int8_t wr_dbm_antsignal; + int8_t wr_dbm_antnoise; +} __packed __aligned(8); +#define LKPI_RTAP_RX_FLAGS_PRESENT \ + ((1 << IEEE80211_RADIOTAP_TSFT) | \ + (1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL) | \ + (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \ + (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE)) + +struct lkpi_txq { + bool seen_dequeue; + struct sk_buff_head skbq; + + /* Must be last! */ + struct ieee80211_txq txq __aligned(CACHE_LINE_SIZE); +}; +#define TXQ_TO_LTXQ(_txq) container_of(_txq, struct lkpi_txq, txq) + + +struct lkpi_sta { + TAILQ_ENTRY(lkpi_sta) lsta_entry; + struct ieee80211_node *ni; + + /* Deferred TX path. */ + /* Eventually we might want to migrate this into net80211 entirely. */ + /* XXX-BZ can we use sta->txq[] instead directly? */ + struct task txq_task; + struct mbufq txq; + struct mtx txq_mtx; + + struct ieee80211_key_conf *kc; + enum ieee80211_sta_state state; + bool added_to_drv; /* Driver knows; i.e. we called ...(). */ + bool in_mgd; + + /* Must be last! */ + struct ieee80211_sta sta __aligned(CACHE_LINE_SIZE); +}; +#define STA_TO_LSTA(_sta) container_of(_sta, struct lkpi_sta, sta) +#define LSTA_TO_STA(_lsta) (&(_lsta)->sta) + +struct lkpi_vif { + TAILQ_ENTRY(lkpi_vif) lvif_entry; + struct ieee80211vap iv_vap; + + struct mtx mtx; + struct wireless_dev wdev; + + /* Other local stuff. */ + int (*iv_newstate)(struct ieee80211vap *, + enum ieee80211_state, int); + TAILQ_HEAD(, lkpi_sta) lsta_head; + bool added_to_drv; /* Driver knows; i.e. we called add_interface(). */ + + /* Must be last! */ + struct ieee80211_vif vif __aligned(CACHE_LINE_SIZE); +}; +#define VAP_TO_LVIF(_vap) container_of(_vap, struct lkpi_vif, iv_vap) +#define LVIF_TO_VAP(_lvif) (&(_lvif)->iv_vap) +#define VIF_TO_LVIF(_vif) container_of(_vif, struct lkpi_vif, vif) +#define LVIF_TO_VIF(_lvif) (&(_lvif)->vif) + + +struct lkpi_hw { /* name it mac80211_sc? */ + const struct ieee80211_ops *ops; + struct ieee80211_scan_request *hw_req; + struct workqueue_struct *workq; + + /* FreeBSD specific compat. */ + /* Linux device is in hw.wiphy->dev after SET_IEEE80211_DEV(). */ + struct ieee80211com *ic; + struct lkpi_radiotap_tx_hdr rtap_tx; + struct lkpi_radiotap_rx_hdr rtap_rx; + + TAILQ_HEAD(, lkpi_vif) lvif_head; + + struct mtx mtx; + + /* Node functions we overload to sync state. */ + struct ieee80211_node * (*ic_node_alloc)(struct ieee80211vap *, + const uint8_t [IEEE80211_ADDR_LEN]); + int (*ic_node_init)(struct ieee80211_node *); + void (*ic_node_cleanup)(struct ieee80211_node *); + void (*ic_node_free)(struct ieee80211_node *); + +#define LKPI_MAC80211_DRV_STARTED 0x00000001 + uint32_t sc_flags; +#define LKPI_SCAN_RUNNING 0x00000001 + uint32_t scan_flags; + bool update_mc; + + /* Must be last! */ + struct ieee80211_hw hw __aligned(CACHE_LINE_SIZE); +}; +#define LHW_TO_HW(_lhw) (&(_lhw)->hw) +#define HW_TO_LHW(_hw) container_of(_hw, struct lkpi_hw, hw) + +struct lkpi_wiphy { + const struct cfg80211_ops *ops; + + /* Must be last! */ + struct wiphy wiphy __aligned(CACHE_LINE_SIZE); +}; +#define WIPHY_TO_LWIPHY(_wiphy) container_of(_wiphy, struct lkpi_wiphy, wiphy) +#define LWIPHY_TO_WIPHY(_lwiphy) (&(_lwiphy)->wiphy) + + +#define LKPI_80211_LHW_LOCK(_lhw) mtx_lock(&(_lhw)->mtx) +#define LKPI_80211_LHW_UNLOCK(_lhw) mtx_unlock(&(_lhw)->mtx) +#define LKPI_80211_LHW_LOCK_ASSERT(_lhw) \ + mtx_assert(&(_lhw)->mtx, MA_OWNED) +#define LKPI_80211_LHW_UNLOCK_ASSERT(_lhw) \ + mtx_assert(&(_lhw)->mtx, MA_NOTOWNED) + +#define LKPI_80211_LVIF_LOCK(_lvif) mtx_lock(&(_lvif)->mtx) +#define LKPI_80211_LVIF_UNLOCK(_lvif) mtx_unlock(&(_lvif)->mtx) + +#define LKPI_80211_LSTA_LOCK(_lsta) mtx_lock(&(_lsta)->txq_mtx) +#define LKPI_80211_LSTA_UNLOCK(_lsta) mtx_unlock(&(_lsta)->txq_mtx) + + +int lkpi_80211_mo_start(struct ieee80211_hw *); +void lkpi_80211_mo_stop(struct ieee80211_hw *); +int lkpi_80211_mo_set_frag_threshold(struct ieee80211_hw *, uint32_t); +int lkpi_80211_mo_set_rts_threshold(struct ieee80211_hw *, uint32_t); +int lkpi_80211_mo_add_interface(struct ieee80211_hw *, struct ieee80211_vif *); +void lkpi_80211_mo_remove_interface(struct ieee80211_hw *, struct ieee80211_vif *); +int lkpi_80211_mo_hw_scan(struct ieee80211_hw *, struct ieee80211_vif *, + struct ieee80211_scan_request *); +void lkpi_80211_mo_cancel_hw_scan(struct ieee80211_hw *, struct ieee80211_vif *); +void lkpi_80211_mo_sw_scan_complete(struct ieee80211_hw *, struct ieee80211_vif *); +void lkpi_80211_mo_sw_scan_start(struct ieee80211_hw *, struct ieee80211_vif *, + const u8 *); +u64 lkpi_80211_mo_prepare_multicast(struct ieee80211_hw *, + struct netdev_hw_addr_list *); +void lkpi_80211_mo_configure_filter(struct ieee80211_hw *, unsigned int, + unsigned int *, u64); +int lkpi_80211_mo_sta_state(struct ieee80211_hw *, struct ieee80211_vif *, + struct ieee80211_sta *, enum ieee80211_sta_state); +int lkpi_80211_mo_config(struct ieee80211_hw *, uint32_t); +int lkpi_80211_mo_assign_vif_chanctx(struct ieee80211_hw *, struct ieee80211_vif *, + struct ieee80211_chanctx_conf *); +void lkpi_80211_mo_unassign_vif_chanctx(struct ieee80211_hw *, struct ieee80211_vif *, + struct ieee80211_chanctx_conf **); +int lkpi_80211_mo_add_chanctx(struct ieee80211_hw *, struct ieee80211_chanctx_conf *); +void lkpi_80211_mo_change_chanctx(struct ieee80211_hw *, + struct ieee80211_chanctx_conf *, uint32_t); +void lkpi_80211_mo_remove_chanctx(struct ieee80211_hw *, + struct ieee80211_chanctx_conf *); +void lkpi_80211_mo_bss_info_changed(struct ieee80211_hw *, struct ieee80211_vif *, + struct ieee80211_bss_conf *, uint32_t); +int lkpi_80211_mo_conf_tx(struct ieee80211_hw *, struct ieee80211_vif *, + uint16_t, const struct ieee80211_tx_queue_params *); +void lkpi_80211_mo_flush(struct ieee80211_hw *, struct ieee80211_vif *, + uint32_t, bool); +void lkpi_80211_mo_mgd_prepare_tx(struct ieee80211_hw *, struct ieee80211_vif *, + struct ieee80211_prep_tx_info *); +void lkpi_80211_mo_mgd_complete_tx(struct ieee80211_hw *, struct ieee80211_vif *, + struct ieee80211_prep_tx_info *); +void lkpi_80211_mo_tx(struct ieee80211_hw *, struct ieee80211_tx_control *, + struct sk_buff *); +void lkpi_80211_mo_wake_tx_queue(struct ieee80211_hw *, struct ieee80211_txq *); +void lkpi_80211_mo_sync_rx_queues(struct ieee80211_hw *); +void lkpi_80211_mo_sta_pre_rcu_remove(struct ieee80211_hw *, + struct ieee80211_vif *, struct ieee80211_sta *); +int lkpi_80211_mo_set_key(struct ieee80211_hw *, enum set_key_cmd, + struct ieee80211_vif *, struct ieee80211_sta *, + struct ieee80211_key_conf *); + +#endif /* _LKPI_SRC_LINUX_80211_H */ diff --git a/sys/compat/linuxkpi/common/src/linux_80211_macops.c b/sys/compat/linuxkpi/common/src/linux_80211_macops.c new file mode 100644 index 00000000000..0004967f350 --- /dev/null +++ b/sys/compat/linuxkpi/common/src/linux_80211_macops.c @@ -0,0 +1,615 @@ +/*- + * Copyright (c) 2021 The FreeBSD Foundation + * + * This software was developed by Björn Zeeb under sponsorship from + * the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#define LINUXKPI_NET80211 +#include + +#include "linux_80211.h" + +int +lkpi_80211_mo_start(struct ieee80211_hw *hw) +{ + struct lkpi_hw *lhw; + int error; + + lhw = HW_TO_LHW(hw); + if (lhw->ops->start == NULL) { + error = EOPNOTSUPP; + goto out; + } + + if ((lhw->sc_flags & LKPI_MAC80211_DRV_STARTED)) { + /* Trying to start twice is an error. */ + error = EEXIST; + goto out; + } + error = lhw->ops->start(hw); + if (error == 0) + lhw->sc_flags |= LKPI_MAC80211_DRV_STARTED; + +out: + return (error); +} + +void +lkpi_80211_mo_stop(struct ieee80211_hw *hw) +{ + struct lkpi_hw *lhw; + + lhw = HW_TO_LHW(hw); + if (lhw->ops->stop == NULL) + return; + + lhw->ops->stop(hw); + lhw->sc_flags &= ~LKPI_MAC80211_DRV_STARTED; +} + +int +lkpi_80211_mo_set_frag_threshold(struct ieee80211_hw *hw, uint32_t frag_th) +{ + struct lkpi_hw *lhw; + int error; + + lhw = HW_TO_LHW(hw); + if (lhw->ops->set_frag_threshold == NULL) { + error = EOPNOTSUPP; + goto out; + } + + error = lhw->ops->set_frag_threshold(hw, frag_th); + +out: + return (error); +} + +int +lkpi_80211_mo_set_rts_threshold(struct ieee80211_hw *hw, uint32_t rts_th) +{ + struct lkpi_hw *lhw; + int error; + + lhw = HW_TO_LHW(hw); + if (lhw->ops->set_rts_threshold == NULL) { + error = EOPNOTSUPP; + goto out; + } + + error = lhw->ops->set_rts_threshold(hw, rts_th); + +out: + return (error); +} + + +int +lkpi_80211_mo_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct lkpi_hw *lhw; + struct lkpi_vif *lvif; + int error; + + lhw = HW_TO_LHW(hw); + if (lhw->ops->add_interface == NULL) { + error = EOPNOTSUPP; + goto out; + } + + lvif = VIF_TO_LVIF(vif); + LKPI_80211_LVIF_LOCK(lvif); + if (lvif->added_to_drv) { + LKPI_80211_LVIF_UNLOCK(lvif); + /* Trying to add twice is an error. */ + error = EEXIST; + goto out; + } + LKPI_80211_LVIF_UNLOCK(lvif); + + error = lhw->ops->add_interface(hw, vif); + if (error == 0) { + LKPI_80211_LVIF_LOCK(lvif); + lvif->added_to_drv = true; + LKPI_80211_LVIF_UNLOCK(lvif); + } + +out: + return (error); +} + +void +lkpi_80211_mo_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct lkpi_hw *lhw; + struct lkpi_vif *lvif; + + lhw = HW_TO_LHW(hw); + if (lhw->ops->remove_interface == NULL) + return; + + lvif = VIF_TO_LVIF(vif); + LKPI_80211_LVIF_LOCK(lvif); + if (!lvif->added_to_drv) { + LKPI_80211_LVIF_UNLOCK(lvif); + return; + } + LKPI_80211_LVIF_UNLOCK(lvif); + + lhw->ops->remove_interface(hw, vif); + LKPI_80211_LVIF_LOCK(lvif); + lvif->added_to_drv = false; + LKPI_80211_LVIF_UNLOCK(lvif); +} + + +int +lkpi_80211_mo_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_scan_request *sr) +{ + struct lkpi_hw *lhw; + int error; + + lhw = HW_TO_LHW(hw); + if (lhw->ops->hw_scan == NULL) { + /* XXX-BZ can we hide other scans like we can for sta_add..? */ + error = EOPNOTSUPP; + goto out; + } + + lhw->scan_flags |= LKPI_SCAN_RUNNING; + error = lhw->ops->hw_scan(hw, vif, sr); + if (error != 0) + lhw->scan_flags &= ~LKPI_SCAN_RUNNING; + +out: + return (error); +} + +void +lkpi_80211_mo_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct lkpi_hw *lhw; + + lhw = HW_TO_LHW(hw); + if (lhw->ops->cancel_hw_scan == NULL) + return; + + lhw->ops->cancel_hw_scan(hw, vif); +} + +void +lkpi_80211_mo_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct lkpi_hw *lhw; + + lhw = HW_TO_LHW(hw); + if (lhw->ops->sw_scan_complete == NULL) + return; + + lhw->ops->sw_scan_complete(hw, vif); + lhw->scan_flags &= ~LKPI_SCAN_RUNNING; +} + +void +lkpi_80211_mo_sw_scan_start(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + const u8 *addr) +{ + struct lkpi_hw *lhw; + + lhw = HW_TO_LHW(hw); + if (lhw->ops->sw_scan_start == NULL) + return; + + lhw->ops->sw_scan_start(hw, vif, addr); +} + + +/* + * We keep the Linux type here; it really is an uintptr_t. + */ +u64 +lkpi_80211_mo_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list) +{ + struct lkpi_hw *lhw; + u64 ptr; + + lhw = HW_TO_LHW(hw); + if (lhw->ops->prepare_multicast == NULL) + return (0); + + ptr = lhw->ops->prepare_multicast(hw, mc_list); + return (ptr); +} + +void +lkpi_80211_mo_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, + unsigned int *total_flags, u64 mc_ptr) +{ + struct lkpi_hw *lhw; + + lhw = HW_TO_LHW(hw); + if (lhw->ops->configure_filter == NULL) + return; + + if (mc_ptr == 0) + return; + + lhw->ops->configure_filter(hw, changed_flags, total_flags, mc_ptr); +} + + +/* + * So far we only called sta_{add,remove} as an alternative to sta_state. + * Let's keep the implementation simpler and hid sta_{add,remove} under the + * hood here calling them if state_state is not available from mo_sta_state. + */ +static int +lkpi_80211_mo_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct lkpi_hw *lhw; + struct lkpi_sta *lsta; + int error; + + lhw = HW_TO_LHW(hw); + if (lhw->ops->sta_add == NULL) { + error = EOPNOTSUPP; + goto out; + } + + lsta = STA_TO_LSTA(sta); + if (lsta->added_to_drv) { + error = EEXIST; + goto out; + } + + error = lhw->ops->sta_add(hw, vif, sta); + if (error == 0) + lsta->added_to_drv = true; + +out: + return error; +} + +static int +lkpi_80211_mo_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct lkpi_hw *lhw; + struct lkpi_sta *lsta; + int error; + + lhw = HW_TO_LHW(hw); + if (lhw->ops->sta_remove == NULL) { + error = EOPNOTSUPP; + goto out; + } + + lsta = STA_TO_LSTA(sta); + if (!lsta->added_to_drv) { + /* If we never added the sta, do not complain on cleanup. */ + error = 0; + goto out; + } + + error = lhw->ops->sta_remove(hw, vif, sta); + if (error == 0) + lsta->added_to_drv = false; + +out: + return error; +} + +int +lkpi_80211_mo_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, enum ieee80211_sta_state nstate) +{ + struct lkpi_hw *lhw; + struct lkpi_sta *lsta; + int error; + + lhw = HW_TO_LHW(hw); + lsta = STA_TO_LSTA(sta); + if (lhw->ops->sta_state != NULL) { + error = lhw->ops->sta_state(hw, vif, sta, lsta->state, nstate); + if (error == 0) { + if (nstate == IEEE80211_STA_NOTEXIST) + lsta->added_to_drv = false; + else + lsta->added_to_drv = true; + lsta->state = nstate; + } + goto out; + } + + /* XXX-BZ is the change state AUTH or ASSOC here? */ + if (lsta->state < IEEE80211_STA_ASSOC && nstate == IEEE80211_STA_ASSOC) + error = lkpi_80211_mo_sta_add(hw, vif, sta); + else if (lsta->state >= IEEE80211_STA_ASSOC && + nstate < IEEE80211_STA_ASSOC) + error = lkpi_80211_mo_sta_remove(hw, vif, sta); + else + /* Nothing to do. */ + error = 0; + +out: + /* XXX-BZ should we manage state in here? */ + return (error); +} + +int +lkpi_80211_mo_config(struct ieee80211_hw *hw, uint32_t changed) +{ + struct lkpi_hw *lhw; + int error; + + lhw = HW_TO_LHW(hw); + if (lhw->ops->config == NULL) { + error = EOPNOTSUPP; + goto out; + } + + error = lhw->ops->config(hw, changed); + +out: + return (error); +} + + +int +lkpi_80211_mo_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *chanctx_conf) +{ + struct lkpi_hw *lhw; + int error; + + lhw = HW_TO_LHW(hw); + if (lhw->ops->assign_vif_chanctx == NULL) { + error = EOPNOTSUPP; + goto out; + } + + error = lhw->ops->assign_vif_chanctx(hw, vif, chanctx_conf); + if (error == 0) + vif->chanctx_conf = chanctx_conf; + +out: + return (error); +} + +void +lkpi_80211_mo_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf **chanctx_conf) +{ + struct lkpi_hw *lhw; + + lhw = HW_TO_LHW(hw); + if (lhw->ops->unassign_vif_chanctx == NULL) + return; + + if (*chanctx_conf == NULL) + return; + + lhw->ops->unassign_vif_chanctx(hw, vif, *chanctx_conf); + *chanctx_conf = NULL; +} + + +int +lkpi_80211_mo_add_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *chanctx_conf) +{ + struct lkpi_hw *lhw; + int error; + + lhw = HW_TO_LHW(hw); + if (lhw->ops->add_chanctx == NULL) { + error = EOPNOTSUPP; + goto out; + } + + error = lhw->ops->add_chanctx(hw, chanctx_conf); + +out: + return (error); +} + +void +lkpi_80211_mo_change_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *chanctx_conf, uint32_t changed) +{ + struct lkpi_hw *lhw; + + lhw = HW_TO_LHW(hw); + if (lhw->ops->change_chanctx == NULL) + return; + + lhw->ops->change_chanctx(hw, chanctx_conf, changed); +} + +void +lkpi_80211_mo_remove_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *chanctx_conf) +{ + struct lkpi_hw *lhw; + + lhw = HW_TO_LHW(hw); + if (lhw->ops->remove_chanctx == NULL) + return; + + lhw->ops->remove_chanctx(hw, chanctx_conf); +} + +void +lkpi_80211_mo_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *conf, uint32_t changed) +{ + struct lkpi_hw *lhw; + + lhw = HW_TO_LHW(hw); + if (lhw->ops->bss_info_changed == NULL) + return; + + lhw->ops->bss_info_changed(hw, vif, conf, changed); +} + + +int +lkpi_80211_mo_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + uint16_t ac, const struct ieee80211_tx_queue_params *txqp) +{ + struct lkpi_hw *lhw; + int error; + + lhw = HW_TO_LHW(hw); + if (lhw->ops->conf_tx == NULL) { + error = EOPNOTSUPP; + goto out; + } + + error = lhw->ops->conf_tx(hw, vif, ac, txqp); + +out: + return (error); +} + +void +lkpi_80211_mo_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + uint32_t nqueues, bool drop) +{ + struct lkpi_hw *lhw; + + lhw = HW_TO_LHW(hw); + if (lhw->ops->flush == NULL) + return; + + lhw->ops->flush(hw, vif, nqueues, drop); +} + +void +lkpi_80211_mo_mgd_prepare_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_prep_tx_info *txinfo) +{ + struct lkpi_hw *lhw; + + lhw = HW_TO_LHW(hw); + if (lhw->ops->mgd_prepare_tx == NULL) + return; + + lhw->ops->mgd_prepare_tx(hw, vif, txinfo); +} + +void +lkpi_80211_mo_mgd_complete_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_prep_tx_info *txinfo) +{ + struct lkpi_hw *lhw; + + lhw = HW_TO_LHW(hw); + if (lhw->ops->mgd_complete_tx == NULL) + return; + + lhw->ops->mgd_complete_tx(hw, vif, txinfo); +} + +void +lkpi_80211_mo_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *txctrl, + struct sk_buff *skb) +{ + struct lkpi_hw *lhw; + + lhw = HW_TO_LHW(hw); + if (lhw->ops->tx == NULL) + return; + + lhw->ops->tx(hw, txctrl, skb); +} + +void +lkpi_80211_mo_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq) +{ + struct lkpi_hw *lhw; + + lhw = HW_TO_LHW(hw); + if (lhw->ops->wake_tx_queue == NULL) + return; + + lhw->ops->wake_tx_queue(hw, txq); +} + +void +lkpi_80211_mo_sync_rx_queues(struct ieee80211_hw *hw) +{ + struct lkpi_hw *lhw; + + lhw = HW_TO_LHW(hw); + if (lhw->ops->sync_rx_queues == NULL) + return; + + lhw->ops->sync_rx_queues(hw); +} + +void +lkpi_80211_mo_sta_pre_rcu_remove(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, struct ieee80211_sta *sta) +{ + struct lkpi_hw *lhw; + + lhw = HW_TO_LHW(hw); + if (lhw->ops->sta_pre_rcu_remove == NULL) + return; + + lhw->ops->sta_pre_rcu_remove(hw, vif, sta); +} + +int +lkpi_80211_mo_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *kc) +{ + struct lkpi_hw *lhw; + int error; + + lhw = HW_TO_LHW(hw); + if (lhw->ops->set_key == NULL) { + error = EOPNOTSUPP; + goto out; + } + + error = lhw->ops->set_key(hw, cmd, vif, sta, kc); + +out: + return (error); +} diff --git a/sys/conf/files b/sys/conf/files index a9525d8beab..9ba3c9b719f 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -4526,6 +4526,10 @@ nlm/nlm_prot_xdr.c optional nfslockd | nfsd nlm/sm_inter_xdr.c optional nfslockd | nfsd # Linux Kernel Programming Interface +compat/linuxkpi/common/src/linux_80211.c optional compat_linuxkpi wlan \ + compile-with "${LINUXKPI_C}" +compat/linuxkpi/common/src/linux_80211_macops.c optional compat_linuxkpi wlan \ + compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_kmod.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_acpi.c optional compat_linuxkpi acpi \ diff --git a/sys/modules/Makefile b/sys/modules/Makefile index 150fd405dc4..74eb38a3b1b 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -211,6 +211,7 @@ SUBDIR= \ libmchain \ lindebugfs \ linuxkpi \ + linuxkpi_wlan \ ${_lio} \ lpt \ mac_biba \ diff --git a/sys/modules/linuxkpi_wlan/Makefile b/sys/modules/linuxkpi_wlan/Makefile new file mode 100644 index 00000000000..4aba6f83137 --- /dev/null +++ b/sys/modules/linuxkpi_wlan/Makefile @@ -0,0 +1,15 @@ +# $FreeBSD$ +.PATH: ${SRCTOP}/sys/compat/linuxkpi/common/src + +KMOD= linuxkpi_wlan +SRCS= linux_80211.c \ + linux_80211_macops.c + +SRCS+= opt_wlan.h +SRCS+= ${LINUXKPI_GENSRCS} + +CFLAGS+= -I${SRCTOP}/sys/compat/linuxkpi/common/include + +EXPORT_SYMS= YES + +.include