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

810 lines
28 KiB
C

/*
* 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/kthread.h>
#include <linux/inetdevice.h>
#include <linux/of.h>
#include <net/cfg80211.h>
#include <linux/etherdevice.h>
#include "ssv_cfg.h"
#include "fmac.h"
#include "hci/drv_hci_ops.h"
#include "fmac_tx.h"
#include "fmac_rx.h"
#include "fmac_msg_tx.h"
#include "netdev_ops.h"
#include "ssv_debug.h"
#include <linux/semaphore.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <linux/if_arp.h>
#include <linux/icmp.h>
/*******************************************************************************
* Local Structures
******************************************************************************/
struct dhcp_data {
u8 op;
u8 htype;
u8 hlen;
u8 hops;
u32 xid;
u16 secs;
u16 flags;
u32 client_ip;
u32 your_ip;
u32 server_ip;
u32 relay_ip;
u8 hw_addr[16];
u8 serv_name[64];
u8 boot_file[128];
} STRUCT_PACKED;
/*******************************************************************************
* Local Variables
******************************************************************************/
/* DHCP message types */
#define DHCPDISCOVER 1
#define DHCPOFFER 2
#define DHCPREQUEST 3
#define DHCPDECLINE 4
#define DHCPACK 5
#define DHCPNAK 6
#define DHCPRELEASE 7
#define DHCPINFORM 8
#define SSV_HT_CAPABILITIES \
{ \
.ht_supported = true, \
.cap = 0, \
.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, \
.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, \
.mcs = { \
.rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, \
.rx_highest = cpu_to_le16(65), \
.tx_params = IEEE80211_HT_MCS_TX_DEFINED, \
}, \
}
#define SSV_VHT_CAPABILITIES \
{ \
.vht_supported = true, \
.cap = \
(7 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT),\
.vht_mcs = { \
.rx_mcs_map = cpu_to_le16( \
IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | \
IEEE80211_VHT_MCS_NOT_SUPPORTED << 2 | \
IEEE80211_VHT_MCS_NOT_SUPPORTED << 4 | \
IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 | \
IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 | \
IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 | \
IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 | \
IEEE80211_VHT_MCS_NOT_SUPPORTED << 14), \
.tx_mcs_map = cpu_to_le16( \
IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | \
IEEE80211_VHT_MCS_NOT_SUPPORTED << 2 | \
IEEE80211_VHT_MCS_NOT_SUPPORTED << 4 | \
IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 | \
IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 | \
IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 | \
IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 | \
IEEE80211_VHT_MCS_NOT_SUPPORTED << 14), \
} \
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)) || defined(IEEE80211_HE_MAC_CAP2_TRS)
#define SSV_HE_CAPABILITIES \
{ \
.has_he = false, \
.he_cap_elem = { \
.mac_cap_info[0] = 0, \
.mac_cap_info[1] = 0, \
.mac_cap_info[2] = 0, \
.mac_cap_info[3] = 0, \
.mac_cap_info[4] = 0, \
.mac_cap_info[5] = 0, \
.phy_cap_info[0] = 0, \
.phy_cap_info[1] = 0, \
.phy_cap_info[2] = 0, \
.phy_cap_info[3] = 0, \
.phy_cap_info[4] = 0, \
.phy_cap_info[5] = 0, \
.phy_cap_info[6] = 0, \
.phy_cap_info[7] = 0, \
.phy_cap_info[8] = 0, \
.phy_cap_info[9] = 0, \
.phy_cap_info[10] = 0, \
}, \
.he_mcs_nss_supp = { \
.rx_mcs_80 = cpu_to_le16(0xfffa), \
.tx_mcs_80 = cpu_to_le16(0xfffa), \
.rx_mcs_160 = cpu_to_le16(0xffff), \
.tx_mcs_160 = cpu_to_le16(0xffff), \
.rx_mcs_80p80 = cpu_to_le16(0xffff), \
.tx_mcs_80p80 = cpu_to_le16(0xffff), \
}, \
.ppe_thres = {0x00}, \
}
#endif
#define RATE(_bitrate, _hw_rate, _flags) { \
.bitrate = (_bitrate), \
.flags = (_flags), \
.hw_value = (_hw_rate), \
}
#define CHAN(_freq) { \
.center_freq = (_freq), \
.max_power = 30, /* FIXME */ \
}
static struct ieee80211_rate ssv_ratetable[] = {
RATE(10, 0x00, 0),
RATE(20, 0x01, IEEE80211_RATE_SHORT_PREAMBLE),
RATE(55, 0x02, IEEE80211_RATE_SHORT_PREAMBLE),
RATE(110, 0x03, IEEE80211_RATE_SHORT_PREAMBLE),
RATE(60, 0x04, 0),
RATE(90, 0x05, 0),
RATE(120, 0x06, 0),
RATE(180, 0x07, 0),
RATE(240, 0x08, 0),
RATE(360, 0x09, 0),
RATE(480, 0x0A, 0),
RATE(540, 0x0B, 0),
};
/* The channels indexes here are not used anymore */
static struct ieee80211_channel ssv_2ghz_channels[] = {
CHAN(2412),
CHAN(2417),
CHAN(2422),
CHAN(2427),
CHAN(2432),
CHAN(2437),
CHAN(2442),
CHAN(2447),
CHAN(2452),
CHAN(2457),
CHAN(2462),
CHAN(2467),
CHAN(2472),
CHAN(2484),
};
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)) || defined(IEEE80211_HE_MAC_CAP2_TRS)
static struct ieee80211_sband_iftype_data ssv_he_capa = {
.types_mask = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP),
.he_cap = SSV_HE_CAPABILITIES,
};
#endif
static struct ieee80211_supported_band ssv_band_2GHz = {
.channels = ssv_2ghz_channels,
.n_channels = ARRAY_SIZE(ssv_2ghz_channels),
.bitrates = ssv_ratetable,
.n_bitrates = ARRAY_SIZE(ssv_ratetable),
.ht_cap = SSV_HT_CAPABILITIES,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
.vht_cap = SSV_VHT_CAPABILITIES,
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)) || defined(IEEE80211_HE_MAC_CAP2_TRS)
.iftype_data = &ssv_he_capa,
.n_iftype_data = 1,
#endif
};
static struct ieee80211_iface_limit ssv_limits[] = {
{ .max = NX_VIRT_DEV_MAX, .types = BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_STATION)}
};
static struct ieee80211_iface_limit ssv_limits_dfs[] = {
{ .max = NX_VIRT_DEV_MAX, .types = BIT(NL80211_IFTYPE_AP)}
};
static const struct ieee80211_iface_combination ssv_combinations[] = {
{
.limits = ssv_limits,
.n_limits = ARRAY_SIZE(ssv_limits),
.num_different_channels = NX_CHAN_CTXT_CNT,
.max_interfaces = NX_VIRT_DEV_MAX,
},
/* Keep this combination as the last one */
{
.limits = ssv_limits_dfs,
.n_limits = ARRAY_SIZE(ssv_limits_dfs),
.num_different_channels = 1,
.max_interfaces = NX_VIRT_DEV_MAX,
#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 8, 0)
.radar_detect_widths = (BIT(NL80211_CHAN_WIDTH_20_NOHT) |
BIT(NL80211_CHAN_WIDTH_20) |
BIT(NL80211_CHAN_WIDTH_40) |
BIT(NL80211_CHAN_WIDTH_80)),
#endif
}
};
/* There isn't a lot of sense in it, but you can transmit anything you like */
static struct ieee80211_txrx_stypes
ssv_default_mgmt_stypes[NUM_NL80211_IFTYPES] = {
[NL80211_IFTYPE_STATION] = {
.tx = 0xffff,
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) || defined(CONFIG_SUPPORT_WPA3)) || defined(CONFIG_WPA_SUPPLICANT_CTL)
BIT(IEEE80211_STYPE_AUTH >> 4) |
#endif
BIT(IEEE80211_STYPE_PROBE_REQ >> 4),
},
[NL80211_IFTYPE_AP] = {
.tx = 0xffff,
.rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
BIT(IEEE80211_STYPE_DISASSOC >> 4) |
BIT(IEEE80211_STYPE_AUTH >> 4) |
BIT(IEEE80211_STYPE_DEAUTH >> 4) |
BIT(IEEE80211_STYPE_ACTION >> 4),
},
[NL80211_IFTYPE_AP_VLAN] = {
/* copy AP */
.tx = 0xffff,
.rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
BIT(IEEE80211_STYPE_DISASSOC >> 4) |
BIT(IEEE80211_STYPE_AUTH >> 4) |
BIT(IEEE80211_STYPE_DEAUTH >> 4) |
BIT(IEEE80211_STYPE_ACTION >> 4),
},
[NL80211_IFTYPE_P2P_CLIENT] = {
.tx = 0xffff,
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4),
},
[NL80211_IFTYPE_P2P_GO] = {
.tx = 0xffff,
.rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
BIT(IEEE80211_STYPE_DISASSOC >> 4) |
BIT(IEEE80211_STYPE_AUTH >> 4) |
BIT(IEEE80211_STYPE_DEAUTH >> 4) |
BIT(IEEE80211_STYPE_ACTION >> 4),
},
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0)
[NL80211_IFTYPE_P2P_DEVICE] = {
.tx = 0xffff,
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4),
},
#endif
};
static u32 cipher_suites[] = {
WLAN_CIPHER_SUITE_WEP40,
WLAN_CIPHER_SUITE_WEP104,
WLAN_CIPHER_SUITE_TKIP,
WLAN_CIPHER_SUITE_CCMP,
0, // reserved entries to enable AES-CMAC and/or SMS4
0,
};
#define NB_RESERVED_CIPHER 2;
extern struct ssv6xxx_cfg ssv_cfg;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0))
extern int ssv_netdev_notifier_register(void);
extern void ssv_netdev_notifier_unregister(void);
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
static void ssv_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request)
#else
static int ssv_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request)
#endif
{
// TODO
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
return;
#else
return 0;
#endif
}
static void ssv6xxx_init_wiphy(struct ssv_softc *sc)
{
struct wiphy *wiphy = sc->wiphy;
// set mac address
memcpy((void *)wiphy->perm_addr, (const void *)&sc->maddr[0][0], ETH_ALEN);
//memcpy(wiphy->perm_addr, init_conf.mac_addr, ETH_ALEN);
wiphy->mgmt_stypes = ssv_default_mgmt_stypes;
wiphy->bands[NL80211_BAND_2GHZ] = &ssv_band_2GHz;
wiphy->interface_modes =
BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_AP_VLAN) |
BIT(NL80211_IFTYPE_MONITOR) |
BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO);
wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
WIPHY_FLAG_HAS_CHANNEL_SWITCH |
#endif
WIPHY_FLAG_4ADDR_STATION |
WIPHY_FLAG_4ADDR_AP;
//wiphy->max_num_csa_counters = BCN_MAX_CSA_CPT;
wiphy->max_remain_on_channel_duration = ssv_mod_params.roc_dur_max;
#ifdef NEED_OBSS_SCAN
wiphy->features |= NL80211_FEATURE_NEED_OBSS_SCAN;
#endif
wiphy->features |= NL80211_FEATURE_SK_TX_STATUS;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
wiphy->features |= NL80211_FEATURE_VIF_TXPOWER;
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE;
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) || defined(CONFIG_SUPPORT_WPA3))
wiphy->features |= NL80211_FEATURE_SAE;
#elif defined(CONFIG_WPA_SUPPLICANT_CTL)
wiphy->features |= SSV_NL80211_FEATURE_SAE;
#endif
wiphy->iface_combinations = ssv_combinations;
/* -1 not to include combination with radar detection, will be re-added in
* bl_handle_dynparams if supported */
wiphy->n_iface_combinations = ARRAY_SIZE(ssv_combinations) - 1;
wiphy->reg_notifier = ssv_reg_notifier;
wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
wiphy->cipher_suites = cipher_suites;
wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites) - NB_RESERVED_CIPHER;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
sc->ext_capa[0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING;
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
sc->ext_capa[7] = WLAN_EXT_CAPA8_OPMODE_NOTIF;
wiphy->extended_capabilities = sc->ext_capa;
wiphy->extended_capabilities_mask = sc->ext_capa;
wiphy->extended_capabilities_len = ARRAY_SIZE(sc->ext_capa);
#endif
}
struct ssv_sta *ssv_get_sta(struct ssv_softc *sc, const u8 *mac_addr)
{
int i;
for (i = 0; i < NX_REMOTE_STA_MAX; i++) {
struct ssv_sta *sta = &sc->sta_table[i];
if (sta->valid && (memcmp(mac_addr, &sta->mac_addr, 6) == 0))
return sta;
}
return NULL;
}
enum data_frame_types ssv_get_data_frame_type(struct sk_buff *skb)
{
enum data_frame_types type = SSV_DATA_UNKNOW;
struct ethhdr *ethhdr;
struct iphdr *iphdr;
struct arphdr *arphdr;
struct udphdr *udphdr;
struct icmphdr *icmphdr;
ethhdr = (struct ethhdr *)skb->data;
//SSV_LOG_DBG( "ethhdr->h_proto = 0x%04x\n", ntohs(ethhdr->h_proto));
/* | |
* | 14B 20B 8B |
* +------------+-----------+-----------+---------------------------+
* | ethhdr | iphdr | udphdr | DATA |
* +------------+-----------+-----------+---------------------------+
*
*/
switch(ntohs(ethhdr->h_proto)){
case ETH_P_IP : //htons(ETH_P_IP): // 0x0800
{
iphdr = (struct iphdr *)(skb->data + sizeof(struct ethhdr)); //sizeof(struct ethhdr)=14
//SSV_LOG_DBG( "iphdr->protocol = %0x, iphdr->daddr = 0x%x, iphdr->saddr = 0x%x\n", iphdr->protocol, ntohl(iphdr->daddr), ntohl(iphdr->saddr));
switch(iphdr->protocol){
case IPPROTO_UDP: // IPPROTO_UDP = 17
{
struct dhcp_data *dhcp;
u8 *opt;
u8 msgtype = 0;
udphdr = (struct udphdr *)(skb->data + sizeof(struct ethhdr) + sizeof(struct iphdr));
if(udphdr->dest == htons(67) || udphdr->source == htons(67))
{
dhcp = (struct dhcp_data *)(skb->data + sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr));
opt = (u8 *)(dhcp + 1);
opt += 4; //RFC1048 magic cookie?
*opt++ = 53;
*opt++ = 1;
msgtype = *opt;
if(msgtype == DHCPDISCOVER){
type = SSV_DHCP_DISCOVER;
}else if(msgtype == DHCPOFFER){
type = SSV_DHCP_OFFER;
}else if(msgtype == DHCPREQUEST){
type = SSV_DHCP_REQUEST;
}else if(msgtype == DHCPACK){
type = SSV_DHCP_ACK;
}else{
break;
}
}
break;
}
case IPPROTO_ICMP: //IPPROTO_ICMP = 1
{
icmphdr = (struct icmphdr *)(skb->data + sizeof(struct ethhdr) + sizeof(struct iphdr));
if(icmphdr->type == ICMP_ECHO){
type = SSV_ICMP_ECHO; //ICMP_ECHO = 8
break;
}
if(icmphdr->type == ICMP_ECHOREPLY){
type = SSV_ICMP_ECHOREPLY; //ICMP_ECHOREPLY = 0
break;
}
}
default:
break;
}
break;
}
case ETH_P_ARP : //htons(ETH_P_ARP): //0x0806
{
arphdr = (struct arphdr *)(skb->data + sizeof(struct ethhdr));
/* check whether the ARP packet carries a valid IP information */
if (arphdr->ar_hrd != htons(ARPHRD_ETHER))
break;
if (arphdr->ar_pro != htons(ETH_P_IP))
break;
if (arphdr->ar_hln != ETH_ALEN)
break;
if (arphdr->ar_pln != 4)
break;
if (arphdr->ar_op == htons(ARPOP_REPLY)){
type = SSV_ARP_REPLY; //ARPOP_REPLY = 2
break;
}
if (arphdr->ar_op == htons(ARPOP_REQUEST)){
type = SSV_ARP_REQUEST; //ARPOP_REQUEST = 1
break;
}
break;
}
case ETH_P_PAE : //0x888e
type = SSV_EAPOL;
break;
default:
break;
}
return type;
}
void ssv_enable_wapi(struct ssv_softc *sc)
{
cipher_suites[sc->wiphy->n_cipher_suites] = WLAN_CIPHER_SUITE_SMS4;
sc->wiphy->n_cipher_suites++;
sc->wiphy->flags |= WIPHY_FLAG_CONTROL_PORT_PROTOCOL;
}
void ssv_enable_mfp(struct ssv_softc *sc)
{
cipher_suites[sc->wiphy->n_cipher_suites] = WLAN_CIPHER_SUITE_AES_CMAC;
sc->wiphy->n_cipher_suites++;
}
#ifndef DIS_NETDEV_INIT
#ifdef FW_RESET_AT_INIT
static void ssv_set_vers(struct ssv_softc *sc)
{
u32 vers = sc->version_cfm.version_lmac;
SSV_LOG_DBG("[%s][%d]\n", __FUNCTION__, __LINE__);
snprintf(sc->wiphy->fw_version,
sizeof(sc->wiphy->fw_version), "%d.%d.%d.%d",
(vers & (0xff << 24)) >> 24, (vers & (0xff << 16)) >> 16,
(vers & (0xff << 8)) >> 8, (vers & (0xff << 0)) >> 0);
}
#endif
#endif
static void ssv_overwrite_modparams(struct ssv_softc *sc)
{
sc->mod_params->use_2040 = ssv_cfg.use_2040;
sc->mod_params->tx_lft = ssv_cfg.ampdu_tx_lft;
sc->mod_params->he_on = ssv_cfg.he_on;
}
extern struct cfg80211_ops ssv_cfg80211_ops;
extern void ssv_wdev_unregister(struct ssv_softc *sc);
/* ssv6xxx_fmac_init must hook hci callback function
* ex: ssv6xxx_hci_proc_rx_register()
* */
int ssv6xxx_fmac_init(void **plat_sc, struct ssv6xxx_hci_ops *hci_ops, void *hci_priv, u8 *maddr, struct device *dev)
{
struct ssv_softc *sc = NULL;
struct wiphy *wiphy;
#ifndef DIS_NETDEV_INIT
struct wireless_dev *wdev;
int i = 0,j = 0;
#endif
int ret;
/* create a new wiphy for use with cfg80211 */
wiphy = wiphy_new(&ssv_cfg80211_ops, sizeof(struct ssv_softc));
if (!wiphy) {
SSV_LOG_DBG("No memory for wiphy\n");
ret = -ENOMEM;
goto err;
}
sc = wiphy_priv(wiphy);
memset((void *)sc, 0, sizeof(struct ssv_softc));
sc->wiphy = wiphy;
sc->hci_priv = hci_priv;
sc->hci_ops = hci_ops;
sc->dev = dev;
memcpy(sc->maddr,maddr,sizeof(sc->maddr));
/* set device pointer for wiphy */
set_wiphy_dev(wiphy, sc->dev);
wiphy->privid = sc;
sc->now_ack_seq=0;
skb_queue_head_init(&sc->wait_sw_ack_q);
ssv6xxx_init_wiphy(sc);
#ifndef DIS_NETDEV_INIT
sc->mod_params = &ssv_mod_params;
ssv_overwrite_modparams(sc);
for (i = 0; i < NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX; i++)
{
sc->avail_idx_map |= BIT(i);
}
#if 0 ///@FIXME: phy_config
{
/// PHY configuration (This is only valid for KARST radio)
u32_l phy_cfg[] = {0x01000000, 0x01000000, 0x01000000, 0x01000000,
0x01000000, 0x01000000, 0x01000000, 0x01000000,
0x0};
memcpy((void *)&sc->phy_config, (const void *)&phy_cfg[0], 33);
}
#endif
for (i = 0; i < NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX; i++)
{
for(j = 0; j < TID_MAX ; j++){
sc->sta_table[i].rx_last_seqcntl[j] = 0xFFFF;
}
}
if ((ret = ssv_ipc_init(sc)))
{
goto err_platon;
}
INIT_LIST_HEAD(&sc->vifs);
mutex_init(&sc->cmd_lock);
mutex_init(&sc->cb_lock);
sema_init( &sc->reconnect_sem, 0);
sc->cmd_sent = false;
INIT_LIST_HEAD(&sc->reset_cmd.list);
sc->recovery_flag = true;
// Set rx path callback after initializing vifs list
// Because it access vifs list in rx path
ssv_drv_hci_set_trigger_conf(sc->hci_priv, sc->hci_ops, ssv_cfg.hci_trigger_en, ssv_cfg.hci_trigger_qlen);
#ifndef DIS_NETDEV_INIT
// HCI TX aggregation setting
ssv_drv_hci_set_cap(sc->hci_priv, sc->hci_ops, HCI_CAP_TX_AGGR, ((ssv_cfg.hw_caps&HW_CAP_HCI_TX_AGGR)?true:false));
// HCI RX aggregation setting
ssv_drv_hci_set_cap(sc->hci_priv, sc->hci_ops, HCI_CAP_RX_AGGR, true); // Always enable HCI RX aggregation.
ssv_send_io_aggr_setting(sc, true, ((ssv_cfg.hw_caps&HW_CAP_HCI_RX_AGGR)?true:false));
ssv6xxx_hci_proc_rx_register((void *)hci_priv, ssv_rx_packet_ind, (void *)sc);
#endif
ssv6xxx_check_resource_register((void *)hci_priv, ssv_tx_check_resource, (void *)sc);
#ifdef FW_RESET_AT_INIT
/* Reset FW */
if ((ret = ssv_send_reset(sc)))
goto err_lmac_reqs;
if ((ret = ssv_send_version_req(sc, &sc->version_cfm)))
goto err_lmac_reqs;
ssv_set_vers(sc);
if ((ret = ssv_handle_dynparams(sc, sc->wiphy)))
goto err_lmac_reqs;
/* Set parameters to firmware */
ssv_send_me_config_req(sc);
#endif
#endif //#ifndef DIS_NETDEV_INIT
if ((ret = wiphy_register(wiphy))) {
SSV_LOG_DBG("Could not register wiphy device\n");
ret = -ENODEV;
goto err_register_wiphy;
}
/* Set channel parameters to firmware (must be done after WiPHY registration) */
ssv_send_me_chan_config_req(sc);
*plat_sc = sc;
SSV_LOG_DBG("ssv wiphy %s\n", wiphy_name(sc->wiphy));
#ifndef DIS_NETDEV_INIT
rtnl_lock();
/* Add an initial station interface */
wdev = ssv_interface_add(sc, "wlan%d", NL80211_IFTYPE_STATION, NULL);
rtnl_unlock();
if (!wdev) {
wiphy_err(wiphy, "Failed to instantiate a network device\n");
ret = -ENOMEM;
goto err_add_interface;
}
rtnl_lock();
wdev = ssv_interface_add(sc, "p2p0", NL80211_IFTYPE_STATION, NULL);
rtnl_unlock();
if (!wdev) {
wiphy_err(wiphy, "Failed to instantiate a network device\n");
ret = -ENOMEM;
goto err_add_interface;
}
SSV_LOG_DBG("[%s][%d]\n", __FUNCTION__, __LINE__);
wiphy_info(wiphy, "New interface create %s", wdev->netdev->name);
#endif //#ifndef DIS_NETDEV_INIT
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0))
/*
* Register netdev notifier to listen netdev state for Kernel < 3.6,
* and call stop_ap while netdev(AP mode) is going down,
* as this version of cfg80211 lacks this operation.
*/
ret = ssv_netdev_notifier_register();
if (ret) {
SSV_LOG_DBG("Fail to register netdev notifier\n");
goto err_netdev_notifier;
}
#endif
#if 1
//set ampdu tx rx
ssv_send_priv_msg_ampdu_setting(sc, ssv_cfg.hw_caps);
#endif
// filter duplicate rx
ssv_send_filter_duplicate_rx(sc, ssv_cfg.filter_duplicate_rx);
//ssv_send_wmm_follow_vo(sc, ssv_cfg.wmm_follow_vo);
ssv_send_ipc_tx_use_one_hwq(sc, ssv_cfg.ipc_tx_use_one_hwq);
ssv_send_macaddr(sc, (u8 *)&sc->maddr[0][0], (u8 *)&sc->maddr[1][0]);
ssv_send_set_policy_tbl(sc, ssv_cfg.set_rts_method);
ssv_send_txq_credit_boundary(sc);
ssv_send_mac_addr_rule_setting(sc, ssv_cfg.mac_low_mask,ssv_cfg.mac_high_mask);
#ifdef CONFIG_MIFI_LOWPOWER
ssv_send_priv_msg_mifi_setting(sc, ssv_cfg.mifi_feature, ssv_cfg.mifi_no_traffic_duration);
#endif
sc->recovery_flag = false;
return 0;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0))
err_netdev_notifier:
ssv_wdev_unregister(sc);
#endif
#ifndef DIS_NETDEV_INIT
err_add_interface:
#endif
wiphy_unregister(sc->wiphy);
err_register_wiphy:
#ifndef DIS_NETDEV_INIT
#ifdef FW_RESET_AT_INIT
err_lmac_reqs:
#endif
ssv_ipc_deinit(sc);
err_platon:
ssv6xxx_hci_proc_rx_unregister((void *)sc->hci_priv, ssv_rx_packet_ind, NULL);
#endif
wiphy_free(wiphy);
err:
return ret;
}
EXPORT_SYMBOL(ssv6xxx_fmac_init);
static void ssv6xxx_fmac_stop_all_running_threads(struct ssv_softc *sc)
{
if (sc->ssv_txtput.txtput_tsk) {
SSV_LOG_DBG("Stopping txtput task...\n");
kthread_stop(sc->ssv_txtput.txtput_tsk);
while (sc->ssv_txtput.txtput_tsk != NULL) {
msleep(1);
}
SSV_LOG_DBG("txtput task is stopped.\n");
}
}
void ssv6xxx_fmac_deinit(void *plat_sc)
{
struct ssv_softc *sc = (struct ssv_softc *)plat_sc;
struct sk_buff *clone_skb;
struct fw_reset_cmd *cur, *tmp;
ssv6xxx_fmac_stop_all_running_threads(sc);
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0))
ssv_netdev_notifier_unregister();
#endif
#ifndef DIS_NETDEV_INIT
ssv_wdev_unregister(sc);
#endif
wiphy_unregister(sc->wiphy);
#ifndef DIS_NETDEV_INIT
ssv6xxx_hci_proc_rx_unregister((void *)sc->hci_priv, ssv_rx_packet_ind, NULL);
ssv_ipc_deinit(sc);
ssv_drv_hci_stop(sc->hci_priv, sc->hci_ops); // pause all hci wifi sw txq
#endif
while( (clone_skb = skb_dequeue(&sc->wait_sw_ack_q)) )
{
dev_kfree_skb_any(clone_skb);
}
list_for_each_entry_safe(cur, tmp, &sc->reset_cmd.list, list) {
list_del(&cur->list);
kfree(cur);
}
//this will free sc, because sc is private member of wiphy
wiphy_free(sc->wiphy);
return;
}
EXPORT_SYMBOL(ssv6xxx_fmac_deinit);