Hide navigation sidebar
Hide table of contents sidebar
Skip to content
Back to top
Toggle table of contents sidebar

# Basic usage

Basic use is termed to mean each endpoint runs in its own dedicated thread. Multiple endpoints in a single thread are possible, please see Advanced usage.

Operation is synchronous in nature: The endpoint tasks make calls to blocking functions and waits for the transfer to complete before proceeding.

## XUD IO task

`XUD_Main()` is the main task that interfaces with the USB transceiver. It performs connection and handshaking on the USB bus as well as other bus-states such as suspend and resume. It also handles passing packets to/from the various endpoints.

This function should be called directly from the top-level `par` statement in `main()` to ensure that the XUD library is ready within the 100ms allowed by the USB specification (assuming a bus-powered device).

int XUD_Main(

  chanend c_epOut[],
  int noEpOut,
  chanend c_epIn[],
  int noEpIn,
  NULLABLE_RESOURCE(chanend, c_sof),
  XUD_EpType epTypeTableOut[],
  XUD_EpType epTypeTableIn[],
  XUD_BusSpeed_t desiredSpeed,
  XUD_PwrConfig pwrConfig,

)

  This performs the low-level USB I/O operations. Note that this needs to run in a thread with at least 80 MIPS worst case execution speed.

  Parameters:

    - c_epOut – An array of channel ends, one channel end per output endpoint (USB OUT transaction); this includes a channel to obtain requests on Endpoint 0.

    - noEpOut – The number of output endpoints, should be at least 1 (for Endpoint 0).

    - c_epIn – An array of channel ends, one channel end per input endpoint (USB IN transaction); this includes a channel to respond to requests on Endpoint 0.

    - noEpIn – The number of input endpoints, should be at least 1 (for Endpoint 0).

    - c_sof – A channel to receive SOF tokens on. This channel must be connected to a process that can receive a token once every 125 ms. If tokens are not read, the USB layer will lock up. If no SOF tokens are required `null` should be used for this parameter.

    - epTypeTableOut – See `epTypeTableIn`.

    - epTypeTableIn – This and `epTypeTableOut` are two arrays indicating the type of the endpoint. Legal types include: `XUD_EPTYPE_CTL` (Endpoint 0), `XUD_EPTYPE_BUL` (Bulk endpoint), `XUD_EPTYPE_ISO` (Isochronous endpoint), `XUD_EPTYPE_INT` (Interrupt endpoint), `XUD_EPTYPE_DIS` (Endpoint not used). The first array contains the endpoint types for each of the OUT endpoints, the second array contains the endpoint types for each of the IN endpoints.

    - desiredSpeed – This parameter specifies what speed the device will attempt to run at i.e. full-speed (ie 12Mbps) or high-speed (480Mbps) if supported by the host. Pass `XUD_SPEED_HS` if high-speed is desired or `XUD_SPEED_FS` if not. Low speed USB is not supported by XUD.

    - pwrConfig – Specifies whether the device is bus or self-powered. When self-powered the XUD will monitor the VBUS line for host disconnections. This is required for compliance reasons. Valid values are XUD_PWR_SELF and XUD_PWR_BUS.

### Endpoint type tables

The endpoint type tables are arrays of type `XUD_EpType` and are used to inform `lib_xud` about the endpoints in use. This information is used to indicate the transfer-type of each endpoint (bulk, control, isochronous or interrupt) as well as whether the endpoint wishes to be informed about bus-resets (see Status Reporting). Two tables are required, one for IN and one for OUT endpoints.

Suitable values are provided in the `XUD_EpTransferType` enum:

> - `XUD_EPTYPE_ISO`: Isochronous endpoint

> - `XUD_EPTYPE_INT`: Interrupt endpoint

> - `XUD_EPTYPE_BUL`: Bulk endpoint

> - `XUD_EPTYPE_CTL`: Control endpoint

> - `XUD_EPTYPE_DIS`: Disabled endpoint

OUT endpoint N will use index N of the output-endpoint-table, IN endpoint 0x8N will use index N of the inpout-endpoint-table. Endpoint 0 must exist in both tables.

..note:

    Endpoints that are not used will ``NAK`` any traffic from the host.

### `PwrConfig`

The `PwrConfig` parameter to `XUD_Main()` indicates if the device is bus or self-powered.

Valid values for this parameter are `XUD_PWR_SELF` and `XUD_PWR_BUS`.

When `XUD_PWR_SELF` is used, `XUD_Main()` monitors the VBUS input for a valid voltage and responds appropriately. The USB Specification states that the devices pull-ups must be disabled when a valid VBUS is not present. This is important when submitting a device for compliance testing since this is explicitly tested.

If the device is bus-powered `XUD_PWR_BUS` can be used since it is assumed that the device is not powered up when VBUS is not present and therefore no voltage monitoring is required. In this configuration the VBUS input to the device/PHY need not be present.

`XUD_PWR_BUS` can be used in order to run on a self-powered board without provision for VBUS wiring to the PHY/device, but this is not advised and is not USB specification compliant.

## VBUS monitoring

For self-powered devices it is important that `lib_xud` is aware of the VBUS state. This allows the device to disconnect its pull-up resistors from D+/D- and ensure the device does not have any voltage on the D+/D- pins when VBUS is not present. Compliance testing specifically checks for this in the USB Back Voltage test.

Warning

Failure to conform to this requirement will lead to an uncompliant device and likely lead to interoperability issues.

USB-enabled xcore-200 series devices have a dedicated VBUS pin which should be wired up as per the data-sheet recommendations including over-voltage protection.

For increasing flexibility, xcore.ai series devices do not have a dedicated VBUS pin. A generic IO port/pin should be used for this purpose (with appropriate external circuitry - see data-sheet recommendations).

`lib_xud` makes a call to a function `XUD_HAL_GetVBusState()` that which should be implemented to match the target hardware. For example:

    on tile[USB_TILE]: in port p_vbus = XS1_PORT_1P;

    unsigned int XUD_HAL_GetVBusState(void)
    {
        unsigned vBus;
        p_vbus :> vBus;
        return vBus;
    }

The function should return 1 if VBUS is present, otherwise 0. In the case of the example above, the validity of VBUS is directly represented by the value on port 1P, however, this may not be the case for all hardware implementations, it could be inverted or even require a read from an external IO expander, for example.

Note

VBUS need not be connected if the device is wholly powered by USB i.e. a bus powered device.

## USB_TILE define

In order that `lib_xua` may instantiate resources on the correct tile (typically ports) it requires a `USB_TILE` define to be set. The default value for the define is `tile[0]` so a developer only needs to set this if `XUD_Main()` is executing on a tile other than 0.

There are two ways of setting this define, either in the application CMakeLists.txt for example:

    set(APP_COMPILER_FLAGS -DUSB_TILE=tile[1])

Or, following XMOS software library convention, providing a xud_conf.h file in the application codebase. This header file will be automatically detected by the build system and used by `lib_xud`. Example content for this header file is as follows:

    #ifndef _XUD_CONF_H_
    #define _XUD_CONF_H_

    #define USB_TILE tile[1]

    #endif

## Data transfer

Communication state between an endpoint client task and the XUD IO task is encapsulated in an opaque type:

typedef unsigned int XUD_ep

  Typedef for endpoint identifiers.

All client calls communicating with the XUD library pass in this type. These data structures can be created at the start of execution of a client task with the following call that takes as an argument the endpoint channel connected to the XUD library:

XUD_ep XUD_InitEp(chanend c_ep)

  Initialises an XUD_ep.

  Parameters:

    - c_ep – Endpoint channel to be connected to the XUD library.

  Returns:

    Endpoint identifier

Endpoint data is sent/received using three main functions, `XUD_SetBuffer()`, `XUD_GetBuffer()` and `XUD_GetSetupBuffer()`.

These functions implement the low-level shared memory/channel communication with the `XUD_Main()` task.

These functions will automatically deal with any low-level complications required such as Packet ID (PID) toggling etc.

### `XUD_SetBuffer()`

XUD_Result_t XUD_SetBuffer(

  XUD_ep ep_in,
  unsigned char buffer[],
  unsigned datalength,

)

  This function must be called by a thread that deals with an IN endpoint. When the host asks for data, the low-level driver will transmit the buffer to the host.

  Parameters:

    - ep_in – The endpoint identifier (created by `XUD_InitEp`).

    - buffer – The buffer of data to transmit to the host.

    - datalength – The number of bytes in the buffer.

  Returns:

    XUD_RES_OKAY on success, for errors see `Status Reporting`_.

### `XUD_GetBuffer()`

XUD_Result_t XUD_GetBuffer(

  XUD_ep ep_out,
  unsigned char buffer[],
  REFERENCE_PARAM(unsigned, length),

)

  This function must be called by a thread that deals with an OUT endpoint. When the host sends data, the low-level driver will fill the buffer. It pauses until data is available.

  Parameters:

    - ep_out – The OUT endpoint identifier (created by `XUD_InitEP`).

    - buffer – The buffer in which to store data received from the host. The buffer is assumed to be word aligned.

    - length – The number of bytes written to the buffer

  Returns:

    XUD_RES_OKAY on success, for errors see `Status Reporting`_.

### `XUD_GetSetupBuffer()`

XUD_Result_t XUD_GetSetupBuffer(

  XUD_ep ep_out,
  unsigned char buffer[],
  REFERENCE_PARAM(unsigned, length),

)

  Request setup data from usb buffer for a specific endpoint, pauses until data is available.

  Parameters:

    - ep_out – The OUT endpoint identifier (created by `XUD_InitEP`).

    - buffer – A char buffer passed by ref into which data is returned.

    - length – Length of the buffer received (expect 8 bytes)

  Returns:

    XUD_RES_OKAY on success, for errors see `Status Reporting`_.

For user convenience these functions are wrapped up in functions that match commonly required packet sequences:

### `XUD_SetBuffer_EpMax()`

This function provides a similar function to `XUD_SetBuffer` function but it breaks the data up in packets of a fixed maximum size. This is especially useful for control transfers where large descriptors must be sent in typically 64 byte transactions.

XUD_Result_t XUD_SetBuffer_EpMax(

  XUD_ep ep_in,
  unsigned char buffer[],
  unsigned datalength,
  unsigned epMax,

)

  Similar to XUD_SetBuffer but breaks up data transfers into smaller packets. This function must be called by a thread that deals with an IN endpoint. When the host asks for data, the low-level driver will transmit the buffer to the host.

  Parameters:

    - ep_in – The IN endpoint identifier (created by `XUD_InitEp`).

    - buffer – The buffer of data to transmit to the host.

    - datalength – The number of bytes in the buffer.

    - epMax – The maximum packet size in bytes.

  Returns:

    XUD_RES_OKAY on success, for errors see `Status Reporting`_.

### `XUD_DoGetRequest()`

XUD_Result_t XUD_DoGetRequest(

  XUD_ep ep_out,
  XUD_ep ep_in,
  unsigned char buffer[],
  unsigned length,
  unsigned requested,

)

  Performs a combined `XUD_SetBuffer` and `XUD_GetBuffer`. It transmits the buffer of the given length over the `ep_in` endpoint to answer an IN request, and then waits for a 0 length Status OUT transaction on `ep_out`. This function is normally called to handle Get control requests to Endpoint 0.

  Parameters:

    - ep_out – The endpoint identifier that handles Endpoint 0 OUT data in the XUD manager.

    - ep_in – The endpoint identifier that handles Endpoint 0 IN data in the XUD manager.

    - buffer – The data to send in response to the IN transaction. Note that this data is chopped up in fragments of at most 64 bytes.

    - length – Length of data to be sent.

    - requested – The length that the host requested, (Typically pass the value `wLength`).

  Returns:

    XUD_RES_OKAY on success, for errors see `Status Reporting`_

### `XUD_DoSetRequestStatus()`

XUD_Result_t XUD_DoSetRequestStatus(XUD_ep ep_in)

  This function sends an empty packet back on the next IN request with PID1. It is normally used by Endpoint 0 to acknowledge success of a control transfer.

  Parameters:

    - ep_in – The Endpoint 0 IN identifier to the XUD manager.

  Returns:

    XUD_RES_OKAY on success, for errors see `Status Reporting`_.

## Data transfer example

A simple endpoint task is shown below demonstrating basic data transfer to the host.

    void ExampleEndpoint(chanend c_ep_in)
    {
        char buffer[512];

        XUD_ep ep_to_host = XUD_InitEp(chan_ep_in);

        while(1)
        {
            XUD_SetBuffer(ep_to_host, buffer, 512);
        }
    }

## Status reporting

An endpoint can register for “status reporting” such that bus state can be known. This is achieved by ORing `XUD_STATUS_ENABLE` into the relevant endpoint in the endpoint type table.

This means that endpoints are notified of USB bus resets (and therefore bus-speed changes), suspend and resume events. The `lib_xud` access functions discussed previously (`XUD_GetBuffer`, `XUD_SetBuffer`, etc) return `XUD_RES_UPDATE` if a USB bus status change is detected.

An endpoint should then call the `XUD_GetBusState()` function to ascertain the new bus-state, this will return one of :

- `XUD_BUS_RESET`

- `XUD_BUS_SUSPEND`

- `XUD_BUS_RESUME`

If `XUD_BUS_RESET` is returned the endpoint must call the `XUD_ResetEndpoint()` function. This will return the current bus speed as a `XUD_BusSpeed_t` with the value `XUD_SPEED_FS` or `XUD_SPEED_HS`.

A reset notification is of particular importance if an endpoint task is expecting alternating IN and OUT transactions. For example, consider the case where an endpoint is always expecting the sequence OUT, IN, OUT (such as a control transfer or a request response protocol). If an unplug/reset event was received after the first OUT, the host would return to sending the initial OUT after a re-plug, whilst the endpoint task would hang trying to send a response the IN. The endpoint needs to know of the bus reset in order to reset its state machine.

Note

Endpoint 0 requires this functionality to be enabled since it deals with bi-directional control transfers

This functionality is also important for high-speed devices, since it is not guaranteed that a host will enumerate the device as a high-speed device, say if it’s plugged via full-speed hub.

If `XUD_BUS_SUSPEND` or `XUD_BUS_RESUME` is returned by `XUD_GetBusState()` then the endpoint should perform any desired functionality - for example powering up/down external circuitry, reducing xcore clock frequency etc, before acknowledging the update with `XUD_AckBusState()`.

### `XUD_ResetEndpoint()`

XUD_BusSpeed_t XUD_ResetEndpoint(

  XUD_ep one,
  NULLABLE_REFERENCE_PARAM(XUD_ep, two),

)

  This function will complete a reset on an endpoint. Can take one or two `XUD_ep` as parameters (the second parameter can be set to `null`). The return value should be inspected to find the new bus-speed. In Endpoint 0 typically two endpoints are reset (IN and OUT). In other endpoints `null` can be passed as the second parameter.

  Parameters:

    - one – IN or OUT endpoint identifier to perform the reset on.

    - two – Optional second IN or OUT endpoint structure to perform a reset on.

  Returns:

    Either `XUD_SPEED_HS` - the device is now running as a high-speed device or `XUD_SPEED_FS` - the device is now running as full speed device.

### `XUD_GetBusState()()`

XUD_BusState_t XUD_GetBusState(

  XUD_ep one,
  NULLABLE_REFERENCE_PARAM(XUD_ep, two),

)

  If an API function returns XUD_RES_UPDATE a bus update notification is available. The endpoint must now call this function to receive the bus update - these updates represent suspend, resume, reset and kill.

  Parameters:

    - one – IN or OUT endpoint identifier to receive update on.

    - two – Optional second IN or OUT endpoint structure to receive update on.

  Returns:

    Either: XUD_BUS_SUSPEND - the host has suspended the device. The Endpoint should perform any desired suspend related functionality and then must call XUD_AckBusState() to inform XUD that it has been accepted. XUD_BUS_RESUME - the host has resumed the device. The Endpoint should perform any desired resume related functionality and then must call XUD_AckBusState() to inform XUD that it has been accepted. XUD_BUS_RESET - the host has issued a bus reset. The endpoint must now call XUD_ResetEndpoint(). XUD_BUS_KILL - indicate that the USB stack has been shut down by another part of the user code (using XUD_Kill). If this value is returned, the endpoint code should call XUD_CloseEndpoint() and then terminate.

### `XUD_AckBusState()`

XUD_Result_t XUD_AckBusState(XUD_ep one, NULLABLE_REFERENCE_PARAM(XUD_ep, two))

  Must be called if an endpoint has received XUD_BUS_RESUME or XUD_BUS_SUSPEND in order to acknowledge the bus state update. Any related actions should be performed (i.e. clocking down the core) before calling this function.

  Parameters:

    - one – IN or OUT endpoint identifier to send the ack on.

    - two – Optional second IN or OUT endpoint structure send the ack on.

  Returns:

    XUD_RES_OKAY on success, for errors see `Status Reporting`_.

## Status reporting example

A simple endpoint task is shown below demonstrating basic data transfer to the host and bus status inspection.

    void ExampleEndpoint(chanend c_ep_in)
    {
        char buffer[512];
        XUD_Result_t result;

        XUD_ep ep_to_host = XUD_InitEp(chan_ep_to_host);

        while(1)
        {
            if((result = XUD_SetBuffer(ep_to_host, buffer, 512)) == XUD_RES_UPDATE)
            {
                XUD_BusState_t busState = XUD_GetBusState(ep_to_host, null);
                switch(busState):
                {
                    case XUD_BUS_RESET:
                        XUD_ResetEndpoint(ep_to_host, null);
                        break;

                    case XUD_BUS_SUSPEND:
                        // Perform any suspend related functionality
                        XUD_AckBusState(ep_to_host, null);
                        break;

                    case XUD_BUS_RESUME:
                        // Perform any resume related functionality
                        XUD_AckBusState(ep_to_host, null);
                        break;
                }
            }
        }
    }

## SOF channel

An application can pass an optional channel-end to the `c_sof` parameter of `XUD_Main()`. This will cause a word of data to be output every time the device receives a SOF (Start Of Frame) packet from the host. This can be used for timing information in audio devices etc.

If this functionality is not required `null` should be passed as the parameter.

Note

If an optional channel-end is passed into `XUD_Main()` there must be a responsive task ready to receive SOF notifications otherwise the `XUD_Main()` task will be blocked attempting to send these messages leading to it being unresponsive to the host.

## Halting

The USB specification requires the ability for an endpoint to send a STALL response to the host if an endpoint is halted, or if control pipe request is not supported. `lib_xud` provides various functions to support this. In some cases it is convenient to use the `XUD_ep` whilst in other cases it is easier to use the endpoint address. Functions to use either are provided.

### `XUD_SetStall()`

void XUD_SetStall(XUD_ep ep)

  Mark an endpoint as STALLed. It is cleared automatically if a SETUP received on the endpoint.

  Warning

  Must be run on same tile as XUD core

  Parameters:

    - ep – XUD_ep type.

### `XUD_SetStallByAddr()`

void XUD_SetStallByAddr(int epNum)

  Mark an endpoint as STALL based on its EP address. Cleared automatically if a SETUP received on the endpoint. Note: the IN bit of the endpoint address is used.

  Warning

  Must be run on same tile as XUD core

  Parameters:

    - epNum – Endpoint number.

### `XUD_ClearStall()`

void XUD_ClearStall(XUD_ep ep)

  Mark an endpoint as NOT STALLed.

  Warning

  Must be run on same tile as XUD core

  Parameters:

    - ep – XUD_ep type.

### `XUD_ClearStallByAddr()`

void XUD_ClearStallByAddr(int epNum)

  Mark an endpoint as NOT STALLed based on its EP address. Note: the IN bit of the endpoint address is used.

  Warning

  Must be run on same tile as XUD core

  Parameters:

    - epNum – Endpoint number.

## USB test modes

`lib_xud` supports the required test modes for USB Compliance testing.

`lib_xud` accepts commands from the endpoint 0 channels (in or out) to signal which test mode to enter via the `XUD_SetTestMode()` function. The commands are based on the definitions of the Test Mode Selector Codes in the USB 2.0 Specification Table 11-24. The supported test modes are summarised in Table 3.

Table 3 Supported Test Mode Selector Codes
Value | Test Mode Description
--- | ---
1 | Test_J
2 | Test_K
3 | Test_SE0_NAK
4 | Test_Packet

The passing other codes endpoints other than 0 to `XUD_SetTestMode()` could result in undefined behaviour.

As per the USB 2.0 Specification a power cycle or reboot is required to exit the selected test mode.

### `XUD_SetTestMode()`

void XUD_SetTestMode(XUD_ep ep, unsigned testMode)

  Enable a specific USB test mode in XUD.

  Warning

  Must be run on same tile as XUD core

  Parameters:

    - ep – XUD_ep type (must be endpoint 0 in or out)

    - testMode – The desired test-mode