blob: 318d62baabf6400aafda770659b3b3b315256d43 [file] [log] [blame]
/*
* Copyright (c) 2017-2019 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/**
* DOC: This file contains definition for mandatory legacy API
*/
#include "qdf_str.h"
#include "wlan_utility.h"
#include <wlan_cmn.h>
#include "wlan_osif_priv.h"
#include <net/cfg80211.h>
#include <qdf_module.h>
#include <wlan_vdev_mlme_api.h>
uint32_t wlan_chan_to_freq(uint8_t chan)
{
/* ch 0 - ch 13 */
if (chan < WLAN_24_GHZ_CHANNEL_14)
return WLAN_24_GHZ_BASE_FREQ + chan * WLAN_CHAN_SPACING_5MHZ;
else if (chan == WLAN_24_GHZ_CHANNEL_14)
return WLAN_CHAN_14_FREQ;
else if (chan < WLAN_24_GHZ_CHANNEL_27)
/* ch 15 - ch 26 */
return WLAN_CHAN_15_FREQ +
(chan - WLAN_24_GHZ_CHANNEL_15) * WLAN_CHAN_SPACING_20MHZ;
else if (chan == WLAN_5_GHZ_CHANNEL_170)
return WLAN_CHAN_170_FREQ;
else
return WLAN_5_GHZ_BASE_FREQ + chan * WLAN_CHAN_SPACING_5MHZ;
}
uint8_t wlan_freq_to_chan(uint32_t freq)
{
uint8_t chan;
if (freq > WLAN_24_GHZ_BASE_FREQ && freq < WLAN_CHAN_14_FREQ)
chan = ((freq - WLAN_24_GHZ_BASE_FREQ) /
WLAN_CHAN_SPACING_5MHZ);
else if (freq == WLAN_CHAN_14_FREQ)
chan = WLAN_24_GHZ_CHANNEL_14;
else if ((freq > WLAN_24_GHZ_BASE_FREQ) &&
(freq < WLAN_5_GHZ_BASE_FREQ))
chan = (((freq - WLAN_CHAN_15_FREQ) /
WLAN_CHAN_SPACING_20MHZ) +
WLAN_24_GHZ_CHANNEL_15);
else
chan = (freq - WLAN_5_GHZ_BASE_FREQ) /
WLAN_CHAN_SPACING_5MHZ;
return chan;
}
bool wlan_is_ie_valid(const uint8_t *ie, size_t ie_len)
{
uint8_t elen;
while (ie_len) {
if (ie_len < 2)
return false;
elen = ie[1];
ie_len -= 2;
ie += 2;
if (elen > ie_len)
return false;
ie_len -= elen;
ie += elen;
}
return true;
}
static const uint8_t *wlan_get_ie_ptr_from_eid_n_oui(uint8_t eid,
const uint8_t *oui,
uint8_t oui_size,
const uint8_t *ie,
uint16_t ie_len)
{
int32_t left = ie_len;
const uint8_t *ptr = ie;
uint8_t elem_id, elem_len;
while (left >= 2) {
elem_id = ptr[0];
elem_len = ptr[1];
left -= 2;
if (elem_len > left)
return NULL;
if (eid == elem_id) {
/* if oui is not provide eid match is enough */
if (!oui)
return ptr;
/*
* if oui is provided and oui_size is more than left
* bytes, then we cannot have match
*/
if (oui_size > left)
return NULL;
if (qdf_mem_cmp(&ptr[2], oui, oui_size) == 0)
return ptr;
}
left -= elem_len;
ptr += (elem_len + 2);
}
return NULL;
}
const uint8_t *wlan_get_ie_ptr_from_eid(uint8_t eid,
const uint8_t *ie,
int ie_len)
{
return wlan_get_ie_ptr_from_eid_n_oui(eid, NULL, 0, ie, ie_len);
}
const uint8_t *wlan_get_vendor_ie_ptr_from_oui(const uint8_t *oui,
uint8_t oui_size,
const uint8_t *ie,
uint16_t ie_len)
{
return wlan_get_ie_ptr_from_eid_n_oui(WLAN_MAC_EID_VENDOR,
oui, oui_size, ie, ie_len);
}
const uint8_t *wlan_get_ext_ie_ptr_from_ext_id(const uint8_t *oui,
uint8_t oui_size,
const uint8_t *ie,
uint16_t ie_len)
{
return wlan_get_ie_ptr_from_eid_n_oui(WLAN_MAC_EID_EXT,
oui, oui_size, ie, ie_len);
}
bool wlan_is_emulation_platform(uint32_t phy_version)
{
if ((phy_version == 0xABC0) || (phy_version == 0xABC1) ||
(phy_version == 0xABC2) || (phy_version == 0xABC3) ||
(phy_version == 0xFFFF) || (phy_version == 0xABCD))
return true;
return false;
}
uint32_t wlan_get_pdev_id_from_vdev_id(struct wlan_objmgr_psoc *psoc,
uint8_t vdev_id,
wlan_objmgr_ref_dbgid dbg_id)
{
struct wlan_objmgr_vdev *vdev;
struct wlan_objmgr_pdev *pdev = NULL;
uint32_t pdev_id = WLAN_INVALID_PDEV_ID;
vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc,
vdev_id, dbg_id);
if (vdev) {
pdev = wlan_vdev_get_pdev(vdev);
if (pdev)
pdev_id = wlan_objmgr_pdev_get_pdev_id(pdev);
wlan_objmgr_vdev_release_ref(vdev, dbg_id);
}
return pdev_id;
}
qdf_export_symbol(wlan_get_pdev_id_from_vdev_id);
static void wlan_vdev_active(struct wlan_objmgr_pdev *pdev, void *object,
void *arg)
{
struct wlan_objmgr_vdev *vdev = (struct wlan_objmgr_vdev *)object;
uint8_t *flag = (uint8_t *)arg;
wlan_vdev_obj_lock(vdev);
if (wlan_vdev_mlme_is_active(vdev) == QDF_STATUS_SUCCESS)
*flag = 1;
wlan_vdev_obj_unlock(vdev);
}
QDF_STATUS wlan_vdev_is_up(struct wlan_objmgr_vdev *vdev)
{
return wlan_vdev_allow_connect_n_tx(vdev);
}
qdf_export_symbol(wlan_vdev_is_up);
QDF_STATUS wlan_util_is_vdev_active(struct wlan_objmgr_pdev *pdev,
wlan_objmgr_ref_dbgid dbg_id)
{
uint8_t flag = 0;
if (!pdev)
return QDF_STATUS_E_INVAL;
wlan_objmgr_pdev_iterate_obj_list(pdev, WLAN_VDEV_OP, wlan_vdev_active,
&flag, 0, dbg_id);
if (flag == 1)
return QDF_STATUS_SUCCESS;
return QDF_STATUS_E_INVAL;
}
qdf_export_symbol(wlan_util_is_vdev_active);
void wlan_util_change_map_index(unsigned long *map, uint8_t id, uint8_t set)
{
if (set)
qdf_set_bit(id, map);
else
qdf_clear_bit(id, map);
}
bool wlan_util_map_index_is_set(unsigned long *map, uint8_t id)
{
return qdf_test_bit(id, map);
}
static void wlan_vdev_chan_change_pending(struct wlan_objmgr_pdev *pdev,
void *object, void *arg)
{
struct wlan_objmgr_vdev *vdev = (struct wlan_objmgr_vdev *)object;
unsigned long *vdev_id_map = (unsigned long *)arg;
uint8_t id = 0;
struct wlan_objmgr_psoc *psoc;
psoc = wlan_pdev_get_psoc(pdev);
if (!psoc)
return;
wlan_vdev_obj_lock(vdev);
if (wlan_vdev_chan_config_valid(vdev) == QDF_STATUS_SUCCESS) {
id = wlan_vdev_get_id(vdev);
/* Invalid vdev id */
if (id >= wlan_psoc_get_max_vdev_count(psoc)) {
wlan_vdev_obj_unlock(vdev);
return;
}
wlan_util_change_map_index(vdev_id_map, id, 1);
}
wlan_vdev_obj_unlock(vdev);
}
QDF_STATUS wlan_pdev_chan_change_pending_vdevs(struct wlan_objmgr_pdev *pdev,
unsigned long *vdev_id_map,
wlan_objmgr_ref_dbgid dbg_id)
{
if (!pdev)
return QDF_STATUS_E_INVAL;
wlan_objmgr_pdev_iterate_obj_list(pdev, WLAN_VDEV_OP,
wlan_vdev_chan_change_pending,
vdev_id_map, 0, dbg_id);
return QDF_STATUS_SUCCESS;
}
QDF_STATUS wlan_chan_eq(struct wlan_channel *chan1, struct wlan_channel *chan2)
{
if ((chan1->ch_ieee == chan2->ch_ieee) &&
(chan1->ch_freq_seg2 == chan2->ch_freq_seg2))
return QDF_STATUS_SUCCESS;
return QDF_STATUS_E_FAILURE;
}
void wlan_chan_copy(struct wlan_channel *tgt, struct wlan_channel *src)
{
qdf_mem_copy(tgt, src, sizeof(struct wlan_channel));
}
struct wlan_channel *wlan_vdev_get_active_channel(struct wlan_objmgr_vdev *vdev)
{
struct wlan_channel *comp_vdev_chan = NULL;
if (wlan_vdev_chan_config_valid(vdev) == QDF_STATUS_SUCCESS) {
/* compare with BSS channel, when vdev is active, since desired
* channel gets update, if channel is triggered in another path
*/
if (wlan_vdev_mlme_is_active(vdev) == QDF_STATUS_SUCCESS)
comp_vdev_chan = wlan_vdev_mlme_get_bss_chan(vdev);
else
comp_vdev_chan = wlan_vdev_mlme_get_des_chan(vdev);
}
return comp_vdev_chan;
}
static void wlan_pdev_chan_match(struct wlan_objmgr_pdev *pdev, void *object,
void *arg)
{
struct wlan_objmgr_vdev *comp_vdev = (struct wlan_objmgr_vdev *)object;
struct wlan_vdev_ch_check_filter *ch_filter = arg;
struct wlan_channel *vdev_chan;
struct wlan_channel *iter_vdev_chan;
if (ch_filter->flag)
return;
if (comp_vdev == ch_filter->vdev)
return;
wlan_vdev_obj_lock(comp_vdev);
wlan_vdev_obj_lock(ch_filter->vdev);
vdev_chan = wlan_vdev_get_active_channel(comp_vdev);
if (vdev_chan) {
iter_vdev_chan = wlan_vdev_mlme_get_des_chan(
ch_filter->vdev);
if (wlan_chan_eq(vdev_chan, iter_vdev_chan)
!= QDF_STATUS_SUCCESS) {
ch_filter->flag = 1;
qdf_nofl_err("==> iter vdev id: %d: ieee %d, mode %d",
wlan_vdev_get_id(comp_vdev),
vdev_chan->ch_ieee,
vdev_chan->ch_phymode);
qdf_nofl_err("fl %016llx, fl-ext %08x, s1 %d, s2 %d ",
vdev_chan->ch_flags, vdev_chan->ch_flagext,
vdev_chan->ch_freq_seg1,
vdev_chan->ch_freq_seg2);
qdf_nofl_err("==> base vdev id: %d: ieee %d mode %d",
wlan_vdev_get_id(ch_filter->vdev),
iter_vdev_chan->ch_ieee,
iter_vdev_chan->ch_phymode);
qdf_nofl_err("fl %016llx, fl-ext %08x s1 %d, s2 %d",
iter_vdev_chan->ch_flags,
iter_vdev_chan->ch_flagext,
iter_vdev_chan->ch_freq_seg1,
iter_vdev_chan->ch_freq_seg2);
}
}
wlan_vdev_obj_unlock(ch_filter->vdev);
wlan_vdev_obj_unlock(comp_vdev);
}
QDF_STATUS wlan_util_pdev_vdevs_deschan_match(struct wlan_objmgr_pdev *pdev,
struct wlan_objmgr_vdev *vdev,
wlan_objmgr_ref_dbgid dbg_id)
{
struct wlan_vdev_ch_check_filter ch_filter;
if (!pdev)
return QDF_STATUS_E_INVAL;
if (wlan_pdev_nif_feat_cap_get(pdev, WLAN_PDEV_F_CHAN_CONCURRENCY))
return QDF_STATUS_SUCCESS;
if (wlan_objmgr_vdev_try_get_ref(vdev, dbg_id) == QDF_STATUS_SUCCESS) {
ch_filter.flag = 0;
ch_filter.vdev = vdev;
wlan_objmgr_pdev_iterate_obj_list(pdev, WLAN_VDEV_OP,
wlan_pdev_chan_match,
&ch_filter, 0, dbg_id);
wlan_objmgr_vdev_release_ref(vdev, dbg_id);
if (ch_filter.flag == 0)
return QDF_STATUS_SUCCESS;
}
return QDF_STATUS_E_FAILURE;
}
static void wlan_vdev_restart_progress(struct wlan_objmgr_pdev *pdev,
void *object, void *arg)
{
struct wlan_objmgr_vdev *vdev = (struct wlan_objmgr_vdev *)object;
uint8_t *flag = (uint8_t *)arg;
wlan_vdev_obj_lock(vdev);
if (wlan_vdev_is_restart_progress(vdev) == QDF_STATUS_SUCCESS)
*flag = 1;
wlan_vdev_obj_unlock(vdev);
}
QDF_STATUS wlan_util_is_pdev_restart_progress(struct wlan_objmgr_pdev *pdev,
wlan_objmgr_ref_dbgid dbg_id)
{
uint8_t flag = 0;
if (!pdev)
return QDF_STATUS_E_INVAL;
wlan_objmgr_pdev_iterate_obj_list(pdev, WLAN_VDEV_OP,
wlan_vdev_restart_progress,
&flag, 0, dbg_id);
if (flag == 1)
return QDF_STATUS_SUCCESS;
return QDF_STATUS_E_INVAL;
}
static void wlan_vdev_scan_allowed(struct wlan_objmgr_pdev *pdev, void *object,
void *arg)
{
struct wlan_objmgr_vdev *vdev = (struct wlan_objmgr_vdev *)object;
uint8_t *flag = (uint8_t *)arg;
wlan_vdev_obj_lock(vdev);
if (wlan_vdev_mlme_is_scan_allowed(vdev) != QDF_STATUS_SUCCESS)
*flag = 1;
wlan_vdev_obj_unlock(vdev);
}
QDF_STATUS wlan_util_is_pdev_scan_allowed(struct wlan_objmgr_pdev *pdev,
wlan_objmgr_ref_dbgid dbg_id)
{
uint8_t flag = 0;
if (!pdev)
return QDF_STATUS_E_INVAL;
wlan_objmgr_pdev_iterate_obj_list(pdev, WLAN_VDEV_OP,
wlan_vdev_scan_allowed,
&flag, 0, dbg_id);
if (flag == 1)
return QDF_STATUS_E_FAILURE;
return QDF_STATUS_SUCCESS;
}
void
wlan_util_stats_get_rssi(bool db2dbm_enabled, int32_t bcn_snr, int32_t dat_snr,
int8_t *rssi)
{
uint32_t snr;
if (db2dbm_enabled) {
if (TGT_IS_VALID_RSSI(bcn_snr))
*rssi = bcn_snr;
else if (TGT_IS_VALID_RSSI(dat_snr))
*rssi = dat_snr;
else
*rssi = TGT_NOISE_FLOOR_DBM;
} else {
if (TGT_IS_VALID_SNR(bcn_snr))
snr = bcn_snr;
else if (TGT_IS_VALID_SNR(dat_snr))
snr = dat_snr;
else
snr = TGT_INVALID_SNR;
/* Get the absolute rssi value from the current rssi value */
*rssi = snr + TGT_NOISE_FLOOR_DBM;
}
}
/**
* wlan_util_get_mode_specific_peer_count - This api gives vdev mode specific
* peer count`
* @pdev: PDEV object
* @object: vdev object
* @arg: argument passed by caller
*
* Return: void
*/
static void
wlan_util_get_mode_specific_peer_count(struct wlan_objmgr_pdev *pdev,
void *object, void *arg)
{
struct wlan_objmgr_vdev *vdev = object;
uint16_t temp_count = 0;
struct wlan_op_mode_peer_count *count = arg;
wlan_vdev_obj_lock(vdev);
if (wlan_vdev_mlme_get_opmode(vdev) == count->opmode) {
temp_count = wlan_vdev_get_peer_count(vdev);
/* Decrement the self peer count */
if (temp_count > 1)
count->peer_count += (temp_count - 1);
}
wlan_vdev_obj_unlock(vdev);
}
uint16_t wlan_util_get_peer_count_for_mode(struct wlan_objmgr_pdev *pdev,
enum QDF_OPMODE mode)
{
struct wlan_op_mode_peer_count count;
count.opmode = mode;
count.peer_count = 0;
wlan_objmgr_pdev_iterate_obj_list(pdev, WLAN_VDEV_OP,
wlan_util_get_mode_specific_peer_count, &count,
0, WLAN_OBJMGR_ID);
return count.peer_count;
}