Esempio n. 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;
}
Esempio n. 2
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;
}