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(¶ms);
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