/* * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** * DOC: Implement API's specific to CoAP component. */ #include #include "wmi.h" #include "ol_defines.h" /* * send_coap_add_pattern_cmd_tlv() - Send wmi cmd for adding CoAP pattern * @wmi_handle: wmi handle * @param: parameter for CoAP add pattern * * Return: QDF_STATUS */ static QDF_STATUS send_coap_add_pattern_cmd_tlv(wmi_unified_t wmi_handle, struct coap_offload_reply_param *param) { WMI_WOW_COAP_ADD_PATTERN_CMD_fixed_param *cmd; wmi_buf_t buf; QDF_STATUS status; uint8_t *buf_ptr; uint32_t len, coapmsg_len_align, verify_len_align; wmi_debug("vdev id %d pattern id %d timeout %d src ip 0x%x:%d coap msg len %d", param->vdev_id, param->pattern_id, param->cache_timeout, param->src_ip_v4, param->src_udp_port, param->coapmsg_len); wmi_debug("filter: dest ip 0x%x:%d is bc %d verify offset %d len %d", param->dest_ip_v4, param->dest_udp_port, param->dest_ip_v4_is_bc, param->verify_offset, param->verify_len); if (!param->verify || !param->verify_len || !param->coapmsg || !param->coapmsg_len) { wmi_err("invalid param"); return QDF_STATUS_E_INVAL; } coapmsg_len_align = qdf_align(param->coapmsg_len, 4); verify_len_align = qdf_align(param->verify_len, 4); len = sizeof(*cmd) + WMI_TLV_HDR_SIZE + coapmsg_len_align + WMI_TLV_HDR_SIZE + verify_len_align; buf = wmi_buf_alloc(wmi_handle, len); if (!buf) return QDF_STATUS_E_NOMEM; buf_ptr = wmi_buf_data(buf); cmd = (WMI_WOW_COAP_ADD_PATTERN_CMD_fixed_param *)buf_ptr; WMITLV_SET_HDR(&cmd->tlv_header, WMITLV_TAG_STRUC_WMI_WOW_COAP_ADD_PATTERN_CMD_fixed_param, WMITLV_GET_STRUCT_TLVLEN( WMI_WOW_COAP_ADD_PATTERN_CMD_fixed_param)); cmd->vdev_id = param->vdev_id; cmd->pattern_id = param->pattern_id; cmd->timeout = param->cache_timeout; WMI_COAP_IPV6_SET(cmd->pattern_type, 0); WMI_COAP_ADDR_TYPE_SET(cmd->pattern_type, param->dest_ip_v4_is_bc ? 1 : 0); qdf_mem_copy(cmd->match_udp_ip.ipv4_addr, ¶m->dest_ip_v4, sizeof(param->dest_ip_v4)); cmd->match_udp_port = param->dest_udp_port; qdf_mem_copy(cmd->udp_local_ip.ipv4_addr, ¶m->src_ip_v4, sizeof(param->src_ip_v4)); cmd->udp_local_port = param->src_udp_port; cmd->verify_offset = param->verify_offset; cmd->verify_len = param->verify_len; cmd->coapmsg_len = param->coapmsg_len; buf_ptr += sizeof(*cmd); WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_BYTE, verify_len_align); buf_ptr += WMI_TLV_HDR_SIZE; qdf_mem_copy(buf_ptr, param->verify, param->verify_len); buf_ptr += verify_len_align; WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_BYTE, coapmsg_len_align); buf_ptr += WMI_TLV_HDR_SIZE; qdf_mem_copy(buf_ptr, param->coapmsg, param->coapmsg_len); buf_ptr += coapmsg_len_align; wmi_mtrace(WMI_WOW_COAP_ADD_PATTERN_CMDID, cmd->vdev_id, cmd->pattern_id); status = wmi_unified_cmd_send(wmi_handle, buf, len, WMI_WOW_COAP_ADD_PATTERN_CMDID); if (status != QDF_STATUS_SUCCESS) { wmi_err("Failed to send wow coap add pattern command %d", status); wmi_buf_free(buf); } return status; } /* * send_coap_del_pattern_cmd_tlv() - Send wmi cmd for deleting CoAP pattern * @wmi_handle: wmi handle * @vdev_id: vdev id * @pattern_id: pattern id * * Return: QDF_STATUS */ static QDF_STATUS send_coap_del_pattern_cmd_tlv(wmi_unified_t wmi_handle, uint8_t vdev_id, uint32_t pattern_id) { WMI_WOW_COAP_DEL_PATTERN_CMD_fixed_param *cmd; wmi_buf_t buf; QDF_STATUS status; uint32_t len = sizeof(*cmd); wmi_debug("vdev id %d pattern id %d", vdev_id, pattern_id); buf = wmi_buf_alloc(wmi_handle, len); if (!buf) return QDF_STATUS_E_NOMEM; cmd = (WMI_WOW_COAP_DEL_PATTERN_CMD_fixed_param *)wmi_buf_data(buf); WMITLV_SET_HDR(&cmd->tlv_header, WMITLV_TAG_STRUC_WMI_WOW_COAP_DEL_PATTERN_CMD_fixed_param, WMITLV_GET_STRUCT_TLVLEN( WMI_WOW_COAP_DEL_PATTERN_CMD_fixed_param)); cmd->vdev_id = vdev_id; cmd->pattern_id = pattern_id; wmi_mtrace(WMI_WOW_COAP_DEL_PATTERN_CMDID, cmd->vdev_id, cmd->pattern_id); status = wmi_unified_cmd_send(wmi_handle, buf, len, WMI_WOW_COAP_DEL_PATTERN_CMDID); if (status != QDF_STATUS_SUCCESS) { wmi_err("Failed to send wow coap del pattern command %d", status); wmi_buf_free(buf); } return status; } /* * send_coap_add_pattern_cmd_tlv() - Send wmi cmd for adding CoAP keepalive * pattern * @wmi_handle: wmi handle * @param: parameter for CoAP add pattern * * Return: QDF_STATUS */ static QDF_STATUS send_coap_add_keepalive_pattern_cmd_tlv(wmi_unified_t wmi_handle, struct coap_offload_periodic_tx_param *param) { WMI_WOW_COAP_ADD_KEEPALIVE_PATTERN_CMD_fixed_param *cmd; wmi_buf_t buf; QDF_STATUS status; uint8_t *buf_ptr; uint32_t len, coapmsg_len_align; wmi_debug("vdev id %d pattern id %d ip src 0x%x:%d dest 0x%x:%d bc %d timeout %d", param->vdev_id, param->pattern_id, param->src_ip_v4, param->src_udp_port, param->dest_ip_v4, param->dest_udp_port, param->dest_ip_v4_is_bc, param->timeout); if (!param->coapmsg || !param->coapmsg_len) { wmi_err("invalid CoAP message"); return QDF_STATUS_E_INVAL; } coapmsg_len_align = qdf_align(param->coapmsg_len, 4); len = sizeof(*cmd) + WMI_TLV_HDR_SIZE + coapmsg_len_align; buf = wmi_buf_alloc(wmi_handle, len); if (!buf) return QDF_STATUS_E_NOMEM; buf_ptr = wmi_buf_data(buf); cmd = (WMI_WOW_COAP_ADD_KEEPALIVE_PATTERN_CMD_fixed_param *)buf_ptr; WMITLV_SET_HDR(&cmd->tlv_header, WMITLV_TAG_STRUC_WMI_WOW_COAP_ADD_KEEPALIVE_PATTERN_CMD_fixed_param, WMITLV_GET_STRUCT_TLVLEN( WMI_WOW_COAP_ADD_KEEPALIVE_PATTERN_CMD_fixed_param)); cmd->vdev_id = param->vdev_id; cmd->pattern_id = param->pattern_id; /* only support IPv4 in current stage */ WMI_COAP_IPV6_SET(cmd->pattern_type, 0); WMI_COAP_ADDR_TYPE_SET(cmd->pattern_type, param->dest_ip_v4_is_bc ? 1 : 0); qdf_mem_copy(cmd->udp_remote_ip.ipv4_addr, ¶m->dest_ip_v4, sizeof(param->dest_ip_v4)); cmd->udp_remote_port = param->dest_udp_port; qdf_mem_copy(cmd->udp_local_ip.ipv4_addr, ¶m->src_ip_v4, sizeof(param->src_ip_v4)); cmd->udp_local_port = param->src_udp_port; cmd->timeout = param->timeout; cmd->coapmsg_len = param->coapmsg_len; buf_ptr += sizeof(*cmd); WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_BYTE, coapmsg_len_align); buf_ptr += WMI_TLV_HDR_SIZE; qdf_mem_copy(buf_ptr, param->coapmsg, param->coapmsg_len); buf_ptr += coapmsg_len_align; wmi_mtrace(WMI_WOW_COAP_ADD_KEEPALIVE_PATTERN_CMDID, cmd->vdev_id, cmd->pattern_id); status = wmi_unified_cmd_send(wmi_handle, buf, len, WMI_WOW_COAP_ADD_KEEPALIVE_PATTERN_CMDID); if (status != QDF_STATUS_SUCCESS) { wmi_err("Failed to send wow coap add keepalive pattern command %d", status); wmi_buf_free(buf); } return status; } /* * send_coap_del_pattern_cmd_tlv() - Send wmi cmd for deleting CoAP * keepalive pattern * @wmi_handle: wmi handle * @vdev_id: vdev id * @pattern_id: pattern id * * Return: QDF_STATUS */ static QDF_STATUS send_coap_del_keepalive_pattern_cmd_tlv(wmi_unified_t wmi_handle, uint8_t vdev_id, uint32_t pattern_id) { WMI_WOW_COAP_DEL_KEEPALIVE_PATTERN_CMD_fixed_param *cmd; wmi_buf_t buf; QDF_STATUS status; uint8_t *buf_ptr; uint32_t len = sizeof(*cmd); wmi_debug("vdev id %d pattern id %d", vdev_id, pattern_id); buf = wmi_buf_alloc(wmi_handle, len); if (!buf) return QDF_STATUS_E_NOMEM; buf_ptr = wmi_buf_data(buf); cmd = (WMI_WOW_COAP_DEL_KEEPALIVE_PATTERN_CMD_fixed_param *)buf_ptr; WMITLV_SET_HDR(&cmd->tlv_header, WMITLV_TAG_STRUC_WMI_WOW_COAP_DEL_KEEPALIVE_PATTERN_CMD_fixed_param, WMITLV_GET_STRUCT_TLVLEN( WMI_WOW_COAP_DEL_PATTERN_CMD_fixed_param)); cmd->vdev_id = vdev_id; cmd->pattern_id = pattern_id; wmi_mtrace(WMI_WOW_COAP_DEL_KEEPALIVE_PATTERN_CMDID, cmd->vdev_id, cmd->pattern_id); status = wmi_unified_cmd_send(wmi_handle, buf, len, WMI_WOW_COAP_DEL_KEEPALIVE_PATTERN_CMDID); if (status != QDF_STATUS_SUCCESS) { wmi_err("Failed to send wow coap del keepalive pattern command %d", status); wmi_buf_free(buf); } return status; } /* * send_coap_cache_get_cmd_tlv() - Send wmi cmd for getting cached CoAP * messages * @wmi_handle: wmi handle * @vdev_id: vdev id * @pattern_id: pattern id * * Return: QDF_STATUS */ static QDF_STATUS send_coap_cache_get_cmd_tlv(wmi_unified_t wmi_handle, uint8_t vdev_id, uint32_t pattern_id) { WMI_WOW_COAP_GET_BUF_INFO_CMD_fixed_param *cmd; wmi_buf_t buf; QDF_STATUS status; uint32_t len = sizeof(*cmd); wmi_debug("vdev id %d pattern id %d", vdev_id, pattern_id); buf = wmi_buf_alloc(wmi_handle, len); if (!buf) return QDF_STATUS_E_NOMEM; cmd = (WMI_WOW_COAP_GET_BUF_INFO_CMD_fixed_param *)wmi_buf_data(buf); WMITLV_SET_HDR(&cmd->tlv_header, WMITLV_TAG_STRUC_WMI_WOW_COAP_GET_BUF_INFO_CMD_fixed_param, WMITLV_GET_STRUCT_TLVLEN( WMI_WOW_COAP_GET_BUF_INFO_CMD_fixed_param)); cmd->vdev_id = vdev_id; cmd->pattern_id = pattern_id; wmi_mtrace(WMI_WOW_COAP_GET_BUF_INFO_CMDID, cmd->vdev_id, cmd->pattern_id); status = wmi_unified_cmd_send(wmi_handle, buf, len, WMI_WOW_COAP_GET_BUF_INFO_CMDID); if (status != QDF_STATUS_SUCCESS) { wmi_err("Failed to send wow coap get buf info command %d", status); wmi_buf_free(buf); } return status; } /** * coap_extract_buf_info_tlv() - Extract CoAP buf info event * @wmi_handle: wmi handle * @evt_buf: Pointer to the event buffer * @info: pointer to CoAP buf info * * The caller needs to free any possible nodes in info->info_list * regardless of failure or success. * * Return: QDF_STATUS */ static QDF_STATUS coap_extract_buf_info_tlv(wmi_unified_t wmi_handle, void *evt_buf, struct coap_buf_info *info) { WMI_WOW_COAP_BUF_INFO_EVENT_fixed_param *buf_info_ev; WMI_WOW_COAP_BUF_INFO_EVENTID_param_tlvs *param_buf = evt_buf; wmi_coap_tuple *tuple; uint8_t *payload; uint32_t num_tuple, num_payload; struct coap_buf_node *buf_node; int i, j; buf_info_ev = param_buf->fixed_param; if (!buf_info_ev) { wmi_debug("received null event data from target"); return QDF_STATUS_E_INVAL; } if (buf_info_ev->vdev_id > WLAN_MAX_VDEVS) { wmi_debug("received invalid vdev_id %d", buf_info_ev->vdev_id); return QDF_STATUS_E_INVAL; } info->vdev_id = buf_info_ev->vdev_id; info->req_id = buf_info_ev->pattern_id; info->more_info = buf_info_ev->more_tuples; num_tuple = param_buf->num_coap_tuple; num_payload = param_buf->num_payloads; for (i = 0, j = 0; i < num_tuple && j < num_payload; i++) { tuple = ¶m_buf->coap_tuple[i]; if (!tuple->payload_len) { wmi_err("idx %d: invalid payload len 0", i); continue; } payload = ¶m_buf->payloads[j]; j += qdf_align(tuple->payload_len, 4); if (j > num_payload) { wmi_err("idx %d: payload len overflow, pos %d - total %d", i, j, num_payload); return QDF_STATUS_E_INVAL; } buf_node = qdf_mem_malloc(sizeof(*buf_node)); if (!buf_node) return QDF_STATUS_E_NOMEM; buf_node->payload = qdf_mem_malloc(tuple->payload_len); if (!buf_node->payload) { qdf_mem_free(buf_node); return QDF_STATUS_E_NOMEM; } buf_node->tsf = tuple->tsf; qdf_mem_copy(&buf_node->src_ip, tuple->src_ip.ipv4_addr, sizeof(buf_node->src_ip)); buf_node->len = tuple->payload_len; qdf_mem_copy(buf_node->payload, payload, buf_node->len); qdf_list_insert_back(&info->info_list, &buf_node->node); wmi_debug("idx %d: src ip 0x%x tsf 0x%llx payload len %d", i, buf_node->src_ip, buf_node->tsf, buf_node->len); } wmi_debug("vdev_id %d req_id %d num_tuple %d payload len %d more info %d", info->vdev_id, info->req_id, num_tuple, num_payload, info->more_info); return QDF_STATUS_SUCCESS; } /** * wmi_coap_attach_tlv() - attach CoAP tlv handlers * @wmi_handle: wmi handle * * Return: void */ void wmi_coap_attach_tlv(wmi_unified_t wmi_handle) { struct wmi_ops *ops = wmi_handle->ops; ops->send_coap_add_pattern_cmd = send_coap_add_pattern_cmd_tlv; ops->send_coap_del_pattern_cmd = send_coap_del_pattern_cmd_tlv; ops->send_coap_add_keepalive_pattern_cmd = send_coap_add_keepalive_pattern_cmd_tlv; ops->send_coap_del_keepalive_pattern_cmd = send_coap_del_keepalive_pattern_cmd_tlv; ops->send_coap_cache_get_cmd = send_coap_cache_get_cmd_tlv; ops->extract_coap_buf_info = coap_extract_buf_info_tlv; }