// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2021 Intel Corporation */ #include "mvm.h" #include <linux/nl80211-vnd-intel.h> #include <net/netlink.h> static const struct nla_policy iwl_mvm_vendor_attr_policy[NUM_IWL_MVM_VENDOR_ATTR] = { [IWL_MVM_VENDOR_ATTR_ROAMING_FORBIDDEN] = { .type = NLA_U8 }, [IWL_MVM_VENDOR_ATTR_AUTH_MODE] = { .type = NLA_U32 }, [IWL_MVM_VENDOR_ATTR_CHANNEL_NUM] = { .type = NLA_U8 }, [IWL_MVM_VENDOR_ATTR_SSID] = { .type = NLA_BINARY, .len = IEEE80211_MAX_SSID_LEN }, [IWL_MVM_VENDOR_ATTR_BAND] = { .type = NLA_U8 }, [IWL_MVM_VENDOR_ATTR_COLLOC_CHANNEL] = { .type = NLA_U8 }, [IWL_MVM_VENDOR_ATTR_COLLOC_ADDR] = { .type = NLA_BINARY, .len = ETH_ALEN }, }; static int iwl_mvm_vendor_get_csme_conn_info(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int data_len) { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_mvm_csme_conn_info *csme_conn_info; struct sk_buff *skb; int err = 0; mutex_lock(&mvm->mutex); csme_conn_info = iwl_mvm_get_csme_conn_info(mvm); if (!csme_conn_info) { err = -EINVAL; goto out_unlock; } skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, 200); if (!skb) { err = -ENOMEM; goto out_unlock; } if (nla_put_u32(skb, IWL_MVM_VENDOR_ATTR_AUTH_MODE, csme_conn_info->conn_info.auth_mode) || nla_put(skb, IWL_MVM_VENDOR_ATTR_SSID, csme_conn_info->conn_info.ssid_len, csme_conn_info->conn_info.ssid) || nla_put_u32(skb, IWL_MVM_VENDOR_ATTR_STA_CIPHER, csme_conn_info->conn_info.pairwise_cipher) || nla_put_u8(skb, IWL_MVM_VENDOR_ATTR_CHANNEL_NUM, csme_conn_info->conn_info.channel) || nla_put(skb, IWL_MVM_VENDOR_ATTR_ADDR, ETH_ALEN, csme_conn_info->conn_info.bssid)) { kfree_skb(skb); err = -ENOBUFS; } out_unlock: mutex_unlock(&mvm->mutex); if (err) return err; return cfg80211_vendor_cmd_reply(skb); } static int iwl_mvm_vendor_host_get_ownership(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int data_len) { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); int ret; mutex_lock(&mvm->mutex); ret = iwl_mvm_mei_get_ownership(mvm); mutex_unlock(&mvm->mutex); return ret; } static const struct wiphy_vendor_command iwl_mvm_vendor_commands[] = { { .info = { .vendor_id = INTEL_OUI, .subcmd = IWL_MVM_VENDOR_CMD_GET_CSME_CONN_INFO, }, .doit = iwl_mvm_vendor_get_csme_conn_info, .flags = WIPHY_VENDOR_CMD_NEED_WDEV, .policy = iwl_mvm_vendor_attr_policy, .maxattr = MAX_IWL_MVM_VENDOR_ATTR, }, { .info = { .vendor_id = INTEL_OUI, .subcmd = IWL_MVM_VENDOR_CMD_HOST_GET_OWNERSHIP, }, .doit = iwl_mvm_vendor_host_get_ownership, .flags = WIPHY_VENDOR_CMD_NEED_WDEV, .policy = iwl_mvm_vendor_attr_policy, .maxattr = MAX_IWL_MVM_VENDOR_ATTR, }, }; enum iwl_mvm_vendor_events_idx { /* 0x0 - 0x3 are deprecated */ IWL_MVM_VENDOR_EVENT_IDX_ROAMING_FORBIDDEN = 4, NUM_IWL_MVM_VENDOR_EVENT_IDX }; static const struct nl80211_vendor_cmd_info iwl_mvm_vendor_events[NUM_IWL_MVM_VENDOR_EVENT_IDX] = { [IWL_MVM_VENDOR_EVENT_IDX_ROAMING_FORBIDDEN] = { .vendor_id = INTEL_OUI, .subcmd = IWL_MVM_VENDOR_CMD_ROAMING_FORBIDDEN_EVENT, }, }; void iwl_mvm_vendor_cmds_register(struct iwl_mvm *mvm) { mvm->hw->wiphy->vendor_commands = iwl_mvm_vendor_commands; mvm->hw->wiphy->n_vendor_commands = ARRAY_SIZE(iwl_mvm_vendor_commands); mvm->hw->wiphy->vendor_events = iwl_mvm_vendor_events; mvm->hw->wiphy->n_vendor_events = ARRAY_SIZE(iwl_mvm_vendor_events); } void iwl_mvm_send_roaming_forbidden_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, bool forbidden) { struct sk_buff *msg = cfg80211_vendor_event_alloc(mvm->hw->wiphy, ieee80211_vif_to_wdev(vif), 200, IWL_MVM_VENDOR_EVENT_IDX_ROAMING_FORBIDDEN, GFP_ATOMIC); if (!msg) return; if (WARN_ON(!vif)) return; if (nla_put(msg, IWL_MVM_VENDOR_ATTR_VIF_ADDR, ETH_ALEN, vif->addr) || nla_put_u8(msg, IWL_MVM_VENDOR_ATTR_ROAMING_FORBIDDEN, forbidden)) goto nla_put_failure; cfg80211_vendor_event(msg, GFP_ATOMIC); return; nla_put_failure: kfree_skb(msg); }