You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
161 lines
7.1 KiB
161 lines
7.1 KiB
# CHRE API Parser + Code Generator for CHPP
|
|
|
|
Since one of the main use cases of CHPP is to support remoting a CHRE PAL
|
|
implementation to a peripheral component, we leverage the CHRE PAL API
|
|
definitions for the platform-specific implementation of those services, such
|
|
as the WWAN service. However, the structures used in the CHRE PAL APIs are the
|
|
same as those used in the CHRE API for nanoapps, which are not inherently
|
|
serializable, as they include things like pointers to other structures. So we
|
|
must create slightly updated versions of these to send over the wire with CHPP.
|
|
|
|
The `chre_api_to_chpp.py` script in this directory parses CHRE APIs according
|
|
to instructions given in `chre_api_annotations.json` and generates structures
|
|
and conversion code used for serialization and deserialization.
|
|
|
|
## Serialization format
|
|
|
|
_TL;DR: The CHPP format is just a packed and flattened version of the CHRE
|
|
structures, replacing pointers with offsets._
|
|
|
|
The serialized output is meant to match the source CHRE structure as closely as
|
|
possible, however some special handling is needed to be able to send them over
|
|
the wire to a different processor in the system. The general process for
|
|
generating a serializable CHPP version of a CHRE structure is as follows:
|
|
|
|
1. Rewrite the name to start with "Chpp" instead of "chre" and mark it packed,
|
|
to ensure there's no padding between fields (which is compiler-dependent)
|
|
1. Flatten the structure by replacing pointers to nested structures/arrays with
|
|
a ChppOffset which describes the location in the payload where the nested
|
|
data resides.
|
|
1. A CHPP application layer header is allocated for convenience when used in
|
|
CHPP. The header values need to be set by CHPP and they are not zeroed out.
|
|
The header length is not included in the offset calculation.
|
|
|
|
ChppOffset is a collection of a 16-bit offset from the beginning of the payload,
|
|
and a length. While we could implicitly derive the offset, the length cannot
|
|
exceed 16 bits due to limitations in the encapsulating CHPP protocol
|
|
(and we don't expect to bump up against this limit anyways), and including the
|
|
offset field helps simplify decoding.
|
|
|
|
This approach allows for a highly optimized, in-place encoding and decoding that
|
|
only requires converting between pointer and ChppOffset types, if the following
|
|
conditions are met:
|
|
|
|
1. The size of the CHRE and CHPP structures are the same, and the offsets to
|
|
all contained fields are the same, and the same property holds for all
|
|
nested structures/unions
|
|
1. (Encoding only) All nested structures appear in contiguous memory - for
|
|
example, if a structure has a pointer to an array, then the array appears
|
|
immediately after the structure in memory, with no padding
|
|
1. (Decoding only) Processor alignment constraints are met for all fields, such
|
|
that they can be safely accessed through normal means at the location in
|
|
memory at which they reside, when accounting for any headers, etc.
|
|
|
|
If conditions 2 or 3 are not met, then optimized encode/decode is still possible
|
|
after copying the memory to a separate allocation. So in-place conversion can't
|
|
be done, but the only translation needed is between pointer and ChppOffset. If
|
|
the first condition is not met, then conversion is done using field-wise
|
|
assignment, which allows the compiler to generate the necessary instructions to
|
|
handle differences in alignment and packing.
|
|
|
|
All generated code currently assumes that it's running on a little endian CPU,
|
|
as the wire format requires little endian byte order.
|
|
|
|
TODO: The script currently only generates code for the pessimistic case, where
|
|
none of the constraints are met. By generating code that checks sizeof/offsetof
|
|
on all fields and placing the optimized path in an if/else condition, the
|
|
compiler can evaluate condition 1 and prune away code for the less-optimal path.
|
|
|
|
## Annotations
|
|
|
|
As the C API does not have enough information for the script to know how to
|
|
handle things like variable-length arrays (VLAs), we use the
|
|
`chre_api_annotations.json` file to provide this detail. The supported fields
|
|
are explained in the illustrated example below:
|
|
|
|
```json
|
|
[// A list of entries for each input file
|
|
{
|
|
// Path starting at <android_root>/system/chre to the input file
|
|
"filename": "chre_api/include/chre_api/chre/wwan.h",
|
|
// List of #includes that also need to be parsed, e.g. because they provide
|
|
// struct definitions that are used in the file
|
|
"includes": [
|
|
"chre_api/include/chre_api/chre/common.h"
|
|
],
|
|
// Which files the output header should pull in to satisfy dependencies
|
|
"output_includes": [
|
|
"chpp/common/common_types.h",
|
|
"chre_api/chre/wwan.h"
|
|
],
|
|
// A list of entries providing additional information for structs/unions that
|
|
// appear in the input
|
|
"struct_info": [
|
|
{
|
|
"name": "chreWwanCellInfoResult",
|
|
// List of annotations for fields within the struct
|
|
"annotations": [
|
|
// Instead of copying the input, always force setting a specific value
|
|
// for a field. The value is whatever should appear in the code for the
|
|
// assignment statement, or the value to memset it to for an array type.
|
|
{
|
|
// Which field within the struct this annotation applies to
|
|
"field": "version",
|
|
// The type of annotation we're supplying
|
|
"annotation": "fixed_value",
|
|
// Additional information is dependent upon the annotation type
|
|
"value": "CHRE_WWAN_CELL_INFO_RESULT_VERSION"
|
|
},
|
|
// Since the 'cookie' field here is a void*, we're rewriting to a
|
|
// uint32_t to keep the same structure size + field offsets (on 32-bit
|
|
// architectures) - in practice we'll also force the value to 0
|
|
{
|
|
"field": "cookie",
|
|
"annotation": "rewrite_type",
|
|
"type_override": "uint32_t"
|
|
},
|
|
// Indicates a variable length array field, with the number of elements
|
|
// given by another field in the same structure called cellInfoCount
|
|
{
|
|
"field": "cells",
|
|
"annotation": "var_len_array",
|
|
"length_field": "cellInfoCount"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"name": "chreWwanCellInfo",
|
|
"annotations": [
|
|
// In this case, we have a union field, where only one of the members is
|
|
// used, according to the provided mapping on the adjacent discriminator
|
|
// field
|
|
{
|
|
"field": "CellInfo",
|
|
"annotation": "union_variant",
|
|
"discriminator": "cellInfoType",
|
|
"mapping": [
|
|
["CHRE_WWAN_CELL_INFO_TYPE_GSM", "gsm"],
|
|
["CHRE_WWAN_CELL_INFO_TYPE_CDMA", "cdma"],
|
|
["CHRE_WWAN_CELL_INFO_TYPE_LTE", "lte"],
|
|
["CHRE_WWAN_CELL_INFO_TYPE_WCDMA", "wcdma"],
|
|
["CHRE_WWAN_CELL_INFO_TYPE_TD_SCDMA", "tdscdma"],
|
|
["CHRE_WWAN_CELL_INFO_TYPE_NR", "nr"]
|
|
]
|
|
}
|
|
]
|
|
}
|
|
],
|
|
// The list of top-level structures appearing in the input that we should
|
|
// create conversion routines for
|
|
"root_structs": [
|
|
"chreWwanCellInfoResult"
|
|
]
|
|
}]
|
|
```
|
|
|
|
## Requirements
|
|
|
|
Tested with Python 3.7, but most versions of Python 3 should work.
|
|
|
|
Requires pyclibrary - see requirements.txt.
|