The Solaris USB Driver Development Kit includes the USB Generic Driver (Ugen), which presents USB devices to applications through a standard read/write
UNIX interface. Ugen allows for easy access to USB peripherals connected to your SPARC processor-based Solaris system. Currently, Ugen supports USB control, bulk, and interrupt-IN transfers, but not isochronous transfers. We provide an example that uses Ugen to access status information about a USB uninterruptible power supply (UPS). This paper targets application programmers with limited knowledge of USB devices.
Please download the latest Solaris 10 Express releases for the new and ongoing enhancements in USB. Information about the Solaris Express release can be found at http://www.sun.com/software/solaris/solaris-express/sol_index.html.
USB HID PDC
Having been invented by engineers, USB comes with a slew of acronyms. An uninterruptible power supply is classified as a USB HID PDC, for Universal Serial Bus Human Interface Devices Power Device Class. The USB Implementers Forum has defined a number of device class specifications, among them the mass storage class specification, the audio class specification, and the human interface device (HID) class. For a given set of devices, a class specification defines the methods of transferring data, the commands the devices should support, and how they should respond to these commands. The HID class deals with keyboards, mice, and joysticks, but also with devices such as a UPS. The HID specifications for these devices are listed on the USB-IF HID web page. The following paragraphs refer to several sections of these specifications.
Simplified UPS Application
The simple UPS application first will obtain manufacturer and device information about UPS. It will also query additional USB information. The application's goal is to show how to obtain the battery status of the UPS. Is the AC wall power available? Is the battery charging? Does the device need to be replaced? Other information can easily be obtained in a similar fashion. To keep the application simple, a HID parser or GUI has been omitted.
Appendix A provides the demonstration source code for usbups.h
, and Appendix B shows the source code for usbups.c
.
Binding Ugen Driver to Your Device
The Solaris Ugen driver will interface with the USBA1.0 Framework to issue low-level USB transfers. Before being able to talk to the device, the Ugen driver must be bound to the device. This is accomplished using device-specific information. Upon reboot of the system or hot-plugging of a USB device, the Solaris USBA1.0 Framework reads out the USB device's device-specific information (device descriptor).
Upon plugging the UPS into our system, the prtconf(1M)
command shows that the device is initially being recognized as an HID device, using the Solaris hid(7D)
driver. Because the current Solaris USB HID implementation does not support UPS devices, the hid
binding will be removed and the device will be bound to the Ugen driver.
# prtconf -v
input (driver not attached)
Hardware properties:
name='low-speed' type=boolean
...
name='compatible' type=string items=8
value='usb463,ffff.1' + 'usb463,ffff'
+ 'usbif463,class3.0.0' + 'usbif463,class3.0'
+ 'usbif463,class3' + 'usbif,class3.0.0'
+ 'usbif,class3.0' + 'usbif,class3'
# rem_drv ugen
# add_drv -i '"usb463,ffff.1"' -m '* 0666 root sys' ugen
# grep ugen /etc/driver_aliases
ugen "usb463,ffff.1"
# reboot -- -r // Reconfiguration reboot
# prtconf -D
...
usb, instance #0 (driver name: ohci)
mouse, instance #0 (driver name: hid)
keyboard, instance #1 (driver name: hid)
input, instance #0 (driver name: ugen)
...
# cd /dev/usb/463.ffff/0
# cd 0
# ls -l
cntrl0 // Default Control Pipe
cntrl0stat // Default Control status
devstat // Device Status
if0in1 // Interrupt IN Pipe
if0in1stat // Interrupt IN status
Details on ugen are available as part of the Solaris man pages. You can also access details at http://developers.sun.com/solaris/developer/support/driver/docs/ugen7d.txt.
Obtaining USB Device Status
Before accessing specific device information, the application intends to find out the overall status of the device. Ugen will report four device status levels:
- Device is available (
USB_DEV_STAT_ONLINE
) - Device has been disconnected (
USB_DEV_STAT_DISCONNECTED
) - Device has been resumed (
USB_DEV_STAT_RESUMED
) - Device has been reconnected (
USB_DEV_STAT_UNAVAILABLE
)
This information can be used throughout the application; for example, it could be used to ask the user to (re)insert the device. To obtain the device status, you need to consult the devstat
device node.
/*
* Obtain Device status
*/
int
get_dev_stat(int fd) {
int error;
int status;
error = read(fd, &status, sizeof(status));
if (error != sizeof(status)) {
fprintf(stderr, "usbups: Could not read device
status: %sn", strerror(error));
status = -1;
}
else {
switch(status) {
case USB_DEV_STAT_ONLINE:
DPRINT("usbups: Device Status - Device
is availablen");
break;
case USB_DEV_STAT_DISCONNECTED:
DPRINT("usbupsS: Device Status - Device
has been disconnectedn");
break;
case USB_DEV_STAT_RESUMED:
DPRINT("usbups: Device Status - Device
has been resumedn");
break;
case USB_DEV_STAT_UNAVAILABLE:
DPRINT("usbups: Device Status - Device
powered downn");
break;
}
}
return status;
} // end get_dev_stat
...
./* Obtain Device Status
* The default device status node is/devstat
* i.e. /dev/usb/463.ffff/0/devstat
*/
strcpy(name_buffer, path);
strcat(name_buffer, "/");
strcat(name_buffer, "devstat");
devstat_fd = open(name_buffer, O_RDONLY | O_EXCL );
if (devstat_fd < 0) {
fprintf(stderr, "usbups: Error opening %s: %sn",
name_buffer, strerror(errno));
exit(errno);
}
if (get_dev_stat(devstat_fd) != USB_DEV_STAT_ONLINE) {
fprintf(stderr, "usbups: Device is not availablen");
exit(1);
}
Reading the USB Device Descriptor
USB devices report their attributes using descriptors. A descriptor is simply a data structure with a defined format. The device descriptor describes general information about a USB device. It includes the manufacturer ID, product ID, class code, and so on (see USB 2.0/ 9.6.1 in References section). The device descriptor information is also requested by the USBA1.0 framework in order to build the device tree; its contents were used when binding Ugen to our device (see the preceding code sample).
USB devices present their device information in response to a GET_DESCRIPTOR SETUP
request in a form that is little-endian and, for multibyte integers, unaligned. This SETUP
request is sent to the device using a control pipe. In the following code snippet, the SETUP
request (setup_data
) is prepared in the init_cntrl_req
function. (init_cntrl_req
fills up an 8-byte data structure, setup_data
, and performs a byte swap for SPARC platforms.) Details about init_cntrl_req
can be found in the source code listing (see Appendixes A and B).
/* Open default control endpoint, in order to send GET_DESCRIPTOR
* requests. The default control pipe name is/cntrl0 */
strcpy(name_buffer, path);
strcat(name_buffer, "/");
strcat(name_buffer, "cntrl0");
ctrl_fd = open(name_buffer, O_RDWR|O_EXCL);
if (ctrl_fd < 0) {
// Error Handling
}
...
/*********************************************************
* Obtain device descriptor
* Device Descriptor Setup Packet
* bmRequestType = USB_DEV_REQ_DEV_TO_HOST (0x80)
* bRequest = USB_REQ_GET_DESCR (0x06)
* wValue = USB_DESCR_TYPE_DEV (0x0001)
* wIndex = 0
* wLength = 12
*********************************************************/
init_cntrl_req(&setup_data, USB_DEV_REQ_DEV_TO_HOST,
USB_REQ_GET_DESCR, USB_DESCR_TYPE_DEV, 0, 0x0012);
count = write(ctrl_fd, &setup_data, sizeof(setup_data));
if (count != sizeof(setup_data)) {
error = get_ctrl_stat(ctrlstat_fd);
fprintf(stderr, "usbups: Error issuing
GET_DESCRIPTOR(device): %sn", error);
}
else { /* Read DEVICE_DESCRIPTOR */
count = read(ctrl_fd, &dev_descr, sizeof(dev_descr));
if (count != sizeof(dev_descr)) {
error = get_ctrl_stat(ctrlstat_fd);
fprintf(stderr, "usbups: Error reading device
descriptor: %sn", error);
}
}
print_dev_descr(dev_descr);
Descriptors Galore: the Configuration, Interface, HID, and Report Descriptors
A single USB device might have several configurations; that is, a high-power device might support a low-power mode, which might have different functionality. The configuration descriptors include general information about each configuration. The configurati