387 lines
9.9 KiB
C
387 lines
9.9 KiB
C
/*
|
|
* Datapath implementation for altobeam APOLLO mac80211 drivers
|
|
* *
|
|
* Copyright (c) 2016, altobeam
|
|
* Author:
|
|
*
|
|
*Based on apollo code
|
|
* Copyright (c) 2010, ST-Ericsson
|
|
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
#ifdef LINUX_OS
|
|
#include <linux/version.h>
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <net/atbm_mac80211.h>
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/udp.h>
|
|
#include <net/ip.h>
|
|
#include <linux/bitops.h>
|
|
#endif
|
|
#include "apollo.h"
|
|
#include "wsm.h"
|
|
#include "bh.h"
|
|
#include "hwio.h"
|
|
#include "sbus.h"
|
|
#include "ieee80211_i.h"
|
|
|
|
|
|
#if defined(CONFIG_ATBM_APOLLO_TX_POLICY_DEBUG)
|
|
#define tx_policy_printk(...) atbm_printk_always(__VA_ARGS__)
|
|
#else
|
|
#define tx_policy_printk(...)
|
|
#endif
|
|
/* ******************************************************************** */
|
|
static int atbm_submit_package(struct atbm_common *hw_priv,struct sk_buff *skb)
|
|
{
|
|
atbm_skb_queue_tail(&hw_priv->tx_frame_queue, skb);
|
|
spin_lock_bh(&hw_priv->tx_queue_lock);
|
|
hw_priv->tx_num_queued++;
|
|
if (hw_priv->tx_queue_overfull == false && hw_priv->tx_num_queued >= ATBM_WIFI_MAX_QUEUE_SZ){
|
|
hw_priv->tx_queue_overfull = true;
|
|
spin_unlock_bh(&hw_priv->tx_queue_lock);
|
|
|
|
netif_tx_stop_all_queues(hw_priv->vif_list[0]->ndev);
|
|
|
|
spin_lock_bh(&hw_priv->tx_queue_lock);
|
|
|
|
}
|
|
spin_unlock_bh(&hw_priv->tx_queue_lock);
|
|
atbm_bh_wakeup(hw_priv);
|
|
return 0;
|
|
}
|
|
static int atbm_tx_h_crypt(struct sta_info *sta,
|
|
struct sk_buff *skb,struct atbm_vif *vif)
|
|
{
|
|
struct ieee80211_hdr *hdr;
|
|
u8 *newhdr;
|
|
enum ieee80211_enc_type enc_type;
|
|
u8 iv_len;
|
|
|
|
if(sta == NULL){
|
|
enc_type = vif->group_key.key_type;
|
|
iv_len = vif->group_key.conf.iv_len;
|
|
}else {
|
|
enc_type = sta->key.key_type;
|
|
iv_len = sta->key.conf.iv_len;
|
|
}
|
|
if(enc_type <= IEEE80211_ENC_TYPE)
|
|
return 0;
|
|
|
|
if(atbm_skb_headroom(skb) < WSM_TX_EXTRA_HEADROOM+iv_len){
|
|
atbm_printk_err("%s headroom not enough\n",__func__);
|
|
return -2;
|
|
}
|
|
//atbm_printk_err("icv_len %d iv_len %d\n",icv_len,sta->key.conf.iv_len);
|
|
hdr = (struct ieee80211_hdr *) skb->data;
|
|
hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
|
|
newhdr = atbm_skb_push(skb, iv_len);
|
|
memmove(newhdr, newhdr + iv_len, ieee80211_hdrlen(hdr->frame_control));
|
|
|
|
return 0;
|
|
}
|
|
/* Default mapping in classifier to work with default
|
|
* queue setup.
|
|
*/
|
|
const int ieee802_1d_to_ac[8] = {
|
|
ATBM_IEEE80211_AC_BE,
|
|
ATBM_IEEE80211_AC_BK,
|
|
ATBM_IEEE80211_AC_BK,
|
|
ATBM_IEEE80211_AC_BE,
|
|
ATBM_IEEE80211_AC_VI,
|
|
ATBM_IEEE80211_AC_VI,
|
|
ATBM_IEEE80211_AC_VO,
|
|
ATBM_IEEE80211_AC_VO
|
|
};
|
|
|
|
static struct wsm_tx *atbm_tx_h_wsm(struct sta_info *sta,struct sk_buff *skb,struct atbm_vif *vif)
|
|
{
|
|
struct wsm_tx *wsm;
|
|
u8 queueid = ieee802_1d_to_ac[skb->priority & 7];
|
|
enum ieee80211_enc_type enc_type;
|
|
u8 icv_len;
|
|
u32 aid;
|
|
|
|
if(sta == NULL){
|
|
enc_type = vif->group_key.key_type;
|
|
icv_len = vif->group_key.conf.icv_len;
|
|
}else {
|
|
enc_type = sta->key.key_type;
|
|
icv_len = sta->key.conf.icv_len;
|
|
}
|
|
|
|
if(enc_type <= IEEE80211_ENC_TYPE)
|
|
icv_len = 0;
|
|
if(enc_type == IEEE80211_ENC_TKIP)
|
|
icv_len += 8;
|
|
|
|
if (sta == NULL){
|
|
aid = 0;
|
|
}else {
|
|
aid = sta->sta.aid;
|
|
}
|
|
|
|
wsm = (struct wsm_tx *)atbm_skb_push(skb, sizeof(struct wsm_tx));
|
|
memset(wsm, 0, sizeof(*wsm));
|
|
wsm->hdr.len = __cpu_to_le16(skb->len + icv_len);
|
|
wsm->hdr.id = __cpu_to_le16(WSM_TRANSMIT_REQ_MSG_ID);
|
|
wsm->queueId =(aid << 2) | wsm_queue_id_to_wsm(queueid);
|
|
wsm->hdr.id |= cpu_to_le16(WSM_TX_IF_ID(vif->if_id));
|
|
wsm->htTxParameters = WSM_TX_LINUX_HOST;
|
|
return wsm;
|
|
}
|
|
static int
|
|
atbm_tx_h_align(struct atbm_vif *priv,
|
|
struct sk_buff *skb,
|
|
u8 *flags)
|
|
{
|
|
u32 offset = (unsigned long)(skb->data) & (u32)3;
|
|
|
|
if (!offset)
|
|
return 0;
|
|
|
|
if (offset & 1) {
|
|
atbm_printk_err(
|
|
"Bug: attempt to transmit a frame "
|
|
"with wrong alignment: %d\n",
|
|
offset);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (atbm_skb_headroom(skb) < offset) {
|
|
atbm_printk_err(
|
|
"Bug: no space allocated "
|
|
"for DMA alignment.\n"
|
|
"headroom: %d\n",
|
|
atbm_skb_headroom(skb));
|
|
return -ENOMEM;
|
|
}
|
|
atbm_skb_push(skb, offset);
|
|
*flags |= WSM_TX_2BYTES_SHIFT;
|
|
return 0;
|
|
}
|
|
static int atbm_tx_h_rate_policy(struct sta_info *sta,
|
|
struct sk_buff *skb,
|
|
struct wsm_tx *wsm)
|
|
{
|
|
/*
|
|
*only surport one stream
|
|
*/
|
|
if(sta == NULL){
|
|
wsm->maxTxRate = RATE_INDEX_B_11M;
|
|
return 0;
|
|
}
|
|
if(sta->bgnMode&STA_11N_MODE){
|
|
wsm->maxTxRate = RATE_INDEX_N_39M;//MCS7
|
|
wsm->flags |= 1<<4;//set rate policy
|
|
}
|
|
else if(sta->bgnMode&STA_11G_MODE){
|
|
wsm->maxTxRate = RATE_INDEX_A_36M;//54M
|
|
|
|
}else {
|
|
wsm->maxTxRate = RATE_INDEX_B_11M;
|
|
}
|
|
|
|
if(sta->bgnMode & STA_11N_MODE){
|
|
wsm->htTxParameters |= cpu_to_le32(WSM_HT_TX_MIXED);
|
|
}
|
|
if(sta->shortGI){
|
|
wsm->htTxParameters |= cpu_to_le32(WSM_HT_TX_SGI);
|
|
}
|
|
return 0;
|
|
}
|
|
static int
|
|
atbm_tx_h_skb_pad(struct atbm_common *priv,
|
|
struct wsm_tx *wsm,
|
|
struct sk_buff *skb)
|
|
{
|
|
u32 len = __le16_to_cpu(wsm->hdr.len);
|
|
u32 padded_len = priv->sbus_ops->align_size(priv->sbus_priv, len);
|
|
|
|
if (WARN_ON(atbm_skb_padto(skb, padded_len) != 0)) {
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void atbm_tx(struct atbm_vif *vif, struct sk_buff *skb)
|
|
{
|
|
struct atbm_vif *priv = vif;
|
|
struct atbm_common *hw_priv = vif->hw_priv;
|
|
struct sta_info *sta;
|
|
struct wsm_tx *wsm;
|
|
u8 flags = 0;
|
|
int ret;
|
|
struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data;
|
|
|
|
if (!skb->data)
|
|
BUG_ON(1);
|
|
|
|
|
|
if (!priv)
|
|
goto drop;
|
|
|
|
if (atomic_read(&priv->enabled) == 0){
|
|
atbm_printk_err("%s:if_id(%d) not enable\n",__func__,priv->if_id);
|
|
goto drop;
|
|
}
|
|
if (WARN_ON(skb_get_queue_mapping(skb) >= 4)){
|
|
atbm_printk_err("%s:if_id(%d) queue err\n",__func__,priv->if_id);
|
|
goto drop;
|
|
}
|
|
sta = sta_info_get(vif,frame->addr1);
|
|
|
|
if(sta == NULL){
|
|
// atbm_printk_err("sta is null[%pM]\n",frame->addr1);
|
|
if(vif->type == NL80211_IFTYPE_STATION){
|
|
atbm_printk_err("%s:[%pM] not associat\n",__func__,frame->addr1);
|
|
goto drop;
|
|
}else if(vif->type == NL80211_IFTYPE_AP){
|
|
if(!is_multicast_ether_addr(frame->addr1)){
|
|
atbm_printk_err("%s:[%pM] not associat\n",__func__,frame->addr1);
|
|
goto drop;
|
|
}
|
|
}
|
|
}
|
|
ret = atbm_tx_h_crypt(sta,skb,vif);
|
|
if (ret){
|
|
atbm_printk_err("%s:if_id(%d) crypt err\n",__func__,priv->if_id);
|
|
goto drop;
|
|
}
|
|
ret = atbm_tx_h_align(priv, skb, &flags);
|
|
if (ret){
|
|
atbm_printk_err("%s:align drop\n",__func__);
|
|
goto drop;
|
|
}
|
|
wsm = atbm_tx_h_wsm(sta, skb,vif);
|
|
if (!wsm) {
|
|
atbm_printk_err("%s:wsm drop\n",__func__);
|
|
ret = -ENOMEM;
|
|
goto drop;
|
|
}
|
|
wsm->flags |= flags;
|
|
ret = atbm_tx_h_rate_policy(sta, skb, wsm);
|
|
if (ret){
|
|
atbm_printk_err("%s:rate drop\n",__func__);
|
|
ret = -1;
|
|
goto drop;
|
|
}
|
|
wsm->htTxParameters &= ~(__cpu_to_le32(WSM_NEED_TX_CONFIRM));
|
|
|
|
ret = atbm_tx_h_skb_pad(hw_priv, wsm, skb);
|
|
if (ret){
|
|
atbm_printk_err("%s:pad drop\n",__func__);
|
|
ret = -2;
|
|
goto drop;
|
|
}
|
|
atbm_submit_package(hw_priv,skb);
|
|
|
|
return;
|
|
drop:
|
|
atbm_dev_kfree_skb(skb);
|
|
return;
|
|
}
|
|
void atbm_rx_cb(struct atbm_vif *priv,
|
|
struct wsm_rx *arg,
|
|
struct sk_buff **skb_p)
|
|
{
|
|
struct atbm_common *hw_priv = ABwifi_vifpriv_to_hwpriv(priv);
|
|
struct sk_buff *skb = *skb_p;
|
|
struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb);
|
|
struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data;
|
|
struct atbm_ieee80211_mgmt *mgmt = (struct atbm_ieee80211_mgmt *)skb->data;
|
|
#ifdef CONFIG_PM
|
|
unsigned long grace_period;
|
|
#endif
|
|
|
|
hdr->flag = 0;
|
|
|
|
if (unlikely(arg->status)) {
|
|
if (arg->status == WSM_STATUS_MICFAILURE) {
|
|
atbm_printk_err( "[RX] MIC failure. ENCRYPTION [%d][%pM][%pM]\n",
|
|
WSM_RX_STATUS_ENCRYPTION(arg->flags),frame->addr1,frame->addr2);
|
|
hdr->flag |= RX_FLAG_MMIC_ERROR;
|
|
} else if (arg->status == WSM_STATUS_NO_KEY_FOUND) {
|
|
atbm_printk_err( "[RX] No key found.ENCRYPTION [%d]\n",WSM_RX_STATUS_ENCRYPTION(arg->flags));
|
|
goto drop;
|
|
} else {
|
|
atbm_printk_err("[RX] Receive failure: %d.ENCRYPTION [%d],fc(%x)\n",
|
|
arg->status,WSM_RX_STATUS_ENCRYPTION(arg->flags),mgmt->frame_control);
|
|
goto drop;
|
|
|
|
}
|
|
}
|
|
#ifdef CHKSUM_HW_SUPPORT
|
|
else {
|
|
|
|
//atbm_printk_err( "arg->flags(%x)\n",arg->flags);
|
|
if (arg->flags & WSM_RX_STATUS_CHKSUM_ERROR){
|
|
//hdr->flag &=~RX_FLAG_HW_CHKSUM_ERROR;
|
|
hdr->flag |= RX_FLAG_HW_CHKSUM_ERROR;
|
|
}else{
|
|
hdr->flag &=~RX_FLAG_HW_CHKSUM_ERROR;
|
|
}
|
|
}
|
|
#endif
|
|
if (skb->len < sizeof(struct ieee80211_pspoll)) {
|
|
atbm_printk_err( "(skb->len < sizeof(struct ieee80211_pspoll),seq(%x)\n",frame->seq_ctrl);
|
|
goto drop;
|
|
}
|
|
|
|
hdr->mactime = 0; /* Not supported by WSM */
|
|
hdr->band = (arg->channelNumber > 14) ?
|
|
IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ;
|
|
hdr->freq = ieee80211_channel_to_frequency(
|
|
arg->channelNumber,
|
|
hdr->band);
|
|
|
|
if (arg->rxedRate >= 14) {
|
|
hdr->flag |= RX_FLAG_HT;
|
|
hdr->rate_idx = arg->rxedRate - 14;
|
|
} else if (arg->rxedRate >= 4) {
|
|
if (hdr->band == IEEE80211_BAND_5GHZ)
|
|
hdr->rate_idx = arg->rxedRate - 6;
|
|
else
|
|
hdr->rate_idx = arg->rxedRate - 2;
|
|
} else {
|
|
hdr->rate_idx = arg->rxedRate;
|
|
}
|
|
|
|
hdr->signal = (s8)arg->rcpiRssi;
|
|
hdr->antenna = 0;
|
|
if(ieee80211_has_protected(frame->frame_control))
|
|
hdr->flag |= RX_FLAG_DECRYPTED;
|
|
if (arg->status == WSM_STATUS_MICFAILURE) {
|
|
hdr->flag |= RX_FLAG_IV_STRIPPED;
|
|
}
|
|
#ifdef CONFIG_PM
|
|
|
|
/* Stay awake for 1sec. after frame is received to give
|
|
* userspace chance to react and acquire appropriate
|
|
* wakelock. */
|
|
if (ieee80211_is_auth(frame->frame_control))
|
|
grace_period = 5 * HZ;
|
|
else if (ieee80211_is_deauth(frame->frame_control))
|
|
grace_period = 5 * HZ;
|
|
else
|
|
grace_period = 0;
|
|
if(grace_period != 0)
|
|
atbm_pm_stay_awake(&hw_priv->pm_state, grace_period);
|
|
#endif
|
|
local_bh_disable();
|
|
ieee80211_rx(priv,skb);
|
|
local_bh_enable();
|
|
*skb_p = NULL;
|
|
|
|
return;
|
|
|
|
drop:
|
|
/* TODO: update failure counters */
|
|
return;
|
|
}
|