// SPDX-License-Identifier: GPL-2.0 /****************************************************************************** * * Copyright (C) 2020 SeekWave Technology Co.,Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation; * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * ******************************************************************************/ #include #include #include "skw_core.h" #include "skw_util.h" #include "skw_cfg80211.h" #ifdef CONFIG_PRINTK_TIME_FROM_ARM_ARCH_TIMER #include u64 skw_local_clock(void) { u64 ns; ns = arch_timer_read_counter() * 1000; do_div(ns, 24); return ns; } #else u64 skw_local_clock(void) { return local_clock(); } #endif #ifdef SKW_IMPORT_NS struct file *skw_file_open(const char *path, int flags, int mode) { struct file *fp = NULL; fp = filp_open(path, flags, mode); if (IS_ERR(fp)) { skw_err("open fail\n"); return NULL; } return fp; } int skw_file_read(struct file *fp, unsigned char *data, size_t size, loff_t offset) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) return kernel_read(fp, data, size, &offset); #else return kernel_read(fp, offset, data, size); #endif } int skw_file_write(struct file *fp, unsigned char *data, size_t size, loff_t offset) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) return kernel_write(fp, data, size, &offset); #else return kernel_write(fp, data, size, offset); #endif } int skw_file_sync(struct file *fp) { return vfs_fsync(fp, 0); } void skw_file_close(struct file *fp) { filp_close(fp, NULL); } MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); #endif void *skw_build_presp_frame(struct wiphy *wiphy, struct skw_iface *iface, u8 *da, u8 *sa, u8 *bssid, u8 *ssid, int ssid_len, u16 chan, struct ieee80211_supported_band *sband, u16 capa, u64 tsf, int beacon_int, void *ie, int ie_len) { u8 *pos; int i, rate; struct skw_template *temp; struct ieee80211_mgmt *mgmt; skw_dbg("ssid: %s, bssid: %pM\n", ssid, bssid); temp = SKW_ZALLOC(1600, GFP_KERNEL); if (!temp) return NULL; mgmt = temp->mgmt; mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP); memcpy(mgmt->sa, sa, ETH_ALEN); memcpy(mgmt->da, da, ETH_ALEN); memcpy(mgmt->bssid, bssid, ETH_ALEN); mgmt->u.beacon.beacon_int = cpu_to_le16(beacon_int); mgmt->u.beacon.timestamp = cpu_to_le64(tsf); mgmt->u.beacon.capab_info = cpu_to_le16(capa); pos = mgmt->u.beacon.variable; *pos++ = WLAN_EID_SSID; *pos++ = ssid_len; memcpy(pos, ssid, ssid_len); pos += ssid_len; *pos++ = WLAN_EID_SUPP_RATES; *pos++ = SKW_BASIC_RATE_COUNT; for (i = 0; i < SKW_BASIC_RATE_COUNT; i++) { rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, 5); if (sband->bitrates[i].flags & 0x1) rate |= 0x80; *pos++ = rate; } #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0) if (sband->band == IEEE80211_BAND_2GHZ) { #else if (sband->band == NL80211_BAND_2GHZ) { #endif *pos++ = WLAN_EID_DS_PARAMS; *pos++ = 1; *pos++ = chan; } if (iface->wdev.iftype == NL80211_IFTYPE_ADHOC) { *pos++ = WLAN_EID_IBSS_PARAMS; *pos++ = 2; *pos++ = 0; *pos++ = 0; } *pos++ = WLAN_EID_EXT_SUPP_RATES; *pos++ = sband->n_bitrates - SKW_BASIC_RATE_COUNT; for (i = SKW_BASIC_RATE_COUNT; i < sband->n_bitrates; i++) { rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, 5); if (sband->bitrates[i].flags & 0x1) rate |= 0x80; *pos++ = rate; } if (ie_len) { memcpy(pos, ie, ie_len); pos += ie_len; } return temp; } int skw_key_idx(u16 bitmap) { static u8 idx[] = {0xff, 0x00, 0x01, 0xff, 0x02, 0xff, 0xff, 0xff, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; return idx[bitmap & 0xf]; } int skw_build_deauth_frame(void *buf, int buf_len, u8 *da, u8 *sa, u8 *bssid, u16 reason_code) { u16 fc = IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH; struct ieee80211_mgmt *mgmt = buf; if (!buf || buf_len < SKW_DEAUTH_FRAME_LEN) return -EINVAL; mgmt->frame_control = cpu_to_le16(fc); mgmt->duration = 0; mgmt->seq_ctrl = 0; skw_ether_copy(mgmt->da, da); skw_ether_copy(mgmt->sa, sa); skw_ether_copy(mgmt->bssid, bssid); mgmt->u.deauth.reason_code = cpu_to_le16(reason_code); return SKW_DEAUTH_FRAME_LEN; } char *skw_mgmt_name(u16 fc) { #define SKW_STYPE_STR(n) {case IEEE80211_STYPE_##n: return #n; } switch (fc) { SKW_STYPE_STR(ASSOC_REQ); SKW_STYPE_STR(ASSOC_RESP); SKW_STYPE_STR(REASSOC_REQ); SKW_STYPE_STR(REASSOC_RESP); SKW_STYPE_STR(PROBE_REQ); SKW_STYPE_STR(PROBE_RESP); SKW_STYPE_STR(BEACON); SKW_STYPE_STR(ATIM); SKW_STYPE_STR(DISASSOC); SKW_STYPE_STR(AUTH); SKW_STYPE_STR(DEAUTH); SKW_STYPE_STR(ACTION); default: break; } #undef SKW_STYPE_STR return "UNDEFINE"; } int skw_freq_to_chn(int freq) { if (freq == 2484) return 14; else if (freq >= 2407 && freq < 2484) return (freq - 2407) / 5; else if (freq >= 4910 && freq <= 4980) return (freq - 4000) / 5; else if (freq >= 5000 && freq <= 45000) return (freq - 5000) / 5; else if (freq >= 58320 && freq <= 64800) return (freq - 56160) / 2160; else return 0; } u32 skw_calc_rate(u64 bytes, u32 delta_ms) { struct skw_tp_rate ret; u64 cal_bytes = bytes; u16 bps; u16 Kbps; u16 Mbps; u16 Gbps; u16 Tbps; ret.ret = 0; cal_bytes *= 8 * 1000; do_div(cal_bytes, delta_ms); bps = do_div(cal_bytes, 1 << 10); Kbps = do_div(cal_bytes, 1 << 10); Mbps = do_div(cal_bytes, 1 << 10); Gbps = do_div(cal_bytes, 1 << 10); Tbps = cal_bytes; if (Tbps) { ret.rate.value = Tbps; ret.rate.unit = 'T'; ret.rate.two_dec = Gbps * 100 >> 10; } else if (Gbps) { ret.rate.value = Gbps; ret.rate.unit = 'G'; ret.rate.two_dec = Mbps * 100 >> 10; } else if (Mbps) { ret.rate.value = Mbps; ret.rate.unit = 'M'; ret.rate.two_dec = Kbps * 100 >> 10; } else { ret.rate.value = Kbps; ret.rate.unit = 'K'; ret.rate.two_dec = bps * 100 >> 10; } return ret.ret; } static u32 skw_peer_rate(struct skw_stats_info *stat) { u64 total_bytes, total_jiffies; total_bytes = stat->bytes - stat->cal_bytes; stat->cal_bytes = stat->bytes; total_jiffies = jiffies - stat->cal_time; stat->cal_time = jiffies; return skw_calc_rate(total_bytes, jiffies_to_msecs(total_jiffies)); } int skw_tx_throughput(struct skw_iface *iface, u8 *mac) { int ret = 0; struct skw_peer_ctx *ctx; if (!iface) { ret = -ENOENT; goto exit; } if (!mac || is_broadcast_ether_addr(mac)) { ret = -EINVAL; goto exit; } ctx = skw_peer_ctx(iface, mac); if (!ctx) { ret = -ENOENT; goto exit; } skw_peer_ctx_lock(ctx); if (ctx->peer) ret = skw_peer_rate(&ctx->peer->tx); skw_peer_ctx_unlock(ctx); exit: return ret; } int skw_rx_throughput(struct skw_iface *iface, u8 *mac) { int ret = 0; struct skw_peer_ctx *ctx; if (!iface) { ret = -ENOENT; goto exit; } if (!mac || is_broadcast_ether_addr(mac)) { ret = -EINVAL; goto exit; } ctx = skw_peer_ctx(iface, mac); if (!ctx) { ret = -ENOENT; goto exit; } skw_peer_ctx_lock(ctx); if (ctx->peer) ret = skw_peer_rate(&ctx->peer->rx); skw_peer_ctx_unlock(ctx); exit: return ret; } int skw_tx_throughput_whole(struct skw_iface *iface, u32 *tp) { int ret = 0; u32 peer_idx_map, idx; struct skw_peer_ctx *ctx; if (!iface) { ret = -ENOENT; goto exit; } peer_idx_map = atomic_read(&iface->peer_map); while (peer_idx_map) { idx = ffs(peer_idx_map) - 1; SKW_CLEAR(peer_idx_map, BIT(idx)); ctx = &iface->skw->hw.lmac[iface->lmac_id].peer_ctx[idx]; skw_peer_ctx_lock(ctx); if (ctx->peer) *(&(tp[idx])) = skw_peer_rate(&ctx->peer->tx); skw_peer_ctx_unlock(ctx); } exit: return ret; } int skw_rx_throughput_whole(struct skw_iface *iface, u32 *tp) { int ret = 0; u32 peer_idx_map, idx; struct skw_peer_ctx *ctx; if (!iface) { ret = -ENOENT; goto exit; } peer_idx_map = atomic_read(&iface->peer_map); while (peer_idx_map) { idx = ffs(peer_idx_map) - 1; SKW_CLEAR(peer_idx_map, BIT(idx)); ctx = &iface->skw->hw.lmac[iface->lmac_id].peer_ctx[idx]; skw_peer_ctx_lock(ctx); if (ctx->peer) *(&(tp[idx])) = skw_peer_rate(&ctx->peer->rx); skw_peer_ctx_unlock(ctx); } exit: return ret; } const u8 *skw_find_ie_match(u8 eid, const u8 *ies, int len, const u8 *match, int match_len, int match_offset) { const struct skw_element *elem; /* match_offset can't be smaller than 2, unless match_len is * zero, in which case match_offset must be zero as well. */ if (WARN_ON((match_len && match_offset < 2) || (!match_len && match_offset))) return NULL; skw_foreach_element_id(elem, eid, ies, len) { if (elem->datalen >= match_offset - 2 + match_len && !memcmp(elem->data + match_offset - 2, match, match_len)) return (void *)elem; } return NULL; } bool skw_bss_check_vendor_name(struct cfg80211_bss *bss, const u8 *oui) { const struct cfg80211_bss_ies *ies; const struct skw_element *elem; bool ret = false; u8 eid = WLAN_EID_VENDOR_SPECIFIC; ies = rcu_dereference(bss->beacon_ies); if (!ies) return false; skw_hex_dump("beacon ies:", ies->data, ies->len, false); skw_foreach_element_id(elem, eid, ies->data, ies->len) { if (!memcmp(elem->data, oui, 3)) { ret = true; break; } } return ret; } int skw_desc_get_rx_rate(struct skw_rate *rate, u8 bw, u8 mode, u8 gi, u8 nss, u8 dcm, u16 data_rate) { u16 skw_supp_bs_rate[] = { 20, 55, 110, /*2M,5.5M,11M*/ }; u16 skw_supp_bl_rate[] = { 10, 20, 55, 110, /*1M,2M,5.5M,11M*/ }; u16 skw_supp_g_rate[] = { 60, 90, 120, 180, 240, 360, 480, 540, /*6M, 9M, 12M, 18M, 24M, 36M, 48M, 54M*/ }; memset(rate, 0x0, sizeof(struct skw_rate)); rate->bw = bw; rate->nss = nss; switch (mode) { case SKW_PPDUMODE_HT_MIXED: rate->flags = SKW_RATE_INFO_FLAGS_HT; rate->mcs_idx = 0x3F & data_rate; rate->gi = gi; break; case SKW_PPDUMODE_VHT_SU: case SKW_PPDUMODE_VHT_MU: rate->flags = SKW_RATE_INFO_FLAGS_VHT; rate->mcs_idx = 0xF & data_rate; rate->gi = gi; break; case SKW_PPDUMODE_HE_SU: case SKW_PPDUMODE_HE_TB: case SKW_PPDUMODE_HE_ER_SU: case SKW_PPDUMODE_HE_MU: rate->flags = SKW_RATE_INFO_FLAGS_HE; rate->mcs_idx = 0xF & data_rate; if (dcm) { rate->mcs_idx = 0x3 & data_rate; rate->he_dcm = dcm; } else if (mode == SKW_PPDUMODE_HE_ER_SU) { rate->mcs_idx = 0x3 & data_rate; } rate->gi = gi; if (bw != SKW_DESC_BW_USED_RU) rate->he_ru = bw + 3; break; case SKW_PPDUMODE_11B_SHORT: rate->flags = SKW_RATE_INFO_FLAGS_LEGACY; if (data_rate < ARRAY_SIZE(skw_supp_bs_rate)) rate->legacy_rate = skw_supp_bs_rate[data_rate]; else skw_warn("illegal 11B_SHORT rate:%d\n", data_rate); break; case SKW_PPDUMODE_11B_LONG: rate->flags = SKW_RATE_INFO_FLAGS_LEGACY; if (data_rate < ARRAY_SIZE(skw_supp_bl_rate)) rate->legacy_rate = skw_supp_bl_rate[data_rate]; else skw_warn("illegal 11B_LONG rate:%d\n", data_rate); break; case SKW_PPDUMODE_11G: rate->flags = SKW_RATE_INFO_FLAGS_LEGACY; if (data_rate < ARRAY_SIZE(skw_supp_g_rate)) rate->legacy_rate = skw_supp_g_rate[data_rate]; else skw_warn("illegal 11G rate:%d\n", data_rate); break; default: skw_warn("unsupport ppdu mode:%d\n", mode); break; }; return 0; } int skw_tlv_add(struct skw_tlv_conf *conf, int type, void *dat, int dat_len) { struct skw_tlv *tlv; if (!conf || !conf->buff) return -EINVAL; if (conf->total_len + dat_len + 4 > conf->buff_len) return -ENOMEM; tlv = (struct skw_tlv *)(conf->buff + conf->total_len); tlv->type = type; tlv->len = dat_len; memcpy(tlv->value, dat, dat_len); conf->total_len += dat_len + 4; return 0; } int skw_tlv_alloc(struct skw_tlv_conf *conf, int len, gfp_t gfp) { if (!conf) return -EINVAL; conf->buff = SKW_ZALLOC(len, GFP_KERNEL); if (!conf->buff) return -ENOMEM; conf->total_len = 0; conf->buff_len = len; return 0; } void *skw_tlv_reserve(struct skw_tlv_conf *conf, int len) { void *start = NULL; if (!conf || !conf->buff) return NULL; if (conf->total_len + len > conf->buff_len) return NULL; start = conf->buff + conf->total_len; conf->total_len += len; return start; } void skw_tlv_free(struct skw_tlv_conf *conf) { if (conf) { SKW_KFREE(conf->buff); conf->total_len = 0; conf->buff_len = 0; } } int skw_util_set_mib_enable(struct wiphy *wiphy, int inst, int mib_id, bool enable) { int ret; u16 *plen; struct skw_tlv_conf conf; bool state = !!enable; skw_dbg("set %d mib:%d\n", mib_id, enable); ret = skw_tlv_alloc(&conf, 128, GFP_KERNEL); if (ret) { skw_err("alloc failed\n"); return ret; } plen = skw_tlv_reserve(&conf, 2); if (!plen) { skw_err("reserve failed\n"); skw_tlv_free(&conf); return -ENOMEM; } if (skw_tlv_add(&conf, mib_id, &state, 1)) { skw_err("set mib failed\n"); skw_tlv_free(&conf); return -EINVAL; } *plen = conf.total_len; ret = skw_msg_xmit(wiphy, inst, SKW_CMD_SET_MIB, conf.buff, conf.total_len, NULL, 0); if (ret) skw_warn("failed, ret: %d\n", ret); skw_tlv_free(&conf); return ret; }