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 */ 24 const int ieee802_1d_to_ac[8] = { 2, 3, 3, 2, 1, 1, 0, 0 }; 25 26 static int wme_downgrade_ac(struct sk_buff *skb) 27 { 28 switch (skb->priority) { 29 case 6: 30 case 7: 31 skb->priority = 5; /* VO -> VI */ 32 return 0; 33 case 4: 34 case 5: 35 skb->priority = 3; /* VI -> BE */ 36 return 0; 37 case 0: 38 case 3: 39 skb->priority = 2; /* BE -> BK */ 40 return 0; 41 default: 42 return -1; 43 } 44 } 45 46 47 /* Indicate which queue to use. */ 48 static u16 classify80211(struct ieee80211_local *local, struct sk_buff *skb) 49 { 50 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; 51 52 if (!ieee80211_is_data(hdr->frame_control)) { 53 /* management frames go on AC_VO queue, but are sent 54 * without QoS control fields */ 55 return 0; 56 } 57 58 if (0 /* injected */) { 59 /* use AC from radiotap */ 60 } 61 62 if (!ieee80211_is_data_qos(hdr->frame_control)) { 63 skb->priority = 0; /* required for correct WPA/11i MIC */ 64 return ieee802_1d_to_ac[skb->priority]; 65 } 66 67 /* use the data classifier to determine what 802.1d tag the 68 * data frame has */ 69 skb->priority = cfg80211_classify8021d(skb); 70 71 /* in case we are a client verify acm is not set for this ac */ 72 while (unlikely(local->wmm_acm & BIT(skb->priority))) { 73 if (wme_downgrade_ac(skb)) { 74 /* 75 * This should not really happen. The AP has marked all 76 * lower ACs to require admission control which is not 77 * a reasonable configuration. Allow the frame to be 78 * transmitted using AC_BK as a workaround. 79 */ 80 break; 81 } 82 } 83 84 /* look up which queue to use for frames with this 1d tag */ 85 return ieee802_1d_to_ac[skb->priority]; 86 } 87 88 u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb) 89 { 90 struct ieee80211_master_priv *mpriv = netdev_priv(dev); 91 struct ieee80211_local *local = mpriv->local; 92 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; 93 u16 queue; 94 u8 tid; 95 96 queue = classify80211(local, skb); 97 if (unlikely(queue >= local->hw.queues)) 98 queue = local->hw.queues - 1; 99 100 /* 101 * Now we know the 1d priority, fill in the QoS header if 102 * there is one (and we haven't done this before). 103 */ 104 if (!skb->requeue && ieee80211_is_data_qos(hdr->frame_control)) { 105 u8 *p = ieee80211_get_qos_ctl(hdr); 106 u8 ack_policy = 0; 107 tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; 108 if (unlikely(local->wifi_wme_noack_test)) 109 ack_policy |= QOS_CONTROL_ACK_POLICY_NOACK << 110 QOS_CONTROL_ACK_POLICY_SHIFT; 111 /* qos header is 2 bytes, second reserved */ 112 *p++ = ack_policy | tid; 113 *p = 0; 114 } 115 116 return queue; 117 } 118