linux/drivers/net/wireless/ath/wil6210/p2p.c
<<
>>
Prefs
   1// SPDX-License-Identifier: ISC
   2/*
   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 2
  12#define P2P_SEARCH_DURATION_MS 500
  13#define P2P_DEFAULT_BI 100
  14
  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 IEs
  98         */
  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