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 ®_info_1, ®_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