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