USB Device Driver Stack for OS/2 Warp

Purpose
This document briefly describes OS/2 USB stack implementation and gives guidelines for host controller driver and class/client driver implementation together with stack driver communication API specification.

Intended Audience
This programmers reference document could be used by host controller and client/Class driver developer and contains design requirements used for OS/2 base USB stack implementation.

Introduction
OS/2 USB driver stack consists of 3 layers 1) host controller driver layer; 2) USBD root layer; 3) class/client layer. Driver stack internally uses communication interface based on Inter Driver Communication (IDC) calls. Interface between the system and class/client drivers depends on particular driver and its application area.USBD driver acts as a root of USB driver stack and both other layer drivers must register themselves within USBD. Root layer driver manages all USB device attach/detach processes and serves USB Hub devices internally. Host controller driver layer interacts with USB host hardware to execute requests received from USDB driver/layer and sends request complete messages (process IRQ requests) to USBD / Class/Client drivers.

Design Overview

 * accepts registration/de-registration requests from all installed HCD layer drivers
 * accepts registration requests from all installed device class/client drivers
 * reset/initialize all host controllers via call to registered host controller drivers. This call starts enumeration process for particular host starting with device descriptor retrieval for controller root hub and then continuing root hub port processing as for regular hub device. Reset/Initialize call is executed as the latest at USBD driver INITIALIZATION COMPLETE strategy call or before if Complete Initialization IDC call is received from any class/client driver (see description below)
 * calls all registered class/client drivers when new non-HUB device is attached or when detached
 * passes I/O requests initiated by class/client drivers to appropriate HCD driver and processes request cancellation
 * manages Hub control pipes to process device attachment/detachment (including I/O request canceling for detached devices)
 * process set device configuration/set interface/clear stalled endpoint (reset endpoint) requests received from Class/Client drivers
 * processes APM events - received from host controller drivers or APM.SYS driverprocess reset port request from Class/Client drivers (this request will initiate device enumeration process for selected device again)
 * notify all registered Class/Client drivers about completed initial device enumeration.

Host Controller driver
Basic OS/2 USB Host Controller Driver processes only BASEDEVINIT (1Bh) and INITIALIZATION COMPLETE (1Fh) strategy calls. Processing of I/O requests and IRQ events (within USB stack requests completed by host/driver are called IRQ events) are handled by means of IDC calls to and from the USB Driver (or class client driver for IRQ processing calls). Host controller driver has to implement I/O request processing for all devices connected to corresponding host controller including root hub (in most cases implemented in host driver code).

BASEDEVINIT Strategy Command

 * Processes CONFIG.SYS parameters. All stack drivers must process /V (verbose) parameter and display during initialization driver version/hardware information if initialization succeeds or error message(s) describing reason of failure. If /V parameter is not specified driver must initialize/fail to initialize quietly, without displaying any messages.
 * Locates host controller device(s)
 * Registers I/O resources within OS/2 Resource Manager
 * Tries to register all host controllers within USBD driver: a) get USBD driver IDC address via DevHelp_AttachDD call with driver name "USBD$ "; b) issue registration call if possible.

INITIALIZATION COMPLETE Strategy Command
OS/2 USB host controller reissues registration call if registration failed because USBD driver was not yet loaded at initialization time.

Request complete (IRQ) notification
Host controller driver notifies I/O request originator by calling IDC routine, address of this routine is stored in request block. Threads that process request and call notification routine must be separated, this is valid also for requests that could be executed immediately - like retrieving port status from I/O space or memory mapped registers.

IDC Processing

 * Accept an I/O request. HCD driver must implement software Root Hub support for host controller hardware
 * Cancel I/O requests for specific USB device
 * Reset Host controller
 * process power management suspend call (optional)


 * parameter checking
 * bandwidth calculations
 * request execution and error / success condition processing
 * client notification about completed request.


 * Process IRQ


 * Register/Unregister HCD driver
 * Notify USBD driver on wake-up events

Root hub implementation

 * retrieving root hub device descriptor
 * setting hub device address
 * retrieving hub device configuration structures
 * setting hub configuration


 * retrieving hub descriptor
 * retrieving hub/hub port status information
 * clearing hub/hub port features
 * setting hub/hub port features
 * retrieving hub "status changed" pipe data.

Class/Client Driver
Class/client drivers serves specific USB device class or device giving access to hardware from client applications or operating system services (like file system, multimedia data streaming). Depending on device class, drivers are implemented as base device drivers or device driver and must process at least BASEDEVINIT (1Bh) or DEVINIT (00h) strategy calls. Support of INITIALIZATION COMPLETE (1Fh) strategy call is recommended to avoid problems with driver configuration string order in CONFIG.SYS file. OS/2 USB stack assumes that if a separate client driver(s) must be used for particular class driver then client driver never communicates directly with USBD driver and all requests must be passed to class driver that forwards requests to USBD driver. Class/Client driver must register within USBD driver. USBD calls class driver later to inform it about device/system status change. OS/2 USB stack use device configuration value to perform device reservation on the level of device. 0 configuration value means that device is not used / claimed for service. Class/Client driver must set configuration value to desired configuration (non-zero number) value to reserve device. Sharing of devices with the same configuration value between several drivers will work only in case if installed set of drivers are designed to work simultaneously or have special reservation mechanisms.

BASEDEVINIT/DEVINIT Strategy Command

 * Processes CONFIG.SYS parameters. All stack drivers must process /V (verbose) parameter and display during initialization driver version/hardware information if initialization succeeds or error message(s) describing reason of failure. If /V parameter is not specified driver must initialize/fail to initialize quietly, without displaying any messages.
 * Tries to register all host controllers within USBD driver: a) get USBD driver IDC address via DevHelp_AttachDD call with driver name "USBD$ "; b) issue registration call if possible (could not be done for BASE device drivers as registration call must be Ring0 call, but could be done later after initialization completes and driver starts normal request processing).

INITIALIZATION COMPLETE Strategy Command
OS/2 USB Class/Client driver reissues registration call if registration failed (or was not executed) at initialization time during initialization complete call.

IDC Processing

 * Accept/reject device for service (during this call device configuration value must be set to non-zero value in corresponding DeviceInfo structure)
 * Process detach device requests (this request must be passed to client driver if it works on the top of class driver)
 * process power management suspend/resume notifications (optional)
 * complete initialization call
 * Process IRQ notification calls
 * Process idle notification calls (to switch from BIOS support mode to native OS/2)


 * class driver registration call
 * I/O requests required for device initialization/functioning
 * configuration/interface setting calls
 * request cancel call
 * clear stalled endpoint call
 * complete initialization call (optional, may be response to adapter driver 'complete initialization' iorb request)

Component Externals
Both the host controller driver (HCD) and USBD driver use IDC calls to perform I/O on the USB devices. These call descriptions can be used to develop new HCD layer drivers and new Device Class drivers.

APIs
Drivers in USB driver stack communicate with each other via IDC routine calls using common parameter format: void IDCRoutine( RPH_GENIOCTL FAR *pRP_GENIOCTL ); where pRP_GENIOCTL is a pointer to a standard OS/2 IOCTL request packet. All structure field usage are as for standard IOCTL request packet. pRP_GENIOCTL->Cmd must be set to 10h (generic IOCtl call). The USBUHCD and USBD drivers ignore the pRP_GENIOCTL->sfn field value. The pRP_GENIOCTL->Category field is used to indicate which USB device driver is to process the request. Valid values include:

The pRP_GENIOCTL->Function field is used to indicate what type of functionality is to be performed. Value values include:

The following table show which device drivers can originate and which driver process specified IDC function types.

Upon successful completion, request packets in the USB driver stack set the STATUS_DONE bit as a return code in the pRP_GENIOCTL->Status field. On error conditions, the STATUS_DONE and STERR bits are set along with the appropriate error code value in the lower 7 bits in the pRP_GENIOCTL->Status field. Valid error status values in the USB stack include the following: The individual IDC routines set specific return codes describing the failed action (see below).

Host Controller driver IDC calls
The following calls must be processed by the Host Controller Driver (calls are issued by USBD driver).

Accept I/O request
This call is used to place a I/O request in USB Host Controller Schedule. Parameters for this request are defined as: pRP_GENIOCTL->Category = 0x90 // Processed by HCD pRP_GENIOCTL->Function = 0x41 // IO Request pRP_GENIOCTL->ParmPacket = &USBRb The USBRb data structure is defined in src\dev\usb\include\usbidc.h as:

Upon completion one of the following return codes is set in the pRP_GENIOCTL->Status field: Isochronous requests are processed in different manner than all other requests - isochronous requests require special initialization/de initialization actions before data transfer starts and after transfer is finished. HCD layer driver calls IRQ routine only once per buffer even if data are split between several frames. Isochronous request details are specified in 'isoFlags' field. Typical calling sequence is the following:
 * If this flag is set, then request block contain Mult, HubAddr, PortNumb fields with valid values set and also isochronous request extension fields (values from these fields are used only if proper flag is set). USB2.0 flag is always on for host / USBD communication and extended request structure is used. Class/client drivers can issue requests using short structure form.

1) isochronous open request (isoFlags=00000001b) allocates data structures to serve request. HCD driver saves isoBuffers and isoFrameLength in these structures. Data block is identified by device address, target endpoint address and transfer direction. Open request will fail if: there are already opened isochronous request for the same device, endpoint addresses and transfer direction (return code 0x8118); not enough memory to allocate request data block (return code 0x8114); not enough USB bandwidth to complete request (return code 0x8115); requested frame size (number of bytes to be transferred during single host frame) exceeds maximal packet size for target endpoint (return code 0x8113). Open call also saves caller identification data (IDC routine address, DS value, caller's category) and requestData1, requestData2, requestData3. Identification data is used in all IRQ calls, but requestData only for canceled request notification.

2) accept isochronous data buffer (isoFlags=00000000b) accepts buffer address and length from buffer1, buffer1Length fields and starts data transfer. In case if individual frame lengths are used then buffer2 and buffer2Length fields are used to pass this information. If constant frame length is used then both these fields must be set to 0. For detailed description of fixed/individual frame size transfers see note below. Buffer will be refused if: no isochronous request is opened for specified device,endpoint and transfer direction (no previous open call) (return code 0x8118); number of buffers exceeds number specified at open time (return code 0x8113). Values specified in requestData1, requestData2, requestData3 fields are used in 'buffer processed' notification call.

3) isochronous close request (isoFlags=00000010b) releases data structures allocated for request. This request must be the last call in isochronous data transfer for specified device, endpoint and transfer direction. This request fails if there were no open call for the same target. Cancel isochronous buffer processing call (isoFlags=00000100b) stops isochronous data processing, clears all the buffer information and returns in buffer1, buffer1Length fields last processed data buffer address and processed data length. Cancel request fails with return code 0x8118 if no isochronous request is opened for specified device,endpoint and transfer direction. Retrieve request status information call (isoFlags=00000100b) returns in buffer1, buffer1Length fields last processed data buffer address and processed data length. Request fails with return code 0x8118 if no isochronous request is opened for specified device,endpoint and transfer direction. The initial implementation supported only fixed frame size isochronous transfers. But it proved that for some clients (like USBAudio) this approach is not flexible enough and causes dropped samples due to fact that internal sampling frequency of device is not the exact multiple of USB internal 1000 Hz frame clock frequency. To allow clients to use explicit synchronization the API was extended to be able to specify the length of every frame individually. This information is passed in previously unused Buffer2 and Buffer2Length fields. There is possibility that device will not be able to send/receive as much information as caller requested. To allow host controller to report back the number of actually transferred bytes the Buffer2 and Buffer2Length fields are used in notification messages.

To pass information about individual frame lengths the array of ISO_FRAME_INFO structures is used. There must be as many structures as number of frames to transfer. Structure contains only one member Length which specifies the number of bytes to transfer between host and device. In notification message the original values are replaced with number of actually transferred bytes.

Cancel I/O request(s)
This call is used to cancel all queued I/O requests for the specified USB device and endpoint. Parameters for this request are defined as: pRP_GENIOCTL->Category = 0x90 // Processed by HCD pRP_GENIOCTL->Function = 0x42 // Cancel request pRP_GENIOCTL->ParmPacket = &USBCancel The USBCancel data structure is defined in src\dev\usb\include\usbidc.h as: Upon completion one of the following return codes is set in the pRP_GENIOCTL->Status field:

Reset host controller
This call is used to reset host controller and move it into 'running' state: pRP_GENIOCTL->Category = 0x90 // Processed by HCD pRP_GENIOCTL->Function = 0x42 // Cancel request pRP_GENIOCTL->ParmPacket = &USBDetach The USBDetach data structure is defined in src\dev\usb\include\usbidc.h as: Upon completion one of the following return codes is set in the pRP_GENIOCTL->Status field:

Notify about power management state changes
This call is used to notify host controller drivers about changes in APM power states. Parameters for this request are defined as: pRP_GENIOCTL->Category = 0x90 // Processed by HOST pRP_GENIOCTL->Function = 0x4d // Power management notification pRP_GENIOCTL->ParmPacket = &ulAPMState // Power state ulAPMState is DWORD value which can contain these values: Return code is not analyzed by originator driver (USBD driver), host driver that processes this request can set it to any proper value.

USBD driver IDC calls
The following calls are processed by the USBD driver (calls may be issued by HCD and/or class driver depending on call type).

Cancel I/O request(s)
This call is used to cancel all queued I/O requests for the specified USB device and endpoint. USBD driver verifies host/device/endpoint and sends request to proper host controller driver. Parameters for this request are defined as: pRP_GENIOCTL->Category = 0x91 // Processed by USBD pRP_GENIOCTL->Function = 0x42 // Cancel request pRP_GENIOCTL->ParmPacket = &USBCancel The USBCancel data structure is defined in src\dev\usb\include\usbidc.h as: Upon completion one of the following return codes is set in the pRP_GENIOCTL->Status field:

Set device configuration request
This call is used by class/client drivers to set device configuration. USBD driver stores configuration value in device data structures that are passed to class/client drivers during attach request processing, configuration value could be used as flag to show that device will be used by class/client driver. Parameters for this request are defined as: pRP_GENIOCTL->Category = 0x91 // Processed by USBD pRP_GENIOCTL->Function = 0x48 // Set configuration pRP_GENIOCTL->ParmPacket = &USBSetConf The USBSetConf data structure is defined in src\dev\usb\include\usbidc.h as: Upon completion one of the following return codes is set in the pRP_GENIOCTL->Status field:

Set interface request
This call is used by class/client drivers to select device Interface. USBD driver stores configuration value in device data structures that are passed to class/client drivers during attach request processing. Parameters for this request are defined as: pRP_GENIOCTL->Category = 0x91 // Processed by USBD pRP_GENIOCTL->Function = 0x49 // Set configuration pRP_GENIOCTL->ParmPacket = &USBSetConf The USBSetConf data structure is defined in src\dev\usb\include\usbidc.h as: Upon completion one of the following return codes is set in the pRP_GENIOCTL->Status field:

Register HCD driver
This call is used to register a Host Controller Driver with the USBD driver. Parameters for this request are defined as: pRP_GENIOCTL->Category = 0x90 // Processed by USBD (!) pRP_GENIOCTL->Function = 0x43 // Register HCD driver pRP_GENIOCTL->ParmPacket = &USBHCD The USBHCD data structure is defined in src\dev\usb\include\usbidc.h as: Last 2 items (hDriver and hAdapter) are repeated hcdCount times. Registration call is processed by USBD even if request category is set to host controller driver to distinguish between host and Client/Class registration requests. This is the only exception in request category processing.

Upon completion one of the following return codes is set in the pRP_GENIOCTL->Status field:

Un-register HCD driver
This call is used to un-register a HCD from the USBD driver. Parameters for this request are defined as: pRP_GENIOCTL->Category = 0x91 // USBD pRP_GENIOCTL->Function = 0x4f // Register Device Class pRP_GENIOCTL->ParmPacket = &USBHCD The USBHCD data structure is defined in src\dev\usb\include\usbidc.h, see details in 'Register HCD Driver' request description. Upon completion one of the following return codes is set in the pRP_GENIOCTL->Status field:

Register Device Class driver
This call is used to register a Device Class Driver with the USBD driver. Parameters for this request are defined as: pRP_GENIOCTL->Category = 0x91 // USBD pRP_GENIOCTL->Function = 0x43 // Register Device Class pRP_GENIOCTL->ParmPacket = &USBDClass The USBDClass data structure is defined in src\dev\usb\include\usbidc.h as: Upon completion one of the following return codes is set in the pRP_GENIOCTL->Status field:

Clear Stalled Endpoint request
This call is used to pass a I/O request to the HCD driver. All the device class drivers call the USBD driver to add their requests to appropriate HCD schedule. Parameters for this request are defined as: pRP_GENIOCTL->Category = 0x91 // Processed by USBD pRP_GENIOCTL->Function = 0x4a // Clear Stalled Request pRP_GENIOCTL->ParmPacket = &USBClearStalled The USBClearStalled data structure is defined in src\dev\usb\include\usbidc.h as: Upon completion one of the following return codes is set in the pRP_GENIOCTL->Status field:

Process I/O request
USBD driver accepts i/o requests in the same way as host drivers: pRP_GENIOCTL->Category = 0x91 // Processed by USBD pRP_GENIOCTL->Function = 0x41 // accept i/o Request pRP_GENIOCTL->ParmPacket = &USBRb The USBRb data structure is defined in src\dev\usb\include\usbidc.h, see details in host controller decryption section. USBD checks for controller/device/endpoint presence and set device dependent flags in request block: 1) device speed flags (low/high speed device); 2) 0 default service time is replaced with actual value from device descriptor; 3) 0 maxPacket size is replaced with actual value . Upon completion one of the following return codes is set in the pRP_GENIOCTL->Status field:

Initialize registered USB host controllers
This call is used to start (move to running state) all registered USB host controller(s). This call could be used to start hosts earlier than all driver chain passes initialization complete phase, like during system boot when it is time to switch from BIOS support to native OS/2 driver support. Parameters for this request are defined as: pRP_GENIOCTL->Category = 0x91 // Processed by USBD pRP_GENIOCTL->Function = 0x4c // Complete initialization Request pRP_GENIOCTL->ParmPacket = &pRP_GENIOCTL // not used, must be non-NULL // address to pass parameter check Upon completion one of the following return codes is set in the pRP_GENIOCTL->Status field:

Notify about power management state changes
This call is used to notify USBD about changes in APM power states - host wake up. Parameters for this request are defined as: pRP_GENIOCTL->Category = 0x91 // Processed by USBD pRP_GENIOCTL->Function = 0x4d // Power management notification pRP_GENIOCTL->ParmPacket = &ulAPMState // Power state ulAPMState is DWORD value which can contain these values: Return code is not analyzed by host controller driver, USBD can set it to any proper value.

Reset port
This call is used to request USBD driver to reset hub port for specified device, port reset will initiate device re-enumeration. Parameters for this request are defined as: pRP_GENIOCTL->Category = 0x91 // Processed by USBD pRP_GENIOCTL->Function = 0x51 // reset port pRP_GENIOCTL->ParmPacket = &USBRb // controller/device IDs The USBRb data structure is defined in src\dev\usb\include\usbidc.h., USBD driver uses controllerId and deviceAddress fields to locate corresponding hub ID and port number to be reset.

Upon completion one of the following return codes is set in the pRP_GENIOCTL->Status field:

Class/client driver IDC calls
The following calls are processed by USB Class/Client drivers (calls may be issued by USBD or by HCD drivers depending on call type).

Initial enumeration complete
USBD driver processes all hub devices/attached devices during initial device enumeration. Class/Client drivers are notified by sending IDLE message sent out by USBD when initial enumeration completes. This notification could be used to switch from system boot time environment to normal processing environment and is sent out only once during boot/operation session. Parameters for this request are defined as: pRP_GENIOCTL->Category = 0x92 // Processed by CLASS/Client pRP_GENIOCTL->Function = 0x52 // idle pRP_GENIOCTL->ParmPacket = (PVOID)1 // not used Return code is not analyzed by USBD driver, Class/Client driver can set it to any proper value.

Process IRQ
This call is used to process an IRQ (request complete processing) at the Class/Client level. Parameters for this request are defined as: pRP_GENIOCTL->Category = 0x92 // Processed by Class/Client pRP_GENIOCTL->Function = 0x44 // Process IRQ pRP_GENIOCTL->ParmPacket = &USBRb The USBRb data structure is defined in src\dev\usb\include\usbidc.h., see details in USBD Process IRQ description.

Accept/reject device
This call is used to inform Class/Client drivers about new device that has completed enumeration process. Client/Class driver must analyze device description information and accept device for service or refuse service. Parameters for this request are defined as: pRP_GENIOCTL->Category = 0x92 // Processed by Class/Client pRP_GENIOCTL->Function = 0x45 // Process IRQ pRP_GENIOCTL->ParmPacket = &USBCDServe The USBCDServe data structure is defined in src\dev\usb\include\usbidc.h. And contains the following fields:

The DeviceInfo data structure is defined in src\dev\usb\include\usbidc.h. and contains the following fields: Upon completion one of the following return codes is set in the pRP_GENIOCTL->Status field:

Detach device
This call is used to process an IRQ (request complete processing) at the Class/Client level. Parameters for this request are defined as: pRP_GENIOCTL->Category = 0x92 // Processed by Class/Client pRP_GENIOCTL->Function = 0x46 // Process IRQ pRP_GENIOCTL->ParmPacket = &USBDetach The USBDetach data structure is defined in src\dev\usb\include\usbidc.h. and contains following fields:

Upon completion one of the following return codes is set in the pRP_GENIOCTL->Status field: