Sun Java Solaris Communities My SDN Account Join SDN
 
Reference

Using the USB Generic Driver (Ugen) to Access USB Peripherals on Solaris Systems

 

Contents

USB HID PDC
Simplified UPS Application
Binding Ugen Driver to Your Device
Obtaining USB Device Status
Reading the USB Device Descriptor
Descriptors Galore: the Configuration, Interface, HID, and Report Descriptors
Reading the Report Descriptor
Reading the HID Input Report
Putting It All Together
Nice to Know
References
Appendix A: usbups.h
Appendix B: usbups.c
Appendix C: Ugen Man Page

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:

  1. Device is available (USB_DEV_STAT_ONLINE)
  2. Device has been disconnected (USB_DEV_STAT_DISCONNECTED)
  3. Device has been resumed (USB_DEV_STAT_RESUMED)
  4. 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 configuration descriptor is obtained in a similar manner as the device descriptor, by sending a GET_DESCRIPTOR request. In this example the configuration descriptor is skipped and the default configuration is assumed. (A HID class device typically has only one configuration descriptor).

Each device (or configuration) may support one or more interfaces. An example of a multiple interface device could be an audio device with a streaming interface and a control interface (for volume, for example). Another example is a CDROM, which can be used as a mass storage device and an audio device, controlled by its respective drivers. The interface descriptor details the different interfaces. Note, however, that devices with multiple interfaces in the same configuration can be used by multiple drivers simultaneously. Drivers with multiple configurations, which each have a single interface, can operate only one interface at a time (as only one configuration can be active at a time).

Reading and processing all the descriptors in our example leads to one goal: parsing HID reports. For a HID device, information is sent to the driver in the form of reports. These reports can contain a lot of different information, from the status of an LED to the battery level to the relative position of the mouse scroll wheel, and so on. Reading the HID descriptor and the report descriptor is the first step in decoding and understanding the HID reports. Figure 1 summarizes the different descriptors for a HID device.

HID Device
Figure 1: Different Descriptors for a HID Device

The following example shows how to use the interface descriptor and how to access the HID descriptor.

/*********************************************************
 * Obtain Interface Descriptor
 * Read the Interface Descriptor to obtain the class and 
 * subclass, _since_ for HID devices the class type is  
 * defined there, and not in the Device Descriptor.
 * Interface Descriptor Setup Packet
 *      bmRequestType   = USB_DEV_REQ_DEV_TO_HOST (0x80)
 *      bRequest        = USB_REQ_GET_DESCR (0x06)
 *      wValue          = USB_DESCR_TYPE_IF (0x0004)
 *      wIndex          = 0
 *      wLength         = 7
 *********************************************************/

init_cntrl_req(&setup_data, USB_DEV_REQ_DEV_TO_HOST, 	
	USB_REQ_GET_DESCR, USB_DESCR_TYPE_IF, 0, 0x0007);

count = write(ctrl_fd, &setup_data, sizeof(setup_data)); 
if (count != sizeof(setup_data)) {
     	error = get_ctrl_stat(ctrlstat_fd);
	// error handling
}
else {
      /* Read INTERFACE_DESCRIPTOR */
      count = read(ctrl_fd, &if_descr, sizeof(if_descr));
      if (count != sizeof(if_descr)) {
          	error = get_ctrl_stat(ctrlstat_fd);
	// error handling 
      }
}        

if (if_descr.bInterfaceClass != 3) {
     fprintf(stderr, "usbups: Device is not a HID device: %sn",	
	   if_descr.bInterfaceClass);
     exit(1);
}


/*********************************************************
 * Obtain HID descriptor
 * Read the HID descriptor to access the Report Descriptor
 * (hid_descr.wReportDescriptorLength)
 * Interface Descriptor Setup Packet
 *      bmRequestType   = USB_DEV_REQ_DEV_TO_HOST (0x80)
 *      bRequest        = USB_REQ_GET_DESCR (0x06)
 *      wValue          = USB_DESCR_TYPE_HID (0x0021)
 *      wIndex          = 0
 *      wLength         = 9
 *********************************************************/

init_cntrl_req(&setup_data, USB_DEV_REQ_DEV_TO_HOST, 
	USB_REQ_GET_DESCR, USB_DESCR_TYPE_HID, 0, 0x0009);

count = write(ctrl_fd, &setup_data, sizeof(setup_data)); 
if (count != sizeof(setup_data)) {
	error = get_ctrl_stat(ctrlstat_fd);
             // error handling
}



else {
	/* Read HID_DESCRIPTOR 
           * Note: because of word alignment/padding, the 
	 * sizeof(hid_descr)==10)
           */

           count = read(ctrl_fd, &hid_descr, sizeof(hid_descr));
           if ((count+1) != sizeof(hid_descr)) {
             	error = get_ctrl_stat(ctrlstat_fd);
                          // error handling
              }
}        

Reading the Report Descriptor

A report descriptor is a complete set of all information items for a device. By looking at the report descriptor alone, an application knows how to handle incoming data, as well as the data's possible uses. The following table shows you an example of a report descriptor. The report descriptor can be obtained by issuing the usual GET_DESCRIPTOR request. The length of the descriptor (wLength) was previously obtained from the HID descriptor (hid_descr->wReportDescriptorLength).

The UPS report descriptor can be dissected using the HID Usage Tables v1.1 and the Usage Tables for HID Power Devices v1.0 specifications (see the References section).

Item Value (Hex)
Usage Page (05) (Power Device) (HID 6.2.2.7) 0x5 0x84
Usage (09) (UPS) (HID 6.2.2.8) 0x9 0x4
Collection (a1) (Physical) (HID 6.2.2.6) 0xa1 0x0
    Usage (09) (Power converter) (HID 6.2.2.8) 0x9 0x16
    Collection (a1) (Physical) 0xa1 0x0
        Usage (Power converter ID) 0x9 0x17
        Report ID (11) 0x85 0xb
        Report Size (8) 0x75 0x8
        Report Count (1) 0x95 0x01
        Logical_Minimum (0) 0x15 0x00
        Logical_Maximum (255) 0x26 0xff 0x00
        Unit (None) 0x65 0x00
        Feature (Cnst, Var, Abs) 0xb1 0x03
        ... ...

The HID specification details how the report descriptor information should be used based upon main (input, output, feature), global and local items. In the preceding example, a feature report with Report ID =1 will list the power converter ID using 8 bits.

An application can request an input, output, or feature report. An input report provides information on input items, that is, the state of switches. An output report lists output data such as the state of an LED. Feature data describes device input and output not intended for the end user. The Report ID is key to later interpretation of incoming reports.

To avoid complexity, the application cheated a little and hid some of the parsing. It is not the intention to build a complete HID parser here, but only to illustrate how to obtain information from the device. Often device manufacturers will publish their HID report descriptor, eliminating the need to write a generic HID parser. This example uses an Ellipse UPS from MGE UPS Systems. The Ellipse HID report descriptor is documented on the Network UPS Tools web site (see References section).

Parsing input items shows a total of 7 bytes, though listed in several types of input reports (depending on the Report ID). For example, when one receives an input report, with Report ID = 2, it will contain 2 bytes (9 bits of battery system information and 7 padding bits).

	RemainingCapacity (8) (Report ID: 22)
	RunTimeToEmpty(16) (Report ID: 22)
	CommunicationLost(1) (Report ID: 1)
	Undefined(7) 			// Padding
	Battery System (9) (Report ID: 2)
		ACPresent(1)
		Charging(1)
		Discharging(1)
		BelowRemainingCapacityLimit (1)
		NeedReplacement (1)
		Power Device: Good (1)
		Power Device: ShutdownImminent(1)
		Power Device: Overload (1)
		Power Device: Internal Failure (1)
	Undefined(7)			// Padding
	SwitchOn/Off (1) (Report ID: 3)
	Undefined(7)			// Padding

	In total: 8 bytes (7 bytes + Report ID byte)

This example focuses on the battery system:

  1. Is the incoming AC power live? (Input Report Item)
  2. Is the battery charging? (Input Report Item)

Reading the HID Input Report

The HID Input Report is read using the interrupt IN pipe. The UPS will send reports on the interrupt IN pipe when a specific event occurs. When an input report with report ID=2 is received, the battery status is printed.

/* Open Interrupt IN pipe */
strcpy(name_buffer, path);
strcat(name_buffer, "/");
strcat(name_buffer, "if0in1"); 

iin_fd = open(name_buffer, O_EXCL |O_RDONLY |O_NONBLOCK |O_NDELAY);
if (iin_fd < 0) {
    	fprintf(stderr, "usbups: Error opening %s: %sn", 
		name_buffer, strerror(errno));
    	exit(errno);
}

/* Interrupt IN pipe status name is <path>/in0in1stat */
strcat(name_buffer, "stat");
iinstat_fd = open(name_buffer, O_RDONLY|O_EXCL);
if (iinstat_fd < 0) {
	fprintf(stderr, "usbups: Error opening %s: %sn", 
		name_buffer, strerror(errno));
          exit(errno);
}
	
while(1) {
	/* Reading Interrupt IN 
	 * If no data is available, -1 is returned and 
	 * USB_LC_STAT_INT_BUF_FULL is set */
	count = read(iin_fd, input_report, sizeof(input_report));
	if (count == -1) {
	    error = get_ctrl_stat(iinstat_fd);	
	    if (error != USB_LC_STAT_INTR_BUF_FULL) { 
		/* real read error */
		fprintf(stderr, "usbups: Error reading interrupt 
			IN pipe: %sn", error);
		exit(1);
	    }			
	}
	else {				
	     if (input_report[0]==2){ /* Battery Sys Info */
		if (input_report[1] & 0x01) { 
			/* ACPresent */
			DPRINT("usbups: ACPresentn");
		}
		if (input_report[1] & 0x02) { 
			/* Charging */
			DPRINT("usbups: Battery Chargingn");
		}	
		if (input_report[1] & 0x04) { 
			/* Discharging */
			DPRINT("usbups: Battery Dischargingn");
		}
		...			
       	    }
	}
	sleep(5);
}

Putting It All Together

The entire sample program will read and print out the different descriptors, as well as some debugging information. When the UPS is disconnected from the AC outlet, this is reported, and action can be taken (that is, reading out the battery level and notifying the user of uptime before shutting down). The source code generating the following output can be found in the Appendixes.

# ./usbups /dev/usb/4633.ffff/0
usbups: Opening the device status node successful
usbups: Device Status - Device is available
usbups: Opening the default control pipe successful
usbups: Opening the default control status pipe successful
usbups:      Sending GET_DESCRIPTOR(device) packet
usbups:      Sending GET_DESCRIPTOR(device) packet successful

Device descriptor: VID:0463, PID:ffff
  Device release:256(0x100), USB release:1(0x1)
  Descriptor Length:18, Type:1
  Device Class:0, Subclass:0, Protocol:0, Num Cfgs:1

usbups:      Sending GET_DESCRIPTOR(interface) packet
usbups:      Sending GET_DESCRIPTOR(interface) packet successful

Interface descriptor:
  Descriptor Length:9, Type:4
  Interface Number: 0, Alternate Setting: 0
  Number of Endpoints: 1
  Interface Class: 3, Interface Subclass: 0

usbups: Device is a HID device
usbups:      Sending GET_DESCRIPTOR(hid) packet
usbups:      Sending GET_DESCRIPTOR(hid) packet successful

HID descriptor:
  Descriptor Length:9, Type:33
  Hid Class spec release: 100
  Country Code: 33 (FYI 33 = US)
  Number of class descriptors: 1
  Class descriptor type: 34
  Total size of Report Descriptor: 512

usbups:      Sending GET_DESCRIPTOR(report) packet
usbups:      Sending GET_DESCRIPTOR(report) packet successful

Report descriptor:
0x5 0x84 0x9 0x4 0xa1 0x0 0x9 0x16 0xa1 0x0 0x9 0x17 0x85 0xb 0x75 0x8 0x95 0x1 0x15 
0x0 0x26 0xff 0x0 0x65 0x0 0xb1 0x3 0x9 0x1c 0xa1 0x0 0x9 0x1d 0xb1 0x3 0x9 0x30 0x85 
0xe 0x67 0x21 0xd1 0xf0 0x0 0x55 0x7 0xb1 0x83 0x9 0x53 0x85 0x13 0xb1 0x82 0x9 0x54 
0x75 0x10 0x26 0xff 0x7f 0xb1 0x82 0xc0 0xc0 0x9 0x1e 0xa1 0x84 0x9 0x1f 0x85 0xb 0x75 
0x8 0x95 0x1 0x65 0x0 0x55 0x0 0x26 0xff 0x0 0xb1 0x3 0x9 0x40 0x85 0x12 0x67 0x21 0xd1 
0xf0 0x0 0x55 0x7 0xb1 0x82 0x9 0x42 0x85 0xd 0x66 0x1 0xf0 0x55 0x0 0xb1 0x83 0x9 0x43
0x75 0x10 0x26 0xff 0x7f 0x66 0x21 0xd1 0x55 0x7 0xb1 0x83 0xc0 0x9 0x24 0xa1 

usbups: Opening the Interrupt IN pipe successful
usbups: Opening the Interrupt IN status pipe successful
usbups: Reading Interrupt IN endpoint data successful: 0 bytes

<DISCONNECT UPS FROM AC OUTLET>

usbups: Reading Interrupt IN endpoint data successful: 3 bytes
Byte[0] = 2
Byte[1] = 24
Byte[2] = 0
usbups: Battery Discharging

<RECONNECTED UPS TO AC OUTLET>

usbups: Reading Interrupt IN endpoint data successful: 3 bytes
Byte[0] = 2
Byte[1] = 23
Byte[2] = 0
usbups: ACPresent
usbups: Battery Charging

Nice to Know

Ugen reports endpoint status via the endpoint status device logical names (that is,/dev/usb/463.ffff/cntrl0stat and /dev/usb/463.ffff/if0in1stat). Appendix C contains the complete list of Ugen error codes. Despite detailed Ugen error reporting, we ran into additional generic I/O and device errors, which we think are nice to know about:

  • The write/read error output is not always specific enough to debug a problem. For example, we learned that write will eventually time out and not complete when sending an incorrect SETUP packet down the pipe.
  • Another error we ran into shows up at the USB OHCI level. When trying to obtain a report using the GET_REPORT command, the device was brought into a confused state.
# breydel usba: usb0:   getting device descriptor failed (<Command timed out> 0x10 -1)
# breydel last message repeated 1 time
  breydel usba: usb0:   getting device descriptor failed (<Endpoint returned stall PID> 0x14 -1)
  breydel usba: WARNING: /pci@8,700000/usb@5,3 (ohci0): Connecting device on port 4 failed

To bring back the device, you can perform a reconfiguration reboot (reboot -- -r), plug the device behind an external hub, or power off the UPS (and wait for several minutes).

References

Note: Other device driver information is available on the Solaris Developer Connection Driver Development White Papers page.

Appendix A: usbups.h

/* 
 * Sample UGEN application
 *
 */

/* 
 * USB Device Setup Packet
 * Refer to USB 2.0/ 9.3
 * 
 * All USB devices respond to requests from the host on the device's
 * Default Control Pipe. These requests are made using control
 * transfers. The request and the request's parameters are sent 
 * to the device in the Setup packet. Every Setup packet has eight
 * bytes: 
 *
 * Offset	Field		Size
 * ----------------------------------
 *   0	bmRequestType	 1
 *   1	bRequest	 	 1
 *   2	wValue		 2
 *   4	wIndex		 2
 *   6	wLength		 2
 *
 * IMPORTANT: Pay attention to (little) endianness. 
 *
 */

typedef struct setup_data {
	uint8_t	bmRequestType;	/* Request Type 	*/
	uint8_t 	bRequest;		/* Setup Request 	*/
	uint16_t	wValue;		/* Request Info	*/
	uint16_t	wIndex;		/* Index/Offset Info	*/
	uint16_t	wLength;		/* # of bytes in transfer */
} setup_data_t;

/*
 * bmRequestType: device request type
 */

#define USB_DEV_REQ_HOST_TO_DEV         		0x00
#define USB_DEV_REQ_DEV_TO_HOST         		0x80
#define USB_DEV_REQ_DIR_MASK            		0x80

#define USB_DEV_REQ_TYPE_STANDARD       		0x00
#define USB_DEV_REQ_TYPE_CLASS          		0x20
#define USB_DEV_REQ_TYPE_VENDOR         		0x40
#define USB_DEV_REQ_TYPE_MASK           		0x60

#define USB_DEV_REQ_RCPT_DEV            		0x00
#define USB_DEV_REQ_RCPT_IF             		0x01
#define USB_DEV_REQ_RCPT_EP             		0x02
#define USB_DEV_REQ_RCPT_OTHER          		0x03
#define USB_DEV_REQ_RCPT_MASK           		0x03

/* Refer to HID1.1/7.2.1 */
#define USB_HID_REQ_DEV_TO_HOST		0xa1	

/* 
 * Defined bRequest Values
 * Refer to USB 2.0/9.4
 */
#define USB_REQ_GET_STATUS              		0x00
#define USB_REQ_CLEAR_FEATURE           		0x01
#define USB_REQ_SET_FEATURE             		0x03
#define USB_REQ_SET_ADDRESS             		0x05
#define USB_REQ_GET_DESCR               		0x06
#define USB_REQ_SET_DESCR               		0x07
#define USB_REQ_GET_CFG                 		0x08
#define USB_REQ_SET_CFG                 		0x09
#define USB_REQ_GET_IF                  		0x0a
#define USB_REQ_SET_IF                  		0x0b
#define USB_REQ_SYNC_FRAME              		0x0c

/* Refer to HID 1.1/7.2.1 */
#define USB_REQ_GET_REP			0x01

/*
 * wValue
 * In the setup packet, the descriptor type is passed in the high 
 * byte of the wValue field. Hence the addition of two zeroes. 
 * Descriptor types:
 */
#define USB_DESCR_TYPE_SETUP_DEV                	0x0100
#define USB_DESCR_TYPE_SETUP_CFG                	0x0200
#define USB_DESCR_TYPE_SETUP_STRING             	0x0300
#define USB_DESCR_TYPE_SETUP_IF                 	0x0400
#define USB_DESCR_TYPE_SETUP_EP                 	0x0500
#define USB_DESCR_TYPE_SETUP_DEV_QLF            	0x0600
#define USB_DESCR_TYPE_SETUP_OTHER_SPEED_CFG    	0x0700
#define USB_DESCR_TYPE_SETUP_IFPWR              	0x0800

#define USB_DESCR_TYPE_DEV                      	0x0100
#define USB_DESCR_TYPE_CFG                      	0x0200
#define USB_DESCR_TYPE_STRING                   	0x0300
#define USB_DESCR_TYPE_IF                       	0x0400
#define USB_DESCR_TYPE_EP                       	0x0500
#define USB_DESCR_TYPE_DEV_QLF                  	0x0600
#define USB_DESCR_TYPE_OTHER_SPEED_CFG          	0x0700
#define USB_DESCR_TYPE_IF_PWR                   	0x0800

/* Refer to HID1.1/7.1 */
#define USB_DESCR_TYPE_HID			0x2100
#define USB_DESCR_TYPE_REP			0x2200
#define	USB_DESCR_TYPE_PHY			0x2300

/* Refer to HID1.1/7.2.1 */
#define USB_DESCR_TYPE_REP_IN			0x0100
#define USB_DESCR_TYPE_REP_OUT			0x0200
#define USB_DESCR_TYPE_REP_FEA			0x0300





/*
 * USB Descriptor Management
 * Standard USB descriptors:
 *
 * USB devices present their configuration information in response to
 * a GET_DESCRIPTOR request in a form which is little-endian and,
 * for multibyte integers, unaligned. It is also position-dependent,
 * which makes non-sequential access to particular interface or
 * endpoint data inconvenient.
 * A GET_DESCRIPTOR request may yield a chunk of data that contains
 * multiple descriptor types. For example, a GET_DESCRIPTOR request
 * for a CONFIGURATION descriptor could return the configuration
 * descriptor followed by an interface descriptor and the relevant
 * endpoint descriptors.
 *
 * usb_dev_descr:
 *      usb device descriptor, refer to USB 2.0/9.6.1,
 */

typedef struct usb_dev_descr {
        uint8_t	bLength;        /* descriptor size              */
        uint8_t      bDescriptorType;/* set to DEVICE                */
        uint16_t     bcdUSB;         /* USB spec rel. number in bcd  */
        uint8_t      bDeviceClass;   /* class code                   */
        uint8_t      bDeviceSubClass;/* sub class code               */
        uint8_t      bDeviceProtocol;/* protocol code                */
        uint8_t      bMaxPacketSize0;/* max pkt size of e/p 0        */
        uint16_t     idVendor;       /* vendor ID                    */
        uint16_t     idProduct;      /* product ID                   */
        uint16_t     bcdDevice;      /* device release number in bcd */
        uint8_t      iManufacturer;  /* manufacturing string         */
        uint8_t      iProduct;       /* product string               */
        uint8_t      iSerialNumber;  /* serial number string index   */
        uint8_t      bNumConfigurations; /* #configs for device      */
} usb_dev_descr_t;


/* 
 * Interface Descriptor
 * Refer to USB 2.0/9.6.5
 */

typedef struct usb_if_descr {
       uint8_t	bLength;	        /* descriptor size	*/
       uint8_t	bDescriptorType;/* Interface descriptor type */
       uint8_t	bInterfaceNumber; /* Interface number	*/
       uint8_t	bAlternateSetting;/*			*/
       uint8_t	bNumEndpoints;    /* Number of Endpoints	*/
       uint8_t	bInterfaceClass;  /* class code	*/
       uint8_t	bInterfaceSubClass;/* Sunclass code	*/
} usb_if_descr_t;



./*
 * HID Descriptor
 * Refer to HID 1.1/6.2.1
 * Watch out for padding! sizeof(usb_hid_descr) is 10.
 */
 
typedef struct usb_hid_descr {
     uint8_t	bLength;	/* descriptor size		*/
     uint8_t	bDescriptorType;/* HID descriptor type	*/
     uint16_t	bcdHID;	/* HID class spec release	*/
     uint8_t	bCountryCode;	/* Country Code	*/
     uint8_t	bNumDescriptors;/* # of class descriptors	*/
     uint8_t	bClassDescriptorType;/*		*/
     uint16_t	wReportDescriptorLength;/* total size of Report Descr */
 } usb_hid_descr_t;

Appendix B: usbups.c

#pragma ident   "@(#)usbups.c  1.2     02/06/20 SMI"

/* 
 * Sample UGEN Application
 * This application demonstrates the use of UGEN (USB Generic Device)
 * Driver. The target device is a USB HID PCD device such as an 
 * Uninterruptible Power Supply (UPS). 
 *
 */


#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/systeminfo.h>
#include <sys/usb/clients/ugen/usb_ugen.h>
#include "usbups.h"

#define UPSDEBUG	1
#define BYTESWAP(in) (((in & 0xFF) << 8) + ((in & 0xFF00) >> 8))
#define DPRINT(m)    if(UPSDEBUG) {printf(m);}

/*
 * Function Prototypes
 */
boolean_t is_sparc();
void	init_cntrl_req(setup_data_t *, uint8_t, uint8_t, uint16_t, uint16_t, uint16_t);
int	get_dev_stat(int);
int	get_ctrl_stat(int);
void 	print_dev_descr(usb_dev_descr_t);
void	print_if_descr(usb_if_descr_t);
void 	print_hid_descr(usb_hid_descr_t);
void	print_rep_descr(uint8_t *);

 
/*
 * Return true if running on a SPARC system.
 */
#define IS_SPARC_BUFSIZE        6
boolean_t is_sparc()
{
	char buf[IS_SPARC_BUFSIZE];
	sysinfo(SI_ARCHITECTURE, buf, IS_SPARC_BUFSIZE);

       	return (strcmp(buf, "sparc") == 0);
}



/*
 * Create control SETUP packet 
 */

void
init_cntrl_req(setup_data_t *sd, uint8_t bmRequestType, uint8_t 
        bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength) {

	(*sd).bmRequestType 	= bmRequestType;
	(*sd).bRequest		= bRequest;
	
	/* Sparc is big endian, USB little endian */
	if(is_sparc()) {
		(*sd).wValue	= BYTESWAP(wValue);
		(*sd).wIndex	= BYTESWAP(wIndex);
		(*sd).wLength	= BYTESWAP(wLength);
	}
	else {
		(*sd).wValue	= wValue;
		(*sd).wIndex	= wIndex;
		(*sd).wLength	= wLength;
	}

}


/* 
 * 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;			                       
                } // end switch
		

      	} // end else

	return status;
	
} // end print_dev_stat()

/*
 * Obtain Control Endpoint Status
 */

int
get_ctrl_stat(int fdstat) {

	int status, error;

	DPRINT("usbups: Reading control status endpoint to get more 
		info on the errorn");
	error = read(fdstat, &status, sizeof(status));
	if (error == sizeof(status)) {
	   switch (status) {
		case USB_LC_STAT_SUSPENDED:
			DPRINT("usbups: CtrlStatus - Device was 
				suspendedn");
			break;
		case USB_LC_STAT_DISCONNECTED:
			DPRINT("usbups: CtrlStatus - Device was 
				disconnectedn");
			break;
		case USB_LC_STAT_NO_RESOURCES:
			DPRINT("usbups: CtrlStatus - No 
				resources available for 
				requestn");
			break;
		case USB_LC_STAT_DEV_NOT_RESP:
			DPRINT("usbups: CtrlStatus - Device is 
				not respondingn");
			break;
		case USB_LC_STAT_TIMEOUT:
			DPRINT("usbups: CtrlStatus - Command
				 time outn");
			break;
		case USB_LC_STAT_HW_ERR:
			DPRINT("usbups: CtrlStatus - Host 
				Controller h/w errorn");
			break;
		case USB_LC_STAT_NOERROR:
			DPRINT("usbups: CtrlStatus - No
				 errorn");
			break;
		default:
			DPRINT("usbups: CtrlStatus - Error Not 
				Determinedn");
			break;
		}
	}
	
	return status;
	
} // end get_ctrl_stat


/* Descriptor Print functions */

void 
print_dev_descr(usb_dev_descr_t dev_descr) {

/*
 * Note that the Vendor ID and Product ID are in binary coded decimal,
 * and that USB is opposite endian of SPARC. Swap bytes to properly
 * display these fields on SPARC.
 */
	printf("nDevice descriptor: ");
	printf("VID:%4.4x, PID:%4.4xn", BYTESWAP(dev_descr.idVendor), 
	  BYTESWAP(dev_descr.idProduct));
	printf("  Device release:%d(0x%x), USB release:%d(0x%x)n",
	   	dev_descr.bcdDevice, dev_descr.bcdDevice, dev_descr.bcdUSB, dev_descr.bcdUSB);
	printf("  Descriptor Length:%d, Type:%dn",
		dev_descr.bLength, dev_descr.bDescriptorType);
	printf("  Device Class:%d, Subclass:%d, Protocol:%d, Num 
		Cfgs:%dnn", dev_descr.bDeviceClass, 
		dev_descr.bDeviceSubClass,	
		dev_descr.bDeviceProtocol,
		dev_descr.bNumConfigurations);
	  
} // end print_dev_descr()


void
print_if_descr(usb_if_descr_t if_descr) {

	printf("nInterface descriptor:n");
	printf("  Descriptor Length:%d, Type:%dn",if_descr.bLength, 
		if_descr.bDescriptorType);
	printf("  Interface Number: %d, Alternate Setting: %dn", 
		if_descr.bInterfaceNumber, 
		if_descr.bAlternateSetting);
	printf("  Number of Endpoints: %dn", 
		if_descr.bNumEndpoints);
	printf("  Interface Class: %d, Interface Subclass: %dnn", 
		if_descr.bInterfaceClass, 
		if_descr.bInterfaceSubClass);
}


void 
print_hid_descr(usb_hid_descr_t hid_descr) {

	printf("nHID descriptor:n");
	printf("  Descriptor Length:%d, Type:%dn",hid_descr.bLength, 
		hid_descr.bDescriptorType);
	
/*
 * Note that the HID class spec release is in binary coded decimal,
 * and that USB is opposite endian of SPARC.  Swap bytes to properly
 * display these fields on SPARC.
 */
	
	printf("  Hid Class spec release: %xn", 
		BYTESWAP(hid_descr.bcdHID));
	printf("  Country Code: %d (FYI 33 = US)n", 
		hid_descr.bCountryCode); 
	printf("  Number of class descriptors: %dn", 
		hid_descr.bNumDescriptors);
	printf("  Class descriptor type: %dn", 	
		hid_descr.bClassDescriptorType);
	printf("  Total size of Report Descriptor: %dnn", 
		hid_descr.wReportDescriptorLength);
}


void	
print_rep_descr(uint8_t *buf) {
	
	int i;
		
	printf("nReport descriptor:n");
	/* First 128 to get a flavor of the report descriptor */
	for (i=0;i<128;i++) {
		printf("0x%x ", *buf);
		buf++;
	}
	
	printf("nn");

}



void
main(int argc, char *argv[]) {
	
	setup_data_t	setup_data;
	usb_dev_descr_t	dev_descr;
	usb_if_descr_t	if_descr;
	usb_hid_descr_t	hid_descr;
	uint8_t		rep_descr[512];
	uint8_t		input_report[7];

	
	int		errno, error, status, count, i;
	int		devstat_fd, ctrl_fd, ctrlstat_fd, 
			iin_fd, iinstat_fd;
	caddr_t		path, name_buffer;



	/* Validate argument, which is directory containing device
	 * fds.  Borrowed from ugen sample app 
	 */
	if (argc != 2) {
                fprintf(stderr, "Usage: %s <directory of UPS device 
		nodes: i.e./dev/usb/463.ffff/0>n", argv[0]);
                exit(EINVAL);
	}

	
	path = malloc(strlen(argv[1]));
	strcpy(path, argv[1]);
	name_buffer = malloc(strlen(argv[1]) + 12);

	/* Obtain Device Status 
	 * The default device status node is <path>/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);
        	}
	DPRINT("usbups: Opening the device status node 
		successfuln");

	if (get_dev_stat(devstat_fd) != USB_DEV_STAT_ONLINE) {
                fprintf(stderr, "usbups: Device is not availablen");
                exit(1);
        	}		
		
	/* Open default control endpoint, in order to send 
	 * GET_DESCRIPTOR requests. 
	 * The default control pipe name is <path>/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) {
                fprintf(stderr, "usbups: Error opening %s: %sn", 
		name_buffer, strerror(errno));
                exit(errno);
        	}
	DPRINT("usbups: Opening the default control pipe 
		successfuln");

        	/* Control pipe status name is <path>/cntrl0stat */
        	strcat(name_buffer, "stat");
        	ctrlstat_fd = open(name_buffer, O_RDONLY|O_EXCL);
        	if (ctrlstat_fd < 0) {
                fprintf(stderr, "usbups: Error opening %s: %sn", 
		name_buffer, strerror(errno));
                exit(errno);
        	}
	DPRINT("usbups: Opening the default control status pipe 
		successfuln");


/*********************************************************
 * 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);
	DPRINT("usbups:      Sending GET_DESCRIPTOR(device) 
		packetn");
	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 {
		DPRINT("usbups:      Sending 	
		     GET_DESCRIPTOR(device) packet successfuln");
		
		/* 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);



/*********************************************************
 * Obtain interface descriptor
 * Read the Interface Descriptor to obtain the class and 
 * subclass. As for HID devices, the class type is 
 * defined not in the Device Descriptor, but in the 
 * interface descriptor.
 * Interface Descriptor Setup Packet
 * 	bmRequestType 	= USB_DEV_REQ_DEV_TO_HOST (0x80)
 *	bRequest  	= USB_REQ_GET_DESCR (0x06)
 *	wValue		= USB_DESCR_TYPE_IF (0x0004)
 *	wIndex		= 0
 *	wLength		= 7
 *********************************************************/

	init_cntrl_req(&setup_data, USB_DEV_REQ_DEV_TO_HOST, 
		USB_REQ_GET_DESCR, USB_DESCR_TYPE_IF, 0, 0x0007);
	DPRINT("usbups:      Sending GET_DESCRIPTOR(interface) 
		packetn");
	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(interface): %sn", error);
	}
	else {
		DPRINT("usbups:      Sending 
		  GET_DESCRIPTOR(interface) packet successfuln"			
		/* Read INTERFACE_DESCRIPTOR */	
		count = read(ctrl_fd, &if_descr, 
		sizeof(if_descr));
		if (count != sizeof(if_descr)) {
			error = get_ctrl_stat(ctrlstat_fd);	fprintf(stderr, 
			  "usbups: Error reading 
			   device descriptor: %sn", error);
		}
	}	 
	print_if_descr(if_descr);

	if (if_descr.bInterfaceClass != 3) {
		fprintf(stderr, "usbups: Device is not a HID 
		    device: %sn", if_descr.bInterfaceClass);
		exit(1);
	}
	DPRINT("usbups: Device is a HID devicen");
	
	
/*********************************************************
 * Obtain HID descriptor
 * Read the HID descriptor to access the Report Descriptor
 * (hid_descr.wReportDescriptorLength)
 *
 * Interface Descriptor Setup Packet
 * 	bmRequestType 	= USB_DEV_REQ_DEV_TO_HOST (0x80)
 *	bRequest  	= USB_REQ_GET_DESCR (0x06)
 *	wValue		= USB_DESCR_TYPE_HID (0x0021)
 *	wIndex		= 0
 *	wLength		= 9
 *********************************************************/

	init_cntrl_req(&setup_data, USB_DEV_REQ_DEV_TO_HOST, 
		USB_REQ_GET_DESCR, USB_DESCR_TYPE_HID, 0, 0x0009);
	DPRINT("usbups:      Sending GET_DESCRIPTOR(hid) packetn");
	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(hid): %sn", error);
	}
	else {
		DPRINT("usbups:      Sending GET_DESCRIPTOR(hid) 
			packet successfuln");	
		
		/* Read HID_DESCRIPTOR 
		 * Note: because of word alignment/padding, the
		 * sizeof(hid_descr)==10)
		 */
		count = read(ctrl_fd, &hid_descr,
			sizeof(hid_descr));
		if ((count+1) != sizeof(hid_descr)) {
			error = get_ctrl_stat(ctrlstat_fd);	fprintf(stderr, 
			  "usbups: Error reading 
			    hid descriptor: %sn", error);
		}
	}	 

	print_hid_descr(hid_descr);



/*********************************************************
 * Obtain Report descriptor 	 
 *
 * Interface Descriptor Setup Packet
 *	bmRequestType	: USB_DEV_REQ_DEV_TO_HOST |  
 *			  USB_DEV_REQ_RCPT_IF (0x80 | 0x01)
 *	bRequest	: USB_REQ_GET_DESCR (0x06)
 *	wValue		: USB_DESCR_TYPE_REP (0x0022)
 *	wIndex		: 0	
 *	wLength		: hid_descr.wReportDescriptorLength
 *********************************************************/

	init_cntrl_req(&setup_data, USB_DEV_REQ_DEV_TO_HOST | 
		USB_DEV_REQ_RCPT_IF, USB_REQ_GET_DESCR, 
		USB_DESCR_TYPE_REP, 0, 
		hid_descr.wReportDescriptorLength);
	DPRINT("usbups:      Sending GET_DESCRIPTOR(report) 
		packetn");
	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(report): %sn", error);
	}
	else {
		DPRINT("usbups:     Sending GET_DESCRIPTOR(report) 
			packet successfuln");	
		
		/* Read REPORT_DESCRIPTOR 
		 * Arbitrary # of bytes, as we will not perform
 		 * hid report parsing. We just want to have an
 		 * idea of how the descriptor looks like
		 */
		 
		count = read(ctrl_fd, rep_descr, 	
			hid_descr.wReportDescriptorLength);
		if (count != hid_descr.wReportDescriptorLength) {
			error = get_ctrl_stat(ctrlstat_fd);	fprintf(stderr, 
			  "usbups: Error reading 
			    report descriptor: %sn", error);
		}
	}	 

	print_rep_descr(rep_descr);


/* Parsing the HID reports is outside the scope of this demo app. 
 * Once it is understood which type of reports can be received and
 * how they should be read, we can query the device for such reports. 
 * In this example, it is assumed the parsing has been completed. 
 * Since the information needed is listed as an input item, we will 
 * listen
 * for an input report )ID=2). 
 *	...
 * 	UsagePage (Battery System)
 *		Usage (ACPresent)
 * 		Usage (Charging)
 *		Usage (Discharging)
 *		Usage (BelowRemainingCapacityLimit)
 * 		Usage (NeedReplacement)
 *		Usage (Power Device: Good)
 * 		Usage (Power Device: ShutdownImminent)
 * 		Usage (Power Device: Overload)
 * 		Usage (Power Device: Internal Failure)
 *		Report_ID (2)
 *		Report_Size (1)
 * 		Report_Count (9)
 * 		Logical_Maximum (1)
 *		Input (Cnst, Var, Abs, Vol)
 *		...
 * When reading the interrupt IN pipe, an event needs to happen with 
 * the device in order for the device to send out the INPUT REPORT 
 * descriptor. In the case of our UPS, a change to the UPS needs to 
 * occur (i.e., loss of AC power) before an input report is sent out. 
 * Note the first byte of the input report is always the report ID. 
 *
 */

	/* Open Interrupt IN pipe */
	strcpy(name_buffer, path);
        	strcat(name_buffer, "/");
        	strcat(name_buffer, "if0in1"); 

	iin_fd = open(name_buffer, O_EXCL |O_RDONLY |O_NONBLOCK 
		|O_NDELAY);
        	if (iin_fd < 0) {
                fprintf(stderr, "usbups: Error opening %s: %sn", 
		name_buffer, strerror(errno));
                exit(errno);
    	}
	DPRINT("usbups: Opening the Interrupt IN pipe successfuln");

        	/* Interrupt IN pipe status name is <path>/in0in1stat */
        	strcat(name_buffer, "stat");
        	iinstat_fd = open(name_buffer, O_RDONLY|O_EXCL);
        	if (iinstat_fd < 0) {
                fprintf(stderr, "usbups: Error opening %s: %sn", 
		name_buffer, strerror(errno));
                exit(errno);
        	}
	DPRINT("usbups: Opening the Interrupt IN status pipe 
		successfuln");

	while(1) {
			
	/* Reading Interrupt IN 
	 * If no data is available, -1 is returned and 
	 * USB_LC_STAT_INT_BUF_FULL is set. 
	 */
	count = read(iin_fd, input_report, sizeof(input_report));
	if (count == -1) {
		error = get_ctrl_stat(iinstat_fd);
		if (error != USB_LC_STAT_INTR_BUF_FULL) {
			/* real read error */
			fprintf(stderr, "usbups: Error reading 
			   interrupt IN pipe: %sn", error);
			exit(1);
		}
			
	}
	else {
		printf("usbups: Reading Interrupt IN endpoint data 
			successful: %d bytesn", count);	
		
		for (i=0; i<count; i++) {
			printf("Byte[%d] = %xn", i, 
				input_report[i]);
		}
				
		if (input_report[0]==2){	
		/* Battery System Information */
  			if (input_report[1] & 0x01) { 
			   /* ACPresent */
			   DPRINT("usbups: ACPresentn");
			}
			if (input_report[1] & 0x02) { 
			   /* Charging */
			   DPRINT("usbups: Battery Chargingn");
			}	
			if (input_report[1] & 0x04) { 
			   /* Discharging */
			   DPRINT("usbups: Battery
				 Dischargingn");
			}
			if (input_report[1] & 0x08) { 
			   /* BelowRemainingCapacityLimit */
			}
			if (input_report[1] & 0x10) { 
			   /* Need Replacement */
			}
			if (input_report[1] & 0x20) { 
			   /* Power Device: Good */
			}
			if (input_report[1] & 0x40) { 
			   /* Power Device: ShutdownImminent */
			}
			if (input_report[1] & 0x80) { 
			   /* Power Device: Overload */
			}
				
		}
	} // end else
	sleep(5);
	} // end while


	/* Close Interrupt IN pipe */
	close(iinstat_fd);
	close(iin_fd);
	DPRINT("usbups: Interrupt IN pipe closedn");

	
	/* Close default control and default control status 
	 * endpoints. 
	 */
	close(ctrlstat_fd);
	close(ctrl_fd);
	DPRINT("usbups: Default control and control status 
		closedn");
	
	/* Close device status node */
	DPRINT("usbups: Device status node closedn");

Appendix C: Ugen Man Page

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.