Device Layer Control Transfers.

The Device Layer forwards control transfer setup packets to the application, where the Recipient field in the Setup packet is set to "Other". The pData parameter of the event handler will point to the control transfer setup packet. The application must respond appropriately to this event. The following flow chart shows the possible sequences of events and application responses.

The Device Layer provides the USB_DEVICE_ControlReceive(), USB_DEVICE_ControlSend() and USB_DEVICE_ControlStatus() functions to complete the control transfers. These functions should be called only in response to the USB_DEVICE_EVENT_CONTROL_TRANSFER_SETUP_REQUEST event. In response to this event, the application can use the USB_DEVICE_ControlReceive( ) function to receive data in the data stage of a Control Write transfer. The reception of data is indicated by the USB_DEVICE_EVENT_CONTROL_TRANSFER_DATA_RECEIVED event. The application can then complete the Control Write transfer by either:

  • accepting the received data and acknowledging the Status Stage of the Control transfer. This is done by calling the USB_DEVICE_ControlStatus() function with the USB_DEVICE_CONTROL_STATUS_OK flag.
  • rejecting the received data and stalling the Status Stage of the Control transfer. This is done by calling the USB_DEVICE_ControlStatus() function with the USB_DEVICE_CONTROL_STATUS_ERROR flag.

The application can use the USB_DEVICE_ControlSend() function to send data in the data stage of a Control Read transfer. The transmission of data is indicated by the USB_DEVICE_EVENT_CONTROL_TRANSFER_DATA_SENT event.

In a case where the Control Transfer does not contain a data stage or if the application does not support the Setup Request, the application can end the Control Transfer by calling the USB_DEVICE_ControlStatus() function in response to the USB_DEVICE_EVENT_CONTROL_TRANSFER_SETUP_REQUEST event. Here the application can

  • accepting the command by acknowledging the Status Stage of the Zero Data Stage of the Control transfer. This is done by calling the USB_DEVICE_ControlStatus() function with the USB_DEVICE_CONTROL_STATUS_OK flag.
  • rejecting the Setup Request and stalling the Status Stage of the Control transfer. This is done by calling the USB_DEVICE_ControlStatus() function with the USB_DEVICE_CONTROL_STATUS_ERROR flag.

The application can also defer the response to Control transfer events. In that, the application does not have to respond to Control Transfer Events in the event handler. This may be needed in cases where resources required to respond to the Control Transfer Events are not readily available. The application, even while deferring the response, must however complete the Control Transfer in a time fashion. Failing to do so, will cause the host to cancel and retry the control transfer. This could also cause the USB device to malfunction and become non-compliant.

The following code shows an example of handling Device Layer events.

USB_DEVICE_EVENT_RESPONSE APP_USBDeviceEventHandler
(
    USB_DEVICE_EVENT event,
    void * pData,
    uintptr_t context
)
{
    uint8_t     activeConfiguration;

    // Handling of each event
switch(event)
    {
        case USB_DEVICE_EVENT_POWER_DETECTED:

            // This means the device detected a valid VBUS voltage
            // and is attached to the USB if the device is bus powered.
            break;

        case USB_DEVICE_EVENT_POWER_REMOVED:

            // This means the device is not attached to the USB.
            break;

        case USB_DEVICE_EVENT_SUSPENDED:

            // The bus is idle. There was no activity detected.
            // The application can switch to a low power mode after
            // exiting the event handler.
            break;

        case USB_DEVICE_EVENT_SOF:

            // A start of frame was received. This is a periodic
            // event and can be used the application for time
            // related activities. pData will point to a USB_DEVICE_EVENT_DATA_SOF type data
            // containing the frame number.
            // Example code for retriveing the frame number.
            // frameNumber = ((USB_DEVICE_EVENT_DATA_SOF *)(pData))->frameNumber;
            break;

        case USB_DEVICE_EVENT_RESET :

            // Reset signalling was detected on the bus. The
            // application can find out the attach speed.
            // Example code for retriveing the speed
            // attachSpeed = USB_DEVICE_ActiveSpeedGet(usbDeviceHandle);
            break;

        case USB_DEVICE_EVENT_DECONFIGURED :

            // This indicates that host has deconfigured the device i.e., it
            // has set the configuration as 0. All function driver instances
            // would have been deinitialized.
            break;

        case USB_DEVICE_EVENT_ERROR :

            // This means an unknown error has occurred on the bus.
            // The application can try detaching and attaching the
            // device again.
            break;

        case USB_DEVICE_EVENT_CONFIGURED :

            // This means that device is configured and the application can
            // start using the device functionality. The application must
            // register function driver event handlers within this event.
            // The pData parameter will be a pointer to a USB_DEVICE_EVENT_DATA_CONFIGURED data type
            // that contains the active configuration number.

            activeConfiguration = ((USB_DEVICE_EVENT_DATA_CONFIGURED *)(pData))->configurationValue;
            if (activeConfiguration == 1)
            {
                // Device is enumerated. Register here the USB Function Driver Event Handler function.
            }
            break;

        case USB_DEVICE_EVENT_RESUMED:

            // This means that the resume signalling was detected on the
            // bus. The application can bring the device out of power
            // saving mode.
break;

        case USB_DEVICE_EVENT_CONTROL_TRANSFER_SETUP_REQUEST:

            // This means that the setup stage of the control transfer is in
            // progress and a setup packet has been received. The pData
            // parameter will point to a USB_SETUP_PACKET data type The
            // application can process the command and update its control
            // transfer state machine. The application for example could call
            // the USB_DEVICE_ControlReceive() function (as shown here) to
            // submit the buffer that would receive data in case of a
            // control read transfer.
            // Example:
            // setupEventData = (USB_SETUP_PACKET *)pData;
            // Application can now respond to the Setup packet by submitting a buffer
            // to receive 32 bytes in the  control write transfer */
            // USB_DEVICE_ControlReceive(appData.usbDevHandle, data, 32);
            break;

        case USB_DEVICE_EVENT_CONTROL_TRANSFER_DATA_RECEIVED:

            // This means that data in the data stage of the control write
            // transfer has been received. The application can either accept
            // the received data by calling the USB_DEVICE_ControlStatus()
            // function with USB_DEVICE_CONTROL_STATUS_OK flag (as shown in
            // this example) or it can reject it by calling the
            // USB_DEVICE_ControlStatus() function with
            // USB_DEVICE_CONTROL_STATUS_ERROR flag.

            USB_DEVICE_ControlStatus(usbDeviceHandle, USB_DEVICE_CONTROL_STATUS_OK);
            break;

        case USB_DEVICE_EVENT_CONTROL_TRANSFER_DATA_SENT:

            // This means that data in the data stage of the control
            // read transfer has been sent.
            break;

        case USB_DEVICE_EVENT_CONTROL_TRANSFER_ABORTED:

            // This means the host has aborted the control transfer. The
            // application can reset it's control transfer state machine.
            break;

        default:
            break;
     }

     return USB_DEVICE_EVENT_RESPONSE_NONE;
}