| /*- |
| * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting |
| * All rights reserved. |
| * |
| * 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, |
| * without modification. |
| * 2. Redistributions in binary form must reproduce at minimum a disclaimer |
| * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any |
| * redistribution must be conditioned upon including a substantially |
| * similar Disclaimer requirement for further binary redistribution. |
| * 3. Neither the names of the above-listed copyright holders nor the names |
| * of any contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * Alternatively, this software may be distributed under the terms of the |
| * GNU General Public License ("GPL") version 2 as published by the Free |
| * Software Foundation. |
| * |
| * NO WARRANTY |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY |
| * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL |
| * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. |
| * |
| * $Id: ieee80211_wireless.c 2614 2007-07-26 12:58:47Z mrenzmann $ |
| */ |
| |
| /* |
| * Wireless extensions support for 802.11 common code. |
| */ |
| #ifndef AUTOCONF_INCLUDED |
| #include <linux/config.h> |
| #endif |
| |
| #include <linux/version.h> |
| #include <linux/module.h> |
| #include <linux/netdevice.h> |
| #include <linux/utsname.h> |
| #include <linux/if_arp.h> /* XXX for ARPHRD_ETHER */ |
| #include <linux/delay.h> |
| #include <linux/random.h> |
| |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0) |
| #include <linux/watch64.h> |
| #endif |
| |
| #include <qtn/muc_phy_stats.h> |
| #include <qtn/shared_defs.h> |
| #include <qtn/skb_recycle.h> |
| #include <qtn/lhost_muc_comm.h> |
| #include <qtn/qtn_global.h> |
| |
| #include <linux/wireless.h> |
| #include <net/iw_handler.h> |
| #include <linux/seq_file.h> |
| #include <linux/jiffies.h> |
| #include <linux/math64.h> |
| |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) |
| #include <linux/pm_qos.h> |
| #else |
| #include <linux/pm_qos_params.h> |
| #include <asm/uaccess.h> |
| #endif |
| #include "net80211/if_media.h" |
| |
| #include "net80211/ieee80211_var.h" |
| #include "net80211/ieee80211_linux.h" |
| #include "net80211/_ieee80211.h" |
| #include "qdrv_sch_const.h" |
| #include "net80211/ieee80211_tpc.h" |
| #include "net80211/ieee80211_tdls.h" |
| #include "net80211/ieee80211_mlme_statistics.h" |
| #include "qtn_logging.h" |
| |
| #include "../qdrv/qdrv_vap.h" |
| #include <qtn/qtn_debug.h> |
| #include <qtn/shared_params.h> |
| #include <qtn/qtn_bb_mutex.h> |
| #include <qtn/qtn_vlan.h> |
| #include "../../net/bridge/br_public.h" |
| |
| #include "qtn/wlan_ioctl.h" |
| |
| #include "soc.h" |
| |
| #include "../qdrv/qdrv_mac.h" |
| #include <qtn/txbf_mbox.h> |
| |
| #include <qtn/topaz_tqe_cpuif.h> |
| |
| #include <qtn/hardware_revision.h> |
| |
| #if defined(CONFIG_QTN_BSA_SUPPORT) |
| #include "net80211/ieee80211_bsa.h" |
| #endif |
| #include <asm/board/pm.h> |
| |
| #define IS_UP(_dev) \ |
| (((_dev)->flags & (IFF_RUNNING|IFF_UP)) == (IFF_RUNNING|IFF_UP)) |
| #define IS_UP_AUTO(_vap) \ |
| (IS_UP((_vap)->iv_dev) && \ |
| (_vap)->iv_ic->ic_roaming == IEEE80211_ROAMING_AUTO) |
| #define RESCAN 1 |
| |
| #define DBGMAC "%02X:%02X:%02X:%02X:%02X:%02X" |
| #define ETHERFMT(a) \ |
| (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] |
| |
| #define STRUCT_MEMBER_SIZEOF(stype, member) sizeof(((stype *)0)->member) |
| #define RSSI_SWING_RANGE 30 |
| #define RSSI_MAX_SMTHING_FCTR 100 |
| #define RSSI_HIGH_SMTHING_FCTR 99 |
| #define RSSI_MED_SMTHING_FCTR 95 |
| #define QTN_AMPDU_DETECT_PERIOD 1 |
| #define QTN_RSSI_SAMPLE_TH 4 |
| |
| #define SCS_CHAN_POWER_DIFF_SAFE 2 |
| #define SCS_CHAN_POWER_DIFF_MAX 16 |
| #define SCS_MAX_RAW_CHAN_METRIC 0x7FFFFFFF |
| #define SCS_MAX_RATE_RATIO_CAP 0x7FFFFFFF |
| #define SCS_PICK_CHAN_MIN_SCALED_TRAFFIC 100 /* ms */ |
| #define MIN_CAC_PERIOD 70 /* seconds */ |
| #define OCAC_MAX_SUPPORTED_VAPS 2 |
| |
| #define DFS_S_DBG_QEVT(qevtdev, ...) do {\ |
| printk(__VA_ARGS__);\ |
| ieee80211_eventf(qevtdev, __VA_ARGS__);\ |
| } while (0) |
| |
| #define IEEE80211_OBSS_AP_SCAN_INT 25 |
| int wlan_11ac_20M_mcs_nss_tbl[] = { |
| 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, -1, |
| 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, -1, |
| 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, |
| 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, -1 |
| }; |
| |
| #define DM_DEFAULT_TX_POWER_FACTOR 2 |
| #define DM_DEFAULT_DFS_FACTOR 8 |
| |
| #if defined(CONFIG_QTN_80211K_SUPPORT) |
| const uint8_t ieee80211_meas_sta_qtn_report_subtype_len[RM_QTN_CTRL_END + 1] = { |
| [RM_QTN_TX_STATS] = sizeof(struct ieee80211_ie_qtn_rm_txstats), |
| [RM_QTN_RX_STATS] = sizeof(struct ieee80211_ie_qtn_rm_rxstats), |
| [RM_QTN_MAX_QUEUED] = STRUCT_MEMBER_SIZEOF(struct ieee80211_ie_qtn_rm_sta_all, max_queued), |
| [RM_QTN_LINK_QUALITY] = STRUCT_MEMBER_SIZEOF(struct ieee80211_ie_qtn_rm_sta_all, link_quality), |
| [RM_QTN_RSSI_DBM] = STRUCT_MEMBER_SIZEOF(struct ieee80211_ie_qtn_rm_sta_all, rssi_dbm), |
| [RM_QTN_BANDWIDTH] = STRUCT_MEMBER_SIZEOF(struct ieee80211_ie_qtn_rm_sta_all, bandwidth), |
| [RM_QTN_SNR] = STRUCT_MEMBER_SIZEOF(struct ieee80211_ie_qtn_rm_sta_all, snr), |
| [RM_QTN_TX_PHY_RATE] = STRUCT_MEMBER_SIZEOF(struct ieee80211_ie_qtn_rm_sta_all, tx_phy_rate), |
| [RM_QTN_RX_PHY_RATE] = STRUCT_MEMBER_SIZEOF(struct ieee80211_ie_qtn_rm_sta_all, rx_phy_rate), |
| [RM_QTN_CCA] = STRUCT_MEMBER_SIZEOF(struct ieee80211_ie_qtn_rm_sta_all, cca), |
| [RM_QTN_BR_IP] = STRUCT_MEMBER_SIZEOF(struct ieee80211_ie_qtn_rm_sta_all, br_ip), |
| [RM_QTN_RSSI] = STRUCT_MEMBER_SIZEOF(struct ieee80211_ie_qtn_rm_sta_all, rssi), |
| [RM_QTN_HW_NOISE] = STRUCT_MEMBER_SIZEOF(struct ieee80211_ie_qtn_rm_sta_all, hw_noise), |
| [RM_QTN_SOC_MACADDR] = STRUCT_MEMBER_SIZEOF(struct ieee80211_ie_qtn_rm_sta_all, soc_macaddr), |
| [RM_QTN_SOC_IPADDR] = STRUCT_MEMBER_SIZEOF(struct ieee80211_ie_qtn_rm_sta_all, soc_ipaddr), |
| [RM_QTN_UNKNOWN] = sizeof(u_int32_t), |
| [RM_QTN_RESET_CNTS] = sizeof(u_int32_t), |
| [RM_QTN_RESET_QUEUED] = sizeof(u_int32_t), |
| }; |
| #endif |
| |
| struct assoc_info_report { |
| uint64_t ai_rx_bytes; |
| uint64_t ai_tx_bytes; |
| uint32_t ai_rx_packets; |
| uint32_t ai_tx_packets; |
| uint32_t ai_rx_errors; |
| uint32_t ai_tx_errors; |
| uint32_t ai_rx_dropped; |
| uint32_t ai_tx_dropped; |
| uint32_t ai_tx_wifi_sent[WMM_AC_NUM]; |
| uint32_t ai_tx_wifi_drop[WME_AC_NUM]; |
| uint32_t ai_tx_ucast; |
| uint32_t ai_rx_ucast; |
| uint32_t ai_tx_mcast; |
| uint32_t ai_rx_mcast; |
| uint32_t ai_tx_bcast; |
| uint32_t ai_rx_bcast; |
| uint32_t ai_tx_failed; |
| uint32_t ai_time_associated; /*Unit: seconds*/ |
| uint16_t ai_assoc_id; |
| uint16_t ai_link_quality; |
| uint16_t ai_tx_phy_rate; |
| uint16_t ai_rx_phy_rate; |
| uint32_t ai_achievable_tx_phy_rate; |
| uint32_t ai_achievable_rx_phy_rate; |
| u_int32_t ai_rx_fragment_pkts; |
| u_int32_t ai_rx_vlan_pkts; |
| uint8_t ai_mac_addr[IEEE80211_ADDR_LEN]; |
| int32_t ai_rssi; |
| int32_t ai_smthd_rssi; |
| int32_t ai_snr; |
| int32_t ai_max_queued; |
| uint8_t ai_bw; |
| uint8_t ai_tx_mcs; |
| uint8_t ai_rx_mcs; |
| uint8_t ai_auth; |
| char ai_ifname[IFNAMSIZ]; |
| uint32_t ai_ip_addr; |
| int32_t ai_hw_noise; |
| uint32_t ai_is_qtn_node; |
| }; |
| |
| struct assoc_info_table { |
| uint16_t unit_size; /* Size of structure assoc_info_table */ |
| uint16_t cnt; /* Record the number of valid entries */ |
| struct assoc_info_report array[QTN_ASSOC_LIMIT]; |
| }; |
| |
| struct sample_assoc_data { |
| uint8_t mac_addr[IEEE80211_ADDR_LEN]; |
| uint8_t assoc_id; |
| uint8_t bw; |
| uint8_t tx_stream; |
| uint8_t rx_stream; |
| uint32_t time_associated; /*Unit: seconds*/ |
| uint32_t achievable_tx_phy_rate; |
| uint32_t achievable_rx_phy_rate; |
| uint32_t rx_packets; |
| uint32_t tx_packets; |
| uint32_t rx_errors; |
| uint32_t tx_errors; |
| uint32_t rx_dropped; |
| uint32_t tx_dropped; |
| uint32_t tx_wifi_drop[WME_AC_NUM]; |
| uint32_t rx_ucast; |
| uint32_t tx_ucast; |
| uint32_t rx_mcast; |
| uint32_t tx_mcast; |
| uint32_t rx_bcast; |
| uint32_t tx_bcast; |
| uint16_t link_quality; |
| uint32_t ip_addr; |
| uint64_t rx_bytes; |
| uint64_t tx_bytes; |
| uint32_t last_rssi_dbm[NUM_ANT + 1]; |
| uint32_t last_rcpi_dbm[NUM_ANT + 1]; |
| uint32_t last_evm_dbm[NUM_ANT + 1]; |
| uint32_t last_hw_noise[NUM_ANT + 1]; |
| uint8_t protocol; |
| uint8_t vendor; |
| }__packed; |
| |
| struct sample_assoc_user_data { |
| int num_entry; |
| int offset; |
| struct sample_assoc_data *data; |
| }; |
| |
| struct node_client_data { |
| struct list_head node_list; |
| struct sample_assoc_data data; |
| }; |
| |
| struct scs_chan_intf_params { |
| struct ieee80211_channel *chan; |
| uint32_t chan_bw; |
| uint32_t cca_intf; |
| uint32_t pmbl_err; |
| uint32_t cca_dur; |
| uint32_t cca_pri; |
| uint32_t cca_sec; |
| uint32_t cca_sec40; |
| }; |
| |
| |
| #ifdef WLAN_MALLOC_FREE_TOT_DEBUG |
| int g_wlan_tot_alloc = 0; |
| int g_wlan_tot_alloc_cnt = 0; |
| int g_wlan_tot_free = 0; |
| int g_wlan_tot_free_cnt = 0; |
| int g_wlan_balance = 0; |
| int g_wlan_tot_node_alloc = 0; |
| int g_wlan_tot_node_alloc_tmp = 0; |
| int g_wlan_tot_node_free = 0; |
| int g_wlan_tot_node_free_tmp = 0; |
| |
| EXPORT_SYMBOL(g_wlan_tot_alloc); |
| EXPORT_SYMBOL(g_wlan_tot_alloc_cnt); |
| EXPORT_SYMBOL(g_wlan_tot_free); |
| EXPORT_SYMBOL(g_wlan_tot_free_cnt); |
| EXPORT_SYMBOL(g_wlan_balance); |
| EXPORT_SYMBOL(g_wlan_tot_node_alloc); |
| EXPORT_SYMBOL(g_wlan_tot_node_alloc_tmp); |
| EXPORT_SYMBOL(g_wlan_tot_node_free); |
| EXPORT_SYMBOL(g_wlan_tot_node_free_tmp); |
| #endif |
| |
| extern uint16_t g_wowlan_host_state; |
| extern uint16_t g_wowlan_match_type; |
| extern uint16_t g_wowlan_l2_ether_type; |
| extern uint16_t g_wowlan_l3_udp_port; |
| |
| extern int fwt_db_get_macs_behind_node(const uint8_t index, uint32_t *num_entries, uint32_t max_req, |
| uint32_t *flags, uint8_t *buf); |
| |
| int ieee80211_send_tuning_data(struct ieee80211_node *); |
| void topaz_congest_set_unicast_queue_count(uint32_t qnum); |
| static void get_node_max_rssi (void *arg, struct ieee80211_node *ni); |
| static void ieee80211_pco_timer_func ( unsigned long arg ); |
| static int ieee80211_ba_setup_detect_set(struct ieee80211vap *vap, int enable); |
| static int ieee80211_wds_vap_exists(struct ieee80211com *ic); |
| static struct ieee80211_node *ieee80211_get_vap_node(struct ieee80211vap *vap); |
| int ieee80211_should_disable_scs(struct ieee80211com *ic); |
| |
| #if defined(CONFIG_QTN_BSA_SUPPORT) |
| static int ieee80211_bsa_macfilter_add(struct ieee80211vap *vap, uint8_t *mac); |
| static int ieee80211_bsa_macfilter_remove(struct ieee80211vap *vap, uint8_t *mac); |
| #endif |
| extern u_int8_t g_channel_fixed; |
| /* |
| * The RSSI values reported in the TX/RX descriptors in the driver are the SNR |
| * expressed in dBm. Thus 'rssi' is signal level above the noise floor in dBm. |
| * |
| * Noise is measured in dBm and is negative unless there is an unimaginable |
| * level of RF noise. |
| * |
| * The signal level is noise + rssi. |
| * |
| * Note that the iw_quality values are 1 byte, and can be signed, unsigned or |
| * negative depending on context. |
| * |
| */ |
| void |
| set_quality(struct iw_quality *iq, u_int rssi, int noise) |
| { |
| iq->qual = rssi; |
| iq->noise = noise; |
| iq->level = ((((int)rssi + noise) <= 0) ? ((int)rssi + noise) : 0); |
| iq->updated = IW_QUAL_ALL_UPDATED; |
| iq->updated |= IW_QUAL_DBM; |
| } |
| static void |
| pre_announced_chanswitch(struct net_device *dev, u_int32_t channel, u_int32_t tbtt); |
| |
| static void |
| preempt_scan(struct net_device *dev, int max_grace, int max_wait) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| int total_delay = 0; |
| int canceled = 0, ready = 0; |
| while (!ready && total_delay < max_grace + max_wait) { |
| if ((ic->ic_flags & IEEE80211_F_SCAN) == 0 |
| #ifdef QTN_BG_SCAN |
| && (ic->ic_flags_qtn & IEEE80211_QTN_BGSCAN) == 0 |
| #endif /* QTN_BG_SCAN */ |
| ) { |
| ready = 1; |
| } else { |
| if (!canceled && total_delay > max_grace) { |
| /* |
| Cancel any existing active scan, so that any new parameters |
| in this scan ioctl (or the defaults) can be honored, then |
| wait around a while to see if the scan cancels properly. |
| */ |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, |
| "%s: cancel pending scan request\n", __func__); |
| (void) ieee80211_cancel_scan(vap); |
| canceled = 1; |
| } |
| mdelay (1); |
| total_delay += 1; |
| } |
| } |
| if (!ready) { |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, |
| "%s: Timeout canceling current scan.\n", |
| __func__); |
| } |
| } |
| |
| static struct iw_statistics * |
| ieee80211_iw_getstats(struct net_device *dev) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| |
| #ifdef USE_LINUX_FRAMEWORK |
| struct iw_statistics *is = &vap->iv_iwstats; |
| set_quality(&is->qual, ieee80211_getrssi(vap->iv_ic), |
| ic->ic_channoise); |
| is->status = vap->iv_state; |
| is->discard.nwid = vap->iv_stats.is_rx_wrongbss + |
| vap->iv_stats.is_rx_ssidmismatch; |
| is->discard.code = vap->iv_stats.is_rx_wepfail + |
| vap->iv_stats.is_rx_decryptcrc; |
| is->discard.fragment = 0; |
| is->discard.retries = 0; |
| is->discard.misc = 0; |
| |
| is->miss.beacon = 0; |
| return is; |
| #else |
| |
| struct iw_statistics *is = &ic->ic_iwstats; |
| ic->ic_get_wlanstats(ic, is); |
| |
| return is; |
| #endif |
| |
| } |
| |
| static int |
| ieee80211_ioctl_giwname(struct net_device *dev, struct iw_request_info *info, |
| char *name, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211_channel *c = vap->iv_ic->ic_curchan; |
| |
| if (vap->iv_ic->ic_des_mode == IEEE80211_MODE_AUTO && |
| vap->iv_ic->ic_rf_chipid == CHIPID_DUAL) |
| /* Display all the supported modes for RFIC5 */ |
| strncpy(name, "IEEE 802.11gnac", IFNAMSIZ); |
| else if ((IEEE80211_IS_CHAN_11AC(c) ) && |
| (vap->iv_ic->ic_phymode >= IEEE80211_MODE_11AC_VHT20PM)) |
| strncpy(name, "IEEE 802.11ac", IFNAMSIZ); |
| else if (IEEE80211_IS_CHAN_108G(c)) |
| strncpy(name, "IEEE 802.11Tg", IFNAMSIZ); |
| else if (IEEE80211_IS_CHAN_108A(c)) |
| strncpy(name, "IEEE 802.11Ta", IFNAMSIZ); |
| else if (IEEE80211_IS_CHAN_TURBO(c)) |
| strncpy(name, "IEEE 802.11T", IFNAMSIZ); |
| else if (IEEE80211_IS_CHAN_11NG(c) && |
| (vap->iv_ic->ic_phymode == IEEE80211_MODE_11NG_HT40PM)) |
| strncpy(name, "IEEE 802.11ng40", IFNAMSIZ); |
| else if (IEEE80211_IS_CHAN_11NG(c) && |
| (vap->iv_ic->ic_phymode == IEEE80211_MODE_11NG)) |
| strncpy(name, "IEEE 802.11ng", IFNAMSIZ); |
| else if (IEEE80211_IS_CHAN_11NA(c) && |
| (vap->iv_ic->ic_phymode == IEEE80211_MODE_11NA_HT40PM)) |
| strncpy(name, "IEEE 802.11na40", IFNAMSIZ); |
| else if (IEEE80211_IS_CHAN_11NA(c) && |
| (vap->iv_ic->ic_phymode == IEEE80211_MODE_11NA)) |
| strncpy(name, "IEEE 802.11na", IFNAMSIZ); |
| else if (IEEE80211_IS_CHAN_ANYG(c) && |
| (vap->iv_ic->ic_phymode == IEEE80211_MODE_11G)) |
| strncpy(name, "IEEE 802.11g", IFNAMSIZ); |
| else if (IEEE80211_IS_CHAN_A(c)) |
| strncpy(name, "IEEE 802.11a", IFNAMSIZ); |
| else if (IEEE80211_IS_CHAN_B(c)) |
| strncpy(name, "IEEE 802.11b", IFNAMSIZ); |
| else |
| strncpy(name, "IEEE 802.11", IFNAMSIZ); |
| /* XXX FHSS */ |
| return 0; |
| } |
| |
| /* |
| * Get a key index from a request. If nothing is |
| * specified in the request we use the current xmit |
| * key index. Otherwise we just convert the index |
| * to be base zero. |
| */ |
| static int |
| getiwkeyix(struct ieee80211vap *vap, const struct iw_point* erq, int *kix) |
| { |
| int kid; |
| |
| kid = erq->flags & IW_ENCODE_INDEX; |
| if (kid < 1 || kid > IEEE80211_WEP_NKID) { |
| kid = vap->iv_def_txkey; |
| if (kid == IEEE80211_KEYIX_NONE) |
| kid = 0; |
| } else { |
| --kid; |
| } |
| if (0 <= kid && kid < IEEE80211_WEP_NKID) { |
| *kix = kid; |
| return 0; |
| } else |
| return -EINVAL; |
| } |
| |
| static int |
| ieee80211_ioctl_siwencode(struct net_device *dev, |
| struct iw_request_info *info, struct iw_point *erq, char *keybuf) |
| { |
| #ifndef IEEE80211_UNUSED_CRYPTO_COMMANDS |
| return -EOPNOTSUPP; |
| #else |
| struct ieee80211vap *vap = netdev_priv(dev); |
| int kid = 0; |
| int error = -EINVAL; |
| int wepchange = 0; |
| |
| if ((erq->flags & IW_ENCODE_DISABLED) == 0) { |
| /* |
| * Enable crypto, set key contents, and |
| * set the default transmit key. |
| */ |
| error = getiwkeyix(vap, erq, &kid); |
| if (error < 0) |
| return error; |
| if (erq->length > IEEE80211_KEYBUF_SIZE) |
| return -EINVAL; |
| /* XXX no way to install 0-length key */ |
| if (erq->length > 0) { |
| struct ieee80211_key *k = &vap->iv_nw_keys[kid]; |
| |
| /* |
| * Set key contents. This interface only supports WEP. |
| * Indicate intended key index. |
| */ |
| k->wk_keyix = kid; |
| k->wk_keylen = erq->length; |
| k->wk_ciphertype = IEEE80211_CIPHER_WEP; |
| memcpy(k->wk_key, keybuf, erq->length); |
| memset(k->wk_key + erq->length, 0, |
| IEEE80211_KEYBUF_SIZE - erq->length); |
| error = vap->iv_key_set(vap, k, vap->iv_myaddr); |
| |
| } else |
| error = -EINVAL; |
| } else { |
| /* |
| * When the length is zero the request only changes |
| * the default transmit key. Verify the new key has |
| * a non-zero length. |
| */ |
| if (vap->iv_nw_keys[kid].wk_keylen == 0) |
| error = -EINVAL; |
| } |
| if (error == 0) { |
| /* |
| * The default transmit key is only changed when: |
| * 1. Privacy is enabled and no key matter is |
| * specified. |
| * 2. Privacy is currently disabled. |
| * This is deduced from the iwconfig man page. |
| */ |
| if (erq->length == 0 || |
| (vap->iv_flags & IEEE80211_F_PRIVACY) == 0) |
| vap->iv_def_txkey = kid; |
| wepchange = (vap->iv_flags & IEEE80211_F_PRIVACY) == 0; |
| vap->iv_flags |= IEEE80211_F_PRIVACY; |
| } else { |
| if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0) |
| return 0; |
| vap->iv_flags &= ~IEEE80211_F_PRIVACY; |
| wepchange = 1; |
| error = 0; |
| } |
| if (error == 0) { |
| /* Set policy for unencrypted frames */ |
| if ((erq->flags & IW_ENCODE_OPEN) && |
| (!(erq->flags & IW_ENCODE_RESTRICTED))) { |
| vap->iv_flags &= ~IEEE80211_F_DROPUNENC; |
| } else if (!(erq->flags & IW_ENCODE_OPEN) && |
| (erq->flags & IW_ENCODE_RESTRICTED)) { |
| vap->iv_flags |= IEEE80211_F_DROPUNENC; |
| } else { |
| /* Default policy */ |
| if (vap->iv_flags & IEEE80211_F_PRIVACY) |
| vap->iv_flags |= IEEE80211_F_DROPUNENC; |
| else |
| vap->iv_flags &= ~IEEE80211_F_DROPUNENC; |
| } |
| } |
| if (error == 0 && IS_UP(vap->iv_dev)) { |
| /* |
| * Device is up and running; we must kick it to |
| * effect the change. If we're enabling/disabling |
| * crypto use then we must re-initialize the device |
| * so the 802.11 state machine is reset. Otherwise |
| * the key state should have been updated above. |
| */ |
| if (wepchange && IS_UP_AUTO(vap)) |
| ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); |
| } |
| return error; |
| #endif /* IEEE80211_UNUSED_CRYPTO_COMMANDS */ |
| } |
| |
| static int |
| ieee80211_ioctl_giwencode(struct net_device *dev, struct iw_request_info *info, |
| struct iw_point *erq, char *key) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211_key *k; |
| int error, kid; |
| |
| if (vap->iv_flags & IEEE80211_F_PRIVACY) { |
| error = getiwkeyix(vap, erq, &kid); |
| if (error < 0) |
| return error; |
| k = &vap->iv_nw_keys[kid]; |
| /* XXX no way to return cipher/key type */ |
| |
| erq->flags = kid + 1; /* NB: base 1 */ |
| if (erq->length > k->wk_keylen) |
| erq->length = k->wk_keylen; |
| memcpy(key, k->wk_key, erq->length); |
| erq->flags |= IW_ENCODE_ENABLED; |
| } else { |
| erq->length = 0; |
| erq->flags = IW_ENCODE_DISABLED; |
| } |
| if (vap->iv_flags & IEEE80211_F_DROPUNENC) |
| erq->flags |= IW_ENCODE_RESTRICTED; |
| else |
| erq->flags |= IW_ENCODE_OPEN; |
| return 0; |
| } |
| |
| #ifndef ifr_media |
| #define ifr_media ifr_ifru.ifru_ivalue |
| #endif |
| |
| static int |
| ieee80211_ioctl_siwrate(struct net_device *dev, struct iw_request_info *info, |
| struct iw_param *rrq, char *extra) |
| { |
| static const u_int mopts[] = { |
| IFM_AUTO, |
| IFM_IEEE80211_11A, |
| IFM_IEEE80211_11B, |
| IFM_IEEE80211_11G, |
| IFM_IEEE80211_FH, |
| IFM_IEEE80211_11A | IFM_IEEE80211_TURBO, |
| IFM_IEEE80211_11G | IFM_IEEE80211_TURBO, |
| IFM_IEEE80211_11NA, |
| IFM_IEEE80211_11NG, |
| IFM_IEEE80211_11NG_HT40PM, |
| IFM_IEEE80211_11NA_HT40PM, |
| IFM_IEEE80211_11AC_VHT20PM, |
| IFM_IEEE80211_11AC_VHT40PM, |
| IFM_IEEE80211_11AC_VHT80PM, |
| IFM_IEEE80211_11AC_VHT160PM, |
| }; |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ifreq ifr; |
| int rate, retv; |
| u_int16_t mode = ic->ic_des_mode; |
| u_int16_t chan_mode = 0; |
| uint8_t sgi = 0; |
| |
| if (mode == IEEE80211_MODE_AUTO) |
| return -EINVAL; |
| |
| if (vap->iv_media.ifm_cur == NULL) |
| return -EINVAL; |
| |
| memset(&ifr, 0, sizeof(ifr)); |
| ifr.ifr_media = vap->iv_media.ifm_cur->ifm_media &~ (IFM_MMASK|IFM_TMASK); |
| ifr.ifr_media |= mopts[ic->ic_des_mode]; |
| |
| |
| if (rrq->fixed) { |
| /* XXX fudge checking rates */ |
| if (mode < IEEE80211_MODE_11NA) { |
| rate = ieee80211_rate2media(ic, 2 * rrq->value / 1000000, |
| ic->ic_des_mode); |
| } else { |
| if (ic->ic_htcap.cap & IEEE80211_HTCAP_C_CHWIDTH40) { |
| chan_mode = 1; |
| sgi = ic->ic_htcap.cap & IEEE80211_HTCAP_C_SHORTGI40 ? 1 : 0; |
| } else { |
| sgi = ic->ic_htcap.cap & IEEE80211_HTCAP_C_SHORTGI20 ? 1 : 0; |
| } |
| |
| rate = ieee80211_rate2mcs(2 * rrq->value / 1000000, chan_mode, sgi); |
| /* No mcs match found. It can be a legacy rate */ |
| if (rate < 0) { |
| rate = ieee80211_mcs2media(ic, |
| (2 * rrq->value / 1000000), |
| ic->ic_des_mode); |
| } else { |
| rate = ieee80211_mcs2media(ic, rate, ic->ic_des_mode); |
| } |
| } |
| if (rate == IFM_AUTO) { /* NB: unknown rate */ |
| return -EINVAL; |
| } |
| } else { |
| rate = IFM_AUTO; |
| vap->iv_mcs_config = IEEE80211_MCS_AUTO_RATE_ENABLE; |
| } |
| ifr.ifr_media |= IFM_SUBTYPE(rate); |
| |
| /* refresh media capabilities based on channel */ |
| ifmedia_removeall(&vap->iv_media); |
| (void) ieee80211_media_setup(ic, &vap->iv_media, |
| vap->iv_caps, vap->iv_media.ifm_change, vap->iv_media.ifm_status); |
| |
| retv = ifmedia_ioctl(vap->iv_dev, &ifr, &vap->iv_media, SIOCSIFMEDIA); |
| if (retv == -ENETRESET) |
| { |
| #if 0 //No need to restart network after rate change |
| retv = IS_UP_AUTO(vap) ? ieee80211_open(vap->iv_dev) : 0; |
| #endif |
| return 0; |
| } |
| return retv; |
| } |
| |
| static int |
| ieee80211_ioctl_giwrate(struct net_device *dev, struct iw_request_info *info, |
| struct iw_param *rrq, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ifmediareq imr; |
| int rate, mcs; |
| u_int16_t mode, chan_mode = 0; |
| uint8_t sgi = 0; |
| mode = ic->ic_des_mode; |
| |
| memset(&imr, 0, sizeof(imr)); |
| vap->iv_media.ifm_status((void *) vap, &imr); |
| |
| rrq->fixed = IFM_SUBTYPE(vap->iv_media.ifm_media) != IFM_AUTO; |
| /* media status will have the current xmit rate if available */ |
| |
| if(mode < IEEE80211_MODE_11NA) |
| { |
| rate = ieee80211_media2rate(imr.ifm_active); |
| |
| if (rate == -1) /* IFM_AUTO */ |
| rate = 0; |
| rrq->value = 1000000 * (rate / 2); |
| } |
| else |
| { |
| if (ic->ic_htcap.cap & IEEE80211_HTCAP_C_CHWIDTH40) { |
| chan_mode = 1; |
| sgi = ic->ic_htcap.cap & IEEE80211_HTCAP_C_SHORTGI40 ? 1 : 0; |
| } else { |
| sgi = ic->ic_htcap.cap & IEEE80211_HTCAP_C_SHORTGI20 ? 1 : 0; |
| } |
| rate = ieee80211_media2mcs(imr.ifm_active); |
| if(rate > 0) //Fixed rate |
| { |
| if(rate & 0x80 ) /* if 11n rate is an mcs index */ |
| { |
| mcs = rate & 0xf; |
| rate = ieee80211_mcs2rate(mcs, chan_mode, sgi, 0); |
| } |
| } |
| else /* IFM_AUTO */ |
| { |
| rate = 0; |
| } |
| rrq->value = 1000000 * (rate / 2); |
| } |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_siwsens(struct net_device *dev, struct iw_request_info *info, |
| struct iw_param *sens, char *extra) |
| { |
| return -EOPNOTSUPP; |
| } |
| |
| static int |
| ieee80211_ioctl_giwsens(struct net_device *dev, struct iw_request_info *info, |
| struct iw_param *sens, char *extra) |
| { |
| sens->value = 1; |
| sens->fixed = 1; |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_siwrts(struct net_device *dev, struct iw_request_info *info, |
| struct iw_param *rts, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| u32 val; |
| |
| if (rts->disabled) |
| val = IEEE80211_RTS_THRESH_OFF; |
| else if (IEEE80211_RTS_MIN <= rts->value && |
| rts->value <= IEEE80211_RTS_MAX) |
| val = rts->value; |
| else |
| return -EINVAL; |
| if (val != vap->iv_rtsthreshold) { |
| vap->iv_rtsthreshold = val; |
| ieee80211_param_to_qdrv(vap, IEEE80211_PARAM_RTSTHRESHOLD, val, NULL, 0); |
| } |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_giwrts(struct net_device *dev, struct iw_request_info *info, |
| struct iw_param *rts, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| |
| rts->value = vap->iv_rtsthreshold; |
| rts->disabled = (rts->value == IEEE80211_RTS_THRESH_OFF); |
| rts->fixed = 1; |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_siwfrag(struct net_device *dev, struct iw_request_info *info, |
| struct iw_param *rts, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| u16 val; |
| |
| if (rts->disabled) |
| val = 2346; |
| else if (rts->value < 256 || rts->value > 2346) |
| return -EINVAL; |
| else |
| val = (rts->value & ~0x1); |
| |
| if (val != vap->iv_fragthreshold) { |
| vap->iv_fragthreshold = val; |
| return ic->ic_reset(ic); |
| } |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_giwfrag(struct net_device *dev, struct iw_request_info *info, |
| struct iw_param *rts, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| |
| rts->value = vap->iv_fragthreshold; |
| rts->disabled = (rts->value == 2346); |
| rts->fixed = 1; |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_siwap(struct net_device *dev, struct iw_request_info *info, |
| struct sockaddr *ap_addr, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| |
| /* NB: should not be set when in AP mode */ |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) |
| return -EINVAL; |
| |
| if (vap->iv_opmode == IEEE80211_M_WDS) |
| IEEE80211_ADDR_COPY(vap->wds_mac, &ap_addr->sa_data); |
| |
| /* |
| * zero address corresponds to 'iwconfig ath0 ap off', which means |
| * enable automatic choice of AP without actually forcing a |
| * reassociation. |
| * |
| * broadcast address corresponds to 'iwconfig ath0 ap any', which |
| * means scan for the current best AP. |
| * |
| * anything else specifies a particular AP. |
| */ |
| vap->iv_flags &= ~IEEE80211_F_DESBSSID; |
| if (!IEEE80211_ADDR_NULL(&ap_addr->sa_data)) { |
| if (!IEEE80211_ADDR_EQ(vap->iv_des_bssid, (u_int8_t*) "\xff\xff\xff\xff\xff\xff")) |
| vap->iv_flags |= IEEE80211_F_DESBSSID; |
| |
| IEEE80211_ADDR_COPY(vap->iv_des_bssid, &ap_addr->sa_data); |
| if (IS_UP_AUTO(vap)) |
| ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); |
| } |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_giwap(struct net_device *dev, struct iw_request_info *info, |
| struct sockaddr *ap_addr, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| |
| if (vap->iv_flags & IEEE80211_F_DESBSSID) { |
| IEEE80211_ADDR_COPY(&ap_addr->sa_data, vap->iv_des_bssid); |
| } else if (vap->iv_opmode == IEEE80211_M_WDS) { |
| IEEE80211_ADDR_COPY(&ap_addr->sa_data, vap->wds_mac); |
| } else if ((vap->iv_opmode == IEEE80211_M_HOSTAP && vap->iv_state == IEEE80211_S_SCAN) || |
| ((vap->iv_state == IEEE80211_S_RUN) && vap->iv_bss)) { |
| IEEE80211_ADDR_COPY(&ap_addr->sa_data, vap->iv_bss->ni_bssid); |
| } else { |
| IEEE80211_ADDR_SET_NULL(&ap_addr->sa_data); |
| } |
| |
| ap_addr->sa_family = ARPHRD_ETHER; |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_siwnickn(struct net_device *dev, struct iw_request_info *info, |
| struct iw_point *data, char *nickname) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| |
| if (data->length > IEEE80211_NWID_LEN) |
| return -EINVAL; |
| |
| memset(vap->iv_nickname, 0, IEEE80211_NWID_LEN); |
| memcpy(vap->iv_nickname, nickname, data->length); |
| vap->iv_nicknamelen = data->length; |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_giwnickn(struct net_device *dev, struct iw_request_info *info, |
| struct iw_point *data, char *nickname) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| |
| if (data->length > vap->iv_nicknamelen + 1) |
| data->length = vap->iv_nicknamelen + 1; |
| if (data->length > 0) { |
| memcpy(nickname, vap->iv_nickname, data->length - 1); /* XXX: strcpy? */ |
| nickname[data->length-1] = '\0'; |
| } |
| return 0; |
| } |
| |
| static int |
| find11gchannel(struct ieee80211com *ic, int i, int freq) |
| { |
| for (; i < ic->ic_nchans; i++) { |
| const struct ieee80211_channel *c = &ic->ic_channels[i]; |
| if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c)) |
| return 1; |
| } |
| return 0; |
| } |
| |
| struct ieee80211_channel * |
| findchannel(struct ieee80211com *ic, int ieee, int mode) |
| { |
| u_int modeflags; |
| int i; |
| |
| modeflags = ieee80211_get_chanflags(mode); |
| for (i = 0; i < ic->ic_nchans; i++) { |
| struct ieee80211_channel *c = &ic->ic_channels[i]; |
| |
| if (c->ic_ieee != ieee) |
| continue; |
| if (mode == IEEE80211_MODE_AUTO) { |
| /* |
| * XXX special-case 11b/g channels so we |
| * always select the g channel if both |
| * are present. |
| */ |
| if (!IEEE80211_IS_CHAN_B(c) || |
| !find11gchannel(ic, i + 1, c->ic_freq)) |
| return c; |
| } else { |
| if ((c->ic_flags & modeflags) == modeflags) |
| return c; |
| } |
| } |
| return NULL; |
| } |
| EXPORT_SYMBOL(findchannel); |
| |
| struct ieee80211_channel * |
| findchannel_any(struct ieee80211com *ic, int ieee, int prefer_mode) |
| { |
| struct ieee80211_channel *c; |
| |
| c = findchannel(ic, ieee, prefer_mode); |
| if (c == NULL) { |
| c = findchannel(ic, ieee, IEEE80211_MODE_AUTO); |
| if (c == NULL) { |
| printk("Channel %d does not exist\n", ieee); |
| c = IEEE80211_CHAN_ANYC; |
| } |
| } |
| |
| return c; |
| } |
| |
| struct ieee80211_channel *ieee80211_find_channel_by_ieee(struct ieee80211com *ic, int chan_ieee) |
| { |
| struct ieee80211_channel *chan; |
| |
| if (chan_ieee > IEEE80211_CHAN_MAX) { |
| return NULL; |
| } |
| |
| if (isclr(ic->ic_chan_active, chan_ieee)) { |
| return NULL; |
| } |
| |
| chan = findchannel(ic, chan_ieee, ic->ic_des_mode); |
| if (chan == NULL) { |
| chan = findchannel(ic, chan_ieee, IEEE80211_MODE_AUTO); |
| } |
| |
| return chan; |
| } |
| EXPORT_SYMBOL(ieee80211_find_channel_by_ieee); |
| |
| static char * |
| ieee80211_wireless_swfeat_desc(const enum swfeat feat) |
| { |
| char *desc = "Invalid feature"; |
| |
| switch (feat) { |
| case SWFEAT_ID_MODE_AP: |
| desc = "Access Point"; |
| break; |
| case SWFEAT_ID_MODE_STA: |
| desc = "Non-AP station"; |
| break; |
| case SWFEAT_ID_MODE_REPEATER: |
| desc = "Repeater"; |
| break; |
| case SWFEAT_ID_PCIE_RC: |
| desc = "PCIe RC mode"; |
| break; |
| case SWFEAT_ID_VHT: |
| desc = "VHT (802.11ac)"; |
| break; |
| case SWFEAT_ID_2X2: |
| desc = "802.11ac 2x2"; |
| break; |
| case SWFEAT_ID_2X4: |
| desc = "802.11ac 2x4"; |
| break; |
| case SWFEAT_ID_3X3: |
| desc = "802.11ac 3x3"; |
| break; |
| case SWFEAT_ID_4X4: |
| desc = "802.11ac 4x4"; |
| break; |
| case SWFEAT_ID_HS20: |
| desc = "Hotspot 2.0 (802.11u)"; |
| break; |
| case SWFEAT_ID_WPA2_ENT: |
| desc = "WPA2 Enterprise"; |
| break; |
| case SWFEAT_ID_MESH: |
| desc = "Mesh (802.11s)"; |
| break; |
| case SWFEAT_ID_TDLS: |
| desc = "TDLS (802.11z)"; |
| break; |
| case SWFEAT_ID_OCAC: |
| desc = "Zero-Second DFS (OCAC)"; |
| break; |
| case SWFEAT_ID_QHOP: |
| desc = "QHOP (WDS Extender)"; |
| break; |
| case SWFEAT_ID_QSV: |
| desc = "Spectrum View (QSV)"; |
| break; |
| case SWFEAT_ID_QSV_NEIGH: |
| desc = "Neighbour Report"; |
| break; |
| case SWFEAT_ID_MU_MIMO: |
| desc = "MU-MIMO"; |
| break; |
| case SWFEAT_ID_DUAL_CHAN_VIRT: |
| desc = "Dual Channel Virtual Concurrent"; |
| break; |
| case SWFEAT_ID_DUAL_CHAN: |
| desc = "Dual Channel Dual Concurrent"; |
| break; |
| case SWFEAT_ID_DUAL_BAND_VIRT: |
| desc = "Dual Band Virtual Concurrent"; |
| break; |
| case SWFEAT_ID_DUAL_BAND: |
| desc = "Dual Band Dual Concurrent"; |
| break; |
| case SWFEAT_ID_QTM_PRIO: |
| desc = "QTM - Per SSID Prioritisation "; |
| break; |
| case SWFEAT_ID_QTM: |
| desc = "QTM - Network Aware"; |
| break; |
| case SWFEAT_ID_SPEC_ANALYZER: |
| desc = "Spectrum Analyzer"; |
| break; |
| case SWFEAT_ID_MAX: |
| break; |
| } |
| |
| return desc; |
| } |
| |
| static int |
| ieee80211_subioctl_print_swfeat_map(struct net_device *dev, |
| void __user *outbuf, int len) |
| { |
| char *buf; |
| char *bufp; |
| int i; |
| int j; |
| int rem = len; |
| int rc = 0; |
| |
| if (!outbuf) { |
| printk("%s: NULL pointer for user request\n", __FUNCTION__); |
| return -EFAULT; |
| } |
| |
| buf = kzalloc(len, GFP_KERNEL); |
| if (buf == NULL) { |
| printk("%s: buffer alloc failed\n", __FUNCTION__); |
| return -EFAULT; |
| } |
| |
| bufp = buf; |
| for (i = 0; i < SWFEAT_ID_MAX; i++) { |
| if (isset(soc_shared_params->swfeat_map, i)) { |
| j = snprintf(bufp, rem, "%s\n", ieee80211_wireless_swfeat_desc(i)); |
| if (j <= 0) |
| break; |
| bufp += j; |
| rem -= j; |
| if (rem <= 0) |
| break; |
| } |
| } |
| |
| if (copy_to_user(outbuf, buf, len) != 0) { |
| printk("%s: copy_to_user failed\n", __FUNCTION__); |
| rc = -EIO; |
| } |
| |
| kfree(buf); |
| |
| return rc; |
| } |
| |
| static int |
| ieee80211_subioctl_get_swfeat_map(struct net_device *dev, |
| void __user *swfeat_map, int len) |
| { |
| if (!swfeat_map) { |
| printk("%s: NULL pointer for user request\n", __FUNCTION__); |
| return -EFAULT; |
| } |
| |
| if (len != sizeof(soc_shared_params->swfeat_map)) { |
| printk("%s: invalid size\n", __FUNCTION__); |
| return -EINVAL; |
| } |
| |
| if (copy_to_user(swfeat_map, &soc_shared_params->swfeat_map, len) != 0) { |
| printk("%s: copy_to_user failed\n", __FUNCTION__); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * Feature restrictions are enforced in the MuC firmware. Bypassing this check will |
| * cause the system to continually reboot. |
| */ |
| int ieee80211_swfeat_is_supported(uint16_t feat, uint8_t print_msg) |
| { |
| if ((feat < SWFEAT_ID_MAX) && isset(soc_shared_params->swfeat_map, feat)) |
| return 1; |
| |
| if (print_msg) |
| printk("%s is not supported on this device\n", |
| ieee80211_wireless_swfeat_desc(feat)); |
| |
| return 0; |
| } |
| EXPORT_SYMBOL(ieee80211_swfeat_is_supported); |
| |
| static inline int |
| ieee80211_vht_tx_mcs_is_valid(uint32_t mcs_val, uint32_t mcs_nss) |
| { |
| if (mcs_val >= IEEE80211_AC_MCS_MAX || mcs_nss >= IEEE80211_AC_MCS_NSS_MAX) |
| return 0; |
| |
| if (ieee80211_swfeat_is_supported(SWFEAT_ID_2X2, 0) || |
| ieee80211_swfeat_is_supported(SWFEAT_ID_2X4, 0)) { |
| if (mcs_nss >= IEEE80211_VHT_NSS2) |
| return 0; |
| } else if (ieee80211_swfeat_is_supported(SWFEAT_ID_3X3, 0)) { |
| if (mcs_nss >= IEEE80211_VHT_NSS3) |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| static inline int |
| ieee80211_ht_tx_mcs_is_valid(uint32_t mcs) |
| { |
| if (mcs < IEEE80211_HT_EQUAL_MCS_START || |
| mcs > IEEE80211_UNEQUAL_MCS_MAX || |
| mcs == IEEE80211_EQUAL_MCS_32) |
| return 0; |
| |
| if (ieee80211_swfeat_is_supported(SWFEAT_ID_2X2, 0) || |
| ieee80211_swfeat_is_supported(SWFEAT_ID_2X4, 0)) { |
| if ((mcs > IEEE80211_HT_EQUAL_MCS_2SS_MAX && |
| mcs < IEEE80211_UNEQUAL_MCS_START) || |
| mcs > IEEE80211_HT_UNEQUAL_MCS_2SS_MAX) { |
| return 0; |
| } |
| } else if (ieee80211_swfeat_is_supported(SWFEAT_ID_3X3, 0)) { |
| if ((mcs > IEEE80211_HT_EQUAL_MCS_3SS_MAX && |
| mcs < IEEE80211_UNEQUAL_MCS_START) || |
| mcs > IEEE80211_HT_UNEQUAL_MCS_3SS_MAX) { |
| return 0; |
| } |
| } |
| |
| return 1; |
| } |
| |
| #define IEEE80211_MODE_TURBO_STATIC_A IEEE80211_MODE_MAX |
| static int |
| ieee80211_check_mode_consistency(struct ieee80211com *ic, int mode, |
| struct ieee80211_channel *c) |
| { |
| if (c == IEEE80211_CHAN_ANYC) |
| return 0; |
| switch (mode) { |
| case IEEE80211_MODE_11B: |
| if (IEEE80211_IS_CHAN_B(c)) |
| return 0; |
| else |
| return 1; |
| break; |
| case IEEE80211_MODE_11G: |
| if (IEEE80211_IS_CHAN_ANYG(c)) { |
| return 0; |
| } else { |
| return 1; |
| } |
| break; |
| case IEEE80211_MODE_11NG: |
| if (IEEE80211_IS_CHAN_11NG(c)) { |
| return 0; |
| } else { |
| return 1; |
| } |
| break; |
| case IEEE80211_MODE_11NG_HT40PM: |
| if (IEEE80211_IS_CHAN_11NG_HT40PLUS(c) || IEEE80211_IS_CHAN_11NG_HT40MINUS(c)) |
| return 0; |
| else |
| return 1; |
| break; |
| case IEEE80211_MODE_11NA: |
| if (IEEE80211_IS_CHAN_11NA(c)) |
| return 0; |
| else |
| return 1; |
| break; |
| case IEEE80211_MODE_11A: |
| if (IEEE80211_IS_CHAN_A(c)) |
| return 0; |
| else |
| return 1; |
| break; |
| case IEEE80211_MODE_11NA_HT40PM: |
| if (IEEE80211_IS_CHAN_11NA_HT40PLUS(c) || IEEE80211_IS_CHAN_11NA_HT40MINUS(c)) |
| return 0; |
| else |
| return 1; |
| break; |
| case IEEE80211_MODE_TURBO_STATIC_A: |
| if (IEEE80211_IS_CHAN_A(c) && IEEE80211_IS_CHAN_STURBO(c)) |
| return -1; /* Mode not supported */ |
| else |
| return 1; |
| break; |
| case IEEE80211_MODE_11AC_VHT20PM: |
| if (IEEE80211_IS_CHAN_11AC(c)) |
| return 0; |
| else |
| return 1; |
| break; |
| case IEEE80211_MODE_11AC_VHT40PM: |
| if (IEEE80211_IS_CHAN_11AC_VHT40PLUS(c) || IEEE80211_IS_CHAN_11AC_VHT40MINUS(c)) |
| return 0; |
| else |
| return 1; |
| break; |
| case IEEE80211_MODE_11AC_VHT80PM: |
| if (IEEE80211_IS_CHAN_11AC_VHT80_EDGEPLUS(c) || |
| IEEE80211_IS_CHAN_11AC_VHT80_CNTRPLUS(c) || |
| IEEE80211_IS_CHAN_11AC_VHT80_CNTRMINUS(c) || |
| IEEE80211_IS_CHAN_11AC_VHT80_EDGEMINUS(c)) |
| return 0; |
| else |
| return 1; |
| case IEEE80211_MODE_AUTO: |
| return 0; |
| break; |
| } |
| return -1; |
| } |
| #undef IEEE80211_MODE_TURBO_STATIC_A |
| |
| static inline int ieee80211_chan_allowed_in_band(struct ieee80211com *ic, |
| struct ieee80211_channel *c, enum ieee80211_opmode opmode) |
| { |
| if (opmode != IEEE80211_M_STA && |
| ieee80211_check_mode_consistency(ic, ic->ic_des_mode, c)) |
| return 0; |
| |
| return 1; |
| } |
| |
| void |
| ieee80211_initiate_scan(struct ieee80211vap *vap) |
| { |
| struct ieee80211com *ic = vap->iv_ic; |
| |
| ic->ic_des_chan = IEEE80211_CHAN_ANYC; |
| |
| if (IS_UP(vap->iv_dev) && (vap->iv_opmode == IEEE80211_M_HOSTAP)) { |
| pre_announced_chanswitch(vap->iv_dev, |
| ieee80211_chan2ieee(ic, ic->ic_des_chan), |
| IEEE80211_DEFAULT_CHANCHANGE_TBTT_COUNT); |
| ic->ic_curchan = ic->ic_des_chan; |
| ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); |
| } |
| } |
| EXPORT_SYMBOL(ieee80211_initiate_scan); |
| |
| static int |
| ieee80211_ioctl_siwfreq(struct net_device *dev, struct iw_request_info *info, |
| struct iw_freq *freq, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211vap *canceled_scan_vap = NULL; |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_channel *c, *c2; |
| int i; |
| |
| if (freq->e > 1) { |
| return -EINVAL; |
| } |
| if (freq->e == 1) { |
| i = (ic->ic_mhz2ieee)(ic, freq->m / 100000, 0); |
| } else { |
| i = freq->m; |
| } |
| |
| if (i != 0) { |
| if (i > IEEE80211_CHAN_MAX) { |
| printk("Channel %d is invalid\n", i); |
| return -EINVAL; |
| } |
| |
| c = findchannel(ic, i, ic->ic_des_mode); |
| if (c == NULL) { |
| printk("Channel %d does not exist\n", i); |
| return -EINVAL; |
| } |
| |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| if ((c->ic_freq == ic->ic_curchan->ic_freq) && ic->ic_chan_is_set) { |
| if (ic->ic_get_init_cac_duration(ic) > 0) { |
| ic->ic_stop_icac_procedure(ic); |
| printk(KERN_DEBUG "ICAC: Aborted ICAC due to set channel request\n"); |
| } |
| return 0; |
| } |
| |
| if (!ic->ic_check_channel(ic, c, 0, 1)) { |
| printk("Channel %d (%d MHz) cannot be selected\n", i, c->ic_freq); |
| return -EINVAL; |
| } |
| } |
| |
| if (!ic->ic_weachan_cac_allowed && |
| (!ieee80211_is_chan_available(c)) && |
| ieee80211_is_on_weather_channel(ic, c)) { |
| printk("Weather channel %d (%d MHz) cannot be selected\n", i, c->ic_freq); |
| return -EINVAL; |
| } |
| |
| c = ieee80211_chk_update_pri_chan(ic, c, 0, "iwconfig", 1); |
| if (ic->ic_opmode == IEEE80211_M_HOSTAP && |
| isset(ic->ic_chan_pri_inactive, c->ic_ieee) && |
| isclr(ic->ic_is_inactive_autochan_only, c->ic_ieee)) { |
| return -EINVAL; |
| } |
| |
| i = c->ic_ieee; |
| |
| /* |
| * Fine tune channel selection based on desired mode: |
| * if 11b is requested, find the 11b version of any |
| * 11g channel returned, |
| * if static turbo, find the turbo version of any |
| * 11a channel return, |
| * otherwise we should be ok with what we've got. |
| */ |
| switch (ic->ic_des_mode) { |
| case IEEE80211_MODE_11B: |
| if (IEEE80211_IS_CHAN_ANYG(c)) { |
| c2 = findchannel(ic, i, IEEE80211_MODE_11B); |
| /* NB: should not happen, =>'s 11g w/o 11b */ |
| if (c2 != NULL) |
| c = c2; |
| } |
| break; |
| case IEEE80211_MODE_TURBO_A: |
| if (IEEE80211_IS_CHAN_A(c)) { |
| c2 = findchannel(ic, i, IEEE80211_MODE_TURBO_A); |
| if (c2 != NULL) |
| c = c2; |
| } |
| break; |
| default: /* NB: no static turboG */ |
| break; |
| } |
| if (ieee80211_check_mode_consistency(ic, ic->ic_des_mode, c)) { |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) |
| return -EINVAL; |
| } |
| |
| /* |
| * Cancel scan before setting desired channel or before return when the channel |
| * is same as bss channel |
| */ |
| if ((vap->iv_opmode == IEEE80211_M_HOSTAP) && ((ic->ic_flags & IEEE80211_F_SCAN) |
| #ifdef QTN_BG_SCAN |
| || (ic->ic_flags_qtn & IEEE80211_QTN_BGSCAN) |
| #endif /* QTN_BG_SCAN */ |
| )) { |
| /* |
| * Find which vap is in SCAN state(Only one can be in SCAN state at the same time, other |
| * is pending for scan done on this vap) |
| * For MBSS, it may be the primary vap, or the last vap whose mode is IEEE80211_M_HOSTAP |
| */ |
| TAILQ_FOREACH(canceled_scan_vap, &ic->ic_vaps, iv_next) { |
| if (canceled_scan_vap->iv_state == IEEE80211_S_SCAN) { |
| break; |
| } |
| } |
| if (canceled_scan_vap != NULL) { |
| if (canceled_scan_vap->iv_state != IEEE80211_S_SCAN) { |
| canceled_scan_vap = NULL; |
| } |
| } |
| |
| if (canceled_scan_vap) { |
| /* |
| * Cancel channel scan on vap which is SCAN state |
| * For example: scan triggered by freq ioctl or channel auto when boot up |
| */ |
| ieee80211_cancel_scan_no_wait(canceled_scan_vap); |
| } else { |
| /* |
| * Cancel channel scan(vap is not in SCAN state) |
| * For example: scan triggered by scan ioctl |
| */ |
| ieee80211_cancel_scan_no_wait(vap); |
| } |
| } |
| |
| if (vap->iv_state == IEEE80211_S_RUN && c == ic->ic_bsschan) |
| return 0; /* no change, return */ |
| |
| ic->ic_des_chan = c; |
| } else { |
| /* |
| * Intepret channel 0 to mean "no desired channel"; |
| * otherwise there's no way to undo fixing the desired |
| * channel. |
| */ |
| if (ic->ic_des_chan == IEEE80211_CHAN_ANYC) |
| return 0; |
| ic->ic_des_chan = IEEE80211_CHAN_ANYC; |
| } |
| |
| /* Go out of idle state and delay idle state check.*/ |
| if (ic->ic_pm_state[QTN_PM_CURRENT_LEVEL] >= BOARD_PM_LEVEL_IDLE) { |
| pm_qos_update_requirement(PM_QOS_POWER_SAVE, BOARD_PM_GOVERNOR_WLAN, BOARD_PM_LEVEL_NO); |
| ic->ic_pm_reason = IEEE80211_PM_LEVEL_SIWFREQ; |
| ieee80211_pm_queue_work(ic); |
| } |
| |
| if (ic->ic_des_chan == IEEE80211_CHAN_ANYC) { |
| ic->ic_csw_reason = IEEE80211_CSW_REASON_SCAN; |
| } else { |
| ic->ic_csw_reason = IEEE80211_CSW_REASON_MANUAL; |
| } |
| |
| if ((vap->iv_opmode == IEEE80211_M_MONITOR || |
| vap->iv_opmode == IEEE80211_M_WDS) && |
| ic->ic_des_chan != IEEE80211_CHAN_ANYC) { |
| /* Monitor and wds modes can switch directly. */ |
| ic->ic_curchan = ic->ic_des_chan; |
| if (vap->iv_state == IEEE80211_S_RUN) { |
| ic->ic_set_channel(ic); |
| } |
| } else if (vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| /* |
| * Use channel switch announcement on beacon if possible. |
| * Otherwise, ic_des_chan will take effect when we are transitioned |
| * to RUN state later. |
| * We use ic_set_channel directly if we are "running" but not "up". |
| */ |
| |
| if (IS_UP(vap->iv_dev)) { |
| if ((ic->ic_des_chan != IEEE80211_CHAN_ANYC) && |
| (vap->iv_state == IEEE80211_S_RUN)) { |
| ieee80211_enter_csa(ic, ic->ic_des_chan, NULL, |
| IEEE80211_CSW_REASON_MANUAL, |
| IEEE80211_DEFAULT_CHANCHANGE_TBTT_COUNT, |
| IEEE80211_CSA_MUST_STOP_TX, |
| IEEE80211_CSA_F_BEACON | IEEE80211_CSA_F_ACTION); |
| } else { |
| if (canceled_scan_vap) { |
| /* |
| * Scan is canceled on vap which is in SCAN state, |
| * do SCAN -> SCAN on vap of scan canceled |
| */ |
| ieee80211_new_state(canceled_scan_vap, IEEE80211_S_SCAN, 0); |
| } else { |
| ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); |
| } |
| } |
| } else if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) { |
| ic->ic_curchan = ic->ic_des_chan; |
| ic->ic_set_channel(ic); |
| } |
| } else { |
| /* Need to go through the state machine in case we need |
| * to reassociate or the like. The state machine will |
| * pickup the desired channel and avoid scanning. */ |
| if (IS_UP_AUTO(vap)) { |
| ic->ic_curchan = ic->ic_des_chan; |
| ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); |
| /* In case of no channel change, Don't Scan. Only VCO cal is required */ |
| ic->ic_set_channel(ic); |
| } else { |
| /* STA doesn't support auto channel */ |
| if ((vap->iv_opmode == IEEE80211_M_STA) && (ic->ic_des_chan == IEEE80211_CHAN_ANYC)) { |
| ic->ic_des_chan = ic->ic_curchan; |
| return -EINVAL; |
| } else { |
| ic->ic_curchan = ic->ic_des_chan; |
| ic->ic_set_channel(ic); |
| } |
| } |
| } |
| |
| if (ic->ic_get_init_cac_duration(ic) > 0) { |
| ic->ic_stop_icac_procedure(ic); |
| printk(KERN_DEBUG "ICAC: Aborted ICAC due to set channel request\n"); |
| } |
| |
| ic->ic_chan_switch_reason_record(ic, IEEE80211_CSW_REASON_MANUAL); |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_giwfreq(struct net_device *dev, struct iw_request_info *info, |
| struct iw_freq *freq, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| |
| if (vap->iv_state == IEEE80211_S_RUN && |
| vap->iv_opmode != IEEE80211_M_MONITOR) { |
| /* |
| * NB: use curchan for monitor mode so you can see |
| * manual scanning by apps like kismet. |
| */ |
| KASSERT(ic->ic_bsschan != IEEE80211_CHAN_ANYC, |
| ("bss channel not set")); |
| freq->m = ic->ic_curchan->ic_freq; |
| } else if (vap->iv_state != IEEE80211_S_INIT) { /* e.g. when scanning */ |
| if (ic->ic_curchan != IEEE80211_CHAN_ANYC) |
| freq->m = ic->ic_curchan->ic_freq; |
| else |
| freq->m = 0; |
| } else if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) { |
| freq->m = ic->ic_des_chan->ic_freq; |
| } else { |
| freq->m = 0; |
| } |
| |
| freq->m *= 100000; |
| freq->e = 1; |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_siwessid(struct net_device *dev, struct iw_request_info *info, |
| struct iw_point *data, char *ssid) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| |
| if (vap->iv_opmode == IEEE80211_M_WDS) |
| return -EOPNOTSUPP; |
| |
| if (data->flags == 0) /* ANY */ |
| vap->iv_des_nssid = 0; |
| else { |
| if (data->length > IEEE80211_NWID_LEN) |
| data->length = IEEE80211_NWID_LEN; |
| /* NB: always use entry 0 */ |
| memcpy(vap->iv_des_ssid[0].ssid, ssid, data->length); |
| vap->iv_des_ssid[0].len = data->length; |
| vap->iv_des_nssid = 1; |
| /* |
| * Deduct a trailing \0 since iwconfig passes a string |
| * length that includes this. Unfortunately this means |
| * that specifying a string with multiple trailing \0's |
| * won't be handled correctly. Not sure there's a good |
| * solution; the API is botched (the length should be |
| * exactly those bytes that are meaningful and not include |
| * extraneous stuff). |
| */ |
| if (data->length > 0 && |
| vap->iv_des_ssid[0].ssid[data->length - 1] == '\0') |
| vap->iv_des_ssid[0].len--; |
| } |
| |
| if (vap->iv_opmode == IEEE80211_M_STA) |
| return IS_UP_AUTO(vap) ? ieee80211_init(vap->iv_dev, RESCAN) : 0; |
| else |
| return IS_UP(vap->iv_dev) ? ieee80211_init(vap->iv_dev, RESCAN) : 0; |
| } |
| |
| static int |
| ieee80211_ioctl_giwessid(struct net_device *dev, struct iw_request_info *info, |
| struct iw_point *data, char *essid) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| |
| if (vap->iv_opmode == IEEE80211_M_WDS) |
| return -EOPNOTSUPP; |
| |
| data->flags = 1; /* active */ |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| if (vap->iv_des_nssid > 0) { |
| if (data->length > vap->iv_des_ssid[0].len) |
| data->length = vap->iv_des_ssid[0].len; |
| memcpy(essid, vap->iv_des_ssid[0].ssid, data->length); |
| } else |
| data->length = 0; |
| } else { |
| if (vap->iv_des_nssid == 0 && vap->iv_bss) { |
| if (data->length > vap->iv_bss->ni_esslen) |
| data->length = vap->iv_bss->ni_esslen; |
| memcpy(essid, vap->iv_bss->ni_essid, data->length); |
| } else { |
| if (data->length > vap->iv_des_ssid[0].len) |
| data->length = vap->iv_des_ssid[0].len; |
| memcpy(essid, vap->iv_des_ssid[0].ssid, data->length); |
| } |
| } |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_giwrange(struct net_device *dev, struct iw_request_info *info, |
| struct iw_point *data, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| //struct ieee80211_node *ni = vap->iv_bss; |
| struct iw_range *range = (struct iw_range *) extra; |
| struct ieee80211_rateset *rs; |
| uint8_t reported[IEEE80211_CHAN_BYTES]; /* XXX stack usage? */ |
| uint8_t *chan_active; |
| int i, r, chan_mode = 0; |
| int step = 0; |
| uint8_t sgi = 0; |
| |
| data->length = sizeof(struct iw_range); |
| memset(range, 0, sizeof(struct iw_range)); |
| |
| /* txpower (128 values, but will print out only IW_MAX_TXPOWER) */ |
| range->num_txpower = (ic->ic_txpowlimit >= 8) ? IW_MAX_TXPOWER : ic->ic_txpowlimit; |
| step = ic->ic_txpowlimit / (2 * (IW_MAX_TXPOWER - 1)); |
| |
| range->txpower[0] = 0; |
| for (i = 1; i < IW_MAX_TXPOWER; i++) |
| range->txpower[i] = (ic->ic_txpowlimit/2) |
| - (IW_MAX_TXPOWER - i - 1) * step; |
| |
| range->txpower_capa = IW_TXPOW_DBM; |
| |
| if (vap->iv_opmode == IEEE80211_M_STA || |
| vap->iv_opmode == IEEE80211_M_IBSS) { |
| range->min_pmp = 1 * 1024; |
| range->max_pmp = 65535 * 1024; |
| range->min_pmt = 1 * 1024; |
| range->max_pmt = 1000 * 1024; |
| range->pmp_flags = IW_POWER_PERIOD; |
| range->pmt_flags = IW_POWER_TIMEOUT; |
| range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | |
| IW_POWER_UNICAST_R | IW_POWER_ALL_R; |
| } |
| |
| range->we_version_compiled = WIRELESS_EXT; |
| range->we_version_source = 13; |
| |
| range->retry_capa = IW_RETRY_LIMIT; |
| range->retry_flags = IW_RETRY_LIMIT; |
| range->min_retry = 0; |
| range->max_retry = 255; |
| |
| if (vap->iv_opmode == IEEE80211_M_STA) |
| chan_active = ic->ic_chan_active_20; |
| else |
| chan_active = ic->ic_chan_active; |
| |
| range->num_frequency = 0; |
| memset(reported, 0, sizeof(reported)); |
| for (i = 0; i < ic->ic_nchans; i++) { |
| struct ieee80211_channel *c = &ic->ic_channels[i]; |
| |
| /* discard if previously reported (e.g. b/g) */ |
| if (isclr(reported, c->ic_ieee) && |
| isset(chan_active, c->ic_ieee) && |
| (ieee80211_chan_allowed_in_band(ic, c, vap->iv_opmode))) { |
| setbit(reported, c->ic_ieee); |
| range->freq[range->num_frequency].i = c->ic_ieee; |
| range->freq[range->num_frequency].m = |
| ic->ic_channels[i].ic_freq * 100000; |
| range->freq[range->num_frequency].e = 1; |
| if (++range->num_frequency == IW_MAX_FREQUENCIES) |
| break; |
| } |
| } |
| |
| /* Supported channels count */ |
| range->num_channels = range->num_frequency; |
| |
| /* Atheros' RSSI value is SNR: 0 -> 60 for old chipsets. Range |
| * for newer chipsets is unknown. This value is arbitarily chosen |
| * to give an indication that full rate will be available and to be |
| * a practicable maximum. */ |
| range->max_qual.qual = 70; |
| |
| /* XXX: This should be updated to use the current noise floor. */ |
| /* These are negative full bytes. |
| * Min. quality is noise + 1 */ |
| #define QNT_DEFAULT_NOISE 0 |
| range->max_qual.updated |= IW_QUAL_DBM; |
| range->max_qual.level = QNT_DEFAULT_NOISE + 1; |
| range->max_qual.noise = QNT_DEFAULT_NOISE; |
| |
| range->sensitivity = 1; |
| |
| range->max_encoding_tokens = IEEE80211_WEP_NKID; |
| /* XXX query driver to find out supported key sizes */ |
| range->num_encoding_sizes = 3; |
| range->encoding_size[0] = 5; /* 40-bit */ |
| range->encoding_size[1] = 13; /* 104-bit */ |
| range->encoding_size[2] = 16; /* 128-bit */ |
| |
| if (ic->ic_htcap.cap & IEEE80211_HTCAP_C_CHWIDTH40) { |
| chan_mode = 1; |
| sgi = ic->ic_htcap.cap & IEEE80211_HTCAP_C_SHORTGI40 ? 1 : 0; |
| } else { |
| sgi = ic->ic_htcap.cap & IEEE80211_HTCAP_C_SHORTGI20 ? 1 : 0; |
| } |
| rs = &ic->ic_sup_rates[ic->ic_des_mode]; |
| range->num_bitrates = rs->rs_nrates; |
| if (range->num_bitrates > MIN(IEEE80211_RATE_MAXSIZE, IW_MAX_BITRATES)) |
| range->num_bitrates = MIN(IEEE80211_RATE_MAXSIZE, IW_MAX_BITRATES); |
| for (i = 0; i < range->num_bitrates; i++) { |
| r = rs->rs_rates[i] & IEEE80211_RATE_VAL; |
| |
| /* Skip legacy rates */ |
| if(i >= (rs->rs_legacy_nrates)) |
| { |
| r = ieee80211_mcs2rate(r, chan_mode, sgi, 0); |
| } |
| range->bitrate[i] = (r * 1000000) / 2; |
| } |
| |
| /* estimated maximum TCP throughput values (bps) */ |
| range->throughput = 5500000; |
| |
| range->min_rts = 0; |
| range->max_rts = 2347; |
| range->min_frag = 256; |
| range->max_frag = 2346; |
| |
| /* Event capability (kernel) */ |
| IW_EVENT_CAPA_SET_KERNEL(range->event_capa); |
| |
| /* Event capability (driver) */ |
| if (vap->iv_opmode == IEEE80211_M_STA || |
| vap->iv_opmode == IEEE80211_M_IBSS || |
| vap->iv_opmode == IEEE80211_M_AHDEMO) { |
| /* for now, only ibss, ahdemo, sta has this cap */ |
| IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN); |
| } |
| |
| if (vap->iv_opmode == IEEE80211_M_STA) { |
| /* for sta only */ |
| IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP); |
| IW_EVENT_CAPA_SET(range->event_capa, IWEVREGISTERED); |
| IW_EVENT_CAPA_SET(range->event_capa, IWEVEXPIRED); |
| } |
| |
| /* this is used for reporting replay failure, which is used by the different encoding schemes */ |
| IW_EVENT_CAPA_SET(range->event_capa, IWEVCUSTOM); |
| |
| /* report supported WPA/WPA2 capabilities to userspace */ |
| range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 | |
| IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP; |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_setspy(struct net_device *dev, struct iw_request_info *info, |
| struct iw_point *data, char *extra) |
| { |
| /* save the list of node addresses */ |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct sockaddr address[IW_MAX_SPY]; |
| unsigned int number = data->length; |
| int i; |
| |
| if (number > IW_MAX_SPY) |
| return -E2BIG; |
| |
| /* get the addresses into the driver */ |
| if (data->pointer) { |
| if (copy_from_user(address, data->pointer, |
| sizeof(struct sockaddr) * number)) |
| return -EFAULT; |
| } else { |
| return -EFAULT; |
| } |
| |
| /* copy the MAC addresses into a list */ |
| if (number > 0) { |
| /* extract the MAC addresses */ |
| for (i = 0; i < number; i++) |
| memcpy(&vap->iv_spy.mac[i * IEEE80211_ADDR_LEN], |
| address[i].sa_data, IEEE80211_ADDR_LEN); |
| /* init rssi timestamps */ |
| memset(vap->iv_spy.ts_rssi, 0, IW_MAX_SPY * sizeof(u_int32_t)); |
| } |
| vap->iv_spy.num = number; |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_getspy(struct net_device *dev, struct iw_request_info *info, |
| struct iw_point *data, char *extra) |
| { |
| /* |
| * locate nodes by mac (ieee80211_find_node()), |
| * copy out rssi, set updated flag appropriately |
| */ |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211_node_table *nt = &vap->iv_ic->ic_sta; |
| struct ieee80211_node *ni; |
| struct ieee80211com *ic = vap->iv_ic; |
| struct sockaddr *address; |
| struct iw_quality *spy_stat; |
| unsigned int number = vap->iv_spy.num; |
| int i; |
| |
| address = (struct sockaddr *) extra; |
| spy_stat = (struct iw_quality *) (extra + number * sizeof(struct sockaddr)); |
| |
| for (i = 0; i < number; i++) { |
| memcpy(address[i].sa_data, &vap->iv_spy.mac[i * IEEE80211_ADDR_LEN], |
| IEEE80211_ADDR_LEN); |
| address[i].sa_family = AF_PACKET; |
| } |
| |
| /* locate a node, read its rssi, check if updated, convert to dBm */ |
| for (i = 0; i < number; i++) { |
| ni = ieee80211_find_node(nt, &vap->iv_spy.mac[i * IEEE80211_ADDR_LEN]); |
| /* check we are associated w/ this vap */ |
| if (ni) { |
| if (ni->ni_vap == vap) { |
| set_quality(&spy_stat[i], ni->ni_rssi, ic->ic_channoise); |
| if (ni->ni_rstamp != vap->iv_spy.ts_rssi[i]) { |
| vap->iv_spy.ts_rssi[i] = ni->ni_rstamp; |
| } else { |
| spy_stat[i].updated = 0; |
| } |
| } |
| ieee80211_free_node(ni); |
| } else { |
| spy_stat[i].updated = IW_QUAL_ALL_INVALID; |
| } |
| } |
| |
| /* copy results to userspace */ |
| data->length = number; |
| return 0; |
| } |
| |
| /* Enhanced iwspy support */ |
| static int |
| ieee80211_ioctl_setthrspy(struct net_device *dev, struct iw_request_info *info, |
| struct iw_point *data, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct iw_thrspy threshold; |
| |
| if (data->length != 1) |
| return -EINVAL; |
| |
| /* get the threshold values into the driver */ |
| if (data->pointer) { |
| if (copy_from_user(&threshold, data->pointer, |
| sizeof(struct iw_thrspy))) |
| return -EFAULT; |
| } else |
| return -EINVAL; |
| |
| if (threshold.low.level == 0) { |
| /* disable threshold */ |
| vap->iv_spy.thr_low = 0; |
| vap->iv_spy.thr_high = 0; |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG, |
| "%s: disabled iw_spy threshold\n", __func__); |
| } else { |
| /* We are passed a signal level/strength - calculate |
| * corresponding RSSI values */ |
| /* XXX: We should use current noise value. */ |
| vap->iv_spy.thr_low = threshold.low.level + QNT_DEFAULT_NOISE; |
| vap->iv_spy.thr_high = threshold.high.level + QNT_DEFAULT_NOISE; |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG, |
| "%s: enabled iw_spy threshold\n", __func__); |
| } |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_getthrspy(struct net_device *dev, struct iw_request_info *info, |
| struct iw_point *data, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct iw_thrspy *threshold; |
| |
| threshold = (struct iw_thrspy *) extra; |
| |
| /* set threshold values */ |
| set_quality(&(threshold->low), vap->iv_spy.thr_low, ic->ic_channoise); |
| set_quality(&(threshold->high), vap->iv_spy.thr_high, ic->ic_channoise); |
| |
| /* copy results to userspace */ |
| data->length = 1; |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_siwmode(struct net_device *dev, struct iw_request_info *info, |
| __u32 *mode, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ifmediareq imr; |
| int valid = 0; |
| |
| memset(&imr, 0, sizeof(imr)); |
| vap->iv_media.ifm_status((void *) vap, &imr); |
| |
| if (imr.ifm_active & IFM_IEEE80211_HOSTAP) |
| valid = (*mode == IW_MODE_MASTER); |
| else if (imr.ifm_active & IFM_IEEE80211_MONITOR) |
| valid = (*mode == IW_MODE_MONITOR); |
| else if (imr.ifm_active & IFM_IEEE80211_ADHOC) |
| valid = (*mode == IW_MODE_ADHOC); |
| else if (imr.ifm_active & IFM_IEEE80211_WDS) |
| valid = (*mode == IW_MODE_REPEAT); |
| else |
| valid = (*mode == IW_MODE_INFRA); |
| |
| return valid ? 0 : -EINVAL; |
| } |
| |
| static int |
| ieee80211_ioctl_giwmode(struct net_device *dev, struct iw_request_info *info, |
| __u32 *mode, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ifmediareq imr; |
| |
| memset(&imr, 0, sizeof(imr)); |
| vap->iv_media.ifm_status((void *) vap, &imr); |
| |
| if (imr.ifm_active & IFM_IEEE80211_HOSTAP) |
| *mode = IW_MODE_MASTER; |
| else if (imr.ifm_active & IFM_IEEE80211_MONITOR) |
| *mode = IW_MODE_MONITOR; |
| else if (imr.ifm_active & IFM_IEEE80211_ADHOC) |
| *mode = IW_MODE_ADHOC; |
| else if (imr.ifm_active & IFM_IEEE80211_WDS) |
| *mode = IW_MODE_REPEAT; |
| else |
| *mode = IW_MODE_INFRA; |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_siwpower(struct net_device *dev, struct iw_request_info *info, |
| struct iw_param *wrq, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| |
| /* XXX: These values, flags, and caps do not seem to be used elsewhere |
| * at all? */ |
| |
| if ((ic->ic_caps & IEEE80211_C_PMGT) == 0) |
| return -EOPNOTSUPP; |
| |
| if (wrq->disabled) { |
| if (ic->ic_flags & IEEE80211_F_PMGTON) |
| ic->ic_flags &= ~IEEE80211_F_PMGTON; |
| } else { |
| switch (wrq->flags & IW_POWER_MODE) { |
| case IW_POWER_UNICAST_R: |
| case IW_POWER_ALL_R: |
| case IW_POWER_ON: |
| if (wrq->flags & IW_POWER_PERIOD) { |
| if (IEEE80211_BINTVAL_VALID(wrq->value)) |
| ic->ic_lintval = IEEE80211_MS_TO_TU(wrq->value); |
| else |
| return -EINVAL; |
| } |
| if (wrq->flags & IW_POWER_TIMEOUT) |
| ic->ic_holdover = IEEE80211_MS_TO_TU(wrq->value); |
| |
| ic->ic_flags |= IEEE80211_F_PMGTON; |
| break; |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| return ic->ic_reset(ic); |
| } |
| |
| static int |
| ieee80211_ioctl_giwpower(struct net_device *dev, struct iw_request_info *info, |
| struct iw_param *rrq, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| |
| rrq->disabled = (ic->ic_flags & IEEE80211_F_PMGTON) == 0; |
| if (!rrq->disabled) { |
| switch (rrq->flags & IW_POWER_TYPE) { |
| case IW_POWER_TIMEOUT: |
| rrq->flags = IW_POWER_TIMEOUT; |
| rrq->value = IEEE80211_TU_TO_MS(ic->ic_holdover); |
| break; |
| case IW_POWER_PERIOD: |
| rrq->flags = IW_POWER_PERIOD; |
| rrq->value = IEEE80211_TU_TO_MS(ic->ic_lintval); |
| break; |
| } |
| rrq->flags |= IW_POWER_ALL_R; |
| } |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_siwretry(struct net_device *dev, struct iw_request_info *info, |
| struct iw_param *rrq, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| |
| if (rrq->disabled) { |
| if (vap->iv_flags & IEEE80211_F_SWRETRY) { |
| vap->iv_flags &= ~IEEE80211_F_SWRETRY; |
| goto done; |
| } |
| return 0; |
| } |
| |
| if ((vap->iv_caps & IEEE80211_C_SWRETRY) == 0) |
| return -EOPNOTSUPP; |
| if (rrq->flags == IW_RETRY_LIMIT) { |
| if (rrq->value >= 0) { |
| vap->iv_txmin = rrq->value; |
| vap->iv_txmax = rrq->value; /* XXX */ |
| vap->iv_txlifetime = 0; /* XXX */ |
| vap->iv_flags |= IEEE80211_F_SWRETRY; |
| } else { |
| vap->iv_flags &= ~IEEE80211_F_SWRETRY; |
| } |
| return 0; |
| } |
| done: |
| return IS_UP(vap->iv_dev) ? ic->ic_reset(ic) : 0; |
| } |
| |
| static int |
| ieee80211_ioctl_giwretry(struct net_device *dev, struct iw_request_info *info, |
| struct iw_param *rrq, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| |
| rrq->disabled = (vap->iv_flags & IEEE80211_F_SWRETRY) == 0; |
| if (!rrq->disabled) { |
| switch (rrq->flags & IW_RETRY_TYPE) { |
| case IW_RETRY_LIFETIME: |
| rrq->flags = IW_RETRY_LIFETIME; |
| rrq->value = IEEE80211_TU_TO_MS(vap->iv_txlifetime); |
| break; |
| case IW_RETRY_LIMIT: |
| rrq->flags = IW_RETRY_LIMIT; |
| switch (rrq->flags & IW_RETRY_MODIFIER) { |
| case IW_RETRY_MIN: |
| rrq->flags |= IW_RETRY_MAX; |
| rrq->value = vap->iv_txmin; |
| break; |
| case IW_RETRY_MAX: |
| rrq->flags |= IW_RETRY_MAX; |
| rrq->value = vap->iv_txmax; |
| break; |
| } |
| break; |
| } |
| } |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_siwtxpow(struct net_device *dev, struct iw_request_info *info, |
| struct iw_param *rrq, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| int fixed, disabled; |
| |
| fixed = (ic->ic_flags & IEEE80211_F_TXPOW_FIXED); |
| if (!vap->iv_bss) { |
| return 0; |
| } |
| |
| disabled = (fixed && vap->iv_bss->ni_txpower == 0); |
| if (rrq->disabled) { |
| if (!disabled) { |
| if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) |
| return -EOPNOTSUPP; |
| ic->ic_flags |= IEEE80211_F_TXPOW_FIXED; |
| vap->iv_bss->ni_txpower = 0; |
| goto done; |
| } |
| return 0; |
| } |
| |
| if (rrq->fixed) { |
| if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) |
| return -EOPNOTSUPP; |
| if (rrq->flags != IW_TXPOW_DBM) |
| return -EOPNOTSUPP; |
| if (ic->ic_bsschan != IEEE80211_CHAN_ANYC) { |
| if (ic->ic_bsschan->ic_maxregpower >= rrq->value && |
| ic->ic_txpowlimit/2 >= rrq->value) { |
| vap->iv_bss->ni_txpower = 2 * rrq->value; |
| ic->ic_newtxpowlimit = 2 * rrq->value; |
| ic->ic_flags |= IEEE80211_F_TXPOW_FIXED; |
| } else |
| return -EINVAL; |
| } else { |
| /* |
| * No channel set yet |
| */ |
| if (ic->ic_txpowlimit/2 >= rrq->value) { |
| vap->iv_bss->ni_txpower = 2 * rrq->value; |
| ic->ic_newtxpowlimit = 2 * rrq->value; |
| ic->ic_flags |= IEEE80211_F_TXPOW_FIXED; |
| } |
| else |
| return -EINVAL; |
| } |
| } else { |
| if (!fixed) /* no change */ |
| return 0; |
| ic->ic_flags &= ~IEEE80211_F_TXPOW_FIXED; |
| } |
| done: |
| return ic->ic_reset(ic); |
| } |
| |
| static int |
| ieee80211_ioctl_giwtxpow(struct net_device *dev, struct iw_request_info *info, |
| struct iw_param *rrq, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| |
| rrq->fixed = (ic->ic_flags & IEEE80211_F_TXPOW_FIXED) != 0; |
| rrq->disabled = (rrq->fixed && rrq->value == 0); |
| rrq->flags = IW_TXPOW_DBM; |
| |
| if (vap->iv_bss) { |
| /* ni_txpower is stored in 0.5dBm units */ |
| rrq->value = vap->iv_bss->ni_txpower >> 1; |
| } else { |
| rrq->value = 0; |
| } |
| |
| return 0; |
| } |
| |
| struct waplistreq { /* XXX: not the right place for declaration? */ |
| struct ieee80211vap *vap; |
| struct sockaddr addr[IW_MAX_AP]; |
| struct iw_quality qual[IW_MAX_AP]; |
| int i; |
| }; |
| |
| static int |
| waplist_cb(void *arg, const struct ieee80211_scan_entry *se) |
| { |
| struct waplistreq *req = arg; |
| int i = req->i; |
| |
| if (i >= IW_MAX_AP) |
| return 0; |
| req->addr[i].sa_family = ARPHRD_ETHER; |
| if (req->vap->iv_opmode == IEEE80211_M_HOSTAP) |
| IEEE80211_ADDR_COPY(req->addr[i].sa_data, se->se_macaddr); |
| else |
| IEEE80211_ADDR_COPY(req->addr[i].sa_data, se->se_bssid); |
| set_quality(&req->qual[i], se->se_rssi, QNT_DEFAULT_NOISE); |
| req->i = i + 1; |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_iwaplist(struct net_device *dev, struct iw_request_info *info, |
| struct iw_point *data, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct waplistreq req; /* XXX off stack */ |
| |
| req.vap = vap; |
| req.i = 0; |
| ieee80211_scan_iterate(ic, waplist_cb, &req); |
| |
| data->length = req.i; |
| memcpy(extra, &req.addr, req.i * sizeof(req.addr[0])); |
| data->flags = 1; /* signal quality present (sort of) */ |
| memcpy(extra + req.i * sizeof(req.addr[0]), &req.qual, |
| req.i * sizeof(req.qual[0])); |
| |
| return 0; |
| } |
| |
| #ifdef SIOCGIWSCAN |
| static qfdr_remote_siwscan_hook_t qfdr_remote_siwscan_hook = NULL; |
| void ieee80211_register_qfdr_remote_siwscan_hook(qfdr_remote_siwscan_hook_t hook) |
| { |
| qfdr_remote_siwscan_hook = hook; |
| } |
| EXPORT_SYMBOL(ieee80211_register_qfdr_remote_siwscan_hook); |
| |
| static int |
| ieee80211_ioctl_siwscan(struct net_device *dev, struct iw_request_info *info, |
| struct iw_point *data, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| |
| uint32_t scan_flags = 0; |
| uint16_t pick_flags = 0; |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_scan_state *ss = ic->ic_scan; |
| int dfs_channel_available = 0; |
| int is_remote_req = 0; |
| |
| if (info->cmd & QFDR_REMOTE_CMD) { |
| is_remote_req = 1; |
| info->cmd &= ~QFDR_REMOTE_CMD; |
| } |
| |
| if (is_remote_req == 0 && qfdr_remote_siwscan_hook != NULL) |
| qfdr_remote_siwscan_hook(vap->iv_dev->name, data); |
| |
| /* |
| * XXX don't permit a scan to be started unless we |
| * know the device is ready. For the moment this means |
| * the device is marked up as this is the required to |
| * initialize the hardware. It would be better to permit |
| * scanning prior to being up but that'll require some |
| * changes to the infrastructure. |
| */ |
| if (!IS_UP(vap->iv_dev)) |
| return -ENETDOWN; /* XXX */ |
| |
| if ((ic->ic_bsschan != IEEE80211_CHAN_ANYC && |
| IEEE80211_IS_CHAN_CAC_IN_PROGRESS(ic->ic_bsschan)) || |
| ic->ic_ocac.ocac_running) |
| return -EBUSY; |
| |
| if (!ieee80211_should_scan(vap)) |
| return -EAGAIN; |
| |
| |
| if (ic->ic_get_init_cac_duration(ic) > 0) { |
| return -EAGAIN; |
| } |
| |
| /* XXX always manual... */ |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, |
| "%s: active scan request\n", __func__); |
| preempt_scan(dev, 100, 100); |
| ss->is_scan_valid = 1; |
| ic->ic_csw_reason = IEEE80211_CSW_REASON_SCAN; |
| |
| if (data && (data->flags & IW_SCAN_THIS_ESSID)) { |
| struct iw_scan_req req; |
| struct ieee80211_scan_ssid ssid; |
| int copyLength; |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, |
| "%s: SCAN_THIS_ESSID requested\n", __func__); |
| if (data->length > sizeof req) { |
| copyLength = sizeof req; |
| } else { |
| copyLength = data->length; |
| } |
| memset(&req, 0, sizeof req); |
| if (is_remote_req) |
| memcpy(&req, data->pointer, copyLength); |
| else { |
| if (copy_from_user(&req, data->pointer, copyLength)) |
| return -EFAULT; |
| } |
| memcpy(&ssid.ssid, req.essid, sizeof ssid.ssid); |
| ssid.len = req.essid_len; |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, |
| "%s: requesting scan of essid '%s'\n", __func__, ssid.ssid); |
| (void) ieee80211_start_scan(vap, |
| IEEE80211_SCAN_ACTIVE | |
| IEEE80211_SCAN_NOPICK | |
| IEEE80211_SCAN_ONCE | |
| (IEEE80211_USE_QTN_BGSCAN(vap) ? IEEE80211_SCAN_QTN_BGSCAN: 0) | |
| ((vap->iv_opmode == IEEE80211_M_HOSTAP) ? IEEE80211_SCAN_FLUSH : 0), |
| IEEE80211_SCAN_FOREVER, |
| 1, &ssid); |
| return 0; |
| } |
| |
| if (data && data->pointer) { |
| u_int16_t flags_tmp; |
| u_int16_t flags_bg_scan_mode = 0; |
| |
| |
| if (is_remote_req) |
| memcpy(&pick_flags, data->pointer, sizeof(pick_flags)); |
| else { |
| if (copy_from_user(&pick_flags, data->pointer, sizeof(pick_flags))) |
| return -EFAULT; |
| } |
| |
| flags_tmp = pick_flags & IEEE80211_PICK_ALGORITHM_MASK; |
| flags_bg_scan_mode = 0; |
| |
| /* |
| * For DFS reentry, check if any DFS channel is available. |
| * If not, skip channel scan and return directly. |
| */ |
| if (pick_flags & (IEEE80211_PICK_REENTRY | IEEE80211_PICK_DFS)) { |
| if (ic->ic_is_dfs_chans_available_for_dfs_reentry) { |
| if ((dfs_channel_available = ic->ic_is_dfs_chans_available_for_dfs_reentry(ic, vap)) <= 0) { |
| return dfs_channel_available; |
| } |
| } else { |
| return -EOPNOTSUPP; |
| } |
| } |
| |
| if (flags_tmp == IEEE80211_PICK_REENTRY || flags_tmp == IEEE80211_PICK_CLEAREST) { |
| scan_flags = IEEE80211_SCAN_FLUSH; |
| /* Go out of idle state and delay idle state check.*/ |
| if (ic->ic_pm_state[QTN_PM_CURRENT_LEVEL] >= BOARD_PM_LEVEL_IDLE) { |
| pm_qos_update_requirement(PM_QOS_POWER_SAVE, BOARD_PM_GOVERNOR_WLAN, BOARD_PM_LEVEL_NO); |
| ic->ic_pm_reason = IEEE80211_PM_LEVEL_SIWSCAN; |
| ieee80211_pm_queue_work(ic); |
| } |
| } |
| else if (flags_tmp == IEEE80211_PICK_NOPICK) { |
| scan_flags = IEEE80211_SCAN_NOPICK; |
| } |
| #ifdef QTN_BG_SCAN |
| else if (flags_tmp == IEEE80211_PICK_NOPICK_BG) { |
| scan_flags = IEEE80211_SCAN_NOPICK; |
| if ((vap->iv_opmode == IEEE80211_M_HOSTAP && ic->ic_sta_assoc > 0) || |
| (vap->iv_opmode == IEEE80211_M_STA && vap->iv_state == IEEE80211_S_RUN)) { |
| scan_flags |= IEEE80211_SCAN_QTN_BGSCAN; |
| flags_bg_scan_mode = pick_flags & IEEE80211_PICK_BG_MODE_MASK; |
| if (IS_MULTIPLE_BITS_SET(flags_bg_scan_mode)) { |
| /* use auto mode if multiple modes are set */ |
| flags_bg_scan_mode = 0; |
| } |
| } |
| } |
| |
| #endif /* QTN_BG_SCAN */ |
| if (pick_flags & IEEE80211_PICK_SCAN_FLUSH) { |
| scan_flags |= IEEE80211_SCAN_FLUSH; |
| } |
| |
| /* |
| * set pick flags before start scanning, and remember to clean it when selection channel done |
| * only for AP mode |
| */ |
| ss->ss_pick_flags = (pick_flags & (~IEEE80211_PICK_CONTROL_MASK)) | flags_bg_scan_mode; |
| } |
| |
| if (IEEE80211_USE_QTN_BGSCAN(vap)) |
| scan_flags |= IEEE80211_SCAN_QTN_BGSCAN; |
| |
| (void) ieee80211_start_scan(vap, IEEE80211_SCAN_ACTIVE | |
| scan_flags | IEEE80211_SCAN_ONCE, |
| IEEE80211_SCAN_FOREVER, |
| /* XXX use ioctl params */ |
| vap->iv_des_nssid, vap->iv_des_ssid); |
| return 0; |
| } |
| |
| int qfdr_siwscan_for_remote(struct qfdr_remote_scan_req *remote_req) |
| { |
| struct net_device *dev; |
| struct iw_point *data; |
| struct iw_point data_remote; |
| struct iw_request_info info; |
| |
| dev = dev_get_by_name(&init_net, remote_req->dev_name); |
| if (!dev) |
| return -EINVAL; |
| |
| memset(&info, 0, sizeof(info)); |
| info.cmd = SIOCSIWSCAN | QFDR_REMOTE_CMD; |
| |
| if (remote_req->type == QFDR_SIWSCAN_SIMPLE) |
| data = NULL; |
| else { |
| data_remote.length = remote_req->length; |
| data_remote.flags = remote_req->flags; |
| data_remote.pointer = remote_req->pointer; |
| data = &data_remote; |
| } |
| |
| ieee80211_ioctl_siwscan(dev, &info, data, NULL); |
| |
| dev_put(dev); |
| return 0; |
| } |
| EXPORT_SYMBOL(qfdr_siwscan_for_remote); |
| |
| /* |
| * Encode a WPA or RSN information element as a custom |
| * element using the hostap format. |
| */ |
| static u_int |
| encode_ie(void *buf, size_t bufsize, const u_int8_t *ie, size_t ielen, |
| const char *leader, size_t leader_len) |
| { |
| char *p; |
| int i; |
| |
| if (bufsize < leader_len) |
| return 0; |
| p = buf; |
| memcpy(p, leader, leader_len); |
| bufsize -= leader_len; |
| p += leader_len; |
| for (i = 0; i < ielen && bufsize > 2; i++) { |
| p += sprintf(p, "%02x", ie[i]); |
| bufsize -= 2; |
| } |
| return (i == ielen ? p - (char *)buf : 0); |
| } |
| |
| /* |
| * Recalculate the RSSI of MBS/RBS |
| * Make sure the RSSI has the following priority. |
| * MBS in best rate range has the highest level RSSI. |
| * RBS in best rate range has the second high level RSSI. |
| * MBS not in best rate range has the third high level RSSI. |
| * RBS not in best rate range has the fourth high level RSSI. |
| */ |
| static int8_t |
| ieee80211_calcu_extwds_node_rssi(struct ieee80211com *ic, |
| const struct ieee80211_scan_entry *se) |
| { |
| int8_t rssi = se->se_rssi; |
| if (se->se_ext_role == IEEE80211_EXTENDER_ROLE_NONE) |
| return rssi; |
| |
| if (se->se_ext_role == IEEE80211_EXTENDER_ROLE_MBS) { |
| if (rssi >= ic->ic_extender_mbs_best_rssi) { |
| rssi = IEEE80211_EXTWDS_MBS_BEST_RATE_RSSI; |
| } else { |
| rssi = (rssi - IEEE80211_EXTWDS_MIN_PSEUDO_RSSI) * |
| IEEE80211_EXTWDS_BEST_RATE_BDRY_RSSI / |
| (ic->ic_extender_mbs_best_rssi - |
| IEEE80211_EXTWDS_MIN_PSEUDO_RSSI) * |
| ic->ic_extender_mbs_wgt / 10; |
| } |
| } else if (se->se_ext_role == IEEE80211_EXTENDER_ROLE_RBS) { |
| if (rssi >= ic->ic_extender_rbs_best_rssi) { |
| rssi = (rssi - ic->ic_extender_rbs_best_rssi) * |
| (IEEE80211_EXTWDS_MAX_PSEUDO_RSSI - |
| IEEE80211_EXTWDS_BEST_RATE_BDRY_RSSI) / |
| (IEEE80211_EXTWDS_MAX_PSEUDO_RSSI - |
| ic->ic_extender_rbs_best_rssi) + |
| IEEE80211_EXTWDS_BEST_RATE_BDRY_RSSI; |
| } else { |
| rssi = (rssi - IEEE80211_EXTWDS_MIN_PSEUDO_RSSI) * |
| IEEE80211_EXTWDS_BEST_RATE_BDRY_RSSI / |
| (ic->ic_extender_rbs_best_rssi - |
| IEEE80211_EXTWDS_MIN_PSEUDO_RSSI) * |
| ic->ic_extender_rbs_wgt / 10; |
| } |
| } |
| |
| return rssi; |
| } |
| |
| static int |
| giwscan_cb(void *arg, const struct ieee80211_scan_entry *se) |
| { |
| struct iwscanreq *req = arg; |
| struct ieee80211vap *vap = req->vap; |
| struct ieee80211com *ic = vap->iv_ic; |
| struct iw_request_info *info = req->info; |
| char *current_ev = req->current_ev; |
| char *end_buf = req->end_buf; |
| char *last_ev; |
| #define MAX_IE_LENGTH 257 |
| char buf[MAX_IE_LENGTH]; |
| #ifndef IWEVGENIE |
| static const char rsn_leader[] = IEEE80211_IE_LEADER_STR_RSN; |
| static const char wpa_leader[] = IEEE80211_IE_LEADER_STR_WPA; |
| #endif |
| struct iw_event iwe; |
| char *current_val; |
| int j; |
| u_int8_t chan_mode = 0; |
| uint8_t sgi = 0; |
| u_int8_t k, r; |
| u_int16_t mask; |
| struct ieee80211_ie_htcap *htcap; |
| struct ieee80211_ie_vhtcap *vhtcap; |
| int rate_ie_exist = 0; |
| |
| if (current_ev >= end_buf) |
| return E2BIG; |
| |
| memset(&iwe, 0, sizeof(iwe)); |
| last_ev = current_ev; |
| iwe.cmd = SIOCGIWAP; |
| iwe.u.ap_addr.sa_family = ARPHRD_ETHER; |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) |
| IEEE80211_ADDR_COPY(iwe.u.ap_addr.sa_data, se->se_macaddr); |
| else |
| IEEE80211_ADDR_COPY(iwe.u.ap_addr.sa_data, se->se_bssid); |
| current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, IW_EV_ADDR_LEN); |
| |
| /* We ran out of space in the buffer. */ |
| if (last_ev == current_ev) |
| return E2BIG; |
| |
| memset(&iwe, 0, sizeof(iwe)); |
| last_ev = current_ev; |
| iwe.cmd = SIOCGIWESSID; |
| iwe.u.data.flags = 1; |
| iwe.u.data.length = se->se_ssid[1]; |
| current_ev = iwe_stream_add_point(info, current_ev, |
| end_buf, &iwe, (char *)se->se_ssid + 2); |
| |
| /* We ran out of space in the buffer. */ |
| if (last_ev == current_ev) |
| return E2BIG; |
| |
| if (se->se_capinfo & (IEEE80211_CAPINFO_ESS|IEEE80211_CAPINFO_IBSS)) { |
| memset(&iwe, 0, sizeof(iwe)); |
| last_ev = current_ev; |
| iwe.cmd = SIOCGIWMODE; |
| iwe.u.mode = se->se_capinfo & IEEE80211_CAPINFO_ESS ? |
| IW_MODE_MASTER : IW_MODE_ADHOC; |
| current_ev = iwe_stream_add_event(info, current_ev, |
| end_buf, &iwe, IW_EV_UINT_LEN); |
| |
| /* We ran out of space in the buffer. */ |
| if (last_ev == current_ev) |
| return E2BIG; |
| } |
| |
| memset(&iwe, 0, sizeof(iwe)); |
| last_ev = current_ev; |
| iwe.cmd = SIOCGIWFREQ; |
| iwe.u.freq.m = se->se_chan->ic_freq * 100000; |
| iwe.u.freq.e = 1; |
| current_ev = iwe_stream_add_event(info, current_ev, |
| end_buf, &iwe, IW_EV_FREQ_LEN); |
| |
| /* We ran out of space in the buffer. */ |
| if (last_ev == current_ev) |
| return E2BIG; |
| |
| memset(&iwe, 0, sizeof(iwe)); |
| last_ev = current_ev; |
| iwe.cmd = IWEVQUAL; |
| set_quality(&iwe.u.qual, se->se_rssi, QNT_DEFAULT_NOISE); |
| /* |
| * Assign the real RSSI to 'level' for MBS/RBS, so wpa_supplicant |
| * can run roaming between MBS and RBS base on the 'level' value. |
| */ |
| if (se->se_ext_role != IEEE80211_EXTENDER_ROLE_NONE) { |
| iwe.u.qual.qual = ieee80211_calcu_extwds_node_rssi(ic, se); |
| iwe.u.qual.level = iwe.u.qual.qual - IEEE80211_PSEUDO_RSSI_TRANSITON_FACTOR; |
| } |
| current_ev = iwe_stream_add_event(info, current_ev, |
| end_buf, &iwe, IW_EV_QUAL_LEN); |
| |
| /* We ran out of space in the buffer */ |
| if (last_ev == current_ev) |
| return E2BIG; |
| |
| memset(&iwe, 0, sizeof(iwe)); |
| last_ev = current_ev; |
| iwe.cmd = SIOCGIWENCODE; |
| if (se->se_capinfo & IEEE80211_CAPINFO_PRIVACY) |
| iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; |
| else |
| iwe.u.data.flags = IW_ENCODE_DISABLED; |
| iwe.u.data.length = 0; |
| current_ev = iwe_stream_add_point(info, current_ev, end_buf, &iwe, ""); |
| |
| /* We ran out of space in the buffer. */ |
| if (last_ev == current_ev) |
| return E2BIG; |
| |
| memset(&iwe, 0, sizeof(iwe)); |
| last_ev = current_ev; |
| iwe.cmd = SIOCGIWRATE; |
| current_val = current_ev + IW_EV_LCP_LEN; |
| /* NB: not sorted, does it matter? */ |
| for (j = 0; j < se->se_rates[1]; j++) { |
| int r = se->se_rates[2 + j] & IEEE80211_RATE_VAL; |
| if (r != 0) { |
| iwe.u.bitrate.value = r * (1000000 / 2); |
| current_val = iwe_stream_add_value(info, current_ev, |
| current_val, end_buf, &iwe, |
| IW_EV_PARAM_LEN); |
| rate_ie_exist++; |
| } |
| } |
| for (j = 0; j < se->se_xrates[1]; j++) { |
| int r = se->se_xrates[2+j] & IEEE80211_RATE_VAL; |
| if (r != 0) { |
| iwe.u.bitrate.value = r * (1000000 / 2); |
| current_val = iwe_stream_add_value(info, current_ev, |
| current_val, end_buf, &iwe, |
| IW_EV_PARAM_LEN); |
| rate_ie_exist++; |
| } |
| } |
| |
| htcap = (struct ieee80211_ie_htcap *)se->se_htcap_ie; |
| if (htcap) { |
| r = 0; |
| if (htcap->hc_cap[0] & IEEE80211_HTCAP_C_CHWIDTH40) { |
| chan_mode = 1; |
| sgi = htcap->hc_cap[0] & IEEE80211_HTCAP_C_SHORTGI40 ? 1 : 0; |
| } else { |
| chan_mode = 0; |
| sgi = htcap->hc_cap[0] & IEEE80211_HTCAP_C_SHORTGI20 ? 1 : 0; |
| } |
| for (j = IEEE80211_HT_MCSSET_20_40_NSS1; j <= IEEE80211_HT_MCSSET_20_40_NSS4; j++) { |
| mask = 1; |
| for (k = 0; k < 8; k++, r++) { |
| if (htcap->hc_mcsset[j] & mask) { |
| /* Copy HT rates */ |
| iwe.u.bitrate.value = ieee80211_mcs2rate(r, chan_mode, sgi, 0) * (1000000 / 2); |
| current_val = iwe_stream_add_value(info, |
| current_ev, |
| current_val, |
| end_buf, |
| &iwe, |
| IW_EV_PARAM_LEN); |
| rate_ie_exist++; |
| } |
| mask = mask << 1; |
| } |
| } |
| } |
| |
| vhtcap = (struct ieee80211_ie_vhtcap *)se->se_vhtcap_ie; |
| if (vhtcap) { |
| u_int16_t mcsmap = 0; |
| r = 0; |
| /* 80+80 or 160 Mhz */ |
| if (IEEE80211_VHTCAP_GET_CHANWIDTH(vhtcap)) { |
| chan_mode = 1; |
| sgi = IEEE80211_VHTCAP_GET_SGI_160MHZ(vhtcap); |
| } else { |
| chan_mode = 0; |
| sgi = IEEE80211_VHTCAP_GET_SGI_80MHZ(vhtcap); |
| } |
| mask = 0x3; |
| mcsmap = (u_int16_t)IEEE80211_VHTCAP_GET_TX_MCS_NSS(vhtcap); |
| for (k = 0; k < 8; k++) { |
| if ((mcsmap & mask) != mask) { |
| int m; |
| int val = (mcsmap & mask)>>(k * 2); |
| r = (val == 2) ? 9: (val == 1) ? 8 : 7; |
| /* Copy HT rates */ |
| for (m = 0; m <= r; m++) { |
| iwe.u.bitrate.value = |
| (ieee80211_mcs2rate(m, chan_mode, sgi, 1) |
| * (1000000 / 2)) * (k+1); |
| current_val = iwe_stream_add_value(info, |
| current_ev, |
| current_val, |
| end_buf, |
| &iwe, |
| IW_EV_PARAM_LEN); |
| rate_ie_exist++; |
| } |
| mask = mask << 2; |
| } else { |
| break; |
| } |
| } |
| } |
| |
| /* remove fixed header if no rates were added */ |
| if ((current_val - current_ev) > IW_EV_LCP_LEN) { |
| current_ev = current_val; |
| } else { |
| /* We ran out of space in the buffer. */ |
| if (last_ev == current_ev && rate_ie_exist) |
| return E2BIG; |
| } |
| |
| memset(&iwe, 0, sizeof(iwe)); |
| last_ev = current_ev; |
| iwe.cmd = IWEVCUSTOM; |
| snprintf(buf, sizeof(buf), "bcn_int=%d", se->se_intval); |
| iwe.u.data.length = strlen(buf); |
| current_ev = iwe_stream_add_point(info, current_ev, end_buf, &iwe, buf); |
| |
| /* We ran out of space in the buffer. */ |
| if (last_ev == current_ev) |
| return E2BIG; |
| |
| memset(&iwe, 0, sizeof(iwe)); |
| memset(buf, 0, sizeof(buf)); |
| last_ev = current_ev; |
| iwe.cmd = IWEVCUSTOM; |
| snprintf(buf, sizeof(buf) - 1, IEEE80211_IE_LEADER_STR_EXT_ROLE"%d", se->se_ext_role); |
| iwe.u.data.length = strlen(buf) + 1; |
| current_ev = iwe_stream_add_point(info, current_ev, end_buf, &iwe, buf); |
| |
| /* We ran out of space in the buffer. */ |
| if (last_ev == current_ev) |
| return E2BIG; |
| |
| if (se->se_rsn_ie != NULL) { |
| last_ev = current_ev; |
| #ifdef IWEVGENIE |
| memset(&iwe, 0, sizeof(iwe)); |
| if ((se->se_rsn_ie[1] + 2) > MAX_IE_LENGTH) |
| return E2BIG; |
| memcpy(buf, se->se_rsn_ie, se->se_rsn_ie[1] + 2); |
| iwe.cmd = IWEVGENIE; |
| iwe.u.data.length = se->se_rsn_ie[1] + 2; |
| #else |
| memset(&iwe, 0, sizeof(iwe)); |
| iwe.cmd = IWEVCUSTOM; |
| if (se->se_rsn_ie[0] == IEEE80211_ELEMID_RSN) |
| iwe.u.data.length = encode_ie(buf, sizeof(buf), |
| se->se_rsn_ie, se->se_rsn_ie[1] + 2, |
| rsn_leader, sizeof(rsn_leader) - 1); |
| #endif |
| if (iwe.u.data.length != 0) { |
| current_ev = iwe_stream_add_point(info, |
| current_ev, end_buf, &iwe, buf); |
| |
| /* We ran out of space in the buffer */ |
| if (last_ev == current_ev) |
| return E2BIG; |
| } |
| } |
| |
| if (se->se_wpa_ie != NULL) { |
| last_ev = current_ev; |
| #ifdef IWEVGENIE |
| memset(&iwe, 0, sizeof(iwe)); |
| if ((se->se_wpa_ie[1] + 2) > MAX_IE_LENGTH) |
| return E2BIG; |
| memcpy(buf, se->se_wpa_ie, se->se_wpa_ie[1] + 2); |
| iwe.cmd = IWEVGENIE; |
| iwe.u.data.length = se->se_wpa_ie[1] + 2; |
| #else |
| memset(&iwe, 0, sizeof(iwe)); |
| iwe.cmd = IWEVCUSTOM; |
| iwe.u.data.length = encode_ie(buf, sizeof(buf), |
| se->se_wpa_ie, se->se_wpa_ie[1] + 2, |
| wpa_leader, sizeof(wpa_leader) - 1); |
| #endif |
| if (iwe.u.data.length != 0) { |
| current_ev = iwe_stream_add_point(info, current_ev, |
| end_buf, &iwe, buf); |
| |
| /* We ran out of space in the buffer. */ |
| if (last_ev == current_ev) |
| return E2BIG; |
| } |
| } |
| |
| if (se->se_wme_ie != NULL) { |
| static const char wme_leader[] = IEEE80211_IE_LEADER_STR_WME; |
| |
| memset(&iwe, 0, sizeof(iwe)); |
| last_ev = current_ev; |
| iwe.cmd = IWEVCUSTOM; |
| iwe.u.data.length = encode_ie(buf, sizeof(buf), |
| se->se_wme_ie, se->se_wme_ie[1] + 2, |
| wme_leader, sizeof(wme_leader) - 1); |
| if (iwe.u.data.length != 0) { |
| current_ev = iwe_stream_add_point(info, current_ev, |
| end_buf, &iwe, buf); |
| |
| /* We ran out of space in the buffer. */ |
| if (last_ev == current_ev) |
| return E2BIG; |
| } |
| } |
| if (se->se_wsc_ie != NULL) { |
| last_ev = current_ev; |
| #ifdef IWEVGENIE |
| memset(&iwe, 0, sizeof(iwe)); |
| if ((se->se_wsc_ie[1] + 2) > sizeof(buf)) |
| return E2BIG; |
| memcpy(buf, se->se_wsc_ie, se->se_wsc_ie[1] + 2); |
| iwe.cmd = IWEVGENIE; |
| iwe.u.data.length = se->se_wsc_ie[1] + 2; |
| #endif |
| if (iwe.u.data.length != 0) { |
| current_ev = iwe_stream_add_point(info, current_ev, |
| end_buf, &iwe, buf); |
| |
| /* We ran out of space in the buffer. */ |
| if (last_ev == current_ev) |
| return E2BIG; |
| } |
| } |
| if (se->se_ath_ie != NULL) { |
| static const char ath_leader[] = IEEE80211_IE_LEADER_STR_ATH; |
| |
| memset(&iwe, 0, sizeof(iwe)); |
| last_ev = current_ev; |
| iwe.cmd = IWEVCUSTOM; |
| iwe.u.data.length = encode_ie(buf, sizeof(buf), |
| se->se_ath_ie, se->se_ath_ie[1] + 2, |
| ath_leader, sizeof(ath_leader) - 1); |
| if (iwe.u.data.length != 0) { |
| current_ev = iwe_stream_add_point(info, current_ev, |
| end_buf, &iwe, buf); |
| |
| /* We ran out of space in the buffer. */ |
| if (last_ev == current_ev) |
| return E2BIG; |
| } |
| } |
| |
| if (se->se_htcap_ie != NULL) { |
| static const char htcap_leader[] = IEEE80211_IE_LEADER_STR_HTCAP; |
| |
| memset(&iwe, 0, sizeof(iwe)); |
| last_ev = current_ev; |
| iwe.cmd = IWEVCUSTOM; |
| iwe.u.data.length = encode_ie(buf, sizeof(buf), |
| se->se_htcap_ie, se->se_htcap_ie[1] + 2, |
| htcap_leader, sizeof(htcap_leader) - 1); |
| if (iwe.u.data.length != 0) { |
| current_ev = iwe_stream_add_point(info, current_ev, |
| end_buf, &iwe, buf); |
| |
| /* We ran out of space in the buffer. */ |
| if (last_ev == current_ev) |
| return E2BIG; |
| } |
| } |
| |
| if (se->se_vhtcap_ie != NULL) { |
| static const char vhtcap_leader[] = IEEE80211_IE_LEADER_STR_VHTCAP; |
| |
| memset(&iwe, 0, sizeof(iwe)); |
| last_ev = current_ev; |
| iwe.cmd = IWEVCUSTOM; |
| iwe.u.data.length = encode_ie(buf, sizeof(buf), |
| se->se_vhtcap_ie, se->se_vhtcap_ie[1] + 2, |
| vhtcap_leader, sizeof(vhtcap_leader) - 1); |
| if (iwe.u.data.length != 0) { |
| current_ev = iwe_stream_add_point(info, current_ev, |
| end_buf, &iwe, buf); |
| |
| /* We ran out of space in the buffer. */ |
| if (last_ev == current_ev) |
| return E2BIG; |
| } |
| } |
| |
| req->current_ev = current_ev; |
| |
| return 0; |
| } |
| |
| static qfdr_remote_giwscan_hook_t qfdr_remote_giwscan_hook = NULL; |
| void ieee80211_register_qfdr_remote_giwscan_hook(qfdr_remote_giwscan_hook_t hook) |
| { |
| qfdr_remote_giwscan_hook = hook; |
| } |
| EXPORT_SYMBOL(ieee80211_register_qfdr_remote_giwscan_hook); |
| |
| static int |
| ieee80211_ioctl_giwscan(struct net_device *dev, struct iw_request_info *info, |
| struct iw_point *data, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct iwscanreq req; |
| int res = 0; |
| int is_remote_req = 0; |
| uint32_t *plen = NULL; |
| uint32_t buflen = data->length; |
| |
| if (info->cmd & QFDR_REMOTE_CMD) { |
| is_remote_req = 1; |
| info->cmd &= ~QFDR_REMOTE_CMD; |
| plen = (uint32_t *)extra; |
| extra += sizeof(uint32_t); |
| buflen = *plen; |
| } |
| |
| ieee80211_dump_scan_res(ic->ic_scan); |
| |
| req.vap = vap; |
| req.current_ev = extra; |
| if (buflen == 0) { |
| req.end_buf = extra + IW_SCAN_MAX_DATA; |
| } else { |
| req.end_buf = extra + buflen; |
| } |
| |
| /* |
| * NB: This is no longer needed, as long as the caller supports |
| * large scan results. |
| * |
| * Don't need do WPA/RSN sort any more since the original scan list |
| * has been sorted. |
| */ |
| req.info = info; |
| res = ieee80211_scan_iterate(ic, giwscan_cb, &req); |
| |
| if (is_remote_req) { |
| *plen = req.current_ev - extra; |
| return res; |
| } |
| |
| if (res == 0 && qfdr_remote_giwscan_hook != NULL) { |
| res = qfdr_remote_giwscan_hook(&req); |
| } |
| |
| data->length = req.current_ev - extra; |
| |
| if (res != 0) { |
| return -res; |
| } |
| |
| return res; |
| } |
| |
| struct qfdr_remote_aplist_rep *qfdr_giwscan_for_remote(struct qfdr_remote_aplist_req *remote_req) |
| { |
| struct net_device *dev; |
| struct qfdr_remote_aplist_rep *rep; |
| char *extra; |
| struct iw_request_info *info; |
| struct iw_point data; |
| |
| dev = dev_get_by_name(&init_net, remote_req->dev_name); |
| if (!dev) |
| return NULL; |
| |
| rep = kmalloc(remote_req->extra_len + sizeof(struct qfdr_remote_aplist_rep), GFP_KERNEL); |
| if (!rep) { |
| printk(KERN_ERR "%s: Failed to alloc buf.\n", __func__); |
| dev_put(dev); |
| return NULL; |
| } |
| |
| rep->length = remote_req->extra_len; |
| extra = (char *)&rep->length; |
| |
| info = &remote_req->info; |
| info->cmd = SIOCGIWSCAN | QFDR_REMOTE_CMD; |
| |
| memset(&data, 0, sizeof(data)); |
| |
| rep->res = ieee80211_ioctl_giwscan(dev, info, &data, extra); |
| rep->type = QFDR_GIWSCAN; |
| |
| dev_put(dev); |
| return rep; |
| } |
| EXPORT_SYMBOL(qfdr_giwscan_for_remote); |
| #endif /* SIOCGIWSCAN */ |
| |
| static int |
| cipher2cap(int cipher) |
| { |
| switch (cipher) { |
| case IEEE80211_CIPHER_WEP: return IEEE80211_C_WEP; |
| case IEEE80211_CIPHER_AES_OCB: return IEEE80211_C_AES; |
| case IEEE80211_CIPHER_AES_CCM: return IEEE80211_C_AES_CCM; |
| case IEEE80211_CIPHER_CKIP: return IEEE80211_C_CKIP; |
| case IEEE80211_CIPHER_TKIP: return IEEE80211_C_TKIP; |
| } |
| return 0; |
| } |
| |
| #define IEEE80211_MODE_TURBO_STATIC_A IEEE80211_MODE_MAX |
| |
| static int |
| ieee80211_convert_mode(const char *mode) |
| { |
| #define TOUPPER(c) ((((c) > 0x60) && ((c) < 0x7b)) ? ((c) - 0x20) : (c)) |
| static const struct { |
| char *name; |
| int mode; |
| } mappings[] = { |
| /* NB: need to order longest strings first for overlaps */ |
| { "11AST" , IEEE80211_MODE_TURBO_STATIC_A }, |
| { "AUTO" , IEEE80211_MODE_AUTO }, |
| { "11A" , IEEE80211_MODE_11A }, |
| { "11B" , IEEE80211_MODE_11B }, |
| { "11G" , IEEE80211_MODE_11G }, |
| { "11NG" , IEEE80211_MODE_11NG }, |
| { "11NG40" , IEEE80211_MODE_11NG_HT40PM}, |
| { "11NA" , IEEE80211_MODE_11NA }, |
| { "11NA40" , IEEE80211_MODE_11NA_HT40PM}, |
| { "11AC" , IEEE80211_MODE_11AC_VHT20PM}, |
| { "11AC40" , IEEE80211_MODE_11AC_VHT40PM}, |
| { "11AC80" , IEEE80211_MODE_11AC_VHT80PM}, |
| { "11AC160" , IEEE80211_MODE_11AC_VHT160PM}, |
| { "FH" , IEEE80211_MODE_FH }, |
| { "0" , IEEE80211_MODE_AUTO }, |
| { "1" , IEEE80211_MODE_11A }, |
| { "2" , IEEE80211_MODE_11B }, |
| { "3" , IEEE80211_MODE_11G }, |
| { "4" , IEEE80211_MODE_FH }, |
| { "5" , IEEE80211_MODE_TURBO_STATIC_A }, |
| { "11AC80EDGE+", IEEE80211_MODE_11AC_VHT80PM}, |
| { "11AC80CNTR+", IEEE80211_MODE_11AC_VHT80PM}, |
| { "11AC80CNTR-", IEEE80211_MODE_11AC_VHT80PM}, |
| { "11AC80EDGE-", IEEE80211_MODE_11AC_VHT80PM}, |
| { "11ACONLY", IEEE80211_MODE_11AC_VHT20PM}, |
| { "11ACONLY40", IEEE80211_MODE_11AC_VHT40PM}, |
| { "11ACONLY80", IEEE80211_MODE_11AC_VHT80PM}, |
| { "11NONLY", IEEE80211_MODE_11NA}, |
| { "11NONLY40", IEEE80211_MODE_11NA_HT40PM}, |
| { NULL } |
| }; |
| int i, j; |
| const char *cp; |
| |
| for (i = 0; mappings[i].name != NULL; i++) { |
| cp = mappings[i].name; |
| for (j = 0; j < strlen(mode) + 1; j++) { |
| /* convert user-specified string to upper case */ |
| if (TOUPPER(mode[j]) != cp[j]) |
| break; |
| if (cp[j] == '\0') |
| return mappings[i].mode; |
| } |
| } |
| return -1; |
| #undef TOUPPER |
| } |
| |
| static int |
| ieee80211_ioctl_postevent(struct net_device *dev, struct iw_request_info *info, |
| struct iw_point *wri, char *extra) |
| { |
| char s[256]; |
| static const char *tag = QEVT_COMMON_PREFIX; |
| memset(s, 0, sizeof(s)); |
| if (wri->length > sizeof(s)) /* silently truncate */ |
| wri->length = sizeof(s); |
| if (copy_from_user(s, wri->pointer, wri->length)) |
| return -EINVAL; |
| s[sizeof(s)-1] = '\0'; /* ensure null termination */ |
| |
| /* We demux - one message is "WPA-PORT-ENABLE", the rest should be pushed |
| * via wireless_send_event. |
| */ |
| if (!strncmp(s, "WPA-PORT-ENABLE", sizeof("WPA-PORT-ENABLE")-1)) { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211_node *ni = vap->iv_bss; |
| if (ni) { |
| _ieee80211_node_authorize(ni); |
| } |
| } else { |
| /* In this case, we assume the message from userspace has the correct format */ |
| ieee80211_eventf(dev, "%s%s", tag, s); |
| } |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_txeapol(struct net_device *dev, struct iw_request_info *info, |
| struct iw_point *wri, char *extra) |
| { |
| char *buf; |
| |
| buf = ieee80211_malloc(wri->length, M_NOWAIT); |
| if (buf == NULL) |
| return -EINVAL; |
| |
| if (copy_from_user(buf, wri->pointer, wri->length)) { |
| ieee80211_free(buf); |
| return -EINVAL; |
| } |
| |
| ieee80211_eap_output(dev, buf, wri->length); |
| |
| ieee80211_free(buf); |
| |
| return 0; |
| } |
| |
| /* |
| * Blacklist a station that is in the hostapd MAC filtering list. |
| */ |
| static void |
| ieee80211_blacklist_add(struct ieee80211_node *ni) |
| { |
| struct ieee80211vap *vap = ni->ni_vap; |
| |
| if (vap->iv_blacklist_timeout > 0) { |
| ni->ni_blacklist_timeout = jiffies + vap->iv_blacklist_timeout; |
| /* Corner case - can't use zero! */ |
| if (ni->ni_blacklist_timeout == 0) { |
| ni->ni_blacklist_timeout = 1; |
| } |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC, |
| "[%s] blacklisted\n", |
| ether_sprintf(ni->ni_macaddr)); |
| } |
| } |
| |
| static void |
| ieee80211_wnm_btm_send_bss_termination(struct ieee80211_node *ni, |
| uint8_t mode,int size, uint8_t *list) |
| { |
| struct ieee80211_ie_btm_bss_termdur term; |
| struct ieee80211com *ic = ni->ni_ic; |
| term.subelem_id = WNM_NEIGHBOR_BTM_TERMINATION_DURATION; |
| term.length = sizeof(term) - 2; |
| term.duration = WNM_BTM_BSS_TERMINATION_DURATION; |
| ic->ic_get_tsf(&term.bss_term_tsf); |
| |
| if (size > 0) |
| mode |= BTM_REQ_PREF_CAND_LIST_INCLUDED; |
| |
| if (ieee80211_send_wnm_bss_tm_solicited_req(ni, mode, 0, |
| WNM_BTM_DEFAULT_VAL_INTVAL, |
| (const uint8_t *)&term, NULL, list, |
| size, |
| 0)) |
| IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_ACTION, |
| "WNM: Failed to send BSS termination BTM request %pM\n", |
| ni->ni_macaddr); |
| |
| } |
| |
| static void |
| ieee80211_domlme(void *arg, struct ieee80211_node *ni) |
| { |
| struct ieee80211req_mlme *mlme = arg; |
| |
| if ((mlme->im_op != IEEE80211_MLME_DEBUG_CLEAR) && |
| (ni->ni_associd != 0)) { |
| /* This status is only used internally, for blacklisting */ |
| if (mlme->im_reason == IEEE80211_STATUS_DENIED) { |
| ieee80211_blacklist_add(ni); |
| mlme->im_reason = IEEE80211_REASON_UNSPECIFIED; |
| } |
| |
| IEEE80211_SEND_MGMT(ni, |
| mlme->im_op == IEEE80211_MLME_DEAUTH ? |
| IEEE80211_FC0_SUBTYPE_DEAUTH : |
| IEEE80211_FC0_SUBTYPE_DISASSOC, |
| mlme->im_reason); |
| |
| /* |
| * Ensure that the deauth/disassoc frame is sent |
| * before the node is deleted. |
| */ |
| if (mlme->im_reason == IEEE80211_REASON_MIC_FAILURE) |
| ieee80211_safe_wait_ms(150, !in_interrupt()); |
| } |
| if (!(IEEE80211_ADDR_EQ(ni->ni_macaddr, ni->ni_bssid))) { |
| if (ni->ni_wnm_capability & IEEE80211_NODE_WNM_BTM_CAPABLE) { |
| uint8_t mode = BTM_REQ_ABRIDGED | BTM_REQ_BSS_TERMINATION_INCLUDED; |
| int nsize = 0; |
| uint8_t *neigh_repos = NULL; |
| |
| nsize = ieee80211_wnm_btm_create_pref_candidate_list(ni, &neigh_repos); |
| if (nsize == 0) |
| mode &= ~BTM_REQ_PREF_CAND_LIST_INCLUDED; |
| ieee80211_wnm_btm_send_bss_termination(ni, mode, nsize, neigh_repos); |
| ieee80211_safe_wait_ms(150, !in_interrupt()); |
| kfree(neigh_repos); |
| } |
| ieee80211_node_leave(ni); |
| } |
| } |
| |
| /** |
| * Common routine to force reassociation due to fundamental changes in config. |
| */ |
| void |
| ieee80211_wireless_reassoc(struct ieee80211vap *vap, int debug, int rescan) |
| { |
| struct net_device *dev = vap->iv_dev; |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211req_mlme mlme; |
| |
| if (vap->iv_state == IEEE80211_S_INIT) |
| { |
| return; |
| } |
| |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, |
| "%s Forcing reassociation\n", __func__); |
| switch (vap->iv_opmode) { |
| case IEEE80211_M_STA: |
| if (rescan) |
| { |
| ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); |
| } |
| else |
| { |
| ieee80211_new_state(vap, IEEE80211_S_ASSOC, 0); |
| } |
| break; |
| case IEEE80211_M_HOSTAP: |
| if (vap->iv_state == IEEE80211_S_RUN) { |
| ic->ic_beacon_update(vap); |
| if (debug) |
| { |
| mlme.im_op = IEEE80211_MLME_DEBUG_CLEAR; |
| mlme.im_reason = IEEE80211_REASON_UNSPECIFIED; |
| } |
| else |
| { |
| mlme.im_op = IEEE80211_MLME_DISASSOC; |
| mlme.im_reason = IEEE80211_REASON_UNSPECIFIED; |
| } |
| ieee80211_iterate_dev_nodes(dev, &ic->ic_sta, ieee80211_domlme, &mlme, 1); |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| static void ieee80211_wireless_reassoc_all_vaps(struct ieee80211com *ic) |
| { |
| struct ieee80211vap *tmp_vap; |
| |
| TAILQ_FOREACH(tmp_vap, &ic->ic_vaps, iv_next) { |
| if (tmp_vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| ieee80211_wireless_reassoc(tmp_vap, 0, 0); |
| } |
| } |
| } |
| |
| static struct ieee80211_channel * |
| find_alt_primary_chan_11ac80(struct ieee80211com *ic, char *mode) |
| { |
| struct ieee80211_channel *c = NULL; |
| int chan_offset = 0; |
| int chan; |
| |
| if (!strncasecmp(mode ,"11ac80Edge+", strlen(mode))) { |
| if (ic->ic_curchan->ic_ext_flags & IEEE80211_CHAN_VHT80_LL) |
| chan_offset = 0; |
| if (ic->ic_curchan->ic_ext_flags & IEEE80211_CHAN_VHT80_LU) |
| chan_offset = -1; |
| if (ic->ic_curchan->ic_ext_flags & IEEE80211_CHAN_VHT80_UL) |
| chan_offset = -2; |
| if (ic->ic_curchan->ic_ext_flags & IEEE80211_CHAN_VHT80_UU) |
| chan_offset = -3; |
| } else if (!strncasecmp(mode ,"11ac80Cntr+", strlen(mode))) { |
| if (ic->ic_curchan->ic_ext_flags & IEEE80211_CHAN_VHT80_LL) |
| chan_offset = 1; |
| if (ic->ic_curchan->ic_ext_flags & IEEE80211_CHAN_VHT80_LU) |
| chan_offset = 0; |
| if (ic->ic_curchan->ic_ext_flags & IEEE80211_CHAN_VHT80_UL) |
| chan_offset = -1; |
| if (ic->ic_curchan->ic_ext_flags & IEEE80211_CHAN_VHT80_UU) |
| chan_offset = -2; |
| } else if (!strncasecmp(mode ,"11ac80Cntr-", strlen(mode))) { |
| if (ic->ic_curchan->ic_ext_flags & IEEE80211_CHAN_VHT80_LL) |
| chan_offset = 2; |
| if (ic->ic_curchan->ic_ext_flags & IEEE80211_CHAN_VHT80_LU) |
| chan_offset = 1; |
| if (ic->ic_curchan->ic_ext_flags & IEEE80211_CHAN_VHT80_UL) |
| chan_offset = 0; |
| if (ic->ic_curchan->ic_ext_flags & IEEE80211_CHAN_VHT80_UU) |
| chan_offset = -1; |
| } else if (!strncasecmp(mode ,"11ac80Edge-", strlen(mode))) { |
| if (ic->ic_curchan->ic_ext_flags & IEEE80211_CHAN_VHT80_LL) |
| chan_offset = 3; |
| if (ic->ic_curchan->ic_ext_flags & IEEE80211_CHAN_VHT80_LU) |
| chan_offset = 2; |
| if (ic->ic_curchan->ic_ext_flags & IEEE80211_CHAN_VHT80_UL) |
| chan_offset = 1; |
| if (ic->ic_curchan->ic_ext_flags & IEEE80211_CHAN_VHT80_UU) |
| chan_offset = 0; |
| } |
| |
| chan = ic->ic_curchan->ic_ieee + (4 * chan_offset); |
| if (chan >= IEEE80211_DEFAULT_5_GHZ_CHANNEL) { |
| c = findchannel_any(ic, chan, ic->ic_curmode); |
| } |
| return c; |
| } |
| |
| static void |
| update_sta_profile(char *s, struct ieee80211vap *vap) |
| { |
| if (strcasecmp(s, "11b") == 0) { |
| vap->iv_2_4ghz_prof.phy_mode = IEEE80211_MODE_11B; |
| } else if (strcasecmp(s, "11a") == 0) { |
| vap->iv_5ghz_prof.phy_mode = IEEE80211_MODE_11A; |
| } else if (strcasecmp(s, "11g") == 0) { |
| vap->iv_2_4ghz_prof.phy_mode = IEEE80211_MODE_11G; |
| } else if (strcasecmp(s, "11ng") == 0) { |
| vap->iv_2_4ghz_prof.phy_mode = IEEE80211_MODE_11NG; |
| } else if (strcasecmp(s, "11ng40") == 0) { |
| vap->iv_2_4ghz_prof.phy_mode = IEEE80211_MODE_11NG_HT40PM; |
| } else if (strcasecmp(s, "11na") == 0) { |
| vap->iv_5ghz_prof.phy_mode = IEEE80211_MODE_11NA; |
| } else if (strcasecmp(s, "11na40") == 0) { |
| vap->iv_5ghz_prof.phy_mode = IEEE80211_MODE_11NA_HT40PM; |
| } else if ((strcasecmp(s, "11ac") == 0 ) || (strcasecmp(s, "11acOnly") == 0)) { |
| vap->iv_5ghz_prof.phy_mode = IEEE80211_MODE_11AC_VHT20PM; |
| } else if ((strcasecmp(s, "11ac40") == 0) || (strcasecmp(s, "11acOnly40") == 0)) { |
| vap->iv_5ghz_prof.phy_mode = IEEE80211_MODE_11AC_VHT40PM; |
| } else if ((strcasecmp(s, "11ac80") == 0) || (strcasecmp(s, "11acOnly80") == 0) || |
| (strcasecmp(s, "11ac80Edge+") == 0) || (strcasecmp(s, "11ac80Edge-") == 0) || |
| (strcasecmp(s, "11ac80Cntr+") == 0) || (strcasecmp(s, "11ac80Cntr-") == 0)) { |
| vap->iv_5ghz_prof.phy_mode = IEEE80211_MODE_11AC_VHT80PM; |
| } else { |
| printk(KERN_INFO "In DBS mode - %s is not correct\n", s); |
| } |
| } |
| |
| static int |
| ieee80211_get_bw_from_phymode(int mode) |
| { |
| int bw; |
| |
| if (mode == IEEE80211_MODE_11AC_VHT160PM) |
| bw = BW_HT160; |
| else if (mode == IEEE80211_MODE_11AC_VHT80PM) |
| bw = BW_HT80; |
| else if ((mode == IEEE80211_MODE_11AC_VHT40PM) || |
| (mode == IEEE80211_MODE_11NG_HT40PM) || |
| (mode == IEEE80211_MODE_11NA_HT40PM)) |
| bw = BW_HT40; |
| else |
| bw = BW_HT20; |
| |
| return bw; |
| } |
| |
| static void |
| ieee80211_update_chanlist_from_bw(struct ieee80211com *ic, int bw) |
| { |
| struct ieee80211_channel *chan; |
| int idx_bw; |
| int i; |
| |
| /* Reset the active channel list as per the bw selected */ |
| ieee80211_update_active_chanlist(ic, bw); |
| |
| /* If region = none do not reconfigure the tx_power for the bw set */ |
| if (ic->ic_country_code == CTRY_DEFAULT) |
| return; |
| |
| /* Update ic_maxpower since bw is changed */ |
| switch(bw) { |
| case BW_HT80: |
| idx_bw = PWR_IDX_80M; |
| break; |
| case BW_HT40: |
| idx_bw = PWR_IDX_40M; |
| break; |
| case BW_HT20: |
| idx_bw = PWR_IDX_20M; |
| break; |
| default: |
| printk("unsupported bw: %u\n", bw); |
| return; |
| } |
| |
| for (i = 0; i < ic->ic_nchans; i++) { |
| chan = &ic->ic_channels[i]; |
| if (!isset(ic->ic_chan_active, chan->ic_ieee)) |
| continue; |
| |
| chan->ic_maxpower = chan->ic_maxpower_table[PWR_IDX_BF_OFF][PWR_IDX_1SS][idx_bw]; |
| chan->ic_maxpower_normal = chan->ic_maxpower; |
| } |
| } |
| |
| static void |
| ieee80211_update_bw_from_phymode(struct ieee80211vap *vap, int mode) |
| { |
| struct ieee80211com *ic = vap->iv_ic; |
| int bw = ieee80211_get_bw_from_phymode(mode); |
| |
| ic->ic_max_system_bw = bw; |
| ieee80211_update_bw_capa(vap, bw); |
| ieee80211_update_chanlist_from_bw(ic, bw); |
| } |
| |
| |
| static int |
| ieee80211_ioctl_setmode(struct net_device *dev, struct iw_request_info *info, |
| struct iw_point *wri, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ifreq ifr; |
| char s[12]; /* big enough for "11ac80Edge+ */ |
| int retv; |
| int mode; |
| int ifr_mode; |
| int aggr = 1; |
| |
| if (ic->ic_media.ifm_cur == NULL) |
| return -EINVAL; |
| if (wri->length > sizeof(s)) /* silently truncate */ |
| wri->length = sizeof(s); |
| if (copy_from_user(s, wri->pointer, wri->length)) |
| return -EINVAL; |
| |
| /* ensure null termination */ |
| s[sizeof(s)-1] = '\0'; |
| mode = ieee80211_convert_mode(s); |
| if (mode < 0) |
| return -EINVAL; |
| |
| if (ic->fixed_legacy_rate_mode) |
| return -EINVAL; |
| |
| /* update station profile */ |
| if ((ic->ic_rf_chipid == CHIPID_DUAL) && (ic->ic_opmode == IEEE80211_M_STA)) |
| update_sta_profile(s, vap); |
| |
| if (((strcasecmp(s, "11ac") == 0) || (strcasecmp(s, "11acOnly") == 0) || |
| (strcasecmp(s, "11acOnly40") == 0) ||(strcasecmp(s, "11acOnly80") == 0)) |
| && !ieee80211_swfeat_is_supported(SWFEAT_ID_VHT, 1)) |
| return -EOPNOTSUPP; |
| |
| if ((strcasecmp(s, "11acOnly") == 0) || (strcasecmp(s, "11acOnly40") == 0) || |
| (strcasecmp(s, "11acOnly80") == 0)) { |
| vap->iv_11ac_and_11n_flag = IEEE80211_11AC_ONLY; |
| } else if ((strcasecmp(s, "11nOnly") == 0) || (strcasecmp(s, "11nOnly40") == 0)) { |
| vap->iv_11ac_and_11n_flag = IEEE80211_11N_ONLY; |
| } else { |
| vap->iv_11ac_and_11n_flag = 0; |
| } |
| |
| /* In AP mode, redefining AUTO mode */ |
| if (mode == IEEE80211_MODE_AUTO && ic->ic_opmode == IEEE80211_M_HOSTAP) { |
| if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) |
| mode = IEEE80211_MODE_11NG; |
| else |
| mode = IEEE80211_MODE_11AC_VHT80PM; |
| } |
| |
| retv = ieee80211_check_mode_consistency(ic, mode, ic->ic_curchan); |
| if (retv == -1) { |
| return -EINVAL; |
| } else if (retv == 1) { |
| /* |
| * Reset current channel with default channel when phy mode |
| * is not consistent with current channel for dual bands chip |
| */ |
| if (ic->ic_rf_chipid != CHIPID_DUAL) { |
| printk(KERN_INFO "mode - %s is not consistent with channel %d\n", |
| s, ic->ic_curchan->ic_ieee); |
| return -EOPNOTSUPP; |
| } |
| |
| /* Send deauth frame before switching channel */ |
| ieee80211_wireless_reassoc(vap, 0, 1); |
| |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) |
| ic->ic_des_chan = findchannel_any(ic, |
| IEEE80211_DEFAULT_2_4_GHZ_CHANNEL, mode); |
| else |
| ic->ic_des_chan = findchannel_any(ic, |
| IEEE80211_DEFAULT_5_GHZ_CHANNEL, mode); |
| |
| ic->ic_curchan = ic->ic_des_chan; |
| ic->ic_csw_reason = IEEE80211_CSW_REASON_CONFIG; |
| ic->ic_set_channel(ic); |
| } else { |
| ic->ic_des_chan = IEEE80211_CHAN_ANYC; |
| } |
| } |
| |
| ifr_mode = mode; |
| memset(&ifr, 0, sizeof(ifr)); |
| |
| if(vap->iv_media.ifm_cur == NULL) |
| return -EINVAL; |
| |
| ifr.ifr_media = vap->iv_media.ifm_cur->ifm_media &~ IFM_MMASK; |
| if (mode == IEEE80211_MODE_TURBO_STATIC_A) |
| ifr_mode = IEEE80211_MODE_11A; |
| ifr.ifr_media |= IFM_MAKEMODE(ifr_mode); |
| /* We cannot call with the parent device, needs to be the VAP device */ |
| retv = ifmedia_ioctl(vap->iv_dev, &ifr, &vap->iv_media, SIOCSIFMEDIA); |
| if (retv == -ENETRESET) { |
| /* Updating bandwidth base on mode */ |
| ieee80211_update_bw_from_phymode(vap, mode); |
| |
| /* Updating all mode related flags */ |
| ic->ic_des_mode = ic->ic_phymode = mode; |
| ieee80211_setmode(ic, ic->ic_des_mode); |
| |
| /* Switch channel according to Edge+/- Cntr +/- */ |
| if (ic->ic_phymode == IEEE80211_MODE_11AC_VHT80PM) { |
| ic->ic_des_chan = find_alt_primary_chan_11ac80(ic, s); |
| if (!is_ieee80211_chan_valid(ic->ic_des_chan)) { |
| ic->ic_des_chan = ic->ic_curchan; |
| return -EINVAL; |
| } |
| |
| ic->ic_curchan = ic->ic_des_chan; |
| ic->ic_csw_reason = IEEE80211_CSW_REASON_CONFIG; |
| ic->ic_set_channel(ic); |
| } |
| |
| if ((vap->iv_opmode == IEEE80211_M_HOSTAP) && IS_UP_AUTO(vap)) |
| ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); |
| |
| if (ic->ic_curmode < IEEE80211_MODE_11NA) |
| aggr = 0; |
| |
| ieee80211_param_to_qdrv(vap, IEEE80211_PARAM_PHY_MODE, ic->ic_phymode, NULL, 0); |
| ieee80211_param_to_qdrv(vap, IEEE80211_PARAM_TX_AMSDU, aggr, NULL, 0); |
| ieee80211_param_to_qdrv(vap, IEEE80211_PARAM_AGGREGATION, aggr, NULL, 0); |
| |
| ieee80211_init_chanset_ranking_params(ic); |
| ieee80211_start_obss_scan_timer(vap); |
| |
| ieee80211_wireless_reassoc(vap, 0, 1); |
| retv = 0; |
| } |
| return -retv; |
| } |
| |
| void ieee80211_param_to_qdrv(struct ieee80211vap *vap, |
| int param, int value, unsigned char *data, int len) |
| { |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_node *ni = ieee80211_get_vap_node(vap); |
| |
| if (ni == NULL) { |
| printk("no bss node: param %d\n", param); |
| return; |
| } |
| |
| KASSERT(ni, ("no bss node")); |
| |
| if (ic->ic_setparam != NULL) { |
| (*ic->ic_setparam)(ni, param, value, data, len); |
| } |
| |
| ieee80211_free_node(ni); |
| |
| return; |
| } |
| EXPORT_SYMBOL(ieee80211_param_to_qdrv); |
| |
| void ieee80211_param_from_qdrv(struct ieee80211vap *vap, |
| int param, int *value, unsigned char *data, int *len) |
| { |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_node *ni = ieee80211_get_vap_node(vap); |
| |
| KASSERT(ni, ("no bss node")); |
| |
| if (ic->ic_getparam != NULL) { |
| (*ic->ic_getparam)(ni, param, value, data, len); |
| } |
| |
| ieee80211_free_node(ni); |
| |
| return; |
| } |
| |
| /* Function attached to the iwpriv wifi0 forcesmps call. */ |
| static int |
| ieee80211_forcesmps(struct ieee80211vap *vap, int value) |
| { |
| /* Ensure we only apply valid values to the local vap state */ |
| short smps_mode = (short)value; |
| if (value == -1) |
| { |
| struct ieee80211_node *ni; |
| ni = ieee80211_find_node(&vap->iv_ic->ic_sta, vap->iv_myaddr); |
| if (ni == NULL) { |
| return 0; |
| } |
| printk("Clearing force SMPS mode in driver\n"); |
| smps_mode = ni->ni_htcap.pwrsave; |
| ieee80211_free_node(ni); |
| } |
| if (smps_mode != IEEE80211_HTCAP_C_MIMOPWRSAVE_NA) |
| { |
| /* If we're a STA, send out an ACTION frame to change our SMPS mode */ |
| if (vap->iv_opmode == IEEE80211_M_STA) |
| { |
| struct ieee80211_action_data act; |
| int action_byte = -1; |
| memset(&act, 0, sizeof(act)); |
| act.cat = IEEE80211_ACTION_CAT_HT; |
| act.action = IEEE80211_ACTION_HT_MIMOPWRSAVE; |
| switch (smps_mode) |
| { |
| /* See 802.11n d11.0 section 7.3.1.22 for the formatting of the HT ACTION SMPS byte. */ |
| case IEEE80211_HTCAP_C_MIMOPWRSAVE_STATIC: |
| action_byte = 0x1; |
| break; |
| case IEEE80211_HTCAP_C_MIMOPWRSAVE_DYNAMIC: |
| action_byte = 0x3; |
| break; |
| case IEEE80211_HTCAP_C_MIMOPWRSAVE_NONE: |
| action_byte = 0x0; |
| break; |
| default: |
| printf("Not sending ACTION frame\n"); |
| break; |
| } |
| if (action_byte >= 0) |
| { |
| act.params = (int *)action_byte; //Contain the single byte for the SMPS action frame in the param field |
| printk("STA Sending HT Action frame to change PS mode (%02X->%02X)\n", vap->iv_smps_force & 0xFF, smps_mode); |
| if (value == -1) |
| { |
| vap->iv_smps_force &= ~0x8000; |
| } |
| else |
| { |
| vap->iv_smps_force = 0x8000 | smps_mode; |
| } |
| IEEE80211_SEND_MGMT(vap->iv_bss, IEEE80211_FC0_SUBTYPE_ACTION, (int)&act); |
| } |
| } |
| } |
| else |
| { |
| printk("Ignoring invalid SMPS mode (%04X) at WLAN driver\n", smps_mode); |
| } |
| return 1; |
| } |
| |
| /* |
| * Check if a node is blacklisted. |
| * Returns 1 if true, else 0. |
| */ |
| int |
| ieee80211_blacklist_check(struct ieee80211_node *ni) |
| { |
| struct ieee80211vap *vap = ni->ni_vap; |
| |
| if (ni->ni_blacklist_timeout == 0) { |
| return 0; |
| } |
| |
| if (time_after(jiffies, ni->ni_blacklist_timeout)) { |
| ni->ni_blacklist_timeout = 0; |
| /* Remove blacklist entry from node table */ |
| ieee80211_remove_node_blacklist_timeout(ni); |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC, |
| "[%s] removed from blacklist\n", |
| ether_sprintf(ni->ni_macaddr)); |
| return 0; |
| } |
| |
| return 1; |
| } |
| EXPORT_SYMBOL(ieee80211_blacklist_check); |
| |
| /* Routine to clear existing BA agreements if necessary - used when the global BA control |
| * changes |
| */ |
| static void |
| ieee80211_wireless_ba_change(struct ieee80211vap *vap) |
| { |
| ieee80211_wireless_reassoc(vap, 0, 1); |
| /* FIXME: re-enable this code once DELBA is working properly. */ |
| #if 0 |
| int i; |
| for (i = 0; i < 8; i++) |
| { |
| if ((vap->iv_ba_old_control & (1 << i)) && (!(vap->iv_ba_control & (1 << i)))) |
| { |
| printk("Deleting block acks on TID %d\n", i); |
| switch (vap->iv_opmode) |
| { |
| case IEEE80211_M_HOSTAP: |
| struct ieee80211com *ic = vap->iv_ic; |
| struct net_device *dev = vap->iv_dev; |
| int tid_del = i; |
| /* Iterate through all STAs - if BA is established, delete it. */ |
| ieee80211_iterate_dev_nodes(dev, &ic->ic_sta, |
| ieee80211_wireless_ba_del, &tid_del, 1); |
| break; |
| |
| case IEEE80211_M_STA: |
| ieee80211_wireless_ba_del((void *)&i, vap->iv_bss); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| } |
| #endif |
| } |
| |
| void ieee80211_obss_scan_timer(unsigned long arg) |
| { |
| struct ieee80211vap *vap = (struct ieee80211vap *)arg; |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_node *ni = vap->iv_bss; |
| int scanflags; |
| |
| if (!IEEE80211_IS_11NG_40(ic) || !ic->ic_obss_scan_enable) |
| return; |
| |
| if (ic->ic_opmode == IEEE80211_M_HOSTAP) { |
| if (!ic->ic_obss_scan_count) { |
| scanflags = IEEE80211_SCAN_NOPICK | |
| IEEE80211_SCAN_ONCE | IEEE80211_SCAN_ACTIVE; |
| mod_timer(&ic->ic_obss_timer, |
| jiffies + IEEE80211_OBSS_AP_SCAN_INT * HZ); |
| (void) ieee80211_start_scan(vap, scanflags, |
| IEEE80211_SCAN_FOREVER, 0, NULL); |
| } |
| } else if (ni && IEEE80211_AID(ni->ni_associd) && |
| (ni->ni_obss_ie.param_id == IEEE80211_ELEMID_OBSS_SCAN)) { |
| scanflags = IEEE80211_SCAN_NOPICK | IEEE80211_SCAN_ACTIVE |
| | IEEE80211_SCAN_ONCE | IEEE80211_SCAN_OBSS; |
| mod_timer(&ic->ic_obss_timer, |
| jiffies + ni->ni_obss_ie.obss_trigger_interval * HZ); |
| (void) ieee80211_start_scan(vap, scanflags, |
| IEEE80211_SCAN_FOREVER, 0, NULL); |
| } |
| } |
| |
| void ieee80211_start_obss_scan_timer(struct ieee80211vap *vap) |
| { |
| struct ieee80211com *ic = vap->iv_ic; |
| |
| if (!IEEE80211_IS_11NG_40(ic) || |
| !ic->ic_obss_scan_enable || |
| !ic->ic_20_40_coex_enable) |
| return; |
| |
| ic->ic_obss_scan_count = 0; |
| ic->ic_obss_timer.function = ieee80211_obss_scan_timer; |
| ic->ic_obss_timer.data = (unsigned long)vap; |
| mod_timer(&ic->ic_obss_timer, jiffies + IEEE80211_OBSS_AP_SCAN_INT * HZ); |
| } |
| |
| uint8_t recalc_opmode(struct ieee80211_node *ni, uint8_t opmode) |
| { |
| uint8_t max_bw = get_max_supported_chwidth(ni); |
| uint8_t bw = MS(opmode, IEEE80211_VHT_OPMODE_CHWIDTH); |
| |
| if (bw > max_bw) { |
| opmode &= ~IEEE80211_VHT_OPMODE_CHWIDTH; |
| opmode |= SM(max_bw, IEEE80211_VHT_OPMODE_CHWIDTH); |
| } |
| |
| return opmode; |
| } |
| |
| void update_node_opmode(struct ieee80211com *ic, struct ieee80211_node *ni) |
| { |
| int cur_opmode; |
| uint8_t opmode; |
| |
| ieee80211_param_from_qdrv(ni->ni_vap, IEEE80211_PARAM_NODE_OPMODE, &cur_opmode, NULL, NULL); |
| ni->ni_chan = ni->ni_ic->ic_curchan; |
| opmode = recalc_opmode(ni, ni->ni_vhtop_notif_mode); |
| if (cur_opmode != opmode) { |
| ieee80211_param_to_qdrv(ni->ni_vap, IEEE80211_PARAM_NODE_OPMODE, |
| opmode, ni->ni_macaddr, IEEE80211_ADDR_LEN); |
| } |
| } |
| |
| static void update_node_opmodes(struct ieee80211com *ic) |
| { |
| struct ieee80211_node_table *nt = &ic->ic_sta; |
| struct ieee80211_node *ni; |
| |
| IEEE80211_NODE_LOCK(nt); |
| |
| TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { |
| if(ni->ni_node_type == IEEE80211_NODE_TYPE_STA) |
| update_node_opmode(ic, ni); |
| } |
| |
| IEEE80211_NODE_UNLOCK(nt); |
| } |
| |
| int get_max_supported_chwidth(struct ieee80211_node *ni) |
| { |
| struct ieee80211_channel *cur_chan = ni->ni_ic->ic_curchan; |
| u_int8_t ch = cur_chan->ic_ieee; |
| |
| if (isclr(ni->ni_supp_chans, ch)) { |
| return -1; |
| } |
| /* Check for 80 MHz */ |
| if (cur_chan->ic_flags & IEEE80211_CHAN_VHT80) { |
| if (cur_chan->ic_ext_flags & IEEE80211_CHAN_VHT80_LL) { |
| if ( isset(ni->ni_supp_chans, ch + 4) |
| && isset(ni->ni_supp_chans, ch + 8) |
| && isset(ni->ni_supp_chans, ch + 12) |
| ) { |
| return IEEE80211_CWM_WIDTH80; |
| } |
| } |
| else if (cur_chan->ic_ext_flags & IEEE80211_CHAN_VHT80_LU) { |
| if ( isset(ni->ni_supp_chans, ch - 4) |
| && isset(ni->ni_supp_chans, ch + 4) |
| && isset(ni->ni_supp_chans, ch + 8) |
| ) { |
| return IEEE80211_CWM_WIDTH80; |
| } |
| } |
| else if (cur_chan->ic_ext_flags & IEEE80211_CHAN_VHT80_UL) { |
| if ( isset(ni->ni_supp_chans, ch - 8) |
| && isset(ni->ni_supp_chans, ch - 4) |
| && isset(ni->ni_supp_chans, ch + 4) |
| ) { |
| return IEEE80211_CWM_WIDTH80; |
| } |
| } |
| else if (cur_chan->ic_ext_flags & IEEE80211_CHAN_VHT80_UU) { |
| if ( isset(ni->ni_supp_chans, ch - 12) |
| && isset(ni->ni_supp_chans, ch - 8) |
| && isset(ni->ni_supp_chans, ch - 4) |
| ) { |
| return IEEE80211_CWM_WIDTH80; |
| } |
| } |
| } |
| |
| /* Check for 40 MHz */ |
| if (cur_chan->ic_flags & IEEE80211_CHAN_VHT40) { |
| if (cur_chan->ic_flags & IEEE80211_CHAN_VHT40U) { |
| if (isset(ni->ni_supp_chans, ch + 4)) { |
| return IEEE80211_CWM_WIDTH40; |
| } |
| } |
| else if (cur_chan->ic_flags & IEEE80211_CHAN_VHT40D) { |
| if (isset(ni->ni_supp_chans, ch - 4)) { |
| return IEEE80211_CWM_WIDTH40; |
| } |
| } |
| } |
| return IEEE80211_CWM_WIDTH20; |
| } |
| |
| void ieee80211_finish_csa(unsigned long arg) |
| { |
| struct ieee80211com *ic = (struct ieee80211com *)arg; |
| struct ieee80211vap *vap; |
| |
| /* clear DFS CAC state on previous channel */ |
| if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && |
| ic->ic_bsschan->ic_freq != ic->ic_csa_chan->ic_freq && |
| IEEE80211_IS_CHAN_CACDONE(ic->ic_bsschan)) { |
| /* |
| * IEEE80211_CHAN_DFS_CAC_DONE indicates whether or not to do CAC afresh. |
| * US : IEEE80211_CHAN_DFS_CAC_DONE shall be cleared whenver we move to |
| * a different channel |
| * ETSI : IEEE80211_CHAN_DFS_CAC_DONE shall be retained; Only event which |
| * would mark the channel as unusable is the radar indication |
| */ |
| if ((ic->ic_dfs_is_eu_region() == false) && |
| (ic->ic_chan_compare_equality(ic, ic->ic_bsschan, ic->ic_csa_chan) == false)) { |
| ic->ic_bsschan->ic_flags &= ~IEEE80211_CHAN_DFS_CAC_DONE; |
| if (ic->ic_mark_channel_dfs_cac_status) { |
| ic->ic_mark_channel_dfs_cac_status(ic, ic->ic_bsschan, IEEE80211_CHAN_DFS_CAC_DONE, false); |
| ic->ic_mark_channel_dfs_cac_status(ic, ic->ic_bsschan, IEEE80211_CHAN_DFS_CAC_IN_PROGRESS, false); |
| } |
| /* Mark the channel as not_available and ready for cac */ |
| if (ic->ic_mark_channel_availability_status) { |
| ic->ic_mark_channel_availability_status(ic, ic->ic_bsschan, |
| IEEE80211_CHANNEL_STATUS_NOT_AVAILABLE_CAC_REQUIRED); |
| } |
| printk(KERN_DEBUG"ieee80211_finish_csa:" |
| "Clearing CAC_DONE Status for chan %d\n", |
| ic->ic_bsschan->ic_ieee); |
| } |
| } |
| |
| ic->ic_prevchan = ic->ic_curchan; |
| ic->ic_curchan = ic->ic_csa_chan; |
| ic->ic_bsschan = ic->ic_csa_chan; |
| ic->ic_des_chan = ic->ic_csa_chan; |
| ic->ic_csa_count = 0; |
| update_node_opmodes(ic); |
| |
| /* Remove the CSA IE from beacons and cause other field in beacon updated */ |
| ic->ic_flags &= ~IEEE80211_F_CHANSWITCH; |
| ic->ic_set_channel(ic); |
| TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { |
| if (vap->iv_opmode != IEEE80211_M_HOSTAP) |
| continue; |
| |
| if ((vap->iv_state != IEEE80211_S_RUN) && (vap->iv_state != IEEE80211_S_SCAN)) |
| continue; |
| ic->ic_beacon_update(vap); |
| } |
| |
| /* record channel change event */ |
| ic->ic_chan_switch_record(ic, ic->ic_csa_chan, ic->ic_csa_reason); |
| ic->ic_chan_switch_reason_record(ic, ic->ic_csa_reason); |
| return; |
| } |
| |
| void ieee80211_csa_finish(struct work_struct *work) |
| { |
| struct ieee80211com *ic = container_of(work, struct ieee80211com, csa_work); |
| struct ieee80211vap *vap; |
| unsigned long ret = 0; |
| |
| while (completion_done(&ic->csa_completion)) { |
| try_wait_for_completion(&ic->csa_completion); |
| DBGPRINTF_W("Warning: CSA completion was done\n"); |
| } |
| |
| TAILQ_FOREACH (vap, &ic->ic_vaps, iv_next) { |
| if (vap->iv_opmode != IEEE80211_M_HOSTAP) |
| continue; |
| |
| if (vap->iv_state != IEEE80211_S_RUN) |
| continue; |
| |
| /* Timeout indicates MuC has not responsed CSA request */ |
| ret = wait_for_completion_interruptible_timeout(&ic->csa_completion, |
| msecs_to_jiffies((ic->ic_csa_count + 1) * ic->ic_lintval)); |
| if (ret == 0) { |
| DBGPRINTF_W("Warning: timeout occurs when LHOST waits for MuC CSA completion!\n"); |
| break; |
| } |
| } |
| |
| if (ic->finish_csa == NULL) { |
| DBGPRINTF_E("Error: finish_csa callback is not setup\n"); |
| return; |
| } |
| |
| IEEE80211_DPRINTF(TAILQ_FIRST(&ic->ic_vaps), IEEE80211_MSG_DOTH, |
| "%s: channel switch finished, owner=%#x\n", __func__, ic->ic_csa_reason); |
| |
| ic->finish_csa((unsigned long)ic); |
| } |
| EXPORT_SYMBOL(ieee80211_csa_finish); |
| /* |
| * Start a CSA process: CSA beacon/action will be sent to STA to notify the CS. |
| * @finish_csa: At the CS time, finish_csa() will be called to do the actual CS. If not provided, |
| * ieee80211_finish_csa() will be called as default action. |
| * @flag: specify whether to use CSA beacon or CSA action or both. |
| */ |
| int ieee80211_enter_csa(struct ieee80211com *ic, struct ieee80211_channel *chan, |
| void (*finish_csa)(unsigned long arg), uint32_t reason, |
| uint8_t csa_count, uint8_t csa_mode, uint32_t flag) |
| { |
| struct ieee80211vap *vap; |
| uint32_t csa_flag; |
| |
| if (ic->ic_flags & IEEE80211_F_CHANSWITCH) { |
| IEEE80211_DPRINTF(TAILQ_FIRST(&ic->ic_vaps), IEEE80211_MSG_DOTH, |
| "%s: CSA already in progress, owner=%d, pre-" |
| "owner=%d\n", __func__, reason, ic->ic_csa_reason); |
| return -1; |
| } |
| |
| ic->ic_csa_chan = chan; |
| csa_flag = ic->ic_csa_flag ? ic->ic_csa_flag : flag; |
| |
| /* now flag the beacon update to include the channel switch IE */ |
| ic->ic_flags |= IEEE80211_F_CHANSWITCH; |
| ic->ic_csa_count = csa_count; |
| ic->ic_csa_mode = csa_mode; |
| ic->ic_csa_reason = reason; |
| ic->ic_csw_reason = reason; |
| |
| TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { |
| if (vap->iv_opmode != IEEE80211_M_HOSTAP) |
| continue; |
| |
| if (vap->iv_state != IEEE80211_S_RUN) |
| continue; |
| |
| /* send broadcast csa action */ |
| if (csa_flag & IEEE80211_CSA_F_ACTION) |
| ic->ic_send_csa_frame(vap, ic->ic_csa_mode, |
| ic->ic_csa_chan->ic_ieee, ic->ic_csa_count, 0); |
| /* Update beacon to include CSA IE */ |
| if (csa_flag & IEEE80211_CSA_F_BEACON) |
| ic->ic_beacon_update(vap); |
| } |
| ic->finish_csa = finish_csa ? finish_csa : ieee80211_finish_csa; |
| queue_work(ic->csa_work_queue, &ic->csa_work); |
| /* |
| * Store original attenuation to handle the following case: |
| * We switched to low power channel when attenuation is small. But then in low |
| * power channel attenuation increases but interference is low. We need to detect |
| * such rate ratio drop to trigger channel ranking. |
| */ |
| if (ic->ic_opmode == IEEE80211_M_HOSTAP && |
| ic->ic_curchan != IEEE80211_CHAN_ANYC && |
| ic->ic_scan && |
| ic->ic_scan->ss_scs_priv) { |
| struct ap_state *as = ic->ic_scan->ss_scs_priv; |
| |
| if (chan->ic_maxpower < (ic->ic_curchan->ic_maxpower - SCS_CHAN_POWER_DIFF_SAFE)) { |
| as->as_sta_atten_expect = as->as_sta_atten_max; |
| } else if (chan->ic_maxpower > (ic->ic_curchan->ic_maxpower + SCS_CHAN_POWER_DIFF_SAFE)) { |
| as->as_sta_atten_expect = SCS_ATTEN_UNINITED; |
| } |
| SCSDBG(SCSLOG_NOTICE, "atten expect set to %d\n", as->as_sta_atten_expect); |
| } |
| |
| /* for dfs reentry demon */ |
| vap = TAILQ_FIRST(&ic->ic_vaps); |
| if (ieee80211_is_repeater(ic) && !ieee80211_is_repeater_associated(ic)) |
| vap = TAILQ_NEXT(vap, iv_next); |
| |
| ic->ic_dfs_chan_switch_notify(vap->iv_dev, ic->ic_csa_chan); |
| |
| return 0; |
| } |
| EXPORT_SYMBOL(ieee80211_enter_csa); |
| |
| int ieee80211_get_cap_bw(struct ieee80211com *ic) |
| { |
| int bw = BW_INVALID; |
| struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); |
| |
| ieee80211_param_from_qdrv(vap, IEEE80211_PARAM_BW_SEL_MUC, &bw, NULL, 0); |
| |
| return bw; |
| } |
| EXPORT_SYMBOL(ieee80211_get_cap_bw); |
| |
| int ieee80211_get_bw(struct ieee80211com *ic) |
| { |
| int bw = ieee80211_get_cap_bw(ic); |
| |
| if ((ic->ic_opmode == IEEE80211_M_STA) && (bw != BW_INVALID) && ic->ic_bss_bw) { |
| bw = MIN(bw, ic->ic_bss_bw); |
| } |
| return bw; |
| } |
| EXPORT_SYMBOL(ieee80211_get_bw); |
| |
| void ieee80211_update_bw_capa(struct ieee80211vap *vap, int bw) |
| { |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_node *bss = vap->iv_bss; |
| |
| if (bw == BW_HT20) { |
| ic->ic_htcap.cap &= ~(IEEE80211_HTCAP_C_CHWIDTH40 | |
| IEEE80211_HTCAP_C_SHORTGI40); |
| if (vap->iv_ht_flags & IEEE80211_HTF_SHORTGI_ENABLED) |
| ic->ic_htcap.cap |= IEEE80211_HTCAP_C_SHORTGI20; |
| ic->ic_htinfo.byte1 &= ~IEEE80211_HTINFO_B1_REC_TXCHWIDTH_40; |
| ic->ic_vhtop.chanwidth = IEEE80211_VHTOP_CHAN_WIDTH_20_40MHZ; |
| } else if (bw == BW_HT40) { |
| ic->ic_htcap.cap |= IEEE80211_HTCAP_C_CHWIDTH40; |
| if (vap->iv_ht_flags & IEEE80211_HTF_SHORTGI_ENABLED) |
| ic->ic_htcap.cap |= (IEEE80211_HTCAP_C_SHORTGI40 | |
| IEEE80211_HTCAP_C_SHORTGI20); |
| ic->ic_htinfo.byte1 |= IEEE80211_HTINFO_B1_REC_TXCHWIDTH_40; |
| ic->ic_vhtop.chanwidth = IEEE80211_VHTOP_CHAN_WIDTH_20_40MHZ; |
| } else if (bw == BW_HT80) { |
| ic->ic_htcap.cap |= IEEE80211_HTCAP_C_CHWIDTH40; |
| if (vap->iv_ht_flags & IEEE80211_HTF_SHORTGI_ENABLED) |
| ic->ic_htcap.cap |= (IEEE80211_HTCAP_C_SHORTGI40 | |
| IEEE80211_HTCAP_C_SHORTGI20); |
| ic->ic_htinfo.byte1 |= IEEE80211_HTINFO_B1_REC_TXCHWIDTH_40; |
| if (vap->iv_vht_flags & IEEE80211_VHTCAP_C_SHORT_GI_80) |
| ic->ic_vhtcap.cap_flags |= IEEE80211_VHTCAP_C_SHORT_GI_80; |
| ic->ic_vhtop.chanwidth = IEEE80211_VHTOP_CHAN_WIDTH_80MHZ; |
| } else { |
| printk(KERN_INFO "%s: error - invalid bw %u\n", __func__, bw); |
| return; |
| } |
| |
| if ((vap->iv_opmode == IEEE80211_M_HOSTAP) && bss) { |
| memcpy(&bss->ni_htcap, &ic->ic_htcap, sizeof(bss->ni_htcap)); |
| memcpy(&bss->ni_htinfo, &ic->ic_htinfo, sizeof(bss->ni_htinfo)); |
| memcpy(&bss->ni_vhtcap, &ic->ic_vhtcap, sizeof(bss->ni_vhtcap)); |
| memcpy(&bss->ni_vhtop, &ic->ic_vhtop, sizeof(bss->ni_vhtop)); |
| } |
| } |
| EXPORT_SYMBOL(ieee80211_update_bw_capa); |
| |
| void ieee80211_change_bw(struct ieee80211vap *vap, int bw, int delay_chan_switch) |
| { |
| struct ieee80211com *ic = vap->iv_ic; |
| |
| ieee80211_param_to_qdrv(vap, IEEE80211_PARAM_BW_SEL_MUC, bw, NULL, 0); |
| |
| ieee80211_update_chanlist_from_bw(ic, bw); |
| |
| /* chanage channel to apply the new bandwidth and power configuration */ |
| if (!delay_chan_switch) |
| ic->ic_set_channel(ic); |
| } |
| |
| int ieee80211_get_mu_grp(struct ieee80211com *ic, |
| struct qtn_mu_grp_args *mu_grp_tbl) |
| { |
| struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); |
| int len = sizeof(*mu_grp_tbl)*IEEE80211_MU_GRP_NUM_MAX; |
| |
| ieee80211_param_from_qdrv(vap, IEEE80211_PARAM_GET_MU_GRP, NULL, (void*)mu_grp_tbl, &len); |
| |
| return len; |
| } |
| EXPORT_SYMBOL(ieee80211_get_mu_grp); |
| |
| int ieee80211_find_sec_chan(struct ieee80211_channel *chan) |
| { |
| int chan_sec = 0; |
| |
| if (chan->ic_flags & IEEE80211_CHAN_HT40D) { |
| chan_sec = chan->ic_ieee - IEEE80211_CHAN_SEC_SHIFT; |
| } else if (chan->ic_flags & IEEE80211_CHAN_HT40U) { |
| chan_sec = chan->ic_ieee + IEEE80211_CHAN_SEC_SHIFT; |
| } |
| |
| return chan_sec; |
| } |
| EXPORT_SYMBOL(ieee80211_find_sec_chan); |
| |
| int ieee80211_find_sec40u_chan(struct ieee80211_channel *chan) |
| { |
| int chan_sec40u = 0; |
| |
| if (chan->ic_ext_flags & IEEE80211_CHAN_VHT80_LL) { |
| chan_sec40u = chan->ic_ieee + 3 * IEEE80211_CHAN_SEC_SHIFT; |
| } else if (chan->ic_ext_flags & IEEE80211_CHAN_VHT80_LU) { |
| chan_sec40u = chan->ic_ieee + 2 * IEEE80211_CHAN_SEC_SHIFT; |
| } else if (chan->ic_ext_flags & IEEE80211_CHAN_VHT80_UL) { |
| chan_sec40u = chan->ic_ieee - IEEE80211_CHAN_SEC_SHIFT; |
| } else if (chan->ic_ext_flags & IEEE80211_CHAN_VHT80_UU) { |
| chan_sec40u = chan->ic_ieee - 2 * IEEE80211_CHAN_SEC_SHIFT; |
| } |
| |
| return chan_sec40u; |
| } |
| EXPORT_SYMBOL(ieee80211_find_sec40u_chan); |
| |
| int ieee80211_find_sec40l_chan(struct ieee80211_channel *chan) |
| { |
| int chan_sec40l = 0; |
| |
| if (chan->ic_ext_flags & IEEE80211_CHAN_VHT80_LL) { |
| chan_sec40l = chan->ic_ieee + 2 * IEEE80211_CHAN_SEC_SHIFT; |
| } else if (chan->ic_ext_flags & IEEE80211_CHAN_VHT80_LU) { |
| chan_sec40l = chan->ic_ieee + IEEE80211_CHAN_SEC_SHIFT; |
| } else if (chan->ic_ext_flags & IEEE80211_CHAN_VHT80_UL) { |
| chan_sec40l = chan->ic_ieee - 2 * IEEE80211_CHAN_SEC_SHIFT; |
| } else if (chan->ic_ext_flags & IEEE80211_CHAN_VHT80_UU) { |
| chan_sec40l = chan->ic_ieee - 3 * IEEE80211_CHAN_SEC_SHIFT; |
| } |
| |
| return chan_sec40l; |
| } |
| EXPORT_SYMBOL(ieee80211_find_sec40l_chan); |
| |
| int ieee80211_find_sec_chan_by_operating_class(struct ieee80211com *ic, int chan, uint32_t preference) |
| { |
| uint8_t *chan_list; |
| int chan_sec = 0; |
| |
| chan_list = kzalloc(howmany(IEEE80211_CHAN_MAX, NBBY), GFP_ATOMIC); |
| if (chan_list == NULL) { |
| printk(KERN_ERR "%s: buffer alloc failed\n", __FUNCTION__); |
| return 0; |
| } |
| |
| ieee80211_get_prichan_list_by_operating_class(ic, |
| BW_HT40, |
| (uint8_t *)chan_list, |
| preference); |
| if (isset(chan_list, chan)) { |
| if (IEEE80211_OC_BEHAV_CHAN_UPPER == preference) |
| chan_sec = chan - IEEE80211_CHAN_SEC_SHIFT; |
| else if (IEEE80211_OC_BEHAV_CHAN_LOWWER == preference) |
| chan_sec = chan + IEEE80211_CHAN_SEC_SHIFT; |
| } |
| |
| if (chan_list) |
| kfree(chan_list); |
| |
| return chan_sec; |
| } |
| EXPORT_SYMBOL(ieee80211_find_sec_chan_by_operating_class); |
| |
| /* |
| * Generate an interference mitigation event |
| */ |
| #ifdef QSCS_ENABLED |
| struct brcm_rxglitch_thrshld_pair brcm_rxglitch_thrshlds[BRCM_RXGLITH_THRSHLD_PWR_NUM][BRCM_RXGLITH_THRSHLD_STEP] = { |
| { |
| {-49, BRCM_RXGLITCH_TOP}, |
| {-58, 32000}, |
| {-65, 20000}, |
| {-73, 12000}, |
| {BRCM_RSSI_MIN, 10000}, |
| }, |
| { |
| {-59, BRCM_RXGLITCH_TOP}, |
| {-68, 40000}, |
| {-74, 20000}, |
| {BRCM_RSSI_MIN, 10000}, |
| {0, 0}, |
| }, |
| }; |
| |
| static struct qtn_scs_vsp_node_stats *ieee80211_scs_find_node_stats(struct ieee80211com *ic, struct qtn_scs_info *scs_info_read, uint16_t aid); |
| int ieee80211_scs_clean_stats(struct ieee80211com *ic, uint32_t level, int clear_dfs_reentry); |
| static uint32_t ieee80211_scs_fix_cca_intf(struct ieee80211com *ic, struct ieee80211_node *ni, uint32_t cca_intf, uint32_t sp_fail, uint32_t lp_fail); |
| |
| static int ieee80211_prichan_is_newchan_better(struct ieee80211com *ic, |
| int newchan_ieee, int oldchan_ieee, int random_select) |
| { |
| int cur_bw; |
| struct ieee80211_channel *newchan; |
| struct ieee80211_channel *oldchan; |
| |
| if (!newchan_ieee || isclr(ic->ic_chan_active, newchan_ieee) || |
| isset(ic->ic_chan_pri_inactive, newchan_ieee)) { |
| return 0; |
| } |
| newchan = findchannel_any(ic, newchan_ieee, ic->ic_des_mode); |
| if (!is_ieee80211_chan_valid(newchan)) { |
| return 0; |
| } |
| |
| if (!oldchan_ieee || isclr(ic->ic_chan_active, oldchan_ieee) || |
| isset(ic->ic_chan_pri_inactive, oldchan_ieee)) { |
| return 1; |
| } |
| oldchan = findchannel_any(ic, oldchan_ieee, ic->ic_des_mode); |
| if (!is_ieee80211_chan_valid(oldchan)) { |
| return 1; |
| } |
| |
| /* Choose the channel with maximal power setting */ |
| cur_bw = ieee80211_get_bw(ic); |
| if (cur_bw >= BW_HT80) { |
| if (newchan->ic_maxpower_table[PWR_IDX_BF_OFF][PWR_IDX_1SS][PWR_IDX_80M] > |
| oldchan->ic_maxpower_table[PWR_IDX_BF_OFF][PWR_IDX_1SS][PWR_IDX_80M]) { |
| return 1; |
| } else if (newchan->ic_maxpower_table[PWR_IDX_BF_OFF][PWR_IDX_1SS][PWR_IDX_80M] < |
| oldchan->ic_maxpower_table[PWR_IDX_BF_OFF][PWR_IDX_1SS][PWR_IDX_80M]) { |
| return 0; |
| } |
| } |
| |
| if (cur_bw >= BW_HT40) { |
| if (newchan->ic_maxpower_table[PWR_IDX_BF_OFF][PWR_IDX_1SS][PWR_IDX_40M] > |
| oldchan->ic_maxpower_table[PWR_IDX_BF_OFF][PWR_IDX_1SS][PWR_IDX_40M]) { |
| return 1; |
| } else if (newchan->ic_maxpower_table[PWR_IDX_BF_OFF][PWR_IDX_1SS][PWR_IDX_40M] < |
| oldchan->ic_maxpower_table[PWR_IDX_BF_OFF][PWR_IDX_1SS][PWR_IDX_40M]) { |
| return 0; |
| } |
| } |
| |
| if (newchan->ic_maxpower_table[PWR_IDX_BF_OFF][PWR_IDX_1SS][PWR_IDX_20M] > |
| oldchan->ic_maxpower_table[PWR_IDX_BF_OFF][PWR_IDX_1SS][PWR_IDX_20M]) { |
| return 1; |
| } else if (newchan->ic_maxpower_table[PWR_IDX_BF_OFF][PWR_IDX_1SS][PWR_IDX_20M] < |
| oldchan->ic_maxpower_table[PWR_IDX_BF_OFF][PWR_IDX_1SS][PWR_IDX_20M]) { |
| return 0; |
| } |
| |
| /* All powers are same, run random selection per request */ |
| if (random_select) { |
| uint8_t rndbuf; |
| get_random_bytes(&rndbuf, 1); |
| return (rndbuf > 127); |
| } |
| |
| return 0; |
| } |
| |
| struct ieee80211_channel* ieee80211_chk_update_pri_chan(struct ieee80211com *ic, |
| struct ieee80211_channel *chan, uint32_t rank_by_pwr, const char* caller, int print_warning) |
| { |
| struct ieee80211_channel *prichan; |
| int newchan_ieee; |
| int prichan_ieee = 0; |
| int cur_bw = ieee80211_get_bw(ic); |
| int is_manual_cfg; |
| |
| if (ic->ic_opmode != IEEE80211_M_HOSTAP) { |
| return chan; |
| } |
| |
| is_manual_cfg = !strcmp(caller, "iwconfig"); |
| |
| if (cur_bw <= BW_HT20) { |
| goto done; |
| } |
| |
| if (isclr(ic->ic_chan_pri_inactive, chan->ic_ieee) || |
| (is_manual_cfg && isset(ic->ic_is_inactive_autochan_only, chan->ic_ieee))) { |
| if (!rank_by_pwr) { |
| return chan; |
| } else { |
| prichan_ieee = chan->ic_ieee; |
| } |
| } |
| |
| newchan_ieee = ieee80211_find_sec_chan(chan); |
| if (ieee80211_prichan_is_newchan_better(ic, newchan_ieee, prichan_ieee, 0)) { |
| prichan_ieee = newchan_ieee; |
| } |
| |
| if (cur_bw > BW_HT40) { |
| newchan_ieee = ieee80211_find_sec40u_chan(chan); |
| if (ieee80211_prichan_is_newchan_better(ic, newchan_ieee, prichan_ieee, |
| prichan_ieee && (prichan_ieee != chan->ic_ieee))) { |
| prichan_ieee = newchan_ieee; |
| } |
| |
| newchan_ieee = ieee80211_find_sec40l_chan(chan); |
| if (ieee80211_prichan_is_newchan_better(ic, newchan_ieee, prichan_ieee, |
| prichan_ieee && (prichan_ieee != chan->ic_ieee))) { |
| prichan_ieee = newchan_ieee; |
| } |
| } |
| |
| if (prichan_ieee && prichan_ieee != chan->ic_ieee) { |
| prichan = findchannel_any(ic, prichan_ieee, ic->ic_des_mode); |
| if (is_ieee80211_chan_valid(prichan)) { |
| if (isset(ic->ic_chan_pri_inactive, chan->ic_ieee)) { |
| if (print_warning) { |
| printk("%s: channel %d can't be used as primary channel," |
| " use %d instead within current bandwidth\n", |
| caller, chan->ic_ieee, prichan_ieee); |
| } |
| } |
| return prichan; |
| } |
| } |
| |
| done: |
| if (isset(ic->ic_chan_pri_inactive, chan->ic_ieee) && |
| (!is_manual_cfg || isclr(ic->ic_is_inactive_autochan_only, chan->ic_ieee))) { |
| if (print_warning) { |
| printk("%s: channel %d can't be used as primary channel," |
| " and no alternative channel within current bandwidth\n", |
| caller, chan->ic_ieee); |
| } |
| } |
| |
| return chan; |
| } |
| EXPORT_SYMBOL(ieee80211_chk_update_pri_chan); |
| |
| static void |
| ieee80211_wireless_scs_msg_send(struct ieee80211vap *vap, char *msg_buf) |
| { |
| ieee80211_eventf(vap->iv_dev, "%s", msg_buf); |
| }; |
| |
| static __inline int |
| ieee80211_is_cac_in_progress(struct ieee80211com *ic) |
| { |
| return ic->ic_curchan->ic_flags & IEEE80211_CHAN_DFS_CAC_IN_PROGRESS; |
| } |
| |
| void ieee80211_off_channel_timeout(unsigned long arg) |
| { |
| struct ieee80211com *ic = (struct ieee80211com *)arg; |
| struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); |
| struct offchan_protect *offchan_prt = &ic->ic_offchan_protect; |
| |
| if (vap == NULL || vap->iv_bss == NULL) { |
| if (vap) { |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG, |
| "%s: BSS not ready, delay the timer\n", |
| __func__); |
| } else { |
| DBGPRINTF(DBG_LL_ERROR, QDRV_LF_VAP, |
| "%s: VAP not ready, delay the timer\n", |
| __func__); |
| } |
| |
| offchan_prt->offchan_stop_expire.data = (unsigned long)ic; |
| offchan_prt->offchan_timeout = jiffies + IEEE80211_OFFCHAN_TIMEOUT_DEFAULT * HZ; |
| mod_timer(&offchan_prt->offchan_stop_expire, offchan_prt->offchan_timeout); |
| return; |
| } |
| |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG, "%s: suspending counter %u\n", |
| __func__, offchan_prt->offchan_suspend_cnt); |
| |
| offchan_prt->offchan_suspend_cnt = 0; |
| offchan_prt->offchan_timeout = 0; |
| ieee80211_param_to_qdrv(vap, IEEE80211_PARAM_OFF_CHAN_SUSPEND, 0, NULL, 0); |
| } |
| |
| void ieee80211_off_channel_suspend(struct ieee80211vap *vap, uint32_t timeout) |
| { |
| struct ieee80211com *ic = vap->iv_ic; |
| struct offchan_protect *offchan_prt = &ic->ic_offchan_protect; |
| |
| if (vap->iv_bss == NULL) { |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG, "%s: BSS not ready\n", |
| __func__); |
| return; |
| } |
| |
| if (offchan_prt->offchan_timeout == 0) { |
| offchan_prt->offchan_stop_expire.data = (unsigned long)ic; |
| offchan_prt->offchan_stop_expire.expires = jiffies + IEEE80211_OFFCHAN_TIMEOUT_DEFAULT * HZ; |
| offchan_prt->offchan_timeout = jiffies + IEEE80211_OFFCHAN_TIMEOUT_DEFAULT * HZ; |
| add_timer(&offchan_prt->offchan_stop_expire); |
| } |
| |
| if (time_after(jiffies + timeout * HZ, offchan_prt->offchan_timeout)) { |
| offchan_prt->offchan_stop_expire.data = (unsigned long)ic; |
| offchan_prt->offchan_timeout = jiffies + timeout * HZ; |
| mod_timer(&offchan_prt->offchan_stop_expire, offchan_prt->offchan_timeout); |
| } |
| |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG, "%s: suspending counter %u, " |
| "timeout %lus later\n", |
| __func__, offchan_prt->offchan_suspend_cnt, |
| (offchan_prt->offchan_timeout - jiffies) / HZ); |
| |
| offchan_prt->offchan_suspend_cnt++; |
| ieee80211_param_to_qdrv(vap, IEEE80211_PARAM_OFF_CHAN_SUSPEND, 1, NULL, 0); |
| ieee80211_scan_scs_sample_cancel(vap); |
| } |
| |
| void ieee80211_off_channel_resume(struct ieee80211vap *vap) |
| { |
| struct ieee80211com *ic = vap->iv_ic; |
| struct offchan_protect *offchan_prt = &ic->ic_offchan_protect; |
| |
| if (vap->iv_bss == NULL) { |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG, "%s: BSS not ready\n", |
| __func__); |
| return; |
| } |
| |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG, "%s: suspending counter %u, " |
| "timeout %lus later\n", |
| __func__, offchan_prt->offchan_suspend_cnt, |
| offchan_prt->offchan_timeout ? |
| (offchan_prt->offchan_timeout - jiffies) / HZ : |
| 0); |
| |
| /* There is a potential race condition here, but the timer will kick in and recover it. |
| * Currently we don't plan to protect against it */ |
| if (offchan_prt->offchan_suspend_cnt) |
| offchan_prt->offchan_suspend_cnt--; |
| else |
| return; |
| |
| if (offchan_prt->offchan_suspend_cnt == 0) { |
| offchan_prt->offchan_timeout = 0; |
| del_timer(&offchan_prt->offchan_stop_expire); |
| ieee80211_param_to_qdrv(vap, IEEE80211_PARAM_OFF_CHAN_SUSPEND, 0, NULL, 0); |
| } |
| } |
| |
| /* |
| * Interference mitigation sampling task |
| * Periodically go off-channel to sample the quality of another channel. |
| */ |
| static void |
| ieee80211_wireless_scs_sampling_task(struct work_struct *work) |
| { |
| struct delayed_work *dwork = (struct delayed_work *)work; |
| struct ieee80211com *ic = |
| container_of(dwork, struct ieee80211com, ic_scs_sample_work); |
| struct ieee80211vap *vap = NULL; |
| struct ieee80211vap *vap_first = TAILQ_FIRST(&ic->ic_vaps); |
| struct ieee80211vap *vap_next; |
| |
| if (ieee80211_is_cac_in_progress(ic)) { |
| SCSDBG(SCSLOG_NOTICE, "%s: not sampling - CAC in progress\n", __func__); |
| goto next_work; |
| } |
| |
| //FIXME check threshold |
| |
| /* Only sample if at least one VAP is in run state and none are scanning */ |
| vap_next = vap_first; |
| while ((vap_next != NULL) && |
| (vap_next->iv_state != IEEE80211_S_SCAN)) { |
| |
| if ((vap == NULL) && (vap_next->iv_opmode == IEEE80211_M_HOSTAP) && |
| (vap_next->iv_state == IEEE80211_S_RUN)) { |
| vap = vap_next; |
| } |
| vap_next = TAILQ_NEXT(vap_next, iv_next); |
| } |
| |
| if (vap) { |
| if (vap_next == NULL) { |
| IEEE80211_SCS_CNT_INC(&ic->ic_scs, IEEE80211_SCS_CNT_TRIGGER); |
| ieee80211_scan_scs_sample(vap); |
| } else { |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, |
| "%s: not sampling - scan in progress\n", __func__); |
| } |
| } else { |
| SCSDBG(SCSLOG_NOTICE, "%s: not sampling - no VAPs in RUN state\n", __func__); |
| } |
| |
| next_work: |
| schedule_delayed_work(&ic->ic_scs_sample_work, ic->ic_scs.scs_sample_intv * HZ); |
| } |
| |
| static void ieee80211_scs_trigger_channel_switch(unsigned long arg) |
| { |
| struct ieee80211com *ic = (struct ieee80211com *)arg; |
| |
| ieee80211_finish_csa((unsigned long)ic); |
| |
| ieee80211_scs_clean_stats(ic, IEEE80211_SCS_STATE_CHANNEL_SWITCHING, 0); |
| |
| return; |
| } |
| |
| static __inline int |
| ieee80211_scs_get_cca_intf_thrshld(struct ieee80211com *ic, uint8_t is_high) |
| { |
| uint32_t thrshld = is_high ? ic->ic_scs.scs_cca_intf_hi_thrshld : |
| ic->ic_scs.scs_cca_intf_lo_thrshld; |
| |
| if (ic->ic_curchan && (ic->ic_curchan->ic_flags & IEEE80211_CHAN_DFS)) { |
| thrshld += ic->ic_scs.scs_cca_intf_dfs_margin; |
| } |
| |
| thrshld = MIN(thrshld, 100); |
| thrshld = thrshld * IEEE80211_SCS_CCA_INTF_SCALE / 100; |
| |
| return thrshld; |
| } |
| |
| static __inline int |
| ieee80211_scs_get_cca_idle_thrshld(struct ieee80211com *ic) |
| { |
| int thrshld = ic->ic_scs.scs_cca_idle_thrshld * IEEE80211_SCS_CCA_INTF_SCALE / 100; |
| |
| return thrshld; |
| } |
| |
| static int |
| ieee80211_scs_is_interference_over_thresholds(struct ieee80211com *ic, |
| uint32_t cca_intf, uint32_t cca_idle, uint32_t pmbl_err) |
| { |
| uint32_t cca_intf_high_thrshld = ieee80211_scs_get_cca_intf_thrshld(ic, 1); |
| uint32_t cca_intf_low_thrshld = ieee80211_scs_get_cca_intf_thrshld(ic, 0); |
| uint32_t cca_idle_thrshld = ieee80211_scs_get_cca_idle_thrshld(ic); |
| uint32_t pmbl_err_thrshld = ic->ic_scs.scs_pmbl_err_thrshld; |
| |
| /* Currently we don't apply the thresholds of FAT and preamble error to QHop case */ |
| if (ieee80211_wds_vap_exists(ic)) { |
| if (cca_intf >= cca_intf_low_thrshld) { |
| SCSDBG(SCSLOG_VERBOSE, "%s: [QHOP case:cca_intf > low thrshld] Trigger channel change\n", __func__); |
| return 1; |
| } |
| } else if (cca_idle < cca_idle_thrshld) { |
| if ((cca_intf > cca_intf_high_thrshld) |
| || ((cca_intf > cca_intf_low_thrshld) |
| && (pmbl_err > pmbl_err_thrshld))) { |
| SCSDBG(SCSLOG_VERBOSE, "%s: [cca_idle < thrshld, %s] - Trigger channel change\n", __func__, |
| ((cca_intf > cca_intf_high_thrshld) ? "cca_intf > high thrshld" : |
| "cca_intf > low thrshld, pmbl_err > thrshld")); |
| return 1; |
| } |
| } |
| return 0; |
| } |
| |
| static int |
| ieee80211_is_cc_required(struct ieee80211com *ic, uint32_t compound_cca_intf, |
| uint32_t cca_idle_smthed, uint32_t pmbl_err) |
| { |
| int res = 0; |
| struct ap_state *as; |
| |
| if (ic->ic_sta_cc) { |
| SCSDBG(SCSLOG_NOTICE, "STA reported SCS measurements\n"); |
| res |= IEEE80211_SCS_STA_CCA_REQ_CC; |
| } |
| |
| if (ic->ic_sta_cc_brcm) { |
| SCSDBG(SCSLOG_NOTICE, "brcm STA info need channel change\n"); |
| res |= IEEE80211_SCS_BRCM_STA_TRIGGER_CC; |
| } |
| |
| if (ic->ic_opmode == IEEE80211_M_STA) { |
| /* For STA mode, always send cca report to AP */ |
| res |= IEEE80211_SCS_SELF_CCA_CC; |
| } else if (ieee80211_scs_is_interference_over_thresholds(ic, |
| compound_cca_intf, cca_idle_smthed, pmbl_err)) { |
| SCSDBG(SCSLOG_NOTICE, "Self CCA requested channel change," |
| " compound_cca_intf=%u, cca_idle_smth=%u, pmbl_err=%u\n", |
| compound_cca_intf, cca_idle_smthed, pmbl_err); |
| res |= IEEE80211_SCS_SELF_CCA_CC; |
| } |
| |
| if (ic->ic_opmode == IEEE80211_M_HOSTAP && ic->ic_scs.scs_atten_sw_enable) { |
| as = ic->ic_scan->ss_scs_priv; |
| if (SCS_ATTEN_VALID(as->as_sta_atten_expect) && |
| SCS_ATTEN_VALID(as->as_sta_atten_max) && |
| (as->as_sta_atten_max >= (as->as_sta_atten_expect + ic->ic_scs.scs_thrshld_atten_inc))) { |
| SCSDBG(SCSLOG_NOTICE, "raw attenuation increased, need channel change, curr=%d, expect=%d\n", |
| as->as_sta_atten_max, as->as_sta_atten_expect); |
| res |= IEEE80211_SCS_ATTEN_INC_CC; |
| } |
| } |
| |
| if ((res) && (ic->ic_radar_test_mode_enabled != NULL) && ic->ic_radar_test_mode_enabled()) { |
| SCSDBG(SCSLOG_NOTICE, "channel change is disabled under radar test mode\n"); |
| res = 0; |
| } |
| |
| /* Don't switch channel under basic WDS mode */ |
| /* But channel switch is now possible on the WDS link if it is an MBS */ |
| if (res) { |
| struct ieee80211vap *vap; |
| TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { |
| if (IEEE80211_VAP_WDS_BASIC(vap)) { |
| SCSDBG(SCSLOG_NOTICE, "channel change is disabled under basic WDS mode\n"); |
| res = 0; |
| break; |
| } |
| } |
| } |
| |
| if (res && (ic->ic_opmode == IEEE80211_M_HOSTAP) && |
| (ic->ic_pm_state[QTN_PM_CURRENT_LEVEL] >= BOARD_PM_LEVEL_DUTY)) { |
| SCSDBG(SCSLOG_NOTICE, "channel change is disabled in CoC idle state\n"); |
| res = 0; |
| } |
| |
| if (res && !ic->ic_scs.scs_enable) { |
| SCSDBG(SCSLOG_NOTICE, "channel change is disabled since SCS is disabled\n"); |
| res = 0; |
| } |
| |
| return res; |
| } |
| |
| void ieee80211_scs_show_ranking_stats(struct ieee80211com *ic, int show_input, int show_result) |
| { |
| struct ap_state *as; |
| int i; |
| |
| if (ic->ic_opmode != IEEE80211_M_HOSTAP) { |
| printk("SCS ranking state is only available in AP mode\n"); |
| return; |
| } |
| |
| as = ic->ic_scan->ss_scs_priv; |
| |
| if (show_input) { |
| printk("SCS: ranking parameters\n"); |
| for (i = 0; i < IEEE80211_CHAN_MAX; i++) { |
| if (isclr(ic->ic_chan_active, i) || |
| !findchannel(ic, i, ic->ic_des_mode)) { |
| continue; |
| } |
| if (as->as_cca_intf[i] != SCS_CCA_INTF_INVALID) { |
| printk("chan %d: cca_intf=%u, pmbl=%u %u\n", |
| i, as->as_cca_intf[i], as->as_pmbl_err_ap[i], |
| as->as_pmbl_err_sta[i]); |
| } |
| } |
| |
| printk("SCS: atten info: num=%d, sum=%d, min=%d, max=%d, avg=%d, expect=%d\n", |
| as->as_sta_atten_num, |
| as->as_sta_atten_sum, |
| as->as_sta_atten_min, |
| as->as_sta_atten_max, |
| as->as_sta_atten_num ? (as->as_sta_atten_sum / as->as_sta_atten_num) : 0, |
| as->as_sta_atten_expect); |
| |
| printk("SCS: tx_ms=%u, rx_ms=%u\n", as->as_tx_ms, as->as_rx_ms); |
| } |
| |
| if (show_result) { |
| int isdfs; |
| int txpower; |
| struct ieee80211_channel *chan; |
| |
| printk("SCS: ranking table, ranking_cnt=%u\n", as->as_scs_ranking_cnt); |
| printk("chan dfs xped txpower cca_intf metric pmbl_ap pmbl_sta\n"); |
| for (i = 1; i < IEEE80211_CHAN_MAX; i++) { |
| chan = ieee80211_find_channel_by_ieee(ic, i); |
| if (chan == NULL) { |
| continue; |
| } |
| |
| isdfs = !!(chan->ic_flags & IEEE80211_CHAN_DFS); |
| txpower = chan->ic_maxpower; |
| |
| printk("%4d %3d %4d %7d %8u %10d %10d %10d\n", |
| i, |
| isdfs, |
| !!isset(as->as_chan_xped, i), |
| txpower, |
| ((as->as_cca_intf[i] == SCS_CCA_INTF_INVALID) ? 0 : as->as_cca_intf[i]), |
| as->as_chanmetric[i], |
| as->as_pmbl_err_ap[i], as->as_pmbl_err_sta[i]); |
| } |
| } |
| } |
| EXPORT_SYMBOL(ieee80211_scs_show_ranking_stats); |
| |
| void ieee80211_show_initial_ranking_stats(struct ieee80211com *ic) |
| { |
| struct ap_state *as; |
| int i; |
| int isdfs; |
| int txpower; |
| struct ieee80211_channel *chan; |
| |
| if (ic->ic_opmode != IEEE80211_M_HOSTAP) { |
| printk("Initial scan ranking state is only available in AP mode\n"); |
| return; |
| } |
| |
| as = ic->ic_scan->ss_priv; |
| if (as == NULL) { |
| printk("Initial scan ranking state is not available because auto channel is disabled\n"); |
| return; |
| } |
| |
| printk("AP: initial ranking table\n"); |
| printk("chan dfs txpower numbeacon cci aci cca_intf pmbl_err metric\n"); |
| for (i = 1; i < IEEE80211_CHAN_MAX; i++) { |
| chan = ieee80211_find_channel_by_ieee(ic, i); |
| if (chan == NULL) { |
| continue; |
| } |
| |
| isdfs = !!(chan->ic_flags & IEEE80211_CHAN_DFS); |
| txpower = chan->ic_maxpower; |
| |
| printk("%4d %3d %7d %10u %10d %10d %10d %10d %10d\n", |
| i, |
| isdfs, |
| txpower, |
| as->as_numbeacons[i], |
| as->as_cci[i], |
| as->as_aci[i], |
| as->as_cca_intf[i], |
| as->as_pmbl_err_ap[i], |
| as->as_chanmetric[i]); |
| } |
| } |
| EXPORT_SYMBOL(ieee80211_show_initial_ranking_stats); |
| |
| static __inline int |
| ieee80211_scs_node_is_valid(struct ieee80211_node *ni) |
| { |
| return (ni->ni_associd && |
| ieee80211_node_is_authorized(ni) && |
| !ieee80211_blacklist_check(ni)); |
| } |
| |
| void ieee80211_scs_node_clean_stats(void *s, struct ieee80211_node *ni) |
| { |
| int level = (uint32_t)s; |
| struct ieee80211com *ic = ni->ni_ic; |
| |
| if (!ieee80211_scs_node_is_valid(ni) && (level != IEEE80211_SCS_STATE_INIT)) { |
| return; |
| } |
| |
| SCSDBG(SCSLOG_VERBOSE, "node 0x%x state clean with level %d\n", ni->ni_associd, level); |
| |
| if (level <= IEEE80211_SCS_STATE_PERIOD_CLEAN) { |
| ni->ni_recent_cca_intf = SCS_CCA_INTF_INVALID; |
| ni->ni_recent_sp_fail = 0; |
| ni->ni_recent_lp_fail = 0; |
| ni->ni_recent_tdls_tx_time = 0; |
| ni->ni_recent_tdls_rx_time = 0; |
| ni->ni_recent_others_time = 0; |
| } |
| |
| if (level <= IEEE80211_SCS_STATE_MEASUREMENT_CHANGE_CLEAN) { |
| /* set to -1 helps to discard first report after assoc or channel switch */ |
| ni->ni_recent_rxglitch_trig_consecut = -1; |
| ni->ni_recent_rxglitch = 0; |
| ni->ni_recent_cca_intf_smthed = 0; |
| ni->ni_others_rx_time_smthed = 0; |
| ni->ni_others_tx_time_smthed = 0; |
| ni->ni_recent_others_time_smth = 0; |
| ni->ni_tdls_tx_time_smthed = 0; |
| ni->ni_tdls_rx_time_smthed = 0; |
| } |
| |
| if (level <= IEEE80211_SCS_STATE_RESET) { |
| ni->ni_atten_smoothed = SCS_ATTEN_UNINITED; |
| } |
| } |
| |
| void ieee80211_scs_clean_tdls_stats_list(struct ieee80211com *ic) |
| { |
| int i; |
| unsigned long flags; |
| struct ieee80211_tdls_scs_entry *scs_entry = NULL; |
| |
| SCSDBG(SCSLOG_NOTICE, "SCS: clean all of tdls stats\n"); |
| spin_lock_irqsave(&ic->ic_scs.scs_tdls_lock, flags); |
| for (i = 0; i < IEEE80211_NODE_HASHSIZE; i++) { |
| LIST_FOREACH(scs_entry, &ic->ic_scs.scs_tdls_list[i], entry) { |
| if (scs_entry != NULL) { |
| scs_entry->stats.is_latest = 0; |
| scs_entry->stats.tx_time = 0; |
| } |
| } |
| } |
| spin_unlock_irqrestore(&ic->ic_scs.scs_tdls_lock, flags); |
| } |
| |
| void |
| ieee80211_scs_metric_update_timestamps(struct ap_state *as) |
| { |
| int i; |
| for (i = 0; i < IEEE80211_CHAN_MAX; ++i) { |
| as->as_chanmetric_timestamp[i] = jiffies; |
| } |
| } |
| EXPORT_SYMBOL(ieee80211_scs_metric_update_timestamps); |
| |
| /* |
| * Clean SCS state with different clean levels. |
| * Valid levels are IEEE80211_SCS_STATE_XXXX. |
| * @clear_dfs_reentry: only effective at level IEEE80211_SCS_STATE_PERIOD_CLEAN. |
| */ |
| int ieee80211_scs_clean_stats(struct ieee80211com *ic, uint32_t level, int clear_dfs_reentry) |
| { |
| struct ap_state *as = ic->ic_scan->ss_scs_priv; |
| int i; |
| |
| SCSDBG(SCSLOG_INFO, "clean stats with level %u\n", level); |
| |
| if (level <= IEEE80211_SCS_STATE_PERIOD_CLEAN) { |
| ic->ic_sta_cc = 0; |
| ic->ic_sta_cc_brcm = 0; |
| if (clear_dfs_reentry) { |
| as->as_dfs_reentry_cnt = 0; |
| as->as_dfs_reentry_level = 0; |
| SCSDBG(SCSLOG_INFO, "dfs reentry state cleared\n"); |
| } |
| ieee80211_scs_clean_tdls_stats_list(ic); |
| } |
| |
| if (level <= IEEE80211_SCS_STATE_MEASUREMENT_CHANGE_CLEAN) { |
| as->as_tx_ms_smth = 0; |
| as->as_rx_ms_smth = 0; |
| as->as_cca_intf_smth = 0; |
| } |
| |
| if (level <= IEEE80211_SCS_STATE_CHANNEL_SWITCHING) { |
| ic->ic_scs.scs_cca_intf_smthed = 0; |
| ic->ic_scs.scs_sp_err_smthed = 0; |
| ic->ic_scs.scs_lp_err_smthed = 0; |
| ic->ic_scs.scs_cca_idle_smthed = 0; |
| } |
| |
| if (level <= IEEE80211_SCS_STATE_RESET) { |
| SCSDBG(SCSLOG_NOTICE, "reset ranking stats\n"); |
| ic->ic_scs.scs_brcm_rxglitch_thrshlds = (struct brcm_rxglitch_thrshld_pair*)brcm_rxglitch_thrshlds; |
| as->as_scs_ranking_cnt = 0; |
| |
| for (i = 0; i < IEEE80211_CHAN_MAX; i++) { |
| as->as_cca_intf[i] = SCS_CCA_INTF_INVALID; |
| as->as_cca_intf_jiffies[i] = 0; |
| as->as_pmbl_err_ap[i] = 0; |
| as->as_pmbl_err_sta[i] = 0; |
| } |
| |
| as->as_sta_atten_num = 0; |
| as->as_sta_atten_sum = 0; |
| as->as_sta_atten_min = SCS_ATTEN_UNINITED; |
| as->as_sta_atten_max = SCS_ATTEN_UNINITED; |
| as->as_sta_atten_expect = SCS_ATTEN_UNINITED; |
| |
| as->as_dfs_reentry_cnt = 0; |
| as->as_dfs_reentry_level = 0; |
| |
| as->as_tx_ms = 0; |
| as->as_rx_ms = 0; |
| |
| memset(as->as_chan_xped, 0, sizeof(as->as_chan_xped)); |
| |
| memset(as->as_chanmetric, 0, sizeof(as->as_chanmetric)); |
| ieee80211_scs_metric_update_timestamps(as); |
| memset(as->as_chanmetric_pref, 0, sizeof(as->as_chanmetric_pref)); |
| |
| ic->ic_scs.scs_burst_is_paused = 0; |
| memset(ic->ic_scs.scs_burst_queue, 0 , sizeof(ic->ic_scs.scs_burst_queue)); |
| } |
| |
| /* |
| * No need to do clean node in level IEEE80211_SCS_STATE_PERIOD_CLEAN |
| * because it is associated with jiffies. So that we don't need to iterate |
| * all node every scs interval. |
| */ |
| if ((level <= IEEE80211_SCS_STATE_MEASUREMENT_CHANGE_CLEAN) && |
| (ic->ic_opmode == IEEE80211_M_HOSTAP)) { |
| ic->ic_iterate_nodes(&ic->ic_sta, ieee80211_scs_node_clean_stats, |
| (void *)level, 1); |
| } |
| |
| return 0; |
| } |
| EXPORT_SYMBOL(ieee80211_scs_clean_stats); |
| |
| static void ieee80211_send_usr_l2_pkt(struct ieee80211vap *vap, uint8_t *pkt, uint32_t pkt_len) |
| { |
| struct sk_buff *skb = dev_alloc_skb(qtn_rx_buf_size()); |
| |
| if (pkt_len > qtn_rx_buf_size()) |
| pkt_len = qtn_rx_buf_size(); |
| |
| if (skb) { |
| if (copy_from_user(skb->data, pkt, pkt_len)) { |
| dev_kfree_skb(skb); |
| return; |
| } |
| skb->len = pkt_len; |
| skb->dest_port = 0; |
| skb->dev = vap->iv_dev; |
| skb->priority = QDRV_SCH_MODULE_ID | QDRV_BAND_AC_BK; |
| dev_queue_xmit(skb); |
| } |
| } |
| |
| void ieee80211_scs_brcm_info_report(struct ieee80211com *ic, struct ieee80211_node *ni, int32_t rssi, uint32_t rxglitch) |
| { |
| int i; |
| uint32_t glitch_thrshld = 0; |
| int ratio; |
| int pwr; |
| uint32_t cca_intf; |
| uint32_t trig_rxglitch; |
| |
| /* Currently we don't support BRCM 11AC STA's CC request*/ |
| if (IEEE80211_NODE_IS_VHT(ni)) { |
| SCSDBG(SCSLOG_NOTICE, "Ignore BRCM 11AC STA's report\n"); |
| return; |
| } |
| |
| if (!ic->ic_curchan) |
| return; |
| |
| if (!ic->ic_scs.scs_stats_on) |
| return; |
| |
| if (rxglitch >= BRCM_RXGLITCH_MAX_PER_INTVL) { |
| SCSDBG(SCSLOG_NOTICE, "brcm node 0x%x "MACSTR" rssi=%d, rxglitch=%u(discard)\n", |
| ni->ni_associd, MAC2STR(ni->ni_macaddr), rssi, rxglitch); |
| return; |
| } else { |
| SCSDBG(SCSLOG_INFO, "brcm node 0x%x "MACSTR" rssi=%d, rxglitch=%u\n", |
| ni->ni_associd, MAC2STR(ni->ni_macaddr), rssi, rxglitch); |
| } |
| |
| if (ic->ic_curchan->ic_maxpower >= IEEE80211_SCS_CHAN_POWER_CUTPOINT) { |
| pwr = BRCM_RXGLITH_THRSHLD_HIPWR; |
| } else { |
| pwr = BRCM_RXGLITH_THRSHLD_LOWPWR; |
| } |
| |
| for (i = 0; i < BRCM_RXGLITH_THRSHLD_STEP; i++) { |
| if (rssi > brcm_rxglitch_thrshlds[pwr][i].rssi) { |
| glitch_thrshld = brcm_rxglitch_thrshlds[pwr][i].rxglitch; |
| glitch_thrshld = glitch_thrshld * ic->ic_scs.scs_brcm_rxglitch_thrshlds_scale / 100; |
| break; |
| } |
| } |
| if (!glitch_thrshld) |
| return; |
| |
| trig_rxglitch = 0; |
| if (rxglitch >= glitch_thrshld){ |
| if (ni->ni_recent_rxglitch_trig_consecut > 0) { |
| trig_rxglitch = rxglitch; |
| SCSDBG(SCSLOG_NOTICE, "brcm node 0x%x is triggered consecutively\n", ni->ni_associd); |
| } else { |
| SCSDBG(SCSLOG_NOTICE, "brcm node 0x%x is not triggered in last report, wait for next\n", ni->ni_associd); |
| } |
| ni->ni_recent_rxglitch_trig_consecut++; |
| } else if ((ni->ni_recent_rxglitch_trig_consecut > 0) && |
| (rxglitch >= (BRCM_RXGLITCH_NEXT_TRIG_THRSHLD * glitch_thrshld / 100))) { |
| SCSDBG(SCSLOG_NOTICE, "brcm node 0x%x is triggered in last report, and validated\n", |
| ni->ni_associd); |
| trig_rxglitch = ni->ni_recent_rxglitch; |
| ni->ni_recent_rxglitch_trig_consecut = 0; |
| } else { |
| ni->ni_recent_rxglitch_trig_consecut = 0; |
| } |
| ni->ni_recent_rxglitch = rxglitch; |
| |
| if (trig_rxglitch) { |
| ratio = trig_rxglitch * 100 / glitch_thrshld; |
| cca_intf = ratio * ic->ic_scs.scs_cca_intf_lo_thrshld * IEEE80211_SCS_CCA_INTF_SCALE / 10000; |
| cca_intf = MIN(cca_intf, IEEE80211_SCS_CCA_INTF_SCALE); |
| SCSDBG(SCSLOG_NOTICE, "brcm node 0x%x report high rxglitch %u > %u, " |
| "with consecutive count %u, mapped to cca_intf %u\n", |
| ni->ni_associd, trig_rxglitch, glitch_thrshld, |
| ni->ni_recent_rxglitch_trig_consecut, |
| cca_intf); |
| ni->ni_recent_cca_intf = cca_intf; |
| ni->ni_recent_cca_intf_jiffies = jiffies; |
| ic->ic_sta_cc_brcm = 1; |
| } |
| } |
| |
| void ieee80211_scs_update_cca_intf(struct ieee80211com *ic, |
| struct scs_chan_intf_params *params, |
| uint8_t iscochan, |
| struct ieee80211_node *ni) |
| { |
| struct ap_state *as = ic->ic_scan->ss_scs_priv; |
| uint16_t cca_intf_scaled; |
| uint16_t old_cca_intf = 0; |
| uint8_t smth_fctr = 0; |
| uint32_t old_pmbl_ap = 0; |
| uint32_t old_pmbl_sta = 0; |
| uint32_t old_cca_pri = 0; |
| uint32_t old_cca_sec = 0; |
| uint32_t old_cca_sec40 = 0; |
| uint32_t chan = params->chan->ic_ieee; |
| |
| if (ic->ic_opmode != IEEE80211_M_HOSTAP) { |
| return; |
| } |
| |
| if (!is_channel_valid(chan)) { |
| return; |
| } |
| |
| /* reset entry */ |
| if (params->cca_intf == SCS_CCA_INTF_INVALID) { |
| SCSDBG(SCSLOG_INFO, "clean chan %u cca_intf in ranking table\n", chan); |
| as->as_cca_intf[chan] = SCS_CCA_INTF_INVALID; |
| as->as_cca_intf_jiffies[chan] = 0; |
| as->as_pmbl_err_ap[chan] = 0; |
| as->as_pmbl_err_sta[chan] = 0; |
| as->as_cca_intf_pri[chan] = 0; |
| as->as_cca_intf_sec[chan] = 0; |
| as->as_cca_intf_sec40[chan] = 0; |
| return; |
| } |
| |
| /* always scale cca_intf so that it is adaptive to cca duration change and off-chan sample */ |
| cca_intf_scaled = params->cca_intf * IEEE80211_SCS_CCA_INTF_SCALE / params->cca_dur; |
| if (params->cca_dur != IEEE80211_SCS_CCA_INTF_SCALE) |
| SCSDBG(SCSLOG_NOTICE, "scale cca_intf from %u to %u\n", params->cca_intf, cca_intf_scaled); |
| |
| if (ni) { |
| SCSDBG(SCSLOG_NOTICE, "%s - sta %x add cca intf %u to chan %u, sta max pmbl=%u\n", |
| iscochan ? "cochan" : "offchan", |
| ni->ni_associd, cca_intf_scaled, chan, params->pmbl_err); |
| } else { |
| SCSDBG(SCSLOG_NOTICE, "%s - self add cca intf %u to chan %u, pmbl=%u\n", |
| iscochan ? "cochan" : "offchan", |
| cca_intf_scaled, chan, params->pmbl_err); |
| } |
| |
| if (iscochan) { |
| /* current channel's cca_intf use maximum of AP and STAs */ |
| if ((cca_intf_scaled > as->as_cca_intf[chan]) || |
| (as->as_cca_intf[chan] == SCS_CCA_INTF_INVALID)) { |
| as->as_cca_intf[chan] = cca_intf_scaled; |
| as->as_cca_intf_jiffies[chan] = jiffies; |
| } |
| if (ni != NULL) { |
| as->as_pmbl_err_sta[chan] = MAX(as->as_pmbl_err_sta[chan], params->pmbl_err); |
| } else { |
| as->as_pmbl_err_ap[chan] = params->pmbl_err; |
| as->as_cca_intf_pri[chan] = params->cca_pri; |
| as->as_cca_intf_sec[chan] = params->cca_sec; |
| as->as_cca_intf_sec40[chan] = params->cca_sec40; |
| } |
| } else { |
| /* update off-channel sampling stats with exponential smoothing */ |
| if (as->as_cca_intf[chan] == SCS_CCA_INTF_INVALID) { |
| as->as_cca_intf[chan] = cca_intf_scaled; |
| /* only have AP side off-channel sampling now */ |
| as->as_pmbl_err_ap[chan] = params->pmbl_err; |
| as->as_cca_intf_pri[chan] = params->cca_pri; |
| as->as_cca_intf_sec[chan] = params->cca_sec; |
| as->as_cca_intf_sec40[chan] = params->cca_sec40; |
| } else { |
| smth_fctr = (isclr(as->as_chan_xped, chan)) ? |
| ic->ic_scs.scs_cca_intf_smth_fctr[SCS_CCA_INTF_SMTH_FCTR_NOXP] : |
| ic->ic_scs.scs_cca_intf_smth_fctr[SCS_CCA_INTF_SMTH_FCTR_XPED]; |
| old_cca_intf = as->as_cca_intf[chan]; |
| as->as_cca_intf[chan] = IEEE80211_SCS_SMOOTH(old_cca_intf, cca_intf_scaled, |
| smth_fctr); |
| /* only have AP side off-channel sampling now */ |
| old_pmbl_ap = as->as_pmbl_err_ap[chan]; |
| as->as_pmbl_err_ap[chan] = IEEE80211_SCS_SMOOTH(old_pmbl_ap, params->pmbl_err, |
| smth_fctr); |
| /* let sta side pmbl smoothing out */ |
| old_pmbl_sta = as->as_pmbl_err_sta[chan]; |
| as->as_pmbl_err_sta[chan] = IEEE80211_SCS_SMOOTH(old_pmbl_sta, 0, |
| smth_fctr); |
| |
| old_cca_pri = as->as_cca_intf_pri[chan]; |
| as->as_cca_intf_pri[chan] = IEEE80211_SCS_SMOOTH(old_cca_pri, params->cca_pri, |
| smth_fctr); |
| |
| old_cca_sec = as->as_cca_intf_sec[chan]; |
| as->as_cca_intf_sec[chan] = IEEE80211_SCS_SMOOTH(old_cca_sec, params->cca_sec, |
| smth_fctr); |
| |
| old_cca_sec40 = as->as_cca_intf_sec40[chan]; |
| as->as_cca_intf_sec40[chan] = IEEE80211_SCS_SMOOTH(old_cca_sec40, params->cca_sec40, |
| smth_fctr); |
| } |
| /* mark channel entry updated so it won't be aged out */ |
| as->as_cca_intf_jiffies[chan] = jiffies; |
| SCSDBG(SCSLOG_INFO, "OC: chan=%u, smth_fctr=%u, cca_intf: prev=%u, " |
| "curr=%u, smthed=%u; pmbl: prev=%u,%u, curr=%u, smthed=%u,%u; " |
| "cca_pri: prev=%u, curr=%u, smthed=%u; " |
| "cca_sec: prev=%u, curr=%u, smthed=%u; " |
| "cca_sec40: prev=%u, curr=%u, smthed=%u\n", |
| chan, smth_fctr, |
| old_cca_intf, cca_intf_scaled, as->as_cca_intf[chan], |
| old_pmbl_ap, old_pmbl_sta, params->pmbl_err, |
| as->as_pmbl_err_ap[chan], as->as_pmbl_err_sta[chan], |
| old_cca_pri, params->cca_pri, as->as_cca_intf_pri[chan], |
| old_cca_sec, params->cca_sec, as->as_cca_intf_sec[chan], |
| old_cca_sec40, params->cca_sec40, as->as_cca_intf_sec40[chan]); |
| } |
| } |
| |
| static void ieee80211_scs_update_chans_cca_intf(struct ieee80211com *ic, |
| struct scs_chan_intf_params *params, |
| uint32_t update_mode, |
| struct ieee80211_node *ni) |
| { |
| uint32_t chan_bw = params->chan_bw; |
| uint32_t chan_sec = 0; |
| uint32_t chan_sec40u = 0; |
| uint32_t chan_sec40l = 0; |
| uint8_t isoffchan = (update_mode == IEEE80211_SCS_OFFCHAN); |
| uint8_t iscochan = (update_mode == IEEE80211_SCS_COCHAN); |
| struct ieee80211_channel *chan; |
| |
| SCSDBG(SCSLOG_INFO, "Update chans cca intf -- chan:%u bw:%u intf:%u dur:%u pmbl:%u " |
| "cca_pri:%u cca_sec:%u cca_sec40:%u\n", |
| params->chan->ic_ieee, |
| params->chan_bw, |
| params->cca_intf, |
| params->cca_dur, |
| params->pmbl_err, |
| params->cca_pri, |
| params->cca_sec, |
| params->cca_sec40); |
| |
| if (isoffchan && params->chan->ic_ieee == ic->ic_curchan->ic_ieee) |
| return; |
| |
| if (chan_bw >= BW_HT40) { |
| chan_sec = ieee80211_find_sec_chan(params->chan); |
| if (isoffchan && chan_sec == ic->ic_curchan->ic_ieee) |
| return; |
| if (chan_bw >= BW_HT80) { |
| chan_sec40u = ieee80211_find_sec40u_chan(params->chan); |
| chan_sec40l = ieee80211_find_sec40l_chan(params->chan); |
| if (isoffchan && (chan_sec40u == ic->ic_curchan->ic_ieee || |
| chan_sec40l == ic->ic_curchan->ic_ieee)) |
| return; |
| } |
| } |
| |
| ieee80211_scs_update_cca_intf(ic, params, iscochan, ni); |
| if (chan_bw >= BW_HT40) { |
| if (chan_sec) { |
| chan = ieee80211_find_channel_by_ieee(ic, chan_sec); |
| if (is_ieee80211_chan_valid(chan)) { |
| params->chan = chan; |
| ieee80211_scs_update_cca_intf(ic, params, iscochan, ni); |
| } |
| } |
| if (chan_bw >= BW_HT80) { |
| if (chan_sec40u) { |
| chan = ieee80211_find_channel_by_ieee(ic, chan_sec40u); |
| if (is_ieee80211_chan_valid(chan)) { |
| params->chan = chan; |
| ieee80211_scs_update_cca_intf(ic, params, iscochan, ni); |
| } |
| } |
| if (chan_sec40l) { |
| chan = ieee80211_find_channel_by_ieee(ic, chan_sec40l); |
| if (is_ieee80211_chan_valid(chan)) { |
| params->chan = chan; |
| ieee80211_scs_update_cca_intf(ic, params, iscochan, ni); |
| } |
| } |
| } |
| } |
| } |
| |
| /* |
| * This function should NOT be called right after the txpower change because the rssi may come |
| * from the original txpower. Make sure txpower and rssi is match. |
| */ |
| void ieee80211_scs_node_update_rssi(void *s, struct ieee80211_node *ni) |
| { |
| struct ieee80211com *ic = s; |
| struct ap_state *as = ic->ic_scan->ss_scs_priv; |
| int32_t rssi = SCS_RSSI_UNINITED; |
| int32_t prev_atten; |
| uint8_t smth_fctr; |
| int32_t txpower = ic->ic_curchan->ic_maxpower; |
| int32_t atten = SCS_ATTEN_UNINITED; |
| |
| if (!ieee80211_scs_node_is_valid(ni)) { |
| return; |
| } |
| |
| /* get recent and smooth */ |
| rssi = ic->ic_rssi(ni); |
| if (SCS_RSSI_VALID(rssi)) { |
| if (ieee80211_node_is_qtn(ni) && ni->ni_txpower) { |
| txpower = ni->ni_txpower; |
| SCSDBG(SCSLOG_VERBOSE, "qtn node %#x, using TX power %d\n", |
| ni->ni_associd, txpower); |
| } |
| |
| atten = txpower - rssi / SCS_RSSI_PRECISION_RECIP; |
| if (ni->ni_atten_smoothed == SCS_ATTEN_UNINITED) { |
| ni->ni_atten_smoothed = atten; |
| SCSDBG(SCSLOG_INFO, "node 0x%x init rssi=%d, atten=%d\n", ni->ni_associd, |
| rssi, ni->ni_atten_smoothed); |
| } else { |
| /* exponential smooth */ |
| prev_atten = ni->ni_atten_smoothed; |
| smth_fctr = (atten <= prev_atten) ? ic->ic_scs.scs_rssi_smth_fctr[SCS_RSSI_SMTH_FCTR_UP] |
| : ic->ic_scs.scs_rssi_smth_fctr[SCS_RSSI_SMTH_FCTR_DOWN]; |
| ni->ni_atten_smoothed = IEEE80211_SCS_SMOOTH(prev_atten, atten, smth_fctr); |
| SCSDBG(SCSLOG_VERBOSE, "node 0x%x prev_atten=%d, curr_rssi=%d, curr_atten=%d, " |
| "smooth_factor=%u, smoothed atten=%d\n", |
| ni->ni_associd, prev_atten, rssi, atten, |
| smth_fctr, ni->ni_atten_smoothed); |
| } |
| } |
| |
| /* report smoothed one to ranking database */ |
| atten = ni->ni_atten_smoothed; |
| if (SCS_ATTEN_VALID(atten)) { |
| as->as_sta_atten_num++; |
| as->as_sta_atten_sum += atten; |
| if ((as->as_sta_atten_min == SCS_ATTEN_UNINITED) || (atten < as->as_sta_atten_min)) { |
| as->as_sta_atten_min = atten; |
| } |
| if ((as->as_sta_atten_max == SCS_ATTEN_UNINITED) || (atten > as->as_sta_atten_max)) { |
| as->as_sta_atten_max = atten; |
| } |
| } |
| } |
| |
| void ieee80211_scs_collect_node_atten(struct ieee80211com *ic) |
| { |
| struct ap_state *as = ic->ic_scan->ss_scs_priv; |
| |
| /* clear the attenuation summary stats before collecting from nodes */ |
| as->as_sta_atten_num = 0; |
| as->as_sta_atten_sum = 0; |
| as->as_sta_atten_min = SCS_ATTEN_UNINITED; |
| as->as_sta_atten_max = SCS_ATTEN_UNINITED; |
| |
| /* let all node update their own information into the ranking stats database */ |
| ic->ic_iterate_nodes(&ic->ic_sta, ieee80211_scs_node_update_rssi, (void *)ic, 1); |
| SCSDBG(SCSLOG_INFO, "atten info: num=%d, sum=%d, min=%d, max=%d, avg=%d\n", |
| as->as_sta_atten_num, as->as_sta_atten_sum, |
| as->as_sta_atten_min, as->as_sta_atten_max, |
| as->as_sta_atten_num ? (as->as_sta_atten_sum / as->as_sta_atten_num) : 0); |
| } |
| |
| #define SCS_MIN_TX_TIME_FOR_COMP 10 /* ms */ |
| #define SCS_MIN_RX_TIME_FOR_COMP 10 /* ms */ |
| #define SCS_MIN_TDLS_TIME_FOR_COMP 10 /* ms */ |
| #define SCS_TX_TIME_COMP_STEP 50 /* ms */ |
| #define SCS_RX_TIME_COMP_STEP 50 /* ms */ |
| #define SCS_TDLS_TIME_COMP_STEP 50 /* ms */ |
| |
| #define SCS_RX_COMPENSTATION 0 |
| #define SCS_TX_COMPENSTATION 1 |
| #define SCS_TDLS_COMPENSTATION 2 |
| |
| static uint32_t tx_time_compenstation[SCS_MAX_TXTIME_COMP_INDEX] = {30, 35, 40, 45, 50, 50, 50, 50}; |
| static uint32_t rx_time_compenstation[SCS_MAX_RXTIME_COMP_INDEX] = {30, 50, 70, 90, 100, 110, 120, 130}; |
| static uint32_t tdls_time_compenstation[SCS_MAX_RXTIME_COMP_INDEX] = {40, 70, 70, 80, 80, 90, 90, 90}; |
| |
| static void ieee80211_scs_set_time_compensation(uint32_t type, uint32_t index, uint32_t comp) |
| { |
| int i; |
| |
| if (type == SCS_RX_COMPENSTATION) { |
| if (index >= SCS_MAX_RXTIME_COMP_INDEX) { |
| printk("SCS: The index(%u) for rxtime compensation is not correct!\n", index); |
| } else { |
| rx_time_compenstation[index] = comp; |
| } |
| |
| printk("Current rx time compensation:\n"); |
| for (i = 0; i < SCS_MAX_RXTIME_COMP_INDEX; i++) { |
| printk(" %u", rx_time_compenstation[i]); |
| } |
| printk("\n"); |
| |
| } else if (type == SCS_TX_COMPENSTATION) { |
| if (index >= SCS_MAX_TXTIME_COMP_INDEX) { |
| printk("SCS: The index(%u) for txtime compensation is not correct!\n", index); |
| } else { |
| tx_time_compenstation[index] = comp; |
| } |
| |
| printk("Current tx time compensation:\n"); |
| for (i = 0; i < SCS_MAX_TXTIME_COMP_INDEX; i++) { |
| printk(" %u", tx_time_compenstation[i]); |
| } |
| printk("\n"); |
| } else if (type == SCS_TDLS_COMPENSTATION) { |
| if (index >= SCS_MAX_TDLSTIME_COMP_INDEX) { |
| printk("SCS: The index(%u) for tdlstime compensation is not correct!\n", index); |
| } else { |
| tdls_time_compenstation[index] = comp; |
| } |
| |
| printk("Current tdls time compensation:\n"); |
| for (i = 0; i < SCS_MAX_TDLSTIME_COMP_INDEX; i++) { |
| printk(" %u", tdls_time_compenstation[i]); |
| } |
| printk("\n"); |
| } |
| } |
| |
| /* Add some compensation because (tx_time+rx_time) always is less than the caused cca interference */ |
| static uint32_t ieee80211_scs_get_time_compensation(uint32_t tx_time, uint32_t rx_time) |
| { |
| uint32_t rx_comp, tx_comp; |
| uint32_t index; |
| |
| if (rx_time > SCS_MIN_RX_TIME_FOR_COMP) { |
| index = rx_time / SCS_RX_TIME_COMP_STEP; |
| index = (index >= SCS_MAX_RXTIME_COMP_INDEX) ? (SCS_MAX_RXTIME_COMP_INDEX - 1) : index; |
| rx_comp = rx_time_compenstation[index]; |
| } else { |
| /* if only downstream traffic or no traffic, don't add compensation */ |
| return 0; |
| } |
| |
| if (tx_time > SCS_MIN_TX_TIME_FOR_COMP) { |
| index = tx_time / SCS_TX_TIME_COMP_STEP; |
| index = (index >= SCS_MAX_TXTIME_COMP_INDEX) ? (SCS_MAX_TXTIME_COMP_INDEX - 1) : index; |
| tx_comp = tx_time_compenstation[index]; |
| } else { |
| tx_comp = 0; |
| } |
| |
| return (tx_comp + rx_comp); |
| } |
| |
| #define SCS_MIN_STATS_FOR_STABLE_CHECK 30 |
| static uint32_t ieee80211_scs_is_stats_unstable(struct ieee80211com *ic, |
| uint32_t last_stats, uint32_t new_stats) |
| { |
| uint32_t diff, sum; |
| |
| if (last_stats < SCS_MIN_STATS_FOR_STABLE_CHECK && new_stats < SCS_MIN_STATS_FOR_STABLE_CHECK) { |
| return 0; |
| } |
| |
| diff = (last_stats > new_stats) ? (last_stats - new_stats) : (new_stats - last_stats); |
| sum = last_stats + new_stats; |
| |
| if (diff > ic->ic_scs.scs_pmp_stats_stable_range) { |
| return 1; |
| } |
| |
| if ((diff * 100 / sum) > ic->ic_scs.scs_pmp_stats_stable_percent) { |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| static void ieee80211_scs_clear_node_smooth_data(struct ieee80211com *ic) |
| { |
| struct ieee80211_node *ni; |
| struct ieee80211_node_table *nt = &ic->ic_sta; |
| |
| if (ic->ic_opmode != IEEE80211_M_HOSTAP) |
| return; |
| |
| IEEE80211_NODE_LOCK_IRQ(nt); |
| TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { |
| if (!ieee80211_scs_node_is_valid(ni)) |
| continue; |
| |
| ni->ni_recent_cca_intf_smthed = 0; |
| ni->ni_others_rx_time_smthed = 0; |
| ni->ni_others_tx_time_smthed = 0; |
| ni->ni_tdls_tx_time_smthed = 0; |
| ni->ni_tdls_rx_time_smthed = 0; |
| } |
| IEEE80211_NODE_UNLOCK_IRQ(nt); |
| } |
| |
| static uint32_t ieee80211_scs_tdls_link_is_existing(struct ieee80211com *ic, |
| struct ieee80211_node *node) |
| { |
| int i; |
| unsigned long flags; |
| struct ieee80211_tdls_scs_entry *scs_entry = NULL; |
| |
| spin_lock_irqsave(&ic->ic_scs.scs_tdls_lock, flags); |
| for (i = 0; i < IEEE80211_NODE_HASHSIZE; i++) { |
| LIST_FOREACH(scs_entry, &ic->ic_scs.scs_tdls_list[i], entry) { |
| if (scs_entry && scs_entry->stats.is_latest) { |
| if (!node || IEEE80211_ADDR_EQ(scs_entry->stats.s_addr, |
| node->ni_macaddr) || |
| IEEE80211_ADDR_EQ(scs_entry->stats.r_addr, |
| node->ni_macaddr)) |
| return 1; |
| } |
| } |
| } |
| spin_unlock_irqrestore(&ic->ic_scs.scs_tdls_lock, flags); |
| |
| return 0; |
| } |
| |
| static uint16_t ieee80211_scs_ap_get_tdls_link_time(struct ieee80211com *ic) |
| { |
| struct ieee80211_node_table *nt = &ic->ic_sta; |
| struct ieee80211_node *ni; |
| struct ieee80211_node *ni_tmp; |
| uint16_t ap_tdls_time = 0; |
| uint16_t tdls_comp; |
| uint16_t index; |
| |
| TAILQ_FOREACH_SAFE(ni, &nt->nt_node, ni_list, ni_tmp) |
| ap_tdls_time += ni->ni_tdls_tx_time_smthed; |
| |
| /* calculate compensation */ |
| if (ap_tdls_time > SCS_MIN_TDLS_TIME_FOR_COMP) { |
| index = ap_tdls_time / SCS_TDLS_TIME_COMP_STEP; |
| index = (index >= SCS_MAX_TDLSTIME_COMP_INDEX) ? |
| (SCS_MAX_TDLSTIME_COMP_INDEX - 1) : index; |
| tdls_comp = tdls_time_compenstation[index]; |
| } else { |
| tdls_comp = 0; |
| } |
| |
| return (ap_tdls_time + tdls_comp); |
| } |
| |
| static uint32_t ieee80211_scs_smooth_ap_cca_intf_time(struct ieee80211com *ic, |
| uint32_t raw_cca_intf, uint32_t *stats_unstable) |
| { |
| struct ap_state *as = ic->ic_scan->ss_scs_priv; |
| struct ieee80211_node_table *nt = &ic->ic_sta; |
| struct ieee80211_node *ni; |
| uint32_t ap_tdls_intf; |
| uint32_t corrective_cca_intf; |
| uint32_t compound_cca_intf; |
| |
| if ((jiffies - ic->ic_scs.scs_cca_intf_smthed_jiffies) > |
| (ic->ic_scs.scs_pmp_stats_clear_interval * HZ)) { |
| as->as_tx_ms_smth = 0; |
| as->as_rx_ms_smth = 0; |
| ic->ic_scs.scs_cca_intf_smthed = 0; |
| ieee80211_scs_clear_node_smooth_data(ic); |
| } |
| |
| *stats_unstable = ieee80211_scs_is_stats_unstable(ic, |
| ic->ic_scs.scs_cca_intf_smthed, raw_cca_intf); |
| *stats_unstable |= ((jiffies - ic->ic_scs.scs_cca_intf_smthed_jiffies) > |
| (5 * ic->ic_scs.scs_cca_sample_dur * HZ)); |
| |
| IEEE80211_NODE_LOCK_IRQ(nt); |
| TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { |
| ni->ni_tdls_rx_time_smthed = IEEE80211_SCS_SMOOTH(ni->ni_tdls_rx_time_smthed, |
| ni->ni_recent_tdls_rx_time, ic->ic_scs.scs_pmp_rx_time_smth_fctr); |
| ni->ni_tdls_tx_time_smthed = IEEE80211_SCS_SMOOTH(ni->ni_tdls_tx_time_smthed, |
| ni->ni_recent_tdls_tx_time, ic->ic_scs.scs_pmp_tx_time_smth_fctr); |
| ni->ni_tdls_time_smth_jiffies = jiffies; |
| SCSDBG(SCSLOG_INFO, "STA 0x%x - tdls_tx_time_smth: %u, tdls_rx_time_smth=%u\n", |
| ni->ni_associd, ni->ni_tdls_tx_time_smthed, ni->ni_tdls_rx_time_smthed); |
| } |
| IEEE80211_NODE_UNLOCK_IRQ(nt); |
| |
| ic->ic_scs.scs_cca_intf_smthed = IEEE80211_SCS_SMOOTH(ic->ic_scs.scs_cca_intf_smthed, |
| raw_cca_intf, ic->ic_scs.scs_pmp_rpt_cca_smth_fctr); |
| ic->ic_scs.scs_cca_intf_smthed_jiffies = jiffies; |
| |
| ap_tdls_intf = ieee80211_scs_ap_get_tdls_link_time(ic); |
| if (ic->ic_scs.scs_cca_intf_smthed > ap_tdls_intf) |
| corrective_cca_intf = ic->ic_scs.scs_cca_intf_smthed - ap_tdls_intf; |
| else |
| corrective_cca_intf = 0; |
| |
| compound_cca_intf = ieee80211_scs_fix_cca_intf(ic, NULL, corrective_cca_intf, |
| ic->ic_scs.scs_sp_err_smthed, ic->ic_scs.scs_lp_err_smthed); |
| |
| SCSDBG(SCSLOG_INFO, "raw_cca_int %u, cca_int_smthed %u, ap_tdls_intf %u," |
| " corrective_cca_intf %u, compound_cca_intf %u, stats_unstable %u\n", |
| raw_cca_intf, ic->ic_scs.scs_cca_intf_smthed, ap_tdls_intf, |
| corrective_cca_intf, compound_cca_intf, *stats_unstable); |
| |
| return compound_cca_intf; |
| } |
| |
| static uint32_t ieee80211_scs_smooth_sta_cca_intf_time(struct ieee80211com *ic, |
| struct ieee80211_node *ni, uint32_t total_tx_time, uint32_t total_rx_time, |
| uint32_t node_tx_time, uint32_t node_rx_time) |
| { |
| uint32_t others_tx_time, others_rx_time; |
| uint32_t others_time, others_time_smth; |
| uint32_t is_stats_unstable = 0; |
| int32_t node_cca_intf; |
| uint32_t node_cca_idle; |
| |
| /* get other nodes' tx/rx time and smooth them */ |
| others_tx_time = (total_tx_time > node_tx_time) ? (total_tx_time - node_tx_time) : 0; |
| others_rx_time = (total_rx_time > node_rx_time) ? (total_rx_time - node_rx_time) : 0; |
| others_time = others_rx_time + others_tx_time; |
| others_time_smth = ni->ni_others_rx_time_smthed + ni->ni_others_tx_time_smthed; /* old smooth value */ |
| if (ieee80211_scs_is_stats_unstable(ic, others_time_smth, others_time)) |
| is_stats_unstable |= IEEE80211_SCS_UNSTABLE_OTHERSTIME; |
| if ((jiffies - ni->ni_others_time_smth_jiffies) > (5 * ic->ic_scs.scs_cca_sample_dur * HZ)) |
| is_stats_unstable |= IEEE80211_SCS_UNSTABLE_OTHERSTIME_OUTDATED; |
| |
| SCSDBG(SCSLOG_NOTICE, "node 0x%x time before smth -- self:(tx:%u, rx:%u), others:%u(tx:%u, rx:%u)," |
| "others_smth:%u(tx:%u, rx:%u), tdls:(tx:%u, rx:%u), tdls_smth:(tx:%u, rx:%u)\n", |
| ni->ni_associd, node_tx_time, node_rx_time, others_time, others_tx_time, others_rx_time, |
| others_time_smth, ni->ni_others_tx_time_smthed, ni->ni_others_rx_time_smthed, |
| ni->ni_recent_tdls_tx_time, ni->ni_recent_tdls_rx_time, |
| ni->ni_tdls_tx_time_smthed, ni->ni_tdls_rx_time_smthed); |
| |
| ni->ni_others_rx_time_smthed = IEEE80211_SCS_SMOOTH(ni->ni_others_rx_time_smthed, others_rx_time, |
| ic->ic_scs.scs_pmp_rx_time_smth_fctr); |
| ni->ni_others_tx_time_smthed = IEEE80211_SCS_SMOOTH(ni->ni_others_tx_time_smthed, others_tx_time, |
| ic->ic_scs.scs_pmp_tx_time_smth_fctr); |
| ni->ni_others_time_smth_jiffies = jiffies; |
| |
| if ((ic->ic_opmode == IEEE80211_M_HOSTAP) && ieee80211_scs_tdls_link_is_existing(ic, ni)) { |
| if (ieee80211_scs_is_stats_unstable(ic, |
| ni->ni_tdls_rx_time_smthed, |
| ni->ni_recent_tdls_rx_time)) |
| is_stats_unstable |= IEEE80211_SCS_UNSTABLE_TDLS_RX; |
| if (ieee80211_scs_is_stats_unstable(ic, |
| ni->ni_tdls_tx_time_smthed, |
| ni->ni_recent_tdls_tx_time)) |
| is_stats_unstable |= IEEE80211_SCS_UNSTABLE_TDLS_TX; |
| if ((jiffies - ni->ni_tdls_time_smth_jiffies) > |
| (5 * ic->ic_scs.scs_cca_sample_dur * HZ)) |
| is_stats_unstable |= IEEE80211_SCS_UNSTABLE_TDLS_OUTDATED; |
| } |
| |
| /* smooth cca interference */ |
| node_cca_intf = ni->ni_recent_cca_intf; |
| node_cca_idle = SCS_CCA_IDLE_INVALID != ni->ni_recent_cca_idle ? ni->ni_recent_cca_idle_smthed : ic->ic_scs.scs_cca_idle_smthed; |
| if ((jiffies - ni->ni_recent_cca_intf_jiffies) < (ic->ic_scs.scs_cca_sample_dur * HZ) && |
| node_cca_intf != SCS_CCA_INTF_INVALID) { |
| SCSDBG(SCSLOG_NOTICE, "node 0x%x cca intf before smth -- smthed:%u, recent_cca_intf:%u\n", |
| ni->ni_associd, ni->ni_recent_cca_intf_smthed, node_cca_intf); |
| |
| if (ieee80211_scs_is_stats_unstable(ic, ni->ni_recent_cca_intf_smthed, node_cca_intf)) |
| is_stats_unstable |= IEEE80211_SCS_UNSTABLE_INTF; |
| if ((jiffies - ni->ni_cca_intf_smth_jiffies) > (5 * ic->ic_scs.scs_cca_sample_dur * HZ)) |
| is_stats_unstable |= IEEE80211_SCS_UNSTABLE_INTF_OUTDATED; |
| |
| ni->ni_recent_cca_intf_smthed = IEEE80211_SCS_SMOOTH(ni->ni_recent_cca_intf_smthed, node_cca_intf, |
| ic->ic_scs.scs_pmp_rpt_cca_smth_fctr); |
| ni->ni_cca_intf_smth_jiffies = jiffies; |
| |
| if (SCS_CCA_IDLE_INVALID == ni->ni_recent_cca_idle) |
| return is_stats_unstable; |
| |
| if (ieee80211_scs_is_stats_unstable(ic, ni->ni_recent_cca_idle_smthed, node_cca_idle)) |
| is_stats_unstable |= IEEE80211_SCS_UNSTABLE_IDLE; |
| if ((jiffies - ni->ni_recent_cca_idle_smth_jiffies) > (5 * ic->ic_scs.scs_cca_sample_dur * HZ)) |
| is_stats_unstable |= IEEE80211_SCS_UNSTABLE_IDLE_OUTDATED; |
| |
| ni->ni_recent_cca_idle_smthed = IEEE80211_SCS_SMOOTH(ni->ni_recent_cca_idle_smthed, node_cca_idle, |
| ic->ic_scs.scs_pmp_rpt_cca_smth_fctr); |
| ni->ni_recent_cca_idle_smth_jiffies = jiffies; |
| } else { |
| is_stats_unstable |= IEEE80211_SCS_UNSTABLE_INTF_INVALID; |
| } |
| |
| return is_stats_unstable; |
| } |
| |
| #define ADD_SCS_TDLS_STATS(frm, s_addr, r_addr, tx_time, is_latest) \ |
| do { \ |
| IEEE80211_ADDR_COPY(frm, s_addr); \ |
| frm += IEEE80211_ADDR_LEN; \ |
| IEEE80211_ADDR_COPY(frm, r_addr); \ |
| frm += IEEE80211_ADDR_LEN; \ |
| ADDINT16LE(frm, tx_time); \ |
| ADDINT16LE(frm, is_latest); \ |
| } while(0) |
| |
| static int ieee80211_scs_add_tdls_stats_ie(struct ieee80211vap *vap, |
| struct qtn_scs_info *scs_info_read, uint8_t *frm, uint16_t frm_len) |
| { |
| #define IEEE80211_SCS_TDLS_TRAINING_DUATION 4 |
| #define IEEE80211_SCS_TDLS_TRAINING_COMPANSATION 20 |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_node_table *nt = &ic->ic_sta; |
| struct ieee80211_node *ni; |
| struct qtn_scs_vsp_node_stats *stats; |
| uint8_t *end = frm + frm_len; |
| int tdls_tx_time = 0; |
| int ie_len = 0; |
| |
| if (vap->iv_opmode != IEEE80211_M_STA || (vap->iv_flags_ext & IEEE80211_FEXT_TDLS_DISABLED)) |
| return ie_len; |
| |
| TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { |
| if (!ieee80211_scs_node_is_valid(ni)) |
| continue; |
| |
| if (IEEE80211_NODE_IS_NONE_TDLS(ni) || IEEE80211_NODE_IS_TDLS_INACTIVE(ni)) |
| continue; |
| |
| stats = ieee80211_scs_find_node_stats(ic, scs_info_read, ni->ni_associd); |
| if (stats) |
| tdls_tx_time = stats->tx_usecs / 1000; |
| else |
| tdls_tx_time = 0; |
| |
| /* |
| * Add some compansation since training packets could |
| * cause high interference in bad environment |
| */ |
| if (time_after(jiffies, ni->ni_training_start) && |
| time_before(jiffies, ni->ni_training_start + |
| IEEE80211_SCS_TDLS_TRAINING_DUATION * HZ)) |
| tdls_tx_time += IEEE80211_SCS_TDLS_TRAINING_COMPANSATION; |
| |
| if (frm < end) { |
| ADD_SCS_TDLS_STATS(frm, vap->iv_myaddr, ni->ni_macaddr, tdls_tx_time, 1); |
| ie_len += sizeof(struct ieee80211_tdls_scs_stats); |
| SCSDBG(SCSLOG_NOTICE, "Add SCS TDLS status: sender_mac %pM " |
| "receiver_mac %pM tx_time %u\n",vap->iv_myaddr, |
| ni->ni_macaddr, tdls_tx_time); |
| } else { |
| SCSDBG(SCSLOG_NOTICE, "Failed to add tdls stats IE\n"); |
| } |
| } |
| |
| return ie_len; |
| } |
| |
| void ieee80211_scs_update_tdls_stats(struct ieee80211com *ic, |
| struct ieee80211_tdls_scs_stats *scs_stats) |
| { |
| int found = 0; |
| unsigned long flags; |
| struct ieee80211_tdls_scs_entry *scs_entry = NULL; |
| int hash = IEEE80211_NODE_HASH(scs_stats->s_addr); |
| |
| SCSDBG(SCSLOG_INFO, "Update SCS TDLS status: sender_mac %pM " |
| "receiver_mac %pM tx_time %u\n", scs_stats->s_addr, |
| scs_stats->r_addr, le16toh(scs_stats->tx_time)); |
| |
| spin_lock_irqsave(&ic->ic_scs.scs_tdls_lock, flags); |
| LIST_FOREACH(scs_entry, &ic->ic_scs.scs_tdls_list[hash], entry) { |
| if (IEEE80211_ADDR_EQ(scs_entry->stats.s_addr, scs_stats->s_addr) && |
| IEEE80211_ADDR_EQ(scs_entry->stats.r_addr, scs_stats->r_addr)) { |
| scs_entry->stats.is_latest = 1; |
| scs_entry->stats.tx_time = le16toh(scs_stats->tx_time); |
| found = 1; |
| break; |
| } |
| } |
| spin_unlock_irqrestore(&ic->ic_scs.scs_tdls_lock, flags); |
| |
| if (found == 0) { |
| MALLOC(scs_entry, struct ieee80211_tdls_scs_entry *, |
| sizeof(*scs_entry), M_DEVBUF, M_WAITOK); |
| if (scs_entry != NULL) { |
| IEEE80211_ADDR_COPY(scs_entry->stats.s_addr, scs_stats->s_addr); |
| IEEE80211_ADDR_COPY(scs_entry->stats.r_addr, scs_stats->r_addr); |
| scs_entry->stats.is_latest = 1; |
| scs_entry->stats.tx_time = le16toh(scs_stats->tx_time); |
| spin_lock_irqsave(&ic->ic_scs.scs_tdls_lock, flags); |
| LIST_INSERT_HEAD(&ic->ic_scs.scs_tdls_list[hash], scs_entry, entry); |
| spin_unlock_irqrestore(&ic->ic_scs.scs_tdls_lock, flags); |
| } else { |
| SCSDBG(SCSLOG_NOTICE, "SCS TDLS entry allocation failed\n"); |
| } |
| } |
| } |
| |
| void ieee80211_scs_free_node_tdls_stats(struct ieee80211com *ic, |
| struct ieee80211_node *ni) |
| { |
| int i; |
| int freed = 0; |
| unsigned long flags; |
| struct ieee80211_tdls_scs_entry *scs_entry; |
| struct ieee80211_tdls_scs_entry *temp_entry; |
| |
| spin_lock_irqsave(&ic->ic_scs.scs_tdls_lock, flags); |
| for (i = 0; i < IEEE80211_NODE_HASHSIZE; i++) { |
| LIST_FOREACH_SAFE(scs_entry, &ic->ic_scs.scs_tdls_list[i], entry, temp_entry) { |
| if ((IEEE80211_ADDR_EQ(scs_entry->stats.s_addr, ni->ni_macaddr) || |
| IEEE80211_ADDR_EQ(scs_entry->stats.r_addr, ni->ni_macaddr))) { |
| LIST_REMOVE(scs_entry, entry); |
| FREE(scs_entry, M_DEVBUF); |
| freed = 1; |
| } |
| } |
| } |
| spin_unlock_irqrestore(&ic->ic_scs.scs_tdls_lock, flags); |
| if (freed) |
| SCSDBG(SCSLOG_NOTICE, "free node %pM tdls stats\n", ni->ni_macaddr); |
| } |
| |
| void ieee80211_scs_free_tdls_stats_list(struct ieee80211com *ic) |
| { |
| int i; |
| unsigned long flags; |
| struct ieee80211_tdls_scs_entry *scs_entry; |
| struct ieee80211_tdls_scs_entry *temp_entry; |
| |
| SCSDBG(SCSLOG_NOTICE, "SCS: free all of tdls stats\n"); |
| spin_lock_irqsave(&ic->ic_scs.scs_tdls_lock, flags); |
| for (i = 0; i < IEEE80211_NODE_HASHSIZE; i++) { |
| LIST_FOREACH_SAFE(scs_entry, &ic->ic_scs.scs_tdls_list[i], entry, temp_entry) { |
| LIST_REMOVE(scs_entry, entry); |
| FREE(scs_entry, M_DEVBUF); |
| } |
| } |
| spin_unlock_irqrestore(&ic->ic_scs.scs_tdls_lock, flags); |
| } |
| |
| static void ieee80211_scs_dump_tdls_stats(struct ieee80211com *ic) |
| { |
| int i; |
| unsigned long flags; |
| struct ieee80211_tdls_scs_entry *scs_entry = NULL; |
| |
| SCSDBG(SCSLOG_INFO, "Dump SCS tdls stats:\n"); |
| spin_lock_irqsave(&ic->ic_scs.scs_tdls_lock, flags); |
| for (i = 0; i < IEEE80211_NODE_HASHSIZE; i++) { |
| LIST_FOREACH(scs_entry, &ic->ic_scs.scs_tdls_list[i], entry) { |
| if (scs_entry != NULL) { |
| SCSDBG(SCSLOG_VERBOSE, "sender_mac %pM, receiver_mac %pM," |
| " tx_time: %u\n", scs_entry->stats.s_addr, |
| scs_entry->stats.r_addr, scs_entry->stats.tx_time); |
| } |
| } |
| } |
| spin_unlock_irqrestore(&ic->ic_scs.scs_tdls_lock, flags); |
| } |
| |
| static void ieee80211_scs_update_current_tdls_time(struct ieee80211com *ic, |
| struct ieee80211_node *ni) |
| { |
| struct ieee80211_tdls_scs_entry *scs_entry = NULL; |
| int hash = IEEE80211_NODE_HASH(ni->ni_macaddr); |
| unsigned long flags; |
| int i; |
| |
| ni->ni_recent_tdls_tx_time = 0; |
| ni->ni_recent_tdls_rx_time = 0; |
| |
| spin_lock_irqsave(&ic->ic_scs.scs_tdls_lock, flags); |
| LIST_FOREACH(scs_entry, &ic->ic_scs.scs_tdls_list[hash], entry) { |
| if (IEEE80211_ADDR_EQ(scs_entry->stats.s_addr, ni->ni_macaddr)) { |
| ni->ni_recent_tdls_tx_time += scs_entry->stats.tx_time; |
| SCSDBG(SCSLOG_VERBOSE, "Node %pM tdls_tx_time: sender_mac %pM," |
| " receiver_mac %pM, tx_time: %u\n", ni->ni_macaddr, |
| scs_entry->stats.s_addr, scs_entry->stats.r_addr, |
| scs_entry->stats.tx_time); |
| } |
| } |
| |
| for (i = 0; i < IEEE80211_NODE_HASHSIZE; i++) { |
| LIST_FOREACH(scs_entry, &ic->ic_scs.scs_tdls_list[i], entry) { |
| if (IEEE80211_ADDR_EQ(scs_entry->stats.r_addr, ni->ni_macaddr)) { |
| ni->ni_recent_tdls_rx_time += scs_entry->stats.tx_time; |
| SCSDBG(SCSLOG_VERBOSE, "Node %pM tdls_rx_time: sender_mac %pM," |
| " receiver_mac %pM, rx_time: %u\n", ni->ni_macaddr, |
| scs_entry->stats.s_addr, scs_entry->stats.r_addr, |
| scs_entry->stats.tx_time); |
| } |
| } |
| } |
| spin_unlock_irqrestore(&ic->ic_scs.scs_tdls_lock, flags); |
| |
| SCSDBG(SCSLOG_INFO, "Node %pM, recent_tdls_tx_time %u, recent_tdls_rx_time %u\n", |
| ni->ni_macaddr, ni->ni_recent_tdls_tx_time, ni->ni_recent_tdls_rx_time); |
| } |
| |
| static int ieee80211_scs_update_tdls_link_time(struct ieee80211com *ic) |
| { |
| struct ieee80211_node *ni; |
| struct ieee80211_node_table *nt = &ic->ic_sta; |
| int failed = 0; |
| |
| if (ic->ic_opmode != IEEE80211_M_HOSTAP) |
| return failed; |
| |
| ieee80211_scs_dump_tdls_stats(ic); |
| |
| IEEE80211_NODE_LOCK_IRQ(nt); |
| TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { |
| if (ni->ni_vap->iv_opmode == IEEE80211_M_WDS) |
| continue; |
| |
| if (!ieee80211_scs_node_is_valid(ni)) |
| continue; |
| |
| /* |
| * We must confirm all of CCA actions are received and all of CCA info are correct, |
| * or we will get wrong total TDLS link time and calculate wrong CCA interference |
| */ |
| if (ni->ni_recent_cca_intf == SCS_CCA_INTF_INVALID) { |
| failed = 1; |
| SCSDBG(SCSLOG_NOTICE, "Get wrong CCA info from STA 0x%x\n", ni->ni_associd); |
| break; |
| } |
| |
| ieee80211_scs_update_current_tdls_time(ic, ni); |
| } |
| IEEE80211_NODE_UNLOCK_IRQ(nt); |
| |
| return failed; |
| } |
| |
| static uint16_t ieee80211_scs_sta_get_tdls_link_time(struct ieee80211com *ic, |
| struct ieee80211_node *cur_ni) |
| { |
| struct ieee80211_node_table *nt = &ic->ic_sta;; |
| struct ieee80211_node *ni; |
| uint16_t sta_tdls_time = 0; |
| uint16_t tdls_comp; |
| uint16_t index; |
| |
| if (ic->ic_opmode != IEEE80211_M_HOSTAP) |
| return 0; |
| |
| TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { |
| if (ni != cur_ni) |
| sta_tdls_time += ni->ni_tdls_tx_time_smthed; |
| } |
| |
| sta_tdls_time = (sta_tdls_time > cur_ni->ni_tdls_rx_time_smthed) ? |
| (sta_tdls_time - cur_ni->ni_tdls_rx_time_smthed) : 0; |
| |
| /* Add some compensation */ |
| if (sta_tdls_time > SCS_MIN_TDLS_TIME_FOR_COMP) { |
| index = sta_tdls_time / SCS_TDLS_TIME_COMP_STEP; |
| index = (index >= SCS_MAX_TDLSTIME_COMP_INDEX) ? |
| (SCS_MAX_TDLSTIME_COMP_INDEX - 1) : index; |
| tdls_comp = tdls_time_compenstation[index]; |
| } else { |
| tdls_comp = 0; |
| } |
| |
| return (sta_tdls_time + tdls_comp); |
| } |
| |
| int ieee80211_scs_is_wds_rbs_node(struct ieee80211com *ic) |
| { |
| struct ieee80211vap *vap; |
| int is_wds_rbs = 0; |
| |
| TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { |
| if (IEEE80211_VAP_WDS_IS_RBS(vap)) { |
| SCSDBG(SCSLOG_INFO, "channel change is disabled under RBS WDS mode\n"); |
| is_wds_rbs = 1; |
| break; |
| } |
| } |
| return is_wds_rbs; |
| } |
| EXPORT_SYMBOL(ieee80211_scs_is_wds_rbs_node); |
| |
| static int |
| ieee80211_scs_is_wds_mbs_node(struct ieee80211com *ic) |
| { |
| struct ieee80211vap *vap; |
| int is_wds_mbs = 0; |
| |
| TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { |
| if (IEEE80211_VAP_WDS_IS_MBS(vap)) { |
| is_wds_mbs = 1; |
| break; |
| } |
| } |
| return is_wds_mbs; |
| } |
| |
| static int ieee80211_wds_vap_exists(struct ieee80211com *ic) |
| { |
| struct ieee80211vap *vap; |
| |
| TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { |
| if ((vap->iv_opmode == IEEE80211_M_WDS) && (vap->iv_state == IEEE80211_S_RUN)) { |
| return 1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static uint32_t |
| ieee80211_adjust_others_time_on_mbs(struct ieee80211com *ic, uint32_t compound_cca_intf) |
| { |
| struct ieee80211_node *ni; |
| struct ieee80211_node_table *nt = &ic->ic_sta; |
| struct ap_state *as = ic->ic_scan->ss_scs_priv; |
| |
| SCSDBG(SCSLOG_INFO, "compound_cca_intf before %u\n", compound_cca_intf); |
| as->as_cca_intf_smth = IEEE80211_SCS_SMOOTH(as->as_cca_intf_smth, compound_cca_intf, IEEE80211_SCS_SMTH_RBS_TIME); |
| if (as->as_cca_intf_smth > compound_cca_intf) { |
| /* Smooth rising up; but sharp drop */ |
| as->as_cca_intf_smth = compound_cca_intf; |
| } |
| SCSDBG(SCSLOG_INFO, "compound_cca_intf smoothed %u\n", as->as_cca_intf_smth); |
| compound_cca_intf = as->as_cca_intf_smth; |
| |
| TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { |
| if (!ieee80211_scs_node_is_valid(ni)) { |
| continue; |
| } |
| compound_cca_intf = (compound_cca_intf > ni->ni_recent_others_time_smth) ? |
| (compound_cca_intf - ni->ni_recent_others_time_smth) : 0; |
| |
| SCSDBG(SCSLOG_INFO, "compound_cca_intf %u others_time %u\n", compound_cca_intf, ni->ni_recent_others_time_smth); |
| } |
| SCSDBG(SCSLOG_INFO, "compound_cca_intf after %u\n", compound_cca_intf); |
| return compound_cca_intf; |
| } |
| |
| static uint32_t |
| ieee80211_adjust_node_rbs_others_time(struct ieee80211com *ic, uint32_t node_cca_intf) |
| { |
| struct ieee80211_node *ni; |
| struct ieee80211_node_table *nt = &ic->ic_sta; |
| |
| SCSDBG(SCSLOG_INFO, "node_cca_intf before %u\n", node_cca_intf); |
| |
| TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { |
| if (!ieee80211_scs_node_is_valid(ni)) { |
| continue; |
| } |
| node_cca_intf = (node_cca_intf > ni->ni_recent_others_time_smth) ? |
| (node_cca_intf - ni->ni_recent_others_time_smth) : 0; |
| } |
| SCSDBG(SCSLOG_INFO, "node_cca_intf after %u\n", node_cca_intf); |
| return node_cca_intf; |
| } |
| |
| uint32_t ieee80211_scs_collect_ranking_stats(struct ieee80211com *ic, struct qtn_scs_info *scs_info_read, uint32_t cc_flag, uint32_t compound_cca_intf) |
| { |
| struct ieee80211_node *ni; |
| struct ieee80211_node_table *nt; |
| int cca_intf_is_recent = 0; |
| uint32_t node_num[SCS_NODE_TRAFFIC_TYPE_NUM][SCS_NODE_INTF_TYPE_NUM]; |
| uint32_t traffic_idx, intf_idx; |
| uint32_t intf_max; |
| struct ieee80211_node *intf_max_ni; |
| struct qtn_scs_vsp_node_stats *stats; |
| uint32_t node_time, node_tx_time, node_rx_time; |
| uint32_t others_time_smth, others_time_comp; |
| uint32_t total_tx_time, total_rx_time; |
| uint32_t is_stats_unstable; |
| uint32_t new_cc_flag = cc_flag; |
| int32_t node_cca_intf, ap_cca_intf, node_compound_cca_intf; |
| uint32_t pmbl_max, node_pmbl, ap_pmbl; |
| int cur_bw = ieee80211_get_bw(ic); |
| int is_wds_mbs = ieee80211_scs_is_wds_mbs_node(ic); |
| uint16_t ap_tdls_intf; |
| uint16_t sta_tdls_intf; |
| uint32_t node_cca_idle; |
| struct scs_chan_intf_params intf_params= {0}; |
| |
| ap_tdls_intf = ieee80211_scs_ap_get_tdls_link_time(ic); |
| ap_cca_intf = (ic->ic_scs.scs_cca_intf_smthed > ap_tdls_intf) ? |
| (ic->ic_scs.scs_cca_intf_smthed - ap_tdls_intf) : 0; |
| ap_pmbl = (ic->ic_scs.scs_sp_err_smthed * ic->ic_scs.scs_sp_wf + |
| ic->ic_scs.scs_lp_err_smthed * ic->ic_scs.scs_lp_wf) / 100; |
| |
| /* |
| * Currently only reset current channel's cca_intf, because other channel's cca_intf |
| * are updated more slowly. |
| */ |
| intf_params.chan = ic->ic_curchan; |
| intf_params.chan_bw = cur_bw; |
| intf_params.cca_intf = SCS_CCA_INTF_INVALID; |
| intf_params.pmbl_err = 0; |
| intf_params.cca_dur = IEEE80211_SCS_CCA_INTF_SCALE; |
| ieee80211_scs_update_chans_cca_intf(ic, &intf_params, IEEE80211_SCS_COCHAN, NULL); |
| |
| /* collect interference info from all stas */ |
| intf_max = SCS_CCA_INTF_INVALID; |
| pmbl_max = 0; |
| intf_max_ni = NULL; |
| memset(node_num, 0x0, sizeof(node_num)); |
| nt = &ic->ic_sta; |
| IEEE80211_SCAN_LOCK_IRQ(nt); |
| IEEE80211_NODE_LOCK_IRQ(nt); |
| |
| total_tx_time = scs_info_read->tx_usecs / 1000; |
| total_rx_time = scs_info_read->rx_usecs / 1000; |
| |
| TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { |
| if (!ieee80211_scs_node_is_valid(ni)) { |
| continue; |
| } |
| |
| /* traffic condition */ |
| stats = ieee80211_scs_find_node_stats(ic, scs_info_read, ni->ni_associd); |
| if (stats) { |
| node_tx_time = stats->tx_usecs / 1000; |
| node_rx_time = stats->rx_usecs / 1000; |
| node_time = node_tx_time + node_rx_time; |
| } else { |
| SCSDBG(SCSLOG_NOTICE, "no stats available for node 0x%x\n", ni->ni_associd); |
| node_tx_time = 0; |
| node_rx_time = 0; |
| node_time = 0; |
| } |
| node_time += ni->ni_recent_tdls_tx_time; |
| |
| is_stats_unstable = ieee80211_scs_smooth_sta_cca_intf_time(ic, ni, total_tx_time, total_rx_time, |
| node_tx_time, node_rx_time); |
| others_time_smth = ni->ni_others_rx_time_smthed + ni->ni_others_tx_time_smthed; /* new smooth value */ |
| others_time_comp = ieee80211_scs_get_time_compensation(ni->ni_others_tx_time_smthed, |
| ni->ni_others_rx_time_smthed); |
| others_time_smth = others_time_smth + others_time_comp; |
| ni->ni_others_time = others_time_smth; /* This is used to compensate for RBS */ |
| sta_tdls_intf = ieee80211_scs_sta_get_tdls_link_time(ic, ni); |
| |
| SCSDBG(SCSLOG_NOTICE, "node 0x%x time after smth and compensation -- others_smth:%u " |
| "(tx:%u, rx:%u, comp:%u), sta_tdls_intf: %u\n", ni->ni_associd, others_time_smth, |
| ni->ni_others_tx_time_smthed, ni->ni_others_rx_time_smthed, others_time_comp, sta_tdls_intf); |
| |
| traffic_idx = (node_time <= (ic->ic_scs.scs_thrshld_loaded * scs_info_read->cca_try / 1000)) ? |
| SCS_NODE_TRAFFIC_IDLE : SCS_NODE_TRAFFIC_LOADED; |
| /* inteference condition */ |
| node_cca_intf = (int32_t)ni->ni_recent_cca_intf; |
| node_cca_idle = SCS_CCA_IDLE_INVALID != ni->ni_recent_cca_idle ? ni->ni_recent_cca_idle_smthed : ic->ic_scs.scs_cca_idle_smthed; |
| node_pmbl = 0; |
| cca_intf_is_recent = (jiffies - ni->ni_recent_cca_intf_jiffies) < (ic->ic_scs.scs_cca_sample_dur * HZ); |
| intf_idx = ((node_cca_intf != SCS_CCA_INTF_INVALID) && (cca_intf_is_recent)) ? |
| SCS_NODE_INTFED : SCS_NODE_NOTINTFED; |
| |
| if (intf_idx == SCS_NODE_INTFED) { |
| /* vendor specific handle */ |
| if ((ni->ni_vendor == PEER_VENDOR_QTN) |
| || IEEE80211_VAP_WDS_IS_MBS(ni->ni_vap)) { |
| if (ic->ic_sta_assoc >= IEEE80211_MAX_STA_CCA_ENABLED) { |
| /* PMP case */ |
| if (is_stats_unstable) { |
| /* stats is unstable, ignore it */ |
| node_cca_intf = 0; |
| node_cca_idle = IEEE80211_SCS_CCA_INTF_SCALE; |
| } else { |
| /* remove the interference that come from other associated station */ |
| node_cca_intf = (ni->ni_recent_cca_intf_smthed > others_time_smth) ? |
| (ni->ni_recent_cca_intf_smthed - others_time_smth) : 0; |
| node_cca_intf = (node_cca_intf > sta_tdls_intf) ? (node_cca_intf - sta_tdls_intf) : 0; |
| node_cca_intf = MIN(node_cca_intf, IEEE80211_SCS_CCA_INTF_SCALE); |
| } |
| SCSDBG(SCSLOG_NOTICE, "node 0x%x -- cca_smth: %u, others_time_smth: %u," |
| "sta_tdls_inf: %u, diff: %d, node_cca_intf: %u, is_unstable: %#x\n", |
| ni->ni_associd, ni->ni_recent_cca_intf_smthed, others_time_smth, sta_tdls_intf, |
| (int)(ni->ni_recent_cca_intf_smthed - others_time_smth - sta_tdls_intf), |
| node_cca_intf, is_stats_unstable); |
| } |
| if (is_wds_mbs) { |
| /* Is this node a non-WDS node ? */ |
| if (!(IEEE80211_VAP_WDS_ANY(ni->ni_vap))) { |
| node_cca_intf = ieee80211_adjust_node_rbs_others_time(ic, node_cca_intf); |
| } else { |
| SCSDBG(SCSLOG_NOTICE, "WDS Node; No adjustment needed\n"); |
| } |
| } |
| node_compound_cca_intf = ieee80211_scs_fix_cca_intf(ic, ni, node_cca_intf, |
| ni->ni_recent_sp_fail, ni->ni_recent_lp_fail); |
| node_pmbl = (ni->ni_recent_sp_fail * ic->ic_scs.scs_sp_wf + |
| ni->ni_recent_lp_fail * ic->ic_scs.scs_lp_wf) / 100; |
| |
| if (ieee80211_scs_is_interference_over_thresholds(ic, |
| node_compound_cca_intf, node_cca_idle, node_pmbl)) { |
| if (!(cc_flag & IEEE80211_SCS_SELF_CCA_CC) && (node_cca_intf > ap_cca_intf)) { |
| SCSDBG(SCSLOG_NOTICE, "increase cca_intf for sta side ACI, sta cca_intf=%u," |
| " self cca_intf=%u\n", node_cca_intf, ap_cca_intf); |
| node_cca_intf = node_cca_intf * 2 - ap_cca_intf; |
| node_cca_intf = MIN(node_cca_intf, IEEE80211_SCS_CCA_INTF_SCALE); |
| node_compound_cca_intf = ieee80211_scs_fix_cca_intf(ic, ni, node_cca_intf, |
| ni->ni_recent_sp_fail, ni->ni_recent_lp_fail); |
| } |
| node_cca_intf = node_compound_cca_intf; |
| } else { |
| intf_idx = SCS_NODE_NOTINTFED; |
| node_pmbl = 0; /* don't record it */ |
| } |
| } else if (!(cc_flag & IEEE80211_SCS_SELF_CCA_CC) && (node_cca_intf > ap_cca_intf)) { |
| SCSDBG(SCSLOG_NOTICE, "increase cca_intf for sta side ACI, sta cca_intf=%u," |
| " self cca_intf=%u\n", node_cca_intf, ap_cca_intf); |
| node_cca_intf = node_cca_intf * 2 - ap_cca_intf; |
| node_cca_intf = MIN(node_cca_intf, IEEE80211_SCS_CCA_INTF_SCALE); |
| } |
| } |
| SCSDBG(SCSLOG_NOTICE, "node 0x%x: vendor=%d, traffic=%d(time=%u), intfed=%d, cca_intf=%d cca_idle=%d\n", |
| ni->ni_associd, ni->ni_vendor, traffic_idx, node_time, intf_idx, |
| (intf_idx == SCS_NODE_INTFED) ? node_cca_intf : -1, |
| (intf_idx == SCS_NODE_INTFED) ? node_cca_idle : -1); |
| node_num[traffic_idx][intf_idx]++; |
| if (intf_idx == SCS_NODE_INTFED) { |
| if ((node_cca_intf > intf_max) |
| || (intf_max == SCS_CCA_INTF_INVALID)) { |
| intf_max = node_cca_intf; |
| intf_max_ni = ni; |
| } |
| } |
| pmbl_max = MAX(node_pmbl, pmbl_max); |
| |
| ni->ni_recent_cca_intf = SCS_CCA_INTF_INVALID; |
| } |
| |
| /* Handle loaded and idle cases */ |
| SCSDBG(SCSLOG_NOTICE, "traffic-intf matrix: loaded_intfed=%d, loaded_notintfed=%d, " |
| "idle_intfed=%d, idle_notintfed=%d\n", |
| node_num[SCS_NODE_TRAFFIC_LOADED][SCS_NODE_INTFED], |
| node_num[SCS_NODE_TRAFFIC_LOADED][SCS_NODE_NOTINTFED], |
| node_num[SCS_NODE_TRAFFIC_IDLE][SCS_NODE_INTFED], |
| node_num[SCS_NODE_TRAFFIC_IDLE][SCS_NODE_NOTINTFED]); |
| if (node_num[SCS_NODE_TRAFFIC_IDLE][SCS_NODE_INTFED] && |
| !node_num[SCS_NODE_TRAFFIC_LOADED][SCS_NODE_INTFED] && |
| node_num[SCS_NODE_TRAFFIC_LOADED][SCS_NODE_NOTINTFED]) { |
| SCSDBG(SCSLOG_NOTICE, "discard intfed idle sta report because loaded sta is not intfed\n"); |
| |
| new_cc_flag &= ~(IEEE80211_SCS_STA_CCA_REQ_CC | IEEE80211_SCS_BRCM_STA_TRIGGER_CC); |
| } else if (node_num[SCS_NODE_TRAFFIC_IDLE][SCS_NODE_INTFED] + |
| node_num[SCS_NODE_TRAFFIC_LOADED][SCS_NODE_INTFED]) { |
| SCSDBG(SCSLOG_NOTICE, "stas are under interference\n"); |
| if (intf_max_ni) |
| memcpy(ic->ic_csw_mac, intf_max_ni->ni_macaddr, IEEE80211_ADDR_LEN); |
| memset(&intf_params, 0, sizeof(intf_params)); |
| intf_params.chan = ic->ic_curchan; |
| intf_params.chan_bw = cur_bw; |
| intf_params.cca_intf = intf_max; |
| intf_params.pmbl_err = pmbl_max; |
| intf_params.cca_dur = IEEE80211_SCS_CCA_INTF_SCALE; |
| ieee80211_scs_update_chans_cca_intf(ic, &intf_params, IEEE80211_SCS_COCHAN, intf_max_ni); |
| if (!(new_cc_flag & (IEEE80211_SCS_STA_CCA_REQ_CC | IEEE80211_SCS_BRCM_STA_TRIGGER_CC))) { |
| new_cc_flag |= IEEE80211_SCS_STA_CCA_REQ_CC; |
| } |
| } else { |
| new_cc_flag &= ~(IEEE80211_SCS_STA_CCA_REQ_CC | IEEE80211_SCS_BRCM_STA_TRIGGER_CC); |
| |
| SCSDBG(SCSLOG_NOTICE, "sta are free of interference\n"); |
| } |
| |
| IEEE80211_NODE_UNLOCK_IRQ(nt); |
| IEEE80211_SCAN_UNLOCK_IRQ(nt); |
| |
| /* Adjust self intf on mbs-wds */ |
| if (is_wds_mbs) { |
| compound_cca_intf = ieee80211_adjust_others_time_on_mbs(ic, compound_cca_intf); |
| } |
| |
| /* update self's */ |
| memset(&intf_params, 0, sizeof(intf_params)); |
| intf_params.chan = ic->ic_curchan; |
| intf_params.chan_bw = cur_bw; |
| intf_params.cca_intf = compound_cca_intf; |
| intf_params.pmbl_err = ap_pmbl; |
| intf_params.cca_dur = IEEE80211_SCS_CCA_INTF_SCALE; |
| intf_params.cca_pri = scs_info_read->cca_pri; |
| intf_params.cca_sec = scs_info_read->cca_sec20; |
| intf_params.cca_sec40 = scs_info_read->cca_sec40; |
| ieee80211_scs_update_chans_cca_intf(ic, &intf_params, IEEE80211_SCS_COCHAN, NULL); |
| |
| /* Recheck the interference detected by AP self */ |
| if (ieee80211_scs_is_interference_over_thresholds(ic, |
| compound_cca_intf, ic->ic_scs.scs_cca_idle_smthed, ap_pmbl)) { |
| new_cc_flag |= IEEE80211_SCS_SELF_CCA_CC; |
| SCSDBG(SCSLOG_NOTICE, "Recheck Self CCA intf, need to change channel" |
| " compound_cca_intf=%u, cca_idle_smth=%u, pmbl_err=%u\n", |
| compound_cca_intf, ic->ic_scs.scs_cca_idle_smthed, ap_pmbl); |
| } else { |
| new_cc_flag &= ~IEEE80211_SCS_SELF_CCA_CC; |
| SCSDBG(SCSLOG_NOTICE, "Recheck Self CCA intf, needn't to change channel" |
| " compound_cca_intf=%u, cca_idle_smth=%u, pmbl_err=%u\n", |
| compound_cca_intf, ic->ic_scs.scs_cca_idle_smthed, ap_pmbl); |
| } |
| |
| return new_cc_flag; |
| } |
| |
| /* |
| * Calculate the rate ratio based on attenuation |
| * return unit: percent |
| * If no attenuation available, return 100 |
| * For domains other than us, return 100 |
| * Otherwise, calculate rate ratio according to attenuation. |
| */ |
| #define SCS_RATE_RATIO_QTN 0 |
| #define SCS_RATE_RATIO_NONQTN 1 |
| #define SCS_RATE_RATIO_NUM 2 |
| #define SCS_RATE_RATIO_ENTRY_MAX 9 |
| #define SCS_RATE_RATIO_DEFAULT 100 |
| #define SCS_RATE_RATIO_MIN 0 |
| struct scs_rate_ratio_table { |
| int atten_min; |
| int atten_max; |
| int atten_intvl; |
| int entry_num; |
| uint8_t rate_ratios[SCS_RATE_RATIO_ENTRY_MAX]; // percent |
| } scs_rate_ratio_set[SCS_RATE_RATIO_NUM] = { |
| /* qtn */ |
| { |
| 76, |
| 104, |
| 4, |
| 9, |
| {95, 90, 80, 70, 60, 45, 30, 20, 0}, |
| }, |
| /* non-qtn, based on test of brcm sta */ |
| { |
| 74, |
| 98, |
| 6, |
| 6, |
| {95, 90, 80, 50, 30, 0}, |
| } |
| }; |
| uint8_t ieee80211_scs_calc_rate_ratio(struct ieee80211com *ic) |
| { |
| struct ap_state *as = ic->ic_scan->ss_scs_priv; |
| int32_t txpower = ic->ic_curchan->ic_maxpower; |
| int32_t attenuation; |
| int32_t idx = 0; |
| uint8_t rate_ratio; |
| int type = ic->ic_nonqtn_sta ? SCS_RATE_RATIO_NONQTN : SCS_RATE_RATIO_QTN; |
| |
| /* currently only use worst case */ |
| if (!SCS_ATTEN_VALID(as->as_sta_atten_max)) { |
| return SCS_RATE_RATIO_MIN; |
| } |
| attenuation = as->as_sta_atten_max; |
| SCSDBG(SCSLOG_NOTICE, "txpower=%d, raw attenuation=%d, attenuation adjust=%d, non_qtn_sta=%d\n", |
| txpower, attenuation, ic->ic_scs.scs_atten_adjust, ic->ic_nonqtn_sta); |
| attenuation += ic->ic_scs.scs_atten_adjust; |
| |
| if (attenuation <= scs_rate_ratio_set[type].atten_min) { |
| idx = 0; |
| } else if (attenuation > scs_rate_ratio_set[type].atten_max) { |
| idx = scs_rate_ratio_set[type].entry_num - 1; |
| } else { |
| idx = (attenuation - scs_rate_ratio_set[type].atten_min + |
| scs_rate_ratio_set[type].atten_intvl - 1) / scs_rate_ratio_set[type].atten_intvl; |
| } |
| rate_ratio = scs_rate_ratio_set[type].rate_ratios[idx]; |
| |
| SCSDBG(SCSLOG_INFO, "txpower=%d, attenuation=%d, rate ratio = %d\n", |
| txpower, attenuation, rate_ratio); |
| |
| return rate_ratio; |
| } |
| |
| int ieee80211_scs_aging(struct ieee80211com *ic, uint32_t thrshld) |
| { |
| struct ap_state *as = ic->ic_scan->ss_scs_priv; |
| int i; |
| int aged_num = 0; |
| uint32_t jiffies_diff = thrshld * 60 * HZ; |
| |
| if (thrshld == 0) |
| return 0; |
| |
| for (i = 1; i < IEEE80211_CHAN_MAX; i++) { |
| if (as->as_cca_intf[i] == SCS_CCA_INTF_INVALID) |
| continue; |
| |
| if ((jiffies - as->as_cca_intf_jiffies[i]) > jiffies_diff) { |
| SCSDBG(SCSLOG_NOTICE, "chan %d cca intf aged out(%u - %u > %u)\n", i, |
| (unsigned int)jiffies, as->as_cca_intf_jiffies[i], |
| jiffies_diff); |
| as->as_cca_intf[i] = SCS_CCA_INTF_INVALID; |
| as->as_cca_intf_jiffies[i] = 0; |
| as->as_pmbl_err_ap[i] = 0; |
| as->as_pmbl_err_sta[i] = 0; |
| aged_num++; |
| } |
| } |
| |
| return aged_num; |
| } |
| |
| static int |
| ieee80211_scs_metric_compare(int32_t metric1, uint32_t metric_pref1, int32_t metric2, uint32_t metric_pref2) |
| { |
| if (metric1 < metric2) { |
| return 1; |
| } else if (metric1 > metric2) { |
| return -1; |
| } else { |
| if (metric_pref1 > metric_pref2) |
| return 1; |
| else if (metric_pref1 == metric_pref2) |
| return 0; |
| else |
| return -1; |
| } |
| } |
| |
| static int |
| ieee80211_scs_metric_compare_by_chan(struct ieee80211com *ic, int32_t chan1, int32_t chan2) |
| { |
| struct ap_state *as = ic->ic_scan->ss_scs_priv; |
| |
| return ieee80211_scs_metric_compare(as->as_chanmetric[chan1], |
| as->as_chanmetric_pref[chan1], |
| as->as_chanmetric[chan2], |
| as->as_chanmetric_pref[chan2]); |
| } |
| |
| static void |
| ieee80211_scs_get_chan_metric(struct ieee80211com *ic, struct ieee80211_channel *chan, |
| uint8_t rate_ratio, int32_t *metric, uint32_t *metric_pref, uint32_t cc_flag) |
| { |
| struct ap_state *as = ic->ic_scan->ss_scs_priv; |
| int32_t chan_metric; |
| int32_t chan_metric_pref = 0; |
| int32_t chan_rate_ratio; |
| int32_t chan_rate_ratio_cap; |
| int32_t txpower; |
| int32_t cur_txpower = 0; |
| uint32_t traffic_ms; |
| uint16_t cca_intf; |
| char rndbuf[2]; |
| uint8_t chan_ieee = chan->ic_ieee; |
| int isdfs = !!(chan->ic_flags & IEEE80211_CHAN_DFS); |
| |
| traffic_ms = MAX((as->as_tx_ms + as->as_rx_ms), SCS_PICK_CHAN_MIN_SCALED_TRAFFIC); |
| if (ic->ic_curchan != IEEE80211_CHAN_ANYC) { |
| cur_txpower = ic->ic_curchan->ic_maxpower; |
| } |
| txpower = chan->ic_maxpower; |
| cca_intf = (as->as_cca_intf[chan_ieee] == SCS_CCA_INTF_INVALID) ? 0 : as->as_cca_intf[chan_ieee]; |
| |
| chan_rate_ratio = 100 - ((100 - rate_ratio) * ABS(txpower - cur_txpower)) / SCS_CHAN_POWER_DIFF_MAX; |
| chan_rate_ratio = MAX(chan_rate_ratio, 0); |
| if ((IEEE80211_SCS_ATTEN_INC_CC & cc_flag) && txpower < cur_txpower) { |
| chan_metric = SCS_MAX_RAW_CHAN_METRIC; |
| chan_rate_ratio_cap = 0; |
| } else if ((txpower - cur_txpower) < -SCS_CHAN_POWER_DIFF_SAFE) { |
| if (chan_rate_ratio > 0) { |
| chan_metric = traffic_ms * 100 / chan_rate_ratio + cca_intf; |
| chan_rate_ratio_cap = chan_rate_ratio; |
| } else { |
| chan_metric = SCS_MAX_RAW_CHAN_METRIC; |
| chan_rate_ratio_cap = 0; |
| } |
| } else if ((txpower - cur_txpower) > SCS_CHAN_POWER_DIFF_SAFE) { |
| chan_metric = traffic_ms * chan_rate_ratio / 100 + cca_intf; |
| if (chan_rate_ratio > 0) { |
| chan_rate_ratio_cap = 100 * 100 / chan_rate_ratio; |
| } else { |
| chan_rate_ratio_cap = SCS_MAX_RATE_RATIO_CAP; |
| } |
| } else { |
| chan_metric = traffic_ms + cca_intf; |
| chan_rate_ratio_cap = 100; |
| } |
| |
| /* Correct channel metric to account for different channel switch margins */ |
| chan_metric = MAX(0, (chan_metric - |
| (isdfs * (ic->ic_scs.scs_leavedfs_chan_mtrc_mrgn - ic->ic_scs.scs_chan_mtrc_mrgn)))); |
| |
| if (metric) { |
| *metric = chan_metric; |
| } |
| |
| if (metric_pref) { |
| int tx_power_factor = DM_DEFAULT_TX_POWER_FACTOR; |
| int dfs_factor = DM_DEFAULT_DFS_FACTOR; |
| |
| if (ic->ic_dm_factor.flags) { |
| if (ic->ic_dm_factor.flags & DM_FLAG_TXPOWER_FACTOR_PRESENT) { |
| tx_power_factor = ic->ic_dm_factor.txpower_factor; |
| } |
| if (ic->ic_dm_factor.flags & DM_FLAG_DFS_FACTOR_PRESENT) { |
| dfs_factor = ic->ic_dm_factor.dfs_factor; |
| } |
| } |
| |
| chan_metric_pref = (tx_power_factor * txpower) + (isdfs * dfs_factor); |
| /* metric preference: power.random */ |
| chan_metric_pref = (chan_metric_pref << 16); |
| /* Add a little noise to equally choose best ones */ |
| get_random_bytes(rndbuf, sizeof(rndbuf)); |
| chan_metric_pref += (rndbuf[0] << 8) | rndbuf[1]; |
| |
| *metric_pref = chan_metric_pref; |
| } |
| |
| SCSDBG(SCSLOG_NOTICE, "chan %d: txpower=%d, dfs=%d, radar=%d, rate_ratio_cap=%d, " |
| "margin_correction=-%d, metric=%d, pref=0x%x\n", |
| chan_ieee, txpower, |
| !!(chan->ic_flags & IEEE80211_CHAN_DFS), |
| !!(chan->ic_flags & IEEE80211_CHAN_RADAR), |
| chan_rate_ratio_cap, |
| (isdfs * (ic->ic_scs.scs_leavedfs_chan_mtrc_mrgn - ic->ic_scs.scs_chan_mtrc_mrgn)), |
| chan_metric, chan_metric_pref); |
| } |
| |
| void ieee80211_ap_pick_alternate_channel(struct ieee80211com *ic, |
| struct ieee80211_channel *bestchan, |
| struct ieee80211_channel *fs1_bestchan, |
| struct ieee80211_channel *fs1_secbestchan, |
| struct ieee80211_channel *fs2_bestchan, |
| struct ieee80211_channel *fs2_secbestchan) |
| { |
| int to_clear_availability; |
| struct ieee80211_channel *chan; |
| |
| if (!is_ieee80211_chan_valid(bestchan)) |
| return; |
| |
| to_clear_availability = (!ic->ic_dfs_is_eu_region() && |
| is_ieee80211_chan_valid(ic->ic_bsschan) && |
| (ic->ic_bsschan->ic_flags & IEEE80211_CHAN_DFS) && |
| !ic->ic_chan_compare_equality(ic, ic->ic_bsschan, bestchan)); |
| #define ALTERCHAN_IS_ACCEPTABLE(deschan) \ |
| (!ic->ic_chan_compare_equality(ic, deschan, bestchan) && \ |
| (!to_clear_availability || \ |
| !ic->ic_chan_compare_equality(ic, deschan, ic->ic_bsschan))) |
| |
| if (fs1_bestchan && (ALTERCHAN_IS_ACCEPTABLE(fs1_bestchan))) |
| ic->ic_ieee_best_alt_chan = fs1_bestchan->ic_ieee; |
| else if (fs1_secbestchan && (ALTERCHAN_IS_ACCEPTABLE(fs1_secbestchan))) |
| ic->ic_ieee_best_alt_chan = fs1_secbestchan->ic_ieee; |
| else if (fs2_bestchan && ALTERCHAN_IS_ACCEPTABLE(fs2_bestchan)) |
| ic->ic_ieee_best_alt_chan = fs2_bestchan->ic_ieee; |
| else if (fs2_secbestchan && ALTERCHAN_IS_ACCEPTABLE(fs2_secbestchan)) |
| ic->ic_ieee_best_alt_chan = fs2_secbestchan->ic_ieee; |
| /* else we keep ic->ic_ieee_best_alt_chan as-is since no better choice */ |
| |
| chan = ieee80211_find_channel_by_ieee(ic, ic->ic_ieee_best_alt_chan); |
| if (ic->ic_chan_compare_equality(ic, chan, bestchan)) { |
| ic->ic_ieee_best_alt_chan = 0; |
| } |
| } |
| EXPORT_SYMBOL(ieee80211_ap_pick_alternate_channel); |
| |
| void ieee80211_update_alternate_channels(struct ieee80211com *ic, |
| struct ieee80211_channel *bestchan, |
| struct ieee80211_channel **fs_bestchan, |
| struct ieee80211_channel **fs_secbestchan, |
| int (*compare_fn)(struct ieee80211com *, int, int)) |
| { |
| if (!ieee80211_is_chan_available(bestchan)) |
| return; |
| |
| if (!*fs_bestchan || compare_fn(ic, |
| bestchan->ic_ieee, |
| (*fs_bestchan)->ic_ieee) >= 0) { |
| *fs_secbestchan = *fs_bestchan; |
| *fs_bestchan = bestchan; |
| } else if (!*fs_secbestchan || compare_fn(ic, |
| bestchan->ic_ieee, |
| (*fs_secbestchan)->ic_ieee) >= 0) { |
| *fs_secbestchan = bestchan; |
| } |
| } |
| EXPORT_SYMBOL(ieee80211_update_alternate_channels); |
| |
| /* |
| * Channel ranking and selection of SCS |
| * Called when SCS decided that channel change is required. |
| * Return the selected channel number. 0 means no valid better channel. |
| */ |
| int |
| ieee80211_scs_pick_channel(struct ieee80211com *ic, int pick_flags, uint32_t cc_flag) |
| { |
| struct ap_state *as = ic->ic_scan->ss_scs_priv; |
| uint8_t rate_ratio; |
| struct ieee80211_channel *chan; |
| struct ieee80211_channel *chan2; |
| struct ieee80211_channel *selected_chan = NULL; |
| struct ieee80211_channel *best_chan = NULL; |
| struct ieee80211_channel *fs1_bestchan = NULL; |
| struct ieee80211_channel *fs1_secbestchan = NULL; |
| struct ieee80211_channel *fs2_bestchan = NULL; |
| struct ieee80211_channel *fs2_secbestchan = NULL; |
| int i; |
| int chan_sec_ieee; |
| int chan_sec40u_ieee; |
| int chan_sec40l_ieee; |
| int selected_subchan; |
| int best_chan_ieee; |
| int chan_metric; |
| int best_metric; |
| int curr_metric = SCS_MAX_RAW_CHAN_METRIC; |
| uint32_t chan_metric_pref; |
| uint32_t best_metric_pref; |
| int isdfs; |
| int is_match_dfs_pickflags; |
| int pick_anyway = (pick_flags & IEEE80211_SCS_PICK_ANYWAY); |
| int curchan; |
| int cur_bw; |
| int bestchan_is_curchan = 0; |
| int is_curchan; |
| int is_weather_chan; |
| int bestchan_is_weather_chan = 0; |
| |
| /* Prepare the information we need to do the channel ranking */ |
| curchan = ic->ic_curchan->ic_ieee; |
| cur_bw = ieee80211_get_bw(ic); |
| rate_ratio = ieee80211_scs_calc_rate_ratio(ic); |
| SCSDBG(SCSLOG_NOTICE, "curchan=%d cur_bw=%d cur_txpower=%d," |
| " rate_ratio=%d, tx_ms=%d, rx_ms=%d\n", |
| curchan, cur_bw, ic->ic_curchan->ic_maxpower, |
| rate_ratio, as->as_tx_ms, as->as_rx_ms); |
| |
| ieee80211_scs_aging(ic, ic->ic_scs.scs_thrshld_aging_nor); |
| |
| /* ranking */ |
| memset(as->as_chanmetric, 0, sizeof(as->as_chanmetric)); |
| ieee80211_scs_metric_update_timestamps(as); |
| |
| best_chan_ieee = SCS_BEST_CHAN_INVALID; |
| best_metric = 0; |
| best_metric_pref = 0; |
| |
| for (i = 1; i < IEEE80211_CHAN_MAX; i++) { |
| chan = ieee80211_find_channel_by_ieee(ic, i); |
| if (chan == NULL) { |
| continue; |
| } |
| |
| isdfs = (chan->ic_flags & IEEE80211_CHAN_DFS); |
| is_match_dfs_pickflags = (isdfs && (pick_flags & IEEE80211_SCS_PICK_DFS_ONLY)) |
| || (isdfs && (pick_flags & IEEE80211_SCS_PICK_AVAILABLE_DFS_ONLY)) |
| || (!isdfs && (pick_flags & IEEE80211_SCS_PICK_NON_DFS_ONLY)) |
| || !(pick_flags & (IEEE80211_SCS_PICK_DFS_ONLY | IEEE80211_SCS_PICK_NON_DFS_ONLY | |
| IEEE80211_SCS_PICK_AVAILABLE_DFS_ONLY)); |
| |
| ieee80211_scs_get_chan_metric(ic, chan, rate_ratio, |
| &as->as_chanmetric[i], &as->as_chanmetric_pref[i], cc_flag); |
| /* |
| * (i) When scs_pick_channel is being called from scs comparison task, scs manual, |
| * we should choose from channels which are available |
| * (ii) When scs_pick_channel is being called from OCAC task |
| * we should choose from DFS channels which are not available, cac not done, and radar not detected. |
| */ |
| |
| /* This is called only from SCS Manual and SCS comparison task context */ |
| if ((pick_flags & IEEE80211_SCS_PICK_AVAILABLE_ANY_CHANNEL) |
| && (!ieee80211_is_chan_available(chan))) { |
| SCSDBG(SCSLOG_INFO, "chan %d skipped as not available\n", chan->ic_ieee); |
| continue; |
| } |
| |
| /* OCAC needs a DFS channel for which CAC is not already done */ |
| if (pick_flags & IEEE80211_SCS_PICK_NOT_AVAILABLE_DFS_ONLY) { |
| if ((chan) && (ic->ic_dfs_chans_available_for_cac(ic, chan) == false)) { |
| SCSDBG(SCSLOG_INFO, "chan %d skipped (flag: %d)\n", chan->ic_ieee, |
| ic->ic_chan_availability_status[chan->ic_ieee]); |
| continue; |
| } |
| } |
| |
| if (cur_bw == BW_HT20) { |
| chan_sec_ieee = -1; |
| chan_sec40u_ieee = -1; |
| chan_sec40l_ieee = -1; |
| |
| chan_metric = as->as_chanmetric[i]; |
| chan_metric_pref = as->as_chanmetric_pref[i]; |
| selected_subchan = i; |
| } else if (cur_bw == BW_HT40) { |
| /* only calculate channel pair when low number 20M channel is already calculated */ |
| if (!(chan->ic_flags & IEEE80211_CHAN_HT40D)) { |
| continue; |
| } |
| chan_sec_ieee = i - IEEE80211_CHAN_SEC_SHIFT; |
| chan_sec40u_ieee = -1; |
| chan_sec40l_ieee = -1; |
| |
| /* select worse channel as primary channel within the chan pair */ |
| if (ieee80211_scs_metric_compare_by_chan(ic, i, chan_sec_ieee) > 0) { |
| selected_subchan = chan_sec_ieee; |
| } else { |
| selected_subchan = i; |
| } |
| |
| chan_metric = as->as_chanmetric[selected_subchan]; |
| chan_metric_pref = as->as_chanmetric_pref[selected_subchan]; |
| } else if (cur_bw >= BW_HT80) { |
| /* only calculate channel set when all 20M channels are already calculated */ |
| if (!(chan->ic_ext_flags & IEEE80211_CHAN_VHT80_UU)) { |
| continue; |
| } |
| chan_sec_ieee = i - IEEE80211_CHAN_SEC_SHIFT; |
| chan_sec40u_ieee = i - 2 * IEEE80211_CHAN_SEC_SHIFT; |
| chan_sec40l_ieee = i - 3 * IEEE80211_CHAN_SEC_SHIFT; |
| |
| /* select worst channel as primary channel within the best chan set */ |
| if (ieee80211_scs_metric_compare_by_chan(ic, i, chan_sec_ieee) > 0) |
| selected_subchan = chan_sec_ieee; |
| else |
| selected_subchan = i; |
| if (ieee80211_scs_metric_compare_by_chan(ic, selected_subchan, chan_sec40u_ieee) > 0) |
| selected_subchan = chan_sec40u_ieee; |
| if (ieee80211_scs_metric_compare_by_chan(ic, selected_subchan, chan_sec40l_ieee) > 0) |
| selected_subchan = chan_sec40l_ieee; |
| |
| chan_metric = as->as_chanmetric[selected_subchan]; |
| chan_metric_pref = as->as_chanmetric_pref[selected_subchan]; |
| } else { |
| printk("SCS: unknown bandwidth: %u\n", cur_bw); |
| continue; |
| } |
| |
| /* Need to calculate DFS channel metric in case current channel is DFS channel */ |
| if ((curchan == i) || |
| (curchan == chan_sec_ieee) || |
| (curchan == chan_sec40u_ieee) || |
| (curchan == chan_sec40l_ieee)) { |
| is_curchan = 1; |
| curr_metric = chan_metric; |
| if (pick_anyway) { |
| continue; |
| } |
| } else { |
| is_curchan = 0; |
| } |
| |
| if ((!(pick_flags & IEEE80211_SCS_PICK_AVAILABLE_ANY_CHANNEL)) && |
| (!(pick_flags & IEEE80211_SCS_PICK_NOT_AVAILABLE_DFS_ONLY))) { |
| if (!is_match_dfs_pickflags) { |
| SCSDBG(SCSLOG_INFO, "chan %d skipped as pick flags mismatch\n", chan->ic_ieee); |
| continue; |
| } else if (pick_flags & IEEE80211_SCS_PICK_AVAILABLE_DFS_ONLY) { |
| if (!ieee80211_is_chan_available(chan)) { |
| SCSDBG(SCSLOG_INFO, "chan %d skipped as not available\n", chan->ic_ieee); |
| continue; |
| } |
| } |
| } |
| |
| if (isset(ic->ic_chan_pri_inactive, i) && |
| ((chan_sec_ieee == -1) || isset(ic->ic_chan_pri_inactive, chan_sec_ieee)) && |
| ((chan_sec40u_ieee == -1) || isset(ic->ic_chan_pri_inactive, chan_sec40u_ieee)) && |
| ((chan_sec40l_ieee == -1) || isset(ic->ic_chan_pri_inactive, chan_sec40l_ieee))) { |
| /* All the sub-channel can't be primary channel */ |
| SCSDBG(SCSLOG_INFO, "chan %d skipped as all inactive\n", chan->ic_ieee); |
| continue; |
| } |
| |
| is_weather_chan = ieee80211_is_on_weather_channel(ic, chan); |
| |
| /* radar detected on this channel, secondary channel or secondary 40MHz channels*/ |
| if (chan->ic_flags & IEEE80211_CHAN_DFS) { |
| if (chan->ic_flags & IEEE80211_CHAN_RADAR) |
| continue; |
| |
| if (cur_bw >= BW_HT40) { |
| chan2 = ieee80211_find_channel_by_ieee(ic, chan_sec_ieee); |
| if (chan2 && (chan2->ic_flags & IEEE80211_CHAN_RADAR)) |
| continue; |
| |
| if (cur_bw >= BW_HT80) { |
| chan2 = ieee80211_find_channel_by_ieee(ic, chan_sec40u_ieee); |
| if (chan2 && (chan2->ic_flags & IEEE80211_CHAN_RADAR)) |
| continue; |
| |
| chan2 = ieee80211_find_channel_by_ieee(ic, chan_sec40l_ieee); |
| if (chan2 && (chan2->ic_flags & IEEE80211_CHAN_RADAR)) |
| continue; |
| } |
| } |
| } |
| |
| if (best_chan_ieee != SCS_BEST_CHAN_INVALID && |
| !bestchan_is_weather_chan && |
| is_weather_chan && |
| !is_curchan && |
| (pick_flags & IEEE80211_SCS_PICK_NOT_AVAILABLE_DFS_ONLY) && |
| (ic->ic_ocac.ocac_cfg.ocac_params.wea_duration_secs > |
| ic->ic_ocac.ocac_cfg.ocac_params.duration_secs)) { |
| /* Weather channel has low priority since it need too long CAC time */ |
| SCSDBG(SCSLOG_INFO, "weather chan %d skipped as low priority\n", chan->ic_ieee); |
| continue; |
| } |
| |
| /* Select best channel */ |
| if (best_chan_ieee == SCS_BEST_CHAN_INVALID || |
| ieee80211_scs_metric_compare_by_chan(ic, |
| selected_subchan, |
| best_chan_ieee) > 0) { |
| best_chan_ieee = selected_subchan; |
| best_metric = chan_metric; |
| best_metric_pref = chan_metric_pref; |
| bestchan_is_curchan = is_curchan; |
| bestchan_is_weather_chan = is_weather_chan; |
| } |
| |
| selected_chan = ieee80211_find_channel_by_ieee(ic, selected_subchan); |
| ieee80211_update_alternate_channels(ic, |
| selected_chan, |
| &fs2_bestchan, |
| &fs2_secbestchan, |
| ieee80211_scs_metric_compare_by_chan); |
| |
| selected_chan = ieee80211_scs_switch_pri_chan(ic->ic_scan, selected_chan); |
| if (best_chan == NULL || (selected_chan && |
| ieee80211_scs_metric_compare_by_chan(ic, |
| selected_chan->ic_ieee, |
| best_chan->ic_ieee) > 0)) { |
| best_chan = selected_chan; |
| } else { |
| SCSDBG(SCSLOG_INFO, "chan %d skipped as OBSS check failed\n", selected_subchan); |
| } |
| |
| ieee80211_update_alternate_channels(ic, |
| selected_chan, |
| &fs1_bestchan, |
| &fs1_secbestchan, |
| ieee80211_scs_metric_compare_by_chan); |
| } |
| |
| if (best_chan) { |
| best_chan_ieee = best_chan->ic_ieee; |
| best_metric = as->as_chanmetric[best_chan_ieee]; |
| best_metric_pref = as->as_chanmetric_pref[best_chan_ieee]; |
| bestchan_is_curchan = ic->ic_chan_compare_equality(ic, ic->ic_curchan, best_chan); |
| } else { |
| best_chan = ieee80211_find_channel_by_ieee(ic, best_chan_ieee); |
| } |
| |
| if (!pick_anyway && best_chan_ieee != SCS_BEST_CHAN_INVALID) { |
| if (bestchan_is_curchan) { |
| best_chan_ieee = SCS_BEST_CHAN_INVALID; |
| SCSDBG(SCSLOG_NOTICE, "current chan is best channel\n"); |
| } else { |
| bool dfs_to_non_dfs = false; |
| |
| if (curr_metric == SCS_MAX_RAW_CHAN_METRIC) { |
| /* If the IEEE80211_CHAN_HT40D channel in 40MHz or IEEE80211_CHAN_VHT80_UU |
| * channel in 80MHz is disabled, curr_metric may be SCS_MAX_RAW_CHAN_METRIC |
| * per above metric calculation, so need to read it from as_chanmetric table. |
| */ |
| curr_metric = as->as_chanmetric[curchan]; |
| } |
| |
| if (best_chan) { |
| dfs_to_non_dfs = (ic->ic_curchan->ic_flags & IEEE80211_CHAN_DFS) && |
| (!(best_chan->ic_flags & IEEE80211_CHAN_DFS)); |
| |
| if (best_metric == SCS_MAX_RAW_CHAN_METRIC || |
| ((curr_metric - best_metric) * 100) < ((dfs_to_non_dfs ? |
| (ic->ic_scs.scs_leavedfs_chan_mtrc_mrgn) : |
| (ic->ic_scs.scs_chan_mtrc_mrgn)) * IEEE80211_SCS_CCA_INTF_SCALE)) { |
| /* Avoid unnecessary channel change */ |
| SCSDBG(SCSLOG_NOTICE, "best chan %u is not better enough (%u - %u) < %u%%\n", |
| best_chan_ieee, curr_metric, best_metric, |
| dfs_to_non_dfs ? ic->ic_scs.scs_leavedfs_chan_mtrc_mrgn : ic->ic_scs.scs_chan_mtrc_mrgn); |
| |
| best_chan_ieee = SCS_BEST_CHAN_INVALID; |
| } |
| } else { |
| best_chan_ieee = SCS_BEST_CHAN_INVALID; |
| } |
| } |
| } |
| as->as_scs_ranking_cnt++; |
| |
| if (best_chan_ieee != SCS_BEST_CHAN_INVALID) { |
| SCSDBG(SCSLOG_NOTICE, "chan %d selected as best chan\n", best_chan_ieee); |
| |
| ieee80211_ap_pick_alternate_channel(ic, |
| best_chan, |
| fs1_bestchan, |
| fs1_secbestchan, |
| fs2_bestchan, |
| fs2_secbestchan); |
| SCSDBG(SCSLOG_NOTICE, "%s: Fast-switch best alt channel updated to %d\n", |
| __func__, ic->ic_ieee_best_alt_chan); |
| } |
| |
| return best_chan_ieee; |
| } |
| |
| void ieee80211_scs_contribute_randomness(uint32_t cca_intf, uint32_t lpre_err, uint32_t spre_err) |
| { |
| unsigned random_buf[] = {cca_intf, lpre_err, spre_err}; |
| |
| add_qtn_randomness(random_buf, ARRAY_SIZE(random_buf)); |
| } |
| |
| int ieee80211_scs_get_scaled_scan_info(struct ieee80211com *ic, int chan_ieee, |
| struct qtn_scs_scan_info *p_scan_info) |
| { |
| struct shared_params *sp = qtn_mproc_sync_shared_params_get(); |
| struct qtn_scs_info_set *scs_info_lh = sp->scs_info_lhost; |
| int ret = -1; |
| |
| p_scan_info->cca_try = 0; |
| if (chan_ieee < IEEE80211_CHAN_MAX) { |
| memcpy(p_scan_info, &scs_info_lh->scan_info[chan_ieee], sizeof(struct qtn_scs_scan_info)); |
| } |
| |
| if (p_scan_info->cca_try) { |
| ieee80211_scs_contribute_randomness(p_scan_info->cca_intf, p_scan_info->lpre_err, |
| p_scan_info->spre_err); |
| p_scan_info->cca_intf = IEEE80211_SCS_NORMALIZE(p_scan_info->cca_intf, p_scan_info->cca_try); |
| p_scan_info->cca_busy = IEEE80211_SCS_NORMALIZE(p_scan_info->cca_busy, p_scan_info->cca_try); |
| p_scan_info->cca_idle = IEEE80211_SCS_NORMALIZE(p_scan_info->cca_idle, p_scan_info->cca_try); |
| p_scan_info->cca_tx = IEEE80211_SCS_NORMALIZE(p_scan_info->cca_tx, p_scan_info->cca_try); |
| p_scan_info->cca_pri = IEEE80211_SCS_NORMALIZE(p_scan_info->cca_pri, p_scan_info->cca_try); |
| p_scan_info->cca_sec20 = IEEE80211_SCS_NORMALIZE(p_scan_info->cca_sec20, p_scan_info->cca_try); |
| p_scan_info->cca_sec40 = IEEE80211_SCS_NORMALIZE(p_scan_info->cca_sec40, p_scan_info->cca_try); |
| p_scan_info->bcn_rcvd = IEEE80211_SCS_NORMALIZE(p_scan_info->bcn_rcvd, p_scan_info->cca_try); |
| p_scan_info->crc_err = IEEE80211_SCS_NORMALIZE(p_scan_info->crc_err, p_scan_info->cca_try); |
| p_scan_info->spre_err = IEEE80211_SCS_NORMALIZE(p_scan_info->spre_err, p_scan_info->cca_try); |
| p_scan_info->lpre_err = IEEE80211_SCS_NORMALIZE(p_scan_info->lpre_err, p_scan_info->cca_try); |
| p_scan_info->cca_try = IEEE80211_SCS_CCA_INTF_SCALE; |
| ret = 0; |
| } |
| |
| return ret; |
| } |
| |
| void ieee80211_scs_update_ranking_table_by_scan(struct ieee80211com *ic) |
| { |
| struct qtn_scs_scan_info scan_info; |
| struct ap_state *as, *scs_as; |
| struct ieee80211_channel *chan; |
| int chansec_ieee; |
| uint32_t pmbl_err; |
| int i, ret; |
| uint32_t update_mode = IEEE80211_SCS_OFFCHAN; |
| struct scs_chan_intf_params intf_params= {0}; |
| |
| if (ic->ic_opmode != IEEE80211_M_HOSTAP) { |
| return; |
| } |
| |
| /* if we didn't return to bss channel then stats for the last channel were not updated */ |
| if (ic->ic_bsschan == IEEE80211_CHAN_ANYC) { |
| ic->ic_scs_update_scan_stats(ic); |
| update_mode = IEEE80211_SCS_INIT_SCAN; |
| } |
| |
| as = ic->ic_scan->ss_priv; |
| scs_as = ic->ic_scan->ss_scs_priv; |
| for (i = 0; i < IEEE80211_CHAN_MAX; i++) { |
| chan = ieee80211_find_channel_by_ieee(ic, i); |
| if (chan == NULL) { |
| continue; |
| } |
| ret = ieee80211_scs_get_scaled_scan_info(ic, i, &scan_info); |
| if (ret != 0) { |
| /* use secondary channel's scan info */ |
| chansec_ieee = ieee80211_find_sec_chan(chan); |
| if (chansec_ieee) { |
| ret = ieee80211_scs_get_scaled_scan_info(ic, chansec_ieee, &scan_info); |
| } |
| SCSDBG(SCSLOG_INFO, "Didn't find scan_info of channel %u, use the scan_info of channel %u\n", |
| i, chansec_ieee); |
| } |
| if (ret == 0) { |
| pmbl_err = (scan_info.spre_err * ic->ic_scs.scs_sp_wf + |
| scan_info.lpre_err * ic->ic_scs.scs_lp_wf) / 100; |
| /* update initial channel ranking table */ |
| if (as) { |
| as->as_pmbl_err_ap[i] = pmbl_err; |
| as->as_cca_intf[i] = scan_info.cca_intf; |
| as->as_cca_intf_jiffies[i] = jiffies; |
| } |
| |
| /* update SCS channel ranking table */ |
| if (scs_as) { |
| uint32_t compound_cca_intf = scan_info.cca_intf; |
| |
| /* |
| * don't add preamble failure counts to compound_cca_intf because |
| * the preamble failure counts got by scanning are not reliable |
| */ |
| /* |
| compound_cca_intf = ieee80211_scs_fix_cca_intf(ic, NULL, scan_info.cca_intf, |
| scan_info.spre_err, scan_info.lpre_err); |
| */ |
| intf_params.chan = chan; |
| intf_params.chan_bw = scan_info.bw_sel; |
| intf_params.cca_intf = compound_cca_intf; |
| intf_params.pmbl_err = pmbl_err; |
| intf_params.cca_dur = IEEE80211_SCS_CCA_INTF_SCALE; |
| intf_params.cca_pri = scan_info.cca_pri; |
| intf_params.cca_sec = scan_info.cca_sec20; |
| intf_params.cca_sec40 = scan_info.cca_sec40; |
| ieee80211_scs_update_chans_cca_intf(ic, &intf_params, update_mode, NULL); |
| } |
| } else { |
| SCSDBG(SCSLOG_INFO, "No available scan_info for channel %u\n", i); |
| /* clear initial channel ranking table */ |
| if (as) { |
| as->as_pmbl_err_ap[i] = 0; |
| as->as_cca_intf[i] = SCS_CCA_INTF_INVALID; |
| as->as_cca_intf_jiffies[i] = jiffies; |
| } |
| } |
| } |
| } |
| EXPORT_SYMBOL(ieee80211_scs_update_ranking_table_by_scan); |
| |
| void ieee80211_scs_scale_cochan_data(struct ieee80211com *ic, struct qtn_scs_info *scs_info_read) |
| { |
| int i; |
| struct qtn_scs_vsp_node_stats *stats; |
| |
| if (scs_info_read->cca_try == 0) |
| return; |
| |
| scs_info_read->cca_idle = IEEE80211_SCS_NORMALIZE(scs_info_read->cca_idle, scs_info_read->cca_try); |
| scs_info_read->cca_busy = IEEE80211_SCS_NORMALIZE(scs_info_read->cca_busy, scs_info_read->cca_try); |
| scs_info_read->cca_interference = IEEE80211_SCS_NORMALIZE(scs_info_read->cca_interference, scs_info_read->cca_try); |
| scs_info_read->cca_tx = IEEE80211_SCS_NORMALIZE(scs_info_read->cca_tx, scs_info_read->cca_try); |
| scs_info_read->tx_usecs = IEEE80211_SCS_NORMALIZE(scs_info_read->tx_usecs, scs_info_read->cca_try); |
| scs_info_read->rx_usecs = IEEE80211_SCS_NORMALIZE(scs_info_read->rx_usecs, scs_info_read->cca_try); |
| scs_info_read->beacon_recvd = IEEE80211_SCS_NORMALIZE(scs_info_read->beacon_recvd, scs_info_read->cca_try); |
| |
| for (i = 0; i < scs_info_read->scs_vsp_info.num_of_assoc; i++) { |
| stats = &scs_info_read->scs_vsp_info.scs_vsp_node_stats[i]; |
| stats->tx_usecs = IEEE80211_SCS_NORMALIZE(stats->tx_usecs, scs_info_read->cca_try); |
| stats->rx_usecs = IEEE80211_SCS_NORMALIZE(stats->rx_usecs, scs_info_read->cca_try); |
| } |
| |
| scs_info_read->cca_try = IEEE80211_SCS_CCA_INTF_SCALE; |
| } |
| |
| void ieee80211_scs_scale_offchan_data(struct ieee80211com *ic, struct qtn_scs_oc_info *scs_oc_info) |
| { |
| if (scs_oc_info->off_chan_cca_try_cnt == 0) |
| return; |
| |
| scs_oc_info->off_chan_cca_busy = IEEE80211_SCS_NORMALIZE(scs_oc_info->off_chan_cca_busy, |
| scs_oc_info->off_chan_cca_try_cnt); |
| scs_oc_info->off_chan_cca_sample_cnt = IEEE80211_SCS_NORMALIZE(scs_oc_info->off_chan_cca_sample_cnt, |
| scs_oc_info->off_chan_cca_try_cnt); |
| scs_oc_info->off_chan_beacon_recvd = IEEE80211_SCS_NORMALIZE(scs_oc_info->off_chan_beacon_recvd, |
| scs_oc_info->off_chan_cca_try_cnt); |
| scs_oc_info->off_chan_crc_errs = IEEE80211_SCS_NORMALIZE(scs_oc_info->off_chan_crc_errs, |
| scs_oc_info->off_chan_cca_try_cnt); |
| scs_oc_info->off_chan_sp_errs = IEEE80211_SCS_NORMALIZE(scs_oc_info->off_chan_sp_errs, |
| scs_oc_info->off_chan_cca_try_cnt); |
| scs_oc_info->off_chan_lp_errs = IEEE80211_SCS_NORMALIZE(scs_oc_info->off_chan_lp_errs, |
| scs_oc_info->off_chan_cca_try_cnt); |
| |
| scs_oc_info->off_chan_cca_try_cnt = IEEE80211_SCS_CCA_INTF_SCALE; |
| } |
| |
| static struct qtn_scs_vsp_node_stats *ieee80211_scs_find_node_stats(struct ieee80211com *ic, struct qtn_scs_info *scs_info_read, uint16_t aid) |
| { |
| int i; |
| struct qtn_scs_vsp_node_stats *stats; |
| |
| for (i = 0; i < scs_info_read->scs_vsp_info.num_of_assoc; i++) { |
| stats = &scs_info_read->scs_vsp_info.scs_vsp_node_stats[i]; |
| if (stats->ni_associd == IEEE80211_AID(aid)) |
| return stats; |
| } |
| |
| return NULL; |
| } |
| |
| uint32_t local_scs_median_parition(uint32_t *buf, uint32_t l, uint32_t r) |
| { |
| uint32_t t; |
| uint32_t i; |
| uint32_t p; |
| |
| if (l == r) |
| return l; |
| |
| for (i = l, p = l; i < r; i++) { |
| if (buf[i] < buf[r]) { |
| t = buf[i]; |
| buf[i] = buf[p]; |
| buf[p] = t; |
| p++; |
| } |
| } |
| |
| t = buf[r]; |
| buf[r] = buf[p]; |
| buf[p] = t; |
| |
| return p; |
| } |
| |
| static uint32_t local_scs_get_median(uint32_t *buf, uint32_t l, uint32_t r) |
| { |
| uint32_t idx; |
| |
| idx = local_scs_median_parition(buf, l, r); |
| |
| if (idx == QTN_SCS_FILTER_MEDIAN_IDX) |
| return buf[idx]; |
| |
| if (idx > QTN_SCS_FILTER_MEDIAN_IDX) |
| return local_scs_get_median(buf, l, idx - 1); |
| else |
| return local_scs_get_median(buf, idx + 1, r); |
| } |
| |
| static uint32_t ieee80211_scs_get_median(struct ieee80211com *ic, |
| struct qtn_scs_data_history *history) |
| { |
| static uint32_t buf[QTN_SCS_FILTER_WINDOW_SZ]; |
| |
| memcpy(buf, history->buffer, sizeof(history->buffer)); |
| |
| return local_scs_get_median(buf, 0, QTN_SCS_FILTER_WINDOW_SZ - 1); |
| } |
| |
| static int32_t ieee80211_scs_get_channel_index(struct ieee80211com *ic, struct ieee80211_channel *chan) |
| { |
| uint32_t i; |
| |
| for (i = 0; i < ic->ic_nchans; i++) { |
| if (ic->ic_channels[i].ic_ieee == chan->ic_ieee) |
| break; |
| } |
| |
| if (i == ic->ic_nchans) |
| return -1; |
| |
| return i; |
| } |
| |
| static void ieee80211_scs_update_cochan_filter_history(struct ieee80211com *ic, |
| struct ieee80211_phy_stats *p_phy_stats, |
| struct qtn_scs_stats_history *history, |
| int32_t idx) |
| { |
| history->sp_errs[idx].buffer[history->sp_errs[idx].idx++] = p_phy_stats->cnt_sp_fail; |
| if (history->sp_errs[idx].idx == QTN_SCS_FILTER_WINDOW_SZ) |
| history->sp_errs[idx].idx = 0; |
| |
| history->lp_errs[idx].buffer[history->lp_errs[idx].idx++] = p_phy_stats->cnt_lp_fail; |
| if (history->lp_errs[idx].idx == QTN_SCS_FILTER_WINDOW_SZ) |
| history->lp_errs[idx].idx = 0; |
| } |
| |
| static uint32_t ieee80211_scs_fix_cca_intf(struct ieee80211com *ic, struct ieee80211_node *ni, uint32_t cca_intf, uint32_t sp_fail, uint32_t lp_fail) |
| { |
| uint32_t pmbl_fail; |
| int pmbl_level; |
| int mapped_intf_max; |
| uint32_t compound_cca_intf; |
| |
| pmbl_fail = (sp_fail * ic->ic_scs.scs_sp_wf + lp_fail * ic->ic_scs.scs_lp_wf) / 100; |
| pmbl_level = pmbl_fail * 100 / ic->ic_scs.scs_pmbl_err_range; |
| pmbl_level = MIN(pmbl_level, 100); |
| mapped_intf_max = ic->ic_scs.scs_pmbl_err_mapped_intf_range * IEEE80211_SCS_CCA_INTF_SCALE / 100; |
| compound_cca_intf = cca_intf + pmbl_level * mapped_intf_max / 100; |
| compound_cca_intf = MIN(compound_cca_intf, IEEE80211_SCS_CCA_INTF_SCALE); |
| SCSDBG(SCSLOG_INFO, "node 0x%x: node_cca_intf=%u, pmbl_smthed_weighted=%u, compound_cca_intf=%u\n", |
| (ni ? ni->ni_associd : 0), |
| cca_intf, pmbl_fail, compound_cca_intf); |
| |
| return compound_cca_intf; |
| } |
| |
| static int |
| ieee80211_scs_change_channel(struct ieee80211com *ic, int newchan_ieee) |
| { |
| struct ieee80211_channel *newchan; |
| struct ieee80211_channel *curchan; |
| int chan2_ieee; |
| struct ap_state *as = ic->ic_scan->ss_scs_priv; |
| int ret = -1; |
| int cur_bw; |
| |
| newchan = ieee80211_find_channel_by_ieee(ic, newchan_ieee); |
| if (newchan == NULL) { |
| return ret; |
| } |
| |
| curchan = ic->ic_curchan; |
| ret = ieee80211_enter_csa(ic, newchan, ieee80211_scs_trigger_channel_switch, |
| ic->ic_csw_reason, |
| IEEE80211_DEFAULT_CHANCHANGE_TBTT_COUNT, |
| IEEE80211_CSA_MUST_STOP_TX, |
| IEEE80211_CSA_F_BEACON | IEEE80211_CSA_F_ACTION); |
| if (ret == 0) { |
| ic->ic_aci_cci_cce.cce_previous = curchan->ic_ieee; |
| ic->ic_aci_cci_cce.cce_current = newchan->ic_ieee; |
| |
| setbit(as->as_chan_xped, curchan->ic_ieee); |
| cur_bw = ieee80211_get_bw(ic); |
| if (cur_bw >= BW_HT40) { |
| chan2_ieee = ieee80211_find_sec_chan(curchan); |
| if (chan2_ieee) |
| setbit(as->as_chan_xped, chan2_ieee); |
| if (cur_bw >= BW_HT80) { |
| chan2_ieee = ieee80211_find_sec40u_chan(curchan); |
| if (chan2_ieee) |
| setbit(as->as_chan_xped, chan2_ieee); |
| chan2_ieee = ieee80211_find_sec40l_chan(curchan); |
| if (chan2_ieee) |
| setbit(as->as_chan_xped, chan2_ieee); |
| } |
| } |
| } |
| |
| return ret; |
| } |
| |
| static void |
| ieee80211_scs_update_dfs_reentry(struct ieee80211com *ic, uint32_t cc_flag, uint32_t *dfs_reentry_clear) |
| { |
| int curr_chan_cca_intf; |
| int aged_num; |
| int curchan = ic->ic_curchan->ic_ieee; |
| struct ap_state *as = ic->ic_scan->ss_scs_priv; |
| |
| if (!ic->ic_scs.scs_thrshld_dfs_reentry) |
| return; |
| |
| curr_chan_cca_intf = (as->as_cca_intf[curchan] == SCS_CCA_INTF_INVALID) ? 0 : as->as_cca_intf[curchan]; |
| |
| /* When request is because of interference */ |
| if ((cc_flag & IEEE80211_SCS_INTF_CC) && |
| ((curr_chan_cca_intf * 100) > |
| (ic->ic_scs.scs_thrshld_dfs_reentry_intf * IEEE80211_SCS_CCA_INTF_SCALE))) { |
| as->as_dfs_reentry_cnt++; |
| SCSDBG(SCSLOG_NOTICE, "channel picking discard counter %d\n", |
| as->as_dfs_reentry_cnt); |
| *dfs_reentry_clear = 0; |
| if ((as->as_dfs_reentry_cnt * ic->ic_scs.scs_cca_sample_dur) >= |
| ic->ic_scs.scs_thrshld_dfs_reentry) { |
| aged_num = ieee80211_scs_aging(ic, ic->ic_scs.scs_thrshld_aging_dfsreent); |
| if (aged_num > 0) { |
| /* don't clear dfs reentry and wait for next interval result */ |
| SCSDBG(SCSLOG_NOTICE, "%u channel entry aged out, " |
| "postpone DFS re-entry and re-try other channel\n", |
| aged_num); |
| } else { |
| as->as_dfs_reentry_level = 1; |
| SCSDBG(SCSLOG_NOTICE, "immediately DFS re-entry triggered with " |
| "channel picking counter %d\n", |
| as->as_dfs_reentry_cnt); |
| } |
| } |
| } |
| } |
| |
| static void ieee80211_scs_update_scs_burst_queue(struct ieee80211com *ic) |
| { |
| int i = 0; |
| int new_chan; |
| |
| if (unlikely(ic->ic_scs.scs_burst_is_paused)) { |
| if (time_after(jiffies, ic->ic_scs.scs_burst_pause_jiffies)) { |
| ic->ic_scs.scs_burst_is_paused = 0; |
| if (!ic->ic_scs.scs_burst_force_switch) |
| return; |
| |
| new_chan = ieee80211_scs_pick_channel(ic, |
| IEEE80211_SCS_PICK_AVAILABLE_ANY_CHANNEL, |
| IEEE80211_SCS_NA_CC); |
| if (new_chan == SCS_BEST_CHAN_INVALID) { |
| SCSDBG(SCSLOG_CRIT, "Switching to channel failed\n"); |
| return; |
| } |
| |
| ic->ic_csw_reason = IEEE80211_CSW_REASON_SCS; |
| if (!ieee80211_scs_change_channel(ic, new_chan)) |
| SCSDBG(SCSLOG_CRIT, "Switching to channel %d after pausing the burst channel switching\n", new_chan); |
| } |
| |
| return; |
| } |
| |
| for (i = 0; i < ic->ic_scs.scs_burst_thresh; i++) { |
| if (!ic->ic_scs.scs_burst_queue[i]) |
| continue; |
| if (ic->ic_scs.scs_burst_queue[i] > ic->ic_scs.scs_cca_sample_dur) |
| ic->ic_scs.scs_burst_queue[i] -= ic->ic_scs.scs_cca_sample_dur; |
| else |
| ic->ic_scs.scs_burst_queue[i] = 0; |
| } |
| |
| } |
| |
| static int ieee80211_scs_add_event_scs_burst_queue(struct ieee80211com *ic) |
| { |
| int i = 0; |
| |
| if (unlikely(ic->ic_scs.scs_burst_is_paused)) |
| return 1; |
| |
| for (i = 0; i < ic->ic_scs.scs_burst_thresh; i++) { |
| if (!ic->ic_scs.scs_burst_queue[i]) { |
| ic->ic_scs.scs_burst_queue[i] = ic->ic_scs.scs_burst_window; |
| return 0; |
| } |
| } |
| |
| ic->ic_scs.scs_burst_is_paused = 1; |
| ic->ic_scs.scs_burst_pause_jiffies = jiffies + ic->ic_scs.scs_burst_pause_time * HZ; |
| memset(ic->ic_scs.scs_burst_queue, 0 , sizeof(ic->ic_scs.scs_burst_queue)); |
| return 1; |
| } |
| |
| static void |
| ieee80211_scs_start_compare(unsigned long arg) |
| { |
| #define OPMODE_PEER_BW_SW_CCA40_TH1 30 |
| #define OPMODE_PEER_BW_SW_CCA40_TH2 10 |
| |
| struct ieee80211com *ic = (struct ieee80211com *) arg; |
| struct ieee80211vap *vap; |
| struct shared_params *sp = qtn_mproc_sync_shared_params_get(); |
| int new_chan; |
| struct qtn_scs_info_set *scs_info_lh = sp->scs_info_lhost; |
| struct qtn_scs_info *scs_info_read = NULL; |
| uint32_t cc_flag; |
| uint32_t pmbl_err; |
| uint32_t dfs_reentry_clear = 1; |
| uint32_t compound_cca_intf, raw_cca_intf; |
| uint32_t clean_level = IEEE80211_SCS_STATE_PERIOD_CLEAN; |
| struct ieee80211_phy_stats phy_stats; |
| struct ap_state *as = ic->ic_scan->ss_scs_priv; |
| uint8_t *tdls_stats_buf = NULL; |
| uint16_t tdls_stats_buf_len = IEEE80211_MAX_TDLS_NODES * |
| sizeof(struct ieee80211_tdls_scs_stats); |
| uint16_t extra_ie_len = 0; |
| int tdls_update_failed = 0; |
| uint32_t stats_unstable = 0; |
| int32_t cca_sec40 = 0; |
| static uint8_t opmode_bw = QTN_BW_80M; |
| static uint8_t debounce = 0; |
| struct scs_chan_intf_params intf_params= {0}; |
| |
| if (ic->ic_get_phy_stats |
| && !ic->ic_get_phy_stats(ic->ic_dev, ic, &phy_stats, 0)) { |
| uint32_t sp_errs; |
| uint32_t lp_errs; |
| int32_t idx = ieee80211_scs_get_channel_index(ic, ic->ic_curchan); |
| if (idx < 0 || idx >= ARRAY_SIZE(scs_info_lh->stats_history.sp_errs)) |
| goto compare_end; |
| ieee80211_scs_update_cochan_filter_history(ic, &phy_stats, |
| &scs_info_lh->stats_history, idx); |
| sp_errs = ieee80211_scs_get_median(ic, &scs_info_lh->stats_history.sp_errs[idx]); |
| lp_errs = ieee80211_scs_get_median(ic, &scs_info_lh->stats_history.lp_errs[idx]); |
| ic->ic_scs.scs_sp_err_smthed = IEEE80211_SCS_SMOOTH(ic->ic_scs.scs_sp_err_smthed, |
| sp_errs, ic->ic_scs.scs_pmbl_err_smth_fctr); |
| ic->ic_scs.scs_lp_err_smthed = IEEE80211_SCS_SMOOTH(ic->ic_scs.scs_lp_err_smthed, |
| lp_errs, ic->ic_scs.scs_pmbl_err_smth_fctr); |
| ieee80211_scs_contribute_randomness(phy_stats.cca_int, phy_stats.cnt_lp_fail, |
| phy_stats.cnt_sp_fail); |
| } |
| |
| /* Copy scs info into a local structure so MuC can continue fill it */ |
| scs_info_read = kmalloc(sizeof(*scs_info_read), GFP_ATOMIC); |
| if (!scs_info_read) { |
| SCSDBG(SCSLOG_NOTICE, "SCS info allocation failed\n"); |
| goto compare_end; |
| } |
| memcpy((void *)scs_info_read, (void *)&scs_info_lh->scs_info[scs_info_lh->valid_index], |
| sizeof(*scs_info_read)); |
| if (scs_info_read->cca_try == 0) { |
| /* make sure cross channel switching stats are cleared */ |
| clean_level = IEEE80211_SCS_STATE_MEASUREMENT_CHANGE_CLEAN; |
| goto compare_end_free_local_info; |
| } |
| |
| /* Read and process off channel stats */ |
| if (scs_info_read->oc_info_count) { |
| int i; |
| uint32_t pmbl; |
| struct qtn_scs_oc_info * p_oc_info; |
| struct ieee80211_channel *off_chan; |
| |
| for (i = 0; i < scs_info_read->oc_info_count; i++) { |
| p_oc_info = &scs_info_read->oc_info[i]; |
| if (p_oc_info->off_chan_cca_try_cnt) { |
| SCSDBG(SCSLOG_INFO, "OC: Channel=%d, bw=%d cca_busy=%d, " |
| "cca_smpl=%d, cca_try=%d, bcn_rcvd=%d, " |
| "crc_err=%d, sp_err=%d, lp_err=%d, " |
| "cca_pri=%d, cca_sec=%d, cca_sec40=%d\n", |
| p_oc_info->off_channel, |
| p_oc_info->off_chan_bw_sel, |
| p_oc_info->off_chan_cca_busy, |
| p_oc_info->off_chan_cca_sample_cnt, |
| p_oc_info->off_chan_cca_try_cnt, |
| p_oc_info->off_chan_beacon_recvd, |
| p_oc_info->off_chan_crc_errs, |
| p_oc_info->off_chan_sp_errs, |
| p_oc_info->off_chan_lp_errs, |
| p_oc_info->off_chan_cca_pri, |
| p_oc_info->off_chan_cca_sec, |
| p_oc_info->off_chan_cca_sec40); |
| off_chan = ieee80211_find_channel_by_ieee(ic, p_oc_info->off_channel); |
| if (off_chan == NULL) { |
| continue; |
| } |
| ieee80211_scs_scale_offchan_data(ic, p_oc_info); |
| raw_cca_intf = p_oc_info->off_chan_cca_busy; |
| compound_cca_intf = ieee80211_scs_fix_cca_intf(ic, NULL, raw_cca_intf, |
| p_oc_info->off_chan_sp_errs, p_oc_info->off_chan_lp_errs); |
| pmbl = (p_oc_info->off_chan_sp_errs * ic->ic_scs.scs_sp_wf + |
| p_oc_info->off_chan_lp_errs * ic->ic_scs.scs_lp_wf) / 100; |
| memset(&intf_params, 0, sizeof(intf_params)); |
| intf_params.chan = off_chan; |
| intf_params.chan_bw = p_oc_info->off_chan_bw_sel; |
| intf_params.cca_intf = compound_cca_intf; |
| intf_params.pmbl_err = pmbl; |
| intf_params.cca_dur = IEEE80211_SCS_CCA_INTF_SCALE; |
| intf_params.cca_pri = p_oc_info->off_chan_cca_pri; |
| intf_params.cca_sec = p_oc_info->off_chan_cca_sec; |
| intf_params.cca_sec40 = p_oc_info->off_chan_cca_sec40; |
| ieee80211_scs_update_chans_cca_intf(ic, &intf_params, IEEE80211_SCS_OFFCHAN, NULL); |
| } |
| } |
| } |
| |
| /* Scale co-channel data */ |
| ieee80211_scs_scale_cochan_data(ic, scs_info_read); |
| |
| SCSDBG(SCSLOG_INFO, "cca_try=%u, cca_idle=%u cca_busy=%u cca_intf=%u cca_tx=%u tx_ms=%u rx_ms=%u\n", |
| scs_info_read->cca_try, |
| scs_info_read->cca_idle, |
| scs_info_read->cca_busy, |
| scs_info_read->cca_interference, |
| scs_info_read->cca_tx, |
| scs_info_read->tx_usecs / 1000, |
| scs_info_read->rx_usecs / 1000); |
| |
| /* Dynamic peer BW scheme based on interference on secondary 40 MHz */ |
| if ((ieee80211_get_bw(ic) == BW_HT80) && ic->ic_opmode_bw_switch_en) { |
| vap = TAILQ_FIRST(&ic->ic_vaps); |
| ieee80211_param_from_qdrv(vap, IEEE80211_PARAM_GET_CCA_STATS, &cca_sec40, NULL, 0); |
| |
| if ((opmode_bw != QTN_BW_40M) && (cca_sec40 > OPMODE_PEER_BW_SW_CCA40_TH1)) { |
| //Switch to 40 MHz |
| ieee80211_send_vht_opmode_to_all(ic, QTN_BW_40M); |
| opmode_bw = QTN_BW_40M; |
| SCSDBG(SCSLOG_INFO,"CCA_SEC40 = %d: request peer to switch to 40 MHz\n", cca_sec40); |
| debounce = 0; |
| } else if ((opmode_bw != QTN_BW_80M) && (cca_sec40 < OPMODE_PEER_BW_SW_CCA40_TH2)) { |
| if (debounce == 1) { |
| //Switch to 80 MHz |
| ieee80211_send_vht_opmode_to_all(ic, QTN_BW_80M); |
| opmode_bw = QTN_BW_80M; |
| SCSDBG(SCSLOG_INFO,"CCA_SEC40 = %d: request peer to switch to 80 MHz\n", cca_sec40); |
| debounce = 0; |
| } else { |
| debounce = 1; |
| } |
| } else { |
| debounce = 0; |
| } |
| } |
| |
| if (ic->ic_scs.scs_debug_enable >= SCSLOG_VERBOSE) { |
| int node_index; |
| |
| for (node_index = 0; node_index < scs_info_read->scs_vsp_info.num_of_assoc; node_index++) { |
| printk("SCS: AssocID = 0x%04X, tx_time = %u, rx_time =%u\n", |
| scs_info_read->scs_vsp_info.scs_vsp_node_stats[node_index].ni_associd, |
| scs_info_read->scs_vsp_info.scs_vsp_node_stats[node_index].tx_usecs / 1000, |
| scs_info_read->scs_vsp_info.scs_vsp_node_stats[node_index].rx_usecs / 1000); |
| } |
| } |
| |
| raw_cca_intf = scs_info_read->cca_interference; |
| if ((ic->ic_opmode == IEEE80211_M_HOSTAP) && ieee80211_scs_tdls_link_is_existing(ic, NULL)) { |
| tdls_update_failed = ieee80211_scs_update_tdls_link_time(ic); |
| compound_cca_intf = ieee80211_scs_smooth_ap_cca_intf_time(ic, raw_cca_intf, |
| &stats_unstable); |
| } else { |
| compound_cca_intf = ieee80211_scs_fix_cca_intf(ic, NULL, raw_cca_intf, |
| ic->ic_scs.scs_sp_err_smthed, ic->ic_scs.scs_lp_err_smthed); |
| } |
| pmbl_err = (ic->ic_scs.scs_sp_err_smthed * ic->ic_scs.scs_sp_wf + |
| ic->ic_scs.scs_lp_err_smthed * ic->ic_scs.scs_lp_wf) / 100; |
| |
| SCSDBG(SCSLOG_INFO, "current pmbl error = %u %u, smoothed = %u %u," |
| " raw_cca_intf = %u, comp_cca_intf = %u\n", |
| phy_stats.cnt_sp_fail, phy_stats.cnt_lp_fail, |
| ic->ic_scs.scs_sp_err_smthed, ic->ic_scs.scs_lp_err_smthed, |
| raw_cca_intf, compound_cca_intf); |
| |
| /* update smoothed free airtime */ |
| if (ic->ic_scs.scs_cca_idle_smthed) { |
| ic->ic_scs.scs_cca_idle_smthed = IEEE80211_SCS_SMOOTH(ic->ic_scs.scs_cca_idle_smthed, |
| scs_info_read->cca_idle, ic->ic_scs.scs_cca_idle_smth_fctr); |
| } else { |
| ic->ic_scs.scs_cca_idle_smthed = scs_info_read->cca_idle; |
| } |
| SCSDBG(SCSLOG_INFO, "cca_idle_smthed %u\n", ic->ic_scs.scs_cca_idle_smthed); |
| |
| if (ic->ic_opmode == IEEE80211_M_HOSTAP) { |
| int cur_bw; |
| |
| ieee80211_scs_collect_node_atten(ic); |
| cur_bw = ieee80211_get_bw(ic); |
| |
| /* clear current cca intf at first */ |
| memset(&intf_params, 0, sizeof(intf_params)); |
| intf_params.chan = ic->ic_curchan; |
| intf_params.chan_bw = cur_bw; |
| intf_params.cca_intf = SCS_CCA_INTF_INVALID; |
| intf_params.pmbl_err = 0; |
| intf_params.cca_dur = IEEE80211_SCS_CCA_INTF_SCALE; |
| ieee80211_scs_update_chans_cca_intf(ic, &intf_params, IEEE80211_SCS_COCHAN, NULL); |
| /* update cca intf stats */ |
| intf_params.cca_intf = compound_cca_intf; |
| intf_params.pmbl_err = pmbl_err; |
| intf_params.cca_pri = scs_info_read->cca_pri; |
| intf_params.cca_sec = scs_info_read->cca_sec20; |
| intf_params.cca_sec40 = scs_info_read->cca_sec40; |
| ieee80211_scs_update_chans_cca_intf(ic, &intf_params, IEEE80211_SCS_COCHAN, NULL); |
| |
| /* update tx/rx time stats */ |
| as->as_tx_ms = scs_info_read->tx_usecs / 1000; |
| as->as_rx_ms = scs_info_read->rx_usecs / 1000; |
| as->as_tx_ms_smth = IEEE80211_SCS_SMOOTH(as->as_tx_ms_smth, as->as_tx_ms, |
| ic->ic_scs.scs_as_tx_time_smth_fctr); |
| as->as_rx_ms_smth = IEEE80211_SCS_SMOOTH(as->as_rx_ms_smth, as->as_rx_ms, |
| ic->ic_scs.scs_as_rx_time_smth_fctr); |
| SCSDBG(SCSLOG_INFO, "AS tx rx = %u %u, smoothed = %u %u\n", |
| as->as_tx_ms, as->as_rx_ms, as->as_tx_ms_smth, as->as_rx_ms_smth); |
| } |
| |
| cc_flag = ieee80211_is_cc_required(ic, compound_cca_intf, |
| ic->ic_scs.scs_cca_idle_smthed, pmbl_err); |
| if (cc_flag) { |
| if (ic->ic_opmode == IEEE80211_M_HOSTAP) { |
| int is_wds_rbs; |
| cc_flag = ieee80211_scs_collect_ranking_stats(ic, scs_info_read, cc_flag, |
| compound_cca_intf); |
| if (ic->ic_scs.scs_debug_enable) |
| ieee80211_scs_show_ranking_stats(ic, 1, 0); |
| |
| is_wds_rbs = ieee80211_scs_is_wds_rbs_node(ic); |
| |
| /* |
| * Pick a channel from DFS and Non-DFS set of channels; |
| * Consider channel margins as well while we pick the channel |
| */ |
| new_chan = ieee80211_scs_pick_channel(ic, IEEE80211_SCS_PICK_AVAILABLE_ANY_CHANNEL, cc_flag); |
| |
| if (!is_wds_rbs) { |
| if (!cc_flag) { |
| SCSDBG(SCSLOG_NOTICE, "all channel change request conditions are cleared\n"); |
| goto compare_end_free_local_info; |
| } |
| |
| if (new_chan == SCS_BEST_CHAN_INVALID) { |
| ieee80211_scs_update_dfs_reentry(ic, cc_flag, &dfs_reentry_clear); |
| goto compare_end_free_local_info; |
| } |
| |
| if (ic->ic_scs.scs_report_only) { |
| SCSDBG(SCSLOG_NOTICE, "channel change is disabled under report only mode\n"); |
| goto compare_end_free_local_info; |
| } |
| |
| if (ieee80211_is_cac_in_progress(ic)) { |
| SCSDBG(SCSLOG_NOTICE, "Channel change is disabled during CAC\n"); |
| goto compare_end_free_local_info; |
| } |
| |
| if ((cc_flag == IEEE80211_SCS_SELF_CCA_CC) && |
| ic->ic_get_cca_adjusting_status()) { |
| SCSDBG(SCSLOG_NOTICE, "Self channel change is paused while adjusting cca threshold\n"); |
| goto compare_end_free_local_info; |
| } |
| |
| if (tdls_update_failed || stats_unstable) |
| goto compare_end_free_local_info; |
| |
| if (ic->ic_scs.scs_burst_enable && ieee80211_scs_add_event_scs_burst_queue(ic)) { |
| SCSDBG(SCSLOG_NOTICE, "Channel change is paused beacause of bursting\n"); |
| |
| goto compare_end_free_local_info; |
| } |
| ic->ic_csw_reason = CSW_REASON_SET_SCS_FLAG(cc_flag, IEEE80211_CSW_REASON_SCS); |
| if (!ieee80211_scs_change_channel(ic, new_chan)) { |
| int curchan_ieee = ic->ic_curchan->ic_ieee; |
| |
| printk("SCS: Switching to chan %d, reason %x," |
| " cca_intf %u %u, pmbl_ap %u %u, pmbl_sta %u %u, cca_idle %u\n", |
| new_chan, cc_flag, as->as_cca_intf[curchan_ieee], as->as_cca_intf[new_chan], |
| as->as_pmbl_err_ap[curchan_ieee], as->as_pmbl_err_ap[new_chan], |
| as->as_pmbl_err_sta[curchan_ieee], as->as_pmbl_err_sta[new_chan], |
| ic->ic_scs.scs_cca_idle_smthed); |
| } |
| } else { |
| /* In RBS mode - inform the MBS AP to change the channel */ |
| /* WDS Link to MBS? */ |
| if (new_chan == SCS_BEST_CHAN_INVALID) { |
| SCSDBG(SCSLOG_NOTICE, "new channel recommendation %d\n", new_chan); |
| } |
| |
| SCSDBG(SCSLOG_NOTICE, "SCS: send busy_fraction %u with cca_intf %u to AP, pmbl_error=%u %u\n", |
| (raw_cca_intf * IEEE80211_11K_CCA_INTF_SCALE |
| / IEEE80211_SCS_CCA_INTF_SCALE), |
| raw_cca_intf, ic->ic_scs.scs_sp_err_smthed, ic->ic_scs.scs_lp_err_smthed); |
| |
| vap = TAILQ_FIRST(&ic->ic_vaps); |
| TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { |
| if (IEEE80211_VAP_WDS_IS_RBS(vap)) { |
| struct ieee80211_node *ni = NULL; |
| uint64_t tsf = 0; |
| uint16_t others_time; |
| |
| ni = ieee80211_get_wds_peer_node_ref(vap); |
| if (ni) { |
| others_time = ni->ni_others_time; |
| SCSDBG(SCSLOG_NOTICE, |
| "RBS: allnodes(tx+rx)- wds %u\n", others_time); |
| /* here replace the old csa mgmt packet sending*/ |
| ic->ic_get_tsf(&tsf); |
| ieee80211_send_action_cca_report(ni, 0, |
| (uint16_t)raw_cca_intf, |
| tsf, (uint16_t)scs_info_read->cca_try, |
| ic->ic_scs.scs_sp_err_smthed, |
| ic->ic_scs.scs_lp_err_smthed, |
| others_time, NULL, 0); |
| ieee80211_send_action_fat_report(ni, 0, |
| (uint16_t)raw_cca_intf, |
| tsf, (uint16_t)scs_info_read->cca_try, |
| (uint16_t)scs_info_read->cca_idle); |
| ieee80211_free_node(ni); |
| } else { |
| printk("%s: RBS: WDS Peer Node is NULL\n",__func__); |
| } |
| } |
| } |
| } |
| } else { |
| /* In STA mode - inform AP to change the channel */ |
| SCSDBG(SCSLOG_NOTICE, "STA: report SCS measurements to AP\n"); |
| if (!ic->ic_nonqtn_sta) { |
| tdls_stats_buf = kmalloc(tdls_stats_buf_len, GFP_ATOMIC); |
| if (!tdls_stats_buf) { |
| SCSDBG(SCSLOG_NOTICE, "TDLS stats buffer allocation failed\n"); |
| goto compare_end_free_local_info; |
| } |
| |
| TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { |
| memset(tdls_stats_buf, 0, tdls_stats_buf_len); |
| extra_ie_len = ieee80211_scs_add_tdls_stats_ie(vap, scs_info_read, |
| tdls_stats_buf, tdls_stats_buf_len); |
| /* STA should be associated */ |
| if ((vap->iv_state == IEEE80211_S_RUN) && vap->iv_bss) { |
| uint64_t tsf = 0; |
| struct ieee80211_node *ni = vap->iv_bss; |
| |
| ic->ic_get_tsf(&tsf); |
| SCSDBG(SCSLOG_NOTICE, "send busy_fraction %u with cca_intf %u to AP, pmbl_error=%u %u\n", |
| (raw_cca_intf * IEEE80211_11K_CCA_INTF_SCALE / IEEE80211_SCS_CCA_INTF_SCALE), |
| raw_cca_intf, ic->ic_scs.scs_sp_err_smthed, ic->ic_scs.scs_lp_err_smthed); |
| /* here replace the old csa mgmt packet sending*/ |
| ieee80211_send_action_cca_report(ni, 0, (uint16_t)raw_cca_intf, tsf, |
| (uint16_t)scs_info_read->cca_try, ic->ic_scs.scs_sp_err_smthed, |
| ic->ic_scs.scs_lp_err_smthed, 0, tdls_stats_buf, extra_ie_len); |
| ieee80211_send_action_fat_report(ni, 0, (uint16_t)raw_cca_intf, |
| tsf, (uint16_t)scs_info_read->cca_try, |
| (uint16_t)scs_info_read->cca_idle); |
| } |
| } |
| |
| if(tdls_stats_buf) |
| kfree(tdls_stats_buf); |
| } |
| } |
| } |
| |
| compare_end_free_local_info: |
| kfree(scs_info_read); |
| compare_end: |
| if (ic->ic_scs.scs_burst_enable) |
| ieee80211_scs_update_scs_burst_queue(ic); |
| |
| ieee80211_scs_clean_stats(ic, clean_level, dfs_reentry_clear); |
| |
| mod_timer(&ic->ic_scs.scs_compare_timer, |
| jiffies + (ic->ic_scs.scs_cca_sample_dur * HZ)); |
| } |
| |
| static void |
| ieee80211_scs_switch_channel_manually(struct ieee80211com *ic, int pick_flags) |
| { |
| int new_chan; |
| |
| if (!ic->ic_scs.scs_enable) { |
| SCSDBG(SCSLOG_CRIT, "Stop switching channel because SCS is disabled!\n"); |
| return; |
| } |
| |
| if (ic->ic_opmode == IEEE80211_M_HOSTAP) { |
| ieee80211_scs_collect_node_atten(ic); |
| |
| if (ic->ic_scs.scs_debug_enable) { |
| ieee80211_scs_show_ranking_stats(ic, 1, 0); |
| } |
| |
| /* |
| * Pick a channel from DFS and Non-DFS sets; |
| * If the picked channel is non-DFS, OCAC performs off-channel CAC for DFS channel; |
| * If the picked channel is DFS, OCAC will not kickin |
| */ |
| new_chan = ieee80211_scs_pick_channel(ic, |
| pick_flags, |
| IEEE80211_SCS_NA_CC); |
| if (new_chan == SCS_BEST_CHAN_INVALID) { |
| goto sc_err; |
| } |
| |
| ic->ic_csw_reason = IEEE80211_CSW_REASON_SCS; |
| if (!ieee80211_scs_change_channel(ic, new_chan)) { |
| SCSDBG(SCSLOG_CRIT, "Switching to channel %d manually\n", new_chan); |
| } |
| } else { |
| SCSDBG(SCSLOG_CRIT, "Support switch channel manually on AP side only now!\n"); |
| } |
| |
| return; |
| |
| sc_err: |
| SCSDBG(SCSLOG_CRIT, "Switch channel manually error!\n"); |
| } |
| |
| void ieee80211_scs_start_comparing_timer(struct ieee80211com *ic) |
| { |
| init_timer(&ic->ic_scs.scs_compare_timer); |
| ic->ic_scs.scs_compare_timer.function = ieee80211_scs_start_compare; |
| ic->ic_scs.scs_compare_timer.data = (unsigned long) ic; |
| ic->ic_scs.scs_compare_timer.expires = jiffies + IEEE80211_SCS_COMPARE_INIT_TIMER * HZ; |
| add_timer(&ic->ic_scs.scs_compare_timer); |
| } |
| |
| static int |
| ieee80211_wireless_scs_stats_task_start(struct ieee80211vap *vap, uint8_t start) |
| { |
| struct ieee80211com *ic = vap->iv_ic; |
| |
| if (start && !ic->ic_scs.scs_stats_on) { |
| ieee80211_scs_clean_stats(ic, IEEE80211_SCS_STATE_RESET, 0); |
| ieee80211_scs_start_comparing_timer(ic); |
| ic->ic_scs.scs_stats_on = 1; |
| ieee80211_wireless_scs_msg_send(vap, "SCS: stats task is started"); |
| } else if (!start && ic->ic_scs.scs_stats_on) { |
| del_timer(&ic->ic_scs.scs_compare_timer); |
| ic->ic_scs.scs_stats_on = 0; |
| ieee80211_wireless_scs_msg_send(vap, "SCS: stats task is stopped"); |
| } |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_wireless_scs_smpl_task_start(struct ieee80211vap *vap, uint8_t start) |
| { |
| #define IEEE80211_SCS_BUF_LEN 256 |
| char msg_buf[IEEE80211_SCS_BUF_LEN]; |
| struct ieee80211com *ic = vap->iv_ic; |
| |
| if (start && ((ic->ic_scs.scs_sample_intv < IEEE80211_SCS_SMPL_INTV_MIN) || |
| (ic->ic_scs.scs_sample_intv > IEEE80211_SCS_SMPL_INTV_MAX))) { |
| return -1; |
| } |
| |
| if (start) { |
| cancel_delayed_work_sync(&ic->ic_scs_sample_work); |
| snprintf(msg_buf, sizeof(msg_buf), |
| "SCS: channel sampling started - interval is %u seconds", |
| ic->ic_scs.scs_sample_intv); |
| ieee80211_wireless_scs_msg_send(vap, msg_buf); |
| INIT_DELAYED_WORK(&ic->ic_scs_sample_work, |
| ieee80211_wireless_scs_sampling_task); |
| if (ic->ic_opmode == IEEE80211_M_HOSTAP) { |
| schedule_delayed_work(&ic->ic_scs_sample_work, (HZ / 2)); |
| } |
| } else { |
| ieee80211_wireless_scs_msg_send(vap, "SCS: channel sampling disabled"); |
| cancel_delayed_work_sync(&ic->ic_scs_sample_work); |
| } |
| |
| return 0; |
| } |
| |
| static void |
| ieee80211_wireless_scs_report_show(struct ieee80211com *ic, uint16_t param) |
| { |
| struct shared_params *sp = qtn_mproc_sync_shared_params_get(); |
| struct qtn_scs_info *scs_info_read = NULL; |
| struct ieee80211vap *vap; |
| uint32_t pmbl_fail; |
| |
| /* CCA information cannot match a certain channel under scan state */ |
| TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { |
| if (vap->iv_state == IEEE80211_S_RUN) { |
| break; |
| } |
| } |
| if (vap == NULL) { |
| printk("SCS: No VAP in running state, no report available\n"); |
| return; |
| } |
| |
| if (param == IEEE80211_SCS_CHAN_ALL) { |
| ieee80211_show_initial_ranking_stats(ic); |
| ieee80211_scs_show_ranking_stats(ic, 0, 1); |
| } else if (param == IEEE80211_SCS_CHAN_CURRENT) { |
| if (ic->ic_scs.scs_stats_on) { |
| /* Copy scs info into a local structure so MuC can continue to fill it in */ |
| scs_info_read = kmalloc(sizeof(*scs_info_read), GFP_KERNEL); |
| if (!scs_info_read) { |
| printk("SCS: info allocation failed\n"); |
| return; |
| } |
| memcpy((void *)scs_info_read, &sp->scs_info_lhost->scs_info[sp->scs_info_lhost->valid_index], |
| sizeof(*scs_info_read)); |
| if (scs_info_read->cca_try) { |
| ieee80211_scs_scale_cochan_data(ic, scs_info_read); |
| pmbl_fail = (ic->ic_scs.scs_sp_err_smthed * ic->ic_scs.scs_sp_wf + |
| ic->ic_scs.scs_lp_err_smthed * ic->ic_scs.scs_lp_wf) / 100; |
| printk("SCS: current channel %d, cca_try=%u, cca_idle=%u cca_busy=%u cca_intf=%u cca_tx=%u tx_ms=%u rx_ms=%u" |
| " pmbl_cnt=%u\n", |
| ic->ic_curchan->ic_ieee, |
| scs_info_read->cca_try, |
| scs_info_read->cca_idle, |
| scs_info_read->cca_busy, |
| scs_info_read->cca_interference, |
| scs_info_read->cca_tx, |
| scs_info_read->tx_usecs / 1000, |
| scs_info_read->rx_usecs / 1000, |
| pmbl_fail); |
| } else { |
| printk("Current channel report is temporarily not available, please try later\n"); |
| } |
| kfree(scs_info_read); |
| } else { |
| printk("SCS is disabled, no report available for current channel\n"); |
| } |
| } |
| } |
| |
| static void |
| ieee80211_wireless_scs_get_internal_stats(struct ieee80211com *ic, uint16_t param) |
| { |
| int i; |
| |
| if (!ic->ic_scs.scs_stats_on) { |
| printk("SCS stats is off, no stats available\n"); |
| return; |
| } |
| |
| printk("SCS lhost stats: off-channel sample counter:\n"); |
| for (i = 0; i < IEEE80211_SCS_CNT_MAX; i++) { |
| printk("NO.%d=%u\n", i, ic->ic_scs.scs_cnt[i]); |
| } |
| } |
| |
| int |
| ieee80211_param_scs_set(struct net_device *dev, struct ieee80211vap *vap, u_int32_t value) |
| { |
| struct ieee80211com *ic = vap->iv_ic; |
| uint16_t cmd = value >> IEEE80211_SCS_COMMAND_S; |
| uint16_t arg = value & IEEE80211_SCS_VALUE_M; |
| uint8_t u8_arg0 = arg >> 8; |
| uint8_t u8_arg1 = arg & 0xFF; |
| #if TOPAZ_FPGA_PLATFORM |
| printk("SCS is not supported yet on Topaz\n"); |
| return -1; |
| #endif |
| |
| if (cmd >= IEEE80211_SCS_SET_MAX) { |
| printk(KERN_WARNING "%s: SCS: invalid config cmd %u, arg=%u\n", |
| dev->name, cmd, arg); |
| return -1; |
| } |
| |
| SCSDBG(SCSLOG_INFO, "set param %u to value 0x%x\n", cmd, arg); |
| |
| switch (cmd) { |
| case IEEE80211_SCS_SET_ENABLE: |
| if (arg > 1) { |
| return -1; |
| } |
| |
| if (ic->ic_scs.scs_enable != arg) { |
| printk("%sabling SCS\n", arg ? "En" : "Dis"); |
| ic->ic_scs.scs_enable = arg; |
| if (ic->ic_scs.scs_enable) { |
| if (ieee80211_wireless_scs_stats_task_start(vap, 1) < 0) { |
| return -1; |
| } |
| } |
| /* SCS off channel sampling follows SCS */ |
| if (!ic->ic_scs.scs_smpl_enable != !arg) { |
| if (ieee80211_wireless_scs_smpl_task_start(vap, arg) < 0) { |
| return -1; |
| } else { |
| ic->ic_scs.scs_smpl_enable = arg; |
| } |
| } |
| } else { |
| return 0; |
| } |
| break; |
| case IEEE80211_SCS_SET_DEBUG_ENABLE: |
| if (arg > 3) { |
| return -1; |
| } |
| if( ic->ic_scs.scs_debug_enable != arg) { |
| ic->ic_scs.scs_debug_enable = arg; |
| } |
| break; |
| case IEEE80211_SCS_SET_SAMPLE_ENABLE: |
| if (arg > 1) { |
| return -1; |
| } |
| |
| if (ic->ic_scs.scs_smpl_enable != arg) { |
| if (ieee80211_wireless_scs_smpl_task_start(vap, arg) < 0) { |
| return -1; |
| } else { |
| ic->ic_scs.scs_smpl_enable = arg; |
| } |
| } |
| break; |
| case IEEE80211_SCS_SET_SAMPLE_DWELL_TIME: |
| if (arg < IEEE80211_SCS_SMPL_DWELL_TIME_MIN || |
| arg > IEEE80211_SCS_SMPL_DWELL_TIME_MAX) { |
| return -1; |
| } |
| if (ic->ic_scs.scs_smpl_dwell_time != arg) { |
| ic->ic_scs.scs_smpl_dwell_time = arg; |
| } |
| break; |
| case IEEE80211_SCS_SET_SAMPLE_INTERVAL: |
| if (arg < IEEE80211_SCS_SMPL_INTV_MIN || |
| arg > IEEE80211_SCS_SMPL_INTV_MAX) { |
| return -1; |
| } |
| ic->ic_scs.scs_sample_intv = arg; |
| break; |
| case IEEE80211_SCS_SET_SAMPLE_TYPE: |
| if (arg & (~QTN_OFF_CHAN_FLAG_MASK)) |
| return -1; |
| if ((arg & -arg) != arg) |
| return -1; |
| ic->ic_scs.scs_sample_type = arg; |
| break; |
| case IEEE80211_SCS_SET_THRSHLD_SMPL_PKTNUM: |
| ic->ic_scs.scs_thrshld_smpl_pktnum = arg; |
| break; |
| case IEEE80211_SCS_SET_THRSHLD_SMPL_AIRTIME: |
| ic->ic_scs.scs_thrshld_smpl_airtime = arg; |
| break; |
| case IEEE80211_SCS_SET_THRSHLD_ATTEN_INC: |
| if (arg > IEEE80211_SCS_THRSHLD_ATTEN_INC_MAX) { |
| return -1; |
| } |
| SCSDBG(SCSLOG_NOTICE, "attenuation increase threshold change from %u to %u\n", |
| ic->ic_scs.scs_thrshld_atten_inc, arg); |
| ic->ic_scs.scs_thrshld_atten_inc = arg; |
| break; |
| case IEEE80211_SCS_SET_THRSHLD_DFS_REENTRY: |
| SCSDBG(SCSLOG_NOTICE, "DFS reentry threshold change from %u to %u\n", |
| ic->ic_scs.scs_thrshld_dfs_reentry, arg); |
| ic->ic_scs.scs_thrshld_dfs_reentry = arg; |
| break; |
| case IEEE80211_SCS_SET_THRSHLD_DFS_REENTRY_INTF: |
| printk("DFS reentry cca intf threshold change from %u to %u\n", |
| ic->ic_scs.scs_thrshld_dfs_reentry_intf, arg); |
| ic->ic_scs.scs_thrshld_dfs_reentry_intf = arg; |
| break; |
| case IEEE80211_SCS_SET_THRSHLD_DFS_REENTRY_MINRATE: |
| SCSDBG(SCSLOG_NOTICE, "DFS reentry minrate threshold change from %u to %u, unit 100kbps\n", |
| ic->ic_scs.scs_thrshld_dfs_reentry_minrate, arg); |
| ic->ic_scs.scs_thrshld_dfs_reentry_minrate = arg; |
| break; |
| case IEEE80211_SCS_SET_THRSHLD_LOAD: |
| if (arg > IEEE80211_SCS_THRSHLD_LOADED_MAX) |
| return -1; |
| SCSDBG(SCSLOG_NOTICE, "traffic load threshold change from %u to %u\n", |
| ic->ic_scs.scs_thrshld_loaded, arg); |
| ic->ic_scs.scs_thrshld_loaded = arg; |
| break; |
| case IEEE80211_SCS_SET_THRSHLD_AGING_NOR: |
| SCSDBG(SCSLOG_NOTICE, "normal aging threshold change from %u to %u minutes\n", |
| ic->ic_scs.scs_thrshld_aging_nor, arg); |
| ic->ic_scs.scs_thrshld_aging_nor = arg; |
| break; |
| case IEEE80211_SCS_SET_THRSHLD_AGING_DFSREENT: |
| SCSDBG(SCSLOG_NOTICE, "dfs re-entry aging threshold change from %u to %u minutes\n", |
| ic->ic_scs.scs_thrshld_aging_dfsreent, arg); |
| ic->ic_scs.scs_thrshld_aging_dfsreent = arg; |
| break; |
| case IEEE80211_SCS_SET_CCA_IDLE_THRSHLD: |
| SCSDBG(SCSLOG_NOTICE, "cca idle threshold change from %u to %u\n", |
| ic->ic_scs.scs_cca_idle_thrshld, arg); |
| ic->ic_scs.scs_cca_idle_thrshld = arg; |
| break; |
| case IEEE80211_SCS_SET_PMBL_ERR_THRSHLD: |
| SCSDBG(SCSLOG_NOTICE, "pmbl error threshold change from %u to %u\n", |
| ic->ic_scs.scs_pmbl_err_thrshld, arg); |
| ic->ic_scs.scs_pmbl_err_thrshld = arg; |
| break; |
| case IEEE80211_SCS_SET_CCA_INTF_LO_THR: |
| SCSDBG(SCSLOG_NOTICE, "cca intf low threshold change from %u to %u\n", |
| ic->ic_scs.scs_cca_intf_lo_thrshld, arg); |
| ic->ic_scs.scs_cca_intf_lo_thrshld = arg; |
| break; |
| case IEEE80211_SCS_SET_CCA_INTF_HI_THR: |
| SCSDBG(SCSLOG_NOTICE, "cca intf high threshold change from %u to %u\n", |
| ic->ic_scs.scs_cca_intf_hi_thrshld, arg); |
| ic->ic_scs.scs_cca_intf_hi_thrshld = arg; |
| break; |
| case IEEE80211_SCS_SET_CCA_INTF_RATIO: |
| SCSDBG(SCSLOG_NOTICE, "cca intf ratio threshold change from %u to %u\n", |
| ic->ic_scs.scs_cca_intf_ratio, arg); |
| ic->ic_scs.scs_cca_intf_ratio = arg; |
| break; |
| case IEEE80211_SCS_SET_CCA_INTF_DFS_MARGIN: |
| SCSDBG(SCSLOG_NOTICE, "cca intf dfs margin change from %u to %u\n", |
| ic->ic_scs.scs_cca_intf_dfs_margin, arg); |
| ic->ic_scs.scs_cca_intf_dfs_margin = arg; |
| break; |
| case IEEE80211_SCS_SET_CCA_SMPL_DUR: |
| if (arg < IEEE80211_SCS_CCA_DUR_MIN || |
| arg > IEEE80211_SCS_CCA_DUR_MAX) { |
| return -1; |
| } |
| ic->ic_scs.scs_cca_sample_dur = arg; |
| ieee80211_scs_clean_stats(ic, IEEE80211_SCS_STATE_MEASUREMENT_CHANGE_CLEAN, 0); |
| break; |
| case IEEE80211_SCS_SET_REPORT_ONLY: |
| ic->ic_scs.scs_report_only = arg; |
| break; |
| case IEEE80211_SCS_GET_REPORT: |
| ieee80211_wireless_scs_report_show(ic, arg); |
| break; |
| case IEEE80211_SCS_GET_INTERNAL_STATS: |
| ieee80211_wireless_scs_get_internal_stats(ic, arg); |
| break; |
| case IEEE80211_SCS_SET_CCA_INTF_SMTH_FCTR: |
| if ((u8_arg0 > IEEE80211_CCA_INTF_SMTH_FCTR_MAX) || |
| (u8_arg1 > IEEE80211_CCA_INTF_SMTH_FCTR_MAX)) { |
| return -1; |
| } |
| ic->ic_scs.scs_cca_intf_smth_fctr[SCS_CCA_INTF_SMTH_FCTR_NOXP] = u8_arg0; |
| ic->ic_scs.scs_cca_intf_smth_fctr[SCS_CCA_INTF_SMTH_FCTR_XPED] = u8_arg1; |
| break; |
| case IEEE80211_SCS_RESET_RANKING_TABLE: |
| ieee80211_scs_clean_stats(ic, IEEE80211_SCS_STATE_RESET, 0); |
| break; |
| case IEEE80211_SCS_SET_CHAN_MTRC_MRGN: |
| if (arg > IEEE80211_SCS_CHAN_MTRC_MRGN_MAX) { |
| return -1; |
| } |
| SCSDBG(SCSLOG_NOTICE, "chan metric margin change from %u to %u\n", |
| ic->ic_scs.scs_chan_mtrc_mrgn, arg); |
| ic->ic_scs.scs_chan_mtrc_mrgn = arg; |
| break; |
| case IEEE80211_SCS_SET_LEAVE_DFS_CHAN_MTRC_MRGN: |
| if (arg > IEEE80211_SCS_CHAN_MTRC_MRGN_MAX) { |
| return -1; |
| } |
| SCSDBG(SCSLOG_NOTICE, "Leave DFS chan metric margin change from %u to %u\n", |
| ic->ic_scs.scs_leavedfs_chan_mtrc_mrgn, arg); |
| ic->ic_scs.scs_leavedfs_chan_mtrc_mrgn = arg; |
| break; |
| case IEEE80211_SCS_SET_RSSI_SMTH_FCTR: |
| if ((u8_arg0 > IEEE80211_SCS_RSSI_SMTH_FCTR_MAX) || |
| (u8_arg1 > IEEE80211_SCS_RSSI_SMTH_FCTR_MAX)) { |
| return -1; |
| } |
| SCSDBG(SCSLOG_NOTICE, "rssi smoothing factor(up/down) change from %u/%u to %u/%u\n", |
| ic->ic_scs.scs_rssi_smth_fctr[SCS_RSSI_SMTH_FCTR_UP], |
| ic->ic_scs.scs_rssi_smth_fctr[SCS_RSSI_SMTH_FCTR_DOWN], |
| u8_arg0, u8_arg1); |
| ic->ic_scs.scs_rssi_smth_fctr[SCS_RSSI_SMTH_FCTR_UP] = u8_arg0; |
| ic->ic_scs.scs_rssi_smth_fctr[SCS_RSSI_SMTH_FCTR_DOWN] = u8_arg1; |
| break; |
| case IEEE80211_SCS_SET_ATTEN_ADJUST: |
| if (((int8_t)arg < IEEE80211_SCS_ATTEN_ADJUST_MIN) || |
| ((int8_t)arg > IEEE80211_SCS_ATTEN_ADJUST_MAX)) { |
| return -1; |
| } |
| SCSDBG(SCSLOG_NOTICE, "attenuation adjust change from %d to %d\n", |
| ic->ic_scs.scs_atten_adjust, (int8_t)arg); |
| ic->ic_scs.scs_atten_adjust = (int8_t)arg; |
| break; |
| case IEEE80211_SCS_SET_ATTEN_SWITCH_ENABLE: |
| if (arg > 1) { |
| return -1; |
| } |
| |
| if (ic->ic_scs.scs_atten_sw_enable != arg) { |
| SCSDBG(SCSLOG_NOTICE, "attenuation channel change logic is %s\n", |
| arg ? "enabled" : "disabled"); |
| ic->ic_scs.scs_atten_sw_enable = (uint16_t)arg; |
| } |
| break; |
| case IEEE80211_SCS_SET_PMBL_ERR_SMTH_FCTR: |
| if (arg > IEEE80211_SCS_PMBL_ERR_SMTH_FCTR_MAX) { |
| return -1; |
| } |
| SCSDBG(SCSLOG_NOTICE, "preamble error smoothing factor change from %u to %u\n", |
| ic->ic_scs.scs_pmbl_err_smth_fctr, arg); |
| ic->ic_scs.scs_pmbl_err_smth_fctr = arg; |
| break; |
| case IEEE80211_SCS_SET_PMP_RPT_CCA_SMTH_FCTR: |
| if (arg > IEEE80211_SCS_PMP_RPT_CCA_SMTH_FCTR_MAX) { |
| return -1; |
| } |
| SCSDBG(SCSLOG_NOTICE, "scs_pmp_rpt_cca_smth_fctr change from %u to %u\n", |
| ic->ic_scs.scs_pmp_rpt_cca_smth_fctr, arg); |
| ic->ic_scs.scs_pmp_rpt_cca_smth_fctr = arg; |
| break; |
| case IEEE80211_SCS_SET_PMP_RX_TIME_SMTH_FCTR: |
| if (arg > IEEE80211_SCS_PMP_RX_TIME_SMTH_FCTR_MAX) { |
| return -1; |
| } |
| SCSDBG(SCSLOG_NOTICE, "scs_pmp_rx_time_smth_fctr change from %u to %u\n", |
| ic->ic_scs.scs_pmp_rx_time_smth_fctr, arg); |
| ic->ic_scs.scs_pmp_rx_time_smth_fctr = arg; |
| break; |
| case IEEE80211_SCS_SET_PMP_TX_TIME_SMTH_FCTR: |
| if (arg > IEEE80211_SCS_PMP_TX_TIME_SMTH_FCTR_MAX) { |
| return -1; |
| } |
| SCSDBG(SCSLOG_NOTICE, "scs_pmp_tx_time_smth_fctr change from %u to %u\n", |
| ic->ic_scs.scs_pmp_tx_time_smth_fctr, arg); |
| ic->ic_scs.scs_pmp_tx_time_smth_fctr = arg; |
| break; |
| case IEEE80211_SCS_SET_PMP_STATS_STABLE_PERCENT: |
| if (arg > IEEE80211_SCS_PMP_STATS_STABLE_PERCENT_MAX) { |
| return -1; |
| } |
| SCSDBG(SCSLOG_NOTICE, "scs_pmp_stats_stable_percent change from %u to %u\n", |
| ic->ic_scs.scs_pmp_stats_stable_percent, arg); |
| ic->ic_scs.scs_pmp_stats_stable_percent = arg; |
| break; |
| case IEEE80211_SCS_SET_PMP_STATS_STABLE_RANGE: |
| if (arg > IEEE80211_SCS_PMP_STATS_STABLE_RANGE_MAX) { |
| return -1; |
| } |
| SCSDBG(SCSLOG_NOTICE, "scs_pmp_stats_stable_range change from %u to %u\n", |
| ic->ic_scs.scs_pmp_stats_stable_range, arg); |
| ic->ic_scs.scs_pmp_stats_stable_range = arg; |
| break; |
| case IEEE80211_SCS_SET_PMP_STATS_CLEAR_INTERVAL: |
| if (arg > IEEE80211_SCS_PMP_STATS_CLEAR_INTERVAL_MAX) { |
| return -1; |
| } |
| SCSDBG(SCSLOG_NOTICE, "scs_pmp_stats_clear_interval change from %u to %u\n", |
| ic->ic_scs.scs_pmp_stats_clear_interval, arg); |
| ic->ic_scs.scs_pmp_stats_clear_interval = arg; |
| break; |
| case IEEE80211_SCS_SET_PMP_TXTIME_COMPENSATION: |
| ieee80211_scs_set_time_compensation(SCS_TX_COMPENSTATION, u8_arg0, u8_arg1); |
| break; |
| case IEEE80211_SCS_SET_PMP_RXTIME_COMPENSATION: |
| ieee80211_scs_set_time_compensation(SCS_RX_COMPENSTATION, u8_arg0, u8_arg1); |
| break; |
| case IEEE80211_SCS_SET_PMP_TDLSTIME_COMPENSATION: |
| ieee80211_scs_set_time_compensation(SCS_TDLS_COMPENSTATION, u8_arg0, u8_arg1); |
| break; |
| case IEEE80211_SCS_SET_SWITCH_CHANNEL_MANUALLY: |
| ieee80211_scs_switch_channel_manually(ic, arg); |
| break; |
| case IEEE80211_SCS_SET_AS_RX_TIME_SMTH_FCTR: |
| if (arg > IEEE80211_SCS_AS_RX_TIME_SMTH_FCTR_MAX) { |
| return -1; |
| } |
| SCSDBG(SCSLOG_NOTICE, "scs_as_rx_time_smth_fctr change from %u to %u\n", |
| ic->ic_scs.scs_as_rx_time_smth_fctr, arg); |
| ic->ic_scs.scs_as_rx_time_smth_fctr = arg; |
| break; |
| case IEEE80211_SCS_SET_AS_TX_TIME_SMTH_FCTR: |
| if (arg > IEEE80211_SCS_AS_TX_TIME_SMTH_FCTR_MAX) { |
| return -1; |
| } |
| SCSDBG(SCSLOG_NOTICE, "scs_as_tx_time_smth_fctr change from %u to %u\n", |
| ic->ic_scs.scs_as_tx_time_smth_fctr, arg); |
| ic->ic_scs.scs_as_tx_time_smth_fctr = arg; |
| break; |
| case IEEE80211_SCS_SET_PMBL_ERR_RANGE: |
| if (arg < IEEE80211_SCS_PMBL_ERR_RANGE_MIN) { |
| return -1; |
| } |
| SCSDBG(SCSLOG_NOTICE, "preamble error range change from %u to %u\n", |
| ic->ic_scs.scs_pmbl_err_range, arg); |
| ic->ic_scs.scs_pmbl_err_range = arg; |
| break; |
| case IEEE80211_SCS_SET_PMBL_ERR_MAPPED_INTF_RANGE: |
| if (arg > IEEE80211_SCS_PMBL_ERR_MAPPED_INTF_RANGE_MAX) { |
| return -1; |
| } |
| SCSDBG(SCSLOG_NOTICE, "preamble error mapped cca_intf range change from %u to %u\n", |
| ic->ic_scs.scs_pmbl_err_mapped_intf_range, arg); |
| ic->ic_scs.scs_pmbl_err_mapped_intf_range = arg; |
| break; |
| case IEEE80211_SCS_SET_PMBL_ERR_WF: |
| if ((u8_arg0 > IEEE80211_SCS_PMBL_ERR_WF_MAX) || |
| (u8_arg1 > IEEE80211_SCS_PMBL_ERR_WF_MAX)) { |
| return -1; |
| } |
| SCSDBG(SCSLOG_NOTICE, "preamble error weighting factor(sp/lp) change from %u/%u to %u/%u\n", |
| ic->ic_scs.scs_sp_wf, |
| ic->ic_scs.scs_lp_wf, |
| u8_arg0, u8_arg1); |
| ic->ic_scs.scs_sp_wf = u8_arg0; |
| ic->ic_scs.scs_lp_wf = u8_arg1; |
| break; |
| case IEEE80211_SCS_SET_STATS_START: |
| SCSDBG(SCSLOG_NOTICE, "%sing scs stats\n", arg ? "start" : "stopp"); |
| if (ieee80211_wireless_scs_stats_task_start(vap, !!arg) < 0) { |
| return -1; |
| } |
| break; |
| case IEEE80211_SCS_SET_CCA_IDLE_SMTH_FCTR: |
| if (arg > IEEE80211_SCS_CCA_IDLE_SMTH_FCTR_MAX) { |
| return -1; |
| } |
| SCSDBG(SCSLOG_NOTICE, "cca idle smoothing factor change from %u to %u\n", |
| ic->ic_scs.scs_cca_idle_smth_fctr, arg); |
| ic->ic_scs.scs_cca_idle_smth_fctr = arg; |
| break; |
| case IEEE80211_SCS_SET_CCA_THRESHOLD_TYPE: |
| if (arg > 1) { |
| return -1; |
| } |
| SCSDBG(SCSLOG_NOTICE, "cca thresholds switched to %s sensitive ones\n", |
| arg ? "more" : "less"); |
| ic->ic_scs.scs_cca_threshold_type = arg + 1; |
| break; |
| case IEEE80211_SCS_SET_BURST_ENABLE: |
| SCSDBG(SCSLOG_NOTICE, "burst channel switching enable: %u\n", arg); |
| if (ic->ic_scs.scs_burst_enable != !!arg) { |
| ic->ic_scs.scs_burst_enable = !!arg; |
| memset(ic->ic_scs.scs_burst_queue, 0, sizeof(ic->ic_scs.scs_burst_queue)); |
| } |
| break; |
| case IEEE80211_SCS_SET_BURST_WINDOW: |
| if ((arg < IEEE80211_SCS_BURST_WINDOW_MIN) || |
| (arg > IEEE80211_SCS_BURST_WINDOW_MAX)) |
| return -1; |
| SCSDBG(SCSLOG_NOTICE, "burst sliding window of time: %u\n", arg); |
| if (ic->ic_scs.scs_burst_window != (arg * 60)) { |
| ic->ic_scs.scs_burst_window = arg * 60; |
| memset(ic->ic_scs.scs_burst_queue, 0, sizeof(ic->ic_scs.scs_burst_queue)); |
| } |
| break; |
| case IEEE80211_SCS_SET_BURST_THRESH: |
| if ((arg < IEEE80211_SCS_BURST_THRESH_MIN) || |
| (arg > IEEE80211_SCS_BURST_THRESH_MAX)) |
| return -1; |
| SCSDBG(SCSLOG_NOTICE, "burst channel switching threshold: %u\n", arg); |
| if (ic->ic_scs.scs_burst_thresh != arg) { |
| ic->ic_scs.scs_burst_thresh = arg; |
| memset(ic->ic_scs.scs_burst_queue, 0, sizeof(ic->ic_scs.scs_burst_queue)); |
| } |
| break; |
| case IEEE80211_SCS_SET_BURST_PAUSE: |
| if ((arg < IEEE80211_SCS_BURST_PAUSE_MIN) || |
| (arg > IEEE80211_SCS_BURST_PAUSE_MAX)) |
| return -1; |
| SCSDBG(SCSLOG_NOTICE, "burst channel switching pause time: %u\n", arg); |
| ic->ic_scs.scs_burst_pause_time = arg * 60; |
| break; |
| case IEEE80211_SCS_SET_BURST_SWITCH: |
| SCSDBG(SCSLOG_NOTICE, "burst channel switching flag: %u\n", arg); |
| ic->ic_scs.scs_burst_force_switch = !!arg; |
| break; |
| default: |
| break; |
| } |
| |
| SCSDBG(SCSLOG_INFO, "set param %u to value 0x%x completed successfully\n", |
| cmd, arg); |
| |
| return 0; |
| } |
| EXPORT_SYMBOL(ieee80211_param_scs_set); |
| |
| static int |
| ieee80211_param_scs_get(struct net_device *dev, uint16_t cmd, uint32_t *value) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| |
| if (cmd >= IEEE80211_SCS_SET_MAX) { |
| printk(KERN_WARNING "%s: SCS: invalid config cmd %u\n", |
| dev->name, cmd); |
| return -1; |
| } |
| |
| SCSDBG(SCSLOG_INFO, "get param %u\n", cmd); |
| |
| switch (cmd) { |
| case 0: /* compatible with iwpriv ifname scs_get */ |
| case IEEE80211_SCS_SET_ENABLE: |
| *value = ic->ic_scs.scs_enable; |
| break; |
| case IEEE80211_SCS_SET_SAMPLE_DWELL_TIME: |
| *value = ic->ic_scs.scs_smpl_dwell_time; |
| break; |
| case IEEE80211_SCS_SET_SAMPLE_INTERVAL: |
| *value = ic->ic_scs.scs_sample_intv; |
| break; |
| default: |
| SCSDBG(SCSLOG_INFO, "get param %u not supported\n", cmd); |
| return -1; |
| } |
| |
| SCSDBG(SCSLOG_INFO, "get param %u: value 0x%x completed successfully\n", |
| cmd, *value); |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_scs_get_currchan_rpt(struct ieee80211com *ic, struct ieee80211req_scs *req, uint32_t *reason) |
| { |
| struct ieee80211req_scs_currchan_rpt rpt; |
| struct shared_params *sp = qtn_mproc_sync_shared_params_get(); |
| struct qtn_scs_info *scs_info_read = NULL; |
| struct ieee80211vap *vap; |
| |
| if (!ic->ic_scs.scs_stats_on) { |
| *reason = IEEE80211REQ_SCS_RESULT_SCS_DISABLED; |
| return -EINVAL; |
| } |
| |
| /* CCA information cannot match a certain channel under scan state */ |
| TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { |
| if (vap->iv_state == IEEE80211_S_RUN) { |
| break; |
| } |
| } |
| if (vap == NULL) { |
| *reason = IEEE80211REQ_SCS_RESULT_NO_VAP_RUNNING; |
| return -EINVAL; |
| } |
| |
| /* Copy scs info into a local structure so MuC can continue to fill it in */ |
| scs_info_read = kmalloc(sizeof(*scs_info_read), GFP_KERNEL); |
| if (!scs_info_read) { |
| SCSDBG(SCSLOG_NOTICE, "SCS info allocation failed\n"); |
| return -ENOMEM; |
| } |
| memcpy((void *)scs_info_read, &sp->scs_info_lhost->scs_info[sp->scs_info_lhost->valid_index], |
| sizeof(*scs_info_read)); |
| if (!scs_info_read->cca_try) { |
| *reason = IEEE80211REQ_SCS_RESULT_TMP_UNAVAILABLE; |
| kfree(scs_info_read); |
| return -EAGAIN; |
| } |
| |
| ieee80211_scs_scale_cochan_data(ic, scs_info_read); |
| |
| memset(&rpt, 0x0, sizeof(struct ieee80211req_scs_currchan_rpt)); |
| |
| rpt.iscr_curchan = ic->ic_curchan->ic_ieee; |
| rpt.iscr_cca_try = scs_info_read->cca_try; |
| rpt.iscr_cca_idle = scs_info_read->cca_idle; |
| rpt.iscr_cca_busy = scs_info_read->cca_busy; |
| rpt.iscr_cca_intf = scs_info_read->cca_interference; |
| rpt.iscr_cca_tx = scs_info_read->cca_tx; |
| rpt.iscr_tx_ms = scs_info_read->tx_usecs / 1000; |
| rpt.iscr_rx_ms = scs_info_read->rx_usecs / 1000; |
| rpt.iscr_pmbl = (ic->ic_scs.scs_sp_err_smthed * ic->ic_scs.scs_sp_wf + |
| ic->ic_scs.scs_lp_err_smthed * ic->ic_scs.scs_lp_wf) / 100; |
| |
| if (copy_to_user(req->is_data, &rpt, req->is_data_len)) { |
| SCSDBG(SCSLOG_NOTICE, "copy_to_user data failed with op=0x%x\n", req->is_op); |
| kfree(scs_info_read); |
| return -EIO; |
| } |
| |
| kfree(scs_info_read); |
| return 0; |
| } |
| |
| static int |
| ieee80211_scs_get_ranking_rpt(struct ieee80211com *ic, struct ieee80211req_scs *req, uint32_t *reason) |
| { |
| static struct ieee80211req_scs_ranking_rpt rpt; |
| struct ieee80211req_scs_ranking_rpt_chan *chan_rpt; |
| int i; |
| int num = 0; |
| struct ieee80211_channel *chan; |
| struct ap_state *as; |
| |
| if (!ic->ic_scs.scs_stats_on) { |
| *reason = IEEE80211REQ_SCS_RESULT_SCS_DISABLED; |
| return -EINVAL; |
| } |
| |
| if (ic->ic_opmode != IEEE80211_M_HOSTAP) { |
| *reason = IEEE80211REQ_SCS_RESULT_APMODE_ONLY; |
| return -EINVAL; |
| } |
| |
| as = ic->ic_scan->ss_scs_priv; |
| |
| memset(&rpt, 0x0, sizeof(struct ieee80211req_scs_ranking_rpt)); |
| |
| /* the ranking table */ |
| for (i = 1; i < IEEE80211_CHAN_MAX; i++) { |
| chan = ieee80211_find_channel_by_ieee(ic, i); |
| if (!is_ieee80211_chan_valid(chan)) { |
| continue; |
| } |
| |
| chan_rpt = &rpt.isr_chans[num]; |
| chan_rpt->isrc_chan = i; |
| chan_rpt->isrc_dfs = !!(chan->ic_flags & IEEE80211_CHAN_DFS); |
| chan_rpt->isrc_txpwr = chan->ic_maxpower; |
| chan_rpt->isrc_numbeacons = as->as_numbeacons[i]; |
| chan_rpt->isrc_cca_intf = (as->as_cca_intf[i] == SCS_CCA_INTF_INVALID) ? 0 : as->as_cca_intf[i]; |
| chan_rpt->isrc_metric = as->as_chanmetric[i]; |
| chan_rpt->isrc_metric_age = (jiffies - as->as_chanmetric_timestamp[i]) / HZ; |
| chan_rpt->isrc_pmbl_ap = as->as_pmbl_err_ap[i]; |
| chan_rpt->isrc_pmbl_sta = as->as_pmbl_err_sta[i]; |
| chan_rpt->isrc_times = ic->ic_chan_occupy_record.times[i]; |
| chan_rpt->isrc_duration = ic->ic_chan_occupy_record.duration[i]; |
| chan_rpt->isrc_chan_avail_status = ic->ic_chan_availability_status[chan->ic_ieee]; |
| if (i == ic->ic_chan_occupy_record.cur_chan) { |
| chan_rpt->isrc_duration += (jiffies - INITIAL_JIFFIES) / HZ - |
| ic->ic_chan_occupy_record.occupy_start; |
| } |
| |
| num++; |
| if (num >= IEEE80211REQ_SCS_REPORT_CHAN_NUM) |
| break; |
| } |
| rpt.isr_num = num; |
| |
| if (copy_to_user(req->is_data, &rpt, req->is_data_len)) { |
| SCSDBG(SCSLOG_NOTICE, "copy_to_user data failed with op=0x%x\n", req->is_op); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_scs_get_interference_rpt(struct ieee80211com *ic, struct ieee80211req_scs *req, uint32_t *reason) |
| { |
| static struct ieee80211req_scs_interference_rpt rpt; |
| struct ieee80211req_scs_interference_rpt_chan *chan_rpt; |
| struct ieee80211_channel *chan; |
| struct ap_state *as; |
| int i; |
| int num = 0; |
| int cur_bw = ieee80211_get_bw(ic); |
| |
| if (!ic->ic_scs.scs_stats_on) { |
| *reason = IEEE80211REQ_SCS_RESULT_SCS_DISABLED; |
| return -EINVAL; |
| } |
| |
| if (ic->ic_opmode != IEEE80211_M_HOSTAP) { |
| *reason = IEEE80211REQ_SCS_RESULT_APMODE_ONLY; |
| return -EINVAL; |
| } |
| |
| as = ic->ic_scan->ss_scs_priv; |
| |
| memset(&rpt, 0x0, sizeof(rpt)); |
| |
| /* the ranking table */ |
| for (i = 1; i < IEEE80211_CHAN_MAX; i++) { |
| chan = ieee80211_find_channel_by_ieee(ic, i); |
| if (!is_ieee80211_chan_valid(chan)) |
| continue; |
| |
| chan_rpt = &rpt.isr_chans[num]; |
| chan_rpt->isrc_chan = i; |
| |
| chan_rpt->isrc_cca_intf_20 = as->as_cca_intf_pri[i]; |
| if (cur_bw < BW_HT40) { |
| chan_rpt->isrc_cca_intf_40 = SCS_CCA_INTF_INVALID; |
| } else { |
| chan_rpt->isrc_cca_intf_40 = as->as_cca_intf_pri[i] + |
| as->as_cca_intf_sec[i]; |
| } |
| if (cur_bw < BW_HT80) { |
| chan_rpt->isrc_cca_intf_80 = SCS_CCA_INTF_INVALID; |
| } else { |
| chan_rpt->isrc_cca_intf_80 = as->as_cca_intf_pri[i] + |
| as->as_cca_intf_sec[i] + |
| as->as_cca_intf_sec40[i]; |
| } |
| |
| num++; |
| if (num >= IEEE80211REQ_SCS_REPORT_CHAN_NUM) |
| break; |
| } |
| rpt.isr_num = num; |
| |
| if (copy_to_user(req->is_data, &rpt, req->is_data_len) != 0) { |
| SCSDBG(SCSLOG_NOTICE, "copy_to_user data failed with op=0x%x\n", req->is_op); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_scs_get_score_rpt(struct ieee80211com *ic, struct ieee80211req_scs *req, uint32_t *reason) |
| { |
| static struct ieee80211req_scs_score_rpt rpt; |
| struct ieee80211req_scs_score_rpt_chan *chan_rpt; |
| struct ieee80211_channel *chan; |
| int32_t chan_metric[IEEE80211REQ_SCS_REPORT_CHAN_NUM]; |
| int32_t max_metric = -1; |
| int32_t min_metric = -1; |
| int32_t max_diff; |
| int iter; |
| int num = 0; |
| uint8_t rate_ratio; |
| |
| if (!ic->ic_scs.scs_stats_on) { |
| *reason = IEEE80211REQ_SCS_RESULT_SCS_DISABLED; |
| return -EINVAL; |
| } |
| |
| if (ic->ic_opmode != IEEE80211_M_HOSTAP) { |
| *reason = IEEE80211REQ_SCS_RESULT_APMODE_ONLY; |
| return -EINVAL; |
| } |
| |
| if (ic->ic_scan == NULL || |
| ic->ic_scan->ss_scs_priv == NULL || |
| ic->ic_curchan == IEEE80211_CHAN_ANYC) { |
| *reason = IEEE80211REQ_SCS_RESULT_TMP_UNAVAILABLE; |
| return -EINVAL; |
| } |
| |
| memset(&rpt, 0x0, sizeof(struct ieee80211req_scs_score_rpt)); |
| |
| ieee80211_scs_aging(ic, ic->ic_scs.scs_thrshld_aging_nor); |
| rate_ratio = ieee80211_scs_calc_rate_ratio(ic); |
| |
| for (iter = 0; iter < ic->ic_nchans; iter++) { |
| chan = &ic->ic_channels[iter]; |
| if (!isset(ic->ic_chan_active, chan->ic_ieee) || |
| !ieee80211_chan_allowed_in_band(ic, chan, |
| ic->ic_opmode)) { |
| continue; |
| } |
| chan_rpt = &rpt.isr_chans[num]; |
| chan_rpt->isrc_chan = chan->ic_ieee; |
| chan_rpt->isrc_score = 0; |
| |
| ieee80211_scs_get_chan_metric(ic, chan, rate_ratio, |
| &chan_metric[num], NULL, IEEE80211_SCS_NA_CC); |
| if (chan_metric[num] >= 0 && |
| chan_metric[num] < SCS_MAX_RAW_CHAN_METRIC) { |
| if (max_metric == -1 || |
| chan_metric[num] > max_metric) { |
| max_metric = chan_metric[num]; |
| } |
| if (min_metric == -1 || |
| chan_metric[num] < min_metric) { |
| min_metric = chan_metric[num]; |
| } |
| } |
| |
| num++; |
| if (num >= IEEE80211REQ_SCS_REPORT_CHAN_NUM) { |
| break; |
| } |
| } |
| if (num == 0) { |
| *reason = IEEE80211REQ_SCS_RESULT_TMP_UNAVAILABLE; |
| return -EINVAL; |
| } |
| |
| rpt.isr_num = num; |
| |
| /* For the scoring, the algorithm as below: |
| * 1. For the channel which has the minimum valid metric, the score is 100 |
| * 2. For the channel which has the maximum valid metric, the score is 0 |
| * 3. For other channels that have valid metrics, the score is |
| * 100 * (max_valid_metric - metric) / (max_valid_metric - min_valid_metric) |
| * 4. For invalid metric, if it is larger than maximum valid metric, the score is 0; |
| * and if it is less than minimum valid metric - it may result from too large |
| * power difference from the current channel, the score is 100. |
| */ |
| if (max_metric != -1) { |
| max_diff = max_metric - min_metric; |
| for (iter = 0; iter < num; iter++) { |
| chan_rpt = &rpt.isr_chans[iter]; |
| if (chan_metric[iter] > max_metric) { |
| chan_rpt->isrc_score = 0; |
| } else if (chan_metric[iter] < min_metric){ |
| chan_rpt->isrc_score = 100; |
| } else { |
| if (max_diff == 0) { |
| chan_rpt->isrc_score = 100; |
| } else { |
| chan_rpt->isrc_score = |
| 100 * (max_metric - chan_metric[iter]) / max_diff; |
| } |
| } |
| } |
| } |
| |
| |
| if (copy_to_user(req->is_data, &rpt, req->is_data_len)) { |
| SCSDBG(SCSLOG_NOTICE, "copy_to_user data failed with op=0x%x\n", req->is_op); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_scs_get_init_ranking_rpt(struct ieee80211com *ic, struct ieee80211req_scs *req, uint32_t *reason) |
| { |
| static struct ieee80211req_scs_ranking_rpt rpt; |
| struct ieee80211req_scs_ranking_rpt_chan *chan_rpt; |
| int i; |
| int num = 0; |
| struct ieee80211_channel *chan; |
| struct ap_state *as; |
| struct ieee80211vap *vap; |
| |
| if (ic->ic_opmode != IEEE80211_M_HOSTAP) { |
| *reason = IEEE80211REQ_SCS_RESULT_APMODE_ONLY; |
| return -EINVAL; |
| } |
| |
| as = ic->ic_scan->ss_priv; |
| if (as == NULL) { |
| *reason = IEEE80211REQ_SCS_RESULT_AUTOCHAN_DISABLED; |
| return -EINVAL; |
| } |
| |
| /* when in auto channel scanning */ |
| TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { |
| if (vap->iv_state == IEEE80211_S_RUN) { |
| break; |
| } |
| } |
| if (vap == NULL) { |
| *reason = IEEE80211REQ_SCS_RESULT_TMP_UNAVAILABLE; |
| return -EAGAIN; |
| } |
| |
| memset(&rpt, 0x0, sizeof(struct ieee80211req_scs_ranking_rpt)); |
| |
| /* the ranking table */ |
| for (i = 1; i < IEEE80211_CHAN_MAX; i++) { |
| chan = ieee80211_find_channel_by_ieee(ic, i); |
| if (chan == NULL) { |
| continue; |
| } |
| |
| chan_rpt = &rpt.isr_chans[num]; |
| chan_rpt->isrc_chan = i; |
| chan_rpt->isrc_dfs = !!(chan->ic_flags & IEEE80211_CHAN_DFS); |
| chan_rpt->isrc_txpwr = chan->ic_maxpower; |
| chan_rpt->isrc_numbeacons = as->as_numbeacons[i]; |
| chan_rpt->isrc_metric = as->as_chanmetric[i]; |
| chan_rpt->isrc_cci = as->as_cci[i]; |
| chan_rpt->isrc_aci = as->as_aci[i]; |
| |
| num++; |
| if (num >= IEEE80211REQ_SCS_REPORT_CHAN_NUM) |
| break; |
| } |
| rpt.isr_num = num; |
| |
| if (copy_to_user(req->is_data, &rpt, req->is_data_len)) { |
| SCSDBG(SCSLOG_NOTICE, "copy_to_user data failed with op=0x%x\n", req->is_op); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_scs_get_param_rpt(struct ieee80211com *ic, struct ieee80211req_scs *req, uint32_t *reason) |
| { |
| int retval = 0; |
| uint32_t i, len; |
| struct ieee80211req_scs_param_rpt *rpt; |
| struct ieee80211_scs *scs = &(ic->ic_scs); |
| |
| len = sizeof(*rpt)*SCS_PARAM_MAX; |
| rpt = (struct ieee80211req_scs_param_rpt *)kmalloc(len, GFP_KERNEL); |
| if (rpt == NULL) { |
| retval = -EIO; |
| goto ready_to_return; |
| } |
| |
| memset(rpt, 0, len); |
| |
| rpt[SCS_SMPL_DWELL_TIME].cfg_param = scs->scs_smpl_dwell_time; |
| rpt[SCS_SAMPLE_INTV].cfg_param = scs->scs_sample_intv; |
| rpt[SCS_SAMPLE_TYPE].cfg_param = scs->scs_sample_type; |
| rpt[SCS_THRSHLD_SMPL_PKTNUM].cfg_param = scs->scs_thrshld_smpl_pktnum; |
| rpt[SCS_THRSHLD_SMPL_AIRTIME].cfg_param = scs->scs_thrshld_smpl_airtime; |
| |
| rpt[SCS_THRSHLD_ATTEN_INC].cfg_param = scs->scs_thrshld_atten_inc; |
| rpt[SCS_THRSHLD_DFS_REENTRY].cfg_param = scs->scs_thrshld_dfs_reentry; |
| rpt[SCS_THRSHLD_DFS_REENTRY_MINRATE].cfg_param = scs->scs_thrshld_dfs_reentry_minrate; |
| rpt[SCS_THRSHLD_DFS_REENTRY_INTF].cfg_param = scs->scs_thrshld_dfs_reentry_intf; |
| rpt[SCS_THRSHLD_LOADED].cfg_param = scs->scs_thrshld_loaded; |
| rpt[SCS_THRSHLD_AGING_NOR].cfg_param = scs->scs_thrshld_aging_nor; |
| rpt[SCS_THRSHLD_AGING_DFSREENT].cfg_param = scs->scs_thrshld_aging_dfsreent; |
| |
| rpt[SCS_ENABLE].cfg_param = (uint32_t)(scs->scs_enable); |
| rpt[SCS_DEBUG_ENABLE].cfg_param = (uint32_t)(scs->scs_debug_enable); |
| rpt[SCS_SMPL_ENABLE].cfg_param = (uint32_t)(scs->scs_smpl_enable); |
| rpt[SCS_REPORT_ONLY].cfg_param = (uint32_t)(scs->scs_report_only); |
| |
| rpt[SCS_CCA_IDLE_THRSHLD].cfg_param = scs->scs_cca_idle_thrshld; |
| rpt[SCS_CCA_INTF_HI_THRSHLD].cfg_param = scs->scs_cca_intf_hi_thrshld; |
| rpt[SCS_CCA_INTF_LO_THRSHLD].cfg_param = scs->scs_cca_intf_lo_thrshld; |
| rpt[SCS_CCA_INTF_RATIO].cfg_param = scs->scs_cca_intf_ratio; |
| rpt[SCS_CCA_INTF_DFS_MARGIN].cfg_param = scs->scs_cca_intf_dfs_margin; |
| rpt[SCS_PMBL_ERR_THRSHLD].cfg_param = scs->scs_pmbl_err_thrshld; |
| rpt[SCS_CCA_SAMPLE_DUR].cfg_param = scs->scs_cca_sample_dur; |
| rpt[SCS_CCA_INTF_SMTH_NOXP].cfg_param = (uint32_t)(scs->scs_cca_intf_smth_fctr[0]); |
| rpt[SCS_CCA_INTF_SMTH_XPED].cfg_param = (uint32_t)(scs->scs_cca_intf_smth_fctr[1]); |
| rpt[SCS_RSSI_SMTH_UP].cfg_param = (uint32_t)(scs->scs_rssi_smth_fctr[0]); |
| rpt[SCS_RSSI_SMTH_DOWN].cfg_param = (uint32_t)(scs->scs_rssi_smth_fctr[1]); |
| |
| rpt[SCS_CHAN_MTRC_MRGN].cfg_param = (uint32_t)(scs->scs_chan_mtrc_mrgn); |
| rpt[SCS_LEAVE_DFS_CHAN_MTRC_MRGN].cfg_param = (uint32_t)(scs->scs_leavedfs_chan_mtrc_mrgn); |
| |
| rpt[SCS_ATTEN_ADJUST].signed_param_flag = 1; |
| rpt[SCS_ATTEN_ADJUST].cfg_param = (uint32_t)(scs->scs_atten_adjust); |
| rpt[SCS_ATTEN_SW_ENABLE].cfg_param = (uint32_t)(scs->scs_atten_sw_enable); |
| |
| rpt[SCS_PMBL_ERR_SMTH_FCTR].cfg_param = scs->scs_pmbl_err_smth_fctr; |
| rpt[SCS_PMBL_ERR_RANGE].cfg_param = scs->scs_pmbl_err_range; |
| rpt[SCS_PMBL_ERR_MAPPED_INTF_RANGE].cfg_param = scs->scs_pmbl_err_mapped_intf_range; |
| rpt[SCS_SP_WF].cfg_param = scs->scs_sp_wf; |
| rpt[SCS_LP_WF].cfg_param = scs->scs_lp_wf; |
| rpt[SCS_PMP_RPT_CCA_SMTH_FCTR].cfg_param = (uint32_t)(scs->scs_pmp_rpt_cca_smth_fctr); |
| rpt[SCS_PMP_RX_TIME_SMTH_FCTR].cfg_param = (uint32_t)(scs->scs_pmp_rx_time_smth_fctr); |
| rpt[SCS_PMP_TX_TIME_SMTH_FCTR].cfg_param = (uint32_t)(scs->scs_pmp_tx_time_smth_fctr); |
| rpt[SCS_PMP_STATS_STABLE_PERCENT].cfg_param = (uint32_t)(scs->scs_pmp_stats_stable_percent); |
| rpt[SCS_PMP_STATS_STABLE_RANGE].cfg_param = (uint32_t)(scs->scs_pmp_stats_stable_range); |
| rpt[SCS_PMP_STATS_CLEAR_INTERVAL].cfg_param = (uint32_t)(scs->scs_pmp_stats_clear_interval); |
| rpt[SCS_AS_RX_TIME_SMTH_FCTR].cfg_param = (uint32_t)(scs->scs_as_rx_time_smth_fctr); |
| rpt[SCS_AS_TX_TIME_SMTH_FCTR].cfg_param = (uint32_t)(scs->scs_as_tx_time_smth_fctr); |
| rpt[SCS_CCA_IDLE_SMTH_FCTR].cfg_param = (uint32_t)(scs->scs_cca_idle_smth_fctr); |
| rpt[SCS_CCA_THRESHOD_TYPE].cfg_param = (uint32_t)(scs->scs_cca_threshold_type); |
| |
| for (i = 0; i < SCS_MAX_TXTIME_COMP_INDEX; i++) { |
| rpt[SCS_TX_TIME_COMPENSTATION_START+i].cfg_param = tx_time_compenstation[i]; |
| } |
| for (i = 0; i < SCS_MAX_RXTIME_COMP_INDEX; i++) { |
| rpt[SCS_RX_TIME_COMPENSTATION_START+i].cfg_param = rx_time_compenstation[i]; |
| } |
| for (i = 0; i < SCS_MAX_TDLSTIME_COMP_INDEX; i++) { |
| rpt[SCS_TDLS_TIME_COMPENSTATION_START+i].cfg_param = tdls_time_compenstation[i]; |
| } |
| |
| rpt[SCS_BURST_ENABLE].cfg_param = scs->scs_burst_enable; |
| rpt[SCS_BURST_WINDOW].cfg_param = scs->scs_burst_window / 60; |
| rpt[SCS_BURST_THRESH].cfg_param = scs->scs_burst_thresh; |
| rpt[SCS_BURST_PAUSE_TIME].cfg_param = scs->scs_burst_pause_time / 60; |
| rpt[SCS_BURST_FORCE_SWITCH].cfg_param = scs->scs_burst_force_switch; |
| |
| if (copy_to_user(req->is_data, rpt, MIN(req->is_data_len, len))) { |
| SCSDBG(SCSLOG_NOTICE, "copy_to_user data failed with op=0x%x\n", req->is_op); |
| retval = -EIO; |
| goto ready_to_return; |
| } |
| |
| ready_to_return: |
| if (rpt != NULL) { |
| kfree(rpt); |
| rpt = NULL; |
| } |
| return retval; |
| } |
| |
| /* WAR for bug16636, supposed to be removed after cca threshold re-tuned */ |
| void ieee80211_scs_adjust_cca_threshold(struct ieee80211com *ic) |
| { |
| struct ieee80211_scan_state *ss = ic->ic_scan; |
| uint32_t value = IEEE80211_SCS_SET_CCA_THRESHOLD_TYPE << IEEE80211_SCS_COMMAND_S; |
| |
| if (ieee80211_get_type_of_neighborhood(ic) == IEEE80211_NEIGHBORHOOD_TYPE_VERY_DENSE && |
| ic->ic_ver_hw == HARDWARE_REVISION_TOPAZ_A2) { |
| IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN, "%s: neighborhood is very dense, " |
| "switch cca thresholds to less sensitive ones\n", __func__); |
| ieee80211_param_to_qdrv(ss->ss_vap, IEEE80211_PARAM_CCA_FIXED, 0, NULL, 0); |
| if (ieee80211_param_scs_set(ss->ss_vap->iv_dev, ss->ss_vap, value) == 0) { |
| ieee80211_param_to_qdrv(ss->ss_vap, IEEE80211_PARAM_SCS, value, NULL, 0); |
| ieee80211_param_to_qdrv(ss->ss_vap, IEEE80211_PARAM_CCA_FIXED, 1, NULL, 0); |
| } |
| } |
| } |
| |
| static int |
| ieee80211_subioctl_scs(struct net_device *dev, struct ieee80211req_scs __user* ps) |
| { |
| int retval = 0; |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211req_scs req; |
| uint32_t reason = IEEE80211REQ_SCS_RESULT_OK; |
| |
| if (!ps) { |
| SCSDBG(SCSLOG_NOTICE, "%s: NULL pointer for user request\n", __FUNCTION__); |
| return -EFAULT; |
| } |
| |
| if (copy_from_user(&req, ps, sizeof(struct ieee80211req_scs))) { |
| SCSDBG(SCSLOG_NOTICE, "%s: copy_from_user failed\n", __FUNCTION__); |
| return -EIO; |
| } |
| |
| if ((req.is_op & IEEE80211REQ_SCS_FLAG_GET) && (!req.is_data)) { |
| SCSDBG(SCSLOG_NOTICE, "%s: NULL pointer for GET operation\n", __FUNCTION__); |
| return -EFAULT; |
| } |
| |
| if (!req.is_status) { |
| SCSDBG(SCSLOG_NOTICE, "%s: NULL pointer for reason field\n", __FUNCTION__); |
| return -EFAULT; |
| } |
| |
| SCSDBG(SCSLOG_INFO, "%s: op=0x%x\n", __FUNCTION__, req.is_op); |
| switch (req.is_op) { |
| case IEEE80211REQ_SCS_GET_CURRCHAN_RPT: |
| retval = ieee80211_scs_get_currchan_rpt(ic, &req, &reason); |
| break; |
| case IEEE80211REQ_SCS_GET_RANKING_RPT: |
| retval = ieee80211_scs_get_ranking_rpt(ic, &req, &reason); |
| break; |
| case IEEE80211REQ_SCS_GET_INTERFERENCE_RPT: |
| retval = ieee80211_scs_get_interference_rpt(ic, &req, &reason); |
| break; |
| case IEEE80211REQ_SCS_GET_INIT_RANKING_RPT: |
| retval = ieee80211_scs_get_init_ranking_rpt(ic, &req, &reason); |
| break; |
| case IEEE80211REQ_SCS_GET_PARAM_RPT: |
| retval = ieee80211_scs_get_param_rpt(ic, &req, &reason); |
| break; |
| case IEEE80211REQ_SCS_GET_SCORE_RPT: |
| retval = ieee80211_scs_get_score_rpt(ic, &req, &reason); |
| break; |
| default: |
| SCSDBG(SCSLOG_NOTICE, "unknown ioctl op=0x%x\n", req.is_op); |
| return -EINVAL; |
| } |
| |
| if (copy_to_user(req.is_status, &reason, sizeof(*req.is_status))) { |
| SCSDBG(SCSLOG_NOTICE, "copy_to_user reason failed with op=0x%x\n", req.is_op); |
| return -EIO; |
| } |
| |
| return retval; |
| } |
| |
| #endif /* QSCS_ENABLED */ |
| |
| static int |
| ieee80211_subioctl_wait_scan_complete(struct net_device *dev, char __user* p_timeout) |
| { |
| int retval = 0; |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| uint32_t timeout; |
| |
| if (copy_from_user(&timeout, p_timeout, sizeof(timeout))) { |
| return -EIO; |
| }; |
| |
| if (((ic->ic_flags & IEEE80211_F_SCAN) == 0) |
| #ifdef QTN_BG_SCAN |
| && ((ic->ic_flags_qtn & IEEE80211_QTN_BGSCAN) == 0) |
| #endif /* QTN_BG_SCAN */ |
| ) { |
| return -1; |
| } |
| |
| retval = wait_event_interruptible_timeout(ic->ic_scan_comp, |
| (((ic->ic_flags & IEEE80211_F_SCAN) == 0) |
| #ifdef QTN_BG_SCAN |
| && ((ic->ic_flags_qtn & IEEE80211_QTN_BGSCAN) == 0) |
| #endif /* QTN_BG_SCAN */ |
| ), timeout * HZ); |
| |
| return retval; |
| } |
| |
| static uint32_t |
| maxrate(const struct ieee80211_scan_entry *se) |
| { |
| int j, chan_mode = 0; |
| uint32_t max = 0; |
| uint8_t sgi = 0; |
| int k, r; |
| u_int16_t mask; |
| struct ieee80211_ie_htcap *htcap; |
| struct ieee80211_ie_vhtcap *vhtcap; |
| |
| htcap = (struct ieee80211_ie_htcap *)se->se_htcap_ie; |
| vhtcap = (struct ieee80211_ie_vhtcap *)se->se_vhtcap_ie; |
| if (vhtcap) { |
| u_int16_t mcsmap = 0; |
| r = 0; |
| /* 80+80 or 160 Mhz */ |
| if (IEEE80211_VHTCAP_GET_CHANWIDTH(vhtcap)) { |
| chan_mode = 1; |
| sgi = IEEE80211_VHTCAP_GET_SGI_160MHZ(vhtcap); |
| } else { |
| chan_mode = 0; |
| sgi = IEEE80211_VHTCAP_GET_SGI_80MHZ(vhtcap); |
| } |
| mask = 0xc000; |
| mcsmap = (u_int16_t)IEEE80211_VHTCAP_GET_TX_MCS_NSS(vhtcap); |
| for (k = 8; k > 0; k--) { |
| if ((mcsmap & mask) != mask) { |
| uint32_t rate = 0; |
| int val = ((mcsmap & mask)>>((k-1) * 2)); |
| r = (val == 2) ? 9: (val == 1) ? 8 : 7; |
| rate = ((uint32_t)ieee80211_mcs2rate(r, chan_mode, sgi, 1) * (1000000 / 2)) * k; |
| if (max < rate) |
| max = rate; |
| break; |
| } |
| mask = mask >> 2; |
| } |
| return max; |
| } else if (htcap) { |
| r = 0; |
| if (htcap->hc_cap[0] & IEEE80211_HTCAP_C_CHWIDTH40) { |
| chan_mode = 1; |
| sgi = htcap->hc_cap[0] & IEEE80211_HTCAP_C_SHORTGI40 ? 1 : 0; |
| } else { |
| sgi = htcap->hc_cap[0] & IEEE80211_HTCAP_C_SHORTGI20 ? 1 : 0; |
| } |
| for (j = IEEE80211_HT_MCSSET_20_40_NSS1; j <= IEEE80211_HT_MCSSET_20_40_NSS4; j++) { |
| mask = 1; |
| for (k = 0; k < 8; k++, r++) { |
| if (htcap->hc_mcsset[j] & mask) { |
| /* Copy HT rates */ |
| int rate = ieee80211_mcs2rate(r, chan_mode, sgi, 0) * (1000000 / 2); |
| if (max < rate) max = rate; |
| } |
| mask = mask << 1; |
| } |
| } |
| return max; |
| } |
| |
| for (j = 0; j < se->se_rates[1]; j++) { |
| int r = se->se_rates[2 + j] & IEEE80211_RATE_VAL; |
| if (r != 0) { |
| r = r * (1000000 / 2); |
| if (max < r) max = r; |
| } |
| } |
| for (j = 0; j < se->se_xrates[1]; j++) { |
| int r = se->se_xrates[2+j] & IEEE80211_RATE_VAL; |
| if (r != 0) { |
| r = r * (1000000 / 2); |
| if (max < r) max = r; |
| } |
| } |
| return max; |
| } |
| |
| static int |
| ieee80211_get_scan_entry_bitrates_legacy(const struct ieee80211_scan_entry *se, |
| uint32_t *rates, uint32_t max_rates, int basic) |
| { |
| int nrates = 0; |
| int i; |
| |
| /* rate */ |
| for (i = 0; (i < se->se_rates[1]) && (nrates < max_rates); i++) { |
| if ((!basic) || (se->se_rates[2 + i] & IEEE80211_RATE_BASIC)) { |
| rates[nrates++] = se->se_rates[2 + i] & IEEE80211_RATE_VAL; |
| } |
| } |
| |
| /* extended rates */ |
| for (i = 0; (i < se->se_xrates[1]) && (nrates < max_rates); i++) { |
| if ((!basic) || (se->se_xrates[2 + i] & IEEE80211_RATE_BASIC)) { |
| rates[nrates++] = se->se_xrates[2 + i] & IEEE80211_RATE_VAL; |
| } |
| } |
| |
| return nrates; |
| } |
| |
| static int |
| ieee80211_get_scan_entry_bitrates_ht(const struct ieee80211_scan_entry *se, |
| uint32_t *rates, uint32_t max_rates, int basic) |
| { |
| int nrates = 0; |
| int chan_mode; |
| uint8_t sgi = 0; |
| uint8_t *ht_mcsset = NULL; |
| int max_mcs; |
| int mcs; |
| struct ieee80211_ie_htcap *htcap; |
| struct ieee80211_ie_htinfo *htinfo; |
| |
| htcap = (struct ieee80211_ie_htcap *)se->se_htcap_ie; |
| htinfo = (struct ieee80211_ie_htinfo *)se->se_htinfo_ie; |
| |
| /* HT rates */ |
| if (htcap && (htinfo || (!basic))) { |
| if (htcap->hc_cap[0] & IEEE80211_HTCAP_C_CHWIDTH40) { |
| chan_mode = 1; |
| sgi = htcap->hc_cap[0] & IEEE80211_HTCAP_C_SHORTGI40 ? 1 : 0; |
| } else { |
| chan_mode = 0; |
| sgi = htcap->hc_cap[0] & IEEE80211_HTCAP_C_SHORTGI20 ? 1 : 0; |
| } |
| |
| ht_mcsset = basic ? htinfo->hi_basicmcsset : htcap->hc_mcsset; |
| max_mcs = (IEEE80211_HT_MCSSET_20_40_NSS4 + 1) * 8 - 1; |
| for (mcs = 0; mcs <= max_mcs; mcs++) { |
| if (nrates >= max_rates) { |
| break; |
| } |
| if (isset(ht_mcsset, mcs)) { |
| rates[nrates++] = ieee80211_mcs2rate(mcs, chan_mode, sgi, 0); |
| } |
| } |
| } |
| |
| return nrates; |
| } |
| |
| static int |
| ieee80211_get_scan_entry_bitrates_vht(const struct ieee80211_scan_entry *se, |
| uint32_t *rates, uint32_t max_rates, int basic) |
| { |
| int nrates = 0; |
| int chan_mode; |
| uint8_t sgi = 0; |
| uint16_t vht_mcsmap; |
| int max_mcs; |
| int mcs, nss; |
| struct ieee80211_ie_vhtcap *vhtcap; |
| struct ieee80211_ie_vhtop *vhtop; |
| |
| vhtcap = (struct ieee80211_ie_vhtcap *)se->se_vhtcap_ie; |
| vhtop = (struct ieee80211_ie_vhtop *)se->se_vhtop_ie; |
| |
| if (vhtcap && (vhtop || (!basic))) { |
| /* 80+80 or 160 Mhz */ |
| if (IEEE80211_VHTCAP_GET_CHANWIDTH(vhtcap)) { |
| chan_mode = 1; |
| sgi = IEEE80211_VHTCAP_GET_SGI_160MHZ(vhtcap); |
| } else { |
| chan_mode = 0; |
| sgi = IEEE80211_VHTCAP_GET_SGI_80MHZ(vhtcap); |
| } |
| |
| if (basic) { |
| vht_mcsmap = (uint16_t)IEEE80211_VHTOP_GET_BASIC_MCS_NSS(vhtop); |
| } |
| else { |
| vht_mcsmap = (uint16_t)IEEE80211_VHTCAP_GET_TX_MCS_NSS(vhtcap); |
| } |
| |
| /* get max nss supported, only output rates for highest spatial stream */ |
| for (nss = IEEE80211_VHT_NSS8; nss >= IEEE80211_VHT_NSS1; nss--) { |
| max_mcs = IEEE80211_VHTCAP_GET_MCS_MAP_ENTRY(vht_mcsmap, (nss - 1)); |
| if (max_mcs != IEEE80211_VHT_MCS_NA) { |
| break; |
| } |
| } |
| |
| if (nss) { |
| switch (max_mcs) { |
| case IEEE80211_VHT_MCS_0_7: |
| max_mcs = 7; |
| break; |
| case IEEE80211_VHT_MCS_0_8: |
| max_mcs = 8; |
| break; |
| case IEEE80211_VHT_MCS_0_9: |
| max_mcs = 9; |
| break; |
| default: |
| goto out; |
| break; |
| } |
| for (mcs = 0; mcs <= max_mcs; mcs++) { |
| if (nrates >= max_rates) { |
| goto out; |
| } |
| rates[nrates++] = ieee80211_mcs2rate(mcs, chan_mode, sgi, 1) * nss; |
| } |
| } |
| } |
| |
| out: |
| return nrates; |
| } |
| |
| static int |
| ieee80211_get_scan_entry_bitrates_all(const struct ieee80211_scan_entry *se, |
| uint32_t *rates, uint32_t max_rates, int basic) |
| { |
| int nrates = 0; |
| |
| if ((se == NULL) || (rates == NULL)) { |
| return 0; |
| } |
| |
| nrates = ieee80211_get_scan_entry_bitrates_legacy(se, rates, max_rates, basic); |
| nrates += ieee80211_get_scan_entry_bitrates_ht(se, rates + nrates, max_rates - nrates, basic); |
| nrates += ieee80211_get_scan_entry_bitrates_vht(se, rates + nrates, max_rates - nrates, basic); |
| |
| return nrates; |
| } |
| |
| static int |
| push_scan_results(void *arg, const struct ieee80211_scan_entry *se) |
| { |
| struct ap_scan_iter *piter = (struct ap_scan_iter*)arg; |
| struct ieee80211_per_ap_scan_result *pap; |
| struct ieee80211_ie_htinfo *htinfo = |
| (struct ieee80211_ie_htinfo *)se->se_htinfo_ie; |
| struct ieee80211_ie_vhtop *vhtop = |
| (struct ieee80211_ie_vhtop *)se->se_vhtop_ie; |
| struct ap_scan_entry *apse; |
| struct sta_entry *stase; |
| |
| pap = (struct ieee80211_per_ap_scan_result *)piter->current_env; |
| |
| if (piter->current_env >= piter->end_buf) |
| return E2BIG; |
| |
| /* check length, set macaddr, ssid, channel, rssi, flags,htcap*/ |
| if (piter->current_env + sizeof(*pap) >= piter->end_buf) |
| return E2BIG; |
| |
| /* ap mac addr */ |
| if (piter->vap->iv_opmode == IEEE80211_M_HOSTAP) |
| IEEE80211_ADDR_COPY(pap->ap_addr_mac, se->se_macaddr); |
| else |
| IEEE80211_ADDR_COPY(pap->ap_addr_mac, se->se_bssid); |
| |
| /* ssid */ |
| memset(pap->ap_name_ssid, 0, sizeof(pap->ap_name_ssid)); |
| memcpy(pap->ap_name_ssid, se->se_ssid + 2, se->se_ssid[1]); |
| |
| /* channel ieee */ |
| pap->ap_channel_ieee = se->se_chan->ic_ieee; |
| |
| /* max bandwidth */ |
| pap->ap_max_bw = ieee80211_get_max_ap_bw(se); |
| |
| /* rssi */ |
| pap->ap_rssi = se->se_rssi; |
| |
| /* flags:privacy */ |
| pap->ap_flags = !!(se->se_capinfo & IEEE80211_CAPINFO_PRIVACY); |
| |
| /* htcap available */ |
| pap->ap_htcap = (se->se_htcap_ie != NULL); |
| |
| /* vhtcap available */ |
| pap->ap_vhtcap = (se->se_vhtcap_ie != NULL); |
| |
| pap->ap_basicrates_num = ieee80211_get_scan_entry_bitrates_all(se, pap->ap_basicrates, AP_SCAN_MAX_NUM_RATES, 1); |
| pap->ap_suprates_num = ieee80211_get_scan_entry_bitrates_all(se, pap->ap_suprates, AP_SCAN_MAX_NUM_RATES, 0); |
| |
| pap->ap_qhop_role = se->se_ext_role; |
| |
| piter->current_env += sizeof(*pap); |
| pap->ap_bestrate = maxrate(se); |
| |
| /* check length, copy wpa_ie, wsc_ie and rsn_ie to buffer if exist */ |
| pap->ap_num_genies = 0; |
| if (se->se_rsn_ie != NULL) { |
| if (piter->current_env + se->se_rsn_ie[1] + 2 >= piter->end_buf) |
| return E2BIG; |
| memcpy(piter->current_env, se->se_rsn_ie, se->se_rsn_ie[1] + 2); |
| piter->current_env += se->se_rsn_ie[1] + 2; |
| pap->ap_num_genies++; |
| } |
| |
| if (se->se_wpa_ie != NULL) { |
| if (piter->current_env + se->se_wpa_ie[1] + 2 >= piter->end_buf) |
| return E2BIG; |
| memcpy(piter->current_env, se->se_wpa_ie, se->se_wpa_ie[1] + 2); |
| piter->current_env += se->se_wpa_ie[1] + 2; |
| pap->ap_num_genies++; |
| } |
| |
| if (se->se_wsc_ie != NULL) { |
| if (piter->current_env + se->se_wsc_ie[1] + 2 >= piter->end_buf) |
| return E2BIG; |
| memcpy(piter->current_env, se->se_wsc_ie, se->se_wsc_ie[1] + 2); |
| piter->current_env += se->se_wsc_ie[1] + 2; |
| pap->ap_num_genies++; |
| } |
| |
| /* FIXME: use default noise for now */ |
| pap->ap_noise = QNT_DEFAULT_NOISE; |
| |
| pap->ap_beacon_intval = se->se_intval; |
| pap->ap_dtim_intval = se->se_dtimperiod; |
| pap->ap_nonerp_present = se->se_erp & IEEE80211_ERP_NON_ERP_PRESENT; |
| pap->ap_is_ess = se->se_capinfo & IEEE80211_CAPINFO_ESS; |
| |
| if (htinfo) |
| pap->ap_ht_secoffset = IEEE80211_HTINFO_B1_EXT_CHOFFSET(htinfo); |
| else |
| pap->ap_ht_secoffset = IEEE80211_HTINFO_EXTOFFSET_NA; |
| |
| if (vhtop && pap->ap_max_bw >= BW_HT80) { |
| pap->ap_chan_center1 = IEEE80211_VHTOP_GET_CENTERFREQ0(vhtop); |
| pap->ap_chan_center2 = IEEE80211_VHTOP_GET_CENTERFREQ1(vhtop); |
| } else { |
| pap->ap_chan_center1 = ieee80211_find_ht_center_chan(piter->vap, se); |
| pap->ap_chan_center2 = 0; |
| } |
| |
| if (piter->vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| apse = container_of(se, struct ap_scan_entry, base); |
| pap->ap_last_beacon = (apse->se_lastupdate - INITIAL_JIFFIES) / HZ; |
| } else { |
| stase = container_of(se, struct sta_entry, base); |
| pap->ap_last_beacon = (stase->se_lastupdate - INITIAL_JIFFIES) / HZ; |
| } |
| |
| /* keep address 4-byte aligned*/ |
| piter->current_env = (char *)(((int)piter->current_env + 3) & (~3)); |
| |
| piter->ap_counts++; |
| return 0; |
| } |
| |
| static inline int |
| ieee80211_set_threshold_of_neighborhood_type(struct ieee80211com *ic, uint32_t type, uint32_t value) |
| { |
| if (IEEE80211_NEIGHBORHOOD_TYPE_SPARSE == type) |
| ic->ic_neighbor_cnt_sparse = value; |
| else if (IEEE80211_NEIGHBORHOOD_TYPE_DENSE == type) |
| ic->ic_neighbor_cnt_dense = value; |
| else |
| return 1; |
| |
| return 0; |
| } |
| |
| static inline uint32_t |
| ieee80211_get_threshold_of_neighborhood_type(struct ieee80211com *ic, uint32_t type) |
| { |
| if (IEEE80211_NEIGHBORHOOD_TYPE_SPARSE == type) |
| return ic->ic_neighbor_cnt_sparse; |
| else if (IEEE80211_NEIGHBORHOOD_TYPE_DENSE == type) |
| return ic->ic_neighbor_cnt_dense; |
| |
| return 0; |
| } |
| |
| static qfdr_remote_ap_scan_results_hook_t qfdr_remote_ap_scan_results_hook = NULL; |
| void ieee80211_register_qfdr_remote_ap_scan_results_hook(qfdr_remote_ap_scan_results_hook_t hook) |
| { |
| qfdr_remote_ap_scan_results_hook = hook; |
| } |
| EXPORT_SYMBOL(ieee80211_register_qfdr_remote_ap_scan_results_hook); |
| |
| struct qfdr_remote_aplist_rep *qfdr_ap_scan_results_for_remote(struct qfdr_remote_aplist_req *remote_req) |
| { |
| struct net_device *dev; |
| struct ieee80211vap *vap; |
| struct ieee80211com *ic; |
| struct qfdr_remote_aplist_rep *rep; |
| char *extra; |
| struct ap_scan_iter iter; |
| |
| dev = dev_get_by_name(&init_net, remote_req->dev_name); |
| if (!dev) |
| return NULL; |
| vap = netdev_priv(dev); |
| dev_put(dev); |
| ic = vap->iv_ic; |
| |
| rep = kmalloc(remote_req->extra_len + sizeof(struct qfdr_remote_aplist_rep), GFP_KERNEL); |
| if (!rep) { |
| printk(KERN_ERR "%s: Failed to alloc buf.\n", __func__); |
| return NULL; |
| } |
| extra = rep->extra; |
| |
| iter.ap_counts = 0; |
| iter.current_env = extra; |
| iter.end_buf = extra + remote_req->extra_len; |
| iter.vap = vap; |
| |
| rep->res = ieee80211_scan_iterate(ic, push_scan_results, &iter); |
| rep->type = QFDR_AP_SCAN_RESULT; |
| |
| rep->ap_counts = iter.ap_counts; |
| rep->length = iter.current_env - extra; |
| return rep; |
| } |
| EXPORT_SYMBOL(qfdr_ap_scan_results_for_remote); |
| |
| static int |
| ieee80211_subioctl_ap_scan_results(struct net_device *dev, char __user* data, int32_t len) |
| { |
| int retval; |
| int i, r, chan_mode = 0;; |
| uint8_t sgi = 0; |
| char *kdata; |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_rateset *rs; |
| struct ap_scan_iter iter; |
| struct ieee80211_general_ap_scan_result *ge_ap_scan_result; |
| uint32_t offchan; |
| struct ieee80211_scan_state *ss = ic->ic_scan; |
| struct ap_state *as; |
| struct ap_state *as_bak = NULL; |
| |
| kdata = kmalloc(len, GFP_KERNEL); |
| if (NULL == kdata) |
| return -ENOMEM; |
| |
| if (copy_from_user(&offchan, data, sizeof(offchan)) != 0) { |
| return -EIO; |
| } |
| |
| if (offchan > 1) { |
| retval = -EINVAL; |
| goto exit; |
| } |
| |
| /* get bit rates from ic->ic_sup_rates[ic->ic_des_mode] */ |
| ge_ap_scan_result = (struct ieee80211_general_ap_scan_result *)kdata; |
| ge_ap_scan_result->num_ap_results = 0; |
| rs = &ic->ic_sup_rates[ic->ic_des_mode]; |
| ge_ap_scan_result->num_bitrates = rs->rs_nrates; |
| |
| if (ge_ap_scan_result->num_bitrates > MIN(IEEE80211_RATE_MAXSIZE, AP_SCAN_MAX_NUM_RATES)) { |
| ge_ap_scan_result->num_bitrates = MIN(IEEE80211_RATE_MAXSIZE, AP_SCAN_MAX_NUM_RATES); |
| } |
| |
| if (vap->iv_ic->ic_htcap.cap & IEEE80211_HTCAP_C_CHWIDTH40) { |
| chan_mode = 1; |
| sgi = vap->iv_ic->ic_htcap.cap & IEEE80211_HTCAP_C_SHORTGI40 ? 1 : 0; |
| } else { |
| sgi = vap->iv_ic->ic_htcap.cap & IEEE80211_HTCAP_C_SHORTGI20 ? 1 : 0; |
| } |
| for (i = 0; i < ge_ap_scan_result->num_bitrates; i++) { |
| r = rs->rs_rates[i] & IEEE80211_RATE_VAL; |
| |
| /* Skip legacy rates */ |
| if(i >= (rs->rs_legacy_nrates)) |
| { |
| r = ieee80211_mcs2rate(r, chan_mode, sgi, 0); |
| } |
| ge_ap_scan_result->bitrates[i] = (r * 1000000) / 2; |
| } |
| |
| /* initialize ap_scan_iter */ |
| iter.ap_counts = 0; |
| iter.current_env = kdata + sizeof(*ge_ap_scan_result); |
| iter.end_buf = kdata + len; |
| iter.vap = vap; |
| |
| /* |
| * iterate scan results to push per-ap data into buffer |
| * |
| * Don't need do WPA/RSN sort any more since the original scan list |
| * has been sorted. |
| */ |
| as = (struct ap_state *)ss->ss_scs_priv; |
| if (offchan) { |
| as_bak = ss->ss_priv; |
| ss->ss_priv = as; |
| } |
| retval = ieee80211_scan_iterate(ic, push_scan_results, &iter); |
| if (offchan) |
| ss->ss_priv = as_bak; |
| |
| if (retval == 0 && qfdr_remote_ap_scan_results_hook != NULL) { |
| retval = qfdr_remote_ap_scan_results_hook(&iter); |
| } |
| |
| ge_ap_scan_result->num_ap_results = iter.ap_counts; |
| if (copy_to_user(data, kdata, iter.current_env - kdata)) |
| retval = -EIO; |
| |
| if (retval > 0) |
| retval = -retval; |
| |
| exit: |
| kfree(kdata); |
| return retval; |
| } |
| |
| static int |
| ieee80211_ocac_clean_stats(struct ieee80211com *ic, int clean_level) |
| { |
| switch (clean_level) { |
| case IEEE80211_OCAC_CLEAN_STATS_STOP: |
| ic->ic_ocac.ocac_counts.clean_stats_stop++; |
| break; |
| case IEEE80211_OCAC_CLEAN_STATS_START: |
| ic->ic_ocac.ocac_counts.clean_stats_start++; |
| break; |
| case IEEE80211_OCAC_CLEAN_STATS_RESET: |
| ic->ic_ocac.ocac_counts.clean_stats_reset++; |
| break; |
| } |
| |
| if (clean_level <= IEEE80211_OCAC_CLEAN_STATS_RESET) { |
| ic->ic_ocac.ocac_accum_duration_secs = 0; |
| ic->ic_ocac.ocac_accum_cac_time_ms = 0; |
| } |
| |
| return 0; |
| } |
| |
| static struct ieee80211_channel * |
| ieee80211_ocac_pick_dfs_channel(struct ieee80211com *ic, int chan_dfs) |
| { |
| int chan_ieee; |
| struct ieee80211_channel *chan = NULL; |
| |
| ic->ic_ocac.ocac_counts.pick_offchan++; |
| if (chan_dfs) { |
| chan_ieee = chan_dfs; |
| } else if (ic->ic_ocac.ocac_cfg.ocac_params.auto_first_dfs_channel) { |
| chan_ieee = ic->ic_ocac.ocac_cfg.ocac_params.auto_first_dfs_channel; |
| ic->ic_ocac.ocac_cfg.ocac_params.auto_first_dfs_channel = 0; |
| } else { |
| /* Pick a DFS channel on which OCAC will be performed */ |
| chan_ieee = ieee80211_scs_pick_channel(ic, |
| (IEEE80211_SCS_PICK_NOT_AVAILABLE_DFS_ONLY | IEEE80211_SCS_PICK_ANYWAY), |
| IEEE80211_SCS_NA_CC); |
| } |
| chan = ieee80211_find_channel_by_ieee(ic, chan_ieee); |
| |
| if (chan && (isset(ic->ic_chan_pri_inactive, chan->ic_ieee))) { |
| ic->ic_ocac.ocac_counts.invalid_offchan++; |
| return NULL; |
| } |
| /* |
| * Select a DFS channel for OCAC, only if CAC is not already done; |
| * Initial CAC might have cleared the channel already; |
| */ |
| if ((chan && (ic->ic_dfs_chans_available_for_cac(ic, chan) == false))) { |
| ic->ic_ocac.ocac_counts.invalid_offchan++; |
| return NULL; |
| } |
| |
| return chan; |
| } |
| |
| static void ieee80211_ocac_set_beacon_interval(struct ieee80211com *ic) |
| { |
| if (ic->ic_lintval != ic->ic_ocac.ocac_cfg.ocac_params.beacon_interval && |
| ic->ic_lintval == ic->ic_lintval_backup) { |
| ieee80211_beacon_interval_set(ic, |
| ic->ic_ocac.ocac_cfg.ocac_params.beacon_interval); |
| ic->ic_ocac.ocac_bcn_intval_set = 1; |
| ic->ic_ocac.ocac_counts.set_bcn_intval++; |
| OCACDBG(OCACLOG_NOTICE, "set beacon interval to %u\n", |
| ic->ic_ocac.ocac_cfg.ocac_params.beacon_interval); |
| } |
| } |
| |
| static void ieee80211_ocac_restore_beacon_interval(struct ieee80211com *ic) |
| { |
| if (ic->ic_lintval == ic->ic_ocac.ocac_cfg.ocac_params.beacon_interval && |
| ic->ic_lintval != ic->ic_lintval_backup && |
| ic->ic_ocac.ocac_bcn_intval_set) { |
| ieee80211_beacon_interval_set(ic, ic->ic_lintval_backup); |
| ic->ic_ocac.ocac_bcn_intval_set = 0; |
| ic->ic_ocac.ocac_counts.restore_bcn_intval++; |
| OCACDBG(OCACLOG_NOTICE, "restore beacon interval to %u\n", |
| ic->ic_lintval_backup); |
| } |
| } |
| |
| static void ieee80211_ocac_trigger_channel_switch(unsigned long arg) |
| { |
| struct ieee80211com *ic = (struct ieee80211com *)arg; |
| struct ieee80211_channel *chan = ic->ic_csa_chan; |
| |
| /* don't run CAC on new channel */ |
| chan->ic_flags |= IEEE80211_CHAN_DFS_OCAC_DONE; |
| ieee80211_finish_csa((unsigned long)ic); |
| chan->ic_flags &= ~IEEE80211_CHAN_DFS_OCAC_DONE; |
| |
| ieee80211_ocac_restore_beacon_interval(ic); |
| |
| return; |
| } |
| |
| static int |
| ieee80211_ocac_check_radar_by_chan_ieee(struct ieee80211com *ic, int chan_ieee) |
| { |
| struct ieee80211_channel *chan; |
| |
| if (chan_ieee) { |
| chan = ieee80211_find_channel_by_ieee(ic, chan_ieee); |
| if (chan && (chan->ic_flags & IEEE80211_CHAN_RADAR)) { |
| ic->ic_ocac.ocac_counts.csw_fail_radar++; |
| OCACDBG(OCACLOG_NOTICE, "switch channel failed " |
| "because radar detected on channel: %u\n", |
| chan_ieee); |
| return -1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * Change channel to DFS channel after off-channel CAC is completed |
| * return: |
| * -1: channel switch failed |
| * 0 : channel switch succeeded. |
| */ |
| static int |
| ieee80211_ocac_change_channel(struct ieee80211com *ic, struct ieee80211_channel *newchan) |
| { |
| int ret; |
| uint32_t cur_cca_intf = 0; |
| uint32_t new_cca_intf = 0; |
| int chan2_ieee = 0; |
| |
| struct ap_state *as; |
| |
| if (ic->ic_ocac.ocac_cfg.ocac_report_only) { |
| ic->ic_ocac.ocac_counts.csw_rpt_only++; |
| OCACDBG(OCACLOG_NOTICE, "didn't switch channel for report only\n"); |
| return -1; |
| } |
| |
| if (ic->ic_dfs_is_eu_region() == true) { |
| ic->ic_ocac.ocac_counts.no_channel_change_eu++; |
| OCACDBG(OCACLOG_NOTICE, "Setting state of dfs channel as AVAILABLE\n"); |
| if (ic->ic_mark_channel_availability_status) { |
| ic->ic_mark_channel_availability_status(ic, ic->ic_ocac.ocac_chan, |
| IEEE80211_CHANNEL_STATUS_AVAILABLE); |
| } |
| return -1; |
| } |
| |
| as = ic->ic_scan->ss_scs_priv; |
| if (as->as_cca_intf[ic->ic_curchan->ic_ieee] != SCS_CCA_INTF_INVALID) { |
| cur_cca_intf = 100 * as->as_cca_intf[ic->ic_curchan->ic_ieee] |
| / IEEE80211_SCS_CCA_INTF_SCALE; |
| } |
| if (as->as_cca_intf[newchan->ic_ieee] != SCS_CCA_INTF_INVALID) { |
| new_cca_intf = 100 * as->as_cca_intf[newchan->ic_ieee] |
| / IEEE80211_SCS_CCA_INTF_SCALE; |
| } |
| |
| if ((new_cca_intf > ic->ic_ocac.ocac_cfg.ocac_params.thresh_cca_intf) |
| && (new_cca_intf > cur_cca_intf)) { |
| ic->ic_ocac.ocac_counts.csw_fail_intf++; |
| OCACDBG(OCACLOG_NOTICE, "can't switch to channel %u, " |
| "cur_intf: %u, new_intf: %u\n", |
| newchan->ic_ieee, cur_cca_intf, new_cca_intf); |
| return -1; |
| } |
| |
| if (ieee80211_get_bw(ic) >= BW_HT40) { |
| chan2_ieee = ieee80211_find_sec_chan(newchan); |
| if (ieee80211_ocac_check_radar_by_chan_ieee(ic, chan2_ieee)) { |
| return -1; |
| } |
| } |
| |
| if (ieee80211_get_bw(ic) >= BW_HT80) { |
| chan2_ieee = ieee80211_find_sec40u_chan(newchan); |
| if (ieee80211_ocac_check_radar_by_chan_ieee(ic, chan2_ieee)) { |
| return -1; |
| } |
| chan2_ieee = ieee80211_find_sec40l_chan(newchan); |
| if (ieee80211_ocac_check_radar_by_chan_ieee(ic, chan2_ieee)) { |
| return -1; |
| } |
| } |
| |
| ret = ieee80211_enter_csa(ic, newchan, ieee80211_ocac_trigger_channel_switch, |
| IEEE80211_CSW_REASON_OCAC, |
| IEEE80211_DEFAULT_CHANCHANGE_TBTT_COUNT, |
| IEEE80211_CSA_MUST_STOP_TX, |
| IEEE80211_CSA_F_BEACON | IEEE80211_CSA_F_ACTION); |
| if (ret == 0) { |
| ic->ic_ocac.ocac_counts.csw_success++; |
| DFS_S_DBG_QEVT(ic2dev(ic), "DFS_s_radio: CAC completed and start working on " |
| "channel %u\n", newchan->ic_ieee); |
| } else { |
| ic->ic_ocac.ocac_counts.csw_fail_csa++; |
| OCACDBG(OCACLOG_NOTICE, "switch to channel %u failed\n", newchan->ic_ieee); |
| } |
| |
| return ret; |
| } |
| |
| /* |
| * * Stop off-channel CAC |
| * */ |
| static int |
| ieee80211_wireless_stop_ocac(struct ieee80211vap *vap) |
| { |
| struct ieee80211com *ic = vap->iv_ic; |
| |
| if (vap->iv_opmode != IEEE80211_M_HOSTAP) { |
| printk("DFS seamless radio is only supported on APs"); |
| return -1; |
| } |
| |
| del_timer(&ic->ic_ocac.ocac_timer); |
| |
| if (ic->ic_set_ocac(vap, NULL)) { |
| return -1; |
| } |
| |
| if (ic->ic_ocac.ocac_running) { |
| ic->ic_ocac.ocac_running = 0; |
| ic->ic_pm_reason = IEEE80211_PM_LEVEL_STOP_OCAC_SDFS; |
| ieee80211_pm_queue_work(ic); |
| ic->ic_ocac.ocac_counts.pm_update++; |
| } |
| ieee80211_ocac_restore_beacon_interval(ic); |
| ieee80211_ocac_clean_stats(ic, IEEE80211_OCAC_CLEAN_STATS_STOP); |
| ic->ic_ocac_release_frame(ic, 1); |
| |
| printk("DFS seamless radio is stopped\n"); |
| |
| return 0; |
| } |
| |
| static __inline__ uint32_t |
| ieee80211_ocac_get_param_duration(struct ieee80211com *ic, struct ieee80211_channel *dfs_chan) |
| { |
| return (ieee80211_is_on_weather_channel(ic, dfs_chan) ? |
| ic->ic_ocac.ocac_cfg.ocac_params.wea_duration_secs : |
| ic->ic_ocac.ocac_cfg.ocac_params.duration_secs); |
| } |
| |
| static __inline__ uint32_t |
| ieee80211_ocac_get_param_cac_time(struct ieee80211com *ic, struct ieee80211_channel *dfs_chan) |
| { |
| return (ieee80211_is_on_weather_channel(ic, dfs_chan) ? |
| ic->ic_ocac.ocac_cfg.ocac_params.wea_cac_time_secs : |
| ic->ic_ocac.ocac_cfg.ocac_params.cac_time_secs); |
| } |
| |
| static __inline uint8_t |
| ieee80211_ocac_get_backoff_count(void) |
| { |
| uint8_t count; |
| |
| /* Random number in [8, 64] */ |
| get_random_bytes(&count, sizeof(count)); |
| count = (count % 56) + 8; |
| |
| return count; |
| } |
| |
| static void |
| ieee80211_ocac_check_simul_cac(struct ieee80211vap *vap) |
| { |
| struct ieee80211com *ic = vap->iv_ic; |
| |
| /* Prevent multiple AP's from performing simultaneous S-DFS based on traffic_ctrl */ |
| if (!ic->ic_ocac.ocac_cfg.ocac_params.traffic_ctrl) { |
| ic->ic_ocac.ocac_available = OCAC_AVAILABLE; |
| return; |
| } |
| |
| if (ic->ic_ocac.ocac_available == OCAC_AVAILABLE) { |
| spin_lock(&ic->ic_ocac.ocac_lock); |
| |
| if (ic->ic_ocac.ocac_rx_state.state == OCAC_STATE_ONGOING) { |
| OCACDBG(OCACLOG_NOTICE, "DFS seamless radio is pending because another AP is doing " |
| "OCAC at this moment\n"); |
| ic->ic_ocac.ocac_counts.cac_in_neighbourhood++; |
| |
| ic->ic_ocac.ocac_available = OCAC_UNAVAILABLE; |
| } |
| |
| spin_unlock(&ic->ic_ocac.ocac_lock); |
| } else { |
| |
| if (!ic->ic_ocac.ocac_backoff_in_progress) { |
| ic->ic_ocac.ocac_backoff_count = ieee80211_ocac_get_backoff_count(); |
| |
| spin_lock(&ic->ic_ocac.ocac_lock); |
| |
| switch (ic->ic_ocac.ocac_rx_state.state) { |
| case OCAC_STATE_NONE: |
| ic->ic_ocac.ocac_backoff_in_progress = 1; |
| break; |
| case OCAC_STATE_BACKOFF: |
| if (ic->ic_ocac.ocac_backoff_count < ic->ic_ocac.ocac_rx_state.param) |
| ic->ic_ocac.ocac_backoff_in_progress = 1; |
| break; |
| case OCAC_STATE_ONGOING: |
| break; |
| } |
| |
| spin_unlock(&ic->ic_ocac.ocac_lock); |
| |
| if (ic->ic_ocac.ocac_backoff_in_progress) { |
| init_completion(&ic->ic_ocac.ocac_backoff_completion); |
| |
| /* MuC will trigger an event and move to state NONE when param reaches 0 */ |
| ic->ic_update_ocac_state_ie(ic, OCAC_STATE_BACKOFF, |
| ic->ic_ocac.ocac_backoff_count); |
| } |
| |
| } else { |
| if (try_wait_for_completion(&ic->ic_ocac.ocac_backoff_completion)) { |
| spin_lock(&ic->ic_ocac.ocac_lock); |
| |
| switch (ic->ic_ocac.ocac_rx_state.state) { |
| case OCAC_STATE_NONE: |
| ic->ic_ocac.ocac_available = OCAC_AVAILABLE; |
| break; |
| case OCAC_STATE_BACKOFF: |
| case OCAC_STATE_ONGOING: |
| break; |
| } |
| |
| spin_unlock(&ic->ic_ocac.ocac_lock); |
| |
| ic->ic_ocac.ocac_backoff_in_progress = 0; |
| } |
| } |
| } |
| } |
| |
| static inline int ieee80211_ocac_check_bcn_scheme(struct ieee80211vap *vap, uint32_t num_vap, |
| uint32_t *reset_duration) |
| { |
| struct ieee80211com *ic = vap->iv_ic; |
| /* If two VAPs are configured, the beaconing scheme should be scheme 1 */ |
| if (num_vap == OCAC_MAX_SUPPORTED_VAPS && ic->ic_beaconing_scheme == QTN_BEACONING_SCHEME_0) { |
| OCACDBG(OCACLOG_NOTICE, "DFS seamless radio with two MBSSIDs is pending because" |
| " of beaconing scheme initiated at boot-up. Updating scheme...\n"); |
| ic->ic_ocac.ocac_counts.beacon_scheme0++; |
| *reset_duration = 1; |
| |
| if (ic->ic_pm_state[QTN_PM_CURRENT_LEVEL] < BOARD_PM_LEVEL_DUTY) { |
| /* Force disassoc of all devices to allow resynchronisation to the beacons */ |
| ieee80211_wireless_reassoc_all_vaps(ic); |
| ic->ic_pm_reason = IEEE80211_PM_LEVEL_BCN_SCHEME_CHANGED_FOR_2VAPS; |
| ieee80211_pm_queue_work_custom(ic, BOARD_PM_WLAN_AP_IDLE_AFTER_BEACON_SCHEME); |
| |
| if (ic->ic_set_beaconing_scheme(vap, IEEE80211_PARAM_BEACONING_SCHEME, |
| QTN_BEACONING_SCHEME_1) < 0) { |
| OCACDBG(OCACLOG_NOTICE, "DFS seamless radio is pending due to beaconing " |
| "scheme update failure\n"); |
| OCACDBG(OCACLOG_NOTICE, "To enable DFS seamless radio with two MBSSIDs," |
| "please save DFS seamless radio configuration and reboot the board" |
| "or restart DFS seamless radio\n"); |
| return -1; |
| } |
| } else { |
| OCACDBG(OCACLOG_NOTICE, "DFS seamless radio is pending due to wrong " |
| "beaconing scheme\n"); |
| return -1; |
| } |
| } |
| return 0; |
| } |
| |
| static void |
| ieee80211_ocac_timer_func(unsigned long arg) |
| { |
| struct ieee80211vap *vap = (struct ieee80211vap *)arg; |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_channel *chan = NULL; |
| struct ieee80211_channel *dfs_chan = NULL; |
| struct ieee80211vap *tmp_vap; |
| uint32_t num_vap = 0; |
| uint32_t vap_idx; |
| uint32_t radar_detected; |
| uint32_t reset_duration = 0; |
| uint8_t prev_ocac_running; |
| |
| if (vap->iv_state != IEEE80211_S_RUN) { |
| OCACDBG(OCACLOG_NOTICE, "DFS seamless radio is pending" |
| " because the AP is not in running state\n"); |
| ic->ic_ocac.ocac_counts.ap_not_running++; |
| goto set_ocac; |
| } |
| |
| if (ic->ic_flags & IEEE80211_F_SCAN) { |
| OCACDBG(OCACLOG_NOTICE, "DFS seamless radio is pending" |
| " because channel scanning is in progress\n"); |
| ic->ic_ocac.ocac_counts.chan_scanning++; |
| goto set_ocac; |
| } |
| |
| if ((ic->ic_curchan != IEEE80211_CHAN_ANYC) && |
| (ic->ic_curchan->ic_flags & IEEE80211_CHAN_DFS)) { |
| OCACDBG(OCACLOG_NOTICE, "DFS seamless radio is pending" |
| " because the current channel is a DFS channel\n"); |
| ic->ic_ocac.ocac_counts.curchan_dfs++; |
| ieee80211_ocac_restore_beacon_interval(ic); |
| reset_duration = 1; |
| goto set_ocac; |
| } |
| |
| TAILQ_FOREACH(tmp_vap, &ic->ic_vaps, iv_next) { |
| if (tmp_vap->iv_opmode == IEEE80211_M_WDS && |
| !IEEE80211_VAP_WDS_IS_MBS(tmp_vap)) { |
| OCACDBG(OCACLOG_NOTICE, "DFS seamless radio is pending" |
| " because a WDS interface exists\n"); |
| ic->ic_ocac.ocac_counts.wds_exist++; |
| ieee80211_ocac_restore_beacon_interval(ic); |
| reset_duration = 1; |
| goto set_ocac; |
| } |
| if (tmp_vap->iv_opmode != IEEE80211_M_HOSTAP) |
| continue; |
| if ((tmp_vap->iv_state != IEEE80211_S_RUN) |
| && (tmp_vap->iv_state != IEEE80211_S_SCAN)) |
| continue; |
| num_vap++; |
| vap_idx = ic->ic_get_vap_idx(tmp_vap); |
| if (vap_idx > 1) { |
| OCACDBG(OCACLOG_NOTICE, "DFS seamless radio is pending because" |
| " unsupported MBSSID(wifi%d) is configured. Supported " |
| "MBSSIDs are wifi0, wifi1\n", vap_idx); |
| /* Support OCAC with two VAPs only - wifi0 and wifi1 */ |
| ic->ic_ocac.ocac_counts.unsupported_mbssid++; |
| ieee80211_ocac_restore_beacon_interval(ic); |
| reset_duration = 1; |
| goto set_ocac; |
| } |
| } |
| |
| if (ieee80211_ocac_check_bcn_scheme(vap, num_vap, &reset_duration) < 0) { |
| ieee80211_ocac_restore_beacon_interval(ic); |
| goto set_ocac; |
| } |
| |
| if (is_ieee80211_chan_valid(ic->ic_ocac.ocac_chan) |
| && ieee80211_is_chan_available(ic->ic_ocac.ocac_chan)) { |
| ic->ic_ocac.ocac_repick_dfs_chan = 1; |
| } |
| |
| if (ic->ic_ocac.ocac_chan == NULL || |
| ic->ic_ocac.ocac_repick_dfs_chan) { |
| /* initial off channel selection */ |
| ic->ic_ocac.ocac_counts.init_offchan++; |
| ic->ic_ocac.ocac_chan = ieee80211_ocac_pick_dfs_channel(ic, |
| ic->ic_ocac.ocac_cfg.ocac_chan_ieee); |
| if (ic->ic_ocac.ocac_chan == NULL) { |
| OCACDBG(OCACLOG_NOTICE, "Init DFS channel selection (%d) error\n", |
| ic->ic_ocac.ocac_cfg.ocac_chan_ieee); |
| ic->ic_ocac.ocac_counts.no_offchan++; |
| reset_duration = 1; |
| goto set_ocac; |
| } |
| ic->ic_ocac.ocac_repick_dfs_chan = 0; |
| ieee80211_ocac_clean_stats(ic, IEEE80211_OCAC_CLEAN_STATS_RESET); |
| DFS_S_DBG_QEVT(ic2dev(ic), "DFS_s_radio: CAC started for channel %u\n", |
| ic->ic_ocac.ocac_chan->ic_ieee); |
| OCACDBG(OCACLOG_NOTICE, "CAC duration: %u secs, minimal valid CAC time: %u secs\n", |
| ieee80211_ocac_get_param_duration(ic, ic->ic_ocac.ocac_chan), |
| ieee80211_ocac_get_param_cac_time(ic, ic->ic_ocac.ocac_chan)); |
| } |
| |
| ieee80211_ocac_check_simul_cac(vap); |
| if (ic->ic_ocac.ocac_available != OCAC_AVAILABLE) { |
| ieee80211_ocac_restore_beacon_interval(ic); |
| reset_duration = 1; |
| goto set_ocac; |
| } |
| |
| ieee80211_ocac_set_beacon_interval(ic); |
| |
| radar_detected = ic->ic_ocac.ocac_chan->ic_flags & IEEE80211_CHAN_RADAR; |
| if (radar_detected) { |
| ic->ic_ocac.ocac_counts.radar_detected++; |
| OCACDBG(OCACLOG_NOTICE, "Radar was detected on channel %u\n", |
| ic->ic_ocac.ocac_chan->ic_ieee); |
| if (ic->ic_ocac.ocac_cfg.ocac_chan_ieee) { |
| OCACDBG(OCACLOG_NOTICE, "CAC stops when radar detected for test mode\n"); |
| goto stop_ocac; |
| } |
| chan = ieee80211_ocac_pick_dfs_channel(ic, |
| ic->ic_ocac.ocac_cfg.ocac_chan_ieee); |
| if (chan && ic->ic_ocac.ocac_chan != chan) { |
| ic->ic_ocac.ocac_chan = chan; |
| DFS_S_DBG_QEVT(ic2dev(ic), "DFS_s_radio: CAC restarted for channel %u\n", |
| chan->ic_ieee); |
| OCACDBG(OCACLOG_NOTICE, "CAC duration: %u secs, minimal valid CAC time: %u secs\n", |
| ieee80211_ocac_get_param_duration(ic, ic->ic_ocac.ocac_chan), |
| ieee80211_ocac_get_param_cac_time(ic, ic->ic_ocac.ocac_chan)); |
| } |
| ieee80211_ocac_clean_stats(ic, IEEE80211_OCAC_CLEAN_STATS_RESET); |
| if (chan == NULL) { |
| reset_duration = 1; |
| goto set_ocac; |
| } |
| } else { |
| #define CAC_STATUS_COMPLETE 0x1 |
| #define CAC_STATUS_SUCCESS 0x2 |
| uint8_t cac_status = 0; |
| int retval; |
| |
| if (ic->ic_ocac.ocac_accum_cac_time_ms >= |
| ieee80211_ocac_get_param_cac_time(ic, ic->ic_ocac.ocac_chan) * 1000) { |
| |
| cac_status = CAC_STATUS_SUCCESS | CAC_STATUS_COMPLETE; |
| |
| } else { |
| if (ic->ic_ocac.ocac_accum_duration_secs >= |
| ieee80211_ocac_get_param_duration(ic, ic->ic_ocac.ocac_chan)) { |
| |
| cac_status = CAC_STATUS_COMPLETE; |
| } |
| } |
| |
| if (cac_status) { |
| ic->ic_ocac.ocac_available = OCAC_UNAVAILABLE; |
| |
| reset_duration = 1; |
| |
| if (cac_status & CAC_STATUS_SUCCESS) { |
| ic->ic_ocac.ocac_counts.cac_success++; |
| OCACDBG(OCACLOG_NOTICE, "CAC succeed and no radar\n"); |
| |
| retval = ieee80211_ocac_change_channel(ic, ic->ic_ocac.ocac_chan); |
| if (ic->ic_ocac.ocac_cfg.ocac_chan_ieee) { |
| if (retval == 0) { |
| OCACDBG(OCACLOG_NOTICE, "CAC stops after switching" |
| " to dfs channel for test mode\n"); |
| goto stop_ocac; |
| } else { |
| goto set_ocac; |
| } |
| } else { |
| if (!ic->ic_ocac.ocac_cfg.ocac_report_only) { |
| ic->ic_ocac.ocac_repick_dfs_chan = 1; |
| } |
| goto set_ocac; |
| } |
| } else { |
| ic->ic_ocac.ocac_counts.cac_failed++; |
| OCACDBG(OCACLOG_NOTICE, "CAC failed and restarted, CAC accumulated %u, CAC desired %u\n", |
| ic->ic_ocac.ocac_accum_cac_time_ms / 1000, |
| ieee80211_ocac_get_param_cac_time(ic, ic->ic_ocac.ocac_chan)); |
| |
| goto set_ocac; |
| } |
| } |
| } |
| |
| dfs_chan = ic->ic_ocac.ocac_chan; |
| |
| set_ocac: |
| ic->ic_set_ocac(vap, dfs_chan); |
| |
| prev_ocac_running = ic->ic_ocac.ocac_running; |
| ic->ic_ocac.ocac_running = dfs_chan ? 1 : 0; |
| |
| if (prev_ocac_running != ic->ic_ocac.ocac_running) { |
| ic->ic_pm_reason = IEEE80211_PM_LEVEL_OCAC_SDFS_TIMER; |
| ieee80211_pm_queue_work(ic); |
| ic->ic_ocac.ocac_counts.pm_update++; |
| } |
| |
| if (reset_duration) { |
| ieee80211_ocac_clean_stats(ic, IEEE80211_OCAC_CLEAN_STATS_RESET); |
| } else { |
| ic->ic_ocac.ocac_accum_duration_secs += |
| ic->ic_ocac.ocac_cfg.ocac_params.timer_interval; |
| } |
| |
| mod_timer(&ic->ic_ocac.ocac_timer, |
| jiffies + (ic->ic_ocac.ocac_cfg.ocac_params.timer_interval * HZ)); |
| |
| return; |
| |
| stop_ocac: |
| ieee80211_wireless_stop_ocac(vap); |
| ic->ic_ocac.ocac_cfg.ocac_enable = 0; |
| ic->ic_beacon_update(vap); |
| } |
| |
| struct ieee80211_ocac_params_dflt { |
| char region[4]; |
| struct ieee80211_ocac_params dflt_params; |
| } ocac_params_dflt[] = { |
| { |
| "EU", |
| { |
| 1, /* traffic control */ |
| 23, /* secure dwell */ |
| 40, /* dwell time */ |
| 720, /* duration */ |
| 240, /* cac time */ |
| 46, /* dwell time for weather channel */ |
| 11520, /* duration for weather channel */ |
| 4329, /* cac time for weather channel */ |
| 90, /* thresh fat */ |
| 30, /* thresh traffic */ |
| 10, /* thresh fat dec */ |
| 20, /* thresh cca intf */ |
| 10, /* offset txhalt */ |
| 7, /* offset offchan */ |
| 2, /* timer interval */ |
| 100 /* beacon interval */ |
| } |
| }, |
| { |
| "US", |
| { |
| 0, /* traffic control */ |
| 23, /* secure dwell */ |
| 80, /* dwell time */ |
| 70, /* duration */ |
| 50, /* cac time */ |
| 80, /* dwell time for weather channel*/ |
| 70, /* duration for weather channel */ |
| 50, /* cac time for weather channel */ |
| 75, /* thresh fat */ |
| 3, /* thresh traffic */ |
| 10, /* thresh fat dec */ |
| 20, /* thresh cca intf */ |
| 5, /* offset txhalt */ |
| 5, /* offset offchan */ |
| 2, /* timer interval */ |
| 100 /* beacon interval */ |
| } |
| } |
| }; |
| |
| static int |
| ieee80211_ocac_is_region_supported(const char *region) |
| { |
| int i; |
| |
| for (i = 0; i < ARRAY_SIZE(ocac_params_dflt); i++) { |
| if (!strcasecmp(region, ocac_params_dflt[i].region)) { |
| return 1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| void |
| ieee80211_ocac_update_params(struct ieee80211com *ic, const char *region) |
| { |
| int i; |
| |
| if (ic->ic_ocac.ocac_cfg.ocac_enable) { |
| return; |
| } |
| |
| if (!strcasecmp(ic->ic_ocac.ocac_cfg.ocac_region, region)) { |
| return; |
| } |
| |
| strncpy(ic->ic_ocac.ocac_cfg.ocac_region, region, |
| sizeof(ic->ic_ocac.ocac_cfg.ocac_region)); |
| |
| for (i = 0; i < ARRAY_SIZE(ocac_params_dflt); i++) { |
| if (!strcasecmp(ic->ic_ocac.ocac_cfg.ocac_region, ocac_params_dflt[i].region)) { |
| memcpy(&ic->ic_ocac.ocac_cfg.ocac_params, &ocac_params_dflt[i].dflt_params, |
| sizeof(ic->ic_ocac.ocac_cfg.ocac_params)); |
| printk("DFS_s_radio: parameters updated, region: %s\n", |
| ic->ic_ocac.ocac_cfg.ocac_region); |
| break; |
| } |
| } |
| } |
| EXPORT_SYMBOL(ieee80211_ocac_update_params); |
| |
| enum qtn_ocac_unsupported_reason { |
| QTN_OCAC_REASON_AP_MODE = 1, |
| QTN_OCAC_REASON_REGION, |
| QTN_OCAC_REASON_WDS, |
| QTN_OCAC_REASON_MBSSID, |
| QTN_OCAC_REASON_NO_DFS_CHAN, |
| QTN_OCAC_REASON_NO_NON_DFS_CHAN, |
| QTN_OCAC_REASON_MAX = QTN_OCAC_REASON_NO_NON_DFS_CHAN |
| }; |
| |
| static char *qtn_ocac_reason_str[QTN_OCAC_REASON_MAX] = { |
| "supported in AP mode only", |
| "not supported for current region", |
| "not supported in the case of WDS interface exist", |
| "not supported in the case of unsupported MBSSID(except wifi0 and wifi1) is configured", |
| "not supported because no DFS channel", |
| "not supported because no non-DFS channel" |
| }; |
| |
| static unsigned int |
| ieee80211_wireless_is_ocac_unsupported(struct ieee80211vap *vap) |
| { |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211vap *tmp_vap; |
| struct ieee80211_channel *c; |
| uint32_t num_dfs_chan = 0; |
| uint32_t num_non_dfs_chan = 0; |
| unsigned int ret = 0; |
| int i; |
| |
| if (vap->iv_opmode != IEEE80211_M_HOSTAP) { |
| ret = QTN_OCAC_REASON_AP_MODE; |
| goto done; |
| } |
| |
| if (!ieee80211_ocac_is_region_supported(ic->ic_ocac.ocac_cfg.ocac_region)) { |
| ret = QTN_OCAC_REASON_REGION; |
| goto done; |
| } |
| |
| TAILQ_FOREACH(tmp_vap, &ic->ic_vaps, iv_next) { |
| if (tmp_vap->iv_opmode == IEEE80211_M_WDS && |
| !IEEE80211_VAP_WDS_IS_MBS(tmp_vap)) { |
| ic->ic_ocac.ocac_counts.wds_exist++; |
| ret = QTN_OCAC_REASON_WDS; |
| goto done; |
| } |
| if (tmp_vap->iv_opmode != IEEE80211_M_HOSTAP) |
| continue; |
| if ((tmp_vap->iv_state != IEEE80211_S_RUN) |
| && (tmp_vap->iv_state != IEEE80211_S_SCAN)) |
| continue; |
| if (ic->ic_get_vap_idx(tmp_vap) > 1) { |
| /* Only support OCAC with two VAPs - wifi0 and wifi1 */ |
| ic->ic_ocac.ocac_counts.unsupported_mbssid++; |
| ret = QTN_OCAC_REASON_MBSSID; |
| goto done; |
| } |
| } |
| |
| for (i = 0; i < ic->ic_nchans; i++) { |
| c = &ic->ic_channels[i]; |
| if (c == NULL || isclr(ic->ic_chan_active, c->ic_ieee) || |
| !ieee80211_chan_allowed_in_band(ic, c, vap->iv_opmode)) { |
| continue; |
| } |
| if (c->ic_flags & IEEE80211_CHAN_DFS) { |
| num_dfs_chan++; |
| } else { |
| num_non_dfs_chan++; |
| } |
| } |
| if (num_dfs_chan == 0) { |
| ret = QTN_OCAC_REASON_NO_DFS_CHAN; |
| goto done; |
| } |
| if (num_non_dfs_chan == 0) { |
| ret = QTN_OCAC_REASON_NO_NON_DFS_CHAN; |
| goto done; |
| } |
| |
| done: |
| return ret; |
| } |
| |
| static int |
| ieee80211_wireless_check_if_ocac_supported(struct ieee80211vap *vap, int chan_ieee) |
| { |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_channel *chan; |
| unsigned int reason; |
| |
| reason = ieee80211_wireless_is_ocac_unsupported(vap); |
| if (reason) { |
| if (reason <= QTN_OCAC_REASON_MAX) { |
| printk("DFS seamless radio is %s\n", qtn_ocac_reason_str[reason - 1]); |
| } |
| return -1; |
| } |
| |
| if (chan_ieee) { |
| chan = ieee80211_find_channel_by_ieee(ic, chan_ieee); |
| if (chan == NULL || !(chan->ic_flags & IEEE80211_CHAN_DFS)) { |
| DFS_S_DBG_QEVT(ic2dev(ic), "DFS_s_radio: channel %u is not a valid DFS channel\n", |
| chan_ieee); |
| return -1; |
| } else if (chan->ic_flags & IEEE80211_CHAN_RADAR) { |
| DFS_S_DBG_QEVT(ic2dev(ic), "DFS_s_radio: radar detected on channel %u\n", |
| chan_ieee); |
| return -1; |
| } |
| } |
| return 0; |
| } |
| |
| /* |
| * Start off-channel CAC |
| */ |
| static void |
| ieee80211_wireless_start_ocac(struct ieee80211vap *vap, int chan_ieee) |
| { |
| struct ieee80211com *ic = vap->iv_ic; |
| |
| ic->ic_ocac.ocac_available = OCAC_UNAVAILABLE; |
| |
| ic->ic_ocac.ocac_cfg.ocac_chan_ieee = chan_ieee; |
| ic->ic_ocac.ocac_chan = NULL; /* reset the dfs channel */ |
| |
| ieee80211_ocac_clean_stats(ic, IEEE80211_OCAC_CLEAN_STATS_START); |
| |
| init_timer(&ic->ic_ocac.ocac_timer); |
| ic->ic_ocac.ocac_timer.function = ieee80211_ocac_timer_func; |
| ic->ic_ocac.ocac_timer.data = (unsigned long) vap; |
| ic->ic_ocac.ocac_timer.expires = jiffies + |
| ic->ic_ocac.ocac_cfg.ocac_timer_expire_init * HZ; |
| add_timer(&ic->ic_ocac.ocac_timer); |
| |
| printk("Starting DFS seamless radio...\n"); |
| } |
| |
| static void |
| ieee80211_ocac_set_dump_counts(struct ieee80211com *ic, int value) |
| { |
| struct ieee80211_ocac_counts *ocac_counts = &ic->ic_ocac.ocac_counts; |
| |
| if (value) { |
| printk("DFS_s_radio counts:\n"); |
| printk(" ap_not_running: %u\n", ocac_counts->ap_not_running); |
| printk(" chan_scanning: %u\n", ocac_counts->chan_scanning); |
| printk(" curchan_dfs: %u\n", ocac_counts->curchan_dfs); |
| printk(" init_offchan: %u\n", ocac_counts->init_offchan); |
| printk(" no_offchan: %u\n", ocac_counts->no_offchan); |
| printk(" pick_offchan: %u\n", ocac_counts->pick_offchan); |
| printk(" invalid_offchan: %u\n", ocac_counts->invalid_offchan); |
| printk(" set_bcn_intval: %u\n", ocac_counts->set_bcn_intval); |
| printk(" restore_bcn_intval: %u\n", ocac_counts->restore_bcn_intval); |
| printk(" pm_update: %u\n", ocac_counts->pm_update); |
| printk(" unsupported_mbssid: %u\n", ocac_counts->unsupported_mbssid); |
| printk(" beacon_scheme0: %u\n", ocac_counts->beacon_scheme0); |
| printk(" wds_exist: %u\n", ocac_counts->wds_exist); |
| printk(" set_run: %u\n", ocac_counts->set_run); |
| printk(" set_pend: %u\n", ocac_counts->set_pend); |
| printk(" skip_set_run: %u\n", ocac_counts->skip_set_run); |
| printk(" skip_set_pend: %u\n", ocac_counts->skip_set_pend); |
| printk(" alloc_skb_error: %u\n", ocac_counts->alloc_skb_error); |
| printk(" set_frame_error: %u\n", ocac_counts->set_frame_error); |
| printk(" hostlink_err: %u\n", ocac_counts->hostlink_err); |
| printk(" hostlink_ok: %u\n", ocac_counts->hostlink_ok); |
| printk(" radar_detected: %u\n", ocac_counts->radar_detected); |
| printk(" cac_failed: %u\n", ocac_counts->cac_failed); |
| printk(" cac_success: %u\n", ocac_counts->cac_success); |
| printk(" csw_rpt_only: %u\n", ocac_counts->csw_rpt_only); |
| printk(" csw_fail_intf: %u\n", ocac_counts->csw_fail_intf); |
| printk(" csw_fail_radar: %u\n", ocac_counts->csw_fail_radar); |
| printk(" csw_fail_csa: %u\n", ocac_counts->csw_fail_csa); |
| printk(" csw_success: %u\n", ocac_counts->csw_success); |
| printk(" clean_stats_reset: %u\n", ocac_counts->clean_stats_reset); |
| printk(" clean_stats_start: %u\n", ocac_counts->clean_stats_start); |
| printk(" clean_stats_stop: %u\n", ocac_counts->clean_stats_stop); |
| printk(" tasklet_off_chan: %u\n", ocac_counts->tasklet_off_chan); |
| printk(" tasklet_data_chan: %u\n", ocac_counts->tasklet_data_chan); |
| printk(" intr_off_chan: %u\n", ocac_counts->intr_off_chan); |
| printk(" intr_data_chan: %u\n", ocac_counts->intr_data_chan); |
| printk(" no_channel_change_eu: %u\n", ocac_counts->no_channel_change_eu); |
| } else { |
| /* clear ocac counts */ |
| memset(ocac_counts, 0, sizeof(ic->ic_ocac.ocac_counts)); |
| } |
| } |
| |
| static void |
| ieee80211_ocac_dump_tsflog(struct ieee80211com *ic) |
| { |
| int i; |
| int cur_index; |
| int next_index; |
| uint32_t time_sw_offchan; |
| uint32_t time_sw_datachan; |
| uint32_t time_on_offchan; |
| uint32_t time_on_datachan; |
| struct ieee80211_ocac_tsflog tsflog; |
| |
| IEEE80211_LOCK_BH(ic); |
| memcpy(&tsflog, &ic->ic_ocac.ocac_tsflog, sizeof(struct ieee80211_ocac_tsflog)); |
| IEEE80211_UNLOCK_BH(ic); |
| |
| printk(" sw_offchan on_offchan sw_datachan on_datachan\n"); |
| cur_index = tsflog.log_index; |
| for (i = 0; i < QTN_OCAC_TSF_LOG_DEPTH; i++) |
| { |
| next_index = (cur_index + 1) % QTN_OCAC_TSF_LOG_DEPTH; |
| |
| time_sw_offchan = (uint32_t)(tsflog.tsf_log[cur_index][OCAC_TSF_LOG_GOTO_OFF_CHAN_DONE] - |
| tsflog.tsf_log[cur_index][OCAC_TSF_LOG_GOTO_OFF_CHAN]); |
| time_on_offchan = (uint32_t)(tsflog.tsf_log[cur_index][OCAC_TSF_LOG_GOTO_DATA_CHAN] - |
| tsflog.tsf_log[cur_index][OCAC_TSF_LOG_GOTO_OFF_CHAN_DONE]); |
| time_on_datachan = (uint32_t)(tsflog.tsf_log[cur_index][OCAC_TSF_LOG_GOTO_OFF_CHAN] - |
| tsflog.tsf_log[cur_index][OCAC_TSF_LOG_GOTO_DATA_CHAN_DONE]); |
| time_sw_datachan = (uint32_t)(tsflog.tsf_log[next_index][OCAC_TSF_LOG_GOTO_DATA_CHAN_DONE] - |
| tsflog.tsf_log[cur_index][OCAC_TSF_LOG_GOTO_DATA_CHAN]); |
| cur_index = next_index; |
| printk(" %10u %10u %10u %10u\n", time_sw_offchan, time_on_offchan, |
| time_sw_datachan, time_on_datachan); |
| } |
| } |
| |
| static void |
| ieee80211_ocac_dump_cfg(struct ieee80211com *ic) |
| { |
| printk("DFS_s_radio cfg:\n"); |
| printk(" region: %s\n", ic->ic_ocac.ocac_cfg.ocac_region); |
| printk(" started: %u\n", ic->ic_ocac.ocac_cfg.ocac_enable); |
| printk(" debug_level: %u\n", ic->ic_ocac.ocac_cfg.ocac_debug_level); |
| printk(" report_only: %u\n", ic->ic_ocac.ocac_cfg.ocac_report_only); |
| printk(" off_channel: %u\n", ic->ic_ocac.ocac_cfg.ocac_chan_ieee); |
| printk(" timer_expire_init: %u\n", ic->ic_ocac.ocac_cfg.ocac_timer_expire_init); |
| printk(" secure_dwell_ms: %u\n", ic->ic_ocac.ocac_cfg.ocac_params.secure_dwell_ms); |
| printk(" dwell_time_ms: %u\n", ic->ic_ocac.ocac_cfg.ocac_params.dwell_time_ms); |
| printk(" duration_secs: %u\n", ic->ic_ocac.ocac_cfg.ocac_params.duration_secs); |
| printk(" cac_time_secs: %u\n", ic->ic_ocac.ocac_cfg.ocac_params.cac_time_secs); |
| printk(" wea_dwell_time_ms: %u\n", ic->ic_ocac.ocac_cfg.ocac_params.wea_dwell_time_ms); |
| printk(" wea_duration_secs: %u\n", ic->ic_ocac.ocac_cfg.ocac_params.wea_duration_secs); |
| printk(" wea_cac_time_secs: %u\n", ic->ic_ocac.ocac_cfg.ocac_params.wea_cac_time_secs); |
| printk(" thrshld_fat: %u\n", ic->ic_ocac.ocac_cfg.ocac_params.thresh_fat); |
| printk(" thrshld_traffic: %u\n", ic->ic_ocac.ocac_cfg.ocac_params.thresh_traffic); |
| printk(" thrshld_cca_intf: %u\n", ic->ic_ocac.ocac_cfg.ocac_params.thresh_cca_intf); |
| printk(" thrshld_fat_dec: %u\n", ic->ic_ocac.ocac_cfg.ocac_params.thresh_fat_dec); |
| printk(" timer_interval: %u\n", ic->ic_ocac.ocac_cfg.ocac_params.timer_interval); |
| printk(" traffic_ctrl: %u\n", ic->ic_ocac.ocac_cfg.ocac_params.traffic_ctrl); |
| printk(" offset_txhalt: %u\n", ic->ic_ocac.ocac_cfg.ocac_params.offset_txhalt); |
| printk(" offset_offchan: %u\n", ic->ic_ocac.ocac_cfg.ocac_params.offset_offchan); |
| printk(" beacon_interval: %u\n", ic->ic_ocac.ocac_cfg.ocac_params.beacon_interval); |
| } |
| |
| int |
| ieee80211_param_ocac_set(struct net_device *dev, struct ieee80211vap *vap, u_int32_t value) |
| { |
| struct ieee80211com *ic = vap->iv_ic; |
| uint32_t cmd = value >> IEEE80211_OCAC_COMMAND_S; |
| uint32_t arg = value & IEEE80211_OCAC_VALUE_M; |
| |
| if (!ieee80211_swfeat_is_supported(SWFEAT_ID_OCAC, 1)) |
| return -1; |
| |
| if (cmd >= IEEE80211_OCAC_SET_MAX) { |
| printk("%s: invalid DFS_s_radio setparam cmd %u, arg=%u\n", |
| dev->name, cmd, arg); |
| return -1; |
| } |
| |
| OCACDBG(OCACLOG_NOTICE, "setparam command: %u, value: 0x%x\n", cmd, arg); |
| |
| switch (cmd) { |
| case IEEE80211_OCAC_SET_ENABLE_AUTO_DFS: |
| if (ieee80211_wireless_check_if_ocac_supported(vap, arg) < 0) |
| return -1; |
| |
| ic->ic_ocac.ocac_cfg.ocac_params.auto_first_dfs_channel = arg & 0xFF; |
| arg = 0; |
| /* Fall through */ |
| case IEEE80211_OCAC_SET_ENABLE: |
| if (ic->ic_ocac.ocac_cfg.ocac_enable) { |
| printk("DFS seamless radio is already running\n"); |
| return -1; |
| } |
| if (ieee80211_wireless_check_if_ocac_supported(vap, arg) < 0) |
| return -1; |
| |
| ieee80211_wireless_start_ocac(vap, arg); |
| ic->ic_ocac.ocac_cfg.ocac_enable = 1; |
| ic->ic_beacon_update(vap); |
| break; |
| case IEEE80211_OCAC_SET_DISABLE: |
| if (ic->ic_ocac.ocac_cfg.ocac_enable) { |
| ieee80211_wireless_stop_ocac(vap); |
| ic->ic_ocac.ocac_cfg.ocac_enable = 0; |
| ic->ic_beacon_update(vap); |
| } |
| ic->ic_ocac.ocac_cfg.ocac_params.auto_first_dfs_channel = 0; |
| break; |
| case IEEE80211_OCAC_SET_DEBUG_LEVEL: |
| ic->ic_ocac.ocac_cfg.ocac_debug_level = arg; |
| break; |
| case IEEE80211_OCAC_SET_DWELL_TIME: |
| if (arg < IEEE80211_OCAC_DWELL_TIME_MIN || |
| arg > IEEE80211_OCAC_DWELL_TIME_MAX) { |
| printk("Invalid DFS_s_radio dwell time: %u\n", arg); |
| return -1; |
| } |
| ic->ic_ocac.ocac_cfg.ocac_params.dwell_time_ms = arg; |
| break; |
| case IEEE80211_OCAC_SET_SECURE_DWELL_TIME: |
| if (arg < IEEE80211_OCAC_SECURE_DWELL_TIME_MIN || |
| arg > IEEE80211_OCAC_SECURE_DWELL_TIME_MAX) { |
| printk("Invalid DFS_s_radio secure dwell time: %u\n", arg); |
| return -1; |
| } |
| ic->ic_ocac.ocac_cfg.ocac_params.secure_dwell_ms = arg; |
| break; |
| case IEEE80211_OCAC_SET_DURATION: |
| if (arg < IEEE80211_OCAC_DURATION_MIN || |
| arg > IEEE80211_OCAC_DURATION_MAX) { |
| printk("Invalid DFS_s_radio duration: %u\n", arg); |
| return -1; |
| } |
| ic->ic_ocac.ocac_cfg.ocac_params.duration_secs = arg; |
| break; |
| case IEEE80211_OCAC_SET_CAC_TIME: |
| if (arg < IEEE80211_OCAC_CAC_TIME_MIN || |
| arg > IEEE80211_OCAC_CAC_TIME_MAX) { |
| printk("Invalid DFS_s_radio cac time: %u\n", arg); |
| return -1; |
| } |
| ic->ic_ocac.ocac_cfg.ocac_params.cac_time_secs = arg; |
| break; |
| case IEEE80211_OCAC_SET_WEATHER_DWELL_TIME: |
| if (arg < IEEE80211_OCAC_DWELL_TIME_MIN || |
| arg > IEEE80211_OCAC_DWELL_TIME_MAX) { |
| printk("Invalid DFS_s_radio dwell time: %u\n", arg); |
| return -1; |
| } |
| ic->ic_ocac.ocac_cfg.ocac_params.wea_dwell_time_ms = arg; |
| break; |
| case IEEE80211_OCAC_SET_WEATHER_DURATION: |
| if (arg & IEEE80211_OCAC_COMPRESS_VALUE_F) { |
| arg = (arg & IEEE80211_OCAC_COMPRESS_VALUE_M) << 2; |
| } |
| if (arg < IEEE80211_OCAC_WEA_DURATION_MIN || |
| arg > IEEE80211_OCAC_WEA_DURATION_MAX) { |
| printk("Invalid DFS_s_radio duration for weather channel: %u\n", arg); |
| return -1; |
| } |
| ic->ic_ocac.ocac_cfg.ocac_params.wea_duration_secs = arg; |
| break; |
| case IEEE80211_OCAC_SET_WEATHER_CAC_TIME: |
| if (arg & IEEE80211_OCAC_COMPRESS_VALUE_F) { |
| arg = (arg & IEEE80211_OCAC_COMPRESS_VALUE_M) << 2; |
| } |
| if (arg < IEEE80211_OCAC_WEA_CAC_TIME_MIN || |
| arg > IEEE80211_OCAC_WEA_CAC_TIME_MAX) { |
| printk("Invalid DFS_s_radio cac time for weather channel: %u\n", arg); |
| return -1; |
| } |
| ic->ic_ocac.ocac_cfg.ocac_params.wea_cac_time_secs = arg; |
| break; |
| case IEEE80211_OCAC_SET_THRESHOLD_FAT: |
| if (arg < IEEE80211_OCAC_THRESHOLD_FAT_MIN || |
| arg > IEEE80211_OCAC_THRESHOLD_FAT_MAX) { |
| printk("Invalid DFS_s_radio fat threshold: %u\n", arg); |
| return -1; |
| } |
| ic->ic_ocac.ocac_cfg.ocac_params.thresh_fat = arg; |
| break; |
| case IEEE80211_OCAC_SET_THRESHOLD_TRAFFIC: |
| if (arg < IEEE80211_OCAC_THRESHOLD_TRAFFIC_MIN || |
| arg > IEEE80211_OCAC_THRESHOLD_TRAFFIC_MAX) { |
| printk("Invalid DFS_s_radio traffic threshold: %u\n", arg); |
| return -1; |
| } |
| ic->ic_ocac.ocac_cfg.ocac_params.thresh_traffic = arg; |
| break; |
| case IEEE80211_OCAC_SET_THRESHOLD_CCA_INTF: |
| if (arg < IEEE80211_OCAC_THRESHOLD_CCA_INTF_MIN || |
| arg > IEEE80211_OCAC_THRESHOLD_CCA_INTF_MAX) { |
| printk("Invalid DFS_s_radio cca_intf threshold: %u\n", arg); |
| return -1; |
| } |
| ic->ic_ocac.ocac_cfg.ocac_params.thresh_cca_intf = arg; |
| break; |
| case IEEE80211_OCAC_SET_THRESHOLD_FAT_DEC: |
| if (arg < IEEE80211_OCAC_THRESHOLD_FAT_DEC_MIN || |
| arg > IEEE80211_OCAC_THRESHOLD_FAT_DEC_MAX) { |
| printk("Invalid DFS_s_radio fat_dec threshold: %u\n", arg); |
| return -1; |
| } |
| ic->ic_ocac.ocac_cfg.ocac_params.thresh_fat_dec = arg; |
| break; |
| case IEEE80211_OCAC_SET_TIMER_INTERVAL: |
| if (arg < IEEE80211_OCAC_TIMER_INTERVAL_MIN || |
| arg > IEEE80211_OCAC_TIMER_INTERVAL_MAX) { |
| printk("Invalid DFS_s_radio timer interval: %u\n", arg); |
| return -1; |
| } |
| ic->ic_ocac.ocac_cfg.ocac_params.timer_interval = arg; |
| break; |
| case IEEE80211_OCAC_SET_TIMER_EXPIRE_INIT: |
| if (arg < IEEE80211_OCAC_TIMER_EXPIRE_INIT_MIN || |
| arg > IEEE80211_OCAC_TIMER_EXPIRE_INIT_MAX) { |
| printk("Invalid DFS_s_radio timer expire init: %u\n", arg); |
| return -1; |
| } |
| ic->ic_ocac.ocac_cfg.ocac_timer_expire_init = arg; |
| break; |
| case IEEE80211_OCAC_SET_OFFSET_TXHALT: |
| if (arg < IEEE80211_OCAC_OFFSET_TXHALT_MIN || |
| arg > IEEE80211_OCAC_OFFSET_TXHALT_MAX) { |
| printk("Invalid DFS_s_radio offset for txhalt: %u\n", arg); |
| return -1; |
| } |
| ic->ic_ocac.ocac_cfg.ocac_params.offset_txhalt = arg; |
| break; |
| case IEEE80211_OCAC_SET_OFFSET_OFFCHAN: |
| if (arg < IEEE80211_OCAC_OFFSET_OFFCHAN_MIN || |
| arg > IEEE80211_OCAC_OFFSET_OFFCHAN_MAX) { |
| printk("Invalid DFS_s_radio offset for switch off channel: %u\n", arg); |
| return -1; |
| } |
| ic->ic_ocac.ocac_cfg.ocac_params.offset_offchan = arg; |
| break; |
| case IEEE80211_OCAC_SET_BEACON_INTERVAL: |
| if (arg < IEEE80211_OCAC_BEACON_INTERVAL_MIN || |
| arg > IEEE80211_OCAC_BEACON_INTERVAL_MAX) { |
| printk("Invalid DFS_s_radio beacon interval: %u\n", arg); |
| return -1; |
| } |
| ic->ic_ocac.ocac_cfg.ocac_params.beacon_interval = arg; |
| break; |
| case IEEE80211_OCAC_SET_DUMP_COUNTS: |
| ieee80211_ocac_set_dump_counts(ic, arg); |
| break; |
| case IEEE80211_OCAC_SET_DUMP_TSFLOG: |
| ieee80211_ocac_dump_tsflog(ic); |
| break; |
| case IEEE80211_OCAC_SET_DUMP_CFG: |
| ieee80211_ocac_dump_cfg(ic); |
| break; |
| case IEEE80211_OCAC_SET_TRAFFIC_CONTROL: |
| ic->ic_ocac.ocac_cfg.ocac_params.traffic_ctrl = arg ? 1 : 0; |
| break; |
| case IEEE80211_OCAC_SET_REPORT_ONLY: |
| ic->ic_ocac.ocac_cfg.ocac_report_only = arg ? 1 : 0; |
| break; |
| case IEEE80211_OCAC_SET_DUMP_CCA_COUNTS: |
| break; |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_set_dfs_fast_switch(struct ieee80211com *ic) |
| { |
| if (ic->ic_ieee_alt_chan != 0) { |
| struct ieee80211_channel *chan = findchannel(ic, ic->ic_ieee_alt_chan, ic->ic_des_mode); |
| |
| if ((chan != NULL) && !(ieee80211_is_chan_available(chan))) { |
| return EINVAL; |
| } |
| } |
| |
| ic->ic_flags_ext |= IEEE80211_FEXT_DFS_FAST_SWITCH; |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_set_alt_chan(struct ieee80211com *ic, uint8_t ieee_alt_chan) |
| { |
| struct ieee80211_channel *chan = NULL; |
| |
| if (ieee_alt_chan == 0) { |
| ic->ic_ieee_alt_chan = ieee_alt_chan; |
| return 0; |
| } |
| |
| if (ic->ic_curchan->ic_ieee == ieee_alt_chan) { |
| return EINVAL; |
| } |
| |
| chan = findchannel(ic, ieee_alt_chan, ic->ic_des_mode); |
| |
| if (chan == NULL) { |
| return EINVAL; |
| } |
| |
| if ((ic->ic_flags_ext & IEEE80211_FEXT_DFS_FAST_SWITCH) |
| && !(ieee80211_is_chan_available(chan))) { |
| return EINVAL; |
| } |
| |
| ic->ic_ieee_alt_chan = ieee_alt_chan; |
| |
| return 0; |
| } |
| |
| |
| static int |
| apply_tx_power(struct ieee80211vap *vap, int enc_val, int flag) |
| { |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_channel *c; |
| int8_t *bw_powers; |
| uint8_t start_chan = (enc_val & 0xFF000000) >> 24; |
| uint8_t stop_chan = (enc_val & 0x00FF0000) >> 16; |
| uint8_t max_power = (enc_val & 0x0000FF00) >> 8; |
| uint8_t min_power = (enc_val & 0x000000FF); |
| int iter; |
| int cur_bw; |
| int idx_bf; |
| int idx_ss; |
| int idx_bw; |
| |
| if (start_chan > stop_chan) |
| return -EINVAL; |
| |
| for (iter = 0; iter < ic->ic_nchans; iter++) { |
| c = ic->ic_channels + iter; |
| if (start_chan <= c->ic_ieee && c->ic_ieee <= stop_chan) { |
| switch (flag) { |
| case IEEE80211_BKUP_TXPOWER_NORMAL: |
| c->ic_maxpower = max_power; |
| c->ic_minpower = min_power; |
| c->ic_maxpower_normal = max_power; |
| c->ic_minpower_normal = min_power; |
| |
| cur_bw = ieee80211_get_bw(ic); |
| switch (cur_bw) { |
| case BW_HT20: |
| idx_bw = PWR_IDX_20M; |
| break; |
| case BW_HT40: |
| idx_bw = PWR_IDX_40M; |
| break; |
| case BW_HT80: |
| idx_bw = PWR_IDX_80M; |
| break; |
| default: |
| idx_bw = PWR_IDX_BW_MAX; /* Invalid case */ |
| break; |
| } |
| if (idx_bw < PWR_IDX_BW_MAX) { |
| c->ic_maxpower_table[PWR_IDX_BF_OFF][PWR_IDX_1SS][idx_bw] = max_power; |
| if (ic->ic_power_table_update) { |
| ic->ic_power_table_update(vap, c); |
| } |
| } |
| break; |
| case IEEE80211_APPLY_LOWGAIN_TXPOWER: |
| if (c->ic_maxpower_normal) { |
| c->ic_maxpower = IEEE80211_LOWGAIN_TXPOW_MAX; |
| c->ic_minpower = IEEE80211_LOWGAIN_TXPOW_MIN; |
| } |
| break; |
| case IEEE80211_APPLY_TXPOWER_NORMAL: |
| if (c->ic_maxpower_normal) { |
| c->ic_maxpower = c->ic_maxpower_normal; |
| c->ic_minpower = c->ic_minpower_normal; |
| } |
| break; |
| case IEEE80211_INIT_TXPOWER_TABLE: |
| c->ic_maxpower = max_power; |
| c->ic_minpower = min_power; |
| c->ic_maxpower_normal = max_power; |
| c->ic_minpower_normal = min_power; |
| for (idx_bf = PWR_IDX_BF_OFF; idx_bf < PWR_IDX_BF_MAX; idx_bf++) { |
| for (idx_ss = PWR_IDX_1SS; idx_ss < PWR_IDX_SS_MAX; idx_ss++) { |
| bw_powers = c->ic_maxpower_table[idx_bf][idx_ss]; |
| bw_powers[PWR_IDX_20M] = max_power; |
| bw_powers[PWR_IDX_40M] = max_power; |
| bw_powers[PWR_IDX_80M] = max_power; |
| } |
| } |
| if (ic->ic_power_table_update) { |
| ic->ic_power_table_update(vap, c); |
| } |
| break; |
| default: |
| printk("%s: Invalid flag", __func__); |
| } |
| } |
| } |
| return 0; |
| } |
| |
| static int ieee80211_set_bw_txpower(struct ieee80211vap *vap, unsigned int enc_val) |
| { |
| struct ieee80211com *ic = vap->iv_ic; |
| uint8_t channel = (enc_val >> 24) & 0xFF; |
| uint8_t bf_on = (enc_val >> 20) & 0xF; |
| uint8_t num_ss = (enc_val >> 16) & 0xF; |
| uint8_t bandwidth = (enc_val >> 8) & 0xF; |
| uint8_t power = enc_val & 0xFF; |
| uint8_t idx_bf = PWR_IDX_BF_OFF + bf_on; |
| uint8_t idx_ss = PWR_IDX_1SS + num_ss - 1; |
| uint8_t idx_bw = PWR_IDX_20M + bandwidth - QTN_BW_20M; |
| int iter; |
| int retval = -EINVAL; |
| |
| if (idx_bf >= PWR_IDX_BF_MAX || |
| idx_ss >= PWR_IDX_SS_MAX || |
| idx_bw >= PWR_IDX_BW_MAX) { |
| return retval; |
| } |
| |
| for (iter = 0; iter < ic->ic_nchans; iter++) { |
| if (ic->ic_channels[iter].ic_ieee == channel) { |
| ic->ic_channels[iter].ic_maxpower_table[idx_bf][idx_ss][idx_bw] = power; |
| /* |
| * Update the maxpower of current bandwidth if it is for bfoff and 1ss |
| */ |
| if (idx_bf == PWR_IDX_BF_OFF && idx_ss == PWR_IDX_1SS) { |
| int cur_bw = ieee80211_get_bw(ic); |
| |
| if ((cur_bw == BW_HT20 && bandwidth == QTN_BW_20M) || |
| (cur_bw == BW_HT40 && bandwidth == QTN_BW_40M) || |
| (cur_bw == BW_HT80 && bandwidth == QTN_BW_80M)) { |
| ic->ic_channels[iter].ic_maxpower = power; |
| ic->ic_channels[iter].ic_maxpower_normal = power; |
| } |
| } |
| retval = 0; |
| |
| break; |
| } |
| } |
| |
| return retval; |
| } |
| |
| static int ieee80211_dump_tx_power(struct ieee80211com *ic) |
| { |
| struct ieee80211_channel *chan; |
| int iter; |
| int idx_bf; |
| int idx_ss; |
| |
| printk("channel max_pwr min_pwr pwr_80M pwr_40M pwr_20M\n"); |
| for (iter = 0; iter < ic->ic_nchans; iter++) { |
| chan = &ic->ic_channels[iter]; |
| if (!isset(ic->ic_chan_active, chan->ic_ieee)) { |
| continue; |
| } |
| for (idx_bf = PWR_IDX_BF_OFF; idx_bf < PWR_IDX_BF_MAX; idx_bf++) { |
| for (idx_ss = PWR_IDX_1SS; idx_ss < PWR_IDX_SS_MAX; idx_ss++) { |
| printk("%7d %7d %7d %7d %7d %7d\n", |
| chan->ic_ieee, |
| chan->ic_maxpower, |
| chan->ic_minpower, |
| chan->ic_maxpower_table[idx_bf][idx_ss][PWR_IDX_80M], |
| chan->ic_maxpower_table[idx_bf][idx_ss][PWR_IDX_40M], |
| chan->ic_maxpower_table[idx_bf][idx_ss][PWR_IDX_20M]); |
| } |
| } |
| } |
| return 0; |
| } |
| |
| static int set_regulatory_tx_power(struct ieee80211com *ic, int enc_val) |
| { |
| u_int8_t start_chan = (enc_val & 0x00FF0000) >> 16; |
| u_int8_t stop_chan = (enc_val & 0x0000FF00) >> 8; |
| u_int8_t reg_power = (enc_val & 0x000000FF); |
| int iter; |
| |
| if (start_chan > stop_chan) |
| return -EINVAL; |
| |
| for (iter = 0; iter < ic->ic_nchans; iter++) { |
| if (start_chan <= ic->ic_channels[iter].ic_ieee && |
| ic->ic_channels[iter].ic_ieee <= stop_chan) { |
| ic->ic_channels[iter].ic_maxregpower = reg_power; |
| } |
| } |
| |
| return 0; |
| } |
| |
| void ieee80211_doth_meas_callback_success(void *ctx) |
| { |
| struct ieee80211_node *ni = (struct ieee80211_node *)ctx; |
| |
| if (ni->ni_meas_info.pending) { |
| ni->ni_meas_info.pending = 0; |
| ni->ni_meas_info.reason = 0; |
| wake_up_interruptible(&ni->ni_meas_info.meas_waitq); |
| } |
| } |
| |
| void ieee80211_doth_meas_callback_fail(void *ctx, int32_t reason) |
| { |
| struct ieee80211_node *ni = (struct ieee80211_node *)ctx; |
| |
| if (ni->ni_meas_info.pending) { |
| ni->ni_meas_info.pending = 0; |
| ni->ni_meas_info.reason = reason; |
| wake_up_interruptible(&ni->ni_meas_info.meas_waitq); |
| } |
| } |
| |
| void ioctl_tpc_report_callback_success(void *ctx) |
| { |
| struct ieee80211_node *ni = (struct ieee80211_node *)ctx; |
| |
| if (ni->ni_tpc_info.tpc_wait_info.tpc_pending) { |
| ni->ni_tpc_info.tpc_wait_info.tpc_pending = 0; |
| ni->ni_tpc_info.tpc_wait_info.reason = 0; |
| wake_up_interruptible(&ni->ni_tpc_info.tpc_wait_info.tpc_waitq); |
| } |
| } |
| |
| void ioctl_tpc_report_callback_fail(void *ctx, int32_t reason) |
| { |
| struct ieee80211_node *ni = (struct ieee80211_node *)ctx; |
| |
| if (ni->ni_tpc_info.tpc_wait_info.tpc_pending) { |
| ni->ni_tpc_info.tpc_wait_info.tpc_pending = 0; |
| ni->ni_tpc_info.tpc_wait_info.reason = reason; |
| wake_up_interruptible(&ni->ni_tpc_info.tpc_wait_info.tpc_waitq); |
| } |
| } |
| |
| static int |
| ieee80211_subioctl_get_doth_dotk_report(struct net_device *dev, char __user *user_pointer) |
| { |
| int ret = 0; |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211req_node_info req_info; |
| union ieee80211rep_node_info rep_info; |
| union ieee80211rep_node_info *p_rep_info; |
| |
| if (copy_from_user(&req_info, user_pointer, sizeof(struct ieee80211req_node_info))) |
| return -EFAULT; |
| |
| p_rep_info = &rep_info; |
| switch (req_info.req_type) { |
| case IOCTL_REQ_MEASUREMENT: |
| { |
| struct ieee80211_node *ni; |
| |
| ni = ieee80211_find_node(&ic->ic_sta, req_info.u_req_info.req_node_meas.mac_addr); |
| if (NULL == ni) |
| return -EINVAL; |
| |
| switch (req_info.u_req_info.req_node_meas.type) { |
| case IOCTL_MEAS_TYPE_BASIC: |
| ieee80211_send_meas_request_basic(ni, |
| req_info.u_req_info.req_node_meas.ioctl_basic.channel, |
| req_info.u_req_info.req_node_meas.ioctl_basic.start_offset_ms, |
| req_info.u_req_info.req_node_meas.ioctl_basic.duration_ms, |
| IEEE80211_MEASUREMENT_REQ_TIMEOUT(req_info.u_req_info.req_node_meas.ioctl_basic.start_offset_ms, |
| req_info.u_req_info.req_node_meas.ioctl_basic.duration_ms), |
| (void *)ieee80211_doth_meas_callback_success, |
| (void *)ieee80211_doth_meas_callback_fail); |
| break; |
| case IOCTL_MEAS_TYPE_CCA: |
| ieee80211_send_meas_request_cca(ni, |
| req_info.u_req_info.req_node_meas.ioctl_cca.channel, |
| req_info.u_req_info.req_node_meas.ioctl_cca.start_offset_ms, |
| req_info.u_req_info.req_node_meas.ioctl_cca.duration_ms, |
| IEEE80211_MEASUREMENT_REQ_TIMEOUT(req_info.u_req_info.req_node_meas.ioctl_cca.start_offset_ms, |
| req_info.u_req_info.req_node_meas.ioctl_cca.duration_ms), |
| (void *)ieee80211_doth_meas_callback_success, |
| (void *)ieee80211_doth_meas_callback_fail); |
| break; |
| case IOCTL_MEAS_TYPE_RPI: |
| ieee80211_send_meas_request_rpi(ni, |
| req_info.u_req_info.req_node_meas.ioctl_rpi.channel, |
| req_info.u_req_info.req_node_meas.ioctl_rpi.start_offset_ms, |
| req_info.u_req_info.req_node_meas.ioctl_rpi.duration_ms, |
| IEEE80211_MEASUREMENT_REQ_TIMEOUT(req_info.u_req_info.req_node_meas.ioctl_rpi.start_offset_ms, |
| req_info.u_req_info.req_node_meas.ioctl_rpi.duration_ms), |
| (void *)ieee80211_doth_meas_callback_success, |
| (void *)ieee80211_doth_meas_callback_fail); |
| break; |
| case IOCTL_MEAS_TYPE_CHAN_LOAD: |
| ieee80211_send_rm_req_chan_load(ni, |
| req_info.u_req_info.req_node_meas.ioctl_chan_load.channel, |
| req_info.u_req_info.req_node_meas.ioctl_chan_load.duration_ms, |
| IEEE80211_MEASUREMENT_REQ_TIMEOUT(0, |
| req_info.u_req_info.req_node_meas.ioctl_chan_load.duration_ms), |
| (void *)ieee80211_doth_meas_callback_success, |
| (void *)ieee80211_doth_meas_callback_fail); |
| break; |
| case IOCTL_MEAS_TYPE_NOISE_HIS: |
| ieee80211_send_rm_req_noise_his(ni, |
| req_info.u_req_info.req_node_meas.ioctl_noise_his.channel, |
| req_info.u_req_info.req_node_meas.ioctl_noise_his.duration_ms, |
| IEEE80211_MEASUREMENT_REQ_TIMEOUT(0, |
| req_info.u_req_info.req_node_meas.ioctl_noise_his.duration_ms), |
| (void *)ieee80211_doth_meas_callback_success, |
| (void *)ieee80211_doth_meas_callback_fail); |
| break; |
| case IOCTL_MEAS_TYPE_BEACON: |
| ieee80211_send_rm_req_beacon(ni, |
| req_info.u_req_info.req_node_meas.ioctl_beacon.op_class, |
| req_info.u_req_info.req_node_meas.ioctl_beacon.channel, |
| req_info.u_req_info.req_node_meas.ioctl_beacon.duration_ms, |
| req_info.u_req_info.req_node_meas.ioctl_beacon.mode, |
| req_info.u_req_info.req_node_meas.ioctl_beacon.bssid, |
| NULL, |
| 0, |
| IEEE80211_MEASUREMENT_REQ_TIMEOUT(0, |
| req_info.u_req_info.req_node_meas.ioctl_beacon.duration_ms), |
| (void *)ieee80211_doth_meas_callback_success, |
| (void *)ieee80211_doth_meas_callback_fail); |
| break; |
| case IOCTL_MEAS_TYPE_FRAME: |
| ieee80211_send_rm_req_frame(ni, |
| req_info.u_req_info.req_node_meas.ioctl_frame.op_class, |
| req_info.u_req_info.req_node_meas.ioctl_frame.channel, |
| req_info.u_req_info.req_node_meas.ioctl_frame.duration_ms, |
| req_info.u_req_info.req_node_meas.ioctl_frame.type, |
| req_info.u_req_info.req_node_meas.ioctl_frame.mac_address, |
| IEEE80211_MEASUREMENT_REQ_TIMEOUT(0, |
| req_info.u_req_info.req_node_meas.ioctl_frame.duration_ms), |
| (void *)ieee80211_doth_meas_callback_success, |
| (void *)ieee80211_doth_meas_callback_fail); |
| break; |
| case IOCTL_MEAS_TYPE_CAT: |
| ieee80211_send_rm_req_tran_stream_cat(ni, |
| req_info.u_req_info.req_node_meas.ioctl_tran_stream_cat.duration_ms, |
| req_info.u_req_info.req_node_meas.ioctl_tran_stream_cat.peer_sta, |
| req_info.u_req_info.req_node_meas.ioctl_tran_stream_cat.tid, |
| req_info.u_req_info.req_node_meas.ioctl_tran_stream_cat.bin0, |
| IEEE80211_MEASUREMENT_REQ_TIMEOUT(0, |
| req_info.u_req_info.req_node_meas.ioctl_tran_stream_cat.duration_ms), |
| (void *)ieee80211_doth_meas_callback_success, |
| (void *)ieee80211_doth_meas_callback_fail); |
| break; |
| case IOCTL_MEAS_TYPE_MUL_DIAG: |
| ieee80211_send_rm_req_multicast_diag(ni, |
| req_info.u_req_info.req_node_meas.ioctl_multicast_diag.duration_ms, |
| req_info.u_req_info.req_node_meas.ioctl_multicast_diag.group_mac, |
| IEEE80211_MEASUREMENT_REQ_TIMEOUT(0, |
| req_info.u_req_info.req_node_meas.ioctl_multicast_diag.duration_ms), |
| (void *)ieee80211_doth_meas_callback_success, |
| (void *)ieee80211_doth_meas_callback_fail); |
| break; |
| case IOCTL_MEAS_TYPE_LINK: |
| ieee80211_send_link_measure_request(ni, |
| HZ / 10, |
| (void *)ieee80211_doth_meas_callback_success, |
| (void *)ieee80211_doth_meas_callback_fail); |
| break; |
| case IOCTL_MEAS_TYPE_NEIGHBOR: |
| ieee80211_send_neighbor_report_request(ni, |
| HZ / 10, |
| (void *)ieee80211_doth_meas_callback_success, |
| (void *)ieee80211_doth_meas_callback_fail); |
| break; |
| default: |
| ieee80211_free_node(ni); |
| return -EOPNOTSUPP; |
| } |
| |
| ni->ni_meas_info.pending = 1; |
| ret = wait_event_interruptible(ni->ni_meas_info.meas_waitq, |
| ni->ni_meas_info.pending == 0); |
| |
| if (ret == 0) { |
| if (ni->ni_meas_info.reason != 0) { |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH | IEEE80211_MSG_DEBUG, |
| "[%s]Measurement Request Fail:timeout waiting for response\n", |
| __func__); |
| switch (ni->ni_meas_info.reason) { |
| case PPQ_FAIL_TIMEOUT: |
| p_rep_info->meas_result.status = IOCTL_MEAS_STATUS_TIMEOUT; |
| break; |
| case PPQ_FAIL_NODELEAVE: |
| p_rep_info->meas_result.status = IOCTL_MEAS_STATUS_NODELEAVE; |
| break; |
| case PPQ_FAIL_STOP: |
| p_rep_info->meas_result.status = IOCTL_MEAS_STATUS_STOP; |
| default: |
| break; |
| } |
| } else { |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH | IEEE80211_MSG_DEBUG, |
| "[%s]Measurement Request SUCC ret = %d\n", |
| __func__, ret); |
| |
| if (req_info.u_req_info.req_node_meas.type == IOCTL_MEAS_TYPE_LINK) { |
| p_rep_info->meas_result.status = IOCTL_MEAS_STATUS_SUCC; |
| p_rep_info->meas_result.report_mode = 0; |
| p_rep_info->meas_result.u_data.link_measure.tpc_report.link_margin = ni->ni_lm.tpc_report.link_margin; |
| p_rep_info->meas_result.u_data.link_measure.tpc_report.tx_power = ni->ni_lm.tpc_report.tx_power; |
| p_rep_info->meas_result.u_data.link_measure.recv_antenna_id = ni->ni_lm.recv_antenna_id; |
| p_rep_info->meas_result.u_data.link_measure.tran_antenna_id = ni->ni_lm.tran_antenna_id; |
| p_rep_info->meas_result.u_data.link_measure.rcpi = ni->ni_lm.rcpi; |
| p_rep_info->meas_result.u_data.link_measure.rsni = ni->ni_lm.rsni; |
| |
| break; |
| } |
| |
| if (req_info.u_req_info.req_node_meas.type == IOCTL_MEAS_TYPE_NEIGHBOR) { |
| uint8_t i; |
| |
| p_rep_info->meas_result.status = IOCTL_MEAS_STATUS_SUCC; |
| p_rep_info->meas_result.report_mode = 0; |
| p_rep_info->meas_result.u_data.neighbor_report.item_num = 0; |
| for (i = 0; i < ni->ni_neighbor.report_count && i < IEEE80211_MAX_NEIGHBOR_REPORT_ITEM; i++) { |
| memcpy(&p_rep_info->meas_result.u_data.neighbor_report.item[i], |
| ni->ni_neighbor.item_table[i], |
| sizeof(p_rep_info->meas_result.u_data.neighbor_report.item[i])); |
| kfree(ni->ni_neighbor.item_table[i]); |
| ni->ni_neighbor.item_table[i] = NULL; |
| } |
| p_rep_info->meas_result.u_data.neighbor_report.item_num = i; |
| ni->ni_neighbor.report_count = 0; |
| break; |
| } |
| |
| p_rep_info->meas_result.status = IOCTL_MEAS_STATUS_SUCC; |
| p_rep_info->meas_result.report_mode = ni->ni_meas_info.ni_meas_rep_mode; |
| if (ni->ni_meas_info.ni_meas_rep_mode == 0) { |
| switch (req_info.u_req_info.req_node_meas.type) { |
| case IOCTL_MEAS_TYPE_BASIC: |
| p_rep_info->meas_result.u_data.basic = ni->ni_meas_info.rep.basic; |
| break; |
| case IOCTL_MEAS_TYPE_CCA: |
| p_rep_info->meas_result.u_data.cca = ni->ni_meas_info.rep.cca; |
| break; |
| case IOCTL_MEAS_TYPE_RPI: |
| memcpy(p_rep_info->meas_result.u_data.rpi, |
| ni->ni_meas_info.rep.rpi, |
| sizeof(p_rep_info->meas_result.u_data.rpi)); |
| break; |
| case IOCTL_MEAS_TYPE_CHAN_LOAD: |
| p_rep_info->meas_result.u_data.chan_load = ni->ni_meas_info.rep.chan_load; |
| break; |
| case IOCTL_MEAS_TYPE_NOISE_HIS: |
| p_rep_info->meas_result.u_data.noise_his.antenna_id = ni->ni_meas_info.rep.noise_his.antenna_id; |
| p_rep_info->meas_result.u_data.noise_his.anpi = ni->ni_meas_info.rep.noise_his.anpi; |
| memcpy(p_rep_info->meas_result.u_data.noise_his.ipi, |
| ni->ni_meas_info.rep.noise_his.ipi, |
| sizeof(p_rep_info->meas_result.u_data.noise_his.ipi)); |
| break; |
| case IOCTL_MEAS_TYPE_BEACON: |
| p_rep_info->meas_result.u_data.beacon.reported_frame_info = ni->ni_meas_info.rep.beacon.reported_frame_info; |
| p_rep_info->meas_result.u_data.beacon.rcpi = ni->ni_meas_info.rep.beacon.rcpi; |
| p_rep_info->meas_result.u_data.beacon.rsni = ni->ni_meas_info.rep.beacon.rsni; |
| memcpy(p_rep_info->meas_result.u_data.beacon.bssid, |
| ni->ni_meas_info.rep.beacon.bssid, |
| sizeof(p_rep_info->meas_result.u_data.beacon.bssid)); |
| p_rep_info->meas_result.u_data.beacon.antenna_id = ni->ni_meas_info.rep.beacon.antenna_id; |
| p_rep_info->meas_result.u_data.beacon.parent_tsf = ni->ni_meas_info.rep.beacon.parent_tsf; |
| break; |
| case IOCTL_MEAS_TYPE_FRAME: |
| p_rep_info->meas_result.u_data.frame.sub_ele_report = ni->ni_meas_info.rep.frame_count.sub_ele_flag; |
| memcpy(p_rep_info->meas_result.u_data.frame.ta, ni->ni_meas_info.rep.frame_count.ta, IEEE80211_ADDR_LEN); |
| memcpy(p_rep_info->meas_result.u_data.frame.bssid, ni->ni_meas_info.rep.frame_count.bssid, IEEE80211_ADDR_LEN); |
| p_rep_info->meas_result.u_data.frame.phy_type = ni->ni_meas_info.rep.frame_count.phy_type; |
| p_rep_info->meas_result.u_data.frame.avg_rcpi = ni->ni_meas_info.rep.frame_count.avg_rcpi; |
| p_rep_info->meas_result.u_data.frame.last_rsni = ni->ni_meas_info.rep.frame_count.last_rsni; |
| p_rep_info->meas_result.u_data.frame.last_rcpi = ni->ni_meas_info.rep.frame_count.last_rcpi; |
| p_rep_info->meas_result.u_data.frame.antenna_id = ni->ni_meas_info.rep.frame_count.antenna_id; |
| p_rep_info->meas_result.u_data.frame.frame_count = ni->ni_meas_info.rep.frame_count.frame_count; |
| break; |
| case IOCTL_MEAS_TYPE_CAT: |
| memcpy(&p_rep_info->meas_result.u_data.tran_stream_cat, |
| &ni->ni_meas_info.rep.tran_stream_cat, |
| sizeof(p_rep_info->meas_result.u_data.tran_stream_cat)); |
| break; |
| case IOCTL_MEAS_TYPE_MUL_DIAG: |
| p_rep_info->meas_result.u_data.multicast_diag.reason = ni->ni_meas_info.rep.multicast_diag.reason; |
| p_rep_info->meas_result.u_data.multicast_diag.mul_rec_msdu_cnt = ni->ni_meas_info.rep.multicast_diag.mul_rec_msdu_cnt; |
| p_rep_info->meas_result.u_data.multicast_diag.first_seq_num = ni->ni_meas_info.rep.multicast_diag.first_seq_num; |
| p_rep_info->meas_result.u_data.multicast_diag.last_seq_num = ni->ni_meas_info.rep.multicast_diag.last_seq_num; |
| p_rep_info->meas_result.u_data.multicast_diag.mul_rate = ni->ni_meas_info.rep.multicast_diag.mul_rate; |
| |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| } else { |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH | IEEE80211_MSG_DEBUG, |
| "[%s]Measurement Request Fail:waiting for response cancelled\n", |
| __func__); |
| ni->ni_meas_info.pending = 0; |
| ret = -ECANCELED; |
| } |
| ieee80211_free_node(ni); |
| break; |
| } |
| case IOCTL_REQ_TPC: |
| { |
| struct ieee80211_action_tpc_request request; |
| struct ieee80211_action_data action_data; |
| struct ieee80211_node *ni; |
| |
| if (((ic->ic_flags & IEEE80211_F_DOTH) == 0) || |
| ((ic->ic_flags_ext & IEEE80211_FEXT_TPC) == 0)) { |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH | IEEE80211_MSG_DEBUG, |
| "[%s]TPC Request fail:802.11 disabled\n", |
| __func__); |
| return -EOPNOTSUPP; |
| } |
| |
| ni = ieee80211_find_node(&ic->ic_sta, req_info.u_req_info.req_node_tpc.mac_addr); |
| if (NULL == ni) { |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH | IEEE80211_MSG_DEBUG, |
| "[%s]TPC Request Fail:no such node %s\n", |
| __func__, |
| ether_sprintf(req_info.u_req_info.req_node_tpc.mac_addr)); |
| return -EINVAL; |
| } |
| if (((IEEE80211_CAPINFO_SPECTRUM_MGMT & ni->ni_capinfo) == 0) || |
| ((ni->ni_flags & IEEE80211_NODE_TPC) == 0)) { |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH | IEEE80211_MSG_DEBUG, |
| "[%s]TPC Request Fail:node don't support 802.11h\n", |
| __func__); |
| ieee80211_free_node(ni); |
| return -EOPNOTSUPP; |
| } |
| |
| request.expire = HZ / 10; |
| request.fn_success = ioctl_tpc_report_callback_success; |
| request.fn_fail = ioctl_tpc_report_callback_fail; |
| action_data.cat = IEEE80211_ACTION_CAT_SPEC_MGMT; |
| action_data.action = IEEE80211_ACTION_S_TPC_REQUEST; |
| action_data.params = &request; |
| IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&action_data); |
| |
| ni->ni_tpc_info.tpc_wait_info.tpc_pending = 1; |
| ni->ni_tpc_info.tpc_wait_info.reason = 0; |
| ret = wait_event_interruptible(ni->ni_tpc_info.tpc_wait_info.tpc_waitq, |
| ni->ni_tpc_info.tpc_wait_info.tpc_pending == 0); |
| |
| if (ret == 0) { |
| if (ni->ni_tpc_info.tpc_wait_info.reason != 0) { |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH | IEEE80211_MSG_DEBUG, |
| "[%s]TPC Request Fail:timeout waiting for response\n", |
| __func__); |
| p_rep_info->tpc_result.status = 1; |
| } else { |
| p_rep_info->tpc_result.status = 0; |
| p_rep_info->tpc_result.link_margin = ni->ni_tpc_info.tpc_report.node_link_margin; |
| p_rep_info->tpc_result.tx_power = ni->ni_tpc_info.tpc_report.node_txpow; |
| } |
| } else { |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH | IEEE80211_MSG_DEBUG, |
| "[%s]TPC Request Fail:waiting for response cancelled\n", |
| __func__); |
| ni->ni_tpc_info.tpc_wait_info.tpc_pending = 0; |
| ret = -ECANCELED; |
| } |
| ieee80211_free_node(ni); |
| break; |
| } |
| default: |
| ret = -EOPNOTSUPP; |
| break; |
| } |
| if (ret == 0) |
| ret = copy_to_user(user_pointer, p_rep_info, sizeof(union ieee80211rep_node_info)); |
| |
| return ret; |
| } |
| |
| void ieee80211_beacon_interval_set(struct ieee80211com *ic, int value) |
| { |
| struct ieee80211vap *vap_each; |
| |
| ic->ic_lintval = value; |
| |
| TAILQ_FOREACH(vap_each, &ic->ic_vaps, iv_next) { |
| if ((vap_each->iv_opmode == IEEE80211_M_HOSTAP) || |
| (vap_each->iv_opmode == IEEE80211_M_IBSS) ){ |
| vap_each->iv_bss->ni_intval = value; |
| ic->ic_beacon_update(vap_each); |
| } |
| } |
| } |
| EXPORT_SYMBOL(ieee80211_beacon_interval_set); |
| |
| static int ieee80211_11ac_mcs_format(int mcs, int bw) |
| { |
| int retval = IEEE80211_11AC_MCS_VAL_ERR; |
| int mcs_val, mcs_nss; |
| |
| /* Check for unequal MCS */ |
| if (mcs & 0x100) { |
| mcs &=0xFF; |
| if ((mcs >= IEEE80211_UNEQUAL_MCS_START) && (mcs <= IEEE80211_UNEQUAL_MCS_MAX)) { |
| if (ieee80211_swfeat_is_supported(SWFEAT_ID_2X2, 0) || |
| ieee80211_swfeat_is_supported(SWFEAT_ID_2X4, 0)) { |
| if (mcs > IEEE80211_HT_UNEQUAL_MCS_2SS_MAX) { |
| return retval; |
| } |
| } else if (ieee80211_swfeat_is_supported(SWFEAT_ID_3X3, 0)) { |
| if (mcs > IEEE80211_HT_UNEQUAL_MCS_3SS_MAX) { |
| return retval; |
| } |
| } |
| retval = (mcs - IEEE80211_UNEQUAL_MCS_START) | IEEE80211_UNEQUAL_MCS_BIT; |
| } |
| } else { |
| mcs_val = mcs & IEEE80211_AC_MCS_VAL_MASK; |
| mcs_nss = (mcs & IEEE80211_AC_MCS_NSS_MASK) >> IEEE80211_11AC_MCS_NSS_SHIFT; |
| |
| if (!ieee80211_vht_tx_mcs_is_valid(mcs_val, mcs_nss)) { |
| return retval; |
| } |
| |
| retval = (bw == 20) ? wlan_11ac_20M_mcs_nss_tbl[(mcs_nss * IEEE80211_AC_MCS_MAX) + mcs_val] : mcs; |
| } |
| return retval; |
| } |
| |
| static int |
| ieee80211_ioctl_setchan_inactive_pri(struct ieee80211com *ic, struct ieee80211vap *vap, uint32_t value) |
| { |
| uint16_t chan = value & 0xFFFF; |
| uint8_t active = (value >> 16) & 0xFF; |
| uint8_t flags = (value >> 24) & 0xFF; |
| |
| int reselect = 0; |
| struct ieee80211_channel *c; |
| int cur_bw; |
| int set_inactive = 0; |
| |
| if (ic->ic_opmode != IEEE80211_M_HOSTAP) { |
| return 0; |
| } |
| |
| c = findchannel_any(ic, chan, ic->ic_des_mode); |
| if (!is_ieee80211_chan_valid(c)) { |
| return -EINVAL; |
| } |
| |
| if (active) { |
| if (isset(ic->ic_chan_pri_inactive, chan)) { |
| /* |
| * clrbit if flag indicates user configurtion. |
| * And save the bit to is_inactive_usercfg to honor user configurtion |
| * so as to override regulatory db. |
| * |
| * Otherwise clrbit only if user has not cfg-ed. |
| */ |
| if (flags & CHAN_PRI_INACTIVE_CFG_USER_OVERRIDE) { |
| clrbit(ic->ic_chan_pri_inactive, chan); |
| printk("channel %u is removed from non-primary channel list\n", chan); |
| setbit(ic->ic_is_inactive_usercfg, chan); |
| } else if (!isset(ic->ic_is_inactive_usercfg, chan)) { |
| clrbit(ic->ic_chan_pri_inactive, chan); |
| printk("channel %u is removed from non-primary channel list\n", chan); |
| } |
| } |
| return 0; |
| } else { |
| if (isclr(ic->ic_chan_pri_inactive, chan) || |
| !!(flags & CHAN_PRI_INACTIVE_CFG_AUTOCHAN_ONLY) != |
| !!isset(ic->ic_is_inactive_autochan_only, chan)) { |
| /* |
| * setbit if flag indicates user configurtion. |
| * And save the bit to is_inactive_usercfg to honor user configurtion |
| * so as to override regulatory db. |
| * |
| * Otherwise setbit only if user has not cfg-ed. |
| */ |
| if (flags & CHAN_PRI_INACTIVE_CFG_USER_OVERRIDE) { |
| setbit(ic->ic_chan_pri_inactive, chan); |
| printk("channel %u is added into non-primary channel list\n", chan); |
| setbit(ic->ic_is_inactive_usercfg, chan); |
| set_inactive = 1; |
| } else if (!isset(ic->ic_is_inactive_usercfg, chan)) { |
| setbit(ic->ic_chan_pri_inactive, chan); |
| printk("channel %u is added into non-primary channel list\n", chan); |
| set_inactive = 1; |
| } |
| if (set_inactive) { |
| if (flags & CHAN_PRI_INACTIVE_CFG_AUTOCHAN_ONLY) { |
| setbit(ic->ic_is_inactive_autochan_only, chan); |
| printk("channel %u is non-primary for auto channel selection only\n", chan); |
| } else { |
| clrbit(ic->ic_is_inactive_autochan_only, chan); |
| } |
| } |
| } else { |
| return 0; |
| } |
| } |
| |
| cur_bw = ieee80211_get_bw(ic); |
| if (cur_bw >= BW_HT40) { |
| int no_pri_chan = 1; |
| int c_ieee = ieee80211_find_sec_chan(c); |
| |
| if (c_ieee && isclr(ic->ic_chan_pri_inactive, c_ieee)) { |
| no_pri_chan = 0; |
| } |
| if (cur_bw >= BW_HT80) { |
| c_ieee = ieee80211_find_sec40u_chan(c); |
| if (c_ieee && isclr(ic->ic_chan_pri_inactive, c_ieee)) { |
| no_pri_chan = 0; |
| } |
| c_ieee = ieee80211_find_sec40l_chan(c); |
| if (c_ieee && isclr(ic->ic_chan_pri_inactive, c_ieee)) { |
| no_pri_chan = 0; |
| } |
| } |
| if (no_pri_chan) { |
| printk("Warning: all the sub channels are not in primary channel list!\n"); |
| } |
| } |
| |
| if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && |
| isset(ic->ic_chan_pri_inactive, ic->ic_bsschan->ic_ieee)) { |
| printk("current channel is %d, cannot be used as primary channel\n", ic->ic_bsschan->ic_ieee); |
| reselect = 1; |
| } |
| if(ic->ic_des_chan != IEEE80211_CHAN_ANYC && |
| isset(ic->ic_chan_pri_inactive, ic->ic_des_chan->ic_ieee)) { |
| printk("Channel %d cannot be used as desired channel\n", ic->ic_des_chan->ic_ieee); |
| ic->ic_des_chan = IEEE80211_CHAN_ANYC; |
| reselect = 1; |
| } |
| |
| if (reselect && IS_UP_AUTO(vap)) { |
| ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); |
| } |
| |
| return 0; |
| } |
| |
| static int |
| local_get_inactive_primary_chan_num(struct ieee80211com *ic, struct ieee80211_inactive_chanlist *chanlist) |
| { |
| int i; |
| int num = 0; |
| |
| if (chanlist) { |
| memset(chanlist, 0, sizeof(struct ieee80211_inactive_chanlist)); |
| } |
| |
| for (i = 1; i < IEEE80211_CHAN_MAX; i++) { |
| if (isset(ic->ic_chan_pri_inactive, i)) { |
| if (chanlist) |
| chanlist->channels[i] = CHAN_PRI_INACTIVE_CFG_USER_OVERRIDE; |
| if (chanlist && isset(ic->ic_is_inactive_autochan_only, i)) |
| chanlist->channels[i] |= CHAN_PRI_INACTIVE_CFG_AUTOCHAN_ONLY; |
| num++; |
| } |
| } |
| |
| return num; |
| } |
| |
| static int |
| ieee80211_get_inactive_primary_chan_num(struct ieee80211com *ic) |
| { |
| return local_get_inactive_primary_chan_num(ic, NULL); |
| } |
| |
| static void |
| ieee80211_training_restart_by_node_idx(struct ieee80211vap *vap, uint16_t node_idx) |
| { |
| struct ieee80211_node *ni; |
| |
| ni = ieee80211_find_node_by_node_idx(vap, node_idx); |
| if (ni) { |
| ieee80211_node_training_start(ni, 1); |
| ieee80211_free_node(ni); |
| } |
| } |
| |
| static int |
| ieee80211_subioctl_set_chan_dfs_required(struct net_device *dev, void __user *pointer, int cnt) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| int channels[IEEE80211_CHAN_MAX]; |
| int i; |
| |
| if (cnt > ARRAY_SIZE(channels)) { |
| printk("%s: max number of supported channels is %d\n", __func__, |
| ARRAY_SIZE(channels)); |
| cnt = ARRAY_SIZE(channels); |
| } |
| |
| if (copy_from_user(channels, pointer, cnt*sizeof(int))) |
| return -EFAULT; |
| |
| for (i = 0; i < cnt; i++) { |
| if (channels[i] < IEEE80211_CHAN_MAX && channels[i] > 0) { |
| setbit(ic->ic_chan_dfs_required, channels[i]); |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH, |
| "Mark DFS channel %d\n", channels[i]); |
| } |
| } |
| |
| ic->ic_mark_dfs_channels(ic, ic->ic_nchans, ic->ic_channels); |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_subioctl_set_chan_weather_radar(struct net_device *dev, void __user *pointer, int cnt) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| int channels[IEEE80211_CHAN_MAX]; |
| int i; |
| |
| if (cnt > ARRAY_SIZE(channels)) { |
| printk("%s: max number of supported channels is %d\n", __func__, |
| ARRAY_SIZE(channels)); |
| cnt = ARRAY_SIZE(channels); |
| } |
| |
| if (copy_from_user(channels, pointer, cnt*sizeof(int))) |
| return -EFAULT; |
| |
| for (i = 0; i < cnt; i++) { |
| if (channels[i] < IEEE80211_CHAN_MAX && channels[i] > 0) { |
| setbit(ic->ic_chan_weather_radar, channels[i]); |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH, |
| "Mark weather channel %d\n", channels[i]); |
| } |
| } |
| |
| ic->ic_mark_weather_radar_chans(ic, ic->ic_nchans, ic->ic_channels); |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_subioctl_setget_chan_disabled(struct net_device *dev, void __user *pointer, int len) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieeee80211_disabled_chanlist channels; |
| int i, j = 0, cnt; |
| |
| if (copy_from_user(&channels, pointer, len)) { |
| printk("%s: copy_from_user failed\n", __FUNCTION__); |
| return -EFAULT; |
| } |
| |
| if (channels.dir == SET_CHAN_DISABLED) { |
| if ((vap->iv_opmode == IEEE80211_M_STA && vap->iv_state == IEEE80211_S_RUN) || |
| (vap->iv_opmode == IEEE80211_M_HOSTAP && ic->ic_sta_assoc != 0)) { |
| printk("%s: channel disable settings is not allowed during associated\n", __FUNCTION__); |
| return -EPERM; |
| } |
| |
| cnt = channels.list_len; |
| if (cnt > ARRAY_SIZE(channels.chan)) { |
| printk("%s: max number of supported channels is %d\n", __func__, |
| ARRAY_SIZE(channels.chan)); |
| return -EFAULT; |
| } |
| |
| for (i = 0; i < cnt; i++) { |
| if (channels.chan[i] >= IEEE80211_CHAN_MAX) |
| return -EFAULT; |
| if (!isset(ic->ic_chan_avail, channels.chan[i])) |
| return -EFAULT; |
| } |
| |
| for (i = 0; i < cnt; i++) { |
| if (!channels.flag) { |
| clrbit(ic->ic_chan_disabled, channels.chan[i]); |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH, |
| "Mark channel %d enabled\n", channels.chan[i]); |
| } else { |
| setbit(ic->ic_chan_disabled, channels.chan[i]); |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH, |
| "Mark channel %d disabled\n", channels.chan[i]); |
| } |
| } |
| } else if (channels.dir == GET_CHAN_DISABLED) { |
| for (i = 1; i <= IEEE80211_CHAN_MAX; i++) { |
| if (isset(ic->ic_chan_disabled, i)) { |
| channels.chan[j++] = i; |
| } |
| } |
| channels.list_len = j; |
| |
| if (copy_to_user(pointer, &channels, sizeof(channels))) { |
| printk("%s: copy_to_user failed\n", __FUNCTION__); |
| return -EIO; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_subioctl_wowlan_setget(struct net_device *dev, struct ieee80211req_wowlan __user* ps, int len) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211req_wowlan req; |
| int ret = 0; |
| |
| if (!ps) { |
| printk("%s: NULL pointer for user request\n", __FUNCTION__); |
| return -EFAULT; |
| } |
| |
| if ((sizeof(req) > len)) { |
| printk("%s: low memory for request's result\n", __FUNCTION__); |
| return -EFAULT; |
| } |
| |
| if (copy_from_user(&req, ps, sizeof(struct ieee80211req_wowlan))) { |
| printk("%s: copy_from_user failed\n", __FUNCTION__); |
| return -EIO; |
| } |
| |
| if (!req.is_data) { |
| printk("%s: user space buffer invalid\n", __FUNCTION__); |
| return -EFAULT; |
| } |
| |
| switch (req.is_op) { |
| case IEEE80211_WOWLAN_MAGIC_PATTERN: |
| if (req.is_data) { |
| uint32_t len = MIN(MAX_USER_DEFINED_MAGIC_LEN, req.is_data_len); |
| memset(ic->ic_wowlan.pattern.magic_pattern, 0, MAX_USER_DEFINED_MAGIC_LEN); |
| if (copy_from_user(ic->ic_wowlan.pattern.magic_pattern, req.is_data, len)) { |
| printk("%s: copy_from_user pattern copy failed\n", __FUNCTION__); |
| return -EIO; |
| } |
| ic->ic_wowlan.pattern.len = len; |
| } |
| return 0; |
| case IEEE80211_WOWLAN_HOST_POWER_SAVE: |
| ret = copy_to_user(req.is_data, &(ic->ic_wowlan.host_state), |
| MIN(req.is_data_len, sizeof(ic->ic_wowlan.host_state))); |
| break; |
| case IEEE80211_WOWLAN_MATCH_TYPE: |
| ret = copy_to_user(req.is_data, &(ic->ic_wowlan.wowlan_match), |
| MIN(req.is_data_len, sizeof(ic->ic_wowlan.wowlan_match))); |
| break; |
| case IEEE80211_WOWLAN_L2_ETHER_TYPE: |
| ret = copy_to_user(req.is_data, &(ic->ic_wowlan.L2_ether_type), |
| MIN(req.is_data_len, sizeof(ic->ic_wowlan.L2_ether_type))); |
| break; |
| case IEEE80211_WOWLAN_L3_UDP_PORT: |
| ret = copy_to_user(req.is_data, &(ic->ic_wowlan.L3_udp_port), |
| MIN(req.is_data_len, sizeof(ic->ic_wowlan.L3_udp_port))); |
| break; |
| case IEEE80211_WOWLAN_MAGIC_PATTERN_GET: |
| req.is_data_len = MIN(req.is_data_len, ic->ic_wowlan.pattern.len); |
| ret = copy_to_user(req.is_data, &(ic->ic_wowlan.pattern.magic_pattern), req.is_data_len); |
| break; |
| default: |
| break; |
| } |
| |
| if (ret) { |
| printk("%s: buffer content: copy_to_user failed\n", __FUNCTION__); |
| return -EIO; |
| } |
| |
| if (copy_to_user(ps, &req, sizeof(req))) { |
| printk("%s: copy_to_user failed\n", __FUNCTION__); |
| return -EIO; |
| } |
| return 0; |
| } |
| |
| static int |
| ieee80211_subioctl_get_sta_auth(struct net_device *dev, struct ieee80211req_auth_description __user* ps, int cnt) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_node *ni; |
| struct ieee80211req_auth_description auth_description; |
| uint8_t *casted_ptr = (uint8_t*)&auth_description.description; |
| |
| if (!ps) { |
| printk("%s: NULL pointer for user request\n", __FUNCTION__); |
| return -EFAULT; |
| } |
| |
| if (sizeof(auth_description) > cnt) { |
| printk("%s: low memory for request's result\n", __FUNCTION__); |
| return -EFAULT; |
| } |
| |
| if (copy_from_user(&auth_description, ps, sizeof(auth_description))) { |
| printk("%s: copy_from_user failed\n", __FUNCTION__); |
| return -EIO; |
| } |
| |
| ni = ieee80211_find_node(&ic->ic_sta, auth_description.macaddr); |
| if (NULL == ni) { |
| printk("%s: client not found\n", __FUNCTION__); |
| return -EINVAL; |
| } |
| |
| casted_ptr[IEEE80211_AUTHDESCR_ALGO_POS] = ni->ni_used_auth_algo; |
| |
| if (ni->ni_rsn_ie != NULL || ni->ni_wpa_ie != NULL) { |
| casted_ptr[IEEE80211_AUTHDESCR_KEYMGMT_POS] = ni->ni_rsn.rsn_keymgmt; |
| casted_ptr[IEEE80211_AUTHDESCR_KEYPROTO_POS] = ni->ni_rsn_ie != NULL ? |
| (uint8_t)IEEE80211_AUTHDESCR_KEYPROTO_RSN : |
| (uint8_t)IEEE80211_AUTHDESCR_KEYPROTO_WPA; |
| casted_ptr[IEEE80211_AUTHDESCR_CIPHER_POS] = (uint8_t)ni->ni_rsn.rsn_ucastcipher; |
| } else { |
| if (vap->iv_flags & IEEE80211_F_PRIVACY) |
| casted_ptr[IEEE80211_AUTHDESCR_KEYMGMT_POS] = (uint8_t)IEEE80211_AUTHDESCR_KEYMGMT_WEP; |
| } |
| |
| ieee80211_free_node(ni); |
| |
| if (copy_to_user(ps, &auth_description, sizeof(auth_description))) { |
| printk("%s: copy_to_user failed\n", __FUNCTION__); |
| return -EIO; |
| } |
| return 0; |
| } |
| |
| static int |
| ieee80211_subioctl_get_sta_vendor(struct net_device *dev, uint8_t __user* ps) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_node *ni; |
| uint8_t macaddr[IEEE80211_ADDR_LEN]; |
| |
| if (!ps) { |
| printk("%s: NULL pointer for user request\n", __FUNCTION__); |
| return -EFAULT; |
| } |
| if (copy_from_user(macaddr, ps, sizeof(macaddr))) { |
| printk("%s: copy_from_user failed\n", __FUNCTION__); |
| return -EIO; |
| } |
| |
| ni = ieee80211_find_node(&ic->ic_sta, macaddr); |
| if (NULL == ni) { |
| printk("%s: client not found\n", __FUNCTION__); |
| return -EINVAL; |
| } |
| |
| if (copy_to_user(ps, &ni->ni_vendor, sizeof(ni->ni_vendor))) { |
| printk("%s: copy_to_user failed\n", __FUNCTION__); |
| ieee80211_free_node(ni); |
| return -EIO; |
| } |
| |
| ieee80211_free_node(ni); |
| return 0; |
| } |
| |
| int |
| ieee80211_is_channel_disabled(struct ieee80211com *ic, int channel, int bw) |
| { |
| if (isset(ic->ic_chan_disabled, channel)) { |
| return 1; |
| } |
| |
| /* based on BW, need to check if more channels need to be disabled*/ |
| if (bw >= BW_HT40) { |
| uint32_t chan_sec = 0; |
| uint32_t chan_sec40u = 0; |
| uint32_t chan_sec40l = 0; |
| struct ieee80211_channel *chan; |
| chan = findchannel(ic, channel, ic->ic_des_mode); |
| if (chan == NULL) { |
| chan = findchannel(ic, channel, IEEE80211_MODE_AUTO); |
| } |
| |
| if (chan) { |
| chan_sec = ieee80211_find_sec_chan(chan); |
| if (unlikely(chan_sec && isset(ic->ic_chan_disabled, chan_sec))) { |
| return 1; |
| } |
| if (bw >= BW_HT80) { |
| chan_sec40u = ieee80211_find_sec40u_chan(chan); |
| chan_sec40l = ieee80211_find_sec40l_chan(chan); |
| if (unlikely(chan_sec40u && isset(ic->ic_chan_disabled, chan_sec40u)) || |
| (chan_sec40l && isset(ic->ic_chan_disabled, chan_sec40l))) { |
| return 1; |
| } |
| } |
| } |
| } |
| return 0; |
| } |
| |
| static int |
| ieee80211_subioctl_set_active_chanlist_by_bw(struct net_device *dev, void __user *pointer) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_active_chanlist list; |
| uint8_t chanlist[IEEE80211_CHAN_BYTES]; |
| int i; |
| int j; |
| int nchan = 0; |
| |
| memset(chanlist, 0, sizeof(chanlist)); |
| |
| if (copy_from_user(&list, pointer, sizeof(list))) |
| return -EFAULT; |
| |
| if ((ic->ic_phytype == IEEE80211_T_DS) || (ic->ic_phytype == IEEE80211_T_OFDM)) |
| i = 1; |
| else |
| i = 0; |
| |
| for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) { |
| if (isset(list.channels, j) && isset(ic->ic_chan_avail, i) && |
| !ieee80211_is_channel_disabled(ic, i, list.bw)) { |
| setbit(chanlist, i); |
| nchan++; |
| } |
| } |
| |
| if (nchan == 0) |
| return -EINVAL; |
| |
| switch (list.bw) { |
| case BW_HT80: |
| memcpy(ic->ic_chan_active_80, chanlist, sizeof(ic->ic_chan_active_80)); |
| break; |
| case BW_HT40: |
| memcpy(ic->ic_chan_active_40, chanlist, sizeof(ic->ic_chan_active_40)); |
| break; |
| case BW_HT20: |
| memcpy(ic->ic_chan_active_20, chanlist, sizeof(ic->ic_chan_active_20)); |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_subioctl_get_sta_tput_caps(struct net_device *dev, |
| struct ieee8011req_sta_tput_caps __user* ps, int cnt) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_node *ni = NULL; |
| struct ieee8011req_sta_tput_caps tput_caps; |
| |
| if (!ps) { |
| printk("%s: NULL pointer for user request\n", __FUNCTION__); |
| return -EFAULT; |
| } |
| |
| if (sizeof(tput_caps) > cnt) { |
| printk("%s: return buffer is too small\n", __FUNCTION__); |
| return -EFAULT; |
| } |
| |
| if (copy_from_user(&tput_caps, ps, sizeof(tput_caps))) { |
| printk("%s: copy_from_user failed\n", __FUNCTION__); |
| return -EIO; |
| } |
| |
| ni = ieee80211_find_node(&ic->ic_sta, tput_caps.macaddr); |
| if (!ni) { |
| tput_caps.mode = IEEE80211_WIFI_MODE_NONE; |
| printk("%s: station %pM not found\n", __FUNCTION__, tput_caps.macaddr); |
| goto copy_and_exit; |
| } |
| |
| COMPILE_TIME_ASSERT(sizeof(ni->ni_ie_htcap) == sizeof(tput_caps.htcap_ie) && |
| sizeof(ni->ni_ie_vhtcap) == sizeof(tput_caps.vhtcap_ie)); |
| |
| tput_caps.mode = ni->ni_wifi_mode; |
| if (IEEE80211_NODE_IS_VHT(ni)) { |
| memcpy(tput_caps.htcap_ie, &ni->ni_ie_htcap, sizeof(tput_caps.htcap_ie)); |
| memcpy(tput_caps.vhtcap_ie, &ni->ni_ie_vhtcap, sizeof(tput_caps.vhtcap_ie)); |
| } else if (IEEE80211_NODE_IS_HT(ni)) { |
| memcpy(tput_caps.htcap_ie, &ni->ni_ie_htcap, sizeof(tput_caps.htcap_ie)); |
| } |
| ieee80211_free_node(ni); |
| copy_and_exit: |
| if (copy_to_user(ps, &tput_caps, sizeof(tput_caps))) { |
| printk("%s: copy_to_user failed\n", __FUNCTION__); |
| return -EIO; |
| } |
| return 0; |
| } |
| |
| static int |
| ieee80211_subioctl_get_chan_power_table(struct net_device *dev, void __user *pointer, uint32_t len) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_channel *c = NULL; |
| struct ieee80211_chan_power_table power_table; |
| int iter; |
| int idx_bf; |
| int idx_ss; |
| |
| if (copy_from_user(&power_table, pointer, sizeof(power_table))) |
| return -EFAULT; |
| |
| for (iter = 0; iter < ic->ic_nchans; iter++) { |
| c = ic->ic_channels + iter; |
| if (c->ic_ieee == power_table.chan_ieee) { |
| break; |
| } |
| } |
| |
| if (iter >= ic->ic_nchans || c == NULL) { |
| /* |
| * Didn't find this channel, set the channel as |
| * 0 to indicate there is no such channel |
| */ |
| power_table.chan_ieee = 0; |
| } else { |
| for (idx_bf = PWR_IDX_BF_OFF; idx_bf < PWR_IDX_BF_MAX; idx_bf++) { |
| for (idx_ss = PWR_IDX_1SS; idx_ss < PWR_IDX_SS_MAX; idx_ss++) { |
| power_table.maxpower_table[idx_bf][idx_ss][PWR_IDX_20M] = |
| c->ic_maxpower_table[idx_bf][idx_ss][PWR_IDX_20M]; |
| power_table.maxpower_table[idx_bf][idx_ss][PWR_IDX_40M] = |
| c->ic_maxpower_table[idx_bf][idx_ss][PWR_IDX_40M]; |
| power_table.maxpower_table[idx_bf][idx_ss][PWR_IDX_80M] = |
| c->ic_maxpower_table[idx_bf][idx_ss][PWR_IDX_80M]; |
| } |
| } |
| } |
| |
| if (copy_to_user(pointer, &power_table, len)) |
| return -EINVAL; |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_subioctl_set_chan_power_table(struct net_device *dev, void __user *pointer) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_channel *c; |
| struct ieee80211_chan_power_table power_table; |
| int8_t *s_pwrs; |
| int8_t *d_pwrs; |
| int iter; |
| int cur_bw; |
| int idx_bf; |
| int idx_ss; |
| |
| if (copy_from_user(&power_table, pointer, sizeof(power_table))) |
| return -EFAULT; |
| |
| for (iter = 0; iter < ic->ic_nchans; iter++) { |
| c = ic->ic_channels + iter; |
| if (c->ic_ieee == power_table.chan_ieee) { |
| for (idx_bf = PWR_IDX_BF_OFF; idx_bf < PWR_IDX_BF_MAX; idx_bf++) { |
| for (idx_ss = PWR_IDX_1SS; idx_ss < PWR_IDX_SS_MAX; idx_ss++) { |
| s_pwrs = power_table.maxpower_table[idx_bf][idx_ss]; |
| d_pwrs = c->ic_maxpower_table[idx_bf][idx_ss]; |
| d_pwrs[PWR_IDX_20M] = s_pwrs[PWR_IDX_20M]; |
| d_pwrs[PWR_IDX_40M] = s_pwrs[PWR_IDX_40M]; |
| d_pwrs[PWR_IDX_80M] = s_pwrs[PWR_IDX_80M]; |
| /* |
| * Update the maxpower of current bandwidth |
| */ |
| if (idx_bf == PWR_IDX_BF_OFF && idx_ss == PWR_IDX_1SS) { |
| cur_bw = ieee80211_get_bw(ic); |
| switch (cur_bw) { |
| case BW_HT20: |
| c->ic_maxpower = s_pwrs[PWR_IDX_20M]; |
| break; |
| case BW_HT40: |
| c->ic_maxpower = s_pwrs[PWR_IDX_40M]; |
| break; |
| case BW_HT80: |
| c->ic_maxpower = s_pwrs[PWR_IDX_80M]; |
| break; |
| } |
| } |
| } |
| } |
| if (ic->ic_power_table_update) { |
| ic->ic_power_table_update(vap, c); |
| } |
| break; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static void |
| get_txrx_airtime_ioctl(void *s, struct ieee80211_node *ni) |
| { |
| struct node_txrx_airtime __user *u_airtime; |
| struct iwreq *iwr = (struct iwreq *)s; |
| struct ieee80211vap *vap = ni->ni_vap; |
| struct ieee80211com *ic = vap->iv_ic; |
| struct node_txrx_airtime airtime; |
| uint16_t __user *u_nr_nodes; |
| struct txrx_airtime *txrxat; |
| uint16_t nr_nodes; |
| |
| /* Ignore node that has associd = 0 */ |
| if (ni->ni_associd == 0) |
| return; |
| |
| memcpy(airtime.macaddr, ni->ni_macaddr, IEEE80211_ADDR_LEN); |
| airtime.tx_airtime = ic->ic_tx_airtime(ni); |
| airtime.tx_airtime_accum = ic->ic_tx_accum_airtime(ni); |
| airtime.rx_airtime = ic->ic_rx_airtime(ni); |
| airtime.rx_airtime_accum = ic->ic_rx_accum_airtime(ni); |
| |
| txrxat = (struct txrx_airtime *)iwr->u.data.pointer; |
| |
| txrxat->total_cli_tx_airtime += airtime.tx_airtime_accum; |
| txrxat->total_cli_rx_airtime += airtime.rx_airtime_accum; |
| |
| txrxat = (struct txrx_airtime *)iwr->u.data.pointer; |
| u_nr_nodes = &txrxat->nr_nodes; |
| |
| if (copy_from_user(&nr_nodes, u_nr_nodes, sizeof(nr_nodes))) |
| return; |
| |
| u_airtime = txrxat->nodes + nr_nodes; |
| |
| if (nr_nodes++ > QTN_ASSOC_LIMIT - 1) |
| return; |
| |
| if (copy_to_user(u_airtime, &airtime, sizeof(airtime))) |
| return; |
| |
| if (copy_to_user(u_nr_nodes, &nr_nodes, sizeof(nr_nodes))) |
| return; |
| } |
| |
| static int |
| ieee80211_subioctl_set_sec_chan(struct net_device *dev, |
| void __user *pointer, uint32_t len) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_channel *chan = NULL; |
| int chan_off_pair[2]; |
| int chan_ieee; |
| int offset; |
| |
| if (vap->iv_opmode != IEEE80211_M_HOSTAP) |
| return -EOPNOTSUPP; |
| |
| if (copy_from_user(chan_off_pair, pointer, sizeof(chan_off_pair))) |
| return -EFAULT; |
| |
| chan_ieee = chan_off_pair[0]; |
| offset = chan_off_pair[1]; |
| if (!ieee80211_dual_sec_chan_supported(ic, chan_ieee)) |
| return -EINVAL; |
| |
| chan = findchannel(ic, chan_ieee, ic->ic_des_mode); |
| if (!chan) |
| return -EINVAL; |
| |
| offset = (offset == 0) ? IEEE80211_HTINFO_CHOFF_SCA : IEEE80211_HTINFO_CHOFF_SCB; |
| ieee80211_update_sec_chan_offset(chan, offset); |
| |
| if (chan_ieee == ic->ic_curchan->ic_ieee) { |
| if (vap->iv_state == IEEE80211_S_RUN) |
| ic->ic_set_channel(ic); |
| ieee80211_wireless_reassoc(vap, 0, 1); |
| } |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_subioctl_get_sec_chan(struct net_device *dev, |
| void __user *pointer, uint32_t len) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_channel *chan = NULL; |
| int cur_bw = ieee80211_get_bw(ic); |
| int chan_off_pair[2]; |
| int chan_ieee; |
| int offset; |
| |
| if (copy_from_user(chan_off_pair, pointer, sizeof(chan_off_pair))) |
| return -EFAULT; |
| |
| chan_ieee = chan_off_pair[0]; |
| offset = chan_ieee; |
| chan = findchannel(ic, chan_ieee, ic->ic_des_mode); |
| if (!chan) |
| return -EINVAL; |
| |
| if (cur_bw >= BW_HT40) { |
| if (chan->ic_flags & IEEE80211_CHAN_HT40U) |
| offset = chan_ieee + IEEE80211_SEC_CHAN_OFFSET; |
| else if (chan->ic_flags & IEEE80211_CHAN_HT40D) |
| offset = chan_ieee - IEEE80211_SEC_CHAN_OFFSET; |
| } |
| chan_off_pair[1] = offset; |
| |
| if (copy_to_user(pointer, chan_off_pair, sizeof(chan_off_pair))) |
| return -EFAULT; |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_subioctl_get_txrx_airtime(struct net_device *dev, struct iwreq *iwr) |
| { |
| struct shared_params *sp = qtn_mproc_sync_shared_params_get(); |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct txrx_airtime *txrxat; |
| uint16_t __user *u_fat; |
| uint16_t fat; |
| |
| txrxat = (struct txrx_airtime *)iwr->u.data.pointer; |
| u_fat = &txrxat->free_airtime; |
| |
| if (copy_from_user(&fat, u_fat, sizeof(fat))) |
| return -EFAULT; |
| fat = sp->free_airtime; |
| copy_to_user(u_fat, &fat, sizeof(fat)); |
| |
| ic->ic_iterate_nodes(&ic->ic_sta, get_txrx_airtime_ioctl, iwr, 1); |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_subioctl_get_chan_pri_inact(struct net_device *dev, |
| void __user *pointer, uint32_t len) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_inactive_chanlist chanlist; |
| |
| if (sizeof(chanlist) > len) |
| return -ENOMEM; |
| |
| memset(&chanlist, 0, sizeof(chanlist)); |
| |
| local_get_inactive_primary_chan_num(ic, &chanlist); |
| |
| if (copy_to_user(pointer, &chanlist, sizeof(chanlist)) != 0) |
| return -EFAULT; |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_di_dfs_channels(struct net_device *dev, void __user *pointer, int cnt) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| int flag_deactive; |
| |
| if (copy_from_user(&flag_deactive, pointer, sizeof(int))) |
| return -EFAULT; |
| |
| ic->ic_dfs_channels_deactive = !!flag_deactive; |
| |
| return 0; |
| } |
| |
| #define MAX_CLIENT_LIST 200 |
| static int |
| ieee80211_subioctl_get_client_mac_list(struct net_device *dev, |
| void __user *pointer, uint32_t len) |
| { |
| int rval = 0; |
| uint32_t num_of_entries = 0; |
| struct ieee80211_mac_list mlist; |
| uint32_t flags = 0; |
| |
| if (copy_from_user(&mlist, pointer, sizeof(mlist)) != 0) |
| return -EFAULT; |
| |
| rval = fwt_db_get_macs_behind_node(mlist.num_entries, &num_of_entries, MAX_CLIENT_LIST, |
| &flags, (uint8_t *)&mlist.macaddr[0]); |
| mlist.num_entries = num_of_entries; |
| mlist.flags = flags; |
| |
| if (copy_to_user(pointer, &mlist, sizeof(mlist)) != 0) |
| return -EFAULT; |
| |
| return rval; |
| } |
| |
| static int |
| ieee80211_set_nss_cap(struct ieee80211vap *vap, const int param, const int nss) |
| { |
| struct ieee80211com *ic = vap->iv_ic; |
| |
| if (nss < 1 || nss > QTN_GLOBAL_RATE_NSS_MAX) |
| return EINVAL; |
| |
| if (ieee80211_swfeat_is_supported(SWFEAT_ID_2X2, 0) || |
| ieee80211_swfeat_is_supported(SWFEAT_ID_2X4, 0)) { |
| if (nss > QTN_2X2_GLOBAL_RATE_NSS_MAX) { |
| return EINVAL; |
| } |
| } else if (ieee80211_swfeat_is_supported(SWFEAT_ID_3X3, 0)) { |
| if (nss > QTN_3X3_GLOBAL_RATE_NSS_MAX) { |
| return EINVAL; |
| } |
| } |
| |
| if (param == IEEE80211_PARAM_HT_NSS_CAP) |
| ic->ic_ht_nss_cap = nss; |
| else if (param == IEEE80211_PARAM_VHT_NSS_CAP) { |
| ic->ic_vht_nss_cap = nss; |
| ic->ic_vhtcap.numsounding = nss -1; |
| ic->ic_vhtcap_24g.numsounding = nss -1; |
| } else |
| return EINVAL; |
| |
| ieee80211_param_to_qdrv(vap, param, nss, NULL, 0); |
| ieee80211_wireless_reassoc(vap, 0, 1); |
| |
| return 0; |
| } |
| |
| #if defined(QBMPS_ENABLE) |
| int ieee80211_wireless_set_sta_bmps(struct ieee80211vap *vap, struct ieee80211com *ic, |
| int value) |
| { |
| struct qdrv_vap *qv = container_of(vap, struct qdrv_vap, iv); |
| |
| /* valid setting is: 0 - disable */ |
| /* 1 - manual mode */ |
| /* 2 - auto mode */ |
| if ((unsigned)value > BMPS_MODE_AUTO) |
| return EINVAL; |
| |
| /* BMPS power-saving only works in STA mode */ |
| if (vap->iv_opmode != IEEE80211_M_STA) |
| return EINVAL; |
| |
| if (qv->qv_bmps_mode == value) |
| return 0; |
| |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER, |
| "%s: qv bmps mode=%d, wowlan status %d, arg %d\n", __func__, |
| qv->qv_bmps_mode, ic->ic_wowlan.host_state, value); |
| |
| if (value == 0) { |
| /* disable power-saving */ |
| ic->ic_flags_qtn &= ~IEEE80211_QTN_BMPS; |
| } else { |
| /* enable power-saving */ |
| ic->ic_flags_qtn |= IEEE80211_QTN_BMPS; |
| } |
| |
| if (qv->qv_bmps_mode == BMPS_MODE_AUTO) { |
| /* stop tput measurement if previously in auto mode */ |
| del_timer(&ic->ic_bmps_tput_check.tput_timer); |
| } |
| |
| /* exit power-saving first if previously power-saving is enabled */ |
| if (qv->qv_bmps_mode != BMPS_MODE_OFF) |
| pm_qos_update_requirement(PM_QOS_POWER_SAVE, |
| BOARD_PM_GOVERNOR_WLAN, |
| BOARD_PM_LEVEL_NO); |
| |
| qv->qv_bmps_mode = value; |
| /* Update station WoWLAN status here; otherwise state would go out of sync if |
| * ps state is changed via set_bmps |
| */ |
| ic->ic_wowlan.host_state = value; |
| g_wowlan_host_state = value; |
| |
| if (vap->iv_state == IEEE80211_S_RUN) { |
| |
| /* update null frame */ |
| ieee80211_sta_bmps_update(vap); |
| |
| if (value == BMPS_MODE_MANUAL) { |
| /* manual mode, start power-saving immediately */ |
| /* only when there is only one STA VAP and no SWBMISS */ |
| if (ieee80211_is_idle_state(ic)) |
| pm_qos_update_requirement(PM_QOS_POWER_SAVE, |
| BOARD_PM_GOVERNOR_WLAN, |
| BOARD_PM_LEVEL_IDLE); |
| } else if (value == BMPS_MODE_AUTO){ |
| /* auto mode, start tput measurement */ |
| vap->iv_bmps_tput_high = -1; |
| ic->ic_bmps_tput_check.tput_timer.expires = jiffies + |
| (BMPS_TPUT_MEASURE_PERIOD_MS / 1000) * HZ; |
| add_timer(&ic->ic_bmps_tput_check.tput_timer); |
| } |
| } |
| |
| return 0; |
| |
| } |
| EXPORT_SYMBOL(ieee80211_wireless_set_sta_bmps); |
| #endif |
| |
| static int |
| ieee80211_subioctl_set_dscp2tid_map(struct net_device *dev, void __user *pointer, uint16_t len) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct qdrv_vap *qv = container_of(vap, struct qdrv_vap, iv); |
| uint8_t dscp2tid[IP_DSCP_NUM]; |
| |
| if (len != IP_DSCP_NUM) |
| return -EINVAL; |
| |
| if (copy_from_user(dscp2tid, pointer, sizeof(dscp2tid))) |
| return -EFAULT; |
| |
| if (!ic->ic_set_dscp2tid_map) |
| return -EINVAL; |
| |
| ic->ic_set_dscp2tid_map(qv->qv_vap_idx, dscp2tid); |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_set_vht_mcs_cap(struct ieee80211vap *vap, const int mcs) |
| { |
| struct ieee80211com *ic = vap->iv_ic; |
| |
| switch(mcs) { |
| case IEEE80211_VHT_MCS_0_7: |
| case IEEE80211_VHT_MCS_0_8: |
| case IEEE80211_VHT_MCS_0_9: |
| ic->ic_vht_mcs_cap = mcs; |
| break; |
| default: |
| return EINVAL; |
| } |
| |
| ieee80211_param_to_qdrv(vap, IEEE80211_PARAM_VHT_MCS_CAP, mcs, NULL, 0); |
| ieee80211_wireless_reassoc(vap, 0, 1); |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_subioctl_get_dscp2tid_map(struct net_device *dev, void __user *pointer, uint16_t len) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct qdrv_vap *qv = container_of(vap, struct qdrv_vap, iv); |
| uint8_t dscp2tid[IP_DSCP_NUM]; |
| |
| if (len != IP_DSCP_NUM) |
| return -EINVAL; |
| |
| if (!ic->ic_get_dscp2tid_map) |
| return -EINVAL; |
| |
| ic->ic_get_dscp2tid_map(qv->qv_vap_idx, dscp2tid); |
| |
| if(copy_to_user(pointer, dscp2tid, len)) |
| return -EFAULT; |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_param_wowlan_set(struct net_device *dev, struct ieee80211vap *vap, u_int32_t value) |
| { |
| struct ieee80211com *ic = vap->iv_ic; |
| uint16_t cmd = value >> 16; |
| uint16_t arg = value & 0xffff; |
| |
| if (cmd >= IEEE80211_WOWLAN_SET_MAX) { |
| printk(KERN_WARNING "%s: WOWLAN set: invalid config cmd %u, arg=%u\n", |
| dev->name, cmd, arg); |
| return -1; |
| } |
| |
| switch (cmd) { |
| case IEEE80211_WOWLAN_HOST_POWER_SAVE: |
| if (arg > 1) |
| return -1; |
| |
| if (ic->ic_wowlan.host_state != arg) { |
| /* trigger WLAN manual mode STA power-saving */ |
| #if defined(QBMPS_ENABLE) |
| ieee80211_wireless_set_sta_bmps(vap, ic, arg); |
| #endif |
| } |
| break; |
| case IEEE80211_WOWLAN_MATCH_TYPE: |
| if (arg > 2) |
| return -1; |
| if (ic->ic_wowlan.wowlan_match != arg) { |
| ic->ic_wowlan.wowlan_match = arg; |
| g_wowlan_match_type = arg; |
| } |
| break; |
| case IEEE80211_WOWLAN_L2_ETHER_TYPE: |
| if (arg < 0x600) |
| return -1; |
| if (ic->ic_wowlan.L2_ether_type != arg) { |
| ic->ic_wowlan.L2_ether_type = arg; |
| g_wowlan_l2_ether_type = arg; |
| } |
| break; |
| case IEEE80211_WOWLAN_L3_UDP_PORT: |
| if (ic->ic_wowlan.L3_udp_port != arg) { |
| ic->ic_wowlan.L3_udp_port = arg; |
| g_wowlan_l3_udp_port = arg; |
| } |
| break; |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| static void |
| ieee80211_ioctl_swfeat_disable(struct ieee80211vap *vap, const int param, int feat) |
| { |
| if (feat >= SWFEAT_ID_MAX) |
| printk("%s: feature id %d is invalid\n", __func__, feat); |
| else |
| ieee80211_param_to_qdrv(vap, IEEE80211_PARAM_SWFEAT_DISABLE, feat, NULL, 0); |
| } |
| |
| void ieee80211_send_vht_opmode_to_all(struct ieee80211com *ic, uint8_t bw) |
| { |
| struct ieee80211_node_table *nt = &ic->ic_sta; |
| struct ieee80211_node *ni; |
| |
| TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { |
| if (ni && IEEE80211_NODE_IS_VHT(ni)) { |
| ieee80211_send_vht_opmode_action(ni->ni_vap, ni, bw, 3); |
| } |
| } |
| } |
| |
| static int |
| ieee80211_ioctl_set_assoc_limit(struct ieee80211com *ic, |
| struct ieee80211vap *vap, int value) |
| { |
| struct ieee80211_node *ni; |
| struct ieee80211_node_table *nt = &ic->ic_sta; |
| int i; |
| |
| if ((vap->iv_opmode != IEEE80211_M_HOSTAP) && |
| !(ic->ic_flags_ext & IEEE80211_FEXT_REPEATER)) { |
| return EINVAL; |
| } |
| |
| if (value < 0 || value > QTN_ASSOC_LIMIT) { |
| printk("%s: Max configurable limit is %d\n", |
| __func__, QTN_ASSOC_LIMIT); |
| return EINVAL; |
| } |
| |
| ic->ic_sta_assoc_limit = value; |
| |
| for (i = 0; i < IEEE80211_MAX_BSS_GROUP; i++) { |
| ic->ic_ssid_grp[i].limit = ic->ic_sta_assoc_limit; |
| ic->ic_ssid_grp[i].reserve = 0; |
| } |
| |
| TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { |
| ieee80211_wireless_reassoc(ni->ni_vap, 0, 0); |
| } |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_set_bss_grp_assoc_limit(struct ieee80211com *ic, |
| struct ieee80211vap *vap, int value) |
| { |
| struct ieee80211_node *ni; |
| struct ieee80211_node_table *nt = &ic->ic_sta; |
| int limit, grp; |
| |
| if ((vap->iv_opmode != IEEE80211_M_HOSTAP) && |
| !(ic->ic_flags_ext & IEEE80211_FEXT_REPEATER)) { |
| return EINVAL; |
| } |
| |
| limit = (value & 0xffff); |
| grp = ((value >> 16) & 0xffff); |
| |
| if (grp < IEEE80211_MIN_BSS_GROUP || grp >= IEEE80211_MAX_BSS_GROUP) { |
| return EINVAL; |
| } |
| |
| if (limit < 0 || limit > ic->ic_sta_assoc_limit |
| || limit < ic->ic_ssid_grp[grp].reserve) { |
| return EINVAL; |
| } |
| |
| ic->ic_ssid_grp[grp].limit = limit; |
| |
| TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { |
| if (ni->ni_vap->iv_ssid_group == grp) { |
| ieee80211_wireless_reassoc(ni->ni_vap, 0, 0); |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_set_bss_grpid(struct ieee80211com *ic, |
| struct ieee80211vap *vap, int value) |
| { |
| struct ieee80211_node *ni; |
| struct ieee80211_node_table *nt = &ic->ic_sta; |
| |
| if (vap->iv_opmode != IEEE80211_M_HOSTAP) { |
| return EINVAL; |
| } |
| |
| if (value < IEEE80211_MIN_BSS_GROUP || value >= IEEE80211_MAX_BSS_GROUP) { |
| return EINVAL; |
| } |
| |
| TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { |
| ieee80211_wireless_reassoc(ni->ni_vap, 0, 0); |
| } |
| |
| vap->iv_ssid_group = value; |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_set_bss_grp_assoc_reserve(struct ieee80211com *ic, |
| struct ieee80211vap *vap, int value) |
| { |
| struct ieee80211_node *ni; |
| struct ieee80211_node_table *nt = &ic->ic_sta; |
| int grp; |
| int reserve; |
| int tot_reserve = 0; |
| int i; |
| |
| if ((vap->iv_opmode != IEEE80211_M_HOSTAP) && |
| !(ic->ic_flags_ext & IEEE80211_FEXT_REPEATER)) { |
| return EINVAL; |
| } |
| |
| reserve = (value & 0xffff); |
| grp = ((value >> 16) & 0xffff); |
| |
| if (grp < IEEE80211_MIN_BSS_GROUP || grp >= IEEE80211_MAX_BSS_GROUP) { |
| return EINVAL; |
| } |
| |
| tot_reserve = reserve; |
| for (i = IEEE80211_MIN_BSS_GROUP; i < IEEE80211_MAX_BSS_GROUP; i++) { |
| if (i == grp) |
| continue; |
| tot_reserve += ic->ic_ssid_grp[i].reserve; |
| } |
| |
| if (reserve > ic->ic_ssid_grp[grp].limit |
| || tot_reserve > ic->ic_sta_assoc_limit) { |
| return EINVAL; |
| } |
| |
| ic->ic_ssid_grp[grp].reserve = reserve; |
| |
| TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { |
| ieee80211_wireless_reassoc(ni->ni_vap, 0, 0); |
| } |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_extender_role(struct ieee80211com *ic, struct ieee80211vap *vap, int value) |
| { |
| struct ieee80211vap *vap_tmp; |
| |
| if (!ieee80211_swfeat_is_supported(SWFEAT_ID_QHOP, 1)) |
| return EOPNOTSUPP; |
| |
| if (ic->ic_extender_role == value) |
| return 0; |
| |
| ic->ic_extender_role = value; |
| switch(ic->ic_extender_role) { |
| case IEEE80211_EXTENDER_ROLE_RBS: |
| case IEEE80211_EXTENDER_ROLE_MBS: |
| case IEEE80211_EXTENDER_ROLE_NONE: |
| if (ic->ic_extender_role == IEEE80211_EXTENDER_ROLE_MBS) |
| IEEE80211_ADDR_COPY(ic->ic_extender_mbs_bssid, ic->ic_myaddr); |
| else |
| IEEE80211_ADDR_SET_NULL(ic->ic_extender_mbs_bssid); |
| ic->ic_extender_rbs_num = 0; |
| memset(ic->ic_extender_rbs_bssid[0], 0, sizeof(ic->ic_extender_rbs_bssid)); |
| |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) |
| ic->ic_beacon_update(vap); |
| break; |
| default: |
| return EINVAL; |
| break; |
| } |
| |
| ieee80211_param_to_qdrv(vap, IEEE80211_PARAM_EXTENDER_ROLE, value, NULL, 0); |
| |
| /* change mode of existing WDS links */ |
| TAILQ_FOREACH(vap_tmp, &ic->ic_vaps, iv_next) { |
| if (!IEEE80211_VAP_WDS_ANY(vap_tmp)) |
| continue; |
| ieee80211_vap_wds_mode_change(vap_tmp); |
| } |
| |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| ieee80211_extender_cleanup_wds_link(vap); |
| if (ic->ic_extender_role == IEEE80211_EXTENDER_ROLE_RBS) |
| mod_timer(&ic->ic_extender_scan_timer, jiffies); |
| } |
| |
| return 0; |
| } |
| |
| static int ieee80211_ioctl_set_vap_pri(struct ieee80211com *ic, struct ieee80211vap *vap, int value) |
| { |
| if (!ieee80211_swfeat_is_supported(SWFEAT_ID_QTM_PRIO, 1)) |
| return EOPNOTSUPP; |
| |
| if (value >= QTN_VAP_PRIORITY_NUM) |
| return EINVAL; |
| |
| vap->iv_pri = value; |
| |
| IEEE80211_LOCK_IRQ(ic); |
| |
| ieee80211_adjust_wme_by_vappri(ic); |
| |
| IEEE80211_UNLOCK_IRQ(ic); |
| |
| ieee80211_param_to_qdrv(vap, IEEE80211_PARAM_VAP_PRI, value, NULL, 0); |
| |
| return 0; |
| } |
| |
| static int ieee80211_wireless_set_mu_tx_rate(struct ieee80211vap *vap, struct ieee80211com *ic, |
| const int value) |
| { |
| int mcs1 = value & IEEE80211_AC_MCS_MASK; |
| int mcs2 = (value >> IEEE80211_AC_MCS_SHIFT) & IEEE80211_AC_MCS_MASK; |
| int mcs_to_muc1 = ieee80211_11ac_mcs_format(mcs1, 80); |
| int mcs_to_muc2 = ieee80211_11ac_mcs_format(mcs2, 80); |
| int mcs_to_muc; |
| |
| if (!ieee80211_swfeat_is_supported(SWFEAT_ID_MU_MIMO, 1)) |
| return EOPNOTSUPP; |
| |
| if (!IS_IEEE80211_VHT_ENABLED(ic)) |
| return EINVAL; |
| |
| if ((mcs_to_muc1 < 0) || (mcs_to_muc2 < 0)) |
| return EINVAL; |
| |
| mcs_to_muc = (mcs_to_muc2 << IEEE80211_AC_MCS_SHIFT) | mcs_to_muc1; |
| |
| ieee80211_param_to_qdrv(vap, IEEE80211_PARAM_FIXED_11AC_MU_TX_RATE, mcs_to_muc, NULL, 0); |
| |
| return 0; |
| } |
| |
| int ieee80211_mac_acl(struct ieee80211vap *vap, int value) |
| { |
| const struct ieee80211_aclator *acl = vap->iv_acl; |
| |
| switch (value) { |
| case IEEE80211_MACCMD_POLICY_OPEN: |
| case IEEE80211_MACCMD_POLICY_ALLOW: |
| case IEEE80211_MACCMD_POLICY_DENY: |
| if (acl == NULL) { |
| /* Don't load module until ACL feature is used */ |
| if (value == IEEE80211_MACCMD_POLICY_OPEN) |
| return 0; |
| |
| acl = ieee80211_aclator_get("mac"); |
| if (acl == NULL || !acl->iac_attach(vap)) |
| return -EINVAL; |
| vap->iv_acl = acl; |
| } |
| acl->iac_setpolicy(vap, value); |
| break; |
| case IEEE80211_MACCMD_FLUSH: |
| if (acl != NULL) |
| acl->iac_flush(vap); |
| /* NB: silently ignore when not in use */ |
| break; |
| case IEEE80211_MACCMD_DETACH: |
| if (acl != NULL) { |
| vap->iv_acl = NULL; |
| acl->iac_detach(vap); |
| } |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static void ieee80211_wireless_set_40mhz_intolerant(struct ieee80211vap *vap, |
| int intol) |
| { |
| struct ieee80211com *ic = vap->iv_ic; |
| |
| /* |
| * 80211 specifies 5G STA shall set 40MHz intolerant to 0 |
| */ |
| if (!(ic->ic_curchan->ic_flags & IEEE80211_CHAN_2GHZ)) |
| return; |
| |
| if (!!intol == !!(vap->iv_coex & WLAN_20_40_BSS_COEX_40MHZ_INTOL) && |
| !!intol == !!(ic->ic_htcap.cap & IEEE80211_HTCAP_C_40_INTOLERANT)) |
| return; |
| |
| if (intol) { |
| vap->iv_coex |= WLAN_20_40_BSS_COEX_40MHZ_INTOL; |
| ic->ic_htcap.cap |= IEEE80211_HTCAP_C_40_INTOLERANT; |
| } else { |
| vap->iv_coex &= ~WLAN_20_40_BSS_COEX_40MHZ_INTOL; |
| ic->ic_htcap.cap &= ~IEEE80211_HTCAP_C_40_INTOLERANT; |
| } |
| |
| ieee80211_change_bw(vap, BW_HT20, 0); |
| |
| if (vap->iv_opmode == IEEE80211_M_STA) |
| ieee80211_send_20_40_bss_coex(vap); |
| } |
| |
| static int |
| ieee80211_fix_legacy_rate(struct ieee80211vap *vap, int param, int rate_index) |
| { |
| struct ieee80211com *ic = vap->iv_ic; |
| int new_phy_mode = -1; |
| int value; |
| int vht_flag = 0; |
| |
| if (rate_index >= IEEE80211_AG_START_RATE_INDEX) { |
| if (rate_index > IEEE80211_AG_RATE_MAXSIZE) |
| return EINVAL; |
| |
| value = IEEE80211_N_RATE_PREFIX | rate_index << 8 | rate_index << 16 | rate_index; |
| |
| if (ic->ic_curmode >= IEEE80211_MODE_11NA || ic->ic_curmode == IEEE80211_MODE_AUTO) { |
| if (IS_IEEE80211_5G_BAND(ic)) { |
| new_phy_mode = IEEE80211_MODE_11A; |
| } else { |
| new_phy_mode = IEEE80211_MODE_11G; |
| } |
| } |
| |
| if (!ic->fixed_legacy_rate_mode) { |
| ic->ic_phymode_save = ic->ic_curmode; |
| ic->fixed_legacy_rate_mode = 1; |
| } |
| } else { |
| if (!ic->fixed_legacy_rate_mode) |
| return EINVAL; |
| |
| if (ic->ic_phymode_save >= IEEE80211_MODE_11NA || ic->ic_phymode_save == IEEE80211_MODE_AUTO) |
| new_phy_mode = ic->ic_phymode_save; |
| |
| ic->ic_phymode_save = 0; |
| ic->fixed_legacy_rate_mode = 0; |
| value = 0xFF; /* Disable fixed rate */ |
| } |
| |
| ieee80211_param_to_qdrv(vap, IEEE80211_PARAM_FIXED_TX_RATE, value, NULL, 0); |
| |
| if (new_phy_mode != -1) { |
| if (new_phy_mode >= IEEE80211_MODE_11AC_VHT20PM) |
| vht_flag = 1; |
| |
| if ((ic->ic_rf_chipid == CHIPID_DUAL) && |
| (ic->ic_opmode == IEEE80211_M_STA)) { |
| if (IS_IEEE80211_5G_BAND(ic)) |
| vap->iv_5ghz_prof.vht = vht_flag; |
| else |
| vap->iv_2_4ghz_prof.vht = vht_flag; |
| } |
| ic->ic_des_mode = ic->ic_phymode = new_phy_mode; |
| ieee80211_setmode(ic, new_phy_mode); |
| |
| ic->ic_csw_reason = IEEE80211_CSW_REASON_CONFIG; |
| ieee80211_wireless_reassoc(vap, 0, 1); |
| |
| /* On AP, change channel to take new bw into effect by BB */ |
| /* On STA, reassociation would result into re-scannning so not required */ |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| ic->ic_set_channel(ic); |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_setparam(struct net_device *dev, struct iw_request_info *info, |
| void *w, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_rsnparms *rsn = NULL; |
| int *i = (int *) extra; |
| int param = i[0]; /* parameter id is 1st */ |
| int value = i[1]; /* NB: most values are TYPE_INT */ |
| int temp_value; |
| int retv = 0; |
| int j, caps; |
| const struct ieee80211_authenticator *auth; |
| struct shared_params *sp = qtn_mproc_sync_shared_params_get(); |
| struct ieee80211vap *vap_each; |
| struct ieee80211vap *vap_tmp; |
| struct ieee80211vap *first_vap; |
| |
| if (vap->iv_bss) |
| rsn = &vap->iv_bss->ni_rsn; |
| |
| switch (param) { |
| case IEEE80211_PARAM_AP_ISOLATE: |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) |
| br_set_ap_isolate(value); |
| else |
| return -EINVAL; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| break; |
| case IEEE80211_PARAM_AUTHMODE: |
| if (!vap->iv_bss) |
| return -EFAULT; |
| |
| switch (value) { |
| case IEEE80211_AUTH_WPA: /* WPA */ |
| case IEEE80211_AUTH_8021X: /* 802.1x */ |
| case IEEE80211_AUTH_OPEN: /* open */ |
| case IEEE80211_AUTH_SHARED: /* shared-key */ |
| case IEEE80211_AUTH_AUTO: /* auto */ |
| auth = ieee80211_authenticator_get(value); |
| if (auth == NULL) |
| return -EINVAL; |
| break; |
| default: |
| return -EINVAL; |
| } |
| switch (value) { |
| case IEEE80211_AUTH_WPA: /* WPA w/ 802.1x */ |
| value = IEEE80211_AUTH_8021X; |
| break; |
| case IEEE80211_AUTH_OPEN: /* open */ |
| vap->iv_flags &= ~(IEEE80211_F_WPA); |
| break; |
| case IEEE80211_AUTH_SHARED: /* shared-key */ |
| case IEEE80211_AUTH_AUTO: /* auto */ |
| case IEEE80211_AUTH_8021X: /* 802.1x */ |
| vap->iv_flags &= ~IEEE80211_F_WPA; |
| break; |
| } |
| /* NB: authenticator attach/detach happens on state change */ |
| vap->iv_bss->ni_authmode = value; |
| /* XXX mixed/mode/usage? */ |
| vap->iv_auth = auth; |
| vap->iv_osen = 0; |
| retv = ENETRESET; |
| break; |
| case IEEE80211_PARAM_PROTMODE: |
| if (value > IEEE80211_PROT_RTSCTS) |
| return -EINVAL; |
| ic->ic_protmode = value; |
| /* NB: if not operating in 11g this can wait */ |
| if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && |
| IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan)) |
| retv = ENETRESET; |
| break; |
| case IEEE80211_PARAM_MCASTCIPHER: |
| if ((vap->iv_caps & cipher2cap(value)) == 0 && |
| !ieee80211_crypto_available(value)) |
| return -EINVAL; |
| if (!rsn) |
| return -EFAULT; |
| if (!IEEE80211_IS_TKIP_ALLOWED(ic)) { |
| if (value == IEEE80211_CIPHER_AES_CCM) |
| rsn->rsn_mcastcipher = value; |
| else |
| printk("%s: invalid cipher %d ignored\n", __FUNCTION__, value); |
| } else { |
| rsn->rsn_mcastcipher = value; |
| } |
| if (vap->iv_flags & IEEE80211_F_WPA) |
| retv = ENETRESET; |
| break; |
| case IEEE80211_PARAM_MCASTKEYLEN: |
| if (!(0 < value && value <= IEEE80211_KEYBUF_SIZE)) |
| return -EINVAL; |
| if (!rsn) |
| return -EFAULT; |
| /* XXX no way to verify driver capability */ |
| rsn->rsn_mcastkeylen = value; |
| if (vap->iv_flags & IEEE80211_F_WPA) |
| retv = ENETRESET; |
| break; |
| case IEEE80211_PARAM_UCASTCIPHERS: |
| if (!rsn) |
| return -EFAULT; |
| /* |
| * NB: this logic intentionally ignores unknown and |
| * unsupported ciphers so folks can specify 0xff or |
| * similar and get all available ciphers. |
| */ |
| /* caps are really ciphers */ |
| caps = 0; |
| for (j = 1; j < 32; j++) /* NB: skip WEP */ |
| if ((value & (1 << j)) && |
| ((vap->iv_caps & cipher2cap(j)) || |
| ieee80211_crypto_available(j))) |
| caps |= 1 << j; |
| if (caps == 0) /* nothing available */ |
| return -EINVAL; |
| /* XXX verify ciphers ok for unicast use? */ |
| /* XXX disallow if running as it'll have no effect */ |
| rsn->rsn_ucastcipherset = caps; |
| if (vap->iv_flags & IEEE80211_F_WPA) |
| retv = ENETRESET; |
| break; |
| case IEEE80211_PARAM_UCASTCIPHER: |
| if ((vap->iv_caps & cipher2cap(value)) == 0 && |
| !ieee80211_crypto_available(value)) |
| return -EINVAL; |
| if (!rsn) |
| return -EFAULT; |
| |
| rsn->rsn_ucastcipher = value; |
| if (vap->iv_flags & IEEE80211_F_WPA) |
| retv = ENETRESET; |
| break; |
| case IEEE80211_PARAM_UCASTKEYLEN: |
| if (!(0 < value && value <= IEEE80211_KEYBUF_SIZE)) |
| return -EINVAL; |
| if (!rsn) |
| return -EFAULT; |
| /* XXX no way to verify driver capability */ |
| rsn->rsn_ucastkeylen = value; |
| break; |
| case IEEE80211_PARAM_KEYMGTALGS: |
| if (!rsn) |
| return -EFAULT; |
| /* |
| * Map supplcant values to RSN values. Included only the currently |
| * used mappings. But, need to increases the cases as we support more. |
| * */ |
| switch (value) { |
| case WPA_KEY_MGMT_PSK: |
| if (vap->iv_pmf == IEEE80211_MFP_PROTECT_REQUIRE) |
| rsn->rsn_keymgmtset = RSN_ASE_8021X_PSK_SHA256; |
| else |
| rsn->rsn_keymgmtset = RSN_ASE_8021X_PSK; |
| break; |
| case WPA_KEY_MGMT_IEEE8021X: |
| rsn->rsn_keymgmtset = RSN_ASE_8021X_UNSPEC; |
| break; |
| case WPA_KEY_MGMT_NONE: |
| rsn->rsn_keymgmtset = RSN_ASE_NONE; |
| break; |
| case WPA_KEY_MGMT_IEEE8021X_SHA256: |
| rsn->rsn_keymgmtset = RSN_ASE_8021X_SHA256; |
| break; |
| case WPA_KEY_MGMT_PSK_SHA256: |
| rsn->rsn_keymgmtset = RSN_ASE_8021X_PSK_SHA256; |
| break; |
| case (WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_IEEE8021X): |
| rsn->rsn_keymgmtset = |
| (RSN_ASE_8021X_UNSPEC | WPA_KEY_MGMT_FT_IEEE8021X); |
| break; |
| case (WPA_KEY_MGMT_FT_PSK | WPA_KEY_MGMT_PSK): |
| rsn->rsn_keymgmtset = (RSN_ASE_8021X_PSK | WPA_KEY_MGMT_FT_PSK); |
| break; |
| default: |
| rsn->rsn_keymgmtset = value; |
| break; |
| } |
| if (vap->iv_flags & IEEE80211_F_WPA) |
| retv = ENETRESET; |
| break; |
| case IEEE80211_PARAM_RSNCAPS: |
| if (!rsn) |
| return -EFAULT; |
| /* XXX check */ |
| rsn->rsn_caps = value; |
| if (vap->iv_flags & IEEE80211_F_WPA) |
| retv = ENETRESET; |
| break; |
| case IEEE80211_PARAM_WPA: |
| if (value > 3) |
| return -EINVAL; |
| /* XXX verify ciphers available */ |
| vap->iv_flags &= ~IEEE80211_F_WPA; |
| switch (value) { |
| case 1: |
| vap->iv_flags |= IEEE80211_F_WPA1; |
| break; |
| case 2: |
| vap->iv_flags |= IEEE80211_F_WPA2; |
| break; |
| case 3: |
| vap->iv_flags |= IEEE80211_F_WPA1 | IEEE80211_F_WPA2; |
| break; |
| } |
| retv = ENETRESET; /* XXX? */ |
| break; |
| case IEEE80211_PARAM_ROAMING: |
| if (!(IEEE80211_ROAMING_DEVICE <= value && |
| value <= IEEE80211_ROAMING_MANUAL)) |
| return -EINVAL; |
| ic->ic_roaming = value; |
| break; |
| case IEEE80211_PARAM_PRIVACY: |
| if (value) { |
| /* XXX check for key state? */ |
| vap->iv_flags |= IEEE80211_F_PRIVACY; |
| } else |
| vap->iv_flags &= ~IEEE80211_F_PRIVACY; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| break; |
| case IEEE80211_PARAM_DROPUNENCRYPTED: |
| if (value) |
| vap->iv_flags |= IEEE80211_F_DROPUNENC; |
| else |
| vap->iv_flags &= ~IEEE80211_F_DROPUNENC; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| break; |
| case IEEE80211_PARAM_DROPUNENC_EAPOL: |
| if (value) |
| IEEE80211_VAP_DROPUNENC_EAPOL_ENABLE(vap); |
| else |
| IEEE80211_VAP_DROPUNENC_EAPOL_DISABLE(vap); |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| break; |
| case IEEE80211_PARAM_COUNTERMEASURES: |
| { |
| int invoked = 0; |
| int cleared = 0; |
| static const char *tag = QEVT_COMMON_PREFIX; |
| |
| if (value) { |
| if ((vap->iv_flags & IEEE80211_F_WPA) == 0) |
| return -EINVAL; |
| vap->iv_flags |= IEEE80211_F_COUNTERM; |
| invoked = 1; |
| } else { |
| if (vap->iv_flags & IEEE80211_F_COUNTERM) { |
| cleared = 1; |
| } |
| vap->iv_flags &= ~IEEE80211_F_COUNTERM; |
| } |
| if (invoked || cleared) |
| { |
| ieee80211_eventf(dev, "%sTKIP countermeasures %s", tag, |
| invoked ? "invoked" : "cleared"); |
| } |
| } |
| break; |
| case IEEE80211_PARAM_DRIVER_CAPS: |
| vap->iv_caps = value; /* NB: for testing */ |
| break; |
| case IEEE80211_PARAM_WMM: |
| if (ic->ic_caps & IEEE80211_C_WME){ |
| if (value) { |
| vap->iv_flags |= IEEE80211_F_WME; |
| vap->iv_ic->ic_flags |= IEEE80211_F_WME; /* XXX needed by ic_reset */ |
| } else { |
| vap->iv_flags &= ~IEEE80211_F_WME; |
| vap->iv_ic->ic_flags &= ~IEEE80211_F_WME; /* XXX needed by ic_reset */ |
| } |
| retv = ENETRESET; /* Renegotiate for capabilities */ |
| } |
| break; |
| case IEEE80211_PARAM_HIDESSID: |
| { |
| int beacon_update_required= 0; |
| |
| if ((!!(vap->iv_flags & IEEE80211_F_HIDESSID)) ^ (!!value)) |
| beacon_update_required = 1; |
| |
| if (value) |
| vap->iv_flags |= IEEE80211_F_HIDESSID; |
| else |
| vap->iv_flags &= ~IEEE80211_F_HIDESSID; |
| |
| if (beacon_update_required && (vap->iv_state & IEEE80211_S_RUN)) |
| ic->ic_beacon_update(vap); |
| |
| } |
| break; |
| case IEEE80211_PARAM_APBRIDGE: |
| if (value == 0) |
| vap->iv_flags |= IEEE80211_F_NOBRIDGE; |
| else |
| vap->iv_flags &= ~IEEE80211_F_NOBRIDGE; |
| break; |
| case IEEE80211_PARAM_INACT: |
| vap->iv_inact_run = value / IEEE80211_INACT_WAIT; |
| break; |
| case IEEE80211_PARAM_INACT_AUTH: |
| vap->iv_inact_auth = value / IEEE80211_INACT_WAIT; |
| break; |
| case IEEE80211_PARAM_INACT_INIT: |
| vap->iv_inact_init = value / IEEE80211_INACT_WAIT; |
| break; |
| case IEEE80211_PARAM_DTIM_PERIOD: |
| if (vap->iv_opmode != IEEE80211_M_HOSTAP && |
| vap->iv_opmode != IEEE80211_M_IBSS) |
| return -EINVAL; |
| if (IEEE80211_DTIM_MIN <= value && |
| value <= IEEE80211_DTIM_MAX) { |
| vap->iv_dtim_period = value; |
| ic->ic_beacon_update(vap); |
| retv = -EINVAL; /* requires restart */ |
| } else |
| retv = EINVAL; |
| break; |
| case IEEE80211_PARAM_BEACON_INTERVAL: |
| if (vap->iv_opmode != IEEE80211_M_HOSTAP && |
| vap->iv_opmode != IEEE80211_M_IBSS) |
| return -EINVAL; |
| |
| if (IEEE80211_BINTVAL_VALID(value)) { |
| vap->iv_ic->ic_lintval_backup = value; |
| ieee80211_beacon_interval_set(vap->iv_ic, value); |
| retv = -EINVAL; |
| } else { |
| retv = EINVAL; |
| } |
| break; |
| case IEEE80211_PARAM_DOTH: |
| if (value) |
| ic->ic_flags |= IEEE80211_F_DOTH; |
| else |
| ic->ic_flags &= ~IEEE80211_F_DOTH; |
| break; |
| case IEEE80211_PARAM_SHPREAMBLE: |
| if (value) { |
| ic->ic_caps |= IEEE80211_C_SHPREAMBLE; |
| ic->ic_flags |= IEEE80211_F_SHPREAMBLE; |
| ic->ic_flags &= ~IEEE80211_F_USEBARKER; |
| } else { |
| ic->ic_caps &= ~IEEE80211_C_SHPREAMBLE; |
| ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; |
| ic->ic_flags |= IEEE80211_F_USEBARKER; |
| } |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| ieee80211_wireless_reassoc(vap, 0, 1); |
| break; |
| case IEEE80211_PARAM_PWRCONSTRAINT: |
| { |
| uint16_t pwr_constraint = (value & 0xffff); |
| struct pwr_info_per_vap pwr; |
| pwr.vap = vap; |
| pwr.max_in_minpwr = -100; /* a value that never be real */ |
| |
| if ((ic->ic_flags & IEEE80211_F_DOTH) && (ic->ic_flags_ext & IEEE80211_FEXT_TPC)) { |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP && ic->ic_bsschan != IEEE80211_CHAN_ANYC) { |
| ieee80211_iterate_nodes(&ic->ic_sta, get_max_in_minpwr, &pwr, 1); |
| if (pwr_constraint >= ic->ic_bsschan->ic_maxregpower) { |
| printk("power constraint(%d) >= current channel max regulatory power(%d)\n", pwr_constraint, ic->ic_bsschan->ic_maxregpower); |
| retv = EINVAL; |
| } |
| else if ((pwr.max_in_minpwr != -100) && |
| ((ic->ic_bsschan->ic_maxregpower - pwr_constraint) < pwr.max_in_minpwr)) { |
| printk("power constraint(%d) make local max transmit power(%d) less than the max value(%d) of min power in associated STAs\n", |
| pwr_constraint, |
| (ic->ic_bsschan->ic_maxregpower - pwr_constraint), |
| pwr.max_in_minpwr); |
| retv = EINVAL; |
| } |
| else { |
| ic->ic_pwr_constraint = pwr_constraint; |
| if (vap->iv_state == IEEE80211_S_RUN) |
| ic->ic_beacon_update(vap); |
| } |
| } else { |
| retv = EOPNOTSUPP; |
| } |
| } else { |
| retv = EOPNOTSUPP; |
| } |
| } |
| break; |
| case IEEE80211_PARAM_GENREASSOC: |
| if (!vap->iv_bss) |
| return -EFAULT; |
| |
| IEEE80211_SEND_MGMT(vap->iv_bss, IEEE80211_FC0_SUBTYPE_REASSOC_REQ, 0); |
| break; |
| case IEEE80211_PARAM_REPEATER: |
| if (IEEE80211_VAP_WDS_IS_RBS(vap) || IEEE80211_VAP_WDS_IS_MBS(vap)) { |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG, |
| "%s can't config repeater since " |
| "it's a extender\n", vap->iv_dev->name); |
| return -EINVAL; |
| } |
| |
| first_vap = TAILQ_FIRST(&ic->ic_vaps); |
| if (first_vap->iv_opmode != IEEE80211_M_STA) |
| return -EINVAL; |
| |
| if (value) { |
| ic->ic_flags_ext |= IEEE80211_FEXT_REPEATER; |
| ieee80211_new_state(first_vap, IEEE80211_S_INIT, 0); |
| } else { |
| ic->ic_flags_ext &= ~IEEE80211_FEXT_REPEATER; |
| } |
| ieee80211_param_to_qdrv(vap, IEEE80211_PARAM_REPEATER, value, NULL, 0); |
| |
| break; |
| case IEEE80211_PARAM_WDS: |
| if (value) { |
| vap->iv_qtn_flags &= ~IEEE80211_QTN_BRIDGEMODE_DISABLED; |
| } else { |
| vap->iv_qtn_flags |= IEEE80211_QTN_BRIDGEMODE_DISABLED; |
| } |
| ieee80211_bridgemode_set(vap, 1); |
| break; |
| case IEEE80211_PARAM_BGSCAN: |
| if (value) { |
| if ((vap->iv_caps & IEEE80211_C_BGSCAN) == 0) |
| return -EINVAL; |
| vap->iv_flags |= IEEE80211_F_BGSCAN; |
| } else { |
| /* XXX racey? */ |
| vap->iv_flags &= ~IEEE80211_F_BGSCAN; |
| ieee80211_cancel_scan(vap); /* anything current */ |
| } |
| break; |
| case IEEE80211_PARAM_BGSCAN_IDLE: |
| if (value >= IEEE80211_BGSCAN_IDLE_MIN) |
| vap->iv_bgscanidle = msecs_to_jiffies(value); |
| else |
| retv = EINVAL; |
| break; |
| case IEEE80211_PARAM_BGSCAN_INTERVAL: |
| if (value >= IEEE80211_BGSCAN_INTVAL_MIN) { |
| vap->iv_bgscanintvl = value * HZ; |
| ic->ic_extender_bgscanintvl = vap->iv_bgscanintvl; |
| } else { |
| retv = EINVAL; |
| } |
| break; |
| case IEEE80211_PARAM_SCAN_OPCHAN: |
| ic->ic_scan_opchan_enable = !!(value); |
| break; |
| case IEEE80211_PARAM_EXTENDER_MBS_RSSI_MARGIN: |
| if (ieee80211_swfeat_is_supported(SWFEAT_ID_QHOP, 1)) |
| ic->ic_extender_mbs_rssi_margin = value; |
| break; |
| case IEEE80211_PARAM_MCAST_RATE: |
| /* units are in KILObits per second */ |
| if (value >= 256 && value <= 54000) |
| vap->iv_mcast_rate = value; |
| else |
| retv = EINVAL; |
| break; |
| case IEEE80211_PARAM_COVERAGE_CLASS: |
| if (value >= 0 && value <= IEEE80211_COVERAGE_CLASS_MAX) { |
| ic->ic_coverageclass = value; |
| if (IS_UP_AUTO(vap)) |
| ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); |
| retv = 0; |
| } else |
| retv = EINVAL; |
| break; |
| case IEEE80211_PARAM_COUNTRY_IE: |
| if (value) |
| ic->ic_flags_ext |= IEEE80211_FEXT_COUNTRYIE; |
| else |
| ic->ic_flags_ext &= ~IEEE80211_FEXT_COUNTRYIE; |
| retv = ENETRESET; |
| break; |
| case IEEE80211_PARAM_REGCLASS: |
| if (value) |
| ic->ic_flags_ext |= IEEE80211_FEXT_REGCLASS; |
| else |
| ic->ic_flags_ext &= ~IEEE80211_FEXT_REGCLASS; |
| retv = ENETRESET; |
| break; |
| case IEEE80211_PARAM_SCANVALID: |
| vap->iv_scanvalid = value * HZ; |
| break; |
| case IEEE80211_PARAM_ROAM_RSSI_11A: |
| vap->iv_roam.rssi11a = value; |
| break; |
| case IEEE80211_PARAM_ROAM_RSSI_11B: |
| vap->iv_roam.rssi11bOnly = value; |
| break; |
| case IEEE80211_PARAM_ROAM_RSSI_11G: |
| vap->iv_roam.rssi11b = value; |
| break; |
| case IEEE80211_PARAM_ROAM_RATE_11A: |
| vap->iv_roam.rate11a = value; |
| break; |
| case IEEE80211_PARAM_ROAM_RATE_11B: |
| vap->iv_roam.rate11bOnly = value; |
| break; |
| case IEEE80211_PARAM_ROAM_RATE_11G: |
| vap->iv_roam.rate11b = value; |
| break; |
| case IEEE80211_PARAM_UAPSDINFO: |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| if (ic->ic_caps & IEEE80211_C_UAPSD) { |
| if (value) |
| IEEE80211_VAP_UAPSD_ENABLE(vap); |
| else |
| IEEE80211_VAP_UAPSD_DISABLE(vap); |
| retv = ENETRESET; |
| } |
| } else if (vap->iv_opmode == IEEE80211_M_STA) { |
| vap->iv_uapsdinfo = value; |
| IEEE80211_VAP_UAPSD_ENABLE(vap); |
| retv = ENETRESET; |
| } |
| break; |
| case IEEE80211_PARAM_SLEEP: |
| if (!vap->iv_bss) |
| return -EFAULT; |
| |
| /* XXX: Forced sleep for testing. Does not actually place the |
| * HW in sleep mode yet. this only makes sense for STAs. |
| */ |
| if (value) { |
| /* goto sleep */ |
| IEEE80211_VAP_GOTOSLEEP(vap); |
| } else { |
| /* wakeup */ |
| IEEE80211_VAP_WAKEUP(vap); |
| } |
| ieee80211_ref_node(vap->iv_bss); |
| ieee80211_send_nulldata(vap->iv_bss); |
| break; |
| case IEEE80211_PARAM_TUNEPD: |
| if (!vap->iv_bss) |
| return -EFAULT; |
| |
| /* Send specified number of frames */ |
| for (j = 0; j < value; j++) |
| ieee80211_send_tuning_data(vap->iv_bss); |
| break; |
| case IEEE80211_PARAM_QOSNULL: |
| { |
| struct ieee80211_node *ni_sta; |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| /* Tx a QoS null frame to an STA by passing node_index */ |
| ni_sta = ieee80211_find_node_by_idx(ic, NULL, value); |
| if (ni_sta) { |
| ieee80211_send_qosnulldata(ni_sta, WMM_AC_BK); |
| } |
| } else { |
| if (!vap->iv_bss) |
| return -EFAULT; |
| |
| /* Force a QoS Null for testing. */ |
| ieee80211_ref_node(vap->iv_bss); |
| ieee80211_send_qosnulldata(vap->iv_bss, value); |
| } |
| } |
| break; |
| case IEEE80211_PARAM_PSPOLL: |
| if (!vap->iv_bss) |
| return -EFAULT; |
| |
| /* Force a PS-POLL for testing. */ |
| ieee80211_send_pspoll(vap->iv_bss); |
| break; |
| case IEEE80211_PARAM_EOSPDROP: |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| if (value) |
| IEEE80211_VAP_EOSPDROP_ENABLE(vap); |
| else |
| IEEE80211_VAP_EOSPDROP_DISABLE(vap); |
| } |
| break; |
| case IEEE80211_PARAM_MARKDFS: |
| if (value) |
| ic->ic_flags_ext |= IEEE80211_FEXT_MARKDFS; |
| else |
| ic->ic_flags_ext &= ~IEEE80211_FEXT_MARKDFS; |
| |
| /* set radar mode for qdrv */ |
| ic->ic_set_radar(value); |
| break; |
| case IEEE80211_PARAM_RADAR_BW: |
| ic->ic_radar_bw = value; |
| break; |
| case IEEE80211_PARAM_STA_DFS: |
| if (vap->iv_opmode == IEEE80211_M_STA) { |
| struct ieee80211_node *ni = vap->iv_bss; |
| if (value) |
| ic->ic_flags_ext |= IEEE80211_FEXT_MARKDFS; |
| else |
| ic->ic_flags_ext &= ~IEEE80211_FEXT_MARKDFS; |
| |
| if (vap->iv_state == IEEE80211_S_RUN) { |
| SCSDBG(SCSLOG_NOTICE, "send qtn DFS report (DFS %s)\n", value ? |
| "Enabled" : "Disabled"); |
| ieee80211_send_action_dfs_report(ni); |
| } |
| |
| ic->ic_enable_sta_dfs(value); |
| } |
| break; |
| case IEEE80211_PARAM_DYNAMIC_AC: |
| if (ic->ic_rf_chipid == CHIPID_DUAL) |
| return -EOPNOTSUPP; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| break; |
| case IEEE80211_PARAM_AMPDU_DENSITY: |
| if (value < IEEE80211_AMPDU_MIN_DENSITY || |
| value > IEEE80211_AMPDU_MAX_DENSITY) { |
| return -EINVAL; |
| } else { |
| if (ic->ic_htcap.mpduspacing != value) { |
| ic->ic_htcap.mpduspacing = value; |
| ieee80211_wireless_reassoc(vap, 0, 1); |
| } |
| } |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| break; |
| case IEEE80211_PARAM_BA_SETUP_ENABLE: |
| ieee80211_ba_setup_detect_set(vap, value); |
| break; |
| case IEEE80211_PARAM_AGGREGATION: |
| if (value == 0 && IS_IEEE80211_VHT_ENABLED(ic)) |
| return -EOPNOTSUPP; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| break; |
| case IEEE80211_PARAM_MCS_CAP: |
| /* |
| * Updating iv_mcs_config to have sync with other MCS |
| * commands, call_qcsapi and iwpriv |
| */ |
| if (!ieee80211_ht_tx_mcs_is_valid(value)) { |
| printk("Invalid MCS in 11n mode\n"); |
| return -EINVAL; |
| } |
| |
| vap->iv_mcs_config = IEEE80211_N_RATE_PREFIX | ((value << 16) & 0xff0000) | |
| ((value << 8) & 0xff00) | (value & 0xff); |
| |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| break; |
| case IEEE80211_PARAM_MU_ENABLE: |
| if (!ieee80211_swfeat_is_supported(SWFEAT_ID_MU_MIMO, 1) || |
| (vap->iv_opmode == IEEE80211_M_STA && |
| get_hardware_revision() < HARDWARE_REVISION_TOPAZ_A2)) { |
| return -EOPNOTSUPP; |
| } else { |
| uint32_t mu_bf_cap_flag = ic->ic_vhtcap.cap_flags & |
| IEEE80211_VHTCAP_C_MU_BEAM_FORMXX_CAP_MASK; |
| |
| ic->ic_vhtcap.cap_flags &= ~IEEE80211_VHTCAP_C_MU_BEAM_FORMXX_CAP_MASK; |
| ic->ic_mu_enable = value; |
| |
| /* when MU is enabled, make sure txBF STS cap is advertising 4SS */ |
| if (ic->ic_mu_enable) { |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| mu_bf_cap_flag = IEEE80211_VHTCAP_C_MU_BEAM_FORMER_CAP; |
| } else if (vap->iv_opmode == IEEE80211_M_STA) { |
| mu_bf_cap_flag = IEEE80211_VHTCAP_C_MU_BEAM_FORMEE_CAP; |
| } |
| |
| ic->ic_vhtcap.bfstscap_save = ic->ic_vhtcap.bfstscap; |
| ic->ic_vhtcap.bfstscap = IEEE80211_VHTCAP_RX_STS_4; |
| ic->ic_vhtcap.cap_flags |= mu_bf_cap_flag; |
| |
| ic->ic_vhtcap_24g.bfstscap_save = ic->ic_vhtcap_24g.bfstscap; |
| ic->ic_vhtcap_24g.bfstscap = IEEE80211_VHTCAP_RX_STS_4; |
| } else { |
| if (ic->ic_vhtcap.bfstscap_save != IEEE80211_VHTCAP_RX_STS_INVALID) { |
| ic->ic_vhtcap.bfstscap = ic->ic_vhtcap.bfstscap_save; |
| } |
| |
| if (ic->ic_vhtcap_24g.bfstscap_save != IEEE80211_VHTCAP_RX_STS_INVALID) { |
| ic->ic_vhtcap_24g.bfstscap = ic->ic_vhtcap_24g.bfstscap_save; |
| } |
| } |
| |
| ieee80211_param_to_qdrv(vap, param, ic->ic_mu_enable, NULL, 0); |
| |
| /* Force a reassociation to use the new BEAM FORMEE/ER settings */ |
| TAILQ_FOREACH_SAFE(vap_each, &ic->ic_vaps, iv_next, vap_tmp) { |
| ieee80211_wireless_reassoc(vap_each, 0, 1); |
| } |
| } |
| break; |
| case IEEE80211_PARAM_TXBF_PERIOD: |
| ic->ic_vopt.bf = value; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| break; |
| case IEEE80211_PARAM_PS_CMD: |
| ic->ic_vopt.bbf = value; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| break; |
| case IEEE80211_PARAM_MIMOMODE: |
| case IEEE80211_PARAM_SHORT_RETRY_LIMIT: |
| case IEEE80211_PARAM_LONG_RETRY_LIMIT: |
| case IEEE80211_PARAM_RETRY_COUNT: |
| case IEEE80211_PARAM_RG: |
| case IEEE80211_PARAM_ACK_POLICY: |
| case IEEE80211_PARAM_EXP_MAT_SEL: |
| case IEEE80211_PARAM_LEGACY_MODE: |
| case IEEE80211_PARAM_MAX_AGG_SUBFRM: |
| case IEEE80211_PARAM_MAX_AGG_SIZE: |
| case IEEE80211_PARAM_TXBF_CTRL: |
| case IEEE80211_PARAM_HTBA_SEQ_CTRL: |
| case IEEE80211_PARAM_HTBA_SIZE_CTRL: |
| case IEEE80211_PARAM_HTBA_TIME_CTRL: |
| case IEEE80211_PARAM_HT_ADDBA: |
| case IEEE80211_PARAM_HT_DELBA: |
| case IEEE80211_PARAM_MUC_PROFILE: |
| case IEEE80211_PARAM_MUC_PHY_STATS: |
| case IEEE80211_PARAM_MUC_SET_PARTNUM: |
| case IEEE80211_PARAM_ENABLE_GAIN_ADAPT: |
| case IEEE80211_PARAM_FORCEMICERROR: |
| case IEEE80211_PARAM_ENABLECOUNTERMEASURES: |
| case IEEE80211_PARAM_RATE_CTRL_FLAGS: |
| case IEEE80211_PARAM_CONFIG_BB_INTR_DO_SRESET: |
| case IEEE80211_PARAM_CONFIG_MAC_INTR_DO_SRESET: |
| case IEEE80211_PARAM_CONFIG_WDG_DO_SRESET: |
| case IEEE80211_PARAM_TRIGGER_RESET: |
| case IEEE80211_PARAM_INJECT_INVALID_FCS: |
| case IEEE80211_PARAM_CONFIG_WDG_SENSITIVITY: |
| case IEEE80211_PARAM_MAX_MGMT_FRAMES: |
| case IEEE80211_PARAM_MCS_ODD_EVEN: |
| case IEEE80211_PARAM_RESTRICTED_MODE: |
| case IEEE80211_PARAM_RESTRICT_RTS: |
| case IEEE80211_PARAM_RESTRICT_LIMIT: |
| case IEEE80211_PARAM_RESTRICT_RATE: |
| case IEEE80211_PARAM_SWRETRY_AGG_MAX: |
| case IEEE80211_PARAM_SWRETRY_NOAGG_MAX: |
| case IEEE80211_PARAM_SWRETRY_SUSPEND_XMIT: |
| case IEEE80211_PARAM_BB_MAC_RESET_MSGS: |
| case IEEE80211_PARAM_BB_MAC_RESET_DONE_WAIT: |
| case IEEE80211_PARAM_TX_AGG_TIMEOUT: |
| case IEEE80211_PARAM_LEGACY_RETRY_LIMIT: |
| case IEEE80211_PARAM_RX_CTRL_FILTER: |
| case IEEE80211_PARAM_DUMP_TCM_FD: |
| case IEEE80211_PARAM_RXCSR_ERR_ALLOW: |
| case IEEE80211_PARAM_DUMP_TRIGGER: |
| case IEEE80211_PARAM_STOP_FLAGS: |
| case IEEE80211_PARAM_CHECK_FLAGS: |
| case IEEE80211_PARAM_PWR_ADJUST: |
| case IEEE80211_PARAM_PWR_ADJUST_AUTO: |
| case IEEE80211_PARAM_RTS_CTS: |
| case IEEE80211_PARAM_TX_QOS_SCHED: |
| case IEEE80211_PARAM_PEER_RTS_MODE: |
| case IEEE80211_PARAM_DYN_WMM: |
| case IEEE80211_PARAM_GET_CH_INUSE: |
| case IEEE80211_PARAM_RX_AGG_TIMEOUT: |
| case IEEE80211_PARAM_FORCE_MUC_HALT: |
| case IEEE80211_PARAM_FORCE_ENABLE_TRIGGERS: |
| case IEEE80211_PARAM_FORCE_MUC_TRACE: |
| case IEEE80211_PARAM_BK_BITMAP_MODE: |
| case IEEE80211_PARAM_TEST_LNCB: |
| case IEEE80211_PARAM_UNKNOWN_DEST_ARP: |
| case IEEE80211_PARAM_UNKNOWN_DEST_FWD: |
| case IEEE80211_PARAM_DBG_MODE_FLAGS: |
| case IEEE80211_PARAM_PWR_SAVE: |
| case IEEE80211_PARAM_DBG_FD: |
| case IEEE80211_PARAM_CCA_PRI: |
| case IEEE80211_PARAM_CCA_SEC: |
| case IEEE80211_PARAM_CCA_SEC40: |
| case IEEE80211_PARAM_CCA_FIXED: |
| case IEEE80211_PARAM_DYN_AGG_TIMEOUT: |
| case IEEE80211_PARAM_SIFS_TIMING: |
| case IEEE80211_PARAM_TEST_TRAFFIC: |
| case IEEE80211_PARAM_TX_AMSDU: |
| case IEEE80211_PARAM_QCAT_STATE: |
| case IEEE80211_PARAM_RALG_DBG: |
| case IEEE80211_PARAM_SINGLE_AGG_QUEUING: |
| case IEEE80211_PARAM_BA_THROT: |
| case IEEE80211_PARAM_TX_QUEUING_ALG: |
| case IEEE80211_PARAM_DAC_DBG: |
| case IEEE80211_PARAM_CARRIER_ID: |
| case IEEE80211_PARAM_WME_THROT: |
| case IEEE80211_PARAM_GENPCAP: |
| case IEEE80211_PARAM_CCA_DEBUG: |
| case IEEE80211_PARAM_CCA_STATS_PERIOD: |
| case IEEE80211_PARAM_TUNEPD_DONE: |
| case IEEE80211_PARAM_AUC_RX_DBG: |
| case IEEE80211_PARAM_AUC_TX_DBG: |
| case IEEE80211_PARAM_RX_ACCELERATE: |
| case IEEE80211_PARAM_RX_ACCEL_LOOKUP_SA: |
| case IEEE80211_PARAM_BR_IP_ADDR: |
| case IEEE80211_PARAM_AC_INHERITANCE: |
| case IEEE80211_PARAM_AC_Q2Q_INHERITANCE: |
| case IEEE80211_PARAM_1SS_AMSDU_SUPPORT: |
| case IEEE80211_PARAM_TACMAP: |
| case IEEE80211_PARAM_AUC_QOS_SCH: |
| case IEEE80211_PARAM_TXBF_IOT: |
| case IEEE80211_PARAM_AGGRESSIVE_AGG: |
| case IEEE80211_PARAM_MU_DEBUG_FLAG: |
| case IEEE80211_PARAM_INST_MU_GRP_QMAT: |
| case IEEE80211_PARAM_DELE_MU_GRP_QMAT: |
| case IEEE80211_PARAM_EN_MU_GRP_QMAT: |
| case IEEE80211_PARAM_DIS_MU_GRP_QMAT: |
| case IEEE80211_PARAM_SET_CRC_ERR: |
| case IEEE80211_PARAM_MU_SWITCH_USR_POS: |
| case IEEE80211_PARAM_SET_GRP_SND_PERIOD: |
| case IEEE80211_PARAM_SET_PREC_SND_PERIOD: |
| case IEEE80211_PARAM_SET_MU_RANK_TOLERANCE: |
| case IEEE80211_PARAM_AUTO_CCA_ENABLE: |
| case IEEE80211_PARAM_AUTO_CCA_PARAMS: |
| case IEEE80211_PARAM_AUTO_CCA_DEBUG: |
| case IEEE80211_PARAM_AUTO_CS_ENABLE: |
| case IEEE80211_PARAM_AUTO_CS_PARAMS: |
| case IEEE80211_PARAM_CS_THRESHOLD: |
| case IEEE80211_PARAM_CS_THRESHOLD_DBM: |
| case IEEE80211_PARAM_DUMP_PPPC_TX_SCALE_BASES: |
| case IEEE80211_PARAM_GLOBAL_FIXED_TX_SCALE_INDEX: |
| case IEEE80211_PARAM_NDPA_LEGACY_FORMAT: |
| case IEEE80211_PARAM_SFS: |
| case IEEE80211_PARAM_INST_1SS_DEF_MAT_ENABLE: |
| case IEEE80211_PARAM_INST_1SS_DEF_MAT_THRESHOLD: |
| case IEEE80211_PARAM_RATE_TRAIN_DBG: |
| case IEEE80211_PARAM_MU_USE_EQ: |
| case IEEE80211_PARAM_MU_AIRTIME_PADDING: |
| case IEEE80211_PARAM_MU_AMSDU_SIZE: |
| case IEEE80211_PARAM_RESTRICT_WLAN_IP: |
| case IEEE80211_PARAM_MUC_SYS_DEBUG: |
| case IEEE80211_PARAM_AUC_TX_AGG_DURATION: |
| case IEEE80211_PARAM_1BIT_PKT_DETECT: |
| case IEEE80211_PARAM_ANTENNA_USAGE: |
| case IEEE80211_PARAM_VSP_NOD_DEBUG: |
| case IEEE80211_PARAM_DYNAMIC_SIFS_TIMING: |
| case IEEE80211_PARAM_BEACON_HANG_TIMEOUT: |
| case IEEE80211_PARAM_BB_DEAFNESS_WAR_EN: |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| break; |
| case IEEE80211_PARAM_OFF_CHAN_SUSPEND: |
| if (value & IEEE80211_OFFCHAN_SUSPEND_MASK) { |
| value = value & IEEE80211_OFFCHAN_TIMEOUT_MASK; |
| if (value > IEEE80211_OFFCHAN_TIMEOUT_MAX) |
| value = IEEE80211_OFFCHAN_TIMEOUT_MAX; |
| else if (value < IEEE80211_OFFCHAN_TIMEOUT_MIN) |
| value = IEEE80211_OFFCHAN_TIMEOUT_MIN; |
| ieee80211_off_channel_suspend(vap, value); |
| } else { |
| ieee80211_off_channel_resume(vap); |
| } |
| break; |
| case IEEE80211_PARAM_QTN_OPTI_MODE: |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| ieee80211_wireless_reassoc(vap, 0, 1); |
| break; |
| case IEEE80211_PARAM_SET_RTS_BW_DYN: |
| ic->ic_rts_bw_dyn = value; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| break; |
| case IEEE80211_PARAM_SET_DUP_RTS: |
| ic->ic_dup_rts = value; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| break; |
| case IEEE80211_PARAM_SET_CTS_BW: |
| ic->ic_cts_bw = value; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| break; |
| case IEEE80211_PARAM_NDPA_DUR: |
| ic->ic_ndpa_dur = value; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| break; |
| case IEEE80211_PARAM_SU_TXBF_PKT_CNT: |
| ic->ic_su_txbf_pkt_cnt = value; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| break; |
| case IEEE80211_PARAM_MU_TXBF_PKT_CNT: |
| ic->ic_mu_txbf_pkt_cnt = value; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| break; |
| case IEEE80211_PARAM_MU_DEBUG_LEVEL: |
| ic->ic_mu_debug_level = value; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| break; |
| case IEEE80211_PARAM_FIXED_SGI: |
| ic->ic_gi_fixed = value; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| break; |
| case IEEE80211_PARAM_FIXED_BW: |
| if (value & QTN_BW_FIXED_EN) { |
| int bw = ieee80211_get_bw(ic); |
| int qtn_bw = MS(value, QTN_BW_FIXED_BW); |
| if (bw == BW_INVALID) { |
| printk("current bw is invalid!\n"); |
| break; |
| } |
| if ((bw == BW_HT20 && qtn_bw > QTN_BW_20M) || |
| (bw == BW_HT40 && qtn_bw > QTN_BW_40M) || |
| (bw == BW_HT80 && qtn_bw > QTN_BW_80M)) { |
| printk("Can't set fixed qtn_bw %u because current bw is %u\n", |
| qtn_bw, bw); |
| break; |
| } |
| } |
| ic->ic_bw_fixed = value; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| break; |
| case IEEE80211_PARAM_RTSTHRESHOLD: |
| if (value == vap->iv_rtsthreshold) // Nothing to do |
| break; |
| |
| if (IEEE80211_RTS_MIN <= value && value <= IEEE80211_RTS_MAX) { |
| vap->iv_rtsthreshold = value; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| } else { |
| retv = EINVAL; |
| } |
| break; |
| case IEEE80211_PARAM_PWR_ADJUST_SCANCNT: |
| ic->ic_pwr_adjust_scancnt = value; |
| break; |
| case IEEE80211_PARAM_MUC_FLAGS: |
| if (value & QTN_FLAG_MCS_UEQM_DISABLE) { |
| ic->ic_caps &= ~IEEE80211_C_UEQM; |
| } else { |
| ic->ic_caps |= IEEE80211_C_UEQM; |
| } |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| ieee80211_wireless_reassoc(vap, 0, 1); |
| break; |
| case IEEE80211_PARAM_HT_NSS_CAP: |
| case IEEE80211_PARAM_VHT_NSS_CAP: |
| retv = ieee80211_set_nss_cap(vap, param, value); |
| break; |
| case IEEE80211_PARAM_VHT_MCS_CAP: |
| retv = ieee80211_set_vht_mcs_cap(vap, value); |
| break; |
| case IEEE80211_PARAM_LDPC: |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| |
| /* Update the HT capabilities */ |
| TAILQ_FOREACH_SAFE(vap_each, &ic->ic_vaps, iv_next, vap_tmp) { |
| if (value) { |
| vap_each->iv_ht_flags |= IEEE80211_HTF_LDPC_ENABLED; |
| vap_each->iv_vht_flags |= IEEE80211_VHTCAP_C_RX_LDPC; |
| ic->ic_vhtcap.cap_flags |= IEEE80211_VHTCAP_C_RX_LDPC; |
| ic->ic_vhtcap_24g.cap_flags |= IEEE80211_VHTCAP_C_RX_LDPC; |
| } else { |
| vap_each->iv_ht_flags &= ~IEEE80211_HTF_LDPC_ENABLED; |
| vap_each->iv_vht_flags &= ~IEEE80211_VHTCAP_C_RX_LDPC; |
| ic->ic_vhtcap.cap_flags &= ~IEEE80211_VHTCAP_C_RX_LDPC; |
| ic->ic_vhtcap_24g.cap_flags &= ~IEEE80211_VHTCAP_C_RX_LDPC; |
| } |
| if ((vap_each->iv_opmode == IEEE80211_M_HOSTAP) && |
| (vap_each->iv_state == IEEE80211_S_RUN)) { |
| ic->ic_beacon_update(vap_each); |
| } |
| /* Force a reassociation to use the new LDPC setting */ |
| ieee80211_wireless_reassoc(vap_each, 0, 1); |
| } |
| break; |
| case IEEE80211_PARAM_STBC: |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| |
| /* Update the HT capabilities */ |
| TAILQ_FOREACH_SAFE(vap_each, &ic->ic_vaps, iv_next, vap_tmp) { |
| if (value) { |
| vap_each->iv_ht_flags |= IEEE80211_HTF_STBC_ENABLED; |
| vap_each->iv_vht_flags |= IEEE80211_VHTCAP_C_TX_STBC; |
| } else { |
| vap_each->iv_ht_flags &= ~IEEE80211_HTF_STBC_ENABLED; |
| vap_each->iv_vht_flags &= ~IEEE80211_VHTCAP_C_TX_STBC; |
| } |
| if ((vap_each->iv_opmode == IEEE80211_M_HOSTAP) && |
| (vap_each->iv_state == IEEE80211_S_RUN)) { |
| ic->ic_beacon_update(vap_each); |
| } |
| /* Force a reassociation to use the new STBC setting */ |
| ieee80211_wireless_reassoc(vap_each, 0, 1); |
| } |
| break; |
| case IEEE80211_PARAM_LDPC_ALLOW_NON_QTN: |
| if (value) { |
| vap->iv_ht_flags |= IEEE80211_HTF_LDPC_ALLOW_NON_QTN; |
| } else { |
| vap->iv_ht_flags &= ~IEEE80211_HTF_LDPC_ALLOW_NON_QTN; |
| } |
| /* Force a reassociation to use the new LDPC setting */ |
| ieee80211_wireless_reassoc(vap, 0, 1); |
| break; |
| case IEEE80211_PARAM_TRAINING_COUNT: |
| vap->iv_rate_training_count = value; |
| break; |
| case IEEE80211_PARAM_FIXED_TX_RATE: |
| { |
| int mcs_val, mcs_nss; |
| |
| if ((value & IEEE80211_RATE_PREFIX_MASK) == IEEE80211_AC_RATE_PREFIX) { |
| if (!IS_IEEE80211_VHT_ENABLED(ic)) |
| return -EINVAL; |
| |
| mcs_val = value & IEEE80211_AC_MCS_VAL_MASK; |
| mcs_nss = (value & IEEE80211_AC_MCS_NSS_MASK) >> IEEE80211_11AC_MCS_NSS_SHIFT; |
| |
| if (!ieee80211_vht_tx_mcs_is_valid(mcs_val, mcs_nss)) { |
| return -EINVAL; |
| } |
| } else if ((value & IEEE80211_RATE_PREFIX_MASK) == IEEE80211_N_RATE_PREFIX) { |
| mcs_val = (value & 0xFF) & 0x7f; |
| if (!ieee80211_ht_tx_mcs_is_valid(mcs_val)) { |
| return -EINVAL; |
| } |
| } |
| printk("Warning: %s MCS rate is fixed at 0x%08x\n", __func__, value); |
| /* Forward fixed MCS rate configuration to the driver and MuC */ |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| /* Remember current configuration in the VAP */ |
| vap->iv_mcs_config = value; |
| } |
| break; |
| case IEEE80211_PARAM_SHORT_GI: |
| /* Forward short GI configuration to the driver and MuC */ |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| |
| TAILQ_FOREACH_SAFE(vap_each, &ic->ic_vaps, iv_next, vap_tmp) { |
| if (value) { |
| vap_each->iv_ht_flags |= IEEE80211_HTF_SHORTGI_ENABLED; |
| vap_each->iv_vht_flags |= IEEE80211_VHTCAP_C_SHORT_GI_80; |
| } else { |
| vap_each->iv_ht_flags &= ~IEEE80211_HTF_SHORTGI_ENABLED; |
| vap_each->iv_vht_flags &= ~IEEE80211_VHTCAP_C_SHORT_GI_80; |
| } |
| |
| if ((vap_each->iv_opmode == IEEE80211_M_HOSTAP) && |
| (vap_each->iv_state == IEEE80211_S_RUN)) { |
| ic->ic_beacon_update(vap_each); |
| } |
| /* Force a reassociation to use the new SGI setting */ |
| ieee80211_wireless_reassoc(vap_each, 0, 1); |
| } |
| break; |
| case IEEE80211_PARAM_BW_SEL_MUC: |
| case IEEE80211_PARAM_BW_SEL: |
| if ((value != BW_HT160) && (value != BW_HT80) && |
| (value != BW_HT40) && (value != BW_HT20)) |
| return -EINVAL; |
| |
| /* update station profile */ |
| if ((ic->ic_rf_chipid == CHIPID_DUAL) && |
| (ic->ic_opmode == IEEE80211_M_STA)) { |
| if (IS_IEEE80211_5G_BAND(ic)) |
| vap->iv_5ghz_prof.bw = value; |
| else |
| vap->iv_2_4ghz_prof.bw = value; |
| } |
| |
| if ((value > BW_HT40) && |
| !ieee80211_swfeat_is_supported(SWFEAT_ID_VHT, 1)) { |
| retv = EOPNOTSUPP; |
| break; |
| } |
| /* |
| * Blocking 40MHZ and 80MHZ in 2.4 band and legacy 11A |
| * only mode |
| */ |
| if ((value >= BW_HT40) && ((ic->ic_curmode == IEEE80211_MODE_11A) || |
| (!IS_IEEE80211_11NG(ic) && IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)))) |
| return -EOPNOTSUPP; |
| |
| ic->ic_max_system_bw = value; |
| |
| /* Forward bandwidth configuration to the driver and MuC */ |
| ieee80211_change_bw(vap, value, 1); |
| |
| ieee80211_start_obss_scan_timer(vap); |
| ic->ic_csw_reason = IEEE80211_CSW_REASON_CONFIG; |
| if (IS_UP_AUTO(vap)) { |
| ieee80211_wireless_reassoc(vap, 0, 1); |
| ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); |
| } else if (vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| ic->ic_set_channel(ic); |
| } |
| break; |
| case IEEE80211_PARAM_PHY_STATS_MODE: |
| if (value != MUC_PHY_STATS_ALTERNATE && |
| value != MUC_PHY_STATS_RSSI_RCPI_ONLY && |
| value != MUC_PHY_STATS_ERROR_SUM_ONLY) { |
| retv = EINVAL; |
| } else { |
| /* Forward Phy Stats configuration to the driver and MuC */ |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| ic->ic_mode_get_phy_stats = value; |
| } |
| break; |
| case IEEE80211_PARAM_FORCE_SMPS: |
| /* Reflect the forced value in the local structures */ |
| ieee80211_forcesmps(vap, value); |
| break; |
| case IEEE80211_PARAM_CHANNEL_NOSCAN: |
| g_channel_fixed = value; |
| break; |
| case IEEE80211_PARAM_LINK_LOSS: |
| vap->iv_link_loss_enabled = value; |
| break; |
| case IEEE80211_PARAM_BCN_MISS_THR: |
| if (!vap->iv_bss) |
| return -EFAULT; |
| |
| if (vap->iv_opmode == IEEE80211_M_STA) { |
| if (value < 1) { |
| printk(KERN_ERR "%s: bad value %d, " |
| "beacon miss threshold must be >= 1\n", |
| dev->name, value); |
| break; |
| } |
| printk(KERN_INFO "%s: set beacon miss threshold to %d\n", |
| dev->name, value); |
| vap->iv_bcn_miss_thr = value; |
| |
| /* Recalculate swbmiss period with new value */ |
| vap->iv_swbmiss_period = |
| IEEE80211_TU_TO_JIFFIES(vap->iv_bss->ni_intval * |
| vap->iv_bcn_miss_thr); |
| |
| if (vap->iv_swbmiss_warnings) |
| vap->iv_swbmiss_period /= (vap->iv_swbmiss_warnings + 1); |
| } else { |
| printk(KERN_ERR "%s: can't set beacon miss threshold for non STA\n", |
| dev->name); |
| } |
| break; |
| case IEEE80211_PARAM_IMPLICITBA: |
| if (vap->iv_implicit_ba != (value & 0xFFFF)) |
| { |
| printk("New implicit BA value (%04X) - remove all associations\n", (value & 0xFFFF)); |
| vap->iv_implicit_ba = value & 0xFFFF; |
| ieee80211_wireless_reassoc(vap, 0, 1); |
| } |
| break; |
| case IEEE80211_PARAM_SHOWMEM: |
| if (value == 0x1) { |
| #ifdef WLAN_MALLOC_FREE_TOT_DEBUG |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) |
| uint32_t refcnt = atomic_read(&vap->iv_dev->refcnt); |
| #else |
| uint32_t refcnt = netdev_refcnt_read(vap->iv_dev); |
| #endif |
| |
| printk("WLAN bytes: allocated=%d freed=%d balance=%d\n" |
| " times: allocated=%d freed=%d\n" |
| " nodes: allocated=%d freed=%d current=%d refs=%u\n" |
| " tmp: allocated=%d freed=%d\n", |
| g_wlan_tot_alloc, g_wlan_tot_free, g_wlan_balance, |
| g_wlan_tot_alloc_cnt, g_wlan_tot_free_cnt, |
| g_wlan_tot_node_alloc, g_wlan_tot_node_free, ic->ic_node_count, |
| refcnt, |
| g_wlan_tot_node_alloc_tmp, g_wlan_tot_node_free_tmp); |
| #else |
| printk("Memory debug statistics disabled\n"); |
| #endif |
| } |
| break; |
| case IEEE80211_PARAM_RX_AMSDU_ENABLE: |
| vap->iv_rx_amsdu_enable = value; |
| ieee80211_wireless_reassoc(vap, 1, 0); |
| break; |
| case IEEE80211_PARAM_RX_AMSDU_THRESHOLD_CCA: |
| vap->iv_rx_amsdu_threshold_cca = value; |
| break; |
| case IEEE80211_PARAM_RX_AMSDU_THRESHOLD_PMBL: |
| vap->iv_rx_amsdu_threshold_pmbl = value; |
| break; |
| case IEEE80211_PARAM_RX_AMSDU_PMBL_WF_SP: |
| vap->iv_rx_amsdu_pmbl_wf_sp = value; |
| break; |
| case IEEE80211_PARAM_RX_AMSDU_PMBL_WF_LP: |
| vap->iv_rx_amsdu_pmbl_wf_lp = value; |
| break; |
| case IEEE80211_PARAM_CLIENT_REMOVE: |
| printk("Removing clients, not forcing deauth\n"); |
| ieee80211_wireless_reassoc(vap, 1, 0); |
| break; |
| case IEEE80211_PARAM_VAP_DBG: |
| vap->iv_debug = value; |
| break; |
| case IEEE80211_PARAM_NODEREF_DBG: |
| ieee80211_node_dbgref_history_dump(); |
| break; |
| case IEEE80211_PARAM_GLOBAL_BA_CONTROL: |
| if (vap->iv_ba_control != (value & 0xFFFF)) |
| { |
| vap->iv_ba_old_control = vap->iv_ba_control; |
| vap->iv_ba_control = value & 0xFFFF; |
| ieee80211_wireless_ba_change(vap); |
| } |
| break; |
| case IEEE80211_PARAM_NO_SSID_ASSOC: |
| if (value) { |
| vap->iv_qtn_options &= ~IEEE80211_QTN_NO_SSID_ASSOC_DISABLED; |
| printk("Enabling associations with no SSID\n"); |
| } else { |
| vap->iv_qtn_options |= IEEE80211_QTN_NO_SSID_ASSOC_DISABLED; |
| printk("Disabling associations with no SSID\n"); |
| } |
| break; |
| case IEEE80211_PARAM_CONFIG_TXPOWER: |
| retv = apply_tx_power(vap, value, IEEE80211_BKUP_TXPOWER_NORMAL); |
| if (retv) { |
| retv = EINVAL; |
| } |
| break; |
| case IEEE80211_PARAM_INITIATE_TXPOWER_TABLE: |
| retv = apply_tx_power(vap, value, IEEE80211_INIT_TXPOWER_TABLE); |
| if (retv) { |
| retv = EINVAL; |
| } |
| break; |
| case IEEE80211_PARAM_CONFIG_BW_TXPOWER: |
| retv = ieee80211_set_bw_txpower(vap, value); |
| if (!retv) { |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| } else { |
| retv = EINVAL; |
| } |
| break; |
| case IEEE80211_PARAM_DUMP_CONFIG_TXPOWER: |
| if (value == 1) { |
| ieee80211_dump_tx_power(ic); |
| } else { |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| } |
| break; |
| case IEEE80211_PARAM_TPC: |
| if (ic->ic_flags & IEEE80211_F_DOTH) { |
| int last_tpc_state; |
| struct ieee80211vap *each_vap; |
| |
| value = !!value; |
| last_tpc_state = !!(ic->ic_flags_ext & IEEE80211_FEXT_TPC); |
| if (value != last_tpc_state) { |
| if (value) { |
| printk("Enable tpc feature\n"); |
| ic->ic_flags_ext |= IEEE80211_FEXT_TPC; |
| ic->ic_pppc_select_enable_backup = ic->ic_pppc_select_enable; |
| if (ic->ic_pppc_select_enable) { |
| ieee80211_param_to_qdrv(vap, IEEE80211_PARAM_PPPC_SELECT, 0, NULL, 0); |
| ic->ic_pppc_select_enable = 0; |
| } |
| } else { |
| printk("Disable tpc feature\n"); |
| ic->ic_flags_ext &= ~IEEE80211_FEXT_TPC; |
| ieee80211_tpc_query_stop(&ic->ic_tpc_query_info); |
| ic->ic_pppc_select_enable = ic->ic_pppc_select_enable_backup; |
| if (ic->ic_pppc_select_enable) |
| ieee80211_param_to_qdrv(vap, IEEE80211_PARAM_PPPC_SELECT, 1, NULL, 0); |
| } |
| TAILQ_FOREACH(each_vap, &ic->ic_vaps, iv_next) { |
| if ((each_vap->iv_opmode == IEEE80211_M_HOSTAP) && (each_vap->iv_state == IEEE80211_S_RUN)) { |
| if (!value) { |
| ieee80211_ppqueue_remove_with_cat_action(&each_vap->iv_ppqueue, |
| IEEE80211_ACTION_CAT_SPEC_MGMT, |
| IEEE80211_ACTION_S_TPC_REPORT); |
| } |
| ic->ic_beacon_update(vap); |
| } |
| } |
| } |
| } else { |
| retv = EOPNOTSUPP; |
| } |
| break; |
| case IEEE80211_PARAM_CONFIG_TPC_INTERVAL: |
| if ((ic->ic_flags & IEEE80211_F_DOTH) && (ic->ic_flags_ext & IEEE80211_FEXT_TPC)) |
| retv = ieee80211_tpc_query_config_interval(&ic->ic_tpc_query_info, value); |
| else |
| retv = EOPNOTSUPP; |
| break; |
| case IEEE80211_PARAM_TPC_QUERY: |
| if ((ic->ic_flags & IEEE80211_F_DOTH) && (ic->ic_flags_ext & IEEE80211_FEXT_TPC)) { |
| if (value == 0) { |
| ieee80211_tpc_query_stop(&ic->ic_tpc_query_info); |
| } else { |
| ieee80211_tpc_query_start(&ic->ic_tpc_query_info); |
| } |
| } |
| else |
| retv = EOPNOTSUPP; |
| break; |
| case IEEE80211_PARAM_CONFIG_REGULATORY_TXPOWER: |
| retv = set_regulatory_tx_power(ic, value); |
| break; |
| case IEEE80211_PARAM_SKB_LIST_MAX: |
| { |
| struct qtn_skb_recycle_list *recycle_list = qtn_get_shared_recycle_list(); |
| recycle_list->max = (int)value; |
| } |
| break; |
| case IEEE80211_PARAM_DFS_FAST_SWITCH: |
| if (value) { |
| retv = ieee80211_ioctl_set_dfs_fast_switch(ic); |
| } else { |
| ic->ic_flags_ext &= ~IEEE80211_FEXT_DFS_FAST_SWITCH; |
| } |
| break; |
| case IEEE80211_PARAM_SCAN_NO_DFS: |
| if (value) { |
| ic->ic_flags_ext |= IEEE80211_FEXT_SCAN_NO_DFS; |
| } else { |
| ic->ic_flags_ext &= ~IEEE80211_FEXT_SCAN_NO_DFS; |
| } |
| break; |
| case IEEE80211_PARAM_11N_40_ONLY_MODE: |
| if (value && !(ic->ic_htcap.cap & IEEE80211_HTCAP_C_CHWIDTH40)) { |
| retv = EINVAL; |
| break; |
| } |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| if ((vap->iv_opmode == IEEE80211_M_HOSTAP) && |
| (vap->iv_state == IEEE80211_S_RUN)) { |
| ic->ic_beacon_update(vap); |
| } |
| break; |
| case IEEE80211_PARAM_REGULATORY_REGION: |
| { |
| u_int16_t iso_code = CTRY_DEFAULT; |
| union { |
| char as_chars[ 4 ]; |
| u_int32_t as_u32; |
| } region; |
| |
| region.as_u32 = (u_int32_t) value; |
| region.as_chars[ 3 ] = '\0'; |
| |
| retv = ieee80211_country_string_to_countryid( region.as_chars, &iso_code ); |
| if (retv == 0) { |
| ic->ic_country_code = iso_code; |
| ic->ic_mark_dfs_channels(ic, ic->ic_nchans, ic->ic_channels); |
| ic->ic_mark_weather_radar_chans(ic, ic->ic_nchans, ic->ic_channels); |
| retv = ieee80211_region_to_operating_class(ic, region.as_chars); |
| if (retv < 0) |
| vap->iv_flags_ext |= IEEE80211_FEXT_TDLS_CS_PROHIB; |
| } |
| } |
| break; |
| case IEEE80211_PARAM_SAMPLE_RATE: |
| ic->ic_sample_rate = value; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| break; |
| case IEEE80211_PARAM_BA_MAX_WIN_SIZE: |
| if (value < IEEE80211_MAX_BA_WINSIZE) { |
| vap->iv_max_ba_win_size = value; |
| } else { |
| vap->iv_max_ba_win_size = IEEE80211_MAX_BA_WINSIZE; |
| } |
| ieee80211_wireless_reassoc(vap, 0, 0); |
| break; |
| #ifdef QSCS_ENABLED |
| case IEEE80211_PARAM_SCS: |
| if (!ieee80211_should_disable_scs(ic)) |
| return -EOPNOTSUPP; |
| |
| if (ieee80211_param_scs_set(dev, vap, value) == 0) { |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| ic->ic_vopt.scs = value & IEEE80211_SCS_VALUE_M; |
| } else { |
| retv = EINVAL; |
| } |
| break; |
| #endif /* QSCS_ENABLED */ |
| case IEEE80211_PARAM_MIN_DWELL_TIME_ACTIVE: |
| ic->ic_mindwell_active = (u_int16_t) value; |
| break; |
| case IEEE80211_PARAM_MIN_DWELL_TIME_PASSIVE: |
| ic->ic_mindwell_passive = (u_int16_t) value; |
| break; |
| case IEEE80211_PARAM_MAX_DWELL_TIME_ACTIVE: |
| ic->ic_maxdwell_active = (u_int16_t) value; |
| break; |
| case IEEE80211_PARAM_MAX_DWELL_TIME_PASSIVE: |
| ic->ic_maxdwell_passive = (u_int16_t) value; |
| break; |
| #ifdef QTN_BG_SCAN |
| case IEEE80211_PARAM_QTN_BGSCAN_DWELL_TIME_ACTIVE: |
| if (value) { |
| ic->ic_qtn_bgscan.dwell_msecs_active = (u_int16_t) value; |
| } |
| break; |
| case IEEE80211_PARAM_QTN_BGSCAN_DWELL_TIME_PASSIVE: |
| if (value) { |
| ic->ic_qtn_bgscan.dwell_msecs_passive = (u_int16_t) value; |
| } |
| break; |
| case IEEE80211_PARAM_QTN_BGSCAN_DURATION_ACTIVE: |
| if (value) { |
| ic->ic_qtn_bgscan.duration_msecs_active = (u_int16_t) value; |
| } |
| break; |
| case IEEE80211_PARAM_QTN_BGSCAN_DURATION_PASSIVE_FAST: |
| if (value) { |
| ic->ic_qtn_bgscan.duration_msecs_passive_fast = (u_int16_t) value; |
| } |
| break; |
| case IEEE80211_PARAM_QTN_BGSCAN_DURATION_PASSIVE_NORMAL: |
| if (value) { |
| ic->ic_qtn_bgscan.duration_msecs_passive_normal = (u_int16_t) value; |
| } |
| break; |
| case IEEE80211_PARAM_QTN_BGSCAN_DURATION_PASSIVE_SLOW: |
| if (value) { |
| ic->ic_qtn_bgscan.duration_msecs_passive_slow = (u_int16_t) value; |
| } |
| break; |
| case IEEE80211_PARAM_QTN_BGSCAN_THRSHLD_PASSIVE_FAST: |
| ic->ic_qtn_bgscan.thrshld_fat_passive_fast = (u_int16_t) value; |
| break; |
| case IEEE80211_PARAM_QTN_BGSCAN_THRSHLD_PASSIVE_NORMAL: |
| ic->ic_qtn_bgscan.thrshld_fat_passive_normal = (u_int16_t) value; |
| break; |
| case IEEE80211_PARAM_QTN_BGSCAN_DEBUG: |
| ic->ic_qtn_bgscan.debug_flags = (u_int16_t) value; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| break; |
| #endif /*QTN_BG_SCAN */ |
| case IEEE80211_PARAM_QTN_BCM_WAR: |
| if (value) |
| ic->ic_flags_qtn |= IEEE80211_QTN_BCM_WAR; |
| else |
| ic->ic_flags_qtn &= ~IEEE80211_QTN_BCM_WAR; |
| break; |
| case IEEE80211_PARAM_ALT_CHAN: |
| retv = ieee80211_ioctl_set_alt_chan(ic, (uint8_t) value); |
| break; |
| case IEEE80211_PARAM_GI_SELECT: |
| ic->ic_gi_select_enable = value; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| break; |
| case IEEE80211_PARAM_RADAR_NONOCCUPY_PERIOD: |
| if ((value >= IEEE80211_MIN_NON_OCCUPANCY_PERIOD) && |
| (value <= IEEE80211_MAX_NON_OCCUPANCY_PERIOD)) { |
| ic->ic_non_occupancy_period = value * HZ; |
| } else { |
| retv = EINVAL; |
| } |
| break; |
| case IEEE80211_PARAM_MC_LEGACY_RATE: |
| { |
| u_int8_t mc_rate = 0; |
| static const char *idx2lr[] = {"6", "9", "12", "18", "24", "36", "48", "54", "1", "2", "5.5", "11"}; |
| int i; |
| for (i = 0; i < 4; i++) { |
| u_int8_t rate = (value >> i*8) & 0xFF; |
| if (rate > 11) { |
| retv = EINVAL; |
| break; |
| } |
| } |
| if (retv != EINVAL) { |
| mc_rate = (value >> 24) & 0xFF; |
| printk("Forcing multicast rate to %sMbps\n", idx2lr[mc_rate]); |
| vap->iv_mc_legacy_rate = value; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| ic->ic_beacon_update(vap); |
| } |
| } |
| } |
| break; |
| case IEEE80211_PARAM_RADAR_NONOCCUPY_ACT_SCAN: |
| if (value) |
| ic->ic_flags_qtn |= IEEE80211_QTN_RADAR_SCAN_START; |
| else |
| ic->ic_flags_qtn &= ~IEEE80211_QTN_RADAR_SCAN_START; |
| break; |
| case IEEE80211_PARAM_FWD_UNKNOWN_MC: |
| vap->iv_forward_unknown_mc = !!value; |
| break; |
| case IEEE80211_PARAM_MC_TO_UC: |
| if (value < IEEE80211_QTN_MC_TO_UC_LEGACY || value > IEEE80211_QTN_MC_TO_UC_ALWAYS) |
| return -EINVAL; |
| vap->iv_mc_to_uc = value; |
| break; |
| case IEEE80211_PARAM_ENABLE_BC_IOT_WAR: |
| if (value < 0) |
| return -EINVAL; |
| vap->enable_iot_sts_war = value; |
| break; |
| case IEEE80211_PARAM_BCST_4: |
| vap->iv_reliable_bcst = !!value; |
| break; |
| case IEEE80211_PARAM_AP_FWD_LNCB: |
| vap->iv_ap_fwd_lncb = !!value; |
| break; |
| case IEEE80211_PARAM_PPPC_SELECT: |
| if ((ic->ic_flags & IEEE80211_F_DOTH) && (ic->ic_flags_ext & IEEE80211_FEXT_TPC)) { |
| retv = EOPNOTSUPP; |
| } else { |
| ic->ic_pppc_select_enable = value; |
| ic->ic_vopt.pppc = value; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| } |
| break; |
| case IEEE80211_PARAM_PPPC_STEP: |
| if ((ic->ic_flags & IEEE80211_F_DOTH) && (ic->ic_flags_ext & IEEE80211_FEXT_TPC)) { |
| retv = EOPNOTSUPP; |
| } else { |
| if ((value >= QTN_SEL_PPPC_MAX_STEPS) || (value < 0)) { |
| return -EINVAL; |
| } |
| ic->ic_pppc_step_db = value; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| ieee80211_wireless_reassoc(vap, 0, 1); |
| } |
| break; |
| case IEEE80211_PARAM_EMI_POWER_SWITCHING: |
| ic->ic_emi_power_switch_enable = value; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| break; |
| case IEEE80211_PARAM_ASSOC_LIMIT: |
| retv = ieee80211_ioctl_set_assoc_limit(ic, vap, value); |
| break; |
| case IEEE80211_PARAM_BSS_ASSOC_LIMIT: |
| retv = ieee80211_ioctl_set_bss_grp_assoc_limit(ic, vap, value); |
| break; |
| case IEEE80211_PARAM_ASSOC_HISTORY: |
| { |
| int i; |
| |
| memset(&ic->ic_assoc_history.ah_macaddr_table[0][0], |
| 0, sizeof(ic->ic_assoc_history.ah_macaddr_table)); |
| for(i = 0; i < IEEE80211_MAX_ASSOC_HISTORY; i++) { |
| ic->ic_assoc_history.ah_timestamp[i] = 0; |
| } |
| } |
| break; |
| case IEEE80211_PARAM_CSW_RECORD: |
| memset(&ic->ic_csw_record, 0, sizeof(ic->ic_csw_record)); |
| break; |
| case IEEE80211_PARAM_IOT_TWEAKS: |
| qtn_mproc_sync_shared_params_get()->iot_tweaks = value; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| break; |
| case IEEE80211_PARAM_FAST_REASSOC: |
| if (value) { |
| ic->ic_flags_ext |= IEEE80211_FEXT_SCAN_FAST_REASS; |
| } else { |
| ic->ic_flags_ext &= ~IEEE80211_FEXT_SCAN_FAST_REASS; |
| } |
| break; |
| case IEEE80211_PARAM_CSA_FLAG: |
| ic->ic_csa_flag = value; |
| break; |
| case IEEE80211_PARAM_DEF_MATRIX: |
| ic->ic_def_matrix = value; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| ieee80211_wireless_reassoc(vap, 0, 1); |
| break; |
| case IEEE80211_PARAM_MODE: |
| if (ic->fixed_legacy_rate_mode) |
| return -EOPNOTSUPP; |
| |
| /* update 5ghz station profile */ |
| if ((ic->ic_rf_chipid == CHIPID_DUAL) && |
| (ic->ic_opmode == IEEE80211_M_STA)) |
| vap->iv_5ghz_prof.vht = value; |
| |
| if (chip_id() >= QTN_BBIC_11AC) { |
| ic->ic_csw_reason = IEEE80211_CSW_REASON_CONFIG; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| ieee80211_wireless_reassoc(vap, 0, 1); |
| |
| /* On AP, change channel to take new bw into effect by BB */ |
| /* On STA, reassociation would result into re-scannning so not required */ |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| ic->ic_set_channel(ic); |
| } |
| } else { |
| retv = EINVAL; |
| } |
| break; |
| case IEEE80211_PARAM_ENABLE_11AC: |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| vap->iv_11ac_enabled = value; |
| break; |
| case IEEE80211_PARAM_FIXED_11AC_MU_TX_RATE: |
| retv = ieee80211_wireless_set_mu_tx_rate(vap, ic, value); |
| break; |
| case IEEE80211_PARAM_FIXED_11AC_TX_RATE: |
| if (IS_IEEE80211_DUALBAND_VHT_ENABLED(ic)) { |
| int mcs_to_muc = ieee80211_11ac_mcs_format(value, 40); |
| if (mcs_to_muc < 0) { |
| retv = EINVAL; |
| break; |
| } |
| /* Forward fixed MCS rate configuration to the driver and MuC */ |
| ieee80211_param_to_qdrv(vap, param, mcs_to_muc, NULL, 0); |
| /* Remember current configuration in the VAP */ |
| vap->iv_mcs_config = (value & 0x0FFFFFF) | IEEE80211_AC_RATE_PREFIX; |
| } else { |
| retv = EINVAL; |
| } |
| |
| break; |
| case IEEE80211_PARAM_VAP_PRI: |
| retv = ieee80211_ioctl_set_vap_pri(ic, vap, value); |
| break; |
| case IEEE80211_PARAM_AIRFAIR: |
| if (value <= 1) { |
| ic->ic_airfair = value; |
| ic->ic_vopt.airfair = value; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| } else { |
| retv = EINVAL; |
| } |
| break; |
| case IEEE80211_PARAM_VAP_PRI_WME: |
| printk("Change auto WME param based on VAP priority from %u to %u\n", |
| ic->ic_vap_pri_wme, value); |
| ic->ic_vap_pri_wme = value; |
| IEEE80211_LOCK_IRQ(ic); |
| ieee80211_adjust_wme_by_vappri(ic); |
| IEEE80211_UNLOCK_IRQ(ic); |
| break; |
| case IEEE80211_PARAM_TDLS_DISC_INT: |
| if (ieee80211_tdls_cfg_disc_int(vap, value) != 0) { |
| retv = EINVAL; |
| } |
| break; |
| case IEEE80211_PARAM_TDLS_PATH_SEL_WEIGHT: |
| vap->tdls_path_sel_weight = value; |
| break; |
| case IEEE80211_PARAM_TDLS_MODE: |
| vap->tdls_path_sel_prohibited = !!value; |
| if (vap->tdls_path_sel_prohibited == 1) |
| ieee80211_tdls_free_peer_ps_info(vap); |
| break; |
| case IEEE80211_PARAM_TDLS_TIMEOUT_TIME: |
| vap->tdls_timeout_time = value; |
| if ((vap->iv_flags_ext & IEEE80211_FEXT_TDLS_DISABLED) == 0) |
| ieee80211_tdls_update_link_timeout(vap); |
| break; |
| case IEEE80211_PARAM_TDLS_TRAINING_PKT_CNT: |
| vap->tdls_training_pkt_cnt = value; |
| break; |
| case IEEE80211_PARAM_TDLS_PATH_SEL_PPS_THRSHLD: |
| vap->tdls_path_sel_pps_thrshld = value; |
| break; |
| case IEEE80211_PARAM_TDLS_PATH_SEL_RATE_THRSHLD: |
| vap->tdls_path_sel_rate_thrshld = value; |
| break; |
| case IEEE80211_PARAM_TDLS_VERBOSE: |
| if (!vap->iv_bss) |
| return -EINVAL; |
| vap->tdls_verbose = value; |
| ic->ic_set_tdls_param(vap->iv_bss, IOCTL_TDLS_DBG_LEVEL, value); |
| break; |
| case IEEE80211_PARAM_TDLS_MIN_RSSI: |
| vap->tdls_min_valid_rssi = value; |
| break; |
| case IEEE80211_PARAM_TDLS_UAPSD_INDICAT_WND: |
| if (vap->tdls_uapsd_indicat_wnd != value) { |
| vap->tdls_uapsd_indicat_wnd = value; |
| ieee80211_tdls_update_uapsd_indicication_windows(vap); |
| } |
| break; |
| case IEEE80211_PARAM_TDLS_SWITCH_INTS: |
| vap->tdls_switch_ints = value; |
| break; |
| case IEEE80211_PARAM_TDLS_RATE_WEIGHT: |
| vap->tdls_phy_rate_wgt = value; |
| break; |
| case IEEE80211_PARAM_TDLS_CS_MODE: |
| if (value == 2) { |
| vap->iv_flags_ext |= IEEE80211_FEXT_TDLS_CS_PASSIVE; |
| vap->iv_flags_ext &= ~IEEE80211_FEXT_TDLS_CS_PROHIB; |
| } else if (value == 1){ |
| vap->iv_flags_ext |= IEEE80211_FEXT_TDLS_CS_PROHIB; |
| vap->iv_flags_ext &= ~IEEE80211_FEXT_TDLS_CS_PASSIVE; |
| } else { |
| vap->iv_flags_ext &= ~IEEE80211_FEXT_TDLS_CS_PROHIB; |
| vap->iv_flags_ext &= ~IEEE80211_FEXT_TDLS_CS_PASSIVE; |
| } |
| break; |
| case IEEE80211_PARAM_TDLS_OFF_CHAN: |
| vap->tdls_fixed_off_chan = value; |
| break; |
| case IEEE80211_PARAM_TDLS_OFF_CHAN_BW: |
| if ((value == BW_INVALID) || (value == BW_HT20) || |
| (value == BW_HT40) || (value == BW_HT80) || |
| (value == BW_HT160)) |
| vap->tdls_fixed_off_chan_bw = value; |
| else |
| retv = EINVAL; |
| break; |
| case IEEE80211_PARAM_TDLS_NODE_LIFE_CYCLE: |
| vap->tdls_node_life_cycle = value; |
| if ((vap->iv_flags_ext & IEEE80211_FEXT_TDLS_DISABLED) == 0) |
| ieee80211_tdls_start_node_expire_timer(vap); |
| break; |
| case IEEE80211_PARAM_TDLS_OVER_QHOP_ENABLE: |
| vap->tdls_over_qhop_en = value; |
| break; |
| case IEEE80211_PARAM_OCAC: |
| case IEEE80211_PARAM_SDFS: |
| if (ieee80211_param_ocac_set(dev, vap, value) == 0) { |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| ic->ic_vopt.ocac = value; |
| } else { |
| retv = EINVAL; |
| } |
| break; |
| case IEEE80211_PARAM_DEACTIVE_CHAN_PRI: |
| retv = ieee80211_ioctl_setchan_inactive_pri(ic, vap, value); |
| break; |
| case IEEE80211_PARAM_SPECIFIC_SCAN: |
| if (!!value) |
| vap->iv_flags_ext |= IEEE80211_FEXT_SPECIFIC_SCAN; |
| else |
| vap->iv_flags_ext &= ~IEEE80211_FEXT_SPECIFIC_SCAN; |
| break; |
| case IEEE80211_PARAM_SCAN_TBL_LEN_MAX: |
| if (value != ic->ic_scan_tbl_len_max) { |
| ic->ic_scan_tbl_len_max = value; |
| ieee80211_scan_flush(ic); |
| } |
| break; |
| case IEEE80211_PARAM_TRAINING_START: |
| ieee80211_training_restart_by_node_idx(vap, value); |
| break; |
| case IEEE80211_PARAM_SPEC_COUNTRY_CODE: |
| { |
| uint16_t iso_code = CTRY_DEFAULT; |
| union { |
| char as_chars[4]; |
| uint32_t as_u32; |
| } region; |
| |
| region.as_u32 = (uint32_t)value; |
| region.as_chars[3] = '\0'; |
| |
| retv = ieee80211_country_string_to_countryid(region.as_chars, &iso_code); |
| if (retv == 0) { |
| ic->ic_spec_country_code = iso_code; |
| ieee80211_build_countryie(ic); |
| } else { |
| retv = -retv; |
| } |
| } |
| break; |
| case IEEE80211_PARAM_VCO_LOCK_DETECT_MODE: |
| sp->vco_lock_detect_mode = value; |
| break; |
| case IEEE80211_PARAM_CONFIG_PMF: |
| /* enable/disable PMF per VAP */ |
| if((value == IEEE80211_MFP_NO_PROTECT) || |
| (value == IEEE80211_MFP_PROTECT_CAPABLE) || |
| (value == IEEE80211_MFP_PROTECT_REQUIRE)) { |
| vap->iv_pmf = value; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| } else { |
| retv = EINVAL; |
| } |
| break; |
| case IEEE80211_PARAM_SCAN_CANCEL: |
| if (value) { |
| /* Force canceling immediately */ |
| ieee80211_cancel_scan_no_wait(vap); |
| } else { |
| ieee80211_cancel_scan(vap); |
| } |
| break; |
| |
| case IEEE80211_PARAM_DSP_DEBUG_LEVEL: |
| DSP_PARAM_SET(debug_level, value); |
| break; |
| |
| case IEEE80211_PARAM_DSP_DEBUG_FLAG: |
| DSP_PARAM_SET(debug_flag, value); |
| break; |
| |
| case IEEE80211_PARAM_DSP_MU_RANK_CRITERIA: |
| DSP_PARAM_SET(rank_criteria_to_use, value); |
| break; |
| |
| case IEEE80211_PARAM_DSP_PRECODING_ALGORITHM: |
| if ( MU_ALLOWED_ALG(value) ) { |
| DSP_PARAM_SET(precoding_algorithm_to_use, value); |
| } else { |
| return -EINVAL; |
| } |
| break; |
| |
| case IEEE80211_PARAM_DSP_RANKING_ALGORITHM: |
| if ( MU_ALLOWED_ALG(value) ) { |
| DSP_PARAM_SET(ranking_algorithm_to_use, value); |
| } else { |
| return -EINVAL; |
| } |
| break; |
| |
| case IEEE80211_PARAM_INTRA_BSS_ISOLATE: |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| if (value) |
| dev->qtn_flags |= QTN_FLAG_INTRA_BSS_ISOLATE; |
| else |
| dev->qtn_flags &= ~QTN_FLAG_INTRA_BSS_ISOLATE; |
| } else { |
| return -EINVAL; |
| } |
| |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| break; |
| case IEEE80211_PARAM_BSS_ISOLATE: |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| if (value) |
| dev->qtn_flags |= QTN_FLAG_BSS_ISOLATE; |
| else |
| dev->qtn_flags &= ~QTN_FLAG_BSS_ISOLATE; |
| } else { |
| return -EINVAL; |
| } |
| |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| break; |
| case IEEE80211_PARAM_BF_RX_STS: |
| if ((value > 0) && (value <= (IEEE80211_VHTCAP_RX_STS_4 + 1))) { |
| ic->ic_vhtcap.bfstscap = value - 1; |
| ic->ic_vhtcap_24g.bfstscap = value - 1; |
| TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { |
| if ((vap->iv_opmode != IEEE80211_M_HOSTAP) || (vap->iv_state != IEEE80211_S_RUN)) |
| continue; |
| ic->ic_beacon_update(vap); |
| } |
| retv = 0; |
| } else { |
| retv = -EINVAL; |
| } |
| break; |
| case IEEE80211_PARAM_PC_OVERRIDE: |
| { |
| uint16_t pwr_constraint = (value & 0xffff); |
| uint8_t rssi_threshold = ((value >> 16) & 0xff); |
| uint8_t sec_offset = ((value >> 24) & 0xff); |
| |
| KASSERT(ic->ic_bsschan != IEEE80211_CHAN_ANYC, ("bss channel not set")); |
| |
| /* |
| * Default mode when value = 1 |
| **/ |
| if (pwr_constraint && !rssi_threshold && !sec_offset) { |
| pwr_constraint = PWR_CONSTRAINT_PC_DEF; |
| rssi_threshold = PWR_CONSTRAINT_RSSI_DEF; |
| sec_offset = PWR_CONSTRAINT_OFFSET; |
| } |
| /* |
| * Hack for ASUS/Broadcomm 3ss client to turn down power |
| * Made sure this does not affect any other code like tpc/pppc |
| * by placing code here. |
| */ |
| if (((ic->ic_flags & IEEE80211_F_DOTH) && (ic->ic_flags_ext & IEEE80211_FEXT_COUNTRYIE)) && |
| !((ic->ic_flags & IEEE80211_F_DOTH) && (ic->ic_flags_ext & IEEE80211_FEXT_TPC))) { |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| if(value && ic->ic_pco.pco_pwr_constraint_save == PWR_CONSTRAINT_SAVE_INIT) { |
| printk("pwr-cons=%d rssi-thr=%d sec-off=%d\n", pwr_constraint, -rssi_threshold, sec_offset); |
| if(pwr_constraint < ic->ic_bsschan->ic_maxregpower) { |
| ic->ic_pco.pco_pwr_constraint_save = ic->ic_pwr_constraint; |
| ic->ic_pco.pco_rssi_threshold = rssi_threshold; |
| ic->ic_pco.pco_pwr_constraint = pwr_constraint; |
| ic->ic_pco.pco_sec_offset = sec_offset; |
| init_timer(&ic->ic_pco.pco_timer); |
| ic->ic_pco.pco_timer.function = ieee80211_pco_timer_func; |
| ic->ic_pco.pco_timer.data = (unsigned long) vap; |
| ic->ic_pco.pco_timer.expires = jiffies + (5 * HZ); |
| add_timer(&ic->ic_pco.pco_timer); |
| } else { |
| printk("power constraint(%d) >= current channel max regulatory power(%d)\n", pwr_constraint, ic->ic_bsschan->ic_maxregpower); |
| retv = EINVAL; |
| } |
| } else { |
| if (!value && ic->ic_pco.pco_pwr_constraint_save != PWR_CONSTRAINT_SAVE_INIT) { |
| printk("PCO Disabled\n"); |
| ic->ic_pco.pco_pwr_constraint = 0; |
| ic->ic_pco.pco_rssi_threshold = 0; |
| ic->ic_pco.pco_sec_offset = 0; |
| ieee80211_pco_timer_func((unsigned long)vap); |
| del_timer(&ic->ic_pco.pco_timer); |
| ic->ic_pwr_constraint = ic->ic_pco.pco_pwr_constraint_save; |
| ic->ic_pco.pco_pwr_constraint_save = PWR_CONSTRAINT_SAVE_INIT; |
| } else { |
| retv = EINVAL; |
| } |
| } |
| } else { |
| retv = EOPNOTSUPP; |
| } |
| } else { |
| printk("power constraint override needs to have TPC disabled\n"); |
| retv = EOPNOTSUPP; |
| } |
| break; |
| } |
| case IEEE80211_PARAM_WOWLAN: |
| if (ieee80211_param_wowlan_set(dev, vap, value) == 0) { |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| } else { |
| retv = EINVAL; |
| } |
| break; |
| case IEEE80211_PARAM_WDS_MODE: |
| if (ieee80211_is_repeater(ic)) { |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG, |
| "%s can't config WDS mode since it's a" |
| " repeater\n", vap->iv_dev->name); |
| retv = EPERM; |
| break; |
| } |
| #ifdef CONFIG_QVSP |
| temp_value = !!IEEE80211_VAP_WDS_IS_RBS(vap); |
| |
| ieee80211_vap_set_extdr_flags(vap, value); |
| |
| value = !!IEEE80211_VAP_WDS_IS_RBS(vap); |
| if (vap->iv_opmode == IEEE80211_M_WDS && temp_value ^ value) |
| ic->ic_vsp_change_stamode(ic, value); |
| #else |
| ieee80211_vap_set_extdr_flags(vap, value); |
| |
| #endif |
| break; |
| case IEEE80211_PARAM_EXTENDER_ROLE: |
| if (ieee80211_is_repeater(ic)) { |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG, |
| "%s can't config extender role since " |
| "it's a repeater\n", vap->iv_dev->name); |
| retv = EPERM; |
| break; |
| } |
| retv = ieee80211_ioctl_extender_role(ic, vap, value); |
| break; |
| case IEEE80211_PARAM_EXTENDER_MBS_BEST_RSSI: |
| if (ieee80211_swfeat_is_supported(SWFEAT_ID_QHOP, 1)) |
| ic->ic_extender_mbs_best_rssi = value; |
| break; |
| case IEEE80211_PARAM_EXTENDER_RBS_BEST_RSSI: |
| if (ieee80211_swfeat_is_supported(SWFEAT_ID_QHOP, 1)) |
| ic->ic_extender_rbs_best_rssi = value; |
| break; |
| case IEEE80211_PARAM_EXTENDER_MBS_WGT: |
| if (ieee80211_swfeat_is_supported(SWFEAT_ID_QHOP, 1)) |
| ic->ic_extender_mbs_wgt = value; |
| break; |
| case IEEE80211_PARAM_VAP_TX_AMSDU: |
| if (vap->iv_tx_amsdu != value) { |
| vap->iv_tx_amsdu = value; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| ieee80211_wireless_reassoc(vap, 0, 1); |
| } |
| break; |
| case IEEE80211_PARAM_TX_MAXMPDU: |
| if ((value < IEEE80211_VHTCAP_MAX_MPDU_3895) || |
| (value > IEEE80211_VHTCAP_MAX_MPDU_11454)) { |
| retv = EINVAL; |
| } else if (vap->iv_tx_max_amsdu != value) { |
| vap->iv_tx_max_amsdu = value; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| } |
| break; |
| case IEEE80211_PARAM_EXTENDER_RBS_WGT: |
| if (ieee80211_swfeat_is_supported(SWFEAT_ID_QHOP, 1)) |
| ic->ic_extender_rbs_wgt = value; |
| break; |
| case IEEE80211_PARAM_EXTENDER_VERBOSE: |
| if (ieee80211_swfeat_is_supported(SWFEAT_ID_QHOP, 1)) |
| ic->ic_extender_verbose = value; |
| break; |
| case IEEE80211_PARAM_BB_PARAM: |
| { |
| /* Channel will check bb_param and change default max gain for high band channel */ |
| sp->bb_param = value; |
| } |
| break; |
| case IEEE80211_PARAM_TQEW_DESCR_LIMIT: |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| if ((value > 0) && (value <= 100)) { |
| ic->ic_tqew_descr_limit = value; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| } else { |
| retv = EINVAL; |
| } |
| } else { |
| retv = EOPNOTSUPP; |
| } |
| break; |
| case IEEE80211_PARAM_SWFEAT_DISABLE: |
| ieee80211_ioctl_swfeat_disable(vap, param, value); |
| break; |
| case IEEE80211_PARAM_HS2: |
| if (!ieee80211_swfeat_is_supported(SWFEAT_ID_HS20, 1)) { |
| retv = EOPNOTSUPP; |
| break; |
| } |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) |
| vap->hs20_enable = value; |
| break; |
| case IEEE80211_PARAM_DGAF_CONTROL: |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| vap->disable_dgaf = !!value; |
| } else { |
| return -EINVAL; |
| } |
| break; |
| case IEEE80211_PARAM_11N_AMSDU_CTRL: |
| if (value) |
| ic->ic_flags_qtn |= QTN_NODE_11N_TXAMSDU_OFF; |
| else |
| ic->ic_flags_qtn &= ~QTN_NODE_11N_TXAMSDU_OFF; |
| break; |
| case IEEE80211_PARAM_SCAN_RESULTS_CHECK_INV : |
| if (value > 0 && value != ic->ic_scan_results_check) { |
| ic->ic_scan_results_check = value; |
| mod_timer(&ic->ic_scan_results_expire, |
| jiffies + ic->ic_scan_results_check * HZ); |
| } |
| break; |
| case IEEE80211_PARAM_FLUSH_SCAN_ENTRY: |
| if (value) |
| ieee80211_scan_flush(ic); |
| break; |
| case IEEE80211_PARAM_VHT_OPMODE_BW: |
| ieee80211_send_vht_opmode_to_all(ic, (uint8_t)value); |
| break; |
| case IEEE80211_PARAM_PROXY_ARP: |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| vap->proxy_arp = !!value; |
| } else { |
| return -EINVAL; |
| } |
| break; |
| case IEEE80211_PARAM_QTN_HAL_PM_CORRUPT_DEBUG: |
| #ifdef QTN_HAL_PM_CORRUPT_DEBUG |
| if (value) |
| sp->qtn_hal_pm_corrupt_debug = 1; |
| else |
| sp->qtn_hal_pm_corrupt_debug = 0; |
| #else |
| sp->qtn_hal_pm_corrupt_debug = 0; |
| printk("pm_corrupt_debug can't be activated when QTN_HAL_PM_CORRUPT_DEBUG " |
| "is undefined\n"); |
| #endif |
| break; |
| case IEEE80211_PARAM_L2_EXT_FILTER: |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| return ic->ic_set_l2_ext_filter(vap, value); |
| } else { |
| return -EINVAL; |
| } |
| break; |
| case IEEE80211_PARAM_L2_EXT_FILTER_PORT: |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) |
| return ic->ic_set_l2_ext_filter_port(vap, value); |
| else |
| return -EINVAL; |
| break; |
| case IEEE80211_PARAM_ENABLE_RX_OPTIM_STATS: |
| if (value >= 0) { |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| } else { |
| retv = EINVAL; |
| } |
| break; |
| case IEEE80211_PARAM_SET_UNICAST_QUEUE_NUM: |
| topaz_congest_set_unicast_queue_count(value); |
| break; |
| case IEEE80211_PARAM_MRC_ENABLE: |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| ieee80211_wireless_reassoc(vap, 0, 1); |
| break; |
| case IEEE80211_PARAM_OBSS_EXEMPT_REQ: |
| if (ic->ic_opmode == IEEE80211_M_STA) { |
| if (value) { |
| vap->iv_coex |= WLAN_20_40_BSS_COEX_OBSS_EXEMPT_REQ; |
| ieee80211_send_20_40_bss_coex(vap); |
| } else { |
| vap->iv_coex &= ~WLAN_20_40_BSS_COEX_OBSS_EXEMPT_REQ; |
| } |
| } else |
| retv = EOPNOTSUPP; |
| break; |
| case IEEE80211_PARAM_OBSS_TRIGG_SCAN_INT: |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| ic->ic_obss_ie.obss_trigger_interval = value; |
| ic->ic_beacon_update(vap); |
| } else |
| retv = EOPNOTSUPP; |
| break; |
| case IEEE80211_PARAM_PREF_BAND: |
| vap->iv_pref_band = value; |
| break; |
| case IEEE80211_PARAM_BW_2_4GHZ: |
| if ((value != BW_HT160) && (value != BW_HT80) && |
| (value != BW_HT40) && (value != BW_HT20)) |
| return -EINVAL; |
| |
| /* update 2.4ghz station profile */ |
| if ((ic->ic_rf_chipid == CHIPID_DUAL) && |
| (ic->ic_opmode == IEEE80211_M_STA)) |
| vap->iv_2_4ghz_prof.bw = value; |
| |
| /* check if phymode is also in 2.4ghz mode then set bw */ |
| if (IS_IEEE80211_24G_BAND(ic)) { |
| /* Blocking 40MHZ in 2.4 11G only mode */ |
| if ((value >= BW_HT40) && IEEE80211_IS_11G(ic)) |
| return -EOPNOTSUPP; |
| |
| ic->ic_max_system_bw = value; |
| |
| /* Forward bandwidth configuration to the driver and MuC */ |
| ieee80211_change_bw(vap, value, 1); |
| |
| ieee80211_start_obss_scan_timer(vap); |
| ic->ic_csw_reason = IEEE80211_CSW_REASON_CONFIG; |
| if (IS_UP_AUTO(vap)) { |
| ieee80211_wireless_reassoc(vap, 0, 1); |
| ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); |
| } |
| } |
| break; |
| case IEEE80211_PARAM_ALLOW_VHT_TKIP: |
| if (!IEEE80211_IS_TKIP_ALLOWED(ic)) |
| return -EOPNOTSUPP; |
| |
| TAILQ_FOREACH_SAFE(vap_each, &ic->ic_vaps, iv_next, vap_tmp) { |
| vap_each->allow_tkip_for_vht = value; |
| if ((vap_each->iv_opmode == IEEE80211_M_HOSTAP) && |
| (vap_each->iv_state == IEEE80211_S_RUN)) { |
| ic->ic_beacon_update(vap_each); |
| } |
| |
| ieee80211_wireless_reassoc(vap_each, 0, 1); |
| } |
| break; |
| case IEEE80211_PARAM_VHT_OPMODE_NOTIF: |
| ic->ic_vht_opmode_notif = (uint16_t)value; |
| TAILQ_FOREACH_SAFE(vap_each, &ic->ic_vaps, iv_next, vap_tmp) { |
| if ((vap_each->iv_opmode == IEEE80211_M_HOSTAP) && |
| (vap_each->iv_state == IEEE80211_S_RUN)) { |
| ic->ic_beacon_update(vap_each); |
| } |
| } |
| break; |
| case IEEE80211_PARAM_USE_NON_HT_DUPLICATE_MU: |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| if (ic->use_non_ht_duplicate_for_mu != value) { |
| ieee80211_wireless_reassoc(vap, 0, 1); |
| } |
| |
| ic->use_non_ht_duplicate_for_mu = value; |
| break; |
| case IEEE80211_PARAM_QTN_BLOCK_BSS: |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG, "%s block state is from %d to %d\n", |
| vap->iv_dev->name, vap->is_block_all_assoc, value); |
| |
| vap->is_block_all_assoc = !!value; |
| break; |
| case IEEE80211_PARAM_VHT_2_4GHZ: |
| if (value) |
| ic->ic_flags_ext |= IEEE80211_FEXT_24GVHT; |
| else |
| ic->ic_flags_ext &= ~IEEE80211_FEXT_24GVHT; |
| ieee80211_param_to_qdrv(vap, IEEE80211_PARAM_VHT_2_4GHZ, value, NULL, 0); |
| |
| TAILQ_FOREACH(vap_each, &ic->ic_vaps, iv_next) |
| ieee80211_wireless_reassoc(vap_each, 0, 0); |
| break; |
| case IEEE80211_PARAM_BEACONING_SCHEME: |
| value = value ? QTN_BEACONING_SCHEME_1 : QTN_BEACONING_SCHEME_0; |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP && ic->ic_beaconing_scheme != value) { |
| if (ic->ic_pm_state[QTN_PM_CURRENT_LEVEL] < BOARD_PM_LEVEL_DUTY) { |
| ieee80211_wireless_reassoc_all_vaps(ic); |
| ic->ic_pm_reason = IEEE80211_PM_LEVEL_BCN_SCHEME_CHANGED; |
| ieee80211_pm_queue_work_custom(ic, BOARD_PM_WLAN_AP_IDLE_AFTER_BEACON_SCHEME); |
| } |
| |
| if (ic->ic_set_beaconing_scheme(vap, param, value) < 0) { |
| retv = -EINVAL; |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG, "%s: Beaconing scheme " |
| "update to value [%d] failed\n", __func__, value); |
| } |
| } |
| break; |
| #if defined(QBMPS_ENABLE) |
| case IEEE80211_PARAM_STA_BMPS: |
| retv = ieee80211_wireless_set_sta_bmps(vap, ic, value); |
| break; |
| #endif |
| case IEEE80211_PARAM_40MHZ_INTOLERANT: |
| ieee80211_wireless_set_40mhz_intolerant(vap, value); |
| break; |
| case IEEE80211_PARAM_DISABLE_TX_BA: |
| vap->tx_ba_disable = value; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| printk("%s: TX Block Ack establishment disable configured to %u\n",dev->name, value); |
| ieee80211_wireless_reassoc(vap, 0, 0); |
| break; |
| case IEEE80211_PARAM_DECLINE_RX_BA: |
| vap->rx_ba_decline = value; |
| printk("%s: RX Block Ack decline configured to %u\n",dev->name, value); |
| ieee80211_wireless_reassoc(vap, 0, 0); |
| break; |
| case IEEE80211_PARAM_VAP_STATE: |
| vap->iv_vap_state = !!value; |
| break; |
| case IEEE80211_PARAM_TX_AIRTIME_CONTROL: |
| ic->ic_tx_airtime_control(vap, value); |
| break; |
| case IEEE80211_PARAM_OSEN: |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| vap->iv_osen = value; |
| if (vap->iv_flags & IEEE80211_F_WPA) |
| retv = ENETRESET; |
| } else { |
| retv = EOPNOTSUPP; |
| } |
| break; |
| case IEEE80211_PARAM_OBSS_SCAN: |
| if (ic->ic_obss_scan_enable != !!value) { |
| ic->ic_obss_scan_enable = !!value; |
| if (ic->ic_obss_scan_enable) |
| ieee80211_start_obss_scan_timer(vap); |
| else |
| del_timer_sync(&ic->ic_obss_timer); |
| } |
| break; |
| case IEEE80211_PARAM_SHORT_SLOT: |
| if (value) { |
| ic->ic_caps |= IEEE80211_C_SHSLOT; |
| ic->ic_flags |= IEEE80211_F_SHSLOT; |
| } else { |
| ic->ic_caps &= ~IEEE80211_C_SHSLOT; |
| ic->ic_flags &= ~IEEE80211_F_SHSLOT; |
| } |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| ieee80211_wireless_reassoc(vap, 0, 1); |
| break; |
| case IEEE80211_PARAM_BG_PROTECT: |
| if (ic->ic_rf_chipid == CHIPID_DUAL |
| && IS_IEEE80211_24G_BAND(ic)) { |
| if (value) { |
| ic->ic_flags_ext |= IEEE80211_FEXT_BG_PROTECT; |
| } else { |
| ic->ic_flags_ext &= ~IEEE80211_FEXT_BG_PROTECT; |
| ic->ic_set_11g_erp(vap, 0); |
| } |
| ieee80211_wireless_reassoc(vap, 0, 1); |
| } else { |
| retv = EOPNOTSUPP; |
| } |
| break; |
| case IEEE80211_PARAM_11N_PROTECT: |
| if (value) { |
| ic->ic_flags_ext |= IEEE80211_FEXT_11N_PROTECT; |
| } else { |
| ic->ic_flags_ext &= ~IEEE80211_FEXT_11N_PROTECT; |
| if (ic->ic_local_rts && |
| (vap->iv_opmode == IEEE80211_M_STA || |
| vap->iv_opmode == IEEE80211_M_WDS)) { |
| ic->ic_local_rts = 0; |
| ic->ic_use_rtscts(ic); |
| } |
| } |
| ieee80211_wireless_reassoc(vap, 0, 1); |
| break; |
| case IEEE80211_PARAM_MU_NDPA_BW_SIGNALING_SUPPORT: |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| ic->rx_bws_support_for_mu_ndpa = value; |
| break; |
| case IEEE80211_PARAM_WPA_STARTED: |
| case IEEE80211_PARAM_HOSTAP_STARTED: |
| ic->hostap_wpa_state = value; |
| break; |
| case IEEE80211_PARAM_BSS_GROUP_ID: |
| retv = ieee80211_ioctl_set_bss_grpid(ic, vap, value); |
| break; |
| case IEEE80211_PARAM_BSS_ASSOC_RESERVE: |
| retv = ieee80211_ioctl_set_bss_grp_assoc_reserve(ic, vap, value); |
| break; |
| case IEEE80211_PARAM_MAX_BCAST_PPS: |
| if (vap->iv_opmode != IEEE80211_M_HOSTAP) { |
| return -EINVAL; |
| } |
| vap->bcast_pps.max_bcast_pps = value; |
| if (vap->bcast_pps.max_bcast_pps) { |
| vap->bcast_pps.rx_bcast_counter = 0; |
| vap->bcast_pps.rx_bcast_pps_start_time = jiffies + HZ; |
| vap->bcast_pps.tx_bcast_counter = 0; |
| vap->bcast_pps.tx_bcast_pps_start_time = jiffies + HZ; |
| } |
| break; |
| case IEEE80211_PARAM_MAX_BOOT_CAC_DURATION: |
| if ((vap->iv_opmode != IEEE80211_M_HOSTAP) |
| || ((value > 0) && (value < MIN_CAC_PERIOD)) |
| || ((value > 0) && (!(ic->ic_dfs_is_eu_region())))) { |
| return -EINVAL; |
| } |
| if (ic->ic_set_init_cac_duration) { |
| ic->ic_set_init_cac_duration(ic, value); |
| } |
| /* Start ICAC procedures */ |
| if (ic->ic_start_icac_procedure) { |
| ic->ic_start_icac_procedure(ic); |
| } |
| |
| break; |
| case IEEE80211_PARAM_RX_BAR_SYNC: |
| ic->ic_rx_bar_sync = value; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| break; |
| case IEEE80211_PARAM_STOP_ICAC: |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| if (ic->ic_get_init_cac_duration(ic) > 0) { |
| ic->ic_stop_icac_procedure(ic); |
| printk(KERN_DEBUG "ICAC: Aborted ICAC due to set channel request\n"); |
| } |
| } |
| break; |
| case IEEE80211_PARAM_STA_DFS_STRICT_MODE: |
| if ((vap->iv_opmode != IEEE80211_M_STA) || (!(ic->ic_dfs_is_eu_region()))) { |
| return -EOPNOTSUPP; |
| } |
| if (value) { |
| /* sta_dfs must be enabled to support sta_dfs_strict mode */ |
| ic->ic_flags_ext |= IEEE80211_FEXT_MARKDFS; |
| } else { |
| ic->ic_flags_ext &= ~IEEE80211_FEXT_MARKDFS; |
| } |
| |
| if (vap->iv_state == IEEE80211_S_RUN) { |
| SCSDBG(SCSLOG_NOTICE, "send qtn DFS report (DFS %s)\n", value ? |
| "Enabled" : "Disabled"); |
| ieee80211_send_action_dfs_report(vap->iv_bss); |
| } |
| |
| sp->csa_lhost->sta_dfs_strict_mode = !!value; |
| ic->ic_enable_sta_dfs(!!value); |
| ic->ic_set_radar(!!value); |
| ic->sta_dfs_info.sta_dfs_strict_mode = !!value; |
| break; |
| case IEEE80211_PARAM_STA_DFS_STRICT_MEASUREMENT_IN_CAC: |
| if ((vap->iv_opmode != IEEE80211_M_STA) || (!(ic->ic_dfs_is_eu_region()))) { |
| return -EINVAL; |
| } |
| ic->sta_dfs_info.sta_dfs_strict_msr_cac = !!value; |
| break; |
| case IEEE80211_PARAM_STA_DFS_STRICT_TX_CHAN_CLOSE_TIME: |
| if ((value >= STA_DFS_STRICT_TX_CHAN_CLOSE_TIME_MIN) |
| && (value <= STA_DFS_STRICT_TX_CHAN_CLOSE_TIME_MAX)) { |
| ic->sta_dfs_info.sta_dfs_tx_chan_close_time = value; |
| } else { |
| return -EINVAL; |
| } |
| break; |
| case IEEE80211_PARAM_NEIGHBORHOOD_THRSHD: |
| if (vap->iv_opmode != IEEE80211_M_HOSTAP) |
| return -EINVAL; |
| |
| ieee80211_set_threshold_of_neighborhood_type(ic, value >> 16, value & 0xFFFF); |
| break; |
| case IEEE80211_PARAM_DFS_CSA_CNT: |
| if (vap->iv_opmode != IEEE80211_M_HOSTAP) |
| return -EINVAL; |
| |
| if (value <= 0) |
| return -EINVAL; |
| |
| /* Should not longer than Channel Closing Transmission Time (1s) */ |
| TAILQ_FOREACH_SAFE(vap_each, &ic->ic_vaps, iv_next, vap_tmp) { |
| if (NULL == vap_each->iv_bss) |
| continue; |
| temp_value = IEEE80211_TU_TO_JIFFIES(vap_each->iv_bss->ni_intval); |
| temp_value *= value; |
| if (temp_value >= HZ) |
| return -EINVAL; |
| } |
| |
| ic->ic_dfs_csa_cnt = value; |
| break; |
| case IEEE80211_PARAM_COEX_20_40_SUPPORT: |
| ic->ic_20_40_coex_enable = !!value; |
| break; |
| case IEEE80211_PARAM_SYNC_CONFIG: |
| if (vap->iv_opmode != IEEE80211_M_HOSTAP) |
| return -EOPNOTSUPP; |
| |
| if (value != 0) |
| vap->iv_flags_ext2 |= IEEE80211_FEXT_SYNC_CONFIG; |
| else |
| vap->iv_flags_ext2 &= ~IEEE80211_FEXT_SYNC_CONFIG; |
| |
| break; |
| case IEEE80211_PARAM_AUTOCHAN_DBG_LEVEL: |
| ic->ic_autochan_dbg_level = value; |
| break; |
| #ifdef CONFIG_NAC_MONITOR |
| case IEEE80211_PARAM_NAC_MONITOR_MODE: |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| } |
| break; |
| #endif |
| case IEEE80211_PARAM_OPMODE_BW_SW_EN: |
| ic->ic_opmode_bw_switch_en = value; |
| break; |
| case IEEE80211_PARAM_MAX_DEVICE_BW: |
| ic->ic_max_system_bw = value; |
| break; |
| case IEEE80211_PARAM_VOPT: |
| if ((ic->ic_opmode == IEEE80211_M_HOSTAP) && |
| (value <= IEEE80211_VOPT_AUTO) && |
| (value >= IEEE80211_VOPT_DISABLED)) |
| ic->ic_vopt.state = value; |
| else |
| return -EOPNOTSUPP; |
| break; |
| case IEEE80211_PARAM_BW_AUTO_SELECT: |
| ic->ic_bw_auto_select = value; |
| break; |
| case IEEE80211_PARAM_CUR_CHAN_CHECK_REQUIRED: |
| ic->ic_chan_is_set = value; |
| break; |
| #ifdef CONFIG_QHOP |
| case IEEE80211_PARAM_RBS_MBS_ALLOW_TX_FRMS_IN_CAC: |
| ic->rbs_mbs_dfs_info.rbs_mbs_allow_tx_frms_in_cac= !!value; |
| break; |
| case IEEE80211_PARAM_RBS_DFS_TX_CHAN_CLOSE_TIME: |
| if ((value >= RBS_DFS_TX_CHAN_CLOSE_TIME_MIN) |
| && (value <= RBS_DFS_TX_CHAN_CLOSE_TIME_MAX)) { |
| ic->rbs_mbs_dfs_info.rbs_dfs_tx_chan_close_time = value; |
| } else { |
| return -EINVAL; |
| } |
| break; |
| #endif |
| case IEEE80211_PARAM_AUTOCHAN_CCI_INSTNT: |
| ic->ic_autochan_ranking_params.cci_instnt_factor = value; |
| break; |
| case IEEE80211_PARAM_AUTOCHAN_ACI_INSTNT: |
| ic->ic_autochan_ranking_params.aci_instnt_factor = value; |
| break; |
| case IEEE80211_PARAM_AUTOCHAN_CCI_LONGTERM: |
| ic->ic_autochan_ranking_params.cci_longterm_factor = value; |
| break; |
| case IEEE80211_PARAM_AUTOCHAN_ACI_LONGTERM: |
| ic->ic_autochan_ranking_params.aci_longterm_factor = value; |
| break; |
| case IEEE80211_PARAM_AUTOCHAN_RANGE_COST: |
| ic->ic_autochan_ranking_params.range_factor = value; |
| break; |
| case IEEE80211_PARAM_AUTOCHAN_DFS_COST: |
| ic->ic_autochan_ranking_params.dfs_factor = value; |
| break; |
| case IEEE80211_PARAM_AUTOCHAN_MIN_CCI_RSSI: |
| ic->ic_autochan_ranking_params.min_cochan_rssi = value; |
| break; |
| case IEEE80211_PARAM_AUTOCHAN_MAXBW_MINBENEFIT: |
| ic->ic_autochan_ranking_params.maxbw_minbenefit = value; |
| break; |
| case IEEE80211_PARAM_AUTOCHAN_DENSE_CCI_SPAN: |
| ic->ic_autochan_ranking_params.dense_cci_span = value; |
| break; |
| case IEEE80211_PARAM_WEATHERCHAN_CAC_ALLOWED: |
| ic->ic_weachan_cac_allowed = !!value; |
| break; |
| case IEEE80211_PARAM_VAP_TX_AMSDU_11N: |
| if (vap->iv_tx_amsdu_11n != !!value) { |
| vap->iv_tx_amsdu_11n = !!value; |
| ieee80211_param_to_qdrv(vap, param, value, NULL, 0); |
| ieee80211_wireless_reassoc(vap, 0, 1); |
| } |
| break; |
| case IEEE80211_PARAM_COC_MOVE_TO_NONDFS_CHANNEL: |
| ic->ic_coc_move_to_ndfs = value; |
| break; |
| case IEEE80211_PARAM_80211K_NEIGH_REPORT: |
| if (value == 1) |
| IEEE80211_COM_NEIGHREPORT_ENABLE(ic); |
| else |
| IEEE80211_COM_NEIGHREPORT_DISABLE(ic); |
| ieee80211_beacon_update_all(ic); |
| break; |
| case IEEE80211_PARAM_80211V_BTM: |
| if (value == 1) |
| IEEE80211_COM_BTM_ENABLE(ic); |
| else |
| IEEE80211_COM_BTM_DISABLE(ic); |
| ieee80211_beacon_update_all(ic); |
| break; |
| case IEEE80211_PARAM_MOBILITY_DOMAIN: |
| if (vap->iv_opmode != IEEE80211_M_HOSTAP) |
| return -EINVAL; |
| vap->iv_mdid = value; |
| ic->ic_beacon_update(vap); |
| break; |
| case IEEE80211_PARAM_FT_OVER_DS: |
| if (vap->iv_opmode != IEEE80211_M_HOSTAP) |
| return -EINVAL; |
| vap->iv_ft_over_ds = value; |
| break; |
| #if defined(PLATFORM_QFDR) |
| case IEEE80211_PARAM_REJECT_AUTH: |
| ic->ic_reject_auth = (uint8_t)value; |
| break; |
| case IEEE80211_PARAM_SCAN_ONLY_FREQ: |
| vap->iv_scan_only_freq = (uint16_t)value; |
| if (vap->iv_scan_only_freq) |
| vap->iv_scan_only_cnt = QFDR_SCAN_ONLY_FREQ_ATTEMPTS; |
| break; |
| #endif |
| case IEEE80211_PARAM_FIX_LEGACY_RATE: |
| retv = ieee80211_fix_legacy_rate(vap, param, value); |
| break; |
| default: |
| retv = EOPNOTSUPP; |
| break; |
| } |
| |
| if (retv == ENETRESET) |
| retv = IS_UP_AUTO(vap) ? ieee80211_open(vap->iv_dev) : 0; |
| |
| return -retv; |
| } |
| |
| /* |
| * Issue two commands to overcome the short range association issue: |
| * 1) Change the transmit power level |
| * 2) Change the rx gain and agc |
| */ |
| int ieee80211_pwr_adjust(struct ieee80211vap *vap, int rxgain_state) |
| { |
| int args[2]; |
| int retval = 0; |
| int anychan = 0; |
| |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: Enabling %s gain Settings\n", |
| __func__, rxgain_state ? "Low" : "High"); |
| |
| if (rxgain_state) |
| retval = apply_tx_power(vap, IEEE80211_TXPOW_ENCODE(anychan), |
| IEEE80211_APPLY_LOWGAIN_TXPOWER); |
| else |
| retval = apply_tx_power(vap, IEEE80211_TXPOW_ENCODE(anychan), |
| IEEE80211_APPLY_TXPOWER_NORMAL); |
| |
| if (retval >= 0) { |
| args[0] = IEEE80211_PARAM_PWR_ADJUST; |
| args[1] = rxgain_state; |
| retval = ieee80211_ioctl_setparam(vap->iv_dev, NULL, NULL, (char*)args); |
| } |
| |
| return retval; |
| } |
| EXPORT_SYMBOL(ieee80211_pwr_adjust); |
| |
| static int |
| ieee80211_ioctl_getparam_txpower(struct ieee80211vap *vap, int *param) |
| { |
| struct ieee80211com *ic = vap->iv_ic; |
| int chan = (param[0] >> 16); |
| int retval = -EINVAL; |
| |
| /* Retrieve and see if we are in low gain state and return power accordingly */ |
| ieee80211_param_from_qdrv(vap, param[0] & 0xffff, ¶m[0], NULL, 0); |
| |
| if (chan <= IEEE80211_CHAN_MAX && chan > 0 && isset(ic->ic_chan_active, chan)) { |
| const struct ieee80211_channel *c = findchannel(ic, chan, IEEE80211_MODE_AUTO); |
| |
| if (c != NULL) { |
| if (param[0] == 1) |
| param[0] = IEEE80211_LOWGAIN_TXPOW_MAX; |
| else |
| param[0] = c->ic_maxpower_normal; |
| retval = 0; |
| } |
| } |
| |
| return retval; |
| } |
| static int |
| ieee80211_ioctl_getparam_bw_txpower(struct ieee80211vap *vap, int *param) |
| { |
| struct ieee80211com *ic = vap->iv_ic; |
| uint32_t chan = (((uint32_t)param[0]) >> 24) & 0xff; |
| uint32_t bf_on = (param[0] >> 20) & 0xf; |
| uint32_t num_ss = (param[0] >> 16) & 0xf; |
| int retval = -EINVAL; |
| |
| if (chan <= IEEE80211_CHAN_MAX && |
| isset(ic->ic_chan_active, chan) && |
| num_ss <= IEEE80211_QTN_NUM_RF_STREAMS) { |
| const struct ieee80211_channel *c = findchannel(ic, chan, IEEE80211_MODE_AUTO); |
| uint32_t idx_bf = PWR_IDX_BF_OFF + bf_on; |
| uint32_t idx_ss = PWR_IDX_1SS + num_ss - 1; |
| |
| if (c != NULL && idx_bf < PWR_IDX_BF_MAX && idx_ss < PWR_IDX_SS_MAX) { |
| param[0] = ((c->ic_maxpower_table[idx_bf][idx_ss][PWR_IDX_80M] & 0xff) << 16) | |
| ((c->ic_maxpower_table[idx_bf][idx_ss][PWR_IDX_40M] & 0xff) << 8) | |
| (c->ic_maxpower_table[idx_bf][idx_ss][PWR_IDX_20M] & 0xff); |
| retval = 0; |
| } |
| } |
| |
| return retval; |
| } |
| |
| static int |
| ieee80211_param_ocac_get(struct ieee80211vap *vap, int *param) |
| { |
| struct ieee80211com *ic = vap->iv_ic; |
| uint32_t param_id = (((uint32_t)param[0]) >> 16) & 0xffff; |
| int retval = 0; |
| |
| switch(param_id) { |
| case IEEE80211_OCAC_GET_STATUS: |
| param[0] = ic->ic_ocac.ocac_cfg.ocac_enable; |
| break; |
| case IEEE80211_OCAC_GET_AVAILABILITY: |
| param[0] = !ieee80211_wireless_is_ocac_unsupported(vap); |
| break; |
| default: |
| retval = -EINVAL; |
| break; |
| } |
| |
| return retval; |
| } |
| |
| static int |
| ieee80211_ioctl_getmode(struct net_device *dev, struct iw_request_info *info, |
| struct iw_point *wri, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| |
| switch (vap->iv_ic->ic_phymode ) { |
| case IEEE80211_MODE_11A: |
| strcpy(extra, "11a"); |
| break; |
| case IEEE80211_MODE_11B: |
| strcpy(extra, "11b"); |
| break; |
| case IEEE80211_MODE_11NG_HT40PM: |
| strcpy(extra, "11ng40"); |
| break; |
| case IEEE80211_MODE_11NA_HT40PM: |
| if (vap->iv_11ac_and_11n_flag & IEEE80211_11N_ONLY) { |
| strcpy(extra, "11nOnly40"); |
| } else { |
| strcpy(extra, "11na40"); |
| } |
| break; |
| case IEEE80211_MODE_11NG: |
| strcpy(extra, "11ng"); |
| break; |
| case IEEE80211_MODE_11G: |
| strcpy(extra, "11g"); |
| break; |
| case IEEE80211_MODE_11NA: |
| if (vap->iv_11ac_and_11n_flag & IEEE80211_11N_ONLY) { |
| strcpy(extra, "11nOnly20"); |
| } else { |
| strcpy(extra, "11na20"); |
| } |
| break; |
| case IEEE80211_MODE_FH: |
| strcpy(extra, "FH"); |
| break; |
| case IEEE80211_MODE_11AC_VHT20PM: |
| if (vap->iv_11ac_and_11n_flag & IEEE80211_11AC_ONLY) { |
| strcpy(extra, "11acOnly20"); |
| } else { |
| strcpy(extra, "11ac20"); |
| } |
| break; |
| case IEEE80211_MODE_11AC_VHT40PM: |
| if (vap->iv_11ac_and_11n_flag & IEEE80211_11AC_ONLY) { |
| strcpy(extra, "11acOnly40"); |
| } else { |
| strcpy(extra, "11ac40"); |
| } |
| break; |
| case IEEE80211_MODE_11AC_VHT80PM: |
| if (vap->iv_11ac_and_11n_flag & IEEE80211_11AC_ONLY) { |
| strcpy(extra, "11acOnly80"); |
| } else { |
| if (IEEE80211_IS_CHAN_VHT80_EDGEPLUS(vap->iv_ic->ic_curchan)) |
| strcpy(extra, "11ac80Edge+"); |
| else if (IEEE80211_IS_CHAN_VHT80_CNTRPLUS(vap->iv_ic->ic_curchan)) |
| strcpy(extra, "11ac80Cntr+"); |
| else if (IEEE80211_IS_CHAN_VHT80_CNTRMINUS(vap->iv_ic->ic_curchan)) |
| strcpy(extra, "11ac80Cntr-"); |
| else if (IEEE80211_IS_CHAN_VHT80_EDGEMINUS(vap->iv_ic->ic_curchan)) |
| strcpy(extra, "11ac80Edge-"); |
| } |
| break; |
| case IFM_AUTO: |
| strcpy(extra, "auto"); |
| break; |
| default: |
| return -EINVAL; |
| } |
| wri->length = strlen(extra); |
| strncpy(wri->pointer, extra, wri->length); |
| return 0; |
| } |
| |
| static void |
| ieee80211_blacklist_node_print(void *arg, struct ieee80211_node *ni) |
| { |
| if (ni->ni_blacklist_timeout > 0) { |
| printf("%s\n", ether_sprintf(ni->ni_macaddr)); |
| } |
| } |
| |
| static uint32_t |
| ieee80211_param_wowlan_get(struct ieee80211com *ic) |
| { |
| return ((ic->ic_wowlan.host_state << 31) | |
| (ic->ic_wowlan.wowlan_match << 29) | |
| ((ic->ic_wowlan.L3_udp_port&0x1fff) << 16) | |
| ic->ic_wowlan.L2_ether_type); |
| } |
| |
| static void |
| ieee80211_extdr_dump_flags(struct ieee80211vap *vap) |
| { |
| const char *wds_mode; |
| |
| if (IEEE80211_VAP_WDS_IS_MBS(vap)) |
| wds_mode = "MBS"; |
| else if (IEEE80211_VAP_WDS_IS_RBS(vap)) |
| wds_mode = "RBS"; |
| else |
| wds_mode = "WDS"; |
| |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_EXTDR, |
| "Legay VAP QHOP mode: %s\n", wds_mode); |
| } |
| |
| static int |
| ieee80211_ioctl_getparam(struct net_device *dev, struct iw_request_info *info, |
| void *w, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_rsnparms *rsn = NULL; |
| int *param = (int *) extra; |
| struct shared_params *sp = qtn_mproc_sync_shared_params_get(); |
| #if defined(QBMPS_ENABLE) |
| struct qdrv_vap *qv = container_of(vap, struct qdrv_vap, iv); |
| #endif |
| |
| if (vap->iv_bss) { |
| rsn = &vap->iv_bss->ni_rsn; |
| } |
| switch (param[0] & 0xffff) { |
| case IEEE80211_PARAM_AP_ISOLATE: |
| param[0] = br_get_ap_isolate(); |
| break; |
| case IEEE80211_PARAM_AUTHMODE: |
| if (vap->iv_flags & IEEE80211_F_WPA) { |
| param[0] = IEEE80211_AUTH_WPA; |
| } else { |
| if (NULL == vap->iv_bss) |
| return -EINVAL; |
| param[0] = vap->iv_bss->ni_authmode; |
| } |
| break; |
| case IEEE80211_PARAM_PROTMODE: |
| param[0] = ic->ic_protmode; |
| break; |
| case IEEE80211_PARAM_MCASTCIPHER: |
| if (!rsn) |
| return -EINVAL; |
| param[0] = rsn->rsn_mcastcipher; |
| break; |
| case IEEE80211_PARAM_MCASTKEYLEN: |
| if (!rsn) |
| return -EINVAL; |
| param[0] = rsn->rsn_mcastkeylen; |
| break; |
| case IEEE80211_PARAM_UCASTCIPHERS: |
| if (!rsn) |
| return -EINVAL; |
| param[0] = rsn->rsn_ucastcipherset; |
| break; |
| case IEEE80211_PARAM_UCASTCIPHER: |
| if (!rsn) |
| return -EINVAL; |
| param[0] = rsn->rsn_ucastcipher; |
| break; |
| case IEEE80211_PARAM_UCASTKEYLEN: |
| if (!rsn) |
| return -EINVAL; |
| param[0] = rsn->rsn_ucastkeylen; |
| break; |
| case IEEE80211_PARAM_KEYMGTALGS: |
| if (!rsn) |
| return -EINVAL; |
| param[0] = rsn->rsn_keymgmtset; |
| break; |
| case IEEE80211_PARAM_RSNCAPS: |
| if (!rsn) |
| return -EINVAL; |
| param[0] = rsn->rsn_caps; |
| break; |
| case IEEE80211_PARAM_WPA: |
| switch (vap->iv_flags & IEEE80211_F_WPA) { |
| case IEEE80211_F_WPA1: |
| param[0] = 1; |
| break; |
| case IEEE80211_F_WPA2: |
| param[0] = 2; |
| break; |
| case IEEE80211_F_WPA1 | IEEE80211_F_WPA2: |
| param[0] = 3; |
| break; |
| default: |
| param[0] = 0; |
| break; |
| } |
| break; |
| case IEEE80211_PARAM_ROAMING: |
| param[0] = ic->ic_roaming; |
| break; |
| case IEEE80211_PARAM_PRIVACY: |
| param[0] = (vap->iv_flags & IEEE80211_F_PRIVACY) != 0; |
| break; |
| case IEEE80211_PARAM_DROPUNENCRYPTED: |
| param[0] = (vap->iv_flags & IEEE80211_F_DROPUNENC) != 0; |
| break; |
| case IEEE80211_PARAM_DROPUNENC_EAPOL: |
| param[0] = IEEE80211_VAP_DROPUNENC_EAPOL(vap); |
| break; |
| case IEEE80211_PARAM_COUNTERMEASURES: |
| param[0] = (vap->iv_flags & IEEE80211_F_COUNTERM) != 0; |
| break; |
| case IEEE80211_PARAM_DRIVER_CAPS: |
| param[0] = vap->iv_caps; |
| break; |
| case IEEE80211_PARAM_WMM: |
| param[0] = (vap->iv_flags & IEEE80211_F_WME) != 0; |
| break; |
| case IEEE80211_PARAM_HIDESSID: |
| param[0] = (vap->iv_flags & IEEE80211_F_HIDESSID) != 0; |
| break; |
| case IEEE80211_PARAM_APBRIDGE: |
| param[0] = (vap->iv_flags & IEEE80211_F_NOBRIDGE) == 0; |
| break; |
| case IEEE80211_PARAM_INACT: |
| param[0] = vap->iv_inact_run * IEEE80211_INACT_WAIT; |
| break; |
| case IEEE80211_PARAM_INACT_AUTH: |
| param[0] = vap->iv_inact_auth * IEEE80211_INACT_WAIT; |
| break; |
| case IEEE80211_PARAM_INACT_INIT: |
| param[0] = vap->iv_inact_init * IEEE80211_INACT_WAIT; |
| break; |
| case IEEE80211_PARAM_DTIM_PERIOD: |
| param[0] = vap->iv_dtim_period; |
| break; |
| case IEEE80211_PARAM_BEACON_INTERVAL: |
| /* NB: get from ic_bss for station mode */ |
| param[0] = ic->ic_lintval_backup; |
| break; |
| case IEEE80211_PARAM_DOTH: |
| param[0] = (ic->ic_flags & IEEE80211_F_DOTH) != 0; |
| break; |
| case IEEE80211_PARAM_SHPREAMBLE: |
| param[0] = (ic->ic_caps & IEEE80211_C_SHPREAMBLE) != 0; |
| break; |
| case IEEE80211_PARAM_PWRCONSTRAINT: |
| if (ic->ic_flags & IEEE80211_F_DOTH && ic->ic_flags_ext & IEEE80211_FEXT_TPC) |
| param[0] = ic->ic_pwr_constraint; |
| else |
| return -EOPNOTSUPP; |
| break; |
| case IEEE80211_PARAM_PUREG: |
| param[0] = (vap->iv_flags & IEEE80211_F_PUREG) != 0; |
| break; |
| case IEEE80211_PARAM_WDS: |
| param[0] = ((vap->iv_flags_ext & IEEE80211_FEXT_WDS) == IEEE80211_FEXT_WDS); |
| break; |
| case IEEE80211_PARAM_REPEATER: |
| param[0] = !!(ic->ic_flags_ext & IEEE80211_FEXT_REPEATER); |
| break; |
| case IEEE80211_PARAM_BGSCAN: |
| param[0] = (vap->iv_flags & IEEE80211_F_BGSCAN) != 0; |
| break; |
| case IEEE80211_PARAM_BGSCAN_IDLE: |
| param[0] = jiffies_to_msecs(vap->iv_bgscanidle); /* ms */ |
| break; |
| case IEEE80211_PARAM_BGSCAN_INTERVAL: |
| param[0] = vap->iv_bgscanintvl / HZ; /* seconds */ |
| break; |
| case IEEE80211_PARAM_SCAN_OPCHAN: |
| param[0] = ic->ic_scan_opchan_enable; |
| break; |
| case IEEE80211_PARAM_EXTENDER_MBS_RSSI_MARGIN: |
| param[0] = ic->ic_extender_mbs_rssi_margin; |
| break; |
| case IEEE80211_PARAM_MCAST_RATE: |
| param[0] = vap->iv_mcast_rate; /* seconds */ |
| break; |
| case IEEE80211_PARAM_COVERAGE_CLASS: |
| param[0] = ic->ic_coverageclass; |
| break; |
| case IEEE80211_PARAM_COUNTRY_IE: |
| param[0] = (ic->ic_flags_ext & IEEE80211_FEXT_COUNTRYIE) != 0; |
| break; |
| case IEEE80211_PARAM_REGCLASS: |
| param[0] = (ic->ic_flags_ext & IEEE80211_FEXT_REGCLASS) != 0; |
| break; |
| case IEEE80211_PARAM_SCANVALID: |
| param[0] = vap->iv_scanvalid / HZ; /* seconds */ |
| break; |
| case IEEE80211_PARAM_ROAM_RSSI_11A: |
| param[0] = vap->iv_roam.rssi11a; |
| break; |
| case IEEE80211_PARAM_ROAM_RSSI_11B: |
| param[0] = vap->iv_roam.rssi11bOnly; |
| break; |
| case IEEE80211_PARAM_ROAM_RSSI_11G: |
| param[0] = vap->iv_roam.rssi11b; |
| break; |
| case IEEE80211_PARAM_ROAM_RATE_11A: |
| param[0] = vap->iv_roam.rate11a; |
| break; |
| case IEEE80211_PARAM_ROAM_RATE_11B: |
| param[0] = vap->iv_roam.rate11bOnly; |
| break; |
| case IEEE80211_PARAM_ROAM_RATE_11G: |
| param[0] = vap->iv_roam.rate11b; |
| break; |
| case IEEE80211_PARAM_UAPSDINFO: |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| if (IEEE80211_VAP_UAPSD_ENABLED(vap)) |
| param[0] = 1; |
| else |
| param[0] = 0; |
| } else if (vap->iv_opmode == IEEE80211_M_STA) |
| param[0] = vap->iv_uapsdinfo; |
| break; |
| case IEEE80211_PARAM_SLEEP: |
| if (NULL == vap->iv_bss) |
| return -EINVAL; |
| param[0] = vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT; |
| break; |
| case IEEE80211_PARAM_EOSPDROP: |
| param[0] = IEEE80211_VAP_EOSPDROP_ENABLED(vap); |
| break; |
| case IEEE80211_PARAM_STA_DFS: |
| case IEEE80211_PARAM_MARKDFS: |
| if (ic->ic_flags_ext & IEEE80211_FEXT_MARKDFS) |
| param[0] = 1; |
| else |
| param[0] = 0; |
| break; |
| case IEEE80211_PARAM_SHORT_GI: |
| /* Checking both SGI flags, if SGI is enabled/disabled */ |
| if (IS_IEEE80211_VHT_ENABLED(ic)) { |
| param[0] = (ic->ic_vhtcap.cap_flags & IEEE80211_VHTCAP_C_SHORT_GI_80) ? 1 : 0; |
| } else { |
| param[0] = (vap->iv_ht_flags & IEEE80211_HTF_SHORTGI_ENABLED && |
| (ic->ic_htcap.cap & IEEE80211_HTCAP_C_SHORTGI20 || |
| ic->ic_htcap.cap & IEEE80211_HTCAP_C_SHORTGI40)) ? 1 : 0; |
| } |
| break; |
| case IEEE80211_PARAM_MCS_CAP: |
| if ((vap->iv_mcs_config & IEEE80211_RATE_PREFIX_MASK) == IEEE80211_N_RATE_PREFIX) { |
| param[0] = (vap->iv_mcs_config & 0xFF); |
| } else { |
| printk("11N MCS is not set\n"); |
| return -EOPNOTSUPP; |
| } |
| break; |
| case IEEE80211_PARAM_LEGACY_RETRY_LIMIT: |
| case IEEE80211_PARAM_MIMOMODE: |
| case IEEE80211_PARAM_SHORT_RETRY_LIMIT: |
| case IEEE80211_PARAM_LONG_RETRY_LIMIT: |
| case IEEE80211_PARAM_RETRY_COUNT: |
| case IEEE80211_PARAM_TXBF_PERIOD: |
| case IEEE80211_PARAM_TXBF_CTRL: |
| case IEEE80211_PARAM_GET_RFCHIP_ID: |
| case IEEE80211_PARAM_GET_RFCHIP_VERID: |
| case IEEE80211_PARAM_LDPC: |
| case IEEE80211_PARAM_STBC: |
| case IEEE80211_PARAM_RTS_CTS: |
| case IEEE80211_PARAM_TX_QOS_SCHED: |
| case IEEE80211_PARAM_PEER_RTS_MODE: |
| case IEEE80211_PARAM_DYN_WMM: |
| case IEEE80211_PARAM_11N_40_ONLY_MODE: |
| case IEEE80211_PARAM_MAX_MGMT_FRAMES: |
| case IEEE80211_PARAM_MCS_ODD_EVEN: |
| case IEEE80211_PARAM_RESTRICTED_MODE: |
| case IEEE80211_PARAM_RESTRICT_RTS: |
| case IEEE80211_PARAM_RESTRICT_LIMIT: |
| case IEEE80211_PARAM_RESTRICT_RATE: |
| case IEEE80211_PARAM_SWRETRY_AGG_MAX: |
| case IEEE80211_PARAM_SWRETRY_NOAGG_MAX: |
| case IEEE80211_PARAM_SWRETRY_SUSPEND_XMIT: |
| case IEEE80211_PARAM_RX_AGG_TIMEOUT: |
| case IEEE80211_PARAM_CARRIER_ID: |
| case IEEE80211_PARAM_TX_QUEUING_ALG: |
| case IEEE80211_PARAM_CONGEST_IDX: |
| case IEEE80211_PARAM_MICHAEL_ERR_CNT: |
| case IEEE80211_PARAM_MAX_AGG_SIZE: |
| case IEEE80211_PARAM_MU_ENABLE: |
| case IEEE80211_PARAM_MU_USE_EQ: |
| case IEEE80211_PARAM_RESTRICT_WLAN_IP: |
| case IEEE80211_PARAM_CCA_FIXED: |
| case IEEE80211_PARAM_AUTO_CCA_ENABLE: |
| case IEEE80211_PARAM_GET_CCA_STATS: |
| case IEEE80211_PARAM_GET_MU_GRP_QMAT: |
| case IEEE80211_PARAM_CACSTATUS: |
| case IEEE80211_PARAM_EP_STATUS: |
| case IEEE80211_PARAM_BEACON_HANG_TIMEOUT: |
| case IEEE80211_PARAM_BB_DEAFNESS_WAR_EN: |
| ieee80211_param_from_qdrv(vap, param[0] & 0xffff, ¶m[0], NULL, 0); |
| break; |
| case IEEE80211_PARAM_BW_SEL_MUC: |
| param[0] = ieee80211_get_bw(ic); |
| break; |
| case IEEE80211_PARAM_MODE: |
| if (IS_IEEE80211_5G_BAND(ic)) |
| ieee80211_param_from_qdrv(vap, param[0] & 0xffff, ¶m[0], NULL, 0); |
| else |
| param[0] = !!(ic->ic_flags_ext & IEEE80211_FEXT_24GVHT); |
| break; |
| case IEEE80211_PARAM_HT_NSS_CAP: |
| param[0] = ic->ic_ht_nss_cap; |
| break; |
| case IEEE80211_PARAM_VHT_NSS_CAP: |
| param[0] = ic->ic_vht_nss_cap; |
| break; |
| case IEEE80211_PARAM_VHT_MCS_CAP: |
| param[0] = ic->ic_vht_mcs_cap; |
| break; |
| case IEEE80211_PARAM_RTSTHRESHOLD: |
| param[0] = vap->iv_rtsthreshold; |
| break; |
| case IEEE80211_PARAM_AMPDU_DENSITY: |
| param[0] = ic->ic_htcap.mpduspacing; |
| break; |
| case IEEE80211_PARAM_SCANSTATUS: |
| if (ic->ic_flags & IEEE80211_F_SCAN) { |
| param[0] = 1; |
| } else { |
| param[0] = 0; |
| } |
| break; |
| case IEEE80211_PARAM_IMPLICITBA: |
| param[0] = vap->iv_implicit_ba; |
| break; |
| case IEEE80211_PARAM_GLOBAL_BA_CONTROL: |
| param[0] = vap->iv_ba_control; |
| break; |
| case IEEE80211_PARAM_VAP_STATS: |
| { |
| param[0] = 0; // no meaning |
| |
| printk("RX stats (delta)\n"); |
| printk(" dup:\t%u\n", vap->iv_stats.is_rx_dup); |
| printk(" beacon:\t%u\n", vap->iv_stats.is_rx_beacon); |
| printk(" elem_missing:\t%u\n", vap->iv_stats.is_rx_elem_missing); |
| printk(" badchan:\t%u\n", vap->iv_stats.is_rx_badchan); |
| printk(" chanmismatch:\t%u\n", vap->iv_stats.is_rx_chanmismatch); |
| |
| // clear |
| memset(&vap->iv_stats, 0, sizeof(vap->iv_stats)); |
| } |
| break; |
| case IEEE80211_PARAM_DFS_FAST_SWITCH: |
| param[0] = ((ic->ic_flags_ext & IEEE80211_FEXT_DFS_FAST_SWITCH) != 0); |
| break; |
| case IEEE80211_PARAM_SCAN_NO_DFS: |
| param[0] = ((ic->ic_flags_ext & IEEE80211_FEXT_SCAN_NO_DFS) != 0); |
| break; |
| case IEEE80211_PARAM_BLACKLIST_GET: |
| ieee80211_iterate_dev_nodes(dev, |
| &ic->ic_sta, ieee80211_blacklist_node_print, NULL, 0); |
| param[0] = 0; |
| break; |
| case IEEE80211_PARAM_FIXED_TX_RATE: |
| param[0] = vap->iv_mcs_config; |
| break; |
| case IEEE80211_PARAM_REGULATORY_REGION: |
| { |
| union { |
| char as_chars[ 4 ]; |
| u_int32_t as_u32; |
| } region; |
| |
| if (ieee80211_countryid_to_country_string( ic->ic_country_code, region.as_chars ) != 0) { |
| /* |
| * If we can't get a country string from the current code, |
| * return the NULL string as the region. |
| */ |
| region.as_u32 = 0; |
| } |
| |
| param[0] = (int) region.as_u32; |
| } |
| break; |
| case IEEE80211_PARAM_SAMPLE_RATE: |
| param[0] = ic->ic_sample_rate; |
| break; |
| case IEEE80211_PARAM_CONFIG_TXPOWER: |
| { |
| int retval = ieee80211_ioctl_getparam_txpower(vap, param); |
| if (retval != 0) { |
| return retval; |
| } |
| } |
| break; |
| case IEEE80211_PARAM_CONFIG_BW_TXPOWER: |
| { |
| int retval = ieee80211_ioctl_getparam_bw_txpower(vap, param); |
| if (retval != 0) { |
| return retval; |
| } |
| } |
| break; |
| case IEEE80211_PARAM_TPC: |
| if (ic->ic_flags & IEEE80211_F_DOTH) { |
| param[0] = !!(ic->ic_flags_ext & IEEE80211_FEXT_TPC); |
| } else { |
| return -EOPNOTSUPP; |
| } |
| break; |
| case IEEE80211_PARAM_CONFIG_TPC_INTERVAL: |
| if ((ic->ic_flags & IEEE80211_F_DOTH) && (ic->ic_flags_ext & IEEE80211_FEXT_TPC)) { |
| param[0] = ieee80211_tpc_query_get_interval(&ic->ic_tpc_query_info); |
| } else { |
| return -EOPNOTSUPP; |
| } |
| break; |
| case IEEE80211_PARAM_TPC_QUERY: |
| if ((ic->ic_flags & IEEE80211_F_DOTH) && (ic->ic_flags_ext & IEEE80211_FEXT_TPC)) { |
| param[0] = ieee80211_tpc_query_state(&ic->ic_tpc_query_info); |
| } else { |
| return -EOPNOTSUPP; |
| } |
| break; |
| case IEEE80211_PARAM_CONFIG_REGULATORY_TXPOWER: |
| { |
| int chan = (param[0] >> 16); |
| |
| if (chan <= IEEE80211_CHAN_MAX && chan > 0 && isset(ic->ic_chan_active, chan)) { |
| const struct ieee80211_channel *c = findchannel(ic, chan, IEEE80211_MODE_AUTO); |
| |
| if (c != NULL) { |
| param[0] = c->ic_maxregpower; |
| } else { |
| return -EINVAL; |
| } |
| } |
| } |
| break; |
| case IEEE80211_PARAM_BA_MAX_WIN_SIZE: |
| param[0] = vap->iv_max_ba_win_size; |
| break; |
| case IEEE80211_PARAM_MIN_DWELL_TIME_ACTIVE: |
| param[0] = ic->ic_mindwell_active; |
| break; |
| case IEEE80211_PARAM_MIN_DWELL_TIME_PASSIVE: |
| param[0] = ic->ic_mindwell_passive; |
| break; |
| case IEEE80211_PARAM_MAX_DWELL_TIME_ACTIVE: |
| param[0] = ic->ic_maxdwell_active; |
| break; |
| #ifdef QTN_BG_SCAN |
| case IEEE80211_PARAM_QTN_BGSCAN_DWELL_TIME_ACTIVE: |
| param[0] = ic->ic_qtn_bgscan.dwell_msecs_active; |
| break; |
| case IEEE80211_PARAM_QTN_BGSCAN_DWELL_TIME_PASSIVE: |
| param[0] = ic->ic_qtn_bgscan.dwell_msecs_passive; |
| break; |
| case IEEE80211_PARAM_QTN_BGSCAN_DURATION_ACTIVE: |
| param[0] = ic->ic_qtn_bgscan.duration_msecs_active; |
| break; |
| case IEEE80211_PARAM_QTN_BGSCAN_DURATION_PASSIVE_FAST: |
| param[0] = ic->ic_qtn_bgscan.duration_msecs_passive_fast; |
| break; |
| case IEEE80211_PARAM_QTN_BGSCAN_DURATION_PASSIVE_NORMAL: |
| param[0] = ic->ic_qtn_bgscan.duration_msecs_passive_normal; |
| break; |
| case IEEE80211_PARAM_QTN_BGSCAN_DURATION_PASSIVE_SLOW: |
| param[0] = ic->ic_qtn_bgscan.duration_msecs_passive_slow; |
| break; |
| case IEEE80211_PARAM_QTN_BGSCAN_THRSHLD_PASSIVE_FAST: |
| param[0] = ic->ic_qtn_bgscan.thrshld_fat_passive_fast; |
| break; |
| case IEEE80211_PARAM_QTN_BGSCAN_THRSHLD_PASSIVE_NORMAL: |
| param[0] = ic->ic_qtn_bgscan.thrshld_fat_passive_normal; |
| break; |
| case IEEE80211_PARAM_QTN_BGSCAN_DEBUG: |
| param[0] = ic->ic_qtn_bgscan.debug_flags; |
| break; |
| #endif /*QTN_BG_SCAN */ |
| case IEEE80211_PARAM_MAX_DWELL_TIME_PASSIVE: |
| param[0] = ic->ic_maxdwell_passive; |
| break; |
| #ifdef QSCS_ENABLED |
| case IEEE80211_PARAM_SCS: |
| if (ieee80211_param_scs_get(dev, param[0] >> IEEE80211_SCS_COMMAND_S, |
| (uint32_t *)¶m[0]) < 0) { |
| return -EINVAL; |
| } |
| break; |
| case IEEE80211_PARAM_SCS_DFS_REENTRY_REQUEST: |
| param[0] = ((struct ap_state *)(ic->ic_scan->ss_scs_priv))->as_dfs_reentry_level; |
| break; |
| case IEEE80211_PARAM_SCS_CCA_INTF: |
| { |
| int chan = (param[0] >> 16); |
| struct ap_state *as = ic->ic_scan->ss_scs_priv; |
| |
| if (as && chan < IEEE80211_CHAN_MAX && chan > 0 && isset(ic->ic_chan_active, chan)) { |
| if (as->as_cca_intf[chan] == SCS_CCA_INTF_INVALID) |
| param[0] = -1; |
| else |
| param[0] = as->as_cca_intf[chan]; |
| } else { |
| return -EINVAL; |
| } |
| } |
| break; |
| #endif /* QSCS_ENABLED */ |
| case IEEE80211_PARAM_ALT_CHAN: |
| param[0] = ic->ic_ieee_alt_chan; |
| break; |
| case IEEE80211_PARAM_LDPC_ALLOW_NON_QTN: |
| param[0] = (vap->iv_ht_flags & IEEE80211_HTF_LDPC_ALLOW_NON_QTN) ? 1 : 0; |
| break; |
| case IEEE80211_PARAM_FWD_UNKNOWN_MC: |
| param[0] = vap->iv_forward_unknown_mc; |
| break; |
| case IEEE80211_PARAM_MC_TO_UC: |
| param[0] = vap->iv_mc_to_uc; |
| break; |
| case IEEE80211_PARAM_BCST_4: |
| param[0] = vap->iv_reliable_bcst; |
| break; |
| case IEEE80211_PARAM_AP_FWD_LNCB: |
| param[0] = vap->iv_ap_fwd_lncb; |
| break; |
| case IEEE80211_PARAM_GI_SELECT: |
| param[0] = ic->ic_gi_select_enable; |
| break; |
| case IEEE80211_PARAM_PPPC_SELECT: |
| if ((ic->ic_flags & IEEE80211_F_DOTH) && (ic->ic_flags_ext & IEEE80211_FEXT_TPC)) { |
| return -EOPNOTSUPP; |
| } else { |
| param[0] = ic->ic_pppc_select_enable; |
| } |
| break; |
| case IEEE80211_PARAM_PPPC_STEP: |
| if ((ic->ic_flags & IEEE80211_F_DOTH) && (ic->ic_flags_ext & IEEE80211_FEXT_TPC)) { |
| return -EOPNOTSUPP; |
| } else { |
| param[0] = ic->ic_pppc_step_db; |
| } |
| break; |
| case IEEE80211_PARAM_EMI_POWER_SWITCHING: |
| param[0] = ic->ic_emi_power_switch_enable; |
| break; |
| case IEEE80211_PARAM_GET_DFS_CCE: |
| param[0] = (ic->ic_dfs_cce.cce_previous << IEEE80211_CCE_PREV_CHAN_SHIFT) | |
| ic->ic_dfs_cce.cce_current; |
| break; |
| case IEEE80211_PARAM_GET_SCS_CCE: |
| param[0] = (ic->ic_aci_cci_cce.cce_previous << IEEE80211_CCE_PREV_CHAN_SHIFT) | |
| ic->ic_aci_cci_cce.cce_current; |
| break; |
| case IEEE80211_PARAM_ASSOC_LIMIT: |
| param[0] = ic->ic_sta_assoc_limit; |
| break; |
| case IEEE80211_PARAM_HW_BONDING: |
| param[0] = soc_shared_params->hardware_options; |
| break; |
| case IEEE80211_PARAM_BSS_ASSOC_LIMIT: |
| { |
| uint32_t group = (((uint32_t)param[0]) >> 16) & 0xffff; |
| |
| if (group < IEEE80211_MIN_BSS_GROUP |
| || group >= IEEE80211_MAX_BSS_GROUP) { |
| return -EINVAL; |
| } |
| |
| param[0] = ic->ic_ssid_grp[group].limit; |
| } |
| break; |
| case IEEE80211_PARAM_BSS_GROUP_ID: |
| param[0] = vap->iv_ssid_group; |
| break; |
| case IEEE80211_PARAM_BSS_ASSOC_RESERVE: |
| { |
| uint32_t group = (((uint32_t)param[0]) >> 16) & 0xffff; |
| |
| if (group < IEEE80211_MIN_BSS_GROUP |
| || group >= IEEE80211_MAX_BSS_GROUP) { |
| return -EINVAL; |
| } |
| |
| param[0] = ic->ic_ssid_grp[group].reserve; |
| } |
| break; |
| case IEEE80211_PARAM_IOT_TWEAKS: |
| param[0] = qtn_mproc_sync_shared_params_get()->iot_tweaks; |
| break; |
| case IEEE80211_PARAM_FAST_REASSOC: |
| param[0] = !!(ic->ic_flags_ext & IEEE80211_FEXT_SCAN_FAST_REASS); |
| break; |
| case IEEE80211_PARAM_CSA_FLAG: |
| param[0] = ic->ic_csa_flag; |
| break; |
| case IEEE80211_PARAM_DEF_MATRIX: |
| param[0] = ic->ic_def_matrix; |
| break; |
| case IEEE80211_PARAM_ENABLE_11AC: |
| param[0] = vap->iv_11ac_enabled; |
| break; |
| case IEEE80211_PARAM_FIXED_11AC_TX_RATE: |
| if ((vap->iv_mcs_config & IEEE80211_RATE_PREFIX_MASK) == |
| IEEE80211_AC_RATE_PREFIX) { |
| param[0] = vap->iv_mcs_config & 0x0F; |
| } else { |
| printk("VHT rate is not set\n"); |
| return -EOPNOTSUPP; |
| } |
| break; |
| case IEEE80211_PARAM_VAP_PRI: |
| param[0] = vap->iv_pri; |
| break; |
| case IEEE80211_PARAM_AIRFAIR: |
| param[0] = ic->ic_airfair; |
| break; |
| case IEEE80211_PARAM_TDLS_STATUS: |
| if ((vap->iv_flags_ext & IEEE80211_FEXT_TDLS_PROHIB) == IEEE80211_FEXT_TDLS_PROHIB) |
| param[0] = 0; |
| else |
| param[0] = 1; |
| break; |
| case IEEE80211_PARAM_TDLS_MODE: |
| param[0] = vap->tdls_path_sel_prohibited; |
| break; |
| case IEEE80211_PARAM_TDLS_TIMEOUT_TIME: |
| param[0] = vap->tdls_timeout_time; |
| break; |
| case IEEE80211_PARAM_TDLS_PATH_SEL_WEIGHT: |
| param[0] = vap->tdls_path_sel_weight; |
| break; |
| case IEEE80211_PARAM_TDLS_TRAINING_PKT_CNT: |
| param[0] = vap->tdls_training_pkt_cnt; |
| break; |
| case IEEE80211_PARAM_TDLS_DISC_INT: |
| param[0] = vap->tdls_discovery_interval; |
| break; |
| case IEEE80211_PARAM_TDLS_PATH_SEL_PPS_THRSHLD: |
| param[0] = vap->tdls_path_sel_pps_thrshld; |
| break; |
| case IEEE80211_PARAM_TDLS_PATH_SEL_RATE_THRSHLD: |
| param[0] = vap->tdls_path_sel_rate_thrshld; |
| break; |
| case IEEE80211_PARAM_TDLS_VERBOSE: |
| param[0] = vap->tdls_verbose; |
| break; |
| case IEEE80211_PARAM_TDLS_MIN_RSSI: |
| param[0] = vap->tdls_min_valid_rssi; |
| break; |
| case IEEE80211_PARAM_TDLS_SWITCH_INTS: |
| param[0] = vap->tdls_switch_ints; |
| break; |
| case IEEE80211_PARAM_TDLS_RATE_WEIGHT: |
| param[0] = vap->tdls_phy_rate_wgt; |
| break; |
| case IEEE80211_PARAM_TDLS_UAPSD_INDICAT_WND: |
| param[0] = vap->tdls_uapsd_indicat_wnd; |
| break; |
| case IEEE80211_PARAM_TDLS_CS_MODE: |
| if (vap->iv_flags_ext & IEEE80211_FEXT_TDLS_CS_PASSIVE) |
| param[0] = 2; |
| else if (vap->iv_flags_ext & IEEE80211_FEXT_TDLS_CS_PROHIB) |
| param[0] = 1; |
| else |
| param[0] = 0; |
| break; |
| case IEEE80211_PARAM_TDLS_OFF_CHAN: |
| param[0] = vap->tdls_fixed_off_chan; |
| break; |
| case IEEE80211_PARAM_TDLS_OFF_CHAN_BW: |
| param[0] = vap->tdls_fixed_off_chan_bw; |
| break; |
| case IEEE80211_PARAM_TDLS_NODE_LIFE_CYCLE: |
| param[0] = vap->tdls_node_life_cycle; |
| break; |
| case IEEE80211_PARAM_TDLS_OVER_QHOP_ENABLE: |
| param[0] = vap->tdls_over_qhop_en; |
| break; |
| case IEEE80211_PARAM_OCAC: |
| case IEEE80211_PARAM_SDFS: |
| { |
| int retval = ieee80211_param_ocac_get(vap, param); |
| if (retval != 0) { |
| return retval; |
| } |
| } |
| break; |
| case IEEE80211_PARAM_DEACTIVE_CHAN_PRI: |
| param[0] = ieee80211_get_inactive_primary_chan_num(ic); |
| break; |
| case IEEE80211_PARAM_SPECIFIC_SCAN: |
| param[0] = (vap->iv_flags_ext & IEEE80211_FEXT_SPECIFIC_SCAN) ? 1 : 0; |
| break; |
| case IEEE80211_PARAM_FIXED_SGI: |
| param[0] = ic->ic_gi_fixed; |
| break; |
| case IEEE80211_PARAM_FIXED_BW: |
| param[0] = ic->ic_bw_fixed; |
| break; |
| case IEEE80211_PARAM_SPEC_COUNTRY_CODE: |
| { |
| union { |
| char as_chars[4]; |
| uint32_t as_u32; |
| } region; |
| |
| if (ieee80211_countryid_to_country_string(ic->ic_spec_country_code, |
| region.as_chars) != 0) { |
| /* |
| * If we can't get a country string from the current code, |
| * return the NULL string as the region. |
| */ |
| region.as_u32 = 0; |
| } |
| |
| param[0] = (int)region.as_u32; |
| } |
| break; |
| case IEEE80211_PARAM_VCO_LOCK_DETECT_MODE: |
| { |
| param[0] = sp->vco_lock_detect_mode; |
| } |
| break; |
| case IEEE80211_PARAM_CONFIG_PMF: |
| param[0] = vap->iv_pmf; |
| break; |
| case IEEE80211_PARAM_RX_AMSDU_ENABLE: |
| param[0] = vap->iv_rx_amsdu_enable; |
| break; |
| case IEEE80211_PARAM_RX_AMSDU_THRESHOLD_CCA: |
| param[0] = vap->iv_rx_amsdu_threshold_cca; |
| break; |
| case IEEE80211_PARAM_RX_AMSDU_THRESHOLD_PMBL: |
| param[0] = vap->iv_rx_amsdu_threshold_pmbl; |
| break; |
| case IEEE80211_PARAM_RX_AMSDU_PMBL_WF_SP: |
| param[0] = vap->iv_rx_amsdu_pmbl_wf_sp; |
| break; |
| case IEEE80211_PARAM_RX_AMSDU_PMBL_WF_LP: |
| param[0] = vap->iv_rx_amsdu_pmbl_wf_lp; |
| break; |
| case IEEE80211_PARAM_INTRA_BSS_ISOLATE: |
| param[0] = !!(dev->qtn_flags & QTN_FLAG_INTRA_BSS_ISOLATE); |
| break; |
| case IEEE80211_PARAM_BSS_ISOLATE: |
| param[0] = !!(dev->qtn_flags & QTN_FLAG_BSS_ISOLATE); |
| break; |
| case IEEE80211_PARAM_BF_RX_STS: |
| param[0] = ic->ic_vhtcap.bfstscap + 1; |
| break; |
| case IEEE80211_PARAM_PC_OVERRIDE: |
| param[0] = ((ic->ic_pwr_constraint)|(ic->ic_pco.pco_set<<8)); |
| break; |
| case IEEE80211_PARAM_WOWLAN: |
| param[0] = ieee80211_param_wowlan_get(ic); |
| break; |
| case IEEE80211_PARAM_SCAN_TBL_LEN_MAX: |
| param[0] = ic->ic_scan_tbl_len_max; |
| break; |
| case IEEE80211_PARAM_WDS_MODE: |
| param[0] = IEEE80211_VAP_WDS_IS_RBS(vap) ? 1 : IEEE80211_VAP_WDS_IS_MBS(vap) ? 0 : 2; |
| ieee80211_extdr_dump_flags(vap); |
| break; |
| case IEEE80211_PARAM_EXTENDER_ROLE: |
| param[0] = ic->ic_extender_role; |
| break; |
| case IEEE80211_PARAM_EXTENDER_MBS_BEST_RSSI: |
| param[0] = ic->ic_extender_mbs_best_rssi; |
| break; |
| case IEEE80211_PARAM_EXTENDER_RBS_BEST_RSSI: |
| param[0] = ic->ic_extender_rbs_best_rssi; |
| break; |
| case IEEE80211_PARAM_EXTENDER_MBS_WGT: |
| param[0] = ic->ic_extender_mbs_wgt; |
| break; |
| case IEEE80211_PARAM_EXTENDER_RBS_WGT: |
| param[0] = ic->ic_extender_rbs_wgt; |
| break; |
| case IEEE80211_PARAM_EXTENDER_VERBOSE: |
| param[0] = ic->ic_extender_verbose; |
| break; |
| case IEEE80211_PARAM_VAP_TX_AMSDU: |
| param[0] = vap->iv_tx_amsdu; |
| break; |
| case IEEE80211_PARAM_TX_MAXMPDU: |
| param[0] = vap->iv_tx_max_amsdu; |
| break; |
| case IEEE80211_PARAM_DISASSOC_REASON: |
| param[0] = vap->iv_disassoc_reason; |
| break; |
| case IEEE80211_PARAM_BB_PARAM: |
| { |
| param[0] = sp->bb_param; |
| } |
| break; |
| case IEEE80211_PARAM_NDPA_DUR: |
| param[0] = ic->ic_ndpa_dur; |
| break; |
| case IEEE80211_PARAM_SU_TXBF_PKT_CNT: |
| param[0] = ic->ic_su_txbf_pkt_cnt; |
| break; |
| case IEEE80211_PARAM_MU_TXBF_PKT_CNT: |
| param[0] = ic->ic_mu_txbf_pkt_cnt; |
| break; |
| case IEEE80211_PARAM_SCAN_RESULTS_CHECK_INV: |
| param[0] = ic->ic_scan_results_check; |
| break; |
| case IEEE80211_PARAM_TQEW_DESCR_LIMIT: |
| param[0] = ic->ic_tqew_descr_limit; |
| break; |
| case IEEE80211_PARAM_CS_THRESHOLD: |
| param[0] = sp->cs_thresh_base_val; |
| break; |
| case IEEE80211_PARAM_L2_EXT_FILTER: |
| param[0] = g_l2_ext_filter; |
| break; |
| case IEEE80211_PARAM_L2_EXT_FILTER_PORT: |
| param[0] = ic->ic_get_l2_ext_filter_port(); |
| break; |
| case IEEE80211_PARAM_OBSS_TRIGG_SCAN_INT: |
| if ((ic->ic_opmode == IEEE80211_M_STA) && |
| (vap->iv_bss) && |
| (IEEE80211_AID(vap->iv_bss->ni_associd))) |
| param[0] = vap->iv_bss->ni_obss_ie.obss_trigger_interval; |
| else |
| param[0] = ic->ic_obss_ie.obss_trigger_interval; |
| break; |
| case IEEE80211_PARAM_PREF_BAND: |
| param[0] = vap->iv_pref_band; |
| break; |
| case IEEE80211_PARAM_BW_2_4GHZ: |
| if (IS_IEEE80211_24G_BAND(ic)) |
| param[0] = ieee80211_get_bw(ic); |
| else |
| return -EINVAL; |
| break; |
| case IEEE80211_PARAM_ALLOW_VHT_TKIP: |
| param[0] = vap->allow_tkip_for_vht; |
| break; |
| case IEEE80211_PARAM_VHT_OPMODE_NOTIF: |
| param[0] = ic->ic_vht_opmode_notif; |
| break; |
| case IEEE80211_PARAM_QTN_BLOCK_BSS: |
| param[0] = !!(vap->is_block_all_assoc); |
| break; |
| case IEEE80211_PARAM_VHT_2_4GHZ: |
| param[0] = !!(ic->ic_flags_ext & IEEE80211_FEXT_24GVHT); |
| break; |
| case IEEE80211_PARAM_BEACONING_SCHEME: |
| param[0] = ic->ic_beaconing_scheme; |
| break; |
| #if defined(QBMPS_ENABLE) |
| case IEEE80211_PARAM_STA_BMPS: |
| param[0] = qv->qv_bmps_mode; |
| break; |
| #endif |
| case IEEE80211_PARAM_40MHZ_INTOLERANT: |
| if (IEEE80211_IS_11B(ic) || IEEE80211_IS_11G(ic)) |
| param[0] = !!(vap->iv_coex & WLAN_20_40_BSS_COEX_40MHZ_INTOL); |
| else if (IS_IEEE80211_11NG(ic)) |
| param[0] = !!(ic->ic_htcap.cap & IEEE80211_HTCAP_C_40_INTOLERANT); |
| else |
| param[0] = 0; |
| break; |
| case IEEE80211_PARAM_SET_RTS_BW_DYN: |
| param[0] = ic->ic_rts_bw_dyn; |
| break; |
| case IEEE80211_PARAM_SET_DUP_RTS: |
| param[0] = ic->ic_dup_rts; |
| break; |
| case IEEE80211_PARAM_SET_CTS_BW: |
| param[0] = ic->ic_cts_bw; |
| break; |
| case IEEE80211_PARAM_USE_NON_HT_DUPLICATE_MU: |
| param[0] = ic->use_non_ht_duplicate_for_mu; |
| break; |
| case IEEE80211_PARAM_DISABLE_TX_BA: |
| param[0] = vap->tx_ba_disable; |
| break; |
| case IEEE80211_PARAM_DECLINE_RX_BA: |
| param[0] = vap->rx_ba_decline; |
| break; |
| case IEEE80211_PARAM_VAP_STATE: |
| param[0] = vap->iv_vap_state; |
| break; |
| case IEEE80211_PARAM_OBSS_SCAN: |
| param[0] = ic->ic_obss_scan_enable; |
| break; |
| case IEEE80211_PARAM_SHORT_SLOT: |
| param[0] = !!(ic->ic_caps & IEEE80211_C_SHSLOT); |
| break; |
| case IEEE80211_PARAM_BG_PROTECT: |
| param[0] = !!(ic->ic_flags_ext & IEEE80211_FEXT_BG_PROTECT); |
| break; |
| case IEEE80211_PARAM_11N_PROTECT: |
| param[0] = !!(ic->ic_flags_ext & IEEE80211_FEXT_11N_PROTECT); |
| break; |
| case IEEE80211_PARAM_MU_NDPA_BW_SIGNALING_SUPPORT: |
| param[0] = ic->rx_bws_support_for_mu_ndpa; |
| break; |
| case IEEE80211_PARAM_WPA_STARTED: |
| case IEEE80211_PARAM_HOSTAP_STARTED: |
| param[0] = ic->hostap_wpa_state; |
| break; |
| case IEEE80211_PARAM_RX_BAR_SYNC: |
| param[0] = ic->ic_rx_bar_sync; |
| break; |
| case IEEE80211_PARAM_GET_REG_DOMAIN_IS_EU: |
| param[0] = ic->ic_dfs_is_eu_region(); |
| break; |
| case IEEE80211_PARAM_GET_CHAN_AVAILABILITY_STATUS: |
| param[0] = 0; |
| ic->ic_dump_chan_availability_status(ic); |
| break; |
| case IEEE80211_PARAM_NEIGHBORHOOD_THRSHD: |
| if (vap->iv_opmode != IEEE80211_M_HOSTAP) |
| return -EINVAL; |
| |
| param[0] = ieee80211_get_threshold_of_neighborhood_type(ic, (((uint32_t)param[0]) >> 16) & 0xFFFF); |
| break; |
| case IEEE80211_PARAM_NEIGHBORHOOD_TYPE: |
| if (vap->iv_opmode != IEEE80211_M_HOSTAP && vap->iv_opmode != IEEE80211_M_STA) |
| return -EINVAL; |
| |
| param[0] = ieee80211_get_type_of_neighborhood(ic); |
| break; |
| case IEEE80211_PARAM_NEIGHBORHOOD_COUNT: |
| if (vap->iv_opmode != IEEE80211_M_HOSTAP && vap->iv_opmode != IEEE80211_M_STA) |
| return -EINVAL; |
| |
| param[0] = ic->ic_neighbor_count; |
| break; |
| case IEEE80211_PARAM_STA_DFS_STRICT_MODE: |
| if ((vap->iv_opmode != IEEE80211_M_STA) || (!(ic->ic_dfs_is_eu_region()))) { |
| return -EINVAL; |
| } |
| param[0] = ic->sta_dfs_info.sta_dfs_strict_mode; |
| break; |
| case IEEE80211_PARAM_STA_DFS_STRICT_MEASUREMENT_IN_CAC: |
| if ((vap->iv_opmode != IEEE80211_M_STA) || (!(ic->ic_dfs_is_eu_region()))) { |
| return -EINVAL; |
| } |
| param[0] = ic->sta_dfs_info.sta_dfs_strict_msr_cac; |
| break; |
| case IEEE80211_PARAM_STA_DFS_STRICT_TX_CHAN_CLOSE_TIME: |
| if ((vap->iv_opmode != IEEE80211_M_STA) || (!(ic->ic_dfs_is_eu_region()))) { |
| return -EINVAL; |
| } |
| param[0] = ic->sta_dfs_info.sta_dfs_tx_chan_close_time; |
| break; |
| case IEEE80211_PARAM_RADAR_NONOCCUPY_PERIOD: |
| param[0] = (ic->ic_non_occupancy_period / HZ); |
| break; |
| case IEEE80211_PARAM_DFS_CSA_CNT: |
| param[0] = ic->ic_dfs_csa_cnt; |
| break; |
| case IEEE80211_PARAM_IS_WEATHER_CHANNEL: |
| { |
| struct ieee80211_channel *chan = ieee80211_find_channel_by_ieee(ic, |
| (((uint32_t)param[0]) >> 16) & 0xFFFF); |
| if (chan) |
| param[0] = ieee80211_is_on_weather_channel(ic, chan); |
| else |
| return -EINVAL; |
| break; |
| } |
| case IEEE80211_PARAM_VAP_DBG: |
| param[0] = vap->iv_debug; |
| break; |
| case IEEE80211_PARAM_MIN_CAC_PERIOD: |
| param[0] = MIN_CAC_PERIOD; |
| break; |
| case IEEE80211_PARAM_DEVICE_MODE: |
| if (ic->ic_extender_role == IEEE80211_EXTENDER_ROLE_MBS) |
| param[0] = IEEE80211_DEV_MODE_MBS; |
| else if (ic->ic_extender_role == IEEE80211_EXTENDER_ROLE_RBS) |
| param[0] = IEEE80211_DEV_MODE_RBS; |
| else if (ieee80211_is_repeater(ic)) |
| param[0] = IEEE80211_DEV_MODE_REPEATER; |
| else |
| param[0] = IEEE80211_DEV_MODE_UNKNOWN; |
| break; |
| case IEEE80211_PARAM_SYNC_CONFIG: |
| param[0] = (vap->iv_flags_ext2 & IEEE80211_FEXT_SYNC_CONFIG) != 0; |
| break; |
| case IEEE80211_PARAM_AUTOCHAN_DBG_LEVEL: |
| param[0] = ic->ic_autochan_dbg_level; |
| break; |
| #ifdef CONFIG_NAC_MONITOR |
| case IEEE80211_PARAM_NAC_MONITOR_MODE: |
| { |
| struct shared_params *sp = qtn_mproc_sync_shared_params_get(); |
| struct nac_mon_info *info = sp->nac_mon_info; |
| param[0] = (info->nac_monitor_on & 0xff) | |
| (((((int)info->nac_on_time * 100) / (int)info->nac_cycle_time) & 0xff) << 8) | |
| (((int)info->nac_cycle_time & 0xffff) << 16); |
| |
| } |
| break; |
| #endif |
| case IEEE80211_PARAM_MAX_DEVICE_BW: |
| param[0] = ic->ic_max_system_bw; |
| break; |
| case IEEE80211_PARAM_VOPT: |
| if (ic->ic_opmode == IEEE80211_M_HOSTAP) |
| param[0] = (ic->ic_vopt.state & 0x0f) << 4 | ic->ic_vopt.cur_state; |
| else |
| param[0] = 0; |
| break; |
| case IEEE80211_PARAM_BW_AUTO_SELECT: |
| param[0] = ic->ic_bw_auto_select; |
| break; |
| case IEEE80211_PARAM_DFS_CHANS_AVAILABLE_FOR_DFS_REENTRY: |
| param[0] = ic->ic_is_dfs_chans_available_for_dfs_reentry(ic, vap); |
| break; |
| #ifdef CONFIG_QHOP |
| case IEEE80211_PARAM_RBS_MBS_ALLOW_TX_FRMS_IN_CAC: |
| param[0] = ic->rbs_mbs_dfs_info.rbs_mbs_allow_tx_frms_in_cac; |
| break; |
| case IEEE80211_PARAM_RBS_DFS_TX_CHAN_CLOSE_TIME: |
| param[0] = ic->rbs_mbs_dfs_info.rbs_dfs_tx_chan_close_time; |
| break; |
| #endif |
| case IEEE80211_PARAM_AUTOCHAN_CCI_INSTNT: |
| param[0] = ic->ic_autochan_ranking_params.cci_instnt_factor; |
| break; |
| case IEEE80211_PARAM_AUTOCHAN_ACI_INSTNT: |
| param[0] = ic->ic_autochan_ranking_params.aci_instnt_factor; |
| break; |
| case IEEE80211_PARAM_AUTOCHAN_CCI_LONGTERM: |
| param[0] = ic->ic_autochan_ranking_params.cci_longterm_factor; |
| break; |
| case IEEE80211_PARAM_AUTOCHAN_ACI_LONGTERM: |
| param[0] = ic->ic_autochan_ranking_params.aci_longterm_factor; |
| break; |
| case IEEE80211_PARAM_AUTOCHAN_RANGE_COST: |
| param[0] = ic->ic_autochan_ranking_params.range_factor; |
| break; |
| case IEEE80211_PARAM_AUTOCHAN_DFS_COST: |
| param[0] = ic->ic_autochan_ranking_params.dfs_factor; |
| break; |
| case IEEE80211_PARAM_AUTOCHAN_MIN_CCI_RSSI: |
| param[0] = ic->ic_autochan_ranking_params.min_cochan_rssi; |
| break; |
| case IEEE80211_PARAM_AUTOCHAN_MAXBW_MINBENEFIT: |
| param[0] = ic->ic_autochan_ranking_params.maxbw_minbenefit; |
| break; |
| case IEEE80211_PARAM_AUTOCHAN_DENSE_CCI_SPAN: |
| param[0] = ic->ic_autochan_ranking_params.dense_cci_span; |
| break; |
| case IEEE80211_PARAM_WEATHERCHAN_CAC_ALLOWED: |
| param[0] = ic->ic_weachan_cac_allowed; |
| break; |
| case IEEE80211_PARAM_VAP_TX_AMSDU_11N: |
| param[0] = vap->iv_tx_amsdu_11n; |
| break; |
| case IEEE80211_PARAM_COC_MOVE_TO_NONDFS_CHANNEL: |
| param[0] = ic->ic_coc_move_to_ndfs; |
| break; |
| case IEEE80211_PARAM_80211K_NEIGH_REPORT: |
| param[0] = IEEE80211_COM_NEIGHREPORT_ENABLED(ic)? 1 : 0; |
| break; |
| case IEEE80211_PARAM_80211V_BTM: |
| param[0] = IEEE80211_COM_BTM_ENABLED(ic)? 1 : 0 ; |
| break; |
| case IEEE80211_PARAM_MOBILITY_DOMAIN: |
| param[0] = vap->iv_mdid; |
| break; |
| default: |
| return -EOPNOTSUPP; |
| } |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_getblockdata(struct net_device *dev, struct iw_request_info *info, |
| void *w, char *extra) |
| { |
| |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct iw_point *iwp = (struct iw_point *)w; |
| int subcmd = iwp->flags; |
| |
| (void) ic; |
| |
| switch (subcmd) { |
| case IEEE80211_PARAM_ASSOC_HISTORY: |
| { |
| struct ieee80211_assoc_history *ah = &ic->ic_assoc_history; |
| |
| iwp->length = sizeof(*ah); |
| memcpy(extra, ah, iwp->length); |
| } |
| break; |
| case IEEE80211_PARAM_CSW_RECORD: |
| { |
| struct ieee80211req_csw_record * record = &ic->ic_csw_record; |
| |
| iwp->length = sizeof(struct ieee80211req_csw_record); |
| memcpy(extra, record, iwp->length); |
| } |
| break; |
| case IEEE80211_PARAM_PWR_SAVE: |
| { |
| iwp->length = sizeof(ic->ic_pm_state); |
| memcpy(extra, ic->ic_pm_state, sizeof(ic->ic_pm_state)); |
| } |
| break; |
| default: |
| return -EOPNOTSUPP; |
| } |
| |
| return 0; |
| } |
| |
| /* returns non-zero if ID is for a system IE (not for app use) */ |
| static int |
| is_sys_ie(u_int8_t ie_id) |
| { |
| /* XXX review this list */ |
| switch (ie_id) { |
| case IEEE80211_ELEMID_SSID: |
| case IEEE80211_ELEMID_RATES: |
| case IEEE80211_ELEMID_FHPARMS: |
| case IEEE80211_ELEMID_DSPARMS: |
| case IEEE80211_ELEMID_CFPARMS: |
| case IEEE80211_ELEMID_TIM: |
| case IEEE80211_ELEMID_IBSSPARMS: |
| case IEEE80211_ELEMID_COUNTRY: |
| case IEEE80211_ELEMID_REQINFO: |
| case IEEE80211_ELEMID_CHALLENGE: |
| case IEEE80211_ELEMID_PWRCNSTR: |
| case IEEE80211_ELEMID_PWRCAP: |
| case IEEE80211_ELEMID_TPCREQ: |
| case IEEE80211_ELEMID_TPCREP: |
| case IEEE80211_ELEMID_SUPPCHAN: |
| case IEEE80211_ELEMID_CHANSWITCHANN: |
| case IEEE80211_ELEMID_MEASREQ: |
| case IEEE80211_ELEMID_MEASREP: |
| case IEEE80211_ELEMID_QUIET: |
| case IEEE80211_ELEMID_IBSSDFS: |
| case IEEE80211_ELEMID_ERP: |
| case IEEE80211_ELEMID_RSN: |
| case IEEE80211_ELEMID_XRATES: |
| case IEEE80211_ELEMID_TPC: |
| case IEEE80211_ELEMID_CCKM: |
| return 1; |
| default: |
| return 0; |
| } |
| } |
| |
| /* returns non-zero if the buffer appears to contain a valid IE list */ |
| static int |
| is_valid_ie_list(u_int32_t buf_len, void *buf, int exclude_sys_ies) |
| { |
| struct ieee80211_ie *ie = (struct ieee80211_ie *)buf; |
| |
| while (buf_len >= sizeof(*ie)) { |
| int ie_elem_len = sizeof(*ie) + ie->len; |
| if (buf_len < ie_elem_len) |
| break; |
| if (exclude_sys_ies && is_sys_ie(ie->id)) |
| break; |
| buf_len -= ie_elem_len; |
| ie = (struct ieee80211_ie *)(ie->info + ie->len); |
| } |
| |
| return (buf_len == 0) ? 1 : 0; |
| } |
| |
| static int |
| ieee80211_ioctl_setoptie(struct net_device *dev, struct iw_request_info *info, |
| struct iw_point *wri, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| void *ie; |
| |
| /* |
| * NB: Doing this for ap operation could be useful (e.g. for |
| * WPA and/or WME) except that it typically is worthless |
| * without being able to intervene when processing |
| * association response frames--so disallow it for now. |
| */ |
| if (vap->iv_opmode != IEEE80211_M_STA) |
| return -EINVAL; |
| if (! is_valid_ie_list(wri->length, extra, 0)) |
| return -EINVAL; |
| /* NB: wri->length is validated by the wireless extensions code */ |
| MALLOC(ie, void *, wri->length, M_DEVBUF, M_WAITOK); |
| if (ie == NULL) |
| return -ENOMEM; |
| memcpy(ie, extra, wri->length); |
| if (vap->iv_opt_ie != NULL) |
| FREE(vap->iv_opt_ie, M_DEVBUF); |
| vap->iv_opt_ie = ie; |
| vap->iv_opt_ie_len = wri->length; |
| |
| ieee80211_parse_cipher_key(vap, vap->iv_opt_ie, vap->iv_opt_ie_len); |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_getoptie(struct net_device *dev, struct iw_request_info *info, |
| struct iw_point *wri, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| |
| if (vap->iv_opt_ie == NULL) { |
| wri->length = 0; |
| return 0; |
| } |
| wri->length = vap->iv_opt_ie_len; |
| memcpy(extra, vap->iv_opt_ie, vap->iv_opt_ie_len); |
| return 0; |
| } |
| |
| /* the following functions are used by the set/get appiebuf functions */ |
| static int |
| add_app_ie(unsigned int frame_type_index, struct ieee80211vap *vap, |
| struct ieee80211req_getset_appiebuf *iebuf) |
| { |
| struct ieee80211_ie *ie; |
| |
| if (! is_valid_ie_list(iebuf->app_buflen, iebuf->app_buf, 1)) |
| return -EINVAL; |
| /* NB: data.length is validated by the wireless extensions code */ |
| MALLOC(ie, struct ieee80211_ie *, iebuf->app_buflen, M_DEVBUF, M_WAITOK); |
| if (ie == NULL) |
| return -ENOMEM; |
| |
| memcpy(ie, iebuf->app_buf, iebuf->app_buflen); |
| if (vap->app_ie[frame_type_index].ie != NULL) |
| FREE(vap->app_ie[frame_type_index].ie, M_DEVBUF); |
| vap->app_ie[frame_type_index].ie = ie; |
| vap->app_ie[frame_type_index].length = iebuf->app_buflen; |
| |
| return 0; |
| } |
| |
| static int |
| remove_app_ie(unsigned int frame_type_index, struct ieee80211vap *vap) |
| { |
| struct ieee80211_app_ie_t *app_ie = &vap->app_ie[frame_type_index]; |
| if (app_ie->ie != NULL) { |
| FREE(app_ie->ie, M_DEVBUF); |
| app_ie->ie = NULL; |
| app_ie->length = 0; |
| } |
| return 0; |
| } |
| |
| static int |
| get_app_ie(unsigned int frame_type_index, struct ieee80211vap *vap, |
| struct ieee80211req_getset_appiebuf *iebuf) |
| { |
| struct ieee80211_app_ie_t *app_ie = &vap->app_ie[frame_type_index]; |
| if (iebuf->app_buflen < app_ie->length) |
| return -EINVAL; |
| |
| iebuf->app_buflen = app_ie->length; |
| memcpy(iebuf->app_buf, app_ie->ie, app_ie->length); |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_setappiebuf(struct net_device *dev, |
| struct iw_request_info *info, |
| struct iw_point *data, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211req_getset_appiebuf *iebuf = |
| (struct ieee80211req_getset_appiebuf *)extra; |
| struct ieee80211_ie *ie; |
| enum ieee80211_opmode chk_opmode; |
| int iebuf_len; |
| int rc = 0; |
| |
| iebuf_len = data->length - sizeof(struct ieee80211req_getset_appiebuf); |
| if ( iebuf_len < 0 || iebuf_len != iebuf->app_buflen || |
| iebuf->app_buflen > IEEE80211_APPIE_MAX ) |
| return -EINVAL; |
| |
| switch (iebuf->app_frmtype) { |
| case IEEE80211_APPIE_FRAME_BEACON: |
| case IEEE80211_APPIE_FRAME_PROBE_RESP: |
| case IEEE80211_APPIE_FRAME_ASSOC_RESP: |
| chk_opmode = IEEE80211_M_HOSTAP; |
| break; |
| case IEEE80211_APPIE_FRAME_PROBE_REQ: |
| case IEEE80211_APPIE_FRAME_ASSOC_REQ: |
| case IEEE80211_APPIE_FRAME_TDLS_ACT: |
| chk_opmode = IEEE80211_M_STA; |
| break; |
| default: |
| return -EINVAL; |
| } |
| if (vap->iv_opmode != chk_opmode) |
| return -EINVAL; |
| |
| if (iebuf->app_frmtype == IEEE80211_APPIE_FRAME_TDLS_ACT) { |
| rc = ieee80211_tdls_send_action_frame(dev, |
| (struct ieee80211_tdls_action_data *)iebuf->app_buf); |
| return rc; |
| } |
| |
| if (iebuf->app_buflen) { |
| |
| if ((iebuf->app_frmtype == IEEE80211_APPIE_FRAME_ASSOC_REQ || |
| iebuf->app_frmtype == IEEE80211_APPIE_FRAME_ASSOC_RESP) && |
| iebuf->flags == F_QTN_IEEE80211_PAIRING_IE) { |
| |
| MALLOC(ie, struct ieee80211_ie *, iebuf->app_buflen, M_DEVBUF, M_WAITOK); |
| |
| if (ie == NULL) |
| return -ENOMEM; |
| |
| memcpy(ie, iebuf->app_buf, iebuf->app_buflen); |
| |
| if (vap->qtn_pairing_ie.ie != NULL) |
| FREE(vap->qtn_pairing_ie.ie, M_DEVBUF); |
| vap->qtn_pairing_ie.ie = ie; |
| vap->qtn_pairing_ie.length = iebuf->app_buflen; |
| |
| return 0; |
| |
| } |
| |
| rc = add_app_ie(iebuf->app_frmtype, vap, iebuf); |
| } else { |
| if ((iebuf->app_frmtype == IEEE80211_APPIE_FRAME_ASSOC_REQ || |
| iebuf->app_frmtype == IEEE80211_APPIE_FRAME_ASSOC_RESP) && |
| iebuf->flags == F_QTN_IEEE80211_PAIRING_IE) { |
| if (vap->qtn_pairing_ie.ie != NULL) { |
| FREE(vap->qtn_pairing_ie.ie, M_DEVBUF); |
| vap->qtn_pairing_ie.ie = NULL; |
| vap->qtn_pairing_ie.length = 0; |
| } |
| |
| return 0; |
| } |
| |
| rc = remove_app_ie(iebuf->app_frmtype, vap); |
| } |
| if ((iebuf->app_frmtype == IEEE80211_APPIE_FRAME_BEACON) && (rc == 0)) { |
| struct ieee80211com *ic = vap->iv_ic; |
| |
| vap->iv_flags_ext |= IEEE80211_FEXT_APPIE_UPDATE; |
| if ((vap->iv_opmode == IEEE80211_M_HOSTAP) && |
| (vap->iv_state == IEEE80211_S_RUN)) { |
| |
| ic->ic_beacon_update(vap); |
| } |
| } |
| |
| return rc; |
| } |
| |
| static int |
| ieee80211_ioctl_startcca(struct net_device *dev, struct iw_request_info *info, |
| struct iw_point *wri, char *extra) |
| |
| { |
| struct qtn_cca_args *ccaval = (struct qtn_cca_args *)extra; |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_channel *chan = NULL; |
| uint64_t tsf = 0; |
| |
| if (copy_from_user(ccaval, wri->pointer, sizeof(struct qtn_cca_args))) { |
| return -EINVAL; |
| } |
| |
| chan = findchannel(ic, ccaval->cca_channel, ic->ic_des_mode); |
| if (chan == NULL) { |
| printk(KERN_ERR "Invalid channel %d ? \n", ccaval->cca_channel); |
| return -EINVAL; |
| } |
| |
| ic->ic_get_tsf(&tsf); |
| tsf = tsf + IEEE80211_SEC_TO_USEC(1); /* after 1 second */ |
| |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_ROAM, |
| "CCA channel change scheduled at tsf %016llX \n", tsf); |
| |
| ic->ic_cca_start_tsf = tsf; |
| ic->ic_cca_duration_tu = IEEE80211_MS_TO_TU(ccaval->duration); |
| ic->ic_cca_chan = chan->ic_ieee; |
| |
| ic->ic_flags |= IEEE80211_F_CCA; |
| ic->ic_cca_token++; |
| |
| TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { |
| if (vap->iv_opmode != IEEE80211_M_HOSTAP) |
| continue; |
| |
| if ((vap->iv_state != IEEE80211_S_RUN) && (vap->iv_state != IEEE80211_S_SCAN)) |
| continue; |
| ic->ic_beacon_update(vap); |
| } |
| |
| ic->ic_set_start_cca_measurement(ic, chan, tsf, ccaval->duration); |
| wri->length = sizeof(struct qtn_cca_args); |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_getappiebuf(struct net_device *dev, struct iw_request_info *info, |
| struct iw_point *data, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211req_getset_appiebuf *iebuf = |
| (struct ieee80211req_getset_appiebuf *)extra; |
| int max_iebuf_len; |
| int rc = 0; |
| |
| max_iebuf_len = data->length - sizeof(struct ieee80211req_getset_appiebuf); |
| if (max_iebuf_len < 0) |
| return -EINVAL; |
| if (copy_from_user(iebuf, data->pointer, sizeof(struct ieee80211req_getset_appiebuf))) |
| return -EFAULT; |
| if (iebuf->app_buflen > max_iebuf_len) |
| iebuf->app_buflen = max_iebuf_len; |
| |
| switch (iebuf->app_frmtype) { |
| case IEEE80211_APPIE_FRAME_BEACON: |
| case IEEE80211_APPIE_FRAME_PROBE_RESP: |
| case IEEE80211_APPIE_FRAME_ASSOC_RESP: |
| if (vap->iv_opmode == IEEE80211_M_STA) |
| return -EINVAL; |
| break; |
| case IEEE80211_APPIE_FRAME_PROBE_REQ: |
| case IEEE80211_APPIE_FRAME_ASSOC_REQ: |
| if (vap->iv_opmode != IEEE80211_M_STA) |
| return -EINVAL; |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| rc = get_app_ie(iebuf->app_frmtype, vap, iebuf); |
| |
| data->length = sizeof(struct ieee80211req_getset_appiebuf) + iebuf->app_buflen; |
| |
| return rc; |
| } |
| |
| |
| static void |
| wpa_hexdump_key(struct ieee80211vap *vap, int level, |
| const char *title, const u8 *buf, size_t len) |
| { |
| #ifdef IEEE80211_DEBUG |
| int show = level; |
| size_t i; |
| |
| if (!ieee80211_msg_debug(vap)) { |
| return; |
| } |
| |
| printk("\n%s - hexdump(len=%lu):", title, (unsigned long) len); |
| if (buf == NULL) { |
| printk(" [NULL]"); |
| } else if (show) { |
| for (i = 0; i < len; i++) |
| printk("%s%02x", i%4==0? "\n":" ", buf[i]); |
| } else { |
| printk(" [REMOVED]"); |
| } |
| printk("\n"); |
| #endif |
| } |
| |
| static int |
| ieee80211_ioctl_setfilter(struct net_device *dev, struct iw_request_info *info, |
| void *w, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211req_set_filter *app_filter = (struct ieee80211req_set_filter *)extra; |
| |
| if ((extra == NULL) || (app_filter->app_filterype & ~IEEE80211_FILTER_TYPE_ALL)) |
| return -EINVAL; |
| |
| vap->app_filter = app_filter->app_filterype; |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_setkey(struct net_device *dev, struct iw_request_info *info, |
| void *w, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211req_key *ik = (struct ieee80211req_key *)extra; |
| struct ieee80211_node *ni; |
| struct ieee80211_key *wk; |
| u_int8_t kid = ik->ik_keyix; |
| int i; |
| int error; |
| |
| if ((ik->ik_keylen > sizeof(ik->ik_keydata)) || |
| (ik->ik_keylen > sizeof(wk->wk_key))) { |
| return -E2BIG; |
| } |
| |
| if(ik->ik_type == IEEE80211_CIPHER_AES_CMAC) { |
| // 802.11w CMAC / IGTK ignore for now. |
| return 0; |
| } |
| |
| if (kid == IEEE80211_KEYIX_NONE) { |
| /* Unicast key */ |
| kid = 0; |
| } else if (kid >= IEEE80211_WEP_NKID) { |
| return -EINVAL; |
| } |
| |
| /* Group keys */ |
| if (((ik->ik_flags & IEEE80211_KEY_XMIT) == 0) || |
| (ik->ik_flags & IEEE80211_KEY_GROUP) || |
| IEEE80211_IS_MULTICAST(ik->ik_macaddr)) { |
| |
| ik->ik_flags |= IEEE80211_KEY_GROUP; |
| if (vap->iv_opmode == IEEE80211_M_STA) { |
| memset(ik->ik_macaddr, 0xff, IEEE80211_ADDR_LEN); |
| } else if(ik->ik_flags & IEEE80211_KEY_VLANGROUP) { |
| qtn_vlan_gen_group_addr(ik->ik_macaddr, ik->ik_vlan, vap->iv_dev->dev_id); |
| } else { |
| if (NULL == vap->iv_bss) |
| return -EINVAL; |
| IEEE80211_ADDR_COPY(ik->ik_macaddr, vap->iv_bss->ni_macaddr); |
| } |
| } |
| |
| /* wk must be set to ni->ni_ucastkey for sw crypto */ |
| wk = &vap->iv_nw_keys[kid]; |
| wk->wk_ciphertype = ik->ik_type; |
| wk->wk_keylen = ik->ik_keylen; |
| wk->wk_flags = ik->ik_flags; |
| wk->wk_keyix = kid; |
| for (i = 0; i < WME_NUM_TID; i++) { |
| wk->wk_keyrsc[i] = ik->ik_keyrsc; |
| } |
| wk->wk_keytsc = 0; |
| memset(wk->wk_key, 0, sizeof(wk->wk_key)); |
| memcpy(wk->wk_key, ik->ik_keydata, ik->ik_keylen); |
| |
| if (!(ik->ik_flags & IEEE80211_KEY_GROUP)) { |
| ni = ieee80211_find_node(&vap->iv_ic->ic_sta, ik->ik_macaddr); |
| if (ni) { |
| memcpy(&ni->ni_ucastkey, wk, sizeof(ni->ni_ucastkey)); |
| ieee80211_free_node(ni); |
| } |
| if (vap->iv_opmode == IEEE80211_M_WDS) |
| memcpy(&vap->iv_wds_peer_key, wk, sizeof(vap->iv_wds_peer_key)); |
| } |
| |
| wpa_hexdump_key(vap, 0, |
| (ik->ik_flags & IEEE80211_KEY_GROUP) ? "GTK" : "PTK", |
| ik->ik_keydata, ik->ik_keylen); |
| |
| ieee80211_key_update_begin(vap); |
| error = vap->iv_key_set(vap, wk, ik->ik_macaddr); |
| ieee80211_key_update_end(vap); |
| |
| #if defined(CONFIG_QTN_BSA_SUPPORT) |
| if ((vap->bsa_status == BSA_STATUS_ACTIVE) && !(ik->ik_flags & IEEE80211_KEY_GROUP)) { |
| ni = ieee80211_find_node(&vap->iv_ic->ic_sta, ik->ik_macaddr); |
| if (ni && (ni->ni_associd != 0)) { |
| ieee80211_bsa_connect_complete_event_send(vap, ni); |
| ieee80211_free_node(ni); |
| } |
| } |
| #endif |
| |
| return error; |
| } |
| |
| static int |
| ieee80211_ioctl_getkey(struct net_device *dev, struct iwreq *iwr) |
| { |
| #ifndef IEEE80211_UNUSED_CRYPTO_COMMANDS |
| /* |
| * This code is put under conditional section as this |
| * may introduce issues with IOT devices and need |
| * more testing with IOT devices. |
| */ |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211req_key ik; |
| int keyrsc; |
| struct ieee80211_key *wk; |
| |
| if (iwr->u.data.length != sizeof(ik)) |
| return -EINVAL; |
| |
| if (copy_from_user(&ik, iwr->u.data.pointer, sizeof(ik)) != 0) |
| return -EFAULT; |
| |
| ieee80211_param_from_qdrv(vap, IEEE80211_IOCTL_GETKEY, &keyrsc, NULL, 0); |
| |
| ik.ik_keyrsc = (uint64_t)keyrsc; |
| ik.ik_keytsc = (uint64_t)keyrsc; |
| |
| if (vap->iv_opmode == IEEE80211_M_WDS) { |
| if (ik.ik_keyix >= IEEE80211_WEP_NKID) |
| return -EINVAL; |
| |
| wk = &vap->iv_nw_keys[ik.ik_keyix]; |
| ik.ik_keylen = wk->wk_keylen; |
| |
| memcpy(ik.ik_keydata, wk->wk_key, wk->wk_keylen); |
| } |
| |
| return (copy_to_user(iwr->u.data.pointer, &ik, sizeof(ik))); |
| #else |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_node *ni; |
| struct ieee80211req_key ik; |
| struct ieee80211_key *wk; |
| const struct ieee80211_cipher *cip; |
| u_int8_t kid; |
| |
| if (iwr->u.data.length != sizeof(ik)) |
| return -EINVAL; |
| if (copy_from_user(&ik, iwr->u.data.pointer, sizeof(ik))) |
| return -EFAULT; |
| kid = ik.ik_keyix; |
| if (kid == IEEE80211_KEYIX_NONE) { |
| ni = ieee80211_find_node(&ic->ic_sta, ik.ik_macaddr); |
| if (ni == NULL) |
| return -EINVAL; |
| wk = &ni->ni_ucastkey; |
| } else { |
| if (kid >= IEEE80211_WEP_NKID) |
| return -EINVAL; |
| wk = &vap->iv_nw_keys[kid]; |
| if (NULL == vap->iv_bss) |
| return -EINVAL; |
| IEEE80211_ADDR_COPY(&ik.ik_macaddr, vap->iv_bss->ni_macaddr); |
| ni = NULL; |
| } |
| cip = wk->wk_cipher; |
| ik.ik_type = cip->ic_cipher; |
| ik.ik_keylen = wk->wk_keylen; |
| ik.ik_flags = wk->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV); |
| if (wk->wk_keyix == vap->iv_def_txkey) |
| ik.ik_flags |= IEEE80211_KEY_DEFAULT; |
| if (capable(CAP_NET_ADMIN)) { |
| /* NB: only root can read key data */ |
| ik.ik_keyrsc = wk->wk_keyrsc[0]; |
| ik.ik_keytsc = wk->wk_keytsc; |
| memcpy(ik.ik_keydata, wk->wk_key, wk->wk_keylen); |
| if (cip->ic_cipher == IEEE80211_CIPHER_TKIP) { |
| memcpy(ik.ik_keydata+wk->wk_keylen, |
| wk->wk_key + IEEE80211_KEYBUF_SIZE, |
| IEEE80211_MICBUF_SIZE); |
| ik.ik_keylen += IEEE80211_MICBUF_SIZE; |
| } |
| } else { |
| ik.ik_keyrsc = 0; |
| ik.ik_keytsc = 0; |
| memset(ik.ik_keydata, 0, sizeof(ik.ik_keydata)); |
| } |
| if (ni != NULL) |
| ieee80211_free_node(ni); |
| return (copy_to_user(iwr->u.data.pointer, &ik, sizeof(ik)) ? -EFAULT : 0); |
| #endif /* IEEE80211_UNUSED_CRYPTO_COMMANDS */ |
| } |
| |
| static int |
| ieee80211_ioctl_delkey(struct net_device *dev, struct iw_request_info *info, |
| void *w, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211req_del_key *dk = (struct ieee80211req_del_key *)extra; |
| struct ieee80211_node *ni; |
| struct ieee80211_key *wk; |
| uint8_t kid = dk->idk_keyix; |
| uint32_t is_group_key = 0; |
| int error; |
| |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_WPA | IEEE80211_MSG_DEBUG, |
| "[%s] deleting key wepkid=%d bcast=%u\n", |
| ether_sprintf(dk->idk_macaddr), |
| dk->idk_keyix, |
| IEEE80211_IS_MULTICAST(dk->idk_macaddr)); |
| |
| if (kid == IEEE80211_KEYIX_NONE) { |
| /* Unicast key */ |
| kid = 0; |
| } else if (kid >= IEEE80211_WEP_NKID) { |
| return -EINVAL; |
| } |
| |
| /* |
| * hostapd sends a delete request for each of the four WEP global keys |
| * during initialisation. WEP is not supported and each vap node entry has its |
| * own global key, so the same key will be deleted four times. |
| */ |
| if (IEEE80211_IS_MULTICAST(dk->idk_macaddr)) { |
| is_group_key = 1; |
| if (NULL == vap->iv_bss) |
| return -EINVAL; |
| IEEE80211_ADDR_COPY(dk->idk_macaddr, vap->iv_bss->ni_macaddr); |
| } |
| |
| /* wk must be set to ni->ni_ucastkey for sw crypto */ |
| wk = &vap->iv_nw_keys[kid]; |
| wk->wk_ciphertype = 0; |
| wk->wk_keytsc = 0; |
| wk->wk_keylen = sizeof(wk->wk_key); |
| memset(wk->wk_key, 0, sizeof(wk->wk_key)); |
| |
| if (!is_group_key) { |
| ni = ieee80211_find_node(&vap->iv_ic->ic_sta, dk->idk_macaddr); |
| if (ni) { |
| memcpy(&ni->ni_ucastkey, wk, sizeof(ni->ni_ucastkey)); |
| ieee80211_free_node(ni); |
| } |
| if (vap->iv_opmode == IEEE80211_M_WDS) { |
| vap->iv_wds_peer_key.wk_ciphertype = 0; |
| vap->iv_wds_peer_key.wk_keytsc = 0; |
| vap->iv_wds_peer_key.wk_keylen = 0; |
| ieee80211_crypto_resetkey(vap, &vap->iv_wds_peer_key, |
| IEEE80211_KEYIX_NONE); |
| } |
| } |
| |
| ieee80211_key_update_begin(vap); |
| error = vap->iv_key_delete(vap, wk, dk->idk_macaddr); |
| ieee80211_key_update_end(vap); |
| |
| return error; |
| } |
| |
| struct scanlookup { /* XXX: right place for declaration? */ |
| const u_int8_t *mac; |
| int esslen; |
| const char *essid; |
| const struct ieee80211_scan_entry *se; |
| }; |
| |
| /* |
| * Match mac address and any ssid. |
| */ |
| static int |
| mlmelookup(void *arg, const struct ieee80211_scan_entry *se) |
| { |
| struct scanlookup *look = arg; |
| |
| if (!IEEE80211_ADDR_EQ(look->mac, se->se_macaddr)) |
| return 0; |
| if (look->esslen != 0) { |
| if (se->se_ssid[1] != look->esslen) |
| return 0; |
| if (memcmp(look->essid, se->se_ssid + 2, look->esslen)) |
| return 0; |
| } |
| look->se = se; |
| |
| return 0; |
| } |
| |
| /* |
| * Set operational Bridge Mode for: |
| * - an AP when the config is changed |
| * - a station when the config is changed or when associating with a new AP |
| * In Bridge Mode, eligible frames (non-1X, unicast data frames) are |
| * transmitted using the 4-address header format. |
| */ |
| u_int8_t ieee80211_bridgemode_set(struct ieee80211vap *vap, u_int8_t config_change) |
| { |
| u_int8_t op_bridgemode; |
| |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| op_bridgemode = !(vap->iv_qtn_flags & IEEE80211_QTN_BRIDGEMODE_DISABLED); |
| } else { |
| op_bridgemode = !(vap->iv_qtn_flags & IEEE80211_QTN_BRIDGEMODE_DISABLED) && |
| (vap->iv_qtn_ap_cap & IEEE80211_QTN_BRIDGEMODE); |
| } |
| |
| /* Has bridge mode changed? */ |
| if ((op_bridgemode && !(vap->iv_flags_ext & IEEE80211_FEXT_WDS)) || |
| (!op_bridgemode && (vap->iv_flags_ext & IEEE80211_FEXT_WDS))) { |
| |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, |
| "%s: %s Bridge Mode\n", __func__, op_bridgemode ? "Enabling" : "Disabling"); |
| |
| if (op_bridgemode) { |
| vap->iv_flags_ext |= IEEE80211_FEXT_WDS; |
| } else { |
| vap->iv_flags_ext &= ~IEEE80211_FEXT_WDS; |
| } |
| |
| /* Notify the MuC */ |
| ieee80211_param_to_qdrv(vap, IEEE80211_PARAM_WDS, op_bridgemode, NULL, 0); |
| |
| /* |
| * If the change was caused by a configuration change, force |
| * reassociation to ensure that everyone is in sync. |
| */ |
| if (config_change) { |
| ieee80211_wireless_reassoc(vap, 0, 0); |
| } |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| void ieee80211_sta_fast_rejoin(unsigned long arg) |
| { |
| struct ieee80211vap *vap = (struct ieee80211vap *) arg; |
| struct ieee80211com *ic = vap->iv_ic; |
| struct scanlookup lookup; |
| |
| if (vap->iv_state >= IEEE80211_S_AUTH) { |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_AUTH, "state(%d) not expected\n", |
| vap->iv_state); |
| return; |
| } |
| |
| lookup.se = NULL; |
| lookup.mac = vap->iv_sta_fast_rejoin_bssid; |
| if (vap->iv_des_nssid != 0) { |
| lookup.esslen = vap->iv_des_ssid[0].len; |
| lookup.essid = (char *)vap->iv_des_ssid[0].ssid; |
| } else { |
| lookup.esslen = 0; |
| lookup.essid = (char *)""; |
| } |
| ieee80211_scan_iterate(ic, mlmelookup, &lookup); |
| if (lookup.se != NULL) { |
| vap->iv_nsdone = 0; |
| vap->iv_nsparams.result = 0; |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_AUTH, "fast rejoin bssid "MACSTR"\n", |
| MAC2STR(vap->iv_sta_fast_rejoin_bssid)); |
| if (!ieee80211_sta_join(vap, lookup.se)) { |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_AUTH, "fast rejoin bssid "MACSTR" failed\n", |
| MAC2STR(vap->iv_sta_fast_rejoin_bssid)); |
| } |
| } else { |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_AUTH, "fast rejoin bssid "MACSTR" not found\n", |
| MAC2STR(vap->iv_sta_fast_rejoin_bssid)); |
| } |
| } |
| |
| void ieee80211_ba_setup_detect_rssi(unsigned long arg) |
| { |
| struct ieee80211com *ic = (struct ieee80211com *) arg; |
| struct ieee80211_node_table *nt = &ic->ic_sta; |
| struct ieee80211_node *ni; |
| int32_t rssi; |
| |
| TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { |
| if (ni && (ni->ni_qtn_flags & QTN_IS_INTEL_NODE) |
| && !IEEE80211_NODE_IS_VHT(ni)) { |
| rssi = ni->ni_shared_stats->rx[STATS_SU].last_rssi_dbm[NUM_ANT]; |
| if (ni->rssi_avg_dbm) { |
| ni->rssi_avg_dbm = (ni->rssi_avg_dbm * |
| (QTN_RSSI_SAMPLE_TH - 1) + rssi) |
| / QTN_RSSI_SAMPLE_TH; |
| } else { |
| ni->rssi_avg_dbm = rssi; |
| } |
| } |
| } |
| |
| mod_timer(&ic->ic_ba_setup_detect, jiffies + HZ * QTN_AMPDU_DETECT_PERIOD); |
| } |
| |
| static int ieee80211_ba_setup_detect_set(struct ieee80211vap *vap, int enable) |
| { |
| struct ieee80211com *ic = vap->iv_ic; |
| |
| if (enable) { |
| init_timer(&ic->ic_ba_setup_detect); |
| ic->ic_ba_setup_detect.function = ieee80211_ba_setup_detect_rssi; |
| ic->ic_ba_setup_detect.data = (unsigned long) ic; |
| ic->ic_ba_setup_detect.expires = jiffies; |
| add_timer(&ic->ic_ba_setup_detect); |
| } else { |
| del_timer(&ic->ic_ba_setup_detect); |
| } |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_setmlme(struct net_device *dev, struct iw_request_info *info, |
| void *w, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211req_mlme *mlme = (struct ieee80211req_mlme *)extra; |
| struct ieee80211_node *ni; |
| |
| if (!IS_UP(dev)) |
| return -EINVAL; |
| |
| if (ic->ic_flags_qtn & IEEE80211_QTN_MONITOR) |
| return 0; |
| |
| switch (mlme->im_op) { |
| case IEEE80211_MLME_ASSOC: |
| if (vap->iv_opmode == IEEE80211_M_STA) { |
| struct scanlookup lookup; |
| |
| lookup.se = NULL; |
| lookup.mac = mlme->im_macaddr; |
| /* XXX use revised api w/ explicit ssid */ |
| if (vap->iv_des_nssid != 0) { |
| lookup.esslen = vap->iv_des_ssid[0].len; |
| lookup.essid = (char *)vap->iv_des_ssid[0].ssid; |
| } else { |
| lookup.esslen = 0; |
| lookup.essid = (char *)""; |
| } |
| ieee80211_scan_iterate(ic, mlmelookup, &lookup); |
| if (lookup.se != NULL) { |
| vap->iv_nsdone = 0; |
| vap->iv_nsparams.result = 0; |
| if (ieee80211_sta_join(vap, lookup.se)) |
| while (!vap->iv_nsdone) |
| IEEE80211_RESCHEDULE(); |
| if (vap->iv_nsparams.result == 0) |
| return 0; |
| } |
| } |
| return -EINVAL; |
| case IEEE80211_MLME_DEBUG_CLEAR: |
| case IEEE80211_MLME_DISASSOC: |
| case IEEE80211_MLME_DEAUTH: |
| switch (vap->iv_opmode) { |
| case IEEE80211_M_STA: |
| /* XXX not quite right */ |
| ieee80211_new_state(vap, IEEE80211_S_INIT, |
| mlme->im_reason); |
| break; |
| case IEEE80211_M_HOSTAP: |
| /* NB: the broadcast address means do 'em all */ |
| IEEE80211_NODE_LOCK_BH(&ic->ic_sta); |
| if (!IEEE80211_ADDR_EQ(mlme->im_macaddr, vap->iv_dev->broadcast)) { |
| ni = ieee80211_find_node(&ic->ic_sta, |
| mlme->im_macaddr); |
| if (ni == NULL) { |
| IEEE80211_NODE_UNLOCK_BH(&ic->ic_sta); |
| return -EINVAL; |
| } |
| if (dev == ni->ni_vap->iv_dev) { |
| ieee80211_domlme(mlme, ni); |
| } |
| ieee80211_free_node(ni); |
| } else { |
| ieee80211_iterate_dev_nodes(dev, &ic->ic_sta, ieee80211_domlme, mlme, 0); |
| } |
| IEEE80211_NODE_UNLOCK_BH(&ic->ic_sta); |
| break; |
| default: |
| return -EINVAL; |
| } |
| break; |
| case IEEE80211_MLME_AUTHORIZE: |
| case IEEE80211_MLME_UNAUTHORIZE: |
| if (vap->iv_opmode != IEEE80211_M_HOSTAP) |
| return -EINVAL; |
| ni = ieee80211_find_node(&ic->ic_sta, mlme->im_macaddr); |
| if (ni == NULL) |
| return -ENOENT; |
| if (mlme->im_op == IEEE80211_MLME_AUTHORIZE) |
| ieee80211_node_authorize(ni); |
| else |
| ieee80211_node_unauthorize(ni); |
| ieee80211_free_node(ni); |
| break; |
| case IEEE80211_MLME_CLEAR_STATS: |
| if (vap->iv_opmode != IEEE80211_M_HOSTAP) |
| return -EINVAL; |
| ni = ieee80211_find_node(&ic->ic_sta, mlme->im_macaddr); |
| if (ni == NULL) |
| return -ENOENT; |
| |
| /* clear statistics */ |
| memset(&ni->ni_stats, 0, sizeof(struct ieee80211_nodestats)); |
| ieee80211_free_node(ni); |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| /* 'iwpriv wifi0 doth_radar X' simulates a radar detection on current channel |
| * triggers a channel switch to a random if X is 0 or to the IEEE channel X */ |
| static int |
| ieee80211_ioctl_radar(struct net_device *dev, struct iw_request_info *info, |
| void *w, char *extra) |
| { |
| int *params = (int *) extra; |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| u_int8_t new_ieee = params[0]; |
| |
| if (ic->ic_curchan->ic_flags & IEEE80211_CHAN_DFS) { |
| ic->ic_radar_detected(ic, new_ieee); |
| return 0; |
| } else { |
| return -EINVAL; |
| } |
| } |
| |
| /* 'iwpriv wifi0 dfsactscan 1' will have STA do active scan on DFS channels, |
| * and 'iwpriv wifi0 dfsactscan 0' revert it to the default |
| * behavior (passive scan on DFS channels) */ |
| static int |
| ieee80211_ioctl_dfsactscan(struct net_device *dev, struct iw_request_info *info, |
| void *w, char *extra) |
| { |
| int* params = (int *) extra; |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| u_int8_t dfsactscan = params[0]; |
| int i; |
| |
| if (ic->ic_opmode != IEEE80211_M_STA) |
| printk("%s: this command can be used only for STA\n", __FUNCTION__); |
| |
| /* Note: this logic should be same with qdrv_radar_is_dfs_required(), but wlan-to-qdrv |
| * dependency is considered not desirable, so the logic is duplicated here |
| */ |
| #define IS_DFS_CHAN(chan) ((5250 <= (chan)->ic_freq) && ((chan)->ic_freq <= 5725)) |
| |
| for (i = 0; i < ic->ic_nchans; i++) { |
| if (IS_DFS_CHAN(&ic->ic_channels[i])) { |
| if (dfsactscan) { |
| ic->ic_channels[i].ic_flags &= ~IEEE80211_CHAN_PASSIVE; |
| } else { |
| ic->ic_channels[i].ic_flags |= IEEE80211_CHAN_PASSIVE; |
| } |
| } |
| } |
| |
| if (dfsactscan) |
| printk("STA is now configured to do an active scan on DFS channels\n"); |
| else |
| printk("STA is now configured to do a passive scan on DFS channels (default)\n"); |
| |
| return 0; |
| |
| #undef IS_DFS_CHAN |
| } |
| |
| static int |
| ieee80211_ioctl_wdsmac(struct net_device *dev, struct iw_request_info *info, |
| void *w, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct sockaddr *sa = (struct sockaddr *)extra; |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211vap *vap_tmp; |
| struct ieee80211vap *vap_found = NULL; |
| |
| if (vap->iv_opmode != IEEE80211_M_WDS) |
| return -EOPNOTSUPP; |
| |
| if (!IEEE80211_ADDR_NULL(vap->wds_mac)) { |
| printk("%s: Failed to add WDS MAC: %s\n", dev->name, |
| ether_sprintf((u_int8_t *)sa->sa_data)); |
| printk("%s: Device already has WDS mac address attached," |
| " remove first\n", dev->name); |
| return -1; |
| } |
| |
| TAILQ_FOREACH(vap_tmp, &ic->ic_vaps, iv_next) { |
| if (IEEE80211_ADDR_EQ(vap_tmp->iv_myaddr, sa->sa_data)) { |
| vap_found = vap_tmp; |
| break; |
| } |
| |
| if ((vap_tmp->iv_opmode == IEEE80211_M_WDS) && |
| (IEEE80211_ADDR_EQ(vap_tmp->wds_mac, sa->sa_data)) ) { |
| vap_found = vap_tmp; |
| break; |
| } |
| } |
| |
| if (vap_found) { |
| printk("%s: The mac address(%s) has been used by device(%s)\n", |
| dev->name, |
| ether_sprintf((u_int8_t *)sa->sa_data), |
| vap_found->iv_dev->name); |
| return -EINVAL; |
| } |
| |
| memcpy(vap->wds_mac, sa->sa_data, IEEE80211_ADDR_LEN); |
| |
| printk("%s: Added WDS MAC: %s\n", dev->name, |
| ether_sprintf(vap->wds_mac)); |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_wdsdelmac(struct net_device *dev, struct iw_request_info *info, |
| void *w, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct sockaddr *sa = (struct sockaddr *)extra; |
| struct ieee80211com *ic = vap->iv_ic; |
| |
| if (IEEE80211_ADDR_NULL(vap->wds_mac)) |
| return 0; |
| |
| if (vap->iv_opmode != IEEE80211_M_WDS) |
| return -EOPNOTSUPP; |
| |
| /* |
| * Compare supplied MAC address with WDS MAC of this interface |
| * remove when mac address is known |
| */ |
| if (IEEE80211_ADDR_EQ(vap->wds_mac, sa->sa_data)) { |
| ieee80211_extender_remove_peer_wds_info(ic, vap->wds_mac); |
| |
| IEEE80211_ADDR_SET_NULL(vap->wds_mac); |
| return 0; |
| } |
| |
| printk("%s: WDS MAC address %s is not known by this interface\n", |
| dev->name, ether_sprintf((u_int8_t *)sa->sa_data)); |
| |
| return -1; |
| } |
| |
| /* |
| * kick associated station with the given MAC address. |
| */ |
| static int |
| ieee80211_ioctl_kickmac(struct net_device *dev, struct iw_request_info *info, |
| void *w, char *extra) |
| { |
| struct sockaddr *sa = (struct sockaddr *)extra; |
| struct ieee80211req_mlme mlme; |
| |
| if (sa->sa_family != ARPHRD_ETHER) |
| return -EINVAL; |
| |
| /* Setup a MLME request for disassociation of the given MAC */ |
| mlme.im_op = IEEE80211_MLME_DISASSOC; |
| mlme.im_reason = IEEE80211_REASON_UNSPECIFIED; |
| IEEE80211_ADDR_COPY(&(mlme.im_macaddr), sa->sa_data); |
| |
| /* Send the MLME request and return the result. */ |
| return ieee80211_ioctl_setmlme(dev, info, w, (char *)&mlme); |
| } |
| |
| /* Currently this function is used to associate with an AP with given bssid */ |
| static int |
| ieee80211_ioctl_addmac(struct net_device *dev, struct iw_request_info *info, |
| void *w, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct sockaddr *sa = (struct sockaddr *)extra; |
| |
| #ifdef DEMO_CONTROL |
| struct ieee80211com *ic = vap->iv_ic; |
| memcpy(vap->iv_des_bssid, sa->sa_data, IEEE80211_ADDR_LEN); |
| |
| |
| ieee80211_param_to_qdrv(vap, IEEE80211_PARAM_BSSID, |
| 0, vap->iv_des_bssid, IEEE80211_ADDR_LEN); |
| |
| if (IS_UP(vap->iv_dev)) |
| return ic->ic_reset(ic); |
| #else |
| const struct ieee80211_aclator *acl = vap->iv_acl; |
| if (acl == NULL) { |
| acl = ieee80211_aclator_get("mac"); |
| if (acl == NULL || !acl->iac_attach(vap)) |
| return -EINVAL; |
| vap->iv_acl = acl; |
| } |
| acl->iac_add(vap, sa->sa_data); |
| #endif |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_delmac(struct net_device *dev, struct iw_request_info *info, |
| void *w, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct sockaddr *sa = (struct sockaddr *)extra; |
| const struct ieee80211_aclator *acl = vap->iv_acl; |
| |
| if (acl == NULL) { |
| acl = ieee80211_aclator_get("mac"); |
| if (acl == NULL || !acl->iac_attach(vap)) |
| return -EINVAL; |
| vap->iv_acl = acl; |
| } |
| acl->iac_remove(vap, (u_int8_t *)sa->sa_data); |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_setchanlist(struct net_device *dev, |
| struct iw_request_info *info, void *w, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211req_chanlist *list = |
| (struct ieee80211req_chanlist *)extra; |
| u_char chanlist[IEEE80211_CHAN_BYTES]; |
| int i, j, k = 0, nchan; |
| struct ieee80211_channel *ch; |
| int bw = ieee80211_get_bw(ic); |
| |
| memset(chanlist, 0, sizeof(chanlist)); |
| /* |
| * Since channel 0 is not available for DS, channel 1 |
| * is assigned to LSB on WaveLAN. |
| */ |
| if ((ic->ic_phytype == IEEE80211_T_DS) || (ic->ic_phytype == IEEE80211_T_OFDM)) |
| i = 1; |
| else |
| i = 0; |
| nchan = 0; |
| for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) { |
| /* |
| * NB: silently discard unavailable channels so users |
| * can specify 1-255 to get all available channels. |
| */ |
| if (isset(list->ic_channels, j) && isset(ic->ic_chan_avail, i)) { |
| if (ic->ic_dfs_channels_deactive && isset(ic->ic_chan_dfs_required, i)) { |
| continue; |
| } |
| if (ieee80211_is_channel_disabled(ic, i, bw)) { |
| continue; |
| } |
| setbit(chanlist, i); |
| nchan++; |
| } |
| } |
| |
| if (nchan == 0) /* no valid channels, disallow */ |
| return -EINVAL; |
| if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && /* XXX */ |
| isclr(chanlist, ic->ic_bsschan->ic_ieee)) { |
| ic->ic_bsschan = IEEE80211_CHAN_ANYC; /* invalidate */ |
| k = 1; |
| } |
| |
| memset(ic->ic_chan_active_80, 0, sizeof(ic->ic_chan_active_80)); |
| memset(ic->ic_chan_active_40, 0, sizeof(ic->ic_chan_active_40)); |
| memset(ic->ic_chan_active_20, 0, sizeof(ic->ic_chan_active_20)); |
| |
| for (i = 0; i < ic->ic_nchans; i++) { |
| ch = &ic->ic_channels[i]; |
| if (isset(chanlist, ch->ic_ieee)) { |
| if (IEEE80211_IS_CHAN_HT40(ch)) |
| setbit(ic->ic_chan_active_40, ch->ic_ieee); |
| |
| if (IEEE80211_IS_CHAN_VHT80(ch)) |
| setbit(ic->ic_chan_active_80, ch->ic_ieee); |
| |
| setbit(ic->ic_chan_active_20, ch->ic_ieee); |
| } |
| } |
| |
| ieee80211_update_active_chanlist(ic, bw); |
| if (IS_UP_AUTO(vap)) { |
| if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && |
| isclr(ic->ic_chan_active, ic->ic_des_chan->ic_ieee)) { |
| ic->ic_des_chan = IEEE80211_CHAN_ANYC; |
| } |
| if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) { |
| if (((bw >= BW_HT80) && !(ic->ic_des_chan->ic_flags & IEEE80211_CHAN_VHT80)) || |
| ((bw >= BW_HT40) && !(ic->ic_des_chan->ic_flags & IEEE80211_CHAN_HT40))) { |
| ic->ic_des_chan = IEEE80211_CHAN_ANYC; |
| } |
| } |
| ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); |
| /* send disassoc to ap when BSS channel is invalid. */ |
| } else if (k && vap->iv_state == IEEE80211_S_RUN && |
| vap->iv_opmode != IEEE80211_M_MONITOR) { |
| ieee80211_new_state(vap, IEEE80211_S_INIT, 0); |
| } |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_getchanlist(struct net_device *dev, |
| struct iw_request_info *info, void *w, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| union iwreq_data *iwr = (union iwreq_data *)w; |
| u_int8_t chanlist[IEEE80211_CHAN_BYTES]; |
| int i; |
| |
| memcpy(chanlist, ic->ic_chan_active, sizeof(ic->ic_chan_active)); |
| |
| for (i = 0; i < ic->ic_nchans; i++) { |
| struct ieee80211_channel *c = &ic->ic_channels[i]; |
| |
| if (isset(chanlist, c->ic_ieee) && |
| (vap->iv_opmode != IEEE80211_M_STA) && |
| (ieee80211_check_mode_consistency(ic, ic->ic_des_mode, c))) { |
| clrbit(chanlist, c->ic_ieee); |
| } |
| } |
| |
| memcpy(extra, chanlist, sizeof(chanlist)); |
| iwr->data.length = sizeof(chanlist); |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_getchaninfo(struct net_device *dev, |
| struct iw_request_info *info, void *w, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| union iwreq_data *iwr = (union iwreq_data *)w; |
| struct ieee80211req_chaninfo *chans = |
| (struct ieee80211req_chaninfo *) extra; |
| u_int8_t reported[IEEE80211_CHAN_BYTES]; /* XXX stack usage? */ |
| int i; |
| |
| memset(chans, 0, sizeof(*chans)); |
| memset(reported, 0, sizeof(reported)); |
| for (i = 0; i < ic->ic_nchans; i++) { |
| const struct ieee80211_channel *c = &ic->ic_channels[i]; |
| const struct ieee80211_channel *c1 = c; |
| |
| if (isclr(reported, c->ic_ieee)) { |
| setbit(reported, c->ic_ieee); |
| |
| /* pick turbo channel over non-turbo channel, and |
| * 11g channel over 11b channel */ |
| if (IEEE80211_IS_CHAN_A(c)) |
| c1 = findchannel(ic, c->ic_ieee, IEEE80211_MODE_TURBO_A); |
| if (IEEE80211_IS_CHAN_ANYG(c)) |
| c1 = findchannel(ic, c->ic_ieee, IEEE80211_MODE_TURBO_G); |
| else if (IEEE80211_IS_CHAN_B(c)) { |
| c1 = findchannel(ic, c->ic_ieee, IEEE80211_MODE_TURBO_G); |
| if (!c1) |
| c1 = findchannel(ic, c->ic_ieee, IEEE80211_MODE_11G); |
| } |
| |
| if (c1) |
| c = c1; |
| chans->ic_chans[chans->ic_nchans].ic_ieee = c->ic_ieee; |
| chans->ic_chans[chans->ic_nchans].ic_freq = c->ic_freq; |
| chans->ic_chans[chans->ic_nchans].ic_flags = c->ic_flags; |
| if (++chans->ic_nchans >= IEEE80211_CHAN_MAX) |
| break; |
| } |
| } |
| |
| iwr->data.length = chans->ic_nchans * sizeof(struct ieee80211_chan); |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_setwmmparams(struct net_device *dev, |
| struct iw_request_info *info, void *w, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| int *param = (int *) extra; |
| int ac = (param[1] >= 0 && param[1] < WME_NUM_AC) ? |
| param[1] : WME_AC_BE; |
| int bss = param[2]; |
| struct ieee80211_wme_state *wme = ieee80211_vap_get_wmestate(vap); |
| #ifdef CONFIG_QVSP |
| struct ieee80211com *ic = vap->iv_ic; |
| #endif |
| |
| switch (param[0]) { |
| case IEEE80211_WMMPARAMS_CWMIN: |
| if (param[3] < 0 || param[3] > 15) |
| return -EINVAL; |
| if (bss) { |
| wme->wme_wmeBssChanParams.cap_wmeParams[ac].wmm_logcwmin = param[3]; |
| wme->wme_wmeBssChanParams.cap_info_count++; |
| if ((wme->wme_flags & WME_F_AGGRMODE) == 0) { |
| wme->wme_bssChanParams.cap_wmeParams[ac].wmm_logcwmin = param[3]; |
| } |
| |
| } else { |
| wme->wme_wmeChanParams.cap_wmeParams[ac].wmm_logcwmin = param[3]; |
| wme->wme_wmeChanParams.cap_info_count++; |
| wme->wme_chanParams.cap_wmeParams[ac].wmm_logcwmin = param[3]; |
| } |
| ieee80211_wme_updateparams(vap, !bss); |
| break; |
| case IEEE80211_WMMPARAMS_CWMAX: |
| if (param[3] < 0 || param[3] > 15) |
| return -EINVAL; |
| if (bss) { |
| wme->wme_wmeBssChanParams.cap_wmeParams[ac].wmm_logcwmax = param[3]; |
| wme->wme_wmeBssChanParams.cap_info_count++; |
| if ((wme->wme_flags & WME_F_AGGRMODE) == 0) { |
| wme->wme_bssChanParams.cap_wmeParams[ac].wmm_logcwmax = param[3]; |
| } |
| } else { |
| wme->wme_wmeChanParams.cap_wmeParams[ac].wmm_logcwmax = param[3]; |
| wme->wme_wmeChanParams.cap_info_count++; |
| wme->wme_chanParams.cap_wmeParams[ac].wmm_logcwmax = param[3]; |
| } |
| ieee80211_wme_updateparams(vap, !bss); |
| break; |
| case IEEE80211_WMMPARAMS_AIFS: |
| if (param[3] < 0 || param[3] > 15) |
| return -EINVAL; |
| if (bss) { |
| wme->wme_wmeBssChanParams.cap_wmeParams[ac].wmm_aifsn = param[3]; |
| wme->wme_wmeBssChanParams.cap_info_count++; |
| if ((wme->wme_flags & WME_F_AGGRMODE) == 0) |
| wme->wme_bssChanParams.cap_wmeParams[ac].wmm_aifsn = param[3]; |
| } else { |
| wme->wme_wmeChanParams.cap_wmeParams[ac].wmm_aifsn = param[3]; |
| wme->wme_wmeChanParams.cap_info_count++; |
| wme->wme_chanParams.cap_wmeParams[ac].wmm_aifsn = param[3]; |
| } |
| ieee80211_wme_updateparams(vap, !bss); |
| break; |
| case IEEE80211_WMMPARAMS_TXOPLIMIT: |
| if (param[3] < 0 || param[3] > 8192) |
| return -EINVAL; |
| if (bss) { |
| wme->wme_wmeBssChanParams.cap_wmeParams[ac].wmm_txopLimit |
| = IEEE80211_US_TO_TXOP(param[3]); |
| wme->wme_wmeBssChanParams.cap_info_count++; |
| if ((wme->wme_flags & WME_F_AGGRMODE) == 0) |
| wme->wme_bssChanParams.cap_wmeParams[ac].wmm_txopLimit = |
| IEEE80211_US_TO_TXOP(param[3]); |
| } else { |
| wme->wme_wmeChanParams.cap_wmeParams[ac].wmm_txopLimit |
| = IEEE80211_US_TO_TXOP(param[3]); |
| wme->wme_wmeChanParams.cap_info_count++; |
| wme->wme_chanParams.cap_wmeParams[ac].wmm_txopLimit |
| = IEEE80211_US_TO_TXOP(param[3]); |
| } |
| ieee80211_wme_updateparams(vap, !bss); |
| break; |
| case IEEE80211_WMMPARAMS_ACM: |
| if (!bss || param[3] < 0 || param[3] > 1) |
| return -EINVAL; |
| /* ACM bit applies to BSS case only */ |
| wme->wme_wmeBssChanParams.cap_wmeParams[ac].wmm_acm = param[3]; |
| wme->wme_wmeBssChanParams.cap_info_count++; |
| if ((wme->wme_flags & WME_F_AGGRMODE) == 0) |
| wme->wme_bssChanParams.cap_wmeParams[ac].wmm_acm = param[3]; |
| ieee80211_wme_updateparams(vap, 0); |
| break; |
| case IEEE80211_WMMPARAMS_NOACKPOLICY: |
| if (bss || param[3] < 0 || param[3] > 1) |
| return -EINVAL; |
| /* ack policy applies to non-BSS case only */ |
| wme->wme_wmeChanParams.cap_wmeParams[ac].wmm_noackPolicy = param[3]; |
| wme->wme_wmeChanParams.cap_info_count++; |
| wme->wme_chanParams.cap_wmeParams[ac].wmm_noackPolicy = param[3]; |
| ieee80211_vap_sync_chan_wmestate(vap); |
| break; |
| default: |
| break; |
| } |
| |
| #ifdef CONFIG_QVSP |
| if (ic->ic_vsp_reset) { |
| ic->ic_vsp_reset(ic); |
| } |
| #endif |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_getwmmparams(struct net_device *dev, |
| struct iw_request_info *info, void *w, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| int *param = (int *) extra; |
| int ac = (param[1] >= 0 && param[1] < WME_NUM_AC) ? |
| param[1] : WME_AC_BE; |
| int bss = param[2]; |
| struct ieee80211_wme_state *wme = ieee80211_vap_get_wmestate(vap); |
| struct chanAccParams *chanParams = (!bss) ? |
| &(wme->wme_chanParams) : &(wme->wme_bssChanParams); |
| |
| switch (param[0]) { |
| case IEEE80211_WMMPARAMS_CWMIN: |
| param[0] = chanParams->cap_wmeParams[ac].wmm_logcwmin; |
| break; |
| case IEEE80211_WMMPARAMS_CWMAX: |
| param[0] = chanParams->cap_wmeParams[ac].wmm_logcwmax; |
| break; |
| case IEEE80211_WMMPARAMS_AIFS: |
| param[0] = chanParams->cap_wmeParams[ac].wmm_aifsn; |
| break; |
| case IEEE80211_WMMPARAMS_TXOPLIMIT: |
| param[0] = IEEE80211_TXOP_TO_US(chanParams->cap_wmeParams[ac].wmm_txopLimit); |
| break; |
| case IEEE80211_WMMPARAMS_ACM: |
| param[0] = wme->wme_wmeBssChanParams.cap_wmeParams[ac].wmm_acm; |
| break; |
| case IEEE80211_WMMPARAMS_NOACKPOLICY: |
| param[0] = wme->wme_wmeChanParams.cap_wmeParams[ac].wmm_noackPolicy; |
| break; |
| default: |
| break; |
| } |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_getwpaie(struct net_device *dev, struct iwreq *iwr) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_node *ni; |
| struct ieee80211req_wpaie wpaie = {{0}}; |
| struct scanlookup lookup; |
| struct ieee80211_ie_qtn_pairing *hash_ie; |
| |
| if (iwr->u.data.length != sizeof(wpaie)) |
| return -EINVAL; |
| if (copy_from_user(&wpaie, iwr->u.data.pointer, IEEE80211_ADDR_LEN)) |
| return -EFAULT; |
| ni = ieee80211_find_node(&ic->ic_sta, wpaie.wpa_macaddr); |
| if ((ni == NULL) && (vap->iv_opmode == IEEE80211_M_STA)) { |
| lookup.mac = wpaie.wpa_macaddr; |
| lookup.esslen = 0; |
| lookup.se = NULL; |
| |
| ieee80211_scan_iterate(ic, mlmelookup, &lookup); |
| |
| if (lookup.se != NULL) { |
| hash_ie = (struct ieee80211_ie_qtn_pairing *) |
| lookup.se->se_pairing_ie; |
| if (hash_ie) { |
| memcpy(wpaie.qtn_pairing_ie, |
| hash_ie->qtn_pairing_tlv.qtn_pairing_tlv_hash, |
| QTN_PAIRING_TLV_HASH_LEN); |
| wpaie.has_pairing_ie = QTN_PAIRING_IE_EXIST; |
| } else { |
| wpaie.has_pairing_ie = QTN_PAIRING_IE_ABSENT; |
| } |
| |
| return (copy_to_user(iwr->u.data.pointer, &wpaie, |
| sizeof(wpaie)) ? -EFAULT : 0); |
| } else { |
| return -EINVAL; /* XXX */ |
| } |
| } |
| if (ni == NULL) |
| return -EINVAL; |
| if (ni->ni_wpa_ie != NULL) { |
| int ielen = ni->ni_wpa_ie[1] + 2; |
| if (ielen > sizeof(wpaie.wpa_ie)) |
| ielen = sizeof(wpaie.wpa_ie); |
| memcpy(wpaie.wpa_ie, ni->ni_wpa_ie, ielen); |
| } |
| if (ni->ni_rsn_ie != NULL) { |
| int ielen = ni->ni_rsn_ie[1] + 2; |
| if (ielen > sizeof(wpaie.rsn_ie)) |
| ielen = sizeof(wpaie.rsn_ie); |
| memcpy(wpaie.rsn_ie, ni->ni_rsn_ie, ielen); |
| } |
| if (ni->ni_osen_ie != NULL) { |
| int ielen = ni->ni_osen_ie[1] + 2; |
| if (ielen > sizeof(wpaie.osen_ie)) |
| ielen = sizeof(wpaie.osen_ie); |
| memcpy(wpaie.osen_ie, ni->ni_osen_ie, ielen); |
| } |
| if (ni->ni_wsc_ie != NULL) { |
| int ielen = ni->ni_wsc_ie[1] + 2; |
| |
| if (ielen > sizeof(wpaie.wps_ie)) { |
| ielen = sizeof(wpaie.wps_ie); |
| } |
| memcpy(wpaie.wps_ie, ni->ni_wsc_ie, ielen); |
| } |
| if (ni->ni_qtn_pairing_ie != NULL) { |
| hash_ie = (struct ieee80211_ie_qtn_pairing *)ni->ni_qtn_pairing_ie; |
| |
| memcpy(wpaie.qtn_pairing_ie, hash_ie->qtn_pairing_tlv.qtn_pairing_tlv_hash, QTN_PAIRING_TLV_HASH_LEN); |
| wpaie.has_pairing_ie = QTN_PAIRING_IE_EXIST; |
| } else { |
| wpaie.has_pairing_ie = QTN_PAIRING_IE_ABSENT; |
| } |
| if (ni->ni_rx_md_ie != NULL) { |
| int ielen = ni->ni_rx_md_ie[1] + 2; |
| memcpy(wpaie.mdie, ni->ni_rx_md_ie, ielen); |
| } else { |
| memset(wpaie.mdie, 0, 5); |
| } |
| if (ni->ni_rx_ft_ie != NULL) { |
| int ielen = ni->ni_rx_ft_ie[1] + 2; |
| memcpy(wpaie.ftie, ni->ni_rx_ft_ie, ielen); |
| } |
| |
| |
| ieee80211_free_node(ni); |
| return (copy_to_user(iwr->u.data.pointer, &wpaie, sizeof(wpaie)) ? |
| -EFAULT : 0); |
| } |
| |
| #if defined(CONFIG_QTN_80211K_SUPPORT) |
| static int |
| ieee80211_ioctl_getstastatistic(struct net_device *dev, |
| struct iw_request_info *info, struct iw_point *data, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_node_table *nt = &ic->ic_sta; |
| struct ieee80211_node *ni = NULL; |
| struct ieee80211req_qtn_rmt_sta_stats *sta_statistic = NULL; |
| struct ieee80211req_qtn_rmt_sta_stats_setpara setpara; |
| int ret = 0; |
| |
| if (copy_from_user(&setpara, data->pointer, sizeof(struct ieee80211req_qtn_rmt_sta_stats_setpara))) { |
| return -EFAULT; |
| } |
| |
| ni = ieee80211_find_node(nt, setpara.macaddr); |
| |
| if (ni) { |
| if (setpara.flags == RM_STANDARD_CCA) { |
| ieee80211_send_rm_req_cca(ni); |
| } else { |
| if (ni->ni_qtn_assoc_ie == NULL) { |
| if (setpara.flags == BIT(RM_QTN_RSSI_DBM)) { |
| setpara.flags = BIT(RM_GRP221_RSSI); |
| } else if (setpara.flags == BIT(RM_QTN_HW_NOISE)) { |
| setpara.flags = BIT(RM_GRP221_PHY_NOISE); |
| } else if (setpara.flags == BIT(RM_QTN_SOC_MACADDR)) { |
| setpara.flags = BIT(RM_GRP221_SOC_MAC); |
| } |
| } |
| |
| sta_statistic = (struct ieee80211req_qtn_rmt_sta_stats *)extra; |
| |
| /* Set pending flag and clear status */ |
| ni->ni_dotk_meas_state.meas_state_sta.pending = 1; |
| ni->ni_dotk_meas_state.meas_state_sta.status = 0; |
| |
| ieee80211_send_rm_req_stastats(ni, setpara.flags); |
| |
| /* |
| * ret = 0, thread is waked up |
| * ret < 0, interrupted by SIGNAL |
| * */ |
| ret = wait_event_interruptible(ni->ni_dotk_waitq, |
| ni->ni_dotk_meas_state.meas_state_sta.pending == 0); |
| |
| data->length = sizeof(struct ieee80211req_qtn_rmt_sta_stats); |
| if (ret == 0) { |
| if (ni->ni_dotk_meas_state.meas_state_sta.status == 0) { |
| memset(sta_statistic, 0, sizeof(struct ieee80211req_qtn_rmt_sta_stats)); |
| |
| if (setpara.flags & RM_QTN_MEASURE_MASK) { |
| memcpy(&(sta_statistic->rmt_sta_stats), |
| &(ni->ni_qtn_rm_sta_all), |
| sizeof(struct ieee80211_ie_qtn_rm_sta_all)); |
| } else { |
| if (setpara.flags & BIT(RM_GRP221_RSSI)) { |
| int rm_rssi = *(int8_t *)&ni->ni_rm_sta_grp221.rssi; |
| if (rm_rssi < 0) { |
| sta_statistic->rmt_sta_stats.rssi_dbm = rm_rssi * 10 + 5; |
| } else { |
| sta_statistic->rmt_sta_stats.rssi_dbm = rm_rssi * 10 - 5; |
| } |
| } |
| |
| if (setpara.flags & BIT(RM_GRP221_PHY_NOISE)) { |
| int rm_noise = *(int8_t *)&ni->ni_rm_sta_grp221.phy_noise; |
| sta_statistic->rmt_sta_stats.hw_noise = rm_noise * 10; |
| } |
| |
| if (setpara.flags & BIT(RM_GRP221_SOC_MAC)) { |
| memcpy(sta_statistic->rmt_sta_stats.soc_macaddr, |
| ni->ni_rm_sta_grp221.soc_macaddr, |
| IEEE80211_ADDR_LEN); |
| } |
| } |
| |
| /* Success */ |
| sta_statistic->status = 0; |
| } else { |
| /* Timer expired / peer don't support */ |
| sta_statistic->status = ni->ni_dotk_meas_state.meas_state_sta.status; |
| } |
| } else { |
| /* Canceled by signal */ |
| sta_statistic->status = -ECANCELED; |
| } |
| ret = 0; |
| } |
| ieee80211_free_node(ni); |
| } else { |
| ret = -EINVAL; |
| } |
| |
| return ret; |
| } |
| #endif |
| |
| static int |
| ieee80211_ioctl_getstastats(struct net_device *dev, struct iwreq *iwr) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_node *ni; |
| u_int8_t macaddr[IEEE80211_ADDR_LEN]; |
| const int off = __offsetof(struct ieee80211req_sta_stats, is_stats); |
| int error; |
| |
| if (iwr->u.data.length < off) |
| return -EINVAL; |
| if (copy_from_user(macaddr, iwr->u.data.pointer, IEEE80211_ADDR_LEN)) |
| return -EFAULT; |
| ni = ieee80211_find_node(&ic->ic_sta, macaddr); |
| if (ni == NULL) |
| return -EINVAL; /* XXX */ |
| if (ic->ic_get_shared_node_stats) |
| ic->ic_get_shared_node_stats(ni); |
| if (iwr->u.data.length > sizeof(struct ieee80211req_sta_stats)) |
| iwr->u.data.length = sizeof(struct ieee80211req_sta_stats); |
| /* NB: copy out only the statistics */ |
| error = copy_to_user(iwr->u.data.pointer + off, &ni->ni_stats, |
| iwr->u.data.length - off); |
| ieee80211_free_node(ni); |
| return (error ? -EFAULT : 0); |
| } |
| |
| struct scanreq { /* XXX: right place for declaration? */ |
| struct ieee80211req_scan_result *sr; |
| size_t space; |
| }; |
| |
| static size_t |
| scan_space(const struct ieee80211_scan_entry *se, int *ielen) |
| { |
| *ielen = 0; |
| if (se->se_rsn_ie != NULL) |
| *ielen += 2 + se->se_rsn_ie[1]; |
| if (se->se_wpa_ie != NULL) |
| *ielen += 2 + se->se_wpa_ie[1]; |
| if (se->se_wme_ie != NULL) |
| *ielen += 2 + se->se_wme_ie[1]; |
| if (se->se_ath_ie != NULL) |
| *ielen += 2 + se->se_ath_ie[1]; |
| if (se->se_htcap_ie != NULL) |
| *ielen += 2 + se->se_htcap_ie[1]; |
| |
| return roundup(sizeof(struct ieee80211req_scan_result) + |
| se->se_ssid[1] + *ielen, sizeof(u_int32_t)); |
| } |
| |
| static int |
| get_scan_space(void *arg, const struct ieee80211_scan_entry *se) |
| { |
| struct scanreq *req = arg; |
| int ielen; |
| |
| req->space += scan_space(se, &ielen); |
| |
| return 0; |
| } |
| |
| static int |
| get_scan_result(void *arg, const struct ieee80211_scan_entry *se) |
| { |
| struct scanreq *req = arg; |
| struct ieee80211req_scan_result *sr; |
| int ielen, len, nr, nxr; |
| u_int8_t *cp; |
| |
| len = scan_space(se, &ielen); |
| if (len > req->space) { |
| printk("[madwifi] %s() : Not enough space.\n", __FUNCTION__); |
| return 0; |
| } |
| |
| sr = req->sr; |
| memset(sr, 0, sizeof(*sr)); |
| sr->isr_ssid_len = se->se_ssid[1]; |
| /* XXX watch for overflow */ |
| sr->isr_ie_len = ielen; |
| sr->isr_len = len; |
| sr->isr_freq = se->se_chan->ic_freq; |
| sr->isr_flags = se->se_chan->ic_flags; |
| sr->isr_rssi = se->se_rssi; |
| sr->isr_intval = se->se_intval; |
| sr->isr_capinfo = se->se_capinfo; |
| sr->isr_erp = se->se_erp; |
| IEEE80211_ADDR_COPY(sr->isr_bssid, se->se_bssid); |
| /* XXX bounds check */ |
| nr = se->se_rates[1]; |
| memcpy(sr->isr_rates, se->se_rates + 2, nr); |
| nxr = se->se_xrates[1]; |
| memcpy(sr->isr_rates+nr, se->se_xrates + 2, nxr); |
| sr->isr_nrates = nr + nxr; |
| |
| cp = (u_int8_t *)(sr + 1); |
| memcpy(cp, se->se_ssid + 2, sr->isr_ssid_len); |
| cp += sr->isr_ssid_len; |
| if (se->se_rsn_ie != NULL) { |
| memcpy(cp, se->se_rsn_ie, 2 + se->se_rsn_ie[1]); |
| cp += 2 + se->se_rsn_ie[1]; |
| } |
| if (se->se_wpa_ie != NULL) { |
| memcpy(cp, se->se_wpa_ie, 2 + se->se_wpa_ie[1]); |
| cp += 2 + se->se_wpa_ie[1]; |
| } |
| if (se->se_wme_ie != NULL) { |
| memcpy(cp, se->se_wme_ie, 2 + se->se_wme_ie[1]); |
| cp += 2 + se->se_wme_ie[1]; |
| } |
| if (se->se_ath_ie != NULL) { |
| memcpy(cp, se->se_ath_ie, 2 + se->se_ath_ie[1]); |
| cp += 2 + se->se_ath_ie[1]; |
| } |
| if (se->se_htcap_ie != NULL) { |
| memcpy(cp, se->se_htcap_ie, 2 + se->se_htcap_ie[1]); |
| cp += 2 + se->se_htcap_ie[1]; |
| } |
| |
| req->space -= len; |
| req->sr = (struct ieee80211req_scan_result *)(((u_int8_t *)sr) + len); |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_getscanresults(struct net_device *dev, struct iwreq *iwr) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct scanreq req; |
| int error; |
| |
| if (iwr->u.data.length < sizeof(struct scanreq)) |
| return -EFAULT; |
| |
| error = 0; |
| req.space = 0; |
| ieee80211_scan_iterate(ic, get_scan_space, &req); |
| if (req.space > iwr->u.data.length) |
| req.space = iwr->u.data.length; |
| if (req.space > 0) { |
| size_t space; |
| void *p; |
| |
| space = req.space; |
| MALLOC(p, void *, space, M_TEMP, M_WAITOK); |
| if (p == NULL) |
| return -ENOMEM; |
| req.sr = p; |
| ieee80211_scan_iterate(ic, get_scan_result, &req); |
| iwr->u.data.length = space - req.space; |
| error = copy_to_user(iwr->u.data.pointer, p, iwr->u.data.length); |
| FREE(p, M_TEMP); |
| } else |
| iwr->u.data.length = 0; |
| |
| return (error ? -EFAULT : 0); |
| } |
| |
| struct stainforeq { /* XXX: right place for declaration? */ |
| struct ieee80211vap *vap; |
| struct ieee80211req_sta_info *si; |
| size_t space; |
| }; |
| |
| static size_t |
| sta_space(const struct ieee80211_node *ni, size_t *ielen) |
| { |
| *ielen = 0; |
| if (ni->ni_rsn_ie != NULL) |
| *ielen += 2+ni->ni_rsn_ie[1]; |
| if (ni->ni_wpa_ie != NULL) |
| *ielen += 2+ni->ni_wpa_ie[1]; |
| if (ni->ni_wme_ie != NULL) |
| *ielen += 2+ni->ni_wme_ie[1]; |
| if (ni->ni_ath_ie != NULL) |
| *ielen += 2+ni->ni_ath_ie[1]; |
| return roundup(sizeof(struct ieee80211req_sta_info) + *ielen, |
| sizeof(u_int32_t)); |
| } |
| |
| static void |
| get_sta_space(void *arg, struct ieee80211_node *ni) |
| { |
| struct stainforeq *req = arg; |
| struct ieee80211vap *vap = ni->ni_vap; |
| size_t ielen; |
| |
| if (vap != req->vap && vap != req->vap->iv_xrvap) /* only entries for this vap */ |
| return; |
| if ((vap->iv_opmode == IEEE80211_M_HOSTAP || |
| vap->iv_opmode == IEEE80211_M_WDS) && |
| ni->ni_associd == 0) /* only associated stations or a WDS peer */ |
| return; |
| req->space += sta_space(ni, &ielen); |
| } |
| |
| static void |
| get_sta_info(void *arg, struct ieee80211_node *ni) |
| { |
| struct stainforeq *req = arg; |
| struct ieee80211vap *vap = ni->ni_vap; |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211req_sta_info *si; |
| size_t ielen, len; |
| u_int8_t *cp; |
| |
| if (vap != req->vap && vap != req->vap->iv_xrvap) /* only entries for this vap (or) xrvap */ |
| return; |
| if ((vap->iv_opmode == IEEE80211_M_HOSTAP || |
| vap->iv_opmode == IEEE80211_M_WDS) && |
| ni->ni_associd == 0) /* only associated stations or a WDS peer */ |
| return; |
| if (ni->ni_chan == IEEE80211_CHAN_ANYC) /* XXX bogus entry */ |
| return; |
| len = sta_space(ni, &ielen); |
| if (len > req->space) |
| return; |
| si = req->si; |
| si->isi_len = len; |
| si->isi_ie_len = ielen; |
| si->isi_freq = ni->ni_chan->ic_freq; |
| si->isi_flags = ni->ni_chan->ic_flags; |
| si->isi_state = ni->ni_flags; |
| si->isi_authmode = ni->ni_authmode; |
| si->isi_rssi = ic->ic_node_getrssi(ni); |
| si->isi_capinfo = ni->ni_capinfo; |
| si->isi_athflags = ni->ni_ath_flags; |
| si->isi_erp = ni->ni_erp; |
| IEEE80211_ADDR_COPY(si->isi_macaddr, ni->ni_macaddr); |
| si->isi_nrates = ni->ni_rates.rs_nrates; |
| if (si->isi_nrates > 15) |
| si->isi_nrates = 15; |
| memcpy(si->isi_rates, ni->ni_rates.rs_rates, si->isi_nrates); |
| si->isi_txrate = ni->ni_txrate; |
| si->isi_ie_len = ielen; |
| si->isi_associd = ni->ni_associd; |
| si->isi_txpower = ni->ni_txpower; |
| si->isi_vlan = ni->ni_vlan; |
| if (ni->ni_flags & IEEE80211_NODE_QOS) { |
| memcpy(si->isi_txseqs, ni->ni_txseqs, sizeof(ni->ni_txseqs)); |
| memcpy(si->isi_rxseqs, ni->ni_rxseqs, sizeof(ni->ni_rxseqs)); |
| } else { |
| si->isi_txseqs[0] = ni->ni_txseqs[0]; |
| si->isi_rxseqs[0] = ni->ni_rxseqs[0]; |
| } |
| si->isi_uapsd = ni->ni_uapsd; |
| if ( vap == req->vap->iv_xrvap) |
| si->isi_opmode = IEEE80211_STA_OPMODE_XR; |
| else |
| si->isi_opmode = IEEE80211_STA_OPMODE_NORMAL; |
| /* NB: leave all cases in case we relax ni_associd == 0 check */ |
| if (ieee80211_node_is_authorized(ni)) |
| si->isi_inact = vap->iv_inact_run; |
| else if (ni->ni_associd != 0) |
| si->isi_inact = vap->iv_inact_auth; |
| else |
| si->isi_inact = vap->iv_inact_init; |
| si->isi_inact = (si->isi_inact - ni->ni_inact) * IEEE80211_INACT_WAIT; |
| |
| cp = (u_int8_t *)(si+1); |
| if (ni->ni_rsn_ie != NULL) { |
| memcpy(cp, ni->ni_rsn_ie, 2 + ni->ni_rsn_ie[1]); |
| cp += 2 + ni->ni_rsn_ie[1]; |
| } |
| if (ni->ni_wpa_ie != NULL) { |
| memcpy(cp, ni->ni_wpa_ie, 2 + ni->ni_wpa_ie[1]); |
| cp += 2 + ni->ni_wpa_ie[1]; |
| } |
| if (ni->ni_wme_ie != NULL) { |
| memcpy(cp, ni->ni_wme_ie, 2 + ni->ni_wme_ie[1]); |
| cp += 2 + ni->ni_wme_ie[1]; |
| } |
| if (ni->ni_ath_ie != NULL) { |
| memcpy(cp, ni->ni_ath_ie, 2 + ni->ni_ath_ie[1]); |
| cp += 2 + ni->ni_ath_ie[1]; |
| } |
| |
| req->si = (struct ieee80211req_sta_info *)(((u_int8_t *)si) + len); |
| req->space -= len; |
| } |
| |
| static int |
| ieee80211_ioctl_getstainfo(struct net_device *dev, struct iwreq *iwr) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct stainforeq req; |
| int error; |
| |
| if (iwr->u.data.length < sizeof(struct stainforeq)) |
| return -EFAULT; |
| |
| /* estimate space required for station info */ |
| error = 0; |
| req.space = sizeof(struct stainforeq); |
| req.vap = vap; |
| ieee80211_iterate_nodes(&ic->ic_sta, get_sta_space, &req, 1); |
| if (req.space > iwr->u.data.length) |
| req.space = iwr->u.data.length; |
| if (req.space > 0) { |
| size_t space; |
| void *p; |
| |
| space = req.space; |
| MALLOC(p, void *, space, M_TEMP, M_WAITOK); |
| req.si = (struct ieee80211req_sta_info *)p; |
| ieee80211_iterate_nodes(&ic->ic_sta, get_sta_info, &req, 1); |
| iwr->u.data.length = space - req.space; |
| error = copy_to_user(iwr->u.data.pointer, p, iwr->u.data.length); |
| FREE(p, M_TEMP); |
| } else |
| iwr->u.data.length = 0; |
| |
| return (error ? -EFAULT : 0); |
| } |
| |
| static __inline const char * |
| ieee80211_get_vendor_str(uint8_t vendor) |
| { |
| const char *vendor_str = "unknown"; |
| |
| switch (vendor) { |
| case PEER_VENDOR_QTN: |
| vendor_str = "qtn"; |
| break; |
| case PEER_VENDOR_BRCM: |
| vendor_str = "brcm"; |
| break; |
| case PEER_VENDOR_ATH: |
| vendor_str = "ath"; |
| break; |
| case PEER_VENDOR_RLNK: |
| vendor_str = "rlnk"; |
| break; |
| case PEER_VENDOR_RTK: |
| vendor_str = "rtk"; |
| break; |
| case PEER_VENDOR_INTEL: |
| vendor_str = "intel"; |
| break; |
| default: |
| break; |
| } |
| |
| return vendor_str; |
| } |
| |
| /* This array must be kept in sync with the IEEE80211_NODE_TYPE_xxx values */ |
| static const char * |
| ieee80211_node_type_str[] = { |
| "none", |
| "vap", |
| "sta", |
| "wds", |
| "tdls" |
| }; |
| |
| static __inline const char * |
| ieee80211_get_node_type_str(uint8_t type) |
| { |
| if (type >= ARRAY_SIZE(ieee80211_node_type_str)) |
| type = IEEE80211_NODE_TYPE_NONE; |
| |
| return ieee80211_node_type_str[type]; |
| } |
| |
| static void |
| get_node_ht_bw_and_sgi(struct ieee80211com *ic, struct ieee80211_node *ni, |
| uint8_t *bw, uint8_t *assoc_bw, uint8_t *sgi) |
| { |
| if (ic->ic_htcap.cap & IEEE80211_HTCAP_C_CHWIDTH40 && |
| ni->ni_htcap.cap & IEEE80211_HTCAP_C_CHWIDTH40) { |
| *assoc_bw = 40; |
| *bw = IEEE80211_CWM_WIDTH40; |
| if (ic->ic_htcap.cap & IEEE80211_HTCAP_C_SHORTGI40 && |
| ni->ni_htcap.cap & IEEE80211_HTCAP_C_SHORTGI40) |
| *sgi = 1; |
| else |
| *sgi = 0; |
| } else { |
| *assoc_bw = 20; |
| *bw = IEEE80211_CWM_WIDTH20; |
| if (ic->ic_htcap.cap & IEEE80211_HTCAP_C_SHORTGI20 && |
| ni->ni_htcap.cap & IEEE80211_HTCAP_C_SHORTGI20) |
| *sgi = 1; |
| else |
| *sgi = 0; |
| } |
| } |
| |
| static void |
| get_node_vht_bw_and_sgi(struct ieee80211_node *ni, uint8_t *bw, |
| uint8_t *assoc_bw, uint8_t *sgi) |
| { |
| switch (ni->ni_vhtop.chanwidth) { |
| case IEEE80211_VHTOP_CHAN_WIDTH_160MHZ: |
| case IEEE80211_VHTOP_CHAN_WIDTH_80PLUS80MHZ: |
| *bw = IEEE80211_CWM_WIDTH160; |
| *assoc_bw = 160; |
| if (ni->ni_vhtcap.cap_flags & IEEE80211_VHTCAP_C_SHORT_GI_160) |
| *sgi = 1; |
| else |
| *sgi = 0; |
| break; |
| case IEEE80211_VHTOP_CHAN_WIDTH_80MHZ: |
| *bw = IEEE80211_CWM_WIDTH80; |
| *assoc_bw = 80; |
| if (ni->ni_vhtcap.cap_flags & IEEE80211_VHTCAP_C_SHORT_GI_80) |
| *sgi = 1; |
| else |
| *sgi = 0; |
| break; |
| case IEEE80211_VHTOP_CHAN_WIDTH_20_40MHZ: |
| get_node_ht_bw_and_sgi(ni->ni_ic, ni, bw, assoc_bw, sgi); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| static void |
| get_node_ht_max_mcs(struct ieee80211com *ic, struct ieee80211_node *ni, |
| uint8_t *max_mcs) |
| { |
| int i; |
| |
| for (i = IEEE80211_HT_MCSSET_20_40_NSS1; i <= IEEE80211_HT_MCSSET_20_40_NSS4; i++) { |
| if ((ic->ic_htcap.mcsset[i] & ni->ni_htcap.mcsset[i]) == 0xff) |
| *max_mcs = 8 * (i - IEEE80211_HT_MCSSET_20_40_NSS1 + 1) - 1; |
| } |
| } |
| |
| static void |
| get_node_vht_max_nss_mcs(struct ieee80211_node *ni, uint8_t *max_nss, uint8_t *max_mcs) |
| { |
| uint8_t mcs = 0; |
| uint8_t nss = 0; |
| uint16_t tx_mcsnss_map; |
| int i; |
| |
| tx_mcsnss_map = ni->ni_vhtcap.txmcsmap; |
| |
| for (i = IEEE80211_VHT_NSS1; i <= IEEE80211_VHT_NSS8; i++) { |
| switch (tx_mcsnss_map & 0x03) { |
| case IEEE80211_VHT_MCS_0_7: |
| mcs = 7; |
| nss++; |
| break; |
| case IEEE80211_VHT_MCS_0_8: |
| mcs = 8; |
| nss++; |
| break; |
| case IEEE80211_VHT_MCS_0_9: |
| mcs = 9; |
| nss++; |
| break; |
| case IEEE80211_VHT_MCS_NA: |
| default: |
| /* do nothing */ |
| break; |
| } |
| tx_mcsnss_map >>= 2; |
| } |
| |
| if (mcs != 0) { |
| *max_mcs = mcs; |
| *max_nss = nss - 1; /* Nss index starts from 0 */ |
| } |
| } |
| |
| static void get_node_max_mimo(struct ieee80211_node *ni, uint8_t *tx, uint8_t *rx) |
| { |
| uint8_t tx_max = 0; |
| uint8_t rx_max = 0; |
| uint16_t vht_mcsmap = 0; |
| struct ieee80211_ie_htcap *htcap = (struct ieee80211_ie_htcap *)&ni->ni_ie_htcap; |
| struct ieee80211_ie_vhtcap *vhtcap = |
| (struct ieee80211_ie_vhtcap *)&ni->ni_ie_vhtcap; |
| |
| switch (ni->ni_wifi_mode) { |
| case IEEE80211_WIFI_MODE_AC: |
| vht_mcsmap = IEEE80211_VHTCAP_GET_RX_MCS_NSS(vhtcap); |
| for (rx_max = 0; rx_max < IEEE80211_VHTCAP_MCS_MAX; ++rx_max) { |
| if (IEEE80211_VHTCAP_GET_MCS_MAP_ENTRY(vht_mcsmap, |
| rx_max) == IEEE80211_VHTCAP_MCS_DISABLED) |
| break; |
| } |
| vht_mcsmap = IEEE80211_VHTCAP_GET_TX_MCS_NSS(vhtcap); |
| for (tx_max = 0; tx_max < IEEE80211_VHTCAP_MCS_MAX; ++tx_max) { |
| if (IEEE80211_VHTCAP_GET_MCS_MAP_ENTRY(vht_mcsmap, |
| tx_max) == IEEE80211_VHTCAP_MCS_DISABLED) |
| break; |
| } |
| break; |
| case IEEE80211_WIFI_MODE_NA: |
| case IEEE80211_WIFI_MODE_NG: |
| if (IEEE80211_HT_IS_4SS_NODE(htcap->hc_mcsset)) { |
| rx_max = 4; |
| } else if (IEEE80211_HT_IS_3SS_NODE(htcap->hc_mcsset)) { |
| rx_max = 3; |
| } else if (IEEE80211_HT_IS_2SS_NODE(htcap->hc_mcsset)) { |
| rx_max = 2; |
| } |
| if ((IEEE80211_HTCAP_MCS_PARAMS(htcap) & |
| IEEE80211_HTCAP_MCS_TX_SET_DEFINED) && |
| (IEEE80211_HTCAP_MCS_PARAMS(htcap) & |
| IEEE80211_HTCAP_MCS_TX_RX_SET_NEQ)) { |
| tx_max = IEEE80211_HTCAP_MCS_STREAMS(htcap) + 1; |
| } else if (IEEE80211_HTCAP_MCS_PARAMS(htcap) & |
| IEEE80211_HTCAP_MCS_TX_RX_SET_NEQ) { |
| tx_max = 0; |
| } else { |
| tx_max = rx_max; |
| } |
| break; |
| default: |
| /* Non ht mode */ |
| tx_max = 1; |
| rx_max = 1; |
| break; |
| } |
| |
| *tx = tx_max; |
| *rx = rx_max; |
| } |
| |
| static void get_node_achievable_phyrate_and_bw(struct ieee80211_node *ni, |
| uint32_t *tx_rate, |
| uint32_t *rx_rate, |
| uint8_t *node_bw) |
| { |
| struct ieee80211vap *vap = ni->ni_vap; |
| struct ieee80211com *ic = vap->iv_ic; |
| uint8_t bw = 0; |
| uint8_t assoc_bw = 0; |
| uint8_t sgi = 0; |
| uint8_t max_mcs = 0; |
| uint8_t max_nss = 0; |
| uint8_t vht = 0; |
| |
| if (ni->ni_wifi_mode != IEEE80211_WIFI_MODE_NG && |
| ni->ni_wifi_mode != IEEE80211_WIFI_MODE_NA && |
| ni->ni_wifi_mode != IEEE80211_WIFI_MODE_AC) { |
| *tx_rate = |
| (ni->ni_rates.rs_rates[ni->ni_rates.rs_nrates - 1] & |
| IEEE80211_RATE_VAL) / 2; |
| *rx_rate = *tx_rate; |
| *node_bw = 20; |
| return; |
| } |
| |
| if (IS_IEEE80211_VHT_ENABLED(ic) && (ni->ni_flags & IEEE80211_NODE_VHT)) { |
| vht = 1; |
| get_node_vht_bw_and_sgi(ni, &bw, &assoc_bw, &sgi); |
| get_node_vht_max_nss_mcs(ni, &max_nss, &max_mcs); |
| } else { |
| get_node_ht_bw_and_sgi(ic, ni, &bw, &assoc_bw, &sgi); |
| get_node_ht_max_mcs(ic, ni, &max_mcs); |
| } |
| |
| *tx_rate = ic->ic_mcs_to_phyrate(bw, sgi, max_mcs, max_nss, vht); |
| *rx_rate = *tx_rate; |
| *node_bw = assoc_bw; |
| } |
| |
| void sample_rel_client_data(struct ieee80211vap *vap) |
| { |
| struct node_client_data *ncd, *ncd_tmp; |
| |
| spin_lock(&vap->sample_sta_lock); |
| list_for_each_entry_safe(ncd, ncd_tmp, &vap->sample_sta_list, node_list) { |
| list_del(&ncd->node_list); |
| kfree(ncd); |
| } |
| vap->sample_sta_count = 0; |
| spin_unlock(&vap->sample_sta_lock); |
| } |
| |
| static void sample_iterate_client_data(void *s, struct ieee80211_node *ni) |
| { |
| struct ieee80211vap *vap = (struct ieee80211vap *)s; |
| struct node_client_data *clt = NULL; |
| int i; |
| struct qtn_node_shared_stats_rx *rx = &ni->ni_shared_stats->rx[STATS_SU]; |
| |
| /* Skipping other interface node and self vap node */ |
| if ((ni->ni_vap != vap) || |
| (memcmp(ni->ni_macaddr, vap->iv_myaddr, IEEE80211_ADDR_LEN) == 0)) { |
| return; |
| } |
| |
| MALLOC(clt, struct node_client_data *, sizeof(struct node_client_data), |
| M_TEMP, M_WAITOK | M_ZERO); |
| |
| if (clt == NULL) { |
| printk("Failed to alloc client data\n"); |
| return; |
| } |
| |
| IEEE80211_ADDR_COPY(clt->data.mac_addr, ni->ni_macaddr); |
| |
| clt->data.assoc_id = IEEE80211_AID(ni->ni_associd); |
| clt->data.protocol = ni->ni_wifi_mode; |
| clt->data.time_associated = (u_int32_t)div_u64(get_jiffies_64() - |
| ni->ni_start_time_assoc, HZ); |
| |
| get_node_max_mimo(ni, &clt->data.tx_stream, &clt->data.rx_stream); |
| get_node_achievable_phyrate_and_bw(ni, &clt->data.achievable_tx_phy_rate, |
| &clt->data.achievable_rx_phy_rate, |
| &clt->data.bw); |
| |
| clt->data.rx_bytes = ni->ni_stats.ns_rx_bytes; |
| clt->data.tx_bytes = ni->ni_stats.ns_tx_bytes; |
| clt->data.rx_packets = ni->ni_stats.ns_rx_data; |
| clt->data.tx_packets = ni->ni_stats.ns_tx_data; |
| clt->data.rx_errors = ni->ni_stats.ns_rx_errors; |
| clt->data.tx_errors = ni->ni_stats.ns_tx_errors; |
| clt->data.rx_dropped = ni->ni_stats.ns_rx_dropped; |
| clt->data.tx_dropped = ni->ni_stats.ns_tx_dropped; |
| clt->data.rx_ucast = ni->ni_stats.ns_rx_ucast; |
| clt->data.tx_ucast = ni->ni_stats.ns_tx_ucast; |
| clt->data.rx_mcast = ni->ni_stats.ns_rx_mcast; |
| clt->data.tx_mcast = ni->ni_stats.ns_tx_mcast; |
| clt->data.rx_bcast = ni->ni_stats.ns_rx_bcast; |
| clt->data.tx_bcast = ni->ni_stats.ns_tx_bcast; |
| clt->data.link_quality = (uint16_t) ni->ni_linkqual; |
| clt->data.ip_addr = ni->ni_ip_addr; |
| clt->data.vendor = ni->ni_vendor; |
| |
| for (i = 0; i < WME_AC_NUM; i++) |
| clt->data.tx_wifi_drop[i] = ni->ni_stats.ns_tx_wifi_drop[i]; |
| |
| for (i = 0; i <= NUM_ANT; i++) { |
| clt->data.last_rssi_dbm[i] = rx->last_rssi_dbm[i]; |
| clt->data.last_rcpi_dbm[i] = rx->last_rcpi_dbm[i]; |
| clt->data.last_evm_dbm[i] = rx->last_evm_dbm[i]; |
| clt->data.last_hw_noise[i] = rx->last_hw_noise[i]; |
| } |
| |
| spin_lock(&vap->sample_sta_lock); |
| list_add(&clt->node_list, &vap->sample_sta_list); |
| vap->sample_sta_count++; |
| spin_unlock(&vap->sample_sta_lock); |
| } |
| |
| static int |
| ieee80211_subioctl_sample_all_clients(struct ieee80211vap *vap, struct iwreq *iwr) |
| { |
| struct ieee80211com *ic = vap->iv_ic; |
| uint8_t __user *sta_count = iwr->u.data.pointer; |
| |
| sample_rel_client_data(vap); |
| ic->ic_iterate_nodes(&ic->ic_sta, sample_iterate_client_data, vap, 1); |
| |
| if (copy_to_user(sta_count, &vap->sample_sta_count, sizeof(*sta_count)) != 0) |
| return -EFAULT; |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_subioctl_get_assoc_data(struct ieee80211vap *vap, void __user *pointer, uint32_t len) |
| { |
| struct node_client_data *ncd; |
| int count = 0; |
| int u_count = 0; |
| int offset; |
| int num_entry; |
| struct sample_assoc_user_data __user *u_pointer = |
| (struct sample_assoc_user_data *)pointer; |
| |
| if (copy_from_user(&num_entry, &u_pointer->num_entry, sizeof(num_entry)) != 0) |
| return -EFAULT; |
| |
| if (copy_from_user(&offset, &u_pointer->offset, sizeof(offset)) != 0) |
| return -EFAULT; |
| |
| if (vap->sample_sta_count < (num_entry + offset)) |
| return -EINVAL; |
| |
| if (len < (num_entry * sizeof(struct sample_assoc_data) |
| + sizeof(u_pointer->num_entry) |
| + sizeof(u_pointer->offset))) |
| return -EINVAL; |
| |
| spin_lock(&vap->sample_sta_lock); |
| list_for_each_entry(ncd, &vap->sample_sta_list, node_list) { |
| if ((count >= offset) && (count < (num_entry + offset))) { |
| if (copy_to_user(((u_pointer->data) + (u_count)), |
| &(ncd->data), sizeof(struct sample_assoc_data)) != 0) { |
| spin_unlock(&vap->sample_sta_lock); |
| return -EFAULT; |
| } |
| u_count++; |
| } |
| count++; |
| } |
| |
| spin_unlock(&vap->sample_sta_lock); |
| |
| return 0; |
| } |
| |
| static void |
| get_interface_wmmac_stats(void *s, struct ieee80211_node *ni) |
| { |
| struct ieee80211req_interface_wmmac_stats wmmac_stats; |
| struct iwreq *iwr = (struct iwreq *)s; |
| struct ieee80211vap *vap = ni->ni_vap; |
| struct ieee80211com *ic = vap->iv_ic; |
| void __user *pointer; |
| int i; |
| |
| pointer = iwr->u.data.pointer; |
| if (copy_from_user(&wmmac_stats, pointer, sizeof(wmmac_stats))) |
| return; |
| |
| if (ic->ic_get_shared_node_stats) |
| ic->ic_get_shared_node_stats(ni); |
| |
| for (i = 0; i < WMM_AC_NUM; i++) { |
| wmmac_stats.tx_wifi_drop[i] += ni->ni_stats.ns_tx_wifi_drop[i]; |
| wmmac_stats.tx_wifi_sent[i] += ni->ni_shared_stats->tx[STATS_SU].tx_sent_data_msdu[i]; |
| } |
| |
| copy_to_user(pointer, &wmmac_stats, sizeof(wmmac_stats)); |
| } |
| |
| static int |
| ieee80211_subioctl_get_interface_wmmac_stats(struct net_device *dev, |
| struct iwreq *iwr) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| |
| ic->ic_iterate_dev_nodes(vap->iv_dev, &ic->ic_sta, get_interface_wmmac_stats, iwr, 1); |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_subioctl_get_freq_range(struct net_device *dev, struct iwreq *iwr) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_freq_range range; |
| uint8_t reported[IEEE80211_CHAN_BYTES]; |
| uint8_t *chan_active; |
| int i; |
| |
| if (iwr->u.data.length < sizeof(range)) |
| return -ENOMEM; |
| |
| if (vap->iv_opmode == IEEE80211_M_STA) |
| chan_active = ic->ic_chan_active_20; |
| else |
| chan_active = ic->ic_chan_active; |
| |
| memset(reported, 0, sizeof(reported)); |
| memset(&range, 0, sizeof(range)); |
| for (i = 0; i < ic->ic_nchans; i++) { |
| const struct ieee80211_channel *c = &ic->ic_channels[i]; |
| |
| /* discard if previously reported (e.g. b/g) */ |
| if (isclr(reported, c->ic_ieee) && |
| isset(chan_active, c->ic_ieee)) { |
| setbit(reported, c->ic_ieee); |
| range.freq[range.num_freq].i = c->ic_ieee; |
| range.freq[range.num_freq].m = |
| ic->ic_channels[i].ic_freq * 100000; |
| range.freq[range.num_freq].e = 1; |
| if (++range.num_freq == QTN_FREQ_RANGE_MAX_NUM) |
| break; |
| } |
| } |
| |
| |
| iwr->u.data.length = sizeof(range); |
| if (copy_to_user(iwr->u.data.pointer, &range, sizeof(range)) != 0) |
| return -EFAULT; |
| |
| return 0; |
| } |
| |
| #ifdef CONFIG_NAC_MONITOR |
| #define NAC_MAX_STATIONS 128 |
| static int |
| ieee80211_subioctl_get_nac_stats(struct net_device *dev, struct iwreq *iwr) |
| { |
| void __user *pointer; |
| struct ieee80211_nac_stats_report *report = NULL; |
| struct shared_params *sp = qtn_mproc_sync_shared_params_get(); |
| struct nac_stats_entry *entry = &sp->nac_mon_info->nac_stats[0]; |
| int i,j; |
| int retval = 0; |
| |
| report = kmalloc(sizeof(*report), GFP_KERNEL); |
| if (!report) { |
| retval = -ENOMEM; |
| goto END; |
| } |
| pointer = iwr->u.data.pointer; |
| if (copy_from_user(report, pointer, sizeof(*report))) { |
| retval = -EFAULT; |
| goto END; |
| } |
| report->nac_entries = 0; |
| for (i = 0,j=0; i < NAC_MAX_STATIONS; i++, entry++) { |
| if(entry->nac_valid) { |
| memcpy(&report->nac_stats[i].nac_txmac[0], |
| &entry->nac_txmac[0], |
| IEEE80211_ADDR_LEN); |
| report->nac_stats[j].nac_avg_rssi = entry->nac_avg_rssi; |
| report->nac_stats[j].nac_timestamp = entry->nac_timestamp; |
| report->nac_stats[j].nac_channel = entry->nac_channel; |
| report->nac_stats[j].nac_packet_type = entry->nac_packet_type; |
| j++; |
| } |
| } |
| report->nac_entries = j; |
| |
| if (copy_to_user(pointer, report, sizeof(*report)) != 0) |
| retval = -EFAULT; |
| END: |
| if (report) { |
| kfree(report); |
| } |
| return retval; |
| } |
| #endif |
| |
| static int |
| ieee80211_subioctl_ft_add_node(struct net_device *dev, |
| void __user *pointer, uint32_t len) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_node *ni = NULL; |
| uint8_t addr[IEEE80211_ADDR_LEN]; |
| |
| int retval = 0; |
| |
| if ( len < IEEE80211_ADDR_LEN) |
| return -1; |
| |
| if (copy_from_user(&addr[0], pointer, len) != 0) { |
| retval = -EFAULT; |
| } |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_AUTH, "[%s] FT add node req\n", ether_sprintf(&addr[0])); |
| ni = ieee80211_find_node(&ic->ic_sta, &addr[0]); |
| if (!ni) { |
| ni = ieee80211_dup_bss(vap, &addr[0]); |
| if (ni == NULL) { |
| mlme_stats_delayed_update(&addr[0], MLME_STAT_AUTH_FAILS, 1); |
| return -1; |
| } |
| ni->ni_node_type = IEEE80211_NODE_TYPE_STA; |
| ni->ni_used_auth_algo = IEEE80211_AUTH_ALG_FT; |
| } |
| IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, |
| ni, "station authenticated (%s)", "FT"); |
| mlme_stats_delayed_update(ni->ni_macaddr, MLME_STAT_AUTH, 1); |
| ieee80211_free_node(ni); |
| return retval; |
| } |
| |
| static int |
| ieee80211_subioctl_send_ft_auth_response(struct net_device *dev, |
| void __user *pointer, uint32_t len) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_frame *frm = NULL;; |
| struct ieee80211_node *ni = NULL; |
| int subtype = 0; |
| struct ieee80211_auth *auth = NULL; |
| uint8_t *buf = NULL; |
| int retval = 0; |
| uint8_t *data = NULL; |
| uint8_t *efrm = NULL; |
| |
| if (len < (sizeof(*frm) + sizeof(*auth))) |
| return -EINVAL; |
| |
| MALLOC(buf, u_int8_t *, len, M_DEVBUF, M_WAITOK); |
| if(!buf) |
| return -ENOMEM; |
| |
| if (copy_from_user(buf, pointer, len) != 0) { |
| retval = -EFAULT; |
| goto END; |
| } |
| |
| data = buf; |
| frm = (struct ieee80211_frame *)data; |
| subtype = frm->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; |
| data += sizeof(*frm); |
| auth = (struct ieee80211_auth *)data; |
| ni = ieee80211_find_node(&ic->ic_sta, frm->i_addr1); |
| if (!ni) { |
| retval = 1; |
| goto END; |
| } |
| data += sizeof(*auth); |
| |
| if (auth->status_code == 0 && (auth->auth_alg == IEEE80211_AUTH_ALG_FT)) { |
| uint8_t ielen = 0; |
| |
| efrm = buf + len; |
| ielen = data[1] + 2; |
| while ((data < efrm) && (ielen < 255) && ((data + ielen) <= efrm)) { |
| switch (*data) { |
| case IEEE80211_ELEMID_RSN: |
| if (ni->ni_tx_rsn_ie) |
| FREE(ni->ni_tx_rsn_ie, M_DEVBUF); |
| MALLOC(ni->ni_tx_rsn_ie, void*, ielen, M_DEVBUF, M_ZERO); |
| if (ni->ni_tx_rsn_ie) |
| memcpy(ni->ni_tx_rsn_ie, data, ielen); |
| break; |
| case IEEE80211_ELEMID_MOBILITY_DOMAIN: |
| if (ni->ni_tx_md_ie) |
| FREE(ni->ni_tx_md_ie, M_DEVBUF); |
| MALLOC(ni->ni_tx_md_ie, void*, ielen, M_DEVBUF, M_ZERO); |
| if (ni->ni_tx_md_ie) |
| memcpy(ni->ni_tx_md_ie, data, ielen); |
| break; |
| case IEEE80211_ELEMID_FTIE: |
| if (ni->ni_tx_ft_ie) |
| FREE(ni->ni_tx_ft_ie, M_DEVBUF); |
| MALLOC(ni->ni_tx_ft_ie, void*, ielen, M_DEVBUF, M_ZERO); |
| if (ni->ni_tx_ft_ie) |
| memcpy(ni->ni_tx_ft_ie, data, ielen); |
| break; |
| default: |
| break; |
| } |
| data += ielen; |
| ielen = data[1] + 2; |
| } |
| } |
| if ((subtype == IEEE80211_FC0_SUBTYPE_AUTH) && (auth->auth_alg == IEEE80211_AUTH_ALG_FT)) { |
| IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_AUTH, |
| (auth->status_code << 16) | IEEE80211_AUTH_FT); |
| mlme_stats_delayed_update(ni->ni_macaddr, MLME_STAT_AUTH, 1); |
| } |
| ieee80211_free_node(ni); |
| |
| END: |
| FREE(buf, M_DEVBUF); |
| return retval; |
| } |
| |
| static int |
| ieee80211_subioctl_send_ft_assoc_response(struct net_device *dev, |
| void __user *pointer, uint32_t len) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_frame *frm = NULL; |
| struct ieee80211_node *ni = NULL; |
| uint8_t *buf = NULL; |
| struct ieee80211_assoc *assoc = NULL; |
| int retval = 0; |
| uint8_t *data = NULL; |
| uint8_t *efrm = NULL; |
| |
| if (len < (sizeof(*frm) + sizeof(*assoc))) |
| return -EINVAL; |
| |
| MALLOC(buf, u_int8_t *, len, M_DEVBUF, M_WAITOK); |
| if(!buf) |
| return -ENOMEM; |
| |
| if (copy_from_user(buf, pointer, len) != 0) { |
| retval = -EFAULT; |
| goto END; |
| } |
| |
| data = buf; |
| frm = (struct ieee80211_frame *)buf; |
| data += sizeof(*frm); |
| assoc = (struct ieee80211_assoc *)data; |
| |
| ni = ieee80211_find_node(&ic->ic_sta, frm->i_addr1); |
| if (!ni) { |
| retval = 1; |
| goto END; |
| } |
| data += sizeof(*assoc); |
| |
| if (assoc->assoc_status_code == IEEE80211_STATUS_SUCCESS) { |
| uint8_t ielen = 0; |
| |
| efrm = buf + len; |
| ielen = data[1] + 2; |
| while ((data < efrm) && (ielen < 255) && ((data + ielen) <= efrm)) { |
| switch (*data) { |
| case IEEE80211_ELEMID_MOBILITY_DOMAIN: |
| if (ni->ni_tx_md_ie) |
| FREE(ni->ni_tx_md_ie, M_DEVBUF); |
| MALLOC(ni->ni_tx_md_ie, void*, ielen, M_DEVBUF, M_ZERO); |
| if (ni->ni_tx_md_ie) |
| memcpy(ni->ni_tx_md_ie, data, ielen); |
| break; |
| case IEEE80211_ELEMID_FTIE: |
| if (ni->ni_tx_ft_ie) |
| FREE(ni->ni_tx_ft_ie, M_DEVBUF); |
| MALLOC(ni->ni_tx_ft_ie, void*, ielen, M_DEVBUF, M_ZERO); |
| if (ni->ni_tx_ft_ie) |
| memcpy(ni->ni_tx_ft_ie, data, ielen); |
| break; |
| default: |
| break; |
| } |
| data += ielen; |
| ielen = data[1] + 2; |
| } |
| ieee80211_node_join(ni, IEEE80211_FC0_SUBTYPE_ASSOC_RESP); |
| } else { |
| IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DEAUTH, assoc->assoc_status_code); |
| } |
| ieee80211_free_node(ni); |
| END: |
| FREE(buf, M_DEVBUF); |
| return retval; |
| } |
| |
| static int |
| ieee80211_subioctl_send_ft_reassoc_response(struct net_device *dev, |
| void __user *pointer, uint32_t len) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_frame *frm = NULL; |
| struct ieee80211_node *ni = NULL; |
| struct ieee80211_assoc *assoc = NULL; |
| int retval = 0; |
| uint8_t *buf = NULL; |
| uint8_t *data = NULL; |
| uint8_t *efrm = NULL; |
| |
| if (len < (sizeof(*frm) + sizeof(*assoc))) |
| return -EINVAL; |
| |
| MALLOC(buf, u_int8_t *, len, M_DEVBUF, M_WAITOK); |
| if(!buf) |
| return -ENOMEM; |
| if (copy_from_user(buf, pointer,len) != 0) { |
| retval = -EFAULT; |
| goto END; |
| } |
| |
| data = buf; |
| frm = (struct ieee80211_frame *)buf; |
| data += sizeof(*frm); |
| assoc = (struct ieee80211_assoc *)data; |
| |
| ni = ieee80211_find_node(&ic->ic_sta, frm->i_addr1); |
| if (!ni) { |
| retval = 1; |
| goto END; |
| } |
| data += sizeof(*assoc); |
| |
| if (assoc->assoc_status_code == IEEE80211_STATUS_SUCCESS) { |
| uint8_t ielen = 0; |
| |
| efrm = buf + len; |
| ielen = data[1] + 2; |
| while ((data < efrm) && (ielen < 255) && ((data + ielen) <= efrm)) { |
| switch (*data) { |
| case IEEE80211_ELEMID_RSN: |
| if (ni->ni_tx_rsn_ie) |
| FREE(ni->ni_tx_rsn_ie, M_DEVBUF); |
| MALLOC(ni->ni_tx_rsn_ie, void*, ielen, M_DEVBUF, M_ZERO); |
| if (ni->ni_tx_rsn_ie) |
| memcpy(ni->ni_tx_rsn_ie, data, ielen); |
| break; |
| case IEEE80211_ELEMID_MOBILITY_DOMAIN: |
| if (ni->ni_tx_md_ie) |
| FREE(ni->ni_tx_md_ie, M_DEVBUF); |
| MALLOC(ni->ni_tx_md_ie, void*, ielen, M_DEVBUF, M_ZERO); |
| if (ni->ni_tx_md_ie) |
| memcpy(ni->ni_tx_md_ie, data, ielen); |
| break; |
| case IEEE80211_ELEMID_FTIE: |
| if (ni->ni_tx_ft_ie) |
| FREE(ni->ni_tx_ft_ie, M_DEVBUF); |
| MALLOC(ni->ni_tx_ft_ie, void*, ielen, M_DEVBUF, M_ZERO); |
| if (ni->ni_tx_ft_ie) |
| memcpy(ni->ni_tx_ft_ie, data, ielen); |
| break; |
| default: |
| break; |
| } |
| data += ielen; |
| ielen = data[1] + 2; |
| } |
| ieee80211_node_join(ni, IEEE80211_FC0_SUBTYPE_REASSOC_RESP); |
| } else { |
| IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DEAUTH, assoc->assoc_status_code); |
| } |
| ieee80211_free_node(ni); |
| END: |
| FREE(buf, M_DEVBUF); |
| return retval; |
| } |
| |
| /* |
| * This function will be called when processes write following command: |
| * get <unit> assoc_info. |
| * into file in sysfs. |
| * |
| * This function is to report all nodes in the table. User can do further filtering. |
| * E.g. user only need MAC address on designated WiFi interface. |
| * |
| * This function works for both AP and STA. |
| */ |
| void |
| get_node_info(void *s, struct ieee80211_node *ni) |
| { |
| struct seq_file *sq = (struct seq_file *)s; |
| struct ieee80211vap *vap = ni->ni_vap; |
| struct ieee80211com *ic = vap->iv_ic; |
| |
| uint8_t *mac = ni->ni_macaddr; |
| uint8_t assoc_bw = 20; |
| uint8_t bw = 0; |
| uint8_t sgi = 0; |
| uint8_t max_mcs = 0; |
| uint8_t max_nss = 0; |
| uint8_t vht; |
| uint32_t achievable_tx_phy_rate; |
| uint32_t achievable_rx_phy_rate; /* Unit: in Kbps */ |
| uint32_t time_associated = 0; /* Unit: second, 32bits should be up to 136 years */ |
| uint32_t combined_ba_state = 0; |
| int32_t i; |
| uint32_t current_tx_phy_rate = 0; |
| uint32_t current_rx_phy_rate = 0; |
| int32_t current_rssi = ic->ic_rssi(ni); |
| int32_t current_snr = ic->ic_snr(ni); |
| int32_t current_max_queue = ic->ic_max_queue(ni); |
| uint32_t tx_failed = ic->ic_tx_failed(ni); |
| |
| ic->ic_rxtx_phy_rate(ni, 0, NULL, NULL, ¤t_tx_phy_rate); |
| ic->ic_rxtx_phy_rate(ni, 1, NULL, NULL, ¤t_rx_phy_rate); |
| |
| if (IS_IEEE80211_VHT_ENABLED(ic) && (ni->ni_flags & IEEE80211_NODE_VHT)) { |
| vht = 1; |
| get_node_vht_bw_and_sgi(ni, &bw, &assoc_bw, &sgi); |
| } else { |
| vht = 0; |
| get_node_ht_bw_and_sgi(ic, ni, &bw, &assoc_bw, &sgi); |
| } |
| |
| if (ieee80211_node_is_authorized(ni)) { |
| if (vht == 0) |
| get_node_ht_max_mcs(ic, ni, &max_mcs); |
| else |
| get_node_vht_max_nss_mcs(ni, &max_nss, &max_mcs); |
| |
| achievable_tx_phy_rate = ic->ic_mcs_to_phyrate(bw, sgi, max_mcs, max_nss, vht); |
| achievable_rx_phy_rate = achievable_tx_phy_rate; |
| } else { |
| achievable_tx_phy_rate = 0; |
| achievable_rx_phy_rate = 0; |
| } |
| |
| if (current_rssi < -1 && current_rssi > -1200) { |
| ni->ni_rssi = current_rssi; |
| } else if (ni->ni_rssi > 0) { |
| /* correct pseudo RSSIs that apparently still get into the node table */ |
| ni->ni_rssi = (ni->ni_rssi * 10) - 900; |
| } |
| |
| ni->ni_smthd_rssi = ic->ic_smoothed_rssi(ni); |
| if ((ni->ni_smthd_rssi > -1) || (ni->ni_smthd_rssi < -1200)) { |
| ni->ni_smthd_rssi = ni->ni_rssi; |
| } |
| |
| if (current_tx_phy_rate > 0) { |
| ni->ni_linkqual = (uint16_t) current_tx_phy_rate; |
| } |
| |
| if (current_rx_phy_rate > 0) { |
| ni->ni_rx_phy_rate = (uint16_t) current_rx_phy_rate; |
| } |
| |
| if (current_snr < -1) { |
| ni->ni_snr = current_snr; |
| } |
| |
| if (current_max_queue > -1) { |
| ni->ni_max_queue = current_max_queue; |
| } |
| |
| if(vap->iv_opmode == IEEE80211_M_STA) { |
| time_associated = (vap->iv_state == IEEE80211_S_RUN) ? |
| (u_int32_t)div_u64(get_jiffies_64() - ni->ni_start_time_assoc, HZ) : 0; |
| } else { |
| time_associated = (vap->iv_bss == ni) ? |
| 0 : (u_int32_t)div_u64(get_jiffies_64() - ni->ni_start_time_assoc, HZ); |
| } |
| |
| COMPILE_TIME_ASSERT(WME_NUM_TID <= 16); |
| |
| for (i = 0; i < WME_NUM_TID; i++) { |
| combined_ba_state |= (int) (ni->ni_ba_rx[i].state == IEEE80211_BA_ESTABLISHED) |
| << (WME_NUM_TID + i); |
| combined_ba_state |= (int) (ni->ni_ba_tx[i].state == IEEE80211_BA_ESTABLISHED) << i; |
| } |
| |
| if (ic->ic_get_shared_node_stats) |
| ic->ic_get_shared_node_stats(ni); |
| |
| if (sq != NULL) { |
| /* NOTE: if this output format changes, there are flow-on effects to qcsapi. */ |
| seq_printf(sq, |
| "%02X:%02X:%02X:%02X:%02X:%02X " |
| "%4u %3u %10s %5u %5u %6u %6u %5d %5d %10u %12llu %10u %12llu " |
| "%5u %5u %5u %5u %7u %7u %7u %7u %7u %7u %5d %6u %2u %8u %4u %08x %s\n", |
| mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], |
| IEEE80211_AID(ni->ni_associd), |
| IEEE80211_NODE_IDX_UNMAP(ni->ni_node_idx), |
| ieee80211_tdls_status_string_get(ni->tdls_status), |
| ni->ni_linkqual, |
| ni->ni_rx_phy_rate, |
| achievable_tx_phy_rate, |
| achievable_rx_phy_rate, |
| ni->ni_smthd_rssi, |
| ni->ni_snr, |
| ni->ni_stats.ns_rx_data, |
| ni->ni_stats.ns_rx_bytes, |
| ni->ni_stats.ns_tx_data, |
| ni->ni_stats.ns_tx_bytes, |
| ni->ni_stats.ns_rx_errors, |
| ni->ni_stats.ns_rx_dropped, |
| ni->ni_stats.ns_tx_errors, |
| ni->ni_stats.ns_tx_dropped, |
| ni->ni_stats.ns_tx_ucast, |
| ni->ni_stats.ns_rx_ucast, |
| ni->ni_stats.ns_tx_mcast, |
| ni->ni_stats.ns_rx_mcast, |
| ni->ni_stats.ns_tx_bcast, |
| ni->ni_stats.ns_rx_bcast, |
| ni->ni_max_queue, |
| tx_failed, |
| assoc_bw, |
| time_associated, |
| ieee80211_node_is_authorized(ni), |
| combined_ba_state, |
| vap->iv_dev->name |
| ); |
| } |
| } |
| EXPORT_SYMBOL(get_node_info); |
| |
| void get_node_assoc_state(void *s, struct ieee80211_node *ni) |
| { |
| struct seq_file *sq = (struct seq_file *)s; |
| struct ieee80211vap *vap = ni->ni_vap; |
| struct ieee80211com *ic = vap->iv_ic; |
| uint8_t *mac = ni->ni_macaddr; |
| uint8_t assoc_bw = 20; |
| uint8_t bw = 0; |
| uint8_t sgi = 0; |
| uint32_t time_associated = 0; /* Unit: second, 32bits should be up to 136 years */ |
| uint32_t combined_ba_state = 0; |
| uint32_t i; |
| |
| const char *wifi_modes_strings[] = WLAN_WIFI_MODES_STRINGS; |
| COMPILE_TIME_ASSERT(ARRAY_SIZE(wifi_modes_strings) == IEEE80211_WIFI_MODE_MAX); |
| |
| if (!sq) |
| return; |
| |
| if (IS_IEEE80211_VHT_ENABLED(ic) && (ni->ni_flags & IEEE80211_NODE_VHT)) |
| get_node_vht_bw_and_sgi(ni, &bw, &assoc_bw, &sgi); |
| else |
| get_node_ht_bw_and_sgi(ic, ni, &bw, &assoc_bw, &sgi); |
| |
| if (vap->iv_opmode == IEEE80211_M_STA) |
| time_associated = (vap->iv_state == IEEE80211_S_RUN) ? |
| (u_int32_t)div_u64(get_jiffies_64()-ni->ni_start_time_assoc, HZ) : 0; |
| else |
| time_associated = (vap->iv_bss == ni) ? |
| 0 : (u_int32_t)div_u64(get_jiffies_64()-ni->ni_start_time_assoc, HZ); |
| |
| COMPILE_TIME_ASSERT(WME_NUM_TID <= 16); |
| |
| for (i = 0; i < WME_NUM_TID; i++) { |
| combined_ba_state |= (int) (ni->ni_ba_rx[i].state == IEEE80211_BA_ESTABLISHED) |
| << (WME_NUM_TID + i); |
| combined_ba_state |= (int) (ni->ni_ba_tx[i].state == IEEE80211_BA_ESTABLISHED) << i; |
| } |
| |
| seq_printf(sq, |
| "%pM %4u %4u %6s %4s %8s %4u %7u %6u %08x %12s %10s %16u\n", |
| mac, |
| IEEE80211_NODE_IDX_UNMAP(ni->ni_node_idx), |
| IEEE80211_AID(ni->ni_associd), |
| ieee80211_get_node_type_str(ni->ni_node_type), |
| wifi_modes_strings[ni->ni_wifi_mode], |
| ieee80211_get_vendor_str(ni->ni_vendor), |
| assoc_bw, |
| time_associated, |
| ieee80211_node_is_authorized(ni), |
| combined_ba_state, |
| ieee80211_tdls_status_string_get(ni->tdls_status), |
| vap->iv_dev->name, |
| ieee80211_node_power_save_scheme(ni)); |
| } |
| EXPORT_SYMBOL(get_node_assoc_state); |
| |
| void get_node_ver(void *s, struct ieee80211_node *ni) |
| { |
| struct seq_file *sq = (struct seq_file *)s; |
| #define IEEE80211_VER_SW_STR_LEN 15 |
| char sw_str[IEEE80211_VER_SW_STR_LEN]; |
| struct ieee80211com *ic = ni->ni_ic; |
| uint32_t *ver_sw; |
| uint16_t ver_platform_id; |
| uint16_t ver_hw; |
| uint32_t timestamp; |
| uint32_t flags; |
| |
| if (sq == NULL) { |
| return; |
| } |
| |
| if (IEEE80211_AID(ni->ni_associd) == 0) { |
| ver_sw = &ic->ic_ver_sw; |
| ver_platform_id = ic->ic_ver_platform_id; |
| ver_hw = ic->ic_ver_hw; |
| timestamp = ic->ic_ver_timestamp; |
| flags = ic->ic_ver_flags; |
| } else { |
| ver_sw = &ni->ni_ver_sw; |
| ver_platform_id = ni->ni_ver_platform_id; |
| ver_hw = ni->ni_ver_hw; |
| timestamp = ni->ni_ver_timestamp; |
| flags = ni->ni_ver_flags; |
| } |
| |
| snprintf(sw_str, sizeof(sw_str), |
| DBGFMT_BYTEFLD4_P, |
| DBGFMT_BYTEFLD4_V(*ver_sw)); |
| |
| seq_printf(sq, "%pM %4u %-*s %-8u %-6u %-10u 0x%08x\n", |
| ni->ni_macaddr, |
| IEEE80211_NODE_IDX_UNMAP(ni->ni_node_idx), |
| IEEE80211_VER_SW_STR_LEN, *ver_sw ? sw_str : "-", |
| ver_platform_id, |
| ver_hw, |
| timestamp, |
| flags); |
| } |
| EXPORT_SYMBOL(get_node_ver); |
| |
| void |
| ieee80211_update_node_assoc_qual(struct ieee80211_node *ni) |
| { |
| struct ieee80211vap *vap = ni->ni_vap; |
| struct ieee80211com *ic = vap->iv_ic; |
| uint32_t current_tx_phy_rate = 0; |
| int32_t current_rssi = ic->ic_rssi(ni); |
| int32_t current_snr = ic->ic_snr(ni); |
| |
| ic->ic_rxtx_phy_rate(ni, 0, NULL, NULL, ¤t_tx_phy_rate); |
| |
| if (current_rssi < -1 && current_rssi > -1200) |
| ni->ni_rssi = current_rssi; |
| else if (ni->ni_rssi > 0) |
| /* correct pseudo RSSIs that apparently still get into the node table */ |
| ni->ni_rssi = (ni->ni_rssi * 10) - 900; |
| |
| ni->ni_smthd_rssi = ic->ic_smoothed_rssi(ni); |
| if ((ni->ni_smthd_rssi > -1) || (ni->ni_smthd_rssi < -1200)) |
| ni->ni_smthd_rssi = ni->ni_rssi; |
| |
| if (current_tx_phy_rate > 0) |
| ni->ni_linkqual = (uint16_t) current_tx_phy_rate; |
| |
| if (current_snr < -1) |
| ni->ni_snr = current_snr; |
| } |
| EXPORT_SYMBOL(ieee80211_update_node_assoc_qual); |
| |
| void |
| get_node_tx_stats(void *s, struct ieee80211_node *ni) |
| { |
| struct seq_file *sq = (struct seq_file *)s; |
| struct ieee80211vap *vap = ni->ni_vap; |
| struct ieee80211com *ic = vap->iv_ic; |
| uint8_t *mac = ni->ni_macaddr; |
| int32_t current_max_queue = ic->ic_max_queue(ni); |
| uint32_t tx_failed = ic->ic_tx_failed(ni); |
| uint32_t achievable_tx_phy_rate; /* Kbps */ |
| uint8_t assoc_bw = 20; |
| uint8_t bw = 0; |
| uint8_t sgi = 0; |
| uint8_t max_mcs = 0; |
| uint8_t max_nss = 0; |
| uint8_t vht; |
| |
| if (!sq) |
| return; |
| |
| if (IS_IEEE80211_VHT_ENABLED(ic) && (ni->ni_flags & IEEE80211_NODE_VHT)) { |
| vht = 1; |
| get_node_vht_bw_and_sgi(ni, &bw, &assoc_bw, &sgi); |
| } else { |
| vht = 0; |
| get_node_ht_bw_and_sgi(ic, ni, &bw, &assoc_bw, &sgi); |
| } |
| |
| if (ieee80211_node_is_authorized(ni)) { |
| if (vht == 0) |
| get_node_ht_max_mcs(ic, ni, &max_mcs); |
| else |
| get_node_vht_max_nss_mcs(ni, &max_nss, &max_mcs); |
| |
| achievable_tx_phy_rate = ic->ic_mcs_to_phyrate(bw, sgi, max_mcs, max_nss, vht); |
| } else { |
| achievable_tx_phy_rate = 0; |
| } |
| |
| if (current_max_queue > -1) |
| ni->ni_max_queue = current_max_queue; |
| |
| seq_printf(sq, |
| "%pM %4u %8u %10u %12llu %8u %8u %10u %10u %10u %5u %7u\n", |
| mac, |
| IEEE80211_NODE_IDX_UNMAP(ni->ni_node_idx), |
| achievable_tx_phy_rate, |
| ni->ni_stats.ns_tx_data, |
| ni->ni_stats.ns_tx_bytes, |
| ni->ni_stats.ns_tx_errors, |
| ni->ni_stats.ns_tx_dropped, |
| ni->ni_stats.ns_tx_ucast, |
| ni->ni_stats.ns_tx_mcast, |
| ni->ni_stats.ns_tx_bcast, |
| ni->ni_max_queue, |
| tx_failed); |
| } |
| EXPORT_SYMBOL(get_node_tx_stats); |
| |
| void |
| get_node_rx_stats(void *s, struct ieee80211_node *ni) |
| { |
| struct seq_file *sq = (struct seq_file *)s; |
| struct ieee80211vap *vap = ni->ni_vap; |
| struct ieee80211com *ic = vap->iv_ic; |
| uint8_t *mac = ni->ni_macaddr; |
| uint32_t achievable_rx_phy_rate; /* Kbps */ |
| uint32_t current_rx_phy_rate = 0; |
| uint8_t assoc_bw = 20; |
| uint8_t bw = 0; |
| uint8_t sgi = 0; |
| uint8_t max_mcs = 0; |
| uint8_t max_nss = 0; |
| uint8_t vht; |
| |
| if (!sq) |
| return; |
| |
| ic->ic_rxtx_phy_rate(ni, 1, NULL, NULL, ¤t_rx_phy_rate); |
| |
| if (IS_IEEE80211_VHT_ENABLED(ic) && (ni->ni_flags & IEEE80211_NODE_VHT)) { |
| vht = 1; |
| get_node_vht_bw_and_sgi(ni, &bw, &assoc_bw, &sgi); |
| } else { |
| vht = 0; |
| get_node_ht_bw_and_sgi(ic, ni, &bw, &assoc_bw, &sgi); |
| } |
| |
| if (ieee80211_node_is_authorized(ni)) { |
| if (vht == 0) |
| get_node_ht_max_mcs(ic, ni, &max_mcs); |
| else |
| get_node_vht_max_nss_mcs(ni, &max_nss, &max_mcs); |
| |
| achievable_rx_phy_rate = ic->ic_mcs_to_phyrate(bw, sgi, max_mcs, max_nss, vht); |
| } else { |
| achievable_rx_phy_rate = 0; |
| } |
| |
| if (current_rx_phy_rate > 0) |
| ni->ni_rx_phy_rate = (uint16_t) current_rx_phy_rate; |
| |
| seq_printf(sq, |
| "%pM %4u %8d %8d %10u %12llu %8u %8u %10u %10u %10u\n", |
| mac, |
| IEEE80211_NODE_IDX_UNMAP(ni->ni_node_idx), |
| achievable_rx_phy_rate, |
| ni->ni_rx_phy_rate, |
| ni->ni_stats.ns_rx_data, |
| ni->ni_stats.ns_rx_bytes, |
| ni->ni_stats.ns_rx_errors, |
| ni->ni_stats.ns_rx_dropped, |
| ni->ni_stats.ns_rx_ucast, |
| ni->ni_stats.ns_rx_mcast, |
| ni->ni_stats.ns_rx_bcast); |
| } |
| EXPORT_SYMBOL(get_node_rx_stats); |
| |
| static void |
| get_node_max_rssi (void *arg, struct ieee80211_node *ni) { |
| |
| int32_t *max_rssi = (int32_t *)arg; |
| int32_t cur_rssi = (ni->ni_ic->ic_rssi(ni)/IEEE80211_RSSI_FACTOR); |
| |
| if (cur_rssi != 0 && |
| (ni->ni_associd != 0) && |
| (ni->ni_vendor == PEER_VENDOR_BRCM) && |
| (cur_rssi > *max_rssi)) { |
| *max_rssi = cur_rssi; |
| } |
| } |
| |
| static void |
| ieee80211_pco_timer_func ( unsigned long arg ) { |
| struct ieee80211vap *vap = (struct ieee80211vap *) arg; |
| struct ieee80211com *ic = vap->iv_ic; |
| uint16_t pwr_constraint = ic->ic_pco.pco_pwr_constraint; |
| uint8_t rssi_threshold = ic->ic_pco.pco_rssi_threshold; |
| uint16_t pwr_constraint_sec = (ic->ic_pco.pco_pwr_constraint > ic->ic_pco.pco_sec_offset) ? (ic->ic_pco.pco_pwr_constraint-ic->ic_pco.pco_sec_offset):0; |
| uint8_t rssi_threshold_sec = ic->ic_pco.pco_rssi_threshold+ic->ic_pco.pco_sec_offset; |
| uint8_t next_update = 1; |
| int32_t max_rssi = -100; |
| |
| // Apply WAR for single STA only |
| if( ic->ic_sta_assoc == 1 ) { |
| // check for brcm node with RSSI > rssi_threshold |
| ieee80211_iterate_nodes(&ic->ic_sta, get_node_max_rssi, &max_rssi, 1); |
| } |
| |
| /* |
| * Apply contraint WAR for higher channels only. |
| * The backoff dB value comparing with max reg tx power is a safety check |
| * to make sure the STAs local_tx_power won't be less than 0dBm |
| */ |
| if ((max_rssi > -rssi_threshold) && !(ic->ic_pco.pco_set) && |
| (pwr_constraint < ic->ic_bsschan->ic_maxregpower) && |
| (ic->ic_curchan->ic_ieee > QTN_5G_LAST_UNII2_OPERATING_CHAN)) { |
| /* Back down the STA power using Power constraint */ |
| ic->ic_pwr_constraint = pwr_constraint; |
| if (vap->iv_state == IEEE80211_S_RUN) |
| ic->ic_beacon_update(vap); |
| ic->ic_pco.pco_set = 1; |
| next_update = 60; |
| } else if ((max_rssi > -rssi_threshold_sec) && !(ic->ic_pco.pco_set) && |
| (pwr_constraint_sec < ic->ic_bsschan->ic_maxregpower) && |
| (ic->ic_curchan->ic_ieee > QTN_5G_LAST_UNII2_OPERATING_CHAN)) { |
| /* Back down the STA power using Secondary Power constraint */ |
| ic->ic_pwr_constraint = pwr_constraint_sec; |
| if (vap->iv_state == IEEE80211_S_RUN) |
| ic->ic_beacon_update(vap); |
| ic->ic_pco.pco_set = 1; |
| next_update = 60; |
| } else if ((ic->ic_pco.pco_set) && (max_rssi <= -rssi_threshold_sec)) { |
| ic->ic_pwr_constraint = ic->ic_pco.pco_pwr_constraint_save; |
| if (vap->iv_state == IEEE80211_S_RUN) |
| ic->ic_beacon_update(vap); |
| ic->ic_pco.pco_set = 0; |
| next_update = 1; |
| } |
| |
| mod_timer(&ic->ic_pco.pco_timer, |
| jiffies + (next_update * HZ)); |
| } |
| /* |
| * Implementation of ioctl command: IEEE80211_IOCTL_GET_ASSOC_TBL |
| * This command is to report all nodes in the table. User can do further filtering nodes. |
| * E.g. user only need MAC address on designated WiFi interface. |
| * |
| * This function works for both AP and STA. |
| */ |
| static void get_node_info_ioctl(void *s, struct ieee80211_node *ni) |
| { |
| struct iwreq *iwr = (struct iwreq *)s; |
| struct assoc_info_report __user *u_record; |
| struct assoc_info_report record; |
| uint16_t __user *u_cnt; |
| uint16_t cnt; |
| struct ieee80211vap *vap = ni->ni_vap; |
| struct ieee80211com *ic = vap->iv_ic; |
| uint8_t *mac = ni->ni_macaddr; |
| uint8_t assoc_bw = 20; |
| uint8_t bw = 0; |
| uint8_t sgi = 0; |
| uint8_t max_mcs = 0; |
| uint8_t max_nss = 0; |
| uint8_t tx_mcs = 0; |
| uint8_t rx_mcs = 0; |
| uint8_t vht; |
| uint32_t max_tx_rate; |
| uint32_t max_rx_rate; /* Unit: Kbps */ |
| uint32_t assoc_time = 0; /* Unit: second, 32bits should be up to 136 years */ |
| uint32_t cur_tx_rate = 0; |
| uint32_t cur_rx_rate = 0; |
| int32_t rssi = ic->ic_rssi(ni); |
| int32_t snr = ic->ic_snr(ni); |
| int32_t max_queue = ic->ic_max_queue(ni); |
| uint32_t tx_failed = ic->ic_tx_failed(ni); |
| int i; |
| |
| ni->ni_hw_noise = ic->ic_hw_noise(ni); |
| ic->ic_rxtx_phy_rate(ni, 0, NULL, &tx_mcs, &cur_tx_rate); |
| ic->ic_rxtx_phy_rate(ni, 1, NULL, &rx_mcs, &cur_rx_rate); |
| |
| if (IS_IEEE80211_VHT_ENABLED(ic) && (ni->ni_flags & IEEE80211_NODE_VHT)) { |
| vht = 1; |
| get_node_vht_bw_and_sgi(ni, &bw, &assoc_bw, &sgi); |
| } else { |
| vht = 0; |
| get_node_ht_bw_and_sgi(ic, ni, &bw, &assoc_bw, &sgi); |
| } |
| |
| if (ieee80211_node_is_authorized(ni)) { |
| if (vht == 0) |
| get_node_ht_max_mcs(ic, ni, &max_mcs); |
| else |
| get_node_vht_max_nss_mcs(ni, &max_nss, &max_mcs); |
| |
| max_tx_rate = ic->ic_mcs_to_phyrate(bw, sgi, max_mcs, max_nss, vht); |
| max_rx_rate = max_tx_rate; |
| } else { |
| max_tx_rate = 0; |
| max_rx_rate = 0; |
| } |
| |
| if (rssi < -1 && rssi > -1200) |
| ni->ni_rssi = rssi; |
| else if (ni->ni_rssi > 0) { |
| /* Correct pseudo RSSIs that apparently still get into the node table */ |
| ni->ni_rssi = (ni->ni_rssi * 10) - 900; |
| } |
| |
| ni->ni_smthd_rssi = ic->ic_smoothed_rssi(ni); |
| if ((ni->ni_smthd_rssi > -1) || (ni->ni_smthd_rssi < -1200)) { |
| ni->ni_smthd_rssi = ni->ni_rssi; |
| } |
| |
| if (cur_tx_rate > 0) |
| ni->ni_linkqual = (uint16_t) cur_tx_rate; |
| |
| if (cur_rx_rate > 0) |
| ni->ni_rx_phy_rate = (uint16_t) cur_rx_rate; |
| |
| if (snr < -1) |
| ni->ni_snr = snr; |
| |
| if (max_queue > -1) |
| ni->ni_max_queue = max_queue; |
| |
| if (vap->iv_opmode == IEEE80211_M_STA) { |
| assoc_time = (vap->iv_state == IEEE80211_S_RUN) ? |
| (u_int32_t)div_u64(get_jiffies_64() - ni->ni_start_time_assoc, HZ) : 0; |
| } else { |
| assoc_time = (vap->iv_bss == ni) ? |
| 0 : (u_int32_t)div_u64(get_jiffies_64() - ni->ni_start_time_assoc, HZ); |
| } |
| |
| if (ic->ic_get_shared_node_stats) |
| ic->ic_get_shared_node_stats(ni); |
| |
| memcpy(record.ai_mac_addr, mac, IEEE80211_ADDR_LEN); |
| |
| record.ai_assoc_id = IEEE80211_AID(ni->ni_associd); |
| record.ai_link_quality = (uint16_t) ni->ni_linkqual; |
| record.ai_rx_phy_rate = ni->ni_rx_phy_rate; |
| record.ai_tx_phy_rate = (uint16_t) ni->ni_linkqual; |
| record.ai_achievable_tx_phy_rate = max_tx_rate; |
| record.ai_achievable_rx_phy_rate = max_rx_rate; |
| record.ai_rssi = ni->ni_rssi; |
| record.ai_smthd_rssi = ni->ni_smthd_rssi; |
| record.ai_snr = ni->ni_snr; |
| record.ai_rx_packets = ni->ni_stats.ns_rx_data; |
| record.ai_rx_bytes = ni->ni_stats.ns_rx_bytes; |
| record.ai_tx_packets = ni->ni_stats.ns_tx_data; |
| record.ai_tx_bytes = ni->ni_stats.ns_tx_bytes; |
| record.ai_rx_errors = ni->ni_stats.ns_rx_errors; |
| record.ai_rx_dropped = ni->ni_stats.ns_rx_dropped; |
| record.ai_tx_ucast = ni->ni_stats.ns_tx_ucast; |
| record.ai_rx_ucast = ni->ni_stats.ns_rx_ucast; |
| record.ai_tx_mcast = ni->ni_stats.ns_tx_mcast; |
| record.ai_rx_mcast = ni->ni_stats.ns_rx_mcast; |
| record.ai_tx_bcast = ni->ni_stats.ns_tx_bcast; |
| record.ai_rx_bcast = ni->ni_stats.ns_rx_bcast; |
| record.ai_tx_errors = ni->ni_stats.ns_tx_errors; |
| record.ai_tx_dropped = ni->ni_stats.ns_tx_dropped; |
| for (i = 0; i < WME_AC_NUM; i++) { |
| record.ai_tx_wifi_drop[i] = ni->ni_stats.ns_tx_wifi_drop[i]; |
| record.ai_tx_wifi_sent[i] = ni->ni_shared_stats->tx[STATS_SU].tx_sent_data_msdu[i]; |
| } |
| record.ai_rx_fragment_pkts = ni->ni_stats.ns_rx_fragment_pkts; |
| record.ai_rx_vlan_pkts = ni->ni_stats.ns_rx_vlan_pkts; |
| record.ai_max_queued = ni->ni_max_queue; |
| record.ai_tx_failed = tx_failed; |
| record.ai_bw = assoc_bw; |
| record.ai_tx_mcs = tx_mcs; |
| record.ai_rx_mcs = rx_mcs; |
| record.ai_time_associated = assoc_time; |
| record.ai_auth = ieee80211_node_is_authorized(ni); |
| record.ai_ip_addr = ni->ni_ip_addr; |
| record.ai_hw_noise = ni->ni_hw_noise; |
| record.ai_is_qtn_node = (ni->ni_qtn_assoc_ie) ? 1 : 0; |
| |
| strncpy(record.ai_ifname, vap->iv_dev->name, strlen(vap->iv_dev->name) + 1); |
| |
| /* Update record to user space */ |
| u_cnt = &(( (struct assoc_info_table *)iwr->u.data.pointer )->cnt); |
| if (copy_from_user(&cnt, u_cnt, sizeof(cnt))) |
| return; |
| |
| u_record = ( (struct assoc_info_table *)iwr->u.data.pointer )->array + cnt; |
| |
| if (cnt++ > QTN_ASSOC_LIMIT - 1) |
| return; |
| |
| if (copy_to_user(u_record, &record, sizeof(record))) |
| return; |
| |
| copy_to_user( u_cnt, &cnt, sizeof(cnt) ); |
| } |
| |
| static int |
| ieee80211_ioctl_getassoctbl(struct net_device *dev, struct iwreq *iwr) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| uint16_t unit_size = ( (struct assoc_info_table *)iwr->u.data.pointer )->unit_size; |
| |
| if (unit_size != sizeof( struct assoc_info_report )) { |
| printk(KERN_ERR "The size of structure assoc_info_report doesn't match\n"); |
| return -EPERM; |
| } |
| |
| ic->ic_iterate_nodes(&ic->ic_sta, get_node_info_ioctl, iwr, 1); |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_subioctl_rst_queue(struct net_device *dev, char __user* mac) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_node *ni = NULL; |
| uint8_t mac_addr[IEEE80211_ADDR_LEN]; |
| |
| if (copy_from_user(mac_addr, mac, IEEE80211_ADDR_LEN)) |
| return -EFAULT; |
| |
| ni = ieee80211_find_node(&ic->ic_sta, mac_addr); |
| if (ni == NULL) |
| return -ENOENT; |
| |
| ic->ic_queue_reset(ni); |
| |
| ieee80211_free_node(ni); |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_subioctl_radar_status(struct net_device *dev, struct ieee80211req_radar_status __user* status) |
| { |
| int retval = 0; |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211req_radar_status rdstatus; |
| int chan_idx=0; |
| |
| if (copy_from_user(&rdstatus, status, sizeof(rdstatus)) != 0) |
| return -EFAULT; |
| |
| for (chan_idx = 0; chan_idx < ic->ic_nchans; chan_idx++) { |
| if (ic->ic_channels[chan_idx].ic_ieee == rdstatus.channel) |
| break; |
| } |
| |
| if (chan_idx >= ic->ic_nchans) { |
| retval = -EINVAL; |
| } else { |
| if (!(ic->ic_channels[chan_idx].ic_flags & IEEE80211_CHAN_DFS)) |
| retval = -EINVAL; |
| } |
| |
| if (retval >= 0) { |
| rdstatus.flags = ((ic->ic_channels[chan_idx].ic_flags) & (IEEE80211_CHAN_RADAR)) ? 1 : 0; |
| rdstatus.ic_radardetected = ic->ic_channels[chan_idx].ic_radardetected; |
| if (copy_to_user(status, &rdstatus, sizeof(rdstatus)) != 0) |
| retval = -EFAULT; |
| } |
| |
| return retval; |
| } |
| |
| static int |
| ieee80211_subioctl_get_phy_stats(struct net_device *dev, struct ieee80211_phy_stats __user* ps) |
| { |
| int retval = 0; |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_phy_stats phy_stats; |
| |
| if (ic->ic_get_phy_stats) { |
| retval = ic->ic_get_phy_stats(dev, ic, &phy_stats, 1); |
| } else |
| return -EINVAL; |
| |
| if (retval >= 0) { |
| if (copy_to_user(ps, &phy_stats, sizeof(struct ieee80211_phy_stats))) |
| retval = -EFAULT; |
| } |
| |
| return retval; |
| } |
| |
| static int |
| ieee80211_subioctl_get_dscp2ac_map(struct net_device *dev, uint8_t __user* ps) |
| { |
| int retval = 0; |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| uint8_t dscp2ac_map[IP_DSCP_NUM] = {0}; |
| uint8_t vap_idx = ic->ic_get_vap_idx(vap); |
| |
| if (!ic->ic_get_dscp2ac_map) |
| return -EINVAL; |
| |
| ic->ic_get_dscp2ac_map(vap_idx, dscp2ac_map); |
| |
| if (copy_to_user(ps, dscp2ac_map, IP_DSCP_NUM) != 0) |
| retval = -EIO; |
| |
| return retval; |
| } |
| |
| static int |
| ieee80211_subioctl_set_dscp2ac_map(struct net_device *dev, struct ieeee80211_dscp2ac __user* ps) |
| { |
| int retval = 0; |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieeee80211_dscp2ac dscp2ac; |
| uint8_t vap_idx = ic->ic_get_vap_idx(vap); |
| |
| if (!ps) { |
| return -EFAULT; |
| } |
| |
| if (copy_from_user(&dscp2ac, ps, sizeof(struct ieeee80211_dscp2ac))) { |
| return -EIO; |
| } |
| |
| if (dscp2ac.list_len > IP_DSCP_NUM) { |
| printk(KERN_WARNING "%s: DSCP list size %u larger then max allowed %d\n", |
| dev->name, dscp2ac.list_len, IP_DSCP_NUM); |
| return -EINVAL; |
| } |
| |
| if (ic->ic_set_dscp2ac_map) { |
| ic->ic_set_dscp2ac_map(vap_idx, dscp2ac.dscp, dscp2ac.list_len, dscp2ac.ac); |
| } else |
| return -EINVAL; |
| |
| return retval; |
| } |
| |
| |
| static int |
| ieee80211_subioctl_brcm(struct net_device *dev, struct ieee80211req_brcm __user* ps) |
| { |
| int retval = 0; |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211req_brcm req; |
| struct ieee80211_node *ni; |
| |
| if (copy_from_user(&req, ps, sizeof(struct ieee80211req_brcm))) { |
| return -EINVAL; |
| }; |
| |
| switch (req.ib_op) { |
| case IEEE80211REQ_BRCM_INFO: |
| if (vap->iv_opmode != IEEE80211_M_HOSTAP) |
| return -EINVAL; |
| ni = ieee80211_find_node(&ic->ic_sta, req.ib_macaddr); |
| if (ni == NULL) |
| return -ENOENT; |
| ieee80211_scs_brcm_info_report(ic, ni, req.ib_rssi, req.ib_rxglitch); |
| ieee80211_free_node(ni); |
| break; |
| case IEEE80211REQ_BRCM_PKT: |
| if (vap->iv_opmode != IEEE80211_M_HOSTAP) |
| return -EINVAL; |
| ieee80211_send_usr_l2_pkt(vap, req.ib_pkt, req.ib_pkt_len); |
| SCSDBG(SCSLOG_INFO, "send brcm info pkt in vap %u\n", vap->iv_unit); |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| return retval; |
| } |
| |
| static int |
| ieee80211_subioctl_disconn_info(struct net_device *dev, struct ieee80211req_disconn_info __user* disconn_info) |
| { |
| int retval = 0; |
| struct ieee80211req_disconn_info info; |
| struct ieee80211vap *vap = netdev_priv(dev); |
| |
| if (copy_from_user(&info, disconn_info, sizeof(info)) != 0) |
| return -EFAULT; |
| |
| if (info.resetflag) { |
| vap->iv_disconn_cnt = 0; |
| vap->iv_disconn_seq = 0; |
| } else { |
| vap->iv_disconn_seq++; |
| if (vap->iv_opmode == IEEE80211_M_STA) { |
| info.asso_sta_count = (vap->iv_state == IEEE80211_S_RUN) ? 1 : 0; |
| } else if (vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| info.asso_sta_count = vap->iv_sta_assoc; |
| } |
| |
| info.disconn_count = vap->iv_disconn_cnt; |
| info.sequence = vap->iv_disconn_seq; |
| info.up_time = (jiffies - INITIAL_JIFFIES) / HZ; |
| |
| if (copy_to_user(disconn_info, &info, sizeof(info)) != 0) |
| retval = -EFAULT; |
| } |
| |
| return retval; |
| } |
| |
| static int |
| ieee80211_subioctl_tdls_operation(struct net_device *dev, char __user* data) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_node *peer_ni = NULL; |
| struct ieee80211_tdls_oper_data oper_data; |
| uint64_t tbtt; |
| uint64_t cur_tsf; |
| unsigned long duration; |
| unsigned long cur_jiffies; |
| |
| if (copy_from_user(&oper_data.dest_mac, data, IEEE80211_ADDR_LEN)) |
| return -EFAULT; |
| if (copy_from_user(&oper_data.oper, data + IEEE80211_ADDR_LEN, sizeof(oper_data.oper))) |
| return -EFAULT; |
| |
| if((oper_data.oper != IEEE80211_TDLS_ENABLE) && (oper_data.oper != IEEE80211_TDLS_DISABLE)) { |
| peer_ni = ieee80211_find_node(&ic->ic_sta, oper_data.dest_mac); |
| if (peer_ni == NULL || IEEE80211_NODE_IS_NONE_TDLS(peer_ni)) { |
| if (peer_ni) |
| ieee80211_free_node(peer_ni); |
| return -ENOENT; |
| } |
| } |
| |
| switch (oper_data.oper) { |
| case IEEE80211_TDLS_SETUP: |
| if (IEEE80211_NODE_IS_TDLS_INACTIVE(peer_ni) || |
| IEEE80211_NODE_IS_TDLS_IDLE(peer_ni)) { |
| IEEE80211_TDLS_DPRINTF(vap, IEEE80211_MSG_TDLS, IEEE80211_TDLS_MSG_WARN, |
| "TDLS %s: setting up data link with peer %pM\n", |
| __func__, peer_ni->ni_macaddr); |
| } |
| break; |
| case IEEE80211_TDLS_TEARDOWN: |
| IEEE80211_TDLS_DPRINTF(vap, IEEE80211_MSG_TDLS, IEEE80211_TDLS_MSG_WARN, |
| "TDLS %s: tearing down data link with peer %pM\n", |
| __func__, peer_ni->ni_macaddr); |
| /* |
| * Just remove the fdb entry form bridge module, |
| * delay the node free in later node_expire timer callback. |
| */ |
| ieee80211_tdls_disable_peer_link(peer_ni); |
| |
| if (vap->iv_flags_ext & IEEE80211_FEXT_TDLS_DISABLED) |
| ieee80211_tdls_node_leave(vap, peer_ni); |
| break; |
| case IEEE80211_TDLS_ENABLE_LINK: |
| IEEE80211_TDLS_DPRINTF(vap, IEEE80211_MSG_TDLS, IEEE80211_TDLS_MSG_WARN, |
| "TDLS %s: data link established successfully with peer %pM\n", |
| __func__, peer_ni->ni_macaddr); |
| |
| ieee80211_tdls_enable_peer_link(vap, peer_ni); |
| break; |
| case IEEE80211_TDLS_DISABLE_LINK: |
| IEEE80211_TDLS_DPRINTF(vap, IEEE80211_MSG_TDLS, IEEE80211_TDLS_MSG_WARN, |
| "TDLS %s: data link removed with peer %pM\n", |
| __func__, peer_ni->ni_macaddr); |
| |
| /* |
| * Just remove the fdb entry form bridge module, |
| * delay the node free in later node_expire timer callback. |
| */ |
| ieee80211_tdls_disable_peer_link(peer_ni); |
| |
| if (vap->iv_flags_ext & IEEE80211_FEXT_TDLS_DISABLED) |
| ieee80211_tdls_node_leave(vap, peer_ni); |
| break; |
| case IEEE80211_TDLS_ENABLE: |
| if (!ieee80211_swfeat_is_supported(SWFEAT_ID_TDLS, 1)) |
| return -EPERM; |
| if (vap->iv_flags_ext & IEEE80211_FEXT_TDLS_PROHIB) { |
| IEEE80211_TDLS_DPRINTF(vap, IEEE80211_MSG_TDLS, IEEE80211_TDLS_MSG_WARN, |
| "TDLS %s: TDLS function is enabled\n", __func__); |
| |
| /* attention: must set the flag firstly */ |
| vap->iv_flags_ext &= ~IEEE80211_FEXT_TDLS_PROHIB; |
| if ((vap->iv_flags_ext & IEEE80211_FEXT_TDLS_DISABLED) == 0) { |
| ieee80211_tdls_start_disc_timer(vap); |
| ieee80211_tdls_start_node_expire_timer(vap); |
| } |
| } |
| break; |
| case IEEE80211_TDLS_DISABLE: |
| if ((vap->iv_flags_ext & IEEE80211_FEXT_TDLS_PROHIB) == 0) { |
| if ((vap->iv_flags_ext & IEEE80211_FEXT_TDLS_DISABLED) == 0) { |
| IEEE80211_TDLS_DPRINTF(vap, IEEE80211_MSG_TDLS, IEEE80211_TDLS_MSG_WARN, |
| "TDLS %s: TDLS function is disabled\n", __func__); |
| |
| /* teardown the link and clear timer */ |
| ieee80211_tdls_teardown_all_link(vap); |
| ieee80211_tdls_clear_disc_timer(vap); |
| ieee80211_tdls_clear_node_expire_timer(vap); |
| ieee80211_tdls_free_all_inactive_peers(vap); |
| } |
| vap->iv_flags_ext |= IEEE80211_FEXT_TDLS_PROHIB; |
| } |
| break; |
| case IEEE80211_TDLS_SWITCH_CHAN: |
| ic->ic_get_tsf(&cur_tsf); |
| cur_jiffies = jiffies; |
| if (NULL == vap->iv_bss) |
| return -EINVAL; |
| tbtt = vap->iv_bss->ni_shared_stats->beacon_tbtt; |
| |
| if (tbtt > cur_tsf) { |
| duration = IEEE80211_USEC_TO_MS((uint32_t)(tbtt - cur_tsf)); |
| } else { |
| duration = 0; |
| IEEE80211_TDLS_DPRINTF(vap, IEEE80211_MSG_TDLS, IEEE80211_TDLS_MSG_WARN, |
| "TDLS %s: Get wrong TBTT\n", __func__); |
| } |
| |
| IEEE80211_TDLS_DPRINTF(vap, IEEE80211_MSG_TDLS, IEEE80211_TDLS_MSG_DBG, |
| "TDLS %s: cur_tsf = %016llx, tbtt = %016llx, duration = %08lx\n", |
| __func__, cur_tsf, tbtt, duration); |
| |
| while (time_before(jiffies, cur_jiffies + duration * HZ / 1000)) |
| msleep(5); |
| ieee80211_tdls_start_channel_switch(vap, peer_ni); |
| break; |
| default: |
| break; |
| } |
| |
| if (peer_ni) { |
| IEEE80211_TDLS_DPRINTF(vap, IEEE80211_MSG_TDLS, IEEE80211_TDLS_MSG_DBG, |
| "TDLS %s: TDLS operation: %d, TDLS status: %d\n", |
| __func__, oper_data.oper, peer_ni->tdls_status); |
| ieee80211_free_node(peer_ni); |
| } |
| |
| return 0; |
| } |
| |
| /* app_buf = [struct app_action_frm_buf] + [Action Frame Payload] |
| * Action Frame Payload = IEEE80211_ACTION_CAT_* (u8) + action (u8) + dialog token (u8) + |
| * status code (u8) + Info |
| * Action Frame Payload is constructed in hostapd |
| */ |
| static int |
| ieee80211_subioctl_send_action_frame(struct net_device *dev, u8 *app_buf, |
| u32 app_buf_len) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct app_action_frame_buf *app_action_frm_buf; |
| struct ieee80211_node *ni = NULL; |
| struct ieee80211_action_data action_data; |
| int retval = 0; |
| |
| /* kzalloc() alloctes memory to copy data from user. */ |
| app_action_frm_buf = (struct app_action_frame_buf *) kzalloc(app_buf_len, GFP_KERNEL); |
| if (!app_action_frm_buf) |
| return -ENOMEM; |
| |
| if (copy_from_user(app_action_frm_buf, app_buf, app_buf_len) != 0) { |
| retval = -EFAULT; |
| goto buf_free; |
| } |
| |
| memset(&action_data, 0, sizeof(action_data)); |
| action_data.cat = app_action_frm_buf->cat; |
| action_data.action = app_action_frm_buf->action; |
| |
| /* Pointer to action frm payload passed to next function */ |
| action_data.params = &app_action_frm_buf->frm_payload; |
| |
| if (vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| ni = ieee80211_find_node(&ic->ic_sta, app_action_frm_buf->dst_mac_addr); |
| |
| /* Public action frames are handled before association */ |
| if (ni == NULL && action_data.cat == IEEE80211_ACTION_CAT_PUBLIC) { |
| ni = ieee80211_tmp_node(vap, app_action_frm_buf->dst_mac_addr); |
| } |
| |
| if (ni == NULL) { |
| retval = -ENOENT; |
| goto buf_free; |
| } |
| |
| if ((action_data.cat == IEEE80211_ACTION_CAT_PUBLIC) |
| || (action_data.cat == IEEE80211_ACTION_CAT_FBSS) |
| || (ni->ni_associd && ieee80211_node_is_authorized(ni))) |
| IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_ACTION, (int)&action_data); |
| else |
| retval = -ENOENT; |
| |
| ieee80211_free_node(ni); |
| } else { |
| /* Currently sending action frames for AP mode only */ |
| retval = -EINVAL; |
| } |
| |
| buf_free: |
| kfree(app_action_frm_buf); |
| |
| return retval; |
| } |
| |
| /* |
| * Function to get the driver capabilities. Currently extended |
| * capabilities IE is sent here |
| **/ |
| static int |
| ieee80211_subioctl_get_driver_capa(struct net_device *dev, |
| uint8_t __user *app_buf, |
| uint32_t app_buf_len) |
| { |
| #define DRV_EXT_CAPABILITY_LEN 8 |
| u_int8_t ext_capability[DRV_EXT_CAPABILITY_LEN] = {0}; |
| u_int8_t *buf = NULL; |
| u_int8_t *pos; |
| u_int32_t len = 0; |
| int retval = 0; |
| |
| MALLOC(buf, u_int8_t *, app_buf_len, M_DEVBUF, M_WAITOK); |
| if(!buf) |
| return -ENOMEM; |
| |
| /*Keep buffer in the form of Type(4 bytes):Length:Value format */ |
| |
| pos = buf; |
| pos += sizeof(u_int32_t); /* total data len in buf */ |
| |
| *pos++ = IEEE80211_ELEMID_EXTCAP; |
| *pos++ = DRV_EXT_CAPABILITY_LEN; |
| |
| ext_capability[0] = IEEE80211_EXTCAP_20_40_COEXISTENCE; |
| /* max msdu in amsdu 0 = unlimited */ |
| ext_capability[7] = IEEE80211_EXTCAP_OPMODE_NOTIFICATION; |
| |
| /* extended capability supported by driver */ |
| memcpy(pos, ext_capability, DRV_EXT_CAPABILITY_LEN); |
| pos += DRV_EXT_CAPABILITY_LEN; |
| |
| /* extended capability mask */ |
| memcpy(pos, ext_capability, DRV_EXT_CAPABILITY_LEN); |
| pos += DRV_EXT_CAPABILITY_LEN; |
| |
| /* TODO Can send more data to application from here */ |
| |
| len = (u_int32_t)(pos - buf); |
| *(u_int32_t *)buf = len; |
| |
| if (len > app_buf_len) { |
| printk (KERN_WARNING "length is more than app_buf_len\n"); |
| retval = -1; |
| goto done; |
| } |
| |
| if (copy_to_user(app_buf, buf, len)) { |
| retval = -EIO; |
| } |
| |
| done: |
| FREE(buf, M_DEVBUF); |
| |
| return retval; |
| |
| #undef DRV_EXT_CAPABILITY_LEN |
| } |
| |
| struct get_links_max_quality_request { |
| uint32_t max_quality; |
| uint8_t mac_addr[IEEE80211_ADDR_LEN]; |
| }; |
| |
| static void get_link_quality_max_callback(void *s, struct ieee80211_node *ni) |
| { |
| struct get_links_max_quality_request* cmd |
| = (struct get_links_max_quality_request*)s; |
| |
| if (ieee80211_node_is_authorized(ni) != 0 && |
| IEEE80211_AID(ni->ni_associd) != 0 && |
| !IEEE80211_ADDR_EQ(ni->ni_macaddr, cmd->mac_addr)) { |
| cmd->max_quality = MAX(cmd->max_quality, (uint32_t)ni->ni_linkqual); |
| } |
| } |
| |
| static int |
| ieee80211_subioctl_get_link_quality_max(struct net_device *dev, uint8_t __user *app_buf, |
| uint32_t app_buf_len) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct get_links_max_quality_request request; |
| |
| if (app_buf_len != sizeof(request.max_quality)) |
| return -EINVAL; |
| |
| IEEE80211_ADDR_COPY(request.mac_addr, dev->dev_addr); |
| request.max_quality = 0; |
| |
| ic->ic_iterate_dev_nodes(dev, &ic->ic_sta, get_link_quality_max_callback, &request, 1); |
| |
| if (copy_to_user(app_buf, &request.max_quality, sizeof(request.max_quality))) |
| return -EINVAL; |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_subioctl_set_ap_info(struct net_device *dev, uint8_t __user* app_buf, |
| uint32_t app_buf_len) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct app_ie *ie = NULL; |
| int retval = 0; |
| |
| if (app_buf_len < 4) |
| return -EINVAL; |
| |
| MALLOC(ie, struct app_ie *, app_buf_len, M_DEVBUF, M_WAITOK); |
| if (!ie) |
| return -ENOMEM; |
| |
| if (copy_from_user(ie, app_buf, app_buf_len) != 0) { |
| retval = -EFAULT; |
| goto buf_free; |
| } |
| |
| /*TODO: This function can be used to handle other Elements from hostapd */ |
| switch (ie->id) { |
| case IEEE80211_ELEMID_INTERWORKING: |
| memset(&vap->interw_info, 0, sizeof(struct interworking_info)); |
| vap->interworking = ie->u.interw.interworking; |
| if (vap->interworking) { |
| vap->interw_info.an_type = ie->u.interw.an_type; |
| if (ie->len > 2) { |
| IEEE80211_ADDR_COPY(vap->interw_info.hessid, |
| ie->u.interw.hessid); |
| } |
| } |
| break; |
| default: |
| retval = -EOPNOTSUPP; |
| break; |
| } |
| |
| buf_free: |
| FREE(ie, M_DEVBUF); |
| return retval; |
| } |
| |
| static int |
| ieee80211_get_supp_chans(struct ieee80211vap *vap, struct iwreq *iwr) |
| { |
| int8_t mac_addr[IEEE80211_ADDR_LEN]; |
| struct ieee80211_node *ni; |
| const char *errmsg = " [buffer overflow]"; |
| void __user* pointer; |
| char *buffer; |
| char *tmp_buf; |
| int buf_len; |
| int chan; |
| int len; |
| int retval; |
| |
| pointer = iwr->u.data.pointer; |
| if (copy_from_user(mac_addr, pointer, IEEE80211_ADDR_LEN) != 0) |
| return -EINVAL; |
| |
| ni = ieee80211_find_node(&vap->iv_ic->ic_sta, (const uint8_t*)mac_addr); |
| if (!ni) |
| return -EINVAL; |
| |
| buf_len = iwr->u.data.length; |
| buffer = kmalloc(buf_len + 1, GFP_KERNEL); |
| if (!buffer) { |
| ieee80211_free_node(ni); |
| return -EINVAL; |
| } |
| memset(buffer, 0, buf_len); |
| |
| len = 0; |
| for (chan = 0; chan < IEEE80211_CHAN_MAX; chan++) { |
| if (isset(ni->ni_supp_chans, chan)) { |
| tmp_buf = buffer + len; |
| len += snprintf(tmp_buf, buf_len - len, "%d,", chan); |
| } |
| } |
| |
| ieee80211_free_node(ni); |
| if (len > buf_len) { |
| len = strlen(errmsg); |
| tmp_buf = buffer + buf_len - len; |
| while (len < buf_len) { |
| if (tmp_buf[0] == ',') |
| break; |
| tmp_buf--; |
| len++; |
| } |
| |
| memset(tmp_buf, 0, len); |
| strcpy(tmp_buf, errmsg); |
| len = strlen(buffer); |
| } else if (len > 0) { |
| buffer[len - 1] = '\0'; |
| } else { |
| len = snprintf(buffer, buf_len, "Not available"); |
| } |
| |
| retval = copy_to_user(pointer, buffer, len); |
| kfree(buffer); |
| |
| if (retval) |
| return -EINVAL; |
| |
| return 0; |
| } |
| |
| #if defined(CONFIG_QTN_BSA_SUPPORT) |
| void ieee80211_bsa_macfilter_detach(struct ieee80211vap *vap) |
| { |
| struct bsa_deny_sta *sta; |
| struct bsa_deny_sta *tmp; |
| if (vap->deny_sta_list_inited) { |
| LIST_FOREACH_SAFE(sta, &vap->deny_sta_list, list, tmp) { |
| if (sta != NULL) { |
| LIST_REMOVE(sta, list); |
| FREE(sta, M_DEVBUF); |
| } |
| } |
| } |
| } |
| |
| static int ieee80211_bsa_macfilter_attach(struct ieee80211vap *vap) |
| { |
| if (vap->deny_sta_list_inited == 0) { |
| LIST_INIT(&vap->deny_sta_list); |
| vap->deny_sta_list_inited = 1; |
| } else { |
| ieee80211_bsa_macfilter_detach(vap); |
| } |
| return 0; |
| } |
| |
| |
| static int |
| ieee80211_subioctl_set_bsa_module(struct net_device *dev, uint8_t __user *buf) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| char bsa_status[3] = {0}; |
| int bsa_enable_flag = 0; |
| int retval = 0; |
| |
| if (!buf) |
| return -EFAULT; |
| |
| if (copy_from_user(bsa_status, buf, sizeof(bsa_status)) != 0) |
| return -EFAULT; |
| |
| sscanf(bsa_status, "%d", &bsa_enable_flag); |
| vap->bsa_status = bsa_enable_flag; |
| |
| if (vap->bsa_status == BSA_STATUS_ACTIVE) |
| ieee80211_bsa_macfilter_attach(vap); |
| else if (vap->bsa_status != BSA_STATUS_ACTIVE) |
| ieee80211_bsa_macfilter_detach(vap); |
| |
| return retval; |
| } |
| |
| static int |
| ieee80211_subioctl_update_macfilter_table(struct net_device *dev, uint8_t __user *buf) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211_bsa_mac_filter bsa_mf_filter; |
| int retval = -1; |
| |
| if (!vap->bsa_status) |
| return -EINVAL; |
| |
| if (copy_from_user(&bsa_mf_filter, buf, sizeof(struct ieee80211_bsa_mac_filter)) != 0) |
| return -EFAULT; |
| |
| if (bsa_mf_filter.allow_mac == BSA_MACFILTER_ALLOW) |
| /* remove from the deny_list */ |
| retval = ieee80211_bsa_macfilter_remove(vap,bsa_mf_filter.sta_mac); |
| else if(bsa_mf_filter.allow_mac == BSA_MACFILTER_DENY) |
| /* add to the deny list */ |
| retval = ieee80211_bsa_macfilter_add(vap, bsa_mf_filter.sta_mac); |
| |
| return retval; |
| } |
| |
| static int |
| ieee80211_subioctl_send_btm_req_frm(struct net_device *dev, struct |
| ieee80211_bsa_btm_req_frm __user *buf, uint16_t len) |
| { |
| struct ieee80211vap *vap = NULL; |
| struct ieee80211_bsa_btm_req_frm bsa_btm_req_frm; |
| uint8_t sta_mac[IEEE80211_ADDR_LEN]; |
| struct ieee80211_node *ni; |
| uint8_t mode; |
| uint16_t disassoc_timer; |
| uint8_t valid_int; |
| uint8_t bss_term_dur = 0; |
| struct ieee80211_ie_neighbor_report *neigh = NULL; |
| int retval = 0; |
| uint8_t *data = NULL; |
| |
| if (!dev || !buf || (len < sizeof (bsa_btm_req_frm))) |
| return -EINVAL; |
| |
| vap = netdev_priv(dev); |
| if (!vap) |
| return -EINVAL; |
| |
| if (copy_from_user(&bsa_btm_req_frm, buf, sizeof(bsa_btm_req_frm)) != 0) |
| return -EFAULT; |
| |
| memcpy(sta_mac, bsa_btm_req_frm.sta_mac, IEEE80211_ADDR_LEN); |
| ni = ieee80211_find_node(&vap->iv_ic->ic_sta, (const uint8_t*)sta_mac); |
| if (!ni) |
| return -EINVAL; |
| |
| disassoc_timer = get_unaligned(&bsa_btm_req_frm.dis_assoc_timer); |
| mode = bsa_btm_req_frm.req_mode; |
| valid_int = bsa_btm_req_frm.val_intvl; |
| |
| MALLOC(neigh, struct ieee80211_ie_neighbor_report *, \ |
| (sizeof (struct ieee80211_ie_neighbor_report) + BSA_BTM_CAND_PREF),\ |
| M_DEVBUF, M_WAITOK | M_ZERO); |
| |
| if (!neigh) { |
| ieee80211_free_node(ni); |
| return -ENOMEM; |
| } |
| |
| neigh->id = IEEE80211_ELEMID_NEIGHBOR_REP; |
| neigh->len = sizeof (struct ieee80211_ie_neighbor_report) + BSA_BTM_CAND_PREF - 2; |
| |
| memcpy(neigh->bssid, bsa_btm_req_frm.bssid, IEEE80211_ADDR_LEN); |
| put_unaligned(get_unaligned(&bsa_btm_req_frm.bssid_info), &neigh->bssid_info); |
| neigh->operating_class = bsa_btm_req_frm.opclass; |
| neigh->channel = bsa_btm_req_frm.channel; |
| neigh->phy_type = bsa_btm_req_frm.phytype; |
| |
| data = (uint8_t *)&neigh->data; |
| |
| /* BSS Transition Candidate Preference sub element */ |
| *data++ = BSA_BTM_CAND_PREF_ID; |
| *data++ = BSA_BTM_CAND_PREF_LEN; |
| *data = BSA_BTM_CAND_PREF_VAL; |
| retval = ieee80211_send_wnm_bss_tm_unsolicited_req(ni, mode, disassoc_timer, valid_int, |
| &bss_term_dur, NULL, (uint8_t *)neigh, neigh->len + 2, 0); |
| |
| ieee80211_free_node(ni); |
| FREE(neigh, M_DEVBUF); |
| return retval; |
| } |
| |
| struct ieee80211_bsa_sta_statinfo { |
| struct ieee80211vap *vap; |
| void *bsa_stats; |
| }; |
| |
| static void get_node_bsa_sta_stats(void *s, struct ieee80211_node *ni) |
| { |
| struct ieee80211_bsa_sta_statinfo *bsa_statinfo = (struct ieee80211_bsa_sta_statinfo *)s; |
| struct ieee80211_bsa_sta_info k_stainfo; |
| struct ieee80211_bsa_sta_info __user *u_pstainfo; |
| struct ieee80211_bsa_sta_stats *u_stastats |
| = (struct ieee80211_bsa_sta_stats *)bsa_statinfo->bsa_stats; |
| uint16_t cnt; |
| uint16_t __user *u_pcnt = &u_stastats->num_sta; |
| uint32_t tx_phyrate; |
| uint32_t rx_phyrate; |
| int32_t rssi; |
| struct ieee80211vap *vap; |
| struct ieee80211com *ic; |
| |
| if (copy_from_user(&cnt, u_pcnt, sizeof(cnt))) |
| return; |
| |
| if (cnt>=IEEE80211_AID_DEF) |
| return; |
| if (ni->ni_vap != bsa_statinfo->vap) |
| return; |
| if ((ni->ni_vap) && (ni->ni_vap->iv_bss == ni)) |
| return; |
| vap = ni->ni_vap; |
| ic = vap->iv_ic; |
| |
| u_pstainfo = &u_stastats->ieee80211_bsa_sta_info_var[cnt]; |
| ic->ic_rxtx_phy_rate(ni, 0, NULL, NULL, &tx_phyrate); |
| ic->ic_rxtx_phy_rate(ni, 1, NULL, NULL, &rx_phyrate); |
| rssi = ic->ic_rssi(ni); |
| memcpy(k_stainfo.sta_mac, ni->ni_macaddr, IEEE80211_ADDR_LEN); |
| put_unaligned(rx_phyrate, &k_stainfo.rx_phy_rate); |
| put_unaligned(0, &k_stainfo.ts_last_rx_pkt); |
| put_unaligned(tx_phyrate, &k_stainfo.tx_phy_rate); |
| put_unaligned(0, &k_stainfo.ts_last_tx_pkt); |
| put_unaligned((rssi - 5) / 10, &k_stainfo.rssi_dbm); |
| |
| if (copy_to_user(u_pstainfo, &k_stainfo, sizeof(struct ieee80211_bsa_sta_info))) |
| return; |
| |
| cnt++; |
| if (copy_to_user(u_pcnt, &cnt, sizeof(cnt))) |
| return; |
| } |
| |
| static void get_node_bsa_associated_sta_stats(void *s, struct ieee80211_node *ni) |
| { |
| struct ieee80211vap *vap = (struct ieee80211vap *)s; |
| |
| if (ni->ni_vap != vap) |
| return; |
| if ((ni->ni_vap) && (ni->ni_vap->iv_bss == ni)) |
| return; |
| if (ni->ni_associd == 0) |
| return; |
| |
| ieee80211_bsa_connect_complete_event_send(vap, ni); |
| } |
| |
| static int |
| ieee80211_subioctl_get_bsa_sta_stats(struct net_device *dev, struct |
| ieee80211_bsa_sta_stats __user *buf, uint16_t len) |
| { |
| int retval = 0; |
| struct ieee80211vap *vap; |
| struct ieee80211com *ic; |
| struct ieee80211_bsa_sta_statinfo bsa_statinfo; |
| |
| if (!dev || !buf || (len < sizeof (struct ieee80211_bsa_sta_stats))) |
| return -EINVAL; |
| |
| vap = netdev_priv(dev); |
| ic = vap->iv_ic; |
| bsa_statinfo.vap = vap; |
| bsa_statinfo.bsa_stats = buf; |
| |
| ic->ic_iterate_nodes(&ic->ic_sta, get_node_bsa_sta_stats, &bsa_statinfo, 1); |
| |
| return retval; |
| } |
| |
| static int |
| ieee80211_subioctl_get_bsa_associated_sta_stats(struct net_device *dev, |
| void __user *buf, uint16_t len) |
| { |
| int retval = 0; |
| struct ieee80211vap *vap; |
| struct ieee80211com *ic; |
| struct ieee80211_bsa_sta_statinfo bsa_statinfo; |
| if (!dev) |
| return -EINVAL; |
| vap = netdev_priv(dev); |
| if (vap->iv_opmode != IEEE80211_M_HOSTAP) |
| return -EFAULT; |
| ic = vap->iv_ic; |
| bsa_statinfo.vap = vap; |
| bsa_statinfo.bsa_stats = buf; |
| ic->ic_iterate_nodes(&ic->ic_sta, get_node_bsa_associated_sta_stats, vap, 1); |
| |
| return retval; |
| } |
| |
| static int |
| ieee80211_subioctl_get_bsa_interface_fat_info(struct net_device *dev, struct |
| ieee80211_bsa_interface_status __user *buf, uint16_t len) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct shared_params *sp = qtn_mproc_sync_shared_params_get(); |
| struct ieee80211_bsa_interface_fat_info bsa_intf_fat_req; |
| int retval = 0; |
| uint16_t fat = 0; |
| |
| if (!dev || !buf || (len < sizeof (bsa_intf_fat_req))) |
| return -EINVAL; |
| |
| if (copy_from_user(&bsa_intf_fat_req, buf, sizeof(bsa_intf_fat_req)) != 0) |
| return -EFAULT; |
| |
| fat = sp->free_airtime; |
| bsa_intf_fat_req.channel = ic->ic_curchan->ic_ieee; |
| |
| if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) |
| bsa_intf_fat_req.band = BSA_OPER_BAND_5G; |
| else |
| bsa_intf_fat_req.band = BSA_OPER_BAND_2G; |
| |
| put_unaligned(fat, &bsa_intf_fat_req.avg_fat); |
| |
| if (copy_to_user(buf, &bsa_intf_fat_req, sizeof(bsa_intf_fat_req)) != 0) |
| return -EFAULT; |
| |
| return retval; |
| } |
| |
| static uint16_t |
| ieee80211_bsa_get_bss_capability(struct ieee80211vap *vap, struct ieee80211com *ic) |
| { |
| uint16_t capinfo; |
| if (vap->iv_opmode == IEEE80211_M_IBSS) |
| capinfo = IEEE80211_CAPINFO_IBSS; |
| else |
| capinfo = IEEE80211_CAPINFO_ESS; |
| if (vap->iv_flags & IEEE80211_F_PRIVACY) |
| capinfo |= IEEE80211_CAPINFO_PRIVACY; |
| if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && |
| IEEE80211_IS_CHAN_2GHZ(ic->ic_bsschan)) |
| capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; |
| if (ic->ic_flags & IEEE80211_F_SHSLOT) |
| capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; |
| if (ic->ic_flags & IEEE80211_F_DOTH) |
| capinfo |= IEEE80211_CAPINFO_SPECTRUM_MGMT; |
| return capinfo; |
| } |
| |
| static int |
| ieee80211_subioctl_get_bsa_interface_info(struct net_device *dev, struct |
| ieee80211_bsa_interface_status __user *buf, uint16_t len) |
| |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_node *ni = ieee80211_get_vap_node(vap); |
| struct ieee80211_bsa_interface_status bsa_intf_req; |
| int bw; |
| int retval = 0; |
| |
| if(!dev || !buf || (len < sizeof (bsa_intf_req))) |
| return -EINVAL; |
| |
| if (copy_from_user(&bsa_intf_req, buf, sizeof(bsa_intf_req)) != 0) |
| return -EFAULT; |
| |
| if (vap->iv_opmode != IEEE80211_M_HOSTAP) { |
| if (ni) |
| ieee80211_free_node(ni); |
| return -EFAULT; |
| } |
| |
| IEEE80211_ADDR_COPY(bsa_intf_req.bssid, vap->iv_myaddr); |
| put_unaligned(0, &bsa_intf_req.mdid); /* MDID */ |
| bsa_intf_req.channel = ic->ic_curchan->ic_ieee; |
| |
| if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) |
| bsa_intf_req.band = BSA_OPER_BAND_5G; |
| else |
| bsa_intf_req.band = BSA_OPER_BAND_2G; |
| |
| bw = ieee80211_get_bw(ic); |
| put_unaligned(ieee80211_get_current_operating_class(ic->ic_country_code, |
| ic->ic_bsschan->ic_ieee, bw), |
| &bsa_intf_req.opclass); /* operating class */ |
| |
| bsa_intf_req.drivercap = BIT(BSA_DRIVER_CAP_BTM_SHIFT); |
| |
| if (IS_IEEE80211_VHT_ENABLED(ic)) |
| bsa_intf_req.phytype = 9; /* vht */ |
| else |
| bsa_intf_req.phytype = 7; /* ht */ |
| put_unaligned(ieee80211_bsa_get_bss_capability(vap, ic), &bsa_intf_req.capinfo); |
| put_unaligned(ic->ic_lintval, &bsa_intf_req.beacon_interval); |
| if (ni) { |
| ieee80211_add_htcap(ni, (uint8_t *)&bsa_intf_req.htcap, |
| &ic->ic_htcap, IEEE80211_FC0_SUBTYPE_PROBE_REQ); |
| ieee80211_add_htinfo(ni, (uint8_t *)&bsa_intf_req.htop, &ic->ic_htinfo); |
| if (IS_IEEE80211_VHT_ENABLED(ic)) { |
| ieee80211_add_vhtcap(ni, (uint8_t *)&bsa_intf_req.vhtcap, |
| &ic->ic_vhtcap, IEEE80211_FC0_SUBTYPE_PROBE_REQ); |
| ieee80211_add_vhtop(ni, (uint8_t *)&bsa_intf_req.vhtop, &ic->ic_vhtop); |
| } |
| ieee80211_free_node(ni); |
| } |
| |
| if (copy_to_user(buf, &bsa_intf_req, sizeof(bsa_intf_req)) != 0) { |
| return -EFAULT; |
| } |
| |
| return retval; |
| } |
| #endif |
| |
| static int |
| ieee80211_subioctl_get_cca_stats(struct net_device *dev, struct qtn_exp_cca_stats __user* cs) |
| { |
| int retval = 0; |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct qtn_exp_cca_stats cca_stats; |
| |
| if (ic->ic_get_cca_stats) { |
| retval = ic->ic_get_cca_stats(dev, ic, &cca_stats); |
| } else { |
| return -EINVAL; |
| } |
| |
| if (retval >= 0) { |
| if (copy_to_user(cs, &cca_stats, sizeof(struct qtn_exp_cca_stats)) != 0) |
| retval = -EFAULT; |
| } |
| |
| return retval; |
| } |
| |
| |
| static int |
| ieee80211_subioctl_set_mac_acl(struct net_device *dev, struct iwreq *iwr) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211_acl_params *params; |
| int retval = 0; |
| |
| MALLOC(params, struct ieee80211_acl_params *, iwr->u.data.length, M_DEVBUF, M_WAITOK); |
| if (!params) |
| return -ENOMEM; |
| |
| if (copy_from_user(params, iwr->u.data.pointer, iwr->u.data.length) != 0) { |
| retval = -EFAULT; |
| goto buf_free; |
| } |
| |
| if (iwr->u.data.length < (sizeof(*params) + sizeof(params->mac_acl[0]) * params->num_mac_acl)) { |
| retval = -EINVAL; |
| goto buf_free; |
| } |
| |
| retval = ieee80211_mac_acl(vap, params->acl_policy); |
| if (retval < 0) |
| goto buf_free; |
| |
| if (params->acl_policy == IEEE80211_MACCMD_POLICY_ALLOW || |
| params->acl_policy == IEEE80211_MACCMD_POLICY_DENY) { |
| const struct ieee80211_aclator *acl = vap->iv_acl; |
| |
| acl->iac_flush(vap); |
| if (params->num_mac_acl > 0) |
| retval = acl->iac_add_mac_list(vap, params->num_mac_acl, params->mac_acl); |
| } |
| |
| buf_free: |
| FREE(params, M_DEVBUF); |
| return retval; |
| } |
| |
| /** |
| * ieee80211_ioctl_ext - dispatch function for sub-ioctl commands |
| * dev: network device descriptor |
| * iwr: request information |
| * |
| * Three parameters are used to support sub-ioctls: |
| * iwr->u.data.flags : the command of sub-ioctl operation |
| * iwr->u.data.pointer: void* pointer to generic object |
| * iwr->u.data.length : the size of generic object |
| */ |
| static int |
| ieee80211_ioctl_ext(struct net_device *dev, struct iwreq *iwr) |
| { |
| int retval = 0; |
| int16_t sub_io_cmd = iwr->u.data.flags; |
| struct ieee80211vap *vap = netdev_priv(dev); |
| |
| switch (sub_io_cmd) { |
| case SIOCDEV_SUBIO_RST_QUEUE: |
| retval = ieee80211_subioctl_rst_queue(dev, iwr->u.data.pointer); |
| break; |
| case SIOCDEV_SUBIO_RADAR_STATUS: |
| retval = ieee80211_subioctl_radar_status(dev, iwr->u.data.pointer); |
| break; |
| case SIOCDEV_SUBIO_GET_PHY_STATS: |
| retval = ieee80211_subioctl_get_phy_stats(dev, iwr->u.data.pointer); |
| break; |
| case SIOCDEV_SUBIO_DISCONN_INFO: |
| retval = ieee80211_subioctl_disconn_info(dev, iwr->u.data.pointer); |
| break; |
| case SIOCDEV_SUBIO_SET_BRCM_IOCTL: |
| retval = ieee80211_subioctl_brcm(dev, iwr->u.data.pointer); |
| break; |
| case SIOCDEV_SUBIO_SCS: |
| retval = ieee80211_subioctl_scs(dev, iwr->u.data.pointer); |
| break; |
| case SIOCDEV_SUBIO_WAIT_SCAN_TIMEOUT: |
| retval = ieee80211_subioctl_wait_scan_complete(dev, iwr->u.data.pointer); |
| break; |
| case SIOCDEV_SUBIO_AP_SCAN_RESULTS: |
| retval = ieee80211_subioctl_ap_scan_results(dev, iwr->u.data.pointer, iwr->u.data.length); |
| break; |
| #if defined(CONFIG_QTN_80211K_SUPPORT) |
| case SIOCDEV_SUBIO_SET_SOC_ADDR_IOCTL: |
| { |
| u_int8_t addr_from_user[IEEE80211_ADDR_LEN]; |
| if (copy_from_user(addr_from_user, iwr->u.data.pointer, IEEE80211_ADDR_LEN)) { |
| retval = -EFAULT; |
| } else { |
| memcpy(vap->iv_ic->soc_addr, addr_from_user, IEEE80211_ADDR_LEN); |
| } |
| break; |
| } |
| #endif |
| case SIOCDEV_SUBIO_SET_TDLS_OPER: |
| retval = ieee80211_subioctl_tdls_operation(dev, iwr->u.data.pointer); |
| break; |
| case SIOCDEV_SUBIO_GET_11H_11K_NODE_INFO: |
| retval = ieee80211_subioctl_get_doth_dotk_report(dev, iwr->u.data.pointer); |
| break; |
| case SIOCDEV_SUBIO_GET_DSCP2AC_MAP: |
| retval = ieee80211_subioctl_get_dscp2ac_map(dev, iwr->u.data.pointer); |
| break; |
| case SIOCDEV_SUBIO_SET_DSCP2AC_MAP: |
| retval = ieee80211_subioctl_set_dscp2ac_map(dev, iwr->u.data.pointer); |
| break; |
| |
| case SIOCDEV_SUBIO_SET_MARK_DFS_CHAN: |
| retval = ieee80211_subioctl_set_chan_dfs_required(dev, iwr->u.data.pointer, iwr->u.data.length); |
| break; |
| case SIOCDEV_SUBIO_SET_WEATHER_CHAN: |
| retval = ieee80211_subioctl_set_chan_weather_radar(dev, iwr->u.data.pointer, iwr->u.data.length); |
| break; |
| case SIOCDEV_SUBIO_SETGET_CHAN_DISABLED: |
| retval = ieee80211_subioctl_setget_chan_disabled(dev, iwr->u.data.pointer, iwr->u.data.length); |
| break; |
| case SIOCDEV_SUBIO_WOWLAN: |
| retval = ieee80211_subioctl_wowlan_setget(dev, iwr->u.data.pointer, iwr->u.data.length); |
| break; |
| |
| case SIOCDEV_SUBIO_GET_STA_AUTH: |
| retval = ieee80211_subioctl_get_sta_auth(dev, iwr->u.data.pointer, iwr->u.data.length); |
| break; |
| case SIOCDEV_SUBIO_GET_STA_VENDOR: |
| retval = ieee80211_subioctl_get_sta_vendor(dev, iwr->u.data.pointer); |
| break; |
| case SIOCDEV_SUBIO_GET_STA_TPUT_CAPS: |
| retval = ieee80211_subioctl_get_sta_tput_caps(dev, iwr->u.data.pointer, |
| iwr->u.data.length); |
| break; |
| case SIOCDEV_SUBIO_GET_SWFEAT_MAP: |
| retval = ieee80211_subioctl_get_swfeat_map(dev, iwr->u.data.pointer, |
| iwr->u.data.length); |
| break; |
| case SIOCDEV_SUBIO_PRINT_SWFEAT_MAP: |
| retval = ieee80211_subioctl_print_swfeat_map(dev, iwr->u.data.pointer, |
| iwr->u.data.length); |
| break; |
| case SIOCDEV_SUBIO_DI_DFS_CHANNELS: |
| retval = ieee80211_ioctl_di_dfs_channels(dev, iwr->u.data.pointer, iwr->u.data.length); |
| break; |
| case SIOCDEV_SUBIO_SEND_ACTION_FRAME: |
| ieee80211_subioctl_send_action_frame(dev, iwr->u.data.pointer, iwr->u.data.length); |
| break; |
| case SIOCDEV_SUBIO_GET_DRIVER_CAPABILITY: |
| ieee80211_subioctl_get_driver_capa(dev, iwr->u.data.pointer, iwr->u.data.length); |
| break; |
| case SIOCDEV_SUBIO_SET_AP_INFO: |
| ieee80211_subioctl_set_ap_info(dev, iwr->u.data.pointer, iwr->u.data.length); |
| break; |
| case SIOCDEV_SUBIO_SET_ACTIVE_CHANNEL_LIST: |
| retval = ieee80211_subioctl_set_active_chanlist_by_bw(dev, iwr->u.data.pointer); |
| break; |
| case SIOCDEV_SUBIO_GET_LINK_QUALITY_MAX: |
| retval = ieee80211_subioctl_get_link_quality_max(dev, iwr->u.data.pointer, iwr->u.data.length); |
| break; |
| case SIOCDEV_SUBIO_GET_CHANNEL_POWER_TABLE: |
| retval = ieee80211_subioctl_get_chan_power_table(dev, iwr->u.data.pointer, iwr->u.data.length); |
| break; |
| case SIOCDEV_SUBIO_SET_CHANNEL_POWER_TABLE: |
| retval = ieee80211_subioctl_set_chan_power_table(dev, iwr->u.data.pointer); |
| break; |
| case SIOCDEV_SUBIO_SET_SEC_CHAN: |
| retval = ieee80211_subioctl_set_sec_chan(dev, iwr->u.data.pointer, iwr->u.data.length); |
| break; |
| case SIOCDEV_SUBIO_GET_SEC_CHAN: |
| retval = ieee80211_subioctl_get_sec_chan(dev, iwr->u.data.pointer, iwr->u.data.length); |
| break; |
| case SIOCDEV_SUBIO_GET_DSCP2TID_MAP: |
| retval = ieee80211_subioctl_get_dscp2tid_map(dev, iwr->u.data.pointer, |
| iwr->u.data.length); |
| break; |
| case SIOCDEV_SUBIO_SET_DSCP2TID_MAP: |
| retval = ieee80211_subioctl_set_dscp2tid_map(dev, iwr->u.data.pointer, |
| iwr->u.data.length); |
| break; |
| case SIOCDEV_SUBIO_GET_TX_AIRTIME: |
| retval = ieee80211_subioctl_get_txrx_airtime(dev, iwr); |
| break; |
| case SIOCDEV_SUBIO_GET_CHAN_PRI_INACT: |
| retval = ieee80211_subioctl_get_chan_pri_inact(dev, iwr->u.data.pointer, iwr->u.data.length); |
| break; |
| case SIOCDEV_SUBIO_GET_SUPP_CHAN: |
| retval = ieee80211_get_supp_chans(vap, iwr); |
| break; |
| case SIOCDEV_SUBIO_GET_CLIENT_MACS: |
| retval = ieee80211_subioctl_get_client_mac_list(dev, iwr->u.data.pointer, |
| iwr->u.data.length); |
| break; |
| case SIOCDEV_SUBIO_SAMPLE_ALL_DATA: |
| retval = ieee80211_subioctl_sample_all_clients(vap, iwr); |
| break; |
| case SIOCDEV_SUBIO_GET_ASSOC_DATA: |
| retval = ieee80211_subioctl_get_assoc_data(vap, iwr->u.data.pointer, iwr->u.data.length); |
| break; |
| case SIOCDEV_SUBIO_GET_INTERFACE_WMMAC_STATS: |
| retval = ieee80211_subioctl_get_interface_wmmac_stats(dev, iwr); |
| break; |
| #if defined(CONFIG_QTN_BSA_SUPPORT) |
| case SIOCDEV_SUBIO_SET_BSA_STATUS: |
| retval = ieee80211_subioctl_set_bsa_module(dev, iwr->u.data.pointer); |
| break; |
| case SIOCDEV_SUBIO_GET_BSA_INTF_INFO: |
| retval = ieee80211_subioctl_get_bsa_interface_info(dev, iwr->u.data.pointer, |
| iwr->u.data.length); |
| break; |
| case SIOCDEV_SUBIO_GET_BSA_FAT_INFO: |
| retval = ieee80211_subioctl_get_bsa_interface_fat_info(dev, iwr->u.data.pointer, |
| iwr->u.data.length); |
| break; |
| case SIOCDEV_SUBIO_UPDATE_MACFILTER_LIST: |
| retval = ieee80211_subioctl_update_macfilter_table(dev, iwr->u.data.pointer); |
| break; |
| case SIOCDEV_SUBIO_GET_BSA_STA_STATS: |
| retval = ieee80211_subioctl_get_bsa_sta_stats(dev, iwr->u.data.pointer, |
| iwr->u.data.length); |
| break; |
| case SIOCDEV_SUBIO_GET_BSA_ASSOC_STA_STATS: |
| retval = ieee80211_subioctl_get_bsa_associated_sta_stats(dev, iwr->u.data.pointer, |
| iwr->u.data.length); |
| break; |
| case SIOCDEV_SUBIO_SEND_BTM_REQ_FRM: |
| retval = ieee80211_subioctl_send_btm_req_frm(dev, iwr->u.data.pointer, |
| iwr->u.data.length); |
| break; |
| #endif |
| case SIOCDEV_SUBIO_GET_FREQ_RANGE: |
| retval = ieee80211_subioctl_get_freq_range(dev, iwr); |
| break; |
| #ifdef CONFIG_NAC_MONITOR |
| case SIOCDEV_SUBIO_GET_NAC_STATS: |
| retval = ieee80211_subioctl_get_nac_stats(dev, iwr); |
| break; |
| #endif |
| case SIOCDEV_SUBIO_SET_MAC_ADDR_ACL: |
| retval = ieee80211_subioctl_set_mac_acl(dev, iwr); |
| break; |
| case SIOCDEV_SUBIO_SET_FT_ASSOC_RESP: |
| retval = ieee80211_subioctl_send_ft_assoc_response(dev, iwr->u.data.pointer, |
| iwr->u.data.length); |
| break; |
| case SIOCDEV_SUBIO_SET_FT_REASSOC_RESP: |
| retval = ieee80211_subioctl_send_ft_reassoc_response(dev, iwr->u.data.pointer, |
| iwr->u.data.length); |
| break; |
| case SIOCDEV_SUBIO_SET_FT_AUTH_RESP: |
| retval = ieee80211_subioctl_send_ft_auth_response(dev, iwr->u.data.pointer, |
| iwr->u.data.length); |
| break; |
| case SIOCDEV_SUBIO_SET_FT_ADD_NODE: |
| retval = ieee80211_subioctl_ft_add_node(dev, iwr->u.data.pointer, |
| iwr->u.data.length); |
| break; |
| case SIOCDEV_SUBIO_GET_CCA_STATS: |
| retval = ieee80211_subioctl_get_cca_stats(dev, iwr->u.data.pointer); |
| break; |
| default: |
| retval = -EOPNOTSUPP; |
| } |
| |
| return retval; |
| } |
| |
| #define MAX_NUM_SUPPORTED_RATES 512 |
| #define GET_SET_BASIC_RATE 1 |
| #define GET_SET_OPERATIONAL_RATE 2 |
| #define GET_SET_MCS_RATE 3 |
| #define MAX_MCS_SIZE 77 |
| #define USEC_PER_SECOND 1000000 |
| |
| static int |
| is_duplicate_rate(const uint32_t *rate_set, int num, uint32_t rate) |
| { |
| int i; |
| |
| for (i = 0; i < num; i++) { |
| if (rate_set[i] == rate) { |
| return 1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int ieee80211_ioctl_get_ht_rates(struct ieee80211com *ic, |
| uint32_t *ht_rates, int pos) |
| { |
| int i = 0; |
| int k = 0, r = 0; |
| u_int16_t chan_20 = 0; |
| u_int16_t mask; |
| u_int16_t chan_40 = 0; |
| int sgi_40 = 0; |
| int sgi_20 = 0; |
| uint32_t rate; |
| |
| /* HT 20 MHz band LGI and SGI rates */ |
| sgi_20 = ic->ic_htcap.cap & IEEE80211_HTCAP_C_SHORTGI20 ? 1 : 0; |
| chan_40 = ic->ic_htcap.cap & IEEE80211_HTCAP_C_CHWIDTH40 ? 1 : 0; |
| sgi_40 = ic->ic_htcap.cap & IEEE80211_HTCAP_C_SHORTGI40 ? 1 : 0; |
| |
| for (r = 0, i = IEEE80211_HT_MCSSET_20_40_NSS1; |
| i <= IEEE80211_HT_MCSSET_20_40_UEQM6; i++) { |
| mask = 1; |
| for (k = 0; k < 8; k++, r++) { |
| if (ic->ic_htcap.mcsset[i] & mask) { |
| rate = ieee80211_mcs2rate(r, |
| chan_20, 0, 0) * (USEC_PER_SECOND / 2); |
| if (!is_duplicate_rate(ht_rates, pos, rate)) { |
| ht_rates[pos++] = rate; |
| } |
| |
| if (sgi_20) { |
| rate = ieee80211_mcs2rate(r, |
| chan_20, sgi_20, 0) * (USEC_PER_SECOND / 2); |
| if (!is_duplicate_rate(ht_rates, pos, rate)) { |
| ht_rates[pos++] = rate; |
| } |
| } |
| |
| if (chan_40) { |
| rate = ieee80211_mcs2rate(r, |
| chan_40, 0, 0) * (USEC_PER_SECOND / 2); |
| if (!is_duplicate_rate(ht_rates, pos, rate)) { |
| ht_rates[pos++] = rate; |
| } |
| |
| if (sgi_40) { |
| rate = ieee80211_mcs2rate(r, |
| chan_40, sgi_40, 0) * (USEC_PER_SECOND / 2); |
| if (!is_duplicate_rate(ht_rates, pos, rate)) { |
| ht_rates[pos++] = rate; |
| } |
| } |
| |
| } |
| } |
| mask = mask << 1; |
| } |
| } |
| |
| return pos; |
| } |
| |
| static int ieee80211_ioctl_get_vht_rates(struct ieee80211com *ic, |
| uint32_t *vht_rates, int pos) |
| { |
| int k = 0, r = 0; |
| u_int16_t mcsmap = 0; |
| int sgi_80; |
| int sgi_160; |
| u_int16_t mask; |
| u_int16_t chan_80 = 0; |
| u_int16_t chan_160 = 0; |
| uint32_t rate; |
| |
| if (ic->ic_vhtcap.cap_flags & IEEE80211_VHTCAP_C_CHWIDTH) { |
| chan_160 = 1; |
| } |
| sgi_80 = (ic->ic_vhtcap.cap_flags & IEEE80211_VHTCAP_C_SHORT_GI_80) ? 1 : 0; |
| sgi_160 = (ic->ic_vhtcap.cap_flags & IEEE80211_VHTCAP_C_SHORT_GI_160) ? 1 : 0; |
| mask = 0x3; |
| mcsmap = ic->ic_vhtcap.txmcsmap; |
| for (k = 0; k < 8; k++) { |
| if ((mcsmap & mask) != mask) { |
| int m; |
| int val = (mcsmap & mask)>>(k * 2); |
| r = (val == 2) ? 9: (val == 1) ? 8 : 7; |
| for (m = 0; m <= r; m++) { |
| rate = (ieee80211_mcs2rate(m, |
| chan_80, 0, 1) * (USEC_PER_SECOND / 2)) * (k+1); |
| if (!is_duplicate_rate(vht_rates, pos, rate)) { |
| vht_rates[pos++] = rate; |
| } |
| if (sgi_80) { |
| rate = (ieee80211_mcs2rate(m, chan_80, |
| sgi_80, 1) * (USEC_PER_SECOND / 2)) * (k+1); |
| if (!is_duplicate_rate(vht_rates, pos, rate)) { |
| vht_rates[pos++] = rate; |
| } |
| } |
| |
| /* 160/80+80 MHz rates */ |
| if (chan_160) { |
| rate = (ieee80211_mcs2rate(m, |
| chan_160, 0, 1) * (USEC_PER_SECOND / 2)) * (k+1); |
| if (!is_duplicate_rate(vht_rates, pos, rate)) { |
| vht_rates[pos++] = rate; |
| } |
| if (sgi_160) { |
| rate = (ieee80211_mcs2rate(m, |
| chan_160, sgi_160, 1) * (USEC_PER_SECOND / 2)) * (k+1); |
| if (!is_duplicate_rate(vht_rates, pos, rate)) { |
| vht_rates[pos++] = rate; |
| } |
| } |
| |
| } |
| } |
| mask = mask << 2; |
| } else { |
| break; |
| } |
| } |
| |
| return pos; |
| } |
| |
| /** |
| * ieee80211_ioctl_get_rates - function to get the rates |
| */ |
| static int |
| ieee80211_ioctl_get_rates(struct net_device *dev, struct iwreq *iwr) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| uint32_t achievable_tx_phy_rates[MAX_NUM_SUPPORTED_RATES]; |
| uint32_t achievable_rates_num = iwr->u.data.length / sizeof(uint32_t); |
| int mode, nrates, i = 0, j = 0; |
| int flags = iwr->u.data.flags; |
| struct ieee80211_rateset *rs; |
| |
| mode = ic->ic_curmode; // Get the current mode |
| rs = &ic->ic_sup_rates[mode]; // Get the supported rates depending on the mode |
| nrates = rs->rs_legacy_nrates; |
| |
| #if 0 |
| /* NB: not sorted */ |
| /* Basic and extended rates */ |
| for (i = 0; i < nrates ; i++) { |
| if ( (flags == GET_SET_BASIC_RATE) && |
| !(rs->rs_rates[i] & IEEE80211_RATE_BASIC) ) { |
| continue; |
| } |
| |
| achievable_tx_phy_rates[j] = rs->rs_rates[i] & IEEE80211_RATE_VAL; |
| // Keep the rates in Mbps. Multiply the rate by 1M |
| achievable_tx_phy_rates[j] *= USEC_PER_SECOND / 2; |
| j++; |
| } |
| |
| if ((flags == GET_SET_OPERATIONAL_RATE && |
| mode >= IEEE80211_MODE_11NA) || |
| flags == GET_SET_MCS_RATE) { |
| /* HT rates */ |
| j = ieee80211_ioctl_get_ht_rates(ic, &achievable_tx_phy_rates[0], j); |
| |
| } |
| |
| if ((flags == GET_SET_OPERATIONAL_RATE && |
| mode >= IEEE80211_MODE_11AC_VHT20PM) || |
| flags == GET_SET_MCS_RATE) { |
| /* VHT rates */ |
| j = ieee80211_ioctl_get_vht_rates(ic, &achievable_tx_phy_rates[0], j); |
| } |
| |
| if (achievable_rates_num > j) { |
| achievable_rates_num = j; |
| } |
| #else |
| if (flags == GET_SET_MCS_RATE) { |
| /* NB: not sorted */ |
| /* Basic and extended rates */ |
| for (i = 0; i < nrates ; i++) { |
| achievable_tx_phy_rates[j] = rs->rs_rates[i] & IEEE80211_RATE_VAL; |
| // Keep the rates in Mbps. Multiply the rate by 1M |
| achievable_tx_phy_rates[j] *= (USEC_PER_SECOND / 2); |
| j++; |
| } |
| |
| /* Possible HT rates */ |
| j = ieee80211_ioctl_get_ht_rates(ic, &achievable_tx_phy_rates[0], j); |
| |
| /* Possible VHT rates */ |
| j = ieee80211_ioctl_get_vht_rates(ic, &achievable_tx_phy_rates[0], j); |
| |
| if (achievable_rates_num > j) { |
| achievable_rates_num = j; |
| } |
| |
| iwr->u.data.length = achievable_rates_num * sizeof(achievable_rates_num); |
| |
| } else { /* Basic or operational rates */ |
| for (i = 0; i < nrates ; i++) { |
| if ( (flags == GET_SET_BASIC_RATE) && !(rs->rs_rates[i] & IEEE80211_RATE_BASIC) ) { |
| continue; |
| } |
| |
| achievable_tx_phy_rates[j] = rs->rs_rates[i] & IEEE80211_RATE_VAL; |
| /* Keep the rates in Mbps. Multiply the rate by 1M */ |
| achievable_tx_phy_rates[j] *= USEC_PER_SECOND; |
| j++; |
| } |
| |
| iwr->u.data.length = j * sizeof(int32_t); |
| } |
| |
| #endif |
| iwr->u.data.length = achievable_rates_num * sizeof(achievable_rates_num); |
| |
| if (copy_to_user(iwr->u.data.pointer, achievable_tx_phy_rates, |
| MIN(sizeof(achievable_tx_phy_rates), iwr->u.data.length))) |
| return -EFAULT; |
| |
| return 0; |
| } |
| |
| /** |
| * ieee80211_ioctl_set_rates - function to set the rates. This can be used for setting the |
| * rates either to basic or operational rates. |
| */ |
| static int |
| ieee80211_ioctl_set_rates(struct net_device *dev, struct iwreq *iwr) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_rateset *rs; |
| char *ptr = ((char *)iwr->u.data.pointer); |
| uint32_t num_rates = iwr->u.data.length; |
| uint32_t flags = iwr->u.data.flags; |
| unsigned long rate; |
| enum ieee80211_phymode mode; |
| int retval = 0; |
| int i = 0; |
| |
| mode = ic->ic_curmode; |
| rs = &ic->ic_sup_rates[mode]; |
| |
| /* Set rates is allowed for only Basic and operational rates (non 11n) */ |
| if (num_rates > IEEE80211_AG_RATE_MAXSIZE) { |
| num_rates = IEEE80211_AG_RATE_MAXSIZE; |
| } |
| |
| while ( num_rates-- ) { |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0) |
| if (kstrtoul(ptr, 0, &rate)) { |
| #else |
| if (strict_strtoul(ptr, 0, &rate)) { |
| #endif |
| printk(KERN_WARNING "Invalide input string\n"); |
| retval = -EINVAL; |
| break; |
| } |
| |
| for (i = 0; i < rs->rs_legacy_nrates; i++) { |
| if (rate == (rs->rs_rates[i] & IEEE80211_RATE_VAL)) { |
| |
| if (flags == GET_SET_BASIC_RATE) { |
| rs->rs_rates[i] |= IEEE80211_RATE_BASIC; |
| } else if (flags == GET_SET_OPERATIONAL_RATE ) { |
| rs->rs_rates[i] &= ~IEEE80211_RATE_BASIC; |
| |
| } else { |
| printk(KERN_WARNING "Not supported to change the MCS rates\n"); |
| retval = -EINVAL; |
| break; |
| } |
| } |
| } |
| ptr += strlen(ptr) + 1; |
| } |
| |
| /* Update the beacon. This will dynamically change the rates |
| * in probe response and beacons */ |
| if (!retval) { |
| ic->ic_beacon_update(vap); |
| } |
| |
| return retval; |
| } |
| |
| static void |
| pre_announced_chanswitch(struct net_device *dev, u_int32_t channel, u_int32_t tbtt) { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| /* now flag the beacon update to include the channel switch IE */ |
| ic->ic_flags |= IEEE80211_F_CHANSWITCH; |
| ic->ic_chanchange_chan = channel; |
| ic->ic_chanchange_tbtt = tbtt; |
| } |
| |
| static int |
| ieee80211_ioctl_chanswitch(struct net_device *dev, struct iw_request_info *info, |
| void *w, char *extra) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct ieee80211com *ic = vap->iv_ic; |
| int *param = (int *) extra; |
| |
| if (!(ic->ic_flags & IEEE80211_F_DOTH)) |
| return 0; |
| |
| pre_announced_chanswitch(dev, param[0], param[1]); |
| |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_siwmlme(struct net_device *dev, |
| struct iw_request_info *info, struct iw_point *erq, char *data) |
| { |
| struct ieee80211req_mlme mlme; |
| struct iw_mlme *wextmlme = (struct iw_mlme *)data; |
| |
| memset(&mlme, 0, sizeof(mlme)); |
| |
| switch(wextmlme->cmd) { |
| case IW_MLME_DEAUTH: |
| mlme.im_op = IEEE80211_MLME_DEAUTH; |
| break; |
| case IW_MLME_DISASSOC: |
| mlme.im_op = IEEE80211_MLME_DISASSOC; |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| mlme.im_reason = wextmlme->reason_code; |
| |
| memcpy(mlme.im_macaddr, wextmlme->addr.sa_data, IEEE80211_ADDR_LEN); |
| |
| return ieee80211_ioctl_setmlme(dev, NULL, NULL, (char*)&mlme); |
| } |
| |
| |
| static int |
| ieee80211_ioctl_giwgenie(struct net_device *dev, |
| struct iw_request_info *info, struct iw_point *out, char *buf) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| |
| if (out->length < vap->iv_opt_ie_len) |
| return -E2BIG; |
| |
| return ieee80211_ioctl_getoptie(dev, info, out, buf); |
| } |
| |
| static int |
| ieee80211_ioctl_siwgenie(struct net_device *dev, |
| struct iw_request_info *info, struct iw_point *erq, char *data) |
| { |
| return ieee80211_ioctl_setoptie(dev, info, erq, data); |
| } |
| |
| |
| static int |
| siwauth_wpa_version(struct net_device *dev, |
| struct iw_request_info *info, struct iw_param *erq, char *buf) |
| { |
| int ver = erq->value; |
| int args[2]; |
| |
| args[0] = IEEE80211_PARAM_WPA; |
| |
| if ((ver & IW_AUTH_WPA_VERSION_WPA) && (ver & IW_AUTH_WPA_VERSION_WPA2)) |
| args[1] = 3; |
| else if (ver & IW_AUTH_WPA_VERSION_WPA2) |
| args[1] = 2; |
| else if (ver & IW_AUTH_WPA_VERSION_WPA) |
| args[1] = 1; |
| else |
| args[1] = 0; |
| |
| return ieee80211_ioctl_setparam(dev, NULL, NULL, (char*)args); |
| } |
| |
| static int |
| iwcipher2ieee80211cipher(int iwciph) |
| { |
| switch(iwciph) { |
| case IW_AUTH_CIPHER_NONE: |
| return IEEE80211_CIPHER_NONE; |
| case IW_AUTH_CIPHER_WEP40: |
| case IW_AUTH_CIPHER_WEP104: |
| return IEEE80211_CIPHER_WEP; |
| case IW_AUTH_CIPHER_TKIP: |
| return IEEE80211_CIPHER_TKIP; |
| case IW_AUTH_CIPHER_CCMP: |
| return IEEE80211_CIPHER_AES_CCM; |
| } |
| return -1; |
| } |
| |
| static int |
| ieee80211cipher2iwcipher(int ieee80211ciph) |
| { |
| switch(ieee80211ciph) { |
| case IEEE80211_CIPHER_NONE: |
| return IW_AUTH_CIPHER_NONE; |
| case IEEE80211_CIPHER_WEP: |
| return IW_AUTH_CIPHER_WEP104; |
| case IEEE80211_CIPHER_TKIP: |
| return IW_AUTH_CIPHER_TKIP; |
| case IEEE80211_CIPHER_AES_CCM: |
| return IW_AUTH_CIPHER_CCMP; |
| } |
| return -1; |
| } |
| |
| /* TODO We don't enforce wep key lengths. */ |
| static int |
| siwauth_cipher_pairwise(struct net_device *dev, |
| struct iw_request_info *info, struct iw_param *erq, char *buf) |
| { |
| int iwciph = erq->value; |
| int args[2]; |
| |
| args[0] = IEEE80211_PARAM_UCASTCIPHER; |
| args[1] = iwcipher2ieee80211cipher(iwciph); |
| if (args[1] < 0) { |
| printk(KERN_WARNING "%s: unknown pairwise cipher %d\n", |
| dev->name, iwciph); |
| return -EINVAL; |
| } |
| return ieee80211_ioctl_setparam(dev, NULL, NULL, (char*)args); |
| } |
| |
| /* TODO We don't enforce wep key lengths. */ |
| static int |
| siwauth_cipher_group(struct net_device *dev, |
| struct iw_request_info *info, struct iw_param *erq, char *buf) |
| { |
| int iwciph = erq->value; |
| int args[2]; |
| |
| args[0] = IEEE80211_PARAM_MCASTCIPHER; |
| args[1] = iwcipher2ieee80211cipher(iwciph); |
| if (args[1] < 0) { |
| printk(KERN_WARNING "%s: unknown group cipher %d\n", |
| dev->name, iwciph); |
| return -EINVAL; |
| } |
| return ieee80211_ioctl_setparam(dev, NULL, NULL, (char*)args); |
| } |
| |
| static int |
| siwauth_key_mgmt(struct net_device *dev, |
| struct iw_request_info *info, struct iw_param *erq, char *buf) |
| { |
| int iwkm = erq->value; |
| int args[2]; |
| |
| args[0] = IEEE80211_PARAM_KEYMGTALGS; |
| args[1] = WPA_ASE_NONE; |
| if (iwkm & IW_AUTH_KEY_MGMT_802_1X) |
| args[1] |= WPA_ASE_8021X_UNSPEC; |
| if (iwkm & IW_AUTH_KEY_MGMT_PSK) |
| args[1] |= WPA_ASE_8021X_PSK; |
| |
| return ieee80211_ioctl_setparam(dev, NULL, NULL, (char*)args); |
| } |
| |
| static int |
| siwauth_tkip_countermeasures(struct net_device *dev, |
| struct iw_request_info *info, struct iw_param *erq, char *buf) |
| { |
| int args[2]; |
| args[0] = IEEE80211_PARAM_COUNTERMEASURES; |
| args[1] = erq->value; |
| return ieee80211_ioctl_setparam(dev, NULL, NULL, (char*)args); |
| } |
| |
| static int |
| siwauth_drop_unencrypted(struct net_device *dev, |
| struct iw_request_info *info, struct iw_param *erq, char *buf) |
| { |
| int args[2]; |
| args[0] = IEEE80211_PARAM_DROPUNENCRYPTED; |
| args[1] = erq->value; |
| return ieee80211_ioctl_setparam(dev, NULL, NULL, (char*)args); |
| } |
| |
| |
| static int |
| siwauth_80211_auth_alg(struct net_device *dev, |
| struct iw_request_info *info, struct iw_param *erq, char *buf) |
| { |
| #define VALID_ALGS_MASK (IW_AUTH_ALG_OPEN_SYSTEM|IW_AUTH_ALG_SHARED_KEY|IW_AUTH_ALG_LEAP) |
| int mode = erq->value; |
| int args[2]; |
| |
| args[0] = IEEE80211_PARAM_AUTHMODE; |
| |
| if (mode & ~VALID_ALGS_MASK) { |
| return -EINVAL; |
| } |
| if (mode & IW_AUTH_ALG_LEAP) { |
| args[1] = IEEE80211_AUTH_8021X; |
| } else if ((mode & IW_AUTH_ALG_SHARED_KEY) && |
| (mode & IW_AUTH_ALG_OPEN_SYSTEM)) { |
| args[1] = IEEE80211_AUTH_AUTO; |
| } else if (mode & IW_AUTH_ALG_SHARED_KEY) { |
| args[1] = IEEE80211_AUTH_SHARED; |
| } else { |
| args[1] = IEEE80211_AUTH_OPEN; |
| } |
| return ieee80211_ioctl_setparam(dev, NULL, NULL, (char*)args); |
| } |
| |
| static int |
| siwauth_wpa_enabled(struct net_device *dev, |
| struct iw_request_info *info, struct iw_param *erq, char *buf) |
| { |
| int enabled = erq->value; |
| int args[2]; |
| |
| args[0] = IEEE80211_PARAM_WPA; |
| if (enabled) |
| args[1] = 3; /* enable WPA1 and WPA2 */ |
| else |
| args[1] = 0; /* disable WPA1 and WPA2 */ |
| |
| return ieee80211_ioctl_setparam(dev, NULL, NULL, (char*)args); |
| } |
| |
| static int |
| siwauth_rx_unencrypted_eapol(struct net_device *dev, |
| struct iw_request_info *info, struct iw_param *erq, char *buf) |
| { |
| int rxunenc = erq->value; |
| int args[2]; |
| |
| args[0] = IEEE80211_PARAM_DROPUNENC_EAPOL; |
| if (rxunenc) |
| args[1] = 1; |
| else |
| args[1] = 0; |
| |
| return ieee80211_ioctl_setparam(dev, NULL, NULL, (char*)args); |
| } |
| |
| static int |
| siwauth_roaming_control(struct net_device *dev, |
| struct iw_request_info *info, struct iw_param *erq, char *buf) |
| { |
| int roam = erq->value; |
| int args[2]; |
| |
| args[0] = IEEE80211_PARAM_ROAMING; |
| switch(roam) { |
| case IW_AUTH_ROAMING_ENABLE: |
| args[1] = IEEE80211_ROAMING_AUTO; |
| break; |
| case IW_AUTH_ROAMING_DISABLE: |
| args[1] = IEEE80211_ROAMING_MANUAL; |
| break; |
| default: |
| return -EINVAL; |
| } |
| return ieee80211_ioctl_setparam(dev, NULL, NULL, (char*)args); |
| } |
| |
| static int |
| siwauth_privacy_invoked(struct net_device *dev, |
| struct iw_request_info *info, struct iw_param *erq, char *buf) |
| { |
| int args[2]; |
| args[0] = IEEE80211_PARAM_PRIVACY; |
| args[1] = erq->value; |
| return ieee80211_ioctl_setparam(dev, NULL, NULL, (char*)args); |
| } |
| |
| /* |
| * If this function is invoked it means someone is using the wireless extensions |
| * API instead of the private madwifi ioctls. That's fine. We translate their |
| * request into the format used by the private ioctls. Note that the |
| * iw_request_info and iw_param structures are not the same ones as the |
| * private ioctl handler expects. Luckily, the private ioctl handler doesn't |
| * do anything with those at the moment. We pass NULL for those, because in |
| * case someone does modify the ioctl handler to use those values, a null |
| * pointer will be easier to debug than other bad behavior. |
| */ |
| static int |
| ieee80211_ioctl_siwauth(struct net_device *dev, |
| struct iw_request_info *info, struct iw_param *erq, char *buf) |
| { |
| int rc = -EINVAL; |
| |
| switch(erq->flags & IW_AUTH_INDEX) { |
| case IW_AUTH_WPA_VERSION: |
| rc = siwauth_wpa_version(dev, info, erq, buf); |
| break; |
| case IW_AUTH_CIPHER_PAIRWISE: |
| rc = siwauth_cipher_pairwise(dev, info, erq, buf); |
| break; |
| case IW_AUTH_CIPHER_GROUP: |
| rc = siwauth_cipher_group(dev, info, erq, buf); |
| break; |
| case IW_AUTH_KEY_MGMT: |
| rc = siwauth_key_mgmt(dev, info, erq, buf); |
| break; |
| case IW_AUTH_TKIP_COUNTERMEASURES: |
| rc = siwauth_tkip_countermeasures(dev, info, erq, buf); |
| break; |
| case IW_AUTH_DROP_UNENCRYPTED: |
| rc = siwauth_drop_unencrypted(dev, info, erq, buf); |
| break; |
| case IW_AUTH_80211_AUTH_ALG: |
| rc = siwauth_80211_auth_alg(dev, info, erq, buf); |
| break; |
| case IW_AUTH_WPA_ENABLED: |
| rc = siwauth_wpa_enabled(dev, info, erq, buf); |
| break; |
| case IW_AUTH_RX_UNENCRYPTED_EAPOL: |
| rc = siwauth_rx_unencrypted_eapol(dev, info, erq, buf); |
| break; |
| case IW_AUTH_ROAMING_CONTROL: |
| rc = siwauth_roaming_control(dev, info, erq, buf); |
| break; |
| case IW_AUTH_PRIVACY_INVOKED: |
| rc = siwauth_privacy_invoked(dev, info, erq, buf); |
| break; |
| default: |
| printk(KERN_WARNING "%s: unknown SIOCSIWAUTH flag %d\n", |
| dev->name, erq->flags); |
| break; |
| } |
| |
| return rc; |
| } |
| |
| static int |
| giwauth_wpa_version(struct net_device *dev, |
| struct iw_request_info *info, struct iw_param *erq, char *buf) |
| { |
| int ver; |
| int rc; |
| int arg = IEEE80211_PARAM_WPA; |
| |
| rc = ieee80211_ioctl_getparam(dev, NULL, NULL, (char*)&arg); |
| if (rc) |
| return rc; |
| |
| switch(arg) { |
| case 1: |
| ver = IW_AUTH_WPA_VERSION_WPA; |
| break; |
| case 2: |
| ver = IW_AUTH_WPA_VERSION_WPA2; |
| break; |
| case 3: |
| ver = IW_AUTH_WPA_VERSION|IW_AUTH_WPA_VERSION_WPA2; |
| break; |
| default: |
| ver = IW_AUTH_WPA_VERSION_DISABLED; |
| break; |
| } |
| |
| erq->value = ver; |
| return rc; |
| } |
| |
| static int |
| giwauth_cipher_pairwise(struct net_device *dev, |
| struct iw_request_info *info, struct iw_param *erq, char *buf) |
| { |
| int rc; |
| int arg = IEEE80211_PARAM_UCASTCIPHER; |
| |
| rc = ieee80211_ioctl_getparam(dev, NULL, NULL, (char*)&arg); |
| if (rc) |
| return rc; |
| |
| erq->value = ieee80211cipher2iwcipher(arg); |
| if (erq->value < 0) |
| return -EINVAL; |
| return 0; |
| } |
| |
| |
| static int |
| giwauth_cipher_group(struct net_device *dev, |
| struct iw_request_info *info, struct iw_param *erq, char *buf) |
| { |
| int rc; |
| int arg = IEEE80211_PARAM_MCASTCIPHER; |
| |
| rc = ieee80211_ioctl_getparam(dev, NULL, NULL, (char*)&arg); |
| if (rc) |
| return rc; |
| |
| erq->value = ieee80211cipher2iwcipher(arg); |
| if (erq->value < 0) |
| return -EINVAL; |
| return 0; |
| } |
| |
| static int |
| giwauth_key_mgmt(struct net_device *dev, |
| struct iw_request_info *info, struct iw_param *erq, char *buf) |
| { |
| int arg; |
| int rc; |
| |
| arg = IEEE80211_PARAM_KEYMGTALGS; |
| rc = ieee80211_ioctl_getparam(dev, NULL, NULL, (char*)&arg); |
| if (rc) |
| return rc; |
| erq->value = 0; |
| if (arg & WPA_ASE_8021X_UNSPEC) |
| erq->value |= IW_AUTH_KEY_MGMT_802_1X; |
| if (arg & WPA_ASE_8021X_PSK) |
| erq->value |= IW_AUTH_KEY_MGMT_PSK; |
| return 0; |
| } |
| |
| static int |
| giwauth_tkip_countermeasures(struct net_device *dev, |
| struct iw_request_info *info, struct iw_param *erq, char *buf) |
| { |
| int arg; |
| int rc; |
| |
| arg = IEEE80211_PARAM_COUNTERMEASURES; |
| rc = ieee80211_ioctl_getparam(dev, NULL, NULL, (char*)&arg); |
| if (rc) |
| return rc; |
| erq->value = arg; |
| return 0; |
| } |
| |
| static int |
| giwauth_drop_unencrypted(struct net_device *dev, |
| struct iw_request_info *info, struct iw_param *erq, char *buf) |
| { |
| int arg; |
| int rc; |
| arg = IEEE80211_PARAM_DROPUNENCRYPTED; |
| rc = ieee80211_ioctl_getparam(dev, NULL, NULL, (char*)&arg); |
| if (rc) |
| return rc; |
| erq->value = arg; |
| return 0; |
| } |
| |
| static int |
| giwauth_80211_auth_alg(struct net_device *dev, |
| struct iw_request_info *info, struct iw_param *erq, char *buf) |
| { |
| return -EOPNOTSUPP; |
| } |
| |
| static int |
| giwauth_wpa_enabled(struct net_device *dev, |
| struct iw_request_info *info, struct iw_param *erq, char *buf) |
| { |
| int rc; |
| int arg = IEEE80211_PARAM_WPA; |
| |
| rc = ieee80211_ioctl_getparam(dev, NULL, NULL, (char*)&arg); |
| if (rc) |
| return rc; |
| |
| erq->value = arg; |
| return 0; |
| |
| } |
| |
| static int |
| giwauth_rx_unencrypted_eapol(struct net_device *dev, |
| struct iw_request_info *info, struct iw_param *erq, char *buf) |
| { |
| return -EOPNOTSUPP; |
| } |
| |
| static int |
| giwauth_roaming_control(struct net_device *dev, |
| struct iw_request_info *info, struct iw_param *erq, char *buf) |
| { |
| int rc; |
| int arg; |
| |
| arg = IEEE80211_PARAM_ROAMING; |
| rc = ieee80211_ioctl_getparam(dev, NULL, NULL, (char*)&arg); |
| if (rc) |
| return rc; |
| |
| switch(arg) { |
| case IEEE80211_ROAMING_DEVICE: |
| case IEEE80211_ROAMING_AUTO: |
| erq->value = IW_AUTH_ROAMING_ENABLE; |
| break; |
| default: |
| erq->value = IW_AUTH_ROAMING_DISABLE; |
| break; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| giwauth_privacy_invoked(struct net_device *dev, |
| struct iw_request_info *info, struct iw_param *erq, char *buf) |
| { |
| int rc; |
| int arg; |
| arg = IEEE80211_PARAM_PRIVACY; |
| rc = ieee80211_ioctl_getparam(dev, NULL, NULL, (char*)&arg); |
| if (rc) |
| return rc; |
| erq->value = arg; |
| return 0; |
| } |
| |
| static int |
| ieee80211_ioctl_giwauth(struct net_device *dev, |
| struct iw_request_info *info, struct iw_param *erq, char *buf) |
| { |
| int rc = -EOPNOTSUPP; |
| |
| switch(erq->flags & IW_AUTH_INDEX) { |
| case IW_AUTH_WPA_VERSION: |
| rc = giwauth_wpa_version(dev, info, erq, buf); |
| break; |
| case IW_AUTH_CIPHER_PAIRWISE: |
| rc = giwauth_cipher_pairwise(dev, info, erq, buf); |
| break; |
| case IW_AUTH_CIPHER_GROUP: |
| rc = giwauth_cipher_group(dev, info, erq, buf); |
| break; |
| case IW_AUTH_KEY_MGMT: |
| rc = giwauth_key_mgmt(dev, info, erq, buf); |
| break; |
| case IW_AUTH_TKIP_COUNTERMEASURES: |
| rc = giwauth_tkip_countermeasures(dev, info, erq, buf); |
| break; |
| case IW_AUTH_DROP_UNENCRYPTED: |
| rc = giwauth_drop_unencrypted(dev, info, erq, buf); |
| break; |
| case IW_AUTH_80211_AUTH_ALG: |
| rc = giwauth_80211_auth_alg(dev, info, erq, buf); |
| break; |
| case IW_AUTH_WPA_ENABLED: |
| rc = giwauth_wpa_enabled(dev, info, erq, buf); |
| break; |
| case IW_AUTH_RX_UNENCRYPTED_EAPOL: |
| rc = giwauth_rx_unencrypted_eapol(dev, info, erq, buf); |
| break; |
| case IW_AUTH_ROAMING_CONTROL: |
| rc = giwauth_roaming_control(dev, info, erq, buf); |
| break; |
| case IW_AUTH_PRIVACY_INVOKED: |
| rc = giwauth_privacy_invoked(dev, info, erq, buf); |
| break; |
| default: |
| printk(KERN_WARNING "%s: unknown SIOCGIWAUTH flag %d\n", |
| dev->name, erq->flags); |
| break; |
| } |
| |
| return rc; |
| } |
| |
| /* |
| * Retrieve information about a key. Open question: should we allow |
| * callers to retrieve unicast keys based on a supplied MAC address? |
| * The ipw2200 reference implementation doesn't, so we don't either. |
| * |
| * Not currently used |
| */ |
| static int |
| ieee80211_ioctl_giwencodeext(struct net_device *dev, |
| struct iw_request_info *info, struct iw_point *erq, char *extra) |
| { |
| #ifndef IEEE80211_UNUSED_CRYPTO_COMMANDS |
| return -EOPNOTSUPP; |
| #else |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct iw_encode_ext *ext; |
| struct ieee80211_key *wk; |
| int error; |
| int kid; |
| int max_key_len; |
| |
| if (!capable(CAP_NET_ADMIN)) |
| return -EPERM; |
| |
| max_key_len = erq->length - sizeof(*ext); |
| if (max_key_len < 0) |
| return -EINVAL; |
| ext = (struct iw_encode_ext *)extra; |
| |
| error = getiwkeyix(vap, erq, &kid); |
| if (error < 0) |
| return error; |
| |
| wk = &vap->iv_nw_keys[kid]; |
| if (wk->wk_keylen > max_key_len) |
| return -E2BIG; |
| |
| erq->flags = kid+1; |
| memset(ext, 0, sizeof(*ext)); |
| |
| ext->key_len = wk->wk_keylen; |
| memcpy(ext->key, wk->wk_key, wk->wk_keylen); |
| |
| /* flags */ |
| if (wk->wk_flags & IEEE80211_KEY_GROUP) |
| ext->ext_flags |= IW_ENCODE_EXT_GROUP_KEY; |
| |
| /* algorithm */ |
| switch(wk->wk_cipher->ic_cipher) { |
| case IEEE80211_CIPHER_NONE: |
| ext->alg = IW_ENCODE_ALG_NONE; |
| erq->flags |= IW_ENCODE_DISABLED; |
| break; |
| case IEEE80211_CIPHER_WEP: |
| ext->alg = IW_ENCODE_ALG_WEP; |
| break; |
| case IEEE80211_CIPHER_TKIP: |
| ext->alg = IW_ENCODE_ALG_TKIP; |
| break; |
| case IEEE80211_CIPHER_AES_OCB: |
| case IEEE80211_CIPHER_AES_CCM: |
| case IEEE80211_CIPHER_CKIP: |
| ext->alg = IW_ENCODE_ALG_CCMP; |
| break; |
| default: |
| return -EINVAL; |
| } |
| return 0; |
| #endif /* IEEE80211_UNUSED_CRYPTO_COMMANDS */ |
| } |
| |
| static int |
| ieee80211_ioctl_siwencodeext(struct net_device *dev, |
| struct iw_request_info *info, struct iw_point *erq, char *extra) |
| { |
| #ifndef IEEE80211_UNUSED_CRYPTO_COMMANDS |
| return -EOPNOTSUPP; |
| #else |
| struct ieee80211vap *vap = netdev_priv(dev); |
| struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; |
| struct ieee80211req_key kr; |
| int error; |
| int kid; |
| |
| error = getiwkeyix(vap, erq, &kid); |
| if (error < 0) |
| return error; |
| |
| if (ext->key_len > (erq->length - sizeof(struct iw_encode_ext))) |
| return -EINVAL; |
| |
| if (ext->alg == IW_ENCODE_ALG_NONE) { |
| /* convert to the format used by IEEE_80211_IOCTL_DELKEY */ |
| struct ieee80211req_del_key dk; |
| |
| memset(&dk, 0, sizeof(dk)); |
| dk.idk_keyix = kid; |
| memcpy(&dk.idk_macaddr, ext->addr.sa_data, IEEE80211_ADDR_LEN); |
| |
| return ieee80211_ioctl_delkey(dev, NULL, NULL, (char*)&dk); |
| } |
| |
| /* TODO This memcmp for the broadcast address seems hackish, but |
| * mimics what wpa supplicant was doing. The wpa supplicant comments |
| * make it sound like they were having trouble with |
| * IEEE80211_IOCTL_SETKEY and static WEP keys. It might be worth |
| * figuring out what their trouble was so the rest of this function |
| * can be implemented in terms of ieee80211_ioctl_setkey */ |
| if (ext->alg == IW_ENCODE_ALG_WEP && |
| memcmp(ext->addr.sa_data, "\xff\xff\xff\xff\xff\xff", |
| IEEE80211_ADDR_LEN) == 0) { |
| /* convert to the format used by SIOCSIWENCODE. The old |
| * format just had the key in the extra buf, whereas the |
| * new format has the key tacked on to the end of the |
| * iw_encode_ext structure */ |
| struct iw_request_info oldinfo; |
| struct iw_point olderq; |
| char *key; |
| |
| memset(&oldinfo, 0, sizeof(oldinfo)); |
| oldinfo.cmd = SIOCSIWENCODE; |
| oldinfo.flags = info->flags; |
| |
| memset(&olderq, 0, sizeof(olderq)); |
| olderq.flags = erq->flags; |
| olderq.pointer = erq->pointer; |
| olderq.length = ext->key_len; |
| |
| key = (char *)ext->key; |
| |
| return ieee80211_ioctl_siwencode(dev, &oldinfo, &olderq, key); |
| } |
| |
| /* convert to the format used by IEEE_80211_IOCTL_SETKEY */ |
| memset(&kr, 0, sizeof(kr)); |
| |
| switch(ext->alg) { |
| case IW_ENCODE_ALG_WEP: |
| kr.ik_type = IEEE80211_CIPHER_WEP; |
| break; |
| case IW_ENCODE_ALG_TKIP: |
| kr.ik_type = IEEE80211_CIPHER_TKIP; |
| break; |
| case IW_ENCODE_ALG_CCMP: |
| kr.ik_type = IEEE80211_CIPHER_AES_CCM; |
| break; |
| default: |
| printk(KERN_WARNING "%s: unknown algorithm %d\n", |
| dev->name, ext->alg); |
| return -EINVAL; |
| } |
| |
| kr.ik_keyix = kid; |
| |
| if (ext->key_len > sizeof(kr.ik_keydata)) { |
| printk(KERN_WARNING "%s: key size %d is too large\n", |
| dev->name, ext->key_len); |
| return -E2BIG; |
| } |
| memcpy(kr.ik_keydata, ext->key, ext->key_len); |
| kr.ik_keylen = ext->key_len; |
| |
| kr.ik_flags = IEEE80211_KEY_RECV; |
| |
| if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) |
| kr.ik_flags |= IEEE80211_KEY_GROUP; |
| |
| if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { |
| kr.ik_flags |= IEEE80211_KEY_XMIT | IEEE80211_KEY_DEFAULT; |
| memcpy(kr.ik_macaddr, ext->addr.sa_data, IEEE80211_ADDR_LEN); |
| } |
| |
| if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) { |
| memcpy(&kr.ik_keyrsc, ext->rx_seq, sizeof(kr.ik_keyrsc)); |
| } |
| |
| return ieee80211_ioctl_setkey(dev, NULL, NULL, (char*)&kr); |
| #endif /* IEEE80211_UNUSED_CRYPTO_COMMANDS */ |
| } |
| |
| #define IW_PRIV_TYPE_OPTIE IW_PRIV_TYPE_BYTE | IEEE80211_MAX_OPT_IE |
| #define IW_PRIV_TYPE_KEY \ |
| IW_PRIV_TYPE_BYTE | sizeof(struct ieee80211req_key) |
| #define IW_PRIV_TYPE_DELKEY \ |
| IW_PRIV_TYPE_BYTE | sizeof(struct ieee80211req_del_key) |
| #define IW_PRIV_TYPE_MLME \ |
| IW_PRIV_TYPE_BYTE | sizeof(struct ieee80211req_mlme) |
| #define IW_PRIV_TYPE_CHANLIST \ |
| IW_PRIV_TYPE_BYTE | sizeof(struct ieee80211req_chanlist) |
| #define IW_PRIV_TYPE_CHANINFO \ |
| IW_PRIV_TYPE_BYTE | sizeof(struct ieee80211req_chaninfo) |
| #define IW_PRIV_TYPE_APPIEBUF \ |
| (IW_PRIV_TYPE_BYTE | (sizeof(struct ieee80211req_getset_appiebuf) + IEEE80211_APPIE_MAX)) |
| #define IW_PRIV_TYPE_FILTER \ |
| IW_PRIV_TYPE_BYTE | sizeof(struct ieee80211req_set_filter) |
| #define IW_PRIV_TYPE_CCA \ |
| (IW_PRIV_TYPE_BYTE | sizeof(struct qtn_cca_args)) |
| #if defined(CONFIG_QTN_80211K_SUPPORT) |
| #define IW_PRIV_TYPE_STASTATISTIC_SETPARA \ |
| (IW_PRIV_TYPE_BYTE | sizeof(struct ieee80211req_qtn_rmt_sta_stats_setpara)) |
| #define IW_PRIV_TYPE_STASTATISTIC_GETPARA \ |
| (IW_PRIV_TYPE_BYTE | sizeof(struct ieee80211req_qtn_rmt_sta_stats)) |
| #endif |
| |
| /* make sure the size of each block_data member is large then IFNAMSIZ */ |
| union block_data { |
| struct ieee80211req_csw_record record; |
| struct ieee80211_assoc_history assoc_history; |
| uint32_t pm_state[QTN_PM_IOCTL_MAX]; |
| }; |
| #define IW_PRIV_BLOCK_DATASIZE (sizeof(union block_data)) |
| |
| static const struct iw_priv_args ieee80211_priv_args[] = |
| { |
| /* NB: setoptie & getoptie are !IW_PRIV_SIZE_FIXED */ |
| { IEEE80211_IOCTL_SETOPTIE, |
| IW_PRIV_TYPE_OPTIE, 0, "setoptie" }, |
| { IEEE80211_IOCTL_GETOPTIE, |
| 0, IW_PRIV_TYPE_OPTIE, "getoptie" }, |
| { IEEE80211_IOCTL_SETKEY, |
| IW_PRIV_TYPE_KEY | IW_PRIV_SIZE_FIXED, 0, "setkey" }, |
| { IEEE80211_IOCTL_DELKEY, |
| IW_PRIV_TYPE_DELKEY | IW_PRIV_SIZE_FIXED, 0, "delkey" }, |
| { IEEE80211_IOCTL_SETMLME, |
| IW_PRIV_TYPE_MLME | IW_PRIV_SIZE_FIXED, 0, "setmlme" }, |
| { IEEE80211_IOCTL_ADDMAC, |
| IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,"setbssid" }, |
| { IEEE80211_IOCTL_DELMAC, |
| IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,"delmac" }, |
| { IEEE80211_IOCTL_KICKMAC, |
| IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "kickmac"}, |
| { IEEE80211_IOCTL_WDSADDMAC, |
| IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,"wds_add" }, |
| { IEEE80211_IOCTL_WDSDELMAC, |
| IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,"wds_del" }, |
| { IEEE80211_IOCTL_SETCHANLIST, |
| IW_PRIV_TYPE_CHANLIST | IW_PRIV_SIZE_FIXED, 0,"setchanlist" }, |
| { IEEE80211_IOCTL_GETCHANLIST, |
| 0, IW_PRIV_TYPE_CHANLIST | IW_PRIV_SIZE_FIXED,"getchanlist" }, |
| { IEEE80211_IOCTL_STARTCCA, |
| IW_PRIV_TYPE_CCA | IW_PRIV_SIZE_FIXED | 1, 0, "startcca" }, |
| { IEEE80211_IOCTL_GETCHANINFO, |
| 0, IW_PRIV_TYPE_CHANINFO | IW_PRIV_SIZE_FIXED,"getchaninfo" }, |
| { IEEE80211_IOCTL_SETMODE, |
| IW_PRIV_TYPE_CHAR | 12, 0, "mode" }, |
| { IEEE80211_IOCTL_GETMODE, |
| 0, IW_PRIV_TYPE_CHAR | 6, "get_mode" }, |
| { IEEE80211_IOCTL_POSTEVENT, |
| IW_PRIV_TYPE_CHAR | 256, 0, "postevent" }, |
| { IEEE80211_IOCTL_TXEAPOL, |
| IW_PRIV_TYPE_BYTE | 2047, 0, "txeapol" }, |
| { IEEE80211_IOCTL_SETWMMPARAMS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 4, 0,"setwmmparams" }, |
| { IEEE80211_IOCTL_GETWMMPARAMS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getwmmparams" }, |
| { IEEE80211_IOCTL_RADAR, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "doth_radar" }, |
| { IEEE80211_IOCTL_DFSACTSCAN, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dfsactscan" }, |
| #if defined(CONFIG_QTN_80211K_SUPPORT) |
| { IEEE80211_IOCTL_GETSTASTATISTIC, |
| IW_PRIV_TYPE_STASTATISTIC_SETPARA, |
| IW_PRIV_TYPE_STASTATISTIC_GETPARA | IW_PRIV_SIZE_FIXED, "getstastatistic" }, |
| #endif |
| /* |
| * These depends on sub-ioctl support which added in version 12. |
| */ |
| { IEEE80211_IOCTL_SETWMMPARAMS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3, 0,"" }, |
| { IEEE80211_IOCTL_GETWMMPARAMS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "" }, |
| /* sub-ioctl handlers */ |
| { IEEE80211_WMMPARAMS_CWMIN, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3, 0,"cwmin" }, |
| { IEEE80211_WMMPARAMS_CWMIN, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_cwmin" }, |
| { IEEE80211_WMMPARAMS_CWMAX, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3, 0,"cwmax" }, |
| { IEEE80211_WMMPARAMS_CWMAX, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_cwmax" }, |
| { IEEE80211_WMMPARAMS_AIFS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3, 0,"aifs" }, |
| { IEEE80211_WMMPARAMS_AIFS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_aifs" }, |
| { IEEE80211_WMMPARAMS_TXOPLIMIT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3, 0,"txoplimit" }, |
| { IEEE80211_WMMPARAMS_TXOPLIMIT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_txoplimit" }, |
| { IEEE80211_WMMPARAMS_ACM, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3, 0,"acm" }, |
| { IEEE80211_WMMPARAMS_ACM, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_acm" }, |
| { IEEE80211_WMMPARAMS_NOACKPOLICY, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3, 0,"noackpolicy" }, |
| { IEEE80211_WMMPARAMS_NOACKPOLICY, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_noackpolicy" }, |
| |
| { IEEE80211_IOCTL_SETPARAM, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "setparam" }, |
| /* |
| * These depends on sub-ioctl support which added in version 12. |
| */ |
| { IEEE80211_IOCTL_GETPARAM, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getparam" }, |
| |
| /* sub-ioctl handlers */ |
| { IEEE80211_IOCTL_SETPARAM, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "" }, |
| { IEEE80211_IOCTL_GETPARAM, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "" }, |
| |
| /* sub-ioctl definitions */ |
| { IEEE80211_PARAM_AUTHMODE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "authmode" }, |
| { IEEE80211_PARAM_AUTHMODE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_authmode" }, |
| { IEEE80211_PARAM_PROTMODE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "protmode" }, |
| { IEEE80211_PARAM_PROTMODE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_protmode" }, |
| { IEEE80211_PARAM_MCASTCIPHER, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mcastcipher" }, |
| { IEEE80211_PARAM_MCASTCIPHER, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_mcastcipher" }, |
| { IEEE80211_PARAM_MCASTKEYLEN, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mcastkeylen" }, |
| { IEEE80211_PARAM_MCASTKEYLEN, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_mcastkeylen" }, |
| { IEEE80211_PARAM_UCASTCIPHERS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ucastciphers" }, |
| { IEEE80211_PARAM_UCASTCIPHERS, |
| /* |
| * NB: can't use "get_ucastciphers" due to iwpriv command names |
| * must be <IFNAMESIZ which is 16. |
| */ |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_uciphers" }, |
| { IEEE80211_PARAM_UCASTCIPHER, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ucastcipher" }, |
| { IEEE80211_PARAM_UCASTCIPHER, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_ucastcipher" }, |
| { IEEE80211_PARAM_UCASTKEYLEN, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ucastkeylen" }, |
| { IEEE80211_PARAM_UCASTKEYLEN, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_ucastkeylen" }, |
| { IEEE80211_PARAM_KEYMGTALGS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "keymgtalgs" }, |
| { IEEE80211_PARAM_KEYMGTALGS, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_keymgtalgs" }, |
| { IEEE80211_PARAM_RSNCAPS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "rsncaps" }, |
| { IEEE80211_PARAM_RSNCAPS, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_rsncaps" }, |
| { IEEE80211_PARAM_ROAMING, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "hostroaming" }, |
| { IEEE80211_PARAM_ROAMING, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_hostroaming" }, |
| { IEEE80211_PARAM_PRIVACY, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "privacy" }, |
| { IEEE80211_PARAM_PRIVACY, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_privacy" }, |
| { IEEE80211_PARAM_COUNTERMEASURES, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "countermeasures" }, |
| { IEEE80211_PARAM_COUNTERMEASURES, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_countermeas" }, |
| { IEEE80211_PARAM_DROPUNENCRYPTED, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dropunencrypted" }, |
| { IEEE80211_PARAM_DROPUNENCRYPTED, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_dropunencry" }, |
| { IEEE80211_PARAM_WPA, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wpa" }, |
| { IEEE80211_PARAM_WPA, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_wpa" }, |
| { IEEE80211_PARAM_DRIVER_CAPS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "driver_caps" }, |
| { IEEE80211_PARAM_DRIVER_CAPS, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_driver_caps" }, |
| { IEEE80211_PARAM_WMM, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wmm" }, |
| { IEEE80211_PARAM_WMM, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_wmm" }, |
| { IEEE80211_PARAM_HIDESSID, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "hide_ssid" }, |
| { IEEE80211_PARAM_HIDESSID, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_hide_ssid" }, |
| { IEEE80211_PARAM_APBRIDGE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ap_bridge" }, |
| { IEEE80211_PARAM_APBRIDGE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_ap_bridge" }, |
| { IEEE80211_PARAM_INACT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "inact" }, |
| { IEEE80211_PARAM_INACT, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_inact" }, |
| { IEEE80211_PARAM_INACT_AUTH, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "inact_auth" }, |
| { IEEE80211_PARAM_INACT_AUTH, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_inact_auth" }, |
| { IEEE80211_PARAM_INACT_INIT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "inact_init" }, |
| { IEEE80211_PARAM_INACT_INIT, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_inact_init" }, |
| { IEEE80211_PARAM_ABOLT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "abolt" }, |
| { IEEE80211_PARAM_ABOLT, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_abolt" }, |
| { IEEE80211_PARAM_DTIM_PERIOD, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dtim_period" }, |
| { IEEE80211_PARAM_DTIM_PERIOD, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_dtim_period" }, |
| { IEEE80211_PARAM_ASSOC_LIMIT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "assoc_limit" }, |
| { IEEE80211_PARAM_ASSOC_LIMIT, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_assoc_limit" }, |
| { IEEE80211_PARAM_BSS_ASSOC_LIMIT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bss_assoc_limit" }, |
| { IEEE80211_PARAM_BSS_ASSOC_LIMIT, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_bss_assolmt" }, |
| /* XXX bintval chosen to avoid 16-char limit */ |
| { IEEE80211_PARAM_BEACON_INTERVAL, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bintval" }, |
| { IEEE80211_PARAM_BEACON_INTERVAL, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_bintval" }, |
| { IEEE80211_PARAM_DOTH, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "doth" }, |
| { IEEE80211_PARAM_DOTH, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_doth" }, |
| { IEEE80211_PARAM_PWRCONSTRAINT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "doth_pwrcst" }, |
| { IEEE80211_PARAM_PWRCONSTRAINT, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_doth_pwrcst" }, |
| { IEEE80211_PARAM_GENREASSOC, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "doth_reassoc" }, |
| #ifdef MATS |
| { IEEE80211_PARAM_COMPRESSION, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "compression" }, |
| { IEEE80211_PARAM_COMPRESSION,0, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_compression" }, |
| { IEEE80211_PARAM_FF, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ff" }, |
| { IEEE80211_PARAM_FF,0, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_ff" }, |
| { IEEE80211_PARAM_TURBO, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "turbo" }, |
| { IEEE80211_PARAM_TURBO,0, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_turbo" }, |
| { IEEE80211_PARAM_XR, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "xr" }, |
| { IEEE80211_PARAM_XR,0, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_xr" }, |
| { IEEE80211_PARAM_BURST, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "burst" }, |
| { IEEE80211_PARAM_BURST,0, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_burst" }, |
| #endif |
| { IEEE80211_IOCTL_CHANSWITCH, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "doth_chanswitch" }, |
| { IEEE80211_PARAM_PUREG, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "pureg" }, |
| { IEEE80211_PARAM_PUREG,0, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_pureg" }, |
| { IEEE80211_PARAM_REPEATER, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "repeater" }, |
| { IEEE80211_PARAM_REPEATER,0, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_repeater" }, |
| { IEEE80211_PARAM_WDS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wds" }, |
| { IEEE80211_PARAM_WDS,0, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_wds" }, |
| { IEEE80211_PARAM_BGSCAN, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bgscan" }, |
| { IEEE80211_PARAM_BGSCAN,0, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_bgscan" }, |
| { IEEE80211_PARAM_BGSCAN_IDLE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bgscanidle" }, |
| { IEEE80211_PARAM_BGSCAN_IDLE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_bgscanidle" }, |
| { IEEE80211_PARAM_BGSCAN_INTERVAL, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bgscanintvl" }, |
| { IEEE80211_PARAM_BGSCAN_INTERVAL, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_bgscanintvl" }, |
| { IEEE80211_PARAM_MCAST_RATE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mcast_rate" }, |
| { IEEE80211_PARAM_MCAST_RATE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_mcast_rate" }, |
| { IEEE80211_PARAM_COVERAGE_CLASS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "coverageclass" }, |
| { IEEE80211_PARAM_COVERAGE_CLASS, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_coveragecls" }, |
| { IEEE80211_PARAM_COUNTRY_IE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "countryie" }, |
| { IEEE80211_PARAM_COUNTRY_IE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_countryie" }, |
| { IEEE80211_PARAM_SCANVALID, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "scanvalid" }, |
| { IEEE80211_PARAM_SCANVALID, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_scanvalid" }, |
| { IEEE80211_PARAM_REGCLASS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "regclass" }, |
| { IEEE80211_PARAM_REGCLASS, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_regclass" }, |
| { IEEE80211_PARAM_DROPUNENC_EAPOL, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dropunenceapol" }, |
| { IEEE80211_PARAM_DROPUNENC_EAPOL, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_dropunencea" }, |
| { IEEE80211_PARAM_SHPREAMBLE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "shpreamble" }, |
| { IEEE80211_PARAM_SHPREAMBLE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_shpreamble" }, |
| /* |
| * NB: these should be roamrssi* etc, but iwpriv usurps all |
| * strings that start with roam! |
| */ |
| { IEEE80211_PARAM_ROAM_RSSI_11A, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "rssi11a" }, |
| { IEEE80211_PARAM_ROAM_RSSI_11A, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_rssi11a" }, |
| { IEEE80211_PARAM_ROAM_RSSI_11B, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "rssi11b" }, |
| { IEEE80211_PARAM_ROAM_RSSI_11B, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_rssi11b" }, |
| { IEEE80211_PARAM_ROAM_RSSI_11G, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "rssi11g" }, |
| { IEEE80211_PARAM_ROAM_RSSI_11G, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_rssi11g" }, |
| { IEEE80211_PARAM_ROAM_RATE_11A, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "rate11a" }, |
| { IEEE80211_PARAM_ROAM_RATE_11A, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_rate11a" }, |
| { IEEE80211_PARAM_ROAM_RATE_11B, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "rate11b" }, |
| { IEEE80211_PARAM_ROAM_RATE_11B, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_rate11b" }, |
| { IEEE80211_PARAM_ROAM_RATE_11G, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "rate11g" }, |
| { IEEE80211_PARAM_ROAM_RATE_11G, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_rate11g" }, |
| { IEEE80211_PARAM_UAPSDINFO, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "uapsd" }, |
| { IEEE80211_PARAM_UAPSDINFO, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_uapsd" }, |
| { IEEE80211_PARAM_SLEEP, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "sleep" }, |
| { IEEE80211_PARAM_SLEEP, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_sleep" }, |
| { IEEE80211_PARAM_QOSNULL, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "qosnull" }, |
| { IEEE80211_PARAM_PSPOLL, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "pspoll" }, |
| { IEEE80211_PARAM_EOSPDROP, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "eospdrop" }, |
| { IEEE80211_PARAM_EOSPDROP, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_eospdrop" }, |
| { IEEE80211_PARAM_STA_DFS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "sta_dfs"}, |
| { IEEE80211_PARAM_STA_DFS, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_sta_dfs"}, |
| { IEEE80211_PARAM_MARKDFS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "markdfs" }, |
| { IEEE80211_PARAM_MARKDFS, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_markdfs" }, |
| { IEEE80211_PARAM_RADAR_BW, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "radar_bw" }, |
| { IEEE80211_IOCTL_SET_APPIEBUF, |
| IW_PRIV_TYPE_APPIEBUF, 0, "setiebuf" }, |
| { IEEE80211_IOCTL_GET_APPIEBUF, |
| 0, IW_PRIV_TYPE_APPIEBUF, "getiebuf" }, |
| { IEEE80211_IOCTL_FILTERFRAME, |
| IW_PRIV_TYPE_FILTER , 0, "setfilter" }, |
| { IEEE80211_PARAM_FIXED_TX_RATE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "fixedtxrate" }, |
| { IEEE80211_PARAM_FIXED_TX_RATE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_fixedtxrate" }, |
| { IEEE80211_PARAM_MIMOMODE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mimomode" }, |
| { IEEE80211_PARAM_MIMOMODE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_mimomode" }, |
| { IEEE80211_PARAM_AGGREGATION, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "aggregation" }, |
| { IEEE80211_PARAM_AGGREGATION, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_aggregation" }, |
| { IEEE80211_PARAM_RETRY_COUNT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "retrycount" }, |
| { IEEE80211_PARAM_RETRY_COUNT, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_retrycount" }, |
| { IEEE80211_PARAM_VAP_DBG, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "vapdebug" }, |
| { IEEE80211_PARAM_VAP_DBG, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_vapdebug" }, |
| { IEEE80211_PARAM_NODEREF_DBG, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "noderef_dbg" }, |
| { IEEE80211_PARAM_EXP_MAT_SEL, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "expmattype" }, |
| { IEEE80211_PARAM_EXP_MAT_SEL, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_expmattype" }, |
| { IEEE80211_PARAM_BW_SEL, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bwselect" }, |
| { IEEE80211_PARAM_BW_SEL, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_bwselect" }, |
| { IEEE80211_PARAM_RG, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "rgselect" }, |
| { IEEE80211_PARAM_RG, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_rgselect" }, |
| { IEEE80211_PARAM_BW_SEL_MUC, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bwselect_muc" }, |
| { IEEE80211_PARAM_BW_SEL_MUC, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_bwselect_muc" }, |
| { IEEE80211_PARAM_ACK_POLICY, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ackpolicy" }, |
| { IEEE80211_PARAM_ACK_POLICY, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_ackpolicy" }, |
| { IEEE80211_PARAM_LEGACY_MODE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "legacyselect" }, |
| { IEEE80211_PARAM_LEGACY_MODE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_legacyselect" }, |
| { IEEE80211_PARAM_MAX_AGG_SIZE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "max_aggsize" }, |
| { IEEE80211_PARAM_MAX_AGG_SIZE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_maxaggsize" }, |
| { IEEE80211_PARAM_TXBF_CTRL, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "txbf_ctrl" }, |
| { IEEE80211_PARAM_TXBF_CTRL, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_txbfctrl" }, |
| { IEEE80211_PARAM_TXBF_PERIOD, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "txbf_period" }, |
| { IEEE80211_PARAM_TXBF_PERIOD, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_txbfperiod" }, |
| { IEEE80211_PARAM_HTBA_SEQ_CTRL, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "htba_seq" }, |
| { IEEE80211_PARAM_HTBA_SEQ_CTRL, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_htba_seq" }, |
| { IEEE80211_PARAM_HTBA_SIZE_CTRL, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "htba_size" }, |
| { IEEE80211_PARAM_HTBA_SIZE_CTRL, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_htba_size" }, |
| { IEEE80211_PARAM_HTBA_TIME_CTRL, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "htba_time" }, |
| { IEEE80211_PARAM_HTBA_TIME_CTRL, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_htba_time" }, |
| { IEEE80211_PARAM_HT_ADDBA, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "htba_addba" }, |
| { IEEE80211_PARAM_HT_ADDBA, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_htba_addba" }, |
| { IEEE80211_PARAM_HT_DELBA, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "htba_delba" }, |
| { IEEE80211_PARAM_HT_DELBA, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_htba_delba" }, |
| { IEEE80211_PARAM_CHANNEL_NOSCAN, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "disablescan" }, |
| { IEEE80211_PARAM_CHANNEL_NOSCAN, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_disablescan" }, |
| { IEEE80211_PARAM_MUC_PROFILE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "muc_profile" }, |
| { IEEE80211_PARAM_MUC_PROFILE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "muc_profile" }, |
| { IEEE80211_PARAM_MUC_PHY_STATS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "muc_set_phystat" }, |
| { IEEE80211_PARAM_MUC_PHY_STATS, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "muc_get_phystat" }, |
| { IEEE80211_PARAM_MUC_SET_PARTNUM, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "muc_set_partnum" }, |
| { IEEE80211_PARAM_MUC_SET_PARTNUM, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "muc_get_partnum" }, |
| { IEEE80211_PARAM_ENABLE_GAIN_ADAPT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ena_gain_adapt" }, |
| { IEEE80211_PARAM_ENABLE_GAIN_ADAPT, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_gain_adapt" }, |
| { IEEE80211_PARAM_GET_RFCHIP_ID, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "rfchipid" }, |
| { IEEE80211_PARAM_GET_RFCHIP_ID, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_rfchipid" }, |
| { IEEE80211_PARAM_GET_RFCHIP_VERID, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "rfchip_verid" }, |
| { IEEE80211_PARAM_GET_RFCHIP_VERID, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_rfchip_verid" }, |
| { IEEE80211_PARAM_SHORT_GI, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "short_gi" }, |
| { IEEE80211_PARAM_SHORT_GI, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_short_gi" }, |
| { IEEE80211_PARAM_FORCE_SMPS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "forcesmps" }, |
| { IEEE80211_PARAM_FORCEMICERROR, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "forcemicerror" }, |
| { IEEE80211_PARAM_ENABLECOUNTERMEASURES, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "en_cmsures" }, |
| { IEEE80211_PARAM_IMPLICITBA, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "implicit_ba" }, |
| { IEEE80211_PARAM_IMPLICITBA, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_implicit_ba" }, |
| { IEEE80211_PARAM_CLIENT_REMOVE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "cl_remove" }, |
| { IEEE80211_PARAM_SHOWMEM, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "showmem" }, |
| { IEEE80211_PARAM_SCANSTATUS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "scanstatus" }, |
| { IEEE80211_PARAM_SCANSTATUS, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_scanstatus" }, |
| { IEEE80211_PARAM_CACSTATUS, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_cacstatus" }, |
| { IEEE80211_PARAM_GLOBAL_BA_CONTROL, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ba_control" }, |
| { IEEE80211_PARAM_GLOBAL_BA_CONTROL, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_ba_control" }, |
| { IEEE80211_PARAM_NO_SSID_ASSOC, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "no_ssid_assoc" }, |
| { IEEE80211_PARAM_CONFIG_TXPOWER, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "cfg_txpower" }, |
| { IEEE80211_PARAM_INITIATE_TXPOWER_TABLE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "init_txpower" }, |
| { IEEE80211_PARAM_CONFIG_BW_TXPOWER, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "cfg_bw_power" }, |
| { IEEE80211_PARAM_CONFIG_TPC_INTERVAL, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_tpc_intvl" }, |
| { IEEE80211_PARAM_CONFIG_TPC_INTERVAL, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "tpc_interval" }, |
| { IEEE80211_PARAM_TPC, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_tpc" }, |
| { IEEE80211_PARAM_TPC, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "tpc" }, |
| { IEEE80211_PARAM_TPC_QUERY, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "tpc_query" }, |
| { IEEE80211_PARAM_TPC_QUERY, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_tpc_query" }, |
| { IEEE80211_PARAM_CONFIG_REGULATORY_TXPOWER, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "cfg_reg_txpower" }, |
| { IEEE80211_PARAM_SKB_LIST_MAX, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "skb_list_max" }, |
| { IEEE80211_PARAM_VAP_STATS, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "vapstats" }, |
| { IEEE80211_PARAM_RATE_CTRL_FLAGS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "rate_ctrl_flags" }, |
| { IEEE80211_PARAM_LDPC, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_ldpc" }, |
| { IEEE80211_PARAM_LDPC, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_ldpc" }, |
| { IEEE80211_PARAM_DFS_FAST_SWITCH, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dfs_fast_switch" }, |
| { IEEE80211_PARAM_DFS_FAST_SWITCH, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_dfs_switch" }, |
| { IEEE80211_PARAM_BLACKLIST_GET, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_blacklist" }, |
| { IEEE80211_PARAM_SCAN_NO_DFS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "scan_no_dfs" }, |
| { IEEE80211_PARAM_SCAN_NO_DFS, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_scan_dfs" }, |
| { IEEE80211_PARAM_SAMPLE_RATE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "sample_rate" }, |
| { IEEE80211_PARAM_SAMPLE_RATE, 0, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_sample_rate" }, |
| { IEEE80211_PARAM_11N_40_ONLY_MODE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_40only_mode" }, |
| { IEEE80211_PARAM_11N_40_ONLY_MODE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_40only_mode" }, |
| { IEEE80211_PARAM_AMPDU_DENSITY, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_ampdu_dens" }, |
| { IEEE80211_PARAM_AMPDU_DENSITY, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_ampdu_dens" }, |
| { IEEE80211_PARAM_REGULATORY_REGION, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "region" }, |
| { IEEE80211_PARAM_REGULATORY_REGION, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_region" }, |
| { IEEE80211_PARAM_SPEC_COUNTRY_CODE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "country_code" }, |
| { IEEE80211_PARAM_SPEC_COUNTRY_CODE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_country_code" }, |
| { IEEE80211_PARAM_MCS_CAP, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mcs_cap" }, |
| { IEEE80211_PARAM_MCS_CAP, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_mcs_cap" }, |
| { IEEE80211_PARAM_MAX_MGMT_FRAMES, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "max_mgmtfrms" }, |
| { IEEE80211_PARAM_MAX_MGMT_FRAMES, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_max_mgtfrms" }, |
| { IEEE80211_PARAM_MCS_ODD_EVEN, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mcs_odd_even" }, |
| { IEEE80211_PARAM_MCS_ODD_EVEN, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_mcs_oddeven" }, |
| { IEEE80211_PARAM_BA_MAX_WIN_SIZE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ba_max" }, |
| { IEEE80211_PARAM_BA_MAX_WIN_SIZE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_ba_max" }, |
| { IEEE80211_PARAM_RESTRICTED_MODE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "tx_restrict" }, |
| { IEEE80211_PARAM_RESTRICTED_MODE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_tx_restrict" }, |
| { IEEE80211_PARAM_PHY_STATS_MODE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mode_phy_stats" }, |
| { IEEE80211_PARAM_MIN_DWELL_TIME_ACTIVE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "min_dt_act" }, |
| { IEEE80211_PARAM_MIN_DWELL_TIME_ACTIVE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_min_dt_act" }, |
| { IEEE80211_PARAM_MIN_DWELL_TIME_PASSIVE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "min_dt_pas" }, |
| { IEEE80211_PARAM_MIN_DWELL_TIME_PASSIVE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_min_dt_pas" }, |
| { IEEE80211_PARAM_MAX_DWELL_TIME_ACTIVE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "max_dt_act" }, |
| { IEEE80211_PARAM_MAX_DWELL_TIME_ACTIVE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_max_dt_act" }, |
| { IEEE80211_PARAM_MAX_DWELL_TIME_PASSIVE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "max_dt_pas" }, |
| { IEEE80211_PARAM_MAX_DWELL_TIME_PASSIVE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_max_dt_pas" }, |
| #ifdef QTN_BG_SCAN |
| { IEEE80211_PARAM_QTN_BGSCAN_DWELL_TIME_ACTIVE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bg_dt_act" }, |
| { IEEE80211_PARAM_QTN_BGSCAN_DWELL_TIME_ACTIVE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_bg_dt_act" }, |
| { IEEE80211_PARAM_QTN_BGSCAN_DWELL_TIME_PASSIVE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bg_dt_pas" }, |
| { IEEE80211_PARAM_QTN_BGSCAN_DWELL_TIME_PASSIVE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_bg_dt_pas" }, |
| { IEEE80211_PARAM_QTN_BGSCAN_DURATION_ACTIVE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bg_dur_act" }, |
| { IEEE80211_PARAM_QTN_BGSCAN_DURATION_ACTIVE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_bg_dur_act" }, |
| { IEEE80211_PARAM_QTN_BGSCAN_DURATION_PASSIVE_FAST, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bg_dur_pf" }, |
| { IEEE80211_PARAM_QTN_BGSCAN_DURATION_PASSIVE_FAST, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_bg_dur_pf" }, |
| { IEEE80211_PARAM_QTN_BGSCAN_DURATION_PASSIVE_NORMAL, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bg_dur_pn" }, |
| { IEEE80211_PARAM_QTN_BGSCAN_DURATION_PASSIVE_NORMAL, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_bg_dur_pn" }, |
| { IEEE80211_PARAM_QTN_BGSCAN_DURATION_PASSIVE_SLOW, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bg_dur_ps" }, |
| { IEEE80211_PARAM_QTN_BGSCAN_DURATION_PASSIVE_SLOW, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_bg_dur_ps" }, |
| { IEEE80211_PARAM_QTN_BGSCAN_THRSHLD_PASSIVE_FAST, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bg_thr_fst" }, |
| { IEEE80211_PARAM_QTN_BGSCAN_THRSHLD_PASSIVE_FAST, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_bg_thr_fst" }, |
| { IEEE80211_PARAM_QTN_BGSCAN_THRSHLD_PASSIVE_NORMAL, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bg_thr_nor" }, |
| { IEEE80211_PARAM_QTN_BGSCAN_THRSHLD_PASSIVE_NORMAL, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_bg_thr_nor" }, |
| { IEEE80211_PARAM_QTN_BGSCAN_DEBUG, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bg_debug" }, |
| { IEEE80211_PARAM_QTN_BGSCAN_DEBUG, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_bg_debug" }, |
| #endif /* QTN_BG_SCAN */ |
| { IEEE80211_PARAM_LEGACY_RETRY_LIMIT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "legacy_retry" }, |
| { IEEE80211_PARAM_LEGACY_RETRY_LIMIT, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_legacyretry" }, |
| #ifdef QSCS_ENABLED |
| { IEEE80211_PARAM_SCS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "scs_set" }, |
| { IEEE80211_PARAM_SCS, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "scs_get" }, |
| { IEEE80211_PARAM_SCS_DFS_REENTRY_REQUEST, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "scs_get_reent" }, |
| #endif /* QSCS_ENABLED */ |
| { IEEE80211_PARAM_TRAINING_COUNT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "training_count" }, |
| { IEEE80211_PARAM_DYNAMIC_AC, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dynamic_ac" }, |
| { IEEE80211_PARAM_DUMP_TRIGGER, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dbg_dump" }, |
| { IEEE80211_PARAM_DUMP_TCM_FD, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dbg_dump_tcm_fd" }, |
| { IEEE80211_PARAM_RXCSR_ERR_ALLOW, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "rxcsr_err_allow" }, |
| { IEEE80211_PARAM_STOP_FLAGS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dbg_stop" }, |
| { IEEE80211_PARAM_CHECK_FLAGS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dbg_check" }, |
| { IEEE80211_PARAM_RX_CTRL_FILTER, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ctrl_filter" }, |
| { IEEE80211_PARAM_ALT_CHAN, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_alt_chan" }, |
| { IEEE80211_PARAM_ALT_CHAN, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_alt_chan" }, |
| { IEEE80211_PARAM_QTN_BCM_WAR, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bcm_fixup" }, |
| { IEEE80211_PARAM_GI_SELECT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "gi_select" }, |
| { IEEE80211_PARAM_GI_SELECT, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_gi_select" }, |
| { IEEE80211_PARAM_FIXED_SGI, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "gi_fixed" }, |
| { IEEE80211_PARAM_FIXED_SGI, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_gi_fixed" }, |
| { IEEE80211_PARAM_FIXED_BW, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bw_fixed" }, |
| { IEEE80211_PARAM_FIXED_BW, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_bw_fixed" }, |
| { IEEE80211_PARAM_LDPC_ALLOW_NON_QTN, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_ldpc_nonqtn" }, |
| { IEEE80211_PARAM_LDPC_ALLOW_NON_QTN, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_ldpc_nonqtn" }, |
| { IEEE80211_PARAM_FWD_UNKNOWN_MC, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "fwd_unknown_mc" }, |
| { IEEE80211_PARAM_FWD_UNKNOWN_MC, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_unknown_mc" }, |
| { IEEE80211_PARAM_BCST_4, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "reliable_bc" }, |
| { IEEE80211_PARAM_BCST_4, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_reliable_bc" }, |
| { IEEE80211_PARAM_AP_FWD_LNCB, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ap_fwd_lncb" }, |
| { IEEE80211_PARAM_AP_FWD_LNCB, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_ap_fwd_lncb" }, |
| { IEEE80211_PARAM_PPPC_SELECT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "pppc" }, |
| { IEEE80211_PARAM_PPPC_SELECT, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_pppc" }, |
| { IEEE80211_PARAM_PPPC_STEP, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "pppc_step" }, |
| { IEEE80211_PARAM_PPPC_STEP, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_pppc_step" }, |
| { IEEE80211_PARAM_TEST_LNCB, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "test_lncb" }, |
| { IEEE80211_PARAM_STBC, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_stbc" }, |
| { IEEE80211_PARAM_STBC, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_stbc" }, |
| { IEEE80211_PARAM_RTS_CTS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_rtscts" }, |
| { IEEE80211_PARAM_RTS_CTS, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_rtscts" }, |
| { IEEE80211_PARAM_TX_QOS_SCHED, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_txqos_sched" }, |
| { IEEE80211_PARAM_TX_QOS_SCHED, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_txqos_sched" }, |
| { IEEE80211_PARAM_GET_DFS_CCE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_dfs_cce" }, |
| #ifdef QSCS_ENABLED |
| { IEEE80211_PARAM_GET_SCS_CCE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_scs_cce" }, |
| #endif /* QSCS_ENABLED */ |
| { IEEE80211_PARAM_RX_AGG_TIMEOUT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "rx_agg_to" }, |
| { IEEE80211_PARAM_RX_AGG_TIMEOUT, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_rx_agg_to" }, |
| { IEEE80211_PARAM_FORCE_MUC_HALT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "muc_halt" }, |
| { IEEE80211_PARAM_FORCE_ENABLE_TRIGGERS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "tr_trig" }, |
| { IEEE80211_PARAM_FORCE_MUC_TRACE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "muc_tb" }, |
| { IEEE80211_PARAM_BK_BITMAP_MODE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_bkbitmap" }, |
| { IEEE80211_PARAM_MUC_FLAGS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "muc_flags" }, |
| { IEEE80211_PARAM_HT_NSS_CAP, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_ht_nss_cap" }, |
| { IEEE80211_PARAM_HT_NSS_CAP, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_ht_nss_cap" }, |
| { IEEE80211_PARAM_VHT_NSS_CAP, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_vht_nss_cap" }, |
| { IEEE80211_PARAM_VHT_NSS_CAP, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_vht_nss_cap" }, |
| { IEEE80211_PARAM_UNKNOWN_DEST_ARP, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "unknown_dst_arp" }, |
| { IEEE80211_PARAM_UNKNOWN_DEST_FWD, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "unknown_dst_fwd" }, |
| { IEEE80211_PARAM_ASSOC_HISTORY, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "reset_assoc_his" }, |
| { IEEE80211_PARAM_CSW_RECORD, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "clean_csw" }, |
| |
| { IEEE80211_PARAM_UPDATE_MU_GRP, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mu_grp_upd" }, |
| { IEEE80211_PARAM_FIXED_11AC_MU_TX_RATE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mu_tx_rate_set" }, |
| { IEEE80211_PARAM_MU_DEBUG_LEVEL, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mu_dbg_lvl_set" }, |
| { IEEE80211_PARAM_MU_ENABLE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mu_enable_set" }, |
| { IEEE80211_PARAM_MU_ENABLE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "mu_enable_get" }, |
| { IEEE80211_PARAM_INST_MU_GRP_QMAT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mu_grp_qmt_inst" }, |
| { IEEE80211_PARAM_DELE_MU_GRP_QMAT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mu_grp_qmt_del" }, |
| { IEEE80211_PARAM_GET_MU_GRP_QMAT, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "mu_grp_qmt_get" }, |
| { IEEE80211_PARAM_EN_MU_GRP_QMAT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mu_grp_qmt_ena" }, |
| { IEEE80211_PARAM_DIS_MU_GRP_QMAT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mu_grp_qmt_dis" }, |
| { IEEE80211_PARAM_MU_DEBUG_FLAG, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mu_dbg_flg_set" }, |
| { IEEE80211_PARAM_MU_AIRTIME_PADDING, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mu_airtime_pad" }, |
| { IEEE80211_PARAM_DSP_DEBUG_LEVEL, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dsp_dbg_lvl_set" }, |
| { IEEE80211_PARAM_DSP_DEBUG_FLAG, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dsp_dbg_flg_set" }, |
| { IEEE80211_PARAM_SET_CRC_ERR, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_crc_error" }, |
| { IEEE80211_PARAM_MU_SWITCH_USR_POS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mu_sw_usr_pos" }, |
| { IEEE80211_PARAM_SET_GRP_SND_PERIOD, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mu_grp_snd_per" }, |
| { IEEE80211_PARAM_SET_PREC_SND_PERIOD, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mu_prec_snd_per" }, |
| { IEEE80211_PARAM_SET_MU_RANK_TOLERANCE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mu_rank_toleran" }, |
| { IEEE80211_PARAM_DSP_PRECODING_ALGORITHM, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dsp_prc_alg_set" }, |
| { IEEE80211_PARAM_DSP_RANKING_ALGORITHM, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dsp_rnk_alg_set" }, |
| { IEEE80211_PARAM_MU_USE_EQ, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mu_set_use_eq" }, |
| { IEEE80211_PARAM_MU_USE_EQ, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "mu_get_use_eq" }, |
| { IEEE80211_PARAM_MU_AMSDU_SIZE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mu_amsdu_size" }, |
| #if defined(QBMPS_ENABLE) |
| { IEEE80211_PARAM_STA_BMPS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_bmps" }, |
| { IEEE80211_PARAM_STA_BMPS,0, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_bmps" }, |
| #endif |
| |
| { IEEE80211_IOCTL_GETBLOCK, |
| 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | IW_PRIV_BLOCK_DATASIZE, "" }, |
| /* sub-ioctl */ |
| { IEEE80211_PARAM_ASSOC_HISTORY, |
| 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | IW_PRIV_BLOCK_DATASIZE, "assoc_history" }, |
| { IEEE80211_PARAM_CSW_RECORD, |
| 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | IW_PRIV_BLOCK_DATASIZE, "get_csw_record" }, |
| { IEEE80211_PARAM_RESTRICT_RTS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "restrict_rts" }, |
| { IEEE80211_PARAM_RESTRICT_RTS, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_rstrict_rts" }, |
| { IEEE80211_PARAM_RESTRICT_LIMIT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "restrict_max" }, |
| { IEEE80211_PARAM_RESTRICT_LIMIT, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_rstrict_max" }, |
| { IEEE80211_PARAM_SWRETRY_AGG_MAX, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "swret_agg" }, |
| { IEEE80211_PARAM_SWRETRY_AGG_MAX, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_swret_agg" }, |
| { IEEE80211_PARAM_SWRETRY_NOAGG_MAX, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "swret_noagg" }, |
| { IEEE80211_PARAM_SWRETRY_NOAGG_MAX, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_swret_noagg" }, |
| { IEEE80211_PARAM_CCA_PRI, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_cca_pri" }, |
| { IEEE80211_PARAM_CCA_SEC, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_cca_sec" }, |
| { IEEE80211_PARAM_CCA_SEC40, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_cca_sec40" }, |
| { IEEE80211_PARAM_CCA_FIXED, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_cca_fixed" }, |
| { IEEE80211_PARAM_CCA_FIXED, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_cca_fixed" }, |
| { IEEE80211_PARAM_PWR_SAVE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "pm" }, |
| { IEEE80211_PARAM_PWR_SAVE, |
| 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | IW_PRIV_BLOCK_DATASIZE, "get_pm" }, |
| { IEEE80211_PARAM_PS_CMD, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ps_cmd" }, |
| { IEEE80211_PARAM_FAST_REASSOC, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "fast_reassoc" }, |
| { IEEE80211_PARAM_TEST_TRAFFIC, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "test_traffic" }, |
| { IEEE80211_PARAM_QCAT_STATE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "qcat_state" }, |
| { IEEE80211_PARAM_RALG_DBG, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ralg_dbg" }, |
| { IEEE80211_PARAM_CSA_FLAG, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "csa_flag" }, |
| { IEEE80211_PARAM_CSA_FLAG, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_csa_flag" }, |
| { IEEE80211_PARAM_DEF_MATRIX, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "def_matrix" }, |
| { IEEE80211_PARAM_DEF_MATRIX, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_def_matrix" }, |
| { IEEE80211_PARAM_TUNEPD, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "tunepd" }, |
| { IEEE80211_PARAM_TUNEPD_DONE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "tunepd_done" }, |
| { IEEE80211_PARAM_RTSTHRESHOLD, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_rtshold" }, |
| { IEEE80211_PARAM_CARRIER_ID, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "carrier_id" }, |
| { IEEE80211_PARAM_CARRIER_ID, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_carrier_id" }, |
| { IEEE80211_PARAM_BA_THROT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ba_throt" }, |
| { IEEE80211_PARAM_TX_QUEUING_ALG, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "queuing_alg" }, |
| { IEEE80211_PARAM_TX_QUEUING_ALG, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_queuing_alg" }, |
| { IEEE80211_PARAM_WME_THROT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wme_throt" }, |
| { IEEE80211_PARAM_MODE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_vht" }, |
| { IEEE80211_PARAM_MODE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_vht" }, |
| { IEEE80211_PARAM_ENABLE_11AC, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "enable_11ac" }, |
| { IEEE80211_PARAM_ENABLE_11AC, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_11ac_status" }, |
| { IEEE80211_PARAM_FIXED_11AC_TX_RATE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_11ac_mcs" }, |
| { IEEE80211_PARAM_FIXED_11AC_TX_RATE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_11ac_mcs" }, |
| { IEEE80211_PARAM_FIXED_11AC_MU_TX_RATE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_mu_mcs" }, |
| { IEEE80211_PARAM_AUC_RX_DBG, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "auc_rx_dbg" }, |
| { IEEE80211_PARAM_AUC_TX_DBG, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "auc_tx_dbg" }, |
| { IEEE80211_PARAM_TX_MAXMPDU, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "tx_maxmpdu" }, |
| { IEEE80211_PARAM_TX_MAXMPDU, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_tx_maxmpdu" }, |
| { IEEE80211_PARAM_RX_ACCELERATE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "rx_accel" }, |
| { IEEE80211_PARAM_RX_ACCEL_LOOKUP_SA, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "rx_accel_lu_sa" }, |
| { IEEE80211_PARAM_TACMAP, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "tacmap" }, |
| { IEEE80211_PARAM_VAP_PRI, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "vap_pri" }, |
| { IEEE80211_PARAM_VAP_PRI, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_vap_pri" }, |
| { IEEE80211_PARAM_AIRFAIR, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "airfair" }, |
| { IEEE80211_PARAM_AIRFAIR, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_airfair" }, |
| { IEEE80211_PARAM_AUC_QOS_SCH, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "auc_qos_sch" }, |
| { IEEE80211_PARAM_VAP_PRI_WME, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "vap_pri_wme" }, |
| { IEEE80211_PARAM_EMI_POWER_SWITCHING, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "emi_pwr_sw" }, |
| { IEEE80211_PARAM_EMI_POWER_SWITCHING, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_emi_pwr" }, |
| { IEEE80211_PARAM_AGGRESSIVE_AGG, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "aggressive_agg" }, |
| { IEEE80211_PARAM_TQEW_DESCR_LIMIT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "tqew_descrs" }, |
| { IEEE80211_PARAM_TQEW_DESCR_LIMIT, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_tqew_descrs" }, |
| { IEEE80211_PARAM_BR_IP_ADDR, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "br_ip" }, |
| { IEEE80211_PARAM_GENPCAP, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "genpcap" }, |
| { IEEE80211_PARAM_TDLS_DISC_INT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "tdls_disc_int" }, |
| { IEEE80211_PARAM_TDLS_PATH_SEL_WEIGHT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "tdls_path_wgt" }, |
| { IEEE80211_PARAM_TDLS_MIN_RSSI, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "tdls_min_rssi" }, |
| { IEEE80211_PARAM_TDLS_SWITCH_INTS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "tdls_swit_ints" }, |
| { IEEE80211_PARAM_TDLS_RATE_WEIGHT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "tdls_rate_wgt" }, |
| { IEEE80211_PARAM_TDLS_OFF_CHAN, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "tdls_offchan" }, |
| { IEEE80211_PARAM_TDLS_OFF_CHAN, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_tdls_offchan" }, |
| { IEEE80211_PARAM_TDLS_OFF_CHAN_BW, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "tdls_offchbw" }, |
| { IEEE80211_PARAM_TDLS_OFF_CHAN_BW, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_tdls_offchbw" }, |
| { IEEE80211_PARAM_OCAC, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ocac_set" }, |
| { IEEE80211_PARAM_OCAC, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "ocac_get" }, |
| { IEEE80211_PARAM_SDFS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "sdfs_set" }, |
| { IEEE80211_PARAM_SDFS, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "sdfs_get" }, |
| { IEEE80211_PARAM_DEACTIVE_CHAN_PRI, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "deact_chan_pri" }, |
| { IEEE80211_PARAM_RESTRICT_RATE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "restrict_rt" }, |
| { IEEE80211_PARAM_RESTRICT_RATE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_restrict_rt" }, |
| { IEEE80211_PARAM_TRAINING_START, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "training_restart" }, |
| { IEEE80211_PARAM_VCO_LOCK_DETECT_MODE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_vco_lock" }, |
| { IEEE80211_PARAM_VCO_LOCK_DETECT_MODE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_vco_lock" }, |
| { IEEE80211_PARAM_CONFIG_PMF, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "pmf_set" }, |
| { IEEE80211_PARAM_CONFIG_PMF, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "pmf_get" }, |
| { IEEE80211_PARAM_SCAN_CANCEL, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "scan_cancel" }, |
| { IEEE80211_PARAM_AUTO_CCA_ENABLE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "auto_cca_enable" }, |
| { IEEE80211_PARAM_AUTO_CCA_ENABLE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_auto_cca_en" }, |
| { IEEE80211_PARAM_AUTO_CCA_PARAMS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "auto_cca_param" }, |
| { IEEE80211_PARAM_AUTO_CCA_DEBUG, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "auto_cca_dbg" }, |
| { IEEE80211_PARAM_AUTO_CS_ENABLE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "autocs_enable" }, |
| { IEEE80211_PARAM_AUTO_CS_PARAMS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "autocs_param" }, |
| { IEEE80211_PARAM_INTRA_BSS_ISOLATE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "intra_bss" }, |
| { IEEE80211_PARAM_INTRA_BSS_ISOLATE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_intra_bss" }, |
| { IEEE80211_PARAM_BSS_ISOLATE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bss_isolate" }, |
| { IEEE80211_PARAM_BSS_ISOLATE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_bss_iso" }, |
| { IEEE80211_PARAM_BF_RX_STS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bf_rxsts" }, |
| { IEEE80211_PARAM_BF_RX_STS, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_bf_rxsts" }, |
| { IEEE80211_PARAM_PC_OVERRIDE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "pc_override" }, |
| { IEEE80211_PARAM_PC_OVERRIDE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_pc_override" }, |
| { IEEE80211_PARAM_WOWLAN, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wowlan_set" }, |
| { IEEE80211_PARAM_WOWLAN, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "wowlan_get" }, |
| { IEEE80211_PARAM_RX_AMSDU_ENABLE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "rx_amsdu" }, |
| { IEEE80211_PARAM_RX_AMSDU_ENABLE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_rx_amsdu" }, |
| { IEEE80211_PARAM_DISASSOC_REASON, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "disassoc_reason" }, |
| { IEEE80211_PARAM_PEER_RTS_MODE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "peer_rts" }, |
| { IEEE80211_PARAM_PEER_RTS_MODE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_peer_rts" }, |
| { IEEE80211_PARAM_DYN_WMM, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dyn_wmm" }, |
| { IEEE80211_PARAM_DYN_WMM, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_dyn_wmm" }, |
| { IEEE80211_PARAM_BA_SETUP_ENABLE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "rssi_for_ba_set" }, |
| { IEEE80211_PARAM_BB_PARAM, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_bb_param" }, |
| { IEEE80211_PARAM_BB_PARAM, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_bb_param" }, |
| { IEEE80211_PARAM_VAP_TX_AMSDU, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "vap_txamsdu" }, |
| { IEEE80211_PARAM_VAP_TX_AMSDU, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_vap_txamsdu" }, |
| { IEEE80211_PARAM_VAP_TX_AMSDU_11N, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "txamsdu_11n" }, |
| { IEEE80211_PARAM_VAP_TX_AMSDU_11N, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_txamsdu_11n" }, |
| { IEEE80211_PARAM_CS_THRESHOLD, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "cs_thres" }, |
| { IEEE80211_PARAM_CS_THRESHOLD, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_cs_thres" }, |
| { IEEE80211_PARAM_CS_THRESHOLD_DBM, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "cs_thres_dbm" }, |
| { IEEE80211_PARAM_SCAN_RESULTS_CHECK_INV, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_scan_inv" }, |
| { IEEE80211_PARAM_SCAN_RESULTS_CHECK_INV, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_scan_inv" }, |
| { IEEE80211_PARAM_SFS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "sfs" }, |
| { IEEE80211_PARAM_INST_1SS_DEF_MAT_ENABLE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "def_1ss_mat_en" }, |
| { IEEE80211_PARAM_INST_1SS_DEF_MAT_THRESHOLD, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "def_1ss_mat_th" }, |
| { IEEE80211_PARAM_SWFEAT_DISABLE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "swfeat_disable"}, |
| { IEEE80211_PARAM_FLUSH_SCAN_ENTRY, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "flush_scan" }, |
| { IEEE80211_PARAM_SCAN_OPCHAN, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_scan_opch" }, |
| { IEEE80211_PARAM_SCAN_OPCHAN, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_scan_opch" }, |
| { IEEE80211_PARAM_DUMP_PPPC_TX_SCALE_BASES, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dump_scale_base" }, |
| { IEEE80211_PARAM_GLOBAL_FIXED_TX_SCALE_INDEX, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "tx_scale_index" }, |
| { IEEE80211_PARAM_QTN_HAL_PM_CORRUPT_DEBUG, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "pm_corrupt_debug"}, |
| { IEEE80211_PARAM_L2_EXT_FILTER, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "l2_ext_filter" }, |
| { IEEE80211_PARAM_L2_EXT_FILTER, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_l2_ext_filt" }, |
| { IEEE80211_PARAM_ENABLE_RX_OPTIM_STATS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_optim_stats" }, |
| { IEEE80211_PARAM_SET_UNICAST_QUEUE_NUM, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "unicast_qcnt" }, |
| { IEEE80211_PARAM_OBSS_EXEMPT_REQ, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "obss_exe_req" }, |
| { IEEE80211_PARAM_OBSS_TRIGG_SCAN_INT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_obss_int" }, |
| { IEEE80211_PARAM_OBSS_TRIGG_SCAN_INT, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_obss_int" }, |
| { IEEE80211_PARAM_ALLOW_VHT_TKIP, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_vht_tkip" }, |
| { IEEE80211_PARAM_ALLOW_VHT_TKIP, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_vht_tkip" }, |
| { IEEE80211_PARAM_VHT_2_4GHZ, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_vht_24g" }, |
| { IEEE80211_PARAM_VHT_2_4GHZ,0, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_vht_24g" }, |
| { IEEE80211_PARAM_BEACONING_SCHEME, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bcn_scheme" }, |
| { IEEE80211_PARAM_BEACONING_SCHEME, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_bcn_scheme" }, |
| { IEEE80211_PARAM_40MHZ_INTOLERANT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "40mhz_intol" }, |
| { IEEE80211_PARAM_40MHZ_INTOLERANT, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_40mhz_intol" }, |
| { IEEE80211_PARAM_VAP_STATE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "vap_state" }, |
| { IEEE80211_PARAM_VAP_STATE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_vap_state" }, |
| { IEEE80211_PARAM_ANTENNA_USAGE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_antenna_usg" }, |
| { IEEE80211_PARAM_ANTENNA_USAGE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_antenna_usg" }, |
| { IEEE80211_PARAM_SET_RTS_BW_DYN, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_rts_bw" }, |
| { IEEE80211_PARAM_SET_RTS_BW_DYN, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_rts_bw" }, |
| { IEEE80211_PARAM_SET_DUP_RTS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_dup_rts" }, |
| { IEEE80211_PARAM_SET_DUP_RTS, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_dup_rts" }, |
| { IEEE80211_PARAM_SET_CTS_BW, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_cts_bw" }, |
| { IEEE80211_PARAM_SET_CTS_BW, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_cts_bw" }, |
| { IEEE80211_PARAM_VHT_MCS_CAP, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_vht_mcs_cap" }, |
| { IEEE80211_PARAM_VHT_MCS_CAP, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_vht_mcs_cap" }, |
| { IEEE80211_PARAM_VHT_OPMODE_NOTIF, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_vht_opmntf" }, |
| { IEEE80211_PARAM_VHT_OPMODE_NOTIF, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_vht_opmntf" }, |
| { IEEE80211_PARAM_USE_NON_HT_DUPLICATE_MU, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_mu_non_ht" }, |
| { IEEE80211_PARAM_USE_NON_HT_DUPLICATE_MU, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_mu_non_ht" }, |
| { IEEE80211_PARAM_BG_PROTECT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "11g_protect" }, |
| { IEEE80211_PARAM_BG_PROTECT, 0, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_11g_protect" }, |
| { IEEE80211_PARAM_MU_NDPA_BW_SIGNALING_SUPPORT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_rx_bws_ndpa" }, |
| { IEEE80211_PARAM_MU_NDPA_BW_SIGNALING_SUPPORT, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_rx_bws_ndpa" }, |
| { IEEE80211_PARAM_RESTRICT_WLAN_IP, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_rstrict_wip" }, |
| { IEEE80211_PARAM_RESTRICT_WLAN_IP, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_rstrict_wip" }, |
| { IEEE80211_PARAM_MC_TO_UC, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_mc_to_uc" }, |
| { IEEE80211_PARAM_MC_TO_UC, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_mc_to_uc" }, |
| { IEEE80211_PARAM_HOSTAP_STARTED, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "hostap_state" }, |
| { IEEE80211_PARAM_HOSTAP_STARTED, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_hostap_state" }, |
| { IEEE80211_PARAM_WPA_STARTED, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wpa_state" }, |
| { IEEE80211_PARAM_WPA_STARTED, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_wpa_state" }, |
| { IEEE80211_PARAM_EP_STATUS, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_ep_status" }, |
| { IEEE80211_PARAM_MAX_BCAST_PPS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_bcast_pps" }, |
| { IEEE80211_PARAM_BSS_GROUP_ID, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_ssid_grpid" }, |
| { IEEE80211_PARAM_BSS_GROUP_ID, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_ssid_grpid" }, |
| { IEEE80211_PARAM_BSS_ASSOC_RESERVE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_assoc_rsrv" }, |
| { IEEE80211_PARAM_BSS_ASSOC_RESERVE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_assoc_rsrv" }, |
| { IEEE80211_PARAM_MAX_BOOT_CAC_DURATION, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_boot_cac" }, |
| { IEEE80211_PARAM_RX_BAR_SYNC, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_rx_bar_sync" }, |
| { IEEE80211_PARAM_RX_BAR_SYNC, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_rx_bar_sync" }, |
| { IEEE80211_PARAM_GET_REG_DOMAIN_IS_EU, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_reg_is_eu" }, |
| { IEEE80211_PARAM_AUC_TX_AGG_DURATION, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "auc_tx_agg_dur" }, |
| { IEEE80211_PARAM_GET_CHAN_AVAILABILITY_STATUS, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "chan_avail_stat" }, |
| { IEEE80211_PARAM_STOP_ICAC, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1 , 0, "stop_icac" }, |
| { IEEE80211_PARAM_STA_DFS_STRICT_MODE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "sta_dfs_strict" }, |
| { IEEE80211_PARAM_STA_DFS_STRICT_MEASUREMENT_IN_CAC, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "sta_dfs_msr_cac" }, |
| { IEEE80211_PARAM_STA_DFS_STRICT_TX_CHAN_CLOSE_TIME, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "sta_dfs_end_tx" }, |
| { IEEE80211_PARAM_NEIGHBORHOOD_THRSHD, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_nbr_thrshd" }, |
| { IEEE80211_PARAM_NEIGHBORHOOD_TYPE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_nbr_type" }, |
| { IEEE80211_PARAM_NEIGHBORHOOD_COUNT, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_nbr_cnt" }, |
| { IEEE80211_PARAM_RADAR_NONOCCUPY_PERIOD, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "radar_nop_time" }, |
| { IEEE80211_PARAM_RADAR_NONOCCUPY_PERIOD, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gt_radar_nop" }, |
| { IEEE80211_PARAM_DFS_CSA_CNT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dfs_csa_cnt" }, |
| { IEEE80211_PARAM_DFS_CSA_CNT, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_dfs_csa_cnt" }, |
| { IEEE80211_PARAM_STA_DFS_STRICT_MODE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gt_stdfs_strict" }, |
| { IEEE80211_PARAM_STA_DFS_STRICT_MEASUREMENT_IN_CAC, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gt_stdfs_msrcac" }, |
| { IEEE80211_PARAM_STA_DFS_STRICT_TX_CHAN_CLOSE_TIME, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gt_stdfs_end_tx" }, |
| { IEEE80211_PARAM_COEX_20_40_SUPPORT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "20_40_bss_coex" }, |
| { IEEE80211_PARAM_MIN_CAC_PERIOD, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_min_cac" }, |
| #ifdef CONFIG_NAC_MONITOR |
| { IEEE80211_PARAM_NAC_MONITOR_MODE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "nac_mode" }, |
| { IEEE80211_PARAM_NAC_MONITOR_MODE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_nac_mode" }, |
| #endif |
| { IEEE80211_PARAM_DEVICE_MODE, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_dev_mode" }, |
| { IEEE80211_PARAM_MAX_DEVICE_BW, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_max_bw" }, |
| { IEEE80211_PARAM_MAX_DEVICE_BW, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_max_bw" }, |
| { IEEE80211_PARAM_BW_AUTO_SELECT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_bw_autosel" }, |
| { IEEE80211_PARAM_BW_AUTO_SELECT, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_bw_autosel" }, |
| { IEEE80211_PARAM_IGNORE_ICAC_SELECTION, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1 , 0, "ignr_icac_sel" }, |
| { IEEE80211_PARAM_DFS_CHANS_AVAILABLE_FOR_DFS_REENTRY, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "dfs_chan_avail" }, |
| |
| { IEEE80211_PARAM_CUR_CHAN_CHECK_REQUIRED, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "check_cur_chan" }, |
| #ifdef CONFIG_QHOP |
| { IEEE80211_PARAM_RBS_MBS_ALLOW_TX_FRMS_IN_CAC, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "rbs_dfs_msr_cac" }, |
| { IEEE80211_PARAM_RBS_MBS_ALLOW_TX_FRMS_IN_CAC, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "g_rbsdfs_msrcac" }, |
| { IEEE80211_PARAM_RBS_DFS_TX_CHAN_CLOSE_TIME, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "rbs_dfs_end_tx" }, |
| { IEEE80211_PARAM_RBS_DFS_TX_CHAN_CLOSE_TIME, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "g_rbsdfs_end_tx" }, |
| #endif |
| { IEEE80211_PARAM_80211K_NEIGH_REPORT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "neigh_repo" }, |
| { IEEE80211_PARAM_80211K_NEIGH_REPORT, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_neigh_repo" }, |
| { IEEE80211_PARAM_DYNAMIC_SIFS_TIMING, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_dyn_sifs" }, |
| { IEEE80211_PARAM_WEATHERCHAN_CAC_ALLOWED, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wea_cac_en" }, |
| { IEEE80211_PARAM_WEATHERCHAN_CAC_ALLOWED, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_wea_cac_en" }, |
| { IEEE80211_PARAM_VOPT, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_vopt" }, |
| { IEEE80211_PARAM_VOPT, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_vopt" }, |
| { IEEE80211_PARAM_BB_DEAFNESS_WAR_EN, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bbdf_war_en" }, |
| { IEEE80211_PARAM_BB_DEAFNESS_WAR_EN, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_bbdfwar_en" }, |
| { IEEE80211_PARAM_COC_MOVE_TO_NONDFS_CHANNEL, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "coc_mv_to_ndfs" }, |
| { IEEE80211_PARAM_COC_MOVE_TO_NONDFS_CHANNEL, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "g_coc_mv_to_ndfs" }, |
| { IEEE80211_PARAM_80211V_BTM, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bss_tm" }, |
| { IEEE80211_PARAM_80211V_BTM, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_bss_tm" }, |
| { IEEE80211_PARAM_MOBILITY_DOMAIN, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mdid" }, |
| { IEEE80211_PARAM_MOBILITY_DOMAIN, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_mdid" }, |
| { IEEE80211_PARAM_FT_OVER_DS, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ft_over_ds" }, |
| { IEEE80211_PARAM_FT_OVER_DS, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_ft_over_ds" }, |
| { IEEE80211_PARAM_SHORT_RETRY_LIMIT, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_sretry_lmt" }, |
| { IEEE80211_PARAM_LONG_RETRY_LIMIT, |
| 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_lretry_lmt" }, |
| #ifdef PLATFORM_QFDR |
| { IEEE80211_PARAM_REJECT_AUTH, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "reject_auth" }, |
| { IEEE80211_PARAM_SCAN_ONLY_FREQ, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "scan_only_freq" }, |
| #endif |
| { IEEE80211_PARAM_FIX_LEGACY_RATE, |
| IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "fix_legacy_rate" }, |
| }; |
| |
| static const iw_handler ieee80211_handlers[] = { |
| (iw_handler) NULL, /* SIOCSIWCOMMIT */ |
| (iw_handler) ieee80211_ioctl_giwname, /* SIOCGIWNAME */ |
| (iw_handler) NULL, /* SIOCSIWNWID */ |
| (iw_handler) NULL, /* SIOCGIWNWID */ |
| (iw_handler) ieee80211_ioctl_siwfreq, /* SIOCSIWFREQ */ |
| (iw_handler) ieee80211_ioctl_giwfreq, /* SIOCGIWFREQ */ |
| (iw_handler) ieee80211_ioctl_siwmode, /* SIOCSIWMODE */ |
| (iw_handler) ieee80211_ioctl_giwmode, /* SIOCGIWMODE */ |
| (iw_handler) ieee80211_ioctl_siwsens, /* SIOCSIWSENS */ |
| (iw_handler) ieee80211_ioctl_giwsens, /* SIOCGIWSENS */ |
| (iw_handler) NULL /* not used */, /* SIOCSIWRANGE */ |
| (iw_handler) ieee80211_ioctl_giwrange, /* SIOCGIWRANGE */ |
| (iw_handler) NULL /* not used */, /* SIOCSIWPRIV */ |
| (iw_handler) NULL /* kernel code */, /* SIOCGIWPRIV */ |
| (iw_handler) NULL /* not used */, /* SIOCSIWSTATS */ |
| (iw_handler) NULL /* kernel code */, /* SIOCGIWSTATS */ |
| (iw_handler) ieee80211_ioctl_setspy, /* SIOCSIWSPY */ |
| (iw_handler) ieee80211_ioctl_getspy, /* SIOCGIWSPY */ |
| (iw_handler) ieee80211_ioctl_setthrspy, /* SIOCSIWTHRSPY */ |
| (iw_handler) ieee80211_ioctl_getthrspy, /* SIOCGIWTHRSPY */ |
| (iw_handler) ieee80211_ioctl_siwap, /* SIOCSIWAP */ |
| (iw_handler) ieee80211_ioctl_giwap, /* SIOCGIWAP */ |
| #ifdef SIOCSIWMLME |
| (iw_handler) ieee80211_ioctl_siwmlme, /* SIOCSIWMLME */ |
| #else |
| (iw_handler) NULL, /* -- hole -- */ |
| #endif |
| (iw_handler) ieee80211_ioctl_iwaplist, /* SIOCGIWAPLIST */ |
| #ifdef SIOCGIWSCAN |
| (iw_handler) ieee80211_ioctl_siwscan, /* SIOCSIWSCAN */ |
| (iw_handler) ieee80211_ioctl_giwscan, /* SIOCGIWSCAN */ |
| #else |
| (iw_handler) NULL, /* SIOCSIWSCAN */ |
| (iw_handler) NULL, /* SIOCGIWSCAN */ |
| #endif /* SIOCGIWSCAN */ |
| (iw_handler) ieee80211_ioctl_siwessid, /* SIOCSIWESSID */ |
| (iw_handler) ieee80211_ioctl_giwessid, /* SIOCGIWESSID */ |
| (iw_handler) ieee80211_ioctl_siwnickn, /* SIOCSIWNICKN */ |
| (iw_handler) ieee80211_ioctl_giwnickn, /* SIOCGIWNICKN */ |
| (iw_handler) NULL, /* -- hole -- */ |
| (iw_handler) NULL, /* -- hole -- */ |
| (iw_handler) ieee80211_ioctl_siwrate, /* SIOCSIWRATE */ |
| (iw_handler) ieee80211_ioctl_giwrate, /* SIOCGIWRATE */ |
| (iw_handler) ieee80211_ioctl_siwrts, /* SIOCSIWRTS */ |
| (iw_handler) ieee80211_ioctl_giwrts, /* SIOCGIWRTS */ |
| (iw_handler) ieee80211_ioctl_siwfrag, /* SIOCSIWFRAG */ |
| (iw_handler) ieee80211_ioctl_giwfrag, /* SIOCGIWFRAG */ |
| (iw_handler) ieee80211_ioctl_siwtxpow, /* SIOCSIWTXPOW */ |
| (iw_handler) ieee80211_ioctl_giwtxpow, /* SIOCGIWTXPOW */ |
| (iw_handler) ieee80211_ioctl_siwretry, /* SIOCSIWRETRY */ |
| (iw_handler) ieee80211_ioctl_giwretry, /* SIOCGIWRETRY */ |
| (iw_handler) ieee80211_ioctl_siwencode, /* SIOCSIWENCODE */ |
| (iw_handler) ieee80211_ioctl_giwencode, /* SIOCGIWENCODE */ |
| (iw_handler) ieee80211_ioctl_siwpower, /* SIOCSIWPOWER */ |
| (iw_handler) ieee80211_ioctl_giwpower, /* SIOCGIWPOWER */ |
| (iw_handler) NULL, /* -- hole -- */ |
| (iw_handler) NULL, /* -- hole -- */ |
| (iw_handler) ieee80211_ioctl_siwgenie, /* SIOCSIWGENIE */ |
| (iw_handler) ieee80211_ioctl_giwgenie, /* SIOCGIWGENIE */ |
| (iw_handler) ieee80211_ioctl_siwauth, /* SIOCSIWAUTH */ |
| (iw_handler) ieee80211_ioctl_giwauth, /* SIOCGIWAUTH */ |
| (iw_handler) ieee80211_ioctl_siwencodeext, /* SIOCSIWENCODEEXT */ |
| (iw_handler) ieee80211_ioctl_giwencodeext, /* SIOCGIWENCODEEXT */ |
| }; |
| |
| static const iw_handler ieee80211_priv_handlers[] = { |
| (iw_handler) ieee80211_ioctl_setparam, /* SIOCIWFIRSTPRIV+0 */ |
| (iw_handler) ieee80211_ioctl_getparam, /* SIOCIWFIRSTPRIV+1 */ |
| (iw_handler) ieee80211_ioctl_setmode, /* SIOCIWFIRSTPRIV+2 */ |
| (iw_handler) ieee80211_ioctl_getmode, /* SIOCIWFIRSTPRIV+3 */ |
| (iw_handler) ieee80211_ioctl_setwmmparams, /* SIOCIWFIRSTPRIV+4 */ |
| (iw_handler) ieee80211_ioctl_getwmmparams, /* SIOCIWFIRSTPRIV+5 */ |
| (iw_handler) ieee80211_ioctl_setchanlist, /* SIOCIWFIRSTPRIV+6 */ |
| (iw_handler) ieee80211_ioctl_getchanlist, /* SIOCIWFIRSTPRIV+7 */ |
| (iw_handler) ieee80211_ioctl_chanswitch, /* SIOCIWFIRSTPRIV+8 */ |
| (iw_handler) ieee80211_ioctl_getappiebuf, /* SIOCIWFIRSTPRIV+9 */ |
| (iw_handler) ieee80211_ioctl_setappiebuf, /* SIOCIWFIRSTPRIV+10 */ |
| (iw_handler) ieee80211_ioctl_getscanresults, /* SIOCIWFIRSTPRIV+11 */ |
| (iw_handler) ieee80211_ioctl_setfilter, /* SIOCIWFIRSTPRIV+12 */ |
| (iw_handler) ieee80211_ioctl_getchaninfo, /* SIOCIWFIRSTPRIV+13 */ |
| (iw_handler) ieee80211_ioctl_setoptie, /* SIOCIWFIRSTPRIV+14 */ |
| (iw_handler) ieee80211_ioctl_getoptie, /* SIOCIWFIRSTPRIV+15 */ |
| (iw_handler) ieee80211_ioctl_setmlme, /* SIOCIWFIRSTPRIV+16 */ |
| (iw_handler) ieee80211_ioctl_radar, /* SIOCIWFIRSTPRIV+17 */ |
| (iw_handler) ieee80211_ioctl_setkey, /* SIOCIWFIRSTPRIV+18 */ |
| (iw_handler) ieee80211_ioctl_postevent, /* SIOCIWFIRSTPRIV+19 */ |
| (iw_handler) ieee80211_ioctl_delkey, /* SIOCIWFIRSTPRIV+20 */ |
| (iw_handler) ieee80211_ioctl_txeapol, /* SIOCIWFIRSTPRIV+21 */ |
| (iw_handler) ieee80211_ioctl_addmac, /* SIOCIWFIRSTPRIV+22 */ |
| (iw_handler) ieee80211_ioctl_startcca, /* SIOCIWFIRSTPRIV+23 */ |
| (iw_handler) ieee80211_ioctl_delmac, /* SIOCIWFIRSTPRIV+24 */ |
| #if defined(CONFIG_QTN_80211K_SUPPORT) |
| (iw_handler) ieee80211_ioctl_getstastatistic, /* SIOCIWFIRSTPRIV+25 */ |
| #else |
| (iw_handler) NULL, /* SIOCIWFIRSTPRIV+25 */ |
| #endif |
| (iw_handler) ieee80211_ioctl_wdsmac, /* SIOCIWFIRSTPRIV+26 */ |
| (iw_handler) NULL, /* SIOCIWFIRSTPRIV+27 */ |
| (iw_handler) ieee80211_ioctl_wdsdelmac, /* SIOCIWFIRSTPRIV+28 */ |
| (iw_handler) ieee80211_ioctl_getblockdata, /* SIOCIWFIRSTPRIV+29 */ |
| (iw_handler) ieee80211_ioctl_kickmac, /* SIOCIWFIRSTPRIV+30 */ |
| (iw_handler) ieee80211_ioctl_dfsactscan, /* SIOCIWFIRSTPRIV+31 */ |
| }; |
| static struct iw_handler_def ieee80211_iw_handler_def = { |
| #define N(a) (sizeof (a) / sizeof (a[0])) |
| .standard = (iw_handler *) ieee80211_handlers, |
| .num_standard = N(ieee80211_handlers), |
| .private = (iw_handler *) ieee80211_priv_handlers, |
| .num_private = N(ieee80211_priv_handlers), |
| .private_args = (struct iw_priv_args *) ieee80211_priv_args, |
| .num_private_args = N(ieee80211_priv_args), |
| #if IW_HANDLER_VERSION >= 7 |
| .get_wireless_stats = ieee80211_iw_getstats, |
| #endif |
| #undef N |
| }; |
| |
| static void ieee80211_delete_wlanunit(u_int); |
| |
| /** |
| * The interface stats and local node's stats are reset here |
| * */ |
| int ieee80211_rst_dev_stats(struct ieee80211vap *vap) |
| { |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0) |
| struct rtnl_link_stats64 *stats = &vap->iv_devstats; |
| #else |
| struct net_device_stats *stats = &vap->iv_devstats; |
| #endif |
| struct ieee80211com *ic = vap->iv_ic; |
| struct ieee80211_node *ni, *next; |
| struct ieee80211_node_table *nt = &ic->ic_sta; |
| |
| /*The interface's statics will be cleared*/ |
| stats->rx_packets = 0; |
| stats->tx_packets = 0; |
| stats->rx_bytes = 0; |
| stats->tx_bytes = 0; |
| stats->rx_unicast_packets = 0; |
| stats->tx_unicast_packets = 0; |
| |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) |
| watch64_reset(&stats->rx_packets, NULL); |
| watch64_reset(&stats->tx_packets, NULL); |
| watch64_reset(&stats->rx_bytes, NULL); |
| watch64_reset(&stats->tx_bytes, NULL); |
| watch64_reset(&stats->rx_unicast_packets, NULL); |
| watch64_reset(&stats->tx_unicast_packets, NULL); |
| #endif |
| |
| stats->tx_multicast_packets = 0; |
| stats->multicast = 0; |
| stats->rx_broadcast_packets = 0; |
| stats->tx_broadcast_packets = 0; |
| stats->rx_unknown_packets = 0; |
| stats->rx_dropped = 0; |
| |
| vap->iv_stats.is_tx_nodefkey = 0; |
| vap->iv_stats.is_tx_noheadroom = 0; |
| vap->iv_stats.is_crypto_enmicfail = 0; |
| stats->tx_errors = 0; |
| |
| vap->iv_stats.is_tx_nobuf = 0; |
| vap->iv_stats.is_tx_nonode = 0; |
| vap->iv_stats.is_tx_unknownmgt = 0; |
| vap->iv_stats.is_tx_badcipher = 0; |
| vap->iv_stats.is_tx_nodefkey = 0; |
| stats->tx_dropped = 0; |
| |
| vap->iv_stats.is_rx_tooshort = 0; |
| vap->iv_stats.is_rx_wepfail = 0; |
| vap->iv_stats.is_rx_decap = 0; |
| vap->iv_stats.is_rx_nobuf = 0; |
| vap->iv_stats.is_rx_decryptcrc = 0; |
| vap->iv_stats.is_rx_ccmpmic = 0; |
| vap->iv_stats.is_rx_tkipmic = 0; |
| vap->iv_stats.is_rx_tkipicv = 0; |
| stats->rx_errors = 0; |
| |
| ic->ic_reset_shared_vap_stats(vap); |
| |
| /* The statics of local nodes in the node table will be cleared */ |
| IEEE80211_NODE_LOCK_IRQ(nt); |
| TAILQ_FOREACH_SAFE(ni, &nt->nt_node, ni_list, next) { |
| if (!IEEE80211_ADDR_EQ(ni->ni_macaddr, vap->iv_myaddr)){ |
| memset(&ni->ni_stats, 0, sizeof(struct ieee80211_nodestats)); |
| ic->ic_reset_shared_node_stats(ni); |
| } |
| } |
| IEEE80211_NODE_UNLOCK_IRQ(nt); |
| |
| return 0; |
| } |
| |
| /* |
| * Handle private ioctl requests. |
| */ |
| static int |
| ieee80211_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) |
| { |
| struct ieee80211vap *vap = netdev_priv(dev); |
| u_int unit; |
| |
| switch (cmd) { |
| case SIOCG80211STATS: |
| return copy_to_user(ifr->ifr_data, &vap->iv_stats, |
| sizeof (vap->iv_stats)) ? -EFAULT : 0; |
| case SIOCR80211STATS: |
| return ieee80211_rst_dev_stats(vap); |
| case SIOC80211IFDESTROY: |
| if (!capable(CAP_NET_ADMIN)) |
| return -EPERM; |
| ieee80211_stop(vap->iv_dev); /* force state before cleanup */ |
| unit = vap->iv_unit; |
| vap->iv_ic->ic_vap_delete(vap); |
| return 0; |
| case IEEE80211_IOCTL_GETKEY: |
| return ieee80211_ioctl_getkey(dev, (struct iwreq *) ifr); |
| case IEEE80211_IOCTL_GETWPAIE: |
| return ieee80211_ioctl_getwpaie(dev, (struct iwreq *) ifr); |
| case IEEE80211_IOCTL_STA_STATS: |
| return ieee80211_ioctl_getstastats(dev, (struct iwreq *) ifr); |
| case IEEE80211_IOCTL_STA_INFO: |
| return ieee80211_ioctl_getstainfo(dev, (struct iwreq *) ifr); |
| case IEEE80211_IOCTL_SCAN_RESULTS: |
| return ieee80211_ioctl_getscanresults(dev, (struct iwreq *)ifr); |
| case IEEE80211_IOCTL_GET_ASSOC_TBL: |
| return ieee80211_ioctl_getassoctbl(dev, (struct iwreq *)ifr); |
| case IEEE80211_IOCTL_GET_RATES: |
| return ieee80211_ioctl_get_rates(dev, (struct iwreq *)ifr); |
| case IEEE80211_IOCTL_SET_RATES: |
| return ieee80211_ioctl_set_rates(dev, (struct iwreq *)ifr); |
| case IEEE80211_IOCTL_EXT: |
| return ieee80211_ioctl_ext(dev, (struct iwreq *)ifr); |
| } |
| return -EOPNOTSUPP; |
| } |
| |
| static u_int8_t wlan_units[32]; /* enough for 256 */ |
| |
| /* |
| * Allocate a new unit number. If the map is full return -1; |
| * otherwise the allocate unit number is returned. |
| */ |
| static int |
| ieee80211_new_wlanunit(void) |
| { |
| #define N(a) (sizeof(a)/sizeof(a[0])) |
| u_int unit; |
| u_int8_t b; |
| int i; |
| |
| /* NB: covered by rtnl_lock */ |
| unit = 0; |
| for (i = 0; i < N(wlan_units) && wlan_units[i] == 0xff; i++) |
| unit += NBBY; |
| if (i == N(wlan_units)) |
| return -1; |
| for (b = wlan_units[i]; b & 1; b >>= 1) |
| unit++; |
| setbit(wlan_units, unit); |
| |
| return unit; |
| #undef N |
| } |
| |
| /* |
| * Reclaim the specified unit number. |
| */ |
| static void |
| ieee80211_delete_wlanunit(u_int unit) |
| { |
| /* NB: covered by rtnl_lock */ |
| KASSERT(unit < sizeof(wlan_units) * NBBY, ("invalid wlan unit %u", unit)); |
| KASSERT(isset(wlan_units, unit), ("wlan unit %u not allocated", unit)); |
| clrbit(wlan_units, unit); |
| } |
| |
| /* |
| * Create a virtual ap. This is public as it must be implemented |
| * outside our control (e.g. in the driver). |
| */ |
| int |
| ieee80211_ioctl_create_vap(struct ieee80211com *ic, struct ifreq *ifr, struct net_device *mdev) |
| { |
| struct ieee80211_clone_params cp; |
| struct ieee80211vap *vap; |
| char name[IFNAMSIZ]; |
| int unit; |
| |
| if (!capable(CAP_NET_ADMIN)) |
| return -EPERM; |
| if (copy_from_user(&cp, ifr->ifr_data, sizeof(cp))) |
| return -EFAULT; |
| |
| unit = ieee80211_new_wlanunit(); |
| if (unit == -1) |
| return -EIO; /* XXX */ |
| memcpy(name, cp.icp_name, IFNAMSIZ - 1); |
| name[IFNAMSIZ - 1] = '\0'; |
| |
| vap = ic->ic_vap_create(ic, name, unit, cp.icp_opmode, cp.icp_flags, mdev); |
| if (vap == NULL) { |
| ieee80211_delete_wlanunit(unit); |
| return -EIO; |
| } |
| /* return final device name */ |
| memcpy(ifr->ifr_name, vap->iv_dev->name, IFNAMSIZ - 1); |
| ifr->ifr_name[IFNAMSIZ - 1] = '\0'; |
| return 0; |
| } |
| EXPORT_SYMBOL(ieee80211_ioctl_create_vap); |
| |
| /* |
| * Create a virtual ap. This is public as it must be implemented |
| * outside our control (e.g. in the driver). |
| * Must be called with rtnl_lock held |
| */ |
| int |
| ieee80211_create_vap(struct ieee80211com *ic, char *name, |
| struct net_device *mdev, int opmode, int opflags) |
| { |
| int unit; |
| |
| if ((unit = ieee80211_new_wlanunit()) == -1) |
| return -EIO; /* XXX */ |
| |
| if (!ic->ic_vap_create(ic, name, unit, opmode, opflags, mdev)) { |
| ieee80211_delete_wlanunit(unit); |
| return -EIO; |
| } |
| return 0; |
| } |
| EXPORT_SYMBOL(ieee80211_create_vap); |
| |
| |
| void |
| ieee80211_ioctl_vattach(struct ieee80211vap *vap) |
| { |
| struct net_device *dev = vap->iv_dev; |
| struct net_device_ops *pndo = (struct net_device_ops *)dev->netdev_ops; |
| |
| pndo->ndo_do_ioctl = ieee80211_ioctl; |
| #if IW_HANDLER_VERSION < 7 |
| dev->get_wireless_stats = ieee80211_iw_getstats; |
| #endif |
| dev->wireless_handlers = &ieee80211_iw_handler_def; |
| } |
| |
| void |
| ieee80211_ioctl_vdetach(struct ieee80211vap *vap) |
| { |
| if ((vap->iv_unit != -1) && isset(wlan_units, vap->iv_unit)) |
| ieee80211_delete_wlanunit(vap->iv_unit); |
| } |
| |
| /* Input function to send a message via the wireless_event kernel mechanism. */ |
| void |
| ieee80211_dot11_msg_send(struct ieee80211vap *vap, |
| const char *mac_bssid, |
| const char *message, |
| const char *message_code, |
| int message_reason, |
| const char *message_description, |
| const char *auth, |
| const char *crypto) |
| { |
| char buf[384]; |
| char mac[6] = {'0', '0', '0', '0', '0', '0'}; |
| const char *tag = QEVT_COMMON_PREFIX; |
| buf[sizeof(buf)-1] = '\0'; |
| |
| /* Ensure we always have a valid address */ |
| if (mac_bssid) |
| { |
| IEEE80211_ADDR_COPY(mac, mac_bssid); |
| } |
| |
| /* Message includes a reason code - disassoc, deauth to/from AP/STA */ |
| if (message_reason >= 0) { |
| ieee80211_eventf(vap->iv_dev, "%s%s [" DBGMAC "] [%s - %d - %s]", |
| tag, |
| message, |
| ETHERFMT(mac), |
| message_code ? message_code : "no code", |
| message_reason, |
| message_description ? message_description : "no description" |
| ); |
| |
| } |
| /* STA/AP connected including auth details */ |
| else if (auth) { |
| ieee80211_eventf(vap->iv_dev, "%s%s [" DBGMAC "] [%s/%s]", |
| tag, |
| message, |
| ETHERFMT(mac), |
| auth, |
| crypto ? crypto : "unspecified" |
| ); |
| } |
| /* Further details included in the message */ |
| else if (message_description) { |
| ieee80211_eventf(vap->iv_dev, "%s%s [" DBGMAC "] [%s - %s]", |
| tag, |
| message, |
| ETHERFMT(mac), |
| message_code ? message_code : "no code", |
| message_description |
| ); |
| } |
| /* Single descriptive text */ |
| else { |
| ieee80211_eventf(vap->iv_dev, "%s%s [" DBGMAC "] [%s]", |
| tag, |
| message, |
| ETHERFMT(mac), |
| message_code ? message_code : "no code" |
| ); |
| } |
| } |
| EXPORT_SYMBOL(ieee80211_dot11_msg_send); |
| |
| #if defined(CONFIG_QTN_BSA_SUPPORT) |
| int ieee80211_bsa_disconnect_event_send(struct ieee80211vap *vap, struct ieee80211_node *ni, |
| uint16_t reason_code, uint8_t fc_subtype, uint8_t direction) |
| { |
| struct qtn_bsa_peer_event_data *p_data; |
| struct qtn_bsa_disconnect_event_info *pevent; |
| uint8_t event_data[IEEE80211_MAX_EVENT_DATA_LEN]; |
| union iwreq_data wreq; |
| memset(&wreq, 0, sizeof(union iwreq_data)); |
| |
| p_data = (void *)event_data; |
| pevent = (void *)(event_data + sizeof(struct qtn_bsa_peer_event_data)); |
| strncpy(p_data->bsa_name, "BSA-PEER-EVENT", sizeof(p_data->bsa_name)); |
| if (fc_subtype == IEEE80211_FC0_SUBTYPE_DISASSOC) |
| put_unaligned(BSA_EVENT_DISASSOC, &p_data->bsa_event_id); |
| else |
| put_unaligned(BSA_EVENT_DEAUTH, &p_data->bsa_event_id); |
| |
| memcpy(p_data->bsa_bssid, ni->ni_bssid, IEEE80211_ADDR_LEN); |
| put_unaligned(sizeof(struct qtn_bsa_peer_event_data), &p_data->offset); |
| memcpy(pevent->bsa_sta_mac, ni->ni_macaddr, IEEE80211_ADDR_LEN); |
| |
| put_unaligned(reason_code, &pevent->reason_code); |
| pevent->direction = direction; |
| wreq.data.length = sizeof(*pevent) + sizeof(*p_data); |
| |
| wireless_send_event(vap->iv_dev, IWEVCUSTOM, &wreq, (char *)&event_data); |
| return 0; |
| } |
| |
| int ieee80211_bsa_connect_complete_event_send(struct ieee80211vap *vap,struct ieee80211_node *ni) |
| { |
| struct ieee80211com *ic = ni->ni_ic; |
| struct qtn_bsa_peer_event_data *p_data; |
| struct qtn_bsa_assoc_compl_event_info *pevent; |
| uint8_t event_data[IEEE80211_MAX_EVENT_DATA_LEN]; |
| struct ieee80211_ie_htcap *htcap_ie = NULL; |
| struct ieee80211_ie_vhtcap *vhtcap_ie = NULL; |
| uint32_t max_ht_phy_rate = 54; |
| uint32_t max_vht_phy_rate = 0; |
| uint32_t max_ht_ss = 1; |
| uint32_t max_vht_ss = 0; |
| uint32_t band_width = 1; |
| |
| union iwreq_data wreq; |
| p_data = (void *)event_data; |
| pevent = (void *)(event_data + sizeof(struct qtn_bsa_peer_event_data)); |
| strncpy(p_data->bsa_name, "BSA-PEER-EVENT", sizeof(p_data->bsa_name)); |
| put_unaligned(BSA_CONNECT_COMPLETE_EVENT, &p_data->bsa_event_id); |
| memcpy(p_data->bsa_bssid, ni->ni_bssid, IEEE80211_ADDR_LEN); |
| put_unaligned(sizeof(struct qtn_bsa_peer_event_data), &p_data->offset); |
| memcpy(pevent->bsa_sta_mac, ni->ni_macaddr, IEEE80211_ADDR_LEN); |
| |
| put_unaligned(ni->ni_rssi, &pevent->bsa_rssi); |
| |
| if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) |
| put_unaligned(BSA_OPER_BAND_5G, &pevent->bsa_curr_band); |
| else |
| put_unaligned(BSA_OPER_BAND_2G, &pevent->bsa_curr_band); |
| |
| put_unaligned(ic->ic_curchan->ic_ieee, &pevent->bsa_channel); |
| put_unaligned(0, &pevent->bsa_band_width); |
| pevent->bsa_vht_capab = 0; |
| pevent->bsa_mu_mimo_capab = 0; |
| |
| if (ni->ni_ext_flags & IEEE80211_NODE_BSS_TRANSITION) |
| pevent->bsa_bss_transition_support = 1; |
| else |
| pevent->bsa_bss_transition_support = 0; |
| if (ni->ni_flags & IEEE80211_NODE_HT) { |
| htcap_ie = (struct ieee80211_ie_htcap *)&ni->ni_ie_htcap; |
| max_ht_phy_rate = ieee80211_wlan_ht_rx_maxrate(htcap_ie, &max_ht_ss); |
| band_width = ((htcap_ie->hc_cap[0] & 0x2) >> 1); |
| pevent->bsa_vht_capab |= BSA_HT_SUPPORTED; |
| } |
| |
| if (ni->ni_flags & IEEE80211_NODE_VHT) { |
| vhtcap_ie = (struct ieee80211_ie_vhtcap * )&ni->ni_ie_vhtcap; |
| max_vht_phy_rate = ieee80211_wlan_vht_rx_maxrate(vhtcap_ie); |
| pevent->bsa_vht_capab |= BSA_VHT_SUPPORTED; |
| pevent->bsa_mu_mimo_capab = ((vhtcap_ie->vht_cap[2] & 0x18)>>3); |
| max_vht_ss = ieee80211_wlan_vht_rxstreams(vhtcap_ie); |
| band_width |= ((vhtcap_ie->vht_cap[0] & 0xc) >> 1); |
| } |
| put_unaligned(band_width, &pevent->bsa_band_width); |
| put_unaligned((max_vht_phy_rate > max_ht_phy_rate) ? max_vht_phy_rate : max_ht_phy_rate, |
| &pevent->bsa_max_phy_rate); |
| put_unaligned((max_ht_ss > max_vht_ss) ? max_ht_ss : max_vht_ss, &pevent->bsa_nss); |
| |
| memset(&wreq, 0, sizeof(wreq)); |
| wreq.data.length = sizeof(*pevent) + sizeof(*p_data); |
| wireless_send_event(vap->iv_dev, IWEVCUSTOM, &wreq, (char *)&event_data); |
| |
| return 0; |
| } |
| |
| static struct bsa_deny_sta * ieee80211_bsa_find_macfilter_list(struct ieee80211vap *vap, |
| uint8_t *macaddr) |
| { |
| struct bsa_deny_sta *bsa_mf = NULL; |
| |
| LIST_FOREACH(bsa_mf, &vap->deny_sta_list, list) { |
| if (IEEE80211_ADDR_EQ(bsa_mf->bsa_macaddr, macaddr)) |
| return bsa_mf; |
| } |
| return NULL; |
| } |
| |
| int ieee80211_bsa_macfilter_check(struct ieee80211vap *vap, uint8_t *mac) |
| { |
| if (ieee80211_bsa_find_macfilter_list(vap, mac) != NULL) |
| return 1; |
| |
| return 0; |
| } |
| |
| static int ieee80211_bsa_macfilter_add(struct ieee80211vap *vap, uint8_t *mac) |
| { |
| struct bsa_deny_sta *deny_sta, *new; |
| |
| LIST_FOREACH(deny_sta, &vap->deny_sta_list, list) { |
| if (IEEE80211_ADDR_EQ(deny_sta->bsa_macaddr, mac)) { |
| printk("BSA: add %s failed, already present\n",ether_sprintf(mac)); |
| return -EEXIST; |
| } |
| } |
| |
| MALLOC(new, struct bsa_deny_sta *, sizeof(struct bsa_deny_sta), M_80211_ACL, M_NOWAIT | M_ZERO); |
| |
| if (new == NULL) { |
| printk("BSA_MF: add %s failed, no memory\n", ether_sprintf(mac)); |
| return -ENOMEM; |
| } |
| |
| IEEE80211_ADDR_COPY(new->bsa_macaddr, mac); |
| LIST_INSERT_HEAD(&vap->deny_sta_list, new, list); |
| |
| return 0; |
| } |
| |
| static int ieee80211_bsa_macfilter_remove(struct ieee80211vap *vap, uint8_t *mac) |
| { |
| struct bsa_deny_sta *sta; |
| |
| sta = ieee80211_bsa_find_macfilter_list(vap, mac); |
| if (sta == NULL) |
| return -EFAULT; |
| |
| LIST_REMOVE(sta, list); |
| FREE(sta, M_80211_BSA_MF); |
| |
| IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, |
| "BSA_MF: remove %s%s\n", ether_sprintf(mac), |
| sta == NULL ? ", not present" : ""); |
| |
| return 0; |
| } |
| #endif |
| |
| /* Dot11Msg messages */ |
| char *d11_m[] = { |
| "Client connected", |
| "Client disconnected", |
| "Client authentication failed", |
| "Client removed", |
| "Connected to AP", |
| "Connection to AP failed", |
| "Disconnected from AP", |
| }; |
| EXPORT_SYMBOL(d11_m); |
| |
| /* Dot11Msg details */ |
| char *d11_c[] = { |
| "Disassociated", |
| "Deauthenticated", |
| "TKIP countermeasures invoked", |
| "Client timeout", |
| "WPA password failure", |
| "WPA timeout", |
| "Beacon loss", |
| "Client sent deauthentication", |
| "Client sent disassociation" |
| }; |
| EXPORT_SYMBOL(d11_c); |
| |
| /* Dot11Msg reason fields - directly taken from the Reason field in the 802.11 spec(s). */ |
| char *d11_r[] = { |
| "Reserved", |
| "Unspecified reason", |
| "Previous authentication no longer valid", |
| "Deauthenticated because sending STA is leaving (or has left) IBSS or ESS", |
| "Disassociated due to inactivity", |
| "Disassociated because AP is unable to handle all currently associated STAs", |
| "Class 2 frame received from nonauthenticated STA", |
| "Class 3 frame received from nonassociated STA", |
| "Disassociated because sending STA is leaving (or has left) BSS", |
| "STA requesting (re)association is not authenticated with responding STA", |
| "Disassociated because the information in the Power Capability element is unacceptable", |
| "Disassociated because the information in the Supported Channels element is unacceptable", |
| "Disassociated due to BSS Tranistion Management", |
| "Invalid information element", |
| "Message integrity code (MIC) failure", |
| "4-Way Handshake timeout", |
| "Group Key Handshake timeout", |
| "Information element in 4-Way Handshake different from (Re)Association Request/Probe Response/Beacon frame", |
| "Invalid group cipher", |
| "Invalid pairwise cipher", |
| "Invalid AKMP", |
| "Unsupported RSN information element version", |
| "Invalid RSN information element capabilities", |
| "IEEE 802.1X authentication failed", |
| "Cipher suite rejected because of the security policy", |
| "Reserved", |
| "Reserved", |
| "Reserved", |
| "Reserved", |
| "Reserved", |
| "Reserved", |
| "TS deleted because QoS AP lacks sufficient bandwidth due to change in BSS or operational mode", |
| "Disassociated for unspecified, QoS-related reason", |
| "Disassociated because QoS AP lacks sufficient bandwidth for this QoS STA", |
| "Disassociated because excessive number of frames need to be acknowledged, but are not acknowledged" |
| "Disassociated because STA is transmitting outside the limits of its TXOPs", |
| "Requested from peer STA as the STA is leaving hte BSS (or resetting)", |
| "Requested from peer STA as it does not want to use the mechanism", |
| "Requested from peer STA as the STA received frames using the mechanism for which a setup is required", |
| "Requested from peer STA due to timeout", |
| "Reserved", |
| "Reserved", |
| "Reserved", |
| "Reserved", |
| "Reserved", |
| "Peer STA does not support the requested cipher suite" |
| }; |
| EXPORT_SYMBOL(d11_r); |
| |
| static struct ieee80211_node *ieee80211_get_vap_node(struct ieee80211vap *vap) |
| { |
| struct ieee80211com *ic; |
| struct ieee80211_node *ni; |
| int ref_node = 1; |
| |
| if ((vap == NULL) || (vap->iv_ic == NULL)) |
| return NULL; |
| |
| ic = vap->iv_ic; |
| ni = vap->iv_bss; |
| |
| if ((ni == NULL) && (vap->iv_opmode == IEEE80211_M_WDS)) { |
| ni = ieee80211_find_wds_node(&ic->ic_sta, vap->wds_mac); |
| ref_node = (ni == NULL); |
| } |
| |
| if ((ni == NULL) && (TAILQ_FIRST(&ic->ic_vaps) != NULL)) |
| ni = TAILQ_FIRST(&ic->ic_vaps)->iv_bss; |
| |
| if ((ni != NULL) && ref_node) |
| ieee80211_ref_node(ni); |
| |
| return ni; |
| } |
| |