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