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_rssi_monitor.c
22  *
23  * WLAN rssi monitoring 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_ext_scan.h>
34 #include <wlan_hdd_rssi_monitor.h>
35 
36 /*
37  * define short names for the global vendor params
38  * used by __wlan_hdd_cfg80211_monitor_rssi()
39  */
40 #define PARAM_MAX QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MAX
41 #define PARAM_REQUEST_ID QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_REQUEST_ID
42 #define PARAM_CONTROL QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CONTROL
43 #define PARAM_MIN_RSSI QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MIN_RSSI
44 #define PARAM_MAX_RSSI QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MAX_RSSI
45 
46 const struct nla_policy moitor_rssi_policy[
47 			QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MAX + 1] = {
48 	[QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_REQUEST_ID] = { .type = NLA_U32 },
49 	[QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CONTROL] = { .type = NLA_U32 },
50 	[QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MIN_RSSI] = { .type = NLA_S8 },
51 	[QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MAX_RSSI] = { .type = NLA_S8 },
52 };
53 
54 /**
55  * __wlan_hdd_cfg80211_monitor_rssi() - monitor rssi
56  * @wiphy: Pointer to wireless phy
57  * @wdev: Pointer to wireless device
58  * @data: Pointer to data
59  * @data_len: Data length
60  *
61  * Return: 0 on success, negative errno on failure
62  */
63 static int
__wlan_hdd_cfg80211_monitor_rssi(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)64 __wlan_hdd_cfg80211_monitor_rssi(struct wiphy *wiphy,
65 				 struct wireless_dev *wdev,
66 				 const void *data,
67 				 int data_len)
68 {
69 	struct net_device *dev = wdev->netdev;
70 	struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
71 	struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
72 	struct nlattr *tb[PARAM_MAX + 1];
73 	struct rssi_monitor_param req;
74 	QDF_STATUS status;
75 	int ret;
76 	uint32_t control;
77 	mac_handle_t mac_handle;
78 
79 	hdd_enter_dev(dev);
80 
81 	if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
82 		hdd_err("Command not allowed in FTM mode");
83 		return -EPERM;
84 	}
85 
86 	if (wlan_hdd_validate_vdev_id(adapter->deflink->vdev_id))
87 		return -EINVAL;
88 
89 	ret = wlan_hdd_validate_context(hdd_ctx);
90 	if (ret)
91 		return ret;
92 
93 	if (!hdd_cm_is_vdev_associated(adapter->deflink)) {
94 		hdd_err("Not in Connected state!");
95 		return -ENOTSUPP;
96 	}
97 
98 	if (wlan_cfg80211_nla_parse(tb, PARAM_MAX, data, data_len,
99 				    moitor_rssi_policy)) {
100 		hdd_err("Invalid ATTR");
101 		return -EINVAL;
102 	}
103 
104 	if (!tb[PARAM_REQUEST_ID]) {
105 		hdd_err("attr request id failed");
106 		return -EINVAL;
107 	}
108 
109 	if (!tb[PARAM_CONTROL]) {
110 		hdd_err("attr control failed");
111 		return -EINVAL;
112 	}
113 
114 	req.request_id = nla_get_u32(tb[PARAM_REQUEST_ID]);
115 	req.vdev_id = adapter->deflink->vdev_id;
116 	control = nla_get_u32(tb[PARAM_CONTROL]);
117 
118 	if (control == QCA_WLAN_RSSI_MONITORING_START) {
119 		req.control = true;
120 		if (!tb[PARAM_MIN_RSSI]) {
121 			hdd_err("attr min rssi failed");
122 			return -EINVAL;
123 		}
124 
125 		if (!tb[PARAM_MAX_RSSI]) {
126 			hdd_err("attr max rssi failed");
127 			return -EINVAL;
128 		}
129 
130 		req.min_rssi = nla_get_s8(tb[PARAM_MIN_RSSI]);
131 		req.max_rssi = nla_get_s8(tb[PARAM_MAX_RSSI]);
132 
133 		if (!(req.min_rssi < req.max_rssi)) {
134 			hdd_warn("min_rssi: %d must be less than max_rssi: %d",
135 				 req.min_rssi, req.max_rssi);
136 			return -EINVAL;
137 		}
138 		hdd_debug("Min_rssi: %d Max_rssi: %d",
139 			  req.min_rssi, req.max_rssi);
140 
141 	} else if (control == QCA_WLAN_RSSI_MONITORING_STOP) {
142 		req.control = false;
143 	} else {
144 		hdd_err("Invalid control cmd: %d", control);
145 		return -EINVAL;
146 	}
147 	hdd_debug("Request Id: %u vdev id: %d Control: %d",
148 		  req.request_id, req.vdev_id, req.control);
149 
150 	mac_handle = hdd_ctx->mac_handle;
151 	status = sme_set_rssi_monitoring(mac_handle, &req);
152 	if (!QDF_IS_STATUS_SUCCESS(status)) {
153 		hdd_err("sme_set_rssi_monitoring failed(err=%d)", status);
154 		return -EINVAL;
155 	}
156 
157 	return 0;
158 }
159 
160 /*
161  * done with short names for the global vendor params
162  * used by __wlan_hdd_cfg80211_monitor_rssi()
163  */
164 #undef PARAM_MAX
165 #undef PARAM_CONTROL
166 #undef PARAM_REQUEST_ID
167 #undef PARAM_MAX_RSSI
168 #undef PARAM_MIN_RSSI
169 
170 int
wlan_hdd_cfg80211_monitor_rssi(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)171 wlan_hdd_cfg80211_monitor_rssi(struct wiphy *wiphy, struct wireless_dev *wdev,
172 			       const void *data, int data_len)
173 {
174 	struct osif_vdev_sync *vdev_sync;
175 	int errno;
176 
177 	errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
178 	if (errno)
179 		return errno;
180 
181 	errno = __wlan_hdd_cfg80211_monitor_rssi(wiphy, wdev, data, data_len);
182 
183 	osif_vdev_sync_op_stop(vdev_sync);
184 
185 	return errno;
186 }
187 
hdd_rssi_threshold_breached(hdd_handle_t hdd_handle,struct rssi_breach_event * data)188 void hdd_rssi_threshold_breached(hdd_handle_t hdd_handle,
189 				 struct rssi_breach_event *data)
190 {
191 	struct hdd_context *hdd_ctx  = hdd_handle_to_context(hdd_handle);
192 	struct sk_buff *skb;
193 	enum qca_nl80211_vendor_subcmds_index index =
194 		QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI_INDEX;
195 
196 	hdd_enter();
197 
198 	if (wlan_hdd_validate_context(hdd_ctx))
199 		return;
200 	if (!data) {
201 		hdd_err("data is null");
202 		return;
203 	}
204 
205 	skb = wlan_cfg80211_vendor_event_alloc(hdd_ctx->wiphy, NULL,
206 					       EXTSCAN_EVENT_BUF_SIZE +
207 					       NLMSG_HDRLEN,
208 					       index, GFP_KERNEL);
209 
210 	if (!skb) {
211 		hdd_err("mem alloc failed");
212 		return;
213 	}
214 
215 	hdd_debug("Req Id: %u Current rssi: %d",
216 		  data->request_id, data->curr_rssi);
217 	hdd_debug("Current BSSID: "QDF_MAC_ADDR_FMT,
218 		  QDF_MAC_ADDR_REF(data->curr_bssid.bytes));
219 
220 	if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_REQUEST_ID,
221 			data->request_id) ||
222 	    nla_put(skb, QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CUR_BSSID,
223 		    sizeof(data->curr_bssid), data->curr_bssid.bytes) ||
224 	    nla_put_s8(skb, QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CUR_RSSI,
225 		       data->curr_rssi)) {
226 		hdd_err("nla put fail");
227 		goto fail;
228 	}
229 
230 	wlan_cfg80211_vendor_event(skb, GFP_KERNEL);
231 	return;
232 
233 fail:
234 	wlan_cfg80211_vendor_free_skb(skb);
235 }
236 
237