/*
|
* Copyright (c) 2015 South Silicon Valley Microelectronics Inc.
|
* Copyright (c) 2015 iComm Corporation
|
*
|
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
*/
|
|
#include <ssv6200.h>
|
#include <linux/nl80211.h>
|
#include <linux/etherdevice.h>
|
#include <linux/delay.h>
|
#include <linux/version.h>
|
#include <linux/time.h>
|
#include <linux/sched.h>
|
#include <net/mac80211.h>
|
#include <ssv6200.h>
|
#include "lib.h"
|
#include "dev.h"
|
#include "ap.h"
|
#include "ssv_rc_common.h"
|
#include "ssv_rc.h"
|
int ssv6200_bcast_queue_len(struct ssv6xxx_bcast_txq *bcast_txq);
|
#define IS_EQUAL(a,b) ( (a) == (b) )
|
#define SET_BIT(v,b) ( (v) |= (0x01<<b) )
|
#define CLEAR_BIT(v,b) ( (v) &= ~(0x01<<b) )
|
#define IS_BIT_SET(v,b) ( (v) & (0x01<<(b) ) )
|
#define PBUF_BASE_ADDR 0x80000000
|
#define PBUF_ADDR_SHIFT 16
|
#define PBUF_MapPkttoID(_PKT) (((u32)_PKT&0x0FFF0000)>>PBUF_ADDR_SHIFT)
|
#define PBUF_MapIDtoPkt(_ID) (PBUF_BASE_ADDR|((_ID)<<PBUF_ADDR_SHIFT))
|
#define SSV6xxx_BEACON_MAX_ALLOCATE_CNT 10
|
#define MTX_BCN_PKTID_CH_LOCK_SHIFT MTX_BCN_PKTID_CH_LOCK_SFT
|
#define MTX_BCN_CFG_VLD_SHIFT MTX_BCN_CFG_VLD_SFT
|
#define MTX_BCN_CFG_VLD_MASK MTX_BCN_CFG_VLD_MSK
|
#define AUTO_BCN_ONGOING_MASK MTX_AUTO_BCN_ONGOING_MSK
|
#define AUTO_BCN_ONGOING_SHIFT MTX_AUTO_BCN_ONGOING_SFT
|
#define MTX_BCN_TIMER_EN_SHIFT MTX_BCN_TIMER_EN_SFT
|
#define MTX_TSF_TIMER_EN_SHIFT MTX_TSF_TIMER_EN_SFT
|
#define MTX_HALT_MNG_UNTIL_DTIM_SHIFT MTX_HALT_MNG_UNTIL_DTIM_SFT
|
#define MTX_BCN_ENABLE_MASK (MTX_BCN_TIMER_EN_I_MSK)
|
#define MTX_BCN_PERIOD_SHIFT MTX_BCN_PERIOD_SFT
|
#define MTX_DTIM_NUM_SHIFT MTX_DTIM_NUM_SFT
|
#define MTX_DTIM_OFST0 MTX_DTIM_OFST0_SFT
|
enum ssv6xxx_beacon_type{
|
SSV6xxx_BEACON_0,
|
SSV6xxx_BEACON_1,
|
};
|
static const u32 ssv6xxx_beacon_adr[] =
|
{
|
ADR_MTX_BCN_CFG0,
|
ADR_MTX_BCN_CFG1,
|
};
|
void ssv6xxx_beacon_reg_lock(struct ssv_softc *sc, bool block)
|
{
|
u32 val;
|
val = block<<MTX_BCN_PKTID_CH_LOCK_SHIFT;
|
#ifdef BEACON_DEBUG
|
printk("ssv6xxx_beacon_reg_lock val[0x:%08x]\n ", val);
|
#endif
|
SMAC_REG_WRITE(sc->sh, ADR_MTX_BCN_MISC, val);
|
}
|
void ssv6xxx_beacon_set_info(struct ssv_softc *sc, u8 beacon_interval, u8 dtim_cnt)
|
{
|
u32 val;
|
if(beacon_interval==0)
|
beacon_interval = 100;
|
#ifdef BEACON_DEBUG
|
printk("[A] BSS_CHANGED_BEACON_INT beacon_int[%d] dtim_cnt[%d]\n",beacon_interval, (dtim_cnt));
|
#endif
|
val = (beacon_interval<<MTX_BCN_PERIOD_SHIFT)| (dtim_cnt<<MTX_DTIM_NUM_SHIFT);
|
SMAC_REG_WRITE(sc->sh, ADR_MTX_BCN_PRD, val);
|
}
|
bool ssv6xxx_beacon_enable(struct ssv_softc *sc, bool bEnable)
|
{
|
u32 regval=0;
|
int ret = 0;
|
if(bEnable && !sc->beacon_usage)
|
{
|
printk("[A] Reject to set beacon!!!. ssv6xxx_beacon_enable bEnable[%d] sc->beacon_usage[%d]\n",bEnable ,sc->beacon_usage);
|
sc->enable_beacon = BEACON_WAITING_ENABLED;
|
return 0;
|
}
|
if((bEnable && (BEACON_ENABLED & sc->enable_beacon))||
|
(!bEnable && !sc->enable_beacon))
|
{
|
printk("[A] ssv6xxx_beacon_enable bEnable[%d] and sc->enable_beacon[%d] are the same. no need to execute.\n",bEnable ,sc->enable_beacon);
|
if(bEnable){
|
printk(" Ignore enable beacon cmd!!!!\n");
|
return 0;
|
}
|
}
|
SMAC_REG_READ(sc->sh, ADR_MTX_BCN_EN_MISC, ®val);
|
#ifdef BEACON_DEBUG
|
printk("[A] ssv6xxx_beacon_enable read misc reg val [%08x]\n", regval);
|
#endif
|
regval &= MTX_BCN_ENABLE_MASK;
|
#ifdef BEACON_DEBUG
|
printk("[A] ssv6xxx_beacon_enable read misc reg val [%08x]\n", regval);
|
#endif
|
regval |= (bEnable<<MTX_BCN_TIMER_EN_SHIFT);
|
ret = SMAC_REG_WRITE(sc->sh, ADR_MTX_BCN_EN_MISC, regval);
|
#ifdef BEACON_DEBUG
|
printk("[A] ssv6xxx_beacon_enable read misc reg val [%08x]\n", regval);
|
#endif
|
sc->enable_beacon = (bEnable==true)?BEACON_ENABLED:0;
|
return ret;
|
}
|
int ssv6xxx_beacon_fill_content(struct ssv_softc *sc, u32 regaddr, u8 *beacon, int size)
|
{
|
u32 i, val;
|
u32 *ptr = (u32*)beacon;
|
size = size/4;
|
for(i=0; i<size; i++)
|
{
|
val = (u32)(*(ptr+i));
|
#ifdef BEACON_DEBUG
|
printk("[%08x] ", val );
|
#endif
|
SMAC_REG_WRITE(sc->sh, regaddr+i*4, val);
|
}
|
#ifdef BEACON_DEBUG
|
printk("\n");
|
#endif
|
return 0;
|
}
|
void ssv6xxx_beacon_fill_tx_desc(struct ssv_softc *sc, struct sk_buff* beacon_skb)
|
{
|
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(beacon_skb);
|
struct ssv6200_tx_desc *tx_desc;
|
u16 pb_offset = TXPB_OFFSET;
|
struct ssv_rate_info ssv_rate;
|
skb_push(beacon_skb, pb_offset);
|
tx_desc = (struct ssv6200_tx_desc *)beacon_skb->data;
|
memset(tx_desc,0, pb_offset);
|
ssv6xxx_rc_hw_rate_idx(sc, tx_info, &ssv_rate);
|
tx_desc->len = beacon_skb->len-pb_offset;
|
tx_desc->c_type = M2_TXREQ;
|
tx_desc->f80211 = 1;
|
tx_desc->ack_policy = 1;
|
tx_desc->hdr_offset = pb_offset;
|
tx_desc->hdr_len = 24;
|
tx_desc->payload_offset = tx_desc->hdr_offset + tx_desc->hdr_len;
|
tx_desc->crate_idx = ssv_rate.crate_hw_idx;
|
tx_desc->drate_idx = ssv_rate.drate_hw_idx;
|
skb_put(beacon_skb, 4);
|
}
|
inline enum ssv6xxx_beacon_type ssv6xxx_beacon_get_valid_reg(struct ssv_softc *sc)
|
{
|
u32 regval =0;
|
SMAC_REG_READ(sc->sh, ADR_MTX_BCN_MISC, ®val);
|
regval &= MTX_BCN_CFG_VLD_MASK;
|
regval = regval >>MTX_BCN_CFG_VLD_SHIFT;
|
if(regval==0x2 || regval == 0x0)
|
return SSV6xxx_BEACON_0;
|
else if(regval==0x1)
|
return SSV6xxx_BEACON_1;
|
else
|
printk("=============>ERROR!!drv_bcn_reg_available\n");
|
return SSV6xxx_BEACON_0;
|
}
|
bool ssv6xxx_beacon_set(struct ssv_softc *sc, struct sk_buff *beacon_skb, int dtim_offset)
|
{
|
u32 reg_tx_beacon_adr = ADR_MTX_BCN_CFG0;
|
enum ssv6xxx_beacon_type avl_bcn_type = SSV6xxx_BEACON_0;
|
bool ret = true;
|
int val;
|
ssv6xxx_beacon_reg_lock(sc, 1);
|
avl_bcn_type = ssv6xxx_beacon_get_valid_reg(sc);
|
if(avl_bcn_type == SSV6xxx_BEACON_1)
|
reg_tx_beacon_adr = ADR_MTX_BCN_CFG1;
|
#ifdef BEACON_DEBUG
|
printk("[A] ssv6xxx_beacon_set avl_bcn_type[%d]\n", avl_bcn_type);
|
#endif
|
do{
|
if(IS_BIT_SET(sc->beacon_usage, avl_bcn_type))
|
{
|
#ifdef BEACON_DEBUG
|
printk("[A] beacon has already been set old len[%d] new len[%d]\n", sc->beacon_info[avl_bcn_type].len, beacon_skb->len);
|
#endif
|
if (sc->beacon_info[avl_bcn_type].len >= beacon_skb->len)
|
{
|
break;
|
}
|
else
|
{
|
if(false == ssv6xxx_pbuf_free(sc, sc->beacon_info[avl_bcn_type].pubf_addr))
|
{
|
#ifdef BEACON_DEBUG
|
printk("=============>ERROR!!Intend to allcoate beacon from ASIC fail.\n");
|
#endif
|
ret = false;
|
goto out;
|
}
|
CLEAR_BIT(sc->beacon_usage, avl_bcn_type);
|
}
|
}
|
sc->beacon_info[avl_bcn_type].pubf_addr = ssv6xxx_pbuf_alloc(sc, beacon_skb->len, TX_BUF);
|
sc->beacon_info[avl_bcn_type].len = beacon_skb->len;
|
if(sc->beacon_info[avl_bcn_type].pubf_addr == 0)
|
{
|
ret = false;
|
goto out;
|
}
|
SET_BIT(sc->beacon_usage, avl_bcn_type);
|
#ifdef BEACON_DEBUG
|
printk("[A] beacon type[%d] usage[%d] allocate new beacon addr[%08x] \n", avl_bcn_type, sc->beacon_usage, sc->beacon_info[avl_bcn_type].pubf_addr);
|
#endif
|
}while(0);
|
ssv6xxx_beacon_fill_content(sc, sc->beacon_info[avl_bcn_type].pubf_addr, beacon_skb->data, beacon_skb->len);
|
val = (PBUF_MapPkttoID(sc->beacon_info[avl_bcn_type].pubf_addr))|(dtim_offset<<MTX_DTIM_OFST0);
|
SMAC_REG_WRITE(sc->sh, reg_tx_beacon_adr, val);
|
#ifdef BEACON_DEBUG
|
printk("[A] update to register reg_tx_beacon_adr[%08x] val[%08x]\n", reg_tx_beacon_adr, val);
|
#endif
|
out:
|
ssv6xxx_beacon_reg_lock(sc, 0);
|
if(sc->beacon_usage && (sc->enable_beacon&BEACON_WAITING_ENABLED)){
|
printk("[A] enable beacon for BEACON_WAITING_ENABLED flags\n");
|
ssv6xxx_beacon_enable(sc, true);
|
}
|
return ret;
|
}
|
inline bool ssv6xxx_auto_bcn_ongoing(struct ssv_softc *sc)
|
{
|
u32 regval;
|
SMAC_REG_READ(sc->sh, ADR_MTX_BCN_MISC, ®val);
|
return ((AUTO_BCN_ONGOING_MASK®val)>>AUTO_BCN_ONGOING_SHIFT);
|
}
|
void ssv6xxx_beacon_release(struct ssv_softc *sc)
|
{
|
int cnt=10;
|
printk("[A] ssv6xxx_beacon_release Enter\n");
|
cancel_work_sync(&sc->set_tim_work);
|
do {
|
if(ssv6xxx_auto_bcn_ongoing(sc))
|
ssv6xxx_beacon_enable(sc, false);
|
else
|
break;
|
cnt--;
|
if(cnt<=0)
|
break;
|
} while(1);
|
if(IS_BIT_SET(sc->beacon_usage, SSV6xxx_BEACON_0))
|
{
|
ssv6xxx_pbuf_free(sc, sc->beacon_info[SSV6xxx_BEACON_0].pubf_addr);
|
CLEAR_BIT(sc->beacon_usage, SSV6xxx_BEACON_0);
|
}
|
if(IS_BIT_SET(sc->beacon_usage, SSV6xxx_BEACON_1))
|
{
|
ssv6xxx_pbuf_free(sc, sc->beacon_info[SSV6xxx_BEACON_1].pubf_addr);
|
CLEAR_BIT(sc->beacon_usage, SSV6xxx_BEACON_1);
|
}
|
sc->enable_beacon = 0;
|
if(sc->beacon_buf){
|
dev_kfree_skb_any(sc->beacon_buf);
|
sc->beacon_buf = NULL;
|
}
|
#ifdef BEACON_DEBUG
|
printk("[A] ssv6xxx_beacon_release leave\n");
|
#endif
|
}
|
void ssv6xxx_beacon_change(struct ssv_softc *sc, struct ieee80211_hw *hw, struct ieee80211_vif *vif, bool aid0_bit_set)
|
{
|
struct sk_buff *skb;
|
struct sk_buff *old_skb = NULL;
|
u16 tim_offset, tim_length;
|
if(sc == NULL || hw == NULL || vif == NULL ){
|
printk("[Error]........ssv6xxx_beacon_change input error\n");
|
return;
|
}
|
do{
|
skb = ieee80211_beacon_get_tim(hw, vif,
|
&tim_offset, &tim_length);
|
if(skb == NULL){
|
printk("[Error]........skb is NULL\n");
|
break;
|
}
|
if (tim_offset && tim_length >= 6) {
|
skb->data[tim_offset + 2] = 0;
|
if (aid0_bit_set)
|
skb->data[tim_offset + 4] |= 1;
|
else
|
skb->data[tim_offset + 4] &= ~1;
|
}
|
#ifdef BEACON_DEBUG
|
printk("[A] beacon len [%d] tim_offset[%d]\n", skb->len, tim_offset);
|
#endif
|
ssv6xxx_beacon_fill_tx_desc(sc, skb);
|
#ifdef BEACON_DEBUG
|
printk("[A] beacon len [%d] tim_offset[%d]\n", skb->len, tim_offset);
|
#endif
|
if(sc->beacon_buf)
|
{
|
if(memcmp(sc->beacon_buf->data, skb->data, (skb->len-FCS_LEN)) == 0){
|
old_skb = skb;
|
break;
|
}
|
else{
|
old_skb = sc->beacon_buf;
|
sc->beacon_buf = skb;
|
}
|
}
|
else{
|
sc->beacon_buf = skb;
|
}
|
tim_offset+=2;
|
if(ssv6xxx_beacon_set(sc, skb, tim_offset))
|
{
|
u8 dtim_cnt = vif->bss_conf.dtim_period-1;
|
if(sc->beacon_dtim_cnt != dtim_cnt)
|
{
|
sc->beacon_dtim_cnt = dtim_cnt;
|
#ifdef BEACON_DEBUG
|
printk("[A] beacon_dtim_cnt [%d]\n", sc->beacon_dtim_cnt);
|
#endif
|
ssv6xxx_beacon_set_info(sc, sc->beacon_interval,
|
sc->beacon_dtim_cnt);
|
}
|
}
|
}while(0);
|
if(old_skb)
|
dev_kfree_skb_any(old_skb);
|
}
|
void ssv6200_set_tim_work(struct work_struct *work)
|
{
|
struct ssv_softc *sc =
|
container_of(work, struct ssv_softc, set_tim_work);
|
#ifdef BROADCAST_DEBUG
|
printk("%s() enter\n", __FUNCTION__);
|
#endif
|
ssv6xxx_beacon_change(sc, sc->hw, sc->ap_vif, sc->aid0_bit_set);
|
#ifdef BROADCAST_DEBUG
|
printk("%s() leave\n", __FUNCTION__);
|
#endif
|
}
|
int ssv6200_bcast_queue_len(struct ssv6xxx_bcast_txq *bcast_txq)
|
{
|
u32 len;
|
unsigned long flags;
|
spin_lock_irqsave(&bcast_txq->txq_lock, flags);
|
len = bcast_txq->cur_qsize;
|
spin_unlock_irqrestore(&bcast_txq->txq_lock, flags);
|
return len;
|
}
|
struct sk_buff* ssv6200_bcast_dequeue(struct ssv6xxx_bcast_txq *bcast_txq, u8 *remain_len)
|
{
|
struct sk_buff *skb = NULL;
|
unsigned long flags;
|
spin_lock_irqsave(&bcast_txq->txq_lock, flags);
|
if(bcast_txq->cur_qsize){
|
bcast_txq->cur_qsize --;
|
if(remain_len)
|
*remain_len = bcast_txq->cur_qsize;
|
skb = __skb_dequeue(&bcast_txq->qhead);
|
}
|
spin_unlock_irqrestore(&bcast_txq->txq_lock, flags);
|
return skb;
|
}
|
int ssv6200_bcast_enqueue(struct ssv_softc *sc, struct ssv6xxx_bcast_txq *bcast_txq,
|
struct sk_buff *skb)
|
{
|
unsigned long flags;
|
spin_lock_irqsave(&bcast_txq->txq_lock, flags);
|
if (bcast_txq->cur_qsize >=
|
SSV6200_MAX_BCAST_QUEUE_LEN){
|
struct sk_buff *old_skb;
|
old_skb = __skb_dequeue(&bcast_txq->qhead);
|
bcast_txq->cur_qsize --;
|
ssv6xxx_txbuf_free_skb(old_skb, (void*)sc);
|
printk("[B] ssv6200_bcast_enqueue - remove oldest queue\n");
|
}
|
__skb_queue_tail(&bcast_txq->qhead, skb);
|
bcast_txq->cur_qsize ++;
|
spin_unlock_irqrestore(&bcast_txq->txq_lock, flags);
|
return bcast_txq->cur_qsize;
|
}
|
void ssv6200_bcast_flush(struct ssv_softc *sc, struct ssv6xxx_bcast_txq *bcast_txq)
|
{
|
struct sk_buff *skb;
|
unsigned long flags;
|
#ifdef BCAST_DEBUG
|
printk("ssv6200_bcast_flush\n");
|
#endif
|
spin_lock_irqsave(&bcast_txq->txq_lock, flags);
|
while(bcast_txq->cur_qsize > 0) {
|
skb = __skb_dequeue(&bcast_txq->qhead);
|
bcast_txq->cur_qsize --;
|
ssv6xxx_txbuf_free_skb(skb, (void*)sc);
|
}
|
spin_unlock_irqrestore(&bcast_txq->txq_lock, flags);
|
}
|
static int queue_block_cnt = 0;
|
void ssv6200_bcast_tx_work(struct work_struct *work)
|
{
|
struct ssv_softc *sc =
|
container_of(work, struct ssv_softc, bcast_tx_work.work);
|
#if 1
|
struct sk_buff* skb;
|
int i;
|
u8 remain_size;
|
#endif
|
unsigned long flags;
|
bool needtimer = true;
|
long tmo = sc->bcast_interval;
|
spin_lock_irqsave(&sc->ps_state_lock, flags);
|
do{
|
#ifdef BCAST_DEBUG
|
printk("[B] bcast_timer: hw_mng_used[%d] HCI_TXQ_EMPTY[%d] bcast_queue_len[%d].....................\n",
|
sc->hw_mng_used, HCI_TXQ_EMPTY(sc->sh, 4), ssv6200_bcast_queue_len(&sc->bcast_txq));
|
#endif
|
if(sc->hw_mng_used != 0 ||
|
false == HCI_TXQ_EMPTY(sc->sh, 4)){
|
#ifdef BCAST_DEBUG
|
printk("HW queue still have frames insdide. skip this one hw_mng_used[%d] bEmptyTXQ4[%d]\n",
|
sc->hw_mng_used, HCI_TXQ_EMPTY(sc->sh, 4));
|
#endif
|
queue_block_cnt++;
|
if(queue_block_cnt>5){
|
queue_block_cnt = 0;
|
ssv6200_bcast_flush(sc, &sc->bcast_txq);
|
needtimer = false;
|
}
|
break;
|
}
|
queue_block_cnt = 0;
|
for(i=0;i<SSV6200_ID_MANAGER_QUEUE;i++){
|
skb = ssv6200_bcast_dequeue(&sc->bcast_txq, &remain_size);
|
if(!skb){
|
needtimer = false;
|
break;
|
}
|
if( (0 != remain_size) &&
|
(SSV6200_ID_MANAGER_QUEUE-1) != i ){
|
struct ieee80211_hdr *hdr;
|
struct ssv6200_tx_desc *tx_desc = (struct ssv6200_tx_desc *)skb->data;
|
hdr = (struct ieee80211_hdr *) ((u8*)tx_desc+tx_desc->hdr_offset);
|
hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
|
}
|
#ifdef BCAST_DEBUG
|
printk("[B] bcast_timer:tx remain_size[%d] i[%d]\n", remain_size, i);
|
#endif
|
spin_unlock_irqrestore(&sc->ps_state_lock, flags);
|
if(HCI_SEND(sc->sh, skb, 4)<0){
|
printk("bcast_timer send fail!!!!!!! \n");
|
ssv6xxx_txbuf_free_skb(skb, (void*)sc);
|
BUG_ON(1);
|
}
|
spin_lock_irqsave(&sc->ps_state_lock, flags);
|
}
|
}while(0);
|
if(needtimer){
|
#ifdef BCAST_DEBUG
|
printk("[B] bcast_timer:need more timer to tx bcast frame time[%d]\n", sc->bcast_interval);
|
#endif
|
queue_delayed_work(sc->config_wq,
|
&sc->bcast_tx_work,
|
tmo);
|
}
|
else{
|
#ifdef BCAST_DEBUG
|
printk("[B] bcast_timer: ssv6200_bcast_stop\n");
|
#endif
|
ssv6200_bcast_stop(sc);
|
}
|
spin_unlock_irqrestore(&sc->ps_state_lock, flags);
|
#ifdef BCAST_DEBUG
|
printk("[B] bcast_timer: leave.....................\n");
|
#endif
|
}
|
void ssv6200_bcast_start_work(struct work_struct *work)
|
{
|
struct ssv_softc *sc =
|
container_of(work, struct ssv_softc, bcast_start_work);
|
#ifdef BCAST_DEBUG
|
printk("[B] ssv6200_bcast_start_work==\n");
|
#endif
|
sc->bcast_interval = (sc->beacon_dtim_cnt+1) *
|
(sc->beacon_interval + 20) * HZ / 1000;
|
if (!sc->aid0_bit_set) {
|
sc->aid0_bit_set = true;
|
ssv6xxx_beacon_change(sc, sc->hw,
|
sc->ap_vif, sc->aid0_bit_set);
|
queue_delayed_work(sc->config_wq,
|
&sc->bcast_tx_work,
|
sc->bcast_interval);
|
#ifdef BCAST_DEBUG
|
printk("[B] bcast_start_work: Modify timer to DTIM[%d]ms==\n",
|
(sc->beacon_dtim_cnt+1)*(sc->beacon_interval + 20));
|
#endif
|
}
|
}
|
void ssv6200_bcast_stop_work(struct work_struct *work)
|
{
|
struct ssv_softc *sc =
|
container_of(work, struct ssv_softc, bcast_stop_work.work);
|
long tmo = HZ / 100;
|
#ifdef BCAST_DEBUG
|
printk("[B] ssv6200_bcast_stop_work\n");
|
#endif
|
if (sc->aid0_bit_set) {
|
if(0== ssv6200_bcast_queue_len(&sc->bcast_txq)){
|
cancel_delayed_work_sync(&sc->bcast_tx_work);
|
sc->aid0_bit_set = false;
|
ssv6xxx_beacon_change(sc, sc->hw,
|
sc->ap_vif, sc->aid0_bit_set);
|
#ifdef BCAST_DEBUG
|
printk("remove group bit in DTIM\n");
|
#endif
|
}
|
else{
|
#ifdef BCAST_DEBUG
|
printk("bcast_stop_work: bcast queue still have data. just modify timer to 10ms\n");
|
#endif
|
queue_delayed_work(sc->config_wq,
|
&sc->bcast_tx_work,
|
tmo);
|
}
|
}
|
}
|
void ssv6200_bcast_stop(struct ssv_softc *sc)
|
{
|
queue_delayed_work(sc->config_wq,
|
&sc->bcast_stop_work, sc->beacon_interval*HZ/1024);
|
}
|
void ssv6200_bcast_start(struct ssv_softc *sc)
|
{
|
queue_work(sc->config_wq, &sc->bcast_start_work);
|
}
|
void ssv6200_release_bcast_frame_res(struct ssv_softc *sc, struct ieee80211_vif *vif)
|
{
|
unsigned long flags;
|
struct ssv_vif_priv_data *priv_vif = (struct ssv_vif_priv_data *)vif->drv_priv;
|
spin_lock_irqsave(&sc->ps_state_lock, flags);
|
priv_vif->sta_asleep_mask = 0;
|
spin_unlock_irqrestore(&sc->ps_state_lock, flags);
|
cancel_work_sync(&sc->bcast_start_work);
|
cancel_delayed_work_sync(&sc->bcast_stop_work);
|
ssv6200_bcast_flush(sc, &sc->bcast_txq);
|
cancel_delayed_work_sync(&sc->bcast_tx_work);
|
}
|