USB Libraries Help > USB Device Libraries > USB CDC Device Library > Using the Library > How the Library Works > Event Handling
MPLAB Harmony USB Stack
Event Handling
Registering a CDC Function Driver Event Handler

While creating USB CDC Device-based application, an event handler must be registered with the Device Layer (the Device Layer Event Handler) and every CDC Function Driver instance (CDC Function Driver Event Handler). The CDC Function Driver event handler receives CDC and CDC ACM events. This event handler should be registered before the USB device_layer acknowledges the SET CONFIGURATION request from the USB Host. To ensure this, the event handler should be set in the USB_DEVICE_EVENT_CONFIGURED event that is generated by the device_layer. While registering the CDC Function Driver event handler, the CDC Function Driver allows the application to also pass a data object in the event handler register function. This data object gets associated with the instance of the CDC Function Driver and is returned by the CDC Function Driver when a CDC Function Driver event occurs. The following code shows an example of how this can be done.

/* This a sample Application Device Layer Event Handler
 * Note how the CDC Function Driver event handler APP_USBDeviceCDCEventHandler()
 * is registered in the USB_DEVICE_EVENT_CONFIGURED event. The appData
 * object that is passed in the USB_DEVICE_CDC_EventHandlerSet()
 * function will be returned as the userData parameter in the
 * when the APP_USBDeviceCDCEventHandler() function is invoked */

void APP_USBDeviceEventCallBack ( USB_DEVICE_EVENT event,
     void * eventData, uintptr_t context )
{
     switch ( event )
     {
        case USB_DEVICE_EVENT_RESET:
        case USB_DEVICE_EVENT_DECONFIGURED:

             // USB device is reset or device is deconfigured.
             // This means that USB device layer is about to deinitialize
             // all function drivers.

             break;

        case USB_DEVICE_EVENT_CONFIGURED:

            /* check the configuration */
            if ( *((uint8_t *)eventData) == 1)
            {

                /* Register the CDC Device application event handler here.
                 * Note how the appData object pointer is passed as the
                 * user data */

                USB_DEVICE_CDC_EventHandlerSet(USB_DEVICE_CDC_INDEX_0,
                      APP_USBDeviceCDCEventHandler, (uintptr_t)&appData);

                /* mark that set configuration is complete */
                appData.isConfigured = true;
            }
            break;

        case USB_DEVICE_EVENT_SUSPENDED:

            break;

        case USB_DEVICE_EVENT_RESUMED:
        case USB_DEVICE_EVENT_ATTACHED:
        case USB_DEVICE_EVENT_DETACHED:
        case USB_DEVICE_EVENT_ERROR:
        default:
            break;
     }
}

The CDC Function Driver event handler executes in an interrupt context when the device stack is configured for Interrupt mode. In Polled mode, the event handler is invoked in the context of the SYS_Tasks function. The application should not call computationally intensive functions, blocking functions, functions that are not interrupt safe, or functions that poll on hardware conditions from the event handler. Doing so will affect the ability of the USB device stack to respond to USB events and could potentially make the USB device non-compliant.

CDC Function Driver Events

The CDC Function Driver generates events to which the application must respond. Some of these events are management requests communicated through control transfers. Therefore, the application must use the Device Layer Control Transfer routines to complete the control transfer. Based on the generated event, the application may be required to:

  • Respond with a USB_DEVICE_ControlSend function, which is completes the data stage of a Control Read Transfer
  • Respond with a USB_DEVICE_ControlReceive function, which provisions the data stage of a Control Write Transfer
  • Respond with a USB_DEVICE_ControlStatus function, which completes the handshake stage of the Control Transfer. The application can either STALL or Acknowledge the handshake stage via the USB_DEVICE_ControlStatus function. The following table shows the CDC Function Driver Control Transfer related events and the required application control transfer actions.
CDC Function Driver Control Transfer Event 
Required Application Action 
USB_DEVICE_CDC_EVENT_SET_LINE_CODING 
Call USB_DEVICE_ControlReceive function with a buffer to receive the USB_CDC_LINE_CODING type data. 
USB_DEVICE_CDC_EVENT_GET_LINE_CODING 
Call USB_DEVICE_ControlSend function with a buffer that contains the current USB_CDC_LINE_CODING type data. 
USB_DEVICE_CDC_EVENT_SET_CONTROL_LINE_STATE 
Acknowledge or stall using the USB_DEVICE_ControlStatus function. 
USB_DEVICE_CDC_EVENT_SEND_BREAK 
Acknowledge or stall using the USB_DEVICE_ControlStatus function. 
USB_DEVICE_CDC_EVENT_CONTROL_TRANSFER_DATA_SENT 
Action not required. 
USB_DEVICE_CDC_EVENT_CONTROL_TRANSFER_DATA_RECEIVED 
Acknowledge or stall using the USB_DEVICE_ControlStatus function. 

Based on the type of event, the application should analyze the pData member of the event handler. This data member should be type cast to an event specific data type. The following table shows the event and the data type to use while type casting. Note that the pData member is not required for all events

CDC Function Driver Event 
Related pData type 
USB_DEVICE_CDC_EVENT_SET_LINE_CODING 
NULL 
USB_DEVICE_CDC_EVENT_GET_LINE_CODING 
NULL 
USB_DEVICE_CDC_EVENT_SET_CONTROL_LINE_STATE 
USB_CDC_CONTROL_LINE_STATE * 
USB_DEVICE_CDC_EVENT_SEND_BREAK 
USB_DEVICE_CDC_EVENT_WRITE_COMPLETE 
USB_DEVICE_CDC_EVENT_READ_COMPLETE 
USB_DEVICE_CDC_EVENT_SERIAL_STATE_NOTIFICATION_COMPLETE 
USB_DEVICE_CDC_EVENT_CONTROL_TRANSFER_DATA_SENT 
NULL 
USB_DEVICE_CDC_EVENT_CONTROL_TRANSFER_DATA_RECEIVED 
NULL 

The possible CDC Function Driver events are described here with the required application response, event specific data, and likely follow-up function driver event:

USB_DEVICE_CDC_EVENT_SET_LINE_CODING

Application Response: This event occurs when the host issues a SET LINE CODING command. The application must provide a USB_CDC_LINE_CODING data structure to the device_layer to receive the line coding data that the host will provide. The application must provide the buffer by calling the USB_DEVICE_CDC_ControlReceive function either in the event handler or in the application after returning from the event handler. The application can use the USB_DEVICE_CDC_EVENT_CONTROL_TRANSFER_DATA_SENT event to track completion of the command. 

Event Specific Data (pData): The pData parameter will be NULL. 

Likely Follow-up event: This event will likely be followed by the USB_DEVICE_CDC_EVENT_CONTROL_TRANSFER_DATA_RECEIVED event. This indicates that the data was received successfully. The application must either acknowledge or stall the handshake stage of the control transfer by calling the USB_DEVICE_ControlStatus function with the USB_DEVICE_CONTROL_STATUS_OK or USB_DEVICE_CONTROL_STATUS_ERROR flag, respectively. 

USB_DEVICE_CDC_EVENT_GET_LINE_CODING

Application Response: This event occurs when the host issues a GET LINE CODING command. The application must provide a USB_CDC_LINE_CODING data structure to the device_layer that contains the line coding data to be provided to the Host. The application must provide the buffer by calling the USB_DEVICE_ControlSend function either in the event handler or in the application after returning from the event handler. The size of the buffer is indicated by the length parameter. The application can use the USB_DEVICE_CDC_EVENT_CONTROL_TRANSFER_DATA_SENT event to track completion of the command. 

Event Specific Data (pData): The pData parameter will be NULL. 

Likely Follow-up event: This event will likely be followed by the USB_DEVICE_CDC_EVENT_CONTROL_TRANSFER_DATA_SENT event. This indicates that the data was sent to the Host successfully. 

USB_DEVICE_CDC_EVENT_SET_CONTROL_LINE_STATE

Application Response: This event occurs when the host issues a SET CONTROL LINE STATE command. The application can then use the USB_DEVICE_ControlStatus function to indicate acceptance of rejection of the command. The USB_DEVICE_ControlStatus function can be called from the event handler or in the application after returning from the event handler. 

Event Specific Data (pData): The application must interpret the pData parameter as a pointer to a USB_CDC_CONTROL_LINE_STATE data type that contains the control line state data. 

Likely Follow-up event: None. 

USB_DEVICE_CDC_EVENT_SEND_BREAK

Application Response: This event occurs when the Host issues a SEND BREAK command. The application can then use the USB_DEVICE_ControlStatus function to indicate acceptance or rejection of the command. The USB_DEVICE_ControlStatus function can be called from the event handler or in the application after returning from the event handler. 

Event Specific Data (pData): The application must interpret the pData parameter as a pointer to a uint16_t data type that contains the break duration data. 

Likely Follow-up event: None. 

USB_DEVICE_CDC_EVENT_WRITE_COMPLETE

Application Response: This event occurs when a write operation scheduled by calling the USB_DEVICE_CDC_Write function has completed. This event does not require the application to respond with any function calls. 

Event Specific Data (pData): The pData member in the event handler will point to the USB_DEVICE_CDC_EVENT_DATA_WRITE_COMPLETE data type. 

Likely Follow-up event: None. 

USB_DEVICE_CDC_EVENT_READ_COMPLETE

Application Response: This event occurs when a read operation scheduled by calling the USB_DEVICE_CDC_Read function has completed. This event does not require the application to respond with any function calls. 

Event Specific Data (pData): The pData member in the event handler will point to the USB_DEVICE_CDC_EVENT_DATA_READ_COMPLETE type. 

Likely Follow-up event: None. 

USB_DEVICE_CDC_EVENT_SERIAL_STATE_NOTIFICATION_COMPLETE

Application Response: This event occurs when a serial state notification send scheduled by calling the USB_DEVICE_CDC_SerialStateNotificationSend function has completed. This event does not require the application to respond with any function calls. 

Event Specific Data (pData): The pData member in the event handler will point to the USB_DEVICE_CDC_EVENT_DATA_SERIAL_STATE_NOTIFICATION_COMPLETE data type. 

Likely Follow-up event: None. 

USB_DEVICE_CDC_EVENT_CONTROL_TRANSFER_DATA_SENT

Application Response: This event occurs when the data stage of a control read transfer has completed in response to the USB_DEVICE_ControlSend function (in the USB_DEVICE_CDC_EVENT_GET_LINE_CODING event). The application must acknowledge the handshake stage of the control transfer by calling the USB_DEVICE_ControlStatus function with the USB_DEVICE_CONTROL_STATUS_OK flag. 

Event Specific Data (pData): The pData parameter will be NULL. 

Likely Follow-up event: None. 

USB_DEVICE_CDC_EVENT_CONTROL_TRANSFER_DATA_RECEIVED

Application Response: This event occurs when the data stage of a control write transfer has completed in response to the USB_DEVICE_ControlReceive function (in the USB_DEVICE_CDC_EVENT_SET_LINE_CODING event). 

Event Specific Data (pData): The pData parameter will be NULL. 

Likely Follow-up event: None. 

CDC Function Driver Event Handling

The following code shows an event handling scheme example. The application always returns from the event handler with a USB_DEVICE_CDC_EVENT_RESPONSE_NONE value.

    // This code example shows all CDC Function Driver possible events
    // and a possible scheme for handling these events. In this case
    // event responses are not deferred.

    uint16_t breakDuration;
    USB_DEVICE_HANDLE    usbDeviceHandle;
    USB_CDC_LINE_CODING  lineCoding;
    USB_CDC_CONTROL_LINE_STATE * controlLineStateData;

    USB_DEVICE_CDC_EVENT_RESPONSE USBDeviceCDCEventHandler
    (
        USB_DEVICE_CDC_INDEX instanceIndex,
        USB_DEVICE_CDC_EVENT event,
        void * pData,
        uintptr_t userData
    )
    {
        switch(event)
        {
            case USB_DEVICE_CDC_EVENT_SET_LINE_CODING:

                // In this case, the application should read the line coding
                // data that is sent by the host.

                USB_DEVICE_ControlReceive(usbDeviceHandle, &lineCoding,
                                    sizeof(USB_CDC_LINE_CODING));

                break;

            case USB_DEVICE_CDC_EVENT_GET_LINE_CODING:

                // In this case, the application should send the line coding
                // data to the host.

                USB_DEVICE_ControlSend(usbDeviceHandle, &lineCoding,
                                    sizeof(USB_CDC_LINE_CODING));

                break;

            case USB_DEVICE_CDC_EVENT_SET_CONTROL_LINE_STATE:

                // In this case, pData should be interpreted as a
                // USB_CDC_CONTROL_LINE_STATE pointer type.  The application
                // acknowledges the parameters by calling the
                // USB_DEVICE_ControlStatus() function with the
                // USB_DEVICE_CONTROL_STATUS_OK option.

                controlLineStateData = (USB_CDC_CONTROL_LINE_STATE *)pData;
                USB_DEVICE_ControlStatus(usbDeviceHandle, USB_DEVICE_CONTROL_STATUS_OK);

                break;

            case USB_DEVICE_CDC_EVENT_SEND_BREAK:

                // In this case, pData should be interpreted as a
                // USB_DEVICE_CDC_EVENT_DATA_SEND_BREAK pointer type
                // to the break duration. The application acknowledges
                // the parameters by calling the USB_DEVICE_ControlStatus()
                // function with the USB_DEVICE_CONTROL_STATUS_OK option.

                breakDuration = ((USB_DEVICE_CDC_EVENT_DATA_SEND_BREAK *) pData)->breakDuration;
                USB_DEVICE_ControlStatus(usbDeviceHandle, USB_DEVICE_CONTROL_STATUS_OK);

                break;

            case USB_DEVICE_CDC_EVENT_CONTROL_TRANSFER_DATA_SENT:

                // This event indicates the data send request associated with
                // the latest USB_DEVICE_ControlSend() function was
                // completed.  The application could use this event to track
                // the completion of the USB_DEVICE_CDC_EVENT_GET_LINE_CODING
                // request.

                break;

            case USB_DEVICE_CDC_EVENT_CONTROL_TRANSFER_DATA_RECEIVED:

                // This means that the data stage is complete. The data in
                // setLineCodingData is valid or data in getLineCodingData was
                // sent to the host.  The application can now decide whether it
                // supports this data. It is not mandatory to do this in the
                // event handler.

                USB_DEVICE_ControlStatus(usbDeviceHandle, USB_DEVICE_CONTROL_STATUS_OK);

                break;

            case USB_DEVICE_CDC_EVENT_WRITE_COMPLETE:

                // This means USB_DEVICE_CDC_Write() operation completed.
                // The pData member will point to a
                // USB_DEVICE_CDC_EVENT_DATA_WRITE_COMPLETE type of data.

                break;

            case USB_DEVICE_CDC_EVENT_READ_COMPLETE:

                // This means USB_DEVICE_CDC_Read() operation completed.
                // The pData member will point to a
                // USB_DEVICE_CDC_EVENT_DATA_READ_COMPLETE type of data.

                break;

            case USB_DEVICE_CDC_EVENT_SERIAL_STATE_NOTIFICATION_COMPLETE:

                // This means USB_DEVICE_CDC_SerialStateNotification() operation
                // completed. The pData member will point to a
                // USB_DEVICE_CDC_EVENT_DATA_SERIAL_STATE_NOTIFICATION_COMPLETE type of data.

                break;

             default:
                break;
         }

        return USB_DEVICE_CDC_EVENT_RESPONSE_NONE;
    }

Refer to the USB_DEVICE_CDC_EVENT enumeration for more details on each event.