Beispiel #1
0
/*
 * This function determines whether the received frame is a valid UAPSD trigger.
 * Called from interrupt context or DPC context depending on parameter isr_context.
 */
bool
ath_net80211_check_uapsdtrigger(ieee80211_handle_t ieee, struct ieee80211_qosframe *qwh, u_int16_t keyix, bool isr_context)
{
    struct ieee80211com *ic = NET80211_HANDLE(ieee);
    struct ath_softc_net80211 *scn = ATH_SOFTC_NET80211(ic);
    struct ieee80211_node *ni;
    int tid, ac;
    u_int16_t frame_seq;
    int queue_depth;
    bool isapsd = false;

    /*
     * Locate the node for sender
     */
    IEEE80211_KEYMAP_LOCK(scn);
    ni = (keyix != HAL_RXKEYIX_INVALID) ? scn->sc_keyixmap[keyix] : NULL;
    if (ni == NULL) {
        IEEE80211_KEYMAP_UNLOCK(scn);
        /*
         * No key index or no entry, do a lookup
         */
        if (isr_context) {
            ni = ieee80211_find_rxnode_nolock(ic, (struct ieee80211_frame_min *)qwh);
        }
        else {
            ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)qwh);
        }
        if (ni == NULL) {
            return isapsd;
        }
    } else {
        ieee80211_ref_node(ni);
        IEEE80211_KEYMAP_UNLOCK(scn);
    }


    if (!(ni->ni_flags & IEEE80211_NODE_UAPSD))
        goto end;

    if (ni->ni_flags & IEEE80211_NODE_UAPSD_SP)
        goto end;    
    /*
     * Must deal with change of state here, since otherwise there would
     * be a race (on two quick frames from STA) between this code and the
     * tasklet where we would:
     *   - miss a trigger on entry to PS if we're already trigger hunting
     *   - generate spurious SP on exit (due to frame following exit frame)
     */
    if ((((qwh->i_fc[1] & IEEE80211_FC1_PWR_MGT) == IEEE80211_FC1_PWR_MGT) ^
        ((ni->ni_flags & IEEE80211_NODE_UAPSD_TRIG) == IEEE80211_NODE_UAPSD_TRIG)))
    {
        ni->ni_flags &= ~IEEE80211_NODE_UAPSD_SP;

        if (qwh->i_fc[1] & IEEE80211_FC1_PWR_MGT) {
            WME_UAPSD_NODE_TRIGSEQINIT(ni);
            ni->ni_stats.ns_uapsd_triggerenabled++;
            ni->ni_flags |= IEEE80211_NODE_UAPSD_TRIG;
        } else {
            /*
             * Node transitioned from UAPSD -> Active state. Flush out UAPSD frames
             */
            ni->ni_stats.ns_uapsd_active++;
            ni->ni_flags &= ~IEEE80211_NODE_UAPSD_TRIG;
            scn->sc_ops->process_uapsd_trigger(scn->sc_dev,
                                               ATH_NODE_NET80211(ni)->an_sta,
                                               WME_UAPSD_NODE_MAXQDEPTH, 0, 1, WME_UAPSD_NODE_MAXQDEPTH);
        }

        goto end;
    }


    /*
     * Check for a valid trigger frame i.e. QoS Data or QoS NULL
     */
      if ( ((qwh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) !=
              IEEE80211_FC0_TYPE_DATA ) ||
             !(qwh->i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS) ) {
          goto end;
      }

    if (((qwh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) &&
       (((qwh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_QOS) ||
        ((qwh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_QOS_NULL)))
    {
        tid = qwh->i_qos[0] & IEEE80211_QOS_TID;
        ac = TID_TO_WME_AC(tid);

        isapsd = true;

        if (WME_UAPSD_AC_CAN_TRIGGER(ac, ni)) {
            /*
             * Detect duplicate triggers and drop if so.
             */
            frame_seq = le16toh(*(u_int16_t *)qwh->i_seq);
            if ((qwh->i_fc[1] & IEEE80211_FC1_RETRY) &&
                frame_seq == ni->ni_uapsd_trigseq[ac])
            {
                ni->ni_stats.ns_uapsd_duptriggers++;
                goto end;
            }

            /*
             * SP in progress for this node, discard trigger.
             */
            if (ni->ni_flags & IEEE80211_NODE_UAPSD_SP) {
                ni->ni_stats.ns_uapsd_ignoretriggers++;
                goto end;
            }

            /* start the SP */
            ni->ni_stats.ns_uapsd_triggers++;
            ni->ni_flags |= IEEE80211_NODE_UAPSD_SP;
            ni->ni_uapsd_trigseq[ac] = frame_seq;

            queue_depth = scn->sc_ops->process_uapsd_trigger(scn->sc_dev,
                                               ATH_NODE_NET80211(ni)->an_sta,
                                               ni->ni_uapsd_maxsp, ac, 0, WME_UAPSD_NODE_MAXQDEPTH);
            if (!queue_depth &&
                (ni->ni_vap->iv_set_tim != NULL) &&
                IEEE80211_NODE_UAPSD_USETIM(ni))
            {
                ni->ni_vap->iv_set_tim(ni, 0);
            }
        }
    }
end:
    ieee80211_free_node(ni);
    return isapsd;
}
Beispiel #2
0
		 */
		tsfadjust = ni->ni_intval *
		    (ATH_BCBUF - avp->av_bslot) / ATH_BCBUF;
		tsfadjust = htole64(tsfadjust << 10);	/* TU -> TSF */

		DPRINTF(sc, ATH_DEBUG_BEACON,
		    "%s: %s beacons bslot %d intval %u tsfadjust %llu\n",
		    __func__, sc->sc_stagbeacons ? "stagger" : "burst",
		    avp->av_bslot, ni->ni_intval,
		    (long long unsigned) le64toh(tsfadjust));

		wh = mtod(m, struct ieee80211_frame *);
		memcpy(&wh[1], &tsfadjust, sizeof(tsfadjust));
	}
	bf->bf_m = m;
	bf->bf_node = ieee80211_ref_node(ni);

	return 0;
}

/*
 * Setup the beacon frame for transmit.
 */
static void
ath_beacon_setup(struct ath_softc *sc, struct ath_buf *bf)
{
#define	USE_SHPREAMBLE(_ic) \
	(((_ic)->ic_flags & (IEEE80211_F_SHPREAMBLE | IEEE80211_F_USEBARKER))\
		== IEEE80211_F_SHPREAMBLE)
	struct ieee80211_node *ni = bf->bf_node;
	struct ieee80211com *ic = ni->ni_ic;
Beispiel #3
0
static int
hwmp_send_action(struct ieee80211_node *ni,
    const uint8_t sa[IEEE80211_ADDR_LEN],
    const uint8_t da[IEEE80211_ADDR_LEN],
    uint8_t *ie, size_t len)
{
	struct ieee80211vap *vap = ni->ni_vap;
	struct ieee80211com *ic = ni->ni_ic;
	struct ieee80211_bpf_params params;
	struct mbuf *m;
	uint8_t *frm;

	if (vap->iv_state == IEEE80211_S_CAC) {
		IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni,
		    "block %s frame in CAC state", "HWMP action");
		vap->iv_stats.is_tx_badstate++;
		return EIO;	/* XXX */
	}

	KASSERT(ni != NULL, ("null node"));
	/*
	 * Hold a reference on the node so it doesn't go away until after
	 * the xmit is complete all the way in the driver.  On error we
	 * will remove our reference.
	 */
#ifdef IEEE80211_DEBUG_REFCNT
	IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
	    "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n",
	    __func__, __LINE__,
	    ni, ether_sprintf(ni->ni_macaddr),
	    ieee80211_node_refcnt(ni)+1);
#endif
	ieee80211_ref_node(ni);

	m = ieee80211_getmgtframe(&frm,
	    ic->ic_headroom + sizeof(struct ieee80211_frame),
	    sizeof(struct ieee80211_action) + len
	);
	if (m == NULL) {
		ieee80211_free_node(ni);
		vap->iv_stats.is_tx_nobuf++;
		return ENOMEM;
	}
	*frm++ = IEEE80211_ACTION_CAT_MESHPATH;
	*frm++ = IEEE80211_ACTION_MESHPATH_SEL;
	switch (*ie) {
	case IEEE80211_ELEMID_MESHPREQ:
		frm = hwmp_add_meshpreq(frm,
		    (struct ieee80211_meshpreq_ie *)ie);
		break;
	case IEEE80211_ELEMID_MESHPREP:
		frm = hwmp_add_meshprep(frm,
		    (struct ieee80211_meshprep_ie *)ie);
		break;
	case IEEE80211_ELEMID_MESHPERR:
		frm = hwmp_add_meshperr(frm,
		    (struct ieee80211_meshperr_ie *)ie);
		break;
	case IEEE80211_ELEMID_MESHRANN:
		frm = hwmp_add_meshrann(frm,
		    (struct ieee80211_meshrann_ie *)ie);
		break;
	}

	m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
	M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT);
	if (m == NULL) {
		ieee80211_free_node(ni);
		vap->iv_stats.is_tx_nobuf++;
		return ENOMEM;
	}
	ieee80211_send_setup(ni, m,
	    IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_ACTION,
	    IEEE80211_NONQOS_TID, sa, da, sa);

	m->m_flags |= M_ENCAP;		/* mark encapsulated */
	IEEE80211_NODE_STAT(ni, tx_mgmt);

	memset(&params, 0, sizeof(params));
	params.ibp_pri = WME_AC_VO;
	params.ibp_rate0 = ni->ni_txparms->mgmtrate;
	if (IEEE80211_IS_MULTICAST(da))
		params.ibp_try0 = 1;
	else
		params.ibp_try0 = ni->ni_txparms->maxretry;
	params.ibp_power = ni->ni_txpower;
	return ic->ic_raw_xmit(ni, m, &params);
}
Beispiel #4
0
/*
 * This function is called for each frame received on the high priority queue.
 * If the hardware has classified this frame as a UAPSD trigger, we locate the node
 * and deliver data.
 * For non-trigger frames, we check for PM transition.
 * Called from interrupt context.
 */
void
ath_net80211_uapsd_deliverdata(ieee80211_handle_t        ieee,
                               struct ieee80211_qosframe *qwh,
                               u_int16_t                 keyix,
                               u_int8_t                  is_trig,
                               bool                      isr_context)
{
    struct ieee80211com          *ic = NET80211_HANDLE(ieee);
    struct ath_softc_net80211    *scn = ATH_SOFTC_NET80211(ic);
    struct ieee80211_node        *ni;
    int                          tid, ac;
    u_int16_t                    frame_seq;
    int                          queue_depth;
    bool                         sent_eosp = false;

    UNREFERENCED_PARAMETER(isr_context);

    /*
     * Locate the node for sender
     */
    IEEE80211_KEYMAP_LOCK(scn);
    ni = (keyix != HAL_RXKEYIX_INVALID) ? scn->sc_keyixmap[keyix] : NULL;
    IEEE80211_KEYMAP_UNLOCK(scn);
    if (ni == NULL) {
        /*
         * No key index or no entry, do a lookup
         */
        ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)qwh);
        if (ni == NULL) {
            return;
        }
    } else {
        ieee80211_ref_node(ni);
    }

    if (!(ni->ni_flags & IEEE80211_NODE_UAPSD) || (ni->ni_flags & IEEE80211_NODE_ATH_PAUSED))
        goto end;

    /* We cannot have a PM state change if this is a trigger frame */
    if (!is_trig)
    {
        /*
         * Must deal with change of state here, since otherwise there would
         * be a race (on two quick frames from STA) between this code and the
         * tasklet where we would:
         *   - miss a trigger on entry to PS if we're already trigger hunting
         *   - generate spurious SP on exit (due to frame following exit frame)
         */
        if ((((qwh->i_fc[1] & IEEE80211_FC1_PWR_MGT) == IEEE80211_FC1_PWR_MGT) ^
            ((ni->ni_flags & IEEE80211_NODE_UAPSD_TRIG) == IEEE80211_NODE_UAPSD_TRIG)))
        {
            ni->ni_flags &= ~IEEE80211_NODE_UAPSD_SP;

            if (qwh->i_fc[1] & IEEE80211_FC1_PWR_MGT) {
                WME_UAPSD_NODE_TRIGSEQINIT(ni);
                ni->ni_stats.ns_uapsd_triggerenabled++;
                ni->ni_flags |= IEEE80211_NODE_UAPSD_TRIG;
            } else {
                /*
                 * Node transitioned from UAPSD -> Active state. Flush out UAPSD frames
                 */
                ni->ni_stats.ns_uapsd_active++;
                ni->ni_flags &= ~IEEE80211_NODE_UAPSD_TRIG;
                scn->sc_ops->process_uapsd_trigger(scn->sc_dev,
                                                   ATH_NODE_NET80211(ni)->an_sta,
                                                   WME_UAPSD_NODE_MAXQDEPTH, 0, 1, &sent_eosp, WME_UAPSD_NODE_MAXQDEPTH);
            }

            goto end;
        }
    } else {  /* Is UAPSD trigger */
        tid = qwh->i_qos[0] & IEEE80211_QOS_TID;
        ac = TID_TO_WME_AC(tid);

        if (WME_UAPSD_AC_CAN_TRIGGER(ac, ni)) {
            /*
             * Detect duplicate triggers and drop if so.
             */
            frame_seq = le16toh(*(u_int16_t *)qwh->i_seq);
            if ((qwh->i_fc[1] & IEEE80211_FC1_RETRY) &&
                frame_seq == ni->ni_uapsd_trigseq[ac])
            {
                ni->ni_stats.ns_uapsd_duptriggers++;
                DPRINTF(scn, ATH_DEBUG_UAPSD, "%s : Drop duplicate trigger\n", __func__);
                goto end;
            }

            if (IEEE80211_IS_TDLS_NODE(ni)) {
            /*
                 * TDLS defines any QoS frame with EOSP
                 * set to be a non-trigger frame. Therefore,
                 * ignore trigger.
                 */
                if(qwh->i_qos[0] & IEEE80211_QOS_EOSP) {
                    ni->ni_stats.ns_uapsd_ignoretriggers++;
                    DPRINTF(scn, ATH_DEBUG_UAPSD, "%s : TDLS QOS frame with EOSP; ignore trigger\n", __func__);
                    goto end;
                }
            }

            /*
             * SP in progress for this node, discard trigger.
             */
            if (ni->ni_flags & IEEE80211_NODE_UAPSD_SP) {
                ni->ni_stats.ns_uapsd_ignoretriggers++;
                DPRINTF(scn, ATH_DEBUG_UAPSD, "%s : SP in-progress; ignore trigger\n", __func__);
                goto end;
            }

            ni->ni_stats.ns_uapsd_triggers++;

            DPRINTF(scn, ATH_DEBUG_UAPSD, "%s : Start SP\n", __func__);

            queue_depth = scn->sc_ops->process_uapsd_trigger(scn->sc_dev,
                                               ATH_NODE_NET80211(ni)->an_sta,
                                               ni->ni_uapsd_maxsp, ac, 0, &sent_eosp, WME_UAPSD_NODE_MAXQDEPTH);
            if (queue_depth == -1)
                goto end;

            /* start the SP */
            ni->ni_flags |= IEEE80211_NODE_UAPSD_SP;
            ni->ni_uapsd_trigseq[ac] = frame_seq;

            if (IEEE80211_IS_TDLS_NODE(ni)) {
                if (sent_eosp) {
                    ni->ni_flags &= ~IEEE80211_NODE_UAPSD_SP;
                    DPRINTF(scn, ATH_DEBUG_UAPSD, "%s : TDLS; End SP\n", __func__);
                }
            }
            else {
            if (!queue_depth &&
                (ni->ni_vap->iv_set_tim != NULL) &&
                IEEE80211_NODE_UAPSD_USETIM(ni))
            {
                ni->ni_vap->iv_set_tim(ni, 0, isr_context);
            }
            }
        }
    }
end:
    ieee80211_free_node(ni);
    return;
}
Beispiel #5
0
/*
 * Allocate a beacon frame and fillin the appropriate bits.
 */
struct sk_buff *
ieee80211_beacon_alloc(struct ieee80211_node *ni,
	struct ieee80211_beacon_offsets *bo)
{
	struct ieee80211vap *vap = ni->ni_vap;
	struct ieee80211com *ic = ni->ni_ic;
	struct ieee80211_frame *wh;
	struct sk_buff *skb;
	int pktlen;
	u_int8_t *frm;
	struct ieee80211_rateset *rs;

	/*
	 * beacon frame format
	 *	[8] time stamp
	 *	[2] beacon interval
	 *	[2] capability information
	 *	[tlv] ssid
	 *	[tlv] supported rates
	 *	[7] FH/DS parameter set
	 *	[tlv] IBSS/TIM parameter set
	 *	[tlv] country code 
	 *	[3] power constraint
	 *	[5] channel switch announcement
	 *	[3] extended rate phy (ERP)
	 *	[tlv] extended supported rates
	 *	[tlv] WME parameters
	 *	[tlv] WPA/RSN parameters
	 *	[tlv] Atheros Advanced Capabilities
	 *	[tlv] AtherosXR parameters
	 * XXX Vendor-specific OIDs (e.g. Atheros)
	 * NB: we allocate the max space required for the TIM bitmap.
	 */
	rs = &ni->ni_rates;
	pktlen = 8					/* time stamp */
		 + sizeof(u_int16_t)			/* beacon interval */
		 + sizeof(u_int16_t)			/* capability information */
		 + 2 + ni->ni_esslen			/* ssid */
		 + 2 + IEEE80211_RATE_SIZE		/* supported rates */
		 + 7 					/* FH/DS parameters max(7,3) */
		 + 2 + 4 + vap->iv_tim_len		/* IBSS/TIM parameter set*/
		 + ic->ic_country_ie.country_len + 2	/* country code */
		 + 3					/* power constraint */
		 + 5					/* channel switch announcement */
		 + 3					/* ERP */
		 + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) /* Ext. Supp. Rates */
		 + (vap->iv_caps & IEEE80211_C_WME ?	/* WME */
			sizeof(struct ieee80211_wme_param) : 0)
		 + (vap->iv_caps & IEEE80211_C_WPA ?	/* WPA 1+2 */
			2 * sizeof(struct ieee80211_ie_wpa) : 0)
		 + sizeof(struct ieee80211_ie_athAdvCap)
#ifdef ATH_SUPERG_XR
		 + (ic->ic_ath_cap & IEEE80211_ATHC_XR ?	/* XR */
			sizeof(struct ieee80211_xr_param) : 0)
#endif
		 ;
	skb = ieee80211_getmgtframe(&frm, pktlen);
	if (skb == NULL) {
		IEEE80211_NOTE(vap, IEEE80211_MSG_ANY, ni,
			"%s: cannot get buf; size %u", __func__, pktlen);
		vap->iv_stats.is_tx_nobuf++;
		return NULL;
	}

	SKB_NI(skb) = ieee80211_ref_node(ni);

	frm = ieee80211_beacon_init(ni, bo, frm);

	skb_trim(skb, frm - skb->data);

	wh = (struct ieee80211_frame *)
		skb_push(skb, sizeof(struct ieee80211_frame));
	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
		IEEE80211_FC0_SUBTYPE_BEACON;
	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
	wh->i_dur = 0;
	IEEE80211_ADDR_COPY(wh->i_addr1, ic->ic_dev->broadcast);
	IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr);
	IEEE80211_ADDR_COPY(wh->i_addr3, vap->iv_bssid);
	IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC,
			  "%s: beacon bssid:" MAC_FMT "\n",
			  __func__, MAC_ADDR(wh->i_addr3));
	*(u_int16_t *)wh->i_seq = 0;

	return skb;
}