static inline void ol_rx_decap_to_native_wifi( struct ol_txrx_vdev_t *vdev, adf_nbuf_t msdu, struct ol_rx_decap_info_t *info, struct ethernet_hdr_t *ethr_hdr) { struct ieee80211_frame_addr4 *wh; u_int16_t hdsize; /* * we need to remove Qos control field and HT control. * MSFT: http://msdn.microsoft.com/en-us/library/windows/hardware/ff552608(v=vs.85).aspx */ wh = (struct ieee80211_frame_addr4 *)info->hdr; if ( (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS) { hdsize = sizeof(struct ieee80211_frame_addr4); } else { hdsize = sizeof(struct ieee80211_frame); } wh = (struct ieee80211_frame_addr4 *) adf_nbuf_push_head(msdu, hdsize); TXRX_ASSERT2(wh != NULL); TXRX_ASSERT2(hdsize <= info->hdr_len); adf_os_mem_copy ((u_int8_t *)wh, info->hdr, hdsize); /* amsdu subfrm handling if ethr_hdr is not NULL */ if (ethr_hdr != NULL) { switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { case IEEE80211_FC1_DIR_NODS: adf_os_mem_copy(wh->i_addr1, ethr_hdr->dest_addr, ETHERNET_ADDR_LEN); adf_os_mem_copy(wh->i_addr2, ethr_hdr->src_addr, ETHERNET_ADDR_LEN); break; case IEEE80211_FC1_DIR_TODS: adf_os_mem_copy(wh->i_addr2, ethr_hdr->src_addr, ETHERNET_ADDR_LEN); adf_os_mem_copy(wh->i_addr3, ethr_hdr->dest_addr, ETHERNET_ADDR_LEN); break; case IEEE80211_FC1_DIR_FROMDS: adf_os_mem_copy(wh->i_addr1, ethr_hdr->dest_addr, ETHERNET_ADDR_LEN); adf_os_mem_copy(wh->i_addr3, ethr_hdr->src_addr, ETHERNET_ADDR_LEN); break; case IEEE80211_FC1_DIR_DSTODS: adf_os_mem_copy(wh->i_addr3, ethr_hdr->dest_addr, ETHERNET_ADDR_LEN); adf_os_mem_copy(wh->i_addr4, ethr_hdr->src_addr, ETHERNET_ADDR_LEN); break; } } if (IEEE80211_QOS_HAS_SEQ(wh) ) { if (wh->i_fc[1] & IEEE80211_FC1_ORDER) { wh->i_fc[1] &= ~IEEE80211_FC1_ORDER; } wh->i_fc[0] &= ~IEEE80211_FC0_SUBTYPE_QOS; } }
void r12a_fill_tx_desc_raw(struct rtwn_softc *sc, struct ieee80211_node *ni, struct mbuf *m, void *buf, const struct ieee80211_bpf_params *params) { struct ieee80211vap *vap = ni->ni_vap; struct rtwn_vap *uvp = RTWN_VAP(vap); struct ieee80211_frame *wh; struct r12a_tx_desc *txd; uint8_t ridx; int ismcast; /* XXX TODO: 11n checks, matching rtwn_fill_tx_desc() */ wh = mtod(m, struct ieee80211_frame *); ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); ridx = rate2ridx(params->ibp_rate0); /* Fill Tx descriptor. */ txd = (struct r12a_tx_desc *)buf; txd->flags0 |= R12A_FLAGS0_LSG | R12A_FLAGS0_FSG; if (ismcast) txd->flags0 |= R12A_FLAGS0_BMCAST; if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) { txd->txdw4 = htole32(R12A_TXDW4_RETRY_LMT_ENA); txd->txdw4 |= htole32(SM(R12A_TXDW4_RETRY_LMT, params->ibp_try0)); } if (params->ibp_flags & IEEE80211_BPF_RTS) r12a_tx_protection(sc, txd, IEEE80211_PROT_RTSCTS, ridx); if (params->ibp_flags & IEEE80211_BPF_CTS) r12a_tx_protection(sc, txd, IEEE80211_PROT_CTSONLY, ridx); txd->txdw1 |= htole32(SM(R12A_TXDW1_MACID, RTWN_MACID_BC)); txd->txdw1 |= htole32(SM(R12A_TXDW1_QSEL, R12A_TXDW1_QSEL_MGNT)); /* Set TX rate index. */ txd->txdw4 |= htole32(SM(R12A_TXDW4_DATARATE, ridx)); txd->txdw4 |= htole32(SM(R12A_TXDW4_DATARATE_FB_LMT, 0x1f)); txd->txdw6 |= htole32(SM(R21A_TXDW6_MBSSID, uvp->id)); txd->txdw3 |= htole32(R12A_TXDW3_DRVRATE); r12a_tx_raid(sc, txd, ni, ismcast); if (!IEEE80211_QOS_HAS_SEQ(wh)) { /* Use HW sequence numbering for non-QoS frames. */ txd->txdw8 |= htole32(R12A_TXDW8_HWSEQ_EN); txd->txdw3 |= htole32(SM(R12A_TXDW3_SEQ_SEL, uvp->id)); } else { /* Set sequence number. */ txd->txdw9 |= htole32(SM(R12A_TXDW9_SEQ, M_SEQNO_GET(m) % IEEE80211_SEQ_RANGE)); } }
void ieee80211_dump_pkt(struct ieee80211com *ic, const u_int8_t *buf, int len, int rate, int rssi) { const struct ieee80211_frame *wh; int i; wh = (const struct ieee80211_frame *)buf; switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { case IEEE80211_FC1_DIR_NODS: printf("NODS %s", ether_sprintf(wh->i_addr2)); printf("->%s", ether_sprintf(wh->i_addr1)); printf("(%s)", ether_sprintf(wh->i_addr3)); break; case IEEE80211_FC1_DIR_TODS: printf("TODS %s", ether_sprintf(wh->i_addr2)); printf("->%s", ether_sprintf(wh->i_addr3)); printf("(%s)", ether_sprintf(wh->i_addr1)); break; case IEEE80211_FC1_DIR_FROMDS: printf("FRDS %s", ether_sprintf(wh->i_addr3)); printf("->%s", ether_sprintf(wh->i_addr1)); printf("(%s)", ether_sprintf(wh->i_addr2)); break; case IEEE80211_FC1_DIR_DSTODS: printf("DSDS %s", ether_sprintf((u_int8_t *)&wh[1])); printf("->%s", ether_sprintf(wh->i_addr3)); printf("(%s", ether_sprintf(wh->i_addr2)); printf("->%s)", ether_sprintf(wh->i_addr1)); break; } switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { case IEEE80211_FC0_TYPE_DATA: printf(" data"); break; case IEEE80211_FC0_TYPE_MGT: printf(" %s", ieee80211_mgt_subtype_name[ (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) >> IEEE80211_FC0_SUBTYPE_SHIFT]); break; default: printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK); break; } if (IEEE80211_QOS_HAS_SEQ(wh)) { const struct ieee80211_qosframe *qwh = (const struct ieee80211_qosframe *)buf; printf(" QoS [TID %u%s]", qwh->i_qos[0] & IEEE80211_QOS_TID, qwh->i_qos[0] & IEEE80211_QOS_ACKPOLICY ? " ACM" : ""); } if (wh->i_fc[1] & IEEE80211_FC1_PROT) { int off; off = ieee80211_anyhdrspace(ic, wh); printf(" WEP [IV %.02x %.02x %.02x", buf[off+0], buf[off+1], buf[off+2]); if (buf[off+IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) printf(" %.02x %.02x %.02x", buf[off+4], buf[off+5], buf[off+6]); printf(" KID %u]", buf[off+IEEE80211_WEP_IVLEN] >> 6); }
int iwl_tx_data(struct iwl_softc *sc, struct mbuf *m, struct ieee80211_node *ni) { struct iwl_ops *ops = &sc->ops; const struct ieee80211_txparam *tp; struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct iwl_node *wn = (void *)ni; struct iwl_tx_ring *ring; struct iwl_tx_desc *desc; struct iwl_tx_data *data; struct iwl_tx_cmd *cmd; struct iwl_cmd_data *tx; struct ieee80211_frame *wh; struct ieee80211_key *k = NULL; struct mbuf *m1; uint32_t flags; uint16_t qos; u_int hdrlen; bus_dma_segment_t *seg, segs[IWL_MAX_SCATTER]; uint8_t tid, ridx, txant, type; int ac, i, totlen, error, pad, nsegs = 0, rate; struct iwl_vap *ivp = IWL_VAP(vap); IWL_LOCK_ASSERT(sc); wh = mtod(m, struct ieee80211_frame *); hdrlen = ieee80211_anyhdrsize(wh); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; /* Select EDCA Access Category and TX ring for this frame. */ if (IEEE80211_QOS_HAS_SEQ(wh)) { qos = ((const struct ieee80211_qosframe *)wh)->i_qos[0]; tid = qos & IEEE80211_QOS_TID; } else { qos = 0; tid = 0; } if(ivp->ctx == IWL_RXON_PAN_CTX) ac = iwl_pan_ac_to_queue[M_WME_GETAC(m)]; else ac = iwl_bss_ac_to_queue[M_WME_GETAC(m)]; if (IEEE80211_QOS_HAS_SEQ(wh) && IEEE80211_AMPDU_RUNNING(&ni->ni_tx_ampdu[ac])) { struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[ac]; ring = &sc->txq[*(int *)tap->txa_private]; *(uint16_t *)wh->i_seq = htole16(ni->ni_txseqs[tid] << IEEE80211_SEQ_SEQ_SHIFT); ni->ni_txseqs[tid]++; } else ring = &sc->txq[ac]; desc = &ring->desc[ring->cur]; data = &ring->data[ring->cur]; /* Choose a TX rate index. */ tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; if (type == IEEE80211_FC0_TYPE_MGT) rate = tp->mgmtrate; else if (IEEE80211_IS_MULTICAST(wh->i_addr1)) rate = tp->mcastrate; else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) rate = tp->ucastrate; else rate = ni->ni_txrate; ridx = ic->ic_rt->rateCodeToIndex[rate]; /* Encrypt the frame if need be. */ if (wh->i_fc[1] & IEEE80211_FC1_WEP) { /* Retrieve key for TX. */ k = ieee80211_crypto_encap(ni, m); if (k == NULL) { m_freem(m); return ENOBUFS; } /* 802.11 header may have moved. */ wh = mtod(m, struct ieee80211_frame *); }
/* * Process a received frame. The node associated with the sender * should be supplied. If nothing was found in the node table then * the caller is assumed to supply a reference to ic_bss instead. * The RSSI and a timestamp are also supplied. The RSSI data is used * during AP scanning to select a AP to associate with; it can have * any units so long as values have consistent units and higher values * mean ``better signal''. The receive timestamp is currently not used * by the 802.11 layer. */ int ieee80211_input(ieee80211com_t *ic, mblk_t *mp, struct ieee80211_node *in, int32_t rssi, uint32_t rstamp) { struct ieee80211_frame *wh; struct ieee80211_key *key; uint8_t *bssid; int hdrspace; int len; uint16_t rxseq; uint8_t dir; uint8_t type; uint8_t subtype; uint8_t tid; uint8_t qos; if (mp->b_flag & M_AMPDU) { /* * Fastpath for A-MPDU reorder q resubmission. Frames * w/ M_AMPDU marked have already passed through here * but were received out of order and been held on the * reorder queue. When resubmitted they are marked * with the M_AMPDU flag and we can bypass most of the * normal processing. */ IEEE80211_LOCK(ic); wh = (struct ieee80211_frame *)mp->b_rptr; type = IEEE80211_FC0_TYPE_DATA; dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; subtype = IEEE80211_FC0_SUBTYPE_QOS; hdrspace = ieee80211_hdrspace(ic, wh); /* optimize */ /* clear driver/net80211 flags before passing up */ mp->b_flag &= ~M_AMPDU; goto resubmit_ampdu; } ASSERT(in != NULL); in->in_inact = in->in_inact_reload; type = (uint8_t)-1; /* undefined */ len = MBLKL(mp); if (len < sizeof (struct ieee80211_frame_min)) { ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_input: " "too short (1): len %u", len); goto out; } /* * Bit of a cheat here, we use a pointer for a 3-address * frame format but don't reference fields past outside * ieee80211_frame_min w/o first validating the data is * present. */ wh = (struct ieee80211_frame *)mp->b_rptr; if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != IEEE80211_FC0_VERSION_0) { ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_input: " "discard pkt with wrong version %x", wh->i_fc[0]); goto out; } dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; IEEE80211_LOCK(ic); if (!(ic->ic_flags & IEEE80211_F_SCAN)) { switch (ic->ic_opmode) { case IEEE80211_M_STA: bssid = wh->i_addr2; if (!IEEE80211_ADDR_EQ(bssid, in->in_bssid)) goto out_exit_mutex; break; case IEEE80211_M_IBSS: case IEEE80211_M_AHDEMO: if (dir != IEEE80211_FC1_DIR_NODS) { bssid = wh->i_addr1; } else if (type == IEEE80211_FC0_TYPE_CTL) { bssid = wh->i_addr1; } else { if (len < sizeof (struct ieee80211_frame)) { ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_input: too short(2):" "len %u\n", len); goto out_exit_mutex; } bssid = wh->i_addr3; } if (type != IEEE80211_FC0_TYPE_DATA) break; /* * Data frame, validate the bssid. */ if (!IEEE80211_ADDR_EQ(bssid, ic->ic_bss->in_bssid) && !IEEE80211_ADDR_EQ(bssid, wifi_bcastaddr)) { /* not interested in */ ieee80211_dbg(IEEE80211_MSG_INPUT, "ieee80211_input: not to bss %s\n", ieee80211_macaddr_sprintf(bssid)); goto out_exit_mutex; } /* * For adhoc mode we cons up a node when it doesn't * exist. This should probably done after an ACL check. */ if (in == ic->ic_bss && ic->ic_opmode != IEEE80211_M_HOSTAP && !IEEE80211_ADDR_EQ(wh->i_addr2, in->in_macaddr)) { /* * Fake up a node for this newly * discovered member of the IBSS. */ in = ieee80211_fakeup_adhoc_node(&ic->ic_sta, wh->i_addr2); if (in == NULL) { /* NB: stat kept for alloc failure */ goto out_exit_mutex; } } break; default: goto out_exit_mutex; } in->in_rssi = (uint8_t)rssi; in->in_rstamp = rstamp; if (!(type & IEEE80211_FC0_TYPE_CTL)) { if (IEEE80211_QOS_HAS_SEQ(wh)) { tid = ((struct ieee80211_qosframe *)wh)-> i_qos[0] & IEEE80211_QOS_TID; if (TID_TO_WME_AC(tid) >= WME_AC_VI) ic->ic_wme.wme_hipri_traffic++; tid++; } else { tid = IEEE80211_NONQOS_TID; } rxseq = LE_16(*(uint16_t *)wh->i_seq); if ((in->in_flags & IEEE80211_NODE_HT) == 0 && (wh->i_fc[1] & IEEE80211_FC1_RETRY) && (rxseq - in->in_rxseqs[tid]) <= 0) { /* duplicate, discard */ ieee80211_dbg(IEEE80211_MSG_INPUT, "ieee80211_input: duplicate", "seqno <%u,%u> fragno <%u,%u> tid %u", rxseq >> IEEE80211_SEQ_SEQ_SHIFT, in->in_rxseqs[tid] >> IEEE80211_SEQ_SEQ_SHIFT, rxseq & IEEE80211_SEQ_FRAG_MASK, in->in_rxseqs[tid] & IEEE80211_SEQ_FRAG_MASK, tid); ic->ic_stats.is_rx_dups++; goto out_exit_mutex; } in->in_rxseqs[tid] = rxseq; } }
/* * 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; }
/* * 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; }
void r12a_fill_tx_desc(struct rtwn_softc *sc, struct ieee80211_node *ni, struct mbuf *m, void *buf, uint8_t ridx, int maxretry) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = ni->ni_vap; struct rtwn_vap *uvp = RTWN_VAP(vap); struct ieee80211_frame *wh; struct r12a_tx_desc *txd; enum ieee80211_protmode prot; uint8_t type, tid, qos, qsel; int hasqos, ismcast, macid; wh = mtod(m, struct ieee80211_frame *); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; hasqos = IEEE80211_QOS_HAS_SEQ(wh); ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); /* Select TX ring for this frame. */ if (hasqos) { qos = ((const struct ieee80211_qosframe *)wh)->i_qos[0]; tid = qos & IEEE80211_QOS_TID; } else { qos = 0; tid = 0; } /* Fill Tx descriptor. */ txd = (struct r12a_tx_desc *)buf; txd->flags0 |= R12A_FLAGS0_LSG | R12A_FLAGS0_FSG; if (ismcast) txd->flags0 |= R12A_FLAGS0_BMCAST; if (!ismcast) { /* Unicast frame, check if an ACK is expected. */ if (!qos || (qos & IEEE80211_QOS_ACKPOLICY) != IEEE80211_QOS_ACKPOLICY_NOACK) { txd->txdw4 = htole32(R12A_TXDW4_RETRY_LMT_ENA); txd->txdw4 |= htole32(SM(R12A_TXDW4_RETRY_LMT, maxretry)); } struct rtwn_node *un = RTWN_NODE(ni); macid = un->id; if (type == IEEE80211_FC0_TYPE_DATA) { qsel = tid % RTWN_MAX_TID; if (m->m_flags & M_AMPDU_MPDU) { txd->txdw2 |= htole32(R12A_TXDW2_AGGEN); txd->txdw2 |= htole32(SM(R12A_TXDW2_AMPDU_DEN, vap->iv_ampdu_density)); txd->txdw3 |= htole32(SM(R12A_TXDW3_MAX_AGG, 0x1f)); /* XXX */ } else txd->txdw2 |= htole32(R12A_TXDW2_AGGBK); if (sc->sc_ratectl == RTWN_RATECTL_NET80211) { txd->txdw2 |= htole32(R12A_TXDW2_SPE_RPT); sc->sc_tx_n_active++; } if (RTWN_RATE_IS_CCK(ridx) && ridx != RTWN_RIDX_CCK1 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) txd->txdw5 |= htole32(R12A_TXDW5_DATA_SHORT); prot = IEEE80211_PROT_NONE; if (ridx >= RTWN_RIDX_MCS(0)) { r12a_tx_set_ht40(sc, txd, ni); r12a_tx_set_sgi(sc, txd, ni); prot = ic->ic_htprotmode; } else if (ic->ic_flags & IEEE80211_F_USEPROT) prot = ic->ic_protmode; /* XXX fix last comparison for A-MSDU (in net80211) */ /* XXX A-MPDU? */ if (m->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold && vap->iv_rtsthreshold != IEEE80211_RTS_MAX) prot = IEEE80211_PROT_RTSCTS; if (prot != IEEE80211_PROT_NONE) r12a_tx_protection(sc, txd, prot, ridx); } else /* IEEE80211_FC0_TYPE_MGT */ qsel = R12A_TXDW1_QSEL_MGNT; } else { macid = RTWN_MACID_BC; qsel = R12A_TXDW1_QSEL_MGNT; } txd->txdw1 |= htole32(SM(R12A_TXDW1_QSEL, qsel)); txd->txdw1 |= htole32(SM(R12A_TXDW1_MACID, macid)); txd->txdw4 |= htole32(SM(R12A_TXDW4_DATARATE, ridx)); /* Data rate fallback limit (max). */ txd->txdw4 |= htole32(SM(R12A_TXDW4_DATARATE_FB_LMT, 0x1f)); /* XXX recheck for non-21au */ txd->txdw6 |= htole32(SM(R21A_TXDW6_MBSSID, uvp->id)); r12a_tx_raid(sc, txd, ni, ismcast); /* Force this rate if needed. */ if (sc->sc_ratectl != RTWN_RATECTL_FW) txd->txdw3 |= htole32(R12A_TXDW3_DRVRATE); if (!hasqos) { /* Use HW sequence numbering for non-QoS frames. */ txd->txdw8 |= htole32(R12A_TXDW8_HWSEQ_EN); txd->txdw3 |= htole32(SM(R12A_TXDW3_SEQ_SEL, uvp->id)); } else { uint16_t seqno; if (m->m_flags & M_AMPDU_MPDU) { seqno = ni->ni_txseqs[tid]; /* NB: clear Fragment Number field. */ *(uint16_t *)wh->i_seq = 0; ni->ni_txseqs[tid]++; } else seqno = M_SEQNO_GET(m) % IEEE80211_SEQ_RANGE; /* Set sequence number. */ txd->txdw9 |= htole32(SM(R12A_TXDW9_SEQ, seqno)); } }