1 /* 2 * Copyright (c) 2014-2016 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 25 bool wil_p2p_is_social_scan(struct cfg80211_scan_request *request) 26 { 27 return (request->n_channels == 1) && 28 (request->channels[0]->hw_value == P2P_DMG_SOCIAL_CHANNEL); 29 } 30 31 void wil_p2p_discovery_timer_fn(ulong x) 32 { 33 struct wil6210_priv *wil = (void *)x; 34 35 wil_dbg_misc(wil, "%s\n", __func__); 36 37 schedule_work(&wil->p2p.discovery_expired_work); 38 } 39 40 int wil_p2p_search(struct wil6210_priv *wil, 41 struct cfg80211_scan_request *request) 42 { 43 int rc; 44 struct wil_p2p_info *p2p = &wil->p2p; 45 46 wil_dbg_misc(wil, "%s: channel %d\n", 47 __func__, P2P_DMG_SOCIAL_CHANNEL); 48 49 mutex_lock(&wil->mutex); 50 51 if (p2p->discovery_started) { 52 wil_err(wil, "%s: search failed. discovery already ongoing\n", 53 __func__); 54 rc = -EBUSY; 55 goto out; 56 } 57 58 rc = wmi_p2p_cfg(wil, P2P_DMG_SOCIAL_CHANNEL, P2P_DEFAULT_BI); 59 if (rc) { 60 wil_err(wil, "%s: wmi_p2p_cfg failed\n", __func__); 61 goto out; 62 } 63 64 rc = wmi_set_ssid(wil, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID); 65 if (rc) { 66 wil_err(wil, "%s: wmi_set_ssid failed\n", __func__); 67 goto out_stop; 68 } 69 70 /* Set application IE to probe request and probe response */ 71 rc = wmi_set_ie(wil, WMI_FRAME_PROBE_REQ, 72 request->ie_len, request->ie); 73 if (rc) { 74 wil_err(wil, "%s: wmi_set_ie(WMI_FRAME_PROBE_REQ) failed\n", 75 __func__); 76 goto out_stop; 77 } 78 79 /* supplicant doesn't provide Probe Response IEs. As a workaround - 80 * re-use Probe Request IEs 81 */ 82 rc = wmi_set_ie(wil, WMI_FRAME_PROBE_RESP, 83 request->ie_len, request->ie); 84 if (rc) { 85 wil_err(wil, "%s: wmi_set_ie(WMI_FRAME_PROBE_RESP) failed\n", 86 __func__); 87 goto out_stop; 88 } 89 90 rc = wmi_start_search(wil); 91 if (rc) { 92 wil_err(wil, "%s: wmi_start_search failed\n", __func__); 93 goto out_stop; 94 } 95 96 p2p->discovery_started = 1; 97 INIT_WORK(&p2p->discovery_expired_work, wil_p2p_search_expired); 98 mod_timer(&p2p->discovery_timer, 99 jiffies + msecs_to_jiffies(P2P_SEARCH_DURATION_MS)); 100 101 out_stop: 102 if (rc) 103 wmi_stop_discovery(wil); 104 105 out: 106 mutex_unlock(&wil->mutex); 107 return rc; 108 } 109 110 int wil_p2p_listen(struct wil6210_priv *wil, unsigned int duration, 111 struct ieee80211_channel *chan, u64 *cookie) 112 { 113 struct wil_p2p_info *p2p = &wil->p2p; 114 u8 channel = P2P_DMG_SOCIAL_CHANNEL; 115 int rc; 116 117 if (chan) 118 channel = chan->hw_value; 119 120 wil_dbg_misc(wil, "%s: duration %d\n", __func__, duration); 121 122 mutex_lock(&wil->mutex); 123 124 if (p2p->discovery_started) { 125 wil_err(wil, "%s: discovery already ongoing\n", __func__); 126 rc = -EBUSY; 127 goto out; 128 } 129 130 rc = wmi_p2p_cfg(wil, channel, P2P_DEFAULT_BI); 131 if (rc) { 132 wil_err(wil, "%s: wmi_p2p_cfg failed\n", __func__); 133 goto out; 134 } 135 136 rc = wmi_set_ssid(wil, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID); 137 if (rc) { 138 wil_err(wil, "%s: wmi_set_ssid failed\n", __func__); 139 goto out_stop; 140 } 141 142 rc = wmi_start_listen(wil); 143 if (rc) { 144 wil_err(wil, "%s: wmi_start_listen failed\n", __func__); 145 goto out_stop; 146 } 147 148 memcpy(&p2p->listen_chan, chan, sizeof(*chan)); 149 *cookie = ++p2p->cookie; 150 151 p2p->discovery_started = 1; 152 INIT_WORK(&p2p->discovery_expired_work, wil_p2p_listen_expired); 153 mod_timer(&p2p->discovery_timer, 154 jiffies + msecs_to_jiffies(duration)); 155 156 out_stop: 157 if (rc) 158 wmi_stop_discovery(wil); 159 160 out: 161 mutex_unlock(&wil->mutex); 162 return rc; 163 } 164 165 u8 wil_p2p_stop_discovery(struct wil6210_priv *wil) 166 { 167 struct wil_p2p_info *p2p = &wil->p2p; 168 u8 started = p2p->discovery_started; 169 170 if (p2p->discovery_started) { 171 del_timer_sync(&p2p->discovery_timer); 172 p2p->discovery_started = 0; 173 wmi_stop_discovery(wil); 174 } 175 176 return started; 177 } 178 179 int wil_p2p_cancel_listen(struct wil6210_priv *wil, u64 cookie) 180 { 181 struct wil_p2p_info *p2p = &wil->p2p; 182 u8 started; 183 184 mutex_lock(&wil->mutex); 185 186 if (cookie != p2p->cookie) { 187 wil_info(wil, "%s: Cookie mismatch: 0x%016llx vs. 0x%016llx\n", 188 __func__, p2p->cookie, cookie); 189 mutex_unlock(&wil->mutex); 190 return -ENOENT; 191 } 192 193 started = wil_p2p_stop_discovery(wil); 194 195 mutex_unlock(&wil->mutex); 196 197 if (!started) { 198 wil_err(wil, "%s: listen not started\n", __func__); 199 return -ENOENT; 200 } 201 202 mutex_lock(&wil->p2p_wdev_mutex); 203 cfg80211_remain_on_channel_expired(wil->radio_wdev, 204 p2p->cookie, 205 &p2p->listen_chan, 206 GFP_KERNEL); 207 wil->radio_wdev = wil->wdev; 208 mutex_unlock(&wil->p2p_wdev_mutex); 209 return 0; 210 } 211 212 void wil_p2p_listen_expired(struct work_struct *work) 213 { 214 struct wil_p2p_info *p2p = container_of(work, 215 struct wil_p2p_info, discovery_expired_work); 216 struct wil6210_priv *wil = container_of(p2p, 217 struct wil6210_priv, p2p); 218 u8 started; 219 220 wil_dbg_misc(wil, "%s()\n", __func__); 221 222 mutex_lock(&wil->mutex); 223 started = wil_p2p_stop_discovery(wil); 224 mutex_unlock(&wil->mutex); 225 226 if (started) { 227 mutex_lock(&wil->p2p_wdev_mutex); 228 cfg80211_remain_on_channel_expired(wil->radio_wdev, 229 p2p->cookie, 230 &p2p->listen_chan, 231 GFP_KERNEL); 232 wil->radio_wdev = wil->wdev; 233 mutex_unlock(&wil->p2p_wdev_mutex); 234 } 235 236 } 237 238 void wil_p2p_search_expired(struct work_struct *work) 239 { 240 struct wil_p2p_info *p2p = container_of(work, 241 struct wil_p2p_info, discovery_expired_work); 242 struct wil6210_priv *wil = container_of(p2p, 243 struct wil6210_priv, p2p); 244 u8 started; 245 246 wil_dbg_misc(wil, "%s()\n", __func__); 247 248 mutex_lock(&wil->mutex); 249 started = wil_p2p_stop_discovery(wil); 250 mutex_unlock(&wil->mutex); 251 252 if (started) { 253 mutex_lock(&wil->p2p_wdev_mutex); 254 cfg80211_scan_done(wil->scan_request, 0); 255 wil->scan_request = NULL; 256 wil->radio_wdev = wil->wdev; 257 mutex_unlock(&wil->p2p_wdev_mutex); 258 } 259 } 260