|
@@ -0,0 +1,683 @@
|
|
|
+/*
|
|
|
+ * 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: defines driver functions interfacing with linux kernel
|
|
|
+ */
|
|
|
+#include <wmi_unified_param.h>
|
|
|
+#include <wlan_osif_request_manager.h>
|
|
|
+#include <osif_sync.h>
|
|
|
+#include <wlan_objmgr_psoc_obj.h>
|
|
|
+#include <wlan_hdd_main.h>
|
|
|
+#include <wlan_coap_main.h>
|
|
|
+#include <wlan_coap_ucfg_api.h>
|
|
|
+#include <wlan_cfg80211_coap.h>
|
|
|
+
|
|
|
+#define COAP_MATCH_DATA_BYTES_MAX 16
|
|
|
+#define COAP_MSG_BYTES_MAX 1152
|
|
|
+#define COAP_OFFLOAD_REPLY_CACHE_EXPTIME_MS 40000
|
|
|
+#define COAP_OFFLOAD_CACHE_GET_TIMEOUT_MS 2000
|
|
|
+
|
|
|
+#define COAP_ATTR(_name) QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_ ## _name
|
|
|
+
|
|
|
+static const struct nla_policy
|
|
|
+coap_offload_filter_policy[COAP_ATTR(FILTER_MAX) + 1] = {
|
|
|
+ [COAP_ATTR(FILTER_DEST_IPV4)] = {.type = NLA_U32},
|
|
|
+ [COAP_ATTR(FILTER_DEST_IPV4_IS_BC)] = {.type = NLA_FLAG},
|
|
|
+ [COAP_ATTR(FILTER_DEST_PORT)] = {.type = NLA_U16},
|
|
|
+ [COAP_ATTR(FILTER_MATCH_OFFSET)] = {.type = NLA_U32},
|
|
|
+ [COAP_ATTR(FILTER_MATCH_DATA)] = {
|
|
|
+ .type = NLA_BINARY, .len = COAP_MATCH_DATA_BYTES_MAX},
|
|
|
+};
|
|
|
+
|
|
|
+static const struct nla_policy
|
|
|
+coap_offload_tx_ipv4_policy[COAP_ATTR(TX_IPV4_MAX) + 1] = {
|
|
|
+ [COAP_ATTR(TX_IPV4_SRC_ADDR)] = {.type = NLA_U32},
|
|
|
+ [COAP_ATTR(TX_IPV4_SRC_PORT)] = {.type = NLA_U16},
|
|
|
+ [COAP_ATTR(TX_IPV4_DEST_ADDR)] = {.type = NLA_U32},
|
|
|
+ [COAP_ATTR(TX_IPV4_DEST_IS_BC)] = {.type = NLA_FLAG},
|
|
|
+ [COAP_ATTR(TX_IPV4_DEST_PORT)] = {.type = NLA_U16},
|
|
|
+};
|
|
|
+
|
|
|
+static const struct nla_policy
|
|
|
+coap_offload_reply_policy[COAP_ATTR(REPLY_MAX) + 1] = {
|
|
|
+ [COAP_ATTR(REPLY_SRC_IPV4)] = {.type = NLA_U32},
|
|
|
+ [COAP_ATTR(REPLY_FILTER)] =
|
|
|
+ VENDOR_NLA_POLICY_NESTED(coap_offload_filter_policy),
|
|
|
+ [COAP_ATTR(REPLY_MSG)] = {
|
|
|
+ .type = NLA_BINARY, .len = COAP_MSG_BYTES_MAX},
|
|
|
+ [COAP_ATTR(REPLY_CACHE_EXPTIME)] = {.type = NLA_U32},
|
|
|
+};
|
|
|
+
|
|
|
+static const struct nla_policy
|
|
|
+coap_offload_periodic_tx_policy[COAP_ATTR(PERIODIC_TX_MAX) + 1] = {
|
|
|
+ [COAP_ATTR(PERIODIC_TX_IPV4)] =
|
|
|
+ VENDOR_NLA_POLICY_NESTED(coap_offload_tx_ipv4_policy),
|
|
|
+ [COAP_ATTR(PERIODIC_TX_PERIOD)] = {.type = NLA_U32},
|
|
|
+ [COAP_ATTR(PERIODIC_TX_MSG)] = {
|
|
|
+ .type = NLA_BINARY, .len = COAP_MSG_BYTES_MAX},
|
|
|
+};
|
|
|
+
|
|
|
+const struct nla_policy
|
|
|
+coap_offload_policy[COAP_ATTR(MAX) + 1] = {
|
|
|
+ [COAP_ATTR(ACTION)] = {.type = NLA_U32 },
|
|
|
+ [COAP_ATTR(REQ_ID)] = {.type = NLA_U32 },
|
|
|
+ [COAP_ATTR(REPLY)] =
|
|
|
+ VENDOR_NLA_POLICY_NESTED(coap_offload_reply_policy),
|
|
|
+ [COAP_ATTR(PERIODIC_TX)] =
|
|
|
+ VENDOR_NLA_POLICY_NESTED(coap_offload_periodic_tx_policy),
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * wlan_cfg80211_coap_offload_reply_fill_filter() - fill filter for CoAP
|
|
|
+ * offload reply.
|
|
|
+ * @attr_filter: pointer to filter attribute
|
|
|
+ * @params: pointer to parameters for CoAP offload reply
|
|
|
+ *
|
|
|
+ * Return: 0 on success; error number otherwise
|
|
|
+ */
|
|
|
+static int
|
|
|
+wlan_cfg80211_coap_offload_reply_fill_filter(struct nlattr *attr_filter,
|
|
|
+ struct coap_offload_reply_param *params)
|
|
|
+{
|
|
|
+ struct nlattr *tb[COAP_ATTR(FILTER_MAX) + 1];
|
|
|
+
|
|
|
+ if (!attr_filter) {
|
|
|
+ coap_err("No ATTR filter");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!nla_data(attr_filter)) {
|
|
|
+ coap_err("Invalid filter");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (wlan_cfg80211_nla_parse_nested(tb, COAP_ATTR(FILTER_MAX),
|
|
|
+ attr_filter,
|
|
|
+ coap_offload_filter_policy)) {
|
|
|
+ coap_err("Invalid ATTR");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!tb[COAP_ATTR(FILTER_DEST_IPV4)]) {
|
|
|
+ coap_err("no ATTR dest IPv4");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ params->dest_ip_v4 = nla_get_u32(tb[COAP_ATTR(FILTER_DEST_IPV4)]);
|
|
|
+ params->dest_ip_v4_is_bc =
|
|
|
+ nla_get_flag(tb[COAP_ATTR(FILTER_DEST_IPV4_IS_BC)]);
|
|
|
+
|
|
|
+ if (!tb[COAP_ATTR(FILTER_DEST_PORT)]) {
|
|
|
+ coap_err("no ATTR dest IPv4 port");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ params->dest_udp_port = nla_get_u16(tb[COAP_ATTR(FILTER_DEST_PORT)]);
|
|
|
+
|
|
|
+ if (!tb[COAP_ATTR(FILTER_MATCH_OFFSET)]) {
|
|
|
+ coap_err("no ATTR match offset");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ params->verify_offset =
|
|
|
+ nla_get_u32(tb[COAP_ATTR(FILTER_MATCH_OFFSET)]);
|
|
|
+
|
|
|
+ if (!tb[COAP_ATTR(FILTER_MATCH_DATA)]) {
|
|
|
+ coap_err("no ATTR match data");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ params->verify_len = nla_len(tb[COAP_ATTR(FILTER_MATCH_DATA)]);
|
|
|
+ if (!params->verify_len) {
|
|
|
+ coap_err("invalid match data len");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ params->verify = nla_data(tb[COAP_ATTR(FILTER_MATCH_DATA)]);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * wlan_cfg80211_coap_offload_reply_enable() - enable CoAP offload reply
|
|
|
+ * @vdev: pointer to vdev object
|
|
|
+ * @req_id: request id
|
|
|
+ * @attr_reply: pointer to CoAP offload reply attribute
|
|
|
+ *
|
|
|
+ * Return: 0 on success; error number otherwise
|
|
|
+ */
|
|
|
+static int
|
|
|
+wlan_cfg80211_coap_offload_reply_enable(struct wlan_objmgr_vdev *vdev,
|
|
|
+ uint32_t req_id,
|
|
|
+ struct nlattr *attr_reply)
|
|
|
+{
|
|
|
+ struct nlattr *tb[COAP_ATTR(REPLY_MAX) + 1];
|
|
|
+ struct coap_offload_reply_param params = {0};
|
|
|
+ struct nlattr *attr;
|
|
|
+ QDF_STATUS status;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ if (!attr_reply) {
|
|
|
+ coap_err("No ATTR reply");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (wlan_cfg80211_nla_parse_nested(tb, COAP_ATTR(REPLY_MAX),
|
|
|
+ attr_reply,
|
|
|
+ coap_offload_reply_policy)) {
|
|
|
+ coap_err("Invalid ATTR");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ attr = tb[COAP_ATTR(REPLY_SRC_IPV4)];
|
|
|
+ if (!attr) {
|
|
|
+ coap_err("No ATTR IPv4");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ params.src_ip_v4 = nla_get_u32(attr);
|
|
|
+
|
|
|
+ attr = tb[COAP_ATTR(REPLY_FILTER)];
|
|
|
+ ret = wlan_cfg80211_coap_offload_reply_fill_filter(attr, ¶ms);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ attr = tb[COAP_ATTR(REPLY_MSG)];
|
|
|
+ if (!attr) {
|
|
|
+ coap_err("No ATTR msg");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ params.coapmsg_len = nla_len(attr);
|
|
|
+ params.coapmsg = nla_data(attr);
|
|
|
+
|
|
|
+ attr = tb[COAP_ATTR(REPLY_CACHE_EXPTIME)];
|
|
|
+ if (!attr)
|
|
|
+ params.cache_timeout = COAP_OFFLOAD_REPLY_CACHE_EXPTIME_MS;
|
|
|
+ else
|
|
|
+ params.cache_timeout = nla_get_u32(attr);
|
|
|
+
|
|
|
+ status = ucfg_coap_offload_reply_enable(vdev, ¶ms);
|
|
|
+ ret = qdf_status_to_os_return(status);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * wlan_cfg80211_coap_offload_fill_tx_ipv4() - fill IPv4 source/destination
|
|
|
+ * address/port for offload transmitting.
|
|
|
+ * @attr_ipv4: pointer to TX IPv4 attribute
|
|
|
+ * @params: pointer to parameters for CoAP offload reply
|
|
|
+ *
|
|
|
+ * Return: 0 on success; error number otherwise
|
|
|
+ */
|
|
|
+static int
|
|
|
+wlan_cfg80211_coap_offload_fill_tx_ipv4(struct nlattr *attr_ipv4,
|
|
|
+ struct coap_offload_periodic_tx_param *params)
|
|
|
+{
|
|
|
+ struct nlattr *tb[COAP_ATTR(TX_IPV4_MAX) + 1];
|
|
|
+
|
|
|
+ if (!attr_ipv4) {
|
|
|
+ coap_err("No ATTR TX IPv4");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (wlan_cfg80211_nla_parse_nested(tb, COAP_ATTR(TX_IPV4_MAX),
|
|
|
+ attr_ipv4,
|
|
|
+ coap_offload_tx_ipv4_policy)) {
|
|
|
+ coap_err("Invalid ATTR");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!tb[COAP_ATTR(TX_IPV4_SRC_ADDR)]) {
|
|
|
+ coap_err("no ATTR src addr");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ params->src_ip_v4 = nla_get_u32(tb[COAP_ATTR(TX_IPV4_SRC_ADDR)]);
|
|
|
+ if (tb[COAP_ATTR(TX_IPV4_SRC_PORT)])
|
|
|
+ params->src_udp_port =
|
|
|
+ nla_get_u32(tb[COAP_ATTR(TX_IPV4_SRC_PORT)]);
|
|
|
+
|
|
|
+ if (!tb[COAP_ATTR(TX_IPV4_DEST_ADDR)]) {
|
|
|
+ coap_err("no ATTR IPv4 dest addr");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ params->dest_ip_v4 = nla_get_u32(tb[COAP_ATTR(TX_IPV4_DEST_ADDR)]);
|
|
|
+ params->dest_ip_v4_is_bc =
|
|
|
+ nla_get_flag(tb[COAP_ATTR(TX_IPV4_DEST_IS_BC)]);
|
|
|
+
|
|
|
+ if (!tb[COAP_ATTR(TX_IPV4_DEST_PORT)]) {
|
|
|
+ coap_err("no ATTR dest IPv4 port");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ params->dest_udp_port =
|
|
|
+ nla_get_u32(tb[COAP_ATTR(TX_IPV4_DEST_PORT)]);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * wlan_cfg80211_coap_offload_periodic_tx_enable() - enable CoAP offload
|
|
|
+ * periodic transmitting
|
|
|
+ * @vdev: pointer to vdev object
|
|
|
+ * @req_id: request id
|
|
|
+ * @attr_reply: pointer to CoAP offload periodic TX attribute
|
|
|
+ *
|
|
|
+ * Return: 0 on success; error number otherwise
|
|
|
+ */
|
|
|
+static int
|
|
|
+wlan_cfg80211_coap_offload_periodic_tx_enable(struct wlan_objmgr_vdev *vdev,
|
|
|
+ uint32_t req_id,
|
|
|
+ struct nlattr *attr_periodic_tx)
|
|
|
+{
|
|
|
+ struct nlattr *tb[COAP_ATTR(PERIODIC_TX_MAX) + 1];
|
|
|
+ struct coap_offload_periodic_tx_param param = {0};
|
|
|
+ struct nlattr *attr_ipv4;
|
|
|
+ QDF_STATUS status;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ if (!attr_periodic_tx) {
|
|
|
+ coap_err("No ATTR periodic tx");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (wlan_cfg80211_nla_parse_nested(tb, COAP_ATTR(PERIODIC_TX_MAX),
|
|
|
+ attr_periodic_tx,
|
|
|
+ coap_offload_periodic_tx_policy)) {
|
|
|
+ coap_err("Invalid ATTR");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!tb[COAP_ATTR(PERIODIC_TX_PERIOD)]) {
|
|
|
+ coap_err("no ATTR period");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ param.timeout = nla_get_u32(tb[COAP_ATTR(PERIODIC_TX_PERIOD)]);
|
|
|
+ attr_ipv4 = tb[COAP_ATTR(PERIODIC_TX_IPV4)];
|
|
|
+ ret = wlan_cfg80211_coap_offload_fill_tx_ipv4(attr_ipv4, ¶m);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ param.vdev_id = wlan_vdev_get_id(vdev);
|
|
|
+ param.pattern_id = req_id;
|
|
|
+ if (!tb[COAP_ATTR(PERIODIC_TX_MSG)]) {
|
|
|
+ coap_err("no ATTR msg");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ param.coapmsg_len = nla_len(tb[COAP_ATTR(PERIODIC_TX_MSG)]);
|
|
|
+ param.coapmsg = nla_data(tb[COAP_ATTR(PERIODIC_TX_MSG)]);
|
|
|
+ status = ucfg_coap_offload_periodic_tx_enable(vdev, ¶m);
|
|
|
+ return qdf_status_to_os_return(status);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * wlan_cfg80211_coap_offload_periodic_tx_enable() - disable CoAP offload
|
|
|
+ * periodic transmitting
|
|
|
+ * @vdev: pointer to vdev object
|
|
|
+ * @req_id: request id
|
|
|
+ *
|
|
|
+ * Return: 0 on success; error number otherwise
|
|
|
+ */
|
|
|
+static int
|
|
|
+wlan_cfg80211_coap_offload_periodic_tx_disable(struct wlan_objmgr_vdev *vdev,
|
|
|
+ uint32_t req_id)
|
|
|
+{
|
|
|
+ QDF_STATUS status;
|
|
|
+
|
|
|
+ status = ucfg_coap_offload_periodic_tx_disable(vdev, req_id);
|
|
|
+ return qdf_status_to_os_return(status);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * wlan_cfg80211_dealloc_coap_buf_info() - Callback to free priv
|
|
|
+ * allocations for CoAP buffer info
|
|
|
+ * @priv: Pointer to priv data statucture
|
|
|
+ *
|
|
|
+ * Return: None
|
|
|
+ */
|
|
|
+static void wlan_cfg80211_dealloc_coap_buf_info(void *priv)
|
|
|
+{
|
|
|
+ struct coap_buf_info *info = priv;
|
|
|
+ struct coap_buf_node *cur, *next;
|
|
|
+
|
|
|
+ if (!info)
|
|
|
+ return;
|
|
|
+
|
|
|
+ qdf_list_for_each_del(&info->info_list, cur, next, node) {
|
|
|
+ qdf_list_remove_node(&info->info_list, &cur->node);
|
|
|
+ qdf_mem_free(cur->payload);
|
|
|
+ qdf_mem_free(cur);
|
|
|
+ }
|
|
|
+
|
|
|
+ qdf_list_destroy(&info->info_list);
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+wlan_cfg80211_coap_cache_get_cbk(void *context, struct coap_buf_info *info)
|
|
|
+{
|
|
|
+ struct osif_request *request;
|
|
|
+ struct coap_buf_info *priv_info;
|
|
|
+
|
|
|
+ if (!context || !info)
|
|
|
+ return;
|
|
|
+
|
|
|
+ request = osif_request_get(context);
|
|
|
+ if (!request)
|
|
|
+ return;
|
|
|
+
|
|
|
+ priv_info = osif_request_priv(request);
|
|
|
+ if (info->req_id != priv_info->req_id)
|
|
|
+ return;
|
|
|
+
|
|
|
+ qdf_list_join(&priv_info->info_list, &info->info_list);
|
|
|
+ if (info->more_info)
|
|
|
+ return;
|
|
|
+
|
|
|
+ osif_request_complete(request);
|
|
|
+ osif_request_put(request);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * wlan_cfg80211_coap_fill_buf_info() - Fill cache get response buffer
|
|
|
+ * @reply_skb : pointer to reply_skb
|
|
|
+ * @info : information of cached CoAP messages
|
|
|
+ * @index : attribute type index for nla_next_start()
|
|
|
+ *
|
|
|
+ * Return : 0 on success and errno on failure
|
|
|
+ */
|
|
|
+static int
|
|
|
+wlan_cfg80211_coap_fill_buf_info(struct sk_buff *reply_skb,
|
|
|
+ struct coap_buf_node *info, int index)
|
|
|
+{
|
|
|
+ struct nlattr *attr;
|
|
|
+
|
|
|
+ attr = nla_nest_start(reply_skb, index);
|
|
|
+ if (!attr) {
|
|
|
+ coap_err("nla_nest_start failed");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (hdd_wlan_nla_put_u64(reply_skb, COAP_ATTR(CACHE_INFO_TS),
|
|
|
+ info->tsf) ||
|
|
|
+ nla_put_u32(reply_skb, COAP_ATTR(CACHE_INFO_SRC_IPV4),
|
|
|
+ info->src_ip) ||
|
|
|
+ nla_put(reply_skb, COAP_ATTR(CACHE_INFO_MSG),
|
|
|
+ info->len, info->payload)) {
|
|
|
+ coap_err("nla_put failed");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ nla_nest_end(reply_skb, attr);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * wlan_cfg80211_coap_offload_cache_deliver() - deliver cached CoAP messages
|
|
|
+ * @wiphy: pointer to wireless wiphy structure.
|
|
|
+ * @cache_list: list of cached CoAP messages
|
|
|
+ *
|
|
|
+ * Return: 0 on success; error number otherwise
|
|
|
+ */
|
|
|
+static int
|
|
|
+wlan_cfg80211_coap_offload_cache_deliver(struct wiphy *wiphy,
|
|
|
+ qdf_list_t *cache_list)
|
|
|
+{
|
|
|
+ struct sk_buff *skb;
|
|
|
+ uint32_t skb_len = NLMSG_HDRLEN;
|
|
|
+ struct coap_buf_node *cur, *next;
|
|
|
+ struct nlattr *attr;
|
|
|
+ int i = 0, ret;
|
|
|
+
|
|
|
+ /* QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHES */
|
|
|
+ skb_len += nla_total_size(0);
|
|
|
+ qdf_list_for_each_del(cache_list, cur, next, node) {
|
|
|
+ if (!cur->len || !cur->payload)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* nest attribute */
|
|
|
+ skb_len += nla_total_size(0);
|
|
|
+
|
|
|
+ /* QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_TS */
|
|
|
+ skb_len += nla_total_size(sizeof(uint64_t));
|
|
|
+
|
|
|
+ /* QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_SRC_IPV4 */
|
|
|
+ skb_len += nla_total_size(sizeof(uint32_t));
|
|
|
+
|
|
|
+ /* QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_MSG */
|
|
|
+ skb_len += nla_total_size(cur->len);
|
|
|
+ }
|
|
|
+
|
|
|
+ skb = wlan_cfg80211_vendor_cmd_alloc_reply_skb(wiphy, skb_len);
|
|
|
+ attr = nla_nest_start(skb, COAP_ATTR(CACHES));
|
|
|
+ if (!attr) {
|
|
|
+ hdd_err("nla_nest_start failed");
|
|
|
+ wlan_cfg80211_vendor_free_skb(skb);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ qdf_list_for_each_del(cache_list, cur, next, node) {
|
|
|
+ if (!cur->len || !cur->payload)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ qdf_list_remove_node(cache_list, &cur->node);
|
|
|
+ ret = wlan_cfg80211_coap_fill_buf_info(skb, cur, i++);
|
|
|
+ if (ret) {
|
|
|
+ wlan_cfg80211_vendor_free_skb(skb);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ qdf_mem_free(cur->payload);
|
|
|
+ qdf_mem_free(cur);
|
|
|
+ }
|
|
|
+
|
|
|
+ nla_nest_end(skb, attr);
|
|
|
+ return wlan_cfg80211_vendor_cmd_reply(skb);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * wlan_cfg80211_coap_offload_cache_get() - get cached CoAP messages
|
|
|
+ * @wiphy: pointer to wireless wiphy structure.
|
|
|
+ * @vdev: pointer to vdev object
|
|
|
+ * @req_id: request id
|
|
|
+ *
|
|
|
+ * Return: 0 on success; error number otherwise
|
|
|
+ */
|
|
|
+static int
|
|
|
+wlan_cfg80211_coap_offload_cache_get(struct wiphy *wiphy,
|
|
|
+ struct wlan_objmgr_vdev *vdev,
|
|
|
+ uint32_t req_id)
|
|
|
+{
|
|
|
+ void *cookie;
|
|
|
+ QDF_STATUS status;
|
|
|
+ struct osif_request *request;
|
|
|
+ struct coap_buf_info *buf_info;
|
|
|
+ int ret;
|
|
|
+ static const struct osif_request_params params = {
|
|
|
+ .priv_size = sizeof(*buf_info),
|
|
|
+ .timeout_ms = COAP_OFFLOAD_CACHE_GET_TIMEOUT_MS,
|
|
|
+ .dealloc = wlan_cfg80211_dealloc_coap_buf_info,
|
|
|
+ };
|
|
|
+
|
|
|
+ request = osif_request_alloc(¶ms);
|
|
|
+ if (!request) {
|
|
|
+ coap_err("Request allocation failure");
|
|
|
+ status = QDF_STATUS_E_NOMEM;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ buf_info = osif_request_priv(request);
|
|
|
+ qdf_list_create(&buf_info->info_list, 0);
|
|
|
+ buf_info->req_id = req_id;
|
|
|
+ buf_info->vdev_id = wlan_vdev_get_id(vdev);
|
|
|
+
|
|
|
+ cookie = osif_request_cookie(request);
|
|
|
+ status = ucfg_coap_offload_cache_get(vdev, req_id,
|
|
|
+ wlan_cfg80211_coap_cache_get_cbk,
|
|
|
+ cookie);
|
|
|
+ if (QDF_IS_STATUS_ERROR(status)) {
|
|
|
+ coap_err("Unable to get cache");
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = osif_request_wait_for_response(request);
|
|
|
+ if (ret) {
|
|
|
+ coap_err("Target response timed out");
|
|
|
+ status = qdf_status_from_os_return(ret);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = wlan_cfg80211_coap_offload_cache_deliver(wiphy,
|
|
|
+ &buf_info->info_list);
|
|
|
+ if (ret) {
|
|
|
+ coap_err("Failed to deliver buf info");
|
|
|
+ status = qdf_status_from_os_return(ret);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+out:
|
|
|
+ if (request)
|
|
|
+ osif_request_put(request);
|
|
|
+ return qdf_status_to_os_return(status);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * wlan_cfg80211_coap_offload_reply_disable() - disable CoAP offload reply
|
|
|
+ * @wiphy: pointer to wireless wiphy structure.
|
|
|
+ * @vdev: pointer to vdev object
|
|
|
+ * @req_id: request id
|
|
|
+ *
|
|
|
+ * Return: 0 on success; error number otherwise
|
|
|
+ */
|
|
|
+static int
|
|
|
+wlan_cfg80211_coap_offload_reply_disable(struct wiphy *wiphy,
|
|
|
+ struct wlan_objmgr_vdev *vdev,
|
|
|
+ uint32_t req_id)
|
|
|
+{
|
|
|
+ void *cookie;
|
|
|
+ QDF_STATUS status;
|
|
|
+ struct osif_request *request;
|
|
|
+ struct coap_buf_info *buf_info;
|
|
|
+ int ret;
|
|
|
+ static const struct osif_request_params params = {
|
|
|
+ .priv_size = sizeof(*buf_info),
|
|
|
+ .timeout_ms = COAP_OFFLOAD_CACHE_GET_TIMEOUT_MS,
|
|
|
+ .dealloc = wlan_cfg80211_dealloc_coap_buf_info,
|
|
|
+ };
|
|
|
+
|
|
|
+ request = osif_request_alloc(¶ms);
|
|
|
+ if (!request) {
|
|
|
+ coap_err("Request allocation failure");
|
|
|
+ status = QDF_STATUS_E_NOMEM;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ buf_info = osif_request_priv(request);
|
|
|
+ qdf_list_create(&buf_info->info_list, 0);
|
|
|
+ buf_info->req_id = req_id;
|
|
|
+ buf_info->vdev_id = wlan_vdev_get_id(vdev);
|
|
|
+
|
|
|
+ cookie = osif_request_cookie(request);
|
|
|
+ status = ucfg_coap_offload_reply_disable(vdev, req_id,
|
|
|
+ wlan_cfg80211_coap_cache_get_cbk, cookie);
|
|
|
+ if (QDF_IS_STATUS_ERROR(status)) {
|
|
|
+ coap_err("Failed to disable offload reply");
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = osif_request_wait_for_response(request);
|
|
|
+ if (ret) {
|
|
|
+ coap_err("Target response timed out");
|
|
|
+ status = qdf_status_from_os_return(ret);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = wlan_cfg80211_coap_offload_cache_deliver(wiphy,
|
|
|
+ &buf_info->info_list);
|
|
|
+ if (ret) {
|
|
|
+ coap_err("Failed to deliver buf info");
|
|
|
+ status = qdf_status_from_os_return(ret);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+out:
|
|
|
+ if (request)
|
|
|
+ osif_request_put(request);
|
|
|
+ return qdf_status_to_os_return(status);
|
|
|
+}
|
|
|
+
|
|
|
+int
|
|
|
+wlan_cfg80211_coap_offload(struct wiphy *wiphy, struct wlan_objmgr_vdev *vdev,
|
|
|
+ const void *data, int data_len)
|
|
|
+{
|
|
|
+ struct nlattr *tb[COAP_ATTR(MAX) + 1];
|
|
|
+ struct nlattr *attr;
|
|
|
+ uint32_t action, req_id;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ if (wlan_cfg80211_nla_parse(tb, COAP_ATTR(MAX),
|
|
|
+ data, data_len, coap_offload_policy)) {
|
|
|
+ coap_err("Invalid ATTR");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!tb[COAP_ATTR(ACTION)]) {
|
|
|
+ coap_err("no attr action");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!tb[COAP_ATTR(REQ_ID)]) {
|
|
|
+ coap_err("no attr req id");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ action = nla_get_u32(tb[COAP_ATTR(ACTION)]);
|
|
|
+ req_id = nla_get_u32(tb[COAP_ATTR(REQ_ID)]);
|
|
|
+ switch (action) {
|
|
|
+ case QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_REPLY_ENABLE:
|
|
|
+ attr = tb[COAP_ATTR(REPLY)];
|
|
|
+ ret = wlan_cfg80211_coap_offload_reply_enable(vdev, req_id,
|
|
|
+ attr);
|
|
|
+ break;
|
|
|
+ case QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_REPLY_DISABLE:
|
|
|
+ ret = wlan_cfg80211_coap_offload_reply_disable(wiphy, vdev,
|
|
|
+ req_id);
|
|
|
+ break;
|
|
|
+ case QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_PERIODIC_TX_ENABLE:
|
|
|
+ attr = tb[COAP_ATTR(PERIODIC_TX)];
|
|
|
+ ret = wlan_cfg80211_coap_offload_periodic_tx_enable(vdev,
|
|
|
+ req_id,
|
|
|
+ attr);
|
|
|
+ break;
|
|
|
+ case QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_PERIODIC_TX_DISABLE:
|
|
|
+ ret = wlan_cfg80211_coap_offload_periodic_tx_disable(vdev,
|
|
|
+ req_id);
|
|
|
+ break;
|
|
|
+ case QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_CACHE_GET:
|
|
|
+ ret = wlan_cfg80211_coap_offload_cache_get(wiphy, vdev,
|
|
|
+ req_id);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ ret = -EINVAL;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ coap_debug("vdev_id %u action %u req id %u ret %d",
|
|
|
+ wlan_vdev_get_id(vdev), action, req_id, ret);
|
|
|
+ return ret;
|
|
|
+}
|