This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Keil Usb example / flow control

Is it possible to overwrite data to the In Endpoint, that is, overwrite data in the buffer before the host has a chance to read it? In my device I am sending a series of responses to a request from the host. Each of these responses are less than 64 bytes. I know the IN is double buffered, is it possible that the 3rd write can overwrite a buffer if the first 2 have not been read yet? Or is all that prevented by hardware? I am looking at a Keil example of writing data to an endpoint and it just writes to the tranmit data register without any type of checking?

Parents
  • How to ensure timely data exchange over USB

    In above post, I've written about the device side of bulk IN transfer for Full-Speed (FS) device. The host side is here.

    a) Read it in advance - Polling-IN transaction
    As USB is host-centric, device cannot send data until host requests it by an IN transaction. But host doesn't know when the device puts data. Then, host puts IN transactions before the device puts data, and repeat the transactions. This method is called as Polling-IN transaction.

    While the endpoint is empty, the USB engine on the device returns NAK to IN transaction from the host. The host controller (hardware) repeats IN transaction endlessly until the device returns DATA. For bulk IN transfer, you'll see more than 30 IN-NAKs on a single USB frame (1ms), when the bulk IN endpoint is assigned full bandwidth. That is, when the device places data to the IN endpoint, it is sent to host within the latency of 1/30 ms or less.

    Polling-IN transaction is implemented on class drivers, like HID, CDC, etc.
    These PC device drivers issue IN transfer repeatedly by itself. The data retrieved from the device is stored the input buffer on the device driver. Host app read out data from this input buffer, indirectly.

    Even using a generic bulk device driver, you can implement polling-IN transaction. The key is, repeated Read and buffering on your host app.

    For Windows, OVERLAPPED ReadFile is called repeatedly in a sub-thread. When a ReadFile completes, the data is stored to a global buffer and next ReadFile is called immediately. To ensure no-gap transfer, more than single ReadFile are applied.

    Please note, this method is same as maximizing the transfer speed.

    // outline of the thread for polling-IN transaction
    
    UINT USBReadProc( LPVOID pParam )
    {
       const int kNum_of_Calls = 4;
       OVERLAPPED ov[ kNum_of_Calls ];
       HANDLE event_list[ kNum_of_Calls + 1 ];
    
    // Initialize OVERLAPPED
       for (int i = 0; i < kNum_of_Calls; i++) {
          ZeroMemory( &ov[i], sizeof(ov[i]);
          ov[i].hEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
       }
    
    // Call multiple ReadFile at a time
       event_list[0] = end_of_App_event;
       For ( int i = 0; i < kNum_of_Calls; i++ ) {
          event_list[i+1] = &ov[i];
          ReadFile( ..., buf[i], ..., &ov[i] );
       }
    
    // Wait the completion of calls and handle it
       while( true ) {
          DWORD result = WaitForMultipleObjects( kNum_of_Calls + 1, event_list, FALSE, INFINITE );
          switch( result ) {
             WAIT_OBJECT_0:       // end_of_App_event
             WAIT_TIMEOUT:
                goto close_and_end;
    
             default:
                                  // check the completion status
                GetOverlappedResult( ..., &ov[result - 1], ... );
                                  //
                                  // transfer read buffer to global buffer
                                  //
                                  // next ReadFile
                ReadFile( ..., buf[result - 1], ... ov[result - 1] );
                break;
          }
       }
    
    close_and_end:
       CancelIo( ... );
       CloseHandle( ov[i].hEvent );
       ...
       return 0
    }
    





    b) Ensure bus bandwidth for bulk
    Greater bus bandwidth is assigned to the endpoint, it gets more chance to start transfer. However, bulk transfer is the lowest priority.

    Interrupt and Isochronous transfer have the first priority on USB bus. These transfers reserve the bandwidth at the device enumeration. Next is Control transfer, up to 10% bandwidth of FS bus is assigned. Bulk transfer is assigned just the rest of the bandwidth. Moreover, all active bulk endpoints on the devices on the bus share this rest bandwidth.

    To ensure bus bandwidth for the bulk endpoint,
    - Connect the FS device over a multi-TT hub, all FS devices on the hub enjoy full bandwidth.
    - Connect it over a single-TT hub, without any other device on the hub
    - Connect it directly to PC USB port
    (TT = Transaction Translator)

    See this post for the way how TT works for FS device(s)
    "USB Hub faster? and Suspend Problem?" on Microchip USB forum
    forum.microchip.com/tm.aspx

    Multi-TT hub
    Belkin F5U234v1 (4port), F5U237v1 (7port)
    IOGEAR GUH274 (4port)

    To check your hub for single-TT or multi-TT,
    It is known by bDeviceProtocol field of the hub device descriptor.

                  bDeviceProtocol
    FS/LS hub        0
    HS single-TT     1
    HS multi-TT      2
    

    USBView read out of Belkin F5U234v1 multi-TT hub

    Device Descriptor:
    bcdUSB:             0x0200
    bDeviceClass:         0x09
    bDeviceSubClass:      0x00
    bDeviceProtocol:      0x02
    bMaxPacketSize0:      0x40 (64)
    idVendor:           0x050D (Belkin Components)
    idProduct:          0x0234
    bcdDevice:          0x0000
    iManufacturer:        0x00
    iProduct:             0x00
    iSerialNumber:        0x00
    bNumConfigurations:   0x01
    

    USBView on FTDI site
    www.ftdichip.com/.../usbview.zip

    Tsuneo

Reply
  • How to ensure timely data exchange over USB

    In above post, I've written about the device side of bulk IN transfer for Full-Speed (FS) device. The host side is here.

    a) Read it in advance - Polling-IN transaction
    As USB is host-centric, device cannot send data until host requests it by an IN transaction. But host doesn't know when the device puts data. Then, host puts IN transactions before the device puts data, and repeat the transactions. This method is called as Polling-IN transaction.

    While the endpoint is empty, the USB engine on the device returns NAK to IN transaction from the host. The host controller (hardware) repeats IN transaction endlessly until the device returns DATA. For bulk IN transfer, you'll see more than 30 IN-NAKs on a single USB frame (1ms), when the bulk IN endpoint is assigned full bandwidth. That is, when the device places data to the IN endpoint, it is sent to host within the latency of 1/30 ms or less.

    Polling-IN transaction is implemented on class drivers, like HID, CDC, etc.
    These PC device drivers issue IN transfer repeatedly by itself. The data retrieved from the device is stored the input buffer on the device driver. Host app read out data from this input buffer, indirectly.

    Even using a generic bulk device driver, you can implement polling-IN transaction. The key is, repeated Read and buffering on your host app.

    For Windows, OVERLAPPED ReadFile is called repeatedly in a sub-thread. When a ReadFile completes, the data is stored to a global buffer and next ReadFile is called immediately. To ensure no-gap transfer, more than single ReadFile are applied.

    Please note, this method is same as maximizing the transfer speed.

    // outline of the thread for polling-IN transaction
    
    UINT USBReadProc( LPVOID pParam )
    {
       const int kNum_of_Calls = 4;
       OVERLAPPED ov[ kNum_of_Calls ];
       HANDLE event_list[ kNum_of_Calls + 1 ];
    
    // Initialize OVERLAPPED
       for (int i = 0; i < kNum_of_Calls; i++) {
          ZeroMemory( &ov[i], sizeof(ov[i]);
          ov[i].hEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
       }
    
    // Call multiple ReadFile at a time
       event_list[0] = end_of_App_event;
       For ( int i = 0; i < kNum_of_Calls; i++ ) {
          event_list[i+1] = &ov[i];
          ReadFile( ..., buf[i], ..., &ov[i] );
       }
    
    // Wait the completion of calls and handle it
       while( true ) {
          DWORD result = WaitForMultipleObjects( kNum_of_Calls + 1, event_list, FALSE, INFINITE );
          switch( result ) {
             WAIT_OBJECT_0:       // end_of_App_event
             WAIT_TIMEOUT:
                goto close_and_end;
    
             default:
                                  // check the completion status
                GetOverlappedResult( ..., &ov[result - 1], ... );
                                  //
                                  // transfer read buffer to global buffer
                                  //
                                  // next ReadFile
                ReadFile( ..., buf[result - 1], ... ov[result - 1] );
                break;
          }
       }
    
    close_and_end:
       CancelIo( ... );
       CloseHandle( ov[i].hEvent );
       ...
       return 0
    }
    





    b) Ensure bus bandwidth for bulk
    Greater bus bandwidth is assigned to the endpoint, it gets more chance to start transfer. However, bulk transfer is the lowest priority.

    Interrupt and Isochronous transfer have the first priority on USB bus. These transfers reserve the bandwidth at the device enumeration. Next is Control transfer, up to 10% bandwidth of FS bus is assigned. Bulk transfer is assigned just the rest of the bandwidth. Moreover, all active bulk endpoints on the devices on the bus share this rest bandwidth.

    To ensure bus bandwidth for the bulk endpoint,
    - Connect the FS device over a multi-TT hub, all FS devices on the hub enjoy full bandwidth.
    - Connect it over a single-TT hub, without any other device on the hub
    - Connect it directly to PC USB port
    (TT = Transaction Translator)

    See this post for the way how TT works for FS device(s)
    "USB Hub faster? and Suspend Problem?" on Microchip USB forum
    forum.microchip.com/tm.aspx

    Multi-TT hub
    Belkin F5U234v1 (4port), F5U237v1 (7port)
    IOGEAR GUH274 (4port)

    To check your hub for single-TT or multi-TT,
    It is known by bDeviceProtocol field of the hub device descriptor.

                  bDeviceProtocol
    FS/LS hub        0
    HS single-TT     1
    HS multi-TT      2
    

    USBView read out of Belkin F5U234v1 multi-TT hub

    Device Descriptor:
    bcdUSB:             0x0200
    bDeviceClass:         0x09
    bDeviceSubClass:      0x00
    bDeviceProtocol:      0x02
    bMaxPacketSize0:      0x40 (64)
    idVendor:           0x050D (Belkin Components)
    idProduct:          0x0234
    bcdDevice:          0x0000
    iManufacturer:        0x00
    iProduct:             0x00
    iSerialNumber:        0x00
    bNumConfigurations:   0x01
    

    USBView on FTDI site
    www.ftdichip.com/.../usbview.zip

    Tsuneo

Children
  • On a semi-related issue, is it possible for the device side to re-initiaite a sort of USB reset? Something similiar to when you first plug in the USB cable and the device is recognized by the host? The D+ line on our board is pulled high, not under software control so I can not manipulate that.

    Paul

  • "is it possible for the device side to re-initiaite a sort of USB reset? "

    Usually, "soft-detach/attach" using D+ (or D-) pull-up resistor is applied for this purpose, for such as firmware update. But in your case, D+ pull-up is fixed one.

    I've seen an implementation in which,
    - D+ pull-up resistor is an external one and fixed
    - At the power-up, the firmware sets the USB engine to drive D+/D- line low (SE0), until the start-up initialization (except for the engine) finishes.
    - When initialized, the firmware resets the engine once, releases D+/D- line drive and initializes it.

    At plug-in, (root) hub doesn't drive D+/D- line until it detects connection; until one of these lines goes high. Therefore, this method is relatively safe, though I don't like it :-)

    For re-enumeration, however, this method may cause output conflict of D+/D- line drivers between your device and hub. The hub puts SOF periodically. When there is any other device on the hub, OUT transactions to other device are sent to your device, too.

    If you have to do it on the device side, modify the circuit to control D+ pull-up resistor.

    To issue bus reset from the host side,
    - 'device reset' control request, when the PC device driver supports it.
    - For Windows, SetupDiChangeState ( DICS_ENABLE / DICS_DISABLE )

    Tsuneo