The Arca Noae USB Stack Implementation
The Arca Noae USB stack has been written to be as compatible as possible with existing class drivers. Recent changes in modern USB hardware makes maintaining complete compatibility impossible, particularly with XHCI connected devices. However, a class driver should not be concerned about which type of Host Controller a device is connected to. The Arca Noae USB stack implementation handles Host Controller differences so that class drivers don’t need to. This wiki documents the requirements and limitations of the Arca Noae USB stack particularly where they might differ from older stacks. Following these guidelines will ensure that your class driver will work regardless of how the device is attached to the system.
The Arca Noae USB stack attempts to comply with the USB specifications as much as possible, and you can use applicable USB specifications for your device class as a guide to writing your class drivers. However, beware that when interfacing to the Arca Noae USB stack you are interfacing with an implementation, not a specification. Your driver must comply with the requirements of the implementation regardless of what any specification says.
The Arca Noae USB stack is a 32 bit stack with fully 32 bit inter-driver function calls. All drivers in the Arca Noae USB stack are 32 bit drivers except for the USB audio class 1 driver. A limited 16 bit IDC interface is maintained for compatibility with older 16 bit class drivers, including the audio driver. 16 bit drivers which use the old style 16 bit IDC interface may not have access to all the features, functionality, and performance of the full 32 bit stack, and may not be able to fully utilize USB3 devices.
Interface / IDC structures
All class drivers and applications should use USBRB2 data structures and set the USB2 flag bit. All HC drivers only accept USBRB2 structures. If an old style USBRB data packet is detected, USBD will attempt to virtualize a USBRB2 structure from the USBRB structure before passing the transaction on to an HC driver. At a minimum, this will cause additional delays and memory usage. In some cases this may cause errors.
The DeviceInfo data structure is owned by the USBD driver. Although a pointer to this structure is passed to class drivers, this is for reference only. Class drivers or applications must not modify or write to any field of the DeviceInfo data structure. In particular, class drivers or applications must not modify or write to the bConfigurationValue field. It is a error to do so and may cause malfunctions.
Handling Stalled Endpoints
Class drivers must issue a Clear Stall to any stalled endpoint including endpoint zero using the USBD ClearStall function. The Arca Noae stack handles whatever needs to be done at the lower levels so that class drivers don’t need to worry about Host Controllers or low level connections.
Setting the Configuration
USBD sets the first configuration (configuration 1) when a new device is attached. Class drivers should only set a different configuration if they need a different configuration than what is already set. The current configuration can be determined by looking at bConfigurationValue in the DeviceInfo structure. Class drivers should set a new configuration only by calling the USBD Set Configuration function. The USBD Set Configuration function handles whatever needs to be done at the lower levels so that class drivers don’t need to worry about Host Controllers or low level connections. Sending a set configuration transaction directly to the device outside of the USBD Set Configuration function may have undefined and undesired results. Setting a different configuration may not always be possible so checking the return status of the function is recommended.
The bConfigurationValue in the DeviceInfo structure indicates which configuration is currently set. Drivers and applications should check the bConfigurationValue field in the DeviceInfo structure to see if the desired configuration is already set. Drivers and applications should not use the bConfigurationValue for any purpose other than to determine which configuration is already set. Class drivers should never write to the bConfigurationValue entry in the DeviceInfo structure. Attempting to set a configuration that is already set is an error and may cause a malfunction. Modifying the bConfigurationValue field in the DeviceInfo structure does not change the currently set configuration. Writing the bConfigurationValue field in the DeviceInfo structure is an error and may cause a malfunction.
Setting the Interface
USBD sets interface 0 alt 0 when a new device is attached. If a class driver only needs to use interface 0 alt 0 and only the endpoints in that interface, then the class driver does not need to set an interface. If a class driver needs to use a different interface then the class driver will need to set that interface. A class driver may set the interface it wants even if it is already set.
Class drivers may not send transactions to endpoints that are not in the interface that has been set. Sending a transaction to any endpoint that has not been enabled by an appropriate set interface operation will return an error 18 (Endpoint not ready) from the AcceptIO function. Note that a set interface operation may not be successful. You must check the return status to ensure that the interface was actually set.
The set interface functionality is additive with regard to endpoints. This means that every set interface action adds/activates any new endpoints in that interface that have not already been activated. Setting a different interface that has fewer or different endpoints will not deactivate already activated endpoints. Setting a different interface that has the same endpoints, but with different attributes will update the endpoint attributes. Every activated endpoint consumes resources including memory and bandwidth. Enabling multiple interfaces with multiple different endpoints can result in bandwidth problems, and malfunctions. Endpoints cannot be deactivated once they are activated, they can only be changed. Drivers and applications should not send transactions to endpoints that are not in the interface that it has set, and doing so is an error.