169 lines
5.3 KiB
C
169 lines
5.3 KiB
C
/*
|
|
* 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;
|
|
}
|