Skip to content

Commit 8c204e7

Browse files
committed
usbd: Add support for configuration open and reset callbacks.
Implement by overriding USBInterface.handle_open or handle_reset. Signed-off-by: Angus Gratton <angus@redyak.com.au>
1 parent 8ef4be6 commit 8c204e7

File tree

1 file changed

+63
-9
lines changed

1 file changed

+63
-9
lines changed

micropython/usbd/device.py

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ def __init__(self):
7878
descriptor_device_cb=self._descriptor_device_cb,
7979
descriptor_config_cb=self._descriptor_config_cb,
8080
descriptor_string_cb=self._descriptor_string_cb,
81-
open_driver_cb=self._open_driver_cb,
81+
open_cb=self._open_cb,
82+
reset_cb=self._reset_cb,
8283
control_xfer_cb=self._control_xfer_cb,
8384
xfer_cb=self._xfer_cb,
8485
)
@@ -286,9 +287,37 @@ def _descriptor_string_cb(self, index):
286287
except IndexError:
287288
return None
288289

289-
def _open_driver_cb(self, interface_desc_view):
290-
# Singleton callback from TinyUSB custom class driver
291-
pass
290+
def _open_cb(self, interface_desc_view):
291+
# Singleton callback from TinyUSB custom class driver, when USB host does
292+
# Set Configuration. The "runtime class device" accepts all interfaces that
293+
# it has sent in descriptors, and calls this callback.
294+
295+
# Walk the view of the "claimed" descriptor data provided in the
296+
# callback and call handle_open() on each claimed interface
297+
#
298+
# ... this may be unnecessary at the moment, as only one configuration is supported so we
299+
# can probably assume all the interfaces will be included.
300+
i = 0
301+
while i < len(interface_desc_view):
302+
# descriptor length, type, and index (if it's an interface descriptor)
303+
dl, dt, di = interface_desc_view[i:i+3]
304+
if dt == _STD_DESC_INTERFACE_TYPE:
305+
if di >= self._usbd.static.itf_max:
306+
di -= self._usbd.static.itf_max
307+
self._itfs[di].handle_open()
308+
i += dl
309+
assert dl
310+
311+
def _reset_cb(self):
312+
# Callback when the USB device is reset by the host
313+
314+
# Cancel outstanding transfer callbacks
315+
for k in self._ep_cbs.keys():
316+
self._ep_cbs[k] = None
317+
318+
# Allow interfaces to respond to the reset
319+
for itf in self._itfs:
320+
itf.handle_reset()
292321

293322
def _submit_xfer(self, ep_addr, data, done_cb=None):
294323
# Singleton function to submit a USB transfer (of any type except control).
@@ -387,6 +416,7 @@ def __init__(
387416
self.bInterfaceSubClass = bInterfaceSubClass
388417
self.bInterfaceProtocol = bInterfaceProtocol
389418
self.interface_str = interface_str
419+
self._open = False
390420

391421
def get_itf_descriptor(self, num_eps, itf_idx, str_idx):
392422
# Return the interface descriptor binary data and associated other
@@ -466,6 +496,27 @@ def get_endpoint_descriptors(self, ep_addr, str_idx):
466496
# start from ep_addr, optionally with the utils.EP_IN_FLAG bit set.)
467497
return (b"", [], [])
468498

499+
def handle_open(self):
500+
# Callback called when the USB host accepts the device configuration.
501+
#
502+
# Override this function to initiate any operations that the USB interface
503+
# should do when the USB device is configured to the host.
504+
self._open = True
505+
506+
def handle_reset(self):
507+
# Callback called on every registered interface when the USB device is
508+
# reset by the host. This can happen when the USB device is unplugged,
509+
# or if the host triggers a reset for some other reason.
510+
#
511+
# At this point, no USB functionality is available - handle_open() will
512+
# be called later if/when the USB host re-enumerates and configures the
513+
# interface.
514+
self._open = False
515+
516+
def is_open(self):
517+
# Returns True if the interface is in use
518+
return self._open
519+
469520
def handle_device_control_xfer(self, stage, request):
470521
# Control transfer callback. Override to handle a non-standard device
471522
# control transfer where bmRequestType Recipient is Device, Type is
@@ -486,11 +537,11 @@ def handle_device_control_xfer(self, stage, request):
486537
# The function can call split_bmRequestType() to split bmRequestType into
487538
# (Recipient, Type, Direction).
488539
#
489-
# Result:
540+
# Result, any of:
490541
#
491-
# - True to continue the request False to STALL the endpoint A buffer
492-
# - interface object to provide a buffer to the host as part of the
493-
# - transfer, if possible.
542+
# - True to continue the request, False to STALL the endpoint.
543+
# - Buffer interface object to provide a buffer to the host as part of the
544+
# transfer, if possible.
494545
return False
495546

496547
def handle_interface_control_xfer(self, stage, request):
@@ -512,7 +563,8 @@ def handle_interface_control_xfer(self, stage, request):
512563
def handle_endpoint_control_xfer(self, stage, request):
513564
# Control transfer callback. Override to handle a device
514565
# control transfer where bmRequestType Recipient is Endpoint and
515-
# the lower byte of wIndex indicates an endpoint address associated with this interface.
566+
# the lower byte of wIndex indicates an endpoint address associated
567+
# with this interface.
516568
#
517569
# bmRequestType Type will generally have any value except
518570
# utils.REQ_TYPE_STANDARD, as Standard endpoint requests are handled by
@@ -546,4 +598,6 @@ def submit_xfer(self, ep_addr, data, done_cb=None):
546598
#
547599
# Note that done_cb may be called immediately, possibly before this
548600
# function has returned to the caller.
601+
if not self._open:
602+
raise RuntimeError()
549603
return get_usbdevice()._submit_xfer(ep_addr, data, done_cb)

0 commit comments

Comments
 (0)