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