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.
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.
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:
ftp://ftp.be.com/pub/samples/r4/usb_kit/usbkit_99-03-23.tgz
Source code for components discussed in this article.
http://www.usb.org/developers/
The USB Implementors Forum: Lots of info, specs, etc. here.
http://www.usb.org/developers/download.htm
The USB 1.1 Spec in PDF (Chapters 9-11 are most helpful).
http://www.amazon.com/exec/obidos/ASIN/0201461374/beincorporated/
Universal Serial Bus System Architecture, by MindShare.
There are five classes in the USB Kit:
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.
These classes cannot be created directly. A USBDevice
instance will
create a USBConfiguration
for each of its possible configurations.
USBConfiguration
objects create
USBInterface
s for each interface they
contain. USBEndpoint
objects are created by
USBInterface
s 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.
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_tUSBRoster
::DeviceAdded
(USBDevice
*dev
) { /* ... */ } voidUSBRoster
::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.
The watcher.cpp
example creates a subclas
of USBRoster
that simply
displays a message when a device is added or removed.
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.
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.
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.
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).
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.
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 { int32buffer_number
; int32subscriber_count
; bigtime_ttime
; int32reserved_1
; int32reserved_2
; bigtime_tsample_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.
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.