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 */
chppDispatchWwanRequest(void * serviceContext,uint8_t * buf,size_t len)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 */
chppWwanServiceOpen(struct ChppWwanServiceState * wwanServiceContext,struct ChppAppHeader * requestHeader)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 */
chppWwanServiceClose(struct ChppWwanServiceState * wwanServiceContext,struct ChppAppHeader * requestHeader)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 */
chppWwanServiceNotifyReset(void * serviceContext)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 */
chppWwanServiceGetCapabilities(struct ChppWwanServiceState * wwanServiceContext,struct ChppAppHeader * requestHeader)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 */
chppWwanServiceGetCellInfoAsync(struct ChppWwanServiceState * wwanServiceContext,struct ChppAppHeader * requestHeader)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 */
chppWwanServiceCellInfoResultCallback(struct chreWwanCellInfoResult * result)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
chppRegisterWwanService(struct ChppAppState * appContext)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
chppDeregisterWwanService(struct ChppAppState * appContext)428 void chppDeregisterWwanService(struct ChppAppState *appContext) {
429 // TODO
430
431 UNUSED_VAR(appContext);
432 }
433