USB PVND Check for ZLP while writing

Hello,

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!!

Matt

Parents Reply Children
  • Hello,

    I still see no example of READING a ZLP.

    also

    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!

    Matt

  • 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 ;

    Regards

  • 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
           OS_ASSERT(false);
        }
    }
    
    
    // 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;
             }
          }
          else
          {
             R_USB_PipeStop(&g_basic0_ctrl, m_usbReadPipe);
          }
       }
    
       return result;
    }

    Thanks again for the help!

    Matt

  • 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

    https://en-support.renesas.com/dashboard

  • 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.