/* * Copyright (c) 2021, The Linux Foundation. All rights reserved. * Copyright (c) 2022-2023 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: wlan_hdd_mdns_offload.c * * WLAN Host Device Driver MDNS offload interface implementation. */ /* Include Files */ #include #include "os_if_fwol.h" #include "wlan_hdd_mdns_offload.h" #include "wlan_hdd_main.h" #define MDNS_ENABLE \ QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_ENABLE #define MDNS_TABLE \ QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_TABLE #define MDNS_ENTRY \ QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_ENTRY #define MDNS_FQDN \ QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_FQDN #define MDNS_RESOURCE_RECORDS_COUNT \ QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_ANSWER_RESOURCE_RECORDS_COUNT #define MDNS_ANSWER_PAYLOAD \ QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_ANSWER_PAYLOAD const struct nla_policy wlan_hdd_set_mdns_offload_policy[ QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_MAX + 1] = { [MDNS_ENABLE] = {.type = NLA_FLAG}, [MDNS_TABLE] = {.type = NLA_NESTED}, [MDNS_ENTRY] = {.type = NLA_NESTED}, [MDNS_FQDN] = {.type = NLA_STRING, .len = MAX_FQDN_LEN - 1 }, [MDNS_RESOURCE_RECORDS_COUNT] = {.type = NLA_U16}, [MDNS_ANSWER_PAYLOAD] = {.type = NLA_BINARY, .len = MAX_MDNS_RESP_LEN }, }; static int hdd_extract_mdns_attr(struct nlattr *tb[], struct mdns_config_info *mdns_info) { int len, count; /* Configure mDNS FQDN*/ if (!tb[MDNS_FQDN]) { hdd_err_rl("mDNS_FQDN attr failed"); return -EINVAL; } len = nla_len(tb[MDNS_FQDN]); mdns_info->fqdn_len = len; mdns_info->fqdn_type = MDNS_FQDN_TYPE_GENERAL; wlan_cfg80211_nla_strscpy(mdns_info->fqdn_data, tb[MDNS_FQDN], sizeof(mdns_info->fqdn_data)); /* Configure mDNS Answer Payload*/ if (!tb[MDNS_RESOURCE_RECORDS_COUNT]) { hdd_err_rl("mDNS_RR count attr failed"); return -EINVAL; } count = nla_get_u16(tb[MDNS_RESOURCE_RECORDS_COUNT]); mdns_info->resource_record_count = count; if (!tb[MDNS_ANSWER_PAYLOAD]) { hdd_err_rl("mDNS_Response attr failed"); return -EINVAL; } len = nla_len(tb[MDNS_ANSWER_PAYLOAD]); mdns_info->answer_payload_len = len; nla_memcpy(mdns_info->answer_payload_data, nla_data(tb[MDNS_ANSWER_PAYLOAD]), sizeof(mdns_info->answer_payload_data)); return 0; } /** * __wlan_hdd_cfg80211_set_mdns_offload() - mDNS Offload configuration * @wiphy: wiphy device pointer * @wdev: wireless device pointer * @data: Vendor command data buffer * @data_len: Buffer length * * Handles QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_MAX. * * Return: Error code. */ static int __wlan_hdd_cfg80211_set_mdns_offload(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int data_len) { struct net_device *dev = wdev->netdev; struct hdd_context *hdd_ctx = wiphy_priv(wiphy); struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); struct nlattr *curr_attr; struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_MAX + 1]; struct nlattr *tb2[QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_MAX + 1]; struct mdns_config_info *mdns_info; bool is_mdns_enable; int errno, rem; hdd_enter_dev(dev); errno = wlan_hdd_validate_context(hdd_ctx); if (errno) return errno; if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) { hdd_err_rl("Command not allowed in FTM mode"); return -EINVAL; } if (!(adapter->device_mode == QDF_STA_MODE || adapter->device_mode == QDF_P2P_CLIENT_MODE)) { hdd_err_rl("mDNS is only supported in STA or P2P CLI modes!"); return -ENOTSUPP; } if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_MAX, data, data_len, wlan_hdd_set_mdns_offload_policy)) { hdd_err_rl("invalid attr"); return -EINVAL; } if (!tb[MDNS_ENABLE]) { os_if_fwol_disable_mdns_offload(hdd_ctx->psoc); hdd_debug_rl("MDNS offload is disabled"); return 0; } is_mdns_enable = nla_get_flag(tb[MDNS_ENABLE]); if (is_mdns_enable && !tb[MDNS_TABLE]) { hdd_err_rl("Invalid mDNS table of records"); return -EINVAL; } mdns_info = qdf_mem_malloc(sizeof(*mdns_info)); if (!mdns_info) return -ENOMEM; mdns_info->enable = is_mdns_enable; mdns_info->vdev_id = adapter->deflink->vdev_id; nla_for_each_nested(curr_attr, tb[MDNS_TABLE], rem) { if (wlan_cfg80211_nla_parse(tb2, QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_MAX, nla_data(curr_attr), nla_len(curr_attr), wlan_hdd_set_mdns_offload_policy)) { hdd_err_rl("Failed to parse mDNS table of records"); errno = -EINVAL; goto out; } errno = hdd_extract_mdns_attr(tb2, mdns_info); if (errno) { hdd_err_rl("Failed to extract MDNS nested attrs"); goto out; } } os_if_fwol_enable_mdns_offload(hdd_ctx->psoc, mdns_info); out: qdf_mem_free(mdns_info); hdd_exit(); return errno; } /** * wlan_hdd_cfg80211_set_mdns_offload() - mDNS Offload configuration * @wiphy: wiphy device pointer * @wdev: wireless device pointer * @data: Vendor command data buffer * @data_len: Buffer length * * Handles QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_MAX. * * Return: EOK or other error codes. */ int wlan_hdd_cfg80211_set_mdns_offload(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int data_len) { int errno; struct osif_vdev_sync *vdev_sync; errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync); if (errno) return errno; errno = __wlan_hdd_cfg80211_set_mdns_offload(wiphy, wdev, data, data_len); osif_vdev_sync_op_stop(vdev_sync); return errno; }