Commit 8a4d32f3 authored by Arik Nemtsov's avatar Arik Nemtsov Committed by Johannes Berg

mac80211: add TDLS channel-switch Rx flow

When receiving a TDLS channel switch request or response, parse the frame
and call a new tdls_recv_channel_switch op in the low level driver with
the parsed data.
Signed-off-by: default avatarArik Nemtsov <arikx.nemtsov@intel.com>
Signed-off-by: default avatarArik Nemtsov <arik@wizery.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent a7a6bdd0
...@@ -1826,6 +1826,31 @@ struct ieee80211_scan_request { ...@@ -1826,6 +1826,31 @@ struct ieee80211_scan_request {
struct cfg80211_scan_request req; struct cfg80211_scan_request req;
}; };
/**
* struct ieee80211_tdls_ch_sw_params - TDLS channel switch parameters
*
* @sta: peer this TDLS channel-switch request/response came from
* @chandef: channel referenced in a TDLS channel-switch request
* @action_code: see &enum ieee80211_tdls_actioncode
* @status: channel-switch response status
* @timestamp: time at which the frame was received
* @switch_time: switch-timing parameter received in the frame
* @switch_timeout: switch-timing parameter received in the frame
* @tmpl_skb: TDLS switch-channel response template
* @ch_sw_tm_ie: offset of the channel-switch timing IE inside @tmpl_skb
*/
struct ieee80211_tdls_ch_sw_params {
struct ieee80211_sta *sta;
struct cfg80211_chan_def *chandef;
u8 action_code;
u32 status;
u32 timestamp;
u16 switch_time;
u16 switch_timeout;
struct sk_buff *tmpl_skb;
u32 ch_sw_tm_ie;
};
/** /**
* wiphy_to_ieee80211_hw - return a mac80211 driver hw struct from a wiphy * wiphy_to_ieee80211_hw - return a mac80211 driver hw struct from a wiphy
* *
...@@ -2925,6 +2950,13 @@ enum ieee80211_reconfig_type { ...@@ -2925,6 +2950,13 @@ enum ieee80211_reconfig_type {
* optionally copy the skb for further re-use. * optionally copy the skb for further re-use.
* @tdls_cancel_channel_switch: Stop channel-switching with a TDLS peer. Both * @tdls_cancel_channel_switch: Stop channel-switching with a TDLS peer. Both
* peers must be on the base channel when the call completes. * peers must be on the base channel when the call completes.
* @tdls_recv_channel_switch: a TDLS channel-switch related frame (request or
* response) has been received from a remote peer. The driver gets
* parameters parsed from the incoming frame and may use them to continue
* an ongoing channel-switch operation. In addition, a channel-switch
* response template is provided, together with the location of the
* switch-timing IE within the template. The skb can only be used within
* the function call.
*/ */
struct ieee80211_ops { struct ieee80211_ops {
void (*tx)(struct ieee80211_hw *hw, void (*tx)(struct ieee80211_hw *hw,
...@@ -3141,10 +3173,13 @@ struct ieee80211_ops { ...@@ -3141,10 +3173,13 @@ struct ieee80211_ops {
struct ieee80211_vif *vif, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, u8 oper_class, struct ieee80211_sta *sta, u8 oper_class,
struct cfg80211_chan_def *chandef, struct cfg80211_chan_def *chandef,
struct sk_buff *skb, u32 ch_sw_tm_ie); struct sk_buff *tmpl_skb, u32 ch_sw_tm_ie);
void (*tdls_cancel_channel_switch)(struct ieee80211_hw *hw, void (*tdls_cancel_channel_switch)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, struct ieee80211_vif *vif,
struct ieee80211_sta *sta); struct ieee80211_sta *sta);
void (*tdls_recv_channel_switch)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_tdls_ch_sw_params *params);
}; };
/** /**
......
...@@ -1337,4 +1337,16 @@ drv_tdls_cancel_channel_switch(struct ieee80211_local *local, ...@@ -1337,4 +1337,16 @@ drv_tdls_cancel_channel_switch(struct ieee80211_local *local,
trace_drv_return_void(local); trace_drv_return_void(local);
} }
static inline void
drv_tdls_recv_channel_switch(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_tdls_ch_sw_params *params)
{
trace_drv_tdls_recv_channel_switch(local, sdata, params);
if (local->ops->tdls_recv_channel_switch)
local->ops->tdls_recv_channel_switch(&local->hw, &sdata->vif,
params);
trace_drv_return_void(local);
}
#endif /* __MAC80211_DRIVER_OPS */ #endif /* __MAC80211_DRIVER_OPS */
...@@ -993,6 +993,7 @@ enum sdata_queue_type { ...@@ -993,6 +993,7 @@ enum sdata_queue_type {
IEEE80211_SDATA_QUEUE_AGG_STOP = 2, IEEE80211_SDATA_QUEUE_AGG_STOP = 2,
IEEE80211_SDATA_QUEUE_RX_AGG_START = 3, IEEE80211_SDATA_QUEUE_RX_AGG_START = 3,
IEEE80211_SDATA_QUEUE_RX_AGG_STOP = 4, IEEE80211_SDATA_QUEUE_RX_AGG_STOP = 4,
IEEE80211_SDATA_QUEUE_TDLS_CHSW = 5,
}; };
enum { enum {
...@@ -2013,6 +2014,8 @@ int ieee80211_tdls_channel_switch(struct wiphy *wiphy, struct net_device *dev, ...@@ -2013,6 +2014,8 @@ int ieee80211_tdls_channel_switch(struct wiphy *wiphy, struct net_device *dev,
void ieee80211_tdls_cancel_channel_switch(struct wiphy *wiphy, void ieee80211_tdls_cancel_channel_switch(struct wiphy *wiphy,
struct net_device *dev, struct net_device *dev,
const u8 *addr); const u8 *addr);
void ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb);
extern const struct ethtool_ops ieee80211_ethtool_ops; extern const struct ethtool_ops ieee80211_ethtool_ops;
......
...@@ -1202,6 +1202,8 @@ static void ieee80211_iface_work(struct work_struct *work) ...@@ -1202,6 +1202,8 @@ static void ieee80211_iface_work(struct work_struct *work)
WLAN_BACK_RECIPIENT, 0, WLAN_BACK_RECIPIENT, 0,
false); false);
mutex_unlock(&local->sta_mtx); mutex_unlock(&local->sta_mtx);
} else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_TDLS_CHSW) {
ieee80211_process_tdls_channel_switch(sdata, skb);
} else if (ieee80211_is_action(mgmt->frame_control) && } else if (ieee80211_is_action(mgmt->frame_control) &&
mgmt->u.action.category == WLAN_CATEGORY_BACK) { mgmt->u.action.category == WLAN_CATEGORY_BACK) {
int len = skb->len; int len = skb->len;
......
...@@ -766,7 +766,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) ...@@ -766,7 +766,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
if ((hw->wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) && if ((hw->wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) &&
(!local->ops->tdls_channel_switch || (!local->ops->tdls_channel_switch ||
!local->ops->tdls_cancel_channel_switch)) !local->ops->tdls_cancel_channel_switch ||
!local->ops->tdls_recv_channel_switch))
return -EOPNOTSUPP; return -EOPNOTSUPP;
#ifdef CONFIG_PM #ifdef CONFIG_PM
......
...@@ -2333,6 +2333,27 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx) ...@@ -2333,6 +2333,27 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
if (!ieee80211_frame_allowed(rx, fc)) if (!ieee80211_frame_allowed(rx, fc))
return RX_DROP_MONITOR; return RX_DROP_MONITOR;
/* directly handle TDLS channel switch requests/responses */
if (unlikely(((struct ethhdr *)rx->skb->data)->h_proto ==
cpu_to_be16(ETH_P_TDLS))) {
struct ieee80211_tdls_data *tf = (void *)rx->skb->data;
if (pskb_may_pull(rx->skb,
offsetof(struct ieee80211_tdls_data, u)) &&
tf->payload_type == WLAN_TDLS_SNAP_RFTYPE &&
tf->category == WLAN_CATEGORY_TDLS &&
(tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST ||
tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE)) {
rx->skb->pkt_type = IEEE80211_SDATA_QUEUE_TDLS_CHSW;
skb_queue_tail(&sdata->skb_queue, rx->skb);
ieee80211_queue_work(&rx->local->hw, &sdata->work);
if (rx->sta)
rx->sta->rx_packets++;
return RX_QUEUED;
}
}
if (rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && if (rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
unlikely(port_control) && sdata->bss) { unlikely(port_control) && sdata->bss) {
sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, sdata = container_of(sdata->bss, struct ieee80211_sub_if_data,
......
This diff is collapsed.
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#define STA_ENTRY __array(char, sta_addr, ETH_ALEN) #define STA_ENTRY __array(char, sta_addr, ETH_ALEN)
#define STA_ASSIGN (sta ? memcpy(__entry->sta_addr, sta->addr, ETH_ALEN) : memset(__entry->sta_addr, 0, ETH_ALEN)) #define STA_ASSIGN (sta ? memcpy(__entry->sta_addr, sta->addr, ETH_ALEN) : memset(__entry->sta_addr, 0, ETH_ALEN))
#define STA_NAMED_ASSIGN(s) memcpy(__entry->sta_addr, (s)->addr, ETH_ALEN)
#define STA_PR_FMT " sta:%pM" #define STA_PR_FMT " sta:%pM"
#define STA_PR_ARG __entry->sta_addr #define STA_PR_ARG __entry->sta_addr
...@@ -2254,6 +2255,50 @@ TRACE_EVENT(drv_tdls_cancel_channel_switch, ...@@ -2254,6 +2255,50 @@ TRACE_EVENT(drv_tdls_cancel_channel_switch,
) )
); );
TRACE_EVENT(drv_tdls_recv_channel_switch,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_tdls_ch_sw_params *params),
TP_ARGS(local, sdata, params),
TP_STRUCT__entry(
LOCAL_ENTRY
VIF_ENTRY
__field(u8, action_code)
STA_ENTRY
CHANDEF_ENTRY
__field(u32, status)
__field(bool, peer_initiator)
__field(u32, timestamp)
__field(u16, switch_time)
__field(u16, switch_timeout)
),
TP_fast_assign(
LOCAL_ASSIGN;
VIF_ASSIGN;
STA_NAMED_ASSIGN(params->sta);
CHANDEF_ASSIGN(params->chandef)
__entry->peer_initiator = params->sta->tdls_initiator;
__entry->action_code = params->action_code;
__entry->status = params->status;
__entry->timestamp = params->timestamp;
__entry->switch_time = params->switch_time;
__entry->switch_timeout = params->switch_timeout;
),
TP_printk(
LOCAL_PR_FMT VIF_PR_FMT " received tdls channel switch packet"
" action:%d status:%d time:%d switch time:%d switch"
" timeout:%d initiator: %d chan:" CHANDEF_PR_FMT STA_PR_FMT,
LOCAL_PR_ARG, VIF_PR_ARG, __entry->action_code, __entry->status,
__entry->timestamp, __entry->switch_time,
__entry->switch_timeout, __entry->peer_initiator,
CHANDEF_PR_ARG, STA_PR_ARG
)
);
#ifdef CONFIG_MAC80211_MESSAGE_TRACING #ifdef CONFIG_MAC80211_MESSAGE_TRACING
#undef TRACE_SYSTEM #undef TRACE_SYSTEM
#define TRACE_SYSTEM mac80211_msg #define TRACE_SYSTEM mac80211_msg
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment