1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "chpp/clients/wwan.h" 18 19 #include <inttypes.h> 20 #include <stdbool.h> 21 #include <stddef.h> 22 #include <stdint.h> 23 24 #include "chpp/app.h" 25 #include "chpp/clients/discovery.h" 26 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC 27 #include "chpp/clients/timesync.h" 28 #endif 29 #include "chpp/common/standard_uuids.h" 30 #include "chpp/common/wwan.h" 31 #include "chpp/common/wwan_types.h" 32 #include "chpp/log.h" 33 #include "chpp/macros.h" 34 #include "chpp/memory.h" 35 #include "chre/pal/wwan.h" 36 37 #ifndef CHPP_WWAN_DISCOVERY_TIMEOUT_MS 38 #define CHPP_WWAN_DISCOVERY_TIMEOUT_MS CHPP_DISCOVERY_DEFAULT_TIMEOUT_MS 39 #endif 40 41 #ifndef CHPP_WWAN_MAX_TIMESYNC_AGE_NS 42 #define CHPP_WWAN_MAX_TIMESYNC_AGE_NS CHPP_TIMESYNC_DEFAULT_MAX_AGE_NS 43 #endif 44 45 /************************************************ 46 * Prototypes 47 ***********************************************/ 48 49 static enum ChppAppErrorCode chppDispatchWwanResponse(void *clientContext, 50 uint8_t *buf, size_t len); 51 static bool chppWwanClientInit(void *clientContext, uint8_t handle, 52 struct ChppVersion serviceVersion); 53 static void chppWwanClientDeinit(void *clientContext); 54 static void chppWwanClientNotifyReset(void *clientContext); 55 static void chppWwanClientNotifyMatch(void *clientContext); 56 57 /************************************************ 58 * Private Definitions 59 ***********************************************/ 60 61 /** 62 * Structure to maintain state for the WWAN client and its Request/Response 63 * (RR) functionality. 64 */ 65 struct ChppWwanClientState { 66 struct ChppClientState client; // WWAN client state 67 const struct chrePalWwanApi *api; // WWAN PAL API 68 69 struct ChppRequestResponseState rRState[CHPP_WWAN_CLIENT_REQUEST_MAX + 1]; 70 71 uint32_t capabilities; // Cached GetCapabilities result 72 }; 73 74 // Note: This global definition of gWwanClientContext supports only one 75 // instance of the CHPP WWAN client at a time. 76 struct ChppWwanClientState gWwanClientContext; 77 static const struct chrePalSystemApi *gSystemApi; 78 static const struct chrePalWwanCallbacks *gCallbacks; 79 80 /** 81 * Configuration parameters for this client 82 */ 83 static const struct ChppClient kWwanClientConfig = { 84 .descriptor.uuid = CHPP_UUID_WWAN_STANDARD, 85 86 // Version 87 .descriptor.version.major = 1, 88 .descriptor.version.minor = 0, 89 .descriptor.version.patch = 0, 90 91 // Notifies client if CHPP is reset 92 .resetNotifierFunctionPtr = &chppWwanClientNotifyReset, 93 94 // Notifies client if they are matched to a service 95 .matchNotifierFunctionPtr = &chppWwanClientNotifyMatch, 96 97 // Service response dispatch function pointer 98 .responseDispatchFunctionPtr = &chppDispatchWwanResponse, 99 100 // Service notification dispatch function pointer 101 .notificationDispatchFunctionPtr = NULL, // Not supported 102 103 // Service response dispatch function pointer 104 .initFunctionPtr = &chppWwanClientInit, 105 106 // Service notification dispatch function pointer 107 .deinitFunctionPtr = &chppWwanClientDeinit, 108 109 // Number of request-response states in the rRStates array. 110 .rRStateCount = ARRAY_SIZE(gWwanClientContext.rRState), 111 112 // Min length is the entire header 113 .minLength = sizeof(struct ChppAppHeader), 114 }; 115 116 /************************************************ 117 * Prototypes 118 ***********************************************/ 119 120 static bool chppWwanClientOpen(const struct chrePalSystemApi *systemApi, 121 const struct chrePalWwanCallbacks *callbacks); 122 static void chppWwanClientClose(void); 123 static uint32_t chppWwanClientGetCapabilities(void); 124 static bool chppWwanClientGetCellInfoAsync(void); 125 static void chppWwanClientReleaseCellInfoResult( 126 struct chreWwanCellInfoResult *result); 127 128 static void chppWwanCloseResult(struct ChppWwanClientState *clientContext, 129 uint8_t *buf, size_t len); 130 static void chppWwanGetCapabilitiesResult( 131 struct ChppWwanClientState *clientContext, uint8_t *buf, size_t len); 132 static void chppWwanGetCellInfoAsyncResult( 133 struct ChppWwanClientState *clientContext, uint8_t *buf, size_t len); 134 135 /************************************************ 136 * Private Functions 137 ***********************************************/ 138 139 /** 140 * Dispatches a service response from the transport layer that is determined to 141 * be for the WWAN client. 142 * 143 * This function is called from the app layer using its function pointer given 144 * during client registration. 145 * 146 * @param clientContext Maintains status for each client instance. 147 * @param buf Input data. Cannot be null. 148 * @param len Length of input data in bytes. 149 * 150 * @return Indicates the result of this function call. 151 */ 152 static enum ChppAppErrorCode chppDispatchWwanResponse(void *clientContext, 153 uint8_t *buf, 154 size_t len) { 155 struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf; 156 struct ChppWwanClientState *wwanClientContext = 157 (struct ChppWwanClientState *)clientContext; 158 enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE; 159 160 if (rxHeader->command > CHPP_WWAN_CLIENT_REQUEST_MAX) { 161 error = CHPP_APP_ERROR_INVALID_COMMAND; 162 163 } else if (!chppClientTimestampResponse( 164 &wwanClientContext->client, 165 &wwanClientContext->rRState[rxHeader->command], rxHeader)) { 166 error = CHPP_APP_ERROR_UNEXPECTED_RESPONSE; 167 168 } else { 169 switch (rxHeader->command) { 170 case CHPP_WWAN_OPEN: { 171 chppClientProcessOpenResponse(&wwanClientContext->client, buf, len); 172 break; 173 } 174 175 case CHPP_WWAN_CLOSE: { 176 chppWwanCloseResult(wwanClientContext, buf, len); 177 break; 178 } 179 180 case CHPP_WWAN_GET_CAPABILITIES: { 181 chppWwanGetCapabilitiesResult(wwanClientContext, buf, len); 182 break; 183 } 184 185 case CHPP_WWAN_GET_CELLINFO_ASYNC: { 186 chppWwanGetCellInfoAsyncResult(wwanClientContext, buf, len); 187 break; 188 } 189 190 default: { 191 error = CHPP_APP_ERROR_INVALID_COMMAND; 192 break; 193 } 194 } 195 } 196 197 return error; 198 } 199 200 /** 201 * Initializes the client and provides its handle number and the version of the 202 * matched service when/if it the client is matched with a service during 203 * discovery. 204 * 205 * @param clientContext Maintains status for each client instance. 206 * @param handle Handle number for this client. 207 * @param serviceVersion Version of the matched service. 208 * 209 * @return True if client is compatible and successfully initialized. 210 */ 211 static bool chppWwanClientInit(void *clientContext, uint8_t handle, 212 struct ChppVersion serviceVersion) { 213 UNUSED_VAR(serviceVersion); 214 215 struct ChppWwanClientState *wwanClientContext = 216 (struct ChppWwanClientState *)clientContext; 217 chppClientInit(&wwanClientContext->client, handle); 218 219 return true; 220 } 221 222 /** 223 * Deinitializes the client. 224 * 225 * @param clientContext Maintains status for each client instance. 226 */ 227 static void chppWwanClientDeinit(void *clientContext) { 228 struct ChppWwanClientState *wwanClientContext = 229 (struct ChppWwanClientState *)clientContext; 230 chppClientDeinit(&wwanClientContext->client); 231 } 232 233 /** 234 * Notifies the client of an incoming reset. 235 * 236 * @param clientContext Maintains status for each client instance. 237 */ 238 static void chppWwanClientNotifyReset(void *clientContext) { 239 struct ChppWwanClientState *wwanClientContext = 240 (struct ChppWwanClientState *)clientContext; 241 242 chppClientCloseOpenRequests(&wwanClientContext->client, &kWwanClientConfig, 243 false /* clearOnly */); 244 245 if (wwanClientContext->client.openState != CHPP_OPEN_STATE_OPENED && 246 !wwanClientContext->client.pseudoOpen) { 247 CHPP_LOGW("WWAN client reset but wasn't open"); 248 } else { 249 CHPP_LOGI("WWAN client reopening from state=%" PRIu8, 250 wwanClientContext->client.openState); 251 chppClientSendOpenRequest(&wwanClientContext->client, 252 &wwanClientContext->rRState[CHPP_WWAN_OPEN], 253 CHPP_WWAN_OPEN, 254 /*blocking=*/false); 255 } 256 } 257 258 /** 259 * Notifies the client of being matched to a service. 260 * 261 * @param clientContext Maintains status for each client instance. 262 */ 263 static void chppWwanClientNotifyMatch(void *clientContext) { 264 struct ChppWwanClientState *wwanClientContext = 265 (struct ChppWwanClientState *)clientContext; 266 267 if (wwanClientContext->client.pseudoOpen) { 268 CHPP_LOGD("Pseudo-open WWAN client opening"); 269 chppClientSendOpenRequest(&wwanClientContext->client, 270 &wwanClientContext->rRState[CHPP_WWAN_OPEN], 271 CHPP_WWAN_OPEN, 272 /*blocking=*/false); 273 } 274 } 275 276 /** 277 * Handles the service response for the close client request. 278 * 279 * This function is called from chppDispatchWwanResponse(). 280 * 281 * @param clientContext Maintains status for each client instance. 282 * @param buf Input data. Cannot be null. 283 * @param len Length of input data in bytes. 284 */ 285 static void chppWwanCloseResult(struct ChppWwanClientState *clientContext, 286 uint8_t *buf, size_t len) { 287 // TODO 288 UNUSED_VAR(clientContext); 289 UNUSED_VAR(buf); 290 UNUSED_VAR(len); 291 } 292 293 /** 294 * Handles the service response for the get capabilities client request. 295 * 296 * This function is called from chppDispatchWwanResponse(). 297 * 298 * @param clientContext Maintains status for each client instance. 299 * @param buf Input data. Cannot be null. 300 * @param len Length of input data in bytes. 301 */ 302 static void chppWwanGetCapabilitiesResult( 303 struct ChppWwanClientState *clientContext, uint8_t *buf, size_t len) { 304 if (len < sizeof(struct ChppWwanGetCapabilitiesResponse)) { 305 struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf; 306 CHPP_LOGE("GetCapabilities resp. too short. err=%" PRIu8, rxHeader->error); 307 308 } else { 309 struct ChppWwanGetCapabilitiesParameters *result = 310 &((struct ChppWwanGetCapabilitiesResponse *)buf)->params; 311 312 CHPP_LOGD("chppWwanGetCapabilitiesResult received capabilities=0x%" PRIx32, 313 result->capabilities); 314 315 #ifdef CHPP_WWAN_DEFAULT_CAPABILITIES 316 CHPP_ASSERT_LOG((result->capabilities == CHPP_WWAN_DEFAULT_CAPABILITIES), 317 "Unexpected capability 0x%" PRIx32 " != 0x%" PRIx32, 318 result->capabilities, CHPP_WWAN_DEFAULT_CAPABILITIES); 319 #endif 320 321 clientContext->capabilities = result->capabilities; 322 } 323 } 324 325 /** 326 * Handles the service response for the asynchronous get cell info client 327 * request. 328 * 329 * This function is called from chppDispatchWwanResponse(). 330 * 331 * @param clientContext Maintains status for each client instance. 332 * @param buf Input data. Cannot be null. 333 * @param len Length of input data in bytes. 334 */ 335 static void chppWwanGetCellInfoAsyncResult( 336 struct ChppWwanClientState *clientContext, uint8_t *buf, size_t len) { 337 UNUSED_VAR(clientContext); 338 CHPP_LOGD("chppWwanGetCellInfoAsyncResult received data len=%" PRIuSIZE, len); 339 340 struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf; 341 struct chreWwanCellInfoResult *chre = NULL; 342 uint8_t errorCode = CHRE_ERROR; 343 344 if (len == sizeof(struct ChppAppHeader)) { 345 // Short response length indicates an error 346 CHPP_LOGE("GetCellInfo resp. too short. err=%" PRIu8, rxHeader->error); 347 348 if (rxHeader->error == CHPP_APP_ERROR_NONE) { 349 errorCode = CHPP_APP_ERROR_INVALID_LENGTH; 350 } else { 351 errorCode = chppAppErrorToChreError(rxHeader->error); 352 } 353 354 } else { 355 buf += sizeof(struct ChppAppHeader); 356 len -= sizeof(struct ChppAppHeader); 357 chre = 358 chppWwanCellInfoResultToChre((struct ChppWwanCellInfoResult *)buf, len); 359 360 if (chre == NULL) { 361 CHPP_LOGE("Cell info conversion failed len=%" PRIuSIZE " err=%" PRIu8, 362 len, rxHeader->error); 363 } 364 } 365 366 if (chre == NULL) { 367 chre = chppMalloc(sizeof(struct chreWwanCellInfoResult)); 368 if (chre == NULL) { 369 CHPP_LOG_OOM(); 370 } else { 371 chre->version = CHRE_WWAN_CELL_INFO_RESULT_VERSION; 372 chre->errorCode = errorCode; 373 chre->cellInfoCount = 0; 374 chre->reserved = 0; 375 chre->cookie = 0; 376 chre->cells = NULL; 377 } 378 379 } else { 380 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC 381 int64_t offset = chppTimesyncGetOffset(gWwanClientContext.client.appContext, 382 CHPP_WWAN_MAX_TIMESYNC_AGE_NS); 383 for (uint8_t i = 0; i < chre->cellInfoCount; i++) { 384 uint64_t *timeStamp = 385 (uint64_t *)(CHPP_CONST_CAST_POINTER(&chre->cells[i].timeStamp)); 386 *timeStamp -= (uint64_t)offset; 387 } 388 #endif 389 } 390 391 if (chre != NULL) { 392 gCallbacks->cellInfoResultCallback(chre); 393 } 394 } 395 396 /** 397 * Initializes the WWAN client upon an open request from CHRE and responds with 398 * the result. 399 * 400 * @param systemApi CHRE system function pointers. 401 * @param callbacks CHRE entry points. 402 * 403 * @return True if successful. False otherwise. 404 */ 405 static bool chppWwanClientOpen(const struct chrePalSystemApi *systemApi, 406 const struct chrePalWwanCallbacks *callbacks) { 407 CHPP_DEBUG_ASSERT(systemApi != NULL); 408 CHPP_DEBUG_ASSERT(callbacks != NULL); 409 410 bool result = false; 411 gSystemApi = systemApi; 412 gCallbacks = callbacks; 413 414 CHPP_LOGD("WWAN client opening"); 415 416 // Wait for discovery to complete for "open" call to succeed 417 if (chppWaitForDiscoveryComplete(gWwanClientContext.client.appContext, 418 CHPP_WWAN_DISCOVERY_TIMEOUT_MS)) { 419 result = chppClientSendOpenRequest( 420 &gWwanClientContext.client, &gWwanClientContext.rRState[CHPP_WWAN_OPEN], 421 CHPP_WWAN_OPEN, 422 /*blocking=*/true); 423 } 424 425 #ifdef CHPP_WWAN_CLIENT_OPEN_ALWAYS_SUCCESS 426 chppClientPseudoOpen(&gWwanClientContext.client); 427 result = true; 428 #endif 429 430 return result; 431 } 432 433 /** 434 * Deinitializes the WWAN client. 435 */ 436 static void chppWwanClientClose(void) { 437 // Remote 438 struct ChppAppHeader *request = chppAllocClientRequestCommand( 439 &gWwanClientContext.client, CHPP_WWAN_CLOSE); 440 441 if (request == NULL) { 442 CHPP_LOG_OOM(); 443 } else if (chppSendTimestampedRequestAndWait( 444 &gWwanClientContext.client, 445 &gWwanClientContext.rRState[CHPP_WWAN_CLOSE], request, 446 sizeof(*request))) { 447 gWwanClientContext.client.openState = CHPP_OPEN_STATE_CLOSED; 448 gWwanClientContext.capabilities = CHRE_WWAN_CAPABILITIES_NONE; 449 chppClientCloseOpenRequests(&gWwanClientContext.client, &kWwanClientConfig, 450 true /* clearOnly */); 451 } 452 } 453 454 /** 455 * Retrieves a set of flags indicating the WWAN features supported by the 456 * current implementation. 457 * 458 * @return Capabilities flags. 459 */ 460 static uint32_t chppWwanClientGetCapabilities(void) { 461 #ifdef CHPP_WWAN_DEFAULT_CAPABILITIES 462 uint32_t capabilities = CHPP_WWAN_DEFAULT_CAPABILITIES; 463 #else 464 uint32_t capabilities = CHRE_WWAN_CAPABILITIES_NONE; 465 #endif 466 467 if (gWwanClientContext.capabilities != CHRE_WWAN_CAPABILITIES_NONE) { 468 // Result already cached 469 capabilities = gWwanClientContext.capabilities; 470 471 } else { 472 struct ChppAppHeader *request = chppAllocClientRequestCommand( 473 &gWwanClientContext.client, CHPP_WWAN_GET_CAPABILITIES); 474 475 if (request == NULL) { 476 CHPP_LOG_OOM(); 477 } else { 478 if (chppSendTimestampedRequestAndWait( 479 &gWwanClientContext.client, 480 &gWwanClientContext.rRState[CHPP_WWAN_GET_CAPABILITIES], request, 481 sizeof(*request))) { 482 // Success. gWwanClientContext.capabilities is now populated 483 capabilities = gWwanClientContext.capabilities; 484 } 485 } 486 } 487 488 return capabilities; 489 } 490 491 /** 492 * Query information about the current serving cell and its neighbors. This does 493 * not perform a network scan, but should return state from the current network 494 * registration data stored in the cellular modem. 495 * 496 * @return True indicates the request was sent off to the service. 497 */ 498 static bool chppWwanClientGetCellInfoAsync(void) { 499 bool result = false; 500 501 struct ChppAppHeader *request = chppAllocClientRequestCommand( 502 &gWwanClientContext.client, CHPP_WWAN_GET_CELLINFO_ASYNC); 503 504 if (request == NULL) { 505 CHPP_LOG_OOM(); 506 } else { 507 result = chppSendTimestampedRequestOrFail( 508 &gWwanClientContext.client, 509 &gWwanClientContext.rRState[CHPP_WWAN_GET_CELLINFO_ASYNC], request, 510 sizeof(*request), CHPP_CLIENT_REQUEST_TIMEOUT_DEFAULT); 511 } 512 513 return result; 514 } 515 516 /** 517 * Releases the memory held for the GetCellInfoAsync result. 518 */ 519 static void chppWwanClientReleaseCellInfoResult( 520 struct chreWwanCellInfoResult *result) { 521 if (result->cellInfoCount > 0) { 522 void *cells = CHPP_CONST_CAST_POINTER(result->cells); 523 CHPP_FREE_AND_NULLIFY(cells); 524 } 525 526 CHPP_FREE_AND_NULLIFY(result); 527 } 528 529 /************************************************ 530 * Public Functions 531 ***********************************************/ 532 533 void chppRegisterWwanClient(struct ChppAppState *appContext) { 534 chppRegisterClient(appContext, (void *)&gWwanClientContext, 535 &gWwanClientContext.client, gWwanClientContext.rRState, 536 &kWwanClientConfig); 537 } 538 539 void chppDeregisterWwanClient(struct ChppAppState *appContext) { 540 // TODO 541 542 UNUSED_VAR(appContext); 543 } 544 545 struct ChppClientState *getChppWwanClientState(void) { 546 return &gWwanClientContext.client; 547 } 548 549 #ifdef CHPP_CLIENT_ENABLED_WWAN 550 551 #ifdef CHPP_CLIENT_ENABLED_CHRE_WWAN 552 const struct chrePalWwanApi *chrePalWwanGetApi(uint32_t requestedApiVersion) { 553 #else 554 const struct chrePalWwanApi *chppPalWwanGetApi(uint32_t requestedApiVersion) { 555 #endif 556 557 static const struct chrePalWwanApi api = { 558 .moduleVersion = CHPP_PAL_WWAN_API_VERSION, 559 .open = chppWwanClientOpen, 560 .close = chppWwanClientClose, 561 .getCapabilities = chppWwanClientGetCapabilities, 562 .requestCellInfo = chppWwanClientGetCellInfoAsync, 563 .releaseCellInfoResult = chppWwanClientReleaseCellInfoResult, 564 }; 565 566 CHPP_STATIC_ASSERT( 567 CHRE_PAL_WWAN_API_CURRENT_VERSION == CHPP_PAL_WWAN_API_VERSION, 568 "A newer CHRE PAL API version is available. Please update."); 569 570 if (!CHRE_PAL_VERSIONS_ARE_COMPATIBLE(api.moduleVersion, 571 requestedApiVersion)) { 572 return NULL; 573 } else { 574 return &api; 575 } 576 } 577 578 #endif 579