├── .gitignore ├── COPYING ├── README.rst ├── doc ├── descriptor.rst ├── hid.rst ├── index.rst ├── optimizations.rst ├── todo.rst ├── usage.rst └── why.rst ├── examples ├── battery.py ├── keyboard.py └── mouse.py ├── hrdc ├── __init__.py ├── converter.py ├── descriptor │ ├── __init__.py │ ├── compiler.py │ ├── descriptor.py │ ├── dumper.py │ ├── extractor.py │ ├── streamer.py │ └── test.py ├── stream │ ├── __init__.py │ ├── comparer.py │ ├── formatter │ │ ├── __init__.py │ │ ├── base.py │ │ ├── binary.py │ │ ├── code.py │ │ └── hex.py │ ├── item.py │ ├── optimizer │ │ ├── __init__.py │ │ ├── base.py │ │ ├── designator_uniq.py │ │ ├── global_merge.py │ │ ├── main_merge.py │ │ ├── physical_merge.py │ │ ├── range_merge.py │ │ └── usage_page.py │ ├── parser │ │ ├── __init__.py │ │ ├── base.py │ │ ├── binary.py │ │ ├── code.py │ │ └── hex.py │ └── stream.py ├── usage │ ├── __init__.py │ ├── arcade.py │ ├── button.py │ ├── camera.py │ ├── consumer.py │ ├── desktop.py │ ├── device.py │ ├── digitizers.py │ ├── game.py │ ├── keyboard.py │ ├── led.py │ ├── lighting.py │ ├── ordinal.py │ ├── pid.py │ ├── sensors.py │ ├── simulation.py │ ├── telephony.py │ ├── usage.py │ └── vr.py └── util │ ├── __init__.py │ └── named_constant.py ├── setup.py └── tests ├── __init__.py ├── issue_6 ├── __init__.py └── description.py ├── issue_7 ├── __init__.py └── test.py ├── test_basic.py └── test_item.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Nicolas Pouillon, 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ================================ 2 | HID Report Descriptor Compiler 3 | ================================ 4 | 5 | This project handles Human Interface Devices (HID_) Report 6 | Descriptors. 7 | 8 | This is a python library with associated tools to manipulate Report 9 | Descriptors. 10 | 11 | .. contents:: 12 | 13 | Presentation 14 | ============ 15 | 16 | Usually, report descriptors are authored in a source form directly. 17 | Some_ tools_ exist, but they all concentrate on translating 18 | representation of a descriptor, keeping it at the abstraction 19 | level from Specification. 20 | 21 | This project introduces a language for the semantic abstraction level 22 | described in the spec, i.e. using Collection, Values and Reports as 23 | first-class objects, not decomposed as Items. 24 | 25 | Moreover, HID uses a lot of symbolic constants for Usage, Item types, 26 | Data types, etc. This should be transparent to the Report Descriptor 27 | author. 28 | 29 | Abstraction levels 30 | ================== 31 | 32 | Two abstraction levels are handled by this library: 33 | 34 | - the usual abstraction level from the HID specification. Various 35 | encoding are handled: 36 | 37 | - Binary data, 38 | 39 | - Hex blob, 40 | 41 | - C code with pretty comments. 42 | 43 | - High-level descriptor: this is an abstract representation with 44 | Reports, Values, Collections, etc. 45 | 46 | Compiler, converters 47 | ==================== 48 | 49 | Conversion between all these abstractions and encodings is supported 50 | through: 51 | 52 | - A generic HID Report Descriptor encoding converter, 53 | 54 | - A Report Descriptor decompiler to get back to abstract 55 | representation, 56 | 57 | - A report descriptor optimizing compiler. 58 | 59 | Goal 60 | ==== 61 | 62 | If you are still reading, you probably know how to interpret this: 63 | 64 | .. code:: c 65 | 66 | 0x05, 0x01, // UsagePage (desktop) 67 | 0x09, 0x02, // Usage (Mouse) 68 | 0xa1, 0x01, // Collection (Application) 69 | 0x15, 0x81, // LogicalMinimum (-127) 70 | 0x25, 0x7f, // LogicalMaximum (127) 71 | 0x75, 0x08, // ReportSize (8) 72 | 0x95, 0x02, // ReportCount (2) 73 | 0x09, 0x30, // Usage (X) 74 | 0x09, 0x31, // Usage (Y) 75 | 0x81, 0x06, // Input (Variable|Relative) 76 | 0x15, 0x00, // LogicalMinimum (0) 77 | 0x25, 0x01, // LogicalMaximum (1) 78 | 0x75, 0x01, // ReportSize (1) 79 | 0x95, 0x03, // ReportCount (3) 80 | 0x05, 0x09, // UsagePage (button) 81 | 0x19, 0x01, // UsageMinimum (Button(1)) 82 | 0x29, 0x03, // UsageMaximum (Button(3)) 83 | 0x81, 0x02, // Input (Variable) 84 | 0xc0, // EndCollection 85 | 86 | The whole purpose of this project is to stop editing this kind of 87 | source code to concentrate on this: 88 | 89 | .. code:: python 90 | 91 | descriptor = Collection(Collection.Application, desktop.Mouse, 92 | Value(Value.Input, desktop.X, 8, flags = Value.Variable | Value.Relative, logicalMin = -127, logicalMax = 127), 93 | Value(Value.Input, desktop.Y, 8, flags = Value.Variable | Value.Relative, logicalMin = -127, logicalMax = 127), 94 | Value(Value.Input, button.Button(1), 1, logicalMin = 0, logicalMax = 1), 95 | Value(Value.Input, button.Button(2), 1, logicalMin = 0, logicalMax = 1), 96 | Value(Value.Input, button.Button(3), 1, logicalMin = 0, logicalMax = 1), 97 | ) 98 | 99 | \... and let the computer take care of Global Items, Logical ranges, 100 | Physical ranges, Units, etc. 101 | 102 | More information 103 | ================ 104 | 105 | See documentation_ for more information about Usage_, `Descriptor 106 | language`_ and rationale_. 107 | 108 | See examples_. 109 | 110 | License 111 | ======= 112 | 113 | MIT 114 | 115 | .. _HID: http://www.usb.org/developers/hidpage/ 116 | .. _some: https://github.com/DIGImend/hidrd/ 117 | .. _tools: http://www.usb.org/developers/tools/ 118 | .. _documentation: doc/index.rst 119 | .. _usage: doc/usage.rst 120 | .. _descriptor language: doc/descriptor.rst 121 | .. _rationale: doc/why.rst 122 | .. _examples: examples/ 123 | -------------------------------------------------------------------------------- /doc/descriptor.rst: -------------------------------------------------------------------------------- 1 | ================================ 2 | High-level descriptor language 3 | ================================ 4 | 5 | Let's take the following input file: 6 | 7 | .. code:: python 8 | :number-lines: 9 | 10 | from hrdc.usage import * 11 | from hrdc.descriptor import * 12 | 13 | descriptor = Collection(Collection.Application, desktop.Mouse, 14 | Value(Value.Input, desktop.X, 8, flags = Value.Variable | Value.Relative, logicalMin = -127, logicalMax = 127), 15 | Value(Value.Input, desktop.Y, 8, flags = Value.Variable | Value.Relative, logicalMin = -127, logicalMax = 127), 16 | Value(Value.Input, button.Button(1), 1, logicalMin = 0, logicalMax = 1), 17 | Value(Value.Input, button.Button(2), 1, logicalMin = 0, logicalMax = 1), 18 | Value(Value.Input, button.Button(3), 1, logicalMin = 0, logicalMax = 1), 19 | ) 20 | 21 | if __name__ == "__main__": 22 | compile_main(descriptor) 23 | 24 | .. contents:: 25 | 26 | Glue 27 | ==== 28 | 29 | Imports 30 | ------- 31 | 32 | .. code:: python 33 | 34 | from hrdc.usage import * 35 | from hrdc.descriptor import * 36 | 37 | - `hrdc.usage` contains most HID Usage pages defined as 38 | `.` constants, see `usage constants`_. 39 | 40 | - `hrdc.descriptor` contains all constructs for defining a high-level 41 | descriptor tree, see `hierarchical elements`_ and `data items`_. 42 | 43 | Command-line handler 44 | -------------------- 45 | 46 | .. code:: python 47 | 48 | if __name__ == "__main__": 49 | compile_main(descriptor) 50 | 51 | At the end of the script, this code handles the command line only if 52 | the script is invoked as main python script. Using this construct has 53 | a benefical side-effect: script may still be imported from another one 54 | as a library. This way, you may write various device descriptors and 55 | merge them as a composite device through imports, but yet be able to 56 | compile them separatly. 57 | 58 | Data items 59 | ========== 60 | 61 | Values 62 | ------ 63 | 64 | Values define Data items in a descriptor. For each value declared, 65 | all the relevant parameters should be defined, not considering if they 66 | are actually serialized as local or global items. There is no global 67 | parameter in this descriptor format. 68 | 69 | .. code:: python 70 | 71 | Value(way, usage, size, 72 | flags = Data | Variable | Absolute, 73 | logicalMin = 1, logicalMax = None, 74 | physicalMin = None, physicalMax = None, 75 | namedArray = None, 76 | unit = 0, unitExponent = 0, 77 | designator = 0, 78 | string = 0, 79 | count = 1, 80 | alignment = None) 81 | 82 | way 83 | This is the Data Item type, either `Value.Input`, `Value.Output` or `Value.Feature` 84 | usage 85 | This is the Usage for the value. For named arrays, this is the 86 | usage for the surrounding logical collection. 87 | size 88 | Data item size in bits 89 | logicalMin, logicalMax 90 | Logical bounds, i.e. value range that device may encode in the 91 | `size`-bits data field 92 | physicalMin, physicalMax 93 | Physical bounds, i.e. semantic value range reported to HID stack 94 | clients. If left to `None`, they will match logical bounds 95 | namedArray 96 | Must be a `list` of Usage constants, or a `UsageRange` object. 97 | When set, value becomes a Named Array. Physical bounds are invalid 98 | for a Named Array, and only `logicalMin` is relevant to set the 99 | logical value for first array item. This defaults to 1 but may be 100 | set to another value for specific purposes (0 most of the time) 101 | unit 102 | Physical value unit, see units_ below 103 | unitExponent 104 | Base-10 exponent to apply to physical value 105 | designator 106 | Designator index, for physical descriptors 107 | string 108 | String index, for string descriptors 109 | count 110 | Useful for array-of-Named-Arrays only, like in keyboard descriptors 111 | alignment 112 | Alignment to enforce before inserting this value 113 | 114 | Padding 115 | ------- 116 | 117 | `Padding()` is a short-hand for constant `Value()` for a given bit 118 | width. 119 | 120 | For instance the following values will be on two consecutive bytes, 121 | each at lower bit: 122 | 123 | .. code:: python 124 | 125 | Value(Value.Input, button.Button(1), 1, logicalMin = 0, logicalMax = 1), 126 | Padding(Value.Input, 7), 127 | Value(Value.Input, button.Button(2), 1, logicalMin = 0, logicalMax = 1), 128 | 129 | Alignment 130 | --------- 131 | 132 | `Align()` construct is a short-hand for `Padding()` where report data 133 | is aligned on next bit size boundary. 134 | 135 | For instance the following values will be on two consecutive bytes, 136 | each at lower bit: 137 | 138 | .. code:: python 139 | 140 | Value(Value.Input, button.Button(1), 1, logicalMin = 0, logicalMax = 1), 141 | Align(Value.Input, 8), 142 | Value(Value.Input, button.Button(2), 1, logicalMin = 0, logicalMax = 1), 143 | 144 | Hierarchical elements 145 | ===================== 146 | 147 | Collection 148 | ---------- 149 | 150 | `Collection()` generates `Collection` and `End Collection` global 151 | items. This hierarchical object needs a type (`Physical`, 152 | `Application`, `Logical`, `Report`, `NamedArray`, `UsageSwitch` or 153 | `UsageModifier`) and a Usage constant. 154 | 155 | .. code:: python 156 | 157 | Collection(Logical, desktop.Keyboard, 158 | Value(...), 159 | ... 160 | ) 161 | 162 | TopLevel 163 | -------- 164 | 165 | `TopLevel()` is a pseudo-collection that generates no Item, but allows 166 | to have more than one top-level collection. 167 | 168 | .. code:: python 169 | 170 | TopLevel( 171 | Collection(Logical, desktop.Keyboard, 172 | Value(...), 173 | ... 174 | ), 175 | Collection(Logical, consumer.ConsumerControl, 176 | Value(...), 177 | ... 178 | ), 179 | ) 180 | 181 | Report 182 | ------ 183 | 184 | `Report()` is a collection that generates no Item, but sets report ID 185 | for subtree. 186 | 187 | .. code:: python 188 | 189 | Collection(Logical, desktop.Keyboard, 190 | Report(1, 191 | Value(...), 192 | Value(...), 193 | Value(...), 194 | ), 195 | 196 | Report(2, 197 | Value(...), 198 | Value(...), 199 | Value(...), 200 | ), 201 | ) 202 | 203 | Usage constants 204 | =============== 205 | 206 | Usage constants are defined as symbolic values. They refer to objects 207 | that behave like ints, but also have a stringifiable correspondance: 208 | 209 | .. code:: python 210 | 211 | >>> from hrdc.usage import * 212 | >>> desktop.X 213 | 214 | >>> str(desktop.X) 215 | 'desktop.X' 216 | >>> hex(int(desktop.X)) 217 | '0x10030' 218 | 219 | `Usage` class can also resolve named constants from the numerical 220 | value: 221 | 222 | .. code:: python 223 | 224 | >>> from hrdc.usage import * 225 | >>> u = Usage.lookup(0x10031) 226 | >>> str(u) 227 | 'desktop.Y' 228 | 229 | For Named-Array Values, constants can be assembled in a Python list, 230 | but when numerous contiguous Usage constants have to be used, you may 231 | use `UsageRange` utility. It behaves like a Python list (operators 232 | `len`, `[]` and iterable), but avoids explicitly enumerating all 233 | intermediate constants. For instance, a PC keyboard key value is 234 | defined as: 235 | 236 | .. code:: python 237 | 238 | Value(Value.Input, usage = None, size = 8, 239 | namedArray = UsageRange(keyboard.NoEvent, keyboard.KeypadHexadecimal), 240 | logicalMin = 0) 241 | 242 | .. code:: python 243 | 244 | >>> from hrdc.usage import * 245 | >>> r = UsageRange(keyboard.NoEvent, keyboard.KeypadHexadecimal) 246 | >>> str(r[0]) 247 | 'keyboard.NoEvent' 248 | >>> str(r[32]) 249 | 'keyboard.ThreeAndNumber' 250 | 251 | Units 252 | ===== 253 | 254 | Units can either be constructed from `system` and various dimensions 255 | (as in spec), or from well-known constants: 256 | 257 | .. code:: python 258 | 259 | >>> from hrdc.descriptor import * 260 | >>> si_length = Unit.SILinear | Unit.length(1) 261 | >>> hex(si_length) 262 | '0x11' 263 | >>> si_length == Unit.Centimeter 264 | True 265 | 266 | Dimensions are: length, mass, time, temperature, current, 267 | luminousintensity. They take an exponent in range [-8, 7] as 268 | argument. 269 | 270 | Well known constants are: Centimeter, Radian, Inch, Degree, Gram, 271 | Slug, Second, Kelvin, Fahrenheit, Ampere, Candela, CmPerSec, Momentum, 272 | G, Newton, Joule, Volt. 273 | -------------------------------------------------------------------------------- /doc/hid.rst: -------------------------------------------------------------------------------- 1 | ===================== 2 | HID Reader's Digest 3 | ===================== 4 | 5 | Foreword 6 | ======== 7 | 8 | This text is a collection of thoughts about HID spec and 9 | implementations in the wild, somewhere between observation of the 10 | current situation after nearly 20 years of HID existence, and basic 11 | rants about overlooked aspects of the Specification. 12 | 13 | Introduction 14 | ============ 15 | 16 | HID got normalized in the USB-1.0 era. This is a specification from 17 | 1990's, with a strong bias towards Microsoft Windows and Intel. At 18 | the time, machines had a few Megabytes of total RAM, there was nothing 19 | to loose there. 20 | 21 | Today, this may seem anecdotical, but this a various implications, 22 | like using a single endianness (little) everywhere, at a time machines 23 | where both little and big-endian, or preferring compactness over data 24 | alignment. Of course, everything is binary. 25 | 26 | HID is a part of the enormous USB protocol stack. It shares the same 27 | design and phylosophy. Everything has to be generic, flexible, future 28 | proof. At the time, PC suffered from too much custom hardware. USB 29 | was meant to be a generic one-size-fits-all transport. HID was meant 30 | to be the only generic protocol handling user interaction. 31 | 32 | Genericity 33 | ========== 34 | 35 | HID defines a framework for Human Interface Devices. In the 36 | specification, this includes Mice and Keyboards, but also VCR, Tape 37 | Recorders, Graphical Equalizers, Graphical Displays. Many of these 38 | definitions were never actually used. 39 | 40 | Designers of the specification were right about the genericity. 20 41 | years later, Spec is still running and functionnal, and got adopted by 42 | Bluetooth, Bluetooth Low Energy, Miracast (?), etc. Genericity was 43 | designed without compromise. 44 | 45 | HID is a meta-framework that describes a method for describing input 46 | (and output) messages format, from a device standpoint. This was a 47 | good idea, because this way, device implementor may pick and choose 48 | what suits its needs among defined basic blocks, in order to get its 49 | data interpreted by host. 50 | 51 | Flexibility is on device side, genericity is on Host side. 52 | 53 | Lack of examples 54 | ================ 55 | 56 | Problem with HID is its too big to be read, understood, and correctly 57 | implemented the first time an implementor sees the spec. There are 58 | too many corner cases, too many disseminated rules, and too few 59 | examples. 60 | 61 | Today, we find ourselves in from of many devices where a driver is 62 | still needed because the HID descriptor is useless (non-existant, full 63 | of vendor-defined items, or simply broken), killing the whole point of 64 | HID. This situation could have been avoided if spec had code with 65 | many examples to copy from. 66 | 67 | Never forget device implementors are lazy like all other engineers, 68 | but as device makers, they put result of their lazyness in ROMs, 69 | rather than in updatable software... 70 | 71 | Too much genericity 72 | =================== 73 | 74 | In some aspects, genericity killed the whole purpose of the 75 | specification. Coupled with lack of examples, some parts are totally 76 | overlooked by implementors. 77 | 78 | The most relevant example is probably the Physical Interface Devices 79 | page, meant for Force-Feedback applications. Looking back nearly 20 80 | years later, this part of the spec looks totaly over-engineered, to an 81 | extent where nearly nobody actually implemented it correctly. Most 82 | devices with force-feedback actually require an Operating System 83 | driver to circumvent the madness of this page. 84 | 85 | Today, even Operating Systems HID stacks seem to have lost faith in 86 | the PID page. 87 | 88 | Rants 89 | ===== 90 | 91 | Now, my personnal collection of rants. 92 | 93 | "Multiple Instances of a Control" 94 | --------------------------------- 95 | 96 | `HUT1_12v2`_, A.5, p. 132, defines the correct way to define a control 97 | that exists more than once in a single device. 98 | 99 | This has totally been overlooked by device implementors over the 100 | years, to a point where today, guidelines from major OS actually 101 | recommand something opposite to the spirit of the spec. `Android 102 | gamepad guidelines`_ suggests using `Z` and `rZ` for secondary thumb 103 | stick. Purpose of `Z` is for vertical axis (the one orthogonal to 104 | both `X` and `Y`), and `rZ` is meant for *rotations* around the same 105 | axis. Nor for secondary `X/Y` couple. This is broken. 106 | 107 | .. _`HUT1_12v2`: http://www.usb.org/developers/hidpage/Hut1_12v2.pdf 108 | .. _`Android gamepad guidelines`: https://source.android.com/compatibility/android-cdd.pdf 109 | -------------------------------------------------------------------------------- /doc/index.rst: -------------------------------------------------------------------------------- 1 | ================================ 2 | HID Report Descriptor Compiler 3 | ================================ 4 | 5 | * `Usage`_ 6 | * `High-level descriptor language`_ 7 | * `Descriptor Stream Optimizations`_ 8 | * `Why a compiler`_ ? 9 | * `HID Reader's digest`_ 10 | * `To-dos`_ 11 | 12 | .. _`Why a compiler`: why.rst 13 | .. _Usage: usage.rst 14 | .. _`Descriptor Stream Optimizations`: optimizations.rst 15 | .. _`High-level descriptor language`: descriptor.rst 16 | .. _`To-dos`: todo.rst 17 | .. _`HID Reader's digest`: hid.rst 18 | -------------------------------------------------------------------------------- /doc/optimizations.rst: -------------------------------------------------------------------------------- 1 | ===================================== 2 | HID Report Descriptor Optimizations 3 | ===================================== 4 | 5 | When serializing HID report descriptors, `hrdc` can optimize them to 6 | get the canonical report descriptor, avoiding any redundancy of items. 7 | 8 | Various optimization passes are implemented in the library. All 9 | together, they produce a decent report descriptor data stream. 10 | 11 | Here is an overview of each pass. 12 | 13 | .. contents:: 14 | 15 | Global merging 16 | ============== 17 | 18 | In a HID report descriptor, Globals are, as you may think, global. 19 | This means their value is never reset by another item. 20 | 21 | This pass remove duplicates items that set a global item to a value it 22 | already has. 23 | 24 | Main merging 25 | ============ 26 | 27 | Main items are Collection or Data items. This pass merges Data items 28 | that have the same characteristics. For instance, defining three 29 | buttons could explicitly be declared as: 30 | 31 | .. code:: c 32 | 33 | 0x15, 0x00, // LogicalMinimum (0) 34 | 0x25, 0x01, // LogicalMaximum (1) 35 | 0x75, 0x01, // ReportSize (1) 36 | 0x95, 0x01, // ReportCount (1) 37 | 0x05, 0x09, // UsagePage (button) 38 | 0x09, 0x01, // Usage (Button(1)) 39 | 0x81, 0x02, // Input (Variable) 40 | 0x09, 0x02, // Usage (Button(2)) 41 | 0x81, 0x02, // Input (Variable) 42 | 0x09, 0x03, // Usage (Button(3)) 43 | 0x81, 0x02, // Input (Variable) 44 | 45 | This pass merges the three declarations to one with an ajusted 46 | ReportCount: 47 | 48 | .. code:: c 49 | 50 | 0x15, 0x00, // LogicalMinimum (0) 51 | 0x25, 0x01, // LogicalMaximum (1) 52 | 0x75, 0x01, // ReportSize (1) 53 | 0x95, 0x03, // ReportCount (3) 54 | 0x05, 0x09, // UsagePage (button) 55 | 0x09, 0x01, // Usage (Button(1)) 56 | 0x09, 0x02, // Usage (Button(2)) 57 | 0x09, 0x03, // Usage (Button(3)) 58 | 0x81, 0x02, // Input (Variable) 59 | 60 | Note: `Range merging`_ will optimize this further. 61 | 62 | Physical Merging 63 | ================ 64 | 65 | Physical Minimum and Physical Maximum are globals, but they also have 66 | an interesting side effect: if they are both set to 0, they actually 67 | take values from Logical range. 68 | 69 | This pass resets Physical range for all cases where it matches Logical 70 | range, in the hope there will be no need to redeclare it on next item. 71 | 72 | Range merging 73 | ============= 74 | 75 | Usage, String Index and Designator Index are local items that can 76 | either be declared one-by-one, or declared as ranges. 77 | 78 | This pass is a generic pass that can merge multiple simple local items 79 | to their range counterpart. 80 | 81 | For instance, this descriptor: 82 | 83 | .. code:: c 84 | 85 | 0x15, 0x00, // LogicalMinimum (0) 86 | 0x25, 0x01, // LogicalMaximum (1) 87 | 0x75, 0x01, // ReportSize (1) 88 | 0x95, 0x03, // ReportCount (3) 89 | 0x05, 0x09, // UsagePage (button) 90 | 0x09, 0x01, // Usage (Button(1)) 91 | 0x09, 0x02, // Usage (Button(2)) 92 | 0x09, 0x03, // Usage (Button(3)) 93 | 0x81, 0x02, // Input (Variable) 94 | 95 | would be optimized as: 96 | 97 | .. code:: c 98 | 99 | 0x15, 0x00, // LogicalMinimum (0) 100 | 0x25, 0x01, // LogicalMaximum (1) 101 | 0x75, 0x01, // ReportSize (1) 102 | 0x95, 0x03, // ReportCount (3) 103 | 0x05, 0x09, // UsagePage (button) 104 | 0x19, 0x01, // Usage Minimum (Button(1)) 105 | 0x29, 0x03, // Usage Maximum (Button(3)) 106 | 0x81, 0x02, // Input (Variable) 107 | 108 | Usage Page 109 | ========== 110 | 111 | This pass takes 32-bit Usage items and splits them as two items, one 112 | Usage Page item and one Usage item. Usage page will only be reset if 113 | needed. 114 | 115 | Designator Uniq 116 | =============== 117 | 118 | This pass merges Designator items that have the same value as 119 | previous, avoiding redundancy. This is mostly useful for default 120 | situation where no designator is set at all, but where descriptor 121 | compiler still outputs a `Designator(0)` item. 122 | -------------------------------------------------------------------------------- /doc/todo.rst: -------------------------------------------------------------------------------- 1 | ======== 2 | TO-DOs 3 | ======== 4 | 5 | Using this library as starting point, we may: 6 | 7 | - provide high-level building blocks for usual constructs. 8 | 9 | - create a visitor for a descriptor tree that outputs C structures 10 | definitions matching the defined reports. 11 | 12 | - implement passes to improve optimization of report descriptors. 13 | Some usual optimization directions involve reordering values to 14 | group them by usage page, or reorder usages to be able to use 15 | ranges. As this changes semantics of descriptors, this should 16 | probably yield warnings rather than do modifications directly. 17 | 18 | - parse / generate foreign tools native data formats (`HID Descriptor 19 | Tool` is one of them). 20 | -------------------------------------------------------------------------------- /doc/usage.rst: -------------------------------------------------------------------------------- 1 | ======= 2 | Usage 3 | ======= 4 | 5 | For the examples here, we'll use a basic Mouse report descriptor. 6 | This example descriptor will contain two relative axes (X and Y) and 7 | Three buttons. 8 | 9 | .. contents:: 10 | 11 | Input / Output Formats 12 | ====================== 13 | 14 | There are 2 main formats: 15 | 16 | - HID Report descriptor (a stream of items) a.k.a. `low level`, this 17 | is the set of constructs defined in HID spec; 18 | - HID Descriptor (a tree of Collections and Values) a.k.a. `high 19 | level` this is a custom language for `hrdc`, see `descriptor language`_. 20 | 21 | Low level stream of item may be represented in three forms: 22 | 23 | - Binary (this is how a device puts it on the wire), 24 | - Hex (this is binary for humans, also outputted by Linux debugfs for 25 | HID devices), 26 | - Code (a C array of byte values with comments in a textual form). 27 | 28 | Library can parse and generate those three formats. 29 | 30 | High-level descriptor format actually is a Python script calling 31 | relevant library classes and functions, see `descriptor language`_. 32 | 33 | Entry points 34 | ============ 35 | 36 | Low-level HID Report Descriptor converter 37 | ----------------------------------------- 38 | 39 | Low-level HID report descriptor stream can be converted from one 40 | representation to another with `hrdc.converter` entry point. You may 41 | invoke it with `python3 -m`: 42 | 43 | .. code:: bash 44 | 45 | $ python3 -m hrdc.converter -h 46 | usage: converter.py [-h] [-i NAME] [-o NAME] [-O] [input] [output] 47 | 48 | Convert a HID report descriptor 49 | 50 | positional arguments: 51 | input Input file name 52 | output Output file name 53 | 54 | optional arguments: 55 | -h, --help show this help message and exit 56 | -i NAME, --input-format NAME 57 | Input parser name 58 | -o NAME, --output-format NAME 59 | Output formatter name 60 | -O, --optimize Optimize stream 61 | 62 | Optionnally, converter may optimize stream, see `optimization`_. 63 | 64 | All files default to stdin / stdout. 65 | 66 | For instance, let's say we have the following descriptor blob in its 67 | hexadecimal format in `mouse.hex`: 68 | 69 | .. code:: 70 | 71 | 05 01 09 02 a1 01 15 81 25 7f 75 08 95 02 09 30 09 31 81 06 15 00 25 72 | 01 75 01 95 03 05 09 19 01 29 03 81 02 c0 73 | 74 | It may be converted to C code with the following command line: 75 | 76 | .. code:: bash 77 | 78 | $ python3 -m hrdc.converter -i hex -o code mouse.hex 79 | 80 | which will output: 81 | 82 | .. code:: c 83 | 84 | 0x05, 0x01, // UsagePage (desktop) 85 | 0x09, 0x02, // Usage (Mouse) 86 | 0xa1, 0x01, // Collection (Application) 87 | 0x15, 0x81, // LogicalMinimum (-127) 88 | 0x25, 0x7f, // LogicalMaximum (127) 89 | 0x75, 0x08, // ReportSize (8) 90 | 0x95, 0x02, // ReportCount (2) 91 | 0x09, 0x30, // Usage (X) 92 | 0x09, 0x31, // Usage (Y) 93 | 0x81, 0x06, // Input (Variable|Relative) 94 | 0x15, 0x00, // LogicalMinimum (0) 95 | 0x25, 0x01, // LogicalMaximum (1) 96 | 0x75, 0x01, // ReportSize (1) 97 | 0x95, 0x03, // ReportCount (3) 98 | 0x05, 0x09, // UsagePage (button) 99 | 0x19, 0x01, // UsageMinimum (Button(1)) 100 | 0x29, 0x03, // UsageMaximum (Button(3)) 101 | 0x81, 0x02, // Input (Variable) 102 | 0xc0, // EndCollection 103 | 104 | Low-level converter extracts the source format to a stream of Items 105 | with attached values. It does not extract any semantic information. 106 | The only constraint is to have balanced `Collection` and `End 107 | Collection` items so that the pretty printer can indent correctly. 108 | 109 | Because items are parsed and serialized back, the actual output may 110 | not be the exact input. For instance the following input stream: 111 | 112 | .. code:: 113 | 114 | 0x26, 0x01, 0x00, // LogicalMaximum (1) 115 | 116 | is outputted as: 117 | 118 | .. code:: 119 | 120 | 0x25, 0x01, // LogicalMaximum (1) 121 | 122 | There is no semantic validation / extraction. 123 | 124 | Low-level to high-level descriptor decompiler 125 | --------------------------------------------- 126 | 127 | This tool is invoked through `python3 -m hrdc.descriptor.extractor`. 128 | The output file is a Python script containing a high-level 129 | representation of the report descriptor. 130 | 131 | .. code:: bash 132 | 133 | $ python3 -m hrdc.descriptor.extractor -h 134 | usage: extractor.py [-h] [-i NAME] [input] [output] 135 | 136 | Decompile a HID report descriptor 137 | 138 | positional arguments: 139 | input Input file name 140 | output Output file name 141 | 142 | optional arguments: 143 | -h, --help show this help message and exit 144 | -i NAME, --input-format NAME 145 | Input parser name 146 | 147 | All files default to stdin / stdout. 148 | 149 | The same mouse descriptor as above would be extracted with: 150 | 151 | .. code:: bash 152 | 153 | $ python3 -m hrdc.descriptor.extractor -i hex mouse.hex mouse.py 154 | 155 | In turn, this file can be used as a python script. `compile_main` 156 | directive at end of file will handle a command line, see `descriptor 157 | language`_. 158 | 159 | High-level Descriptor Compiler 160 | ------------------------------ 161 | 162 | High level descriptor compiler takes input from a Python script using 163 | the `hrdc` library components to model a tree of: 164 | 165 | - Collections, 166 | - Reports, 167 | - Values. 168 | 169 | Values are data items in HID terminology. Values can be: 170 | 171 | - Single absolute/relative items, 172 | - Named Arrays (surrounding logical collection is implicit), 173 | - Data arrays, 174 | - Padding. 175 | 176 | A HID mouse device descriptor source code could be: 177 | 178 | .. code:: python 179 | 180 | from hrdc.usage import * 181 | from hrdc.descriptor import * 182 | 183 | descriptor = Collection(Collection.Application, desktop.Mouse, 184 | Value(Value.Input, desktop.X, 8, flags = Value.Variable | Value.Relative, logicalMin = -127, logicalMax = 127), 185 | Value(Value.Input, desktop.Y, 8, flags = Value.Variable | Value.Relative, logicalMin = -127, logicalMax = 127), 186 | Value(Value.Input, button.Button(1), 1, logicalMin = 0, logicalMax = 1), 187 | Value(Value.Input, button.Button(2), 1, logicalMin = 0, logicalMax = 1), 188 | Value(Value.Input, button.Button(3), 1, logicalMin = 0, logicalMax = 1), 189 | ) 190 | 191 | if __name__ == "__main__": 192 | compile_main(descriptor) 193 | 194 | Execution of this script will handle a command-line and allow 195 | generation of an output HID report descriptor item stream. 196 | 197 | .. code:: bash 198 | 199 | $ python3 mouse.py -h 200 | usage: mouse.py [-h] [-o NAME] [-N] [output] 201 | 202 | Compile a HID report descriptor 203 | 204 | positional arguments: 205 | output Output file name 206 | 207 | optional arguments: 208 | -h, --help show this help message and exit 209 | -o NAME, --output-format NAME 210 | Output formatter name 211 | -N, --no-optimize Dont optimize output stream 212 | 213 | Please note default behavior is to optimize output stream, contrary to 214 | the HID Report Descriptor converter above, because non-optimized 215 | stream generated by descriptor serializer is mostly useless. 216 | 217 | Here is an example of non-optimized report: 218 | 219 | .. code:: bash 220 | 221 | $ python3 mouse.py -N -o code 222 | 0x0b, 0x02, 0x00, 0x01, 0x00, // Usage (desktop.Mouse) 223 | 0xa1, 0x01, // Collection (Application) 224 | 0x0b, 0x30, 0x00, 0x01, 0x00, // Usage (desktop.X) 225 | 0x35, 0x00, // PhysicalMinimum (0) 226 | 0x45, 0x00, // PhysicalMaximum (0) 227 | 0x15, 0x81, // LogicalMinimum (-127) 228 | 0x25, 0x7f, // LogicalMaximum (127) 229 | 0x65, 0x00, // Unit (0) 230 | 0x55, 0x00, // UnitExponent (0) 231 | 0x95, 0x01, // ReportCount (1) 232 | 0x75, 0x08, // ReportSize (8) 233 | 0x81, 0x06, // Input (Variable|Relative) 234 | 0x0b, 0x31, 0x00, 0x01, 0x00, // Usage (desktop.Y) 235 | 0x35, 0x00, // PhysicalMinimum (0) 236 | 0x45, 0x00, // PhysicalMaximum (0) 237 | 0x15, 0x81, // LogicalMinimum (-127) 238 | 0x25, 0x7f, // LogicalMaximum (127) 239 | 0x65, 0x00, // Unit (0) 240 | 0x55, 0x00, // UnitExponent (0) 241 | 0x95, 0x01, // ReportCount (1) 242 | 0x75, 0x08, // ReportSize (8) 243 | 0x81, 0x06, // Input (Variable|Relative) 244 | 0x0b, 0x01, 0x00, 0x09, 0x00, // Usage (button.Button(1)) 245 | 0x35, 0x00, // PhysicalMinimum (0) 246 | 0x45, 0x00, // PhysicalMaximum (0) 247 | 0x15, 0x00, // LogicalMinimum (0) 248 | 0x25, 0x01, // LogicalMaximum (1) 249 | 0x65, 0x00, // Unit (0) 250 | 0x55, 0x00, // UnitExponent (0) 251 | 0x95, 0x01, // ReportCount (1) 252 | 0x75, 0x01, // ReportSize (1) 253 | 0x81, 0x02, // Input (Variable) 254 | 0x0b, 0x02, 0x00, 0x09, 0x00, // Usage (button.Button(2)) 255 | 0x35, 0x00, // PhysicalMinimum (0) 256 | 0x45, 0x00, // PhysicalMaximum (0) 257 | 0x15, 0x00, // LogicalMinimum (0) 258 | 0x25, 0x01, // LogicalMaximum (1) 259 | 0x65, 0x00, // Unit (0) 260 | 0x55, 0x00, // UnitExponent (0) 261 | 0x95, 0x01, // ReportCount (1) 262 | 0x75, 0x01, // ReportSize (1) 263 | 0x81, 0x02, // Input (Variable) 264 | 0x0b, 0x03, 0x00, 0x09, 0x00, // Usage (button.Button(3)) 265 | 0x35, 0x00, // PhysicalMinimum (0) 266 | 0x45, 0x00, // PhysicalMaximum (0) 267 | 0x15, 0x00, // LogicalMinimum (0) 268 | 0x25, 0x01, // LogicalMaximum (1) 269 | 0x65, 0x00, // Unit (0) 270 | 0x55, 0x00, // UnitExponent (0) 271 | 0x95, 0x01, // ReportCount (1) 272 | 0x75, 0x01, // ReportSize (1) 273 | 0x81, 0x02, // Input (Variable) 274 | 0xc0, // EndCollection 275 | 276 | Next steps: 277 | 278 | - `descriptor language`_, 279 | - `optimization`_. 280 | 281 | .. _`optimization`: optimizations.rst 282 | .. _`descriptor language`: descriptor.rst 283 | -------------------------------------------------------------------------------- /doc/why.rst: -------------------------------------------------------------------------------- 1 | ============================== 2 | Why use a compiler for HID ? 3 | ============================== 4 | 5 | HID is simple enough for a trained engineer to assume it can be 6 | authored by hand. This is wrong. Unfortunately, HID is hard enough 7 | for most bugs to be forgotten in a final product, and when a product 8 | ships bug-free, most of the time, there is room for improvement. 9 | 10 | .. contents:: 11 | 12 | HID is Error prone 13 | ================== 14 | 15 | HID is error prone. Because of mix of local and global items, some 16 | parameters have to be reset between data items, others may not. 17 | Moreover, some items (i.e. Physical range) have an implicit value that 18 | depend on others if set to 0. 19 | 20 | Some examples may be of some interest. 21 | 22 | A sample gamepad 23 | ---------------- 24 | 25 | This report descriptor is a excerpt from an existing product. It has 26 | been extracted directly from device. This is the first main 27 | collection (there are others collections, irrelevant for the example). 28 | 29 | Hex form: 30 | 31 | .. code:: 32 | 33 | 05 01 09 05 A1 01 85 01 05 09 19 01 29 0F 15 00 25 01 95 0F 75 01 81 34 | 02 75 01 95 01 81 03 05 01 09 39 15 01 25 08 35 00 46 3B 01 66 14 00 35 | 75 04 95 01 81 42 75 04 95 01 81 03 05 01 09 01 A1 00 09 30 09 31 15 36 | 00 26 FF FF 35 00 46 FF FF 95 02 75 10 81 02 C0 09 32 09 35 15 00 26 37 | FF FF 35 00 46 FF FF 95 02 75 10 81 02 05 02 09 C5 09 C4 15 00 26 FF 38 | FF 35 00 46 FF FF 95 02 75 10 81 02 C0 39 | 40 | Corresponding code with textual form, from a `sibling tool`_: 41 | 42 | .. code:: bash 43 | :number-lines: 44 | 45 | $ hidrd-convert -i hex -o code broken.hex 46 | 0x05, 0x01, /* Usage Page (Desktop), */ 47 | 0x09, 0x05, /* Usage (Gamepad), */ 48 | 0xA1, 0x01, /* Collection (Application), */ 49 | 0x85, 0x01, /* Report ID (1), */ 50 | 0x05, 0x09, /* Usage Page (Button), */ 51 | 0x19, 0x01, /* Usage Minimum (01h), */ 52 | 0x29, 0x0F, /* Usage Maximum (0Fh), */ 53 | 0x15, 0x00, /* Logical Minimum (0), */ 54 | 0x25, 0x01, /* Logical Maximum (1), */ 55 | 0x95, 0x0F, /* Report Count (15), */ 56 | 0x75, 0x01, /* Report Size (1), */ 57 | 0x81, 0x02, /* Input (Variable), */ 58 | 0x75, 0x01, /* Report Size (1), */ 59 | 0x95, 0x01, /* Report Count (1), */ 60 | 0x81, 0x03, /* Input (Constant, Variable), */ 61 | 0x05, 0x01, /* Usage Page (Desktop), */ 62 | 0x09, 0x39, /* Usage (Hat Switch), */ 63 | 0x15, 0x01, /* Logical Minimum (1), */ 64 | 0x25, 0x08, /* Logical Maximum (8), */ 65 | 0x35, 0x00, /* Physical Minimum (0), */ 66 | 0x46, 0x3B, 0x01, /* Physical Maximum (315), */ 67 | 0x66, 0x14, 0x00, /* Unit (Degrees), */ 68 | 0x75, 0x04, /* Report Size (4), */ 69 | 0x95, 0x01, /* Report Count (1), */ 70 | 0x81, 0x42, /* Input (Variable, Null State), */ 71 | 0x75, 0x04, /* Report Size (4), */ 72 | 0x95, 0x01, /* Report Count (1), */ 73 | 0x81, 0x03, /* Input (Constant, Variable), */ 74 | 0x05, 0x01, /* Usage Page (Desktop), */ 75 | 0x09, 0x01, /* Usage (Pointer), */ 76 | 0xA1, 0x00, /* Collection (Physical), */ 77 | 0x09, 0x30, /* Usage (X), */ 78 | 0x09, 0x31, /* Usage (Y), */ 79 | 0x15, 0x00, /* Logical Minimum (0), */ 80 | 0x26, 0xFF, 0xFF, /* Logical Maximum (-1), */ 81 | 0x35, 0x00, /* Physical Minimum (0), */ 82 | 0x46, 0xFF, 0xFF, /* Physical Maximum (-1), */ 83 | 0x95, 0x02, /* Report Count (2), */ 84 | 0x75, 0x10, /* Report Size (16), */ 85 | 0x81, 0x02, /* Input (Variable), */ 86 | 0xC0, /* End Collection, */ 87 | 0x09, 0x32, /* Usage (Z), */ 88 | 0x09, 0x35, /* Usage (Rz), */ 89 | 0x15, 0x00, /* Logical Minimum (0), */ 90 | 0x26, 0xFF, 0xFF, /* Logical Maximum (-1), */ 91 | 0x35, 0x00, /* Physical Minimum (0), */ 92 | 0x46, 0xFF, 0xFF, /* Physical Maximum (-1), */ 93 | 0x95, 0x02, /* Report Count (2), */ 94 | 0x75, 0x10, /* Report Size (16), */ 95 | 0x81, 0x02, /* Input (Variable), */ 96 | 0x05, 0x02, /* Usage Page (Simulation), */ 97 | 0x09, 0xC5, /* Usage (Brake), */ 98 | 0x09, 0xC4, /* Usage (Accelerator), */ 99 | 0x15, 0x00, /* Logical Minimum (0), */ 100 | 0x26, 0xFF, 0xFF, /* Logical Maximum (-1), */ 101 | 0x35, 0x00, /* Physical Minimum (0), */ 102 | 0x46, 0xFF, 0xFF, /* Physical Maximum (-1), */ 103 | 0x95, 0x02, /* Report Count (2), */ 104 | 0x75, 0x10, /* Report Size (16), */ 105 | 0x81, 0x02, /* Input (Variable), */ 106 | 0xC0 /* End Collection */ 107 | 108 | This defines a gamepad, 15 buttons, a hat switch, two thumb sticks and 109 | two analog triggers. 110 | 111 | There are some broken constructs (despite in some third party 112 | recommandation documents) a compiler could do nothing about: 113 | 114 | - line 43, the second thumb stick using Z and Rz as axis Usage codes 115 | instead of X and Y in another Physical Collection (see `HUT1_12v2`_, 116 | A.5, p. 132); 117 | 118 | - line 53, analog triggers use specific usages even if nothing 119 | enforces using those two triggers for "Accelerator" and "Brake". 120 | Actually, specification explicitly says "Button" usages should be 121 | preferred over specific usages (see `HID1_11`_, 6.2.2.8, in 122 | footnote, p. 40). 123 | 124 | There are other constructs where a compiler could have been useful: 125 | 126 | - line 35 onwards, range for analog controls is broken, it goes from 0 127 | to -1. Logical minimum and Logical maximum are signed, value is 16 128 | bits, maximum should have been defined with a 4-byte item (most HID 129 | parsers are tolerant about this one); 130 | 131 | - finally, this descriptor is suboptimal. It repeats physical bounds 132 | that are the same as logical ones. Repeating them is not needed as 133 | physical range is meant to be the same as logical one when both 134 | physical minimum and physical maximum are 0 (see `HID1_11`_, 135 | 6.2.2.7, p. 38). 136 | 137 | But they are not the worst thing. There is a blatant error. With 138 | extractor, it may become clearer: 139 | 140 | .. code:: bash 141 | 142 | $ python3 -m hrdc.descriptor.extractor -i hex broken.hex 143 | 144 | .. code:: python 145 | :number-lines: 146 | 147 | from hrdc.usage import * 148 | from hrdc.descriptor import * 149 | 150 | descriptor = TopLevel( 151 | Report(1, 152 | Collection(Collection.Application, desktop.Gamepad, 153 | Value(Value.Input, button.Button(1), 1, logicalMin = 0, logicalMax = 1), 154 | Value(Value.Input, button.Button(2), 1, logicalMin = 0, logicalMax = 1), 155 | Value(Value.Input, button.Button(3), 1, logicalMin = 0, logicalMax = 1), 156 | Value(Value.Input, button.Button(4), 1, logicalMin = 0, logicalMax = 1), 157 | Value(Value.Input, button.Button(5), 1, logicalMin = 0, logicalMax = 1), 158 | Value(Value.Input, button.Button(6), 1, logicalMin = 0, logicalMax = 1), 159 | Value(Value.Input, button.Button(7), 1, logicalMin = 0, logicalMax = 1), 160 | Value(Value.Input, button.Button(8), 1, logicalMin = 0, logicalMax = 1), 161 | Value(Value.Input, button.Button(9), 1, logicalMin = 0, logicalMax = 1), 162 | Value(Value.Input, button.Button(10), 1, logicalMin = 0, logicalMax = 1), 163 | Value(Value.Input, button.Button(11), 1, logicalMin = 0, logicalMax = 1), 164 | Value(Value.Input, button.Button(12), 1, logicalMin = 0, logicalMax = 1), 165 | Value(Value.Input, button.Button(13), 1, logicalMin = 0, logicalMax = 1), 166 | Value(Value.Input, button.Button(14), 1, logicalMin = 0, logicalMax = 1), 167 | Value(Value.Input, button.Button(15), 1, logicalMin = 0, logicalMax = 1), 168 | Padding(Value.Input, 1), 169 | Value(Value.Input, desktop.HatSwitch, 4, flags = Value.Variable|Value.NullState, logicalMax = 8, physicalMin = 0, physicalMax = 315, unit = Unit.Degree), 170 | Padding(Value.Input, 4), 171 | Collection(Collection.Physical, desktop.Pointer, 172 | Value(Value.Input, desktop.X, 16, logicalMin = 0, logicalMax = -1, unit = Unit.Degree), 173 | Value(Value.Input, desktop.Y, 16, logicalMin = 0, logicalMax = -1, unit = Unit.Degree), 174 | ), 175 | Value(Value.Input, desktop.Z, 16, logicalMin = 0, logicalMax = -1, unit = Unit.Degree), 176 | Value(Value.Input, desktop.Rz, 16, logicalMin = 0, logicalMax = -1, unit = Unit.Degree), 177 | Value(Value.Input, simulation.Brake, 16, logicalMin = 0, logicalMax = -1, unit = Unit.Degree), 178 | Value(Value.Input, simulation.Accelerator, 16, logicalMin = 0, logicalMax = -1, unit = Unit.Degree), 179 | ), 180 | ), 181 | ) 182 | 183 | if __name__ == "__main__": 184 | compile_main(descriptor) 185 | 186 | After the Hat switch definition, line 23, all subsequent values have 187 | Degree as unit. This is most probably not wanted. 188 | 189 | Why did this happen ? Because Unit is a global item, but this may 190 | easily be forgotten about. A `Unit()` item should have reset the unit 191 | somewhere after line 26 of descriptor above. 192 | 193 | Another gamepad 194 | --------------- 195 | 196 | Again, here is a binary descriptor from an actual device: 197 | 198 | .. code:: 199 | 200 | 05 01 09 05 a1 01 05 01 09 01 a1 00 05 09 19 01 29 0c 15 00 25 01 75 201 | 01 95 0c 81 02 75 08 95 01 81 01 05 01 09 39 25 07 35 00 46 0e 01 66 202 | 40 00 75 04 81 42 09 30 09 31 15 80 25 7f 46 ff 00 66 00 00 75 08 95 203 | 02 81 02 09 35 95 01 81 02 09 36 16 00 00 26 ff 00 81 02 09 bb 15 00 204 | 26 ff 00 35 00 46 ff 00 75 08 95 04 91 02 c0 c0 205 | 206 | Spec-annotated code: 207 | 208 | .. code:: bash 209 | :number-lines: 210 | 211 | $ hidrd-convert -i hex -o code broken2.hex 212 | 0x05, 0x01, /* Usage Page (Desktop), */ 213 | 0x09, 0x05, /* Usage (Gamepad), */ 214 | 0xA1, 0x01, /* Collection (Application), */ 215 | 0x05, 0x01, /* Usage Page (Desktop), */ 216 | 0x09, 0x01, /* Usage (Pointer), */ 217 | 0xA1, 0x00, /* Collection (Physical), */ 218 | 0x05, 0x09, /* Usage Page (Button), */ 219 | 0x19, 0x01, /* Usage Minimum (01h), */ 220 | 0x29, 0x0C, /* Usage Maximum (0Ch), */ 221 | 0x15, 0x00, /* Logical Minimum (0), */ 222 | 0x25, 0x01, /* Logical Maximum (1), */ 223 | 0x75, 0x01, /* Report Size (1), */ 224 | 0x95, 0x0C, /* Report Count (12), */ 225 | 0x81, 0x02, /* Input (Variable), */ 226 | 0x75, 0x08, /* Report Size (8), */ 227 | 0x95, 0x01, /* Report Count (1), */ 228 | 0x81, 0x01, /* Input (Constant), */ 229 | 0x05, 0x01, /* Usage Page (Desktop), */ 230 | 0x09, 0x39, /* Usage (Hat Switch), */ 231 | 0x25, 0x07, /* Logical Maximum (7), */ 232 | 0x35, 0x00, /* Physical Minimum (0), */ 233 | 0x46, 0x0E, 0x01, /* Physical Maximum (270), */ 234 | 0x66, 0x40, 0x00, /* Unit (40h), */ 235 | 0x75, 0x04, /* Report Size (4), */ 236 | 0x81, 0x42, /* Input (Variable, Null State), */ 237 | 0x09, 0x30, /* Usage (X), */ 238 | 0x09, 0x31, /* Usage (Y), */ 239 | 0x15, 0x80, /* Logical Minimum (-128), */ 240 | 0x25, 0x7F, /* Logical Maximum (127), */ 241 | 0x46, 0xFF, 0x00, /* Physical Maximum (255), */ 242 | 0x66, 0x00, 0x00, /* Unit, */ 243 | 0x75, 0x08, /* Report Size (8), */ 244 | 0x95, 0x02, /* Report Count (2), */ 245 | 0x81, 0x02, /* Input (Variable), */ 246 | 0x09, 0x35, /* Usage (Rz), */ 247 | 0x95, 0x01, /* Report Count (1), */ 248 | 0x81, 0x02, /* Input (Variable), */ 249 | 0x09, 0x36, /* Usage (Slider), */ 250 | 0x16, 0x00, 0x00, /* Logical Minimum (0), */ 251 | 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ 252 | 0x81, 0x02, /* Input (Variable), */ 253 | 0x09, 0xBB, /* Usage (BBh), */ 254 | 0x15, 0x00, /* Logical Minimum (0), */ 255 | 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ 256 | 0x35, 0x00, /* Physical Minimum (0), */ 257 | 0x46, 0xFF, 0x00, /* Physical Maximum (255), */ 258 | 0x75, 0x08, /* Report Size (8), */ 259 | 0x95, 0x04, /* Report Count (4), */ 260 | 0x91, 0x02, /* Output (Variable), */ 261 | 0xC0, /* End Collection, */ 262 | 0xC0 /* End Collection */ 263 | 264 | With extractor: 265 | 266 | .. code:: bash 267 | 268 | $ python3 -m hrdc.descriptor.extractor -i hex broken2.hex 269 | 270 | .. code:: python 271 | :number-lines: 272 | 273 | from hrdc.usage import * 274 | from hrdc.descriptor import * 275 | 276 | descriptor = TopLevel( 277 | Report(0, 278 | Collection(Collection.Application, desktop.Gamepad, 279 | Collection(Collection.Physical, desktop.Pointer, 280 | Value(Value.Input, button.Button(1), 1, logicalMin = 0, logicalMax = 1), 281 | Value(Value.Input, button.Button(2), 1, logicalMin = 0, logicalMax = 1), 282 | Value(Value.Input, button.Button(3), 1, logicalMin = 0, logicalMax = 1), 283 | Value(Value.Input, button.Button(4), 1, logicalMin = 0, logicalMax = 1), 284 | Value(Value.Input, button.Button(5), 1, logicalMin = 0, logicalMax = 1), 285 | Value(Value.Input, button.Button(6), 1, logicalMin = 0, logicalMax = 1), 286 | Value(Value.Input, button.Button(7), 1, logicalMin = 0, logicalMax = 1), 287 | Value(Value.Input, button.Button(8), 1, logicalMin = 0, logicalMax = 1), 288 | Value(Value.Input, button.Button(9), 1, logicalMin = 0, logicalMax = 1), 289 | Value(Value.Input, button.Button(10), 1, logicalMin = 0, logicalMax = 1), 290 | Value(Value.Input, button.Button(11), 1, logicalMin = 0, logicalMax = 1), 291 | Value(Value.Input, button.Button(12), 1, logicalMin = 0, logicalMax = 1), 292 | Padding(Value.Input, 8), 293 | Value(Value.Input, desktop.HatSwitch, 4, flags = Value.Variable|Value.NullState, logicalMin = 0, logicalMax = 7, physicalMin = 0, physicalMax = 270, unit = 64), 294 | Value(Value.Input, desktop.X, 8, logicalMin = -128, logicalMax = 127, physicalMin = 0, physicalMax = 255), 295 | Value(Value.Input, desktop.Y, 8, logicalMin = -128, logicalMax = 127, physicalMin = 0, physicalMax = 255), 296 | Value(Value.Input, desktop.Rz, 8, logicalMin = -128, logicalMax = 127, physicalMin = 0, physicalMax = 255), 297 | Value(Value.Input, desktop.Slider, 8, logicalMin = 0, logicalMax = 255), 298 | Value(Value.Output, 0x100bb, 8, logicalMin = 0, logicalMax = 255), 299 | Value(Value.Output, 0x100bb, 8, logicalMin = 0, logicalMax = 255), 300 | Value(Value.Output, 0x100bb, 8, logicalMin = 0, logicalMax = 255), 301 | Value(Value.Output, 0x100bb, 8, logicalMin = 0, logicalMax = 255), 302 | ), 303 | ), 304 | ), 305 | ) 306 | 307 | if __name__ == "__main__": 308 | compile_main(descriptor) 309 | 310 | Broken constructs: 311 | 312 | - Hat switch unit is 0x40, which decodes to `No system, None^4 313 | (Length)`. This is totally invalid (the person who did this 314 | probably mixed rows and columns in table from page 37 in 315 | `HID1_11`_); 316 | 317 | - Hat switch goes from 0 to 7 (logical) and from 0° to 270° 318 | (physical), that means decoded values are 0°, 38.571°, 77.143°, 319 | 115.714°, 154.286°, 192.857°, 231.429° and 270°. On a compliant 320 | host, user may not be able to point North-West. How could this go 321 | to the field uncatched ? 322 | 323 | HID is hard to optimize 324 | ======================= 325 | 326 | HID is hard to optimize by hand. Once optimized, a report descriptor 327 | is detious to edit by hand because author has to think about 328 | interactions between local and global items. 329 | 330 | HRDC contains an optimizer. It can be used to rewrite existing 331 | descriptors with identical meaning (including bugs above), but with 332 | the canonical representation. 333 | 334 | Let's try this on various report descriptors found in the wild. 335 | I took various HID report descriptors from various devices, ran the 336 | optimizer on them, compared report descriptor sizes. There is always 337 | some difference. 338 | 339 | =========== ========== ============ 340 | Size before Size after Gain 341 | =========== ========== ============ 342 | 32 30 \- 6 % 343 | 61 60 \- 1 % 344 | 73 61 \- 16 % 345 | 97 45 \- 53 % 346 | 98 73 \- 25 % 347 | 98 73 \- 25 % 348 | 101 85 \- 15 % 349 | 103 87 \- 15 % 350 | 106 102 \- 3 % 351 | 108 91 \- 15 % 352 | 108 91 \- 15 % 353 | 116 113 \- 2 % 354 | 117 82 \- 29 % 355 | 119 92 \- 22 % 356 | 119 100 \- 15 % 357 | 142 112 \- 21 % 358 | 146 116 \- 20 % 359 | 148 118 \- 20 % 360 | 148 118 \- 20 % 361 | 148 118 \- 20 % 362 | 148 142 \- 4 % 363 | 149 135 \- 9 % 364 | 152 98 \- 35 % 365 | 156 104 \- 33 % 366 | 166 164 \- 1 % 367 | 174 125 \- 28 % 368 | 178 155 \- 12 % 369 | 184 159 \- 13 % 370 | 196 168 \- 14 % 371 | 214 204 \- 4 % 372 | 214 211 \- 1 % 373 | 217 199 \- 8 % 374 | 246 234 \- 4 % 375 | 259 232 \- 10 % 376 | 266 237 \- 10 % 377 | 275 234 \- 14 % 378 | 326 293 \- 10 % 379 | 326 293 \- 10 % 380 | 409 350 \- 14 % 381 | =========== ========== ============ 382 | 383 | Average gain: 15% 384 | 385 | Conclusion 386 | ========== 387 | 388 | There is no good reason we had to write HID report descriptors by hand 389 | for so long! 390 | 391 | .. _`HID1_11`: http://www.usb.org/developers/hidpage/HID1_11.pdf 392 | .. _`HUT1_12v2`: http://www.usb.org/developers/hidpage/Hut1_12v2.pdf 393 | .. _`sibling tool`: https://github.com/DIGImend/hidrd/ 394 | -------------------------------------------------------------------------------- /examples/battery.py: -------------------------------------------------------------------------------- 1 | from hrdc.usage import * 2 | from hrdc.descriptor import * 3 | 4 | battery = Collection(Collection.Application, consumer.ConsumerControl, 5 | Collection(Collection.Logical, desktop.Keyboard, 6 | Value(Value.Input, device.BatteryStrength, 8, 7 | logicalMin = 0, logicalMax = 100) 8 | ) 9 | ) 10 | 11 | 12 | if __name__ == "__main__": 13 | compile_main(battery) 14 | 15 | -------------------------------------------------------------------------------- /examples/keyboard.py: -------------------------------------------------------------------------------- 1 | from hrdc.usage import * 2 | from hrdc.descriptor import * 3 | 4 | bit_in = lambda x: Value(Value.Input, x, 1, 5 | logicalMin = 0, logicalMax = 1) 6 | 7 | bit_out = lambda x: Value(Value.Output, x, 1, 8 | logicalMin = 0, logicalMax = 1) 9 | 10 | key_range = lambda n: Value(Value.Input, usage = None, size = 8, 11 | namedArray = UsageRange(keyboard.NoEvent, keyboard.KeypadHexadecimal), 12 | logicalMin = 0, count = n) 13 | 14 | keyboard = Collection(Collection.Application, desktop.Keyboard, 15 | bit_in(keyboard.LeftControl), 16 | bit_in(keyboard.LeftShift), 17 | bit_in(keyboard.LeftAlt), 18 | bit_in(keyboard.LeftGui), 19 | bit_in(keyboard.RightControl), 20 | bit_in(keyboard.RightShift), 21 | bit_in(keyboard.RightAlt), 22 | bit_in(keyboard.RightGui), 23 | 24 | key_range(6), 25 | 26 | bit_out(led.NumLock), 27 | bit_out(led.CapsLock), 28 | bit_out(led.ScrollLock), 29 | ) 30 | 31 | 32 | if __name__ == "__main__": 33 | compile_main(keyboard) 34 | 35 | -------------------------------------------------------------------------------- /examples/mouse.py: -------------------------------------------------------------------------------- 1 | from hrdc.usage import * 2 | from hrdc.descriptor import * 3 | 4 | mouse = Collection(Collection.Application, desktop.Mouse, 5 | Value(Value.Input, desktop.X, 8, 6 | flags = Value.Variable | Value.Relative, 7 | logicalMin = -127, logicalMax = 127), 8 | Value(Value.Input, desktop.Y, 8, 9 | flags = Value.Variable | Value.Relative, 10 | logicalMin = -127, logicalMax = 127), 11 | Value(Value.Input, button.Button(1), 1, 12 | logicalMin = 0, logicalMax = 1), 13 | Value(Value.Input, button.Button(2), 1, 14 | logicalMin = 0, logicalMax = 1), 15 | Value(Value.Input, button.Button(3), 1, 16 | logicalMin = 0, logicalMax = 1), 17 | ) 18 | 19 | 20 | if __name__ == "__main__": 21 | compile_main(mouse) 22 | 23 | -------------------------------------------------------------------------------- /hrdc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipo/hrdc/749abec1accf6a97701ae47e249df4dc63d4a69d/hrdc/__init__.py -------------------------------------------------------------------------------- /hrdc/converter.py: -------------------------------------------------------------------------------- 1 | from .stream import * 2 | import sys 3 | import argparse 4 | 5 | def _main(): 6 | cmdline = argparse.ArgumentParser(description='Convert a HID report descriptor') 7 | 8 | cmdline.add_argument("-i", "--input-format", metavar = "NAME", type = str, 9 | default = "binary", help = "Input parser name") 10 | 11 | cmdline.add_argument("-o", "--output-format", metavar = "NAME", type = str, 12 | default = "binary", help = "Output formatter name") 13 | 14 | cmdline.add_argument("input", type = argparse.FileType('r'), 15 | default = "-", 16 | nargs = "?", help = "Input file name") 17 | 18 | cmdline.add_argument("output", type = argparse.FileType('w'), 19 | default = "-", 20 | nargs = "?", help = "Output file name") 21 | 22 | cmdline.add_argument("-O", "--optimize", action = "store_true", 23 | help = "Optimize stream") 24 | 25 | args = cmdline.parse_args() 26 | 27 | _parser = parser.Parser.get(args.input_format) 28 | _formatter = formatter.Formatter.get(args.output_format) 29 | 30 | stream = _formatter(args.output) 31 | 32 | if args.optimize: 33 | stream = optimizer.Optimizer.new(stream) 34 | 35 | p = _parser(args.input, stream) 36 | p.read() 37 | 38 | if __name__ == '__main__': 39 | _main() 40 | -------------------------------------------------------------------------------- /hrdc/descriptor/__init__.py: -------------------------------------------------------------------------------- 1 | from .descriptor import * 2 | from .streamer import * 3 | from .compiler import * 4 | 5 | -------------------------------------------------------------------------------- /hrdc/descriptor/compiler.py: -------------------------------------------------------------------------------- 1 | from . import streamer 2 | from ..stream.formatter import base as formatter 3 | from ..stream import optimizer 4 | import sys 5 | import argparse 6 | 7 | __all__ = ["compile_main"] 8 | 9 | def compile_main(desc): 10 | 11 | import argparse 12 | 13 | parser = argparse.ArgumentParser(description='Compile a HID report descriptor') 14 | parser.add_argument("-o", "--output-format", metavar = "NAME", type = str, 15 | default = "binary", help = "Output formatter name") 16 | 17 | parser.add_argument("output", type = argparse.FileType('w'), 18 | default = "-", 19 | nargs = "?", help = "Output file name") 20 | 21 | parser.add_argument("-N", "--no-optimize", action = "store_true", 22 | help = "Dont optimize output stream") 23 | 24 | args = parser.parse_args() 25 | 26 | _formatter = formatter.Formatter.get(args.output_format) 27 | output = _formatter(args.output) 28 | 29 | if not args.no_optimize: 30 | output = optimizer.Optimizer.new(output) 31 | 32 | visitor = streamer.Streamer(output) 33 | desc.accept(visitor) 34 | output.close() 35 | -------------------------------------------------------------------------------- /hrdc/descriptor/descriptor.py: -------------------------------------------------------------------------------- 1 | from ..stream import item 2 | 3 | __all__ = ["Report", "Collection", "Value", "Align", "TopLevel", "Padding", "Unit"] 4 | 5 | Unit = item.Unit 6 | 7 | class Base: 8 | def accept(self, visitor): 9 | raise RuntimeError("This method should have been overridden") 10 | 11 | class Hierarchical(Base): 12 | def __init__(self, *members): 13 | self.members = list(members) 14 | 15 | def accept(self, visitor): 16 | visitor.enter(self) 17 | for m in self.members: 18 | m.accept(visitor) 19 | visitor.leave(self) 20 | 21 | def __getitem__(self, i): 22 | return self.members[i] 23 | 24 | def __iter__(self): 25 | return iter(self.members) 26 | 27 | def append(self, member): 28 | self.members.append(member) 29 | 30 | def pop(self): 31 | self.members.pop() 32 | 33 | def __len__(self): 34 | return len(self.members) 35 | 36 | class Report(Hierarchical): 37 | def __init__(self, id, *members): 38 | self.id = id 39 | Hierarchical.__init__(self, *members) 40 | 41 | class TopLevel(Hierarchical): pass 42 | 43 | class Collection(Hierarchical): 44 | Physical = item.Collection.Physical 45 | Application = item.Collection.Application 46 | Logical = item.Collection.Logical 47 | Report = item.Collection.Report 48 | NamedArray = item.Collection.NamedArray 49 | UsageSwitch = item.Collection.UsageSwitch 50 | UsageModifier = item.Collection.UsageModifier 51 | 52 | def __init__(self, kind, usage, *members): 53 | self.kind = kind 54 | self.usage = usage 55 | Hierarchical.__init__(self, *members) 56 | 57 | class Value(Base): 58 | Input = item.DataItem.Input 59 | Output = item.DataItem.Output 60 | Feature = item.DataItem.Feature 61 | 62 | Data = item.DataItem.Data 63 | Constant = item.DataItem.Constant 64 | Array = item.DataItem.Array 65 | Variable = item.DataItem.Variable 66 | Absolute = item.DataItem.Absolute 67 | Relative = item.DataItem.Relative 68 | NoWrap = item.DataItem.NoWrap 69 | Wrap = item.DataItem.Wrap 70 | Linear = item.DataItem.Linear 71 | NonLinear = item.DataItem.NonLinear 72 | PreferredState = item.DataItem.PreferredState 73 | NoPreferred = item.DataItem.NoPreferred 74 | NoNullPosition = item.DataItem.NoNullPosition 75 | NullState = item.DataItem.NullState 76 | NonVolatile = item.DataItem.NonVolatile 77 | Volatile = item.DataItem.Volatile 78 | BitField = item.DataItem.BitField 79 | BufferedBytes = item.DataItem.BufferedBytes 80 | 81 | def __init__(self, way, usage, size, 82 | flags = Data | Variable | Absolute, 83 | logicalMin = 1, logicalMax = None, 84 | physicalMin = None, physicalMax = None, 85 | namedArray = None, 86 | unit = 0, unitExponent = 0, 87 | designator = 0, 88 | string = 0, 89 | count = 1, 90 | alignment = None): 91 | self.way = way 92 | self.flags = flags 93 | self.usage = usage 94 | self.size = size 95 | if alignment is None: 96 | self.alignment = min((8, self.size)) 97 | else: 98 | self.alignment = alignment 99 | if (logicalMax == None or logicalMin == None) \ 100 | and not namedArray \ 101 | and not (flags & self.Constant): 102 | raise ValueError("Must specify either logical bounds or a named array") 103 | self.logicalMin = logicalMin 104 | self.logicalMax = logicalMax 105 | if not physicalMin and not physicalMax: 106 | self.physicalMin = logicalMin 107 | self.physicalMax = logicalMax 108 | else: 109 | self.physicalMin = physicalMin or 0 110 | self.physicalMax = physicalMax or 0 111 | self.namedArray = namedArray 112 | self.unit = unit 113 | self.unitExponent = unitExponent 114 | self.designator = designator 115 | self.string = string 116 | self.count = count 117 | 118 | if namedArray: 119 | if usage: 120 | self.flags = self.Variable 121 | else: 122 | self.flags = self.Data | self.Array 123 | self.logicalMax = len(self.namedArray) + self.logicalMin - 1 124 | 125 | def accept(self, visitor): 126 | visitor.element(self) 127 | 128 | class Align(Value): 129 | def __init__(self, way, boundary, flags = Value.Constant | Value.Variable): 130 | Value.__init__(self, way, 0, 0, flags) 131 | self.boundary = boundary 132 | 133 | class Padding(Value): 134 | def __init__(self, way, size): 135 | Value.__init__(self, way, 0, size, Value.Constant | Value.Variable) 136 | 137 | class Visitor: 138 | def enter(self, element): 139 | pass 140 | 141 | def leave(self, element): 142 | pass 143 | 144 | def element(self, element): 145 | pass 146 | -------------------------------------------------------------------------------- /hrdc/descriptor/dumper.py: -------------------------------------------------------------------------------- 1 | from . import descriptor 2 | from ..stream import item 3 | from ..usage import Usage, UsageRange 4 | from ..util import NamedConstant 5 | import sys 6 | import re 7 | 8 | __all__ = ["Dumper"] 9 | 10 | class Dumper(descriptor.Visitor): 11 | def __init__(self, output = sys.stdout): 12 | self.report_id = 0 13 | self.__indent = 0 14 | self.__output = output 15 | 16 | def write(self, *data): 17 | self.__output.write(" ".join(map(str, data))) 18 | 19 | def indent(self, offset = 0): 20 | return " " * ((self.__indent + offset) * 4) 21 | 22 | def enter(self, e): 23 | if self.__indent == 0: 24 | self.write("""from hrdc.usage import * 25 | from hrdc.descriptor import * 26 | 27 | descriptor = """) 28 | 29 | if isinstance(e, descriptor.TopLevel): 30 | self.write(self.indent() + "TopLevel(\n") 31 | 32 | self.__indent += 1 33 | elif isinstance(e, descriptor.Report): 34 | self.write(self.indent() + "Report(%d,\n" % (e.id)) 35 | 36 | self.__indent += 1 37 | elif isinstance(e, descriptor.Collection): 38 | self.write(self.indent() + "Collection(Collection.%s, %s,\n" % (e.kind, Usage.lookup(e.usage) if e.usage is not None else None)) 39 | 40 | self.__indent += 1 41 | 42 | def leave(self, e): 43 | if isinstance(e, (descriptor.Collection, descriptor.TopLevel, descriptor.Report)): 44 | self.__indent -= 1 45 | self.write(self.indent() + ")" + ("," if self.__indent else "") + "\n") 46 | 47 | if self.__indent == 0: 48 | self.write(""" 49 | if __name__ == "__main__": 50 | compile_main(descriptor) 51 | """) 52 | 53 | def element(self, e): 54 | if isinstance(e, descriptor.Align): 55 | self.write(self.indent() + "Align(Value.%s, %d)," % (e.way, e.boundary)+"\n") 56 | 57 | elif isinstance(e, descriptor.Padding): 58 | self.write(self.indent() + "Padding(Value.%s, %d)," % (e.way, e.size)+"\n") 59 | 60 | elif isinstance(e, descriptor.Value): 61 | us = Usage.lookup(e.usage) if e.usage else None 62 | self.write(self.indent() + "Value(Value.%s, %s, %d" % (e.way, us, e.size)) 63 | if int(e.flags) != int(item.DataItem.Data | item.DataItem.Variable | item.DataItem.Absolute) \ 64 | and not (e.namedArray and e.usage and e.flags == item.DataItem.Variable) \ 65 | and not (e.namedArray and not e.usage and e.flags == item.DataItem.Data | item.DataItem.Array): 66 | flags = str(e.flags) 67 | flags = re.sub(r"([A-Za-z]+)", r"Value.\1", flags) 68 | self.write(", flags = %s" % flags) 69 | # self.write(", reportId = %s" % list(e._Extractor__report_ids)[0]) 70 | if e.logicalMin != 1: 71 | self.write(", logicalMin = %s" % e.logicalMin) 72 | if isinstance(e.namedArray, UsageRange): 73 | self.write(", namedArray = %s" % e.namedArray) 74 | elif e.namedArray != None: 75 | self.write(", namedArray = [\n") 76 | for u in e.namedArray: 77 | self.write(self.indent(1) + str(u) + ",\n") 78 | self.write(self.indent(1) + "]") 79 | else: 80 | if e.logicalMax != None: 81 | self.write(", logicalMax = %s" % e.logicalMax) 82 | if e.physicalMin != e.logicalMin or e.physicalMax != e.logicalMax: 83 | if e.physicalMin != None: 84 | self.write(", physicalMin = %s" % e.physicalMin) 85 | if e.physicalMax != None: 86 | self.write(", physicalMax = %s" % e.physicalMax) 87 | if e.unit != 0: 88 | unit = re.sub(r"([A-Za-z]+)", r"Unit.\1", str(e.unit)) 89 | self.write(", unit = %s" % unit) 90 | if e.unitExponent != 0: 91 | self.write(", unitExponent = %s" % e.unitExponent) 92 | if e.designator != 0: 93 | self.write(", designator = %s" % e.designator) 94 | if e.string != 0: 95 | self.write(", string = %s" % e.string) 96 | if e.count != 1: 97 | self.write(", count = %d" % e.count) 98 | auto_alignment = min((8, e.size)) 99 | if e.alignment != None and e.alignment != auto_alignment: 100 | self.write(", alignment = %s" % e.alignment) 101 | self.write("),\n") 102 | -------------------------------------------------------------------------------- /hrdc/descriptor/extractor.py: -------------------------------------------------------------------------------- 1 | from ..stream import Stream 2 | from ..stream import item 3 | from .. import usage 4 | from .descriptor import * 5 | from .descriptor import Hierarchical 6 | 7 | class Extractor(Stream): 8 | def __init__(self): 9 | self.globals = {} 10 | for kind, tag in item.Item.registry.keys(): 11 | if kind == item.Item.Global: 12 | self.globals[int(tag)] = 0 13 | self.localsClear() 14 | self.stack = [] 15 | self.currentLevel = TopLevel() 16 | self.inNamedArrayCollection = False 17 | 18 | def localsClear(self): 19 | self.usages = [] 20 | self.strings = [] 21 | self.designators = [] 22 | self.locals = {} 23 | 24 | for tag in (item.LocalItem.UsageMinimum, item.LocalItem.UsageMaximum, 25 | item.LocalItem.DesignatorMinimum, item.LocalItem.DesignatorMaximum, 26 | item.LocalItem.StringMinimum, item.LocalItem.StringMaximum, 27 | item.LocalItem.Delimiter): 28 | self.locals[int(tag)] = None 29 | 30 | def globalValue(self, tag): 31 | return self.globals.get(int(tag), 0) 32 | 33 | def localValue(self, tag): 34 | return self.locals.get(int(tag), 0) 35 | 36 | def usage(self, index = 0): 37 | if not self.usages: 38 | return None 39 | u = self.usages[min((len(self.usages) - 1, int(index)))] 40 | if u >> 16: 41 | return u 42 | return usage.Usage.lookup((self.globals[int(item.GlobalItem.UsagePage)] << 16) | u) 43 | 44 | def string(self, index = 0): 45 | if not self.strings: 46 | return 0 47 | return self.strings[min((len(self.strings) - 1, int(index)))] 48 | 49 | def designator(self, index = 0): 50 | if not self.designators: 51 | return 0 52 | return self.designators[min((len(self.designators) - 1, int(index)))] 53 | 54 | def append(self, i): 55 | """Stream entry point""" 56 | try: 57 | self._append(i) 58 | except Exception as e: 59 | print("Exception while appending item", i) 60 | print("globals", self.globals) 61 | print("locals", self.locals) 62 | print("usages", self.usages) 63 | print("strings", self.strings) 64 | print("designators", self.designators) 65 | raise 66 | 67 | def _append(self, i): 68 | if i.kind == item.Item.Global: 69 | self.globals[int(i.tag)] = i.value 70 | 71 | elif i.kind == item.Item.Local: 72 | if i.tag == item.LocalItem.Usage: 73 | self.usages.append(i.value) 74 | 75 | elif i.tag == item.LocalItem.DesignatorIndex: 76 | self.designators.append(i.value) 77 | 78 | elif i.tag == item.LocalItem.StringIndex: 79 | self.strings.append(i.value) 80 | 81 | else: 82 | self.locals[int(i.tag)] = i.value 83 | 84 | elif i.kind == item.Item.Main: 85 | # Expand ranges 86 | for min, max, target in [ 87 | (item.LocalItem.UsageMinimum, item.LocalItem.UsageMaximum, self.usages), 88 | (item.LocalItem.DesignatorMinimum, item.LocalItem.DesignatorMaximum, self.designators), 89 | (item.LocalItem.StringMinimum, item.LocalItem.StringMaximum, self.strings), 90 | ]: 91 | if self.locals[int(min)] is not None and self.locals[int(max)] is not None and not target: 92 | for val in range(int(self.locals[int(min)]), int(self.locals[int(max)] + 1)): 93 | target.append(val) 94 | 95 | if i.tag == item.MainItem.Collection: 96 | self.enter(i.value, self.usage()) 97 | 98 | elif i.tag == item.MainItem.EndCollection: 99 | if self.inNamedArrayCollection: 100 | self.inNamedArrayCollection = False 101 | else: 102 | self.leave(self.currentLevel) 103 | 104 | if len(self.stack) == 0: 105 | self.reportIdMerge(self.currentLevel) 106 | self.ended(self.currentLevel) 107 | 108 | elif i.tag in (item.MainItem.Input, 109 | item.MainItem.Output, 110 | item.MainItem.Feature): 111 | self.handleDataItem(i) 112 | 113 | self.localsClear() 114 | 115 | def enter(self, kind, usage): 116 | self.stack.append(self.currentLevel) 117 | self.currentLevel = Collection(kind, usage) 118 | 119 | def leave(self, level = None): 120 | self.currentLevel = self.stack.pop() 121 | if level: 122 | self.reportIdMerge(level) 123 | self.currentLevel.append(level) 124 | 125 | def reportIdMerge(self, level): 126 | level.__report_ids = set() 127 | for child in level.members: 128 | level.__report_ids |= child.__report_ids 129 | 130 | def addValue(self, v): 131 | v.__report_ids = set([int(self.globalValue(item.GlobalItem.ReportID))]) 132 | self.currentLevel.append(v) 133 | 134 | def ended(self, root): 135 | self.root = root 136 | self.root = self.reportInsert(self.root) 137 | 138 | if len(self.root) == 1 and isinstance(self.root.members[0], TopLevel): 139 | self.root = self.root.members[0] 140 | 141 | def reportInsert(self, level): 142 | assert isinstance(level, Hierarchical) 143 | 144 | if isinstance(level, TopLevel): 145 | r = TopLevel() 146 | elif isinstance(level, Collection): 147 | r = Collection(level.kind, level.usage) 148 | else: 149 | raise ValueError(level) 150 | 151 | currentReport = None 152 | 153 | for value in level.members: 154 | if len(value.__report_ids) == 1 or isinstance(value, Value): 155 | if not currentReport or currentReport.id not in value.__report_ids: 156 | currentReport = Report(list(value.__report_ids)[0]) 157 | r.append(currentReport) 158 | 159 | currentReport.append(value) 160 | 161 | else: 162 | assert isinstance(value, Hierarchical) 163 | currentReport = None 164 | r.append(self.reportInsert(value)) 165 | 166 | return r 167 | 168 | def handleDataItem(self, i): 169 | if i.value & item.DataItem.Constant: 170 | self.addValue(Padding(i.tag, self.globalValue(item.GlobalItem.ReportSize) 171 | * self.globalValue(item.GlobalItem.ReportCount))) 172 | 173 | elif (self.globalValue(item.GlobalItem.ReportCount) == 1 174 | and len(self.usages) == (self.globalValue(item.GlobalItem.LogicalMaximum) 175 | - self.globalValue(item.GlobalItem.LogicalMinimum) + 1) 176 | and ((i.value == item.DataItem.Variable 177 | and self.currentLevel.kind == Collection.NamedArray) 178 | or (i.value == item.DataItem.Array 179 | and self.currentLevel.kind == Collection.Logical 180 | and usage.NAry.test(self.currentLevel.usage)) 181 | or (self.currentLevel.kind == Collection.Logical 182 | and usage.NAry.test(self.currentLevel.usage)))): 183 | 184 | self.inNamedArrayCollection = True 185 | arrayCollection = self.currentLevel 186 | self.leave() 187 | assert len(arrayCollection) == 0 188 | 189 | uArray = list(map(self.usage, list(range(len(self.usages))))) 190 | try: 191 | uArray = usage.UsageRange.from_array(uArray) 192 | except ValueError: 193 | pass 194 | 195 | self.addValue( 196 | Value(i.tag, arrayCollection.usage, 197 | self.globalValue(item.GlobalItem.ReportSize), 198 | logicalMin = self.globalValue(item.GlobalItem.LogicalMinimum), 199 | namedArray = uArray, 200 | designator = self.designator(), 201 | string = self.string(), 202 | ) 203 | ) 204 | 205 | # Named array without enclosing collection (like in Keyboard RD) 206 | elif i.value == (item.DataItem.Data | item.DataItem.Array) and \ 207 | len(self.usages) == (self.globalValue(item.GlobalItem.LogicalMaximum) 208 | - self.globalValue(item.GlobalItem.LogicalMinimum) + 1): 209 | 210 | uArray = list(map(self.usage, list(range(len(self.usages))))) 211 | try: 212 | uArray = usage.UsageRange.from_array(uArray) 213 | except ValueError as e: 214 | print(e) 215 | pass 216 | for no in range(self.globalValue(item.GlobalItem.ReportCount)): 217 | self.addValue( 218 | Value(i.tag, usage = None, 219 | size = self.globalValue(item.GlobalItem.ReportSize), 220 | logicalMin = self.globalValue(item.GlobalItem.LogicalMinimum), 221 | namedArray = uArray, 222 | designator = self.designator(), 223 | string = self.string(), 224 | ) 225 | ) 226 | 227 | elif i.value & item.DataItem.BufferedBytes: 228 | 229 | ue = self.globalValue(item.GlobalItem.UnitExponent) 230 | self.addValue( 231 | Value(i.tag, self.usage(), 232 | self.globalValue(item.GlobalItem.ReportSize), 233 | flags = i.value, 234 | logicalMin = self.globalValue(item.GlobalItem.LogicalMinimum), 235 | logicalMax = self.globalValue(item.GlobalItem.LogicalMaximum), 236 | physicalMin = self.globalValue(item.GlobalItem.PhysicalMinimum), 237 | physicalMax = self.globalValue(item.GlobalItem.PhysicalMaximum), 238 | designator = self.designator(), 239 | string = self.string(), 240 | unit = self.globalValue(item.GlobalItem.Unit), 241 | unitExponent = ue, 242 | count = self.globalValue(item.GlobalItem.ReportCount), 243 | ) 244 | ) 245 | 246 | else: 247 | 248 | for n in range(self.globalValue(item.GlobalItem.ReportCount)): 249 | self.addValue( 250 | Value(i.tag, self.usage(n), 251 | self.globalValue(item.GlobalItem.ReportSize), 252 | flags = i.value, 253 | logicalMin = self.globalValue(item.GlobalItem.LogicalMinimum), 254 | logicalMax = self.globalValue(item.GlobalItem.LogicalMaximum), 255 | physicalMin = self.globalValue(item.GlobalItem.PhysicalMinimum), 256 | physicalMax = self.globalValue(item.GlobalItem.PhysicalMaximum), 257 | designator = self.designator(n), 258 | string = self.string(n), 259 | unit = self.globalValue(item.GlobalItem.Unit), 260 | unitExponent = self.globalValue(item.GlobalItem.UnitExponent), 261 | ) 262 | ) 263 | 264 | def main(): 265 | import sys 266 | import argparse 267 | from ..stream import parser 268 | 269 | cmdline = argparse.ArgumentParser(description='Decompile a HID report descriptor') 270 | 271 | cmdline.add_argument("-i", "--input-format", metavar = "NAME", type = str, 272 | default = "binary", help = "Input parser name") 273 | 274 | cmdline.add_argument("input", type = argparse.FileType('r'), 275 | default = "-", 276 | nargs = "?", help = "Input file name") 277 | 278 | cmdline.add_argument("output", type = argparse.FileType('w'), 279 | default = "-", 280 | nargs = "?", help = "Output file name") 281 | 282 | args = cmdline.parse_args() 283 | 284 | _parser = parser.Parser.get(args.input_format) 285 | 286 | extractor = Extractor() 287 | 288 | p = _parser(args.input, extractor) 289 | p.read() 290 | 291 | from .dumper import Dumper 292 | extractor.root.accept(Dumper(args.output)) 293 | 294 | 295 | if __name__ == "__main__": 296 | main() 297 | -------------------------------------------------------------------------------- /hrdc/descriptor/streamer.py: -------------------------------------------------------------------------------- 1 | from . import descriptor 2 | from ..stream import item 3 | 4 | __all__ = ["Streamer"] 5 | 6 | class Streamer(descriptor.Visitor): 7 | def __init__(self, stream): 8 | self.stream = stream 9 | self.report_size = { 10 | int(item.MainItem.Input) : [0] * 256, 11 | int(item.MainItem.Output) : [0] * 256, 12 | int(item.MainItem.Feature) : [0] * 256, 13 | } 14 | self.report_id = 0 15 | 16 | def enter(self, e): 17 | a = self.stream.append 18 | 19 | if isinstance(e, descriptor.Report): 20 | self.report_id = e.id 21 | if e.id: 22 | a(item.ReportID(e.id)) 23 | 24 | elif isinstance(e, descriptor.Collection): 25 | a(item.Usage(e.usage)) 26 | a(item.Collection(e.kind)) 27 | 28 | def leave(self, e): 29 | a = self.stream.append 30 | 31 | if isinstance(e, descriptor.Collection): 32 | a(item.EndCollection()) 33 | 34 | def element(self, e): 35 | a = self.stream.append 36 | align = 0 37 | pad = 0 38 | 39 | if isinstance(e, descriptor.Align): 40 | align = e.boundary 41 | 42 | elif isinstance(e, descriptor.Padding): 43 | pad = e.size 44 | 45 | elif isinstance(e, descriptor.Value): 46 | align = e.alignment 47 | 48 | if align > 1: 49 | current_size = self.report_size[int(e.way)][self.report_id] 50 | partial = current_size % align 51 | pad = (align - partial) if partial else 0 52 | 53 | if pad: 54 | a(item.ReportCount(1)) 55 | a(item.ReportSize(pad)) 56 | self.report_size[int(e.way)][self.report_id] += pad 57 | a(item.Item.itemclass(item.Item.Main, e.way)(item.DataItem.Constant | item.DataItem.Variable)) 58 | 59 | if isinstance(e, descriptor.Value) and not isinstance(e, (descriptor.Align, descriptor.Padding)): 60 | constant = bool(e.flags & item.DataItem.Constant) 61 | if not constant: 62 | if e.usage: 63 | a(item.Usage(e.usage)) 64 | 65 | if e.namedArray: 66 | a(item.PhysicalMinimum(0)) 67 | a(item.PhysicalMaximum(0)) 68 | if e.usage: 69 | a(item.Collection(item.Collection.Logical)) 70 | for usage in e.namedArray: 71 | a(item.Usage(usage)) 72 | 73 | elif e.physicalMin == e.logicalMin and e.physicalMax == e.logicalMax: 74 | a(item.PhysicalMinimum(0)) 75 | a(item.PhysicalMaximum(0)) 76 | else: 77 | a(item.PhysicalMinimum(e.physicalMin)) 78 | a(item.PhysicalMaximum(e.physicalMax)) 79 | 80 | a(item.LogicalMinimum(e.logicalMin)) 81 | a(item.LogicalMaximum(e.logicalMax)) 82 | 83 | a(item.Unit(e.unit)) 84 | a(item.UnitExponent(e.unitExponent)) 85 | if int(e.designator): 86 | a(item.DesignatorIndex(e.designator)) 87 | if int(e.string): 88 | a(item.StringIndex(e.string)) 89 | 90 | a(item.ReportCount(e.count)) 91 | a(item.ReportSize(e.size)) 92 | self.report_size[int(e.way)][self.report_id] += int(e.size) 93 | a(item.Item.itemclass(item.Item.Main, e.way)(e.flags)) 94 | 95 | if e.namedArray and not constant and e.usage: 96 | a(item.EndCollection()) 97 | -------------------------------------------------------------------------------- /hrdc/descriptor/test.py: -------------------------------------------------------------------------------- 1 | def descriptor_expected(test_case, descriptor, expected_code): 2 | from ..stream import optimizer, formatter, comparer 3 | from . import streamer 4 | 5 | output = comparer.ExpectedStream(test_case, expected_code) 6 | output = optimizer.Optimizer.new(output) 7 | visitor = streamer.Streamer(output) 8 | descriptor.accept(visitor) 9 | output.close() 10 | -------------------------------------------------------------------------------- /hrdc/stream/__init__.py: -------------------------------------------------------------------------------- 1 | from .stream import Stream 2 | from .optimizer import Optimizer 3 | from . import formatter 4 | from . import parser 5 | 6 | -------------------------------------------------------------------------------- /hrdc/stream/comparer.py: -------------------------------------------------------------------------------- 1 | import re 2 | from . import stream 3 | from .formatter.binary import Binary 4 | from .parser.code import Code 5 | 6 | class ExpectedStream(stream.Stream): 7 | def __init__(self, test_case, expected_code): 8 | self.test_case = test_case 9 | 10 | contents = "" 11 | for line in expected_code.split("\n"): 12 | contents += line.split("//", 1)[0] + " " 13 | contents = re.sub(r"/\*.*?\*/", "", contents) 14 | contents = re.sub(r"\s+", "", contents) 15 | self.expected = bytes(int(x, 16) for x in contents.split(",") if x) 16 | self.point = 0 17 | 18 | def append(self, item): 19 | part = item.bytes() 20 | expected = self.expected[self.point : self.point + len(part)] 21 | self.test_case.assertEqual(expected, part) 22 | self.point += len(part) 23 | 24 | def close(self): 25 | self.test_case.assertEqual(self.point, len(self.expected)) 26 | -------------------------------------------------------------------------------- /hrdc/stream/formatter/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import Formatter 2 | from .hex import Hex 3 | from .code import Code 4 | from .binary import Binary 5 | 6 | -------------------------------------------------------------------------------- /hrdc/stream/formatter/base.py: -------------------------------------------------------------------------------- 1 | from ..stream import Stream 2 | 3 | class _FormatterMeta(type): 4 | def __init__(cls, name, bases, dct): 5 | type.__init__(cls, name, bases, dct) 6 | try: 7 | Formatter.registry[cls.__name__.lower()] = cls 8 | except: 9 | pass 10 | 11 | class Formatter(Stream, metaclass = _FormatterMeta): 12 | 13 | registry = {} 14 | 15 | @classmethod 16 | def get(cls, name): 17 | return cls.registry[name] 18 | -------------------------------------------------------------------------------- /hrdc/stream/formatter/binary.py: -------------------------------------------------------------------------------- 1 | from .base import Formatter 2 | 3 | class Binary(Formatter): 4 | def __init__(self, output): 5 | import sys 6 | if output == sys.stdout and hasattr(output, "buffer"): 7 | self.output = output.buffer 8 | else: 9 | self.output = output 10 | 11 | def append(self, i): 12 | self.output.write(i.bytes()) 13 | 14 | def close(self): 15 | pass 16 | -------------------------------------------------------------------------------- /hrdc/stream/formatter/code.py: -------------------------------------------------------------------------------- 1 | from .base import Formatter 2 | 3 | class Code(Formatter): 4 | def __init__(self, output): 5 | self.indent = 0 6 | self.output = output 7 | 8 | def append(self, i): 9 | from ...stream import item 10 | 11 | blob = ", ".join(["0x%02x" % x for x in i.bytes()]) + "," 12 | if isinstance(i, item.EndCollection): 13 | self.indent -= 1 14 | self.output.write(" " + blob.ljust(6 * 5) + " // " + (" " * self.indent * 4) + str(i) + "\n") 15 | if isinstance(i, item.Collection): 16 | self.indent += 1 17 | 18 | def close(self): 19 | if self.indent != 0: 20 | import warnings 21 | warnings.warn("Closing an item stream with non-zero indentation level") 22 | 23 | 24 | -------------------------------------------------------------------------------- /hrdc/stream/formatter/hex.py: -------------------------------------------------------------------------------- 1 | from .base import Formatter 2 | 3 | class Hex(Formatter): 4 | def __init__(self, output): 5 | self.output = output 6 | 7 | def append(self, i): 8 | blob = " ".join(["%02x" % x for x in i.bytes()]) 9 | self.output.write(blob + "\n") 10 | 11 | def close(self): 12 | pass 13 | -------------------------------------------------------------------------------- /hrdc/stream/item.py: -------------------------------------------------------------------------------- 1 | from ..util import NamedConstant 2 | 3 | class _ItemMeta(type): 4 | def __init__(cls, name, bases, dct): 5 | type.__init__(cls, name, bases, dct) 6 | try: 7 | key = int(cls.kind), int(cls.tag) 8 | Item.registry[key] = cls 9 | except: 10 | pass 11 | 12 | class Item(metaclass = _ItemMeta): 13 | Main = NamedConstant("Main", 0) 14 | Global = NamedConstant("Global", 1) 15 | Local = NamedConstant("Local", 2) 16 | 17 | signed = False 18 | 19 | registry = {} 20 | 21 | @property 22 | def tagname(self): 23 | if isinstance(self.tag, NamedConstant): 24 | return str(self.tag) 25 | return self.__class__.__name__ 26 | 27 | @classmethod 28 | def itemclass(cls, kind, tag): 29 | return cls.registry[(int(kind), int(tag))] 30 | 31 | @classmethod 32 | def parse(cls, data): 33 | kind = (data[0] >> 2) & 3 34 | tag = data[0] >> 4 35 | itemclass = cls.itemclass(kind, tag) 36 | 37 | size = data[0] & 0x3 38 | 39 | if size == 1: 40 | format = "= 0, \ 62 | ValueError("Unexpected value for %s: %d" 63 | % (self.__class__.__name__, self.value)) 64 | self.optionalValue = self.kind == self.Main and \ 65 | self.tag in (MainItem.Input, MainItem.EndCollection) 66 | 67 | def __str__(self): 68 | if int(self.value) or not self.optionalValue: 69 | return "%s (%s)" % (self.tagname, self.value) 70 | else: 71 | return "%s" % (self.tagname,) 72 | 73 | def bytes(self): 74 | import math 75 | import struct 76 | 77 | m = int(self.value) 78 | bits = int(not self.optionalValue) 79 | neg = self.signed and m < 0 80 | 81 | if neg: 82 | m = - m - 1 83 | 84 | if m: 85 | bits = int(math.log(m, 2)) + int(self.signed) + 1 86 | else: 87 | bits = 1 if self.value else 0 88 | 89 | if not bits and self.optionalValue: 90 | fmt = "" 91 | l = 0 92 | elif bits <= 8: 93 | fmt = " 1: 25 | self.pending.append(Item.itemclass(Item.Local, self.min)(self.minValue)) 26 | self.pending.append(Item.itemclass(Item.Local, self.max)(self.maxValue)) 27 | else: 28 | self.pending.append(Item.itemclass(Item.Local, self.lookup)(self.minValue)) 29 | self.pending.append(Item.itemclass(Item.Local, self.lookup)(self.maxValue)) 30 | 31 | for i in self.pending: 32 | self.stream.append(i) 33 | self.pending = [] 34 | 35 | self.merging = True 36 | self.minValue = None 37 | self.maxValue = None 38 | self.main = None 39 | 40 | def close(self): 41 | self.flush() 42 | self.stream.close() 43 | 44 | def append(self, item): 45 | from ..item import Item, LocalItem 46 | 47 | if self.merging and item.kind == Item.Local: 48 | if item.tag in (self.min, self.max): 49 | self.merging = False 50 | 51 | elif item.tag == self.lookup: 52 | if self.maxValue is None: 53 | self.maxValue = item.value 54 | self.minValue = item.value 55 | elif int(item.value) == int(self.maxValue) + 1: 56 | self.maxValue = item.value 57 | else: 58 | self.merging = False 59 | 60 | if item.kind == Item.Main: 61 | self.flush() 62 | 63 | self.pending.append(item) 64 | 65 | class DesignatorMerge(RangeMerge): 66 | precedence = 30 67 | 68 | def __init__(self, stream): 69 | from ..item import LocalItem 70 | RangeMerge.__init__(self, stream, LocalItem.DesignatorIndex, LocalItem.DesignatorMinimum, LocalItem.DesignatorMaximum) 71 | 72 | class StringMerge(RangeMerge): 73 | precedence = 35 74 | 75 | def __init__(self, stream): 76 | from ..item import LocalItem 77 | RangeMerge.__init__(self, stream, LocalItem.StringIndex, LocalItem.StringMinimum, LocalItem.StringMaximum) 78 | 79 | class UsageMerge(RangeMerge): 80 | precedence = 20 81 | 82 | def __init__(self, stream): 83 | from ..item import LocalItem 84 | RangeMerge.__init__(self, stream, LocalItem.Usage, LocalItem.UsageMinimum, LocalItem.UsageMaximum) 85 | -------------------------------------------------------------------------------- /hrdc/stream/optimizer/usage_page.py: -------------------------------------------------------------------------------- 1 | from .base import Optimizer 2 | 3 | class UsagePage(Optimizer): 4 | precedence = 10 5 | 6 | def __init__(self, stream): 7 | self.stream = stream 8 | self.page = None 9 | 10 | def append(self, item): 11 | from ..item import Item, GlobalItem, LocalItem, UsagePage, Usage 12 | from ...util import NamedConstant 13 | from ...usage import Usage 14 | 15 | if item.kind == Item.Local and item.tag in (LocalItem.Usage, LocalItem.UsageMinimum, LocalItem.UsageMaximum): 16 | 17 | sp, su = Usage.lookup(item.value).split() 18 | 19 | if self.page is None or self.page != int(sp): 20 | self.page = int(sp) 21 | self.stream.append(UsagePage(sp)) 22 | 23 | item = Item.itemclass(item.kind, item.tag)(su) 24 | 25 | self.stream.append(item) 26 | 27 | if item.kind == Item.Global and item.tag == GlobalItem.UsagePage: 28 | self.page = int(item.value) 29 | 30 | def close(self): 31 | self.stream.close() 32 | -------------------------------------------------------------------------------- /hrdc/stream/parser/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import Parser 2 | from .binary import Binary 3 | from .hex import Hex 4 | from .code import Code 5 | -------------------------------------------------------------------------------- /hrdc/stream/parser/base.py: -------------------------------------------------------------------------------- 1 | from ..stream import Stream 2 | from .. import item 3 | from ... import usage 4 | 5 | class _ParserMeta(type): 6 | def __init__(cls, name, bases, dct): 7 | type.__init__(cls, name, bases, dct) 8 | try: 9 | Parser.registry[cls.__name__.lower()] = cls 10 | except: 11 | pass 12 | 13 | class Parser(Stream, metaclass = _ParserMeta): 14 | registry = {} 15 | 16 | @classmethod 17 | def get(cls, name): 18 | return cls.registry[name] 19 | 20 | def __init__(self, input, output): 21 | self.input = input 22 | self.output = output 23 | self.usagePage = 0 24 | 25 | def blobParse(self, blob): 26 | while blob: 27 | header = blob[0] 28 | size = header & 0x3 29 | data_size = (1 << (size - 1)) + 1 if size else 1 30 | item = blob[:data_size] 31 | blob = blob[data_size:] 32 | self.itemParse(item) 33 | self.output.close() 34 | 35 | def itemParse(self, data): 36 | self.append(item.Item.parse(data)) 37 | 38 | def append(self, i): 39 | if i.kind == item.Item.Global and i.tag == item.GlobalItem.UsagePage: 40 | self.usagePage = int(i.value) 41 | i = item.UsagePage(usage.Usage.page(i.value)) 42 | 43 | if i.kind == item.Item.Local \ 44 | and i.tag in (item.LocalItem.Usage, item.LocalItem.UsageMinimum, item.LocalItem.UsageMaximum) \ 45 | and (int(i.value) >> 16) == 0: 46 | i = item.Item.itemclass(i.kind, i.tag)(usage.Usage.inpage(self.usagePage, i.value)) 47 | 48 | self.output.append(i) 49 | 50 | -------------------------------------------------------------------------------- /hrdc/stream/parser/binary.py: -------------------------------------------------------------------------------- 1 | from .base import Parser 2 | 3 | class Binary(Parser): 4 | def __init__(self, input, output): 5 | import sys 6 | 7 | if input == sys.stdin and hasattr(input, "buffer"): 8 | input = input.buffer 9 | 10 | Parser.__init__(self, input, output) 11 | 12 | def read(self, *args): 13 | blob = self.input.read(*args) 14 | self.blobParse(blob) 15 | 16 | -------------------------------------------------------------------------------- /hrdc/stream/parser/code.py: -------------------------------------------------------------------------------- 1 | from .base import Parser 2 | import re 3 | 4 | class Code(Parser): 5 | def read(self, *args): 6 | contents = "" 7 | for line in self.input.read(*args).split("\n"): 8 | contents += line.split("//", 1)[0] + " " 9 | contents = re.sub(r"/\*.*?\*/", "", contents) 10 | contents = re.sub(r"\s+", "", contents) 11 | blob = bytes(int(x, 16) for x in contents.split(",") if x) 12 | self.blobParse(blob) 13 | 14 | -------------------------------------------------------------------------------- /hrdc/stream/parser/hex.py: -------------------------------------------------------------------------------- 1 | from .base import Parser 2 | import re 3 | 4 | class Hex(Parser): 5 | def read(self, *args): 6 | contents = self.input.read(*args) 7 | blob = bytes(int(x, 16) for x in re.findall(r"\b(?:0x)?[0-9A-Fa-f]{2}\b", contents)) 8 | self.blobParse(blob) 9 | 10 | -------------------------------------------------------------------------------- /hrdc/stream/stream.py: -------------------------------------------------------------------------------- 1 | 2 | class Stream(object): 3 | """ 4 | A report descriptor item stream 5 | """ 6 | 7 | def append(self, item): 8 | """ 9 | Append the item to the report descriptor item stream 10 | """ 11 | raise RuntimeError("unimplemented") 12 | 13 | def close(self): 14 | """ 15 | Mark item stream as done 16 | """ 17 | ... 18 | -------------------------------------------------------------------------------- /hrdc/usage/__init__.py: -------------------------------------------------------------------------------- 1 | from .usage import * 2 | 3 | from . import button 4 | from . import pid 5 | from . import keyboard 6 | from . import desktop 7 | from . import telephony 8 | from . import consumer 9 | from . import ordinal 10 | from . import simulation 11 | from . import led 12 | from . import vr 13 | from . import game 14 | from . import camera 15 | from . import device 16 | from . import digitizers 17 | from . import lighting 18 | # Arcade page looks vendor-specific (0xff00), dont include it by default 19 | # import arcade 20 | from . import sensors 21 | -------------------------------------------------------------------------------- /hrdc/usage/arcade.py: -------------------------------------------------------------------------------- 1 | from .usage import * 2 | 3 | GeneralPurposeIOCard = Usage("arcade.GeneralPurposeIOCard", 0xff000001, CA) 4 | CoinDoor = Usage("arcade.CoinDoor", 0xff000002, CA) 5 | WatchdogTimer = Usage("arcade.WatchdogTimer", 0xff000003, CA) 6 | GeneralPurposeAnalogInputState = Usage("arcade.GeneralPurposeAnalogInputState", 0xff000030, DV) 7 | GeneralPurposeDigitalInputState = Usage("arcade.GeneralPurposeDigitalInputState", 0xff000031, DV) 8 | GeneralPurposeOpticalInputState = Usage("arcade.GeneralPurposeOpticalInputState", 0xff000032, DV) 9 | GeneralPurposeDigitalOutputState = Usage("arcade.GeneralPurposeDigitalOutputState", 0xff000033, DV) 10 | NumberofCoinDoors = Usage("arcade.NumberofCoinDoors", 0xff000034, DV) 11 | CoinDrawerDropCount = Usage("arcade.CoinDrawerDropCount", 0xff000035, DV) 12 | CoinDrawerStart = Usage("arcade.CoinDrawerStart", 0xff000036, OOC) 13 | CoinDrawerService = Usage("arcade.CoinDrawerService", 0xff000037, OOC) 14 | CoinDrawerTilt = Usage("arcade.CoinDrawerTilt", 0xff000038, OOC) 15 | CoinDoorTest = Usage("arcade.CoinDoorTest", 0xff000039, OOC) 16 | CoinDoorLockout = Usage("arcade.CoinDoorLockout", 0xff000040, OOC) 17 | WatchdogTimeout = Usage("arcade.WatchdogTimeout", 0xff000041, DV) 18 | WatchdogAction = Usage("arcade.WatchdogAction", 0xff000042, NAry) 19 | WatchdogReboot = Usage("arcade.WatchdogReboot", 0xff000043, Sel) 20 | WatchdogRestart = Usage("arcade.WatchdogRestart", 0xff000044, Sel) 21 | AlarmInput = Usage("arcade.AlarmInput", 0xff000045, DV) 22 | CoinDoorCounter = Usage("arcade.CoinDoorCounter", 0xff000046, OOC) 23 | IODirectionMapping = Usage("arcade.IODirectionMapping", 0xff000047, DV) 24 | SetIODirection = Usage("arcade.SetIODirection", 0xff000048, OOC) 25 | ExtendedOpticalInputState = Usage("arcade.ExtendedOpticalInputState", 0xff000049, DV) 26 | PinPadInputState = Usage("arcade.PinPadInputState", 0xff00004A, DV) 27 | PinPadStatus = Usage("arcade.PinPadStatus", 0xff00004B, DV) 28 | PinPadOutput = Usage("arcade.PinPadOutput", 0xff00004C, OOC) 29 | PinPadCommand = Usage("arcade.PinPadCommand", 0xff00004D,DV) 30 | -------------------------------------------------------------------------------- /hrdc/usage/button.py: -------------------------------------------------------------------------------- 1 | from .usage import * 2 | 3 | def Button(n): 4 | return Usage("button.Button(%d)" % int(n), 0x90000 + int(n), 5 | Sel, OOC, MC, OSC) 6 | 7 | for i in range(128): 8 | Button(i) 9 | -------------------------------------------------------------------------------- /hrdc/usage/camera.py: -------------------------------------------------------------------------------- 1 | from .usage import * 2 | 3 | Autofocus = Usage("camera.Autofocus", 0x900020, OSC) 4 | Shutter = Usage("camera.Shutter", 0x900021, OSC) 5 | -------------------------------------------------------------------------------- /hrdc/usage/desktop.py: -------------------------------------------------------------------------------- 1 | from .usage import * 2 | 3 | Pointer = Usage("desktop.Pointer", 0x10001, CP) 4 | Mouse = Usage("desktop.Mouse", 0x10002, CA) 5 | Joystick = Usage("desktop.Joystick", 0x10004, CA) 6 | Gamepad = Usage("desktop.Gamepad", 0x10005, CA) 7 | Keyboard = Usage("desktop.Keyboard", 0x10006, CA) 8 | Keypad = Usage("desktop.Keypad", 0x10007, CA) 9 | MultiAxisCtrl = Usage("desktop.MultiAxisCtrl", 0x10008, CA) 10 | TabletPCSystem = Usage("desktop.TabletPcSystem", 0x10009, CA) 11 | 12 | # HUTRR51 13 | PortableDeviceControl = Usage("desktop.PortableDeviceControl", 0x1000d, CA) 14 | 15 | X = Usage("desktop.X", 0x10030, DV) 16 | Y = Usage("desktop.Y", 0x10031, DV) 17 | Z = Usage("desktop.Z", 0x10032, DV) 18 | Rx = Usage("desktop.Rx", 0x10033, DV) 19 | Ry = Usage("desktop.Ry", 0x10034, DV) 20 | Rz = Usage("desktop.Rz", 0x10035, DV) 21 | Slider = Usage("desktop.Slider", 0x10036, DV) 22 | Dial = Usage("desktop.Dial", 0x10037, DV) 23 | Wheel = Usage("desktop.Wheel", 0x10038, DV) 24 | HatSwitch = Usage("desktop.HatSwitch", 0x10039, DV) 25 | CountedBuffer = Usage("desktop.CountedBuffer", 0x1003A, CL) 26 | ByteCount = Usage("desktop.ByteCount", 0x1003B, DV) 27 | MotionWakeup = Usage("desktop.MotionWakeup", 0x1003C, OSC) 28 | Start = Usage("desktop.start", 0x1003D, OOC) 29 | Select = Usage("desktop.select", 0x1003E, OOC) 30 | Vx = Usage("desktop.Vx", 0x10040, DV) 31 | Vy = Usage("desktop.Vy", 0x10041, DV) 32 | Vz = Usage("desktop.Vz", 0x10042, DV) 33 | Vbrx = Usage("desktop.Vbrx", 0x10043, DV) 34 | Vbry = Usage("desktop.Vbry", 0x10044, DV) 35 | Vbrz = Usage("desktop.Vbrz", 0x10045, DV) 36 | Vno = Usage("desktop.Vno", 0x10046, DV) 37 | FeatureNotification = Usage("desktop.FeatureNotification", 0x10047, DV) 38 | ResolutionMultiplier = Usage("desktop.ResolutionMultiplier", 0x10048, DV) 39 | SysControl = Usage("desktop.SysControl", 0x10080, CA) 40 | SysPowerDown = Usage("desktop.SysPowerDown", 0x10081, OSC) 41 | SysSleep = Usage("desktop.SysSleep", 0x10082, OSC) 42 | SysWakeUp = Usage("desktop.SysWakeUp", 0x10083, OSC) 43 | SysContextMenu = Usage("desktop.SysContextMenu", 0x10084, OSC) 44 | SysMainMenu = Usage("desktop.SysMainMenu", 0x10085, OSC) 45 | SysAppMenu = Usage("desktop.SysAppMenu", 0x10086, OSC) 46 | SysMenuHelp = Usage("desktop.SysMenuHelp", 0x10087, OSC) 47 | SysMenuExit = Usage("desktop.SysMenuExit", 0x10088, OSC) 48 | SysMenuSelect = Usage("desktop.SysMenuSelect", 0x10089, OSC) 49 | SysMenuRight = Usage("desktop.SysMenuRight", 0x1008A, RTC) 50 | SysMenuLeft = Usage("desktop.SysMenuLeft", 0x1008B, RTC) 51 | SysMenuUp = Usage("desktop.SysMenuUp", 0x1008C, RTC) 52 | SysMenuDown = Usage("desktop.SysMenuDown", 0x1008D, RTC) 53 | SysColdRestart = Usage("desktop.SysColdRestart", 0x1008E, OSC) 54 | SysWarmRestart = Usage("desktop.SysWarmRestart", 0x1008F, OSC) 55 | DPadUp = Usage("desktop.DPadUp", 0x10090, OOC) 56 | DPadDown = Usage("desktop.DPadDown", 0x10091, OOC) 57 | DPadRight = Usage("desktop.DPadRight", 0x10092, OOC) 58 | DPadLeft = Usage("desktop.DPadLeft", 0x10093, OOC) 59 | SysDock = Usage("desktop.SysDock", 0x100A0, OSC) 60 | SysUndock = Usage("desktop.SysUndock", 0x100A1, OSC) 61 | SysSetup = Usage("desktop.SysSetup", 0x100A2, OSC) 62 | SysBreak = Usage("desktop.SysBreak", 0x100A3, OSC) 63 | SysDbgrBreak = Usage("desktop.SysDbgrBreak", 0x100A4, OSC) 64 | AppBreak = Usage("desktop.AppBreak", 0x100A5, OSC) 65 | AppDbgrBreak = Usage("desktop.AppDbgrBreak", 0x100A6, OSC) 66 | SysSpeakerMute = Usage("desktop.SysSpeakerMute", 0x100A7, OSC) 67 | SysHibernate = Usage("desktop.SysHibernate", 0x100A8, OSC) 68 | SysDsplInvert = Usage("desktop.SysDsplInvert", 0x100B0, OSC) 69 | SysDsplInternal = Usage("desktop.SysDsplInternal", 0x100B1, OSC) 70 | SysDsplExternal = Usage("desktop.SysDsplExternal", 0x100B2, OSC) 71 | SysDsplBoth = Usage("desktop.SysDsplBoth", 0x100B3, OSC) 72 | SysDsplDual = Usage("desktop.SysDsplDual", 0x100B4, OSC) 73 | SysDsplToggleIntExt = Usage("desktop.SysDsplToggleIntExt", 0x100B5, OSC) 74 | SysDsplSwap12 = Usage("desktop.SysDsplSwap12", 0x100B6, OSC) 75 | SysDsplLcdAutoscale = Usage("desktop.SysDsplLcdAutoscale", 0x100B7, OSC) 76 | 77 | # HUTRR40 78 | WirelessRadioControls = Usage("desktop.WirelessRadioControls", 0x1000c, CA) 79 | WirelessRadioButton = Usage("desktop.WirelessRadioButton", 0x100c6, OOC) 80 | WirelessRadioLed = Usage("desktop.WirelessRadioLed", 0x100c7, OOC) 81 | WirelessRadioSliderSwitch = Usage("desktop.WirelessRadioSliderSwitch", 0x100c8, OOC) 82 | 83 | # HUTRR52 84 | SystemDisplayRotationLockButton = Usage("desktop.SystemDisplayRotationLockButton", 0x1000C9, OOC) 85 | SystemDisplayRotationLockSliderSwitch = Usage("desktop.SystemDisplayRotationLockSliderSwitch", 0x100CA, OOC) 86 | 87 | # HUTRR50 88 | ControlEnable = Usage("desktop.ControlEnable", 0x100cb, DF) 89 | -------------------------------------------------------------------------------- /hrdc/usage/device.py: -------------------------------------------------------------------------------- 1 | from .usage import * 2 | 3 | BatteryStrength = Usage("device.BatteryStrength", 0x60020, DV) 4 | WirelessChannel = Usage("device.WirelessChannel", 0x60021, DV) 5 | WirelessID = Usage("device.WirelessID", 0x60022, DV) 6 | DiscoverWirelessControl = Usage("device.DiscoverWirelessControl", 0x60023, OSC) 7 | SecurityCodeCharacterEntered = Usage("device.SecurityCodeCharacterEntered", 0x60024, OSC) 8 | SecurityCodeCharacterErased = Usage("device.SecurityCodeCharacterErased", 0x60025, OSC) 9 | SecurityCodeCleared = Usage("device.SecurityCodeCleared", 0x60026, OSC) 10 | -------------------------------------------------------------------------------- /hrdc/usage/digitizers.py: -------------------------------------------------------------------------------- 1 | from .usage import * 2 | 3 | Digitizer = Usage("digitizers.Digitizer", 0xd0001, CA) 4 | Pen = Usage("digitizers.Pen", 0xd0002, CA) 5 | LightPen = Usage("digitizers.LightPen", 0xd0003, CA) 6 | TouchScreen = Usage("digitizers.TouchScreen", 0xd0004, CA) 7 | TouchPad = Usage("digitizers.TouchPad", 0xd0005, CA) 8 | WhiteBoard = Usage("digitizers.WhiteBoard", 0xd0006, CA) 9 | CoordinateMeasuringMachine = Usage("digitizers.CoordinateMeasuringMachine", 0xd0007, CA) 10 | ThreeDDigitizer = Usage("digitizers.ThreeDDigitizer", 0xd0008, CA) 11 | StereoPlotter = Usage("digitizers.StereoPlotter", 0xd0009, CA) 12 | ArticulatedArm = Usage("digitizers.ArticulatedArm", 0xd000A, CA) 13 | Armature = Usage("digitizers.Armature", 0xd000B, CA) 14 | MultiplePointDigitizer = Usage("digitizers.MultiplePointDigitizer", 0xd000C, CA) 15 | FreeSpaceWand = Usage("digitizers.FreeSpaceWand", 0xd000D, CA) 16 | Stylus = Usage("digitizers.Stylus", 0xd0020, CL) 17 | Puck = Usage("digitizers.Puck", 0xd0021, CL) 18 | Finger = Usage("digitizers.Finger", 0xd0022, CL) 19 | TipPressure = Usage("digitizers.TipPressure", 0xd0030, DV) 20 | BarrelPressure = Usage("digitizers.BarrelPressure", 0xd0031, DV) 21 | InRange = Usage("digitizers.InRange", 0xd0032, MC) 22 | Touch = Usage("digitizers.Touch", 0xd0033, MC) 23 | Untouch = Usage("digitizers.Untouch", 0xd0034, OSC) 24 | Tap = Usage("digitizers.Tap", 0xd0035, OSC) 25 | Quality = Usage("digitizers.Quality", 0xd0036, DV) 26 | DataValid = Usage("digitizers.DataValid", 0xd0037, MC) 27 | TransducerIndex = Usage("digitizers.TransducerIndex", 0xd0038, DV) 28 | TabletFunctionKeys = Usage("digitizers.TabletFunctionKeys", 0xd0039, CL) 29 | ProgramChangeKeys = Usage("digitizers.ProgramChangeKeys", 0xd003A, CL) 30 | BatteryStrength = Usage("digitizers.BatteryStrength", 0xd003B, DV) 31 | Invert = Usage("digitizers.Invert", 0xd003C, MC) 32 | XTilt = Usage("digitizers.XTilt", 0xd003D, DV) 33 | YTilt = Usage("digitizers.YTilt", 0xd003E, DV) 34 | Azimuth = Usage("digitizers.Azimuth", 0xd003F, DV) 35 | Altitude = Usage("digitizers.Altitude", 0xd0040, DV) 36 | Twist = Usage("digitizers.Twist", 0xd0041, DV) 37 | TipSwitch = Usage("digitizers.TipSwitch", 0xd0042, MC) 38 | SecondaryTipSwitch = Usage("digitizers.SecondaryTipSwitch", 0xd0043, MC) 39 | BarrelSwitch = Usage("digitizers.BarrelSwitch", 0xd0044, MC) 40 | Eraser = Usage("digitizers.Eraser", 0xd0045, MC) 41 | TabletPick = Usage("digitizers.TabletPick", 0xd0046, MC) 42 | 43 | # HUTRR30 44 | TouchValid = Usage("digitizers.TouchValid", 0xd0047, MC) 45 | Width = Usage("digitizers.Width", 0xd0048, DV) 46 | Height = Usage("digitizers.Height", 0xd0049, DV) 47 | 48 | # HUTRR34 49 | Contactidentifier = Usage("digitizers.Contactidentifier", 0xd0051, DV) 50 | Deviceconfiguration = Usage("digitizers.Deviceconfiguration", 0xd000E, CA) 51 | Devicemode = Usage("digitizers.Devicemode", 0xd0052, DV) 52 | Deviceidentifier = Usage("digitizers.Deviceidentifier", 0xd0053, DV) 53 | Contactcount = Usage("digitizers.Contactcount", 0xd0054, DV) 54 | Contactcountmaximum = Usage("digitizers.Contactcountmaximum", 0xd0055, DV) 55 | Devicesettings = Usage("digitizers.Devicesettings", 0xd0023, CL) 56 | 57 | # HUTRR46 58 | SecondaryBarrelSwitch = Usage("digitizers.SecondaryBarrelSwitch", 0xd005A, MC) 59 | TransducerSerialNumber = Usage("digitizers.TransducerSerialNumber", 0xd005b, SV) 60 | 61 | # HUTRR53 62 | PreferredColor = Usage("digitizers.PreferredColor", 0xd005C, DV) 63 | -------------------------------------------------------------------------------- /hrdc/usage/game.py: -------------------------------------------------------------------------------- 1 | from .usage import * 2 | 3 | ThreeDGameController = Usage("game.ThreeDGameController", 0x50001, CA) 4 | PinballDevice = Usage("game.PinballDevice", 0x50002, CA) 5 | GunDevice = Usage("game.GunDevice", 0x50003, CA) 6 | PointofView = Usage("game.PointofView", 0x50020, CP) 7 | TurnRightLeft = Usage("game.TurnRightLeft", 0x50021, DV) 8 | PitchForwardBackward = Usage("game.PitchForwardBackward", 0x50022, DV) 9 | RollRightLeft = Usage("game.RollRightLeft", 0x50023, DV) 10 | MoveRightLeft = Usage("game.MoveRightLeft", 0x50024, DV) 11 | MoveForwardBackward = Usage("game.MoveForwardBackward", 0x50025, DV) 12 | MoveUpDown = Usage("game.MoveUpDown", 0x50026, DV) 13 | LeanRightLeft = Usage("game.LeanRightLeft", 0x50027, DV) 14 | LeanForwardBackward = Usage("game.LeanForwardBackward", 0x50028, DV) 15 | HeightofPOV = Usage("game.HeightofPOV", 0x50029, DV) 16 | Flipper = Usage("game.Flipper", 0x5002A, MC) 17 | SecondaryFlipper = Usage("game.SecondaryFlipper", 0x5002B, MC) 18 | Bump = Usage("game.Bump", 0x5002C, MC) 19 | NewGame = Usage("game.NewGame", 0x5002D, OSC) 20 | ShootBall = Usage("game.ShootBall", 0x5002E, OSC) 21 | Player = Usage("game.Player", 0x5002F, OSC) 22 | GunBolt = Usage("game.GunBolt", 0x50030, OOC) 23 | GunClip = Usage("game.GunClip", 0x50031, OOC) 24 | GunSelector = Usage("game.GunSelector", 0x50032, NAry) 25 | GunSingleShot = Usage("game.GunSingleShot", 0x50033, Sel) 26 | GunBurst = Usage("game.GunBurst", 0x50034, Sel) 27 | GunAutomatic = Usage("game.GunAutomatic", 0x50035, Sel) 28 | GunSafety = Usage("game.GunSafety", 0x50036, OOC) 29 | GamepadFireJump = Usage("game.GamepadFireJump", 0x50037, CL) 30 | GamepadTrigger = Usage("game.GamepadTrigger", 0x50039, CL) 31 | -------------------------------------------------------------------------------- /hrdc/usage/keyboard.py: -------------------------------------------------------------------------------- 1 | from .usage import * 2 | 3 | NoEvent = Usage("keyboard.NoEvent", 0x70000, Sel) 4 | Rollover = Usage("keyboard.Rollover", 0x70001, Sel) 5 | Postfail = Usage("keyboard.Postfail", 0x70002, Sel) 6 | Undefined = Usage("keyboard.Undefined", 0x70003, Sel) 7 | A = Usage("keyboard.A", 0x70004, Sel) 8 | B = Usage("keyboard.B", 0x70005, Sel) 9 | C = Usage("keyboard.C", 0x70006, Sel) 10 | D = Usage("keyboard.D", 0x70007, Sel) 11 | E = Usage("keyboard.E", 0x70008, Sel) 12 | F = Usage("keyboard.F", 0x70009, Sel) 13 | G = Usage("keyboard.G", 0x7000A, Sel) 14 | H = Usage("keyboard.H", 0x7000B, Sel) 15 | I = Usage("keyboard.I", 0x7000C, Sel) 16 | J = Usage("keyboard.J", 0x7000D, Sel) 17 | K = Usage("keyboard.K", 0x7000E, Sel) 18 | L = Usage("keyboard.L", 0x7000F, Sel) 19 | M = Usage("keyboard.M", 0x70010, Sel) 20 | N = Usage("keyboard.N", 0x70011, Sel) 21 | O = Usage("keyboard.O", 0x70012, Sel) 22 | P = Usage("keyboard.P", 0x70013, Sel) 23 | Q = Usage("keyboard.Q", 0x70014, Sel) 24 | R = Usage("keyboard.R", 0x70015, Sel) 25 | S = Usage("keyboard.S", 0x70016, Sel) 26 | T = Usage("keyboard.T", 0x70017, Sel) 27 | U = Usage("keyboard.U", 0x70018, Sel) 28 | V = Usage("keyboard.V", 0x70019, Sel) 29 | W = Usage("keyboard.W", 0x7001A, Sel) 30 | X = Usage("keyboard.X", 0x7001B, Sel) 31 | Y = Usage("keyboard.Y", 0x7001C, Sel) 32 | Z = Usage("keyboard.Z", 0x7001D, Sel) 33 | OneAndExclam = Usage("keyboard.OneAndExclam", 0x7001E, Sel) 34 | TwoAndAt = Usage("keyboard.TwoAndAt", 0x7001F, Sel) 35 | ThreeAndNumber = Usage("keyboard.ThreeAndNumber", 0x70020, Sel) 36 | FourAndDollar = Usage("keyboard.FourAndDollar", 0x70021, Sel) 37 | FiveAndPercent = Usage("keyboard.FiveAndPercent", 0x70022, Sel) 38 | SixAndCaret = Usage("keyboard.SixAndCaret", 0x70023, Sel) 39 | SevenAndAmp = Usage("keyboard.SevenAndAmp", 0x70024, Sel) 40 | EightAndStar = Usage("keyboard.EightAndStar", 0x70025, Sel) 41 | NineAndLeftParen = Usage("keyboard.NineAndLeftParen", 0x70026, Sel) 42 | ZeroAndRightParen = Usage("keyboard.ZeroAndRightParen", 0x70027, Sel) 43 | ReturnEnter = Usage("keyboard.ReturnEnter", 0x70028, Sel) 44 | Escape = Usage("keyboard.Escape", 0x70029, Sel) 45 | DeleteBackspace = Usage("keyboard.DeleteBackspace", 0x7002A, Sel) 46 | Tab = Usage("keyboard.Tab", 0x7002B, Sel) 47 | Spacebar = Usage("keyboard.Spacebar", 0x7002C, Sel) 48 | MinusAndUnderscore = Usage("keyboard.MinusAndUnderscore", 0x7002D, Sel) 49 | EqualAndPlus = Usage("keyboard.EqualAndPlus", 0x7002E, Sel) 50 | LeftBoxAndLeftCurly = Usage("keyboard.LeftBoxAndLeftCurly", 0x7002F, Sel) 51 | RightBoxAndRightCurly = Usage("keyboard.RightBoxAndRightCurly", 0x70030, Sel) 52 | BackslashAndPipe = Usage("keyboard.BackslashAndPipe", 0x70031, Sel) 53 | NonUsNumberAndTilde = Usage("keyboard.NonUsNumberAndTilde", 0x70032, Sel) 54 | SemicolonAndColon = Usage("keyboard.SemicolonAndColon", 0x70033, Sel) 55 | QuotAndDoubleQuot = Usage("keyboard.QuotAndDoubleQuot", 0x70034, Sel) 56 | GraveAccentAndTilde = Usage("keyboard.GraveAccentAndTilde", 0x70035, Sel) 57 | CommaAndLt = Usage("keyboard.CommaAndLt", 0x70036, Sel) 58 | PeriodAndGt = Usage("keyboard.PeriodAndGt", 0x70037, Sel) 59 | SlashAndQuestion = Usage("keyboard.SlashAndQuestion", 0x70038, Sel) 60 | CapsLock = Usage("keyboard.CapsLock", 0x70039, Sel) 61 | F1 = Usage("keyboard.F1", 0x7003A, Sel) 62 | F2 = Usage("keyboard.F2", 0x7003B, Sel) 63 | F3 = Usage("keyboard.F3", 0x7003C, Sel) 64 | F4 = Usage("keyboard.F4", 0x7003D, Sel) 65 | F5 = Usage("keyboard.F5", 0x7003E, Sel) 66 | F6 = Usage("keyboard.F6", 0x7003F, Sel) 67 | F7 = Usage("keyboard.F7", 0x70040, Sel) 68 | F8 = Usage("keyboard.F8", 0x70041, Sel) 69 | F9 = Usage("keyboard.F9", 0x70042, Sel) 70 | F10 = Usage("keyboard.F10", 0x70043, Sel) 71 | F11 = Usage("keyboard.F11", 0x70044, Sel) 72 | F12 = Usage("keyboard.F12", 0x70045, Sel) 73 | PrintScreen = Usage("keyboard.PrintScreen", 0x70046, Sel) 74 | ScrollLock = Usage("keyboard.ScrollLock", 0x70047, Sel) 75 | Pause = Usage("keyboard.Pause", 0x70048, Sel) 76 | Insert = Usage("keyboard.Insert", 0x70049, Sel) 77 | Home = Usage("keyboard.Home", 0x7004A, Sel) 78 | Pageup = Usage("keyboard.Pageup", 0x7004B, Sel) 79 | DeleteForward = Usage("keyboard.DeleteForward", 0x7004C, Sel) 80 | End = Usage("keyboard.End", 0x7004D, Sel) 81 | Pagedown = Usage("keyboard.Pagedown", 0x7004E, Sel) 82 | Rightarrow = Usage("keyboard.Rightarrow", 0x7004F, Sel) 83 | Leftarrow = Usage("keyboard.Leftarrow", 0x70050, Sel) 84 | Downarrow = Usage("keyboard.Downarrow", 0x70051, Sel) 85 | Uparrow = Usage("keyboard.Uparrow", 0x70052, Sel) 86 | KeypadNumLockAndClear = Usage("keyboard.KeypadNumLockAndClear", 0x70053, Sel) 87 | KeypadSlash = Usage("keyboard.KeypadSlash", 0x70054, Sel) 88 | KeypadStar = Usage("keyboard.KeypadStar", 0x70055, Sel) 89 | KeypadMinus = Usage("keyboard.KeypadMinus", 0x70056, Sel) 90 | KeypadPlus = Usage("keyboard.KeypadPlus", 0x70057, Sel) 91 | KeypadEnter = Usage("keyboard.KeypadEnter", 0x70058, Sel) 92 | Keypad1AndEnd = Usage("keyboard.Keypad1AndEnd", 0x70059, Sel) 93 | Keypad2AndDownArrow = Usage("keyboard.Keypad2AndDownArrow", 0x7005A, Sel) 94 | Keypad3AndPagedn = Usage("keyboard.Keypad3AndPagedn", 0x7005B, Sel) 95 | Keypad4AndLeftArrow = Usage("keyboard.Keypad4AndLeftArrow", 0x7005C, Sel) 96 | Keypad5 = Usage("keyboard.Keypad5", 0x7005D, Sel) 97 | Keypad6AndRightArrow = Usage("keyboard.Keypad6AndRightArrow", 0x7005E, Sel) 98 | Keypad7AndHome = Usage("keyboard.Keypad7AndHome", 0x7005F, Sel) 99 | Keypad8AndUpArrow = Usage("keyboard.Keypad8AndUpArrow", 0x70060, Sel) 100 | Keypad9AndPageup = Usage("keyboard.Keypad9AndPageup", 0x70061, Sel) 101 | Keypad0AndInsert = Usage("keyboard.Keypad0AndInsert", 0x70062, Sel) 102 | KeypadPointAndDelete = Usage("keyboard.KeypadPointAndDelete", 0x70063, Sel) 103 | NonUsBackslashAndPipe = Usage("keyboard.NonUsBackslashAndPipe", 0x70064, Sel) 104 | Application = Usage("keyboard.Application", 0x70065, Sel) 105 | Power = Usage("keyboard.Power", 0x70066, Sel) 106 | KeypadEqual = Usage("keyboard.KeypadEqual", 0x70067, Sel) 107 | F13 = Usage("keyboard.F13", 0x70068, Sel) 108 | F14 = Usage("keyboard.F14", 0x70069, Sel) 109 | F15 = Usage("keyboard.F15", 0x7006A, Sel) 110 | F16 = Usage("keyboard.F16", 0x7006B, Sel) 111 | F17 = Usage("keyboard.F17", 0x7006C, Sel) 112 | F18 = Usage("keyboard.F18", 0x7006D, Sel) 113 | F19 = Usage("keyboard.F19", 0x7006E, Sel) 114 | F20 = Usage("keyboard.F20", 0x7006F, Sel) 115 | F21 = Usage("keyboard.F21", 0x70070, Sel) 116 | F22 = Usage("keyboard.F22", 0x70071, Sel) 117 | F23 = Usage("keyboard.F23", 0x70072, Sel) 118 | F24 = Usage("keyboard.F24", 0x70073, Sel) 119 | Execute = Usage("keyboard.Execute", 0x70074, Sel) 120 | Help = Usage("keyboard.Help", 0x70075, Sel) 121 | Menu = Usage("keyboard.Menu", 0x70076, Sel) 122 | Select = Usage("keyboard.Select", 0x70077, Sel) 123 | Stop = Usage("keyboard.Stop", 0x70078, Sel) 124 | Again = Usage("keyboard.Again", 0x70079, Sel) 125 | Undo = Usage("keyboard.Undo", 0x7007A, Sel) 126 | Cut = Usage("keyboard.Cut", 0x7007B, Sel) 127 | Copy = Usage("keyboard.Copy", 0x7007C, Sel) 128 | Paste = Usage("keyboard.Paste", 0x7007D, Sel) 129 | Find = Usage("keyboard.Find", 0x7007E, Sel) 130 | Mute = Usage("keyboard.Mute", 0x7007F, Sel) 131 | VolumeUp = Usage("keyboard.VolumeUp", 0x70080, Sel) 132 | VolumeDown = Usage("keyboard.VolumeDown", 0x70081, Sel) 133 | LockingCapsLock = Usage("keyboard.LockingCapsLock", 0x70082, Sel) 134 | LockingNumLock = Usage("keyboard.LockingNumLock", 0x70083, Sel) 135 | LockingScrollLock = Usage("keyboard.LockingScrollLock", 0x70084, Sel) 136 | KeypadComma = Usage("keyboard.KeypadComma", 0x70085, Sel) 137 | KeypadEqualAs400 = Usage("keyboard.KeypadEqualAs400", 0x70086, Sel) 138 | International1 = Usage("keyboard.International1", 0x70087, Sel) 139 | International2 = Usage("keyboard.International2", 0x70088, Sel) 140 | International3 = Usage("keyboard.International3", 0x70089, Sel) 141 | International4 = Usage("keyboard.International4", 0x7008A, Sel) 142 | International5 = Usage("keyboard.International5", 0x7008B, Sel) 143 | International6 = Usage("keyboard.International6", 0x7008C, Sel) 144 | International7 = Usage("keyboard.International7", 0x7008D, Sel) 145 | International8 = Usage("keyboard.International8", 0x7008E, Sel) 146 | International9 = Usage("keyboard.International9", 0x7008F, Sel) 147 | Lang1 = Usage("keyboard.Lang1", 0x70090, Sel) 148 | Lang2 = Usage("keyboard.Lang2", 0x70091, Sel) 149 | Lang3 = Usage("keyboard.Lang3", 0x70092, Sel) 150 | Lang4 = Usage("keyboard.Lang4", 0x70093, Sel) 151 | Lang5 = Usage("keyboard.Lang5", 0x70094, Sel) 152 | Lang6 = Usage("keyboard.Lang6", 0x70095, Sel) 153 | Lang7 = Usage("keyboard.Lang7", 0x70096, Sel) 154 | Lang8 = Usage("keyboard.Lang8", 0x70097, Sel) 155 | Lang9 = Usage("keyboard.Lang9", 0x70098, Sel) 156 | AlternateErase = Usage("keyboard.AlternateErase", 0x70099, Sel) 157 | SysreqAttn = Usage("keyboard.SysreqAttn", 0x7009A, Sel) 158 | Cancel = Usage("keyboard.Cancel", 0x7009B, Sel) 159 | Clear = Usage("keyboard.Clear", 0x7009C, Sel) 160 | Prior = Usage("keyboard.Prior", 0x7009D, Sel) 161 | Return = Usage("keyboard.Return", 0x7009E, Sel) 162 | Separator = Usage("keyboard.Separator", 0x7009F, Sel) 163 | Out = Usage("keyboard.Out", 0x700A0, Sel) 164 | Oper = Usage("keyboard.Oper", 0x700A1, Sel) 165 | ClearAgain = Usage("keyboard.ClearAgain", 0x700A2, Sel) 166 | CrselProps = Usage("keyboard.CrselProps", 0x700A3, Sel) 167 | Exsel = Usage("keyboard.Exsel", 0x700A4, Sel) 168 | Keypad00 = Usage("keyboard.Keypad00", 0x700B0, Sel) 169 | Keypad000 = Usage("keyboard.Keypad000", 0x700B1, Sel) 170 | ThousandsSeparator = Usage("keyboard.ThousandsSeparator", 0x700B2, Sel) 171 | DecimalSeparator = Usage("keyboard.DecimalSeparator", 0x700B3, Sel) 172 | CurrencyUnit = Usage("keyboard.CurrencyUnit", 0x700B4, Sel) 173 | CurrencySubMinusUnit = Usage("keyboard.CurrencySubMinusUnit", 0x700B5, Sel) 174 | KeypadLeftParen = Usage("keyboard.KeypadLeftParen", 0x700B6, Sel) 175 | KeypadRightParen = Usage("keyboard.KeypadRightParen", 0x700B7, Sel) 176 | KeypadLeftCurly = Usage("keyboard.KeypadLeftCurly", 0x700B8, Sel) 177 | KeypadRightCurly = Usage("keyboard.KeypadRightCurly", 0x700B9, Sel) 178 | KeypadTab = Usage("keyboard.KeypadTab", 0x700BA, Sel) 179 | KeypadBackspace = Usage("keyboard.KeypadBackspace", 0x700BB, Sel) 180 | KeypadA = Usage("keyboard.KeypadA", 0x700BC, Sel) 181 | KeypadB = Usage("keyboard.KeypadB", 0x700BD, Sel) 182 | KeypadC = Usage("keyboard.KeypadC", 0x700BE, Sel) 183 | KeypadD = Usage("keyboard.KeypadD", 0x700BF, Sel) 184 | KeypadE = Usage("keyboard.KeypadE", 0x700C0, Sel) 185 | KeypadF = Usage("keyboard.KeypadF", 0x700C1, Sel) 186 | KeypadXor = Usage("keyboard.KeypadXor", 0x700C2, Sel) 187 | KeypadCaret = Usage("keyboard.KeypadCaret", 0x700C3, Sel) 188 | KeypadPercent = Usage("keyboard.KeypadPercent", 0x700C4, Sel) 189 | KeypadLt = Usage("keyboard.KeypadLt", 0x700C5, Sel) 190 | KeypadGt = Usage("keyboard.KeypadGt", 0x700C6, Sel) 191 | KeypadAmp = Usage("keyboard.KeypadAmp", 0x700C7, Sel) 192 | KeypadAmpAmp = Usage("keyboard.KeypadAmpAmp", 0x700C8, Sel) 193 | KeypadPipe = Usage("keyboard.KeypadPipe", 0x700C9, Sel) 194 | KeypadPipePipe = Usage("keyboard.KeypadPipePipe", 0x700CA, Sel) 195 | KeypadColon = Usage("keyboard.KeypadColon", 0x700CB, Sel) 196 | KeypadNumber = Usage("keyboard.KeypadNumber", 0x700CC, Sel) 197 | KeypadSpace = Usage("keyboard.KeypadSpace", 0x700CD, Sel) 198 | KeypadAt = Usage("keyboard.KeypadAt", 0x700CE, Sel) 199 | KeypadExclam = Usage("keyboard.KeypadExclam", 0x700CF, Sel) 200 | KeypadMemoryStore = Usage("keyboard.KeypadMemoryStore", 0x700D0, Sel) 201 | KeypadMemoryRecall = Usage("keyboard.KeypadMemoryRecall", 0x700D1, Sel) 202 | KeypadMemoryClear = Usage("keyboard.KeypadMemoryClear", 0x700D2, Sel) 203 | KeypadMemoryAdd = Usage("keyboard.KeypadMemoryAdd", 0x700D3, Sel) 204 | KeypadMemorySubtract = Usage("keyboard.KeypadMemorySubtract", 0x700D4, Sel) 205 | KeypadMemoryMultiply = Usage("keyboard.KeypadMemoryMultiply", 0x700D5, Sel) 206 | KeypadMemoryDivide = Usage("keyboard.KeypadMemoryDivide", 0x700D6, Sel) 207 | KeypadPlusMinus = Usage("keyboard.KeypadPlusMinus", 0x700D7, Sel) 208 | KeypadClear = Usage("keyboard.KeypadClear", 0x700D8, Sel) 209 | KeypadClearEntry = Usage("keyboard.KeypadClearEntry", 0x700D9, Sel) 210 | KeypadBinary = Usage("keyboard.KeypadBinary", 0x700DA, Sel) 211 | KeypadOctal = Usage("keyboard.KeypadOctal", 0x700DB, Sel) 212 | KeypadDecimal = Usage("keyboard.KeypadDecimal", 0x700DC, Sel) 213 | KeypadHexadecimal = Usage("keyboard.KeypadHexadecimal", 0x700DD, Sel) 214 | LeftControl = Usage("keyboard.LeftControl", 0x700E0, DV) 215 | LeftShift = Usage("keyboard.LeftShift", 0x700E1, DV) 216 | LeftAlt = Usage("keyboard.LeftAlt", 0x700E2, DV) 217 | LeftGui = Usage("keyboard.LeftGui", 0x700E3, DV) 218 | RightControl = Usage("keyboard.RightControl", 0x700E4, DV) 219 | RightShift = Usage("keyboard.RightShift", 0x700E5, DV) 220 | RightAlt = Usage("keyboard.RightAlt", 0x700E6, DV) 221 | RightGui = Usage("keyboard.RightGui", 0x700E7, DV) 222 | -------------------------------------------------------------------------------- /hrdc/usage/led.py: -------------------------------------------------------------------------------- 1 | from .usage import * 2 | 3 | NumLock = Usage("led.NumLock", 0x80001, OOC) 4 | CapsLock = Usage("led.CapsLock", 0x80002, OOC) 5 | ScrollLock = Usage("led.ScrollLock", 0x80003, OOC) 6 | Compose = Usage("led.Compose", 0x80004, OOC) 7 | Kana = Usage("led.Kana", 0x80005, OOC) 8 | Power = Usage("led.Power", 0x80006, OOC) 9 | Shift = Usage("led.Shift", 0x80007, OOC) 10 | DoNotDisturb = Usage("led.DoNotDisturb", 0x80008, OOC) 11 | Mute = Usage("led.Mute", 0x80009, OOC) 12 | ToneEnable = Usage("led.ToneEnable", 0x8000A, OOC) 13 | HighCutFilter = Usage("led.HighCutFilter", 0x8000B, OOC) 14 | LowCutFilter = Usage("led.LowCutFilter", 0x8000C, OOC) 15 | EqualizerEnable = Usage("led.EqualizerEnable", 0x8000D, OOC) 16 | SoundFieldOn = Usage("led.SoundFieldOn", 0x8000E, OOC) 17 | SurroundOn = Usage("led.SurroundOn", 0x8000F, OOC) 18 | Repeat = Usage("led.Repeat", 0x80010, OOC) 19 | Stereo = Usage("led.Stereo", 0x80011, OOC) 20 | SamplingRateDetect = Usage("led.SamplingRateDetect", 0x80012, OOC) 21 | Spinning = Usage("led.Spinning", 0x80013, OOC) 22 | CAV = Usage("led.CAV", 0x80014, OOC) 23 | CLV = Usage("led.CLV", 0x80015, OOC) 24 | RecordingFormatDetect = Usage("led.RecordingFormatDetect", 0x80016, OOC) 25 | OffHook = Usage("led.OffHook", 0x80017, OOC) 26 | Ring = Usage("led.Ring", 0x80018, OOC) 27 | MessageWaiting = Usage("led.MessageWaiting", 0x80019, OOC) 28 | DataMode = Usage("led.DataMode", 0x8001A, OOC) 29 | BatteryOperation = Usage("led.BatteryOperation", 0x8001B, OOC) 30 | BatteryOK = Usage("led.BatteryOK", 0x8001C, OOC) 31 | BatteryLow = Usage("led.BatteryLow", 0x8001D, OOC) 32 | Speaker = Usage("led.Speaker", 0x8001E, OOC) 33 | HeadSet = Usage("led.HeadSet", 0x8001F, OOC) 34 | Hold = Usage("led.Hold", 0x80020, OOC) 35 | Microphone = Usage("led.Microphone", 0x80021, OOC) 36 | Coverage = Usage("led.Coverage", 0x80022, OOC) 37 | NightMode = Usage("led.NightMode", 0x80023, OOC) 38 | SendCalls = Usage("led.SendCalls", 0x80024, OOC) 39 | CallPickup = Usage("led.CallPickup", 0x80025, OOC) 40 | Conference = Usage("led.Conference", 0x80026, OOC) 41 | StandBy = Usage("led.StandBy", 0x80027, OOC) 42 | CameraOn = Usage("led.CameraOn", 0x80028, OOC) 43 | CameraOff = Usage("led.CameraOff", 0x80029, OOC) 44 | OnLine = Usage("led.OnLine", 0x8002A, OOC) 45 | OffLine = Usage("led.OffLine", 0x8002B, OOC) 46 | Busy = Usage("led.Busy", 0x8002C, OOC) 47 | Ready = Usage("led.Ready", 0x8002D, OOC) 48 | PaperOut = Usage("led.PaperOut", 0x8002E, OOC) 49 | PaperJam = Usage("led.PaperJam", 0x8002F, OOC) 50 | Remote = Usage("led.Remote", 0x80030, OOC) 51 | Forward = Usage("led.Forward", 0x80031, OOC) 52 | Reverse = Usage("led.Reverse", 0x80032, OOC) 53 | Stop = Usage("led.Stop", 0x80033, OOC) 54 | Rewind = Usage("led.Rewind", 0x80034, OOC) 55 | FastForward = Usage("led.FastForward", 0x80035, OOC) 56 | Play = Usage("led.Play", 0x80036, OOC) 57 | Pause = Usage("led.Pause", 0x80037, OOC) 58 | Record = Usage("led.Record", 0x80038, OOC) 59 | Error = Usage("led.Error", 0x80039, OOC) 60 | UsageSelectedIndicator = Usage("led.UsageSelectedIndicator", 0x8003A, US) 61 | UsageInUseIndicator = Usage("led.UsageInUseIndicator", 0x8003B, US) 62 | UsageMultiModeIndicator = Usage("led.UsageMultiModeIndicator", 0x8003C, UM) 63 | IndicatorOn = Usage("led.IndicatorOn", 0x8003D, Sel) 64 | IndicatorFlash = Usage("led.IndicatorFlash", 0x8003E, Sel) 65 | IndicatorSlowBlink = Usage("led.IndicatorSlowBlink", 0x8003F, Sel) 66 | IndicatorFastBlink = Usage("led.IndicatorFastBlink", 0x80040, Sel) 67 | IndicatorOff = Usage("led.IndicatorOff", 0x80041, Sel) 68 | FlashOnTime = Usage("led.FlashOnTime", 0x80042, DV) 69 | SlowBlinkOnTime = Usage("led.SlowBlinkOnTime", 0x80043, DV) 70 | SlowBlinkOffTime = Usage("led.SlowBlinkOffTime", 0x80044, DV) 71 | FastBlinkOnTime = Usage("led.FastBlinkOnTime", 0x80045, DV) 72 | FastBlinkOffTime = Usage("led.FastBlinkOffTime", 0x80046, DV) 73 | UsageIndicatorColor = Usage("led.UsageIndicatorColor", 0x80047, UM) 74 | IndicatorRed = Usage("led.IndicatorRed", 0x80048, Sel) 75 | IndicatorAmber = Usage("led.IndicatorAmber", 0x8004A, Sel) 76 | SystemSuspend = Usage("led.SystemSuspend", 0x8004C, OOC) 77 | ExternalPowerConnected = Usage("led.ExternalPowerConnected", 0x8004D, OOC) 78 | 79 | # HUTRR47 80 | PlayerIndicator = Usage("led.PlayerIndicator", 0x8004e, NAry) 81 | Player1 = Usage("led.Player1", 0x8004f, Sel) 82 | Player2 = Usage("led.Player2", 0x80050, Sel) 83 | Player3 = Usage("led.Player3", 0x80051, Sel) 84 | Player4 = Usage("led.Player4", 0x80052, Sel) 85 | Player5 = Usage("led.Player5", 0x80053, Sel) 86 | Player6 = Usage("led.Player6", 0x80054, Sel) 87 | Player7 = Usage("led.Player7", 0x80055, Sel) 88 | Player8 = Usage("led.Player8", 0x80056, Sel) 89 | -------------------------------------------------------------------------------- /hrdc/usage/lighting.py: -------------------------------------------------------------------------------- 1 | from .usage import * 2 | 3 | # HUTRR84 4 | 5 | LampArray = Usage("lighting.LampArray", 0x590001, CA) 6 | LampArrayAttributesReport = Usage("lighting.LampArrayAttributesReport", 0x590002, CL) 7 | LampCount = Usage("lighting.LampCount", 0x590003, SV) 8 | BoundingBoxWidthInMicrometers = Usage("lighting.BoundingBoxWidthInMicrometers", 0x590004, SV) 9 | BoundingBoxHeightInMicrometers = Usage("lighting.BoundingBoxHeightInMicrometers", 0x590005, SV) 10 | BoundingBoxDepthInMicrometers = Usage("lighting.BoundingBoxDepthInMicrometers", 0x590006, SV) 11 | LampArrayKind = Usage("lighting.LampArrayKind", 0x590007, SV) 12 | MinUpdateIntervalInMicroseconds = Usage("lighting. MinUpdateIntervalInMicroseconds", 0x590008, SV) 13 | LampAttributesRequestReport = Usage("lighting.LampAttributesRequestReport", 0x590020, CL) 14 | LampId = Usage("lighting.LampId", 0x590021, SV) 15 | LampAttributesResponseReport = Usage("lighting.LampAttributesResponseReport", 0x590022, CL) 16 | PositionXInMicrometers = Usage("lighting.PositionXInMicrometers", 0x590023, DV) 17 | PositionYInMicrometers = Usage("lighting.PositionYInMicrometers", 0x590024, DV) 18 | PositionZInMicrometers = Usage("lighting.PositionZInMicrometers", 0x590025, DV) 19 | LampPurposes = Usage("lighting.LampPurposes", 0x590026, DV) 20 | UpdateLatencyInMicroseconds = Usage("lighting.UpdateLatencyInMicroseconds", 0x590027, DV) 21 | RedLevelCount = Usage("lighting.RedLevelCount", 0x590028, DV) 22 | GreenLevelCount = Usage("lighting.GreenLevelCount", 0x590029, DV) 23 | BlueLevelCount = Usage("lighting.BlueLevelCount", 0x59002A, DV) 24 | IntensityLevelCount = Usage("lighting.IntensityLevelCount", 0x59002B, DV) 25 | IsProgrammable = Usage("lighting.IsProgrammable", 0x59002C, DV) 26 | InputBinding = Usage("lighting.InputBinding", 0x59002D, DV) 27 | LampMultiUpdateReport = Usage("lighting.LampMultiUpdateReport", 0x590050, CL) 28 | RedUpdateChannel = Usage("lighting.RedUpdateChannel", 0x590051, DV) 29 | GreenUpdateChannel = Usage("lighting.GreenUpdateChannel", 0x590052, DV) 30 | BlueUpdateChannel = Usage("lighting.BlueUpdateChannel", 0x590053, DV) 31 | IntensityUpdateChannel = Usage("lighting.IntensityUpdateChannel", 0x590054, DV) 32 | LampUpdateFlags = Usage("lighting.LampUpdateFlags", 0x590055, DV) 33 | LampRangeUpdateReport = Usage("lighting.LampRangeUpdateReport", 0x590060, CL) 34 | LampIdStart = Usage("lighting.LampIdStart", 0x590061, DV) 35 | LampIdEnd = Usage("lighting.LampIdEnd", 0x590062, DV) 36 | LampArrayControlReport = Usage("lighting.LampArrayControlReport", 0x590070, CL) 37 | AutonomousMode = Usage("lighting.AutonomousMode", 0x590071, DV) 38 | -------------------------------------------------------------------------------- /hrdc/usage/ordinal.py: -------------------------------------------------------------------------------- 1 | from .usage import * 2 | 3 | def Ordinal(n): 4 | return Usage("ordinal.Ordinal(%d)" % int(n), 0xa0000 + int(n), CL) 5 | 6 | for i in range(128): 7 | Ordinal(i) 8 | -------------------------------------------------------------------------------- /hrdc/usage/pid.py: -------------------------------------------------------------------------------- 1 | from .usage import * 2 | 3 | PhysicalInterfaceDevice = Usage("pid.PhysicalInterfaceDevice", 0xf0001, CA) 4 | Normal = Usage("pid.Normal", 0xf0020, DV) 5 | SetEffectReport = Usage("pid.SetEffectReport", 0xf0021, CL) 6 | EffectBlockIndex = Usage("pid.EffectBlockIndex", 0xf0022, DV) 7 | ParameterBlockOffset = Usage("pid.ParameterBlockOffset", 0xf0023, DV) 8 | ROMFlag = Usage("pid.ROMFlag", 0xf0024, DF) 9 | EffectType = Usage("pid.EffectType", 0xf0025, NAry) 10 | ETConstantForce = Usage("pid.ETConstantForce", 0xf0026, Sel) 11 | ETRamp = Usage("pid.ETRamp", 0xf0027, Sel) 12 | ETCustomForceData = Usage("pid.ETCustomForceData", 0xf0028, Sel) 13 | ETSquare = Usage("pid.ETSquare", 0xf0030, Sel) 14 | ETSine = Usage("pid.ETSine", 0xf0031, Sel) 15 | ETTriangle = Usage("pid.ETTriangle", 0xf0032, Sel) 16 | ETSawtoothUp = Usage("pid.ETSawtoothUp", 0xf0033, Sel) 17 | ETSawtoothDown = Usage("pid.ETSawtoothDown", 0xf0034, Sel) 18 | ETSpring = Usage("pid.ETSpring", 0xf0040, Sel) 19 | ETDamper = Usage("pid.ETDamper", 0xf0041, Sel) 20 | ETInertia = Usage("pid.ETInertia", 0xf0042, Sel) 21 | ETFriction = Usage("pid.ETFriction", 0xf0043, Sel) 22 | Duration = Usage("pid.Duration", 0xf0050, DV) 23 | SamplePeriod = Usage("pid.SamplePeriod", 0xf0051, DV) 24 | Gain = Usage("pid.Gain", 0xf0052, DV) 25 | TriggerButton = Usage("pid.TriggerButton", 0xf0053, DV) 26 | TriggerRepeatInterval = Usage("pid.TriggerRepeatInterval", 0xf0054, DV) 27 | AxesEnable = Usage("pid.AxesEnable", 0xf0055, US) 28 | DirectionEnable = Usage("pid.DirectionEnable", 0xf0056, DF) 29 | Direction = Usage("pid.Direction", 0xf0057, CL) 30 | TypeSpecificBlockOffset = Usage("pid.TypeSpecificBlockOffset", 0xf0058, CL) 31 | BlockType = Usage("pid.BlockType", 0xf0059, NAry) 32 | SetEnvelopeReport = Usage("pid.SetEnvelopeReport", 0xf005A, CL) 33 | AttackLevel = Usage("pid.AttackLevel", 0xf005B, DV) 34 | AttackTime = Usage("pid.AttackTime", 0xf005C, DV) 35 | FadeLevel = Usage("pid.FadeLevel", 0xf005D, DV) 36 | FadeTime = Usage("pid.FadeTime", 0xf005E, DV) 37 | SetConditionReport = Usage("pid.SetConditionReport", 0xf005F, CL) 38 | CPOffset = Usage("pid.CPOffset", 0xf0060, DV) 39 | PositiveCoefficient = Usage("pid.PositiveCoefficient", 0xf0061, DV) 40 | NegativeCoefficient = Usage("pid.NegativeCoefficient", 0xf0062, DV) 41 | PositiveSaturation = Usage("pid.PositiveSaturation", 0xf0063, DV) 42 | NegativeSaturation = Usage("pid.NegativeSaturation", 0xf0064, DV) 43 | DeadBand = Usage("pid.DeadBand", 0xf0065, DV) 44 | DownloadForceSample = Usage("pid.DownloadForceSample", 0xf0066, CL) 45 | IsochCustomForceEnable = Usage("pid.IsochCustomForceEnable", 0xf0067, CL) 46 | CustomForceDataReport = Usage("pid.CustomForceDataReport", 0xf0068, CL) 47 | CustomForceData = Usage("pid.CustomForceData", 0xf0069, DV) 48 | CustomForceVendorDefinedData = Usage("pid.CustomForceVendorDefinedData", 0xf006A, DV) 49 | SetCustomForceReport = Usage("pid.SetCustomForceReport", 0xf006B, CL) 50 | CustomForceDataOffset = Usage("pid.CustomForceDataOffset", 0xf006C, DV) 51 | SampleCount = Usage("pid.SampleCount", 0xf006D, DV) 52 | SetPeriodicReport = Usage("pid.SetPeriodicReport", 0xf006E, CL) 53 | Offset = Usage("pid.Offset", 0xf006F, DV) 54 | Magnitude = Usage("pid.Magnitude", 0xf0070, DV) 55 | Phase = Usage("pid.Phase", 0xf0071, DV) 56 | Period = Usage("pid.Period", 0xf0072, DV) 57 | SetConstantForceReport = Usage("pid.SetConstantForceReport", 0xf0073, CL) 58 | SetRampForceReport = Usage("pid.SetRampForceReport", 0xf0074, CL) 59 | RampStart = Usage("pid.RampStart", 0xf0075, DV) 60 | RampEnd = Usage("pid.RampEnd", 0xf0076, DV) 61 | EffectOperationReport = Usage("pid.EffectOperationReport", 0xf0077, CL) 62 | EffectOperation = Usage("pid.EffectOperation", 0xf0078, NAry) 63 | OpEffectStart = Usage("pid.OpEffectStart", 0xf0079, Sel) 64 | OpEffectStartSolo = Usage("pid.OpEffectStartSolo", 0xf007A, Sel) 65 | OpEffectStop = Usage("pid.OpEffectStop", 0xf007B, Sel) 66 | LoopCount = Usage("pid.LoopCount", 0xf007C, DV) 67 | DeviceGainReport = Usage("pid.DeviceGainReport", 0xf007D, CL) 68 | DeviceGain = Usage("pid.DeviceGain", 0xf007E, DV) 69 | PoolReport = Usage("pid.PoolReport", 0xf007F, CL) 70 | RAMPoolSize = Usage("pid.RAMPoolSize", 0xf0080, DV) 71 | ROMPoolSize = Usage("pid.ROMPoolSize", 0xf0081, SV) 72 | ROMEffectBlockCount = Usage("pid.ROMEffectBlockCount", 0xf0082, SV) 73 | SimultaneousEffectsMax = Usage("pid.SimultaneousEffectsMax", 0xf0083, SV) 74 | PoolAlignment = Usage("pid.PoolAlignment", 0xf0084, SV) 75 | PoolMoveReport = Usage("pid.PoolMoveReport", 0xf0085, CL) 76 | MoveSource = Usage("pid.MoveSource", 0xf0086, DV) 77 | MoveDestination = Usage("pid.MoveDestination", 0xf0087, DV) 78 | MoveLength = Usage("pid.MoveLength", 0xf0088, DV) 79 | BlockLoadReport = Usage("pid.BlockLoadReport", 0xf0089, CL) 80 | BlockLoadStatus = Usage("pid.BlockLoadStatus", 0xf008B, NAry) 81 | BlockLoadSuccess = Usage("pid.BlockLoadSuccess", 0xf008C, Sel) 82 | BlockLoadFull = Usage("pid.BlockLoadFull", 0xf008D, Sel) 83 | BlockLoadError = Usage("pid.BlockLoadError", 0xf008E, Sel) 84 | BlockHandle = Usage("pid.BlockHandle", 0xf008F, DV) 85 | BlockFreeReport = Usage("pid.BlockFreeReport", 0xf0090, CL) 86 | TypeSpecificBlockHandle = Usage("pid.TypeSpecificBlockHandle", 0xf0091, DV) 87 | StateReport = Usage("pid.StateReport", 0xf0092, CL) 88 | EffectPlaying = Usage("pid.EffectPlaying", 0xf0094, DF) 89 | DeviceControlReport = Usage("pid.DeviceControlReport", 0xf0095, CL) 90 | DeviceControl = Usage("pid.DeviceControl", 0xf0096, NAry) 91 | DCEnableActuators = Usage("pid.DCEnableActuators", 0xf0097, Sel) 92 | DCDisableActuators = Usage("pid.DCDisableActuators", 0xf0098, Sel) 93 | DCStopAllEffects = Usage("pid.DCStopAllEffects", 0xf0099, Sel) 94 | DCDeviceReset = Usage("pid.DCDeviceReset", 0xf009A, Sel) 95 | DCDevicePause = Usage("pid.DCDevicePause", 0xf009B, Sel) 96 | DCDeviceContinue = Usage("pid.DCDeviceContinue", 0xf009C, Sel) 97 | DevicePaused = Usage("pid.DevicePaused", 0xf009F, DF) 98 | ActuatorsEnabled = Usage("pid.ActuatorsEnabled", 0xf00A0, DF) 99 | SafetySwitch = Usage("pid.SafetySwitch", 0xf00A4, DF) 100 | ActuatorOverrideSwitch = Usage("pid.ActuatorOverrideSwitch", 0xf00A5, DF) 101 | ActuatorPower = Usage("pid.ActuatorPower", 0xf00A6, OOC) 102 | StartDelay = Usage("pid.StartDelay", 0xf00A7, DV) 103 | ParameterBlockSize = Usage("pid.ParameterBlockSize", 0xf00A8, CL) 104 | DeviceManagedPool = Usage("pid.DeviceManagedPool", 0xf00A9, SF) 105 | SharedParameterBlocks = Usage("pid.SharedParameterBlocks", 0xf00AA, SF) 106 | CreateNewEffectReport = Usage("pid.CreateNewEffectReport", 0xf00AB, CL) 107 | RAMPoolAvailable = Usage("pid.RAMPoolAvailable", 0xf00AC, DV) 108 | -------------------------------------------------------------------------------- /hrdc/usage/sensors.py: -------------------------------------------------------------------------------- 1 | from .usage import * 2 | 3 | Sensor = Usage("sensors.Sensor", 0x200001, CA, CP) 4 | Biometric = Usage("sensors.Biometric", 0x200010, CA, CP) 5 | BiometricHumanPresence = Usage("sensors.BiometricHumanPresence", 0x200011, CA, CP) 6 | BiometricHumanProximity = Usage("sensors.BiometricHumanProximity", 0x200012, CA, CP) 7 | BiometricHumanTouch = Usage("sensors.BiometricHumanTouch", 0x200013, CA, CP) 8 | Electrical = Usage("sensors.Electrical", 0x200020, CA, CP) 9 | ElectricalCapacitance = Usage("sensors.ElectricalCapacitance", 0x200021, CA, CP) 10 | ElectricalCurrent = Usage("sensors.ElectricalCurrent", 0x200022, CA, CP) 11 | ElectricalPower = Usage("sensors.ElectricalPower", 0x200023, CA, CP) 12 | ElectricalInductance = Usage("sensors.ElectricalInductance", 0x200024, CA, CP) 13 | ElectricalResistance = Usage("sensors.ElectricalResistance", 0x200025, CA, CP) 14 | ElectricalVoltage = Usage("sensors.ElectricalVoltage", 0x200026, CA, CP) 15 | ElectricalPotentiometer = Usage("sensors.ElectricalPotentiometer", 0x200027, CA, CP) 16 | ElectricalFrequency = Usage("sensors.ElectricalFrequency", 0x200028, CA, CP) 17 | ElectricalPeriod = Usage("sensors.ElectricalPeriod", 0x200029, CA, CP) 18 | Environmental = Usage("sensors.Environmental", 0x200030, CA, CP) 19 | EnvironmentalAtmosphericPressure = Usage("sensors.EnvironmentalAtmosphericPressure", 0x200031, CA, CP) 20 | EnvironmentalHumidity = Usage("sensors.EnvironmentalHumidity", 0x200032, CA, CP) 21 | EnvironmentalTemperature = Usage("sensors.EnvironmentalTemperature", 0x200033, CA, CP) 22 | EnvironmentalWindDirection = Usage("sensors.EnvironmentalWindDirection", 0x200034, CA, CP) 23 | EnvironmentalWindSpeed = Usage("sensors.EnvironmentalWindSpeed", 0x200035, CA, CP) 24 | Light = Usage("sensors.Light", 0x200040, CA, CP) 25 | LightAmbientLight = Usage("sensors.LightAmbientLight", 0x200041, CA, CP) 26 | LightConsumerInfrared = Usage("sensors.LightConsumerInfrared", 0x200042, CA, CP) 27 | Location = Usage("sensors.Location", 0x200050, CA, CP) 28 | LocationBroadcast = Usage("sensors.LocationBroadcast", 0x200051, CA, CP) 29 | LocationDeadReckoning = Usage("sensors.LocationDeadReckoning", 0x200052, CA, CP) 30 | LocationGPS = Usage("sensors.LocationGPS", 0x200053, CA, CP) 31 | LocationLookup = Usage("sensors.LocationLookup", 0x200054, CA, CP) 32 | LocationOther = Usage("sensors.LocationOther", 0x200055, CA, CP) 33 | LocationStatic = Usage("sensors.LocationStatic", 0x200056, CA, CP) 34 | LocationTriangulation = Usage("sensors.LocationTriangulation", 0x200057, CA, CP) 35 | Mechanical = Usage("sensors.Mechanical", 0x200060, CA, CP) 36 | MechanicalBooleanSwitch = Usage("sensors.MechanicalBooleanSwitch", 0x200061, CA, CP) 37 | MechanicalBooleanSwitchArray = Usage("sensors.MechanicalBooleanSwitchArray", 0x200062, CA, CP) 38 | MechanicalMultivalueSwitch = Usage("sensors.MechanicalMultivalueSwitch", 0x200063, CA, CP) 39 | MechanicalForce = Usage("sensors.MechanicalForce", 0x200064, CA, CP) 40 | MechanicalPressure = Usage("sensors.MechanicalPressure", 0x200065, CA, CP) 41 | MechanicalStrain = Usage("sensors.MechanicalStrain", 0x200066, CA, CP) 42 | MechanicalWeight = Usage("sensors.MechanicalWeight", 0x200067, CA, CP) 43 | MechanicalHapticVibrator = Usage("sensors.MechanicalHapticVibrator", 0x200068, CA, CP) 44 | MechanicalHallEffectSwitch = Usage("sensors.MechanicalHallEffectSwitch", 0x200069, CA, CP) 45 | Motion = Usage("sensors.Motion", 0x200070, CA, CP) 46 | MotionAccelerometer1D = Usage("sensors.MotionAccelerometer1D", 0x200071, CA, CP) 47 | MotionAccelerometer2D = Usage("sensors.MotionAccelerometer2D", 0x200072, CA, CP) 48 | MotionAccelerometer3D = Usage("sensors.MotionAccelerometer3D", 0x200073, CA, CP) 49 | MotionGyrometer1D = Usage("sensors.MotionGyrometer1D", 0x200074, CA, CP) 50 | MotionGyrometer2D = Usage("sensors.MotionGyrometer2D", 0x200075, CA, CP) 51 | MotionGyrometer3D = Usage("sensors.MotionGyrometer3D", 0x200076, CA, CP) 52 | MotionMotionDetector = Usage("sensors.MotionMotionDetector", 0x200077, CA, CP) 53 | MotionSpeedometer = Usage("sensors.MotionSpeedometer", 0x200078, CA, CP) 54 | MotionAccelerometer = Usage("sensors.MotionAccelerometer", 0x200079, CA, CP) 55 | MotionGyrometer = Usage("sensors.MotionGyrometer", 0x20007A, CA, CP) 56 | Orientation = Usage("sensors.Orientation", 0x200080, CA, CP) 57 | OrientationCompass1D = Usage("sensors.OrientationCompass1D", 0x200081, CA, CP) 58 | OrientationCompass2D = Usage("sensors.OrientationCompass2D", 0x200082, CA, CP) 59 | OrientationCompass3D = Usage("sensors.OrientationCompass3D", 0x200083, CA, CP) 60 | OrientationInclinometer1D = Usage("sensors.OrientationInclinometer1D", 0x200084, CA, CP) 61 | OrientationInclinometer2D = Usage("sensors.OrientationInclinometer2D", 0x200085, CA, CP) 62 | OrientationInclinometer3D = Usage("sensors.OrientationInclinometer3D", 0x200086, CA, CP) 63 | OrientationDistance1D = Usage("sensors.OrientationDistance1D", 0x200087, CA, CP) 64 | OrientationDistance2D = Usage("sensors.OrientationDistance2D", 0x200088, CA, CP) 65 | OrientationDistance3D = Usage("sensors.OrientationDistance3D", 0x200089, CA, CP) 66 | OrientationDeviceOrientation = Usage("sensors.OrientationDeviceOrientation", 0x20008A, CA, CP) 67 | OrientationCompass = Usage("sensors.OrientationCompass", 0x20008B, CA, CP) 68 | OrientationInclinometer = Usage("sensors.OrientationInclinometer", 0x20008C, CA, CP) 69 | OrientationDistance = Usage("sensors.OrientationDistance", 0x20008D, CA, CP) 70 | Scanner = Usage("sensors.Scanner", 0x200090, CA, CP) 71 | ScannerBarcode = Usage("sensors.ScannerBarcode", 0x200091, CA, CP) 72 | ScannerRFID = Usage("sensors.ScannerRFID", 0x200092, CA, CP) 73 | ScannerNFC = Usage("sensors.ScannerNFC", 0x200093, CA, CP) 74 | Time = Usage("sensors.Time", 0x2000A0, CA, CP) 75 | TimeAlarmTimer = Usage("sensors.TimeAlarmTimer", 0x2000A1, CA, CP) 76 | TimeRealTimeClock = Usage("sensors.TimeRealTimeClock", 0x2000A2, CA, CP) 77 | Other = Usage("sensors.Other", 0x2000E0, CA, CP) 78 | OtherCustom = Usage("sensors.OtherCustom", 0x2000E1, CA, CP) 79 | OtherGeneric = Usage("sensors.OtherGeneric", 0x2000E2, CA, CP) 80 | OtherGenericEnumerator = Usage("sensors.OtherGenericEnumerator", 0x2000E3, CA, CP) 81 | 82 | Event = Usage("sensors.Event", 0x200200) 83 | EventSensorState = Usage("sensors.EventSensorState", 0x200201, NAry) 84 | SensorStateUndefined = Usage("sensors.SensorStateUndefined", 0x200800, Sel) 85 | SensorStateReady = Usage("sensors.SensorStateReady", 0x200801, Sel) 86 | SensorStateNotAvailable = Usage("sensors.SensorStateNotAvailable", 0x200802, Sel) 87 | SensorStateNoData = Usage("sensors.SensorStateNoData", 0x200803, Sel) 88 | SensorStateInitializing = Usage("sensors.SensorStateInitializing", 0x200804, Sel) 89 | SensorStateAccessDenied = Usage("sensors.SensorStateAccessDenied", 0x200805, Sel) 90 | SensorStateError = Usage("sensors.SensorStateError", 0x200806, Sel) 91 | EventSensorEvent = Usage("sensors.EventSensorEvent", 0x200202, NAry) 92 | SensorEventUnknown = Usage("sensors.SensorEventUnknown", 0x200810, NAry) 93 | SensorEventStateChanged = Usage("sensors.SensorEventStateChanged", 0x200811, NAry) 94 | SensorEventPropertyChanged = Usage("sensors.SensorEventPropertyChanged", 0x200812, NAry) 95 | SensorEventDataUpdated = Usage("sensors.SensorEventDataUpdated", 0x200813, NAry) 96 | SensorEventPollResponse = Usage("sensors.SensorEventPollResponse", 0x200814, NAry) 97 | SensorEventChangeSensitivity = Usage("sensors.SensorEventChangeSensitivity", 0x200815, NAry) 98 | SensorEventRangeMaximumReached = Usage("sensors.SensorEventRangeMaximumReached", 0x200816, NAry) 99 | SensorEventRangeMinimumReached = Usage("sensors.SensorEventRangeMinimumReached", 0x200817, NAry) 100 | SensorEventHighThresholdCrossUpward = Usage("sensors.SensorEventHighThresholdCrossUpward", 0x200818, NAry) 101 | SensorEventHighThresholdCrossDownward = Usage("sensors.SensorEventHighThresholdCrossDownward", 0x200819, NAry) 102 | SensorEventLowThresholdCrossUpward = Usage("sensors.SensorEventLowThresholdCrossUpward", 0x20081A, NAry) 103 | SensorEventLowThresholdCrossDownward = Usage("sensors.SensorEventLowThresholdCrossDownward", 0x20081B, NAry) 104 | SensorEventZeroThresholdCrossUpward = Usage("sensors.SensorEventZeroThresholdCrossUpward", 0x20081C, NAry) 105 | SensorEventZeroThresholdCrossDownward = Usage("sensors.SensorEventZeroThresholdCrossDownward", 0x20081D, NAry) 106 | SensorEventPeriodExceeded = Usage("sensors.SensorEventPeriodExceeded", 0x20081E, NAry) 107 | SensorEventFrequencyExceeded = Usage("sensors.SensorEventFrequencyExceeded", 0x20081F, NAry) 108 | SensorEventComplexTrigger = Usage("sensors.SensorEventComplexTrigger", 0x200820, NAry) 109 | 110 | Property = Usage("sensors.Property", 0x200300) 111 | PropertyFriendlyName = Usage("sensors.PropertyFriendlyName", 0x200301, SV) 112 | PropertyPersistentUniqueID = Usage("sensors.PropertyPersistentUniqueID", 0x200302, DV) 113 | PropertySensorStatus = Usage("sensors.PropertySensorStatus", 0x200303, DV) 114 | PropertyMinimumReportInterval = Usage("sensors.PropertyMinimumReportInterval", 0x200304, SV) 115 | PropertySensorManufacturer = Usage("sensors.PropertySensorManufacturer", 0x200305, SV) 116 | PropertySensorModel = Usage("sensors.PropertySensorModel", 0x200306, SV) 117 | PropertySensorSerialNumber = Usage("sensors.PropertySensorSerialNumber", 0x200307, SV) 118 | PropertySensorDescription = Usage("sensors.PropertySensorDescription", 0x200308, SV) 119 | PropertySensorConnectionType = Usage("sensors.PropertySensorConnectionType", 0x200309, NAry) 120 | ConnectionTypePCIntegrated = Usage("sensors.ConnectionTypePCIntegrated", 0x200830, Sel) 121 | ConnectionTypePCAttached = Usage("sensors.ConnectionTypePCAttached", 0x200831, Sel) 122 | ConnectionTypePCExternal = Usage("sensors.ConnectionTypePCExternal", 0x200832, Sel) 123 | PropertySensorDevicePath = Usage("sensors.PropertySensorDevicePath", 0x20030A, DV) 124 | PropertyHardwareRevision = Usage("sensors.PropertyHardwareRevision", 0x20030B, SV) 125 | PropertyFirmwareVersion = Usage("sensors.PropertyFirmwareVersion", 0x20030C, SV) 126 | PropertyReleaseDate = Usage("sensors.PropertyReleaseDate", 0x20030D, SV) 127 | PropertyReportInterval = Usage("sensors.PropertyReportInterval", 0x20030E, DV) 128 | PropertyChangeSensitivityAbsolute = Usage("sensors.PropertyChangeSensitivityAbsolute", 0x20030F, DV) 129 | PropertyChangeSensitivityPercentofRange = Usage("sensors.PropertyChangeSensitivityPercentofRange", 0x200310, DV) 130 | PropertyChangeSensitivityPercentRelative = Usage("sensors.PropertyChangeSensitivityPercentRelative", 0x200311, DV) 131 | PropertyAccuracy = Usage("sensors.PropertyAccuracy", 0x200312, DV) 132 | PropertyResolution = Usage("sensors.PropertyResolution", 0x200313, DV) 133 | PropertyMaximum = Usage("sensors.PropertyMaximum", 0x200314, DV) 134 | PropertyMinimum = Usage("sensors.PropertyMinimum", 0x200315, DV) 135 | PropertyReportingState = Usage("sensors.PropertyReportingState", 0x200316, NAry) 136 | ReportingStateReportNoEvents = Usage("sensors.ReportingStateReportNoEvents", 0x200840, Sel) 137 | ReportingStateReportAllEvents = Usage("sensors.ReportingStateReportAllEvents", 0x200841, Sel) 138 | ReportingStateReportThresholdEvents = Usage("sensors.ReportingStateReportThresholdEvents", 0x200842, Sel) 139 | ReportingStateWakeOnNoEvents = Usage("sensors.ReportingStateWakeOnNoEvents", 0x200843, Sel) 140 | 141 | ReportingStateWakeOnAllEvents = Usage("sensors.ReportingStateWakeOnAllEvents", 0x200844, Sel) 142 | ReportingStateWakeOnThresholdEvents = Usage("sensors.ReportingStateWakeOnThresholdEvents", 0x200845, Sel) 143 | PropertySamplingRate = Usage("sensors.PropertySamplingRate", 0x200317, DV) 144 | PropertyResponseCurve = Usage("sensors.PropertyResponseCurve", 0x200318, DV) 145 | PropertyPowerState = Usage("sensors.PropertyPowerState", 0x200319, NAry) 146 | PowerStateUndefined = Usage("sensors.PowerStateUndefined", 0x200850, Sel) 147 | PowerStateD0FullPower = Usage("sensors.PowerStateD0FullPower", 0x200851, Sel) 148 | PowerStateD1LowPower = Usage("sensors.PowerStateD1LowPower", 0x200852, Sel) 149 | PowerStateD2StandbyPowerwithWakeup = Usage("sensors.PowerStateD2StandbyPowerwithWakeup", 0x200853, Sel) 150 | PowerStateD3SleepwithWakeup = Usage("sensors.PowerStateD3SleepwithWakeup", 0x200854, Sel) 151 | PowerStateD4PowerOff = Usage("sensors.PowerStateD4PowerOff", 0x200855, Sel) 152 | 153 | DataFieldLocation = Usage("sensors.DataFieldLocation", 0x200400, SV) 154 | DataFieldLocationReserved = Usage("sensors.DataFieldLocationReserved", 0x200401) 155 | DataFieldAltitudeAntennaSeaLevel = Usage("sensors.DataFieldAltitudeAntennaSeaLevel", 0x200402, SV) 156 | DataFieldDifferentialReferenceStationID = Usage("sensors.DataFieldDifferentialReferenceStationID", 0x200403, SV) 157 | DataFieldAltitudeEllipsoidError = Usage("sensors.DataFieldAltitudeEllipsoidError", 0x200404, SV) 158 | DataFieldAltitudeEllipsoid = Usage("sensors.DataFieldAltitudeEllipsoid", 0x200405, SV) 159 | DataFieldAltitudeSeaLevelError = Usage("sensors.DataFieldAltitudeSeaLevelError", 0x200406, SV) 160 | DataFieldAltitudeSeaLevel = Usage("sensors.DataFieldAltitudeSeaLevel", 0x200407, SV) 161 | DataFieldDifferentialGPSDataAge = Usage("sensors.DataFieldDifferentialGPSDataAge", 0x200408, SV) 162 | DataFieldErrorRadius = Usage("sensors.DataFieldErrorRadius", 0x200409, SV) 163 | DataFieldFixQuality = Usage("sensors.DataFieldFixQuality", 0x20040A, NAry) 164 | FixQualityNoFix = Usage("sensors.FixQualityNoFix", 0x200870, Sel) 165 | FixQualityGPS = Usage("sensors.FixQualityGPS", 0x200871, Sel) 166 | FixQualityDGPS = Usage("sensors.FixQualityDGPS", 0x200872, Sel) 167 | DataFieldFixType = Usage("sensors.DataFieldFixType", 0x20040B, NAry) 168 | FixTypeNoFix = Usage("sensors.FixTypeNoFix", 0x200880, Sel) 169 | FixTypeGPSSPSModeFixValid = Usage("sensors.FixTypeGPSSPSModeFixValid", 0x200881, Sel) 170 | FixTypeDGPSSPSModeFixValid = Usage("sensors.FixTypeDGPSSPSModeFixValid", 0x200882, Sel) 171 | FixTypeGPSPPSModeFixValid = Usage("sensors.FixTypeGPSPPSModeFixValid", 0x200883, Sel) 172 | FixTypeRealTimeKinematic = Usage("sensors.FixTypeRealTimeKinematic", 0x200884, Sel) 173 | FixTypeFloatRTK = Usage("sensors.FixTypeFloatRTK", 0x200885, Sel) 174 | FixTypeEstimated = Usage("sensors.FixTypeEstimated", 0x200886, Sel) 175 | FixTypeManualInputMode = Usage("sensors.FixTypeManualInputMode", 0x200887, Sel) 176 | FixTypeSimulatorMode = Usage("sensors.FixTypeSimulatorMode", 0x200888, Sel) 177 | DataFieldGeoidalSeparation = Usage("sensors.DataFieldGeoidalSeparation", 0x20040C, SV) 178 | DataFieldGPSOperationMode = Usage("sensors.DataFieldGPSOperationMode", 0x20040D, NAry) 179 | GPSOperationModeManual = Usage("sensors.GPSOperationModeManual", 0x200890, Sel) 180 | GPSOperationModeAutomatic = Usage("sensors.GPSOperationModeAutomatic", 0x200891, Sel) 181 | DataFieldGPSSelectionMode = Usage("sensors.DataFieldGPSSelectionMode", 0x20040E, SV) 182 | GPSSelectionModeAutonomous = Usage("sensors.GPSSelectionModeAutonomous", 0x2008A0, Sel) 183 | GPSSelectionModeDGPS = Usage("sensors.GPSSelectionModeDGPS", 0x2008A1, Sel) 184 | GPSSelectionModeEstimated = Usage("sensors.GPSSelectionModeEstimated", 0x2008A2, Sel) 185 | GPSSelectionModeManualInput = Usage("sensors.GPSSelectionModeManualInput", 0x2008A3, Sel) 186 | -------------------------------------------------------------------------------- /hrdc/usage/simulation.py: -------------------------------------------------------------------------------- 1 | from .usage import * 2 | 3 | FlightSimulationDevice = Usage("simulation.FlightSimulationDevice", 0x20001, CA) 4 | AutomobileSimulationDevice = Usage("simulation.AutomobileSimulationDevice", 0x20002, CA) 5 | TankSimulationDevice = Usage("simulation.TankSimulationDevice", 0x20003, CA) 6 | SpaceshipSimulationDevice = Usage("simulation.SpaceshipSimulationDevice", 0x20004, CA) 7 | SubmarineSimulationDevice = Usage("simulation.SubmarineSimulationDevice", 0x20005, CA) 8 | SailingSimulationDevice = Usage("simulation.SailingSimulationDevice", 0x20006, CA) 9 | MotorcycleSimulationDevice = Usage("simulation.MotorcycleSimulationDevice", 0x20007, CA) 10 | SportsSimulationDevice = Usage("simulation.SportsSimulationDevice", 0x20008, CA) 11 | AirplaneSimulationDevice = Usage("simulation.AirplaneSimulationDevice", 0x20009, CA) 12 | HelicopterSimulationDevice = Usage("simulation.HelicopterSimulationDevice", 0x2000A, CA) 13 | MagicCarpetSimulationDevice = Usage("simulation.MagicCarpetSimulationDevice", 0x2000B, CA) 14 | BicycleSimulationDevice = Usage("simulation.BicycleSimulationDevice", 0x2000C, CA) 15 | FlightControlStick = Usage("simulation.FlightControlStick", 0x20020, CA) 16 | FlightStick = Usage("simulation.FlightStick", 0x20021, CA) 17 | CyclicControl = Usage("simulation.CyclicControl", 0x20022, CP) 18 | CyclicTrim = Usage("simulation.CyclicTrim", 0x20023, CP) 19 | FlightYoke = Usage("simulation.FlightYoke", 0x20024, CA) 20 | TrackControl = Usage("simulation.TrackControl", 0x20025, CP) 21 | Aileron = Usage("simulation.Aileron", 0x200B0, DV) 22 | AileronTrim = Usage("simulation.AileronTrim", 0x200B1, DV) 23 | AntiTorqueControl = Usage("simulation.TorqueControl", 0x200B2, DV) 24 | AutopilotEnable = Usage("simulation.AutopilotEnable", 0x200B3, OOC) 25 | ChaffRelease = Usage("simulation.ChaffRelease", 0x200B4, OSC) 26 | CollectiveControl = Usage("simulation.CollectiveControl", 0x200B5, DV) 27 | DiveBrake = Usage("simulation.DiveBrake", 0x200B6, DV) 28 | ElectronicCountermeasures = Usage("simulation.ElectronicCountermeasures", 0x200B7, OOC) 29 | Elevator = Usage("simulation.Elevator", 0x200B8, DV) 30 | ElevatorTrim = Usage("simulation.ElevatorTrim", 0x200B9, DV) 31 | Rudder = Usage("simulation.Rudder", 0x200BA, DV) 32 | Throttle = Usage("simulation.Throttle", 0x200BB, DV) 33 | FlightCommunications = Usage("simulation.FlightCommunications", 0x200BC, OOC) 34 | FlareRelease = Usage("simulation.FlareRelease", 0x200BD, OSC) 35 | LandingGear = Usage("simulation.LandingGear", 0x200BE, OOC) 36 | ToeBrake = Usage("simulation.ToeBrake", 0x200BF, DV) 37 | Trigger = Usage("simulation.Trigger", 0x200C0, MC) 38 | WeaponsArm = Usage("simulation.WeaponsArm", 0x200C1, OOC) 39 | WeaponsSelect = Usage("simulation.WeaponsSelect", 0x200C2, OSC) 40 | WingFlaps = Usage("simulation.WingFlaps", 0x200C3, DV) 41 | Accelerator = Usage("simulation.Accelerator", 0x200C4, DV) 42 | Brake = Usage("simulation.Brake", 0x200C5, DV) 43 | Clutch = Usage("simulation.Clutch", 0x200C6, DV) 44 | Shifter = Usage("simulation.Shifter", 0x200C7, DV) 45 | Steering = Usage("simulation.Steering", 0x200C8, DV) 46 | TurretDirection = Usage("simulation.TurretDirection", 0x200C9, DV) 47 | BarrelElevation = Usage("simulation.BarrelElevation", 0x200CA, DV) 48 | DivePlane = Usage("simulation.DivePlane", 0x200CB, DV) 49 | Ballast = Usage("simulation.Ballast", 0x200CC, DV) 50 | BicycleCrank = Usage("simulation.BicycleCrank", 0x200CD, DV) 51 | HandleBars = Usage("simulation.HandleBars", 0x200CE, DV) 52 | FrontBrake = Usage("simulation.FrontBrake", 0x200CF, DV) 53 | RearBrake = Usage("simulation.RearBrake", 0x200D0, DV) 54 | -------------------------------------------------------------------------------- /hrdc/usage/telephony.py: -------------------------------------------------------------------------------- 1 | from .usage import * 2 | 3 | Phone = Usage("telephony.Phone", 0x0B0001, CA) 4 | AnsweringMachine = Usage("telephony.AnsweringMachine", 0x0B0002, CA) 5 | MessageControls = Usage("telephony.MessageControls", 0x0B0003, CL) 6 | Handset = Usage("telephony.Handset", 0x0B0004, CL) 7 | Headset = Usage("telephony.Headset", 0x0B0005, CL) 8 | TelephonyKeyPad = Usage("telephony.TelephonyKeyPad", 0x0B0006, NAry) 9 | ProgrammableButton = Usage("telephony.ProgrammableButton", 0x0B0007, NAry) 10 | HookSwitch = Usage("telephony.HookSwitch", 0x0B0020, OOC) 11 | Flash = Usage("telephony.Flash", 0x0B0021, MC) 12 | Feature = Usage("telephony.Feature", 0x0B0022, OSC) 13 | Hold = Usage("telephony.Hold", 0x0B0023, OOC) 14 | Redial = Usage("telephony.Redial", 0x0B0024, OSC) 15 | Transfer = Usage("telephony.Transfer", 0x0B0025, OSC) 16 | Drop = Usage("telephony.Drop", 0x0B0026, OSC) 17 | Park = Usage("telephony.Park", 0x0B0027, OOC) 18 | ForwardCalls = Usage("telephony.ForwardCalls", 0x0B0028, OOC) 19 | AlternateFunction = Usage("telephony.AlternateFunction", 0x0B0029, MC) 20 | Line = Usage("telephony.Line", 0x0B002A, OSC) 21 | SpeakerPhone = Usage("telephony.SpeakerPhone", 0x0B002B, OOC) 22 | Conference = Usage("telephony.Conference", 0x0B002C, OOC) 23 | RingEnable = Usage("telephony.RingEnable", 0x0B002D, OOC) 24 | RingSelect = Usage("telephony.RingSelect", 0x0B002E, OSC) 25 | PhoneMute = Usage("telephony.PhoneMute", 0x0B002F, OOC) 26 | CallerID = Usage("telephony.CallerID", 0x0B0030, MC) 27 | SpeedDial = Usage("telephony.SpeedDial", 0x0B0050, OSC) 28 | StoreNumber = Usage("telephony.StoreNumber", 0x0B0051, OSC) 29 | RecallNumber = Usage("telephony.RecallNumber", 0x0B0052, OSC) 30 | PhoneDirectory = Usage("telephony.PhoneDirectory", 0x0B0053, OOC) 31 | VoiceMail = Usage("telephony.VoiceMail", 0x0B0070, OOC) 32 | ScreenCalls = Usage("telephony.ScreenCalls", 0x0B0071, OOC) 33 | DoNotDisturb = Usage("telephony.DoNotDisturb", 0x0B0072, OOC) 34 | Message = Usage("telephony.Message", 0x0B0073, OSC) 35 | AnswerOnOff = Usage("telephony.AnswerOnOff", 0x0B0074, OOC) 36 | InsideDialTone = Usage("telephony.InsideDialTone", 0x0B0090, MC) 37 | OutsideDialTone = Usage("telephony.OutsideDialTone", 0x0B0091, MC) 38 | InsideRingTone = Usage("telephony.InsideRingTone", 0x0B0092, MC) 39 | OutsideRingTone = Usage("telephony.OutsideRingTone", 0x0B0093, MC) 40 | PriorityRingTone = Usage("telephony.PriorityRingTone", 0x0B0094, MC) 41 | InsideRingback = Usage("telephony.InsideRingback", 0x0B0095, MC) 42 | PriorityRingback = Usage("telephony.PriorityRingback", 0x0B0096, MC) 43 | LineBusyTone = Usage("telephony.LineBusyTone", 0x0B0097, MC) 44 | ReorderTone = Usage("telephony.ReorderTone", 0x0B0098, MC) 45 | CallWaitingTone = Usage("telephony.CallWaitingTone", 0x0B0099, MC) 46 | ConfirmationTone1 = Usage("telephony.ConfirmationTone1", 0x0B009A, MC) 47 | ConfirmationTone2 = Usage("telephony.ConfirmationTone2", 0x0B009B, MC) 48 | TonesOff = Usage("telephony.TonesOff", 0x0B009C, OOC) 49 | OutsideRingback = Usage("telephony.OutsideRingback", 0x0B009D, MC) 50 | Ringer = Usage("telephony.Ringer", 0x0B009E, OOC) 51 | PhoneKey0 = Usage("telephony.PhoneKey0", 0x0B00B0, Sel) 52 | PhoneKey1 = Usage("telephony.PhoneKey1", 0x0B00B1, Sel) 53 | PhoneKey2 = Usage("telephony.PhoneKey2", 0x0B00B2, Sel) 54 | PhoneKey3 = Usage("telephony.PhoneKey3", 0x0B00B3, Sel) 55 | PhoneKey4 = Usage("telephony.PhoneKey4", 0x0B00B4, Sel) 56 | PhoneKey5 = Usage("telephony.PhoneKey5", 0x0B00B5, Sel) 57 | PhoneKey6 = Usage("telephony.PhoneKey6", 0x0B00B6, Sel) 58 | PhoneKey7 = Usage("telephony.PhoneKey7", 0x0B00B7, Sel) 59 | PhoneKey8 = Usage("telephony.PhoneKey8", 0x0B00B8, Sel) 60 | PhoneKey9 = Usage("telephony.PhoneKey9", 0x0B00B9, Sel) 61 | PhoneKeyStar = Usage("telephony.PhoneKeyStar", 0x0B00BA, Sel) 62 | PhoneKeyPound = Usage("telephony.PhoneKeyPound", 0x0B00BB, Sel) 63 | PhoneKeyA = Usage("telephony.PhoneKeyA", 0x0B00BC, Sel) 64 | PhoneKeyB = Usage("telephony.PhoneKeyB", 0x0B00BD, Sel) 65 | PhoneKeyC = Usage("telephony.PhoneKeyC", 0x0B00BE, Sel) 66 | PhoneKeyD = Usage("telephony.PhoneKeyD", 0x0B00BF, Sel) 67 | -------------------------------------------------------------------------------- /hrdc/usage/usage.py: -------------------------------------------------------------------------------- 1 | from .. import util as _util 2 | 3 | class Usage(_util.NamedConstant): 4 | reverse = dict() 5 | 6 | def __init__(self, name, value, *kinds, **opts): 7 | register = opts.get('register', True) 8 | _util.NamedConstant.__init__(self, name, value) 9 | self.kinds = set(kinds) 10 | if register: 11 | self.reverse[int(value)] = self 12 | 13 | @classmethod 14 | def page(cls, no): 15 | no = int(no) 16 | name = cls.pageName.get(no, str(no)) 17 | return _util.NamedConstant(name, no) 18 | 19 | @classmethod 20 | def inpage(cls, page, no): 21 | u = cls.lookup((int(page) << 16) | int(no)) 22 | sp, su = u.split() 23 | return su 24 | 25 | pageName = { 26 | 0x1: "GenericDesktop", 27 | 0x2: "Simulation", 28 | 0x3: "VR", 29 | 0x5: "Game", 30 | 0x6: "Device", 31 | 0x7: "Keyboard", 32 | 0x8: "Led", 33 | 0x9: "Button", 34 | 0xa: "Ordinal", 35 | 0xb: "TelephonyDevice", 36 | 0xc: "Consumer", 37 | 0xd: "Digitizers", 38 | 0xf: "PID", 39 | 0x20: "Sensors", 40 | 0x59: "Lighting", 41 | 0x90: "Camera", 42 | 0xff00: "Arcade", 43 | } 44 | 45 | @classmethod 46 | def lookup(cls, value): 47 | if isinstance(value, Usage): 48 | return value 49 | if isinstance(value, _util.NamedConstant): 50 | return Usage(value.name, int(value.value), register = False) 51 | if int(value) >> 16: 52 | try: 53 | return cls.reverse[int(value)] 54 | except: 55 | pass 56 | return cls(hex(int(value)), int(value), register = False) 57 | 58 | def split(self): 59 | try: 60 | p, u = self.name.split(".", 1) 61 | return ( 62 | _util.NamedConstant(p, self.value >> 16), 63 | _util.NamedConstant(u, self.value & 0xffff), 64 | ) 65 | except: 66 | u = int(self.value) & 0xffff 67 | return self.page(int(self.value) >> 16), _util.NamedConstant(str(u), u) 68 | 69 | class UsageRange: 70 | def __init__(self, min, max): 71 | self.min = min 72 | self.max = max 73 | 74 | def __str__(self): 75 | return "UsageRange(%s, %s)" % (self.min, self.max) 76 | 77 | def __len__(self): 78 | return int(self.max) - int(self.min) + 1 79 | 80 | def __getitem__(self, no): 81 | return Usage.lookup(int(self.min) + no) 82 | 83 | def __iter__(self): 84 | for i in range(int(self.min), int(self.max) + 1): 85 | yield Usage.lookup(i) 86 | 87 | @classmethod 88 | def from_array(cls, arr): 89 | if len(arr) < 2: 90 | raise ValueError("Cannot create range from array with less than 2 elements") 91 | for i, j in zip(arr, arr[1:]): 92 | if int(i) + 1 != int(j): 93 | raise ValueError("Noncontiguous values") 94 | return cls(arr[0], arr[-1]) 95 | 96 | class UsageType: 97 | def __init__(self, name): 98 | self.name = name 99 | 100 | def test(self, usage): 101 | usage = Usage.lookup(usage) 102 | if isinstance(usage, Usage): 103 | return self in usage.kinds 104 | return False 105 | 106 | CA = UsageType("Collection Application") 107 | CL = UsageType("Collection Logical") 108 | CP = UsageType("Collection Physical") 109 | RTC = UsageType("Re-Trigger Control") 110 | LC = UsageType("Linear Control") 111 | MC = UsageType("Momentary Control") 112 | OSC = UsageType("One Shot Control") 113 | Sel = UsageType("Selector") 114 | SV = UsageType("Static Value") 115 | SF = UsageType("Static Flag") 116 | DF = UsageType("Dynamic Flag") 117 | DV = UsageType("Dynamic Value") 118 | NAry = UsageType("Named Array") 119 | US = UsageType("Usage Switch") 120 | UM = UsageType("Usage Modifier") 121 | OOC = UsageType("On/Off Control") 122 | -------------------------------------------------------------------------------- /hrdc/usage/vr.py: -------------------------------------------------------------------------------- 1 | from .usage import * 2 | 3 | Belt = Usage("vr.Belt", 0x30001, CA) 4 | BodySuit = Usage("vr.BodySuit", 0x30002, CA) 5 | Flexor = Usage("vr.Flexor", 0x30003, CP) 6 | Glove = Usage("vr.Glove", 0x30004, CA) 7 | HeadTracker = Usage("vr.HeadTracker", 0x30005, CP) 8 | HeadMountedDisplay = Usage("vr.HeadMountedDisplay", 0x30006, CA) 9 | HandTracker = Usage("vr.HandTracker", 0x30007, CA) 10 | Oculometer = Usage("vr.Oculometer", 0x30008, CA) 11 | Vest = Usage("vr.Vest", 0x30009, CA) 12 | AnimatronicDevice = Usage("vr.AnimatronicDevice", 0x3000A, CA) 13 | StereoEnable = Usage("vr.StereoEnable", 0x30020, OOC) 14 | DisplayEnable = Usage("vr.DisplayEnable", 0x30021, OOC) 15 | -------------------------------------------------------------------------------- /hrdc/util/__init__.py: -------------------------------------------------------------------------------- 1 | from .named_constant import NamedConstant 2 | -------------------------------------------------------------------------------- /hrdc/util/named_constant.py: -------------------------------------------------------------------------------- 1 | 2 | class NamedConstant: 3 | def __init__(self, name, value): 4 | self.name = name 5 | self.value = value 6 | 7 | def __int__(self): 8 | return self.value 9 | 10 | def __str__(self): 11 | return self.name 12 | 13 | def __lt__(self, other): 14 | return int(self) < int(other) 15 | 16 | def __eq__(self, other): 17 | return int(self) == int(other) 18 | 19 | def __hash__(self): 20 | return hash(int(self)) 21 | 22 | def __add__(self, other): 23 | if isinstance(other, (NamedConstant, int)): 24 | return self.value + int(other) 25 | raise NotImplemented() 26 | 27 | def __invert__(self): 28 | return ~int(self) 29 | 30 | def __mul__(self, other): 31 | if isinstance(other, (NamedConstant, int)): 32 | return self.value * int(other) 33 | raise NotImplemented() 34 | 35 | def __and__(self, other): 36 | if isinstance(other, (NamedConstant, int)): 37 | return self.value & int(other) 38 | raise NotImplemented() 39 | 40 | def __rand__(self, other): 41 | if isinstance(other, (NamedConstant, int)): 42 | return self.value & int(other) 43 | raise NotImplemented() 44 | 45 | def __lshift__(self, other): 46 | if isinstance(other, (NamedConstant, int)): 47 | return self.value << int(other) 48 | raise NotImplemented() 49 | 50 | def __rshift__(self, other): 51 | if isinstance(other, (NamedConstant, int)): 52 | return self.value >> int(other) 53 | raise NotImplemented() 54 | 55 | def __or__(self, other): 56 | if isinstance(other, (NamedConstant, int)): 57 | return self.value | int(other) 58 | raise NotImplemented() 59 | 60 | def __ror__(self, other): 61 | if isinstance(other, (NamedConstant, int)): 62 | return self.value | int(other) 63 | raise NotImplemented() 64 | 65 | 66 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup( 4 | name = "hrdc", 5 | version = "0.1.0", 6 | description = "HID Report descriptor compiler", 7 | author = "Nicolas Pouillon", 8 | author_email = "nipo@ssji.net", 9 | license = "BSD", 10 | classifiers = [ 11 | "Development Status :: 4 - Beta", 12 | "Programming Language :: Python", 13 | ], 14 | use_2to3 = False, 15 | packages = find_packages(), 16 | ) 17 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipo/hrdc/749abec1accf6a97701ae47e249df4dc63d4a69d/tests/__init__.py -------------------------------------------------------------------------------- /tests/issue_6/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipo/hrdc/749abec1accf6a97701ae47e249df4dc63d4a69d/tests/issue_6/__init__.py -------------------------------------------------------------------------------- /tests/issue_6/description.py: -------------------------------------------------------------------------------- 1 | from hrdc.usage import * 2 | from hrdc.descriptor import * 3 | from hrdc.stream import formatter 4 | from hrdc.stream import optimizer 5 | 6 | mouse = Collection(Collection.Application, desktop.Mouse, 7 | Value(Value.Input, desktop.X, 8, 8 | flags = Value.Variable | Value.Relative, 9 | logicalMin = -127, logicalMax = 127), 10 | Value(Value.Input, desktop.Y, 8, 11 | flags = Value.Variable | Value.Relative, 12 | logicalMin = -127, logicalMax = 127), 13 | Value(Value.Input, button.Button(1), 1, 14 | logicalMin = 0, logicalMax = 1), 15 | Value(Value.Input, button.Button(2), 1, 16 | logicalMin = 0, logicalMax = 1), 17 | Value(Value.Input, button.Button(3), 1, 18 | logicalMin = 0, logicalMax = 1), 19 | ) 20 | 21 | class HexList(formatter.Formatter): 22 | def __init__(self, output): 23 | self.output = output 24 | 25 | def append(self, i): 26 | blob = " ".join(["%02x" % x for x in i.bytes()]) 27 | self.output.append(blob + "\n") 28 | 29 | _list = [] 30 | _output = HexList(_list) 31 | _output = optimizer.Optimizer.new(_output) 32 | _visitor = streamer.Streamer(_output) 33 | mouse.accept(_visitor) 34 | if hasattr(_output, "close"): 35 | _output.close() 36 | else: 37 | del _visitor 38 | del _output 39 | print(''.join(_list)) 40 | -------------------------------------------------------------------------------- /tests/issue_7/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipo/hrdc/749abec1accf6a97701ae47e249df4dc63d4a69d/tests/issue_7/__init__.py -------------------------------------------------------------------------------- /tests/issue_7/test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from hrdc.usage import * 3 | from hrdc.descriptor.test import descriptor_expected 4 | from hrdc.descriptor import * 5 | 6 | descriptor = Collection(Collection.Application, sensors.Environmental, 7 | Collection(Collection.Physical, sensors.Environmental, 8 | Report(1, 9 | Value(Value.Input, sensors.EnvironmentalHumidity, 16, logicalMin = 0, logicalMax = 1400), 10 | Value(Value.Input, sensors.EnvironmentalTemperature, 8, logicalMin = 0, logicalMax = 100, count = 2), 11 | Value(Value.Input, sensors.EnvironmentalAtmosphericPressure, 8, logicalMin = 0, logicalMax = 100), 12 | ), 13 | ), 14 | ) 15 | 16 | class TestIssue7(unittest.TestCase): 17 | def test_item_merging(self): 18 | descriptor_expected(self, descriptor, """ 19 | 0x05, 0x20, // UsagePage (sensors) 20 | 0x09, 0x30, // Usage (Environmental) 21 | 0xa1, 0x01, // Collection (Application) 22 | 0x09, 0x30, // Usage (Environmental) 23 | 0xa1, 0x00, // Collection (Physical) 24 | 0x85, 0x01, // ReportID (1) 25 | 0x15, 0x00, // LogicalMinimum (0) 26 | 0x26, 0x78, 0x05, // LogicalMaximum (1400) 27 | 0x75, 0x10, // ReportSize (16) 28 | 0x95, 0x01, // ReportCount (1) 29 | 0x09, 0x32, // Usage (EnvironmentalHumidity) 30 | 0x81, 0x02, // Input (Variable) 31 | 0x25, 0x64, // LogicalMaximum (100) 32 | 0x75, 0x08, // ReportSize (8) 33 | 0x95, 0x02, // ReportCount (2) 34 | 0x09, 0x33, // Usage (EnvironmentalTemperature) 35 | 0x81, 0x02, // Input (Variable) 36 | 0x95, 0x01, // ReportCount (1) 37 | 0x09, 0x31, // Usage (EnvironmentalAtmosphericPressure) 38 | 0x81, 0x02, // Input (Variable) 39 | 0xc0, // EndCollection 40 | 0xc0, // EndCollection 41 | """) 42 | 43 | if __name__ == '__main__': 44 | compile_main(descriptor) 45 | 46 | -------------------------------------------------------------------------------- /tests/test_basic.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from hrdc.usage import * 3 | from hrdc.descriptor.test import descriptor_expected 4 | from hrdc.descriptor import * 5 | 6 | class TestBasicDescriptors(unittest.TestCase): 7 | def test_battery(self): 8 | battery = Collection(Collection.Application, consumer.ConsumerControl, 9 | Collection(Collection.Logical, desktop.Keyboard, 10 | Value(Value.Input, device.BatteryStrength, 8, 11 | logicalMin = 0, logicalMax = 100) 12 | ) 13 | ) 14 | 15 | descriptor_expected(self, battery, """ 16 | 0x05, 0x0c, // UsagePage (consumer) 17 | 0x09, 0x01, // Usage (ConsumerControl) 18 | 0xa1, 0x01, // Collection (Application) 19 | 0x05, 0x01, // UsagePage (desktop) 20 | 0x09, 0x06, // Usage (Keyboard) 21 | 0xa1, 0x02, // Collection (Logical) 22 | 0x15, 0x00, // LogicalMinimum (0) 23 | 0x25, 0x64, // LogicalMaximum (100) 24 | 0x75, 0x08, // ReportSize (8) 25 | 0x95, 0x01, // ReportCount (1) 26 | 0x05, 0x06, // UsagePage (device) 27 | 0x09, 0x20, // Usage (BatteryStrength) 28 | 0x81, 0x02, // Input (Variable) 29 | 0xc0, // EndCollection 30 | 0xc0, // EndCollection 31 | """) 32 | 33 | def test_keyboard(self): 34 | bit_in = lambda x: Value(Value.Input, x, 1, 35 | logicalMin = 0, logicalMax = 1) 36 | 37 | bit_out = lambda x: Value(Value.Output, x, 1, 38 | logicalMin = 0, logicalMax = 1) 39 | 40 | key_range = lambda n: Value(Value.Input, usage = None, size = 8, 41 | namedArray = UsageRange(keyboard.NoEvent, keyboard.KeypadHexadecimal), 42 | logicalMin = 0, count = n) 43 | 44 | descriptor = Collection(Collection.Application, desktop.Keyboard, 45 | bit_in(keyboard.LeftControl), 46 | bit_in(keyboard.LeftShift), 47 | bit_in(keyboard.LeftAlt), 48 | bit_in(keyboard.LeftGui), 49 | bit_in(keyboard.RightControl), 50 | bit_in(keyboard.RightShift), 51 | bit_in(keyboard.RightAlt), 52 | bit_in(keyboard.RightGui), 53 | 54 | key_range(6), 55 | 56 | bit_out(led.NumLock), 57 | bit_out(led.CapsLock), 58 | bit_out(led.ScrollLock), 59 | ) 60 | 61 | descriptor_expected(self, descriptor, """ 62 | 0x05, 0x01, // UsagePage (desktop) 63 | 0x09, 0x06, // Usage (Keyboard) 64 | 0xa1, 0x01, // Collection (Application) 65 | 0x15, 0x00, // LogicalMinimum (0) 66 | 0x25, 0x01, // LogicalMaximum (1) 67 | 0x75, 0x01, // ReportSize (1) 68 | 0x95, 0x08, // ReportCount (8) 69 | 0x05, 0x07, // UsagePage (keyboard) 70 | 0x19, 0xe0, // UsageMinimum (LeftControl) 71 | 0x29, 0xe7, // UsageMaximum (RightGui) 72 | 0x81, 0x02, // Input (Variable) 73 | 0x26, 0xdd, 0x00, // LogicalMaximum (221) 74 | 0x75, 0x08, // ReportSize (8) 75 | 0x95, 0x06, // ReportCount (6) 76 | 0x19, 0x00, // UsageMinimum (NoEvent) 77 | 0x29, 0xdd, // UsageMaximum (KeypadHexadecimal) 78 | 0x81, 0x00, // Input 79 | 0x25, 0x01, // LogicalMaximum (1) 80 | 0x75, 0x01, // ReportSize (1) 81 | 0x95, 0x03, // ReportCount (3) 82 | 0x05, 0x08, // UsagePage (led) 83 | 0x19, 0x01, // UsageMinimum (NumLock) 84 | 0x29, 0x03, // UsageMaximum (ScrollLock) 85 | 0x91, 0x02, // Output (Variable) 86 | 0xc0, // EndCollection 87 | """) 88 | 89 | def test_mouse(self): 90 | mouse = Collection(Collection.Application, desktop.Mouse, 91 | Value(Value.Input, desktop.X, 8, 92 | flags = Value.Variable | Value.Relative, 93 | logicalMin = -127, logicalMax = 127), 94 | Value(Value.Input, desktop.Y, 8, 95 | flags = Value.Variable | Value.Relative, 96 | logicalMin = -127, logicalMax = 127), 97 | Value(Value.Input, button.Button(1), 1, 98 | logicalMin = 0, logicalMax = 1), 99 | Value(Value.Input, button.Button(2), 1, 100 | logicalMin = 0, logicalMax = 1), 101 | Value(Value.Input, button.Button(3), 1, 102 | logicalMin = 0, logicalMax = 1), 103 | ) 104 | 105 | descriptor_expected(self, mouse, """ 106 | 0x05, 0x01, // UsagePage (desktop) 107 | 0x09, 0x02, // Usage (Mouse) 108 | 0xa1, 0x01, // Collection (Application) 109 | 0x15, 0x81, // LogicalMinimum (-127) 110 | 0x25, 0x7f, // LogicalMaximum (127) 111 | 0x75, 0x08, // ReportSize (8) 112 | 0x95, 0x02, // ReportCount (2) 113 | 0x09, 0x30, // Usage (X) 114 | 0x09, 0x31, // Usage (Y) 115 | 0x81, 0x06, // Input (Variable|Relative) 116 | 0x15, 0x00, // LogicalMinimum (0) 117 | 0x25, 0x01, // LogicalMaximum (1) 118 | 0x75, 0x01, // ReportSize (1) 119 | 0x95, 0x03, // ReportCount (3) 120 | 0x05, 0x09, // UsagePage (button) 121 | 0x19, 0x01, // UsageMinimum (Button(1)) 122 | 0x29, 0x03, // UsageMaximum (Button(3)) 123 | 0x81, 0x02, // Input (Variable) 124 | 0xc0, // EndCollection 125 | """) 126 | -------------------------------------------------------------------------------- /tests/test_item.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from hrdc.stream import item 3 | 4 | class TestItemEncoding(unittest.TestCase): 5 | 6 | def test_signedness(self): 7 | self.assertEqual(item.Usage(6).bytes(), b'\x09\x06') 8 | self.assertEqual(item.Usage(0xff).bytes(), b'\x09\xff') 9 | self.assertEqual(item.Usage(0xffff).bytes(), b'\x0a\xff\xff') 10 | self.assertEqual(item.Usage(0x8000).bytes(), b'\x0a\x00\x80') 11 | 12 | self.assertEqual(item.LogicalMinimum(0).bytes(), b'\x15\x00') 13 | self.assertEqual(item.LogicalMinimum(6).bytes(), b'\x15\x06') 14 | self.assertEqual(item.LogicalMinimum(-5).bytes(), b'\x15\xfb') 15 | self.assertEqual(item.LogicalMinimum(0xffff).bytes(), b'\x17\xff\xff\x00\x00') 16 | self.assertEqual(item.LogicalMinimum(-32768).bytes(), b'\x16\x00\x80') 17 | self.assertEqual(item.LogicalMinimum(32767).bytes(), b'\x16\xff\x7f') 18 | self.assertEqual(item.LogicalMinimum(32768).bytes(), b'\x17\x00\x80\x00\x00') 19 | 20 | def test_zero_bits(self): 21 | self.assertEqual(item.EndCollection().bytes(), b'\xc0') 22 | 23 | def test_data_items(self): 24 | self.assertEqual(item.Input(item.Input.Variable).bytes(), b'\x81\x02') 25 | self.assertEqual(item.Output(item.Input.Variable).bytes(), b'\x91\x02') 26 | self.assertEqual(item.Feature(item.Input.Variable).bytes(), b'\xb1\x02') 27 | --------------------------------------------------------------------------------