On Tue, 2009-01-06 at 09:28 +0530, Sujith wrote: > Move to the advertised channel on reception of > a CSA element. This is needed for 802.11h compliance. > > Signed-off-by: Sujith <Sujith.Manoharan@xxxxxxxxxxx> Looks good to me now, sorry about the ping pong. Acked-by: Johannes Berg <johannes@xxxxxxxxxxxxxxxx> > --- > v2 > -- > * Add a new variable to hold the CSA channel > * Use msecs_to_jiffies for calculating expiration time > * Add a check to drop beacons in case of a frequency mismatch > > v3 > -- > * Add a BSSID check when handling CSA action frame > > net/mac80211/ieee80211_i.h | 11 ++++++- > net/mac80211/iface.c | 2 + > net/mac80211/mlme.c | 13 +++++++ > net/mac80211/rx.c | 20 +++++++++++ > net/mac80211/spectmgmt.c | 77 ++++++++++++++++++++++++++++++++++++++++++++ > 5 files changed, 122 insertions(+), 1 deletions(-) > > diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h > index 117718b..d2a007a 100644 > --- a/net/mac80211/ieee80211_i.h > +++ b/net/mac80211/ieee80211_i.h > @@ -259,6 +259,7 @@ struct mesh_preq_queue { > #define IEEE80211_STA_AUTO_CHANNEL_SEL BIT(12) > #define IEEE80211_STA_PRIVACY_INVOKED BIT(13) > #define IEEE80211_STA_TKIP_WEP_USED BIT(14) > +#define IEEE80211_STA_CSA_RECEIVED BIT(15) > /* flags for MLME request */ > #define IEEE80211_STA_REQ_SCAN 0 > #define IEEE80211_STA_REQ_DIRECT_PROBE 1 > @@ -283,7 +284,9 @@ enum ieee80211_sta_mlme_state { > > struct ieee80211_if_sta { > struct timer_list timer; > + struct timer_list chswitch_timer; > struct work_struct work; > + struct work_struct chswitch_work; > u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; > u8 ssid[IEEE80211_MAX_SSID_LEN]; > enum ieee80211_sta_mlme_state state; > @@ -542,6 +545,7 @@ enum { > enum queue_stop_reason { > IEEE80211_QUEUE_STOP_REASON_DRIVER, > IEEE80211_QUEUE_STOP_REASON_PS, > + IEEE80211_QUEUE_STOP_REASON_CSA > }; > > /* maximum number of hardware queues we support. */ > @@ -631,7 +635,7 @@ struct ieee80211_local { > unsigned long last_scan_completed; > struct delayed_work scan_work; > struct ieee80211_sub_if_data *scan_sdata; > - struct ieee80211_channel *oper_channel, *scan_channel; > + struct ieee80211_channel *oper_channel, *scan_channel, *csa_channel; > enum nl80211_channel_type oper_channel_type; > u8 scan_ssid[IEEE80211_MAX_SSID_LEN]; > size_t scan_ssid_len; > @@ -964,6 +968,11 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, > void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, > struct ieee80211_mgmt *mgmt, > size_t len); > +void ieee80211_chswitch_timer(unsigned long data); > +void ieee80211_chswitch_work(struct work_struct *work); > +void ieee80211_process_chanswitch(struct ieee80211_sub_if_data *sdata, > + struct ieee80211_channel_sw_ie *sw_elem, > + struct ieee80211_bss *bss); > > /* utility functions/constants */ > extern void *mac80211_wiphy_privid; /* for wiphy privid */ > diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c > index 2c7a87d..f927641 100644 > --- a/net/mac80211/iface.c > +++ b/net/mac80211/iface.c > @@ -443,6 +443,7 @@ static int ieee80211_stop(struct net_device *dev) > WLAN_REASON_DEAUTH_LEAVING); > > memset(sdata->u.sta.bssid, 0, ETH_ALEN); > + del_timer_sync(&sdata->u.sta.chswitch_timer); > del_timer_sync(&sdata->u.sta.timer); > /* > * If the timer fired while we waited for it, it will have > @@ -452,6 +453,7 @@ static int ieee80211_stop(struct net_device *dev) > * it no longer is. > */ > cancel_work_sync(&sdata->u.sta.work); > + cancel_work_sync(&sdata->u.sta.chswitch_work); > /* > * When we get here, the interface is marked down. > * Call synchronize_rcu() to wait for the RX path > diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c > index e317c6d..4d25cf9 100644 > --- a/net/mac80211/mlme.c > +++ b/net/mac80211/mlme.c > @@ -1645,6 +1645,13 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, > if (!bss) > return; > > + if (elems->ch_switch_elem && (elems->ch_switch_elem_len == 3) && > + (memcmp(mgmt->bssid, sdata->u.sta.bssid, ETH_ALEN) == 0)) { > + struct ieee80211_channel_sw_ie *sw_elem = > + (struct ieee80211_channel_sw_ie *)elems->ch_switch_elem; > + ieee80211_process_chanswitch(sdata, sw_elem, bss); > + } > + > /* was just updated in ieee80211_bss_info_update */ > beacon_timestamp = bss->timestamp; > > @@ -1781,6 +1788,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, > memcmp(ifsta->bssid, mgmt->bssid, ETH_ALEN) != 0) > return; > > + if (rx_status->freq != local->hw.conf.channel->center_freq) > + return; > + > ieee80211_sta_wmm_params(local, ifsta, elems.wmm_param, > elems.wmm_param_len); > > @@ -2447,8 +2457,11 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) > > ifsta = &sdata->u.sta; > INIT_WORK(&ifsta->work, ieee80211_sta_work); > + INIT_WORK(&ifsta->chswitch_work, ieee80211_chswitch_work); > setup_timer(&ifsta->timer, ieee80211_sta_timer, > (unsigned long) sdata); > + setup_timer(&ifsta->chswitch_timer, ieee80211_chswitch_timer, > + (unsigned long) sdata); > skb_queue_head_init(&ifsta->skb_queue); > > ifsta->capab = WLAN_CAPABILITY_ESS; > diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c > index 384cb3b..4b7bc3d 100644 > --- a/net/mac80211/rx.c > +++ b/net/mac80211/rx.c > @@ -1564,7 +1564,9 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) > { > struct ieee80211_local *local = rx->local; > struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev); > + struct ieee80211_if_sta *ifsta = &sdata->u.sta; > struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data; > + struct ieee80211_bss *bss; > int len = rx->skb->len; > > if (!ieee80211_is_action(mgmt->frame_control)) > @@ -1613,6 +1615,24 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) > return RX_DROP_MONITOR; > ieee80211_process_measurement_req(sdata, mgmt, len); > break; > + case WLAN_ACTION_SPCT_CHL_SWITCH: > + if (len < (IEEE80211_MIN_ACTION_SIZE + > + sizeof(mgmt->u.action.u.chan_switch))) > + return RX_DROP_MONITOR; > + > + if (memcmp(mgmt->bssid, ifsta->bssid, ETH_ALEN) != 0) > + return RX_DROP_MONITOR; > + > + bss = ieee80211_rx_bss_get(local, ifsta->bssid, > + local->hw.conf.channel->center_freq, > + ifsta->ssid, ifsta->ssid_len); > + if (!bss) > + return RX_DROP_MONITOR; > + > + ieee80211_process_chanswitch(sdata, > + &mgmt->u.action.u.chan_switch.sw_elem, bss); > + ieee80211_rx_bss_put(local, bss); > + break; > } > break; > default: > diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c > index f72bad6..22ad480 100644 > --- a/net/mac80211/spectmgmt.c > +++ b/net/mac80211/spectmgmt.c > @@ -84,3 +84,80 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, > mgmt->sa, mgmt->bssid, > mgmt->u.action.u.measurement.dialog_token); > } > + > +void ieee80211_chswitch_work(struct work_struct *work) > +{ > + struct ieee80211_sub_if_data *sdata = > + container_of(work, struct ieee80211_sub_if_data, u.sta.chswitch_work); > + struct ieee80211_bss *bss; > + struct ieee80211_if_sta *ifsta = &sdata->u.sta; > + > + if (!netif_running(sdata->dev)) > + return; > + > + bss = ieee80211_rx_bss_get(sdata->local, ifsta->bssid, > + sdata->local->hw.conf.channel->center_freq, > + ifsta->ssid, ifsta->ssid_len); > + if (!bss) > + goto exit; > + > + sdata->local->oper_channel = sdata->local->csa_channel; > + if (!ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL)) > + bss->freq = sdata->local->oper_channel->center_freq; > + > + ieee80211_rx_bss_put(sdata->local, bss); > +exit: > + ifsta->flags &= ~IEEE80211_STA_CSA_RECEIVED; > + ieee80211_wake_queues_by_reason(&sdata->local->hw, > + IEEE80211_QUEUE_STOP_REASON_CSA); > +} > + > +void ieee80211_chswitch_timer(unsigned long data) > +{ > + struct ieee80211_sub_if_data *sdata = > + (struct ieee80211_sub_if_data *) data; > + struct ieee80211_if_sta *ifsta = &sdata->u.sta; > + > + queue_work(sdata->local->hw.workqueue, &ifsta->chswitch_work); > +} > + > +void ieee80211_process_chanswitch(struct ieee80211_sub_if_data *sdata, > + struct ieee80211_channel_sw_ie *sw_elem, > + struct ieee80211_bss *bss) > +{ > + struct ieee80211_channel *new_ch; > + struct ieee80211_if_sta *ifsta = &sdata->u.sta; > + int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num); > + > + /* FIXME: Handle ADHOC later */ > + if (sdata->vif.type != NL80211_IFTYPE_STATION) > + return; > + > + if (ifsta->state != IEEE80211_STA_MLME_ASSOCIATED) > + return; > + > + if (sdata->local->sw_scanning || sdata->local->hw_scanning) > + return; > + > + /* Disregard subsequent beacons if we are already running a timer > + processing a CSA */ > + > + if (ifsta->flags & IEEE80211_STA_CSA_RECEIVED) > + return; > + > + new_ch = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq); > + if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED) > + return; > + > + sdata->local->csa_channel = new_ch; > + > + if (sw_elem->count <= 1) { > + queue_work(sdata->local->hw.workqueue, &ifsta->chswitch_work); > + } else { > + ieee80211_stop_queues_by_reason(&sdata->local->hw, > + IEEE80211_QUEUE_STOP_REASON_CSA); > + ifsta->flags |= IEEE80211_STA_CSA_RECEIVED; > + mod_timer(&ifsta->chswitch_timer, > + jiffies + msecs_to_jiffies(sw_elem->count * bss->beacon_int)); > + } > +}
Attachment:
signature.asc
Description: This is a digitally signed message part