blob: 267b2ad5247baccd2d139b6a6c9abfcb2f5b770b [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "hid-parser/item.h"
#include <string.h>
#include <zircon/assert.h>
namespace hid {
namespace impl {
Item::Tag get_main_tag(uint8_t b_tag) {
switch (b_tag) {
case 8: return Item::Tag::kInput;
case 9: return Item::Tag::kOutput;
case 10: return Item::Tag::kCollection;
case 11: return Item::Tag::kFeature;
case 12: return Item::Tag::kEndCollection;
default: return Item::Tag::kReserved;
}
}
Item::Tag get_global_tag(uint8_t b_tag) {
switch (b_tag) {
case 0: return Item::Tag::kUsagePage;
case 1: return Item::Tag::kLogicalMinimum;
case 2: return Item::Tag::kLogicalMaximum;
case 3: return Item::Tag::kPhysicalMinimum;
case 4: return Item::Tag::kPhysicalMaximum;
case 5: return Item::Tag::kUnitExponent;
case 6: return Item::Tag::kUnit;
case 7: return Item::Tag::kReportSize;
case 8: return Item::Tag::kReportId;
case 9: return Item::Tag::kReportCount;
case 10: return Item::Tag::kPush;
case 11: return Item::Tag::kPop;
default: return Item::Tag::kReserved;
}
}
Item::Tag get_local_tag(uint8_t b_tag) {
switch (b_tag) {
case 0: return Item::Tag::kUsage;
case 1: return Item::Tag::kUsageMinimum;
case 2: return Item::Tag::kUsageMaximum;
case 3: return Item::Tag::kDesignatorIndex;
case 4: return Item::Tag::kDesignatorMinimum;
case 5: return Item::Tag::kDesignatorMaximum;
// No tag for 6.
case 7: return Item::Tag::kStringIndex;
case 8: return Item::Tag::kStringMinimum;
case 9: return Item::Tag::kStringMaximum;
case 10: return Item::Tag::kDelimiter;
default: return Item::Tag::kReserved;
}
}
// This is the bit pattern for long items which this
// library does not support.
constexpr uint8_t kLongItemMarker = 0xfe;
Item::Type get_type_and_size(uint8_t data, uint8_t* size) {
if (data == kLongItemMarker)
return Item::Type::kLongItem;
// Short item.
// Payload size is 0,1,2,4 bytes.
auto b_size = static_cast<uint8_t>(data & 0x03);
*size = ( b_size != 3) ? b_size : 4;
switch ((data >> 2) & 0x03) {
case 0: return Item::Type::kMain;
case 1: return Item::Type::kGlobal;
case 2: return Item::Type::kLocal;
default: return Item::Type::kReserved;
}
}
Item::Tag get_tag(Item::Type type, uint8_t data) {
uint8_t b_tag = (data >> 4) & 0x0f;
switch (type) {
case Item::Type::kMain: return get_main_tag(b_tag);
case Item::Type::kGlobal: return get_global_tag(b_tag);
case Item::Type::kLocal: return get_local_tag(b_tag);
default: return Item::Tag::kReserved;
}
}
} // namespace impl.
Item Item::ReadNext(const uint8_t* data, size_t len, size_t* actual) {
ZX_DEBUG_ASSERT(len != 0);
uint8_t size = 0;
auto type = impl::get_type_and_size(data[0], &size);
auto tag = impl::get_tag(type, data[0]);
// Amount to parse is 1-byte for the header and |size| for the payload
// for short items. Long items are not supported.
*actual = (type != Item::Type::kLongItem) ? 1 + size : 0;
uint32_t item_data = 0;
if (*actual <= len) {
for (uint8_t ix = 0; ix < size; ++ix) {
item_data |= data[1 + ix] << (8 * ix);
}
}
return Item(type, tag, size, item_data);
}
// Type punning beyond the magic 'char' type is no longer tolerated
// for example the simpler "return *reinterpret_cast<T*>(data);"
// generates a warning even on gcc.
template <typename T>
T bit_cast(const uint32_t* data) {
T dest;
memcpy(&dest, data, sizeof(dest));
return dest;
}
int32_t Item::signed_data() const {
switch (size_) {
case 1: return bit_cast<int8_t>(&data_);
case 2: return bit_cast<int16_t>(&data_);
case 4: return bit_cast<int32_t>(&data_);
default: return 0;
}
}
} // namespace hid