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?
Thanks.
The documentation states the select endpoint command also initializes an internal pointer to the start of the selected buffer in EP_RAM. Sounds like this is more than I need to do as all i want is to look at the select endpoint register.
Currently my IN endpoint ISR sets an event which I wait on, but only when writing more than 64 bytes. I have added that logic to all writes and am seeing the data I would expect, so I believe I was overwritting the buffer. This is essentially setting a flag in the ISR which was one of your suggestions.
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
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 )