@@ -29,8 +29,19 @@ void tuh_mount_cb(uint8_t dev_addr) {
2929 _mounted_devices |= 1 << dev_addr ;
3030}
3131
32+ // Bulk IN buffering needs the completion callback to run from CircuitPython's
33+ // background tasks, where it can't interrupt read() partway through.
34+ #define IN_BUFFER_ENABLED (CFG_TUSB_OS == OPT_OS_NONE || CFG_TUSB_OS == OPT_OS_PICO)
35+
36+ #if IN_BUFFER_ENABLED
37+ static void _in_buffer_release_device (uint8_t daddr );
38+ #endif
39+
3240void tuh_umount_cb (uint8_t dev_addr ) {
3341 _mounted_devices &= ~(1 << dev_addr );
42+ #if IN_BUFFER_ENABLED
43+ _in_buffer_release_device (dev_addr );
44+ #endif
3445}
3546
3647static xfer_result_t _xfer_result ;
@@ -57,6 +68,122 @@ static uint8_t *_ensure_dma_buffer(usb_core_device_obj_t *self, const uint8_t *b
5768}
5869
5970#endif
71+
72+ #if IN_BUFFER_ENABLED
73+ // Once Python reads a bulk IN endpoint, keep a transfer queued on it so the device
74+ // is polled while Python is busy. Completed transfers wait here for read(). When
75+ // the queue is full no transfer is queued, and the device holds its data.
76+ #define IN_BUFFER_SLOTS 4
77+ #define IN_BUFFER_DEPTH 8
78+ // How long a released buffer is kept for an aborted transfer to finish writing.
79+ #define IN_BUFFER_STALE_MS 10
80+
81+ typedef struct {
82+ usb_core_device_obj_t * owner ; // NULL when the slot is not in use
83+ uint8_t * data ; // IN_BUFFER_DEPTH transfers of mps bytes each
84+ uint8_t * stale_data ; // released while a transfer was queued, freed by _in_buffer_reap()
85+ uint32_t stale_time ;
86+ uint16_t len [IN_BUFFER_DEPTH ];
87+ uint16_t mps ;
88+ uint16_t offset ; // bytes of the head transfer already read
89+ uint8_t daddr ;
90+ uint8_t ep_addr ;
91+ uint8_t head ;
92+ uint8_t count ;
93+ uint8_t gen ; // changed on release so an old completion is ignored
94+ bool busy ;
95+ xfer_result_t error ;
96+ } in_buffer_t ;
97+
98+ static in_buffer_t _in_buffers [IN_BUFFER_SLOTS ];
99+
100+ static void _in_buffer_queue (in_buffer_t * buf );
101+
102+ static void _in_buffer_cb (tuh_xfer_t * xfer ) {
103+ in_buffer_t * buf = & _in_buffers [xfer -> user_data & 0xff ];
104+ if (buf -> owner == NULL || !buf -> busy || buf -> gen != (uint8_t )(xfer -> user_data >> 8 )) {
105+ return ;
106+ }
107+ buf -> busy = false;
108+ if (xfer -> result != XFER_RESULT_SUCCESS ) {
109+ buf -> error = xfer -> result ;
110+ return ;
111+ }
112+ buf -> len [(buf -> head + buf -> count ) % IN_BUFFER_DEPTH ] = xfer -> actual_len ;
113+ buf -> count ++ ;
114+ _in_buffer_queue (buf );
115+ }
116+
117+ static void _in_buffer_queue (in_buffer_t * buf ) {
118+ if (buf -> busy || buf -> error != XFER_RESULT_SUCCESS || buf -> count == IN_BUFFER_DEPTH ) {
119+ return ;
120+ }
121+ tuh_xfer_t xfer = {
122+ .daddr = buf -> daddr ,
123+ .ep_addr = buf -> ep_addr ,
124+ .buflen = buf -> mps ,
125+ .buffer = buf -> data + ((buf -> head + buf -> count ) % IN_BUFFER_DEPTH ) * buf -> mps ,
126+ .complete_cb = _in_buffer_cb ,
127+ .user_data = (buf - _in_buffers ) | (buf -> gen << 8 ),
128+ };
129+ buf -> busy = tuh_edpt_xfer (& xfer );
130+ }
131+
132+ static void _in_buffer_release (in_buffer_t * buf ) {
133+ if (buf -> busy ) {
134+ // Some host controllers can still write into the buffer after an abort, and
135+ // TinyUSB may have its completion queued, so free it later.
136+ tuh_edpt_abort_xfer (buf -> daddr , buf -> ep_addr );
137+ buf -> stale_data = buf -> data ;
138+ buf -> stale_time = supervisor_ticks_ms32 ();
139+ } else {
140+ port_free (buf -> data );
141+ }
142+ buf -> data = NULL ;
143+ buf -> owner = NULL ;
144+ buf -> busy = false;
145+ buf -> head = 0 ;
146+ buf -> count = 0 ;
147+ buf -> offset = 0 ;
148+ buf -> gen ++ ;
149+ }
150+
151+ static bool _in_buffer_in_use (uint8_t daddr , uint8_t ep_addr ) {
152+ for (size_t i = 0 ; i < IN_BUFFER_SLOTS ; i ++ ) {
153+ in_buffer_t * buf = & _in_buffers [i ];
154+ if (buf -> owner != NULL && buf -> daddr == daddr && buf -> ep_addr == ep_addr ) {
155+ return true;
156+ }
157+ }
158+ return false;
159+ }
160+
161+ static void _in_buffer_release_device (uint8_t daddr ) {
162+ for (size_t i = 0 ; i < IN_BUFFER_SLOTS ; i ++ ) {
163+ in_buffer_t * buf = & _in_buffers [i ];
164+ if (buf -> owner != NULL && buf -> daddr == daddr ) {
165+ _in_buffer_release (buf );
166+ }
167+ }
168+ }
169+
170+ // Runs TinyUSB until released buffers can't be written to or completed into, then
171+ // frees them. Called from Python context, never from a finaliser or callback.
172+ static void _in_buffer_reap (void ) {
173+ for (size_t i = 0 ; i < IN_BUFFER_SLOTS ; i ++ ) {
174+ in_buffer_t * buf = & _in_buffers [i ];
175+ if (buf -> stale_data == NULL ) {
176+ continue ;
177+ }
178+ while (supervisor_ticks_ms32 () - buf -> stale_time < IN_BUFFER_STALE_MS ) {
179+ RUN_BACKGROUND_TASKS ;
180+ }
181+ port_free (buf -> stale_data );
182+ buf -> stale_data = NULL ;
183+ }
184+ }
185+ #endif
186+
60187bool common_hal_usb_core_device_construct (usb_core_device_obj_t * self , uint8_t device_address ) {
61188 if (!tuh_inited ()) {
62189 mp_raise_RuntimeError (MP_ERROR_TEXT ("No usb host port initialized" ));
@@ -82,9 +209,23 @@ void common_hal_usb_core_device_deinit(usb_core_device_obj_t *self) {
82209 if (common_hal_usb_core_device_deinited (self )) {
83210 return ;
84211 }
212+ #if IN_BUFFER_ENABLED
213+ for (size_t i = 0 ; i < IN_BUFFER_SLOTS ; i ++ ) {
214+ if (_in_buffers [i ].owner == self ) {
215+ _in_buffer_release (& _in_buffers [i ]);
216+ }
217+ }
218+ #endif
85219 size_t open_size = sizeof (self -> open_endpoints );
86220 for (size_t i = 0 ; i < open_size ; i ++ ) {
87221 if (self -> open_endpoints [i ] != 0 ) {
222+ #if IN_BUFFER_ENABLED
223+ // Another Device object for the same device is still buffering it.
224+ if (_in_buffer_in_use (self -> device_address , self -> open_endpoints [i ])) {
225+ self -> open_endpoints [i ] = 0 ;
226+ continue ;
227+ }
228+ #endif
88229 tuh_edpt_close (self -> device_address , self -> open_endpoints [i ]);
89230 self -> open_endpoints [i ] = 0 ;
90231 }
@@ -411,6 +552,27 @@ static size_t _xfer(tuh_xfer_t *xfer, mp_int_t timeout, bool our_buffer, bool ra
411552 return _handle_timed_transfer_callback (xfer , timeout , our_buffer , raise_on_timeout );
412553}
413554
555+ static tusb_desc_endpoint_t const * _find_endpoint_descriptor (usb_core_device_obj_t * self , mp_int_t endpoint ) {
556+ tusb_desc_configuration_t * desc_cfg = (tusb_desc_configuration_t * )self -> configuration_descriptor ;
557+
558+ uint32_t total_length = tu_le16toh (desc_cfg -> wTotalLength );
559+ uint8_t const * desc_end = ((uint8_t const * )desc_cfg ) + total_length ;
560+ uint8_t const * p_desc = tu_desc_next (desc_cfg );
561+
562+ // parse each interfaces
563+ while (p_desc < desc_end ) {
564+ if (TUSB_DESC_ENDPOINT == tu_desc_type (p_desc )) {
565+ tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const * )p_desc ;
566+ if (desc_ep -> bEndpointAddress == endpoint ) {
567+ return desc_ep ;
568+ }
569+ }
570+
571+ p_desc = tu_desc_next (p_desc );
572+ }
573+ return NULL ;
574+ }
575+
414576static bool _open_endpoint (usb_core_device_obj_t * self , mp_int_t endpoint ) {
415577 bool endpoint_open = false;
416578 size_t open_size = sizeof (self -> open_endpoints );
@@ -431,27 +593,10 @@ static bool _open_endpoint(usb_core_device_obj_t *self, mp_int_t endpoint) {
431593 return false;
432594 }
433595
434- tusb_desc_configuration_t * desc_cfg = (tusb_desc_configuration_t * )self -> configuration_descriptor ;
435-
436- uint32_t total_length = tu_le16toh (desc_cfg -> wTotalLength );
437- uint8_t const * desc_end = ((uint8_t const * )desc_cfg ) + total_length ;
438- uint8_t const * p_desc = tu_desc_next (desc_cfg );
439-
440- // parse each interfaces
441- while (p_desc < desc_end ) {
442- if (TUSB_DESC_ENDPOINT == tu_desc_type (p_desc )) {
443- tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const * )p_desc ;
444- if (desc_ep -> bEndpointAddress == endpoint ) {
445- break ;
446- }
447- }
448-
449- p_desc = tu_desc_next (p_desc );
450- }
451- if (p_desc >= desc_end ) {
596+ tusb_desc_endpoint_t const * desc_ep = _find_endpoint_descriptor (self , endpoint );
597+ if (desc_ep == NULL ) {
452598 return false;
453599 }
454- tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const * )p_desc ;
455600
456601 bool open = tuh_edpt_open (self -> device_address , desc_ep );
457602 if (open ) {
@@ -460,6 +605,95 @@ static bool _open_endpoint(usb_core_device_obj_t *self, mp_int_t endpoint) {
460605 return open ;
461606}
462607
608+ #if IN_BUFFER_ENABLED
609+ // Returns the buffer for a bulk IN endpoint, claiming a slot on first use. Returns
610+ // NULL for other endpoints, or when no slot or memory is free, and read() then
611+ // reads directly as before.
612+ static in_buffer_t * _in_buffer_get (usb_core_device_obj_t * self , mp_int_t endpoint ) {
613+ in_buffer_t * free_buf = NULL ;
614+ for (size_t i = 0 ; i < IN_BUFFER_SLOTS ; i ++ ) {
615+ in_buffer_t * buf = & _in_buffers [i ];
616+ if (buf -> owner != NULL && buf -> daddr == self -> device_address && buf -> ep_addr == endpoint ) {
617+ return buf ;
618+ }
619+ if (free_buf == NULL && buf -> owner == NULL && buf -> stale_data == NULL ) {
620+ free_buf = buf ;
621+ }
622+ }
623+ if (free_buf == NULL || tu_edpt_dir (endpoint ) != TUSB_DIR_IN ||
624+ (_mounted_devices & (1 << self -> device_address )) == 0 ) {
625+ return NULL ;
626+ }
627+ tusb_desc_endpoint_t const * desc_ep = _find_endpoint_descriptor (self , endpoint );
628+ if (desc_ep == NULL || desc_ep -> bmAttributes .xfer != TUSB_XFER_BULK ) {
629+ return NULL ;
630+ }
631+ uint16_t mps = tu_edpt_packet_size (desc_ep );
632+ if (mps == 0 ) {
633+ return NULL ;
634+ }
635+ free_buf -> data = port_malloc (IN_BUFFER_DEPTH * mps , true);
636+ if (free_buf -> data == NULL ) {
637+ return NULL ;
638+ }
639+ free_buf -> owner = self ;
640+ free_buf -> mps = mps ;
641+ free_buf -> daddr = self -> device_address ;
642+ free_buf -> ep_addr = endpoint ;
643+ free_buf -> error = XFER_RESULT_SUCCESS ;
644+ return free_buf ;
645+ }
646+
647+ // Copies queued packets into buffer until it is full or a short packet ends the
648+ // transfer, like a direct read.
649+ static size_t _in_buffer_read (in_buffer_t * buf , uint8_t * buffer , size_t len , mp_int_t timeout , bool raise_on_timeout ) {
650+ _in_buffer_queue (buf );
651+ uint32_t start_time = supervisor_ticks_ms32 ();
652+ while ((timeout == 0 || supervisor_ticks_ms32 () - start_time < (uint32_t )timeout ) &&
653+ !mp_hal_is_interrupted () &&
654+ buf -> count == 0 && buf -> busy ) {
655+ RUN_BACKGROUND_TASKS ;
656+ }
657+ if (mp_hal_is_interrupted ()) {
658+ return 0 ;
659+ }
660+ if (buf -> count == 0 ) {
661+ xfer_result_t error = buf -> error ;
662+ buf -> error = XFER_RESULT_SUCCESS ;
663+ if (error == XFER_RESULT_STALLED ) {
664+ mp_raise_usb_core_USBError (MP_ERROR_TEXT ("Pipe error" ));
665+ }
666+ if ((error == XFER_RESULT_SUCCESS && !buf -> busy ) ||
667+ (error != XFER_RESULT_SUCCESS && error != XFER_RESULT_TIMEOUT )) {
668+ mp_raise_usb_core_USBError (NULL );
669+ }
670+ if (raise_on_timeout ) {
671+ mp_raise_usb_core_USBTimeoutError ();
672+ }
673+ return 0 ;
674+ }
675+ size_t total = 0 ;
676+ while (buf -> count > 0 && total < len ) {
677+ uint16_t packet_len = buf -> len [buf -> head ];
678+ size_t n = MIN (len - total , (size_t )(packet_len - buf -> offset ));
679+ memcpy (buffer + total , buf -> data + buf -> head * buf -> mps + buf -> offset , n );
680+ total += n ;
681+ buf -> offset += n ;
682+ if (buf -> offset < packet_len ) {
683+ break ;
684+ }
685+ buf -> offset = 0 ;
686+ buf -> head = (buf -> head + 1 ) % IN_BUFFER_DEPTH ;
687+ buf -> count -- ;
688+ if (packet_len < buf -> mps ) {
689+ break ;
690+ }
691+ }
692+ _in_buffer_queue (buf );
693+ return total ;
694+ }
695+ #endif
696+
463697mp_int_t common_hal_usb_core_device_write (usb_core_device_obj_t * self , mp_int_t endpoint , const uint8_t * buffer , mp_int_t len , mp_int_t timeout ) {
464698 if (!_open_endpoint (self , endpoint )) {
465699 mp_raise_usb_core_USBError (NULL );
@@ -497,6 +731,14 @@ mp_int_t common_hal_usb_core_device_read(usb_core_device_obj_t *self, mp_int_t e
497731 return 0 ;
498732 }
499733
734+ #if IN_BUFFER_ENABLED
735+ _in_buffer_reap ();
736+ in_buffer_t * in_buf = _in_buffer_get (self , endpoint );
737+ if (in_buf != NULL ) {
738+ return _in_buffer_read (in_buf , buffer , len , timeout , raise_on_timeout );
739+ }
740+ #endif
741+
500742 #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE
501743 // Ensure buffer is in DMA-capable memory
502744 uint8_t * dma_buffer = _ensure_dma_buffer (self , buffer , len , false); // false = for read
0 commit comments