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