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, approxlen: 200); |
40 | if (!skb) { |
41 | err = -ENOMEM; |
42 | goto out_unlock; |
43 | } |
44 | |
45 | if (nla_put_u32(skb, attrtype: IWL_MVM_VENDOR_ATTR_AUTH_MODE, |
46 | value: csme_conn_info->conn_info.auth_mode) || |
47 | nla_put(skb, attrtype: IWL_MVM_VENDOR_ATTR_SSID, |
48 | attrlen: csme_conn_info->conn_info.ssid_len, |
49 | data: csme_conn_info->conn_info.ssid) || |
50 | nla_put_u32(skb, attrtype: IWL_MVM_VENDOR_ATTR_STA_CIPHER, |
51 | value: csme_conn_info->conn_info.pairwise_cipher) || |
52 | nla_put_u8(skb, attrtype: IWL_MVM_VENDOR_ATTR_CHANNEL_NUM, |
53 | value: csme_conn_info->conn_info.channel) || |
54 | nla_put(skb, attrtype: IWL_MVM_VENDOR_ATTR_ADDR, ETH_ALEN, |
55 | data: csme_conn_info->conn_info.bssid)) { |
56 | kfree_skb(skb); |
57 | err = -ENOBUFS; |
58 | } |
59 | |
60 | out_unlock: |
61 | mutex_unlock(lock: &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 | int ret; |
75 | |
76 | mutex_lock(&mvm->mutex); |
77 | ret = iwl_mvm_mei_get_ownership(mvm); |
78 | mutex_unlock(lock: &mvm->mutex); |
79 | |
80 | return ret; |
81 | } |
82 | |
83 | static const struct wiphy_vendor_command iwl_mvm_vendor_commands[] = { |
84 | { |
85 | .info = { |
86 | .vendor_id = INTEL_OUI, |
87 | .subcmd = IWL_MVM_VENDOR_CMD_GET_CSME_CONN_INFO, |
88 | }, |
89 | .doit = iwl_mvm_vendor_get_csme_conn_info, |
90 | .flags = WIPHY_VENDOR_CMD_NEED_WDEV, |
91 | .policy = iwl_mvm_vendor_attr_policy, |
92 | .maxattr = MAX_IWL_MVM_VENDOR_ATTR, |
93 | }, |
94 | { |
95 | .info = { |
96 | .vendor_id = INTEL_OUI, |
97 | .subcmd = IWL_MVM_VENDOR_CMD_HOST_GET_OWNERSHIP, |
98 | }, |
99 | .doit = iwl_mvm_vendor_host_get_ownership, |
100 | .flags = WIPHY_VENDOR_CMD_NEED_WDEV, |
101 | .policy = iwl_mvm_vendor_attr_policy, |
102 | .maxattr = MAX_IWL_MVM_VENDOR_ATTR, |
103 | }, |
104 | }; |
105 | |
106 | enum iwl_mvm_vendor_events_idx { |
107 | /* 0x0 - 0x3 are deprecated */ |
108 | IWL_MVM_VENDOR_EVENT_IDX_ROAMING_FORBIDDEN = 4, |
109 | NUM_IWL_MVM_VENDOR_EVENT_IDX |
110 | }; |
111 | |
112 | static const struct nl80211_vendor_cmd_info |
113 | iwl_mvm_vendor_events[NUM_IWL_MVM_VENDOR_EVENT_IDX] = { |
114 | [IWL_MVM_VENDOR_EVENT_IDX_ROAMING_FORBIDDEN] = { |
115 | .vendor_id = INTEL_OUI, |
116 | .subcmd = IWL_MVM_VENDOR_CMD_ROAMING_FORBIDDEN_EVENT, |
117 | }, |
118 | }; |
119 | |
120 | void iwl_mvm_vendor_cmds_register(struct iwl_mvm *mvm) |
121 | { |
122 | mvm->hw->wiphy->vendor_commands = iwl_mvm_vendor_commands; |
123 | mvm->hw->wiphy->n_vendor_commands = ARRAY_SIZE(iwl_mvm_vendor_commands); |
124 | mvm->hw->wiphy->vendor_events = iwl_mvm_vendor_events; |
125 | mvm->hw->wiphy->n_vendor_events = ARRAY_SIZE(iwl_mvm_vendor_events); |
126 | } |
127 | |
128 | void iwl_mvm_send_roaming_forbidden_event(struct iwl_mvm *mvm, |
129 | struct ieee80211_vif *vif, |
130 | bool forbidden) |
131 | { |
132 | struct sk_buff *msg = |
133 | cfg80211_vendor_event_alloc(wiphy: mvm->hw->wiphy, |
134 | wdev: ieee80211_vif_to_wdev(vif), |
135 | approxlen: 200, event_idx: IWL_MVM_VENDOR_EVENT_IDX_ROAMING_FORBIDDEN, |
136 | GFP_ATOMIC); |
137 | if (!msg) |
138 | return; |
139 | |
140 | if (WARN_ON(!vif)) |
141 | return; |
142 | |
143 | if (nla_put(skb: msg, attrtype: IWL_MVM_VENDOR_ATTR_VIF_ADDR, |
144 | ETH_ALEN, data: vif->addr) || |
145 | nla_put_u8(skb: msg, attrtype: IWL_MVM_VENDOR_ATTR_ROAMING_FORBIDDEN, value: forbidden)) |
146 | goto nla_put_failure; |
147 | |
148 | cfg80211_vendor_event(skb: msg, GFP_ATOMIC); |
149 | return; |
150 | |
151 | nla_put_failure: |
152 | kfree_skb(skb: msg); |
153 | } |
154 | |