Skip to content

Commit 9cdfb69

Browse files
committed
extmod/machine_usb_device: Add static mode USB device support.
This commit implements support for USB device functionality in static mode (when MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE=0), allowing control of built-in USB drivers without runtime descriptor configuration. Key features include automatic USB initialization detection at boot, bitfield-based builtin driver combinations (CDC, MSC, NCM), and dynamic class state management. The system triggers USB re-enumeration when drivers are changed in static mode. Signed-off-by: Andrew Leech <[email protected]>
1 parent 2fbb46c commit 9cdfb69

File tree

7 files changed

+543
-213
lines changed

7 files changed

+543
-213
lines changed

docs/library/machine.USBDevice.rst

Lines changed: 147 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ class USBDevice -- USB Device driver
99
supports native USB.
1010

1111
USBDevice provides a low-level Python API for implementing USB device functions using
12-
Python code.
12+
Python code. This includes both runtime device descriptor configuration and
13+
build-time customization of USB configuration.
1314

1415
.. warning:: This low-level API assumes familiarity with the USB standard. There
1516
are high-level `usb driver modules in micropython-lib`_ which provide a
@@ -188,23 +189,39 @@ Methods
188189
.. attribute:: USBDevice.builtin_driver
189190

190191
This attribute holds the current built-in driver configuration, and must be
191-
set to one of the ``USBDevice.BUILTIN_`` named constants defined on this object.
192+
set to one of the ``USBDevice.BUILTIN_`` constants or a combination of them
193+
using the bitwise OR operator (``|``).
192194

193195
By default it holds the value :data:`USBDevice.BUILTIN_NONE`.
194196

195197
Runtime USB device must be inactive when setting this field. Call the
196198
:func:`USBDevice.active` function to deactivate before setting if necessary
197199
(and again to activate after setting).
198200

201+
**Combining Built-in Drivers:**
202+
203+
Multiple built-in USB classes can be enabled simultaneously by combining
204+
constants with the bitwise OR operator::
205+
206+
usb = machine.USBDevice()
207+
usb.active(False)
208+
# Enable both CDC and MSC
209+
usb.builtin_driver = usb.BUILTIN_CDC | usb.BUILTIN_MSC
210+
usb.active(True)
211+
212+
The combined configuration will dynamically generate appropriate USB
213+
descriptors containing only the selected classes.
214+
199215
If this value is set to any value other than :data:`USBDevice.BUILTIN_NONE` then
200216
the following restrictions apply to the :func:`USBDevice.config` arguments:
201217

202218
- ``desc_cfg`` should begin with the built-in USB interface descriptor data
203219
accessible via :data:`USBDevice.builtin_driver` attribute ``desc_cfg``.
204-
Descriptors appended after the built-in configuration descriptors should use
205-
interface, string and endpoint numbers starting from the max built-in values
206-
defined in :data:`USBDevice.builtin_driver` attributes ``itf_max``, ``str_max`` and
207-
``ep_max``.
220+
The descriptor is dynamically generated based on the combination of classes
221+
enabled. Descriptors appended after the built-in configuration descriptors
222+
should use interface, string and endpoint numbers starting from the max
223+
built-in values defined in :data:`USBDevice.builtin_driver` attributes
224+
``itf_max``, ``str_max`` and ``ep_max``.
208225

209226
- The ``bNumInterfaces`` field in the built-in configuration
210227
descriptor will also need to be updated if any new interfaces
@@ -266,39 +283,134 @@ Constants
266283
---------
267284

268285
.. data:: USBDevice.BUILTIN_NONE
286+
287+
Constant representing no built-in USB drivers enabled. This is the
288+
default value for :data:`USBDevice.builtin_driver`.
289+
269290
.. data:: USBDevice.BUILTIN_DEFAULT
291+
292+
Constant representing the default combination of built-in USB drivers
293+
for this firmware build. The exact combination depends on compile-time
294+
configuration options.
295+
270296
.. data:: USBDevice.BUILTIN_CDC
297+
298+
Constant representing the built-in USB CDC (serial port) driver.
299+
Can be combined with other ``BUILTIN_`` constants using the bitwise
300+
OR operator (``|``).
301+
271302
.. data:: USBDevice.BUILTIN_MSC
303+
304+
Constant representing the built-in USB MSC (mass storage) driver.
305+
Can be combined with other ``BUILTIN_`` constants using the bitwise
306+
OR operator (``|``).
307+
308+
.. data:: USBDevice.BUILTIN_NCM
309+
310+
Constant representing the built-in USB NCM (network) driver.
311+
Only available when ``MICROPY_HW_NETWORK_USBNET`` is enabled in the
312+
firmware build. Can be combined with other ``BUILTIN_`` constants
313+
using the bitwise OR operator (``|``).
314+
315+
.. note:: When NCM is disabled after being active, the network
316+
interface is automatically cleaned up.
317+
272318
.. data:: USBDevice.BUILTIN_CDC_MSC
273319

274-
These constant objects hold the built-in descriptor data which is
275-
compiled into the MicroPython firmware. ``USBDevice.BUILTIN_NONE`` and
276-
``USBDevice.BUILTIN_DEFAULT`` are always present. Additional objects may be present
277-
depending on the firmware build configuration and the actual built-in drivers.
278-
279-
.. note:: Currently at most one of ``USBDevice.BUILTIN_CDC``,
280-
``USBDevice.BUILTIN_MSC`` and ``USBDevice.BUILTIN_CDC_MSC`` is defined
281-
and will be the same object as ``USBDevice.BUILTIN_DEFAULT``.
282-
These constants are defined to allow run-time detection of
283-
the built-in driver (if any). Support for selecting one of
284-
multiple built-in driver configurations may be added in the
285-
future.
286-
287-
These values are assigned to :data:`USBDevice.builtin_driver` to get/set the
288-
built-in configuration.
289-
290-
Each object contains the following read-only fields:
291-
292-
- ``itf_max`` - One more than the highest bInterfaceNumber value used
293-
in the built-in configuration descriptor.
294-
- ``ep_max`` - One more than the highest bEndpointAddress value used
295-
in the built-in configuration descriptor. Does not include any
296-
``IN`` flag bit (0x80).
297-
- ``str_max`` - One more than the highest string descriptor index
298-
value used by any built-in descriptor.
299-
- ``desc_dev`` - ``bytes`` object containing the built-in USB device
300-
descriptor.
301-
- ``desc_cfg`` - ``bytes`` object containing the complete built-in USB
302-
configuration descriptor.
320+
.. deprecated::
321+
Use ``USBDevice.BUILTIN_CDC | USBDevice.BUILTIN_MSC`` instead.
322+
323+
Legacy constant for CDC and MSC combination. Provided for backward
324+
compatibility only.
325+
326+
**Combining Built-in Constants:**
327+
328+
Built-in driver constants can be combined using the bitwise OR operator to
329+
create custom USB device configurations::
330+
331+
# CDC serial port only
332+
usb.builtin_driver = USBDevice.BUILTIN_CDC
333+
334+
# CDC and MSC (serial + mass storage)
335+
usb.builtin_driver = USBDevice.BUILTIN_CDC | USBDevice.BUILTIN_MSC
336+
337+
# All available interfaces
338+
usb.builtin_driver = USBDevice.BUILTIN_CDC | USBDevice.BUILTIN_MSC | USBDevice.BUILTIN_NCM
339+
340+
# No built-in drivers (for fully custom USB devices)
341+
usb.builtin_driver = USBDevice.BUILTIN_NONE
342+
343+
The combined configuration dynamically generates the appropriate USB descriptors
344+
and updates the ``itf_max``, ``ep_max``, and ``str_max`` properties accordingly.
345+
346+
Each constant object contains the following read-only fields:
347+
348+
- ``itf_max`` - One more than the highest bInterfaceNumber value used
349+
in the configuration. Dynamically calculated based on enabled classes.
350+
- ``ep_max`` - One more than the highest bEndpointAddress value used
351+
in the configuration. Does not include any ``IN`` flag bit (0x80).
352+
Dynamically calculated based on enabled classes.
353+
- ``str_max`` - One more than the highest string descriptor index
354+
value used by any descriptor. Dynamically calculated.
355+
- ``desc_dev`` - ``bytes`` object containing the USB device descriptor.
356+
- ``desc_cfg`` - ``bytes`` object containing the complete USB
357+
configuration descriptor for the enabled classes.
358+
359+
Usage Examples
360+
--------------
361+
362+
**Basic CDC Serial Port:**
363+
::
364+
365+
import machine
366+
367+
usb = machine.USBDevice()
368+
usb.active(False)
369+
usb.builtin_driver = usb.BUILTIN_CDC
370+
usb.active(True)
371+
372+
**CDC + Mass Storage:**
373+
::
374+
375+
import machine
376+
377+
usb = machine.USBDevice()
378+
usb.active(False)
379+
usb.builtin_driver = usb.BUILTIN_CDC | usb.BUILTIN_MSC
380+
usb.active(True)
381+
382+
**Network Device with Serial Console:**
383+
::
384+
385+
import machine
386+
387+
usb = machine.USBDevice()
388+
usb.active(False)
389+
# Enable both CDC for console and NCM for networking
390+
usb.builtin_driver = usb.BUILTIN_CDC | usb.BUILTIN_NCM
391+
usb.active(True)
392+
393+
**Switching Configurations at Runtime:**
394+
::
395+
396+
import machine
397+
398+
usb = machine.USBDevice()
399+
400+
# Start with CDC only
401+
usb.active(False)
402+
usb.builtin_driver = usb.BUILTIN_CDC
403+
usb.active(True)
404+
405+
# Later, add mass storage
406+
usb.active(False)
407+
usb.builtin_driver = usb.BUILTIN_CDC | usb.BUILTIN_MSC
408+
usb.active(True)
409+
410+
# Remove MSC, add NCM
411+
usb.active(False)
412+
usb.builtin_driver = usb.BUILTIN_CDC | usb.BUILTIN_NCM
413+
usb.active(True)
414+
303415

304416
.. _usb driver modules in micropython-lib: https://github.com/micropython/micropython-lib/tree/master/micropython/usb#readme

0 commit comments

Comments
 (0)