finalisers for SPI; clean up sdcardio; reset all pins after finalisers; avoid USB SD races#10699
Merged
tannewt merged 2 commits intoadafruit:mainfrom Nov 1, 2025
Merged
Conversation
f21a2e2 to
d708bdf
Compare
…s; avoid USB SD races
d708bdf to
6b458a9
Compare
tannewt
requested changes
Oct 31, 2025
Member
tannewt
left a comment
There was a problem hiding this comment.
Overall this looks good. You should be able to remove more never_reset machinery. You will still need to pass never reset into the pin tracking because we still bulk reset them.
Collaborator
Author
|
Tested on Metro ESP32-S3, Fruit Jam, Feather nRF52840 with AdaLogger FeatherWing, ESP32-S3 TFT Feather with AdaLogger, PyPortal. All mounted SD card manually except for Fruit Jam. The USB presentation worked and I was able to read files. I could write files when the SD card was mounted Ready for approval to merge. |
29 tasks
dhalbert
added a commit
to dhalbert/circuitpython
that referenced
this pull request
Feb 22, 2026
Before adafruit#10699, `reset_all_pins()` was called in `reset_port()`. That changed to reset all pins in main after the finalizers ran. However, this mean that pins were not all reset on power-up or hard reset. This caused adafruit#1841, because the power control pin for Feather S3 TFT was not powering the display as required before the display was reset. Add a call to `reset_all_pins()` early in `main()` to serve the same purpose.
This was referenced Feb 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is a multi-part fix for race conditions occurring on VM shutdown when an SD card mounted in user code is exposed via USB MSC. There can be filesystem operations initiated by the host that are in process when
code.pyends., which cause crashes or USB disconnects. In the process of debugging this, I found several other things to clean up and add.SPI resetting when VM shuts down
Previously, many ports had
spi_reset()orreset_spi()routines that reset all SPI buses that were not persistent when the VM shut down. If there were host-initiated USB MSC operations (usually just reads) on a user-mounted filesystem, these forced resets would interfere with those USB MSC operations, or vice versa. For instance, on Espressif, the forced resets would fail if an SPI operation was in progress.busio.SPI()to have a finaliser. This obviates the need for a global SPI reset.reset_spi()andspi_reset(). Thecommon_hal_busio_spi_deinit()routines take care of all SPI resetting necessary when the finalisers are run.common_hal_busio_spi_mark_deinit()routines to allbusio.SPIimplementations, and call them as appropriate in the variousSPI.cfiles.common_hal_busio_spi_mark_deinit()inshared-module/displayio/__init__.cwhen aFourWirebus is copied. This mimics what was already done fordisplayioI2C bus copies, whenbusio.I2C()was changed to use a finaliser.Don't reset pins before finalisers run
reset_port()is called inmain.cbefore finalisers are run. On all ports,reset_port()would callreset_all_pins(). However, this meant that SPI pins would get reset while SD card operations might still be in progress, allowing the kind of race conditions described above.reset_all_pins()out of the port-specificreset_port()routines, and instead call it inmain.cafterstop_mp(), and so therefore after the finalisers run. All ports providereset_all_pins(), so it does not need to be called individually in each port. I was initially thinking about add areset_port_after_finalisers()routine for each port that calledreset_all_pins(), but this was not necessary.sdcardioimprovementscmd_nodata, which wasboolbut should have beenint.-MP_EIOinstead of-EIOseveral places. The value is the same.persistent_mountflag tosdcardio_sdcard_construct()to distinguish user-mounted SD cards from automounted SD card mounts that persist across VM's. This flag is used to implement the next item.lock_and_configure()inSDCard.c. Once it has been acquired, check whether the VM is still running and whether the mount is persistent before proceeding. If the mount is not persistent and the VM is no longer running, give up the lock.supervisor/shared/usb/usb_msc_flash.cfixesCIRCUITPY_SDCARD_USBinstead ofCIRCUITPY_SDCARDIO.#ifdef SDCARD_LUNinstead ofCIRCUITPY_SDCARD_USB.get_vfs()will now check if the LUN passed in is for a user-mounted SD card but the VM is no longer running, and will return NULL. This prevents USB MSC operations that are attempted to be initiated after the VM starts to shut down. However, there are still races that can happen and the other checks above avoid those races.Other
bool vm_is_running()routine, which istruewhen the VM has finished starting up and is set tofalsewhen it starts to shut down. This supports the "VM is running" checks mentioned above. The flag is set and cleared inmain.c.This is a draft to start with because I need to test the non-Espressif ports for SPI issues, especially with SD cards, and I can use the PR builds for that testing.