Go to main content

Writing Device Drivers for Oracle® Solaris 11.3

Exit Print View

Updated: March 2019

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.

Before the Client Driver Is Attached

    The following events take place before the client driver is attached:

  1. The PROM (OBP/BIOS) and USBA framework gain access to the device before any client driver is attached.

  2. The hub driver probes devices on each of its hub's ports for identity and configuration.

  3. The default control pipe to each device is opened, and each device is probed for its device descriptor.

  4. 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.

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.

Figure 23  A Hierarchical USB Descriptor Tree

image:Diagram shows a tree of pairs of descriptors for each interface of a device with two configurations.

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:


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.

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. Specify USB_PARSE_LVL_IF for the parse level in the usb_get_dev_data() call to retrieve only those descriptors.

  • USB_PARSE_LVL_CFG. If your client driver binds to the whole device, specify USB_PARSE_LVL_CFG to retrieve all descriptors of the current configuration.

  • USB_PARSE_LVL_ALL. Specify USB_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 theusb_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.