blob: 2c42d35f95ea0bf5a9649c73f0f116e432fd27ed [file] [log] [blame]
/*
* File Name : 80211_if.c
*
* This file is the glue layer between net/mac80211 and UMAC
*
* Copyright (c) 2011, 2012, 2013, 2014 Imagination Technologies Ltd.
* All rights reserved
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
#include <linux/kernel.h>
#include <linux/moduleparam.h>
#include <linux/version.h>
#include <linux/device.h>
#include <net/mac80211.h>
#include <net/cfg80211.h>
#include <net/ieee80211_radiotap.h>
#include <linux/udp.h>
#include <linux/ip.h>
#include <linux/etherdevice.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/proc_fs.h>
#include "version.h"
#include "core.h"
#include "utils.h"
#include <linux/firmware.h>
#include <fwldr.h>
/* Its value will be the default mac address and it can only be updated with the
* command line arguments
*/
unsigned int vht_support = 1;
module_param(vht_support, int, 0);
MODULE_PARM_DESC(vht_support, "Configure the 11ac support for this device");
static unsigned int ftm;
module_param(ftm, int, 0);
MODULE_PARM_DESC(ftm, "Factory Test Mode, should be used only for calibrations.");
unsigned int system_rev = 0x494D47; /*ASCII: IMG*/
static void uccp420_roc_complete_work(struct work_struct *work);
static void uccp420wlan_exit(void);
static int load_fw(struct ieee80211_hw *hw);
int uccp_reinit;
#define CHAN2G(_freq, _idx) { \
.band = IEEE80211_BAND_2GHZ, \
.center_freq = (_freq), \
.hw_value = (_idx), \
.max_power = 20, \
}
#define CHAN5G(_freq, _idx, _flags) { \
.band = IEEE80211_BAND_5GHZ, \
.center_freq = (_freq), \
.hw_value = (_idx), \
.max_power = 20, \
.flags = (_flags), \
}
struct wifi_dev {
struct proc_dir_entry *umac_proc_dir_entry;
struct wifi_params params;
struct wifi_stats stats;
struct ieee80211_hw *hw;
};
static struct wifi_dev *wifi;
static struct ieee80211_channel dsss_chantable[] = {
CHAN2G(2412, 0), /* Channel 1 */
CHAN2G(2417, 1), /* Channel 2 */
CHAN2G(2422, 2), /* Channel 3 */
CHAN2G(2427, 3), /* Channel 4 */
CHAN2G(2432, 4), /* Channel 5 */
CHAN2G(2437, 5), /* Channel 6 */
CHAN2G(2442, 6), /* Channel 7 */
CHAN2G(2447, 7), /* Channel 8 */
CHAN2G(2452, 8), /* Channel 9 */
CHAN2G(2457, 9), /* Channel 10 */
CHAN2G(2462, 10), /* Channel 11 */
CHAN2G(2467, 11), /* Channel 12 */
CHAN2G(2472, 12), /* Channel 13 */
CHAN2G(2484, 13), /* Channel 14 */
};
static struct ieee80211_channel ofdm_chantable[] = {
CHAN5G(5180, 14, 0), /* Channel 36 */
CHAN5G(5200, 15, 0), /* Channel 40 */
CHAN5G(5220, 16, 0), /* Channel 44 */
CHAN5G(5240, 17, 0), /* Channel 48 */
CHAN5G(5260, 18, IEEE80211_CHAN_RADAR), /* Channel 52 */
CHAN5G(5280, 19, IEEE80211_CHAN_RADAR), /* Channel 56 */
CHAN5G(5300, 20, IEEE80211_CHAN_RADAR), /* Channel 60 */
CHAN5G(5320, 21, IEEE80211_CHAN_RADAR), /* Channel 64 */
CHAN5G(5500, 22, IEEE80211_CHAN_RADAR), /* Channel 100 */
CHAN5G(5520, 23, IEEE80211_CHAN_RADAR), /* Channel 104 */
CHAN5G(5540, 24, IEEE80211_CHAN_RADAR), /* Channel 108 */
CHAN5G(5560, 25, IEEE80211_CHAN_RADAR), /* Channel 112 */
CHAN5G(5580, 26, IEEE80211_CHAN_RADAR), /* Channel 116 */
CHAN5G(5600, 27, IEEE80211_CHAN_RADAR), /* Channel 120 */
CHAN5G(5620, 28, IEEE80211_CHAN_RADAR), /* Channel 124 */
CHAN5G(5640, 29, IEEE80211_CHAN_RADAR), /* Channel 128 */
CHAN5G(5660, 30, IEEE80211_CHAN_RADAR), /* Channel 132 */
CHAN5G(5680, 31, IEEE80211_CHAN_RADAR), /* Channel 136 */
CHAN5G(5700, 32, IEEE80211_CHAN_RADAR), /* Channel 140 */
CHAN5G(5720, 33, IEEE80211_CHAN_RADAR), /* Channel 144 */
CHAN5G(5745, 34, 0), /* Channel 149 */
CHAN5G(5765, 35, 0), /* Channel 153 */
CHAN5G(5785, 36, 0), /* Channel 157 */
CHAN5G(5805, 37, 0), /* Channel 161 */
CHAN5G(5825, 38, 0), /* Channel 165 */
};
static struct ieee80211_rate dsss_rates[] = {
{ .bitrate = 10, .hw_value = 2},
{ .bitrate = 20, .hw_value = 4,
.flags = IEEE80211_RATE_SHORT_PREAMBLE},
{ .bitrate = 55, .hw_value = 11,
.flags = IEEE80211_RATE_SHORT_PREAMBLE},
{ .bitrate = 110, .hw_value = 22,
.flags = IEEE80211_RATE_SHORT_PREAMBLE},
{ .bitrate = 60, .hw_value = 12},
{ .bitrate = 90, .hw_value = 18},
{ .bitrate = 120, .hw_value = 24},
{ .bitrate = 180, .hw_value = 36},
{ .bitrate = 240, .hw_value = 48},
{ .bitrate = 360, .hw_value = 72},
{ .bitrate = 480, .hw_value = 96},
{ .bitrate = 540, .hw_value = 108}
};
static struct ieee80211_rate ofdm_rates[] = {
{ .bitrate = 60, .hw_value = 12},
{ .bitrate = 90, .hw_value = 18},
{ .bitrate = 120, .hw_value = 24},
{ .bitrate = 180, .hw_value = 36},
{ .bitrate = 240, .hw_value = 48},
{ .bitrate = 360, .hw_value = 72},
{ .bitrate = 480, .hw_value = 96},
{ .bitrate = 540, .hw_value = 108}
};
static struct ieee80211_supported_band band_2ghz = {
.channels = dsss_chantable,
.n_channels = ARRAY_SIZE(dsss_chantable),
.band = IEEE80211_BAND_2GHZ,
.bitrates = dsss_rates,
.n_bitrates = ARRAY_SIZE(dsss_rates),
};
static struct ieee80211_supported_band band_5ghz = {
.channels = ofdm_chantable,
.n_channels = ARRAY_SIZE(ofdm_chantable),
.band = IEEE80211_BAND_5GHZ,
.bitrates = ofdm_rates,
.n_bitrates = ARRAY_SIZE(ofdm_rates),
};
/* Interface combinations for Virtual interfaces */
static const struct ieee80211_iface_limit if_limit1[] = {
{ .max = 2, .types = BIT(NL80211_IFTYPE_STATION)}
};
static const struct ieee80211_iface_limit if_limit2[] = {
{ .max = 1, .types = BIT(NL80211_IFTYPE_STATION)},
{ .max = 1, .types = BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_ADHOC) |
BIT(NL80211_IFTYPE_P2P_GO)}
};
static const struct ieee80211_iface_limit if_limit3[] = {
{ .max = 2, .types = BIT(NL80211_IFTYPE_P2P_CLIENT)}
};
static const struct ieee80211_iface_limit if_limit4[] = {
{ .max = 1, .types = BIT(NL80211_IFTYPE_ADHOC)},
{ .max = 1, .types = BIT(NL80211_IFTYPE_P2P_CLIENT)}
};
#ifdef MULTI_CHAN_SUPPORT
static const struct ieee80211_iface_limit if_limit5[] = {
{ .max = 1, .types = BIT(NL80211_IFTYPE_STATION)},
{ .max = 1, .types = BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_P2P_GO) |
BIT(NL80211_IFTYPE_P2P_CLIENT)}
};
#endif
static const struct ieee80211_iface_combination if_comb[] = {
{ .limits = if_limit1,
.n_limits = ARRAY_SIZE(if_limit1),
.max_interfaces = 2,
.num_different_channels = 1},
{ .limits = if_limit2,
.n_limits = ARRAY_SIZE(if_limit2),
.max_interfaces = 2,
.num_different_channels = 1},
{ .limits = if_limit3,
.n_limits = ARRAY_SIZE(if_limit3),
.max_interfaces = 2,
.num_different_channels = 1},
#ifdef MULTI_CHAN_SUPPORT
{ .limits = if_limit5,
.n_limits = ARRAY_SIZE(if_limit5),
.max_interfaces = 2,
.num_different_channels = 2},
{ .limits = if_limit1,
.n_limits = ARRAY_SIZE(if_limit1),
.max_interfaces = 2,
.num_different_channels = 2},
#endif
{ .limits = if_limit4,
.n_limits = ARRAY_SIZE(if_limit4),
.max_interfaces = 2,
.num_different_channels = 1}
};
/* For getting the dev pointer */
static struct class *hwsim_class;
static const struct wiphy_wowlan_support uccp_wowlan_support = {
.flags = WIPHY_WOWLAN_ANY,
};
static int conv_str_to_byte(unsigned char *byte,
unsigned char *str,
int len)
{
int i, j = 0;
unsigned char ch, val = 0;
for (i = 0; i < (len * 2); i++) {
/*convert to lower*/
ch = ((str[i] >= 'A' && str[i] <= 'Z') ? str[i] + 32 : str[i]);
if ((ch < '0' || ch > '9') && (ch < 'a' || ch > 'f'))
return -1;
if (ch >= '0' && ch <= '9') /*check is digit*/
ch = ch - '0';
else
ch = ch - 'a' + 10;
val += ch;
if (!(i%2))
val <<= 4;
else {
byte[j] = val;
j++;
val = 0;
}
}
return 0;
}
static void uccp420_roc_complete_work(struct work_struct *work)
{
struct delayed_work *dwork = NULL;
struct mac80211_dev *dev = NULL;
unsigned long flags;
struct umac_chanctx *off_chanctx = NULL;
struct umac_vif *uvif = NULL, *tmp = NULL;
struct tx_config *tx = NULL;
u32 roc_queue = 0;
bool need_offchan;
int roc_off_chanctx_idx = -1;
int chan_id = 0;
dwork = container_of(work, struct delayed_work, work);
dev = container_of(dwork, struct mac80211_dev, roc_complete_work);
tx = &dev->tx;
mutex_lock(&dev->mutex);
need_offchan = dev->roc_params.need_offchan;
roc_queue = tx_queue_unmap(UMAC_ROC_AC);
roc_off_chanctx_idx = dev->roc_off_chanctx_idx;
/* Stop the ROC queue */
ieee80211_stop_queue(dev->hw, roc_queue);
/* Unlock RCU immediately as we are freeing off_chanctx in this funciton
* only and because flush_vif_queues sleep
*/
rcu_read_lock();
off_chanctx = rcu_dereference(dev->off_chanctx[roc_off_chanctx_idx]);
rcu_read_unlock();
list_for_each_entry_safe(uvif, tmp, &off_chanctx->vifs, list) {
if (uvif == NULL || uvif->off_chanctx == NULL)
continue;
/* Flush the TX queues */
uccp420_flush_vif_queues(dev,
uvif,
uvif->off_chanctx->index,
BIT(UMAC_ROC_AC),
UMAC_VIF_CHANCTX_TYPE_OFF);
spin_lock_irqsave(&tx->lock, flags);
spin_lock(&dev->chanctx_lock);
/* ROC DONE: Move the channel context */
if (uvif->chanctx)
dev->curr_chanctx_idx = uvif->chanctx->index;
else
dev->curr_chanctx_idx = -1;
spin_unlock(&dev->chanctx_lock);
spin_unlock_irqrestore(&tx->lock, flags);
if (need_offchan) {
/* DEL from OFF chan list */
list_del_init(&uvif->list);
if (uvif->chanctx) {
/* Add it back to OP chan list */
list_add_tail(&uvif->list,
&uvif->chanctx->vifs);
/* !need_offchan: In this case, the frames are
* transmitted, so trigger is not needed.
*
* need_offchan: In this case, frames are
* buffered so we need trigger in case no frames
* come from mac80211.
*/
/* Process OPER pending frames only.
* TXQ is flushed before start of ROC
*/
chan_id = uvif->chanctx->index;
uccp420wlan_tx_proc_send_pend_frms_all(dev,
chan_id);
}
off_chanctx->nvifs--;
}
uvif->off_chanctx = NULL;
}
if (need_offchan)
kfree(off_chanctx);
rcu_assign_pointer(dev->off_chanctx[roc_off_chanctx_idx], NULL);
dev->roc_off_chanctx_idx = -1;
dev->roc_params.roc_in_progress = 0;
if (dev->cancel_roc == 0) {
ieee80211_remain_on_channel_expired(dev->hw);
DEBUG_LOG("%s-80211IF: ROC STOPPED..\n", dev->name);
} else {
dev->cancel_hw_roc_done = 1;
dev->cancel_roc = 0;
DEBUG_LOG("%s-80211IF: ROC CANCELLED..\n", dev->name);
}
/* Start the ROC queue */
ieee80211_wake_queue(dev->hw, roc_queue);
mutex_unlock(&dev->mutex);
}
static void tx(struct ieee80211_hw *hw,
struct ieee80211_tx_control *txctl,
struct sk_buff *skb)
{
struct mac80211_dev *dev = hw->priv;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
struct umac_vif *uvif;
unsigned char null_bssid[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
struct iphdr *iphdr;
unsigned char *pktgen_magic;
unsigned int orig_pktgen_magic = 0x55e99bbe; /*Endianness 0xbe9be955*/
struct umac_event_noa noa_event;
#ifdef MULTI_CHAN_SUPPORT
int curr_chanctx_idx = -1;
#endif
if (tx_info->control.vif == NULL) {
pr_debug("%s: Dropping injected TX frame\n",
dev->name);
dev_kfree_skb_any(skb);
return;
}
uvif = (struct umac_vif *)(tx_info->control.vif->drv_priv);
if (wifi->params.production_test) {
if (((hdr->frame_control &
IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA) ||
(tx_info->control.vif == NULL))
goto tx_status;
iphdr = (struct iphdr *) skb_network_header(skb);
if (iphdr->protocol == IPPROTO_UDP) {
pktgen_magic = skb_transport_header(skb);
pktgen_magic += sizeof(struct udphdr);
/*If not PKTGEN, then drop it*/
if (memcmp(pktgen_magic, &orig_pktgen_magic, 4) != 0) {
pr_debug("%s:%d prod_mode: The pkt is NOT PKTGEN so dropping it\n",
__func__, __LINE__);
goto tx_status;
}
} else {
pr_debug("%s:%d prod_mode: The pkt is NOT PKTGEN so dropping it.\n",
__func__, __LINE__);
goto tx_status;
}
}
if (!memcmp(hdr->addr3, null_bssid, ETH_ALEN))
goto tx_status;
if ((dev->power_save == PWRSAVE_STATE_DOZE) &&
(((hdr->frame_control &
IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) ||
is_bufferable_mgmt_frame(hdr)))
hdr->frame_control |= IEEE80211_FCTL_PM;
if (uvif->noa_active) {
memset(&noa_event, 0, sizeof(noa_event));
noa_event.if_index = uvif->vif_index;
uccp420wlan_noa_event(FROM_TX, &noa_event, dev, skb);
return;
}
#ifdef MULTI_CHAN_SUPPORT
spin_lock_bh(&dev->chanctx_lock);
curr_chanctx_idx = dev->curr_chanctx_idx;
spin_unlock_bh(&dev->chanctx_lock);
#endif
uccp420wlan_tx_frame(skb,
txctl->sta,
dev,
#ifdef MULTI_CHAN_SUPPORT
curr_chanctx_idx,
#endif
false);
return;
tx_status:
tx_info->flags |= IEEE80211_TX_STAT_ACK;
tx_info->status.rates[0].count = 1;
ieee80211_tx_status(hw, skb);
}
static int start(struct ieee80211_hw *hw)
{
struct mac80211_dev *dev = (struct mac80211_dev *)hw->priv;
if ((wifi->params.fw_loading == 1) && load_fw(hw)) {
DEBUG_LOG("%s-80211IF: FW load failed\n", dev->name);
return -ENODEV;
}
DEBUG_LOG("%s-80211IF: In start\n", dev->name);
mutex_lock(&dev->mutex);
if ((uccp420wlan_core_init(dev, ftm)) < 0) {
DEBUG_LOG("%s-80211IF: umac init failed\n", dev->name);
mutex_unlock(&dev->mutex);
return -ENODEV;
}
INIT_DELAYED_WORK(&dev->roc_complete_work, uccp420_roc_complete_work);
dev->state = STARTED;
memset(dev->params->pdout_voltage, 0,
sizeof(char) * MAX_AUX_ADC_SAMPLES);
dev->roc_off_chanctx_idx = -1;
mutex_unlock(&dev->mutex);
return 0;
}
static void stop(struct ieee80211_hw *hw)
{
struct mac80211_dev *dev = (struct mac80211_dev *)hw->priv;
DEBUG_LOG("%s-80211IF:In stop\n", dev->name);
mutex_lock(&dev->mutex);
uccp420wlan_core_deinit(dev, ftm);
dev->state = STOPPED;
mutex_unlock(&dev->mutex);
hal_ops.reset_hal_params();
}
static int add_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
struct mac80211_dev *dev = hw->priv;
struct ieee80211_vif *v;
struct umac_vif *uvif;
int vif_index, iftype;
mutex_lock(&dev->mutex);
iftype = vif->type;
v = vif;
vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER;
vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD;
if (dev->current_vif_count == wifi->params.num_vifs) {
pr_err("%s: Exceeded Maximum supported VIF's cur:%d max: %d.\n",
__func__,
dev->current_vif_count,
wifi->params.num_vifs);
mutex_unlock(&dev->mutex);
return -ENOTSUPP;
}
if (!(iftype == NL80211_IFTYPE_STATION ||
iftype == NL80211_IFTYPE_ADHOC ||
iftype == NL80211_IFTYPE_AP)) {
pr_err("Invalid Interface type\n");
return -ENOTSUPP;
}
if (wifi->params.production_test) {
if (dev->active_vifs || iftype != NL80211_IFTYPE_ADHOC) {
mutex_unlock(&dev->mutex);
return -EBUSY;
}
}
for (vif_index = 0; vif_index < wifi->params.num_vifs; vif_index++)
if (memcmp(dev->if_mac_addresses[vif_index].addr,
vif->addr,
ETH_ALEN) == 0)
break;
if (vif_index == wifi->params.num_vifs) {
pr_err("Failed to lookup vif_index\n");
mutex_unlock(&dev->mutex);
return -EINVAL;
}
uvif = (struct umac_vif *)&v->drv_priv;
uvif->vif_index = vif_index;
uvif->vif = v;
uvif->dev = dev;
uvif->seq_no = 0;
uccp420wlan_vif_add(uvif);
dev->active_vifs |= (1 << vif_index);
dev->current_vif_count++;
if (iftype == NL80211_IFTYPE_ADHOC)
dev->tx_last_beacon = 0;
rcu_assign_pointer(dev->vifs[vif_index], v);
synchronize_rcu();
mutex_unlock(&dev->mutex);
return 0;
}
static void remove_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
struct mac80211_dev *dev = hw->priv;
struct ieee80211_vif *v;
int vif_index;
mutex_lock(&dev->mutex);
v = vif;
vif_index = ((struct umac_vif *)&v->drv_priv)->vif_index;
uccp420wlan_vif_remove((struct umac_vif *)&v->drv_priv);
dev->active_vifs &= ~(1 << vif_index);
rcu_assign_pointer(dev->vifs[vif_index], NULL);
synchronize_rcu();
dev->current_vif_count--;
mutex_unlock(&dev->mutex);
}
static int config(struct ieee80211_hw *hw,
unsigned int changed)
{
struct mac80211_dev *dev = hw->priv;
struct ieee80211_conf *conf = &hw->conf;
unsigned int pri_chnl_num;
unsigned int freq_band;
unsigned int ch_width;
unsigned int center_freq = 0;
unsigned int center_freq1 = 0;
unsigned int center_freq2 = 0;
int i;
DEBUG_LOG("%s-80211IF:In config\n", dev->name);
mutex_lock(&dev->mutex);
if (changed & IEEE80211_CONF_CHANGE_POWER) {
dev->txpower = conf->power_level;
uccp420wlan_prog_txpower(dev->txpower);
}
/* Check for change in channel */
if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
center_freq = conf->chandef.chan->center_freq;
center_freq1 = conf->chandef.center_freq1;
center_freq2 = conf->chandef.center_freq2;
freq_band = conf->chandef.chan->band;
ch_width = conf->chandef.width;
pri_chnl_num = ieee80211_frequency_to_channel(center_freq);
DEBUG_LOG("%s-80211IF:Primary Channel is %d\n",
dev->name,
pri_chnl_num);
dev->chan_prog_done = 0;
uccp420wlan_prog_channel(pri_chnl_num,
center_freq1, center_freq2,
ch_width,
#ifdef MULTI_CHAN_SUPPORT
0,
#endif
freq_band);
}
/* Check for change in Power save state */
for (i = 0; i < MAX_VIFS; i++) {
if (!(changed & IEEE80211_CONF_CHANGE_PS))
break;
if (!(dev->active_vifs & (1 << i)))
continue;
/* When ROC is in progress, do not mess with
* PS state
*/
if (dev->roc_params.roc_in_progress)
continue;
if (wifi->params.disable_power_save)
continue;
if (conf->flags & IEEE80211_CONF_PS)
dev->power_save = PWRSAVE_STATE_DOZE;
else
dev->power_save = PWRSAVE_STATE_AWAKE;
DEBUG_LOG("%s-80211IF:PS state of VIF %d changed to %d\n",
dev->name,
i,
dev->power_save);
uccp420wlan_prog_ps_state(i,
dev->if_mac_addresses[i].addr,
dev->power_save);
}
/* TODO: Make this global config as it effects all VIF's */
for (i = 0; i < MAX_VIFS; i++) {
if (!(changed & IEEE80211_CONF_CHANGE_SMPS))
break;
if (wifi->params.production_test == 1)
break;
if (!(dev->active_vifs & (1 << i)))
continue;
DEBUG_LOG("%s-80211IF:MIMO PS state of VIF %d -> %d\n",
dev->name,
i,
conf->smps_mode);
uccp420wlan_prog_vif_smps(i,
dev->if_mac_addresses[i].addr,
conf->smps_mode);
}
/* Check for change in Retry Limits */
if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) {
DEBUG_LOG("%s-80211IF:Retry Limits changed to %d and %d\n",
dev->name,
conf->short_frame_max_tx_count,
conf->long_frame_max_tx_count);
}
for (i = 0; i < MAX_VIFS; i++) {
if (!(changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS))
break;
if (!(dev->active_vifs & (1 << i)))
continue;
uccp420wlan_prog_short_retry(i,
dev->if_mac_addresses[i].addr,
conf->short_frame_max_tx_count);
uccp420wlan_prog_long_retry(i,
dev->if_mac_addresses[i].addr,
conf->long_frame_max_tx_count);
}
mutex_unlock(&dev->mutex);
return 0;
}
static u64 prepare_multicast(struct ieee80211_hw *hw,
struct netdev_hw_addr_list *mc_list)
{
struct mac80211_dev *dev = hw->priv;
int i;
struct netdev_hw_addr *ha;
int mc_count = 0;
if (dev->state != STARTED)
return 0;
mc_count = netdev_hw_addr_list_count(mc_list);
{
if (mc_count > MCST_ADDR_LIMIT) {
mc_count = 0;
pr_warn("%s-80211IF:Disabling MCAST filter (cnt=%d)\n",
dev->name, mc_count);
goto out;
}
}
DEBUG_LOG("%s-80211IF:M-cast filter cnt adding:%d removing: %d\n",
dev->name, mc_count, dev->mc_filter_count);
if (dev->mc_filter_count > 0) {
/* Remove all previous multicast addresses from the LMAC */
for (i = 0; i < dev->mc_filter_count; i++)
uccp420wlan_prog_mcast_addr_cfg(dev->mc_filters[i],
WLAN_MCAST_ADDR_REM);
}
i = 0;
netdev_hw_addr_list_for_each(ha, mc_list) {
/* Prog the multicast address into the LMAC */
uccp420wlan_prog_mcast_addr_cfg(ha->addr, WLAN_MCAST_ADDR_ADD);
memcpy(dev->mc_filters[i], ha->addr, 6);
i++;
}
dev->mc_filter_count = mc_count;
out:
return mc_count;
}
static void configure_filter(struct ieee80211_hw *hw,
unsigned int changed_flags,
unsigned int *new_flags,
u64 mc_count)
{
struct mac80211_dev *dev = hw->priv;
mutex_lock(&dev->mutex);
changed_flags &= SUPPORTED_FILTERS;
*new_flags &= SUPPORTED_FILTERS;
if (dev->state != STARTED) {
mutex_unlock(&dev->mutex);
return;
}
if ((*new_flags & FIF_ALLMULTI) || (mc_count == 0)) {
/* Disable the multicast filter in LMAC */
DEBUG_LOG("%s-80211IF: Multicast filters disabled\n",
dev->name);
uccp420wlan_prog_mcast_filter_control(MCAST_FILTER_DISABLE);
} else if (mc_count) {
/* Enable the multicast filter in LMAC */
DEBUG_LOG("%s-80211IF: Multicast filters enabled\n",
dev->name);
uccp420wlan_prog_mcast_filter_control(MCAST_FILTER_ENABLE);
}
if (changed_flags == 0)
/* No filters which we support changed */
goto out;
if (wifi->params.production_test == 0) {
if (*new_flags & FIF_BCN_PRBRESP_PROMISC) {
/* Receive all beacons and probe responses */
DEBUG_LOG("%s-80211IF: RCV ALL bcns\n",
dev->name);
uccp420wlan_prog_rcv_bcn_mode(RCV_ALL_BCNS);
} else {
/* Receive only network beacons and probe responses */
DEBUG_LOG("%s-80211IF: RCV NW bcns\n",
dev->name);
uccp420wlan_prog_rcv_bcn_mode(RCV_ALL_NETWORK_ONLY);
}
}
out:
if (wifi->params.production_test == 1) {
DEBUG_LOG("%s-80211IF: RCV ALL bcns\n", dev->name);
uccp420wlan_prog_rcv_bcn_mode(RCV_ALL_BCNS);
}
mutex_unlock(&dev->mutex);
}
static int conf_vif_tx(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
unsigned short queue,
const struct ieee80211_tx_queue_params *txq_params)
{
struct mac80211_dev *dev = hw->priv;
int vif_index, vif_active;
struct edca_params params;
for (vif_index = 0; vif_index < wifi->params.num_vifs; vif_index++)
if (memcmp(dev->if_mac_addresses[vif_index].addr,
vif->addr,
ETH_ALEN) == 0)
break;
if (vif_index == wifi->params.num_vifs) {
pr_err("Failed to lookup vif_index\n");
return -EINVAL;
}
vif_active = 0;
if ((dev->active_vifs & (1 << vif_index)))
vif_active = 1;
memset(&params, 0, sizeof(params));
params.aifs = txq_params->aifs;
params.txop = txq_params->txop;
params.cwmin = txq_params->cw_min;
params.cwmax = txq_params->cw_max;
params.uapsd = txq_params->uapsd;
mutex_lock(&dev->mutex);
uccp420wlan_vif_set_edca_params(queue,
(struct umac_vif *)&vif->drv_priv,
&params,
vif_active);
mutex_unlock(&dev->mutex);
return 0;
}
static int set_key(struct ieee80211_hw *hw,
enum set_key_cmd cmd,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *key_conf)
{
struct umac_key sec_key;
unsigned int result = 0;
struct mac80211_dev *dev = hw->priv;
unsigned int cipher_type, key_type;
int vif_index;
struct umac_vif *uvif;
uvif = ((struct umac_vif *)&vif->drv_priv);
memset(&sec_key, 0, sizeof(struct umac_key));
switch (key_conf->cipher) {
case WLAN_CIPHER_SUITE_WEP40:
sec_key.key = key_conf->key;
cipher_type = CIPHER_TYPE_WEP40;
break;
case WLAN_CIPHER_SUITE_WEP104:
sec_key.key = key_conf->key;
cipher_type = CIPHER_TYPE_WEP104;
break;
case WLAN_CIPHER_SUITE_TKIP:
key_conf->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
/* We get the key in the following form:
* KEY (16 bytes) - TX MIC (8 bytes) - RX MIC (8 bytes)
*/
sec_key.key = key_conf->key;
sec_key.tx_mic = key_conf->key + 16;
sec_key.rx_mic = key_conf->key + 24;
cipher_type = CIPHER_TYPE_TKIP;
break;
case WLAN_CIPHER_SUITE_CCMP:
sec_key.key = key_conf->key;
cipher_type = CIPHER_TYPE_CCMP;
break;
default:
result = -EOPNOTSUPP;
mutex_unlock(&dev->mutex);
goto out;
}
vif_index = ((struct umac_vif *)&vif->drv_priv)->vif_index;
mutex_lock(&dev->mutex);
if (cmd == SET_KEY) {
key_conf->hw_key_idx = 0; /* Don't really use this */
/* This flag indicate that it requires IV generation */
key_conf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
if (cipher_type == CIPHER_TYPE_WEP40 ||
cipher_type == CIPHER_TYPE_WEP104) {
DEBUG_LOG("%s-80211IF:ADD IF KEY.vif_index = %d\n",
dev->name,
vif_index);
DEBUG_LOG(" keyidx = %d\n",
key_conf->keyidx);
DEBUG_LOG(" cipher_type = %d\n",
cipher_type);
uccp420wlan_prog_if_key(vif_index,
vif->addr,
KEY_CTRL_ADD,
key_conf->keyidx,
cipher_type,
&sec_key);
} else if (sta) {
sec_key.peer_mac = sta->addr;
if (key_conf->flags & IEEE80211_KEY_FLAG_PAIRWISE)
key_type = KEY_TYPE_UCAST;
else
key_type = KEY_TYPE_BCAST;
DEBUG_LOG("%s-80211IF:ADD PEER KEY.vif_index = %d",
dev->name, vif_index);
DEBUG_LOG(" keyidx = %d, keytype = %d\n",
key_conf->keyidx, key_type);
DEBUG_LOG(" cipher_type = %d\n",
cipher_type);
uccp420wlan_prog_peer_key(vif_index,
vif->addr,
KEY_CTRL_ADD,
key_conf->keyidx,
key_type,
cipher_type,
&sec_key);
} else {
key_type = KEY_TYPE_BCAST;
if (vif->type == NL80211_IFTYPE_STATION) {
sec_key.peer_mac =
(unsigned char *)vif->bss_conf.bssid;
memcpy(uvif->bssid,
(vif->bss_conf.bssid),
ETH_ALEN);
DEBUG_LOG("%s-80211IF: ADD PEER KEY\n",
dev->name);
DEBUG_LOG(" vif_index = %d\n",
vif_index);
DEBUG_LOG(" keyidx = %d\n",
key_conf->keyidx);
DEBUG_LOG(" keytype = %d\n",
key_type);
DEBUG_LOG(" cipher = %d\n",
cipher_type);
uccp420wlan_prog_peer_key(vif_index,
vif->addr,
KEY_CTRL_ADD,
key_conf->keyidx,
key_type, cipher_type,
&sec_key);
} else if (vif->type == NL80211_IFTYPE_AP) {
DEBUG_LOG("%s-80211IF: ADD IF KEY.\n",
dev->name);
DEBUG_LOG(" vif_index = %d\n",
vif_index);
DEBUG_LOG(" keyidx = %d\n",
key_conf->keyidx);
DEBUG_LOG(" cipher_type = %d\n",
cipher_type);
uccp420wlan_prog_if_key(vif_index,
vif->addr,
KEY_CTRL_ADD,
key_conf->keyidx,
cipher_type,
&sec_key);
} else {
/* ADHOC */
DEBUG_LOG("%s-80211IF: ADD IF KEY.\n",
dev->name);
DEBUG_LOG(" vif_index = %d\n",
vif_index);
DEBUG_LOG(" keyidx = %d\n",
key_conf->keyidx);
DEBUG_LOG(" cipher_type = %d\n",
cipher_type);
uccp420wlan_prog_if_key(vif_index,
vif->addr,
KEY_CTRL_ADD,
key_conf->keyidx,
cipher_type,
&sec_key);
}
}
} else if (cmd == DISABLE_KEY) {
if ((cipher_type == CIPHER_TYPE_WEP40) ||
(cipher_type == CIPHER_TYPE_WEP104)) {
uccp420wlan_prog_if_key(vif_index,
vif->addr,
KEY_CTRL_DEL,
key_conf->keyidx,
cipher_type,
&sec_key);
} else if (sta) {
sec_key.peer_mac = sta->addr;
if (key_conf->flags & IEEE80211_KEY_FLAG_PAIRWISE)
key_type = KEY_TYPE_UCAST;
else
key_type = KEY_TYPE_BCAST;
uccp420wlan_prog_peer_key(vif_index,
vif->addr,
KEY_CTRL_DEL,
key_conf->keyidx,
key_type,
cipher_type,
&sec_key);
} else {
if (vif->type == NL80211_IFTYPE_STATION) {
sec_key.peer_mac = uvif->bssid;
uccp420wlan_prog_peer_key(vif_index,
vif->addr,
KEY_CTRL_DEL,
key_conf->keyidx,
KEY_TYPE_BCAST,
cipher_type,
&sec_key);
} else if (vif->type == NL80211_IFTYPE_AP)
uccp420wlan_prog_if_key(vif_index,
vif->addr,
KEY_CTRL_DEL,
key_conf->keyidx,
cipher_type,
&sec_key);
else
uccp420wlan_prog_if_key(vif_index,
vif->addr,
KEY_CTRL_DEL,
key_conf->keyidx,
cipher_type,
&sec_key);
}
}
mutex_unlock(&dev->mutex);
out:
return result;
}
static void bss_info_changed(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *bss_conf,
unsigned int changed)
{
struct mac80211_dev *dev = hw->priv;
mutex_lock(&dev->mutex);
if (wifi->params.production_test || wifi->params.disable_beacon_ibss) {
/* Disable beacon generation when running pktgen
* for performance
*/
changed &= ~BSS_CHANGED_BEACON_INT;
changed &= ~BSS_CHANGED_BEACON_ENABLED;
}
uccp420wlan_vif_bss_info_changed((struct umac_vif *)&vif->drv_priv,
bss_conf,
changed);
mutex_unlock(&dev->mutex);
}
static void setup_ht_cap(struct ieee80211_sta_ht_cap *ht_info)
{
int i;
memset(ht_info, 0, sizeof(*ht_info));
ht_info->ht_supported = true;
pr_info("SETUP HT CALLED\n");
#if 0
ht_info->cap |= IEEE80211_HT_CAP_DSSSCCK40;
#endif
ht_info->cap = 0;
ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU;
ht_info->cap |= IEEE80211_HT_CAP_SGI_40;
ht_info->cap |= IEEE80211_HT_CAP_SGI_20;
ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD;
ht_info->cap |= IEEE80211_HT_CAP_LDPC_CODING;
ht_info->cap |= IEEE80211_HT_CAP_TX_STBC;
ht_info->cap |= (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT);
ht_info->cap |= IEEE80211_HT_CAP_LSIG_TXOP_PROT;
/*We support SMPS*/
ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_4;
memset(&ht_info->mcs, 0, sizeof(ht_info->mcs));
if (wifi->params.max_tx_streams != wifi->params.max_rx_streams) {
ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF;
ht_info->mcs.tx_params |= ((wifi->params.max_tx_streams - 1)
<< IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT);
}
for (i = 0; i < wifi->params.max_rx_streams; i++)
ht_info->mcs.rx_mask[i] = 0xff;
ht_info->mcs.rx_mask[4] = 0x1;
ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED;
}
#define IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT 13
#define IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT 16
static void setup_vht_cap(struct ieee80211_sta_vht_cap *vht_info)
{
if (!vht_support)
return;
memset(vht_info, 0, sizeof(*vht_info));
vht_info->vht_supported = true;
pr_info("SETUP VHT CALLED\n");
vht_info->cap = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 |
/*64KB Rx buffer size*/
(3 <<
IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT) |
#if 0
IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
(1 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT) |
(1 << IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT) |
#endif
IEEE80211_VHT_CAP_SHORT_GI_80 |
IEEE80211_VHT_CAP_RXLDPC |
IEEE80211_VHT_CAP_TXSTBC |
IEEE80211_VHT_CAP_RXSTBC_1 |
IEEE80211_VHT_CAP_HTC_VHT;
/* 1x1 */
if ((wifi->params.max_tx_streams == 1) &&
(wifi->params.max_rx_streams == 1)) {
vht_info->vht_mcs.rx_mcs_map =
((IEEE80211_VHT_MCS_SUPPORT_0_7) << (2*0)) |
((IEEE80211_VHT_MCS_NOT_SUPPORTED) << (2*1)) |
((IEEE80211_VHT_MCS_NOT_SUPPORTED) << (2*2)) |
((IEEE80211_VHT_MCS_NOT_SUPPORTED) << (2*3)) |
((IEEE80211_VHT_MCS_NOT_SUPPORTED) << (2*4)) |
((IEEE80211_VHT_MCS_NOT_SUPPORTED) << (2*5)) |
((IEEE80211_VHT_MCS_NOT_SUPPORTED) << (2*6)) |
((IEEE80211_VHT_MCS_NOT_SUPPORTED) << (2*7));
}
/*2x2 */
if ((wifi->params.max_tx_streams == 2) &&
(wifi->params.max_rx_streams == 2)) {
vht_info->vht_mcs.rx_mcs_map =
((IEEE80211_VHT_MCS_SUPPORT_0_7) << (2*0)) |
((IEEE80211_VHT_MCS_SUPPORT_0_7) << (2*1)) |
((IEEE80211_VHT_MCS_NOT_SUPPORTED) << (2*2)) |
((IEEE80211_VHT_MCS_NOT_SUPPORTED) << (2*3)) |
((IEEE80211_VHT_MCS_NOT_SUPPORTED) << (2*4)) |
((IEEE80211_VHT_MCS_NOT_SUPPORTED) << (2*5)) |
((IEEE80211_VHT_MCS_NOT_SUPPORTED) << (2*6)) |
((IEEE80211_VHT_MCS_NOT_SUPPORTED) << (2*7));
}
vht_info->vht_mcs.tx_mcs_map = vht_info->vht_mcs.rx_mcs_map;
}
static void init_hw(struct ieee80211_hw *hw)
{
struct mac80211_dev *dev = (struct mac80211_dev *)hw->priv;
int num_if_comb = 0;
/* Supported Interface Types and other Default values*/
hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_ADHOC) |
BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO);
hw->wiphy->iface_combinations = if_comb;
num_if_comb = (sizeof(if_comb) /
sizeof(struct ieee80211_iface_combination));
hw->wiphy->n_iface_combinations = num_if_comb;
hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS;
hw->flags |= IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING;
hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION;
hw->flags |= IEEE80211_HW_MFP_CAPABLE;
hw->flags |= IEEE80211_HW_REPORTS_TX_ACK_STATUS;
if (wifi->params.dot11a_support)
hw->flags |= IEEE80211_HW_SPECTRUM_MGMT;
hw->flags |= IEEE80211_HW_SUPPORTS_PER_STA_GTK;
hw->flags |= IEEE80211_HW_CONNECTION_MONITOR;
hw->wiphy->max_scan_ssids = MAX_NUM_SSIDS; /* 4 */
/* Low priority bg scan */
hw->wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN;
hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
hw->max_listen_interval = 10;
hw->wiphy->max_remain_on_channel_duration = 5000; /*ROC*/
hw->offchannel_tx_hw_queue = WLAN_AC_VO;
hw->max_rates = 4;
hw->max_rate_tries = 5;
hw->queues = 4;
/* Size */
hw->extra_tx_headroom = 0;
hw->vif_data_size = sizeof(struct umac_vif);
hw->sta_data_size = sizeof(struct umac_sta);
#ifdef MULTI_CHAN_SUPPORT
hw->chanctx_data_size = sizeof(struct umac_chanctx);
#endif
if (wifi->params.dot11g_support) {
hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &band_2ghz;
setup_ht_cap(&hw->wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap);
}
if (wifi->params.dot11a_support) {
if (vht_support)
setup_vht_cap(&band_5ghz.vht_cap);
hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &band_5ghz;
setup_ht_cap(&hw->wiphy->bands[IEEE80211_BAND_5GHZ]->ht_cap);
}
memset(hw->wiphy->addr_mask, 0, sizeof(hw->wiphy->addr_mask));
if (wifi->params.num_vifs == 1) {
hw->wiphy->addresses = NULL;
SET_IEEE80211_PERM_ADDR(hw, dev->if_mac_addresses[0].addr);
} else {
hw->wiphy->n_addresses = wifi->params.num_vifs;
hw->wiphy->addresses = dev->if_mac_addresses;
}
hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
if (!wifi->params.disable_power_save &&
!wifi->params.disable_sm_power_save) {
/* SMPS Support both Static and Dynamic */
hw->wiphy->features |= NL80211_FEATURE_STATIC_SMPS;
hw->wiphy->features |= NL80211_FEATURE_DYNAMIC_SMPS;
}
#ifdef CONFIG_PM
hw->wiphy->wowlan = &uccp_wowlan_support;
#endif
}
static int ampdu_action(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
enum ieee80211_ampdu_mlme_action action,
struct ieee80211_sta *sta,
u16 tid, u16 *ssn, u8 buf_size)
{
int ret = 0;
unsigned int val = 0;
struct mac80211_dev *dev = (struct mac80211_dev *)hw->priv;
DEBUG_LOG("%s-80211IF: ampdu action started\n",
((struct mac80211_dev *)(hw->priv))->name);
switch (action) {
case IEEE80211_AMPDU_RX_START:
{
val = tid | TID_INITIATOR_AP;
dev->tid_info[val].tid_state = TID_STATE_AGGR_START;
dev->tid_info[val].ssn = *ssn;
uccp420wlan_prog_ba_session_data(1,
tid,
&dev->tid_info[val].ssn,
1,
vif->addr,
(unsigned char *)(vif->bss_conf.bssid));
}
break;
case IEEE80211_AMPDU_RX_STOP:
{
val = tid | TID_INITIATOR_AP;
dev->tid_info[val].tid_state = TID_STATE_AGGR_STOP;
uccp420wlan_prog_ba_session_data(0,
tid,
&dev->tid_info[val].ssn,
1,
vif->addr,
(unsigned char *)(vif->bss_conf.bssid));
}
break;
case IEEE80211_AMPDU_TX_START:
{
val = tid | TID_INITIATOR_STA;
ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
dev->tid_info[val].tid_state = TID_STATE_AGGR_START;
dev->tid_info[val].ssn = *ssn;
}
break;
case IEEE80211_AMPDU_TX_STOP_FLUSH:
case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
case IEEE80211_AMPDU_TX_STOP_CONT:
{
val = tid | TID_INITIATOR_STA;
dev->tid_info[val].tid_state = TID_STATE_AGGR_STOP;
ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
}
break;
case IEEE80211_AMPDU_TX_OPERATIONAL:
{
val = tid | TID_INITIATOR_STA;
dev->tid_info[val].tid_state = TID_STATE_AGGR_OPERATIONAL;
}
break;
default:
pr_err("%s: Invalid command, ignoring\n",
__func__);
}
return ret;
}
static int set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
{
struct mac80211_dev *dev = (struct mac80211_dev *)hw->priv;
/* Maximum no of antenna supported =2 */
if (!tx_ant || (tx_ant & ~3) || !rx_ant || (rx_ant & ~3))
return -EINVAL;
dev->tx_antenna = (tx_ant & 3);
return 0;
}
static int remain_on_channel(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_channel *channel,
int duration,
enum ieee80211_roc_type type)
{
struct mac80211_dev *dev = (struct mac80211_dev *)hw->priv;
unsigned int pri_chnl_num =
ieee80211_frequency_to_channel(channel->center_freq);
struct umac_vif *uvif = (struct umac_vif *)vif->drv_priv;
struct umac_chanctx *off_chanctx = NULL;
int off_chanctx_id = 0, i = 0;
unsigned long flags;
struct tx_config *tx = &dev->tx;
u32 hw_queue_map = 0;
struct ieee80211_chanctx_conf *vif_chanctx;
bool need_offchan = true;
mutex_lock(&dev->mutex);
DEBUG_LOG("%s-80211IF: Params are Chan:%d Dur:%d Type: %d\n",
dev->name,
ieee80211_frequency_to_channel(channel->center_freq),
duration,
type);
if (dev->roc_params.roc_in_progress) {
DEBUG_LOG("%s-80211IF: Dropping roc...Busy\n", dev->name);
mutex_unlock(&dev->mutex);
return -EBUSY;
}
if (dev->num_active_chanctx == 2) {
DEBUG_LOG("%s-80211IF: ROC is not supported in TSMC Mode\n",
dev->name);
mutex_unlock(&dev->mutex);
return -ENOTSUPP;
}
/* Inform FW that ROC is started:
* For pure TX we send OFFCHANNEL_TX so that driver can terminate ROC
* For Tx + Rx we use NORMAL, FW will terminate ROC based on duration.
*/
if (duration != 10 && type == ROC_TYPE_OFFCHANNEL_TX)
type = ROC_TYPE_NORMAL;
/* uvif is in connected state
*/
if (uvif->chanctx) {
rcu_read_lock();
vif_chanctx =
rcu_dereference(dev->chanctx[uvif->chanctx->index]);
/* AS ROC frames are MGMT frames, checking only for Primary
* Channel.
*/
if (vif_chanctx->def.chan->center_freq == channel->center_freq)
need_offchan = false;
rcu_read_unlock();
}
DEBUG_LOG("%s-80211IF: need_offchan: %d\n", dev->name, need_offchan);
dev->roc_params.need_offchan = need_offchan;
if (need_offchan) {
/* Different chan context than the uvif */
off_chanctx = kmalloc(sizeof(struct umac_chanctx),
GFP_KERNEL);
if (!off_chanctx) {
pr_err("%s: Unable to alloc mem for channel context\n",
__func__);
mutex_unlock(&dev->mutex);
return -ENOMEM;
}
/** Currently OFFCHAN is limited to handling ROC case
* but it is meant for a generic case.
* ideally we should look for existing offchan context
* and re-use/create.
*/
for (i = 0; i < MAX_OFF_CHANCTX; i++) {
if (!dev->off_chanctx[i]) {
off_chanctx_id = i;
break;
}
}
if (uvif->chanctx) {
ieee80211_stop_queues(hw);
hw_queue_map = BIT(WLAN_AC_BK) |
BIT(WLAN_AC_BE) |
BIT(WLAN_AC_VI) |
BIT(WLAN_AC_VO) |
BIT(WLAN_AC_BCN);
uccp420_flush_vif_queues(dev,
uvif,
uvif->chanctx->index,
hw_queue_map,
UMAC_VIF_CHANCTX_TYPE_OPER);
}
off_chanctx->index = OFF_CHANCTX_IDX_BASE + off_chanctx_id;
dev->roc_off_chanctx_idx = off_chanctx_id;
INIT_LIST_HEAD(&off_chanctx->vifs);
off_chanctx->nvifs = 0;
if (uvif->chanctx) {
/* Delete the uvif from OP channel list */
list_del_init(&uvif->list);
}
/* Add the vif to the off_chanctx */
list_add_tail(&uvif->list, &off_chanctx->vifs);
off_chanctx->nvifs++;
rcu_assign_pointer(dev->off_chanctx[off_chanctx_id],
off_chanctx);
synchronize_rcu();
/* Move the channel context */
spin_lock_bh(&dev->chanctx_lock);
dev->curr_chanctx_idx = off_chanctx->index;
spin_unlock_bh(&dev->chanctx_lock);
} else {
/* Same channel context, just update off_chanctx
* to chanctx
*/
off_chanctx = uvif->chanctx;
for (i = 0; i < MAX_OFF_CHANCTX; i++) {
if (!dev->off_chanctx[i]) {
off_chanctx_id = i;
break;
}
}
dev->roc_off_chanctx_idx = off_chanctx->index;
rcu_assign_pointer(dev->off_chanctx[off_chanctx_id],
off_chanctx);
synchronize_rcu();
}
spin_lock_irqsave(&tx->lock, flags);
uvif->off_chanctx = off_chanctx;
spin_unlock_irqrestore(&tx->lock, flags);
uccp420wlan_prog_roc(ROC_START, pri_chnl_num, duration, type);
if (uvif->chanctx)
ieee80211_wake_queues(hw);
mutex_unlock(&dev->mutex);
return 0;
}
static int cancel_remain_on_channel(struct ieee80211_hw *hw)
{
struct mac80211_dev *dev = (struct mac80211_dev *)hw->priv;
int ret = 0;
mutex_lock(&dev->mutex);
if (dev->roc_params.roc_in_progress) {
dev->cancel_hw_roc_done = 0;
dev->cancel_roc = 1;
DEBUG_LOG("%s-80211IF: Cancelling HW ROC....\n", dev->name);
uccp420wlan_prog_roc(ROC_STOP, 0, 0, 0);
mutex_unlock(&dev->mutex);
if (!wait_for_cancel_hw_roc(dev)) {
DEBUG_LOG("%s-80211IF: Cancel HW ROC....done\n",
dev->name);
ret = 0;
} else {
DEBUG_LOG("%s-80211IF: Cancel HW ROC..timedout\n",
dev->name);
ret = -1;
}
} else {
mutex_unlock(&dev->mutex);
}
return ret;
}
/* Needed in case of IBSS to send out probe responses when we are beaconing */
static int tx_last_beacon(struct ieee80211_hw *hw)
{
struct mac80211_dev *dev = (struct mac80211_dev *)hw->priv;
return dev->tx_last_beacon;
}
#ifdef CONFIG_PM
static int wait_for_econ_ps_cfg(struct mac80211_dev *dev)
{
int count = 0;
char econ_ps_cfg_done = 0;
check_econ_ps_cfg_complete:
mutex_lock(&dev->mutex);
econ_ps_cfg_done = dev->econ_ps_cfg_stats.completed;
mutex_unlock(&dev->mutex);
if (!econ_ps_cfg_done && (count < PS_ECON_CFG_TIMEOUT_TICKS)) {
count++;
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(1);
goto check_econ_ps_cfg_complete;
}
if (!econ_ps_cfg_done) {
pr_warn("%s: Didn't get ECON_PS_CFG_DONE event\n",
__func__);
return -1;
}
pr_debug("%s : Received ECON_PS_CFG_DONE event\n",
__func__);
return 0;
}
static int img_resume(struct ieee80211_hw *hw)
{
int i = 0;
int active_vif_index = -1;
struct mac80211_dev *dev = NULL;
if (hw == NULL) {
pr_err("%s: Invalid parameters\n",
__func__);
return -1;
}
dev = (struct mac80211_dev *)hw->priv;
mutex_lock(&dev->mutex);
for (i = 0; i < MAX_VIFS; i++) {
if (dev->active_vifs & (1 << i))
active_vif_index = i;
}
dev->econ_ps_cfg_stats.completed = 0;
dev->econ_ps_cfg_stats.result = 0;
if (uccp420wlan_prog_econ_ps_state(active_vif_index,
PWRSAVE_STATE_AWAKE)) {
pr_err("%s : Error Occured\n",
__func__);
mutex_unlock(&dev->mutex);
return -1;
}
mutex_unlock(&dev->mutex);
if (!wait_for_econ_ps_cfg(dev)) {
if (!dev->econ_ps_cfg_stats.result) {
dev->power_save = PWRSAVE_STATE_AWAKE;
pr_debug("%s: Successful\n",
__func__);
hal_ops.disable_irq_wake();
return 0;
}
}
pr_err("%s: Error Occured\n",
__func__);
return -1;
}
static int img_suspend(struct ieee80211_hw *hw,
struct cfg80211_wowlan *wowlan)
{
int i = 0;
int active_vif_index = -1;
int count = 0;
struct mac80211_dev *dev = NULL;
if (hw == NULL) {
pr_err("%s: Invalid parameters\n",
__func__);
return -1;
}
if (WARN_ON((wifi->params.hw_scan_status == HW_SCAN_STATUS_PROGRESS)))
return -1;
dev = (struct mac80211_dev *)hw->priv;
mutex_lock(&dev->mutex);
for (i = 0; i < MAX_VIFS; i++) {
if (dev->active_vifs & (1 << i)) {
active_vif_index = i;
count++;
}
}
if (count != 1) {
pr_err("%s: Economy mode supp only for single VIF(STA mode)\n",
__func__);
mutex_unlock(&dev->mutex);
return -1;
}
if (dev->vifs[active_vif_index]->type != NL80211_IFTYPE_STATION) {
pr_err("%s: VIF is not in STA Mode\n",
__func__);
mutex_unlock(&dev->mutex);
return -1;
}
dev->econ_ps_cfg_stats.completed = 0;
dev->econ_ps_cfg_stats.result = 0;
dev->econ_ps_cfg_stats.wake_trig = -1;
if (uccp420wlan_prog_econ_ps_state(active_vif_index,
PWRSAVE_STATE_DOZE)) {
pr_err("%s : Error Occured\n",
__func__);
mutex_unlock(&dev->mutex);
return -1;
}
mutex_unlock(&dev->mutex);
if (!wait_for_econ_ps_cfg(dev)) {
if (!dev->econ_ps_cfg_stats.result) {
dev->power_save = PWRSAVE_STATE_DOZE;
pr_debug("%s: Successful\n",
__func__);
hal_ops.enable_irq_wake();
return 0;
}
}
pr_err("%s: Error Occured\n",
__func__);
return -1;
}
#endif
int scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_scan_request *ireq)
{
struct umac_vif *uvif = (struct umac_vif *)vif->drv_priv;
struct scan_req scan_req = {0};
int i = 0;
struct cfg80211_scan_request *req;
req = &ireq->req;
scan_req.n_ssids = req->n_ssids;
scan_req.n_channels = req->n_channels;
scan_req.ie_len = req->ie_len;
if (wifi->params.hw_scan_status != HW_SCAN_STATUS_NONE)
return -EBUSY; /* Already in HW SCAN State */
/* Keep track of HW Scan requests and compeltes */
wifi->params.hw_scan_status = HW_SCAN_STATUS_PROGRESS;
if (uvif->dev->params->production_test == 1) {
/* Drop scan, its just intended for IBSS
* and some data traffic
*/
if (wifi->params.hw_scan_status != HW_SCAN_STATUS_NONE) {
ieee80211_scan_completed(uvif->dev->hw, false);
wifi->params.hw_scan_status = HW_SCAN_STATUS_NONE;
}
return 0;
}
if (req->ie_len)
memcpy(scan_req.ie, req->ie, req->ie_len);
for (i = 0; i < req->n_channels; i++) {
scan_req.center_freq[i] = req->channels[i]->center_freq;
scan_req.freq_max_power[i] = req->channels[i]->max_power;
scan_req.chan_flags[i] = req->channels[i]->flags;
/* The type of scan comes from mac80211 so its taken care of */
}
scan_req.p2p_probe = req->no_cck;
/* For hostapd scan (40MHz) and scan_type=passive, n_ssids=0
* and req->ssids is NULL
*/
if (req->n_ssids > 0) {
for (i = 0; i < req->n_ssids; i++) {
scan_req.ssids[i].ssid_len = req->ssids[i].ssid_len;
if (req->ssids[i].ssid_len > 0)
memcpy(scan_req.ssids[i].ssid,
req->ssids[i].ssid,
req->ssids[i].ssid_len);
}
}
return uccp420wlan_scan(uvif->vif_index, &scan_req);
}
void uccp420wlan_scan_complete(void *context,
struct host_event_scanres *scan_res,
unsigned char *skb,
unsigned int len)
{
struct mac80211_dev *dev = (struct mac80211_dev *)context;
int i = 0;
struct ieee80211_vif *vif = NULL;
const char ra[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
/* DO NOT update the scan results through cfg80211 API's we just pass
* the beacons and probe responses up and mac80211 will inform cfg80211
*/
if (scan_res->more_results == 0) {
DEBUG_LOG("Event Scan Complete from UCCP:\n");
DEBUG_LOG(" More_results: 0, Scan is Completed\n");
/* There can be a race where we receive remove_interface and
* abort the scan(1)
* But we get scan_complete from the FW(2), this check will make
* sure we are not calling scan_complete when we have already
* aborted the scan. Eg: Killing wpa_supplicant in middle of
* scanning
*/
if (wifi->params.hw_scan_status != HW_SCAN_STATUS_NONE) {
dev->stats->umac_scan_complete++;
ieee80211_scan_completed(dev->hw, false);
/* WAR for TT_PRB0164. To be removed after patch
* submitted to kernel
*/
for (i = 0; i < MAX_VIFS; i++) {
if (!(dev->active_vifs & (1 << i)))
continue;
rcu_read_lock();
vif = rcu_dereference(dev->vifs[i]);
rcu_read_unlock();
if (vif->type != NL80211_IFTYPE_AP)
continue;
ieee80211_stop_tx_ba_cb_irqsafe(vif,
ra, IEEE80211_NUM_TIDS);
}
/* Keep track of HW Scan requests and compeltes */
wifi->params.hw_scan_status = HW_SCAN_STATUS_NONE;
}
} else {
DEBUG_LOG("Event Scan Complete from UCCP:\n");
DEBUG_LOG("More_results: %d, Still Scanning\n",
scan_res->more_results);
}
}
void cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
struct umac_vif *uvif = (struct umac_vif *)vif->drv_priv;
struct mac80211_dev *dev = NULL;
dev = (struct mac80211_dev *)hw->priv;
if (wifi->params.hw_scan_status == HW_SCAN_STATUS_PROGRESS) {
pr_info("Aborting pending scan request...\n");
dev->scan_abort_done = 0;
if (uccp420wlan_scan_abort(uvif->vif_index))
return;
if (!wait_for_scan_abort(dev)) {
ieee80211_scan_completed(hw, true);
wifi->params.hw_scan_status = HW_SCAN_STATUS_NONE;
dev->stats->umac_scan_complete++;
return;
}
}
}
int set_rts_threshold(struct ieee80211_hw *hw,
u32 value)
{
struct mac80211_dev *dev = NULL;
dev = (struct mac80211_dev *)hw->priv;
/*if thres>=2347 (default case) hostapd sends down (u32) -1*/
if (value > 65536)
dev->rts_threshold = 65536;
else
dev->rts_threshold = value;
return 0;
}
int sta_add(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta)
{
struct umac_vif *uvif = (struct umac_vif *)vif->drv_priv;
struct peer_sta_info peer_st_info = {0};
int i;
int result = 0;
struct mac80211_dev *dev = hw->priv;
struct umac_sta *usta = (struct umac_sta *)sta->drv_priv;
unsigned int peer_id = 0;
for (i = 0; i < MAX_PEERS; i++) {
if (!dev->peers[i]) {
peer_id = i;
break;
}
}
if (i == MAX_PEERS) {
pr_err("Exceeded Max STA limit(%d)\n", MAX_PEERS);
return -1;
}
for (i = 0; i < STA_NUM_BANDS; i++)
peer_st_info.supp_rates[i] = sta->supp_rates[i];
/* HT info */
peer_st_info.ht_cap = sta->ht_cap.cap;
peer_st_info.ht_supported = sta->ht_cap.ht_supported;
peer_st_info.vht_supported = sta->vht_cap.vht_supported;
peer_st_info.vht_cap = sta->vht_cap.cap;
peer_st_info.ampdu_factor = sta->ht_cap.ampdu_factor;
peer_st_info.ampdu_density = sta->ht_cap.ampdu_density;
peer_st_info.rx_highest = sta->ht_cap.mcs.rx_highest;
peer_st_info.tx_params = sta->ht_cap.mcs.tx_params;
peer_st_info.uapsd_queues = sta->uapsd_queues;
/* Will be used in enforcing rules during Aggregation */
uvif->peer_ampdu_factor = (1 << (13 + sta->ht_cap.ampdu_factor)) - 1;
if (sta->vht_cap.vht_supported) {
if (sta->vht_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)
uvif->dev->params->vht_beamform_support = 1;
}
for (i = 0; i < HT_MCS_MASK_LEN; i++)
peer_st_info.rx_mask[i] = sta->ht_cap.mcs.rx_mask[i];
for (i = 0; i < ETH_ALEN; i++)
peer_st_info.addr[i] = sta->addr[i];
result = uccp420wlan_sta_add(uvif->vif_index, &peer_st_info);
if (!result) {
rcu_assign_pointer(dev->peers[peer_id], sta);
synchronize_rcu();
usta->index = peer_id;
#ifdef MULTI_CHAN_SUPPORT
usta->chanctx = uvif->chanctx;
usta->vif_index = uvif->vif_index;
#endif
}
return result;
}
int sta_remove(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta)
{
struct umac_vif *uvif = (struct umac_vif *)vif->drv_priv;
struct peer_sta_info peer_st_info = {0};
int i;
int result = 0;
struct mac80211_dev *dev = hw->priv;
struct umac_sta *usta = (struct umac_sta *)sta->drv_priv;
for (i = 0; i < ETH_ALEN; i++)
peer_st_info.addr[i] = sta->addr[i];
result = uccp420wlan_sta_remove(uvif->vif_index, &peer_st_info);
if (!result) {
rcu_assign_pointer(dev->peers[usta->index], NULL);
synchronize_rcu();
usta->index = -1;
}
return result;
}
static int load_fw(struct ieee80211_hw *hw)
{
int err = 0;
int i = 0;
struct mac80211_dev *dev = (struct mac80211_dev *)hw->priv;
const struct firmware *fw = NULL;
const char *bin_name[FWLDR_NUM_BINS] = {FWLDR_HW_BIN,
FWLDR_FW_BIN};
do {
err = request_firmware(&fw, bin_name[i], dev->dev);
if (err) {
pr_err("Failed to get %s, Error = %d\n",
bin_name[i],
err);
break;
}
err = fwldr_load_fw(fw->data, i);
if (err == FWLDR_SUCCESS)
pr_info("%s is loaded\n", bin_name[i]);
else
pr_err("Loading of %s failed\n", bin_name[i]);
release_firmware(fw);
i++;
} while ((i < FWLDR_NUM_BINS) && (!err));
return err;
}
#ifdef MULTI_CHAN_SUPPORT
static void umac_chanctx_set_channel(struct mac80211_dev *dev,
struct umac_vif *uvif,
struct cfg80211_chan_def *chandef)
{
unsigned int freq_band = 0;
unsigned int ch_width = 0;
int center_freq1 = 0;
int center_freq2 = 0;
unsigned int pri_chan;
pri_chan = ieee80211_frequency_to_channel(chandef->chan->center_freq);
center_freq1 = chandef->center_freq1;
center_freq2 = chandef->center_freq2;
freq_band = chandef->chan->band;
ch_width = chandef->width;
uccp420wlan_prog_channel(pri_chan, center_freq1,
center_freq2,
ch_width,
uvif->vif_index,
freq_band);
}
static int add_chanctx(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *conf)
{
struct mac80211_dev *dev = NULL;
struct umac_chanctx *ctx = NULL;
int chanctx_id = 0;
int i = 0;
dev = hw->priv;
for (i = 0; i < MAX_CHANCTX; i++) {
if (!dev->chanctx[i]) {
chanctx_id = i;
break;
}
}
if (i == MAX_CHANCTX) {
pr_err("Exceeded Max chan contexts limit(%d)\n", MAX_CHANCTX);
return -1;
}
DEBUG_LOG("%s: %d MHz\n",
__func__,
conf->def.chan->center_freq);
mutex_lock(&dev->mutex);
ctx = (struct umac_chanctx *)conf->drv_priv;
ctx->index = chanctx_id;
INIT_LIST_HEAD(&ctx->vifs);
ctx->nvifs = 0;
rcu_assign_pointer(dev->chanctx[i], conf);
synchronize_rcu();
mutex_unlock(&dev->mutex);
return 0;
}
static void remove_chanctx(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *conf)
{
struct mac80211_dev *dev = NULL;
struct umac_chanctx *ctx = NULL;
dev = hw->priv;
ctx = (struct umac_chanctx *)conf->drv_priv;
DEBUG_LOG("%s: %d MHz\n",
__func__,
conf->def.chan->center_freq);
mutex_lock(&dev->mutex);
/* Unassign_vif_chanctx should have been called to free all the assigned
* vifs before this call is called, hence we dont need to specifically
* free the vifs here
*/
rcu_assign_pointer(dev->chanctx[ctx->index], NULL);
synchronize_rcu();
ctx->index = -1;
mutex_unlock(&dev->mutex);
}
static void change_chanctx(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *conf,
u32 changed)
{
#ifdef NOT_YET
struct umac_vif *uvif = NULL;
struct mac80211_dev *dev = NULL;
struct umac_chanctx *ctx = NULL;
dev = hw->priv;
ctx = (struct umac_chanctx *)conf->drv_priv;
DEBUG_LOG("%s: %d MHz\n",
__func__,
conf->def.chan->center_freq);
/* SDK: See why this is needed */
if (dev->curr_chanctx_idx != ctx->index) {
DEBUG_LOG("Current ctx differs from the new ctx\n");
return;
}
list_for_each_entry(uvif, &ctx->vifs, list)
umac_chanctx_set_channel(dev, uvif, &conf->def);
#else
return;
#endif
}
static int assign_vif_chanctx(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_chanctx_conf *conf)
{
struct mac80211_dev *dev = NULL;
struct umac_vif *uvif = NULL;
struct umac_chanctx *ctx = NULL;
int prog_chanctx_time_info = 0;
dev = hw->priv;
uvif = (struct umac_vif *)vif->drv_priv;
ctx = (struct umac_chanctx *)conf->drv_priv;
DEBUG_LOG("%s: addr: %pM, type: %d, p2p: %d chan: %d MHz\n",
__func__,
vif->addr,
vif->type,
vif->p2p,
conf->def.chan->center_freq);
mutex_lock(&dev->mutex);
uvif->chanctx = ctx;
list_add_tail(&uvif->list, &ctx->vifs);
prog_chanctx_time_info = !(ctx->nvifs);
ctx->nvifs++;
/* If this is the first vif being assigned to the channel context then
* increment our count of the active channel contexts
*/
if (prog_chanctx_time_info) {
if (!dev->num_active_chanctx)
dev->curr_chanctx_idx = ctx->index;
dev->num_active_chanctx++;
uccp420wlan_prog_chanctx_time_info();
}
umac_chanctx_set_channel(dev, uvif, &conf->def);
mutex_unlock(&dev->mutex);
return 0;
}
static void unassign_vif_chanctx(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_chanctx_conf *conf)
{
struct mac80211_dev *dev = NULL;
struct umac_vif *uvif = NULL;
struct umac_chanctx *ctx = NULL;
u32 hw_queue_map = 0;
int i = 0;
dev = hw->priv;
uvif = (struct umac_vif *)vif->drv_priv;
ctx = (struct umac_chanctx *)conf->drv_priv;
DEBUG_LOG("%s: addr: %pM, type: %d, p2p: %d chan: %d MHz\n",
__func__,
vif->addr,
vif->type,
vif->p2p,
conf->def.chan->center_freq);
mutex_lock(&dev->mutex);
/* We need to specifically handle flushing tx queues for the AP VIF
* here (for STA VIF, mac80211 handles this via flush_queues)
*/
if (vif->type == NL80211_IFTYPE_AP) {
/* Flush all queues for this VIF */
for (i = 0; i < NUM_ACS; i++)
hw_queue_map |= BIT(i);
uccp420_flush_vif_queues(dev,
uvif,
uvif->chanctx->index,
hw_queue_map,
UMAC_VIF_CHANCTX_TYPE_OPER);
}
uvif->chanctx = NULL;
list_del(&uvif->list);
ctx->nvifs--;
if (!ctx->nvifs) {
dev->num_active_chanctx--;
if (dev->num_active_chanctx)
uccp420wlan_prog_chanctx_time_info();
}
mutex_unlock(&dev->mutex);
}
static void flush_queues(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
u32 queues,
bool drop)
{
struct mac80211_dev *dev = NULL;
struct umac_vif *uvif = NULL;
u32 hw_queue_map = 0;
int i = 0;
dev = hw->priv;
mutex_lock(&dev->mutex);
if (!vif)
goto out;
uvif = (struct umac_vif *)vif->drv_priv;
if (!uvif->chanctx)
goto out;
/* Convert the mac80211 queue map to our hw queue map */
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
if (queues & BIT(i))
hw_queue_map |= BIT(tx_queue_map(i));
}
/* This op should not get called during ROC operation, so we can assume
* that the vif_chanctx_type will be UMAC_VIF_CHANCTX_TYPE_OPER. As for
* TSMC operation the VIF can only be associated to one channel context,
* so we pass uvif->chanctx->index as the parameter for chanctx_idx
*/
uccp420_flush_vif_queues(dev,
uvif,
uvif->chanctx->index,
hw_queue_map,
UMAC_VIF_CHANCTX_TYPE_OPER);
out:
mutex_unlock(&dev->mutex);
}
#endif
static struct ieee80211_ops ops = {
.tx = tx,
.start = start,
.stop = stop,
.add_interface = add_interface,
.remove_interface = remove_interface,
.config = config,
.prepare_multicast = prepare_multicast,
.configure_filter = configure_filter,
.sw_scan_start = NULL,
.sw_scan_complete = NULL,
.get_stats = NULL,
.sta_notify = NULL,
.conf_tx = conf_vif_tx,
.bss_info_changed = bss_info_changed,
.set_tim = NULL,
.set_key = set_key,
.get_tkip_seq = NULL,
.tx_last_beacon = tx_last_beacon,
.ampdu_action = ampdu_action,
.set_antenna = set_antenna,
.remain_on_channel = remain_on_channel,
.cancel_remain_on_channel = cancel_remain_on_channel,
#ifdef CONFIG_PM
.suspend = img_suspend,
.resume = img_resume,
#endif
.hw_scan = scan,
.cancel_hw_scan = cancel_hw_scan,
.set_rekey_data = NULL,
.set_rts_threshold = set_rts_threshold,
.sta_add = sta_add,
.sta_remove = sta_remove,
#ifdef MULTI_CHAN_SUPPORT
.add_chanctx = add_chanctx,
.remove_chanctx = remove_chanctx,
.change_chanctx = change_chanctx,
.assign_vif_chanctx = assign_vif_chanctx,
.unassign_vif_chanctx = unassign_vif_chanctx,
.flush = flush_queues,
#endif
};
static void uccp420wlan_exit(void)
{
/* DEV Release */
struct mac80211_dev *dev = (struct mac80211_dev *)wifi->hw->priv;
if (wifi->hw) {
ieee80211_unregister_hw(wifi->hw);
device_release_driver(dev->dev);
device_destroy(hwsim_class, 0);
ieee80211_free_hw(wifi->hw);
wifi->hw = NULL;
}
class_destroy(hwsim_class);
}
static int uccp420wlan_init(void)
{
struct ieee80211_hw *hw;
int error;
struct mac80211_dev *dev = NULL;
int i;
/* Allocate new hardware device */
hw = ieee80211_alloc_hw(sizeof(struct mac80211_dev), &ops);
if (hw == NULL) {
pr_err("Failed to allocate memory for ieee80211_hw\n");
error = -ENOMEM;
goto out;
}
dev = (struct mac80211_dev *)hw->priv;
memset(dev, 0, sizeof(struct mac80211_dev));
hwsim_class = class_create(THIS_MODULE, "uccp420");
if (IS_ERR(hwsim_class)) {
pr_err("Failed to create the device class\n");
error = PTR_ERR(hwsim_class);
goto out;
}
/* Only 1 per physical intf*/
dev->dev = device_create(hwsim_class, NULL, 0, hw, "uccwlan");
if (IS_ERR(dev->dev)) {
pr_err("uccwlan: device_create failed (%ld)\n",
PTR_ERR(dev->dev));
error = -ENOMEM;
goto auto_dev_class_failed;
}
dev->dev->driver = &img_uccp_driver.driver;
if (device_is_registered(dev->dev)) {
error = device_bind_driver(dev->dev);
} else {
pr_err("Device is not registered\n");
error = -ENODEV;
goto failed_hw;
}
if (error != 0) {
pr_err("uccwlan: device_bind_driver failed (%d)\n", error);
goto failed_hw;
}
pr_info("MAC ADDR: %pM\n", vif_macs);
SET_IEEE80211_DEV(hw, dev->dev);
mutex_init(&dev->mutex);
spin_lock_init(&dev->bcast_lock);
#ifdef MULTI_CHAN_SUPPORT
spin_lock_init(&dev->chanctx_lock);
#endif
spin_lock_init(&dev->roc_lock);
dev->state = STOPPED;
dev->active_vifs = 0;
dev->txpower = DEFAULT_TX_POWER;
dev->tx_antenna = DEFAULT_TX_ANT_SELECT;
dev->rts_threshold = DEFAULT_RTS_THRESHOLD;
strncpy(dev->name, UCCP_DRIVER_NAME, 11);
dev->name[11] = '\0';
for (i = 0; i < wifi->params.num_vifs; i++)
ether_addr_copy(dev->if_mac_addresses[i].addr, vif_macs[i]);
/* Initialize HW parameters */
init_hw(hw);
dev->hw = hw;
dev->params = &wifi->params;
dev->stats = &wifi->stats;
dev->umac_proc_dir_entry = wifi->umac_proc_dir_entry;
dev->current_vif_count = 0;
dev->stats->system_rev = system_rev;
#ifdef MULTI_CHAN_SUPPORT
dev->num_active_chanctx = 0;
for (i = 0; i < MAX_VIFS; i++)
dev->vifs[i] = NULL;
#endif
/*Register hardware*/
error = ieee80211_register_hw(hw);
/* Production test hack: Set all channel flags to 0 to allow IBSS
* creation in all channels
*/
if (wifi->params.production_test && !error) {
enum ieee80211_band band;
struct ieee80211_supported_band *sband;
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
sband = hw->wiphy->bands[band];
if (sband)
for (i = 0; i < sband->n_channels; i++)
sband->channels[i].flags = 0;
}
}
if (!error) {
wifi->hw = hw;
goto out;
} else {
uccp420wlan_exit();
goto out;
}
failed_hw:
device_release_driver(dev->dev);
device_destroy(hwsim_class, 0);
auto_dev_class_failed:
class_destroy(hwsim_class);
out:
return error;
}
static int proc_read_config(struct seq_file *m, void *v)
{
int i = 0;
int cnt = 0;
int rf_params_size = sizeof(wifi->params.rf_params) /
sizeof(wifi->params.rf_params[0]);
struct mac80211_dev *dev = ((struct mac80211_dev *)(wifi->hw->priv));
seq_puts(m, "************* Configurable Parameters ***********\n");
seq_printf(m, "dot11g_support = %d\n", wifi->params.dot11g_support);
seq_printf(m, "dot11a_support = %d\n", wifi->params.dot11a_support);
seq_printf(m, "sensitivity = %d\n", wifi->params.ed_sensitivity);
seq_printf(m, "auto_sensitivity = %d\n", wifi->params.auto_sensitivity);
/*RF Input params*/
seq_puts(m, "rf_params =");
for (i = 0; i < rf_params_size; i++)
seq_printf(m, " %02X", wifi->params.rf_params[i]);
seq_puts(m, "\n");
seq_puts(m, "rf_params_vpd =");
for (i = 0; i < rf_params_size; i++)
seq_printf(m, " %02X", wifi->params.rf_params_vpd[i]);
seq_puts(m, "\n");
seq_printf(m, "production_test = %d\n", wifi->params.production_test);
seq_printf(m, "bypass_vpd = %d\n", wifi->params.bypass_vpd);
seq_printf(m, "tx_fixed_mcs_indx = %d (%s)\n",
wifi->params.tx_fixed_mcs_indx,
(wifi->params.prod_mode_rate_flag &
ENABLE_VHT_FORMAT) ?
"VHT" : (wifi->params.prod_mode_rate_flag &
ENABLE_11N_FORMAT) ? "HT" : "Not Set");
if (wifi->params.tx_fixed_rate > -1) {
if (wifi->params.tx_fixed_rate == 55)
seq_puts(m, "tx_fixed_rate = 5.5\n");
else
seq_printf(m, "tx_fixed_rate = %d\n",
wifi->params.tx_fixed_rate);
} else
seq_printf(m, "tx_fixed_rate = %d\n",
wifi->params.tx_fixed_rate);
seq_printf(m, "num_spatial_streams (Per Frame) = %d\n",
wifi->params.num_spatial_streams);
seq_printf(m, "uccp_num_spatial_streams (UCCP Init) = %d\n",
wifi->params.uccp_num_spatial_streams);
seq_printf(m, "antenna_sel (UCCP Init) = %d\n",
wifi->params.antenna_sel);
seq_printf(m, "max_data_size = %d (%dK)\n",
wifi->params.max_data_size,
wifi->params.max_data_size/1024);
seq_printf(m, "max_tx_cmds = %d\n",
wifi->params.max_tx_cmds);
seq_printf(m, "disable_power_save (Disables all power save's) = %d\n",
wifi->params.disable_power_save);
seq_printf(m, "disable_sm_power_save (Disables MIMO PS only) = %d\n",
wifi->params.disable_sm_power_save);
seq_printf(m, "mgd_mode_tx_fixed_mcs_indx = %d (%s)\n",
wifi->params.mgd_mode_tx_fixed_mcs_indx,
(wifi->params.prod_mode_rate_flag &
ENABLE_VHT_FORMAT) ?
"VHT" : (wifi->params.prod_mode_rate_flag &
ENABLE_11N_FORMAT) ? "HT" : "Not Set");
if (wifi->params.mgd_mode_tx_fixed_rate > -1) {
if (wifi->params.mgd_mode_tx_fixed_rate == 55)
seq_puts(m, "mgd_mode_tx_fixed_rate = 5.5\n");
else
seq_printf(m, "mgd_mode_tx_fixed_rate = %d\n",
wifi->params.mgd_mode_tx_fixed_rate);
} else
seq_printf(m, "mgd_mode_tx_fixed_rate = %d\n",
wifi->params.mgd_mode_tx_fixed_rate);
seq_printf(m, "num_vifs = %d\n",
wifi->params.num_vifs);
seq_puts(m, "vif_macs =");
for (i = 0; i < wifi->params.num_vifs; i++) {
seq_printf(m, " %02x:%02x:%02x:%02x:%02x:%02x",
vif_macs[i][0], vif_macs[i][1], vif_macs[i][2],
vif_macs[i][3], vif_macs[i][4], vif_macs[i][5]
);
}
seq_puts(m, "\n");
seq_printf(m, "chnl_bw = %d\n",
wifi->params.chnl_bw);
seq_printf(m, "prod_mode_chnl_bw_40_mhz = %d\n",
wifi->params.prod_mode_chnl_bw_40_mhz);
if (vht_support)
seq_printf(m, "prod_mode_chnl_bw_80_mhz = %d\n",
wifi->params.prod_mode_chnl_bw_80_mhz);
seq_printf(m, "sec_ch_offset_40_plus = %d\n",
wifi->params.sec_ch_offset_40_plus);
seq_printf(m, "sec_ch_offset_40_minus = %d\n",
wifi->params.sec_ch_offset_40_minus);
if (vht_support) {
seq_printf(m, "sec_40_ch_offset_80_plus = %d\n",
wifi->params.sec_40_ch_offset_80_plus);
seq_printf(m, "sec_40_ch_offset_80_minus = %d\n",
wifi->params.sec_40_ch_offset_80_minus);
}
seq_printf(m, "rate_protection_type = %d (0: Disable, 1: Enable)\n",
wifi->params.rate_protection_type);
seq_puts(m, "Bits:80MHz-VHT-11N-SGI-40MHz-GF\n");
seq_printf(m, "prod_mode_rate_flag = %d\n",
wifi->params.prod_mode_rate_flag);
seq_printf(m, "prod_mode_rate_preamble_type (0: Short, 1: Long) = %d\n",
wifi->params.prod_mode_rate_preamble_type);
seq_printf(m, "prod_mode_stbc_enabled = %d\n",
wifi->params.prod_mode_stbc_enabled);
seq_printf(m, "prod_mode_bcc_or_ldpc = %d\n",
wifi->params.prod_mode_bcc_or_ldpc);
seq_printf(m, "vht_beamformer_enable = %d\n",
wifi->params.vht_beamform_enable);
seq_printf(m, "vht_beamformer_period = %dms\n",
wifi->params.vht_beamform_period);
seq_printf(m, "bg_scan_enable = %d\n",
wifi->params.bg_scan_enable);
seq_puts(m, "bg_scan_channel_list =");
for (i = 0; i < wifi->params.bg_scan_num_channels; i++) {
if (wifi->params.bg_scan_channel_list[i])
seq_printf(m, " %d",
wifi->params.bg_scan_channel_list[i]);
}
seq_puts(m, "\n");
seq_puts(m, "bg_scan_channel_flags =");
for (i = 0; i < wifi->params.bg_scan_num_channels; i++) {
if (wifi->params.bg_scan_channel_flags[i])
seq_printf(m, " %d",
wifi->params.bg_scan_channel_flags[i]);
}
seq_puts(m, "\n");
seq_printf(m, "bg_scan_intval = %dms\n",
wifi->params.bg_scan_intval/1000);
/*currently not used in LMAC, so don't export to user*/
#if 0
seq_printf(m, "bg_scan_chan_dur = %d\n", wifi->params.bg_scan_chan_dur);
seq_printf(m, "bg_scan_serv_chan_dur = %d\n",
wifi->params.bg_scan_serv_chan_dur);
#endif
seq_printf(m, "bg_scan_num_channels = %d\n",
wifi->params.bg_scan_num_channels);
seq_printf(m, "nw_selection = %d\n",
wifi->params.nw_selection);
seq_printf(m, "scan_type = %d (PASSIVE: 0, ACTIVE: 1)\n",
wifi->params.scan_type);
#ifdef PERF_PROFILING
seq_printf(m, "driver_tput = %d\n",
wifi->params.driver_tput);
#endif
seq_printf(m, "fw_loading = %d\n", wifi->params.fw_loading);
seq_printf(m, "bt_state = %d\n", wifi->params.bt_state);
/* Beacon Time Stamp */
if (dev->state == STARTED) {
for (cnt = 0; cnt < MAX_VIFS; cnt++) {
unsigned long long ts1;
unsigned long long bssid, atu;
int status;
char dev_name[10];
unsigned int t2;
spin_lock_bh(&tsf_lock);
ts1 = get_unaligned_le64(wifi->params.sync[cnt].ts1);
bssid =
get_unaligned_le64(wifi->params.sync[cnt].bssid);
status = wifi->params.sync[cnt].status;
sprintf(dev_name, "%s%d", "wlan", cnt);
atu = wifi->params.sync[cnt].atu;
t2 = wifi->params.sync[cnt].ts2;
spin_unlock_bh(&tsf_lock);
if (status && wifi->params.sync[cnt].name)
seq_printf(m,
"sync=%s %d %llu %llu %llx t2=%u\n",
dev_name,
status,
(unsigned long long)ts1,
atu,
(unsigned long long)bssid,
t2);
}
}
seq_puts(m, "****** Production Test (or) FTM Parameters *******\n");
seq_printf(m, "start_packet_gen = %d (-1: Infinite loop)\n",
wifi->params.pkt_gen_val);
seq_printf(m, "payload_length = %d bytes\n",
wifi->params.payload_length);
seq_printf(m, "start_prod_mode = channel: %d\n",
wifi->params.start_prod_mode);
seq_printf(m, "continuous_tx = %d\n",
wifi->params.cont_tx);
if (ftm || wifi->params.production_test)
seq_printf(m, "set_tx_power = %d dB\n",
wifi->params.set_tx_power);
seq_printf(m, "center_frequency = %d\n",
ieee80211_frequency_to_channel(dev->cur_chan.center_freq1));
if (ftm)
seq_printf(m, "aux_adc_chain_id = %d\n",
wifi->params.aux_adc_chain_id);
seq_puts(m, "To see the updated stats\n");
seq_puts(m, "please run: echo get_stats=1 > /proc/uccp420/params\n");
seq_puts(m, "To see the cleared phy stats\n");
seq_puts(m, "please run: echo clear_stats=1 > /proc/uccp420/params\n");
seq_puts(m, "************* VERSION ***********\n");
seq_printf(m, "UCCP_DRIVER_VERSION = %s\n", UCCP_DRIVER_VERSION);
if (wifi->hw &&
(((struct mac80211_dev *)(wifi->hw->priv))->state != STARTED)) {
seq_printf(m, "LMAC_VERSION = %s\n", "UNKNOWN");
seq_printf(m, "Firmware version = %s\n", "UNKNOWN");
} else {
seq_printf(m, "LMAC_VERSION = %s\n",
wifi->stats.uccp420_lmac_version);
seq_printf(m, "Firmware version= %d.%d\n",
(wifi->stats.uccp420_lmac_version[0] - '0'),
(wifi->stats.uccp420_lmac_version[2] - '0'));
}
return 0;
}
static int proc_read_phy_stats(struct seq_file *m, void *v)
{
int i = 0;
seq_puts(m, "************* BB Stats ***********\n");
seq_printf(m, "ed_cnt=%d\n",
wifi->stats.ed_cnt);
seq_printf(m, "mpdu_cnt=%d\n",
wifi->stats.mpdu_cnt);
seq_printf(m, "ofdm_crc32_pass_cnt=%d\n",
wifi->stats.ofdm_crc32_pass_cnt);
seq_printf(m, "ofdm_crc32_fail_cnt=%d\n",
wifi->stats.ofdm_crc32_fail_cnt);
seq_printf(m, "dsss_crc32_pass_cnt=%d\n",
wifi->stats.dsss_crc32_pass_cnt);
seq_printf(m, "dsss_crc32_fail_cnt=%d\n",
wifi->stats.dsss_crc32_fail_cnt);
seq_printf(m, "mac_id_pass_cnt=%d\n",
wifi->stats.mac_id_pass_cnt);
seq_printf(m, "mac_id_fail_cnt=%d\n",
wifi->stats.mac_id_fail_cnt);
seq_printf(m, "ofdm_corr_pass_cnt=%d\n",
wifi->stats.ofdm_corr_pass_cnt);
seq_printf(m, "ofdm_corr_fail_cnt=%d\n",
wifi->stats.ofdm_corr_fail_cnt);
seq_printf(m, "dsss_corr_pass_cnt=%d\n",
wifi->stats.dsss_corr_pass_cnt);
seq_printf(m, "dsss_corr_fail_cnt=%d\n",
wifi->stats.dsss_corr_fail_cnt);
seq_printf(m, "ofdm_s2l_fail_cnt=%d\n",
wifi->stats.ofdm_s2l_fail_cnt);
seq_printf(m, "lsig_fail_cnt=%d\n",
wifi->stats.lsig_fail_cnt);
seq_printf(m, "htsig_fail_cnt=%d\n",
wifi->stats.htsig_fail_cnt);
seq_printf(m, "vhtsiga_fail_cnt=%d\n",
wifi->stats.vhtsiga_fail_cnt);
seq_printf(m, "vhtsigb_fail_cnt=%d\n",
wifi->stats.vhtsigb_fail_cnt);
seq_printf(m, "nonht_ofdm_cnt=%d\n",
wifi->stats.nonht_ofdm_cnt);
seq_printf(m, "nonht_dsss_cnt=%d\n",
wifi->stats.nonht_dsss_cnt);
seq_printf(m, "mm_cnt=%d\n",
wifi->stats.mm_cnt);
seq_printf(m, "gf_cnt=%d\n",
wifi->stats.gf_cnt);
seq_printf(m, "vht_cnt=%d\n",
wifi->stats.vht_cnt);
seq_printf(m, "aggregation_cnt=%d\n",
wifi->stats.aggregation_cnt);
seq_printf(m, "non_aggregation_cnt=%d\n",
wifi->stats.non_aggregation_cnt);
seq_printf(m, "ndp_cnt=%d\n",
wifi->stats.ndp_cnt);
seq_printf(m, "ofdm_ldpc_cnt=%d\n",
wifi->stats.ofdm_ldpc_cnt);
seq_printf(m, "ofdm_bcc_cnt=%d\n",
wifi->stats.ofdm_bcc_cnt);
seq_printf(m, "midpacket_cnt=%d\n",
wifi->stats.midpacket_cnt);
seq_printf(m, "dsss_sfd_fail_cnt=%d\n",
wifi->stats.dsss_sfd_fail_cnt);
seq_printf(m, "dsss_hdr_fail_cnt=%d\n",
wifi->stats.dsss_hdr_fail_cnt);
seq_printf(m, "dsss_short_preamble_cnt=%d\n",
wifi->stats.dsss_short_preamble_cnt);
seq_printf(m, "dsss_long_preamble_cnt=%d\n",
wifi->stats.dsss_long_preamble_cnt);
seq_printf(m, "sifs_event_cnt=%d\n",
wifi->stats.sifs_event_cnt);
seq_printf(m, "cts_cnt=%d\n",
wifi->stats.cts_cnt);
seq_printf(m, "ack_cnt=%d\n",
wifi->stats.ack_cnt);
seq_printf(m, "sifs_no_resp_cnt=%d\n",
wifi->stats.sifs_no_resp_cnt);
seq_printf(m, "unsupported_cnt=%d\n",
wifi->stats.unsupported_cnt);
seq_printf(m, "l1_corr_fail_cnt=%d\n",
wifi->stats.l1_corr_fail_cnt);
seq_printf(m, "phy_stats_reserved22=%d\n",
wifi->stats.phy_stats_reserved22);
seq_printf(m, "phy_stats_reserved23=%d\n",
wifi->stats.phy_stats_reserved23);
seq_printf(m, "phy_stats_reserved24=%d\n",
wifi->stats.phy_stats_reserved24);
seq_printf(m, "phy_stats_reserved25=%d\n",
wifi->stats.phy_stats_reserved25);
seq_printf(m, "phy_stats_reserved26=%d\n",
wifi->stats.phy_stats_reserved26);
seq_printf(m, "phy_stats_reserved27=%d\n",
wifi->stats.phy_stats_reserved27);
seq_printf(m, "phy_stats_reserved28=%d\n",
wifi->stats.phy_stats_reserved28);
seq_printf(m, "phy_stats_reserved29=%d\n",
wifi->stats.phy_stats_reserved29);
seq_printf(m, "phy_stats_reserved30=%d\n",
wifi->stats.phy_stats_reserved30);
seq_printf(m, "tx_pkts_from_lmac = %d\n",
wifi->stats.tx_pkts_from_lmac);
seq_printf(m, "tx_pkts_tx2tx = %d\n", wifi->stats.tx_pkts_tx2tx);
seq_printf(m, "tx_pkts_from_rx = %d\n", wifi->stats.tx_pkts_from_rx);
seq_printf(m, "tx_pkts_ofdm = %d\n", wifi->stats.tx_pkts_ofdm);
seq_printf(m, "tx_pkts_dsss = %d\n", wifi->stats.tx_pkts_dsss);
seq_printf(m, "tx_pkts_reached_end_of_fsm = %d\n",
wifi->stats.tx_pkts_reached_end_of_fsm);
seq_printf(m, "tx_unsupported_modulation = %d\n",
wifi->stats.tx_unsupported_modulation);
seq_printf(m, "tx_latest_pkt_from_lmac_or_sifs = %d\n",
wifi->stats.tx_latest_pkt_from_lmac_or_sifs);
seq_printf(m, "tx_abort_bt_confirm_cnt = %d\n",
wifi->stats.tx_abort_bt_confirm_cnt);
seq_printf(m, "tx_abort_txstart_timeout_cnt = %d\n",
wifi->stats.tx_abort_txstart_timeout_cnt);
seq_printf(m, "tx_abort_midBT_cnt = %d\n",
wifi->stats.tx_abort_mid_bt_cnt);
seq_printf(m, "tx_abort_dac_underrun_cnt = %d\n",
wifi->stats.tx_abort_dac_underrun_cnt);
seq_printf(m, "tx_ofdm_symbols_master = %d\n",
wifi->stats.tx_ofdm_symbols_master);
seq_printf(m, "tx_ofdm_symbols_slave1 = %d\n",
wifi->stats.tx_ofdm_symbols_slave1);
seq_printf(m, "tx_ofdm_symbols_slave2 = %d\n",
wifi->stats.tx_ofdm_symbols_slave2);
seq_printf(m, "tx_dsss_symbols = %d\n", wifi->stats.tx_dsss_symbols);
seq_puts(m, "************* RF Stats ***********\n");
/*RF output data*/
seq_puts(m, "rf_calib_data =");
for (i = 0; i < wifi->stats.rf_calib_data_length; i++)
seq_printf(m, "%02X", wifi->stats.rf_calib_data[i]);
seq_puts(m, "\n");
return 0;
}
static int proc_read_mac_stats(struct seq_file *m, void *v)
{
unsigned int index;
unsigned int total_samples = 0;
unsigned int total_value = 0;
int total_rssi_samples = 0;
int total_rssi_value = 0;
struct mac80211_dev *dev = NULL;
if (ftm) {
for (index = 0; index < MAX_AUX_ADC_SAMPLES; index++) {
if (!wifi->params.pdout_voltage[index])
continue;
total_value += wifi->params.pdout_voltage[index];
total_samples++;
}
}
for (index = 0; index < MAX_RSSI_SAMPLES; index++) {
if (!wifi->params.production_test)
break;
if (!wifi->params.rssi_average[index])
continue;
total_rssi_value += wifi->params.rssi_average[index];
total_rssi_samples++;
}
seq_puts(m, "************* UMAC STATS ***********\n");
seq_printf(m, "rx_packet_mgmt_count = %d\n",
wifi->stats.rx_packet_mgmt_count);
seq_printf(m, "rx_packet_data_count = %d\n",
wifi->stats.rx_packet_data_count);
seq_printf(m, "tx_packet_count(HT MCS0) = %d\n",
wifi->stats.ht_tx_mcs0_packet_count);
seq_printf(m, "tx_packet_count(HT MCS1) = %d\n",
wifi->stats.ht_tx_mcs1_packet_count);
seq_printf(m, "tx_packet_count(HT MCS2) = %d\n",
wifi->stats.ht_tx_mcs2_packet_count);
seq_printf(m, "tx_packet_count(HT MCS3) = %d\n",
wifi->stats.ht_tx_mcs3_packet_count);
seq_printf(m, "tx_packet_count(HT MCS4) = %d\n",
wifi->stats.ht_tx_mcs4_packet_count);
seq_printf(m, "tx_packet_count(HT MCS5) = %d\n",
wifi->stats.ht_tx_mcs5_packet_count);
seq_printf(m, "tx_packet_count(HT MCS6) = %d\n",
wifi->stats.ht_tx_mcs6_packet_count);
seq_printf(m, "tx_packet_count(HT MCS7) = %d\n",
wifi->stats.ht_tx_mcs7_packet_count);
if (wifi->params.uccp_num_spatial_streams == 2) {
seq_printf(m, "tx_packet_count(HT MCS8) = %d\n",
wifi->stats.ht_tx_mcs8_packet_count);
seq_printf(m, "tx_packet_count(HT MCS9) = %d\n",
wifi->stats.ht_tx_mcs9_packet_count);
seq_printf(m, "tx_packet_count(HT MCS10) = %d\n",
wifi->stats.ht_tx_mcs10_packet_count);
seq_printf(m, "tx_packet_count(HT MCS11) = %d\n",
wifi->stats.ht_tx_mcs11_packet_count);
seq_printf(m, "tx_packet_count(HT MCS12) = %d\n",
wifi->stats.ht_tx_mcs12_packet_count);
seq_printf(m, "tx_packet_count(HT MCS13) = %d\n",
wifi->stats.ht_tx_mcs13_packet_count);
seq_printf(m, "tx_packet_count(HT MCS14) = %d\n",
wifi->stats.ht_tx_mcs14_packet_count);
seq_printf(m, "tx_packet_count(HT MCS15) = %d\n",
wifi->stats.ht_tx_mcs15_packet_count);
}
if (vht_support) {
seq_printf(m, "tx_packet_count(VHT MCS0) = %d\n",
wifi->stats.vht_tx_mcs0_packet_count);
seq_printf(m, "tx_packet_count(VHT MCS1) = %d\n",
wifi->stats.vht_tx_mcs1_packet_count);
seq_printf(m, "tx_packet_count(VHT MCS2) = %d\n",
wifi->stats.vht_tx_mcs2_packet_count);
seq_printf(m, "tx_packet_count(VHT MCS3) = %d\n",
wifi->stats.vht_tx_mcs3_packet_count);
seq_printf(m, "tx_packet_count(VHT MCS4) = %d\n",
wifi->stats.vht_tx_mcs4_packet_count);
seq_printf(m, "tx_packet_count(VHT MCS5) = %d\n",
wifi->stats.vht_tx_mcs5_packet_count);
seq_printf(m, "tx_packet_count(VHT MCS6) = %d\n",
wifi->stats.vht_tx_mcs6_packet_count);
seq_printf(m, "tx_packet_count(VHT MCS7) = %d\n",
wifi->stats.vht_tx_mcs7_packet_count);
seq_printf(m, "tx_packet_count(VHT MCS8) = %d\n",
wifi->stats.vht_tx_mcs8_packet_count);
seq_printf(m, "tx_packet_count(VHT MCS9) = %d\n",
wifi->stats.vht_tx_mcs9_packet_count);
}
seq_printf(m, "tx_cmds_from_stack= %d\n",
wifi->stats.tx_cmds_from_stack);
seq_printf(m, "tx_dones_to_stack= %d\n",
wifi->stats.tx_dones_to_stack);
seq_printf(m, "oustanding_cmd_cnt = %d\n",
wifi->stats.outstanding_cmd_cnt);
seq_printf(m, "gen_cmd_send_count = %d\n",
wifi->stats.gen_cmd_send_count);
seq_printf(m, "umac_scan_req = %d\n",
wifi->stats.umac_scan_req);
seq_printf(m, "umac_scan_complete = %d\n",
wifi->stats.umac_scan_complete);
seq_printf(m, "tx_cmd_send_count_single = %d\n",
wifi->stats.tx_cmd_send_count_single);
seq_printf(m, "tx_cmd_send_count_multi = %d\n",
wifi->stats.tx_cmd_send_count_multi);
seq_printf(m, "tx_done_recv_count = %d\n",
wifi->stats.tx_done_recv_count);
dev = (struct mac80211_dev *)(wifi->hw->priv);
seq_printf(m, "tx_buff_pool_map = %ld\n",
dev->tx.buf_pool_bmp[0]);
if (ftm)
seq_printf(m, "pdout_val = %d (total samples: %d)\n",
total_samples ? (total_value/total_samples) : 0,
total_samples);
if (wifi->params.production_test)
seq_printf(m,
"rssi_average = %d dBm (total rssi samples: %d)\n",
total_rssi_samples ?
(total_rssi_value/total_rssi_samples) : 0,
total_rssi_samples);
seq_puts(m, "************* LMAC STATS ***********\n");
seq_printf(m, "roc_start =%d\n",
wifi->stats.roc_start);
seq_printf(m, "roc_stop =%d\n",
wifi->stats.roc_stop);
seq_printf(m, "roc_complete =%d\n",
wifi->stats.roc_complete);
seq_printf(m, "roc_stop_complete =%d\n",
wifi->stats.roc_stop_complete);
/* TX related */
seq_printf(m, "tx_cmd_cnt =%d\n",
wifi->stats.tx_cmd_cnt);
seq_printf(m, "tx_done_cnt =%d\n",
wifi->stats.tx_done_cnt);
seq_printf(m, "tx_edca_trigger_cnt =%d\n",
wifi->stats.tx_edca_trigger_cnt);
seq_printf(m, "tx_edca_isr_cnt =%d\n",
wifi->stats.tx_edca_isr_cnt);
seq_printf(m, "tx_start_cnt =%d\n",
wifi->stats.tx_start_cnt);
seq_printf(m, "tx_abort_cnt =%d\n",
wifi->stats.tx_abort_cnt);
seq_printf(m, "tx_abort_isr_cnt =%d\n",
wifi->stats.tx_abort_isr_cnt);
seq_printf(m, "tx_underrun_cnt =%d\n",
wifi->stats.tx_underrun_cnt);
seq_printf(m, "tx_rts_cnt =%d\n",
wifi->stats.tx_rts_cnt);
seq_printf(m, "tx_ampdu_cnt =%d\n",
wifi->stats.tx_ampdu_cnt);
seq_printf(m, "tx_mpdu_cnt =%d\n",
wifi->stats.tx_mpdu_cnt);
/* RX related */
seq_printf(m, "rx_isr_cnt =%d\n",
wifi->stats.rx_isr_cnt);
seq_printf(m, "rx_ack_cts_to_cnt =%d\n",
wifi->stats.rx_ack_cts_to_cnt);
seq_printf(m, "rx_cts_cnt =%d\n",
wifi->stats.rx_cts_cnt);
seq_printf(m, "rx_ack_resp_cnt =%d\n",
wifi->stats.rx_ack_resp_cnt);
seq_printf(m, "rx_ba_resp_cnt =%d\n",
wifi->stats.rx_ba_resp_cnt);
seq_printf(m, "rx_fail_in_ba_bitmap_cnt =%d\n",
wifi->stats.rx_fail_in_ba_bitmap_cnt);
seq_printf(m, "rx_circular_buffer_free_cnt =%d\n",
wifi->stats.rx_circular_buffer_free_cnt);
seq_printf(m, "rx_mic_fail_cnt =%d\n",
wifi->stats.rx_mic_fail_cnt);
/* HAL related */
seq_printf(m, "hal_cmd_cnt =%d\n",
wifi->stats.hal_cmd_cnt);
seq_printf(m, "hal_event_cnt =%d\n",
wifi->stats.hal_event_cnt);
seq_printf(m, "hal_ext_ptr_null_cnt =%d\n",
wifi->stats.hal_ext_ptr_null_cnt);
return 0;
}
static long param_get_val(unsigned char *buf,
unsigned char *str,
unsigned long *val)
{
unsigned char *temp;
if (strstr(buf, str)) {
temp = strstr(buf, "=") + 1;
/*To handle the fixed rate 5.5Mbps case*/
if (!strncmp(temp, "5.5", 3)) {
*val = 55;
return 1;
} else if (!kstrtoul(temp, 0, val)) {
return 1;
} else {
return 0;
}
} else {
return 0;
}
}
static long param_get_sval(unsigned char *buf,
unsigned char *str,
long *val)
{
unsigned char *temp;
if (strstr(buf, str)) {
temp = strstr(buf, "=") + 1;
/*To handle the fixed rate 5.5Mbps case*/
if (!strncmp(temp, "5.5", 3)) {
*val = 55;
return 1;
} else if (!kstrtol(temp, 0, val)) {
return 1;
} else {
return 0;
}
} else {
return 0;
}
}
static long param_get_match(unsigned char *buf, unsigned char *str)
{
if (strstr(buf, str))
return 1;
else
return 0;
}
void uccp420wlan_reinit(void)
{
if (wifi->hw)
uccp420wlan_exit();
uccp420wlan_init();
uccp_reinit = 1;
}
static ssize_t proc_write_config(struct file *file,
const char __user *buffer,
size_t count,
loff_t *ppos)
{
char buf[(RF_PARAMS_SIZE * 2) + 50];
unsigned long val;
long sval;
unsigned int rate = wifi->params.prod_mode_rate_flag;
unsigned int b40 = wifi->params.prod_mode_chnl_bw_40_mhz;
unsigned int b80 = wifi->params.prod_mode_chnl_bw_80_mhz;
struct mac80211_dev *dev = wifi->hw->priv;
if (count >= sizeof(buf))
count = sizeof(buf) - 1;
if (copy_from_user(buf, buffer, count))
return -EFAULT;
buf[count] = '\0';
if (param_get_val(buf, "dot11a_support=", &val)) {
if (((val == 0) || (val == 1)) &&
(wifi->params.dot11a_support != val)) {
wifi->params.dot11a_support = val;
if ((wifi->params.dot11g_support == 0) &&
(wifi->params.dot11a_support == 0)) {
pr_err("Invalid parameter value. Both bands can't be disabled, at least 1 is needed\n");
} else {
uccp420wlan_reinit();
pr_info("Re-initializing UMAC ..with 2.4GHz support %s and 5GHz support %s\n",
wifi->params.dot11g_support == 0 ?
"disabled" : "enabled",
wifi->params.dot11a_support == 0 ?
"disabled" : "enabled");
}
} else
pr_err("Invalid parameter value\n");
} else if (param_get_val(buf, "dot11g_support=", &val)) {
if (((val == 0) || (val == 1)) &&
(wifi->params.dot11g_support != val)) {
wifi->params.dot11g_support = val;
if ((wifi->params.dot11g_support == 0) &&
(wifi->params.dot11a_support == 0)) {
pr_err("Invalid parameter value. Both bands can't be disabled, at least 1 is needed\n");
} else {
uccp420wlan_reinit();
pr_info("Re-initializing UMAC ..with 2.4GHz support %s and 5GHz support %s\n",
wifi->params.dot11g_support == 0 ?
"disabled" : "enabled",
wifi->params.dot11a_support == 0 ?
"disabled" : "enabled");
}
} else
pr_err("Invalid parameter value\n");
} else if (param_get_sval(buf, "sensitivity=", &sval)) {
/*if (sval > -51 || sval < -96 || (sval % 3 != 0))*/
/* pr_err("Invalid parameter value\n");*/
/*else*/
wifi->params.ed_sensitivity = sval;
} else if (param_get_val(buf, "auto_sensitivity=", &val)) {
if ((val == 0) || (val == 1))
wifi->params.auto_sensitivity = val;
else
pr_err("Invalid parameter value.\n");
} else if (param_get_val(buf, "production_test=", &val)) {
if ((val == 0) || (val == 1)) {
if (wifi->params.production_test != val) {
if (wifi->params.production_test)
wifi->params.num_vifs = 1;
wifi->params.production_test = val;
uccp420wlan_reinit();
pr_err("Re-initializing UMAC ..\n");
}
} else
pr_err("Invalid parameter value\n");
} else if (param_get_val(buf, "bypass_vpd=", &val)) {
if ((val == 0) || (val == 1)) {
if (wifi->params.bypass_vpd != val)
wifi->params.bypass_vpd = val;
} else
pr_err("Invalid parameter value\n");
} else if (param_get_val(buf, "num_vifs=", &val)) {
if (val > 0 && val <= MAX_VIFS) {
if (wifi->params.num_vifs != val) {
uccp420wlan_reinit();
pr_err("Re-initializing UMAC ..\n");
wifi->params.num_vifs = val;
}
}
} else if (param_get_match(buf, "rf_params=")) {
conv_str_to_byte(wifi->params.rf_params,
strstr(buf, "=") + 1,
RF_PARAMS_SIZE);
} else if (param_get_val(buf, "rx_packet_mgmt_count=", &val)) {
wifi->stats.rx_packet_mgmt_count = val;
} else if (param_get_val(buf, "rx_packet_data_count=", &val)) {
wifi->stats.rx_packet_data_count = val;
} else if (param_get_val(buf, "pdout_val=", &val)) {
wifi->stats.pdout_val = val;
} else if (param_get_val(buf, "get_stats=", &val)) {
if (dev->state != STARTED) {
pr_err("Interface is not initialized\n");
goto error;
}
uccp420wlan_prog_mib_stats();
} else if (param_get_val(buf, "max_data_size=", &val)) {
if (wifi->params.max_data_size != val) {
if ((wifi->params.max_data_size >= 2 * 1024) &&
(wifi->params.max_data_size <= (12 * 1024))) {
wifi->params.max_data_size = val;
uccp420wlan_reinit();
pr_err("Re-initalizing UCCP420 with %ld as max data size\n",
val);
} else
pr_err("Invalid Value for max data size: should be (2K-12K)\n");
}
} else if (param_get_val(buf, "max_tx_cmds=", &val)) {
if (val >= 1 || val <= MAX_TX_CMDS)
wifi->params.max_tx_cmds = val;
} else if (param_get_val(buf, "disable_power_save=", &val)) {
if ((val == 0) || (val == 1)) {
if (val != wifi->params.disable_power_save) {
wifi->params.disable_power_save = val;
uccp420wlan_reinit();
pr_err("Re-initalizing UCCP420 with global powerave %s\n",
val ? "DISABLED" : "ENABLED");
}
}
} else if (param_get_val(buf, "disable_sm_power_save=", &val)) {
if ((val == 0) || (val == 1)) {
if (val != wifi->params.disable_sm_power_save) {
wifi->params.disable_sm_power_save = val;
uccp420wlan_reinit();
pr_err("Re-initalizing UCCP420 with smps %s\n",
val ? "DISABLED" : "ENABLED");
}
}
} else if (param_get_val(buf, "uccp_num_spatial_streams=", &val)) {
if (val > 0 && val <= min(MAX_TX_STREAMS, MAX_RX_STREAMS)) {
if (val != wifi->params.uccp_num_spatial_streams) {
wifi->params.uccp_num_spatial_streams = val;
wifi->params.num_spatial_streams = val;
wifi->params.max_tx_streams = val;
wifi->params.max_rx_streams = val;
uccp420wlan_reinit();
pr_err("Re-initalizing UCCP420 with %ld spatial streams\n",
val);
}
} else
pr_err("Invalid parameter value: Allowed Range: 1 to %d\n",
min(MAX_TX_STREAMS, MAX_RX_STREAMS));
} else if (param_get_val(buf, "antenna_sel=", &val)) {
if (val == 1 || val == 2) {
if (val != wifi->params.antenna_sel) {
wifi->params.antenna_sel = val;
uccp420wlan_reinit();
pr_err("Re-initalizing UCCP420 with %ld antenna selection\n",
val);
}
} else
pr_err("Invalid parameter value: Allowed Values: 1 or 2\n");
} else if (param_get_val(buf, "num_spatial_streams=", &val)) {
if (val > 0 && val <= wifi->params.uccp_num_spatial_streams)
wifi->params.num_spatial_streams = val;
else
pr_err("Invalid parameter value, should be less than or equal to uccp_num_spatial_streams\n");
} else if (param_get_sval(buf, "mgd_mode_tx_fixed_mcs_indx=", &sval)) {
if (wifi->params.mgd_mode_tx_fixed_rate == -1) {
int mcs_indx = wifi->params.mgd_mode_tx_fixed_mcs_indx;
if (vht_support && (wifi->params.prod_mode_rate_flag &
ENABLE_VHT_FORMAT)) {
if ((sval >= -1) && (sval <= 9)) {
/* Get_rate will do the MCS holes
* validation
*/
mcs_indx = sval;
} else
pr_err("Invalid parameter value.\n");
} else {
if (wifi->params.num_spatial_streams == 2) {
if ((sval >= -1) && (sval <= 15))
mcs_indx = sval;
else
pr_err("Invalid MIMO HT MCS: %ld\n",
sval);
}
if (wifi->params.num_spatial_streams == 1) {
if ((sval >= -1) && (sval <= 7))
mcs_indx = sval;
else
pr_err("Invalid SISO HT MCS: %ld\n",
sval);
}
}
wifi->params.mgd_mode_tx_fixed_mcs_indx = mcs_indx;
} else
pr_err("Fixed rate other than MCS index is currently set\n");
} else if (param_get_sval(buf, "mgd_mode_tx_fixed_rate=", &sval)) {
if (wifi->params.mgd_mode_tx_fixed_mcs_indx == -1) {
int tx_fixed_rate = wifi->params.mgd_mode_tx_fixed_rate;
if (wifi->params.dot11g_support == 1 &&
((sval == 1) ||
(sval == 2) ||
(sval == 55) ||
(sval == 11))) {
tx_fixed_rate = sval;
} else if ((sval == 6) ||
(sval == 9) ||
(sval == 12) ||
(sval == 18) ||
(sval == 24) ||
(sval == 36) ||
(sval == 48) ||
(sval == 54) ||
(sval == -1)) {
tx_fixed_rate = sval;
} else {
pr_err("Invalid parameter value.\n");
return count;
}
wifi->params.mgd_mode_tx_fixed_rate = tx_fixed_rate;
} else
pr_err("MCS data rate(index) is currently set\n");
} else if (param_get_sval(buf, "tx_fixed_mcs_indx=", &sval)) {
if (wifi->params.production_test != 1) {
pr_err("Only can be set in production mode.\n");
goto error;
}
if (sval == -1) {
wifi->params.tx_fixed_mcs_indx = -1;
goto error;
}
if (wifi->params.tx_fixed_rate != -1) {
pr_err("Fixed rate other than MCS index is currently set\n");
goto error;
}
if (vht_support && (rate & ENABLE_VHT_FORMAT)) {
if ((sval >= -1) && (sval <= 9)) {
if ((b40 == 0) && (b80 == 0) && (sval == 9)) {
pr_err("Invalid VHT MCS: 20MHZ-MCS9.\n");
/*Reset to Default*/
wifi->params.tx_fixed_mcs_indx = 7;
} else
wifi->params.tx_fixed_mcs_indx = sval;
} else
pr_err("Invalid parameter value.\n");
} else if (vht_support && (rate & ENABLE_11N_FORMAT)) {
if (wifi->params.num_spatial_streams == 2) {
if ((sval >= -1) && (sval <= 15))
wifi->params.tx_fixed_mcs_indx = sval;
else
pr_err("Invalid MIMO HT MCS: %ld\n",
sval);
} else if (wifi->params.num_spatial_streams == 1) {
if ((sval >= -1) && (sval <= 7))
wifi->params.tx_fixed_mcs_indx = sval;
else
pr_err("Invalid SISO HT MCS: %ld\n",
sval);
}
} else
pr_err("MCS Setting is invalid for Legacy, please set prod_mode_rate_flag first.\n");
} else if (param_get_sval(buf, "tx_fixed_rate=", &sval)) {
if (wifi->params.production_test != 1) {
pr_err("Only can be set in production mode.\n");
goto error;
}
if (sval == -1) {
wifi->params.tx_fixed_rate = -1;
goto error;
}
if (wifi->params.tx_fixed_mcs_indx != -1) {
pr_err("MCS Index is currently set.\n");
goto error;
}
if ((wifi->params.dot11g_support == 1) &&
((sval == 1) ||
(sval == 2) ||
(sval == 55) ||
(sval == 11))) {
wifi->params.tx_fixed_rate = sval;
} else if ((sval == 6) ||
(sval == 9) ||
(sval == 12) ||
(sval == 18) ||
(sval == 24) ||
(sval == 36) ||
(sval == 48) ||
(sval == 54) ||
(sval == -1)) {
wifi->params.tx_fixed_rate = sval;
} else {
pr_err("Invalid parameter value: tx_fixed_rate=%ld\n",
sval);
goto error;
}
} else if (param_get_val(buf, "chnl_bw=", &val)) {
if (((val == 0) ||
(vht_support && (val == 2)) ||
(val == 1))) {
wifi->params.chnl_bw = val;
uccp420wlan_reinit();
pr_err("Re-initializing UMAC ..\n");
} else
pr_err("Invalid parameter value.\n");
} else if (param_get_match(buf, "vif_macs=")) {
char *macdata = strstr(buf, "=") + 1;
int dataok = 1;
int i;
for (i = 0; i < wifi->params.num_vifs; i++, macdata += ETH_ALEN*2) {
if (conv_str_to_byte(vif_macs[i], macdata, ETH_ALEN) != 0) {
dataok = 0;
break;
}
}
if (dataok) {
uccp420wlan_reinit();
pr_err("Re-initializing UMAC ..\n");
} else
pr_err("Invalid parameter value.\n");
} else if (param_get_val(buf, "prod_mode_chnl_bw_40_mhz=", &val)) {
do {
if (wifi->params.production_test != 1) {
pr_err("Can be set in only in production mode.\n");
break;
}
if (!((val == 0) || (val == 1))) {
pr_err("Invalid parameter value.\n");
break;
}
wifi->params.prod_mode_chnl_bw_40_mhz = val;
if (!vht_support)
break;
wifi->params.prod_mode_chnl_bw_80_mhz = 0;
} while (0);
} else if (vht_support &&
param_get_val(buf, "prod_mode_chnl_bw_80_mhz=", &val)) {
if (wifi->params.production_test == 1) {
if ((val == 0) || (val == 1)) {
wifi->params.prod_mode_chnl_bw_40_mhz = 0;
wifi->params.prod_mode_chnl_bw_80_mhz = val;
} else
pr_err("Invalid parameter value.\n");
} else
pr_err("Can be set in only in production mode.\n");
} else if (param_get_val(buf, "sec_ch_offset_40_plus=", &val)) {
do {
if (wifi->params.production_test != 1) {
pr_err("Can be set in only in production mode.\n");
break;
}
if (val == 0) {
wifi->params.sec_ch_offset_40_plus = 0;
goto error;
}
if (!((wifi->params.prod_mode_chnl_bw_40_mhz == 1)
|| (vht_support &&
(wifi->params.prod_mode_chnl_bw_80_mhz == 1))
)) {
pr_err("Can be set only when prod_mode_chnl_bw_40_mhz is set.\n");
break;
}
if (wifi->params.sec_ch_offset_40_minus == 1) {
pr_err("Can be set only when sec_ch_offset_40_minus is not set\n");
break;
}
if (!((val == 0) || (val == 1))) {
pr_err("Invalid parameter value.\n");
break;
}
wifi->params.sec_ch_offset_40_plus = val;
} while (0);
} else if (param_get_val(buf, "sec_ch_offset_40_minus=", &val)) {
do {
if (wifi->params.production_test != 1) {
pr_err("Can be set in only in production mode.\n");
break;
}
if (val == 0) {
wifi->params.sec_ch_offset_40_minus = 0;
goto error;
}
if (!((wifi->params.prod_mode_chnl_bw_40_mhz == 1)
|| (vht_support &&
(wifi->params.prod_mode_chnl_bw_80_mhz == 1))
)) {
pr_err("Can be set only when prod_mode_chnl_bw_40_mhz is set.\n");
break;
}
if (wifi->params.sec_ch_offset_40_plus == 1) {
pr_err("Can be set only when sec_ch_offset_40_plus is not set\n");
break;
}
if (!((val == 0) || (val == 1))) {
pr_err("Invalid parameter value.\n");
break;
}
wifi->params.sec_ch_offset_40_minus = val;
} while (0);
} else if (vht_support &&
param_get_val(buf, "sec_40_ch_offset_80_plus=", &val)) {
do {
if (wifi->params.production_test != 1) {
pr_err("Can be set in only in production mode.\n");
break;
}
if (val == 0) {
wifi->params.sec_40_ch_offset_80_plus = 0;
goto error;
}
if (!(wifi->params.prod_mode_chnl_bw_80_mhz == 1)) {
pr_err("Can be set only when prod_mode_chnl_bw_80_mhz is set\n");
break;
}
if (wifi->params.sec_40_ch_offset_80_minus == 1) {
pr_err("Can be set only when sec_40_ch_offset_80_minus is not set\n");
break;
}
if (!((val == 0) || (val == 1))) {
pr_err("Invalid parameter value.\n");
break;
}
wifi->params.sec_40_ch_offset_80_plus = val;
} while (0);
} else if (vht_support &&
(param_get_val(buf, "sec_40_ch_offset_80_minus=", &val))) {
do {
if (wifi->params.production_test != 1) {
pr_err("Can be set in only in production mode.\n");
break;
}
if (val == 0) {
wifi->params.sec_40_ch_offset_80_minus = 0;
goto error;
}
if (!(wifi->params.prod_mode_chnl_bw_80_mhz == 1)) {
pr_err("Can be set if prod_mode_chnl_bw_80_mhz is set\n");
break;
}
if (wifi->params.sec_40_ch_offset_80_plus == 1) {
pr_err("Can be set only when sec_40_ch_offset_80_plus is not set\n");
break;
}
if (!((val == 0) || (val == 1))) {
pr_err("Invalid parameter value.\n");
break;
}
wifi->params.sec_40_ch_offset_80_minus = val;
} while (0);
} else if (param_get_val(buf, "prod_mode_rate_flag=", &val)) {
do {
/*Only first 6 flags are defined currently*/
if (val > 63)
pr_err("Invalid parameter value");
if ((val & ENABLE_VHT_FORMAT) &&
(val & ENABLE_11N_FORMAT)) {
pr_err("Cannot set HT and VHT both.");
break;
}
if ((val & ENABLE_CHNL_WIDTH_40MHZ) &&
(val & ENABLE_CHNL_WIDTH_80MHZ)) {
pr_err("Cannot set 40 and 80 both.");
break;
}
if ((wifi->params.uccp_num_spatial_streams == 1) &&
(val & ENABLE_SGI) &&
(val & ENABLE_GREEN_FIELD)) {
pr_err("Cannot set GreenField when SGI is enabled for SISO");
break;
}
wifi->params.prod_mode_rate_flag = val;
} while (0);
} else if (param_get_val(buf, "rate_protection_type=", &val)) {
/* 0 is None, 1 is RTS/CTS, 2 is for CTS2SELF */
if ((val == 0) || (val == 1) /*|| (val == 2)*/)
wifi->params.rate_protection_type = val;
else
pr_err("Invalid parameter value");
} else if (param_get_val(buf, "prod_mode_rate_preamble_type=", &val)) {
/*0 is short, 1 is Long*/
if ((val == 0) || (val == 1))
wifi->params.prod_mode_rate_preamble_type = val;
else
pr_err("Invalid parameter value");
} else if (param_get_val(buf, "prod_mode_stbc_enabled=", &val)) {
if (val <= 1)
wifi->params.prod_mode_stbc_enabled = val;
else
pr_err("Invalid parameter value\n");
} else if (param_get_val(buf, "prod_mode_bcc_or_ldpc=", &val)) {
if (val <= 1)
wifi->params.prod_mode_bcc_or_ldpc = val;
else
pr_err("Invalid parameter value\n");
} else if (param_get_val(buf, "reset_hal_params=", &val)) {
if (dev->state != STARTED) {
if (val != 1)
pr_err("Invalid parameter value\n");
else
hal_ops.reset_hal_params();
} else
pr_err("HAL parameters reset can be done only when all interface are down\n");
} else if (param_get_val(buf, "vht_beamformer_enable=", &val)) {
do {
int vht_beamform_period;
if (wifi->params.vht_beamform_enable != val)
break;
if (!((val == VHT_BEAMFORM_ENABLE) ||
(val == VHT_BEAMFORM_DISABLE))) {
pr_err("Invalid VHT Beamforming Enable value should be 1 or 0\n");
break;
}
wifi->params.vht_beamform_enable = val;
/* If not associated, it will be sent upon
* association
*/
if (!wifi->params.is_associated)
break;
vht_beamform_period = wifi->params.vht_beamform_period;
if (dev->state != STARTED) {
pr_err("Interface is not initialized\n");
goto error;
}
uccp420wlan_prog_vht_bform(val, vht_beamform_period);
} while (0);
} else if (param_get_val(buf, "vht_beamformer_period=", &val)) {
do {
int vht_beamform_enable;
if (wifi->params.vht_beamform_enable !=
VHT_BEAMFORM_ENABLE) {
pr_err("VHT Beamforming is disabled, please enable it first\n");
break;
}
if (wifi->params.vht_beamform_enable != val)
break;
if (!((val > 100) || (val < 10000))) {
pr_err("Invalid VHT Beamforming Period must be between 100-10000ms\n");
break;
}
wifi->params.vht_beamform_period = val;
/* If not associated, it will be sent upon
* association
*/
if (!wifi->params.is_associated)
break;
vht_beamform_enable = wifi->params.vht_beamform_period;
if (dev->state != STARTED) {
pr_err("Interface is not initialized\n");
goto error;
}
uccp420wlan_prog_vht_bform(vht_beamform_enable, val);
} while (0);
} else if (param_get_val(buf, "bg_scan_enable=", &val)) {
if (wifi->params.bg_scan_enable != val) {
if ((val == 1) || (val == 0)) {
wifi->params.bg_scan_enable = val;
uccp420wlan_reinit();
pr_err("Re-initializing UMAC ..\n");
} else
pr_err("Invalid bg_scan_enable value should be 1 or 0\n");
}
} else if (param_get_match(buf, "bg_scan_channel_list=")) {
conv_str_to_byte(wifi->params.bg_scan_channel_list,
strstr(buf, "=") + 1,
50);
} else if (param_get_match(buf, "bg_scan_channel_flags=")) {
conv_str_to_byte(wifi->params.bg_scan_channel_flags,
strstr(buf, "=") + 1,
50);
} else if (param_get_val(buf, "bg_scan_intval=", &val)) {
if ((val >= 1000) && (val <= 60000))
wifi->params.bg_scan_intval = val * 1000;/* us */
else
pr_err("Invalid bgscan duration/interval value should be between 1000 to 60000 ms.\n");
#if 0
/*currently not used in LMAC, so don't export to user*/
} else if (param_get_val(buf, "bg_scan_chan_dur =", &val)) {
if ((val >= 100) && (val <= 1000))
wifi->params.bg_scan_chan_dur = val;
else
pr_err("Invalid chan duration value should be between 100 to 1000.\n");
} else if (param_get_val(buf, "bg_scan_serv_chan_dur =", &val)) {
if ((val >= 100) && (val <= 1000))
wifi->params.bg_scan_serv_chan_dur = val;
else
pr_err("Invalid serv chan duration value should be between 100 to 1000.\n");
#endif
} else if (param_get_val(buf, "bg_scan_num_channels=", &val)) {
wifi->params.bg_scan_num_channels = val;
} else if (param_get_val(buf, "nw_selection=", &val)) {
if (dev->state != STARTED) {
pr_err("Interface is not initialized\n");
goto error;
}
if ((val == 1) || (val == 0)) {
wifi->params.nw_selection = val;
pr_err("in nw_selection\n");
uccp420wlan_prog_nw_selection(1, vif_macs[0]);
} else
pr_err("Invalid nw selection value should be 1 or 0\n");
} else if (param_get_val(buf, "scan_type=", &val)) {
if ((val == 0) || (val == 1))
wifi->params.scan_type = val;
else
pr_err("Invalid scan type value %d, should be 0 or 1\n",
(unsigned int)val);
} else if (ftm && param_get_val(buf, "aux_adc_chain_id=", &val)) {
if (dev->state != STARTED) {
pr_err("Interface is not initialized\n");
goto error;
}
memset(wifi->params.pdout_voltage, 0,
sizeof(char) * MAX_AUX_ADC_SAMPLES);
if ((val == AUX_ADC_CHAIN1) || (val == AUX_ADC_CHAIN2)) {
wifi->params.aux_adc_chain_id = val;
uccp420wlan_prog_aux_adc_chain(val);
} else
pr_err("Invalid chain id %d, should be %d or %d\n",
(unsigned int) val,
AUX_ADC_CHAIN1,
AUX_ADC_CHAIN2);
} else if (param_get_val(buf, "continuous_tx=", &val)) {
if (wifi->params.production_test != 1) {
pr_err("continuous_tx: Can be set in only in production mode.\n");
goto error;
}
if (dev->state != STARTED) {
pr_err("Interface is not initialized\n");
goto error;
}
if (val == 0 || val == 1) {
wifi->params.cont_tx = val;
uccp420wlan_prog_cont_tx(val);
} else
pr_err("Invalid tx_continuous parameter\n");
} else if (param_get_val(buf, "start_prod_mode=", &val)) {
unsigned int pri_chnl_num = 0;
unsigned int freq_band = IEEE80211_BAND_5GHZ;
int center_freq = 0;
if (wifi->params.production_test != 1) {
pr_err("start_prod_mode: Can be set in only in production mode.\n");
goto error;
}
if (wifi->params.init_prod) {
pr_err("Production Test is already initialized.\n");
goto error;
}
pri_chnl_num = val;
wifi->params.start_prod_mode = val;
tasklet_init(&dev->proc_tx_tasklet, packet_generation,
(unsigned long)dev);
if (pri_chnl_num < 15)
freq_band = IEEE80211_BAND_2GHZ;
else
freq_band = IEEE80211_BAND_5GHZ;
center_freq =
ieee80211_channel_to_frequency(pri_chnl_num,
freq_band);
if ((wifi->params.fw_loading == 1) &&
load_fw(dev->hw)) {
pr_err("%s: Firmware loading failed\n",
dev->name);
goto error;
}
if (!uccp420wlan_core_init(dev, ftm)) {
uccp420wlan_prog_vif_ctrl(0,
dev->if_mac_addresses[0].addr,
IF_MODE_STA_IBSS,
IF_ADD);
proc_bss_info_changed(
dev->if_mac_addresses[0].addr,
val);
uccp420wlan_prog_channel(pri_chnl_num,
center_freq,
0,
0,
/*It will be overwritten anyway*/
#ifdef MULTI_CHAN_SUPPORT
0,
#endif
freq_band);
skb_queue_head_init(&dev->tx.proc_tx_list[0]);
wifi->params.init_prod = 1;
dev->state = STARTED;
uccp_reinit = 0;
} else {
pr_err("RPU Initialization Failed\n");
wifi->params.init_prod = 0;
}
} else if (param_get_sval(buf, "stop_prod_mode=", &sval)) {
if (!wifi->params.init_prod) {
DEBUG_LOG("Prod mode is not initialized\n");
goto error;
}
tasklet_kill(&dev->proc_tx_tasklet);
#if 0
/* Todo: Enabling this causes RPU Lockup,
* need to debug
*/
uccp420wlan_prog_vif_ctrl(0,
dev->if_mac_addresses[0].addr,
IF_MODE_STA_IBSS,
IF_REM);
#endif
if (!uccp_reinit)
stop(wifi->hw);
wifi->params.start_prod_mode = 0;
wifi->params.pkt_gen_val = 1;
wifi->params.init_prod = 0;
wifi->params.init_pkt_gen = 0;
} else if (param_get_sval(buf, "start_packet_gen=", &sval)) {
if (!wifi->params.init_prod) {
pr_err("NEW Production Mode is not Initialized\n");
goto error;
}
if (wifi->params.init_pkt_gen) {
pr_err("packet gen is already running\n");
goto error;
}
if (wifi->params.tx_fixed_mcs_indx == -1 &&
wifi->params.tx_fixed_rate == -1) {
pr_err("Either tx_fixed_mcs_index Or tx_fixed_rate should be set, both can't be NULL.\n");
goto error;
}
wifi->params.init_pkt_gen = 1;
wifi->params.pkt_gen_val = sval;
if (sval != 0)
tasklet_schedule(&dev->proc_tx_tasklet);
} else if (param_get_sval(buf, "stop_packet_gen=", &sval)) {
if (!wifi->params.init_prod) {
DEBUG_LOG("NEW Production Mode is not Initialized\n");
goto error;
}
wifi->params.pkt_gen_val = 1;
wifi->params.init_pkt_gen = 0;
tasklet_kill(&dev->proc_tx_tasklet);
} else if (param_get_val(buf, "payload_length=", &val)) {
wifi->params.payload_length = val;
} else if (param_get_sval(buf, "set_tx_power=", &sval)) {
if (wifi->params.production_test != 1 && !ftm) {
pr_err("set_tx_power: Can be set in only in FTM/production mode.\n");
goto error;
}
if (!wifi->params.init_prod) {
DEBUG_LOG("NEW Production Mode is not Initialized\n");
goto error;
}
memset(wifi->params.pdout_voltage, 0,
sizeof(char) * MAX_AUX_ADC_SAMPLES);
wifi->params.set_tx_power = sval;
uccp420wlan_prog_txpower(sval);
#ifdef PERF_PROFILING
} else if (param_get_val(buf, "driver_tput=", &val)) {
if ((val == 1) || (val == 0))
wifi->params.driver_tput = val;
else
pr_err("Invalid driver_tput value should be 1 or 0\n");
#endif
} else if (param_get_val(buf, "fw_loading=", &val)) {
wifi->params.fw_loading = val;
} else if (param_get_val(buf, "bt_state=", &val)) {
if (dev->state != STARTED) {
pr_err("Interface is not initialized\n");
goto error;
}
if (val == 0 || val == 1) {
if (val != wifi->params.bt_state) {
wifi->params.bt_state = val;
uccp420wlan_prog_btinfo(val);
}
} else
pr_err("Invalid parameter value: Allowed values: 0 or 1\n");
} else if (param_get_val(buf, "clear_stats=", &val)) {
if (dev->state != STARTED) {
pr_err("Interface is not initialized\n");
goto error;
}
uccp420wlan_prog_clear_stats();
} else if (param_get_val(buf, "disable_beacon_ibss=", &val)) {
if ((val == 1) || (val == 0))
wifi->params.disable_beacon_ibss = val;
else
pr_err("Invalid driver_tput value should be 1 or 0\n");
} else
pr_err("Invalid parameter name: %s\n", buf);
error:
return count;
}
static int proc_open_config(struct inode *inode, struct file *file)
{
return single_open(file, proc_read_config, NULL);
}
static int proc_open_phy_stats(struct inode *inode, struct file *file)
{
return single_open(file, proc_read_phy_stats, NULL);
}
static int proc_open_mac_stats(struct inode *inode, struct file *file)
{
return single_open(file, proc_read_mac_stats, NULL);
}
static const struct file_operations params_fops_config = {
.open = proc_open_config,
.read = seq_read,
.llseek = seq_lseek,
.write = proc_write_config,
.release = single_release
};
static const struct file_operations params_fops_phy_stats = {
.open = proc_open_phy_stats,
.read = seq_read,
.llseek = seq_lseek,
.write = NULL,
.release = single_release
};
static const struct file_operations params_fops_mac_stats = {
.open = proc_open_mac_stats,
.read = seq_read,
.llseek = seq_lseek,
.write = NULL,
.release = single_release
};
static int proc_init(struct proc_dir_entry ***main_dir_entry)
{
struct proc_dir_entry *entry;
int err = 0;
unsigned int i = 0;
/*2.4GHz and 5 GHz PD and TX-PWR calibration params*/
unsigned char rf_params[RF_PARAMS_SIZE * 2];
strncpy(rf_params,
"1E00000000002426292A2C2E3237393F454A52576066000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F00000000002426292A2C2E3237393F454A52576066000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F000000002B2C3033373A3D44474D51575A61656B6F0808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808",
(RF_PARAMS_SIZE * 2));
wifi = kzalloc(sizeof(struct wifi_dev), GFP_KERNEL);
if (!wifi) {
err = -ENOMEM;
goto out;
}
wifi->umac_proc_dir_entry = proc_mkdir("uccp420", NULL);
if (!wifi->umac_proc_dir_entry) {
pr_err("Failed to create proc dir\n");
err = -ENOMEM;
goto proc_dir_fail;
}
entry = proc_create("params", 0644, wifi->umac_proc_dir_entry,
&params_fops_config);
if (!entry) {
pr_err("Failed to create proc entry\n");
err = -ENOMEM;
goto proc_entry1_fail;
}
entry = proc_create("phy_stats", 0444, wifi->umac_proc_dir_entry,
&params_fops_phy_stats);
if (!entry) {
pr_err("Failed to create proc entry\n");
err = -ENOMEM;
goto proc_entry2_fail;
}
entry = proc_create("mac_stats", 0444, wifi->umac_proc_dir_entry,
&params_fops_mac_stats);
if (!entry) {
pr_err("Failed to create proc entry\n");
err = -ENOMEM;
goto proc_entry3_fail;
}
/* Initialize WLAN params */
memset(&wifi->params, 0, sizeof(struct wifi_params));
/* TODO: Make this a struct */
memset(wifi->params.rf_params, 0xFF, sizeof(wifi->params.rf_params));
conv_str_to_byte(wifi->params.rf_params, rf_params, RF_PARAMS_SIZE);
if (!rf_params_vpd)
rf_params_vpd = wifi->params.rf_params;
memcpy(wifi->params.rf_params_vpd, rf_params_vpd, RF_PARAMS_SIZE);
wifi->params.is_associated = 0;
wifi->params.ed_sensitivity = -89;
wifi->params.auto_sensitivity = 1;
wifi->params.dot11a_support = 1;
wifi->params.dot11g_support = 1;
wifi->params.num_vifs = 2;
/* Check, if required add it */
wifi->params.tx_fixed_mcs_indx = -1;
wifi->params.tx_fixed_rate = -1;
wifi->params.num_spatial_streams = min(MAX_TX_STREAMS, MAX_RX_STREAMS);
wifi->params.uccp_num_spatial_streams = min(MAX_TX_STREAMS,
MAX_RX_STREAMS);
wifi->params.antenna_sel = 1;
if (num_streams_vpd > 0)
wifi->params.uccp_num_spatial_streams = num_streams_vpd;
wifi->params.bt_state = 1;
wifi->params.mgd_mode_tx_fixed_mcs_indx = -1;
wifi->params.mgd_mode_tx_fixed_rate = -1;
if (vht_support)
wifi->params.chnl_bw = WLAN_80MHZ_OPERATION;
else
wifi->params.chnl_bw = WLAN_20MHZ_OPERATION;
wifi->params.max_tx_streams = MAX_TX_STREAMS;
wifi->params.max_rx_streams = MAX_RX_STREAMS;
wifi->params.max_data_size = 8 * 1024;
wifi->params.vht_beamform_enable = VHT_BEAMFORM_DISABLE;
wifi->params.vht_beamform_period = 1000; /* ms */
wifi->params.vht_beamform_support = 0;
if (vht_support)
wifi->params.max_tx_cmds = 24;
else
wifi->params.max_tx_cmds = 16;
wifi->params.disable_power_save = 0;
wifi->params.disable_sm_power_save = 0;
wifi->params.rate_protection_type = 0; /* Disable protection by def */
wifi->params.prod_mode_rate_preamble_type = 1; /* LONG */
wifi->params.prod_mode_stbc_enabled = 0;
wifi->params.prod_mode_bcc_or_ldpc = 0;
wifi->params.bg_scan_enable = 0;
memset(wifi->params.bg_scan_channel_list, 0, 50);
memset(wifi->params.bg_scan_channel_flags, 0, 50);
if (wifi->params.dot11g_support) {
wifi->params.bg_scan_num_channels = 3;
wifi->params.bg_scan_channel_list[i] = 1;
wifi->params.bg_scan_channel_flags[i++] = ACTIVE;
wifi->params.bg_scan_channel_list[i] = 6;
wifi->params.bg_scan_channel_flags[i++] = ACTIVE;
wifi->params.bg_scan_channel_list[i] = 11;
wifi->params.bg_scan_channel_flags[i++] = ACTIVE;
}
if (wifi->params.dot11a_support) {
wifi->params.bg_scan_num_channels += 4;
wifi->params.bg_scan_channel_list[i] = 36;
wifi->params.bg_scan_channel_flags[i++] = ACTIVE;
wifi->params.bg_scan_channel_list[i] = 40;
wifi->params.bg_scan_channel_flags[i++] = ACTIVE;
wifi->params.bg_scan_channel_list[i] = 44;
wifi->params.bg_scan_channel_flags[i++] = ACTIVE;
wifi->params.bg_scan_channel_list[i] = 48;
wifi->params.bg_scan_channel_flags[i++] = ACTIVE;
}
wifi->params.disable_beacon_ibss = 0;
wifi->params.pkt_gen_val = -1;
wifi->params.init_pkt_gen = 0;
wifi->params.payload_length = 4000;
wifi->params.start_prod_mode = 0;
wifi->params.init_prod = 0;
wifi->params.bg_scan_intval = 5000 * 1000; /* Once in 5 seconds */
wifi->params.bg_scan_chan_dur = 300; /* Channel spending time */
wifi->params.bg_scan_serv_chan_dur = 100; /* Oper chan spending time */
wifi->params.nw_selection = 0;
wifi->params.scan_type = ACTIVE;
wifi->params.hw_scan_status = HW_SCAN_STATUS_NONE;
wifi->params.fw_loading = 1;
**main_dir_entry = wifi->umac_proc_dir_entry;
return err;
proc_entry3_fail:
remove_proc_entry("phy_stats", wifi->umac_proc_dir_entry);
proc_entry2_fail:
remove_proc_entry("params", wifi->umac_proc_dir_entry);
proc_entry1_fail:
remove_proc_entry("uccp420", NULL);
proc_dir_fail:
kfree(wifi);
out:
return err;
}
static void proc_exit(void)
{
/* This is created in hal_init */
remove_proc_entry("hal_stats", wifi->umac_proc_dir_entry);
remove_proc_entry("mac_stats", wifi->umac_proc_dir_entry);
remove_proc_entry("phy_stats", wifi->umac_proc_dir_entry);
remove_proc_entry("params", wifi->umac_proc_dir_entry);
remove_proc_entry("uccp420", NULL);
kfree(wifi);
}
int _uccp420wlan_80211if_init(struct proc_dir_entry **main_dir_entry)
{
int error;
error = proc_init(&main_dir_entry);
if (error)
return error;
error = uccp420wlan_init();
return error;
}
void _uccp420wlan_80211if_exit(void)
{
struct mac80211_dev *dev = (struct mac80211_dev *)wifi->hw->priv;
if (wifi && wifi->hw) {
/* We can safely call stop as mac80211
* will not call stop because of new
* production mode.
*/
if (dev && wifi->params.init_prod)
stop(wifi->hw);
uccp420wlan_exit();
proc_exit();
}
}