diff options
Diffstat (limited to 'drivers/net/wireless/ath/wil6210/p2p.c')
-rw-r--r-- | drivers/net/wireless/ath/wil6210/p2p.c | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/wil6210/p2p.c b/drivers/net/wireless/ath/wil6210/p2p.c new file mode 100644 index 000000000..f26bf046d --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/p2p.c @@ -0,0 +1,379 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (c) 2014-2017 Qualcomm Atheros, Inc. + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + */ + +#include "wil6210.h" +#include "wmi.h" + +#define P2P_WILDCARD_SSID "DIRECT-" +#define P2P_DMG_SOCIAL_CHANNEL 2 +#define P2P_SEARCH_DURATION_MS 500 +#define P2P_DEFAULT_BI 100 + +static int wil_p2p_start_listen(struct wil6210_vif *vif) +{ + struct wil6210_priv *wil = vif_to_wil(vif); + struct wil_p2p_info *p2p = &vif->p2p; + u8 channel = p2p->listen_chan.hw_value; + int rc; + + lockdep_assert_held(&wil->mutex); + + rc = wmi_p2p_cfg(vif, channel, P2P_DEFAULT_BI); + if (rc) { + wil_err(wil, "wmi_p2p_cfg failed\n"); + goto out; + } + + rc = wmi_set_ssid(vif, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID); + if (rc) { + wil_err(wil, "wmi_set_ssid failed\n"); + goto out_stop; + } + + rc = wmi_start_listen(vif); + if (rc) { + wil_err(wil, "wmi_start_listen failed\n"); + goto out_stop; + } + + INIT_WORK(&p2p->discovery_expired_work, wil_p2p_listen_expired); + mod_timer(&p2p->discovery_timer, + jiffies + msecs_to_jiffies(p2p->listen_duration)); +out_stop: + if (rc) + wmi_stop_discovery(vif); + +out: + return rc; +} + +bool wil_p2p_is_social_scan(struct cfg80211_scan_request *request) +{ + return (request->n_channels == 1) && + (request->channels[0]->hw_value == P2P_DMG_SOCIAL_CHANNEL); +} + +int wil_p2p_search(struct wil6210_vif *vif, + struct cfg80211_scan_request *request) +{ + struct wil6210_priv *wil = vif_to_wil(vif); + int rc; + struct wil_p2p_info *p2p = &vif->p2p; + + wil_dbg_misc(wil, "p2p_search: channel %d\n", P2P_DMG_SOCIAL_CHANNEL); + + lockdep_assert_held(&wil->mutex); + + if (p2p->discovery_started) { + wil_err(wil, "search failed. discovery already ongoing\n"); + rc = -EBUSY; + goto out; + } + + rc = wmi_p2p_cfg(vif, P2P_DMG_SOCIAL_CHANNEL, P2P_DEFAULT_BI); + if (rc) { + wil_err(wil, "wmi_p2p_cfg failed\n"); + goto out; + } + + rc = wmi_set_ssid(vif, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID); + if (rc) { + wil_err(wil, "wmi_set_ssid failed\n"); + goto out_stop; + } + + /* Set application IE to probe request and probe response */ + rc = wmi_set_ie(vif, WMI_FRAME_PROBE_REQ, + request->ie_len, request->ie); + if (rc) { + wil_err(wil, "wmi_set_ie(WMI_FRAME_PROBE_REQ) failed\n"); + goto out_stop; + } + + /* supplicant doesn't provide Probe Response IEs. As a workaround - + * re-use Probe Request IEs + */ + rc = wmi_set_ie(vif, WMI_FRAME_PROBE_RESP, + request->ie_len, request->ie); + if (rc) { + wil_err(wil, "wmi_set_ie(WMI_FRAME_PROBE_RESP) failed\n"); + goto out_stop; + } + + rc = wmi_start_search(vif); + if (rc) { + wil_err(wil, "wmi_start_search failed\n"); + goto out_stop; + } + + p2p->discovery_started = 1; + INIT_WORK(&p2p->discovery_expired_work, wil_p2p_search_expired); + mod_timer(&p2p->discovery_timer, + jiffies + msecs_to_jiffies(P2P_SEARCH_DURATION_MS)); + +out_stop: + if (rc) + wmi_stop_discovery(vif); + +out: + return rc; +} + +int wil_p2p_listen(struct wil6210_priv *wil, struct wireless_dev *wdev, + unsigned int duration, struct ieee80211_channel *chan, + u64 *cookie) +{ + struct wil6210_vif *vif = wdev_to_vif(wil, wdev); + struct wil_p2p_info *p2p = &vif->p2p; + int rc; + + if (!chan) + return -EINVAL; + + wil_dbg_misc(wil, "p2p_listen: duration %d\n", duration); + + mutex_lock(&wil->mutex); + + if (p2p->discovery_started) { + wil_err(wil, "discovery already ongoing\n"); + rc = -EBUSY; + goto out; + } + + memcpy(&p2p->listen_chan, chan, sizeof(*chan)); + *cookie = ++p2p->cookie; + p2p->listen_duration = duration; + + mutex_lock(&wil->vif_mutex); + if (vif->scan_request) { + wil_dbg_misc(wil, "Delaying p2p listen until scan done\n"); + p2p->pending_listen_wdev = wdev; + p2p->discovery_started = 1; + rc = 0; + mutex_unlock(&wil->vif_mutex); + goto out; + } + mutex_unlock(&wil->vif_mutex); + + rc = wil_p2p_start_listen(vif); + if (rc) + goto out; + + p2p->discovery_started = 1; + if (vif->mid == 0) + wil->radio_wdev = wdev; + + cfg80211_ready_on_channel(wdev, *cookie, chan, duration, + GFP_KERNEL); + +out: + mutex_unlock(&wil->mutex); + return rc; +} + +u8 wil_p2p_stop_discovery(struct wil6210_vif *vif) +{ + struct wil_p2p_info *p2p = &vif->p2p; + u8 started = p2p->discovery_started; + + if (p2p->discovery_started) { + if (p2p->pending_listen_wdev) { + /* discovery not really started, only pending */ + p2p->pending_listen_wdev = NULL; + } else { + del_timer_sync(&p2p->discovery_timer); + wmi_stop_discovery(vif); + } + p2p->discovery_started = 0; + } + + return started; +} + +int wil_p2p_cancel_listen(struct wil6210_vif *vif, u64 cookie) +{ + struct wil6210_priv *wil = vif_to_wil(vif); + struct wil_p2p_info *p2p = &vif->p2p; + u8 started; + + mutex_lock(&wil->mutex); + + if (cookie != p2p->cookie) { + wil_info(wil, "Cookie mismatch: 0x%016llx vs. 0x%016llx\n", + p2p->cookie, cookie); + mutex_unlock(&wil->mutex); + return -ENOENT; + } + + started = wil_p2p_stop_discovery(vif); + + mutex_unlock(&wil->mutex); + + if (!started) { + wil_err(wil, "listen not started\n"); + return -ENOENT; + } + + mutex_lock(&wil->vif_mutex); + cfg80211_remain_on_channel_expired(vif_to_radio_wdev(wil, vif), + p2p->cookie, + &p2p->listen_chan, + GFP_KERNEL); + if (vif->mid == 0) + wil->radio_wdev = wil->main_ndev->ieee80211_ptr; + mutex_unlock(&wil->vif_mutex); + return 0; +} + +void wil_p2p_listen_expired(struct work_struct *work) +{ + struct wil_p2p_info *p2p = container_of(work, + struct wil_p2p_info, discovery_expired_work); + struct wil6210_vif *vif = container_of(p2p, + struct wil6210_vif, p2p); + struct wil6210_priv *wil = vif_to_wil(vif); + u8 started; + + wil_dbg_misc(wil, "p2p_listen_expired\n"); + + mutex_lock(&wil->mutex); + started = wil_p2p_stop_discovery(vif); + mutex_unlock(&wil->mutex); + + if (!started) + return; + + mutex_lock(&wil->vif_mutex); + cfg80211_remain_on_channel_expired(vif_to_radio_wdev(wil, vif), + p2p->cookie, + &p2p->listen_chan, + GFP_KERNEL); + if (vif->mid == 0) + wil->radio_wdev = wil->main_ndev->ieee80211_ptr; + mutex_unlock(&wil->vif_mutex); +} + +void wil_p2p_search_expired(struct work_struct *work) +{ + struct wil_p2p_info *p2p = container_of(work, + struct wil_p2p_info, discovery_expired_work); + struct wil6210_vif *vif = container_of(p2p, + struct wil6210_vif, p2p); + struct wil6210_priv *wil = vif_to_wil(vif); + u8 started; + + wil_dbg_misc(wil, "p2p_search_expired\n"); + + mutex_lock(&wil->mutex); + started = wil_p2p_stop_discovery(vif); + mutex_unlock(&wil->mutex); + + if (started) { + struct cfg80211_scan_info info = { + .aborted = false, + }; + + mutex_lock(&wil->vif_mutex); + if (vif->scan_request) { + cfg80211_scan_done(vif->scan_request, &info); + vif->scan_request = NULL; + if (vif->mid == 0) + wil->radio_wdev = + wil->main_ndev->ieee80211_ptr; + } + mutex_unlock(&wil->vif_mutex); + } +} + +void wil_p2p_delayed_listen_work(struct work_struct *work) +{ + struct wil_p2p_info *p2p = container_of(work, + struct wil_p2p_info, delayed_listen_work); + struct wil6210_vif *vif = container_of(p2p, + struct wil6210_vif, p2p); + struct wil6210_priv *wil = vif_to_wil(vif); + int rc; + + mutex_lock(&wil->mutex); + + wil_dbg_misc(wil, "Checking delayed p2p listen\n"); + if (!p2p->discovery_started || !p2p->pending_listen_wdev) + goto out; + + mutex_lock(&wil->vif_mutex); + if (vif->scan_request) { + /* another scan started, wait again... */ + mutex_unlock(&wil->vif_mutex); + goto out; + } + mutex_unlock(&wil->vif_mutex); + + rc = wil_p2p_start_listen(vif); + + mutex_lock(&wil->vif_mutex); + if (rc) { + cfg80211_remain_on_channel_expired(p2p->pending_listen_wdev, + p2p->cookie, + &p2p->listen_chan, + GFP_KERNEL); + if (vif->mid == 0) + wil->radio_wdev = wil->main_ndev->ieee80211_ptr; + } else { + cfg80211_ready_on_channel(p2p->pending_listen_wdev, p2p->cookie, + &p2p->listen_chan, + p2p->listen_duration, GFP_KERNEL); + if (vif->mid == 0) + wil->radio_wdev = p2p->pending_listen_wdev; + } + p2p->pending_listen_wdev = NULL; + mutex_unlock(&wil->vif_mutex); + +out: + mutex_unlock(&wil->mutex); +} + +void wil_p2p_stop_radio_operations(struct wil6210_priv *wil) +{ + struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); + struct wil_p2p_info *p2p = &vif->p2p; + struct cfg80211_scan_info info = { + .aborted = true, + }; + + lockdep_assert_held(&wil->mutex); + lockdep_assert_held(&wil->vif_mutex); + + if (wil->radio_wdev != wil->p2p_wdev) + goto out; + + if (!p2p->discovery_started) { + /* Regular scan on the p2p device */ + if (vif->scan_request && + vif->scan_request->wdev == wil->p2p_wdev) + wil_abort_scan(vif, true); + goto out; + } + + /* Search or listen on p2p device */ + mutex_unlock(&wil->vif_mutex); + wil_p2p_stop_discovery(vif); + mutex_lock(&wil->vif_mutex); + + if (vif->scan_request) { + /* search */ + cfg80211_scan_done(vif->scan_request, &info); + vif->scan_request = NULL; + } else { + /* listen */ + cfg80211_remain_on_channel_expired(wil->radio_wdev, + p2p->cookie, + &p2p->listen_chan, + GFP_KERNEL); + } + +out: + wil->radio_wdev = wil->main_ndev->ieee80211_ptr; +} |