xref: /wlan-driver/qca-wifi-host-cmn/target_if/init_deinit/src/mlo_global_h_shmem_arena.c (revision 5113495b16420b49004c444715d2daae2066e7dc)
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