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