1*5113495bSYour Name /*
2*5113495bSYour Name * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved.
3*5113495bSYour Name * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4*5113495bSYour Name *
5*5113495bSYour Name * Permission to use, copy, modify, and/or distribute this software for
6*5113495bSYour Name * any purpose with or without fee is hereby granted, provided that the
7*5113495bSYour Name * above copyright notice and this permission notice appear in all
8*5113495bSYour Name * copies.
9*5113495bSYour Name *
10*5113495bSYour Name * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11*5113495bSYour Name * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12*5113495bSYour Name * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13*5113495bSYour Name * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14*5113495bSYour Name * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15*5113495bSYour Name * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16*5113495bSYour Name * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17*5113495bSYour Name * PERFORMANCE OF THIS SOFTWARE.
18*5113495bSYour Name */
19*5113495bSYour Name
20*5113495bSYour Name #include "wmi_tlv_platform.c"
21*5113495bSYour Name #include "wmi_tlv_defs.h"
22*5113495bSYour Name #include "wmi_version.h"
23*5113495bSYour Name #include "qdf_module.h"
24*5113495bSYour Name
25*5113495bSYour Name #define WMITLV_GET_ATTRIB_NUM_TLVS 0xFFFFFFFF
26*5113495bSYour Name
27*5113495bSYour Name #define WMITLV_GET_CMDID(val) (val & 0x00FFFFFF)
28*5113495bSYour Name #define WMITLV_GET_NUM_TLVS(val) ((val >> 24) & 0xFF)
29*5113495bSYour Name
30*5113495bSYour Name #define WMITLV_GET_TAGID(val) (val & 0x00000FFF)
31*5113495bSYour Name #define WMITLV_GET_TAG_STRUCT_SIZE(val) ((val >> 12) & 0x000001FF)
32*5113495bSYour Name #define WMITLV_GET_TAG_ARRAY_SIZE(val) ((val >> 21) & 0x000001FF)
33*5113495bSYour Name #define WMITLV_GET_TAG_VARIED(val) ((val >> 30) & 0x00000001)
34*5113495bSYour Name
35*5113495bSYour Name #define WMITLV_SET_ATTRB0(id) ((WMITLV_GET_TAG_NUM_TLV_ATTRIB(id) << 24) | \
36*5113495bSYour Name (id & 0x00FFFFFF))
37*5113495bSYour Name #define WMITLV_SET_ATTRB1(tagID, tagStructSize, tagArraySize, tagVaried) \
38*5113495bSYour Name (((tagVaried&0x1)<<30) | ((tagArraySize&0x1FF)<<21) | \
39*5113495bSYour Name ((tagStructSize&0x1FF)<<12) | (tagID&0xFFF))
40*5113495bSYour Name
41*5113495bSYour Name #define WMITLV_OP_SET_TLV_ATTRIB_macro(param_ptr, param_len, wmi_cmd_event_id, \
42*5113495bSYour Name elem_tlv_tag, elem_struc_type, elem_name, var_len, arr_size) \
43*5113495bSYour Name WMITLV_SET_ATTRB1(elem_tlv_tag, sizeof(elem_struc_type), arr_size, var_len),
44*5113495bSYour Name
45*5113495bSYour Name #define WMITLV_GET_CMD_EVT_ATTRB_LIST(id) \
46*5113495bSYour Name WMITLV_SET_ATTRB0(id), \
47*5113495bSYour Name WMITLV_TABLE(id,SET_TLV_ATTRIB, NULL, 0)
48*5113495bSYour Name
49*5113495bSYour Name uint32_t cmd_attr_list[] = {
50*5113495bSYour Name WMITLV_ALL_CMD_LIST(WMITLV_GET_CMD_EVT_ATTRB_LIST)
51*5113495bSYour Name };
52*5113495bSYour Name
53*5113495bSYour Name uint32_t evt_attr_list[] = {
54*5113495bSYour Name WMITLV_ALL_EVT_LIST(WMITLV_GET_CMD_EVT_ATTRB_LIST)
55*5113495bSYour Name };
56*5113495bSYour Name
57*5113495bSYour Name #ifdef NO_DYNAMIC_MEM_ALLOC
58*5113495bSYour Name static wmitlv_cmd_param_info *g_wmi_static_cmd_param_info_buf;
59*5113495bSYour Name uint32_t g_wmi_static_max_cmd_param_tlvs;
60*5113495bSYour Name #endif
61*5113495bSYour Name
62*5113495bSYour Name
63*5113495bSYour Name /**
64*5113495bSYour Name * wmitlv_set_static_param_tlv_buf() - tlv helper function
65*5113495bSYour Name * @param_tlv_buf: tlv buffer parameter
66*5113495bSYour Name * @max_tlvs_accommodated: max no of tlv entries
67*5113495bSYour Name *
68*5113495bSYour Name *
69*5113495bSYour Name * WMI TLV Helper function to set the static cmd_param_tlv structure
70*5113495bSYour Name * and number of TLVs that can be accommodated in the structure.
71*5113495bSYour Name * This function should be used when dynamic memory allocation is not
72*5113495bSYour Name * supported. When dynamic memory allocation is not supported by any
73*5113495bSYour Name * component then NO_DYNAMIC_MEMALLOC macro has to be defined in respective
74*5113495bSYour Name * tlv_platform.c file. And respective component has to allocate
75*5113495bSYour Name * cmd_param_tlv structure buffer to accommodate whatever number of TLV's.
76*5113495bSYour Name * Both the buffer address and number of TLV's that can be accommodated in
77*5113495bSYour Name * the buffer should be sent as arguments to this function.
78*5113495bSYour Name *
79*5113495bSYour Name * Return None
80*5113495bSYour Name */
81*5113495bSYour Name void
wmitlv_set_static_param_tlv_buf(void * param_tlv_buf,uint32_t max_tlvs_accommodated)82*5113495bSYour Name wmitlv_set_static_param_tlv_buf(void *param_tlv_buf,
83*5113495bSYour Name uint32_t max_tlvs_accommodated)
84*5113495bSYour Name {
85*5113495bSYour Name #ifdef NO_DYNAMIC_MEM_ALLOC
86*5113495bSYour Name g_wmi_static_cmd_param_info_buf = param_tlv_buf;
87*5113495bSYour Name g_wmi_static_max_cmd_param_tlvs = max_tlvs_accommodated;
88*5113495bSYour Name #endif
89*5113495bSYour Name }
90*5113495bSYour Name
91*5113495bSYour Name /**
92*5113495bSYour Name * wmitlv_get_attributes() - tlv helper function
93*5113495bSYour Name * @is_cmd_id: boolean for command attribute
94*5113495bSYour Name * @cmd_event_id: command event id
95*5113495bSYour Name * @curr_tlv_order: tlv order
96*5113495bSYour Name * @tlv_attr_ptr: pointer to tlv attribute
97*5113495bSYour Name *
98*5113495bSYour Name *
99*5113495bSYour Name * WMI TLV Helper functions to find the attributes of the
100*5113495bSYour Name * Command/Event TLVs.
101*5113495bSYour Name *
102*5113495bSYour Name * Return: 0 if success. Return >=1 if failure.
103*5113495bSYour Name */
104*5113495bSYour Name static
wmitlv_get_attributes(uint32_t is_cmd_id,uint32_t cmd_event_id,uint32_t curr_tlv_order,wmitlv_attributes_struc * tlv_attr_ptr)105*5113495bSYour Name uint32_t wmitlv_get_attributes(uint32_t is_cmd_id, uint32_t cmd_event_id,
106*5113495bSYour Name uint32_t curr_tlv_order,
107*5113495bSYour Name wmitlv_attributes_struc *tlv_attr_ptr)
108*5113495bSYour Name {
109*5113495bSYour Name uint32_t i, base_index, num_tlvs, num_entries;
110*5113495bSYour Name uint32_t *pAttrArrayList;
111*5113495bSYour Name
112*5113495bSYour Name if (is_cmd_id) {
113*5113495bSYour Name pAttrArrayList = &cmd_attr_list[0];
114*5113495bSYour Name num_entries = QDF_ARRAY_SIZE(cmd_attr_list);
115*5113495bSYour Name } else {
116*5113495bSYour Name pAttrArrayList = &evt_attr_list[0];
117*5113495bSYour Name num_entries = QDF_ARRAY_SIZE(evt_attr_list);
118*5113495bSYour Name }
119*5113495bSYour Name
120*5113495bSYour Name for (i = 0; i < num_entries; i++) {
121*5113495bSYour Name num_tlvs = WMITLV_GET_NUM_TLVS(pAttrArrayList[i]);
122*5113495bSYour Name if (WMITLV_GET_CMDID(cmd_event_id) ==
123*5113495bSYour Name WMITLV_GET_CMDID(pAttrArrayList[i])) {
124*5113495bSYour Name tlv_attr_ptr->cmd_num_tlv = num_tlvs;
125*5113495bSYour Name /* Return success from here when only number of TLVS for
126*5113495bSYour Name * this command/event is required */
127*5113495bSYour Name if (curr_tlv_order == WMITLV_GET_ATTRIB_NUM_TLVS) {
128*5113495bSYour Name wmi_tlv_print_verbose
129*5113495bSYour Name ("%s: WMI TLV attribute definitions for %s:0x%x found; num_of_tlvs:%d\n",
130*5113495bSYour Name __func__, (is_cmd_id ? "Cmd" : "Evt"),
131*5113495bSYour Name cmd_event_id, num_tlvs);
132*5113495bSYour Name return 0;
133*5113495bSYour Name }
134*5113495bSYour Name
135*5113495bSYour Name /* Return failure if tlv_order is more than the expected
136*5113495bSYour Name * number of TLVs */
137*5113495bSYour Name if (curr_tlv_order >= num_tlvs) {
138*5113495bSYour Name wmi_tlv_print_error
139*5113495bSYour Name ("%s: ERROR: TLV order %d greater than num_of_tlvs:%d for %s:0x%x\n",
140*5113495bSYour Name __func__, curr_tlv_order, num_tlvs,
141*5113495bSYour Name (is_cmd_id ? "Cmd" : "Evt"), cmd_event_id);
142*5113495bSYour Name return 1;
143*5113495bSYour Name }
144*5113495bSYour Name
145*5113495bSYour Name base_index = i + 1; /* index to first TLV attributes */
146*5113495bSYour Name wmi_tlv_print_verbose
147*5113495bSYour Name ("%s: WMI TLV attributes for %s:0x%x tlv[%d]:0x%x\n",
148*5113495bSYour Name __func__, (is_cmd_id ? "Cmd" : "Evt"),
149*5113495bSYour Name cmd_event_id, curr_tlv_order,
150*5113495bSYour Name pAttrArrayList[(base_index + curr_tlv_order)]);
151*5113495bSYour Name tlv_attr_ptr->tag_order = curr_tlv_order;
152*5113495bSYour Name tlv_attr_ptr->tag_id =
153*5113495bSYour Name WMITLV_GET_TAGID(pAttrArrayList
154*5113495bSYour Name [(base_index + curr_tlv_order)]);
155*5113495bSYour Name tlv_attr_ptr->tag_struct_size =
156*5113495bSYour Name WMITLV_GET_TAG_STRUCT_SIZE(pAttrArrayList
157*5113495bSYour Name [(base_index +
158*5113495bSYour Name curr_tlv_order)]);
159*5113495bSYour Name tlv_attr_ptr->tag_varied_size =
160*5113495bSYour Name WMITLV_GET_TAG_VARIED(pAttrArrayList
161*5113495bSYour Name [(base_index +
162*5113495bSYour Name curr_tlv_order)]);
163*5113495bSYour Name tlv_attr_ptr->tag_array_size =
164*5113495bSYour Name WMITLV_GET_TAG_ARRAY_SIZE(pAttrArrayList
165*5113495bSYour Name [(base_index +
166*5113495bSYour Name curr_tlv_order)]);
167*5113495bSYour Name return 0;
168*5113495bSYour Name }
169*5113495bSYour Name i += num_tlvs;
170*5113495bSYour Name }
171*5113495bSYour Name
172*5113495bSYour Name wmi_tlv_print_error
173*5113495bSYour Name ("%s: ERROR: Didn't found WMI TLV attribute definitions for %s:0x%x\n",
174*5113495bSYour Name __func__, (is_cmd_id ? "Cmd" : "Evt"), cmd_event_id);
175*5113495bSYour Name return 1;
176*5113495bSYour Name }
177*5113495bSYour Name
178*5113495bSYour Name /**
179*5113495bSYour Name * wmitlv_check_tlv_params() - tlv helper function
180*5113495bSYour Name * @os_handle: os context handle
181*5113495bSYour Name * @param_struc_ptr: pointer to tlv structure
182*5113495bSYour Name * @param_buf_len: length of input buffer
183*5113495bSYour Name * @is_cmd_id: boolean for command attribute
184*5113495bSYour Name * @wmi_cmd_event_id: command event id
185*5113495bSYour Name *
186*5113495bSYour Name *
187*5113495bSYour Name * Helper Function to validate the prepared TLV's for
188*5113495bSYour Name * an WMI event/command to be sent.
189*5113495bSYour Name *
190*5113495bSYour Name * Return: 0 if success. Return < 0 if failure.
191*5113495bSYour Name */
192*5113495bSYour Name static int
wmitlv_check_tlv_params(void * os_handle,void * param_struc_ptr,uint32_t param_buf_len,uint32_t is_cmd_id,uint32_t wmi_cmd_event_id)193*5113495bSYour Name wmitlv_check_tlv_params(void *os_handle, void *param_struc_ptr,
194*5113495bSYour Name uint32_t param_buf_len, uint32_t is_cmd_id,
195*5113495bSYour Name uint32_t wmi_cmd_event_id)
196*5113495bSYour Name {
197*5113495bSYour Name wmitlv_attributes_struc attr_struct_ptr;
198*5113495bSYour Name uint32_t buf_idx = 0;
199*5113495bSYour Name uint32_t tlv_index = 0;
200*5113495bSYour Name uint8_t *buf_ptr = (unsigned char *)param_struc_ptr;
201*5113495bSYour Name uint32_t expected_num_tlvs, expected_tlv_len;
202*5113495bSYour Name int32_t error = -1;
203*5113495bSYour Name
204*5113495bSYour Name /* Get the number of TLVs for this command/event */
205*5113495bSYour Name if (wmitlv_get_attributes
206*5113495bSYour Name (is_cmd_id, wmi_cmd_event_id, WMITLV_GET_ATTRIB_NUM_TLVS,
207*5113495bSYour Name &attr_struct_ptr) != 0) {
208*5113495bSYour Name wmi_tlv_print_error
209*5113495bSYour Name ("%s: ERROR: Couldn't get expected number of TLVs for Cmd=%d\n",
210*5113495bSYour Name __func__, wmi_cmd_event_id);
211*5113495bSYour Name goto Error_wmitlv_check_tlv_params;
212*5113495bSYour Name }
213*5113495bSYour Name
214*5113495bSYour Name /* NOTE: the returned number of TLVs is in "attr_struct_ptr.cmd_num_tlv" */
215*5113495bSYour Name
216*5113495bSYour Name expected_num_tlvs = attr_struct_ptr.cmd_num_tlv;
217*5113495bSYour Name
218*5113495bSYour Name while ((buf_idx + WMI_TLV_HDR_SIZE) <= param_buf_len) {
219*5113495bSYour Name uint32_t curr_tlv_tag =
220*5113495bSYour Name WMITLV_GET_TLVTAG(WMITLV_GET_HDR(buf_ptr));
221*5113495bSYour Name uint32_t curr_tlv_len =
222*5113495bSYour Name WMITLV_GET_TLVLEN(WMITLV_GET_HDR(buf_ptr));
223*5113495bSYour Name
224*5113495bSYour Name if ((buf_idx + WMI_TLV_HDR_SIZE + curr_tlv_len) > param_buf_len) {
225*5113495bSYour Name wmi_tlv_print_error
226*5113495bSYour Name ("%s: ERROR: Invalid TLV length for Cmd=%d Tag_order=%d buf_idx=%d Tag:%d Len:%d TotalLen:%d\n",
227*5113495bSYour Name __func__, wmi_cmd_event_id, tlv_index, buf_idx,
228*5113495bSYour Name curr_tlv_tag, curr_tlv_len, param_buf_len);
229*5113495bSYour Name goto Error_wmitlv_check_tlv_params;
230*5113495bSYour Name }
231*5113495bSYour Name
232*5113495bSYour Name /* Get the attributes of the TLV with the given order in "tlv_index" */
233*5113495bSYour Name wmi_tlv_OS_MEMZERO(&attr_struct_ptr,
234*5113495bSYour Name sizeof(wmitlv_attributes_struc));
235*5113495bSYour Name if (wmitlv_get_attributes
236*5113495bSYour Name (is_cmd_id, wmi_cmd_event_id, tlv_index,
237*5113495bSYour Name &attr_struct_ptr) != 0) {
238*5113495bSYour Name wmi_tlv_print_error
239*5113495bSYour Name ("%s: ERROR: No TLV attributes found for Cmd=%d Tag_order=%d\n",
240*5113495bSYour Name __func__, wmi_cmd_event_id, tlv_index);
241*5113495bSYour Name goto Error_wmitlv_check_tlv_params;
242*5113495bSYour Name }
243*5113495bSYour Name
244*5113495bSYour Name /* Found the TLV that we wanted */
245*5113495bSYour Name wmi_tlv_print_verbose("%s: [tlv %d]: tag=%d, len=%d\n",
246*5113495bSYour Name __func__, tlv_index, curr_tlv_tag,
247*5113495bSYour Name curr_tlv_len);
248*5113495bSYour Name
249*5113495bSYour Name /* Validating Tag ID order */
250*5113495bSYour Name if (curr_tlv_tag != attr_struct_ptr.tag_id) {
251*5113495bSYour Name wmi_tlv_print_error
252*5113495bSYour Name ("%s: ERROR: TLV has wrong tag in order for Cmd=0x%x. Given=%d, Expected=%d.\n",
253*5113495bSYour Name __func__, wmi_cmd_event_id, curr_tlv_tag,
254*5113495bSYour Name attr_struct_ptr.tag_id);
255*5113495bSYour Name goto Error_wmitlv_check_tlv_params;
256*5113495bSYour Name }
257*5113495bSYour Name
258*5113495bSYour Name /* Validate Tag length */
259*5113495bSYour Name /* Array TLVs length checking needs special handling */
260*5113495bSYour Name if ((curr_tlv_tag >= WMITLV_TAG_FIRST_ARRAY_ENUM)
261*5113495bSYour Name && (curr_tlv_tag <= WMITLV_TAG_LAST_ARRAY_ENUM)) {
262*5113495bSYour Name if (attr_struct_ptr.tag_varied_size == WMITLV_SIZE_FIX) {
263*5113495bSYour Name /* Array size can't be invalid for fixed size Array TLV */
264*5113495bSYour Name if (WMITLV_ARR_SIZE_INVALID ==
265*5113495bSYour Name attr_struct_ptr.tag_array_size) {
266*5113495bSYour Name wmi_tlv_print_error
267*5113495bSYour Name ("%s: ERROR: array_size can't be invalid for Array TLV Cmd=0x%x Tag=%d\n",
268*5113495bSYour Name __func__, wmi_cmd_event_id,
269*5113495bSYour Name curr_tlv_tag);
270*5113495bSYour Name goto Error_wmitlv_check_tlv_params;
271*5113495bSYour Name }
272*5113495bSYour Name
273*5113495bSYour Name expected_tlv_len =
274*5113495bSYour Name attr_struct_ptr.tag_array_size *
275*5113495bSYour Name attr_struct_ptr.tag_struct_size;
276*5113495bSYour Name /* Paddding is only required for Byte array Tlvs all other
277*5113495bSYour Name * array tlv's should be aligned to 4 bytes during their
278*5113495bSYour Name * definition */
279*5113495bSYour Name if (WMITLV_TAG_ARRAY_BYTE ==
280*5113495bSYour Name attr_struct_ptr.tag_id) {
281*5113495bSYour Name expected_tlv_len =
282*5113495bSYour Name roundup(expected_tlv_len,
283*5113495bSYour Name sizeof(uint32_t));
284*5113495bSYour Name }
285*5113495bSYour Name
286*5113495bSYour Name if (curr_tlv_len != expected_tlv_len) {
287*5113495bSYour Name wmi_tlv_print_error
288*5113495bSYour Name ("%s: ERROR: TLV has wrong length for Cmd=0x%x. Tag_order=%d Tag=%d, Given_Len:%d Expected_Len=%d.\n",
289*5113495bSYour Name __func__, wmi_cmd_event_id,
290*5113495bSYour Name tlv_index, curr_tlv_tag,
291*5113495bSYour Name curr_tlv_len, expected_tlv_len);
292*5113495bSYour Name goto Error_wmitlv_check_tlv_params;
293*5113495bSYour Name }
294*5113495bSYour Name } else {
295*5113495bSYour Name /* Array size should be invalid for variable size Array TLV */
296*5113495bSYour Name if (WMITLV_ARR_SIZE_INVALID !=
297*5113495bSYour Name attr_struct_ptr.tag_array_size) {
298*5113495bSYour Name wmi_tlv_print_error
299*5113495bSYour Name ("%s: ERROR: array_size should be invalid for Array TLV Cmd=0x%x Tag=%d\n",
300*5113495bSYour Name __func__, wmi_cmd_event_id,
301*5113495bSYour Name curr_tlv_tag);
302*5113495bSYour Name goto Error_wmitlv_check_tlv_params;
303*5113495bSYour Name }
304*5113495bSYour Name
305*5113495bSYour Name /* Incase of variable length TLV's, there is no expectation
306*5113495bSYour Name * on the length field so do whatever checking you can
307*5113495bSYour Name * depending on the TLV tag if TLV length is non-zero */
308*5113495bSYour Name if (curr_tlv_len != 0) {
309*5113495bSYour Name /* Verify TLV length is aligned to the size of structure */
310*5113495bSYour Name if ((curr_tlv_len %
311*5113495bSYour Name attr_struct_ptr.tag_struct_size) !=
312*5113495bSYour Name 0) {
313*5113495bSYour Name wmi_tlv_print_error
314*5113495bSYour Name ("%s: ERROR: TLV length %d for Cmd=0x%x is not aligned to size of structure(%d bytes)\n",
315*5113495bSYour Name __func__, curr_tlv_len,
316*5113495bSYour Name wmi_cmd_event_id,
317*5113495bSYour Name attr_struct_ptr.
318*5113495bSYour Name tag_struct_size);
319*5113495bSYour Name goto Error_wmitlv_check_tlv_params;
320*5113495bSYour Name }
321*5113495bSYour Name
322*5113495bSYour Name if (curr_tlv_tag ==
323*5113495bSYour Name WMITLV_TAG_ARRAY_STRUC) {
324*5113495bSYour Name uint8_t *tlv_buf_ptr = NULL;
325*5113495bSYour Name uint32_t in_tlv_len;
326*5113495bSYour Name uint32_t idx;
327*5113495bSYour Name uint32_t num_of_elems;
328*5113495bSYour Name
329*5113495bSYour Name /* Verify length of inner TLVs */
330*5113495bSYour Name
331*5113495bSYour Name num_of_elems =
332*5113495bSYour Name curr_tlv_len /
333*5113495bSYour Name attr_struct_ptr.
334*5113495bSYour Name tag_struct_size;
335*5113495bSYour Name /* Set tlv_buf_ptr to the first inner TLV address */
336*5113495bSYour Name tlv_buf_ptr =
337*5113495bSYour Name buf_ptr + WMI_TLV_HDR_SIZE;
338*5113495bSYour Name for (idx = 0;
339*5113495bSYour Name idx < num_of_elems;
340*5113495bSYour Name idx++) {
341*5113495bSYour Name in_tlv_len =
342*5113495bSYour Name WMITLV_GET_TLVLEN
343*5113495bSYour Name (WMITLV_GET_HDR
344*5113495bSYour Name (tlv_buf_ptr));
345*5113495bSYour Name if ((in_tlv_len +
346*5113495bSYour Name WMI_TLV_HDR_SIZE)
347*5113495bSYour Name !=
348*5113495bSYour Name attr_struct_ptr.
349*5113495bSYour Name tag_struct_size) {
350*5113495bSYour Name wmi_tlv_print_error
351*5113495bSYour Name ("%s: ERROR: TLV has wrong length for Cmd=0x%x. Tag_order=%d Tag=%d, Given_Len:%zu Expected_Len=%d.\n",
352*5113495bSYour Name __func__,
353*5113495bSYour Name wmi_cmd_event_id,
354*5113495bSYour Name tlv_index,
355*5113495bSYour Name curr_tlv_tag,
356*5113495bSYour Name (in_tlv_len
357*5113495bSYour Name +
358*5113495bSYour Name WMI_TLV_HDR_SIZE),
359*5113495bSYour Name attr_struct_ptr.
360*5113495bSYour Name tag_struct_size);
361*5113495bSYour Name goto Error_wmitlv_check_tlv_params;
362*5113495bSYour Name }
363*5113495bSYour Name tlv_buf_ptr +=
364*5113495bSYour Name in_tlv_len +
365*5113495bSYour Name WMI_TLV_HDR_SIZE;
366*5113495bSYour Name }
367*5113495bSYour Name } else
368*5113495bSYour Name if ((curr_tlv_tag ==
369*5113495bSYour Name WMITLV_TAG_ARRAY_UINT32)
370*5113495bSYour Name || (curr_tlv_tag ==
371*5113495bSYour Name WMITLV_TAG_ARRAY_BYTE)
372*5113495bSYour Name || (curr_tlv_tag ==
373*5113495bSYour Name WMITLV_TAG_ARRAY_FIXED_STRUC)) {
374*5113495bSYour Name /* Nothing to verify here */
375*5113495bSYour Name } else {
376*5113495bSYour Name wmi_tlv_print_error
377*5113495bSYour Name ("%s ERROR Need to handle the Array tlv %d for variable length for Cmd=0x%x\n",
378*5113495bSYour Name __func__,
379*5113495bSYour Name attr_struct_ptr.tag_id,
380*5113495bSYour Name wmi_cmd_event_id);
381*5113495bSYour Name goto Error_wmitlv_check_tlv_params;
382*5113495bSYour Name }
383*5113495bSYour Name }
384*5113495bSYour Name }
385*5113495bSYour Name } else {
386*5113495bSYour Name /* Non-array TLV. */
387*5113495bSYour Name
388*5113495bSYour Name if ((curr_tlv_len + WMI_TLV_HDR_SIZE) !=
389*5113495bSYour Name attr_struct_ptr.tag_struct_size) {
390*5113495bSYour Name wmi_tlv_print_error
391*5113495bSYour Name ("%s: ERROR: TLV has wrong length for Cmd=0x%x. Given=%zu, Expected=%d.\n",
392*5113495bSYour Name __func__, wmi_cmd_event_id,
393*5113495bSYour Name (curr_tlv_len + WMI_TLV_HDR_SIZE),
394*5113495bSYour Name attr_struct_ptr.tag_struct_size);
395*5113495bSYour Name goto Error_wmitlv_check_tlv_params;
396*5113495bSYour Name }
397*5113495bSYour Name }
398*5113495bSYour Name
399*5113495bSYour Name /* Check TLV length is aligned to 4 bytes or not */
400*5113495bSYour Name if ((curr_tlv_len % sizeof(uint32_t)) != 0) {
401*5113495bSYour Name wmi_tlv_print_error
402*5113495bSYour Name ("%s: ERROR: TLV length %d for Cmd=0x%x is not aligned to %zu bytes\n",
403*5113495bSYour Name __func__, curr_tlv_len, wmi_cmd_event_id,
404*5113495bSYour Name sizeof(uint32_t));
405*5113495bSYour Name goto Error_wmitlv_check_tlv_params;
406*5113495bSYour Name }
407*5113495bSYour Name
408*5113495bSYour Name tlv_index++;
409*5113495bSYour Name buf_ptr += curr_tlv_len + WMI_TLV_HDR_SIZE;
410*5113495bSYour Name buf_idx += curr_tlv_len + WMI_TLV_HDR_SIZE;
411*5113495bSYour Name }
412*5113495bSYour Name
413*5113495bSYour Name if (tlv_index != expected_num_tlvs) {
414*5113495bSYour Name wmi_tlv_print_verbose
415*5113495bSYour Name ("%s: INFO: Less number of TLVs filled for Cmd=0x%x Filled %d Expected=%d\n",
416*5113495bSYour Name __func__, wmi_cmd_event_id, tlv_index, expected_num_tlvs);
417*5113495bSYour Name }
418*5113495bSYour Name
419*5113495bSYour Name return 0;
420*5113495bSYour Name Error_wmitlv_check_tlv_params:
421*5113495bSYour Name return error;
422*5113495bSYour Name }
423*5113495bSYour Name
424*5113495bSYour Name /**
425*5113495bSYour Name * wmitlv_check_event_tlv_params() - tlv helper function
426*5113495bSYour Name * @os_handle: os context handle
427*5113495bSYour Name * @param_struc_ptr: pointer to tlv structure
428*5113495bSYour Name * @param_buf_len: length of input buffer
429*5113495bSYour Name * @wmi_cmd_event_id: command event id
430*5113495bSYour Name *
431*5113495bSYour Name *
432*5113495bSYour Name * Helper Function to validate the prepared TLV's for
433*5113495bSYour Name * an WMI event/command to be sent.
434*5113495bSYour Name *
435*5113495bSYour Name * Return: 0 if success. Return < 0 if failure.
436*5113495bSYour Name */
437*5113495bSYour Name int
wmitlv_check_event_tlv_params(void * os_handle,void * param_struc_ptr,uint32_t param_buf_len,uint32_t wmi_cmd_event_id)438*5113495bSYour Name wmitlv_check_event_tlv_params(void *os_handle, void *param_struc_ptr,
439*5113495bSYour Name uint32_t param_buf_len, uint32_t wmi_cmd_event_id)
440*5113495bSYour Name {
441*5113495bSYour Name uint32_t is_cmd_id = 0;
442*5113495bSYour Name
443*5113495bSYour Name return wmitlv_check_tlv_params
444*5113495bSYour Name (os_handle, param_struc_ptr, param_buf_len, is_cmd_id,
445*5113495bSYour Name wmi_cmd_event_id);
446*5113495bSYour Name }
447*5113495bSYour Name
448*5113495bSYour Name /**
449*5113495bSYour Name * wmitlv_check_command_tlv_params() - tlv helper function
450*5113495bSYour Name * @os_handle: os context handle
451*5113495bSYour Name * @param_struc_ptr: pointer to tlv structure
452*5113495bSYour Name * @param_buf_len: length of input buffer
453*5113495bSYour Name * @wmi_cmd_event_id: command event id
454*5113495bSYour Name *
455*5113495bSYour Name *
456*5113495bSYour Name * Helper Function to validate the prepared TLV's for
457*5113495bSYour Name * an WMI event/command to be sent.
458*5113495bSYour Name *
459*5113495bSYour Name * Return: 0 if success. Return < 0 if failure.
460*5113495bSYour Name */
461*5113495bSYour Name int
wmitlv_check_command_tlv_params(void * os_handle,void * param_struc_ptr,uint32_t param_buf_len,uint32_t wmi_cmd_event_id)462*5113495bSYour Name wmitlv_check_command_tlv_params(void *os_handle, void *param_struc_ptr,
463*5113495bSYour Name uint32_t param_buf_len,
464*5113495bSYour Name uint32_t wmi_cmd_event_id)
465*5113495bSYour Name {
466*5113495bSYour Name uint32_t is_cmd_id = 1;
467*5113495bSYour Name
468*5113495bSYour Name return wmitlv_check_tlv_params
469*5113495bSYour Name (os_handle, param_struc_ptr, param_buf_len, is_cmd_id,
470*5113495bSYour Name wmi_cmd_event_id);
471*5113495bSYour Name }
472*5113495bSYour Name qdf_export_symbol(wmitlv_check_command_tlv_params);
473*5113495bSYour Name
474*5113495bSYour Name /**
475*5113495bSYour Name * wmitlv_check_and_pad_tlvs() - tlv helper function
476*5113495bSYour Name * @os_handle: os context handle
477*5113495bSYour Name * @param_buf_len: length of tlv parameter
478*5113495bSYour Name * @param_struc_ptr: pointer to tlv structure
479*5113495bSYour Name * @is_cmd_id: boolean for command attribute
480*5113495bSYour Name * @wmi_cmd_event_id: command event id
481*5113495bSYour Name * @wmi_cmd_struct_ptr: wmi command structure
482*5113495bSYour Name *
483*5113495bSYour Name *
484*5113495bSYour Name * validate the TLV's coming for an event/command and
485*5113495bSYour Name * also pads data to TLV's if necessary
486*5113495bSYour Name *
487*5113495bSYour Name * Return: 0 if success. Return < 0 if failure.
488*5113495bSYour Name */
489*5113495bSYour Name static int
wmitlv_check_and_pad_tlvs(void * os_handle,void * param_struc_ptr,uint32_t param_buf_len,uint32_t is_cmd_id,uint32_t wmi_cmd_event_id,void ** wmi_cmd_struct_ptr)490*5113495bSYour Name wmitlv_check_and_pad_tlvs(void *os_handle, void *param_struc_ptr,
491*5113495bSYour Name uint32_t param_buf_len, uint32_t is_cmd_id,
492*5113495bSYour Name uint32_t wmi_cmd_event_id, void **wmi_cmd_struct_ptr)
493*5113495bSYour Name {
494*5113495bSYour Name wmitlv_attributes_struc attr_struct_ptr;
495*5113495bSYour Name uint32_t buf_idx = 0;
496*5113495bSYour Name uint32_t tlv_index = 0;
497*5113495bSYour Name uint32_t num_of_elems = 0;
498*5113495bSYour Name int tlv_size_diff = 0;
499*5113495bSYour Name uint8_t *buf_ptr = (unsigned char *)param_struc_ptr;
500*5113495bSYour Name wmitlv_cmd_param_info *cmd_param_tlvs_ptr = NULL;
501*5113495bSYour Name uint32_t remaining_expected_tlvs = 0xFFFFFFFF;
502*5113495bSYour Name uint32_t len_wmi_cmd_struct_buf;
503*5113495bSYour Name uint32_t free_buf_len;
504*5113495bSYour Name int32_t error = -1;
505*5113495bSYour Name
506*5113495bSYour Name /* Get the number of TLVs for this command/event */
507*5113495bSYour Name if (wmitlv_get_attributes
508*5113495bSYour Name (is_cmd_id, wmi_cmd_event_id, WMITLV_GET_ATTRIB_NUM_TLVS,
509*5113495bSYour Name &attr_struct_ptr) != 0) {
510*5113495bSYour Name wmi_tlv_print_error
511*5113495bSYour Name ("%s: ERROR: Couldn't get expected number of TLVs for Cmd=%d\n",
512*5113495bSYour Name __func__, wmi_cmd_event_id);
513*5113495bSYour Name return error;
514*5113495bSYour Name }
515*5113495bSYour Name /* NOTE: the returned number of TLVs is in "attr_struct_ptr.cmd_num_tlv" */
516*5113495bSYour Name
517*5113495bSYour Name if (param_buf_len < WMI_TLV_HDR_SIZE) {
518*5113495bSYour Name wmi_tlv_print_error
519*5113495bSYour Name ("%s: ERROR: Incorrect param buf length passed\n",
520*5113495bSYour Name __func__);
521*5113495bSYour Name return error;
522*5113495bSYour Name }
523*5113495bSYour Name
524*5113495bSYour Name /* Create base structure of format wmi_cmd_event_id##_param_tlvs */
525*5113495bSYour Name len_wmi_cmd_struct_buf =
526*5113495bSYour Name attr_struct_ptr.cmd_num_tlv * sizeof(wmitlv_cmd_param_info);
527*5113495bSYour Name #ifndef NO_DYNAMIC_MEM_ALLOC
528*5113495bSYour Name /* Dynamic memory allocation supported */
529*5113495bSYour Name wmi_tlv_os_mem_alloc(os_handle, *wmi_cmd_struct_ptr,
530*5113495bSYour Name len_wmi_cmd_struct_buf);
531*5113495bSYour Name #else
532*5113495bSYour Name /* Dynamic memory allocation is not supported. Use the buffer
533*5113495bSYour Name * g_wmi_static_cmd_param_info_buf, which should be set using
534*5113495bSYour Name * wmi_tlv_set_static_param_tlv_buf(),
535*5113495bSYour Name * for base structure of format wmi_cmd_event_id##_param_tlvs */
536*5113495bSYour Name *wmi_cmd_struct_ptr = g_wmi_static_cmd_param_info_buf;
537*5113495bSYour Name if (attr_struct_ptr.cmd_num_tlv > g_wmi_static_max_cmd_param_tlvs) {
538*5113495bSYour Name /* Error: Expecting more TLVs that accommodated for static structure */
539*5113495bSYour Name wmi_tlv_print_error
540*5113495bSYour Name ("%s: Error: Expecting more TLVs that accommodated for static structure. Expected:%d Accommodated:%d\n",
541*5113495bSYour Name __func__, attr_struct_ptr.cmd_num_tlv,
542*5113495bSYour Name g_wmi_static_max_cmd_param_tlvs);
543*5113495bSYour Name return error;
544*5113495bSYour Name }
545*5113495bSYour Name #endif
546*5113495bSYour Name if (!*wmi_cmd_struct_ptr) {
547*5113495bSYour Name /* Error: unable to alloc memory */
548*5113495bSYour Name wmi_tlv_print_error
549*5113495bSYour Name ("%s: Error: unable to alloc memory (size=%d) for TLV\n",
550*5113495bSYour Name __func__, len_wmi_cmd_struct_buf);
551*5113495bSYour Name return error;
552*5113495bSYour Name }
553*5113495bSYour Name
554*5113495bSYour Name cmd_param_tlvs_ptr = (wmitlv_cmd_param_info *) *wmi_cmd_struct_ptr;
555*5113495bSYour Name wmi_tlv_OS_MEMZERO(cmd_param_tlvs_ptr, len_wmi_cmd_struct_buf);
556*5113495bSYour Name remaining_expected_tlvs = attr_struct_ptr.cmd_num_tlv;
557*5113495bSYour Name
558*5113495bSYour Name while (((buf_idx + WMI_TLV_HDR_SIZE) <= param_buf_len)
559*5113495bSYour Name && (remaining_expected_tlvs)) {
560*5113495bSYour Name uint32_t curr_tlv_tag =
561*5113495bSYour Name WMITLV_GET_TLVTAG(WMITLV_GET_HDR(buf_ptr));
562*5113495bSYour Name uint32_t curr_tlv_len =
563*5113495bSYour Name WMITLV_GET_TLVLEN(WMITLV_GET_HDR(buf_ptr));
564*5113495bSYour Name int num_padding_bytes = 0;
565*5113495bSYour Name
566*5113495bSYour Name free_buf_len = param_buf_len - (buf_idx + WMI_TLV_HDR_SIZE);
567*5113495bSYour Name if (curr_tlv_len > free_buf_len) {
568*5113495bSYour Name wmi_tlv_print_error("%s: TLV length overflow",
569*5113495bSYour Name __func__);
570*5113495bSYour Name goto Error_wmitlv_check_and_pad_tlvs;
571*5113495bSYour Name }
572*5113495bSYour Name
573*5113495bSYour Name /* Get the attributes of the TLV with the given order in "tlv_index" */
574*5113495bSYour Name wmi_tlv_OS_MEMZERO(&attr_struct_ptr,
575*5113495bSYour Name sizeof(wmitlv_attributes_struc));
576*5113495bSYour Name if (wmitlv_get_attributes
577*5113495bSYour Name (is_cmd_id, wmi_cmd_event_id, tlv_index,
578*5113495bSYour Name &attr_struct_ptr) != 0) {
579*5113495bSYour Name wmi_tlv_print_error
580*5113495bSYour Name ("%s: ERROR: No TLV attributes found for Cmd=%d Tag_order=%d\n",
581*5113495bSYour Name __func__, wmi_cmd_event_id, tlv_index);
582*5113495bSYour Name goto Error_wmitlv_check_and_pad_tlvs;
583*5113495bSYour Name }
584*5113495bSYour Name
585*5113495bSYour Name /* Found the TLV that we wanted */
586*5113495bSYour Name wmi_tlv_print_verbose("%s: [tlv %d]: tag=%d, len=%d\n",
587*5113495bSYour Name __func__, tlv_index, curr_tlv_tag,
588*5113495bSYour Name curr_tlv_len);
589*5113495bSYour Name
590*5113495bSYour Name /* Validating Tag order */
591*5113495bSYour Name if (curr_tlv_tag != attr_struct_ptr.tag_id) {
592*5113495bSYour Name wmi_tlv_print_error
593*5113495bSYour Name ("%s: ERROR: TLV has wrong tag in order for Cmd=0x%x. Given=%d, Expected=%d, total_tlv=%d, remaining tlv=%d.\n",
594*5113495bSYour Name __func__, wmi_cmd_event_id, curr_tlv_tag,
595*5113495bSYour Name attr_struct_ptr.tag_id,
596*5113495bSYour Name attr_struct_ptr.cmd_num_tlv,
597*5113495bSYour Name remaining_expected_tlvs);
598*5113495bSYour Name goto Error_wmitlv_check_and_pad_tlvs;
599*5113495bSYour Name }
600*5113495bSYour Name
601*5113495bSYour Name if ((curr_tlv_tag >= WMITLV_TAG_FIRST_ARRAY_ENUM)
602*5113495bSYour Name && (curr_tlv_tag <= WMITLV_TAG_LAST_ARRAY_ENUM)) {
603*5113495bSYour Name /* Current Tag is an array of some kind. */
604*5113495bSYour Name /* Skip the TLV header of this array */
605*5113495bSYour Name buf_ptr += WMI_TLV_HDR_SIZE;
606*5113495bSYour Name buf_idx += WMI_TLV_HDR_SIZE;
607*5113495bSYour Name } else {
608*5113495bSYour Name /* Non-array TLV. */
609*5113495bSYour Name curr_tlv_len += WMI_TLV_HDR_SIZE;
610*5113495bSYour Name }
611*5113495bSYour Name
612*5113495bSYour Name if (attr_struct_ptr.tag_varied_size == WMITLV_SIZE_FIX) {
613*5113495bSYour Name /* This TLV is fixed length */
614*5113495bSYour Name if (WMITLV_ARR_SIZE_INVALID ==
615*5113495bSYour Name attr_struct_ptr.tag_array_size) {
616*5113495bSYour Name tlv_size_diff =
617*5113495bSYour Name curr_tlv_len -
618*5113495bSYour Name attr_struct_ptr.tag_struct_size;
619*5113495bSYour Name num_of_elems =
620*5113495bSYour Name (curr_tlv_len > WMI_TLV_HDR_SIZE) ? 1 : 0;
621*5113495bSYour Name } else {
622*5113495bSYour Name tlv_size_diff =
623*5113495bSYour Name curr_tlv_len -
624*5113495bSYour Name (attr_struct_ptr.tag_struct_size *
625*5113495bSYour Name attr_struct_ptr.tag_array_size);
626*5113495bSYour Name num_of_elems = attr_struct_ptr.tag_array_size;
627*5113495bSYour Name }
628*5113495bSYour Name } else {
629*5113495bSYour Name /* This TLV has a variable number of elements */
630*5113495bSYour Name if (WMITLV_TAG_ARRAY_STRUC == attr_struct_ptr.tag_id) {
631*5113495bSYour Name uint32_t in_tlv_len = 0;
632*5113495bSYour Name
633*5113495bSYour Name if (curr_tlv_len != 0) {
634*5113495bSYour Name in_tlv_len =
635*5113495bSYour Name WMITLV_GET_TLVLEN(WMITLV_GET_HDR
636*5113495bSYour Name (buf_ptr));
637*5113495bSYour Name in_tlv_len += WMI_TLV_HDR_SIZE;
638*5113495bSYour Name if (in_tlv_len > curr_tlv_len) {
639*5113495bSYour Name wmi_tlv_print_error("%s: Invalid in_tlv_len=%d",
640*5113495bSYour Name __func__,
641*5113495bSYour Name in_tlv_len);
642*5113495bSYour Name goto
643*5113495bSYour Name Error_wmitlv_check_and_pad_tlvs;
644*5113495bSYour Name }
645*5113495bSYour Name tlv_size_diff =
646*5113495bSYour Name in_tlv_len -
647*5113495bSYour Name attr_struct_ptr.tag_struct_size;
648*5113495bSYour Name num_of_elems =
649*5113495bSYour Name curr_tlv_len / in_tlv_len;
650*5113495bSYour Name wmi_tlv_print_verbose
651*5113495bSYour Name ("%s: WARN: TLV array of structures in_tlv_len=%d struct_size:%d diff:%d num_of_elems=%d \n",
652*5113495bSYour Name __func__, in_tlv_len,
653*5113495bSYour Name attr_struct_ptr.tag_struct_size,
654*5113495bSYour Name tlv_size_diff, num_of_elems);
655*5113495bSYour Name } else {
656*5113495bSYour Name tlv_size_diff = 0;
657*5113495bSYour Name num_of_elems = 0;
658*5113495bSYour Name }
659*5113495bSYour Name } else
660*5113495bSYour Name if ((WMITLV_TAG_ARRAY_UINT32 ==
661*5113495bSYour Name attr_struct_ptr.tag_id)
662*5113495bSYour Name || (WMITLV_TAG_ARRAY_BYTE ==
663*5113495bSYour Name attr_struct_ptr.tag_id)
664*5113495bSYour Name || (WMITLV_TAG_ARRAY_FIXED_STRUC ==
665*5113495bSYour Name attr_struct_ptr.tag_id) ||
666*5113495bSYour Name (WMITLV_TAG_ARRAY_INT16 ==
667*5113495bSYour Name attr_struct_ptr.tag_id)) {
668*5113495bSYour Name tlv_size_diff = 0;
669*5113495bSYour Name num_of_elems =
670*5113495bSYour Name curr_tlv_len /
671*5113495bSYour Name attr_struct_ptr.tag_struct_size;
672*5113495bSYour Name } else {
673*5113495bSYour Name wmi_tlv_print_error
674*5113495bSYour Name ("%s ERROR Need to handle this tag ID for variable length %d\n",
675*5113495bSYour Name __func__, attr_struct_ptr.tag_id);
676*5113495bSYour Name goto Error_wmitlv_check_and_pad_tlvs;
677*5113495bSYour Name }
678*5113495bSYour Name }
679*5113495bSYour Name
680*5113495bSYour Name if ((WMITLV_TAG_ARRAY_STRUC == attr_struct_ptr.tag_id) &&
681*5113495bSYour Name (tlv_size_diff != 0)) {
682*5113495bSYour Name void *new_tlv_buf = NULL;
683*5113495bSYour Name uint8_t *tlv_buf_ptr = NULL;
684*5113495bSYour Name uint32_t in_tlv_len;
685*5113495bSYour Name uint32_t i;
686*5113495bSYour Name
687*5113495bSYour Name if (attr_struct_ptr.tag_varied_size == WMITLV_SIZE_FIX) {
688*5113495bSYour Name /* This is not allowed. The tag WMITLV_TAG_ARRAY_STRUC can
689*5113495bSYour Name * only be used with variable-length structure array
690*5113495bSYour Name * should not have a fixed number of elements (contradicting).
691*5113495bSYour Name * Use WMITLV_TAG_ARRAY_FIXED_STRUC tag for fixed size
692*5113495bSYour Name * structure array(where structure never change without
693*5113495bSYour Name * breaking compatibility) */
694*5113495bSYour Name wmi_tlv_print_error
695*5113495bSYour Name ("%s: ERROR: TLV (tag=%d) should be variable-length and not fixed length\n",
696*5113495bSYour Name __func__, curr_tlv_tag);
697*5113495bSYour Name goto Error_wmitlv_check_and_pad_tlvs;
698*5113495bSYour Name }
699*5113495bSYour Name
700*5113495bSYour Name /* Warning: Needs to allocate a larger structure and pad with zeros */
701*5113495bSYour Name wmi_tlv_print_verbose
702*5113495bSYour Name ("%s: WARN: TLV array of structures needs padding. tlv_size_diff=%d\n",
703*5113495bSYour Name __func__, tlv_size_diff);
704*5113495bSYour Name
705*5113495bSYour Name /* incoming structure length */
706*5113495bSYour Name in_tlv_len =
707*5113495bSYour Name WMITLV_GET_TLVLEN(WMITLV_GET_HDR(buf_ptr)) +
708*5113495bSYour Name WMI_TLV_HDR_SIZE;
709*5113495bSYour Name #ifndef NO_DYNAMIC_MEM_ALLOC
710*5113495bSYour Name wmi_tlv_os_mem_alloc(os_handle, new_tlv_buf,
711*5113495bSYour Name (num_of_elems *
712*5113495bSYour Name attr_struct_ptr.tag_struct_size));
713*5113495bSYour Name if (!new_tlv_buf) {
714*5113495bSYour Name /* Error: unable to alloc memory */
715*5113495bSYour Name wmi_tlv_print_error
716*5113495bSYour Name ("%s: Error: unable to alloc memory (size=%d) for padding the TLV array %d\n",
717*5113495bSYour Name __func__,
718*5113495bSYour Name (num_of_elems *
719*5113495bSYour Name attr_struct_ptr.tag_struct_size),
720*5113495bSYour Name curr_tlv_tag);
721*5113495bSYour Name goto Error_wmitlv_check_and_pad_tlvs;
722*5113495bSYour Name }
723*5113495bSYour Name
724*5113495bSYour Name wmi_tlv_OS_MEMZERO(new_tlv_buf,
725*5113495bSYour Name (num_of_elems *
726*5113495bSYour Name attr_struct_ptr.tag_struct_size));
727*5113495bSYour Name tlv_buf_ptr = (uint8_t *) new_tlv_buf;
728*5113495bSYour Name for (i = 0; i < num_of_elems; i++) {
729*5113495bSYour Name if (tlv_size_diff > 0) {
730*5113495bSYour Name /* Incoming structure size is greater than expected
731*5113495bSYour Name * structure size. so copy the number of bytes equal
732*5113495bSYour Name * to expected structure size */
733*5113495bSYour Name wmi_tlv_OS_MEMCPY(tlv_buf_ptr,
734*5113495bSYour Name (void *)(buf_ptr +
735*5113495bSYour Name i *
736*5113495bSYour Name in_tlv_len),
737*5113495bSYour Name attr_struct_ptr.
738*5113495bSYour Name tag_struct_size);
739*5113495bSYour Name } else {
740*5113495bSYour Name /* Incoming structure size is smaller than expected
741*5113495bSYour Name * structure size. so copy the number of bytes equal
742*5113495bSYour Name * to incoming structure size */
743*5113495bSYour Name wmi_tlv_OS_MEMCPY(tlv_buf_ptr,
744*5113495bSYour Name (void *)(buf_ptr +
745*5113495bSYour Name i *
746*5113495bSYour Name in_tlv_len),
747*5113495bSYour Name in_tlv_len);
748*5113495bSYour Name }
749*5113495bSYour Name tlv_buf_ptr += attr_struct_ptr.tag_struct_size;
750*5113495bSYour Name }
751*5113495bSYour Name #else
752*5113495bSYour Name {
753*5113495bSYour Name uint8_t *src_addr;
754*5113495bSYour Name uint8_t *dst_addr;
755*5113495bSYour Name uint32_t buf_mov_len;
756*5113495bSYour Name
757*5113495bSYour Name if (tlv_size_diff < 0) {
758*5113495bSYour Name /* Incoming structure size is smaller than expected size
759*5113495bSYour Name * then this needs padding for each element in the array */
760*5113495bSYour Name
761*5113495bSYour Name /* Find amount of bytes to be padded for one element */
762*5113495bSYour Name num_padding_bytes = tlv_size_diff * -1;
763*5113495bSYour Name
764*5113495bSYour Name /* Move subsequent TLVs by number of bytes to be padded
765*5113495bSYour Name * for all elements */
766*5113495bSYour Name if ((free_buf_len <
767*5113495bSYour Name attr_struct_ptr.tag_struct_size *
768*5113495bSYour Name num_of_elems) ||
769*5113495bSYour Name (param_buf_len <
770*5113495bSYour Name buf_idx + curr_tlv_len +
771*5113495bSYour Name num_padding_bytes * num_of_elems)) {
772*5113495bSYour Name wmi_tlv_print_error("%s: Insufficient buffer\n",
773*5113495bSYour Name __func__);
774*5113495bSYour Name goto
775*5113495bSYour Name Error_wmitlv_check_and_pad_tlvs;
776*5113495bSYour Name } else {
777*5113495bSYour Name src_addr =
778*5113495bSYour Name buf_ptr + curr_tlv_len;
779*5113495bSYour Name dst_addr =
780*5113495bSYour Name buf_ptr + curr_tlv_len +
781*5113495bSYour Name (num_padding_bytes *
782*5113495bSYour Name num_of_elems);
783*5113495bSYour Name buf_mov_len =
784*5113495bSYour Name param_buf_len - (buf_idx +
785*5113495bSYour Name curr_tlv_len);
786*5113495bSYour Name
787*5113495bSYour Name wmi_tlv_OS_MEMMOVE(dst_addr,
788*5113495bSYour Name src_addr,
789*5113495bSYour Name buf_mov_len);
790*5113495bSYour Name }
791*5113495bSYour Name
792*5113495bSYour Name /* Move subsequent elements of array down by number of
793*5113495bSYour Name * bytes to be padded for one element and also set
794*5113495bSYour Name * padding bytes to zero */
795*5113495bSYour Name tlv_buf_ptr = buf_ptr;
796*5113495bSYour Name for (i = 0; i < num_of_elems - 1; i++) {
797*5113495bSYour Name src_addr =
798*5113495bSYour Name tlv_buf_ptr + in_tlv_len;
799*5113495bSYour Name if (i != (num_of_elems - 1)) {
800*5113495bSYour Name dst_addr =
801*5113495bSYour Name tlv_buf_ptr +
802*5113495bSYour Name in_tlv_len +
803*5113495bSYour Name num_padding_bytes;
804*5113495bSYour Name buf_mov_len =
805*5113495bSYour Name curr_tlv_len -
806*5113495bSYour Name ((i +
807*5113495bSYour Name 1) * in_tlv_len);
808*5113495bSYour Name
809*5113495bSYour Name wmi_tlv_OS_MEMMOVE
810*5113495bSYour Name (dst_addr, src_addr,
811*5113495bSYour Name buf_mov_len);
812*5113495bSYour Name }
813*5113495bSYour Name
814*5113495bSYour Name /* Set the padding bytes to zeroes */
815*5113495bSYour Name wmi_tlv_OS_MEMZERO(src_addr,
816*5113495bSYour Name num_padding_bytes);
817*5113495bSYour Name
818*5113495bSYour Name tlv_buf_ptr +=
819*5113495bSYour Name attr_struct_ptr.
820*5113495bSYour Name tag_struct_size;
821*5113495bSYour Name }
822*5113495bSYour Name src_addr = tlv_buf_ptr + in_tlv_len;
823*5113495bSYour Name wmi_tlv_OS_MEMZERO(src_addr,
824*5113495bSYour Name num_padding_bytes);
825*5113495bSYour Name
826*5113495bSYour Name /* Update the number of padding bytes to total number
827*5113495bSYour Name * of bytes padded for all elements in the array */
828*5113495bSYour Name num_padding_bytes =
829*5113495bSYour Name num_padding_bytes * num_of_elems;
830*5113495bSYour Name
831*5113495bSYour Name new_tlv_buf = buf_ptr;
832*5113495bSYour Name } else {
833*5113495bSYour Name /* Incoming structure size is greater than expected size
834*5113495bSYour Name * then this needs shrinking for each element in the array */
835*5113495bSYour Name
836*5113495bSYour Name /* Find amount of bytes to be shrunk for one element */
837*5113495bSYour Name num_padding_bytes = tlv_size_diff * -1;
838*5113495bSYour Name
839*5113495bSYour Name /* Move subsequent elements of array up by number of bytes
840*5113495bSYour Name * to be shrunk for one element */
841*5113495bSYour Name tlv_buf_ptr = buf_ptr;
842*5113495bSYour Name for (i = 0; i < (num_of_elems - 1); i++) {
843*5113495bSYour Name src_addr =
844*5113495bSYour Name tlv_buf_ptr + in_tlv_len;
845*5113495bSYour Name dst_addr =
846*5113495bSYour Name tlv_buf_ptr + in_tlv_len +
847*5113495bSYour Name num_padding_bytes;
848*5113495bSYour Name buf_mov_len =
849*5113495bSYour Name curr_tlv_len -
850*5113495bSYour Name ((i + 1) * in_tlv_len);
851*5113495bSYour Name
852*5113495bSYour Name wmi_tlv_OS_MEMMOVE(dst_addr,
853*5113495bSYour Name src_addr,
854*5113495bSYour Name buf_mov_len);
855*5113495bSYour Name
856*5113495bSYour Name tlv_buf_ptr +=
857*5113495bSYour Name attr_struct_ptr.
858*5113495bSYour Name tag_struct_size;
859*5113495bSYour Name }
860*5113495bSYour Name
861*5113495bSYour Name /* Move subsequent TLVs by number of bytes to be shrunk
862*5113495bSYour Name * for all elements */
863*5113495bSYour Name if (param_buf_len >
864*5113495bSYour Name (buf_idx + curr_tlv_len)) {
865*5113495bSYour Name src_addr =
866*5113495bSYour Name buf_ptr + curr_tlv_len;
867*5113495bSYour Name dst_addr =
868*5113495bSYour Name buf_ptr + curr_tlv_len +
869*5113495bSYour Name (num_padding_bytes *
870*5113495bSYour Name num_of_elems);
871*5113495bSYour Name buf_mov_len =
872*5113495bSYour Name param_buf_len - (buf_idx +
873*5113495bSYour Name curr_tlv_len);
874*5113495bSYour Name
875*5113495bSYour Name wmi_tlv_OS_MEMMOVE(dst_addr,
876*5113495bSYour Name src_addr,
877*5113495bSYour Name buf_mov_len);
878*5113495bSYour Name }
879*5113495bSYour Name
880*5113495bSYour Name /* Update the number of padding bytes to total number of
881*5113495bSYour Name * bytes shrunk for all elements in the array */
882*5113495bSYour Name num_padding_bytes =
883*5113495bSYour Name num_padding_bytes * num_of_elems;
884*5113495bSYour Name
885*5113495bSYour Name new_tlv_buf = buf_ptr;
886*5113495bSYour Name }
887*5113495bSYour Name }
888*5113495bSYour Name #endif
889*5113495bSYour Name cmd_param_tlvs_ptr[tlv_index].tlv_ptr = new_tlv_buf;
890*5113495bSYour Name cmd_param_tlvs_ptr[tlv_index].num_elements =
891*5113495bSYour Name num_of_elems;
892*5113495bSYour Name cmd_param_tlvs_ptr[tlv_index].buf_is_allocated = 1; /* Indicates that buffer is allocated */
893*5113495bSYour Name
894*5113495bSYour Name } else if (tlv_size_diff >= 0) {
895*5113495bSYour Name /* Warning: some parameter truncation */
896*5113495bSYour Name if (tlv_size_diff > 0) {
897*5113495bSYour Name wmi_tlv_print_verbose
898*5113495bSYour Name ("%s: WARN: TLV truncated. tlv_size_diff=%d, curr_tlv_len=%d\n",
899*5113495bSYour Name __func__, tlv_size_diff, curr_tlv_len);
900*5113495bSYour Name }
901*5113495bSYour Name /* TODO: this next line needs more comments and explanation */
902*5113495bSYour Name cmd_param_tlvs_ptr[tlv_index].tlv_ptr =
903*5113495bSYour Name (attr_struct_ptr.tag_varied_size
904*5113495bSYour Name && !curr_tlv_len) ? NULL : (void *)buf_ptr;
905*5113495bSYour Name cmd_param_tlvs_ptr[tlv_index].num_elements =
906*5113495bSYour Name num_of_elems;
907*5113495bSYour Name cmd_param_tlvs_ptr[tlv_index].buf_is_allocated = 0; /* Indicates that buffer is not allocated */
908*5113495bSYour Name } else {
909*5113495bSYour Name void *new_tlv_buf = NULL;
910*5113495bSYour Name
911*5113495bSYour Name /* Warning: Needs to allocate a larger structure and pad with zeros */
912*5113495bSYour Name wmi_tlv_print_verbose
913*5113495bSYour Name ("%s: WARN: TLV needs padding. tlv_size_diff=%d\n",
914*5113495bSYour Name __func__, tlv_size_diff);
915*5113495bSYour Name #ifndef NO_DYNAMIC_MEM_ALLOC
916*5113495bSYour Name /* Dynamic memory allocation is supported */
917*5113495bSYour Name wmi_tlv_os_mem_alloc(os_handle, new_tlv_buf,
918*5113495bSYour Name (curr_tlv_len - tlv_size_diff));
919*5113495bSYour Name if (!new_tlv_buf) {
920*5113495bSYour Name /* Error: unable to alloc memory */
921*5113495bSYour Name wmi_tlv_print_error
922*5113495bSYour Name ("%s: Error: unable to alloc memory (size=%d) for padding the TLV %d\n",
923*5113495bSYour Name __func__, (curr_tlv_len - tlv_size_diff),
924*5113495bSYour Name curr_tlv_tag);
925*5113495bSYour Name goto Error_wmitlv_check_and_pad_tlvs;
926*5113495bSYour Name }
927*5113495bSYour Name
928*5113495bSYour Name wmi_tlv_OS_MEMZERO(new_tlv_buf,
929*5113495bSYour Name (curr_tlv_len - tlv_size_diff));
930*5113495bSYour Name wmi_tlv_OS_MEMCPY(new_tlv_buf, (void *)buf_ptr,
931*5113495bSYour Name curr_tlv_len);
932*5113495bSYour Name #else
933*5113495bSYour Name /* Dynamic memory allocation is not supported. Padding has
934*5113495bSYour Name * to be done with in the existing buffer assuming we have
935*5113495bSYour Name * enough space to grow */
936*5113495bSYour Name {
937*5113495bSYour Name /* Note: tlv_size_diff is a value less than zero */
938*5113495bSYour Name /* Move the Subsequent TLVs by amount of bytes needs to be padded */
939*5113495bSYour Name uint8_t *src_addr;
940*5113495bSYour Name uint8_t *dst_addr;
941*5113495bSYour Name uint32_t src_len;
942*5113495bSYour Name
943*5113495bSYour Name num_padding_bytes = (tlv_size_diff * -1);
944*5113495bSYour Name
945*5113495bSYour Name src_addr = buf_ptr + curr_tlv_len;
946*5113495bSYour Name dst_addr =
947*5113495bSYour Name buf_ptr + curr_tlv_len + num_padding_bytes;
948*5113495bSYour Name src_len =
949*5113495bSYour Name param_buf_len - (buf_idx + curr_tlv_len);
950*5113495bSYour Name
951*5113495bSYour Name wmi_tlv_OS_MEMMOVE(dst_addr, src_addr, src_len);
952*5113495bSYour Name
953*5113495bSYour Name /* Set the padding bytes to zeroes */
954*5113495bSYour Name wmi_tlv_OS_MEMZERO(src_addr, num_padding_bytes);
955*5113495bSYour Name
956*5113495bSYour Name new_tlv_buf = buf_ptr;
957*5113495bSYour Name }
958*5113495bSYour Name #endif
959*5113495bSYour Name cmd_param_tlvs_ptr[tlv_index].tlv_ptr = new_tlv_buf;
960*5113495bSYour Name cmd_param_tlvs_ptr[tlv_index].num_elements =
961*5113495bSYour Name num_of_elems;
962*5113495bSYour Name cmd_param_tlvs_ptr[tlv_index].buf_is_allocated = 1; /* Indicates that buffer is allocated */
963*5113495bSYour Name }
964*5113495bSYour Name
965*5113495bSYour Name tlv_index++;
966*5113495bSYour Name remaining_expected_tlvs--;
967*5113495bSYour Name buf_ptr += curr_tlv_len + num_padding_bytes;
968*5113495bSYour Name buf_idx += curr_tlv_len + num_padding_bytes;
969*5113495bSYour Name }
970*5113495bSYour Name
971*5113495bSYour Name return 0;
972*5113495bSYour Name Error_wmitlv_check_and_pad_tlvs:
973*5113495bSYour Name if (is_cmd_id) {
974*5113495bSYour Name wmitlv_free_allocated_command_tlvs(wmi_cmd_event_id,
975*5113495bSYour Name wmi_cmd_struct_ptr);
976*5113495bSYour Name } else {
977*5113495bSYour Name wmitlv_free_allocated_event_tlvs(wmi_cmd_event_id,
978*5113495bSYour Name wmi_cmd_struct_ptr);
979*5113495bSYour Name }
980*5113495bSYour Name *wmi_cmd_struct_ptr = NULL;
981*5113495bSYour Name return error;
982*5113495bSYour Name }
983*5113495bSYour Name
984*5113495bSYour Name /**
985*5113495bSYour Name * wmitlv_check_and_pad_event_tlvs() - tlv helper function
986*5113495bSYour Name * @os_handle: os context handle
987*5113495bSYour Name * @param_struc_ptr: pointer to tlv structure
988*5113495bSYour Name * @param_buf_len: length of tlv parameter
989*5113495bSYour Name * @wmi_cmd_event_id: command event id
990*5113495bSYour Name * @wmi_cmd_struct_ptr: wmi command structure
991*5113495bSYour Name *
992*5113495bSYour Name *
993*5113495bSYour Name * validate and pad(if necessary) for incoming WMI Event TLVs
994*5113495bSYour Name *
995*5113495bSYour Name * Return: 0 if success. Return < 0 if failure.
996*5113495bSYour Name */
997*5113495bSYour Name int
wmitlv_check_and_pad_event_tlvs(void * os_handle,void * param_struc_ptr,uint32_t param_buf_len,uint32_t wmi_cmd_event_id,void ** wmi_cmd_struct_ptr)998*5113495bSYour Name wmitlv_check_and_pad_event_tlvs(void *os_handle, void *param_struc_ptr,
999*5113495bSYour Name uint32_t param_buf_len,
1000*5113495bSYour Name uint32_t wmi_cmd_event_id,
1001*5113495bSYour Name void **wmi_cmd_struct_ptr)
1002*5113495bSYour Name {
1003*5113495bSYour Name uint32_t is_cmd_id = 0;
1004*5113495bSYour Name return wmitlv_check_and_pad_tlvs
1005*5113495bSYour Name (os_handle, param_struc_ptr, param_buf_len, is_cmd_id,
1006*5113495bSYour Name wmi_cmd_event_id, wmi_cmd_struct_ptr);
1007*5113495bSYour Name }
1008*5113495bSYour Name qdf_export_symbol(wmitlv_check_and_pad_event_tlvs);
1009*5113495bSYour Name
1010*5113495bSYour Name /**
1011*5113495bSYour Name * wmitlv_check_and_pad_command_tlvs() - tlv helper function
1012*5113495bSYour Name * @os_handle: os context handle
1013*5113495bSYour Name * @param_struc_ptr: pointer to tlv structure
1014*5113495bSYour Name * @param_buf_len: length of tlv parameter
1015*5113495bSYour Name * @wmi_cmd_event_id: command event id
1016*5113495bSYour Name * @wmi_cmd_struct_ptr: wmi command structure
1017*5113495bSYour Name *
1018*5113495bSYour Name *
1019*5113495bSYour Name * validate and pad(if necessary) for incoming WMI Command TLVs
1020*5113495bSYour Name *
1021*5113495bSYour Name * Return: 0 if success. Return < 0 if failure.
1022*5113495bSYour Name */
1023*5113495bSYour Name int
wmitlv_check_and_pad_command_tlvs(void * os_handle,void * param_struc_ptr,uint32_t param_buf_len,uint32_t wmi_cmd_event_id,void ** wmi_cmd_struct_ptr)1024*5113495bSYour Name wmitlv_check_and_pad_command_tlvs(void *os_handle, void *param_struc_ptr,
1025*5113495bSYour Name uint32_t param_buf_len,
1026*5113495bSYour Name uint32_t wmi_cmd_event_id,
1027*5113495bSYour Name void **wmi_cmd_struct_ptr)
1028*5113495bSYour Name {
1029*5113495bSYour Name uint32_t is_cmd_id = 1;
1030*5113495bSYour Name return wmitlv_check_and_pad_tlvs
1031*5113495bSYour Name (os_handle, param_struc_ptr, param_buf_len, is_cmd_id,
1032*5113495bSYour Name wmi_cmd_event_id, wmi_cmd_struct_ptr);
1033*5113495bSYour Name }
1034*5113495bSYour Name
1035*5113495bSYour Name /**
1036*5113495bSYour Name * wmitlv_free_allocated_tlvs() - tlv helper function
1037*5113495bSYour Name * @is_cmd_id: bollean to check if cmd or event tlv
1038*5113495bSYour Name * @cmd_event_id: command or event id
1039*5113495bSYour Name * @wmi_cmd_struct_ptr: wmi command structure
1040*5113495bSYour Name *
1041*5113495bSYour Name *
1042*5113495bSYour Name * free any allocated buffers for WMI Event/Command TLV processing
1043*5113495bSYour Name *
1044*5113495bSYour Name * Return: none
1045*5113495bSYour Name */
wmitlv_free_allocated_tlvs(uint32_t is_cmd_id,uint32_t cmd_event_id,void ** wmi_cmd_struct_ptr)1046*5113495bSYour Name static void wmitlv_free_allocated_tlvs(uint32_t is_cmd_id,
1047*5113495bSYour Name uint32_t cmd_event_id,
1048*5113495bSYour Name void **wmi_cmd_struct_ptr)
1049*5113495bSYour Name {
1050*5113495bSYour Name void *ptr = *wmi_cmd_struct_ptr;
1051*5113495bSYour Name
1052*5113495bSYour Name if (!ptr) {
1053*5113495bSYour Name wmi_tlv_print_error("%s: Nothing to free for CMD/Event 0x%x\n",
1054*5113495bSYour Name __func__, cmd_event_id);
1055*5113495bSYour Name return;
1056*5113495bSYour Name }
1057*5113495bSYour Name #ifndef NO_DYNAMIC_MEM_ALLOC
1058*5113495bSYour Name
1059*5113495bSYour Name /* macro to free that previously allocated memory for this TLV. When (op==FREE_TLV_ELEM). */
1060*5113495bSYour Name #define WMITLV_OP_FREE_TLV_ELEM_macro(param_ptr, param_len, wmi_cmd_event_id, elem_tlv_tag, elem_struc_type, elem_name, var_len, arr_size) \
1061*5113495bSYour Name if ((((WMITLV_TYPEDEF_STRUCT_PARAMS_TLVS(wmi_cmd_event_id) *)ptr)->WMITLV_FIELD_BUF_IS_ALLOCATED(elem_name)) && \
1062*5113495bSYour Name (((WMITLV_TYPEDEF_STRUCT_PARAMS_TLVS(wmi_cmd_event_id) *)ptr)->elem_name)) \
1063*5113495bSYour Name { \
1064*5113495bSYour Name wmi_tlv_os_mem_free(((WMITLV_TYPEDEF_STRUCT_PARAMS_TLVS(wmi_cmd_event_id) *)ptr)->elem_name); \
1065*5113495bSYour Name }
1066*5113495bSYour Name
1067*5113495bSYour Name #define WMITLV_FREE_TLV_ELEMS(id) \
1068*5113495bSYour Name case id: \
1069*5113495bSYour Name { \
1070*5113495bSYour Name WMITLV_TABLE(id, FREE_TLV_ELEM, NULL, 0) \
1071*5113495bSYour Name } \
1072*5113495bSYour Name break;
1073*5113495bSYour Name
1074*5113495bSYour Name if (is_cmd_id) {
1075*5113495bSYour Name switch (cmd_event_id) {
1076*5113495bSYour Name WMITLV_ALL_CMD_LIST(WMITLV_FREE_TLV_ELEMS);
1077*5113495bSYour Name default:
1078*5113495bSYour Name wmi_tlv_print_error
1079*5113495bSYour Name ("%s: ERROR: Cannot find the TLVs attributes for Cmd=0x%x, %d\n",
1080*5113495bSYour Name __func__, cmd_event_id, cmd_event_id);
1081*5113495bSYour Name }
1082*5113495bSYour Name } else {
1083*5113495bSYour Name switch (cmd_event_id) {
1084*5113495bSYour Name WMITLV_ALL_EVT_LIST(WMITLV_FREE_TLV_ELEMS);
1085*5113495bSYour Name default:
1086*5113495bSYour Name wmi_tlv_print_error
1087*5113495bSYour Name ("%s: ERROR: Cannot find the TLVs attributes for Cmd=0x%x, %d\n",
1088*5113495bSYour Name __func__, cmd_event_id, cmd_event_id);
1089*5113495bSYour Name }
1090*5113495bSYour Name }
1091*5113495bSYour Name
1092*5113495bSYour Name wmi_tlv_os_mem_free(*wmi_cmd_struct_ptr);
1093*5113495bSYour Name *wmi_cmd_struct_ptr = NULL;
1094*5113495bSYour Name #endif
1095*5113495bSYour Name
1096*5113495bSYour Name return;
1097*5113495bSYour Name }
1098*5113495bSYour Name
1099*5113495bSYour Name /**
1100*5113495bSYour Name * wmitlv_free_allocated_command_tlvs() - tlv helper function
1101*5113495bSYour Name * @cmd_event_id: command or event id
1102*5113495bSYour Name * @wmi_cmd_struct_ptr: wmi command structure
1103*5113495bSYour Name *
1104*5113495bSYour Name *
1105*5113495bSYour Name * free any allocated buffers for WMI Event/Command TLV processing
1106*5113495bSYour Name *
1107*5113495bSYour Name * Return: none
1108*5113495bSYour Name */
wmitlv_free_allocated_command_tlvs(uint32_t cmd_event_id,void ** wmi_cmd_struct_ptr)1109*5113495bSYour Name void wmitlv_free_allocated_command_tlvs(uint32_t cmd_event_id,
1110*5113495bSYour Name void **wmi_cmd_struct_ptr)
1111*5113495bSYour Name {
1112*5113495bSYour Name wmitlv_free_allocated_tlvs(1, cmd_event_id, wmi_cmd_struct_ptr);
1113*5113495bSYour Name }
1114*5113495bSYour Name
1115*5113495bSYour Name /**
1116*5113495bSYour Name * wmitlv_free_allocated_event_tlvs() - tlv helper function
1117*5113495bSYour Name * @cmd_event_id: command or event id
1118*5113495bSYour Name * @wmi_cmd_struct_ptr: wmi command structure
1119*5113495bSYour Name *
1120*5113495bSYour Name *
1121*5113495bSYour Name * free any allocated buffers for WMI Event/Command TLV processing
1122*5113495bSYour Name *
1123*5113495bSYour Name * Return: none
1124*5113495bSYour Name */
wmitlv_free_allocated_event_tlvs(uint32_t cmd_event_id,void ** wmi_cmd_struct_ptr)1125*5113495bSYour Name void wmitlv_free_allocated_event_tlvs(uint32_t cmd_event_id,
1126*5113495bSYour Name void **wmi_cmd_struct_ptr)
1127*5113495bSYour Name {
1128*5113495bSYour Name wmitlv_free_allocated_tlvs(0, cmd_event_id, wmi_cmd_struct_ptr);
1129*5113495bSYour Name }
1130*5113495bSYour Name qdf_export_symbol(wmitlv_free_allocated_event_tlvs);
1131*5113495bSYour Name
1132*5113495bSYour Name /**
1133*5113495bSYour Name * wmi_versions_are_compatible() - tlv helper function
1134*5113495bSYour Name * @vers1: host wmi version
1135*5113495bSYour Name * @vers2: target wmi version
1136*5113495bSYour Name *
1137*5113495bSYour Name *
1138*5113495bSYour Name * check if two given wmi versions are compatible
1139*5113495bSYour Name *
1140*5113495bSYour Name * Return: none
1141*5113495bSYour Name */
1142*5113495bSYour Name int
wmi_versions_are_compatible(wmi_abi_version * vers1,wmi_abi_version * vers2)1143*5113495bSYour Name wmi_versions_are_compatible(wmi_abi_version *vers1, wmi_abi_version *vers2)
1144*5113495bSYour Name {
1145*5113495bSYour Name if ((vers1->abi_version_ns_0 != vers2->abi_version_ns_0) ||
1146*5113495bSYour Name (vers1->abi_version_ns_1 != vers2->abi_version_ns_1) ||
1147*5113495bSYour Name (vers1->abi_version_ns_2 != vers2->abi_version_ns_2) ||
1148*5113495bSYour Name (vers1->abi_version_ns_3 != vers2->abi_version_ns_3)) {
1149*5113495bSYour Name /* The namespaces are different. Incompatible. */
1150*5113495bSYour Name return 0;
1151*5113495bSYour Name }
1152*5113495bSYour Name
1153*5113495bSYour Name if (vers1->abi_version_0 != vers2->abi_version_0) {
1154*5113495bSYour Name /* The major or minor versions are different. Incompatible */
1155*5113495bSYour Name return 0;
1156*5113495bSYour Name }
1157*5113495bSYour Name /* We ignore the build version */
1158*5113495bSYour Name return 1;
1159*5113495bSYour Name }
1160*5113495bSYour Name
1161*5113495bSYour Name /**
1162*5113495bSYour Name * wmi_versions_can_downgrade() - tlv helper function
1163*5113495bSYour Name * @num_allowlist: number of entries in @version_whitelist_table
1164*5113495bSYour Name * @version_whitelist_table: version table
1165*5113495bSYour Name * @my_vers: host version
1166*5113495bSYour Name * @opp_vers: target version
1167*5113495bSYour Name * @out_vers: downgraded version
1168*5113495bSYour Name *
1169*5113495bSYour Name *
1170*5113495bSYour Name * check if target wmi version can be downgraded
1171*5113495bSYour Name *
1172*5113495bSYour Name * Return: 0 if success. Return < 0 if failure.
1173*5113495bSYour Name */
1174*5113495bSYour Name static int
wmi_versions_can_downgrade(int num_allowlist,wmi_whitelist_version_info * version_whitelist_table,wmi_abi_version * my_vers,wmi_abi_version * opp_vers,wmi_abi_version * out_vers)1175*5113495bSYour Name wmi_versions_can_downgrade(int num_allowlist,
1176*5113495bSYour Name wmi_whitelist_version_info *version_whitelist_table,
1177*5113495bSYour Name wmi_abi_version *my_vers,
1178*5113495bSYour Name wmi_abi_version *opp_vers,
1179*5113495bSYour Name wmi_abi_version *out_vers)
1180*5113495bSYour Name {
1181*5113495bSYour Name uint8_t can_try_to_downgrade;
1182*5113495bSYour Name uint32_t my_major_vers = WMI_VER_GET_MAJOR(my_vers->abi_version_0);
1183*5113495bSYour Name uint32_t my_minor_vers = WMI_VER_GET_MINOR(my_vers->abi_version_0);
1184*5113495bSYour Name uint32_t opp_major_vers = WMI_VER_GET_MAJOR(opp_vers->abi_version_0);
1185*5113495bSYour Name uint32_t opp_minor_vers = WMI_VER_GET_MINOR(opp_vers->abi_version_0);
1186*5113495bSYour Name uint32_t downgraded_minor_vers;
1187*5113495bSYour Name
1188*5113495bSYour Name if ((my_vers->abi_version_ns_0 != opp_vers->abi_version_ns_0) ||
1189*5113495bSYour Name (my_vers->abi_version_ns_1 != opp_vers->abi_version_ns_1) ||
1190*5113495bSYour Name (my_vers->abi_version_ns_2 != opp_vers->abi_version_ns_2) ||
1191*5113495bSYour Name (my_vers->abi_version_ns_3 != opp_vers->abi_version_ns_3)) {
1192*5113495bSYour Name /* The namespaces are different. Incompatible. */
1193*5113495bSYour Name can_try_to_downgrade = false;
1194*5113495bSYour Name } else if (my_major_vers != opp_major_vers) {
1195*5113495bSYour Name /* Major version is different. Incompatible and cannot downgrade. */
1196*5113495bSYour Name can_try_to_downgrade = false;
1197*5113495bSYour Name } else {
1198*5113495bSYour Name /* Same major version. */
1199*5113495bSYour Name
1200*5113495bSYour Name if (my_minor_vers < opp_minor_vers) {
1201*5113495bSYour Name /* Opposite party is newer. Incompatible and cannot downgrade. */
1202*5113495bSYour Name can_try_to_downgrade = false;
1203*5113495bSYour Name } else if (my_minor_vers > opp_minor_vers) {
1204*5113495bSYour Name /* Opposite party is older. Check allowlist if
1205*5113495bSYour Name * we can downgrade
1206*5113495bSYour Name */
1207*5113495bSYour Name can_try_to_downgrade = true;
1208*5113495bSYour Name } else {
1209*5113495bSYour Name /* Same version */
1210*5113495bSYour Name wmi_tlv_OS_MEMCPY(out_vers, my_vers,
1211*5113495bSYour Name sizeof(wmi_abi_version));
1212*5113495bSYour Name return 1;
1213*5113495bSYour Name }
1214*5113495bSYour Name }
1215*5113495bSYour Name
1216*5113495bSYour Name if (!can_try_to_downgrade) {
1217*5113495bSYour Name wmi_tlv_print_error("%s: Warning: incompatible WMI version.\n",
1218*5113495bSYour Name __func__);
1219*5113495bSYour Name wmi_tlv_OS_MEMCPY(out_vers, my_vers, sizeof(wmi_abi_version));
1220*5113495bSYour Name return 0;
1221*5113495bSYour Name }
1222*5113495bSYour Name /* Try to see we can downgrade the supported version */
1223*5113495bSYour Name downgraded_minor_vers = my_minor_vers;
1224*5113495bSYour Name while (downgraded_minor_vers > opp_minor_vers) {
1225*5113495bSYour Name uint8_t downgraded = false;
1226*5113495bSYour Name int i;
1227*5113495bSYour Name
1228*5113495bSYour Name for (i = 0; i < num_allowlist; i++) {
1229*5113495bSYour Name if (version_whitelist_table[i].major != my_major_vers)
1230*5113495bSYour Name continue; /* skip */
1231*5113495bSYour Name if (version_whitelist_table[i].namespace_0 !=
1232*5113495bSYour Name my_vers->abi_version_ns_0 ||
1233*5113495bSYour Name version_whitelist_table[i].namespace_1 !=
1234*5113495bSYour Name my_vers->abi_version_ns_1 ||
1235*5113495bSYour Name version_whitelist_table[i].namespace_2 !=
1236*5113495bSYour Name my_vers->abi_version_ns_2 ||
1237*5113495bSYour Name version_whitelist_table[i].namespace_3 !=
1238*5113495bSYour Name my_vers->abi_version_ns_3) {
1239*5113495bSYour Name continue; /* skip */
1240*5113495bSYour Name }
1241*5113495bSYour Name if (version_whitelist_table[i].minor ==
1242*5113495bSYour Name downgraded_minor_vers) {
1243*5113495bSYour Name /* Found the next version that I can downgrade */
1244*5113495bSYour Name wmi_tlv_print_error
1245*5113495bSYour Name ("%s: Note: found a allowlist entry to downgrade. wh. list ver: %d,%d,0x%x 0x%x 0x%x 0x%x\n",
1246*5113495bSYour Name __func__,
1247*5113495bSYour Name version_whitelist_table[i].major,
1248*5113495bSYour Name version_whitelist_table[i].minor,
1249*5113495bSYour Name version_whitelist_table[i].namespace_0,
1250*5113495bSYour Name version_whitelist_table[i].namespace_1,
1251*5113495bSYour Name version_whitelist_table[i].namespace_2,
1252*5113495bSYour Name version_whitelist_table[i].namespace_3);
1253*5113495bSYour Name downgraded_minor_vers--;
1254*5113495bSYour Name downgraded = true;
1255*5113495bSYour Name break;
1256*5113495bSYour Name }
1257*5113495bSYour Name }
1258*5113495bSYour Name if (!downgraded) {
1259*5113495bSYour Name break; /* Done since we did not find any allowlist
1260*5113495bSYour Name * to downgrade version
1261*5113495bSYour Name */
1262*5113495bSYour Name }
1263*5113495bSYour Name }
1264*5113495bSYour Name wmi_tlv_OS_MEMCPY(out_vers, my_vers, sizeof(wmi_abi_version));
1265*5113495bSYour Name out_vers->abi_version_0 =
1266*5113495bSYour Name WMI_VER_GET_VERSION_0(my_major_vers, downgraded_minor_vers);
1267*5113495bSYour Name if (downgraded_minor_vers != opp_minor_vers) {
1268*5113495bSYour Name wmi_tlv_print_error
1269*5113495bSYour Name ("%s: Warning: incompatible WMI version and cannot downgrade.\n",
1270*5113495bSYour Name __func__);
1271*5113495bSYour Name return 0; /* Incompatible */
1272*5113495bSYour Name } else {
1273*5113495bSYour Name return 1; /* Compatible */
1274*5113495bSYour Name }
1275*5113495bSYour Name }
1276*5113495bSYour Name
1277*5113495bSYour Name /**
1278*5113495bSYour Name * wmi_cmp_and_set_abi_version() - tlv helper function
1279*5113495bSYour Name * @num_allowlist: number of entries in @version_whitelist_table
1280*5113495bSYour Name * @version_whitelist_table: version table
1281*5113495bSYour Name * @my_vers: host version
1282*5113495bSYour Name * @opp_vers: target version
1283*5113495bSYour Name * @out_vers: downgraded version
1284*5113495bSYour Name *
1285*5113495bSYour Name * This routine will compare and set the WMI ABI version.
1286*5113495bSYour Name * First, compare my version with the opposite side's version.
1287*5113495bSYour Name * If incompatible, then check the allowlist to see if our side can downgrade.
1288*5113495bSYour Name * Finally, fill in the final ABI version into the output, out_vers.
1289*5113495bSYour Name * Return 0 if the output version is compatible
1290*5113495bSYour Name * Else return 1 if the output version is incompatible
1291*5113495bSYour Name *
1292*5113495bSYour Name * Return: 0 if the output version is compatible else < 0.
1293*5113495bSYour Name */
1294*5113495bSYour Name int
wmi_cmp_and_set_abi_version(int num_allowlist,wmi_whitelist_version_info * version_whitelist_table,struct _wmi_abi_version * my_vers,struct _wmi_abi_version * opp_vers,struct _wmi_abi_version * out_vers)1295*5113495bSYour Name wmi_cmp_and_set_abi_version(int num_allowlist,
1296*5113495bSYour Name wmi_whitelist_version_info *
1297*5113495bSYour Name version_whitelist_table,
1298*5113495bSYour Name struct _wmi_abi_version *my_vers,
1299*5113495bSYour Name struct _wmi_abi_version *opp_vers,
1300*5113495bSYour Name struct _wmi_abi_version *out_vers)
1301*5113495bSYour Name {
1302*5113495bSYour Name wmi_tlv_print_verbose
1303*5113495bSYour Name ("%s: Our WMI Version: Mj=%d, Mn=%d, bd=%d, ns0=0x%x ns1:0x%x ns2:0x%x ns3:0x%x\n",
1304*5113495bSYour Name __func__, WMI_VER_GET_MAJOR(my_vers->abi_version_0),
1305*5113495bSYour Name WMI_VER_GET_MINOR(my_vers->abi_version_0), my_vers->abi_version_1,
1306*5113495bSYour Name my_vers->abi_version_ns_0, my_vers->abi_version_ns_1,
1307*5113495bSYour Name my_vers->abi_version_ns_2, my_vers->abi_version_ns_3);
1308*5113495bSYour Name
1309*5113495bSYour Name wmi_tlv_print_verbose
1310*5113495bSYour Name ("%s: Opposite side WMI Version: Mj=%d, Mn=%d, bd=%d, ns0=0x%x ns1:0x%x ns2:0x%x ns3:0x%x\n",
1311*5113495bSYour Name __func__, WMI_VER_GET_MAJOR(opp_vers->abi_version_0),
1312*5113495bSYour Name WMI_VER_GET_MINOR(opp_vers->abi_version_0),
1313*5113495bSYour Name opp_vers->abi_version_1, opp_vers->abi_version_ns_0,
1314*5113495bSYour Name opp_vers->abi_version_ns_1, opp_vers->abi_version_ns_2,
1315*5113495bSYour Name opp_vers->abi_version_ns_3);
1316*5113495bSYour Name
1317*5113495bSYour Name /* By default, the output version is our version. */
1318*5113495bSYour Name wmi_tlv_OS_MEMCPY(out_vers, my_vers, sizeof(wmi_abi_version));
1319*5113495bSYour Name if (!wmi_versions_are_compatible(my_vers, opp_vers)) {
1320*5113495bSYour Name /* Our host version and the given firmware version are incompatible. */
1321*5113495bSYour Name if (wmi_versions_can_downgrade(num_allowlist,
1322*5113495bSYour Name version_whitelist_table,
1323*5113495bSYour Name my_vers,
1324*5113495bSYour Name opp_vers,
1325*5113495bSYour Name out_vers)) {
1326*5113495bSYour Name /* We can downgrade our host versions to match firmware. */
1327*5113495bSYour Name wmi_tlv_print_error
1328*5113495bSYour Name ("%s: Host downgraded WMI Versions to match fw. Ret version: Mj=%d, Mn=%d, bd=%d, ns0=0x%x ns1:0x%x ns2:0x%x ns3:0x%x\n",
1329*5113495bSYour Name __func__,
1330*5113495bSYour Name WMI_VER_GET_MAJOR(out_vers->abi_version_0),
1331*5113495bSYour Name WMI_VER_GET_MINOR(out_vers->abi_version_0),
1332*5113495bSYour Name out_vers->abi_version_1,
1333*5113495bSYour Name out_vers->abi_version_ns_0,
1334*5113495bSYour Name out_vers->abi_version_ns_1,
1335*5113495bSYour Name out_vers->abi_version_ns_2,
1336*5113495bSYour Name out_vers->abi_version_ns_3);
1337*5113495bSYour Name return 0; /* Compatible */
1338*5113495bSYour Name } else {
1339*5113495bSYour Name /* Warn: We cannot downgrade our host versions to match firmware. */
1340*5113495bSYour Name wmi_tlv_print_error
1341*5113495bSYour Name ("%s: WARN: Host WMI Versions mismatch with fw. Ret version: Mj=%d, Mn=%d, bd=%d, ns0=0x%x ns1:0x%x ns2:0x%x ns3:0x%x\n",
1342*5113495bSYour Name __func__,
1343*5113495bSYour Name WMI_VER_GET_MAJOR(out_vers->abi_version_0),
1344*5113495bSYour Name WMI_VER_GET_MINOR(out_vers->abi_version_0),
1345*5113495bSYour Name out_vers->abi_version_1,
1346*5113495bSYour Name out_vers->abi_version_ns_0,
1347*5113495bSYour Name out_vers->abi_version_ns_1,
1348*5113495bSYour Name out_vers->abi_version_ns_2,
1349*5113495bSYour Name out_vers->abi_version_ns_3);
1350*5113495bSYour Name
1351*5113495bSYour Name return 1; /* Incompatible */
1352*5113495bSYour Name }
1353*5113495bSYour Name } else {
1354*5113495bSYour Name /* We are compatible. Our host version is the output version */
1355*5113495bSYour Name wmi_tlv_print_verbose
1356*5113495bSYour Name ("%s: Host and FW Compatible WMI Versions. Ret version: Mj=%d, Mn=%d, bd=%d, ns0=0x%x ns1:0x%x ns2:0x%x ns3:0x%x\n",
1357*5113495bSYour Name __func__, WMI_VER_GET_MAJOR(out_vers->abi_version_0),
1358*5113495bSYour Name WMI_VER_GET_MINOR(out_vers->abi_version_0),
1359*5113495bSYour Name out_vers->abi_version_1, out_vers->abi_version_ns_0,
1360*5113495bSYour Name out_vers->abi_version_ns_1, out_vers->abi_version_ns_2,
1361*5113495bSYour Name out_vers->abi_version_ns_3);
1362*5113495bSYour Name return 0; /* Compatible */
1363*5113495bSYour Name }
1364*5113495bSYour Name }
1365