2007-05-05 22:45:53 +04:00
/*
* Copyright 2004 , Instant802 Networks , Inc .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/netdevice.h>
# include <linux/skbuff.h>
# include <linux/module.h>
# include <linux/if_arp.h>
# include <linux/types.h>
# include <net/ip.h>
# include <net/pkt_sched.h>
# include <net/mac80211.h>
# include "ieee80211_i.h"
# include "wme.h"
2008-07-15 14:34:57 +04:00
/* Default mapping in classifier to work with default
2008-04-30 20:51:21 +04:00
* queue setup .
*/
2010-11-16 22:49:58 +03:00
const int ieee802_1d_to_ac [ 8 ] = {
IEEE80211_AC_BE ,
IEEE80211_AC_BK ,
IEEE80211_AC_BK ,
IEEE80211_AC_BE ,
IEEE80211_AC_VI ,
IEEE80211_AC_VI ,
IEEE80211_AC_VO ,
IEEE80211_AC_VO
} ;
2007-05-05 22:45:53 +04:00
2008-07-15 14:34:57 +04:00
static int wme_downgrade_ac ( struct sk_buff * skb )
2007-05-05 22:45:53 +04:00
{
switch ( skb - > priority ) {
case 6 :
case 7 :
skb - > priority = 5 ; /* VO -> VI */
return 0 ;
case 4 :
case 5 :
skb - > priority = 3 ; /* VI -> BE */
return 0 ;
case 0 :
case 3 :
skb - > priority = 2 ; /* BE -> BK */
return 0 ;
default :
return - 1 ;
}
}
2010-01-05 20:00:58 +03:00
/* Indicate which queue to use. */
u16 ieee80211_select_queue ( struct ieee80211_sub_if_data * sdata ,
struct sk_buff * skb )
2007-05-05 22:45:53 +04:00
{
2010-01-05 20:00:58 +03:00
struct ieee80211_local * local = sdata - > local ;
struct sta_info * sta = NULL ;
const u8 * ra = NULL ;
bool qos = false ;
2007-05-05 22:45:53 +04:00
2010-01-05 20:00:58 +03:00
if ( local - > hw . queues < 4 | | skb - > len < 6 ) {
skb - > priority = 0 ; /* required for correct WPA/11i MIC */
2010-12-22 12:15:30 +03:00
return min_t ( u16 , local - > hw . queues - 1 , IEEE80211_AC_BE ) ;
2010-01-05 20:00:58 +03:00
}
rcu_read_lock ( ) ;
switch ( sdata - > vif . type ) {
case NL80211_IFTYPE_AP_VLAN :
sta = rcu_dereference ( sdata - > u . vlan . sta ) ;
2010-12-22 12:15:30 +03:00
if ( sta ) {
2011-09-29 18:04:36 +04:00
qos = test_sta_flag ( sta , WLAN_STA_WME ) ;
2010-01-05 20:00:58 +03:00
break ;
2010-12-22 12:15:30 +03:00
}
2010-01-05 20:00:58 +03:00
case NL80211_IFTYPE_AP :
ra = skb - > data ;
break ;
case NL80211_IFTYPE_WDS :
ra = sdata - > u . wds . remote_addr ;
break ;
# ifdef CONFIG_MAC80211_MESH
case NL80211_IFTYPE_MESH_POINT :
2011-09-08 04:49:52 +04:00
ra = skb - > data ;
2010-01-05 20:00:58 +03:00
break ;
# endif
case NL80211_IFTYPE_STATION :
ra = sdata - > u . mgd . bssid ;
break ;
case NL80211_IFTYPE_ADHOC :
ra = skb - > data ;
break ;
default :
break ;
2007-05-05 22:45:53 +04:00
}
2010-01-05 20:00:58 +03:00
if ( ! sta & & ra & & ! is_multicast_ether_addr ( ra ) ) {
sta = sta_info_get ( sdata , ra ) ;
if ( sta )
2011-09-29 18:04:36 +04:00
qos = test_sta_flag ( sta , WLAN_STA_WME ) ;
2007-05-05 22:45:53 +04:00
}
2010-01-05 20:00:58 +03:00
rcu_read_unlock ( ) ;
if ( ! qos ) {
2007-05-05 22:45:53 +04:00
skb - > priority = 0 ; /* required for correct WPA/11i MIC */
2010-12-22 12:15:30 +03:00
return IEEE80211_AC_BE ;
2007-05-05 22:45:53 +04:00
}
/* use the data classifier to determine what 802.1d tag the
2007-08-29 01:01:55 +04:00
* data frame has */
2009-05-21 17:47:03 +04:00
skb - > priority = cfg80211_classify8021d ( skb ) ;
2007-05-05 22:45:53 +04:00
2010-01-05 20:00:58 +03:00
return ieee80211_downgrade_queue ( local , skb ) ;
}
u16 ieee80211_downgrade_queue ( struct ieee80211_local * local ,
struct sk_buff * skb )
{
2007-08-29 01:01:55 +04:00
/* in case we are a client verify acm is not set for this ac */
2007-05-05 22:45:53 +04:00
while ( unlikely ( local - > wmm_acm & BIT ( skb - > priority ) ) ) {
if ( wme_downgrade_ac ( skb ) ) {
2009-03-05 18:23:46 +03:00
/*
* This should not really happen . The AP has marked all
* lower ACs to require admission control which is not
* a reasonable configuration . Allow the frame to be
* transmitted using AC_BK as a workaround .
2008-07-15 14:34:57 +04:00
*/
2009-03-05 18:23:46 +03:00
break ;
2007-05-05 22:45:53 +04:00
}
}
/* look up which queue to use for frames with this 1d tag */
return ieee802_1d_to_ac [ skb - > priority ] ;
}
2011-09-08 04:49:53 +04:00
void ieee80211_set_qos_hdr ( struct ieee80211_sub_if_data * sdata ,
struct sk_buff * skb )
2007-05-05 22:45:53 +04:00
{
2010-01-05 20:00:58 +03:00
struct ieee80211_hdr * hdr = ( void * ) skb - > data ;
/* Fill in the QoS header if there is one. */
2009-06-07 23:58:37 +04:00
if ( ieee80211_is_data_qos ( hdr - > frame_control ) ) {
2008-06-12 01:21:59 +04:00
u8 * p = ieee80211_get_qos_ctl ( hdr ) ;
2011-11-03 12:59:39 +04:00
u8 ack_policy , tid ;
2010-01-05 20:00:58 +03:00
2008-07-02 22:05:34 +04:00
tid = skb - > priority & IEEE80211_QOS_CTL_TAG1D_MASK ;
2010-01-05 20:00:58 +03:00
2011-11-03 12:59:39 +04:00
/* preserve EOSP bit */
ack_policy = * p & IEEE80211_QOS_CTL_EOSP ;
2011-09-08 04:49:53 +04:00
if ( unlikely ( sdata - > local - > wifi_wme_noack_test ) )
2011-06-22 12:06:59 +04:00
ack_policy | = IEEE80211_QOS_CTL_ACK_POLICY_NOACK ;
2011-09-08 04:49:53 +04:00
/* qos header is 2 bytes */
2008-06-12 01:21:59 +04:00
* p + + = ack_policy | tid ;
2011-09-08 04:49:53 +04:00
* p = ieee80211_vif_is_mesh ( & sdata - > vif ) ?
( IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT > > 8 ) : 0 ;
2007-05-05 22:45:53 +04:00
}
}