Share via


Input Device Haptics Implementation Guide

This document details the protocol implementation for haptic-enabled input devices connecting to a compatible Windows 11 host. This does not include guidance on mechanical constraints, electrical constraints or component selection for generating the haptic response within the input device's hardware.

Supported Device Classes

Windows 11 supports the following classes of haptic-enabled input devices:

  • Haptic Touchpad is an extension of the Touchpad Device class on Windows. This implementation guide adds to the Touchpad Implementation Guide and focuses on implementing haptics within the touchpad digitizer, so haptic touchpads must meet the requirements in Touchpad Implementation Guide in addition to those contained here.

  • Haptic Mouse is an extension of the Mouse Device class on Windows. Haptic mice must meet the requirements contained in this documentation.

Note: Haptic-enabled pen input devices are a special device class that will not be covered in this document. For information on how to implement a pen device with haptic feedback, see the Haptic Pen Implementation Guide.

Input Device Haptics Protocol Implementation

A good understanding of the HID protocol is needed to be able to understand the information presented here. See the following resources for information about the HID protocol:

Haptic-enabled input device firmware only needs to report the usages described in this topic. Windows will use the firmware and its own HID drivers to enable the device and give Windows applications access to the device.

Sample descriptors for each supported device class are provided in the Sample Report Descriptors section below.

Common Guidance

The items in this section apply to all classes of input haptic devices.

Required HID Collection

Functionality related to haptics must be included in a HID SimpleHapticsController collection (Page 0xE, Usage 0x1).

For Haptic Touchpad devices, this collection must be a child of the Windows Precision Touchpad top-level collection.

For Haptic Mouse devices, this collection must be a top-level collection and a sibling of the Mouse top-level collection.

Device-Initiated Haptic Feedback

A haptic-enabled input device can optionally support initiating its own haptic feedback, e.g., in response to a button being pressed or released.

For haptic touchpads, the "Haptic Touchpad Guidance" section below describes how the device can choose to support SET_FEATURE reports to allow user customization of its behavior when initiating haptic feedback.

Haptic mice are allowed to trigger device-initiated feedback, but Windows has no mechanism to configure this behavior.

Host-Initiated Haptic Feedback

A haptic-enabled input device can support host-initiated haptic feedback, which can occur at any time after enumeration.

Haptic touchpads and mice can optionally support host-initiated feedback. For haptic touchpads, if host-initiated feedback is supported, both SET_FEATURE reports for device-initiated feedback customization must be supported as well.

Support for host-initiated haptic feedback requires two SimpleHapticsController logical child collections (Page 0x0E, Usage 0x01). These logical collections must be children of the main SimpleHapticsController collection for the device class being implemented (as documented in the "Required HID Collection" section above) and must be separate from the collection used for configuring intensity of device-initiated haptic feedback for touchpads. One of these logical child collections must define a GET_FEATURE report used by the host to query for supported waveforms and their durations. The other logical child collection must define an output report used by the host to manually trigger haptics.

The device must NOT declare support for auto-triggered haptics, and the device must NOT support any continuous waveforms.

Waveforms

The following table defines the waveforms supported by the host for haptic-enabled input devices. The waveforms supported by a device are associated with an ordinal. The waveform usage and duration are provided to the host via the waveform information feature report (see below). When triggering feedback, the host provides the ordinal of the desired waveform as the value for the manual trigger usage.

Mandatory and Optional
Waveform Description Page ID Mandatory/Optional
None No-op. Should not impact the play state of ongoing waveforms 0x0E 0x1001 Mandatory
Stop Stops the playback of ongoing waveforms 0x0E 0x1002 Mandatory
Hover A light pulse used to indicate hover and signal the potential for an upcoming action 0x0E 0x1008 Mandatory
Collide A soft pulse used to indicate collisions with screen boundaries or the ends of sliders and scrollbars 0x0E 0x1012 Mandatory
Align A crisp pulse that confirms object alignment during drag, scale, or rotate interactions with guides or canvas edges 0x0E 0x1013 Mandatory
Step A firm pulse used for stepping through items in sliders, lists, or scrubbers 0x0E 0x1014 Mandatory
Grow A dynamic pulse that conveys motion, transitions, or intelligent system activity 0x0E 0x1015 Mandatory
Press The haptic signal triggered by the device when it determines that the surface button has been pressed. If supported, Release must also be supported. 0x0E 0x1006 Optional
Release The haptic signal triggered by the device when it determines that the surface button has been released. If supported, Press must also be supported. 0x0E 0x1007 Optional
Success Strong haptic signal to alert user an action has succeeded 0x0E 0x1009 Optional
Error Strong haptic signal to alert user an action has failed, or an error has occurred 0x0E 0x100A Optional
Forbidden

The following waveforms MUST NOT be supported.

Waveform ID Notes
Click 0x1003 Would cause confusion with the existing haptic feedback for button presses.
Buzz Continuous 0x1004 Continuous waveforms should not be supported.
Rumble Continuous 0x1005 Continuous waveforms should not be supported.
Ink Continuous 0x100B Only applicable to pens.
Pencil Continuous 0x100C Only applicable to pens.
Marker Continuous 0x100D Only applicable to pens.
Chisel Marker Continuous 0x100E Only applicable to pens.
Brush Continuous 0x100F Only applicable to pens.
Eraser Continuous 0x1010 Only applicable to pens.
Sparkle Continuous 0x1011 Only applicable to pens.

Waveform Information Feature Report

The host will issue this GET_FEATURE report when querying the device for its supported waveforms. This feature report must have a dedicated report ID.

The logical collection must have two children logical collections, one for the waveform list and one for the duration list. These collections must define a usage range on the Ordinal page (0x0A), which allow the host to query for the waveform and duration associated with each ordinal.

Mandatory and Optional Usages
Member Description Page ID Mandatory/Optional
Waveform List Logical collection containing an ordered list of haptic waveforms supported by the device 0x0E 0x10 Mandatory
Duration List Logical collection containing an ordered list of durations for waveforms in the Waveform List 0x0E 0x11 Mandatory
Waveform List (Mandatory)

This collection provides the mapping between ordinals and the corresponding waveforms. Ordinals 1 and 2 correspond to WAVEFORM_NONE and WAVEFORM_STOP implicitly and do not need to be declared in the descriptor. Therefore, the usage minimum of the collection’s usage range can be 3, and the usage maximum should be large enough to assign ordinals to all supported waveforms.

If the usage maximum is larger than the number of waveforms supported by the device, the device should report WAVEFORM_NONE for the unsupported ordinals.

The logical range of the usage range should include all supported waveform usages. The physical range and units must be 0.

Duration List (Mandatory)

This collection provides the durations of the waveforms defined in the waveform list. The usage minimum and maximum of the collection’s usage range must be identical to those of the waveform list.

Discrete waveforms must have a non-zero duration. WAVEFORM_NONE and WAVEFORM_STOP, if specified, must have a duration of zero.

The logical minimum of the usage range must be zero, and the logical maximum must be at least as large as the duration of the longest discrete waveform. The host will treat the logical values as milliseconds. The physical range must either be zero or identical to the logical range. If the physical range and logical range match, the units should be milliseconds.

Manual Trigger Output Report

The host will issue this report when triggering discrete haptic feedback. This output report must have a dedicated report ID.

Mandatory and Optional Usages
Member Description Page ID Mandatory/Optional
Manual Trigger Waveform to fire as explicit command from the host 0x0E 0x21 Mandatory
Intensity Intensity of the feedback 0x0E 0x23 Mandatory
Repeat Count Number of times to repeat the feedback after initial play 0x0E 0x24 Optional
Retrigger Period Duration of time to wait before re-triggering the feedback when repeating 0x0E 0x25 Optional
Waveform Cutoff Time Max time the feedback can play before being cut off 0x0E 0x28 Optional
Forbidden Usages
Usage ID Notes
Auto Trigger 0x20 Not supported by the host.
Auto Trigger Associated Control 0x22 Not supported by the host.
Manual Trigger (Mandatory)

This usage contains the ordinal of the waveform, as defined from the waveform information feature report, which has been requested to be played by the host. When an output report containing an ordinal other than WAVEFORM_NONE is sent to the device, it should immediately begin playing the specified waveform with the additional properties included in the output report (Intensity, Repeat Count, Retrigger Period, Cutoff Time, if supported). The device should only honor ordinals for discrete waveforms, WAVEFORM_NONE, and WAVEFORM_STOP. If the ordinal corresponds to WAVEFORM_STOP, any ongoing discrete waveform playback should be stopped. If the ordinal corresponds to WAVEFORM_NONE, no action should be performed, and ongoing haptic feedback should continue to play.

The logical range must include all possible ordinals, including the implicit ordinals 1 (WAVEFORM_NONE) and 2 (WAVEFORM_STOP). The physical range and units must be 0.

Intensity (Mandatory)

This usage represents the percentage of maximum intensity to apply to the requested waveform, with the logical maximum representing maximum intensity and the logical minimum representing no feedback at all.

The logical minimum must be zero, and the logical maximum should be selected based on the device’s capabilities – for instance, if the device supports four levels of intensity, the logical maximum should be four. If the device supports more granular intensity, the logical maximum can be larger, but it should not exceed 100. The device must support at least four levels of intensity, so the minimum logical maximum is four. An intensity of zero indicates no feedback should be played – the host will only use this value for WAVEFORM_STOP.

The physical range and units must be 0.

Repeat Count (Optional)

This usage represents the number of times to repeat the waveform after the initial playback. A value of zero indicates that the waveform should only be played once.

If this usage is supported, the retrigger period and cutoff time usages must also be supported.

The logical minimum must be zero, and the logical maximum must be greater than zero. The logical maximum should be capped at a small value (for instance, 10). The physical range and units must be 0.

Retrigger Period (Optional)

This usage represents the duration in between retriggers of the waveform, measured from the start time of the previous trigger. A value of zero should be interpreted as identical to the default duration for the waveform, so the retrigger occurs immediately after the previous one completes. Values less than the default duration for the waveform should interrupt the waveform and restart it.

If this usage is supported, the repeat count and cutoff time usages must also be supported.

The host will treat the logical values as milliseconds. The logical minimum must be zero, and the logical maximum must be at least 1000 (representing one second). The physical range must either be zero or identical to the logical range. If the physical range is non-zero, the units should be milliseconds.

Waveform Cutoff Time (Optional)

This usage represents the maximum amount of time a single trigger can result in playback, taking into account the repeat count and retrigger period.

If this usage is supported, the repeat count and retrigger usages must also be supported.

The host will treat the logical values as milliseconds. The logical minimum must be at least as large as the duration of the longest discrete waveform, multiplied by the logical maximum of the repeat count usage. The physical range must either be zero or identical to the logical range. If the physical range is non-zero, the units should be milliseconds.

Haptic Touchpad Guidance

The items in this section apply only to haptic touchpads.

Device-Initiated Haptic Feedback

A haptic touchpad is responsible for triggering haptic feedback when it determines that the touchpad's surface button has been pressed or released. It can choose to support SET_FEATURE reports to allow user customization of its behavior when doing so:

  • The intensity of the haptic feedback
  • The force required to trigger a button press

Both of these feature reports are mandatory if the touchpad also supports host-initiated haptic feedback. Each report must use a distinct report ID, not used with any other usage.

During enumeration, the host will assess the supported logical and physical range from the descriptor and compute the exposed options for the settings UI including the defaults. The host shall issue the SET_FEATURE to communicate the user specified value to the device; this issuance may occur at any time, but shall occur whenever the setting is changed, a user switch occurs, and when the device is enumerated or resets.

Haptic Intensity Feature Report

This SET_FEATURE report specifies the user's preference for the intensity of the haptic feedback for button press and release. It does NOT apply to the intensity of any host-initiated feedback, if supported by the device. To support this configuration, the device must define a SimpleHapticsController logical child collection (Page 0x0E, Usage 0x01) in the Windows Precision Touchpad top-level collection, containing the Haptic Intensity usage (Page 0x0E, Usage 0x23) as a feature report with a dedicated report ID. This child collection must not contain the Auto Trigger (Page 0x0E, Usage 0x20) or Manual Trigger (Page 0x0E, Usage 0x21) usages.

The logical minimum must be equal to zero. The user's preference will be linearly scaled into the logical range, with zero indicating that no feedback should be triggered for button press and release.

Button Press Threshold Feature Report

This SET_FEATURE report specifies the user's preference for the amount of force required to trigger a button press. To support this configuration, the device must define the Button Press Threshold usage (Page 0x0D, Usage 0xB0) as a feature report with a dedicated report ID in the Windows Precision Touchpad top-level collection.

The logical range shall linearly map to the physical range of values, and be evenly spaced and centered around the default value. Upon acquiring the logical range, the default will be calculated using the following formula:

Diagram showing formula for calculating the default button press threshold in logical units

The Logical Minimum, Default, and Logical Maximum, will correspond to 3 distinct levels of button press force exposed to a user through Windows settings UI (supporting “Low”, “Medium”, and “High”, respectively).

The recommended physical range for Button Press Threshold is to at least cover the range between 110g and 190g, corresponding to the minimum and maximum values respectively. For a sample descriptor utilizing a Physical Maximum of 190g, and Physical Minimum of 110g (thus, based on the above formula the default would be 150g) see Sample Report Descriptors.

Sample HID Report Descriptors

Sample Haptic Touchpad Descriptor

The following descriptor supports all mandatory and optional usages. It declares support for five waveforms, with the longest having a duration of 50ms.

All logical ranges should be updated based on device support. To support a different number of waveforms:

  • The logical range of the Manual Trigger usage must be updated
  • The usage ranges and report count for Waveform List and Duration List must be updated

To support a different maximum waveform length, the following logical ranges must be updated:

  • Retrigger Period (Output)
  • Waveform Cutoff Time (Output)
  • Duration List (Feature)
0x05, 0x0D,       // UsagePage(Digitizers[0x000D])
0x09, 0x05,       // UsageId(Touch Pad[0x0005])
0xA1, 0x01,       // Collection(Application)
0x85, 0x40,       //  ReportId(64)
0x05, 0x0D,       //  UsagePage(Digitizers[0x000D])
0x09, 0xB0,       //  UsageId(Button Press Threshold[0x00B0])
0x35, 0x6E,       //  PhysicalMinimum(110)
0x46, 0xBE, 0x00, //  PhysicalMaximum(190)
0x66, 0x01, 0x01, //  Unit('gram', SiLinear, Gram:1)
0x55, 0x00,       //  UnitExponent(1)
0x15, 0x01,       //  LogicalMinimum(1)
0x25, 0x03,       //  LogicalMaximum(3)
0x95, 0x01,       //  ReportCount(1)
0x75, 0x08,       //  ReportSize(8)
0xB1, 0x02,       //  Feature(Data, Variable, Absolute)
0x85, 0x41,       //  ReportId(65)
0x05, 0x0E,       //  UsagePage(Haptics[0x000E])
0x09, 0x01,       //  UsageId(Simple Haptic Controller[0x0001])
0xA1, 0x02,       //  Collection(Logical)
0x05, 0x0E,       //   UsagePage(Haptics[0x000E])
0x09, 0x23,       //   UsageId(Intensity[0x0023])
0x35, 0x00,       //   PhysicalMinimum(0)
0x45, 0x00,       //   PhysicalMaximum(0)
0x65, 0x00,       //   Unit(None)
0x55, 0x00,       //   UnitExponent(1)
0x15, 0x00,       //   LogicalMinimum(0)
0x25, 0x04,       //   LogicalMaximum(4)
0x95, 0x01,       //   ReportCount(1)
0x75, 0x08,       //   ReportSize(8)
0xB1, 0x02,       //   Feature(Data, Variable, Absolute)
0xC0,             //  EndCollection()
0x85, 0x42,       //  ReportId(66)
0x05, 0x0E,       //  UsagePage(Haptics[0x000E])
0x09, 0x01,       //  UsageId(Simple Haptic Controller[0x0001])
0xA1, 0x02,       //  Collection(Logical)
0x05, 0x0E,       //   UsagePage(Haptics[0x000E])
0x09, 0x10,       //   UsageId(Waveform List[0x0010])
0xA1, 0x02,       //   Collection(Logical)
0x05, 0x0A,       //    UsagePage(Ordinal[0x000A])
0x19, 0x03,       //    UsageIdMin(Instance 3[0x0003])
0x29, 0x07,       //    UsageIdMax(Instance 7[0x0007])
0x35, 0x00,       //    PhysicalMinimum(0)
0x45, 0x00,       //    PhysicalMaximum(0)
0x65, 0x00,       //    Unit(None)
0x55, 0x00,       //    UnitExponent(1)
0x16, 0x03, 0x10, //    LogicalMinimum(4,099)
0x26, 0xFF, 0x2F, //    LogicalMaximum(12,287)
0x95, 0x05,       //    ReportCount(5)
0x75, 0x10,       //    ReportSize(16)
0xB1, 0x02,       //    Feature(Data, Variable, Absolute)
0xC0,             //   EndCollection()
0x05, 0x0E,       //   UsagePage(Haptics[0x000E])
0x09, 0x11,       //   UsageId(Duration List[0x0011])
0xA1, 0x02,       //   Collection(Logical)
0x05, 0x0A,       //    UsagePage(Ordinal[0x000A])
0x19, 0x03,       //    UsageIdMin(Instance 3[0x0003])
0x29, 0x07,       //    UsageIdMax(Instance 7[0x0007])
0x35, 0x00,       //    PhysicalMinimum(0)
0x45, 0x32,       //    PhysicalMaximum(50)
0x66, 0x01, 0x10, //    Unit('millisecond', SiLinear, Seconds:1)
0x55, 0x0D,       //    UnitExponent(0.001)
0x15, 0x00,       //    LogicalMinimum(0)
0x25, 0x32,       //    LogicalMaximum(50)
0x95, 0x05,       //    ReportCount(5)
0x75, 0x08,       //    ReportSize(8)
0xB1, 0x02,       //    Feature(Data, Variable, Absolute)
0xC0,             //   EndCollection()
0xC0,             //  EndCollection()
0x85, 0x43,       //  ReportId(67)
0x05, 0x0E,       //  UsagePage(Haptics[0x000E])
0x09, 0x01,       //  UsageId(Simple Haptic Controller[0x0001])
0xA1, 0x02,       //  Collection(Logical)
0x05, 0x0E,       //   UsagePage(Haptics[0x000E])
0x09, 0x21,       //   UsageId(Manual Trigger[0x0021])
0x35, 0x00,       //   PhysicalMinimum(0)
0x45, 0x00,       //   PhysicalMaximum(0)
0x65, 0x00,       //   Unit(None)
0x55, 0x00,       //   UnitExponent(1)
0x15, 0x01,       //   LogicalMinimum(1)
0x25, 0x07,       //   LogicalMaximum(7)
0x95, 0x01,       //   ReportCount(1)
0x75, 0x08,       //   ReportSize(8)
0x91, 0x02,       //   Output(Data, Variable, Absolute)
0x05, 0x0E,       //   UsagePage(Haptics[0x000E])
0x09, 0x23,       //   UsageId(Intensity[0x0023])
0x35, 0x00,       //   PhysicalMinimum(0)
0x45, 0x00,       //   PhysicalMaximum(0)
0x65, 0x00,       //   Unit(None)
0x55, 0x00,       //   UnitExponent(1)
0x15, 0x00,       //   LogicalMinimum(0)
0x25, 0x04,       //   LogicalMaximum(4)
0x95, 0x01,       //   ReportCount(1)
0x75, 0x08,       //   ReportSize(8)
0x91, 0x02,       //   Output(Data, Variable, Absolute)
0x05, 0x0E,       //   UsagePage(Haptics[0x000E])
0x09, 0x24,       //   UsageId(Repeat Count[0x0024])
0x35, 0x00,       //   PhysicalMinimum(0)
0x45, 0x00,       //   PhysicalMaximum(0)
0x65, 0x00,       //   Unit(None)
0x55, 0x00,       //   UnitExponent(1)
0x15, 0x00,       //   LogicalMinimum(0)
0x25, 0x05,       //   LogicalMaximum(5)
0x95, 0x01,       //   ReportCount(1)
0x75, 0x08,       //   ReportSize(8)
0x91, 0x02,       //   Output(Data, Variable, Absolute)
0x05, 0x0E,       //   UsagePage(Haptics[0x000E])
0x09, 0x25,       //   UsageId(Retrigger Period[0x0025])
0x35, 0x00,       //   PhysicalMinimum(0)
0x46, 0xE8, 0x03, //   PhysicalMaximum(1,000)
0x66, 0x01, 0x10, //   Unit('millisecond', SiLinear, Seconds:1)
0x55, 0x0D,       //   UnitExponent(0.001)
0x15, 0x00,       //   LogicalMinimum(0)
0x26, 0xE8, 0x03, //   LogicalMaximum(1,000)
0x95, 0x01,       //   ReportCount(1)
0x75, 0x10,       //   ReportSize(16)
0x91, 0x02,       //   Output(Data, Variable, Absolute)
0x05, 0x0E,       //   UsagePage(Haptics[0x000E])
0x09, 0x28,       //   UsageId(Waveform Cutoff Time[0x0028])
0x36, 0xE8, 0x03, //   PhysicalMinimum(1,000)
0x46, 0x88, 0x13, //   PhysicalMaximum(5,000)
0x66, 0x01, 0x10, //   Unit('millisecond', SiLinear, Seconds:1)
0x55, 0x0D,       //   UnitExponent(0.001)
0x16, 0xE8, 0x03, //   LogicalMinimum(1,000)
0x26, 0x88, 0x13, //   LogicalMaximum(5,000)
0x95, 0x01,       //   ReportCount(1)
0x75, 0x10,       //   ReportSize(16)
0x91, 0x02,       //   Output(Data, Variable, Absolute)
0xC0,             //  EndCollection()
0xC0,             // EndCollection()

The above descriptor was generated via the following Waratah file:

[[settings]]
packingInBytes = 1
optimize = false

[[unit]]
name = 'millisecond'
second = [0.001, 1.0]

[[applicationCollection]]
usage = ['Digitizers', 'Touch Pad']

 # Button press threshold feature report
 [[applicationCollection.featureReport]]
 id = 0x40

  [[applicationCollection.featureReport.variableItem]]
  usage = ['Digitizers', 'Button Press Threshold']
  logicalValueRange = [1, 3]
  physicalValueRange = [110, 190]
  unit = 'gram'

 # Feedback intensity feature report
 [[applicationCollection.featureReport]]
 id = 0x41

  [[applicationCollection.featureReport.logicalCollection]]
  usage = ['Haptics', 'Simple Haptic Controller']

   [[applicationCollection.featureReport.logicalCollection.variableItem]]
   usage = ['Haptics', 'Intensity']
   logicalValueRange = [0, 4]

 # Host-initiated waveform information feature report
 [[applicationCollection.featureReport]]
 id = 0x42

  [[applicationCollection.featureReport.logicalCollection]]
  usage = ['Haptics', 'Simple Haptic Controller']

   [[applicationCollection.featureReport.logicalCollection.logicalCollection]]
   usage = ['Haptics', 'Waveform List']

    [[applicationCollection.featureReport.logicalCollection.logicalCollection.variableItem]]
    usageRange = ['Ordinal', 'Instance 3', 'Instance 7']
    logicalValueRange = [0x1003, 0x2FFF]

   [[applicationCollection.featureReport.logicalCollection.logicalCollection]]
   usage = ['Haptics', 'Duration List']

    [[applicationCollection.featureReport.logicalCollection.logicalCollection.variableItem]]
    usageRange = ['Ordinal', 'Instance 3', 'Instance 7']
    logicalValueRange = [0, 50]
    physicalValueRange = [0, 50]
    unit = 'millisecond'

 # Host-initiated waveform manual trigger output report
 [[applicationCollection.outputReport]]
 id = 0x43

  [[applicationCollection.outputReport.logicalCollection]]
  usage = ['Haptics', 'Simple Haptic Controller']

   [[applicationCollection.outputReport.logicalCollection.variableItem]]
   usage = ['Haptics', 'Manual Trigger']
   logicalValueRange = [1, 7]

   [[applicationCollection.outputReport.logicalCollection.variableItem]]
   usage = ['Haptics', 'Intensity']
   logicalValueRange = [0, 4]

   [[applicationCollection.outputReport.logicalCollection.variableItem]]
   usage = ['Haptics', 'Repeat Count']
   logicalValueRange = [0, 5]

   [[applicationCollection.outputReport.logicalCollection.variableItem]]
   usage = ['Haptics', 'Retrigger Period']
   logicalValueRange = [0, 1000]
   physicalValueRange = [0, 1000]
   unit = 'millisecond'

   [[applicationCollection.outputReport.logicalCollection.variableItem]]
   usage = ['Haptics', 'Waveform Cutoff Time']
   logicalValueRange = [1000, 5000]
   physicalValueRange = [1000, 5000]
   unit = 'millisecond'

Sample Haptic Mouse Descriptor

The following descriptor supports all mandatory and optional usages. It declares support for eight waveforms, with the longest having a duration of 200ms.

All logical ranges should be updated based on device support. To support a different number of waveforms:

  • The logical range of the Manual Trigger usage must be updated
  • The usage ranges and report count for Waveform List and Duration List must be updated

To support a different maximum waveform length, the following logical ranges must be updated:

  • Retrigger Period (Output)
  • Waveform Cutoff Time (Output)
  • Duration List (Feature)
0x05, 0x01,       // UsagePage(Generic Desktop[0x0001])
0x09, 0x02,       // UsageId(Mouse[0x0002])
0xA1, 0x01,       // Collection(Application)
0x85, 0x01,       //  ReportId(1)
0x09, 0x01,       //  UsageId(Pointer[0x0001])
0xA1, 0x00,       //  Collection(Physical)
0x09, 0x30,       //   UsageId(X[0x0030])
0x09, 0x31,       //   UsageId(Y[0x0031])
0x15, 0x80,       //   LogicalMinimum(-128)
0x25, 0x7F,       //   LogicalMaximum(127)
0x95, 0x02,       //   ReportCount(2)
0x75, 0x08,       //   ReportSize(8)
0x81, 0x06,       //   Input(Data, Variable, Relative)
0x05, 0x09,       //   UsagePage(Button[0x0009])
0x19, 0x01,       //   UsageIdMin(Button 1[0x0001])
0x29, 0x03,       //   UsageIdMax(Button 3[0x0003])
0x15, 0x00,       //   LogicalMinimum(0)
0x25, 0x01,       //   LogicalMaximum(1)
0x95, 0x03,       //   ReportCount(3)
0x75, 0x01,       //   ReportSize(1)
0x81, 0x02,       //   Input(Data, Variable, Absolute)
0xC0,             //  EndCollection()
0x95, 0x01,       //  ReportCount(1)
0x75, 0x05,       //  ReportSize(5)
0x81, 0x03,       //  Input(Constant, Variable, Absolute)
0xC0,             // EndCollection()
0x05, 0x0E,       // UsagePage(Haptics[0x000E])
0x09, 0x01,       // UsageId(Simple Haptic Controller[0x0001])
0xA1, 0x01,       // Collection(Application)
0x85, 0x10,       //  ReportId(16)
0x09, 0x10,       //  UsageId(Waveform List[0x0010])
0xA1, 0x02,       //  Collection(Logical)
0x05, 0x0A,       //   UsagePage(Ordinal[0x000A])
0x19, 0x03,       //   UsageIdMin(Instance 3[0x0003])
0x29, 0x0A,       //   UsageIdMax(Instance 10[0x000A])
0x16, 0x03, 0x10, //   LogicalMinimum(4,099)
0x26, 0xFF, 0x2F, //   LogicalMaximum(12,287)
0x95, 0x08,       //   ReportCount(8)
0x75, 0x0E,       //   ReportSize(14)
0xB1, 0x02,       //   Feature(Data, Variable, Absolute)
0xC0,             //  EndCollection()
0x05, 0x0E,       //  UsagePage(Haptics[0x000E])
0x09, 0x11,       //  UsageId(Duration List[0x0011])
0xA1, 0x02,       //  Collection(Logical)
0x05, 0x0A,       //   UsagePage(Ordinal[0x000A])
0x19, 0x03,       //   UsageIdMin(Instance 3[0x0003])
0x29, 0x0A,       //   UsageIdMax(Instance 10[0x000A])
0x46, 0xC8, 0x00, //   PhysicalMaximum(200)
0x66, 0x01, 0x10, //   Unit('millisecond', SiLinear, Seconds:1)
0x55, 0x0D,       //   UnitExponent(0.001)
0x15, 0x00,       //   LogicalMinimum(0)
0x26, 0xC8, 0x00, //   LogicalMaximum(200)
0x75, 0x08,       //   ReportSize(8)
0xB1, 0x02,       //   Feature(Data, Variable, Absolute)
0xC0,             //  EndCollection()
0x85, 0x11,       //  ReportId(17)
0x05, 0x0E,       //  UsagePage(Haptics[0x000E])
0x09, 0x21,       //  UsageId(Manual Trigger[0x0021])
0x45, 0x00,       //  PhysicalMaximum(0)
0x65, 0x00,       //  Unit(None)
0x55, 0x00,       //  UnitExponent(1)
0x15, 0x01,       //  LogicalMinimum(1)
0x25, 0x0A,       //  LogicalMaximum(10)
0x95, 0x01,       //  ReportCount(1)
0x75, 0x04,       //  ReportSize(4)
0x91, 0x02,       //  Output(Data, Variable, Absolute)
0x09, 0x23,       //  UsageId(Intensity[0x0023])
0x15, 0x00,       //  LogicalMinimum(0)
0x25, 0x04,       //  LogicalMaximum(4)
0x75, 0x03,       //  ReportSize(3)
0x91, 0x02,       //  Output(Data, Variable, Absolute)
0x09, 0x24,       //  UsageId(Repeat Count[0x0024])
0x25, 0x05,       //  LogicalMaximum(5)
0x91, 0x02,       //  Output(Data, Variable, Absolute)
0x09, 0x25,       //  UsageId(Retrigger Period[0x0025])
0x46, 0xE8, 0x03, //  PhysicalMaximum(1,000)
0x66, 0x01, 0x10, //  Unit('millisecond', SiLinear, Seconds:1)
0x55, 0x0D,       //  UnitExponent(0.001)
0x26, 0xE8, 0x03, //  LogicalMaximum(1,000)
0x75, 0x0A,       //  ReportSize(10)
0x91, 0x02,       //  Output(Data, Variable, Absolute)
0x09, 0x28,       //  UsageId(Waveform Cutoff Time[0x0028])
0x36, 0xE8, 0x03, //  PhysicalMinimum(1,000)
0x46, 0x88, 0x13, //  PhysicalMaximum(5,000)
0x16, 0xE8, 0x03, //  LogicalMinimum(1,000)
0x26, 0x88, 0x13, //  LogicalMaximum(5,000)
0x75, 0x0D,       //  ReportSize(13)
0x91, 0x02,       //  Output(Data, Variable, Absolute)
0x75, 0x07,       //  ReportSize(7)
0x91, 0x03,       //  Output(Constant, Variable, Absolute)
0xC0,             // EndCollection()

The above descriptor was generated via the following Waratah file:

[[unit]]
name = 'millisecond'
second = [0.001, 1.0]

[[applicationCollection]]
usage = ['Generic Desktop', 'Mouse']

 # Mouse
 [[applicationCollection.inputReport]]

  [[applicationCollection.inputReport.physicalCollection]]
  usage = ['Generic Desktop', 'Pointer']

   [[applicationCollection.inputReport.physicalCollection.variableItem]]
   usage = ['Generic Desktop', 'X']
   sizeInBits = 8
   logicalValueRange = 'maxSignedSizeRange'
   reportFlags = ['relative']

   [[applicationCollection.inputReport.physicalCollection.variableItem]]
   usage = ['Generic Desktop', 'Y']
   sizeInBits = 8
   logicalValueRange = 'maxSignedSizeRange'
   reportFlags = ['relative']

   [[applicationCollection.inputReport.physicalCollection.variableItem]]
   usageRange = ['Button', 'Button 1', 'Button 3']
   logicalValueRange = [0, 1]

[[applicationCollection]]
usage = ['Haptics', 'Simple Haptic Controller']

 # Host-initiated waveform information feature report
 [[applicationCollection.featureReport]]
 id = 0x10

  [[applicationCollection.featureReport.logicalCollection]]
  usage = ['Haptics', 'Waveform List']

   [[applicationCollection.featureReport.logicalCollection.variableItem]]
   usageRange = ['Ordinal', 'Instance 3', 'Instance 10']
   logicalValueRange = [0x1003, 0x2FFF]

  [[applicationCollection.featureReport.logicalCollection]]
  usage = ['Haptics', 'Duration List']

   [[applicationCollection.featureReport.logicalCollection.variableItem]]
   usageRange = ['Ordinal', 'Instance 3', 'Instance 10']
   logicalValueRange = [0, 200]
   physicalValueRange = [0, 200]
   unit = 'millisecond'

 # Host-initiated waveform manual trigger output report
 [[applicationCollection.outputReport]]
 id = 0x11

  [[applicationCollection.outputReport.variableItem]]
  usage = ['Haptics', 'Manual Trigger']
  logicalValueRange = [1, 10]

  [[applicationCollection.outputReport.variableItem]]
  usage = ['Haptics', 'Intensity']
  logicalValueRange = [0, 4]

  [[applicationCollection.outputReport.variableItem]]
  usage = ['Haptics', 'Repeat Count']
  logicalValueRange = [0, 5]

  [[applicationCollection.outputReport.variableItem]]
  usage = ['Haptics', 'Retrigger Period']
  logicalValueRange = [0, 1000]
  physicalValueRange = [0, 1000]
  unit = 'millisecond'

  [[applicationCollection.outputReport.variableItem]]
  usage = ['Haptics', 'Waveform Cutoff Time']
  logicalValueRange = [1000, 5000]
  physicalValueRange = [1000, 5000]
  unit = 'millisecond'