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/services/wwan.h" 18 19 #include <inttypes.h> 20 #include <stdbool.h> 21 #include <stddef.h> 22 #include <stdint.h> 23 24 #include "chpp/common/standard_uuids.h" 25 #include "chpp/common/wwan.h" 26 #include "chpp/common/wwan_types.h" 27 #include "chpp/log.h" 28 #include "chpp/macros.h" 29 #include "chpp/memory.h" 30 #include "chpp/services.h" 31 #include "chre/pal/wwan.h" 32 33 /************************************************ 34 * Prototypes 35 ***********************************************/ 36 37 static enum ChppAppErrorCode chppDispatchWwanRequest(void *serviceContext, 38 uint8_t *buf, size_t len); 39 static void chppWwanServiceNotifyReset(void *serviceContext); 40 41 /************************************************ 42 * Private Definitions 43 ***********************************************/ 44 45 /** 46 * Configuration parameters for this service 47 */ 48 static const struct ChppService kWwanServiceConfig = { 49 .descriptor.uuid = CHPP_UUID_WWAN_STANDARD, 50 51 // Human-readable name 52 .descriptor.name = "WWAN", 53 54 // Version 55 .descriptor.version.major = 1, 56 .descriptor.version.minor = 0, 57 .descriptor.version.patch = 0, 58 59 // Notifies service if CHPP is reset 60 .resetNotifierFunctionPtr = &chppWwanServiceNotifyReset, 61 62 // Client request dispatch function pointer 63 .requestDispatchFunctionPtr = &chppDispatchWwanRequest, 64 65 // Client notification dispatch function pointer 66 .notificationDispatchFunctionPtr = NULL, // Not supported 67 68 // Min length is the entire header 69 .minLength = sizeof(struct ChppAppHeader), 70 }; 71 72 /** 73 * Structure to maintain state for the WWAN service and its Request/Response 74 * (RR) functionality. 75 */ 76 struct ChppWwanServiceState { 77 struct ChppServiceState service; // WWAN service state 78 const struct chrePalWwanApi *api; // WWAN PAL API 79 80 struct ChppRequestResponseState open; // Service init state 81 struct ChppRequestResponseState close; // Service deinit state 82 struct ChppRequestResponseState getCapabilities; // Get Capabilities state 83 struct ChppRequestResponseState getCellInfoAsync; // Get CellInfo Async state 84 }; 85 86 // Note: This global definition of gWwanServiceContext supports only one 87 // instance of the CHPP WWAN service at a time. This limitation is primarily due 88 // to the PAL API. 89 // It would be possible to generate different API and callback pointers to 90 // support multiple instances of the service or modify the PAL API to pass a 91 // void* for context, but this is not necessary in the current version of CHPP. 92 // In such case, wwanServiceContext would be allocated dynamically as part of 93 // chppRegisterWwanService(), e.g. 94 // struct ChppWwanServiceState *wwanServiceContext = chppMalloc(...); 95 // instead of globally here. 96 struct ChppWwanServiceState gWwanServiceContext; 97 98 /************************************************ 99 * Prototypes 100 ***********************************************/ 101 102 static enum ChppAppErrorCode chppWwanServiceOpen( 103 struct ChppWwanServiceState *wwanServiceContext, 104 struct ChppAppHeader *requestHeader); 105 static enum ChppAppErrorCode chppWwanServiceClose( 106 struct ChppWwanServiceState *wwanServiceContext, 107 struct ChppAppHeader *requestHeader); 108 static enum ChppAppErrorCode chppWwanServiceGetCapabilities( 109 struct ChppWwanServiceState *wwanServiceContext, 110 struct ChppAppHeader *requestHeader); 111 static enum ChppAppErrorCode chppWwanServiceGetCellInfoAsync( 112 struct ChppWwanServiceState *wwanServiceContext, 113 struct ChppAppHeader *requestHeader); 114 115 static void chppWwanServiceCellInfoResultCallback( 116 struct chreWwanCellInfoResult *result); 117 118 /************************************************ 119 * Private Functions 120 ***********************************************/ 121 122 /** 123 * Dispatches a client request from the transport layer that is determined to be 124 * for the WWAN service. If the result of the dispatch is an error, this 125 * function responds to the client with the same error. 126 * 127 * This function is called from the app layer using its function pointer given 128 * during service registration. 129 * 130 * @param serviceContext Maintains status for each service instance. 131 * @param buf Input data. Cannot be null. 132 * @param len Length of input data in bytes. 133 * 134 * @return Indicates the result of this function call. 135 */ 136 static enum ChppAppErrorCode chppDispatchWwanRequest(void *serviceContext, 137 uint8_t *buf, size_t len) { 138 struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf; 139 struct ChppWwanServiceState *wwanServiceContext = 140 (struct ChppWwanServiceState *)serviceContext; 141 struct ChppRequestResponseState *rRState = NULL; 142 enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE; 143 bool dispatched = true; 144 145 UNUSED_VAR(len); 146 147 switch (rxHeader->command) { 148 case CHPP_WWAN_OPEN: { 149 rRState = &wwanServiceContext->open; 150 chppServiceTimestampRequest(rRState, rxHeader); 151 error = chppWwanServiceOpen(wwanServiceContext, rxHeader); 152 break; 153 } 154 155 case CHPP_WWAN_CLOSE: { 156 rRState = &wwanServiceContext->close; 157 chppServiceTimestampRequest(rRState, rxHeader); 158 error = chppWwanServiceClose(wwanServiceContext, rxHeader); 159 break; 160 } 161 162 case CHPP_WWAN_GET_CAPABILITIES: { 163 rRState = &wwanServiceContext->getCapabilities; 164 chppServiceTimestampRequest(rRState, rxHeader); 165 error = chppWwanServiceGetCapabilities(wwanServiceContext, rxHeader); 166 break; 167 } 168 169 case CHPP_WWAN_GET_CELLINFO_ASYNC: { 170 rRState = &wwanServiceContext->getCellInfoAsync; 171 chppServiceTimestampRequest(rRState, rxHeader); 172 error = chppWwanServiceGetCellInfoAsync(wwanServiceContext, rxHeader); 173 break; 174 } 175 176 default: { 177 dispatched = false; 178 error = CHPP_APP_ERROR_INVALID_COMMAND; 179 break; 180 } 181 } 182 183 if (dispatched == true && error != CHPP_APP_ERROR_NONE) { 184 // Request was dispatched but an error was returned. Close out 185 // chppServiceTimestampRequest() 186 chppServiceTimestampResponse(rRState); 187 } 188 189 return error; 190 } 191 192 /** 193 * Initializes the WWAN service upon an open request from the client and 194 * responds to the client with the result. 195 * 196 * @param serviceContext Maintains status for each service instance. 197 * @param requestHeader App layer header of the request. 198 * 199 * @return Indicates the result of this function call. 200 */ 201 static enum ChppAppErrorCode chppWwanServiceOpen( 202 struct ChppWwanServiceState *wwanServiceContext, 203 struct ChppAppHeader *requestHeader) { 204 static const struct chrePalWwanCallbacks palCallbacks = { 205 .cellInfoResultCallback = chppWwanServiceCellInfoResultCallback, 206 }; 207 208 enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE; 209 210 if (wwanServiceContext->service.openState == CHPP_OPEN_STATE_OPENED) { 211 CHPP_LOGE("WWAN service already open"); 212 CHPP_DEBUG_ASSERT(false); 213 error = CHPP_APP_ERROR_INVALID_COMMAND; 214 215 } else if (!wwanServiceContext->api->open( 216 wwanServiceContext->service.appContext->systemApi, 217 &palCallbacks)) { 218 CHPP_LOGE("WWAN PAL open failed"); 219 CHPP_DEBUG_ASSERT(false); 220 error = CHPP_APP_ERROR_BEYOND_CHPP; 221 222 } else { 223 CHPP_LOGI("WWAN service opened"); 224 wwanServiceContext->service.openState = CHPP_OPEN_STATE_OPENED; 225 226 struct ChppAppHeader *response = 227 chppAllocServiceResponseFixed(requestHeader, struct ChppAppHeader); 228 size_t responseLen = sizeof(*response); 229 230 if (response == NULL) { 231 CHPP_LOG_OOM(); 232 error = CHPP_APP_ERROR_OOM; 233 } else { 234 chppSendTimestampedResponseOrFail(&wwanServiceContext->service, 235 &wwanServiceContext->open, response, 236 responseLen); 237 } 238 } 239 240 return error; 241 } 242 243 /** 244 * Deinitializes the WWAN service. 245 * 246 * @param serviceContext Maintains status for each service instance. 247 * @param requestHeader App layer header of the request. 248 * 249 * @return Indicates the result of this function call. 250 */ 251 static enum ChppAppErrorCode chppWwanServiceClose( 252 struct ChppWwanServiceState *wwanServiceContext, 253 struct ChppAppHeader *requestHeader) { 254 enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE; 255 256 wwanServiceContext->api->close(); 257 wwanServiceContext->service.openState = CHPP_OPEN_STATE_CLOSED; 258 259 CHPP_LOGI("WWAN service closed"); 260 261 struct ChppAppHeader *response = 262 chppAllocServiceResponseFixed(requestHeader, struct ChppAppHeader); 263 size_t responseLen = sizeof(*response); 264 265 if (response == NULL) { 266 CHPP_LOG_OOM(); 267 error = CHPP_APP_ERROR_OOM; 268 } else { 269 chppSendTimestampedResponseOrFail(&wwanServiceContext->service, 270 &wwanServiceContext->close, response, 271 responseLen); 272 } 273 274 return error; 275 } 276 277 /** 278 * Notifies the service of an incoming reset. 279 * 280 * @param serviceContext Maintains status for each service instance. 281 */ 282 static void chppWwanServiceNotifyReset(void *serviceContext) { 283 struct ChppWwanServiceState *wwanServiceContext = 284 (struct ChppWwanServiceState *)serviceContext; 285 286 if (wwanServiceContext->service.openState != CHPP_OPEN_STATE_OPENED) { 287 CHPP_LOGW("WWAN service reset but wasn't open"); 288 } else { 289 CHPP_LOGI("WWAN service reset. Closing"); 290 wwanServiceContext->service.openState = CHPP_OPEN_STATE_CLOSED; 291 wwanServiceContext->api->close(); 292 } 293 } 294 295 /** 296 * Retrieves a set of flags indicating the WWAN features supported by the 297 * current implementation. 298 * 299 * @param serviceContext Maintains status for each service instance. 300 * @param requestHeader App layer header of the request. 301 * 302 * @return Indicates the result of this function call. 303 */ 304 static enum ChppAppErrorCode chppWwanServiceGetCapabilities( 305 struct ChppWwanServiceState *wwanServiceContext, 306 struct ChppAppHeader *requestHeader) { 307 enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE; 308 309 struct ChppWwanGetCapabilitiesResponse *response = 310 chppAllocServiceResponseFixed(requestHeader, 311 struct ChppWwanGetCapabilitiesResponse); 312 size_t responseLen = sizeof(*response); 313 314 if (response == NULL) { 315 CHPP_LOG_OOM(); 316 error = CHPP_APP_ERROR_OOM; 317 } else { 318 response->params.capabilities = wwanServiceContext->api->getCapabilities(); 319 320 CHPP_LOGD("chppWwanServiceGetCapabilities returning 0x%" PRIx32 321 ", %" PRIuSIZE " bytes", 322 response->params.capabilities, responseLen); 323 chppSendTimestampedResponseOrFail(&wwanServiceContext->service, 324 &wwanServiceContext->getCapabilities, 325 response, responseLen); 326 } 327 328 return error; 329 } 330 331 /** 332 * Query information about the current serving cell and its neighbors in 333 * response to a client request. This does not perform a network scan, but 334 * should return state from the current network registration data stored in the 335 * cellular modem. 336 * 337 * This function returns an error code synchronously. The requested cellular 338 * information shall be returned asynchronously to the client via the 339 * chppPlatformWwanCellInfoResultEvent() service response. 340 * 341 * @param serviceContext Maintains status for each service instance. 342 * @param requestHeader App layer header of the request. 343 * 344 * @return Indicates the result of this function call. 345 */ 346 static enum ChppAppErrorCode chppWwanServiceGetCellInfoAsync( 347 struct ChppWwanServiceState *wwanServiceContext, 348 struct ChppAppHeader *requestHeader) { 349 UNUSED_VAR(requestHeader); 350 351 enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE; 352 353 if (!wwanServiceContext->api->requestCellInfo()) { 354 CHPP_LOGE( 355 "WWAN requestCellInfo PAL API failed. Unable to register for callback"); 356 error = CHPP_APP_ERROR_UNSPECIFIED; 357 } 358 359 return error; 360 } 361 362 /** 363 * PAL callback to provide the result of a prior Request Cell Info 364 * (cellInfoResultCallback). 365 * 366 * @param result Scan results. 367 */ 368 static void chppWwanServiceCellInfoResultCallback( 369 struct chreWwanCellInfoResult *result) { 370 // Recover state 371 struct ChppRequestResponseState *rRState = 372 &gWwanServiceContext.getCellInfoAsync; 373 struct ChppWwanServiceState *wwanServiceContext = 374 container_of(rRState, struct ChppWwanServiceState, getCellInfoAsync); 375 376 // Craft response per parser script 377 struct ChppWwanCellInfoResultWithHeader *response = NULL; 378 size_t responseLen = 0; 379 if (!chppWwanCellInfoResultFromChre(result, &response, &responseLen)) { 380 CHPP_LOGE("CellInfo conversion failed (OOM?) ID=%" PRIu8, 381 rRState->transaction); 382 383 response = chppMalloc(sizeof(struct ChppAppHeader)); 384 if (response == NULL) { 385 CHPP_LOG_OOM(); 386 } else { 387 responseLen = sizeof(struct ChppAppHeader); 388 } 389 } 390 391 if (response != NULL) { 392 response->header.handle = wwanServiceContext->service.handle; 393 response->header.type = CHPP_MESSAGE_TYPE_SERVICE_RESPONSE; 394 response->header.transaction = rRState->transaction; 395 response->header.error = (responseLen > sizeof(struct ChppAppHeader)) 396 ? CHPP_APP_ERROR_NONE 397 : CHPP_APP_ERROR_CONVERSION_FAILED; 398 response->header.command = CHPP_WWAN_GET_CELLINFO_ASYNC; 399 400 chppSendTimestampedResponseOrFail(&wwanServiceContext->service, rRState, 401 response, responseLen); 402 } 403 404 gWwanServiceContext.api->releaseCellInfoResult(result); 405 } 406 407 /************************************************ 408 * Public Functions 409 ***********************************************/ 410 411 void chppRegisterWwanService(struct ChppAppState *appContext) { 412 gWwanServiceContext.api = chrePalWwanGetApi(CHPP_PAL_WWAN_API_VERSION); 413 414 if (gWwanServiceContext.api == NULL) { 415 CHPP_LOGE( 416 "WWAN PAL API version not compatible with CHPP. Cannot register WWAN " 417 "service"); 418 CHPP_DEBUG_ASSERT(false); 419 420 } else { 421 gWwanServiceContext.service.appContext = appContext; 422 gWwanServiceContext.service.handle = chppRegisterService( 423 appContext, (void *)&gWwanServiceContext, &kWwanServiceConfig); 424 CHPP_DEBUG_ASSERT(gWwanServiceContext.service.handle); 425 } 426 } 427 428 void chppDeregisterWwanService(struct ChppAppState *appContext) { 429 // TODO 430 431 UNUSED_VAR(appContext); 432 } 433