xref: /wlan-dirver/qca-wifi-host-cmn/os_if/linux/wifi_pos/src/os_if_wifi_pos.c (revision 3149adf58a329e17232a4c0e58d460d025edd55a)
1 /*
2  * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
3  *
4  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
5  *
6  *
7  * Permission to use, copy, modify, and/or distribute this software for
8  * any purpose with or without fee is hereby granted, provided that the
9  * above copyright notice and this permission notice appear in all
10  * copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
13  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
14  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
15  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
16  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
17  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
18  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
19  * PERFORMANCE OF THIS SOFTWARE.
20  */
21 
22 /*
23  * This file was originally distributed by Qualcomm Atheros, Inc.
24  * under proprietary terms before Copyright ownership was assigned
25  * to the Linux Foundation.
26  */
27 
28 /**
29  * DOC: wlan_hdd_wifi_pos.c
30  * This file defines the important functions pertinent to wifi positioning
31  * component's os_if layer.
32  */
33 
34 #include "wlan_nlink_srv.h"
35 #include "wlan_ptt_sock_svc.h"
36 #include "wlan_nlink_common.h"
37 #include "os_if_wifi_pos.h"
38 #include "wifi_pos_api.h"
39 #include "wlan_cfg80211.h"
40 #include "wlan_objmgr_psoc_obj.h"
41 #ifdef CNSS_GENL
42 #include <net/cnss_nl.h>
43 #endif
44 
45 /**
46  * os_if_wifi_pos_send_rsp() - send oem registration response
47  *
48  * This function sends oem message to registered application process
49  *
50  * Return:  none
51  */
52 static void os_if_wifi_pos_send_rsp(uint32_t pid, uint32_t rsp_msg_type,
53 				    uint32_t buf_len, uint8_t *buf)
54 {
55 	tAniMsgHdr *aniHdr;
56 	struct sk_buff *skb;
57 	struct nlmsghdr *nlh;
58 
59 	/* OEM msg is always to a specific process and cannot be a broadcast */
60 	if (pid == 0) {
61 		cfg80211_err("invalid dest pid");
62 		return;
63 	}
64 
65 	skb = alloc_skb(NLMSG_SPACE(sizeof(tAniMsgHdr) + buf_len), GFP_ATOMIC);
66 	if (skb == NULL) {
67 		cfg80211_alert("alloc_skb failed");
68 		return;
69 	}
70 
71 	nlh = (struct nlmsghdr *)skb->data;
72 	nlh->nlmsg_pid = 0;     /* from kernel */
73 	nlh->nlmsg_flags = 0;
74 	nlh->nlmsg_seq = 0;
75 	nlh->nlmsg_type = WLAN_NL_MSG_OEM;
76 	nlh->nlmsg_len = NLMSG_LENGTH(sizeof(tAniMsgHdr) + buf_len);
77 
78 	aniHdr = NLMSG_DATA(nlh);
79 	aniHdr->type = rsp_msg_type;
80 	qdf_mem_copy(&aniHdr[1], buf, buf_len);
81 	aniHdr->length = buf_len;
82 
83 	skb_put(skb, NLMSG_SPACE(sizeof(tAniMsgHdr) + buf_len));
84 	cfg80211_debug("sending oem rsp: type: %d len(%d) to pid (%d)",
85 		      rsp_msg_type, buf_len, pid);
86 	nl_srv_ucast_oem(skb, pid, MSG_DONTWAIT);
87 }
88 
89 #ifdef CNSS_GENL
90 static int  wifi_pos_parse_req(const void *data, int len, int pid,
91 		    struct wifi_pos_req_msg *req)
92 {
93 	tAniMsgHdr *msg_hdr;
94 	struct nlattr *tb[CLD80211_ATTR_MAX + 1];
95 
96 	if (wlan_cfg80211_nla_parse(tb, CLD80211_ATTR_MAX, data, len, NULL)) {
97 		cfg80211_err("invalid data in request");
98 		return OEM_ERR_INVALID_MESSAGE_TYPE;
99 	}
100 
101 	if (!tb[CLD80211_ATTR_DATA]) {
102 		cfg80211_err("CLD80211_ATTR_DATA not present");
103 		return OEM_ERR_INVALID_MESSAGE_TYPE;
104 	}
105 
106 	msg_hdr = (tAniMsgHdr *)nla_data(tb[CLD80211_ATTR_DATA]);
107 	if (!msg_hdr) {
108 		cfg80211_err("msg_hdr null");
109 		return OEM_ERR_NULL_MESSAGE_HEADER;
110 	}
111 
112 	req->msg_type = msg_hdr->type;
113 	req->buf_len = msg_hdr->length;
114 	req->buf = (uint8_t *)&msg_hdr[1];
115 	req->pid = pid;
116 
117 	if (tb[CLD80211_ATTR_META_DATA]) {
118 		req->field_info_buf = (struct wifi_pos_field_info *)
119 					nla_data(tb[CLD80211_ATTR_META_DATA]);
120 		req->field_info_buf_len = nla_len(tb[CLD80211_ATTR_META_DATA]);
121 	}
122 
123 	return 0;
124 }
125 #else
126 static int wifi_pos_parse_req(struct sk_buff *skb, struct wifi_pos_req_msg *req)
127 {
128 	/* SKB->data contains NL msg */
129 	/* NLMSG_DATA(nlh) contains ANI msg */
130 	struct nlmsghdr *nlh;
131 	tAniMsgHdr *msg_hdr;
132 
133 	nlh = (struct nlmsghdr *)skb->data;
134 	if (!nlh) {
135 		cfg80211_err("Netlink header null");
136 		return OEM_ERR_NULL_MESSAGE_HEADER;
137 	}
138 
139 	msg_hdr = NLMSG_DATA(nlh);
140 	if (!msg_hdr) {
141 		cfg80211_err("Message header null");
142 		return OEM_ERR_NULL_MESSAGE_HEADER;
143 	}
144 
145 	if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(*msg_hdr) + msg_hdr->length)) {
146 		cfg80211_err("nlmsg_len(%d) and animsg_len(%d) mis-match",
147 			nlh->nlmsg_len, msg_hdr->length);
148 		return OEM_ERR_INVALID_MESSAGE_LENGTH;
149 	}
150 
151 	req->msg_type = msg_hdr->type;
152 	req->buf_len = msg_hdr->length;
153 	req->buf = (uint8_t *)&msg_hdr[1];
154 	req->pid = nlh->nlmsg_pid;
155 
156 	return 0;
157 }
158 #endif
159 
160 /**
161  * os_if_wifi_pos_callback() - callback registered with NL service socket to
162  * process wifi pos request
163  * @skb: request message sk_buff
164  *
165  * Return: status of operation
166  */
167 #ifdef CNSS_GENL
168 static void os_if_wifi_pos_callback(const void *data, int data_len,
169 				    void *ctx, int pid)
170 {
171 	uint8_t err;
172 	QDF_STATUS status;
173 	struct wifi_pos_req_msg req = {0};
174 	struct wlan_objmgr_psoc *psoc = wifi_pos_get_psoc();
175 
176 	cfg80211_debug("enter: pid %d", pid);
177 	if (!psoc) {
178 		cfg80211_err("global psoc object not registered yet.");
179 		return;
180 	}
181 
182 	wlan_objmgr_psoc_get_ref(psoc, WLAN_WIFI_POS_ID);
183 	err = wifi_pos_parse_req(data, data_len, pid, &req);
184 	if (err) {
185 		os_if_wifi_pos_send_rsp(wifi_pos_get_app_pid(psoc),
186 					ANI_MSG_OEM_ERROR, sizeof(err), &err);
187 		status = QDF_STATUS_E_INVAL;
188 		goto release_psoc_ref;
189 	}
190 
191 	status = ucfg_wifi_pos_process_req(psoc, &req, os_if_wifi_pos_send_rsp);
192 	if (QDF_IS_STATUS_ERROR(status))
193 		cfg80211_err("ucfg_wifi_pos_process_req failed. status: %d",
194 				status);
195 
196 release_psoc_ref:
197 	wlan_objmgr_psoc_release_ref(psoc, WLAN_WIFI_POS_ID);
198 }
199 #else
200 static int os_if_wifi_pos_callback(struct sk_buff *skb)
201 {
202 	uint8_t err;
203 	QDF_STATUS status;
204 	struct wifi_pos_req_msg req = {0};
205 	struct wlan_objmgr_psoc *psoc = wifi_pos_get_psoc();
206 
207 	cfg80211_debug("enter");
208 	if (!psoc) {
209 		cfg80211_err("global psoc object not registered yet.");
210 		return -EINVAL;
211 	}
212 
213 	wlan_objmgr_psoc_get_ref(psoc, WLAN_WIFI_POS_ID);
214 	err = wifi_pos_parse_req(skb, &req);
215 	if (err) {
216 		os_if_wifi_pos_send_rsp(wifi_pos_get_app_pid(psoc),
217 					ANI_MSG_OEM_ERROR, sizeof(err), &err);
218 		status = QDF_STATUS_E_INVAL;
219 		goto release_psoc_ref;
220 	}
221 
222 	status = ucfg_wifi_pos_process_req(psoc, &req, os_if_wifi_pos_send_rsp);
223 	if (QDF_IS_STATUS_ERROR(status))
224 		cfg80211_err("ucfg_wifi_pos_process_req failed. status: %d",
225 				status);
226 
227 release_psoc_ref:
228 	wlan_objmgr_psoc_release_ref(psoc, WLAN_WIFI_POS_ID);
229 
230 	return qdf_status_to_os_return(status);
231 }
232 #endif
233 
234 #ifdef CNSS_GENL
235 int os_if_wifi_pos_register_nl(void)
236 {
237 	int ret = register_cld_cmd_cb(WLAN_NL_MSG_OEM,
238 				os_if_wifi_pos_callback, NULL);
239 	if (ret)
240 		cfg80211_err("register_cld_cmd_cb failed");
241 
242 	return ret;
243 }
244 #else
245 int os_if_wifi_pos_register_nl(void)
246 {
247 	return nl_srv_register(WLAN_NL_MSG_OEM, os_if_wifi_pos_callback);
248 }
249 #endif /* CNSS_GENL */
250 
251 #ifdef CNSS_GENL
252 int os_if_wifi_pos_deregister_nl(void)
253 {
254 	int ret = deregister_cld_cmd_cb(WLAN_NL_MSG_OEM);
255 	if (ret)
256 		cfg80211_err("deregister_cld_cmd_cb failed");
257 
258 	return ret;
259 }
260 #else
261 int os_if_wifi_pos_deregister_nl(void)
262 {
263 	return 0;
264 }
265 #endif /* CNSS_GENL */
266 
267 void os_if_wifi_pos_send_peer_status(struct qdf_mac_addr *peer_mac,
268 				uint8_t peer_status,
269 				uint8_t peer_timing_meas_cap,
270 				uint8_t session_id,
271 				struct wifi_pos_ch_info *chan_info,
272 				enum QDF_OPMODE dev_mode)
273 {
274 	struct wlan_objmgr_psoc *psoc = wifi_pos_get_psoc();
275 	struct wmi_pos_peer_status_info *peer_info;
276 
277 	if (!psoc) {
278 		cfg80211_err("global wifi_pos psoc object not registered");
279 		return;
280 	}
281 
282 	if (!wifi_pos_is_app_registered(psoc) ||
283 			wifi_pos_get_app_pid(psoc) == 0) {
284 		cfg80211_err("app is not registered or pid is invalid");
285 		return;
286 	}
287 
288 	peer_info = qdf_mem_malloc(sizeof(*peer_info));
289 	if (!peer_info) {
290 		cfg80211_alert("malloc failed");
291 		return;
292 	}
293 	qdf_mem_copy(peer_info->peer_mac_addr, peer_mac->bytes,
294 		     sizeof(peer_mac->bytes));
295 	peer_info->peer_status = peer_status;
296 	peer_info->vdev_id = session_id;
297 	peer_info->peer_capability = peer_timing_meas_cap;
298 	peer_info->reserved0 = 0;
299 	/* Set 0th bit of reserved0 for STA mode */
300 	if (QDF_STA_MODE == dev_mode)
301 		peer_info->reserved0 |= 0x01;
302 
303 	if (chan_info) {
304 		peer_info->peer_chan_info.chan_id = chan_info->chan_id;
305 		peer_info->peer_chan_info.reserved0 = 0;
306 		peer_info->peer_chan_info.mhz = chan_info->mhz;
307 		peer_info->peer_chan_info.band_center_freq1 =
308 			chan_info->band_center_freq1;
309 		peer_info->peer_chan_info.band_center_freq2 =
310 			chan_info->band_center_freq2;
311 		peer_info->peer_chan_info.info = chan_info->info;
312 		peer_info->peer_chan_info.reg_info_1 = chan_info->reg_info_1;
313 		peer_info->peer_chan_info.reg_info_2 = chan_info->reg_info_2;
314 	}
315 
316 	os_if_wifi_pos_send_rsp(wifi_pos_get_app_pid(psoc),
317 				ANI_MSG_PEER_STATUS_IND,
318 				sizeof(*peer_info), (uint8_t *)peer_info);
319 	qdf_mem_free(peer_info);
320 }
321 
322 int os_if_wifi_pos_populate_caps(struct wlan_objmgr_psoc *psoc,
323 				   struct wifi_pos_driver_caps *caps)
324 {
325 	if (!psoc || !caps) {
326 		cfg80211_err("psoc or caps buffer is null");
327 		return -EINVAL;
328 	}
329 
330 	return qdf_status_to_os_return(wifi_pos_populate_caps(psoc, caps));
331 }
332