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