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 080c26d24d6..e9c228c3011 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -4578,6 +4578,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 c6a385b51c8..7ae601fb48f 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -216,6 +216,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