linux/net/mac80211/wme.c
<<
>>
Prefs
   1/*
   2 * Copyright 2004, Instant802 Networks, Inc.
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 as
   6 * published by the Free Software Foundation.
   7 */
   8
   9#include <linux/netdevice.h>
  10#include <linux/skbuff.h>
  11#include <linux/module.h>
  12#include <linux/if_arp.h>
  13#include <linux/types.h>
  14#include <net/ip.h>
  15#include <net/pkt_sched.h>
  16
  17#include <net/mac80211.h>
  18#include "ieee80211_i.h"
  19#include "wme.h"
  20
  21/* Default mapping in classifier to work with default
  22 * queue setup.
  23 */
  24const int ieee802_1d_to_ac[8] = {
  25        IEEE80211_AC_BE,
  26        IEEE80211_AC_BK,
  27        IEEE80211_AC_BK,
  28        IEEE80211_AC_BE,
  29        IEEE80211_AC_VI,
  30        IEEE80211_AC_VI,
  31        IEEE80211_AC_VO,
  32        IEEE80211_AC_VO
  33};
  34
  35static int wme_downgrade_ac(struct sk_buff *skb)
  36{
  37        switch (skb->priority) {
  38        case 6:
  39        case 7:
  40                skb->priority = 5; /* VO -> VI */
  41                return 0;
  42        case 4:
  43        case 5:
  44                skb->priority = 3; /* VI -> BE */
  45                return 0;
  46        case 0:
  47        case 3:
  48                skb->priority = 2; /* BE -> BK */
  49                return 0;
  50        default:
  51                return -1;
  52        }
  53}
  54
  55static u16 ieee80211_downgrade_queue(struct ieee80211_sub_if_data *sdata,
  56                                     struct sk_buff *skb)
  57{
  58        /* in case we are a client verify acm is not set for this ac */
  59        while (unlikely(sdata->wmm_acm & BIT(skb->priority))) {
  60                if (wme_downgrade_ac(skb)) {
  61                        /*
  62                         * This should not really happen. The AP has marked all
  63                         * lower ACs to require admission control which is not
  64                         * a reasonable configuration. Allow the frame to be
  65                         * transmitted using AC_BK as a workaround.
  66                         */
  67                        break;
  68                }
  69        }
  70
  71        /* look up which queue to use for frames with this 1d tag */
  72        return ieee802_1d_to_ac[skb->priority];
  73}
  74
  75/* Indicate which queue to use for this fully formed 802.11 frame */
  76u16 ieee80211_select_queue_80211(struct ieee80211_sub_if_data *sdata,
  77                                 struct sk_buff *skb,
  78                                 struct ieee80211_hdr *hdr)
  79{
  80        struct ieee80211_local *local = sdata->local;
  81        u8 *p;
  82
  83        if (local->hw.queues < IEEE80211_NUM_ACS)
  84                return 0;
  85
  86        if (!ieee80211_is_data(hdr->frame_control)) {
  87                skb->priority = 7;
  88                return ieee802_1d_to_ac[skb->priority];
  89        }
  90        if (!ieee80211_is_data_qos(hdr->frame_control)) {
  91                skb->priority = 0;
  92                return ieee802_1d_to_ac[skb->priority];
  93        }
  94
  95        p = ieee80211_get_qos_ctl(hdr);
  96        skb->priority = *p & IEEE80211_QOS_CTL_TAG1D_MASK;
  97
  98        return ieee80211_downgrade_queue(sdata, skb);
  99}
 100
 101/* Indicate which queue to use. */
 102u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
 103                           struct sk_buff *skb)
 104{
 105        struct ieee80211_local *local = sdata->local;
 106        struct sta_info *sta = NULL;
 107        const u8 *ra = NULL;
 108        bool qos = false;
 109
 110        if (local->hw.queues < IEEE80211_NUM_ACS || skb->len < 6) {
 111                skb->priority = 0; /* required for correct WPA/11i MIC */
 112                return 0;
 113        }
 114
 115        rcu_read_lock();
 116        switch (sdata->vif.type) {
 117        case NL80211_IFTYPE_AP_VLAN:
 118                sta = rcu_dereference(sdata->u.vlan.sta);
 119                if (sta) {
 120                        qos = test_sta_flag(sta, WLAN_STA_WME);
 121                        break;
 122                }
 123        case NL80211_IFTYPE_AP:
 124                ra = skb->data;
 125                break;
 126        case NL80211_IFTYPE_WDS:
 127                ra = sdata->u.wds.remote_addr;
 128                break;
 129#ifdef CONFIG_MAC80211_MESH
 130        case NL80211_IFTYPE_MESH_POINT:
 131                qos = true;
 132                break;
 133#endif
 134        case NL80211_IFTYPE_STATION:
 135                ra = sdata->u.mgd.bssid;
 136                break;
 137        case NL80211_IFTYPE_ADHOC:
 138                ra = skb->data;
 139                break;
 140        default:
 141                break;
 142        }
 143
 144        if (!sta && ra && !is_multicast_ether_addr(ra)) {
 145                sta = sta_info_get(sdata, ra);
 146                if (sta)
 147                        qos = test_sta_flag(sta, WLAN_STA_WME);
 148        }
 149        rcu_read_unlock();
 150
 151        if (!qos) {
 152                skb->priority = 0; /* required for correct WPA/11i MIC */
 153                return IEEE80211_AC_BE;
 154        }
 155
 156        /* use the data classifier to determine what 802.1d tag the
 157         * data frame has */
 158        skb->priority = cfg80211_classify8021d(skb);
 159
 160        return ieee80211_downgrade_queue(sdata, skb);
 161}
 162
 163/**
 164 * ieee80211_set_qos_hdr - Fill in the QoS header if there is one.
 165 *
 166 * @sdata: local subif
 167 * @skb: packet to be updated
 168 */
 169void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata,
 170                           struct sk_buff *skb)
 171{
 172        struct ieee80211_hdr *hdr = (void *)skb->data;
 173        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 174        u8 *p;
 175        u8 ack_policy, tid;
 176
 177        if (!ieee80211_is_data_qos(hdr->frame_control))
 178                return;
 179
 180        p = ieee80211_get_qos_ctl(hdr);
 181        tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
 182
 183        /* preserve EOSP bit */
 184        ack_policy = *p & IEEE80211_QOS_CTL_EOSP;
 185
 186        if (is_multicast_ether_addr(hdr->addr1) ||
 187            sdata->noack_map & BIT(tid)) {
 188                ack_policy |= IEEE80211_QOS_CTL_ACK_POLICY_NOACK;
 189                info->flags |= IEEE80211_TX_CTL_NO_ACK;
 190        }
 191
 192        /* qos header is 2 bytes */
 193        *p++ = ack_policy | tid;
 194        if (ieee80211_vif_is_mesh(&sdata->vif)) {
 195                /* preserve RSPI and Mesh PS Level bit */
 196                *p &= ((IEEE80211_QOS_CTL_RSPI |
 197                        IEEE80211_QOS_CTL_MESH_PS_LEVEL) >> 8);
 198
 199                /* Nulls don't have a mesh header (frame body) */
 200                if (!ieee80211_is_qos_nullfunc(hdr->frame_control))
 201                        *p |= (IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT >> 8);
 202        } else {
 203                *p = 0;
 204        }
 205}
 206