Merge remote-tracking branch 'origin/main'

This commit is contained in:
Hosted Weblate 2024-01-25 19:51:43 +01:00
commit 526faae2a1
No known key found for this signature in database
GPG key ID: A3FAAA06E6569B4C
52 changed files with 745 additions and 545 deletions

View file

@ -165,6 +165,13 @@ Returns a JSON representation of the directory.
* `403 Forbidden` - No `CIRCUITPY_WEB_API_PASSWORD` set
* `404 Not Found` - Missing directory
Returns directory information:
* `free`: Count of free blocks on the disk holding this directory.
* `total`: Total blocks that make up the disk holding this directory.
* `block_size`: Size of a block in bytes.
* `writable`: True when CircuitPython and the web workflow can write to the disk. USB may claim a disk instead.
* `files`: Array of objects. One for each file.
Returns information about each file in the directory:
* `name` - File name. No trailing `/` on directory names
@ -179,14 +186,20 @@ curl -v -u :passw0rd -H "Accept: application/json" -L --location-trusted http://
```
```json
[
{
"name": "world.txt",
"directory": false,
"modified_ns": 946934328000000000,
"file_size": 12
}
]
{
"free": 451623,
"total": 973344,
"block_size": 32768,
"writable": true,
"files": [
{
"name": "world.txt",
"directory": false,
"modified_ns": 946934328000000000,
"file_size": 12
}
]
}
```
##### PUT
@ -196,7 +209,7 @@ time resolution) used for the directories modification time. The RTC time will u
Returns:
* `204 No Content` - Directory exists
* `204 No Content` - Directory or file exists
* `201 Created` - Directory created
* `401 Unauthorized` - Incorrect password
* `403 Forbidden` - No `CIRCUITPY_WEB_API_PASSWORD` set
@ -373,10 +386,10 @@ curl -v -L http://circuitpython.local/cp/devices.json
Returns information about the attached disk(s). A list of objects, one per disk.
* `root`: Filesystem path to the root of the disk.
* `free`: Count of free bytes on the disk.
* `free`: Count of free blocks on the disk.
* `total`: Total blocks that make up the disk.
* `block_size`: Size of a block in bytes.
* `writable`: True when CircuitPython and the web workflow can write to the disk. USB may claim a disk instead.
* `total`: Total bytes that make up the disk.
Example:
```sh
@ -405,7 +418,7 @@ This is an authenticated endpoint in both modes.
Returns information about the device.
* `web_api_version`: Between `1` and `3`. This versions the rest of the API and new versions may not be backwards compatible. See below for more info.
* `web_api_version`: Between `1` and `4`. This versions the rest of the API and new versions may not be backwards compatible. See below for more info.
* `version`: CircuitPython build version.
* `build_date`: CircuitPython build date.
* `board_name`: Human readable name of the board.
@ -467,3 +480,5 @@ Only one WebSocket at a time is supported.
* `1` - Initial version.
* `2` - Added `/cp/diskinfo.json`.
* `3` - Changed `/cp/diskinfo.json` to return a list in preparation for multi-disk support.
* `4` - Changed directory json to an object with additional data. File list is under `files` and is
the same as the old format.

View file

@ -48,6 +48,8 @@
#define MP_BLOCKDEV_FLAG_USB_WRITABLE (0x0010)
// Bit set when the above flag is checked before opening a file for write.
#define MP_BLOCKDEV_FLAG_CONCURRENT_WRITE_PROTECTED (0x0020)
// Bit set when something has claimed the right to mutate the blockdev.
#define MP_BLOCKDEV_FLAG_LOCKED (0x0040)
// constants for block protocol ioctl
#define MP_BLOCKDEV_IOCTL_INIT (1)

View file

@ -30,12 +30,41 @@
#include "py/mperrno.h"
#include "extmod/vfs.h"
#if CIRCUITPY_SDCARDIO
#include "shared-bindings/sdcardio/SDCard.h"
#endif
#if CIRCUITPY_SDIOIO
#include "shared-bindings/sdioio/SDCard.h"
#endif
#if MICROPY_VFS
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
#if CIRCUITPY_SDIOIO
if (mp_obj_get_type(bdev) == &sdioio_SDCard_type) {
// TODO: Enable native blockdev for SDIO too.
}
#endif
if (self->u.ioctl[0] != MP_OBJ_NULL) {
// Device supports new block protocol, so indicate it
self->flags |= MP_BLOCKDEV_FLAG_HAVE_IOCTL;
@ -48,8 +77,10 @@ void mp_vfs_blockdev_init(mp_vfs_blockdev_t *self, mp_obj_t bdev) {
int mp_vfs_blockdev_read(mp_vfs_blockdev_t *self, size_t block_num, size_t num_blocks, uint8_t *buf) {
if (self->flags & MP_BLOCKDEV_FLAG_NATIVE) {
mp_uint_t (*f)(uint8_t *, uint32_t, uint32_t) = (void *)(uintptr_t)self->readblocks[2];
return f(buf, block_num, num_blocks);
// CIRCUITPY-CHANGE: Pass the blockdev object into native readblocks so
// it has the corresponding state.
mp_uint_t (*f)(mp_obj_t self, uint8_t *, uint32_t, uint32_t) = (void *)(uintptr_t)self->readblocks[2];
return f(self->readblocks[1], buf, block_num, num_blocks);
} else {
mp_obj_array_t ar = {{&mp_type_bytearray}, BYTEARRAY_TYPECODE, 0, num_blocks *self->block_size, buf};
self->readblocks[2] = MP_OBJ_NEW_SMALL_INT(block_num);
@ -80,8 +111,10 @@ int mp_vfs_blockdev_write(mp_vfs_blockdev_t *self, size_t block_num, size_t num_
}
if (self->flags & MP_BLOCKDEV_FLAG_NATIVE) {
mp_uint_t (*f)(const uint8_t *, uint32_t, uint32_t) = (void *)(uintptr_t)self->writeblocks[2];
return f(buf, block_num, num_blocks);
// CIRCUITPY-CHANGE: Pass the blockdev object into native readblocks so
// it has the corresponding state.
mp_uint_t (*f)(mp_obj_t self, const uint8_t *, uint32_t, uint32_t) = (void *)(uintptr_t)self->writeblocks[2];
return f(self->writeblocks[1], buf, block_num, num_blocks);
} else {
mp_obj_array_t ar = {{&mp_type_bytearray}, BYTEARRAY_TYPECODE, 0, num_blocks *self->block_size, (void *)buf};
self->writeblocks[2] = MP_OBJ_NEW_SMALL_INT(block_num);
@ -112,6 +145,16 @@ int mp_vfs_blockdev_write_ext(mp_vfs_blockdev_t *self, size_t block_num, size_t
mp_obj_t mp_vfs_blockdev_ioctl(mp_vfs_blockdev_t *self, uintptr_t cmd, uintptr_t arg) {
if (self->flags & MP_BLOCKDEV_FLAG_HAVE_IOCTL) {
// CIRCUITPY-CHANGE: Support native IOCTL so it can run outside of the VM.
if (self->flags & MP_BLOCKDEV_FLAG_NATIVE) {
size_t out_value;
bool (*f)(mp_obj_t self, uint32_t, uint32_t, size_t *) = (void *)(uintptr_t)self->u.ioctl[2];
bool b = f(self->u.ioctl[1], cmd, arg, &out_value);
if (!b) {
return mp_const_none;
}
return MP_OBJ_NEW_SMALL_INT(out_value);
}
// New protocol with ioctl
self->u.ioctl[2] = MP_OBJ_NEW_SMALL_INT(cmd);
self->u.ioctl[3] = MP_OBJ_NEW_SMALL_INT(arg);

View file

@ -34,6 +34,10 @@ typedef struct _fs_user_mount_t {
mp_obj_base_t base;
mp_vfs_blockdev_t blockdev;
FATFS fatfs;
// CIRCUITPY-CHANGE: Count the users that are manipulating the blockdev via
// native fatfs so we can lock and unlock the blockdev.
int8_t lock_count;
} fs_user_mount_t;
extern const byte fresult_to_errno_table[20];

View file

@ -208,7 +208,7 @@ msgstr ""
#: ports/raspberrypi/bindings/cyw43/__init__.c py/argcheck.c py/objexcept.c
#: shared-bindings/canio/CAN.c shared-bindings/digitalio/Pull.c
#: shared-module/synthio/Synthesizer.c
#: shared-bindings/supervisor/__init__.c shared-module/synthio/Synthesizer.c
msgid "%q must be of type %q or %q, not %q"
msgstr ""
@ -216,7 +216,7 @@ msgstr ""
msgid "%q must be of type %q, %q, or %q, not %q"
msgstr ""
#: py/argcheck.c shared-bindings/bitmapfilter/__init__.c
#: py/argcheck.c py/runtime.c shared-bindings/bitmapfilter/__init__.c
#: shared-module/synthio/__init__.c
msgid "%q must be of type %q, not %q"
msgstr ""
@ -234,7 +234,7 @@ msgstr ""
#: ports/nrf/common-hal/pulseio/PulseIn.c
#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c
#: ports/stm/common-hal/pulseio/PulseIn.c py/argcheck.c
#: shared-bindings/canio/Match.c
#: shared-bindings/canio/Match.c shared-bindings/time/__init__.c
msgid "%q out of range"
msgstr ""
@ -498,7 +498,6 @@ msgstr ""
#: ports/nrf/common-hal/countio/Counter.c
#: ports/nrf/common-hal/pulseio/PulseIn.c
#: ports/nrf/common-hal/rotaryio/IncrementalEncoder.c
#: shared-bindings/pwmio/PWMOut.c
msgid "All channels in use"
msgstr ""
@ -521,13 +520,11 @@ msgid "All sync event channels in use"
msgstr ""
#: ports/raspberrypi/common-hal/picodvi/Framebuffer.c
#: shared-bindings/pwmio/PWMOut.c
msgid "All timers for this pin are in use"
msgstr ""
#: ports/atmel-samd/common-hal/_pew/PewPew.c
#: ports/atmel-samd/common-hal/audioio/AudioOut.c
#: ports/atmel-samd/common-hal/frequencyio/FrequencyIn.c
#: ports/atmel-samd/common-hal/pulseio/PulseIn.c
#: ports/atmel-samd/common-hal/pulseio/PulseOut.c
#: ports/cxd56/common-hal/pulseio/PulseOut.c
@ -538,7 +535,7 @@ msgstr ""
#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c
#: ports/nrf/common-hal/pulseio/PulseIn.c ports/nrf/peripherals/nrf/timers.c
#: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c
#: ports/stm/peripherals/timers.c shared-bindings/pwmio/PWMOut.c
#: ports/stm/peripherals/timers.c
msgid "All timers in use"
msgstr ""
@ -777,12 +774,6 @@ msgstr ""
msgid "Cannot remount '/' when visible via USB."
msgstr ""
#: ports/atmel-samd/common-hal/microcontroller/__init__.c
#: ports/cxd56/common-hal/microcontroller/__init__.c
#: ports/mimxrt10xx/common-hal/microcontroller/__init__.c
msgid "Cannot reset into bootloader because no bootloader is present"
msgstr ""
#: ports/espressif/common-hal/socketpool/Socket.c
msgid "Cannot set socket options"
msgstr ""
@ -800,10 +791,6 @@ msgstr ""
msgid "Cannot subclass slice"
msgstr ""
#: shared-bindings/pwmio/PWMOut.c
msgid "Cannot vary frequency on a timer that is already in use"
msgstr ""
#: ports/nrf/common-hal/alarm/pin/PinAlarm.c
msgid "Cannot wake on pin edge, only level"
msgstr ""
@ -846,10 +833,6 @@ msgstr ""
msgid "Could not set address"
msgstr ""
#: shared-bindings/pwmio/PWMOut.c
msgid "Could not start PWM"
msgstr ""
#: ports/stm/common-hal/busio/UART.c
msgid "Could not start interrupt, RX busy"
msgstr ""
@ -941,16 +924,6 @@ msgstr ""
msgid "ESP-IDF memory allocation failed"
msgstr ""
#: ports/atmel-samd/common-hal/alarm/pin/PinAlarm.c
#: ports/atmel-samd/common-hal/countio/Counter.c
#: ports/atmel-samd/common-hal/frequencyio/FrequencyIn.c
#: ports/atmel-samd/common-hal/ps2io/Ps2.c
#: ports/atmel-samd/common-hal/pulseio/PulseIn.c
#: ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c
#: ports/cxd56/common-hal/pulseio/PulseIn.c
msgid "EXTINT channel already in use"
msgstr ""
#: extmod/modre.c
msgid "Error in regex"
msgstr ""
@ -1083,10 +1056,6 @@ msgid ""
"Frequency must be 24, 150, 396, 450, 528, 600, 720, 816, 912, 960 or 1008 Mhz"
msgstr ""
#: shared-bindings/pwmio/PWMOut.c
msgid "Frequency must match existing PWMOut using this timer"
msgstr ""
#: shared-bindings/bitbangio/I2C.c shared-bindings/bitbangio/SPI.c
#: shared-bindings/busio/I2C.c shared-bindings/busio/SPI.c
msgid "Function requires lock"
@ -1121,11 +1090,6 @@ msgstr ""
msgid "Heap allocation when VM not running."
msgstr ""
#: supervisor/shared/safe_mode.c
msgid ""
"Heap was corrupted because the stack was too small. Increase stack size."
msgstr ""
#: extmod/vfs_posix_file.c py/objstringio.c
msgid "I/O operation on closed file"
msgstr ""
@ -1201,7 +1165,7 @@ msgid "Internal define error"
msgstr ""
#: ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c
#: shared-module/os/getenv.c
#: shared-bindings/pwmio/PWMOut.c shared-module/os/getenv.c
msgid "Internal error"
msgstr ""
@ -1210,6 +1174,16 @@ msgstr ""
msgid "Internal error #%d"
msgstr ""
#: ports/atmel-samd/common-hal/alarm/pin/PinAlarm.c
#: ports/atmel-samd/common-hal/countio/Counter.c
#: ports/atmel-samd/common-hal/frequencyio/FrequencyIn.c
#: ports/atmel-samd/common-hal/ps2io/Ps2.c
#: ports/atmel-samd/common-hal/pulseio/PulseIn.c
#: ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c
#: ports/cxd56/common-hal/pulseio/PulseIn.c shared-bindings/pwmio/PWMOut.c
msgid "Internal resource(s) in use"
msgstr ""
#: supervisor/shared/safe_mode.c
msgid "Internal watchdog timer expired."
msgstr ""
@ -1227,7 +1201,7 @@ msgstr ""
#: ports/raspberrypi/bindings/picodvi/Framebuffer.c
#: ports/raspberrypi/common-hal/picodvi/Framebuffer.c py/argcheck.c
#: shared-bindings/digitalio/DigitalInOut.c
#: shared-bindings/epaperdisplay/EPaperDisplay.c
#: shared-bindings/epaperdisplay/EPaperDisplay.c shared-bindings/pwmio/PWMOut.c
msgid "Invalid %q"
msgstr ""
@ -1375,6 +1349,10 @@ msgstr ""
msgid "Missing jmp_pin. %q[%u] jumps on pin"
msgstr ""
#: shared-module/storage/__init__.c
msgid "Mount point directory missing"
msgstr ""
#: shared-bindings/busio/UART.c shared-bindings/displayio/Group.c
msgid "Must be a %q subclass."
msgstr ""
@ -1459,8 +1437,10 @@ msgstr ""
msgid "No IP"
msgstr ""
#: ports/atmel-samd/common-hal/frequencyio/FrequencyIn.c
msgid "No available clocks"
#: ports/atmel-samd/common-hal/microcontroller/__init__.c
#: ports/cxd56/common-hal/microcontroller/__init__.c
#: ports/mimxrt10xx/common-hal/microcontroller/__init__.c
msgid "No bootloader present"
msgstr ""
#: ports/espressif/common-hal/imagecapture/ParallelImageCapture.c
@ -1479,15 +1459,12 @@ msgstr ""
msgid "No default %q bus"
msgstr ""
#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c
#: ports/atmel-samd/common-hal/audiobusio/PDMIn.c
#: ports/atmel-samd/common-hal/touchio/TouchIn.c
msgid "No free GCLKs"
msgstr ""
#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c
msgid "No free GLCKs"
msgstr ""
#: shared-bindings/os/__init__.c
msgid "No hardware random available"
msgstr ""
@ -1686,11 +1663,6 @@ msgstr ""
msgid "Out-buffer elements must be <= 4 bytes long"
msgstr ""
#: shared-bindings/pwmio/PWMOut.c
msgid ""
"PWM frequency not writable when variable_frequency is False on construction."
msgstr ""
#: ports/stm/common-hal/pwmio/PWMOut.c
msgid "PWM restart"
msgstr ""
@ -1736,11 +1708,6 @@ msgstr ""
msgid "Pin must be on PWM Channel B"
msgstr ""
#: ports/atmel-samd/common-hal/countio/Counter.c
#: ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c
msgid "Pin must support hardware interrupts"
msgstr ""
#: shared-bindings/rgbmatrix/RGBMatrix.c
#, c-format
msgid ""
@ -1971,12 +1938,8 @@ msgstr ""
msgid "Specify exactly one of data0 or data_pins"
msgstr ""
#: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c
msgid "Stereo left must be on PWM channel A"
msgstr ""
#: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c
msgid "Stereo right must be on PWM channel B"
#: supervisor/shared/safe_mode.c
msgid "Stack overflow. Increase stack size."
msgstr ""
#: shared-bindings/alarm/time/TimeAlarm.c
@ -2085,10 +2048,6 @@ msgstr ""
msgid "Traceback (most recent call last):\n"
msgstr ""
#: shared-bindings/time/__init__.c
msgid "Tuple or struct_time argument required"
msgstr ""
#: ports/stm/common-hal/busio/UART.c
msgid "UART de-init"
msgstr ""
@ -2442,10 +2401,6 @@ msgstr ""
msgid "argsort is not implemented for flattened arrays"
msgstr ""
#: py/runtime.c shared-bindings/supervisor/__init__.c
msgid "argument has wrong type"
msgstr ""
#: py/compile.c
msgid "argument name reused"
msgstr ""
@ -2455,10 +2410,6 @@ msgstr ""
msgid "argument num/types mismatch"
msgstr ""
#: py/runtime.c
msgid "argument should be a '%q' not a '%q'"
msgstr ""
#: extmod/ulab/code/numpy/numerical.c extmod/ulab/code/numpy/transform.c
msgid "arguments must be ndarrays"
msgstr ""
@ -2643,7 +2594,7 @@ msgstr ""
msgid "can't convert %s to float"
msgstr ""
#: py/runtime.c
#: py/objint.c py/runtime.c
#, c-format
msgid "can't convert %s to int"
msgstr ""
@ -2652,18 +2603,10 @@ msgstr ""
msgid "can't convert '%q' object to %q implicitly"
msgstr ""
#: py/objint.c
msgid "can't convert NaN to int"
msgstr ""
#: extmod/ulab/code/numpy/vector.c
msgid "can't convert complex to float"
msgstr ""
#: py/objint.c
msgid "can't convert inf to int"
msgstr ""
#: py/obj.c
msgid "can't convert to complex"
msgstr ""
@ -3151,10 +3094,6 @@ msgstr ""
msgid "function takes %d positional arguments but %d were given"
msgstr ""
#: shared-bindings/time/__init__.c
msgid "function takes exactly 9 arguments"
msgstr ""
#: py/objgenerator.c
msgid "generator already executing"
msgstr ""
@ -4005,10 +3944,6 @@ msgstr ""
msgid "size is defined for ndarrays only"
msgstr ""
#: shared-bindings/time/__init__.c
msgid "sleep length must be non-negative"
msgstr ""
#: py/nativeglue.c
msgid "slice unsupported"
msgstr ""

View file

@ -235,7 +235,7 @@ static void pinalarm_set_alarms_light(size_t n_alarms, const mp_obj_t *alarms) {
case PINALARM_ERR_NOEXTINT:
raise_ValueError_invalid_pin();
case PINALARM_ERR_NOCHANNEL:
mp_raise_RuntimeError(MP_ERROR_TEXT("EXTINT channel already in use"));
mp_raise_RuntimeError(MP_ERROR_TEXT("Internal resource(s) in use"));
default:
mp_raise_RuntimeError(MP_ERROR_TEXT("Unknown reason."));
}

View file

@ -243,7 +243,7 @@ void common_hal_audiobusio_i2sout_play(audiobusio_i2sout_obj_t *self,
// Find a free GCLK to generate the MCLK signal.
uint8_t gclk = find_free_gclk(divisor);
if (gclk > GCLK_GEN_NUM) {
mp_raise_RuntimeError(MP_ERROR_TEXT("No free GLCKs"));
mp_raise_RuntimeError(MP_ERROR_TEXT("No free GCLKs"));
}
self->gclk = gclk;

View file

@ -1,6 +1,7 @@
#include "common-hal/countio/Counter.h"
#include "shared-bindings/countio/Counter.h"
#include "shared-bindings/microcontroller/Pin.h"
#include "atmel_start_pins.h"
@ -11,13 +12,13 @@
void common_hal_countio_counter_construct(countio_counter_obj_t *self,
const mcu_pin_obj_t *pin, countio_edge_t edge, digitalio_pull_t pull) {
if (!pin->has_extint) {
mp_raise_RuntimeError(MP_ERROR_TEXT("Pin must support hardware interrupts"));
raise_ValueError_invalid_pin();
}
if (eic_get_enable()) {
if (!eic_channel_free(pin->extint_channel)) {
mp_raise_RuntimeError(MP_ERROR_TEXT("EXTINT channel already in use"));
mp_raise_RuntimeError(MP_ERROR_TEXT("Internal resource(s) in use"));
}
} else {
turn_on_external_interrupt_controller();

View file

@ -295,12 +295,12 @@ void common_hal_frequencyio_frequencyin_construct(frequencyio_frequencyin_obj_t*
#ifdef SAM_D5X_E5X
((EIC->INTENSET.bit.EXTINT & mask) != 0 || (EIC->EVCTRL.bit.EXTINTEO & mask) != 0)) {
#endif
mp_raise_RuntimeError(MP_ERROR_TEXT("EXTINT channel already in use"));
mp_raise_RuntimeError(MP_ERROR_TEXT("Internal resource(s) in use"));
}
uint8_t timer_index = find_free_timer();
if (timer_index == 0xff) {
mp_raise_RuntimeError(MP_ERROR_TEXT("All timers in use"));
mp_raise_RuntimeError(MP_ERROR_TEXT("Internal resource(s) in use"));
}
Tc *tc = tc_insts[timer_index];
@ -329,7 +329,7 @@ void common_hal_frequencyio_frequencyin_construct(frequencyio_frequencyin_obj_t*
frequencyin_samd51_start_dpll();
if (dpll_gclk == 0xff && !clock_get_enabled(0, GCLK_SOURCE_DPLL1)) {
common_hal_frequencyio_frequencyin_deinit(self);
mp_raise_RuntimeError(MP_ERROR_TEXT("No available clocks"));
mp_raise_RuntimeError(MP_ERROR_TEXT("Internal resource(s) in use"));
}
set_timer_handler(timer_index, dpll_gclk, TC_HANDLER_NO_INTERRUPT);
turn_on_clocks(true, timer_index, dpll_gclk);
@ -399,7 +399,7 @@ void common_hal_frequencyio_frequencyin_construct(frequencyio_frequencyin_obj_t*
reference_tc = find_free_timer();
if (reference_tc == 0xff) {
common_hal_frequencyio_frequencyin_deinit(self);
mp_raise_RuntimeError(MP_ERROR_TEXT("All timers in use"));
mp_raise_RuntimeError(MP_ERROR_TEXT("Internal resource(s) in use"));
}
frequencyin_reference_tc_init();
}

View file

@ -64,7 +64,7 @@ void common_hal_mcu_enable_interrupts(void) {
void common_hal_mcu_on_next_reset(mcu_runmode_t runmode) {
if ((runmode == RUNMODE_BOOTLOADER) || (runmode == RUNMODE_UF2)) {
if (!bootloader_available()) {
mp_raise_ValueError(MP_ERROR_TEXT("Cannot reset into bootloader because no bootloader is present"));
mp_raise_ValueError(MP_ERROR_TEXT("No bootloader present"));
}
// Pretend to be the first of the two reset presses needed to enter the
// bootloader. That way one reset will end in the bootloader.

View file

@ -248,7 +248,7 @@ void common_hal_ps2io_ps2_construct(ps2io_ps2_obj_t *self,
mp_arg_error_invalid(MP_QSTR_clock_pin);
}
if (eic_get_enable() && !eic_channel_free(clock_pin->extint_channel)) {
mp_raise_RuntimeError(MP_ERROR_TEXT("EXTINT channel already in use"));
mp_raise_RuntimeError(MP_ERROR_TEXT("Internal resource(s) in use"));
}
clk_hi(self);

View file

@ -155,7 +155,7 @@ void common_hal_pulseio_pulsein_construct(pulseio_pulsein_obj_t *self,
raise_ValueError_invalid_pin();
}
if (eic_get_enable() && !eic_channel_free(pin->extint_channel)) {
mp_raise_RuntimeError(MP_ERROR_TEXT("EXTINT channel already in use"));
mp_raise_RuntimeError(MP_ERROR_TEXT("Internal resource(s) in use"));
}
self->buffer = (uint16_t *)m_malloc(maxlen * sizeof(uint16_t));

View file

@ -150,7 +150,6 @@ pwmout_result_t common_hal_pwmio_pwmout_construct(pwmio_pwmout_obj_t *self,
// one output so we start with the TCs to see if they work.
int8_t direction = -1;
uint8_t start = NUM_TIMERS_PER_PIN - 1;
bool found = false;
if (variable_frequency) {
direction = 1;
start = 0;
@ -162,7 +161,6 @@ pwmout_result_t common_hal_pwmio_pwmout_construct(pwmio_pwmout_obj_t *self,
continue;
}
if (t->is_tc) {
found = true;
Tc *tc = tc_insts[t->index];
if (tc->COUNT16.CTRLA.bit.ENABLE == 0 && t->wave_output == 1) {
timer = t;
@ -178,10 +176,7 @@ pwmout_result_t common_hal_pwmio_pwmout_construct(pwmio_pwmout_obj_t *self,
}
if (timer == NULL) {
if (found) {
return PWMOUT_ALL_TIMERS_ON_PIN_IN_USE;
}
return PWMOUT_ALL_TIMERS_IN_USE;
return PWMOUT_INTERNAL_RESOURCES_IN_USE;
}
uint8_t resolution = 0;

View file

@ -25,6 +25,7 @@
*/
#include "common-hal/rotaryio/IncrementalEncoder.h"
#include "shared-bindings/microcontroller/Pin.h"
#include "shared-bindings/rotaryio/IncrementalEncoder.h"
#include "shared-module/rotaryio/IncrementalEncoder.h"
@ -36,8 +37,11 @@
void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencoder_obj_t *self,
const mcu_pin_obj_t *pin_a, const mcu_pin_obj_t *pin_b) {
if (!pin_a->has_extint || !pin_b->has_extint) {
mp_raise_RuntimeError(MP_ERROR_TEXT("Pin must support hardware interrupts"));
if (!pin_a->has_extint) {
raise_ValueError_invalid_pin_name(MP_QSTR_pin_a);
}
if (!pin_b->has_extint) {
raise_ValueError_invalid_pin_name(MP_QSTR_pin_b);
}
// TODO: The SAMD51 has a peripheral dedicated to quadrature encoder debugging. Use it instead
@ -45,7 +49,7 @@ void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencode
if (eic_get_enable()) {
if (!eic_channel_free(pin_a->extint_channel) || !eic_channel_free(pin_b->extint_channel)) {
mp_raise_RuntimeError(MP_ERROR_TEXT("EXTINT channel already in use"));
mp_raise_RuntimeError(MP_ERROR_TEXT("Internal resource(s) in use"));
}
} else {
turn_on_external_interrupt_controller();

View file

@ -72,7 +72,7 @@ void common_hal_mcu_enable_interrupts(void) {
void common_hal_mcu_on_next_reset(mcu_runmode_t runmode) {
if (runmode == RUNMODE_BOOTLOADER) {
mp_raise_ValueError(MP_ERROR_TEXT("Cannot reset into bootloader because no bootloader is present"));
mp_raise_ValueError(MP_ERROR_TEXT("No bootloader present"));
} else if (runmode == RUNMODE_SAFE_MODE) {
safe_mode_on_next_reset(SAFE_MODE_PROGRAMMATIC);
}

View file

@ -100,7 +100,7 @@ void common_hal_pulseio_pulsein_construct(pulseio_pulsein_obj_t *self,
int irq = pulsein_set_config(self, true);
if (irq < 0) {
mp_raise_RuntimeError(MP_ERROR_TEXT("EXTINT channel already in use"));
mp_raise_RuntimeError(MP_ERROR_TEXT("Internal resource(s) in use"));
} else {
pulsein_objects[irq - CXD56_IRQ_EXDEVICE_0] = self;
}

View file

@ -36,7 +36,8 @@
void board_init(void) {
mp_import_stat_t stat_b = mp_import_stat("boot.py");
if (stat_b != MP_IMPORT_STAT_FILE) {
FATFS *fatfs = filesystem_circuitpy();
fs_user_mount_t *fs_mount = filesystem_circuitpy();
FATFS *fatfs = &fs_mount->fatfs;
FIL fs;
UINT char_written = 0;
const byte buffer[] = "#Serial port upload mode\nimport storage\nstorage.remount(\"/\", False)\nstorage.disable_usb_drive()\n";
@ -44,7 +45,7 @@ void board_init(void) {
f_open(fatfs, &fs, "/boot.py", FA_WRITE | FA_CREATE_ALWAYS);
f_write(&fs, buffer, sizeof(buffer) - 1, &char_written);
f_close(&fs);
// Delete code.Py, use main.py
// Delete code.py, use main.py
mp_import_stat_t stat_c = mp_import_stat("code.py");
if (stat_c == MP_IMPORT_STAT_FILE) {
f_unlink(fatfs, "/code.py");

View file

@ -103,7 +103,7 @@ pwmout_result_t common_hal_pwmio_pwmout_construct(pwmio_pwmout_obj_t *self,
}
if (timer_index == INDEX_EMPTY) {
// Running out of timers isn't pin related on ESP32S2.
return PWMOUT_ALL_TIMERS_IN_USE;
return PWMOUT_INTERNAL_RESOURCES_IN_USE;
}
// Find a viable channel
@ -114,7 +114,7 @@ pwmout_result_t common_hal_pwmio_pwmout_construct(pwmio_pwmout_obj_t *self,
}
}
if (channel_index == INDEX_EMPTY) {
return PWMOUT_ALL_CHANNELS_IN_USE;
return PWMOUT_INTERNAL_RESOURCES_IN_USE;
}
// Run configuration

View file

@ -60,7 +60,8 @@ STATIC uint32_t _cache_lba = 0xffffffff;
#define SECSIZE(fs) ((fs)->ssize)
#endif // FF_MAX_SS == FF_MIN_SS
STATIC DWORD fatfs_bytes(void) {
FATFS *fatfs = filesystem_circuitpy();
fs_user_mount_t *fs_mount = filesystem_circuitpy();
FATFS *fatfs = &fs_mount->fatfs;
return (fatfs->csize * SECSIZE(fatfs)) * (fatfs->n_fatent - 2);
}
STATIC bool storage_extended = true;

View file

@ -67,7 +67,7 @@ void PLACE_IN_ITCM(common_hal_mcu_enable_interrupts)(void) {
void common_hal_mcu_on_next_reset(mcu_runmode_t runmode) {
if (runmode == RUNMODE_BOOTLOADER) {
if (!bootloader_available()) {
mp_raise_ValueError(MP_ERROR_TEXT("Cannot reset into bootloader because no bootloader is present"));
mp_raise_ValueError(MP_ERROR_TEXT("No bootloader present"));
}
// Pretend to be the first of the two reset presses needed to enter the
// bootloader. That way one reset will end in the bootloader.

View file

@ -189,7 +189,7 @@ pwmout_result_t common_hal_pwmio_pwmout_construct(pwmio_pwmout_obj_t *self,
if (((flexpwm->MCTRL >> PWM_MCTRL_RUN_SHIFT) & sm_mask) != 0) {
// Another output has claimed this submodule for variable frequency already.
if ((_pwm_variable_frequency[flexpwm_index] & sm_mask) != 0) {
return PWMOUT_ALL_TIMERS_ON_PIN_IN_USE;
return PWMOUT_INTERNAL_RESOURCES_IN_USE;
}
// We want variable frequency but another class has already claim a fixed frequency.
@ -199,7 +199,7 @@ pwmout_result_t common_hal_pwmio_pwmout_construct(pwmio_pwmout_obj_t *self,
// Another pin is already using this output.
if ((flexpwm->OUTEN & outen_mask) != 0) {
return PWMOUT_ALL_TIMERS_ON_PIN_IN_USE;
return PWMOUT_INTERNAL_RESOURCES_IN_USE;
}
if (frequency != _pwm_sm_frequencies[flexpwm_index][submodule]) {

View file

@ -220,7 +220,7 @@ pwmout_result_t common_hal_pwmio_pwmout_construct(pwmio_pwmout_obj_t *self,
&channel, &pwm_already_in_use, NULL);
if (self->pwm == NULL) {
return PWMOUT_ALL_TIMERS_IN_USE;
return PWMOUT_INTERNAL_RESOURCES_IN_USE;
}
self->channel = channel;

View file

@ -119,21 +119,21 @@ pwmout_result_t pwmout_allocate(uint8_t slice, uint8_t ab_channel, bool variable
// Check the channel first.
if ((channel_use & channel_use_mask) != 0) {
return PWMOUT_ALL_TIMERS_ON_PIN_IN_USE;
return PWMOUT_INTERNAL_RESOURCES_IN_USE;
}
// Now check if the slice is in use and if we can share with it.
if (target_slice_frequencies[slice] > 0) {
// If we want to change frequency then we can't share.
if (variable_frequency) {
return PWMOUT_ALL_TIMERS_ON_PIN_IN_USE;
return PWMOUT_VARIABLE_FREQUENCY_NOT_AVAILABLE;
}
// If the other user wants a variable frequency then we can't share either.
if ((slice_variable_frequency & (1 << slice)) != 0) {
return PWMOUT_ALL_TIMERS_ON_PIN_IN_USE;
return PWMOUT_INTERNAL_RESOURCES_IN_USE;
}
// If we're both fixed frequency but we don't match target frequencies then we can't share.
if (target_slice_frequencies[slice] != frequency) {
return PWMOUT_ALL_TIMERS_ON_PIN_IN_USE;
return PWMOUT_INVALID_FREQUENCY_ON_PIN;
}
}

View file

@ -83,7 +83,7 @@ pwmout_result_t common_hal_pwmio_pwmout_construct(pwmio_pwmout_obj_t *self,
}
if (self->tim == NULL) {
return PWMOUT_ALL_TIMERS_ON_PIN_IN_USE;
return PWMOUT_INTERNAL_RESOURCES_IN_USE;
}
self->duty_cycle = duty;

View file

@ -26,3 +26,9 @@ MCU_PACKAGE = UFQFPN48
LD_COMMON = boards/common_nvm.ld
LD_FILE = boards/STM32F411_nvm_nofs.ld
# Disable TERMINALIO on translations with missing characters.
ifneq (,$(filter $(TRANSLATION),ja ko ru))
CIRCUITPY_TERMINALIO = 0
RELEASE_NEEDS_CLEAN_BUILD = $(CIRCUITPY_DISPLAYIO)
endif

View file

@ -110,12 +110,12 @@ pwmout_result_t common_hal_pwmio_pwmout_construct(pwmio_pwmout_obj_t *self,
if (tim_index < TIM_BANK_ARRAY_LEN && tim_channels_taken[tim_index] != 0) {
// Timer has already been reserved by an internal module
if (stm_peripherals_timer_is_reserved(mcu_tim_banks[tim_index])) {
last_failure = PWMOUT_ALL_TIMERS_ON_PIN_IN_USE;
last_failure = PWMOUT_INTERNAL_RESOURCES_IN_USE;
continue; // keep looking
}
// is it the same channel? (or all channels reserved by a var-freq)
if (tim_channels_taken[tim_index] & (1 << tim_channel_index)) {
last_failure = PWMOUT_ALL_TIMERS_ON_PIN_IN_USE;
last_failure = PWMOUT_INTERNAL_RESOURCES_IN_USE;
continue; // keep looking, might be another viable option
}
// If the frequencies are the same it's ok

View file

@ -140,9 +140,9 @@ mp_obj_t mp_obj_new_int_from_float(mp_float_t val) {
if (u.p.exp == ((1 << MP_FLOAT_EXP_BITS) - 1)) {
// ...then number is Inf (positive or negative) if fraction is 0, else NaN.
if (u.p.frc == 0) {
mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("can't convert inf to int"));
mp_raise_msg_varg(&mp_type_OverflowError, MP_ERROR_TEXT("can't convert %s to int"), "inf");
} else {
mp_raise_ValueError(MP_ERROR_TEXT("can't convert NaN to int"));
mp_raise_ValueError_varg(MP_ERROR_TEXT("can't convert %s to int"), "NaN");
}
} else {
mp_fp_as_int_class_t icl = mp_classify_fp_as_int(val);

View file

@ -1095,12 +1095,7 @@ STATIC mp_obj_t checked_fun_call(mp_obj_t self_in, size_t n_args, size_t n_kw, c
if (n_args > 0) {
const mp_obj_type_t *arg0_type = mp_obj_get_type(args[0]);
if (arg0_type != self->type) {
#if MICROPY_ERROR_REPORTING != MICROPY_ERROR_REPORTING_DETAILED
mp_raise_TypeError(MP_ERROR_TEXT("argument has wrong type"));
#else
mp_raise_msg_varg(&mp_type_TypeError,
MP_ERROR_TEXT("argument should be a '%q' not a '%q'"), self->type->name, arg0_type->name);
#endif
mp_raise_TypeError_varg(MP_ERROR_TEXT("%q must be of type %q, not %q"), MP_QSTR_self, self->type->name, arg0_type->name);
}
}
return mp_call_function_n_kw(self->fun, n_args, n_kw, args);

View file

@ -46,23 +46,17 @@ void common_hal_pwmio_pwmout_raise_error(pwmout_result_t result) {
mp_arg_error_invalid(MP_QSTR_frequency);
break;
case PWMOUT_INVALID_FREQUENCY_ON_PIN:
mp_raise_ValueError(MP_ERROR_TEXT("Frequency must match existing PWMOut using this timer"));
mp_arg_error_invalid(MP_QSTR_frequency);
break;
case PWMOUT_VARIABLE_FREQUENCY_NOT_AVAILABLE:
mp_raise_ValueError(MP_ERROR_TEXT("Cannot vary frequency on a timer that is already in use"));
mp_arg_error_invalid(MP_QSTR_variable_frequency);
break;
case PWMOUT_ALL_TIMERS_ON_PIN_IN_USE:
mp_raise_ValueError(MP_ERROR_TEXT("All timers for this pin are in use"));
break;
case PWMOUT_ALL_TIMERS_IN_USE:
mp_raise_RuntimeError(MP_ERROR_TEXT("All timers in use"));
break;
case PWMOUT_ALL_CHANNELS_IN_USE:
mp_raise_RuntimeError(MP_ERROR_TEXT("All channels in use"));
case PWMOUT_INTERNAL_RESOURCES_IN_USE:
mp_raise_RuntimeError(MP_ERROR_TEXT("Internal resource(s) in use"));
break;
default:
case PWMOUT_INITIALIZATION_ERROR:
mp_raise_RuntimeError(MP_ERROR_TEXT("Could not start PWM"));
mp_raise_RuntimeError(MP_ERROR_TEXT("Internal error"));
break;
}
}
@ -268,8 +262,7 @@ STATIC mp_obj_t pwmio_pwmout_obj_set_frequency(mp_obj_t self_in, mp_obj_t freque
pwmio_pwmout_obj_t *self = MP_OBJ_TO_PTR(self_in);
check_for_deinit(self);
if (!common_hal_pwmio_pwmout_get_variable_frequency(self)) {
mp_raise_AttributeError(MP_ERROR_TEXT(
"PWM frequency not writable when variable_frequency is False on construction."));
mp_raise_msg_varg(&mp_type_AttributeError, MP_ERROR_TEXT("Invalid %q"), MP_QSTR_variable_frequency);
}
mp_int_t freq = mp_obj_get_int(frequency);
if (freq == 0) {

View file

@ -38,9 +38,7 @@ typedef enum pwmout_result_t {
PWMOUT_INVALID_FREQUENCY,
PWMOUT_INVALID_FREQUENCY_ON_PIN,
PWMOUT_VARIABLE_FREQUENCY_NOT_AVAILABLE,
PWMOUT_ALL_TIMERS_ON_PIN_IN_USE,
PWMOUT_ALL_TIMERS_IN_USE,
PWMOUT_ALL_CHANNELS_IN_USE,
PWMOUT_INTERNAL_RESOURCES_IN_USE,
PWMOUT_INITIALIZATION_ERROR,
} pwmout_result_t;

View file

@ -131,7 +131,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(sdcardio_sdcard_deinit_obj, sdcardio_sdcard_deinit);
//|
//| :return: None"""
STATIC mp_obj_t sdcardio_sdcard_readblocks(mp_obj_t self_in, mp_obj_t start_block_in, mp_obj_t buf_in) {
STATIC mp_obj_t _sdcardio_sdcard_readblocks(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_WRITE);
@ -143,7 +143,7 @@ STATIC mp_obj_t sdcardio_sdcard_readblocks(mp_obj_t self_in, mp_obj_t start_bloc
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_3(sdcardio_sdcard_readblocks_obj, sdcardio_sdcard_readblocks);
MP_DEFINE_CONST_FUN_OBJ_3(sdcardio_sdcard_readblocks_obj, _sdcardio_sdcard_readblocks);
//| def sync(self) -> None:
//| """Ensure all blocks written are actually committed to the SD card
@ -171,7 +171,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(sdcardio_sdcard_sync_obj, sdcardio_sdcard_sync);
//| :return: None"""
//|
STATIC mp_obj_t sdcardio_sdcard_writeblocks(mp_obj_t self_in, mp_obj_t start_block_in, mp_obj_t buf_in) {
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);
@ -182,7 +182,7 @@ STATIC mp_obj_t sdcardio_sdcard_writeblocks(mp_obj_t self_in, mp_obj_t start_blo
}
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_3(sdcardio_sdcard_writeblocks_obj, sdcardio_sdcard_writeblocks);
MP_DEFINE_CONST_FUN_OBJ_3(sdcardio_sdcard_writeblocks_obj, _sdcardio_sdcard_writeblocks);
STATIC const mp_rom_map_elem_t sdcardio_sdcard_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_count), MP_ROM_PTR(&sdcardio_sdcard_count_obj) },

View file

@ -27,4 +27,19 @@
#pragma once
#include "shared-module/sdcardio/SDCard.h"
extern const mp_obj_type_t sdcardio_SDCard_type;
void common_hal_sdcardio_sdcard_construct(sdcardio_sdcard_obj_t *self, busio_spi_obj_t *spi, const mcu_pin_obj_t *cs, int baudrate);
void common_hal_sdcardio_sdcard_deinit(sdcardio_sdcard_obj_t *self);
void common_hal_sdcardio_sdcard_check_for_deinit(sdcardio_sdcard_obj_t *self);
int common_hal_sdcardio_sdcard_get_blockcount(sdcardio_sdcard_obj_t *self);
int common_hal_sdcardio_sdcard_readblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf);
int common_hal_sdcardio_sdcard_sync(sdcardio_sdcard_obj_t *self);
int common_hal_sdcardio_sdcard_writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf);
// Used by native vfs blockdev.
mp_uint_t sdcardio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, uint32_t start_block, uint32_t buflen);
mp_uint_t sdcardio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, uint32_t start_block, uint32_t buflen);
bool sdcardio_sdcard_ioctl(mp_obj_t self_in, size_t cmd, size_t arg, mp_int_t *out_value);

View file

@ -164,7 +164,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(sdioio_sdcard_count_obj, sdioio_sdcard_count);
//| :param ~circuitpython_typing.WriteableBuffer buf: The buffer to write into. Length must be multiple of 512.
//|
//| :return: None"""
STATIC mp_obj_t sdioio_sdcard_readblocks(mp_obj_t self_in, mp_obj_t start_block_in, mp_obj_t buf_in) {
STATIC mp_obj_t _sdioio_sdcard_readblocks(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_WRITE);
@ -176,7 +176,7 @@ STATIC mp_obj_t sdioio_sdcard_readblocks(mp_obj_t self_in, mp_obj_t start_block_
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_3(sdioio_sdcard_readblocks_obj, sdioio_sdcard_readblocks);
MP_DEFINE_CONST_FUN_OBJ_3(sdioio_sdcard_readblocks_obj, _sdioio_sdcard_readblocks);
//| def writeblocks(self, start_block: int, buf: ReadableBuffer) -> None:
//| """Write one or more blocks to the card
@ -185,7 +185,7 @@ MP_DEFINE_CONST_FUN_OBJ_3(sdioio_sdcard_readblocks_obj, sdioio_sdcard_readblocks
//| :param ~circuitpython_typing.ReadableBuffer buf: The buffer to read from. Length must be multiple of 512.
//|
//| :return: None"""
STATIC mp_obj_t sdioio_sdcard_writeblocks(mp_obj_t self_in, mp_obj_t start_block_in, mp_obj_t buf_in) {
STATIC mp_obj_t _sdioio_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);
@ -197,7 +197,7 @@ STATIC mp_obj_t sdioio_sdcard_writeblocks(mp_obj_t self_in, mp_obj_t start_block
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_3(sdioio_sdcard_writeblocks_obj, sdioio_sdcard_writeblocks);
MP_DEFINE_CONST_FUN_OBJ_3(sdioio_sdcard_writeblocks_obj, _sdioio_sdcard_writeblocks);
//| frequency: int
//| """The actual SDIO bus frequency. This may not match the frequency

View file

@ -130,11 +130,12 @@ STATIC mp_obj_t supervisor_set_next_code_file(size_t n_args, const mp_obj_t *pos
mp_arg_val_t sticky_on_reload;
} args;
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, (mp_arg_val_t *)&args);
if (!mp_obj_is_str_or_bytes(args.filename.u_obj) && args.filename.u_obj != mp_const_none) {
mp_raise_TypeError(MP_ERROR_TEXT("argument has wrong type"));
mp_obj_t filename_obj = args.filename.u_obj;
if (!mp_obj_is_str_or_bytes(filename_obj) && filename_obj != mp_const_none) {
mp_raise_TypeError_varg(MP_ERROR_TEXT("%q must be of type %q or %q, not %q"), MP_QSTR_filename, MP_QSTR_str, MP_QSTR_None, mp_obj_get_type(filename_obj)->name);
}
if (args.filename.u_obj == mp_const_none) {
args.filename.u_obj = mp_const_empty_bytes;
if (filename_obj == mp_const_none) {
filename_obj = mp_const_empty_bytes;
}
uint8_t options = 0;
if (args.reload_on_success.u_bool) {
@ -153,7 +154,7 @@ STATIC mp_obj_t supervisor_set_next_code_file(size_t n_args, const mp_obj_t *pos
options |= SUPERVISOR_NEXT_CODE_OPT_STICKY_ON_RELOAD;
}
size_t len;
const char *filename = mp_obj_str_get_data(args.filename.u_obj, &len);
const char *filename = mp_obj_str_get_data(filename_obj, &len);
if (next_code_configuration != NULL) {
port_free(next_code_configuration);
next_code_configuration = NULL;

View file

@ -83,9 +83,7 @@ STATIC mp_obj_t time_sleep(mp_obj_t seconds_o) {
mp_int_t seconds = mp_obj_get_int(seconds_o);
mp_int_t msecs = 1000 * seconds;
#endif
if (seconds < 0) {
mp_raise_ValueError(MP_ERROR_TEXT("sleep length must be non-negative"));
}
mp_arg_validate_int_min(msecs, 0, MP_QSTR_seconds);
common_hal_time_delay_ms(msecs);
return mp_const_none;
}
@ -161,13 +159,13 @@ void struct_time_to_tm(mp_obj_t t, timeutils_struct_time_t *tm) {
mp_obj_t *elems;
size_t len;
if (!mp_obj_is_type(t, &mp_type_tuple) && !mp_obj_is_type(t, (mp_obj_type_t *)&struct_time_type_obj.base)) {
mp_raise_TypeError(MP_ERROR_TEXT("Tuple or struct_time argument required"));
if (!mp_obj_is_type(t, &mp_type_tuple)) {
mp_arg_validate_type(t, (mp_obj_type_t *)&struct_time_type_obj.base, MP_QSTR_value);
}
mp_obj_tuple_get(t, &len, &elems);
if (len != 9) {
mp_raise_TypeError(MP_ERROR_TEXT("function takes exactly 9 arguments"));
mp_raise_TypeError_varg(MP_ERROR_TEXT("function takes %d positional arguments but %d were given"), 9, len);
}
tm->tm_year = mp_obj_get_int(elems[0]);
@ -277,8 +275,8 @@ STATIC mp_obj_t time_mktime(mp_obj_t t) {
mp_obj_t *elem;
size_t len;
if (!mp_obj_is_type(t, &mp_type_tuple) && !mp_obj_is_type(t, (mp_obj_type_t *)&struct_time_type_obj.base)) {
mp_raise_TypeError(MP_ERROR_TEXT("Tuple or struct_time argument required"));
if (!mp_obj_is_type(t, &mp_type_tuple)) {
mp_arg_validate_type(t, (mp_obj_type_t *)&struct_time_type_obj.base, MP_QSTR_value);
}
mp_obj_tuple_get(t, &len, &elem);
@ -287,7 +285,7 @@ STATIC mp_obj_t time_mktime(mp_obj_t t) {
}
if (mp_obj_get_int(elem[0]) < 2000) {
mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("timestamp out of range for platform time_t"));
mp_raise_msg_varg(&mp_type_OverflowError, MP_ERROR_TEXT("%q out of range"), MP_QSTR_tm_year);
}
mp_uint_t secs = timeutils_mktime(mp_obj_get_int(elem[0]), mp_obj_get_int(elem[1]), mp_obj_get_int(elem[2]),

View file

@ -62,8 +62,12 @@ STATIC bool open_file(const char *name, file_arg *active_file) {
return false;
}
#else
FATFS *fs = filesystem_circuitpy();
FRESULT result = f_open(fs, active_file, name, FA_READ);
fs_user_mount_t *fs_mount = filesystem_circuitpy();
if (fs_mount == NULL) {
return false;
}
FATFS *fatfs = &fs_mount->fatfs;
FRESULT result = f_open(fatfs, active_file, name, FA_READ);
return result == FR_OK;
#endif
}

View file

@ -26,6 +26,8 @@
// This implementation largely follows the structure of adafruit_sdcard.py
#include "extmod/vfs.h"
#include "shared-bindings/busio/SPI.h"
#include "shared-bindings/digitalio/DigitalInOut.h"
#include "shared-bindings/sdcardio/SDCard.h"
@ -358,23 +360,24 @@ STATIC int readinto(sdcardio_sdcard_obj_t *self, void *buf, size_t size) {
return 0;
}
STATIC int readblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf) {
uint32_t nblocks = buf->len / 512;
mp_uint_t sdcardio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, uint32_t start_block, uint32_t nblocks) {
sdcardio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in);
if (!lock_and_configure_bus(self)) {
return MP_EAGAIN;
}
int r = 0;
size_t buflen = 512 * nblocks;
if (nblocks == 1) {
// Use CMD17 to read a single block
return block_cmd(self, 17, start_block, buf->buf, buf->len, true, true);
r = block_cmd(self, 17, start_block, buf, buflen, true, true);
} else {
// Use CMD18 to read multiple blocks
int r = block_cmd(self, 18, start_block, NULL, 0, true, true);
if (r < 0) {
return r;
}
uint8_t *ptr = buf->buf;
while (nblocks--) {
r = block_cmd(self, 18, start_block, NULL, 0, true, true);
uint8_t *ptr = buf;
while (nblocks-- && r >= 0) {
r = readinto(self, ptr, 512);
if (r < 0) {
return r;
if (r != 0) {
break;
}
ptr += 512;
}
@ -387,12 +390,13 @@ STATIC int readblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buff
uint8_t single_byte;
common_hal_busio_spi_read(self->bus, &single_byte, 1, 0xff);
if (single_byte & 0x80) {
return r;
break;
}
r = single_byte;
}
}
return 0;
extraclock_and_unlock_bus(self);
return r;
}
int common_hal_sdcardio_sdcard_readblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf) {
@ -401,10 +405,7 @@ int common_hal_sdcardio_sdcard_readblocks(sdcardio_sdcard_obj_t *self, uint32_t
mp_raise_ValueError(MP_ERROR_TEXT("Buffer length must be a multiple of 512"));
}
lock_and_configure_bus(self);
int r = readblocks(self, start_block, buf);
extraclock_and_unlock_bus(self);
return r;
return sdcardio_sdcard_readblocks(MP_OBJ_FROM_PTR(self), buf->buf, start_block, buf->len / 512);
}
STATIC int _write(sdcardio_sdcard_obj_t *self, uint8_t token, void *buf, size_t size) {
@ -452,17 +453,20 @@ STATIC int _write(sdcardio_sdcard_obj_t *self, uint8_t token, void *buf, size_t
return 0;
}
STATIC int writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf) {
mp_uint_t sdcardio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, uint32_t start_block, uint32_t nblocks) {
sdcardio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in);
common_hal_sdcardio_check_for_deinit(self);
uint32_t nblocks = buf->len / 512;
DEBUG_PRINT("cmd25? %d next_block %d start_block %d\n", self->in_cmd25, self->next_block, start_block);
if (!lock_and_configure_bus(self)) {
return MP_EAGAIN;
}
if (!self->in_cmd25 || start_block != self->next_block) {
DEBUG_PRINT("entering CMD25 at %d\n", (int)start_block);
// Use CMD25 to write multiple block
int r = block_cmd(self, 25, start_block, NULL, 0, true, true);
if (r < 0) {
extraclock_and_unlock_bus(self);
return r;
}
self->in_cmd25 = true;
@ -470,17 +474,19 @@ STATIC int writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buf
self->next_block = start_block;
uint8_t *ptr = buf->buf;
uint8_t *ptr = buf;
while (nblocks--) {
int r = _write(self, TOKEN_CMD25, ptr, 512);
if (r < 0) {
self->in_cmd25 = false;
extraclock_and_unlock_bus(self);
return r;
}
self->next_block++;
ptr += 512;
}
extraclock_and_unlock_bus(self);
return 0;
}
@ -498,7 +504,29 @@ int common_hal_sdcardio_sdcard_writeblocks(sdcardio_sdcard_obj_t *self, uint32_t
mp_raise_ValueError(MP_ERROR_TEXT("Buffer length must be a multiple of 512"));
}
lock_and_configure_bus(self);
int r = writeblocks(self, start_block, buf);
int r = sdcardio_sdcard_writeblocks(MP_OBJ_FROM_PTR(self), buf->buf, start_block, buf->len / 512);
extraclock_and_unlock_bus(self);
return r;
}
bool sdcardio_sdcard_ioctl(mp_obj_t self_in, size_t cmd, size_t arg, mp_int_t *out_value) {
sdcardio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in);
*out_value = 0;
switch (cmd) {
case MP_BLOCKDEV_IOCTL_DEINIT:
common_hal_sdcardio_sdcard_sync(self);
break; // TODO properly
case MP_BLOCKDEV_IOCTL_SYNC:
common_hal_sdcardio_sdcard_sync(self);
break;
case MP_BLOCKDEV_IOCTL_BLOCK_COUNT:
*out_value = common_hal_sdcardio_sdcard_get_blockcount(self);
break;
case MP_BLOCKDEV_IOCTL_BLOCK_SIZE:
*out_value = 512;
break;
default:
return false;
}
return true;
}

View file

@ -44,11 +44,3 @@ typedef struct {
uint32_t next_block;
bool in_cmd25;
} sdcardio_sdcard_obj_t;
void common_hal_sdcardio_sdcard_construct(sdcardio_sdcard_obj_t *self, busio_spi_obj_t *spi, const mcu_pin_obj_t *cs, int baudrate);
void common_hal_sdcardio_sdcard_deinit(sdcardio_sdcard_obj_t *self);
void common_hal_sdcardio_sdcard_check_for_deinit(sdcardio_sdcard_obj_t *self);
int common_hal_sdcardio_sdcard_get_blockcount(sdcardio_sdcard_obj_t *self);
int common_hal_sdcardio_sdcard_readblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf);
int common_hal_sdcardio_sdcard_sync(sdcardio_sdcard_obj_t *self);
int common_hal_sdcardio_sdcard_writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf);

View file

@ -177,15 +177,20 @@ void common_hal_storage_mount(mp_obj_t vfs_obj, const char *mount_path, bool rea
args[0] = readonly ? mp_const_true : mp_const_false;
args[1] = mp_const_false; // Don't make the file system automatically when mounting.
// Check that there's no file or directory with the same name as the mount point.
// Check that there is a directory with the same name as the mount point.
// But it's ok to mount '/' in any case.
if (strcmp(vfs->str, "/") != 0) {
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
common_hal_os_stat(mount_path);
mp_obj_t mount_point_stat = common_hal_os_stat(mount_path);
nlr_pop();
// Something with the same name exists.
mp_raise_OSError(MP_EEXIST);
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mount_point_stat);
if ((MP_OBJ_SMALL_INT_VALUE(t->items[0]) & MP_S_IFDIR) == 0) {
mp_raise_RuntimeError(MP_ERROR_TEXT("Mount point directory missing"));
}
} else {
// Something with the same name doesn't exist.
mp_raise_RuntimeError(MP_ERROR_TEXT("Mount point directory missing"));
}
}

View file

@ -24,8 +24,7 @@
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_SUPERVISOR_FILESYSTEM_H
#define MICROPY_INCLUDED_SUPERVISOR_FILESYSTEM_H
#pragma once
#include <stdbool.h>
@ -45,6 +44,16 @@ void filesystem_set_concurrent_write_protection(fs_user_mount_t *vfs, bool concu
bool filesystem_is_writable_by_python(fs_user_mount_t *vfs);
bool filesystem_is_writable_by_usb(fs_user_mount_t *vfs);
FATFS *filesystem_circuitpy(void);
fs_user_mount_t *filesystem_circuitpy(void);
fs_user_mount_t *filesystem_for_path(const char *path_in, const char **path_under_mount);
bool filesystem_native_fatfs(fs_user_mount_t *fs_mount);
#endif // MICROPY_INCLUDED_SUPERVISOR_FILESYSTEM_H
// We have two levels of locking. filesystem_* calls grab a shared blockdev lock to allow
// CircuitPython's fatfs code edit the blocks. blockdev_* class grab a lock to mutate blocks
// directly, excluding any filesystem_* locks.
bool filesystem_lock(fs_user_mount_t *fs_mount);
void filesystem_unlock(fs_user_mount_t *fs_mount);
bool blockdev_lock(fs_user_mount_t *fs_mount);
void blockdev_unlock(fs_user_mount_t *fs_mount);

View file

@ -126,13 +126,21 @@ void background_callback_reset() {
background_callback_t *cb = (background_callback_t *)callback_head;
while (cb) {
background_callback_t *next = cb->next;
if (gc_ptr_on_heap((void *)cb)) {
*previous_next = cb;
previous_next = &cb->next;
cb->next = NULL;
new_tail = cb;
cb->next = NULL;
// Unlink any callbacks that are allocated on the python heap or if they
// reference data on the python heap. The python heap will be disappear
// soon after this.
if (gc_ptr_on_heap((void *)cb) || gc_ptr_on_heap(cb->data)) {
cb->prev = NULL; // Used to indicate a callback isn't queued.
} else {
memset(cb, 0, sizeof(*cb));
// Set .next of the previous callback.
*previous_next = cb;
// Set our .next for the next callback.
previous_next = &cb->next;
// Set our prev to the last callback.
cb->prev = new_tail;
// Now we're the tail of the list.
new_tail = cb;
}
cb = next;
}

View file

@ -151,6 +151,7 @@ STATIC uint64_t truncate_time(uint64_t input_time, DWORD *fattime) {
// Used by read and write.
STATIC FIL active_file;
STATIC fs_user_mount_t *active_mount;
STATIC uint8_t _process_read(const uint8_t *raw_buf, size_t command_len) {
struct read_command *command = (struct read_command *)raw_buf;
size_t header_size = sizeof(struct read_command);
@ -170,11 +171,19 @@ STATIC uint8_t _process_read(const uint8_t *raw_buf, size_t command_len) {
return THIS_COMMAND;
}
char *path = (char *)((uint8_t *)command) + header_size;
path[command->path_length] = '\0';
char *full_path = (char *)((uint8_t *)command) + header_size;
full_path[command->path_length] = '\0';
FATFS *fs = filesystem_circuitpy();
FRESULT result = f_open(fs, &active_file, path, FA_READ);
const char *mount_path;
active_mount = filesystem_for_path(full_path, &mount_path);
if (active_mount == NULL || !filesystem_native_fatfs(active_mount)) {
response.status = STATUS_ERROR;
common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, response_size, NULL, 0);
return ANY_COMMAND;
}
FATFS *fs = &active_mount->fatfs;
FRESULT result = f_open(fs, &active_file, mount_path, FA_READ);
if (result != FR_OK) {
response.status = STATUS_ERROR;
common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, response_size, NULL, 0);
@ -250,22 +259,6 @@ STATIC uint8_t _process_read_pacing(const uint8_t *raw_buf, size_t command_len)
STATIC size_t total_write_length;
STATIC uint64_t _truncated_time;
// Returns true if usb is active and replies with an error if so. If not, it grabs
// the USB mass storage lock and returns false. Make sure to release the lock with
// usb_msc_unlock() when the transaction is complete.
STATIC bool _usb_active(void *response, size_t response_size) {
// Check to see if USB has already been mounted. If not, then we "eject" from USB until we're done.
#if CIRCUITPY_USB && CIRCUITPY_USB_MSC
if (storage_usb_enabled() && !usb_msc_lock()) {
// Status is always the second byte of the response.
((uint8_t *)response)[1] = STATUS_ERROR_READONLY;
common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)response, response_size, NULL, 0);
return true;
}
#endif
return false;
}
STATIC uint8_t _process_write(const uint8_t *raw_buf, size_t command_len) {
struct write_command *command = (struct write_command *)raw_buf;
size_t header_size = sizeof(struct write_command);
@ -284,23 +277,31 @@ STATIC uint8_t _process_write(const uint8_t *raw_buf, size_t command_len) {
}
total_write_length = command->total_length;
char *path = (char *)command->path;
path[command->path_length] = '\0';
if (_usb_active(&response, sizeof(struct write_pacing))) {
char *full_path = (char *)command->path;
full_path[command->path_length] = '\0';
const char *mount_path;
active_mount = filesystem_for_path(full_path, &mount_path);
if (active_mount == NULL || !filesystem_native_fatfs(active_mount)) {
response.status = STATUS_ERROR;
common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct write_pacing), NULL, 0);
return ANY_COMMAND;
}
if (!filesystem_lock(active_mount)) {
response.status = STATUS_ERROR_READONLY;
common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct write_pacing), NULL, 0);
return ANY_COMMAND;
}
FATFS *fs = filesystem_circuitpy();
FATFS *fs = &active_mount->fatfs;
DWORD fattime;
_truncated_time = truncate_time(command->modification_time, &fattime);
override_fattime(fattime);
FRESULT result = f_open(fs, &active_file, path, FA_WRITE | FA_OPEN_ALWAYS);
FRESULT result = f_open(fs, &active_file, mount_path, FA_WRITE | FA_OPEN_ALWAYS);
if (result != FR_OK) {
response.status = STATUS_ERROR;
common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct write_pacing), NULL, 0);
#if CIRCUITPY_USB_MSC
usb_msc_unlock();
#endif
filesystem_unlock(active_mount);
override_fattime(0);
return ANY_COMMAND;
}
@ -315,9 +316,7 @@ STATIC uint8_t _process_write(const uint8_t *raw_buf, size_t command_len) {
f_truncate(&active_file);
f_close(&active_file);
override_fattime(0);
#if CIRCUITPY_USB_MSC
usb_msc_unlock();
#endif
filesystem_unlock(active_mount);
}
response.offset = offset;
response.free_space = chunk_size;
@ -342,9 +341,7 @@ STATIC uint8_t _process_write_data(const uint8_t *raw_buf, size_t command_len) {
// TODO: throw away any more packets of path.
response.status = STATUS_ERROR;
common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct write_pacing), NULL, 0);
#if CIRCUITPY_USB_MSC
usb_msc_unlock();
#endif
filesystem_unlock(active_mount);
override_fattime(0);
return ANY_COMMAND;
}
@ -360,9 +357,7 @@ STATIC uint8_t _process_write_data(const uint8_t *raw_buf, size_t command_len) {
// TODO: throw away any more packets of path.
response.status = STATUS_ERROR;
common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct write_pacing), NULL, 0);
#if CIRCUITPY_USB_MSC
usb_msc_unlock();
#endif
filesystem_unlock(active_mount);
override_fattime(0);
return ANY_COMMAND;
}
@ -377,9 +372,7 @@ STATIC uint8_t _process_write_data(const uint8_t *raw_buf, size_t command_len) {
f_truncate(&active_file);
f_close(&active_file);
override_fattime(0);
#if CIRCUITPY_USB_MSC
usb_msc_unlock();
#endif
filesystem_unlock(active_mount);
// Don't reload until everything is written out of the packet buffer.
common_hal_bleio_packet_buffer_flush(&_transfer_packet_buffer);
return ANY_COMMAND;
@ -399,29 +392,19 @@ STATIC uint8_t _process_delete(const uint8_t *raw_buf, size_t command_len) {
common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct delete_status), NULL, 0);
return ANY_COMMAND;
}
if (_usb_active(&response, sizeof(struct delete_status))) {
return ANY_COMMAND;
}
// We need to receive another packet to have the full path.
if (command_len < header_size + command->path_length) {
return THIS_COMMAND;
}
FATFS *fs = filesystem_circuitpy();
char *path = (char *)((uint8_t *)command) + header_size;
path[command->path_length] = '\0';
FILINFO file;
FRESULT result = f_stat(fs, path, &file);
if (result == FR_OK) {
if ((file.fattrib & AM_DIR) != 0) {
result = supervisor_workflow_delete_directory_contents(fs, path);
}
if (result == FR_OK) {
result = f_unlink(fs, path);
}
char *full_path = (char *)((uint8_t *)command) + header_size;
full_path[command->path_length] = '\0';
FRESULT result = supervisor_workflow_delete_recursive(full_path);
if (result == FR_WRITE_PROTECTED) {
response.status = STATUS_ERROR_READONLY;
}
#if CIRCUITPY_USB_MSC
usb_msc_unlock();
#endif
if (result != FR_OK) {
response.status = STATUS_ERROR;
}
@ -456,25 +439,16 @@ STATIC uint8_t _process_mkdir(const uint8_t *raw_buf, size_t command_len) {
common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct mkdir_status), NULL, 0);
return ANY_COMMAND;
}
if (_usb_active(&response, sizeof(struct mkdir_status))) {
return ANY_COMMAND;
}
// We need to receive another packet to have the full path.
if (command_len < header_size + command->path_length) {
return THIS_COMMAND;
}
FATFS *fs = filesystem_circuitpy();
char *path = (char *)command->path;
_terminate_path(path, command->path_length);
char *full_path = (char *)command->path;
_terminate_path(full_path, command->path_length);
DWORD fattime;
response.truncated_time = truncate_time(command->modification_time, &fattime);
override_fattime(fattime);
FRESULT result = supervisor_workflow_mkdir_parents(fs, path);
override_fattime(0);
#if CIRCUITPY_USB_MSC
usb_msc_unlock();
#endif
FRESULT result = supervisor_workflow_mkdir(fattime, full_path);
if (result != FR_OK) {
response.status = STATUS_ERROR;
}
@ -520,12 +494,21 @@ STATIC uint8_t _process_listdir(uint8_t *raw_buf, size_t command_len) {
return THIS_COMMAND;
}
FATFS *fs = filesystem_circuitpy();
char *path = (char *)&command->path;
_terminate_path(path, command->path_length);
// mp_printf(&mp_plat_print, "list %s\n", path);
char *full_path = (char *)&command->path;
_terminate_path(full_path, command->path_length);
const char *mount_path;
active_mount = filesystem_for_path(full_path, &mount_path);
if (active_mount == NULL || !filesystem_native_fatfs(active_mount)) {
entry->command = LISTDIR_ENTRY;
entry->status = STATUS_ERROR_NO_FILE;
send_listdir_entry_header(entry, max_packet_size);
return ANY_COMMAND;
}
FATFS *fs = &active_mount->fatfs;
FF_DIR dir;
FRESULT res = f_opendir(fs, &dir, path);
FRESULT res = f_opendir(fs, &dir, mount_path);
entry->command = LISTDIR_ENTRY;
entry->status = STATUS_OK;
@ -601,27 +584,21 @@ STATIC uint8_t _process_move(const uint8_t *raw_buf, size_t command_len) {
common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct move_status), NULL, 0);
return ANY_COMMAND;
}
if (_usb_active(&response, sizeof(struct move_status))) {
return ANY_COMMAND;
}
// We need to receive another packet to have the full path.
if (command_len < header_size + total_path_length) {
return THIS_COMMAND;
}
FATFS *fs = filesystem_circuitpy();
char *old_path = (char *)command->paths;
old_path[command->old_path_length] = '\0';
char *new_path = old_path + command->old_path_length + 1;
new_path[command->new_path_length] = '\0';
// mp_printf(&mp_plat_print, "move %s to %s\n", old_path, new_path);
FRESULT result = f_rename(fs, old_path, new_path);
#if CIRCUITPY_USB_MSC
usb_msc_unlock();
#endif
if (result != FR_OK) {
FRESULT result = supervisor_workflow_move(old_path, new_path);
if (result == FR_WRITE_PROTECTED) {
response.status = STATUS_ERROR_READONLY;
} else if (result != FR_OK) {
response.status = STATUS_ERROR;
}
common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct move_status), NULL, 0);

View file

@ -221,9 +221,56 @@ bool filesystem_present(void) {
return _mp_vfs.len > 0;
}
FATFS *filesystem_circuitpy(void) {
fs_user_mount_t *filesystem_circuitpy(void) {
if (!filesystem_present()) {
return NULL;
}
return &_internal_vfs.fatfs;
return &_internal_vfs;
}
fs_user_mount_t *filesystem_for_path(const char *path_in, const char **path_under_mount) {
mp_vfs_mount_t *vfs = mp_vfs_lookup_path(path_in, path_under_mount);
if (vfs == MP_VFS_NONE) {
return NULL;
}
fs_user_mount_t *fs_mount;
*path_under_mount = path_in;
if (vfs == MP_VFS_ROOT) {
fs_mount = filesystem_circuitpy();
} else {
fs_mount = MP_OBJ_TO_PTR(vfs->obj);
*path_under_mount += strlen(vfs->str);
}
return fs_mount;
}
bool filesystem_native_fatfs(fs_user_mount_t *fs_mount) {
return fs_mount->base.type == &mp_fat_vfs_type && (fs_mount->blockdev.flags & MP_BLOCKDEV_FLAG_NATIVE) != 0;
}
bool filesystem_lock(fs_user_mount_t *fs_mount) {
if (fs_mount->lock_count == 0 && !blockdev_lock(fs_mount)) {
return false;
}
fs_mount->lock_count += 1;
return true;
}
void filesystem_unlock(fs_user_mount_t *fs_mount) {
fs_mount->lock_count -= 1;
if (fs_mount->lock_count == 0) {
blockdev_unlock(fs_mount);
}
}
bool blockdev_lock(fs_user_mount_t *fs_mount) {
if ((fs_mount->blockdev.flags & MP_BLOCKDEV_FLAG_LOCKED) != 0) {
return false;
}
fs_mount->blockdev.flags |= MP_BLOCKDEV_FLAG_LOCKED;
return true;
}
void blockdev_unlock(fs_user_mount_t *fs_mount) {
fs_mount->blockdev.flags &= ~MP_BLOCKDEV_FLAG_LOCKED;
}

View file

@ -87,7 +87,7 @@ static void build_partition(uint8_t *buf, int boot, int type, uint32_t start_blo
buf[15] = num_blocks >> 24;
}
static mp_uint_t flash_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t num_blocks) {
static mp_uint_t flash_read_blocks(mp_obj_t self, uint8_t *dest, uint32_t block_num, uint32_t num_blocks) {
if (block_num == 0) {
// fake the MBR so we can decide on our own partition table
@ -117,7 +117,7 @@ static mp_uint_t flash_read_blocks(uint8_t *dest, uint32_t block_num, uint32_t n
static volatile bool filesystem_dirty = false;
static mp_uint_t flash_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_blocks) {
static mp_uint_t flash_write_blocks(mp_obj_t self, const uint8_t *src, uint32_t block_num, uint32_t num_blocks) {
if (block_num == 0) {
if (num_blocks > 1) {
return 1; // error
@ -150,7 +150,7 @@ void PLACE_IN_ITCM(supervisor_flash_flush)(void) {
STATIC mp_obj_t supervisor_flash_obj_readblocks(mp_obj_t self, mp_obj_t block_num, mp_obj_t buf) {
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_WRITE);
mp_uint_t ret = flash_read_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / FILESYSTEM_BLOCK_SIZE);
mp_uint_t ret = flash_read_blocks(self, bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / FILESYSTEM_BLOCK_SIZE);
return MP_OBJ_NEW_SMALL_INT(ret);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_3(supervisor_flash_obj_readblocks_obj, supervisor_flash_obj_readblocks);
@ -158,13 +158,15 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_3(supervisor_flash_obj_readblocks_obj, supervisor
STATIC mp_obj_t supervisor_flash_obj_writeblocks(mp_obj_t self, mp_obj_t block_num, mp_obj_t buf) {
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ);
mp_uint_t ret = flash_write_blocks(bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / FILESYSTEM_BLOCK_SIZE);
mp_uint_t ret = flash_write_blocks(self, bufinfo.buf, mp_obj_get_int(block_num), bufinfo.len / FILESYSTEM_BLOCK_SIZE);
return MP_OBJ_NEW_SMALL_INT(ret);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_3(supervisor_flash_obj_writeblocks_obj, supervisor_flash_obj_writeblocks);
static bool flash_ioctl(size_t cmd, mp_int_t *out_value) {
*out_value = 0;
STATIC bool flash_ioctl(mp_obj_t self_in, size_t cmd, size_t arg, mp_int_t *out_value) {
if (out_value != NULL) {
*out_value = 0;
}
switch (cmd) {
case MP_BLOCKDEV_IOCTL_INIT:
supervisor_flash_init();
@ -189,8 +191,9 @@ static bool flash_ioctl(size_t cmd, mp_int_t *out_value) {
STATIC mp_obj_t supervisor_flash_obj_ioctl(mp_obj_t self, mp_obj_t cmd_in, mp_obj_t arg_in) {
mp_int_t cmd = mp_obj_get_int(cmd_in);
mp_int_t arg = mp_obj_get_int(arg_in);
mp_int_t out_value;
if (flash_ioctl(cmd, &out_value)) {
if (flash_ioctl(self, cmd, arg, &out_value)) {
return MP_OBJ_NEW_SMALL_INT(out_value);
}
return mp_const_none;

View file

@ -177,7 +177,7 @@ void print_safe_mode_message(safe_mode_t reason) {
break;
#endif
case SAFE_MODE_STACK_OVERFLOW:
message = MP_ERROR_TEXT("Heap was corrupted because the stack was too small. Increase stack size.");
message = MP_ERROR_TEXT("Stack overflow. Increase stack size.");
break;
case SAFE_MODE_USB_TOO_MANY_ENDPOINTS:
message = MP_ERROR_TEXT("USB devices need more endpoints than are available.");

View file

@ -41,58 +41,7 @@
#define MSC_FLASH_BLOCK_SIZE 512
static bool ejected[1] = {true};
// Lock to track if something else is using the filesystem when USB is plugged in. If so, the drive
// will be made available once the lock is released.
static bool _usb_msc_lock = false;
static bool _usb_connected_while_locked = false;
STATIC void _usb_msc_uneject(void) {
for (uint8_t i = 0; i < sizeof(ejected); i++) {
ejected[i] = false;
}
}
void usb_msc_mount(void) {
// Reset the ejection tracking every time we're plugged into USB. This allows for us to battery
// power the device, eject, unplug and plug it back in to get the drive.
if (_usb_msc_lock) {
_usb_connected_while_locked = true;
return;
}
_usb_msc_uneject();
_usb_connected_while_locked = false;
}
void usb_msc_umount(void) {
}
bool usb_msc_ejected(void) {
bool all_ejected = true;
for (uint8_t i = 0; i < sizeof(ejected); i++) {
all_ejected &= ejected[i];
}
return all_ejected;
}
bool usb_msc_lock(void) {
if ((storage_usb_enabled() && !usb_msc_ejected()) || _usb_msc_lock) {
return false;
}
_usb_msc_lock = true;
return true;
}
void usb_msc_unlock(void) {
if (!_usb_msc_lock) {
// Mismatched unlock.
return;
}
if (_usb_connected_while_locked) {
_usb_msc_uneject();
}
_usb_msc_lock = false;
}
static bool locked[1] = {false};
// The root FS is always at the end of the list.
static fs_user_mount_t *get_vfs(int lun) {
@ -111,6 +60,36 @@ static fs_user_mount_t *get_vfs(int lun) {
return current_mount->obj;
}
STATIC void _usb_msc_uneject(void) {
for (uint8_t i = 0; i < sizeof(ejected); i++) {
ejected[i] = false;
locked[i] = false;
}
}
void usb_msc_mount(void) {
_usb_msc_uneject();
}
void usb_msc_umount(void) {
for (uint8_t i = 0; i < sizeof(ejected); i++) {
fs_user_mount_t *vfs = get_vfs(i + 1);
if (vfs == NULL) {
continue;
}
blockdev_unlock(vfs);
locked[i] = false;
}
}
bool usb_msc_ejected(void) {
bool all_ejected = true;
for (uint8_t i = 0; i < sizeof(ejected); i++) {
all_ejected &= ejected[i];
}
return all_ejected;
}
// Callback invoked when received an SCSI command not in built-in list below
// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, TEST_UNIT_READY, START_STOP_UNIT, MODE_SENSE6, REQUEST_SENSE
// - READ10 and WRITE10 have their own callbacks
@ -164,6 +143,11 @@ bool tud_msc_is_writable_cb(uint8_t lun) {
if (vfs->blockdev.writeblocks[0] == MP_OBJ_NULL || !filesystem_is_writable_by_usb(vfs)) {
return false;
}
// Lock the blockdev once we say we're writable.
if (!locked[lun] && !blockdev_lock(vfs)) {
return false;
}
locked[lun] = true;
return true;
}
@ -267,7 +251,9 @@ bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, boo
if (disk_ioctl(current_mount, CTRL_SYNC, NULL) != RES_OK) {
return false;
} else {
blockdev_unlock(current_mount);
ejected[lun] = true;
locked[lun] = false;
}
} else {
// We can only load if it hasn't been ejected.

View file

@ -17,8 +17,9 @@
<hr>
<label>📄 <input type="file" id="files" multiple></label>
<label for="dirs">📁 <input type="file" id="dirs" multiple webkitdirectory></label>
<label>Upload progress:<progress value="0"></progress></label>
<span id="progress"></span>
<hr>
+📁&nbsp;<input type="text" id="name"><button type="submit" id="mkdir">Create Directory</button>
<label>Disk usage:<span id="usage"></span></label>
<script src="/directory.js" defer=true></script>
</body></html>

View file

@ -4,7 +4,28 @@ let dirs = document.getElementById("dirs");
var url_base = window.location;
var current_path;
var editable = undefined;
// From: https://stackoverflow.com/questions/10420352/converting-file-size-in-bytes-to-human-readable-string
function humanFileSize(bytes) {
const thresh = 1000;
if (Math.abs(bytes) < thresh) {
return bytes + ' B';
}
const units = ['kB', 'MB', 'GB', 'TB'];
let u = -1;
const r = 10;
do {
bytes /= thresh;
++u;
} while (Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1);
return bytes.toFixed(1) + ' ' + units[u];
}
function compareValues(a, b) {
if (a.directory == b.directory && a.name.toLowerCase() === b.name.toLowerCase()) {
@ -42,21 +63,20 @@ async function refresh_list() {
var path = document.querySelector("#path");
path.textContent = current_path;
var template = document.querySelector('#row');
var disk_usage = document.querySelector('#usage');
if (editable === undefined) {
const status = await fetch(new URL("/fs/", url_base),
{
method: "OPTIONS",
credentials: "include"
}
);
editable = status.headers.get("Access-Control-Allow-Methods").includes("DELETE");
new_directory_name.disabled = !editable;
set_upload_enabled(editable);
if (!editable) {
let usbwarning = document.querySelector("#usbwarning");
usbwarning.style.display = "block";
}
let used = humanFileSize((data.total - data.free) * data.block_size);
let total = humanFileSize(data.total * data.block_size);
disk_usage.textContent = `${used} out of ${total}`;
let editable = data.writable;
new_directory_name.disabled = !editable;
set_upload_enabled(editable);
let usbwarning = document.querySelector("#usbwarning");
if (!editable) {
usbwarning.style.display = "block";
} else {
usbwarning.style.display = "none";
}
if (current_path != "/") {
@ -74,9 +94,9 @@ async function refresh_list() {
new_children.push(clone);
}
data.sort(compareValues);
data.files.sort(compareValues);
for (const f of data) {
for (const f of data.files) {
// Clone the new row and insert it into the table
var clone = template.content.cloneNode(true);
var td = clone.querySelectorAll("td");
@ -106,7 +126,7 @@ async function refresh_list() {
text_file = true;
}
td[0].textContent = icon;
td[1].textContent = f.file_size;
td[1].textContent = humanFileSize(f.file_size);
var path = clone.querySelector("a.path");
path.href = file_path;
path.textContent = f.name;
@ -173,10 +193,13 @@ async function mkdir(e) {
async function upload(e) {
set_upload_enabled(false);
let progress = document.querySelector("progress");
let progress = document.querySelector("#progress");
let made_dirs = new Set();
progress.max = files.files.length + dirs.files.length;
progress.value = 0;
let total = files.files.length + dirs.files.length;
var uploaded = 0;
var failed = 0;
progress.textContent = `Uploaded ${uploaded} out of ${total} files`;
for (const file of [...files.files, ...dirs.files]) {
let file_name = file.name;
if (file.webkitRelativePath) {
@ -203,12 +226,21 @@ async function upload(e) {
)
if (response.ok) {
refresh_list();
uploaded += 1;
} else {
failed += 1;
}
progress.value += 1;
progress.textContent = `Uploaded ${uploaded} out of ${total} files`;
}
var s = "";
if (failed > 0) {
if (failed > 1) {
s = "s";
}
progress.textContent = `${failed} upload${s} failed`;
}
files.value = "";
dirs.value = "";
progress.value = 0;
set_upload_enabled(true);
}

View file

@ -525,17 +525,6 @@ static bool _origin_ok(_request *request) {
return false;
}
STATIC bool _usb_active(void) {
// Check to see if USB has already been mounted. If not, then we "eject" from USB until we're done.
#if CIRCUITPY_USB && CIRCUITPY_USB_MSC
if (storage_usb_enabled() && !usb_msc_lock()) {
return true;
}
#endif
return false;
}
static const char *OK_JSON = "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\nContent-Type: application/json\r\n";
static void _cors_header(socketpool_socket_obj_t *socket, _request *request) {
@ -574,13 +563,7 @@ static void _reply_access_control(socketpool_socket_obj_t *socket, _request *req
"Content-Length: 0\r\n",
"Access-Control-Expose-Headers: Access-Control-Allow-Methods\r\n",
"Access-Control-Allow-Headers: X-Timestamp, X-Destination, Content-Type, Authorization\r\n",
"Access-Control-Allow-Methods:GET, OPTIONS", NULL);
if (!_usb_active()) {
_send_str(socket, ", PUT, DELETE, MOVE");
#if CIRCUITPY_USB_MSC
usb_msc_unlock();
#endif
}
"Access-Control-Allow-Methods:GET, OPTIONS, PUT, DELETE, MOVE", NULL);
_send_str(socket, "\r\n");
_cors_header(socket, request);
_send_final_str(socket, "\r\n");
@ -687,17 +670,47 @@ static void _reply_redirect(socketpool_socket_obj_t *socket, _request *request,
}
#endif
static void _reply_directory_json(socketpool_socket_obj_t *socket, _request *request, FF_DIR *dir, const char *request_path, const char *path) {
static void _reply_directory_json(socketpool_socket_obj_t *socket, _request *request, fs_user_mount_t *fs_mount, FF_DIR *dir, const char *request_path, const char *path) {
FILINFO file_info;
char *fn = file_info.fname;
FRESULT res = f_readdir(dir, &file_info);
if (res != FR_OK) {
_reply_missing(socket, request);
return;
}
socketpool_socket_send(socket, (const uint8_t *)OK_JSON, strlen(OK_JSON));
_cors_header(socket, request);
_send_str(socket, "\r\n");
mp_print_t _socket_print = {socket, _print_chunk};
_send_chunk(socket, "[");
// Send mount info.
DWORD free_clusters = 0;
FATFS *fatfs = &fs_mount->fatfs;
f_getfree(fatfs, &free_clusters);
size_t ssize;
#if FF_MAX_SS != FF_MIN_SS
ssize = fatfs->ssize;
#else
ssize = FF_MIN_SS;
#endif
uint32_t cluster_size = fatfs->csize * ssize;
uint32_t total_clusters = fatfs->n_fatent - 2;
const char *writable = "false";
if (filesystem_is_writable_by_python(fs_mount)) {
writable = "true";
}
mp_printf(&_socket_print,
"{\"free\": %u, "
"\"total\": %u, "
"\"block_size\": %u, "
"\"writable\": %s, ", free_clusters, total_clusters, cluster_size, writable);
// Send file list
_send_chunk(socket, "\"files\": [");
bool first = true;
FILINFO file_info;
char *fn = file_info.fname;
FRESULT res = f_readdir(dir, &file_info);
while (res == FR_OK && fn[0] != 0) {
if (!first) {
_send_chunk(socket, ",");
@ -733,7 +746,7 @@ static void _reply_directory_json(socketpool_socket_obj_t *socket, _request *req
first = false;
res = f_readdir(dir, &file_info);
}
_send_chunk(socket, "]");
_send_chunk(socket, "]}");
_send_chunk(socket, "");
}
@ -851,7 +864,7 @@ static void _reply_with_version_json(socketpool_socket_obj_t *socket, _request *
_update_encoded_ip();
// Note: this leverages the fact that C concats consecutive string literals together.
mp_printf(&_socket_print,
"{\"web_api_version\": 3, "
"{\"web_api_version\": 4, "
"\"version\": \"" MICROPY_GIT_TAG "\", "
"\"build_date\": \"" MICROPY_BUILD_DATE "\", "
"\"board_name\": \"%s\", "
@ -880,27 +893,46 @@ static void _reply_with_diskinfo_json(socketpool_socket_obj_t *socket, _request
_cors_header(socket, request);
_send_str(socket, "\r\n");
mp_print_t _socket_print = {socket, _print_chunk};
_send_chunk(socket, "[");
DWORD free_clusters;
FATFS *fs = filesystem_circuitpy();
FRESULT blk_result = f_getfree(fs, &free_clusters);
uint16_t block_size;
if (blk_result == FR_OK) {
disk_ioctl(fs, GET_SECTOR_SIZE, &block_size);
mp_vfs_mount_t *vfs = MP_STATE_VM(vfs_mount_table);
size_t i = 0;
while (vfs != NULL) {
if (i > 0) {
_send_chunk(socket, ",");
}
fs_user_mount_t *fs = MP_OBJ_TO_PTR(vfs->obj);
// Skip non-fat and non-native block file systems.
if (fs->base.type != &mp_fat_vfs_type || (fs->blockdev.flags & MP_BLOCKDEV_FLAG_NATIVE) == 0) {
vfs = vfs->next;
continue;
}
DWORD free_clusters = 0;
FATFS *fatfs = &fs->fatfs;
f_getfree(fatfs, &free_clusters);
size_t ssize;
#if FF_MAX_SS != FF_MIN_SS
ssize = fatfs->ssize;
#else
ssize = FF_MIN_SS;
#endif
size_t block_size = fatfs->csize * ssize;
size_t total_size = fatfs->n_fatent - 2;
const char *writable = "false";
if (filesystem_is_writable_by_python(fs)) {
writable = "true";
}
mp_printf(&_socket_print,
"{\"root\": \"%s\", "
"\"free\": %u, "
"\"total\": %u, "
"\"block_size\": %u, "
"\"writable\": %s}", vfs->str, free_clusters, total_size, block_size, writable);
i++;
vfs = vfs->next;
}
uint16_t total_size = fs->n_fatent - 2;
const char *writable = "false";
if (!_usb_active()) {
writable = "true";
}
mp_printf(&_socket_print,
"[{\"root\": \"/\", "
"\"free\": %d, "
"\"block_size\": %d, "
"\"writable\": %s, "
"\"total\": %d}]", free_clusters * block_size, block_size, writable, total_size * block_size);
_send_chunk(socket, "]");
// Empty chunk signals the end of the response.
_send_chunk(socket, "");
@ -937,10 +969,10 @@ STATIC void _discard_incoming(socketpool_socket_obj_t *socket, size_t amount) {
}
}
static void _write_file_and_reply(socketpool_socket_obj_t *socket, _request *request, FATFS *fs, const TCHAR *path) {
static void _write_file_and_reply(socketpool_socket_obj_t *socket, _request *request, fs_user_mount_t *fs_mount, const TCHAR *path) {
FIL active_file;
if (_usb_active()) {
if (!filesystem_lock(fs_mount)) {
_discard_incoming(socket, request->content_length);
_reply_conflict(socket, request);
return;
@ -951,6 +983,7 @@ static void _write_file_and_reply(socketpool_socket_obj_t *socket, _request *req
override_fattime(fattime);
}
FATFS *fs = &fs_mount->fatfs;
FRESULT result = f_open(fs, &active_file, path, FA_WRITE);
bool new_file = false;
size_t old_length = 0;
@ -963,18 +996,14 @@ static void _write_file_and_reply(socketpool_socket_obj_t *socket, _request *req
if (result == FR_NO_PATH) {
override_fattime(0);
#if CIRCUITPY_USB_MSC
usb_msc_unlock();
#endif
filesystem_unlock(fs_mount);
_discard_incoming(socket, request->content_length);
_reply_missing(socket, request);
return;
}
if (result != FR_OK) {
override_fattime(0);
#if CIRCUITPY_USB_MSC
usb_msc_unlock();
#endif
filesystem_unlock(fs_mount);
_discard_incoming(socket, request->content_length);
_reply_server_error(socket, request);
return;
@ -994,9 +1023,7 @@ static void _write_file_and_reply(socketpool_socket_obj_t *socket, _request *req
f_unlink(fs, path);
}
override_fattime(0);
#if CIRCUITPY_USB_MSC
usb_msc_unlock();
#endif
filesystem_unlock(fs_mount);
// Too large.
if (request->expect) {
_reply_expectation_failed(socket, request);
@ -1034,9 +1061,7 @@ static void _write_file_and_reply(socketpool_socket_obj_t *socket, _request *req
}
f_close(&active_file);
#if CIRCUITPY_USB_MSC
usb_msc_unlock();
#endif
filesystem_unlock(fs_mount);
override_fattime(0);
if (error) {
@ -1165,7 +1190,6 @@ static bool _reply(socketpool_socket_obj_t *socket, _request *request) {
char *path = request->path + 3;
size_t pathlen = strlen(path);
FATFS *fs = filesystem_circuitpy();
// Trailing / is a directory.
bool directory = false;
if (path[pathlen - 1] == '/') {
@ -1175,29 +1199,16 @@ static bool _reply(socketpool_socket_obj_t *socket, _request *request) {
}
directory = true;
}
// These manipulations work on the full path so do them first.
// Delete is almost identical for files and directories so share the
// implementation.
if (strcasecmp(request->method, "DELETE") == 0) {
if (_usb_active()) {
FRESULT result = supervisor_workflow_delete_recursive(path);
if (result == FR_WRITE_PROTECTED) {
_reply_conflict(socket, request);
return false;
}
FILINFO file;
FRESULT result = f_stat(fs, path, &file);
if (result == FR_OK) {
if ((file.fattrib & AM_DIR) != 0) {
result = supervisor_workflow_delete_directory_contents(fs, path);
}
if (result == FR_OK) {
result = f_unlink(fs, path);
}
}
#if CIRCUITPY_USB_MSC
usb_msc_unlock();
#endif
if (result == FR_NO_PATH || result == FR_NO_FILE) {
} else if (result == FR_NO_PATH || result == FR_NO_FILE) {
_reply_missing(socket, request);
} else if (result != FR_OK) {
_reply_server_error(socket, request);
@ -1205,12 +1216,8 @@ static bool _reply(socketpool_socket_obj_t *socket, _request *request) {
_reply_no_content(socket, request);
return true;
}
return false;
} else if (strcasecmp(request->method, "MOVE") == 0) {
if (_usb_active()) {
_reply_conflict(socket, request);
return false;
}
_decode_percents(request->destination);
char *destination = request->destination + 3;
size_t destinationlen = strlen(destination);
@ -1218,11 +1225,10 @@ static bool _reply(socketpool_socket_obj_t *socket, _request *request) {
destination[destinationlen - 1] = '\0';
}
FRESULT result = f_rename(fs, path, destination);
#if CIRCUITPY_USB_MSC
usb_msc_unlock();
#endif
if (result == FR_EXIST) { // File exists and won't be overwritten.
FRESULT result = supervisor_workflow_move(path, destination);
if (result == FR_WRITE_PROTECTED) {
_reply_conflict(socket, request);
} else if (result == FR_EXIST) { // File exists and won't be overwritten.
_reply_precondition_failed(socket, request);
} else if (result == FR_NO_PATH || result == FR_NO_FILE) { // Missing higher directories or target file.
_reply_missing(socket, request);
@ -1232,7 +1238,51 @@ static bool _reply(socketpool_socket_obj_t *socket, _request *request) {
_reply_created(socket, request);
return true;
}
} else if (directory) {
return false;
} else if (directory && strcasecmp(request->method, "PUT") == 0) {
DWORD fattime = 0;
if (request->timestamp_ms > 0) {
truncate_time(request->timestamp_ms * 1000000, &fattime);
}
FRESULT result = supervisor_workflow_mkdir_parents(fattime, path);
if (result == FR_WRITE_PROTECTED) {
_reply_conflict(socket, request);
} else if (result == FR_EXIST) {
_reply_no_content(socket, request);
} else if (result == FR_NO_PATH) {
_reply_missing(socket, request);
} else if (result != FR_OK) {
_reply_server_error(socket, request);
} else {
_reply_created(socket, request);
return true;
}
return false;
}
// These responses don't use helpers because they stream data in and
// out. So, share the mount lookup code.
const char *path_out = NULL;
mp_vfs_mount_t *vfs = mp_vfs_lookup_path(path, &path_out);
if (vfs == MP_VFS_NONE) {
_reply_missing(socket, request);
return false;
}
fs_user_mount_t *fs_mount;
if (vfs == MP_VFS_ROOT) {
fs_mount = filesystem_circuitpy();
} else {
fs_mount = MP_OBJ_TO_PTR(vfs->obj);
// Skip non-fat and non-native block file systems.
if (!filesystem_native_fatfs(fs_mount)) {
_reply_missing(socket, request);
return false;
}
path += strlen(vfs->str);
pathlen = strlen(path);
}
FATFS *fs = &fs_mount->fatfs;
if (directory) {
if (strcasecmp(request->method, "GET") == 0) {
FF_DIR dir;
FRESULT res = f_opendir(fs, &dir, path);
@ -1245,7 +1295,7 @@ static bool _reply(socketpool_socket_obj_t *socket, _request *request) {
return false;
}
if (request->json) {
_reply_directory_json(socket, request, &dir, request->path, path);
_reply_directory_json(socket, request, fs_mount, &dir, request->path, path);
} else if (pathlen == 1) {
_REPLY_STATIC(socket, request, directory_html);
} else {
@ -1253,32 +1303,6 @@ static bool _reply(socketpool_socket_obj_t *socket, _request *request) {
}
f_closedir(&dir);
} else if (strcasecmp(request->method, "PUT") == 0) {
if (_usb_active()) {
_reply_conflict(socket, request);
return false;
}
if (request->timestamp_ms > 0) {
DWORD fattime;
truncate_time(request->timestamp_ms * 1000000, &fattime);
override_fattime(fattime);
}
FRESULT result = supervisor_workflow_mkdir_parents(fs, path);
override_fattime(0);
#if CIRCUITPY_USB_MSC
usb_msc_unlock();
#endif
if (result == FR_EXIST) {
_reply_no_content(socket, request);
} else if (result == FR_NO_PATH) {
_reply_missing(socket, request);
} else if (result != FR_OK) {
_reply_server_error(socket, request);
} else {
_reply_created(socket, request);
return true;
}
}
} else { // Dealing with a file.
if (strcasecmp(request->method, "GET") == 0) {
@ -1293,7 +1317,7 @@ static bool _reply(socketpool_socket_obj_t *socket, _request *request) {
f_close(&active_file);
} else if (strcasecmp(request->method, "PUT") == 0) {
_write_file_and_reply(socket, request, fs, path);
_write_file_and_reply(socket, request, fs_mount, path);
return true;
}
}

View file

@ -29,6 +29,8 @@
#include "py/mpstate.h"
#include "py/stackctrl.h"
#include "supervisor/background_callback.h"
#include "supervisor/fatfs.h"
#include "supervisor/filesystem.h"
#include "supervisor/workflow.h"
#include "supervisor/serial.h"
#include "supervisor/shared/workflow.h"
@ -118,24 +120,81 @@ void supervisor_workflow_start(void) {
#endif
}
FRESULT supervisor_workflow_mkdir_parents(FATFS *fs, char *path) {
FRESULT supervisor_workflow_move(const char *old_path, const char *new_path) {
const char *old_mount_path;
const char *new_mount_path;
fs_user_mount_t *active_mount = filesystem_for_path(old_path, &old_mount_path);
fs_user_mount_t *new_mount = filesystem_for_path(new_path, &new_mount_path);
if (active_mount == NULL || new_mount == NULL || active_mount != new_mount || !filesystem_native_fatfs(active_mount)) {
return FR_NO_PATH;
}
if (!filesystem_lock(active_mount)) {
return FR_WRITE_PROTECTED;
}
FATFS *fs = &active_mount->fatfs;
FRESULT result = f_rename(fs, old_mount_path, new_mount_path);
filesystem_unlock(active_mount);
return result;
}
FRESULT supervisor_workflow_mkdir(DWORD fattime, const char *full_path) {
const char *mount_path;
fs_user_mount_t *active_mount = filesystem_for_path(full_path, &mount_path);
if (active_mount == NULL || !filesystem_native_fatfs(active_mount)) {
return FR_NO_PATH;
}
// If there is a mount on the directory, then the mount_path will be empty.
if (strlen(mount_path) == 0) {
return FR_EXIST;
}
// Check to see if the directory exists already. We don't care about writing
// it if it already exists.
FATFS *fs = &active_mount->fatfs;
FILINFO file;
FRESULT result = f_stat(fs, mount_path, &file);
if (result == FR_OK) {
return FR_EXIST;
}
if (!filesystem_lock(active_mount)) {
return FR_WRITE_PROTECTED;
}
override_fattime(fattime);
result = f_mkdir(fs, mount_path);
override_fattime(0);
filesystem_unlock(active_mount);
return result;
}
FRESULT supervisor_workflow_mkdir_parents(DWORD fattime, char *path) {
override_fattime(fattime);
FRESULT result = FR_OK;
// Make parent directories.
for (size_t j = 1; j < strlen(path); j++) {
if (path[j] == '/') {
path[j] = '\0';
result = f_mkdir(fs, path);
result = supervisor_workflow_mkdir(fattime, path);
path[j] = '/';
if (result != FR_OK && result != FR_EXIST) {
return result;
break;
}
}
}
// Make the target directory.
return f_mkdir(fs, path);
if (result == FR_OK || result == FR_EXIST) {
result = supervisor_workflow_mkdir(fattime, path);
// This may return FR_EXIST when a file with the same name already exists.
// FATFS does the same thing.
}
override_fattime(0);
return result;
}
FRESULT supervisor_workflow_delete_directory_contents(FATFS *fs, const TCHAR *path) {
STATIC FRESULT supervisor_workflow_delete_directory_contents(FATFS *fs, const TCHAR *path) {
FF_DIR dir;
FILINFO file_info;
// Check the stack since we're putting paths on it.
@ -174,3 +233,27 @@ FRESULT supervisor_workflow_delete_directory_contents(FATFS *fs, const TCHAR *pa
f_closedir(&dir);
return res;
}
FRESULT supervisor_workflow_delete_recursive(const char *full_path) {
const char *mount_path;
fs_user_mount_t *active_mount = filesystem_for_path(full_path, &mount_path);
if (active_mount == NULL || !filesystem_native_fatfs(active_mount)) {
return FR_NO_PATH;
}
if (!filesystem_lock(active_mount)) {
return FR_WRITE_PROTECTED;
}
FATFS *fs = &active_mount->fatfs;
FILINFO file;
FRESULT result = f_stat(fs, mount_path, &file);
if (result == FR_OK) {
if ((file.fattrib & AM_DIR) != 0) {
result = supervisor_workflow_delete_directory_contents(fs, mount_path);
}
if (result == FR_OK) {
result = f_unlink(fs, mount_path);
}
}
filesystem_unlock(active_mount);
return result;
}

View file

@ -31,5 +31,7 @@
extern bool supervisor_workflow_connecting(void);
// File system helpers for workflow code.
FRESULT supervisor_workflow_mkdir_parents(FATFS *fs, char *path);
FRESULT supervisor_workflow_delete_directory_contents(FATFS *fs, const TCHAR *path);
FRESULT supervisor_workflow_move(const char *old_path, const char *new_path);
FRESULT supervisor_workflow_mkdir(DWORD fattime, const char *full_path);
FRESULT supervisor_workflow_mkdir_parents(DWORD fattime, char *path);
FRESULT supervisor_workflow_delete_recursive(const char *full_path);

View file

@ -24,8 +24,7 @@
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_SUPERVISOR_USB_H
#define MICROPY_INCLUDED_SUPERVISOR_USB_H
#pragma once
#include <stdbool.h>
#include <stddef.h>
@ -85,11 +84,6 @@ void usb_setup_with_vm(void);
void usb_msc_mount(void);
void usb_msc_umount(void);
bool usb_msc_ejected(void);
// Locking MSC prevents presenting the drive on plug-in when in use by something
// else (likely BLE.)
bool usb_msc_lock(void);
void usb_msc_unlock(void);
#endif
#if CIRCUITPY_USB_KEYBOARD_WORKFLOW
@ -102,5 +96,3 @@ void usb_keyboard_detach(uint8_t dev_addr, uint8_t interface);
void usb_keyboard_attach(uint8_t dev_addr, uint8_t interface);
void usb_keymap_set(const uint8_t *buf, size_t len);
#endif
#endif // MICROPY_INCLUDED_SUPERVISOR_USB_H