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