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