►
From YouTube: How can we write the best device driver?
Description
Writing a device driver can be a lot of work, writing a good one even more.
In this talk, we’ll explore some different styles of writing to and reading from registers and we’ll be weighing the pros and the cons. It will not conclude with a definitive best way, but hopefully it will give some ideas to use for the next driver you write.
The library discussed in this talk has also been publicly released since the talk was recorded: https://crates.io/crates/device-driver
A
Hello
and
we're
back
after
the
break
we're
driving
music
to
driving
hardware.
Diane
will
talk
to
us
about
how
we
can
write
the
best
device
driver
for
rust.
That's
a
tall
order.
I'll
give
you
the
stage.
B
A
little
bit
clickbaity
but
yeah
welcome
everybody.
Welcome
to
my
talk
about
how
can
we
write
the
best
device
driver?
Let's
get
my
slides
together
here?
Yes,
okay,
yeah,
I'm
the
undoctor
and
I'm
gonna
be
talking
about
this.
B
So
due
to
time,
I
can
talk
about
everything
I
want,
so
it's
quite
limited,
but
hopefully
it'll
give
you
some
ideas
for
your
next
well
device
driver.
So
for
the
overview.
First,
we're
gonna
talk
about
how
to
do
the
hardware
interface
we're
going
to
be
looking
at
how
it
is
generally
done
in
c
and
then
how
we
can
improve
it
in
rust
and
then
we're
going
to
talk
about
the
software
interface
and
we'll
take
a
an
imaginary
chip
and
write
a
little
start
of
a
device
driver
for
it.
B
So
there's
a
lot
of
code,
but
I
hope
you'll
like
it,
so
the
hardware
interface.
What
is
that
actually?
Well,
it's
how
we
want
to
talk
to
the
device
and
if
we
look
at
c
well,
we
get
into
a
problem
because
well
we
want
an
abstract
interface.
We
want
to
plug
in
anything,
and
if
you
google,
see
abstract
interface
well,
it
only
shows
other
languages,
not
c,
so
that's
a
little
bit
problem
problematic.
B
So
in
c
there
are
three
main
ways
that
I've
seen
people
do
it
and
I'm
gonna
go
through
them
all
now
and
the
first
one
I've
called
pre-made
fill-in
functions.
So
the
idea
is
that
you,
you
clone
the
library.
There
are
some
functions
provided
for
you
and
you
need
to
fill
in
to
talk
to
your
hardware.
You
need
to
fill
them
some
code
and
the
good
for
this
is
that
it's
pretty
performant
like
the
compiler,
has
got
all
the
information
it
needs.
B
B
And
if
we
look
at
the
assembly,
we
can
see
our
function
over
here
and
it's
all
just
moves,
because
my
implementation,
I've
written
over
here
for
the
hardware
interface
just
pokes
on
registers,
read
some
values
out
of
it.
So
we'd
expect
only
to
to
have
move
instructions
and
that's
the
case
in
all
the
other
examples.
I
will
use
the
exact
same
instructions
here,
so
we
can
compare
them
all
but
yeah.
B
We
we
need
to
maintain
our
own
fork
and
it's
all
static
and
stuff,
and
I
don't
really
like
it.
So,
let's,
let's
throw
it
away
the
other
one
is
runtime
function,
pointers,
and
this
is
where
the
library
doesn't
have
like
the
defined
functions
for
it,
but
it
allows
you
to
plug
in
your
own
function,
pointers
and
for
usability.
This
is
pretty
good,
because
when
you
pass
in
a
function
pointer,
it's
actually
type
checked
even
in
c.
B
If
we
look
at
the
code
at
the
top
here,
we
have
our
definitions
for
our
function.
Pointers
they
get
stored
here
in
these
static
variables.
Our
initializer
functions
just
takes
them
and
stores
them
here
and
our
example
looks
pretty
simple
again:
just
enable
transfer
transfer
disable
result,
but
our
assembly
is
not
as
good
like.
The
initialization
is
pretty
good.
B
It's
just
moving
the
arguments
to
the
to
the
fields
over
here,
but
our
example
is
suddenly
quite
inefficient,
we're
doing
all
kinds
of
stuff
and
mostly
we're
actually
calling
and
branching
out
to
the
function
that
is
in
the
function,
pointers.
So
that's
not
very
good
performance.
We
want
good
performance
and
it's
even
longer
as
well.
B
So
no,
no!
Let's,
let's
take
this
away.
Another
one
is
extern
link
time
binding,
so
our
library
doesn't
really
say
what
functions
there
are
there.
You
just
say
like
hey:
there
are
some
somewhere
over.
B
B
So
what
does
it
look
like?
Well,
if
we
look
at
the
assembly
over
here,
the
example
function
is
pretty
good,
because
I've
written
an
implementation
here-
and
you
need
to
this-
is
normally
done
in
another
file,
but
here
on
godbolt,
I've
just
done
it
over
here.
So
the
example
function
is
pretty
efficient,
but
we
can
also
see
that
these
transfer
enable
and
disable
functions
are
now
part
of
the
public
api,
because
well
extern
private
doesn't
really
make
much
sense.
B
So
that's
logical,
but
our
api
is
not
public
and
I
don't
really
know
if
that's
a
good
idea
so
throw
it
away
as
well.
Now
we're
going
to
look
at
the
rest.
What
if
we
want
both
performance
and
usability
well,
rust
does
provide
abstract
interface
machinery
and
we
can
do
it
with
generics
and
traits
and
actually,
if
we
use
trades,
we
get
more
usability
than
c
could
ever
offer
us
because
we
can
reuse
trades
from
the
embedded
hull.
B
So
if
we
want
to
implement
spi,
we
just
take
the
spi
trade
from
the
embedded
hall
and
any
platform
that
implements
that
can
just
plug
it
in
to
our
library
and
nobody
needs
to
implement
anything.
So
that's
a
huge
plus,
but
for
now
we'll
stick
to
our
own
interface
and
what
I've
also
done
is
using
a
struct
to
implement
it
in
because
I
don't
want
to
use
dynamic
dispatch.
B
So
we've
got
a
constructor
for
the
device
and
our
example
now
goes
through
self.
interface,
but
all
the
functions
are
the
same
and
if
we
look
at
the
assembly,
I
hope
everything
is
big
enough
because
there's
a
lot
of
code,
but
we
can
look
over
here
and
I
had
to
trick
godbolt
into
showing
the
assembly.
But
it's
all
exactly
the
same.
Just
move
instructions,
that's
it
and
the
spi
implementation
here
can
be
part
of
the
public
api.
But
you
need
an
instance
for
this.
So
that's
that's
fine.
B
B
B
A
B
Side
of
things,
but
you
can
do
a
lot
of
things
in
high
level
as
well
but
yeah
time.
So
this
is
our
imaginary
device.
It's
a
gpio
expander.
It
runs
over
spi.
It
has
six
registers
of
each
one
byte
and
we
want
to
do
the
following
thing
with
it.
Just
as
an
example,
if
the
manufacturer
id
is
not
0,
then
we
must
set,
pin
7
high.
B
It's
pretty
easy.
We
need
to
read.
We
need
to
write
something,
it's
a
pretty
good
example.
I
don't
know
if
a
mage
mark
makes
much
sense,
but
yeah.
So
here's
what
you
would
do
in
c,
you
would
generally
define
the
register
definitions
with
either
an
enum
or
macro
defines
and
then
you'd
provide
some
functions
to
write
and
read
those
registers,
and
the
only
thing
you
really
have
are
the
raw
words.
You
say:
hey
to
register
12
write
15,
something
like
that
and
to
use
those
raw
words.
B
B
B
You
transfer
the
address
and
then
transfer
zero
and
you
get
the
result
or
you
transfer
the
actual
value
you
want
to
write
and
in
our
example
code
here
we
first
want
to
read
the
manufacturer
id.
So
we
read
register
the
id
register
we
and
it
with
the
manufacturer,
mask
that
I've
defined
up
here
and
then
we
bit
shift
it
with
the
amount
of
positions
here
and
if
it's
not
0,
then
we
want
to
read
the
port
register.
B
We
want
to
enable
the
seventh,
or
rather
eighth
gpio
pin,
so
we
set
the
the
the
biggest
bit
and
then
we
write
back
the
register.
So
it's
pretty
straightforward,
but
it's
not
really
well.
The
api
is
not
really
that
nice,
but
if
we
look
at
the
assembly,
the
assembly
is
like
perfect.
If
you
had
written
this
by
hand,
it
wouldn't
look
much
different
well,
except
for
the
numbers
maybe,
but
we
just
move
move
move
with
move,
so
that's
pretty
efficient.
We
do
a
comparing
the
jump.
Oh
hey!
B
That
sounds
like
an
if
statement
we
compare
and
we
maybe
we
jump,
but
we
do
more
moves.
We
do
an
ore,
that's
this
or
more
moves
and
then
return.
So
this
is
as
efficient
as
it
can
be,
but
this
api
is,
I
don't.
I
don't
really
like
it,
because
we
we
need
to
do
the
mask
ourselves.
We
need
to
do
bit
shifts.
B
We
need
to
create
a
temporary
register
here.
It
works,
but
I
think
we
can
do
better
and
some
of
the
things
can
already
be
better
if
we
do
it
in
rust,
because
I
almost
forgot
this
register
here.
This
address.
The
only
valid
values
are
one
through
six
but
c
doesn't
stop
us
from
just
plugging
in
seven
or
a
hundred,
and
what
happens
then?
B
Well,
I
don't
know,
but
it's
probably
not
good,
but
c
will
just
allow
you
and
we
can
fix
that
by
doing
it
in
rust,
so
we
actually
use
a
proper
rust
enum
and
if
you
plug
in
any
other
value,
then
the
rust
will
give
a
compiler
error.
B
So
everything
else
is
still
pretty
much.
The
same.
We've
got
our
device
struct
constructor,
and
then
we
implement
read,
register
and
write
register
and
they
do
pretty
much
the
same,
and
in
our
example
code
we
say:
hey
we've
got
our
device
based
on
our
sbi.
We
read
the
manufacturer,
we
mask
it.
We
bit
shift
it
if
the
manufacturer
is
not
equal
to
zero.
B
Well,
everything
really
looks
the
same,
but
at
least
it's
a
little
bit
safer
because
we
now
use
a
proper
enum
for
it
and
the
assembly
the
output
is
still
just
as
efficient.
This
is
literally
the
same
output,
so
yeah,
but
we
still
got
the
problems
that
we
need
to
mask
and
shift
and
create
this
temporary.
B
We
can
make
it
type
safe.
We
can
use
types
to
do
everything
because,
if
you
think
about
it,
every
register
is
actually
different
because
you've
got
an
id
and
a
port
and
the
port
controls
pins
and
the
id
just
has
some
id
values.
Those
are
different.
So
why
are
we
using
a
u8
or
a
char
for
all
of
them?
What
if
we
create
more
types
to
represent
the
registers,
because,
right
now,
the
burden
of
correctness
is
on
the
user
instead
of
the
library
writer,
while
the
library
writer
probably
has
more
knowledge
about
it.
B
So
we
want
to
improve
that.
So
we
can
you
we
can
make
it
both
easier
and
correct
by
default,
because
we
have
a
good
example
of
where
it's
done
already,
namely
the
pack
crates.
They
have
a
pretty
good
api,
which
is
all
which
is
also
very
efficient.
B
B
We
really
need
for
our
example,
but
we've
got
the
id
register
and
the
port
register
and
what
they
do
is
they
return
a
register
accessor,
and
this
is
the
trick
we
will
use
the
register.
Accessor
borrows
the
interface,
so
you
get
the
object
back,
which
borrows
like
yeah
the
interface,
so
you
can't
use
the
device
until
you've
dealt
with
the
register
and
we
use
generics
to
wrap
the
r
and
w
value
around
it.
So
what
are
the
w
and
r
value?
B
B
Well,
I've
decided
that
the
id
register
is
only
so.
We
only
create
an
r
struct
and
it's
around
a
u8,
which
is
our
our
word
now.
Our
manufacturer
number
is
now
a
function
and
the
masking
is
done
right
in
the
function.
We
just
get
the
u8
back
and
apparently
there's
a
version
field
as
well,
which
get
masks
and
returned
and
in
our
register
accessor.
B
So
actually
that's
it.
So
what
does
the
api
now
look
like?
Well,
if
we
want
to
get
the
manufacturer,
we
just
say
hey
device.
I
want
to
access
the
id
register
and
I
want
to
read
it
and
then
I
want
to
read
the
manufacturer
field.
So
we
just
get
it.
Then,
if
the
manufacturer
is
not
zero,
the
device
we
want
to
access
the
port
register,
we
want
to
modify
the
port
register
and
what
we
do
is
our
right
object.
We
want
to
enable
the
7th
or
again
8th
gpio,
because
we
set
it
to
true.
B
That's
it
so
now
we've
got
a
a
pretty
good
api.
If
you
ask
me
that
doesn't
allow
for
mistakes,
because
all
the
shifting
and
masking
is
done
underneath
this.
But
how
efficient
is
this
because
well
a
nicer
api?
It's
higher
level.
It's
got
to
be
more
inefficient
right.
Well,
the
answer
is
no.
The
assembly
is
exactly
the
same,
which
is,
I
think,
like
amazing.
B
It's
exactly
the
same.
While
the
api
is
a
lot
nicer,
so
you
can
go
a
lot
further
with
this,
but
I
haven't
got
time
to
really
show
you
how
to
implement
it,
but
I
can
show
you
a
practical
example
of
where
I've
implemented
this
myself
and
it's
here.
B
This
is
what
I've
written
for
my
job
and
I
got
permission
to
show
it
to
you,
and
this
is
basically
the
same
api,
but
a
little
bit
more
extended
and
it's
wrapped
inside
a
macro
to
easily
implement
all
the
registers.
B
So
I've
got
my
ll
device
low
level
device
and
then
I
implement
all
the
registers,
so
I've
got
the
bmcr
register.
This
is
the
address.
It
is
read,
write,
and
these
are
all
the
fields
that
are
in
here
and
I
can
just
specify
at
which
bits
they
are
now.
This
register
isn't
really
interesting,
but
if
I
go
down,
I
need
to
look
for
it
a
little
bit.
That's
a
good
register,
for
example
here.
B
So
we
see
that
in
the
var
control
register,
we've
got
the
var
timer
and
it
takes
two
bits
of
of
width,
but
it
doesn't
really
turn
a
bit.
No,
it
returns
a
variance,
computation
timer,
which
is
just
an
enum
over
here,
because
I
don't
want
to
deal
with
the
register
being
0,
1,
2
or
3.
No,
I'm
on
2.
No
is
it
well,
it
isn't
properly
documented,
but
2,
milliseconds,
4,
milliseconds,
6
or
eight,
which
I
think
is
a
lot
better
than
zero
one.
Two
three-
and
there
are
other
examples
here
as
well.
B
Can
I
find
them?
Where
are
you.
A
B
Here,
for
example,
so
we've
got
the
three
bits
over
here:
ptp
trigger
number
yeah,
so
these
are
a
lot
of
registers,
which
is
why
I
created
this
macro
to
implement,
implement
it
all.
For
example,
here
event
capture:
it
can
be
zero
or
one,
but
I
don't
want
to
know
if
it's
zero
or
one.
I
want
to
know
if
it's
falling
or
rising
and
yeah.
So
this
is
the
low
level
api.
But
what
does
a
high
level
look
like
or
what's
possible?
Well,
I've
talked
about
the
reset
function.
B
B
So
if
I
hover
over
this,
I
can
just
see
the
documentation.
That's
part
of
this.
This
bit,
which
is
self-clearing,
returns
a
value
of
1
until
the
reset
process
is
complete.
So
if
I
want
to
know
that
the
reset
process
is
complete,
I
need
to
keep
checking
it
so
I'll.
Do
that
within
the
while
loop
haven't
had
to
access
the
data
sheet
for
this,
which
is
pretty
good,
I
think
and
there's
also
another
benefit,
namely
documentation.
B
This
is
the
generated
documentation
for
this
and,
if
I
go
to
the
low
level
well
well,
here
are
all
the
registers,
and
I
can
just
pick
one:
no,
they
all
are
small
and
then
go
to
the
reader,
and
I
can
just
read
or
yeah
what
fields
are
in
here.
I
can
read
what
the
register
is
for.
So
all
the
documentation
is
now
part
of
my
rust
documentation,
which
I
think
is
fantastic.
B
Let's
go
back
yes,
so
I'm
almost
done.
This
is
the
conclusion.
B
The
api
I've
described
is,
I
think,
pretty
good
and
we
can
even
use
macros
to
make
our
lives
even
easier
for
implementing
it,
but
compared
to
the
more
c-like
type
the
compile
times
are
longer
because
because
we
use
generics
and
macros
yeah,
that's
that's
just
one
of
the
costs.
Maybe
we
can
even
generate
it
from
a
file,
so
we
maybe
we
don't
want
to
do
it
in
macros,
but
maybe
from
a
file,
because
the
pack
crates
also
do
it
from
the
svd
files
that
are
part
of
the
which
go
alongside
the
microcontrollers.
B
Therefore,
so
maybe
that's
a
good
idea
and
a
lot
can
be
done
in
the
high
level
part
as
well.
I've
shown
you
the
reset
function,
which
is
pretty
easy,
but
you
can
also
use
a
lot
of
type
state
there,
because
there
is
the
crate,
which
is
a
radio
chip
and
it
uses
type
state
to
manage
when
the
radio
is
sending
receiving
or
idle.
B
So
yeah
a
lot.
A
lot
more
can
be
said
and
discussed,
and
I
hope
to
see
some
questions
and
discussion
in
in
the
chat.
But
for
now
I
want
to
throw
up
a
ball.
Maybe
maybe
we
can
create
a
unified
device
crate,
which
maybe
makes
it
easier
to
implement
a
good
api
like
the
one
I've
showed
you,
maybe
we
can
have
traits
for
a
general,
a
device
trade
for
which
manages
a
power
up
reset.
B
Maybe
sleep
access
to
the
api.
Maybe
that's
a
good
idea
that
some
of
you
would
like
to
see
so
we
can
maybe
discuss
about
that
and
with
that
that's
the
end.
I
hope
I'm
not
too
much
over
time.
So
thanks
for
watching
and
I'd
love
to
answer
any.
A
All
questions
we're
running
ahead
of
time,
anyways,
there's
a
number
of
questions
in
the
channel
and
definitely
good
feedback.
But
here's
just
the
questions.
Are
you
planning
to
open
source,
your
register
creation
macro?
This
is
actually
a
question
by
james.
B
Well,
it's
written
for
my
job.
I
got
permission
to
show
it,
but
my
company
is
sadly
not
really
into
open
source
but
yeah.
If
there's
interest,
I
may
be
willing
to
do
something
in
my
free
time
to
to
set
something
up.
If
there's
interest
yeah.
A
B
Yeah,
I've
not
tested
it
for
this
right
now,
but
the
benefits
or
one
of
the
things
you
can
do
in
cargo
is
you
can
selectively
optimize
separate
libraries.
So
if
it's
a
separate
library
you
can
just
enable
the
optimizations
there
and
then
you
won't
get
slowed
down
as
much
but
yeah.
It's
definitely
a
bit
slower
yeah.
A
Okay,
one
other
question:
how
does
error
handling
regarding
the
underlying
peripherals,
for
example,
i2c
bus
errors,
work
out
here.
B
Well,
in
my
in
my
practical
example,
I've
dealt
with
it
that
you,
whenever
you
read
you
get
an
error
back,
so
you
need
to
do
a
question
mark
behind
it.
Well,
if
you
want
and
every
time
you
actually
read
a
field
when
you
read
a
an
enum
back,
that
can
also
go
wrong
because
maybe
it
it
hands
you
back
the
number
12,
but
your
enums
does
don't
expect
that.
A
Okay
and
then
there's
one
other
question:
how
is
this
related
to
svd
to
rust.
B
It's
not
directly
related,
but
the
api
is
very
similar.
The
ideas
behind
it
are
well
yeah
similar.
So
that's
the
only
connection
really.
Okay.
A
Yeah,
that's
all
the
questions.
There
has
been
a
lot
of
discussion
in
the
chat.
I
also
want
to
highlight
how
people
have
now
gone
to
actually
highlight
the
questions
in
chat
by
prefixing
them
while
having
active
discussion.
So
I
leave
you
to
the
discussion
around
your
subject.
There's
definitely
a
lot
that
you
can
follow
up
on
and
I'm
very
happy
that
the
talks
turn
out
to
be
the
conversations
go
on
until
the
next
one
starts.
So
there's
always
something
to
see
during
the
breaks
and
yeah.
This
is
the
next
one.