WWDC2001 Session 207

Transcript

Kind: captions
Language: en
good afternoon and welcome to the USB
in-depth session we're going to go into
USB both for user client and kernel
level deep development I'm Craig
Keithley I'm the USB and firewire
technology manager actually I'm one of
two Greg Mullins also in my group is
another USB and firewire technology
manager so with this session we'll go
into what's involved with writing user
level drivers and kernel level drivers
we'll do some demonstrations on
debugging and with that I want to
introduce Fernando or bina Andros
Halliwell thank you very much Thank You
Craig we have a lot of topics to cover
today so why don't we just get right on
it today we're gonna give you an
overview of the IU USB family class in
Mac os10 talk a little bit about the
objects that we have and how we use them
in in the kernel we're going to go into
some detail into writing kernel and USB
drivers and how to decide whether it be
in the kernel or in user space a very
very critical part of writing drivers is
how to match your device to your driver
so we're also going to go into quite a
bit of detail into that and at the end
of the session we'll show some demos on
how to debug and USB drivers in the
kernel and in userspace as you probably
know you will need to use a project
builder which is a both integrated
development environment to write both
kernel and user space drivers if you are
writing user space drivers you will be
able to use the debugging features of
project builder to debug your driver
unfortunately if your
kernel-space driver you will have to get
down and dirty with gdb and terminal and
debug your drivers that way later on I'm
gonna talk about whether you should
write a user space or a kernel space
driver this is the first decision that
you have to make when you decide that
you're writing a USB driver for Mac OS
10 so so it's a very important to to
know what you're gonna do there if you
are in the kernel you will be able to
use io qids object-oriented features so
for example if you are writing a driver
that is gonna be subclass from our mass
storage driver you will only have to
write the methods that you are
overriding to to implement your device
specific features so it's a very
convenient way of doing it
roads we'll talk later on about user
space drivers in some detail and as was
mentioned in the previous session fire
wire we use the device interface model
in order to communicate between user
space and our internal IO USB family
objects the USB family is in the kernel
there's a four or five objects that we
provide they are delivered as a kernel
extension that goes in the extensions
folder we use this facility to update
USB without having to give you a whole
new kernel user space drivers can
obviously access the internal objects
through the device user or the device
interface client that I mentioned
earlier this light gives you just a
brief description of the internal
objects for the audio USB family those
are the ones in
in purple and in a later slide I will
talk in some detail about them I just
wanted to give you a an overview of
where we fit within the user space on
the kernel space so let's talk a little
more about kernel mode USB drivers how
do you decide whether you're going to be
in the kernel or in user space if at all
possible you should not be in users you
should not be in the kernel sorry
however in some cases you don't have a
chance if you are writing a vendor
specific driver for a keyboard you need
to be in the kernel because at boot time
if you're going into single user mode in
the Machine you need to have a keyboard
there's no user user land processes
running at that time so you're you have
to be there the other condition is if
your clients live in the kernel for
example mass storage or networking
client the file system or the tcp/ip
stack they are in the kernel you have to
be in the kernel and so you don't have
you don't have a choice again if you
haven't gotten the message we really
recommend that that you are that you
write your drivers in the user space
there are a lot easier to debug if
you're in the kernel it's really easy to
panic the machine and users don't like
that so try to write user space drivers
the just like all other iokit drivers
USB drivers are written in the subset of
C++ the base class of all iokit drivers
is IO service when you write the driver
in order to load it against your device
you will need to provide a matching
dictionary so that we can load and later
on we're gonna show you on the demo
machines the different fields that go
into a into a dictionary and how we
decide on how to match it against your
device the driver itself that project
builder makes is a text package that
goes in the extensions folder and as as
we mentioned earlier in the previous
presentation that IO us be family or
your driver can include sub drivers in
the one package this slide just gives
you a overview of the different USB
family objects that are in the kernel
the IO USB device IO USB interface and I
owe us beep I'm going to talk in more
detail about each after this slide we
also have two other objects which are
the i/o USB device user client and IO
USB interface user clients these are the
user client objects that are used to
communicate between user space and our
internal objects when you plug in a USB
device into a bus our family will create
a io USB device object for that device
the provider of that device will be the
IO USB controller class the IOU io USB
device object it's an abstraction of the
physical IOU
we devised this class provides methods
for accessing the device descriptor
fields of the USB device we also provide
methods for getting the configuration
descriptor of the device and also for
setting the configuration for the device
once you set the configuration for the
for the device the family will go ahead
and create I O USB interface objects for
all of the interfaces that are in that
particular configuration the i/o USB
device class will be the provider for
all these IOUs B interface objects again
there is gonna be one I owe USB an
interface object for each interface in
the particular configuration figuration
description of the device the interface
objects are an abstraction of USB
interface the methods that we provide
for the USB interface are those that
allow you to access the fields of the
interface descriptor and also allow you
to set alternate interfaces if they are
available for your interface when we
create the i/o USB interface object we
will also instantiate IO USB pipe
objects for each of the pipes in the in
the interface there will be one for the
default control endpoint and we'll also
create however many objects that are
needed to match against all the other
endpoints that are specified in the
face the scripture the methods that we
provide for the IOUs be pipe objects are
those that are needed to actually
communicate with the device so we have
the reed pipe we have the right pipe we
also provide methods to get status on
the pipe to clear the a possible stall
on the pipe and cetera the other two
objects that we have as I mentioned
before are the i/o USB device user
client and the i/o USB interface user
clients and these provide the necessary
glue for a user task user driver to talk
to the internal objects you could tell
whether you actually have a user task
attached to your device driver or your
interface driver by using the i/o
register Explorer going down the tree to
find your driver node and you can then
see if there is actually one of these
user clients attached to it the
convention that we use for in our family
for the Apple supply drivers is the
following if the driver starts with the
i/o letters we intend that you could
subclass that driver and add
functionality just by defining new
methods or overriding some of the base
class methods if the driver starts with
the word Apple that is ours and you're
not allowed to subclass it however if
you have a vendor specific device you
could just get our code adapted to what
your device does and unuse it that way
and create your own driver this is a
list of the USB drivers that we are
providing the kernel these are the all
the Apple ones it's the standard Mouse
keyboard
hub driver we provide just like in mac
os9 a composite class driver that will
just look at that configuration find the
first configuration and do a set
configuration on that device to create
the interfaces for that device the to
subclass able kernel drivers that we
provide are the head driver and the mass
storage class driver again the nice
thing about living in the kernel and
being able to use these drivers is that
if your device just is a little
different than one of the devices that
we support you can just use C++ and
override those methods that you need to
change and you have a lot simpler driver
than starting and writing everything
from scratch those of you who attended
the USB overview session yesterday pro
will recognize the following two slides
but we are including them here because
we think it's really important to
understand how the drivers stack of the
USB drivers is built up and in the next
slide how it all fits together so again
the i/o PCI family will discover the
ohci USB controller and it will load a
driver for that device that is our i/o
USB controller driver during startup
this controller driver will instantiate
a IO USB device for the root hub of that
controller we use iokit methods to match
a driver against that device and we have
the Apple USB hub driver the hub driver
sits there waiting for devices to get
plugged in and for example if you
suddenly plug
in a pair of speakers the Apple USB hub
driver will notice it will enumerate
those speakers and it will create an i/o
USB device and attach it to the i/o USB
controller in this case for this example
speakers are usually a composite class
device so the composite class driver
will go ahead and load against this i/o
USB device it will do a set
configuration on it which is as I
mentioned earlier will load V or create
the i/o USB interfaces for that device
in this case I'm only showing one of
those interfaces the again we use the
i/o kit matching code too as for a
driver to be match against this
interface and we have the Apple USB
audio device driver loaded against it so
this is how in this works and any device
that you plug in will do the same thing
and we'll try to find drivers for it and
and instantiate them for your device as
long as they're in the kernel this
driver is sorry this slide is here to
show a distinction between being a
driver and the distinction the one that
we want to make is who your provider is
and what member of which family you are
for example the Apple USB audio device
is a member of the i/o audio device this
is the base class from which they derive
they get their behavior their API from
the i/o audio device family however when
they actually want to send data to the
pair of speakers they use the i/o USB
family as the transport mechanism to
get that data from memory down to the
speakers so it's sort of a dual
relationship you are a member of the
family but you are a client of the
transport family which in this case is
the IOUs B family but it could be
another another mechanism another family
if there were firewire speakers you
could have firewire in this example the
Apple USB audio device is is a leaf node
driver so isn't it is not intended to be
subclassed however if we had the hit
driver attached to an interface like for
the buttons of a speaker as you've seen
in other slides it has a little puzzle
piece which means that you could
subclass that that driver and add the
functionality that you need in Mac OS 9
we had a DDK that provided some example
drivers some of them we or most of them
we used internally at some point but
there weren't the live sources that we
were using to release the USB stack on
Mac OS 9 that has changed with Mac OS 10
we are open sourced we live in the
Darwin repository so our live IO USB
family sources are there the module name
is the i/o USB family no surprise there
if you haven't gone and become a member
of the open-source project so that you
can check out the sources you should go
ahead and do it
download it look at the source look at
the drivers borrow whatever you need
tell us where we have the bugs it would
be nice if you said you know 9:23 of
such-and-such a file but if it is there
for you to look at and we go we went
through a lot of effort to get this out
out into the open source community so
you are more than welcome
to go ahead and look at it okay
IOUs be family driver matching if we
cannot drive your driver if you cannot
if we cannot match your driver to your
device then you know game over
why have the driver so it's very
critical that we explain it so you can
get it right and the first item that you
have to decide is are you going to be
other than being in Colonel versatile
user space but once you have decided
that you are a kernel driver you need to
decide whether you're a device driver or
whether you're a composite driver you
will specify in your driver personality
and we'll show you a demo of where that
goes in a little bit whether or you need
to specify the i/o provider class other
iokit drivers use the i/o probe score
inside the personality to give a hint to
I okayed of how important this driver is
what the ranking is in the i/o USB
family we do not use and you do not need
to provide the i/o probe score this is
because USB and the USB specs have a
very definite set of criteria that spell
out how multiple drivers are ranked and
which one has the highest rank and which
one has the lowest rank so that if we
find n drivers we can rank them and we
can tell I okay okay try this
this driver first in this manner if we
provide a class driver for one of your
devices for example a mouse driver you
still can provide a driver that has a
higher matching criteria and I okay will
load that driver first and give it a
first shot at controlling the device so
again the USB common class specification
is
document that details exactly what
fields you need to have in your
personality to match against drivers and
just by chance here I have a copy of the
table that is used by the common class
spec to specify device as opposed to
interface driver rankings all the fields
in the boxes are fields of the USB
device descriptor at the top level with
the highest possible rank that a driver
can have is a driver that specifies the
product the vendor and let's call BCD
device but it's the release number of
the device one step lower we have the
vendor and the product that will just
match against all drivers of that vendor
and product ID without caring about the
release number down in the orange boxes
we have what we what we call the class
drivers these are classes that are not
vendor specific and that is what the
0xff means and you can match against the
class the subclass and the protocol in
the next-to-last
item or just device and device subclass
so as we're going to show you you're
gonna specify these and this is what the
USB family uses to generate a probe
score for iokit to decide which driver
is the highest ranked driver
as was mentioned in the overview
yesterday once IO kid has a list of
drivers that match to a device it will
give each driver a shot at denying
whether they want to control that device
or not by calling the probe method of
that driver the default implementation
for 4 probe method is to just return
success so you don't even need to
implement that but it is just here to
give you a shot at a denying control of
the device later on
you still get another shot at the 9
control of that device once the ordered
list of drivers with a higher one at the
top and the lowest ranking one at the
bottom is created I okay we'll call the
start method for the highest ranked
driver first this tells you that your
driver is really starting you can do do
all your stuff that you need to do to to
get your driver in working condition if
you return false from this method then I
okay we'll go okay well he didn't want
it let's try the next one in the list so
you get this second shot at it but now
I'm gonna show you or here roads is
going to help me out if we could have
them on one machine on the slides I'm
going to show you how to specify the
matching dictionary so we have the
project builder this is a sample vendor
specific driver that we wrote and this
pane that we see up there is the bundle
settings tab of the project builder
target in there we have a iokit
personality dictionary entry this is
where the matching criteria among other
things is going to live the first item
the
I'm gonna talk about here is the CF
bundle identifier this string needs to
match match the CF bundle identifier
that is declared in the upper level the
best way to do it is just to copy and
paste because if it doesn't match your
driver won't get loaded the next field
is the IO class field this is the class
of the driver that that you're going to
use so here we have the header file
where the C++ my software vendor my
software company class is defined that
is what you need to put in that entry
next you need to tell us whether you are
a device driver or an interface driver
and so you specify IO USB device or IO
USB interface in this case we have a
vendor specific device driver so we have
IO USB device now we don't have here and
but Roach's gonna type in we decided to
make this a vendor specific device
driver and we're going to type in the
common class spec fields that are
required for that for that device and it
is the ID product it's a number and the
ID vendor and it's a number those
numbers correspond to the obviously the
product and device fields of the of the
device descriptor finally we have this
i/o kit debug dictionary entry this is
used internally within i/o kit to put
debugging information out to the console
to the system log it helps some in
debugging why your driver doesn't load
from an i/o kit perspective however
right
just says that matching failed it is
also used internally within I okayed for
other other features it is a actually a
bit field of different features but if
you put 65 535 in it it will you know
match to all the features and it will
put out a lot of data we do realize that
right now it is very frustrating to
determine why your seemingly good driver
is not loading against your device when
you plug it in we are very aware of this
and yeah it's not going to stay like
that for long we will use the i/o kit
debug field to actually put some
meaningful information out to the system
blog to give you an idea of what what's
going on when we tried to match your
driver the final two two entries in the
bundle settings tab here are the always
bundled libraries this is a dependency
entry it tells a akkad that this driver
is dependent on the Condor Apple dot I
owe kid I owe USB family version 1.8 if
you were depending in the future against
an API that was introduced in version
one point eight point seven you would
put here one point eight point seven and
I okayed one wouldn't even load you if
the IU USB family version one point
eight point seven was not around so that
way you don't have to do runtime checks
whether a particular API is available
like you used to do in Mac OS 9 the
final property is the OS bundle required
property there is a a driver matching
that goes on very early on during the
boot process
this property tells iokit which drivers
need to participate in this early driver
matching for example our mouse on our
keyboard drivers have this this property
because you as I mentioned earlier need
the keyboard to go into single user mode
the reason I mention it here is because
if you are writing a vendor specific
keyboard or mouse driver and you want to
override our USB class driver you need
to also put that property there if not
what's going to happen is the early-on
driver matching during boot time will
load our class driver and then when I
okay well I okay then won't even scan
your keyboard or your mouse for our
driver because it already has one loaded
so you need to specify that if that is
what you are doing we could go to the
slides now again
this slide shows you which I okayed
methods are called during the two-step
driver loading process the first step is
when I okay it is looking for drivers
for a device as I mentioned earlier the
probe method is here which gives you a
chance to to deny your your your
matching you get the you need the attach
the probe method and then you get
detached again once I okay decided that
you are the one that you are the driver
who is going to control that device you
get an attach method again and then you
get the start method the thing to take
out of this slide is that your attach
method will get called more than once so
you have to be prepared to deal with it
when your device gets disconnected we
get the file
following a termination sequence you
first get a message method with the k io
services terminated message this is your
clue that your provider is gonna go away
you need to cease and desist all IO to
your device you need to stop everything
returned from it then you'll get a
terminate message followed by the other
sorry terminate method call followed by
all the other ones that I list here in
the slide the key there is the the first
notice that you get that your device has
been unplugged is is this services
terminated message if you have your
provider open your device or your
interface you need to close it at that
time now if we could go back to the demo
machines we have a vendor specific
driver there is a very dummy driver all
it does is it spits out IO logs which by
the way an IO log will print a message
in the system console every time that
the
that the message is called and is just
to prove you that is it's not smoke and
mirrors and we are actually calling all
those methods in the in the order that
we said so here we have a security
dongle and roads here is going to load
the driver so that it's available for
when we plug in the device he'll then
open or do a tail on the console log so
that we see any messages that get
displayed when we go ahead and plug the
device when it crashes no whoops there
we go we get the init method followed by
the attach the probe the detach and
again the attach method and finally the
start and we successfully start and
there we go
now when we disconnect the device we
unplug it
boom we get all the other methods and
your driver after a minute or so will
actually get a little different memory
so now I'll give a key span to Rhodes
and he'll tell you more about user space
drivers all right we need to go back to
the slides please
well actually there was one more thing
about the colonel that we wanted to talk
about because this has been a subject of
recent discussion on the USB mailing
list and there's some some confusion
about it and this has to do with work
loops with an i/o kit IO kid has a
concept for kernel threads of a work
loop which is a work loop that's
protected by a lock similar to a
secondary interrupt queue if you will
under OS 9 the biggest difference is
that with Mac OS 10 this secondary
interrupt queue there's more than one of
them there's generally one per hardware
interrupt source so in the USB family we
have a work loop and we will generally
have at least two because we have two
separate USB controllers and so there'll
be one of these work loop per controller
every member of the USB family that's
attached to a particular controller and
every driver of any of those USB family
mechs which controls a device or an
interface say participates on this same
work loop which is a way of serializing
calls to the bus and what this means is
that your driver has to be careful about
what types of calls it makes to USB
because if it is in a callback routine
for example an asynchronous callback
routine and it issues a synchronous call
to the bus that call cannot complete and
you have a deadlock because in order to
complete an interrupt has to come in on
a different thread and that interrupt
will not be able to execute on the work
loop because the loop is locked by the
callback routine so we need to be aware
when we write our drivers about what the
execution context is in which thread is
currently active on the work loop and if
necessary you may move the thread
context to a different thread using a
kernel
mechanism called the thread call
mechanism the stuff is being discussed
on the USB mailing list over the last
few days and I encourage you to
understand it from from that list okay I
want to talk about USB in userspace
as we have said before Apple strongly
recommends that if at all possible you
write your USB driver to operate in
userspace
it's much easier to debug there are
better debugging tools and in general if
your driver crashes you're not going to
bring the entire system down which is
much easier in the valley in the
development stage as well as in the
end-user stage now within the kernel and
as this slide shows the the stuff below
the kernel boundary here we have our
members of the USB family so we have in
this example an i/o USB device object
the USB family provides a mechanism for
communicating from user space to that IO
USB device object and that mechanism
involves two pieces on the user side it
is something called an i/o USB device
interface and on the kernel side it is
something called an i/o USB device user
client these two pieces come together in
pairs and when your user land code for
example in this case a USB scanner
driver wants to communicate with the
kernel object in this case the i/o USB
device it uses those pair of objects
there is also a user client object and a
device interface or an interface object
used to communicate with an i/o USB
interface object in the kernel
unfortunately the the
there is some confusion because the
generic name of these objects in user
spaces are device interface objects and
of course both of those terms were
already exist in the USB world because
of device descriptors and interface to
scriptures and so what we really have
with USB is we have IO USB device device
interfaces and IO USB interface device
interfaces which of course is difficult
to say much less fit on a PowerPoint
slide so we have shortened that to be a
device interface and an interface
interface so what do you need to do if
you're going to write your your USB
Driver code and user space well you need
to use two frameworks in particular the
core foundation framework which gets you
access to things like CF plugins and CF
run loops and the i/o kit framework
which gets you access to the i/o kit
functions as well as the USB functions
which are part of the i/o kit framework
this mechanism uses as I've said before
the CF plugin model and the two plugins
provided by USB are the i/o USB device
interface plug-in and the i/o USB
interface interface plug-in we do not
provide an interface plug-in for the i/o
USB pipe object this this is
encapsulated within the plugin for the
interface mostly although there is one
pipe in the device which is the default
control pipe but the other pipes are all
part of the interface they are in the
kernel those pipe objects are
instantiated when the interface is
opened and then you have access to
methods within the interface interface
plug-in to determine the characteristics
of a pipe and to then send and receive
data over any of the given pipes
the iokit API is in as I said in the i/o
kit framework and it's in much of it is
in the i/o kit Lib dot H file the
plug-in API that we use is also in the
i/o kit framework and it is in IOC F
plugin dot H and then finally the USB
interface API is in the i/o kit
framework in the USB subfolder and it's
in IO USB live H again the two
interfaces we provide four USB drivers
are the i/o USB device interface and the
IO USB interface interface so if you're
running in user space you need to
communicate with a kernel object either
an i/o USB device object or an i/o USB
interface object so how do you do that
well the first step is to find the
correct kernel object this these
procedures by the way are outlined in a
in a document available from the the
website called accessing Hardware from
applications or something so something
along those lines
so what are the steps involved well
first you have to from user space you
have to obtain a mock port that you use
to communicate with the kernel then you
have to create a matching dictionary
that's very similar to the matching
dictionary we showed in the demo of of
the personality all the nth and the same
rules apply to this matching dictionary
you must fill in fields from either the
device descriptor or the interface
descriptor based on the weather your
driver is looking to to be a class type
driver or a vendor specific type driver
so you you add these fields by adding
qualifications to this matching
dictionary and then you ask IO kit to
give you a kernel iterator of kernel
objects that match your particular
dictionary so how do we obtain the
master port well the the Interfaith the
API is defined in mock H and it's just a
call to IO
master port when you're done you should
be allocate that port to tell i/o kit
you're done using it so how do I create
the matching dictionary well it depends
on whether you're looking for an i/o USB
device or an i/o USB interface in the
device case you you ask for an i/o
service matching dictionary with the
device class name if you're looking for
an interface you asked for the
dictionary with the interface class name
then you add qualifications to the
matching dictionary again these are the
qualifications we showed a table earlier
they're from the section 3.10 of the USB
come class specification and in the
current shipping version of of USB the
i/o USB family the one in 1003 and in
1002 in 1001 and 1002 this matching
dictionary what that means is that if
you want to match say the second line
which was ID vendor and ID product you
must include both of those fields in
your matching dictionary and you may not
include any additional fields in your
matching dictionary such as the device
class or the device subclass or else
your matching dictionary will fail so
and you also need to be aware when
creating these matching dictionaries of
the different rules too in this section
of the common class specification for
matching against IO USB device objects
and IO USB interface objects once you've
added the qualifications to your
matching dictionary the next step is to
get a list of services within the kernel
these are the kernel objects that are
that are subclasses of i/o service like
every kernel object IO kit kernel object
is and you and an i/o kit will return to
you an iterator of all of these services
you then can access these services one
at a time to determine if it is in fact
the object that you wish to control so
to use this IO this IO service T that I
you back through this iterator you then
can call IO create plug-in interface for
service this is the call that
instantiates the pair of objects the
device interface or interface interface
on the user side with the device user
client or the interface user client on
the kernel side this call creates those
two objects sets up that communication
channel and allows you to then
communicate with the object that you
want to communicate with so if you were
creating a user client if you were
looking for a user client for the USB
device object you would use the first
call here with ki o USB device user
client type ID and if you were
interested in the interface instead you
would do ki o USB interface user client
type ID these these big long constant
names by the way are defined in IO USB
live H once you have this IO CF plug-in
interface this is actually a very small
interface that that does not quite yet
represent the the device or interface
you're looking for you have to then call
a function called query interface to say
I'm looking for this particular type of
interface whether it's the device
interface or the interface interface now
let's say I have an i/o USB device
interface what can I do with it well
this would be the pieces of a standard
initialization sequence you would open
the device you would then get a pointer
to the configuration descriptors for
this device you might then find the
configuration value out of one of those
configuration descriptors and call set
configuration and then once you've
called set configuration you would be
interested mostly in in looking at the
different interfaces that have now
appeared on this device so you would
create an interface iterator and this
would in turn return to you io service T
tokens for the various interfaces
attached to this device and you could
create interface interface
for each of them to to find the one
you're looking for when you're done of
course you should close that USB device
and release the interface so what now
that I have an i/o USB interface
interface what might I do with it well
you would call USB interface open to
obtain exclusive access to this
interface you could then find out how
many endpoints this interface has in it
you might choose an alternate interface
if you know that there is one you might
get the properties of each of the pipes
in this interface you could then call
Reed pipe and right pipe to read and
write data across this pipe to the
endpoint you're interested in and when
you're done you would close the
interface close the i/o USB interface
and then release the interface interface
how do you do a synchronous i/o from
user space you don't need to be careful
about doing synchronous i/o versus
asynchronous i/o the because the user
space threads are never running on the
work loop it's okay to do synchronous
i/o all the time if however you want to
do asynchronous i/o then you need to
take your interface whether it's the
device interface or the interface
interface and create an async event
source and add that event source to your
CF run loop and documentation for CF run
loop is in CF run Luke H and you would
then call CF run loop run and your your
event source and possibly more than one
event source would then get processed by
that CF run loop calling back the
callback routines for your asynchronous
calls again the the USB specific header
file in the i/o kit framework is listed
above it as I said it's in the i/o kit
framework and it provides the API for
both the device interface and the
interface interface
now how do you package this up if you're
writing a user mode driver we really
discourage applications from talking
directly to USB devices although it
certainly can be done using this
mechanism but how do i package this up
well typically you would package this as
either a mock o CF bundle or Ora see a
plug in
you may recognize this slide from the
i/o kit overview the currently there's a
problem with CFM apps talking directly
to CF plugins so you're you you would
package it up as a mock o CF bundle
which could be communicated from the CFM
app and then your bundle would talk to
our CF plug-in which represents the
device interface or the interface
interface if it if the Mac if the app in
question is a mock o app instead of a
CFM app it can talk directly to either
your cf plug-in or our CF plug-in so you
could create a plug-in that was sort of
a layer between because one CF plug-in
can certainly talk to another CF plug-in
so that's how you package this in user
land and get it delivered it can also be
for example a daemon type process that's
always sitting around waiting to be
notified when a particular USB device or
USB interface appears and can then start
using that device or interface as it
sees fit ok now we're going to switch to
the demo machines and we're going to do
a little bit of debugging a little bit
of a demo on how to debug we're going to
show you first how easy it is to debug a
user mode driver
or application and then we're going to
actually attempt to do a two machine
debugging for kernel mode driver using
both machines do you have
very point okay so here is my userspace
driver it's actually just written as a
as a UNIX tool so of course it has the
main routine and and what I'm gonna do
here is I'm gonna go ahead and and just
put a breakpoint right there I'm gonna
go ahead and rebuild the driver just to
make sure that it's built okay okay and
then I'm going to hit the debug can to
show you that Here I am I'm at this line
of code I'm creating my master port I
have all my arguments up here this is
the project builder interface to gdb for
those of you who haven't seen it so I
create my master port
I create my let me scroll this up a
little bit I create the matching
dictionary I'm saying I'm looking for an
IO USB device I'm gonna go ahead and
plug the device in let's see I need to
can actually see where is it yeah
I can actually show you the console the
gdb console and the printf statements
that we stick in here
you know appear on that console so I
create the matching dictionary I add the
property for the vendor name and the
product name which are really the vendor
ID and Product ID someone has filed a
bug with us that those are terrible
constant names and and I agree I create
a notification for the for this
particular type of device and at this
point I think I actually will have been
notified about it because I've already
plugged it in so what I'm going to do is
I'm going to go into my notification
routine and
I'm going to set a breakpoint at the
call to the to create the plug-in
interface and I'm going to continue okay
so now I'm at the create plug-in
interface for service and I'm going to
go ahead and bring up the i/o registry
Explorer it's at the cool
okay so this is the device I just
plugged in it's called USB toca and
that's the name string that's inside the
device and what we can see here is that
there is no user client attached to this
device yet this user client an it object
is used to tell i/o kit which object to
create when it's when someone asks for a
user client so now if I go back to my
debug statement here and create the
plug-in interface for service now if I
go back to i/o well it should have
created it hold on oh I need to do the
query interface that's gotta be what it
is
well looks like IO registries not
okay there's the device user client I
don't know why this version of IO
registry is not updating but there's the
IO device user client which is the
kernel side object of the device
interface pair so that I am now I now
have a user mode application
communicating with this IO USB device
object so then what I will do is I will
I will call get the vendor get the
product and get the release number we
can see that the vendor if I look in my
my variables here the vendor is 1209
which is what I expected and the product
is 768 which is also what I expected I
do a little test to make sure it's what
I expected and it is and so I open this
device using the USB device open
function then I'm going to configure
this device and so I'm I'm going to
first determine how many configurations
there are and I can see that there's one
configuration and I'm going to then get
the configuration descriptor pointer for
this configuration and here is the the
configuration description pointer the
configuration value that I'm going to
end up calling set configuration with is
a 1 and I call set configuration and now
I ochio registry explorer does update
and when I go and I look in fact now I
have an interface associated with this
device attached to this device that
interface appeared in the kernel because
I made a set configuration call but I
didn't make it directly in the kernel I
made it from user space and it went
through the device interface and and
created the configuration so that's
pretty much the end of this user
debugging demo but you can see that it's
pretty simple to to debug this using
project builder in gdb because you're in
user space and you're not trying to you
know step through code in the kernel so
that's the user space driver demo again
it's much easier so we
recommend that that's where you put your
driver however in the case where you
really need to be doing a kernel-mode
driver for example a a mouse driver
keyboard driver mass storage driver and
so forth we're gonna show you the
techniques that you need to use to do
two machine debugging with gdb this
stuff is defined in an i/o kit document
debugging an i/o kit driver or something
like that that that goes through these
steps but we're just going to show you
that in fact they they it can be done so
the first step that you need to do when
trying to set up a two machine debugging
environment is you have to know the
hardware Ethernet address of the other
machine you could look at the ARP table
for those of you who are UNIX people and
you say and the first thing you got to
do is you have to delete the ARP entry
for the other machine so I'm gonna do
that then you have to create a static
entry for that IP address for that
ethernet that IP address to that
Hardware Ethernet address all of this
again is documented in the debugging
kernel driver document now that I have
that connection it's a permanent
connection in my ARP table which you can
see and so now if I have the driver over
here cousin we have I can talk to it so
can we split the screen and put demo two
on one of them okay he's going to load a
driver and we have a debug statement in
the in the start method of this driver
he's gonna plug it in and he's frozen
because he's got this debug statement so
okay so I'm going to load gdb and then
I'm going to say target remote KDP this
is in that document again and then I'm
going to attach to this other machine
okay I am now connected to that other
machine I can do a back trace and I can
see that I'm in a debugger call but I
need the symbols for my for my driver so
I will use a command called show all kay
mods which shows me all of the kernel
modules that are loaded on this machine
so that would be that one and and then
I'm going to say okay oops
here's the address of my driver and now
I need to create symbols for this driver
which I built on this machine so
so I do K mod Sims
and because it is dependent on the IOUs
be family and I need to know the address
of the IOUs be family so that's in here
as well so you can see that debugging is
somewhat more painful when in the kernel
- hole
okay I gotta get the address again okay
now I have a simple file for this and so
I add this symbol file to my to my gdb
session and now if I do a back trace I
can actually see all my line numbers and
and it didn't put it in well okay so we
are out of time basically I can then I
attach to this thing I can go ahead and
continue so this machine is now up and
running again you can see by that and he
can unplug the device and oh it kernel
panic well we were actually expecting
that we did that on purpose and if you
look at the back trace you can actually
see where in the drive where it
terminated and if I had the symbols
working properly which we're out of time
to do we put a line of code in there the
dereference null and we did that on
purpose to try to debug it but again we
do need to answer some questions so
we've taken five minutes of
question-and-answer time to do this but
that's the process you have to go
through to do two machine debugging so
it's somewhat more painful and we
recommend doing user mode whenever you
can okay
[Applause]
you