| /* |
| * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved. |
| * |
| * Permission to use, copy, modify, and/or distribute this software for |
| * any purpose with or without fee is hereby granted, provided that the |
| * above copyright notice and this permission notice appear in all |
| * copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL |
| * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE |
| * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
| * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| * PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| /** |
| * DOC: wifi_pos_main.c |
| * This file defines the important functions pertinent to |
| * wifi positioning to initialize and de-initialize the component. |
| */ |
| #include "target_if_wifi_pos.h" |
| #include "wifi_pos_oem_interface_i.h" |
| #include "wifi_pos_utils_i.h" |
| #include "wifi_pos_api.h" |
| #include "wifi_pos_main_i.h" |
| #include "wifi_pos_ucfg_i.h" |
| #include "wlan_objmgr_cmn.h" |
| #include "wlan_objmgr_global_obj.h" |
| #include "wlan_objmgr_psoc_obj.h" |
| #include "wlan_objmgr_pdev_obj.h" |
| #include "wlan_objmgr_vdev_obj.h" |
| #include "wlan_ptt_sock_svc.h" |
| |
| #include "wlan_reg_services_api.h" |
| /* forward declartion */ |
| struct regulatory_channel; |
| |
| #define REG_SET_CHANNEL_REG_POWER(reg_info_1, val) do { \ |
| reg_info_1 &= 0xff00ffff; \ |
| reg_info_1 |= ((val & 0xff) << 16); \ |
| } while (0) |
| |
| /* max tx power is in 1 dBm units */ |
| #define REG_SET_CHANNEL_MAX_TX_POWER(reg_info_2, val) do { \ |
| reg_info_2 &= 0xffff00ff; \ |
| reg_info_2 |= ((val & 0xff) << 8); \ |
| } while (0) |
| |
| /* channel info consists of 6 bits of channel mode */ |
| |
| #define REG_SET_CHANNEL_MODE(reg_channel, val) do { \ |
| (reg_channel)->info &= 0xffffffc0; \ |
| (reg_channel)->info |= (val); \ |
| } while (0) |
| |
| /* |
| * obj mgr api to iterate over vdevs does not provide a direct array or vdevs, |
| * rather takes a callback that is called for every vdev. wifi pos needs to |
| * store device mode and vdev id of all active vdevs and provide this info to |
| * user space as part of APP registration response. due to this, vdev_idx is |
| * used to identify how many vdevs have been populated by obj manager API. |
| */ |
| static uint32_t vdev_idx; |
| |
| /** |
| * wifi_pos_get_tlv_support: indicates if firmware supports TLV wifi pos msg |
| * @psoc: psoc object |
| * |
| * Return: status of operation |
| */ |
| static bool wifi_pos_get_tlv_support(struct wlan_objmgr_psoc *psoc) |
| { |
| /* this is TBD */ |
| return true; |
| } |
| |
| struct wlan_lmac_if_wifi_pos_tx_ops * |
| wifi_pos_get_tx_ops(struct wlan_objmgr_psoc *psoc) |
| { |
| if (!psoc) { |
| wifi_pos_err("psoc is null"); |
| return NULL; |
| } |
| |
| return &psoc->soc_cb.tx_ops.wifi_pos_tx_ops; |
| } |
| |
| static QDF_STATUS wifi_pos_process_data_req(struct wlan_objmgr_psoc *psoc, |
| struct wifi_pos_req_msg *req) |
| { |
| uint8_t idx; |
| uint32_t sub_type = 0; |
| uint32_t channel_mhz = 0; |
| uint32_t pdev_id = 0; |
| uint32_t offset; |
| struct oem_data_req data_req; |
| struct wlan_lmac_if_wifi_pos_tx_ops *tx_ops; |
| struct wlan_objmgr_pdev *pdev; |
| struct wifi_pos_psoc_priv_obj *wifi_pos_obj = |
| wifi_pos_get_psoc_priv_obj(psoc); |
| |
| if (!wifi_pos_obj) { |
| wifi_pos_err("wifi_pos priv obj is null"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| wifi_pos_debug("Received data req pid(%d), len(%d)", |
| req->pid, req->buf_len); |
| |
| /* look for fields */ |
| if (req->field_info_buf) |
| for (idx = 0; idx < req->field_info_buf->count; idx++) { |
| offset = req->field_info_buf->fields[idx].offset; |
| /* |
| * replace following reads with read_api based on |
| * length |
| */ |
| if (req->field_info_buf->fields[idx].id == |
| WMIRTT_FIELD_ID_oem_data_sub_type) { |
| sub_type = *((uint32_t *)&req->buf[offset]); |
| continue; |
| } |
| |
| if (req->field_info_buf->fields[idx].id == |
| WMIRTT_FIELD_ID_channel_mhz) { |
| channel_mhz = *((uint32_t *)&req->buf[offset]); |
| continue; |
| } |
| |
| if (req->field_info_buf->fields[idx].id == |
| WMIRTT_FIELD_ID_pdev) { |
| pdev_id = *((uint32_t *)&req->buf[offset]); |
| /* pdev_id in FW starts from 1. So convert it to |
| * host id by decrementing it. |
| * zero has special meaning due to backward |
| * compatibility. Dont change it. |
| */ |
| if (pdev_id) |
| pdev_id -= 1; |
| continue; |
| } |
| } |
| |
| switch (sub_type) { |
| case TARGET_OEM_CAPABILITY_REQ: |
| /* TBD */ |
| break; |
| case TARGET_OEM_CONFIGURE_LCR: |
| /* TBD */ |
| break; |
| case TARGET_OEM_CONFIGURE_LCI: |
| /* TBD */ |
| break; |
| case TARGET_OEM_MEASUREMENT_REQ: |
| /* TBD */ |
| break; |
| case TARGET_OEM_CONFIGURE_FTMRR: |
| wifi_pos_debug("FTMRR request"); |
| if (wifi_pos_obj->wifi_pos_send_action) |
| wifi_pos_obj->wifi_pos_send_action(psoc, sub_type, |
| req->buf, |
| req->buf_len); |
| break; |
| case TARGET_OEM_CONFIGURE_WRU: |
| wifi_pos_debug("WRU request"); |
| if (wifi_pos_obj->wifi_pos_send_action) |
| wifi_pos_obj->wifi_pos_send_action(psoc, sub_type, |
| req->buf, |
| req->buf_len); |
| break; |
| default: |
| wifi_pos_debug("invalid sub type or not passed"); |
| |
| tx_ops = wifi_pos_get_tx_ops(psoc); |
| if (!tx_ops) { |
| wifi_pos_err("tx ops null"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| pdev = wlan_objmgr_get_pdev_by_id(psoc, pdev_id, |
| WLAN_WIFI_POS_CORE_ID); |
| if (pdev) { |
| data_req.data_len = req->buf_len; |
| data_req.data = req->buf; |
| tx_ops->data_req_tx(pdev, &data_req); |
| wlan_objmgr_pdev_release_ref(pdev, |
| WLAN_WIFI_POS_CORE_ID); |
| } |
| break; |
| } |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| static QDF_STATUS wifi_pos_process_set_cap_req(struct wlan_objmgr_psoc *psoc, |
| struct wifi_pos_req_msg *req) |
| { |
| int error_code; |
| struct wifi_pos_psoc_priv_obj *wifi_pos_obj = |
| wifi_pos_get_psoc_priv_obj(psoc); |
| struct wifi_pos_user_defined_caps *caps = |
| (struct wifi_pos_user_defined_caps *)req->buf; |
| |
| if (!wifi_pos_obj) { |
| wifi_pos_err("wifi_pos priv obj is null"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| wifi_pos_debug("Received set cap req pid(%d), len(%d)", |
| req->pid, req->buf_len); |
| |
| wifi_pos_obj->ftm_rr = caps->ftm_rr; |
| wifi_pos_obj->lci_capability = caps->lci_capability; |
| error_code = qdf_status_to_os_return(QDF_STATUS_SUCCESS); |
| wifi_pos_obj->wifi_pos_send_rsp(wifi_pos_obj->app_pid, |
| ANI_MSG_SET_OEM_CAP_RSP, |
| sizeof(error_code), |
| (uint8_t *)&error_code); |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| static QDF_STATUS wifi_pos_process_get_cap_req(struct wlan_objmgr_psoc *psoc, |
| struct wifi_pos_req_msg *req) |
| { |
| struct wifi_pos_oem_get_cap_rsp cap_rsp = { { {0} } }; |
| struct wifi_pos_psoc_priv_obj *wifi_pos_obj = |
| wifi_pos_get_psoc_priv_obj(psoc); |
| |
| if (!wifi_pos_obj) { |
| wifi_pos_err("wifi_pos priv obj is null"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| wifi_pos_debug("Received get cap req pid(%d), len(%d)", |
| req->pid, req->buf_len); |
| |
| wifi_pos_populate_caps(psoc, &cap_rsp.driver_cap); |
| cap_rsp.user_defined_cap.ftm_rr = wifi_pos_obj->ftm_rr; |
| cap_rsp.user_defined_cap.lci_capability = wifi_pos_obj->lci_capability; |
| wifi_pos_obj->wifi_pos_send_rsp(wifi_pos_obj->app_pid, |
| ANI_MSG_GET_OEM_CAP_RSP, |
| sizeof(cap_rsp), |
| (uint8_t *)&cap_rsp); |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| QDF_STATUS wifi_pos_send_report_resp(struct wlan_objmgr_psoc *psoc, |
| int req_id, uint8_t *dest_mac, |
| int err_code) |
| { |
| struct wifi_pos_err_msg_report err_report = {0}; |
| struct wifi_pos_psoc_priv_obj *wifi_pos_obj = |
| wifi_pos_get_psoc_priv_obj(psoc); |
| |
| if (!wifi_pos_obj) { |
| wifi_pos_err("wifi_pos priv obj is null"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| err_report.msg_tag_len = OEM_MSG_RSP_HEAD_TAG_ID << 16; |
| err_report.msg_tag_len |= (sizeof(err_report) - |
| sizeof(err_report.err_rpt)) & 0x0000FFFF; |
| err_report.msg_subtype = TARGET_OEM_ERROR_REPORT_RSP; |
| err_report.req_id = req_id & 0xFFFF; |
| err_report.req_id |= ((err_code & 0xFF) << 16); |
| err_report.req_id |= (0x1 << 24); |
| err_report.time_left = 0xFFFFFFFF; |
| err_report.err_rpt.tag_len = OEM_MEAS_RSP_HEAD_TAG_ID << 16; |
| err_report.err_rpt.tag_len |= |
| (sizeof(struct wifi_pos_err_rpt)) & 0x0000FFFF; |
| memcpy(&err_report.err_rpt.dest_mac, dest_mac, QDF_MAC_ADDR_SIZE); |
| |
| wifi_pos_obj->wifi_pos_send_rsp(wifi_pos_obj->app_pid, |
| ANI_MSG_OEM_DATA_RSP, |
| sizeof(err_report), |
| (uint8_t *)&err_report); |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| static void wifi_update_channel_bw_info(struct wlan_objmgr_psoc *psoc, |
| struct wlan_objmgr_pdev *pdev, |
| uint16_t chan, |
| struct wifi_pos_ch_info_rsp *chan_info) |
| { |
| struct ch_params ch_params = {0}; |
| uint16_t sec_ch_2g = 0; |
| struct wifi_pos_psoc_priv_obj *wifi_pos_psoc = |
| wifi_pos_get_psoc_priv_obj(psoc); |
| uint32_t phy_mode; |
| |
| if (!wifi_pos_psoc) { |
| wifi_pos_err("wifi_pos priv obj is null"); |
| return; |
| } |
| |
| /* Passing CH_WIDTH_MAX will give the max bandwidth supported */ |
| ch_params.ch_width = CH_WIDTH_MAX; |
| |
| wlan_reg_set_channel_params(pdev, chan, sec_ch_2g, &ch_params); |
| if (ch_params.center_freq_seg0) |
| chan_info->band_center_freq1 = |
| wlan_reg_legacy_chan_to_freq( |
| pdev, |
| ch_params.center_freq_seg0); |
| |
| wifi_pos_psoc->wifi_pos_get_phy_mode(chan, ch_params.ch_width, |
| &phy_mode); |
| |
| REG_SET_CHANNEL_MODE(chan_info, phy_mode); |
| } |
| |
| static void wifi_pos_get_reg_info(struct wlan_objmgr_pdev *pdev, |
| uint32_t chan_num, uint32_t *reg_info_1, |
| uint32_t *reg_info_2) |
| { |
| uint32_t reg_power = wlan_reg_get_channel_reg_power(pdev, chan_num); |
| |
| *reg_info_1 = 0; |
| *reg_info_2 = 0; |
| |
| REG_SET_CHANNEL_REG_POWER(*reg_info_1, reg_power); |
| REG_SET_CHANNEL_MAX_TX_POWER(*reg_info_2, reg_power); |
| } |
| |
| /** |
| * wifi_pos_get_valid_channels: Get the list of valid channels from the |
| * given channel list |
| * @channels: Channel list to be validated |
| * @num_ch: NUmber of channels in the channel list to be validated |
| * @valid_channel_list: Pointer to valid channel list |
| * |
| * Return: Number of valid channels in the given list |
| */ |
| |
| static uint32_t wifi_pos_get_valid_channels(uint8_t *channels, uint32_t num_ch, |
| uint8_t *valid_channel_list) { |
| uint32_t i, num_valid_channels = 0; |
| |
| for (i = 0; i < num_ch; i++) { |
| if (wlan_reg_get_chan_enum(channels[i]) == INVALID_CHANNEL) |
| continue; |
| valid_channel_list[num_valid_channels++] = channels[i]; |
| } |
| return num_valid_channels; |
| } |
| |
| static QDF_STATUS wifi_pos_process_ch_info_req(struct wlan_objmgr_psoc *psoc, |
| struct wifi_pos_req_msg *req) |
| { |
| uint8_t idx; |
| uint8_t *buf; |
| uint32_t len; |
| uint32_t reg_info_1; |
| uint32_t reg_info_2; |
| uint8_t *channels = req->buf; |
| struct wlan_objmgr_pdev *pdev; |
| uint32_t num_ch = req->buf_len; |
| uint8_t valid_channel_list[NUM_CHANNELS]; |
| uint32_t num_valid_channels; |
| struct wifi_pos_ch_info_rsp *ch_info; |
| struct wifi_pos_psoc_priv_obj *wifi_pos_obj = |
| wifi_pos_get_psoc_priv_obj(psoc); |
| |
| if (!wifi_pos_obj) { |
| wifi_pos_err("wifi_pos priv obj is null"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| wifi_pos_debug("Received ch info req pid(%d), len(%d)", |
| req->pid, req->buf_len); |
| |
| /* get first pdev since we need that only for freq and dfs state */ |
| pdev = wlan_objmgr_get_pdev_by_id(psoc, 0, WLAN_WIFI_POS_CORE_ID); |
| if (!pdev) { |
| wifi_pos_err("pdev get API failed"); |
| return QDF_STATUS_E_INVAL; |
| } |
| if (num_ch > NUM_CHANNELS) { |
| wifi_pos_err("Invalid number of channels"); |
| return QDF_STATUS_E_INVAL; |
| } |
| num_valid_channels = wifi_pos_get_valid_channels(channels, num_ch, |
| valid_channel_list); |
| |
| len = sizeof(uint8_t) + sizeof(struct wifi_pos_ch_info_rsp) * |
| num_valid_channels; |
| buf = qdf_mem_malloc(len); |
| if (!buf) { |
| wlan_objmgr_pdev_release_ref(pdev, WLAN_WIFI_POS_CORE_ID); |
| return QDF_STATUS_E_NOMEM; |
| } |
| |
| qdf_mem_zero(buf, len); |
| |
| /* First byte of message body will have num of channels */ |
| buf[0] = num_valid_channels; |
| ch_info = (struct wifi_pos_ch_info_rsp *)&buf[1]; |
| for (idx = 0; idx < num_valid_channels; idx++) { |
| ch_info[idx].chan_id = valid_channel_list[idx]; |
| wifi_pos_get_reg_info(pdev, ch_info[idx].chan_id, |
| ®_info_1, ®_info_2); |
| ch_info[idx].reserved0 = 0; |
| ch_info[idx].mhz = wlan_reg_get_channel_freq( |
| pdev, |
| valid_channel_list[idx]); |
| ch_info[idx].band_center_freq1 = ch_info[idx].mhz; |
| ch_info[idx].band_center_freq2 = 0; |
| ch_info[idx].info = 0; |
| if (wlan_reg_is_dfs_ch(pdev, valid_channel_list[idx])) |
| WIFI_POS_SET_DFS(ch_info[idx].info); |
| |
| wifi_update_channel_bw_info(psoc, pdev, |
| ch_info[idx].chan_id, |
| &ch_info[idx]); |
| |
| ch_info[idx].reg_info_1 = reg_info_1; |
| ch_info[idx].reg_info_2 = reg_info_2; |
| } |
| |
| wifi_pos_obj->wifi_pos_send_rsp(wifi_pos_obj->app_pid, |
| ANI_MSG_CHANNEL_INFO_RSP, |
| len, buf); |
| qdf_mem_free(buf); |
| wlan_objmgr_pdev_release_ref(pdev, WLAN_WIFI_POS_CORE_ID); |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| static void wifi_pos_vdev_iterator(struct wlan_objmgr_psoc *psoc, |
| void *vdev, void *arg) |
| { |
| struct app_reg_rsp_vdev_info *vdev_info = arg; |
| |
| vdev_info[vdev_idx].dev_mode = wlan_vdev_mlme_get_opmode(vdev); |
| vdev_info[vdev_idx].vdev_id = wlan_vdev_get_id(vdev); |
| vdev_idx++; |
| } |
| |
| #ifdef CNSS_GENL |
| static uint8_t * |
| wifi_pos_prepare_reg_resp(uint32_t *rsp_len, |
| struct app_reg_rsp_vdev_info *vdevs_info) |
| { |
| uint32_t *nl_sign; |
| uint8_t *resp_buf; |
| struct wifi_app_reg_rsp *app_reg_rsp; |
| |
| /* |
| * allocate ENHNC_FLAGS_LEN i.e. 4bytes extra memory in app_reg_resp |
| * to indicate NLA type resoponse is supported for OEM request |
| * commands. |
| */ |
| *rsp_len = (sizeof(struct app_reg_rsp_vdev_info) * vdev_idx) |
| + sizeof(uint8_t) + ENHNC_FLAGS_LEN; |
| resp_buf = qdf_mem_malloc(*rsp_len); |
| if (!resp_buf) |
| return NULL; |
| |
| app_reg_rsp = (struct wifi_app_reg_rsp *)resp_buf; |
| app_reg_rsp->num_inf = vdev_idx; |
| qdf_mem_copy(&app_reg_rsp->vdevs, vdevs_info, |
| sizeof(struct app_reg_rsp_vdev_info) * vdev_idx); |
| |
| nl_sign = (uint32_t *)&app_reg_rsp->vdevs[vdev_idx]; |
| *nl_sign |= NL_ENABLE_OEM_REQ_RSP; |
| |
| return resp_buf; |
| } |
| #else |
| static uint8_t * |
| wifi_pos_prepare_reg_resp(uint32_t *rsp_len, |
| struct app_reg_rsp_vdev_info *vdevs_info) |
| { |
| uint8_t *resp_buf; |
| struct wifi_app_reg_rsp *app_reg_rsp; |
| |
| *rsp_len = (sizeof(struct app_reg_rsp_vdev_info) * vdev_idx) |
| + sizeof(uint8_t); |
| resp_buf = qdf_mem_malloc(*rsp_len); |
| if (!resp_buf) |
| return NULL; |
| |
| app_reg_rsp = (struct wifi_app_reg_rsp *)resp_buf; |
| app_reg_rsp->num_inf = vdev_idx; |
| qdf_mem_copy(&app_reg_rsp->vdevs, vdevs_info, |
| sizeof(struct app_reg_rsp_vdev_info) * vdev_idx); |
| |
| return resp_buf; |
| } |
| #endif |
| |
| static QDF_STATUS wifi_pos_process_app_reg_req(struct wlan_objmgr_psoc *psoc, |
| struct wifi_pos_req_msg *req) |
| { |
| QDF_STATUS ret = QDF_STATUS_SUCCESS; |
| uint8_t err = 0, *app_reg_rsp; |
| uint32_t rsp_len; |
| char *sign_str = NULL; |
| struct app_reg_rsp_vdev_info vdevs_info[WLAN_UMAC_PSOC_MAX_VDEVS] |
| = { { 0 } }; |
| struct wifi_pos_psoc_priv_obj *wifi_pos_obj = |
| wifi_pos_get_psoc_priv_obj(psoc); |
| |
| if (!wifi_pos_obj) { |
| wifi_pos_err("wifi_pos priv obj is null"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| wifi_pos_err("Received App Req Req pid(%d), len(%d)", |
| req->pid, req->buf_len); |
| |
| sign_str = (char *)req->buf; |
| /* Registration request is only allowed for QTI Application */ |
| if ((OEM_APP_SIGNATURE_LEN != req->buf_len) || |
| (strncmp(sign_str, OEM_APP_SIGNATURE_STR, |
| OEM_APP_SIGNATURE_LEN))) { |
| wifi_pos_err("Invalid signature pid(%d)", req->pid); |
| ret = QDF_STATUS_E_PERM; |
| err = OEM_ERR_INVALID_SIGNATURE; |
| goto app_reg_failed; |
| } |
| |
| wifi_pos_debug("Valid App Req Req from pid(%d)", req->pid); |
| qdf_spin_lock_bh(&wifi_pos_obj->wifi_pos_lock); |
| wifi_pos_obj->is_app_registered = true; |
| wifi_pos_obj->app_pid = req->pid; |
| qdf_spin_unlock_bh(&wifi_pos_obj->wifi_pos_lock); |
| |
| vdev_idx = 0; |
| wlan_objmgr_iterate_obj_list(psoc, WLAN_VDEV_OP, |
| wifi_pos_vdev_iterator, |
| vdevs_info, true, WLAN_WIFI_POS_CORE_ID); |
| |
| app_reg_rsp = wifi_pos_prepare_reg_resp(&rsp_len, vdevs_info); |
| if (!app_reg_rsp) { |
| ret = QDF_STATUS_E_NOMEM; |
| err = OEM_ERR_NULL_CONTEXT; |
| goto app_reg_failed; |
| } |
| |
| if (!vdev_idx) |
| wifi_pos_debug("no active vdev"); |
| |
| vdev_idx = 0; |
| wifi_pos_obj->wifi_pos_send_rsp(req->pid, ANI_MSG_APP_REG_RSP, |
| rsp_len, (uint8_t *)app_reg_rsp); |
| |
| qdf_mem_free(app_reg_rsp); |
| return ret; |
| |
| app_reg_failed: |
| |
| wifi_pos_obj->wifi_pos_send_rsp(req->pid, ANI_MSG_OEM_ERROR, |
| sizeof(err), &err); |
| return ret; |
| } |
| |
| /** |
| * wifi_pos_tlv_callback: wifi pos msg handler registered for TLV type req |
| * @wmi_msg: wmi type request msg |
| * |
| * Return: status of operation |
| */ |
| static QDF_STATUS wifi_pos_tlv_callback(struct wlan_objmgr_psoc *psoc, |
| struct wifi_pos_req_msg *req) |
| { |
| wifi_pos_debug("enter: msg_type: %d", req->msg_type); |
| switch (req->msg_type) { |
| case ANI_MSG_APP_REG_REQ: |
| return wifi_pos_process_app_reg_req(psoc, req); |
| case ANI_MSG_OEM_DATA_REQ: |
| return wifi_pos_process_data_req(psoc, req); |
| case ANI_MSG_CHANNEL_INFO_REQ: |
| return wifi_pos_process_ch_info_req(psoc, req); |
| case ANI_MSG_SET_OEM_CAP_REQ: |
| return wifi_pos_process_set_cap_req(psoc, req); |
| case ANI_MSG_GET_OEM_CAP_REQ: |
| return wifi_pos_process_get_cap_req(psoc, req); |
| default: |
| wifi_pos_err("invalid request type"); |
| break; |
| } |
| return 0; |
| } |
| |
| /** |
| * wifi_pos_non_tlv_callback: wifi pos msg handler registered for non-TLV |
| * type req |
| * @wmi_msg: wmi type request msg |
| * |
| * Return: status of operation |
| */ |
| static QDF_STATUS wifi_pos_non_tlv_callback(struct wlan_objmgr_psoc *psoc, |
| struct wifi_pos_req_msg *req) |
| { |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| QDF_STATUS wifi_pos_psoc_obj_created_notification( |
| struct wlan_objmgr_psoc *psoc, void *arg_list) |
| { |
| QDF_STATUS status; |
| struct wifi_pos_psoc_priv_obj *wifi_pos_obj; |
| |
| /* |
| * this is for WIN, if they have multiple psoc, we dont want to create |
| * multiple priv object. Since there is just one LOWI app registered to |
| * one driver, avoid 2nd private object with another psoc. |
| */ |
| if (wifi_pos_get_psoc()) { |
| wifi_pos_debug("global psoc obj already set. do not allocate another psoc private object"); |
| return QDF_STATUS_SUCCESS; |
| } else { |
| wifi_pos_debug("setting global pos object"); |
| wifi_pos_set_psoc(psoc); |
| } |
| |
| /* initialize wifi-pos psoc priv object */ |
| wifi_pos_obj = qdf_mem_malloc(sizeof(*wifi_pos_obj)); |
| if (!wifi_pos_obj) { |
| wifi_pos_clear_psoc(); |
| return QDF_STATUS_E_NOMEM; |
| } |
| |
| qdf_spinlock_create(&wifi_pos_obj->wifi_pos_lock); |
| /* Register TLV or non-TLV callbacks depending on target fw version */ |
| if (wifi_pos_get_tlv_support(psoc)) |
| wifi_pos_obj->wifi_pos_req_handler = wifi_pos_tlv_callback; |
| else |
| wifi_pos_obj->wifi_pos_req_handler = wifi_pos_non_tlv_callback; |
| |
| /* |
| * MGMT Rx is not handled in this phase since wifi pos only uses few |
| * measurement subtypes under RRM_RADIO_MEASURE_REQ. Rest of them are |
| * used for 80211k. That part is not yet converged and still follows |
| * legacy MGMT Rx to work. Action frame in new TXRX can be registered |
| * at per ACTION Frame type granularity only. |
| */ |
| |
| status = wlan_objmgr_psoc_component_obj_attach(psoc, |
| WLAN_UMAC_COMP_WIFI_POS, |
| wifi_pos_obj, |
| QDF_STATUS_SUCCESS); |
| |
| if (QDF_IS_STATUS_ERROR(status)) { |
| wifi_pos_err("obj attach with psoc failed with status: %d", |
| status); |
| qdf_spinlock_destroy(&wifi_pos_obj->wifi_pos_lock); |
| qdf_mem_free(wifi_pos_obj); |
| wifi_pos_clear_psoc(); |
| } |
| |
| return status; |
| } |
| |
| QDF_STATUS wifi_pos_psoc_obj_destroyed_notification( |
| struct wlan_objmgr_psoc *psoc, void *arg_list) |
| { |
| QDF_STATUS status; |
| struct wifi_pos_psoc_priv_obj *wifi_pos_obj = NULL; |
| |
| if (wifi_pos_get_psoc() == psoc) { |
| wifi_pos_debug("deregistering wifi_pos_psoc object"); |
| wifi_pos_clear_psoc(); |
| } else { |
| wifi_pos_warn("un-related PSOC closed. do nothing"); |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| wifi_pos_obj = wifi_pos_get_psoc_priv_obj(psoc); |
| if (!wifi_pos_obj) { |
| wifi_pos_err("wifi_pos_obj is NULL"); |
| return QDF_STATUS_E_FAULT; |
| } |
| |
| target_if_wifi_pos_deinit_dma_rings(psoc); |
| |
| status = wlan_objmgr_psoc_component_obj_detach(psoc, |
| WLAN_UMAC_COMP_WIFI_POS, |
| wifi_pos_obj); |
| if (status != QDF_STATUS_SUCCESS) |
| wifi_pos_err("wifi_pos_obj detach failed"); |
| |
| wifi_pos_debug("wifi_pos_obj deleted with status %d", status); |
| qdf_spinlock_destroy(&wifi_pos_obj->wifi_pos_lock); |
| qdf_mem_free(wifi_pos_obj); |
| |
| return status; |
| } |
| |
| int wifi_pos_oem_rsp_handler(struct wlan_objmgr_psoc *psoc, |
| struct oem_data_rsp *oem_rsp) |
| { |
| uint32_t len; |
| uint8_t *data; |
| uint32_t app_pid; |
| struct wifi_pos_psoc_priv_obj *priv = |
| wifi_pos_get_psoc_priv_obj(psoc); |
| void (*wifi_pos_send_rsp)(uint32_t, uint32_t, uint32_t, uint8_t *); |
| |
| if (!priv) { |
| wifi_pos_err("private object is NULL"); |
| return -EINVAL; |
| } |
| |
| qdf_spin_lock_bh(&priv->wifi_pos_lock); |
| app_pid = priv->app_pid; |
| wifi_pos_send_rsp = priv->wifi_pos_send_rsp; |
| qdf_spin_unlock_bh(&priv->wifi_pos_lock); |
| |
| len = oem_rsp->rsp_len_1 + oem_rsp->rsp_len_2 + oem_rsp->dma_len; |
| if (oem_rsp->rsp_len_1 > OEM_DATA_RSP_SIZE || |
| oem_rsp->rsp_len_2 > OEM_DATA_RSP_SIZE) { |
| wifi_pos_err("invalid length of Oem Data response"); |
| return -EINVAL; |
| } |
| |
| if (!wifi_pos_send_rsp) { |
| wifi_pos_err("invalid response handler"); |
| return -EINVAL; |
| } |
| |
| wifi_pos_debug("oem data rsp, len: %d to pid: %d", len, app_pid); |
| |
| if (oem_rsp->rsp_len_2 + oem_rsp->dma_len) { |
| /* stitch togther the msg data_1 + CIR/CFR + data_2 */ |
| data = qdf_mem_malloc(len); |
| if (!data) |
| return -ENOMEM; |
| |
| qdf_mem_copy(data, oem_rsp->data_1, oem_rsp->rsp_len_1); |
| qdf_mem_copy(&data[oem_rsp->rsp_len_1], |
| oem_rsp->vaddr, oem_rsp->dma_len); |
| qdf_mem_copy(&data[oem_rsp->rsp_len_1 + oem_rsp->dma_len], |
| oem_rsp->data_2, oem_rsp->rsp_len_2); |
| |
| wifi_pos_send_rsp(app_pid, ANI_MSG_OEM_DATA_RSP, len, data); |
| qdf_mem_free(data); |
| } else { |
| wifi_pos_send_rsp(app_pid, ANI_MSG_OEM_DATA_RSP, |
| oem_rsp->rsp_len_1, oem_rsp->data_1); |
| } |
| |
| return 0; |
| } |
| |
| void wifi_pos_register_rx_ops(struct wlan_lmac_if_rx_ops *rx_ops) |
| { |
| struct wlan_lmac_if_wifi_pos_rx_ops *wifi_pos_rx_ops; |
| |
| wifi_pos_rx_ops = &rx_ops->wifi_pos_rx_ops; |
| wifi_pos_rx_ops->oem_rsp_event_rx = wifi_pos_oem_rsp_handler; |
| } |
| |
| static void wifi_pos_pdev_iterator(struct wlan_objmgr_psoc *psoc, |
| void *obj, void *arg) |
| { |
| QDF_STATUS status; |
| uint8_t i, num_channels, size, valid_ch = 0; |
| struct wlan_objmgr_pdev *pdev = obj; |
| struct wifi_pos_driver_caps *caps = arg; |
| struct channel_power *ch_list; |
| bool oem_6g_support_disable; |
| struct wifi_pos_psoc_priv_obj *wifi_pos_obj = |
| wifi_pos_get_psoc_priv_obj(psoc); |
| |
| size = QDF_MAX(OEM_CAP_MAX_NUM_CHANNELS, NUM_CHANNELS); |
| ch_list = qdf_mem_malloc(size * sizeof(*ch_list)); |
| if (!ch_list) |
| return; |
| |
| status = wlan_reg_get_channel_list_with_power(pdev, ch_list, |
| &num_channels); |
| |
| if (QDF_IS_STATUS_ERROR(status)) { |
| wifi_pos_err("Failed to get valid channel list"); |
| qdf_mem_free(ch_list); |
| return; |
| } |
| if (num_channels > OEM_CAP_MAX_NUM_CHANNELS) |
| num_channels = OEM_CAP_MAX_NUM_CHANNELS; |
| |
| qdf_spin_lock_bh(&wifi_pos_obj->wifi_pos_lock); |
| oem_6g_support_disable = wifi_pos_obj->oem_6g_support_disable; |
| qdf_spin_unlock_bh(&wifi_pos_obj->wifi_pos_lock); |
| |
| for (i = 0; i < num_channels; i++) { |
| if (oem_6g_support_disable && |
| WLAN_REG_IS_6GHZ_CHAN_FREQ(ch_list[i].center_freq)) |
| continue; |
| caps->channel_list[valid_ch++] = ch_list[i].chan_num; |
| } |
| caps->num_channels = valid_ch; |
| qdf_mem_free(ch_list); |
| } |
| |
| static void wifi_pos_get_ch_info(struct wlan_objmgr_psoc *psoc, |
| struct wifi_pos_driver_caps *caps) |
| { |
| wlan_objmgr_iterate_obj_list(psoc, WLAN_PDEV_OP, |
| wifi_pos_pdev_iterator, |
| caps, true, WLAN_WIFI_POS_CORE_ID); |
| wifi_pos_err("num channels: %d", caps->num_channels); |
| } |
| |
| QDF_STATUS wifi_pos_populate_caps(struct wlan_objmgr_psoc *psoc, |
| struct wifi_pos_driver_caps *caps) |
| { |
| struct wifi_pos_psoc_priv_obj *wifi_pos_obj = |
| wifi_pos_get_psoc_priv_obj(psoc); |
| |
| wifi_pos_debug("Enter"); |
| if (!wifi_pos_obj) { |
| wifi_pos_err("wifi_pos_obj is null"); |
| return QDF_STATUS_E_NULL_VALUE; |
| } |
| |
| strlcpy(caps->oem_target_signature, |
| OEM_TARGET_SIGNATURE, |
| OEM_TARGET_SIGNATURE_LEN); |
| caps->oem_target_type = wifi_pos_obj->oem_target_type; |
| caps->oem_fw_version = wifi_pos_obj->oem_fw_version; |
| caps->driver_version.major = wifi_pos_obj->driver_version.major; |
| caps->driver_version.minor = wifi_pos_obj->driver_version.minor; |
| caps->driver_version.patch = wifi_pos_obj->driver_version.patch; |
| caps->driver_version.build = wifi_pos_obj->driver_version.build; |
| caps->allowed_dwell_time_min = wifi_pos_obj->allowed_dwell_time_min; |
| caps->allowed_dwell_time_max = wifi_pos_obj->allowed_dwell_time_max; |
| caps->curr_dwell_time_min = wifi_pos_obj->current_dwell_time_min; |
| caps->curr_dwell_time_max = wifi_pos_obj->current_dwell_time_max; |
| caps->supported_bands = wlan_objmgr_psoc_get_band_capability(psoc); |
| wifi_pos_get_ch_info(psoc, caps); |
| return QDF_STATUS_SUCCESS; |
| } |