/* * Copyright (c) 2019-2020 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: defines driver functions interfacing with linux kernel */ #include #include #include #include #include #include #include #include #include #include #include #include "wlan_hdd_main.h" #include "cfg_ucfg_api.h" #include "wlan_hdd_object_manager.h" const struct nla_policy interop_issues_ap_policy[QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_MAX + 1] = { [QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_TYPE] = { .type = NLA_U32, .len = sizeof(uint32_t) }, [QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_LIST] = { .type = NLA_U32, .len = sizeof(uint32_t) }, [QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_BSSID] = VENDOR_NLA_POLICY_MAC_ADDR, }; /** * wlan_cfg80211_send_interop_issues_ap_cb() - report information * @data: interop issues ap mac received from fw * * Generate a wlan interop issues ap info package and send it to user * space daemon through netlink. * * Return: none */ static void wlan_cfg80211_send_interop_issues_ap_cb( struct wlan_interop_issues_ap_event *data) { struct wlan_objmgr_pdev *pdev; struct pdev_osif_priv *os_priv; struct sk_buff *skb; uint32_t index, len; if (!data) { osif_err("Invalid result."); return; } pdev = data->pdev; if (!pdev) { osif_err("pdev is null."); return; } os_priv = wlan_pdev_get_ospriv(pdev); if (!os_priv) { osif_err("os_priv is null."); return; } index = QCA_NL80211_VENDOR_SUBCMD_INTEROP_ISSUES_AP_INDEX; len = nla_total_size(QDF_MAC_ADDR_SIZE + NLMSG_HDRLEN); skb = wlan_cfg80211_vendor_event_alloc(os_priv->wiphy, NULL, len, index, GFP_KERNEL); if (!skb) { osif_err("skb alloc failed"); return; } osif_debug("interop issues ap mac:" QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(data->rap_addr.bytes)); if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_BSSID, QDF_MAC_ADDR_SIZE, data->rap_addr.bytes)) { osif_err("nla put fail"); wlan_cfg80211_vendor_free_skb(skb); return; } wlan_cfg80211_vendor_event(skb, GFP_KERNEL); } static void wlan_interop_issues_ap_register_cbk(struct wlan_objmgr_pdev *pdev) { struct wlan_interop_issues_ap_callbacks cb; cb.os_if_interop_issues_ap_event_handler = wlan_cfg80211_send_interop_issues_ap_cb; ucfg_register_interop_issues_ap_callback(pdev, &cb); } /** * wlan_parse_interop_issues_ap() - parse the interop issues ap info * @interop_issues_ap: the pointer of interop issues ap * @attr: list of attributes * * Return: 0 on success; error number on failure */ static int wlan_parse_interop_issues_ap(struct qdf_mac_addr *interop_issues_ap, struct nlattr *attr) { struct nlattr *tb2[QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_MAX + 1]; struct nlattr *curr_attr = NULL; uint32_t rem; qdf_size_t i = 0; nla_for_each_nested(curr_attr, attr, rem) { if (i == MAX_INTEROP_ISSUES_AP_NUM) { osif_err("Ignoring excess"); break; } if (wlan_cfg80211_nla_parse(tb2, QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_MAX, nla_data(curr_attr), nla_len(curr_attr), interop_issues_ap_policy)) { osif_err("nla_parse failed"); return -EINVAL; } if (!tb2[QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_BSSID]) { osif_err("attr addr failed"); return -EINVAL; } nla_memcpy(interop_issues_ap[i].bytes, tb2[QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_BSSID], QDF_MAC_ADDR_SIZE); osif_debug(QDF_MAC_ADDR_FMT, QDF_MAC_ADDR_REF(interop_issues_ap[i].bytes)); i++; } return i; } /** * __wlan_cfg80211_set_interop_issues_ap_config() - set config status * @wiphy: WIPHY structure pointer * @wdev: Wireless device structure pointer * @data: Pointer to the data received * @data_len: Length of the data received * * Return: 0 on success and errno on failure */ static int __wlan_cfg80211_set_interop_issues_ap_config(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int data_len) { struct net_device *dev = wdev->netdev; struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev); struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_MAX + 1]; struct nlattr *attr; uint32_t count = 0; struct wlan_interop_issues_ap_info interop_issues_ap = {0}; struct wlan_objmgr_psoc *psoc; struct wlan_objmgr_vdev *vdev; if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { hdd_err("Command not allowed in FTM mode"); return -EPERM; } vdev = hdd_objmgr_get_vdev_by_user(adapter->deflink, WLAN_INTEROP_ISSUES_AP_ID); if (!vdev) { osif_err("Invalid vdev"); return -EINVAL; } psoc = wlan_vdev_get_psoc(vdev); hdd_objmgr_put_vdev_by_user(vdev, WLAN_INTEROP_ISSUES_AP_ID); if (!psoc) { osif_err("Invalid psoc"); return -EINVAL; } if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_MAX, data, data_len, interop_issues_ap_policy)) { osif_err("Invalid ATTR"); return -EINVAL; } attr = tb[QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_LIST]; if (attr) { count = wlan_parse_interop_issues_ap(interop_issues_ap.rap_items, attr); if (count < 0) return -EINVAL; } osif_debug("Num of interop issues ap: %d", count); interop_issues_ap.count = count; interop_issues_ap.detect_enable = true; /* * need to figure out a converged way of obtaining the vdev for * a given netdev that doesn't involve the legacy mechanism. */ ucfg_set_interop_issues_ap_config(psoc, &interop_issues_ap); return 0; } int wlan_cfg80211_set_interop_issues_ap_config(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int data_len) { struct osif_psoc_sync *psoc_sync; int ret; ret = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); if (ret) return ret; ret = __wlan_cfg80211_set_interop_issues_ap_config(wiphy, wdev, data, data_len); osif_psoc_sync_op_stop(psoc_sync); return ret; } void wlan_cfg80211_init_interop_issues_ap(struct wlan_objmgr_pdev *pdev) { /* * the special mac is used to trigger uplayer sets * interop issues ap list to fw when driver reloads but * cnss-daemon does not restart. */ uint8_t fmac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; struct wlan_interop_issues_ap_info interop_issues_ap = {0}; struct wlan_interop_issues_ap_event data; struct wlan_objmgr_psoc *psoc; if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { hdd_err("Operation not supported in FTM mode"); return; } wlan_interop_issues_ap_register_cbk(pdev); psoc = wlan_pdev_get_psoc(pdev); if (!psoc) { osif_err("Invalid psoc"); return; } interop_issues_ap.detect_enable = true; ucfg_set_interop_issues_ap_config(psoc, &interop_issues_ap); data.pdev = pdev; qdf_mem_copy(data.rap_addr.bytes, fmac, QDF_MAC_ADDR_SIZE); wlan_cfg80211_send_interop_issues_ap_cb(&data); }