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