Hardware • 37:40
This session is an introduction to the Mac OS X USB architecture. Asynchronous vs. blocking design and the implications this has for USB I/O will be covered. Details about the drivers provided in Mac OS X and how older USB drivers will function in the Classic environment are discussed.
Speakers: David Ferguson, Rhoads Hollowell
Unlisted on Apple Developer site
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
Good afternoon and welcome to the USB Overview session. I'm Craig Keithley. I'm the USB and FireWire Technology Manager within Apple's Worldwide Developer Relations organization. And I'm pleased to see you here today. This session will cover some of the basics of USB on Mac OS X. Before I bring up the engineers to have them do their presentation, I want to talk a little bit about the things that we like about USB in X and in general with our Digital Hub strategy.
One of those things is the nature of powering devices over USB. More importantly than powering devices, recharging devices. So I brought with me here today A new cable, this is a USB adapter cable for a Palm 5, has a sync button in it, and it charges the Palm 5.
You think about our iBook commercial we're running where the gentleman's sitting in the middle seat and he's playing with all his neat devices at 35,000 feet. It wouldn't be too cool if all those devices required a power strip on the floor of the plane with a 110-volt to 12-volt converter to run all those devices. So we really want you to look very carefully at powering your devices over USB and charging them over USB.
Some devices that are good about this are things like the Zip Drive, some of the floppy drives. They receive power over the USB bus. They operate without the need for an AC adapter. This is a great way to go. And if you can charge them, that would be even better.
Now as you know, we've been doing USB for about four years now. We've got a lot of experience in it. We've got some of the best engineers in the business working on USB for Mac OS X. We've made great progress. We know we have more distance to go on this. So I want to leave you with a couple of thoughts with regard to helping us move forward on USB on X. The first one is, please use the bug reporting system. We do look at those bug reports very seriously.
Don't assume that we've seen a bug report. Don't assume that if it's happening to you, we must have seen it and we must be fixing it. We want to have those in the database. We want to be working on them. We want to know about them. The other thought that I would share with you is that we are working very aggressively on providing updates. You've watched us produce three updates in the last 58 days.
We will be doing more. So if you get your bug reports in, there's a very good chance we'll be able to move forward on them. So with that, I'd like to bring up Rhodes Hollowell and Dave Ferguson. Dave is the software engineering manager for USB development on Mac OS X. And Rhodes Hollowell is our lead engineer on USB development. Thanks very much.
Good morning. Thank you, Craig. Well, what we're going to talk about today So what you're going to learn in this session is we're going to go over the USB hardware architecture as delivered in Macintosh models that we ship today. I'll talk a little bit about where the different ports go, numbers of controllers, compatibility with different controllers, that sort of stuff.
We'll talk about the driver architecture in Mac OS X, specifically how it fits in with I/O Kit and how that family fits with other families. We will talk about converting your Mac OS 9 drivers into 10. So obviously you have a lot of experience developing Mac OS 9 USB drivers. We have a lot of devices and a lot of drivers.
And so I know that people have things and they want to get them converted over to OS X. And so what's the same, what's different, what do you need to do to get started? In doing that, we'll detail a little bit of the differences between kernel mode and user mode drivers.
And we'll talk about what happens today. When you have classic and you have support for existing USB drivers, which USB devices can be operated in classic? How do they work? How does classic and native OS X drivers work together? And finally, we'll do a quick review of Apple's class drivers, the drivers that we deliver so you can deliver USB products that don't have any software component at all that you need to, that a user needs to install. Just drivers that we bring.
So, universal serial bus hardware has been included in every Macintosh model since we introduced the iMac in August of '98. Today, every single machine that we ship has two independent USB buses. Both of those are OHCI, open host controller interface compatible. Our software is compatible with OHCI controllers, so in fact, in addition to the two buses that we are supplying, it's also possible to install PCI cards or Cardbus cards that have OHCI compliant controllers. And our software will detect those cards and support the USB bus on that.
Now, other USB hardware that we have included with every machine In addition to the two independent buses, we are also including hubs in both our monitors as well as the keyboards. Now, the hubs that are in the monitors are all self-powered hubs. They provide more high-power ports, just like the ports on the back of a machine.
And the hubs that are in the keyboards are bus-powered hubs, so they can only supply the 100 milliamps of power and are useful for some bus-powered devices, usually other self-powered devices that it's just more convenient to plug them in right on the keyboard. So controllers, mice and stuff attached to a keyboard, other high-powered devices, we've made it convenient to put ports on the back or on the sides of monitors.
So let's talk about how the I/O USB family in Mac OS X fits into the I/O Kit family architecture. This slide shows basically I/O Kit as a large field with a whole series of families. We've denoted some of these families with different colors indicating the type of family they are. The I/O USB family is one of these on the left which is a transport family. It's one that actually transports data over some type of bus.
So I/O USB family, Firewire family, SCSI family are all capable of transporting data over a specific bus. Things such as the I/O Serial family, the SCSI architecture family, the audio family don't necessarily have a specific hardware component associated with them. They're responsible more for managing and converting data to transport over some other family's transport mechanism. I/O Kit includes a lot more families than the I/O Kit. The I/O Kit includes a lot more families than this. I'm just trying to give you a sense of where USB fits into that family architecture.
Okay, thank you Dave. So we're going to talk a little bit about the I/O kernel driver stack. What happens when the system detects that USB is present? So at the very early stage of USB initialization there is an I/O PCI device that the system detects and the PCI family is responsible for detecting this device and that device is the USB controller chip. The USB driver is then matched against that controller chip and the name of that, the class of that driver is I/O USB controller. Now this driver then initializes the USB controller chip and and instantiates an I/O USB device. This device represents the root hub.
Inside the controller chip. So that USB PCI device is then matched by I/O Kit and it loads another driver. And the name of that driver is the Apple USB hub driver. So this driver loads against any hub in the system, including the root hub. But in the case of the early initialization of the root hub, it then scans the bus looking for USB devices. So let's say we have a pair of speakers plugged into this bus.
Well, the I/O USB controller can have more than one child or client that it is a provider for. And so it instantiates another kernel object, C++ kernel object, the in I/O Kit called, that is an I/O USB device representing these speakers. There is a driver inside the system that matches against this device because it's a composite device. And that driver is then used to connect the USB device to the I/O USB hub. And that driver sets the configuration inside the device, which causes the interfaces to appear. So one of these interfaces is the audio channel for these speakers.
And so this I/O USB interface appears and I/O Kit then matches a driver against it. And in this case, that driver is the Apple USB audio driver. So this is just a quick overview of the stack of the I/O Kit objects. We will go into some more detail today. and we will go into even more detail on this tomorrow.
Now, USB drivers are not members of the USB family. They use the USB family for their transport mechanism, and so they are clients of the USB family, or in other words, the USB family objects are the providers for these drivers. So here, for example, is that same audio driver, and its provider is an I/O USB interface object, but the driver is itself subclassed from the I/O Audio Device class, which is a subclass of I/O Service, I/O Service being the base class for all I/O Kit objects, and so this driver is a member of the audio family.
and in turn this driver may have its own clients for what to do with this audio stream. Another example would be the HID interface which then loads the HID driver which is a member of the HID family. We represent this with these puzzle pieces because there can be a different HID driver that is a subclass of the I/O HID device class which uses a different transport mechanism than USB.
Now, how does the API for USB in Mac OS X compare with the API in Mac OS 9? Well, many of the function names are similar, but in fact the API is vastly different in the respect that in 9 you have a parameter block interface, whereas in 10 you have a series of C++ methods that you call you know, with C++.
So, and also, in 10 -- in 9, all calls to the API are asynchronous calls. You must provide a callback routine, and everything happens asynchronously. In 10, there is a capability for making synchronous calls to the usb stack. However, you must be careful about making these synchronous calls, because it is -- there is a potential to deadlock the system. Finally, in 9, everything is -- all memory buffers are passed as held and locked memory buffers, whereas in 10, it's possible to pass buffers using an I/O Kit object class called I/O memory descriptor, which we will get into in a little more detail.
Now in the kernel, the USB family provides three basic object classes that represent the contents of a USB device. These three classes are I/O USB device, which is an encapsulation, if you will, of the device descriptor inside the physical device. Then there's the I/O USB interface class. This is an encapsulation of the interface descriptor within a particular configuration of the device.
So if a configuration has three interface descriptors within it, there will be three I/O USB interface objects which are created to represent those three interface descriptors. Finally, there's the I/O USB pipe object. This object encapsulates the contents of an endpoint within an interface descriptor and it also provides the channel between the client software and the endpoint on the device.
From user space, because Apple recommends that you write your drivers in user space if at all possible, we provide access to these same objects, but we do them using the I/O CF plug-in model. That is part of I/O Kit and we have two plug-in types that we provide in user space. The I/O USB device interface and the I/O USB interface interface. Now these names can become a mouthful. The I/O Kit team decided to call these plug-ins device interfaces.
And of course in USB there is already the concept of a device as in a device descriptor and an interface as in an interface descriptor. And so we actually had device-device interfaces and interface-device interfaces and that got to be a little much. So we called them the I/O USB device interface and the I/O USB interface interface.
How do the memory buffers compare? Well, as I said before, in Mac OS 9, when you passed a memory buffer to USB, because it needed to use DMA to transfer the actual data, you would have to lock the memory and make sure that it was physically held and resident and pass a pointer to this memory.
In Mac OS X, this is handled with a concept called an I/O memory descriptor. I/O memory descriptors are more flexible than just straight memory buffers because they can provide access to memory that is not necessarily contiguous. A piece of the memory can be over here, and then the next piece of the memory can be over here, and then another piece can be somewhere else. And the memory descriptor can describe that, and then the USB engine can then transfer the data into disk drive. So, these are all joint buffers.
Also, if you are accessing, passing the memory in from user space, the memory does not necessarily need to be wired into the kernel address space in order to be used. These memory descriptors can describe memory that is inaccessible from the kernel directly, but is accessible using DMA. And this is a benefit because wiring memory into kernel address space is a very accessible process.
It is a very expensive operation, and one that we hope to avoid at all costs. Now, user mode code that accesses USB does in fact pass these buffers as straight memory buffers, just pointers, but within the kernel the memory descriptors are used to manipulate them and to get the physical address for the DMA engine.
As I said before, with OS X it's possible to use synchronous I/O as opposed to having every call in the API be asynchronous. This means that you can make a call that will block your thread until the call completes. If you are writing a user land driver, this is something that is always safe to do.
You make a synchronous call, your thread blocks, and when the call completes, your thread picks up again. If you are running a kernel mode driver, you must be careful that this synchronous call does not deadlock your thread. And there are specific guidelines to help do that and we're learning more about how to document that as we go along.
If you do make asynchronous calls, then there's two methods for continuing the processing once your callback routine is called. Within the kernel, your callback routine will be called immediately upon the completion of the routine and your execution thread will in fact pick up at your callback routine once the processing is done. It's slightly different in user mode.
There is no mechanism with Mac OS X and with I/O Kit to make direct callbacks from kernel run threads to user threads. And so what happens is a Mach message is posted on a port from the kernel side and then there's a user thread that will check that port using the CF run loop technology for that callback message. And at that point dispatch the callback routine.
So how do drivers get matched and loaded with Mac OS X? Well the first thing that happens is when a device is plugged in, the hub driver that is controlling the hub to which that device is attached notices the port, turns it on, does typical USB enumeration things, and creates the I/O USB device based on the information it finds in the device descriptor.
It then tells I/O Kit, I have this new device, please find a driver for it. So first, I/O Kit looks through its entire table of drivers that it knows about and it creates a pool of drivers that are capable of driving an I/O USB device object. It then passes the information from the I/O USB device object into the USB family with the information from the driver and it says, "Tell me what you think about this driver running with this device." And the USB family does a scoring system where the different drivers are scored based on the criteria specified by the USB common class specification.
This creates a ranked list and then the I/O Kit goes through the drivers and can call the probe method such that any driver in the list can say, no, I'm not interested in controlling that device. So this is a three level matching system and at the end there is an ordered list of drivers with a ranking.
The I/O Kit then takes the top driver in that ranking and says, okay, it's your device. And as long as that driver comes back and says, I got it, it's mine, then that's it. If the driver comes back and says, oh, no, I changed my mind, I'm not interested in that device anymore, then the next driver in the list is tried.
So what happens when a driver, when a USB device is removed? You unplug it. Well, the hub driver again notices that the device has gone away, and it issues a terminate message to the IO USB device object, which in turn sends a message to each of its children, including a driver and potentially any IO USB interface objects, which in turn send messages to their children and so forth, so that the leaf node of the tree gets notified, hey, it's time to go away. It cleans up, and then that propagates back up, so that eventually the IO USB device object cleans up and goes away.
So if your driver is the leaf node in this process, you get a message saying, hey, it's time for you to stop doing any I/O because your parent is about to go away. The driver then says, OK, closes the parent, terminates its state machine, and I/O Kit removes the driver from memory.
So how do drivers, if you will, work from user space? Apple, again, recommends that you write your USB driver code to work in user space. However, you have to be aware that user space drivers, which are really just Threads that run in user space and are typically packaged as libraries, but in one sense they function just like applications in that the matching has to, you know, they have to, there's no automatic matching for them. So these drivers do not compete with the kernel drivers, so if there is a user space driver for a particular device and a kernel mode driver for a particular device, the kernel mode driver ends up winning the race and controlling the device.
User space drivers can have code that's running, daemon code, for example, that gets notified by I/O Kit whenever a new USB device appears in the system. However, once again, this notification doesn't happen until after the kernel drivers get a chance to control the device. And so, some of the things that we've seen in the past, and I'm not going to go into too much detail about this, but I'm going to go into a little bit more detail about this, is that the kernel mode drivers are not the only drivers that are running the device. The kernel mode drivers are not the only drivers that are running the device. Sometimes it may be necessary to create a dummy driver in the kernel which claims the device so that then later on some user land code can take over that device.
So if you are running in user code, how do you find the device you're interested in? Well, there are some I/O Kit service calls that you use to find your device. The first one, here are some I/O Kit calls. The first one, I/O Service Matching, says I want to create a matching dictionary that looks for any device in the kernel whose type is I/O USB device.
Then it calls I/O Service Git matching services and it passes in this matching dictionary and I/O Kit returns an iterator of all the devices that it found that match that criteria. Finally, the user code can create a while loop that looks through all the devices of this iterator and decide which ones it is interested in controlling, if any.
So how do we share a device? Well, Dave's going to talk about this. So let's talk a little bit about device sharing now. The I/O Kit model for using a particular object down in the kernel, whether it's a USB interface or a USB device object, is that it's exclusive access.
Only one thing can be accessing that particular object at a time. The I/O USB family enforces that by allowing only one entity to open that at a time. However, there are methods and mechanisms by which tasks or threads can cooperatively share a device. And as we're all starting to develop drivers and we're getting more user mode drivers, people are interested in how we do that. And I want to talk a little bit about that.
The key is a message method that your driver provides, whether it's a user mode driver or a kernel mode driver. And there are two specific messages that are passed. One is... . And this happens when you have a particular device open. One is a message that says, "I/O message service is requesting close." So this says that another entity is attempting to open this device, and it's requesting that the entity that currently has it open please close down. Now, you don't have to close down at that. You can know that you have I/O active at the time, and you don't want to do that.
But if you are merely keeping the device open, let's say in the middle of the night, let's say in the example that it's a modem and you're waiting for a call to come in, well, the driver that's waiting for a call to come in can go ahead and close up because a call hasn't come in, and another entity would like to use it to make an outgoing call. So you can close that, and you can get notifications whenever someone else closes a particular object, a USB device or a USB interface object.
The other message that can come in while you have the object open is the "I/O message service is attempting open." So this is where you can find out that another entity is trying to open you, but he has not requested that you close down. He's not requested to grab the interface. He's just tried to open it to see if it's available.
In general, a shared device would want to close down in that particular circumstance as well. So requesting close is kind of a more urgent request to close down, attempting open is your clue that someone else would like to access that device, and you should close it if at all possible. Now, this sharing mechanism is really how we intend for user mode control panels that want to set particular states in a device might get to that, even though a kernel mode driver provides most of the transport with the device.
The user mode driver would attempt to open that thing, that object, and would do it with the claim flag, we're working on the API to get that set up, that generates this "Services requesting close" message, and that's how a driver knows. Now, this device sharing model also works with USB in Classic. Classic is nothing more than a particular application that is being a user mode driver, okay, for one particular USB device object down in the kernel. Let's talk about the Classic side for a moment, and then we'll talk about how Classic uses Mac OS X.
Classic presents the entire Mac OS 9.1 operating system to applications. That includes things like USB driver. Our entire Mac OS 9 USB stack is living within the Classic environment. If you have an existing Classic driver for a vendor-specific device, Classic can open that device object using the user mode mechanism and can make that device available just as though it were plugged into an OHCI controller directly attached to Classic.
And the USB stack will do all of the exact same enumeration, discover a device, load a Classic driver exactly the same way it does in Mac OS 9. Right, right. So a driver shouldn't see any difference between how a device behaves within Classic and how it behaves in pure native Mac OS 9.1.
Today, Classic attempts to capture two different kinds of devices. The first is printer devices. Because we have the print center inside of Mac OS X that has a USB client and already knows to talk to USB printing devices for things that have 10 native imaging drivers available, it's already available for applications that want to print in Mac OS X. Some applications such as Classic wouldn't have access to the Mac OS X print center, so we decided to make printers available directly within Classic.
And we just used whatever driver was available within Classic, whether it be the Apple USB printer class driver that we provide, or whether it's a vendor-specific printer class driver, we allow that to load and match the same way it always does. So within a Classic application, you use the chooser to select which printer it is you want to print to, and printing works by using the Classic USB emulation.
The other type of device that Classic will attempt to claim and control are vendor-specific devices. Any vendor-specific device that wasn't claimed by a vendor-specific OS X native driver, well it just defaults to going to the Classic user mode driver. Now, this can be a little tricky while you're trying to develop your code, because you might have Classic loaded, and Classic is loaded and running so that you can run some legacy application, and meanwhile you're trying to do KMOD load of specific drivers, and Classic keeps grabbing those devices every time you plug it in, because it grabs vendor-specific devices and printer devices.
Classic is migrating toward using the sharing model, the same one I just talked about right here. It's going to use and support these exact same messages, so it's going to play fair with all the other user mode drivers and all the other kernel mode drivers. As long as we all use this sharing model, things are going to work great.
The Apple Class drivers that we are providing with Mac OS X is almost the full complement of drivers that we're providing on 9 currently. The drivers that we are providing are of course the Hub Class driver and the HID Class driver for HID devices, mice, keyboards. We do have a new API available in Mac OS X that we did not have in Mac OS 9 called the HID Manager. The HID Manager encapsulates a lot of the same functionality that we had within the HID library within Mac OS 9.1.
The HID library functionality is rolled into this new HID Manager API. The HID Manager, in addition to working with USB HID devices, also works with other HID devices that might be attached to the system and actually makes them look like USB HID devices. It uses USB's HID report. It uses HID reports and HID report descriptors as a mechanism to describe the data that comes from a device.
So ADB mice, you know, if Mac OS X was running on a machine that had ADB, an ADB mouse would actually show up in the HID Manager just like it were kind of a USB device with an actual report descriptor and reports and all of that information. The other drivers we're providing include the Audio Class driver.
The Audio Class driver currently supports stereo, input, output. The HID driver is also available in Mac OS X. The HID driver is also available in Mac OS 9.1. Audio Class is one of the drivers that was updated with the recent 1002 and 1003 updates included new USB Audio Class drivers and there's more on the way from the Audio team. Those guys are working really hard trying to get all the support for all the devices that are out there.
We have a Mass Storage Class driver. This does actually more than our Storage Class driver in Mac OS 9 did. The Storage Class driver in 10 supports optical media as well. The Storage Class driver in 10 supports optical media as well. Because the Mass Storage team has abstracted the SCSI architecture model family, that was one of the families that we saw in the I/O Kit family, it can actually support other devices that are attached via USB as its transport mechanism, but actually are communicated with SCSI commands.
The storage class guys are working on their stuff too. We're shy on a couple of things that work pretty well on Mac OS 9. Manual eject and stuff is on its way. Communication devices. This is the exact same support for abstract control model modems. You have that available in Mac OS X, just like you did in 9.
And of course we have printing class support. What we had on Mac OS 9 was printing class support for any imaging chooser type driver. And that included our LaserWriter PostScript driver for PostScript printers. We include exactly the same functionality on OS X and it's possible to write new print center imaging pieces that plug in and still use our printing class transport mechanism. You just have to worry about the imaging. We'll handle that. you get the data to the printer.
Okay, that about sums up our overview. We have a few slides here with where you might turn for more information. In addition to the URLs here, you don't need to copy those down. They're available on a specific page off of the apple.com website. Places that we would point you to include the USB Implementers Forum.
USB Implementers Forum contains the actual specs for USB, for the classes of drivers that we've talked about Apple supporting. It includes information about upcoming classes. I'm sure you're all familiar with getting documentation off of there. You can find Mac OS X developer information on developer.apple.com/macosx and USB information is available on developer.apple.com/hardware/usb.
We recently posted up there Mac OS X SDK as well as our existing Mac OS 9 DDKs. Some people have been somewhat surprised that the 10 SDK doesn't have as much in it, but actually it contains the same type of information. All we are really providing is some example code and it automatically installs in /developer/examples I believe. The other thing that gets installed when you install our SDK is the absolute latest header file. You would need that if you're compiling a USB project.
Now, other USB drivers, one of the things we did in our DDK is we separated out the actual drivers that we used in OS 9 and made them available as sample code. Well, all of the I/O USB family is available as sample code inside the Darwin project. So it's possible for you to take a look at how any, well practically all of our USB drivers are available in Darwin and all of the source code to the family is in Darwin.
The USB team operates out of that Darwin repository live, so when we check in some new code or new fixes or new changes, they're actually available in Darwin almost immediately. And you can check those out and see what we're up to. We love bug reports that say in this file on that line, that line right there is wrong. So feel free to send those to us.
Okay, other sessions that you might want to know about at WWDC. Yesterday you had the I/O Kit update. Right after this session we'll have the I/O Kit storage drivers. There is a session on the image capture framework tomorrow, tomorrow morning. And, of course, our USB in-depth is available tomorrow at 3.30 p.m. That seems wrong. I thought it was 2 o'clock. Anyway, it's available tomorrow in the Civic Auditorium, which is across the street. Finally, we have our feedback forum on Thursday, an always popular event. We're looking forward to it.
Okay, if you want to contact somebody at Apple, Craig Keithley, he's the guy. He does USB and FireWire. There's his email address. And we have a developer mailing list where developers help each other with questions and answers and the USB team, when it has time, tries to provide a little guidance there as well. So it's a good way to keep track of what's going on with USB.