Issue 4-12, March 24, 1999

Be Engineering Insights: Exact Change Only—Riding the Universal Serial Bus Through User-land

By Brian Swetland

Deja Vu

The last Developer Newsletter article I wrote had a similar theme to this one—I explained how to use the new scsi_raw device to talk directly to SCSI devices from outside the kernel. This time around we'll look at a slightly newer bus. The Universal Serial Bus (USB) is available on most new PCs. If we're lucky, it may do away with a number of the bizarre ports on the back of our PCs, replacing them with one standard interface. I could live with that.

Coming Soon To A Release Near You

BeOS Release 4.1 (coming soon) provides support for USB keyboards, mice, and printers. It also supports USB hubs (used to connect several devices to one USB port) and all the assorted bookkeeping and bus management involved. There is a USB Bus Manager for device drivers to use, but it's a fairly complex critter that's still evolving. My original intention was to provide a usb_raw device to allow user programs to talk to USB devices, but even that proved to be tricky—the usb_raw device uses the not-officially-documented, subject-to-change USB Bus Manager, has to "cheat" a bit in places, and has a rather complex ioctl() interface. Where the scsi_raw device had one function—to send a SCSI Command -- the usb_raw device must support a number of operations.

Wouldn't it be nice if there was a C++ Kit (like some of the other Be APIs) to make this all easier? Well that's what you'll get this week. Be aware that this is a prototype right now and not an "Official" Be Kit. This is your opportunity to take a look at a new Kit as it's being developed, and to provide some feedback. Let me know what you like or dislike—we'll post updated versions as things evolve, and once it's finalized, it will become part of the standard BeOS Kits.

You'll need R4.1 to actually use the code in this article. The usb_raw device and USB.h system header are provided for those who are interested in what the kernel side of things looks like, but we're not going to cover them in detail here. Suffice to say, they contain many sharp edges that we intend to sand down, and they are not for people who are afraid of cutting themselves on the bleeding edge of undocumented kernel API development.

Everything You Ever Wanted To Know About USB But Were Afraid To Ask

This article refers to several USB concepts and terms, but does not attempt to explain them. The following resources will prove useful to people who want to learn more about USB:

The Prototype USB Kit

There are five classes in the USB Kit:

USBDevice (usb/Device.cpp)

The USBDevice class is initialized from a path to a raw USB device (e.g., /dev/bus/usb/0/0) and uses the usb_raw driver to provide access to most of the functionality of the USB Bus Manager from user space

USBDevice objects are best obtained by using the USBRoster, described below. Using the USBRoster allows for the dynamic nature of USB. Just examine the device in the DeviceAdded() method to see if it's the one you're looking for.

A number of methods exist to examine the contents of the Device Descriptor (a structure defined by the USB Specification that describes the device). These methods include USBVersion(), Class(), Subclass(), Protocol(), MaxEndpoint0PacketSize(), VendorID(), ProductID(), Version(), ManufacturerString(), and ProductString().

A USB device may be in one of several configurations (represented by USBConfiguration objects in this Kit). USBDevice provides a CountConfigurations() method to determine how many exist and ConfigurationAt() to obtain a specific configuration. Once you select a configuration, a device may be configured with the SetConfiguration() method. The current configuration (or NULL if the device is unconfigured) may be obtained via the ActiveConfiguration() method.

Control requests (always sent to the implied endpoint 0) may be initiated using the ControlTransfer() method.

USBConfiguration (usb/Configuration.cpp)
USBInterface (usb/Interface.cpp)
USBEndpoint (usb/Endpoint.cpp)

These classes cannot be created directly. A USBDevice instance will create a USBConfiguration for each of its possible configurations. USBConfiguration objects create USBInterfaces for each interface they contain. USBEndpoint objects are created by USBInterfaces to allow access to device endpoints. All of these classes are data containers—all the actual work happens in USBDevice.

A configuration is a collection of interfaces. USBConfiguration allows iteration over the interfaces that it contains with the CountInterfaces() and InterfaceAt() methods.

Interfaces are a collection of USBEndpoint objects (which may be iterated over using CountEndpoints() and EndpointAt() methods). Details from the Interface descriptor are available from the Class(), Subclass(), Protocol(), and InterfaceString() methods.

An endpoint is where all the action is—BulkTransfer() and InterruptTransfer() initiate actual transfers. IsBulk(), IsInterrupt(), and IsIsochronous() let you discover what sort of endpoint it is.

USBRoster (usb/Roster.cpp)

The USBRoster is a tool that prevents you from having to wander through /dev/bus/usb/... looking for devices. The USBRoster takes advantage of the node_monitor system to watch for changes in available USB devices; it was inspired by Scott Barta's FolderWatcher. Be Engineering Insights: The Tracker Is Your Friend

Since devfs doesn't allow files to be renamed or moved, the implementation here is much simpler. The USBRoster uses a private class (USBRosterLooper) to actually do the work—a Looper is needed to provide a thread to receive node monitor messages in.

The USBRoster is used by subclassing—two pure virtual methods are provided that you must implement in your subclass:

status_t USBRoster::DeviceAdded(USBDevice *dev) { /* ... */ }
void     USBRoster::DeviceRemoved(USBDevice *dev) { /* ... */ }

DeviceAdded() is called when a new USB device appears on the bus (when the USBRoster is first started, DeviceAdded() will be called for every device that is already on the bus).

The USBRoster creates a new USBDevice and keeps it around until unplug, as long as DeviceAdded() returns B_OK. If DeviceAdded() returns anything else, the USBDevice is deleted immediately and now DeviceRemoved() is called. DeviceRemoved() is your warning that the USBRoster is about to delete the USBDevice in question because it has been removed from the bus. When DeviceRemoved() returns, the USBDevice will be deleted—make sure you don't refer to it again after this point.

Example 1—Waiting For The Bus

The watcher.cpp example creates a subclas of USBRoster that simply displays a message when a device is added or removed.

Example 2—Display Some Info

The info.cpp example is a program that takes one command line argument, which is the name of a raw usb device (e.g., /dev/bus/usb/0/hub is the "root hub"). The device descriptor and information about configurations, interfaces, and endpoints are displayed.

Example 3—Talking To Rodents

The mouse.cpp example attempts to read 4-byte interrupt events from the device specified on the command line. It doesn't do much in the way of error checking (and it will probably do nothing meaningful if the specified device isn't a USB mouse), but it illustrates the use of an interrupt endpoint to read data.

Example 4—Something Almost Useful

Cypress Semiconductor makes a chip (the CY7C6300) which can be used for simple low-speed USB devices. This is a nifty 20-pin DIP package that requires only a 6MHz crystal and a USB cable connector to work (the remaining pins provide bidirectional IO ports). For $100 you can purchase a "USB Starter Kit" that comes with a chip programmer, an evaluation board with a button and a temperature sensor, two reprogrammable CY7C6300s, and software for Windows. Unfortunately, Cypress appears uninterested in supporting the BeOS as a development environment. Bummer.

In a random (and warranty-voiding) survey of eight different USB mice, we discovered that all of them used this Cypress part. Many of the designs were surprisingly similar to the "Designing a Low Cost USB Mouse" application note provided on their website.

The fourth example (thermo.cpp) talks to the eval board provided in this kit. It uses a subclass of USBRoster to look for such boards (identifying them by the VendorID and ProductID) and creates a ThermoWatcher object for each board on bus. The ThermoWatcher spawns a thread to monitor both the button and the temperature sensor via periodic control messages, as well as flash the green LED provided.

Next Stop: More Features

There are a number of interesting things that could be done with this toolkit: a visual USB Monitor to let you examine the topology and devices of your bus; user-land interfaces to digital cameras, scanners, or other USB devices. The USB Kit will continue to evolve, providing (if nothing else) better error reporting, timeouts, and other features not present yet. Please send me any suggestions you have—nothing is set in stone yet. The USB Bus Manager in R4.1 does not support isochronous transfers -- there is support down in the guts of the USB stack, but it's not exposed at the driver level yet. This will be coming sometime after 4.1 (probably with the next BeOS release, sooner if things go well).


Be Engineering Insights: Getting Connected

By Hank Sackett

You've just installed the BeOS on your shiny new machine, launched NetPositive, clicked on the link to the Be web site...and you're waiting. What's this waiting about? Isn't Be the OS that doesn't make you wait? Sadly, the link LED doesn't always shine on brightly.

If this happens to you, the first thing to do is check the connector. You'll find a nice diagram of Category 5 RJ45 wiring at http://www.alvin.woco.ohio.gov/cat5/ On some cables only four of the eight conductors are populated, and will only work at 10 Megabit, not 100 MB speeds.

Crossover cables—the Ethernet equivalent of a null modem cable, where the transmit and receive lines are crossed—are also something to watch for. You can use a crossover cable to connect two machines without using a hub. Some hubs have an "uplink" switch, which crosses or uncrosses transmit and receive within the hub. Normally, straight wired cables are used to connect the network interface card to the hub, and the uplink port (with the switch set to crossover) is used with a straight wired cable to connect to another hub. You could connect the Ethernet card from your computer with a crossover cable and uncross it in the hub with the uplink switch, but that's not unlike using a double negative.

The next thing to check is the link LED on the card, and the hub or switch. Normally the link LED lights up when the hub and the machine are powered up and connected. Occasionally, a BIOS setting for power management needs to be turned on for an on-line Ethernet to power up. Some DEC cards require the driver to load and enable the LEDs in software before they'll light up, and some vendors save a few cents by leaving the LEDs off. Nice vendors include lots of LEDs for link, speed (10 or 100 MB), full- or half-duplex, and transmit and receive. Often transmit and receive are combined into one activity LED, with a red or yellow LED used to indicate collisions.

Just as there are choices on each end of the wire for media, speed, and duplex—all combinations occur—there is also what might be called the husband and wife case. This is where the card comes up at 100 MB speed and is connected to a 10 MB hub, effectively jamming communication on all ports on the hub, with the collision LED blazing like a 100 watt bulb in a 60 watt socket.

A happier but still troubled couple is a card that's in full-duplex mode connected to a hub, which is by definition half-duplex. Unhappily, upgrading to a full- duplex card that doesn't correctly detect a half-duplex hub decreases throughput. That's because when transmits and receives occur simultaneously they generate collisions, instead of improving performance from two-way traffic. Higher-level protocols can mask this behavior with retransmits, and the casual observer may miss the problem entirely.

Some 3Com cards ship with a DOS utility for setting a media override value in nonvolatile memory on the card. These settings may also be written by Windows device configuration user interfaces. The BeOS driver will faithfully configure the card media with the values, which may be blessing or a curse: a curse if you've received a card from another environment and something has set the card memory with parameters that don't match your hub or switch, but a blessing if you need to use these settings to make the card work with your hub or switch.

IEEE standards, like a marriage counselor, provide the n-way auto-negotiate protocol and the media-independent interface (MII), which help both sides of the wire to come up with the same speed and duplex settings. Look for these features if you're buying network hardware, and look for hardware with lots of LEDs, so you can check the states.

Reading the print on the largest chip on the LAN card or getting the Vendor and Device ID numbers from the Devices preference will tell you if the card has a BeOS driver. Vendors often change hardware as parts become more available or cheaper, and supporting all the hardware is a real chore. To understand why the entire family of DEC LAN controllers is not yet supported, check the Linux community's description of the problem at http://195.113.31.123/~ftp/linux/tulip/tulip-media.html

You say your speed and duplex LEDs and cabling look OK, but still no joy? Connecting the serial port with a null modem cable to a terminal emulator set to 19.2, 8-1-none to receive serial debug output may give you a clue. When the driver is restarted from Network preferences, the debug output will show the IRQ and Ethernet address of the card. If the IRQ is 00 or ff, it's time to open the Devices preference and look for conflicts in resource usage and problems in the BIOS settings. ISA PnP and jumpered card IRQ and port settings should match those of the Network prefs configuration.

To verify that your network hardware is working, enter "ping xx.xx.xx.xx" from the Terminal application, where xx.xx.xx.xx is the IP address of a known machine on the net; the router IP address may be a good choice. If the ping works but other network services don't, check domain name, Domain Name Server (DNS), and the host and login name settings in Network preferences. Make sure that AppleTalk service is on if you're printing to an AppleTalk printer.

Look for improved BeOS network drivers, including support for additional hardware in the future, at http://www.be.com/support/updates/index.html. You'll also find third-party drivers, which sometimes appear before Be offers supported versions, at http://www.be.com/beware/Drivers.html.


Developers' Workshop: Updating An Old Sound Card Driver

By Marc Ferguson

The new Media Kit contains a compatibility interface which lets you use a pre-R4 sound card driver in R4 with only minor modifications to the driver. Here is a description of those modifications, assuming that you are starting with a driver written to the old API described in the Newsletter article:

Developers' Workshop: DynaDraw, Part Two

The most important change is that the driver must publish a "sample clock" which allows the Media Kit to synchronize to the sound card. The sample clock corresponds to performance time measured by the DAC (or ADC) clock in microseconds. If the nominal sample rate is 44100 samples per second then the sample clock moves at (1000000 / 44100) microseconds per sample processed by the DAC (or ADC). If the DAC is actually processing 44109 samples per second than the DAC's sample clock will run slightly faster than real-time.

The sample clock is returned by the driver in the audio_buffer_header structure which now looks like this:

typedef struct audio_buffer_header {
  int32 buffer_number;
  int32 subscriber_count;
  bigtime_t time;
  int32 reserved_1;
  int32 reserved_2;
  bigtime_t sample_clock;
} audio_buffer_header;

As before, the SOUND_WRITE_BUFFER and SOUND_READ_BUFFER ioctls should store an estimate of the system_time() corresponding to the beginning of the buffer in the time slot of the audio_buffer_header. They should also fill the sample_clock slot with the sample clock for the beginning of the buffer.

To calculate the sample clock, keep track of the number of samples processed and multiply by the sample clock rate:

header->sample_clock =
  (samples_processed * 1000000LL) / sample_rate;

As long as buffers are flowing continuously, the sample clock will move at the same rate as the performance time of the buffers. But if there is an interruption between buffers then the sample clock must account for the length of the interruption. One way to do this is to measure the duration of the interruption and increment samples_processed by the number of samples that would have been played during that time:

samples_processed += (time_skipped * sample_rate) / 1000000;

There are four new ioctl codes which a driver may optionally support to negotiate an optimal buffer size with the Media Kit. They appear at the end of this list:

enum {
  SOUND_GET_PARAMS = B_DEVICE_OP_CODES_END,
  SOUND_SET_PARAMS,
  SOUND_SET_PLAYBACK_COMPLETION_SEM,
  SOUND_SET_CAPTURE_COMPLETION_SEM,
  SOUND_RESERVED_1,   /* unused */
  SOUND_RESERVED_2,   /* unused */
  SOUND_DEBUG_ON,     /* unused */
  SOUND_DEBUG_OFF,    /* unused */
  SOUND_WRITE_BUFFER,
  SOUND_READ_BUFFER,
  SOUND_LOCK_FOR_DMA,
  SOUND_SET_CAPTURE_PREFERRED_BUF_SIZE,
  SOUND_SET_PLAYBACK_PREFERRED_BUF_SIZE,
  SOUND_GET_CAPTURE_PREFERRED_BUF_SIZE,
  SOUND_GET_PLAYBACK_PREFERRED_BUF_SIZE
};

The SOUND_SET_CAPTURE_PREFERRED_BUF_SIZE and SOUND_SET_PLAYBACK_PREFERRED_BUF_SIZE ioctls take an (int32) argument containing the buffer size (without the header) that the Media Kit plans to send. If the driver is using a circular DMA buffer it may want to set the size of the DMA buffer to twice the preferred buffer size to minimize latency.

The SOUND_GET_CAPTURE_PREFERRED_BUF_SIZE and SOUND_GET_PLAYBACK_PREFERRED_BUF_SIZE ioctls take an (int32*) argument in which the driver can return the current setting of the preferred buffer size.

Devices supporting the above API should be published under the /dev/audio/old/ directory where they will be found by the "legacy" media add-on.

Adding a sample clock to your old R3 sound card driver will get it up and running again under R4.


Where Is Your Voice?

By Jean-Louis Gassée

A few weeks ago, I was on a teleconference discussing technology trends with the advisory board of a Swiss investment fund. Once we were done scratching our heads over e-stocks and the Great Kleiner Perkins Roll-Up, handicapping the DOJ vs. Microsoft fight and the advent of the Great Digital Convergence, the conversation drifted toward more mundane themes such as IP telephony, a.k.a. VOIP. Experts think this is The Next Big Thing. Not being one of the chosen, I don't know. It's not that I don't trust the sages; it's just that I haven't had an opportunity to test any incarnations of VOIP, to get a personal feel for the devilish details that make or break a great idea. Ascribe this, if you will, to my own involvement with the Newton or, more generally, to software that runs on acetate.

This cautious attitude didn't endear me to one of the participants, who challenged me in two ways. You call your BeOS a "Media OS," this person said, and you don't even use one of the most natural media: Voice. You have no opinion because you haven't tried the current products. I hadn't -- I was caught with my opinion down.

The relationship of BeOS to VOIP wasn't obvious, but the need to answer the Media OS challenge was clear. I went out forthwith, and bought three of the leading products from L&H, IBM, and Dragon Systems.

My experience with them was time consuming, and painful but instructive. Installing the software itself was no more or less complicated than it is for other mainstream Windows applications. This is a coded statement meant to convey that you won't recognize your hard disk after the fragmentation grenade has strewn .DLLs everywhere. The uninstalling process is only partially successful, as the system appears unsure of its ability to reverse parts of the process.

But that's nothing compared to the rest of the experience. First come the microphone problems. We all remember how it feels to struggle with a balky microphone when giving a speech. Difficulties start with the very first link in the system: Speech recognition is very sensitive to the quality of material it is fed; this, in turn, varies greatly with the microphone's position.

Knowing this, L&H and IBM have adopted the same solution: They ask you to wear a headset that combines microphone and earphone. This ensures a reliable, repeatable setting for the delicate "position" variable. The version of Dragon Systems I tried added a twist: They supply you with a neat little digital dictation recorder that you can later connect to your PC, and transfer your voice notes to the speech recognition system.

All three systems demand fairly extensive training that takes up to two hours of your time. The idea is to let the system store variations of repeated speech patterns in the way you dictate sentences and commands. IBM astutely uses the training exercises to explain the difficulties involved in recognizing speech, and outlines some techniques it uses to distinguish between similar-sounding words. This is interesting, and a good way to set expectations. Unfortunately, with all three products, expectations are set too high. In particular, after I had "successfully" completed a lengthy training sequence, I expected the system to work. It didn't. In all cases, dictation yielded too many errors—I couldn't manage two lines of text without several errors. And error correction, in turn, proved too error prone: I had difficulty using voice commands to do it, and had the same problem with other application and system actions. Of course, I suspected my French accent. I'm sure it adds to the strain on the poor system.

As a result of my difficulties, I did two things: I asked around and I thought about the problem. Asking around, I had trouble finding the kind of happy users depicted in the IBM commercials: I talk, it types, get your own. I talk, it balks, take mine is closer to the actual experience. Someone pointed me to a story done by an enterprising reporter who contacted reviewers who had sung the praises of these products a few months before. If memory serves, none of the reviewers used the products they had praised.

As for thinking, I went back to the accent problem. This is a country of many accents, both indigenous and foreign, and a country of immigrants. Regardless of my own "challenges," these products do not appear to be ready to serve users in the same way the mouse did when it was introduced. It worked immediately, reliably, and for everyone. Voice recognition has very good specialized uses, but it's not robust enough for general use. I'm disappointed, because it would be nice to have a voice-enabled BeOS, but it will have to wait for a new generation of technology.

Creative Commons License
Legal Notice
This work is licensed under a Creative Commons Attribution-Non commercial-No Derivative Works 3.0 License.