1 /*
2 * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved.
3 * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for
6 * any purpose with or without fee is hereby granted, provided that the
7 * above copyright notice and this permission notice appear in all
8 * copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 /**
21 * DOC: wlan_hdd_bss_transition.c
22 *
23 * WLAN bss transition functions
24 *
25 */
26
27 #include "osif_sync.h"
28 #include <wlan_hdd_includes.h>
29 #include <linux/netdevice.h>
30 #include <linux/skbuff.h>
31 #include <linux/etherdevice.h>
32 #include <linux/if_ether.h>
33 #include <wlan_hdd_bss_transition.h>
34
35 /**
36 * wlan_hdd_is_bt_in_progress() - check if bt activity is in progress
37 * @hdd_ctx : HDD context
38 *
39 * Return: true if BT activity is in progress else false
40 */
wlan_hdd_is_bt_in_progress(struct hdd_context * hdd_ctx)41 static bool wlan_hdd_is_bt_in_progress(struct hdd_context *hdd_ctx)
42 {
43 if (hdd_ctx->bt_a2dp_active || hdd_ctx->bt_vo_active)
44 return true;
45
46 return false;
47 }
48
49 /**
50 * wlan_hdd_fill_btm_resp() - Fill bss candidate response buffer
51 * @reply_skb : pointer to reply_skb
52 * @info : bss candidate information
53 * @index : attribute type index for nla_next_start()
54 *
55 * Return : 0 on success and errno on failure
56 */
wlan_hdd_fill_btm_resp(struct sk_buff * reply_skb,struct bss_candidate_info * info,int index)57 static int wlan_hdd_fill_btm_resp(struct sk_buff *reply_skb,
58 struct bss_candidate_info *info,
59 int index)
60 {
61 struct nlattr *attr;
62
63 attr = nla_nest_start(reply_skb, index);
64 if (!attr) {
65 hdd_err("nla_nest_start failed");
66 wlan_cfg80211_vendor_free_skb(reply_skb);
67 return -EINVAL;
68 }
69
70 if (nla_put(reply_skb,
71 QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID,
72 ETH_ALEN, info->bssid.bytes) ||
73 nla_put_u32(reply_skb,
74 QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS,
75 info->status)) {
76 hdd_err("nla_put failed");
77 wlan_cfg80211_vendor_free_skb(reply_skb);
78 return -EINVAL;
79 }
80
81 nla_nest_end(reply_skb, attr);
82
83 return 0;
84 }
85
86 const struct nla_policy
87 btm_params_policy[QCA_WLAN_VENDOR_ATTR_MAX + 1] = {
88 [QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON] = {
89 .type = NLA_U8},
90 [QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO] =
91 VENDOR_NLA_POLICY_NESTED(btm_cand_list_policy),
92 };
93
94 const struct nla_policy
95 btm_cand_list_policy[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX + 1]
96 = {[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID] = {
97 .len = QDF_MAC_ADDR_SIZE},
98 [QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS] = {
99 .type = NLA_U32},
100 };
101
102 /**
103 * __wlan_hdd_cfg80211_fetch_bss_transition_status() - fetch bss transition
104 * status
105 * @wiphy : WIPHY structure pointer
106 * @wdev : Wireless device structure pointer
107 * @data : Pointer to the data received
108 * @data_len : Length of the data received
109 *
110 * This function is used to fetch transition status for candidate bss. The
111 * transition status is either accept or reason for reject.
112 *
113 * Return : 0 on success and errno on failure
114 */
115 static int
__wlan_hdd_cfg80211_fetch_bss_transition_status(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)116 __wlan_hdd_cfg80211_fetch_bss_transition_status(struct wiphy *wiphy,
117 struct wireless_dev *wdev,
118 const void *data, int data_len)
119 {
120 struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1];
121 struct nlattr *tb_msg[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX + 1];
122 uint8_t transition_reason;
123 struct nlattr *attr;
124 struct sk_buff *skb;
125 int rem, j;
126 int ret;
127 bool is_bt_in_progress;
128 struct bss_candidate_info candidate_info[MAX_CANDIDATE_INFO];
129 uint16_t nof_candidates, i = 0;
130 struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
131 struct net_device *dev = wdev->netdev;
132 struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
133 struct hdd_station_ctx *hdd_sta_ctx;
134 mac_handle_t mac_handle;
135 QDF_STATUS status;
136
137 hdd_enter();
138
139 if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) {
140 hdd_err("Command not allowed in FTM mode");
141 return -EINVAL;
142 }
143
144 ret = wlan_hdd_validate_context(hdd_ctx);
145 if (ret)
146 return ret;
147
148 hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter->deflink);
149 if (adapter->device_mode != QDF_STA_MODE ||
150 !hdd_cm_is_vdev_associated(adapter->deflink)) {
151 hdd_err("Command is either not invoked for STA mode (device mode: %d) or STA is not associated (Connection state: %d)",
152 adapter->device_mode, hdd_sta_ctx->conn_info.conn_state);
153 return -EINVAL;
154 }
155
156 ret = wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_MAX, data,
157 data_len, btm_params_policy);
158 if (ret) {
159 hdd_err("Attribute parse failed");
160 return -EINVAL;
161 }
162
163 if (!tb[QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON] ||
164 !tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO]) {
165 hdd_err("Missing attributes");
166 return -EINVAL;
167 }
168
169 transition_reason = nla_get_u8(
170 tb[QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON]);
171
172 nla_for_each_nested(attr,
173 tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO],
174 rem) {
175 ret = wlan_cfg80211_nla_parse_nested(tb_msg,
176 QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX,
177 attr, btm_cand_list_policy);
178 if (ret) {
179 hdd_err("Attribute parse failed");
180 return -EINVAL;
181 }
182
183 if (!tb_msg[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID]) {
184 hdd_err("Missing BSSID attribute");
185 return -EINVAL;
186 }
187
188 qdf_mem_copy((void *)candidate_info[i].bssid.bytes,
189 nla_data(tb_msg[
190 QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID]),
191 QDF_MAC_ADDR_SIZE);
192 i++;
193 if (i == MAX_CANDIDATE_INFO)
194 break;
195 }
196
197 /*
198 * Determine status for each candidate and fill in the status field.
199 * Also arrange the candidates in the order of preference.
200 */
201 nof_candidates = i;
202
203 is_bt_in_progress = wlan_hdd_is_bt_in_progress(hdd_ctx);
204
205 mac_handle = hdd_ctx->mac_handle;
206 status = sme_get_bss_transition_status(mac_handle, transition_reason,
207 &hdd_sta_ctx->conn_info.bssid,
208 candidate_info,
209 nof_candidates,
210 is_bt_in_progress);
211 if (QDF_IS_STATUS_ERROR(status))
212 return -EINVAL;
213
214 /* Prepare the reply and send it to userspace */
215 skb = wlan_cfg80211_vendor_cmd_alloc_reply_skb(wiphy,
216 (QDF_MAC_ADDR_SIZE +
217 sizeof(uint32_t)) *
218 nof_candidates +
219 NLMSG_HDRLEN);
220 if (!skb) {
221 hdd_err("reply buffer alloc failed");
222 return -ENOMEM;
223 }
224
225 attr = nla_nest_start(skb,
226 QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO);
227 if (!attr) {
228 hdd_err("nla_nest_start failed");
229 wlan_cfg80211_vendor_free_skb(skb);
230 return -EINVAL;
231 }
232
233 /*
234 * Order candidates as - accepted candidate list followed by rejected
235 * candidate list
236 */
237 for (i = 0, j = 0; i < nof_candidates; i++) {
238 /* copy accepted candidate list */
239 if (candidate_info[i].status == QCA_STATUS_ACCEPT) {
240 if (wlan_hdd_fill_btm_resp(skb,
241 &candidate_info[i], j))
242 return -EINVAL;
243 j++;
244 }
245 }
246 for (i = 0; i < nof_candidates; i++) {
247 /* copy rejected candidate list */
248 if (candidate_info[i].status != QCA_STATUS_ACCEPT) {
249 if (wlan_hdd_fill_btm_resp(skb,
250 &candidate_info[i], j))
251 return -EINVAL;
252 j++;
253 }
254 }
255 nla_nest_end(skb, attr);
256
257 hdd_exit();
258
259 return wlan_cfg80211_vendor_cmd_reply(skb);
260 }
261
wlan_hdd_cfg80211_fetch_bss_transition_status(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)262 int wlan_hdd_cfg80211_fetch_bss_transition_status(struct wiphy *wiphy,
263 struct wireless_dev *wdev,
264 const void *data,
265 int data_len)
266 {
267 struct osif_vdev_sync *vdev_sync;
268 int errno;
269
270 errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
271 if (errno)
272 return errno;
273
274 errno = __wlan_hdd_cfg80211_fetch_bss_transition_status(wiphy, wdev,
275 data, data_len);
276
277 osif_vdev_sync_op_stop(vdev_sync);
278
279 return errno;
280 }
281
282