@@ -354,14 +354,6 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
wil_dbg_misc(wil, "%s(), wdev=0x%p iftype=%d\n",
__func__, wdev, wdev->iftype);
- mutex_lock(&wil->p2p_wdev_mutex);
- if (wil->scan_request) {
- wil_err(wil, "Already scanning\n");
- mutex_unlock(&wil->p2p_wdev_mutex);
- return -EAGAIN;
- }
- mutex_unlock(&wil->p2p_wdev_mutex);
-
/* check we are client side */
switch (wdev->iftype) {
case NL80211_IFTYPE_STATION:
@@ -378,12 +370,24 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
return -EBUSY;
}
+ mutex_lock(&wil->mutex);
+
+ mutex_lock(&wil->p2p_wdev_mutex);
+ if (wil->scan_request || wil->p2p.discovery_started) {
+ wil_err(wil, "Already scanning\n");
+ mutex_unlock(&wil->p2p_wdev_mutex);
+ rc = -EAGAIN;
+ goto out;
+ }
+ mutex_unlock(&wil->p2p_wdev_mutex);
+
/* social scan on P2P_DEVICE is handled as p2p search */
if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE &&
wil_p2p_is_social_scan(request)) {
if (!wil->p2p.p2p_dev_started) {
wil_err(wil, "P2P search requested on stopped P2P device\n");
- return -EIO;
+ rc = -EIO;
+ goto out;
}
wil->scan_request = request;
wil->radio_wdev = wdev;
@@ -392,7 +396,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
wil->radio_wdev = wil_to_wdev(wil);
wil->scan_request = NULL;
}
- return rc;
+ goto out;
}
(void)wil_p2p_stop_discovery(wil);
@@ -415,7 +419,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
if (rc) {
wil_err(wil, "set SSID for scan request failed: %d\n", rc);
- return rc;
+ goto out;
}
wil->scan_request = request;
@@ -448,7 +452,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
rc = wmi_set_ie(wil, WMI_FRAME_PROBE_REQ, request->ie_len, request->ie);
if (rc)
- goto out;
+ goto out_restore;
if (wil->discovery_mode && cmd.cmd.scan_type == WMI_ACTIVE_SCAN) {
cmd.cmd.discovery_mode = 1;
@@ -459,13 +463,14 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
rc = wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) +
cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0]));
-out:
+out_restore:
if (rc) {
del_timer_sync(&wil->scan_timer);
wil->radio_wdev = wil_to_wdev(wil);
wil->scan_request = NULL;
}
-
+out:
+ mutex_unlock(&wil->mutex);
return rc;
}
@@ -988,16 +993,8 @@ static int wil_remain_on_channel(struct wiphy *wiphy,
wil_dbg_misc(wil, "%s() center_freq=%d, duration=%d iftype=%d\n",
__func__, chan->center_freq, duration, wdev->iftype);
- rc = wil_p2p_listen(wil, duration, chan, cookie);
- if (rc)
- return rc;
-
- wil->radio_wdev = wdev;
-
- cfg80211_ready_on_channel(wdev, *cookie, chan, duration,
- GFP_KERNEL);
-
- return 0;
+ rc = wil_p2p_listen(wil, wdev, duration, chan, cookie);
+ return rc;
}
static int wil_cancel_remain_on_channel(struct wiphy *wiphy,
@@ -518,6 +518,7 @@ int wil_priv_init(struct wil6210_priv *wil)
INIT_WORK(&wil->wmi_event_worker, wmi_event_worker);
INIT_WORK(&wil->fw_error_worker, wil_fw_error_worker);
INIT_WORK(&wil->probe_client_worker, wil_probe_client_worker);
+ INIT_WORK(&wil->p2p.delayed_listen_work, wil_p2p_delayed_listen_work);
INIT_LIST_HEAD(&wil->pending_wmi_ev);
INIT_LIST_HEAD(&wil->probe_client_pending);
@@ -579,6 +580,7 @@ void wil_priv_deinit(struct wil6210_priv *wil)
cancel_work_sync(&wil->disconnect_worker);
cancel_work_sync(&wil->fw_error_worker);
cancel_work_sync(&wil->p2p.discovery_expired_work);
+ cancel_work_sync(&wil->p2p.delayed_listen_work);
mutex_lock(&wil->mutex);
wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
mutex_unlock(&wil->mutex);
@@ -22,6 +22,43 @@
#define P2P_SEARCH_DURATION_MS 500
#define P2P_DEFAULT_BI 100
+static int wil_p2p_start_listen(struct wil6210_priv *wil)
+{
+ struct wil_p2p_info *p2p = &wil->p2p;
+ u8 channel = p2p->listen_chan.hw_value;
+ int rc;
+
+ lockdep_assert_held(&wil->mutex);
+
+ rc = wmi_p2p_cfg(wil, channel, P2P_DEFAULT_BI);
+ if (rc) {
+ wil_err(wil, "wmi_p2p_cfg failed\n");
+ goto out;
+ }
+
+ rc = wmi_set_ssid(wil, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID);
+ if (rc) {
+ wil_err(wil, "wmi_set_ssid failed\n");
+ goto out_stop;
+ }
+
+ rc = wmi_start_listen(wil);
+ 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(wil);
+
+out:
+ return rc;
+}
+
bool wil_p2p_is_social_scan(struct cfg80211_scan_request *request)
{
return (request->n_channels == 1) &&
@@ -46,7 +83,7 @@ int wil_p2p_search(struct wil6210_priv *wil,
wil_dbg_misc(wil, "%s: channel %d\n",
__func__, P2P_DMG_SOCIAL_CHANNEL);
- mutex_lock(&wil->mutex);
+ lockdep_assert_held(&wil->mutex);
if (p2p->discovery_started) {
wil_err(wil, "%s: search failed. discovery already ongoing\n",
@@ -103,22 +140,19 @@ int wil_p2p_search(struct wil6210_priv *wil,
wmi_stop_discovery(wil);
out:
- mutex_unlock(&wil->mutex);
return rc;
}
-int wil_p2p_listen(struct wil6210_priv *wil, unsigned int duration,
- struct ieee80211_channel *chan, u64 *cookie)
+int wil_p2p_listen(struct wil6210_priv *wil, struct wireless_dev *wdev,
+ unsigned int duration, struct ieee80211_channel *chan,
+ u64 *cookie)
{
struct wil_p2p_info *p2p = &wil->p2p;
- u8 channel = P2P_DMG_SOCIAL_CHANNEL;
int rc;
if (!chan)
return -EINVAL;
- channel = chan->hw_value;
-
wil_dbg_misc(wil, "%s: duration %d\n", __func__, duration);
mutex_lock(&wil->mutex);
@@ -129,35 +163,30 @@ int wil_p2p_listen(struct wil6210_priv *wil, unsigned int duration,
goto out;
}
- rc = wmi_p2p_cfg(wil, channel, P2P_DEFAULT_BI);
- if (rc) {
- wil_err(wil, "%s: wmi_p2p_cfg failed\n", __func__);
- goto out;
- }
-
- rc = wmi_set_ssid(wil, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID);
- if (rc) {
- wil_err(wil, "%s: wmi_set_ssid failed\n", __func__);
- goto out_stop;
- }
+ memcpy(&p2p->listen_chan, chan, sizeof(*chan));
+ *cookie = ++p2p->cookie;
+ p2p->listen_duration = duration;
- rc = wmi_start_listen(wil);
- if (rc) {
- wil_err(wil, "%s: wmi_start_listen failed\n", __func__);
- goto out_stop;
+ mutex_lock(&wil->p2p_wdev_mutex);
+ if (wil->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->p2p_wdev_mutex);
+ goto out;
}
+ mutex_unlock(&wil->p2p_wdev_mutex);
- memcpy(&p2p->listen_chan, chan, sizeof(*chan));
- *cookie = ++p2p->cookie;
+ rc = wil_p2p_start_listen(wil);
+ if (rc)
+ goto out;
p2p->discovery_started = 1;
- INIT_WORK(&p2p->discovery_expired_work, wil_p2p_listen_expired);
- mod_timer(&p2p->discovery_timer,
- jiffies + msecs_to_jiffies(duration));
+ wil->radio_wdev = wdev;
-out_stop:
- if (rc)
- wmi_stop_discovery(wil);
+ cfg80211_ready_on_channel(wdev, *cookie, chan, duration,
+ GFP_KERNEL);
out:
mutex_unlock(&wil->mutex);
@@ -170,9 +199,14 @@ u8 wil_p2p_stop_discovery(struct wil6210_priv *wil)
u8 started = p2p->discovery_started;
if (p2p->discovery_started) {
- del_timer_sync(&p2p->discovery_timer);
+ 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(wil);
+ }
p2p->discovery_started = 0;
- wmi_stop_discovery(wil);
}
return started;
@@ -257,11 +291,57 @@ void wil_p2p_search_expired(struct work_struct *work)
};
mutex_lock(&wil->p2p_wdev_mutex);
- cfg80211_scan_done(wil->scan_request, &info);
- wil->scan_request = NULL;
- wil->radio_wdev = wil->wdev;
+ if (wil->scan_request) {
+ cfg80211_scan_done(wil->scan_request, &info);
+ wil->scan_request = NULL;
+ wil->radio_wdev = wil->wdev;
+ }
+ mutex_unlock(&wil->p2p_wdev_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_priv *wil = container_of(p2p,
+ struct wil6210_priv, p2p);
+ 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->p2p_wdev_mutex);
+ if (wil->scan_request) {
+ /* another scan started, wait again... */
mutex_unlock(&wil->p2p_wdev_mutex);
+ goto out;
}
+ mutex_unlock(&wil->p2p_wdev_mutex);
+
+ rc = wil_p2p_start_listen(wil);
+
+ mutex_lock(&wil->p2p_wdev_mutex);
+ if (rc) {
+ cfg80211_remain_on_channel_expired(p2p->pending_listen_wdev,
+ p2p->cookie,
+ &p2p->listen_chan,
+ GFP_KERNEL);
+ wil->radio_wdev = wil->wdev;
+ } else {
+ cfg80211_ready_on_channel(p2p->pending_listen_wdev, p2p->cookie,
+ &p2p->listen_chan,
+ p2p->listen_duration, GFP_KERNEL);
+ wil->radio_wdev = p2p->pending_listen_wdev;
+ }
+ p2p->pending_listen_wdev = NULL;
+ mutex_unlock(&wil->p2p_wdev_mutex);
+
+out:
+ mutex_unlock(&wil->mutex);
}
void wil_p2p_stop_radio_operations(struct wil6210_priv *wil)
@@ -461,8 +461,11 @@ struct wil_p2p_info {
u8 discovery_started;
u8 p2p_dev_started;
u64 cookie;
+ struct wireless_dev *pending_listen_wdev;
+ unsigned int listen_duration;
struct timer_list discovery_timer; /* listen/search duration */
struct work_struct discovery_expired_work; /* listen/search expire */
+ struct work_struct delayed_listen_work; /* listen after scan done */
};
enum wil_sta_status {
@@ -843,13 +846,15 @@ int wil_addba_rx_request(struct wil6210_priv *wil, u8 cidxtid,
void wil_p2p_discovery_timer_fn(ulong x);
int wil_p2p_search(struct wil6210_priv *wil,
struct cfg80211_scan_request *request);
-int wil_p2p_listen(struct wil6210_priv *wil, unsigned int duration,
- struct ieee80211_channel *chan, u64 *cookie);
+int wil_p2p_listen(struct wil6210_priv *wil, struct wireless_dev *wdev,
+ unsigned int duration, struct ieee80211_channel *chan,
+ u64 *cookie);
u8 wil_p2p_stop_discovery(struct wil6210_priv *wil);
int wil_p2p_cancel_listen(struct wil6210_priv *wil, u64 cookie);
void wil_p2p_listen_expired(struct work_struct *work);
void wil_p2p_search_expired(struct work_struct *work);
void wil_p2p_stop_radio_operations(struct wil6210_priv *wil);
+void wil_p2p_delayed_listen_work(struct work_struct *work);
/* WMI for P2P */
int wmi_p2p_cfg(struct wil6210_priv *wil, int channel, int bi);
@@ -441,6 +441,10 @@ static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id,
wil->radio_wdev = wil->wdev;
wil->scan_request = NULL;
wake_up_interruptible(&wil->wq);
+ if (wil->p2p.pending_listen_wdev) {
+ wil_dbg_misc(wil, "Scheduling delayed listen\n");
+ schedule_work(&wil->p2p.delayed_listen_work);
+ }
} else {
wil_err(wil, "SCAN_COMPLETE while not scanning\n");
}