xref: /wlan-driver/qcacld-3.0/core/hdd/src/wlan_hdd_p2p_listen_offload.c (revision 5113495b16420b49004c444715d2daae2066e7dc)
1 /*
2  * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for
6  * any purpose with or without fee is hereby granted, provided that the
7  * above copyright notice and this permission notice appear in all
8  * copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 /**
21  * DOC: wlan_hdd_vendor_p2p_listen_offload.c
22  *
23  * WLAN p2p listen offload functions
24  *
25  */
26 
27 #include "osif_sync.h"
28 #include <wlan_hdd_includes.h>
29 #include <linux/netdevice.h>
30 #include <linux/skbuff.h>
31 #include <linux/etherdevice.h>
32 #include <linux/if_ether.h>
33 #include <wlan_hdd_p2p.h>
34 #include <wlan_p2p_ucfg_api.h>
35 #include <wlan_hdd_p2p_listen_offload.h>
36 
37 /* P2P listen offload device types parameters length in bytes */
38 #define P2P_LO_MAX_REQ_DEV_TYPE_COUNT (10)
39 #define P2P_LO_WPS_DEV_TYPE_LEN (8)
40 #define P2P_LO_DEV_TYPE_MAX_LEN \
41 	(P2P_LO_MAX_REQ_DEV_TYPE_COUNT * P2P_LO_WPS_DEV_TYPE_LEN)
42 
43 const struct nla_policy
44 p2p_listen_offload_policy[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_MAX + 1] = {
45 	[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CHANNEL] = { .type = NLA_U32 },
46 	[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_PERIOD] = { .type = NLA_U32 },
47 	[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_INTERVAL] = {
48 							.type = NLA_U32 },
49 	[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_COUNT] = { .type = NLA_U32 },
50 	[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_DEVICE_TYPES] = {
51 					.type = NLA_BINARY,
52 					.len = P2P_LO_DEV_TYPE_MAX_LEN },
53 	[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_VENDOR_IE] = {
54 					.type = NLA_BINARY,
55 					.len = MAX_GENIE_LEN },
56 	[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CTRL_FLAG] = {
57 					.type = NLA_U32 },
58 	[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CHANNEL] = { .type = NLA_U32 },
59 	[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_STOP_REASON] = {
60 						.type = NLA_U8 },
61 };
62 
63 /**
64  * wlan_hdd_listen_offload_start() - hdd set listen offload start
65  * @adapter: adapter context
66  * @params: listen offload parameters
67  *
68  * This function sets listen offload start parameters.
69  *
70  * Return: 0 on success, others on failure
71  */
wlan_hdd_listen_offload_start(struct hdd_adapter * adapter,struct sir_p2p_lo_start * params)72 static int wlan_hdd_listen_offload_start(struct hdd_adapter *adapter,
73 					 struct sir_p2p_lo_start *params)
74 {
75 	struct wlan_objmgr_psoc *psoc;
76 	struct p2p_lo_start lo_start;
77 	struct hdd_context *hdd_ctx;
78 	QDF_STATUS status;
79 
80 	if (!adapter || !params) {
81 		hdd_err("null param, adapter:%pK, params:%pK",
82 			adapter, params);
83 		return -EINVAL;
84 	}
85 
86 	hdd_ctx = WLAN_HDD_GET_CTX(adapter);
87 	psoc = hdd_ctx->psoc;
88 	if (!psoc) {
89 		hdd_err("psoc is null");
90 		return -EINVAL;
91 	}
92 
93 	lo_start.vdev_id = params->vdev_id;
94 	lo_start.ctl_flags = params->ctl_flags;
95 	lo_start.freq = params->freq;
96 	lo_start.period = params->period;
97 	lo_start.interval = params->interval;
98 	lo_start.count = params->count;
99 	lo_start.device_types = params->device_types;
100 	lo_start.dev_types_len = params->dev_types_len;
101 	lo_start.probe_resp_tmplt = params->probe_resp_tmplt;
102 	lo_start.probe_resp_len = params->probe_resp_len;
103 
104 	status = ucfg_p2p_lo_start(psoc, &lo_start);
105 	hdd_debug("p2p listen offload start, status:%d", status);
106 
107 	return qdf_status_to_os_return(status);
108 }
109 
110 /**
111  * __wlan_hdd_cfg80211_p2p_lo_start () - start P2P Listen Offload
112  * @wiphy: Pointer to wireless phy
113  * @wdev: Pointer to wireless device
114  * @data: Pointer to data
115  * @data_len: Data length
116  *
117  * This function is to process the p2p listen offload start vendor
118  * command. It parses the input parameters and invoke WMA API to
119  * send the command to firmware.
120  *
121  * Return: 0 on success, negative errno on failure
122  */
__wlan_hdd_cfg80211_p2p_lo_start(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)123 static int __wlan_hdd_cfg80211_p2p_lo_start(struct wiphy *wiphy,
124 					    struct wireless_dev *wdev,
125 					    const void *data,
126 					    int data_len)
127 {
128 	int ret;
129 	struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
130 	struct net_device *dev = wdev->netdev;
131 	struct hdd_adapter *adapter;
132 	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_MAX + 1];
133 	struct sir_p2p_lo_start params;
134 
135 	hdd_enter_dev(dev);
136 
137 	ret = wlan_hdd_validate_context(hdd_ctx);
138 	if (ret)
139 		return ret;
140 
141 	if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) {
142 		hdd_err("Command not allowed in FTM mode");
143 		return -EPERM;
144 	}
145 
146 	adapter = WLAN_HDD_GET_PRIV_PTR(dev);
147 	if ((adapter->device_mode != QDF_P2P_DEVICE_MODE) &&
148 	    (adapter->device_mode != QDF_P2P_CLIENT_MODE) &&
149 	    (adapter->device_mode != QDF_P2P_GO_MODE)) {
150 		hdd_err("Invalid device mode %d", adapter->device_mode);
151 		return -EINVAL;
152 	}
153 
154 	if (wlan_cfg80211_nla_parse(tb,
155 				    QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_MAX,
156 				    data, data_len,
157 				    p2p_listen_offload_policy)) {
158 		hdd_err("Invalid ATTR");
159 		return -EINVAL;
160 	}
161 
162 	memset(&params, 0, sizeof(params));
163 
164 	if (!tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CTRL_FLAG])
165 		params.ctl_flags = 1;  /* set to default value */
166 	else
167 		params.ctl_flags = nla_get_u32(tb
168 			[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CTRL_FLAG]);
169 
170 	if (!tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CHANNEL] ||
171 	    !tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_PERIOD] ||
172 	    !tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_INTERVAL] ||
173 	    !tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_COUNT] ||
174 	    !tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_DEVICE_TYPES] ||
175 	    !tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_VENDOR_IE]) {
176 		hdd_err("Attribute parsing failed");
177 		return -EINVAL;
178 	}
179 
180 	params.vdev_id = adapter->deflink->vdev_id;
181 	params.freq = nla_get_u32(tb
182 		[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CHANNEL]);
183 	if ((params.freq != 2412) && (params.freq != 2437) &&
184 	    (params.freq != 2462)) {
185 		hdd_err("Invalid listening channel: %d", params.freq);
186 		return -EINVAL;
187 	}
188 
189 	params.period = nla_get_u32(tb
190 		[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_PERIOD]);
191 	if (!((params.period > 0) && (params.period < UINT_MAX))) {
192 		hdd_err("Invalid period: %d", params.period);
193 		return -EINVAL;
194 	}
195 
196 	params.interval = nla_get_u32(tb
197 		[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_INTERVAL]);
198 	if (!((params.interval > 0) && (params.interval < UINT_MAX))) {
199 		hdd_err("Invalid interval: %d", params.interval);
200 		return -EINVAL;
201 	}
202 
203 	params.count = nla_get_u32(tb
204 		[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_COUNT]);
205 	if (!((params.count >= 0) && (params.count < UINT_MAX))) {
206 		hdd_err("Invalid count: %d", params.count);
207 		return -EINVAL;
208 	}
209 
210 	params.device_types = nla_data(tb
211 		[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_DEVICE_TYPES]);
212 	if (!params.device_types) {
213 		hdd_err("Invalid device types");
214 		return -EINVAL;
215 	}
216 
217 	params.dev_types_len = nla_len(tb
218 		[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_DEVICE_TYPES]);
219 	/* device type length has to be multiple of P2P_LO_WPS_DEV_TYPE_LEN */
220 	if (0 != (params.dev_types_len % P2P_LO_WPS_DEV_TYPE_LEN)) {
221 		hdd_err("Invalid device type length: %d", params.dev_types_len);
222 		return -EINVAL;
223 	}
224 
225 	params.probe_resp_tmplt = nla_data(tb
226 		[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_VENDOR_IE]);
227 	if (!params.probe_resp_tmplt) {
228 		hdd_err("Invalid probe response template");
229 		return -EINVAL;
230 	}
231 
232 	/*
233 	 * IEs minimum length should be 2 bytes: 1 byte for element id
234 	 * and 1 byte for element id length.
235 	 */
236 	params.probe_resp_len = nla_len(tb
237 		[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_VENDOR_IE]);
238 	if (params.probe_resp_len < MIN_GENIE_LEN) {
239 		hdd_err("Invalid probe resp template length: %d",
240 			params.probe_resp_len);
241 		return -EINVAL;
242 	}
243 
244 	hdd_debug("P2P LO params: freq=%d, period=%d, interval=%d, count=%d",
245 		  params.freq, params.period, params.interval, params.count);
246 
247 	return wlan_hdd_listen_offload_start(adapter, &params);
248 }
249 
wlan_hdd_cfg80211_p2p_lo_start(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)250 int wlan_hdd_cfg80211_p2p_lo_start(struct wiphy *wiphy,
251 				   struct wireless_dev *wdev,
252 				   const void *data,
253 				   int data_len)
254 {
255 	struct osif_vdev_sync *vdev_sync;
256 	int errno;
257 
258 	errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
259 	if (errno)
260 		return errno;
261 
262 	errno = __wlan_hdd_cfg80211_p2p_lo_start(wiphy, wdev,
263 						 data, data_len);
264 
265 	osif_vdev_sync_op_stop(vdev_sync);
266 
267 	return errno;
268 }
269 
270 /**
271  * wlan_hdd_listen_offload_stop() - hdd set listen offload stop
272  * @adapter: adapter context
273  *
274  * This function sets listen offload stop parameters.
275  *
276  * Return: 0 on success, others on failure
277  */
wlan_hdd_listen_offload_stop(struct hdd_adapter * adapter)278 static int wlan_hdd_listen_offload_stop(struct hdd_adapter *adapter)
279 {
280 	struct wlan_objmgr_psoc *psoc;
281 	struct hdd_context *hdd_ctx;
282 	uint32_t vdev_id;
283 	QDF_STATUS status;
284 
285 	if (!adapter) {
286 		hdd_err("adapter is null, adapter:%pK", adapter);
287 		return -EINVAL;
288 	}
289 
290 	vdev_id = (uint32_t)adapter->deflink->vdev_id;
291 	hdd_ctx = WLAN_HDD_GET_CTX(adapter);
292 	psoc = hdd_ctx->psoc;
293 	if (!psoc) {
294 		hdd_err("psoc is null");
295 		return -EINVAL;
296 	}
297 
298 	status = ucfg_p2p_lo_stop(psoc, vdev_id);
299 	hdd_debug("p2p listen offload stop, status:%d", status);
300 
301 	return qdf_status_to_os_return(status);
302 }
303 
304 /**
305  * __wlan_hdd_cfg80211_p2p_lo_stop () - stop P2P Listen Offload
306  * @wiphy: Pointer to wireless phy
307  * @wdev: Pointer to wireless device
308  * @data: Pointer to data
309  * @data_len: Data length
310  *
311  * This function is to process the p2p listen offload stop vendor
312  * command. It invokes WMA API to send command to firmware.
313  *
314  * Return: 0 on success, negative errno on failure
315  */
__wlan_hdd_cfg80211_p2p_lo_stop(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)316 static int __wlan_hdd_cfg80211_p2p_lo_stop(struct wiphy *wiphy,
317 					   struct wireless_dev *wdev,
318 					   const void *data,
319 					   int data_len)
320 {
321 	struct hdd_adapter *adapter;
322 	struct net_device *dev = wdev->netdev;
323 
324 	if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) {
325 		hdd_err("Command not allowed in FTM mode");
326 		return -EPERM;
327 	}
328 
329 	adapter = WLAN_HDD_GET_PRIV_PTR(dev);
330 	if ((adapter->device_mode != QDF_P2P_DEVICE_MODE) &&
331 	    (adapter->device_mode != QDF_P2P_CLIENT_MODE) &&
332 	    (adapter->device_mode != QDF_P2P_GO_MODE)) {
333 		hdd_err("Invalid device mode");
334 		return -EINVAL;
335 	}
336 
337 	return wlan_hdd_listen_offload_stop(adapter);
338 }
339 
wlan_hdd_cfg80211_p2p_lo_stop(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)340 int wlan_hdd_cfg80211_p2p_lo_stop(struct wiphy *wiphy,
341 				  struct wireless_dev *wdev,
342 				  const void *data,
343 				  int data_len)
344 {
345 	struct osif_vdev_sync *vdev_sync;
346 	int errno;
347 
348 	errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
349 	if (errno)
350 		return errno;
351 
352 	errno = __wlan_hdd_cfg80211_p2p_lo_stop(wiphy, wdev,
353 						data, data_len);
354 
355 	osif_vdev_sync_op_stop(vdev_sync);
356 
357 	return errno;
358 }
359 
360