summaryrefslogtreecommitdiff
path: root/extmod
diff options
context:
space:
mode:
Diffstat (limited to 'extmod')
-rw-r--r--extmod/crypto-algorithms/sha256.c1
-rw-r--r--extmod/fsusermount.c209
-rw-r--r--extmod/machine_pulse.c6
-rw-r--r--extmod/machine_signal.c183
-rw-r--r--extmod/machine_signal.h35
-rw-r--r--extmod/machine_spi.c6
-rw-r--r--extmod/modbtree.c47
-rw-r--r--extmod/modframebuf.c116
-rw-r--r--extmod/modlwip.c39
-rw-r--r--extmod/moductypes.c2
-rw-r--r--extmod/modujson.c2
-rw-r--r--extmod/modurandom.c15
-rw-r--r--extmod/modure.c4
-rw-r--r--extmod/moduselect.c142
-rw-r--r--extmod/modussl_mbedtls.c4
-rw-r--r--extmod/modutimeq.c22
-rw-r--r--extmod/modwebrepl.c2
-rw-r--r--extmod/modwebsocket.c2
-rw-r--r--extmod/utime_mphal.c6
-rw-r--r--extmod/vfs.c457
-rw-r--r--extmod/vfs.h86
-rw-r--r--extmod/vfs_fat.c213
-rw-r--r--extmod/vfs_fat.h (renamed from extmod/fsusermount.h)33
-rw-r--r--extmod/vfs_fat_diskio.c59
-rw-r--r--extmod/vfs_fat_ffconf.c82
-rw-r--r--extmod/vfs_fat_file.c34
-rw-r--r--extmod/vfs_fat_file.h16
-rw-r--r--extmod/vfs_fat_misc.c103
-rw-r--r--extmod/vfs_reader.c (renamed from extmod/vfs_fat_reader.c)71
-rw-r--r--extmod/virtpin.h3
30 files changed, 1324 insertions, 676 deletions
diff --git a/extmod/crypto-algorithms/sha256.c b/extmod/crypto-algorithms/sha256.c
index 82e5d9c7b..276611cfd 100644
--- a/extmod/crypto-algorithms/sha256.c
+++ b/extmod/crypto-algorithms/sha256.c
@@ -14,7 +14,6 @@
/*************************** HEADER FILES ***************************/
#include <stdlib.h>
-#include <memory.h>
#include "sha256.h"
/****************************** MACROS ******************************/
diff --git a/extmod/fsusermount.c b/extmod/fsusermount.c
deleted file mode 100644
index 5882aba99..000000000
--- a/extmod/fsusermount.c
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * This file is part of the Micro Python project, http://micropython.org/
- *
- * The MIT License (MIT)
- *
- * Copyright (c) 2014 Damien P. George
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "py/mpconfig.h"
-#if MICROPY_FSUSERMOUNT
-#include <string.h>
-#include <errno.h>
-
-#include "py/nlr.h"
-#include "py/runtime.h"
-#include "py/mperrno.h"
-#include "lib/fatfs/ff.h"
-#include "extmod/fsusermount.h"
-
-fs_user_mount_t *fatfs_mount_mkfs(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args, bool mkfs) {
- static const mp_arg_t allowed_args[] = {
- { MP_QSTR_readonly, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
- { MP_QSTR_mkfs, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
- };
-
- // parse args
- mp_obj_t device = pos_args[0];
- mp_obj_t mount_point = pos_args[1];
- mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
- mp_arg_parse_all(n_args - 2, pos_args + 2, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
-
- // get the mount point
- mp_uint_t mnt_len;
- const char *mnt_str = mp_obj_str_get_data(mount_point, &mnt_len);
-
- if (device == mp_const_none) {
- // umount
- FRESULT res = FR_NO_FILESYSTEM;
- for (size_t i = 0; i < MP_ARRAY_SIZE(MP_STATE_PORT(fs_user_mount)); ++i) {
- fs_user_mount_t *vfs = MP_STATE_PORT(fs_user_mount)[i];
- if (vfs != NULL && !memcmp(mnt_str, vfs->str, mnt_len + 1)) {
- res = f_mount(NULL, vfs->str, 0);
- if (vfs->flags & FSUSER_FREE_OBJ) {
- m_del_obj(fs_user_mount_t, vfs);
- }
- MP_STATE_PORT(fs_user_mount)[i] = NULL;
- break;
- }
- }
- if (res != FR_OK) {
- nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't umount"));
- }
- return NULL;
- } else {
- // mount
- size_t i = 0;
- for (; i < MP_ARRAY_SIZE(MP_STATE_PORT(fs_user_mount)); ++i) {
- if (MP_STATE_PORT(fs_user_mount)[i] == NULL) {
- break;
- }
- }
- if (i == MP_ARRAY_SIZE(MP_STATE_PORT(fs_user_mount))) {
- nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "too many devices mounted"));
- }
-
- // create new object
- fs_user_mount_t *vfs = m_new_obj(fs_user_mount_t);
- vfs->str = mnt_str;
- vfs->len = mnt_len;
- vfs->flags = FSUSER_FREE_OBJ;
-
- // load block protocol methods
- mp_load_method(device, MP_QSTR_readblocks, vfs->readblocks);
- mp_load_method_maybe(device, MP_QSTR_writeblocks, vfs->writeblocks);
- mp_load_method_maybe(device, MP_QSTR_ioctl, vfs->u.ioctl);
- if (vfs->u.ioctl[0] != MP_OBJ_NULL) {
- // device supports new block protocol, so indicate it
- vfs->flags |= FSUSER_HAVE_IOCTL;
- } else {
- // no ioctl method, so assume the device uses the old block protocol
- mp_load_method_maybe(device, MP_QSTR_sync, vfs->u.old.sync);
- mp_load_method(device, MP_QSTR_count, vfs->u.old.count);
- }
-
- // Read-only device indicated by writeblocks[0] == MP_OBJ_NULL.
- // User can specify read-only device by:
- // 1. readonly=True keyword argument
- // 2. nonexistent writeblocks method (then writeblocks[0] == MP_OBJ_NULL already)
- if (args[0].u_bool) {
- vfs->writeblocks[0] = MP_OBJ_NULL;
- }
-
- // Register the vfs object so that it can be found by the FatFS driver using
- // ff_get_ldnumber. We don't register it any earlier than this point in case there
- // is an exception, in which case there would remain a partially mounted device.
- MP_STATE_PORT(fs_user_mount)[i] = vfs;
-
- // mount the block device (if mkfs, only pre-mount)
- FRESULT res = f_mount(&vfs->fatfs, vfs->str, !mkfs);
- // check the result
- if (res == FR_OK) {
- if (mkfs) {
- goto mkfs;
- }
- } else if (res == FR_NO_FILESYSTEM && args[1].u_bool) {
-mkfs:
- res = f_mkfs(vfs->str, 1, 0);
- if (res != FR_OK) {
-mkfs_error:
- MP_STATE_PORT(fs_user_mount)[i] = NULL;
- nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't mkfs"));
- }
- if (mkfs) {
- // If requested to only mkfs, unmount pre-mounted device
- res = f_mount(NULL, vfs->str, 0);
- if (res != FR_OK) {
- goto mkfs_error;
- }
- MP_STATE_PORT(fs_user_mount)[i] = NULL;
- return NULL;
- }
- } else {
- MP_STATE_PORT(fs_user_mount)[i] = NULL;
- nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't mount"));
- }
-
- /*
- if (vfs->writeblocks[0] == MP_OBJ_NULL) {
- printf("mounted read-only");
- } else {
- printf("mounted read-write");
- }
- DWORD nclst;
- FATFS *fatfs;
- f_getfree(vfs->str, &nclst, &fatfs);
- printf(" on %s with %u bytes free\n", vfs->str, (uint)(nclst * fatfs->csize * 512));
- */
- return vfs;
- }
-}
-
-STATIC mp_obj_t fatfs_mount(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
- fatfs_mount_mkfs(n_args, pos_args, kw_args, false);
- return mp_const_none;
-}
-MP_DEFINE_CONST_FUN_OBJ_KW(fsuser_mount_obj, 2, fatfs_mount);
-
-mp_obj_t fatfs_umount(mp_obj_t bdev_or_path_in) {
- size_t i = 0;
- if (MP_OBJ_IS_STR(bdev_or_path_in)) {
- mp_uint_t mnt_len;
- const char *mnt_str = mp_obj_str_get_data(bdev_or_path_in, &mnt_len);
- for (; i < MP_ARRAY_SIZE(MP_STATE_PORT(fs_user_mount)); ++i) {
- fs_user_mount_t *vfs = MP_STATE_PORT(fs_user_mount)[i];
- if (vfs != NULL && !memcmp(mnt_str, vfs->str, mnt_len + 1)) {
- break;
- }
- }
- } else {
- for (; i < MP_ARRAY_SIZE(MP_STATE_PORT(fs_user_mount)); ++i) {
- fs_user_mount_t *vfs = MP_STATE_PORT(fs_user_mount)[i];
- if (vfs != NULL && bdev_or_path_in == vfs->readblocks[1]) {
- break;
- }
- }
- }
-
- if (i == MP_ARRAY_SIZE(MP_STATE_PORT(fs_user_mount))) {
- mp_raise_OSError(MP_EINVAL);
- }
-
- fs_user_mount_t *vfs = MP_STATE_PORT(fs_user_mount)[i];
- FRESULT res = f_mount(NULL, vfs->str, 0);
- if (vfs->flags & FSUSER_FREE_OBJ) {
- m_del_obj(fs_user_mount_t, vfs);
- }
- MP_STATE_PORT(fs_user_mount)[i] = NULL;
- if (res != FR_OK) {
- nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't umount"));
- }
- return mp_const_none;
-}
-MP_DEFINE_CONST_FUN_OBJ_1(fsuser_umount_obj, fatfs_umount);
-
-STATIC mp_obj_t fatfs_mkfs(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
- fatfs_mount_mkfs(n_args, pos_args, kw_args, true);
- return mp_const_none;
-}
-MP_DEFINE_CONST_FUN_OBJ_KW(fsuser_mkfs_obj, 2, fatfs_mkfs);
-
-#endif // MICROPY_FSUSERMOUNT
diff --git a/extmod/machine_pulse.c b/extmod/machine_pulse.c
index b2a78d72e..5f837479d 100644
--- a/extmod/machine_pulse.c
+++ b/extmod/machine_pulse.c
@@ -34,7 +34,7 @@ mp_uint_t machine_time_pulse_us(mp_hal_pin_obj_t pin, int pulse_level, mp_uint_t
mp_uint_t start = mp_hal_ticks_us();
while (mp_hal_pin_read(pin) != pulse_level) {
if ((mp_uint_t)(mp_hal_ticks_us() - start) >= timeout_us) {
- return (mp_uint_t)-1;
+ return (mp_uint_t)-2;
}
}
start = mp_hal_ticks_us();
@@ -57,9 +57,7 @@ STATIC mp_obj_t machine_time_pulse_us_(size_t n_args, const mp_obj_t *args) {
timeout_us = mp_obj_get_int(args[2]);
}
mp_uint_t us = machine_time_pulse_us(pin, level, timeout_us);
- if (us == (mp_uint_t)-1) {
- mp_raise_OSError(MP_ETIMEDOUT);
- }
+ // May return -1 or -2 in case of timeout
return mp_obj_new_int(us);
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_time_pulse_us_obj, 2, 3, machine_time_pulse_us_);
diff --git a/extmod/machine_signal.c b/extmod/machine_signal.c
new file mode 100644
index 000000000..d08931296
--- /dev/null
+++ b/extmod/machine_signal.c
@@ -0,0 +1,183 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Paul Sokolovsky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "py/mpconfig.h"
+#if MICROPY_PY_MACHINE
+
+#include <string.h>
+
+#include "py/obj.h"
+#include "py/runtime.h"
+#include "extmod/virtpin.h"
+#include "extmod/machine_signal.h"
+
+// Signal class
+
+typedef struct _machine_signal_t {
+ mp_obj_base_t base;
+ mp_obj_t pin;
+ bool invert;
+} machine_signal_t;
+
+STATIC mp_obj_t signal_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+ mp_obj_t pin = args[0];
+ bool invert = false;
+
+ #if defined(MICROPY_PY_MACHINE_PIN_MAKE_NEW)
+ mp_pin_p_t *pin_p = NULL;
+
+ if (MP_OBJ_IS_OBJ(pin)) {
+ mp_obj_base_t *pin_base = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[0]);
+ pin_p = (mp_pin_p_t*)pin_base->type->protocol;
+ }
+
+ if (pin_p == NULL) {
+ // If first argument isn't a Pin-like object, we filter out "invert"
+ // from keyword arguments and pass them all to the exported Pin
+ // constructor to create one.
+ mp_obj_t pin_args[n_args + n_kw * 2];
+ memcpy(pin_args, args, n_args * sizeof(mp_obj_t));
+ const mp_obj_t *src = args + n_args;
+ mp_obj_t *dst = pin_args + n_args;
+ mp_obj_t *sig_value = NULL;
+ for (size_t cnt = n_kw; cnt; cnt--) {
+ if (*src == MP_OBJ_NEW_QSTR(MP_QSTR_invert)) {
+ invert = mp_obj_is_true(src[1]);
+ n_kw--;
+ } else {
+ *dst++ = *src;
+ *dst++ = src[1];
+ }
+ if (*src == MP_OBJ_NEW_QSTR(MP_QSTR_value)) {
+ // Value is pertained to Signal, so we should invert
+ // it for Pin if needed, and we should do it only when
+ // inversion status is guaranteedly known.
+ sig_value = dst - 1;
+ }
+ src += 2;
+ }
+
+ if (invert && sig_value != NULL) {
+ *sig_value = mp_obj_is_true(*sig_value) ? MP_OBJ_NEW_SMALL_INT(0) : MP_OBJ_NEW_SMALL_INT(1);
+ }
+
+ // Here we pass NULL as a type, hoping that mp_pin_make_new()
+ // will just ignore it as set a concrete type. If not, we'd need
+ // to expose port's "default" pin type too.
+ pin = MICROPY_PY_MACHINE_PIN_MAKE_NEW(NULL, n_args, n_kw, pin_args);
+ }
+ else
+ #endif
+ // Otherwise there should be 1 or 2 args
+ {
+ if (n_args == 1) {
+ if (n_kw == 0) {
+ } else if (n_kw == 1 && args[1] == MP_OBJ_NEW_QSTR(MP_QSTR_invert)) {
+ invert = mp_obj_is_true(args[1]);
+ } else {
+ goto error;
+ }
+ } else {
+ error:
+ mp_raise_TypeError(NULL);
+ }
+ }
+
+ machine_signal_t *o = m_new_obj(machine_signal_t);
+ o->base.type = type;
+ o->pin = pin;
+ o->invert = invert;
+ return MP_OBJ_FROM_PTR(o);
+}
+
+STATIC mp_uint_t signal_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) {
+ (void)errcode;
+ machine_signal_t *self = MP_OBJ_TO_PTR(self_in);
+
+ switch (request) {
+ case MP_PIN_READ: {
+ return mp_virtual_pin_read(self->pin) ^ self->invert;
+ }
+ case MP_PIN_WRITE: {
+ mp_virtual_pin_write(self->pin, arg ^ self->invert);
+ return 0;
+ }
+ }
+ return -1;
+}
+
+// fast method for getting/setting signal value
+STATIC mp_obj_t signal_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+ mp_arg_check_num(n_args, n_kw, 0, 1, false);
+ if (n_args == 0) {
+ // get pin
+ return MP_OBJ_NEW_SMALL_INT(mp_virtual_pin_read(self_in));
+ } else {
+ // set pin
+ mp_virtual_pin_write(self_in, mp_obj_is_true(args[0]));
+ return mp_const_none;
+ }
+}
+
+STATIC mp_obj_t signal_value(size_t n_args, const mp_obj_t *args) {
+ return signal_call(args[0], n_args - 1, 0, args + 1);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(signal_value_obj, 1, 2, signal_value);
+
+STATIC mp_obj_t signal_on(mp_obj_t self_in) {
+ mp_virtual_pin_write(self_in, 1);
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(signal_on_obj, signal_on);
+
+STATIC mp_obj_t signal_off(mp_obj_t self_in) {
+ mp_virtual_pin_write(self_in, 0);
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(signal_off_obj, signal_off);
+
+STATIC const mp_rom_map_elem_t signal_locals_dict_table[] = {
+ { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&signal_value_obj) },
+ { MP_ROM_QSTR(MP_QSTR_on), MP_ROM_PTR(&signal_on_obj) },
+ { MP_ROM_QSTR(MP_QSTR_off), MP_ROM_PTR(&signal_off_obj) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(signal_locals_dict, signal_locals_dict_table);
+
+STATIC const mp_pin_p_t signal_pin_p = {
+ .ioctl = signal_ioctl,
+};
+
+const mp_obj_type_t machine_signal_type = {
+ { &mp_type_type },
+ .name = MP_QSTR_Signal,
+ .make_new = signal_make_new,
+ .call = signal_call,
+ .protocol = &signal_pin_p,
+ .locals_dict = (void*)&signal_locals_dict,
+};
+
+#endif // MICROPY_PY_MACHINE
diff --git a/extmod/machine_signal.h b/extmod/machine_signal.h
new file mode 100644
index 000000000..7f88cbaa8
--- /dev/null
+++ b/extmod/machine_signal.h
@@ -0,0 +1,35 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Paul Sokolovsky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef __MICROPY_INCLUDED_EXTMOD_MACHINE_SIGNAL_H__
+#define __MICROPY_INCLUDED_EXTMOD_MACHINE_SIGNAL_H__
+
+#include "py/obj.h"
+
+extern const mp_obj_type_t machine_signal_type;
+
+#endif // __MICROPY_INCLUDED_EXTMOD_MACHINE_SIGNAL_H__
diff --git a/extmod/machine_spi.c b/extmod/machine_spi.c
index 6e7678498..a67d294ba 100644
--- a/extmod/machine_spi.c
+++ b/extmod/machine_spi.c
@@ -90,12 +90,6 @@ void mp_machine_soft_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint
if (dest != NULL) {
dest[i] = data_in;
}
-
- // Some ports need a regular callback, but probably we don't need
- // to do this every byte, or even at all.
- #ifdef MICROPY_EVENT_POLL_HOOK
- MICROPY_EVENT_POLL_HOOK;
- #endif
}
}
diff --git a/extmod/modbtree.c b/extmod/modbtree.c
index bb75845b1..127dd71a3 100644
--- a/extmod/modbtree.c
+++ b/extmod/modbtree.c
@@ -97,12 +97,8 @@ STATIC mp_obj_t btree_put(size_t n_args, const mp_obj_t *args) {
(void)n_args;
mp_obj_btree_t *self = MP_OBJ_TO_PTR(args[0]);
DBT key, val;
- // Different ports may have different type sizes
- mp_uint_t v;
- key.data = (void*)mp_obj_str_get_data(args[1], &v);
- key.size = v;
- val.data = (void*)mp_obj_str_get_data(args[2], &v);
- val.size = v;
+ key.data = (void*)mp_obj_str_get_data(args[1], &key.size);
+ val.data = (void*)mp_obj_str_get_data(args[2], &val.size);
return MP_OBJ_NEW_SMALL_INT(__bt_put(self->db, &key, &val, 0));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(btree_put_obj, 3, 4, btree_put);
@@ -110,10 +106,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(btree_put_obj, 3, 4, btree_put);
STATIC mp_obj_t btree_get(size_t n_args, const mp_obj_t *args) {
mp_obj_btree_t *self = MP_OBJ_TO_PTR(args[0]);
DBT key, val;
- // Different ports may have different type sizes
- mp_uint_t v;
- key.data = (void*)mp_obj_str_get_data(args[1], &v);
- key.size = v;
+ key.data = (void*)mp_obj_str_get_data(args[1], &key.size);
int res = __bt_get(self->db, &key, &val, 0);
if (res == RET_SPECIAL) {
if (n_args > 2) {
@@ -132,10 +125,7 @@ STATIC mp_obj_t btree_seq(size_t n_args, const mp_obj_t *args) {
int flags = MP_OBJ_SMALL_INT_VALUE(args[1]);
DBT key, val;
if (n_args > 2) {
- // Different ports may have different type sizes
- mp_uint_t v;
- key.data = (void*)mp_obj_str_get_data(args[2], &v);
- key.size = v;
+ key.data = (void*)mp_obj_str_get_data(args[2], &key.size);
}
int res = __bt_seq(self->db, &key, &val, flags);
@@ -184,7 +174,8 @@ STATIC mp_obj_t btree_items(size_t n_args, const mp_obj_t *args) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(btree_items_obj, 1, 4, btree_items);
-STATIC mp_obj_t btree_getiter(mp_obj_t self_in) {
+STATIC mp_obj_t btree_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) {
+ (void)iter_buf;
mp_obj_btree_t *self = MP_OBJ_TO_PTR(self_in);
if (self->next_flags != 0) {
// If we're called immediately after keys(), values(), or items(),
@@ -205,14 +196,11 @@ STATIC mp_obj_t btree_iternext(mp_obj_t self_in) {
mp_obj_btree_t *self = MP_OBJ_TO_PTR(self_in);
DBT key, val;
int res;
- // Different ports may have different type sizes
- mp_uint_t v;
bool desc = self->flags & FLAG_DESC;
if (self->start_key != MP_OBJ_NULL) {
int flags = R_FIRST;
if (self->start_key != mp_const_none) {
- key.data = (void*)mp_obj_str_get_data(self->start_key, &v);
- key.size = v;
+ key.data = (void*)mp_obj_str_get_data(self->start_key, &key.size);
flags = R_CURSOR;
} else if (desc) {
flags = R_LAST;
@@ -230,8 +218,7 @@ STATIC mp_obj_t btree_iternext(mp_obj_t self_in) {
if (self->end_key != mp_const_none) {
DBT end_key;
- end_key.data = (void*)mp_obj_str_get_data(self->end_key, &v);
- end_key.size = v;
+ end_key.data = (void*)mp_obj_str_get_data(self->end_key, &end_key.size);
BTREE *t = self->db->internal;
int cmp = t->bt_cmp(&key, &end_key);
if (desc) {
@@ -263,13 +250,10 @@ STATIC mp_obj_t btree_iternext(mp_obj_t self_in) {
STATIC mp_obj_t btree_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) {
mp_obj_btree_t *self = MP_OBJ_TO_PTR(self_in);
- // Different ports may have different type sizes
- mp_uint_t v;
if (value == MP_OBJ_NULL) {
// delete
DBT key;
- key.data = (void*)mp_obj_str_get_data(index, &v);
- key.size = v;
+ key.data = (void*)mp_obj_str_get_data(index, &key.size);
int res = __bt_delete(self->db, &key, 0);
if (res == RET_SPECIAL) {
nlr_raise(mp_obj_new_exception(&mp_type_KeyError));
@@ -279,8 +263,7 @@ STATIC mp_obj_t btree_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) {
} else if (value == MP_OBJ_SENTINEL) {
// load
DBT key, val;
- key.data = (void*)mp_obj_str_get_data(index, &v);
- key.size = v;
+ key.data = (void*)mp_obj_str_get_data(index, &key.size);
int res = __bt_get(self->db, &key, &val, 0);
if (res == RET_SPECIAL) {
nlr_raise(mp_obj_new_exception(&mp_type_KeyError));
@@ -290,10 +273,8 @@ STATIC mp_obj_t btree_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) {
} else {
// store
DBT key, val;
- key.data = (void*)mp_obj_str_get_data(index, &v);
- key.size = v;
- val.data = (void*)mp_obj_str_get_data(value, &v);
- val.size = v;
+ key.data = (void*)mp_obj_str_get_data(index, &key.size);
+ val.data = (void*)mp_obj_str_get_data(value, &val.size);
int res = __bt_put(self->db, &key, &val, 0);
CHECK_ERROR(res);
return mp_const_none;
@@ -304,10 +285,8 @@ STATIC mp_obj_t btree_binary_op(mp_uint_t op, mp_obj_t lhs_in, mp_obj_t rhs_in)
mp_obj_btree_t *self = MP_OBJ_TO_PTR(lhs_in);
switch (op) {
case MP_BINARY_OP_IN: {
- mp_uint_t v;
DBT key, val;
- key.data = (void*)mp_obj_str_get_data(rhs_in, &v);
- key.size = v;
+ key.data = (void*)mp_obj_str_get_data(rhs_in, &key.size);
int res = __bt_get(self->db, &key, &val, 0);
CHECK_ERROR(res);
return mp_obj_new_bool(res != RET_SPECIAL);
diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c
index 22841102f..a07392675 100644
--- a/extmod/modframebuf.c
+++ b/extmod/modframebuf.c
@@ -53,6 +53,41 @@ typedef struct _mp_framebuf_p_t {
fill_rect_t fill_rect;
} mp_framebuf_p_t;
+// constants for formats
+#define FRAMEBUF_MVLSB (0)
+#define FRAMEBUF_RGB565 (1)
+#define FRAMEBUF_GS4_HMSB (2)
+#define FRAMEBUF_MHLSB (3)
+#define FRAMEBUF_MHMSB (4)
+
+// Functions for MHLSB and MHMSB
+
+STATIC void mono_horiz_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t color) {
+ size_t index = (x + y * fb->stride) >> 3;
+ int offset = fb->format == FRAMEBUF_MHMSB ? x & 0x07 : 7 - (x & 0x07);
+ ((uint8_t*)fb->buf)[index] = (((uint8_t*)fb->buf)[index] & ~(0x01 << offset)) | ((color != 0) << offset);
+}
+
+STATIC uint32_t mono_horiz_getpixel(const mp_obj_framebuf_t *fb, int x, int y) {
+ size_t index = (x + y * fb->stride) >> 3;
+ int offset = fb->format == FRAMEBUF_MHMSB ? x & 0x07 : 7 - (x & 0x07);
+ return (((uint8_t*)fb->buf)[index] >> (offset)) & 0x01;
+}
+
+STATIC void mono_horiz_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) {
+ int reverse = fb->format == FRAMEBUF_MHMSB;
+ int advance = fb->stride >> 3;
+ while (w--) {
+ uint8_t *b = &((uint8_t*)fb->buf)[(x >> 3) + y * advance];
+ int offset = reverse ? x & 7 : 7 - (x & 7);
+ for (int hh = h; hh; --hh) {
+ *b = (*b & ~(0x01 << offset)) | ((col != 0) << offset);
+ b += advance;
+ }
+ ++x;
+ }
+}
+
// Functions for MVLSB format
STATIC void mvlsb_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t color) {
@@ -97,13 +132,63 @@ STATIC void rgb565_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, i
}
}
-// constants for formats
-#define FRAMEBUF_MVLSB (0)
-#define FRAMEBUF_RGB565 (1)
+// Functions for GS4_HMSB format
+
+STATIC void gs4_hmsb_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) {
+ uint8_t *pixel = &((uint8_t*)fb->buf)[(x + y * fb->stride) >> 1];
+
+ if (x % 2) {
+ *pixel = ((uint8_t)col & 0x0f) | (*pixel & 0xf0);
+ } else {
+ *pixel = ((uint8_t)col << 4) | (*pixel & 0x0f);
+ }
+}
+
+STATIC uint32_t gs4_hmsb_getpixel(const mp_obj_framebuf_t *fb, int x, int y) {
+ if (x % 2) {
+ return ((uint8_t*)fb->buf)[(x + y * fb->stride) >> 1] & 0x0f;
+ }
+
+ return ((uint8_t*)fb->buf)[(x + y * fb->stride) >> 1] >> 4;
+}
+
+STATIC void gs4_hmsb_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) {
+ col &= 0x0f;
+ uint8_t *pixel_pair = &((uint8_t*)fb->buf)[(x + y * fb->stride) >> 1];
+ uint8_t col_shifted_left = col << 4;
+ uint8_t colored_pixel_pair = col_shifted_left | col;
+ int pixel_count_till_next_line = (fb->stride - w) >> 1;
+ bool odd_x = (x % 2 == 1);
+
+ while (h--) {
+ int ww = w;
+
+ if (odd_x && ww > 0) {
+ *pixel_pair = (*pixel_pair & 0xf0) | col;
+ pixel_pair++;
+ ww--;
+ }
+
+ memset(pixel_pair, colored_pixel_pair, ww >> 1);
+ pixel_pair += ww >> 1;
+
+ if (ww % 2) {
+ *pixel_pair = col_shifted_left | (*pixel_pair & 0x0f);
+ if (!odd_x) {
+ pixel_pair++;
+ }
+ }
+
+ pixel_pair += pixel_count_till_next_line;
+ }
+}
STATIC mp_framebuf_p_t formats[] = {
[FRAMEBUF_MVLSB] = {mvlsb_setpixel, mvlsb_getpixel, mvlsb_fill_rect},
[FRAMEBUF_RGB565] = {rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect},
+ [FRAMEBUF_GS4_HMSB] = {gs4_hmsb_setpixel, gs4_hmsb_getpixel, gs4_hmsb_fill_rect},
+ [FRAMEBUF_MHLSB] = {mono_horiz_setpixel, mono_horiz_getpixel, mono_horiz_fill_rect},
+ [FRAMEBUF_MHMSB] = {mono_horiz_setpixel, mono_horiz_getpixel, mono_horiz_fill_rect},
};
static inline void setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t color) {
@@ -115,7 +200,7 @@ static inline uint32_t getpixel(const mp_obj_framebuf_t *fb, int x, int y) {
}
STATIC void fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) {
- if (x + w <= 0 || y + h <= 0 || y >= fb->height || x >= fb->width) {
+ if (h < 1 || w < 1 || x + w <= 0 || y + h <= 0 || y >= fb->height || x >= fb->width) {
// No operation needed.
return;
}
@@ -152,6 +237,11 @@ STATIC mp_obj_t framebuf_make_new(const mp_obj_type_t *type, size_t n_args, size
switch (o->format) {
case FRAMEBUF_MVLSB:
case FRAMEBUF_RGB565:
+ case FRAMEBUF_GS4_HMSB:
+ break;
+ case FRAMEBUF_MHLSB:
+ case FRAMEBUF_MHMSB:
+ o->stride = (o->stride + 7) & ~7;
break;
default:
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError,
@@ -302,9 +392,13 @@ STATIC mp_obj_t framebuf_line(size_t n_args, const mp_obj_t *args) {
mp_int_t e = 2 * dy - dx;
for (mp_int_t i = 0; i < dx; ++i) {
if (steep) {
- setpixel(self, y1, x1, col);
+ if (0 <= y1 && y1 < self->width && 0 <= x1 && x1 < self->height) {
+ setpixel(self, y1, x1, col);
+ }
} else {
- setpixel(self, x1, y1, col);
+ if (0 <= x1 && x1 < self->width && 0 <= y1 && y1 < self->height) {
+ setpixel(self, x1, y1, col);
+ }
}
while (e >= 0) {
y1 += sy;
@@ -314,7 +408,9 @@ STATIC mp_obj_t framebuf_line(size_t n_args, const mp_obj_t *args) {
e += 2 * dy;
}
- setpixel(self, x2, y2, col);
+ if (0 <= x2 && x2 < self->width && 0 <= y2 && y2 < self->height) {
+ setpixel(self, x2, y2, col);
+ }
return mp_const_none;
}
@@ -353,7 +449,7 @@ STATIC mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args) {
int cx1 = x1;
for (int cx0 = x0; cx0 < x0end; ++cx0) {
color = getpixel(source, cx1, y1);
- if (key == -1 || color != (uint32_t)key) {
+ if (color != (uint32_t)key) {
setpixel(self, cx0, y0, color);
}
++cx1;
@@ -483,7 +579,11 @@ STATIC const mp_rom_map_elem_t framebuf_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_FrameBuffer), MP_ROM_PTR(&mp_type_framebuf) },
{ MP_ROM_QSTR(MP_QSTR_FrameBuffer1), MP_ROM_PTR(&legacy_framebuffer1_obj) },
{ MP_ROM_QSTR(MP_QSTR_MVLSB), MP_OBJ_NEW_SMALL_INT(FRAMEBUF_MVLSB) },
+ { MP_ROM_QSTR(MP_QSTR_MONO_VLSB), MP_OBJ_NEW_SMALL_INT(FRAMEBUF_MVLSB) },
{ MP_ROM_QSTR(MP_QSTR_RGB565), MP_OBJ_NEW_SMALL_INT(FRAMEBUF_RGB565) },
+ { MP_ROM_QSTR(MP_QSTR_GS4_HMSB), MP_OBJ_NEW_SMALL_INT(FRAMEBUF_GS4_HMSB) },
+ { MP_ROM_QSTR(MP_QSTR_MONO_HLSB), MP_OBJ_NEW_SMALL_INT(FRAMEBUF_MHLSB) },
+ { MP_ROM_QSTR(MP_QSTR_MONO_HMSB), MP_OBJ_NEW_SMALL_INT(FRAMEBUF_MHMSB) },
};
STATIC MP_DEFINE_CONST_DICT(framebuf_module_globals, framebuf_module_globals_table);
diff --git a/extmod/modlwip.c b/extmod/modlwip.c
index 9194fa5e4..79b750979 100644
--- a/extmod/modlwip.c
+++ b/extmod/modlwip.c
@@ -1,10 +1,11 @@
/*
- * This file is part of the Micro Python project, http://micropython.org/
+ * This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2013, 2014 Damien P. George
* Copyright (c) 2015 Galen Hazelwood
+ * Copyright (c) 2015-2016 Paul Sokolovsky
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -35,7 +36,7 @@
#include "py/mperrno.h"
#include "py/mphal.h"
-#include "netutils.h"
+#include "lib/netutils/netutils.h"
#include "lwip/init.h"
#include "lwip/timers.h"
@@ -372,7 +373,7 @@ STATIC err_t _lwip_tcp_recv(void *arg, struct tcp_pcb *tcpb, struct pbuf *p, err
}
/*******************************************************************************/
-// Functions for socket send/recieve operations. Socket send/recv and friends call
+// Functions for socket send/receive operations. Socket send/recv and friends call
// these to do the work.
// Helper function for send/sendto to handle UDP packets.
@@ -731,7 +732,9 @@ STATIC mp_obj_t lwip_socket_accept(mp_obj_t self_in) {
// accept incoming connection
if (socket->incoming.connection == NULL) {
- if (socket->timeout != -1) {
+ if (socket->timeout == 0) {
+ mp_raise_OSError(MP_EAGAIN);
+ } else if (socket->timeout != -1) {
for (mp_uint_t retries = socket->timeout / 100; retries--;) {
mp_hal_delay_ms(100);
if (socket->incoming.connection != NULL) break;
@@ -799,12 +802,12 @@ STATIC mp_obj_t lwip_socket_connect(mp_obj_t self_in, mp_obj_t addr_in) {
case MOD_NETWORK_SOCK_STREAM: {
if (socket->state != STATE_NEW) {
if (socket->state == STATE_CONNECTED) {
- mp_raise_OSError(MP_EALREADY);
+ mp_raise_OSError(MP_EISCONN);
} else {
- mp_raise_OSError(MP_EINPROGRESS);
+ mp_raise_OSError(MP_EALREADY);
}
}
- // Register our recieve callback.
+ // Register our receive callback.
tcp_recv(socket->pcb.tcp, _lwip_tcp_recv);
socket->state = STATE_CONNECTING;
err = tcp_connect(socket->pcb.tcp, &dest, port, _lwip_tcp_connected);
@@ -821,7 +824,7 @@ STATIC mp_obj_t lwip_socket_connect(mp_obj_t self_in, mp_obj_t addr_in) {
if (socket->state != STATE_CONNECTING) break;
}
if (socket->state == STATE_CONNECTING) {
- mp_raise_OSError(MP_ETIMEDOUT);
+ mp_raise_OSError(MP_EINPROGRESS);
}
} else {
while (socket->state == STATE_CONNECTING) {
@@ -1142,8 +1145,11 @@ STATIC mp_uint_t lwip_socket_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_
ret |= MP_STREAM_POLL_WR;
}
- if (flags & MP_STREAM_POLL_HUP && socket->state == STATE_PEER_CLOSED) {
- ret |= MP_STREAM_POLL_HUP;
+ if (socket->state == STATE_PEER_CLOSED) {
+ // Peer-closed socket is both readable and writable: read will
+ // return EOF, write - error. Without this poll will hang on a
+ // socket which was closed by peer.
+ ret |= flags & (MP_STREAM_POLL_RD | MP_STREAM_POLL_WR);
}
} else {
@@ -1172,6 +1178,7 @@ STATIC const mp_map_elem_t lwip_socket_locals_dict_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR_makefile), (mp_obj_t)&lwip_socket_makefile_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_read), (mp_obj_t)&mp_stream_read_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_readinto), (mp_obj_t)&mp_stream_readinto_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_readline), (mp_obj_t)&mp_stream_unbuffered_readline_obj},
{ MP_OBJ_NEW_QSTR(MP_QSTR_write), (mp_obj_t)&mp_stream_write_obj },
};
@@ -1261,9 +1268,13 @@ STATIC void lwip_getaddrinfo_cb(const char *name, ip_addr_t *ipaddr, void *arg)
}
// lwip.getaddrinfo
-STATIC mp_obj_t lwip_getaddrinfo(mp_obj_t host_in, mp_obj_t port_in) {
- mp_uint_t hlen;
- const char *host = mp_obj_str_get_data(host_in, &hlen);
+STATIC mp_obj_t lwip_getaddrinfo(size_t n_args, const mp_obj_t *args) {
+ if (n_args > 2) {
+ mp_warning("getaddrinfo constraints not supported");
+ }
+
+ mp_obj_t host_in = args[0], port_in = args[1];
+ const char *host = mp_obj_str_get_str(host_in);
mp_int_t port = mp_obj_get_int(port_in);
getaddrinfo_state_t state;
@@ -1298,7 +1309,7 @@ STATIC mp_obj_t lwip_getaddrinfo(mp_obj_t host_in, mp_obj_t port_in) {
tuple->items[4] = netutils_format_inet_addr((uint8_t*)&state.ipaddr, port, NETUTILS_BIG);
return mp_obj_new_list(1, (mp_obj_t*)&tuple);
}
-STATIC MP_DEFINE_CONST_FUN_OBJ_2(lwip_getaddrinfo_obj, lwip_getaddrinfo);
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(lwip_getaddrinfo_obj, 2, 6, lwip_getaddrinfo);
// Debug functions
diff --git a/extmod/moductypes.c b/extmod/moductypes.c
index 44b36561f..641c1de64 100644
--- a/extmod/moductypes.c
+++ b/extmod/moductypes.c
@@ -360,7 +360,7 @@ STATIC void set_aligned(uint val_type, void *p, mp_int_t index, mp_obj_t val) {
return;
}
#endif
- mp_int_t v = mp_obj_get_int(val);
+ mp_int_t v = mp_obj_get_int_truncated(val);
switch (val_type) {
case UINT8:
((uint8_t*)p)[index] = (uint8_t)v; return;
diff --git a/extmod/modujson.c b/extmod/modujson.c
index ca4e6df10..bb2d45274 100644
--- a/extmod/modujson.c
+++ b/extmod/modujson.c
@@ -274,7 +274,7 @@ STATIC mp_obj_t mod_ujson_load(mp_obj_t stream_obj) {
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_ujson_load_obj, mod_ujson_load);
STATIC mp_obj_t mod_ujson_loads(mp_obj_t obj) {
- mp_uint_t len;
+ size_t len;
const char *buf = mp_obj_str_get_data(obj, &len);
vstr_t vstr = {len, len, (char*)buf, true};
mp_obj_stringio_t sio = {{&mp_type_stringio}, &vstr, 0};
diff --git a/extmod/modurandom.c b/extmod/modurandom.c
index 12e56741e..4b63dace4 100644
--- a/extmod/modurandom.c
+++ b/extmod/modurandom.c
@@ -74,7 +74,7 @@ STATIC uint32_t yasmarang_randbelow(uint32_t n) {
STATIC mp_obj_t mod_urandom_getrandbits(mp_obj_t num_in) {
int n = mp_obj_get_int(num_in);
if (n > 32 || n == 0) {
- nlr_raise(mp_obj_new_exception(&mp_type_ValueError));
+ mp_raise_ValueError(NULL);
}
uint32_t mask = ~0;
// Beware of C undefined behavior when shifting by >= than bit size
@@ -102,7 +102,7 @@ STATIC mp_obj_t mod_urandom_randrange(size_t n_args, const mp_obj_t *args) {
if (start > 0) {
return mp_obj_new_int(yasmarang_randbelow(start));
} else {
- nlr_raise(mp_obj_new_exception(&mp_type_ValueError));
+ goto error;
}
} else {
mp_int_t stop = mp_obj_get_int(args[1]);
@@ -111,7 +111,7 @@ STATIC mp_obj_t mod_urandom_randrange(size_t n_args, const mp_obj_t *args) {
if (start < stop) {
return mp_obj_new_int(start + yasmarang_randbelow(stop - start));
} else {
- nlr_raise(mp_obj_new_exception(&mp_type_ValueError));
+ goto error;
}
} else {
// range(start, stop, step)
@@ -122,15 +122,18 @@ STATIC mp_obj_t mod_urandom_randrange(size_t n_args, const mp_obj_t *args) {
} else if (step < 0) {
n = (stop - start + step + 1) / step;
} else {
- nlr_raise(mp_obj_new_exception(&mp_type_ValueError));
+ goto error;
}
if (n > 0) {
return mp_obj_new_int(start + step * yasmarang_randbelow(n));
} else {
- nlr_raise(mp_obj_new_exception(&mp_type_ValueError));
+ goto error;
}
}
}
+
+error:
+ mp_raise_ValueError(NULL);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_urandom_randrange_obj, 1, 3, mod_urandom_randrange);
@@ -140,7 +143,7 @@ STATIC mp_obj_t mod_urandom_randint(mp_obj_t a_in, mp_obj_t b_in) {
if (a <= b) {
return mp_obj_new_int(a + yasmarang_randbelow(b - a + 1));
} else {
- nlr_raise(mp_obj_new_exception(&mp_type_ValueError));
+ mp_raise_ValueError(NULL);
}
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_urandom_randint_obj, mod_urandom_randint);
diff --git a/extmod/modure.c b/extmod/modure.c
index 6cd700763..817cf0ee9 100644
--- a/extmod/modure.c
+++ b/extmod/modure.c
@@ -96,7 +96,7 @@ STATIC mp_obj_t ure_exec(bool is_anchored, uint n_args, const mp_obj_t *args) {
(void)n_args;
mp_obj_re_t *self = MP_OBJ_TO_PTR(args[0]);
Subject subj;
- mp_uint_t len;
+ size_t len;
subj.begin = mp_obj_str_get_data(args[1], &len);
subj.end = subj.begin + len;
int caps_num = (self->re.sub + 1) * 2;
@@ -128,7 +128,7 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(re_search_obj, 2, 4, re_search);
STATIC mp_obj_t re_split(size_t n_args, const mp_obj_t *args) {
mp_obj_re_t *self = MP_OBJ_TO_PTR(args[0]);
Subject subj;
- mp_uint_t len;
+ size_t len;
subj.begin = mp_obj_str_get_data(args[1], &len);
subj.end = subj.begin + len;
int caps_num = (self->re.sub + 1) * 2;
diff --git a/extmod/moduselect.c b/extmod/moduselect.c
index 5cc72c69c..f51ee4f51 100644
--- a/extmod/moduselect.c
+++ b/extmod/moduselect.c
@@ -114,7 +114,7 @@ STATIC mp_uint_t poll_map_poll(mp_map_t *poll_map, mp_uint_t *rwx_num) {
/// \function select(rlist, wlist, xlist[, timeout])
STATIC mp_obj_t select_select(uint n_args, const mp_obj_t *args) {
// get array data from tuple/list arguments
- mp_uint_t rwx_len[3];
+ size_t rwx_len[3];
mp_obj_t *r_array, *w_array, *x_array;
mp_obj_get_array(args[0], &rwx_len[0], &r_array);
mp_obj_get_array(args[1], &rwx_len[1], &w_array);
@@ -183,6 +183,11 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_select_select_obj, 3, 4, select_select);
typedef struct _mp_obj_poll_t {
mp_obj_base_t base;
mp_map_t poll_map;
+ short iter_cnt;
+ short iter_idx;
+ int flags;
+ // callee-owned tuple
+ mp_obj_t ret_tuple;
} mp_obj_poll_t;
/// \method register(obj[, eventmask])
@@ -220,9 +225,7 @@ STATIC mp_obj_t poll_modify(mp_obj_t self_in, mp_obj_t obj_in, mp_obj_t eventmas
}
MP_DEFINE_CONST_FUN_OBJ_3(poll_modify_obj, poll_modify);
-/// \method poll([timeout])
-/// Timeout is in milliseconds.
-STATIC mp_obj_t poll_poll(uint n_args, const mp_obj_t *args) {
+STATIC mp_uint_t poll_poll_internal(uint n_args, const mp_obj_t *args) {
mp_obj_poll_t *self = args[0];
// work out timeout (its given already in ms)
@@ -240,48 +243,109 @@ STATIC mp_obj_t poll_poll(uint n_args, const mp_obj_t *args) {
}
}
+ self->flags = flags;
+
mp_uint_t start_tick = mp_hal_ticks_ms();
+ mp_uint_t n_ready;
for (;;) {
// poll the objects
- mp_uint_t n_ready = poll_map_poll(&self->poll_map, NULL);
-
+ n_ready = poll_map_poll(&self->poll_map, NULL);
if (n_ready > 0 || (timeout != -1 && mp_hal_ticks_ms() - start_tick >= timeout)) {
- // one or more objects are ready, or we had a timeout
- mp_obj_list_t *ret_list = mp_obj_new_list(n_ready, NULL);
- n_ready = 0;
- for (mp_uint_t i = 0; i < self->poll_map.alloc; ++i) {
- if (!MP_MAP_SLOT_IS_FILLED(&self->poll_map, i)) {
- continue;
- }
- poll_obj_t *poll_obj = (poll_obj_t*)self->poll_map.table[i].value;
- if (poll_obj->flags_ret != 0) {
- mp_obj_t tuple[2] = {poll_obj->obj, MP_OBJ_NEW_SMALL_INT(poll_obj->flags_ret)};
- ret_list->items[n_ready++] = mp_obj_new_tuple(2, tuple);
- if (flags & FLAG_ONESHOT) {
- // Don't poll next time, until new event flags will be set explicitly
- poll_obj->flags = 0;
- }
- }
- }
- return ret_list;
+ break;
}
MICROPY_EVENT_POLL_HOOK
}
+
+ return n_ready;
+}
+
+STATIC mp_obj_t poll_poll(uint n_args, const mp_obj_t *args) {
+ mp_obj_poll_t *self = args[0];
+ mp_uint_t n_ready = poll_poll_internal(n_args, args);
+
+ // one or more objects are ready, or we had a timeout
+ mp_obj_list_t *ret_list = mp_obj_new_list(n_ready, NULL);
+ n_ready = 0;
+ for (mp_uint_t i = 0; i < self->poll_map.alloc; ++i) {
+ if (!MP_MAP_SLOT_IS_FILLED(&self->poll_map, i)) {
+ continue;
+ }
+ poll_obj_t *poll_obj = (poll_obj_t*)self->poll_map.table[i].value;
+ if (poll_obj->flags_ret != 0) {
+ mp_obj_t tuple[2] = {poll_obj->obj, MP_OBJ_NEW_SMALL_INT(poll_obj->flags_ret)};
+ ret_list->items[n_ready++] = mp_obj_new_tuple(2, tuple);
+ if (self->flags & FLAG_ONESHOT) {
+ // Don't poll next time, until new event flags will be set explicitly
+ poll_obj->flags = 0;
+ }
+ }
+ }
+ return ret_list;
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_poll_obj, 1, 3, poll_poll);
-STATIC const mp_map_elem_t poll_locals_dict_table[] = {
- { MP_OBJ_NEW_QSTR(MP_QSTR_register), (mp_obj_t)&poll_register_obj },
- { MP_OBJ_NEW_QSTR(MP_QSTR_unregister), (mp_obj_t)&poll_unregister_obj },
- { MP_OBJ_NEW_QSTR(MP_QSTR_modify), (mp_obj_t)&poll_modify_obj },
- { MP_OBJ_NEW_QSTR(MP_QSTR_poll), (mp_obj_t)&poll_poll_obj },
+STATIC mp_obj_t poll_ipoll(size_t n_args, const mp_obj_t *args) {
+ mp_obj_poll_t *self = MP_OBJ_TO_PTR(args[0]);
+
+ if (self->ret_tuple == MP_OBJ_NULL) {
+ self->ret_tuple = mp_obj_new_tuple(2, NULL);
+ }
+
+ int n_ready = poll_poll_internal(n_args, args);
+ self->iter_cnt = n_ready;
+ self->iter_idx = 0;
+
+ return args[0];
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_ipoll_obj, 1, 3, poll_ipoll);
+
+STATIC mp_obj_t poll_iternext(mp_obj_t self_in) {
+ mp_obj_poll_t *self = MP_OBJ_TO_PTR(self_in);
+
+ if (self->iter_cnt == 0) {
+ return MP_OBJ_STOP_ITERATION;
+ }
+
+ self->iter_cnt--;
+
+ for (mp_uint_t i = self->iter_idx; i < self->poll_map.alloc; ++i) {
+ self->iter_idx++;
+ if (!MP_MAP_SLOT_IS_FILLED(&self->poll_map, i)) {
+ continue;
+ }
+ poll_obj_t *poll_obj = (poll_obj_t*)self->poll_map.table[i].value;
+ if (poll_obj->flags_ret != 0) {
+ mp_obj_tuple_t *t = MP_OBJ_TO_PTR(self->ret_tuple);
+ t->items[0] = poll_obj->obj;
+ t->items[1] = MP_OBJ_NEW_SMALL_INT(poll_obj->flags_ret);
+ if (self->flags & FLAG_ONESHOT) {
+ // Don't poll next time, until new event flags will be set explicitly
+ poll_obj->flags = 0;
+ }
+ return MP_OBJ_FROM_PTR(t);
+ }
+ }
+
+ assert(!"inconsistent number of poll active entries");
+ self->iter_cnt = 0;
+ return MP_OBJ_STOP_ITERATION;
+}
+
+STATIC const mp_rom_map_elem_t poll_locals_dict_table[] = {
+ { MP_ROM_QSTR(MP_QSTR_register), MP_ROM_PTR(&poll_register_obj) },
+ { MP_ROM_QSTR(MP_QSTR_unregister), MP_ROM_PTR(&poll_unregister_obj) },
+ { MP_ROM_QSTR(MP_QSTR_modify), MP_ROM_PTR(&poll_modify_obj) },
+ { MP_ROM_QSTR(MP_QSTR_poll), MP_ROM_PTR(&poll_poll_obj) },
+ { MP_ROM_QSTR(MP_QSTR_ipoll), MP_ROM_PTR(&poll_ipoll_obj) },
};
STATIC MP_DEFINE_CONST_DICT(poll_locals_dict, poll_locals_dict_table);
STATIC const mp_obj_type_t mp_type_poll = {
{ &mp_type_type },
.name = MP_QSTR_poll,
- .locals_dict = (mp_obj_t)&poll_locals_dict,
+ .getiter = mp_identity_getiter,
+ .iternext = poll_iternext,
+ .locals_dict = (void*)&poll_locals_dict,
};
/// \function poll()
@@ -289,18 +353,20 @@ STATIC mp_obj_t select_poll(void) {
mp_obj_poll_t *poll = m_new_obj(mp_obj_poll_t);
poll->base.type = &mp_type_poll;
mp_map_init(&poll->poll_map, 0);
+ poll->iter_cnt = 0;
+ poll->ret_tuple = MP_OBJ_NULL;
return poll;
}
MP_DEFINE_CONST_FUN_OBJ_0(mp_select_poll_obj, select_poll);
-STATIC const mp_map_elem_t mp_module_select_globals_table[] = {
- { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_uselect) },
- { MP_OBJ_NEW_QSTR(MP_QSTR_select), (mp_obj_t)&mp_select_select_obj },
- { MP_OBJ_NEW_QSTR(MP_QSTR_poll), (mp_obj_t)&mp_select_poll_obj },
- { MP_OBJ_NEW_QSTR(MP_QSTR_POLLIN), MP_OBJ_NEW_SMALL_INT(MP_STREAM_POLL_RD) },
- { MP_OBJ_NEW_QSTR(MP_QSTR_POLLOUT), MP_OBJ_NEW_SMALL_INT(MP_STREAM_POLL_WR) },
- { MP_OBJ_NEW_QSTR(MP_QSTR_POLLERR), MP_OBJ_NEW_SMALL_INT(MP_STREAM_POLL_ERR) },
- { MP_OBJ_NEW_QSTR(MP_QSTR_POLLHUP), MP_OBJ_NEW_SMALL_INT(MP_STREAM_POLL_HUP) },
+STATIC const mp_rom_map_elem_t mp_module_select_globals_table[] = {
+ { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uselect) },
+ { MP_ROM_QSTR(MP_QSTR_select), MP_ROM_PTR(&mp_select_select_obj) },
+ { MP_ROM_QSTR(MP_QSTR_poll), MP_ROM_PTR(&mp_select_poll_obj) },
+ { MP_ROM_QSTR(MP_QSTR_POLLIN), MP_OBJ_NEW_SMALL_INT(MP_STREAM_POLL_RD) },
+ { MP_ROM_QSTR(MP_QSTR_POLLOUT), MP_OBJ_NEW_SMALL_INT(MP_STREAM_POLL_WR) },
+ { MP_ROM_QSTR(MP_QSTR_POLLERR), MP_OBJ_NEW_SMALL_INT(MP_STREAM_POLL_ERR) },
+ { MP_ROM_QSTR(MP_QSTR_POLLHUP), MP_OBJ_NEW_SMALL_INT(MP_STREAM_POLL_HUP) },
};
STATIC MP_DEFINE_CONST_DICT(mp_module_select_globals, mp_module_select_globals_table);
diff --git a/extmod/modussl_mbedtls.c b/extmod/modussl_mbedtls.c
index 291d4bb0c..35b470a83 100644
--- a/extmod/modussl_mbedtls.c
+++ b/extmod/modussl_mbedtls.c
@@ -156,13 +156,13 @@ STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) {
mbedtls_ssl_set_bio(&o->ssl, &o->sock, _mbedtls_ssl_send, _mbedtls_ssl_recv, NULL);
if (args->key.u_obj != MP_OBJ_NULL) {
- mp_uint_t key_len;
+ size_t key_len;
const byte *key = (const byte*)mp_obj_str_get_data(args->key.u_obj, &key_len);
// len should include terminating null
ret = mbedtls_pk_parse_key(&o->pkey, key, key_len + 1, NULL, 0);
assert(ret == 0);
- mp_uint_t cert_len;
+ size_t cert_len;
const byte *cert = (const byte*)mp_obj_str_get_data(args->cert.u_obj, &cert_len);
// len should include terminating null
ret = mbedtls_x509_crt_parse(&o->cert, cert, cert_len + 1);
diff --git a/extmod/modutimeq.c b/extmod/modutimeq.c
index 26ccb5182..e54415b28 100644
--- a/extmod/modutimeq.c
+++ b/extmod/modutimeq.c
@@ -4,7 +4,7 @@
* The MIT License (MIT)
*
* Copyright (c) 2014 Damien P. George
- * Copyright (c) 2016 Paul Sokolovsky
+ * Copyright (c) 2016-2017 Paul Sokolovsky
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -43,6 +43,7 @@
struct qentry {
mp_uint_t time;
+ mp_uint_t id;
mp_obj_t callback;
mp_obj_t args;
};
@@ -54,6 +55,7 @@ typedef struct _mp_obj_utimeq_t {
struct qentry items[];
} mp_obj_utimeq_t;
+STATIC mp_uint_t utimeq_id;
STATIC mp_obj_utimeq_t *get_heap(mp_obj_t heap_in) {
return MP_OBJ_TO_PTR(heap_in);
@@ -63,6 +65,11 @@ STATIC bool time_less_than(struct qentry *item, struct qentry *parent) {
mp_uint_t item_tm = item->time;
mp_uint_t parent_tm = parent->time;
mp_uint_t res = parent_tm - item_tm;
+ if (res == 0) {
+ // TODO: This actually should use the same "ring" logic
+ // as for time, to avoid artifacts when id's overflow.
+ return item->id < parent->id;
+ }
if ((mp_int_t)res < 0) {
res += MODULO;
}
@@ -125,6 +132,7 @@ STATIC mp_obj_t mod_utimeq_heappush(size_t n_args, const mp_obj_t *args) {
}
mp_uint_t l = heap->len;
heap->items[l].time = MP_OBJ_SMALL_INT_VALUE(args[1]);
+ heap->items[l].id = utimeq_id++;
heap->items[l].callback = args[2];
heap->items[l].args = args[3];
heap_siftdown(heap, 0, heap->len);
@@ -158,6 +166,17 @@ STATIC mp_obj_t mod_utimeq_heappop(mp_obj_t heap_in, mp_obj_t list_ref) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_utimeq_heappop_obj, mod_utimeq_heappop);
+STATIC mp_obj_t mod_utimeq_peektime(mp_obj_t heap_in) {
+ mp_obj_utimeq_t *heap = get_heap(heap_in);
+ if (heap->len == 0) {
+ nlr_raise(mp_obj_new_exception_msg(&mp_type_IndexError, "empty heap"));
+ }
+
+ struct qentry *item = &heap->items[0];
+ return MP_OBJ_NEW_SMALL_INT(item->time);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_utimeq_peektime_obj, mod_utimeq_peektime);
+
#if DEBUG
STATIC mp_obj_t mod_utimeq_dump(mp_obj_t heap_in) {
mp_obj_utimeq_t *heap = get_heap(heap_in);
@@ -182,6 +201,7 @@ STATIC mp_obj_t utimeq_unary_op(mp_uint_t op, mp_obj_t self_in) {
STATIC const mp_rom_map_elem_t utimeq_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_push), MP_ROM_PTR(&mod_utimeq_heappush_obj) },
{ MP_ROM_QSTR(MP_QSTR_pop), MP_ROM_PTR(&mod_utimeq_heappop_obj) },
+ { MP_ROM_QSTR(MP_QSTR_peektime), MP_ROM_PTR(&mod_utimeq_peektime_obj) },
#if DEBUG
{ MP_ROM_QSTR(MP_QSTR_dump), MP_ROM_PTR(&mod_utimeq_dump_obj) },
#endif
diff --git a/extmod/modwebrepl.c b/extmod/modwebrepl.c
index 571acabfe..ec965587e 100644
--- a/extmod/modwebrepl.c
+++ b/extmod/modwebrepl.c
@@ -308,7 +308,7 @@ STATIC mp_obj_t webrepl_close(mp_obj_t self_in) {
STATIC MP_DEFINE_CONST_FUN_OBJ_1(webrepl_close_obj, webrepl_close);
STATIC mp_obj_t webrepl_set_password(mp_obj_t passwd_in) {
- mp_uint_t len;
+ size_t len;
const char *passwd = mp_obj_str_get_data(passwd_in, &len);
if (len > sizeof(webrepl_passwd) - 1) {
mp_raise_ValueError("");
diff --git a/extmod/modwebsocket.c b/extmod/modwebsocket.c
index 8200ea708..9e17d6a6d 100644
--- a/extmod/modwebsocket.c
+++ b/extmod/modwebsocket.c
@@ -132,7 +132,7 @@ STATIC mp_uint_t websocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int
self->buf_pos = 0;
self->to_recv = to_recv;
- self->msg_sz = sz; // May be overriden by FRAME_OPT
+ self->msg_sz = sz; // May be overridden by FRAME_OPT
if (to_recv != 0) {
self->state = FRAME_OPT;
} else {
diff --git a/extmod/utime_mphal.c b/extmod/utime_mphal.c
index f447b3a68..e99ba46ce 100644
--- a/extmod/utime_mphal.c
+++ b/extmod/utime_mphal.c
@@ -37,13 +37,11 @@
#include "extmod/utime_mphal.h"
STATIC mp_obj_t time_sleep(mp_obj_t seconds_o) {
- MP_THREAD_GIL_EXIT();
#if MICROPY_PY_BUILTINS_FLOAT
mp_hal_delay_ms(1000 * mp_obj_get_float(seconds_o));
#else
mp_hal_delay_ms(1000 * mp_obj_get_int(seconds_o));
#endif
- MP_THREAD_GIL_ENTER();
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_1(mp_utime_sleep_obj, time_sleep);
@@ -51,9 +49,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(mp_utime_sleep_obj, time_sleep);
STATIC mp_obj_t time_sleep_ms(mp_obj_t arg) {
mp_int_t ms = mp_obj_get_int(arg);
if (ms > 0) {
- MP_THREAD_GIL_EXIT();
mp_hal_delay_ms(ms);
- MP_THREAD_GIL_ENTER();
}
return mp_const_none;
}
@@ -62,9 +58,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(mp_utime_sleep_ms_obj, time_sleep_ms);
STATIC mp_obj_t time_sleep_us(mp_obj_t arg) {
mp_int_t us = mp_obj_get_int(arg);
if (us > 0) {
- MP_THREAD_GIL_EXIT();
mp_hal_delay_us(us);
- MP_THREAD_GIL_ENTER();
}
return mp_const_none;
}
diff --git a/extmod/vfs.c b/extmod/vfs.c
new file mode 100644
index 000000000..3bdce80db
--- /dev/null
+++ b/extmod/vfs.c
@@ -0,0 +1,457 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include "py/runtime0.h"
+#include "py/runtime.h"
+#include "py/objstr.h"
+#include "py/mperrno.h"
+#include "extmod/vfs.h"
+
+#if MICROPY_VFS
+
+#if MICROPY_VFS_FAT
+#include "extmod/vfs_fat.h"
+#endif
+
+// path is the path to lookup and *path_out holds the path within the VFS
+// object (starts with / if an absolute path).
+// Returns MP_VFS_ROOT for root dir (and then path_out is undefined) and
+// MP_VFS_NONE for path not found.
+mp_vfs_mount_t *mp_vfs_lookup_path(const char *path, const char **path_out) {
+ if (*path == '/' || MP_STATE_VM(vfs_cur) == MP_VFS_ROOT) {
+ // an absolute path, or the current volume is root, so search root dir
+ bool is_abs = 0;
+ if (*path == '/') {
+ ++path;
+ is_abs = 1;
+ }
+ if (*path == '\0') {
+ // path is "" or "/" so return virtual root
+ return MP_VFS_ROOT;
+ }
+ for (mp_vfs_mount_t *vfs = MP_STATE_VM(vfs_mount_table); vfs != NULL; vfs = vfs->next) {
+ size_t len = vfs->len - 1;
+ if (len == 0) {
+ *path_out = path - is_abs;
+ return vfs;
+ }
+ if (strncmp(path, vfs->str + 1, len) == 0) {
+ if (path[len] == '/') {
+ *path_out = path + len;
+ return vfs;
+ } else if (path[len] == '\0') {
+ *path_out = "/";
+ return vfs;
+ }
+ }
+ }
+
+ // if we get here then there's nothing mounted on /
+
+ if (is_abs) {
+ // path began with / and was not found
+ return MP_VFS_NONE;
+ }
+ }
+
+ // a relative path within a mounted device
+ *path_out = path;
+ return MP_STATE_VM(vfs_cur);
+}
+
+// Version of mp_vfs_lookup_path that takes and returns uPy string objects.
+STATIC mp_vfs_mount_t *lookup_path(mp_obj_t path_in, mp_obj_t *path_out) {
+ const char *path = mp_obj_str_get_str(path_in);
+ const char *p_out;
+ mp_vfs_mount_t *vfs = mp_vfs_lookup_path(path, &p_out);
+ if (vfs != MP_VFS_NONE && vfs != MP_VFS_ROOT) {
+ *path_out = mp_obj_new_str_of_type(mp_obj_get_type(path_in),
+ (const byte*)p_out, strlen(p_out));
+ }
+ return vfs;
+}
+
+STATIC mp_obj_t mp_vfs_proxy_call(mp_vfs_mount_t *vfs, qstr meth_name, size_t n_args, const mp_obj_t *args) {
+ if (vfs == MP_VFS_NONE) {
+ // mount point not found
+ mp_raise_OSError(MP_ENODEV);
+ }
+ if (vfs == MP_VFS_ROOT) {
+ // can't do operation on root dir
+ mp_raise_OSError(MP_EPERM);
+ }
+ mp_obj_t meth[n_args + 2];
+ mp_load_method(vfs->obj, meth_name, meth);
+ if (args != NULL) {
+ memcpy(meth + 2, args, n_args * sizeof(*args));
+ }
+ return mp_call_method_n_kw(n_args, 0, meth);
+}
+
+mp_import_stat_t mp_vfs_import_stat(const char *path) {
+ const char *path_out;
+ mp_vfs_mount_t *vfs = mp_vfs_lookup_path(path, &path_out);
+ if (vfs == MP_VFS_NONE || vfs == MP_VFS_ROOT) {
+ return MP_IMPORT_STAT_NO_EXIST;
+ }
+ #if MICROPY_VFS_FAT
+ // fast paths for known VFS types
+ if (mp_obj_get_type(vfs->obj) == &mp_fat_vfs_type) {
+ return fat_vfs_import_stat(MP_OBJ_TO_PTR(vfs->obj), path_out);
+ }
+ #endif
+ // TODO delegate to vfs.stat() method
+ return MP_IMPORT_STAT_NO_EXIST;
+}
+
+mp_obj_t mp_vfs_mount(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ enum { ARG_readonly, ARG_mkfs };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_readonly, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_false} },
+ { MP_QSTR_mkfs, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_false} },
+ };
+
+ // parse args
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args - 2, pos_args + 2, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ // get the mount point
+ size_t mnt_len;
+ const char *mnt_str = mp_obj_str_get_data(pos_args[1], &mnt_len);
+
+ // see if we need to auto-detect and create the filesystem
+ mp_obj_t vfs_obj = pos_args[0];
+ mp_obj_t dest[2];
+ mp_load_method_maybe(vfs_obj, MP_QSTR_mount, dest);
+ if (dest[0] == MP_OBJ_NULL) {
+ // Input object has no mount method, assume it's a block device and try to
+ // auto-detect the filesystem and create the corresponding VFS entity.
+ // (At the moment we only support FAT filesystems.)
+ #if MICROPY_VFS_FAT
+ vfs_obj = mp_fat_vfs_type.make_new(&mp_fat_vfs_type, 1, 0, &vfs_obj);
+ #endif
+ }
+
+ // create new object
+ mp_vfs_mount_t *vfs = m_new_obj(mp_vfs_mount_t);
+ vfs->str = mnt_str;
+ vfs->len = mnt_len;
+ vfs->obj = vfs_obj;
+ vfs->next = NULL;
+
+ // call the underlying object to do any mounting operation
+ mp_vfs_proxy_call(vfs, MP_QSTR_mount, 2, (mp_obj_t*)&args);
+
+ // check that the destination mount point is unused
+ const char *path_out;
+ mp_vfs_mount_t *existing_mount = mp_vfs_lookup_path(mp_obj_str_get_str(pos_args[1]), &path_out);
+ if (existing_mount != MP_VFS_NONE && existing_mount != MP_VFS_ROOT) {
+ if (vfs->len != 1 && existing_mount->len == 1) {
+ // if root dir is mounted, still allow to mount something within a subdir of root
+ } else {
+ // mount point in use
+ mp_raise_OSError(MP_EPERM);
+ }
+ }
+
+ // insert the vfs into the mount table
+ mp_vfs_mount_t **vfsp = &MP_STATE_VM(vfs_mount_table);
+ while (*vfsp != NULL) {
+ if ((*vfsp)->len == 1) {
+ // make sure anything mounted at the root stays at the end of the list
+ vfs->next = *vfsp;
+ break;
+ }
+ vfsp = &(*vfsp)->next;
+ }
+ *vfsp = vfs;
+
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_KW(mp_vfs_mount_obj, 2, mp_vfs_mount);
+
+mp_obj_t mp_vfs_umount(mp_obj_t mnt_in) {
+ // remove vfs from the mount table
+ mp_vfs_mount_t *vfs = NULL;
+ size_t mnt_len;
+ const char *mnt_str = NULL;
+ if (MP_OBJ_IS_STR(mnt_in)) {
+ mnt_str = mp_obj_str_get_data(mnt_in, &mnt_len);
+ }
+ for (mp_vfs_mount_t **vfsp = &MP_STATE_VM(vfs_mount_table); *vfsp != NULL; vfsp = &(*vfsp)->next) {
+ if ((mnt_str != NULL && !memcmp(mnt_str, (*vfsp)->str, mnt_len + 1)) || (*vfsp)->obj == mnt_in) {
+ vfs = *vfsp;
+ *vfsp = (*vfsp)->next;
+ break;
+ }
+ }
+
+ if (vfs == NULL) {
+ mp_raise_OSError(MP_EINVAL);
+ }
+
+ // if we unmounted the current device then set current to root
+ if (MP_STATE_VM(vfs_cur) == vfs) {
+ MP_STATE_VM(vfs_cur) = MP_VFS_ROOT;
+ }
+
+ // call the underlying object to do any unmounting operation
+ mp_vfs_proxy_call(vfs, MP_QSTR_umount, 0, NULL);
+
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_umount_obj, mp_vfs_umount);
+
+// Note: buffering and encoding args are currently ignored
+mp_obj_t mp_vfs_open(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ enum { ARG_file, ARG_mode, ARG_encoding };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_file, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} },
+ { MP_QSTR_mode, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_QSTR(MP_QSTR_r)} },
+ { MP_QSTR_buffering, MP_ARG_INT, {.u_int = -1} },
+ { MP_QSTR_encoding, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} },
+ };
+
+ // parse args
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ mp_vfs_mount_t *vfs = lookup_path((mp_obj_t)args[ARG_file].u_rom_obj, &args[ARG_file].u_obj);
+ return mp_vfs_proxy_call(vfs, MP_QSTR_open, 2, (mp_obj_t*)&args);
+}
+MP_DEFINE_CONST_FUN_OBJ_KW(mp_vfs_open_obj, 0, mp_vfs_open);
+
+mp_obj_t mp_vfs_chdir(mp_obj_t path_in) {
+ mp_obj_t path_out;
+ mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out);
+ MP_STATE_VM(vfs_cur) = vfs;
+ if (vfs == MP_VFS_ROOT) {
+ // If we change to the root dir and a VFS is mounted at the root then
+ // we must change that VFS's current dir to the root dir so that any
+ // subsequent relative paths begin at the root of that VFS.
+ for (vfs = MP_STATE_VM(vfs_mount_table); vfs != NULL; vfs = vfs->next) {
+ if (vfs->len == 1) {
+ mp_obj_t root = mp_obj_new_str("/", 1, false);
+ mp_vfs_proxy_call(vfs, MP_QSTR_chdir, 1, &root);
+ break;
+ }
+ }
+ } else {
+ mp_vfs_proxy_call(vfs, MP_QSTR_chdir, 1, &path_out);
+ }
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_chdir_obj, mp_vfs_chdir);
+
+mp_obj_t mp_vfs_getcwd(void) {
+ if (MP_STATE_VM(vfs_cur) == MP_VFS_ROOT) {
+ return MP_OBJ_NEW_QSTR(MP_QSTR__slash_);
+ }
+ mp_obj_t cwd_o = mp_vfs_proxy_call(MP_STATE_VM(vfs_cur), MP_QSTR_getcwd, 0, NULL);
+ if (MP_STATE_VM(vfs_cur)->len == 1) {
+ // don't prepend "/" for vfs mounted at root
+ return cwd_o;
+ }
+ const char *cwd = mp_obj_str_get_str(cwd_o);
+ vstr_t vstr;
+ vstr_init(&vstr, MP_STATE_VM(vfs_cur)->len + strlen(cwd) + 1);
+ vstr_add_strn(&vstr, MP_STATE_VM(vfs_cur)->str, MP_STATE_VM(vfs_cur)->len);
+ if (!(cwd[0] == '/' && cwd[1] == 0)) {
+ vstr_add_str(&vstr, cwd);
+ }
+ return mp_obj_new_str_from_vstr(&mp_type_str, &vstr);
+}
+MP_DEFINE_CONST_FUN_OBJ_0(mp_vfs_getcwd_obj, mp_vfs_getcwd);
+
+typedef struct _mp_vfs_ilistdir_it_t {
+ mp_obj_base_t base;
+ mp_fun_1_t iternext;
+ union {
+ mp_vfs_mount_t *vfs;
+ mp_obj_t iter;
+ } cur;
+ bool is_str;
+ bool is_iter;
+} mp_vfs_ilistdir_it_t;
+
+STATIC mp_obj_t mp_vfs_ilistdir_it_iternext(mp_obj_t self_in) {
+ mp_vfs_ilistdir_it_t *self = MP_OBJ_TO_PTR(self_in);
+ if (self->is_iter) {
+ // continue delegating to root dir
+ return mp_iternext(self->cur.iter);
+ } else if (self->cur.vfs == NULL) {
+ // finished iterating mount points and no root dir is mounted
+ return MP_OBJ_STOP_ITERATION;
+ } else {
+ // continue iterating mount points
+ mp_vfs_mount_t *vfs = self->cur.vfs;
+ self->cur.vfs = vfs->next;
+ if (vfs->len == 1) {
+ // vfs is mounted at root dir, delegate to it
+ mp_obj_t root = mp_obj_new_str("/", 1, false);
+ self->is_iter = true;
+ self->cur.iter = mp_vfs_proxy_call(vfs, MP_QSTR_ilistdir, 1, &root);
+ return mp_iternext(self->cur.iter);
+ } else {
+ // a mounted directory
+ mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL));
+ t->items[0] = mp_obj_new_str_of_type(
+ self->is_str ? &mp_type_str : &mp_type_bytes,
+ (const byte*)vfs->str + 1, vfs->len - 1);
+ t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFDIR);
+ t->items[2] = MP_OBJ_NEW_SMALL_INT(0); // no inode number
+ return MP_OBJ_FROM_PTR(t);
+ }
+ }
+}
+
+mp_obj_t mp_vfs_ilistdir(size_t n_args, const mp_obj_t *args) {
+ mp_obj_t path_in;
+ if (n_args == 1) {
+ path_in = args[0];
+ } else {
+ path_in = MP_OBJ_NEW_QSTR(MP_QSTR_);
+ }
+
+ mp_obj_t path_out;
+ mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out);
+
+ if (vfs == MP_VFS_ROOT) {
+ // list the root directory
+ mp_vfs_ilistdir_it_t *iter = m_new_obj(mp_vfs_ilistdir_it_t);
+ iter->base.type = &mp_type_polymorph_iter;
+ iter->iternext = mp_vfs_ilistdir_it_iternext;
+ iter->cur.vfs = MP_STATE_VM(vfs_mount_table);
+ iter->is_str = mp_obj_get_type(path_in) == &mp_type_str;
+ iter->is_iter = false;
+ return MP_OBJ_FROM_PTR(iter);
+ }
+
+ return mp_vfs_proxy_call(vfs, MP_QSTR_ilistdir, 1, &path_out);
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_vfs_ilistdir_obj, 0, 1, mp_vfs_ilistdir);
+
+mp_obj_t mp_vfs_listdir(size_t n_args, const mp_obj_t *args) {
+ mp_obj_t iter = mp_vfs_ilistdir(n_args, args);
+ mp_obj_t dir_list = mp_obj_new_list(0, NULL);
+ mp_obj_t next;
+ while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
+ mp_obj_t *items;
+ mp_obj_get_array_fixed_n(next, 3, &items);
+ mp_obj_list_append(dir_list, items[0]);
+ }
+ return dir_list;
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_vfs_listdir_obj, 0, 1, mp_vfs_listdir);
+
+mp_obj_t mp_vfs_mkdir(mp_obj_t path_in) {
+ mp_obj_t path_out;
+ mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out);
+ if (vfs == MP_VFS_ROOT || (vfs != MP_VFS_NONE && !strcmp(mp_obj_str_get_str(path_out), "/"))) {
+ mp_raise_OSError(MP_EEXIST);
+ }
+ return mp_vfs_proxy_call(vfs, MP_QSTR_mkdir, 1, &path_out);
+}
+MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_mkdir_obj, mp_vfs_mkdir);
+
+mp_obj_t mp_vfs_remove(mp_obj_t path_in) {
+ mp_obj_t path_out;
+ mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out);
+ return mp_vfs_proxy_call(vfs, MP_QSTR_remove, 1, &path_out);
+}
+MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_remove_obj, mp_vfs_remove);
+
+mp_obj_t mp_vfs_rename(mp_obj_t old_path_in, mp_obj_t new_path_in) {
+ mp_obj_t args[2];
+ mp_vfs_mount_t *old_vfs = lookup_path(old_path_in, &args[0]);
+ mp_vfs_mount_t *new_vfs = lookup_path(new_path_in, &args[1]);
+ if (old_vfs != new_vfs) {
+ // can't rename across filesystems
+ mp_raise_OSError(MP_EPERM);
+ }
+ return mp_vfs_proxy_call(old_vfs, MP_QSTR_rename, 2, args);
+}
+MP_DEFINE_CONST_FUN_OBJ_2(mp_vfs_rename_obj, mp_vfs_rename);
+
+mp_obj_t mp_vfs_rmdir(mp_obj_t path_in) {
+ mp_obj_t path_out;
+ mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out);
+ return mp_vfs_proxy_call(vfs, MP_QSTR_rmdir, 1, &path_out);
+}
+MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_rmdir_obj, mp_vfs_rmdir);
+
+mp_obj_t mp_vfs_stat(mp_obj_t path_in) {
+ mp_obj_t path_out;
+ mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out);
+ if (vfs == MP_VFS_ROOT) {
+ mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
+ t->items[0] = MP_OBJ_NEW_SMALL_INT(MP_S_IFDIR); // st_mode
+ for (int i = 1; i <= 9; ++i) {
+ t->items[i] = MP_OBJ_NEW_SMALL_INT(0); // dev, nlink, uid, gid, size, atime, mtime, ctime
+ }
+ return MP_OBJ_FROM_PTR(t);
+ }
+ return mp_vfs_proxy_call(vfs, MP_QSTR_stat, 1, &path_out);
+}
+MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_stat_obj, mp_vfs_stat);
+
+mp_obj_t mp_vfs_statvfs(mp_obj_t path_in) {
+ mp_obj_t path_out;
+ mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out);
+ if (vfs == MP_VFS_ROOT) {
+ // statvfs called on the root directory, see if there's anything mounted there
+ for (vfs = MP_STATE_VM(vfs_mount_table); vfs != NULL; vfs = vfs->next) {
+ if (vfs->len == 1) {
+ break;
+ }
+ }
+
+ // If there's nothing mounted at root then return a mostly-empty tuple
+ if (vfs == NULL) {
+ mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
+
+ // fill in: bsize, frsize, blocks, bfree, bavail, files, ffree, favail, flags
+ for (int i = 0; i <= 8; ++i) {
+ t->items[i] = MP_OBJ_NEW_SMALL_INT(0);
+ }
+
+ // Put something sensible in f_namemax
+ t->items[9] = MP_OBJ_NEW_SMALL_INT(MICROPY_ALLOC_PATH_MAX);
+
+ return MP_OBJ_FROM_PTR(t);
+ }
+
+ // VFS mounted at root so delegate the call to it
+ path_out = MP_OBJ_NEW_QSTR(MP_QSTR__slash_);
+ }
+ return mp_vfs_proxy_call(vfs, MP_QSTR_statvfs, 1, &path_out);
+}
+MP_DEFINE_CONST_FUN_OBJ_1(mp_vfs_statvfs_obj, mp_vfs_statvfs);
+
+#endif // MICROPY_VFS
diff --git a/extmod/vfs.h b/extmod/vfs.h
new file mode 100644
index 000000000..edaeb5349
--- /dev/null
+++ b/extmod/vfs.h
@@ -0,0 +1,86 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef MICROPY_INCLUDED_EXTMOD_VFS_H
+#define MICROPY_INCLUDED_EXTMOD_VFS_H
+
+#include "py/lexer.h"
+#include "py/obj.h"
+
+// return values of mp_vfs_lookup_path
+// ROOT is 0 so that the default current directory is the root directory
+#define MP_VFS_NONE ((mp_vfs_mount_t*)1)
+#define MP_VFS_ROOT ((mp_vfs_mount_t*)0)
+
+// MicroPython's port-standardized versions of stat constants
+#define MP_S_IFDIR (0x4000)
+#define MP_S_IFREG (0x8000)
+
+// constants for block protocol ioctl
+#define BP_IOCTL_INIT (1)
+#define BP_IOCTL_DEINIT (2)
+#define BP_IOCTL_SYNC (3)
+#define BP_IOCTL_SEC_COUNT (4)
+#define BP_IOCTL_SEC_SIZE (5)
+
+typedef struct _mp_vfs_mount_t {
+ const char *str; // mount point with leading /
+ size_t len;
+ mp_obj_t obj;
+ struct _mp_vfs_mount_t *next;
+} mp_vfs_mount_t;
+
+mp_vfs_mount_t *mp_vfs_lookup_path(const char *path, const char **path_out);
+mp_import_stat_t mp_vfs_import_stat(const char *path);
+mp_obj_t mp_vfs_mount(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
+mp_obj_t mp_vfs_umount(mp_obj_t mnt_in);
+mp_obj_t mp_vfs_open(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
+mp_obj_t mp_vfs_chdir(mp_obj_t path_in);
+mp_obj_t mp_vfs_getcwd(void);
+mp_obj_t mp_vfs_ilistdir(size_t n_args, const mp_obj_t *args);
+mp_obj_t mp_vfs_listdir(size_t n_args, const mp_obj_t *args);
+mp_obj_t mp_vfs_mkdir(mp_obj_t path_in);
+mp_obj_t mp_vfs_remove(mp_obj_t path_in);
+mp_obj_t mp_vfs_rename(mp_obj_t old_path_in, mp_obj_t new_path_in);
+mp_obj_t mp_vfs_rmdir(mp_obj_t path_in);
+mp_obj_t mp_vfs_stat(mp_obj_t path_in);
+mp_obj_t mp_vfs_statvfs(mp_obj_t path_in);
+
+MP_DECLARE_CONST_FUN_OBJ_KW(mp_vfs_mount_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_umount_obj);
+MP_DECLARE_CONST_FUN_OBJ_KW(mp_vfs_open_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_chdir_obj);
+MP_DECLARE_CONST_FUN_OBJ_0(mp_vfs_getcwd_obj);
+MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_vfs_ilistdir_obj);
+MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_vfs_listdir_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_mkdir_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_remove_obj);
+MP_DECLARE_CONST_FUN_OBJ_2(mp_vfs_rename_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_rmdir_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_stat_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_statvfs_obj);
+
+#endif // MICROPY_INCLUDED_EXTMOD_VFS_H
diff --git a/extmod/vfs_fat.c b/extmod/vfs_fat.c
index 0043b77bd..0ec3fe6d2 100644
--- a/extmod/vfs_fat.c
+++ b/extmod/vfs_fat.c
@@ -28,40 +28,71 @@
#include "py/mpconfig.h"
#if MICROPY_VFS_FAT
+#if !MICROPY_VFS
+#error "with MICROPY_VFS_FAT enabled, must also enable MICROPY_VFS"
+#endif
+
#include <string.h>
#include "py/nlr.h"
#include "py/runtime.h"
#include "py/mperrno.h"
-#include "lib/fatfs/ff.h"
-#include "lib/fatfs/diskio.h"
-#include "extmod/vfs_fat_file.h"
-#include "extmod/fsusermount.h"
-#include "timeutils.h"
+#include "lib/oofatfs/ff.h"
+#include "extmod/vfs_fat.h"
+#include "lib/timeutils/timeutils.h"
+
+#if _MAX_SS == _MIN_SS
+#define SECSIZE(fs) (_MIN_SS)
+#else
+#define SECSIZE(fs) ((fs)->ssize)
+#endif
#define mp_obj_fat_vfs_t fs_user_mount_t
STATIC mp_obj_t fat_vfs_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
- mp_arg_check_num(n_args, n_kw, 2, 2, false);
- mp_obj_fat_vfs_t *vfs = fatfs_mount_mkfs(n_args, args, (mp_map_t*)&mp_const_empty_map, false);
+ mp_arg_check_num(n_args, n_kw, 1, 1, false);
+
+ // create new object
+ fs_user_mount_t *vfs = m_new_obj(fs_user_mount_t);
vfs->base.type = type;
+ vfs->flags = FSUSER_FREE_OBJ;
+ vfs->fatfs.drv = vfs;
+
+ // load block protocol methods
+ mp_load_method(args[0], MP_QSTR_readblocks, vfs->readblocks);
+ mp_load_method_maybe(args[0], MP_QSTR_writeblocks, vfs->writeblocks);
+ mp_load_method_maybe(args[0], MP_QSTR_ioctl, vfs->u.ioctl);
+ if (vfs->u.ioctl[0] != MP_OBJ_NULL) {
+ // device supports new block protocol, so indicate it
+ vfs->flags |= FSUSER_HAVE_IOCTL;
+ } else {
+ // no ioctl method, so assume the device uses the old block protocol
+ mp_load_method_maybe(args[0], MP_QSTR_sync, vfs->u.old.sync);
+ mp_load_method(args[0], MP_QSTR_count, vfs->u.old.count);
+ }
+
return MP_OBJ_FROM_PTR(vfs);
}
STATIC mp_obj_t fat_vfs_mkfs(mp_obj_t bdev_in) {
- mp_obj_t args[] = {bdev_in, MP_OBJ_NEW_QSTR(MP_QSTR_mkfs)};
- fatfs_mount_mkfs(2, args, (mp_map_t*)&mp_const_empty_map, true);
+ // create new object
+ fs_user_mount_t *vfs = MP_OBJ_TO_PTR(fat_vfs_make_new(&mp_fat_vfs_type, 1, 0, &bdev_in));
+
+ // make the filesystem
+ uint8_t working_buf[_MAX_SS];
+ FRESULT res = f_mkfs(&vfs->fatfs, FM_FAT | FM_SFD, 0, working_buf, sizeof(working_buf));
+ if (res != FR_OK) {
+ mp_raise_OSError(fresult_to_errno_table[res]);
+ }
+
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_mkfs_fun_obj, fat_vfs_mkfs);
STATIC MP_DEFINE_CONST_STATICMETHOD_OBJ(fat_vfs_mkfs_obj, MP_ROM_PTR(&fat_vfs_mkfs_fun_obj));
-STATIC mp_obj_t fat_vfs_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
- // Skip self
- return fatfs_builtin_open(n_args - 1, args + 1, kwargs);
-}
-MP_DEFINE_CONST_FUN_OBJ_KW(fat_vfs_open_obj, 2, fat_vfs_open);
+STATIC MP_DEFINE_CONST_FUN_OBJ_3(fat_vfs_open_obj, fatfs_builtin_open_self);
-STATIC mp_obj_t fat_vfs_listdir_func(size_t n_args, const mp_obj_t *args) {
+STATIC mp_obj_t fat_vfs_ilistdir_func(size_t n_args, const mp_obj_t *args) {
+ mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(args[0]);
bool is_str_type = true;
const char *path;
if (n_args == 2) {
@@ -73,19 +104,16 @@ STATIC mp_obj_t fat_vfs_listdir_func(size_t n_args, const mp_obj_t *args) {
path = "";
}
- return fat_vfs_listdir(path, is_str_type);
+ return fat_vfs_ilistdir2(self, path, is_str_type);
}
-STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fat_vfs_listdir_obj, 1, 2, fat_vfs_listdir_func);
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fat_vfs_ilistdir_obj, 1, 2, fat_vfs_ilistdir_func);
-STATIC mp_obj_t fat_vfs_remove_internal(mp_obj_t path_in, mp_int_t attr) {
+STATIC mp_obj_t fat_vfs_remove_internal(mp_obj_t vfs_in, mp_obj_t path_in, mp_int_t attr) {
+ mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in);
const char *path = mp_obj_str_get_str(path_in);
FILINFO fno;
-#if _USE_LFN
- fno.lfname = NULL;
- fno.lfsize = 0;
-#endif
- FRESULT res = f_stat(path, &fno);
+ FRESULT res = f_stat(&self->fatfs, path, &fno);
if (res != FR_OK) {
mp_raise_OSError(fresult_to_errno_table[res]);
@@ -93,7 +121,7 @@ STATIC mp_obj_t fat_vfs_remove_internal(mp_obj_t path_in, mp_int_t attr) {
// check if path is a file or directory
if ((fno.fattrib & AM_DIR) == attr) {
- res = f_unlink(path);
+ res = f_unlink(&self->fatfs, path);
if (res != FR_OK) {
mp_raise_OSError(fresult_to_errno_table[res]);
@@ -105,27 +133,25 @@ STATIC mp_obj_t fat_vfs_remove_internal(mp_obj_t path_in, mp_int_t attr) {
}
STATIC mp_obj_t fat_vfs_remove(mp_obj_t vfs_in, mp_obj_t path_in) {
- (void)vfs_in;
- return fat_vfs_remove_internal(path_in, 0); // 0 == file attribute
+ return fat_vfs_remove_internal(vfs_in, path_in, 0); // 0 == file attribute
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_remove_obj, fat_vfs_remove);
STATIC mp_obj_t fat_vfs_rmdir(mp_obj_t vfs_in, mp_obj_t path_in) {
- (void) vfs_in;
- return fat_vfs_remove_internal(path_in, AM_DIR);
+ return fat_vfs_remove_internal(vfs_in, path_in, AM_DIR);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_rmdir_obj, fat_vfs_rmdir);
STATIC mp_obj_t fat_vfs_rename(mp_obj_t vfs_in, mp_obj_t path_in, mp_obj_t path_out) {
- (void)vfs_in;
+ mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in);
const char *old_path = mp_obj_str_get_str(path_in);
const char *new_path = mp_obj_str_get_str(path_out);
- FRESULT res = f_rename(old_path, new_path);
+ FRESULT res = f_rename(&self->fatfs, old_path, new_path);
if (res == FR_EXIST) {
// if new_path exists then try removing it (but only if it's a file)
- fat_vfs_remove_internal(path_out, 0); // 0 == file attribute
+ fat_vfs_remove_internal(vfs_in, path_out, 0); // 0 == file attribute
// try to rename again
- res = f_rename(old_path, new_path);
+ res = f_rename(&self->fatfs, old_path, new_path);
}
if (res == FR_OK) {
return mp_const_none;
@@ -137,9 +163,9 @@ STATIC mp_obj_t fat_vfs_rename(mp_obj_t vfs_in, mp_obj_t path_in, mp_obj_t path_
STATIC MP_DEFINE_CONST_FUN_OBJ_3(fat_vfs_rename_obj, fat_vfs_rename);
STATIC mp_obj_t fat_vfs_mkdir(mp_obj_t vfs_in, mp_obj_t path_o) {
- (void)vfs_in;
+ mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in);
const char *path = mp_obj_str_get_str(path_o);
- FRESULT res = f_mkdir(path);
+ FRESULT res = f_mkdir(&self->fatfs, path);
if (res == FR_OK) {
return mp_const_none;
} else {
@@ -150,15 +176,11 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_mkdir_obj, fat_vfs_mkdir);
/// Change current directory.
STATIC mp_obj_t fat_vfs_chdir(mp_obj_t vfs_in, mp_obj_t path_in) {
- (void)vfs_in;
+ mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in);
const char *path;
path = mp_obj_str_get_str(path_in);
- FRESULT res = f_chdrive(path);
-
- if (res == FR_OK) {
- res = f_chdir(path);
- }
+ FRESULT res = f_chdir(&self->fatfs, path);
if (res != FR_OK) {
mp_raise_OSError(fresult_to_errno_table[res]);
@@ -170,71 +192,31 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_chdir_obj, fat_vfs_chdir);
/// Get the current directory.
STATIC mp_obj_t fat_vfs_getcwd(mp_obj_t vfs_in) {
- (void)vfs_in;
+ mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in);
char buf[MICROPY_ALLOC_PATH_MAX + 1];
- FRESULT res = f_getcwd(buf, sizeof buf);
-
+ FRESULT res = f_getcwd(&self->fatfs, buf, sizeof(buf));
if (res != FR_OK) {
mp_raise_OSError(fresult_to_errno_table[res]);
}
-
return mp_obj_new_str(buf, strlen(buf), false);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_getcwd_obj, fat_vfs_getcwd);
-// Checks for path equality, ignoring trailing slashes:
-// path_equal(/, /) -> true
-// second argument must be in canonical form (meaning no trailing slash, unless it's just /)
-STATIC bool path_equal(const char *path, const char *path_canonical) {
- while (*path_canonical != '\0' && *path == *path_canonical) {
- ++path;
- ++path_canonical;
- }
- if (*path_canonical != '\0') {
- return false;
- }
- while (*path == '/') {
- ++path;
- }
- return *path == '\0';
-}
-
/// \function stat(path)
/// Get the status of a file or directory.
STATIC mp_obj_t fat_vfs_stat(mp_obj_t vfs_in, mp_obj_t path_in) {
- (void)vfs_in;
+ mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in);
const char *path = mp_obj_str_get_str(path_in);
FILINFO fno;
-#if _USE_LFN
- fno.lfname = NULL;
- fno.lfsize = 0;
-#endif
- FRESULT res;
-
- if (path_equal(path, "/")) {
+ if (path[0] == 0 || (path[0] == '/' && path[1] == 0)) {
// stat root directory
fno.fsize = 0;
fno.fdate = 0x2821; // Jan 1, 2000
fno.ftime = 0;
fno.fattrib = AM_DIR;
} else {
- res = FR_NO_PATH;
- for (size_t i = 0; i < MP_ARRAY_SIZE(MP_STATE_PORT(fs_user_mount)); ++i) {
- fs_user_mount_t *vfs = MP_STATE_PORT(fs_user_mount)[i];
- if (vfs != NULL && path_equal(path, vfs->str)) {
- // stat mounted device directory
- fno.fsize = 0;
- fno.fdate = 0x2821; // Jan 1, 2000
- fno.ftime = 0;
- fno.fattrib = AM_DIR;
- res = FR_OK;
- }
- }
- if (res == FR_NO_PATH) {
- // stat normal file
- res = f_stat(path, &fno);
- }
+ FRESULT res = f_stat(&self->fatfs, path, &fno);
if (res != FR_OK) {
mp_raise_OSError(fresult_to_errno_table[res]);
}
@@ -243,9 +225,9 @@ STATIC mp_obj_t fat_vfs_stat(mp_obj_t vfs_in, mp_obj_t path_in) {
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
mp_int_t mode = 0;
if (fno.fattrib & AM_DIR) {
- mode |= 0x4000; // stat.S_IFDIR
+ mode |= MP_S_IFDIR;
} else {
- mode |= 0x8000; // stat.S_IFREG
+ mode |= MP_S_IFREG;
}
mp_int_t seconds = timeutils_seconds_since_2000(
1980 + ((fno.fdate >> 9) & 0x7f),
@@ -272,25 +254,21 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_stat_obj, fat_vfs_stat);
// Get the status of a VFS.
STATIC mp_obj_t fat_vfs_statvfs(mp_obj_t vfs_in, mp_obj_t path_in) {
- (void)vfs_in;
- const char *path = mp_obj_str_get_str(path_in);
+ mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in);
+ (void)path_in;
- FATFS *fatfs;
DWORD nclst;
- FRESULT res = f_getfree(path, &nclst, &fatfs);
+ FATFS *fatfs = &self->fatfs;
+ FRESULT res = f_getfree(fatfs, &nclst);
if (FR_OK != res) {
mp_raise_OSError(fresult_to_errno_table[res]);
}
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
- #if _MIN_SS != _MAX_SS
- t->items[0] = MP_OBJ_NEW_SMALL_INT(fatfs->csize * fatfs->ssize); // f_bsize
- #else
- t->items[0] = MP_OBJ_NEW_SMALL_INT(fatfs->csize * _MIN_SS); // f_bsize
- #endif
+ t->items[0] = MP_OBJ_NEW_SMALL_INT(fatfs->csize * SECSIZE(fatfs)); // f_bsize
t->items[1] = t->items[0]; // f_frsize
- t->items[2] = MP_OBJ_NEW_SMALL_INT((fatfs->n_fatent - 2) * fatfs->csize); // f_blocks
+ t->items[2] = MP_OBJ_NEW_SMALL_INT((fatfs->n_fatent - 2)); // f_blocks
t->items[3] = MP_OBJ_NEW_SMALL_INT(nclst); // f_bfree
t->items[4] = t->items[3]; // f_bavail
t->items[5] = MP_OBJ_NEW_SMALL_INT(0); // f_files
@@ -303,17 +281,47 @@ STATIC mp_obj_t fat_vfs_statvfs(mp_obj_t vfs_in, mp_obj_t path_in) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_statvfs_obj, fat_vfs_statvfs);
-// Unmount the filesystem
-STATIC mp_obj_t fat_vfs_umount(mp_obj_t vfs_in) {
- fatfs_umount(((fs_user_mount_t *)vfs_in)->readblocks[1]);
+STATIC mp_obj_t vfs_fat_mount(mp_obj_t self_in, mp_obj_t readonly, mp_obj_t mkfs) {
+ fs_user_mount_t *self = MP_OBJ_TO_PTR(self_in);
+
+ // Read-only device indicated by writeblocks[0] == MP_OBJ_NULL.
+ // User can specify read-only device by:
+ // 1. readonly=True keyword argument
+ // 2. nonexistent writeblocks method (then writeblocks[0] == MP_OBJ_NULL already)
+ if (mp_obj_is_true(readonly)) {
+ self->writeblocks[0] = MP_OBJ_NULL;
+ }
+
+ // mount the block device
+ FRESULT res = f_mount(&self->fatfs);
+
+ // check if we need to make the filesystem
+ if (res == FR_NO_FILESYSTEM && mp_obj_is_true(mkfs)) {
+ uint8_t working_buf[_MAX_SS];
+ res = f_mkfs(&self->fatfs, FM_FAT | FM_SFD, 0, working_buf, sizeof(working_buf));
+ }
+ if (res != FR_OK) {
+ mp_raise_OSError(fresult_to_errno_table[res]);
+ }
+
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_3(vfs_fat_mount_obj, vfs_fat_mount);
+
+STATIC mp_obj_t vfs_fat_umount(mp_obj_t self_in) {
+ fs_user_mount_t *self = MP_OBJ_TO_PTR(self_in);
+ FRESULT res = f_umount(&self->fatfs);
+ if (res != FR_OK) {
+ mp_raise_OSError(fresult_to_errno_table[res]);
+ }
return mp_const_none;
}
-STATIC MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_umount_obj, fat_vfs_umount);
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_umount_obj, vfs_fat_umount);
STATIC const mp_rom_map_elem_t fat_vfs_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_mkfs), MP_ROM_PTR(&fat_vfs_mkfs_obj) },
{ MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&fat_vfs_open_obj) },
- { MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&fat_vfs_listdir_obj) },
+ { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&fat_vfs_ilistdir_obj) },
{ MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&fat_vfs_mkdir_obj) },
{ MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&fat_vfs_rmdir_obj) },
{ MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&fat_vfs_chdir_obj) },
@@ -322,6 +330,7 @@ STATIC const mp_rom_map_elem_t fat_vfs_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&fat_vfs_rename_obj) },
{ MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&fat_vfs_stat_obj) },
{ MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&fat_vfs_statvfs_obj) },
+ { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&vfs_fat_mount_obj) },
{ MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&fat_vfs_umount_obj) },
};
STATIC MP_DEFINE_CONST_DICT(fat_vfs_locals_dict, fat_vfs_locals_dict_table);
diff --git a/extmod/fsusermount.h b/extmod/vfs_fat.h
index abfd2d4e3..90d66443e 100644
--- a/extmod/fsusermount.h
+++ b/extmod/vfs_fat.h
@@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
- * Copyright (c) 2014 Damien P. George
+ * Copyright (c) 2013, 2014 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -24,12 +24,12 @@
* THE SOFTWARE.
*/
-
-#ifndef __MICROPY_INCLUDED_EXTMOD_FSUSERMOUNT_H__
-#define __MICROPY_INCLUDED_EXTMOD_FSUSERMOUNT_H__
-
-#include "lib/fatfs/ff.h"
+#ifndef __MICROPY_INCLUDED_EXTMOD_VFS_FAT_H__
+#define __MICROPY_INCLUDED_EXTMOD_VFS_FAT_H__
+#include "py/lexer.h"
#include "py/obj.h"
+#include "lib/oofatfs/ff.h"
+#include "extmod/vfs.h"
// these are the values for fs_user_mount_t.flags
#define FSUSER_NATIVE (0x0001) // readblocks[2]/writeblocks[2] contain native func
@@ -38,17 +38,8 @@
// Device is write-able over USB and read-only to MicroPython.
#define FSUSER_USB_WRITEABLE (0x0008)
-// constants for block protocol ioctl
-#define BP_IOCTL_INIT (1)
-#define BP_IOCTL_DEINIT (2)
-#define BP_IOCTL_SYNC (3)
-#define BP_IOCTL_SEC_COUNT (4)
-#define BP_IOCTL_SEC_SIZE (5)
-
typedef struct _fs_user_mount_t {
mp_obj_base_t base;
- const char *str;
- uint16_t len; // length of str
uint16_t flags;
mp_obj_t readblocks[4];
mp_obj_t writeblocks[4];
@@ -63,11 +54,17 @@ typedef struct _fs_user_mount_t {
FATFS fatfs;
} fs_user_mount_t;
-fs_user_mount_t *fatfs_mount_mkfs(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args, bool mkfs);
-mp_obj_t fatfs_umount(mp_obj_t bdev_or_path_in);
+extern const byte fresult_to_errno_table[20];
+extern const mp_obj_type_t mp_fat_vfs_type;
+
+mp_import_stat_t fat_vfs_import_stat(struct _fs_user_mount_t *vfs, const char *path);
+mp_obj_t fatfs_builtin_open_self(mp_obj_t self_in, mp_obj_t path, mp_obj_t mode);
+MP_DECLARE_CONST_FUN_OBJ_KW(mp_builtin_open_obj);
+
+mp_obj_t fat_vfs_ilistdir2(struct _fs_user_mount_t *vfs, const char *path, bool is_str_type);
MP_DECLARE_CONST_FUN_OBJ_KW(fsuser_mount_obj);
MP_DECLARE_CONST_FUN_OBJ_1(fsuser_umount_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(fsuser_mkfs_obj);
-#endif // __MICROPY_INCLUDED_EXTMOD_FSUSERMOUNT_H__
+#endif // __MICROPY_INCLUDED_EXTMOD_VFS_FAT_H__
diff --git a/extmod/vfs_fat_diskio.c b/extmod/vfs_fat_diskio.c
index 4d776f6b4..282599ffc 100644
--- a/extmod/vfs_fat_diskio.c
+++ b/extmod/vfs_fat_diskio.c
@@ -28,7 +28,7 @@
*/
#include "py/mpconfig.h"
-#if MICROPY_FSUSERMOUNT
+#if MICROPY_VFS && MICROPY_VFS_FAT
#include <stdint.h>
#include <stdio.h>
@@ -36,9 +36,9 @@
#include "py/mphal.h"
#include "py/runtime.h"
-#include "lib/fatfs/ff.h" /* FatFs lower layer API */
-#include "lib/fatfs/diskio.h" /* FatFs lower layer API */
-#include "extmod/fsusermount.h"
+#include "lib/oofatfs/ff.h"
+#include "lib/oofatfs/diskio.h"
+#include "extmod/vfs_fat.h"
#if _MAX_SS == _MIN_SS
#define SECSIZE(fs) (_MIN_SS)
@@ -46,20 +46,18 @@
#define SECSIZE(fs) ((fs)->ssize)
#endif
-STATIC fs_user_mount_t *disk_get_device(uint id) {
- if (id < MP_ARRAY_SIZE(MP_STATE_PORT(fs_user_mount))) {
- return MP_STATE_PORT(fs_user_mount)[id];
- } else {
- return NULL;
- }
+typedef void *bdev_t;
+STATIC fs_user_mount_t *disk_get_device(void *bdev) {
+ return (fs_user_mount_t*)bdev;
}
/*-----------------------------------------------------------------------*/
/* Initialize a Drive */
/*-----------------------------------------------------------------------*/
+STATIC
DSTATUS disk_initialize (
- BYTE pdrv /* Physical drive nmuber (0..) */
+ bdev_t pdrv /* Physical drive nmuber (0..) */
)
{
fs_user_mount_t *vfs = disk_get_device(pdrv);
@@ -89,8 +87,9 @@ DSTATUS disk_initialize (
/* Get Disk Status */
/*-----------------------------------------------------------------------*/
+STATIC
DSTATUS disk_status (
- BYTE pdrv /* Physical drive number (0..) */
+ bdev_t pdrv /* Physical drive nmuber (0..) */
)
{
fs_user_mount_t *vfs = disk_get_device(pdrv);
@@ -113,7 +112,7 @@ DSTATUS disk_status (
/*-----------------------------------------------------------------------*/
DRESULT disk_read (
- BYTE pdrv, /* Physical drive number (0..) */
+ bdev_t pdrv, /* Physical drive nmuber (0..) */
BYTE *buff, /* Data buffer to store read data */
DWORD sector, /* Sector address (LBA) */
UINT count /* Number of sectors to read (1..128) */
@@ -143,9 +142,8 @@ DRESULT disk_read (
/* Write Sector(s) */
/*-----------------------------------------------------------------------*/
-#if _USE_WRITE
DRESULT disk_write (
- BYTE pdrv, /* Physical drive nmuber (0..) */
+ bdev_t pdrv, /* Physical drive nmuber (0..) */
const BYTE *buff, /* Data to be written */
DWORD sector, /* Sector address (LBA) */
UINT count /* Number of sectors to write (1..128) */
@@ -175,16 +173,14 @@ DRESULT disk_write (
return RES_OK;
}
-#endif
/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions */
/*-----------------------------------------------------------------------*/
-#if _USE_IOCTL
DRESULT disk_ioctl (
- BYTE pdrv, /* Physical drive nmuber (0..) */
+ bdev_t pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
@@ -221,6 +217,10 @@ DRESULT disk_ioctl (
} else {
*((WORD*)buff) = mp_obj_get_int(ret);
}
+ #if _MAX_SS != _MIN_SS
+ // need to store ssize because we use it in disk_read/disk_write
+ vfs->fatfs.ssize = *((WORD*)buff);
+ #endif
return RES_OK;
}
@@ -228,6 +228,14 @@ DRESULT disk_ioctl (
*((DWORD*)buff) = 1; // erase block size in units of sector size
return RES_OK;
+ case IOCTL_INIT:
+ *((DSTATUS*)buff) = disk_initialize(pdrv);
+ return RES_OK;
+
+ case IOCTL_STATUS:
+ *((DSTATUS*)buff) = disk_status(pdrv);
+ return RES_OK;
+
default:
return RES_PARERR;
}
@@ -248,17 +256,28 @@ DRESULT disk_ioctl (
case GET_SECTOR_SIZE:
*((WORD*)buff) = 512; // old protocol had fixed sector size
+ #if _MAX_SS != _MIN_SS
+ // need to store ssize because we use it in disk_read/disk_write
+ vfs->fatfs.ssize = 512;
+ #endif
return RES_OK;
case GET_BLOCK_SIZE:
*((DWORD*)buff) = 1; // erase block size in units of sector size
return RES_OK;
+ case IOCTL_INIT:
+ *((DSTATUS*)buff) = disk_initialize(pdrv);
+ return RES_OK;
+
+ case IOCTL_STATUS:
+ *((DSTATUS*)buff) = disk_status(pdrv);
+ return RES_OK;
+
default:
return RES_PARERR;
}
}
}
-#endif
-#endif // MICROPY_FSUSERMOUNT
+#endif // MICROPY_VFS && MICROPY_VFS_FAT
diff --git a/extmod/vfs_fat_ffconf.c b/extmod/vfs_fat_ffconf.c
deleted file mode 100644
index f8935af75..000000000
--- a/extmod/vfs_fat_ffconf.c
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * This file is part of the Micro Python project, http://micropython.org/
- *
- * The MIT License (MIT)
- *
- * Copyright (c) 2013, 2014 Damien P. George
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "py/mpconfig.h"
-#if MICROPY_FSUSERMOUNT
-
-#include <string.h>
-
-#include "py/mpstate.h"
-#include "lib/fatfs/ff.h"
-#include "lib/fatfs/ffconf.h"
-#include "lib/fatfs/diskio.h"
-#include "extmod/fsusermount.h"
-
-STATIC bool check_path(const TCHAR **path, const char *mount_point_str, mp_uint_t mount_point_len) {
- if (strncmp(*path, mount_point_str, mount_point_len) == 0) {
- if ((*path)[mount_point_len] == '/') {
- *path += mount_point_len;
- return true;
- } else if ((*path)[mount_point_len] == '\0') {
- *path = "/";
- return true;
- }
- }
- return false;
-}
-
-// "path" is the path to lookup; will advance this pointer beyond the volume name.
-// Returns logical drive number (-1 means invalid path).
-int ff_get_ldnumber (const TCHAR **path) {
- if (!(*path)) {
- return -1;
- }
-
- if (**path != '/') {
- #if _FS_RPATH
- return ff_CurrVol;
- #else
- return -1;
- #endif
- }
-
- for (size_t i = 0; i < MP_ARRAY_SIZE(MP_STATE_PORT(fs_user_mount)); ++i) {
- fs_user_mount_t *vfs = MP_STATE_PORT(fs_user_mount)[i];
- if (vfs != NULL && check_path(path, vfs->str, vfs->len)) {
- return i;
- }
- }
-
- return -1;
-}
-
-void ff_get_volname(BYTE vol, TCHAR **dest) {
- fs_user_mount_t *vfs = MP_STATE_PORT(fs_user_mount)[vol];
- memcpy(*dest, vfs->str, vfs->len);
- *dest += vfs->len;
-}
-
-#endif // MICROPY_FSUSERMOUNT
diff --git a/extmod/vfs_fat_file.c b/extmod/vfs_fat_file.c
index ef57225a9..9c166e163 100644
--- a/extmod/vfs_fat_file.c
+++ b/extmod/vfs_fat_file.c
@@ -24,11 +24,10 @@
* THE SOFTWARE.
*/
-#include "extmod/vfs_fat_file.h"
+#include "py/mpconfig.h"
+#if MICROPY_VFS && MICROPY_VFS_FAT
-// *_ADHOC part is for cc3200 port which doesn't use general uPy
-// infrastructure and instead duplicates code. TODO: Resolve.
-#if MICROPY_FSUSERMOUNT || MICROPY_FSUSERMOUNT_ADHOC
+#include "extmod/vfs_fat_file.h"
#include <stdio.h>
#include <errno.h>
@@ -37,7 +36,8 @@
#include "py/runtime.h"
#include "py/stream.h"
#include "py/mperrno.h"
-#include "lib/fatfs/ff.h"
+#include "lib/oofatfs/ff.h"
+#include "extmod/vfs_fat.h"
// this table converts from FRESULT to POSIX errno
const byte fresult_to_errno_table[20] = {
@@ -99,7 +99,7 @@ STATIC mp_uint_t file_obj_write(mp_obj_t self_in, const void *buf, mp_uint_t siz
STATIC mp_obj_t file_obj_close(mp_obj_t self_in) {
pyb_file_obj_t *self = MP_OBJ_TO_PTR(self_in);
// if fs==NULL then the file is closed and in that case this method is a no-op
- if (self->fp.fs != NULL) {
+ if (self->fp.obj.fs != NULL) {
FRESULT res = f_close(&self->fp);
if (res != FR_OK) {
mp_raise_OSError(fresult_to_errno_table[res]);
@@ -165,7 +165,7 @@ STATIC const mp_arg_t file_open_args[] = {
};
#define FILE_OPEN_NUM_ARGS MP_ARRAY_SIZE(file_open_args)
-STATIC mp_obj_t file_open(const mp_obj_type_t *type, mp_arg_val_t *args) {
+STATIC mp_obj_t file_open(fs_user_mount_t *vfs, const mp_obj_type_t *type, mp_arg_val_t *args) {
int mode = 0;
const char *mode_s = mp_obj_str_get_str(args[1].u_obj);
// TODO make sure only one of r, w, x, a, and b, t are specified
@@ -201,7 +201,8 @@ STATIC mp_obj_t file_open(const mp_obj_type_t *type, mp_arg_val_t *args) {
o->base.type = type;
const char *fname = mp_obj_str_get_str(args[0].u_obj);
- FRESULT res = f_open(&o->fp, fname, mode);
+ assert(vfs != NULL);
+ FRESULT res = f_open(&vfs->fatfs, &o->fp, fname, mode);
if (res != FR_OK) {
m_del_obj(pyb_file_obj_t, o);
mp_raise_OSError(fresult_to_errno_table[res]);
@@ -218,7 +219,7 @@ STATIC mp_obj_t file_open(const mp_obj_type_t *type, mp_arg_val_t *args) {
STATIC mp_obj_t file_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_val_t arg_vals[FILE_OPEN_NUM_ARGS];
mp_arg_parse_all_kw_array(n_args, n_kw, args, FILE_OPEN_NUM_ARGS, file_open_args, arg_vals);
- return file_open(type, arg_vals);
+ return file_open(NULL, type, arg_vals);
}
// TODO gc hook to close the file if not already closed
@@ -252,7 +253,7 @@ const mp_obj_type_t mp_type_fileio = {
.name = MP_QSTR_FileIO,
.print = file_obj_print,
.make_new = file_obj_make_new,
- .getiter = mp_identity,
+ .getiter = mp_identity_getiter,
.iternext = mp_stream_unbuffered_iter,
.protocol = &fileio_stream_p,
.locals_dict = (mp_obj_dict_t*)&rawfile_locals_dict,
@@ -271,18 +272,21 @@ const mp_obj_type_t mp_type_textio = {
.name = MP_QSTR_TextIOWrapper,
.print = file_obj_print,
.make_new = file_obj_make_new,
- .getiter = mp_identity,
+ .getiter = mp_identity_getiter,
.iternext = mp_stream_unbuffered_iter,
.protocol = &textio_stream_p,
.locals_dict = (mp_obj_dict_t*)&rawfile_locals_dict,
};
// Factory function for I/O stream classes
-mp_obj_t fatfs_builtin_open(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
+mp_obj_t fatfs_builtin_open_self(mp_obj_t self_in, mp_obj_t path, mp_obj_t mode) {
// TODO: analyze buffering args and instantiate appropriate type
+ fs_user_mount_t *self = MP_OBJ_TO_PTR(self_in);
mp_arg_val_t arg_vals[FILE_OPEN_NUM_ARGS];
- mp_arg_parse_all(n_args, args, kwargs, FILE_OPEN_NUM_ARGS, file_open_args, arg_vals);
- return file_open(&mp_type_textio, arg_vals);
+ arg_vals[0].u_obj = path;
+ arg_vals[1].u_obj = mode;
+ arg_vals[2].u_obj = mp_const_none;
+ return file_open(self, &mp_type_textio, arg_vals);
}
-#endif // MICROPY_FSUSERMOUNT
+#endif // MICROPY_VFS && MICROPY_VFS_FAT
diff --git a/extmod/vfs_fat_file.h b/extmod/vfs_fat_file.h
index 5dc627986..96efe9a7c 100644
--- a/extmod/vfs_fat_file.h
+++ b/extmod/vfs_fat_file.h
@@ -27,18 +27,15 @@
#ifndef __MICROPY_INCLUDED_EXTMOD_VFS_FAT_FILE_H__
#define __MICROPY_INCLUDED_EXTMOD_VFS_FAT_FILE_H__
-#include "lib/fatfs/ff.h"
#include "py/mpconfig.h"
-#include "py/obj.h"
-#if MICROPY_FSUSERMOUNT || MICROPY_FSUSERMOUNT_ADHOC
+#if MICROPY_VFS && MICROPY_VFS_FAT
-extern const byte fresult_to_errno_table[20];
+#include "lib/oofatfs/ff.h"
+#include "py/obj.h"
-#if MICROPY_VFS_FAT
#define mp_type_fileio fatfs_type_fileio
#define mp_type_textio fatfs_type_textio
-#endif
extern const mp_obj_type_t mp_type_fileio;
extern const mp_obj_type_t mp_type_textio;
@@ -48,11 +45,6 @@ typedef struct _pyb_file_obj_t {
FIL fp;
} pyb_file_obj_t;
-mp_obj_t fatfs_builtin_open(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kwargs);
-MP_DECLARE_CONST_FUN_OBJ_KW(mp_builtin_open_obj);
-
-mp_obj_t fat_vfs_listdir(const char *path, bool is_str_type);
-
-#endif // MICROPY_FSUSERMOUNT || MICROPY_FSUSERMOUNT_ADHOC
+#endif // MICROPY_VFS && MICROPY_VFS_FAT
#endif // __MICROPY_INCLUDED_EXTMOD_VFS_FAT_FILE_H__
diff --git a/extmod/vfs_fat_misc.c b/extmod/vfs_fat_misc.c
index d3507a85f..7c16db7e5 100644
--- a/extmod/vfs_fat_misc.c
+++ b/extmod/vfs_fat_misc.c
@@ -25,86 +25,77 @@
*/
#include "py/mpconfig.h"
-// *_ADHOC part is for cc3200 port which doesn't use general uPy
-// infrastructure and instead duplicates code. TODO: Resolve.
-#if MICROPY_VFS_FAT || MICROPY_FSUSERMOUNT || MICROPY_FSUSERMOUNT_ADHOC
+#if MICROPY_VFS_FAT
#include <string.h>
#include "py/nlr.h"
#include "py/runtime.h"
-#include "lib/fatfs/ff.h"
-#include "lib/fatfs/diskio.h"
-#include "extmod/vfs_fat_file.h"
-#include "extmod/fsusermount.h"
+#include "lib/oofatfs/ff.h"
+#include "extmod/vfs_fat.h"
#include "py/lexer.h"
-#if _USE_LFN
-STATIC char lfn[_MAX_LFN + 1]; /* Buffer to store the LFN */
-#endif
+typedef struct _mp_vfs_fat_ilistdir_it_t {
+ mp_obj_base_t base;
+ mp_fun_1_t iternext;
+ bool is_str;
+ FF_DIR dir;
+} mp_vfs_fat_ilistdir_it_t;
-// TODO: actually, the core function should be ilistdir()
-mp_obj_t fat_vfs_listdir(const char *path, bool is_str_type) {
- FRESULT res;
- FILINFO fno;
- DIR dir;
-#if _USE_LFN
- fno.lfname = lfn;
- fno.lfsize = sizeof lfn;
-#endif
-
- res = f_opendir(&dir, path); /* Open the directory */
- if (res != FR_OK) {
- mp_raise_OSError(fresult_to_errno_table[res]);
- }
-
- mp_obj_t dir_list = mp_obj_new_list(0, NULL);
+STATIC mp_obj_t mp_vfs_fat_ilistdir_it_iternext(mp_obj_t self_in) {
+ mp_vfs_fat_ilistdir_it_t *self = MP_OBJ_TO_PTR(self_in);
for (;;) {
- res = f_readdir(&dir, &fno); /* Read a directory item */
- if (res != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */
- if (fno.fname[0] == '.' && fno.fname[1] == 0) continue; /* Ignore . entry */
- if (fno.fname[0] == '.' && fno.fname[1] == '.' && fno.fname[2] == 0) continue; /* Ignore .. entry */
-
-#if _USE_LFN
- char *fn = *fno.lfname ? fno.lfname : fno.fname;
-#else
+ FILINFO fno;
+ FRESULT res = f_readdir(&self->dir, &fno);
char *fn = fno.fname;
-#endif
+ if (res != FR_OK || fn[0] == 0) {
+ // stop on error or end of dir
+ break;
+ }
+
+ // Note that FatFS already filters . and .., so we don't need to
- /*
+ // make 3-tuple with info about this entry
+ mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL));
+ if (self->is_str) {
+ t->items[0] = mp_obj_new_str(fn, strlen(fn), false);
+ } else {
+ t->items[0] = mp_obj_new_bytes((const byte*)fn, strlen(fn));
+ }
if (fno.fattrib & AM_DIR) {
// dir
+ t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFDIR);
} else {
// file
+ t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFREG);
}
- */
-
- // make a string object for this entry
- mp_obj_t entry_o;
- if (is_str_type) {
- entry_o = mp_obj_new_str(fn, strlen(fn), false);
- } else {
- entry_o = mp_obj_new_bytes((const byte*)fn, strlen(fn));
- }
+ t->items[2] = MP_OBJ_NEW_SMALL_INT(0); // no inode number
- // add the entry to the list
- mp_obj_list_append(dir_list, entry_o);
+ return MP_OBJ_FROM_PTR(t);
}
- f_closedir(&dir);
+ // ignore error because we may be closing a second time
+ f_closedir(&self->dir);
- return dir_list;
+ return MP_OBJ_STOP_ITERATION;
}
-mp_import_stat_t fat_vfs_import_stat(const char *path);
+mp_obj_t fat_vfs_ilistdir2(fs_user_mount_t *vfs, const char *path, bool is_str_type) {
+ mp_vfs_fat_ilistdir_it_t *iter = m_new_obj(mp_vfs_fat_ilistdir_it_t);
+ iter->base.type = &mp_type_polymorph_iter;
+ iter->iternext = mp_vfs_fat_ilistdir_it_iternext;
+ iter->is_str = is_str_type;
+ FRESULT res = f_opendir(&vfs->fatfs, &iter->dir, path);
+ if (res != FR_OK) {
+ mp_raise_OSError(fresult_to_errno_table[res]);
+ }
+ return MP_OBJ_FROM_PTR(iter);
+}
-mp_import_stat_t fat_vfs_import_stat(const char *path) {
+mp_import_stat_t fat_vfs_import_stat(fs_user_mount_t *vfs, const char *path) {
FILINFO fno;
-#if _USE_LFN
- fno.lfname = NULL;
- fno.lfsize = 0;
-#endif
- FRESULT res = f_stat(path, &fno);
+ assert(vfs != NULL);
+ FRESULT res = f_stat(&vfs->fatfs, path, &fno);
if (res == FR_OK) {
if ((fno.fattrib & AM_DIR) != 0) {
return MP_IMPORT_STAT_DIR;
diff --git a/extmod/vfs_fat_reader.c b/extmod/vfs_reader.c
index 7a00f18de..891098aa1 100644
--- a/extmod/vfs_fat_reader.c
+++ b/extmod/vfs_reader.c
@@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
- * Copyright (c) 2013-2016 Damien P. George
+ * Copyright (c) 2013-2017 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -25,64 +25,63 @@
*/
#include <stdio.h>
-#include <assert.h>
+#include <string.h>
-#include "py/mperrno.h"
+#include "py/runtime.h"
+#include "py/stream.h"
#include "py/reader.h"
+#include "extmod/vfs.h"
-#if MICROPY_READER_FATFS
+#if MICROPY_READER_VFS
-#include "lib/fatfs/ff.h"
-#include "extmod/vfs_fat_file.h"
-
-typedef struct _mp_reader_fatfs_t {
- FIL fp;
+typedef struct _mp_reader_vfs_t {
+ mp_obj_t file;
uint16_t len;
uint16_t pos;
- byte buf[20];
-} mp_reader_fatfs_t;
+ byte buf[24];
+} mp_reader_vfs_t;
-STATIC mp_uint_t mp_reader_fatfs_readbyte(void *data) {
- mp_reader_fatfs_t *reader = (mp_reader_fatfs_t*)data;
+STATIC mp_uint_t mp_reader_vfs_readbyte(void *data) {
+ mp_reader_vfs_t *reader = (mp_reader_vfs_t*)data;
if (reader->pos >= reader->len) {
if (reader->len < sizeof(reader->buf)) {
return MP_READER_EOF;
} else {
- UINT n;
- f_read(&reader->fp, reader->buf, sizeof(reader->buf), &n);
- if (n == 0) {
+ int errcode;
+ reader->len = mp_stream_rw(reader->file, reader->buf, sizeof(reader->buf),
+ &errcode, MP_STREAM_RW_READ | MP_STREAM_RW_ONCE);
+ if (errcode != 0) {
+ // TODO handle errors properly
+ return MP_READER_EOF;
+ }
+ if (reader->len == 0) {
return MP_READER_EOF;
}
- reader->len = n;
reader->pos = 0;
}
}
return reader->buf[reader->pos++];
}
-STATIC void mp_reader_fatfs_close(void *data) {
- mp_reader_fatfs_t *reader = (mp_reader_fatfs_t*)data;
- f_close(&reader->fp);
- m_del_obj(mp_reader_fatfs_t, reader);
+STATIC void mp_reader_vfs_close(void *data) {
+ mp_reader_vfs_t *reader = (mp_reader_vfs_t*)data;
+ mp_stream_close(reader->file);
+ m_del_obj(mp_reader_vfs_t, reader);
}
-int mp_reader_new_file(mp_reader_t *reader, const char *filename) {
- mp_reader_fatfs_t *rf = m_new_obj_maybe(mp_reader_fatfs_t);
- if (rf == NULL) {
- return MP_ENOMEM;
- }
- FRESULT res = f_open(&rf->fp, filename, FA_READ);
- if (res != FR_OK) {
- return fresult_to_errno_table[res];
+void mp_reader_new_file(mp_reader_t *reader, const char *filename) {
+ mp_reader_vfs_t *rf = m_new_obj(mp_reader_vfs_t);
+ mp_obj_t arg = mp_obj_new_str(filename, strlen(filename), false);
+ rf->file = mp_vfs_open(1, &arg, (mp_map_t*)&mp_const_empty_map);
+ int errcode;
+ rf->len = mp_stream_rw(rf->file, rf->buf, sizeof(rf->buf), &errcode, MP_STREAM_RW_READ | MP_STREAM_RW_ONCE);
+ if (errcode != 0) {
+ mp_raise_OSError(errcode);
}
- UINT n;
- f_read(&rf->fp, rf->buf, sizeof(rf->buf), &n);
- rf->len = n;
rf->pos = 0;
reader->data = rf;
- reader->readbyte = mp_reader_fatfs_readbyte;
- reader->close = mp_reader_fatfs_close;
- return 0; // success
+ reader->readbyte = mp_reader_vfs_readbyte;
+ reader->close = mp_reader_vfs_close;
}
-#endif
+#endif // MICROPY_READER_VFS
diff --git a/extmod/virtpin.h b/extmod/virtpin.h
index 3821f9dec..041010350 100644
--- a/extmod/virtpin.h
+++ b/extmod/virtpin.h
@@ -38,3 +38,6 @@ typedef struct _mp_pin_p_t {
int mp_virtual_pin_read(mp_obj_t pin);
void mp_virtual_pin_write(mp_obj_t pin, int value);
+
+// If a port exposes a Pin object, it's constructor should be like this
+mp_obj_t mp_pin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args);