diff options
author | Avinash Patil <patila@marvell.com> | 2015-01-28 05:24:24 -0500 |
---|---|---|
committer | Kalle Valo <kvalo@codeaurora.org> | 2015-01-29 03:22:10 -0500 |
commit | 7d652034d1a08bd240c98727bbd55901a174c245 (patch) | |
tree | a8473d503aeb2e51a70a59b10ff3d10871807280 /drivers/net/wireless/mwifiex | |
parent | 3b57c1a713a9dd3b8da74b6df9f16ce1f8f9144b (diff) |
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>
Diffstat (limited to 'drivers/net/wireless/mwifiex')
-rw-r--r-- | drivers/net/wireless/mwifiex/11h.c | 37 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/cfg80211.c | 85 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/ie.c | 12 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/main.h | 5 |
4 files changed, 136 insertions, 3 deletions
diff --git a/drivers/net/wireless/mwifiex/11h.c b/drivers/net/wireless/mwifiex/11h.c index 08c12aece9ae..d794686e31d6 100644 --- a/drivers/net/wireless/mwifiex/11h.c +++ b/drivers/net/wireless/mwifiex/11h.c | |||
@@ -240,3 +240,40 @@ int mwifiex_11h_handle_radar_detected(struct mwifiex_private *priv, | |||
240 | 240 | ||
241 | return 0; | 241 | return 0; |
242 | } | 242 | } |
243 | |||
244 | /* This is work queue function for channel switch handling. | ||
245 | * This function takes care of updating new channel definitin to | ||
246 | * bss config structure, restart AP and indicate channel switch success | ||
247 | * to cfg80211. | ||
248 | */ | ||
249 | void mwifiex_dfs_chan_sw_work_queue(struct work_struct *work) | ||
250 | { | ||
251 | struct mwifiex_uap_bss_param *bss_cfg; | ||
252 | struct delayed_work *delayed_work = | ||
253 | container_of(work, struct delayed_work, work); | ||
254 | struct mwifiex_private *priv = | ||
255 | container_of(delayed_work, struct mwifiex_private, | ||
256 | dfs_chan_sw_work); | ||
257 | |||
258 | if (WARN_ON(!priv)) | ||
259 | return; | ||
260 | |||
261 | bss_cfg = &priv->bss_cfg; | ||
262 | if (!bss_cfg->beacon_period) { | ||
263 | dev_err(priv->adapter->dev, | ||
264 | "channel switch: AP already stopped\n"); | ||
265 | return; | ||
266 | } | ||
267 | |||
268 | mwifiex_uap_set_channel(bss_cfg, priv->dfs_chandef); | ||
269 | |||
270 | if (mwifiex_config_start_uap(priv, bss_cfg)) { | ||
271 | dev_dbg(priv->adapter->dev, | ||
272 | "Failed to start AP after channel switch\n"); | ||
273 | return; | ||
274 | } | ||
275 | |||
276 | dev_notice(priv->adapter->dev, | ||
277 | "indicating channel switch completion to kernel\n"); | ||
278 | cfg80211_ch_switch_notify(priv->netdev, &priv->dfs_chandef); | ||
279 | } | ||
diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 7a969fd2f0cd..2d1ea938e08e 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c | |||
@@ -2399,7 +2399,7 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, | |||
2399 | struct mwifiex_private *priv; | 2399 | struct mwifiex_private *priv; |
2400 | struct net_device *dev; | 2400 | struct net_device *dev; |
2401 | void *mdev_priv; | 2401 | void *mdev_priv; |
2402 | char dfs_cac_str[MWIFIEX_MAX_WQ_LEN]; | 2402 | char dfs_cac_str[MWIFIEX_MAX_WQ_LEN], dfs_chsw_str[MWIFIEX_MAX_WQ_LEN]; |
2403 | 2403 | ||
2404 | if (!adapter) | 2404 | if (!adapter) |
2405 | return ERR_PTR(-EFAULT); | 2405 | return ERR_PTR(-EFAULT); |
@@ -2582,6 +2582,24 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, | |||
2582 | 2582 | ||
2583 | INIT_DELAYED_WORK(&priv->dfs_cac_work, mwifiex_dfs_cac_work_queue); | 2583 | INIT_DELAYED_WORK(&priv->dfs_cac_work, mwifiex_dfs_cac_work_queue); |
2584 | 2584 | ||
2585 | strcpy(dfs_chsw_str, "MWIFIEX_DFS_CHSW"); | ||
2586 | strcat(dfs_chsw_str, name); | ||
2587 | priv->dfs_chan_sw_workqueue = alloc_workqueue(dfs_chsw_str, | ||
2588 | WQ_HIGHPRI | WQ_UNBOUND | | ||
2589 | WQ_MEM_RECLAIM, 1); | ||
2590 | if (!priv->dfs_chan_sw_workqueue) { | ||
2591 | wiphy_err(wiphy, "cannot register virtual network device\n"); | ||
2592 | free_netdev(dev); | ||
2593 | priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; | ||
2594 | priv->netdev = NULL; | ||
2595 | memset(&priv->wdev, 0, sizeof(priv->wdev)); | ||
2596 | priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; | ||
2597 | return ERR_PTR(-ENOMEM); | ||
2598 | } | ||
2599 | |||
2600 | INIT_DELAYED_WORK(&priv->dfs_chan_sw_work, | ||
2601 | mwifiex_dfs_chan_sw_work_queue); | ||
2602 | |||
2585 | sema_init(&priv->async_sem, 1); | 2603 | sema_init(&priv->async_sem, 1); |
2586 | 2604 | ||
2587 | dev_dbg(adapter->dev, "info: %s: Marvell 802.11 Adapter\n", dev->name); | 2605 | 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) | |||
2637 | priv->dfs_cac_workqueue = NULL; | 2655 | priv->dfs_cac_workqueue = NULL; |
2638 | } | 2656 | } |
2639 | 2657 | ||
2658 | if (priv->dfs_chan_sw_workqueue) { | ||
2659 | flush_workqueue(priv->dfs_chan_sw_workqueue); | ||
2660 | destroy_workqueue(priv->dfs_chan_sw_workqueue); | ||
2661 | priv->dfs_chan_sw_workqueue = NULL; | ||
2662 | } | ||
2640 | /* Clear the priv in adapter */ | 2663 | /* Clear the priv in adapter */ |
2641 | priv->netdev->ieee80211_ptr = NULL; | 2664 | priv->netdev->ieee80211_ptr = NULL; |
2642 | priv->netdev = NULL; | 2665 | priv->netdev = NULL; |
@@ -3116,6 +3139,62 @@ mwifiex_cfg80211_add_station(struct wiphy *wiphy, struct net_device *dev, | |||
3116 | } | 3139 | } |
3117 | 3140 | ||
3118 | static int | 3141 | static int |
3142 | mwifiex_cfg80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, | ||
3143 | struct cfg80211_csa_settings *params) | ||
3144 | { | ||
3145 | struct ieee_types_header *chsw_ie; | ||
3146 | struct ieee80211_channel_sw_ie *channel_sw; | ||
3147 | int chsw_msec; | ||
3148 | struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); | ||
3149 | |||
3150 | if (priv->adapter->scan_processing) { | ||
3151 | dev_err(priv->adapter->dev, | ||
3152 | "radar detection: scan in process...\n"); | ||
3153 | return -EBUSY; | ||
3154 | } | ||
3155 | |||
3156 | if (priv->wdev.cac_started) | ||
3157 | return -EBUSY; | ||
3158 | |||
3159 | if (cfg80211_chandef_identical(¶ms->chandef, | ||
3160 | &priv->dfs_chandef)) | ||
3161 | return -EINVAL; | ||
3162 | |||
3163 | chsw_ie = (void *)cfg80211_find_ie(WLAN_EID_CHANNEL_SWITCH, | ||
3164 | params->beacon_csa.tail, | ||
3165 | params->beacon_csa.tail_len); | ||
3166 | if (!chsw_ie) { | ||
3167 | dev_err(priv->adapter->dev, | ||
3168 | "Could not parse channel switch announcement IE\n"); | ||
3169 | return -EINVAL; | ||
3170 | } | ||
3171 | |||
3172 | channel_sw = (void *)(chsw_ie + 1); | ||
3173 | if (channel_sw->mode) { | ||
3174 | if (netif_carrier_ok(priv->netdev)) | ||
3175 | netif_carrier_off(priv->netdev); | ||
3176 | mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter); | ||
3177 | } | ||
3178 | |||
3179 | if (mwifiex_del_mgmt_ies(priv)) | ||
3180 | wiphy_err(wiphy, "Failed to delete mgmt IEs!\n"); | ||
3181 | |||
3182 | if (mwifiex_set_mgmt_ies(priv, ¶ms->beacon_csa)) { | ||
3183 | wiphy_err(wiphy, "%s: setting mgmt ies failed\n", __func__); | ||
3184 | return -EFAULT; | ||
3185 | } | ||
3186 | |||
3187 | memcpy(&priv->dfs_chandef, ¶ms->chandef, sizeof(priv->dfs_chandef)); | ||
3188 | memcpy(&priv->beacon_after, ¶ms->beacon_after, | ||
3189 | sizeof(priv->beacon_after)); | ||
3190 | |||
3191 | chsw_msec = max(channel_sw->count * priv->bss_cfg.beacon_period, 100); | ||
3192 | queue_delayed_work(priv->dfs_chan_sw_workqueue, &priv->dfs_chan_sw_work, | ||
3193 | msecs_to_jiffies(chsw_msec)); | ||
3194 | return 0; | ||
3195 | } | ||
3196 | |||
3197 | static int | ||
3119 | mwifiex_cfg80211_start_radar_detection(struct wiphy *wiphy, | 3198 | mwifiex_cfg80211_start_radar_detection(struct wiphy *wiphy, |
3120 | struct net_device *dev, | 3199 | struct net_device *dev, |
3121 | struct cfg80211_chan_def *chandef, | 3200 | struct cfg80211_chan_def *chandef, |
@@ -3210,6 +3289,7 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = { | |||
3210 | .add_station = mwifiex_cfg80211_add_station, | 3289 | .add_station = mwifiex_cfg80211_add_station, |
3211 | .change_station = mwifiex_cfg80211_change_station, | 3290 | .change_station = mwifiex_cfg80211_change_station, |
3212 | .start_radar_detection = mwifiex_cfg80211_start_radar_detection, | 3291 | .start_radar_detection = mwifiex_cfg80211_start_radar_detection, |
3292 | .channel_switch = mwifiex_cfg80211_channel_switch, | ||
3213 | }; | 3293 | }; |
3214 | 3294 | ||
3215 | #ifdef CONFIG_PM | 3295 | #ifdef CONFIG_PM |
@@ -3313,7 +3393,8 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) | |||
3313 | wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | | 3393 | wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | |
3314 | WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | | 3394 | WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | |
3315 | WIPHY_FLAG_AP_UAPSD | | 3395 | WIPHY_FLAG_AP_UAPSD | |
3316 | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; | 3396 | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | |
3397 | WIPHY_FLAG_HAS_CHANNEL_SWITCH; | ||
3317 | 3398 | ||
3318 | if (ISSUPP_TDLS_ENABLED(adapter->fw_cap_info)) | 3399 | if (ISSUPP_TDLS_ENABLED(adapter->fw_cap_info)) |
3319 | wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | | 3400 | wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | |
diff --git a/drivers/net/wireless/mwifiex/ie.c b/drivers/net/wireless/mwifiex/ie.c index a6af7b88bf05..f3b6ed249403 100644 --- a/drivers/net/wireless/mwifiex/ie.c +++ b/drivers/net/wireless/mwifiex/ie.c | |||
@@ -325,6 +325,7 @@ static int mwifiex_uap_set_head_tail_ies(struct mwifiex_private *priv, | |||
325 | { | 325 | { |
326 | struct mwifiex_ie *gen_ie; | 326 | struct mwifiex_ie *gen_ie; |
327 | struct ieee_types_header *rsn_ie = NULL, *wpa_ie = NULL; | 327 | struct ieee_types_header *rsn_ie = NULL, *wpa_ie = NULL; |
328 | struct ieee_types_header *chsw_ie = NULL; | ||
328 | u16 gen_idx = MWIFIEX_AUTO_IDX_MASK, ie_len = 0; | 329 | u16 gen_idx = MWIFIEX_AUTO_IDX_MASK, ie_len = 0; |
329 | const u8 *vendor_ie; | 330 | const u8 *vendor_ie; |
330 | 331 | ||
@@ -356,9 +357,18 @@ static int mwifiex_uap_set_head_tail_ies(struct mwifiex_private *priv, | |||
356 | ie_len += wpa_ie->len + 2; | 357 | ie_len += wpa_ie->len + 2; |
357 | gen_ie->ie_length = cpu_to_le16(ie_len); | 358 | gen_ie->ie_length = cpu_to_le16(ie_len); |
358 | } | 359 | } |
360 | |||
361 | chsw_ie = (void *)cfg80211_find_ie(WLAN_EID_CHANNEL_SWITCH, | ||
362 | info->tail, info->tail_len); | ||
363 | if (chsw_ie) { | ||
364 | memcpy(gen_ie->ie_buffer + ie_len, | ||
365 | chsw_ie, chsw_ie->len + 2); | ||
366 | ie_len += chsw_ie->len + 2; | ||
367 | gen_ie->ie_length = cpu_to_le16(ie_len); | ||
368 | } | ||
359 | } | 369 | } |
360 | 370 | ||
361 | if (rsn_ie || wpa_ie) { | 371 | if (rsn_ie || wpa_ie || chsw_ie) { |
362 | if (mwifiex_update_uap_custom_ie(priv, gen_ie, &gen_idx, NULL, | 372 | if (mwifiex_update_uap_custom_ie(priv, gen_ie, &gen_idx, NULL, |
363 | NULL, NULL, NULL)) { | 373 | NULL, NULL, NULL)) { |
364 | kfree(gen_ie); | 374 | kfree(gen_ie); |
diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index ad9d679c3eed..599698c6b627 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h | |||
@@ -591,6 +591,10 @@ struct mwifiex_private { | |||
591 | struct cfg80211_chan_def dfs_chandef; | 591 | struct cfg80211_chan_def dfs_chandef; |
592 | struct workqueue_struct *dfs_cac_workqueue; | 592 | struct workqueue_struct *dfs_cac_workqueue; |
593 | struct delayed_work dfs_cac_work; | 593 | struct delayed_work dfs_cac_work; |
594 | struct timer_list dfs_chan_switch_timer; | ||
595 | struct workqueue_struct *dfs_chan_sw_workqueue; | ||
596 | struct delayed_work dfs_chan_sw_work; | ||
597 | struct cfg80211_beacon_data beacon_after; | ||
594 | }; | 598 | }; |
595 | 599 | ||
596 | enum mwifiex_ba_status { | 600 | enum mwifiex_ba_status { |
@@ -1394,6 +1398,7 @@ struct sk_buff * | |||
1394 | mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv, | 1398 | mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv, |
1395 | struct sk_buff *skb, u8 flag, u64 *cookie); | 1399 | struct sk_buff *skb, u8 flag, u64 *cookie); |
1396 | void mwifiex_dfs_cac_work_queue(struct work_struct *work); | 1400 | void mwifiex_dfs_cac_work_queue(struct work_struct *work); |
1401 | void mwifiex_dfs_chan_sw_work_queue(struct work_struct *work); | ||
1397 | void mwifiex_abort_cac(struct mwifiex_private *priv); | 1402 | void mwifiex_abort_cac(struct mwifiex_private *priv); |
1398 | int mwifiex_11h_handle_radar_detected(struct mwifiex_private *priv, | 1403 | int mwifiex_11h_handle_radar_detected(struct mwifiex_private *priv, |
1399 | struct sk_buff *skb); | 1404 | struct sk_buff *skb); |