1bfcfdb59SEmmanuel Grumbach // SPDX-License-Identifier: GPL-2.0-only
2bfcfdb59SEmmanuel Grumbach /*
3bfcfdb59SEmmanuel Grumbach  * Copyright (C) 2021 Intel Corporation
4bfcfdb59SEmmanuel Grumbach  */
5bfcfdb59SEmmanuel Grumbach #include "mvm.h"
6bfcfdb59SEmmanuel Grumbach #include <linux/nl80211-vnd-intel.h>
7e6e59044SJakub Kicinski #include <net/netlink.h>
8bfcfdb59SEmmanuel Grumbach 
9bfcfdb59SEmmanuel Grumbach static const struct nla_policy
10bfcfdb59SEmmanuel Grumbach iwl_mvm_vendor_attr_policy[NUM_IWL_MVM_VENDOR_ATTR] = {
11bfcfdb59SEmmanuel Grumbach 	[IWL_MVM_VENDOR_ATTR_ROAMING_FORBIDDEN] = { .type = NLA_U8 },
12bfcfdb59SEmmanuel Grumbach 	[IWL_MVM_VENDOR_ATTR_AUTH_MODE] = { .type = NLA_U32 },
13bfcfdb59SEmmanuel Grumbach 	[IWL_MVM_VENDOR_ATTR_CHANNEL_NUM] = { .type = NLA_U8 },
14bfcfdb59SEmmanuel Grumbach 	[IWL_MVM_VENDOR_ATTR_SSID] = { .type = NLA_BINARY,
15bfcfdb59SEmmanuel Grumbach 				       .len = IEEE80211_MAX_SSID_LEN },
16bfcfdb59SEmmanuel Grumbach 	[IWL_MVM_VENDOR_ATTR_BAND] = { .type = NLA_U8 },
17bfcfdb59SEmmanuel Grumbach 	[IWL_MVM_VENDOR_ATTR_COLLOC_CHANNEL] = { .type = NLA_U8 },
18bfcfdb59SEmmanuel Grumbach 	[IWL_MVM_VENDOR_ATTR_COLLOC_ADDR] = { .type = NLA_BINARY, .len = ETH_ALEN },
19bfcfdb59SEmmanuel Grumbach };
20bfcfdb59SEmmanuel Grumbach 
iwl_mvm_vendor_get_csme_conn_info(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)21bfcfdb59SEmmanuel Grumbach static int iwl_mvm_vendor_get_csme_conn_info(struct wiphy *wiphy,
22bfcfdb59SEmmanuel Grumbach 					     struct wireless_dev *wdev,
23bfcfdb59SEmmanuel Grumbach 					     const void *data, int data_len)
24bfcfdb59SEmmanuel Grumbach {
25bfcfdb59SEmmanuel Grumbach 	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
26bfcfdb59SEmmanuel Grumbach 	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
27bfcfdb59SEmmanuel Grumbach 	struct iwl_mvm_csme_conn_info *csme_conn_info;
28bfcfdb59SEmmanuel Grumbach 	struct sk_buff *skb;
29bfcfdb59SEmmanuel Grumbach 	int err = 0;
30bfcfdb59SEmmanuel Grumbach 
31bfcfdb59SEmmanuel Grumbach 	mutex_lock(&mvm->mutex);
32bfcfdb59SEmmanuel Grumbach 	csme_conn_info = iwl_mvm_get_csme_conn_info(mvm);
33bfcfdb59SEmmanuel Grumbach 
34bfcfdb59SEmmanuel Grumbach 	if (!csme_conn_info) {
35bfcfdb59SEmmanuel Grumbach 		err = -EINVAL;
36bfcfdb59SEmmanuel Grumbach 		goto out_unlock;
37bfcfdb59SEmmanuel Grumbach 	}
38bfcfdb59SEmmanuel Grumbach 
39bfcfdb59SEmmanuel Grumbach 	skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, 200);
40bfcfdb59SEmmanuel Grumbach 	if (!skb) {
41bfcfdb59SEmmanuel Grumbach 		err = -ENOMEM;
42bfcfdb59SEmmanuel Grumbach 		goto out_unlock;
43bfcfdb59SEmmanuel Grumbach 	}
44bfcfdb59SEmmanuel Grumbach 
45bfcfdb59SEmmanuel Grumbach 	if (nla_put_u32(skb, IWL_MVM_VENDOR_ATTR_AUTH_MODE,
46bfcfdb59SEmmanuel Grumbach 			csme_conn_info->conn_info.auth_mode) ||
47bfcfdb59SEmmanuel Grumbach 	    nla_put(skb, IWL_MVM_VENDOR_ATTR_SSID,
48bfcfdb59SEmmanuel Grumbach 		    csme_conn_info->conn_info.ssid_len,
49bfcfdb59SEmmanuel Grumbach 		    csme_conn_info->conn_info.ssid) ||
50bfcfdb59SEmmanuel Grumbach 	    nla_put_u32(skb, IWL_MVM_VENDOR_ATTR_STA_CIPHER,
51bfcfdb59SEmmanuel Grumbach 			csme_conn_info->conn_info.pairwise_cipher) ||
52bfcfdb59SEmmanuel Grumbach 	    nla_put_u8(skb, IWL_MVM_VENDOR_ATTR_CHANNEL_NUM,
53bfcfdb59SEmmanuel Grumbach 		       csme_conn_info->conn_info.channel) ||
54bfcfdb59SEmmanuel Grumbach 	    nla_put(skb, IWL_MVM_VENDOR_ATTR_ADDR, ETH_ALEN,
55bfcfdb59SEmmanuel Grumbach 		    csme_conn_info->conn_info.bssid)) {
56bfcfdb59SEmmanuel Grumbach 		kfree_skb(skb);
57bfcfdb59SEmmanuel Grumbach 		err = -ENOBUFS;
58bfcfdb59SEmmanuel Grumbach 	}
59bfcfdb59SEmmanuel Grumbach 
60bfcfdb59SEmmanuel Grumbach out_unlock:
61bfcfdb59SEmmanuel Grumbach 	mutex_unlock(&mvm->mutex);
62bfcfdb59SEmmanuel Grumbach 	if (err)
63bfcfdb59SEmmanuel Grumbach 		return err;
64bfcfdb59SEmmanuel Grumbach 
65bfcfdb59SEmmanuel Grumbach 	return cfg80211_vendor_cmd_reply(skb);
66bfcfdb59SEmmanuel Grumbach }
67bfcfdb59SEmmanuel Grumbach 
iwl_mvm_vendor_host_get_ownership(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)68bfcfdb59SEmmanuel Grumbach static int iwl_mvm_vendor_host_get_ownership(struct wiphy *wiphy,
69bfcfdb59SEmmanuel Grumbach 					     struct wireless_dev *wdev,
70bfcfdb59SEmmanuel Grumbach 					     const void *data, int data_len)
71bfcfdb59SEmmanuel Grumbach {
72bfcfdb59SEmmanuel Grumbach 	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
73bfcfdb59SEmmanuel Grumbach 	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
74*e6e91ec9SEmmanuel Grumbach 	int ret;
75bfcfdb59SEmmanuel Grumbach 
76bfcfdb59SEmmanuel Grumbach 	mutex_lock(&mvm->mutex);
77*e6e91ec9SEmmanuel Grumbach 	ret = iwl_mvm_mei_get_ownership(mvm);
78bfcfdb59SEmmanuel Grumbach 	mutex_unlock(&mvm->mutex);
79bfcfdb59SEmmanuel Grumbach 
80*e6e91ec9SEmmanuel Grumbach 	return ret;
81bfcfdb59SEmmanuel Grumbach }
82bfcfdb59SEmmanuel Grumbach 
83bfcfdb59SEmmanuel Grumbach static const struct wiphy_vendor_command iwl_mvm_vendor_commands[] = {
84bfcfdb59SEmmanuel Grumbach 	{
85bfcfdb59SEmmanuel Grumbach 		.info = {
86bfcfdb59SEmmanuel Grumbach 			.vendor_id = INTEL_OUI,
87bfcfdb59SEmmanuel Grumbach 			.subcmd = IWL_MVM_VENDOR_CMD_GET_CSME_CONN_INFO,
88bfcfdb59SEmmanuel Grumbach 		},
89bfcfdb59SEmmanuel Grumbach 		.doit = iwl_mvm_vendor_get_csme_conn_info,
90bfcfdb59SEmmanuel Grumbach 		.flags = WIPHY_VENDOR_CMD_NEED_WDEV,
91bfcfdb59SEmmanuel Grumbach 		.policy = iwl_mvm_vendor_attr_policy,
92bfcfdb59SEmmanuel Grumbach 		.maxattr = MAX_IWL_MVM_VENDOR_ATTR,
93bfcfdb59SEmmanuel Grumbach 	},
94bfcfdb59SEmmanuel Grumbach 	{
95bfcfdb59SEmmanuel Grumbach 		.info = {
96bfcfdb59SEmmanuel Grumbach 			.vendor_id = INTEL_OUI,
97bfcfdb59SEmmanuel Grumbach 			.subcmd = IWL_MVM_VENDOR_CMD_HOST_GET_OWNERSHIP,
98bfcfdb59SEmmanuel Grumbach 		},
99bfcfdb59SEmmanuel Grumbach 		.doit = iwl_mvm_vendor_host_get_ownership,
100bfcfdb59SEmmanuel Grumbach 		.flags = WIPHY_VENDOR_CMD_NEED_WDEV,
101bfcfdb59SEmmanuel Grumbach 		.policy = iwl_mvm_vendor_attr_policy,
102bfcfdb59SEmmanuel Grumbach 		.maxattr = MAX_IWL_MVM_VENDOR_ATTR,
103bfcfdb59SEmmanuel Grumbach 	},
104bfcfdb59SEmmanuel Grumbach };
105bfcfdb59SEmmanuel Grumbach 
106bfcfdb59SEmmanuel Grumbach enum iwl_mvm_vendor_events_idx {
107bfcfdb59SEmmanuel Grumbach         /* 0x0 - 0x3 are deprecated */
108bfcfdb59SEmmanuel Grumbach         IWL_MVM_VENDOR_EVENT_IDX_ROAMING_FORBIDDEN = 4,
109bfcfdb59SEmmanuel Grumbach         NUM_IWL_MVM_VENDOR_EVENT_IDX
110bfcfdb59SEmmanuel Grumbach };
111bfcfdb59SEmmanuel Grumbach 
112bfcfdb59SEmmanuel Grumbach static const struct nl80211_vendor_cmd_info
113bfcfdb59SEmmanuel Grumbach iwl_mvm_vendor_events[NUM_IWL_MVM_VENDOR_EVENT_IDX] = {
114bfcfdb59SEmmanuel Grumbach 	[IWL_MVM_VENDOR_EVENT_IDX_ROAMING_FORBIDDEN] = {
115bfcfdb59SEmmanuel Grumbach 		.vendor_id = INTEL_OUI,
116bfcfdb59SEmmanuel Grumbach 		.subcmd = IWL_MVM_VENDOR_CMD_ROAMING_FORBIDDEN_EVENT,
117bfcfdb59SEmmanuel Grumbach 	},
118bfcfdb59SEmmanuel Grumbach };
119bfcfdb59SEmmanuel Grumbach 
iwl_mvm_vendor_cmds_register(struct iwl_mvm * mvm)120bfcfdb59SEmmanuel Grumbach void iwl_mvm_vendor_cmds_register(struct iwl_mvm *mvm)
121bfcfdb59SEmmanuel Grumbach {
122bfcfdb59SEmmanuel Grumbach 	mvm->hw->wiphy->vendor_commands = iwl_mvm_vendor_commands;
123bfcfdb59SEmmanuel Grumbach 	mvm->hw->wiphy->n_vendor_commands = ARRAY_SIZE(iwl_mvm_vendor_commands);
124bfcfdb59SEmmanuel Grumbach 	mvm->hw->wiphy->vendor_events = iwl_mvm_vendor_events;
125bfcfdb59SEmmanuel Grumbach 	mvm->hw->wiphy->n_vendor_events = ARRAY_SIZE(iwl_mvm_vendor_events);
126bfcfdb59SEmmanuel Grumbach }
127bfcfdb59SEmmanuel Grumbach 
iwl_mvm_send_roaming_forbidden_event(struct iwl_mvm * mvm,struct ieee80211_vif * vif,bool forbidden)128bfcfdb59SEmmanuel Grumbach void iwl_mvm_send_roaming_forbidden_event(struct iwl_mvm *mvm,
129bfcfdb59SEmmanuel Grumbach 					  struct ieee80211_vif *vif,
130bfcfdb59SEmmanuel Grumbach 					  bool forbidden)
131bfcfdb59SEmmanuel Grumbach {
132bfcfdb59SEmmanuel Grumbach 	struct sk_buff *msg =
133bfcfdb59SEmmanuel Grumbach 		cfg80211_vendor_event_alloc(mvm->hw->wiphy,
134bfcfdb59SEmmanuel Grumbach 					    ieee80211_vif_to_wdev(vif),
135bfcfdb59SEmmanuel Grumbach 					    200, IWL_MVM_VENDOR_EVENT_IDX_ROAMING_FORBIDDEN,
136bfcfdb59SEmmanuel Grumbach 					    GFP_ATOMIC);
137bfcfdb59SEmmanuel Grumbach 	if (!msg)
138bfcfdb59SEmmanuel Grumbach 		return;
139bfcfdb59SEmmanuel Grumbach 
140bfcfdb59SEmmanuel Grumbach 	if (WARN_ON(!vif))
141bfcfdb59SEmmanuel Grumbach 		return;
142bfcfdb59SEmmanuel Grumbach 
143bfcfdb59SEmmanuel Grumbach 	if (nla_put(msg, IWL_MVM_VENDOR_ATTR_VIF_ADDR,
144bfcfdb59SEmmanuel Grumbach 		    ETH_ALEN, vif->addr) ||
145bfcfdb59SEmmanuel Grumbach 	    nla_put_u8(msg, IWL_MVM_VENDOR_ATTR_ROAMING_FORBIDDEN, forbidden))
146bfcfdb59SEmmanuel Grumbach 		goto nla_put_failure;
147bfcfdb59SEmmanuel Grumbach 
148bfcfdb59SEmmanuel Grumbach 	cfg80211_vendor_event(msg, GFP_ATOMIC);
149bfcfdb59SEmmanuel Grumbach 	return;
150bfcfdb59SEmmanuel Grumbach 
151bfcfdb59SEmmanuel Grumbach  nla_put_failure:
152bfcfdb59SEmmanuel Grumbach 	kfree_skb(msg);
153bfcfdb59SEmmanuel Grumbach }
154