Hardware • 57:54
USB offers several benefits to users, such as low cost, expandability, auto-configuration, and hot plugging. Learn about the progress Apple has made with USB over the past year, including our adoption of USB 2.0 – in all currently shipping products. We discuss where things are going in the future, what new device classes are on the horizon, and we outline the types of solutions possible with USB today.
Speakers: Craig Keithley, Fernando Urbina, Rhoads Hollowell, Barry Twycross
Unlisted on Apple Developer site
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
Welcome. So I was thinking about this session over the weekend, and it occurs to me that I've been speaking at the USB session since 1998, when I had more hair. And what was notable about 1998 for me was that that was probably the last year that Mac Week was published in print form. And Mac the Knife wrote an article that said, "Coming up to WWDC, the session you've got to go to is USB." And the room was absolutely packed.
Now, the room's not packed today. And actually, I think that's okay, because it really refers or relates a lot to the maturity of the product. Now, we're going to talk a lot about where we're going. We're going to talk about some new things that we have. But let's look at where we've come since 1998, at the dawn of USB, when USB really only had two major devices that were well-supported, well-engineered, and there were many of them. You know what they were? Keyboard devices. So since then, we've come up with mass storage devices, modems, printers, networking devices, cameras, digital steel cameras, video cameras, and so forth.
So just as an overview for today, I want to give you a progress report on what we've been doing, what's ahead. We're going to have some presentation on remote USB. We have an I/O lab tomorrow. The only thing that I would say about this I/O lab is that it will be all day long in the developer relations, developer technical support lab, except for the USB and FireWire feedback form.
It wouldn't make much sense to have you go to the lab when you could come and give us feedback. And then the beginning of the day on Thursday, Thursday night, we have the USB, FireWire, Bluetooth rendezvous plugfest at the campus during the campus bash. It's on top of the cafeteria in I/O lab. the cafeteria in aisle four.
So what you'll learn today, we will focus on what we've been doing over the past year in Jaguar. We're going to talk about the issues that affect you as developers, and of course we want your feedback at the end of the session to learn even more. And really the most important thing is the right way to write code, the right way to write drivers, the right way to design devices that are going to get you the best behavior. But there are some techniques that you can use to match your devices to drivers without writing any code at all. These code-less texts just allow you to put in the vendor and product ID and match up to a device automatically.
So as you know, right now, every single CPU we ship comes with high-speed USB 2.0 – the Emacs, the iMacs, the Powerbooks, PowerMacs, etc. The notable thing is that we've added EHCI host controller support. That's how we get high-speed USB. They'll talk later on in what we're doing to add even more controller support, so I'll leave that surprise for them. There are a number of robustness fixes that we put in the 10.3 update.
And one thing that's very cool for us is that if you remember where we got to in the very final days of Mac OS 9.1, 9.2, 9.2.2, is we added its support for USB UPS devices so that you would automatically see them on a desktop machine. If you plug in a USB device, a USB enabled UPS, it would automatically appear as a battery device. And that kind of support is now present as well. So with that, let me bring up Fernando Urbino.
I will be talking about some new APIs that we have added to our family, some changes that we have made to existing class drivers, some of them based on feedback that we got at last year's developers conference, and finally I'm going to give you an overview of new class drivers that we have added, especially we've added support to the video device class specification, the CDC Ethernet devices are now supported and there are some changes that we made there as well, and finally we've added support for UHCI controllers.
One of the new APIs that we have implemented is the timestamping of USB reads. As most of you may know, the callbacks from your drivers are issued on the USB work loop. This work loop can be delayed depending on whether other high priority threads are running in the system. So if the USB transaction completes on the bus and the hardware notifies us at a certain time, there could be a latency or some amount of milliseconds before your driver gets that callback. This is what we call the work loop latency issue.
We discovered that some clients, including our head driver, would sometimes like to know at what time the hardware issued the interrupt and our filter interrupt process that interrupt, so that it could take care of any delays that happened before its callback was issued. So we went ahead and added this new API. It's very similar to one of the existing IOUSB pipe read APIs. The only difference is, as it's highlighted, is that we now have a IOUSB completion with timestamp parameter instead of just a regular IOUSB completion.
This timestamping is currently implemented in your TigerSeed, and it's only implemented for interrupt and bulk reads. We saw no reason to implement it for writes, but if you have a good or invalid reason to do it, let us know through the USB list or at the feedback forum or down at the Hardware I/O Lab tomorrow and Thursday, and we'll consider and we might add it.
The I/O USB completion with timestamp looks as so in the screen. The action is the different parameter there, and it is an I/O USB completion action with timestamp. The way it works is that your action routine, or your callback essentially, gets a new parameter. In the past you've gotten, of course, the status, the buffer size remaining, and now you're going to get the timestamp. Again, the timestamp is the time not when the controller completed the transaction, but where our filter interrupt routine, which is our primary interrupt routine, actually received that interrupt. So there's a little delay there between the hardware and the filter interrupt routine running.
We now have an iOSB composite driver instead of an Apple USB composite driver. It is the same code, however we've changed the name because now we guarantee binary compatibility in the future. So we had recommended in the past that if you needed the behavior of the Apple USB composite driver, that you go ahead and download the family sources from Darwin and do a copy and paste and generate your own kernel extension. Now we're allowing you to subclass that driver so you have to write less code and any bugs that we fix, even though it's a fairly straightforward driver, you'll just get by default instead of having stale code.
You would use this driver if you need to customize the startup behavior of your device. The composite device, the composite driver, all it does is essentially generate a set configuration on the bus and the family actually instantiates the USB interfaces for your device. However, sometimes the set configuration is not enough and you want to do something else.
So you go ahead and subclass this driver and add your code and that's it. It is also, or it also can be used to solve what we call that root driver dependency problem. And Rhodes will talk about this in a little bit. But essentially by overriding the set configuration method of the driver, you'll be able to solve this problem.
[Transcript missing]
I have an example here of subclassing the interface user-client, a class declaration for it. It's very straightforward. In this case, for this example, I'm just going to override the control request in method of the interface user-client class. This is a method used when you want to read, issue a device request to the device. So it's from device to host.
This implementation of that method shows how I go ahead and intercept the USB stream. First of all, at the top, I need to decode the parameters of the method into device request fields. You can look at the USB family sources to see how we did it, but it's just like that.
All the different fields are packed and encoded into UN32s. Then, since it's a request from the device to the host, we first issue the call to our superclass, and that will cause the transaction to go out on the bus. Since it's a synchronous transaction, it will return when that has completed.
Once it has completed, in this example, I want to know whether the control request was a printer class specific get device ID request to interface zero, alternate setting zero. I go ahead and check those parameters. If it is, then I can do something with it. In this example, I don't do anything, but I could examine the buffer value and see what the device returned and do something to it again. At the end, of course, you've got to remember to return the error that your superclass returned.
In the past, the user clients were called actually I/O USB device user client and I/O USB interface user client. However, they were not subclassable, and this goes against everything that we've told since the beginning, that if it was I/O USB, it means it was subclassable. Of course, the headers were not available for you, so you really couldn't subclass it.
And even if you copied the headers, they said right there, this is not intended to be subclassed. Anyways, we're fixing that now. What that means is that we're going to guarantee binary compatibility in the future if you subclass the user clients. And there is sample code available that is a complete example that I just showed that compiles and works.
Couple of miscellaneous updates in developing the video digitizer to support the USB video class specification. I realized that there were some APIs that were exposed in the kernel, but were not exposed to user space. And so I went ahead and implemented Find Next, associated descriptor and Find Next Alt Interface. And those of you doing isochronous drivers from user space might find it handy to use those to decode the descriptors instead of having to do it all by hand.
One part that is really not an external API change, but it's an internal implementation in the family, is that the family now uses the command sleep and command wake up IOK functions. In the past, those of you who have seen the sources, we, we simulated synchronous commands to the USB synchronous transactions by using the IO Sync object from IO Kid. And that allowed us to send a command to the USB bus and sit and wait for that command to complete and then call our client. That's how we simulated a synchronous command on an asynchronous bus.
Buzz. IO Sync has now been deprecated in favor of command sleep and command wake. This is a lot friendlier to the system. It uses less resources. It allows other things to run in when you're command, when you issue that command sleep, which essentially puts the thread to sleep.
And it also solves a bug that we see every so often and that we've talked in previous WWDCs, and that is the problem that you cannot issue a synchronous command from your callback, because you're on the I/O USB work loop, and if you do, you'll deadlock the bus and nothing will work from there on.
There was no – you could do that in previous versions and you get no error or anything. By using command sleep, We will issue an error right away, an immediate error, of KIO USB sync request on work loop thread to you, so you'll know that your synchronous requests did not work and you won't deadlock the bus, and it'll save us all some time.
Through the open source process this past year, we received a submission to support the UHCI controller. As you know, we support OHCI from way back then, and last year we added EHCI controllers. But we've never added support for UHCI controllers because Apple has never shipped a machine with such a controller. We received the submission, we tweaked it a little bit here and there, and added it to our project.
And it is actually shipping and available in your TigerSeed. The purpose of this slide is just to show you that the open source submission really does work, and that we take all submissions seriously. So we again encourage you to download our sources, and if you use them for something that other people can use, send it back to us and we'll take it from there.
In terms of new device class support, as I mentioned, we have several or a couple of new device class drivers. Actually, the 10.3.3 update included support for USB-CDC Ethernet devices. In the past, this was just sample code that was available on request from DTS, but with 10.3.3, it became part of the system.
However, as I'm going to mention in a couple of slides, for Tiger, we now have a new USB-CDC driver that supports modem and Ethernet-CDC devices. And as some of you know, last August the USB Implementers Forum approved version 1.0 of the USB Video Class Device specification, and Tiger is going to add support for some of that specification.
CDC class devices. We've had support for them since the beginning. The driver in Mac OS X was essentially ported from our Mac OS 9 driver. It has worked well. It's a device driver as opposed to an interface driver. It uses a fairly simple buffering model, and it worked well for low bandwidth CDC devices.
However, new devices with higher bandwidth and with multiple interfaces have come into the market recently, all of them CDC compliant. And we realized that there was a need to revamp this driver. So we've gone and created this new driver that is in your TigerSeed. It supports devices that have multiple interfaces, not just the data interfaces, but maybe mass storage and others.
It also has a multiple buffering model to allow us to better support the high bandwidth devices. The new driver architecture essentially looks like this. At the device driver level, we still have a device driver, but it's a very simple device driver that is essentially just the equivalent of the composite driver.
It creates the interfaces and then it causes either the ACM driver for low bandwidth devices to be instantiated for those interfaces, or the ECM driver for the high bandwidth devices. So that works really well. An example of how the CDC's devices have changed is, for example, the third generation cell phones.
That can have a whole series of USB interfaces, and having the old driver that assumed that all the interfaces were the data communication interface would certainly not work in this case. You could have a hidden interface for the keypad, you can have mass storage interface to access the card where the pictures are stored. Of course, you can have the data interfaces, you can have the Bluetooth interfaces. And perhaps even audio interfaces in there. So you can see that CDC devices have come a long ways, and that is the reason for our change.
The USB video class was approved, like I said, last August, and I've been working with the device working group for way before that. The base specification itself is Video format agnostic. It just tells you what kind of interfaces or what kind of functions, video functions, that device supports, how still image is supported, whether it's bulk stream or ISOC streams that are supported, how the host and the device negotiate.
What kind of stream to send and at what bandwidth? It's very general in those terms. The base spec itself is followed by several payload specifications that tell the driver/writer what the format of the data that is encapsulated in those streams looks like and how all the information in there is to be decoded.
There's payload description specifications for YUV 422, a flavor of YUV 422, and a flavor of Motion JPEG, and those are mainly for web camera situations. DV is over USB spec in there, MPEG-1 and MPEG-2. So I encourage you, if you're thinking about video class devices, to go and make a video class compliant device. device.
Tiger will have support for the USB video class specification by using a video digitizer that lives at library QuickTime USB VDC digitizer component. The software will support web cameras, either in the motion JPEG or the YUV 422 format. It supports bulk and isochronous streams, and right now cameras are shipping with both kinds of streams, even though the manufacturers actually don't make a big deal that they are VDC class compliant devices, but they are shipping there, as we'll show you in a little bit.
The still image support will be provided through the image capture framework, so that it is seamless to the user. It will just look like another. source of still pictures. The spec actually is going through some ongoing revisions to further clarify the DV support, so there is no DV support for USB video class devices in Tiger.
Describing a video class device can be a little tricky because they can have, in this example of a digital camera or a video camera, you can have the video class interfaces, you could have audio interfaces for the microphone, you can have mass storage interfaces for the picture card that is in the machine.
So, they needed a way to describe in terms of the USB device descriptor how this device looks so that host software could readily find the particular interfaces that are of its concern and use them instead of having to go through every single interface and see what kind of interface it is. So, the USB IEF came up with the Interface Association Description. This descriptor tells us at the device level that it is a miscellaneous class device with a common subclass and an interface association descriptor protocol.
This just tells the device driver that you need to look into the configuration descriptor for the interface association descriptor that follows, which is going to group the different functional interfaces. This just tells the device driver that you need to look into the configuration descriptor for the interface association descriptor that follows, which is going to group the different functional interfaces. And if we can go to the demo machine, I will show you how the device will be able to do this. And if we can go to the demo machine, I will show you how the device will be able to do this.
Here we have a Panasonic video class-specific compliant device. I'm going to turn it on and start USB Prover and look at the camera right here. You can see that the device is a miscellaneous common class interface association descriptor. Here on the configuration descriptor, we see that there are one and two interface association descriptors.
One for the video interface collection and one for the audio collection. If we disclose one of those, it just tells us that the first interface is interface number zero, and there are two interfaces, and furthermore, then it tells us that it is the video subclass. We go down here and now look at the audio control.
The first interface is 2, and there's again two interfaces for the audio control and the audio streaming, and the class is the audio class. So this is just a way for the device to tell us what functions it supports, and our drivers can then match. And in fact, for this camera, we can see that The driver has the video control interface and the video streaming interface, and then there's the audio, Apple USB audio device and engine matching to the device.
[Transcript missing]
And this one is right down here. And in this case, it doesn't have a microphone, so there will only be one video interface collection. But we can start a video capture application, and you can see that... That support is real and it's there. And we are continuing to... Oh, I didn't want to do that.
We're continuing to support all the cameras that we can get a hold of. The support now is such that new cameras that have come in, I haven't had to change the driver at all, so we're getting there. I believe that that is the end of the demo, and if we can go to the – Rhoaads will now come and talk to you about some other USB issues.
I want to talk to you about some of the issues that have come up in the last year. These are issues that have come up in our own internal driver development and on the USB developer mailing list. I'm going to talk about three specific things. One is what I call the split transaction error problem. These are USB errors with full speed or low speed devices attached through a high speed hub to a high speed bus.
Then another issue that's more of an I/O Kit issue, but we call it Apple USB Composite Stole My Device. This is an issue that has come up quite frequently with third-party developers. And then another one called My Device Gets the Wrong Config, which is more rare, but an interesting solution.
So first we're going to talk about the split transaction errors. So what you have here is a picture that's sort of adapted from the USB 2.0 specification, Appendix A, Table A-12. Here is a – what we're going to go through here is a split transaction between the high-speed host, a high-speed hub, and then a full-speed or low-speed device attached to that hub. So let's say the host needs to send some data out to the classic speed device.
So it first sends a start split token to the high-speed host. So it sends a start split token to the high-speed hub saying I have a command for a classic speed device, followed by the out or setup packet. That's what the Table A-12 will show you, followed by the data. And the high-speed hub acknowledges that data transfer.
Now the high-speed hub needs to send the data to the device. So first it sends the out setup packet, followed by the data. But in this example, the device either ignores the data, does not respond to it in any way, or it sends the data to the device, and then the device ignores the data.
So it sends the data to the device, and then the device ignores the data, or it sends the data to the device, and then the device ignores the data, or it sends the data to the device, and then the device ignores the data, or never gets the data for an electrical problem, or because it is physically disconnected, or for some other reason.
And that happens three times in a row. So finally, the host wants to find out what happened to that data packet, so it sends a complete split command to the hub, identifies which complete split it's looking for, and gets a response of stall from the high-speed hub. So that's one example. Now we're going to go to another example and compare them.
So in this example, this is table A-23 from the USB 2.0 specification. And in this example, the same situation – the host sends the start split, followed by the out or setup packet, followed by the data, and gets acknowledged. The high speed hub then sends the out setup packet and sends the data to the device. This time, the device gets the data, but it doesn't like it, so it returns a stall PID.
Later on, the host wants to find out what happened. It sends the complete split, it identifies the complete split, and it gets a stall back from the high-speed hub. Now I'm going to show you the other table here on the next slide. I want you to pay close attention to the information on the left-hand side of the screen.
When we go back to Table A-12, you'll notice that the left-hand side did not change. Let's go back to the other one again. It didn't change. What this means is that from a controller point of view in the host, we can't tell the difference between the situation where the device did not get the data and the situation where the device got the data but didn't like it.
So, what does your driver need to do? Well, first of all, you need to realize that "stall" does not always mean "stall." It may mean that the device stalled the data, and it may mean that the device didn't respond at all, so the high-speed hub stalls the command.
What we have done with this in the USB family is when this situation occurs, we will return an error of KIOUSB pipe stall the first time we see it on a particular pipe, and the third time, and the fifth time, and all the odd times. But on the even times, we will return a different error – KIO return not responding. So that hopefully if you issue the command twice in a row, one of the two errors will cause your driver to know what to do with the situation.
When this happens, no matter which of the two situations occurs, the endpoint is halted inside the host controller. It needs to be cleared, at least in the host end, and possibly in the device end, but we don't know that. The device may or may not have a halted endpoint, and your driver needs to figure it out.
It needs to ask the device, it needs to reset the device, it needs to do something to deal with this situation. But you need to realize that this is something that is different between classic speed USB on an OHCI bus and classic speed USB on a high speed bus. And you need to deal with it appropriately.
Okay, another issue that has come up and we wanted to talk about, and this is more of an I/O Kit thing, but we've done some stuff to help solve it here, is the – we call it Apple USB Composite Stole My Device. Now what does this mean? This is when you have a device driver for an I/O USB device nub, as opposed to an interface driver.
Again, this is something we encourage people to use the composite driver when they can, but let's say you have your own device driver for some reason, and you need that. And so you create a personality for your I/O USB device, and it's specified here, and you plug in your device and you open up USB Prober, and sure enough, there's your driver, instantiated, controlling this device. And that's great. Everything's wonderful.
But then, when you reboot the machine, you open up Prober again, and this time the Apple USB Composite driver is installed. So the composite driver is now controlling your device instead of your own driver. And if you unplug the device and plug it back in, you'll see that your own driver is controlling it again.
So you ask yourself, well, why did the Apple USB Composite driver match against my device? So you download the family sources from Darwin, and you open up the Apple USB Composite target, and you notice this property: OS bundle required of root. And you ask us on the list, or the I/O Kit list, "Well, what does that mean?" In the system, there are certain kernel extensions and the kernel itself that are listed with an OS bundle required property of root.
There are a few other OS bundle required properties, but this is sort of the main one. These are kernel extensions that are required to run very, very, very early in the system, to do things like enable you to go into single user mode or verbose mode, that kind of thing.
We want to be able to type on a keyboard, for example, in single user mode. So these kernel extensions are things like the Mach text itself and the I/O Kit and the USB family and the various controller drivers and so forth. And one of them is Apple USB Composite.
So this is a root driver, it loads very early in the system. But then there are other kernel extensions that are not root drivers. For example, the I/O Serial family, or the I/O Audio family, or some other family that your kernel extension needs to link against in order to be resolved and load.
So what that means, of course, is that if you have to link against a driver that's not available at root time, you can't be available at root time. So what do you do? What we came up with was a clever solution that we call the poison pill. And in this solution, you create an interface driver for your I/O USB interface object. And here is a personality for that driver. And then inside that interface driver in the start method, you just call re-enumerate device, which causes your device to get essentially disconnected and reconnected from the bus.
Your interface driver, not being a root driver, will load once the system is fully booted, and when it causes the device re-enumeration, again, it's like physically disconnecting and reconnecting the device, and now your device driver, which has a higher matching priority than the composite device driver, will win the matching process.
Now, this solution does have a couple of caveats, one of which is that your device driver, which might be a subclass of the new I/O USB composite driver, for example, needs to call set configuration with a parameter of false to prevent I/O Kit from matching drivers against the interface nubs, or else you get into a loop where it's just constantly re-enumerating the device. However, preventing I/O Kit from matching the interface nubs will also prevent I/O USB interface user-client from matching, which means that if you need a user-client for your device, you need to do a little more work.
Finally, I'm going to talk about a situation that has come up. We call it "my device gets the wrong config." This is where you have a device, an I/O USB device, that has more than one configuration descriptor. And the composite driver does everything you want. It's a composite device, i.e.
the device class and device subclass are both zero. And the Apple USB composite driver does a good job, except for it picks the wrong configuration, because the composite driver always picks the first configuration. And you want an alternate configuration to be instantiated for your device under certain circumstances.
Well, You don't need to write any code to do this. You can create a codeless kernel extension, where all you need is a file with a personality in it, a property list. And this property list specifies your device by vendor ID and product ID, as you can see, and specifies that you want to load the Apple USB Composite bundle and instantiate the Apple USB Composite class to match against your device. You then add one more property, in blue at the bottom of the list, called preferred configuration, and you specify the configuration number of the configuration that you want, and the Apple USB Composite driver will then load that configuration.
This same technique of creating a codeless KEXT can be used if you have a vendor-specific device, i.e. the device class and subclass are FF instead of 0, but other than that, the device really will behave just like a composite device, and you want the Apple USB Composite driver to load against this device. And with that, I'm going to bring up Barry Twycross to talk about remote USB.
Thanks Rhos. Okay, so I'm going to talk about remote USB. What is it? Why you would want to do this? How it's done? And then the interesting part, what it actually means to you. Remote USB – what is it? It's when you use a device which is not actually attached to your computer. Ah, but you say, this is not USB, is it? Well, no, it's pretending to be USB. However, users do it anyway, so you might as well be prepared for it.
Remote USB. Why would you want to do this? Well, there is user demand. Users like to share their peripherals. A classic example is... is a printer. They want one in the household, everyone in the household can print to the one printer. They also like to unwire, so they can be sitting comfortably on their couch in the living room, print something off to their printer, and never have to get up at all, except when they actually go to get the print out from the printer. It's also a business opportunity. You can sell devices which implement remote USB.
Users will buy them from you. And it's actually quite a common developer question. And here's an example from the USBIF's mailing list, where this guy says, I'm a student, I have a project, I want to cut the wire on my USB, put radio transceivers at both ends, and will it work? Well, the answer to that was basically no.
Let's think about the problem a little more rationally. Let's try using networking concepts. Networking is the standard way of getting data from here to there, and we want to get data from here to there. Also, decouple the transport of the data from the data itself. So if you want to do it wirelessly, don't worry that you want to do it wirelessly. Do it with a network, and then use a wireless network. It can be wireless this way.
So we'll use TCP/IP, it being the commonly available networking. It's built into all our machines. Everyone knows and loves it. So we need to think about the data flow that's involved in USB, intercept it at some point, and then send it across the network to a device which takes it and then sends it on to the actual USB device.
So, the data flow. And you start off at the top with the application. Here's an example of printing again. You start off with the application, it sends data to the print driver, it bubbles down through the user client, and eventually ends up with a controller, and actually then ends up on the USB bus itself, and finally with a device. We thought of three interesting places where you could actually intercept this data, basically at a very high level, at a very low level, and somewhere in between.
So, let's think about high level. An example is, in fact, airport extreme printing. The application in this case is, in fact, the print manager, and the print driver's I/O module, instead of sending it to USB, sends it across the network to the device – well, in this case, an airport extreme base station, which sends it down through its own USB stack to the printer itself.
Now, doing this at a high level has a big disadvantage in that the application itself is required to know what it's doing. It needs to know that it's talking to a network service. So, it's incompatible with most solutions which are out there. However, it has a very big advantage in that protocols exist to do this already. In particular, the two big issues here are discovery and session management.
You want to find out what devices or what services are available to actually use out there. That's discovery. We have lots of interesting protocols, including Rendezvous, which they always like us to plug for you. And the other problem is session management. You only want one person talking to your device at once, otherwise your device gets mightily confused. Thank you.
So, as you don't want to get your device mightily confused, you have to have some way of managing who talks to the device at one time. The person talking to the device is in fact a session in networking speak, and you just need to manage this. That's session management. So, we were thinking, okay, let's do Airport Extreme, let's do remote printing with a USB printer. We thought it was the perfect application.
We actually own the application, so we can put the knowledge of how to do this into the application, the application being the printer driver itself. And we also own the networking, we own the base station, so it seemed like the perfect application. So, we went ahead, we added a host controller, we added a USB stack, we added a...
[Transcript missing]
Fine.
But we found that the feature set of this protocol was not actually rich enough. Print drivers wanted to do things such as soft reset, getting of the printer ID string, and in fact getting the entronic status, which the protocol didn't allow for. The printer module was faked to actually help with this to some extent, and then there's a new print module in Panther which does an even better job, but still, in all circumstances, it doesn't work. There'll be more about that later.
Another idea we had, let's try an intermediate level. In fact, the user client. With the user client, you're already passing messages across a hard boundary, which is from user space into kernel space, so why not instead just pass those messages across the network to a device which can receive them and do exactly what the user client on your local Mac would do. In this case, send data down to the printer. Sounds like a very good idea.
To some extent, discovery and session management are already built into this. When you iterate through available user clients, or when you iterate through available interfaces, you're actually doing discovery of some description. And when you open a device, use a device, close a device, that is in fact a crude form of session management. Sounds like a good idea, and it still is only an idea as far as we know. No one has ever done this.
At the other end of the scale is at a low level. An example of this is the Keyspan remote USB server. I was very surprised in January. I walked into Macworld, came up to the Keyspan booth and thought, "Oh, that was interesting." They had in fact made a device which had a USB controller attached to the network. And indeed, the data bubbled down through the system as usual. The host controller driver said, "Here, Mr. Wim, please send this data to the USB." And in fact, that Wim does send the data to the USB, but the USB happens to be somewhere else.
At this very low level, it's actually quite a lot more compatible. Nothing in the system actually has to be aware that At this very low level, it's actually quite a lot more compatible. Nothing in the system actually has to be aware that At this very low level, it's actually quite a lot more compatible.
Nothing in the system actually has to be aware that it searches for all the devices on them and makes them available on the list. The user can click on one and say, "I want to use this now." And they also had to add some session management to this.
They have various methods, some of which work for some applications, like the open, print, close, some of which don't. Like if you have a mass storage device, it's always attached. The mass storage driver doesn't open, print, and close. It opens and uses forever. So there's various methods of doing that. that.
This means that the user experience is not exactly as seamless as you might want it to be. So they're actually looking at putting in some higher-level protocols, say add a print server to the USB server, or a mass storage file server, something like that, just to overcome the disadvantage of being at such a low level.
So what does this all mean to you? There are two very big issues with any implementation like this: latency and throughput. These are quite tightly coupled, in that when your latency increases, your throughput tends to decrease. Some protocols we find are very sensitive to latency, specifically ones where you do actions serially, you ask the device something, wait for a reply before you ask the device something again. An example is, say, the mass storage protocol.
You send it a command, you wait for that to be acknowledged, you send it some data, you wait for the data to be acknowledged, then you ask it for the status, and the status comes back to you. At each stage you're waiting for something, if there is latency in the system, that just
[Transcript missing]
We find that in some cases, driver timeouts are too aggressive. One printer driver was found to have a two-second timeout. Locally, it didn't tend to time out, but across a network, it was timing out all the time.
If you have such an aggressive timeout, you could find that it could happen to you locally. So this is not actually a good idea. If your bus was heavily loaded, if Nano's webcam was turned on using 90% of the bus, the 10% you have left works a hell of a lot slower than you thought it was going to be, your data doesn't go out and come back, you time out, the user isn't happy. So timeouts which are set too aggressive are not a good thing.
Another thing: Do use a standard component if it's available. For example, using the Apple-supplied printer module, so that it can then take the data and send it across the network and everything's happy. In general, if you're using a standard component, we can fix it, you don't have to.
But in the case of the printer, we found that the feature set wasn't rich enough, and in particular for printer utilities. They're very picky about what they do, and they don't tend to work, say, with the airport extreme printing. So if you want to declog the head on your printer, you actually have to go and plug in locally.
There's actually not a lot we can do about this, except we can ask you to provide feedback. If you find that any component we supply doesn't actually meet your need, then we can actually rev it at some time, and eventually it will meet your need, and then everything will be happy.
So, some things you want to do: Be standard. to a standard, not to any particular implementation. Don't say it works on that system so it should work on your system. If you design to a standard, it should work on any system. And if you did design to a standard and it doesn't work on a system, then the system vendor will probably be motivated to actually fix it. And have a standard.
That's a very good one. Or even have a well-defined standard. Some of the problems we're having is that there is no standard for this or that the standard does not define what a device should do in this circumstance so it does something quite unexpected. For example, don't overload a standard that exists.
Like we found that some printers, in fact, would change their device ID string to provide status. We were expecting the device ID string to be static so we'd read it once, send it over, and never bother sending it over again. and some printers just refused to print because they were getting stale status.
We fix that. Don't expect everything. Say if you have a compound or composite device, don't expect to get all of the interfaces or all of the devices in that compound device. Just use the one that you actually need. For example, you say you had a device which was a printer, a scanner, and a modem, but you wanted to print something. If the printer either freaked out, if it found the scanner wasn't there, even if it only wanted to print, That could be embarrassing if you just told your remote USB to actually just attach to the printer and not to the scanner and the modem as well.
And also, be optimal locally in your data transfer. If you transfer data optimally, even just locally, well that's good, and then when you get into a situation where the network adds its latencies, you'll find that you'll be in a much better position. For example, using multiple reads or multiple transfers, and using larger transfers. Let the system do the work of actually getting the data there, rather than your driver trying to do all the work.
And one final point, which I'm not sure if it's a good thing or not, is whether you should post a read. If you have a readout standing on a device, you'll know as soon as it tells you that it has something to say. However, on some of the earlier systems, some of the slower systems, having a readout standing like that, doing nothing, uses up a lot of the bus bandwidth, and the entire system can actually slow down, which is not a good thing. So, whether this is a good thing or not is left to your conscience.
So, with this, where should you go for more information? Well, Craig is a good person to talk to, at least ask him who else he should talk to, or indeed send stuff to our mailing list. We like to hear from you. Yeah, we like to hear from you. And some pointers to documentation on the web page you might be interested in seeing. This reference is available somewhere, I'm not quite sure where.