/* * 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); }
/* 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); }
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; }
/* 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); }
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; } } }
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)); }
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; }
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; }
/* 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 }
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; }
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; }
/*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; }
/* * 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); }
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)); }
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); } }
/* * 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; }
/* * 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; }
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); } }
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); } }
/* * 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); } }
/* * 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; }
/* * 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; }
/* * 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 */ }
/* * 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; }
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 {
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; }
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; }
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; }
/* * 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); } } } }
/* * 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); }