/* * 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; }