| # 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. |