USB Drivers
This chapter describes how to write a client USB device driver using the USBA 2.0 framework for the illumos environment. This chapter discusses the following topics:
20.1. USB in the illumos Environment
The illumos USB architecture includes the USBA 2.0 framework and USB client drivers.
20.1.1. USBA 2.0 Framework
The USBA 2.0 framework is a service layer that presents an abstract view of USB devices to USBA-compliant client drivers. The framework enables USBA-compliant client drivers to manage their USB devices. The USBA 2.0 framework supports the USB 2.0 specification except for high speed isochronous pipes. For information on the USB 2.0 specification, see http://www.usb.org/.
The USBA 2.0 framework is platform-independent. The illumos USB architecture is shown in the following figure. The USBA 2.0 framework is the USBA layer in the figure. This layer interfaces through a hardware-independent host controller driver interface to hardware-specific host controller drivers. The host controller drivers access the USB physical devices through the host controllers they manage.

20.1.2. USB Client Drivers
The USBA 2.0 framework is not a device driver itself. This chapter describes the
client drivers shown in illumos USB Architecture and Driver and Controller Interfaces. The client drivers interact
with various kinds of USB devices such as mass storage devices, printers,
and human interface devices. The hub driver is a client driver that is also
a nexus driver. The hub driver enumerates devices on its ports and creates devinfo
nodes for those devices and then attaches the client drivers.
This chapter does not describe how to write a hub driver.
USB drivers have the same structure as any other illumos driver. USB drivers can be block drivers, character drivers, or STREAMS drivers. USB drivers follow the calling conventions and use the data structures and routines described in the illumos section 9 man pages. See Intro(9E), Intro(9F), and Intro(9S).
The difference between USB drivers and other illumos drivers is that USB drivers call USBA 2.0 framework functions to access the device instead of directly accessing the device. The USBA 2.0 framework supplements the standard illumos DDI routines. See the following figure.

Driver and Controller Interfaces shows interfaces in more detail than illumos USB Architecture does. Driver and Controller Interfaces shows that the USBA is a kernel subsystem into which a client driver can call, just as a client driver can call DDI functions.
Not all systems have all of the host controller interfaces shown in Driver and Controller Interfaces. OHCI (Open Host Controller Interface) hardware is most prevalent on SPARC systems and third-party USB PCI cards. UHCI (Universal Host Controller Interface) hardware is most prevalent on x86 systems. However, both OHCI and UHCI hardware can be used on any system. When EHCI (Enhanced Host Controller Interface) hardware is present, the EHCI hardware is on the same card and shares the same ports with either OHCI or UHCI.
The host controllers, host controller drivers, and HCDI make up a transport layer that is commanded by the USBA. You cannot directly call into the OHCI, EHCI, or UHCI. You call into them indirectly through the platform-independent USBA interface.
20.2. Binding Client Drivers
This section discusses binding a driver to a device. It discusses compatible device names for devices with single interfaces and devices with multiple interfaces.
20.2.1. How USB Devices Appear to the System
A USB device can support multiple configurations. Only one configuration is active at any given time. The active configuration is called the current configuration.
A configuration can have more than one interface, possibly with intervening interface-associations that group two or more interfaces for a function. All interfaces of a configuration are active simultaneously. Different interfaces might be operated by different device drivers.
An interface can represent itself to the host system in different ways by using alternate settings. Only one alternate setting is active for any given interface.
Each alternate setting provides device access through endpoints. Each endpoint has a specific purpose. The host system communicates with the device by establishing a communication channel to an endpoint. This communication channel is called a pipe.
20.2.2. USB Devices and the illumos Device Tree
If a USB device has one configuration, one interface, and device class zero, the device is represented as a single device node. If a USB device has multiple interfaces, the device is represented as a hierarchical device structure. In a hierarchical device structure, the device node for each interface is a child of the top-level device node. An example of a device with multiple interfaces is an audio device that presents simultaneously to the host computer both an audio control interface and an audio streaming interface. The audio control interface and the audio streaming interface each could be controlled by its own driver.
20.2.3. Compatible Device Names
The illumos software builds an ordered list of compatible device names for USB binding based on identification information kept within each device. This information includes device class, subclass, vendor ID, product ID, revision, and protocol. See http://www.usb.org/ for a list of USB classes and subclasses.
This name hierarchy enables binding to a general driver if a more device-specific
driver is not available. An example of a general driver is a class-specific
driver. Device names that begin with usbif
designate single
interface devices. See USB Mouse Compatible Device Names for
examples. The USBA 2.0 framework defines all compatible names for a device. Use the
prtconf
command to display these device names, as shown in Compatible Device Names Shown by the Print Configuration
Command.
The following example shows an example of compatible device names for a USB mouse device. This mouse device represents a combined node entirely operated by a single driver. The USBA 2.0 framework gives this device node the names shown in the example, in the order shown.
1. 'usb430,100.102' Vendor 430, product 100, revision 102
2. 'usb430,100' Vendor 430, product 100
3. 'usbif430,class3.1.2' Vendor 430, class 3, subclass 1, protocol 2
4. 'usbif430,class3.1' Vendor 430, class 3, subclass 1
5. 'usbif430,class3' Vendor 430, class 3
6. 'usbif,class3.1.2' Class 3, subclass 1, protocol 2
7. 'usbif,class3.1' Class 3, subclass 1
8. 'usbif,class3' Class 3
Note that the
names in the above example progress from the most specific to the most general.
Entry 1 binds only to a particular revision of a specific product from a particular
vendor. Entries 3, 4, and 5 are for class 3 devices manufactured by vendor
430. Entries 6, 7, and 8 are for class 3 devices from any vendor. The
binding process looks for a match on the name from the top name down. To bind,
drivers must be added to the system with an alias that matches one of these
names. To get a list of compatible device names to which to bind when you
add your driver, check the compatible
property of the device
in the output from the prtconf
-vp
command.
The following example shows compatible property lists for a keyboard
and a mouse. Use the prtconf
-D
command
to display the bound driver.
# prtconf -vD | grep compatible compatible: 'usb430,5.200' + 'usb430,5' + 'usbif430,class3.1.1' + 'usbif430,class3.1' + 'usbif430,class3' + 'usbif,class3.1.1' + 'usbif,class3.1' + 'usbif,class3' compatible: 'usb2222,2071.200' + 'usb2222,2071' + 'usbif2222,class3.1.2' + 'usbif2222,class3.1' + 'usbif2222,class3' + 'usbif,class3.1.2' + 'usbif,class3.1' + 'usbif,class3'
Use the most specific name you can to more accurately identify a driver for a device or group of devices. To bind drivers written for a specific revision of a specific product, use the most specific name match possible. For example, if you have a USB mouse driver written by vendor 430 for revision 102 of their product 100, use the following command to add that driver to the system:
add_drv -n -i '"usb430,100.102"' specific_mouse_driver
To add a driver written for any USB mouse (class 3, subclass 1, protocol 2) from vendor 430, use the following command:
add_drv -n -i '"usbif430,class3.1.2"' more_generic_mouse_driver
If you install both of these drivers and then connect a compatible device,
the system binds the correct driver to the connected device. For example,
if you install both of these drivers and then connect a vendor 430, model
100, revision 102 device, this device is bound to specific_mouse_driver
.
If you connect a vendor 430, model 98 device, this device is bound to more_generic_mouse_driver
. If you connect a mouse from another vendor, this device also is
bound to more_generic_mouse_driver
. If multiple drivers
are available for a specific device, the driver binding framework selects
the driver with the first matching compatible name in the compatible names
list.
20.2.4. Devices With Multiple Interfaces
Composite devices are devices that support multiple
interfaces. Composite devices have a list of compatible names for each interface.
This compatible names list ensures that the best available driver is bound
to the interface. The most general multiple interface entry is usb,device
.
For a USB audio composite device, the compatible names are as follows:
1. 'usb471,101.100' Vendor 471, product 101, revision 100
2. 'usb471,101' Vendor 471, product 101
3. 'usb,device' Generic USB device
The name usb,device
is a compatible
name that represents any whole USB device. The usb_mid(7D) driver (USB multiple-interface
driver) binds to the usb,device
device node if no other
driver has claimed the whole device. The usb_mid
driver
creates a child device node for each interface of the physical device. The usb_mid
driver also generates a set of compatible names for each
interface. Each of these generated compatible names begins with usbif
.
The system then uses these generated compatible names to find the best driver
for each interface. In this way, different interfaces of one physical device
can be bound to different drivers.
For example, the usb_mid
driver binds to a multiple-interface
audio device through the usb,device
node name of that audio
device. The usb_mid
driver then creates interface-specific
device nodes. Each of these interface-specific device nodes has its own compatible
name list. For an audio control interface node, the compatible name list might
look like the list shown in the following example.
1. 'usbif471,101.100.config1.0' Vend 471, prod 101, rev 100, cnfg 1, iface 0
2. 'usbif471,101.config1.0' Vend 471, product 101, config 1, interface 0
3. 'usbif471,class1.1.0' Vend 471, class 1, subclass 1, protocol 0
4. 'usbif471,class1.1' Vend 471, class 1, subclass 1
5. 'usbif471,class1' Vend 471, class 1
6. 'usbif,class1.1.0' Class 1, subclass 1, protocol 0
7. 'usbif,class1.1' Class 1, subclass 1
8. 'usbif,class1' Class 1
Use the following command to bind a vendor-specific, device-specific
client driver named vendor_model_audio_usb
to the vendor-specific,
device-specific configuration 1, interface 0 interface compatible name shown
in USB Audio Compatible Device Names.
add_drv -n -i '"usbif471,101.config1.0"' vendor_model_audio_usb
Use the following command to bind a class driver named audio_class_usb_if_driver
to the more general class 1, subclass 1 interface compatible name shown in USB Audio Compatible Device Names:
add_drv -n -i '"usbif,class1.1"' audio_class_usb_if_driver
Use the prtconf
-D
command to show a list of devices and their drivers. In the following
example, the prtconf
-D
command shows that
the usb_mid
driver manages the audio
device.
The usb_mid
driver is splitting the audio
device
into interfaces. Each interface is indented under the audio
device
name. For each interface shown in the indented list, the prtconf
-D
command shows which driver manages the interface.
audio, instance #0 (driver name: usb_mid)
sound-control, instance #2 (driver name: usb_ac)
sound, instance #2 (driver name: usb_as)
input, instance #8 (driver name: hid)
20.2.5. Devices With Interface-Association Descriptors
If the device includes an interface-association descriptor, the device tree can be parsed at the following three levels:
-
The usb_mid(7D) USB multi-interface driver binds to device level nodes of a composite device if no vendor or class-specific driver is available.
-
A client driver is bound to the interface association nodes.
-
The usb_ia(7D) USB interface association driver is bound by default if no client driver is found. Then client drivers can be bound to the interface level of this interface association.
The usb_mid
driver creates an ia
(interface
association) node for each ia
. The compatible names of ia
nodes generally begin with usbia
. The name usb,ia
is a compatible name that represents any ia
as
the tail of the compatible names. The usb_ia
driver is
bound to an ia
node if no other driver has claimed this ia
. The usb_ia
driver creates a child node for
each interface. An interface node as the child node of an ia
node
has the same properties with an interface node as the child of a device node.
1. 'usbia46d,8c9.5.config1.0' vend 46d, prod 8c9, rev 5, cnfg 1, first_if_in_ia 0
2. 'usbia46d,8c9.config1.0' vend 46d, prod 8c9, cnfg 1, first_if_in_ia 0
3. 'usbia46d,classe.3.0' vend 46d, class e, subclass 3, protocol 0
4. 'usbia46d,classe.3' vend 46d, class e, subclass 3
5. 'usbia46d,classe' vend 46d, class e
6. 'usbia,classe.3.0' class e, subclass 3, protocol 0
7. 'usbia,classe.3' class e, subclass 3
8. 'usbia,classe' class e
9. 'usb,ia' by default
Use the following command to bind a vendor-specific, device-specific
client driver named vendor_model_video_usb
to the vendor-specific,
device-specific configuration 1, first_if_in_ia 0 compatible name shown in USB Video Interface Association Compatible Names:
add_drv -n -i '"usbia46d,8c9.config1.0"' vendor_model_video_usb
Use the following command to bind a class driver named video_class_usb_ia_driver
to the more general class e compatible names shown in USB Video Interface Association Compatible Names:
add_drv -n -i '"usbia,classee"' video_class_usb_ia_driver
In the following example, the prtconf
-D
command
shows a device tree of a webcam with ia
of video
and audio
. The usb_mid
driver manages the device
and creates two ia
respectively for video and audio. A
video driver usbvc
is bound to the video ia
,
and audio drivers are bound to the interface of the audio ia
.
miscellaneous, instance #28 (driver name: usb_mid)
video, instance #24 (driver name: usbvc)
audio, instance #30 (driver name: usb_ia)
sound-control, instance #38 (driver name: usb_ac)
sound, instance #47 (driver name: usb_as)
20.2.6. Checking Device Driver Bindings
The file /etc/driver_aliases contains entries for the bindings that already exist on a system. Each line of the /etc/driver_aliases file shows a driver name, followed by a space, followed by a device name. Use this file to check existing device driver bindings.
Do not edit the /etc/driver_aliases file manually. Use the add_drv(1M) command to establish a binding. Use the update_drv(1M) command to change a binding.
20.3. Basic Device Access
This section describes how to access a USB device and how to register a client driver. This section also discusses the descriptor tree.
20.3.1. Before the Client Driver Is Attached
The following events take place before the client driver is attached:
-
The PROM (OBP/BIOS) and USBA framework gain access to the device before any client driver is attached.
-
The hub driver probes devices on each of its hub's ports for identity and configuration.
-
The default control pipe to each device is opened, and each device is probed for its device descriptor.
-
Compatible names properties are constructed for each device, using the device and interface descriptors.
The compatible names properties define different parts of the device that can be individually bound to client drivers. Client drivers can bind either to the entire device or to just one interface. See Binding Client Drivers.
20.3.2. The Descriptor Tree
Parsing descriptors involves aligning structure members at natural boundaries and converting the structure members to the endianness of the host CPU. Parsed standard USB configuration descriptors, interface descriptors, and endpoint descriptors are available to the client driver in the form of a hierarchical tree for each configuration. Any raw class-specific or vendor-specific descriptor information also is available to the client driver in the same hierarchical tree.
Call the usb_get_dev_data(9F) function to retrieve the hierarchical descriptor tree. The “SEE ALSO” section of the usb_get_dev_data(9F) man page lists the man pages for each standard USB descriptor. Use the usb_parse_data(9F) function to parse raw descriptor information.
A descriptor tree for a device with two configurations might look like the tree shown in the following figure.

The dev_cfg
array shown in the above figure contains
nodes that correspond to configurations. Each node contains the following
information:
-
A parsed configuration descriptor
-
A pointer to an array of descriptors that correspond to the interfaces of that configuration
-
A pointer to an array of class-specific or vendor-specific raw data, if any exists
The node that represents the second interface of the second indexed
configuration is at dev_cfg[1].cfg_if[1]
in the diagram.
That node contains an array of nodes that represent the alternate settings
for that interface. The hierarchy of USB descriptors propagates through the
tree. ASCII strings from string descriptor data are attached where the USB
specification says these strings exist.
The array of configurations is non-sparse and is indexed by the configuration
index. The first valid configuration (configuration 1) is dev_cfg[0]
. Interfaces and alternate settings have indices that align with
their numbers. Endpoints of each alternate setting are indexed consecutively.
The first endpoint of each alternate setting is at index 0.
This numbering scheme makes the tree easy to traverse. For example, the raw descriptor data of endpoint index 0, alternate 0, interface 1, configuration index 1 is at the node defined by the following path:
dev_cfg[1].cfg_if[1].if_alt[0].altif_ep[0].ep_descr
An alternative to using the descriptor tree directly is using the usb_lookup_ep_data(9F) function. The usb_lookup_ep_data(9F) function takes as arguments the interface, alternate, which endpoint, endpoint type, and direction. You can use the usb_lookup_ep_data(9F) function to traverse the descriptor tree to get a particular endpoint. See the usb_get_dev_data(9F) man page for more information.
20.3.3. Registering Drivers to Gain Device Access
Two of the first calls into the USBA 2.0 framework by a client driver are calls to the usb_client_attach(9F) function and the usb_get_dev_data(9F) function. These two calls come from the client driver's attach(9E) entry point. You must call the usb_client_attach(9F) function before you call the usb_get_dev_data(9F) function.
The usb_client_attach(9F) function registers a client driver with the USBA 2.0 framework. The usb_client_attach(9F) function enforces versioning. All client driver source files must start with the following lines:
#define USBDRV_MAJOR_VER 2 #define USBDRV_MINOR_VER minor-version #include <sys/usb/usba.h>
The value of minor-version must be less than
or equal to USBA_MINOR_VER
. The symbol USBA_MINOR_VER
is defined in the <sys/usb/usbai.h>
header
file. The <sys/usb/usbai.h> header file is included
by the <sys/usb/usba.h> header file.
USBDRV_VERSION
is a macro that generates the version
number from USBDRV_MAJOR_VERSION
and USBDRV_MINOR_VERSION
. The second argument to usb_client_attach
must
be USBDRV_VERSION
. The usb_client_attach
function
fails if the second argument is not USBDRV_VERSION
or if USBDRV_VERSION
reflects an invalid version. This restriction ensures
programming interface compatibility.
The usb_get_dev_data
function
returns information that is required for proper USB device management. For
example, the usb_get_dev_data
function returns the following
information:
-
The default control pipe
-
The iblock_cookie to use in mutex initializations (see mutex_init(9F))
-
The parsed device descriptor
-
ID strings
-
The tree hierarchy as described in The Descriptor Tree
The call to the usb_get_dev_data
function is mandatory.
Calling usb_get_dev_data
is the only way to retrieve
the default control pipe and retrieve the iblock_cookie required
for mutex initialization.
After calling usb_get_dev_data
,
the client driver's attach(9E) routine typically copies the desired
descriptors and data from the descriptor tree to the driver's soft state.
Endpoint descriptors copied to the soft state are used later to open pipes
to those endpoints. The attach(9E) routine usually calls usb_free_descr_tree(9F) to free the descriptor tree after copying descriptors.
Alternatively, you might choose to keep the descriptor tree and not copy the
descriptors.
Specify one of the following three parse levels to the usb_get_dev_data(9F) function to request the breadth of the descriptor tree you want returned. You need greater tree breadth if your driver needs to bind to more of the device.
-
USB_PARSE_LVL_IF
. If your client driver binds to a specific interface, the driver needs the descriptors for only that interface. SpecifyUSB_PARSE_LVL_IF
for the parse level in theusb_get_dev_data
call to retrieve only those descriptors. -
USB_PARSE_LVL_CFG
. If your client driver binds to the whole device, specifyUSB_PARSE_LVL_CFG
to retrieve all descriptors of the current configuration. -
USB_PARSE_LVL_ALL
. SpecifyUSB_PARSE_LVL_ALL
to retrieve all descriptors of all configurations. For example, you need this greatest tree breadth to use usb_print_descr_tree(9F) to print a descriptor dump of all configurations of a device.
The client driver's detach(9E)
routine must call the usb_free_dev_data(9F)
function to release all resources allocated by the usb_get_dev_data
function. The
usb_free_dev_data
function accepts handles where the descriptor tree has already
been freed with the usb_free_descr_tree
function. The client driver's detach
routine
also must call the usb_client_detach(9F) function to release all resources
allocated by the usb_client_attach(9F) function.
20.4. Device Communication
USB devices operate by passing requests through communication channels called pipes. Pipes must be open before you can submit requests. Pipes also can be flushed, queried, and closed. This section discusses pipes, data transfers and callbacks, and data requests.
20.4.1. USB Endpoints
The four kinds of pipes that communicate with the four kinds of USB endpoints are:
-
Control. Control pipes are used primarily to send commands and retrieve status. Control pipes are intended for non-periodic, host-initiated request and response communication of small-sized structured data. Control pipes are bidirectional. The default pipe is a control pipe. See The Default Pipe.
-
Bulk. Bulk pipes are used primarily for data transfer. Bulk pipes offer reliable transportation of large amounts of data. Bulk pipes do not necessarily deliver the data in a timely manner. Bulk pipes are unidirectional.
-
Interrupt. Interrupt pipes offer timely, reliable communication of small amounts of unstructured data. Periodic polling often is started on interrupt-IN pipes. Interrupt-IN pipes return data to the host when the data becomes present on the device. Some devices have interrupt-OUT pipes. Interrupt-OUT pipes transfer data to the device with the same timely, reliable “interrupt pipe” characteristics of interrupt-IN pipes. Interrupt pipes are unidirectional.
-
Isochronous. Isochronous pipes offer a channel for transferring constant-rate, time-relevant data, such as for audio devices. Data is not retried on error. Isochronous pipes are unidirectional.
See Chapter 5 of the USB 2.0 specification or see Requests for more information on the transfer types that correspond to these endpoints.
20.4.2. The Default Pipe
Each USB device has a special control endpoint called the default endpoint. Its communication channel is called the default pipe. Most, if not all, device setup is done through this pipe. Many USB devices have this pipe as their only control pipe.
The usb_get_dev_data(9F) function provides the default control pipe to the client driver. This pipe is pre-opened to accommodate any special setup needed before opening other pipes. This default control pipe is special in the following ways:
-
This pipe is shared. Drivers that are operating other interfaces of the same device use the same default control pipe. The USBA 2.0 framework arbitrates this pipe among the different drivers.
-
This pipe cannot be opened, closed, or reset by the client driver. This restriction exists because the pipe is shared.
-
The pipe is autocleared on an exception.
Other pipes, including other control pipes, must be opened explicitly and are exclusive-open only.
20.4.3. Pipe States
Pipes are in one of the following states:
-
USB_PIPE_STATE_IDLE
-
All control and bulk pipes, interrupt-OUT pipes, and isochronous-OUT pipes: No request is in progress.
-
Interrupt-IN and isochronous-IN pipes: No polling is in progress.
-
-
USB_PIPE_STATE_ACTIVE
-
All control and bulk pipes, interrupt-OUT pipes, and isochronous-OUT pipes: The pipe is transferring data or an I/O request is active.
-
Interrupt-IN and isochronous-IN pipes: Polling is active.
-
-
USB_PIPE_STATE_ERROR
. An error occurred. If this pipe is not the default pipe and if autoclearing is not enabled, then the client driver must call the usb_pipe_reset(9F) function. -
USB_PIPE_STATE_CLOSING
. The pipe is being closed. -
USB_PIPE_STATE_CLOSED
. The pipe is closed.
Call the usb_pipe_get_state(9F) function to retrieve the state of a pipe.
20.4.4. Opening Pipes
To open a pipe, pass to the usb_pipe_open(9F) function the endpoint descriptor that corresponds to the pipe you want to open. Use the usb_get_dev_data(9F) and usb_lookup_ep_data(9F) functions to retrieve the endpoint descriptor from the descriptor tree. The usb_pipe_open(9F) function returns a handle to the pipe.
You must specify a pipe policy when you open a pipe. The pipe policy contains an estimate of the number of concurrent asynchronous operations that require separate threads that will be needed for this pipe. An estimate of the number of threads is the number of parallel operations that could occur during a callback. The value of this estimate must be at least 2. See the usb_pipe_open(9F) man page for more information on pipe policy.
20.4.5. Closing Pipes
The driver must use the usb_pipe_close(9F) function to close pipes other than the default pipe. The usb_pipe_close(9F) function enables all remaining requests in the pipe to complete. The function then allows one second for all callbacks of those requests to complete.
20.4.6. Data Transfer
For all pipe types, the programming model is as follows:
-
Allocate a request.
-
Submit the request using one of the pipe transfer functions. See the usb_pipe_bulk_xfer(9F), usb_pipe_ctrl_xfer(9F), usb_pipe_intr_xfer(9F), and usb_pipe_isoc_xfer(9F) man pages.
-
Wait for completion notification.
-
Free the request.
See Requests for more information on requests. The following sections describe the features of different request types.
Synchronous and Asynchronous Transfers and Callbacks
Transfers are either synchronous or asynchronous. Synchronous transfers
block until they complete. Asynchronous transfers callback into the client
driver when they complete. Most transfer functions called with the
USB_FLAGS_SLEEP
flag set in the flags
argument are synchronous.
Continuous transfers such as polling and isochronous transfers cannot
be synchronous. Calls to transfer functions for continuous transfers made
with the USB_FLAGS_SLEEP
flag set block only to wait for
resources before the transfer begins.
Synchronous transfers are the most simple transfers to set up because synchronous transfers do not require any callback functions. Synchronous transfer functions return a transfer start status, even though synchronous transfer functions block until the transfer is completed. Upon completion, you can find additional information about the transfer status in the completion reason field and callback flags field of the request. Completion reasons and callback flags fields are discussed below.
If the USB_FLAGS_SLEEP
flag is not specified in the
flags argument, that transfer operation is asynchronous. The
exception to this rule are isochronous transfers. Asynchronous transfer operations
set up and start the transfer, and then return before the transfer is complete.
Asynchronous transfer operations return a transfer start status. The client
driver receives transfer completion status through callback handlers.
Callback handlers are functions that are called when asynchronous transfers complete. Do not set up an asynchronous transfer without callbacks. The two types of callback handlers are normal completion handlers and exception handlers. You can specify one handler to be called in both of these cases.
-
Normal completion. A normal completion callback handler is called to notify of a normally completed transfer.
-
Exception. An exception callback handler is called to notify of an abnormally completed transfer and to process its errors.
Both completion handlers and exception handlers receive the transfer's
request as an argument. Exception handlers use the completion reason and callback
status in the request to find out what happened. The completion reason (usb_cr_t
) indicates how the original transaction completed. For
example, a completion reason of USB_CR_TIMEOUT
indicates
that the transfer timed out. As another example, if a USB device is removed
while in use, client drivers might receive USB_CR_DEV_NOT_RESP
as
the completion reason on their outstanding requests. The callback status (usb_cb_flags_t
) indicates what the USBA framework did to remedy
the situation. For example, a callback status of USB_CB_STALL_CLEARED
indicates
that the USBA framework cleared a functional stall condition. See the usb_completion_reason(9S) man page for more information on completion reasons.
See the usb_callback_flags(9S) man page for more information on callback status flags.
The context of the callback and the policy of the pipe on which the requests are run limit what you can do in the callback.
-
Callback context. Most callbacks execute in kernel context and usually can block. Some callbacks execute in interrupt context and cannot block. The
USB_CB_INTR_CONTEXT
flag is set in the callback flags to denote interrupt context. See the usb_callback_flags(9S) man page for more information on callback context and details on blocking. -
Pipe policy. The pipe policy's hint on concurrent asynchronous operations limits the number of operations that can be run in parallel, including those executed from a callback handler. Blocking on a synchronous operation counts as one operation. See the usb_pipe_open(9F) man page for more information on pipe policy.
Requests
This section discusses request structures and allocating and deallocating different types of requests.
Request Allocation and Deallocation
Requests are implemented as initialized request structures. Each different endpoint type takes a different type of request. Each type of request has a different request structure type. The following table shows the structure type for each type of request. This table also lists the functions to use to allocate and free each type of structure.
Pipe or Endpoint Type |
Request Structure |
Request Structure Allocation Function |
Request Structure Free Function |
---|---|---|---|
Control |
|
||
Bulk |
|
||
Interrupt |
|
||
Isochronous |
|
The following table lists the transfer functions that you can use for each type of request.
Pipe or Endpoint Type |
Transfer Functions |
---|---|
Control |
|
Bulk |
|
Interrupt |
|
Isochronous |
Use the following procedure to allocate and deallocate a request:
-
Use the appropriate allocation function to allocate a request structure for the type of request you need. The man pages for the request structure allocation functions are listed in Request Initialization.
-
Initialize any fields you need in the structure. See Request Features and Fields or the appropriate request structure man page for more information. The man pages for the request structures are listed in Request Initialization.
-
When the data transfer is complete, use the appropriate free function to free the request structure. The man pages for the request structure free functions are listed in Request Initialization.
Request Features and Fields
Data for all requests is passed in message blocks so that the data is
handled uniformly whether the driver is a STREAMS, character, or block driver.
The message block type, mblk_t
, is described in the mblk(9S) man page. The DDI offers
several routines for manipulating message blocks. Examples include allocb(9F) and freemsg(9F). To learn
about other routines for manipulating message blocks, see the “SEE ALSO”
sections of the allocb(9F) and freemsg(9F) man pages. Also see the STREAMS Programming Guide.
The following request fields are included in all transfer types. In each field name, the possible values for xxxx are: ctrl, bulk, intr, or isoc.
- xxxx_client_private
-
This field value is a pointer that is intended for internal data to be passed around the client driver along with the request. This pointer is not used to transfer data to the device.
- xxxx_attributes
-
This field value is a set of transfer attributes. While this field is common to all request structures, the initialization of this field is somewhat different for each transfer type. See the appropriate request structure man page for more information. These man pages are listed in Request Initialization. See also the usb_request_attributes(9S) man page.
- xxxx_cb
-
This field value is a callback function for normal transfer completion. This function is called when an asynchronous transfer completes without error.
- xxxx_exc_cb
-
This field value is a callback function for error handling. This function is called only when asynchronous transfers complete with errors.
- xxxx_completion_reason
-
This field holds the completion status of the transfer itself. If an error occurred, this field shows what went wrong. See the usb_completion_reason(9S) man page for more information. This field is updated by the USBA 2.0 framework.
- xxxx_cb_flags
-
This field lists the recovery actions that were taken by the USBA 2.0 framework before calling the callback handler. The
USB_CB_INTR_CONTEXT
flag indicates whether a callback is running in interrupt context. See the usb_callback_flags(9S) man page for more information. This field is updated by the USBA 2.0 framework.
The following sections describe the request fields that are different for the four different transfer types. These sections describe how to initialize these structure fields. These sections also describe the restrictions on various combinations of attributes and parameters.
Control Requests
Use control requests to initiate message transfers down a control pipe. You can set up transfers manually, as described below. You can also set up and send synchronous transfers using the usb_pipe_ctrl_xfer_wait(9F) wrapper function.
The client driver must initialize the ctrl_bmRequestType, ctrl_bRequest, ctrl_wValue, ctrl_wIndex, and ctrl_wLength fields as described in the USB 2.0 specification.
The ctrl_data field of the request must be initialized to point to a data buffer. The usb_alloc_ctrl_req(9F) function initializes this field when you pass a positive value as the buffer len. The buffer must, of course, be initialized for any outbound transfers. In all cases, the client driver must free the request when the transfer is complete.
Multiple control requests can be queued. Queued requests can be a combination of synchronous and asynchronous requests.
The ctrl_timeout field defines the maximum wait time for the request to be processed, excluding wait time on the queue. This field applies to both synchronous and asynchronous requests. The ctrl_timeout field is specified in seconds.
The ctrl_exc_cb field accepts the address
of a function to call if an exception occurs. The arguments of this exception
handler are specified in the usb_ctrl_request(9S) man page. The second argument
of the exception handler is the usb_ctrl_req_t
structure.
Passing the request structure as an argument allows the exception handler
to check the ctrl_completion_reason and ctrl_cb_flags fields of the request to determine the best recovery action.
The USB_ATTRS_ONE_XFER
and USB_ATTRS_ISOC_*
flags
are invalid attributes for all control requests. The USB_ATTRS_SHORT_XFER_OK
flag is valid only for host-bound requests.
Bulk Requests
Use bulk requests to send data that is not time-critical. Bulk requests can take several USB frames to complete, depending on overall bus load.
All requests must receive an initialized message block. See the mblk(9S) man page for a description
of the mblk_t
message block type. This message block either
supplies the data or stores the data, depending on the transfer direction.
Refer to the usb_bulk_request(9S) man page for more details.
The USB_ATTRS_ONE_XFER
and USB_ATTRS_ISOC_*
flags
are invalid attributes for all bulk requests. The USB_ATTRS_SHORT_XFER_OK
flag is valid only for host-bound requests.
The usb_pipe_get_max_bulk_transfer_size(9F) function specifies the maximum number of bytes per request. The value retrieved can be the maximum value used in the client driver's minphys(9F) routine.
Multiple bulk requests can be queued.
Interrupt Requests
Interrupt requests typically are for periodic inbound data. Interrupt requests periodically poll the device for data. However, the USBA 2.0 framework supports one-time inbound interrupt data requests, as well as outbound interrupt data requests. All interrupt requests can take advantage of the USB interrupt transfer features of timeliness and retry.
The USB_ATTRS_ISOC_*
flags are invalid attributes
for all interrupt requests. The USB_ATTRS_SHORT_XFER_OK
and USB_ATTRS_ONE_XFER
flags are valid only for host-bound requests.
Only one-time polls can be done as synchronous interrupt transfers.
Specifying the USB_ATTRS_ONE_XFER
attribute in the request
results in a one-time poll.
Periodic polling is started as an asynchronous interrupt transfer. An
original interrupt request is passed to usb_pipe_intr_xfer(9F). When polling
finds new data to return, a new usb_intr_req_t
structure
is cloned from the original and is populated with an initialized data block.
When allocating the request, specify zero for the len argument
to the usb_alloc_intr_req(9F) function. The len argument
is zero because the USBA 2.0 framework allocates and fills in a new request with each
callback. After you allocate the request structure, fill in the intr_len field to specify the number of bytes you want the framework
to allocate with each poll. Data beyond intr_len bytes
is not returned.
The client driver must free each request it receives. If the message
block is sent upstream, decouple the message block from the request before
you send the message block upstream. To decouple the message block from the
request, set the data pointer of the request to NULL
. Setting
the data pointer of the request to NULL
prevents the message
block from being freed when the request is deallocated.
Call the usb_pipe_stop_intr_polling(9F) function to cancel periodic polling.
When polling is stopped or the pipe is closed, the original request structure
is returned through an exception callback. This returned request structure
has its completion reason set to USB_CR_STOPPED_POLLING
.
Do not start polling while polling is already in progress. Do not start polling while a call to usb_pipe_stop_intr_polling(9F) is in progress.
Isochronous Requests
Isochronous requests are for streaming, constant-rate, time-relevant data. Retries are not made on errors. Isochronous requests have the following request-specific fields:
- isoc_frame_no
-
Specify this field when the overall transfer must start from a specific frame number. The value of this field must be greater than the current frame number. Use usb_get_current_frame_number(9F) to find the current frame number. Note that the current frame number is a moving target. For low-speed and full-speed buses, the current frame is new each millisecond. For high-speed buses, the current frame is new each 0.125 millisecond. Set the
USB_ATTR_ISOC_START_FRAME
attribute so that the isoc_frame_no field is recognized.To ignore this frame number field and start as soon as possible, set the
USB_ATTR_ISOC_XFER_ASAP
flag. - isoc_pkts_count
-
This field is the number of packets in the request. This value is bounded by the value returned by the usb_get_max_pkts_per_isoc_request(9F) function and by the size of the isoc_pkt_descr array (see below). The number of bytes transferable with this request is equal to the product of this isoc_pkts_count value and the wMaxPacketSize value of the endpoint.
- isoc_pkts_length
-
This field is the sum of the lengths of all packets of the request. This value is set by the initiator. This value should be set to zero so that the sum of isoc_pkts_length in the isoc_pkt_descr list will be used automatically and no check will be applied to this element.
- isoc_error_count
-
This field is the number of packets that completed with errors. This value is set by the USBA 2.0 framework.
- isoc_pkt_descr
-
This field points to an array of packet descriptors that define how much data to transfer per packet. For an outgoing request, this value defines a private queue of sub-requests to process. For an incoming request, this value describes how the data arrived in pieces. The client driver allocates these descriptors for outgoing requests. The framework allocates and initializes these descriptors for incoming requests. Descriptors in this array contain framework-initialized fields that hold the number of bytes actually transferred and the status of the transfer. See the usb_isoc_request(9S) man page for more details.
All requests must receive an initialized message block. This message
block either supplies the data or stores the data. See the mblk(9S) man page for a description
of the mblk_t
message block type.
The USB_ATTR_ONE_XFER
flag is an illegal attribute
because the system decides how to vary the amounts of data through available
packets. The USB_ATTR_SHORT_XFER_OK
flag is valid only
on host-bound data.
The usb_pipe_isoc_xfer(9F) function
makes all isochronous transfers asynchronous, regardless of whether the USB_FLAGS_SLEEP
flag is set. All isochronous input requests start
polling.
Call the usb_pipe_stop_isoc_polling(9F) function
to cancel periodic polling. When polling is stopped or the pipe is closed,
the original request structure is returned through an exception callback.
This returned request structure has its completion reason set to USB_CR_STOPPED_POLLING
.
Polling continues until one of the following events occurs:
-
A usb_pipe_stop_isoc_polling(9F) call is received.
-
A device disconnect is reported through an exception callback.
-
A usb_pipe_close(9F) call is received.
20.4.7. Flushing Pipes
You might need to clean up a pipe after errors, or you might want to wait for a pipe to clear. Use one of the following methods to flush or clear pipes:
-
The usb_pipe_reset(9F) function resets the pipe and flushes all of its requests. Do this for pipes that are in an error state if autoclearing is not enabled on those pipes. Use usb_pipe_get_state(9F) to determine the state of a pipe.
-
The usb_pipe_drain_reqs(9F) function blocks waiting for all pending requests to complete before continuing. This function can wait indefinitely, or it can time-out after a specified period of time. The usb_pipe_drain_reqs(9F) function neither closes nor flushes the pipe.
20.5. Device State Management
Managing a USB device includes accounting for hotplugging, system power
management (checkpoint and resume), and device power management. All client
drivers should implement the basic state machine shown in the following figure.
For more information, see /usr/include/sys/usb/usbai.h
.

This state machine and its four states can be augmented with driver-specific
states. Device states 0x80
to 0xff
can
be defined and used only by client drivers.
20.5.1. Hotplugging USB Devices
USB devices support hotplugging. A USB device can be inserted or removed at any time. The client driver must handle removal and reinsertion of an open device. Use hotplug callbacks to handle open devices. Insertion and removal of closed devices is handled by the attach(9E) and detach(9E) entry points.
Hotplug Callbacks
The USBA 2.0 framework supports the following event notifications:
-
The client driver receives a callback when the device is hot removed.
-
The client driver receives a callback when the device is returned after hot removal. This event callback can occur when the user returns the device to its original port if the driver instance of the device is not offlined. If the driver instance is held open, then the driver instance cannot be offlined.
Client drivers must call usb_register_hotplug_cbs(9F) in their attach(9E) routine to register for event callbacks. Drivers must call usb_unregister_hotplug_cbs(9F) in their detach(9E) routine before dismantling.
Hot Insertion
The sequence of events for hot insertion of a USB device is as follows:
-
The hub driver, hubd(7D), waits for a port connect status change.
-
The
hubd
driver detects a port connect. -
The
hubd
driver enumerates the device, creates child device nodes, and attaches client drivers. Refer to Binding Client Drivers for compatible names definitions. -
The client driver manages the device. The driver is in the
ONLINE
state.
Hot Removal
The sequence of events for hot removal of a USB device is as follows:
-
The hub driver, hubd(7D), waits for a port connect status change.
-
The
hubd
driver detects a port disconnect. -
The
hubd
driver sends a disconnect event to the child client driver. If the child client driver is thehubd
driver or the usb_mid(7D) multi-interface driver, then the child client driver propagates the event to its children. -
The client driver receives the disconnect event notification in kernel thread context. Kernel thread context enables the driver's disconnect handler to block.
-
The client driver moves to the
DISCONNECTED
state. Outstanding I/O transfers fail with the completion reason ofdevice not responding
. All new I/O transfers and attempts to open the device node also fail. The client driver is not required to close pipes. The driver is required to save the device and driver context that needs to be restored if the device is reconnected. -
The
hubd
driver attempts to offline the OS device node and its children in bottom-up order.
The following events take place if the device node is not open when
the hubd
driver attempts to offline the device node:
-
The client driver's detach(9E) entry point is called.
-
The device node is destroyed.
-
The port becomes available for a new device.
-
The hotplug sequence of events starts over. The
hubd
driver waits for a port connect status change.
The following events take place if the device node is open when the hubd
driver attempts to offline the device node:
-
The
hubd
driver puts the offline request in the periodic offline retry queue. -
The port remains unavailable for a new device.
If the device node was open when the hubd
driver
attempted to offline the device node and the user later closes the device
node, the hubd
driver periodic offlining of that device
node succeeds and the following events take place:
-
The client driver's detach(9E) entry point is called.
-
The device node is destroyed.
-
The port becomes available for a new device.
-
The hotplug sequence of events starts over. The
hubd
driver waits for a port connect status change.
If the user closes all applications that use the device, the port becomes available again. If the application does not terminate or does not close the device, the port remains unavailable.
Hot Reinsertion
The following events take place if a previously-removed device is reinserted into the same port while the device node of the device is still open:
-
The hub driver, hubd(7D), detects a port connect.
-
The
hubd
driver restores the bus address and the device configuration. -
The
hubd
driver cancels the offline retry request. -
The
hubd
driver sends a connect event to the client driver. -
The client driver receives the connect event.
-
The client driver determines whether the new device is the same as the device that was previously connected. The client driver makes this determination first by comparing device descriptors. The client driver might also compare serial numbers and configuration descriptor clouds.
The following events might take place if the client driver determines that the current device is not the same as the device that was previously connected:
-
The client driver might issue a warning message to the console.
-
The user might remove the device again. If the user removes the device again, the hot remove sequence of events starts over. The
hubd
driver detects a port disconnect. If the user does not remove the device again, the following events take place:-
The client driver remains in the
DISCONNECTED
state, failing all requests and opens. -
The port remains unavailable. The user must close and disconnect the device to free the port.
-
The hotplug sequence of events starts over when the port is freed. The
hubd
driver waits for a port connect status change.
-
The following events might take place if the client driver determines that the current device is the same as the device that was previously connected:
-
The client driver might restore its state and continue normal operation. This policy is up to the client driver. Audio speakers are a good example where the client driver should continue.
-
If it is safe to continue using the reconnected device, the hotplug sequence of events starts over. The
hubd
driver waits for a port connect status change. The device is in service once again.
20.5.2. Power Management
This section discusses device power management and system power management.
Device power management manages individual USB devices depending on their I/O activity or idleness.
System power management uses checkpoint and resume to checkpoint the state of the system into a file and shut down the system completely. (Checkpoint is sometimes called “system suspend.”) The system is resumed to its pre-suspend state when the system is powered up again.
Device Power Management
The following summary lists what your driver needs to do to power manage a USB device. A more detailed description of power management follows this summary.
-
Create power management components during attach(9E). See the usb_create_pm_components(9F) man page.
-
Implement the power(9E) entry point.
-
Call pm_busy_component(9F) and pm_raise_power(9F) before accessing the device.
-
Call pm_idle_component(9F) when finished accessing the device.
The USBA 2.0 framework supports four power levels as specified by the USB interface
power management specification. See /usr/include/sys/usb/usbai.h
for
information on mapping USB power levels to operating system power levels.
The hubd
driver suspends the port when the device
goes to the USB_DEV_OS_PWR_OFF
state. The hubd
driver
resumes the port when the device goes to the USB_DEV_OS_PWR_1
state
and above. Note that port suspend is different from system suspend. In port
suspend, only the USB port is shut off. System suspend is defined in System Power Management.
The client driver might choose
to enable remote wakeup on the device. See the usb_handle_remote_wakeup(9F) man page.
When the hubd
driver sees a remote wakeup on a port, the hubd
driver completes the wakeup operation and calls pm_raise_power(9F) to notify the child.
The following figure shows the relationship between the different pieces of power management.

The driver can implement one of the two power management schemes described at the bottom of USB Power Management. The passive scheme is simpler than the active scheme because the passive scheme does not do power management during device transfers.
Active Power Management
This section describes the functions you need to use to implement the active power management scheme.
Do the following work in the attach(9E) entry point for your driver:
-
Optionally call usb_handle_remote_wakeup(9F) with
USB_REMOTE_WAKEUP_ENABLE
as the second argument to enable a remote wakeup on the device. -
Call pm_busy_component(9F).
-
Call pm_raise_power(9F) to take power to the
USB_DEV_OS_FULL_PWR
level. -
Communicate with the device to initialize the device.
-
Call pm_idle_component(9F).
Do the following work in the detach(9E) entry point for your driver:
-
Call pm_busy_component(9F).
-
Call pm_raise_power(9F) to take power to the
USB_DEV_OS_FULL_PWR
level. -
If you called the usb_handle_remote_wakeup(9F) function in your attach(9E) entry point, call usb_handle_remote_wakeup(9F) here with
USB_REMOTE_WAKEUP_DISABLE
as the second argument. -
Communicate with the device to cleanly shut down the device.
-
Call pm_lower_power(9F) to take power to the
USB_DEV_OS_PWR_OFF
level.This is the only time a client driver calls pm_lower_power(9F).
-
Call pm_idle_component(9F).
When a driver thread wants to start I/O to the device, that thread does the following tasks:
-
Call pm_busy_component(9F).
-
Call pm_raise_power(9F) to take power to the
USB_DEV_OS_FULL_PWR
level. -
Begin the I/O transfer.
The driver calls pm_idle_component(9F) when the driver receives notice that an I/O transfer has completed.
In the power(9E) entry point for your driver, check whether the power level to which you are transitioning is valid. You might also need to account for different threads calling into power(9E) at the same time.
The power(9E) routine might be called to take the device to the USB_DEV_OS_PWR_OFF
state if the device has been idle for some time or the system is
shutting down. This state corresponds to the PWRED_DWN
state
shown in USB Device State Machine. If the device
is going to the USB_DEV_OS_PWR_OFF
state, do the following
work in your power(9E) routine:
-
Put all open pipes into the idle state. For example, stop polling on the interrupt pipe.
-
Save any device or driver context that needs to be saved.
The port to which the device is connected is suspended after the call to power(9E) completes.
The power(9E) routine might be called to power on the device when either
a device-initiated remote wakeup or a system-initiated wakeup is received.
Wakeup notices occur after the device has been powered down due to extended
idle time or system suspend. If the device is going to the USB_DEV_OS_PWR_1
state or above, do the following work in your power(9E) routine:
-
Restore any needed device and driver context.
-
Restart activity on the pipe that is appropriate to the specified power level. For example, start polling on the interrupt pipe.
If the port to which the device is connected was previously suspended, that port is resumed before power(9E) is called.
Passive Power Management
The passive power management scheme is simpler than the active power management scheme described above. In this passive scheme, no power management is done during transfers. To implement this passive scheme, call pm_busy_component(9F) and pm_raise_power(9F) when you open the device. Then call pm_idle_component(9F) when you close the device.
System Power Management
System power management consists of turning off the entire system after
saving its state, and restoring the state after the system is turned back
on. This process is called CPR (checkpoint and resume).
USB client drivers operate the same way that other client drivers operate
with respect to CPR. To suspend a device, the driver's detach(9E) entry point
is called with a cmd argument of DDI_SUSPEND
.
To resume a device, the driver's attach(9E) entry point is called with
a cmd argument of DDI_RESUME
.
When you handle the DDI_SUSPEND
command in your detach(9E) routine,
clean up device state and clean up driver state as much as necessary for a
clean resume later. (Note that this corresponds to the SUSPENDED
state
in USB Device State Machine.) When you handle
the DDI_RESUME
command in your attach(9E) routine, always take
the device to full power to put the system in sync with the device.
For USB devices, suspend and resume are handled similarly to a hotplug disconnect and reconnect (see Hotplugging USB Devices). An important difference between CPR and hotplugging is that with CPR the driver can fail the checkpoint process if the device is not in a state from which it can be suspended. For example, the device cannot be suspended if the device has an error recovery in progress. The device also cannot be suspended if the device is busy and cannot be stopped safely.
20.5.3. Serialization
In general, a driver should not call USBA functions while the driver is holding a mutex. Therefore, race conditions in a client driver can be difficult to prevent.
Do not allow normal operational code to run simultaneously with the processing of asynchronous events such as a disconnect or CPR. These types of asynchronous events normally clean up and dismantle pipes and could disrupt the normal operational code.
One way to manage race conditions and protect normal operational code
is to write a serialization facility that can acquire and release an exclusive-access
synchronization object. You can write the serialization facility in such a
way that the synchronization object is safe to hold through calls to USBA
functions. The usbskel
sample driver demonstrates this
technique. See Sample USB Device Driver for
information on the usbskel
driver.
20.6. Utility Functions
This section describes several functions that are of general use.
20.6.1. Device Configuration Facilities
This section describes functions related to device configuration.
Getting Interface Numbers
If you are using a multiple-interface device where the usb_mid(7D) driver is making only one of its interfaces available to the calling driver, you might need to know the number of the interface to which the calling driver is bound. Use the usb_get_if_number(9F) function to do any of the following tasks:
-
Return the number of the interface to which the calling driver is bound. The usb_get_if_number(9F) function returns an interface number greater than zero in this case.
-
Discover that the calling driver manages an entire multi-interface device. The driver is bound at the device level so that
usb_mid
has not split it. The usb_get_if_number(9F) function returnsUSB_DEVICE_NODE
in this case. -
Discover that the calling driver manages an entire device by managing the only interface that device offers in its current configuration. The usb_get_if_number(9F) function returns
USB_COMBINED_NODE
in this case.
Managing Entire Devices
If a driver manages an entire composite device, that driver can bind to the entire device by using a compatible name that contains vendor ID, product ID, and revision ID. A driver that is bound to an entire composite device must manage all the interfaces of that device as a nexus driver would. In general, you should not bind your driver to an entire composite device. Instead, you should use the generic multiple-interface driver usb_mid(7D).
Use the usb_owns_device(9F) function to determine whether a driver owns an entire
device. The device might be a composite device. The usb_owns_device(9F) function returns TRUE
if the driver owns
the entire device.
Multiple-Configuration Devices
USB devices make only a single configuration available to the host at any particular time. Most devices support only a single configuration. However, a few USB devices support multiple configurations.
Any device that has multiple configurations is placed
into the first configuration for which a driver is available. When seeking
a match, device configurations are considered in numeric order. If no matching
driver is found, the device is set to the first configuration. In this case,
the usb_mid
driver takes over the device and splits the
device into interface nodes. Use the usb_get_cfg(9F) function to return the current
configuration of a device.
You can use either of the following two methods to request a different configuration. Using either of these two methods to modify the device configuration ensures that the USBA module remains in sync with the device.
-
Use the cfgadm_usb(1M) command.
-
Call the usb_set_cfg(9F) function from the driver.
Because changing device configuration affects an entire device, the client driver must meet all of the following criteria to call the usb_set_cfg(9F) function successfully:
-
The client driver must own the entire device.
-
The device must have no child nodes, because other drivers could drive the device through them.
-
All pipes except the default pipe must be closed.
-
The device must have multiple configurations.
-
Do not change the device configuration by doing a SET_CONFIGURATION
USB request manually. Using a SET_CONFIGURATION
request
to change the configuration is not supported.
Modifying or Getting the Alternate Setting
A client driver can call the usb_set_alt_if(9F) function to change the selected alternate setting of the currently selected interface. Be sure to close all pipes that were opened explicitly. When switching alternate settings, the usb_set_alt_if(9F) function verifies that only the default pipe is open. Be sure the device is settled before you call usb_set_alt_if(9F).
Changing the alternate setting can affect which endpoints and which class-specific and vendor-specific descriptors are available to the driver. See The Descriptor Tree for more information about endpoints and descriptors.
Call the usb_get_alt_if(9F) function to retrieve the number of the current alternate setting.
When you request a new alternate setting, a new configuration, or a new interface, all pipes except the default pipe to the device must be closed. This is because changing an alternate setting, a configuration, or an interface changes the mode of operation of the device. Also, changing an alternate setting, a configuration, or an interface changes the device's presentation to the system.
20.6.2. Other Utility Functions
This section describes other functions that are useful in USB device drivers.
Retrieving a String Descriptor
Call the usb_get_string_descr(9F) function to retrieve a string descriptor given its index. Some configuration, interface, or device descriptors have string IDs associated with them. Such descriptors contain string index fields with nonzero values. Pass a string index field value to the usb_get_string_descr(9F) to retrieve the corresponding string.
Pipe Private Data Facility
Each pipe has one pointer of space set aside for the client driver's private use. Use the usb_pipe_set_private(9F) function to install a value. Use the usb_pipe_get_private(9F) function to retrieve the value. This facility is useful in callbacks, when pipes might need to bring their own client-defined state to the callback for specific processing.
Clearing a USB Condition
Use the usb_clr_feature(9F) function to do the following tasks:
-
Issue a USB
CLEAR_FEATURE
request to clear a halt condition on an endpoint. -
Clear a remote wakeup condition on a device.
-
Clear a device-specific condition at a device, interface, or endpoint level.
Getting Device, Interface, or Endpoint Status
Use the usb_get_status(9F) function to issue a USB GET_STATUS
request
to retrieve the status of a device, interface, or endpoint.
-
Device status. Self-powered and remote-wakeup-enabled.
-
Interface status. Returns zero, per USB 2.0 specification.
-
Endpoint status. Endpoint halted. This status indicates a functional stall. A halt must be cleared before the device can operate again.
A protocol stall indicates that an unsupported control pipe request has been made. A protocol stall is cleared automatically at the beginning of the next control transfer.
Getting the Bus Address of a Device
Use the usb_get_addr(9F) function to get the USB bus address of a device for debugging purposes. This address maps to a particular USB port.
20.7. Sample USB Device Driver
This section describes a template USB device driver that uses the USBA 2.0 framework
for
the illumos environment. This driver demonstrates many of the features discussed
in this chapter. This template or skeleton driver is named usbskel
.
The usbskel
driver is a template that you can use
to start your own USB device driver. The usbskel
driver
demonstrates the following features:
-
Reading the raw configuration data of a device. Every USB device needs to be able to report device raw configuration data.
-
Managing pipes. The
usbskel
driver opens an interrupt pipe to show how to manage pipes. -
Polling. Comments in the
usbskel
driver discuss how to do polling. -
USB version management and registration.
-
USB logging.
-
Accommodations for USB hotplugging.
-
Accommodations for illumos suspend and resume.
-
Accommodations for power management.
-
USB serialization.
-
Use of USB callbacks.
This usbskel
driver is available on Sun's web site
at http://www.sun.com/bigadmin/software/usbskel/.
For source for additional USB drivers, see the illumos web site. Go to http://src.illumos.org/.