This chapter documents the USB peripheral stack, which is an extension of the USB Developer's Kit, also known as the USB Kit. The USB peripheral stack interprets USB commands on USB devices running VxWorks.
The USB peripheral stack includes the source code for the USB peripheral stack and firmware emulators.
This chapter shows you how to write your own USB target controller driver and USB target firmware. The USB peripheral stack is designed to allow:
Figure 1 presents an overview of the USB peripheral stack.
At the bottom of the stack is the USB target controller (USB TC), the hardware part of the peripheral that connects to the USB. Many manufacturers build USB target controllers, which vary widely in implementation. The sample code provided by Wind River supports an evaluation kit from Philips based on the Philips PDIUSBD12 target chip. The PDIUSBD12 is a general purpose USB target chip that supports each of the four basic USB transfers: control, interrupt, bulk, and isochronous. In the evaluation kit, the PDIUSBD12 sits on a standalone printed circuit board; a separate ISA bus adapter card is used to connect the board to a standard PC system.
For each type of target controller, there is a corresponding USB target controller driver (TCD). The TCD provides a consistent, abstract view of the target controller to the upper software layers.
The source for the pre-built Wind River driver for the Philips PDIUSBD12 is contained in the library usbTcdPhilipsPdiusbd12EvalLib. However, given the range of USB target chips available, there is a good chance a customer does not use the Philips PDIUSBD12, and even less likely a customer uses the Philips evaluation kit as part of an actual product. You should be prepared to customize or replace the sample USB target controller driver provided with the USB peripheral stack.
Above the TCD sits the usbTargLib library, a hardware-independent module that provides an abstract set of services for simplifying the job of writing USB target applications. This library incorporates automatic handling, where possible, for USB requests. When automatic processing is not possible, usbTargLib relies on the target application to complete USB transactions.
At run-time, one or more TCDs are attached to usbTargLib. For each TCD, a single target application is also registered with usbTargLib; and usbTargLib becomes responsible for routing requests and responses between the TCD and the target application. Architecturally, usbTargLib is capable of handling multiple TCDs and their corresponding target applications. As a practical matter, however, few peripherals have more than one target controller, TCD, and target application.
At the top of the peripheral stack sits the target application. The target application is the module that gives the peripheral its personality. In response to USB requests received by the TCD and routed through usbTargLib, it is the target application's responsibility to provide appropriate responses. For example, when a request is received to get a USB descriptor from the peripheral, it is the target application's responsibility to provide the contents of the descriptor.
Figure 2 illustrates the functional relationships between the modules that comprise Wind River's USB peripheral driver stack.
The peripheral stack shares a consistent architectural approach with the USB host driver stack, as well as a number of common libraries. (Compare Figure 2 in this supplement with the figures in 1.2 Architecture Overview.)
Modules that are unique to the USB peripheral stack are described in this section. For information on modules shared with the host stack, please see 1.2 Architecture Overview. For additional information on USB-related libraries and subroutines, see the associated reference pages in the online VxWorks Reference Manual: USB Libraries.
This module is a test application that exercises both the Wind River USB host and peripheral stacks. The usbTool functions that control the peripheral stack are independent of those that control the host stack. In fact, if a VxWorks target has both USB host and peripheral hardware, usbTool can be used to control both simultaneously and the two can talk to one another!
The Philips PDIUSBD12 evaluation kit includes a reference firmware implementation. This firmware is implemented as a DOS application. When running, the firmware makes the PDIUSBD12 look like a test device recognizable by a Windows-based Philips test program. The Philips test program allows the user to exercise certain functions on the PDIUSBD12 evaluation board. The usbTargPhilipsD12EvalLib module is a target application that emulates the behavior of the Philips reference firmware. Thus, the Philips test program communicates with the PDIUSBD12 evaluation board when the PDIUSBD12 is under the control of the Wind River USB peripheral stack.
This module is a very simple (and incomplete) example of a target application that acts like a USB printer. This module was originally developed to test USB bulk transfers through the PDIUSBD12; its emulation of a printer is not complete enough to allow it to talk to a standard Windows host, for example.
Like usbTargPrnLib, this module is a very simple and incomplete example of a target application that acts like a USB keyboard. It was originally developed to test USB interrupt transfers through the PDIUSBD12.
Taken together, the three libraries (usbTargPhilipsD12EvalLib, usbTargPrnLib, and usbTargKbdLib) are useful principally to demonstrate how simple it is to create a target application on top of the usbTargLib interface.
This module is the core of the USB peripheral stack. It is responsible for routing requests between TCDs and their corresponding target applications. The usbTargLib module automatically manages the default control pipe for each TCD and provides default handlers, where meaningful, for each USB control request. This module also incorporates the necessary functions to allow target applications to transfer data across the USB, either in response to control requests or on other USB pipes.
This module provides functions to invoke each of the services provided by a USB TCD. It is used by usbTargLib to communicate with underlying HCDs. The functions in usbTcdLib are not called directly by modules other than usbTargLib.
This module is the TCD for the Philips PDIUSBD12 evaluation kit. All hardware-dependent code for a target controller is contained in the TCD. Each TCD exports a single entry point, generally with a name of the form usbTcdXxxxExec( ), and all communication with the TCD is performed through this function. There is no direct link between usbTargLib and any underlying TCD; that is, usbTargLib has no prior (compile-time) knowledge of which TCDs are to be loaded in a particular system. Each TCD's entry point is exposed to usbTargLib during a process called TCD attachment, described in 2.2.2 Attaching and Detaching TCDs.
This module provides the TCD with an abstract set of ISA bus services, allowing the TCD to be written independent of variations in the underlying BSP. In the case of the pcPentium BSP, on which the Wind River USB peripheral stack was developed and tested, each of the usbIsaLib functions is redirected to a corresponding function in usbPciLib. For a description of usbPciLib, see 1.2 Architecture Overview.
There is no requirement that a particular TCD be written to use usbIsaLib or even usbPciLib. These modules were designed to promote portability across BSP platforms. In systems where that is not an issue, developers can freely code "directly to the hardware" in the TCD itself.
Initializing usbTargLib is a two-step process. First, usbTargLib's entry point, usbTargInitialize( ), must be called at least once. The usbTargInitialize( ) routine initializes internal usbTargLib data structures and, in turn, calls the initialization entry points of other modules in the USB driver stack. For a given system, it is acceptable to call usbTargInitialize( ) once (perhaps during the boot sequence) or to call it many times (as during the initialization of each TCD or target application). The usbTargLib driver maintains a usage count that is incremented for each successful call to usbTargInitialize( ) and decremented for each corresponding call to usbTargShutdown( ). The driver only truly initializes itself when the usage count goes from zero to one, and it only truly "shuts down" when the usage count returns to zero.
The following code fragment illustrates the proper initialization and shutdown of usbTargLib:
... /* Initialize usbTargLib. */ if (usbTargInitialize () != OK) return ERROR; ... ... /* target application code. */ ... ... /* Shut down usbTargLib. Note: we only execute this code if the return * code from usbTargInitialize() was OK. Note also that there's really no * reason to check the result of usbTargShutdown(). */ usbTargShutdown ();
The second step of usbTargLib initialization is to attach at least one TCD to the driver, using usbTargLib's usbTargTcdAttach( ) routine. Normally, the attachment of TCDs to usbTargLib happens during the VxWorks boot sequence. However, usbTargLib is designed to allow TCDs to be attached (and detached) at run-time. This flexibility allows usbTargLib to support systems in which USB target controllers can be "hot-swapped" without restarting the system.
You decide when to attach TCDs to usbTargLib and when to detach them, if ever. In most peripheral applications, a single TCD and corresponding target application are attached to usbTargLib during the initial boot sequence and remain attached indefinitely.
In order to attach a TCD to usbTargLib, the caller passes the TCD's execution entry point and a TCD-defined parameter (the TCD attachment parameter) to the usbTargTcdAttach( ) routine. The caller must also provide the address of a callback table that identifies key entry points in the target application. The target application's callback table is described more fully in 2.2.4 Target Application Callback Table. Finally, the caller provides pointers to variables that receive information about the TCD attachment, including a handle to the attached TCD and information about the USB endpoints supported by the TCD.
In response to the usbTargTcdAttach( ) call, usbTargLib invokes the TCD's execution entry point with a TCD_FNC_ATTACH service request, passing it the same TCD attachment parameter provided by the caller. Use of this TCD-defined parameter can vary, however the Philips PDIUSBD12 example interprets this parameter as a pointer to a structure of the following form:
typedef struct usb_tcd_pdiusbd12_params
{
UINT32 ioBase; /* Base I/O address range */
UINT16 irq; /* IRQ channel (e.g., 5 = IRQ5) */
UINT16 dma; /* DMA channel (e.g., 3 = DMA3) */
} USB_TCD_PDIUSBD12_PARAMS, *pUSB_TCD_PDIUSBD12_PARAMS;
For the Philips evaluation kit, the caller must initialize this structure with settings corresponding to the Philips ISA bus board. For other target systems, these values can be hard-coded. The TCD uses the information in this structure to locate and manage the specified target controller.
The following code fragment demonstrates how to attach the Philips evaluation kit to usbTargLib:
/* Attach the Philips PDIUSBD12 evaluation kit TCD to usbTargLib. */
USB_TCD_PDIUSBD12_PARAMS params;
memset (¶ms, 0, sizeof (params));
params.ioBase = (UINT32) 0x368;
params.irq = (UINT16) 5;
params.dma = (UINT16) 7;
if (usbTargTcdAttach (
usbTcdPdiusbd12EvalExec, /* TCD's entry point */
(pVOID) ¶ms, /* TCD-specific parameter */
callbackTable, /* target app's callback table */
/* is defined elsewhere */
callbackParam, /* target app-specific parameter */
&targChannel, /* "handle" returned by usbTargLib */
&numEndpoints, /* nbr of endpoints supported by TCD */
&pEndpoints,) /* an array of info about endpoints */)
== OK)
{
/* TCD & target application were initialized successfully. */
/* Do post-attach processing. */
...
...
/* Now enable the TCD. */
if (usbTargEnable (targChannel) != OK)
{
/* Failed to enable TCD. */
}
}
else
{
/* Failed to attach the TCD & initialize the target application. */
}
In some situations, the target application may not be ready to begin handling USB requests as soon as it calls usbTargTcdAttach( ). For example, the target application may need to store and process the endpoint information returned by usbTargTcdAttach( ) first. For this reason, usbTargLib provides two additional functions to enable and disable the TCD.
The routine usbTargEnable( ) enables the specified TCD. The TCD, in turn, enables the underlying USB target controller. By convention, the target controller should not turn on its USB drivers until the TCD has been enabled by calling usbTargEnable( ). Thus, until usbTargEnable( ) has been called, the target controller, and thus the peripheral, is not visible on the USB.
The routine usbTargDisable( ) disables the specified TCD and is generally called just before detaching a TCD.
The previous code fragment in 2.2.2 Attaching and Detaching TCDs illustrates one use of the usbTargEnable( ) function.
USB peripherals never initiate activity on the USB1 . Instead, all USB activity is initiated by the USB host. The architecture of the USB peripheral stack reflects this in the way communication is handled between usbTargLib and the target application. Once the TCD and its associated target application have been attached to usbTargLib, usbTargLib asynchronously calls back into the target application by way of the entry points exposed in the target application's callback table. The callback table takes the following form:
typedef struct usb_targ_callback_table
{
/* device management callbacks */
USB_TARG_MANAGEMENT_FUNC mngmtFunc;
/* Control pipe callbacks */
USB_TARG_FEATURE_CLEAR_FUNC featureClear;
USB_TARG_FEATURE_SET_FUNC featureSet;
USB_TARG_CONFIGURATION_GET_FUNC configurationGet;
USB_TARG_CONFIGURATION_SET_FUNC configurationSet;
USB_TARG_DESCRIPTOR_GET_FUNC descriptorGet;
USB_TARG_DESCRIPTOR_SET_FUNC descriptorSet;
USB_TARG_INTERFACE_GET_FUNC interfaceGet;
USB_TARG_INTERFACE_SET_FUNC interfaceSet;
USB_TARG_STATUS_GET_FUNC statusGet;
USB_TARG_ADDRESS_SET_FUNC addressSet;
USB_TARG_SYNCH_FRAME_GET_FUNC synchFrameGet;
USB_TARG_VENDOR_SPECIFIC_FUNC vendorSpecific;
} USB_TARG_CALLBACK_TABLE, *pUSB_TARG_CALLBACK_TABLE;
Before calling the usbTargTcdAttach( ) routine, the function pointers in this table should be set to point to corresponding entry points in the target application. As soon as usbTargTcdAttach( ) is called, usbTargLib begins making asynchronous calls to the entry points in the callback table. (Some callbacks are immediate and occur during the attach process itself; others happen in response to activity on the USB.) The target application should provide pointers for each function whose requests need software processing. The usbTargLib driver provides default handling for any functions whose corresponding entry in the callback table is NULL.
The prototypes for each of these callbacks are defined in usbTargLib.h. Two parameters are common to each of the callback functions:
pVOID param USB_TARG_CHANNEL targChannel
The target driver usbTargLib invokes the target application's mngmtFunc( ) callback to inform the application of changes that broadly affect the USB's operation. This callback takes the following form:
typedef STATUS (*USB_TARG_MANAGEMENT_FUNC) ( pVOID param, USB_TARG_CHANNEL targChannel, UINT16 mngmtCode /* management code */ );
The mngmtCode parameter tells the target application what management event has happened. It takes the following values, as defined in usbTcd.h:
TCD_MNGMT_ATTACH initial TCD attachment TCD_MNGMT_DETACH TCD detach TCD_MNGMT_VBUS_DETECT bus detected TCD_MNGMT_VBUS_LOST Vbus lost TCD_MNGMT_BUS_RESET bus reset TCD_MNGMT_SUSPEND suspend signaling detected TCD_MNGMT_RESUME resume signaling detected
The target application can process or ignore these management codes as it sees fit. In most cases, usbTargLib ignores the status returned by the mngmtFunc( ) callback. However, usbTargLib does examine the mngmtFunc( ) return code in response to the TCD_MNGMT_ATTACH code. In this case, if the mngmtFunc( ) returns ERROR, TCD attachment is aborted.
The target driver provides default handling for the mngmtFunc( ) callback. The default handler always returns OK.
The target driver usbTargLib provides default processing for all standard requests received on the peripheral's default control pipe (endpoint #0). For each of the standard USB control requests, usbTargLib provides some form of default processing. The following callbacks can be provided by the target application to modify default processing.
The target driver usbTargLib invokes the target application's featureClear( ) and featureSet( ) callbacks in response to the USB CLEAR_FEATURE and SET_FEATURE requests, respectively. These callbacks take the following form:
typedef STATUS (*USB_TARG_FEATURE_CLEAR_FUNC) ( pVOID param, USB_TARG_CHANNEL targChannel, UINT8 requestType, UINT16 feature, UINT16 index );
typedef STATUS (*USB_TARG_FEATURE_SET_FUNC) ( pVOID param, USB_TARG_CHANNEL targChannel, UINT8 requestType, UINT16 feature, UINT16 index );
If the target application supports these functions, it should clear or set the feature identified by the requestType, feature, and index parameters.
If the target application does not provide these callbacks, then usbTargLib returns an error in response to the corresponding requests. Similarly, if the callbacks return ERROR, usbTargLib returns an error in response to the corresponding request.
The target driver usbTargLib invokes the target application's configurationGet( ) and configurationSet( ) callbacks in response to the USB GET_CONFIGURATION and SET_CONFIGURATION requests. These callbacks take the following form:
typedef STATUS (*USB_TARG_CONFIGURATION_GET_FUNC) ( pVOID param, USB_TARG_CHANNEL targChannel, pUINT8 pConfiguration ); typedef STATUS (*USB_TARG_CONFIGURATION_SET_FUNC) ( pVOID param, USB_TARG_CHANNEL targChannel, UINT8 configuration );
For the configurationGet( ) callback, the target application should fill the pConfiguration buffer with the UINT8 value identifying the current configuration; usbTargLib transmits the response back to the USB host. For configurationSet( ), the target application should set the current configuration to the value indicated by configuration.
If the target application does not provide these callbacks, then usbTargLib returns an error in response to the corresponding requests. Similarly, if the callbacks return ERROR, usbTargLib returns an error in response to the corresponding request.
The target driver usbTargLib invokes the target application's descriptorGet( ) and descriptorSet( ) callbacks in response to the USB GET_DESCRIPTOR and SET_DESCRIPTOR requests. These callbacks take the following form:
typedef STATUS (*USB_TARG_DESCRIPTOR_GET_FUNC) ( pVOID param, USB_TARG_CHANNEL targChannel, UINT8 requestType, UINT8 descriptorType, UINT8 descriptorIndex, UINT16 languageId, UINT16 length, pUINT8 pBfr, pUINT16 pActLen ); typedef STATUS (*USB_TARG_DESCRIPTOR_SET_FUNC) ( pVOID param, USB_TARG_CHANNEL targChannel, UINT8 requestType, UINT8 descriptorType, UINT8 descriptorIndex, UINT16 languageId, UINT16 length );
The requestType, descriptorType, descriptorIndex, and languageId parameters identify the descriptor requested by the USB host. For the descriptorGet( ) callback, the target application should fill the pBfr buffer with the requested descriptor (not to exceed the buffer size specified by length). The target application should also set the pActLen variable to the length of the descriptor placed in pBfr. The target driver transmits the descriptor back to the USB host.
In the case of descriptorSet( ), the target application must use the usbTargControlPayloadRcv( ) routine (see Control Pipe Data Transfers) to receive the descriptor being sent by the USB host, then process it appropriately.
If the target application does not provide these callbacks, usbTargLib returns an error in response to the corresponding requests. Similarly, if the callbacks return ERROR, usbTargLib returns an error in response to the corresponding request.
The target driver usbTargLib invokes the target application's interfaceGet( )) and interfaceSet( )) callbacks in response to the USB GET_DESCRIPTOR and SET_DESCRIPTOR requests. These callbacks take the following form:
typedef STATUS (*USB_TARG_INTERFACE_GET_FUNC) ( pVOID param, USB_TARG_CHANNEL targChannel, UINT16 interfaceIndex, pUINT8 pAlternateSetting ); typedef STATUS (*USB_TARG_INTERFACE_SET_FUNC) ( pVOID param, USB_TARG_CHANNEL targChannel, UINT16 interfaceIndex, UINT8 alternateSetting );
For the interfaceGet( ) callback, the target application should store its current interface setting in the variable pAlternateSetting, for the interface specified by interfaceIndex. For interfaceSet( ), the target application should set its interface according to the interfaceIndex and alternateSetting parameters.
If the target application does not provide these callbacks, then usbTargLib returns an error in response to the corresponding requests. Similarly, if the callbacks return ERROR, usbTargLib returns an error in response to the corresponding request.
The target driver usbTargLib invokes the target application's statusGet( ) callback in response to the USB GET_STATUS request. This callback takes the following form:
typedef STATUS (*USB_TARG_STATUS_GET_FUNC) ( pVOID param, USB_TARG_CHANNEL targChannel, UINT16 requestType, UINT16 index, UINT16 length, pUINT8 pBfr, pUINT16 pActLen );
If the target application supports this callback, it should store the appropriate status in pBfr (not to exceed the length specified by length). It should also store the length of the status in pActLen. The target driver sends the status back to the USB host.
If the target application does not provide this callback, usbTargLib returns an error in response to GET_STATUS requests. Similarly, if the statusGet( ) callback returns ERROR, then usbTargLib returns an error in response to the corresponding request.
The target driver usbTargLib invokes the target application's addressSet( ) callback in response to the USB SET_ADDRESS request. This callback takes the following form:
typedef STATUS (*USB_TARG_ADDRESS_SET_FUNC) ( pVOID param, USB_TARG_CHANNEL targChannel, UINT16 deviceAddress );
The target driver handles the SET_ADDRESS request automatically whether or not the target application supports this callback. The current implementation of usbTargLib ignores the STATUS returned by this callback.
The target driver usbTargLib invokes the target application's synchFrameGet( ) callback in response to the USB GET_SYNCH_FRAME request. This callback takes the following form:
typedef STATUS (*USB_TARG_SYNCH_FRAME_GET_FUNC) ( pVOID param, USB_TARG_CHANNEL targChannel, UINT16 endpoint, pUINT16 pFrameNo );
Typically, this function is used to synchronize isochronous data transfers. If the application supports it, then the callback should store the synch frame information in pFrameNo and return OK. The target driver sends the response to the USB host.
If the target application does not provide this callback, usbTargLib returns an error in response to GET_SYNCH_FRAME requests. Similarly, if the synchFrameGet( ) callback returns ERROR, usbTargLib returns an error in response to the corresponding request.
All requests received on the peripheral's default control pipe and not recognized as standard requests are routed to the target application's vendorSpecific( ) callback, if one is provided. This callback takes the following form:
typedef STATUS (*USB_TARG_VENDOR_SPECIFIC_FUNC) ( pVOID param, USB_TARG_CHANNEL targChannel, UINT8 requestType, UINT8 request, UINT16 value, UINT16 index, UINT16 length );
The target application's handling of vendor-specific requests varies widely. Some vendor-specific requests can require additional data transfer, perhaps using usbTargLib's usbTargControlPayloadRcv( ) or usbTargControlResponseSend( ) for support. If the target application handles the vendor-specific request successfully, it should return OK.
If the target application does not provide this callback, usbTargLib returns an error in response to a vendor-specific request. Similarly, if the vendorSpecific( ) callback returns ERROR, usbTargLib returns an error in response to the corresponding request.
In response to the usbTargTcdAttach( ) routine, usbTargLib returns the number of endpoints supported by the target and a pointer to an array describing each endpoint. Each entry in this array takes the following form:
typedef struct usb_targ_endpoint_info
{
UINT16 endpointId; /* TCD-assigned endpoint ID */
UINT16 flags; /* endpoint characteristics */
UINT16 endpointNumMask; /* mask of allowed endpoint numbers */
UINT32 ctlMaxPacketSize; /* max packet size in control mode */
UINT32 bulkInMaxPacketSize; /* max packet size in bulk IN mode */
UINT32 bulkOutMaxPacketSize; /* max packet size in bulk OUT mode */
UINT32 bulkInOutMaxPacketSize; /* max packet size in bulk I/O mode */
UINT32 intInMaxPacketSize; /* max packet size in intrpt IN mode */
UINT32 intOutMaxPacketSize; /* max packet size in intrpt OUT mode */
UINT32 intInOutMaxPacketSize; /* max packet size in intrpt I/O mode */
UINT32 isochInMaxPacketSize; /* max packet size in isoch IN mode */
UINT32 isochOutMaxPacketSize; /* max packet size in isoch OUT mode */
UINT32 isochInOutMaxPacketSize; /* max packet size in isoch I/O mode */
UINT16 endpointNum; /* currently assigned endpoint num */
UINT16 configuration; /* current configuration */
UINT16 interface; /* current interface */
UINT16 transferType; /* current transfer type */
UINT16 direction; /* current direction */
UINT16 maxPacketSize; /* max packet size as configured */
} USB_TARG_ENDPOINT_INFO, *pUSB_TARG_ENDPOINT_INFO;
The purpose of this structure is to give TCDs, and thus usbTargLib, a way to describe the endpoint capabilities of a particular TCD and target controller in a hardware-independent manner. The target application should treat this structure as read-only. Its fields are defined as follows:
TCD_ENDPOINT_IN_OK host->device capable TCD_ENDPOINT_OUT_OK device->host capable TCD_ENDPOINT_CTRL_OK capable of control xfr TCD_ENDPOINT_BULK_OK capable of bulk xfr TCD_ENDPOINT_INT_OK capable of interrupt xfr TCD_ENDPOINT_ISOCH_OK capable of isoch xfr TCD_ENDPOINT_IN_USE endpoint has pipe
While this abstract endpoint structure permits target applications to be written without prior knowledge of the capabilities of the underlying TCD and target controller, many target application developers are likely to bypass this abstraction and code their target applications to use specific endpoints based on their knowledge of the underlying hardware's capability.
All data transfers on a USB are initiated by the USB host. In general, a host creates a pipe--in other words, a connection--to a specific endpoint on a given peripheral. In addition to the address of the peripheral and the endpoint number, the pipe has other characteristics, such as the data type (control, bulk, interrupt, or isochronous), direction, and perhaps information describing the bandwidth required by the pipe. Having created a pipe, the USB host application requests data transfers by formatting IRPs (I/O request packets) and submitting them to the USB driver (called the USBD in the Wind River USB host stack).
The Wind River USB peripheral stack functions similarly. A target application creates pipes and attaches them to specific endpoints supported by the TCD and target controller. Pipes are created and destroyed using the routines usbTargPipeCreate( ) and usbTargPipeDestroy( ). As described further in Control Pipe Data Transfers, usbTargLib automatically creates the pipe for the default control endpoint (endpoint #0); so the target application itself omits the pipe creation step for the default control pipe.
After creating the necessary pipes, the target application formats USB_ERPs (USB endpoint request packets) which describe data to be transferred. When the USB host requests a transfer to or from a specific target endpoint, the USB peripheral stack responds by transferring data to or from USB_ERPs provided by the target application. USB_ERPs take the following form:
typedef struct usb_erp
{
LINK targLink; /* link field used internally by usbTargLib */
pVOID targPtr; /* ptr field for use by usbTargLib */
LINK tcdLink; /* link field used internally by USB TCD */
pVOID tcdPtr; /* ptr field for use by USB TCD */
pVOID userPtr; /* ptr field for use by client */
UINT16 erpLen; /* total length of ERP structure */
int result; /* ERP completion result: S_usbTcdLib_xxxx */
ERP_CALLBACK targCallback; /* usbTargLib completion callback routine */
ERP_CALLBACK userCallback; /* client's completion callback routine */
UINT16 endpointId; /* device endpoint */
UINT16 transferType; /* type of ERP: control, bulk, etc. */
UINT16 dataToggle; /* ERP should start with DATA0/DATA1. */
UINT16 bfrCount; /* indicates count of buffers in BfrList */
USB_BFR_LIST bfrList [1];
} USB_ERP, *pUSB_ERP;
There are numerous similarities between this data structure and the USB_IRP used by the Wind River USB host stack. The USB_BFR_LIST structure in the USB_ERP is identical to the one included as part of the USB_IRP.
As with the USB host stack, the USB peripheral stack automatically manages the dataToggle for ERPs; thus, the target application is not required to keep track of the current state of DAT0 and DATA1 for each pipe.
The usbTargLib driver automatically manages communication on the default control pipe (endpoint #0), making it unnecessary for the target application to create a pipe for this particular endpoint. However, this also means that the target application has no direct access to the default control pipe. For this reason, usbTargLib provides two routines that allow the target application to transfer data through the default control pipe:
Unlike the general data transfer routines (see General Data Transfers), these routines take as parameters only the TCD handle and a pointer to a USB_ERP. The TCD handle uniquely identifies the default control pipe, because there can only be one default control pipe for each TCD and target controller
Target applications typically use these routines when servicing USB requests like GET_DESCRIPTOR and SET_DESCRIPTOR, or when servicing vendor-specific requests.
In order to perform data transfers that do not involve the default control pipe, the target application must first create pipes as described 2.2.6 Data Transfer. In response to a request to create a pipe, usbTargLib returns a USB_TARG_PIPE handle to the target application. The target application must use this handle to identify the correct pipe in subsequent calls to usbTargTransfer( ) and usbTargTransferAbort( ).
The target application passes a USB_TARG_PIPE handle and a formatted USB_ERP to usbTargTransfer( ) to request a data transfer. The usbTargTransferAbort( ) routine allows previously submitted transfers to be aborted.
When the target application detects certain error conditions, it may wish to set a particular endpoint to the STALLed state. The STALL state is an indication to the USB host that an error has been detected.
The target driver usbTargLib provides the routine usbTargPipeStatusSet( ) which allows the target application to set the state of a pipe as STALLed or unSTALLed. In addition, this routine allows the target application to reset the next expected data toggle for the endpoint to DATA0, as may happen after the target receives other commands from the USB host, perhaps on the default control pipe.
The target application cannot set the STALL or unSTALL state directly for the default control pipe. The usbTargLib driver automatically sets the default control pipe to the STALL state whenever an error is detected in the processing of a request on the default control pipe. The target driver automatically unSTALLs the default control pipe whenever a new SETUP packet is received, as required by the USB Specification.
For more information on the STALL state, see the USB Specification.
Some target applications need to be able to drive USB RESUME signaling on the bus in order to "wake up" the USB host. In some systems, the USB is set to the SUSPEND state when the USB host enters a low-power mode; some devices (for example, keyboards) will want the ability to "wake up" the host from the low-power mode.
The usbTargLib driver provides the usbTargSignalResume( ) routine which allows a peripheral to drive RESUME signaling. This routine has no effect if the bus is not in the SUSPEND state; and there is no guarantee that a particular host will respond to USB RESUME signaling.
Each TCD exports a single entry point, its execution entry point. This function is of the following form:
typedef STATUS (*USB_TCD_EXEC_FUNC) (pVOID pTrb);
All requests to the TCD are made by constructing Target Request Blocks (TRB) and passing them to the TCD's execution entry point for processing. TRBs begin with a common header that is followed by parameters unique to the function being executed. The format of the header is:
typedef struct trb_header
{
TCD_HANDLE handle; /* I/O: caller's TCD client handle */
UINT16 function; /* IN: TCD function code */
UINT16 trbLength; /* IN: Length of the total TRB */
} TRB_HEADER, *pTRB_HEADER;
For the TCD_FNC_ATTACH request (see also TCD_FNC_ATTACH), the handle field is returned by the TCD to usbTargLib. For all other TRB requests, the handle field is passed by usbTargLib to the TCD.
The function field in TRB_HEADER defines one of the following functions as the TCD function to be executed:
A TCD must implement all of the above functions in order to perform properly.
The trbLength field is set by the caller to be the total length of the TRB, including the header and any additional parameters that follow. Each of the TCD functions listed above, including any associated TRB, is described in the remainder of this section.
typedef struct trb_attach
{
TRB_HEADER header; /* TRB header */
pVOID tcdParam; /* IN: TCD-specific parameter */
USB_TCD_MNGMT_CALLBACK mngmtCallback;
/* IN: caller's management callback */
pVOID mngmtCallbackParam; /* IN: param to mngmt callback */
UINT16 speed; /* OUT: target speed: USB_SPEED_xxxx */
UINT16 numEndpoints; /* OUT: number of endpoints supported */
pUSB_TARG_ENDPOINT_INFO pEndpoints;
/* OUT: ptr to array of endpoints */
} TRB_ATTACH, *pTRB_ATTACH;
|
|
NOTE:
The TCD should not enable the controller until usbTargLib invokes the TCD's TCD_FNC_ENABLE request (see TCD_FNC_ENABLE).
|
||||||||||||||||||
The usbTargLib driver invokes the TCD's TCD_FNC_ATTACH function when usbTargTcdAttach( ) is called. Unlike other TCD functions, usbTargLib does not store an existing TCD_HANDLE in the TRB_HEADER.handle field. Instead, upon successful completion of this routine, the TCD is expected to store a newly created TCD_HANDLE in the TRB_HEADER.handle field so it can be retrieved by usbTargLib. The usbTargLib driver uses the newly created TCD_HANDLE to identify the indicated target controller in subsequent calls to the TCD. Typically, the TCD casts the newly created TCD_HANDLE as a pointer to an internal TCD data structure used to manage a specific target controller.
The TCD-defined parameter--originally passed to the usbTargTcdAttach( ) routine--is passed to the TCD in the tcdParam field. The usbTargLib driver makes no attempt to interpret the meaning of this parameter. The TCD can use the tcdParam field as needed; for example, the Philips PDIUSBD12 TCD provided by Wind River casts this parameter to a pointer to a USB_TCD_PDIUSBD12_PARAMS structure (defined in usbTcdPdiusbd12EvalLib.h).
The mngmtCallback parameter is a pointer to a routine in usbTargLib that should be called asynchronously by the TCD when it detects certain USB management events. Each time the TCD invokes the routine pointed to by usbTargLib's mngmtCallback parameter, the TCD passes the value specified by mngmtCallbackParam.
typedef struct trb_detach
{
TRB_HEADER header; /* TRB header */
} TRB_DETACH, *pTRB_DETACH;
typedef struct trb_enable_disable
{
TRB_HEADER header; /* TRB header */
} TRB_ENABLE_DISABLE, *pTRB_ENABLE_DISABLE;
As described in 2.2.3 Enabling and Disabling TCDs, usbTargLib separates the attachment and enabling of a target controller into two steps. In response to the TCD_FNC_ENABLE request, the TCD enables the target controller's I/O drivers, making the peripheral visible on the USB (assuming it is currently connected to a USB).
By default, the target controller must respond to bus address 0 immediately after TCD_FNC_ENABLE.
This function shares the TRB used by TCD_FNC_ENABLE (see TCD_FNC_ENABLE).
In response to the TCD_FNC_DISABLE, the TCD disables the target controller's I/O drivers, in effect making the peripheral disappear from the USB to which it is attached. This function is generally requested only if the target application is in the process of shutting down.
typedef struct trb_address_set
{
TRB_HEADER header; /* TRB header */
UINT16 deviceAddress; /* IN: new device address */
} TRB_ADDRESS_SET, *pTRB_ADDRESS_SET;
In response to the TCD_FNC_ADDRESS_SET function, the TCD sets the target controller to begin responding to a new USB device address as specified by deviceAddress. Once the peripheral is enumerated by the USB host, the USB host assigns it a new address. The usbTargLib driver interprets the USB SET_ADDRESS request sent by the USB host and invokes the TCD_FNC_ADDRESS_SET request to change the target controller's address.
typedef struct trb_endpoint_assign
{
TRB_HEADER header; /* TRB header */
UINT16 endpointId; /* IN: TCD-assigned endpoint ID */
UINT16 endpointNum; /* IN: newly assigned endpoint num */
UINT16 configuration; /* IN: new configuration */
UINT16 interface; /* IN: new interface */
UINT16 transferType; /* IN: new transfer type */
UINT16 direction; /* IN: new direction */
} TRB_ENDPOINT_ASSIGN, *pTRB_ENDPOINT_ASSIGN;
In response to the TCD_FNC_ENDPOINT_ASSIGN request, the TCD enables the indicated hardware point as specified:
typedef struct trb_endpoint_release
{
TRB_HEADER header; /* TRB header */
UINT16 endpointId; /* IN: TCD-assigned endpoint Id */
} TRB_ENDPOINT_RELEASE, *pTRB_ENDPOINT_RELEASE;
typedef struct trb_signal_resume
{
TRB_HEADER header; /* TRB header */
} TRB_SIGNAL_RESUME, *pTRB_SIGNAL_RESUME;
typedef struct trb_endpoint_state_set
{
TRB_HEADER header; /* TRB header */
UINT16 endpointId; /* IN: endpoint Id */
UINT16 state; /* IN: TCD_ENDPOINT_xxxx bitmask */
} TRB_ENDPOINT_STATE_SET, *pTRB_ENDPOINT_STATE_SET;
typedef struct trb_current_frame_get
{
TRB_HEADER header; /* TRB header */
UINT16 frameNo; /* OUT: current frame number */
} TRB_CURRENT_FRAME_GET, *pTRB_CURRENT_FRAME_GET;
typedef struct trb_erp_submit
{
TRB_HEADER header; /* TRB header */
pUSB_ERP pErp; /* IN: ptr to IRP */
} TRB_ERP_SUBMIT, *pTRB_ERP_SUBMIT;
In response to the TCD_FNC_ERP_SUBMIT request, the TCD queues the specified USB_ERP for subsequent transfer on one of the target controller's endpoints. USB_ERPs are described in 2.2.6 Data Transfer.
typedef struct trb_erp_cancel
{
TRB_HEADER header; /* TRB header */
pUSB_ERP pErp; /* IN: ptr to IPR to be canceled */
} TRB_ERP_CANCEL, *pTRB_ERP_CANCEL;
The management callback to the TCD provided by usbTargLib as part of the TCD_FNC_ATTACH request gives the TCD a way to notify usbTargLib when the TCD detects asynchronous management events. The format of the callback is:
typedef VOID (*USB_TCD_MNGMT_CALLBACK) ( pVOID mngmtCallbackParam, /* caller-defined param */ TCD_HANDLE handle, /* channel */ UINT16 mngmtCode /* management code */ );
The mngmtCallbackParam parameter passed to the callback is the TRB.mngmtCallbackParam value originally passed to the TCD during the TCD_FNC_ATTACH request. The handle parameter is the TCD-assigned TCD_HANDLE for the corresponding target controller.
The values for mngmtCode are the same as those introduced in the description of the mngmtFunc( ) callback in mngmtFunc( ) Callback. The usbTargLib driver processes some of these events internally. Generally, usbTargLib passes all management events up to the target application through the target application's mngmtFunc( ) callback.
A number of standard TCD error codes are defined in usbTcd.h. You are encouraged to use the existing TCD error codes when creating new TCDs. These error codes are returned both in response to TCD function requests through the TCD's execution entry point, as well as in the result field of each USB_ERP processed by the TCD. To conform to Wind River conventions, the TCD should also set the system errno whenever returning an error. TCDs return the standard VxWorks constant, OK, when a function or ERP completes successfully. TCD error codes are as follows:
In particular, TCDs must store the error code S_usbTcdLib_ERP_CANCELED in the result field of USB_ERPs that have been canceled for any reason. Higher layers rely on this error code to recognize situations in which ERPs should not be retried. For their own convenience, TCDs can temporarily store other values in the USB_ERP result field while processing ERPs (such as a value indicating that the ERP is pending). However, before invoking an ERP's completion callback routine, the TCD is required to store either the constant OK or one of the above error codes in the USB_ERP result field.
Please see the Tornado Getting Started Guide for general instructions on how to install the USB Kit, and all other Wind River products.
The peripheral stack supplement to the USB Kit implements an example peripheral stack based on the Philips PDIUSBD12 USB controller chip. Included in the supplement are the firmware templates used to implement a rudimentary keyboard, a rudimentary printer, and a device called a PDIUSBD12 firmware emulator. This latter template was developed to utilize the Windows application included with the Philips PDIUSBD12 evaluation kit. The other two modules are designed as skeletons for creating either a printer or keyboard device. Together, the three modules demonstrate how three types of USB transfers (control, bulk, and interrupt) should be implemented.
The PDIUSBD12 firmware emulator is tested with a Windows 98 USB host using the application included with the Philips evaluation kit. The printer and keyboard emulator are tested on a VxWorks USB host using the test application usbTool.
Install the USB CD-ROM on top of your current Tornado installation. If you already have the USB Developer's Kit for the host installed, you only have to install the peripheral stack supplement:
If you are installing both the host stack source and libraries, as well as the peripheral stack supplement, you must install the above module and the following three parts:
For additional information about installation of the USB host stack, see 1. USB Host Stack and the USB Developer's Kit Release Notes. The remainder of this section contains information pertaining to installation of the peripheral stack only.
Source files related to the peripheral stack are created in the Tornado tree as follows. The header files for both host and peripheral stacks are installed:
Because this is a source code product, object files are not created during installation. The CD-ROM image has no binaries on it. To be able to use the peripheral stack components mentioned above to create a bootable VxWorks image, you must build the .o files from the command line, as follows:
make CPU=PENTIUM release
The following directories must be built:
The makefiles put object modules in the following directories:
The object files are also linked into their corresponding archive (.a) files in the target/lib directory.
After installing the USB peripheral stack, you can run it as a peripheral stack alone or in conjunction with the host stack.
Regardless of whether you are running the peripheral stack independently or with the host stack, there are two ways to use the Tornado project facility to configure and download projects:
From the Tornado project facility, start a downloadable VxWorks project based on your BSP. The peripheral stack supplement software provides components that support USB peripheral device functionalities.
For general instructions on using the project facility and creating projects, see the Tornado User's Guide: Projects.
The following steps configure a basic USB peripheral stack setup:
Select the USB Peripheral Stack component from the project facility component hierarchy to provide support for the Philips PDIUSBD12 target controller.
Select the usbTool component to include the usbTool module, which allows you to exercise the peripheral stack and its features, as well as the USB's host functions. For information about using usbTool, see 2.6 Working with usbTool.
The peripheral stack supplement includes a utility called usbTool, which you can use to exercise the modules comprising the USB peripheral stack. For example, if you are implementing a different firmware module, you can use usbTool to exercise and debug your code.
The usbTool utility provides a code skeleton which you can customize to suit your test needs.
For more information about how to run usbTool, see 1. USB Host Stack.
This section focuses on using usbTool to exercise the USB peripheral stack.
|
|
|||||||||||||||||||
Table 1 lists the usbTool commands available when their associated device components are included in your VxWorks image:
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
When using a serial connection to your target, the PC_CONSOLE component must be removed from your image. (Figure 3 shows the location of the PC_CONSOLE component.)
1: There is one exception: When the USB is in the SUSPEND state, a USB peripheral can initiate RESUME signaling, as described in 2.2.9 Resume Signaling.