380 lines · c
1// SPDX-License-Identifier: ISC2/*3 * Copyright (c) 2014-2017 Qualcomm Atheros, Inc.4 * Copyright (c) 2018, The Linux Foundation. All rights reserved.5 */6 7#include "wil6210.h"8#include "wmi.h"9 10#define P2P_WILDCARD_SSID "DIRECT-"11#define P2P_DMG_SOCIAL_CHANNEL 212#define P2P_SEARCH_DURATION_MS 50013#define P2P_DEFAULT_BI 10014 15static int wil_p2p_start_listen(struct wil6210_vif *vif)16{17 struct wil6210_priv *wil = vif_to_wil(vif);18 struct wil_p2p_info *p2p = &vif->p2p;19 u8 channel = p2p->listen_chan.hw_value;20 int rc;21 22 lockdep_assert_held(&wil->mutex);23 24 rc = wmi_p2p_cfg(vif, channel, P2P_DEFAULT_BI);25 if (rc) {26 wil_err(wil, "wmi_p2p_cfg failed\n");27 goto out;28 }29 30 rc = wmi_set_ssid(vif, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID);31 if (rc) {32 wil_err(wil, "wmi_set_ssid failed\n");33 goto out_stop;34 }35 36 rc = wmi_start_listen(vif);37 if (rc) {38 wil_err(wil, "wmi_start_listen failed\n");39 goto out_stop;40 }41 42 INIT_WORK(&p2p->discovery_expired_work, wil_p2p_listen_expired);43 mod_timer(&p2p->discovery_timer,44 jiffies + msecs_to_jiffies(p2p->listen_duration));45out_stop:46 if (rc)47 wmi_stop_discovery(vif);48 49out:50 return rc;51}52 53bool wil_p2p_is_social_scan(struct cfg80211_scan_request *request)54{55 return (request->n_channels == 1) &&56 (request->channels[0]->hw_value == P2P_DMG_SOCIAL_CHANNEL);57}58 59int wil_p2p_search(struct wil6210_vif *vif,60 struct cfg80211_scan_request *request)61{62 struct wil6210_priv *wil = vif_to_wil(vif);63 int rc;64 struct wil_p2p_info *p2p = &vif->p2p;65 66 wil_dbg_misc(wil, "p2p_search: channel %d\n", P2P_DMG_SOCIAL_CHANNEL);67 68 lockdep_assert_held(&wil->mutex);69 70 if (p2p->discovery_started) {71 wil_err(wil, "search failed. discovery already ongoing\n");72 rc = -EBUSY;73 goto out;74 }75 76 rc = wmi_p2p_cfg(vif, P2P_DMG_SOCIAL_CHANNEL, P2P_DEFAULT_BI);77 if (rc) {78 wil_err(wil, "wmi_p2p_cfg failed\n");79 goto out;80 }81 82 rc = wmi_set_ssid(vif, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID);83 if (rc) {84 wil_err(wil, "wmi_set_ssid failed\n");85 goto out_stop;86 }87 88 /* Set application IE to probe request and probe response */89 rc = wmi_set_ie(vif, WMI_FRAME_PROBE_REQ,90 request->ie_len, request->ie);91 if (rc) {92 wil_err(wil, "wmi_set_ie(WMI_FRAME_PROBE_REQ) failed\n");93 goto out_stop;94 }95 96 /* supplicant doesn't provide Probe Response IEs. As a workaround -97 * re-use Probe Request IEs98 */99 rc = wmi_set_ie(vif, WMI_FRAME_PROBE_RESP,100 request->ie_len, request->ie);101 if (rc) {102 wil_err(wil, "wmi_set_ie(WMI_FRAME_PROBE_RESP) failed\n");103 goto out_stop;104 }105 106 rc = wmi_start_search(vif);107 if (rc) {108 wil_err(wil, "wmi_start_search failed\n");109 goto out_stop;110 }111 112 p2p->discovery_started = 1;113 INIT_WORK(&p2p->discovery_expired_work, wil_p2p_search_expired);114 mod_timer(&p2p->discovery_timer,115 jiffies + msecs_to_jiffies(P2P_SEARCH_DURATION_MS));116 117out_stop:118 if (rc)119 wmi_stop_discovery(vif);120 121out:122 return rc;123}124 125int wil_p2p_listen(struct wil6210_priv *wil, struct wireless_dev *wdev,126 unsigned int duration, struct ieee80211_channel *chan,127 u64 *cookie)128{129 struct wil6210_vif *vif = wdev_to_vif(wil, wdev);130 struct wil_p2p_info *p2p = &vif->p2p;131 int rc;132 133 if (!chan)134 return -EINVAL;135 136 wil_dbg_misc(wil, "p2p_listen: duration %d\n", duration);137 138 mutex_lock(&wil->mutex);139 140 if (p2p->discovery_started) {141 wil_err(wil, "discovery already ongoing\n");142 rc = -EBUSY;143 goto out;144 }145 146 memcpy(&p2p->listen_chan, chan, sizeof(*chan));147 *cookie = ++p2p->cookie;148 p2p->listen_duration = duration;149 150 mutex_lock(&wil->vif_mutex);151 if (vif->scan_request) {152 wil_dbg_misc(wil, "Delaying p2p listen until scan done\n");153 p2p->pending_listen_wdev = wdev;154 p2p->discovery_started = 1;155 rc = 0;156 mutex_unlock(&wil->vif_mutex);157 goto out;158 }159 mutex_unlock(&wil->vif_mutex);160 161 rc = wil_p2p_start_listen(vif);162 if (rc)163 goto out;164 165 p2p->discovery_started = 1;166 if (vif->mid == 0)167 wil->radio_wdev = wdev;168 169 cfg80211_ready_on_channel(wdev, *cookie, chan, duration,170 GFP_KERNEL);171 172out:173 mutex_unlock(&wil->mutex);174 return rc;175}176 177u8 wil_p2p_stop_discovery(struct wil6210_vif *vif)178{179 struct wil_p2p_info *p2p = &vif->p2p;180 u8 started = p2p->discovery_started;181 182 if (p2p->discovery_started) {183 if (p2p->pending_listen_wdev) {184 /* discovery not really started, only pending */185 p2p->pending_listen_wdev = NULL;186 } else {187 del_timer_sync(&p2p->discovery_timer);188 wmi_stop_discovery(vif);189 }190 p2p->discovery_started = 0;191 }192 193 return started;194}195 196int wil_p2p_cancel_listen(struct wil6210_vif *vif, u64 cookie)197{198 struct wil6210_priv *wil = vif_to_wil(vif);199 struct wil_p2p_info *p2p = &vif->p2p;200 u8 started;201 202 mutex_lock(&wil->mutex);203 204 if (cookie != p2p->cookie) {205 wil_info(wil, "Cookie mismatch: 0x%016llx vs. 0x%016llx\n",206 p2p->cookie, cookie);207 mutex_unlock(&wil->mutex);208 return -ENOENT;209 }210 211 started = wil_p2p_stop_discovery(vif);212 213 mutex_unlock(&wil->mutex);214 215 if (!started) {216 wil_err(wil, "listen not started\n");217 return -ENOENT;218 }219 220 mutex_lock(&wil->vif_mutex);221 cfg80211_remain_on_channel_expired(vif_to_radio_wdev(wil, vif),222 p2p->cookie,223 &p2p->listen_chan,224 GFP_KERNEL);225 if (vif->mid == 0)226 wil->radio_wdev = wil->main_ndev->ieee80211_ptr;227 mutex_unlock(&wil->vif_mutex);228 return 0;229}230 231void wil_p2p_listen_expired(struct work_struct *work)232{233 struct wil_p2p_info *p2p = container_of(work,234 struct wil_p2p_info, discovery_expired_work);235 struct wil6210_vif *vif = container_of(p2p,236 struct wil6210_vif, p2p);237 struct wil6210_priv *wil = vif_to_wil(vif);238 u8 started;239 240 wil_dbg_misc(wil, "p2p_listen_expired\n");241 242 mutex_lock(&wil->mutex);243 started = wil_p2p_stop_discovery(vif);244 mutex_unlock(&wil->mutex);245 246 if (!started)247 return;248 249 mutex_lock(&wil->vif_mutex);250 cfg80211_remain_on_channel_expired(vif_to_radio_wdev(wil, vif),251 p2p->cookie,252 &p2p->listen_chan,253 GFP_KERNEL);254 if (vif->mid == 0)255 wil->radio_wdev = wil->main_ndev->ieee80211_ptr;256 mutex_unlock(&wil->vif_mutex);257}258 259void wil_p2p_search_expired(struct work_struct *work)260{261 struct wil_p2p_info *p2p = container_of(work,262 struct wil_p2p_info, discovery_expired_work);263 struct wil6210_vif *vif = container_of(p2p,264 struct wil6210_vif, p2p);265 struct wil6210_priv *wil = vif_to_wil(vif);266 u8 started;267 268 wil_dbg_misc(wil, "p2p_search_expired\n");269 270 mutex_lock(&wil->mutex);271 started = wil_p2p_stop_discovery(vif);272 mutex_unlock(&wil->mutex);273 274 if (started) {275 struct cfg80211_scan_info info = {276 .aborted = false,277 };278 279 mutex_lock(&wil->vif_mutex);280 if (vif->scan_request) {281 cfg80211_scan_done(vif->scan_request, &info);282 vif->scan_request = NULL;283 if (vif->mid == 0)284 wil->radio_wdev =285 wil->main_ndev->ieee80211_ptr;286 }287 mutex_unlock(&wil->vif_mutex);288 }289}290 291void wil_p2p_delayed_listen_work(struct work_struct *work)292{293 struct wil_p2p_info *p2p = container_of(work,294 struct wil_p2p_info, delayed_listen_work);295 struct wil6210_vif *vif = container_of(p2p,296 struct wil6210_vif, p2p);297 struct wil6210_priv *wil = vif_to_wil(vif);298 int rc;299 300 mutex_lock(&wil->mutex);301 302 wil_dbg_misc(wil, "Checking delayed p2p listen\n");303 if (!p2p->discovery_started || !p2p->pending_listen_wdev)304 goto out;305 306 mutex_lock(&wil->vif_mutex);307 if (vif->scan_request) {308 /* another scan started, wait again... */309 mutex_unlock(&wil->vif_mutex);310 goto out;311 }312 mutex_unlock(&wil->vif_mutex);313 314 rc = wil_p2p_start_listen(vif);315 316 mutex_lock(&wil->vif_mutex);317 if (rc) {318 cfg80211_remain_on_channel_expired(p2p->pending_listen_wdev,319 p2p->cookie,320 &p2p->listen_chan,321 GFP_KERNEL);322 if (vif->mid == 0)323 wil->radio_wdev = wil->main_ndev->ieee80211_ptr;324 } else {325 cfg80211_ready_on_channel(p2p->pending_listen_wdev, p2p->cookie,326 &p2p->listen_chan,327 p2p->listen_duration, GFP_KERNEL);328 if (vif->mid == 0)329 wil->radio_wdev = p2p->pending_listen_wdev;330 }331 p2p->pending_listen_wdev = NULL;332 mutex_unlock(&wil->vif_mutex);333 334out:335 mutex_unlock(&wil->mutex);336}337 338void wil_p2p_stop_radio_operations(struct wil6210_priv *wil)339{340 struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);341 struct wil_p2p_info *p2p = &vif->p2p;342 struct cfg80211_scan_info info = {343 .aborted = true,344 };345 346 lockdep_assert_held(&wil->mutex);347 lockdep_assert_held(&wil->vif_mutex);348 349 if (wil->radio_wdev != wil->p2p_wdev)350 goto out;351 352 if (!p2p->discovery_started) {353 /* Regular scan on the p2p device */354 if (vif->scan_request &&355 vif->scan_request->wdev == wil->p2p_wdev)356 wil_abort_scan(vif, true);357 goto out;358 }359 360 /* Search or listen on p2p device */361 mutex_unlock(&wil->vif_mutex);362 wil_p2p_stop_discovery(vif);363 mutex_lock(&wil->vif_mutex);364 365 if (vif->scan_request) {366 /* search */367 cfg80211_scan_done(vif->scan_request, &info);368 vif->scan_request = NULL;369 } else {370 /* listen */371 cfg80211_remain_on_channel_expired(wil->radio_wdev,372 p2p->cookie,373 &p2p->listen_chan,374 GFP_KERNEL);375 }376 377out:378 wil->radio_wdev = wil->main_ndev->ieee80211_ptr;379}380