USB CDC-ACM function class

Hi,

I use a modified version of Keil's USB CDC-ACM function class implementation. It works really well but I am having a problem in the way I am using it.
I use the virtual COM-port to connect as a modem to the host PC.
My target connects through PPP to the USB CDC-ACM input/output. This allows me to create multiple sockets for multi-channel TCP/IP communication. This creates the problem! With increasing communication throughput my application showes signs of instability.
I am looking for a way how I could limit the data throughput at the USB device level.
Could someone provide me with an feasable example of how to achieve this?
I appreciate your help - thank you!

-Frank

Parents
  • Keil CDC example misses these points on bulk endpoints (EPs) handling for CDC.

    a) bulk OUT EP
    The transfer speed is automatically tuned using NAK flow control. While the EP is occupied by current received data, the transfer from host is delayed by NAKing. In this way, lossless transfer is achieved.

    When the data is left unread on the EP buffer, no more EP interrupt is generated. Then, we have to poll the endpoint status periodically out side of the EP ISR, too. SOF interrupt is often used for this purpose.

    A typical implementation is,
    In USB_SOF_Event() (usb_user.c) [b]AND[/b] USB_EndPoint2() ISR - USB_EVT_OUT selector, call this subroutine.

    - Select the bulk OUT EP (USB_CTRL)
    - Check PKT_RDY bit of RX_PLENGTH
    - - if not ready, return
    - Compare the packet length and the room on the RX buffer of your firmware
    - - if no room, return
    - Read out data from the EP to the RX buffer
    - Clear the EP (CMD_SEL_EP and CMD_CLR_BUF)

    This subroutine is easily made modifying USB_ReadEP() (usbhw.c)
    Just add 2-3 lines.

    b) bulk IN EP
    Here, NAK flow control is applied, too. Just when we have data to send, the data is put to the bulk IN EP. When no data is written to the IN EP, no more EP interrupt is generated. Then, we have to poll the number of data on the TX buffer periodically out side of the EP ISR, too. SOF interrupt is often used for this purpose.

    When the last transaction is just 64 bytes, host controller waits for next short packets forever. Until receiving a shot packet, host controller doesn't pass the data to the input buffer on the PC device driver. In this case, Zero-Length Packet (ZLP) should be put to IN EP, to speed up the response of host. This ZLP is also added in SOF ISR.

    A typical implementation is,
    In USB_EndPoint2() - always sends full (64 bytes) packet

    USB_EndPoint2() ISR - USB_EVT_IN selector
    - If the number of the data on the TX buffer is less than 64, return
    - Check the FE bit of "Select Endpoint" command, if no empty EP buffer, return
    - - WrCmd( CMD_SEL_EP(EPAdr(EPNum)) ); if ( CMD_DATA & EP_SEL_F != 0 ) return;
    - Send 64 bytes from the buffer to the IN EP using USB_WriteEP()
    - Raise a flag - flag_send_ZLP

    In USB_SOF_Event(), call this subroutine

    - Check the FE bit of "Select Endpoint" command, if no empty EP buffer, return
    - send_cnt = number of data on the TX buffer
    - if (send_cnt == 0) && (! flag_send_ZLP), return
    - if send_cnt >= 64 bytes, send_cnt = 63 bytes (short packet)
    - Send specified number of data, USB_WriteEP( CDC_DEP_IN, TX_buffer, send_cnt );
    - drop flag_send_ZLP

    For TX and RX buffer, cyclic buffers fit well for this implementation.




    "Could someone provide me with an feasable example of how to achieve this?

    Implement above modification by yourself for your exercise :-)
    Or wait until KEIL brush up the implementation.

    Tsuneo

Reply
  • Keil CDC example misses these points on bulk endpoints (EPs) handling for CDC.

    a) bulk OUT EP
    The transfer speed is automatically tuned using NAK flow control. While the EP is occupied by current received data, the transfer from host is delayed by NAKing. In this way, lossless transfer is achieved.

    When the data is left unread on the EP buffer, no more EP interrupt is generated. Then, we have to poll the endpoint status periodically out side of the EP ISR, too. SOF interrupt is often used for this purpose.

    A typical implementation is,
    In USB_SOF_Event() (usb_user.c) [b]AND[/b] USB_EndPoint2() ISR - USB_EVT_OUT selector, call this subroutine.

    - Select the bulk OUT EP (USB_CTRL)
    - Check PKT_RDY bit of RX_PLENGTH
    - - if not ready, return
    - Compare the packet length and the room on the RX buffer of your firmware
    - - if no room, return
    - Read out data from the EP to the RX buffer
    - Clear the EP (CMD_SEL_EP and CMD_CLR_BUF)

    This subroutine is easily made modifying USB_ReadEP() (usbhw.c)
    Just add 2-3 lines.

    b) bulk IN EP
    Here, NAK flow control is applied, too. Just when we have data to send, the data is put to the bulk IN EP. When no data is written to the IN EP, no more EP interrupt is generated. Then, we have to poll the number of data on the TX buffer periodically out side of the EP ISR, too. SOF interrupt is often used for this purpose.

    When the last transaction is just 64 bytes, host controller waits for next short packets forever. Until receiving a shot packet, host controller doesn't pass the data to the input buffer on the PC device driver. In this case, Zero-Length Packet (ZLP) should be put to IN EP, to speed up the response of host. This ZLP is also added in SOF ISR.

    A typical implementation is,
    In USB_EndPoint2() - always sends full (64 bytes) packet

    USB_EndPoint2() ISR - USB_EVT_IN selector
    - If the number of the data on the TX buffer is less than 64, return
    - Check the FE bit of "Select Endpoint" command, if no empty EP buffer, return
    - - WrCmd( CMD_SEL_EP(EPAdr(EPNum)) ); if ( CMD_DATA & EP_SEL_F != 0 ) return;
    - Send 64 bytes from the buffer to the IN EP using USB_WriteEP()
    - Raise a flag - flag_send_ZLP

    In USB_SOF_Event(), call this subroutine

    - Check the FE bit of "Select Endpoint" command, if no empty EP buffer, return
    - send_cnt = number of data on the TX buffer
    - if (send_cnt == 0) && (! flag_send_ZLP), return
    - if send_cnt >= 64 bytes, send_cnt = 63 bytes (short packet)
    - Send specified number of data, USB_WriteEP( CDC_DEP_IN, TX_buffer, send_cnt );
    - drop flag_send_ZLP

    For TX and RX buffer, cyclic buffers fit well for this implementation.




    "Could someone provide me with an feasable example of how to achieve this?

    Implement above modification by yourself for your exercise :-)
    Or wait until KEIL brush up the implementation.

    Tsuneo

Children
More questions in this forum