Skip to content

The sdcardio library is not working correctly #10232

Open
@vano7209

Description

@vano7209

CircuitPython version and board name

Adafruit CircuitPython 10.0.0-alpha.1-19-g380b8bda7a-dirty on 2025-04-08; ESP32-S3-DevKitC-1-N8R8 with ESP32S3
Board ID:espressif_esp32s3_devkitc_1_n8r8
UID:468E334483CD

Code/REPL

import busio
import board
import sdcardio

sd_card_cs_pin  = board.IO15
spi0_module     = busio.SPI(clock=board.IO12, MOSI=board.IO11, MISO=board.IO13)
sd_card = sdcardio.SDCard(spi0_module, sd_card_cs_pin, baudrate=int(250000))

buff_write = bytearray([0xAA]*512)
buff_read = bytearray([0]*512)

sd_card.writeblocks(0, buff_write)
sd_card.readblocks(0, buff_read)

if buff_write[0] != buff_read[0]:
    print(buff_read)

Behavior

Auto-reload is on. Simply save files over USB to run them or enter REPL to disable.

Press any key to enter the REPL. Use CTRL-D to reload.
soft reboot

Auto-reload is on. Simply save files over USB to run them or enter REPL to disable.
main.py output:
bytearray(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02;v'\x00\x00\x00\x01\x01\x00\x0c\xfe\x7f\xe8?\x00\x00\x00\xc1\xffw\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00U\xaa")

Code done running.

Press any key to enter the REPL. Use CTRL-D to reload.

Description

The writeblocks function from the sdcardio library does not work in principle. Any attempts to use it fail. And here's why:

  • The writeblocks call is addressed to the file "shared-bindings/sdcardio/SDCard.c", function "_sdcardio_sdcard_writeblocks":
static mp_obj_t _sdcardio_sdcard_writeblocks(mp_obj_t self_in, mp_obj_t start_block_in, mp_obj_t buf_in) {
    uint32_t start_block = mp_obj_get_int(start_block_in);
    mp_buffer_info_t bufinfo;
    mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ);
    sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t *)self_in;
    int result = common_hal_sdcardio_sdcard_writeblocks(self, start_block, &bufinfo);
    if (result < 0) {
        mp_raise_OSError(-result);
    }
    return mp_const_none;
}
  • This function forwards the call to "shared-module/sdcardio/SDCard.c", function "common_hal_sdcardio_sdcard_writeblocks":
int common_hal_sdcardio_sdcard_writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf) {
    // deinit check is in lock_and_configure_bus()
    if (buf->len % 512 != 0) {
        mp_raise_ValueError_varg(MP_ERROR_TEXT("Buffer must be a multiple of %d bytes"), 512);
    }
    lock_and_configure_bus(self);
    int r = sdcardio_sdcard_writeblocks(MP_OBJ_FROM_PTR(self), buf->buf, start_block, buf->len / 512);
    extraclock_and_unlock_bus(self);
    return r;
}
  • In this function, the SPI locking and setting is performed and then the "sdcardio_sdcard_writeblocks" function is called in which the locking is called again:
mp_uint_t sdcardio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, uint32_t start_block, uint32_t nblocks) {
    // deinit check is in lock_and_configure_bus()
    sdcardio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in);
    if (!lock_and_configure_bus(self)) {
        return MP_EAGAIN;
    }
…
  • Since the line is already taken, the lock fails and an MP_EAGAIN error is returned. But besides the fact that the SPI is taken twice, the returned error is greater than zero, which is treated as no error in the "_sdcardio_sdcard_writeblocks" function and is ignored.

  • The problem does not occur when using storage|vfsFAT, because in the file "extmod/vfs_blockdev.c", functions "mp_vfs_blockdev_init":

void mp_vfs_blockdev_init(mp_vfs_blockdev_t *self, mp_obj_t bdev) {
    mp_load_method(bdev, MP_QSTR_readblocks, self->readblocks);
    mp_load_method_maybe(bdev, MP_QSTR_writeblocks, self->writeblocks);
    mp_load_method_maybe(bdev, MP_QSTR_ioctl, self->u.ioctl);

    // CIRCUITPY-CHANGE: Support native SD cards.
    #if CIRCUITPY_SDCARDIO
    if (mp_obj_get_type(bdev) == &sdcardio_SDCard_type) {
        self->flags |= MP_BLOCKDEV_FLAG_NATIVE | MP_BLOCKDEV_FLAG_HAVE_IOCTL;
        self->readblocks[0] = mp_const_none;
        self->readblocks[1] = bdev;
        self->readblocks[2] = (mp_obj_t)sdcardio_sdcard_readblocks; // native version
        self->writeblocks[0] = mp_const_none;
        self->writeblocks[1] = bdev;
        self->writeblocks[2] = (mp_obj_t)sdcardio_sdcard_writeblocks; // native version
        self->u.ioctl[0] = mp_const_none;
        self->u.ioctl[1] = bdev;
        self->u.ioctl[2] = (mp_obj_t)sdcardio_sdcard_ioctl; // native version
    }
    #endif

Binds calls without “common_hal_”.

Additional information

It is necessary to replace errors (MP_EAGAIN) with (-MP_EAGAIN), and remove line lock/unlock in the function "common_hal_sdcardio_sdcard_writeblocks", as it is done in "common_hal_sdcardio_sdcard_readblocks".

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions