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