1 /*
2  * Copyright (c) 2019 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2022 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_hw_capability.c
22  *
23  * The implementation of get hw capability
24  *
25  */
26 
27 #include "wlan_hdd_main.h"
28 #include "wmi_unified_param.h"
29 #include "wlan_hdd_hw_capability.h"
30 #include "qca_vendor.h"
31 #include "wlan_osif_request_manager.h"
32 #include "osif_sync.h"
33 
34 /**
35  * hdd_get_isolation_cb - Callback function to get isolation information
36  * @context: opaque context originally passed to SME. HDD always passes
37  * a cookie for the request context
38  * @isolation: pointer of isolation information
39  *
40  * This function will fill isolation information to isolation priv adapter
41  *
42  * Return: None
43  */
hdd_get_isolation_cb(struct sir_isolation_resp * isolation,void * context)44 static void hdd_get_isolation_cb(struct sir_isolation_resp *isolation,
45 				 void *context)
46 {
47 	struct osif_request *request;
48 	struct sir_isolation_resp *priv;
49 
50 	if (!isolation) {
51 		hdd_err("Bad param");
52 		return;
53 	}
54 
55 	request = osif_request_get(context);
56 	if (!request) {
57 		hdd_err("Obsolete request");
58 		return;
59 	}
60 
61 	priv = osif_request_priv(request);
62 	priv->isolation_chain0 = isolation->isolation_chain0;
63 	priv->isolation_chain1 = isolation->isolation_chain1;
64 	priv->isolation_chain2 = isolation->isolation_chain2;
65 	priv->isolation_chain3 = isolation->isolation_chain3;
66 
67 	osif_request_complete(request);
68 	osif_request_put(request);
69 }
70 
71 /**
72  * hdd_post_isolation - send rsp to user space
73  * @hdd_ctx: pointer to hdd context
74  * @isolation: antenna isolation information
75  *
76  * Return: 0 for success, non-zero for failure
77  */
hdd_post_isolation(struct hdd_context * hdd_ctx,struct sir_isolation_resp * isolation)78 static int hdd_post_isolation(struct hdd_context *hdd_ctx,
79 			      struct sir_isolation_resp *isolation)
80 {
81 	struct sk_buff *skb;
82 	uint32_t skb_len = NLMSG_HDRLEN;
83 
84 	skb_len += (sizeof(u8) + NLA_HDRLEN) + (sizeof(u8) + NLA_HDRLEN) +
85 		(sizeof(u8) + NLA_HDRLEN) + (sizeof(u8) + NLA_HDRLEN);
86 	skb = wlan_cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, skb_len);
87 	if (!skb) {
88 		hdd_err("wlan_cfg80211_vendor_event_alloc failed");
89 		return -ENOMEM;
90 	}
91 
92 	if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_ANTENNA_ISOLATION,
93 		       isolation->isolation_chain0)) {
94 		hdd_err("put fail");
95 		goto nla_put_failure;
96 	}
97 
98 	if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_ANTENNA_ISOLATION,
99 		       isolation->isolation_chain1)) {
100 		hdd_err("put fail");
101 		goto nla_put_failure;
102 	}
103 
104 	if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_ANTENNA_ISOLATION,
105 		       isolation->isolation_chain2)) {
106 		hdd_err("put fail");
107 		goto nla_put_failure;
108 	}
109 
110 	if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_ANTENNA_ISOLATION,
111 		       isolation->isolation_chain3)) {
112 		hdd_err("put fail");
113 		goto nla_put_failure;
114 	}
115 
116 	wlan_cfg80211_vendor_cmd_reply(skb);
117 	return 0;
118 
119 nla_put_failure:
120 	wlan_cfg80211_vendor_free_skb(skb);
121 	return -EINVAL;
122 }
123 
124 /**
125  * __wlan_hdd_cfg80211_get_hw_capability() - get hw capability
126  * @wiphy: wiphy device pointer
127  * @wdev: wireless device pointer
128  * @data: Vendor command data buffer
129  * @data_len: Buffer length
130  *
131  * Return: 0 on success; error number otherwise.
132  *
133  */
__wlan_hdd_cfg80211_get_hw_capability(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)134 static int __wlan_hdd_cfg80211_get_hw_capability(struct wiphy *wiphy,
135 						 struct wireless_dev *wdev,
136 						 const void *data,
137 						 int data_len)
138 {
139 	struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
140 	struct osif_request *request;
141 	struct sir_isolation_resp *priv;
142 	mac_handle_t mac_handle;
143 	void *cookie;
144 	QDF_STATUS status;
145 	int ret;
146 	static const struct osif_request_params params = {
147 		.priv_size = sizeof(*priv),
148 		.timeout_ms = WLAN_WAIT_TIME_ANTENNA_ISOLATION,
149 	};
150 
151 	hdd_enter();
152 
153 	if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) {
154 		hdd_err("Command not allowed in FTM mode");
155 		return -EPERM;
156 	}
157 
158 	if (wlan_hdd_validate_context(hdd_ctx))
159 		return -EINVAL;
160 
161 	request = osif_request_alloc(&params);
162 	if (!request) {
163 		hdd_err("Request allocation failure");
164 		return -ENOMEM;
165 	}
166 
167 	cookie = osif_request_cookie(request);
168 
169 	mac_handle = hdd_ctx->mac_handle;
170 	status = sme_get_isolation(mac_handle,
171 				   cookie,
172 				   hdd_get_isolation_cb);
173 	if (QDF_IS_STATUS_ERROR(status)) {
174 		hdd_err("Unable to retrieve isolation");
175 		ret = -EFAULT;
176 	} else {
177 		ret = osif_request_wait_for_response(request);
178 		if (ret) {
179 			hdd_err("SME timed out while retrieving isolation");
180 			ret = -ETIMEDOUT;
181 		} else {
182 			priv = osif_request_priv(request);
183 			hdd_post_isolation(hdd_ctx, priv);
184 			ret = 0;
185 		}
186 	}
187 	osif_request_put(request);
188 
189 	return ret;
190 }
191 
wlan_hdd_cfg80211_get_hw_capability(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)192 int wlan_hdd_cfg80211_get_hw_capability(struct wiphy *wiphy,
193 					struct wireless_dev *wdev,
194 					const void *data,
195 					int data_len)
196 {
197 	int errno;
198 	struct osif_vdev_sync *vdev_sync;
199 
200 	errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
201 	if (errno)
202 		return errno;
203 
204 	errno = __wlan_hdd_cfg80211_get_hw_capability(wiphy, wdev,
205 						      data, data_len);
206 
207 	osif_vdev_sync_op_stop(vdev_sync);
208 
209 	return errno;
210 }
211