linux-wireless.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [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 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

* 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

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).