1// Copyright 2017 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "hid-parser/item.h" 6 7#include <string.h> 8#include <zircon/assert.h> 9 10namespace hid { 11namespace impl { 12 13Item::Tag get_main_tag(uint8_t b_tag) { 14 switch (b_tag) { 15 case 8: return Item::Tag::kInput; 16 case 9: return Item::Tag::kOutput; 17 case 10: return Item::Tag::kCollection; 18 case 11: return Item::Tag::kFeature; 19 case 12: return Item::Tag::kEndCollection; 20 default: return Item::Tag::kReserved; 21 } 22} 23 24Item::Tag get_global_tag(uint8_t b_tag) { 25 switch (b_tag) { 26 case 0: return Item::Tag::kUsagePage; 27 case 1: return Item::Tag::kLogicalMinimum; 28 case 2: return Item::Tag::kLogicalMaximum; 29 case 3: return Item::Tag::kPhysicalMinimum; 30 case 4: return Item::Tag::kPhysicalMaximum; 31 case 5: return Item::Tag::kUnitExponent; 32 case 6: return Item::Tag::kUnit; 33 case 7: return Item::Tag::kReportSize; 34 case 8: return Item::Tag::kReportId; 35 case 9: return Item::Tag::kReportCount; 36 case 10: return Item::Tag::kPush; 37 case 11: return Item::Tag::kPop; 38 default: return Item::Tag::kReserved; 39 } 40} 41 42Item::Tag get_local_tag(uint8_t b_tag) { 43 switch (b_tag) { 44 case 0: return Item::Tag::kUsage; 45 case 1: return Item::Tag::kUsageMinimum; 46 case 2: return Item::Tag::kUsageMaximum; 47 case 3: return Item::Tag::kDesignatorIndex; 48 case 4: return Item::Tag::kDesignatorMinimum; 49 case 5: return Item::Tag::kDesignatorMaximum; 50 // No tag for 6. 51 case 7: return Item::Tag::kStringIndex; 52 case 8: return Item::Tag::kStringMinimum; 53 case 9: return Item::Tag::kStringMaximum; 54 case 10: return Item::Tag::kDelimiter; 55 default: return Item::Tag::kReserved; 56 } 57} 58 59// This is the bit pattern for long items which this 60// library does not support. 61constexpr uint8_t kLongItemMarker = 0xfe; 62 63Item::Type get_type_and_size(uint8_t data, uint8_t* size) { 64 if (data == kLongItemMarker) 65 return Item::Type::kLongItem; 66 67 // Short item. 68 // Payload size is 0,1,2,4 bytes. 69 auto b_size = static_cast<uint8_t>(data & 0x03); 70 *size = ( b_size != 3) ? b_size : 4; 71 72 switch ((data >> 2) & 0x03) { 73 case 0: return Item::Type::kMain; 74 case 1: return Item::Type::kGlobal; 75 case 2: return Item::Type::kLocal; 76 default: return Item::Type::kReserved; 77 } 78} 79 80Item::Tag get_tag(Item::Type type, uint8_t data) { 81 uint8_t b_tag = (data >> 4) & 0x0f; 82 switch (type) { 83 case Item::Type::kMain: return get_main_tag(b_tag); 84 case Item::Type::kGlobal: return get_global_tag(b_tag); 85 case Item::Type::kLocal: return get_local_tag(b_tag); 86 default: return Item::Tag::kReserved; 87 } 88} 89 90} // namespace impl. 91 92Item Item::ReadNext(const uint8_t* data, size_t len, size_t* actual) { 93 ZX_DEBUG_ASSERT(len != 0); 94 95 uint8_t size = 0; 96 97 auto type = impl::get_type_and_size(data[0], &size); 98 auto tag = impl::get_tag(type, data[0]); 99 100 // Amount to parse is 1-byte for the header and |size| for the payload 101 // for short items. Long items are not supported. 102 *actual = (type != Item::Type::kLongItem) ? 1 + size : 0; 103 104 uint32_t item_data = 0; 105 if (*actual <= len) { 106 for (uint8_t ix = 0; ix < size; ++ix) { 107 item_data |= data[1 + ix] << (8 * ix); 108 } 109 } 110 111 return Item(type, tag, size, item_data); 112} 113 114// Type punning beyond the magic 'char' type is no longer tolerated 115// for example the simpler "return *reinterpret_cast<T*>(data);" 116// generates a warning even on gcc. 117template <typename T> 118T bit_cast(const uint32_t* data) { 119 T dest; 120 memcpy(&dest, data, sizeof(dest)); 121 return dest; 122} 123 124int32_t Item::signed_data() const { 125 switch (size_) { 126 case 1: return bit_cast<int8_t>(&data_); 127 case 2: return bit_cast<int16_t>(&data_); 128 case 4: return bit_cast<int32_t>(&data_); 129 default: return 0; 130 } 131} 132 133} // namespace hid 134