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