luckfox-pico-sdk/sysdrv/drv_ko/wifi/ssv6115/fmac/fmac_utils.c
luckfox-eng29 8f34c2760d project:build.sh: Added fastboot support; custom modifications to U-Boot and kernel implemented using patches.
project:cfg:BoardConfig_IPC: Added fastboot BoardConfig file and firmware post-scripts, distinguishing between
the BoardConfigs for Luckfox Pico Pro and Luckfox Pico Max. project:app: Added fastboot_client and rk_smart_door
for quick boot applications; updated rkipc app to adapt to the latest media library. media:samples: Added more
usage examples. media:rockit: Fixed bugs; removed support for retrieving data frames from VPSS. media:isp:
Updated rkaiq library and related tools to support connection to RKISP_Tuner. sysdrv:Makefile: Added support for
compiling drv_ko on Luckfox Pico Ultra W using Ubuntu; added support for custom root filesystem.
sysdrv:tools:board: Updated Buildroot optional mirror sources, updated some software versions, and stored device
tree files and configuration files that undergo multiple modifications for U-Boot and kernel separately.
sysdrv:source:mcu: Used RISC-V MCU SDK with RT-Thread system, mainly for initializing camera AE during quick
boot. sysdrv:source:uboot: Added support for fastboot; added high baud rate DDR bin for serial firmware upgrades.
sysdrv:source:kernel: Upgraded to version 5.10.160; increased NPU frequency for RV1106G3; added support for
fastboot.

Signed-off-by: luckfox-eng29 <eng29@luckfox.com>
2024-10-14 09:47:04 +08:00

509 lines
15 KiB
C

/*
* Copyright (c) 2021 iComm-semi Ltd.
*
* 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.
*/
/**
* @file fmac_cmds.c
* @brief Handles queueing (push to IPC, ack/cfm from IPC) of commands issued to LMAC FW
*/
/*******************************************************************************
* Include Files
******************************************************************************/
#include <linux/device.h>
#include <linux/dmapool.h>
#include <linux/moduleparam.h>
#include <net/cfg80211.h>
#include "fmac.h"
#include "fmac_utils.h"
#include "fmac_defs.h"
#include "fmac_rx.h"
#include "fmac_tx.h"
#include "fmac_msg_rx.h"
#include "hci/drv_hci_ops.h"
#include "ssv_debug.h"
#include "fmac_msg_tx.h"
/*******************************************************************************
* Local Defines
******************************************************************************/
/*******************************************************************************
* Local Enumerations
******************************************************************************/
/*******************************************************************************
* Local Structures
******************************************************************************/
/*******************************************************************************
* Global Variables
******************************************************************************/
/*******************************************************************************
* Local Variables
******************************************************************************/
/*******************************************************************************
* Local Functions
******************************************************************************/
/**
*
*/
static int _ssv_dbginfo_allocs(struct ssv_softc *sc)
{
return 0;
}
/**
*
*/
static void ssv_dbginfo_deallocs(struct ssv_softc *sc)
{
}
/**
* @brief Deallocate storage elements.
*
* This function deallocates all the elements required for communications with LMAC,
* such as Rx Data elements, MSGs elements, ...
*
* This function should be called in correspondence with the allocation function.
*
* @param[in] sc Pointer to main structure storing all the relevant information
*/
static void ssv_elems_deallocs(struct ssv_softc *sc)
{
SSV_LOG_DBG("[%s][%d]\n", __FUNCTION__, __LINE__);
ssv_dbginfo_deallocs(sc);
}
/**
* @brief Allocate storage elements.
*
* This function allocates all the elements required for communications with LMAC,
* such as Rx Data elements, MSGs elements, ...
*
* This function should be called in correspondence with the deallocation function.
*
* @param[in] sc Pointer to main structure storing all the relevant information
*/
static int ssv_elems_allocs(struct ssv_softc *sc)
{
SSV_LOG_DBG("[%s][%d]\n", __FUNCTION__, __LINE__);
/* Initialize the debug information buffer */
if (_ssv_dbginfo_allocs(sc)) {
SSV_LOG_ERR("%s:%d: ALLOCATIONS FAILED !\n", __func__,
__LINE__);
goto err_alloc;
}
return 0;
err_alloc:
ssv_elems_deallocs(sc);
return -ENOMEM;
}
/*******************************************************************************
* Global Functions
******************************************************************************/
/**
* WLAN driver call-back function for message reception indication
*/
u8 ssv_msgind(void *pthis, void *hostid)
{
struct ssv_softc *sc = (struct ssv_softc *)pthis;
struct ipc_e2a_msg *msg = (struct ipc_e2a_msg *)hostid;
// SSV_LOG_DBG("[%s][%d]\n", __FUNCTION__, __LINE__);
/* Relay further actions to the msg parser */
if(0==ssv_rx_handle_msg(sc, msg))
{
return 0;
}
else
{
return 1;
}
}
/**
* FIXME
*
*/
u8 ssv_msgackind(void *pthis, void *hostid)
{
struct ssv_softc *sc = (struct ssv_softc *)pthis;
sc->cmd_mgr.llind(&sc->cmd_mgr, (struct ssv_cmd *)hostid);
return 0;
}
int ssv_ipc_init(struct ssv_softc *sc)
{
struct ipc_host_cb_tag cb;
SSV_LOG_DBG("[%s][%d]\n", __FUNCTION__, __LINE__);
/* initialize the API interface */
cb.recv_msg_ind = ssv_msgind;
cb.recv_msgack_ind = ssv_msgackind;
/* set the IPC environment */
sc->ipc_env = (struct ipc_host_env_tag *)
kzalloc(sizeof(struct ipc_host_env_tag), GFP_KERNEL);
/* call the initialization of the IPC */
ssv_ipc_host_init(sc->ipc_env, &cb, sc);
ssv_cmd_mgr_init(&sc->cmd_mgr);
return ssv_elems_allocs(sc);
}
/**
*
*/
void ssv_ipc_deinit(struct ssv_softc *sc)
{
SSV_LOG_DBG("[%s][%d]\n", __FUNCTION__, __LINE__);
ssv_cmd_mgr_deinit(&sc->cmd_mgr);
ssv_elems_deallocs(sc);
kfree(sc->ipc_env);
sc->ipc_env = NULL;
}
void ssv_error_ind(struct ssv_softc *sc)
{
#if 0
SSV_LOG_DBG("%s (type %d): dump received\n", __func__,
sc->dbginfo.buf->dbg_info.error_type);
#endif
// sc->debugfs.trace_prst = true;
}
/**
* Link channel ctxt to a vif and thus increments count for this context.
*/
void ssv_chanctx_link(struct ssv_vif *vif, u8 ch_idx,
struct cfg80211_chan_def *chandef)
{
struct ssv_chanctx *ctxt;
if (ch_idx >= NX_CHAN_CTXT_CNT) {
WARN(1, "Invalid channel ctxt id %d", ch_idx);
return;
}
vif->ch_index = ch_idx;
ctxt = &vif->sc->chanctx_table[ch_idx];
ctxt->count++;
// For now chandef is NULL for STATION interface
if (chandef) {
if (!ctxt->chan_def.chan)
ctxt->chan_def = *chandef;
else {
// TODO. check that chandef is the same as the one already
// set for this ctxt
}
}
}
/**
* Unlink channel ctxt from a vif and thus decrements count for this context
*/
void ssv_chanctx_unlink(struct ssv_vif *vif)
{
struct ssv_chanctx *ctxt;
if (vif->ch_index == SSV_CH_NOT_SET)
return;
ctxt = &vif->sc->chanctx_table[vif->ch_index];
if (ctxt->count == 0) {
WARN(1, "Chan ctxt ref count is already 0");
} else {
ctxt->count--;
}
if (ctxt->count == 0) {
/* set chan to null, so that if this ctxt is relinked to a vif that
don't have channel information, don't use wrong information */
ctxt->chan_def.chan = NULL;
}
vif->ch_index = SSV_CH_NOT_SET;
}
int ssv_chanctx_valid(struct ssv_softc *sc, u8 ch_idx)
{
if (ch_idx >= NX_CHAN_CTXT_CNT ||
sc->chanctx_table[ch_idx].chan_def.chan == NULL) {
return 0;
}
return 1;
}
void ssv_rxtput_calculation(struct ssv_softc *sc, u32 len, u32 seq_no)
{
unsigned long rx_result = 0;
if (seq_no == 0) {
sc->rx_evt_size = len;
sc->throughput_timestamp = jiffies;
rx_result = 0;
} else {
sc->rx_evt_size += len;
if (time_after(jiffies, (sc->throughput_timestamp + msecs_to_jiffies(1000)))) {
rx_result = ((sc->rx_evt_size << 3) / jiffies_to_msecs(jiffies - sc->throughput_timestamp));
SSV_LOG_DBG("data[%ld] RX throughput %ld Kbps\n", sc->rx_evt_size, rx_result);
sc->throughput_timestamp = jiffies;
sc->rx_evt_size = 0;
}
}
}
extern void ssv_update_mgmt_txdesc(struct ssv_vif *vif, struct ssv_sta *sta,
struct sk_buff *skb, bool robust, bool no_cck);
int ssv_xmit_deauth_frame(struct ssv_softc *sc, struct ssv_vif *vif, struct ssv_sta *sta,
u8 *da, u8 *sa, u8 *bssid, u16 reason)
{
#define IEEE80211_DEAUTH_FRAME_LEN (24 /* hdr */ + 2 /* reason */)
struct ieee80211_mgmt *mgmt = NULL;
struct sk_buff *skb=NULL;
u8 *data = NULL;
struct txdesc_api *desc = NULL;
int mgmt_txq = (0 == vif->drv_vif_index) ? SSV_SW_TXQ_ID_MNG0 : SSV_SW_TXQ_ID_MNG1;
skb = dev_alloc_skb(SSV_TX_HDR_SIZE + IEEE80211_DEAUTH_FRAME_LEN);
if(!skb){
return -1;
}
/*
* Move skb->data pointer in order to reserve room for ssv_txhdr
* headroom value will be equal to sizeof(struct ssv_txhdr)
*/
skb_reserve(skb, SSV_TX_HDR_SIZE);
/*
* Extend the buffer data area in order to contain the provided packet
* len value (for skb) will be equal to param->len
*/
data = skb_put(skb, IEEE80211_DEAUTH_FRAME_LEN);
mgmt=(struct ieee80211_mgmt *)data;
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH);
mgmt->duration = 0; /* initialize only */
mgmt->seq_ctrl = 0; /* initialize only */
memcpy(mgmt->da, da, ETH_ALEN);
memcpy(mgmt->sa, sa, ETH_ALEN);
memcpy(mgmt->bssid, bssid, ETH_ALEN);
/* u.deauth.reason_code == u.disassoc.reason_code */
mgmt->u.deauth.reason_code = cpu_to_le16(reason);
ssv_update_mgmt_txdesc(vif, sta, skb, 0, 0);
desc = (struct txdesc_api *)((u8 *)skb->data + sizeof(struct sdio_hdr) + sizeof(struct tx_bmu_desc));
desc->host.sw_ack = 0;
//ssv_hex_dump(skb->data,SSV_TX_HDR_SIZE + IEEE80211_DEAUTH_FRAME_LEN);
#if 1
if(-1 == ssv_fmac_hci_tx(sc, skb, mgmt_txq, false, 0))
{
return -EINVAL;
}
#else
dev_kfree_skb_any(skb);
#endif
return 0;
}
struct sk_buff *ssv_build_nulldata_frame(u8 *da, u8 *sa, u8 *bssid, bool qos, bool fromds)
{
struct ieee80211_qos_hdr *nullfunc;
struct sk_buff *skb;
int size = sizeof(struct ieee80211_qos_hdr);
__le16 fc;
int max_headroom = SSV_TX_HDR_SIZE;
u8 *data;
if (qos) {
fc = cpu_to_le16(IEEE80211_FTYPE_DATA |
IEEE80211_STYPE_QOS_NULLFUNC |
(fromds ? IEEE80211_FCTL_FROMDS : IEEE80211_FCTL_TODS));
} else {
size -= 2;
fc = cpu_to_le16(IEEE80211_FTYPE_DATA |
IEEE80211_STYPE_NULLFUNC |
(fromds ? IEEE80211_FCTL_FROMDS : IEEE80211_FCTL_TODS));
}
skb = dev_alloc_skb(max_headroom + size);
if (!skb) {
SSV_LOG_DBG("Cannot alloc probe station packet\n");
return NULL;
}
skb_reserve(skb, max_headroom);
data = skb_put(skb, size);
nullfunc = (struct ieee80211_qos_hdr *)data;
nullfunc->frame_control = fc;
nullfunc->duration_id = 0;
memcpy(nullfunc->addr1, da, ETH_ALEN);
memcpy(nullfunc->addr2, sa, ETH_ALEN);
memcpy(nullfunc->addr3, bssid, ETH_ALEN);
nullfunc->seq_ctrl = 0;
if (qos)
nullfunc->qos_ctrl = cpu_to_le16(7);
return skb;
}
int ssv_fmac_hci_tx(struct ssv_softc *sc, struct sk_buff *skb, int txqid, bool force_trigger, u32 tx_flags)
{
if ((NULL == sc->hci_priv) || (NULL == sc->hci_ops->hci_tx))
return -1;
if(sc->pktrec != NULL) {
if(((sc->dump_level & 0x2)==0x2) ||((sc->dump_level & 0x8)==0x8)) {
sc->pktrec(NULL, skb);
}
}
return ssv_drv_hci_tx(sc->hci_priv, sc->hci_ops, skb, txqid, force_trigger, tx_flags);
}
#ifndef WLAN_REASON_STA_CHANNEL_CHANGE
#define WLAN_REASON_STA_CHANNEL_CHANGE 9999
#endif
//softap_send_deauth_to_hostapd, hostapd will disable/enable iface
static void ssv6xxx_softap_send_deauth_to_hostapd(struct ssv_vif *vif, u16 freq, enum nl80211_channel_type chann_type)
{
u8 mac[ETH_ALEN] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55};
struct sk_buff *skb;
struct ieee80211_mgmt *deauth;
skb = dev_alloc_skb(sizeof(struct ieee80211_mgmt) + 64);
if(skb == NULL) {
SSV_LOG_DBG("[%s][%d] : deauth skb allocate fail!\n", __FUNCTION__, __LINE__);
return;
}
skb_reserve(skb, 64);
deauth = (struct ieee80211_mgmt *)skb_put(skb, sizeof(struct ieee80211_mgmt));
memcpy(deauth->da, mac, ETH_ALEN);
memcpy(deauth->sa, mac, ETH_ALEN);
memcpy(deauth->bssid, mac, ETH_ALEN);
deauth->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_DEAUTH |
IEEE80211_FCTL_TODS);
deauth->u.deauth.reason_code = WLAN_REASON_STA_CHANNEL_CHANGE;
deauth->seq_ctrl = ieee80211_frequency_to_channel(freq);
if(NL80211_CHAN_HT40MINUS == chann_type) {
deauth->duration = ieee80211_frequency_to_channel(freq) - 4; //use duration to send second channel
}
else if(NL80211_CHAN_HT40PLUS == chann_type) {
deauth->duration = ieee80211_frequency_to_channel(freq) + 4; //use duration to send second channel
}
else {
deauth->duration = 0;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) || defined(CONFIG_SSV_CHANNEL_FOLLOW)
//only support kernel > 3.11 or cfg80211 patch
cfg80211_rx_unprot_mlme_mgmt(vif->ndev, (u8 *)deauth, skb->len);
SSV_LOG_DBG("SSV send unprotected deauth to hostapd with ch_type = %d, freq = %d\n", chann_type, freq);
#endif
kfree_skb(skb);
}
#ifdef CONFIG_SSV_CHANNEL_FOLLOW
//check softap and station's channel
void ssv_channel_switch_check(struct ssv_softc *sc, u16 freq, enum nl80211_channel_type chann_type)
{
struct ssv_vif *sta_vif = NULL, *ap_vif = NULL;
struct ssv_vif *vif;
struct ssv_chanctx *ap_ctxt = NULL, *sta_ctxt = NULL;
enum nl80211_channel_type ch_type = NL80211_CHAN_NO_HT;
// Look for VIF entry
list_for_each_entry(vif, &sc->vifs, list) {
if (vif->up) {
if (NL80211_IFTYPE_STATION == SSV_VIF_TYPE(vif))
sta_vif = vif;
if (NL80211_IFTYPE_AP == SSV_VIF_TYPE(vif))
ap_vif = vif;
}
}
if ((NULL == sta_vif) || (NULL == ap_vif)) {
SSV_LOG_DBG("Not concurrency mode\n");
return;
}
ap_ctxt = &ap_vif->sc->chanctx_table[ap_vif->ch_index];
sta_ctxt = &sta_vif->sc->chanctx_table[sta_vif->ch_index];
if (NULL == ap_ctxt->chan_def.chan) {
SSV_LOG_DBG("Cannot find softap's chandef\n");
return;
}
ch_type = cfg80211_get_chandef_type(&ap_ctxt->chan_def);
if (freq != ap_ctxt->chan_def.chan->center_freq || chann_type != ch_type) { //homeAP only change ch_type.
ssv6xxx_softap_send_deauth_to_hostapd(ap_vif, freq, chann_type);
SSV_LOG_DBG("SSV notify channel switch.\n");
}
}
#endif /*CONFIG_SSV_CHANNEL_FOLLOW */
void ssv6xxx_fw_reset_send_deauth_check(struct ssv_softc *sc, struct ssv_vif *vif)
{
enum nl80211_channel_type channel_type = sc->chan_type;
u16 center_freq = sc->center_freq;
SSV_LOG_DBG("[%s][%d]\n", __FUNCTION__, __LINE__);
if (NL80211_IFTYPE_AP != SSV_VIF_TYPE(vif)) {
SSV_LOG_DBG("error iftype!!!\n");
return;
}
ssv6xxx_softap_send_deauth_to_hostapd(vif, center_freq, channel_type);
SSV_LOG_DBG("SSV notify fw reset.\n");
}