* [PATCH v3 1/2] cfg80211: add channel switch notify event
@ 2012-04-06 20:35 Thomas Pedersen
2012-04-06 20:35 ` [PATCH v3 2/2] ath6kl: handle concurrent AP-STA channel switches Thomas Pedersen
2012-04-12 3:37 ` [PATCH v3 1/2] cfg80211: add channel switch notify event Johannes Berg
0 siblings, 2 replies; 6+ messages in thread
From: Thomas Pedersen @ 2012-04-06 20:35 UTC (permalink / raw)
To: kvalo; +Cc: ath6kl-devel, linux-wireless, Thomas Pedersen
The firmware may decide to switch channels while already beaconing, e.g.
in response to a cfg80211 connect request on a different vif. Add this
event to notify userspace when an AP or GO interface has successfully
migrated to a new channel, so it can update its configuration
accordingly.
Signed-off-by: Thomas Pedersen <c_tpeder@qca.qualcomm.com>
---
v2: update wdev->channel (Johannes)
enforce AP or GO interface type
v3: only support GFP_KERNEL context (Johannes)
include/linux/nl80211.h | 7 +++++++
include/net/cfg80211.h | 11 +++++++++++
net/wireless/mlme.c | 27 +++++++++++++++++++++++++++
net/wireless/nl80211.c | 32 ++++++++++++++++++++++++++++++++
net/wireless/nl80211.h | 4 ++++
5 files changed, 81 insertions(+), 0 deletions(-)
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index e474f6e..49f2bbe 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -548,6 +548,11 @@
* @NL80211_CMD_SET_NOACK_MAP: sets a bitmap for the individual TIDs whether
* No Acknowledgement Policy should be applied.
*
+ * @NL80211_CMD_CH_SWITCH_NOTIFY: An AP or GO may decide to switch channels
+ * independently of the userspace SME, send this event indicating
+ * %NL80211_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ with
+ * %NL80211_ATTR_WIPHY_CHANNEL_TYPE.
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -689,6 +694,8 @@ enum nl80211_commands {
NL80211_CMD_SET_NOACK_MAP,
+ NL80211_CMD_CH_SWITCH_NOTIFY,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 69b7ad3..0f246b6 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -3340,6 +3340,17 @@ int cfg80211_can_beacon_sec_chan(struct wiphy *wiphy,
enum nl80211_channel_type channel_type);
/*
+ * cfg80211_ch_switch_notify - update wdev channel and notify userspace
+ * @dev: the device which switched channels
+ * @freq: new channel frequency (in MHz)
+ * @type: channel type
+ *
+ * Acquires wdev_lock, so must only be called from sleepable driver context!
+ */
+void cfg80211_ch_switch_notify(struct net_device *dev, int freq,
+ enum nl80211_channel_type type);
+
+/*
* cfg80211_calculate_bitrate - calculate actual bitrate (in 100Kbps units)
* @rate: given rate_info to calculate bitrate from
*
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index f5a7ac3..50da120 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -928,6 +928,33 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index,
}
EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify);
+void cfg80211_ch_switch_notify(struct net_device *dev, int freq,
+ enum nl80211_channel_type type)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+ struct ieee80211_channel *chan;
+
+ wdev_lock(wdev);
+
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
+ wdev->iftype != NL80211_IFTYPE_P2P_GO))
+ goto out;
+
+ chan = rdev_freq_to_chan(rdev, freq, type);
+ if (WARN_ON(!chan))
+ goto out;
+
+ wdev->channel = chan;
+
+ nl80211_ch_switch_notify(rdev, dev, freq, type, GFP_KERNEL);
+out:
+ wdev_unlock(wdev);
+ return;
+}
+EXPORT_SYMBOL(cfg80211_ch_switch_notify);
+
bool cfg80211_rx_spurious_frame(struct net_device *dev,
const u8 *addr, gfp_t gfp)
{
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 4c1eb94..d5d489f 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -7890,6 +7890,38 @@ void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
nlmsg_free(msg);
}
+void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, int freq,
+ enum nl80211_channel_type type, gfp_t gfp)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CH_SWITCH_NOTIFY);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, type);
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+ nl80211_mlme_mcgrp.id, gfp);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
void
nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *peer,
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index 4ffe50d..01a1122 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -118,6 +118,10 @@ void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
struct net_device *netdev, int index,
const u8 *bssid, bool preauth, gfp_t gfp);
+void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, int freq,
+ enum nl80211_channel_type type, gfp_t gfp);
+
bool nl80211_unexpected_frame(struct net_device *dev,
const u8 *addr, gfp_t gfp);
bool nl80211_unexpected_4addr_frame(struct net_device *dev,
--
1.7.4.1
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH v3 2/2] ath6kl: handle concurrent AP-STA channel switches 2012-04-06 20:35 [PATCH v3 1/2] cfg80211: add channel switch notify event Thomas Pedersen @ 2012-04-06 20:35 ` Thomas Pedersen 2012-04-09 15:05 ` Kalle Valo 2012-04-12 6:56 ` Kalle Valo 2012-04-12 3:37 ` [PATCH v3 1/2] cfg80211: add channel switch notify event Johannes Berg 1 sibling, 2 replies; 6+ messages in thread From: Thomas Pedersen @ 2012-04-06 20:35 UTC (permalink / raw) To: kvalo; +Cc: ath6kl-devel, linux-wireless, Thomas Pedersen If an ath6kl AP vif is beaconing on one channel, and a STA vif associates on a different channel, a WMI_DISCONNECT event will be sent to the AP vif. Make the AP vif follow the STA interface, and notify userspace. Signed-off-by: Thomas Pedersen <c_tpeder@qca.qualcomm.com> --- v2: coalesce formats (Joe) v3: cfg80211 API changed drivers/net/wireless/ath/ath6kl/cfg80211.c | 15 ++++++++ drivers/net/wireless/ath/ath6kl/cfg80211.h | 2 + drivers/net/wireless/ath/ath6kl/core.h | 2 + drivers/net/wireless/ath/ath6kl/main.c | 55 +++++++++++++++++++++++++++- drivers/net/wireless/ath/ath6kl/wmi.h | 12 ++++++ 5 files changed, 85 insertions(+), 1 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 1272508..1079bf1 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1015,6 +1015,20 @@ out: vif->scan_req = NULL; } +void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq, + enum wmi_phy_mode mode) +{ + enum nl80211_channel_type type; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "channel switch notify nw_type %d freq %d mode %d\n", + vif->nw_type, freq, mode); + + type = (mode == WMI_11G_HT20) ? NL80211_CHAN_HT20 : NL80211_CHAN_NO_HT; + + cfg80211_ch_switch_notify(vif->ndev, freq, type); +} + static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, u8 key_index, bool pairwise, const u8 *mac_addr, @@ -2643,6 +2657,7 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, return res; } + memcpy(&vif->profile, &p, sizeof(p)); res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p); if (res < 0) return res; diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.h b/drivers/net/wireless/ath/ath6kl/cfg80211.h index c5def43..5ea8cbb 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.h +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.h @@ -28,6 +28,8 @@ enum ath6kl_cfg_suspend_mode { struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name, enum nl80211_iftype type, u8 fw_vif_idx, u8 nw_type); +void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq, + enum wmi_phy_mode mode); void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted); void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel, diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 75b1d86..0b4078d 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -540,6 +540,7 @@ struct ath6kl_vif { u8 assoc_bss_dtim_period; struct net_device_stats net_stats; struct target_stats target_stats; + struct wmi_connect_cmd profile; struct list_head mc_filter; }; @@ -628,6 +629,7 @@ struct ath6kl { u8 sta_list_index; struct ath6kl_req_key ap_mode_bkey; struct sk_buff_head mcastpsq; + u32 want_ch_switch; /* * FIXME: protects access to mcastpsq but is actually useless as diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index 7f3addd..58468e0 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -434,6 +434,13 @@ void ath6kl_connect_ap_mode_bss(struct ath6kl_vif *vif, u16 channel) break; } + if (ar->want_ch_switch & (1 << vif->fw_vif_idx)) { + ar->want_ch_switch &= ~(1 << vif->fw_vif_idx); + /* we actually don't know the phymode, default to HT20 */ + ath6kl_cfg80211_ch_switch_notify(vif, channel, + WMI_11G_HT20); + } + ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, NONE_BSS_FILTER, 0); set_bit(CONNECTED, &vif->flags); netif_carrier_on(vif->ndev); @@ -582,6 +589,45 @@ void ath6kl_scan_complete_evt(struct ath6kl_vif *vif, int status) ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "scan complete: %d\n", status); } +static int ath6kl_commit_ch_switch(struct ath6kl_vif *vif, u16 channel) +{ + + struct ath6kl *ar = vif->ar; + + vif->next_chan = cpu_to_le16(channel); + vif->profile.ch = cpu_to_le16(channel); + + switch (vif->nw_type) { + case AP_NETWORK: + return ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, + &vif->profile); + default: + ath6kl_err("won't switch channels nw_type=%d\n", vif->nw_type); + return -ENOTSUPP; + } +} + +static void ath6kl_check_ch_switch(struct ath6kl *ar, u16 channel) +{ + + struct ath6kl_vif *vif; + int res = 0; + + if (!ar->want_ch_switch) + return; + + spin_lock_bh(&ar->list_lock); + list_for_each_entry(vif, &ar->vif_list, list) { + if (ar->want_ch_switch & (1 << vif->fw_vif_idx)) + res = ath6kl_commit_ch_switch(vif, channel); + + if (res) + ath6kl_err("channel switch failed nw_type %d res %d\n", + vif->nw_type, res); + } + spin_unlock_bh(&ar->list_lock); +} + void ath6kl_connect_event(struct ath6kl_vif *vif, u16 channel, u8 *bssid, u16 listen_int, u16 beacon_int, enum network_type net_type, u8 beacon_ie_len, @@ -599,9 +645,11 @@ void ath6kl_connect_event(struct ath6kl_vif *vif, u16 channel, u8 *bssid, memcpy(vif->bssid, bssid, sizeof(vif->bssid)); vif->bss_ch = channel; - if ((vif->nw_type == INFRA_NETWORK)) + if ((vif->nw_type == INFRA_NETWORK)) { ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx, vif->listen_intvl_t, 0); + ath6kl_check_ch_switch(ar, channel); + } netif_wake_queue(vif->ndev); @@ -924,6 +972,11 @@ void ath6kl_disconnect_event(struct ath6kl_vif *vif, u8 reason, u8 *bssid, struct ath6kl *ar = vif->ar; if (vif->nw_type == AP_NETWORK) { + /* disconnect due to other STA vif switching channels */ + if (reason == BSS_DISCONNECTED && + prot_reason_status == WMI_AP_REASON_STA_ROAM) + ar->want_ch_switch |= 1 << vif->fw_vif_idx; + if (!ath6kl_remove_sta(ar, bssid, prot_reason_status)) return; diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index b99e9bd..88a1603 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -1145,6 +1145,7 @@ enum wmi_phy_mode { WMI_11AG_MODE = 0x3, WMI_11B_MODE = 0x4, WMI_11GONLY_MODE = 0x5, + WMI_11G_HT20 = 0x6, }; #define WMI_MAX_CHANNELS 32 @@ -1452,6 +1453,17 @@ enum wmi_disconnect_reason { IBSS_MERGE = 0xe, }; +/* AP mode disconnect proto_reasons */ +enum ap_disconnect_reason { + WMI_AP_REASON_STA_LEFT = 101, + WMI_AP_REASON_FROM_HOST = 102, + WMI_AP_REASON_COMM_TIMEOUT = 103, + WMI_AP_REASON_MAX_STA = 104, + WMI_AP_REASON_ACL = 105, + WMI_AP_REASON_STA_ROAM = 106, + WMI_AP_REASON_DFS_CHANNEL = 107, +}; + #define ATH6KL_COUNTRY_RD_SHIFT 16 struct ath6kl_wmi_regdomain { -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v3 2/2] ath6kl: handle concurrent AP-STA channel switches 2012-04-06 20:35 ` [PATCH v3 2/2] ath6kl: handle concurrent AP-STA channel switches Thomas Pedersen @ 2012-04-09 15:05 ` Kalle Valo 2012-04-11 19:35 ` John W. Linville 2012-04-12 6:56 ` Kalle Valo 1 sibling, 1 reply; 6+ messages in thread From: Kalle Valo @ 2012-04-09 15:05 UTC (permalink / raw) To: John W. Linville; +Cc: Thomas Pedersen, ath6kl-devel, linux-wireless On 04/06/2012 11:35 PM, Thomas Pedersen wrote: > If an ath6kl AP vif is beaconing on one channel, and a STA vif > associates on a different channel, a WMI_DISCONNECT event will be sent > to the AP vif. Make the AP vif follow the STA interface, and notify > userspace. > > Signed-off-by: Thomas Pedersen <c_tpeder@qca.qualcomm.com> John, if it's ok for you I can take this patch ath6kl.git once you have applied the cfg80211 patch. Kalle ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v3 2/2] ath6kl: handle concurrent AP-STA channel switches 2012-04-09 15:05 ` Kalle Valo @ 2012-04-11 19:35 ` John W. Linville 0 siblings, 0 replies; 6+ messages in thread From: John W. Linville @ 2012-04-11 19:35 UTC (permalink / raw) To: Kalle Valo; +Cc: Thomas Pedersen, ath6kl-devel, linux-wireless On Mon, Apr 09, 2012 at 06:05:10PM +0300, Kalle Valo wrote: > On 04/06/2012 11:35 PM, Thomas Pedersen wrote: > > If an ath6kl AP vif is beaconing on one channel, and a STA vif > > associates on a different channel, a WMI_DISCONNECT event will be sent > > to the AP vif. Make the AP vif follow the STA interface, and notify > > userspace. > > > > Signed-off-by: Thomas Pedersen <c_tpeder@qca.qualcomm.com> > > John, if it's ok for you I can take this patch ath6kl.git once you have > applied the cfg80211 patch. OK. -- John W. Linville Someday the world will need a hero, and you linville@tuxdriver.com might be all we have. Be ready. ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v3 2/2] ath6kl: handle concurrent AP-STA channel switches 2012-04-06 20:35 ` [PATCH v3 2/2] ath6kl: handle concurrent AP-STA channel switches Thomas Pedersen 2012-04-09 15:05 ` Kalle Valo @ 2012-04-12 6:56 ` Kalle Valo 1 sibling, 0 replies; 6+ messages in thread From: Kalle Valo @ 2012-04-12 6:56 UTC (permalink / raw) To: Thomas Pedersen; +Cc: ath6kl-devel, linux-wireless On 04/06/2012 11:35 PM, Thomas Pedersen wrote: > If an ath6kl AP vif is beaconing on one channel, and a STA vif > associates on a different channel, a WMI_DISCONNECT event will be sent > to the AP vif. Make the AP vif follow the STA interface, and notify > userspace. > > Signed-off-by: Thomas Pedersen <c_tpeder@qca.qualcomm.com> Thanks, applied. But there was a sparse warning: drivers/net/wireless/ath/ath6kl/main.c:599:24: warning: incorrect type in assignment (different base types) drivers/net/wireless/ath/ath6kl/main.c:599:24: expected unsigned short [unsigned] [usertype] next_chan drivers/net/wireless/ath/ath6kl/main.c:599:24: got restricted __le16 [usertype] <noident> I fixed it like this: vif->next_chan = channel; vif->profile.ch = cpu_to_le16(channel); Please check my changes. Kalle ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v3 1/2] cfg80211: add channel switch notify event 2012-04-06 20:35 [PATCH v3 1/2] cfg80211: add channel switch notify event Thomas Pedersen 2012-04-06 20:35 ` [PATCH v3 2/2] ath6kl: handle concurrent AP-STA channel switches Thomas Pedersen @ 2012-04-12 3:37 ` Johannes Berg 1 sibling, 0 replies; 6+ messages in thread From: Johannes Berg @ 2012-04-12 3:37 UTC (permalink / raw) To: Thomas Pedersen; +Cc: kvalo, ath6kl-devel, linux-wireless On Fri, 2012-04-06 at 13:35 -0700, Thomas Pedersen wrote: > /* > + * cfg80211_ch_switch_notify - update wdev channel and notify userspace > + * @dev: the device which switched channels > + * @freq: new channel frequency (in MHz) > + * @type: channel type > + * > + * Acquires wdev_lock, so must only be called from sleepable driver context! > + */ > +void cfg80211_ch_switch_notify(struct net_device *dev, int freq, > + enum nl80211_channel_type type); I think this could be a bit more detailed -- the driver authors won't know what wdev_lock is (nor care), but the other consequence of it is that the function absolutely must not be called in the context of a cfg80211 call into the driver. johannes ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2012-04-12 6:56 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2012-04-06 20:35 [PATCH v3 1/2] cfg80211: add channel switch notify event Thomas Pedersen 2012-04-06 20:35 ` [PATCH v3 2/2] ath6kl: handle concurrent AP-STA channel switches Thomas Pedersen 2012-04-09 15:05 ` Kalle Valo 2012-04-11 19:35 ` John W. Linville 2012-04-12 6:56 ` Kalle Valo 2012-04-12 3:37 ` [PATCH v3 1/2] cfg80211: add channel switch notify event Johannes Berg
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).