►
From YouTube: William Light - Linux USB HID Interfacing in Userspace
Description
Detailing the process of writing a userspace driver for a USB human-interface device in Rust using mio. Will briefly touch on reverse-engineering undocumented HID protocols and MIDI devices.
---
For more go to https://rustfest.eu or follow us on Twitter: https://twitter.com/rustfest
A
B
So
about
two
hours
ago,
I
had
to
patch
my
presentation,
software
cuz.
It
wasn't
working
so
we're
gonna
hope
that
everything
holds
together.
So
hey
my
name
is:
will
I
write
software,
my
backgrounds
in
like
C
low
level
system
stuff?
You
probably
don't
know
anything
about
me,
but
if
you
do
it's
because
of
this
thing
here,
this
is
a
mono
and
grid
128
Mon
ohms
there
are
indie
music
hardware,
basically
hold
Box
buttons
that
light
up,
and
you
can
configure
in
program
it
to
do
whatever
you
want.
B
I
have
been
developing
and
maintaining
the
drivers
for
this
for
the
past
five
years.
I
never
think
about
this
is
open
source.
It's
really
cool
we're
talking
about
drivers
today,
so
normally
when
people
think
about
drivers,
they
think
about
drivers
and
kernel-space,
and
the
driver
handles
low-level
details
of
hardware
and
then
it
kind
of
hoists
user
code
up
to
a
higher
level
of
abstraction
interpreting
some
sort
of
low-level
protocol
bytes
from
some
source
and
then
translating
that
to
a
higher-level
interface.
B
So,
for
example,
a
keyboard
or
a
mouse
will
send
some
bytes
and
then
the
kernel
will
interpret
it
and
then
output.
It
on
sort
of
the
input
subsystem
if
we're
talking
about
Linux,
so
the
benefit
here,
of
course,
is
that
you
can
abstract
away
some
of
the
underlying
transport,
so
USB
ps2
trackpad
whatever,
but
drivers
don't
have
to
exist
just
in
the
kernel
today,
we're
gonna
be
talking
about
drivers
and
user
space
so
still
doing
roughly
the
same
thing,
but
since
we're
in
user
space,
we
don't
have
to
worry
about
causing
kernel
panics.
B
We
don't
have
to
worry
about
ya
breaking
the
system
if
we
happen
to
hit
a
seg
fault,
which
of
course
we
won't
be
doing
today
today.
Excuse
me,
the
performance
is
slightly
lower.
We
don't
have
like
we
can't
dip
in
do
real-time
stuff.
If
we
need
to
move
a
lot
of
audio.
For
example,
that's
kind
of
my
background.
That's
gonna
be
a
little
bit
more
difficult
to
do,
but
we
don't
have
quite
a
steep
a
learning
curve,
so
we're
gonna
be
writing
a
user
space
driver
for
this
thing
right
here.
This
is
a
native
instruments.
B
Maschine
mikro
mark
2.
It
has
these
kind
of
16
pressure-sensitive
pads
here
for
like
finger
drumming
stuff
like
that,
a
bunch
of
buttons
screen
everything
lights
up.
So
this
is
a
kind
of
purpose-built
hardware
and
Native
Instruments
shift
software
for
Windows
and
Mac.
That
has
really
tight
integration,
but
I
don't
have
any
computers
that
do
Windows
or
Mac.
So
I
wanted
to
figure
out
how
to
make
this
thing
work.
So
the
game
plan
for
today
is
first
we're
gonna
cover
at
the
table
of
contents.
Naturally,
we're
gonna
figure
out
how
to
reverse
engineer
this
thing.
B
We're
gonna,
write
the
driver
and
then
we're
gonna
kind
of
revisit
and
see
how
far
we've
come
and
how
much
we've
grown
as
people,
so
reverse
engineering.
First,
let's
touch
on
the
concept
of
the
USB
human
interface
devices.
So
USB
is
a
fairly
complex
protocol.
We
don't
have
to
worry
about
that.
Really
we're
just
moving
bytes
around
so
hid
devices.
It's
one
of
those
human
interface
device
devices
communicate
via
reports
and
a
report
is
basically
just
a
message.
B
B
But,
of
course,
USB
HID
devices
can
also
just
be
raw.
They
can
be
vendor
specific
and
that's
what
this
guy
up
here
is.
So
I'm
really
rushing
through
this
right
now,
I
thought
this
was
gonna
take
longer,
but
it's
okay,
it's
okay,
we're
behind
schedule
getting
us
back
on
track,
okay,
so
a
number
of
different
ways
that
we
can
reverse
engineer
it
we're
gonna,
go
through
each
of
these
in
turn.
There's
the
easy
bin
a
complete
way.
B
We
can
get
slightly
dodgy
look
at
the
way
that
professionals
do
it
when
they
have
a
lot
of
money
and
then
look
at
kind
of
a
really
a
really
fun
hack.
That
I
didn't
know
that
you
could
do
so.
The
easy
way
to
do
this
is
we're
gonna
create
a
little
rust
program
that
just
opens
the
device
and
then
dumps
data
from
it.
Then
we're
gonna
fiddle
around
with
the
device
and
see
what
bites
come
out.
B
B
So
we're
going
to
have
some
very
bountiful
output
here
because
that's
the
wrong
monitor
but
yep,
okay.
So
here's
what
our
device
looks
like
when
we
open
it
up-
and
this
is
just
this
kind
of
caught
me
by
surprise
when
I
first
started
doing
it,
but
you'll
notice
that
if
we
start
pressing
down,
we
get
some
of
these
button
or
we
get
some
of
these
bytes
that
start
to
change
and
you'll
notice
that
as
we
go
down
the
line,
we
start
up
here
so
we're.
B
Basically,
this
was
dumping
pressure
values
as
fast
as
it
can
send
them
quit
that
back
so
much
yeah,
it's
a
ton
of
output,
but
that
only
gives
us
the
input
messages
from
the
device
to
the
computer.
This
has
a
bunch
of
lights
on
it.
We
need
to
know
how
to
do
that,
so
the
method
that
I
used,
which
I
don't
necessarily
recommend
because
I
don't
know
how
well
Hardware
will
deal
with
it.
B
If
you
just
fuzz
it,
you
just
throw
as
much
data
at
the
device
as
you
possibly
can
and
wait
for
it
to
do
something.
When
I
ran
this,
all
the
lights
lit
up
and
I
was
like
alright
cool,
so
we're
done
here,
we're
done
here
and
then
I
literally
just
put
a
sleep
in
and
then
I
had
to
mix
to
my
computer
and
I
would
just
run
it
and
then,
when
the
lights
came
on,
I'd
be
I,
would
stop
it
there
and
then
restart
it
and
then
just
narrow
it
down.
B
I
found
out
afterwards
that
during
USB
enumeration
hit,
devices
are
required
to
specify
what
reports
they
receive
and
how
long
that
report
is,
but
that's
not
as
much
fun.
So,
let's
just
do
it.
Let's
just
do
it
this
way,
but
if
you
remember,
there's
a
screen
here
and
that's
we're
not
likely
to
figure
out
how
to
run
that
just
by
throwing
bytes
at
it,
there's
gonna
be
something
more
complicated.
So,
let's
move
on
to
some
slightly
more
proper
methods
of
reversing,
unfortunately
proper
also
translates
to
expensive.
B
Now,
from
what
I
understand
total
face,
which
makes
the
Beagle
these
are,
you
know
top-of-the-line
products
and
they're
fantastic,
but
that
I
I'm
not
gonna
drop,
and
you
know
we'd
only
need
this
one,
so
we'd
only
need
to
drop
fourteen
hundred,
but
I
don't
have
that
for
like
a
weekend
project.
This
was
like
three
hundred
I'm
not
going
to
reverse
engineer
enough
things.
So
really
what
we
need
is.
We
need
a
computer
that
communicates
with
the
device
using
the
official
software
and
then
we
need
some
way
of
sniffing
it.
B
So
since
we
live
in
the
future,
we
can
actually
run
a
computer
inside
of
our
computer
and
then
man
in
the
middle
of
the
USB
connection
from
there.
So
that's
exactly
what
we're
gonna
do:
we're
gonna
open
up
a
Windows,
VM
and
VirtualBox
and
then
we're
gonna.
Do
this
move
USB
Mon
is
a
Linux
kernel
interface
that
basically
just
lets
you
tap
each
individual
USB
bus,
naturally
using
wireshark,
so
I'm
serious,
I'm,
serious
I
took
these
screenshots
on
Thursday.
B
So,
as
you
can
see,
we've
got
the
right
device
here.
I
just
made
a
little
filter
expression,
so
we
can
make
sure
we've
got
the
right
one
and
then
let's
go
back
and
take
a
look
at
this.
So
this
is
what
the
the
button
message
looks
like,
and
this
is
basically
just
every
time
you
press
a
button
on
this.
B
I
was
I
was
toggling
a
button
on
and
off
we
get
the
same
thing
here
and
then
what
we
need
is
we
need
the
response
to
this
lighting
up
the
button
from
the
official
software,
and
here
we
have
it
and
then
we
have
another
one
and
unfortunately,
the
light
that
I
want
is
kind
of
obscured
by
this
seam
in
the
screen.
But
you
can
see
so
we've
got
that
on
enough.
B
B
That's
kind
of
used
for
like
bespoke
interfaces
between
multimedia
software
and
hardware,
it's
UDP
for
low
latency
and
then
the
outputs
are
we're
gonna,
send
MIDI,
which,
if
you
don't
know,
Middies
super
old-school
communication
between
sequencers
and
synthesizers,
like
five
pinned
in
cables,
and
then
it
just
got
used
on
computers
because
it
was
there
and
we've
been
stuck
with
it
ever
since
so,
output,
MIDI
and
then
output
OSC
as
well.
So
quick
note
on
OSC,
it's
like
slightly
rusty
looking,
but
the
serialization
format
is
different.
It
has
argument
types
inline.
B
It's
really
cool
it,
just
kind
of
sucks
as
inter-process
communication,
which
seems
to
be
all
that
anybody
ever
uses
it
for
and,
coincidentally,
what
we're
also
gonna
be
using
it
for,
but
you
use
what
you
have
so,
let's
start
building
the
driver.
Let's
revisit
opening
the
devices
so
same
thing
that
we
were
doing
except
you'll
notice
that
we're
opening
our
hydro
device
non-block
this
time
and
then
we're
just
using
stock
standard
net
UDP
socket
and
then
we're
gonna
dip
into
Nix
again
and
construct
a
pole
loop.
And
this
is
so.
B
But
it's
not
quite
as
nice.
We're
also
going
to
construct
a
timer
here
because
we
don't
want
to
send
LED
responses
to
the
device.
Quite
so
fast.
We
end
up
kind
of
overloading
it,
and
then
things
start
to
lag
and
it's
not
as
much
fun
but
pretty
straightforward,
Poul
type
loop
here
and
then
so
I
could
just
leave
it
here
and
say
and
then
everything
else
is
like
protocol-specific,
but
I'm
I
thought
this
was
gonna,
take
five
minutes
longer,
so
I'll
just
kind
of
get
into
talking
about
the
buttons.
B
There's
a
couple:
crafty
little
algorithms
that
I
stumbled
upon
here's.
So
what
I
did
since
we
have
a
bitmap
of
buttons
and
we
just
need
to
know
what's
set
and
what's
not
and
each
button
report
I
have
an
enum
and
then
I
have
kind
of
a
lookup
array
here
and
then
every
time
we
receive
a
button
report
I
do
this
little
dance
where
I've
saved
the
last
one.
So
this
self
buttons
is
the
is
the
previous
button
state
and
I
exclusive-or
that
and
then
this
dysfunction
on
line
five
trailing
zeros.
B
I
can
essentially
use
that
to
skip
through
a
bit
field
and
find
all
of
the
ones,
and
then,
if
there
are
no
one,
so
if
you've
got
a
complete,
if
you've
just
got
zeros
in
your
bed
field,
it
becomes
kind
of
a
no
op.
So
you
can.
This
is
one
way
that
if
you
ever
need
to
kind
of
like
catch
the
dirty
state
of
a
set,
for
example,
if
you
can
stuff
it
into
a
bit
field,
then
you
can
minimize
the
amount
of
work
that
you
do.
B
You
can
just
kind
of
trailing
zeros
find
the
one
trailing
zeros
find
the
one
trailing
zeros
find
the
one,
and
then
the
pads
are
a
little
bit
more
complicated.
As
you
saw,
we
just
get
raw
pressure
value
from
the
device
and
even
worse
than
that,
it's
not
be
bounced
very
well
and
I'll
describe
it
in
a
second
I'm.
Just
gonna
start
up
the
actual
program
I
used
here,
okay
cool!
So
now,
we've
got
now.
B
B
So
we
have
to
do
some
sort
of
filtering
of
the
data
in
order
to
work
with
it
and
even
more
than
that,
like
okay,
when
you
think
about
this
from
a
playability
perspective
as
it's
like
a
finger
drummer
or
somebody
trying
to
program
in
beats,
you're
gonna
want
to
set
a
threshold
like
these.
You
obviously
don't
want
a
step
to
trigger
if
you
press
it
too
lightly,
and
then
you
want
pretty
decent
pressure
gradient
up
to
the
maximum
that
you're
gonna
touch
and
linear
exponential,
that's
usually
configurable.
B
B
So,
even
if
you
press
too
faintly
to
trigger
a
note
and
then
you
press
harder,
it
still
tracks
that
so
we
end
up
down
here
in
this
pressed
below
threshold
state
and
otherwise
we
end
up
in
in
the
fresh
depressed
above
threshold
state,
and
then
we
also
send
MIDI
after
touch,
which
is
kind
of
inside
of
a
note.
You
can
vary
the
pressure
up
and
down.
B
So
future
work
there's
this
there's
this
screen
on
it
and
I
figured
out
how
to
make
it
work.
I
ended
up
dumping.
The
protocol.
This
picture
is
kind
of
a
lie.
Actually
the
program
that
puts
that
on
there's
written
in
C,
so
I,
don't
know
yeah
yeah
bring
it
bring.
It
come
on,
come
on,
come
on
come
on,
but
it
doesn't
seg
fault.
B
So
I'm
good
I
wanted
to
do
this
in
rust,
but
I
wanted
to
have
like
a
nice
graphics,
a
few
get
API
for
doing
that
and
I
looked
at
like
GFX
RS,
but
I
couldn't
find
one
that
had
just
a
simple
one
bit
backing
store
that
I
could
just
draw
some
lines
and
like
blitz
stuff
on
so
I
gave
up
and
I
just
put
it
on
the
back
burner.
So
you
know
I'll
pick
that
up
at
some
point,
so
some
parting
thoughts,
there's
actually
so.
B
Besides
the
low-level
binding
to
the
Linux
MIDI
sequencer
interface,
there's
actually
only
one
block
of
unsafe
code
in
this
entire
user
space
driver
and
that's
just
because
I
was
too
lazy.
I
just
wanted
to
take
this.
You
a
buff.
If
you
remember
back
when
we
saw
that
huge
block
of
pressure
values-
they're
basically
little
endian
u16s,
so
I'll
treat
him
like
it.
B
B
But
then,
when
I
upgraded
from
0.3
to
0.5
and
rewrote
the
API,
I
got
an
additional
15
to
25
milliseconds
of
latency,
and
that
can
be
a
problem
for
these
types
of
applications,
because
10
milliseconds
is
about
the
maximum
that
you
can
have
for
a
person
to
consider
the
response
to
an
action
as
being
the
same
action
beyond
10
milliseconds
you,
you
can
perceive
the
delay
and
really
you
want
closer
to
5
to
7.
Milliseconds
I
haven't
actually
measure
how
quickly
the
pad
reports
come
in,
but
I've
ballparked
it.
B
But
you
know
1
to
2
to
3
milliseconds.
They
come
in
very
quickly,
so
having
an
additional
15
to
25.
Milliseconds
of
latency
is
kind
of
unacceptable.
I
reported
the
bug.
They
thought
that
it
was
because
of
TCP
no
delay.
We
don't
have
any
TCP
sockets
here,
I,
don't
know
something
to
continue
investigating
I'm
sure.
So
I
want
to
come
back
to
this
for
a
second,
because
I
can
kind
of
do
a
retrospective
on
the
code
that
I
wrote
for
the
mono.
B
So
when
I
originally
wrote,
this
I
used
the
low-level
event:
loop
primitives
for
all
of
the
operating
systems,
so
I
was
using
wait
for
multiple
events
on
Windows
poll
on
Linux
and
select
on
OS
10,
and
the
astute
attendees
may
be
wondering
why
didn't
I
just
use
poll
on
both
Linux
and
OS
10.
That's
because
on
Linux
you
can't
select
on
a
USB
serial
device
and
on
OS
10.
B
You
can't
pull
on
one
welcome
to
cross-platform
low-level
programming,
but
about
a
year
and
a
half
ago,
I
tried
to
refactor
this
and
use
libuv
for
all
of
my
underlying
I/o.
It
worked
fine
on
Linux
and
broke
entirely
on
Windows
and
OS
10,
and
it's
if
you're
gonna
be
writing
code
at
this
level.
I
think
that
it
pays
off
to
actually
know
the
api's
that
you're
sitting
on
top
I
went
and
I
took
a
look.
So
cereal
ask
is
about
five
thousand
lines
of
code.
B
Most
of
its
platform-independent
windows,
of
course,
is
the
most
special
of
all
of
the
platforms
and
then
there's
not
actually
a
whole
lot
of
code
for
Darwin
and
Linux.
It
just
comes
down
to
if
the
code
breaks
on
an
OS
update
or
in
somebody's
special
case,
it's
one
thing
to
be
able
to
go
to
Libby
UV
or
go
to
mio,
and
you
know
file
a
bug
report
and
then
drill
down.
But
it's
if
you're
shipping
production
software.
That
kind
of
that
book
has
to
stop
with
you.
B
B
A
B
A
A
B
So
I
have
two
more
things:
I
want
to
say.
Also
I
am
recently
on
the
job
market,
so
I'm
looking
for
cool
projects
that
companies
to
work
on
and
for
talking
around
or
you
know,
come
see
me
I'll
be
around
also
code
links
while
I
have
you
all
here,
there's
a
particular
RFC
allow
trade
allow
fields
and
traits
that
map
to
L
values
in
an
in-plane
type.
Can
we
please
have
this?
Can
we
please
have
this?
B
A
C
B
So
two
reasons
a
there
was
no
real
reason
to
do
it
in
kernel
space
there's.
Actually
a
previous
generation
of
this
device
was
not
just
so
it
was
vendor
specific
entirely.
It
wasn't
even
like
a
human
interface
device
who
was
just
vendor
specific
and
I
would
have
had
to
use
Lib
USB
and
that's
kind
of
its
own
special
brand
of
hatred,
I
suppose
so.
B
I
did
that
in
the
kernel
and
that's
in
the
Linux
kernel
now,
which
is
cool
but
yeah
a
there
was
no
real
reason
to
do
that
and
be
one
of
the
things
that
it
does
is
create
a
network
socket,
so
that
programs
can
communicate
with
it
and
that's
not
something
that
I
feel
like
a
kernel.
Module
should
be
doing
I,
don't
feel
like
loading.
A
kernel
module
should
open
up
some
user,
some
like
like.
Then
you
have
to
deal
with.
How
do
you
set
the
port
form
for
me?
B
Kernel
module
shouldn't
be
opening
up
network
ports,
that
kind
of
speaks
to
their
not
being
the
right
kernel,
interface
for
that
I
suppose
in
the
case
of
the
mono
stuff.
The
reason
why
I
did
that
in
user
space
is
so
that
we
could
standardize
on
one
API
across
platforms,
and
so
if
we
just
have
this
is
open
sound
control,
there's
a
port
running
you
communicate
with
it
that
way,
then
you
can
run
the
same
application
on
Windows,
Mac
or
Linux.
B
A
B
So
so
here's
the
thing
it's
been
a
while
in
the
kind
of
wider
consumer
space
since
I've
seen
a
piece
of
input
hardware,
that's
really
vendor
specific
and
kind
of
lock
down.
Usually,
if
you
go
out
and
buy
a
mouse
or
you
buy
a
keyboard,
you
plug
it
in
to
your
Linux
machine
or
whatever,
and
it's
just
gonna
work,
but
when
you
start
to
get
to
more
niche
areas
like
music
production
or
like
video
production,
they'll
usually
have
these
kind
of
like
custom,
controller
type
things
or
3d
modeling.
B
There's
all
of
these
really
specific
pieces
of
hardware
and
I
think
that
people
should
be
able
to
kind
of
crack
those
open
and
do
whatever
they
want
to
with
them.
I
think
that
this
is
really
important
and
that's
why
I
had
so
much
content
dedicated
to
find
some
weird
USB
device
and
here's
how
you
can
figure
out
what
it's
doing.
I
would
like
to
see
more
people.
Do
it
and
rest,
and
that's
kind
of
why
I'm
giving
this
talk
here,
because.
B
So,
while
libuv
and
mio
I
don't
know
if
this
is
necessarily
their
Forte,
I,
don't
know
if
they
should
be
dealing
with
this
really
low-level
thing.
I
know
that
there's
a
libuv
pull
request
for
like
a
UV
dev
type
of
handle,
but
I
think
that
there's
an
avenue
here
to
maybe
start
building
libraries
crates
to
do
this
on
a
more
cross-platform
basis.
I
think
that
there's
there's
an
opportunity
here
to
open
up
some
hardware
if
that
makes
them.
Thank
you.
Thank
you.
I
appreciate
it.