1 /*
2  * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2022-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_lpass.c
22  *
23  *  WLAN Host Device Driver LPASS feature implementation
24  *
25  */
26 
27 /* Include Files */
28 #include "wlan_hdd_main.h"
29 #include "wlan_hdd_lpass.h"
30 #include "wlan_hdd_oemdata.h"
31 #include <cds_utils.h>
32 #include "qwlan_version.h"
33 
34 /**
35  * wlan_hdd_get_channel_info() - Get channel info
36  * @hdd_ctx: HDD context
37  * @chan_info: Pointer to the structure that stores channel info
38  * @chan_freq: Channel freq
39  *
40  * Fill in the channel info to chan_info structure.
41  */
wlan_hdd_get_channel_info(struct hdd_context * hdd_ctx,struct svc_channel_info * chan_info,uint32_t chan_freq)42 static void wlan_hdd_get_channel_info(struct hdd_context *hdd_ctx,
43 				      struct svc_channel_info *chan_info,
44 				      uint32_t chan_freq)
45 {
46 	uint32_t reg_info_1;
47 	uint32_t reg_info_2;
48 	QDF_STATUS status = QDF_STATUS_E_FAILURE;
49 
50 	status = sme_get_reg_info(hdd_ctx->mac_handle, chan_freq,
51 				  &reg_info_1, &reg_info_2);
52 	if (status != QDF_STATUS_SUCCESS)
53 		return;
54 
55 	chan_info->mhz = chan_freq;
56 	chan_info->band_center_freq1 = chan_info->mhz;
57 	chan_info->band_center_freq2 = 0;
58 	chan_info->info = 0;
59 	if (CHANNEL_STATE_DFS ==
60 	    wlan_reg_get_channel_state_for_pwrmode(hdd_ctx->pdev, chan_freq,
61 						   REG_CURRENT_PWR_MODE))
62 		WMI_SET_CHANNEL_FLAG(chan_info,
63 				     WMI_CHAN_FLAG_DFS);
64 	hdd_update_channel_bw_info(hdd_ctx, chan_freq,
65 				   chan_info);
66 	chan_info->reg_info_1 = reg_info_1;
67 	chan_info->reg_info_2 = reg_info_2;
68 }
69 
70 /**
71  * wlan_hdd_gen_wlan_status_pack() - Create lpass adapter status package
72  * @data: Status data record to be created
73  * @link_info: Link info pointer in HDD adapter
74  * @sta_ctx: Station-specific context of @adapter
75  * @is_on: Is wlan driver loaded?
76  * @is_connected: Is @adapter connected to an AP?
77  *
78  * Generate a wlan vdev status package. The status info includes wlan
79  * on/off status, vdev ID, vdev mode, supported channels, etc.
80  *
81  * Return: 0 if package was created, otherwise a negative errno
82  */
wlan_hdd_gen_wlan_status_pack(struct wlan_status_data * data,struct wlan_hdd_link_info * link_info,struct hdd_station_ctx * sta_ctx,uint8_t is_on,uint8_t is_connected)83 static int wlan_hdd_gen_wlan_status_pack(struct wlan_status_data *data,
84 					 struct wlan_hdd_link_info *link_info,
85 					 struct hdd_station_ctx *sta_ctx,
86 					 uint8_t is_on, uint8_t is_connected)
87 {
88 	struct hdd_context *hdd_ctx = NULL;
89 	int i;
90 	uint32_t chan_id;
91 	uint32_t *chan_freq_list, chan_freq_len;
92 	struct svc_channel_info *chan_info;
93 	bool lpass_support, wls_6ghz_capable = false;
94 	QDF_STATUS status;
95 	struct hdd_adapter *adapter;
96 
97 	if (!data) {
98 		hdd_err("invalid data pointer");
99 		return -EINVAL;
100 	}
101 	if (!link_info) {
102 		if (is_on) {
103 			/* no active interface */
104 			data->lpss_support = 0;
105 			data->is_on = is_on;
106 			return 0;
107 		}
108 		hdd_err("invalid adapter pointer");
109 		return -EINVAL;
110 	}
111 
112 	if (wlan_hdd_validate_vdev_id(link_info->vdev_id))
113 		return -EINVAL;
114 
115 	adapter = link_info->adapter;
116 	hdd_ctx = WLAN_HDD_GET_CTX(adapter);
117 
118 	status = ucfg_mlme_get_lpass_support(hdd_ctx->psoc, &lpass_support);
119 	if (QDF_IS_STATUS_ERROR(status)) {
120 		hdd_err("Failed to get LPASS support config");
121 		return -EIO;
122 	}
123 	ucfg_mlme_get_wls_6ghz_cap(hdd_ctx->psoc, &wls_6ghz_capable);
124 	if (hdd_ctx->lpss_support && lpass_support)
125 		data->lpss_support = 1;
126 	else
127 		data->lpss_support = 0;
128 
129 	chan_freq_list =
130 		qdf_mem_malloc(sizeof(uint32_t) * WLAN_SVC_MAX_NUM_CHAN);
131 	if (!chan_freq_list)
132 		return -ENOMEM;
133 
134 	chan_freq_len = WLAN_SVC_MAX_NUM_CHAN;
135 	sme_get_cfg_valid_channels(chan_freq_list, &chan_freq_len);
136 
137 	data->numChannels = 0;
138 	for (i = 0; i < chan_freq_len; i++) {
139 		if (!wls_6ghz_capable &&
140 		    wlan_reg_is_6ghz_chan_freq(chan_freq_list[i]))
141 			continue;
142 
143 		chan_id = wlan_reg_freq_to_chan(hdd_ctx->pdev,
144 						chan_freq_list[i]);
145 		if (!chan_id)
146 			continue;
147 
148 		chan_info = &data->channel_info[data->numChannels];
149 		data->channel_list[data->numChannels] = chan_id;
150 		chan_info->chan_id = chan_id;
151 		wlan_hdd_get_channel_info(hdd_ctx,
152 					  chan_info,
153 					  chan_freq_list[i]);
154 		data->numChannels++;
155 	}
156 
157 	qdf_mem_free(chan_freq_list);
158 
159 	wlan_reg_get_cc_and_src(hdd_ctx->psoc, data->country_code);
160 	data->is_on = is_on;
161 	data->vdev_id = link_info->vdev_id;
162 	data->vdev_mode = adapter->device_mode;
163 	if (sta_ctx) {
164 		data->is_connected = is_connected;
165 		data->rssi = link_info->rssi;
166 		data->freq = sta_ctx->conn_info.chan_freq;
167 		if (WLAN_SVC_MAX_SSID_LEN >=
168 		    sta_ctx->conn_info.ssid.SSID.length) {
169 			data->ssid_len = sta_ctx->conn_info.ssid.SSID.length;
170 			memcpy(data->ssid,
171 			       sta_ctx->conn_info.ssid.SSID.ssId,
172 			       sta_ctx->conn_info.ssid.SSID.length);
173 		}
174 		if (QDF_MAC_ADDR_SIZE >= sizeof(sta_ctx->conn_info.bssid))
175 			memcpy(data->bssid, sta_ctx->conn_info.bssid.bytes,
176 			       QDF_MAC_ADDR_SIZE);
177 	}
178 	return 0;
179 }
180 
181 /**
182  * wlan_hdd_gen_wlan_version_pack() - Create lpass version package
183  * @data: Version data record to be created
184  * @fw_version: Version code from firmware
185  * @chip_id: WLAN chip ID
186  * @chip_name: WLAN chip name
187  *
188  * Generate a wlan software/hw version info package. The version info
189  * includes wlan host driver version, wlan fw driver version, wlan hw
190  * chip id & wlan hw chip name.
191  *
192  * Return: 0 if package was created, otherwise a negative errno
193  */
wlan_hdd_gen_wlan_version_pack(struct wlan_version_data * data,uint32_t fw_version,uint32_t chip_id,const char * chip_name)194 static int wlan_hdd_gen_wlan_version_pack(struct wlan_version_data *data,
195 					  uint32_t fw_version,
196 					  uint32_t chip_id,
197 					  const char *chip_name)
198 {
199 	if (!data) {
200 		hdd_err("invalid data pointer");
201 		return -EINVAL;
202 	}
203 
204 	data->chip_id = chip_id;
205 	strlcpy(data->chip_name, chip_name, WLAN_SVC_MAX_STR_LEN);
206 	if (strncmp(chip_name, "Unknown", 7))
207 		strlcpy(data->chip_from, "Qualcomm", WLAN_SVC_MAX_STR_LEN);
208 	else
209 		strlcpy(data->chip_from, "Unknown", WLAN_SVC_MAX_STR_LEN);
210 	strlcpy(data->host_version, QWLAN_VERSIONSTR, WLAN_SVC_MAX_STR_LEN);
211 	scnprintf(data->fw_version, WLAN_SVC_MAX_STR_LEN, "%d.%d.%d.%d",
212 		  (fw_version & 0xf0000000) >> 28,
213 		  (fw_version & 0xf000000) >> 24,
214 		  (fw_version & 0xf00000) >> 20, (fw_version & 0x7fff));
215 	return 0;
216 }
217 
218 /**
219  * wlan_hdd_send_status_pkg() - Send adapter status to lpass
220  * @link_info: Link info pointer in HDD adapter
221  * @sta_ctx: Station-specific context of @adapter
222  * @is_on: Is @adapter enabled
223  * @is_connected: Is @adapter connected
224  *
225  * Generate wlan vdev status package and send it to a user space
226  * daemon through netlink.
227  *
228  * Return: none
229  */
wlan_hdd_send_status_pkg(struct wlan_hdd_link_info * link_info,struct hdd_station_ctx * sta_ctx,uint8_t is_on,uint8_t is_connected)230 static void wlan_hdd_send_status_pkg(struct wlan_hdd_link_info *link_info,
231 				     struct hdd_station_ctx *sta_ctx,
232 				     uint8_t is_on, uint8_t is_connected)
233 {
234 	int ret = 0;
235 	struct wlan_status_data *data = NULL;
236 	struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
237 
238 	if (!hdd_ctx)
239 		return;
240 
241 	if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam())
242 		return;
243 
244 	data = kzalloc(sizeof(*data), GFP_KERNEL);
245 	if (!data)
246 		return;
247 
248 	if (is_on)
249 		ret = wlan_hdd_gen_wlan_status_pack(data, link_info,
250 						    sta_ctx, is_on,
251 						    is_connected);
252 
253 	if (!ret)
254 		wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index,
255 					    WLAN_SVC_WLAN_STATUS_IND,
256 					    data, sizeof(*data));
257 	kfree(data);
258 }
259 
260 /**
261  * wlan_hdd_send_version_pkg() - report version information to lpass
262  * @fw_version: Version code from firmware
263  * @chip_id: WLAN chip ID
264  * @chip_name: WLAN chip name
265  *
266  * Generate a wlan sw/hw version info package and send it to a user
267  * space daemon through netlink.
268  *
269  * Return: none
270  */
wlan_hdd_send_version_pkg(uint32_t fw_version,uint32_t chip_id,const char * chip_name)271 static void wlan_hdd_send_version_pkg(uint32_t fw_version,
272 				      uint32_t chip_id,
273 				      const char *chip_name)
274 {
275 	int ret = 0;
276 	struct wlan_version_data data;
277 	struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
278 
279 	if (!hdd_ctx)
280 		return;
281 
282 	if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam())
283 		return;
284 
285 	memset(&data, 0, sizeof(struct wlan_version_data));
286 	ret = wlan_hdd_gen_wlan_version_pack(&data, fw_version, chip_id,
287 					     chip_name);
288 	if (!ret)
289 		wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index,
290 					WLAN_SVC_WLAN_VERSION_IND,
291 					    &data, sizeof(data));
292 }
293 
294 /**
295  * wlan_hdd_send_scan_intf_info() - report scan interfaces to lpass
296  * @link_info: Link info pointer in HDD adapter
297  *
298  * This function indicates adapter that supports scanning to lpass.
299  *
300  * Return: none
301  */
302 static inline void
wlan_hdd_send_scan_intf_info(struct wlan_hdd_link_info * link_info)303 wlan_hdd_send_scan_intf_info(struct wlan_hdd_link_info *link_info)
304 {
305 	wlan_hdd_send_status_pkg(link_info, NULL, 1, 0);
306 }
307 
308 /*
309  * hdd_lpass_target_config() - Handle LPASS target configuration
310  * (public function documented in wlan_hdd_lpass.h)
311  */
hdd_lpass_target_config(struct hdd_context * hdd_ctx,struct wma_tgt_cfg * target_config)312 void hdd_lpass_target_config(struct hdd_context *hdd_ctx,
313 			     struct wma_tgt_cfg *target_config)
314 {
315 	hdd_ctx->lpss_support = target_config->lpss_support;
316 }
317 
318 /*
319  * hdd_lpass_populate_cds_config() - Populate LPASS configuration
320  * (public function documented in wlan_hdd_lpass.h)
321  */
hdd_lpass_populate_cds_config(struct cds_config_info * cds_config,struct hdd_context * hdd_ctx)322 void hdd_lpass_populate_cds_config(struct cds_config_info *cds_config,
323 				   struct hdd_context *hdd_ctx)
324 {
325 	bool lpass_support = false;
326 	QDF_STATUS status;
327 
328 	status = ucfg_mlme_get_lpass_support(hdd_ctx->psoc, &lpass_support);
329 	if (QDF_IS_STATUS_ERROR(status))
330 		hdd_err("Failed to get LPASS support config");
331 
332 	cds_config->is_lpass_enabled = lpass_support;
333 }
334 
335 /*
336  * hdd_lpass_populate_pmo_config() - Populate LPASS configuration
337  * (public function documented in wlan_hdd_lpass.h)
338  */
hdd_lpass_populate_pmo_config(struct pmo_psoc_cfg * pmo_config,struct hdd_context * hdd_ctx)339 void hdd_lpass_populate_pmo_config(struct pmo_psoc_cfg *pmo_config,
340 				   struct hdd_context *hdd_ctx)
341 {
342 	bool lpass_support = false;
343 	QDF_STATUS status;
344 
345 	status = ucfg_mlme_get_lpass_support(hdd_ctx->psoc, &lpass_support);
346 	if (QDF_IS_STATUS_ERROR(status))
347 		hdd_err("Failed to get LPASS support config");
348 
349 	pmo_config->lpass_enable = lpass_support;
350 }
351 
hdd_lpass_notify_connect(struct wlan_hdd_link_info * link_info)352 void hdd_lpass_notify_connect(struct wlan_hdd_link_info *link_info)
353 {
354 	struct hdd_station_ctx *sta_ctx;
355 
356 	/* only send once per connection */
357 	if (link_info->rssi_send)
358 		return;
359 
360 	/* don't send if driver is unloading */
361 	if (cds_is_driver_unloading())
362 		return;
363 
364 	link_info->rssi_send = true;
365 	sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(link_info);
366 	wlan_hdd_send_status_pkg(link_info, sta_ctx, 1, 1);
367 }
368 
hdd_lpass_notify_disconnect(struct wlan_hdd_link_info * link_info)369 void hdd_lpass_notify_disconnect(struct wlan_hdd_link_info *link_info)
370 {
371 	struct hdd_station_ctx *sta_ctx;
372 
373 	link_info->rssi_send = false;
374 	sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(link_info);
375 	wlan_hdd_send_status_pkg(link_info, sta_ctx, 1, 0);
376 }
377 
hdd_lpass_notify_mode_change(struct wlan_hdd_link_info * link_info)378 void hdd_lpass_notify_mode_change(struct wlan_hdd_link_info *link_info)
379 {
380 	if (link_info->adapter->device_mode != QDF_STA_MODE)
381 		return;
382 
383 	hdd_debug("Sending Lpass mode change notification");
384 
385 	wlan_hdd_send_scan_intf_info(link_info);
386 }
387 
388 /*
389  * hdd_lpass_notify_wlan_version() - Notify LPASS WLAN Host/FW version
390  *
391  * implementation note: Notify LPASS for the WLAN host/firmware version.
392  */
hdd_lpass_notify_wlan_version(struct hdd_context * hdd_ctx)393 void hdd_lpass_notify_wlan_version(struct hdd_context *hdd_ctx)
394 {
395 	hdd_enter();
396 
397 	wlan_hdd_send_version_pkg(hdd_ctx->target_fw_version,
398 				  hdd_ctx->target_hw_version,
399 				  hdd_ctx->target_hw_name);
400 
401 	hdd_exit();
402 }
403 
hdd_lpass_notify_start(struct wlan_hdd_link_info * link_info)404 void hdd_lpass_notify_start(struct wlan_hdd_link_info *link_info)
405 {
406 	hdd_enter();
407 
408 	if (link_info->adapter->device_mode != QDF_STA_MODE)
409 		return;
410 
411 	hdd_debug("Sending Start Lpass notification");
412 
413 	wlan_hdd_send_scan_intf_info(link_info);
414 
415 	hdd_exit();
416 }
417 
hdd_lpass_notify_stop(struct hdd_context * hdd_ctx)418 void hdd_lpass_notify_stop(struct hdd_context *hdd_ctx)
419 {
420 	hdd_debug("Sending Lpass stop notification");
421 	wlan_hdd_send_status_pkg(NULL, NULL, 0, 0);
422 }
423 
424 /*
425  * hdd_lpass_is_supported() - Is lpass feature supported?
426  * (public function documented in wlan_hdd_lpass.h)
427  */
hdd_lpass_is_supported(struct hdd_context * hdd_ctx)428 bool hdd_lpass_is_supported(struct hdd_context *hdd_ctx)
429 {
430 	bool lpass_support = false;
431 	QDF_STATUS status;
432 
433 	status = ucfg_mlme_get_lpass_support(hdd_ctx->psoc, &lpass_support);
434 	if (QDF_IS_STATUS_ERROR(status))
435 		hdd_err("Failed to get LPASS support config");
436 
437 	return lpass_support;
438 }
439