1 /* 2 * Copyright (c) 2013-2018, 2020 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 <wmi_unified_p2p_api.h> 23 24 /** 25 * send_set_p2pgo_noa_req_cmd_tlv() - send p2p go noa request to fw 26 * @wmi_handle: wmi handle 27 * @noa: p2p power save parameters 28 * 29 * Return: CDF status 30 */ 31 static QDF_STATUS send_set_p2pgo_noa_req_cmd_tlv(wmi_unified_t wmi_handle, 32 struct p2p_ps_params *noa) 33 { 34 wmi_p2p_set_noa_cmd_fixed_param *cmd; 35 wmi_p2p_noa_descriptor *noa_discriptor; 36 wmi_buf_t buf; 37 uint8_t *buf_ptr; 38 uint16_t len; 39 QDF_STATUS status; 40 uint32_t duration; 41 42 len = sizeof(*cmd) + WMI_TLV_HDR_SIZE + sizeof(*noa_discriptor); 43 buf = wmi_buf_alloc(wmi_handle, len); 44 if (!buf) { 45 status = QDF_STATUS_E_FAILURE; 46 goto end; 47 } 48 49 buf_ptr = (uint8_t *)wmi_buf_data(buf); 50 cmd = (wmi_p2p_set_noa_cmd_fixed_param *)buf_ptr; 51 WMITLV_SET_HDR(&cmd->tlv_header, 52 WMITLV_TAG_STRUC_wmi_p2p_set_noa_cmd_fixed_param, 53 WMITLV_GET_STRUCT_TLVLEN 54 (wmi_p2p_set_noa_cmd_fixed_param)); 55 duration = (noa->count == 1) ? noa->single_noa_duration : noa->duration; 56 cmd->vdev_id = noa->session_id; 57 cmd->enable = (duration) ? true : false; 58 cmd->num_noa = 1; 59 60 WMITLV_SET_HDR((buf_ptr + sizeof(wmi_p2p_set_noa_cmd_fixed_param)), 61 WMITLV_TAG_ARRAY_STRUC, sizeof(wmi_p2p_noa_descriptor)); 62 noa_discriptor = (wmi_p2p_noa_descriptor *)(buf_ptr + 63 sizeof 64 (wmi_p2p_set_noa_cmd_fixed_param) 65 + WMI_TLV_HDR_SIZE); 66 WMITLV_SET_HDR(&noa_discriptor->tlv_header, 67 WMITLV_TAG_STRUC_wmi_p2p_noa_descriptor, 68 WMITLV_GET_STRUCT_TLVLEN(wmi_p2p_noa_descriptor)); 69 noa_discriptor->type_count = noa->count; 70 noa_discriptor->duration = duration; 71 noa_discriptor->interval = noa->interval; 72 noa_discriptor->start_time = 0; 73 74 wmi_debug("SET P2P GO NOA:vdev_id:%d count:%d duration:%d interval:%d", 75 cmd->vdev_id, noa->count, noa_discriptor->duration, 76 noa->interval); 77 wmi_mtrace(WMI_FWTEST_P2P_SET_NOA_PARAM_CMDID, cmd->vdev_id, 0); 78 status = wmi_unified_cmd_send(wmi_handle, buf, len, 79 WMI_FWTEST_P2P_SET_NOA_PARAM_CMDID); 80 if (QDF_IS_STATUS_ERROR(status)) { 81 wmi_err("Failed to send WMI_FWTEST_P2P_SET_NOA_PARAM_CMDID"); 82 wmi_buf_free(buf); 83 } 84 85 end: 86 return status; 87 } 88 89 /** 90 * send_set_p2pgo_oppps_req_cmd_tlv() - send p2p go opp power save request to fw 91 * @wmi_handle: wmi handle 92 * @noa: p2p opp power save parameters 93 * 94 * Return: CDF status 95 */ 96 static QDF_STATUS send_set_p2pgo_oppps_req_cmd_tlv(wmi_unified_t wmi_handle, 97 struct p2p_ps_params *oppps) 98 { 99 wmi_p2p_set_oppps_cmd_fixed_param *cmd; 100 wmi_buf_t buf; 101 QDF_STATUS status; 102 103 buf = wmi_buf_alloc(wmi_handle, sizeof(*cmd)); 104 if (!buf) { 105 status = QDF_STATUS_E_FAILURE; 106 goto end; 107 } 108 109 cmd = (wmi_p2p_set_oppps_cmd_fixed_param *) wmi_buf_data(buf); 110 WMITLV_SET_HDR(&cmd->tlv_header, 111 WMITLV_TAG_STRUC_wmi_p2p_set_oppps_cmd_fixed_param, 112 WMITLV_GET_STRUCT_TLVLEN( 113 wmi_p2p_set_oppps_cmd_fixed_param)); 114 cmd->vdev_id = oppps->session_id; 115 if (oppps->ctwindow) 116 WMI_UNIFIED_OPPPS_ATTR_ENABLED_SET(cmd); 117 118 WMI_UNIFIED_OPPPS_ATTR_CTWIN_SET(cmd, oppps->ctwindow); 119 wmi_debug("SET P2P GO OPPPS:vdev_id:%d ctwindow:%d", 120 cmd->vdev_id, oppps->ctwindow); 121 wmi_mtrace(WMI_P2P_SET_OPPPS_PARAM_CMDID, cmd->vdev_id, 0); 122 status = wmi_unified_cmd_send(wmi_handle, buf, sizeof(*cmd), 123 WMI_P2P_SET_OPPPS_PARAM_CMDID); 124 if (QDF_IS_STATUS_ERROR(status)) { 125 wmi_err("Failed to send WMI_P2P_SET_OPPPS_PARAM_CMDID"); 126 wmi_buf_free(buf); 127 } 128 129 end: 130 return status; 131 } 132 133 /** 134 * extract_p2p_noa_ev_param_tlv() - extract p2p noa information from event 135 * @wmi_handle: wmi handle 136 * @param evt_buf: pointer to event buffer 137 * @param param: Pointer to hold p2p noa info 138 * 139 * Return: QDF_STATUS_SUCCESS for success or error code 140 */ 141 static QDF_STATUS extract_p2p_noa_ev_param_tlv( 142 wmi_unified_t wmi_handle, void *evt_buf, 143 struct p2p_noa_info *param) 144 { 145 WMI_P2P_NOA_EVENTID_param_tlvs *param_tlvs; 146 wmi_p2p_noa_event_fixed_param *fixed_param; 147 uint8_t i; 148 wmi_p2p_noa_info *wmi_noa_info; 149 uint8_t *buf_ptr; 150 uint32_t descriptors; 151 152 param_tlvs = (WMI_P2P_NOA_EVENTID_param_tlvs *)evt_buf; 153 if (!param_tlvs) { 154 wmi_err("Invalid P2P NoA event buffer"); 155 return QDF_STATUS_E_INVAL; 156 } 157 158 if (!param) { 159 wmi_err("noa information param is null"); 160 return QDF_STATUS_E_INVAL; 161 } 162 163 fixed_param = param_tlvs->fixed_param; 164 buf_ptr = (uint8_t *) fixed_param; 165 buf_ptr += sizeof(wmi_p2p_noa_event_fixed_param); 166 wmi_noa_info = (wmi_p2p_noa_info *) (buf_ptr); 167 168 if (!WMI_UNIFIED_NOA_ATTR_IS_MODIFIED(wmi_noa_info)) { 169 wmi_err("noa attr is not modified"); 170 return QDF_STATUS_E_INVAL; 171 } 172 173 param->vdev_id = fixed_param->vdev_id; 174 param->index = 175 (uint8_t)WMI_UNIFIED_NOA_ATTR_INDEX_GET(wmi_noa_info); 176 param->opps_ps = 177 (uint8_t)WMI_UNIFIED_NOA_ATTR_OPP_PS_GET(wmi_noa_info); 178 param->ct_window = 179 (uint8_t)WMI_UNIFIED_NOA_ATTR_CTWIN_GET(wmi_noa_info); 180 descriptors = WMI_UNIFIED_NOA_ATTR_NUM_DESC_GET(wmi_noa_info); 181 param->num_desc = (uint8_t)descriptors; 182 if (param->num_desc > WMI_P2P_MAX_NOA_DESCRIPTORS) { 183 wmi_err("Invalid num desc: %d", param->num_desc); 184 return QDF_STATUS_E_INVAL; 185 } 186 187 wmi_debug("index %u, opps_ps %u, ct_window %u, num_descriptors = %u", 188 param->index, param->opps_ps, param->ct_window, 189 param->num_desc); 190 for (i = 0; i < param->num_desc; i++) { 191 param->noa_desc[i].type_count = 192 (uint8_t)wmi_noa_info->noa_descriptors[i]. 193 type_count; 194 param->noa_desc[i].duration = 195 wmi_noa_info->noa_descriptors[i].duration; 196 param->noa_desc[i].interval = 197 wmi_noa_info->noa_descriptors[i].interval; 198 param->noa_desc[i].start_time = 199 wmi_noa_info->noa_descriptors[i].start_time; 200 wmi_debug("NoA descriptor[%d] type_count %u, duration %u, interval %u, start_time = %u", 201 i, param->noa_desc[i].type_count, 202 param->noa_desc[i].duration, 203 param->noa_desc[i].interval, 204 param->noa_desc[i].start_time); 205 } 206 207 return QDF_STATUS_SUCCESS; 208 } 209 210 static QDF_STATUS 211 send_set_mac_addr_rx_filter_cmd_tlv(wmi_unified_t wmi_handle, 212 struct p2p_set_mac_filter *param) 213 { 214 wmi_vdev_add_mac_addr_to_rx_filter_cmd_fixed_param *cmd; 215 uint32_t len; 216 wmi_buf_t buf; 217 int ret; 218 219 if (!wmi_handle) { 220 wmi_err("WMA context is invald!"); 221 return QDF_STATUS_E_INVAL; 222 } 223 224 len = sizeof(*cmd); 225 buf = wmi_buf_alloc(wmi_handle, len); 226 if (!buf) { 227 wmi_err("Failed allocate wmi buffer"); 228 return QDF_STATUS_E_NOMEM; 229 } 230 231 cmd = (wmi_vdev_add_mac_addr_to_rx_filter_cmd_fixed_param *) 232 wmi_buf_data(buf); 233 234 WMITLV_SET_HDR( 235 &cmd->tlv_header, 236 WMITLV_TAG_STRUC_wmi_vdev_add_mac_addr_to_rx_filter_cmd_fixed_param, 237 WMITLV_GET_STRUCT_TLVLEN( 238 wmi_vdev_add_mac_addr_to_rx_filter_cmd_fixed_param)); 239 240 cmd->vdev_id = param->vdev_id; 241 cmd->freq = param->freq; 242 WMI_CHAR_ARRAY_TO_MAC_ADDR(param->mac, &cmd->mac_addr); 243 if (param->set) 244 cmd->enable = 1; 245 else 246 cmd->enable = 0; 247 wmi_debug("set random mac rx vdev %d freq %d set %d "QDF_MAC_ADDR_FMT, 248 param->vdev_id, param->freq, param->set, 249 QDF_MAC_ADDR_REF(param->mac)); 250 ret = wmi_unified_cmd_send(wmi_handle, buf, len, 251 WMI_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_CMDID); 252 if (ret) { 253 wmi_err("Failed to send action frame random mac cmd"); 254 wmi_buf_free(buf); 255 return QDF_STATUS_E_FAILURE; 256 } 257 258 return QDF_STATUS_SUCCESS; 259 } 260 261 static QDF_STATUS extract_mac_addr_rx_filter_evt_param_tlv( 262 wmi_unified_t wmi_handle, void *evt_buf, 263 struct p2p_set_mac_filter_evt *param) 264 { 265 WMI_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_STATUS_EVENTID_param_tlvs *param_buf; 266 wmi_vdev_add_mac_addr_to_rx_filter_status_event_fixed_param *event; 267 268 param_buf = 269 (WMI_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_STATUS_EVENTID_param_tlvs *) 270 evt_buf; 271 if (!param_buf) { 272 wmi_err("Invalid action frame filter mac event"); 273 return QDF_STATUS_E_INVAL; 274 } 275 event = param_buf->fixed_param; 276 if (!event) { 277 wmi_err("Invalid fixed param"); 278 return QDF_STATUS_E_INVAL; 279 } 280 param->vdev_id = event->vdev_id; 281 param->status = event->status; 282 283 return QDF_STATUS_SUCCESS; 284 } 285 286 #ifdef FEATURE_P2P_LISTEN_OFFLOAD 287 /** 288 * send_p2p_lo_start_cmd_tlv() - send p2p lo start request to fw 289 * @wmi_handle: wmi handle 290 * @param: p2p listen offload start parameters 291 * 292 * Return: QDF status 293 */ 294 static QDF_STATUS send_p2p_lo_start_cmd_tlv(wmi_unified_t wmi_handle, 295 struct p2p_lo_start *param) 296 { 297 wmi_buf_t buf; 298 wmi_p2p_lo_start_cmd_fixed_param *cmd; 299 int32_t len = sizeof(*cmd); 300 uint8_t *buf_ptr; 301 QDF_STATUS status; 302 int device_types_len_aligned; 303 int probe_resp_len_aligned; 304 305 if (!param) { 306 wmi_err("lo start param is null"); 307 return QDF_STATUS_E_INVAL; 308 } 309 310 wmi_debug("vdev_id: %d", param->vdev_id); 311 312 device_types_len_aligned = 313 qdf_roundup(param->dev_types_len, 314 sizeof(uint32_t)); 315 probe_resp_len_aligned = 316 qdf_roundup(param->probe_resp_len, 317 sizeof(uint32_t)); 318 319 len += 2 * WMI_TLV_HDR_SIZE + device_types_len_aligned + 320 probe_resp_len_aligned; 321 322 buf = wmi_buf_alloc(wmi_handle, len); 323 if (!buf) { 324 return QDF_STATUS_E_NOMEM; 325 } 326 327 cmd = (wmi_p2p_lo_start_cmd_fixed_param *)wmi_buf_data(buf); 328 buf_ptr = (uint8_t *) wmi_buf_data(buf); 329 330 WMITLV_SET_HDR(&cmd->tlv_header, 331 WMITLV_TAG_STRUC_wmi_p2p_lo_start_cmd_fixed_param, 332 WMITLV_GET_STRUCT_TLVLEN(wmi_p2p_lo_start_cmd_fixed_param)); 333 334 cmd->vdev_id = param->vdev_id; 335 cmd->ctl_flags = param->ctl_flags; 336 cmd->channel = param->freq; 337 cmd->period = param->period; 338 cmd->interval = param->interval; 339 cmd->count = param->count; 340 cmd->device_types_len = param->dev_types_len; 341 cmd->prob_resp_len = param->probe_resp_len; 342 343 buf_ptr += sizeof(wmi_p2p_lo_start_cmd_fixed_param); 344 WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_BYTE, 345 device_types_len_aligned); 346 buf_ptr += WMI_TLV_HDR_SIZE; 347 qdf_mem_copy(buf_ptr, param->device_types, 348 param->dev_types_len); 349 350 buf_ptr += device_types_len_aligned; 351 WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_BYTE, 352 probe_resp_len_aligned); 353 buf_ptr += WMI_TLV_HDR_SIZE; 354 qdf_mem_copy(buf_ptr, param->probe_resp_tmplt, 355 param->probe_resp_len); 356 357 wmi_debug("Sending WMI_P2P_LO_START command, channel=%d, period=%d, interval=%d, count=%d", 358 cmd->channel, cmd->period, cmd->interval, cmd->count); 359 360 wmi_mtrace(WMI_P2P_LISTEN_OFFLOAD_START_CMDID, cmd->vdev_id, 0); 361 status = wmi_unified_cmd_send(wmi_handle, 362 buf, len, 363 WMI_P2P_LISTEN_OFFLOAD_START_CMDID); 364 if (status != QDF_STATUS_SUCCESS) { 365 wmi_err("Failed to send p2p lo start: %d", status); 366 wmi_buf_free(buf); 367 return status; 368 } 369 370 wmi_debug("Successfully sent WMI_P2P_LO_START"); 371 372 return QDF_STATUS_SUCCESS; 373 } 374 375 /** 376 * send_p2p_lo_stop_cmd_tlv() - send p2p lo stop request to fw 377 * @wmi_handle: wmi handle 378 * @param: p2p listen offload stop parameters 379 * 380 * Return: QDF status 381 */ 382 static QDF_STATUS send_p2p_lo_stop_cmd_tlv(wmi_unified_t wmi_handle, 383 uint8_t vdev_id) 384 { 385 wmi_buf_t buf; 386 wmi_p2p_lo_stop_cmd_fixed_param *cmd; 387 int32_t len; 388 QDF_STATUS status; 389 390 wmi_debug("vdev_id: %d", vdev_id); 391 392 len = sizeof(*cmd); 393 buf = wmi_buf_alloc(wmi_handle, len); 394 if (!buf) { 395 return QDF_STATUS_E_NOMEM; 396 } 397 cmd = (wmi_p2p_lo_stop_cmd_fixed_param *)wmi_buf_data(buf); 398 399 WMITLV_SET_HDR(&cmd->tlv_header, 400 WMITLV_TAG_STRUC_wmi_p2p_lo_stop_cmd_fixed_param, 401 WMITLV_GET_STRUCT_TLVLEN(wmi_p2p_lo_stop_cmd_fixed_param)); 402 403 cmd->vdev_id = vdev_id; 404 405 wmi_debug("Sending WMI_P2P_LO_STOP command"); 406 407 wmi_mtrace(WMI_P2P_LISTEN_OFFLOAD_STOP_CMDID, cmd->vdev_id, 0); 408 status = wmi_unified_cmd_send(wmi_handle, 409 buf, len, 410 WMI_P2P_LISTEN_OFFLOAD_STOP_CMDID); 411 if (status != QDF_STATUS_SUCCESS) { 412 wmi_err("Failed to send p2p lo stop: %d", status); 413 wmi_buf_free(buf); 414 return status; 415 } 416 417 wmi_debug("Successfully sent WMI_P2P_LO_STOP"); 418 419 return QDF_STATUS_SUCCESS; 420 } 421 422 /** 423 * extract_p2p_lo_stop_ev_param_tlv() - extract p2p lo stop 424 * information from event 425 * @wmi_handle: wmi handle 426 * @param evt_buf: pointer to event buffer 427 * @param param: Pointer to hold p2p lo stop event information 428 * 429 * Return: QDF_STATUS_SUCCESS for success or error code 430 */ 431 static QDF_STATUS extract_p2p_lo_stop_ev_param_tlv( 432 wmi_unified_t wmi_handle, void *evt_buf, 433 struct p2p_lo_event *param) 434 { 435 WMI_P2P_LISTEN_OFFLOAD_STOPPED_EVENTID_param_tlvs *param_tlvs; 436 wmi_p2p_lo_stopped_event_fixed_param *lo_param; 437 438 param_tlvs = (WMI_P2P_LISTEN_OFFLOAD_STOPPED_EVENTID_param_tlvs *) 439 evt_buf; 440 if (!param_tlvs) { 441 wmi_err("Invalid P2P lo stop event buffer"); 442 return QDF_STATUS_E_INVAL; 443 } 444 445 if (!param) { 446 wmi_err("lo stop event param is null"); 447 return QDF_STATUS_E_INVAL; 448 } 449 450 lo_param = param_tlvs->fixed_param; 451 param->vdev_id = lo_param->vdev_id; 452 param->reason_code = lo_param->reason; 453 wmi_debug("vdev_id:%d, reason:%d", 454 param->vdev_id, param->reason_code); 455 456 return QDF_STATUS_SUCCESS; 457 } 458 459 void wmi_p2p_listen_offload_attach_tlv(wmi_unified_t wmi_handle) 460 { 461 struct wmi_ops *ops = wmi_handle->ops; 462 463 ops->send_p2p_lo_start_cmd = send_p2p_lo_start_cmd_tlv; 464 ops->send_p2p_lo_stop_cmd = send_p2p_lo_stop_cmd_tlv; 465 ops->extract_p2p_lo_stop_ev_param = 466 extract_p2p_lo_stop_ev_param_tlv; 467 } 468 #endif /* FEATURE_P2P_LISTEN_OFFLOAD */ 469 470 void wmi_p2p_attach_tlv(wmi_unified_t wmi_handle) 471 { 472 struct wmi_ops *ops = wmi_handle->ops; 473 474 ops->send_set_p2pgo_oppps_req_cmd = send_set_p2pgo_oppps_req_cmd_tlv; 475 ops->send_set_p2pgo_noa_req_cmd = send_set_p2pgo_noa_req_cmd_tlv; 476 ops->extract_p2p_noa_ev_param = extract_p2p_noa_ev_param_tlv; 477 ops->set_mac_addr_rx_filter = send_set_mac_addr_rx_filter_cmd_tlv, 478 ops->extract_mac_addr_rx_filter_evt_param = 479 extract_mac_addr_rx_filter_evt_param_tlv, 480 wmi_p2p_listen_offload_attach_tlv(wmi_handle); 481 } 482 483