luckfox-pico-sdk/sysdrv/drv_ko/wifi/ssv6115/ssvdevice/init.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

541 lines
16 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2015 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.
*/
#include <linux/module.h>
#include <linux/version.h>
#include <linux/inetdevice.h>
#include <linux/of.h>
#include <net/cfg80211.h>
#include <linux/etherdevice.h>
#include "dev.h"
#include <hwif/hwif.h>
#include <hci/ssv_hci.h>
#include <fmac/fmac.h>
#include <fmac/fmac_tx.h>
#include <fmac/fmac_rx.h>
#include <fmac/host_msg.h>
#include "ssv_cfg.h"
#include "rftool/ssv_phy_rf.h"
#include "ssv_custom_func.h"
#include "rftool/ssv_efuse.h"
#include "ipc_msg.h"
#include "hci/drv_hci_ops.h"
#include "ssv_firmware_version.h"
#include "ssv_debug.h"
#include <fmac/fmac_msg_rx.h>
#include <fmac/fmac_msg_tx.h>
#include <fmac/fmac_utils.h>
MODULE_AUTHOR("iComm-semi, Ltd");
MODULE_DESCRIPTION("Support for SSV6xxx wireless LAN cards.");
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0))
MODULE_SUPPORTED_DEVICE("SSV6xxx 802.11n WLAN cards");
#endif
MODULE_LICENSE("Dual BSD/GPL");
extern struct ssv6xxx_cfg ssv_cfg;
struct ssv_hw *g_ssv_hw = NULL;
EXPORT_SYMBOL(g_ssv_hw);
extern int ssv_ctl_init(void);
#if defined(CONFIG_WPA_SUPPLICANT_CTL)
extern int ssv_wpas_ctl_init(void);
#endif
extern int ssv_init_cli(const char *dev_name, struct ssv_cmd_data *cmd_data);
extern int ssv6xxx_fmac_init(void **plat_sc, struct ssv6xxx_hci_ops *hci_ops, void *hci_priv, u8 *maddr, struct device *dev);
extern int ssv_nimble_init(void **plat_sbc, struct ssv6xxx_hci_ops *hci_ops, void *hci_priv, struct device *dev);
extern int ssv_rftool_init(void **plat_srfc, struct ssv6xxx_hci_ops *hci_ops, void *hci_priv, struct device *dev);
#ifdef CONFIG_BLE
extern int ssv_ble_hci_init(void **plat_sbsc, struct ssv6xxx_hci_ops *hci_ops, void *hci_priv, u8 *maddr, struct device *dev);
#endif
extern void ssv_ctl_deinit(void);
#if defined(CONFIG_WPA_SUPPLICANT_CTL)
extern void ssv_wpas_ctl_deinit(void);
#endif
extern void ssv_deinit_cli(const char *dev_name, struct ssv_cmd_data *cmd_data);
extern void ssv6xxx_fmac_deinit(void *plat_sc);
extern void ssv_nimble_deinit(void *plat_sbc);
extern void ssv_rftool_deinit(void *plat_srfc);
#ifdef CONFIG_BLE
extern void ssv_ble_hci_deinit(void *plat_sbsc);
#endif
#if (CONFIG_PRE_ALLOC_SKB == 1)
extern int ssv_pre_allocate_init(void);
extern void ssv_pre_allocate_exit(void);
#elif (CONFIG_PRE_ALLOC_SKB == 2)
extern u32 ssv_pre_allocate_rx_skb_get_max_running(void);
extern u32 ssv_pre_allocate_rx_skb_get_current_q_len(void);
extern int ssv_pre_allocate_rx_skb_free_all(void);
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0))
static inline void house_keeping_timer_hdl(struct timer_list *in_timer)
{
struct ssv_timer_list *ptimer = from_timer(ptimer, in_timer, timer);
ptimer->function((unsigned long)ptimer->arg);
}
#endif
void ssv6xxx_fw_reset_softap_deauth_to_host(struct ssv_softc *sc)
{
int i = 0;
struct ssv_vif *vif = NULL;
SSV_LOG_DBG("[%s][%d]\n", __FUNCTION__, __LINE__);
for(i = 0; i < (NX_VIRT_DEV_MAX); i++) {
if (sc->vif_table[i] == NULL)
continue;
vif = sc->vif_table[i];
if (NL80211_IFTYPE_AP == SSV_VIF_TYPE(vif)) {
if (sc->sta_table[vif->ap.bcmc_index].valid) {
if (vif->ap.bcmc_index == sc->sta_table[vif->ap.bcmc_index].sta_idx) {
if (i != sc->if_mode[i][1]) {
SSV_LOG_DBG("!!!!!!!!! err1 !!!!!!!!\n");
}
//SSV_LOG_DBG("vif->ap.bcmc_index = %d,vif->drv_vif_index = %d \n",vif->ap.bcmc_index, vif->drv_vif_index);
ssv6xxx_fw_reset_send_deauth_check(sc, vif);
}
}
}
}
}
void ssv6xxx_fw_reset_sta_dis_to_host(struct ssv_softc *sc)
{
int i = 0;
struct ssv_vif *vif = NULL;
SSV_LOG_DBG("[%s][%d]\n", __FUNCTION__, __LINE__);
for(i = 0; i < (NX_VIRT_DEV_MAX); i++) {
if (sc->vif_table[i] == NULL)
continue;
vif = sc->vif_table[i];
if (NL80211_IFTYPE_STATION != SSV_VIF_TYPE(vif))
continue;
if (vif->sta.ap->valid == true) {
ssv_rx_sm_disconnect_ind_for_fw_reset(sc, i);
}
}
}
static void ssv6xxx_fw_reset_scan_done(struct ssv_softc *sc)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0))
struct cfg80211_scan_info info = {
.aborted = false,
};
#endif
if (sc->scan_request)
{
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0))
cfg80211_scan_done(sc->scan_request, false);
#else
cfg80211_scan_done(sc->scan_request, &info);
#endif
SSV_LOG_DBG("ssv6xxx_fw_reset_scan_done: scan done\n");
}
sc->scan_request = NULL;
}
void _ssv_fwreset_msg_to_hci(struct ssv_softc *sc, u8 *msg_buffer, u32 msg_len, u32 msg_type);
void ssv6xxx_fw_reset_process(struct work_struct *work)
{
int i = 0;
struct ssv_softc *sc = container_of(work, struct ssv_softc, fw_reset_work);
struct fw_reset_cmd *cur, *tmp;
if(sc->fw_reset_run == true) {
SSV_LOG_DBG("reset process already run\n");
return;
}
sc->fw_reset_run = true;
SSV_LOG_DBG("**************************\n");
SSV_LOG_DBG("*** Software MAC reset ***\n");
SSV_LOG_DBG("**************************\n");
SSV_LOG_DBG("**************************\n");
SSV_LOG_DBG("*** wait MM_RESET_CFM ***\n");
SSV_LOG_DBG("**************************\n");
for(i = 0; i < (SSV_SW_TXQ_NUM -1); i++) {
ssv_drv_hci_tx_pause_by_txqid(sc->hci_priv, sc->hci_ops, i);
ssv_drv_hci_tx_inactive_by_txqid(sc->hci_priv, sc->hci_ops, i);
}
ssv_drv_hci_fw_reset(sc->hci_priv, sc->hci_ops);
//mdelay(500);
#if 1
list_for_each_entry_safe(cur, tmp, &sc->reset_cmd.list, list) {
_ssv_fwreset_msg_to_hci(sc, cur->data, cur->msg_len, cur->msg_type);
}
#endif
SSV_LOG_DBG("**********************************\n");
SSV_LOG_DBG("*** Software MAC reset ready ***\n");
SSV_LOG_DBG("**********************************\n");
for(i = 0; i < (SSV_SW_TXQ_NUM -1); i++) {
ssv_drv_hci_tx_active_by_txqid(sc->hci_priv, sc->hci_ops, i);
ssv_drv_hci_tx_resume_by_txqid(sc->hci_priv, sc->hci_ops, i);
}
ssv6xxx_fw_reset_sta_dis_to_host(sc);
ssv6xxx_fw_reset_scan_done(sc);
ssv6xxx_fw_reset_softap_deauth_to_host(sc);
sc->fw_reset_run = false;
}
void ssv6xxx_house_keeping(unsigned long argv)
{
#define TIMEOUT_REORDER_WORK 50
#define TIMEOUT_PROBE_STA_WORK 1000
#define TIMEOUT_SEND_KEEP_ALIVE_WORK 30000
#define TIMEOUT_CHECK_FW_STATUS 100
struct ssv_hw *ssv_hw = (struct ssv_hw *)argv;
unsigned long tick = jiffies;
u32 timestamp = 0;
if (time_after(tick, (ssv_hw->reorder_work_timestamp + msecs_to_jiffies(TIMEOUT_REORDER_WORK)))) {
ssv_hw->reorder_work_timestamp = tick;
ssv_push_private_msg_to_host(ssv_hw->sc, (u32)E_HOST_PRIV_MSG_TYPE_RX_REORD_TIMEOUT, (u8 *)NULL, (u32)0);
}
if (time_after(tick, (ssv_hw->probe_sta_work_timestamp + msecs_to_jiffies(TIMEOUT_PROBE_STA_WORK)))) {
ssv_hw->probe_sta_work_timestamp = tick;
ssv_push_private_msg_to_host(ssv_hw->sc, (u32)E_HOST_PRIV_MSG_TYPE_PROBE_CLIENT, (u8 *)NULL, (u32)0);
}
#ifdef SEND_KEEP_ALIVE
if (time_after(tick, (ssv_hw->send_keep_alive_timestamp + msecs_to_jiffies(TIMEOUT_SEND_KEEP_ALIVE_WORK)))) {
ssv_hw->send_keep_alive_timestamp = tick;
ssv_push_private_msg_to_host(ssv_hw->sc, (u32)E_HOST_PRIV_MSG_TYPE_SEND_KEEP_ALIVE, (u8 *)NULL, (u32)0);
}
#endif
if(ssv_cfg.fw_reset) {
if (time_after(tick, (ssv_hw->check_fw_status_timestamp + msecs_to_jiffies(TIMEOUT_CHECK_FW_STATUS)))) {
ssv_hw->check_fw_status_timestamp = tick;
ssv_push_private_msg_to_host(ssv_hw->sc, (u32)E_HOST_PRIV_MSG_TYPE_CHECK_FW_STATUS, (u8 *)NULL, (u32)0);
}
}
if (ssv_hw->hci_ops->hci_get_tx_timestamp)
ssv_hw->hci_ops->hci_get_tx_timestamp(ssv_hw->hci_priv, &timestamp);
if (time_after(tick, timestamp + msecs_to_jiffies(ssv_cfg.hci_idle_period))) {
if (ssv_hw->hci_ops->hci_set_tx_timestamp)
ssv_hw->hci_ops->hci_set_tx_timestamp(ssv_hw->hci_priv, tick);
if (ssv_hw->hci_ops->hci_trigger_tx)
ssv_hw->hci_ops->hci_trigger_tx(ssv_hw->hci_priv);
}
//flow control
ssv_tx_flow_control(ssv_hw->sc, false);
// be the last
mod_timer(&ssv_hw->house_keeping.time.timer, jiffies + msecs_to_jiffies(HOUSE_KEEPING_TIMEOUT));
}
int ssv6xxx_housekeeping_init(struct ssv_hw *ssv_hw)
{
struct ssv_softc *sc = ssv_hw->sc;
ssv_hw->reorder_work_timestamp = jiffies;
ssv_hw->check_fw_status_timestamp = jiffies;
ssv_hw->reorder_work_timestamp = jiffies;
ssv_hw->house_keeping.time.function = ssv6xxx_house_keeping;
ssv_hw->house_keeping.time.arg = (void *)ssv_hw;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0))
timer_setup(&ssv_hw->house_keeping.time.timer, house_keeping_timer_hdl, 0);
ssv_hw->house_keeping.time.timer.expires = jiffies + msecs_to_jiffies(HOUSE_KEEPING_TIMEOUT);
#else
init_timer(&ssv_hw->house_keeping.time.timer);
ssv_hw->house_keeping.time.timer.expires = jiffies + msecs_to_jiffies(HOUSE_KEEPING_TIMEOUT);
ssv_hw->house_keeping.time.timer.function = ssv6xxx_house_keeping;
ssv_hw->house_keeping.time.timer.data = (unsigned long)ssv_hw;
#endif
sc->fw_reset_wq= create_singlethread_workqueue("fw_reset_wq");
INIT_WORK(&sc->fw_reset_work, ssv6xxx_fw_reset_process);
add_timer(&ssv_hw->house_keeping.time.timer);
return 0;
}
int ssv6xxx_housekeeping_deinit(struct ssv_hw *ssv_hw)
{
struct ssv_softc *sc = ssv_hw->sc;
// remove house keeping
sc->fw_reset_run= false;
del_timer_sync(&ssv_hw->house_keeping.time.timer);
destroy_workqueue(sc->fw_reset_wq);
return 0;
}
extern const char *sw_driver_version;
extern const char *driver_build_date;
void ssv6xxx_check_fw_version(struct ssv_softc *sc)
{
u32 regval = 0;
if(sc->hci_ops->hci_read_word)
sc->hci_ops->hci_read_word(sc->hci_priv, REG_FW_VERSION, &regval);
sc->fw_version=regval;
SSV_LOG_DBG("SSV firmware version %d\n", regval);
if (regval != ssv_firmware_version)
{
if (ssv_cfg.ignore_firmware_version == 0) {
SSV_LOG_ERR("Firmware version mapping not match[%d(0x%08x)]!!\n",regval, regval);
SSV_LOG_ERR("It's should be [%d(0x%08x)]!!\n",ssv_firmware_version, ssv_firmware_version);
BUG_ON(1);
}
else
SSV_LOG_DBG("Force ignore_firmware_version\n");
}
}
int ssv6xxx_platform_init(struct device *dev, struct ssv6xxx_hwif_ops *hwif_ops, void **plat_hw)
{
struct ssv_hw *ssv_hw = NULL;
#ifdef CONFIG_FMAC
struct ssv_softc *sc = NULL;
#endif
#ifdef CONFIG_HWIF_AND_HCI
void *hci_priv = NULL;
#endif
struct ssv_rftool_softc *srfc = NULL;
#ifdef CONFIG_NIMBLE
struct ssv_nimble_softc *snc = NULL;
#endif
#ifdef CONFIG_BLE
struct ssv_ble_softc *sbsc = NULL;
#endif
int ret = 0;
int error = 0;
ssv_hw = kzalloc(sizeof(struct ssv_hw), GFP_KERNEL);
if (NULL == ssv_hw) {
SSV_LOG_DBG(KERN_ERR "Fail to alloc ssv_hw\n");
ret = -ENOMEM;
goto out;
}
g_ssv_hw = ssv_hw;
ssv_hw->dev = dev;
ssv_hw->hwif_ops = hwif_ops;
efuse_read_all_map(ssv_hw);
ssv_custom_modify_macaddr(ssv_hw);
#if (CONFIG_PRE_ALLOC_SKB == 1)
ret = ssv_pre_allocate_init();
if (ret != 0) {
SSV_LOG_DBG(KERN_ERR "Fail to init pre-alloc!\n");
goto err_pre_alloc;
}
#endif
#ifdef CONFIG_HWIF_AND_HCI
ret = ssv6xxx_hci_init((void *)&hci_priv, hwif_ops, dev);
if (ret) {
SSV_LOG_DBG(KERN_ERR "Fail to alloc hci\n");
ret = -ENOMEM;
goto err_hci;
}
#endif
#ifdef CONFIG_FMAC
ret = ssv6xxx_fmac_init((void *)&sc, &ssv_hci_ops, hci_priv, (u8 *)ssv_hw->maddr, dev);
if (ret) {
SSV_LOG_DBG("Fail to fmac init\n");
ret = -ENODEV;
goto err_fmac;
}
#endif
SSV_LOG_DBG("SSV host driver version %sBuild Data %s\n", sw_driver_version,driver_build_date);
ssv6xxx_check_fw_version(sc);
ret = ssv_rftool_init((void *)&srfc, &ssv_hci_ops, hci_priv, dev);
if (ret) {
SSV_LOG_DBG("Fail to rftool init\n");
ret = -ENODEV;
goto err_fmac;
}
#ifdef CONFIG_NIMBLE
ret = ssv_nimble_init((void *)&snc, &ssv_hci_ops, hci_priv, dev);
if (ret) {
SSV_LOG_DBG("Fail to nimble init\n");
ret = -ENODEV;
goto err_ble;
}
#endif
#ifdef CONFIG_BLE
ret = ssv_ble_hci_init((void*)&sbsc, &ssv_hci_ops, hci_priv, (u8 *)&ssv_hw->maddr[1], dev);
if (ret) {
SSV_LOG_DBG("Fail to ble init\n");
ret = -ENODEV;
goto err_ble;
}
#endif
#ifdef CONFIG_HWIF_AND_HCI
ssv_hw->hci_priv = hci_priv;
ssv_hw->hci_ops = &ssv_hci_ops;
#endif
#ifdef CONFIG_FMAC
ssv_hw->sc = sc;
#endif
ssv_hw->srfc = srfc;
#ifdef CONFIG_NIMBLE
ssv_hw->snc = snc;
#endif
#ifdef CONFIG_BLE
ssv_hw->sbsc = sbsc;
#endif
mutex_init(&ssv_hw->mutex);
ssv6xxx_housekeeping_init(ssv_hw);
/* wifi_ctl init*/
ssv_ctl_init();
#if defined(CONFIG_WPA_SUPPLICANT_CTL)
/* wpas_ctl init*/
ssv_wpas_ctl_init();
#endif
/* create debug fs */
ssv_init_cli("ssv", &ssv_hw->cmd_data);
*plat_hw = ssv_hw;
SSV_LOG_DBG("SSV6X5X of iComm-semi");
#if 1
//do calibration first then send table to fw
ssv_update_rf_conf_table(ssv_hw);
ssv_custom_modify_rf_conf_table(ssv_hw);
ssv_kdump_rf_conf_table(ssv_hw);
sc->recovery_flag = true;
ssv_set_pll_phy_rf(ssv_hw);
sc->recovery_flag = false;
#endif
if(1 == ssv_cfg.firmware_choice) //Normal mode
{
/*Send RF_CALIBRATION_RESULT to FW*/
// SSV_LOG_DBG("Send RF_CALIBRATION_RESULT to FW*\n");
error = ssv_send_priv_msg_rf_calib_result_backup(sc);
if(error)
{
SSV_LOG_DBG("send rf calibration result error %d\n", error);
#ifdef CONFIG_NIMBLE
goto err_ble;
#endif
#ifdef CONFIG_FMAC
goto err_fmac;
#endif
}
#if 0 //reserved for low power
/*Send RF_AGC_Setting and RF_PHY_SETTING to FW*/
SSV_LOG_DBG("Send RF_PYH_TABLE to FW*\n");
error = ssv_send_priv_msg_agc_reg_table(sc);
if(error)
{
SSV_LOG_DBG("send agc error %d\n", error);
return error;
}
error = ssv_send_priv_msg_rfphy_reg_talbe(sc);
if(error)
{
SSV_LOG_DBG("send rfphy error %d\n", error);
return error;
}
#endif
}
return 0;
#if defined(CONFIG_NIMBLE) || defined(CONFIG_BLE)
err_ble:
#ifdef CONFIG_FMAC
ssv6xxx_fmac_deinit((void *)sc);
#endif
#endif
#ifdef CONFIG_FMAC
err_fmac:
#ifdef CONFIG_HWIF_AND_HCI
ssv6xxx_hci_deinit((void *)hci_priv);
#endif
#endif
#ifdef CONFIG_HWIF_AND_HCI
err_hci:
#endif
#if (CONFIG_PRE_ALLOC_SKB == 1)
ssv_pre_allocate_exit();
err_pre_alloc:
#endif
kfree(ssv_hw);
out:
return ret;
}
EXPORT_SYMBOL(ssv6xxx_platform_init);
void ssv6xxx_platform_deinit(void *plat_hw)
{
struct ssv_hw *ssv_hw = (struct ssv_hw *)plat_hw;
ssv6xxx_housekeeping_deinit(ssv_hw);
ssv_deinit_cli("ssv", &ssv_hw->cmd_data);
ssv_ctl_deinit();
#if defined(CONFIG_WPA_SUPPLICANT_CTL)
ssv_wpas_ctl_deinit();
#endif
ssv_rftool_deinit((void *)ssv_hw->srfc);
#ifdef CONFIG_NIMBLE
ssv_nimble_deinit((void *)ssv_hw->snc);
#endif
#ifdef CONFIG_BLE
ssv_ble_hci_deinit((void *)ssv_hw->sbsc);
#endif
#ifdef CONFIG_FMAC
ssv6xxx_fmac_deinit((void *)ssv_hw->sc);
#endif
#ifdef CONFIG_HWIF_AND_HCI
ssv6xxx_hci_deinit((void *)ssv_hw->hci_priv);
#endif
#if (CONFIG_PRE_ALLOC_SKB == 1)
ssv_pre_allocate_exit();
#elif (CONFIG_PRE_ALLOC_SKB == 2)
ssv_pre_allocate_rx_skb_free_all();
SSV_LOG_DBG("max_running:%u, current_qlen:%u\n",
ssv_pre_allocate_rx_skb_get_max_running(),
ssv_pre_allocate_rx_skb_get_current_q_len());
#endif
kfree(ssv_hw);
return;
}
EXPORT_SYMBOL(ssv6xxx_platform_deinit);