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