luckfox-pico-sdk/sysdrv/drv_ko/wifi/atbm/hal_apollo/mac80211/main.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

2312 lines
63 KiB
C

/*
* Copyright 2002-2005, Instant802 Networks, Inc.
* Copyright 2005-2006, Devicescape Software, Inc.
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <net/atbm_mac80211.h>
#include <linux/nl80211.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/skbuff.h>
#include <linux/etherdevice.h>
#include <linux/if_arp.h>
#include <linux/rtnetlink.h>
#include <linux/bitmap.h>
#ifdef CONFIG_ATBM_PM_QOS
#include <linux/pm_qos.h>
#endif
#include <linux/inetdevice.h>
#include <net/net_namespace.h>
#include <net/cfg80211.h>
#ifdef CONFIG_WIRELESS_EXT
#include <net/iw_handler.h>
#endif
#ifdef IPV6_FILTERING
#include <net/if_inet6.h>
#include <net/addrconf.h>
#endif /*IPV6_FILTERING*/
#include "ieee80211_i.h"
#include "driver-ops.h"
#include "rate.h"
#include "mesh.h"
#include "wep.h"
#include "led.h"
#include "cfg.h"
#include "debugfs.h"
#include "wext_cfg.h"
#include "../apollo.h"
//extern int g_connetting;
#ifdef CONFIG_IF1NAME
#define WIFI_IF1NAME CONFIG_IF1NAME
#else
#define WIFI_IF1NAME "wlan%d"
#endif
#ifdef CONFIG_IF2NAME
#define WIFI_IF2NAME CONFIG_IF2NAME
#else
#define WIFI_IF2NAME "p2p%d"
#endif
#pragma message(WIFI_IF1NAME)
#pragma message(WIFI_IF2NAME)
static struct lock_class_key ieee80211_rx_skb_queue_class;
void ieee80211_configure_filter(struct ieee80211_sub_if_data *sdata)
{
u64 mc;
unsigned int changed_flags;
unsigned int new_flags = 0;
struct ieee80211_local *local = sdata->local;
#if 0
if (!(SDATA_STATE_RUNNING & sdata->state))
return;
#endif
if (sdata->flags & IEEE80211_SDATA_ALLMULTI)
new_flags |= FIF_ALLMULTI;
if (sdata->flags & IEEE80211_SDATA_PROMISC)
new_flags |= FIF_PROMISC_IN_BSS;
if (sdata->vif.type == NL80211_IFTYPE_MONITOR ||
local->scan_sdata == sdata)
new_flags |= FIF_BCN_PRBRESP_PROMISC;
if (sdata->vif.type == NL80211_IFTYPE_AP
#ifdef CONFIG_ATBM_SUPPORT_IBSS
|| sdata->vif.type == NL80211_IFTYPE_ADHOC
#endif
)
new_flags |= FIF_PROBE_REQ;
if (sdata->vif.type == NL80211_IFTYPE_AP)
new_flags |= FIF_PSPOLL;
new_flags |= sdata->req_filt_flags;
spin_lock_bh(&local->filter_lock);
changed_flags = sdata->filter_flags ^ new_flags;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35))
mc = drv_prepare_multicast(local, sdata, &sdata->mc_list);
#else
mc = drv_prepare_multicast(local, sdata, sdata->mc_count,
sdata->mc_list);
#endif
spin_unlock_bh(&local->filter_lock);
/* be a bit nasty */
new_flags |= (1<<31);
drv_configure_filter(local, sdata, changed_flags, &new_flags, mc);
WARN_ON(new_flags & (1<<31));
sdata->filter_flags = new_flags & ~(1<<31);
}
#ifdef CONFIG_ATBM_MAC80211_NO_USE
void ieee80211_notify_channel_change(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata)
{
if (local->hw.flags & IEEE80211_HW_SUPPORTS_MULTI_CHANNEL) {
//BUG_ON(!sdata);
if(!sdata){
atbm_printk_err("%s %d ,ERROR !!! sdata is NULL\n",__func__,__LINE__);
return;
}
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_CHANNEL);
} else {
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
}
}
#endif
static struct ieee80211_channel *ieee80211_recalc_channel(
struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
u32 *changed)
{
struct ieee80211_channel_state *chan_state;
struct ieee80211_channel *chan, *scan_chan;
enum nl80211_channel_type channel_type;
u32 offchannel_flag;
bool multi_channel;
// BUG_ON(local && sdata);
// BUG_ON(!local && !sdata);
if((local && sdata) || (!local && !sdata)){
atbm_printk_err("%s %d ,ERROR !!! local = %p sdata=%p\n",__func__,__LINE__,local,sdata);
return NULL;
}
multi_channel = sdata ? true : false;
local = local ? local : sdata->local;
scan_chan = local->scan_channel;
if (multi_channel) {
if (local->scan_sdata != sdata)
scan_chan = NULL;
chan_state = &sdata->chan_state;
} else if (scan_chan) {
// BUG_ON(!local->scan_sdata);
if(!local->scan_sdata){
atbm_printk_err("%s %d ,ERROR !!! local->scan_sdata is NULL\n",__func__,__LINE__);
return NULL;
}
chan_state = &local->scan_sdata->chan_state;
} else {
chan_state = &local->chan_state;
}
offchannel_flag = chan_state->conf.offchannel;
if (scan_chan) {
chan = scan_chan;
/* If scanning on oper channel, use whatever channel-type
* is currently in use.
*/
if (chan == chan_state->oper_channel)
channel_type = chan_state->_oper_channel_type;
else
channel_type = NL80211_CHAN_NO_HT;
chan_state->conf.offchannel = true;
} else if (chan_state->tmp_channel &&
chan_state->oper_channel != chan_state->tmp_channel) {
chan = scan_chan = chan_state->tmp_channel;
channel_type = chan_state->tmp_channel_type;
chan_state->conf.offchannel = true;
} else {
chan = chan_state->oper_channel;
channel_type = chan_state->_oper_channel_type;
chan_state->conf.offchannel = false;
}
offchannel_flag ^= chan_state->conf.offchannel;
if (offchannel_flag || chan != chan_state->conf.channel ||
channel_type != chan_state->conf.channel_type) {
chan_state->conf.channel = chan;
chan_state->conf.channel_type = channel_type;
if (multi_channel) {
*changed |= BSS_CHANGED_CHANNEL;
} else {
*changed |= IEEE80211_CONF_CHANGE_CHANNEL;
}
}
return chan;
}
int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
{
struct ieee80211_channel *chan = NULL; /* Only used when multi channel is off */
#ifdef CONFIG_ATBM_SUPPORT_MULTI_CHANNEL
struct ieee80211_sub_if_data *sdata;
#endif
int ret = 0;
int power;
bool is_ht;
might_sleep();
#ifdef CONFIG_ATBM_SUPPORT_MULTI_CHANNEL
if (local->hw.flags & IEEE80211_HW_SUPPORTS_MULTI_CHANNEL) {
// XXX: broken code ahead
// we can't call bss_info_changed while in rcu section!
rcu_read_lock();
list_for_each_entry_rcu(sdata, &local->interfaces, list)
ieee80211_recalc_channel(NULL, sdata, &changed);
rcu_read_unlock();
// XXX: broken code end
} else
#endif
{
chan = ieee80211_recalc_channel(local, NULL, &changed);
}
#ifdef CONFIG_ATBM_SUPPORT_MULTI_CHANNEL
if (local->hw.flags & IEEE80211_HW_SUPPORTS_MULTI_CHANNEL) {
is_ht = true;
rcu_read_lock();
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
if (!conf_is_ht(&sdata->chan_state.conf)) {
is_ht = false;
break;
}
}
rcu_read_unlock();
} else
#endif
{
is_ht = conf_is_ht(local->hw.conf.chan_conf);
}
if (!is_ht) {
/*
* atbm_mac80211.h documents that this is only valid
* when the channel is set to an HT type, and
* that otherwise STATIC is used.
*/
local->hw.conf.smps_mode = IEEE80211_SMPS_STATIC;
} else if (local->hw.conf.smps_mode != local->smps_mode) {
local->hw.conf.smps_mode = local->smps_mode;
changed |= IEEE80211_CONF_CHANGE_SMPS;
}
#ifdef CONFIG_ATBM_SUPPORT_MULTI_CHANNEL
if (local->hw.flags & IEEE80211_HW_SUPPORTS_MULTI_CHANNEL) {
power = 0;
rcu_read_lock();
list_for_each_entry_rcu(sdata, &local->interfaces, list)
power = max(power, sdata->chan_state.conf.channel->max_power);
list_for_each_entry_rcu(sdata, &local->interfaces, list)
power = min(power, (sdata->chan_state.conf.channel->max_power -
local->power_constr_level));
rcu_read_unlock();
} else
#endif
{
if ((local->scanning & SCAN_SW_SCANNING) ||
(local->scanning & SCAN_HW_SCANNING)) {
power = chan->max_power;
} else {
power = local->power_constr_level ?
(chan->max_power - local->power_constr_level) :
chan->max_power;
}
}
if (local->user_power_level >= 0)
power = min(power, local->user_power_level);
if (local->hw.conf.power_level != power) {
changed |= IEEE80211_CONF_CHANGE_POWER;
local->hw.conf.power_level = power;
}
if (changed && local->open_count) {
ret = drv_config(local, changed);
/*
* Goal:
* HW reconfiguration should never fail, the driver has told
* us what it can support so it should live up to that promise.
*
* Current status:
* rfkill is not integrated with mac80211 and a
* configuration command can thus fail if hardware rfkill
* is enabled
*
* FIXME: integrate rfkill with mac80211 and then add this
* WARN_ON() back
*
*/
/* WARN_ON(ret); */
}
return ret;
}
void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
u32 changed)
{
struct ieee80211_local *local = sdata->local;
static const u8 zero[ETH_ALEN] = { 0 };
if (!changed)
return;
#ifdef CONFIG_ATBM_SUPPORT_MULTI_CHANNEL
if (local->hw.flags & IEEE80211_HW_SUPPORTS_MULTI_CHANNEL)
ieee80211_recalc_channel(NULL, sdata, &changed);
#endif
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
/*
* While not associated, claim a BSSID of all-zeroes
* so that drivers don't do any weird things with the
* BSSID at that time.
*/
if (sdata->vif.bss_conf.assoc)
sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid;
else
sdata->vif.bss_conf.bssid = zero;
}
#ifdef CONFIG_ATBM_SUPPORT_IBSS
else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
sdata->vif.bss_conf.bssid = sdata->u.ibss.bssid;
#endif
else if (sdata->vif.type == NL80211_IFTYPE_AP)
sdata->vif.bss_conf.bssid = sdata->vif.addr;
#ifdef CONFIG_ATBM_SUPPORT_WDS
else if (sdata->vif.type == NL80211_IFTYPE_WDS)
sdata->vif.bss_conf.bssid = NULL;
#endif
#ifdef CONFIG_MAC80211_ATBM_MESH
else if (ieee80211_vif_is_mesh(&sdata->vif)) {
sdata->vif.bss_conf.bssid = zero;
}
#endif
else if (sdata->vif.type == NL80211_IFTYPE_MONITOR){
sdata->vif.bss_conf.bssid = zero;
} else {
WARN_ON(1);
return;
}
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP:
#ifdef CONFIG_ATBM_SUPPORT_IBSS
case NL80211_IFTYPE_ADHOC:
#endif
case NL80211_IFTYPE_WDS:
#ifdef CONFIG_MAC80211_ATBM_MESH
case NL80211_IFTYPE_MESH_POINT:
#endif
break;
default:
/* do not warn to simplify caller in scan.c */
changed &= ~BSS_CHANGED_BEACON_ENABLED;
if (WARN_ON(changed & BSS_CHANGED_BEACON))
return;
break;
}
if (changed & BSS_CHANGED_BEACON_ENABLED) {
if (local->quiescing || !ieee80211_sdata_running(sdata) ||
test_bit(SCAN_SW_SCANNING, &local->scanning)) {
sdata->vif.bss_conf.enable_beacon = false;
} else {
/*
* Beacon should be enabled, but AP mode must
* check whether there is a beacon configured.
*/
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP:
sdata->vif.bss_conf.enable_beacon =
!!sdata->u.ap.beacon;
break;
#ifdef CONFIG_ATBM_SUPPORT_IBSS
case NL80211_IFTYPE_ADHOC:
sdata->vif.bss_conf.enable_beacon =
!!sdata->u.ibss.presp;
break;
#endif
#ifdef CONFIG_MAC80211_ATBM_MESH
case NL80211_IFTYPE_MESH_POINT:
sdata->vif.bss_conf.enable_beacon =
!!sdata->u.mesh.mesh_id_len;
break;
#endif
default:
/* not reached */
WARN_ON(1);
break;
}
}
}
drv_bss_info_changed(local, sdata, &sdata->vif.bss_conf, changed);
}
u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata)
{
sdata->vif.bss_conf.use_cts_prot = false;
sdata->vif.bss_conf.use_short_preamble = false;
sdata->vif.bss_conf.use_short_slot = false;
return BSS_CHANGED_ERP_CTS_PROT |
BSS_CHANGED_ERP_PREAMBLE |
BSS_CHANGED_ERP_SLOT;
}
static void ieee80211_tasklet_handler(unsigned long data)
{
struct ieee80211_local *local = (struct ieee80211_local *) data;
struct sta_info *sta, *tmp;
struct skb_eosp_msg_data *eosp_data;
struct sk_buff *skb;
struct sk_buff_head local_list;
unsigned long flags;
__u32 queue_len = 0;
__atbm_skb_queue_head_init(&local_list);
spin_lock_irqsave(&local->skb_queue.lock,flags);
local->tasklet_running = true;
restart:
queue_len += atbm_skb_queue_len(&local->skb_queue);
atbm_skb_queue_splice_tail_init(&local->skb_queue, &local_list);
spin_unlock_irqrestore(&local->skb_queue.lock,flags);
while ((skb = __atbm_skb_dequeue(&local_list)) != NULL){
switch (skb->pkt_type) {
case IEEE80211_RX_MSG:
/* Clear skb->pkt_type in order to not confuse kernel
* netstack. */
skb->pkt_type = 0;
ieee80211_rx(local_to_hw(local), skb);
break;
case IEEE80211_TX_STATUS_MSG:
skb->pkt_type = 0;
ieee80211_tx_status(local_to_hw(local), skb);
break;
case IEEE80211_EOSP_MSG:
eosp_data = (void *)skb->cb;
for_each_sta_info(local, eosp_data->sta, sta, tmp) {
/* skip wrong virtual interface */
if (memcmp(eosp_data->iface,
sta->sdata->vif.addr, ETH_ALEN))
continue;
clear_sta_flag(sta, WLAN_STA_SP);
break;
}
atbm_dev_kfree_skb(skb);
break;
default:
WARN(1, "unknown type %d\n",skb->pkt_type);
atbm_dev_kfree_skb(skb);
break;
}
}
spin_lock_irqsave(&local->skb_queue.lock,flags);
if(!atbm_skb_queue_empty(&local->skb_queue))
goto restart;
local->tasklet_running = false;
spin_unlock_irqrestore(&local->skb_queue.lock,flags);
if(queue_len >= 1000){
atbm_printk_err("process %d skbs\n",queue_len);
}
#if 0
while ((skb = atbm_skb_dequeue(&local->skb_queue)) ||
(skb = atbm_skb_dequeue(&local->skb_queue_unreliable))) {
switch (skb->pkt_type) {
case IEEE80211_RX_MSG:
/* Clear skb->pkt_type in order to not confuse kernel
* netstack. */
skb->pkt_type = 0;
ieee80211_rx(local_to_hw(local), skb);
break;
case IEEE80211_TX_STATUS_MSG:
skb->pkt_type = 0;
ieee80211_tx_status(local_to_hw(local), skb);
break;
case IEEE80211_EOSP_MSG:
eosp_data = (void *)skb->cb;
for_each_sta_info(local, eosp_data->sta, sta, tmp) {
/* skip wrong virtual interface */
if (memcmp(eosp_data->iface,
sta->sdata->vif.addr, ETH_ALEN))
continue;
clear_sta_flag(sta, WLAN_STA_SP);
break;
}
atbm_dev_kfree_skb(skb);
break;
default:
WARN(1, "mac80211: Packet is of unknown type %d\n",
skb->pkt_type);
atbm_dev_kfree_skb(skb);
break;
}
}
#endif
}
#ifdef CONFIG_ATBM_MAC80211_NO_USE
static void ieee80211_restart_work(struct atbm_work_struct *work)
{
struct ieee80211_local *local =
container_of(work, struct ieee80211_local, restart_work);
/* wait for scan work complete */
atbm_flush_workqueue(local->workqueue);
mutex_lock(&local->mtx);
WARN(test_bit(SCAN_HW_SCANNING, &local->scanning)
#ifdef CONFIG_ATBM_SUPPORT_SCHED_SCAN
|| local->sched_scanning
#endif
,"called with hardware scan in progress\n");
mutex_unlock(&local->mtx);
rtnl_lock();
ieee80211_scan_cancel(local);
ieee80211_reconfig(local);
rtnl_unlock();
}
void ieee80211_restart_hw(struct ieee80211_hw *hw)
{
struct ieee80211_local *local = hw_to_local(hw);
trace_api_restart_hw(local);
wiphy_info(hw->wiphy,
"Hardware restart was requested\n");
/* use this reason, ieee80211_reconfig will unblock it */
ieee80211_stop_queues_by_reason(hw,
IEEE80211_QUEUE_STOP_REASON_SUSPEND);
atbm_schedule_work(&local->restart_work);
}
//EXPORT_SYMBOL(ieee80211_restart_hw);
#endif
int ieee80211_pre_restart_hw_sync(struct ieee80211_hw *hw)
{
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_sub_if_data *sdata;
if (!local->open_count)
goto prepare_done;
ieee80211_scan_cancel(local);
list_for_each_entry(sdata, &local->interfaces, list) {
atbm_cancel_work_sync(&sdata->work);
if (!ieee80211_sdata_running(sdata))
continue;
if(local->pending_scan_sdata&&(local->pending_scan_sdata == sdata))
{
atbm_printk_init("cancle pendding scan request\n");
local->scan_sdata = local->pending_scan_sdata;
local->scan_req = local->pending_scan_req;
local->pending_scan_sdata = NULL;
local->pending_scan_req = NULL;
ieee80211_scan_cancel(local);
}
ieee80211_work_purge(sdata);
#ifdef CONFIG_ATBM_SUPPORT_P2P
ieee80211_roc_purge(sdata);
#endif
}
prepare_done:
ieee80211_stop_queues_by_reason(hw,
IEEE80211_QUEUE_STOP_REASON_SUSPEND);
/* flush out all packets */
synchronize_net();
local->quiescing = true;
/* make quiescing visible to timers everywhere */
mb();
atbm_flush_workqueue(local->workqueue);
list_for_each_entry(sdata, &local->interfaces, list) {
atbm_cancel_work_sync(&sdata->work);
if (!ieee80211_sdata_running(sdata))
continue;
if (sdata->vif.type == NL80211_IFTYPE_MONITOR)
continue;
drv_remove_interface(local, &sdata->vif);
}
/* stop hardware - this must stop RX */
if (local->open_count)
ieee80211_stop_device(local);
local->suspended = true;
/* need suspended to be visible before quiescing is false */
barrier();
local->quiescing = false;
return 0;
}
int ieee80211_restart_hw_sync(struct ieee80211_hw *hw)
{
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_sub_if_data *sdata;
struct ieee80211_sub_if_data *sdata_ap = NULL;
struct sta_info *sta;
int res = 0;
u8 i = 0;
local->suspended = false;
mb();
/* setup fragmentation threshold */
drv_set_frag_threshold(local, hw->wiphy->frag_threshold);
/* reset coverage class */
drv_set_coverage_class(local, hw->wiphy->coverage_class);
/* everything else happens only if HW was up & running */
if (!local->open_count)
goto restart_end;
res = drv_start(local);
if (res) {
goto restart_end;
}
/* add interfaces */
list_for_each_entry(sdata, &local->interfaces, list) {
if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
sdata->vif.type != NL80211_IFTYPE_MONITOR &&
ieee80211_sdata_running(sdata))
res = drv_add_interface(local, &sdata->vif);
}
/* reconfigure tx conf */
if (hw->queues >= IEEE80211_NUM_ACS) {
list_for_each_entry(sdata, &local->interfaces, list) {
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
sdata->vif.type == NL80211_IFTYPE_MONITOR ||
!ieee80211_sdata_running(sdata))
continue;
for (i = 0; i < IEEE80211_NUM_ACS; i++)
drv_conf_tx(local, sdata, i,
&sdata->tx_conf[i]);
}
}
/* reconfigure hardware */
ieee80211_hw_config(local, ~0);
list_for_each_entry(sdata, &local->interfaces, list)
ieee80211_configure_filter(sdata);
/* Finally also reconfigure all the BSS information */
list_for_each_entry(sdata, &local->interfaces, list) {
u32 changed;
if (!ieee80211_sdata_running(sdata))
continue;
/* common change flags for all interface types */
changed = BSS_CHANGED_ERP_CTS_PROT |
BSS_CHANGED_ERP_PREAMBLE |
BSS_CHANGED_ERP_SLOT |
BSS_CHANGED_HT |
BSS_CHANGED_BASIC_RATES |
BSS_CHANGED_BEACON_INT |
BSS_CHANGED_BSSID |
BSS_CHANGED_CQM |
BSS_CHANGED_QOS |
BSS_CHANGED_PS;
switch (sdata->vif.type) {
case NL80211_IFTYPE_STATION:
mutex_lock(&sdata->u.mgd.mtx);
if(sdata->vif.bss_conf.assoc){
changed |= (BSS_CHANGED_STA_RESTART|BSS_CHANGED_ASSOC);
if (sdata->vif.bss_conf.arp_filter_enabled) {
changed |= BSS_CHANGED_ARP_FILTER;
}
}
ieee80211_bss_info_change_notify(sdata, changed);
mutex_unlock(&sdata->u.mgd.mtx);
ieee80211_connection_loss(&sdata->vif);
atbm_flush_workqueue(local->workqueue);
break;
#ifdef CONFIG_ATBM_SUPPORT_IBSS
case NL80211_IFTYPE_ADHOC:
changed |= BSS_CHANGED_IBSS;
#endif
/* fall through */
case NL80211_IFTYPE_AP:
changed |= BSS_CHANGED_SSID;
sdata_ap = sdata;
/* fall through */
#ifdef CONFIG_MAC80211_ATBM_MESH
case NL80211_IFTYPE_MESH_POINT:
#endif
changed |= BSS_CHANGED_BEACON |
BSS_CHANGED_BEACON_ENABLED;
ieee80211_bss_info_change_notify(sdata, changed);
break;
case NL80211_IFTYPE_WDS:
break;
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_MONITOR:
/* ignore virtual */
break;
case NL80211_IFTYPE_UNSPECIFIED:
case NUM_NL80211_IFTYPES:
#ifdef CONFIG_ATBM_SUPPORT_P2P
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_P2P_GO:
#endif
WARN_ON(1);
break;
default:
break;
}
}
/* add STAs back */
mutex_lock(&local->sta_mtx);
list_for_each_entry(sta, &local->sta_list, list) {
if (sta->uploaded) {
sdata = sta->sdata;
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
sdata = container_of(sdata->bss,
struct ieee80211_sub_if_data,
u.ap);
set_sta_flag(sta,WLAN_STA_RESTART);
WARN_ON(drv_sta_add(local, sdata, &sta->sta));
if(sdata->vif.type == NL80211_IFTYPE_AP){
WARN_ON(ieee80211_rx_sta_cook_deauthen(sta)==false);
WARN_ON(ieee80211_tx_sta_deauthen(sta)==false);
}
clear_sta_flag(sta,WLAN_STA_RESTART);
}
}
mutex_unlock(&local->sta_mtx);
if(sdata_ap){
WARN_ON(ieee80211_tx_multicast_deauthen(sdata_ap)==false);
}
/* add back keys */
list_for_each_entry(sdata, &local->interfaces, list)
if (ieee80211_sdata_running(sdata))
ieee80211_enable_keys(sdata);
/* setup RTS threshold */
list_for_each_entry(sdata, &local->interfaces, list)
drv_set_rts_threshold(local, sdata, sdata->vif.bss_conf.rts_threshold);
restart_end:
atbm_mod_timer(&local->sta_cleanup, jiffies + 1);
ieee80211_wake_queues_by_reason(hw,
IEEE80211_QUEUE_STOP_REASON_SUSPEND);
return res;
}
#ifdef CONFIG_ATBM_SMPS
static void ieee80211_recalc_smps_work(struct atbm_work_struct *work)
{
struct ieee80211_local *local =
container_of(work, struct ieee80211_local, recalc_smps);
mutex_lock(&local->iflist_mtx);
ieee80211_recalc_smps(local);
mutex_unlock(&local->iflist_mtx);
}
#endif
#ifdef CONFIG_INET
static int ieee80211_ifa_changed(struct notifier_block *nb,
unsigned long data, void *arg)
{
struct in_ifaddr *ifa = arg;
struct ieee80211_local *local =
container_of(nb, struct ieee80211_local,
ifa_notifier);
struct net_device *ndev = ifa->ifa_dev->dev;
struct wireless_dev *wdev = ndev->ieee80211_ptr;
struct in_device *idev;
struct ieee80211_sub_if_data *sdata;
struct ieee80211_bss_conf *bss_conf;
struct ieee80211_if_managed *ifmgd=NULL;
u8 bssid[ETH_ALEN];
u8 sta_associated = 0;
int c = 0;
/* Make sure it's our interface that got changed */
if (!wdev)
return NOTIFY_DONE;
if (wdev->wiphy != local->hw.wiphy)
return NOTIFY_DONE;
sdata = IEEE80211_DEV_TO_SUB_IF(ndev);
bss_conf = &sdata->vif.bss_conf;
if (!ieee80211_sdata_running(sdata))
return NOTIFY_DONE;
/* ARP filtering is only supported in managed mode */
switch (sdata->vif.type) {
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO:
break;
default:
return NOTIFY_DONE;
}
idev = __in_dev_get_rtnl(sdata->dev);
if (!idev)
return NOTIFY_DONE;
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
ifmgd = &sdata->u.mgd;
mutex_lock(&ifmgd->mtx);
}
/* Copy the addresses to the bss_conf list */
ifa = idev->ifa_list;
while (c < IEEE80211_BSS_ARP_ADDR_LIST_LEN && ifa) {
bss_conf->arp_addr_list[c] = ifa->ifa_address;
ifa = ifa->ifa_next;
c++;
}
/* If not all addresses fit the list, disable filtering */
if (ifa) {
sdata->arp_filter_state = false;
c = 0;
} else {
sdata->arp_filter_state = true;
}
bss_conf->arp_addr_cnt = c;
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
/* Configure driver only if associated */
if (ifmgd->associated) {
sta_associated = 1;
memcpy(bssid,ifmgd->associated->bssid,ETH_ALEN);
bss_conf->arp_filter_enabled = sdata->arp_filter_state;
ieee80211_bss_info_change_notify(sdata,
BSS_CHANGED_ARP_FILTER);
}
mutex_unlock(&ifmgd->mtx);
} else {
bss_conf->arp_filter_enabled = sdata->arp_filter_state;
ieee80211_bss_info_change_notify(sdata,
BSS_CHANGED_ARP_FILTER);
}
if(sdata->vif.type == NL80211_IFTYPE_STATION){
if(sta_associated&&(sdata->vif.bss_conf.arp_filter_enabled == true)
&&(sdata->vif.bss_conf.arp_addr_cnt>0)){
atbm_printk_debug("(%s):IPv4 enable,end_time(%d)\n",sdata->name,jiffies_to_msecs(jiffies));
mutex_lock(&local->mtx);
ieee80211_cancle_connecting_work(sdata,bssid,false);
mutex_unlock(&local->mtx);
}
}
return NOTIFY_DONE;
}
#ifdef IPV6_FILTERING
static void ieee80211_ifa6_changed_work(struct atbm_work_struct *work)
{
struct ieee80211_local *local =
container_of(work, struct ieee80211_local, ifa6_changed_work);
struct ieee80211_sub_if_data *sdata = local->ifa6_sdata;
struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
mutex_lock(&ifmgd->mtx);
if (ifmgd->associated) {
bss_conf->ndp_filter_enabled = sdata->ndp_filter_state;
ieee80211_bss_info_change_notify(sdata,
BSS_CHANGED_NDP_FILTER);
}
mutex_unlock(&ifmgd->mtx);
if(sdata->vif.type == NL80211_IFTYPE_STATION){
if(ifmgd->associated&&(sdata->vif.bss_conf.ndp_filter_enabled == true)
&&(sdata->vif.bss_conf.ndp_addr_cnt>0)){
mutex_lock(&local->mtx);
ieee80211_cancle_connecting_work(sdata,ifmgd->associated->bssid,false);
mutex_unlock(&local->mtx);
}
}
}
static int ieee80211_ifa6_changed(struct notifier_block *nb,
unsigned long data, void *arg)
{
struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)arg;
struct ieee80211_local *local =
container_of(nb, struct ieee80211_local,
ifa6_notifier);
struct net_device *ndev = (struct net_device *)ifa->idev->dev;
struct wireless_dev *wdev = ndev->ieee80211_ptr;
struct inet6_dev *idev;
struct ieee80211_sub_if_data *sdata;
struct ieee80211_bss_conf *bss_conf;
//struct ieee80211_if_managed *ifmgd;
int c = 0;
/* Make sure it's our interface that got changed */
if (!wdev)
return NOTIFY_DONE;
if (wdev->wiphy != local->hw.wiphy)
return NOTIFY_DONE;
sdata = IEEE80211_DEV_TO_SUB_IF(ndev);
bss_conf = &sdata->vif.bss_conf;
if (!ieee80211_sdata_running(sdata))
return NOTIFY_DONE;
/* NDP filtering is only supported in managed mode */
switch (sdata->vif.type) {
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO:
break;
default:
return NOTIFY_DONE;
}
idev = __in6_dev_get(sdata->dev);
if (!idev)
return NOTIFY_DONE;
#if 0
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
ifmgd = &sdata->u.mgd;
mutex_lock(&ifmgd->mtx);
}
#endif
list_for_each_entry(ifa, &idev->addr_list, if_list) {
c++;
if (c > IEEE80211_BSS_NDP_ADDR_LIST_LEN)
break;
memcpy(&bss_conf->ndp_addr_list[c-1], &ifa->addr, sizeof(struct in6_addr));
}
if (c > IEEE80211_BSS_NDP_ADDR_LIST_LEN) {
sdata->ndp_filter_state = false;
c = 0;
} else {
sdata->ndp_filter_state = true;
}
bss_conf->ndp_addr_cnt = c;
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
local->ifa6_sdata = sdata;
atbm_schedule_work(&local->ifa6_changed_work);
#if 0
/* Configure driver only if associated */
if (ifmgd->associated) {
bss_conf->ndp_filter_enabled = sdata->ndp_filter_state;
ieee80211_bss_info_change_notify(sdata,
BSS_CHANGED_NDP_FILTER);
}
mutex_unlock(&ifmgd->mtx);
#endif
} else {
bss_conf->ndp_filter_enabled = sdata->ndp_filter_state;
}
return NOTIFY_DONE;
}
#endif /*IPV6_FILTERING*/
#endif /*CONFIG_INET*/
#ifdef IEEE80211_SUPPORT_NAPI
static int ieee80211_napi_poll(struct napi_struct *napi, int budget)
{
struct ieee80211_local *local =
container_of(napi, struct ieee80211_local, napi);
return local->ops->napi_poll(&local->hw, budget);
}
void ieee80211_napi_schedule(struct ieee80211_hw *hw)
{
struct ieee80211_local *local = hw_to_local(hw);
napi_schedule(&local->napi);
}
//EXPORT_SYMBOL(ieee80211_napi_schedule);
void ieee80211_napi_complete(struct ieee80211_hw *hw)
{
struct ieee80211_local *local = hw_to_local(hw);
napi_complete(&local->napi);
}
//EXPORT_SYMBOL(ieee80211_napi_complete);
#endif
/* There isn't a lot of sense in it, but you can transmit anything you like */
static const struct ieee80211_txrx_stypes
ieee80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = {
#ifdef CONFIG_ATBM_SUPPORT_IBSS
[NL80211_IFTYPE_ADHOC] = {
.tx = 0xffff,
.rx = BIT(IEEE80211_STYPE_ACTION >> 4),
},
#endif
[NL80211_IFTYPE_STATION] = {
.tx = 0xffff,
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4),
},
[NL80211_IFTYPE_AP] = {
.tx = 0xffff,
.rx = BIT(IEEE80211_STYPE_ACTION >> 4)|
#ifndef ATBM_AP_SME
BIT(IEEE80211_STYPE_ASSOC_REQ >> 4)|
BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
BIT(IEEE80211_STYPE_DISASSOC >> 4) |
BIT(IEEE80211_STYPE_AUTH >> 4) |
BIT(IEEE80211_STYPE_DEAUTH >> 4) |
#endif
BIT(IEEE80211_STYPE_PROBE_REQ >> 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),
},
#ifdef CONFIG_ATBM_SUPPORT_P2P
[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_PROBE_REQ >> 4) |
#ifndef ATBM_AP_SME
BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
BIT(IEEE80211_STYPE_DISASSOC >> 4) |
BIT(IEEE80211_STYPE_AUTH >> 4) |
BIT(IEEE80211_STYPE_DEAUTH >> 4) |
#endif
BIT(IEEE80211_STYPE_ACTION >> 4),
},
#endif
#ifdef CONFIG_MAC80211_ATBM_MESH
[NL80211_IFTYPE_MESH_POINT] = {
.tx = 0xffff,
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_AUTH >> 4) |
BIT(IEEE80211_STYPE_DEAUTH >> 4),
},
#endif
};
void ieee80211_resume_timer(unsigned long data)
{
struct ieee80211_local *local = (struct ieee80211_local *)data;
atomic_set(&local->resume_timer_start,0);
atbm_printk_pm("end resume\n");
}
bool ieee80211_clear_extra_ie(struct ieee80211_sub_if_data *sdata,enum ieee80211_special_work_type type)
{
union iee80211_extra_ie *new_extra;
union iee80211_extra_ie *old_extra;
ASSERT_RTNL();
switch(type){
case IEEE80211_SPECIAL_AP_SPECIAL_BEACON:
case IEEE80211_SPECIAL_AP_SPECIAL_PROBRSP:
if(sdata->vif.type != NL80211_IFTYPE_AP){
return false;
}
if(rtnl_dereference(sdata->u.ap.beacon) == NULL){
return false;
}
if(type == IEEE80211_SPECIAL_AP_SPECIAL_BEACON){
old_extra = (union iee80211_extra_ie *)rtnl_dereference(sdata->u.ap.beacon_extra);
}else{
old_extra = (union iee80211_extra_ie *)rtnl_dereference(sdata->u.ap.probe_response_extra);
}
break;
case IEEE80211_SPECIAL_STA_SPECIAL_PROBR:
if(sdata->vif.type != NL80211_IFTYPE_STATION){
return false;
}
old_extra = (union iee80211_extra_ie *)rtnl_dereference(sdata->u.mgd.probe_request_extra);
break;
default:
return false;
}
new_extra = atbm_kzalloc(sizeof(union iee80211_extra_ie),GFP_KERNEL);
if(new_extra == NULL){
return false;
}
new_extra->extra.extra = NULL;
new_extra->extra.extra_len = 0;
switch(type){
case IEEE80211_SPECIAL_AP_SPECIAL_BEACON:
case IEEE80211_SPECIAL_AP_SPECIAL_PROBRSP:
if(type == IEEE80211_SPECIAL_AP_SPECIAL_BEACON)
rcu_assign_pointer(sdata->u.ap.beacon_extra, &new_extra->beacon);
else
rcu_assign_pointer(sdata->u.ap.probe_response_extra, &new_extra->proberesponse);
synchronize_rcu();
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED |
BSS_CHANGED_BEACON |
BSS_CHANGED_SSID);
break;
case IEEE80211_SPECIAL_STA_SPECIAL_PROBR:
rcu_assign_pointer(sdata->u.mgd.probe_request_extra,&new_extra->proberequest);
synchronize_rcu();
break;
default:
break;
}
if(old_extra)
atbm_kfree(old_extra);
return true;
}
bool ieee80211_updata_extra_ie(struct ieee80211_sub_if_data *sdata,enum ieee80211_special_work_type type,
union iee80211_extra_ie *extra)
{
union iee80211_extra_ie *new_extra;
union iee80211_extra_ie *old_extra;
ASSERT_RTNL();
switch(type){
case IEEE80211_SPECIAL_AP_SPECIAL_BEACON:
case IEEE80211_SPECIAL_AP_SPECIAL_PROBRSP:
if(sdata->vif.type != NL80211_IFTYPE_AP){
return false;
}
if(rtnl_dereference(sdata->u.ap.beacon) == NULL){
return false;
}
if(type == IEEE80211_SPECIAL_AP_SPECIAL_BEACON)
old_extra = (union iee80211_extra_ie *)rtnl_dereference(sdata->u.ap.beacon_extra);
else
old_extra = (union iee80211_extra_ie *)rtnl_dereference(sdata->u.ap.probe_response_extra);
break;
case IEEE80211_SPECIAL_STA_SPECIAL_PROBR:
if(sdata->vif.type != NL80211_IFTYPE_STATION){
return false;
}
old_extra = (union iee80211_extra_ie *)rtnl_dereference(sdata->u.mgd.probe_request_extra);
break;
default:
return false;
}
new_extra = atbm_kzalloc(sizeof(union iee80211_extra_ie)+extra->extra.extra_len,GFP_KERNEL);
if(new_extra == NULL){
return false;
}
new_extra->extra.extra = ((u8*)new_extra) + (sizeof(union iee80211_extra_ie));
new_extra->extra.extra_len = extra->extra.extra_len;
memcpy(new_extra->extra.extra,extra->extra.extra,extra->extra.extra_len);
switch(type){
case IEEE80211_SPECIAL_AP_SPECIAL_BEACON:
case IEEE80211_SPECIAL_AP_SPECIAL_PROBRSP:
if(type == IEEE80211_SPECIAL_AP_SPECIAL_BEACON)
rcu_assign_pointer(sdata->u.ap.beacon_extra, &new_extra->beacon);
else
rcu_assign_pointer(sdata->u.ap.probe_response_extra, &new_extra->proberesponse);
synchronize_rcu();
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED |
BSS_CHANGED_BEACON |
BSS_CHANGED_SSID);
break;
case IEEE80211_SPECIAL_STA_SPECIAL_PROBR:
rcu_assign_pointer(sdata->u.mgd.probe_request_extra,&new_extra->proberequest);
synchronize_rcu();
break;
default:
break;
}
if(old_extra)
atbm_kfree(old_extra);
return true;
}
#ifdef CONFIG_ATBM_SUPPORT_AP_CONFIG
bool ieee80211_update_ap_config(struct ieee80211_sub_if_data *sdata,struct ieee80211_internal_ap_conf *conf,bool clear)
{
struct ieee80211_internal_ap_conf *new_conf;
struct ieee80211_internal_ap_conf *old_conf;
old_conf = rtnl_dereference(sdata->internal_ap_conf);
if(clear == false){
new_conf = atbm_kzalloc(sizeof(struct ieee80211_internal_ap_conf),GFP_KERNEL);
if(new_conf == NULL){
return false;
}
memcpy(new_conf,conf,sizeof(struct ieee80211_internal_ap_conf));
atbm_printk_debug("%s:ssid[%s],bssid[%pM],channel[%d]\n",__func__,conf->ssid,conf->bssid,conf->channel);
}else {
new_conf = NULL;
}
rcu_assign_pointer(sdata->internal_ap_conf,new_conf);
synchronize_rcu();
if(old_conf)
atbm_kfree(old_conf);
return true;
}
#endif
static bool ieee80211_special_work_scan_cb(struct ieee80211_sub_if_data *sdata,void *scan_store,
struct ieee80211_internal_scan_result *result,bool finish){
struct ieee80211_rx_status *rx_status;
if(finish == true){
return false;
}
if (!ieee80211_sdata_running(sdata)){
return false;
}
if(sdata->vif.type != NL80211_IFTYPE_STATION){
return false;
}
if(result->sta.skb == NULL)
return false;
atbm_printk_debug("%s:receive\n",__func__);
rx_status = (struct ieee80211_rx_status *) result->sta.skb->cb;
rx_status->flag |= RX_FLAG_STA_LISTEN;
atbm_skb_queue_tail(&sdata->skb_queue, result->sta.skb);
ieee80211_queue_work(&sdata->local->hw, &sdata->work);
return true;
}
bool atbm_internal_cmd_scan_triger(struct ieee80211_sub_if_data *sdata,struct ieee80211_internal_scan_request *req);
static void ieee80211_special_work(struct atbm_work_struct *work)
{
struct ieee80211_local *local =
container_of(work, struct ieee80211_local, special_work);
struct sk_buff *skb = NULL;
struct ieee80211_sub_if_data *sdata = NULL;
struct ieee80211_sub_if_data *sdata_tmp = NULL;
struct ieee80211_special_work_common *work_common;
/*
*rtnl lock here is need,make sure other cmd from application can not process at
*the same time
*/
rtnl_lock();
while ((skb = atbm_skb_dequeue(&local->special_req_list))){
work_common = (struct ieee80211_special_work_common *)skb->cb;
list_for_each_entry(sdata_tmp, &local->interfaces, list) {
/*
*it is important to see if the sdata is running
*/
if (!ieee80211_sdata_running(sdata_tmp))
continue;
if(sdata_tmp != work_common->req_sdata)
continue;
sdata = sdata_tmp;
break;
}
/*
*maybe sdata has been removed or down
*/
if(sdata == NULL){
atbm_kfree_skb(skb);
continue;
}
switch(skb->pkt_type){
case IEEE80211_SPECIAL_AP_SPECIAL_BEACON:
case IEEE80211_SPECIAL_AP_SPECIAL_PROBRSP:
case IEEE80211_SPECIAL_STA_SPECIAL_PROBR:
{
struct ieee80211_update_special *special_update;
union iee80211_extra_ie extra;
special_update = (struct ieee80211_update_special*)skb->cb;
/*
*special ie err
*/
if((special_update->special_len == 0)||(special_update->special_ie == NULL)){
ieee80211_clear_extra_ie(sdata,(enum ieee80211_special_work_type)skb->pkt_type);
break;
}
extra.extra.extra = special_update->special_ie;
extra.extra.extra_len = special_update->special_len;
ieee80211_updata_extra_ie(sdata,(enum ieee80211_special_work_type)skb->pkt_type,&extra);
break;
}
case IEEE80211_SPECIAL_STA_PASSICE_SCAN:
case IEEE80211_SPECIAL_STA_POSITIVE_SCAN:
{
struct ieee80211_special_work_scan *special_scan;
struct ieee80211_internal_scan_request req;
special_scan = (struct ieee80211_special_work_scan *)skb->cb;
memset(&req,0,sizeof(struct ieee80211_internal_scan_request));
req.channels = special_scan->channels;
req.n_channels = special_scan->n_channels;
req.ies = special_scan->ie;
req.ie_len = special_scan->ie_len;
req.ssids = special_scan->ssid;
if(req.ssids)
req.n_ssids = 1;
if(special_scan->bssid){
req.req_flags |= IEEE80211_INTERNAL_SCAN_FLAGS__NEED_BSSID;
memcpy(req.bssid,special_scan->bssid,6);
atbm_printk_err("[Scan Bssid][%pM]\n",req.bssid);
}
if(skb->pkt_type == IEEE80211_SPECIAL_STA_PASSICE_SCAN)
req.req_flags = IEEE80211_INTERNAL_SCAN_FLAGS__PASSAVI_SCAN;
req.req_flags |= IEEE80211_INTERNAL_SCAN_FLAGS__NEED_SKB;
rcu_assign_pointer(req.result_handle,ieee80211_special_work_scan_cb);
rcu_assign_pointer(req.priv,NULL);
atbm_internal_cmd_scan_triger(sdata,&req);
break;
}
case IEEE80211_SPECIAL_CHANGE_CHANNEL_FREQ:
break;
case IEEE80211_SPECIAL_STA_SET_AP_CONFIG:
break;
default:
WARN_ON(1);
break;
}
atbm_kfree_skb(skb);
}
rtnl_unlock();
}
bool ieee80211_special_freq_update(struct ieee80211_local *local,struct ieee80211_special_freq *special)
{
int hash_index = 0;
struct hlist_head *hlist;
struct hlist_node *node;
struct ieee80211_special_freq *freq_node;
struct ieee80211_special_freq *freq_node_target = NULL;
ASSERT_RTNL();
hash_index = atbm_hash_index((u8*)(&special->channel),sizeof(void*),ATBM_COMMON_HASHBITS);
hlist = &local->special_freq_list[hash_index];
hlist_for_each(node,hlist){
freq_node = hlist_entry(node,struct ieee80211_special_freq,hnode);
if (special->channel == freq_node->channel){
freq_node_target = freq_node;
break;
}
}
if(freq_node_target == NULL){
freq_node_target = atbm_kzalloc(sizeof(struct ieee80211_special_freq),GFP_ATOMIC);
if(freq_node_target == NULL){
return false;
}
hlist_add_head(&freq_node_target->hnode,hlist);
atbm_printk_debug("%s:channel[%d] freq[%d]->freq[%d]\n",__func__,
channel_hw_value(special->channel),channel_center_freq(special->channel),
special->freq);
}
//BUG_ON(freq_node_target == NULL);
if(freq_node_target == NULL){
atbm_printk_err("%s %d ,ERROR !!! freq_node_target is NULL\n",__func__,__LINE__);
return false;
}
freq_node_target->channel = special->channel;
freq_node_target->freq = special->freq;
channel_mask_special(freq_node_target->channel);
return true;
}
void ieee80211_special_freq_clear(struct ieee80211_local *local,struct ieee80211_special_freq *special)
{
int hash_index = 0;
struct hlist_head *hlist;
struct hlist_node *node;
struct hlist_node *node_temp;
struct ieee80211_special_freq *freq_node;
ASSERT_RTNL();
channel_clear_special(special->channel);
hash_index = atbm_hash_index((u8*)(&special->channel),sizeof(void*),ATBM_COMMON_HASHBITS);
hlist = &local->special_freq_list[hash_index];
hlist_for_each_safe(node,node_temp,hlist){
freq_node = hlist_entry(node,struct ieee80211_special_freq,hnode);
if(special->channel != freq_node->channel){
continue;
}
atbm_printk_debug("%s:channel(%d),freq(%d)\n",__func__,channel_hw_value(freq_node->channel),freq_node->freq);
hlist_del(&freq_node->hnode);
atbm_kfree(freq_node);
return;
}
WARN_ON(1);
}
void ieee80211_special_freq_free(struct ieee80211_local *local)
{
struct hlist_head *hlist;
struct hlist_node *node;
struct hlist_node *node_temp;
struct ieee80211_special_freq *freq_node;
int hash_index = 0;
ASSERT_RTNL();
for(hash_index = 0;hash_index<ATBM_COMMON_HASHENTRIES;hash_index++){
hlist = &local->special_freq_list[hash_index];
hlist_for_each_safe(node,node_temp,hlist){
freq_node = hlist_entry(node,struct ieee80211_special_freq,hnode);
hlist_del(&freq_node->hnode);
atbm_kfree(freq_node);
}
}
}
static void ieee80211_name_init(struct ieee80211_local *local)
{
local->ieee80211_name_base = NULL;
local->ieee80211_name_len = 0;
spin_lock_init(&local->ieee80211_name_lock);
INIT_LIST_HEAD(&local->ieee80211_name_list);
}
static void ieee80211_name_free(struct ieee80211_local *local)
{
struct ieee80211_name_def *def = NULL;
spin_lock_bh(&local->ieee80211_name_lock);
while(!list_empty(&local->ieee80211_name_list)){
def = list_first_entry(&local->ieee80211_name_list, struct ieee80211_name_def,list);
list_del(&def->list);
atbm_kfree(def);
}
spin_unlock_bh(&local->ieee80211_name_lock);
}
//#include <net/regulatory.h>
//#include <net/wireless/reg.h>
int country_chan_found(const char *country)
{
int i = 0,chan = 0;
struct country_chan{
char *country;
u8 chan;
};
struct country_chan country_t[15]={
{"CN",13},
{"JP",14},
{"US",11},{"CA",11},{"CO",11},{"DO",11},{"GT",11},
{"MX",11},{"PA",11},{"PT",11},{"TW",11},{"UZ",11},
{NULL,0}
};
do{
if(memcmp(country,country_t[i].country,2) == 0){
chan = country_t[i].chan;
break;
}
i++;
}while(country_t[i].country);
if(country_t[i].country == NULL){
return 14;
}
return chan;
}
void atbm_get_cfg80211_country_code(struct wiphy *wiphy,struct regulatory_request *request)
{
#ifdef CONFIG_ATBM_5G_PRETEND_2G
atbm_printk_err("atbm_get_cfg80211_country_code\n");
#else
struct ieee80211_local *local;
const struct ieee80211_regdomain *cfg80211_regdomain;
if((!wiphy) || (!request)){
//atbm_printk_err("%s(%d) ,%s \n",wiphy?" ":"wiphy is NULL",request?" ":"request is NULL");
return;
}
local = wiphy_priv(wiphy);
memcpy(local->country_code,request->alpha2,2);
cfg80211_regdomain = rtnl_dereference(wiphy->regd);
if(cfg80211_regdomain){
atbm_printk_err("start freq = %d , end freq = %d \n",
cfg80211_regdomain->reg_rules[0].freq_range.start_freq_khz,
cfg80211_regdomain->reg_rules[0].freq_range.end_freq_khz);
local->country_support_chan = (cfg80211_regdomain->reg_rules[0].freq_range.end_freq_khz -
cfg80211_regdomain->reg_rules[0].freq_range.start_freq_khz)/5000 - 3;
if(memcmp(request->alpha2,"JP",2) == 0){
local->country_support_chan += ((cfg80211_regdomain->reg_rules[1].freq_range.end_freq_khz -
cfg80211_regdomain->reg_rules[1].freq_range.start_freq_khz)/5000 - 3);
}
}else{
atbm_printk_err("cfg80211_regdomain is NULL\n");
local->country_support_chan = country_chan_found(local->country_code);
}
atbm_printk_err("country code %c%c country_support_chan = %d \n",request->alpha2[0],request->alpha2[1],local->country_support_chan);
#endif
}
struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
const struct ieee80211_ops *ops)
{
struct ieee80211_local *local;
int priv_size, i;
struct wiphy *wiphy;
/* Ensure 32-byte alignment of our private data and hw private data.
* We use the wiphy priv data for both our ieee80211_local and for
* the driver's private data
*
* In memory it'll be like this:
*
* +-------------------------+
* | struct wiphy |
* +-------------------------+
* | struct ieee80211_local |
* +-------------------------+
* | driver's private data |
* +-------------------------+
*
*/
priv_size = ALIGN(sizeof(*local), NETDEV_ALIGN) + priv_data_len;
wiphy = wiphy_new(&mac80211_config_ops, priv_size);
if (!wiphy)
return NULL;
wiphy->reg_notifier = atbm_get_cfg80211_country_code;
wiphy->mgmt_stypes = ieee80211_default_mgmt_stypes;
wiphy->privid = mac80211_wiphy_privid;
wiphy->flags |= WIPHY_FLAG_NETNS_OK
#ifdef CONFIG_ATBM_4ADDR
| WIPHY_FLAG_4ADDR_AP
| WIPHY_FLAG_4ADDR_STATION
#endif
#if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 2, 0))
| WIPHY_FLAG_REPORTS_OBSS
#endif
;
#ifdef CONFIG_ATBM_SUPPORT_SAE
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0))
//#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 0, 0))
wiphy->features |= NL80211_FEATURE_SAE;
#endif
#endif
if (!ops->set_key)
wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
wiphy->bss_priv_size = sizeof(struct ieee80211_bss);
local = wiphy_priv(wiphy);
local->hw.wiphy = wiphy;
local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN);
#if 0
BUG_ON(!ops->tx);
BUG_ON(!ops->start);
BUG_ON(!ops->stop);
BUG_ON(!ops->config);
BUG_ON(!ops->add_interface);
BUG_ON(!ops->remove_interface);
BUG_ON(!ops->configure_filter);
#endif
if(!ops->tx || !ops->start || !ops->stop || !ops->add_interface || !ops->remove_interface || !ops->configure_filter){
atbm_printk_err("%s %d ,ERROR !!! ops->tx=%p ops->start=%p ops->stop=%p ops->config=%p"
" ops->add_interface=%p ops->remove_interface=%p ops->configure_filter=%p\n",
__func__,__LINE__,
ops->tx,
ops->start,
ops->stop,
ops->config,
ops->add_interface,
ops->remove_interface,
ops->configure_filter);
return NULL;
}
local->ops = ops;
local->hw.conf.chan_conf = &local->chan_state.conf;
ieee80211_name_init(local);
/* set up some defaults */
local->hw.queues = 1;
local->hw.max_rates = 1;
local->hw.max_report_rates = 0;
local->hw.max_rx_aggregation_subframes = 64;//IEEE80211_MAX_AMPDU_BUF;
local->hw.max_tx_aggregation_subframes = 64;//IEEE80211_MAX_AMPDU_BUF;
local->hw.offchannel_tx_hw_queue = IEEE80211_INVAL_HW_QUEUE;
local->user_power_level = -1;
local->uapsd_queues = IEEE80211_DEFAULT_UAPSD_QUEUES;
local->uapsd_max_sp_len = IEEE80211_DEFAULT_MAX_SP_LEN;
local->tasklet_running = false;
local->adaptive_started = false;
INIT_LIST_HEAD(&local->interfaces);
mutex_init(&local->iflist_mtx);
mutex_init(&local->mtx);
mutex_init(&local->key_mtx);
spin_lock_init(&local->filter_lock);
spin_lock_init(&local->queue_stop_reason_lock);
LOCAL_CONNECT_INIT(local);
/*
* The rx_skb_queue is only accessed from tasklets,
* but other SKB queues are used from within IRQ
* context. Therefore, this one needs a different
* locking class so our direct, non-irq-safe use of
* the queue's lock doesn't throw lockdep warnings.
*/
skb_queue_head_init_class(&local->rx_skb_queue,
&ieee80211_rx_skb_queue_class);
spin_lock_init(&local->rx_path_lock);
ATBM_INIT_DELAYED_WORK(&local->scan_work, ieee80211_scan_work);
ieee80211_work_init(local);
#ifdef CONFIG_ATBM_MAC80211_NO_USE
ATBM_INIT_WORK(&local->restart_work, ieee80211_restart_work);
#endif
ATBM_INIT_WORK(&local->special_work,ieee80211_special_work);
atbm_skb_queue_head_init(&local->special_req_list);
atbm_common_hash_list_init(local->special_freq_list,ATBM_COMMON_HASHENTRIES);
#ifdef CONFIG_INET
#ifdef IPV6_FILTERING
ATBM_INIT_WORK(&local->ifa6_changed_work, ieee80211_ifa6_changed_work);
#endif /*IPV6_FILTERING*/
#endif
#ifdef CONFIG_ATBM_SMPS
ATBM_INIT_WORK(&local->recalc_smps, ieee80211_recalc_smps_work);
#endif
local->smps_mode = IEEE80211_SMPS_OFF;
#ifdef CONFIG_ATBM_SUPPORT_SCHED_SCAN
ATBM_INIT_WORK(&local->sched_scan_stopped_work,
ieee80211_sched_scan_stopped_work);
#endif
sta_info_init(local);
for (i = 0; i < IEEE80211_MAX_QUEUES; i++) {
atbm_skb_queue_head_init(&local->pending[i]);
atomic_set(&local->agg_queue_stop[i], 0);
}
tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending,
(unsigned long)local);
tasklet_init(&local->tasklet,
ieee80211_tasklet_handler,
(unsigned long) local);
atbm_skb_queue_head_init(&local->skb_queue);
atbm_skb_queue_head_init(&local->skb_queue_unreliable);
#ifdef IEEE80211_SUPPORT_NAPI
/* init dummy netdev for use w/ NAPI */
init_dummy_netdev(&local->napi_dev);
#endif
ieee80211_led_names(local);
#ifdef CONFIG_ATBM_SUPPORT_P2P
ieee80211_hw_roc_setup(local);
#endif
atomic_set(&local->resume_timer_start,0);
atbm_setup_timer(&local->resume_timer, ieee80211_resume_timer,
(unsigned long)local);
ieee80211_scan_internal_int(local);
return local_to_hw(local);
}
//EXPORT_SYMBOL(ieee80211_alloc_hw);
#ifdef CONFIG_ATBM_APOLLO_DEBUG
extern int atbm_debug_init_common(void * priv);
#endif
static char *init_dev_name = "ALTOBEAM";
int ieee80211_register_hw(struct ieee80211_hw *hw)
{
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_channel_state *chan_state = &local->chan_state;
int result, i;
enum ieee80211_band band;
int channels, max_bitrates;
bool supp_ht;
static const u32 cipher_suites[] = {
/* keep WEP first, it may be removed below */
WLAN_CIPHER_SUITE_WEP40,
WLAN_CIPHER_SUITE_WEP104,
WLAN_CIPHER_SUITE_TKIP,
WLAN_CIPHER_SUITE_CCMP,
#ifdef CONFIG_WAPI_SUPPORT
WLAN_CIPHER_SUITE_SMS4,
#endif
/* keep last -- depends on hw flags! */
WLAN_CIPHER_SUITE_AES_CMAC
};
if (hw->flags & IEEE80211_HW_QUEUE_CONTROL &&
(local->hw.offchannel_tx_hw_queue == IEEE80211_INVAL_HW_QUEUE ||
local->hw.offchannel_tx_hw_queue >= local->hw.queues))
return -EINVAL;
if (hw->max_report_rates == 0)
hw->max_report_rates = hw->max_rates;
// BUG_ON(strlen(WIFI_IF1NAME)>IFNAMSIZ-1);
// BUG_ON(strlen(WIFI_IF2NAME)>IFNAMSIZ-1);
if((strlen(WIFI_IF1NAME)>IFNAMSIZ-1) || (strlen(WIFI_IF2NAME)>IFNAMSIZ-1)){
atbm_printk_err("%s %d ,ERROR !!! if1name len=%d , if2name len=%d , std_name len=%d\n",
__func__,__LINE__,strlen(WIFI_IF1NAME),strlen(WIFI_IF2NAME),IFNAMSIZ);
return -EINVAL;
}
/*
* generic code guarantees at least one band,
* set this very early because much code assumes
* that chan_state->conf.channel is assigned
*/
LOCAL_SET_CONNECT_STOP(local);
channels = 0;
max_bitrates = 0;
supp_ht = false;
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
struct ieee80211_supported_band *sband;
sband = local->hw.wiphy->bands[band];
if (!sband)
continue;
if (!chan_state->oper_channel) {
/* init channel we're on */
/* soumik: Set default channel to a non-social channel */
chan_state->conf.channel =
/* chan_state->oper_channel = &sband->channels[0]; */
chan_state->oper_channel = &sband->channels[2];
chan_state->conf.channel_type = NL80211_CHAN_NO_HT;
}
channels += sband->n_channels;
if (max_bitrates < sband->n_bitrates)
max_bitrates = sband->n_bitrates;
supp_ht = supp_ht || sband->ht_cap.ht_supported;
}
local->int_scan_req = atbm_kzalloc(sizeof(*local->int_scan_req) +
sizeof(void *) * channels, GFP_KERNEL);
if (!local->int_scan_req)
return -ENOMEM;
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
if (!local->hw.wiphy->bands[band])
continue;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0))
local->int_scan_req->rates[band] = (u32) -1;
#endif
}
/* if low-level driver supports AP, we also support VLAN */
if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) {
hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP_VLAN);
hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_AP_VLAN);
}
/* mac80211 always supports monitor */
hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR);
/*
* mac80211 doesn't support more than 1 channel, and also not more
* than one IBSS interface
*/
for (i = 0; i < hw->wiphy->n_iface_combinations; i++) {
const struct ieee80211_iface_combination *c;
#ifdef CONFIG_ATBM_SUPPORT_IBSS
int j;
#endif
c = &hw->wiphy->iface_combinations[i];
if (c->num_different_channels > 1)
return -EINVAL;
#ifdef CONFIG_ATBM_SUPPORT_IBSS
for (j = 0; j < c->n_limits; j++)
if ((c->limits[j].types & BIT(NL80211_IFTYPE_ADHOC)) &&
c->limits[j].max > 1)
return -EINVAL;
#endif
}
#ifndef CONFIG_MAC80211_ATBM_MESH
/* mesh depends on Kconfig, but drivers should set it if they want */
local->hw.wiphy->interface_modes &= ~BIT(NL80211_IFTYPE_MESH_POINT);
#else
/* if the underlying driver supports mesh, mac80211 will (at least)
* provide routing of mesh authentication frames to userspace */
if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_MESH_POINT))
local->hw.wiphy->flags |= WIPHY_FLAG_MESH_AUTH;
#endif
/* mac80211 supports control port protocol changing */
local->hw.wiphy->flags |= WIPHY_FLAG_CONTROL_PORT_PROTOCOL;
if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
local->hw.wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)
local->hw.wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC;
/*
* Calculate scan IE length -- we need this to alloc
* memory and to subtract from the driver limit. It
* includes the DS Params, (extended) supported rates, and HT
* information -- SSID is the driver's responsibility.
*/
local->scan_ies_len = 4 + max_bitrates /* (ext) supp rates */ +
3 /* DS Params */;
if (supp_ht)
local->scan_ies_len += 2 + sizeof(struct ieee80211_ht_cap);
if (!local->ops->hw_scan) {
/* For hw_scan, driver needs to set these up. */
local->hw.wiphy->max_scan_ssids = 4;
local->hw.wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
}
#ifdef SIGMSTAR_SCAN_FEATURE
{
unsigned int randNum;
for(i=0;i<CHANNEL_NUM;i++){
get_random_bytes(&randNum,sizeof(unsigned int));
local->noise_floor[i] = -85 - ((u32)randNum%10);
}
}
#endif //SIGMSTAR_SCAN_FEATURE
/*
* If the driver supports any scan IEs, then assume the
* limit includes the IEs mac80211 will add, otherwise
* leave it at zero and let the driver sort it out; we
* still pass our IEs to the driver but userspace will
* not be allowed to in that case.
*/
if (local->hw.wiphy->max_scan_ie_len)
local->hw.wiphy->max_scan_ie_len -= local->scan_ies_len;
/* Set up cipher suites unless driver already did */
if (!local->hw.wiphy->cipher_suites) {
local->hw.wiphy->cipher_suites = cipher_suites;
local->hw.wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
if (!(local->hw.flags & IEEE80211_HW_MFP_CAPABLE))
local->hw.wiphy->n_cipher_suites--;
}
#ifdef CONFIG_ATBM_USE_SW_ENC
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(5, 4, 0))
if (IS_ERR(local->wep_tx_tfm) || IS_ERR(local->wep_rx_tfm)) {
#else
//need find the condition later
if (0) {
#endif
if (local->hw.wiphy->cipher_suites == cipher_suites) {
local->hw.wiphy->cipher_suites += 2;
local->hw.wiphy->n_cipher_suites -= 2;
} else {
u32 *suites;
int r, w = 0;
/* Filter out WEP */
suites = kmemdup(
local->hw.wiphy->cipher_suites,
sizeof(u32) * local->hw.wiphy->n_cipher_suites,
GFP_KERNEL);
if (!suites)
return -ENOMEM;
for (r = 0; r < local->hw.wiphy->n_cipher_suites; r++) {
u32 suite = local->hw.wiphy->cipher_suites[r];
if (suite == WLAN_CIPHER_SUITE_WEP40 ||
suite == WLAN_CIPHER_SUITE_WEP104)
continue;
suites[w++] = suite;
}
local->hw.wiphy->cipher_suites = suites;
local->hw.wiphy->n_cipher_suites = w;
local->wiphy_ciphers_allocated = true;
}
}
#endif
if (!local->ops->remain_on_channel)
local->hw.wiphy->max_remain_on_channel_duration = 5000;
#ifdef CONFIG_ATBM_SUPPORT_SCHED_SCAN
if (local->ops->sched_scan_start)
local->hw.wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
#endif
#ifdef ATBM_SURPORT_TDLS
/* mac80211 based drivers don't support internal TDLS setup */
#if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 0, 8))
if (local->hw.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)
local->hw.wiphy->flags |= WIPHY_FLAG_TDLS_EXTERNAL_SETUP;
#endif
#endif
result = wiphy_register(local->hw.wiphy);
if (result < 0)
goto fail_wiphy_register;
local->hw.wiphy->dev.init_name = init_dev_name;
/*
* We use the number of queues for feature tests (QoS, HT) internally
* so restrict them appropriately.
*/
if (hw->queues > IEEE80211_MAX_QUEUES)
hw->queues = IEEE80211_MAX_QUEUES;
local->workqueue =
atbm_alloc_ordered_workqueue(wiphy_name(local->hw.wiphy), 0);
if (!local->workqueue) {
result = -ENOMEM;
goto fail_workqueue;
}
/*
* The hardware needs headroom for sending the frame,
* and we need some headroom for passing the frame to monitor
* interfaces, but never both at the same time.
*/
local->tx_headroom = max_t(unsigned int , local->hw.extra_tx_headroom,
IEEE80211_TX_STATUS_HEADROOM);
debugfs_hw_add(local);
#ifdef CONFIG_ATBM_APOLLO_DEBUG
atbm_debug_init_common(hw->priv);
#endif
/*
* if the driver doesn't specify a max listen interval we
* use 5 which should be a safe default
*/
if (local->hw.max_listen_interval == 0)
local->hw.max_listen_interval = 5;
#ifdef CONFIG_ATBM_USE_SW_ENC
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(5, 4, 0))
result = ieee80211_wep_init(local);
#else
ieee80211_wep_init(local);
result = 0;
#endif
if (result < 0)
atbm_printk_err("Failed to initialize wep: %d\n",
result);
#endif
rtnl_lock();
#ifndef CONFIG_RATE_HW_CONTROL
result = atbm_ieee80211_init_rate_ctrl_alg(local,
hw->rate_control_algorithm);
if (result < 0) {
wiphy_debug(local->hw.wiphy,
"Failed to initialize rate control algorithm\n");
goto fail_rate;
}
#endif
#ifdef CONFIG_WIRELESS_EXT
register_wext_common(local);
#endif
/* add one default STA interface if supported */
if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION)) {
result = ieee80211_if_add(local, WIFI_IF1NAME, NULL,
NL80211_IFTYPE_STATION, NULL);
if (result)
atbm_printk_warn("Failed to add default virtual iface\n");
#if (ATBM_WIFI_PLATFORM != 12) && (NEED_P2P0_INTERFACE == 1)//CDLINUX no need p2p0
result = ieee80211_if_add(local, WIFI_IF2NAME, NULL,
NL80211_IFTYPE_STATION, NULL);
if (result)
atbm_printk_warn("Failed to add default virtual p2p iface\n");
#endif //#if (ATBM_WIFI_PLATFORM == 11)
}
rtnl_unlock();
ieee80211_led_init(local);
#ifdef CONFIG_ATBM_PM_QOS
local->network_latency_notifier.notifier_call =
ieee80211_max_network_latency;
result = pm_qos_add_notifier(PM_QOS_NETWORK_LATENCY,
&local->network_latency_notifier);
if (result) {
rtnl_lock();
goto fail_pm_qos;
}
#endif
#ifdef CONFIG_INET
local->ifa_notifier.notifier_call = ieee80211_ifa_changed;
result = register_inetaddr_notifier(&local->ifa_notifier);
if (result)
goto fail_ifa;
#ifdef IPV6_FILTERING
local->ifa6_notifier.notifier_call = ieee80211_ifa6_changed;
result = register_inet6addr_notifier(&local->ifa6_notifier);
if (result)
goto fail_ifa;
#endif /*IPV6_FILTERING*/
#endif
#ifdef IEEE80211_SUPPORT_NAPI
netif_napi_add(&local->napi_dev, &local->napi, ieee80211_napi_poll,
local->hw.napi_weight);
#endif
return 0;
#ifdef CONFIG_INET
fail_ifa:
#ifdef CONFIG_ATBM_PM_QOS
pm_qos_remove_notifier(PM_QOS_NETWORK_LATENCY,
&local->network_latency_notifier);
#endif
rtnl_lock();
#endif
#ifdef CONFIG_ATBM_PM_QOS
fail_pm_qos:
ieee80211_led_exit(local);
ieee80211_remove_interfaces(local);
#endif
#ifndef CONFIG_RATE_HW_CONTROL
fail_rate:
#endif
rtnl_unlock();
#ifdef CONFIG_ATBM_USE_SW_ENC
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(5, 4, 0))
ieee80211_wep_free(local);
#endif
#endif
sta_info_stop(local);
atbm_destroy_workqueue(local->workqueue);
fail_workqueue:
wiphy_unregister(local->hw.wiphy);
fail_wiphy_register:
if (local->wiphy_ciphers_allocated)
atbm_kfree((void*)local->hw.wiphy->cipher_suites);
atbm_kfree(local->int_scan_req);
return result;
}
//EXPORT_SYMBOL(ieee80211_register_hw);
void ieee80211_unregister_hw(struct ieee80211_hw *hw)
{
struct ieee80211_local *local = hw_to_local(hw);
tasklet_kill(&local->tx_pending_tasklet);
tasklet_kill(&local->tasklet);
#ifdef CONFIG_ATBM_PM_QOS
pm_qos_remove_notifier(PM_QOS_NETWORK_LATENCY,
&local->network_latency_notifier);
#endif
#ifdef CONFIG_INET
unregister_inetaddr_notifier(&local->ifa_notifier);
#ifdef IPV6_FILTERING
unregister_inet6addr_notifier(&local->ifa6_notifier);
#endif /*IPV6_FILTERING*/
#endif
rtnl_lock();
/*
* At this point, interface list manipulations are fine
* because the driver cannot be handing us frames any
* more and the tasklet is killed.
*/
ieee80211_remove_interfaces(local);
ieee80211_special_freq_free(local);
rtnl_unlock();
atomic_set(&local->resume_timer_start,0);
atbm_del_timer_sync(&local->resume_timer);
/*
* Now all work items will be gone, but the
* timer might still be armed, so delete it
*/
atbm_del_timer_sync(&local->work_timer);
#ifdef CONFIG_ATBM_MAC80211_NO_USE
atbm_cancel_work_sync(&local->restart_work);
#endif
atbm_cancel_work_sync(&local->special_work);
#ifdef IPV6_FILTERING
#ifdef CONFIG_INET
atbm_cancel_work_sync(&local->ifa6_changed_work);
#endif
#endif /*IPV6_FILTERING*/
ieee80211_clear_tx_pending(local);
#ifndef CONFIG_RATE_HW_CONTROL
rate_control_deinitialize(local);
#endif
if (atbm_skb_queue_len(&local->skb_queue) ||
atbm_skb_queue_len(&local->skb_queue_unreliable))
atbm_printk_warn("skb_queue not empty\n");
atbm_skb_queue_purge(&local->skb_queue);
atbm_skb_queue_purge(&local->skb_queue_unreliable);
atbm_skb_queue_purge(&local->rx_skb_queue);
atbm_skb_queue_purge(&local->special_req_list);
#ifdef CONFIG_MAC80211_ATBM_ROAMING_CHANGES
/*
* Work can also be sheduled in call_rcu callback.
* Wait for all rcu callbacks to finish.
*/
rcu_barrier();
#endif
atbm_flush_workqueue(local->workqueue);
atbm_destroy_workqueue(local->workqueue);
wiphy_unregister(local->hw.wiphy);
sta_info_stop(local);
#ifdef CONFIG_ATBM_USE_SW_ENC
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(5, 4, 0))
ieee80211_wep_free(local);
#endif
#endif
ieee80211_led_exit(local);
ieee80211_scan_internal_deinit(local);
atbm_kfree(local->int_scan_req);
}
//EXPORT_SYMBOL(ieee80211_unregister_hw);
void ieee80211_free_hw(struct ieee80211_hw *hw)
{
struct ieee80211_local *local = hw_to_local(hw);
mutex_destroy(&local->iflist_mtx);
mutex_destroy(&local->mtx);
if (local->wiphy_ciphers_allocated)
atbm_kfree((void*)(local->hw.wiphy->cipher_suites));
ieee80211_name_free(local);
wiphy_free(local->hw.wiphy);
}
//EXPORT_SYMBOL(ieee80211_free_hw);
int atbm_ieee80211_init(void)
//static int __init atbm_ieee80211_init(void)
{
struct sk_buff *skb;
int ret;
BUILD_BUG_ON(sizeof(struct ieee80211_tx_info) > sizeof(skb->cb));
BUILD_BUG_ON(offsetof(struct ieee80211_tx_info, driver_data) +
IEEE80211_TX_INFO_DRIVER_DATA_SIZE > sizeof(skb->cb));
ETF_Test_is_Start();
#ifndef CONFIG_RATE_HW_CONTROL
ret = rc80211_minstrel_init();
if (ret)
return ret;
ret = rc80211_minstrel_ht_init();
if (ret)
goto err_minstrel;
ret = rc80211_pid_init();
if (ret)
goto err_pid;
#endif
ret = ieee80211_iface_init();
if (ret)
goto err_netdev;
return 0;
err_netdev:
#ifndef CONFIG_RATE_HW_CONTROL
atbm_rc80211_pid_exit();
err_pid:
atbm_rc80211_minstrel_ht_exit();
err_minstrel:
rc80211_minstrel_exit();
#endif
return ret;
}
void atbm_ieee80211_exit(void)
//static void __exit ieee80211_exit(void)
{
#ifndef CONFIG_RATE_HW_CONTROL
atbm_printk_exit("ieee80211_exit\n");
atbm_rc80211_pid_exit();
atbm_printk_exit("rc80211_pid_exit\n");
atbm_rc80211_minstrel_ht_exit();
atbm_printk_exit("atbm_rc80211_minstrel_ht_exit\n");
rc80211_minstrel_exit();
atbm_printk_exit("rc80211_minstrel_exit\n");
#endif
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37))
flush_scheduled_work();
#endif
if (mesh_allocated)
ieee80211s_stop();
ieee80211_iface_exit();
atbm_printk_exit("ieee80211_iface_exit\n");
rcu_barrier();
}
//subsys_initcall(atbm_ieee80211_init);
//module_exit(ieee80211_exit);
//MODULE_DESCRIPTION("IEEE 802.11 subsystem");
//MODULE_LICENSE("GPL");