]>
Commit | Line | Data |
---|---|---|
f0706e82 JB |
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 | ||
51cb6db0 | 21 | /* Default mapping in classifier to work with default |
e100bb64 JB |
22 | * queue setup. |
23 | */ | |
9e723492 | 24 | const int ieee802_1d_to_ac[8] = { 2, 3, 3, 2, 1, 1, 0, 0 }; |
f0706e82 | 25 | |
a8bdf29c | 26 | static const char llc_ip_hdr[8] = {0xAA, 0xAA, 0x3, 0, 0, 0, 0x08, 0}; |
f0706e82 | 27 | |
51cb6db0 DM |
28 | /* Given a data frame determine the 802.1p/1d tag to use. */ |
29 | static unsigned int classify_1d(struct sk_buff *skb) | |
f0706e82 | 30 | { |
51cb6db0 | 31 | unsigned int dscp; |
f0706e82 JB |
32 | |
33 | /* skb->priority values from 256->263 are magic values to | |
51cb6db0 DM |
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 | */ | |
f0706e82 JB |
38 | if (skb->priority >= 256 && skb->priority <= 263) |
39 | return skb->priority - 256; | |
40 | ||
51cb6db0 | 41 | switch (skb->protocol) { |
60678040 | 42 | case htons(ETH_P_IP): |
51cb6db0 DM |
43 | dscp = ip_hdr(skb)->tos & 0xfc; |
44 | break; | |
f0706e82 | 45 | |
51cb6db0 DM |
46 | default: |
47 | return 0; | |
48 | } | |
f0706e82 | 49 | |
f0706e82 JB |
50 | return dscp >> 5; |
51 | } | |
52 | ||
53 | ||
51cb6db0 | 54 | static int wme_downgrade_ac(struct sk_buff *skb) |
f0706e82 JB |
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 | ||
51cb6db0 | 75 | /* Indicate which queue to use. */ |
b4a4bf5d | 76 | static u16 classify80211(struct ieee80211_local *local, struct sk_buff *skb) |
f0706e82 | 77 | { |
f0706e82 | 78 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; |
f0706e82 | 79 | |
002aaf4e | 80 | if (!ieee80211_is_data(hdr->frame_control)) { |
f0706e82 JB |
81 | /* management frames go on AC_VO queue, but are sent |
82 | * without QoS control fields */ | |
e100bb64 | 83 | return 0; |
f0706e82 JB |
84 | } |
85 | ||
f9d540ee JB |
86 | if (0 /* injected */) { |
87 | /* use AC from radiotap */ | |
f0706e82 JB |
88 | } |
89 | ||
002aaf4e | 90 | if (!ieee80211_is_data_qos(hdr->frame_control)) { |
f0706e82 JB |
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 | |
3c3b00ca | 96 | * data frame has */ |
51cb6db0 | 97 | skb->priority = classify_1d(skb); |
f0706e82 | 98 | |
3c3b00ca | 99 | /* in case we are a client verify acm is not set for this ac */ |
f0706e82 JB |
100 | while (unlikely(local->wmm_acm & BIT(skb->priority))) { |
101 | if (wme_downgrade_ac(skb)) { | |
51cb6db0 DM |
102 | /* The old code would drop the packet in this |
103 | * case. | |
104 | */ | |
105 | return 0; | |
f0706e82 JB |
106 | } |
107 | } | |
108 | ||
109 | /* look up which queue to use for frames with this 1d tag */ | |
110 | return ieee802_1d_to_ac[skb->priority]; | |
111 | } | |
112 | ||
51cb6db0 | 113 | u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb) |
f0706e82 | 114 | { |
b4a4bf5d JB |
115 | struct ieee80211_master_priv *mpriv = netdev_priv(dev); |
116 | struct ieee80211_local *local = mpriv->local; | |
f0706e82 | 117 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; |
51cb6db0 | 118 | u16 queue; |
9e723492 | 119 | u8 tid; |
f0706e82 | 120 | |
b4a4bf5d | 121 | queue = classify80211(local, skb); |
51cb6db0 DM |
122 | if (unlikely(queue >= local->hw.queues)) |
123 | queue = local->hw.queues - 1; | |
124 | ||
96f5e66e JB |
125 | /* |
126 | * Now we know the 1d priority, fill in the QoS header if | |
127 | * there is one (and we haven't done this before). | |
f0706e82 | 128 | */ |
96f5e66e | 129 | if (!skb->requeue && ieee80211_is_data_qos(hdr->frame_control)) { |
002aaf4e | 130 | u8 *p = ieee80211_get_qos_ctl(hdr); |
9e723492 | 131 | u8 ack_policy = 0; |
238f74a2 | 132 | tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; |
f0706e82 | 133 | if (local->wifi_wme_noack_test) |
9e723492 | 134 | ack_policy |= QOS_CONTROL_ACK_POLICY_NOACK << |
f0706e82 JB |
135 | QOS_CONTROL_ACK_POLICY_SHIFT; |
136 | /* qos header is 2 bytes, second reserved */ | |
002aaf4e | 137 | *p++ = ack_policy | tid; |
f0706e82 JB |
138 | *p = 0; |
139 | } | |
140 | ||
f0706e82 JB |
141 | return queue; |
142 | } |