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 */
send_ocb_set_utc_time_cmd_tlv(wmi_unified_t wmi_handle,struct ocb_utc_param * utc)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 */
send_ocb_start_timing_advert_cmd_tlv(wmi_unified_t wmi_handle,struct ocb_timing_advert_param * timing_advert)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 */
send_ocb_stop_timing_advert_cmd_tlv(wmi_unified_t wmi_handle,struct ocb_timing_advert_param * timing_advert)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 */
send_ocb_get_tsf_timer_cmd_tlv(wmi_unified_t wmi_handle,uint8_t vdev_id)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 */
send_dcc_get_stats_cmd_tlv(wmi_unified_t wmi_handle,struct ocb_dcc_get_stats_param * get_stats_param)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 */
send_dcc_clear_stats_cmd_tlv(wmi_unified_t wmi_handle,uint32_t vdev_id,uint32_t dcc_stats_bitmap)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 */
send_dcc_update_ndl_cmd_tlv(wmi_unified_t wmi_handle,struct ocb_dcc_update_ndl_param * update_ndl_param)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 */
send_ocb_set_config_cmd_tlv(wmi_unified_t wmi_handle,struct ocb_config * config)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 */
extract_ocb_channel_config_resp_tlv(wmi_unified_t wmi_handle,void * evt_buf,uint32_t * status)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 */
extract_ocb_tsf_timer_tlv(wmi_unified_t wmi_handle,void * evt_buf,struct ocb_get_tsf_timer_response * resp)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 */
extract_ocb_ndl_resp_tlv(wmi_unified_t wmi_handle,void * evt_buf,struct ocb_dcc_update_ndl_response * resp)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 */
extract_ocb_dcc_stats_tlv(wmi_unified_t wmi_handle,void * evt_buf,struct ocb_dcc_get_stats_response ** resp)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
wmi_ocb_attach_tlv(wmi_unified_t wmi_handle)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