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 return -EINVAL; 119 120 channel = chan->hw_value; 121 122 wil_dbg_misc(wil, "%s: duration %d\n", __func__, duration); 123 124 mutex_lock(&wil->mutex); 125 126 if (p2p->discovery_started) { 127 wil_err(wil, "%s: discovery already ongoing\n", __func__); 128 rc = -EBUSY; 129 goto out; 130 } 131 132 rc = wmi_p2p_cfg(wil, channel, P2P_DEFAULT_BI); 133 if (rc) { 134 wil_err(wil, "%s: wmi_p2p_cfg failed\n", __func__); 135 goto out; 136 } 137 138 rc = wmi_set_ssid(wil, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID); 139 if (rc) { 140 wil_err(wil, "%s: wmi_set_ssid failed\n", __func__); 141 goto out_stop; 142 } 143 144 rc = wmi_start_listen(wil); 145 if (rc) { 146 wil_err(wil, "%s: wmi_start_listen failed\n", __func__); 147 goto out_stop; 148 } 149 150 memcpy(&p2p->listen_chan, chan, sizeof(*chan)); 151 *cookie = ++p2p->cookie; 152 153 p2p->discovery_started = 1; 154 INIT_WORK(&p2p->discovery_expired_work, wil_p2p_listen_expired); 155 mod_timer(&p2p->discovery_timer, 156 jiffies + msecs_to_jiffies(duration)); 157 158 out_stop: 159 if (rc) 160 wmi_stop_discovery(wil); 161 162 out: 163 mutex_unlock(&wil->mutex); 164 return rc; 165 } 166 167 u8 wil_p2p_stop_discovery(struct wil6210_priv *wil) 168 { 169 struct wil_p2p_info *p2p = &wil->p2p; 170 u8 started = p2p->discovery_started; 171 172 if (p2p->discovery_started) { 173 del_timer_sync(&p2p->discovery_timer); 174 p2p->discovery_started = 0; 175 wmi_stop_discovery(wil); 176 } 177 178 return started; 179 } 180 181 int wil_p2p_cancel_listen(struct wil6210_priv *wil, u64 cookie) 182 { 183 struct wil_p2p_info *p2p = &wil->p2p; 184 u8 started; 185 186 mutex_lock(&wil->mutex); 187 188 if (cookie != p2p->cookie) { 189 wil_info(wil, "%s: Cookie mismatch: 0x%016llx vs. 0x%016llx\n", 190 __func__, p2p->cookie, cookie); 191 mutex_unlock(&wil->mutex); 192 return -ENOENT; 193 } 194 195 started = wil_p2p_stop_discovery(wil); 196 197 mutex_unlock(&wil->mutex); 198 199 if (!started) { 200 wil_err(wil, "%s: listen not started\n", __func__); 201 return -ENOENT; 202 } 203 204 mutex_lock(&wil->p2p_wdev_mutex); 205 cfg80211_remain_on_channel_expired(wil->radio_wdev, 206 p2p->cookie, 207 &p2p->listen_chan, 208 GFP_KERNEL); 209 wil->radio_wdev = wil->wdev; 210 mutex_unlock(&wil->p2p_wdev_mutex); 211 return 0; 212 } 213 214 void wil_p2p_listen_expired(struct work_struct *work) 215 { 216 struct wil_p2p_info *p2p = container_of(work, 217 struct wil_p2p_info, discovery_expired_work); 218 struct wil6210_priv *wil = container_of(p2p, 219 struct wil6210_priv, p2p); 220 u8 started; 221 222 wil_dbg_misc(wil, "%s()\n", __func__); 223 224 mutex_lock(&wil->mutex); 225 started = wil_p2p_stop_discovery(wil); 226 mutex_unlock(&wil->mutex); 227 228 if (started) { 229 mutex_lock(&wil->p2p_wdev_mutex); 230 cfg80211_remain_on_channel_expired(wil->radio_wdev, 231 p2p->cookie, 232 &p2p->listen_chan, 233 GFP_KERNEL); 234 wil->radio_wdev = wil->wdev; 235 mutex_unlock(&wil->p2p_wdev_mutex); 236 } 237 238 } 239 240 void wil_p2p_search_expired(struct work_struct *work) 241 { 242 struct wil_p2p_info *p2p = container_of(work, 243 struct wil_p2p_info, discovery_expired_work); 244 struct wil6210_priv *wil = container_of(p2p, 245 struct wil6210_priv, p2p); 246 u8 started; 247 248 wil_dbg_misc(wil, "%s()\n", __func__); 249 250 mutex_lock(&wil->mutex); 251 started = wil_p2p_stop_discovery(wil); 252 mutex_unlock(&wil->mutex); 253 254 if (started) { 255 struct cfg80211_scan_info info = { 256 .aborted = false, 257 }; 258 259 mutex_lock(&wil->p2p_wdev_mutex); 260 cfg80211_scan_done(wil->scan_request, &info); 261 wil->scan_request = NULL; 262 wil->radio_wdev = wil->wdev; 263 mutex_unlock(&wil->p2p_wdev_mutex); 264 } 265 } 266