Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
526faae2a1
52 changed files with 745 additions and 545 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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 ""
|
||||
|
|
|
|||
|
|
@ -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."));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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]) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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) },
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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]),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.");
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
+📁 <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>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue