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