1 /*
2 * Copyright (c) 2021, 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 any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 /**
19 * DOC: mlo_global_h_shmem_arena.c
20 * This file contains definition of functions that parse the MLO global
21 * shared memory arena.
22 */
23
24 #include<mlo_global_h_shmem_arena.h>
25 #include<wlan_mlo_mgr_public_structs.h>
26 static struct wlan_host_mlo_glb_h_shmem_arena_ctx
27 g_shmem_arena_ctx[WLAN_MAX_MLO_GROUPS];
28
29 #define get_shmem_arena_ctx(__grp_id) (&g_shmem_arena_ctx[__grp_id])
30
31 /**
32 * is_field_present_in_tlv() - Check whether a given field is present
33 * in a given TLV
34 * @ptlv: Pointer to start of the TLV
35 * @field_name: name of the field in the TLV structure
36 * @tlv_len: Length of the TLV
37 *
38 * Return: true if the field is present within the TLV,
39 * else false
40 */
41 #define is_field_present_in_tlv(ptlv, field_name, tlv_len) \
42 (qdf_offsetof(typeof(*(ptlv)), field_name) < (tlv_len) ? \
43 true : false)
44
45 #ifdef BIG_ENDIAN_HOST
46 static inline void
convert_dwords_to_host_order(uint32_t * pwords,size_t num_words)47 convert_dwords_to_host_order(uint32_t *pwords, size_t num_words)
48 {
49 size_t word = 0;
50
51 for (; word < num_words; word++) {
52 *pwords = qdf_le32_to_cpu(*pwords);
53 ++pwords;
54 }
55 }
56 #else
57 static inline void
convert_dwords_to_host_order(uint32_t * pwords,size_t num_words)58 convert_dwords_to_host_order(uint32_t *pwords, size_t num_words)
59 {
60 }
61 #endif
62
63 /**
64 * get_field_value_in_tlv() - Get the value of a given field in a given TLV
65 * @ptlv: Pointer to start of the TLV
66 * @field_name: name of the field in the TLV structure
67 * @tlv_len: Length of the TLV
68 *
69 * Return: Value of the given field if the offset of the field with in the TLV
70 * structure is less than the TLV length, else 0.
71 */
72 #define get_field_value_in_tlv(ptlv, field_name, tlv_len) \
73 (qdf_offsetof(typeof(*(ptlv)), field_name) >= (tlv_len) ? 0 : \
74 ({ \
75 typeof((ptlv)->field_name) _field_ = (ptlv)->field_name; \
76 qdf_assert(!(sizeof(_field_) & 0x3)); \
77 convert_dwords_to_host_order((uint32_t *)&_field_, \
78 sizeof(_field_) >> 2); \
79 _field_; \
80 }) \
81 )
82
83 /**
84 * get_field_pointer_in_tlv() - Get the address of a given field in a given TLV
85 * @ptlv: Pointer to start of the TLV
86 * @field_name: name of the field in the TLV structure
87 * @tlv_len: Length of the TLV
88 *
89 * Return: Address of the given field if the offset of the field with in the
90 * TLV structure is less than the TLV length, else NULL.
91 */
92 #define get_field_pointer_in_tlv(ptlv, field_name, tlv_len) \
93 (qdf_offsetof(typeof(*(ptlv)), field_name) < (tlv_len) ? \
94 &(ptlv)->field_name : NULL)
95
96 /**
97 * process_tlv_header() - Process a given TLV header
98 * @data: Pointer to start of the TLV
99 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer
100 * @expected_tag: Expected TLV tag
101 * @tlv_len: Address of TLV length variable to be populated. This API populates
102 * the entire length(payload + header) of the TLV into @tlv_len
103 * @tlv_tag: Address of TLV Tag variable to be populated.
104 *
105 * Return: 0 on success, -1 on failure
106 */
107 static int
process_tlv_header(const uint8_t * data,size_t remaining_len,uint32_t expected_tag,uint32_t * tlv_len,uint32_t * tlv_tag)108 process_tlv_header(const uint8_t *data, size_t remaining_len,
109 uint32_t expected_tag, uint32_t *tlv_len,
110 uint32_t *tlv_tag)
111 {
112 uint32_t tlv_header;
113
114 if (remaining_len < MLO_SHMEM_TLV_HDR_SIZE) {
115 target_if_err("Not enough space(%zu) to read TLV header(%u)",
116 remaining_len, (uint32_t)MLO_SHMEM_TLV_HDR_SIZE);
117 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE);
118 }
119
120 tlv_header = MLO_SHMEMTLV_GET_HDR(data);
121 tlv_header = qdf_le32_to_cpu(tlv_header);
122
123 *tlv_len = MLO_SHMEMTLV_GET_TLVLEN(tlv_header);
124 *tlv_len += MLO_SHMEM_TLV_HDR_SIZE;
125 if (remaining_len < *tlv_len) {
126 target_if_err("Not enough space(%zu) to read TLV payload(%u)",
127 remaining_len, *tlv_len);
128 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE);
129 }
130
131 *tlv_tag = MLO_SHMEMTLV_GET_TLVTAG(tlv_header);
132 if (*tlv_tag != expected_tag) {
133 target_if_err("Unexpected TLV tag: %u is seen. Expected: %u",
134 *tlv_tag,
135 expected_tag);
136 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE);
137 }
138
139 return 0;
140 }
141
142 #define validate_parsed_bytes_advance_data_pointer(parsed_bytes, data, \
143 remaining_len) \
144 do { \
145 if ((parsed_bytes) < 0) { \
146 target_if_err("TLV extraction failed"); \
147 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE); \
148 } \
149 (data) += (parsed_bytes); \
150 (remaining_len) -= (parsed_bytes); \
151 } while (0)
152
153 /**
154 * extract_mgmt_rx_reo_snapshot_tlv() - extract MGMT_RX_REO_SNAPSHOT TLV
155 * @data: Pointer to start of the TLV
156 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer
157 * @address_ptr: Pointer to the snapshot address. This API will populate the
158 * snapshot address into the variable pointed by @address_ptr
159 *
160 * Return: On success, the number of bytes parsed. On failure, -1.
161 */
162 static int
extract_mgmt_rx_reo_snapshot_tlv(uint8_t * data,size_t remaining_len,void ** address_ptr)163 extract_mgmt_rx_reo_snapshot_tlv(uint8_t *data, size_t remaining_len,
164 void **address_ptr)
165 {
166 mgmt_rx_reo_snapshot *ptlv;
167 uint32_t tlv_len, tlv_tag;
168
169 qdf_assert_always(data);
170 qdf_assert_always(address_ptr);
171
172 /* process MLO_SHMEM_TLV_STRUCT_MGMT_RX_REO_SNAPSHOT TLV */
173 if (process_tlv_header(data, remaining_len,
174 MLO_SHMEM_TLV_STRUCT_MGMT_RX_REO_SNAPSHOT,
175 &tlv_len, &tlv_tag) != 0) {
176 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE);
177 }
178
179 ptlv = (mgmt_rx_reo_snapshot *)data;
180 *address_ptr = get_field_pointer_in_tlv(ptlv, mgmt_rx_reo_snapshot_low,
181 tlv_len);
182
183 return tlv_len;
184 }
185
186 /**
187 * extract_mlo_glb_rx_reo_per_link_info_tlv() - extract
188 * RX_REO_PER_LINK_SNAPSHOT_INFO TLV
189 * @data: Pointer to start of the TLV
190 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer
191 * @link_id: link ID of interest
192 * @link_info: Pointer to MGMT Rx REO per link info. Extracted information
193 * will be populated in this data structure.
194 *
195 * Return: On success, the number of bytes parsed. On failure, -1.
196 */
197 static int
extract_mlo_glb_rx_reo_per_link_info_tlv(uint8_t * data,size_t remaining_len,uint8_t link_id,struct wlan_host_mlo_glb_rx_reo_per_link_info * link_info)198 extract_mlo_glb_rx_reo_per_link_info_tlv(
199 uint8_t *data, size_t remaining_len, uint8_t link_id,
200 struct wlan_host_mlo_glb_rx_reo_per_link_info *link_info)
201 {
202 mlo_glb_rx_reo_per_link_snapshot_info *ptlv;
203 uint32_t tlv_len, tlv_tag;
204 int len;
205 uint8_t *fw_consumed;
206 int parsed_bytes;
207
208 qdf_assert_always(data);
209 qdf_assert_always(link_info);
210
211 /* process MLO_SHMEM_TLV_STRUCT_MLO_GLB_RX_REO_PER_LINK_SNAPSHOT_INFO TLV */
212 if (process_tlv_header(data, remaining_len,
213 MLO_SHMEM_TLV_STRUCT_MLO_GLB_RX_REO_PER_LINK_SNAPSHOT_INFO,
214 &tlv_len, &tlv_tag) != 0) {
215 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE);
216 }
217
218 ptlv = (mlo_glb_rx_reo_per_link_snapshot_info *)data;
219
220 link_info->link_id = link_id;
221
222 /*
223 * Get the pointer to the fw_consumed snapshot with in the TLV.
224 * Note that snapshots are nested TLVs within link_sanpshot_info TLV.
225 */
226 data += qdf_offsetof(mlo_glb_rx_reo_per_link_snapshot_info,
227 fw_consumed);
228 fw_consumed = (uint8_t *)get_field_pointer_in_tlv(ptlv, fw_consumed,
229 tlv_len);
230 remaining_len -= qdf_offsetof(mlo_glb_rx_reo_per_link_snapshot_info,
231 fw_consumed);
232 parsed_bytes = qdf_offsetof(mlo_glb_rx_reo_per_link_snapshot_info,
233 fw_consumed);
234
235 /* extract fw_consumed snapshot */
236 len = extract_mgmt_rx_reo_snapshot_tlv(data, remaining_len,
237 &link_info->fw_consumed);
238 validate_parsed_bytes_advance_data_pointer(len, data, remaining_len);
239 parsed_bytes += len;
240
241 /* extract fw_forwarded snapshot */
242 len = extract_mgmt_rx_reo_snapshot_tlv(data, remaining_len,
243 &link_info->fw_forwarded);
244 validate_parsed_bytes_advance_data_pointer(len, data, remaining_len);
245 parsed_bytes += len;
246
247 /* extract hw_forwarded snapshot */
248 len = extract_mgmt_rx_reo_snapshot_tlv(data, remaining_len,
249 &link_info->hw_forwarded);
250 validate_parsed_bytes_advance_data_pointer(len, data, remaining_len);
251 parsed_bytes += len;
252
253 /*
254 * Return the length of link_sanpshot_info TLV itself as the snapshots
255 * are nested inside link_sanpshot_info TLV and hence no need to add
256 * their lengths separately.
257 */
258 return tlv_len;
259 }
260
261 /**
262 * get_num_links_from_valid_link_bitmap() - Get the number of valid links
263 * @valid_link_bmap: Link bit map where the valid links are set to 1
264 *
265 * Return: Number of valid links
266 */
267 static uint8_t
get_num_links_from_valid_link_bitmap(uint16_t valid_link_bmap)268 get_num_links_from_valid_link_bitmap(uint16_t valid_link_bmap)
269 {
270 uint8_t num_links = 0;
271
272 /* Find the number of set bits */
273 while (valid_link_bmap) {
274 num_links++;
275 valid_link_bmap &= (valid_link_bmap - 1);
276 }
277
278 return num_links;
279 }
280
281 /**
282 * extract_mlo_glb_rx_reo_snapshot_info_tlv() - extract
283 * MLO_SHMEM_TLV_STRUCT_MLO_GLB_RX_REO_SNAPSHOT_INFO TLV
284 * @data: Pointer to start of the TLV
285 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer
286 * @snapshot_info: Pointer to MGMT Rx REO snapshot info. Extracted information
287 * will be populated in this data structure.
288 *
289 * Return: On success, the number of bytes parsed. On failure, -1.
290 */
291 static int
extract_mlo_glb_rx_reo_snapshot_info_tlv(uint8_t * data,size_t remaining_len,struct wlan_host_mlo_glb_rx_reo_snapshot_info * snapshot_info)292 extract_mlo_glb_rx_reo_snapshot_info_tlv(
293 uint8_t *data, size_t remaining_len,
294 struct wlan_host_mlo_glb_rx_reo_snapshot_info *snapshot_info)
295 {
296 mlo_glb_rx_reo_snapshot_info *ptlv;
297 uint32_t tlv_len, tlv_tag;
298 uint32_t link_info;
299 uint16_t valid_link_bmap;
300
301 qdf_assert_always(data);
302 qdf_assert_always(snapshot_info);
303
304 /* process MLO_SHMEM_TLV_STRUCT_MLO_GLB_RX_REO_SNAPSHOT_INFO TLV */
305 if (process_tlv_header(data, remaining_len,
306 MLO_SHMEM_TLV_STRUCT_MLO_GLB_RX_REO_SNAPSHOT_INFO,
307 &tlv_len, &tlv_tag) != 0) {
308 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE);
309 }
310
311 ptlv = (mlo_glb_rx_reo_snapshot_info *)data;
312 link_info = get_field_value_in_tlv(ptlv, link_info, tlv_len);
313 valid_link_bmap =
314 MLO_SHMEM_GLB_LINK_INFO_PARAM_VALID_LINK_BMAP_GET(link_info);
315
316 snapshot_info->valid_link_bmap = valid_link_bmap;
317
318 if (is_field_present_in_tlv(ptlv, snapshot_ver_info, tlv_len)) {
319 uint32_t snapshot_ver_info;
320
321 snapshot_ver_info = get_field_value_in_tlv
322 (ptlv, snapshot_ver_info, tlv_len);
323 snapshot_info->hw_forwarded_snapshot_ver =
324 MLO_SHMEM_GLB_RX_REO_SNAPSHOT_PARAM_HW_FWD_SNAPSHOT_VER_GET(snapshot_ver_info);
325 snapshot_info->fw_forwarded_snapshot_ver =
326 MLO_SHMEM_GLB_RX_REO_SNAPSHOT_PARAM_FW_FWD_SNAPSHOT_VER_GET(snapshot_ver_info);
327 snapshot_info->fw_consumed_snapshot_ver =
328 MLO_SHMEM_GLB_RX_REO_SNAPSHOT_PARAM_FW_CONSUMED_SNAPSHOT_VER_GET(snapshot_ver_info);
329 }
330
331 snapshot_info->num_links =
332 get_num_links_from_valid_link_bitmap(valid_link_bmap);
333 snapshot_info->link_info = qdf_mem_malloc(
334 sizeof(*snapshot_info->link_info) *
335 snapshot_info->num_links);
336 if (!snapshot_info->link_info) {
337 target_if_err("Couldn't allocate memory for rx_reo_per_link_info");
338 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE);
339 }
340
341 return tlv_len;
342 }
343
344 /**
345 * extract_mlo_glb_link_info_tlv() - extract lobal link info from shmem
346 * @data: Pointer to the first TLV in the arena
347 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer
348 * @link_info: Pointer to which link info needs to be copied
349 *
350 * Return: On success, the number of bytes parsed. On failure, -1.
351 */
352 static int
extract_mlo_glb_link_info_tlv(uint8_t * data,size_t remaining_len,uint32_t * link_info)353 extract_mlo_glb_link_info_tlv(uint8_t *data,
354 size_t remaining_len,
355 uint32_t *link_info)
356 {
357 mlo_glb_link_info *ptlv;
358 uint32_t tlv_len, tlv_tag;
359
360 qdf_assert_always(data);
361 qdf_assert_always(link_info);
362
363 if (process_tlv_header(data, remaining_len,
364 MLO_SHMEM_TLV_STRUCT_MLO_GLB_LINK_INFO,
365 &tlv_len, &tlv_tag) != 0) {
366 return -EPERM;
367 }
368
369 ptlv = (mlo_glb_link_info *)data;
370
371 *link_info = get_field_value_in_tlv(ptlv, link_info, tlv_len);
372
373 return tlv_len;
374 }
375
376 /**
377 * process_mlo_glb_per_link_status_tlv() - process per link info
378 * @data: Pointer to the first TLV in the arena
379 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer
380 *
381 * Return: On success, the number of bytes parsed. On failure, -1.
382 */
383 static int
process_mlo_glb_per_link_status_tlv(uint8_t * data,size_t remaining_len)384 process_mlo_glb_per_link_status_tlv(uint8_t *data, size_t remaining_len)
385 {
386 uint32_t tlv_len, tlv_tag;
387
388 qdf_assert_always(data);
389
390 if (process_tlv_header(data, remaining_len,
391 MLO_SHMEM_TLV_STRUCT_MLO_GLB_LINK,
392 &tlv_len, &tlv_tag) != 0) {
393 return -EPERM;
394 }
395
396 return tlv_len;
397 }
398
399 /**
400 * parse_global_link_info() - parse lobal link info
401 * @data: Pointer to the first TLV in the arena
402 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer
403 *
404 * Return: On success, the number of bytes parsed. On failure, -1.
405 */
406 static int
parse_global_link_info(uint8_t * data,size_t remaining_len)407 parse_global_link_info(uint8_t *data, size_t remaining_len)
408 {
409 int parsed_bytes, len;
410 uint8_t link;
411 uint32_t link_info;
412 uint8_t num_links;
413
414 qdf_assert_always(data);
415
416 /* Extract MLO_SHMEM_TLV_STRUCT_MLO_GLB_LINK_INFO_TLV */
417 len = extract_mlo_glb_link_info_tlv(data, remaining_len, &link_info);
418 validate_parsed_bytes_advance_data_pointer(len, data, remaining_len);
419 parsed_bytes = len;
420
421 num_links = MLO_SHMEM_GLB_LINK_INFO_PARAM_NO_OF_LINKS_GET(link_info);
422
423 for (link = 0; link < num_links; link++) {
424 len = process_mlo_glb_per_link_status_tlv(data, remaining_len);
425 validate_parsed_bytes_advance_data_pointer(len, data,
426 remaining_len);
427 parsed_bytes += len;
428 }
429
430 return parsed_bytes;
431 }
432
433 /**
434 * free_mlo_glb_rx_reo_per_link_info() - Free Rx REO per-link info
435 * @snapshot_info: Pointer to MGMT Rx REO snapshot info
436 *
437 * Return: None
438 */
free_mlo_glb_rx_reo_per_link_info(struct wlan_host_mlo_glb_rx_reo_snapshot_info * snapshot_info)439 static void free_mlo_glb_rx_reo_per_link_info(
440 struct wlan_host_mlo_glb_rx_reo_snapshot_info *snapshot_info)
441 {
442 if (snapshot_info && snapshot_info->link_info) {
443 qdf_mem_free(snapshot_info->link_info);
444 snapshot_info->link_info = NULL;
445 }
446 }
447
448 /**
449 * get_next_valid_link_id() - Get next valid link ID
450 * @valid_link_bmap: Link bit map where the valid links are set to 1
451 * @prev_link_id: Previous link ID
452 *
453 * Return: Next valid link ID if there are valid links after @prev_link_id,
454 * else -1
455 */
456 static int
get_next_valid_link_id(uint16_t valid_link_bmap,int prev_link_id)457 get_next_valid_link_id(uint16_t valid_link_bmap, int prev_link_id)
458 {
459 int cur_link_id;
460 uint16_t mask;
461 uint8_t maxbits = sizeof(valid_link_bmap) * QDF_CHAR_BIT;
462
463 cur_link_id = prev_link_id + 1;
464 mask = 1 << cur_link_id;
465
466 while (!(valid_link_bmap & mask)) {
467 cur_link_id++;
468 if (cur_link_id == maxbits)
469 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE);
470 mask = mask << 1;
471 }
472
473 return cur_link_id;
474 }
475
476 /**
477 * extract_mlo_glb_rx_reo_snapshot_info() - extract MGMT Rx REO snapshot info
478 * @data: Pointer to start of MLO_SHMEM_TLV_STRUCT_MLO_GLB_RX_REO_SNAPSHOT_INFO
479 * TLV
480 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer
481 * @snapshot_info: Pointer to MGMT Rx REO snapshot info. Extracted information
482 * will be populated in this data structure.
483 *
484 * Return: On success, the number of bytes parsed. On failure, -1.
485 */
486 static int
extract_mlo_glb_rx_reo_snapshot_info(uint8_t * data,size_t remaining_len,struct wlan_host_mlo_glb_rx_reo_snapshot_info * snapshot_info)487 extract_mlo_glb_rx_reo_snapshot_info(
488 uint8_t *data, size_t remaining_len,
489 struct wlan_host_mlo_glb_rx_reo_snapshot_info *snapshot_info)
490 {
491 int parsed_bytes, len;
492 uint8_t link;
493 int cur_link_id, prev_link_id = -1;
494 uint16_t valid_link_bmap;
495
496 qdf_assert_always(data);
497 qdf_assert_always(snapshot_info);
498
499 /* Extract MLO_SHMEM_TLV_STRUCT_MLO_GLB_RX_REO_SNAPSHOT_INFO TLV */
500 len = extract_mlo_glb_rx_reo_snapshot_info_tlv(data, remaining_len,
501 snapshot_info);
502 validate_parsed_bytes_advance_data_pointer(len, data, remaining_len);
503 parsed_bytes = len;
504
505 valid_link_bmap = snapshot_info->valid_link_bmap;
506 /* Foreach valid link */
507 for (link = 0; link < snapshot_info->num_links; ++link) {
508 cur_link_id = get_next_valid_link_id(valid_link_bmap,
509 prev_link_id);
510
511 qdf_assert_always(cur_link_id >= 0);
512
513 /* Extract per_link_info */
514 len = extract_mlo_glb_rx_reo_per_link_info_tlv(
515 data, remaining_len, cur_link_id,
516 &snapshot_info->link_info[link]);
517 validate_parsed_bytes_advance_data_pointer(len, data,
518 remaining_len);
519 parsed_bytes += len;
520 prev_link_id = cur_link_id;
521 }
522
523 return parsed_bytes;
524 }
525
526 /**
527 * mlo_glb_h_shmem_arena_get_no_of_chips_from_crash_info() - get the number of
528 * chips from crash info
529 * @grp_id: Id of the required MLO Group
530 *
531 * Return: number of chips participating in MLO from crash info shared by target
532 * in case of success, else returns 0
533 */
mlo_glb_h_shmem_arena_get_no_of_chips_from_crash_info(uint8_t grp_id)534 uint8_t mlo_glb_h_shmem_arena_get_no_of_chips_from_crash_info(uint8_t grp_id)
535 {
536 struct wlan_host_mlo_glb_h_shmem_arena_ctx *shmem_arena_ctx;
537
538 if (grp_id >= WLAN_MAX_MLO_GROUPS)
539 return 0;
540
541 shmem_arena_ctx = get_shmem_arena_ctx(grp_id);
542
543 if (!shmem_arena_ctx) {
544 target_if_err("mlo_glb_h_shmem_arena context is NULL");
545 return 0;
546 }
547
548 return shmem_arena_ctx->chip_crash_info.no_of_chips;
549 }
550
551 /**
552 * mlo_glb_h_shmem_arena_get_crash_reason_address() - get the address of crash
553 * reason associated with chip_id
554 * @grp_id: Id of the required MLO Group
555 * @chip_id: MLO Chip Id
556 *
557 * Return: Address of crash_reason field from global shmem arena in case of
558 * success, else returns NULL
559 */
mlo_glb_h_shmem_arena_get_crash_reason_address(uint8_t grp_id,uint8_t chip_id)560 void *mlo_glb_h_shmem_arena_get_crash_reason_address(uint8_t grp_id,
561 uint8_t chip_id)
562 {
563 struct wlan_host_mlo_glb_h_shmem_arena_ctx *shmem_arena_ctx;
564 struct wlan_host_mlo_glb_chip_crash_info *crash_info;
565 struct wlan_host_mlo_glb_per_chip_crash_info *per_chip_crash_info;
566 uint8_t chip;
567
568 if (grp_id > WLAN_MAX_MLO_GROUPS)
569 return NULL;
570
571 shmem_arena_ctx = get_shmem_arena_ctx(grp_id);
572 if (!shmem_arena_ctx) {
573 target_if_err("mlo_glb_h_shmem_arena context is NULL");
574 return NULL;
575 }
576
577 crash_info = &shmem_arena_ctx->chip_crash_info;
578
579 for (chip = 0; chip < crash_info->no_of_chips; chip++) {
580 per_chip_crash_info = &crash_info->per_chip_crash_info[chip];
581
582 if (chip_id == per_chip_crash_info->chip_id)
583 break;
584 }
585
586 if (chip == crash_info->no_of_chips) {
587 target_if_err("No crash info corresponding to chip %u",
588 chip_id);
589 return NULL;
590 }
591
592 return per_chip_crash_info->crash_reason;
593 }
594
595 /**
596 * mlo_glb_h_shmem_arena_get_recovery_mode_address() - get the address of
597 * recovery mode associated with chip_id
598 * @grp_id: Id of the required MLO Group
599 * @chip_id: MLO Chip Id
600 *
601 * Return: Address of recovery mode field from global shmem arena in case of
602 * success, else returns NULL
603 */
mlo_glb_h_shmem_arena_get_recovery_mode_address(uint8_t grp_id,uint8_t chip_id)604 void *mlo_glb_h_shmem_arena_get_recovery_mode_address(uint8_t grp_id,
605 uint8_t chip_id)
606 {
607 struct wlan_host_mlo_glb_h_shmem_arena_ctx *shmem_arena_ctx;
608 struct wlan_host_mlo_glb_chip_crash_info *crash_info;
609 struct wlan_host_mlo_glb_per_chip_crash_info *per_chip_crash_info;
610 uint8_t chip;
611
612 if (grp_id > WLAN_MAX_MLO_GROUPS)
613 return NULL;
614
615 shmem_arena_ctx = get_shmem_arena_ctx(grp_id);
616 if (!shmem_arena_ctx) {
617 target_if_err("mlo_glb_h_shmem_arena context is NULL");
618 return NULL;
619 }
620
621 crash_info = &shmem_arena_ctx->chip_crash_info;
622
623 for (chip = 0; chip < crash_info->no_of_chips; chip++) {
624 per_chip_crash_info = &crash_info->per_chip_crash_info[chip];
625
626 if (chip_id == per_chip_crash_info->chip_id)
627 break;
628 }
629
630 if (chip == crash_info->no_of_chips) {
631 target_if_err("No crash info corresponding to chip %u",
632 chip_id);
633 return NULL;
634 }
635
636 return per_chip_crash_info->recovery_mode;
637 }
638
639 /**
640 * free_mlo_glb_per_chip_crash_info() - free per chip crash info
641 * @crash_info: Pointer to crash info
642 *
643 * Return: None
644 */
free_mlo_glb_per_chip_crash_info(struct wlan_host_mlo_glb_chip_crash_info * crash_info)645 static void free_mlo_glb_per_chip_crash_info(
646 struct wlan_host_mlo_glb_chip_crash_info *crash_info)
647 {
648 if (crash_info) {
649 qdf_mem_free(crash_info->per_chip_crash_info);
650 crash_info->per_chip_crash_info = NULL;
651 }
652 }
653
654 /**
655 * extract_mlo_glb_per_chip_crash_info_tlv() - extract PER_CHIP_CRASH_INFO TLV
656 * @data: Pointer to start of the TLV
657 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer
658 * @chip_id: Chip id to which the crash info tlv being extracted.
659 * @chip_crash_info: Pointer to the per chip crash info. This API will populate
660 * the crash_reason address & chip_id into chip_crash_info
661 */
extract_mlo_glb_per_chip_crash_info_tlv(uint8_t * data,size_t remaining_len,uint8_t chip_id,struct wlan_host_mlo_glb_per_chip_crash_info * chip_crash_info)662 static int extract_mlo_glb_per_chip_crash_info_tlv(
663 uint8_t *data, size_t remaining_len, uint8_t chip_id,
664 struct wlan_host_mlo_glb_per_chip_crash_info *chip_crash_info)
665 {
666 mlo_glb_per_chip_crash_info *ptlv;
667 uint32_t tlv_len, tlv_tag;
668 uint8_t *crash_reason;
669 uint8_t *recovery_mode;
670
671 qdf_assert_always(data);
672 qdf_assert_always(chip_crash_info);
673
674 /* process MLO_SHMEM_TLV_STRUCT_MLO_GLB_PER_CHIP_CRASH_INFO TLV */
675 if (process_tlv_header(data, remaining_len,
676 MLO_SHMEM_TLV_STRUCT_MLO_GLB_PER_CHIP_CRASH_INFO,
677 &tlv_len, &tlv_tag) != 0) {
678 return -EPERM;
679 }
680
681 ptlv = (mlo_glb_per_chip_crash_info *)data;
682
683 chip_crash_info->chip_id = chip_id;
684 crash_reason = (uint8_t *)get_field_pointer_in_tlv(
685 ptlv, crash_reason, tlv_len);
686 recovery_mode = (uint8_t *)get_field_pointer_in_tlv(
687 ptlv, recovery_mode, tlv_len);
688 chip_crash_info->crash_reason = (void *)crash_reason;
689 chip_crash_info->recovery_mode = (void *)recovery_mode;
690 return tlv_len;
691 }
692
693 /**
694 * extract_mlo_glb_chip_crash_info_tlv() - extract CHIP_CRASH_INFO TLV
695 * @data: Pointer to start of the TLV
696 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer
697 * @crash_info: Pointer to the crash_info structure to which crash info fields
698 * are populated.
699 *
700 * Return: On success, the number of bytes parsed. On failure, -1.
701 */
extract_mlo_glb_chip_crash_info_tlv(uint8_t * data,size_t remaining_len,struct wlan_host_mlo_glb_chip_crash_info * crash_info)702 static int extract_mlo_glb_chip_crash_info_tlv(
703 uint8_t *data, size_t remaining_len,
704 struct wlan_host_mlo_glb_chip_crash_info *crash_info)
705 {
706 mlo_glb_chip_crash_info *ptlv;
707 uint32_t tlv_len, tlv_tag;
708 uint32_t chip_info;
709 uint8_t chip_map;
710
711 qdf_assert_always(data);
712 qdf_assert_always(crash_info);
713
714 if (process_tlv_header(data, remaining_len,
715 MLO_SHMEM_TLV_STRUCT_MLO_GLB_CHIP_CRASH_INFO,
716 &tlv_len, &tlv_tag) != 0) {
717 return -EPERM;
718 }
719
720 ptlv = (mlo_glb_chip_crash_info *)data;
721 chip_info = get_field_value_in_tlv(ptlv, chip_info, tlv_len);
722 crash_info->no_of_chips =
723 MLO_SHMEM_CHIP_CRASH_INFO_PARAM_NO_OF_CHIPS_GET(chip_info);
724 chip_map =
725 MLO_SHMEM_CHIP_CRASH_INFO_PARAM_VALID_CHIP_BMAP_GET(chip_info);
726 qdf_mem_copy(crash_info->valid_chip_bmap,
727 &chip_map,
728 qdf_min(sizeof(crash_info->valid_chip_bmap),
729 sizeof(chip_map)));
730
731 crash_info->per_chip_crash_info =
732 qdf_mem_malloc(sizeof(*crash_info->per_chip_crash_info) *
733 crash_info->no_of_chips);
734
735 if (!crash_info->per_chip_crash_info) {
736 target_if_err("Couldn't allocate memory for crash info");
737 return -EPERM;
738 }
739
740 return tlv_len;
741 }
742
743 /**
744 * extract_mlo_glb_chip_crash_info() - extract the crash information from global
745 * shmem arena
746 * @data: Pointer to start of the TLV
747 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer
748 * @crash_info: Pointer to the crash_info structure to which crash info fields
749 * are populated.
750 *
751 * Return: On success, the number of bytes parsed. On failure, -1.
752 */
extract_mlo_glb_chip_crash_info(uint8_t * data,size_t remaining_len,struct wlan_host_mlo_glb_chip_crash_info * crash_info)753 static int extract_mlo_glb_chip_crash_info(
754 uint8_t *data, size_t remaining_len,
755 struct wlan_host_mlo_glb_chip_crash_info *crash_info)
756 {
757 int parsed_bytes, len;
758 int cur_chip_id;
759 qdf_bitmap(valid_chip_bmap, QDF_CHAR_BIT);
760 uint8_t chip;
761
762 qdf_assert_always(data);
763 qdf_assert_always(crash_info);
764
765 /* Extract MLO_SHMEM_TLV_STRUCT_MLO_GLB_CHIP_CRASH_INFO_TLV */
766 len = extract_mlo_glb_chip_crash_info_tlv(
767 data, remaining_len, crash_info);
768 validate_parsed_bytes_advance_data_pointer(len, data, remaining_len);
769
770 parsed_bytes = len;
771 qdf_mem_copy(valid_chip_bmap,
772 crash_info->valid_chip_bmap,
773 qdf_min(sizeof(valid_chip_bmap),
774 sizeof(crash_info->valid_chip_bmap)));
775 /* Foreach valid chip_id */
776 for (chip = 0; chip < crash_info->no_of_chips; chip++) {
777 cur_chip_id = qdf_find_first_bit(valid_chip_bmap, QDF_CHAR_BIT);
778 qdf_clear_bit(cur_chip_id, valid_chip_bmap);
779 qdf_assert_always(cur_chip_id >= 0);
780 /* Extract per_chip_crash_info */
781 len = extract_mlo_glb_per_chip_crash_info_tlv(
782 data, remaining_len, cur_chip_id,
783 &crash_info->per_chip_crash_info[chip]);
784 validate_parsed_bytes_advance_data_pointer(
785 len, data, remaining_len);
786 parsed_bytes += len;
787 }
788 return parsed_bytes;
789 }
790
791 /**
792 * extract_mlo_glb_h_shmem_tlv() - extract MLO_SHMEM_TLV_STRUCT_MLO_GLB_H_SHMEM
793 * TLV
794 * @data: Pointer to start of the TLV
795 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer
796 * @shmem_params: Pointer to MLO Global shared memory parameters. Extracted
797 * information will be populated in this data structure.
798 *
799 * Return: On success, the number of bytes parsed. On failure, -1.
800 */
801 static int
extract_mlo_glb_h_shmem_tlv(uint8_t * data,size_t remaining_len,struct wlan_host_mlo_glb_h_shmem_params * shmem_params)802 extract_mlo_glb_h_shmem_tlv(
803 uint8_t *data, size_t remaining_len,
804 struct wlan_host_mlo_glb_h_shmem_params *shmem_params)
805 {
806 mlo_glb_h_shmem *ptlv;
807 uint32_t tlv_len, tlv_tag;
808 uint32_t major_minor_version;
809
810 qdf_assert_always(data);
811 qdf_assert_always(shmem_params);
812 if (process_tlv_header(data, remaining_len,
813 MLO_SHMEM_TLV_STRUCT_MLO_GLB_H_SHMEM,
814 &tlv_len, &tlv_tag) != 0) {
815 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE);
816 }
817
818 ptlv = (mlo_glb_h_shmem *)data;
819 major_minor_version = get_field_value_in_tlv(ptlv, major_minor_version,
820 tlv_len);
821 shmem_params->major_version =
822 MLO_SHMEM_GLB_H_SHMEM_PARAM_MAJOR_VERSION_GET(
823 major_minor_version);
824 shmem_params->minor_version =
825 MLO_SHMEM_GLB_H_SHMEM_PARAM_MINOR_VERSION_GET(
826 major_minor_version);
827
828 return tlv_len;
829 }
830
831 /**
832 * parse_mlo_glb_h_shmem_arena() - Parse MLO Global shared memory arena
833 * @data: Pointer to the first TLV in the arena
834 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer
835 * @shmem_arena_ctx: Pointer to MLO Global shared memory arena context.
836 * Extracted information will be populated in this data structure.
837 *
838 * Return: On success, the number of bytes parsed. On failure, -1.
839 */
parse_mlo_glb_h_shmem_arena(uint8_t * data,size_t remaining_len,struct wlan_host_mlo_glb_h_shmem_arena_ctx * shmem_arena_ctx)840 static int parse_mlo_glb_h_shmem_arena(
841 uint8_t *data, size_t remaining_len,
842 struct wlan_host_mlo_glb_h_shmem_arena_ctx *shmem_arena_ctx)
843 {
844 int parsed_bytes;
845 int len;
846
847 qdf_assert_always(data);
848 qdf_assert_always(shmem_arena_ctx);
849
850 len = extract_mlo_glb_h_shmem_tlv(data, remaining_len,
851 &shmem_arena_ctx->shmem_params);
852 validate_parsed_bytes_advance_data_pointer(len, data, remaining_len);
853 parsed_bytes = len;
854
855 len = extract_mlo_glb_rx_reo_snapshot_info(
856 data, remaining_len, &shmem_arena_ctx->rx_reo_snapshot_info);
857 validate_parsed_bytes_advance_data_pointer(len, data, remaining_len);
858 parsed_bytes += len;
859
860 len = parse_global_link_info(data, remaining_len);
861 validate_parsed_bytes_advance_data_pointer(len, data, remaining_len);
862 parsed_bytes += len;
863
864 len = extract_mlo_glb_chip_crash_info(
865 data, remaining_len, &shmem_arena_ctx->chip_crash_info);
866 validate_parsed_bytes_advance_data_pointer(len, data, remaining_len);
867 parsed_bytes += len;
868
869 return parsed_bytes;
870 }
871
mlo_glb_h_shmem_arena_ctx_init(void * arena_vaddr,size_t arena_len,uint8_t grp_id)872 QDF_STATUS mlo_glb_h_shmem_arena_ctx_init(void *arena_vaddr,
873 size_t arena_len,
874 uint8_t grp_id)
875 {
876 struct wlan_host_mlo_glb_h_shmem_arena_ctx *shmem_arena_ctx;
877
878 if (grp_id > WLAN_MAX_MLO_GROUPS)
879 return QDF_STATUS_E_INVAL;
880
881 shmem_arena_ctx = get_shmem_arena_ctx(grp_id);
882 if (!shmem_arena_ctx) {
883 target_if_err("mlo_glb_h_shmem_arena context is NULL");
884 return QDF_STATUS_E_NULL_VALUE;
885 }
886
887 /* We need to initialize only for the first invocation */
888 if (qdf_atomic_read(&shmem_arena_ctx->init_count))
889 goto success;
890
891 if (parse_mlo_glb_h_shmem_arena(arena_vaddr, arena_len,
892 shmem_arena_ctx) < 0) {
893 free_mlo_glb_rx_reo_per_link_info(
894 &shmem_arena_ctx->rx_reo_snapshot_info);
895 free_mlo_glb_per_chip_crash_info(
896 &shmem_arena_ctx->chip_crash_info);
897 return QDF_STATUS_E_FAILURE;
898 }
899
900 success:
901 qdf_atomic_inc(&shmem_arena_ctx->init_count);
902 return QDF_STATUS_SUCCESS;
903 }
904
905 qdf_export_symbol(mlo_glb_h_shmem_arena_ctx_init);
906
mlo_glb_h_shmem_arena_ctx_deinit(uint8_t grp_id)907 QDF_STATUS mlo_glb_h_shmem_arena_ctx_deinit(uint8_t grp_id)
908 {
909 struct wlan_host_mlo_glb_h_shmem_arena_ctx *shmem_arena_ctx;
910
911 if (grp_id > WLAN_MAX_MLO_GROUPS)
912 return QDF_STATUS_E_INVAL;
913
914 shmem_arena_ctx = get_shmem_arena_ctx(grp_id);
915 if (!shmem_arena_ctx) {
916 target_if_err("mlo_glb_h_shmem_arena context is NULL");
917 return QDF_STATUS_E_NULL_VALUE;
918 }
919
920 if (!qdf_atomic_read(&shmem_arena_ctx->init_count)) {
921 target_if_fatal("shmem_arena_ctx ref cnt is 0");
922 return QDF_STATUS_E_FAILURE;
923 }
924
925 /* We need to de-initialize only for the last invocation */
926 if (!qdf_atomic_dec_and_test(&shmem_arena_ctx->init_count))
927 goto success;
928
929 free_mlo_glb_rx_reo_per_link_info(
930 &shmem_arena_ctx->rx_reo_snapshot_info);
931 free_mlo_glb_per_chip_crash_info(
932 &shmem_arena_ctx->chip_crash_info);
933
934 success:
935 return QDF_STATUS_SUCCESS;
936 }
937
938 qdf_export_symbol(mlo_glb_h_shmem_arena_ctx_deinit);
939
940 #ifdef WLAN_MGMT_RX_REO_SUPPORT
mgmt_rx_reo_get_valid_link_bitmap(uint8_t grp_id)941 uint16_t mgmt_rx_reo_get_valid_link_bitmap(uint8_t grp_id)
942 {
943 struct wlan_host_mlo_glb_h_shmem_arena_ctx *shmem_arena_ctx;
944
945 if (grp_id >= WLAN_MAX_MLO_GROUPS)
946 return 0;
947
948 shmem_arena_ctx = get_shmem_arena_ctx(grp_id);
949 if (!shmem_arena_ctx) {
950 target_if_err("mlo_glb_h_shmem_arena context is NULL");
951 return 0;
952 }
953
954 return shmem_arena_ctx->rx_reo_snapshot_info.valid_link_bmap;
955 }
956
mgmt_rx_reo_get_num_links(uint8_t grp_id)957 int mgmt_rx_reo_get_num_links(uint8_t grp_id)
958 {
959 struct wlan_host_mlo_glb_h_shmem_arena_ctx *shmem_arena_ctx;
960
961 if (grp_id >= WLAN_MAX_MLO_GROUPS)
962 return -EINVAL;
963
964 shmem_arena_ctx = get_shmem_arena_ctx(grp_id);
965 if (!shmem_arena_ctx) {
966 target_if_err("mlo_glb_h_shmem_arena context is NULL");
967 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE);
968 }
969
970 return shmem_arena_ctx->rx_reo_snapshot_info.num_links;
971 }
972
mgmt_rx_reo_get_snapshot_address(uint8_t grp_id,uint8_t link_id,enum mgmt_rx_reo_shared_snapshot_id snapshot_id)973 void *mgmt_rx_reo_get_snapshot_address(
974 uint8_t grp_id,
975 uint8_t link_id,
976 enum mgmt_rx_reo_shared_snapshot_id snapshot_id)
977 {
978 struct wlan_host_mlo_glb_h_shmem_arena_ctx *shmem_arena_ctx;
979 struct wlan_host_mlo_glb_rx_reo_snapshot_info *snapshot_info;
980 struct wlan_host_mlo_glb_rx_reo_per_link_info *snapshot_link_info;
981 uint8_t link;
982
983 if (snapshot_id >= MGMT_RX_REO_SHARED_SNAPSHOT_MAX) {
984 target_if_err("Invalid snapshot ID: %d", snapshot_id);
985 return NULL;
986 }
987
988 if (grp_id > WLAN_MAX_MLO_GROUPS)
989 return NULL;
990
991 shmem_arena_ctx = get_shmem_arena_ctx(grp_id);
992 if (!shmem_arena_ctx) {
993 target_if_err("mlo_glb_h_shmem_arena context is NULL");
994 return NULL;
995 }
996
997 snapshot_info = &shmem_arena_ctx->rx_reo_snapshot_info;
998
999 for (link = 0; link < snapshot_info->num_links; ++link) {
1000 snapshot_link_info = &snapshot_info->link_info[link];
1001
1002 if (link_id == snapshot_link_info->link_id)
1003 break;
1004 }
1005
1006 if (link == snapshot_info->num_links) {
1007 target_if_err("Couldn't find the snapshot link info"
1008 "corresponding to the link %d", link_id);
1009 return NULL;
1010 }
1011
1012 switch (snapshot_id) {
1013 case MGMT_RX_REO_SHARED_SNAPSHOT_MAC_HW:
1014 return snapshot_link_info->hw_forwarded;
1015
1016 case MGMT_RX_REO_SHARED_SNAPSHOT_FW_CONSUMED:
1017 return snapshot_link_info->fw_consumed;
1018
1019 case MGMT_RX_REO_SHARED_SNAPSHOT_FW_FORWARDED:
1020 return snapshot_link_info->fw_forwarded;
1021
1022 default:
1023 qdf_assert_always(0);
1024 }
1025
1026 return NULL;
1027 }
1028
mgmt_rx_reo_get_snapshot_version(uint8_t grp_id,enum mgmt_rx_reo_shared_snapshot_id id)1029 int8_t mgmt_rx_reo_get_snapshot_version(uint8_t grp_id,
1030 enum mgmt_rx_reo_shared_snapshot_id id)
1031 {
1032 struct wlan_host_mlo_glb_h_shmem_arena_ctx *shmem_arena_ctx;
1033 struct wlan_host_mlo_glb_rx_reo_snapshot_info *snapshot_info;
1034 int8_t snapshot_version;
1035
1036 if (id >= MGMT_RX_REO_SHARED_SNAPSHOT_MAX) {
1037 target_if_err("Invalid snapshot ID: %d", id);
1038 return MGMT_RX_REO_INVALID_SNAPSHOT_VERSION;
1039 }
1040
1041 if (grp_id > WLAN_MAX_MLO_GROUPS)
1042 return MGMT_RX_REO_INVALID_SNAPSHOT_VERSION;
1043
1044 shmem_arena_ctx = get_shmem_arena_ctx(grp_id);
1045 if (!shmem_arena_ctx) {
1046 target_if_err("mlo_glb_h_shmem_arena context is NULL");
1047 return MGMT_RX_REO_INVALID_SNAPSHOT_VERSION;
1048 }
1049
1050 snapshot_info = &shmem_arena_ctx->rx_reo_snapshot_info;
1051
1052 switch (id) {
1053 case MGMT_RX_REO_SHARED_SNAPSHOT_MAC_HW:
1054 snapshot_version = snapshot_info->hw_forwarded_snapshot_ver;
1055 break;
1056
1057 case MGMT_RX_REO_SHARED_SNAPSHOT_FW_CONSUMED:
1058 snapshot_version = snapshot_info->fw_consumed_snapshot_ver;
1059 break;
1060
1061 case MGMT_RX_REO_SHARED_SNAPSHOT_FW_FORWARDED:
1062 snapshot_version = snapshot_info->fw_forwarded_snapshot_ver;
1063 break;
1064
1065 default:
1066 snapshot_version = MGMT_RX_REO_INVALID_SNAPSHOT_VERSION;
1067 break;
1068 }
1069
1070 return snapshot_version;
1071 }
1072 #endif /* WLAN_MGMT_RX_REO_SUPPORT */
1073