USB PVND Check for ZLP while writing


I'm hoping someone can help me with a use case regarding the r_usb_pvnd stack:

When sending a large amount of data (long transfer) from RA4 to PC over USB, we want to periodically check the other pipe for a ZLP, which indicates the PC client is no longer receiving and wants to end the transfer early.

I've tried periodically reading, waiting a bit for the callback, when nothing happens, stop the read by calling R_USB_PipeStop() and continue sending. But when I'm done sending, and go to read the next request from the PC, there are bytes missing from the beginning of that next read. The amount of bytes missing match the amount attempted to be read during the send. All this is in the same thread.

Any idea what would cause that? Is there any other way other than reading to see if there are any bytes waiting, specifically a ZLP?

Another related question:

What is the proper way to detect a ZLP sent from PC to RA4 using a pipe read? For example:

fsp_err_t err = R_USB_PipeRead ( g_basic0_ctrl, buffer,  64, usbReadPipe  );

Is there a usb_event_info_t status, event, and data_size combination that would indicate a ZLP in this case? Would it be USB_STATUS_WRITE_COMPLETE, FSP_SUCCESS, 0 ?

Thank you!!


  • Hello  Matt,

    Thanks for reaching out Renesas Community.

    There is  an example of code which shows how to use r_usb_pvnd stack with RTOS.

    Please check the link below:

    Also, there is another thread on the community, which is similar with your issue, take a look below:

    Hope it helps!



  • Yes, I have seen the example and the other question. I have a working implementation. Would you mind addressing my two specific questions? Neither are covered in either the example or the unrelated thread.

    Also, one more thing, we are using FreeRTOS.



  • Can you read, then after waiting a bit for a read complete event that never comes, abandon that and start a write.

    The examples always wait forever on the callback (waiting on queue), Can you wait for a bit, then give up and start a write? Using Using R_USB_PipeStop()  in the case of abandoning the read, doesn't seem to work. The next read after that is missing data.

    Thanks Again,


  • Hello Matt,

    On provided usb_pvnd_ek_ra6m3_ep (you may use some different part number of ra mcu) project, there is the function fsp_err_t process_usb_events(void), which handles the R_USB_PipeRead, R_USB_PipeWrite, based on the condition of  p_usb_event each time  and includes  ZLP case.

    You can take a look and customized it.



  • Hello,

    I still see no example of READING a ZLP.


    We occasionally forward large amounts of data from one interface to the USB,  out to the PC. We can't do a single write, followed be read, followed by write like in the example. What I'm trying to explain is that we need to send a chunk of data, stop and check for ZLP from the PC (read), then continue if none received. So with that in mind, we can't wait forever on the queue after periodically reading (portMAX_DELAY) for possible ZLP.

    So we stop sending, read and block for a bit on the queue, then continue sending if no ZLP is received. When we do this, it appears that later reads are corrupted.

    All is working except the periodic stop and check for incoming ZLP.  The ZLP is used to tell the RA4 that the PC client has terminated the request early.

    Hopefully this clarifies a bit. Thanks again!


  • Hello,

    I think the only way is to check that the received data size is 0.

    The received data size is set in member (size) of the usb_ctrl_t structure. To figure out the size of the data when a read is complete, check the return value (USB_STATUS_READ_COMPLETE) of the R_USB_GetEvent function, and then refer to the member (size) of the usb_crtl_t structure.

    For example

    size = g_basic0_ctrl.data_size ;


  • Hi,

    I'm using FreeRTOS, so I can't call R_USB_GetEvent. I have to rely on a callback to send the usb_event_info_t via queue to the reading thread.  Is there a way to tell the difference between no data available to read vs ZLP received? I don't want to block forever on the queue as in the examples.

    This is what I've tried, but since I'm not blocking on the queue, there may have been no data to read, so data size of zero doesn't necessarily mean ZLP.. Also the next time the client sends a request over USB and we do a bulk read, it is missing data corresponding to how many times clientHasCanceled was called.

    void usbCallback(usb_event_info_t* p_event_info, usb_hdl_t handler, usb_onoff_t state)
        FSP_PARAMETER_NOT_USED (handler);
        FSP_PARAMETER_NOT_USED (state);
        if(xQueueSend(g_usbEventQueue, (const void *)&p_event_info, pdMS_TO_TICKS(100)) != pdTRUE)
           // The queue should never be full
    // Periodically call this between consecutive sends in case client is no 
    // longer receiving. The transfers could be 5-10 minutes long.
    bool TWtuHttpSendRecvUsb::clientHasCanceled()
       usb_event_info_t *pUsbEvent = nullptr;
       bool result = false;
       uint8_t buf;
       if (R_USB_PipeRead(&g_basic0_ctrl, &buf, BulkReadPacketSize, m_usbReadPipe) == FSP_SUCCESS)
          if (xQueueReceive(g_usbEventQueue, &pUsbEvent, pdMS_TO_TICKS(0) == pdTRUE))
             if ((USB_STATUS_READ_COMPLETE == pUsbEvent->event)
                   && (FSP_SUCCESS == pUsbEvent->status)
                   && (0 == pUsbEvent->data_size))
                result = true;
             R_USB_PipeStop(&g_basic0_ctrl, m_usbReadPipe);
       return result;

    Thanks again for the help!


  • Hi Matt.   The if clause above looks incorrect, you're not checking if the queue received or timed out

    if (xQueueReceive(g_usbEventQueue, &pUsbEvent, pdMS_TO_TICKS(0) == pdTRUE))

    change to

    if (xQueueReceive(g_usbEventQueue, &pUsbEvent, pdMS_TO_TICKS(0)) == pdTRUE)

    There is also a different api, xQueueSendFromISR(...) for use in interrupt context.

    I've been asked to help out on this.  If this doesn't correct your issue, please submit a ticket

  • After looking at the code, that callback is not in the interrupt context, so it is ok

  • Yes there was a syntax error in my minimum reproducible example. This is not the problem. I will continue this in the opened ticket.