blob: ca8bba94f6410ca62e9ac938329a0388e9121638 [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.
#ifndef HID_PARSER_PARSER_H_
#define HID_PARSER_PARSER_H_
#include <stdint.h>
#include <stdlib.h>
namespace hid {
// The hid report descriptor parser consists of a single function
// ParseReportDescriptor() that takes as input a USB report descriptor
// byte stream and on success returns a heap-allocated DeviceDescriptor
// structure. When not needed it can be freed via FreeDeviceDescriptor().
//
// The DeviceDescriptor data is organized at the first level by the
// three arrays which correspond to the feature fields for the input,
// output, and feature reports. Input, ouput, and feature reports each
// have their own length and fields: they are logically connected only
// because they share a report_id. The arrays of report features are of
// length {type}_count. Eg: the input_fields array has input_size ReportField
// structures.
//
// report[0].input_fields ---> ReportField
// +report_id
// +field_type == kInput
// +attr
// +col -------> Collection
// +type
// +parent ---> Collection
//
// The structure describes all the information returned by the device;
// no information present in the original stream is lost.
//
// The attr field of the ReportField contains all information to parse
// a report sent by the device. The ExtractUint functions will use the
// offset in the attribute to extract the necessary data.
//
// An example will enlighten. Assume |report| array as follows,
// with most fields omitted for brevity:
//
// report[0]
// .input_fields---> [0] report_id: 1
// usage button,1
// flags data,var
// bit_sz 1
// // For descriptors that have more than 1 report,
// // the first 8 bits are always the report id
// offset 8
//
// [1] report_id: 1
// usage button,2
// flags data,var
// bit_sz 1
// offset 9
//
// [2] report_id: 1
// usage button,none
// flags const
// bit_sz 6
// offset 10
// report[1]
// .input_fields---> [3] report_id: 3
// usage desktop,X
// flags data,var
// bit_sz 8
// offset 8
//
// [4] report_id: 3
// usage desktop,Y
// flags data,var
// bit_sz 8
// offset 16
// report[2]
// .input_fields---> [5] report_id: 4
// usage desktop,wheel
// flags data,var
// bit_sz 5
// offset 8
//
// [6] report_id: 4
// usage desktop,none
// flags const
// bit_sz 3
// offset 13
//
// Now given the following report stream, with byte-order
// left to right:
//
// 03 b4 67 01 02 04 13 03 b5 6a
// ------>--------->----------->
//
// Can be parsed as the following 4 reports:
//
// - 03 is report id, so Node [3] and Node [4] are in play
// b4 is X-coord/desktop (8-bits)
// 67 is Y-coord/desktop (8-bits)
//
// - 01 is report id, so Node [0], Node [1] and Node [3] are in play
// 02 is split into bits
// 0 is 1/button (1-bit)
// 1 is 2/button (1-bit)
// 0 is none/button (7-bit) padding
//
// - 04 is report id, so Node [5] and Node [6] are in play
// 13 is split into bits
// 13 is desktop/wheel (5-bit)
// 0 is desktop/none (3-bit) padding
//
// - 03 is report id, so Node [3] and Node [4] are in play
// b5 is X-coord/desktop (8-bits)
// 6a is Y-coord/desktop (8-bits)
//
//
// Input, Output, and Feature reports are all sent over unique channels, which
// is how they are distingushed. This is important because they can share the
// same report id.
// Logical minimum and maximum per hid spec.
struct MinMax {
int64_t min;
int64_t max;
};
// Physical units descriptor.
struct Unit {
uint32_t type;
int32_t exp;
};
// Describes the semantic meaning of fields. See the "HID Usage tables"
// document from usb.org.
struct Usage {
uint16_t page;
uint32_t usage;
};
enum class CollectionType : uint32_t {
kPhysical,
kApplication,
kLogical,
kReport,
kNamedArray,
kUsageSwitch,
kUsageModifier,
kReserved,
kVendor
};
enum NodeType : uint32_t {
kInput,
kOutput,
kFeature
};
enum FieldTypeFlags : uint32_t {
// Indicates if field can be modfied. Constant often means is padding.
kData = 1 << 0,
kConstant = 1 << 1,
// The field is either an array or scalar. If it is an array only
// the kData|kConstant and kAbsolute|kRelative flags are valid.
kArray = 1 << 2,
kScalar = 1 << 3,
// Value is absolute wrt to a fixed origin or not.
kAbsolute = 1 << 4,
kRelative = 1 << 5,
// Whether the data rolls over wrt to the logical min/max.
kNoWrap = 1 << 6,
kWrap = 1 << 7,
// Data has been pre-processed, for example dead-zone.
kLinear = 1 << 8,
kNonLinear = 1 << 9,
// Value returns to a preset value when the user is not interacting with control.
kPreferredState = 1 << 10,
kNoPreferred = 1 << 11,
// If the control can enter a state when it does not report data.
kNoNullPosition = 1 << 12,
kNullState = 1 << 13,
// Output-only: can the value be modified without host interaction.
kNonVolatile = 1 << 14,
kVolatile = 1 << 15,
// Data is a fixed size stream.
kBitField = 1 << 16,
kBufferedBytes = 1 << 17,
};
// TODO(cpu): consider repurposing the kData| kArray | kNonLinear to indicate
// an array field which requires a lookup table. See adafruit trinket report id 4
// for an example of this case.
struct Collection {
CollectionType type;
Usage usage;
Collection* parent; // Enclosing collection or null.
};
struct Attributes {
Usage usage;
Unit unit;
MinMax logc_mm;
MinMax phys_mm;
uint8_t bit_sz;
uint32_t offset;
};
struct ReportField {
uint8_t report_id;
Attributes attr;
NodeType type;
uint32_t flags;
Collection* col;
};
struct ReportDescriptor {
uint8_t report_id;
// The byte size includes the 1 byte for the report ID if the report ID is
// not equal to zero.
size_t input_byte_sz;
size_t input_count;
ReportField* input_fields;
size_t output_byte_sz;
size_t output_count;
ReportField* output_fields;
size_t feature_byte_sz;
size_t feature_count;
ReportField* feature_fields;
};
struct DeviceDescriptor {
size_t rep_count;
ReportDescriptor report[];
};
enum ParseResult : uint32_t {
kParseOk = 0,
kParseNoMemory = 1,
kParseMoreNeeded = 2,
kParseUnsuported = 3,
kParseInvalidTag = 4,
kParseInvalidItemType = 5,
kParseInvalidItemValue = 6,
kParseUsageLimit = 7,
kParseInvalidRange = 8,
kParseOverflow = 9,
kParseLeftovers = 10,
kParseUnexpectedCol = 11,
kParseUnexectedItem = 12,
kParseInvalidUsage = 13,
kParseMissingUsage = 14,
kParserMissingPage = 15,
kParserUnexpectedPop = 16,
kParserInvalidID = 17
};
ParseResult ParseReportDescriptor(
const uint8_t* rpt_desc, size_t desc_len,
DeviceDescriptor** dev_desc);
void FreeDeviceDescriptor(DeviceDescriptor* dev_desc);
Collection* GetAppCollection(const ReportField* field);
// Helper for creating Usage constants.
template <typename P, typename U>
constexpr Usage USAGE(P page, U usage) {
return Usage{static_cast<uint16_t>(page), static_cast<uint32_t>(usage)};
}
} // namespace hid
inline bool operator==(hid::Usage a, hid::Usage b) {
return (a.page == b.page) && (a.usage == b.usage);
}
inline bool operator!=(hid::Usage a, hid::Usage b) {
return (a.page != b.page) || (a.usage != b.usage);
}
inline bool operator==(hid::MinMax a, hid::MinMax b) {
return (a.min == b.min) && (a.max == b.max);
}
#endif // HID_PARSER_PARSER_H_