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