WebHID API: Control Everything via USB

Nikita Dubko, Web Standards

WebHID API: Control Everything via USB*

Nikita Dubko, Web Standards

* not only USB

Nikita
Dubko

GDE Directory

Knowledge 🪄

How it all
started...

Frontend is not real programming!!!
Somebody on Twitter
You don't even have access to real devices!
Somebody on Twitter

Really? 🤔

Somewhere in C++

HID

usb.org

Human
Interface
Devices

USB
&
Bluetooth HID-Class

USB
&
Bluetooth HID-Class

Drivers

Drivers

#include "clisrv.h"
#include "device.h"
#include "server.h"
#if defined(EVENT_TRACING)
#include "server.tmh"
#endif
_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS
BthEchoSrvQueryInterfaces(_In_ PBTHECHOSAMPLE_SERVER_CONTEXT DevCtx);
#ifdef ALLOC_PRAGMA
#pragma alloc_text (PAGE, BthEchoSrvInitialize)
#pragma alloc_text (PAGE, BthEchoSrvUnregisterPSM)
#pragma alloc_text (PAGE, BthEchoSrvUnregisterL2CAPServer)
#pragma alloc_text (PAGE, BthEchoSrvPublishSdpRecord)
#pragma alloc_text (PAGE, BthEchoSrvRemoveSdpRecord)
#pragma alloc_text (PAGE, BthEchoSrvQueryInterfaces)
#endif

void InsertConnectionEntryLocked(
    PBTHECHOSAMPLE_SERVER_CONTEXT devCtx,
    PLIST_ENTRY ple
    )
{
    WdfSpinLockAcquire(devCtx->ConnectionListLock);
    InsertTailList(&devCtx->ConnectionList, ple);
    WdfSpinLockRelease(devCtx->ConnectionListLock);
}
microsoft / Windows-driver-samples

Polling

125 Hz

125 times per second

60 FPS

Connection

Enumeration

Poll

Have something?

Give it to me

Input Reports,
Feature Reports, and
Output Reports

Push

SET_REPORT

Push

SET_FEATURE

What is report?

Offset(d) 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15

00000000  01 7d 80 7f 80 08 00 f0 00 00 98 5b fd 07 00 57
00000016  00 9b fe 8d 0d 1b 1f 6d 05 00 00 00 00 00 1b 00
00000032  00 01 2b a9 b8 41 16 ab 86 e5 12 00 80 00 00 00
00000048  80 00 00 00 00 80 00 00 00 80 00 00 00 00 80 00
Offset(d) 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15

00000000  01 7e 81 7f 7e 08 00 bc 00 00 17 a1 fd fd ff 01
00000016  00 01 00 8d 00 a3 20 d1 07 00 00 00 00 00 1b 00
00000032  00 02 3c 0b 5b 94 1a 87 de c0 1b 41 0b 5e 94 1a
00000048  87 de c0 1b 00 80 00 00 00 80 00 00 00 00 80 00
Device Class Definition for Human Interface Devices
Device Class Definition for Human Interface Devices
How to Sniff USB Traffic/Reverse Engineer USB Device Interactions
libusb / hidapi

HID is already
in browsers!

services / device / public / mojom / hid.mojom

const uint16 kGenericDesktopUndefined = 0x00;
const uint16 kGenericDesktopPointer = 0x01;
const uint16 kGenericDesktopMouse = 0x02;
const uint16 kGenericDesktopJoystick = 0x04;
const uint16 kGenericDesktopGamePad = 0x05;
const uint16 kGenericDesktopKeyboard = 0x06;
const uint16 kGenericDesktopKeypad = 0x07;
const uint16 kGenericDesktopMultiAxisController = 0x08;
const uint16 kGenericDesktopX = 0x30;
const uint16 kGenericDesktopY = 0x31;
const uint16 kGenericDesktopZ = 0x32;
const uint16 kGenericDesktopRx = 0x33;
const uint16 kGenericDesktopRy = 0x34;
const uint16 kGenericDesktopRz = 0x35;
const uint16 kGenericDesktopSlider = 0x36;
const uint16 kGenericDesktopDial = 0x37;
const uint16 kGenericDesktopWheel = 0x38;
const uint16 kGenericDesktopHatSwitch = 0x39;
const uint16 kGenericDesktopCountedBuffer = 0x3a;
const uint16 kGenericDesktopByteCount = 0x3b;
const uint16 kGenericDesktopMotionWakeup = 0x3c;
const uint16 kGenericDesktopStart = 0x3d;
const uint16 kGenericDesktopSelect = 0x3e;
const uint16 kGenericDesktopVx = 0x40;
...
Chromium Source Code

Human
Interface
Devices

WebHID

Browser API

Is NOT a W3C Standard

WebHID API

Chrome 89+ ✅

Chrome Platform Status

Mozilla 🚫

mozilla / standards-positions

Safari 🚫

Tracking Prevention in WebKit

Doesn't work with
trusted input

bool IsAlwaysProtected(const mojom::HidUsageAndPage& hid_usage_and_page) {
    const uint16_t usage = hid_usage_and_page.usage;
    const uint16_t usage_page = hid_usage_and_page.usage_page;

    if (usage_page == mojom::kPageKeyboard)
        return true;
    if (usage_page != mojom::kPageGenericDesktop)
        return false;
    if (usage == mojom::kGenericDesktopPointer ||
        usage == mojom::kGenericDesktopMouse ||
        usage == mojom::kGenericDesktopKeyboard ||
        usage == mojom::kGenericDesktopKeypad) {
        return true;
    }
    if (usage >= mojom::kGenericDesktopSystemControl &&
        usage <= mojom::kGenericDesktopSystemWarmRestart) {
        return true;
    }
    if (usage >= mojom::kGenericDesktopSystemDock &&
        usage <= mojom::kGenericDesktopSystemDisplaySwap) {
        return true;
    }

    return false;
}
Chromium Source Code

Requires
user gesture
to allow

How to work with HID?

navigator.hid

if ("hid" in navigator) {
    const opts = {
        filters: [
            {
                vendorId: 0x0fd9,
                productId: 0x006d
            }
        ]
    };
    const devices = await navigator.hid.requestDevice(opts);
}

about://device-log

devicehunt.com

All USB Vendors
const device = devices[0];

await device.open();

device.addEventListener("inputreport", (event) => {
    const { data, device, reportId } = event;

    if (reportId === 0x01) { // <== You need specs here
        // Do something
    }
});

DataView

DS4-USB
const buttonsLeftRight = data.getUint8(4);
const dPad = buttonsLeftRight & 0x0f;
const additionalButtons = data.getUint8(5);
const serviceButtons = data.getUint8(6);

const buttons = {
    triangle: !!(buttonsLeftRight & 0x80),
    circle: !!(buttonsLeftRight & 0x40),
    cross: !!(buttonsLeftRight & 0x20),
    square: !!(buttonsLeftRight & 0x10),

    up: dPad === 7 || dPad === 0 || dPad === 1,
    right: dPad === 1 || dPad === 2 || dPad === 3,
    down: dPad === 3 || dPad === 4 || dPad === 5,
    left: dPad === 5 || dPad === 6 || dPad === 7,
    // ...
};
const report = new Uint8Array(16);

// Report ID
report[0] = 0x05;

// Enable Rumble (0x01), Lightbar (0x02)
report[1] = 0xf0 | 0x01 | 0x02;

// Light / Heavy rumble motor
report[4] = 64;
report[5] = 127;

// Lightbar Red / Green / Blue
report[6] = 255;
report[7] = 0;
report[8] = 0;

return device.sendReport(report[0], report.slice(1));

Demo Time 🎮

WebHID-DS4

🤔

Stream Deck

Elgato Stream Deck

Pete LePage
+
StreamDeck
+
Google Meet
=

petele / StreamDeck-Meet

Bramus Van Damme
+
StreamDeck Helper
=

WebHID Demo: Elgato Stream Deck Daft Punk Soundboard
Stream Deck Protocol

Demo Time 🎸

device.close()

What's next?

Awesome WebHID
Connecting to uncommon HID devices

Frontend Power!

mefody.dev/talks/webhid/
@dark_mefody
n.a.dubko@gmail.com QR-код со ссылкой на доклад