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 "wlan_nlink_srv.h" 27 #include "wlan_ptt_sock_svc.h" 28 #include "wlan_nlink_common.h" 29 #include "os_if_wifi_pos.h" 30 #include "wifi_pos_api.h" 31 #include "wlan_cfg80211.h" 32 #include "wlan_objmgr_psoc_obj.h" 33 #ifdef CNSS_GENL 34 #include <net/cnss_nl.h> 35 #endif 36 37 /** 38 * os_if_wifi_pos_send_rsp() - send oem registration response 39 * 40 * This function sends oem message to registered application process 41 * 42 * Return: none 43 */ 44 static void os_if_wifi_pos_send_rsp(uint32_t pid, uint32_t rsp_msg_type, 45 uint32_t buf_len, uint8_t *buf) 46 { 47 tAniMsgHdr *aniHdr; 48 struct sk_buff *skb; 49 struct nlmsghdr *nlh; 50 51 /* OEM msg is always to a specific process and cannot be a broadcast */ 52 if (pid == 0) { 53 cfg80211_err("invalid dest pid"); 54 return; 55 } 56 57 skb = alloc_skb(NLMSG_SPACE(sizeof(tAniMsgHdr) + buf_len), GFP_ATOMIC); 58 if (!skb) { 59 cfg80211_alert("alloc_skb failed"); 60 return; 61 } 62 63 nlh = (struct nlmsghdr *)skb->data; 64 nlh->nlmsg_pid = 0; /* from kernel */ 65 nlh->nlmsg_flags = 0; 66 nlh->nlmsg_seq = 0; 67 nlh->nlmsg_type = WLAN_NL_MSG_OEM; 68 nlh->nlmsg_len = NLMSG_LENGTH(sizeof(tAniMsgHdr) + buf_len); 69 70 aniHdr = NLMSG_DATA(nlh); 71 aniHdr->type = rsp_msg_type; 72 qdf_mem_copy(&aniHdr[1], buf, buf_len); 73 aniHdr->length = buf_len; 74 75 skb_put(skb, NLMSG_SPACE(sizeof(tAniMsgHdr) + buf_len)); 76 cfg80211_debug("sending oem rsp: type: %d len(%d) to pid (%d)", 77 rsp_msg_type, buf_len, pid); 78 nl_srv_ucast_oem(skb, pid, MSG_DONTWAIT); 79 } 80 81 #ifdef CNSS_GENL 82 static int wifi_pos_parse_req(const void *data, int len, int pid, 83 struct wifi_pos_req_msg *req) 84 { 85 tAniMsgHdr *msg_hdr; 86 struct nlattr *tb[CLD80211_ATTR_MAX + 1]; 87 88 if (wlan_cfg80211_nla_parse(tb, CLD80211_ATTR_MAX, data, len, NULL)) { 89 cfg80211_err("invalid data in request"); 90 return OEM_ERR_INVALID_MESSAGE_TYPE; 91 } 92 93 if (!tb[CLD80211_ATTR_DATA]) { 94 cfg80211_err("CLD80211_ATTR_DATA not present"); 95 return OEM_ERR_INVALID_MESSAGE_TYPE; 96 } 97 98 msg_hdr = (tAniMsgHdr *)nla_data(tb[CLD80211_ATTR_DATA]); 99 if (!msg_hdr) { 100 cfg80211_err("msg_hdr null"); 101 return OEM_ERR_NULL_MESSAGE_HEADER; 102 } 103 104 req->msg_type = msg_hdr->type; 105 req->buf_len = msg_hdr->length; 106 req->buf = (uint8_t *)&msg_hdr[1]; 107 req->pid = pid; 108 109 if (tb[CLD80211_ATTR_META_DATA]) { 110 req->field_info_buf = (struct wifi_pos_field_info *) 111 nla_data(tb[CLD80211_ATTR_META_DATA]); 112 req->field_info_buf_len = nla_len(tb[CLD80211_ATTR_META_DATA]); 113 } 114 115 return 0; 116 } 117 #else 118 static int wifi_pos_parse_req(struct sk_buff *skb, struct wifi_pos_req_msg *req) 119 { 120 /* SKB->data contains NL msg */ 121 /* NLMSG_DATA(nlh) contains ANI msg */ 122 struct nlmsghdr *nlh; 123 tAniMsgHdr *msg_hdr; 124 125 nlh = (struct nlmsghdr *)skb->data; 126 if (!nlh) { 127 cfg80211_err("Netlink header null"); 128 return OEM_ERR_NULL_MESSAGE_HEADER; 129 } 130 131 msg_hdr = NLMSG_DATA(nlh); 132 if (!msg_hdr) { 133 cfg80211_err("Message header null"); 134 return OEM_ERR_NULL_MESSAGE_HEADER; 135 } 136 137 if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(*msg_hdr) + msg_hdr->length)) { 138 cfg80211_err("nlmsg_len(%d) and animsg_len(%d) mis-match", 139 nlh->nlmsg_len, msg_hdr->length); 140 return OEM_ERR_INVALID_MESSAGE_LENGTH; 141 } 142 143 req->msg_type = msg_hdr->type; 144 req->buf_len = msg_hdr->length; 145 req->buf = (uint8_t *)&msg_hdr[1]; 146 req->pid = nlh->nlmsg_pid; 147 148 return 0; 149 } 150 #endif 151 152 /** 153 * __os_if_wifi_pos_callback() - callback registered with NL service socket to 154 * process wifi pos request 155 * @skb: request message sk_buff 156 * 157 * Return: status of operation 158 */ 159 #ifdef CNSS_GENL 160 static void __os_if_wifi_pos_callback(const void *data, int data_len, 161 void *ctx, int pid) 162 { 163 uint8_t err; 164 QDF_STATUS status; 165 struct wifi_pos_req_msg req = {0}; 166 struct wlan_objmgr_psoc *psoc = wifi_pos_get_psoc(); 167 168 cfg80211_debug("enter: pid %d", pid); 169 if (!psoc) { 170 cfg80211_err("global psoc object not registered yet."); 171 return; 172 } 173 174 wlan_objmgr_psoc_get_ref(psoc, WLAN_WIFI_POS_OSIF_ID); 175 err = wifi_pos_parse_req(data, data_len, pid, &req); 176 if (err) { 177 os_if_wifi_pos_send_rsp(wifi_pos_get_app_pid(psoc), 178 ANI_MSG_OEM_ERROR, sizeof(err), &err); 179 status = QDF_STATUS_E_INVAL; 180 goto release_psoc_ref; 181 } 182 183 status = ucfg_wifi_pos_process_req(psoc, &req, os_if_wifi_pos_send_rsp); 184 if (QDF_IS_STATUS_ERROR(status)) 185 cfg80211_err("ucfg_wifi_pos_process_req failed. status: %d", 186 status); 187 188 release_psoc_ref: 189 wlan_objmgr_psoc_release_ref(psoc, WLAN_WIFI_POS_OSIF_ID); 190 } 191 192 static void os_if_wifi_pos_callback(const void *data, int data_len, 193 void *ctx, int pid) 194 { 195 struct qdf_op_sync *op_sync; 196 197 if (qdf_op_protect(&op_sync)) 198 return; 199 200 __os_if_wifi_pos_callback(data, data_len, ctx, pid); 201 qdf_op_unprotect(op_sync); 202 } 203 #else 204 static int __os_if_wifi_pos_callback(struct sk_buff *skb) 205 { 206 uint8_t err; 207 QDF_STATUS status; 208 struct wifi_pos_req_msg req = {0}; 209 struct wlan_objmgr_psoc *psoc = wifi_pos_get_psoc(); 210 211 cfg80211_debug("enter"); 212 if (!psoc) { 213 cfg80211_err("global psoc object not registered yet."); 214 return -EINVAL; 215 } 216 217 wlan_objmgr_psoc_get_ref(psoc, WLAN_WIFI_POS_OSIF_ID); 218 err = wifi_pos_parse_req(skb, &req); 219 if (err) { 220 os_if_wifi_pos_send_rsp(wifi_pos_get_app_pid(psoc), 221 ANI_MSG_OEM_ERROR, sizeof(err), &err); 222 status = QDF_STATUS_E_INVAL; 223 goto release_psoc_ref; 224 } 225 226 status = ucfg_wifi_pos_process_req(psoc, &req, os_if_wifi_pos_send_rsp); 227 if (QDF_IS_STATUS_ERROR(status)) 228 cfg80211_err("ucfg_wifi_pos_process_req failed. status: %d", 229 status); 230 231 release_psoc_ref: 232 wlan_objmgr_psoc_release_ref(psoc, WLAN_WIFI_POS_OSIF_ID); 233 234 return qdf_status_to_os_return(status); 235 } 236 237 static int os_if_wifi_pos_callback(struct sk_buff *skb) 238 { 239 struct qdf_op_sync *op_sync; 240 int err; 241 242 if (qdf_op_protect(&op_sync)) 243 return -EINVAL; 244 245 err = __os_if_wifi_pos_callback(skb); 246 qdf_op_unprotect(op_sync); 247 248 return err; 249 } 250 #endif 251 252 #ifdef CNSS_GENL 253 int os_if_wifi_pos_register_nl(void) 254 { 255 int ret = register_cld_cmd_cb(WLAN_NL_MSG_OEM, 256 os_if_wifi_pos_callback, NULL); 257 if (ret) 258 cfg80211_err("register_cld_cmd_cb failed"); 259 260 return ret; 261 } 262 #else 263 int os_if_wifi_pos_register_nl(void) 264 { 265 return nl_srv_register(WLAN_NL_MSG_OEM, os_if_wifi_pos_callback); 266 } 267 #endif /* CNSS_GENL */ 268 269 #ifdef CNSS_GENL 270 int os_if_wifi_pos_deregister_nl(void) 271 { 272 int ret = deregister_cld_cmd_cb(WLAN_NL_MSG_OEM); 273 if (ret) 274 cfg80211_err("deregister_cld_cmd_cb failed"); 275 276 return ret; 277 } 278 #else 279 int os_if_wifi_pos_deregister_nl(void) 280 { 281 return 0; 282 } 283 #endif /* CNSS_GENL */ 284 285 void os_if_wifi_pos_send_peer_status(struct qdf_mac_addr *peer_mac, 286 uint8_t peer_status, 287 uint8_t peer_timing_meas_cap, 288 uint8_t session_id, 289 struct wifi_pos_ch_info *chan_info, 290 enum QDF_OPMODE dev_mode) 291 { 292 struct wlan_objmgr_psoc *psoc = wifi_pos_get_psoc(); 293 struct wmi_pos_peer_status_info *peer_info; 294 295 if (!psoc) { 296 cfg80211_err("global wifi_pos psoc object not registered"); 297 return; 298 } 299 300 if (!wifi_pos_is_app_registered(psoc) || 301 wifi_pos_get_app_pid(psoc) == 0) { 302 cfg80211_debug("app is not registered or pid is invalid"); 303 return; 304 } 305 306 peer_info = qdf_mem_malloc(sizeof(*peer_info)); 307 if (!peer_info) 308 return; 309 310 qdf_mem_copy(peer_info->peer_mac_addr, peer_mac->bytes, 311 sizeof(peer_mac->bytes)); 312 peer_info->peer_status = peer_status; 313 peer_info->vdev_id = session_id; 314 peer_info->peer_capability = peer_timing_meas_cap; 315 peer_info->reserved0 = 0; 316 /* Set 0th bit of reserved0 for STA mode */ 317 if (QDF_STA_MODE == dev_mode) 318 peer_info->reserved0 |= 0x01; 319 320 if (chan_info) { 321 peer_info->peer_chan_info.chan_id = chan_info->chan_id; 322 peer_info->peer_chan_info.reserved0 = 0; 323 peer_info->peer_chan_info.mhz = chan_info->mhz; 324 peer_info->peer_chan_info.band_center_freq1 = 325 chan_info->band_center_freq1; 326 peer_info->peer_chan_info.band_center_freq2 = 327 chan_info->band_center_freq2; 328 peer_info->peer_chan_info.info = chan_info->info; 329 peer_info->peer_chan_info.reg_info_1 = chan_info->reg_info_1; 330 peer_info->peer_chan_info.reg_info_2 = chan_info->reg_info_2; 331 } 332 333 os_if_wifi_pos_send_rsp(wifi_pos_get_app_pid(psoc), 334 ANI_MSG_PEER_STATUS_IND, 335 sizeof(*peer_info), (uint8_t *)peer_info); 336 qdf_mem_free(peer_info); 337 } 338 339 int os_if_wifi_pos_populate_caps(struct wlan_objmgr_psoc *psoc, 340 struct wifi_pos_driver_caps *caps) 341 { 342 if (!psoc || !caps) { 343 cfg80211_err("psoc or caps buffer is null"); 344 return -EINVAL; 345 } 346 347 return qdf_status_to_os_return(wifi_pos_populate_caps(psoc, caps)); 348 } 349