33# These contain the classes and utilities that are needed to
44# implement a USB device, not any complete USB drivers.
55#
6- # MIT license; Copyright (c) 2022-2024 Angus Gratton
6+ # MIT license; Copyright (c) 2022-2024 Angus Gratton, 2025 Harm Lammers
77from micropython import const
88import machine
99import struct
10+ import time
1011
1112try :
1213 from _thread import get_ident
@@ -108,7 +109,6 @@ def config( # noqa: PLR0913
108109 device_class = 0 ,
109110 device_subclass = 0 ,
110111 device_protocol = 0 ,
111- config_str = None ,
112112 max_power_ma = None ,
113113 remote_wakeup = False ,
114114 ):
@@ -166,7 +166,9 @@ def maybe_set(value, idx):
166166 # Keep track of the interface and endpoint indexes
167167 itf_num = builtin_driver .itf_max
168168 ep_num = max (builtin_driver .ep_max , 1 ) # Endpoint 0 always reserved for control
169- while len (strs ) < builtin_driver .str_max :
169+ while len (strs ) < builtin_driver .str_max - 1 : # This is possibly unnecessary or wrong because
170+ # https://docs.micropython.org/en/latest/library/machine.USBDevice.html
171+ # states all string values except index 0 should be plain ASCII
170172 strs .append (None ) # Reserve other string indexes used by builtin drivers
171173 initial_cfg = builtin_driver .desc_cfg or (b"\x00 " * _STD_DESC_CONFIG_LEN )
172174
@@ -204,10 +206,11 @@ def maybe_set(value, idx):
204206 )
205207
206208 # Configuration string is optional but supported
207- iConfiguration = 0
208209 if configuration_str :
209210 iConfiguration = len (strs )
210211 strs .append (configuration_str )
212+ else :
213+ iConfiguration = 0
211214
212215 if max_power_ma is not None :
213216 # Convert from mA to the units used in the descriptor
@@ -665,6 +668,7 @@ def interface(
665668 bInterfaceSubClass = _INTERFACE_SUBCLASS_NONE ,
666669 bInterfaceProtocol = _PROTOCOL_NONE ,
667670 iInterface = 0 ,
671+ bAlternateSetting = 0 ,
668672 ):
669673 # Utility function to append a standard Interface descriptor, with
670674 # the properties specified in the parameter list.
@@ -680,7 +684,7 @@ def interface(
680684 _STD_DESC_INTERFACE_LEN , # bLength
681685 _STD_DESC_INTERFACE_TYPE , # bDescriptorType
682686 bInterfaceNumber ,
683- 0 , # bAlternateSetting, not currently supported
687+ bAlternateSetting ,
684688 bNumEndpoints ,
685689 bInterfaceClass ,
686690 bInterfaceSubClass ,
@@ -791,17 +795,18 @@ class Buffer:
791795 # approximate a Python-based single byte ringbuffer.
792796 #
793797 def __init__ (self , length ):
798+ self ._l = length
794799 self ._b = memoryview (bytearray (length ))
795- # number of bytes in buffer read to read, starting at index 0. Updated
800+ # number of bytes in buffer ready to read, starting at index 0. Updated
796801 # by both producer & consumer.
797802 self ._n = 0
798- # start index of a pending write into the buffer, if any. equals
803+ # start index of a pending write into the buffer, if any. Equals
799804 # len(self._b) if no write is pending. Updated by producer only.
800805 self ._w = length
801806
802807 def writable (self ):
803808 # Number of writable bytes in the buffer. Assumes no pending write is outstanding.
804- return len ( self ._b ) - self ._n
809+ return self ._l - self ._n
805810
806811 def readable (self ):
807812 # Number of readable bytes in the buffer. Assumes no pending read is outstanding.
@@ -815,16 +820,16 @@ def pend_write(self, wmax=None):
815820 # this many bytes long.
816821 #
817822 # (No critical section needed as self._w is only updated by the producer.)
818- self ._w = self ._n
819- end = (self . _w + wmax ) if wmax else len ( self ._b )
820- return self ._b [self . _w : end ]
823+ self ._w = ( _w := self ._n )
824+ end = (_w + wmax ) if wmax else self ._l
825+ return self ._b [_w : end ]
821826
822827 def finish_write (self , nbytes ):
823828 # Called by the producer to indicate it wrote nbytes into the buffer.
824829 ist = machine .disable_irq ()
825830 try :
826- assert nbytes <= len ( self ._b ) - self ._w # can't say we wrote more than was pended
827- if self ._n == self . _w :
831+ assert nbytes <= self ._l - ( _w := self ._w ) # can't say we wrote more than was pended
832+ if self ._n == _w :
828833 # no data was read while the write was happening, so the buffer is already in place
829834 # (this is the fast path)
830835 self ._n += nbytes
@@ -834,13 +839,14 @@ def finish_write(self, nbytes):
834839 #
835840 # As this updates self._n we have to do it in the critical
836841 # section, so do it byte by byte to avoid allocating.
842+ _b = self ._b
837843 while nbytes > 0 :
838- self . _b [self ._n ] = self . _b [self ._w ]
844+ _b [self ._n ] = _b [self ._w ]
839845 self ._n += 1
840846 self ._w += 1
841847 nbytes -= 1
842848
843- self ._w = len ( self ._b )
849+ self ._w = self ._l
844850 finally :
845851 machine .enable_irq (ist )
846852
@@ -866,10 +872,11 @@ def finish_read(self, nbytes):
866872 assert nbytes <= self ._n # can't say we read more than was available
867873 i = 0
868874 self ._n -= nbytes
875+ _b = self ._b
869876 while i < self ._n :
870877 # consumer only read part of the buffer, so shuffle remaining
871878 # read data back towards index 0 to avoid fragmentation
872- self . _b [i ] = self . _b [i + nbytes ]
879+ _b [i ] = _b [i + nbytes ]
873880 i += 1
874881 finally :
875882 machine .enable_irq (ist )
@@ -881,4 +888,4 @@ def readinto(self, b):
881888 if to_r :
882889 b [:to_r ] = pr [:to_r ]
883890 self .finish_read (to_r )
884- return to_r
891+ return to_r
0 commit comments