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