WWDC2001 Session 104

Transcript

Kind: captions
Language: en
good afternoon and welcome to worldwide
developer conference I'm Craig Keithley
i'm the USB and firewire technology
manager in Apple's Worldwide Developer
Relations program and I've been working
on USB and firewire drivers and products
probably for the last four years I've
worked on just about every different
kind of driver that exists at Apple and
it's been incredible to watch the
adoption of i/o kit over the last year
I've worked with developers internal
Apple and external to Apple and all of
them come to me and tell me how powerful
it is how useful it is the object
inheritance model that we have makes it
easy to write drivers that leverage off
of the families that are in the
operating system and so to introduce
this to you describe it to you in depth
I'd like to bring up Dean Reese Dean hi
good afternoon
Dean Reese I manage the i/o kit team at
Apple and I don't know how in depth
we'll be able to get today because I oak
it covers a lot of API and a lot of
ground but we'll definitely cover as
much of it as we can and there's a whole
lot of sessions for the rest of the week
that will hopefully give you the details
you need to be able to make your
products so in introduction we're gonna
talk about sort of the design and the
scope of i/o kit it's an i/o kit is just
one word it doesn't really tell you what
all it covers and it's actually
important to know what fits in IO kit
and what doesn't fit an i/o kit will
talk about kernel extensions some
because that's the basic unit of
delivery for i/o kit components and I'm
also going to spend a little bit of time
talking about how applications can get
access to devices this is very important
obviously somebody's porting any kind of
application that maybe wants to sync to
a handheld device or touch any kind of
hardware that's that's out there where a
higher level abstraction is not
appropriate so we'll spend some time
talking about the various models for
that so it's only a couple sessions into
WWDC this year but I'm sure you're
already familiar with this slide we're
gonna be focusing in on the bottom
the Darwin capsule there and to expand
on that a little bit this is the
explosion of the Darwin kernel and
there's a lot of components in there IO
kit is just one of them and drivers are
built based on i/o kit primitives and
provide services to the rest of the core
OS the Darwin kernel and to higher level
applications as well so what is IO kit
there's really three views of Io kit to
driver developers it's it's a framework
that makes it as easy as possible
to write a driver that's that's our goal
is to make it as easy as possible to
write a device driver for Mac OS 10 and
I've got in my definition here correct
Mac OS 10 drivers we've had to spend a
lot of time thinking about the threading
model and in memory management because
it's very different under Mac OS 10 than
it was under plastic Mac OS or even
under other operating systems out there
in the world so what we wanted to do was
put as much of that knowledge into one
framework as possible so you didn't have
to go and reinvent all of that and
that's one of the fundamental goals of
i/o kit application developers ioq it
presents a abstraction of hardware and a
rendezvous mechanism and by rendezvous
mechanism I mean also arbitration where
you can have one device being shared
between two applications there's higher
level abstraction services built higher
up in the operating system but all of
them ultimately boil down to what ioq it
allows for mutual exclusion of devices
and to the OS ioq it really serves as an
abstraction of each attached device
that's sort of its simplest definition
it tries to provide an abstract object
for each device attached to the system
now more from a development perspective
IO kit is a framework in a collection of
families for developing Mac OS 10
drivers simply put that's what it is
it's a library but it's a very big one
with a lot of api's it's delivered from
the Mac OS 10 developer CD so there's
not a separate SDK or KDK that you have
to download if you've got the Mac OS 10
developer CD you have i/o kit now we
will be occasionally posting updates to
our website but um
basically go to go to the CD and you'll
find the developer package there that
contains all of the API is an i/o kit
from a runtime perspective I okayed as
part of the Darwin kernel it's compiled
into the kernel it executes sin the
kernel address space and all of the AP
is that i/o kit presents are present in
the kernel framework this is that is all
the in kernel ap is are present there
the applications that we excuse me the
AP is that we present to application
developers are present in the i/o kit
framework now the distinction here is as
has been said in previous sessions
inside the kernel and outside the kernel
development environments are very
different there are different api's
there's different rules for threading
from memory management and as a result
we have to have two separate sets of
api's for that the i/o kit in kernel
implementation is an object-oriented
design based on a subset of the C++
language now the application level bits
are still object-oriented in nature but
they are straight C we don't use C++ for
the OP level api's now the question of
course comes up when do I need to use
i/o kit and when not well if you're
developing a driver that must run inside
the Mac os10 kernel and I'll spend a
little more time on that later then
you're probably going to be living
inside the i/o kit framework you're
gonna be making use of i/o kit api's and
if you're developing application that
needs to directly access devices or
drivers then you're gonna make use of
i/o kit as well not necessarily by sub
classing from it but by making use of
its user level API is to get at those
get at the hardware now not all drivers
in Mac OS 10 are i/o kit drivers this is
important
most of the imaging devices scanners
printers cameras things like that are
not managed inside of i/o kit we really
only try to put things in the kernel
that need to be there for other reasons
and I'll dive into some of these details
a little bit later the design goals of
i/o kit are pretty straightforward it's
a very complex task writing a driver for
system that can run on NP and have a
pre-emptive scheduling you have to deal
with multiple address spaces and
also ideally be architecture neutral
something that isn't tied to one
particular platform because is they know
evolution of platforms tends to maybe
render some of those design decisions
inappropriate in a few years so we've
spent a lot of time trying to hammer
these things out drivers really focus on
mechanism when you write a device driver
you're focusing entirely on the bits of
code that are necessary to push data in
and out of a physical device ideally
we'd like to keep policy out of drivers
the idea is the drivers there to move
data between the operating system and a
device and you really want policy user
experience issues to be driven at a
higher level and third this is an
important point though third parties can
a third party developers can extend Mac
os10
and IO kit just like Apple can through
the darwin open source community you can
download 95% of the source code to i/o
kit and its families you can develop the
new families you can develop drivers for
existing families it's a very flexible
model and we don't actually do anything
with an apple that we make it impossible
for a third party to use now I ok it
itself is as I said a very very large
framework and it contains a lot of a lot
of API a lot of surface area well just
for us to be able to manage it and think
about it we had to subdivide it and the
way we did it with the way we did this
is by dividing it into families and then
within families multiple drivers so i/o
kit in and of itself is just a framework
it doesn't have any intrinsic protocol
support scuzzy is not part of the i/o
kit meta framework it doesn't understand
scuzzy commands the protocol specifics
are embedded into families the idea here
is you would have a scuzzy family that
understands what it means to be a scuzzy
device and a USB family that understands
what it means to be a USB device
underneath each one of those families
are a collection of drivers some of them
written by Apple some of them written by
third parties and all the device
specifics are embedded in those drivers
now iokit families contain classes that
present an abstract view of a device
type that's basically what it's trying
to say there is the family presents the
abstraction of a type of device to the
so if all disk drives fundamentally
looked the same the family the storage
family would present that abstraction
and everybody that builds a device
driver underneath that abstraction would
inherit from it and therefore get sort
of the same appearance to the upper
layers in this way
upper level services can look and find
things in IO kit in a common way a
device driver from one vendor doesn't
look completely different from a device
driver for another vendor if they are
for the same type of device device
drivers inherit the device independent
details from the family and then as a
developer you fill in the device
dependent pieces so just to give you a
shopping list here of some of the common
families you can see a kind of a naming
theme here and there's plenty more this
is probably a third of them there's you
know also ADB family and other somewhat
more esoteric ones that you probably
won't ever see these are the ones that
get most questions about from developers
now from an object-oriented class
perspective drivers in the i/o kit
universe are leaf classes that is they
don't intend to have any other class
inherit from them now
io kit developers excuse me the i/o kit
engineers at Apple who built I Oh kit
have had to write all their code with
the knowledge that you people are going
to be subclassing them so we've had to
put a lot of attention into the subclass
interface now that takes a good bit of
discipline and an object-oriented
experience to be able to design a class
that intends to be subclassed so
fortunately you don't have to bother
with that for drivers basically you
right you inherit from something we've
written you write the bits that you need
to and you don't have to worry about
third parties inheriting from you unless
you want to and that's a whole nother
presentation and your driver really
should focus in on the requirements of
one particular device or possibly a
collection of devices that are very
similar now there's a data structure
that's in IO kit it's actually exists in
the kernel address space called the IO
registry and it's important to
understand the IO registry is not
something that's brought in from a file
it's never archived on disk
it is literally the network of active
iokit objects in the system and it's
roughly a tree structure
I say roughly if you if you browse it
through any of our tools like IO
registry Explorer or IO edge it will
appear as a tree structure it is
actually possible to have one node have
two parents this is done for fan end
devices like a soft raid might choose to
use that for implementation so the
technical term I believe it's a a
directed acyclic graph but basically a
tree structure now what it does is it
tracks all of the relationships between
the various objects as I said the nodes
in this graph are the actual objects
that make up your drivers and the other
family components and so there are links
between them there are planes within the
IO registry there's a service plane
which is the one you'll hear about 99%
of the time when you're looking at IO
yet the service plane basically says
this object depends on this other object
for some service and the service is
usually named by the class name so IO
PCI device provides a service to some
other driver and the service it provides
as access to an IO PCI device IO kit
tries to mimic the physical world
wherever possible also built into IO kit
is the basis for driver matching now
each family can override this and can
change the behavior so the example I'm
about to walk through is gonna be true
99% true for every driver that you see
but there's always going to be some
slight variations so you have to check
with the header files for your family
and documentation that's available for
whatever family you're in to understand
how the matching will be different for
you it's basically four stages and I'll
walk through those in four future slides
but it's fundamentally a tie a score
system so effectively you've got some
set of drivers identified as appropriate
we use scores to try to figure out which
one is best it's very powerful and
flexible there's quite a lot that you
can do to change the way that matching
occurs as needed so step one device
discovery this is really sort of before
matching begins but during boot or when
a dynamic bus has been scheduled in
rescanned we notice a new piece of
hardware in this case I've written it up
as a PCI device discovered during boot
well the host controller driver is going
to create a nub an IO PCI device nub
that's a term we kind of invented that
represents that piece of hardware if
there's five PCI devices in the system
you'll have five separate i/o PCI device
nubs one that represents each piece of
hardware and that nub will register
itself for matching this is how matching
gets kicked off so in step two it's
called class matching this is something
that's done basically an i/o kit innards
it's not something that the family
typically alters basically i/o kit
starts with a list of all drivers known
to the system everything that's been
installed it we're aware of we then go
through and remove kernel extensions
that don't appear appropriate first off
it would have to be an i/o kit driver so
it's going to have to have an i/o kit
personality's property and we're gonna
look at the i/o kit provider class that
would be the name of the class for which
this driver is appropriate in the
example that this is being an i/o PCI
device all the continuing drivers would
need to list IO provider classes IO PCI
device obviously if it's looking for an
IO audio stream it's not appropriate to
try to match it on top of an IO PCI
device so at the end of this process the
list is sorted in probe score order now
effectively we have a list of drivers
any of which could conceivably talk to
this piece of hardware and based on the
highest score to the lowest score we're
gonna be drilling down a little deeper
so step 3 is called passive matching we
call it passive because the driver being
considered isn't really running it's not
been brought into the system at that
point it's property list has been
brought in and this property list
contains properties that we use to
determine if this driver would be
appropriate the details of passing
passive matching are typically family
specific but generally each family is
going to put properties in its device
and
it's device nubs that would be useful
for it to match on so a vendor ID a
device ID in some cases like scuzzy it
might be a manufacturer string or a
serial number all of these properties
again you look at the family spec but
each each family will publish a set of
properties there that are appropriate
for passive matching the driver is being
considered would also provide
information in their match property and
effectively the passive matching step
goes through and rules out drivers that
are not appropriate if it's you know a
piece of hardware from one company and
the driver says I only want to match on
vendor ID X and it happens to be
different discard that driver it doesn't
match at the end of this probe scores
may have been adjusted now the device
driver itself is not going to have
adjusted its probe score but the family
may have the family at this point has
got a little more information than it
did in the previous step so it may have
decided to promote or move down in
priority some driver based on family
specific criteria I know that the USB
family implements the USB matching
specification and it does in fact affect
the probe score so again we have a list
sorted by score at the end of the step
ideally we're down to one driver but
maybe we're not step four in this
process is called active matching and
it's active in that each driver is
brought in and probed so each of the
remaining drivers is loaded and probed
and effectively they're given the
opportunity in C or C++ code to be able
to talk to the device find out if it is
appropriate for them or not adjust to
their own probe score and return they
can say this device is not appropriate
for me at all
well they can say it is appropriate but
I want to raise my probe score lower on
my probe score because I discovered
something about it that effects that
it's pretty rare that you would do that
but it is as I said this is pretty
flexible
now all the drivers that fail their
probe are unloaded at this point they're
clearly not of interest with the
remaining drivers are started starting
with the highest probe score and
marching down the list until one of them
successfully starts in general if you've
passed your probe you will
successfully pastors start as well so
it's generally the very first driver on
the list at this point but it is
possible that you discover something
during start they causes you to bail on
this hardware and at that point we have
a winning driver we have a notion of
match categories that you might have
noticed in some i/o kit documentation in
the tutorials or some other places in
general you won't need to use that I
bring this up only because I've had a
number of questions about it the idea
there was there may be multiple drivers
could usefully be attached to one device
at any given time and the idea here is
you could have one piece of hardware
that maybe is bimodal it can operate as
a tape drive or it can operate as a
floor buffer whatever and effectively
you can have the floor buffer driver be
in one category and it can win for that
category and you can have the tape drive
driver be in a different category and
win for that category you can only
typically have one driver actually
accessing the device at a time so you'd
have to go through open clothes to be
able to actually acquire the device and
use it but that allows you to have
multifunction Hardware of share drivers
so if you need to do something like that
if you have a really interesting
multifunction device this may be one
technique you can use so this matching
process by the way is repeated
independently for each match category
now developing inside the kernel is is a
bit of an art it's not something that we
encourage all of our developers to do
obviously we want the kernel to be as
lean as possible but it's also something
that isn't necessary in a lot of cases
well talk a little bit about what it
means to develop inside the kernel for
now you have to use Apple provided tools
if you wanted to use code warrior or
some other tool for that you'll have to
contact the provider of that tool and
ask for that support Apple is willing to
work with other tool vendors to show
them what's needed to develop in this
space but presently you have to use our
tools when you want to do debugging you
need to use a two machine debugger we
have a debug environment that uses gdb
and it uses two machines connected over
the Ethernet it's a very powerful
environment there's a lot you can do
with it but it's probably different
unless you've already used gdb it's
probably different than anything you're
used to so there's going to be a
learning curve there once you've learned
it I think you'll find it very powerful
and the fact that you can download
source code from Darwin means that you
can actually do source code level
debugging within the kernel now we have
restricted language features
specifically a lot of the libraries the
standard POSIX libraries that UNIX
programmers are used to using aren't
available in the kernel you basically
use it's a mini environment so to speak
we we would encourage you to go and look
at the headers and the kernel framework
to see what's available there's also a
tool called n/m which will tell you all
the symbols so if there's some UNIX
developer there's some symbol you're
used to using and you just want to find
out if it's in the kernel it's a very
easy way to find out I run an inn and M
on the kernel and find out if it's there
also if you're developing an i/o kit
you're going to be using C++ C++ is
available for other types of kernel
extensions as well but it's pretty much
required for i/o kit kernel extensions
we don't support a full-blown C++ and
from implementation our initial port of
i/o kit c++ was based on a movement
called embedded c++ from a few years ago
and we're going to be of evolving what
that means to us but basically some of
the higher-level features like multiple
in hair
exceptions are TTI these things are not
available also you really need to use
our constructor metaclasses
we have our own replacement for our TTI
that's used to maintain reference
counting and automatically unload
drivers keeps track of a lot of very
important things and if you're writing a
driver and you're making use of objects
that derive from the OS object class
which is the root of all our intrinsic
C++ support then you have to use the OS
metaclass macros when you define a new
class I know this has not been well
documented and we're working on
improving that but it is very important
otherwise you're going to get into
situations that can cause panics and
other crashes because we may decide
download your driver because there's no
references of it
well the references of it but our
runtime system doesn't know that there's
very limited user interaction inside the
kernel you can't just communicate with
an app directly you have to set up
explicit communications now we do have
some basic facilities we have this the
kernel user notification center that
allows you to pop up simple panels that
allows you to possibly launch an
application if you needed to have a GUI
but the code that actually does that is
not in the kernel the code that would
would display on the screen is outside
of the kernel so you can have your in
kernel component cooperate with
something out in user space but you
can't directly talk to Carbon api's or
toolbox api's or anything like that
inside the kernel resources are much
more costly than they are in user land
now the reason this is so is because
kernel memory is typically wired if you
allocate memory in the kernel unless you
go out of your way to unwire it and
that's tricky to do that memory is wired
and that means that that memory is
permanently unavailable for other
applications other components in the
system to use if if that same amount of
memory is being used by some tasks in
user space when that task is not active
those pages will page out and that
physical memory will become available
for something at some other entity to
use so basically you don't want to
develop inside the kernel unless you
have to think that's probably been made
clear the last important reason is
failures are fatal we want Mac os10
to be the most stable operating system
on the planet and obviously you want to
try to contain failures if an
application crashes we want the
application to crash and not affect
anything else if you've got something
inside the kernel and it crashes it's
going to probably bring the whole system
down depending on the nature of the
failure but basically if you crash
inside the kernel you've crashed the
system that being said if you've decided
to forge ahead and write code inside the
kernel here's how you do it
you're gonna have to write a kernel
extension which is basically a package
for binary code to be delivered into the
kernel now you can write a kernel
extension that doesn't exist inside one
of the three well-defined frameworks but
it's not really gonna do very much
because nobody's going to call it so you
really are going to be looking at one of
these three universes the i/o kit
universe the kernel network extension
nke universe or file system plugins the
three of them use fairly different api's
so I can't go into any specifics for the
latter two but basically the idea is the
kernel extensions are there to deliver
binary code into one of these universes
and then the api's that are appropriate
in that space take over from there an
extension in classic Mac OS is not
necessarily a kernel extension on Mac OS
10 if you've delivered functionality in
the past using an extension it may not
be appropriate to do that on 10 you need
to stop and think about the best way to
package your functionality so get inside
the kernel only if you have to so a
kernel extension is the way to get into
the kernel
if you want to do something a little bit
more interesting kernel extensions can
also serve as libraries to other kernel
extensions it's a relatively
straightforward layering mechanism so
let's say that you're writing a family
of drivers for your company that share a
common service library so you've got a
USB version of something in a firewire
version of something and 90% of the code
is the same but you need to have some
bus specific code as well
you can write a library effectively that
contains that 90% common code and then
you can write a kernel extension that
depends on that library for the bus
specific details and when you've done
that you've basically just written a
family that's the way families work
they're just a library that has leaf
classes plug into it so how do you
actually go about writing a library
there's really only a couple things you
have to do that are unique first off you
have to identify yourself by a CF bundle
identifier and a CF bundle version these
are properties in the P list not too
onerous you have to do this whether
you're writing a library not all kernel
extensions have that but you indicate
that you are a I didn't put it on the
slide you indicate that you're a library
by expressing the OS bundle compatible
version which basically says this is the
oldest version to which I'm binary
compatible and that is the clue to the
system that AHA this is a library and
somebody could try to link against this
somebody wanting to make use of the
library indicates this through the OS
bundle libraries property basically you
list all the libraries on which you
depend when you write a kernel extension
here's a list of some of them the list
is very long the first five on this list
are components of the kernel itself you
could say I depend on comm Doppel lot
kernel and pick a version number based
on one that we've shipped and that
basically means you're dependent on some
version of the whole kernel most of you
won't need to do that because you're
only going to be including API from i/o
kit and maybe from mock and Lib current
as well but basically we've subdivided
the kernel into four other player four
other pieces mock BSD IO kit and Lib
Kern and you need to list these
explicitly in your kernel extension when
you're writing an i/o kit driver
we recommend that i/o kit drivers list
calm down a poll not colonel dot IO kit
is a dependency as well as Lib Kern
because that's where all the OS objects
are defined and as in mock because
you're gonna be using primitives from
mock for return codes and things like
that you're also going to want to list
the specific family on which you depend
if you
you are writing a driver that depends on
a family which most of you will be so
for example if you're writing a driver
for a USB controller card that sits on a
PCI bus you would need to include the
the PCI family and the USB family
they're the two families that you depend
on so now to actually talk about kernel
extensions and how they exist on the
file system got a series of slides here
that explodes it if you look at this
this is a directory hierarchy and you'll
see that there's only two files in here
the rest of its all structure we
basically chose to mirror the
application bundle format because all of
our tools are there to deal with it
finder already knows how to deal with
these and they have some nice features
about them it's probably a little bit
more than strictly necessary for
delivering drivers but it doesn't really
cost anything so there are really two
files in the kernel in a typical kernel
extension that are going to be
interesting to you the first one is this
info.plist every bundle of any
description on Mac os10 has one of these
files and basically it is the table of
contents so to speak for the kernel
extension it provides information on
what it is why it would need to be
loaded this is where the i/o kit
personalities with the matching
information goes so I've listed four
basic properties here that are pretty
much going to be necessary for all IO
kit drivers CF bundle identifier
identifies your driver now we've gone
with a reverse DNS namespace because
this doesn't really require Apple to put
some kind of a program together and
police it basically if you can sign up
for a domain name put it in a reverse
order so for Apple we ship drivers and
other components under the name comm
Doppel dot whatever obviously unless you
work for Apple you shouldn't be doing
that
you should be providing your own company
name there and once you get within
within your company domain you can
decide whatever namespace within that
that you want so it's actually pretty
flexible it tends to be a little verbose
but that's okay this information is
usually only thrown around when we're
loading drivers once they're loaded it's
just sitting around is it for me
your CF bundle version is also going to
be important our version checking code
makes use of this to know that it's
getting the latest and greatest piece of
software so it right now the format is
specified is a classic Mac OS very
source something you're probably
familiar with already OS bundle
libraries this is where you list all of
the libraries on which you depend so
comma pol dot IO kit dot whatever family
you know all the libraries that you care
about it's very important to understand
that Mac OS 10 as it shipped so far
doesn't enforce all of these properties
it makes note of them and it does its
best to use them but we didn't have time
to get all of our drivers into
compliance so we have to allow them to
be a little bit fuzzy future releases of
operating of the Mac OS 10 operating
system are going to require that these
be in order or your driver will not load
so for example if you don't have a CF
bundle identifier it won't load I don't
think it would load today actually
because that's a key to a lot of things
if you don't have a bundle version it
won't load if you don't list any bundle
libraries it won't load because by
definition if all you're doing is
calling into the kernel the kernel is
acting is your library you have to list
at least one thing as a dependency
otherwise your kernel extension could
not call any API and if nobody is
calling you and you're not calling
anybody it's kind of a useless kernel
extension and the fourth property here
IO kit personalities this is the only
one that's IO kit specific this is where
you would list all of the personalities
of your driver this is a little subtle
by and large your drivers will have a
single personality that is one set of
matching criteria a few properties to
help your driver get oriented when it
gets loaded but you could also have
multiple personalities you could have
say two PCI cards that you ship that are
similar but slightly different and they
need different match criteria
well one driver could drive them but you
could list two personalities one for
each of those flavors it's it's very
powerful we use this ourselves we have
several multi personality drivers as a
very useful resource go into the
extensions folder on ten and poke around
and see what's there
though I will warn you as I said not of
all not all of our drivers are in spec
so we'll also look at the log that comes
out and see all the warnings that get
posted the warnings are on our drivers
as well so the next file that's
important here is the binary that the
text is delivering in this case I've
exploded the USB family but this is the
actual binary file first and foremost
it's optional which may seem a little
bit odd the kernel extension is a way to
deliver binaries what does it mean to
have it be optional we have a notion of
a driverless driver that is used
occasionally and this is where
personalities come in your in your
personality your iokit personality you
will specify the class the basic class
for your driver and you'll also specify
the CF bundle identify ur for the kernel
extension that contains your the binary
code you can write a driver that depends
on some other driver and actually just
instantiate sits classes directly there
are a few cases where this is useful
basically you would put in some new
property in the personality you know
maybe it's as simple as changing the
Probst or Probst core you know I want to
use this driver over here but I want to
make it slightly different matching and
I want to have a higher probe score okay
enough said about that if you do have a
binary there it's going to be in the
maka format which is the only format the
kernel will load presently and please
run strip dash X on it before shipping
it I've looked at some of the drivers
that have been posted obviously I'm
interested and some developers have
forgotten to run this and it means that
their download size is about ten times
larger than it needs to be and they're
giving away all their debugging symbols
it doesn't affect the runtime at all we
only load the important pieces into the
kernel but you know if you want to have
faster downloads stripped dash X is a
good thing to do
okay last is this plugins folder inside
the the kernel extension contents and
this is this is kind of neat again it's
optional if you have a plugins folder it
gives you a place to deliver other
kernel extensions the reason you'd want
to do this is for the drag-and-drop
functionality let's say you've got a
family a family collection of three or
four drivers that you always want to
move around together as a collection
what you can do is pick one that is sort
of the definitive text and then put all
of the other pieces as subtext as we
call them inside the plug-in folder the
advantage there is to the user it
appears to be one kernel extension to
our loading system however we consider
every kernel extension equal so we don't
care whether it's embedded or at the top
level that's actually important to note
the fact that it's embedded doesn't mean
anything at all it's just a way to
deliver it now the plugins folder can
contain other texts it can also contain
device interface libraries this would be
typically if you're writing a driver
with some sort of a custom interface
that applications would need to get at
directly or if you're writing a family
and you want to provide a user client or
a device interface for for an
application that's a handy place to
store the bundle for that and in the
future they'll probably be other things
of interest that could go in that
plugins folder it's sort of a generic
place to put add-ons only one level of
nesting is supported though you can't
have a kernel extension inside a kernel
extension inside a kernel extension well
we could have made this work but there
was very little use for it the user sees
the top-level that's our intent you can
package things internally if we want to
have more complex relationships and
organizations send us notes on how you'd
like to have that done I don't think
creating a massive tree inside the
extensions folder is necessarily the
right way to do that all right now I
want to talk about the user kernel
boundary this is a this is new I think
to developers just coming in from 9:00
looking at 10:00 there are different
places on Mac OS 10 different address
spaces drivers live inside the kernel
vacations live outside the kernel so
anytime your app needs to talk to a
driver you've got to cross that boundary
it's as simple as that there's no way
around it
Mac os10 and the Darwin kernel and
infrastructure provide a large number of
ways to get across that boundary if you
can use a sufficiently high abstraction
like a file system the file system
provides that bridge for you it makes
use of BSD to cross that bridge and get
data out of the kernel and into your
application networking sockets does that
for you as well but if you can't use a
high-level abstraction you and you have
to use a low-level abstraction you've
got to get directly at a device you can
use we basically it's a CF plug-in
interface that we use called a device
interface oh and the the class in kernel
class that you derive from is IO user
client the name derives from the fact
that from the kernels perspective the
client is a user therefore the object is
a user client so your code belongs
outside the kernel unless you must
process interrupts if you have to get at
an interrupt you need to be inside the
kernel unless you have to deliver
services to an inner kernel component
for example file systems live inside the
kernel so if you're writing a disk
driver and the disk is most likely going
to be used by file systems it makes
sense for that driver to live inside the
kernel it is possible of course to put
almost anything you want outside the
kernel but then moving data means more
user kernel boundary crossings and
that's cost a lot of time so you're not
going to get a huge amount of
performance but you know if it's a
really slow device you can have drivers
live outside and tunnel back and forth
across the user kernel boundary the
other reason for putting something
inside the kernel would be if it
provides a service that's needed by a
large number of clients and it's needed
much of the time now this is kind of a
judgement call here but basically the
kernel is sort of the only guaranteed
rendezvous mechanism on the system an
app running in darwin or cocoa or carbon
can pretty much guarantee it's there and
they their api's to get at it so if
you've got a service that's available
inside the kernel it's a convenient
rendezvous mechanism but if you only
have one client that needs to get at
your
vice or if it's only used one percent of
the time why put it in the kernel that
memory is going to be used all of the
time not just when it's been use so an
example there might be a backup driver -
a device driver that's only used to do
backups most people aren't backing up
all of the time you could have one
application that could load such a
driver when it needs it or it could
process it in user space and then unload
it when it's no longer needed so the
device interface model this is a this is
sort of the app view of i/o kit the
device interface model provides API
access for many devices in the system it
doesn't provide access for all devices
this is important we don't provide
access for applications to directly get
at the PCI bus we do let you get it
devices hanging off the PCI bus so if
you've got a scuzzy controller there you
can talk to the scuzzy bus but you can't
talk to the pci bus it's actually
security issue there also there are some
devices it really doesn't make sense to
get out from user space so we don't
create the ability to cross that
boundary for every kind of device but
the ability is there if we need to make
use of it in the future the device
interface model wraps a number of
mechanisms basically since Mach is the
IPC model everything that we do is
simply a wrapper for that and our idea
there is for i/o kit to make the most
common uses of the Mach infrastructure
easy to use IO user client provides this
so system calls Mach traps which are
kind of similar Mach IPC and Mach
shared-memory all of these mechanisms
are available in the IO user client
class so if you want to set up some
private communication you can make use
of any or all of these as it makes sense
so to a driver this boundary crossing
layer really looks like an internal
client that subclass of IO user client
the object IO user client is inside the
kernel it's talking to the driver like
any other internal component it's just
moving that data across the user kernel
boundary as its primary goal to an
application it looks like a typically a
CF plug-in generally mokou binary that's
the
format but basically it is there's
proxies in both space the proxy in the
user space is the CF plug-in and the
proxy in the kernel space is an i/o user
client
there's weird to talk to each other and
your app talks to the CF bundle is he
have plug-in and the i/o kit driver is
going to talk to the user client so I'm
gonna go through a four step sequence
here of how this works so from an
application the first thing to do is
find the device of interest and the way
you do that is by searching the i/o
registry there's a number of different
ways to search the i/o registry and
discover the the device or devices of
interest but probably the most common
and most useful one is IO service get
matching services this basically allows
you to find all of the instances of IO
scuzzy device or all the instances of IO
USB device and you can provide some
further match criteria that is driven
actually very similarly to the way
driver matching works but basically you
find the devices of interest and you get
a list back and you use io iterator next
to actually walk through that list and
when you find the device you're
interested in you make note of it and in
step 2 you're going to need to call IO
create plug-in interface for service if
this is a CF plug in style user client
the the technique here is to take the
cookie that you got from searching i/o
registry and request a session here so
you basically create a plug-in interface
this loads the CF bundle into your
application and then you call query
interface which actually returns an
interface to the desired device
all right at this point you actually
don't own the device you just have an
interface to the device
this is I okay it has multiple layers
here because we also have multiple
clients competing for devices they may
actually they might all want to have in
a session going to the device but they
don't want to keep it open all the time
then would it be a good citizen and
close it periodically to allow others
the opportunity to use it so in this
case the details are family specific but
basically it follows a pretty basic open
IO closed procedure here is nothing
really surprising there ideally you call
open as late as possible and you call
closed as early and as often as possible
because as long as you have a device
open nobody else is going to be able to
get at it each family will instrument
its own its own arbitration policy but
in general assume mutual exclusion
unless you've heard something else and
ideally if you're doing something to a
device that only takes a few seconds
open it do that IO and close it you only
keep it open if you really need to
because it's in some volatile state that
you can't easily restore or it doesn't
make sense for it to deter a donor's
briefly and the last step is to release
the interface to the device this just
cleans things up if your app crashes
this gets done for you but ideally you
explicitly clean this up there is a
release note I'm sorry I don't have a
more specific reference but if you go to
the ADC website you should be able to
search for this and find it CF bundle
and CF plugins release note describe a
lot of the api's app level api's now I
want to talk for a little while about
the different app models I gave you a
rundown of how to do that assuming that
your app could talk to Amoco plug-in but
there's a number of different
application spaces on Mac OS 10 so for
starters let's take the classic
application well there's no direct i/o
kit access for a classic application it
wouldn't be a compatibility environment
if it included all the i/o kit api's so
basically whatever is present in classic
whatever abstractions are there that's
what you get
all communications go through classic
itself the same is true for a pure
carbon app if you're writing a carbon
app in CFM and you're keeping it pure
you're only calling carbon api's you
only get what's in carbon and that's
kind of the definition of a pure carbon
out so you go through the carbon
framework and that's how you get down
into the kernel and get it drivers but
there's not a tremendous amount there
now it starts to get a little bit more
interesting when you are willing to do
Mac OS 10 specific code in your
application and in this case we've got a
CFM carbon application which has
basically two ways to talk to I Oh kit
one is through carbon in play in places
like iOS cozy action where we've
actually provided some carbon api's for
device access you can make use of that
but you can also make use of the plug-in
architecture that I described in the
previous slides now the coloration here
is a little bit of a giveaway the orange
is represents parts of the darwin kernel
things that apple provides you the
purple is the app environment that
you're using and the blue boxes on the
top are the boxes that represent code
that you write you write the application
itself and you write the CF bundle that
provides the interface down into i/o kit
now this is actually kind of handy
because it allows you to write a
different plugin for running on Mac OS 9
so you can write one application that
runs on 9 or 10 and it would load a
operating system specific plugin to do
its i/o now if you're writing a a mock o
Carbon app it actually eliminates a
layer there you don't have to have the
plug-in because you're not switching
over from CFM tamaco you can directly
call the plugins that i/o kit provides
and of course you can still go through
the carbon environment that's always
available to you and the same is true
for a cocoa app basically you can go
through cocoa or you can go through the
CF plugin what I don't have up here is a
pure BSD application but a BSD
application has all the CF plug-in stuff
available to it plus all the BSD ap is
as well
so I don't have any other details on Io
kit for you today what I do have is a
rather lengthy road map for other
sessions because that's where the
details are going to be conveyed to you
there are sessions on graphics actually
right after this session there is
sessions on image capture sessions on
input devices firewire USB spend a
little time looking through your guide
if you haven't already and figure out
which ones are going to have information
most relevant to you there's a lot of
details there's a lot of API that fits
under the i/o kit umbrella and yet
another page of them
you