@@ -74,8 +74,13 @@ static void usb_error(int rc, const char *caption, int exitcode)
7474static const int AW_USB_MAX_BULK_SEND = 512 * 1024 ;
7575static const int AW_USB_PROGRESS_BULK_SEND = 128 * 1024 ;
7676static const int AW_USB_FAST_PROGRESS_BULK_SEND = 1024 * 1024 ;
77+ static const int AW_USB_ASYNC_BULK_SEND = 16 * 1024 ;
7778static const int FEL_RX_DMA_PACKET_SIZE = 512 ;
7879
80+ enum {
81+ AW_USB_ASYNC_DEPTH = 4 ,
82+ };
83+
7984#define DRAM_BASE 0x40000000
8085#define DRAM_SIZE 0x80000000
8186#define DRAM_END (DRAM_BASE + DRAM_SIZE)
@@ -84,27 +89,160 @@ static uint32_t fel_rx_dma_thunk[] = {
8489 #include "thunks/fel-rx-dma-thunk.h"
8590};
8691
92+ static int usb_transfer_status_to_error (enum libusb_transfer_status status )
93+ {
94+ switch (status ) {
95+ case LIBUSB_TRANSFER_COMPLETED :
96+ return 0 ;
97+ case LIBUSB_TRANSFER_TIMED_OUT :
98+ return LIBUSB_ERROR_TIMEOUT ;
99+ case LIBUSB_TRANSFER_CANCELLED :
100+ return LIBUSB_ERROR_INTERRUPTED ;
101+ case LIBUSB_TRANSFER_STALL :
102+ return LIBUSB_ERROR_PIPE ;
103+ case LIBUSB_TRANSFER_NO_DEVICE :
104+ return LIBUSB_ERROR_NO_DEVICE ;
105+ case LIBUSB_TRANSFER_OVERFLOW :
106+ return LIBUSB_ERROR_OVERFLOW ;
107+ case LIBUSB_TRANSFER_ERROR :
108+ default :
109+ return LIBUSB_ERROR_IO ;
110+ }
111+ }
112+
113+ static size_t usb_bulk_send_chunk_size (size_t length , size_t max_chunk )
114+ {
115+ size_t chunk = length ;
116+
117+ if (chunk > (size_t )AW_USB_ASYNC_BULK_SEND )
118+ chunk = AW_USB_ASYNC_BULK_SEND ;
119+ if (chunk > max_chunk )
120+ chunk = max_chunk ;
121+ return chunk ;
122+ }
123+
124+ struct usb_bulk_async_slot {
125+ struct libusb_transfer * transfer ;
126+ size_t length ;
127+ int * event_completed ;
128+ bool completed ;
129+ };
130+
131+ static void LIBUSB_CALL usb_bulk_send_async_cb (struct libusb_transfer * transfer )
132+ {
133+ struct usb_bulk_async_slot * slot = transfer -> user_data ;
134+
135+ slot -> completed = true;
136+ * slot -> event_completed = 1 ;
137+ }
138+
139+ static void usb_bulk_send_async_submit (libusb_device_handle * usb , int ep ,
140+ struct usb_bulk_async_slot * slot ,
141+ const unsigned char * data , size_t length )
142+ {
143+ int rc ;
144+
145+ slot -> length = length ;
146+ slot -> completed = false;
147+ libusb_fill_bulk_transfer (slot -> transfer , usb , ep ,
148+ (unsigned char * )data , length ,
149+ usb_bulk_send_async_cb , slot , USB_TIMEOUT );
150+ rc = libusb_submit_transfer (slot -> transfer );
151+ if (rc )
152+ usb_error (rc , "libusb_submit_transfer()" , 2 );
153+ }
154+
87155/*
88156 * Keep progress updates frequent enough for slow BROM PIO transfers. Once an
89157 * RX-DMA thunk is installed, 1 MiB chunks avoid host-side progress overhead.
158+ *
159+ * The USB writes themselves use a small queue of asynchronous transfers. This
160+ * keeps the host controller fed during fast transfers without relying on large
161+ * per-transfer sizes that would be unsuitable for slow BROM PIO paths.
90162 */
91163static void usb_bulk_send (libusb_device_handle * usb , int ep , const void * data ,
92164 size_t length , size_t max_chunk , bool progress )
93165{
94- size_t chunk ;
95- int rc , sent ;
96- while (length > 0 ) {
97- chunk = length < max_chunk ? length : max_chunk ;
98- rc = libusb_bulk_transfer (usb , ep , (void * )data , chunk ,
99- & sent , USB_TIMEOUT );
100- if (rc != 0 )
101- usb_error (rc , "usb_bulk_send()" , 2 );
102- length -= sent ;
103- data += sent ;
166+ struct usb_bulk_async_slot slots [AW_USB_ASYNC_DEPTH ] = { 0 };
167+ const unsigned char * ptr = data ;
168+ size_t progress_pending = 0 ;
169+ size_t offset = 0 ;
170+ int event_completed = 0 ;
171+ int active = 0 ;
172+ int rc ;
173+ int i ;
174+
175+ if (length == 0 )
176+ return ;
177+
178+ for (i = 0 ; i < AW_USB_ASYNC_DEPTH ; i ++ ) {
179+ slots [i ].transfer = libusb_alloc_transfer (0 );
180+ if (!slots [i ].transfer )
181+ usb_error (LIBUSB_ERROR_NO_MEM ,
182+ "libusb_alloc_transfer()" , 2 );
183+ slots [i ].event_completed = & event_completed ;
184+ }
185+
186+ for (i = 0 ; i < AW_USB_ASYNC_DEPTH && offset < length ; i ++ ) {
187+ size_t chunk = usb_bulk_send_chunk_size (length - offset ,
188+ max_chunk );
189+
190+ usb_bulk_send_async_submit (usb , ep , & slots [i ],
191+ ptr + offset , chunk );
192+ offset += chunk ;
193+ active ++ ;
194+ }
195+
196+ while (active > 0 ) {
197+ event_completed = 0 ;
198+ rc = libusb_handle_events_completed (NULL , & event_completed );
199+ if (rc ) {
200+ if (rc == LIBUSB_ERROR_INTERRUPTED )
201+ continue ;
202+ usb_error (rc , "libusb_handle_events_completed()" , 2 );
203+ }
104204
105- if (progress )
106- progress_update (sent ); /* notification after each chunk */
205+ for (i = 0 ; i < AW_USB_ASYNC_DEPTH ; i ++ ) {
206+ struct libusb_transfer * transfer = slots [i ].transfer ;
207+ size_t chunk ;
208+
209+ if (!slots [i ].completed )
210+ continue ;
211+
212+ slots [i ].completed = false;
213+ active -- ;
214+
215+ rc = usb_transfer_status_to_error (transfer -> status );
216+ if (rc )
217+ usb_error (rc , "usb_bulk_send()" , 2 );
218+ if (transfer -> actual_length != (int )slots [i ].length )
219+ usb_error (LIBUSB_ERROR_IO , "usb_bulk_send()" , 2 );
220+
221+ if (progress ) {
222+ progress_pending += transfer -> actual_length ;
223+ if (progress_pending >= max_chunk ) {
224+ progress_update (progress_pending );
225+ progress_pending = 0 ;
226+ }
227+ }
228+
229+ if (offset >= length )
230+ continue ;
231+
232+ chunk = usb_bulk_send_chunk_size (length - offset ,
233+ max_chunk );
234+ usb_bulk_send_async_submit (usb , ep , & slots [i ],
235+ ptr + offset , chunk );
236+ offset += chunk ;
237+ active ++ ;
238+ }
107239 }
240+
241+ for (i = 0 ; i < AW_USB_ASYNC_DEPTH ; i ++ )
242+ libusb_free_transfer (slots [i ].transfer );
243+
244+ if (progress && progress_pending )
245+ progress_update (progress_pending );
108246}
109247
110248static void usb_bulk_recv (libusb_device_handle * usb , int ep , void * data ,
0 commit comments