2007-05-05 22:45:53 +04:00
/*
* Copyright 2004 , Instant802 Networks , Inc .
2014-09-03 16:24:57 +04:00
* Copyright 2013 - 2014 Intel Mobile Communications GmbH
2007-05-05 22:45:53 +04:00
*
* 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 ;
}
}
2014-11-19 14:47:38 +03:00
/**
* ieee80211_fix_reserved_tid - return the TID to use if this one is reserved
* @ tid : the assumed - reserved TID
*
* Returns : the alternative TID to use , or 0 on error
*/
static inline u8 ieee80211_fix_reserved_tid ( u8 tid )
{
switch ( tid ) {
case 0 :
return 3 ;
case 1 :
return 2 ;
case 2 :
return 1 ;
case 3 :
return 0 ;
case 4 :
return 5 ;
case 5 :
return 4 ;
case 6 :
return 7 ;
case 7 :
return 6 ;
}
return 0 ;
}
2012-06-20 16:39:13 +04:00
static u16 ieee80211_downgrade_queue ( struct ieee80211_sub_if_data * sdata ,
2014-10-07 11:38:50 +04:00
struct sta_info * sta , struct sk_buff * skb )
2012-03-27 16:18:38 +04:00
{
2014-10-07 11:38:50 +04:00
struct ieee80211_if_managed * ifmgd = & sdata - > u . mgd ;
2012-03-27 16:18:38 +04:00
/* in case we are a client verify acm is not set for this ac */
2014-10-07 11:38:50 +04:00
while ( sdata - > wmm_acm & BIT ( skb - > priority ) ) {
int ac = ieee802_1d_to_ac [ skb - > priority ] ;
if ( ifmgd - > tx_tspec [ ac ] . admitted_time & &
skb - > priority = = ifmgd - > tx_tspec [ ac ] . up )
return ac ;
2012-03-27 16:18:38 +04:00
if ( wme_downgrade_ac ( skb ) ) {
/*
* 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 .
*/
break ;
}
}
2014-11-19 14:47:38 +03:00
/* Check to see if this is a reserved TID */
if ( sta & & sta - > reserved_tid = = skb - > priority )
skb - > priority = ieee80211_fix_reserved_tid ( skb - > priority ) ;
2012-03-27 16:18:38 +04:00
/* look up which queue to use for frames with this 1d tag */
return ieee802_1d_to_ac [ skb - > priority ] ;
}
2011-11-25 05:15:23 +04:00
/* Indicate which queue to use for this fully formed 802.11 frame */
2012-06-20 16:39:13 +04:00
u16 ieee80211_select_queue_80211 ( struct ieee80211_sub_if_data * sdata ,
2011-11-25 05:15:23 +04:00
struct sk_buff * skb ,
struct ieee80211_hdr * hdr )
{
2012-06-20 16:39:13 +04:00
struct ieee80211_local * local = sdata - > local ;
2011-11-25 05:15:23 +04:00
u8 * p ;
2012-03-28 13:04:29 +04:00
if ( local - > hw . queues < IEEE80211_NUM_ACS )
2011-11-25 05:15:23 +04:00
return 0 ;
if ( ! ieee80211_is_data ( hdr - > frame_control ) ) {
skb - > priority = 7 ;
return ieee802_1d_to_ac [ skb - > priority ] ;
}
if ( ! ieee80211_is_data_qos ( hdr - > frame_control ) ) {
skb - > priority = 0 ;
return ieee802_1d_to_ac [ skb - > priority ] ;
}
p = ieee80211_get_qos_ctl ( hdr ) ;
skb - > priority = * p & IEEE80211_QOS_CTL_TAG1D_MASK ;
2014-10-07 11:38:50 +04:00
return ieee80211_downgrade_queue ( sdata , NULL , skb ) ;
2011-11-25 05:15:23 +04:00
}
2007-05-05 22:45:53 +04:00
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 ;
2013-12-17 11:04:43 +04:00
struct mac80211_qos_map * qos_map ;
2014-10-07 11:38:50 +04:00
u16 ret ;
2007-05-05 22:45:53 +04:00
2012-03-28 13:04:29 +04:00
if ( local - > hw . queues < IEEE80211_NUM_ACS | | skb - > len < 6 ) {
2010-01-05 20:00:58 +03:00
skb - > priority = 0 ; /* required for correct WPA/11i MIC */
2012-03-28 13:04:26 +04:00
return 0 ;
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 ) {
2014-07-22 16:50:47 +04:00
qos = sta - > 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-11-04 08:11:13 +04:00
qos = true ;
2010-01-05 20:00:58 +03:00
break ;
# endif
case NL80211_IFTYPE_STATION :
2014-11-19 14:47:38 +03:00
/* might be a TDLS station */
sta = sta_info_get ( sdata , skb - > data ) ;
if ( sta )
qos = sta - > sta . wme ;
2010-01-05 20:00:58 +03:00
ra = sdata - > u . mgd . bssid ;
break ;
case NL80211_IFTYPE_ADHOC :
ra = skb - > data ;
break ;
2014-11-03 12:33:19 +03:00
case NL80211_IFTYPE_OCB :
/* all stations are required to support WME */
qos = true ;
break ;
2010-01-05 20:00:58 +03:00
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 )
2014-07-22 16:50:47 +04:00
qos = sta - > sta . wme ;
2007-05-05 22:45:53 +04:00
}
2010-01-05 20:00:58 +03:00
if ( ! qos ) {
2007-05-05 22:45:53 +04:00
skb - > priority = 0 ; /* required for correct WPA/11i MIC */
2014-10-07 11:38:50 +04:00
ret = IEEE80211_AC_BE ;
goto out ;
2007-05-05 22:45:53 +04:00
}
2014-02-11 19:02:47 +04:00
if ( skb - > protocol = = sdata - > control_port_protocol ) {
skb - > priority = 7 ;
2014-10-07 11:38:50 +04:00
goto downgrade ;
2014-02-11 19:02:47 +04:00
}
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 */
2013-12-17 11:04:43 +04:00
qos_map = rcu_dereference ( sdata - > qos_map ) ;
skb - > priority = cfg80211_classify8021d ( skb , qos_map ?
& qos_map - > qos_map : NULL ) ;
2007-05-05 22:45:53 +04:00
2014-10-07 11:38:50 +04:00
downgrade :
ret = ieee80211_downgrade_queue ( sdata , sta , skb ) ;
out :
rcu_read_unlock ( ) ;
return ret ;
2010-01-05 20:00:58 +03:00
}
2012-11-22 06:40:31 +04:00
/**
* ieee80211_set_qos_hdr - Fill in the QoS header if there is one .
*
* @ sdata : local subif
* @ skb : packet to be updated
*/
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 ;
2011-11-18 17:20:44 +04:00
struct ieee80211_tx_info * info = IEEE80211_SKB_CB ( skb ) ;
2016-10-12 15:17:13 +03:00
u8 tid = skb - > priority & IEEE80211_QOS_CTL_TAG1D_MASK ;
u8 flags ;
2012-11-22 06:40:31 +04:00
u8 * p ;
2010-01-05 20:00:58 +03:00
2012-11-22 06:40:31 +04:00
if ( ! ieee80211_is_data_qos ( hdr - > frame_control ) )
return ;
2010-01-05 20:00:58 +03:00
2012-11-22 06:40:31 +04:00
p = ieee80211_get_qos_ctl ( hdr ) ;
2011-11-03 12:59:39 +04:00
2016-10-12 15:17:13 +03:00
/* set up the first byte */
/*
* preserve everything but the TID and ACK policy
* ( which we both write here )
*/
flags = * p & ~ ( IEEE80211_QOS_CTL_TID_MASK |
IEEE80211_QOS_CTL_ACK_POLICY_MASK ) ;
2011-11-18 17:20:44 +04:00
2012-11-22 06:40:31 +04:00
if ( is_multicast_ether_addr ( hdr - > addr1 ) | |
sdata - > noack_map & BIT ( tid ) ) {
2016-10-12 15:17:13 +03:00
flags | = IEEE80211_QOS_CTL_ACK_POLICY_NOACK ;
2012-11-22 06:40:31 +04:00
info - > flags | = IEEE80211_TX_CTL_NO_ACK ;
2007-05-05 22:45:53 +04:00
}
2012-11-22 06:40:31 +04:00
2016-10-12 15:17:13 +03:00
* p = flags | tid ;
/* set up the second byte */
p + + ;
2013-01-30 21:14:08 +04:00
if ( ieee80211_vif_is_mesh ( & sdata - > vif ) ) {
/* preserve RSPI and Mesh PS Level bit */
* p & = ( ( IEEE80211_QOS_CTL_RSPI |
IEEE80211_QOS_CTL_MESH_PS_LEVEL ) > > 8 ) ;
/* Nulls don't have a mesh header (frame body) */
if ( ! ieee80211_is_qos_nullfunc ( hdr - > frame_control ) )
* p | = ( IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT > > 8 ) ;
} else {
* p = 0 ;
}
2007-05-05 22:45:53 +04:00
}