|  | /* | 
|  | * Copyright (C) 2021 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | #include "chre/pal/util/wifi_pal_convert.h" | 
|  |  | 
|  | // Constants defining the number of bits per LCI IE field. | 
|  | #define LCI_IE_UNCERTAINTY_BITS 6 | 
|  | #define LCI_IE_LAT_LONG_BITS 34 | 
|  | #define LCI_IE_ALT_TYPE_BITS 4 | 
|  | #define LCI_IE_ALT_BITS 30 | 
|  |  | 
|  | // The LCI subelement ID. | 
|  | #define LCI_SUBELEMENT_ID 0 | 
|  |  | 
|  | /************************************************ | 
|  | *  Private functions | 
|  | ***********************************************/ | 
|  |  | 
|  | /** | 
|  | * Reverses the bit positions in a byte. | 
|  | * | 
|  | * @param input The input byte. | 
|  | * | 
|  | * @return The output byte with reversed bits. | 
|  | */ | 
|  | static uint8_t reverseBits(uint8_t input) { | 
|  | uint8_t output = 0; | 
|  | for (size_t i = 0; i < 8; i++) { | 
|  | output <<= 1; | 
|  | uint8_t tmp = (uint8_t)(input & 1); | 
|  | output |= tmp; | 
|  | input >>= 1; | 
|  | } | 
|  |  | 
|  | return output; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @param buf A non-null pointer to a buffer. | 
|  | * @param bufferBitOffset The bit offset with respect to the buffer pointer. | 
|  | * | 
|  | * @return The bit value of the desired bit offset. | 
|  | */ | 
|  | static uint64_t getBitAtBitOffsetInByteArray(const uint8_t *buf, | 
|  | size_t bufferBitOffset) { | 
|  | size_t index = bufferBitOffset / 8; | 
|  | size_t offsetInByte = bufferBitOffset % 8; | 
|  | return ((buf[index] & 0x80 >> offsetInByte) != 0); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns the field value of the LCI IE buffer. | 
|  | * | 
|  | * The user must invoke this method in order of the IE data fields, providing | 
|  | * the number of bits the field is encoded as in numBits, and updating | 
|  | * bufferBitPos sequentially. | 
|  | * | 
|  | * @param buf A non-null pointer to a buffer. | 
|  | * @param numBits The number of bits the value is encoded as. | 
|  | * @param bufferBitPos The current bit position. This value will be updated as a | 
|  | * result of this function invocation, and will be incremented by numBits. | 
|  | * | 
|  | * @return The field value. | 
|  | */ | 
|  | static uint64_t getField(const uint8_t *buf, size_t numBits, | 
|  | size_t *bufferBitPos) { | 
|  | uint64_t field = 0; | 
|  | for (size_t i = 0; i < numBits; i++) { | 
|  | // Per specs, we need to store the bits in MSB first per field, | 
|  | // so we store the bits in reverse order (since we have reverse the bits | 
|  | // per byte earlier). | 
|  | field |= getBitAtBitOffsetInByteArray(buf, *bufferBitPos + i) << i; | 
|  | } | 
|  |  | 
|  | *bufferBitPos += numBits; | 
|  | return field; | 
|  | } | 
|  |  | 
|  | static int64_t convert34BitTwosComplementToInt64(uint64_t input) { | 
|  | // This is 34 bits, so we need to sign extend | 
|  | if ((input & 0x200000000) != 0) { | 
|  | input |= 0xFFFFFFFC00000000; | 
|  | } | 
|  |  | 
|  | return (int64_t)input; | 
|  | } | 
|  |  | 
|  | static int32_t convert30BitTwosComplementToInt32(uint32_t input) { | 
|  | // This is 30 bits, so we need to sign extend | 
|  | if ((input & 0x20000000) != 0) { | 
|  | input |= 0xC0000000; | 
|  | } | 
|  |  | 
|  | return (int32_t)input; | 
|  | } | 
|  |  | 
|  | static void decodeLciSubelement(const uint8_t *lciSubelement, | 
|  | struct chreWifiLci *out) { | 
|  | uint8_t lciDataTmp[CHRE_LCI_SUBELEMENT_DATA_LEN_BYTES]; | 
|  | size_t bufferBitPos = 0; | 
|  | uint64_t x; | 
|  |  | 
|  | // First, reverse the bits to get the LSB first per specs. | 
|  | for (size_t i = 0; i < CHRE_LCI_SUBELEMENT_DATA_LEN_BYTES; i++) { | 
|  | lciDataTmp[i] = reverseBits(lciSubelement[i]); | 
|  | } | 
|  |  | 
|  | out->latitudeUncertainty = | 
|  | (uint8_t)getField(lciDataTmp, LCI_IE_UNCERTAINTY_BITS, &bufferBitPos); | 
|  |  | 
|  | x = getField(lciDataTmp, LCI_IE_LAT_LONG_BITS, &bufferBitPos); | 
|  | out->latitude = convert34BitTwosComplementToInt64(x); | 
|  |  | 
|  | out->longitudeUncertainty = | 
|  | (uint8_t)getField(lciDataTmp, LCI_IE_UNCERTAINTY_BITS, &bufferBitPos); | 
|  |  | 
|  | x = getField(lciDataTmp, LCI_IE_LAT_LONG_BITS, &bufferBitPos); | 
|  | out->longitude = convert34BitTwosComplementToInt64(x); | 
|  |  | 
|  | out->altitudeType = | 
|  | (uint8_t)getField(lciDataTmp, LCI_IE_ALT_TYPE_BITS, &bufferBitPos); | 
|  | out->altitudeUncertainty = | 
|  | (uint8_t)getField(lciDataTmp, LCI_IE_UNCERTAINTY_BITS, &bufferBitPos); | 
|  |  | 
|  | x = getField(lciDataTmp, LCI_IE_ALT_BITS, &bufferBitPos); | 
|  | out->altitude = convert30BitTwosComplementToInt32((uint32_t)x); | 
|  | } | 
|  |  | 
|  | /************************************************ | 
|  | *  Public functions | 
|  | ***********************************************/ | 
|  | bool chreWifiLciFromIe(const uint8_t *ieData, size_t len, | 
|  | struct chreWifiRangingResult *outResult) { | 
|  | bool success = false; | 
|  | const size_t kHeaderLen = | 
|  | CHRE_LCI_IE_HEADER_LEN_BYTES + CHRE_LCI_SUBELEMENT_HEADER_LEN_BYTES; | 
|  | if (len >= kHeaderLen) { | 
|  | size_t pos = CHRE_LCI_IE_HEADER_LEN_BYTES; | 
|  |  | 
|  | uint8_t subelementId = ieData[pos++]; | 
|  | uint8_t subelementLength = ieData[pos++]; | 
|  | if ((subelementId == LCI_SUBELEMENT_ID) && | 
|  | (len >= kHeaderLen + subelementLength)) { | 
|  | success = true; | 
|  | if (subelementLength < CHRE_LCI_SUBELEMENT_DATA_LEN_BYTES) { | 
|  | outResult->flags = 0; | 
|  | } else { | 
|  | outResult->flags = CHRE_WIFI_RTT_RESULT_HAS_LCI; | 
|  | decodeLciSubelement(&ieData[pos], &outResult->lci); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return success; | 
|  | } |