1 /* 2 * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. 3 * Copyright (c) 2022 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_fwol_public_structs.h" 24 #include "wmi_unified_fwol_api.h" 25 26 #ifdef WLAN_FEATURE_ELNA 27 /** 28 * send_set_elna_bypass_cmd_tlv() - send set elna bypass cmd to fw 29 * @wmi_handle: wmi handle 30 * @req: set eLNA bypass request 31 * 32 * Send WMI_SET_ELNA_BYPASS_CMDID to fw. 33 * 34 * Return: QDF_STATUS 35 */ 36 static QDF_STATUS 37 send_set_elna_bypass_cmd_tlv(wmi_unified_t wmi_handle, 38 struct set_elna_bypass_request *req) 39 { 40 wmi_buf_t buf; 41 wmi_set_elna_bypass_cmd_fixed_param *cmd; 42 uint16_t len = sizeof(*cmd); 43 QDF_STATUS ret; 44 45 buf = wmi_buf_alloc(wmi_handle, len); 46 if (!buf) { 47 wmi_err("Failed to allocate wmi buffer"); 48 return QDF_STATUS_E_NOMEM; 49 } 50 51 cmd = (wmi_set_elna_bypass_cmd_fixed_param *)wmi_buf_data(buf); 52 WMITLV_SET_HDR(&cmd->tlv_header, 53 WMITLV_TAG_STRUC_wmi_set_elna_bypass_cmd_fixed_param, 54 WMITLV_GET_STRUCT_TLVLEN 55 (wmi_set_elna_bypass_cmd_fixed_param)); 56 cmd->vdev_id = req->vdev_id; 57 cmd->en_dis = req->elna_mode; 58 wmi_mtrace(WMI_SET_ELNA_BYPASS_CMDID, req->vdev_id, req->elna_mode); 59 ret = wmi_unified_cmd_send(wmi_handle, buf, len, 60 WMI_SET_ELNA_BYPASS_CMDID); 61 if (QDF_IS_STATUS_ERROR(ret)) { 62 wmi_err("Failed to send set param command ret = %d", ret); 63 wmi_buf_free(buf); 64 } 65 66 return ret; 67 } 68 69 /** 70 * send_get_elna_bypass_cmd_tlv() - send get elna bypass cmd to fw 71 * @wmi_handle: wmi handle 72 * @req: get eLNA bypass request 73 * 74 * Send WMI_GET_ELNA_BYPASS_CMDID to fw. 75 * 76 * Return: QDF_STATUS 77 */ 78 static QDF_STATUS 79 send_get_elna_bypass_cmd_tlv(wmi_unified_t wmi_handle, 80 struct get_elna_bypass_request *req) 81 { 82 wmi_buf_t buf; 83 wmi_get_elna_bypass_cmd_fixed_param *cmd; 84 uint16_t len = sizeof(*cmd); 85 QDF_STATUS ret; 86 87 buf = wmi_buf_alloc(wmi_handle, len); 88 if (!buf) { 89 wmi_err("Failed to allocate wmi buffer"); 90 return QDF_STATUS_E_NOMEM; 91 } 92 93 cmd = (wmi_get_elna_bypass_cmd_fixed_param *)wmi_buf_data(buf); 94 WMITLV_SET_HDR(&cmd->tlv_header, 95 WMITLV_TAG_STRUC_wmi_get_elna_bypass_cmd_fixed_param, 96 WMITLV_GET_STRUCT_TLVLEN 97 (wmi_get_elna_bypass_cmd_fixed_param)); 98 cmd->vdev_id = req->vdev_id; 99 wmi_mtrace(WMI_GET_ELNA_BYPASS_CMDID, req->vdev_id, 0); 100 ret = wmi_unified_cmd_send(wmi_handle, buf, len, 101 WMI_GET_ELNA_BYPASS_CMDID); 102 if (QDF_IS_STATUS_ERROR(ret)) { 103 wmi_err("Failed to send set param command ret = %d", ret); 104 wmi_buf_free(buf); 105 } 106 107 return ret; 108 } 109 110 /** 111 * extract_get_elna_bypass_resp_tlv() - Extract WMI get eLNA bypass response 112 * @wmi_handle: wmi handle 113 * @resp_buf: response buffer 114 * @resp: get eLNA bypass response 115 * 116 * Extract WMI get eLNA bypass response from firmware. 117 * 118 * Return: QDF_STATUS 119 */ 120 static QDF_STATUS 121 extract_get_elna_bypass_resp_tlv(struct wmi_unified *wmi_handle, void *resp_buf, 122 struct get_elna_bypass_response *resp) 123 { 124 WMI_GET_ELNA_BYPASS_EVENTID_param_tlvs *param_buf; 125 wmi_get_elna_bypass_event_fixed_param *evt; 126 127 param_buf = resp_buf; 128 evt = param_buf->fixed_param; 129 if (!evt) { 130 wmi_err("Invalid get elna bypass event"); 131 return QDF_STATUS_E_INVAL; 132 } 133 134 wmi_debug("Get elna bypass %d from vdev %d", 135 evt->en_dis, evt->vdev_id); 136 137 resp->vdev_id = evt->vdev_id; 138 resp->elna_mode = evt->en_dis; 139 140 return QDF_STATUS_SUCCESS; 141 } 142 #endif /* WLAN_FEATURE_ELNA */ 143 144 #ifdef WLAN_FEATURE_ELNA 145 static void wmi_fwol_attach_elna_tlv(struct wmi_ops *ops) 146 { 147 ops->send_set_elna_bypass_cmd = send_set_elna_bypass_cmd_tlv; 148 ops->send_get_elna_bypass_cmd = send_get_elna_bypass_cmd_tlv; 149 ops->extract_get_elna_bypass_resp = extract_get_elna_bypass_resp_tlv; 150 } 151 #else 152 static void wmi_fwol_attach_elna_tlv(struct wmi_ops *ops) 153 { 154 } 155 #endif /* WLAN_FEATURE_ELNA */ 156 157 #ifdef WLAN_SEND_DSCP_UP_MAP_TO_FW 158 /** 159 * send_dscp_tid_map_cmd_tlv() - send dscp to tid map cmd to fw 160 * @wmi_handle: wmi handle 161 * @dscp_to_tid_map: array of dscp to tid map values 162 * 163 * Send WMI_PDEV_SET_DSCP_TID_MAP_CMDID to fw. 164 * 165 * Return: QDF_STATUS 166 */ 167 static QDF_STATUS 168 send_dscp_tid_map_cmd_tlv(wmi_unified_t wmi_handle, 169 uint32_t *dscp_to_tid_map) 170 { 171 QDF_STATUS status; 172 wmi_pdev_set_dscp_tid_map_cmd_fixed_param *cmd; 173 wmi_buf_t buf; 174 uint16_t len = sizeof(*cmd); 175 176 buf = wmi_buf_alloc(wmi_handle, len); 177 if (!buf) { 178 wmi_err("Failed to allocate wmi buffer"); 179 return QDF_STATUS_E_NOMEM; 180 } 181 182 cmd = (wmi_pdev_set_dscp_tid_map_cmd_fixed_param *)wmi_buf_data(buf); 183 WMITLV_SET_HDR( 184 &cmd->tlv_header, 185 WMITLV_TAG_STRUC_wmi_pdev_set_dscp_tid_map_cmd_fixed_param, 186 WMITLV_GET_STRUCT_TLVLEN 187 (wmi_pdev_set_dscp_tid_map_cmd_fixed_param)); 188 cmd->reserved0 = WMI_PDEV_ID_SOC; 189 qdf_mem_copy(&cmd->dscp_to_tid_map, dscp_to_tid_map, 190 sizeof(uint32_t) * WMI_DSCP_MAP_MAX); 191 192 status = wmi_unified_cmd_send(wmi_handle, buf, len, 193 WMI_PDEV_SET_DSCP_TID_MAP_CMDID); 194 if (status) { 195 wmi_err("Failed to send dscp_up_map_to_fw %d", status); 196 wmi_buf_free(buf); 197 } 198 199 return status; 200 } 201 202 static void wmi_fwol_attach_dscp_tid_tlv(struct wmi_ops *ops) 203 { 204 ops->send_dscp_tid_map_cmd = send_dscp_tid_map_cmd_tlv; 205 } 206 #else 207 static void wmi_fwol_attach_dscp_tid_tlv(struct wmi_ops *ops) 208 { 209 } 210 #endif /* WLAN_SEND_DSCP_UP_MAP_TO_FW */ 211 212 #ifdef WLAN_FEATURE_MDNS_OFFLOAD 213 /** 214 * send_set_mdns_fqdn_cmd_tlv() - send set mDNS FQDN cmd to fw 215 * @wmi_handle: wmi handle 216 * @mdns_info: mDNS config info 217 * 218 * Send WMI_MDNS_SET_FQDN_CMDID to fw. 219 * 220 * Return: QDF_STATUS 221 */ 222 static QDF_STATUS 223 send_set_mdns_fqdn_cmd_tlv(wmi_unified_t wmi_handle, 224 struct mdns_config_info *mdns_info) 225 { 226 wmi_buf_t buf; 227 uint8_t *buf_ptr; 228 wmi_mdns_set_fqdn_cmd_fixed_param *cmd; 229 uint16_t len = sizeof(*cmd); 230 uint16_t fqdn_len_aligned; 231 QDF_STATUS ret; 232 233 fqdn_len_aligned = roundup(mdns_info->fqdn_len, sizeof(uint32_t)); 234 if (fqdn_len_aligned < mdns_info->fqdn_len) { 235 wmi_err_rl("integer overflow while rounding up data_len"); 236 return QDF_STATUS_E_FAILURE; 237 } 238 239 if (fqdn_len_aligned > WMI_SVC_MSG_MAX_SIZE - WMI_TLV_HDR_SIZE) { 240 wmi_err_rl("wmi_max_msg_size overflow for given data_len"); 241 return QDF_STATUS_E_FAILURE; 242 } 243 244 len += WMI_TLV_HDR_SIZE + fqdn_len_aligned; 245 buf = wmi_buf_alloc(wmi_handle, len); 246 if (!buf) { 247 wmi_err_rl("Failed to allocate wmi buffer"); 248 return QDF_STATUS_E_NOMEM; 249 } 250 251 buf_ptr = wmi_buf_data(buf); 252 cmd = (wmi_mdns_set_fqdn_cmd_fixed_param *)buf_ptr; 253 WMITLV_SET_HDR(&cmd->tlv_header, 254 WMITLV_TAG_STRUC_wmi_mdns_set_fqdn_cmd_fixed_param, 255 WMITLV_GET_STRUCT_TLVLEN 256 (wmi_mdns_set_fqdn_cmd_fixed_param)); 257 cmd->vdev_id = mdns_info->vdev_id; 258 cmd->type = mdns_info->fqdn_type; 259 cmd->fqdn_len = mdns_info->fqdn_len; 260 buf_ptr += sizeof(*cmd); 261 WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_BYTE, fqdn_len_aligned); 262 buf_ptr += WMI_TLV_HDR_SIZE; 263 qdf_mem_copy(buf_ptr, mdns_info->fqdn_data, cmd->fqdn_len); 264 265 wmi_mtrace(WMI_MDNS_SET_FQDN_CMDID, mdns_info->vdev_id, 266 mdns_info->fqdn_type); 267 268 ret = wmi_unified_cmd_send(wmi_handle, buf, len, 269 WMI_MDNS_SET_FQDN_CMDID); 270 if (QDF_IS_STATUS_ERROR(ret)) 271 wmi_buf_free(buf); 272 273 return ret; 274 } 275 276 /** 277 * send_set_mdns_response_cmd_tlv() - send set mDNS response cmd to fw 278 * @wmi_handle: wmi handle 279 * @mdns_info: mDNS config info 280 * 281 * Send WMI_MDNS_SET_RESPONSE_CMDID to fw. 282 * 283 * Return: QDF_STATUS 284 */ 285 static QDF_STATUS 286 send_set_mdns_response_cmd_tlv(wmi_unified_t wmi_handle, 287 struct mdns_config_info *mdns_info) 288 { 289 wmi_buf_t buf; 290 uint8_t *buf_ptr; 291 wmi_mdns_set_resp_cmd_fixed_param *cmd; 292 uint16_t len = sizeof(*cmd); 293 uint16_t resp_len_aligned; 294 QDF_STATUS ret; 295 296 resp_len_aligned = roundup(mdns_info->answer_payload_len, sizeof(uint32_t)); 297 if (resp_len_aligned < mdns_info->answer_payload_len) { 298 wmi_err_rl("integer overflow while rounding up data_len"); 299 return QDF_STATUS_E_FAILURE; 300 } 301 302 if (resp_len_aligned > WMI_SVC_MSG_MAX_SIZE - WMI_TLV_HDR_SIZE) { 303 wmi_err_rl("wmi_max_msg_size overflow for given data_len"); 304 return QDF_STATUS_E_FAILURE; 305 } 306 307 len += WMI_TLV_HDR_SIZE + resp_len_aligned; 308 buf = wmi_buf_alloc(wmi_handle, len); 309 if (!buf) { 310 wmi_err_rl("Failed to allocate wmi buffer"); 311 return QDF_STATUS_E_NOMEM; 312 } 313 314 buf_ptr = wmi_buf_data(buf); 315 cmd = (wmi_mdns_set_resp_cmd_fixed_param *)buf_ptr; 316 WMITLV_SET_HDR(&cmd->tlv_header, 317 WMITLV_TAG_STRUC_wmi_mdns_set_resp_cmd_fixed_param, 318 WMITLV_GET_STRUCT_TLVLEN 319 (wmi_mdns_set_resp_cmd_fixed_param)); 320 cmd->vdev_id = mdns_info->vdev_id; 321 cmd->AR_count = mdns_info->resource_record_count; 322 cmd->resp_len = mdns_info->answer_payload_len; 323 buf_ptr += sizeof(*cmd); 324 WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_BYTE, resp_len_aligned); 325 buf_ptr += WMI_TLV_HDR_SIZE; 326 qdf_mem_copy(buf_ptr, mdns_info->answer_payload_data, cmd->resp_len); 327 328 wmi_mtrace(WMI_MDNS_SET_RESPONSE_CMDID, mdns_info->vdev_id, 0); 329 330 ret = wmi_unified_cmd_send(wmi_handle, buf, len, 331 WMI_MDNS_SET_RESPONSE_CMDID); 332 if (QDF_IS_STATUS_ERROR(ret)) 333 wmi_buf_free(buf); 334 335 return ret; 336 } 337 338 /** 339 * send_set_mdns_offload_cmd_tlv() - send set mDNS offload cmd to fw 340 * @wmi_handle: wmi handle 341 * @mdns_info: mDNS config info 342 * 343 * Send WMI_MDNS_OFFLOAD_ENABLE_CMDID to fw. 344 * 345 * Return: QDF_STATUS 346 */ 347 static QDF_STATUS 348 send_set_mdns_offload_cmd_tlv(wmi_unified_t wmi_handle, 349 struct mdns_config_info *mdns_info) 350 { 351 wmi_buf_t buf; 352 wmi_mdns_offload_cmd_fixed_param *cmd; 353 uint16_t len = sizeof(*cmd); 354 QDF_STATUS ret; 355 356 buf = wmi_buf_alloc(wmi_handle, len); 357 if (!buf) { 358 wmi_err_rl("Failed to allocate wmi buffer"); 359 return QDF_STATUS_E_NOMEM; 360 } 361 362 cmd = (wmi_mdns_offload_cmd_fixed_param *)wmi_buf_data(buf); 363 WMITLV_SET_HDR(&cmd->tlv_header, 364 WMITLV_TAG_STRUC_wmi_mdns_offload_cmd_fixed_param, 365 WMITLV_GET_STRUCT_TLVLEN 366 (wmi_mdns_offload_cmd_fixed_param)); 367 cmd->vdev_id = mdns_info->vdev_id; 368 cmd->enable = mdns_info->enable; 369 370 wmi_mtrace(WMI_MDNS_OFFLOAD_ENABLE_CMDID, mdns_info->vdev_id, 0); 371 372 ret = wmi_unified_cmd_send(wmi_handle, buf, len, 373 WMI_MDNS_OFFLOAD_ENABLE_CMDID); 374 if (QDF_IS_STATUS_ERROR(ret)) 375 wmi_buf_free(buf); 376 377 return ret; 378 } 379 380 /** 381 * send_set_mdns_config_cmd_tlv() - send set mDNS config cmd to fw 382 * @wmi_handle: wmi handle 383 * @mdns_info: mdns config info 384 * 385 * Return: QDF_STATUS 386 */ 387 static QDF_STATUS 388 send_set_mdns_config_cmd_tlv(wmi_unified_t wmi_handle, 389 struct mdns_config_info *mdns_info) 390 { 391 QDF_STATUS ret; 392 393 if (!mdns_info->enable) { 394 ret = send_set_mdns_offload_cmd_tlv(wmi_handle, mdns_info); 395 if (QDF_IS_STATUS_ERROR(ret)) 396 wmi_err_rl("Failed to send mDNS offload command. ret = %d", ret); 397 398 return ret; 399 } 400 401 ret = send_set_mdns_fqdn_cmd_tlv(wmi_handle, mdns_info); 402 if (QDF_IS_STATUS_ERROR(ret)) { 403 wmi_err_rl("Failed to send set fqdn command. ret = %d", ret); 404 return ret; 405 } 406 407 ret = send_set_mdns_response_cmd_tlv(wmi_handle, mdns_info); 408 if (QDF_IS_STATUS_ERROR(ret)) { 409 wmi_err_rl("Failed to send set mDNS response command. ret = %d", ret); 410 return ret; 411 } 412 413 ret = send_set_mdns_offload_cmd_tlv(wmi_handle, mdns_info); 414 if (QDF_IS_STATUS_ERROR(ret)) { 415 wmi_err_rl("Failed to send set mDNS offload command. ret = %d", ret); 416 return ret; 417 } 418 419 return ret; 420 } 421 422 static void wmi_fwol_attach_mdns_tlv(struct wmi_ops *ops) 423 { 424 ops->send_set_mdns_config_cmd = send_set_mdns_config_cmd_tlv; 425 } 426 #else 427 static void wmi_fwol_attach_mdns_tlv(struct wmi_ops *ops) 428 { 429 } 430 #endif /* WLAN_FEATURE_MDNS_OFFLOAD */ 431 432 #ifdef THERMAL_STATS_SUPPORT 433 /** 434 * send_get_thermal_stats_cmd_tlv() - send get thermal stats cmd to fw 435 * @wmi_handle: wmi handle 436 * @req_type: req type 437 * @temp_offset: temperature offset 438 * 439 * Send WMI_REQUEST_THERMAL_STATS_CMDID to fw. 440 * 441 * Return: QDF_STATUS 442 */ 443 static QDF_STATUS 444 send_get_thermal_stats_cmd_tlv(wmi_unified_t wmi_handle, 445 enum thermal_stats_request_type req_type, 446 uint8_t temp_offset) 447 { 448 wmi_buf_t buf; 449 wmi_thermal_stats_cmd_fixed_param *cmd; 450 uint16_t len = sizeof(*cmd); 451 QDF_STATUS ret; 452 453 buf = wmi_buf_alloc(wmi_handle, len); 454 if (!buf) { 455 wmi_err("Failed to allocate wmi buffer"); 456 return QDF_STATUS_E_NOMEM; 457 } 458 459 cmd = (wmi_thermal_stats_cmd_fixed_param *)wmi_buf_data(buf); 460 WMITLV_SET_HDR(&cmd->tlv_header, 461 WMITLV_TAG_STRUC_wmi_thermal_stats_cmd_fixed_param, 462 WMITLV_GET_STRUCT_TLVLEN 463 (wmi_thermal_stats_cmd_fixed_param)); 464 cmd->thermal_action = req_type; 465 cmd->thermal_offset = temp_offset; 466 ret = wmi_unified_cmd_send(wmi_handle, buf, len, 467 WMI_REQUEST_THERMAL_STATS_CMDID); 468 if (QDF_IS_STATUS_ERROR(ret)) { 469 wmi_err("Failed to send get thermal stats cmd = %d", ret); 470 wmi_buf_free(buf); 471 } 472 473 return ret; 474 } 475 476 static void wmi_fwol_attach_thermal_stats_tlv(struct wmi_ops *ops) 477 { 478 ops->send_get_thermal_stats_cmd = send_get_thermal_stats_cmd_tlv; 479 } 480 #else 481 static void wmi_fwol_attach_thermal_stats_tlv(struct wmi_ops *ops) 482 { 483 } 484 #endif /* FW_THERMAL_THROTTLE_SUPPORT */ 485 486 void wmi_fwol_attach_tlv(wmi_unified_t wmi_handle) 487 { 488 struct wmi_ops *ops = wmi_handle->ops; 489 490 wmi_fwol_attach_elna_tlv(ops); 491 wmi_fwol_attach_dscp_tid_tlv(ops); 492 wmi_fwol_attach_mdns_tlv(ops); 493 wmi_fwol_attach_thermal_stats_tlv(ops); 494 } 495