USB Libraries Help > USB Device Libraries > Generic USB Device Library > Using the Library > How the Library Works > Endpoint Data Transfer
MPLAB Harmony USB Stack
Endpoint Data Transfer

The application should call the USB_DEVICE_EndpointRead and USB_DEVICE_EndpointWrite functions to transfer data over an enabled endpoint. Calling this function causes a USB transfer to be scheduled on the endpoint. The transfer is added to the endpoint queue and is serviced as the host schedules the transaction on the bus. The USB_DEVICE_EndpointRead and USB_DEVICE_EndpointWrite functions return a unique transfer handle which can be track the transfer. These transfer handles are returned along with the USB_DEVICE_EVENT_ENDPOINT_READ_COMPLETE (when a endpoint read transfer is complete) and USB_DEVICE_EVENT_ENDPOINT_WRITE_COMPLETE (when an endpoint write is complete) events. 

The following code shows an example of sending data over an endpoint.

/* This code shows an example of using the USB_DEVICE_EndpointWrite
 * function to send data over the endpoint. The completion of the write is
 * indicated by the USB_DEVICE_EVENT_ENDPOINT_WRITE_COMPLETE event. The
 * transfer handle is returned in appData.writeTransferHandle */
USB_DEVICE_EndpointWrite ( appData.usbDevHandle, &appData.writeTranferHandle,
        appData.endpointTx, &transmitDataBuffer[0], sizeof(transmitDataBuffer),
        USB_DEVICE_TRANSFER_FLAGS_DATA_COMPLETE );

void APP_USBDeviceEventHandler(USB_DEVICE_EVENT event, void * eventData, uintptr_t context)
{
    /* This is the Device Layer event handler */
    case USB_DEVICE_EVENT_ENDPOINT_WRITE_COMPLETE:
        /* Endpoint write is complete */
        appData.epDataWritePending = false;
        break;
}

The USB_DEVICE_EndpointWrite function allows the application to send data to the host without ending the transfer. This is done by specifying USB_DEVICE_TRANSFER_FLAGS_DATA_PENDING as the transfer flag in the call to the USB_DEVICE_EndpointWrite function. The application can use this option when the data to be sent is not readily available or when the application is memory constrained. The combination of the transfer flag and the transfer size affects how the data is sent to the host:

  • If size is a multiple of maxPacketSize (the IN endpoint size) and flag is set as USB_DEVICE_TRANSFER_FLAGS_DATA_COMPLETE, the write function will append a Zero Length Packet (ZLP) to complete the transfer
  • If size is a multiple of maxPacketSize and flag is set as USB_DEVICE_TRANSFER_FLAGS_MORE_DATA_PENDING, the write function will not append a ZLP and therefore and hence will not complete the transfer
  • If size is greater than but not a multiple of maxPacketSize and flags is set as USB_DEVICE_TRANSFER_FLAGS_DATA_COMPLETE, the write function schedules (length/maxPacketSize) packets and one packet for the residual data
  • If size is greater than but not a multiple of maxPacketSize and flags is set as USB_DEVICE_TRANSFER_FLAGS_MORE_DATA_PENDING, the write function returns an error code and sets the transferHandle parameter to USB_DEVICE_TRANSFER_HANDLE_INVALID
  • If size is less than maxPacketSize and flag is set USB_DEVICE_TRANSFER_FLAGS_DATA_COMPLETE, the write function schedules one packet
  • If size is less than maxPacketSize and flag is set as USB_DEVICE_TRANSFER_FLAGS_MORE_DATA_PENDING, the write function returns an error code and sets the transferHandle parameter to USB_DEVICE_TRANSFER_HANDLE_INVALID

Refer to USB_DEVICE_EndpointWrite function API description for more details and code examples. 

The application should use the USB_DEVICE_EndpointRead function to read data from an endpoint. The size of the buffer that is specified in this function should always be a multiple of the endpoint size. The following code shows an example of using the USB_DEVICE_EndpointRead function.

/* This code shows to use the USB_DEVICE_EndpointRead function
 * to read from an endpoint.  The transfer handle is returned in
 * appData.readTransferHandle. The size of receivedDataBuffer should
 * be a multiple of the receive endpoint size. */

USB_DEVICE_EndpointRead(appData.usbDevHandle, &appData.readTranferHandle,
              appData.endpointRx, &receivedDataBuffer[0], sizeof(receivedDataBuffer) );
void APP_USBDeviceEventHandler(USB_DEVICE_EVENT event, void * eventData, uintptr_t context)
{
    /* This is the Device Layer event handler */
    case USB_DEVICE_EVENT_ENDPOINT_READ_COMPLETE:
        /* Endpoint write is complete */
        appData.epDataReadPending = false;
        break;
}

In a case where a transfer is in progress, the USB_DEVICE_EndpointRead and USB_DEVICE_EndpointWrite functions can queue up transfers. The maximum number of read transfers that can queued (on any receive endpoint) is specified by the endpointQueueSizeRead member of the USB_DEVICE_INIT data structure. The maximum number of write transfers that can queued (on any transmit endpoint) is specified by the endpointQueueSizeWrite member of the USB_DEVICE_INIT data structure. The USB_DEVICE_ENDPOINT_QUEUE_DEPTH_COMBINED configuration macro should be set to total of read and write transfers that need to be queued. 

For example, consider a Generic USB Device that contains two OUT (read) endpoint (EP1 and EP2) and one IN write endpoint (EP1). The application will queue a maximum of three read transfers on EP1, a maximum of five read transfers on EP2 and a maximum of four write transfers on EP1. Therefore, the total read transfer that will be queued in eight (3 + 5) and total write transfers that will be queued is four. The endpointQueueSizeRead member of the USB_DEVICE_INIT data structure should be set to eight. The endpointQueueSizeWrite member of the USB_DEVICE_INIT data structure should be set to four. The USB_DEVICE_ENDPOINT_QUEUE_DEPTH_COMBINED configuration macro should be set to 12 (8 + 4).