/* * Copyright (c) 2015 iComm-semi Ltd. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #ifdef SSV_MAC80211 #include "ssv_mac80211.h" #else #include #endif #include #include "lib.h" #include "dev.h" #include "ap.h" #include "ssv_skb.h" #include int ssv6200_bcast_queue_len(struct ssv6xxx_bcast_txq *bcast_txq); void ssv6x5x_beacon_enable(struct ssv_hw *sh, bool bEnable) { struct ssv_softc *sc = sh->sc; struct sk_buff *skb; struct cfg_host_cmd *host_cmd; int retval = 0; struct ssv6xxx_sw_beacon_params *ptr; skb = ssv_skb_alloc(sh->sc, HOST_CMD_HDR_LEN + sizeof(struct ssv6xxx_sw_beacon_params)); if (skb == NULL) { printk("%s:_skb_alloc fail!!!\n", __func__); return; } skb_put(skb, HOST_CMD_HDR_LEN + sizeof(struct ssv6xxx_sw_beacon_params)); host_cmd = (struct cfg_host_cmd *)skb->data; memset(host_cmd, 0x0, HOST_CMD_HDR_LEN + sizeof(struct ssv6xxx_sw_beacon_params)); host_cmd->c_type = HOST_CMD; host_cmd->sub_h_cmd = (bEnable ? SSV6XXX_SOFT_BEACON_START : SSV6XXX_SOFT_BEACON_STOP); host_cmd->h_cmd = (u8)SSV6XXX_HOST_CMD_SOFT_BEACON; host_cmd->len = skb->len; ptr = (struct ssv6xxx_sw_beacon_params *)host_cmd->un.dat8; if (false == bEnable) { // only use beacon id 0 ptr->beacon_id_maps |= 0x1; //ptr->beacon_id_maps |= 0x2; } if(sh->cfg.ap_force_tim_always_high==1) { ptr->force_tim_to_high=1; } retval = HCI_SEND_CMD(sh, skb); if (retval) { printk("%s(): Fail to send soft beacon cmd\n", __FUNCTION__); } // fw will release pbuf, host just clear related beacon information if (false == bEnable) { if (sc->beacon_buf) { dev_kfree_skb_any(sc->beacon_buf); sc->beacon_buf = NULL; } } return; } void ssv6x5x_beacon_set_interval(struct ssv_hw *sh, u16 beacon_interval, u8 dtim_cnt) { struct sk_buff *skb; struct cfg_host_cmd *host_cmd; int retval = 0; struct ssv6xxx_sw_beacon_params *ptr; if (beacon_interval==0) beacon_interval = 100; skb = ssv_skb_alloc(sh, HOST_CMD_HDR_LEN + sizeof(struct ssv6xxx_sw_beacon_params)); if (skb == NULL) { printk("%s:_skb_alloc fail!!!\n", __func__); return; } skb_put(skb, HOST_CMD_HDR_LEN + sizeof(struct ssv6xxx_sw_beacon_params)); host_cmd = (struct cfg_host_cmd *)skb->data; memset(host_cmd, 0x0, HOST_CMD_HDR_LEN + sizeof(struct ssv6xxx_sw_beacon_params)); host_cmd->c_type = HOST_CMD; host_cmd->sub_h_cmd = SSV6XXX_SOFT_BEACON_SET_INTERVAL; host_cmd->h_cmd = (u8)SSV6XXX_HOST_CMD_SOFT_BEACON; host_cmd->len = skb->len; ptr = (struct ssv6xxx_sw_beacon_params *)host_cmd->un.dat8; ptr->interval = beacon_interval; ptr->dtim_cnt = dtim_cnt; retval = HCI_SEND_CMD(sh, skb); if (retval) { printk("%s(): Fail to send soft beacon cmd\n", __FUNCTION__); } } void ssv6xxx_beacon_set(struct ssv_softc *sc, struct sk_buff *beacon_skb, int dtim_offset) { struct sk_buff *skb; struct cfg_host_cmd *host_cmd; int retval = 0; struct ssv6xxx_sw_beacon_params *ptr; skb = ssv_skb_alloc_ex(sc->sh, HOST_CMD_HDR_LEN + sizeof(struct ssv6xxx_sw_beacon_params) + beacon_skb->len, GFP_ATOMIC); if (skb == NULL) { printk("%s:_skb_alloc fail!!!\n", __func__); return; } skb_put(skb, HOST_CMD_HDR_LEN + sizeof(struct ssv6xxx_sw_beacon_params) + beacon_skb->len); host_cmd = (struct cfg_host_cmd *)skb->data; memset(host_cmd, 0x0, HOST_CMD_HDR_LEN + sizeof(struct ssv6xxx_sw_beacon_params) + beacon_skb->len); host_cmd->c_type = HOST_CMD; host_cmd->sub_h_cmd = SSV6XXX_SOFT_BEACON_FILL_CONTEXT; host_cmd->h_cmd = (u8)SSV6XXX_HOST_CMD_SOFT_BEACON; host_cmd->len = skb->len; ptr = (struct ssv6xxx_sw_beacon_params *)host_cmd->un.dat8; ptr->dtim_offset = dtim_offset; ptr->dtim_cnt = sc->beacon_dtim_cnt; ptr->interval = (0 == sc->beacon_interval) ? 100 : sc->beacon_interval; ptr->beacon_len = beacon_skb->len; memcpy((u8 *)ptr->beacon_context, (u8 *)beacon_skb->data, beacon_skb->len); retval = HCI_SEND_CMD(sc->sh, skb); if (retval) { printk("%s(): Fail to send soft beacon cmd\n", __FUNCTION__); } } //need to stop beacon firstly. void ssv6xxx_beacon_release(struct ssv_softc *sc) { printk("[A] ssv6xxx_beacon_release Enter\n"); ssv6x5x_beacon_enable(sc->sh, false); } void ssv6xxx_beacon_change(struct ssv_softc *sc, struct ieee80211_hw *hw, struct ieee80211_vif *vif, bool aid0_bit_set) { //struct ssv_hw *sh = sc->sh; struct sk_buff *skb; struct sk_buff *old_skb = NULL; u16 tim_offset, tim_length; // printk("[A] ssv6xxx_beacon_change\n"); 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) { /* Ignore DTIM count from mac80211: * firmware handles DTIM internally. */ skb->data[tim_offset + 2] = 0; // for sw beacon, set dtim period = 1 if (!(sc->sh->cfg.hw_caps & SSV6200_HW_CAP_BEACON)) skb->data[tim_offset + 3] = 1; /* Set/reset aid0 bit */ if (aid0_bit_set) skb->data[tim_offset + 4] |= 1; else skb->data[tim_offset + 4] &= ~1; } dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_BEACON, "[A] beacon len [%d] tim_offset[%d]\n", skb->len, tim_offset); SSV_FILL_BEACON_TX_DESC(sc, skb); dbgprint(&sc->cmd_data, sc->log_ctrl, LOG_BEACON, "[A] beacon len [%d] tim_offset[%d]\n", skb->len, tim_offset); if(sc->beacon_buf) { //struct ssv6200_tx_desc *tx_desc = (struct ssv6200_tx_desc *)sc->beacon_buf->data; /* * Compare if content is the same ( not include fcs) */ if(memcmp(sc->beacon_buf->data, skb->data, (skb->len-FCS_LEN)) == 0){ /* no need set new beacon to register*/ old_skb = skb; break; } else{ old_skb = sc->beacon_buf; sc->beacon_buf = skb; } } else{ sc->beacon_buf = skb; } //for debug // { // int i; // u8 *ptr = &skb->data[tim_offset]; // printk("=================DTIM===================\n"); // for(i=0;ibeacon_dtim_cnt = vif->bss_conf.dtim_period-1; ssv6xxx_beacon_set(sc, skb, tim_offset); }while(0); if (old_skb) dev_kfree_skb_any(old_skb); } void ssv6200_set_tim_work(struct ssv_softc *sc) { #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); /* Release oldest frame. */ 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 --; //dev_kfree_skb_any(old_skb); 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 --; //dev_kfree_skb_any(skb); ssv6xxx_txbuf_free_skb(skb, (void*)sc); } spin_unlock_irqrestore(&bcast_txq->txq_lock, flags); } static int queue_block_cnt = 0; /* If buffer any mcast frame, send it. */ //void ssv6200_bcast_timer(unsigned long arg) void ssv6200_bcast_tx_work(struct work_struct *work) { struct ssv_softc *sc = container_of(work, struct ssv_softc, bcast_tx_work.work); // struct ssv_softc *sc = (struct ssv_softc *)arg; #if 1 struct sk_buff* skb; int i; u8 remain_size; #endif unsigned long flags; bool needtimer = true; long tmo = sc->bcast_interval; //Trigger after DTIM 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, 8), ssv6200_bcast_queue_len(&sc->bcast_txq)); #endif //HCI_PAUSE_HWSWQ(sc->sh, (TXQ_MGMT)); /* if there is any frame in low layer, stop sending at this time */ if(sc->hw_mng_used != 0 || false == HCI_TXQ_EMPTY(sc->sh, 8)){ #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, 8)); #endif queue_block_cnt++; /* does hw queue have problem??? flush bcast queue to prevent tx_work drop in an inifate loop*/ 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;ibcast_txq, &remain_size); if(!skb){ needtimer = false; break; } if( (0 != remain_size) && (SSV6200_ID_MANAGER_QUEUE-1) != i ){ /* tell station there are more broadcast frame sending... */ 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); //tx_desc->hdr_offset = TXPB_OFFSET for always hdr = (struct ieee80211_hdr *) ((u8*)skb->data+TXPB_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, SSV_SW_TXQ_ID_MNG, false)<0){ printk("bcast_timer send fail!!!!!!! \n"); //dev_kfree_skb_any(skb); 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); //mod_timer(&sc->bcast_timeout, jiffies + 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 } /** *1. Update DTIM *2. Send Broadcast frame after DTIM */ 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 /* Every DTIM launch timer to send b-frames*/ sc->bcast_interval = (sc->beacon_dtim_cnt+1) * (sc->beacon_interval + 20) * HZ / 1000; //Trigger after DTIM; if (!sc->aid0_bit_set) { sc->aid0_bit_set = true; /* 1. Update DTIM */ ssv6xxx_beacon_change(sc, sc->hw, sc->ap_vif, sc->aid0_bit_set); /* 2. Send Broadcast frame after DTIM */ //mod_timer(&sc->bcast_timeout, jiffies + sc->bcast_interval); 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 } } /** *1. Update DTIM. *2. Remove timer to send beacon. */ 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;//10ms #ifdef BCAST_DEBUG printk("[B] ssv6200_bcast_stop_work\n"); #endif /* expired every 10ms*/ //sc->bcast_interval = HZ / 10; if (sc->aid0_bit_set) { if(0== ssv6200_bcast_queue_len(&sc->bcast_txq)){ /* 1. Remove timer to send beacon. */ // del_timer_sync(&sc->bcast_timeout); cancel_delayed_work_sync(&sc->bcast_tx_work); sc->aid0_bit_set = false; /* 2. Update DTIM. */ 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 //mod_timer(&sc->bcast_timeout, jiffies + sc->bcast_interval); queue_delayed_work(sc->config_wq, &sc->bcast_tx_work, tmo); } } } void ssv6200_bcast_stop(struct ssv_softc *sc) { //cancel_work_sync(&sc->bcast_start_work); queue_delayed_work(sc->config_wq, &sc->bcast_stop_work, sc->beacon_interval*HZ/1024); } void ssv6200_bcast_start(struct ssv_softc *sc) { //cancel_delayed_work_sync(&sc->bcast_stop_work); 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; //?? no need do this ?? spin_lock_irqsave(&sc->ps_state_lock, flags); /*clear asleep mask to deny new frame insert*/ 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); } //Beacon related end //----------------------------------------------------------------------------