From 76ff302d570a19baf14dd09e8781ab66cc790855 Mon Sep 17 00:00:00 2001 From: Roshan Pius Date: Fri, 28 Oct 2016 10:33:34 -0700 Subject: [PATCH 1/2] wifi: Add gscan API wrappers in WifiLegacyHal The legacy gscan API's were designed to be used in the following sequence: a) Start the scan using |wifi_start_gscan|. b) Scan Events are indicated to the caller via the provided |on_scan_event| callback. c) When one of the result events are received, the caller is expected to retrieve the cached results via |wifi_get_cached_gscan_results|. There are some extra knobs here to determine if the results need to be flushed after fetch or not. d) Any scan failures are also notified via the provided |on_scan_event| callback. e) Full scan results are delivered one by one via the provided |on_full_scan_result| callback. In our use case step (b) above is always followed by step (c), so these 2 steps have been merged together in the HIDL interface: a) Start the scan using |IWifiStaIface.startBackgroundScan|. b) Scan results are now directly delivered via |IWifiStaIfaceEventCallback.onBackgroundScanResults| callback. c) Any scan failures will be delivered via |IWifiStaIfaceEventCallback.onBackgroundScanFailure| callback. d) Full scan results are delivered one by one via |IWifiStaIfaceEventCallback.onBackgroundFullScanResult| callback. Bug: 31991459 Test: Compiles Change-Id: I0870eae095a667eec1d8de75fe1cc04a1b5a0bd3 --- wifi/1.0/default/wifi_legacy_hal.cpp | 162 ++++++++++++++++++++++++++- wifi/1.0/default/wifi_legacy_hal.h | 35 ++++++ 2 files changed, 196 insertions(+), 1 deletion(-) diff --git a/wifi/1.0/default/wifi_legacy_hal.cpp b/wifi/1.0/default/wifi_legacy_hal.cpp index 28b7f9a21b..cb4d9d45cd 100644 --- a/wifi/1.0/default/wifi_legacy_hal.cpp +++ b/wifi/1.0/default/wifi_legacy_hal.cpp @@ -28,8 +28,13 @@ namespace wifi { namespace V1_0 { namespace implementation { namespace legacy_hal { -// Constants used in the class. +// Constants ported over from the legacy HAL calling code +// (com_android_server_wifi_WifiNative.cpp). This will all be thrown +// away when this shim layer is replaced by the real vendor +// implementation. static constexpr uint32_t kMaxVersionStringLength = 256; +static constexpr uint32_t kMaxCachedGscanResults = 64; +static constexpr uint32_t kMaxGscanFrequenciesForBand = 64; // Legacy HAL functions accept "C" style function pointers, so use global // functions to pass to the legacy HAL function and store the corresponding @@ -57,6 +62,27 @@ void onFirmwareMemoryDump(char* buffer, int buffer_size) { on_firmware_memory_dump_internal_callback(buffer, buffer_size); } } + +// Callback to be invoked for Gscan events. +std::function + on_gscan_event_internal_callback; +void onGscanEvent(wifi_request_id id, wifi_scan_event event) { + if (on_gscan_event_internal_callback) { + on_gscan_event_internal_callback(id, event); + } +} + +// Callback to be invoked for Gscan full results. +std::function + on_gscan_full_result_internal_callback; +void onGscanFullResult(wifi_request_id id, + wifi_scan_result* result, + uint32_t buckets_scanned) { + if (on_gscan_full_result_internal_callback) { + on_gscan_full_result_internal_callback(id, result, buckets_scanned); + } +} + // End of the free-standing "C" style callbacks. WifiLegacyHal::WifiLegacyHal() @@ -206,6 +232,109 @@ wifi_error WifiLegacyHal::setPacketFilter(const std::vector& program) { wlan_interface_handle_, program.data(), program.size()); } +std::pair +WifiLegacyHal::getGscanCapabilities() { + wifi_gscan_capabilities caps; + wifi_error status = global_func_table_.wifi_get_gscan_capabilities( + wlan_interface_handle_, &caps); + return {status, caps}; +} + +wifi_error WifiLegacyHal::startGscan( + wifi_request_id id, + const wifi_scan_cmd_params& params, + const std::function& on_failure_user_callback, + const on_gscan_results_callback& on_results_user_callback, + const on_gscan_full_result_callback& on_full_result_user_callback) { + // If there is already an ongoing background scan, reject new scan requests. + if (on_gscan_event_internal_callback || + on_gscan_full_result_internal_callback) { + return WIFI_ERROR_NOT_AVAILABLE; + } + + // This callback will be used to either trigger |on_results_user_callback| or + // |on_failure_user_callback|. + on_gscan_event_internal_callback = + [on_failure_user_callback, on_results_user_callback, this]( + wifi_request_id id, wifi_scan_event event) { + switch (event) { + case WIFI_SCAN_RESULTS_AVAILABLE: + case WIFI_SCAN_THRESHOLD_NUM_SCANS: + case WIFI_SCAN_THRESHOLD_PERCENT: { + wifi_error status; + std::vector cached_scan_results; + std::tie(status, cached_scan_results) = getGscanCachedResults(); + if (status == WIFI_SUCCESS) { + on_results_user_callback(id, cached_scan_results); + return; + } + } + // Fall through if failed. Failure to retrieve cached scan results + // should trigger a background scan failure. + case WIFI_SCAN_FAILED: + on_failure_user_callback(id); + on_gscan_event_internal_callback = nullptr; + on_gscan_full_result_internal_callback = nullptr; + return; + } + LOG(FATAL) << "Unexpected gscan event received: " << event; + }; + + on_gscan_full_result_internal_callback = [on_full_result_user_callback]( + wifi_request_id id, wifi_scan_result* result, uint32_t buckets_scanned) { + if (result) { + on_full_result_user_callback(id, result, buckets_scanned); + } + }; + + wifi_scan_result_handler handler = {onGscanFullResult, onGscanEvent}; + wifi_error status = global_func_table_.wifi_start_gscan( + id, wlan_interface_handle_, params, handler); + if (status != WIFI_SUCCESS) { + on_gscan_event_internal_callback = nullptr; + on_gscan_full_result_internal_callback = nullptr; + } + return status; +} + +wifi_error WifiLegacyHal::stopGscan(wifi_request_id id) { + // If there is no an ongoing background scan, reject stop requests. + // TODO(b/32337212): This needs to be handled by the HIDL object because we + // need to return the NOT_STARTED error code. + if (!on_gscan_event_internal_callback && + !on_gscan_full_result_internal_callback) { + return WIFI_ERROR_NOT_AVAILABLE; + } + wifi_error status = + global_func_table_.wifi_stop_gscan(id, wlan_interface_handle_); + // If the request Id is wrong, don't stop the ongoing background scan. Any + // other error should be treated as the end of background scan. + if (status != WIFI_ERROR_INVALID_REQUEST_ID) { + on_gscan_event_internal_callback = nullptr; + on_gscan_full_result_internal_callback = nullptr; + } + return status; +} + +std::pair> +WifiLegacyHal::getValidFrequenciesForGscan(wifi_band band) { + static_assert(sizeof(uint32_t) >= sizeof(wifi_channel), + "Wifi Channel cannot be represented in output"); + std::vector freqs; + freqs.resize(kMaxGscanFrequenciesForBand); + int32_t num_freqs = 0; + wifi_error status = global_func_table_.wifi_get_valid_channels( + wlan_interface_handle_, + band, + freqs.size(), + reinterpret_cast(freqs.data()), + &num_freqs); + CHECK(num_freqs >= 0 && + static_cast(num_freqs) <= kMaxGscanFrequenciesForBand); + freqs.resize(num_freqs); + return {status, std::move(freqs)}; +} + wifi_error WifiLegacyHal::retrieveWlanInterfaceHandle() { const std::string& ifname_to_find = getStaIfaceName(); wifi_interface_handle* iface_handles = nullptr; @@ -245,12 +374,43 @@ void WifiLegacyHal::runEventLoop() { if_tool.SetWifiUpState(false); } +std::pair> +WifiLegacyHal::getGscanCachedResults() { + std::vector cached_scan_results; + cached_scan_results.resize(kMaxCachedGscanResults); + int32_t num_results = 0; + wifi_error status = global_func_table_.wifi_get_cached_gscan_results( + wlan_interface_handle_, + true /* always flush */, + cached_scan_results.size(), + cached_scan_results.data(), + &num_results); + CHECK(num_results >= 0 && + static_cast(num_results) <= kMaxCachedGscanResults); + cached_scan_results.resize(num_results); + // Check for invalid IE lengths in these cached scan results and correct it. + for (auto& cached_scan_result : cached_scan_results) { + int num_scan_results = cached_scan_result.num_results; + for (int i = 0; i < num_scan_results; i++) { + auto& scan_result = cached_scan_result.results[i]; + if (scan_result.ie_length > 0) { + LOG(ERROR) << "Cached scan result has non-zero IE length " + << scan_result.ie_length; + scan_result.ie_length = 0; + } + } + } + return {status, std::move(cached_scan_results)}; +} + void WifiLegacyHal::invalidate() { global_handle_ = nullptr; wlan_interface_handle_ = nullptr; on_stop_complete_internal_callback = nullptr; on_driver_memory_dump_internal_callback = nullptr; on_firmware_memory_dump_internal_callback = nullptr; + on_gscan_event_internal_callback = nullptr; + on_gscan_full_result_internal_callback = nullptr; } } // namespace legacy_hal diff --git a/wifi/1.0/default/wifi_legacy_hal.h b/wifi/1.0/default/wifi_legacy_hal.h index 8bd146a167..4026834e1f 100644 --- a/wifi/1.0/default/wifi_legacy_hal.h +++ b/wifi/1.0/default/wifi_legacy_hal.h @@ -39,6 +39,16 @@ struct PacketFilterCapabilities { uint32_t max_len; }; +// Full scan results contain IE info and are hence passed by reference, to +// preserve the variable length array member |ie_data|. Callee must not retain +// the pointer. +using on_gscan_full_result_callback = + std::function; +// These scan results don't contain any IE info, so no need to pass by +// reference. +using on_gscan_results_callback = std::function&)>; + /** * Class that encapsulates all legacy HAL interactions. * This class manages the lifetime of the event loop thread used by legacy HAL. @@ -65,12 +75,37 @@ class WifiLegacyHal { // APF functions. std::pair getPacketFilterCapabilities(); wifi_error setPacketFilter(const std::vector& program); + // Gscan functions. + std::pair getGscanCapabilities(); + // These API's provides a simplified interface over the legacy Gscan API's: + // a) All scan events from the legacy HAL API other than the + // |WIFI_SCAN_FAILED| are treated as notification of results. + // This method then retrieves the cached scan results from the legacy + // HAL API and triggers the externally provided |on_results_user_callback| + // on success. + // b) |WIFI_SCAN_FAILED| scan event or failure to retrieve cached scan results + // triggers the externally provided |on_failure_user_callback|. + // c) Full scan result event triggers the externally provided + // |on_full_result_user_callback|. + wifi_error startGscan( + wifi_request_id id, + const wifi_scan_cmd_params& params, + const std::function& on_failure_callback, + const on_gscan_results_callback& on_results_callback, + const on_gscan_full_result_callback& on_full_result_callback); + wifi_error stopGscan(wifi_request_id id); + std::pair> getValidFrequenciesForGscan( + wifi_band band); private: // Retrieve the interface handle to be used for the "wlan" interface. wifi_error retrieveWlanInterfaceHandle(); // Run the legacy HAL event loop thread. void runEventLoop(); + // Retrieve the cached gscan results to pass the results back to the external + // callbacks. + std::pair> + getGscanCachedResults(); void invalidate(); // Event loop thread used by legacy HAL. From 7cece41299945d95fd85579c57d3218109313b8b Mon Sep 17 00:00:00 2001 From: Roshan Pius Date: Fri, 28 Oct 2016 10:38:21 -0700 Subject: [PATCH 2/2] wifi: Add link layer stats API wrappers in WifiLegacyHal Adds wrappers over the legacy HAL API for the following: 1. enable/disable link layer stats collection. 2. fetch link layer stats. Note: Link layer stats structure is quite ugly. The wrapper stucture declared here (LinkLayerStats) tries to separate out the pointer elements and ignore the unnecessary variable size elements from them. Bug: 31991459 Test: Compiles Change-Id: I7c4188115786542866c7be56cf9f116b3f78e6a3 --- wifi/1.0/default/wifi_legacy_hal.cpp | 68 ++++++++++++++++++++++++++++ wifi/1.0/default/wifi_legacy_hal.h | 19 ++++++++ 2 files changed, 87 insertions(+) diff --git a/wifi/1.0/default/wifi_legacy_hal.cpp b/wifi/1.0/default/wifi_legacy_hal.cpp index cb4d9d45cd..15b6bfc74f 100644 --- a/wifi/1.0/default/wifi_legacy_hal.cpp +++ b/wifi/1.0/default/wifi_legacy_hal.cpp @@ -35,6 +35,7 @@ namespace legacy_hal { static constexpr uint32_t kMaxVersionStringLength = 256; static constexpr uint32_t kMaxCachedGscanResults = 64; static constexpr uint32_t kMaxGscanFrequenciesForBand = 64; +static constexpr uint32_t kLinkLayerStatsDataMpduSizeThreshold = 128; // Legacy HAL functions accept "C" style function pointers, so use global // functions to pass to the legacy HAL function and store the corresponding @@ -83,6 +84,19 @@ void onGscanFullResult(wifi_request_id id, } } +// Callback to be invoked for link layer stats results. +std::function + on_link_layer_stats_result_internal_callback; +void onLinkLayerStatsDataResult(wifi_request_id id, + wifi_iface_stat* iface_stat, + int num_radios, + wifi_radio_stat* radio_stat) { + if (on_link_layer_stats_result_internal_callback) { + on_link_layer_stats_result_internal_callback( + id, iface_stat, num_radios, radio_stat); + } +} + // End of the free-standing "C" style callbacks. WifiLegacyHal::WifiLegacyHal() @@ -335,6 +349,59 @@ WifiLegacyHal::getValidFrequenciesForGscan(wifi_band band) { return {status, std::move(freqs)}; } +wifi_error WifiLegacyHal::enableLinkLayerStats(bool debug) { + wifi_link_layer_params params; + params.mpdu_size_threshold = kLinkLayerStatsDataMpduSizeThreshold; + params.aggressive_statistics_gathering = debug; + return global_func_table_.wifi_set_link_stats(wlan_interface_handle_, params); +} + +wifi_error WifiLegacyHal::disableLinkLayerStats() { + // TODO: Do we care about these responses? + uint32_t clear_mask_rsp; + uint8_t stop_rsp; + return global_func_table_.wifi_clear_link_stats( + wlan_interface_handle_, 0xFFFFFFFF, &clear_mask_rsp, 1, &stop_rsp); +} + +std::pair WifiLegacyHal::getLinkLayerStats() { + LinkLayerStats link_stats{}; + LinkLayerStats* link_stats_ptr = &link_stats; + + on_link_layer_stats_result_internal_callback = [&link_stats_ptr]( + wifi_request_id /* id */, + wifi_iface_stat* iface_stats_ptr, + int num_radios, + wifi_radio_stat* radio_stats_ptr) { + if (iface_stats_ptr != nullptr) { + link_stats_ptr->iface = *iface_stats_ptr; + link_stats_ptr->iface.num_peers = 0; + } else { + LOG(ERROR) << "Invalid iface stats in link layer stats"; + } + if (num_radios == 1 && radio_stats_ptr != nullptr) { + link_stats_ptr->radio = *radio_stats_ptr; + // Copy over the tx level array to the separate vector. + if (radio_stats_ptr->num_tx_levels > 0 && + radio_stats_ptr->tx_time_per_levels != nullptr) { + link_stats_ptr->radio_tx_time_per_levels.assign( + radio_stats_ptr->tx_time_per_levels, + radio_stats_ptr->tx_time_per_levels + + radio_stats_ptr->num_tx_levels); + } + link_stats_ptr->radio.num_tx_levels = 0; + link_stats_ptr->radio.tx_time_per_levels = nullptr; + } else { + LOG(ERROR) << "Invalid radio stats in link layer stats"; + } + }; + + wifi_error status = global_func_table_.wifi_get_link_stats( + 0, wlan_interface_handle_, {onLinkLayerStatsDataResult}); + on_link_layer_stats_result_internal_callback = nullptr; + return {status, link_stats}; +} + wifi_error WifiLegacyHal::retrieveWlanInterfaceHandle() { const std::string& ifname_to_find = getStaIfaceName(); wifi_interface_handle* iface_handles = nullptr; @@ -411,6 +478,7 @@ void WifiLegacyHal::invalidate() { on_firmware_memory_dump_internal_callback = nullptr; on_gscan_event_internal_callback = nullptr; on_gscan_full_result_internal_callback = nullptr; + on_link_layer_stats_result_internal_callback = nullptr; } } // namespace legacy_hal diff --git a/wifi/1.0/default/wifi_legacy_hal.h b/wifi/1.0/default/wifi_legacy_hal.h index 4026834e1f..df1c3d6cea 100644 --- a/wifi/1.0/default/wifi_legacy_hal.h +++ b/wifi/1.0/default/wifi_legacy_hal.h @@ -39,6 +39,21 @@ struct PacketFilterCapabilities { uint32_t max_len; }; +// WARNING: We don't care about the variable sized members of either +// |wifi_iface_stat|, |wifi_radio_stat| structures. So, using the pragma +// to escape the compiler warnings regarding this. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wgnu-variable-sized-type-not-at-end" +// The |wifi_radio_stat.tx_time_per_levels| stats is provided as a pointer in +// |wifi_radio_stat| structure in the legacy HAL API. Separate that out +// into a separate return element to avoid passing pointers around. +struct LinkLayerStats { + wifi_iface_stat iface; + wifi_radio_stat radio; + std::vector radio_tx_time_per_levels; +}; +#pragma GCC diagnostic pop + // Full scan results contain IE info and are hence passed by reference, to // preserve the variable length array member |ie_data|. Callee must not retain // the pointer. @@ -96,6 +111,10 @@ class WifiLegacyHal { wifi_error stopGscan(wifi_request_id id); std::pair> getValidFrequenciesForGscan( wifi_band band); + // Link layer stats functions. + wifi_error enableLinkLayerStats(bool debug); + wifi_error disableLinkLayerStats(); + std::pair getLinkLayerStats(); private: // Retrieve the interface handle to be used for the "wlan" interface.