xref: /wlan-dirver/qca-wifi-host-cmn/wmi/src/wmi_unified_ocb_tlv.c (revision 213581ab0b186c35d7952c56e7dd9aeed79a7ca9)
1 /*
2  * Copyright (c) 2013-2020 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for
6  * any purpose with or without fee is hereby granted, provided that the
7  * above copyright notice and this permission notice appear in all
8  * copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <osdep.h>
21 #include <wmi.h>
22 #include <wmi_unified_priv.h>
23 #include <wlan_ocb_public_structs.h>
24 #include <wmi_unified_ocb_api.h>
25 
26 /**
27  * send_ocb_set_utc_time_cmd_tlv() - send the UTC time to the firmware
28  * @wmi_handle: pointer to the wmi handle
29  * @utc: pointer to the UTC time struct
30  *
31  * Return: 0 on success
32  */
33 static QDF_STATUS send_ocb_set_utc_time_cmd_tlv(wmi_unified_t wmi_handle,
34 						struct ocb_utc_param *utc)
35 {
36 	QDF_STATUS ret;
37 	wmi_ocb_set_utc_time_cmd_fixed_param *cmd;
38 	uint8_t *buf_ptr;
39 	uint32_t len, i;
40 	wmi_buf_t buf;
41 
42 	len = sizeof(*cmd);
43 	buf = wmi_buf_alloc(wmi_handle, len);
44 	if (!buf) {
45 		return QDF_STATUS_E_NOMEM;
46 	}
47 
48 	buf_ptr = (uint8_t *)wmi_buf_data(buf);
49 	cmd = (wmi_ocb_set_utc_time_cmd_fixed_param *)buf_ptr;
50 	WMITLV_SET_HDR(&cmd->tlv_header,
51 		WMITLV_TAG_STRUC_wmi_ocb_set_utc_time_cmd_fixed_param,
52 		WMITLV_GET_STRUCT_TLVLEN(wmi_ocb_set_utc_time_cmd_fixed_param));
53 	cmd->vdev_id = utc->vdev_id;
54 
55 	for (i = 0; i < SIZE_UTC_TIME; i++)
56 		WMI_UTC_TIME_SET(cmd, i, utc->utc_time[i]);
57 
58 	for (i = 0; i < SIZE_UTC_TIME_ERROR; i++)
59 		WMI_TIME_ERROR_SET(cmd, i, utc->time_error[i]);
60 
61 	wmi_mtrace(WMI_OCB_SET_UTC_TIME_CMDID, cmd->vdev_id, 0);
62 	ret = wmi_unified_cmd_send(wmi_handle, buf, len,
63 				   WMI_OCB_SET_UTC_TIME_CMDID);
64 	if (QDF_IS_STATUS_ERROR(ret)) {
65 		wmi_err("Failed to set OCB UTC time");
66 		wmi_buf_free(buf);
67 	}
68 
69 	return ret;
70 }
71 
72 /**
73  * send_ocb_start_timing_advert_cmd_tlv() - start sending the timing advertisement
74  *				   frames on a channel
75  * @wmi_handle: pointer to the wmi handle
76  * @timing_advert: pointer to the timing advertisement struct
77  *
78  * Return: 0 on success
79  */
80 static QDF_STATUS send_ocb_start_timing_advert_cmd_tlv(wmi_unified_t wmi_handle,
81 				struct ocb_timing_advert_param *timing_advert)
82 {
83 	QDF_STATUS ret;
84 	wmi_ocb_start_timing_advert_cmd_fixed_param *cmd;
85 	uint8_t *buf_ptr;
86 	uint32_t len, len_template;
87 	wmi_buf_t buf;
88 
89 	len = sizeof(*cmd) +
90 		     WMI_TLV_HDR_SIZE;
91 
92 	len_template = timing_advert->template_length;
93 	/* Add padding to the template if needed */
94 	if (len_template % 4 != 0)
95 		len_template += 4 - (len_template % 4);
96 	len += len_template;
97 
98 	buf = wmi_buf_alloc(wmi_handle, len);
99 	if (!buf) {
100 		return QDF_STATUS_E_NOMEM;
101 	}
102 
103 	buf_ptr = (uint8_t *)wmi_buf_data(buf);
104 	cmd = (wmi_ocb_start_timing_advert_cmd_fixed_param *)buf_ptr;
105 	WMITLV_SET_HDR(&cmd->tlv_header,
106 		WMITLV_TAG_STRUC_wmi_ocb_start_timing_advert_cmd_fixed_param,
107 		WMITLV_GET_STRUCT_TLVLEN(
108 			wmi_ocb_start_timing_advert_cmd_fixed_param));
109 	cmd->vdev_id = timing_advert->vdev_id;
110 	cmd->repeat_rate = timing_advert->repeat_rate;
111 	cmd->channel_freq = timing_advert->chan_freq;
112 	cmd->timestamp_offset = timing_advert->timestamp_offset;
113 	cmd->time_value_offset = timing_advert->time_value_offset;
114 	cmd->timing_advert_template_length = timing_advert->template_length;
115 	buf_ptr += sizeof(*cmd);
116 
117 	/* Add the timing advert template */
118 	WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_BYTE,
119 		       len_template);
120 	qdf_mem_copy(buf_ptr + WMI_TLV_HDR_SIZE,
121 		     (uint8_t *)timing_advert->template_value,
122 		     timing_advert->template_length);
123 
124 	wmi_mtrace(WMI_OCB_START_TIMING_ADVERT_CMDID, cmd->vdev_id, 0);
125 	ret = wmi_unified_cmd_send(wmi_handle, buf, len,
126 				   WMI_OCB_START_TIMING_ADVERT_CMDID);
127 	if (QDF_IS_STATUS_ERROR(ret)) {
128 		wmi_err("Failed to start OCB timing advert");
129 		wmi_buf_free(buf);
130 	}
131 
132 	return ret;
133 }
134 
135 /**
136  * send_ocb_stop_timing_advert_cmd_tlv() - stop sending the timing advertisement frames
137  *				  on a channel
138  * @wmi_handle: pointer to the wmi handle
139  * @timing_advert: pointer to the timing advertisement struct
140  *
141  * Return: 0 on success
142  */
143 static QDF_STATUS send_ocb_stop_timing_advert_cmd_tlv(wmi_unified_t wmi_handle,
144 	struct ocb_timing_advert_param *timing_advert)
145 {
146 	QDF_STATUS ret;
147 	wmi_ocb_stop_timing_advert_cmd_fixed_param *cmd;
148 	uint8_t *buf_ptr;
149 	uint32_t len;
150 	wmi_buf_t buf;
151 
152 	len = sizeof(*cmd);
153 	buf = wmi_buf_alloc(wmi_handle, len);
154 	if (!buf) {
155 		return QDF_STATUS_E_NOMEM;
156 	}
157 
158 	buf_ptr = (uint8_t *)wmi_buf_data(buf);
159 	cmd = (wmi_ocb_stop_timing_advert_cmd_fixed_param *)buf_ptr;
160 	WMITLV_SET_HDR(&cmd->tlv_header,
161 		WMITLV_TAG_STRUC_wmi_ocb_stop_timing_advert_cmd_fixed_param,
162 		WMITLV_GET_STRUCT_TLVLEN(
163 			wmi_ocb_stop_timing_advert_cmd_fixed_param));
164 	cmd->vdev_id = timing_advert->vdev_id;
165 	cmd->channel_freq = timing_advert->chan_freq;
166 
167 	wmi_mtrace(WMI_OCB_STOP_TIMING_ADVERT_CMDID, cmd->vdev_id, 0);
168 	ret = wmi_unified_cmd_send(wmi_handle, buf, len,
169 				   WMI_OCB_STOP_TIMING_ADVERT_CMDID);
170 	if (QDF_IS_STATUS_ERROR(ret)) {
171 		wmi_err("Failed to stop OCB timing advert");
172 		wmi_buf_free(buf);
173 	}
174 
175 	return ret;
176 }
177 
178 /**
179  * send_ocb_get_tsf_timer_cmd_tlv() - get ocb tsf timer val
180  * @wmi_handle: pointer to the wmi handle
181  * @vdev_id: vdev identifier
182  *
183  * Return: 0 on success
184  */
185 static QDF_STATUS send_ocb_get_tsf_timer_cmd_tlv(wmi_unified_t wmi_handle,
186 			  uint8_t vdev_id)
187 {
188 	QDF_STATUS ret;
189 	wmi_ocb_get_tsf_timer_cmd_fixed_param *cmd;
190 	uint8_t *buf_ptr;
191 	wmi_buf_t buf;
192 	int32_t len;
193 
194 	len = sizeof(*cmd);
195 	buf = wmi_buf_alloc(wmi_handle, len);
196 	if (!buf) {
197 		return QDF_STATUS_E_NOMEM;
198 	}
199 	buf_ptr = (uint8_t *)wmi_buf_data(buf);
200 
201 	cmd = (wmi_ocb_get_tsf_timer_cmd_fixed_param *)buf_ptr;
202 	qdf_mem_zero(cmd, len);
203 	WMITLV_SET_HDR(&cmd->tlv_header,
204 		WMITLV_TAG_STRUC_wmi_ocb_get_tsf_timer_cmd_fixed_param,
205 		WMITLV_GET_STRUCT_TLVLEN(
206 			wmi_ocb_get_tsf_timer_cmd_fixed_param));
207 	cmd->vdev_id = vdev_id;
208 
209 	/* Send the WMI command */
210 	wmi_mtrace(WMI_OCB_GET_TSF_TIMER_CMDID, cmd->vdev_id, 0);
211 	ret = wmi_unified_cmd_send(wmi_handle, buf, len,
212 				   WMI_OCB_GET_TSF_TIMER_CMDID);
213 	/* If there is an error, set the completion event */
214 	if (QDF_IS_STATUS_ERROR(ret)) {
215 		wmi_err("Failed to send WMI message: %d", ret);
216 		wmi_buf_free(buf);
217 	}
218 
219 	return ret;
220 }
221 
222 /**
223  * send_dcc_get_stats_cmd_tlv() - get the DCC channel stats
224  * @wmi_handle: pointer to the wmi handle
225  * @get_stats_param: pointer to the dcc stats
226  *
227  * Return: 0 on success
228  */
229 static QDF_STATUS send_dcc_get_stats_cmd_tlv(wmi_unified_t wmi_handle,
230 		     struct ocb_dcc_get_stats_param *get_stats_param)
231 {
232 	QDF_STATUS ret;
233 	wmi_dcc_get_stats_cmd_fixed_param *cmd;
234 	wmi_dcc_channel_stats_request *channel_stats_array;
235 	wmi_buf_t buf;
236 	uint8_t *buf_ptr;
237 	uint32_t len;
238 	uint32_t i;
239 
240 	/* Validate the input */
241 	if (get_stats_param->request_array_len !=
242 	    get_stats_param->channel_count * sizeof(*channel_stats_array)) {
243 		wmi_err("Invalid parameter");
244 		return QDF_STATUS_E_INVAL;
245 	}
246 
247 	/* Allocate memory for the WMI command */
248 	len = sizeof(*cmd) + WMI_TLV_HDR_SIZE +
249 		get_stats_param->request_array_len;
250 
251 	buf = wmi_buf_alloc(wmi_handle, len);
252 	if (!buf) {
253 		return QDF_STATUS_E_NOMEM;
254 	}
255 
256 	buf_ptr = wmi_buf_data(buf);
257 	qdf_mem_zero(buf_ptr, len);
258 
259 	/* Populate the WMI command */
260 	cmd = (wmi_dcc_get_stats_cmd_fixed_param *)buf_ptr;
261 	buf_ptr += sizeof(*cmd);
262 
263 	WMITLV_SET_HDR(&cmd->tlv_header,
264 		       WMITLV_TAG_STRUC_wmi_dcc_get_stats_cmd_fixed_param,
265 		       WMITLV_GET_STRUCT_TLVLEN(
266 			   wmi_dcc_get_stats_cmd_fixed_param));
267 	cmd->vdev_id = get_stats_param->vdev_id;
268 	cmd->num_channels = get_stats_param->channel_count;
269 
270 	WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_STRUC,
271 		       get_stats_param->request_array_len);
272 	buf_ptr += WMI_TLV_HDR_SIZE;
273 
274 	channel_stats_array = (wmi_dcc_channel_stats_request *)buf_ptr;
275 	qdf_mem_copy(channel_stats_array, get_stats_param->request_array,
276 		     get_stats_param->request_array_len);
277 	for (i = 0; i < cmd->num_channels; i++)
278 		WMITLV_SET_HDR(&channel_stats_array[i].tlv_header,
279 			       WMITLV_TAG_STRUC_wmi_dcc_channel_stats_request,
280 			       WMITLV_GET_STRUCT_TLVLEN(
281 					wmi_dcc_channel_stats_request));
282 
283 	/* Send the WMI command */
284 	wmi_mtrace(WMI_DCC_GET_STATS_CMDID, cmd->vdev_id, 0);
285 	ret = wmi_unified_cmd_send(wmi_handle, buf, len,
286 				   WMI_DCC_GET_STATS_CMDID);
287 
288 	if (QDF_IS_STATUS_ERROR(ret)) {
289 		wmi_err("Failed to send WMI message: %d", ret);
290 		wmi_buf_free(buf);
291 	}
292 
293 	return ret;
294 }
295 
296 /**
297  * send_dcc_clear_stats_cmd_tlv() - command to clear the DCC stats
298  * @wmi_handle: pointer to the wmi handle
299  * @vdev_id: vdev id
300  * @dcc_stats_bitmap: dcc status bitmap
301  *
302  * Return: 0 on success
303  */
304 static QDF_STATUS send_dcc_clear_stats_cmd_tlv(wmi_unified_t wmi_handle,
305 				uint32_t vdev_id, uint32_t dcc_stats_bitmap)
306 {
307 	QDF_STATUS ret;
308 	wmi_dcc_clear_stats_cmd_fixed_param *cmd;
309 	wmi_buf_t buf;
310 	uint8_t *buf_ptr;
311 	uint32_t len;
312 
313 	/* Allocate memory for the WMI command */
314 	len = sizeof(*cmd);
315 
316 	buf = wmi_buf_alloc(wmi_handle, len);
317 	if (!buf) {
318 		return QDF_STATUS_E_NOMEM;
319 	}
320 
321 	buf_ptr = wmi_buf_data(buf);
322 	qdf_mem_zero(buf_ptr, len);
323 
324 	/* Populate the WMI command */
325 	cmd = (wmi_dcc_clear_stats_cmd_fixed_param *)buf_ptr;
326 
327 	WMITLV_SET_HDR(&cmd->tlv_header,
328 		       WMITLV_TAG_STRUC_wmi_dcc_clear_stats_cmd_fixed_param,
329 		       WMITLV_GET_STRUCT_TLVLEN(
330 			   wmi_dcc_clear_stats_cmd_fixed_param));
331 	cmd->vdev_id = vdev_id;
332 	cmd->dcc_stats_bitmap = dcc_stats_bitmap;
333 
334 	/* Send the WMI command */
335 	wmi_mtrace(WMI_DCC_CLEAR_STATS_CMDID, cmd->vdev_id, 0);
336 	ret = wmi_unified_cmd_send(wmi_handle, buf, len,
337 				   WMI_DCC_CLEAR_STATS_CMDID);
338 	if (QDF_IS_STATUS_ERROR(ret)) {
339 		wmi_err("Failed to send the WMI command");
340 		wmi_buf_free(buf);
341 	}
342 
343 	return ret;
344 }
345 
346 /**
347  * send_dcc_update_ndl_cmd_tlv() - command to update the NDL data
348  * @wmi_handle: pointer to the wmi handle
349  * @update_ndl_param: pointer to the request parameters
350  *
351  * Return: 0 on success
352  */
353 static QDF_STATUS send_dcc_update_ndl_cmd_tlv(wmi_unified_t wmi_handle,
354 		       struct ocb_dcc_update_ndl_param *update_ndl_param)
355 {
356 	QDF_STATUS qdf_status;
357 	wmi_dcc_update_ndl_cmd_fixed_param *cmd;
358 	wmi_dcc_ndl_chan *ndl_chan_array;
359 	wmi_dcc_ndl_active_state_config *ndl_active_state_array;
360 	uint32_t active_state_count;
361 	wmi_buf_t buf;
362 	uint8_t *buf_ptr;
363 	uint32_t len;
364 	uint32_t i;
365 
366 	/* validate the input */
367 	if (update_ndl_param->dcc_ndl_chan_list_len !=
368 	    update_ndl_param->channel_count * sizeof(*ndl_chan_array)) {
369 		wmi_err("Invalid parameter");
370 		return QDF_STATUS_E_INVAL;
371 	}
372 	active_state_count = 0;
373 	ndl_chan_array = update_ndl_param->dcc_ndl_chan_list;
374 	for (i = 0; i < update_ndl_param->channel_count; i++)
375 		active_state_count +=
376 			WMI_NDL_NUM_ACTIVE_STATE_GET(&ndl_chan_array[i]);
377 	if (update_ndl_param->dcc_ndl_active_state_list_len !=
378 	    active_state_count * sizeof(*ndl_active_state_array)) {
379 		wmi_err("Invalid parameter");
380 		return QDF_STATUS_E_INVAL;
381 	}
382 
383 	/* Allocate memory for the WMI command */
384 	len = sizeof(*cmd) +
385 		WMI_TLV_HDR_SIZE + update_ndl_param->dcc_ndl_chan_list_len +
386 		WMI_TLV_HDR_SIZE +
387 		update_ndl_param->dcc_ndl_active_state_list_len;
388 
389 	buf = wmi_buf_alloc(wmi_handle, len);
390 	if (!buf) {
391 		return QDF_STATUS_E_NOMEM;
392 	}
393 
394 	buf_ptr = wmi_buf_data(buf);
395 	qdf_mem_zero(buf_ptr, len);
396 
397 	/* Populate the WMI command */
398 	cmd = (wmi_dcc_update_ndl_cmd_fixed_param *)buf_ptr;
399 	buf_ptr += sizeof(*cmd);
400 
401 	WMITLV_SET_HDR(&cmd->tlv_header,
402 		       WMITLV_TAG_STRUC_wmi_dcc_update_ndl_cmd_fixed_param,
403 		       WMITLV_GET_STRUCT_TLVLEN(
404 			   wmi_dcc_update_ndl_cmd_fixed_param));
405 	cmd->vdev_id = update_ndl_param->vdev_id;
406 	cmd->num_channel = update_ndl_param->channel_count;
407 
408 	WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_STRUC,
409 		       update_ndl_param->dcc_ndl_chan_list_len);
410 	buf_ptr += WMI_TLV_HDR_SIZE;
411 
412 	ndl_chan_array = (wmi_dcc_ndl_chan *)buf_ptr;
413 	qdf_mem_copy(ndl_chan_array, update_ndl_param->dcc_ndl_chan_list,
414 		     update_ndl_param->dcc_ndl_chan_list_len);
415 	for (i = 0; i < cmd->num_channel; i++)
416 		WMITLV_SET_HDR(&ndl_chan_array[i].tlv_header,
417 			       WMITLV_TAG_STRUC_wmi_dcc_ndl_chan,
418 			       WMITLV_GET_STRUCT_TLVLEN(
419 					wmi_dcc_ndl_chan));
420 	buf_ptr += update_ndl_param->dcc_ndl_chan_list_len;
421 
422 	WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_STRUC,
423 		       update_ndl_param->dcc_ndl_active_state_list_len);
424 	buf_ptr += WMI_TLV_HDR_SIZE;
425 
426 	ndl_active_state_array = (wmi_dcc_ndl_active_state_config *)buf_ptr;
427 	qdf_mem_copy(ndl_active_state_array,
428 		     update_ndl_param->dcc_ndl_active_state_list,
429 		     update_ndl_param->dcc_ndl_active_state_list_len);
430 	for (i = 0; i < active_state_count; i++) {
431 		WMITLV_SET_HDR(&ndl_active_state_array[i].tlv_header,
432 			WMITLV_TAG_STRUC_wmi_dcc_ndl_active_state_config,
433 			WMITLV_GET_STRUCT_TLVLEN(
434 			    wmi_dcc_ndl_active_state_config));
435 	}
436 	buf_ptr += update_ndl_param->dcc_ndl_active_state_list_len;
437 
438 	/* Send the WMI command */
439 	wmi_mtrace(WMI_DCC_UPDATE_NDL_CMDID, cmd->vdev_id, 0);
440 	qdf_status = wmi_unified_cmd_send(wmi_handle, buf, len,
441 					  WMI_DCC_UPDATE_NDL_CMDID);
442 	/* If there is an error, set the completion event */
443 	if (QDF_IS_STATUS_ERROR(qdf_status)) {
444 		wmi_err("Failed to send WMI message: %d", qdf_status);
445 		wmi_buf_free(buf);
446 	}
447 
448 	return qdf_status;
449 }
450 
451 /**
452  * send_ocb_set_config_cmd_tlv() - send the OCB config to the FW
453  * @wmi_handle: pointer to the wmi handle
454  * @config: the OCB configuration
455  *
456  * Return: 0 on success
457  */
458 static QDF_STATUS send_ocb_set_config_cmd_tlv(wmi_unified_t wmi_handle,
459 					      struct ocb_config *config)
460 {
461 	QDF_STATUS ret;
462 	wmi_ocb_set_config_cmd_fixed_param *cmd;
463 	wmi_channel *chan;
464 	wmi_ocb_channel *ocb_chan;
465 	wmi_qos_parameter *qos_param;
466 	wmi_dcc_ndl_chan *ndl_chan;
467 	wmi_dcc_ndl_active_state_config *ndl_active_config;
468 	wmi_ocb_schedule_element *sched_elem;
469 	uint8_t *buf_ptr;
470 	wmi_buf_t buf;
471 	int32_t len;
472 	int32_t i, j, active_state_count;
473 
474 	/*
475 	 * Validate the dcc_ndl_chan_list_len and count the number of active
476 	 * states. Validate dcc_ndl_active_state_list_len.
477 	 */
478 	active_state_count = 0;
479 	if (config->dcc_ndl_chan_list_len) {
480 		if (!config->dcc_ndl_chan_list ||
481 			config->dcc_ndl_chan_list_len !=
482 			config->channel_count * sizeof(wmi_dcc_ndl_chan)) {
483 			wmi_err("NDL channel is invalid. List len: %d",
484 				 config->dcc_ndl_chan_list_len);
485 			return QDF_STATUS_E_INVAL;
486 		}
487 
488 		for (i = 0, ndl_chan = config->dcc_ndl_chan_list;
489 				i < config->channel_count; ++i, ++ndl_chan)
490 			active_state_count +=
491 				WMI_NDL_NUM_ACTIVE_STATE_GET(ndl_chan);
492 
493 		if (active_state_count) {
494 			if (!config->dcc_ndl_active_state_list ||
495 				config->dcc_ndl_active_state_list_len !=
496 				active_state_count *
497 				sizeof(wmi_dcc_ndl_active_state_config)) {
498 				wmi_err("NDL active state is invalid");
499 				return QDF_STATUS_E_INVAL;
500 			}
501 		}
502 	}
503 
504 	len = sizeof(*cmd) +
505 		WMI_TLV_HDR_SIZE + config->channel_count *
506 			sizeof(wmi_channel) +
507 		WMI_TLV_HDR_SIZE + config->channel_count *
508 			sizeof(wmi_ocb_channel) +
509 		WMI_TLV_HDR_SIZE + config->channel_count *
510 			sizeof(wmi_qos_parameter) * WMI_MAX_NUM_AC +
511 		WMI_TLV_HDR_SIZE + config->dcc_ndl_chan_list_len +
512 		WMI_TLV_HDR_SIZE + active_state_count *
513 			sizeof(wmi_dcc_ndl_active_state_config) +
514 		WMI_TLV_HDR_SIZE + config->schedule_size *
515 			sizeof(wmi_ocb_schedule_element);
516 	buf = wmi_buf_alloc(wmi_handle, len);
517 	if (!buf) {
518 		return QDF_STATUS_E_NOMEM;
519 	}
520 
521 	buf_ptr = (uint8_t *)wmi_buf_data(buf);
522 	cmd = (wmi_ocb_set_config_cmd_fixed_param *)buf_ptr;
523 	WMITLV_SET_HDR(&cmd->tlv_header,
524 		WMITLV_TAG_STRUC_wmi_ocb_set_config_cmd_fixed_param,
525 		WMITLV_GET_STRUCT_TLVLEN(wmi_ocb_set_config_cmd_fixed_param));
526 	cmd->vdev_id = config->vdev_id;
527 	cmd->channel_count = config->channel_count;
528 	cmd->schedule_size = config->schedule_size;
529 	cmd->flags = config->flags;
530 	buf_ptr += sizeof(*cmd);
531 
532 	/* Add the wmi_channel info */
533 	WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_STRUC,
534 		       config->channel_count * sizeof(wmi_channel));
535 	buf_ptr += WMI_TLV_HDR_SIZE;
536 	for (i = 0; i < config->channel_count; i++) {
537 		chan = (wmi_channel *)buf_ptr;
538 		WMITLV_SET_HDR(&chan->tlv_header,
539 			       WMITLV_TAG_STRUC_wmi_channel,
540 			       WMITLV_GET_STRUCT_TLVLEN(wmi_channel));
541 		chan->mhz = config->channels[i].chan_freq;
542 		chan->band_center_freq1 = config->channels[i].chan_freq;
543 		chan->band_center_freq2 = 0;
544 		chan->info = 0;
545 
546 		WMI_SET_CHANNEL_MODE(chan, config->channels[i].ch_mode);
547 		WMI_SET_CHANNEL_MAX_POWER(chan, config->channels[i].max_pwr);
548 		WMI_SET_CHANNEL_MIN_POWER(chan, config->channels[i].min_pwr);
549 		WMI_SET_CHANNEL_MAX_TX_POWER(chan, config->channels[i].max_pwr);
550 		WMI_SET_CHANNEL_REG_POWER(chan, config->channels[i].reg_pwr);
551 		WMI_SET_CHANNEL_ANTENNA_MAX(chan,
552 					    config->channels[i].antenna_max);
553 
554 		if (config->channels[i].bandwidth < 10)
555 			WMI_SET_CHANNEL_FLAG(chan, WMI_CHAN_FLAG_QUARTER_RATE);
556 		else if (config->channels[i].bandwidth < 20)
557 			WMI_SET_CHANNEL_FLAG(chan, WMI_CHAN_FLAG_HALF_RATE);
558 		buf_ptr += sizeof(*chan);
559 	}
560 
561 	/* Add the wmi_ocb_channel info */
562 	WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_STRUC,
563 		       config->channel_count * sizeof(wmi_ocb_channel));
564 	buf_ptr += WMI_TLV_HDR_SIZE;
565 	for (i = 0; i < config->channel_count; i++) {
566 		ocb_chan = (wmi_ocb_channel *)buf_ptr;
567 		WMITLV_SET_HDR(&ocb_chan->tlv_header,
568 			       WMITLV_TAG_STRUC_wmi_ocb_channel,
569 			       WMITLV_GET_STRUCT_TLVLEN(wmi_ocb_channel));
570 		ocb_chan->bandwidth = config->channels[i].bandwidth;
571 		WMI_CHAR_ARRAY_TO_MAC_ADDR(
572 					config->channels[i].mac_address.bytes,
573 					&ocb_chan->mac_address);
574 		buf_ptr += sizeof(*ocb_chan);
575 	}
576 
577 	/* Add the wmi_qos_parameter info */
578 	WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_STRUC,
579 		config->channel_count * sizeof(wmi_qos_parameter)*WMI_MAX_NUM_AC);
580 	buf_ptr += WMI_TLV_HDR_SIZE;
581 	/* WMI_MAX_NUM_AC parameters for each channel */
582 	for (i = 0; i < config->channel_count; i++) {
583 		for (j = 0; j < WMI_MAX_NUM_AC; j++) {
584 			qos_param = (wmi_qos_parameter *)buf_ptr;
585 			WMITLV_SET_HDR(&qos_param->tlv_header,
586 				WMITLV_TAG_STRUC_wmi_qos_parameter,
587 				WMITLV_GET_STRUCT_TLVLEN(wmi_qos_parameter));
588 			qos_param->aifsn =
589 				config->channels[i].qos_params[j].aifsn;
590 			qos_param->cwmin =
591 				config->channels[i].qos_params[j].cwmin;
592 			qos_param->cwmax =
593 				config->channels[i].qos_params[j].cwmax;
594 			buf_ptr += sizeof(*qos_param);
595 		}
596 	}
597 
598 	/* Add the wmi_dcc_ndl_chan (per channel) */
599 	WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_STRUC,
600 		       config->dcc_ndl_chan_list_len);
601 	buf_ptr += WMI_TLV_HDR_SIZE;
602 	if (config->dcc_ndl_chan_list_len) {
603 		ndl_chan = (wmi_dcc_ndl_chan *)buf_ptr;
604 		qdf_mem_copy(ndl_chan, config->dcc_ndl_chan_list,
605 			     config->dcc_ndl_chan_list_len);
606 		for (i = 0; i < config->channel_count; i++)
607 			WMITLV_SET_HDR(&(ndl_chan[i].tlv_header),
608 				WMITLV_TAG_STRUC_wmi_dcc_ndl_chan,
609 				WMITLV_GET_STRUCT_TLVLEN(wmi_dcc_ndl_chan));
610 		buf_ptr += config->dcc_ndl_chan_list_len;
611 	}
612 
613 	/* Add the wmi_dcc_ndl_active_state_config */
614 	WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_STRUC, active_state_count *
615 		       sizeof(wmi_dcc_ndl_active_state_config));
616 	buf_ptr += WMI_TLV_HDR_SIZE;
617 	if (active_state_count) {
618 		ndl_active_config = (wmi_dcc_ndl_active_state_config *)buf_ptr;
619 		qdf_mem_copy(ndl_active_config,
620 			config->dcc_ndl_active_state_list,
621 			active_state_count * sizeof(*ndl_active_config));
622 		for (i = 0; i < active_state_count; ++i)
623 			WMITLV_SET_HDR(&(ndl_active_config[i].tlv_header),
624 			  WMITLV_TAG_STRUC_wmi_dcc_ndl_active_state_config,
625 			  WMITLV_GET_STRUCT_TLVLEN(
626 				wmi_dcc_ndl_active_state_config));
627 		buf_ptr += active_state_count *
628 			sizeof(*ndl_active_config);
629 	}
630 
631 	/* Add the wmi_ocb_schedule_element info */
632 	WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_STRUC,
633 		config->schedule_size * sizeof(wmi_ocb_schedule_element));
634 	buf_ptr += WMI_TLV_HDR_SIZE;
635 	for (i = 0; i < config->schedule_size; i++) {
636 		sched_elem = (wmi_ocb_schedule_element *)buf_ptr;
637 		WMITLV_SET_HDR(&sched_elem->tlv_header,
638 			WMITLV_TAG_STRUC_wmi_ocb_schedule_element,
639 			WMITLV_GET_STRUCT_TLVLEN(wmi_ocb_schedule_element));
640 		sched_elem->channel_freq = config->schedule[i].chan_freq;
641 		sched_elem->total_duration = config->schedule[i].total_duration;
642 		sched_elem->guard_interval = config->schedule[i].guard_interval;
643 		buf_ptr += sizeof(*sched_elem);
644 	}
645 
646 	wmi_mtrace(WMI_OCB_SET_CONFIG_CMDID, cmd->vdev_id, 0);
647 	ret = wmi_unified_cmd_send(wmi_handle, buf, len,
648 				   WMI_OCB_SET_CONFIG_CMDID);
649 	if (QDF_IS_STATUS_ERROR(ret)) {
650 		wmi_err("Failed to set OCB config");
651 		wmi_buf_free(buf);
652 	}
653 
654 	return ret;
655 }
656 
657 /**
658  * extract_ocb_channel_config_resp_tlv() - extract ocb channel config resp
659  * @wmi_handle: wmi handle
660  * @evt_buf: wmi event buffer
661  * @status: status buffer
662  *
663  * Return: QDF_STATUS_SUCCESS on success
664  */
665 static QDF_STATUS extract_ocb_channel_config_resp_tlv(wmi_unified_t wmi_handle,
666 						      void *evt_buf,
667 						      uint32_t *status)
668 {
669 	WMI_OCB_SET_CONFIG_RESP_EVENTID_param_tlvs *param_tlvs;
670 	wmi_ocb_set_config_resp_event_fixed_param *fix_param;
671 
672 	param_tlvs = evt_buf;
673 	fix_param = param_tlvs->fixed_param;
674 
675 	*status = fix_param->status;
676 	return QDF_STATUS_SUCCESS;
677 }
678 
679 /**
680  * extract_ocb_tsf_timer_tlv() - extract TSF timer from event buffer
681  * @wmi_handle: wmi handle
682  * @evt_buf: wmi event buffer
683  * @resp: response buffer
684  *
685  * Return: QDF_STATUS_SUCCESS on success
686  */
687 static QDF_STATUS extract_ocb_tsf_timer_tlv(wmi_unified_t wmi_handle,
688 			void *evt_buf, struct ocb_get_tsf_timer_response *resp)
689 {
690 	WMI_OCB_GET_TSF_TIMER_RESP_EVENTID_param_tlvs *param_tlvs;
691 	wmi_ocb_get_tsf_timer_resp_event_fixed_param *fix_param;
692 
693 	param_tlvs = evt_buf;
694 	fix_param = param_tlvs->fixed_param;
695 	resp->vdev_id = fix_param->vdev_id;
696 	resp->timer_high = fix_param->tsf_timer_high;
697 	resp->timer_low = fix_param->tsf_timer_low;
698 
699 	return QDF_STATUS_SUCCESS;
700 }
701 
702 /**
703  * extract_ocb_ndl_resp_tlv() - extract TSF timer from event buffer
704  * @wmi_handle: wmi handle
705  * @evt_buf: wmi event buffer
706  * @resp: response buffer
707  *
708  * Return: QDF_STATUS_SUCCESS on success
709  */
710 static QDF_STATUS extract_ocb_ndl_resp_tlv(wmi_unified_t wmi_handle,
711 		void *evt_buf, struct ocb_dcc_update_ndl_response *resp)
712 {
713 	WMI_DCC_UPDATE_NDL_RESP_EVENTID_param_tlvs *param_tlvs;
714 	wmi_dcc_update_ndl_resp_event_fixed_param *fix_param;
715 
716 	param_tlvs = evt_buf;
717 	fix_param = param_tlvs->fixed_param;
718 	resp->vdev_id = fix_param->vdev_id;
719 	resp->status = fix_param->status;
720 	return QDF_STATUS_SUCCESS;
721 }
722 
723 /**
724  * extract_ocb_dcc_stats_tlv() - extract DCC stats from event buffer
725  * @wmi_handle: wmi handle
726  * @evt_buf: wmi event buffer
727  * @resp: response buffer
728  *
729  * Since length of stats is variable, buffer for DCC stats will be allocated
730  * in this function. The caller must free the buffer.
731  *
732  * Return: QDF_STATUS_SUCCESS on success
733  */
734 static QDF_STATUS extract_ocb_dcc_stats_tlv(wmi_unified_t wmi_handle,
735 		void *evt_buf, struct ocb_dcc_get_stats_response **resp)
736 {
737 	struct ocb_dcc_get_stats_response *response;
738 	WMI_DCC_GET_STATS_RESP_EVENTID_param_tlvs *param_tlvs;
739 	wmi_dcc_get_stats_resp_event_fixed_param *fix_param;
740 
741 	param_tlvs = (WMI_DCC_GET_STATS_RESP_EVENTID_param_tlvs *)evt_buf;
742 	fix_param = param_tlvs->fixed_param;
743 
744 	/* Allocate and populate the response */
745 	if (fix_param->num_channels > ((WMI_SVC_MSG_MAX_SIZE -
746 	    sizeof(*fix_param)) / sizeof(wmi_dcc_ndl_stats_per_channel)) ||
747 	    fix_param->num_channels > param_tlvs->num_stats_per_channel_list) {
748 		wmi_warn("Too many channels:%d actual:%d",
749 			 fix_param->num_channels,
750 			 param_tlvs->num_stats_per_channel_list);
751 		*resp = NULL;
752 		return QDF_STATUS_E_INVAL;
753 	}
754 	response = qdf_mem_malloc(sizeof(*response) + fix_param->num_channels *
755 				  sizeof(wmi_dcc_ndl_stats_per_channel));
756 	*resp = response;
757 	if (!response)
758 		return  QDF_STATUS_E_NOMEM;
759 
760 	response->vdev_id = fix_param->vdev_id;
761 	response->num_channels = fix_param->num_channels;
762 	response->channel_stats_array_len =
763 		fix_param->num_channels *
764 		sizeof(wmi_dcc_ndl_stats_per_channel);
765 	response->channel_stats_array = ((uint8_t *)response) +
766 					sizeof(*response);
767 	qdf_mem_copy(response->channel_stats_array,
768 		     param_tlvs->stats_per_channel_list,
769 		     response->channel_stats_array_len);
770 
771 	return QDF_STATUS_SUCCESS;
772 }
773 
774 void wmi_ocb_attach_tlv(wmi_unified_t wmi_handle)
775 {
776 	struct wmi_ops *ops = wmi_handle->ops;
777 
778 	ops->send_ocb_set_utc_time_cmd = send_ocb_set_utc_time_cmd_tlv;
779 	ops->send_ocb_get_tsf_timer_cmd = send_ocb_get_tsf_timer_cmd_tlv;
780 	ops->send_dcc_clear_stats_cmd = send_dcc_clear_stats_cmd_tlv;
781 	ops->send_dcc_get_stats_cmd = send_dcc_get_stats_cmd_tlv;
782 	ops->send_dcc_update_ndl_cmd = send_dcc_update_ndl_cmd_tlv;
783 	ops->send_ocb_set_config_cmd = send_ocb_set_config_cmd_tlv;
784 	ops->send_ocb_stop_timing_advert_cmd =
785 			send_ocb_stop_timing_advert_cmd_tlv;
786 	ops->send_ocb_start_timing_advert_cmd =
787 			send_ocb_start_timing_advert_cmd_tlv;
788 	ops->extract_ocb_chan_config_resp =
789 			extract_ocb_channel_config_resp_tlv;
790 	ops->extract_ocb_tsf_timer = extract_ocb_tsf_timer_tlv;
791 	ops->extract_dcc_update_ndl_resp = extract_ocb_ndl_resp_tlv;
792 	ops->extract_dcc_stats = extract_ocb_dcc_stats_tlv;
793 }
794