/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "chpp/clients/wifi.h" #include #include #include #include #include #include "chpp/app.h" #include "chpp/clients.h" #include "chpp/clients/discovery.h" #ifdef CHPP_CLIENT_ENABLED_TIMESYNC #include "chpp/clients/timesync.h" #endif #include "chpp/common/standard_uuids.h" #include "chpp/common/wifi.h" #include "chpp/common/wifi_types.h" #include "chpp/common/wifi_utils.h" #include "chpp/log.h" #include "chpp/macros.h" #include "chpp/memory.h" #include "chpp/time.h" #include "chre/pal/wifi.h" #include "chre_api/chre/wifi.h" #ifndef CHPP_WIFI_DISCOVERY_TIMEOUT_MS #define CHPP_WIFI_DISCOVERY_TIMEOUT_MS CHPP_DISCOVERY_DEFAULT_TIMEOUT_MS #endif #ifndef CHPP_WIFI_MAX_TIMESYNC_AGE_NS #define CHPP_WIFI_MAX_TIMESYNC_AGE_NS CHPP_TIMESYNC_DEFAULT_MAX_AGE_NS #endif #ifndef CHPP_WIFI_SCAN_RESULT_TIMEOUT_NS #define CHPP_WIFI_SCAN_RESULT_TIMEOUT_NS \ (CHRE_WIFI_SCAN_RESULT_TIMEOUT_NS - CHRE_NSEC_PER_SEC) #endif /************************************************ * Prototypes ***********************************************/ static enum ChppAppErrorCode chppDispatchWifiResponse(void *clientContext, uint8_t *buf, size_t len); static enum ChppAppErrorCode chppDispatchWifiNotification(void *clientContext, uint8_t *buf, size_t len); static bool chppWifiClientInit(void *clientContext, uint8_t handle, struct ChppVersion serviceVersion); static void chppWifiClientDeinit(void *clientContext); static void chppWifiClientNotifyReset(void *clientContext); static void chppWifiClientNotifyMatch(void *clientContext); /************************************************ * Private Definitions ***********************************************/ /** * Structure to maintain state for the WiFi client and its Request/Response * (RR) functionality. */ struct ChppWifiClientState { struct ChppEndpointState client; // CHPP client state const struct chrePalWifiApi *api; // WiFi PAL API struct ChppOutgoingRequestState outReqStates[CHPP_WIFI_CLIENT_REQUEST_MAX + 1]; uint32_t capabilities; // Cached GetCapabilities result bool scanMonitorEnabled; // Scan monitoring is enabled bool scanMonitorSilenceCallback; // Silence callback during recovery from a // service reset bool capabilitiesValid; // Flag to indicate if the capabilities result // is valid }; // Note: This global definition of gWifiClientContext supports only one // instance of the CHPP WiFi client at a time. struct ChppWifiClientState gWifiClientContext; static const struct chrePalSystemApi *gSystemApi; static const struct chrePalWifiCallbacks *gCallbacks; /** * Configuration parameters for this client */ static const struct ChppClient kWifiClientConfig = { .descriptor.uuid = CHPP_UUID_WIFI_STANDARD, // Version .descriptor.version.major = 1, .descriptor.version.minor = 0, .descriptor.version.patch = 0, // Notifies client if CHPP is reset .resetNotifierFunctionPtr = &chppWifiClientNotifyReset, // Notifies client if they are matched to a service .matchNotifierFunctionPtr = &chppWifiClientNotifyMatch, // Service response dispatch function pointer .responseDispatchFunctionPtr = &chppDispatchWifiResponse, // Service notification dispatch function pointer .notificationDispatchFunctionPtr = &chppDispatchWifiNotification, // Service response dispatch function pointer .initFunctionPtr = &chppWifiClientInit, // Service notification dispatch function pointer .deinitFunctionPtr = &chppWifiClientDeinit, // Number of request-response states in the outReqStates array. .outReqCount = ARRAY_SIZE(gWifiClientContext.outReqStates), // Min length is the entire header .minLength = sizeof(struct ChppAppHeader), }; /************************************************ * Prototypes ***********************************************/ static bool chppWifiClientOpen(const struct chrePalSystemApi *systemApi, const struct chrePalWifiCallbacks *callbacks); static void chppWifiClientClose(void); static uint32_t chppWifiClientGetCapabilities(void); static bool chppWifiClientConfigureScanMonitor(bool enable); static bool chppWifiClientRequestScan(const struct chreWifiScanParams *params); static void chppWifiClientReleaseScanEvent(struct chreWifiScanEvent *event); static bool chppWifiClientRequestRanging( const struct chreWifiRangingParams *params); static void chppWifiClientReleaseRangingEvent( struct chreWifiRangingEvent *event); static void chppWiFiRecoverScanMonitor( struct ChppWifiClientState *clientContext); static void chppWifiCloseResult(struct ChppWifiClientState *clientContext, uint8_t *buf, size_t len); static void chppWifiGetCapabilitiesResult( struct ChppWifiClientState *clientContext, uint8_t *buf, size_t len); static void chppWifiConfigureScanMonitorResult( struct ChppWifiClientState *clientContext, uint8_t *buf, size_t len); static void chppWifiRequestScanResult(struct ChppWifiClientState *clientContext, uint8_t *buf, size_t len); static void chppWifiRequestRangingResult( struct ChppWifiClientState *clientContext, uint8_t *buf, size_t len); static void chppWifiRequestNanSubscribeResult(uint8_t *buf, size_t len); static void chppWifiScanEventNotification( struct ChppWifiClientState *clientContext, uint8_t *buf, size_t len); static void chppWifiRangingEventNotification( struct ChppWifiClientState *clientContext, uint8_t *buf, size_t len); static void chppWifiDiscoveryEventNotification(uint8_t *buf, size_t len); static void chppWifiNanServiceLostEventNotification(uint8_t *buf, size_t len); static void chppWifiNanServiceTerminatedEventNotification(uint8_t *buf, size_t len); static void chppWifiRequestNanSubscribeNotification(uint8_t *buf, size_t len); static void chppWifiNanSubscriptionCanceledNotification(uint8_t *buf, size_t len); static void chppWifiNanSubscriptionCanceledResult(uint8_t *buf, size_t len); /************************************************ * Private Functions ***********************************************/ /** * Dispatches a service response from the transport layer that is determined to * be for the WiFi client. * * This function is called from the app layer using its function pointer given * during client registration. * * @param clientContext Maintains status for each client instance. * @param buf Input data. Cannot be null. * @param len Length of input data in bytes. * * @return Indicates the result of this function call. */ static enum ChppAppErrorCode chppDispatchWifiResponse(void *clientContext, uint8_t *buf, size_t len) { struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf; struct ChppWifiClientState *wifiClientContext = (struct ChppWifiClientState *)clientContext; enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE; if (rxHeader->command > CHPP_WIFI_CLIENT_REQUEST_MAX) { error = CHPP_APP_ERROR_INVALID_COMMAND; } else if (!chppTimestampIncomingResponse( wifiClientContext->client.appContext, &wifiClientContext->outReqStates[rxHeader->command], rxHeader)) { error = CHPP_APP_ERROR_UNEXPECTED_RESPONSE; } else { switch (rxHeader->command) { case CHPP_WIFI_OPEN: { chppClientProcessOpenResponse(&wifiClientContext->client, buf, len); chppWiFiRecoverScanMonitor(wifiClientContext); break; } case CHPP_WIFI_CLOSE: { chppWifiCloseResult(wifiClientContext, buf, len); break; } case CHPP_WIFI_GET_CAPABILITIES: { chppWifiGetCapabilitiesResult(wifiClientContext, buf, len); break; } case CHPP_WIFI_CONFIGURE_SCAN_MONITOR_ASYNC: { chppWifiConfigureScanMonitorResult(wifiClientContext, buf, len); break; } case CHPP_WIFI_REQUEST_SCAN_ASYNC: { chppWifiRequestScanResult(wifiClientContext, buf, len); break; } case CHPP_WIFI_REQUEST_RANGING_ASYNC: case CHPP_WIFI_REQUEST_NAN_RANGING_ASYNC: { chppWifiRequestRangingResult(wifiClientContext, buf, len); break; } case CHPP_WIFI_REQUEST_NAN_SUB: { chppWifiRequestNanSubscribeResult(buf, len); break; } case CHPP_WIFI_REQUEST_NAN_SUB_CANCEL: { chppWifiNanSubscriptionCanceledResult(buf, len); break; } default: { error = CHPP_APP_ERROR_INVALID_COMMAND; break; } } } return error; } /** * Dispatches a service notification from the transport layer that is determined * to be for the WiFi client. * * This function is called from the app layer using its function pointer given * during client registration. * * @param clientContext Maintains status for each client instance. * @param buf Input data. Cannot be null. * @param len Length of input data in bytes. * * @return Indicates the result of this function call. */ static enum ChppAppErrorCode chppDispatchWifiNotification(void *clientContext, uint8_t *buf, size_t len) { struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf; struct ChppWifiClientState *wifiClientContext = (struct ChppWifiClientState *)clientContext; enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE; switch (rxHeader->command) { case CHPP_WIFI_REQUEST_SCAN_ASYNC: { chppWifiScanEventNotification(wifiClientContext, buf, len); break; } case CHPP_WIFI_REQUEST_RANGING_ASYNC: case CHPP_WIFI_REQUEST_NAN_RANGING_ASYNC: { chppWifiRangingEventNotification(wifiClientContext, buf, len); break; } case CHPP_WIFI_NOTIFICATION_NAN_SERVICE_DISCOVERY: { chppWifiDiscoveryEventNotification(buf, len); break; } case CHPP_WIFI_NOTIFICATION_NAN_SERVICE_LOST: { chppWifiNanServiceLostEventNotification(buf, len); break; } case CHPP_WIFI_NOTIFICATION_NAN_SERVICE_TERMINATED: { chppWifiNanServiceTerminatedEventNotification(buf, len); break; } case CHPP_WIFI_REQUEST_NAN_SUB: { chppWifiRequestNanSubscribeNotification(buf, len); break; } case CHPP_WIFI_REQUEST_NAN_SUB_CANCEL: { chppWifiNanSubscriptionCanceledNotification(buf, len); break; } default: { error = CHPP_APP_ERROR_INVALID_COMMAND; break; } } return error; } /** * Initializes the client and provides its handle number and the version of the * matched service when/if it the client is matched with a service during * discovery. * * @param clientContext Maintains status for each client instance. * @param handle Handle number for this client. * @param serviceVersion Version of the matched service. * * @return True if client is compatible and successfully initialized. */ static bool chppWifiClientInit(void *clientContext, uint8_t handle, struct ChppVersion serviceVersion) { UNUSED_VAR(serviceVersion); struct ChppWifiClientState *wifiClientContext = (struct ChppWifiClientState *)clientContext; chppClientInit(&wifiClientContext->client, handle); return true; } /** * Deinitializes the client. * * @param clientContext Maintains status for each client instance. */ static void chppWifiClientDeinit(void *clientContext) { struct ChppWifiClientState *wifiClientContext = (struct ChppWifiClientState *)clientContext; chppClientDeinit(&wifiClientContext->client); } /** * Notifies the client of an incoming reset. * * @param clientContext Maintains status for each client instance. */ static void chppWifiClientNotifyReset(void *clientContext) { struct ChppWifiClientState *wifiClientContext = (struct ChppWifiClientState *)clientContext; chppClientCloseOpenRequests(&wifiClientContext->client, &kWifiClientConfig, false /* clearOnly */); chppCheckWifiScanEventNotificationReset(); if (wifiClientContext->client.openState != CHPP_OPEN_STATE_OPENED && !wifiClientContext->client.pseudoOpen) { CHPP_LOGW("WiFi client reset but wasn't open"); } else { CHPP_LOGI("WiFi client reopening from state=%" PRIu8, wifiClientContext->client.openState); chppClientSendOpenRequest(&wifiClientContext->client, &wifiClientContext->outReqStates[CHPP_WIFI_OPEN], CHPP_WIFI_OPEN, /*blocking=*/false); } } /** * Notifies the client of being matched to a service. * * @param clientContext Maintains status for each client instance. */ static void chppWifiClientNotifyMatch(void *clientContext) { struct ChppWifiClientState *wifiClientContext = (struct ChppWifiClientState *)clientContext; if (wifiClientContext->client.pseudoOpen) { CHPP_LOGD("Pseudo-open WiFi client opening"); chppClientSendOpenRequest(&wifiClientContext->client, &wifiClientContext->outReqStates[CHPP_WIFI_OPEN], CHPP_WIFI_OPEN, /*blocking=*/false); } } /** * Restores the state of scan monitoring after an incoming reset. * * @param clientContext Maintains status for each client instance. */ static void chppWiFiRecoverScanMonitor( struct ChppWifiClientState *clientContext) { if (clientContext->scanMonitorEnabled) { CHPP_LOGD("Re-enabling WiFi scan monitoring after reset"); clientContext->scanMonitorEnabled = false; clientContext->scanMonitorSilenceCallback = true; if (!chppWifiClientConfigureScanMonitor(true)) { clientContext->scanMonitorSilenceCallback = false; CHPP_DEBUG_ASSERT_LOG(false, "Failed to re-enable WiFi scan monitoring"); } } } /** * Handles the service response for the close client request. * * This function is called from chppDispatchWifiResponse(). * * @param clientContext Maintains status for each client instance. * @param buf Input data. Cannot be null. * @param len Length of input data in bytes. */ static void chppWifiCloseResult(struct ChppWifiClientState *clientContext, uint8_t *buf, size_t len) { // TODO UNUSED_VAR(clientContext); UNUSED_VAR(buf); UNUSED_VAR(len); } /** * Handles the service response for the get capabilities client request. * * This function is called from chppDispatchWifiResponse(). * * @param clientContext Maintains status for each client instance. * @param buf Input data. Cannot be null. * @param len Length of input data in bytes. */ static void chppWifiGetCapabilitiesResult( struct ChppWifiClientState *clientContext, uint8_t *buf, size_t len) { if (len < sizeof(struct ChppWifiGetCapabilitiesResponse)) { CHPP_LOGE("Bad WiFi capabilities len=%" PRIuSIZE, len); } else { struct ChppWifiGetCapabilitiesParameters *result = &((struct ChppWifiGetCapabilitiesResponse *)buf)->params; CHPP_LOGD("chppWifiGetCapabilitiesResult received capabilities=0x%" PRIx32, result->capabilities); CHPP_ASSERT((result->capabilities & CHPP_WIFI_DEFAULT_CAPABILITIES) == CHPP_WIFI_DEFAULT_CAPABILITIES); if (result->capabilities != CHPP_WIFI_DEFAULT_CAPABILITIES) { CHPP_LOGE("WiFi capabilities 0x%" PRIx32 " != 0x%" PRIx32, result->capabilities, CHPP_WIFI_DEFAULT_CAPABILITIES); } clientContext->capabilitiesValid = true; clientContext->capabilities = result->capabilities; } } /** * Handles the service response for the Configure Scan Monitor client request. * * This function is called from chppDispatchWifiResponse(). * * @param clientContext Maintains status for each client instance. * @param buf Input data. Cannot be null. * @param len Length of input data in bytes. */ static void chppWifiConfigureScanMonitorResult( struct ChppWifiClientState *clientContext, uint8_t *buf, size_t len) { UNUSED_VAR(clientContext); if (len < sizeof(struct ChppWifiConfigureScanMonitorAsyncResponse)) { // Short response length indicates an error gCallbacks->scanMonitorStatusChangeCallback( false, chppAppShortResponseErrorHandler(buf, len, "ScanMonitor")); } else { struct ChppWifiConfigureScanMonitorAsyncResponseParameters *result = &((struct ChppWifiConfigureScanMonitorAsyncResponse *)buf)->params; gWifiClientContext.scanMonitorEnabled = result->enabled; CHPP_LOGD( "chppWifiConfigureScanMonitorResult received enable=%d, " "errorCode=%" PRIu8, result->enabled, result->errorCode); if (!gWifiClientContext.scanMonitorSilenceCallback) { // Per the scanMonitorStatusChangeCallback API contract, unsolicited // calls to scanMonitorStatusChangeCallback must not be made, and it // should only be invoked as the direct result of an earlier call to // configureScanMonitor. gCallbacks->scanMonitorStatusChangeCallback(result->enabled, result->errorCode); } // Else, the WiFi subsystem has been reset and we are required to // silently reenable the scan monitor. gWifiClientContext.scanMonitorSilenceCallback = false; } } /** * Handles the service response for the Request Scan Result client request. * * This function is called from chppDispatchWifiResponse(). * * @param clientContext Maintains status for each client instance. * @param buf Input data. Cannot be null. * @param len Length of input data in bytes. */ static void chppWifiRequestScanResult(struct ChppWifiClientState *clientContext, uint8_t *buf, size_t len) { UNUSED_VAR(clientContext); if (len < sizeof(struct ChppWifiRequestScanResponse)) { // Short response length indicates an error gCallbacks->scanResponseCallback( false, chppAppShortResponseErrorHandler(buf, len, "ScanRequest")); } else { struct ChppWifiRequestScanResponseParameters *result = &((struct ChppWifiRequestScanResponse *)buf)->params; CHPP_LOGI("Scan request success=%d at service", result->pending); gCallbacks->scanResponseCallback(result->pending, result->errorCode); } } /** * Handles the service response for the Request Ranging Result client request. * * This function is called from chppDispatchWifiResponse(). * * @param clientContext Maintains status for each client instance. * @param buf Input data. Cannot be null. * @param len Length of input data in bytes. */ static void chppWifiRequestRangingResult( struct ChppWifiClientState *clientContext, uint8_t *buf, size_t len) { UNUSED_VAR(clientContext); UNUSED_VAR(len); struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf; if (rxHeader->error != CHPP_APP_ERROR_NONE) { gCallbacks->rangingEventCallback(chppAppErrorToChreError(rxHeader->error), NULL); } else { CHPP_LOGD("Ranging request accepted at service"); } } /** * Handles the service response for the NAN subscribe client request. * * This function is called from chppDispatchWifiResponse(). * * @param buf Input data. Cannot be null. * @param len Length of input data in bytes. */ static void chppWifiRequestNanSubscribeResult(uint8_t *buf, size_t len) { UNUSED_VAR(len); struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf; if (rxHeader->error != CHPP_APP_ERROR_NONE) { gCallbacks->nanServiceIdentifierCallback( chppAppErrorToChreError(rxHeader->error), 0 /* subscriptionId */); } else { CHPP_LOGD("NAN sub accepted at service"); } } /** * Handles the service response for the NAN subscription cancel client request. * * This function is called from chppDispatchWifiResponse(). * * @param buf Input data. Cannot be null. * @param len Length of input data in bytes. */ static void chppWifiNanSubscriptionCanceledResult(uint8_t *buf, size_t len) { UNUSED_VAR(len); struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf; if (rxHeader->error != CHPP_APP_ERROR_NONE) { gCallbacks->nanSubscriptionCanceledCallback( chppAppErrorToChreError(rxHeader->error), 0 /* subscriptionId */); } else { CHPP_LOGD("NAN sub cancel accepted at service"); } } /** * Handles the WiFi scan event service notification. * * This function is called from chppDispatchWifiNotification(). * * @param clientContext Maintains status for each client instance. * @param buf Input data. Cannot be null. * @param len Length of input data in bytes. */ static void chppWifiScanEventNotification( struct ChppWifiClientState *clientContext, uint8_t *buf, size_t len) { UNUSED_VAR(clientContext); CHPP_LOGD("chppWifiScanEventNotification received data len=%" PRIuSIZE, len); buf += sizeof(struct ChppAppHeader); len -= sizeof(struct ChppAppHeader); struct chreWifiScanEvent *chre = chppWifiScanEventToChre((struct ChppWifiScanEvent *)buf, len); if (chre == NULL) { CHPP_LOGE("Scan event conversion failed len=%" PRIuSIZE, len); } else { #ifdef CHPP_CLIENT_ENABLED_TIMESYNC uint64_t correctedTime = chre->referenceTime - (uint64_t)chppTimesyncGetOffset(gWifiClientContext.client.appContext, CHPP_WIFI_MAX_TIMESYNC_AGE_NS); uint64_t currentTime = chppGetCurrentTimeNs(); if (correctedTime > currentTime) { CHPP_LOGD("WiFi scan time overcorrected %" PRIu64 " current %" PRIu64, correctedTime / CHPP_NSEC_PER_MSEC, currentTime / CHPP_NSEC_PER_MSEC); correctedTime = currentTime; } CHPP_LOGD("WiFi scan time corrected from %" PRIu64 "to %" PRIu64, chre->referenceTime / CHPP_NSEC_PER_MSEC, correctedTime / CHPP_NSEC_PER_MSEC); chre->referenceTime = correctedTime; #endif CHPP_DEBUG_ASSERT(chppCheckWifiScanEventNotification(chre)); gCallbacks->scanEventCallback(chre); } } /** * Handles the WiFi ranging event service notification. * * This function is called from chppDispatchWifiNotification(). * * @param clientContext Maintains status for each client instance. * @param buf Input data. Cannot be null. * @param len Length of input data in bytes. */ static void chppWifiRangingEventNotification( struct ChppWifiClientState *clientContext, uint8_t *buf, size_t len) { UNUSED_VAR(clientContext); CHPP_LOGD("chppWifiRangingEventNotification received data len=%" PRIuSIZE, len); buf += sizeof(struct ChppAppHeader); len -= sizeof(struct ChppAppHeader); // Timestamp correction prior to conversion to avoid const casting issues. #ifdef CHPP_CLIENT_ENABLED_TIMESYNC struct ChppWifiRangingEvent *event = (struct ChppWifiRangingEvent *)buf; for (size_t i = 0; i < event->resultCount; i++) { struct ChppWifiRangingResult *results = (struct ChppWifiRangingResult *)&buf[event->results.offset]; uint64_t correctedTime = results[i].timestamp - (uint64_t)chppTimesyncGetOffset(gWifiClientContext.client.appContext, CHPP_WIFI_MAX_TIMESYNC_AGE_NS); CHPP_LOGD("WiFi ranging result time corrected from %" PRIu64 "to %" PRIu64, results[i].timestamp / CHPP_NSEC_PER_MSEC, correctedTime / CHPP_NSEC_PER_MSEC); results[i].timestamp = correctedTime; } #endif struct chreWifiRangingEvent *chre = chppWifiRangingEventToChre((struct ChppWifiRangingEvent *)buf, len); uint8_t error = CHRE_ERROR_NONE; if (chre == NULL) { error = CHRE_ERROR; CHPP_LOGE("Ranging event conversion failed len=%" PRIuSIZE, len); } gCallbacks->rangingEventCallback(error, chre); } /** * Handles the NAN discovery event service notification. * * This function is called from chppDispatchWifiNotification(). * * @param buf Input data. Cannot be null. * @param len Length of input data in bytes. */ static void chppWifiDiscoveryEventNotification(uint8_t *buf, size_t len) { CHPP_LOGD("chppWifiDiscoveryEventNotification data len=%" PRIuSIZE, len); buf += sizeof(struct ChppAppHeader); len -= sizeof(struct ChppAppHeader); struct ChppWifiNanDiscoveryEvent *chppEvent = (struct ChppWifiNanDiscoveryEvent *)buf; struct chreWifiNanDiscoveryEvent *event = chppWifiNanDiscoveryEventToChre(chppEvent, len); if (event == NULL) { CHPP_LOGE("Discovery event CHPP -> CHRE conversion failed"); } else { gCallbacks->nanServiceDiscoveryCallback(event); } } /** * Handles the NAN connection lost event service notification. * * This function is called from chppDispatchWifiNotification(). * * @param buf Input data. Cannot be null. * @param len Length of input data in bytes. */ static void chppWifiNanServiceLostEventNotification(uint8_t *buf, size_t len) { buf += sizeof(struct ChppAppHeader); len -= sizeof(struct ChppAppHeader); struct ChppWifiNanSessionLostEvent *chppEvent = (struct ChppWifiNanSessionLostEvent *)buf; struct chreWifiNanSessionLostEvent *event = chppWifiNanSessionLostEventToChre(chppEvent, len); if (event == NULL) { CHPP_LOGE("Session lost event CHPP -> CHRE conversion failed"); } else { gCallbacks->nanServiceLostCallback(event->id, event->peerId); } } /** * Handles the NAN subscription termination event service notification. * * This function is called from chppDispatchWifiNotification(). * * @param buf Input data. Cannot be null. * @param len Length of input data in bytes. */ static void chppWifiNanServiceTerminatedEventNotification(uint8_t *buf, size_t len) { buf += sizeof(struct ChppAppHeader); len -= sizeof(struct ChppAppHeader); struct ChppWifiNanSessionTerminatedEvent *chppEvent = (struct ChppWifiNanSessionTerminatedEvent *)buf; struct chreWifiNanSessionTerminatedEvent *event = chppWifiNanSessionTerminatedEventToChre(chppEvent, len); if (event == NULL) { CHPP_LOGE("Session terminated event CHPP -> CHRE conversion failed"); } else { gCallbacks->nanServiceTerminatedCallback(event->reason, event->id); } } /** * Handles the service response for the NAN subscribe client request. * * This function is called from chppDispatchWifiNotification(). * * @param buf Input data. Cannot be null. * @param len Length of input data in bytes. */ static void chppWifiRequestNanSubscribeNotification(uint8_t *buf, size_t len) { uint8_t errorCode = CHRE_ERROR_NONE; uint32_t subscriptionId = 0; if (len < sizeof(struct ChppWifiNanServiceIdentifier)) { errorCode = CHRE_ERROR; } else { struct ChppWifiNanServiceIdentifier *id = (struct ChppWifiNanServiceIdentifier *)buf; errorCode = id->errorCode; subscriptionId = id->subscriptionId; } gCallbacks->nanServiceIdentifierCallback(errorCode, subscriptionId); } /** * Handles the service response for the NAN subscription cancel client request. * * This function is called from chppDispatchWifiNotification(). * * @param buf Input data. Cannot be null. * @param len Length of input data in bytes. */ static void chppWifiNanSubscriptionCanceledNotification(uint8_t *buf, size_t len) { uint8_t errorCode = CHRE_ERROR_NONE; uint32_t subscriptionId = 0; if (len < (sizeof(struct ChppWifiNanSubscriptionCanceledResponse))) { errorCode = CHRE_ERROR; } else { struct ChppWifiNanSubscriptionCanceledResponse *chppNotif = (struct ChppWifiNanSubscriptionCanceledResponse *)buf; errorCode = chppNotif->errorCode; subscriptionId = chppNotif->subscriptionId; } gCallbacks->nanSubscriptionCanceledCallback(errorCode, subscriptionId); } /** * Initializes the WiFi client upon an open request from CHRE and responds * with the result. * * @param systemApi CHRE system function pointers. * @param callbacks CHRE entry points. * * @return True if successful. False otherwise. */ static bool chppWifiClientOpen(const struct chrePalSystemApi *systemApi, const struct chrePalWifiCallbacks *callbacks) { CHPP_DEBUG_NOT_NULL(systemApi); CHPP_DEBUG_NOT_NULL(callbacks); bool result = false; gSystemApi = systemApi; gCallbacks = callbacks; CHPP_LOGD("WiFi client opening"); if (gWifiClientContext.client.appContext == NULL) { CHPP_LOGE("WiFi client app is null"); } else { if (chppWaitForDiscoveryComplete(gWifiClientContext.client.appContext, CHPP_WIFI_DISCOVERY_TIMEOUT_MS)) { result = chppClientSendOpenRequest( &gWifiClientContext.client, &gWifiClientContext.outReqStates[CHPP_WIFI_OPEN], CHPP_WIFI_OPEN, /*blocking=*/true); } // Since CHPP_WIFI_DEFAULT_CAPABILITIES is mandatory, we can always // pseudo-open and return true. Otherwise, these should have been gated. chppClientPseudoOpen(&gWifiClientContext.client); result = true; } return result; } /** * Deinitializes the WiFi client. */ static void chppWifiClientClose(void) { // Remote struct ChppAppHeader *request = chppAllocClientRequestCommand( &gWifiClientContext.client, CHPP_WIFI_CLOSE); if (request == NULL) { CHPP_LOG_OOM(); } else if (chppClientSendTimestampedRequestAndWait( &gWifiClientContext.client, &gWifiClientContext.outReqStates[CHPP_WIFI_CLOSE], request, sizeof(*request))) { gWifiClientContext.client.openState = CHPP_OPEN_STATE_CLOSED; gWifiClientContext.capabilities = CHRE_WIFI_CAPABILITIES_NONE; gWifiClientContext.capabilitiesValid = false; chppClientCloseOpenRequests(&gWifiClientContext.client, &kWifiClientConfig, true /* clearOnly */); } } /** * Retrieves a set of flags indicating the WiFi features supported by the * current implementation. * * @return Capabilities flags. */ static uint32_t chppWifiClientGetCapabilities(void) { uint32_t capabilities = CHPP_WIFI_DEFAULT_CAPABILITIES; if (gWifiClientContext.capabilitiesValid) { // Result already cached capabilities = gWifiClientContext.capabilities; } else { struct ChppAppHeader *request = chppAllocClientRequestCommand( &gWifiClientContext.client, CHPP_WIFI_GET_CAPABILITIES); if (request == NULL) { CHPP_LOG_OOM(); } else { if (chppClientSendTimestampedRequestAndWait( &gWifiClientContext.client, &gWifiClientContext.outReqStates[CHPP_WIFI_GET_CAPABILITIES], request, sizeof(*request))) { // Success. gWifiClientContext.capabilities is now populated if (gWifiClientContext.capabilitiesValid) { capabilities = gWifiClientContext.capabilities; } } } } return capabilities; } /** * Enables/disables receiving unsolicited scan results (scan monitoring). * * @param enable True to enable. * * @return True indicates the request was sent off to the service. */ static bool chppWifiClientConfigureScanMonitor(bool enable) { bool result = false; struct ChppWifiConfigureScanMonitorAsyncRequest *request = chppAllocClientRequestFixed( &gWifiClientContext.client, struct ChppWifiConfigureScanMonitorAsyncRequest); if (request == NULL) { CHPP_LOG_OOM(); } else { request->header.command = CHPP_WIFI_CONFIGURE_SCAN_MONITOR_ASYNC; request->params.enable = enable; request->params.cookie = &gWifiClientContext .outReqStates[CHPP_WIFI_CONFIGURE_SCAN_MONITOR_ASYNC]; result = chppClientSendTimestampedRequestOrFail( &gWifiClientContext.client, &gWifiClientContext .outReqStates[CHPP_WIFI_CONFIGURE_SCAN_MONITOR_ASYNC], request, sizeof(*request), CHPP_REQUEST_TIMEOUT_DEFAULT); } return result; } /** * Request that the WiFi chipset perform a scan or deliver results from its * cache. * * @param params See chreWifiRequestScanAsync(). * * @return True indicates the request was sent off to the service. */ static bool chppWifiClientRequestScan(const struct chreWifiScanParams *params) { struct ChppWifiScanParamsWithHeader *request; size_t requestLen; bool result = chppWifiScanParamsFromChre(params, &request, &requestLen); if (!result) { CHPP_LOG_OOM(); } else { request->header.handle = gWifiClientContext.client.handle; request->header.type = CHPP_MESSAGE_TYPE_CLIENT_REQUEST; request->header.transaction = gWifiClientContext.client.transaction++; request->header.error = CHPP_APP_ERROR_NONE; request->header.command = CHPP_WIFI_REQUEST_SCAN_ASYNC; CHPP_STATIC_ASSERT( CHRE_WIFI_SCAN_RESULT_TIMEOUT_NS > CHPP_WIFI_SCAN_RESULT_TIMEOUT_NS, "Chpp wifi scan timeout needs to be smaller than CHRE wifi scan " "timeout"); result = chppClientSendTimestampedRequestOrFail( &gWifiClientContext.client, &gWifiClientContext.outReqStates[CHPP_WIFI_REQUEST_SCAN_ASYNC], request, requestLen, CHPP_WIFI_SCAN_RESULT_TIMEOUT_NS); } return result; } /** * Releases the memory held for the scan event callback. * * @param event Location event to be released. */ static void chppWifiClientReleaseScanEvent(struct chreWifiScanEvent *event) { if (event->scannedFreqListLen > 0) { void *scannedFreqList = CHPP_CONST_CAST_POINTER(event->scannedFreqList); CHPP_FREE_AND_NULLIFY(scannedFreqList); } if (event->resultCount > 0) { void *results = CHPP_CONST_CAST_POINTER(event->results); CHPP_FREE_AND_NULLIFY(results); } CHPP_FREE_AND_NULLIFY(event); } /** * Request that the WiFi chipset perform RTT ranging. * * @param params See chreWifiRequestRangingAsync(). * * @return True indicates the request was sent off to the service. */ static bool chppWifiClientRequestRanging( const struct chreWifiRangingParams *params) { struct ChppWifiRangingParamsWithHeader *request; size_t requestLen; bool result = chppWifiRangingParamsFromChre(params, &request, &requestLen); if (!result) { CHPP_LOG_OOM(); } else { request->header.handle = gWifiClientContext.client.handle; request->header.type = CHPP_MESSAGE_TYPE_CLIENT_REQUEST; request->header.transaction = gWifiClientContext.client.transaction++; request->header.error = CHPP_APP_ERROR_NONE; request->header.command = CHPP_WIFI_REQUEST_RANGING_ASYNC; result = chppClientSendTimestampedRequestOrFail( &gWifiClientContext.client, &gWifiClientContext.outReqStates[CHPP_WIFI_REQUEST_RANGING_ASYNC], request, requestLen, CHRE_WIFI_RANGING_RESULT_TIMEOUT_NS); } return result; } /** * Releases the memory held for the RTT ranging event callback. * * @param event Location event to be released. */ static void chppWifiClientReleaseRangingEvent( struct chreWifiRangingEvent *event) { if (event->resultCount > 0) { void *results = CHPP_CONST_CAST_POINTER(event->results); CHPP_FREE_AND_NULLIFY(results); } CHPP_FREE_AND_NULLIFY(event); } /** * Request that the WiFi chipset perform a NAN subscription. * @see chreWifiNanSubscribe for more information. * * @param config NAN service subscription configuration. * @return true if subscribe request was successful, false otherwise. */ static bool chppWifiClientNanSubscribe( const struct chreWifiNanSubscribeConfig *config) { struct ChppWifiNanSubscribeConfigWithHeader *request; size_t requestLen; bool result = chppWifiNanSubscribeConfigFromChre(config, &request, &requestLen); if (!result) { CHPP_LOG_OOM(); } else { request->header.handle = gWifiClientContext.client.handle; request->header.type = CHPP_MESSAGE_TYPE_CLIENT_REQUEST; request->header.transaction = gWifiClientContext.client.transaction++; request->header.error = CHPP_APP_ERROR_NONE; request->header.command = CHPP_WIFI_REQUEST_NAN_SUB; result = chppClientSendTimestampedRequestOrFail( &gWifiClientContext.client, &gWifiClientContext.outReqStates[CHPP_WIFI_REQUEST_NAN_SUB], request, requestLen, CHRE_ASYNC_RESULT_TIMEOUT_NS); } return result; } /** * Request the WiFi chipset to cancel a NAN subscription. * @param subscriptionId Identifier assigned by the NAN engine for a service * subscription. * @return true if cancelation request was successfully dispatched, false * otherwise. */ static bool chppWifiClientNanSubscribeCancel(uint32_t subscriptionId) { bool result = false; struct ChppWifiNanSubscribeCancelRequest *request = chppAllocClientRequestFixed(&gWifiClientContext.client, struct ChppWifiNanSubscribeCancelRequest); if (request == NULL) { CHPP_LOG_OOM(); } else { request->header.handle = gWifiClientContext.client.handle; request->header.command = CHPP_WIFI_REQUEST_NAN_SUB_CANCEL; request->header.type = CHPP_MESSAGE_TYPE_CLIENT_REQUEST; request->header.transaction = gWifiClientContext.client.transaction++; request->header.error = CHPP_APP_ERROR_NONE; request->subscriptionId = subscriptionId; result = chppClientSendTimestampedRequestAndWait( &gWifiClientContext.client, &gWifiClientContext.outReqStates[CHPP_WIFI_REQUEST_NAN_SUB_CANCEL], request, sizeof(*request)); } return result; } /** * Release the memory held for the NAN service discovery callback. * * @param event Discovery event to be freed. */ static void chppWifiClientNanReleaseDiscoveryEvent( struct chreWifiNanDiscoveryEvent *event) { if (event != NULL) { if (event->serviceSpecificInfo != NULL) { void *info = CHPP_CONST_CAST_POINTER(event->serviceSpecificInfo); CHPP_FREE_AND_NULLIFY(info); } CHPP_FREE_AND_NULLIFY(event); } } /** * Request that the WiFi chipset perform NAN ranging. * * @param params WiFi NAN ranging parameters. * @return true if the ranging request was successfully dispatched, false * otherwise. */ static bool chppWifiClientNanRequestNanRanging( const struct chreWifiNanRangingParams *params) { struct ChppWifiNanRangingParamsWithHeader *request; size_t requestLen; bool result = chppWifiNanRangingParamsFromChre(params, &request, &requestLen); if (!result) { CHPP_LOG_OOM(); } else { request->header.handle = gWifiClientContext.client.handle; request->header.command = CHPP_WIFI_REQUEST_NAN_RANGING_ASYNC; request->header.type = CHPP_MESSAGE_TYPE_CLIENT_REQUEST; request->header.transaction = gWifiClientContext.client.transaction++; request->header.error = CHPP_APP_ERROR_NONE; result = chppClientSendTimestampedRequestOrFail( &gWifiClientContext.client, &gWifiClientContext.outReqStates[CHPP_WIFI_REQUEST_NAN_RANGING_ASYNC], request, requestLen, CHRE_ASYNC_RESULT_TIMEOUT_NS); } return result; } static bool chppWifiGetNanCapabilites( struct chreWifiNanCapabilities *capabilities) { // Not implemented yet. UNUSED_VAR(capabilities); return false; } /************************************************ * Public Functions ***********************************************/ void chppRegisterWifiClient(struct ChppAppState *appContext) { memset(&gWifiClientContext, 0, sizeof(gWifiClientContext)); chppRegisterClient(appContext, (void *)&gWifiClientContext, &gWifiClientContext.client, gWifiClientContext.outReqStates, &kWifiClientConfig); } void chppDeregisterWifiClient(struct ChppAppState *appContext) { // TODO UNUSED_VAR(appContext); } struct ChppEndpointState *getChppWifiClientState(void) { return &gWifiClientContext.client; } #ifdef CHPP_CLIENT_ENABLED_WIFI #ifdef CHPP_CLIENT_ENABLED_CHRE_WIFI const struct chrePalWifiApi *chrePalWifiGetApi(uint32_t requestedApiVersion) { #else const struct chrePalWifiApi *chppPalWifiGetApi(uint32_t requestedApiVersion) { #endif static const struct chrePalWifiApi api = { .moduleVersion = CHPP_PAL_WIFI_API_VERSION, .open = chppWifiClientOpen, .close = chppWifiClientClose, .getCapabilities = chppWifiClientGetCapabilities, .configureScanMonitor = chppWifiClientConfigureScanMonitor, .requestScan = chppWifiClientRequestScan, .releaseScanEvent = chppWifiClientReleaseScanEvent, .requestRanging = chppWifiClientRequestRanging, .releaseRangingEvent = chppWifiClientReleaseRangingEvent, .nanSubscribe = chppWifiClientNanSubscribe, .nanSubscribeCancel = chppWifiClientNanSubscribeCancel, .releaseNanDiscoveryEvent = chppWifiClientNanReleaseDiscoveryEvent, .requestNanRanging = chppWifiClientNanRequestNanRanging, .getNanCapabilities = chppWifiGetNanCapabilites, }; CHPP_STATIC_ASSERT( CHRE_PAL_WIFI_API_CURRENT_VERSION == CHPP_PAL_WIFI_API_VERSION, "A newer CHRE PAL API version is available. Please update."); if (!CHRE_PAL_VERSIONS_ARE_COMPATIBLE(api.moduleVersion, requestedApiVersion)) { return NULL; } else { return &api; } } #endif