ThreadX docsThreadX docs
ThreadX
ThreadX Modules
NetX Duo
FileX
GUIX
USBX
TraceX
LevelX
ThreadX
ThreadX Modules
NetX Duo
FileX
GUIX
USBX
TraceX
LevelX
  • USBX Documentation

    • Understand USBX
    • USBX documentation
    • Chapter 1 - Introduction to USBX Device Stack
    • Chapter 2 - USBX Device Stack Installation
    • Chapter 3 - Functional Components of USBX Device Stack
    • Chapter 4 - Description of USBX Device Services
    • Chapter 5 - USBX Device Class Considerations
    • Chapter 1 - Introduction to the USBX Device Stack User Guide Supplement
    • Chapter 2 - USBX Device Class Considerations
    • Chapter 3 - USBX DPUMP Class Considerations
    • Chapter 4 -USBX Pictbridge implementation
    • Chapter 5 - USBX OTG
    • Chapter 6 - USBX Implement controller driver (DCD)
    • Chapter 6 - USBX Implement device class modules
    • USBX Device Stack User Guide
    • Chapter 1 - Introduction to USBX Host Stack
    • Chapter 2 - USBX Host Stack Installation
    • Chapter 3 - Functional Components of USBX Host Stack
    • Chapter 4 - Description of USBX Host Services
    • Chapter 5 - USBX Host Classes API
    • Chapter 6 - USBX CDC-ECM Class Usage
    • Chapter 1 - Introduction to the USBX Host Stack User Guide Supplement
    • USBX Host Classes API
    • USBX DPUMP Class Considerations
    • USBX Pictbridge implementation
    • USBX OTG
    • USBX Host Stack User Guide

Chapter 6 - USBX Implement controller driver (DCD)

Device Controller Driver (DCD)

The device controller driver is a driver layer to provide a unique way to access different USB hardware peripherals.

DCD for USBX Device Stack

The USBX device stack accesses device controller through a unique struct of device controller driver (DCD). After USBX system initialization (ux_system_initialize), the USBX DCD struct (UX_SLAVE_DCD) is allocated and can be referenced by _ux_system_slave -> ux_system_slave_dcd.

DCD for specific USB controller hardware

For different USB controller hardware the DCD struct is initialized by specific initialize function, called in application. The actual implements are then linked for USBX device stack to communicate, so that the USB flow can be performed on actual physical device. There are three parts in such a interface:

  • APIs for application
  • APIs for USBX stack
  • USB events (callbacks) to call USBX stack

APIs for application

  • _ux_dcd_xxxxx_initialize
  • _ux_dcd_xxxx_uninitialize

_ux_dcd_xxxxx_initialize

The initialize function for specific DCD is called by USBX application, after ux_system_initialize, ux_device_stack_initialize and ux_device_stack_class_register. It allocates necessary resources for the driver. Usually DCD operates directly on hardware, so the initialize function initialize hardware and connect USB.

Implement example is as following:

/**************************************************************************/
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */
/*    This function initializes the USB device controller of the XXXXX    */
/*    microcontroller in USBX device system.                              */
/*                                                                        */
/*  INPUT                                                                 */
/*                                                                        */
/*    dcd_io                                Driver specific parameter 1   */
/*    parameter                             Driver specific parameter 2   */
/*                                                                        */
/*  OUTPUT                                                                */
/*                                                                        */
/*    Completion Status                                                   */
/*                                                                        */
/*  CALLED BY                                                             */
/*                                                                        */
/*    Application                                                         */
/*                                                                        */
/**************************************************************************/
UINT  _ux_dcd_xxx_initialize(ULONG dcd_io, ULONG parameter)
{

UX_SLAVE_DCD            *dcd;
UX_DCD_XXXXX            *dcd_xxxxx;


    UX_PARAMETER_NOT_USED(dcd_io);

    /* Get the pointer to the DCD.  */
    dcd =  &_ux_system_slave -> ux_system_slave_dcd;

    /* The controller initialized here is of XXXXX type.  */
    dcd -> ux_slave_dcd_controller_type =  UX_DCD_XXXXX_SLAVE_CONTROLLER;

    /* Allocate memory for this XXXXX DCD instance.  */
    dcd_xxxxx =  _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_DCD_XXXXX));

    /* Check if memory was properly allocated.  */
    if(dcd_xxxxx == UX_NULL)
        return(UX_MEMORY_INSUFFICIENT);

    /* Set the pointer to the XXXXX DCD.  */
    dcd -> ux_slave_dcd_controller_hardware =  (VOID *) dcd_xxxxx;

    /* Initialize the function collector for this DCD.  */
    dcd -> ux_slave_dcd_function =  _ux_dcd_xxxxx_function;

    /* Set the generic DCD owner for the XXXXX DCD.  */
    dcd_xxxxx -> ux_dcd_xxxxx_dcd_owner =  dcd;

    /* Initialize XXXXX DCD.  */
    /* TODO: Initialize XXXXX DCD struct fields.  */
    /* TODO: if not initialized in application (after invoking this function),
             initialize controller hardware and attach to host.  */

    /* Set the state of the controller to OPERATIONAL now.  */
    dcd -> ux_slave_dcd_status =  UX_DCD_STATUS_OPERATIONAL;

    /* Return successful completion.  */
    return(UX_SUCCESS);
}

_ux_dcd_xxxx_uninitialize (optional)

The uninitialize function for specific DCD is optional, since the USB device system is usually enabled and always ready to serve. In some cases, it is called by USBX application, if the application needs a way to disconnect USB, completely free resources and uninitialize hardware. Code example is as following:

/**************************************************************************/
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */
/*    This function uninitializes the USB device controller in USBX       */
/*    device system.                                                      */
/*                                                                        */
/*  INPUT                                                                 */
/*                                                                        */
/*    dcd_io                                Driver specific parameter 1   */
/*    parameter                             Driver specific parameter 2   */
/*                                                                        */
/*  OUTPUT                                                                */
/*                                                                        */
/*    Completion Status                                                   */
/*                                                                        */
/*  CALLED BY                                                             */
/*                                                                        */
/*    Application                                                         */
/*                                                                        */
/**************************************************************************/
UINT  _ux_dcd_xxxxx_uninitialize(ULONG dcd_io, ULONG parameter)
{
UX_SLAVE_DCD            *dcd;
UX_DCD_XXXXX            *dcd_xxxxx;
    UX_PARAMETER_NOT_USED(dcd_io);
    UX_PARAMETER_NOT_USED(parameter);
    /* Get the pointer to the DCD.  */
    dcd =  &_ux_system_slave -> ux_system_slave_dcd;
    /* Set the state of the controller to HALTED now.  */
    dcd -> ux_slave_dcd_status =  UX_DCD_STATUS_HALTED;
    /* Get controller driver.  */
    dcd_xxxxx = (UX_DCD_XXXXX *)dcd -> ux_slave_dcd_controller_hardware;
    /* Free XXXXX driver.  */
    {
        _ux_utility_memory_free(dcd_xxxxx);
        dcd -> ux_slave_dcd_controller_hardware = UX_NULL;
    }
    /* TODO: If not uninitialized in application (after invoking this function),
             detach from host and uninitialize the controller.  */
    return(UX_SUCCESS);
}

Stack APIs for USBX

ux_dcd_xxxxx_function

In USBX device stack, the commands sent to controller is in a single API call defined as:

UINT ux_dcd_xxxxx_function(UX_SLAVE_DCD *dcd, UINT cmd, VOID *param);

The commands should be implemented as following:

commandparameterdescription
UX_DCD_TRANSFER_REQUEST/UX_DCD_TRANSFER_RUNUX_SLAVE_TRANSFER*Issue a transfer/run transfer state machine
UX_DCD_TRANSFER_ABORTUX_SLAVE_TRANSFER*Abort on going transfer
UX_DCD_CREATE_ENDPOINTUX_SLAVE_ENDPOINT*Create a physical endpoint
UX_DCD_DESTROY_ENDPOINTUX_SLAVE_ENDPOINT*Destroy a physical endpoint
UX_DCD_RESET_ENDPOINTUX_SLAVE_ENDPOINT*Reset an endpoint
UX_DCD_STALL_ENDPOINTUX_SLAVE_ENDPOINT*Stall an endpoint
UX_DCD_ENDPOINT_STATUSULONGReturn status of endpoint (index/address)
UX_DCD_SET_DEVICE_ADDRESSULONGSet the device address
UX_DCD_CHANGE_STATEULONG (UX_DEVICE_REMOTE_WAKEUP)Change USB state (to remote wakeup)
UX_DCD_ISR_PENDING/UX_DCD_TASKS_RUN-For standalone, run background task state machine
Note for command UX_DCD_CHANGE_STATE, parameter of UX_DEVICE_REMOTE_WAKEUP should be accepted and send remote wakeup signal to host.
Implement example is as following:
/**************************************************************************/
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */
/*    This function dispatches the DCD function internally to the XXXXX   */
/*    controller in USBX device system.                                   */
/*                                                                        */
/*    Following commands are supported:                                   */
/*    - UX_DCD_TRANSFER_REQUEST/UX_DCD_TRANSFER_RUN                       */
/*      - control transfer: mandatory for all devices;                    */
/*      - bulk/interrupt/isochronous transfer: optional if needed, e.g.,   */
/*        bulk is needed by CDC, Mass Storage classes,                    */
/*        interrupt is needed by HID class,                               */
/*        isochronous is needed by audio/video classes, etc.              */
/*    - UX_DCD_TRANSFER_ABORT                                             */
/*    - UX_DCD_CREATE_ENDPOINT                                            */
/*    - UX_DCD_DESTROY_ENDPOINT                                           */
/*    - UX_DCD_RESET_ENDPOINT                                             */
/*    - UX_DCD_ENDPOINT_STATUS                                            */
/*    - UX_DCD_SET_DEVICE_ADDRESS                                         */
/*    - UX_DCD_CHANGE_STATE                                               */
/*      - UX_DEVICE_REMOTE_WAKEUP: optional if remote wakeup is supported */
/*    - UX_DCD_ISR_PENDING/UX_DCD_TASKS_RUN                               */
/*                                                                        */
/*  INPUT                                                                 */
/*                                                                        */
/*    dcd                                   Pointer to device controller  */
/*    function                              Function requested            */
/*    parameter                             Pointer to function parameters*/
/*                                                                        */
/*  OUTPUT                                                                */
/*                                                                        */
/*    Completion Status                                                   */
/*                                                                        */
/*  CALLS                                                                 */
/*                                                                        */
/*                                                                        */
/*  CALLED BY                                                             */
/*                                                                        */
/*    USBX Device Stack                                                   */
/*                                                                        */
/**************************************************************************/
UINT  _ux_dcd_xxxxx_function(UX_SLAVE_DCD *dcd, UINT function, VOID *parameter)
{
UINT                status = UX_FUNCTION_NOT_SUPPORTED;
UX_DCD_XXXXX        *dcd_xxxxx;
    /* Check the status of the controller.  */
    if (dcd -> ux_slave_dcd_status == UX_UNUSED)
    {
        /* Error trap. */
        _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_DCD, UX_CONTROLLER_UNKNOWN);
        /* If trace is enabled, insert this event into the trace buffer.  */
        UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONTROLLER_UNKNOWN, 0, 0, 0, UX_TRACE_ERRORS, 0, 0)
        return(UX_CONTROLLER_UNKNOWN);
    }
    /* Get the pointer to the XXXXX DCD.  */
    dcd_xxxxx =  (UX_DCD_XXXXX *) dcd -> ux_slave_dcd_controller_hardware;
    /* Look at the function and route it.  */
    switch(function)
    {
    case UX_DCD_TRANSFER_REQUEST:
        /* TODO: controller level transfer state machine/transfer request, parameter: UX_SLAVE_TRANSFER *transfer. */
        /* Transfer types to support:
           Control: mandatory for all devices;
           Bulk: optional, for functions that need bulk endpoint (like CDC-ACM, Mass Storage, etc.);
           Interrupt: optional, for function that need interrupt endpoint (like HID, CDC-ACM, etc.);
           Isochronous: optional, for function that need Isochronous endpoint (like audio, video, etc.).
         */
#if defined(UX_DEVICE_STANDALONE)
        /* Must be non-blocking;
           in start state: start background transfer and return UX_STATE_NEXT;
           in pending state: return UX_STATE_WAIT;
           in done state: confirm transfer fields including completion code are updated and return UX_STATE_NEXT. */
        /* ......  */
#else
        /* control request: start background transfer for data stage followed by status stage.
        /* non-control: it's blocking, start background transfer and wait transfer->ux_slave_transfer_request_semaphore. */
        /* ......  */
#endif
        break;
    case UX_DCD_TRANSFER_ABORT:
        /* TODO: controller level transfer abort, parameter: UX_SLAVE_TRANSFER *transfer. */
        /* ......  */
        break;
    case UX_DCD_CREATE_ENDPOINT:
        /* TODO: controller level endpoint create, parameter: UX_SLAVE_ENDPOINT *endpoint. */
        /* ......  */
        break;
    case UX_DCD_DESTROY_ENDPOINT:
        /* TODO: controller level endpoint destroy, parameter: UX_SLAVE_ENDPOINT *endpoint. */
        /* ......  */
        break;
    case UX_DCD_RESET_ENDPOINT:
        /* TODO: controller level endpoint reset to clear halt and reset toggle, parameter: UX_SLAVE_ENDPOINT *endpoint.  */
        /* ......  */
        break;
    case UX_DCD_STALL_ENDPOINT:
        /* TODO: controller level endpoint stall, parameter: UX_SLAVE_ENDPOINT *endpoint. */
        /* ......  */
        break;
    case UX_DCD_SET_DEVICE_ADDRESS:
        /* TODO: update hardware to set address of the device, parameter: ULONG address.  */
        /* ......  */
        break;
    case UX_DCD_CHANGE_STATE:
        /* TODO: actively change USB state in controller level, parameter: ULONG state. It could be:
           - UX_DEVICE_REMOTE_WAKEUP: send remote wakeup signal on USB bus (if device needs the feature). */
        /* ......  */
        break;
    case UX_DCD_ENDPOINT_STATUS:
        /* TODO: get endpoint status (stall).. */
        /* If UX_DEVICE_BIDIRECTIONAL_ENDPOINT_SUPPORT is defined parameter is ULONG endpoint_address,
           which kept endpoint direction, in this case both 0x81 and 0x01 are accepted as different
           endpoints addresses.
           Otherwise, parameter is ULONG endpoint_index, which removes endpoint direction, in this case
           address of 0x81 and 0x01 both pass index 0x01 as parameter, so the index must be unique. */
        /* ......  */
        break;
#if defined(UX_DEVICE_STANDALONE)
    case UX_DCD_TASKS_RUN:
        /* TODO: controller level background tasks running. */
        /* ......  */
        status = UX_SUCCESS;
        break;
#endif
    default:
        /* Error trap. */
        _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_DCD, status);
        /* If trace is enabled, insert this event into the trace buffer.  */
        UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, status, 0, 0, 0, UX_TRACE_ERRORS, 0, 0)
        break;
    }
    /* Return completion status.  */
    return(status);
}

Events CALLs to USBX

Following USBX device stack function should be called in event handling:

ux_device_stack_control_request_process(UX_SLAVE_TRANSFER *)

  • Control SETUP received and IN (device to host) data is expected
  • Control SETUP received and there is no OUT (host to device) data
  • Control SETUP received and OUT data also received

E.g., for a DCD based on STM32 HAL, the code reference could be:

void HAL_PCD_SetupStageCallback(PCD_HandleTypeDef *hpcd)
{
    /* ...... */

    /* Check if the transaction is IN.  */
    if (*transfer_request -> ux_slave_transfer_request_setup & UX_REQUEST_IN)
    {

        /* ...... */

        /* Call the Control Transfer dispatcher.  */
        _ux_device_stack_control_request_process(transfer_request);

    }
    else
    {
        /* ....... */

        /* We are in a OUT transaction. Check if there is a data payload. If so, wait for the payload
           to be delivered.  */
        if (*(transfer_request -> ux_slave_transfer_request_setup + 6) == 0 &&
            *(transfer_request -> ux_slave_transfer_request_setup + 7) == 0)
        {

            /* Call the Control Transfer dispatcher.  */
            _ux_device_stack_control_request_process(transfer_request);

            /* TODO: start background control request status phase.  */
            /* ...... */
        }
        else
        {
            /* TODO: start background control request data OUT transfer,
                     when transfer/partial transfer is done, HAL_PCD_DataOutStageCallback is triggered.  */
            /* ...... */
        }
    }
}
void HAL_PCD_DataOutStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum)
{
    /* Endpoint 0 is the control endpoint.  */
    if (epnum == 0U)
    {
        /* Check if we have received something on control endpoint during data phase .  */
        if (ed -> ux_dcd_stm32_ed_state == UX_DCD_STM32_ED_STATE_DATA_RX)
        {
                /* Are we done with this transfer ? */
                if ((transfer_request -> ux_slave_transfer_request_actual_length ==
                     transfer_request -> ux_slave_transfer_request_requested_length) || 
                    (transfer_length != endpoint -> ux_slave_endpoint_descriptor.wMaxPacketSize))
                {
                    /* ...... */

                    _ux_device_stack_control_request_process(transfer_request);
                }
        }
    }

}

ux_device_stack_disconnect(void)

  • A USB bus reset happen in not reset states.
  • A USB bus disconnect happen in not reset states E.g., for a DCD based on STM32 HAL, the code reference could be:
void HAL_PCD_ResetCallback(PCD_HandleTypeDef *hpcd)
{
    /* If the device is attached or configured, we need to disconnect it.  */
    if (_ux_system_slave -> ux_system_slave_device.ux_slave_device_state !=  UX_DEVICE_RESET)
    {
        /* Disconnect the device.  */
        _ux_device_stack_disconnect();
    }
    /* Put the device in attached default state.  */
    /* ...... */
}
void HAL_PCD_DisconnectCallback(PCD_HandleTypeDef *hpcd)
{
    /* Check if the device is attached or configured.  */
    if (_ux_system_slave -> ux_system_slave_device.ux_slave_device_state !=  UX_DEVICE_RESET)
    {
        /* Disconnect the device.  */
        _ux_device_stack_disconnect();
    }
}

_ux_system_slave -> ux_system_slave_speed and _ux_system_slave -> ux_system_slave_device.ux_slave_device_state

  • Updated when USB bus reset is done

E.g., for a DCD based on STM32 HAL, the code reference could be:

void HAL_PCD_ResetCallback(PCD_HandleTypeDef *hpcd)
{

    /* ...... */

    /* Set USB Current Speed */
    switch(hpcd -> Init.speed)
    {
    case PCD_SPEED_HIGH:

        /* We are connected at high speed.  */
        _ux_system_slave -> ux_system_slave_speed =  UX_HIGH_SPEED_DEVICE;
        break;

    case PCD_SPEED_FULL:

        /* We are connected at full speed.  */
        _ux_system_slave -> ux_system_slave_speed =  UX_FULL_SPEED_DEVICE;
        break;   

    default:

        /* We are connected at full speed.  */
        _ux_system_slave -> ux_system_slave_speed =  UX_FULL_SPEED_DEVICE;
        break;
    }

    /* ...... */

    /* Mark the device as attached now.  */
    _ux_system_slave -> ux_system_slave_device.ux_slave_device_state =  UX_DEVICE_ATTACHED;
}
Prev
Chapter 5 - USBX OTG
Next
Chapter 6 - USBX Implement device class modules