From cde9070d9970eef1f7ec2360586c802a16230ad8 Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Fri, 10 May 2024 07:43:50 +0000 Subject: [PATCH] rtl88x2CE_WiFi_linux driver --- kernel/drivers/net/wireless/rockchip_wlan/cywdhd/bcmdhd/dhd_rtt.c | 3028 +++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 files changed, 2,501 insertions(+), 527 deletions(-) diff --git a/kernel/drivers/net/wireless/rockchip_wlan/cywdhd/bcmdhd/dhd_rtt.c b/kernel/drivers/net/wireless/rockchip_wlan/cywdhd/bcmdhd/dhd_rtt.c index 28717b5..ccb53b3 100644 --- a/kernel/drivers/net/wireless/rockchip_wlan/cywdhd/bcmdhd/dhd_rtt.c +++ b/kernel/drivers/net/wireless/rockchip_wlan/cywdhd/bcmdhd/dhd_rtt.c @@ -1,15 +1,16 @@ -/* SPDX-License-Identifier: GPL-2.0 */ /* * Broadcom Dongle Host Driver (DHD), RTT * - * Copyright (C) 1999-2019, Broadcom Corporation - * + * Portions of this code are copyright (c) 2022 Cypress Semiconductor Corporation + * + * Copyright (C) 1999-2017, Broadcom Corporation + * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you * under the terms of the GNU General Public License version 2 (the "GPL"), * available at http://www.broadcom.com/licenses/GPLv2.php, with the * following added to such license: - * + * * As a special exception, the copyright holders of this software give you * permission to link this software with independent modules, and to copy and * distribute the resulting executable under terms of your choice, provided that @@ -17,12 +18,15 @@ * the license of that module. An independent module is a module which is not * derived from this software. The special exception does not apply to any * modifications of the software. - * + * * Notwithstanding the above, under no circumstances may you combine this * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: dhd_rtt.c 674349 2017-10-19 07:59:24Z $ + * + * <<Broadcom-WL-IPTag/Open:>> + * + * $Id$ */ #include <typedefs.h> #include <osl.h> @@ -38,13 +42,24 @@ #include <linux/sort.h> #include <dngl_stats.h> #include <wlioctl.h> +#include <bcmwifi_rspec.h> -#include <proto/bcmevent.h> +#include <bcmevent.h> #include <dhd.h> +#include <dhd_linux.h> #include <dhd_rtt.h> #include <dhd_dbg.h> -#define GET_RTTSTATE(dhd) ((rtt_status_info_t *)dhd->rtt_state) +#include <dhd_bus.h> +#include <wldev_common.h> +#ifdef WL_CFG80211 +#include <wl_cfg80211.h> +#endif /* WL_CFG80211 */ +#ifdef WL_NAN +#include <wl_cfgnan.h> +#endif /* WL_NAN */ + static DEFINE_SPINLOCK(noti_list_lock); +#ifndef NULL_CHECK #define NULL_CHECK(p, s, err) \ do { \ if (!(p)) { \ @@ -53,11 +68,15 @@ return err; \ } \ } while (0) +#endif // endif -#define RTT_IS_ENABLED(rtt_status) (rtt_status->status == RTT_ENABLED) -#define RTT_IS_STOPPED(rtt_status) (rtt_status->status == RTT_STOPPED) #define TIMESPEC_TO_US(ts) (((uint64)(ts).tv_sec * USEC_PER_SEC) + \ (ts).tv_nsec / NSEC_PER_USEC) + +#undef DHD_RTT_MEM +#undef DHD_RTT_ERR +#define DHD_RTT_MEM DHD_LOG_MEM +#define DHD_RTT_ERR DHD_ERROR #define FTM_IOC_BUFSZ 2048 /* ioc buffsize for our module (> BCM_XTLV_HDR_SIZE) */ #define FTM_AVAIL_MAX_SLOTS 32 @@ -66,9 +85,11 @@ #define FTM_DEFAULT_SESSION 1 #define FTM_BURST_TIMEOUT_UNIT 250 /* 250 ns */ #define FTM_INVALID -1 -#define FTM_DEFAULT_CNT_20M 12 -#define FTM_DEFAULT_CNT_40M 10 -#define FTM_DEFAULT_CNT_80M 5 +#define FTM_DEFAULT_CNT_20M 24u +#define FTM_DEFAULT_CNT_40M 16u +#define FTM_DEFAULT_CNT_80M 11u +/* To handle congestion env, set max dur/timeout */ +#define FTM_MAX_BURST_DUR_TMO_MS 128u /* convenience macros */ #define FTM_TU2MICRO(_tu) ((uint64)(_tu) << 10) @@ -84,6 +105,14 @@ /* broadcom specific set to have more accurate data */ #define ENABLE_VHT_ACK +#define CH_MIN_5G_CHANNEL 34 + +/* CUR ETH became obsolete with this major version onwards */ +#define RTT_IOV_CUR_ETH_OBSOLETE 12 + +/* PROXD TIMEOUT */ +#define DHD_RTT_TIMER_INTERVAL_MS 5000u +#define DHD_NAN_RTT_TIMER_INTERVAL_MS 10000u struct rtt_noti_callback { struct list_head list; @@ -91,30 +120,10 @@ dhd_rtt_compl_noti_fn noti_fn; }; -typedef struct rtt_status_info { - dhd_pub_t *dhd; - int8 status; /* current status for the current entry */ - int8 txchain; /* current device tx chain */ - int8 mpc; /* indicate we change mpc mode */ - int8 cur_idx; /* current entry to do RTT */ - bool all_cancel; /* cancel all request once we got the cancel requet */ - struct capability { - int32 proto :8; - int32 feature :8; - int32 preamble :8; - int32 bw :8; - } rtt_capa; /* rtt capability */ - struct mutex rtt_mutex; - rtt_config_params_t rtt_config; - struct work_struct work; - struct list_head noti_fn_list; - struct list_head rtt_results_cache; /* store results for RTT */ -} rtt_status_info_t; - /* bitmask indicating which command groups; */ typedef enum { FTM_SUBCMD_FLAG_METHOD = 0x01, /* FTM method command */ - FTM_SUBCMD_FLAG_SESSION = 0x02, /* FTM session command */ + FTM_SUBCMD_FLAG_SESSION = 0x02, /* FTM session command */ FTM_SUBCMD_FLAG_ALL = FTM_SUBCMD_FLAG_METHOD | FTM_SUBCMD_FLAG_SESSION } ftm_subcmd_flag_t; @@ -125,7 +134,6 @@ FTM_CONFIG_CAT_AVAIL = 3, /* 'config avail' */ } ftm_config_category_t; - typedef struct ftm_subcmd_info { int16 version; /* FTM version (optional) */ char *name; /* cmd-name string as cmdline input */ @@ -134,9 +142,8 @@ ftm_subcmd_flag_t cmdflag; /* CMD flag (optional) */ } ftm_subcmd_info_t; - typedef struct ftm_config_options_info { - uint32 flags; /* wl_proxd_flags_t/wl_proxd_session_flags_t */ + uint32 flags; /* wl_proxd_flags_t/wl_proxd_session_flags_t */ bool enable; } ftm_config_options_info_t; @@ -149,157 +156,118 @@ uint32 data32; uint16 data16; uint8 data8; + uint32 event_mask; }; } ftm_config_param_info_t; /* * definition for id-string mapping. -* This is used to map an id (can be cmd-id, tlv-id, ....) to a text-string -* for debug-display or cmd-log-display +* This is used to map an id (can be cmd-id, tlv-id, ....) to a text-string +* for debug-display or cmd-log-display */ typedef struct ftm_strmap_entry { int32 id; char *text; } ftm_strmap_entry_t; - typedef struct ftm_status_map_host_entry { wl_proxd_status_t proxd_status; rtt_reason_t rtt_reason; } ftm_status_map_host_entry_t; +static uint16 +rtt_result_ver(uint16 tlvid, const uint8 *p_data); + static int -dhd_rtt_convert_results_to_host(rtt_report_t *rtt_report, uint8 *p_data, uint16 tlvid, uint16 len); +dhd_rtt_convert_results_to_host_v1(rtt_result_t *rtt_result, const uint8 *p_data, + uint16 tlvid, uint16 len); + +static int +dhd_rtt_convert_results_to_host_v2(rtt_result_t *rtt_result, const uint8 *p_data, + uint16 tlvid, uint16 len); static wifi_rate_t dhd_rtt_convert_rate_to_host(uint32 ratespec); +#if defined(WL_CFG80211) && defined(RTT_DEBUG) +const char * +ftm_cmdid_to_str(uint16 cmdid); +#endif /* WL_CFG80211 && RTT_DEBUG */ + +#ifdef WL_CFG80211 static int dhd_rtt_start(dhd_pub_t *dhd); +static int dhd_rtt_create_failure_result(rtt_status_info_t *rtt_status, + struct ether_addr *addr); +static void dhd_rtt_handle_rtt_session_end(dhd_pub_t *dhd); +static void dhd_rtt_timeout_work(struct work_struct *work); +#endif /* WL_CFG80211 */ static const int burst_duration_idx[] = {0, 0, 1, 2, 4, 8, 16, 32, 64, 128, 0, 0}; /* ftm status mapping to host status */ static const ftm_status_map_host_entry_t ftm_status_map_info[] = { - {WL_PROXD_E_INCOMPLETE, RTT_REASON_FAILURE}, - {WL_PROXD_E_OVERRIDDEN, RTT_REASON_FAILURE}, - {WL_PROXD_E_ASAP_FAILED, RTT_REASON_FAILURE}, - {WL_PROXD_E_NOTSTARTED, RTT_REASON_FAIL_NOT_SCHEDULED_YET}, - {WL_PROXD_E_INVALIDAVB, RTT_REASON_FAIL_INVALID_TS}, - {WL_PROXD_E_INCAPABLE, RTT_REASON_FAIL_NO_CAPABILITY}, - {WL_PROXD_E_MISMATCH, RTT_REASON_FAILURE}, - {WL_PROXD_E_DUP_SESSION, RTT_REASON_FAILURE}, - {WL_PROXD_E_REMOTE_FAIL, RTT_REASON_FAILURE}, - {WL_PROXD_E_REMOTE_INCAPABLE, RTT_REASON_FAILURE}, - {WL_PROXD_E_SCHED_FAIL, RTT_REASON_FAIL_SCHEDULE}, - {WL_PROXD_E_PROTO, RTT_REASON_FAIL_PROTOCOL}, - {WL_PROXD_E_EXPIRED, RTT_REASON_FAILURE}, - {WL_PROXD_E_TIMEOUT, RTT_REASON_FAIL_TM_TIMEOUT}, - {WL_PROXD_E_NOACK, RTT_REASON_FAIL_NO_RSP}, - {WL_PROXD_E_DEFERRED, RTT_REASON_FAILURE}, - {WL_PROXD_E_INVALID_SID, RTT_REASON_FAILURE}, - {WL_PROXD_E_REMOTE_CANCEL, RTT_REASON_FAILURE}, - {WL_PROXD_E_CANCELED, RTT_REASON_ABORTED}, - {WL_PROXD_E_INVALID_SESSION, RTT_REASON_FAILURE}, - {WL_PROXD_E_BAD_STATE, RTT_REASON_FAILURE}, - {WL_PROXD_E_ERROR, RTT_REASON_FAILURE}, - {WL_PROXD_E_OK, RTT_REASON_SUCCESS} -}; - -/* ftm tlv-id mapping */ -static const ftm_strmap_entry_t ftm_tlvid_loginfo[] = { - /* { WL_PROXD_TLV_ID_xxx, "text for WL_PROXD_TLV_ID_xxx" }, */ - { WL_PROXD_TLV_ID_NONE, "none" }, - { WL_PROXD_TLV_ID_METHOD, "method" }, - { WL_PROXD_TLV_ID_FLAGS, "flags" }, - { WL_PROXD_TLV_ID_CHANSPEC, "chanspec" }, - { WL_PROXD_TLV_ID_TX_POWER, "tx power" }, - { WL_PROXD_TLV_ID_RATESPEC, "ratespec" }, - { WL_PROXD_TLV_ID_BURST_DURATION, "burst duration" }, - { WL_PROXD_TLV_ID_BURST_PERIOD, "burst period" }, - { WL_PROXD_TLV_ID_BURST_FTM_SEP, "burst ftm sep" }, - { WL_PROXD_TLV_ID_BURST_NUM_FTM, "burst num ftm" }, - { WL_PROXD_TLV_ID_NUM_BURST, "num burst" }, - { WL_PROXD_TLV_ID_FTM_RETRIES, "ftm retries" }, - { WL_PROXD_TLV_ID_BSS_INDEX, "BSS index" }, - { WL_PROXD_TLV_ID_BSSID, "bssid" }, - { WL_PROXD_TLV_ID_INIT_DELAY, "burst init delay" }, - { WL_PROXD_TLV_ID_BURST_TIMEOUT, "burst timeout" }, - { WL_PROXD_TLV_ID_EVENT_MASK, "event mask" }, - { WL_PROXD_TLV_ID_FLAGS_MASK, "flags mask" }, - { WL_PROXD_TLV_ID_PEER_MAC, "peer addr" }, - { WL_PROXD_TLV_ID_FTM_REQ, "ftm req" }, - { WL_PROXD_TLV_ID_LCI_REQ, "lci req" }, - { WL_PROXD_TLV_ID_LCI, "lci" }, - { WL_PROXD_TLV_ID_CIVIC_REQ, "civic req" }, - { WL_PROXD_TLV_ID_CIVIC, "civic" }, - { WL_PROXD_TLV_ID_AVAIL, "availability" }, - { WL_PROXD_TLV_ID_SESSION_FLAGS, "session flags" }, - { WL_PROXD_TLV_ID_SESSION_FLAGS_MASK, "session flags mask" }, - { WL_PROXD_TLV_ID_RX_MAX_BURST, "rx max bursts" }, - { WL_PROXD_TLV_ID_RANGING_INFO, "ranging info" }, - { WL_PROXD_TLV_ID_RANGING_FLAGS, "ranging flags" }, - { WL_PROXD_TLV_ID_RANGING_FLAGS_MASK, "ranging flags mask" }, - /* output - 512 + x */ - { WL_PROXD_TLV_ID_STATUS, "status" }, - { WL_PROXD_TLV_ID_COUNTERS, "counters" }, - { WL_PROXD_TLV_ID_INFO, "info" }, - { WL_PROXD_TLV_ID_RTT_RESULT, "rtt result" }, - { WL_PROXD_TLV_ID_AOA_RESULT, "aoa result" }, - { WL_PROXD_TLV_ID_SESSION_INFO, "session info" }, - { WL_PROXD_TLV_ID_SESSION_STATUS, "session status" }, - { WL_PROXD_TLV_ID_SESSION_ID_LIST, "session ids" }, - /* debug tlvs can be added starting 1024 */ - { WL_PROXD_TLV_ID_DEBUG_MASK, "debug mask" }, - { WL_PROXD_TLV_ID_COLLECT, "collect" }, - { WL_PROXD_TLV_ID_STRBUF, "result" } + {WL_PROXD_E_INCOMPLETE, RTT_STATUS_FAILURE}, + {WL_PROXD_E_OVERRIDDEN, RTT_STATUS_FAILURE}, + {WL_PROXD_E_ASAP_FAILED, RTT_STATUS_FAILURE}, + {WL_PROXD_E_NOTSTARTED, RTT_STATUS_FAIL_NOT_SCHEDULED_YET}, + {WL_PROXD_E_INVALIDMEAS, RTT_STATUS_FAIL_INVALID_TS}, + {WL_PROXD_E_INCAPABLE, RTT_STATUS_FAIL_NO_CAPABILITY}, + {WL_PROXD_E_MISMATCH, RTT_STATUS_FAILURE}, + {WL_PROXD_E_DUP_SESSION, RTT_STATUS_FAILURE}, + {WL_PROXD_E_REMOTE_FAIL, RTT_STATUS_FAILURE}, + {WL_PROXD_E_REMOTE_INCAPABLE, RTT_STATUS_FAILURE}, + {WL_PROXD_E_SCHED_FAIL, RTT_STATUS_FAIL_SCHEDULE}, + {WL_PROXD_E_PROTO, RTT_STATUS_FAIL_PROTOCOL}, + {WL_PROXD_E_EXPIRED, RTT_STATUS_FAILURE}, + {WL_PROXD_E_TIMEOUT, RTT_STATUS_FAIL_TM_TIMEOUT}, + {WL_PROXD_E_NOACK, RTT_STATUS_FAIL_NO_RSP}, + {WL_PROXD_E_DEFERRED, RTT_STATUS_FAILURE}, + {WL_PROXD_E_INVALID_SID, RTT_STATUS_FAILURE}, + {WL_PROXD_E_REMOTE_CANCEL, RTT_STATUS_FAILURE}, + {WL_PROXD_E_CANCELED, RTT_STATUS_ABORTED}, + {WL_PROXD_E_INVALID_SESSION, RTT_STATUS_FAILURE}, + {WL_PROXD_E_BAD_STATE, RTT_STATUS_FAILURE}, + {WL_PROXD_E_ERROR, RTT_STATUS_FAILURE}, + {WL_PROXD_E_OK, RTT_STATUS_SUCCESS} }; static const ftm_strmap_entry_t ftm_event_type_loginfo[] = { - /* wl_proxd_event_type_t, text-string */ - { WL_PROXD_EVENT_NONE, "none" }, + /* wl_proxd_event_type_t, text-string */ + { WL_PROXD_EVENT_NONE, "none" }, { WL_PROXD_EVENT_SESSION_CREATE, "session create" }, { WL_PROXD_EVENT_SESSION_START, "session start" }, - { WL_PROXD_EVENT_FTM_REQ, "FTM req" }, + { WL_PROXD_EVENT_FTM_REQ, "FTM req" }, { WL_PROXD_EVENT_BURST_START, "burst start" }, - { WL_PROXD_EVENT_BURST_END, "burst end" }, + { WL_PROXD_EVENT_BURST_END, "burst end" }, { WL_PROXD_EVENT_SESSION_END, "session end" }, { WL_PROXD_EVENT_SESSION_RESTART, "session restart" }, { WL_PROXD_EVENT_BURST_RESCHED, "burst rescheduled" }, { WL_PROXD_EVENT_SESSION_DESTROY, "session destroy" }, - { WL_PROXD_EVENT_RANGE_REQ, "range request" }, - { WL_PROXD_EVENT_FTM_FRAME, "FTM frame" }, - { WL_PROXD_EVENT_DELAY, "delay" }, + { WL_PROXD_EVENT_RANGE_REQ, "range request" }, + { WL_PROXD_EVENT_FTM_FRAME, "FTM frame" }, + { WL_PROXD_EVENT_DELAY, "delay" }, { WL_PROXD_EVENT_VS_INITIATOR_RPT, "initiator-report " }, /* rx initiator-rpt */ - { WL_PROXD_EVENT_RANGING, "ranging " }, + { WL_PROXD_EVENT_RANGING, "ranging " }, + { WL_PROXD_EVENT_COLLECT, "collect" }, + { WL_PROXD_EVENT_MF_STATS, "mf_stats" }, }; /* * session-state --> text string mapping */ static const ftm_strmap_entry_t ftm_session_state_value_loginfo[] = { - /* wl_proxd_session_state_t, text string */ - { WL_PROXD_SESSION_STATE_CREATED, "created" }, + /* wl_proxd_session_state_t, text string */ + { WL_PROXD_SESSION_STATE_CREATED, "created" }, { WL_PROXD_SESSION_STATE_CONFIGURED, "configured" }, - { WL_PROXD_SESSION_STATE_STARTED, "started" }, - { WL_PROXD_SESSION_STATE_DELAY, "delay" }, - { WL_PROXD_SESSION_STATE_USER_WAIT, "user-wait" }, + { WL_PROXD_SESSION_STATE_STARTED, "started" }, + { WL_PROXD_SESSION_STATE_DELAY, "delay" }, + { WL_PROXD_SESSION_STATE_USER_WAIT, "user-wait" }, { WL_PROXD_SESSION_STATE_SCHED_WAIT, "sched-wait" }, - { WL_PROXD_SESSION_STATE_BURST, "burst" }, - { WL_PROXD_SESSION_STATE_STOPPING, "stopping" }, - { WL_PROXD_SESSION_STATE_ENDED, "ended" }, + { WL_PROXD_SESSION_STATE_BURST, "burst" }, + { WL_PROXD_SESSION_STATE_STOPPING, "stopping" }, + { WL_PROXD_SESSION_STATE_ENDED, "ended" }, { WL_PROXD_SESSION_STATE_DESTROYING, "destroying" }, - { WL_PROXD_SESSION_STATE_NONE, "none" } -}; - -/* -* ranging-state --> text string mapping -*/ -static const ftm_strmap_entry_t ftm_ranging_state_value_loginfo [] = { - /* wl_proxd_ranging_state_t, text string */ - { WL_PROXD_RANGING_STATE_NONE, "none" }, - { WL_PROXD_RANGING_STATE_NOTSTARTED, "nonstarted" }, - { WL_PROXD_RANGING_STATE_INPROGRESS, "inprogress" }, - { WL_PROXD_RANGING_STATE_DONE, "done" }, + { WL_PROXD_SESSION_STATE_NONE, "none" } }; /* @@ -310,61 +278,39 @@ { WL_PROXD_E_OVERRIDDEN, "overridden" }, { WL_PROXD_E_ASAP_FAILED, "ASAP failed" }, { WL_PROXD_E_NOTSTARTED, "not started" }, - { WL_PROXD_E_INVALIDAVB, "invalid AVB" }, - { WL_PROXD_E_INCAPABLE, "incapable" }, - { WL_PROXD_E_MISMATCH, "mismatch"}, + { WL_PROXD_E_INVALIDMEAS, "invalid measurement" }, + { WL_PROXD_E_INCAPABLE, "incapable" }, + { WL_PROXD_E_MISMATCH, "mismatch"}, { WL_PROXD_E_DUP_SESSION, "dup session" }, { WL_PROXD_E_REMOTE_FAIL, "remote fail" }, - { WL_PROXD_E_REMOTE_INCAPABLE, "remote incapable" }, + { WL_PROXD_E_REMOTE_INCAPABLE, "remote incapable" }, { WL_PROXD_E_SCHED_FAIL, "sched failure" }, { WL_PROXD_E_PROTO, "protocol error" }, { WL_PROXD_E_EXPIRED, "expired" }, { WL_PROXD_E_TIMEOUT, "timeout" }, { WL_PROXD_E_NOACK, "no ack" }, - { WL_PROXD_E_DEFERRED, "deferred" }, + { WL_PROXD_E_DEFERRED, "deferred" }, { WL_PROXD_E_INVALID_SID, "invalid session id" }, { WL_PROXD_E_REMOTE_CANCEL, "remote cancel" }, - { WL_PROXD_E_CANCELED, "canceled" }, - { WL_PROXD_E_INVALID_SESSION, "invalid session" }, - { WL_PROXD_E_BAD_STATE, "bad state" }, + { WL_PROXD_E_CANCELED, "canceled" }, + { WL_PROXD_E_INVALID_SESSION, "invalid session" }, + { WL_PROXD_E_BAD_STATE, "bad state" }, { WL_PROXD_E_ERROR, "error" }, - { WL_PROXD_E_OK, "OK" } + { WL_PROXD_E_OK, "OK" } }; /* * time interval unit --> text string mapping */ static const ftm_strmap_entry_t ftm_tmu_value_loginfo[] = { - /* wl_proxd_tmu_t, text-string */ - { WL_PROXD_TMU_TU, "TU" }, - { WL_PROXD_TMU_SEC, "sec" }, + /* wl_proxd_tmu_t, text-string */ + { WL_PROXD_TMU_TU, "TU" }, + { WL_PROXD_TMU_SEC, "sec" }, { WL_PROXD_TMU_MILLI_SEC, "ms" }, { WL_PROXD_TMU_MICRO_SEC, "us" }, { WL_PROXD_TMU_NANO_SEC, "ns" }, { WL_PROXD_TMU_PICO_SEC, "ps" } }; - -#define RSPEC_BW(rspec) ((rspec) & WL_RSPEC_BW_MASK) -#define RSPEC_IS20MHZ(rspec) (RSPEC_BW(rspec) == WL_RSPEC_BW_20MHZ) -#define RSPEC_IS40MHZ(rspec) (RSPEC_BW(rspec) == WL_RSPEC_BW_40MHZ) -#define RSPEC_IS80MHZ(rspec) (RSPEC_BW(rspec) == WL_RSPEC_BW_80MHZ) -#define RSPEC_IS160MHZ(rspec) (RSPEC_BW(rspec) == WL_RSPEC_BW_160MHZ) - -#define IS_MCS(rspec) (((rspec) & WL_RSPEC_ENCODING_MASK) != WL_RSPEC_ENCODE_RATE) -#define IS_STBC(rspec) (((((rspec) & WL_RSPEC_ENCODING_MASK) == WL_RSPEC_ENCODE_HT) || \ - (((rspec) & WL_RSPEC_ENCODING_MASK) == WL_RSPEC_ENCODE_VHT)) && \ - (((rspec) & WL_RSPEC_STBC) == WL_RSPEC_STBC)) -#define RSPEC_ISSGI(rspec) (((rspec) & WL_RSPEC_SGI) != 0) -#define RSPEC_ISLDPC(rspec) (((rspec) & WL_RSPEC_LDPC) != 0) -#define RSPEC_ISSTBC(rspec) (((rspec) & WL_RSPEC_STBC) != 0) -#define RSPEC_ISTXBF(rspec) (((rspec) & WL_RSPEC_TXBF) != 0) -#define RSPEC_ISVHT(rspec) (((rspec) & WL_RSPEC_ENCODING_MASK) == WL_RSPEC_ENCODE_VHT) -#define RSPEC_ISHT(rspec) (((rspec) & WL_RSPEC_ENCODING_MASK) == WL_RSPEC_ENCODE_HT) -#define RSPEC_ISLEGACY(rspec) (((rspec) & WL_RSPEC_ENCODING_MASK) == WL_RSPEC_ENCODE_RATE) -#define RSPEC2RATE(rspec) (RSPEC_ISLEGACY(rspec) ? \ - ((rspec) & RSPEC_RATE_MASK) : rate_rspec2rate(rspec)) -/* return rate in unit of 500Kbps -- for internal use in wlc_rate_sel.c */ -#define RSPEC2KBPS(rspec) rate_rspec2rate(rspec) struct ieee_80211_mcs_rate_info { uint8 constellation_bits; @@ -449,10 +395,10 @@ } /* wlc_rate_mcs2rate */ /** take a well formed ratespec_t arg and return phy rate in [Kbps] units */ -int +static uint32 rate_rspec2rate(uint32 rspec) { - int rate = -1; + int rate = 0; if (RSPEC_ISLEGACY(rspec)) { rate = 500 * (rspec & WL_RSPEC_RATE_MASK); @@ -469,16 +415,17 @@ } else if (RSPEC_ISVHT(rspec)) { uint mcs = (rspec & WL_RSPEC_VHT_MCS_MASK); uint nss = (rspec & WL_RSPEC_VHT_NSS_MASK) >> WL_RSPEC_VHT_NSS_SHIFT; - - ASSERT(mcs <= 9); - ASSERT(nss <= 8); + if (mcs > 9 || nss > 8) { + DHD_RTT(("%s: Invalid mcs %d or nss %d\n", __FUNCTION__, mcs, nss)); + goto exit; + } rate = rate_mcs2rate(mcs, nss, RSPEC_BW(rspec), RSPEC_ISSGI(rspec)); } else { - ASSERT(0); + DHD_RTT(("%s: wrong rspec:%d\n", __FUNCTION__, rspec)); } - - return (rate == 0) ? -1 : rate; +exit: + return rate; } char resp_buf[WLC_IOCTL_SMLEN]; @@ -536,7 +483,7 @@ } p_entry++; /* next entry */ } - return RTT_REASON_FAILURE; /* not found */ + return RTT_STATUS_FAILURE; /* not found */ } /* * lookup 'id' (as a key) from a table @@ -578,9 +525,7 @@ return "invalid"; } - -#ifdef RTT_DEBUG - +#if defined(WL_CFG80211) && defined(RTT_DEBUG) /* define entry, e.g. { WL_PROXD_CMD_xxx, "WL_PROXD_CMD_xxx" } */ #define DEF_STRMAP_ENTRY(id) { (id), #id } @@ -613,13 +558,12 @@ /* * map a ftm cmd-id to a text-string for display */ -static const char * +const char * ftm_cmdid_to_str(uint16 cmdid) { return ftm_map_id_to_str((int32) cmdid, &ftm_cmdid_map[0], ARRAYSIZE(ftm_cmdid_map)); } -#endif /* RTT_DEBUG */ - +#endif /* WL_CFG80211 && RTT_DEBUG */ /* * convert BCME_xxx error codes into related error strings @@ -674,7 +618,7 @@ ARRAYSIZE(ftm_session_state_value_loginfo)); } - +#ifdef WL_CFG80211 /* * send 'proxd' iovar for all ftm get-related commands */ @@ -690,7 +634,7 @@ status = dhd_getiovar(dhd, 0, "proxd", (char *)p_proxd_iov, proxd_iovsize, (char **)&p_iovresp, WLC_IOCTL_SMLEN); if (status != BCME_OK) { - DHD_ERROR(("%s: failed to send getbuf proxd iovar (CMD ID : %d), status=%d\n", + DHD_RTT_ERR(("%s: failed to send getbuf proxd iovar (CMD ID : %d), status=%d\n", __FUNCTION__, p_subcmd_info->cmdid, status)); return status; } @@ -702,7 +646,7 @@ tlvs_len = ltoh16(p_iovresp->len) - WL_PROXD_IOV_HDR_SIZE; if (tlvs_len < 0) { - DHD_ERROR(("%s: alert, p_iovresp->len(%d) should not be smaller than %d\n", + DHD_RTT_ERR(("%s: alert, p_iovresp->len(%d) should not be smaller than %d\n", __FUNCTION__, ltoh16(p_iovresp->len), (int) WL_PROXD_IOV_HDR_SIZE)); tlvs_len = 0; } @@ -716,13 +660,12 @@ return status; } - static wl_proxd_iov_t * rtt_alloc_getset_buf(wl_proxd_method_t method, wl_proxd_session_id_t session_id, wl_proxd_cmd_t cmdid, uint16 tlvs_bufsize, uint16 *p_out_bufsize) { uint16 proxd_iovsize; - uint16 kflags; + uint32 kflags; wl_proxd_tlv_t *p_tlv; wl_proxd_iov_t *p_proxd_iov = (wl_proxd_iov_t *) NULL; @@ -733,7 +676,7 @@ p_proxd_iov = kzalloc(proxd_iovsize, kflags); if (p_proxd_iov == NULL) { - DHD_ERROR(("error: failed to allocate %d bytes of memory\n", proxd_iovsize)); + DHD_RTT_ERR(("error: failed to allocate %d bytes of memory\n", proxd_iovsize)); return NULL; } @@ -754,7 +697,6 @@ return p_proxd_iov; } - static int dhd_rtt_common_get_handler(dhd_pub_t *dhd, ftm_subcmd_info_t *p_subcmd_info, wl_proxd_method_t method, @@ -767,7 +709,7 @@ DHD_RTT(("enter %s: method=%d, session_id=%d, cmdid=%d(%s)\n", __FUNCTION__, method, session_id, p_subcmd_info->cmdid, ftm_cmdid_to_str(p_subcmd_info->cmdid))); -#endif +#endif // endif /* alloc mem for ioctl headr + reserved 0 bufsize for tlvs (initialize to zero) */ p_proxd_iov = rtt_alloc_getset_buf(method, session_id, p_subcmd_info->cmdid, 0, &proxd_iovsize); @@ -809,10 +751,11 @@ int ret; #ifdef RTT_DEBUG - DHD_ERROR(("enter %s: method=%d, session_id=%d, cmdid=%d(%s)\n", + DHD_RTT(("enter %s: method=%d, session_id=%d, cmdid=%d(%s)\n", __FUNCTION__, method, session_id, p_subcmd_info->cmdid, ftm_cmdid_to_str(p_subcmd_info->cmdid))); -#endif +#endif // endif + /* allocate and initialize a temp buffer for 'set proxd' iovar */ proxd_iovsize = 0; p_proxd_iov = rtt_alloc_getset_buf(method, session_id, p_subcmd_info->cmdid, @@ -824,38 +767,269 @@ ret = dhd_iovar(dhd, 0, "proxd", (char *)p_proxd_iov, proxd_iovsize, NULL, 0, TRUE); #ifdef RTT_DEBUG if (ret != BCME_OK) { - DHD_ERROR(("error: Proxd IOVAR failed, status=%d\n", ret)); + DHD_RTT(("error: IOVAR failed, status=%d\n", ret)); } -#endif +#endif // endif /* clean up */ kfree(p_proxd_iov); + + return ret; +} +#endif /* WL_CFG80211 */ + +/* gets the length and returns the version + * of the wl_proxd_collect_event_t version + */ +static uint +rtt_collect_data_event_ver(uint16 len) +{ + if (len > sizeof(wl_proxd_collect_event_data_v3_t)) { + return WL_PROXD_COLLECT_EVENT_DATA_VERSION_MAX; + } else if (len == sizeof(wl_proxd_collect_event_data_v3_t)) { + return WL_PROXD_COLLECT_EVENT_DATA_VERSION_3; + } else if (len == sizeof(wl_proxd_collect_event_data_v2_t)) { + return WL_PROXD_COLLECT_EVENT_DATA_VERSION_2; + } else { + return WL_PROXD_COLLECT_EVENT_DATA_VERSION_1; + } +} + +static void +rtt_collect_event_data_display(uint8 ver, void *ctx, const uint8 *p_data, uint16 len) +{ + int i; + wl_proxd_collect_event_data_v1_t *p_collect_data_v1 = NULL; + wl_proxd_collect_event_data_v2_t *p_collect_data_v2 = NULL; + wl_proxd_collect_event_data_v3_t *p_collect_data_v3 = NULL; + + if (!ctx || !p_data) { + return; + } + + switch (ver) { + case WL_PROXD_COLLECT_EVENT_DATA_VERSION_1: + DHD_RTT(("\tVERSION_1\n")); + memcpy(ctx, p_data, sizeof(wl_proxd_collect_event_data_v1_t)); + p_collect_data_v1 = (wl_proxd_collect_event_data_v1_t *)ctx; + DHD_RTT(("\tH_RX\n")); + for (i = 0; i < K_TOF_COLLECT_H_SIZE_20MHZ; i++) { + p_collect_data_v1->H_RX[i] = ltoh32_ua(&p_collect_data_v1->H_RX[i]); + DHD_RTT(("\t%u\n", p_collect_data_v1->H_RX[i])); + } + DHD_RTT(("\n")); + DHD_RTT(("\tH_LB\n")); + for (i = 0; i < K_TOF_COLLECT_H_SIZE_20MHZ; i++) { + p_collect_data_v1->H_LB[i] = ltoh32_ua(&p_collect_data_v1->H_LB[i]); + DHD_RTT(("\t%u\n", p_collect_data_v1->H_LB[i])); + } + DHD_RTT(("\n")); + DHD_RTT(("\tri_rr\n")); + for (i = 0; i < FTM_TPK_RI_RR_LEN; i++) { + DHD_RTT(("\t%u\n", p_collect_data_v1->ri_rr[i])); + } + p_collect_data_v1->phy_err_mask = ltoh32_ua(&p_collect_data_v1->phy_err_mask); + DHD_RTT(("\tphy_err_mask=0x%x\n", p_collect_data_v1->phy_err_mask)); + break; + case WL_PROXD_COLLECT_EVENT_DATA_VERSION_2: + memcpy(ctx, p_data, sizeof(wl_proxd_collect_event_data_v2_t)); + p_collect_data_v2 = (wl_proxd_collect_event_data_v2_t *)ctx; + DHD_RTT(("\tH_RX\n")); + for (i = 0; i < K_TOF_COLLECT_H_SIZE_20MHZ; i++) { + p_collect_data_v2->H_RX[i] = ltoh32_ua(&p_collect_data_v2->H_RX[i]); + DHD_RTT(("\t%u\n", p_collect_data_v2->H_RX[i])); + } + DHD_RTT(("\n")); + DHD_RTT(("\tH_LB\n")); + for (i = 0; i < K_TOF_COLLECT_H_SIZE_20MHZ; i++) { + p_collect_data_v2->H_LB[i] = ltoh32_ua(&p_collect_data_v2->H_LB[i]); + DHD_RTT(("\t%u\n", p_collect_data_v2->H_LB[i])); + } + DHD_RTT(("\n")); + DHD_RTT(("\tri_rr\n")); + for (i = 0; i < FTM_TPK_RI_RR_LEN_SECURE_2_0; i++) { + DHD_RTT(("\t%u\n", p_collect_data_v2->ri_rr[i])); + } + p_collect_data_v2->phy_err_mask = ltoh32_ua(&p_collect_data_v2->phy_err_mask); + DHD_RTT(("\tphy_err_mask=0x%x\n", p_collect_data_v2->phy_err_mask)); + break; + case WL_PROXD_COLLECT_EVENT_DATA_VERSION_3: + memcpy(ctx, p_data, sizeof(wl_proxd_collect_event_data_v3_t)); + p_collect_data_v3 = (wl_proxd_collect_event_data_v3_t *)ctx; + switch (p_collect_data_v3->version) { + case WL_PROXD_COLLECT_EVENT_DATA_VERSION_3: + if (p_collect_data_v3->length != + (len - OFFSETOF(wl_proxd_collect_event_data_v3_t, H_LB))) { + DHD_RTT(("\tversion/length mismatch\n")); + break; + } + DHD_RTT(("\tH_RX\n")); + for (i = 0; i < K_TOF_COLLECT_H_SIZE_20MHZ; i++) { + p_collect_data_v3->H_RX[i] = + ltoh32_ua(&p_collect_data_v3->H_RX[i]); + DHD_RTT(("\t%u\n", p_collect_data_v3->H_RX[i])); + } + DHD_RTT(("\n")); + DHD_RTT(("\tH_LB\n")); + for (i = 0; i < K_TOF_COLLECT_H_SIZE_20MHZ; i++) { + p_collect_data_v3->H_LB[i] = + ltoh32_ua(&p_collect_data_v3->H_LB[i]); + DHD_RTT(("\t%u\n", p_collect_data_v3->H_LB[i])); + } + DHD_RTT(("\n")); + DHD_RTT(("\tri_rr\n")); + for (i = 0; i < FTM_TPK_RI_RR_LEN_SECURE_2_0; i++) { + DHD_RTT(("\t%u\n", p_collect_data_v3->ri_rr[i])); + } + p_collect_data_v3->phy_err_mask = + ltoh32_ua(&p_collect_data_v3->phy_err_mask); + DHD_RTT(("\tphy_err_mask=0x%x\n", p_collect_data_v3->phy_err_mask)); + break; + /* future case */ + } + break; + } +} + +static uint16 +rtt_result_ver(uint16 tlvid, const uint8 *p_data) +{ + uint16 ret = BCME_OK; + const wl_proxd_rtt_result_v2_t *r_v2 = NULL; + + switch (tlvid) { + case WL_PROXD_TLV_ID_RTT_RESULT: + BCM_REFERENCE(p_data); + ret = WL_PROXD_RTT_RESULT_VERSION_1; + break; + case WL_PROXD_TLV_ID_RTT_RESULT_V2: + if (p_data) { + r_v2 = (const wl_proxd_rtt_result_v2_t *)p_data; + if (r_v2->version == WL_PROXD_RTT_RESULT_VERSION_2) { + ret = WL_PROXD_RTT_RESULT_VERSION_2; + } + } + break; + default: + DHD_RTT_ERR(("%s: > Unsupported TLV ID %d\n", + __FUNCTION__, tlvid)); + break; + } return ret; } +/* pretty hex print a contiguous buffer */ +static void +rtt_prhex(const char *msg, const uint8 *buf, uint nbytes) +{ + char line[128], *p; + int len = sizeof(line); + int nchar; + uint i; + + if (msg && (msg[0] != '\0')) + DHD_RTT(("%s:\n", msg)); + + p = line; + for (i = 0; i < nbytes; i++) { + if (i % 16 == 0) { + nchar = snprintf(p, len, " %04d: ", i); /* line prefix */ + p += nchar; + len -= nchar; + } + if (len > 0) { + nchar = snprintf(p, len, "%02x ", buf[i]); + p += nchar; + len -= nchar; + } + + if (i % 16 == 15) { + DHD_RTT(("%s\n", line)); /* flush line */ + p = line; + len = sizeof(line); + } + } + + /* flush last partial line */ + if (p != line) + DHD_RTT(("%s\n", line)); +} + static int -rtt_unpack_xtlv_cbfn(void *ctx, uint8 *p_data, uint16 tlvid, uint16 len) +rtt_unpack_xtlv_cbfn(void *ctx, const uint8 *p_data, uint16 tlvid, uint16 len) { int ret = BCME_OK; - wl_proxd_ftm_session_status_t *p_data_info; + int i; + wl_proxd_ftm_session_status_t *p_data_info = NULL; + uint32 chan_data_entry = 0; + uint16 expected_rtt_result_ver = 0; + + BCM_REFERENCE(p_data_info); + switch (tlvid) { case WL_PROXD_TLV_ID_RTT_RESULT: - ret = dhd_rtt_convert_results_to_host((rtt_report_t *)ctx, - p_data, tlvid, len); + case WL_PROXD_TLV_ID_RTT_RESULT_V2: + DHD_RTT(("WL_PROXD_TLV_ID_RTT_RESULT\n")); + expected_rtt_result_ver = rtt_result_ver(tlvid, p_data); + switch (expected_rtt_result_ver) { + case WL_PROXD_RTT_RESULT_VERSION_1: + ret = dhd_rtt_convert_results_to_host_v1((rtt_result_t *)ctx, + p_data, tlvid, len); + break; + case WL_PROXD_RTT_RESULT_VERSION_2: + ret = dhd_rtt_convert_results_to_host_v2((rtt_result_t *)ctx, + p_data, tlvid, len); + break; + default: + DHD_RTT_ERR((" > Unsupported RTT_RESULT version\n")); + ret = BCME_UNSUPPORTED; + break; + } break; case WL_PROXD_TLV_ID_SESSION_STATUS: + DHD_RTT(("WL_PROXD_TLV_ID_SESSION_STATUS\n")); memcpy(ctx, p_data, sizeof(wl_proxd_ftm_session_status_t)); p_data_info = (wl_proxd_ftm_session_status_t *)ctx; + p_data_info->sid = ltoh16_ua(&p_data_info->sid); p_data_info->state = ltoh16_ua(&p_data_info->state); p_data_info->status = ltoh32_ua(&p_data_info->status); + p_data_info->burst_num = ltoh16_ua(&p_data_info->burst_num); + DHD_RTT(("\tsid=%u, state=%d, status=%d, burst_num=%u\n", + p_data_info->sid, p_data_info->state, + p_data_info->status, p_data_info->burst_num)); + + break; + case WL_PROXD_TLV_ID_COLLECT_DATA: + DHD_RTT(("WL_PROXD_TLV_ID_COLLECT_DATA\n")); + rtt_collect_event_data_display( + rtt_collect_data_event_ver(len), + ctx, p_data, len); + break; + case WL_PROXD_TLV_ID_COLLECT_CHAN_DATA: + GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST(); + DHD_RTT(("WL_PROXD_TLV_ID_COLLECT_CHAN_DATA\n")); + DHD_RTT(("\tchan est %u\n", (uint32) (len / sizeof(uint32)))); + for (i = 0; i < (len/sizeof(chan_data_entry)); i++) { + uint32 *p = (uint32*)p_data; + chan_data_entry = ltoh32_ua(p + i); + DHD_RTT(("\t%u\n", chan_data_entry)); + } + GCC_DIAGNOSTIC_POP(); + break; + case WL_PROXD_TLV_ID_MF_STATS_DATA: + DHD_RTT(("WL_PROXD_TLV_ID_MF_STATS_DATA\n")); + DHD_RTT(("\tmf stats len=%u\n", len)); + rtt_prhex("", p_data, len); break; default: - DHD_ERROR(("> Unsupported TLV ID %d\n", tlvid)); + DHD_RTT_ERR(("> Unsupported TLV ID %d\n", tlvid)); ret = BCME_ERROR; break; } return ret; } + +#ifdef WL_CFG80211 static int rtt_handle_config_options(wl_proxd_session_id_t session_id, wl_proxd_tlv_t **p_tlv, uint16 *p_buf_space_left, ftm_config_options_info_t *ftm_configs, int ftm_cfg_cnt) @@ -885,9 +1059,9 @@ flags_mask = htol32(flags_mask); /* setup flags_mask TLV */ ret = bcm_pack_xtlv_entry((uint8 **)p_tlv, p_buf_space_left, - type, sizeof(uint32), &flags_mask, BCM_XTLV_OPTION_ALIGN32); + type, sizeof(uint32), (uint8 *)&flags_mask, BCM_XTLV_OPTION_ALIGN32); if (ret != BCME_OK) { - DHD_ERROR(("%s : bcm_pack_xltv_entry() for mask flags failed, status=%d\n", + DHD_RTT_ERR(("%s : bcm_pack_xltv_entry() for mask flags failed, status=%d\n", __FUNCTION__, ret)); goto exit; } @@ -896,12 +1070,12 @@ WL_PROXD_TLV_ID_FLAGS : WL_PROXD_TLV_ID_SESSION_FLAGS; /* setup flags TLV */ ret = bcm_pack_xtlv_entry((uint8 **)p_tlv, p_buf_space_left, - type, sizeof(uint32), &flags, BCM_XTLV_OPTION_ALIGN32); + type, sizeof(uint32), (uint8 *)&flags, BCM_XTLV_OPTION_ALIGN32); if (ret != BCME_OK) { -//#ifdef RTT_DEBUG +#ifdef RTT_DEBUG DHD_RTT(("%s: bcm_pack_xltv_entry() for flags failed, status=%d\n", __FUNCTION__, ret)); -//#endif +#endif // endif } exit: return ret; @@ -947,6 +1121,7 @@ break; case WL_PROXD_TLV_ID_BSSID: /* mac address */ case WL_PROXD_TLV_ID_PEER_MAC: + case WL_PROXD_TLV_ID_CUR_ETHER_ADDR: p_src_data = &p_config_param_info->mac_addr; src_data_size = sizeof(struct ether_addr); break; @@ -963,36 +1138,22 @@ break; } if (ret != BCME_OK) { - DHD_ERROR(("%s bad TLV ID : %d\n", + DHD_RTT_ERR(("%s bad TLV ID : %d\n", __FUNCTION__, p_config_param_info->tlvid)); break; } ret = bcm_pack_xtlv_entry((uint8 **) p_tlv, p_buf_space_left, - p_config_param_info->tlvid, src_data_size, p_src_data, + p_config_param_info->tlvid, src_data_size, (uint8 *)p_src_data, BCM_XTLV_OPTION_ALIGN32); if (ret != BCME_OK) { - DHD_ERROR(("%s: bcm_pack_xltv_entry() failed," + DHD_RTT_ERR(("%s: bcm_pack_xltv_entry() failed," " status=%d\n", __FUNCTION__, ret)); break; } } } - return ret; -} - -static int -dhd_rtt_get_version(dhd_pub_t *dhd, int *out_version) -{ - int ret; - ftm_subcmd_info_t subcmd_info; - subcmd_info.name = "ver"; - subcmd_info.cmdid = WL_PROXD_CMD_GET_VERSION; - subcmd_info.handler = NULL; - ret = dhd_rtt_common_get_handler(dhd, &subcmd_info, - WL_PROXD_METHOD_FTM, WL_PROXD_SESSION_ID_GLOBAL); - *out_version = (ret == BCME_OK) ? subcmd_info.version : 0; return ret; } @@ -1028,6 +1189,119 @@ return dhd_rtt_common_set_handler(dhd, &subcmd_info, WL_PROXD_METHOD_FTM, session_id); } +#ifdef WL_NAN +int +dhd_rtt_delete_nan_session(dhd_pub_t *dhd) +{ + struct net_device *dev = dhd_linux_get_primary_netdev(dhd); + struct wireless_dev *wdev = ndev_to_wdev(dev); + struct wiphy *wiphy = wdev->wiphy; + struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); + wl_cfgnan_terminate_directed_rtt_sessions(dev, cfg); + return BCME_OK; +} +#endif /* WL_NAN */ +/* API to find out if the given Peer Mac from FTM events +* is nan-peer. Based on this we will handle the SESSION_END +* event. For nan-peer FTM_SESSION_END event is ignored and handled in +* nan-ranging-cancel or nan-ranging-end event. +*/ +static bool +dhd_rtt_is_nan_peer(dhd_pub_t *dhd, struct ether_addr *peer_mac) +{ +#ifdef WL_NAN + struct net_device *dev = dhd_linux_get_primary_netdev(dhd); + struct wireless_dev *wdev = ndev_to_wdev(dev); + struct wiphy *wiphy = wdev->wiphy; + struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); + nan_ranging_inst_t *ranging_inst = NULL; + bool ret = FALSE; + + if (cfg->nan_enable == FALSE || ETHER_ISNULLADDR(peer_mac)) { + goto exit; + } + + ranging_inst = wl_cfgnan_check_for_ranging(cfg, peer_mac); + if (ranging_inst) { + DHD_RTT((" RTT peer is of type NAN\n")); + ret = TRUE; + goto exit; + } +exit: + return ret; +#else + return FALSE; +#endif /* WL_NAN */ +} + +#ifdef WL_NAN +static int +dhd_rtt_nan_start_session(dhd_pub_t *dhd, rtt_target_info_t *rtt_target) +{ + s32 err = BCME_OK; + struct net_device *dev = dhd_linux_get_primary_netdev(dhd); + struct wireless_dev *wdev = ndev_to_wdev(dev); + struct wiphy *wiphy = wdev->wiphy; + struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); + wl_nan_ev_rng_rpt_ind_t range_res; + nan_ranging_inst_t *ranging_inst = NULL; + rtt_status_info_t *rtt_status = GET_RTTSTATE(dhd); + + NAN_MUTEX_LOCK(); + + bzero(&range_res, sizeof(range_res)); + + if (!rtt_status) { + err = BCME_NOTENABLED; + goto done; + } + + if (!cfg->nan_enable) { /* If nan is not enabled report error */ + err = BCME_NOTENABLED; + goto done; + } + + /* check if new ranging session allowed */ + if (!wl_cfgnan_ranging_allowed(cfg)) { + /* responder should be in progress because initiator requests are + * queued in DHD. Since initiator has more proef cancel responder + * sessions + */ + wl_cfgnan_cancel_rng_responders(dev, cfg); + } + + ranging_inst = wl_cfgnan_get_ranging_inst(cfg, + &rtt_target->addr, NAN_RANGING_ROLE_INITIATOR); + if (!ranging_inst) { + err = BCME_NORESOURCE; + goto done; + } + + DHD_RTT(("Trigger nan based range request\n")); + err = wl_cfgnan_trigger_ranging(bcmcfg_to_prmry_ndev(cfg), + cfg, ranging_inst, NULL, NAN_RANGE_REQ_CMD, TRUE); + if (unlikely(err)) { + goto done; + } + ranging_inst->range_type = RTT_TYPE_NAN_DIRECTED; + ranging_inst->range_role = NAN_RANGING_ROLE_INITIATOR; + /* schedule proxd timeout */ + schedule_delayed_work(&rtt_status->proxd_timeout, + msecs_to_jiffies(DHD_NAN_RTT_TIMER_INTERVAL_MS)); +done: + if (err) { /* notify failure RTT event to host */ + DHD_RTT_ERR(("Failed to issue Nan Ranging Request err %d\n", err)); + dhd_rtt_handle_nan_rtt_session_end(dhd, &rtt_target->addr); + /* try to reset geofence */ + if (ranging_inst) { + wl_cfgnan_reset_geofence_ranging(cfg, ranging_inst, + RTT_SCHED_DIR_TRIGGER_FAIL); + } + } + NAN_MUTEX_UNLOCK(); + return err; +} +#endif /* WL_NAN */ static int dhd_rtt_ftm_config(dhd_pub_t *dhd, wl_proxd_session_id_t session_id, @@ -1042,6 +1316,7 @@ uint16 buf_space_left; uint16 all_tlvsize; int ret = BCME_OK; + subcmd_info.name = "config"; subcmd_info.cmdid = WL_PROXD_CMD_CONFIG; @@ -1049,7 +1324,7 @@ FTM_IOC_BUFSZ, &proxd_iovsize); if (p_proxd_iov == NULL) { - DHD_ERROR(("%s : failed to allocate the iovar (size :%d)\n", + DHD_RTT_ERR(("%s : failed to allocate the iovar (size :%d)\n", __FUNCTION__, FTM_IOC_BUFSZ)); return BCME_NOMEM; } @@ -1072,13 +1347,29 @@ ret = dhd_iovar(dhd, 0, "proxd", (char *)p_proxd_iov, all_tlvsize + WL_PROXD_IOV_HDR_SIZE, NULL, 0, TRUE); if (ret != BCME_OK) { - DHD_ERROR(("%s : failed to set config \n", __FUNCTION__)); + DHD_RTT_ERR(("%s : failed to set config\n", __FUNCTION__)); } } /* clean up */ kfree(p_proxd_iov); return ret; } + +static int +dhd_rtt_get_version(dhd_pub_t *dhd, int *out_version) +{ + int ret; + ftm_subcmd_info_t subcmd_info; + subcmd_info.name = "ver"; + subcmd_info.cmdid = WL_PROXD_CMD_GET_VERSION; + subcmd_info.handler = NULL; + ret = dhd_rtt_common_get_handler(dhd, &subcmd_info, + WL_PROXD_METHOD_FTM, WL_PROXD_SESSION_ID_GLOBAL); + *out_version = (ret == BCME_OK) ? subcmd_info.version : 0; + return ret; +} +#endif /* WL_CFG80211 */ + chanspec_t dhd_rtt_convert_to_chspec(wifi_channel_info_t channel) { @@ -1108,7 +1399,7 @@ chanspec = wf_chspec_80(center_chan, primary_chan); break; default: - DHD_ERROR(("doesn't support this bandwith : %d", channel.width)); + DHD_RTT_ERR(("doesn't support this bandwith : %d", channel.width)); bw = -1; break; } @@ -1129,27 +1420,50 @@ { int err = BCME_OK; int idx; - rtt_status_info_t *rtt_status; - NULL_CHECK(params, "params is NULL", err); + rtt_status_info_t *rtt_status = NULL; + struct net_device *dev = NULL; + NULL_CHECK(params, "params is NULL", err); NULL_CHECK(dhd, "dhd is NULL", err); + + dev = dhd_linux_get_primary_netdev(dhd); rtt_status = GET_RTTSTATE(dhd); NULL_CHECK(rtt_status, "rtt_status is NULL", err); + NULL_CHECK(dev, "dev is NULL", err); + if (!HAS_11MC_CAP(rtt_status->rtt_capa.proto)) { - DHD_ERROR(("doesn't support RTT \n")); - return BCME_ERROR; + DHD_RTT_ERR(("doesn't support RTT \n")); + err = BCME_ERROR; + goto exit; } - if (rtt_status->status != RTT_STOPPED) { - DHD_ERROR(("rtt is already started\n")); - return BCME_BUSY; - } + DHD_RTT(("%s enter\n", __FUNCTION__)); + if (params->rtt_target_cnt > 0) { +#ifdef WL_NAN + /* cancel ongoing geofence RTT if there */ + if ((err = wl_cfgnan_suspend_geofence_rng_session(dev, + NULL, RTT_GEO_SUSPN_HOST_DIR_RTT_TRIG, 0)) != BCME_OK) { + goto exit; + } +#endif /* WL_NAN */ + } else { + err = BCME_BADARG; + goto exit; + } + + mutex_lock(&rtt_status->rtt_mutex); + if (rtt_status->status != RTT_STOPPED) { + DHD_RTT_ERR(("rtt is already started\n")); + err = BCME_BUSY; + goto exit; + } memset(rtt_status->rtt_config.target_info, 0, TARGET_INFO_SIZE(RTT_MAX_TARGET_CNT)); rtt_status->rtt_config.rtt_target_cnt = params->rtt_target_cnt; memcpy(rtt_status->rtt_config.target_info, params->target_info, TARGET_INFO_SIZE(params->rtt_target_cnt)); rtt_status->status = RTT_STARTED; + DHD_RTT_MEM(("dhd_rtt_set_cfg: RTT Started, target_cnt = %d\n", params->rtt_target_cnt)); /* start to measure RTT from first device */ /* find next target to trigger RTT */ for (idx = rtt_status->cur_idx; idx < rtt_status->rtt_config.rtt_target_cnt; idx++) { @@ -1164,15 +1478,511 @@ } if (idx < rtt_status->rtt_config.rtt_target_cnt) { DHD_RTT(("rtt_status->cur_idx : %d\n", rtt_status->cur_idx)); + rtt_status->rtt_sched_reason = RTT_SCHED_HOST_TRIGGER; schedule_work(&rtt_status->work); } +exit: + mutex_unlock(&rtt_status->rtt_mutex); return err; +} + +#define GEOFENCE_RTT_LOCK(rtt_status) mutex_lock(&(rtt_status)->geofence_mutex) +#define GEOFENCE_RTT_UNLOCK(rtt_status) mutex_unlock(&(rtt_status)->geofence_mutex) + +#ifdef WL_NAN +/* sets geofence role concurrency state TRUE/FALSE */ +void +dhd_rtt_set_role_concurrency_state(dhd_pub_t *dhd, bool state) +{ + rtt_status_info_t *rtt_status = GET_RTTSTATE(dhd); + if (!rtt_status) { + return; + } + GEOFENCE_RTT_LOCK(rtt_status); + rtt_status->geofence_cfg.role_concurr_state = state; + GEOFENCE_RTT_UNLOCK(rtt_status); +} + +/* returns TRUE if geofence role concurrency constraint exists */ +bool +dhd_rtt_get_role_concurrency_state(dhd_pub_t *dhd) +{ + rtt_status_info_t *rtt_status = GET_RTTSTATE(dhd); + if (!rtt_status) { + return FALSE; + } + return rtt_status->geofence_cfg.role_concurr_state; +} + +int8 +dhd_rtt_get_geofence_target_cnt(dhd_pub_t *dhd) +{ + rtt_status_info_t *rtt_status = GET_RTTSTATE(dhd); + if (!rtt_status) { + return 0; + } + return rtt_status->geofence_cfg.geofence_target_cnt; +} + +/* sets geofence rtt state TRUE/FALSE */ +void +dhd_rtt_set_geofence_rtt_state(dhd_pub_t *dhd, bool state) +{ + rtt_status_info_t *rtt_status = GET_RTTSTATE(dhd); + if (!rtt_status) { + return; + } + GEOFENCE_RTT_LOCK(rtt_status); + rtt_status->geofence_cfg.rtt_in_progress = state; + GEOFENCE_RTT_UNLOCK(rtt_status); +} + +/* returns TRUE if geofence rtt is in progress */ +bool +dhd_rtt_get_geofence_rtt_state(dhd_pub_t *dhd) +{ + rtt_status_info_t *rtt_status = GET_RTTSTATE(dhd); + + if (!rtt_status) { + return FALSE; + } + + return rtt_status->geofence_cfg.rtt_in_progress; +} + +/* returns geofence RTT target list Head */ +rtt_geofence_target_info_t* +dhd_rtt_get_geofence_target_head(dhd_pub_t *dhd) +{ + rtt_status_info_t *rtt_status = GET_RTTSTATE(dhd); + rtt_geofence_target_info_t* head = NULL; + + if (!rtt_status) { + return NULL; + } + + if (rtt_status->geofence_cfg.geofence_target_cnt) { + head = &rtt_status->geofence_cfg.geofence_target_info[0]; + } + + return head; +} + +int8 +dhd_rtt_get_geofence_cur_target_idx(dhd_pub_t *dhd) +{ + int8 target_cnt = 0, cur_idx = DHD_RTT_INVALID_TARGET_INDEX; + rtt_status_info_t *rtt_status = GET_RTTSTATE(dhd); + + if (!rtt_status) { + goto exit; + } + + target_cnt = rtt_status->geofence_cfg.geofence_target_cnt; + if (target_cnt == 0) { + goto exit; + } + + cur_idx = rtt_status->geofence_cfg.cur_target_idx; + ASSERT(cur_idx <= target_cnt); + +exit: + return cur_idx; +} + +void +dhd_rtt_move_geofence_cur_target_idx_to_next(dhd_pub_t *dhd) +{ + rtt_status_info_t *rtt_status = GET_RTTSTATE(dhd); + + if (!rtt_status) { + return; + } + + if (rtt_status->geofence_cfg.geofence_target_cnt == 0) { + /* Invalidate current idx if no targets */ + rtt_status->geofence_cfg.cur_target_idx = + DHD_RTT_INVALID_TARGET_INDEX; + /* Cancel pending retry timer if any */ + if (delayed_work_pending(&rtt_status->rtt_retry_timer)) { + cancel_delayed_work(&rtt_status->rtt_retry_timer); + } + return; + } + rtt_status->geofence_cfg.cur_target_idx++; + + if (rtt_status->geofence_cfg.cur_target_idx >= + rtt_status->geofence_cfg.geofence_target_cnt) { + /* Reset once all targets done */ + rtt_status->geofence_cfg.cur_target_idx = 0; + } +} + +/* returns geofence current RTT target */ +rtt_geofence_target_info_t* +dhd_rtt_get_geofence_current_target(dhd_pub_t *dhd) +{ + rtt_status_info_t *rtt_status = GET_RTTSTATE(dhd); + rtt_geofence_target_info_t* cur_target = NULL; + int cur_idx = 0; + + if (!rtt_status) { + return NULL; + } + + cur_idx = dhd_rtt_get_geofence_cur_target_idx(dhd); + if (cur_idx >= 0) { + cur_target = &rtt_status->geofence_cfg.geofence_target_info[cur_idx]; + } + + return cur_target; +} + +/* returns geofence target from list for the peer */ +rtt_geofence_target_info_t* +dhd_rtt_get_geofence_target(dhd_pub_t *dhd, struct ether_addr* peer_addr, int8 *index) +{ + int8 i; + rtt_status_info_t *rtt_status; + int target_cnt; + rtt_geofence_target_info_t *geofence_target_info, *tgt = NULL; + + rtt_status = GET_RTTSTATE(dhd); + + if (!rtt_status) { + return NULL; + } + + target_cnt = rtt_status->geofence_cfg.geofence_target_cnt; + geofence_target_info = rtt_status->geofence_cfg.geofence_target_info; + + /* Loop through to find target */ + for (i = 0; i < target_cnt; i++) { + if (geofence_target_info[i].valid == FALSE) { + break; + } + if (!memcmp(peer_addr, &geofence_target_info[i].peer_addr, + ETHER_ADDR_LEN)) { + *index = i; + tgt = &geofence_target_info[i]; + } + } + if (!tgt) { + DHD_RTT(("dhd_rtt_get_geofence_target: Target not found in list," + " MAC ADDR: "MACDBG" \n", MAC2STRDBG(peer_addr))); + } + return tgt; +} + +/* add geofence target to the target list */ +int +dhd_rtt_add_geofence_target(dhd_pub_t *dhd, rtt_geofence_target_info_t *target) +{ + int err = BCME_OK; + rtt_status_info_t *rtt_status; + rtt_geofence_target_info_t *geofence_target_info; + int8 geofence_target_cnt, index; + + NULL_CHECK(dhd, "dhd is NULL", err); + rtt_status = GET_RTTSTATE(dhd); + NULL_CHECK(rtt_status, "rtt_status is NULL", err); + + GEOFENCE_RTT_LOCK(rtt_status); + + /* Get the geofence_target via peer addr, index param is dumm here */ + geofence_target_info = dhd_rtt_get_geofence_target(dhd, &target->peer_addr, &index); + if (geofence_target_info) { + DHD_RTT(("Duplicate geofencing RTT add request dropped\n")); + err = BCME_OK; + goto exit; + } + + geofence_target_cnt = rtt_status->geofence_cfg.geofence_target_cnt; + if (geofence_target_cnt >= RTT_MAX_GEOFENCE_TARGET_CNT) { + DHD_RTT(("Queue full, Geofencing RTT add request dropped\n")); + err = BCME_NORESOURCE; + goto exit; + } + + /* Add Geofence RTT request and increment target count */ + geofence_target_info = rtt_status->geofence_cfg.geofence_target_info; + /* src and dest buffer len same, pointers of same DS statically allocated */ + (void)memcpy_s(&geofence_target_info[geofence_target_cnt], + sizeof(geofence_target_info[geofence_target_cnt]), target, + sizeof(*target)); + geofence_target_info[geofence_target_cnt].valid = TRUE; + rtt_status->geofence_cfg.geofence_target_cnt++; + if (rtt_status->geofence_cfg.geofence_target_cnt == 1) { + /* Adding first target */ + rtt_status->geofence_cfg.cur_target_idx = 0; + } + +exit: + GEOFENCE_RTT_UNLOCK(rtt_status); + return err; +} + +/* removes geofence target from the target list */ +int +dhd_rtt_remove_geofence_target(dhd_pub_t *dhd, struct ether_addr *peer_addr) +{ + int err = BCME_OK; + rtt_status_info_t *rtt_status; + rtt_geofence_target_info_t *geofence_target_info; + int8 geofence_target_cnt, j, index = 0; + + NULL_CHECK(dhd, "dhd is NULL", err); + rtt_status = GET_RTTSTATE(dhd); + NULL_CHECK(rtt_status, "rtt_status is NULL", err); + + GEOFENCE_RTT_LOCK(rtt_status); + + geofence_target_cnt = dhd_rtt_get_geofence_target_cnt(dhd); + if (geofence_target_cnt == 0) { + DHD_RTT(("Queue Empty, Geofencing RTT remove request dropped\n")); + ASSERT(0); + goto exit; + } + + /* Get the geofence_target via peer addr */ + geofence_target_info = dhd_rtt_get_geofence_target(dhd, peer_addr, &index); + if (geofence_target_info == NULL) { + DHD_RTT(("Geofencing RTT target not found, remove request dropped\n")); + err = BCME_NOTFOUND; + goto exit; + } + + /* left shift all the valid entries, as we dont keep holes in list */ + for (j = index; (j+1) < geofence_target_cnt; j++) { + if (geofence_target_info[j].valid == TRUE) { + /* + * src and dest buffer len same, pointers of same DS + * statically allocated + */ + (void)memcpy_s(&geofence_target_info[j], sizeof(geofence_target_info[j]), + &geofence_target_info[j + 1], + sizeof(geofence_target_info[j + 1])); + } else { + break; + } + } + rtt_status->geofence_cfg.geofence_target_cnt--; + if ((rtt_status->geofence_cfg.geofence_target_cnt == 0) || + (index == rtt_status->geofence_cfg.cur_target_idx)) { + /* Move cur_idx to next target */ + dhd_rtt_move_geofence_cur_target_idx_to_next(dhd); + } else if (index < rtt_status->geofence_cfg.cur_target_idx) { + /* Decrement cur index if cur target position changed */ + rtt_status->geofence_cfg.cur_target_idx--; + } + +exit: + GEOFENCE_RTT_UNLOCK(rtt_status); + return err; +} + +/* deletes/empty geofence target list */ +int +dhd_rtt_delete_geofence_target_list(dhd_pub_t *dhd) +{ + rtt_status_info_t *rtt_status; + + int err = BCME_OK; + + NULL_CHECK(dhd, "dhd is NULL", err); + rtt_status = GET_RTTSTATE(dhd); + NULL_CHECK(rtt_status, "rtt_status is NULL", err); + GEOFENCE_RTT_LOCK(rtt_status); + memset_s(&rtt_status->geofence_cfg, sizeof(rtt_geofence_cfg_t), + 0, sizeof(rtt_geofence_cfg_t)); + GEOFENCE_RTT_UNLOCK(rtt_status); + return err; +} + +int +dhd_rtt_sched_geofencing_target(dhd_pub_t *dhd) +{ + rtt_geofence_target_info_t *geofence_target_info; + struct net_device *dev = dhd_linux_get_primary_netdev(dhd); + int ret = BCME_OK; + bool geofence_state; + bool role_concurrency_state; + u8 rtt_invalid_reason = RTT_STATE_VALID; + struct bcm_cfg80211 *cfg = wl_get_cfg(dev); + rtt_status_info_t *rtt_status = GET_RTTSTATE(dhd); + + NAN_MUTEX_LOCK(); + + if ((cfg->nan_init_state == FALSE) || + (cfg->nan_enable == FALSE)) { + ret = BCME_NOTENABLED; + goto done; + } + geofence_state = dhd_rtt_get_geofence_rtt_state(dhd); + role_concurrency_state = dhd_rtt_get_role_concurrency_state(dhd); + + DHD_RTT_ERR(("dhd_rtt_sched_geofencing_target: sched_reason = %d\n", + rtt_status->rtt_sched_reason)); + + if (geofence_state == TRUE || role_concurrency_state == TRUE) { + ret = BCME_ERROR; + DHD_RTT_ERR(("geofencing constraint , sched request dropped," + " geofence_state = %d, role_concurrency_state = %d\n", + geofence_state, role_concurrency_state)); + goto done; + } + + /* Get current geofencing target */ + geofence_target_info = dhd_rtt_get_geofence_current_target(dhd); + + /* call cfg API for trigerring geofencing RTT */ + if (geofence_target_info) { + /* check for dp/others concurrency */ + rtt_invalid_reason = dhd_rtt_invalid_states(dev, + &geofence_target_info->peer_addr); + if (rtt_invalid_reason != RTT_STATE_VALID) { + ret = BCME_BUSY; + DHD_RTT_ERR(("DRV State is not valid for RTT, " + "invalid_state = %d\n", rtt_invalid_reason)); + goto done; + } + + ret = wl_cfgnan_trigger_geofencing_ranging(dev, + &geofence_target_info->peer_addr); + if (ret == BCME_OK) { + dhd_rtt_set_geofence_rtt_state(dhd, TRUE); + } + } else { + DHD_RTT(("No RTT target to schedule\n")); + ret = BCME_NOTFOUND; + } + +done: + NAN_MUTEX_UNLOCK(); + return ret; +} +#endif /* WL_NAN */ + +#ifdef WL_CFG80211 +#ifdef WL_NAN +static void +dhd_rtt_retry(dhd_pub_t *dhd) +{ + struct net_device *dev = dhd_linux_get_primary_netdev(dhd); + struct bcm_cfg80211 *cfg = wl_get_cfg(dev); + rtt_geofence_target_info_t *geofence_target = NULL; + nan_ranging_inst_t *ranging_inst = NULL; + + geofence_target = dhd_rtt_get_geofence_current_target(dhd); + if (!geofence_target) { + DHD_RTT(("dhd_rtt_retry: geofence target null\n")); + goto exit; + } + ranging_inst = wl_cfgnan_get_ranging_inst(cfg, + &geofence_target->peer_addr, NAN_RANGING_ROLE_INITIATOR); + if (!ranging_inst) { + DHD_RTT(("dhd_rtt_retry: ranging instance null\n")); + goto exit; + } + wl_cfgnan_reset_geofence_ranging(cfg, + ranging_inst, RTT_SCHED_RTT_RETRY_GEOFENCE); + +exit: + return; +} + +static void +dhd_rtt_retry_work(struct work_struct *work) +{ + rtt_status_info_t *rtt_status = NULL; + dhd_pub_t *dhd = NULL; + struct net_device *dev = NULL; + struct bcm_cfg80211 *cfg = NULL; + +#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif // endif + rtt_status = container_of(work, rtt_status_info_t, proxd_timeout.work); +#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) +#pragma GCC diagnostic pop +#endif // endif + + dhd = rtt_status->dhd; + if (dhd == NULL) { + DHD_RTT_ERR(("%s : dhd is NULL\n", __FUNCTION__)); + goto exit; + } + dev = dhd_linux_get_primary_netdev(dhd); + cfg = wl_get_cfg(dev); + + NAN_MUTEX_LOCK(); + mutex_lock(&rtt_status->rtt_mutex); + (void) dhd_rtt_retry(dhd); + mutex_unlock(&rtt_status->rtt_mutex); + NAN_MUTEX_UNLOCK(); + +exit: + return; +} +#endif /* WL_NAN */ + +/* + * Return zero (0) + * for valid RTT state + * means if RTT is applicable + */ +uint8 +dhd_rtt_invalid_states(struct net_device *ndev, struct ether_addr *peer_addr) +{ + uint8 invalid_reason = RTT_STATE_VALID; + struct bcm_cfg80211 *cfg = wl_get_cfg(ndev); + + UNUSED_PARAMETER(cfg); + UNUSED_PARAMETER(invalid_reason); + + /* Make sure peer addr is not NULL in caller */ + ASSERT(peer_addr); + /* + * Keep adding prohibited drv states here + * Only generic conditions which block + * All RTTs like NDP connection + */ + +#ifdef WL_NAN + if (wl_cfgnan_data_dp_exists_with_peer(cfg, peer_addr)) { + invalid_reason = RTT_STATE_INV_REASON_NDP_EXIST; + DHD_RTT(("NDP in progress/connected, RTT prohibited\n")); + goto exit; + } +#endif /* WL_NAN */ + + /* Remove below #defines once more exit calls come */ +#ifdef WL_NAN +exit: +#endif /* WL_NAN */ + return invalid_reason; +} +#endif /* WL_CFG80211 */ + +void +dhd_rtt_schedule_rtt_work_thread(dhd_pub_t *dhd, int sched_reason) +{ + rtt_status_info_t *rtt_status = GET_RTTSTATE(dhd); + if (rtt_status == NULL) { + ASSERT(0); + } else { + rtt_status->rtt_sched_reason = sched_reason; + schedule_work(&rtt_status->work); + } + return; } int dhd_rtt_stop(dhd_pub_t *dhd, struct ether_addr *mac_list, int mac_cnt) { int err = BCME_OK; +#ifdef WL_CFG80211 int i = 0, j = 0; rtt_status_info_t *rtt_status; rtt_results_header_t *entry, *next; @@ -1183,7 +1993,7 @@ rtt_status = GET_RTTSTATE(dhd); NULL_CHECK(rtt_status, "rtt_status is NULL", err); if (rtt_status->status == RTT_STOPPED) { - DHD_ERROR(("rtt is not started\n")); + DHD_RTT_ERR(("rtt is not started\n")); return BCME_OK; } DHD_RTT(("%s enter\n", __FUNCTION__)); @@ -1203,6 +2013,7 @@ /* remove the rtt results in cache */ if (!list_empty(&rtt_status->rtt_results_cache)) { /* Iterate rtt_results_header list */ + GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST(); list_for_each_entry_safe(entry, next, &rtt_status->rtt_results_cache, list) { list_del(&entry->list); @@ -1214,12 +2025,14 @@ } kfree(entry); } + GCC_DIAGNOSTIC_POP(); } /* send the rtt complete event to wake up the user process */ + GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST(); list_for_each_entry(iter, &rtt_status->noti_fn_list, list) { + GCC_DIAGNOSTIC_POP(); iter->noti_fn(iter->ctx, &rtt_status->rtt_results_cache); } - /* reinitialize the HEAD */ INIT_LIST_HEAD(&rtt_status->rtt_results_cache); /* clear information for rtt_config */ @@ -1227,20 +2040,128 @@ memset(rtt_status->rtt_config.target_info, 0, TARGET_INFO_SIZE(RTT_MAX_TARGET_CNT)); rtt_status->cur_idx = 0; + /* Cancel pending proxd timeout work if any */ + if (delayed_work_pending(&rtt_status->proxd_timeout)) { + cancel_delayed_work(&rtt_status->proxd_timeout); + } dhd_rtt_delete_session(dhd, FTM_DEFAULT_SESSION); +#ifdef WL_NAN + dhd_rtt_delete_nan_session(dhd); +#endif /* WL_NAN */ dhd_rtt_ftm_enable(dhd, FALSE); } mutex_unlock(&rtt_status->rtt_mutex); +#endif /* WL_CFG80211 */ return err; +} + +#ifdef WL_CFG80211 +static void +dhd_rtt_timeout(dhd_pub_t *dhd) +{ + rtt_status_info_t *rtt_status; +#ifndef DHD_DUMP_ON_RTT_TIMEOUT + rtt_target_info_t *rtt_target = NULL; + rtt_target_info_t *rtt_target_info = NULL; +#ifdef WL_NAN + nan_ranging_inst_t *ranging_inst = NULL; + int ret = BCME_OK; + uint32 status; + struct net_device *ndev = dhd_linux_get_primary_netdev(dhd); + struct bcm_cfg80211 *cfg = wiphy_priv(ndev->ieee80211_ptr->wiphy); +#endif /* WL_NAN */ +#endif /* !DHD_DUMP_ON_RTT_TIMEOUT */ + + rtt_status = GET_RTTSTATE(dhd); + if (!rtt_status) { + DHD_RTT_ERR(("Proxd timer expired but no RTT status\n")); + goto exit; + } + + if (RTT_IS_STOPPED(rtt_status)) { + DHD_RTT_ERR(("Proxd timer expired but no RTT Request\n")); + goto exit; + } + +#ifdef DHD_DUMP_ON_RTT_TIMEOUT + /* Dump, and Panic depending on memdump.info */ + if (dhd_query_bus_erros(dhd)) { + goto exit; + } +#ifdef DHD_FW_COREDUMP + if (dhd->memdump_enabled) { + /* Behave based on user memdump info */ + dhd->memdump_type = DUMP_TYPE_PROXD_TIMEOUT; + dhd_bus_mem_dump(dhd); + } +#endif /* DHD_FW_COREDUMP */ +#else /* DHD_DUMP_ON_RTT_TIMEOUT */ + /* Cancel RTT for target and proceed to next target */ + rtt_target_info = rtt_status->rtt_config.target_info; + if ((!rtt_target_info) || + (rtt_status->cur_idx >= rtt_status->rtt_config.rtt_target_cnt)) { + goto exit; + } + rtt_target = &rtt_target_info[rtt_status->cur_idx]; + WL_ERR(("Proxd timer expired for Target: "MACDBG" \n", MAC2STRDBG(&rtt_target->addr))); +#ifdef WL_NAN + if (rtt_target->peer == RTT_PEER_NAN) { + ranging_inst = wl_cfgnan_check_for_ranging(cfg, &rtt_target->addr); + if (!ranging_inst) { + goto exit; + } + ret = wl_cfgnan_cancel_ranging(ndev, cfg, ranging_inst->range_id, + NAN_RNG_TERM_FLAG_IMMEDIATE, &status); + if (unlikely(ret) || unlikely(status)) { + WL_ERR(("%s:nan range cancel failed ret = %d status = %d\n", + __FUNCTION__, ret, status)); + } + } else +#endif /* WL_NAN */ + { + /* For Legacy RTT */ + dhd_rtt_delete_session(dhd, FTM_DEFAULT_SESSION); + } + dhd_rtt_create_failure_result(rtt_status, &rtt_target->addr); + dhd_rtt_handle_rtt_session_end(dhd); +#endif /* DHD_DUMP_ON_RTT_TIMEOUT */ +exit: + return; +} + +static void +dhd_rtt_timeout_work(struct work_struct *work) +{ + rtt_status_info_t *rtt_status = NULL; + dhd_pub_t *dhd = NULL; + +#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif // endif + rtt_status = container_of(work, rtt_status_info_t, proxd_timeout.work); +#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) +#pragma GCC diagnostic pop +#endif // endif + + dhd = rtt_status->dhd; + if (dhd == NULL) { + DHD_RTT_ERR(("%s : dhd is NULL\n", __FUNCTION__)); + return; + } + mutex_lock(&rtt_status->rtt_mutex); + (void) dhd_rtt_timeout(dhd); + mutex_unlock(&rtt_status->rtt_mutex); } static int dhd_rtt_start(dhd_pub_t *dhd) { int err = BCME_OK; + int err_at = 0; char eabuf[ETHER_ADDR_STR_LEN]; char chanbuf[CHANSPEC_STR_LEN]; - int mpc = 0; + int pm = PM_OFF; int ftm_cfg_cnt = 0; int ftm_param_cnt = 0; uint32 rspec = 0; @@ -1248,39 +2169,86 @@ ftm_config_param_info_t ftm_params[FTM_MAX_PARAMS]; rtt_target_info_t *rtt_target; rtt_status_info_t *rtt_status; + struct net_device *dev = dhd_linux_get_primary_netdev(dhd); + u8 ioctl_buf[WLC_IOCTL_SMLEN]; + u8 rtt_invalid_reason = RTT_STATE_VALID; + int rtt_sched_type = RTT_TYPE_INVALID; + NULL_CHECK(dhd, "dhd is NULL", err); rtt_status = GET_RTTSTATE(dhd); NULL_CHECK(rtt_status, "rtt_status is NULL", err); + + DHD_RTT(("Enter %s\n", __FUNCTION__)); + + if (RTT_IS_STOPPED(rtt_status)) { + DHD_RTT(("No Directed RTT target to process, check for geofence\n")); + goto geofence; + } + if (rtt_status->cur_idx >= rtt_status->rtt_config.rtt_target_cnt) { err = BCME_RANGE; - DHD_ERROR(("%s : idx %d is out of range\n", __FUNCTION__, rtt_status->cur_idx)); - goto exit; - } - if (RTT_IS_STOPPED(rtt_status)) { - DHD_ERROR(("RTT is stopped\n")); - goto exit; - } - /* turn off mpc in case of non-associted */ - if (!dhd_is_associated(dhd, NULL, NULL)) { - err = dhd_iovar(dhd, 0, "mpc", (char *)&mpc, sizeof(mpc), NULL, 0, TRUE); - if (err) { - DHD_ERROR(("%s : failed to set mpc\n", __FUNCTION__)); - goto exit; + err_at = 1; + DHD_RTT(("%s : idx %d is out of range\n", __FUNCTION__, rtt_status->cur_idx)); + if (rtt_status->flags == WL_PROXD_SESSION_FLAG_TARGET) { + DHD_RTT_ERR(("STA is set as Target/Responder \n")); + err = BCME_ERROR; + err_at = 1; } - rtt_status->mpc = 1; /* Either failure or complete, we need to enable mpc */ + goto exit; + } + + rtt_status->pm = PM_OFF; + err = wldev_ioctl_get(dev, WLC_GET_PM, &rtt_status->pm, sizeof(rtt_status->pm)); + if (err) { + DHD_RTT_ERR(("Failed to get the PM value\n")); + } else { + err = wldev_ioctl_set(dev, WLC_SET_PM, &pm, sizeof(pm)); + if (err) { + DHD_RTT_ERR(("Failed to set the PM\n")); + rtt_status->pm_restore = FALSE; + } else { + rtt_status->pm_restore = TRUE; + } } mutex_lock(&rtt_status->rtt_mutex); /* Get a target information */ rtt_target = &rtt_status->rtt_config.target_info[rtt_status->cur_idx]; mutex_unlock(&rtt_status->rtt_mutex); - DHD_ERROR(("%s enter\n", __FUNCTION__)); + DHD_RTT(("%s enter\n", __FUNCTION__)); + + if (ETHER_ISNULLADDR(rtt_target->addr.octet)) { + err = BCME_BADADDR; + err_at = 2; + DHD_RTT(("RTT Target addr is NULL\n")); + goto exit; + } + + /* check for dp/others concurrency */ + rtt_invalid_reason = dhd_rtt_invalid_states(dev, &rtt_target->addr); + if (rtt_invalid_reason != RTT_STATE_VALID) { + err = BCME_BUSY; + err_at = 3; + DHD_RTT(("DRV State is not valid for RTT\n")); + goto exit; + } + +#ifdef WL_NAN + if (rtt_target->peer == RTT_PEER_NAN) { + rtt_sched_type = RTT_TYPE_NAN_DIRECTED; + rtt_status->status = RTT_ENABLED; + /* Ignore return value..failure taken care inside the API */ + dhd_rtt_nan_start_session(dhd, rtt_target); + goto exit; + } +#endif /* WL_NAN */ if (!RTT_IS_ENABLED(rtt_status)) { /* enable ftm */ err = dhd_rtt_ftm_enable(dhd, TRUE); if (err) { - DHD_ERROR(("failed to enable FTM (%d)\n", err)); + DHD_RTT_ERR(("failed to enable FTM (%d)\n", err)); + err_at = 5; goto exit; } } @@ -1288,7 +2256,8 @@ /* delete session of index default sesession */ err = dhd_rtt_delete_session(dhd, FTM_DEFAULT_SESSION); if (err < 0 && err != BCME_NOTFOUND) { - DHD_ERROR(("failed to delete session of FTM (%d)\n", err)); + DHD_RTT_ERR(("failed to delete session of FTM (%d)\n", err)); + err_at = 6; goto exit; } rtt_status->status = RTT_ENABLED; @@ -1300,45 +2269,72 @@ ftm_configs[ftm_cfg_cnt++].flags = WL_PROXD_SESSION_FLAG_INITIATOR; dhd_rtt_ftm_config(dhd, FTM_DEFAULT_SESSION, FTM_CONFIG_CAT_OPTIONS, ftm_configs, ftm_cfg_cnt); + + memset(ioctl_buf, 0, WLC_IOCTL_SMLEN); + + /* Rand Mac for newer version in place of cur_eth */ + if (dhd->wlc_ver_major < RTT_IOV_CUR_ETH_OBSOLETE) { + err = wldev_iovar_getbuf(dev, "cur_etheraddr", NULL, 0, + ioctl_buf, WLC_IOCTL_SMLEN, NULL); + if (err) { + DHD_RTT_ERR(("WLC_GET_CUR_ETHERADDR failed, error %d\n", err)); + err_at = 7; + goto exit; + } + memcpy(rtt_target->local_addr.octet, ioctl_buf, ETHER_ADDR_LEN); + + /* local mac address */ + if (!ETHER_ISNULLADDR(rtt_target->local_addr.octet)) { + ftm_params[ftm_param_cnt].mac_addr = rtt_target->local_addr; + ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_CUR_ETHER_ADDR; + bcm_ether_ntoa(&rtt_target->local_addr, eabuf); + DHD_RTT((">\t local %s\n", eabuf)); + } + } /* target's mac address */ if (!ETHER_ISNULLADDR(rtt_target->addr.octet)) { ftm_params[ftm_param_cnt].mac_addr = rtt_target->addr; ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_PEER_MAC; - DHD_ERROR((">\t target %s\n", bcm_ether_ntoa(&rtt_target->addr, eabuf))); + bcm_ether_ntoa(&rtt_target->addr, eabuf); + DHD_RTT((">\t target %s\n", eabuf)); } /* target's chanspec */ if (rtt_target->chanspec) { ftm_params[ftm_param_cnt].chanspec = htol32((uint32)rtt_target->chanspec); ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_CHANSPEC; - DHD_ERROR((">\t chanspec : %s\n", wf_chspec_ntoa(rtt_target->chanspec, chanbuf))); + wf_chspec_ntoa(rtt_target->chanspec, chanbuf); + DHD_RTT((">\t chanspec : %s\n", chanbuf)); } /* num-burst */ if (rtt_target->num_burst) { ftm_params[ftm_param_cnt].data16 = htol16(rtt_target->num_burst); ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_NUM_BURST; - DHD_ERROR((">\t num of burst : %d\n", rtt_target->num_burst)); + DHD_RTT((">\t num of burst : %d\n", rtt_target->num_burst)); } /* number of frame per burst */ - if (rtt_target->num_frames_per_burst == 0) { - rtt_target->num_frames_per_burst = - CHSPEC_IS20(rtt_target->chanspec) ? FTM_DEFAULT_CNT_20M : - CHSPEC_IS40(rtt_target->chanspec) ? FTM_DEFAULT_CNT_40M : - FTM_DEFAULT_CNT_80M; + rtt_target->num_frames_per_burst = FTM_DEFAULT_CNT_80M; + if (CHSPEC_IS80(rtt_target->chanspec)) { + rtt_target->num_frames_per_burst = FTM_DEFAULT_CNT_80M; + } else if (CHSPEC_IS40(rtt_target->chanspec)) { + rtt_target->num_frames_per_burst = FTM_DEFAULT_CNT_40M; + } else if (CHSPEC_IS20(rtt_target->chanspec)) { + rtt_target->num_frames_per_burst = FTM_DEFAULT_CNT_20M; } ftm_params[ftm_param_cnt].data16 = htol16(rtt_target->num_frames_per_burst); ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_BURST_NUM_FTM; - DHD_ERROR((">\t number of frame per burst : %d\n", rtt_target->num_frames_per_burst)); + DHD_RTT((">\t number of frame per burst : %d\n", rtt_target->num_frames_per_burst)); + /* FTM retry count */ if (rtt_target->num_retries_per_ftm) { ftm_params[ftm_param_cnt].data8 = rtt_target->num_retries_per_ftm; ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_FTM_RETRIES; - DHD_ERROR((">\t retry count of FTM : %d\n", rtt_target->num_retries_per_ftm)); + DHD_RTT((">\t retry count of FTM : %d\n", rtt_target->num_retries_per_ftm)); } /* FTM Request retry count */ if (rtt_target->num_retries_per_ftmr) { ftm_params[ftm_param_cnt].data8 = rtt_target->num_retries_per_ftmr; ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_FTM_REQ_RETRIES; - DHD_ERROR((">\t retry count of FTM Req : %d\n", rtt_target->num_retries_per_ftm)); + DHD_RTT((">\t retry count of FTM Req : %d\n", rtt_target->num_retries_per_ftmr)); } /* burst-period */ if (rtt_target->burst_period) { @@ -1346,86 +2342,140 @@ htol32(rtt_target->burst_period); /* ms */ ftm_params[ftm_param_cnt].data_intvl.tmu = WL_PROXD_TMU_MILLI_SEC; ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_BURST_PERIOD; - DHD_ERROR((">\t burst period : %d ms\n", rtt_target->burst_period)); + DHD_RTT((">\t burst period : %d ms\n", rtt_target->burst_period)); } + /* Setting both duration and timeout to MAX duration + * to handle the congestion environments. + * Hence ignoring the user config. + */ /* burst-duration */ + rtt_target->burst_duration = FTM_MAX_BURST_DUR_TMO_MS; if (rtt_target->burst_duration) { ftm_params[ftm_param_cnt].data_intvl.intvl = - htol32(rtt_target->burst_period); /* ms */ + htol32(rtt_target->burst_duration); /* ms */ ftm_params[ftm_param_cnt].data_intvl.tmu = WL_PROXD_TMU_MILLI_SEC; ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_BURST_DURATION; - DHD_ERROR((">\t burst duration : %d ms\n", + DHD_RTT((">\t burst duration : %d ms\n", rtt_target->burst_duration)); } + /* burst-timeout */ + rtt_target->burst_timeout = FTM_MAX_BURST_DUR_TMO_MS; + if (rtt_target->burst_timeout) { + ftm_params[ftm_param_cnt].data_intvl.intvl = + htol32(rtt_target->burst_timeout); /* ms */ + ftm_params[ftm_param_cnt].data_intvl.tmu = WL_PROXD_TMU_MILLI_SEC; + ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_BURST_TIMEOUT; + DHD_RTT((">\t burst timeout : %d ms\n", + rtt_target->burst_timeout)); + } + /* event_mask..applicable for only Legacy RTT. + * For nan-rtt config happens from firmware + */ + ftm_params[ftm_param_cnt].event_mask = ((1 << WL_PROXD_EVENT_BURST_END) | + (1 << WL_PROXD_EVENT_SESSION_END)); + ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_EVENT_MASK; + if (rtt_target->bw && rtt_target->preamble) { bool use_default = FALSE; int nss; int mcs; switch (rtt_target->preamble) { - case RTT_PREAMBLE_LEGACY: - rspec |= WL_RSPEC_ENCODE_RATE; /* 11abg */ - rspec |= WL_RATE_6M; - break; - case RTT_PREAMBLE_HT: - rspec |= WL_RSPEC_ENCODE_HT; /* 11n HT */ - mcs = 0; /* default MCS 0 */ - rspec |= mcs; - break; - case RTT_PREAMBLE_VHT: - rspec |= WL_RSPEC_ENCODE_VHT; /* 11ac VHT */ - mcs = 0; /* default MCS 0 */ - nss = 1; /* default Nss = 1 */ - rspec |= (nss << WL_RSPEC_VHT_NSS_SHIFT) | mcs; - break; - default: - DHD_ERROR(("doesn't support this preamble : %d\n", rtt_target->preamble)); - use_default = TRUE; - break; + case RTT_PREAMBLE_LEGACY: + rspec |= WL_RSPEC_ENCODE_RATE; /* 11abg */ + rspec |= WL_RATE_6M; + break; + case RTT_PREAMBLE_HT: + rspec |= WL_RSPEC_ENCODE_HT; /* 11n HT */ + mcs = 0; /* default MCS 0 */ + rspec |= mcs; + break; + case RTT_PREAMBLE_VHT: + rspec |= WL_RSPEC_ENCODE_VHT; /* 11ac VHT */ + mcs = 0; /* default MCS 0 */ + nss = 1; /* default Nss = 1 */ + rspec |= (nss << WL_RSPEC_VHT_NSS_SHIFT) | mcs; + break; + default: + DHD_RTT(("doesn't support this preamble : %d\n", + rtt_target->preamble)); + use_default = TRUE; + break; } switch (rtt_target->bw) { - case RTT_BW_20: - rspec |= WL_RSPEC_BW_20MHZ; - break; - case RTT_BW_40: - rspec |= WL_RSPEC_BW_40MHZ; - break; - case RTT_BW_80: - rspec |= WL_RSPEC_BW_80MHZ; - break; - default: - DHD_ERROR(("doesn't support this BW : %d\n", rtt_target->bw)); - use_default = TRUE; - break; + case RTT_BW_20: + rspec |= WL_RSPEC_BW_20MHZ; + break; + case RTT_BW_40: + rspec |= WL_RSPEC_BW_40MHZ; + break; + case RTT_BW_80: + rspec |= WL_RSPEC_BW_80MHZ; + break; + default: + DHD_RTT(("doesn't support this BW : %d\n", rtt_target->bw)); + use_default = TRUE; + break; } if (!use_default) { ftm_params[ftm_param_cnt].data32 = htol32(rspec); ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_RATESPEC; - DHD_ERROR((">\t ratespec : %d\n", rspec)); + DHD_RTT((">\t ratespec : %d\n", rspec)); } } + dhd_set_rand_mac_oui(dhd); dhd_rtt_ftm_config(dhd, FTM_DEFAULT_SESSION, FTM_CONFIG_CAT_GENERAL, - ftm_params, ftm_param_cnt); + ftm_params, ftm_param_cnt); + rtt_sched_type = RTT_TYPE_LEGACY; err = dhd_rtt_start_session(dhd, FTM_DEFAULT_SESSION, TRUE); if (err) { - DHD_ERROR(("failed to start session of FTM : error %d\n", err)); + DHD_RTT_ERR(("failed to start session of FTM : error %d\n", err)); + err_at = 8; + } else { + /* schedule proxd timeout */ + schedule_delayed_work(&rtt_status->proxd_timeout, + msecs_to_jiffies(DHD_NAN_RTT_TIMER_INTERVAL_MS)); + } + + goto exit; +geofence: +#ifdef WL_NAN + /* sched geofencing rtt */ + rtt_sched_type = RTT_TYPE_NAN_GEOFENCE; + if ((err = dhd_rtt_sched_geofencing_target(dhd)) != BCME_OK) { + DHD_RTT_ERR(("geofencing sched failed, err = %d\n", err)); + err_at = 9; + } +#endif /* WL_NAN */ + exit: if (err) { + /* RTT Failed */ + DHD_RTT_ERR(("dhd_rtt_start: Failed & RTT_STOPPED, err = %d," + " err_at = %d, rtt_sched_type = %d, rtt_invalid_reason = %d\n" + " sched_reason = %d", + err, err_at, rtt_sched_type, rtt_invalid_reason, + rtt_status->rtt_sched_reason)); rtt_status->status = RTT_STOPPED; /* disable FTM */ dhd_rtt_ftm_enable(dhd, FALSE); - if (rtt_status->mpc) { - /* enable mpc again in case of error */ - mpc = 1; - rtt_status->mpc = 0; - err = dhd_iovar(dhd, 0, "mpc", (char *)&mpc, sizeof(mpc), - NULL, 0, TRUE); + if (rtt_status->pm_restore) { + pm = PM_FAST; + DHD_RTT_ERR(("pm_restore =%d func =%s \n", + rtt_status->pm_restore, __FUNCTION__)); + err = wldev_ioctl_set(dev, WLC_SET_PM, &pm, sizeof(pm)); + if (err) { + DHD_RTT_ERR(("Failed to set PM \n")); + } else { + rtt_status->pm_restore = FALSE; + } } } return err; } +#endif /* WL_CFG80211 */ int dhd_rtt_register_noti_callback(dhd_pub_t *dhd, void *ctx, dhd_rtt_compl_noti_fn noti_fn) @@ -1439,7 +2489,9 @@ rtt_status = GET_RTTSTATE(dhd); NULL_CHECK(rtt_status, "rtt_status is NULL", err); spin_lock_bh(¬i_list_lock); + GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST(); list_for_each_entry(iter, &rtt_status->noti_fn_list, list) { + GCC_DIAGNOSTIC_POP(); if (iter->noti_fn == noti_fn) { goto exit; } @@ -1468,13 +2520,16 @@ rtt_status = GET_RTTSTATE(dhd); NULL_CHECK(rtt_status, "rtt_status is NULL", err); spin_lock_bh(¬i_list_lock); + GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST(); list_for_each_entry(iter, &rtt_status->noti_fn_list, list) { + GCC_DIAGNOSTIC_POP(); if (iter->noti_fn == noti_fn) { cb = iter; list_del(&cb->list); break; } } + spin_unlock_bh(¬i_list_lock); if (cb) { kfree(cb); @@ -1486,56 +2541,103 @@ dhd_rtt_convert_rate_to_host(uint32 rspec) { wifi_rate_t host_rate; + uint32 bandwidth; memset(&host_rate, 0, sizeof(wifi_rate_t)); - if ((rspec & WL_RSPEC_ENCODING_MASK) == WL_RSPEC_ENCODE_RATE) { + if (RSPEC_ISLEGACY(rspec)) { host_rate.preamble = 0; - } else if ((rspec & WL_RSPEC_ENCODING_MASK) == WL_RSPEC_ENCODE_HT) { + } else if (RSPEC_ISHT(rspec)) { host_rate.preamble = 2; host_rate.rateMcsIdx = rspec & WL_RSPEC_RATE_MASK; - } else if ((rspec & WL_RSPEC_ENCODING_MASK) == WL_RSPEC_ENCODE_VHT) { + } else if (RSPEC_ISVHT(rspec)) { host_rate.preamble = 3; host_rate.rateMcsIdx = rspec & WL_RSPEC_VHT_MCS_MASK; host_rate.nss = (rspec & WL_RSPEC_VHT_NSS_MASK) >> WL_RSPEC_VHT_NSS_SHIFT; } - host_rate.bw = (rspec & WL_RSPEC_BW_MASK) - 1; + + bandwidth = RSPEC_BW(rspec); + switch (bandwidth) { + case WL_RSPEC_BW_20MHZ: + host_rate.bw = RTT_RATE_20M; + break; + case WL_RSPEC_BW_40MHZ: + host_rate.bw = RTT_RATE_40M; + break; + case WL_RSPEC_BW_80MHZ: + host_rate.bw = RTT_RATE_80M; + break; + case WL_RSPEC_BW_160MHZ: + host_rate.bw = RTT_RATE_160M; + break; + default: + host_rate.bw = RTT_RATE_20M; + break; + } + host_rate.bitrate = rate_rspec2rate(rspec) / 100; /* 100kbps */ DHD_RTT(("bit rate : %d\n", host_rate.bitrate)); return host_rate; } - +#define FTM_FRAME_TYPES {"SETUP", "TRIGGER", "TIMESTAMP"} static int -dhd_rtt_convert_results_to_host(rtt_report_t *rtt_report, uint8 *p_data, uint16 tlvid, uint16 len) +dhd_rtt_convert_results_to_host_v1(rtt_result_t *rtt_result, const uint8 *p_data, + uint16 tlvid, uint16 len) { + int i; int err = BCME_OK; char eabuf[ETHER_ADDR_STR_LEN]; - wl_proxd_rtt_result_t *p_data_info; wl_proxd_result_flags_t flags; wl_proxd_session_state_t session_state; wl_proxd_status_t proxd_status; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)) struct timespec64 ts; -#else +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) struct timespec ts; -#endif +#endif /* LINUX_VER >= 2.6.39 */ uint32 ratespec; uint32 avg_dist; - wl_proxd_rtt_sample_t *p_sample; + const wl_proxd_rtt_result_v1_t *p_data_info = NULL; + const wl_proxd_rtt_sample_v1_t *p_sample_avg = NULL; + const wl_proxd_rtt_sample_v1_t *p_sample = NULL; wl_proxd_intvl_t rtt; wl_proxd_intvl_t p_time; + uint16 num_rtt = 0, snr = 0, bitflips = 0; + wl_proxd_phy_error_t tof_phy_error = 0; + wl_proxd_phy_error_t tof_phy_tgt_error = 0; + wl_proxd_snr_t tof_target_snr = 0; + wl_proxd_bitflips_t tof_target_bitflips = 0; + int16 rssi = 0; + int32 dist = 0; + uint8 num_ftm = 0; + char *ftm_frame_types[] = FTM_FRAME_TYPES; + rtt_report_t *rtt_report = &(rtt_result->report); + + BCM_REFERENCE(ftm_frame_types); + BCM_REFERENCE(dist); + BCM_REFERENCE(rssi); + BCM_REFERENCE(tof_target_bitflips); + BCM_REFERENCE(tof_target_snr); + BCM_REFERENCE(tof_phy_tgt_error); + BCM_REFERENCE(tof_phy_error); + BCM_REFERENCE(bitflips); + BCM_REFERENCE(snr); + BCM_REFERENCE(session_state); + BCM_REFERENCE(ftm_session_state_value_to_logstr); NULL_CHECK(rtt_report, "rtt_report is NULL", err); NULL_CHECK(p_data, "p_data is NULL", err); DHD_RTT(("%s enter\n", __FUNCTION__)); - p_data_info = (wl_proxd_rtt_result_t *) p_data; + p_data_info = (const wl_proxd_rtt_result_v1_t *) p_data; /* unpack and format 'flags' for display */ flags = ltoh16_ua(&p_data_info->flags); /* session state and status */ session_state = ltoh16_ua(&p_data_info->state); proxd_status = ltoh32_ua(&p_data_info->status); + bcm_ether_ntoa((&(p_data_info->peer)), eabuf); + ftm_status_value_to_logstr(proxd_status); DHD_RTT((">\tTarget(%s) session state=%d(%s), status=%d(%s)\n", - bcm_ether_ntoa((&(p_data_info->peer)), eabuf), + eabuf, session_state, ftm_session_state_value_to_logstr(session_state), proxd_status, @@ -1558,13 +2660,14 @@ p_data_info->num_ftm)); /* in a session */ } /* show 'avg_rtt' sample */ - p_sample = &p_data_info->avg_rtt; + p_sample_avg = &p_data_info->avg_rtt; + ftm_tmu_value_to_logstr(ltoh16_ua(&p_sample_avg->rtt.tmu)); DHD_RTT((">\tavg_rtt sample: rssi=%d rtt=%d%s std_deviation =%d.%d ratespec=0x%08x\n", - (int16) ltoh16_ua(&p_sample->rssi), - ltoh32_ua(&p_sample->rtt.intvl), - ftm_tmu_value_to_logstr(ltoh16_ua(&p_sample->rtt.tmu)), + (int16) ltoh16_ua(&p_sample_avg->rssi), + ltoh32_ua(&p_sample_avg->rtt.intvl), + ftm_tmu_value_to_logstr(ltoh16_ua(&p_sample_avg->rtt.tmu)), ltoh16_ua(&p_data_info->sd_rtt)/10, ltoh16_ua(&p_data_info->sd_rtt)%10, - ltoh32_ua(&p_sample->ratespec))); + ltoh32_ua(&p_sample_avg->ratespec))); /* set peer address */ rtt_report->addr = p_data_info->peer; @@ -1578,8 +2681,10 @@ /* status */ rtt_report->status = ftm_get_statusmap_info(proxd_status, &ftm_status_map_info[0], ARRAYSIZE(ftm_status_map_info)); + /* rssi (0.5db) */ - rtt_report->rssi = (int16)ltoh16_ua(&p_data_info->avg_rtt.rssi) * 2; + rtt_report->rssi = ABS((wl_proxd_rssi_t)ltoh16_ua(&p_data_info->avg_rtt.rssi)) * 2; + /* rx rate */ ratespec = ltoh32_ua(&p_data_info->avg_rtt.ratespec); rtt_report->rx_rate = dhd_rtt_convert_rate_to_host(ratespec); @@ -1592,26 +2697,24 @@ /* rtt_sd */ rtt.tmu = ltoh16_ua(&p_data_info->avg_rtt.rtt.tmu); rtt.intvl = ltoh32_ua(&p_data_info->avg_rtt.rtt.intvl); - rtt_report->rtt = FTM_INTVL2NSEC(&rtt) * 10; /* nano -> 0.1 nano */ + rtt_report->rtt = (wifi_timespan)FTM_INTVL2NSEC(&rtt) * 1000; /* nano -> pico seconds */ rtt_report->rtt_sd = ltoh16_ua(&p_data_info->sd_rtt); /* nano -> 0.1 nano */ DHD_RTT(("rtt_report->rtt : %llu\n", rtt_report->rtt)); DHD_RTT(("rtt_report->rssi : %d (0.5db)\n", rtt_report->rssi)); /* average distance */ if (avg_dist != FTM_INVALID) { - rtt_report->distance = (avg_dist >> 8) * 100; /* meter -> cm */ - rtt_report->distance += (avg_dist & 0xff) * 100 / 256; + rtt_report->distance = (avg_dist >> 8) * 1000; /* meter -> mm */ + rtt_report->distance += (avg_dist & 0xff) * 1000 / 256; } else { rtt_report->distance = FTM_INVALID; } /* time stamp */ /* get the time elapsed from boot time */ -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)) - ktime_get_boottime_ts64(&ts); -#else +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) get_monotonic_boottime(&ts); -#endif rtt_report->ts = (uint64)TIMESPEC_TO_US(ts); +#endif /* LINUX_VER >= 2.6.39 */ if (proxd_status == WL_PROXD_E_REMOTE_FAIL) { /* retry time after failure */ @@ -1631,31 +2734,741 @@ ftm_tmu_value_to_logstr(ltoh16_ua(&p_data_info->u.burst_duration.tmu)))); DHD_RTT(("rtt_report->burst_duration : %d\n", rtt_report->burst_duration)); } + + /* display detail if available */ + num_rtt = ltoh16_ua(&p_data_info->num_rtt); + if (num_rtt > 0) { + DHD_RTT((">\tnum rtt: %d samples\n", num_rtt)); + p_sample = &p_data_info->rtt[0]; + for (i = 0; i < num_rtt; i++) { + snr = 0; + bitflips = 0; + tof_phy_error = 0; + tof_phy_tgt_error = 0; + tof_target_snr = 0; + tof_target_bitflips = 0; + rssi = 0; + dist = 0; + num_ftm = p_data_info->num_ftm; + /* FTM frames 1,4,7,11 have valid snr, rssi and bitflips */ + if ((i % num_ftm) == 1) { + rssi = (wl_proxd_rssi_t) ltoh16_ua(&p_sample->rssi); + snr = (wl_proxd_snr_t) ltoh16_ua(&p_sample->snr); + bitflips = (wl_proxd_bitflips_t) ltoh16_ua(&p_sample->bitflips); + tof_phy_error = + (wl_proxd_phy_error_t) + ltoh32_ua(&p_sample->tof_phy_error); + tof_phy_tgt_error = + (wl_proxd_phy_error_t) + ltoh32_ua(&p_sample->tof_tgt_phy_error); + tof_target_snr = + (wl_proxd_snr_t) + ltoh16_ua(&p_sample->tof_tgt_snr); + tof_target_bitflips = + (wl_proxd_bitflips_t) + ltoh16_ua(&p_sample->tof_tgt_bitflips); + dist = ltoh32_ua(&p_sample->distance); + } else { + rssi = -1; + snr = 0; + bitflips = 0; + dist = 0; + tof_target_bitflips = 0; + tof_target_snr = 0; + tof_phy_tgt_error = 0; + } + DHD_RTT((">\t sample[%d]: id=%d rssi=%d snr=0x%x bitflips=%d" + " tof_phy_error %x tof_phy_tgt_error %x target_snr=0x%x" + " target_bitflips=%d dist=%d rtt=%d%s status %s" + " Type %s coreid=%d\n", + i, p_sample->id, rssi, snr, + bitflips, tof_phy_error, tof_phy_tgt_error, + tof_target_snr, + tof_target_bitflips, dist, + ltoh32_ua(&p_sample->rtt.intvl), + ftm_tmu_value_to_logstr(ltoh16_ua(&p_sample->rtt.tmu)), + ftm_status_value_to_logstr(ltoh32_ua(&p_sample->status)), + ftm_frame_types[i % num_ftm], p_sample->coreid)); + p_sample++; + } + } return err; } + +static int +dhd_rtt_convert_results_to_host_v2(rtt_result_t *rtt_result, const uint8 *p_data, + uint16 tlvid, uint16 len) +{ + int i; + int err = BCME_OK; + char eabuf[ETHER_ADDR_STR_LEN]; + wl_proxd_result_flags_t flags; + wl_proxd_session_state_t session_state; + wl_proxd_status_t proxd_status; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)) + struct timespec64 ts; +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) + struct timespec ts; +#endif /* LINUX_VER >= 2.6.39 */ + uint32 ratespec; + uint32 avg_dist; + const wl_proxd_rtt_result_v2_t *p_data_info = NULL; + const wl_proxd_rtt_sample_v2_t *p_sample_avg = NULL; + const wl_proxd_rtt_sample_v2_t *p_sample = NULL; + uint16 num_rtt = 0; + wl_proxd_intvl_t rtt; + wl_proxd_intvl_t p_time; + uint16 snr = 0, bitflips = 0; + wl_proxd_phy_error_t tof_phy_error = 0; + wl_proxd_phy_error_t tof_phy_tgt_error = 0; + wl_proxd_snr_t tof_target_snr = 0; + wl_proxd_bitflips_t tof_target_bitflips = 0; + int16 rssi = 0; + int32 dist = 0; + uint32 chanspec = 0; + uint8 num_ftm = 0; + char *ftm_frame_types[] = FTM_FRAME_TYPES; + rtt_report_t *rtt_report = &(rtt_result->report); + + BCM_REFERENCE(ftm_frame_types); + BCM_REFERENCE(dist); + BCM_REFERENCE(rssi); + BCM_REFERENCE(tof_target_bitflips); + BCM_REFERENCE(tof_target_snr); + BCM_REFERENCE(tof_phy_tgt_error); + BCM_REFERENCE(tof_phy_error); + BCM_REFERENCE(bitflips); + BCM_REFERENCE(snr); + BCM_REFERENCE(chanspec); + BCM_REFERENCE(session_state); + BCM_REFERENCE(ftm_session_state_value_to_logstr); + + NULL_CHECK(rtt_report, "rtt_report is NULL", err); + NULL_CHECK(p_data, "p_data is NULL", err); + DHD_RTT(("%s enter\n", __FUNCTION__)); + p_data_info = (const wl_proxd_rtt_result_v2_t *) p_data; + /* unpack and format 'flags' for display */ + flags = ltoh16_ua(&p_data_info->flags); + /* session state and status */ + session_state = ltoh16_ua(&p_data_info->state); + proxd_status = ltoh32_ua(&p_data_info->status); + bcm_ether_ntoa((&(p_data_info->peer)), eabuf); + + if (proxd_status != BCME_OK) { + DHD_RTT_ERR((">\tTarget(%s) session state=%d(%s), status=%d(%s) " + "num_meas_ota %d num_valid_rtt %d result_flags %x\n", + eabuf, session_state, + ftm_session_state_value_to_logstr(session_state), + proxd_status, ftm_status_value_to_logstr(proxd_status), + p_data_info->num_meas, p_data_info->num_valid_rtt, + p_data_info->flags)); + } else { + DHD_RTT((">\tTarget(%s) session state=%d(%s), status=%d(%s)\n", + eabuf, session_state, + ftm_session_state_value_to_logstr(session_state), + proxd_status, ftm_status_value_to_logstr(proxd_status))); + } + /* show avg_dist (1/256m units), burst_num */ + avg_dist = ltoh32_ua(&p_data_info->avg_dist); + if (avg_dist == 0xffffffff) { /* report 'failure' case */ + DHD_RTT((">\tavg_dist=-1m, burst_num=%d, valid_measure_cnt=%d\n", + ltoh16_ua(&p_data_info->burst_num), + p_data_info->num_valid_rtt)); /* in a session */ + avg_dist = FTM_INVALID; + } else { + DHD_RTT((">\tavg_dist=%d.%04dm, burst_num=%d, valid_measure_cnt=%d num_ftm=%d " + "num_meas_ota=%d, result_flags=%x\n", avg_dist >> 8, /* 1/256m units */ + ((avg_dist & 0xff) * 625) >> 4, + ltoh16_ua(&p_data_info->burst_num), + p_data_info->num_valid_rtt, + p_data_info->num_ftm, p_data_info->num_meas, + p_data_info->flags)); /* in a session */ + } + rtt_result->rtt_detail.num_ota_meas = p_data_info->num_meas; + rtt_result->rtt_detail.result_flags = p_data_info->flags; + /* show 'avg_rtt' sample */ + /* in v2, avg_rtt is the first element of the variable rtt[] */ + p_sample_avg = &p_data_info->rtt[0]; + ftm_tmu_value_to_logstr(ltoh16_ua(&p_sample_avg->rtt.tmu)); + DHD_RTT((">\tavg_rtt sample: rssi=%d rtt=%d%s std_deviation =%d.%d" + "ratespec=0x%08x chanspec=0x%08x\n", + (int16) ltoh16_ua(&p_sample_avg->rssi), + ltoh32_ua(&p_sample_avg->rtt.intvl), + ftm_tmu_value_to_logstr(ltoh16_ua(&p_sample_avg->rtt.tmu)), + ltoh16_ua(&p_data_info->sd_rtt)/10, ltoh16_ua(&p_data_info->sd_rtt)%10, + ltoh32_ua(&p_sample_avg->ratespec), + ltoh32_ua(&p_sample_avg->chanspec))); + + /* set peer address */ + rtt_report->addr = p_data_info->peer; + + /* burst num */ + rtt_report->burst_num = ltoh16_ua(&p_data_info->burst_num); + + /* success num */ + rtt_report->success_num = p_data_info->num_valid_rtt; + + /* num-ftm configured */ + rtt_report->ftm_num = p_data_info->num_ftm; + + /* actual number of FTM supported by peer */ + rtt_report->num_per_burst_peer = p_data_info->num_ftm; + rtt_report->negotiated_burst_num = p_data_info->num_ftm; + + /* status */ + rtt_report->status = ftm_get_statusmap_info(proxd_status, + &ftm_status_map_info[0], ARRAYSIZE(ftm_status_map_info)); + + /* Framework expects status as SUCCESS else all results will be + * set to zero even if we have partial valid result. + * So setting status as SUCCESS if we have a valid_rtt + * On burst timeout we stop burst with "timeout" reason and + * on msch end we set status as "cancel" + */ + if ((proxd_status == WL_PROXD_E_TIMEOUT || + proxd_status == WL_PROXD_E_CANCELED) && + rtt_report->success_num) { + rtt_report->status = RTT_STATUS_SUCCESS; + } + + /* rssi (0.5db) */ + rtt_report->rssi = ABS((wl_proxd_rssi_t)ltoh16_ua(&p_sample_avg->rssi)) * 2; + + /* rx rate */ + ratespec = ltoh32_ua(&p_sample_avg->ratespec); + rtt_report->rx_rate = dhd_rtt_convert_rate_to_host(ratespec); + + /* tx rate */ + if (flags & WL_PROXD_RESULT_FLAG_VHTACK) { + rtt_report->tx_rate = dhd_rtt_convert_rate_to_host(0x2010010); + } else { + rtt_report->tx_rate = dhd_rtt_convert_rate_to_host(0xc); + } + + /* rtt_sd */ + rtt.tmu = ltoh16_ua(&p_sample_avg->rtt.tmu); + rtt.intvl = ltoh32_ua(&p_sample_avg->rtt.intvl); + rtt_report->rtt = (wifi_timespan)FTM_INTVL2NSEC(&rtt) * 1000; /* nano -> pico seconds */ + rtt_report->rtt_sd = ltoh16_ua(&p_data_info->sd_rtt); /* nano -> 0.1 nano */ + DHD_RTT(("rtt_report->rtt : %llu\n", rtt_report->rtt)); + DHD_RTT(("rtt_report->rssi : %d (0.5db)\n", rtt_report->rssi)); + + /* average distance */ + if (avg_dist != FTM_INVALID) { + rtt_report->distance = (avg_dist >> 8) * 1000; /* meter -> mm */ + rtt_report->distance += (avg_dist & 0xff) * 1000 / 256; + /* rtt_sd is in 0.1 ns. + * host needs distance_sd in milli mtrs + * (0.1 * rtt_sd/2 * 10^-9) * C * 1000 + */ + rtt_report->distance_sd = rtt_report->rtt_sd * 15; /* mm */ + } else { + rtt_report->distance = FTM_INVALID; + } + /* time stamp */ + /* get the time elapsed from boot time */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) + get_monotonic_boottime(&ts); + rtt_report->ts = (uint64)TIMESPEC_TO_US(ts); +#endif /* LINUX_VER >= 2.6.39 */ + + if (proxd_status == WL_PROXD_E_REMOTE_FAIL) { + /* retry time after failure */ + p_time.intvl = ltoh32_ua(&p_data_info->u.retry_after.intvl); + p_time.tmu = ltoh16_ua(&p_data_info->u.retry_after.tmu); + rtt_report->retry_after_duration = FTM_INTVL2SEC(&p_time); /* s -> s */ + DHD_RTT((">\tretry_after: %d%s\n", + ltoh32_ua(&p_data_info->u.retry_after.intvl), + ftm_tmu_value_to_logstr(ltoh16_ua(&p_data_info->u.retry_after.tmu)))); + } else { + /* burst duration */ + p_time.intvl = ltoh32_ua(&p_data_info->u.retry_after.intvl); + p_time.tmu = ltoh16_ua(&p_data_info->u.retry_after.tmu); + rtt_report->burst_duration = FTM_INTVL2MSEC(&p_time); /* s -> ms */ + DHD_RTT((">\tburst_duration: %d%s\n", + ltoh32_ua(&p_data_info->u.burst_duration.intvl), + ftm_tmu_value_to_logstr(ltoh16_ua(&p_data_info->u.burst_duration.tmu)))); + DHD_RTT(("rtt_report->burst_duration : %d\n", rtt_report->burst_duration)); + } + /* display detail if available */ + num_rtt = ltoh16_ua(&p_data_info->num_rtt); + if (num_rtt > 0) { + DHD_RTT((">\tnum rtt: %d samples\n", num_rtt)); + p_sample = &p_data_info->rtt[1]; + for (i = 0; i < num_rtt; i++) { + snr = 0; + bitflips = 0; + tof_phy_error = 0; + tof_phy_tgt_error = 0; + tof_target_snr = 0; + tof_target_bitflips = 0; + rssi = 0; + dist = 0; + num_ftm = p_data_info->num_ftm; + /* FTM frames 1,4,7,11 have valid snr, rssi and bitflips */ + if ((i % num_ftm) == 1) { + rssi = (wl_proxd_rssi_t) ltoh16_ua(&p_sample->rssi); + snr = (wl_proxd_snr_t) ltoh16_ua(&p_sample->snr); + bitflips = (wl_proxd_bitflips_t) ltoh16_ua(&p_sample->bitflips); + tof_phy_error = + (wl_proxd_phy_error_t) + ltoh32_ua(&p_sample->tof_phy_error); + tof_phy_tgt_error = + (wl_proxd_phy_error_t) + ltoh32_ua(&p_sample->tof_tgt_phy_error); + tof_target_snr = + (wl_proxd_snr_t) + ltoh16_ua(&p_sample->tof_tgt_snr); + tof_target_bitflips = + (wl_proxd_bitflips_t) + ltoh16_ua(&p_sample->tof_tgt_bitflips); + dist = ltoh32_ua(&p_sample->distance); + chanspec = ltoh32_ua(&p_sample->chanspec); + } else { + rssi = -1; + snr = 0; + bitflips = 0; + dist = 0; + tof_target_bitflips = 0; + tof_target_snr = 0; + tof_phy_tgt_error = 0; + } + DHD_RTT((">\t sample[%d]: id=%d rssi=%d snr=0x%x bitflips=%d" + " tof_phy_error %x tof_phy_tgt_error %x target_snr=0x%x" + " target_bitflips=%d dist=%d rtt=%d%s status %s Type %s" + " coreid=%d chanspec=0x%08x\n", + i, p_sample->id, rssi, snr, + bitflips, tof_phy_error, tof_phy_tgt_error, + tof_target_snr, + tof_target_bitflips, dist, + ltoh32_ua(&p_sample->rtt.intvl), + ftm_tmu_value_to_logstr(ltoh16_ua(&p_sample->rtt.tmu)), + ftm_status_value_to_logstr(ltoh32_ua(&p_sample->status)), + ftm_frame_types[i % num_ftm], p_sample->coreid, + chanspec)); + p_sample++; + } + } + return err; +} +#ifdef WL_CFG80211 +/* Common API for handling Session End. +* This API will flush out the results for a peer MAC. +* +* @For legacy FTM session, this API will be called +* when legacy FTM_SESSION_END event is received. +* @For legacy Nan-RTT , this API will be called when +* we are cancelling the nan-ranging session or on +* nan-ranging-end event. +*/ +static void +dhd_rtt_handle_rtt_session_end(dhd_pub_t *dhd) +{ + + int idx; + struct rtt_noti_callback *iter; + rtt_results_header_t *entry, *next; + rtt_result_t *next2; + rtt_result_t *rtt_result; + rtt_status_info_t *rtt_status = GET_RTTSTATE(dhd); + + /* Cancel pending proxd timeout work if any */ + if (delayed_work_pending(&rtt_status->proxd_timeout)) { + cancel_delayed_work(&rtt_status->proxd_timeout); + } + + /* find next target to trigger RTT */ + for (idx = (rtt_status->cur_idx + 1); + idx < rtt_status->rtt_config.rtt_target_cnt; idx++) { + /* skip the disabled device */ + if (rtt_status->rtt_config.target_info[idx].disable) { + continue; + } else { + /* set the idx to cur_idx */ + rtt_status->cur_idx = idx; + break; + } + } + if (idx < rtt_status->rtt_config.rtt_target_cnt) { + /* restart to measure RTT from next device */ + DHD_INFO(("restart to measure rtt\n")); + schedule_work(&rtt_status->work); + } else { + DHD_RTT(("RTT_STOPPED\n")); + rtt_status->status = RTT_STOPPED; + /* notify the completed information to others */ + GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST(); + list_for_each_entry(iter, &rtt_status->noti_fn_list, list) { + iter->noti_fn(iter->ctx, &rtt_status->rtt_results_cache); + } + /* remove the rtt results in cache */ + if (!list_empty(&rtt_status->rtt_results_cache)) { + /* Iterate rtt_results_header list */ + list_for_each_entry_safe(entry, next, + &rtt_status->rtt_results_cache, list) { + list_del(&entry->list); + /* Iterate rtt_result list */ + list_for_each_entry_safe(rtt_result, next2, + &entry->result_list, list) { + list_del(&rtt_result->list); + kfree(rtt_result); + } + kfree(entry); + } + } + GCC_DIAGNOSTIC_POP(); + /* reinitialize the HEAD */ + INIT_LIST_HEAD(&rtt_status->rtt_results_cache); + /* clear information for rtt_config */ + rtt_status->rtt_config.rtt_target_cnt = 0; + memset_s(rtt_status->rtt_config.target_info, TARGET_INFO_SIZE(RTT_MAX_TARGET_CNT), + 0, TARGET_INFO_SIZE(RTT_MAX_TARGET_CNT)); + rtt_status->cur_idx = 0; + } +} +#endif /* WL_CFG80211 */ + +#ifdef WL_CFG80211 +static int +dhd_rtt_create_failure_result(rtt_status_info_t *rtt_status, + struct ether_addr *addr) +{ + rtt_results_header_t *rtt_results_header = NULL; + rtt_target_info_t *rtt_target_info; + int ret = BCME_OK; + rtt_result_t *rtt_result; + + /* allocate new header for rtt_results */ + rtt_results_header = (rtt_results_header_t *)MALLOCZ(rtt_status->dhd->osh, + sizeof(rtt_results_header_t)); + if (!rtt_results_header) { + ret = -ENOMEM; + goto exit; + } + rtt_target_info = &rtt_status->rtt_config.target_info[rtt_status->cur_idx]; + /* Initialize the head of list for rtt result */ + INIT_LIST_HEAD(&rtt_results_header->result_list); + /* same src and dest len */ + (void)memcpy_s(&rtt_results_header->peer_mac, + ETHER_ADDR_LEN, addr, ETHER_ADDR_LEN); + list_add_tail(&rtt_results_header->list, &rtt_status->rtt_results_cache); + + /* allocate rtt_results for new results */ + rtt_result = (rtt_result_t *)MALLOCZ(rtt_status->dhd->osh, + sizeof(rtt_result_t)); + if (!rtt_result) { + ret = -ENOMEM; + kfree(rtt_results_header); + goto exit; + } + /* fill out the results from the configuration param */ + rtt_result->report.ftm_num = rtt_target_info->num_frames_per_burst; + rtt_result->report.type = RTT_TWO_WAY; + DHD_RTT(("report->ftm_num : %d\n", rtt_result->report.ftm_num)); + rtt_result->report_len = RTT_REPORT_SIZE; + rtt_result->report.status = RTT_STATUS_FAIL_NO_RSP; + /* same src and dest len */ + (void)memcpy_s(&rtt_result->report.addr, ETHER_ADDR_LEN, + &rtt_target_info->addr, ETHER_ADDR_LEN); + rtt_result->report.distance = FTM_INVALID; + list_add_tail(&rtt_result->list, &rtt_results_header->result_list); + rtt_results_header->result_cnt++; + rtt_results_header->result_tot_len += rtt_result->report_len; +exit: + return ret; +} + +static bool +dhd_rtt_get_report_header(rtt_status_info_t *rtt_status, + rtt_results_header_t **rtt_results_header, struct ether_addr *addr) +{ + rtt_results_header_t *entry; + GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST(); + /* find a rtt_report_header for this mac address */ + list_for_each_entry(entry, &rtt_status->rtt_results_cache, list) { + GCC_DIAGNOSTIC_POP(); + if (!memcmp(&entry->peer_mac, addr, ETHER_ADDR_LEN)) { + /* found a rtt_report_header for peer_mac in the list */ + if (rtt_results_header) { + *rtt_results_header = entry; + } + return TRUE; + } + } + return FALSE; +} + +int +dhd_rtt_handle_nan_rtt_session_end(dhd_pub_t *dhd, struct ether_addr *peer) +{ + bool is_new = TRUE; + rtt_status_info_t *rtt_status = GET_RTTSTATE(dhd); + mutex_lock(&rtt_status->rtt_mutex); + is_new = !dhd_rtt_get_report_header(rtt_status, NULL, peer); + + if (is_new) { /* no FTM result..create failure result */ + dhd_rtt_create_failure_result(rtt_status, peer); + } + dhd_rtt_handle_rtt_session_end(dhd); + mutex_unlock(&rtt_status->rtt_mutex); + return BCME_OK; +} +#endif /* WL_CFG80211 */ + +static bool +dhd_rtt_is_valid_measurement(rtt_result_t *rtt_result) +{ + bool ret = FALSE; + + if (rtt_result && (rtt_result->report.success_num != 0)) { + ret = TRUE; + } + return ret; +} + +static int +dhd_rtt_parse_result_event(wl_proxd_event_t *proxd_ev_data, + int tlvs_len, rtt_result_t *rtt_result) +{ + int ret = BCME_OK; + + /* unpack TLVs and invokes the cbfn to print the event content TLVs */ + ret = bcm_unpack_xtlv_buf((void *) rtt_result, + (uint8 *)&proxd_ev_data->tlvs[0], tlvs_len, + BCM_XTLV_OPTION_ALIGN32, rtt_unpack_xtlv_cbfn); + if (ret != BCME_OK) { + DHD_RTT_ERR(("%s : Failed to unpack xtlv for an event\n", + __FUNCTION__)); + goto exit; + } + /* fill out the results from the configuration param */ + rtt_result->report.type = RTT_TWO_WAY; + DHD_RTT(("report->ftm_num : %d\n", rtt_result->report.ftm_num)); + rtt_result->report_len = RTT_REPORT_SIZE; + rtt_result->detail_len = sizeof(rtt_result->rtt_detail); + +exit: + return ret; + +} + +static int +dhd_rtt_handle_directed_rtt_burst_end(dhd_pub_t *dhd, struct ether_addr *peer_addr, + wl_proxd_event_t *proxd_ev_data, int tlvs_len, rtt_result_t *rtt_result, bool is_nan) +{ + rtt_status_info_t *rtt_status; + rtt_results_header_t *rtt_results_header = NULL; + bool is_new = TRUE; + int ret = BCME_OK; + int err_at = 0; + + rtt_status = GET_RTTSTATE(dhd); + is_new = !dhd_rtt_get_report_header(rtt_status, + &rtt_results_header, peer_addr); + + if (tlvs_len > 0) { + if (is_new) { + /* allocate new header for rtt_results */ + rtt_results_header = (rtt_results_header_t *)MALLOCZ(rtt_status->dhd->osh, + sizeof(rtt_results_header_t)); + if (!rtt_results_header) { + ret = BCME_NORESOURCE; + err_at = 1; + goto exit; + } + /* Initialize the head of list for rtt result */ + INIT_LIST_HEAD(&rtt_results_header->result_list); + /* same src and header len */ + (void)memcpy_s(&rtt_results_header->peer_mac, ETHER_ADDR_LEN, + peer_addr, ETHER_ADDR_LEN); + list_add_tail(&rtt_results_header->list, &rtt_status->rtt_results_cache); + } + + ret = dhd_rtt_parse_result_event(proxd_ev_data, tlvs_len, rtt_result); + if ((ret == BCME_OK) && ((!is_nan) || + dhd_rtt_is_valid_measurement(rtt_result))) { + /* + * Add to list, if non-nan RTT (legacy) or + * valid measurement in nan rtt case + */ + list_add_tail(&rtt_result->list, &rtt_results_header->result_list); + rtt_results_header->result_cnt++; + rtt_results_header->result_tot_len += rtt_result->report_len + + rtt_result->detail_len; + } else { + err_at = 2; + if (ret == BCME_OK) { + /* Case for nan rtt invalid measurement */ + ret = BCME_ERROR; + err_at = 3; + } + goto exit; + } + } else { + ret = BCME_ERROR; + err_at = 4; + goto exit; + } + +exit: + if (ret != BCME_OK) { + DHD_RTT_ERR(("dhd_rtt_handle_directed_rtt_burst_end: failed, " + " ret = %d, err_at = %d\n", ret, err_at)); + if (rtt_results_header) { + list_del(&rtt_results_header->list); + kfree(rtt_results_header); + rtt_results_header = NULL; + } + } + return ret; +} + +#ifdef WL_NAN +static void +dhd_rtt_nan_range_report(struct bcm_cfg80211 *cfg, + rtt_result_t *rtt_result) +{ + wl_nan_ev_rng_rpt_ind_t range_res; + nan_ranging_inst_t *rng_inst = NULL; + dhd_pub_t *dhd = (struct dhd_pub *)(cfg->pub); + rtt_status_info_t *rtt_status = GET_RTTSTATE(dhd); + + UNUSED_PARAMETER(range_res); + + if (!dhd_rtt_is_valid_measurement(rtt_result)) { + /* Drop Invalid Measurements for NAN RTT report */ + DHD_RTT(("dhd_rtt_nan_range_report: Drop Invalid Measurements\n")); + return; + } + bzero(&range_res, sizeof(range_res)); + range_res.indication = 0; + range_res.dist_mm = rtt_result->report.distance; + /* same src and header len, ignoring ret val here */ + (void)memcpy_s(&range_res.peer_m_addr, ETHER_ADDR_LEN, + &rtt_result->report.addr, ETHER_ADDR_LEN); + wl_cfgnan_process_range_report(cfg, &range_res); + /* + * suspend geofence ranging for this target + * and move to next target + * after valid measurement for the target + */ + rng_inst = wl_cfgnan_check_for_ranging(cfg, &range_res.peer_m_addr); + if (rng_inst) { + wl_cfgnan_suspend_geofence_rng_session(bcmcfg_to_prmry_ndev(cfg), + &rng_inst->peer_addr, RTT_GEO_SUSPN_RANGE_RES_REPORTED, 0); + GEOFENCE_RTT_LOCK(rtt_status); + dhd_rtt_move_geofence_cur_target_idx_to_next(dhd); + GEOFENCE_RTT_UNLOCK(rtt_status); + wl_cfgnan_reset_geofence_ranging(cfg, + rng_inst, RTT_SCHED_RNG_RPT_GEOFENCE); + } +} + +static int +dhd_rtt_handle_nan_burst_end(dhd_pub_t *dhd, struct ether_addr *peer_addr, + wl_proxd_event_t *proxd_ev_data, int tlvs_len) +{ + struct net_device *ndev = NULL; + struct bcm_cfg80211 *cfg = NULL; + nan_ranging_inst_t *rng_inst = NULL; + rtt_status_info_t *rtt_status = NULL; + rtt_result_t *rtt_result = NULL; + bool is_geofence = FALSE; + int ret = BCME_OK; + + ndev = dhd_linux_get_primary_netdev(dhd); + cfg = wiphy_priv(ndev->ieee80211_ptr->wiphy); + + rtt_status = GET_RTTSTATE(dhd); + NULL_CHECK(rtt_status, "rtt_status is NULL", ret); + NAN_MUTEX_LOCK(); + mutex_lock(&rtt_status->rtt_mutex); + + if ((cfg->nan_enable == FALSE) || + ETHER_ISNULLADDR(peer_addr)) { + DHD_RTT_ERR(("Received Burst End with NULL ether addr, " + "or nan disable, nan_enable = %d\n", cfg->nan_enable)); + ret = BCME_UNSUPPORTED; + goto exit; + } + + rng_inst = wl_cfgnan_check_for_ranging(cfg, peer_addr); + if (rng_inst) { + is_geofence = (rng_inst->range_type + == RTT_TYPE_NAN_GEOFENCE); + } else { + DHD_RTT_ERR(("Received Burst End without Ranging Instance\n")); + ret = BCME_ERROR; + goto exit; + } + + /* allocate rtt_results for new results */ + rtt_result = (rtt_result_t *)MALLOCZ(dhd->osh, sizeof(rtt_result_t)); + if (!rtt_result) { + ret = BCME_NORESOURCE; + goto exit; + } + + if (is_geofence) { + ret = dhd_rtt_parse_result_event(proxd_ev_data, tlvs_len, rtt_result); + if (ret != BCME_OK) { + DHD_RTT_ERR(("avilog: dhd_rtt_handle_nan_burst_end: " + "dhd_rtt_parse_result_event failed\n")); + goto exit; + } + } else { + if (RTT_IS_STOPPED(rtt_status)) { + /* Ignore the Proxd event */ + DHD_RTT((" event handler rtt is stopped \n")); + if (rtt_status->flags == WL_PROXD_SESSION_FLAG_TARGET) { + DHD_RTT(("Device is target/Responder. Recv the event. \n")); + } else { + ret = BCME_UNSUPPORTED; + goto exit; + } + } + ret = dhd_rtt_handle_directed_rtt_burst_end(dhd, peer_addr, + proxd_ev_data, tlvs_len, rtt_result, TRUE); + if (ret != BCME_OK) { + goto exit; + } + + } + +exit: + mutex_unlock(&rtt_status->rtt_mutex); + if (ret == BCME_OK) { + dhd_rtt_nan_range_report(cfg, rtt_result); + } + if (rtt_result && + ((ret != BCME_OK) || is_geofence)) { + kfree(rtt_result); + rtt_result = NULL; + } + NAN_MUTEX_UNLOCK(); + return ret; +} +#endif /* WL_NAN */ int dhd_rtt_event_handler(dhd_pub_t *dhd, wl_event_msg_t *event, void *event_data) { int ret = BCME_OK; int tlvs_len; - int idx; uint16 version; wl_proxd_event_t *p_event; wl_proxd_event_type_t event_type; wl_proxd_ftm_session_status_t session_status; const ftm_strmap_entry_t *p_loginfo; + rtt_result_t *rtt_result; +#ifdef WL_CFG80211 rtt_status_info_t *rtt_status; - rtt_target_info_t *rtt_target_info; - struct rtt_noti_callback *iter; - rtt_results_header_t *entry, *next, *rtt_results_header = NULL; - rtt_result_t *rtt_result, *next2; - gfp_t kflags; + rtt_results_header_t *rtt_results_header = NULL; bool is_new = TRUE; +#endif /* WL_CFG80211 */ + DHD_RTT(("Enter %s \n", __FUNCTION__)); NULL_CHECK(dhd, "dhd is NULL", ret); - rtt_status = GET_RTTSTATE(dhd); - NULL_CHECK(rtt_status, "rtt_status is NULL", ret); if (ntoh32_ua((void *)&event->datalen) < OFFSETOF(wl_proxd_event_t, tlvs)) { DHD_RTT(("%s: wrong datalen:%d\n", __FUNCTION__, @@ -1664,46 +3477,37 @@ } event_type = ntoh32_ua((void *)&event->event_type); if (event_type != WLC_E_PROXD) { - DHD_ERROR((" failed event \n")); + DHD_RTT_ERR((" failed event \n")); return -EINVAL; } if (!event_data) { - DHD_ERROR(("%s: event_data:NULL\n", __FUNCTION__)); + DHD_RTT_ERR(("%s: event_data:NULL\n", __FUNCTION__)); return -EINVAL; - } - - if (RTT_IS_STOPPED(rtt_status)) { - /* Ignore the Proxd event */ - return ret; } p_event = (wl_proxd_event_t *) event_data; version = ltoh16(p_event->version); if (version < WL_PROXD_API_VERSION) { - DHD_ERROR(("ignore non-ftm event version = 0x%0x < WL_PROXD_API_VERSION (0x%x)\n", + DHD_RTT_ERR(("ignore non-ftm event version = 0x%0x < WL_PROXD_API_VERSION (0x%x)\n", version, WL_PROXD_API_VERSION)); return ret; } - if (!in_atomic()) { - mutex_lock(&rtt_status->rtt_mutex); - } - event_type = (wl_proxd_event_type_t) ltoh16(p_event->type); - kflags = in_softirq()? GFP_ATOMIC : GFP_KERNEL; + event_type = (wl_proxd_event_type_t) ltoh16(p_event->type); DHD_RTT(("event_type=0x%x, ntoh16()=0x%x, ltoh16()=0x%x\n", p_event->type, ntoh16(p_event->type), ltoh16(p_event->type))); p_loginfo = ftm_get_event_type_loginfo(event_type); if (p_loginfo == NULL) { - DHD_ERROR(("receive an invalid FTM event %d\n", event_type)); + DHD_RTT_ERR(("receive an invalid FTM event %d\n", event_type)); ret = -EINVAL; - goto exit; /* ignore this event */ + return ret; /* ignore this event */ } /* get TLVs len, skip over event header */ if (ltoh16(p_event->len) < OFFSETOF(wl_proxd_event_t, tlvs)) { - DHD_ERROR(("invalid FTM event length:%d\n", ltoh16(p_event->len))); + DHD_RTT_ERR(("invalid FTM event length:%d\n", ltoh16(p_event->len))); ret = -EINVAL; - goto exit; + return ret; } tlvs_len = ltoh16(p_event->len) - OFFSETOF(wl_proxd_event_t, tlvs); DHD_RTT(("receive '%s' event: version=0x%x len=%d method=%d sid=%d tlvs_len=%d\n", @@ -1713,17 +3517,38 @@ ltoh16(p_event->method), ltoh16(p_event->sid), tlvs_len)); - rtt_target_info = &rtt_status->rtt_config.target_info[rtt_status->cur_idx]; - /* find a rtt_report_header for this mac address */ - list_for_each_entry(entry, &rtt_status->rtt_results_cache, list) { - if (!memcmp(&entry->peer_mac, &event->addr, ETHER_ADDR_LEN)) { - /* found a rtt_report_header for peer_mac in the list */ - is_new = FALSE; - rtt_results_header = entry; - break; - } +#ifdef WL_CFG80211 +#ifdef WL_NAN + if ((event_type == WL_PROXD_EVENT_BURST_END) && + dhd_rtt_is_nan_peer(dhd, &event->addr)) { + DHD_RTT(("WL_PROXD_EVENT_BURST_END for NAN RTT\n")); + ret = dhd_rtt_handle_nan_burst_end(dhd, &event->addr, p_event, tlvs_len); + return ret; } +#endif /* WL_NAN */ + rtt_status = GET_RTTSTATE(dhd); + NULL_CHECK(rtt_status, "rtt_status is NULL", ret); + mutex_lock(&rtt_status->rtt_mutex); + + if (RTT_IS_STOPPED(rtt_status)) { + /* Ignore the Proxd event */ + DHD_RTT((" event handler rtt is stopped \n")); + if (rtt_status->flags == WL_PROXD_SESSION_FLAG_TARGET) { + DHD_RTT(("Device is target/Responder. Recv the event. \n")); + } else { + ret = BCME_NOTREADY; + goto exit; + } + } +#endif /* WL_CFG80211 */ + +#ifdef WL_CFG80211 + GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST(); + is_new = !dhd_rtt_get_report_header(rtt_status, + &rtt_results_header, &event->addr); + GCC_DIAGNOSTIC_POP(); +#endif /* WL_CFG80211 */ switch (event_type) { case WL_PROXD_EVENT_SESSION_CREATE: DHD_RTT(("WL_PROXD_EVENT_SESSION_CREATE\n")); @@ -1735,143 +3560,55 @@ DHD_RTT(("WL_PROXD_EVENT_BURST_START\n")); break; case WL_PROXD_EVENT_BURST_END: - DHD_RTT(("WL_PROXD_EVENT_BURST_END\n")); - if (is_new) { - /* allocate new header for rtt_results */ - rtt_results_header = kzalloc(sizeof(rtt_results_header_t), kflags); - if (!rtt_results_header) { - ret = -ENOMEM; - goto exit; - } - /* Initialize the head of list for rtt result */ - INIT_LIST_HEAD(&rtt_results_header->result_list); - rtt_results_header->peer_mac = event->addr; - list_add_tail(&rtt_results_header->list, &rtt_status->rtt_results_cache); + DHD_RTT(("WL_PROXD_EVENT_BURST_END for Legacy RTT\n")); + /* allocate rtt_results for new legacy rtt results */ + rtt_result = (rtt_result_t *)MALLOCZ(dhd->osh, sizeof(rtt_result_t)); + if (!rtt_result) { + ret = -ENOMEM; + goto exit; } - if (tlvs_len > 0) { - /* allocate rtt_results for new results */ - rtt_result = kzalloc(sizeof(rtt_result_t), kflags); - if (!rtt_result) { - ret = -ENOMEM; - goto exit; - } - /* unpack TLVs and invokes the cbfn to print the event content TLVs */ - ret = bcm_unpack_xtlv_buf((void *) &(rtt_result->report), - (uint8 *)&p_event->tlvs[0], tlvs_len, - BCM_XTLV_OPTION_ALIGN32, rtt_unpack_xtlv_cbfn); - if (ret != BCME_OK) { - DHD_ERROR(("%s : Failed to unpack xtlv for an event\n", - __FUNCTION__)); - goto exit; - } - /* fill out the results from the configuration param */ - rtt_result->report.ftm_num = rtt_target_info->num_frames_per_burst; - rtt_result->report.type = RTT_TWO_WAY; - DHD_RTT(("report->ftm_num : %d\n", rtt_result->report.ftm_num)); - rtt_result->report_len = RTT_REPORT_SIZE; - - list_add_tail(&rtt_result->list, &rtt_results_header->result_list); - rtt_results_header->result_cnt++; - rtt_results_header->result_tot_len += rtt_result->report_len; + ret = dhd_rtt_handle_directed_rtt_burst_end(dhd, &event->addr, + p_event, tlvs_len, rtt_result, FALSE); + if (rtt_result && (ret != BCME_OK)) { + kfree(rtt_result); + rtt_result = NULL; + goto exit; } break; case WL_PROXD_EVENT_SESSION_END: - DHD_RTT(("WL_PROXD_EVENT_SESSION_END\n")); + DHD_RTT(("WL_PROXD_EVENT_SESSION_END\n")); + if (dhd_rtt_is_nan_peer(dhd, &event->addr)) { + /* + * Nothing to do for session end for nan peer + * All taken care in burst end and nan rng rep + */ + break; + } +#ifdef WL_CFG80211 if (!RTT_IS_ENABLED(rtt_status)) { DHD_RTT(("Ignore the session end evt\n")); goto exit; } +#endif /* WL_CFG80211 */ if (tlvs_len > 0) { /* unpack TLVs and invokes the cbfn to print the event content TLVs */ ret = bcm_unpack_xtlv_buf((void *) &session_status, (uint8 *)&p_event->tlvs[0], tlvs_len, BCM_XTLV_OPTION_ALIGN32, rtt_unpack_xtlv_cbfn); if (ret != BCME_OK) { - DHD_ERROR(("%s : Failed to unpack xtlv for an event\n", + DHD_RTT_ERR(("%s : Failed to unpack xtlv for an event\n", __FUNCTION__)); goto exit; } } +#ifdef WL_CFG80211 /* In case of no result for the peer device, make fake result for error case */ if (is_new) { - /* allocate new header for rtt_results */ - rtt_results_header = kzalloc(sizeof(rtt_results_header_t), GFP_KERNEL); - if (!rtt_results_header) { - ret = -ENOMEM; - goto exit; - } - /* Initialize the head of list for rtt result */ - INIT_LIST_HEAD(&rtt_results_header->result_list); - rtt_results_header->peer_mac = event->addr; - list_add_tail(&rtt_results_header->list, &rtt_status->rtt_results_cache); - - /* allocate rtt_results for new results */ - rtt_result = kzalloc(sizeof(rtt_result_t), kflags); - if (!rtt_result) { - ret = -ENOMEM; - kfree(rtt_results_header); - goto exit; - } - /* fill out the results from the configuration param */ - rtt_result->report.ftm_num = rtt_target_info->num_frames_per_burst; - rtt_result->report.type = RTT_TWO_WAY; - DHD_RTT(("report->ftm_num : %d\n", rtt_result->report.ftm_num)); - rtt_result->report_len = RTT_REPORT_SIZE; - rtt_result->report.status = RTT_REASON_FAIL_NO_RSP; - rtt_result->report.addr = rtt_target_info->addr; - rtt_result->report.distance = FTM_INVALID; - list_add_tail(&rtt_result->list, &rtt_results_header->result_list); - rtt_results_header->result_cnt++; - rtt_results_header->result_tot_len += rtt_result->report_len; + dhd_rtt_create_failure_result(rtt_status, &event->addr); } - /* find next target to trigger RTT */ - for (idx = (rtt_status->cur_idx + 1); - idx < rtt_status->rtt_config.rtt_target_cnt; idx++) { - /* skip the disabled device */ - if (rtt_status->rtt_config.target_info[idx].disable) { - continue; - } else { - /* set the idx to cur_idx */ - rtt_status->cur_idx = idx; - break; - } - } - if (idx < rtt_status->rtt_config.rtt_target_cnt) { - /* restart to measure RTT from next device */ - schedule_work(&rtt_status->work); - } else { - DHD_RTT(("RTT_STOPPED\n")); - rtt_status->status = RTT_STOPPED; - /* to turn on mpc mode */ - schedule_work(&rtt_status->work); - /* notify the completed information to others */ - list_for_each_entry(iter, &rtt_status->noti_fn_list, list) { - iter->noti_fn(iter->ctx, &rtt_status->rtt_results_cache); - } - /* remove the rtt results in cache */ - if (!list_empty(&rtt_status->rtt_results_cache)) { - /* Iterate rtt_results_header list */ - list_for_each_entry_safe(entry, next, - &rtt_status->rtt_results_cache, list) { - list_del(&entry->list); - /* Iterate rtt_result list */ - list_for_each_entry_safe(rtt_result, next2, - &entry->result_list, list) { - list_del(&rtt_result->list); - kfree(rtt_result); - } - kfree(entry); - } - } - - /* reinitialize the HEAD */ - INIT_LIST_HEAD(&rtt_status->rtt_results_cache); - /* clear information for rtt_config */ - rtt_status->rtt_config.rtt_target_cnt = 0; - memset(rtt_status->rtt_config.target_info, 0, - TARGET_INFO_SIZE(RTT_MAX_TARGET_CNT)); - rtt_status->cur_idx = 0; - } + DHD_RTT(("\n Not Nan peer..proceed to notify result and restart\n")); + dhd_rtt_handle_rtt_session_end(dhd); +#endif /* WL_CFG80211 */ break; case WL_PROXD_EVENT_SESSION_RESTART: DHD_RTT(("WL_PROXD_EVENT_SESSION_RESTART\n")); @@ -1894,36 +3631,78 @@ case WL_PROXD_EVENT_RANGING: DHD_RTT(("WL_PROXD_EVENT_RANGING\n")); break; + case WL_PROXD_EVENT_COLLECT: + DHD_RTT(("WL_PROXD_EVENT_COLLECT\n")); + if (tlvs_len > 0) { + void *buffer = NULL; + if (!(buffer = (void *)MALLOCZ(dhd->osh, tlvs_len))) { + ret = -ENOMEM; + goto exit; + } + /* unpack TLVs and invokes the cbfn to print the event content TLVs */ + ret = bcm_unpack_xtlv_buf(buffer, + (uint8 *)&p_event->tlvs[0], tlvs_len, + BCM_XTLV_OPTION_NONE, rtt_unpack_xtlv_cbfn); + kfree(buffer); + if (ret != BCME_OK) { + DHD_RTT_ERR(("%s : Failed to unpack xtlv for event %d\n", + __FUNCTION__, event_type)); + goto exit; + } + } + break; + case WL_PROXD_EVENT_MF_STATS: + DHD_RTT(("WL_PROXD_EVENT_MF_STATS\n")); + if (tlvs_len > 0) { + void *buffer = NULL; + if (!(buffer = (void *)MALLOCZ(dhd->osh, tlvs_len))) { + ret = -ENOMEM; + goto exit; + } + /* unpack TLVs and invokes the cbfn to print the event content TLVs */ + ret = bcm_unpack_xtlv_buf(buffer, + (uint8 *)&p_event->tlvs[0], tlvs_len, + BCM_XTLV_OPTION_NONE, rtt_unpack_xtlv_cbfn); + kfree(buffer); + if (ret != BCME_OK) { + DHD_RTT_ERR(("%s : Failed to unpack xtlv for event %d\n", + __FUNCTION__, event_type)); + goto exit; + } + } + break; default: - DHD_ERROR(("WLC_E_PROXD: not supported EVENT Type:%d\n", event_type)); + DHD_RTT_ERR(("WLC_E_PROXD: not supported EVENT Type:%d\n", event_type)); break; } exit: - if (!in_atomic()) { - mutex_unlock(&rtt_status->rtt_mutex); - } +#ifdef WL_CFG80211 + mutex_unlock(&rtt_status->rtt_mutex); +#endif /* WL_CFG80211 */ return ret; } +#ifdef WL_CFG80211 static void dhd_rtt_work(struct work_struct *work) { rtt_status_info_t *rtt_status; dhd_pub_t *dhd; + + GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST(); rtt_status = container_of(work, rtt_status_info_t, work); - if (rtt_status == NULL) { - DHD_ERROR(("%s : rtt_status is NULL\n", __FUNCTION__)); - return; - } + GCC_DIAGNOSTIC_POP(); + dhd = rtt_status->dhd; if (dhd == NULL) { - DHD_ERROR(("%s : dhd is NULL\n", __FUNCTION__)); + DHD_RTT_ERR(("%s : dhd is NULL\n", __FUNCTION__)); return; } (void) dhd_rtt_start(dhd); } +#endif /* WL_CFG80211 */ int dhd_rtt_capability(dhd_pub_t *dhd, rtt_capabilities_t *capa) @@ -1935,29 +3714,22 @@ NULL_CHECK(rtt_status, "rtt_status is NULL", err); NULL_CHECK(capa, "capa is NULL", err); bzero(capa, sizeof(rtt_capabilities_t)); - switch (rtt_status->rtt_capa.proto) { - case RTT_CAP_ONE_WAY: - capa->rtt_one_sided_supported = 1; - break; - case RTT_CAP_FTM_WAY: - capa->rtt_ftm_supported = 1; - break; - } - switch (rtt_status->rtt_capa.feature) { - case RTT_FEATURE_LCI: + /* set rtt capabilities */ + if (rtt_status->rtt_capa.proto & RTT_CAP_ONE_WAY) + capa->rtt_one_sided_supported = 1; + if (rtt_status->rtt_capa.proto & RTT_CAP_FTM_WAY) + capa->rtt_ftm_supported = 1; + + if (rtt_status->rtt_capa.feature & RTT_FEATURE_LCI) capa->lci_support = 1; - break; - case RTT_FEATURE_LCR: + if (rtt_status->rtt_capa.feature & RTT_FEATURE_LCR) capa->lcr_support = 1; - break; - case RTT_FEATURE_PREAMBLE: + if (rtt_status->rtt_capa.feature & RTT_FEATURE_PREAMBLE) capa->preamble_support = 1; - break; - case RTT_FEATURE_BW: + if (rtt_status->rtt_capa.feature & RTT_FEATURE_BW) capa->bw_support = 1; - break; - } + /* bit mask */ capa->preamble_support = rtt_status->rtt_capa.preamble; capa->bw_support = rtt_status->rtt_capa.bw; @@ -1965,44 +3737,223 @@ return err; } +#ifdef WL_CFG80211 +int +dhd_rtt_avail_channel(dhd_pub_t *dhd, wifi_channel_info *channel_info) +{ + u32 chanspec = 0; + int err = BCME_OK; + chanspec_t c = 0; + u32 channel; + struct net_device *dev = dhd_linux_get_primary_netdev(dhd); + + if ((err = wldev_iovar_getint(dev, "chanspec", + (s32 *)&chanspec)) == BCME_OK) { + c = (chanspec_t)dtoh32(chanspec); + c = wl_chspec_driver_to_host(c); + channel = wf_chspec_ctlchan(c); + DHD_RTT((" control channel is %d \n", channel)); + if (CHSPEC_IS20(c)) { + channel_info->width = WIFI_CHAN_WIDTH_20; + DHD_RTT((" band is 20 \n")); + } else if (CHSPEC_IS40(c)) { + channel_info->width = WIFI_CHAN_WIDTH_40; + DHD_RTT(("band is 40 \n")); + } else { + channel_info->width = WIFI_CHAN_WIDTH_80; + DHD_RTT(("band is 80 \n")); + } + if (CHSPEC_IS2G(c) && (channel >= CH_MIN_2G_CHANNEL) && + (channel <= CH_MAX_2G_CHANNEL)) { + channel_info->center_freq = + ieee80211_channel_to_frequency(channel, IEEE80211_BAND_2GHZ); + } else if (CHSPEC_IS5G(c) && channel >= CH_MIN_5G_CHANNEL) { + channel_info->center_freq = + ieee80211_channel_to_frequency(channel, IEEE80211_BAND_5GHZ); + } + if ((channel_info->width == WIFI_CHAN_WIDTH_80) || + (channel_info->width == WIFI_CHAN_WIDTH_40)) { + channel = CHSPEC_CHANNEL(c); + channel_info->center_freq0 = + ieee80211_channel_to_frequency(channel, IEEE80211_BAND_5GHZ); + } + } else { + DHD_RTT_ERR(("Failed to get the chanspec \n")); + } + return err; +} + +int +dhd_rtt_enable_responder(dhd_pub_t *dhd, wifi_channel_info *channel_info) +{ + int err = BCME_OK; + char chanbuf[CHANSPEC_STR_LEN]; + int pm = PM_OFF; + int ftm_cfg_cnt = 0; + chanspec_t chanspec; + wifi_channel_info_t channel; + struct net_device *dev = dhd_linux_get_primary_netdev(dhd); + ftm_config_options_info_t ftm_configs[FTM_MAX_CONFIGS]; + ftm_config_param_info_t ftm_params[FTM_MAX_PARAMS]; + rtt_status_info_t *rtt_status; + + memset(&channel, 0, sizeof(channel)); + BCM_REFERENCE(chanbuf); + NULL_CHECK(dhd, "dhd is NULL", err); + rtt_status = GET_RTTSTATE(dhd); + NULL_CHECK(rtt_status, "rtt_status is NULL", err); + if (RTT_IS_STOPPED(rtt_status)) { + DHD_RTT(("STA responder/Target. \n")); + } + DHD_RTT(("Enter %s \n", __FUNCTION__)); + if (!dhd_is_associated(dhd, 0, NULL)) { + if (channel_info) { + channel.width = channel_info->width; + channel.center_freq = channel_info->center_freq; + channel.center_freq0 = channel_info->center_freq; + } + else { + channel.width = WIFI_CHAN_WIDTH_80; + channel.center_freq = DEFAULT_FTM_FREQ; + channel.center_freq0 = DEFAULT_FTM_CNTR_FREQ0; + } + chanspec = dhd_rtt_convert_to_chspec(channel); + DHD_RTT(("chanspec/channel set as %s for rtt.\n", + wf_chspec_ntoa(chanspec, chanbuf))); + err = wldev_iovar_setint(dev, "chanspec", chanspec); + if (err) { + DHD_RTT_ERR(("Failed to set the chanspec \n")); + } + } + rtt_status->pm = PM_OFF; + err = wldev_ioctl_get(dev, WLC_GET_PM, &rtt_status->pm, sizeof(rtt_status->pm)); + DHD_RTT(("Current PM value read %d\n", rtt_status->pm)); + if (err) { + DHD_RTT_ERR(("Failed to get the PM value \n")); + } else { + err = wldev_ioctl_set(dev, WLC_SET_PM, &pm, sizeof(pm)); + if (err) { + DHD_RTT_ERR(("Failed to set the PM \n")); + rtt_status->pm_restore = FALSE; + } else { + rtt_status->pm_restore = TRUE; + } + } + if (!RTT_IS_ENABLED(rtt_status)) { + err = dhd_rtt_ftm_enable(dhd, TRUE); + if (err) { + DHD_RTT_ERR(("Failed to enable FTM (%d)\n", err)); + goto exit; + } + DHD_RTT(("FTM enabled \n")); + } + rtt_status->status = RTT_ENABLED; + DHD_RTT(("Responder enabled \n")); + memset(ftm_configs, 0, sizeof(ftm_configs)); + memset(ftm_params, 0, sizeof(ftm_params)); + ftm_configs[ftm_cfg_cnt].enable = TRUE; + ftm_configs[ftm_cfg_cnt++].flags = WL_PROXD_SESSION_FLAG_TARGET; + rtt_status->flags = WL_PROXD_SESSION_FLAG_TARGET; + DHD_RTT(("Set the device as responder \n")); + err = dhd_rtt_ftm_config(dhd, FTM_DEFAULT_SESSION, FTM_CONFIG_CAT_OPTIONS, + ftm_configs, ftm_cfg_cnt); +exit: + if (err) { + rtt_status->status = RTT_STOPPED; + DHD_RTT_ERR(("rtt is stopped %s \n", __FUNCTION__)); + dhd_rtt_ftm_enable(dhd, FALSE); + DHD_RTT(("restoring the PM value \n")); + if (rtt_status->pm_restore) { + pm = PM_FAST; + err = wldev_ioctl_set(dev, WLC_SET_PM, &pm, sizeof(pm)); + if (err) { + DHD_RTT_ERR(("Failed to restore PM \n")); + } else { + rtt_status->pm_restore = FALSE; + } + } + } + return err; +} + +int +dhd_rtt_cancel_responder(dhd_pub_t *dhd) +{ + int err = BCME_OK; + rtt_status_info_t *rtt_status; + int pm = 0; + struct net_device *dev = dhd_linux_get_primary_netdev(dhd); + + NULL_CHECK(dhd, "dhd is NULL", err); + rtt_status = GET_RTTSTATE(dhd); + NULL_CHECK(rtt_status, "rtt_status is NULL", err); + DHD_RTT(("Enter %s \n", __FUNCTION__)); + err = dhd_rtt_ftm_enable(dhd, FALSE); + if (err) { + DHD_RTT_ERR(("failed to disable FTM (%d)\n", err)); + } + rtt_status->status = RTT_STOPPED; + if (rtt_status->pm_restore) { + pm = PM_FAST; + DHD_RTT(("pm_restore =%d \n", rtt_status->pm_restore)); + err = wldev_ioctl_set(dev, WLC_SET_PM, &pm, sizeof(pm)); + if (err) { + DHD_RTT_ERR(("Failed to restore PM \n")); + } else { + rtt_status->pm_restore = FALSE; + } + } + return err; +} +#endif /* WL_CFG80211 */ + int dhd_rtt_init(dhd_pub_t *dhd) { - int err = BCME_OK, ret; - int32 up = 1; + int err = BCME_OK; +#ifdef WL_CFG80211 + int ret; + int32 drv_up = 1; int32 version; rtt_status_info_t *rtt_status; + ftm_config_param_info_t ftm_params[FTM_MAX_PARAMS]; + int ftm_param_cnt = 0; + NULL_CHECK(dhd, "dhd is NULL", err); if (dhd->rtt_state) { return err; } - dhd->rtt_state = kzalloc(sizeof(rtt_status_info_t), GFP_KERNEL); + dhd->rtt_state = (rtt_status_info_t *)MALLOCZ(dhd->osh, + sizeof(rtt_status_info_t)); if (dhd->rtt_state == NULL) { err = BCME_NOMEM; - DHD_ERROR(("%s : failed to create rtt_state\n", __FUNCTION__)); + DHD_RTT_ERR(("%s : failed to create rtt_state\n", __FUNCTION__)); return err; } bzero(dhd->rtt_state, sizeof(rtt_status_info_t)); rtt_status = GET_RTTSTATE(dhd); rtt_status->rtt_config.target_info = - kzalloc(TARGET_INFO_SIZE(RTT_MAX_TARGET_CNT), GFP_KERNEL); + (rtt_target_info_t *)MALLOCZ(dhd->osh, + TARGET_INFO_SIZE(RTT_MAX_TARGET_CNT)); if (rtt_status->rtt_config.target_info == NULL) { - DHD_ERROR(("%s failed to allocate the target info for %d\n", + DHD_RTT_ERR(("%s failed to allocate the target info for %d\n", __FUNCTION__, RTT_MAX_TARGET_CNT)); err = BCME_NOMEM; goto exit; } rtt_status->dhd = dhd; /* need to do WLC_UP */ - dhd_wl_ioctl_cmd(dhd, WLC_UP, (char *)&up, sizeof(int32), TRUE, 0); + dhd_wl_ioctl_cmd(dhd, WLC_UP, (char *)&drv_up, sizeof(int32), TRUE, 0); ret = dhd_rtt_get_version(dhd, &version); if (ret == BCME_OK && (version == WL_PROXD_API_VERSION)) { - DHD_ERROR(("%s : FTM is supported\n", __FUNCTION__)); + DHD_RTT_ERR(("%s : FTM is supported\n", __FUNCTION__)); /* rtt_status->rtt_capa.proto |= RTT_CAP_ONE_WAY; */ rtt_status->rtt_capa.proto |= RTT_CAP_FTM_WAY; /* indicate to set tx rate */ + rtt_status->rtt_capa.feature |= RTT_FEATURE_LCI; + rtt_status->rtt_capa.feature |= RTT_FEATURE_LCR; rtt_status->rtt_capa.feature |= RTT_FEATURE_PREAMBLE; rtt_status->rtt_capa.preamble |= RTT_PREAMBLE_VHT; rtt_status->rtt_capa.preamble |= RTT_PREAMBLE_HT; @@ -2014,30 +3965,48 @@ rtt_status->rtt_capa.bw |= RTT_BW_80; } else { if ((ret != BCME_OK) || (version == 0)) { - DHD_ERROR(("%s : FTM is not supported\n", __FUNCTION__)); + DHD_RTT_ERR(("%s : FTM is not supported\n", __FUNCTION__)); } else { - DHD_ERROR(("%s : FTM version mismatch between HOST (%d) and FW (%d)\n", + DHD_RTT_ERR(("%s : FTM version mismatch between HOST (%d) and FW (%d)\n", __FUNCTION__, WL_PROXD_API_VERSION, version)); } } /* cancel all of RTT request once we got the cancel request */ rtt_status->all_cancel = TRUE; mutex_init(&rtt_status->rtt_mutex); + mutex_init(&rtt_status->geofence_mutex); INIT_LIST_HEAD(&rtt_status->noti_fn_list); INIT_LIST_HEAD(&rtt_status->rtt_results_cache); INIT_WORK(&rtt_status->work, dhd_rtt_work); + /* initialize proxd timer */ + INIT_DELAYED_WORK(&rtt_status->proxd_timeout, dhd_rtt_timeout_work); +#ifdef WL_NAN + /* initialize proxd retry timer */ + INIT_DELAYED_WORK(&rtt_status->rtt_retry_timer, dhd_rtt_retry_work); + /* initialize non zero params of geofenne cfg */ + rtt_status->geofence_cfg.cur_target_idx = DHD_RTT_INVALID_TARGET_INDEX; +#endif /* WL_NAN */ + /* Global proxd config */ + ftm_params[ftm_param_cnt].event_mask = ((1 << WL_PROXD_EVENT_BURST_END) | + (1 << WL_PROXD_EVENT_SESSION_END)); + ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_EVENT_MASK; + dhd_rtt_ftm_config(dhd, 0, FTM_CONFIG_CAT_GENERAL, + ftm_params, ftm_param_cnt); exit: if (err < 0) { kfree(rtt_status->rtt_config.target_info); kfree(dhd->rtt_state); } +#endif /* WL_CFG80211 */ return err; + } int dhd_rtt_deinit(dhd_pub_t *dhd) { int err = BCME_OK; +#ifdef WL_CFG80211 rtt_status_info_t *rtt_status; rtt_results_header_t *rtt_header, *next; rtt_result_t *rtt_result, *next2; @@ -2046,7 +4015,9 @@ rtt_status = GET_RTTSTATE(dhd); NULL_CHECK(rtt_status, "rtt_status is NULL", err); rtt_status->status = RTT_STOPPED; + DHD_RTT(("rtt is stopped %s \n", __FUNCTION__)); /* clear evt callback list */ + GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST(); if (!list_empty(&rtt_status->noti_fn_list)) { list_for_each_entry_safe(iter, iter2, &rtt_status->noti_fn_list, list) { list_del(&iter->list); @@ -2065,8 +4036,11 @@ kfree(rtt_header); } } + GCC_DIAGNOSTIC_POP(); + kfree(rtt_status->rtt_config.target_info); kfree(dhd->rtt_state); dhd->rtt_state = NULL; +#endif /* WL_CFG80211 */ return err; } -- Gitblit v1.6.2