Skip to content

daniel-starke/HidDescCTC

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

HidDescCTC

The USB HID Descriptor Compile Time Compiler can be used to define USB HID descriptors without any runtime overhead using a domain specific language and the constexpr feature in C++14.

Features

  • USB HID complaint descriptor encoding
  • zero runtime overhead
  • parameterizable fields and values
  • easy to understand text based declarative language
  • compile time syntax and sanity checks
  • single, dependency free header file (only requires stdint.h and stddef.h)
  • PlatformIO compatible
  • WASM compatible (see playground below)

Playground

Try out the HidDescCTC with C++ here:
https://godbolt.org

The implemented compiler and syntax can also be tried out online using WebAssembly:
https://daniel-starke.github.io/HidDescCTC
This is available as progressive web application (PWA).
Note that all user parameters are replaced with the value 0 here.

Syntax

The used syntax is very close to the one used in the standard and by the HID Descriptor Tool.
Apply the following rules to get the HidDescCTC usage name from the name in the standard:

  • replace leading + by Plus
  • replace /second/second by PerSecondSquared
  • remove all non-alphanumeric characters like space and underscore characters
  • capitalize words/abbreviations, whereas dimensions count as one word (e.g. Usb3dControl)
  • move words with a leading digit behind the first word
  • remove second key meaning for the keyboard/keypad usage table entries
  • replace non-alphanumeric characters of the keyboard/keypad usage table entries with their spelled out names

Usage

Example:

DEF_HID_DESCRIPTOR_AS(
	static hidDesc,
	(R"(
# Example based on HID 1.11 appendix B.1
UsagePage(GenericDesktop)
Usage(Keyboard)
Collection(Application)
	ReportId({id})
	ReportSize(1)
	ReportCount(8)
	UsagePage(Keyboard)
	UsageMinimum(224)
	UsageMaximum(231)
	LogicalMinimum(0)
	LogicalMaximum(1)
	Input(Data, Var, Abs) # Modifier byte
	ReportCount(1)
	ReportSize(8)
	Input(Cnst) # Reserved byte
	ReportCount(5)
	ReportSize(1)
	UsagePage(LED)
	UsageMinimum(1)
	UsageMaximum({maxLedId})
	Output(Data, Var, Abs) # LED report
	ReportCount(1)
	ReportSize(3)
	Output(Cnst) # LED report padding
	ReportCount(6)
	ReportSize(8)
	LogicalMinimum(0)
	LogicalMaximum(255)
	UsagePage(Keyboard)
	UsageMinimum(0)
	UsageMaximum(255)
	Input(Data, Ary)
EndCollection
)")
	("id", 1)
	("maxLedId", 5)
);

This provides the compiled HID descriptor with:

  • hidDesc.data as const uint8_t * pointing to the compiled data
  • hidDesc.size() as size_t with the size of the compiled data

Any number of parameters can be passed and used as seen above.
These, however, need to be compile time evaluable.
Usually, report IDs are defined via enum and included in the HID descriptor accordingly for later use in the protocol. These can be included as parameters in the HID descriptor source.

Define HID_DESCRIPTOR_NO_ERROR_REPORT to suppress syntax error outputs.
DEF_HID_DESCRIPTOR_AS can be used in global, namespace and function scope.

If you rather like to avoid using the macro you can compile the HID descriptor like this:

constexpr static const auto hidSrc = hid::fromSource(R"(
# Example based on HID 1.11 appendix B.1
UsagePage(GenericDesktop)
Usage(Keyboard)
Collection(Application)
	ReportId({id})
	ReportSize(1)
	ReportCount(8)
	UsagePage(Keyboard)
	UsageMinimum(224)
	UsageMaximum(231)
	LogicalMinimum(0)
	LogicalMaximum(1)
	Input(Data, Var, Abs) # Modifier byte
	ReportCount(1)
	ReportSize(8)
	Input(Cnst) # Reserved byte
	ReportCount(5)
	ReportSize(1)
	UsagePage(LED)
	UsageMinimum(1)
	UsageMaximum({maxLedId})
	Output(Data, Var, Abs) # LED report
	ReportCount(1)
	ReportSize(3)
	Output(Cnst) # LED report padding
	ReportCount(6)
	ReportSize(8)
	LogicalMinimum(0)
	LogicalMaximum(255)
	UsagePage(Keyboard)
	UsageMinimum(0)
	UsageMaximum(255)
	Input(Data, Ary)
EndCollection
)")
	("id", 1)
	("maxLedId", 5)
;
constexpr static const auto hidDesc = hid::Descriptor<hid::compiledSize(hidSrc)>(hidSrc);

This performs the same steps as the macro above but without error reporting. Compile time error reporting can be added with the following code:

constexpr static const hid::Error error = hid::compileError(hidSrc);
constexpr static const size_t dummy = hid::reporter<error.line, error.column, error.message>();

PlatformIO Integration

The PlatformIO documentation describes here and here how to add libraries from GitHub directly.
The latest HidDescCTC version can be included by adding the following line to platformio.ini in your environment section:

lib_deps = HidDescCTC=https://github.com/daniel-starke/HidDescCTC/archive/refs/heads/main.zip

USB Standard References

Other References

Extended Backus–Naur Form

Syntax description according to ISO/IEC 14977.

Exponent = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" ;
SignedExponent = "-", ( Exponent | "8" ) ;
Digit = Exponent | "8" | "9" ;

LowerLetter = "a" | "b" | "c" | "d" | "e" | "f" | "g"  "h" | "i" | "j" | "k" | "l" | "m" | "n"  "o" | "p" | q" | "r" | "s" | "t" | "u"  "v" | "w" | "x" | "y" | "z" ;
UpperLetter = "A" | "B" | "C" | "D" | "E" | "F" | "G"  "H" | "I" | "J" | "K" | "L" | "M" | "N"  "O" | "P" | Q" | "R" | "S" | "T" | "U"  "V" | "W" | "X" | "Y" | "Z" ;
EndOfLine = ? line-feed or carrier-return ? ;
Character = ? all visible characters ? ;

Number = Digit, { Digit } ;
SignedNumber = "-", Number ;
HexNumber = "0x", Digit, { Digit } ;

ItemChar = { "_" | LowerLetter | UpperLetter } ;
ArgChar = { ItemChar | Digit } ;

Parameter = "{", { Character - "}" }, "}" ;

UnitName = "Length" | "Mass" | "Time" | "Temp" | "Current" | "Luminous" ;
BaseUnit = UnitName, [ "^", ( Exponent | SignedExponent ) ] ;
Unit = BaseUnit, { BaseUnit } ;

ArgumentName = ItemChar, { ArgChar } ;
Argument = ArgumentName | Number | SignedNumber | HexNumber | Parameter ;
ArgumentList = Argument, ( ( "(", Unit, ")" ) | { ",", Argument } ) ;

Item = ItemChar, { ItemChar }, [ "(", ArgumentList , ")" ] ;
Comment = ( ";" | "#" ), { Character - EndOfLine } ;

Grammar = { Item | Number | HexNumber | Parameter | Comment } ;

Limitations

  • Deduction of the Usage Page map from a numeric value or user parameter is not supported.
  • Semantic checks against usage types as defined in HID 1.11 ch. 3.4 are not yet implemented.

License

See LICENSE.

Contributions

No content contributions are accepted. Please file a bug report or feature request instead.
This decision was made in consideration of the used license.