blob: 1bc1a5af2310989d5129fcfa134ddece4f725afd [file] [log] [blame]
/*
* 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/services/wifi.h"
#include <inttypes.h>
#include <stddef.h>
#include <stdint.h>
#include "chpp/common/standard_uuids.h"
#include "chpp/common/wifi.h"
#include "chpp/macros.h"
#include "chpp/platform/log.h"
#include "chpp/services.h"
#include "chpp/services/wifi_types.h"
#include "chre/pal/wifi.h"
/************************************************
* Prototypes
***********************************************/
static enum ChppAppErrorCode chppDispatchWifiRequest(void *serviceContext,
uint8_t *buf, size_t len);
/************************************************
* Private Definitions
***********************************************/
/**
* Configuration parameters for this service
*/
static const struct ChppService kWifiServiceConfig = {
.descriptor.uuid = CHPP_UUID_WIFI_STANDARD,
// Human-readable name
.descriptor.name = "WiFi",
// Version
.descriptor.version.major = 1,
.descriptor.version.minor = 0,
.descriptor.version.patch = 0,
// Client request dispatch function pointer
.requestDispatchFunctionPtr = &chppDispatchWifiRequest,
// Client notification dispatch function pointer
.notificationDispatchFunctionPtr = NULL, // Not supported
// Min length is the entire header
.minLength = sizeof(struct ChppAppHeader),
};
/**
* Structure to maintain state for the WiFi service and its Request/Response
* (RR) functionality.
*/
struct ChppWifiServiceState {
struct ChppServiceState service; // WiFi service state
const struct chrePalWifiApi *api; // WiFi PAL API
// Based on chre/pal/wifi.h and chrePalWifiApi
struct ChppRequestResponseState open; // Service init state
struct ChppRequestResponseState close; // Service deinit state
struct ChppRequestResponseState getCapabilities; // Get Capabilities state
struct ChppRequestResponseState
configureScanMonitorAsync; // Configure scan monitor state
struct ChppRequestResponseState requestScanAsync; // Request scan state
struct ChppRequestResponseState requestRangingAsync; // Request ranging state
};
// Note: The CHRE PAL API only allows for one definition - see comment in WWAN
// service for details.
// Note: There is no notion of a cookie in the CHRE WiFi API so we need to use
// the global service state (gWifiServiceContext) directly in all callbacks.
struct ChppWifiServiceState gWifiServiceContext;
/************************************************
* Prototypes
***********************************************/
static enum ChppAppErrorCode chppWifiServiceOpen(
struct ChppWifiServiceState *wifiServiceContext,
struct ChppAppHeader *requestHeader);
static enum ChppAppErrorCode chppWifiServiceClose(
struct ChppWifiServiceState *wifiServiceContext,
struct ChppAppHeader *requestHeader);
static enum ChppAppErrorCode chppWifiServiceGetCapabilities(
struct ChppWifiServiceState *wifiServiceContext,
struct ChppAppHeader *requestHeader);
static enum ChppAppErrorCode chppWifiServiceConfigureScanMonitorAsync(
struct ChppWifiServiceState *wifiServiceContext,
struct ChppAppHeader *requestHeader, uint8_t *buf, size_t len);
static enum ChppAppErrorCode chppWifiServiceRequestScanAsync(
struct ChppWifiServiceState *wifiServiceContext,
struct ChppAppHeader *requestHeader, uint8_t *buf, size_t len);
static enum ChppAppErrorCode chppWifiServiceRequestRangingAsync(
struct ChppWifiServiceState *wifiServiceContext,
struct ChppAppHeader *requestHeader, uint8_t *buf, size_t len);
static void chppWifiServiceScanMonitorStatusChangeCallback(bool enabled,
uint8_t errorCode);
static void chppWifiServiceScanResponseCallback(bool pending,
uint8_t errorCode);
static void chppWifiServiceScanEventCallback(struct chreWifiScanEvent *event);
static void chppWifiServiceRangingEventCallback(
uint8_t errorCode, struct chreWifiRangingEvent *event);
/************************************************
* Private Functions
***********************************************/
/**
* Dispatches a client request from the transport layer that is determined to be
* for the WiFi service. If the result of the dispatch is an error, this
* function responds to the client with the same error.
*
* This function is called from the app layer using its function pointer given
* during service registration.
*
* @param serviceContext Maintains status for each service 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 chppDispatchWifiRequest(void *serviceContext,
uint8_t *buf, size_t len) {
struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
buf += sizeof(struct ChppAppHeader);
len -= sizeof(struct ChppAppHeader);
struct ChppWifiServiceState *wifiServiceContext =
(struct ChppWifiServiceState *)serviceContext;
struct ChppRequestResponseState *rRState;
enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE;
bool dispatched = true;
switch (rxHeader->command) {
case CHPP_WIFI_OPEN: {
rRState = &wifiServiceContext->open;
chppServiceTimestampRequest(rRState, rxHeader);
error = chppWifiServiceOpen(wifiServiceContext, rxHeader);
break;
}
case CHPP_WIFI_CLOSE: {
rRState = &wifiServiceContext->close;
chppServiceTimestampRequest(rRState, rxHeader);
error = chppWifiServiceClose(wifiServiceContext, rxHeader);
break;
}
case CHPP_WIFI_GET_CAPABILITIES: {
rRState = &wifiServiceContext->getCapabilities;
chppServiceTimestampRequest(rRState, rxHeader);
error = chppWifiServiceGetCapabilities(wifiServiceContext, rxHeader);
break;
}
case CHPP_WIFI_CONFIGURE_SCAN_MONITOR_ASYNC: {
rRState = &wifiServiceContext->configureScanMonitorAsync;
chppServiceTimestampRequest(rRState, rxHeader);
error = chppWifiServiceConfigureScanMonitorAsync(wifiServiceContext,
rxHeader, buf, len);
break;
}
case CHPP_WIFI_REQUEST_SCAN_ASYNC: {
rRState = &wifiServiceContext->requestScanAsync;
chppServiceTimestampRequest(rRState, rxHeader);
error = chppWifiServiceRequestScanAsync(wifiServiceContext, rxHeader, buf,
len);
break;
}
case CHPP_WIFI_REQUEST_RANGING_ASYNC: {
rRState = &wifiServiceContext->requestRangingAsync;
chppServiceTimestampRequest(rRState, rxHeader);
error = chppWifiServiceRequestRangingAsync(wifiServiceContext, rxHeader,
buf, len);
break;
}
default: {
dispatched = false;
error = CHPP_APP_ERROR_INVALID_COMMAND;
break;
}
}
if (dispatched == true && error != CHPP_APP_ERROR_NONE) {
// Request was dispatched but an error was returned. Close out
// chppServiceTimestampRequest()
chppServiceTimestampResponse(rRState);
}
return error;
}
/**
* Initializes the WiFi service upon an open request from the client and
* responds to the client with the result.
*
* @param serviceContext Maintains status for each service instance.
* @param requestHeader App layer header of the request.
*
* @return Indicates the result of this function call.
*/
static enum ChppAppErrorCode chppWifiServiceOpen(
struct ChppWifiServiceState *wifiServiceContext,
struct ChppAppHeader *requestHeader) {
static const struct chrePalWifiCallbacks palCallbacks = {
.scanMonitorStatusChangeCallback =
chppWifiServiceScanMonitorStatusChangeCallback,
.scanResponseCallback = chppWifiServiceScanResponseCallback,
.scanEventCallback = chppWifiServiceScanEventCallback,
.rangingEventCallback = chppWifiServiceRangingEventCallback,
};
enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE;
if (!wifiServiceContext->api->open(
wifiServiceContext->service.appContext->systemApi, &palCallbacks)) {
CHPP_LOGE("CHPP WiFi PAL API initialization failed");
CHPP_DEBUG_ASSERT(false);
error = CHPP_APP_ERROR_UNSPECIFIED;
} else {
CHPP_LOGI("CHPP WiFi service initialized");
struct ChppAppHeader *response =
chppAllocServiceResponseFixed(requestHeader, struct ChppAppHeader);
if (response == NULL) {
CHPP_LOG_OOM();
error = CHPP_APP_ERROR_OOM;
} else {
chppSendTimestampedResponseOrFail(&wifiServiceContext->service,
&wifiServiceContext->open, response,
sizeof(*response));
}
}
return error;
}
/**
* Deinitializes the WiFi service.
*
* @param serviceContext Maintains status for each service instance.
* @param requestHeader App layer header of the request.
*
* @return Indicates the result of this function call.
*/
static enum ChppAppErrorCode chppWifiServiceClose(
struct ChppWifiServiceState *wifiServiceContext,
struct ChppAppHeader *requestHeader) {
enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE;
wifiServiceContext->api->close();
CHPP_LOGI("CHPP WiFi service deinitialized");
struct ChppAppHeader *response =
chppAllocServiceResponseFixed(requestHeader, struct ChppAppHeader);
if (response == NULL) {
CHPP_LOG_OOM();
error = CHPP_APP_ERROR_OOM;
} else {
chppSendTimestampedResponseOrFail(&wifiServiceContext->service,
&wifiServiceContext->close, response,
sizeof(*response));
}
return error;
}
/**
* Retrieves a set of flags indicating the WiFi features supported by the
* current implementation.
*
* @param serviceContext Maintains status for each service instance.
* @param requestHeader App layer header of the request.
*
* @return Indicates the result of this function call.
*/
static enum ChppAppErrorCode chppWifiServiceGetCapabilities(
struct ChppWifiServiceState *wifiServiceContext,
struct ChppAppHeader *requestHeader) {
enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE;
struct ChppWifiGetCapabilitiesResponse *response =
chppAllocServiceResponseFixed(requestHeader,
struct ChppWifiGetCapabilitiesResponse);
if (response == NULL) {
CHPP_LOG_OOM();
error = CHPP_APP_ERROR_OOM;
} else {
response->params.capabilities = wifiServiceContext->api->getCapabilities();
CHPP_LOGD("chppWifiServiceGetCapabilities returning 0x%" PRIx32
", %" PRIuSIZE " bytes",
response->params.capabilities, sizeof(*response));
chppSendTimestampedResponseOrFail(&wifiServiceContext->service,
&wifiServiceContext->getCapabilities,
response, sizeof(*response));
}
return error;
}
/**
* Configures whether scanEventCallback receives unsolicited scan results, i.e.
* the results of scans not performed at the request of CHRE.
*
* This function returns an error code synchronously. A subsequent call to
* chppWifiServiceScanMonitorStatusChangeCallback() will be used to communicate
* the result of the operation.
*
* @param serviceContext Maintains status for each service instance.
* @param requestHeader App layer header of the request.
* @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 chppWifiServiceConfigureScanMonitorAsync(
struct ChppWifiServiceState *wifiServiceContext,
struct ChppAppHeader *requestHeader, uint8_t *buf, size_t len) {
UNUSED_VAR(requestHeader);
enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE;
if (len < sizeof(bool)) {
error = CHPP_APP_ERROR_INVALID_ARG;
} else {
bool *enable = (bool *)buf;
if (!wifiServiceContext->api->configureScanMonitor(*enable)) {
error = CHPP_APP_ERROR_UNSPECIFIED;
}
}
return error;
}
/**
* Request that the WiFi chipset perform a scan, or deliver results from its
* cache if the parameters allow for it.
*
* This function returns an error code synchronously. A subsequent call to
* chppWifiServiceScanEventCallback() will be used to communicate the scan
* results.
*
* @param serviceContext Maintains status for each service instance.
* @param requestHeader App layer header of the request.
* @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 chppWifiServiceRequestScanAsync(
struct ChppWifiServiceState *wifiServiceContext,
struct ChppAppHeader *requestHeader, uint8_t *buf, size_t len) {
enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE;
// TODO: replace with chppWifiScanParamsToChre() when available
if (len < sizeof(struct chreWifiScanParams)) {
error = CHPP_APP_ERROR_INVALID_ARG;
} else {
struct ChppWifiScanParams *in = (struct ChppWifiScanParams *)buf;
struct chreWifiScanParams *params = (struct chreWifiScanParams *)buf;
if (in->frequencyListLen > 0 &&
len >= in->frequencyList.offset +
in->frequencyListLen * sizeof(uint32_t)) {
params->frequencyList =
(const uint32_t *)(buf + in->frequencyList.offset);
} else {
params->frequencyList = NULL;
}
if (in->ssidListLen > 0 &&
len >= in->ssidList.offset +
in->ssidListLen * sizeof(struct chreWifiSsidListItem)) {
params->ssidList =
(const struct chreWifiSsidListItem *)(buf + in->ssidList.offset);
} else {
params->ssidList = NULL;
}
if (!wifiServiceContext->api->requestScan(params)) {
error = CHPP_APP_ERROR_UNSPECIFIED;
} else {
struct ChppAppHeader *response =
chppAllocServiceResponseFixed(requestHeader, struct ChppAppHeader);
if (response == NULL) {
CHPP_LOG_OOM();
error = CHPP_APP_ERROR_OOM;
} else {
chppSendTimestampedResponseOrFail(&wifiServiceContext->service,
&wifiServiceContext->requestScanAsync,
response, sizeof(*response));
}
}
}
return error;
}
/**
* Request that the WiFi chipset perform RTT ranging against a set of access
* points specified in params.
*
* This function returns an error code synchronously. A subsequent call to
* chppWifiServiceRangingEventCallback() will be used to communicate the
* result of the operation
*
* @param serviceContext Maintains status for each service instance.
* @param requestHeader App layer header of the request.
* @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 chppWifiServiceRequestRangingAsync(
struct ChppWifiServiceState *wifiServiceContext,
struct ChppAppHeader *requestHeader, uint8_t *buf, size_t len) {
enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE;
if (len < sizeof(struct chreWifiRangingParams)) {
error = CHPP_APP_ERROR_INVALID_ARG;
} else {
struct chreWifiRangingParams *params = (struct chreWifiRangingParams *)buf;
if (!wifiServiceContext->api->requestRanging(params)) {
error = CHPP_APP_ERROR_UNSPECIFIED;
} else {
struct ChppAppHeader *response =
chppAllocServiceResponseFixed(requestHeader, struct ChppAppHeader);
if (response == NULL) {
CHPP_LOG_OOM();
error = CHPP_APP_ERROR_OOM;
} else {
chppSendTimestampedResponseOrFail(
&wifiServiceContext->service,
&wifiServiceContext->requestRangingAsync, response,
sizeof(*response));
}
}
}
return error;
}
/**
* PAL callback with the result of changes to the scan monitor registration
* status requested via configureScanMonitor.
*
* @param enabled true if the scan monitor is currently active
* @param errorCode An error code from enum chreError
*/
static void chppWifiServiceScanMonitorStatusChangeCallback(bool enabled,
uint8_t errorCode) {
// Recreate request header
struct ChppAppHeader requestHeader = {
.handle = gWifiServiceContext.service.handle,
.transaction = gWifiServiceContext.configureScanMonitorAsync.transaction,
.command = CHPP_WIFI_CONFIGURE_SCAN_MONITOR_ASYNC,
};
struct ChppWifiConfigureScanMonitorAsyncResponse *response =
chppAllocServiceResponseFixed(
&requestHeader, struct ChppWifiConfigureScanMonitorAsyncResponse);
if (response == NULL) {
CHPP_LOG_OOM();
CHPP_ASSERT(false);
} else {
response->params.enabled = enabled;
response->params.errorCode = errorCode;
chppSendTimestampedResponseOrFail(
&gWifiServiceContext.service,
&gWifiServiceContext.configureScanMonitorAsync, response,
sizeof(struct ChppWifiConfigureScanMonitorAsyncResponse));
}
}
/**
* PAL callback with the result of a requestScan.
*
* @param pending true if the request was successful.
* @param errorCode An error code from enum chreError.
*/
static void chppWifiServiceScanResponseCallback(bool pending,
uint8_t errorCode) {
// Recreate request header
struct ChppAppHeader requestHeader = {
.handle = gWifiServiceContext.service.handle,
.transaction = gWifiServiceContext.requestScanAsync.transaction,
.command = CHPP_WIFI_REQUEST_SCAN_ASYNC,
};
struct ChppWifiRequestScanResponse *response = chppAllocServiceResponseFixed(
&requestHeader, struct ChppWifiRequestScanResponse);
if (response == NULL) {
CHPP_LOG_OOM();
CHPP_ASSERT(false);
} else {
response->pending = pending;
response->errorCode = errorCode;
chppSendTimestampedResponseOrFail(
&gWifiServiceContext.service, &gWifiServiceContext.requestScanAsync,
response, sizeof(struct ChppWifiRequestScanResponse));
}
}
/**
* PAL callback with WiFi scan result.
*
* @param event Scan result data.
*/
static void chppWifiServiceScanEventCallback(struct chreWifiScanEvent *event) {
// Craft response per parser script
struct ChppWifiScanEventWithHeader *notification;
size_t notificationLen;
if (!chppWifiScanEventFromChre(event, &notification, &notificationLen)) {
CHPP_LOGE(
"chppWifiScanEventFromChre failed (OOM?). Transaction ID = "
"%" PRIu8,
gWifiServiceContext.requestScanAsync.transaction);
// TODO: consider sending an error response if this fails
} else {
notification->header.handle = gWifiServiceContext.service.handle;
notification->header.type = CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION;
notification->header.transaction =
gWifiServiceContext.requestScanAsync.transaction;
notification->header.error = CHPP_APP_ERROR_NONE;
notification->header.command = CHPP_WIFI_REQUEST_SCAN_ASYNC;
chppEnqueueTxDatagramOrFail(
gWifiServiceContext.service.appContext->transportContext, notification,
notificationLen);
}
gWifiServiceContext.api->releaseScanEvent(event);
}
/**
* PAL callback with RTT ranging results from the WiFi module.
*
* @param errorCode An error code from enum chreError.
* @param event Ranging data.
*/
static void chppWifiServiceRangingEventCallback(
uint8_t errorCode, struct chreWifiRangingEvent *event) {
// TODO
UNUSED_VAR(errorCode);
gWifiServiceContext.api->releaseRangingEvent(event);
}
/************************************************
* Public Functions
***********************************************/
void chppRegisterWifiService(struct ChppAppState *appContext) {
gWifiServiceContext.api = chrePalWifiGetApi(CHRE_PAL_WIFI_API_V1_2);
if (gWifiServiceContext.api == NULL) {
CHPP_LOGE(
"WiFi PAL API version not compatible with CHPP. Cannot register WiFi "
"service");
CHPP_DEBUG_ASSERT(false);
} else {
gWifiServiceContext.service.appContext = appContext;
gWifiServiceContext.service.handle = chppRegisterService(
appContext, (void *)&gWifiServiceContext, &kWifiServiceConfig);
CHPP_DEBUG_ASSERT(gWifiServiceContext.service.handle);
}
}
void chppDeregisterWifiService(struct ChppAppState *appContext) {
// TODO
UNUSED_VAR(appContext);
}