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