Skip to content

Commit

Permalink
mac80211: add offload channel switch support
Browse files Browse the repository at this point in the history
This adds support for offloading the channel switch
operation to devices that support such, typically
by having specific firmware API for it. The reasons
for this could be that the firmware provides better
timing or that regulatory enforcement done by the
device requires special handling of CSAs.

In order to allow drivers to specify the timing to
the device, the new channel_switch callback will
pass through the received frame's mactime, where
available.

Signed-off-by: Wey-Yi Guy <[email protected]>
Signed-off-by: Johannes Berg <[email protected]>
Signed-off-by: John W. Linville <[email protected]>
  • Loading branch information
jmberg authored and linvjw committed May 12, 2010
1 parent b29e7eb commit 5ce6e43
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 5 deletions.
39 changes: 39 additions & 0 deletions include/net/mac80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -711,6 +711,28 @@ struct ieee80211_conf {
enum ieee80211_smps_mode smps_mode;
};

/**
* struct ieee80211_channel_switch - holds the channel switch data
*
* The information provided in this structure is required for channel switch
* operation.
*
* @timestamp: value in microseconds of the 64-bit Time Synchronization
* Function (TSF) timer when the frame containing the channel switch
* announcement was received. This is simply the rx.mactime parameter
* the driver passed into mac80211.
* @block_tx: Indicates whether transmission must be blocked before the
* scheduled channel switch, as indicated by the AP.
* @channel: the new channel to switch to
* @count: the number of TBTT's until the channel switch event
*/
struct ieee80211_channel_switch {
u64 timestamp;
bool block_tx;
struct ieee80211_channel *channel;
u8 count;
};

/**
* struct ieee80211_vif - per-interface data
*
Expand Down Expand Up @@ -1631,6 +1653,11 @@ enum ieee80211_ampdu_mlme_action {
* @flush: Flush all pending frames from the hardware queue, making sure
* that the hardware queues are empty. If the parameter @drop is set
* to %true, pending frames may be dropped. The callback can sleep.
*
* @channel_switch: Drivers that need (or want) to offload the channel
* switch operation for CSAs received from the AP may implement this
* callback. They must then call ieee80211_chswitch_done() to indicate
* completion of the channel switch.
*/
struct ieee80211_ops {
int (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb);
Expand Down Expand Up @@ -1694,6 +1721,8 @@ struct ieee80211_ops {
int (*testmode_cmd)(struct ieee80211_hw *hw, void *data, int len);
#endif
void (*flush)(struct ieee80211_hw *hw, bool drop);
void (*channel_switch)(struct ieee80211_hw *hw,
struct ieee80211_channel_switch *ch_switch);
};

/**
Expand Down Expand Up @@ -2444,6 +2473,16 @@ void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
enum nl80211_cqm_rssi_threshold_event rssi_event,
gfp_t gfp);

/**
* ieee80211_chswitch_done - Complete channel switch process
* @vif: &struct ieee80211_vif pointer from the add_interface callback.
* @success: make the channel switch successful or not
*
* Complete the channel switch post-process: set the new operational channel
* and wake up the suspended queues.
*/
void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success);

/* Rate control API */

/**
Expand Down
11 changes: 11 additions & 0 deletions net/mac80211/driver-ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -373,4 +373,15 @@ static inline void drv_flush(struct ieee80211_local *local, bool drop)
if (local->ops->flush)
local->ops->flush(&local->hw, drop);
}

static inline void drv_channel_switch(struct ieee80211_local *local,
struct ieee80211_channel_switch *ch_switch)
{
might_sleep();

local->ops->channel_switch(&local->hw, ch_switch);

trace_drv_channel_switch(local, ch_switch);
}

#endif /* __MAC80211_DRIVER_OPS */
49 changes: 49 additions & 0 deletions net/mac80211/driver-trace.h
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,34 @@ TRACE_EVENT(drv_flush,
)
);

TRACE_EVENT(drv_channel_switch,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_channel_switch *ch_switch),

TP_ARGS(local, ch_switch),

TP_STRUCT__entry(
LOCAL_ENTRY
__field(u64, timestamp)
__field(bool, block_tx)
__field(u16, freq)
__field(u8, count)
),

TP_fast_assign(
LOCAL_ASSIGN;
__entry->timestamp = ch_switch->timestamp;
__entry->block_tx = ch_switch->block_tx;
__entry->freq = ch_switch->channel->center_freq;
__entry->count = ch_switch->count;
),

TP_printk(
LOCAL_PR_FMT " new freq:%u count:%d",
LOCAL_PR_ARG, __entry->freq, __entry->count
)
);

/*
* Tracing for API calls that drivers call.
*/
Expand Down Expand Up @@ -992,6 +1020,27 @@ TRACE_EVENT(api_sta_block_awake,
)
);

TRACE_EVENT(api_chswitch_done,
TP_PROTO(struct ieee80211_sub_if_data *sdata, bool success),

TP_ARGS(sdata, success),

TP_STRUCT__entry(
VIF_ENTRY
__field(bool, success)
),

TP_fast_assign(
VIF_ASSIGN;
__entry->success = success;
),

TP_printk(
VIF_PR_FMT " success=%d",
VIF_PR_ARG, __entry->success
)
);

/*
* Tracing for internal functions
* (which may also be called in response to driver calls)
Expand Down
3 changes: 2 additions & 1 deletion net/mac80211/ieee80211_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -999,7 +999,8 @@ int ieee80211_max_network_latency(struct notifier_block *nb,
unsigned long data, void *dummy);
void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
struct ieee80211_channel_sw_ie *sw_elem,
struct ieee80211_bss *bss);
struct ieee80211_bss *bss,
u64 timestamp);
void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata);
void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata);

Expand Down
56 changes: 52 additions & 4 deletions net/mac80211/mlme.c
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,11 @@ static void ieee80211_chswitch_work(struct work_struct *work)
goto out;

sdata->local->oper_channel = sdata->local->csa_channel;
ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL);
if (!sdata->local->ops->channel_switch) {
/* call "hw_config" only if doing sw channel switch */
ieee80211_hw_config(sdata->local,
IEEE80211_CONF_CHANGE_CHANNEL);
}

/* XXX: shouldn't really modify cfg80211-owned data! */
ifmgd->associated->channel = sdata->local->oper_channel;
Expand All @@ -353,6 +357,29 @@ static void ieee80211_chswitch_work(struct work_struct *work)
mutex_unlock(&ifmgd->mtx);
}

void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)
{
struct ieee80211_sub_if_data *sdata;
struct ieee80211_if_managed *ifmgd;

sdata = vif_to_sdata(vif);
ifmgd = &sdata->u.mgd;

trace_api_chswitch_done(sdata, success);
if (!success) {
/*
* If the channel switch was not successful, stay
* around on the old channel. We currently lack
* good handling of this situation, possibly we
* should just drop the association.
*/
sdata->local->csa_channel = sdata->local->oper_channel;
}

ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
}
EXPORT_SYMBOL(ieee80211_chswitch_done);

static void ieee80211_chswitch_timer(unsigned long data)
{
struct ieee80211_sub_if_data *sdata =
Expand All @@ -369,7 +396,8 @@ static void ieee80211_chswitch_timer(unsigned long data)

void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
struct ieee80211_channel_sw_ie *sw_elem,
struct ieee80211_bss *bss)
struct ieee80211_bss *bss,
u64 timestamp)
{
struct cfg80211_bss *cbss =
container_of((void *)bss, struct cfg80211_bss, priv);
Expand Down Expand Up @@ -397,6 +425,24 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,

sdata->local->csa_channel = new_ch;

if (sdata->local->ops->channel_switch) {
/* use driver's channel switch callback */
struct ieee80211_channel_switch ch_switch;
memset(&ch_switch, 0, sizeof(ch_switch));
ch_switch.timestamp = timestamp;
if (sw_elem->mode) {
ch_switch.block_tx = true;
ieee80211_stop_queues_by_reason(&sdata->local->hw,
IEEE80211_QUEUE_STOP_REASON_CSA);
}
ch_switch.channel = new_ch;
ch_switch.count = sw_elem->count;
ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
drv_channel_switch(sdata->local, &ch_switch);
return;
}

/* channel switch handled in software */
if (sw_elem->count <= 1) {
ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
} else {
Expand Down Expand Up @@ -1316,7 +1362,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
ETH_ALEN) == 0)) {
struct ieee80211_channel_sw_ie *sw_elem =
(struct ieee80211_channel_sw_ie *)elems->ch_switch_elem;
ieee80211_sta_process_chanswitch(sdata, sw_elem, bss);
ieee80211_sta_process_chanswitch(sdata, sw_elem,
bss, rx_status->mactime);
}
}

Expand Down Expand Up @@ -1648,7 +1695,8 @@ static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,

ieee80211_sta_process_chanswitch(sdata,
&mgmt->u.action.u.chan_switch.sw_elem,
(void *)ifmgd->associated->priv);
(void *)ifmgd->associated->priv,
rx_status->mactime);
break;
}
mutex_unlock(&ifmgd->mtx);
Expand Down

0 comments on commit 5ce6e43

Please sign in to comment.