Make SD cards available over web workflow
This changes storage.mount() to require that a mount point exist on the parent file system. A bug in background tasks is also fixed where the function parameter is cleared on pending callbacks during "reset". Disk usage is shown on the directory listing and changes based on the mounted file system. Writable is also loaded per-directory. Fixes #8108. Fixes #8690. Fixes #8107.
This commit is contained in:
parent
9beefdbe8d
commit
25e862d110
26 changed files with 581 additions and 356 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
|
||||
|
|
@ -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,8 @@ 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);
|
||||
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 +109,8 @@ 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);
|
||||
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 +141,15 @@ 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) {
|
||||
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,7 @@ typedef struct _fs_user_mount_t {
|
|||
mp_obj_base_t base;
|
||||
mp_vfs_blockdev_t blockdev;
|
||||
FATFS fatfs;
|
||||
int8_t lock_count;
|
||||
} fs_user_mount_t;
|
||||
|
||||
extern const byte fresult_to_errno_table[20];
|
||||
|
|
|
|||
|
|
@ -1378,6 +1378,10 @@ msgstr ""
|
|||
msgid "Missing jmp_pin. %q[%u] jumps on pin"
|
||||
msgstr ""
|
||||
|
||||
#: shared-module/storage/__init__.c
|
||||
msgid "Mount point missing. Create first (maybe via USB)"
|
||||
msgstr ""
|
||||
|
||||
#: shared-bindings/busio/UART.c shared-bindings/displayio/Group.c
|
||||
msgid "Must be a %q subclass."
|
||||
msgstr ""
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,16 @@ 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's file or 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);
|
||||
nlr_pop();
|
||||
// Something with the same name exists.
|
||||
mp_raise_OSError(MP_EEXIST);
|
||||
} else {
|
||||
// Something with the same name doesn't exists.
|
||||
mp_raise_RuntimeError(MP_ERROR_TEXT("Mount point missing. Create first (maybe via USB)"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -20,5 +20,6 @@
|
|||
<label>Upload progress:<progress value="0"></progress></label>
|
||||
<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,18 @@ 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);
|
||||
if (!editable) {
|
||||
let usbwarning = document.querySelector("#usbwarning");
|
||||
usbwarning.style.display = "block";
|
||||
}
|
||||
|
||||
if (current_path != "/") {
|
||||
|
|
@ -74,9 +92,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 +124,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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
@ -1164,8 +1189,28 @@ static bool _reply(socketpool_socket_obj_t *socket, _request *request) {
|
|||
_decode_percents(request->path);
|
||||
|
||||
char *path = request->path + 3;
|
||||
const char *path_out = NULL;
|
||||
size_t pathlen = strlen(path);
|
||||
FATFS *fs = filesystem_circuitpy();
|
||||
|
||||
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;
|
||||
// Trailing / is a directory.
|
||||
bool directory = false;
|
||||
if (path[pathlen - 1] == '/') {
|
||||
|
|
@ -1178,25 +1223,7 @@ static bool _reply(socketpool_socket_obj_t *socket, _request *request) {
|
|||
// Delete is almost identical for files and directories so share the
|
||||
// implementation.
|
||||
if (strcasecmp(request->method, "DELETE") == 0) {
|
||||
if (_usb_active()) {
|
||||
_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
|
||||
FRESULT result = supervisor_workflow_delete_recursive(path);
|
||||
if (result == FR_NO_PATH || result == FR_NO_FILE) {
|
||||
_reply_missing(socket, request);
|
||||
} else if (result != FR_OK) {
|
||||
|
|
@ -1206,11 +1233,6 @@ static bool _reply(socketpool_socket_obj_t *socket, _request *request) {
|
|||
return true;
|
||||
}
|
||||
} 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 +1240,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);
|
||||
|
|
@ -1245,7 +1266,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 {
|
||||
|
|
@ -1254,22 +1275,14 @@ 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;
|
||||
}
|
||||
|
||||
DWORD fattime = 0;
|
||||
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) {
|
||||
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);
|
||||
|
|
@ -1293,7 +1306,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,65 @@ 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_path, new_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 (!filesystem_lock(active_mount)) {
|
||||
return FR_WRITE_PROTECTED;
|
||||
}
|
||||
FATFS *fs = &active_mount->fatfs;
|
||||
|
||||
override_fattime(fattime);
|
||||
FRESULT 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);
|
||||
}
|
||||
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 +217,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, full_path, &file);
|
||||
if (result == FR_OK) {
|
||||
if ((file.fattrib & AM_DIR) != 0) {
|
||||
result = supervisor_workflow_delete_directory_contents(fs, full_path);
|
||||
}
|
||||
if (result == FR_OK) {
|
||||
result = f_unlink(fs, full_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