mwifiex: channel switch support for mwifiex
This patch adds cfg80211 channel_switch support for mwifiex. Upon receiving channel switch request, driver would parse channel switch announcement IE from beacon_data. If TX is blocked, netdev queues are stopped. IEs from csa_beacon are then parsed and set to FW. Signed-off-by: Avinash Patil <patila@marvell.com> Signed-off-by: Qingshui Gao <gaoqs@marvell.com> Signed-off-by: Cathy Luo <cluo@marvell.com> Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
This commit is contained in:
		
							parent
							
								
									3b57c1a713
								
							
						
					
					
						commit
						7d652034d1
					
				| @ -240,3 +240,40 @@ int mwifiex_11h_handle_radar_detected(struct mwifiex_private *priv, | |||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | /* This is work queue function for channel switch handling.
 | ||||||
|  |  * This function takes care of updating new channel definitin to | ||||||
|  |  * bss config structure, restart AP and indicate channel switch success | ||||||
|  |  * to cfg80211. | ||||||
|  |  */ | ||||||
|  | void mwifiex_dfs_chan_sw_work_queue(struct work_struct *work) | ||||||
|  | { | ||||||
|  | 	struct mwifiex_uap_bss_param *bss_cfg; | ||||||
|  | 	struct delayed_work *delayed_work = | ||||||
|  | 			container_of(work, struct delayed_work, work); | ||||||
|  | 	struct mwifiex_private *priv = | ||||||
|  | 			container_of(delayed_work, struct mwifiex_private, | ||||||
|  | 				     dfs_chan_sw_work); | ||||||
|  | 
 | ||||||
|  | 	if (WARN_ON(!priv)) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	bss_cfg = &priv->bss_cfg; | ||||||
|  | 	if (!bss_cfg->beacon_period) { | ||||||
|  | 		dev_err(priv->adapter->dev, | ||||||
|  | 			"channel switch: AP already stopped\n"); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	mwifiex_uap_set_channel(bss_cfg, priv->dfs_chandef); | ||||||
|  | 
 | ||||||
|  | 	if (mwifiex_config_start_uap(priv, bss_cfg)) { | ||||||
|  | 		dev_dbg(priv->adapter->dev, | ||||||
|  | 			"Failed to start AP after channel switch\n"); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	dev_notice(priv->adapter->dev, | ||||||
|  | 		   "indicating channel switch completion to kernel\n"); | ||||||
|  | 	cfg80211_ch_switch_notify(priv->netdev, &priv->dfs_chandef); | ||||||
|  | } | ||||||
|  | |||||||
| @ -2399,7 +2399,7 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, | |||||||
| 	struct mwifiex_private *priv; | 	struct mwifiex_private *priv; | ||||||
| 	struct net_device *dev; | 	struct net_device *dev; | ||||||
| 	void *mdev_priv; | 	void *mdev_priv; | ||||||
| 	char dfs_cac_str[MWIFIEX_MAX_WQ_LEN]; | 	char dfs_cac_str[MWIFIEX_MAX_WQ_LEN], dfs_chsw_str[MWIFIEX_MAX_WQ_LEN]; | ||||||
| 
 | 
 | ||||||
| 	if (!adapter) | 	if (!adapter) | ||||||
| 		return ERR_PTR(-EFAULT); | 		return ERR_PTR(-EFAULT); | ||||||
| @ -2582,6 +2582,24 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, | |||||||
| 
 | 
 | ||||||
| 	INIT_DELAYED_WORK(&priv->dfs_cac_work, mwifiex_dfs_cac_work_queue); | 	INIT_DELAYED_WORK(&priv->dfs_cac_work, mwifiex_dfs_cac_work_queue); | ||||||
| 
 | 
 | ||||||
|  | 	strcpy(dfs_chsw_str, "MWIFIEX_DFS_CHSW"); | ||||||
|  | 	strcat(dfs_chsw_str, name); | ||||||
|  | 	priv->dfs_chan_sw_workqueue = alloc_workqueue(dfs_chsw_str, | ||||||
|  | 						      WQ_HIGHPRI | WQ_UNBOUND | | ||||||
|  | 						      WQ_MEM_RECLAIM, 1); | ||||||
|  | 	if (!priv->dfs_chan_sw_workqueue) { | ||||||
|  | 		wiphy_err(wiphy, "cannot register virtual network device\n"); | ||||||
|  | 		free_netdev(dev); | ||||||
|  | 		priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; | ||||||
|  | 		priv->netdev = NULL; | ||||||
|  | 		memset(&priv->wdev, 0, sizeof(priv->wdev)); | ||||||
|  | 		priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; | ||||||
|  | 		return ERR_PTR(-ENOMEM); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	INIT_DELAYED_WORK(&priv->dfs_chan_sw_work, | ||||||
|  | 			  mwifiex_dfs_chan_sw_work_queue); | ||||||
|  | 
 | ||||||
| 	sema_init(&priv->async_sem, 1); | 	sema_init(&priv->async_sem, 1); | ||||||
| 
 | 
 | ||||||
| 	dev_dbg(adapter->dev, "info: %s: Marvell 802.11 Adapter\n", dev->name); | 	dev_dbg(adapter->dev, "info: %s: Marvell 802.11 Adapter\n", dev->name); | ||||||
| @ -2637,6 +2655,11 @@ int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) | |||||||
| 		priv->dfs_cac_workqueue = NULL; | 		priv->dfs_cac_workqueue = NULL; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if (priv->dfs_chan_sw_workqueue) { | ||||||
|  | 		flush_workqueue(priv->dfs_chan_sw_workqueue); | ||||||
|  | 		destroy_workqueue(priv->dfs_chan_sw_workqueue); | ||||||
|  | 		priv->dfs_chan_sw_workqueue = NULL; | ||||||
|  | 	} | ||||||
| 	/* Clear the priv in adapter */ | 	/* Clear the priv in adapter */ | ||||||
| 	priv->netdev->ieee80211_ptr = NULL; | 	priv->netdev->ieee80211_ptr = NULL; | ||||||
| 	priv->netdev = NULL; | 	priv->netdev = NULL; | ||||||
| @ -3115,6 +3138,62 @@ mwifiex_cfg80211_add_station(struct wiphy *wiphy, struct net_device *dev, | |||||||
| 	return mwifiex_tdls_oper(priv, mac, MWIFIEX_TDLS_CREATE_LINK); | 	return mwifiex_tdls_oper(priv, mac, MWIFIEX_TDLS_CREATE_LINK); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static int | ||||||
|  | mwifiex_cfg80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, | ||||||
|  | 				struct cfg80211_csa_settings *params) | ||||||
|  | { | ||||||
|  | 	struct ieee_types_header *chsw_ie; | ||||||
|  | 	struct ieee80211_channel_sw_ie *channel_sw; | ||||||
|  | 	int chsw_msec; | ||||||
|  | 	struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); | ||||||
|  | 
 | ||||||
|  | 	if (priv->adapter->scan_processing) { | ||||||
|  | 		dev_err(priv->adapter->dev, | ||||||
|  | 			"radar detection: scan in process...\n"); | ||||||
|  | 		return -EBUSY; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (priv->wdev.cac_started) | ||||||
|  | 		return -EBUSY; | ||||||
|  | 
 | ||||||
|  | 	if (cfg80211_chandef_identical(¶ms->chandef, | ||||||
|  | 				       &priv->dfs_chandef)) | ||||||
|  | 		return -EINVAL; | ||||||
|  | 
 | ||||||
|  | 	chsw_ie = (void *)cfg80211_find_ie(WLAN_EID_CHANNEL_SWITCH, | ||||||
|  | 					   params->beacon_csa.tail, | ||||||
|  | 					   params->beacon_csa.tail_len); | ||||||
|  | 	if (!chsw_ie) { | ||||||
|  | 		dev_err(priv->adapter->dev, | ||||||
|  | 			"Could not parse channel switch announcement IE\n"); | ||||||
|  | 		return -EINVAL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	channel_sw = (void *)(chsw_ie + 1); | ||||||
|  | 	if (channel_sw->mode) { | ||||||
|  | 		if (netif_carrier_ok(priv->netdev)) | ||||||
|  | 			netif_carrier_off(priv->netdev); | ||||||
|  | 		mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (mwifiex_del_mgmt_ies(priv)) | ||||||
|  | 		wiphy_err(wiphy, "Failed to delete mgmt IEs!\n"); | ||||||
|  | 
 | ||||||
|  | 	if (mwifiex_set_mgmt_ies(priv, ¶ms->beacon_csa)) { | ||||||
|  | 		wiphy_err(wiphy, "%s: setting mgmt ies failed\n", __func__); | ||||||
|  | 		return -EFAULT; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	memcpy(&priv->dfs_chandef, ¶ms->chandef, sizeof(priv->dfs_chandef)); | ||||||
|  | 	memcpy(&priv->beacon_after, ¶ms->beacon_after, | ||||||
|  | 	       sizeof(priv->beacon_after)); | ||||||
|  | 
 | ||||||
|  | 	chsw_msec = max(channel_sw->count * priv->bss_cfg.beacon_period, 100); | ||||||
|  | 	queue_delayed_work(priv->dfs_chan_sw_workqueue, &priv->dfs_chan_sw_work, | ||||||
|  | 			   msecs_to_jiffies(chsw_msec)); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static int | static int | ||||||
| mwifiex_cfg80211_start_radar_detection(struct wiphy *wiphy, | mwifiex_cfg80211_start_radar_detection(struct wiphy *wiphy, | ||||||
| 				       struct net_device *dev, | 				       struct net_device *dev, | ||||||
| @ -3210,6 +3289,7 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = { | |||||||
| 	.add_station = mwifiex_cfg80211_add_station, | 	.add_station = mwifiex_cfg80211_add_station, | ||||||
| 	.change_station = mwifiex_cfg80211_change_station, | 	.change_station = mwifiex_cfg80211_change_station, | ||||||
| 	.start_radar_detection = mwifiex_cfg80211_start_radar_detection, | 	.start_radar_detection = mwifiex_cfg80211_start_radar_detection, | ||||||
|  | 	.channel_switch = mwifiex_cfg80211_channel_switch, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #ifdef CONFIG_PM | #ifdef CONFIG_PM | ||||||
| @ -3313,7 +3393,8 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) | |||||||
| 	wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | | 	wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | | ||||||
| 			WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | | 			WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | | ||||||
| 			WIPHY_FLAG_AP_UAPSD | | 			WIPHY_FLAG_AP_UAPSD | | ||||||
| 			WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; | 			WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | | ||||||
|  | 			WIPHY_FLAG_HAS_CHANNEL_SWITCH; | ||||||
| 
 | 
 | ||||||
| 	if (ISSUPP_TDLS_ENABLED(adapter->fw_cap_info)) | 	if (ISSUPP_TDLS_ENABLED(adapter->fw_cap_info)) | ||||||
| 		wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | | 		wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | | ||||||
|  | |||||||
| @ -325,6 +325,7 @@ static int mwifiex_uap_set_head_tail_ies(struct mwifiex_private *priv, | |||||||
| { | { | ||||||
| 	struct mwifiex_ie *gen_ie; | 	struct mwifiex_ie *gen_ie; | ||||||
| 	struct ieee_types_header *rsn_ie = NULL, *wpa_ie = NULL; | 	struct ieee_types_header *rsn_ie = NULL, *wpa_ie = NULL; | ||||||
|  | 	struct ieee_types_header *chsw_ie = NULL; | ||||||
| 	u16 gen_idx = MWIFIEX_AUTO_IDX_MASK, ie_len = 0; | 	u16 gen_idx = MWIFIEX_AUTO_IDX_MASK, ie_len = 0; | ||||||
| 	const u8 *vendor_ie; | 	const u8 *vendor_ie; | ||||||
| 
 | 
 | ||||||
| @ -356,9 +357,18 @@ static int mwifiex_uap_set_head_tail_ies(struct mwifiex_private *priv, | |||||||
| 			ie_len += wpa_ie->len + 2; | 			ie_len += wpa_ie->len + 2; | ||||||
| 			gen_ie->ie_length = cpu_to_le16(ie_len); | 			gen_ie->ie_length = cpu_to_le16(ie_len); | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
|  | 		chsw_ie = (void *)cfg80211_find_ie(WLAN_EID_CHANNEL_SWITCH, | ||||||
|  | 						   info->tail, info->tail_len); | ||||||
|  | 		if (chsw_ie) { | ||||||
|  | 			memcpy(gen_ie->ie_buffer + ie_len, | ||||||
|  | 			       chsw_ie, chsw_ie->len + 2); | ||||||
|  | 			ie_len += chsw_ie->len + 2; | ||||||
|  | 			gen_ie->ie_length = cpu_to_le16(ie_len); | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (rsn_ie || wpa_ie) { | 	if (rsn_ie || wpa_ie || chsw_ie) { | ||||||
| 		if (mwifiex_update_uap_custom_ie(priv, gen_ie, &gen_idx, NULL, | 		if (mwifiex_update_uap_custom_ie(priv, gen_ie, &gen_idx, NULL, | ||||||
| 						 NULL, NULL, NULL)) { | 						 NULL, NULL, NULL)) { | ||||||
| 			kfree(gen_ie); | 			kfree(gen_ie); | ||||||
|  | |||||||
| @ -591,6 +591,10 @@ struct mwifiex_private { | |||||||
| 	struct cfg80211_chan_def dfs_chandef; | 	struct cfg80211_chan_def dfs_chandef; | ||||||
| 	struct workqueue_struct *dfs_cac_workqueue; | 	struct workqueue_struct *dfs_cac_workqueue; | ||||||
| 	struct delayed_work dfs_cac_work; | 	struct delayed_work dfs_cac_work; | ||||||
|  | 	struct timer_list dfs_chan_switch_timer; | ||||||
|  | 	struct workqueue_struct *dfs_chan_sw_workqueue; | ||||||
|  | 	struct delayed_work dfs_chan_sw_work; | ||||||
|  | 	struct cfg80211_beacon_data beacon_after; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| enum mwifiex_ba_status { | enum mwifiex_ba_status { | ||||||
| @ -1394,6 +1398,7 @@ struct sk_buff * | |||||||
| mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv, | mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv, | ||||||
| 				struct sk_buff *skb, u8 flag, u64 *cookie); | 				struct sk_buff *skb, u8 flag, u64 *cookie); | ||||||
| void mwifiex_dfs_cac_work_queue(struct work_struct *work); | void mwifiex_dfs_cac_work_queue(struct work_struct *work); | ||||||
|  | void mwifiex_dfs_chan_sw_work_queue(struct work_struct *work); | ||||||
| void mwifiex_abort_cac(struct mwifiex_private *priv); | void mwifiex_abort_cac(struct mwifiex_private *priv); | ||||||
| int mwifiex_11h_handle_radar_detected(struct mwifiex_private *priv, | int mwifiex_11h_handle_radar_detected(struct mwifiex_private *priv, | ||||||
| 				      struct sk_buff *skb); | 				      struct sk_buff *skb); | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user