예제 #1
0
/*
 * Temporarily added to support older WMI events. We should move all events to unified
 * when the target is ready to support it.
 */
void
wmi_control_rx(void *ctx, HTC_PACKET *htc_packet)
{
    u_int16_t id;
    u_int8_t *data;
    u_int32_t len;
    int status = EOK;
    wmi_buf_t evt_buf;
    struct wmi_unified *wmi_handle = (struct wmi_unified *) ctx;
 
    evt_buf =  (wmi_buf_t)  htc_packet->pPktContext;

    /** 
     * This is  a HACK due to a Hack/WAR in HTC !!.
     * the head of the wbuf still contains the HTC header
     * but the length excludes htc header.  
     */
    wbuf_set_pktlen(evt_buf, htc_packet->ActualLength +  HTC_HEADER_LEN);
    wbuf_pull(evt_buf, HTC_HEADER_LEN);

    id = WMI_GET_FIELD(wbuf_header(evt_buf), WMI_CMD_HDR, COMMANDID);

    if ((id >= WMI_START_EVENTID) && (id <= WMI_END_EVENTID)) {
        status = wmi_unified_event_rx(wmi_handle, evt_buf);
        return ;
    }

    if (wbuf_pull(evt_buf, sizeof(WMI_CMD_HDR)) == NULL) {
        status = -1;
        goto end;
    }

    data = wbuf_header(evt_buf);
    len = wbuf_get_pktlen(evt_buf);

    switch(id)
    {
        default:
            printk("%s: Unhandled WMI command %d\n", __func__, id);
            break;
        case WMI_SERVICE_READY_EVENTID:
            status = wmi_service_ready_event_rx(wmi_handle, data, len);
            break;
        case WMI_READY_EVENTID:
            status = wmi_ready_event_rx(wmi_handle, data, len);
            break;
        case WMI_WLAN_VERSION_EVENTID:
            printk("%s: Handle WMI_VERSION_EVENTID\n", __func__);
            break;
        case WMI_REGDOMAIN_EVENTID:
            printk("%s: Handle WMI_REGDOMAIN_EVENTID\n", __func__);
            break;
        case WMI_DEBUG_MESG_EVENTID:
            dbglog_message_handler(wmi_handle->scn_handle, evt_buf);
            return;
    }

end:
    wbuf_free(evt_buf);
}
예제 #2
0
/* WMI command API */
int
wmi_unified_cmd_send(wmi_unified_t wmi_handle, wmi_buf_t buf, int len, WMI_CMD_ID cmd_id)
{
    A_STATUS status;
    struct cookie *cookie; 

    if (wbuf_push(buf, sizeof(WMI_CMD_HDR)) == NULL) {
        return -ENOMEM;
    }

    WMI_SET_FIELD(wbuf_header(buf), WMI_CMD_HDR, COMMANDID, cmd_id);
    //WMI_CMD_HDR_SET_DEVID(cmd_hdr, 0); // unused

    cookie = ol_alloc_cookie(wmi_handle);
    if (!cookie) {
        return -ENOMEM;
    }

    cookie->PacketContext = buf;
    SET_HTC_PACKET_INFO_TX(&cookie->HtcPkt,
                           cookie,
                           wbuf_header(buf),
                           len+sizeof(WMI_CMD_HDR),
                           /* htt_host_data_dl_len(buf)+20 */
                           wmi_handle->wmi_endpoint_id,
                           0/*htc_tag*/);

    SET_HTC_PACKET_NET_BUF_CONTEXT(&cookie->HtcPkt, buf);


    status = HTCSendPkt(wmi_handle->htc_handle, &cookie->HtcPkt); 

    return ((status == A_OK) ? EOK : -1);
}
예제 #3
0
wmi_buf_t
wmi_buf_alloc(wmi_unified_t wmi_handle, int len)
{
    wmi_buf_t wmi_buf;
    /* NOTE: For now the wbuf type is used as WBUF_TX_CTL
     * But this need to be changed appropriately to reserve
     * proper headroom for wmi_buffers
     */
    wmi_buf = wbuf_alloc(wmi_handle->osdev, WBUF_TX_CTL, len);
    if( NULL == wmi_buf )
    {
        /* wbuf_alloc returns NULL if the internel pool in wmi_handle->osdev
         * is empty
         */
        return NULL;
    }

    
    /* Clear the wmi buffer */
    OS_MEMZERO(wbuf_header(wmi_buf), len);
	
    /*
     * Set the length of the buffer to match the allocation size.
     */
    wbuf_set_pktlen(wmi_buf, len);
    return wmi_buf;
}
예제 #4
0
/* Debug function to dump the status of the Queue
 * This function will be used handy to debug the 
 * buffer corruptions.
 * Prints Next, Previous and Last buffers of first
 * buffer of every frm in the Q. Also it prints all 
 * the control words of first desc of every frame and
 * status words of last desc of every frame.
 */
void
dumpTxQueue(struct ath_softc *sc, ath_bufhead *bfhead) 
{
    struct ath_buf *bf = TAILQ_FIRST(bfhead);
    struct ath_desc *ds, *lastds;
    struct ieee80211_frame      *wh;
  
    if (!bf)
        printk("EMPTY QUEUE\n");
 
    while (bf) {
       ds = bf->bf_desc;
       if (bf->bf_lastfrm) {
           wh = (struct ieee80211_frame *)wbuf_header(bf->bf_mpdu);
           lastds = bf->bf_lastfrm->bf_desc;
           printk("\nSWRInfo: seqno %d isswRetry %d retryCnt %d\n",wh ? (*(u_int16_t *)&wh->i_seq[0]) >> 4 : 0, bf->bf_isswretry,bf->bf_swretries);

          
       printk("Buffer #%p --> Next#%p Prev#%p Last#%p\n",bf, TAILQ_NEXT(bf,bf_list), 
                TAILQ_PREV(bf, ath_bufhead_s, bf_list), bf->bf_lastfrm);
       printk("                     Stas#%08X flag#%08X Node#%p\n", bf->bf_status, bf->bf_flags, bf->bf_node);

       printk("Descr  #%08X --> Next#%08X Data#%08X Ctl0#%08X Ctl1#%08X\n", bf->bf_daddr, ds->ds_link, ds->ds_data, ds->ds_ctl0, ds->ds_ctl1);
       printk("                     Ctl2#%08X Ctl3#%08X Sta0#%08X Sta1#%08X\n",ds->ds_hw[0], ds->ds_hw[1], lastds->ds_hw[2], lastds->ds_hw[3]);
       }       
       bf = TAILQ_NEXT(bf,bf_list);
    }                           
예제 #5
0
파일: ath_ald.c 프로젝트: KHATEEBNSIT/AP
void ath_ald_update_frame_stats(struct ath_softc *sc, struct ath_buf *bf, struct ath_tx_status *ts)
{
    struct ieee80211_frame *wh;
    int type;
    int bfUsed = 0;
    u_int32_t ptime = 0;
    u_int32_t airtime = 0;
    int i;
    void *ds = bf->bf_lastbf->bf_desc;

    wh = (struct ieee80211_frame *)wbuf_header(bf->bf_mpdu);
    type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
    if((type == IEEE80211_FC0_TYPE_DATA) &&
        (!IEEE80211_IS_MULTICAST(wh->i_addr1)) &&
        (!IEEE80211_IS_BROADCAST(wh->i_addr1))){
        for(i=0; i < HAL_NUM_TX_QUEUES; i++) {
            if (ATH_TXQ_SETUP(sc, i)) {
                bfUsed += sc->sc_txq[i].axq_num_buf_used;
            }
        }
        ptime = sc->sc_ald.sc_ald_txairtime;
        airtime = ath_hal_txcalcairtime(sc->sc_ah, ds, ts, AH_FALSE, 1, 1);
        sc->sc_ald.sc_ald_txairtime += airtime;
        if (ptime > sc->sc_ald.sc_ald_txairtime) {
            sc->sc_ald.sc_ald_txairtime = airtime;
            sc->sc_ald.sc_ald_pktnum = 0;
        }
        if (airtime) {
            sc->sc_ald.sc_ald_pktlen += (bf->bf_isaggr ? bf->bf_al : bf->bf_frmlen);
            sc->sc_ald.sc_ald_pktnum += bf->bf_nframes;
            sc->sc_ald.sc_ald_bfused += bfUsed;
            sc->sc_ald.sc_ald_counter += 1;
        }
    }
}
예제 #6
0
void
wep_dump(struct ieee80211_key *k, wbuf_t wbuf, int hdrlen)
{
	struct ieee80211_frame *wh;
    	struct wep_ctx *ctx = k->wk_private;
    	struct ieee80211vap *vap = ctx->wc_vap;
	unsigned int iv,idx,icv;
	unsigned char *ptr;
	unsigned char kbuf[64];


	wh = (struct ieee80211_frame *)wbuf_header(wbuf);
	ptr = (unsigned char*)wh;       

	idx = iv = 0;
	memcpy(&iv, ptr+hdrlen, 3);
	idx = ptr[hdrlen+3];
	memcpy(&icv, ptr+wbuf_get_pktlen(wbuf)-4, 4);

	memcpy(kbuf, k->wk_key, k->wk_keylen);
 
	IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr1,
            "%s and seq=%02x-%02x\n", "addr1", wh->i_seq[0], wh->i_seq[1]);
	IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr3,
            "%s", "addr3");
	IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
            "IV=%08x idx=%d ICV=%08x, hdrlen=%d",iv, idx&0xff, icv, hdrlen);

	printk("key dump:len=%d\n", k->wk_keylen);
	dump_hex_buf(kbuf, (int)k->wk_keylen);
	printk("packet dump:pktlen=%d,len=%d\n", wbuf_get_pktlen(wbuf), wbuf_get_len(wbuf));
	dump_hex_buf((uint8_t*)ptr/*wbuf_raw_data(wbuf)*/, (int)wbuf_get_pktlen(wbuf));
}
예제 #7
0
int ieee80211_crypto_handle_keymiss(struct ieee80211com *ic,
                                    wbuf_t wbuf,
                                    struct ieee80211_rx_status *rs)
{
    struct ieee80211_node *ni = NULL;

    /*   
     * Handle packets with keycache miss
     */
    if ((rs->rs_flags & IEEE80211_RX_KEYMISS)) {
        ni = ieee80211_find_rxnode(ic,
                (const struct ieee80211_frame_min *) wbuf_header(wbuf));

        if( ni != NULL) {
            struct ieee80211vap *vap = ni->ni_vap;

            ieee80211_key_update_begin(vap);
            if (!ieee80211_crypto_keymiss(ni, wbuf, rs)) {
	            IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO, "%s: Couldn't decrypt, dropping packet.\n",
                                             __func__);
                wbuf_free(wbuf);
                ieee80211_key_update_end(vap);
                ieee80211_free_node(ni); 	
                return 1;
            }else {
                ieee80211_key_update_end(vap);
                ieee80211_free_node(ni);
            }
        }
    }
	return 0;
}
예제 #8
0
int32_t ieee80211_aow_ctrl_cmd_handler(struct ieee80211com *ic, wbuf_t wbuf,
                                       struct ieee80211_rx_status *rs)
{
    struct audio_pkt *apkt;
    struct ether_header *eh;
    u_int8_t* data;
    u_int32_t datalen;

    eh = (struct ether_header*)wbuf_header(wbuf);

    apkt = GET_AOW_PKT(wbuf);

    if (apkt->signature != ATH_AOW_SIGNATURE) {
        return 0;
    }        

    if (apkt->pkt_type != AOW_CTRL_PKT) {
        return 0;
    }        
    
    ic->ic_aow.rx_ctrl_framecount++;

    data = GET_AOW_DATA(wbuf);
    datalen = GET_AOW_DATA_LEN(wbuf);
            
    if (!is_aow_usb_calls_registered()) {
        return 0;
    }

    ieee80211_aow_send_to_host(ic, data, datalen, AOW_HOST_PKT_DATA, 0, eh->ether_shost);
 
    return 0;
}
예제 #9
0
/* TODO: only support linux for now */
void wlan_offchan_send_data_frame(struct ieee80211_node *ni, struct net_device *netdev)
{
#if defined(LINUX) || defined(__linux__)
    struct ieee80211vap *vap = ni->ni_vap;
    struct ieee80211com *ic = ni->ni_ic;
    wbuf_t wbuf;
    struct ieee80211_qosframe *qwh;
    const u_int8_t dst[6] = {0x00, 0x02, 0x03, 0x04, 0x05, 0x06};
    struct sk_buff *skb;

    wbuf = wbuf_alloc(ic->ic_osdev, WBUF_TX_DATA, 1000);
    if (wbuf == NULL)
    {
        return ;
    }
    ieee80211_prepare_qosnulldata(ni, wbuf, WME_AC_VO);

    qwh = (struct ieee80211_qosframe *)wbuf_header(wbuf);
    ieee80211_send_setup(vap, ni, (struct ieee80211_frame *)qwh,
        IEEE80211_FC0_TYPE_DATA,
        vap->iv_myaddr, /* SA */
        dst,            /* DA */
        ni->ni_bssid);

    wbuf_set_pktlen(wbuf, 1000);
    /* force with NONPAUSE_TID */
    wbuf_set_tid(wbuf, OFFCHAN_EXT_TID_NONPAUSE);

    skb = (struct sk_buff *)wbuf;
    skb->dev = netdev;

    dev_queue_xmit(skb);
#endif
}
예제 #10
0
int
wlan_frm_haswscie(const wbuf_t wbuf)
{
    const u_int8_t *frm = ((u_int8_t *)wbuf_header(wbuf) + sizeof(struct ieee80211_frame));
    const u_int8_t *efrm = ((u_int8_t *)wbuf_header(wbuf) + wbuf_get_pktlen(wbuf));
    while (frm < efrm) {
        switch (*frm) {
            case IEEE80211_ELEMID_VENDOR:
                if (iswpsoui((u_int8_t *)frm))
                    return 1;
                break;
                default:
                        break;
        }
        frm += frm[1] + 2;
    }
    return 0;
}
예제 #11
0
static int wmi_unified_event_rx(struct wmi_unified *wmi_handle, wmi_buf_t evt_buf)
{
    u_int16_t id;
    u_int8_t *event;
    u_int16_t len;
    int status = -1;
    u_int16_t handler_id;

    ASSERT(evt_buf != NULL);

    id = WMI_GET_FIELD(wbuf_header(evt_buf), WMI_CMD_HDR, COMMANDID);
    handler_id = (id - WMI_START_EVENTID);

    if (wbuf_pull(evt_buf, sizeof(WMI_CMD_HDR)) == NULL) {
        //A_DPRINTF(DBG_WMI, (DBGFMT "bad packet 1\n", DBGARG));
        //wmip->wmi_stats.cmd_len_err++;
        goto end;
    }
    if (handler_id >= WMI_UNIFIED_MAX_EVENT) {
        printk("%s : unkown event id : 0x%x  event handler id 0x%x \n", 
                __func__, id, handler_id);
        goto end;
    }
   
    event = wbuf_header(evt_buf);
    len = wbuf_get_pktlen(evt_buf);


    if (!wmi_handle->event_handler[handler_id]) {
        printk("%s : no registered event handler : event id 0x%x \n", 
                __func__, id);
        goto end;
    }


    /* Call the WMI registered event handler */
    status = wmi_handle->event_handler[handler_id](wmi_handle->scn_handle, event, len,
                                           wmi_handle->event_handler_cookie[handler_id]);

end:
    wbuf_free(evt_buf);
    return status;
}
예제 #12
0
/*zhaoyang1 add start for interfere ap*/
wbuf_t
autelan_beacon_alloc(struct ieee80211_node *ni,
                       struct ieee80211_beacon_offsets *bo, u_int8_t *ap_mac, u_int8_t chan)
{
    struct ieee80211vap *vap = ni->ni_vap;
    struct ieee80211com *ic = ni->ni_ic;
    wbuf_t wbuf;
    struct ieee80211_frame *wh;
    u_int8_t *frm;
	
    wbuf = wbuf_alloc(ic->ic_osdev, WBUF_TX_BEACON, MAX_TX_RX_PACKET_SIZE);
    if (wbuf == NULL)
        return NULL;

    wh = (struct ieee80211_frame *)wbuf_header(wbuf);
    wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
        IEEE80211_FC0_SUBTYPE_BEACON;
    wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
    *(u_int16_t *)wh->i_dur = 0;
    IEEE80211_ADDR_COPY(wh->i_addr1, IEEE80211_GET_BCAST_ADDR(ic));
    IEEE80211_ADDR_COPY(wh->i_addr2, ap_mac);
    IEEE80211_ADDR_COPY(wh->i_addr3, ap_mac);
    *(u_int16_t *)wh->i_seq = 0;

    frm = (u_int8_t *)&wh[1];
    frm = autelan_beacon_init(ni, bo, frm, chan);
    if (ieee80211_vap_copy_beacon_is_set(vap)) {
        store_beacon_frame(vap, (u_int8_t *)wh, (frm - (u_int8_t *)wh));
    }

    wbuf_set_pktlen(wbuf, (frm - (u_int8_t *) wbuf_header(wbuf)));
    wbuf_set_node(wbuf, ieee80211_ref_node(ni));

    IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MLME, vap->iv_myaddr,
                       "%s \n", __func__);
    return wbuf;
}
예제 #13
0
파일: if_ath_htc.c 프로젝트: jorneytu/wlan
/*
 * callback hadlers: Action frames TX Complete.
 */
void
ath_action_tx_event(void *Context, int8_t tx_status)
{
    struct ath_softc_net80211 *scn = (struct ath_softc_net80211 *)Context;
    struct ath_usb_p2p_action_queue *p2p_action_wbuf;

    IEEE80211_STATE_P2P_ACTION_LOCK_IRQ(scn);
    if (scn->sc_p2p_action_queue_head) {
        ieee80211_vap_complete_buf_handler handler;
        void *arg;
        wbuf_t wbuf;

        p2p_action_wbuf = scn->sc_p2p_action_queue_head;
        if (scn->sc_p2p_action_queue_head == scn->sc_p2p_action_queue_tail) {
            scn->sc_p2p_action_queue_head = scn->sc_p2p_action_queue_tail = NULL;
        } else {
            scn->sc_p2p_action_queue_head = scn->sc_p2p_action_queue_head->next;
        }

        wbuf = p2p_action_wbuf->wbuf;
        if (!p2p_action_wbuf->deleted) {
            wbuf_get_complete_handler(wbuf,(void **)&handler, &arg);
            if (handler) {
                struct ieee80211_node *ni = wbuf_get_node(wbuf);
                struct ieee80211_frame *wh = (struct ieee80211_frame *)wbuf_header(wbuf);
                wlan_if_t vap = ni->ni_vap;
                struct ieee80211_tx_status ts;

                ts.ts_flags = tx_status;
                ts.ts_retries = 0;
                handler(vap, wbuf, arg, wh->i_addr1, wh->i_addr2, wh->i_addr3, &ts);
            }
        } else {
#ifndef MAGPIE_HIF_GMAC 
            printk("### action frame %p marked as deleted\n", wbuf);
#endif        
        }
        wbuf_release(scn->sc_osdev, p2p_action_wbuf->wbuf);
        OS_FREE(p2p_action_wbuf);

        //printk("### %s (%d) : Action TX EVENT DONE...\n", __FUNCTION__, __LINE__);
    }
    IEEE80211_STATE_P2P_ACTION_UNLOCK_IRQ(scn);
}
예제 #14
0
파일: if_ath_htc.c 프로젝트: jorneytu/wlan
void
host_htc_eptx_comp(void *context, wbuf_t skb, HTC_ENDPOINT_ID epid)
{

	/* for HTC, since we don't have the "real" tx-status, we do them here. */
    struct ath_softc_net80211 	*scn = (struct ath_softc_net80211 *)context;
	ath_dev_t  				 	sc_dev = scn->sc_dev;
	struct ath_softc 			*sc = ATH_DEV_TO_SC(sc_dev);
	u_int16_t 					min_len_req_by_stats = sizeof(ath_data_hdr_t)+sizeof(struct ieee80211_frame);

	if( wbuf_get_pktlen(skb)> min_len_req_by_stats &&
		wbuf_get_tid(skb) < ATH_HTC_WME_NUM_TID &&(
		epid == sc->sc_data_BE_ep ||
		epid == sc->sc_data_BK_ep ||
		epid == sc->sc_data_VI_ep ||
		epid == sc->sc_data_VO_ep	)){
		
		struct ieee80211_node 		*ni = wbuf_get_node(skb);
		struct ieee80211vap 		*vap = ni->ni_vap;
		struct ieee80211_frame 		*wh;
		struct ieee80211_tx_status 	ts;
		struct ieee80211_mac_stats 	*mac_stats;
		u_int8_t 					type, subtype;
		u_int8_t 					is_mcast;
				
		wh = (struct ieee80211_frame *)(wbuf_header(skb)+sizeof(ath_data_hdr_t));
		type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
		subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
		/* we fake ts becuase we don't have ts in HTC tx */
		ts.ts_flags = 0;                                    
    	ts.ts_retries = 0;  

		ieee80211_update_stats(vap, skb, wh, type, subtype, &ts);
		
		/* patch: HTC tx only ath_data_hdr length */
		is_mcast = IEEE80211_IS_MULTICAST(wh->i_addr1) ? 1 : 0;
		mac_stats = is_mcast ? &vap->iv_multicast_stats : &vap->iv_unicast_stats;
		mac_stats->ims_tx_bytes -= sizeof(ath_data_hdr_t);

 	}
	
    ieee80211_free_node(wbuf_get_node(skb));
}
예제 #15
0
static void sm_reassoc_complete(os_handle_t osif, IEEE80211_STATUS status, u_int16_t aid, wbuf_t wbuf)
{
    wlan_btamp_conn_sm_t      sm;
    struct ieee80211_frame    *wh = NULL;
    
    if (wbuf)
        wh = (struct ieee80211_frame *)wbuf_header(wbuf);

    sm = ieee80211_btamp_get_smhandle((wlan_btamp_conn_sm_t *)osif, wh->i_addr2,
                                      IEEE80211_BTAMP_CONN_STATE_ASSOC); 
    if (!sm || sm->is_stop_requested) {
        return;
    }

    if (status == IEEE80211_STATUS_SUCCESS) {
        ieee80211_sm_dispatch(sm->hsm_handle,IEEE80211_BTAMP_CONN_EVENT_ASSOC_SUCCESS,0,NULL);
    } else {
        ieee80211_sm_dispatch(sm->hsm_handle,IEEE80211_BTAMP_CONN_EVENT_ASSOC_FAIL,0,NULL);
    }
}
예제 #16
0
/*
 * This function is called when we have successsfully transmitted EOSP.
 * It clears the SP flag so that we are ready to accept more triggers
 * from this node.
 */
void
ath_net80211_uapsd_eospindicate(ieee80211_node_t node, wbuf_t wbuf, int txok)
{
    struct ieee80211_qosframe *qwh;
    struct ieee80211_node *ni;

    qwh = (struct ieee80211_qosframe *)wbuf_header(wbuf);
    ni = (struct ieee80211_node *)node;

    if ((qwh->i_fc[0] == (IEEE80211_FC0_SUBTYPE_QOS|IEEE80211_FC0_TYPE_DATA)) ||
        (qwh->i_fc[0] == (IEEE80211_FC0_SUBTYPE_QOS_NULL|IEEE80211_FC0_TYPE_DATA)))
    {
        if (qwh->i_qos[0] & IEEE80211_QOS_EOSP) {
            ni->ni_flags &= ~IEEE80211_NODE_UAPSD_SP;
            if (!txok)
                ni->ni_stats.ns_tx_eosplost++;
        }
    }

    return;
}
예제 #17
0
/*
 * This function is called when we have successsfully transmitted EOSP.
 * It clears the SP flag so that we are ready to accept more triggers
 * from this node.
 */
void
ath_net80211_uapsd_eospindicate(ieee80211_node_t node, wbuf_t wbuf, int txok, int force_eosp)
{
    struct ieee80211_qosframe *qwh;
    struct ieee80211_node *ni;
    struct ath_softc_net80211 *scn;

    if (node == NULL)
        return;
    qwh = (struct ieee80211_qosframe *)wbuf_header(wbuf);
    ni = (struct ieee80211_node *)node;
    scn = ATH_SOFTC_NET80211(ni->ni_ic);

    if (node == NULL)
        return;

    if ((qwh->i_fc[0] == (IEEE80211_FC0_SUBTYPE_QOS|IEEE80211_FC0_TYPE_DATA)) ||
        (qwh->i_fc[0] == (IEEE80211_FC0_SUBTYPE_QOS_NULL|IEEE80211_FC0_TYPE_DATA)) ||
        force_eosp)
    {
        if (
#if ATH_SUPPORT_WIFIPOS
            (ni->ni_flags & IEEE80211_NODE_WAKEUP) ||
#endif      
            (qwh->i_qos[0] & IEEE80211_QOS_EOSP) || force_eosp) {
            struct ieee80211com         *ic     = ni->ni_ic;
            struct ath_softc_net80211   *scn    = ATH_SOFTC_NET80211(ic);
            ni->ni_flags &= ~IEEE80211_NODE_UAPSD_SP;
            DPRINTF(scn, ATH_DEBUG_UAPSD, "%s : End SP\n", __func__);
            if (!txok)
                ni->ni_stats.ns_tx_eosplost++;
        }
    }
    if ((qwh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_MGT) {
        /* clear the SP for node */
        ni->ni_flags &= ~IEEE80211_NODE_UAPSD_SP;
    }

    return;
}
예제 #18
0
void ieee80211_recv_beacon_ibss(struct ieee80211_node *ni, wbuf_t wbuf, int subtype, 
                                struct ieee80211_rx_status *rs, ieee80211_scan_entry_t  scan_entry)
{
    struct ieee80211vap                          *vap = ni->ni_vap;
    struct ieee80211com                          *ic = ni->ni_ic;
    u_int16_t                                    capinfo;
    int                                          ibssmerge = 0;
    struct ieee80211_channelswitch_ie            *chanie = NULL;
    struct ieee80211_extendedchannelswitch_ie    *echanie = NULL;
    struct ieee80211_frame                       *wh;
    u_int64_t                                    tsf;
    u_int8_t                                     *quiet_elm = NULL; 
    bool                                         free_node = FALSE;
#if ATH_SUPPORT_IBSS_DFS
    struct ieee80211_ibssdfs_ie                  *ibssdfsie = NULL;
#endif /* ATH_SUPPORT_IBSS_DFS */

    wh = (struct ieee80211_frame *)wbuf_header(wbuf);
    capinfo = ieee80211_scan_entry_capinfo(scan_entry);

    if (!(capinfo & IEEE80211_CAPINFO_IBSS))
        return;

    OS_MEMCPY((u_int8_t *)&tsf, ieee80211_scan_entry_tsf(scan_entry), sizeof(tsf));
    if (ni != ni->ni_bss_node) {
        OS_MEMCPY(ni->ni_tstamp.data, &tsf, sizeof(ni->ni_tstamp));
        /* update activity indication */
        ni->ni_beacon_rstamp = OS_GET_TIMESTAMP();
        ni->ni_probe_ticks   = 0;
    }

    /*
     * Check BBSID before updating our beacon configuration to make 
     * sure the received beacon really belongs to our Adhoc network.
     */
    if ((subtype == IEEE80211_FC0_SUBTYPE_BEACON) &&
        ieee80211_vap_ready_is_set(vap) &&
        (IEEE80211_ADDR_EQ(wh->i_addr3, ieee80211_node_get_bssid(vap->iv_bss)))) {
        /*
         * if the neighbor is not in the list, add it to the list.
         */
        if (ni == ni->ni_bss_node) {
            ni = ieee80211_add_neighbor(ni, scan_entry);
            if (ni == NULL) {
                return;
            }
            OS_MEMCPY(ni->ni_tstamp.data, &tsf, sizeof(ni->ni_tstamp));
            free_node = TRUE;
        }
        ni->ni_rssi = rs->rs_rssi;

        /*
         * record tsf of last beacon into bss node
         */
        OS_MEMCPY(ni->ni_bss_node->ni_tstamp.data, &tsf, sizeof(ni->ni_tstamp));

        /*
         * record absolute time of last beacon
         */
        vap->iv_last_beacon_time = OS_GET_TIMESTAMP();

        ic->ic_beacon_update(ni, rs->rs_rssi);

#if ATH_SUPPORT_IBSS_WMM
        /*
         * check for WMM parameters
         */
        if ((ieee80211_scan_entry_wmeparam_ie(scan_entry) != NULL) ||
            (ieee80211_scan_entry_wmeinfo_ie(scan_entry)  != NULL)) {

            /* Node is WMM-capable if WME IE (either subtype) is present */
            ni->ni_ext_caps |= IEEE80211_NODE_C_QOS;

            /* QOS-enable */
            ieee80211node_set_flag(ni, IEEE80211_NODE_QOS);
        } else {
            /* If WME IE not present node is not WMM capable */
            ni->ni_ext_caps &= ~IEEE80211_NODE_C_QOS;
            
            ieee80211node_clear_flag(ni, IEEE80211_NODE_QOS);
        }
#endif  
#if ATH_SUPPORT_IBSS_DFS

        if (ic->ic_curchan->ic_flagext & IEEE80211_CHAN_DFS) {
            //check and see if we need to update other info for ibss_dfsie
            ibssdfsie = (struct ieee80211_ibssdfs_ie *)ieee80211_scan_entry_ibssdfs_ie(scan_entry);
            if(ibssdfsie) {
               if (ieee80211_check_and_update_ibss_dfs(vap, ibssdfsie)) {
                   ieee80211_ibss_beacon_update_start(ic);
               }
            }

            /* update info from DFS owner */
            if(ibssdfsie){
                if(IEEE80211_ADDR_EQ(wh->i_addr2, ibssdfsie->owner)) {
                    if(OS_MEMCMP(ibssdfsie, &vap->iv_ibssdfs_ie_data, MIN_IBSS_DFS_IE_CONTENT_SIZE)) {
                        if(le64_to_cpu(tsf) >= rs->rs_tstamp.tsf){
                            IEEE80211_ADDR_COPY(vap->iv_ibssdfs_ie_data.owner, ibssdfsie->owner);
                            vap->iv_ibssdfs_ie_data.rec_interval = ibssdfsie->rec_interval;
                            ieee80211_ibss_beacon_update_start(ic);
                        }
                    }
                }
            }
            /* check if owner and it is not transmitting ibssIE */
            else if(IEEE80211_ADDR_EQ(wh->i_addr2, vap->iv_ibssdfs_ie_data.owner)){
                IEEE80211_ADDR_COPY(vap->iv_ibssdfs_ie_data.owner, vap->iv_myaddr);
                vap->iv_ibssdfs_state = IEEE80211_IBSSDFS_OWNER;
                ieee80211_ibss_beacon_update_start(ic);            
            } 
        }
   
#endif /* ATH_SUPPORT_IBSS_DFS */

        /*
         * check for spectrum management
         */
        if (capinfo & IEEE80211_CAPINFO_SPECTRUM_MGMT) {
            chanie  = (struct ieee80211_channelswitch_ie *)         ieee80211_scan_entry_csa(scan_entry);
#if ATH_SUPPORT_IBSS_DFS
            if(chanie)
            {
                 OS_MEMCPY(&vap->iv_channelswitch_ie_data, chanie, sizeof(struct ieee80211_channelswitch_ie));
                 ieee80211_ibss_beacon_update_start(ic);
            }
#endif /* ATH_SUPPORT_IBSS_DFS */
            echanie = (struct ieee80211_extendedchannelswitch_ie *) ieee80211_scan_entry_xcsa(scan_entry);
            if ((!chanie) && (!echanie)) {
                quiet_elm = ieee80211_scan_entry_quiet(scan_entry);
            }
        }
    }

    /*
     * Handle ibss merge as needed; check the tsf on the
     * frame before attempting the merge.  The 802.11 spec
     * says the station should change its bssid to match
     * the oldest station with the same ssid, where oldest
     * is determined by the tsf.  Note that hardware
     * reconfiguration happens through callback to
     * ath as the state machine will go from
     * RUN -> RUN when this happens.
     */
    if (ieee80211_vap_ready_is_set(vap) &&
        (le64_to_cpu(tsf) >= rs->rs_tstamp.tsf)) {
        ibssmerge = ieee80211_ibss_merge(ni,scan_entry);
    }

    if (ibssmerge) {
        ieee80211_mlme_adhoc_merge_start(ni);
    }

    /*
     * RNWF-specific: indicate to NDIS about the potential association.
     * It should be done after IBSS merge, which is called from ath_beacon_update().
     */
    if (IEEE80211_ADDR_EQ(wh->i_addr3, ieee80211_node_get_bssid(ni))) {
        /* Indicate node joined IBSS */
    if ((capinfo & IEEE80211_CAPINFO_RADIOMEAS)
         && ieee80211_vap_rrm_is_set(vap)) {
        if(ni->ni_assoc_state != IEEE80211_NODE_ADHOC_STATE_AUTH_ASSOC)
            ieee80211_set_node_rrm(ni,TRUE);
    } else {
        ieee80211_set_node_rrm(ni,FALSE);
    } 
        ieee80211_mlme_adhoc_join_indication(ni, wbuf);

        /* notify mlme of beacon reception */
        ieee80211_mlme_join_complete_adhoc(ni);
    }

    if (ibssmerge) {
        ieee80211_mlme_adhoc_merge_completion(ni);
    }

    if (quiet_elm) {
        ic->ic_set_quiet(ni, quiet_elm);
    }

    if (free_node == TRUE) {
        ieee80211_free_node(ni);
    }

}
예제 #19
0
void mlme_recv_auth_btamp(struct ieee80211_node *ni,
                          u_int16_t algo, u_int16_t seq, u_int16_t status_code,
                          u_int8_t *challenge, u_int8_t challenge_length, wbuf_t wbuf)
{

    struct ieee80211vap           *vap = ni->ni_vap;
    struct ieee80211_mlme_priv    *mlme_priv = vap->iv_mlme_priv;
    struct ieee80211_frame        *wh;
    u_int16_t                     indication_status = IEEE80211_STATUS_SUCCESS,response_status = IEEE80211_STATUS_SUCCESS ;
    bool                          send_auth_response=true, indicate=true;

    wh = (struct ieee80211_frame *) wbuf_header(wbuf);
    /* AP must be up and running */
    if (!mlme_priv->im_connection_up || ieee80211_vap_ready_is_clear(vap)) {
        return;
    }

    IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_AUTH, wh->i_addr2,
                       "recv auth frame with algorithm %d seq %d \n", algo, seq);

    do {

        /* Check node existance for the peer */
        if (ni == vap->iv_bss) {
            return;
        } else {
            ieee80211_ref_node(ni);
        }

        /* Validate algo */
        if (algo == IEEE80211_AUTH_ALG_OPEN) {
            if (mlme_priv->im_expected_auth_seq_number) {
                send_auth_response = false;
                indicate = false;
                if (seq == mlme_priv->im_expected_auth_seq_number) {
                    if (!OS_CANCEL_TIMER(&mlme_priv->im_timeout_timer)) {
                        IEEE80211_DPRINTF(vap, IEEE80211_MSG_MLME, "%s: Timed-out already\n", __func__);
                        break;
                    }

                    IEEE80211_DPRINTF(vap, IEEE80211_MSG_MLME, "%s: mlme_auth_complete\n", __func__);

                    /* Request complete */
                    mlme_priv->im_request_type = MLME_REQ_NONE;

                    /* Authentication complete (success or failure) */
                    IEEE80211_DELIVER_EVENT_MLME_AUTH_COMPLETE(vap, status_code);
                    vap->iv_mlme_priv->im_expected_auth_seq_number = 0;
                } else {
                    break;
                }
            } else {
                if (seq != IEEE80211_AUTH_OPEN_REQUEST) {
                    response_status = IEEE80211_STATUS_SEQUENCE;
                    indication_status = IEEE80211_STATUS_SEQUENCE;
                    break;
                } else {
                    indicate = true;
                    send_auth_response = true;
                }
            }
        } else if (algo == IEEE80211_AUTH_ALG_SHARED) {
            response_status = IEEE80211_STATUS_ALG;
            indication_status = IEEE80211_STATUS_ALG;
            break;
        } else {
            IEEE80211_DPRINTF(vap, IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO,
                              "[%s] auth: unsupported algorithm %d \n",ether_sprintf(wh->i_addr2),algo);
            vap->iv_stats.is_rx_auth_unsupported++;
            response_status = IEEE80211_STATUS_ALG;
            indication_status = IEEE80211_STATUS_ALG;
            break;
        }
    } while (FALSE);

    IEEE80211_DELIVER_EVENT_MLME_AUTH_INDICATION(vap, ni->ni_macaddr, indication_status);

    if (send_auth_response) {
        ieee80211_send_auth(ni, seq + 1, response_status, NULL, 0);
    }

    if (ni) {
        if (indication_status != IEEE80211_STATUS_SUCCESS ){
            /* auth is not success, remove the node from node table*/
            ieee80211_node_leave(ni);
        }
        /*
         * release the reference created at the begining of the case above
         * either by alloc_node or ref_node.
         */ 
        ieee80211_free_node(ni);
    }
}
예제 #20
0
 /*
  * processes data frames.
  * ieee80211_wow_magic_parser parses the wbuf to check if match magic packet.
  */
void 
ieee80211_wow_magic_parser(struct ieee80211_node *ni, wbuf_t wbuf)
{
    struct ieee80211vap *vap = ni->ni_vap;
    a_uint8_t    i_addr[IEEE80211_ADDR_LEN];
    a_int32_t    left_len = wbuf_get_pktlen(wbuf);
    a_uint8_t    *cur_ptr;
    a_uint32_t   dup_cnt, is_match = FALSE, is_bcast = FALSE;

    //wow_dump_pkt(wbuf->data, wbuf->len);

    if (left_len < IEEE80211_WOW_MAGIC_PKTLEN)
        return;
        
    cur_ptr = wbuf_header(wbuf);
    IEEE80211_ADDR_COPY(i_addr, vap->iv_myaddr);

    /* parse whole pkt */
    while (cur_ptr && (left_len>0) && (!is_match)) {
        
        /* left len is less than magic pkt size, give up */
        if (left_len < IEEE80211_WOW_MAGIC_PKTLEN)
            break;

        /* to skip continuous 0xFF and to match last 6 bytes of 0xFF */
        while (IEEE80211_IS_BROADCAST(cur_ptr) && (left_len>=IEEE80211_WOW_MAGIC_PKTLEN)) {
            is_bcast = TRUE;
            cur_ptr += IEEE80211_ADDR_LEN;
            left_len -= IEEE80211_ADDR_LEN;
        }

        /* 6 bytes of 0xFF matched, to check if 16 duplications of the IEEE address */
        if (is_bcast) {
            
            dup_cnt = 0;
            
            /* if 0xFF exists at head, skip it */
            while ((cur_ptr[0]==0xFF) && (left_len>=IEEE80211_WOW_MAGIC_DUPLEN)) {
                cur_ptr++;
                left_len--;
            }

            /* left len is less than duplication size, give up */
            if (left_len < IEEE80211_WOW_MAGIC_DUPLEN)
                break;
            
            /* to check if 16 duplications of the IEEE address */
            while (cur_ptr && (left_len>=IEEE80211_ADDR_LEN) && IEEE80211_ADDR_EQ(cur_ptr, i_addr)) {
                cur_ptr += IEEE80211_ADDR_LEN;
                left_len -= IEEE80211_ADDR_LEN;
                dup_cnt++;
                
                if (dup_cnt >= IEEE80211_WOW_MAGIC_DUPCNT) {
                    is_match = TRUE;
                    break;
                }
            }

            /* not match magic pkt, keep parsing */
            is_bcast = FALSE;

        } else {
            cur_ptr++;
            left_len--;
        }        
    }
    
    if (is_match) {
        adf_os_print("Magic packet received...\n");
        ieee80211_wow_set_gpio(vap);
    }
}
예제 #21
0
/*
 * Add privacy headers appropriate for the specified key.
 */
static int
ccmp_encap(struct ieee80211_key *k, wbuf_t wbuf, u_int8_t keyid)
{
    struct ccmp_ctx *ctx = k->wk_private;
    struct ieee80211com *ic = ctx->cc_ic;
    u_int8_t *ivp;
    int hdrlen;
    int is4addr, isqos, owl_wdswar;
    struct ieee80211_frame *wh;
    struct ieee80211_node *ni = wbuf_get_node(wbuf);

    wh = (struct ieee80211_frame *)wbuf_header(wbuf);
    is4addr = ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS) ? 1 : 0;
    isqos = IEEE80211_QOS_HAS_SEQ(wh);
    owl_wdswar = (ni->ni_flags & IEEE80211_NODE_OWL_WDSWAR);

    hdrlen = ieee80211_hdrspace(ic, wbuf_header(wbuf));

    /*
     * Copy down 802.11 header and add the IV, KeyID, and ExtIV.
     */
#ifndef __CARRIER_PLATFORM__
    ivp = (u_int8_t *)wbuf_push(wbuf, ccmp.ic_header);

    /* 
     * refresh the wh pointer,
     * wbuf header  pointer is changed with wbuf_push.
     */
    wh = (struct ieee80211_frame *) wbuf_header(wbuf); /* recompute wh */

    memmove(ivp, ivp + ccmp.ic_header, hdrlen);
#else
    if (wbuf_is_encap_done(wbuf)) {
        ivp = (u_int8_t *)wbuf_header(wbuf);
    }
    else { 
        ivp = (u_int8_t *)wbuf_push(wbuf, ccmp.ic_header);
        memmove(ivp, ivp + ccmp.ic_header, hdrlen);
        /* 
         * refresh the wh pointer,
         * wbuf header  pointer is changed with wbuf_push.
         */
        wh = (struct ieee80211_frame *) wbuf_header(wbuf); /* recompute wh */
    }
#endif
    ivp += hdrlen;

    /*
     * Due to OWL specific HW bug, increment key tsc by 16, since
     * we're copying the TID into bits [3:0] of IV0.
     * XXX: Need logic to not implement workaround for SOWL or greater.
     */
    if (is4addr && isqos && owl_wdswar)
        k->wk_keytsc += 16;
    else
        k->wk_keytsc++;		/* XXX wrap at 48 bits */

    ivp[0] = k->wk_keytsc >> 0;		/* PN0 */
    ivp[1] = k->wk_keytsc >> 8;		/* PN1 */
    ivp[2] = 0;				/* Reserved */
    ivp[3] = keyid | IEEE80211_WEP_EXTIV;	/* KeyID | ExtID */
    ivp[4] = k->wk_keytsc >> 16;		/* PN2 */
    ivp[5] = k->wk_keytsc >> 24;		/* PN3 */
    ivp[6] = k->wk_keytsc >> 32;		/* PN4 */
    ivp[7] = k->wk_keytsc >> 40;		/* PN5 */

    /*
     * Finally, do software encrypt if neeed.
     */
    if (k->wk_flags & IEEE80211_KEY_SWCRYPT) {
        if (!ccmp_encrypt(k, wbuf, hdrlen, 0)) {
            return 0;
        }
    }
    if ((k->wk_flags & IEEE80211_KEY_MFP) && IEEE80211_IS_MFP_FRAME(wh)) {
        if (ic->ic_get_mfpsupport(ic) != IEEE80211_MFP_HW_CRYPTO) {
            /* HW MFP is not enabled - do software encrypt */
            if (!ccmp_encrypt(k, wbuf, hdrlen, 1)) {
                return 0;
            }
        }
    }
    return 1;
}
예제 #22
0
/*
 * Validate and strip privacy headers (and trailer) for a
 * received frame. The specified key should be correct but
 * is also verified.
 */
static int
ccmp_decap(struct ieee80211_key *k, wbuf_t wbuf, int hdrlen, struct ieee80211_rx_status *rs)
{
    struct ccmp_ctx *ctx = k->wk_private;
    struct ieee80211vap *vap = ctx->cc_vap;
    struct ieee80211_frame *wh;
    uint8_t *ivp, *origHdr;
    u_int64_t pn;
    u_int8_t tid;
    struct ieee80211_mac_stats *mac_stats;
    u_int32_t hwmfp;
    uint8_t  update_keyrsc = 1;

    /*
     * Header should have extended IV and sequence number;
     * verify the former and validate the latter.
     */
    origHdr = (u_int8_t *)wbuf_header(wbuf);
    wh = (struct ieee80211_frame *)origHdr;
    mac_stats = IEEE80211_IS_MULTICAST(wh->i_addr1) ? &vap->iv_multicast_stats : &vap->iv_unicast_stats;
    if (IEEE80211_IS_MFP_FRAME(wh)) {
            IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO, "%s MFP frame\n", __func__);
    }

    if (rs->rs_flags & IEEE80211_RX_DECRYPT_ERROR) {
        IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO, "%s decrypt error\n", __func__);
        if (IEEE80211_IS_MFP_FRAME(wh)) {
            /* It may not be real crypto error, but hardware didn't do correct
             * decryption. Try software decrypt later.
             */
            rs->rs_flags &= ~IEEE80211_RX_DECRYPT_ERROR;
            rs->rs_flags |= IEEE80211_RX_MIC_ERROR;
        } else {
            /* The frame already failed decryption in hardware,
             * just update statistics and return. */
            mac_stats->ims_rx_ccmpmic++;
            return 0;
        }
    }

    ivp = origHdr + hdrlen;
    if ((ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) == 0) {
        /*
         * No extended IV; discard frame.
         */
        IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
                           "%s", "Missing ExtIV for AES-CCM cipher");
        mac_stats->ims_rx_ccmpformat++;
        return 0;
    }
    tid = IEEE80211_NON_QOS_SEQ;
    if (IEEE80211_QOS_HAS_SEQ(wh)) {
        if ( (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) 
             == IEEE80211_FC1_DIR_DSTODS ) {
            tid = ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] & IEEE80211_QOS_TID;
        } else {
            tid = ((struct ieee80211_qosframe *)wh)->i_qos[0] & IEEE80211_QOS_TID;
        }
    }

    /* NB: assume IEEEE80211_WEP_MINLEN covers the extended IV */ 
    pn = READ_6(ivp[0], ivp[1], ivp[4], ivp[5], ivp[6], ivp[7]);
    if (pn <= k->wk_keyrsc[tid]) {
        /*
         * Replay violation.
         */
        IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
                          "%s: CCMP Replay! Throw away. new pn=0x%x%x, "
                          "current pn=0x%x%x. seq=0x%x,Rsvd=0x%x,keyID=0x%x,tid=%d\n", 
                           __func__,
                           (u_int32_t)(pn>>32), (u_int32_t)pn, 
                           (u_int32_t)(k->wk_keyrsc[tid] >> 32), (u_int32_t)k->wk_keyrsc[tid],
                           *(u_int16_t*)(wh->i_seq), 
                           ivp[2], ivp[3],
                           tid
                           );
        ieee80211_notify_replay_failure(vap, wh, k, pn);
        mac_stats->ims_rx_ccmpreplay++;
        return 0;
    }
예제 #23
0
/*
 * node saveq flush.
 */
void
ath_node_pwrsaveq_flush(ath_node_t node)
{
    struct ath_node *an = ATH_NODE(node);
    struct ath_softc *sc = an->an_sc;
    ath_wbuf_t athwbuf;
    wbuf_t wbuf;
    struct ath_node_pwrsaveq *dataq,*mgtq;
    int dqlen, mqlen;
    ieee80211_tx_control_t *txctl;

    dataq = ATH_NODE_PWRSAVEQ_DATAQ(an); 
    mgtq  = ATH_NODE_PWRSAVEQ_MGMTQ(an); 

    /* XXX if no stations in ps mode, flush mc frames */
    dqlen = ATH_NODE_PWRSAVEQ_QLEN(dataq);

    /*
     * Flush queued mgmt frames.
     */
    ATH_NODE_PWRSAVEQ_LOCK(mgtq);
    if (ATH_NODE_PWRSAVEQ_QLEN(mgtq) != 0) {
        DPRINTF(sc, ATH_DEBUG_PWR_SAVE, "flush mgt ps queue, %u packets queued \n",
                                         ATH_NODE_PWRSAVEQ_QLEN(mgtq));
        for (;;) {

            ATH_NODE_PWRSAVEQ_DEQUEUE(mgtq, athwbuf);
            if (athwbuf == NULL)
                break;
            wbuf = athwbuf->wbuf;
            ASSERT(wbuf);
            mqlen = ATH_NODE_PWRSAVEQ_QLEN(mgtq);
            DPRINTF(sc, ATH_DEBUG_PWR_SAVE, "sendmgmt ps queue, %u packets remaining \n", mqlen);
            /*
             * For a Station node (non bss node) If this is the last packet, turn off the TIM bit,
             * Set the PWR_SAV bit on every frame but the last one
             * to allow encap to test for
             * adding more MORE_DATA bit to wh.
             */
            if (mqlen || dqlen) {
                wbuf_set_moredata(wbuf);
                if (wbuf_is_moredata(wbuf)) {
                    struct ieee80211_frame *wh = (struct ieee80211_frame *)wbuf_header(wbuf);
                    wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
                } 
            }

            wbuf_clear_legacy_ps(wbuf);
            txctl = &athwbuf->txctl;

            if (sc->sc_ath_ops.tx(sc, wbuf, txctl) != 0) {
                DPRINTF(sc, ATH_DEBUG_PWR_SAVE, "%s: send error, comple the wbuf\n", __func__);
                ath_node_pwrsaveq_complete_athwbuf(an, athwbuf);
            } else {
                if (athwbuf) {
                    OS_FREE_PS(athwbuf);
                }
            }
        }
    }
    mgtq->nsq_num_ps_frames = 0;
    ATH_NODE_PWRSAVEQ_UNLOCK(mgtq);

    /*
     * Flush queued unicast frames.
     */
    ATH_NODE_PWRSAVEQ_LOCK(dataq);
    if (ATH_NODE_PWRSAVEQ_QLEN(dataq) != 0) {
        DPRINTF(sc, ATH_DEBUG_PWR_SAVE, "flush data ps queue, %u packets queued \n",
            ATH_NODE_PWRSAVEQ_QLEN(dataq));
        for (;;) {

            ATH_NODE_PWRSAVEQ_DEQUEUE(dataq, athwbuf);
            if (athwbuf == NULL)
                break;
            wbuf = athwbuf->wbuf;
            ASSERT(wbuf);
            dqlen = ATH_NODE_PWRSAVEQ_QLEN(dataq);
            DPRINTF(sc, ATH_DEBUG_PWR_SAVE, "senddata ps queue, %u packets remaining \n", dqlen);
            /*
             * For a Station node (non bss node) If this is the last packet, turn off the TIM bit,
             * Set the PWR_SAV bit on every frame but the last one
             * to allow encap to test for
             * adding more MORE_DATA bit to wh.
             */
            if (dqlen) {
                wbuf_set_moredata(wbuf);
                if (wbuf_is_moredata(wbuf)) {
                    struct ieee80211_frame *wh = (struct ieee80211_frame *)wbuf_header(wbuf);
                    wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
                } 
            }
            
            wbuf_clear_legacy_ps(wbuf);
            txctl = &athwbuf->txctl;

            if (sc->sc_ath_ops.tx(sc, wbuf, txctl) != 0) {
                DPRINTF(sc, ATH_DEBUG_PWR_SAVE, "%s: send error, comple the wbuf\n", __func__);
                ath_node_pwrsaveq_complete_athwbuf(an, athwbuf);
            } else {
                if (athwbuf) {
                    OS_FREE_PS(athwbuf);
                }
            }
        }
    }
    ATH_NODE_PWRSAVEQ_UNLOCK(dataq);

    /* TIM will be set by UMAC caller */
}
예제 #24
0
/*
 * send one frme out of power save queue .
 * called in reponse to PS poll.
 * returns 1 if succesfully sends a frame. 0 other wise.
 */
int
ath_node_pwrsaveq_send(ath_node_t node, u_int8_t frame_type)
{
    struct ath_node *an = ATH_NODE(node);
    struct ath_softc *sc = an->an_sc;
    ath_wbuf_t athwbuf;
    wbuf_t wbuf = NULL;
    int qlen;
    struct ath_node_pwrsaveq *dataq, *mgtq;
    ieee80211_tx_control_t *txctl;
    struct ieee80211_frame *wh;
    u_int8_t more_frag;

    dataq = ATH_NODE_PWRSAVEQ_DATAQ(an); 
    mgtq  = ATH_NODE_PWRSAVEQ_MGMTQ(an); 

next_frag:

    ATH_NODE_PWRSAVEQ_LOCK(dataq);
    ATH_NODE_PWRSAVEQ_LOCK(mgtq);

    if (frame_type == IEEE80211_FC0_TYPE_MGT) {
        ATH_NODE_PWRSAVEQ_DEQUEUE(mgtq, athwbuf);
        if (athwbuf)
            wbuf = athwbuf->wbuf;
        if (wbuf && wbuf_is_pwrsaveframe(wbuf))  {
            /* ps frame (null (or) pspoll frame) */
            --mgtq->nsq_num_ps_frames;
        } 
    } else {
        ATH_NODE_PWRSAVEQ_DEQUEUE(dataq, athwbuf);
        if (athwbuf)
            wbuf = athwbuf->wbuf;
    }

    if (athwbuf == NULL || wbuf == NULL) {
        ATH_NODE_PWRSAVEQ_UNLOCK(mgtq);
        ATH_NODE_PWRSAVEQ_UNLOCK(dataq);
        return 0;
    }
        
    qlen = ATH_NODE_PWRSAVEQ_QLEN(dataq);
    qlen += ATH_NODE_PWRSAVEQ_QLEN(mgtq);

    ATH_NODE_PWRSAVEQ_UNLOCK(mgtq);
    ATH_NODE_PWRSAVEQ_UNLOCK(dataq);

    wh = (struct ieee80211_frame *)wbuf_header(wbuf);
    more_frag = wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG;

    /* 
     * If there are more packets, set the more packets bit
     * in the packet dispatched to the station; otherwise
     * turn off the TIM bit.
     */
    if (qlen != 0) {
        DPRINTF(sc, ATH_DEBUG_PWR_SAVE, "send packet, %u still queued \n", qlen);
        /* 
         * encap will set more data bit.
         */
        wbuf_set_moredata(wbuf);
        if (wbuf_is_moredata(wbuf)) {
             wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
        } 
    } else {
        DPRINTF(sc, ATH_DEBUG_PWR_SAVE, "%s", "send packet, queue empty \n");
        /* TIM bit will be set by UMAC caller */
    }

    wbuf_clear_legacy_ps(wbuf);
    txctl = &athwbuf->txctl;

    if (sc->sc_ath_ops.tx(sc, wbuf, txctl) != 0) {
        DPRINTF(sc, ATH_DEBUG_PWR_SAVE, "%s: send error, comple the wbuf\n", __func__);
        ath_node_pwrsaveq_complete_athwbuf(an, athwbuf);
        /* process all fragment together */
        if (more_frag)
            goto next_frag;

        return 0;
    }

    if (athwbuf) {
        OS_FREE_PS(athwbuf);
    }

    /* process all fragments together */
    if (more_frag)
        goto next_frag;

    return 1;
}
예제 #25
0
int
ccmp_encrypt(struct ieee80211_key *key, wbuf_t wbuf0, int hdrlen, int mfp)
{
    struct ccmp_ctx *ctx = key->wk_private;
    struct ieee80211_frame *wh;
    wbuf_t wbuf = wbuf0;
    int data_len, i, space;
    uint8_t aad[2 * AES_BLOCK_LEN], b0[AES_BLOCK_LEN], b[AES_BLOCK_LEN],
        e[AES_BLOCK_LEN], s0[AES_BLOCK_LEN];
    uint8_t *pos;

    ctx->cc_vap->iv_stats.is_crypto_ccmp++;
    wh = (struct ieee80211_frame *)wbuf_header(wbuf);
    data_len = wbuf_get_pktlen(wbuf) - (hdrlen + ccmp.ic_header);
    ccmp_init_blocks(&ctx->cc_aes, wh, key->wk_keytsc,
                     data_len, b0, aad, b, s0, mfp);

    i = 1;
    pos = (u_int8_t *)wbuf_header(wbuf) + hdrlen + ccmp.ic_header;
    /* NB: assumes header is entirely in first mbuf */
    space = wbuf_get_pktlen(wbuf) - (hdrlen + ccmp.ic_header);
    for (;;) {
        if (space > data_len)
            space = data_len;
        /*
         * Do full blocks.
         */
        while (space >= AES_BLOCK_LEN) {
            CCMP_ENCRYPT(i, b, b0, pos, e, AES_BLOCK_LEN);
            pos += AES_BLOCK_LEN, space -= AES_BLOCK_LEN;
            data_len -= AES_BLOCK_LEN;
            i++;
        }
        if (data_len <= 0)		/* no more data */
            break;
        wbuf = wbuf_next(wbuf);
        if (wbuf == NULL) {		/* last buffer */
            if (space != 0) {
                /*
                 * Short last block.
                 */
                CCMP_ENCRYPT(i, b, b0, pos, e, space);
            }
            break;
        }
#if 1 /* assume only one chunk */
        break;
#else
        if (space != 0) {
            uint8_t *pos_next;
            int space_next;
            int len, dl, sp;
            wbuf_t wbufi_new;

            /*
             * Block straddles one or more mbufs, gather data
             * into the block buffer b, apply the cipher, then
             * scatter the results back into the mbuf chain.
             * The buffer will automatically get space bytes
             * of data at offset 0 copied in+out by the
             * CCMP_ENCRYPT request so we must take care of
             * the remaining data.
             */
            wbuf_new = wbuf;
            dl = data_len;
            sp = space;
            for (;;) {
                pos_next = (u_int8_t *)wbuf_header(wbuf_new);
                len = min(dl, AES_BLOCK_LEN);
                space_next = len > sp ? len - sp : 0;
                if (wbuf_get_len(wbuf_new) >= space_next) {
                    /*
                     * This mbuf has enough data; just grab
                     * what we need and stop.
                     */
                    xor_block(b+sp, pos_next, space_next);
                    break;
                }
                /*
                 * This mbuf's contents are insufficient,
                 * take 'em all and prepare to advance to
                 * the next mbuf.
                 */
                xor_block(b+sp, pos_next, n->m_len);
                sp += wbuf_get_len(wbuf_new), dl -= wbuf_get_len(wbuf_new);
                wbuf_next = m_next;
                if (n == NULL)
                    break;
            }

            CCMP_ENCRYPT(i, b, b0, pos, e, space);

            /* NB: just like above, but scatter data to mbufs */
            dl = data_len;
            sp = space;
            for (;;) {
                pos_next = mtod(m, uint8_t *);
                len = min(dl, AES_BLOCK_LEN);
                space_next = len > sp ? len - sp : 0;
                if (m->m_len >= space_next) {
                    xor_block(pos_next, e+sp, space_next);
                    break;
                }
                xor_block(pos_next, e+sp, m->m_len);
                sp += m->m_len, dl -= m->m_len;
                m = m->m_next;
                if (m == NULL)
                    goto done;
            }
            /*
             * Do bookkeeping.  m now points to the last mbuf
             * we grabbed data from.  We know we consumed a
             * full block of data as otherwise we'd have hit
             * the end of the mbuf chain, so deduct from data_len.
             * Otherwise advance the block number (i) and setup
             * pos+space to reflect contents of the new mbuf.
             */
            data_len -= AES_BLOCK_LEN;
            i++;
            pos = pos_next + space_next;
            space = m->m_len - space_next;
        } else {
예제 #26
0
int
ieee80211_recv_probereq(struct ieee80211_node *ni, wbuf_t wbuf, int subtype)
{
    struct ieee80211com *ic = ni->ni_ic;
    struct ieee80211vap *vap = ni->ni_vap;
    struct ieee80211_frame *wh;
    u_int8_t *frm, *efrm;
    u_int8_t *ssid, *rates, *ven;

#if ATH_SUPPORT_AP_WDS_COMBO
    if (vap->iv_opmode == IEEE80211_M_STA || !ieee80211_vap_ready_is_set(vap) || vap->iv_no_beacon) {
#else
    if (vap->iv_opmode == IEEE80211_M_STA || !ieee80211_vap_ready_is_set(vap)) {
#endif
        vap->iv_stats.is_rx_mgtdiscard++;   /* XXX stat */
        return -EINVAL;
    }

    wh = (struct ieee80211_frame *) wbuf_header(wbuf);
    frm = (u_int8_t *)&wh[1];
    efrm = wbuf_header(wbuf) + wbuf_get_pktlen(wbuf);

	/*zhaoyang1 add start for  probe request REQUIREMENTS-340*/
	if (vap->iv_probe_request) {
		switch (vap->iv_probe_request) {
			case IEEE80211_BROADCAST_PROBE:
				if (IEEE80211_IS_BROADCAST(wh->i_addr1)) {
					vap->iv_stats.is_rx_mgtdiscard++;   /* XXX stat */
			        return -EINVAL;
				}
				break;
			case IEEE80211_ALL_PROBE:
				vap->iv_stats.is_rx_mgtdiscard++;   /* XXX stat */
	    		return -EINVAL;
			default:
				printk("Probe_req value is wrong, iv_probe_request = %d\n", vap->iv_probe_request);
				break;
		}
	}
	/*zhaoyang1 add end*/
	if (IEEE80211_IS_MULTICAST(wh->i_addr2)) {
        /* frame must be directed */
        vap->iv_stats.is_rx_mgtdiscard++;   /* XXX stat */
        return -EINVAL;
    }

    /*
     * prreq frame format
     *  [tlv] ssid
     *  [tlv] supported rates
     *  [tlv] extended supported rates
     *  [tlv] Atheros Advanced Capabilities
     */
    ssid = rates = NULL;
    while (((frm+1) < efrm) && (frm + frm[1] + 1 < efrm)) {
        switch (*frm) {
        case IEEE80211_ELEMID_SSID:
            ssid = frm;
            break;
        case IEEE80211_ELEMID_RATES:
            rates = frm;
            break;
        case IEEE80211_ELEMID_VENDOR:
            if (vap->iv_venie && vap->iv_venie->ven_oui_set) {            
                ven = frm;
                if (ven[2] == vap->iv_venie->ven_oui[0] && 
                    ven[3] == vap->iv_venie->ven_oui[1] && 
                    ven[4] == vap->iv_venie->ven_oui[2]) {
                    vap->iv_venie->ven_ie_len = MIN(ven[1] + 2, IEEE80211_MAX_IE_LEN);
                    OS_MEMCPY(vap->iv_venie->ven_ie, ven, vap->iv_venie->ven_ie_len);
                }
            }
            break;
        }
        frm += frm[1] + 2;
    }

    if (frm > efrm) {
        return -EINVAL;
    }

    IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
    IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN);
    IEEE80211_VERIFY_SSID(vap->iv_bss, ssid);

    if (IEEE80211_VAP_IS_HIDESSID_ENABLED(vap) && (ssid[1] == 0)) {
        IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
                          wh, ieee80211_mgt_subtype_name[
                              subtype >> IEEE80211_FC0_SUBTYPE_SHIFT],
                          "%s", "no ssid with ssid suppression enabled");
        vap->iv_stats.is_rx_ssidmismatch++; /*XXX*/
        return -EINVAL;
    }

    /*
     * Skip Probe Requests received while the scan algorithm is setting a new 
     * channel, or while in a foreign channel.
     * Trying to transmit a frame (Probe Response) during a channel change 
     * (which includes a channel reset) can cause a NMI due to invalid HW 
     * addresses. 
     * Trying to transmit the Probe Response while in a foreign channel 
     * wouldn't do us any good either.
     */
    if (ieee80211_scan_can_transmit(ic->ic_scanner))
        ieee80211_send_proberesp(ni, wh->i_addr2, NULL,0);
    
    return 0;
}
예제 #27
0
int
ieee80211_recv_asreq(struct ieee80211_node *ni, wbuf_t wbuf, int subtype)
{
    struct ieee80211com *ic = ni->ni_ic;
    struct ieee80211vap *vap = ni->ni_vap;
    struct ieee80211_frame *wh;
    u_int8_t *frm, *efrm;
    u_int16_t capinfo, bintval;
    struct ieee80211_rsnparms rsn;
    u_int8_t reason;
    int reassoc, resp;
    u_int8_t *ssid, *rates, *xrates, *wpa, *wme, *ath, *htcap,*vendor_ie, *wps;
    u_int8_t *athextcap;

    if (vap->iv_opmode != IEEE80211_M_HOSTAP && vap->iv_opmode != IEEE80211_M_BTAMP) {
        vap->iv_stats.is_rx_mgtdiscard++;
        return -EINVAL;
    }

    wh = (struct ieee80211_frame *) wbuf_header(wbuf);
    frm = (u_int8_t *)&wh[1];
    efrm = wbuf_header(wbuf) + wbuf_get_pktlen(wbuf);

    if (subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
        reassoc = 1;
        resp = IEEE80211_FC0_SUBTYPE_REASSOC_RESP;
		/*zhaoyang1 transplant from 717*/
		/*pengruofeng add start for management frame stats 2011-5-9*/
		vap->iv_stats.is_rx_reassoc++;
		/*pengruofeng add end 2011-5-9*/	
		/*zhaoyang1 transplant end*/
    } else {
        reassoc = 0;
        resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP;
    }
    /*
     * asreq frame format
     *    [2] capability information
     *    [2] listen interval
     *    [6*] current AP address (reassoc only)
     *    [tlv] ssid
     *    [tlv] supported rates
     *    [tlv] extended supported rates
     *    [tlv] WPA or RSN
     *    [tlv] WME
     *    [tlv] HT Capabilities
     *    [tlv] Atheros capabilities
     */
    IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4));
    if (!IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_bss->ni_bssid)) {
        IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
                          wh, ieee80211_mgt_subtype_name[subtype >>
                                                         IEEE80211_FC0_SUBTYPE_SHIFT],
                          "%s\n", "wrong bssid");
		/*zhaoyang1 transplant from 717*/
		/*pengruofeng add start for management frame stats 2011-5-9*/
		if (reassoc) {
		
			vap->iv_stats.is_rx_reassoc_bss++;
		} else {
		
			vap->iv_stats.is_rx_assoc_bss++;
		}
		/*pengruofeng add end 2011-5-9*/
		/*zhaoyang1 transplant end*/
		
        return -EINVAL;
    }
예제 #28
0
void ieee80211_send_report(ieee80211_rrm_t rrm)
{
    struct ieee80211vap *vap =NULL;
    struct ieee80211_node *ni = NULL;
    struct ieee80211_measrsp_ie *measrsp=NULL;
    wbuf_t wbuf = NULL;
    u_int8_t *frm = NULL,*fptr = NULL;
    struct ieee80211_action_rm_rsp *rm_rsp=NULL;
    struct ieee80211_rrmreq_info *params=NULL;
    enum ieee80211_opmode opmode; 

    params = (struct ieee80211_rrmreq_info *)(rrm->rrmcb);
    vap = rrm->rrm_vap;
    opmode = wlan_vap_get_opmode(vap);
    ni = rrm->ni;

    RRM_FUNCTION_ENTER;

    wbuf = ieee80211_getmgtframe(ni,IEEE80211_FC0_SUBTYPE_ACTION, &frm,0);
    if(wbuf == NULL)
        return;

    rm_rsp = (struct ieee80211_action_rm_rsp *)(frm);
    rm_rsp->header.ia_category = IEEE80211_ACTION_CAT_RM;
    rm_rsp->header.ia_action  = IEEE80211_ACTION_RM_RESP; 
    rm_rsp->dialogtoken = params->rm_dialogtoken; 

    switch(rrm->pending_report_type)
    {
        case IEEE80211_MEASREQ_BR_TYPE:
            fptr = ieee80211_rrm_send_bcnreq_resp(rrm,&rm_rsp->rsp_ies[0]);
            break;
        case IEEE80211_MEASREQ_CHANNEL_LOAD_REQ:
            fptr = ieee80211_rrm_send_chload_resp(rrm,&rm_rsp->rsp_ies[0]);
            break;
        case IEEE80211_MEASREQ_STA_STATS_REQ:
            fptr = ieee80211_rrm_send_stastats_resp(rrm,&rm_rsp->rsp_ies[0]);
            break;
        case IEEE80211_MEASREQ_NOISE_HISTOGRAM_REQ:
            fptr = ieee80211_rrm_send_nhist_resp(rrm,&rm_rsp->rsp_ies[0]);
            break;
        case IEEE80211_MEASREQ_LCI_REQ:
            fptr = ieee80211_rrm_send_lci_resp(rrm, &rm_rsp->rsp_ies[0]);
            break;
        case IEEE80211_MEASREQ_OTHER:
            measrsp = (struct ieee80211_measrsp_ie *)(&rm_rsp->rsp_ies[0]);
            measrsp->id = IEEE80211_ELEMID_MEASREP;
            measrsp->token=params->rep_dialogtoken;
            measrsp->rspmode |= params->reject_mode;
            measrsp->rsptype = params->reject_type;
            fptr = (u_int8_t *)(&measrsp->rsp[0]);
            measrsp->len = fptr -(u_int8_t *)(&measrsp->token);
            break;
        default:
            return;
    }

    if(IEEE80211_ADDR_EQ(ni->ni_macaddr, vap->iv_myaddr)) { /* self report */
        ieee80211_rrm_recv_action(vap,ni,IEEE80211_ACTION_RM_RESP,frm,(fptr - frm));
        OS_FREE(wbuf);
    } else {
        wbuf_set_pktlen(wbuf, (fptr -(u_int8_t*)(wbuf_header(wbuf))));
        ieee80211_send_mgmt(vap, ni, wbuf, false);
    }
    rrm->rrm_in_progress = 0; /* making module available */
    ieee80211_rrm_free_report(rrm);

    RRM_FUNCTION_EXIT;
    return;
}
예제 #29
0
/*
 * pause traffic of a vap from a specified queue.
 * if vap is null all the traffic will be paused.
 */ 
static void ath_tx_vap_pause_txq(struct ath_softc *sc, struct ath_txq *txq, struct ath_vap *avp)
{
   struct ath_buf *bf, *lastbf;
   ath_bufhead bf_head, bf_stage;
   struct ath_node *an,*an_uapsd_head;
   ath_bufhead    vap_mcast_stage_q[ATH_VAPSIZE];         /* temprorary per vap staging queue for cabq traffic */
   struct ath_vap        *mcast_vap_q[ATH_VAPSIZE];
   u_int32_t i;
   struct ieee80211_frame *wh;

   if (txq == sc->sc_cabq) {
       for (i=0;i<ATH_VAPSIZE;++i) {
          TAILQ_INIT(&vap_mcast_stage_q[i]);
          mcast_vap_q[i] = NULL;
       }
   }

   an_uapsd_head=NULL;

   TAILQ_INIT(&bf_stage);
    /*
     * NB: this assumes output has been stopped and
     *     we do not need to block ath_tx_tasklet
     */
    for (;;) {
        ATH_TXQ_LOCK(txq);
        if (sc->sc_enhanceddmasupport) {
            bf = TAILQ_FIRST(&txq->axq_fifo[txq->axq_tailindex]);
            if (bf == NULL) {
                if (txq->axq_headindex != txq->axq_tailindex)
                    printk("ath_tx_draintxq: ERR head %d tail %d\n",
                           txq->axq_headindex, txq->axq_tailindex);
                txq->axq_headindex = 0;
                txq->axq_tailindex = 0;
                ATH_TXQ_UNLOCK(txq);
                break;
            }
        } else {
            bf = TAILQ_FIRST(&txq->axq_q);
            if (bf == NULL) {
                txq->axq_link = NULL;
                txq->axq_linkbuf = NULL;
                ATH_TXQ_UNLOCK(txq);
                break;
            }

            if (bf->bf_status & ATH_BUFSTATUS_STALE) {
                ATH_TXQ_REMOVE_STALE_HEAD(txq, bf, bf_list);
                ATH_TXQ_UNLOCK(txq);
#ifdef ATH_SUPPORT_UAPSD
                if (bf->bf_qosnulleosp) {

                    ath_tx_uapsdqnulbf_complete(sc, bf, false);

                } else
#endif
                {
                    ATH_TXBUF_LOCK(sc);
                    sc->sc_txbuf_free++;
#if ATH_TX_BUF_FLOW_CNTL
					if(bf) {
                    txq->axq_num_buf_used--;
					}
#endif
                    TAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
                    
#if TRACE_TX_LEAK                
                    TAILQ_REMOVE(&sc->sc_tx_trace_head,bf,bf_tx_trace_list);
#endif //TRACE_TX_LEAK

                    ATH_TXBUF_UNLOCK(sc);
#if ATH_SUPPORT_FLOWMAC_MODULE
                    if (sc->sc_osnetif_flowcntrl) {
                        ath_netif_wake_queue(sc);
                    }
#endif
                }
                continue;
            }
        }

        lastbf = bf->bf_lastbf;
 
        TAILQ_INIT(&bf_head);

        /* remove ath_buf's of the same mpdu from txq */
        if (sc->sc_enhanceddmasupport) {
             if (txq == sc->sc_cabq || txq == sc->sc_uapsdq) {
                 ATH_EDMA_MCASTQ_MOVE_HEAD_UNTIL(txq, &bf_head, lastbf, bf_list);
             } else {
                 ATH_EDMA_TXQ_MOVE_HEAD_UNTIL(txq, &bf_head, lastbf, bf_list);
             }
        } else {
            ATH_TXQ_MOVE_HEAD_UNTIL(txq, &bf_head, lastbf, bf_list);
        }

        txq->axq_totalqueued --;                                      

        if (bf->bf_isaggr) {
            txq->axq_aggr_depth--;
        }
#if ATH_SUPPORT_CFEND
        if (txq == sc->sc_cfendq) {
            /* process CF End packet */
            if (bf->bf_state.bfs_iscfend) {
                ath_tx_cfend_complete (sc, bf, &bf_head);
                ATH_TXQ_UNLOCK(txq);
                continue; /* process rest of the buffers */
            }
        }
#endif


        ATH_TXQ_UNLOCK(txq);

#ifdef AR_DEBUG
        if (!sc->sc_enhanceddmasupport && CHK_SC_DEBUG(sc, ATH_DEBUG_RESET))
            /* Legacy only as the enhanced DMA txprocdesc() 
             * will move the tx status ring tail pointer.
             */
            ath_printtxbuf(bf, ath_hal_txprocdesc(sc->sc_ah, bf->bf_desc) == HAL_OK);
#endif /* AR_DEBUG */

        an = bf->bf_node;
        /*
         * if the node belongs to the vap being paused (or) if the request
         * is to pause all vaps (avp is NULL)
         * then put it back in to the nodes queue.
         */
        if (!avp || (avp && an->an_avp == avp) ) {
#ifdef ATH_SUPPORT_UAPSD
            if (txq == sc->sc_uapsdq) {
                /* 
                 * if the node is not on the UAPSD node list then put it on the list.
                 * alwasys put it on the head of the list.
                 */
                if (!an->an_temp_next && (an != an_uapsd_head)) {
                    if(an_uapsd_head){
                        an->an_temp_next = an_uapsd_head;
                    }
                    an_uapsd_head = an;
                }
                if (TAILQ_FIRST(&bf_head) == NULL ) {
                    DPRINTF(sc, ATH_DEBUG_ANY,"#####%s : %d  bf_head is empty \n",__func__, __LINE__);
                } else {
                    ath_tx_stage_queue_uapsd(sc,an, &bf_head);
                }
                continue;
            }

#endif
            if (txq == sc->sc_cabq) {
                ath_bufhead    *mcast_stage_q = NULL;
                /* 
                 * get the mcast staging queue for this vap and 
                 * add the frame to the mcast staging queue.
                 */

                for (i=0;i<ATH_VAPSIZE;++i) {
                   if (mcast_vap_q[i] == avp) {
                       mcast_stage_q =  &vap_mcast_stage_q[i]; 
                   } else if (mcast_vap_q[i] == NULL) {
                       mcast_stage_q =  &vap_mcast_stage_q[i]; 
                       mcast_vap_q[i] = avp;
                   }
                   if (mcast_stage_q ) {
                       break;
                   }
                }

                if (mcast_stage_q == NULL) {
                   DPRINTF(sc, ATH_DEBUG_ANY, "%s: mcat_stage_q is NULL \n", __func__);
                   continue; 
                }

                TAILQ_CONCAT(mcast_stage_q, &bf_head, bf_list);                   
                continue;
            }

            if (bf->bf_isampdu) {
                if (!bf->bf_isaggr) {
                    __11nstats(sc,tx_unaggr_comperror);
                }
                ath_tx_mark_aggr_rifs_done(sc, txq, bf, &bf_head,
                                          &((struct ath_desc *)(lastbf->bf_desc))->ds_txstat, 0);
            } else {
#ifdef ATH_SWRETRY
                if (sc->sc_enhanceddmasupport) {
                    /*
                     * Decrement of swr_num_eligible_frms for AMPDU is done
                     * above in ath_tx-complete_aggr_rifs.
                     */
                    if (!bf->bf_isampdu && bf->bf_isdata) {
                        struct ath_node *an = bf->bf_node;
                        if (an) {
                            struct ath_swretry_info *pInfo = &an->an_swretry_info[txq->axq_qnum];
                            ATH_NODE_SWRETRY_TXBUF_LOCK(an);
                            ASSERT(pInfo->swr_num_eligible_frms);
                            pInfo->swr_num_eligible_frms --;
                            ATH_NODE_SWRETRY_TXBUF_UNLOCK(an);
                        }
                    }
                }
#endif
                if (bf->bf_isbar) {
                    DPRINTF(sc, ATH_DEBUG_RESET, "*****%s: BAR frame \n", __func__);
#ifdef ATH_SUPPORT_TxBF
                    ath_tx_complete_buf(sc, bf, &bf_head, 0, 0, 0);
#else
                    ath_tx_complete_buf(sc, bf, &bf_head, 0);
#endif
                } else {
                    /*
                     *  Non Aggregates, put them at the head of the tid queue (if node is still avail,)
                     */

                    atomic_inc(&an->an_active_tx_cnt);
                    /* Make sure that Node is still alive and not temporary node */
                    if ((an->an_flags & (ATH_NODE_TEMP | ATH_NODE_CLEAN)) == 0) {
                        struct ath_atx_tid *tid = ATH_AN_2_TID(an, bf->bf_tidno);
                        TAILQ_INSERTQ_HEAD(&tid->buf_q, &bf_head, bf_list);
                        atomic_dec(&an->an_active_tx_cnt);
                    }
                    else {
                        if ((an->an_flags & ATH_NODE_TEMP) != 0) {
                            DPRINTF(sc, ATH_DEBUG_ANY, "%s: an=0x%p is Temp-node.\n", __func__, an);
                        }
                        if ((an->an_flags & ATH_NODE_CLEAN) != 0) {
                            DPRINTF(sc, ATH_DEBUG_ANY, "%s: an=0x%p is already CLEAN.\n", __func__, an);
                        }

                        atomic_dec(&an->an_active_tx_cnt);
                        
                        // Free these buffers.
#ifdef ATH_SUPPORT_TxBF
                        ath_tx_complete_buf(sc, bf, &bf_head, 0, 0, 0);
#else
                        ath_tx_complete_buf(sc, bf, &bf_head, 0);
#endif
                    }
                }
            }
        } else {
            /*
             * if the frame does not need to be paused
             * then put it on to a staging queue.
             */
            TAILQ_CONCAT(&bf_stage, &bf_head, bf_list);                   
        }
    }

#ifdef ATH_SUPPORT_UAPSD
    while(an_uapsd_head) {
        an=an_uapsd_head;
        an_uapsd_head = an->an_temp_next;
        an->an_temp_next=NULL;
        ath_tx_prepend_uapsd_stage_queue(sc,an);
    }
#endif
    /* prepend the staging queue back to vap mcast queue */
    if (txq == sc->sc_cabq) {
        ath_bufhead    *mcast_stage_q = NULL;

        for (i=0;i<ATH_VAPSIZE;++i) {
           mcast_stage_q =  &vap_mcast_stage_q[i]; 
           /*
            * prepend only if the mcast staging queue is not empty
            */ 
           if (TAILQ_FIRST(mcast_stage_q))   { 
              /*
               * need to prepend the frames from staging queue to the vap mcast queue.
               * do it in 2 steps.
               * move the frames from the vap mcast queue to the
               * end of the staging queue and move all the frames from staging queue 
               * to the vaps mcast queue.
               */ 
              TAILQ_CONCAT(mcast_stage_q, &mcast_vap_q[i]->av_mcastq.axq_q, bf_list);
              mcast_vap_q[i]->av_mcastq.axq_depth=0; 
              mcast_vap_q[i]->av_mcastq.axq_totalqueued = 0;                        
              mcast_vap_q[i]->av_mcastq.axq_linkbuf = 0;                        
              mcast_vap_q[i]->av_mcastq.axq_link = NULL;                        

              bf = TAILQ_FIRST(mcast_stage_q);
              while (bf) {
                  /*
                   * Remove a single ath_buf from the staging  queue and add it to
                   * the mcast queue.
                   */
                  lastbf = bf->bf_lastbf;

                   wh = (struct ieee80211_frame *)wbuf_header(bf->bf_mpdu);
                  
                   DPRINTF(sc, ATH_DEBUG_ANY, "%s: queue mcast frame back seq # %d \n", __func__,
                        le16toh(*(u_int16_t *)wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT);
                  TAILQ_REMOVE_HEAD_UNTIL(mcast_stage_q, &bf_head, lastbf, bf_list);
                  if (ath_tx_mcastqaddbuf(sc, mcast_vap_q[i], &bf_head) != EOK) {
                      /* failed to queue the buf, complete it with an error */
#ifdef ATH_SUPPORT_TxBF
                      ath_tx_complete_buf(sc,bf,&bf_head,0,0, 0);
#else
                      ath_tx_complete_buf(sc,bf,&bf_head,0);
#endif
                  }

                  bf = TAILQ_FIRST(mcast_stage_q);
              }
           }
        }
    }
예제 #30
0
/*
 * Send a probe response frame.
 * NB: for probe response, the node may not represent the peer STA.
 * We could use BSS node to reduce the memory usage from temporary node.
 */
int
ieee80211_send_proberesp(struct ieee80211_node *ni, u_int8_t *macaddr,
                         const void *optie, const size_t  optielen)
{
    struct ieee80211vap *vap = ni->ni_vap;
    struct ieee80211com *ic = ni->ni_ic;
    struct ieee80211_rsnparms *rsn = &vap->iv_rsn;
    wbuf_t wbuf;
    struct ieee80211_frame *wh;
    u_int8_t *frm;
    u_int16_t capinfo;
    int enable_htrates;
    bool add_wpa_ie = true;

    ASSERT(vap->iv_opmode == IEEE80211_M_HOSTAP || vap->iv_opmode == IEEE80211_M_IBSS ||
           vap->iv_opmode == IEEE80211_M_BTAMP);
    
    wbuf = wbuf_alloc(ic->ic_osdev, WBUF_TX_MGMT, MAX_TX_RX_PACKET_SIZE);
    if (wbuf == NULL)
        return -ENOMEM;

#ifdef IEEE80211_DEBUG_REFCNT
    printk("%s ,line %u: increase node %p <%s> refcnt to %d\n",
           __func__, __LINE__, ni, ether_sprintf(ni->ni_macaddr),
           ieee80211_node_refcnt(ni));
#endif
    
    /* setup the wireless header */
    wh = (struct ieee80211_frame *)wbuf_header(wbuf);
    ieee80211_send_setup(vap, ni, wh,
                         IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP,
                         vap->iv_myaddr, macaddr,
                         ieee80211_node_get_bssid(ni));
    frm = (u_int8_t *)&wh[1];

    /*
     * probe response frame format
     *  [8] time stamp
     *  [2] beacon interval
     *  [2] cabability information
     *  [tlv] ssid
     *  [tlv] supported rates
     *  [tlv] parameter set (FH/DS)
     *  [tlv] parameter set (IBSS)
     *  [tlv] extended rate phy (ERP)
     *  [tlv] extended supported rates
     *  [tlv] country (if present)
     *  [3] power constraint
     *  [tlv] WPA
     *  [tlv] WME
     *  [tlv] HT Capabilities
     *  [tlv] HT Information
     *      [tlv] Atheros Advanced Capabilities
     */
    OS_MEMZERO(frm, 8);  /* timestamp should be filled later */
    frm += 8;
    *(u_int16_t *)frm = htole16(vap->iv_bss->ni_intval);
    frm += 2;
    if (vap->iv_opmode == IEEE80211_M_IBSS)
        capinfo = IEEE80211_CAPINFO_IBSS;
    else
        capinfo = IEEE80211_CAPINFO_ESS;
    if (IEEE80211_VAP_IS_PRIVACY_ENABLED(vap))
        capinfo |= IEEE80211_CAPINFO_PRIVACY;
    if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
        IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan))
        capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
    if (ic->ic_flags & IEEE80211_F_SHSLOT)
        capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
    if (ieee80211_ic_doth_is_set(ic) && ieee80211_vap_doth_is_set(vap))
        capinfo |= IEEE80211_CAPINFO_SPECTRUM_MGMT;
    *(u_int16_t *)frm = htole16(capinfo);
    frm += 2;
    
    frm = ieee80211_add_ssid(frm, vap->iv_bss->ni_essid,
                             vap->iv_bss->ni_esslen);
    
    frm = ieee80211_add_rates(frm, &vap->iv_bss->ni_rates, ic);

    if (!IEEE80211_IS_CHAN_FHSS(vap->iv_bsschan)) {
        *frm++ = IEEE80211_ELEMID_DSPARMS;
        *frm++ = 1;
        *frm++ = ieee80211_chan2ieee(ic, ic->ic_curchan);
    }
    
    if (vap->iv_opmode == IEEE80211_M_IBSS) {
        *frm++ = IEEE80211_ELEMID_IBSSPARMS;
        *frm++ = 2;
        *frm++ = 0; *frm++ = 0;     /* TODO: ATIM window */
    }

    if (IEEE80211_IS_COUNTRYIE_ENABLED(ic)) {
        frm = ieee80211_add_country(frm, vap);
    }

    if (ieee80211_ic_doth_is_set(ic) && ieee80211_vap_doth_is_set(vap)) {
        *frm++ = IEEE80211_ELEMID_PWRCNSTR;
        *frm++ = 1;
        *frm++ = IEEE80211_PWRCONSTRAINT_VAL(vap);
    }
   
#if ATH_SUPPORT_IBSS_DFS
    if (vap->iv_opmode == IEEE80211_M_IBSS) {
         frm =  ieee80211_add_ibss_dfs(frm,vap);
    }
#endif

    if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) || 
        IEEE80211_IS_CHAN_11NG(ic->ic_curchan)) {
        frm = ieee80211_add_erp(frm, ic);
    }

    /*
     * check if os shim has setup RSN IE it self.
     */
    IEEE80211_VAP_LOCK(vap);
    if (vap->iv_app_ie[IEEE80211_FRAME_TYPE_PROBERESP].length) {
        add_wpa_ie = ieee80211_check_wpaie(vap, vap->iv_app_ie[IEEE80211_FRAME_TYPE_PROBERESP].ie,
                                           vap->iv_app_ie[IEEE80211_FRAME_TYPE_PROBERESP].length);
    }
    if (vap->iv_opt_ie.length) {
        add_wpa_ie = ieee80211_check_wpaie(vap, vap->iv_opt_ie.ie,
                                           vap->iv_opt_ie.length);
    }
    IEEE80211_VAP_UNLOCK(vap);

    if (add_wpa_ie) {
        if (RSN_AUTH_IS_RSNA(rsn))
            frm = ieee80211_setup_rsn_ie(vap, frm);
    }

#if ATH_SUPPORT_WAPI
    if (RSN_AUTH_IS_WAI(rsn))
        frm = ieee80211_setup_wapi_ie(vap, frm);
#endif

    frm = ieee80211_add_xrates(frm, &vap->iv_bss->ni_rates, ic);

    enable_htrates = ieee80211vap_htallowed(vap);
    
    if (IEEE80211_IS_CHAN_11N(ic->ic_curchan) && enable_htrates) {
        frm = ieee80211_add_htcap(frm, ni, IEEE80211_FC0_SUBTYPE_PROBE_RESP);

        if (!IEEE80211_IS_HTVIE_ENABLED(ic))
            frm = ieee80211_add_htcap_pre_ana(frm, ni, IEEE80211_FC0_SUBTYPE_PROBE_RESP);

        frm = ieee80211_add_htinfo(frm, ni);

        if (!IEEE80211_IS_HTVIE_ENABLED(ic))
            frm = ieee80211_add_htinfo_pre_ana(frm, ni);

        if (!(ic->ic_flags & IEEE80211_F_COEXT_DISABLE)) {
            frm = ieee80211_add_obss_scan(frm, ni);
            frm = ieee80211_add_extcap(frm, ni);
        }
    }

    if (add_wpa_ie) {
        if (RSN_AUTH_IS_WPA(rsn))
            frm = ieee80211_setup_wpa_ie(vap, frm);
    }
    
    if (ieee80211_vap_wme_is_set(vap) &&
        (vap->iv_opmode == IEEE80211_M_HOSTAP || vap->iv_opmode == IEEE80211_M_BTAMP)) /* don't support WMM in ad-hoc for now */
        frm = ieee80211_add_wme_param(frm, &ic->ic_wme, IEEE80211_VAP_IS_UAPSD_ENABLED(vap));

    if ((IEEE80211_IS_CHAN_11N(ic->ic_curchan)) && (IEEE80211_IS_HTVIE_ENABLED(ic)) && enable_htrates) {
        frm = ieee80211_add_htcap_vendor_specific(frm, ni, IEEE80211_FC0_SUBTYPE_PROBE_RESP);
        frm = ieee80211_add_htinfo_vendor_specific(frm, ni);
    }

    if (vap->iv_bss->ni_ath_flags) {
        frm = ieee80211_add_athAdvCap(frm, vap->iv_bss->ni_ath_flags,
                                      vap->iv_bss->ni_ath_defkeyindex);
    } else {
        frm = ieee80211_add_athAdvCap(frm, 0, IEEE80211_INVAL_DEFKEY);
    }

    /* Insert ieee80211_ie_ath_extcap IE to beacon */
    if (ic->ic_ath_extcap)
        frm = ieee80211_add_athextcap(frm, ic->ic_ath_extcap, ic->ic_weptkipaggr_rxdelim);

#ifdef notyet
    if (ni->ni_ath_ie != NULL)    /* XXX */
        frm = ieee80211_add_ath(frm, ni);
#endif
    
    IEEE80211_VAP_LOCK(vap);
    if (vap->iv_opt_ie.length) {
        OS_MEMCPY(frm, vap->iv_opt_ie.ie,
                  vap->iv_opt_ie.length);
        frm += vap->iv_opt_ie.length;
    }

    if (vap->iv_app_ie[IEEE80211_FRAME_TYPE_PROBERESP].length) {
        OS_MEMCPY(frm, vap->iv_app_ie[IEEE80211_FRAME_TYPE_PROBERESP].ie,
                  vap->iv_app_ie[IEEE80211_FRAME_TYPE_PROBERESP].length);
        frm += vap->iv_app_ie[IEEE80211_FRAME_TYPE_PROBERESP].length;
    }

    /* Add the Application IE's */
    frm = ieee80211_mlme_app_ie_append(vap, IEEE80211_FRAME_TYPE_PROBERESP, frm);
    IEEE80211_VAP_UNLOCK(vap);
    
    if (optie != NULL && optielen != 0) {
        OS_MEMCPY(frm, optie, optielen);
        frm += optielen;
    }

    wbuf_set_pktlen(wbuf, (frm - (u_int8_t *)wbuf_header(wbuf)));

    return ieee80211_send_mgmt(vap,ni, wbuf,true);
}