diff options
Diffstat (limited to 'cc3200/mods/moduos.c')
-rw-r--r-- | cc3200/mods/moduos.c | 421 |
1 files changed, 293 insertions, 128 deletions
diff --git a/cc3200/mods/moduos.c b/cc3200/mods/moduos.c index 0d62a96e85..8dc7716b56 100644 --- a/cc3200/mods/moduos.c +++ b/cc3200/mods/moduos.c @@ -28,21 +28,22 @@ #include <stdint.h> #include <string.h> -#include "py/mpconfig.h" +#include "py/mpstate.h" #include "py/nlr.h" -#include "py/obj.h" #include "py/objtuple.h" #include "py/objstr.h" +#include "py/runtime.h" #include "genhdr/mpversion.h" #include "ff.h" #include "diskio.h" #include "sflash_diskio.h" #include "file.h" #include "random.h" -#include "pybsd.h" #include "mpexception.h" #include "version.h" #include "timeutils.h" +#include "moduos.h" +#include "pybsd.h" /// \module os - basic "operating system" services /// @@ -52,15 +53,155 @@ /// drives are accessible from here. They are currently: /// /// /flash -- the serial flash filesystem -/// /sd -- the SD card (if it exists) /// -/// On boot up, the current directory is `/flash` if no SD card is inserted, -/// otherwise it is `/sd`. +/// On boot up, the current directory is `/flash`. + +/****************************************************************************** + DECLARE PRIVATE DATA + ******************************************************************************/ +STATIC uint32_t os_num_mounted_devices; + +/****************************************************************************** + DEFINE PUBLIC FUNCTIONS + ******************************************************************************/ + +void moduos_init0 (void) { + // initialize the mount objects list + mp_obj_list_init(&MP_STATE_PORT(mount_obj_list), 0); + os_num_mounted_devices = 0; +} + +os_fs_mount_t *osmount_find_by_path (const char *path) { + for (mp_uint_t i = 0; i < MP_STATE_PORT(mount_obj_list).len; i++) { + os_fs_mount_t *mount_obj = ((os_fs_mount_t *)(MP_STATE_PORT(mount_obj_list).items[i])); + if (!strcmp(path, mount_obj->path)) { + return mount_obj; + } + } + return NULL; +} + +os_fs_mount_t *osmount_find_by_volume (uint8_t vol) { + for (mp_uint_t i = 0; i < MP_STATE_PORT(mount_obj_list).len; i++) { + os_fs_mount_t *mount_obj = ((os_fs_mount_t *)(MP_STATE_PORT(mount_obj_list).items[i])); + if (vol == mount_obj->vol) { + return mount_obj; + } + } + return NULL; +} + +os_fs_mount_t *osmount_find_by_device (mp_obj_t device) { + for (mp_uint_t i = 0; i < MP_STATE_PORT(mount_obj_list).len; i++) { + os_fs_mount_t *mount_obj = ((os_fs_mount_t *)(MP_STATE_PORT(mount_obj_list).items[i])); + if (device == mount_obj->device) { + return mount_obj; + } + } + return NULL; +} /****************************************************************************** DEFINE PRIVATE FUNCTIONS ******************************************************************************/ +// Checks for path equality, ignoring trailing slashes: +// path_equal(/, /) -> true +// path_equal(/flash//, /flash) -> 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) { + for (; *path_canonical != '\0' && *path == *path_canonical; ++path, ++path_canonical) { + } + if (*path_canonical != '\0') { + return false; + } + for (; *path == '/'; ++path) { + } + return *path == '\0'; +} + +STATIC void append_dir_item (mp_obj_t dirlist, const char *item, bool string) { + // make a string object for this entry + mp_obj_t entry_o; + if (string) { + entry_o = mp_obj_new_str(item, strlen(item), false); + } else { + entry_o = mp_obj_new_bytes((const byte*)item, strlen(item)); + } + + // add the entry to the list + mp_obj_list_append(dirlist, entry_o); +} + +STATIC void mount (mp_obj_t device, const char *path, uint pathlen, bool readonly) { + // is the mount point already in use? + FILINFO fno; +#if _USE_LFN + fno.lfname = NULL; + fno.lfsize = 0; +#endif + // cannot mount twice or on existing paths + if (f_stat(path, &fno) == FR_OK || osmount_find_by_device(device)) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, mpexception_os_request_not_possible)); + } + + // create a new object + os_fs_mount_t *self = m_new_obj(os_fs_mount_t); + self->device = device; + self->path = path; + self->pathlen = pathlen; + self->vol = os_num_mounted_devices + 1; // '/flash' is volume 0 + + if (device == (mp_obj_t)&pybsd_obj) { + // need to make it different to NULL, otherwise it's read only by default + self->writeblocks[0] = mp_const_none; + self->sync[0] = MP_OBJ_NULL; // no need to sync the SD card + self->count[0] = MP_OBJ_NULL; + } else { + // load block protocol methods + mp_load_method(device, MP_QSTR_readblocks, self->readblocks); + mp_load_method_maybe(device, MP_QSTR_writeblocks, self->writeblocks); + mp_load_method_maybe(device, MP_QSTR_sync, self->sync); + mp_load_method(device, MP_QSTR_count, self->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 (readonly) { + self->writeblocks[0] = MP_OBJ_NULL; + } + + // we need to add it before doing the actual mount, so that the volume can be found + mp_obj_list_append(&MP_STATE_PORT(mount_obj_list), self); + + // actually mount it + if (f_mount(&self->fatfs, self->path, 1) != FR_OK) { + // remove it and raise + mp_obj_list_remove(&MP_STATE_PORT(mount_obj_list), self); + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, mpexception_os_operation_failed)); + } + + // mount succeeded, increment the count + os_num_mounted_devices++; +} + +STATIC void unmount (const char *path) { + if (FR_OK != f_mount (NULL, path, 1)) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, mpexception_os_operation_failed)); + } + + // remove from the list after the actual unmount + os_fs_mount_t *mount_obj; + if ((mount_obj = osmount_find_by_path(path))) { + mp_obj_list_remove(&MP_STATE_PORT(mount_obj_list), mount_obj); + os_num_mounted_devices--; + } else { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, mpexception_value_invalid_arguments)); + } +} + /******************************************************************************/ // Micro Python bindings // @@ -110,25 +251,20 @@ STATIC mp_obj_t os_chdir(mp_obj_t path_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_chdir_obj, os_chdir); -/// \function getcwd() -/// Get the current directory. STATIC mp_obj_t os_getcwd(void) { char buf[MICROPY_ALLOC_PATH_MAX + 1]; FRESULT res = f_getcwd(buf, sizeof buf); - if (res != FR_OK) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(fresult_to_errno_table[res]))); } - return mp_obj_new_str(buf, strlen(buf), false); } STATIC MP_DEFINE_CONST_FUN_OBJ_0(os_getcwd_obj, os_getcwd); -/// \function listdir([dir]) -/// With no argument, list the current directory. Otherwise list the given directory. STATIC mp_obj_t os_listdir(mp_uint_t n_args, const mp_obj_t *args) { bool is_str_type = true; const char *path; + mp_obj_t dir_list = mp_obj_new_list(0, NULL); if (n_args == 1) { if (mp_obj_get_type(args[0]) == &mp_type_bytes) { @@ -139,66 +275,51 @@ STATIC mp_obj_t os_listdir(mp_uint_t n_args, const mp_obj_t *args) { path = ""; } - // "hack" to list root directory + // "hack" to list the root directory if (path[0] == '/' && path[1] == '\0') { - mp_obj_t dir_list = mp_obj_new_list(0, NULL); - mp_obj_list_append(dir_list, MP_OBJ_NEW_QSTR(MP_QSTR_flash)); -#if MICROPY_HW_HAS_SDCARD - if (pybsd_is_mounted()) { - mp_obj_list_append(dir_list, MP_OBJ_NEW_QSTR(MP_QSTR_sd)); + // add 'flash' to the list + append_dir_item (dir_list, "flash", is_str_type); + for (mp_uint_t i = 0; i < MP_STATE_PORT(mount_obj_list).len; i++) { + os_fs_mount_t *mount_obj = ((os_fs_mount_t *)(MP_STATE_PORT(mount_obj_list).items[i])); + append_dir_item (dir_list, &mount_obj->path[1], is_str_type); + } + } else { + FRESULT res; + DIR dir; + FILINFO fno; + #if _USE_LFN + char lfn_buf[_MAX_LFN + 1]; + fno.lfname = lfn_buf; + fno.lfsize = sizeof(lfn_buf); + #endif + + res = f_opendir(&dir, path); /* Open the directory */ + if (res != FR_OK) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, mpexception_os_operation_failed)); } -#endif - return dir_list; - } - - FRESULT res; - DIR dir; - FILINFO fno; -#if _USE_LFN - char lfn_buf[_MAX_LFN + 1]; - fno.lfname = lfn_buf; - fno.lfsize = sizeof(lfn_buf); -#endif - - res = f_opendir(&dir, path); /* Open the directory */ - if (res != FR_OK) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, mpexception_os_operation_failed)); - } - - mp_obj_t dir_list = mp_obj_new_list(0, NULL); - 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 */ + 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 - char *fn = fno.fname; -#endif + #if _USE_LFN + char *fn = *fno.lfname ? fno.lfname : fno.fname; + #else + char *fn = fno.fname; + #endif - // 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)); + // add the entry to the list + append_dir_item (dir_list, fn, is_str_type); } - - // add the entry to the list - mp_obj_list_append(dir_list, entry_o); + f_closedir(&dir); } - f_closedir(&dir); - return dir_list; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(os_listdir_obj, 0, 1, os_listdir); -/// \function mkdir(path) -/// Create a new directory. STATIC mp_obj_t os_mkdir(mp_obj_t path_o) { const char *path = mp_obj_str_get_str(path_o); FRESULT res = f_mkdir(path); @@ -214,8 +335,6 @@ STATIC mp_obj_t os_mkdir(mp_obj_t path_o) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_mkdir_obj, os_mkdir); -/// \function rename(old_path, new_path) -/// Rename a file STATIC mp_obj_t os_rename(mp_obj_t path_in, mp_obj_t path_out) { const char *old_path = mp_obj_str_get_str(path_in); const char *new_path = mp_obj_str_get_str(path_out); @@ -226,12 +345,9 @@ STATIC mp_obj_t os_rename(mp_obj_t path_in, mp_obj_t path_out) { default: nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, mpexception_os_operation_failed)); } - } STATIC MP_DEFINE_CONST_FUN_OBJ_2(os_rename_obj, os_rename); -/// \function remove(path) -/// Remove a file or a directory STATIC mp_obj_t os_remove(mp_obj_t path_o) { const char *path = mp_obj_str_get_str(path_o); FRESULT res = f_unlink(path); @@ -244,55 +360,35 @@ STATIC mp_obj_t os_remove(mp_obj_t path_o) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_remove_obj, os_remove); -// Checks for path equality, ignoring trailing slashes: -// path_equal(/, /) -> true -// path_equal(/flash//, /flash) -> 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) { - for (; *path_canonical != '\0' && *path == *path_canonical; ++path, ++path_canonical) { - } - if (*path_canonical != '\0') { - return false; - } - for (; *path == '/'; ++path) { - } - return *path == '\0'; -} - -/// \function stat(path) -/// Get the status of a file or directory. STATIC mp_obj_t os_stat(mp_obj_t path_in) { const char *path = mp_obj_str_get_str(path_in); - - FRESULT res; + bool isbuilt_in = false; FILINFO fno; + FRESULT res; #if _USE_LFN fno.lfname = NULL; fno.lfsize = 0; #endif - if (path_equal(path, "/") || path_equal(path, "/flash") || path_equal(path, "/sd")) { - // stat built-in directory -#if MICROPY_HW_HAS_SDCARD - if (path[1] == 's' && !pybsd_is_mounted()) { -#else - if (path[1] == 's') { -#endif - // no /sd directory - res = FR_NO_PATH; - goto error; - } - fno.fsize = 0; - fno.fdate = 0; - fno.ftime = 0; - fno.fattrib = AM_DIR; - } else { - res = f_stat(path, &fno); - if (res != FR_OK) { - goto error; + // check on the user mounted devices + for (mp_uint_t i = 0; i < MP_STATE_PORT(mount_obj_list).len; i++) { + os_fs_mount_t *mount_obj = ((os_fs_mount_t *)(MP_STATE_PORT(mount_obj_list).items[i])); + if (path_equal(path, mount_obj->path)) { + isbuilt_in = true; + break; } } + if (path_equal(path, "/") || path_equal(path, "/flash") || isbuilt_in) { + // stat built-in directory + fno.fsize = 0; + fno.fdate = 0; + fno.ftime = 0; + fno.fattrib = AM_DIR; + } else if ((res = f_stat(path, &fno)) != FR_OK) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(fresult_to_errno_table[res]))); + } + mp_obj_tuple_t *t = mp_obj_new_tuple(10, NULL); mp_int_t mode = 0; if (fno.fattrib & AM_DIR) { @@ -318,26 +414,16 @@ STATIC mp_obj_t os_stat(mp_obj_t path_in) { t->items[7] = mp_obj_new_int(seconds); // st_atime t->items[8] = t->items[7]; // st_mtime t->items[9] = t->items[7]; // st_ctime - return t; - -error: - nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(fresult_to_errno_table[res]))); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_stat_obj, os_stat); -/// \function sync() -/// Sync all filesystems. STATIC mp_obj_t os_sync(void) { sflash_disk_flush(); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_0(os_sync_obj, os_sync); -#if MICROPY_HW_ENABLE_RNG -/// \function urandom(n) -/// Return a bytes object with n random bytes, generated by the hardware -/// random number generator. STATIC mp_obj_t os_urandom(mp_obj_t num) { mp_int_t n = mp_obj_get_int(num); vstr_t vstr; @@ -348,23 +434,102 @@ STATIC mp_obj_t os_urandom(mp_obj_t num) { return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_urandom_obj, os_urandom); -#endif -/// \function mkfs('drive') -/// Formats the selected drive, useful when the filesystem has been damaged beyond repair. -/// Path must be either '/sd' or '/flash' -STATIC mp_obj_t os_mkfs(mp_obj_t path_o) { +STATIC mp_obj_t os_mount(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t mount_args[] = { + { MP_QSTR_readonly, 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(mount_args)]; + mp_arg_parse_all(n_args - 2, pos_args + 2, kw_args, MP_ARRAY_SIZE(mount_args), mount_args, args); + + // get the mount point + mp_uint_t pathlen; + const char *path_in = mp_obj_str_get_data(mount_point, &pathlen); + if (pathlen == 0) { + goto invalid_args; + } + + char *path = m_new(char, pathlen + 1); + memcpy(path, path_in, pathlen); + path[pathlen] = '\0'; + + // "remove" any extra slahes at the end + while (path[(pathlen - 1)] == '/') { + path[--pathlen] = '\0'; + } + + // is the mount point valid? + if (pathlen < 2 || path[0] !='/' || strchr(&path[1], '/')) { + goto invalid_args; + } + + // now mount it + mount(device, path, pathlen, args[0].u_bool); + + return mp_const_none; + +invalid_args: + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, mpexception_value_invalid_arguments)); +} +MP_DEFINE_CONST_FUN_OBJ_KW(os_mount_obj, 2, os_mount); + +STATIC mp_obj_t os_unmount(mp_obj_t path_o) { const char *path = mp_obj_str_get_str(path_o); - uint8_t sfd; - if (!strcmp(path, "/flash")) { + // '/flash' cannot be unmounted, also not the current working directory + if (path_equal(path, "/flash")) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, mpexception_os_request_not_possible)); + } + + // now unmount it + unmount (path); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_unmount_obj, os_unmount); + +STATIC mp_obj_t os_mkfs(mp_obj_t device) { + const char *path = "/__mkfs__mnt__"; + bool unmt = false; + FRESULT res; + + if (MP_OBJ_IS_STR_OR_BYTES(device)) { + path = mp_obj_str_get_str(device); + // otherwise the relative path check will pass... + if (path[0] != '/') { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, mpexception_value_invalid_arguments)); + } + } else { + // mount it and unmount it briefly + unmt = true; + mount(device, path, strlen(path), false); + } + + byte sfd = 0; + if (!memcmp(path, "/flash", strlen("/flash"))) { sfd = 1; - } else if (!strcmp(path, "/sd")) { - sfd = 0; } else { - nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, mpexception_value_invalid_arguments)); + os_fs_mount_t *mount_obj; + if ((mount_obj = osmount_find_by_path(path))) { + if (mount_obj->device != (mp_obj_t)&pybsd_obj && + mp_obj_get_int(mp_call_method_n_kw(0, 0, mount_obj->count)) < 2048) { + sfd = 1; + } + } } - if (FR_OK != f_mkfs(path, sfd, 0)) { + + // now format the device + res = f_mkfs(path, sfd, 0); + + if (unmt) { + unmount (path); + } + + if (res != FR_OK) { nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, mpexception_os_operation_failed)); } return mp_const_none; @@ -381,13 +546,13 @@ STATIC const mp_map_elem_t os_module_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_mkdir), (mp_obj_t)&os_mkdir_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_rename), (mp_obj_t)&os_rename_obj}, { MP_OBJ_NEW_QSTR(MP_QSTR_remove), (mp_obj_t)&os_remove_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_rmdir), (mp_obj_t)&os_remove_obj }, // rmdir aliases to remove + { MP_OBJ_NEW_QSTR(MP_QSTR_rmdir), (mp_obj_t)&os_remove_obj }, // rmdir aliases to remove { MP_OBJ_NEW_QSTR(MP_QSTR_stat), (mp_obj_t)&os_stat_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_unlink), (mp_obj_t)&os_remove_obj }, // unlink aliases to remove + { MP_OBJ_NEW_QSTR(MP_QSTR_unlink), (mp_obj_t)&os_remove_obj }, // unlink aliases to remove { MP_OBJ_NEW_QSTR(MP_QSTR_sync), (mp_obj_t)&os_sync_obj }, -#if MICROPY_HW_ENABLE_RNG { MP_OBJ_NEW_QSTR(MP_QSTR_urandom), (mp_obj_t)&os_urandom_obj }, -#endif + { MP_OBJ_NEW_QSTR(MP_QSTR_mount), (mp_obj_t)&os_mount_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_unmount), (mp_obj_t)&os_unmount_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_mkfs), (mp_obj_t)&os_mkfs_obj }, /// \constant sep - separation character used in paths |