From 136b5cbd7669e8318f8455fc2706da97a5b7994c Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 12 Oct 2014 20:24:55 +0100 Subject: stmhal: Rename module files to keep consistency with module name. --- stmhal/modos.c | 369 ---------------------------------------------------- stmhal/modselect.c | 310 ------------------------------------------- stmhal/modtime.c | 362 --------------------------------------------------- stmhal/moduos.c | 369 ++++++++++++++++++++++++++++++++++++++++++++++++++++ stmhal/moduselect.c | 310 +++++++++++++++++++++++++++++++++++++++++++ stmhal/modutime.c | 362 +++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 1041 insertions(+), 1041 deletions(-) delete mode 100644 stmhal/modos.c delete mode 100644 stmhal/modselect.c delete mode 100644 stmhal/modtime.c create mode 100644 stmhal/moduos.c create mode 100644 stmhal/moduselect.c create mode 100644 stmhal/modutime.c (limited to 'stmhal') diff --git a/stmhal/modos.c b/stmhal/modos.c deleted file mode 100644 index 95446cdb0..000000000 --- a/stmhal/modos.c +++ /dev/null @@ -1,369 +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 -#include - -#include "mpconfig.h" -#include "nlr.h" -#include "misc.h" -#include "qstr.h" -#include "obj.h" -#include "objtuple.h" -#include "systick.h" -#include "rng.h" -#include "storage.h" -#include "ff.h" -#include "file.h" -#include "sdcard.h" -#include "portmodules.h" - -/// \module os - basic "operating system" services -/// -/// The `os` module contains functions for filesystem access and `urandom`. -/// -/// The filesystem has `/` as the root directory, and the available physical -/// drives are accessible from here. They are currently: -/// -/// /flash -- the internal 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`. - -#if _USE_LFN -static char lfn[_MAX_LFN + 1]; /* Buffer to store the LFN */ -#endif - -STATIC bool sd_in_root(void) { -#if MICROPY_HW_HAS_SDCARD - // TODO this is not the correct logic to check for /sd - return sdcard_is_present(); -#else - return false; -#endif -} - -/// \function chdir(path) -/// Change current directory. -STATIC mp_obj_t os_chdir(mp_obj_t path_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); - } - - if (res != FR_OK) { - // TODO should be mp_type_FileNotFoundError - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "No such file or directory: '%s'", path)); - } - - return mp_const_none; -} -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; - if (n_args == 1) { - if (mp_obj_get_type(args[0]) == &mp_type_bytes) { - is_str_type = false; - } - path = mp_obj_str_get_str(args[0]); - } else { - path = ""; - } - - // "hack" to list 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 (sd_in_root()) { - mp_obj_list_append(dir_list, MP_OBJ_NEW_QSTR(MP_QSTR_sd)); - } - return dir_list; - } - - 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) { - // TODO should be mp_type_FileNotFoundError - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "No such file or directory: '%s'", path)); - } - - 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 */ - -#if _USE_LFN - char *fn = *fno.lfname ? fno.lfname : fno.fname; -#else - char *fn = fno.fname; -#endif - - /* - if (fno.fattrib & AM_DIR) { - // dir - } else { - // file - } - */ - - // 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 - mp_obj_list_append(dir_list, entry_o); - } - - 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); - switch (res) { - case FR_OK: - return mp_const_none; - case FR_EXIST: - // TODO should be FileExistsError - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "File exists: '%s'", path)); - default: - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "Error creating directory '%s'", path)); - } -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_mkdir_obj, os_mkdir); - -/// \function remove(path) -/// Remove a file. -STATIC mp_obj_t os_remove(mp_obj_t path_o) { - const char *path = mp_obj_str_get_str(path_o); - // TODO check that path is actually a file before trying to unlink it - FRESULT res = f_unlink(path); - switch (res) { - case FR_OK: - return mp_const_none; - default: - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "Error removing file '%s'", path)); - } -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_remove_obj, os_remove); - -/// \function rmdir(path) -/// Remove a directory. -STATIC mp_obj_t os_rmdir(mp_obj_t path_o) { - const char *path = mp_obj_str_get_str(path_o); - // TODO check that path is actually a directory before trying to unlink it - FRESULT res = f_unlink(path); - switch (res) { - case FR_OK: - return mp_const_none; - default: - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "Error removing directory '%s'", path)); - } -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_rmdir_obj, os_rmdir); - -// 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); - - FILINFO fno; -#if _USE_LFN - fno.lfname = NULL; - fno.lfsize = 0; -#endif - - FRESULT res; - if (path_equal(path, "/") || path_equal(path, "/flash") || path_equal(path, "/sd")) { - // stat built-in directory - if (path[1] == 's' && !sd_in_root()) { - // 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; - } - } - - mp_obj_tuple_t *t = mp_obj_new_tuple(10, NULL); - mp_int_t mode = 0; - if (fno.fattrib & AM_DIR) { - mode |= 0x4000; // stat.S_IFDIR - } else { - mode |= 0x8000; // stat.S_IFREG - } - mp_int_t seconds = mod_time_seconds_since_2000( - 1980 + ((fno.fdate >> 9) & 0x7f), - (fno.fdate >> 5) & 0x0f, - fno.fdate & 0x1f, - (fno.ftime >> 11) & 0x1f, - (fno.ftime >> 5) & 0x3f, - 2 * (fno.ftime & 0x1f) - ); - t->items[0] = MP_OBJ_NEW_SMALL_INT(mode); // st_mode - t->items[1] = MP_OBJ_NEW_SMALL_INT(0); // st_ino - t->items[2] = MP_OBJ_NEW_SMALL_INT(0); // st_dev - t->items[3] = MP_OBJ_NEW_SMALL_INT(0); // st_nlink - t->items[4] = MP_OBJ_NEW_SMALL_INT(0); // st_uid - t->items[5] = MP_OBJ_NEW_SMALL_INT(0); // st_gid - t->items[6] = MP_OBJ_NEW_SMALL_INT(fno.fsize); // st_size - t->items[7] = MP_OBJ_NEW_SMALL_INT(seconds); // st_atime - t->items[8] = MP_OBJ_NEW_SMALL_INT(seconds); // st_mtime - t->items[9] = MP_OBJ_NEW_SMALL_INT(seconds); // 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) { - storage_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); - byte *data; - mp_obj_t o = mp_obj_str_builder_start(&mp_type_bytes, n, &data); - for (int i = 0; i < n; i++) { - data[i] = rng_get(); - } - return mp_obj_str_builder_end(o); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_urandom_obj, os_urandom); -#endif - -STATIC const mp_map_elem_t os_module_globals_table[] = { - { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_uos) }, - - { MP_OBJ_NEW_QSTR(MP_QSTR_chdir), (mp_obj_t)&os_chdir_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_getcwd), (mp_obj_t)&os_getcwd_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_listdir), (mp_obj_t)&os_listdir_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_mkdir), (mp_obj_t)&os_mkdir_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_rmdir_obj }, - { 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_sync), (mp_obj_t)&os_sync_obj }, - - /// \constant sep - separation character used in paths - { MP_OBJ_NEW_QSTR(MP_QSTR_sep), MP_OBJ_NEW_QSTR(MP_QSTR__slash_) }, - -#if MICROPY_HW_ENABLE_RNG - { MP_OBJ_NEW_QSTR(MP_QSTR_urandom), (mp_obj_t)&os_urandom_obj }, -#endif -}; - -STATIC const mp_obj_dict_t os_module_globals = { - .base = {&mp_type_dict}, - .map = { - .all_keys_are_qstrs = 1, - .table_is_fixed_array = 1, - .used = MP_ARRAY_SIZE(os_module_globals_table), - .alloc = MP_ARRAY_SIZE(os_module_globals_table), - .table = (mp_map_elem_t*)os_module_globals_table, - }, -}; - -const mp_obj_module_t mp_module_uos = { - .base = { &mp_type_module }, - .name = MP_QSTR_uos, - .globals = (mp_obj_dict_t*)&os_module_globals, -}; diff --git a/stmhal/modselect.c b/stmhal/modselect.c deleted file mode 100644 index d1bf52289..000000000 --- a/stmhal/modselect.c +++ /dev/null @@ -1,310 +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 -#include -#include - -#include "stm32f4xx_hal.h" - -#include "mpconfig.h" -#include "misc.h" -#include "nlr.h" -#include "qstr.h" -#include "obj.h" -#include "objlist.h" -#include "pybioctl.h" - -/// \module select - Provides select function to wait for events on a stream -/// -/// This module provides the select function. - -typedef struct _poll_obj_t { - mp_obj_t obj; - mp_uint_t (*ioctl)(mp_obj_t obj, mp_uint_t request, int *errcode, ...); - mp_uint_t flags; - mp_uint_t flags_ret; -} poll_obj_t; - -STATIC void poll_map_add(mp_map_t *poll_map, const mp_obj_t *obj, mp_uint_t obj_len, mp_uint_t flags, bool or_flags) { - for (mp_uint_t i = 0; i < obj_len; i++) { - mp_map_elem_t *elem = mp_map_lookup(poll_map, mp_obj_id(obj[i]), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); - if (elem->value == NULL) { - // object not found; get its ioctl and add it to the poll list - mp_obj_type_t *type = mp_obj_get_type(obj[i]); - if (type->stream_p == NULL || type->stream_p->ioctl == NULL) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "object with stream.ioctl required")); - } - poll_obj_t *poll_obj = m_new_obj(poll_obj_t); - poll_obj->obj = obj[i]; - poll_obj->ioctl = type->stream_p->ioctl; - poll_obj->flags = flags; - poll_obj->flags_ret = 0; - elem->value = poll_obj; - } else { - // object exists; update its flags - if (or_flags) { - ((poll_obj_t*)elem->value)->flags |= flags; - } else { - ((poll_obj_t*)elem->value)->flags = flags; - } - } - } -} - -// poll each object in the map -STATIC mp_uint_t poll_map_poll(mp_map_t *poll_map, mp_uint_t *rwx_num) { - mp_uint_t n_ready = 0; - for (mp_uint_t i = 0; i < poll_map->alloc; ++i) { - if (!MP_MAP_SLOT_IS_FILLED(poll_map, i)) { - continue; - } - - poll_obj_t *poll_obj = (poll_obj_t*)poll_map->table[i].value; - int errcode; - mp_int_t ret = poll_obj->ioctl(poll_obj->obj, MP_IOCTL_POLL, &errcode, poll_obj->flags); - poll_obj->flags_ret = ret; - - if (ret == -1) { - // error doing ioctl - nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(errcode))); - } - - if (ret != 0) { - // object is ready - n_ready += 1; - if (rwx_num != NULL) { - if (ret & MP_IOCTL_POLL_RD) { - rwx_num[0] += 1; - } - if (ret & MP_IOCTL_POLL_WR) { - rwx_num[1] += 1; - } - if ((ret & ~(MP_IOCTL_POLL_RD | MP_IOCTL_POLL_WR)) != 0) { - rwx_num[2] += 1; - } - } - } - } - return n_ready; -} - -/// \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]; - 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); - mp_obj_get_array(args[2], &rwx_len[2], &x_array); - - // get timeout - mp_uint_t timeout = -1; - if (n_args == 4) { - if (args[3] != mp_const_none) { - #if MICROPY_PY_BUILTINS_FLOAT - float timeout_f = mp_obj_get_float(args[3]); - if (timeout_f >= 0) { - timeout = (mp_uint_t)(timeout_f * 1000); - } - #else - timeout = mp_obj_get_int(args[3]) * 1000; - #endif - } - } - - // merge separate lists and get the ioctl function for each object - mp_map_t poll_map; - mp_map_init(&poll_map, rwx_len[0] + rwx_len[1] + rwx_len[2]); - poll_map_add(&poll_map, r_array, rwx_len[0], MP_IOCTL_POLL_RD, true); - poll_map_add(&poll_map, w_array, rwx_len[1], MP_IOCTL_POLL_WR, true); - poll_map_add(&poll_map, x_array, rwx_len[2], MP_IOCTL_POLL_ERR | MP_IOCTL_POLL_HUP, true); - - mp_uint_t start_tick = HAL_GetTick(); - rwx_len[0] = rwx_len[1] = rwx_len[2] = 0; - for (;;) { - // poll the objects - mp_uint_t n_ready = poll_map_poll(&poll_map, rwx_len); - - if (n_ready > 0 || (timeout != -1 && HAL_GetTick() - start_tick >= timeout)) { - // one or more objects are ready, or we had a timeout - mp_obj_t list_array[3]; - list_array[0] = mp_obj_new_list(rwx_len[0], NULL); - list_array[1] = mp_obj_new_list(rwx_len[1], NULL); - list_array[2] = mp_obj_new_list(rwx_len[2], NULL); - rwx_len[0] = rwx_len[1] = rwx_len[2] = 0; - for (mp_uint_t i = 0; i < poll_map.alloc; ++i) { - if (!MP_MAP_SLOT_IS_FILLED(&poll_map, i)) { - continue; - } - poll_obj_t *poll_obj = (poll_obj_t*)poll_map.table[i].value; - if (poll_obj->flags_ret & MP_IOCTL_POLL_RD) { - ((mp_obj_list_t*)list_array[0])->items[rwx_len[0]++] = poll_obj->obj; - } - if (poll_obj->flags_ret & MP_IOCTL_POLL_WR) { - ((mp_obj_list_t*)list_array[1])->items[rwx_len[1]++] = poll_obj->obj; - } - if ((poll_obj->flags_ret & ~(MP_IOCTL_POLL_RD | MP_IOCTL_POLL_WR)) != 0) { - ((mp_obj_list_t*)list_array[2])->items[rwx_len[2]++] = poll_obj->obj; - } - } - mp_map_deinit(&poll_map); - return mp_obj_new_tuple(3, list_array); - } - __WFI(); - } -} -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_select_select_obj, 3, 4, select_select); - -/// \class Poll - poll class - -typedef struct _mp_obj_poll_t { - mp_obj_base_t base; - mp_map_t poll_map; -} mp_obj_poll_t; - -/// \method register(obj[, eventmask]) -STATIC mp_obj_t poll_register(uint n_args, const mp_obj_t *args) { - mp_obj_poll_t *self = args[0]; - mp_uint_t flags; - if (n_args == 3) { - flags = mp_obj_get_int(args[2]); - } else { - flags = MP_IOCTL_POLL_RD | MP_IOCTL_POLL_WR; - } - poll_map_add(&self->poll_map, &args[1], 1, flags, false); - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_register_obj, 2, 3, poll_register); - -/// \method unregister(obj) -STATIC mp_obj_t poll_unregister(mp_obj_t self_in, mp_obj_t obj_in) { - mp_obj_poll_t *self = self_in; - mp_map_lookup(&self->poll_map, mp_obj_id(obj_in), MP_MAP_LOOKUP_REMOVE_IF_FOUND); - // TODO raise KeyError if obj didn't exist in map - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_2(poll_unregister_obj, poll_unregister); - -/// \method modify(obj, eventmask) -STATIC mp_obj_t poll_modify(mp_obj_t self_in, mp_obj_t obj_in, mp_obj_t eventmask_in) { - mp_obj_poll_t *self = self_in; - mp_map_elem_t *elem = mp_map_lookup(&self->poll_map, mp_obj_id(obj_in), MP_MAP_LOOKUP); - if (elem == NULL) { - nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(ENOENT))); - } - ((poll_obj_t*)elem->value)->flags = mp_obj_get_int(eventmask_in); - return mp_const_none; -} -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) { - mp_obj_poll_t *self = args[0]; - - // work out timeout (its given already in ms) - mp_uint_t timeout = -1; - if (n_args == 2) { - if (args[1] != mp_const_none) { - mp_int_t timeout_i = mp_obj_get_int(args[1]); - if (timeout_i >= 0) { - timeout = timeout_i; - } - } - } - - mp_uint_t start_tick = HAL_GetTick(); - for (;;) { - // poll the objects - mp_uint_t n_ready = poll_map_poll(&self->poll_map, NULL); - - if (n_ready > 0 || (timeout != -1 && HAL_GetTick() - 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); - mp_uint_t 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); - } - } - return ret_list; - } - __WFI(); - } -} -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_poll_obj, 1, 2, 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_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, -}; - -/// \function poll() -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); - 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 }, -}; - -STATIC const mp_obj_dict_t mp_module_select_globals = { - .base = {&mp_type_dict}, - .map = { - .all_keys_are_qstrs = 1, - .table_is_fixed_array = 1, - .used = MP_ARRAY_SIZE(mp_module_select_globals_table), - .alloc = MP_ARRAY_SIZE(mp_module_select_globals_table), - .table = (mp_map_elem_t*)mp_module_select_globals_table, - }, -}; - -const mp_obj_module_t mp_module_uselect = { - .base = { &mp_type_module }, - .name = MP_QSTR_uselect, - .globals = (mp_obj_dict_t*)&mp_module_select_globals, -}; diff --git a/stmhal/modtime.c b/stmhal/modtime.c deleted file mode 100644 index fd8fbb36a..000000000 --- a/stmhal/modtime.c +++ /dev/null @@ -1,362 +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 -#include -#include "stm32f4xx_hal.h" - -#include "mpconfig.h" -#include "nlr.h" -#include "misc.h" -#include "qstr.h" -#include "obj.h" -#include "portmodules.h" -#include "rtc.h" - -/// \module time - time related functions -/// -/// The `time` module provides functions for getting the current time and date, -/// and for sleeping. - -STATIC const uint16_t days_since_jan1[]= { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; - -STATIC bool is_leap_year(mp_uint_t year) { - return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; -} - -// Month is one based -STATIC mp_uint_t mod_time_days_in_month(mp_uint_t year, mp_uint_t month) { - mp_uint_t mdays = days_since_jan1[month] - days_since_jan1[month - 1]; - if (month == 2 && is_leap_year(year)) { - mdays++; - } - return mdays; -} - -// compute the day of the year, between 1 and 366 -// month should be between 1 and 12, date should start at 1 -STATIC mp_uint_t mod_time_year_day(mp_uint_t year, mp_uint_t month, mp_uint_t date) { - mp_uint_t yday = days_since_jan1[month - 1] + date; - if (month >= 3 && is_leap_year(year)) { - yday += 1; - } - return yday; -} - -// returns the number of seconds, as an integer, since 2000-01-01 -mp_uint_t mod_time_seconds_since_2000(mp_uint_t year, mp_uint_t month, mp_uint_t date, mp_uint_t hour, mp_uint_t minute, mp_uint_t second) { - return - second - + minute * 60 - + hour * 3600 - + (mod_time_year_day(year, month, date) - 1 - + ((year - 2000 + 3) / 4) // add a day each 4 years starting with 2001 - - ((year - 2000 + 99) / 100) // subtract a day each 100 years starting with 2001 - + ((year - 2000 + 399) / 400) // add a day each 400 years starting with 2001 - ) * 86400 - + (year - 2000) * 31536000; -} - -// LEAPOCH corresponds to 2000-03-01, which is a mod-400 year, immediately -// after Feb 29. We calculate seconds as a signed integer relative to that. -// -// Our timebase is is relative to 2000-01-01. - -#define LEAPOCH ((31 + 29) * 86400) - -#define DAYS_PER_400Y (365*400 + 97) -#define DAYS_PER_100Y (365*100 + 24) -#define DAYS_PER_4Y (365*4 + 1) - -typedef struct { - uint16_t tm_year; // i.e. 2014 - uint8_t tm_mon; // 1..12 - uint8_t tm_mday; // 1..31 - uint8_t tm_hour; // 0..23 - uint8_t tm_min; // 0..59 - uint8_t tm_sec; // 0..59 - uint8_t tm_wday; // 0..6 0 = Monday - uint16_t tm_yday; // 1..366 -} mod_struct_time; - -STATIC void mod_time_seconds_since_2000_to_struct_time(mp_uint_t t, mod_struct_time *tm) { - // The following algorithm was adapted from musl's __secs_to_tm and adapted - // for differences in Micro Python's timebase. - - mp_int_t seconds = t - LEAPOCH; - - mp_int_t days = seconds / 86400; - seconds %= 86400; - tm->tm_hour = seconds / 3600; - tm->tm_min = seconds / 60 % 60; - tm->tm_sec = seconds % 60; - - mp_int_t wday = (days + 2) % 7; // Mar 1, 2000 was a Wednesday (2) - if (wday < 0) { - wday += 7; - } - tm->tm_wday = wday; - - mp_int_t qc_cycles = days / DAYS_PER_400Y; - days %= DAYS_PER_400Y; - if (days < 0) { - days += DAYS_PER_400Y; - qc_cycles--; - } - mp_int_t c_cycles = days / DAYS_PER_100Y; - if (c_cycles == 4) { - c_cycles--; - } - days -= (c_cycles * DAYS_PER_100Y); - - mp_int_t q_cycles = days / DAYS_PER_4Y; - if (q_cycles == 25) { - q_cycles--; - } - days -= q_cycles * DAYS_PER_4Y; - - mp_int_t years = days / 365; - if (years == 4) { - years--; - } - days -= (years * 365); - - /* We will compute tm_yday at the very end - mp_int_t leap = !years && (q_cycles || !c_cycles); - - tm->tm_yday = days + 31 + 28 + leap; - if (tm->tm_yday >= 365 + leap) { - tm->tm_yday -= 365 + leap; - } - - tm->tm_yday++; // Make one based - */ - - tm->tm_year = 2000 + years + 4 * q_cycles + 100 * c_cycles + 400 * qc_cycles; - - // Note: days_in_month[0] corresponds to March - STATIC const int8_t days_in_month[] = {31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 29}; - - mp_int_t month; - for (month = 0; days_in_month[month] <= days; month++) { - days -= days_in_month[month]; - } - - tm->tm_mon = month + 2; - if (tm->tm_mon >= 12) { - tm->tm_mon -= 12; - tm->tm_year++; - } - tm->tm_mday = days + 1; // Make one based - tm->tm_mon++; // Make one based - - tm->tm_yday = mod_time_year_day(tm->tm_year, tm->tm_mon, tm->tm_mday); -} - -/// \function localtime([secs]) -/// Convert a time expressed in seconds since Jan 1, 2000 into an 8-tuple which -/// contains: (year, month, mday, hour, minute, second, weekday, yearday) -/// If secs is not provided or None, then the current time from the RTC is used. -/// year includes the century (for example 2014) -/// month is 1-12 -/// mday is 1-31 -/// hour is 0-23 -/// minute is 0-59 -/// second is 0-59 -/// weekday is 0-6 for Mon-Sun. -/// yearday is 1-366 -STATIC mp_obj_t time_localtime(mp_uint_t n_args, const mp_obj_t *args) { - if (n_args == 0 || args[0] == mp_const_none) { - // get current date and time - // note: need to call get time then get date to correctly access the registers - RTC_DateTypeDef date; - RTC_TimeTypeDef time; - HAL_RTC_GetTime(&RTCHandle, &time, FORMAT_BIN); - HAL_RTC_GetDate(&RTCHandle, &date, FORMAT_BIN); - mp_obj_t tuple[8] = { - mp_obj_new_int(2000 + date.Year), - mp_obj_new_int(date.Month), - mp_obj_new_int(date.Date), - mp_obj_new_int(time.Hours), - mp_obj_new_int(time.Minutes), - mp_obj_new_int(time.Seconds), - mp_obj_new_int(date.WeekDay - 1), - mp_obj_new_int(mod_time_year_day(2000 + date.Year, date.Month, date.Date)), - }; - return mp_obj_new_tuple(8, tuple); - } else { - mp_int_t seconds = mp_obj_get_int(args[0]); - mod_struct_time tm; - mod_time_seconds_since_2000_to_struct_time(seconds, &tm); - mp_obj_t tuple[8] = { - tuple[0] = mp_obj_new_int(tm.tm_year), - tuple[1] = mp_obj_new_int(tm.tm_mon), - tuple[2] = mp_obj_new_int(tm.tm_mday), - tuple[3] = mp_obj_new_int(tm.tm_hour), - tuple[4] = mp_obj_new_int(tm.tm_min), - tuple[5] = mp_obj_new_int(tm.tm_sec), - tuple[6] = mp_obj_new_int(tm.tm_wday), - tuple[7] = mp_obj_new_int(tm.tm_yday), - }; - return mp_obj_new_tuple(8, tuple); - } -} -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(time_localtime_obj, 0, 1, time_localtime); - - -/// \function mktime() -/// This is inverse function of localtime. It's argument is a full 8-tuple -/// which expresses a time as per localtime. It returns an integer which is -/// the number of seconds since Jan 1, 2000. -STATIC mp_obj_t time_mktime(mp_obj_t tuple) { - - mp_uint_t len; - mp_obj_t *elem; - - mp_obj_get_array(tuple, &len, &elem); - - // localtime generates a tuple of len 8. CPython uses 9, so we accept both. - if (len < 8 || len > 9) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "mktime needs a tuple of length 8 or 9 (%d given)", len)); - } - - mp_int_t year = mp_obj_get_int(elem[0]); - mp_int_t month = mp_obj_get_int(elem[1]); - mp_int_t mday = mp_obj_get_int(elem[2]); - mp_int_t hours = mp_obj_get_int(elem[3]); - mp_int_t minutes = mp_obj_get_int(elem[4]); - mp_int_t seconds = mp_obj_get_int(elem[5]); - - // Normalise the tuple. This allows things like: - // - // tm_tomorrow = list(time.localtime()) - // tm_tomorrow[2] += 1 # Adds 1 to mday - // tomorrow = time.mktime(tm_tommorrow) - // - // And not have to worry about all the weird overflows. - // - // You can subtract dates/times this way as well. - - minutes += seconds / 60; - if ((seconds = seconds % 60) < 0) { - seconds += 60; - minutes--; - } - - hours += minutes / 60; - if ((minutes = minutes % 60) < 0) { - minutes += 60; - hours--; - } - - mday += hours / 24; - if ((hours = hours % 24) < 0) { - hours += 24; - mday--; - } - - month--; // make month zero based - year += month / 12; - if ((month = month % 12) < 0) { - month += 12; - year--; - } - month++; // back to one based - - while (mday < 1) { - if (--month == 0) { - month = 12; - year--; - } - mday += mod_time_days_in_month(year, month); - } - while (mday > mod_time_days_in_month(year, month)) { - mday -= mod_time_days_in_month(year, month); - if (++month == 13) { - month = 1; - year++; - } - } - return mp_obj_new_int_from_uint(mod_time_seconds_since_2000(year, month, mday, hours, minutes, seconds)); -} -MP_DEFINE_CONST_FUN_OBJ_1(time_mktime_obj, time_mktime); - - -/// \function sleep(seconds) -/// Sleep for the given number of seconds. Seconds can be a floating-point number to -/// sleep for a fractional number of seconds. -STATIC mp_obj_t time_sleep(mp_obj_t seconds_o) { -#if MICROPY_PY_BUILTINS_FLOAT - if (MP_OBJ_IS_INT(seconds_o)) { -#endif - HAL_Delay(1000 * mp_obj_get_int(seconds_o)); -#if MICROPY_PY_BUILTINS_FLOAT - } else { - HAL_Delay((uint32_t)(1000 * mp_obj_get_float(seconds_o))); - } -#endif - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_1(time_sleep_obj, time_sleep); - -/// \function time() -/// Returns the number of seconds, as an integer, since 1/1/2000. -STATIC mp_obj_t time_time(void) { - // get date and time - // note: need to call get time then get date to correctly access the registers - RTC_DateTypeDef date; - RTC_TimeTypeDef time; - HAL_RTC_GetTime(&RTCHandle, &time, FORMAT_BIN); - HAL_RTC_GetDate(&RTCHandle, &date, FORMAT_BIN); - return mp_obj_new_int(mod_time_seconds_since_2000(2000 + date.Year, date.Month, date.Date, time.Hours, time.Minutes, time.Seconds)); -} -MP_DEFINE_CONST_FUN_OBJ_0(time_time_obj, time_time); - -STATIC const mp_map_elem_t time_module_globals_table[] = { - { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_utime) }, - - { MP_OBJ_NEW_QSTR(MP_QSTR_localtime), (mp_obj_t)&time_localtime_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_mktime), (mp_obj_t)&time_mktime_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_sleep), (mp_obj_t)&time_sleep_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_time), (mp_obj_t)&time_time_obj }, -}; - -STATIC const mp_obj_dict_t time_module_globals = { - .base = {&mp_type_dict}, - .map = { - .all_keys_are_qstrs = 1, - .table_is_fixed_array = 1, - .used = MP_ARRAY_SIZE(time_module_globals_table), - .alloc = MP_ARRAY_SIZE(time_module_globals_table), - .table = (mp_map_elem_t*)time_module_globals_table, - }, -}; - -const mp_obj_module_t mp_module_utime = { - .base = { &mp_type_module }, - .name = MP_QSTR_utime, - .globals = (mp_obj_dict_t*)&time_module_globals, -}; diff --git a/stmhal/moduos.c b/stmhal/moduos.c new file mode 100644 index 000000000..95446cdb0 --- /dev/null +++ b/stmhal/moduos.c @@ -0,0 +1,369 @@ +/* + * 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 +#include + +#include "mpconfig.h" +#include "nlr.h" +#include "misc.h" +#include "qstr.h" +#include "obj.h" +#include "objtuple.h" +#include "systick.h" +#include "rng.h" +#include "storage.h" +#include "ff.h" +#include "file.h" +#include "sdcard.h" +#include "portmodules.h" + +/// \module os - basic "operating system" services +/// +/// The `os` module contains functions for filesystem access and `urandom`. +/// +/// The filesystem has `/` as the root directory, and the available physical +/// drives are accessible from here. They are currently: +/// +/// /flash -- the internal 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`. + +#if _USE_LFN +static char lfn[_MAX_LFN + 1]; /* Buffer to store the LFN */ +#endif + +STATIC bool sd_in_root(void) { +#if MICROPY_HW_HAS_SDCARD + // TODO this is not the correct logic to check for /sd + return sdcard_is_present(); +#else + return false; +#endif +} + +/// \function chdir(path) +/// Change current directory. +STATIC mp_obj_t os_chdir(mp_obj_t path_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); + } + + if (res != FR_OK) { + // TODO should be mp_type_FileNotFoundError + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "No such file or directory: '%s'", path)); + } + + return mp_const_none; +} +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; + if (n_args == 1) { + if (mp_obj_get_type(args[0]) == &mp_type_bytes) { + is_str_type = false; + } + path = mp_obj_str_get_str(args[0]); + } else { + path = ""; + } + + // "hack" to list 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 (sd_in_root()) { + mp_obj_list_append(dir_list, MP_OBJ_NEW_QSTR(MP_QSTR_sd)); + } + return dir_list; + } + + 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) { + // TODO should be mp_type_FileNotFoundError + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "No such file or directory: '%s'", path)); + } + + 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 */ + +#if _USE_LFN + char *fn = *fno.lfname ? fno.lfname : fno.fname; +#else + char *fn = fno.fname; +#endif + + /* + if (fno.fattrib & AM_DIR) { + // dir + } else { + // file + } + */ + + // 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 + mp_obj_list_append(dir_list, entry_o); + } + + 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); + switch (res) { + case FR_OK: + return mp_const_none; + case FR_EXIST: + // TODO should be FileExistsError + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "File exists: '%s'", path)); + default: + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "Error creating directory '%s'", path)); + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_mkdir_obj, os_mkdir); + +/// \function remove(path) +/// Remove a file. +STATIC mp_obj_t os_remove(mp_obj_t path_o) { + const char *path = mp_obj_str_get_str(path_o); + // TODO check that path is actually a file before trying to unlink it + FRESULT res = f_unlink(path); + switch (res) { + case FR_OK: + return mp_const_none; + default: + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "Error removing file '%s'", path)); + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_remove_obj, os_remove); + +/// \function rmdir(path) +/// Remove a directory. +STATIC mp_obj_t os_rmdir(mp_obj_t path_o) { + const char *path = mp_obj_str_get_str(path_o); + // TODO check that path is actually a directory before trying to unlink it + FRESULT res = f_unlink(path); + switch (res) { + case FR_OK: + return mp_const_none; + default: + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, "Error removing directory '%s'", path)); + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_rmdir_obj, os_rmdir); + +// 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); + + FILINFO fno; +#if _USE_LFN + fno.lfname = NULL; + fno.lfsize = 0; +#endif + + FRESULT res; + if (path_equal(path, "/") || path_equal(path, "/flash") || path_equal(path, "/sd")) { + // stat built-in directory + if (path[1] == 's' && !sd_in_root()) { + // 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; + } + } + + mp_obj_tuple_t *t = mp_obj_new_tuple(10, NULL); + mp_int_t mode = 0; + if (fno.fattrib & AM_DIR) { + mode |= 0x4000; // stat.S_IFDIR + } else { + mode |= 0x8000; // stat.S_IFREG + } + mp_int_t seconds = mod_time_seconds_since_2000( + 1980 + ((fno.fdate >> 9) & 0x7f), + (fno.fdate >> 5) & 0x0f, + fno.fdate & 0x1f, + (fno.ftime >> 11) & 0x1f, + (fno.ftime >> 5) & 0x3f, + 2 * (fno.ftime & 0x1f) + ); + t->items[0] = MP_OBJ_NEW_SMALL_INT(mode); // st_mode + t->items[1] = MP_OBJ_NEW_SMALL_INT(0); // st_ino + t->items[2] = MP_OBJ_NEW_SMALL_INT(0); // st_dev + t->items[3] = MP_OBJ_NEW_SMALL_INT(0); // st_nlink + t->items[4] = MP_OBJ_NEW_SMALL_INT(0); // st_uid + t->items[5] = MP_OBJ_NEW_SMALL_INT(0); // st_gid + t->items[6] = MP_OBJ_NEW_SMALL_INT(fno.fsize); // st_size + t->items[7] = MP_OBJ_NEW_SMALL_INT(seconds); // st_atime + t->items[8] = MP_OBJ_NEW_SMALL_INT(seconds); // st_mtime + t->items[9] = MP_OBJ_NEW_SMALL_INT(seconds); // 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) { + storage_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); + byte *data; + mp_obj_t o = mp_obj_str_builder_start(&mp_type_bytes, n, &data); + for (int i = 0; i < n; i++) { + data[i] = rng_get(); + } + return mp_obj_str_builder_end(o); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_urandom_obj, os_urandom); +#endif + +STATIC const mp_map_elem_t os_module_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_uos) }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_chdir), (mp_obj_t)&os_chdir_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_getcwd), (mp_obj_t)&os_getcwd_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_listdir), (mp_obj_t)&os_listdir_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_mkdir), (mp_obj_t)&os_mkdir_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_rmdir_obj }, + { 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_sync), (mp_obj_t)&os_sync_obj }, + + /// \constant sep - separation character used in paths + { MP_OBJ_NEW_QSTR(MP_QSTR_sep), MP_OBJ_NEW_QSTR(MP_QSTR__slash_) }, + +#if MICROPY_HW_ENABLE_RNG + { MP_OBJ_NEW_QSTR(MP_QSTR_urandom), (mp_obj_t)&os_urandom_obj }, +#endif +}; + +STATIC const mp_obj_dict_t os_module_globals = { + .base = {&mp_type_dict}, + .map = { + .all_keys_are_qstrs = 1, + .table_is_fixed_array = 1, + .used = MP_ARRAY_SIZE(os_module_globals_table), + .alloc = MP_ARRAY_SIZE(os_module_globals_table), + .table = (mp_map_elem_t*)os_module_globals_table, + }, +}; + +const mp_obj_module_t mp_module_uos = { + .base = { &mp_type_module }, + .name = MP_QSTR_uos, + .globals = (mp_obj_dict_t*)&os_module_globals, +}; diff --git a/stmhal/moduselect.c b/stmhal/moduselect.c new file mode 100644 index 000000000..d1bf52289 --- /dev/null +++ b/stmhal/moduselect.c @@ -0,0 +1,310 @@ +/* + * 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 +#include +#include + +#include "stm32f4xx_hal.h" + +#include "mpconfig.h" +#include "misc.h" +#include "nlr.h" +#include "qstr.h" +#include "obj.h" +#include "objlist.h" +#include "pybioctl.h" + +/// \module select - Provides select function to wait for events on a stream +/// +/// This module provides the select function. + +typedef struct _poll_obj_t { + mp_obj_t obj; + mp_uint_t (*ioctl)(mp_obj_t obj, mp_uint_t request, int *errcode, ...); + mp_uint_t flags; + mp_uint_t flags_ret; +} poll_obj_t; + +STATIC void poll_map_add(mp_map_t *poll_map, const mp_obj_t *obj, mp_uint_t obj_len, mp_uint_t flags, bool or_flags) { + for (mp_uint_t i = 0; i < obj_len; i++) { + mp_map_elem_t *elem = mp_map_lookup(poll_map, mp_obj_id(obj[i]), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); + if (elem->value == NULL) { + // object not found; get its ioctl and add it to the poll list + mp_obj_type_t *type = mp_obj_get_type(obj[i]); + if (type->stream_p == NULL || type->stream_p->ioctl == NULL) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "object with stream.ioctl required")); + } + poll_obj_t *poll_obj = m_new_obj(poll_obj_t); + poll_obj->obj = obj[i]; + poll_obj->ioctl = type->stream_p->ioctl; + poll_obj->flags = flags; + poll_obj->flags_ret = 0; + elem->value = poll_obj; + } else { + // object exists; update its flags + if (or_flags) { + ((poll_obj_t*)elem->value)->flags |= flags; + } else { + ((poll_obj_t*)elem->value)->flags = flags; + } + } + } +} + +// poll each object in the map +STATIC mp_uint_t poll_map_poll(mp_map_t *poll_map, mp_uint_t *rwx_num) { + mp_uint_t n_ready = 0; + for (mp_uint_t i = 0; i < poll_map->alloc; ++i) { + if (!MP_MAP_SLOT_IS_FILLED(poll_map, i)) { + continue; + } + + poll_obj_t *poll_obj = (poll_obj_t*)poll_map->table[i].value; + int errcode; + mp_int_t ret = poll_obj->ioctl(poll_obj->obj, MP_IOCTL_POLL, &errcode, poll_obj->flags); + poll_obj->flags_ret = ret; + + if (ret == -1) { + // error doing ioctl + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(errcode))); + } + + if (ret != 0) { + // object is ready + n_ready += 1; + if (rwx_num != NULL) { + if (ret & MP_IOCTL_POLL_RD) { + rwx_num[0] += 1; + } + if (ret & MP_IOCTL_POLL_WR) { + rwx_num[1] += 1; + } + if ((ret & ~(MP_IOCTL_POLL_RD | MP_IOCTL_POLL_WR)) != 0) { + rwx_num[2] += 1; + } + } + } + } + return n_ready; +} + +/// \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]; + 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); + mp_obj_get_array(args[2], &rwx_len[2], &x_array); + + // get timeout + mp_uint_t timeout = -1; + if (n_args == 4) { + if (args[3] != mp_const_none) { + #if MICROPY_PY_BUILTINS_FLOAT + float timeout_f = mp_obj_get_float(args[3]); + if (timeout_f >= 0) { + timeout = (mp_uint_t)(timeout_f * 1000); + } + #else + timeout = mp_obj_get_int(args[3]) * 1000; + #endif + } + } + + // merge separate lists and get the ioctl function for each object + mp_map_t poll_map; + mp_map_init(&poll_map, rwx_len[0] + rwx_len[1] + rwx_len[2]); + poll_map_add(&poll_map, r_array, rwx_len[0], MP_IOCTL_POLL_RD, true); + poll_map_add(&poll_map, w_array, rwx_len[1], MP_IOCTL_POLL_WR, true); + poll_map_add(&poll_map, x_array, rwx_len[2], MP_IOCTL_POLL_ERR | MP_IOCTL_POLL_HUP, true); + + mp_uint_t start_tick = HAL_GetTick(); + rwx_len[0] = rwx_len[1] = rwx_len[2] = 0; + for (;;) { + // poll the objects + mp_uint_t n_ready = poll_map_poll(&poll_map, rwx_len); + + if (n_ready > 0 || (timeout != -1 && HAL_GetTick() - start_tick >= timeout)) { + // one or more objects are ready, or we had a timeout + mp_obj_t list_array[3]; + list_array[0] = mp_obj_new_list(rwx_len[0], NULL); + list_array[1] = mp_obj_new_list(rwx_len[1], NULL); + list_array[2] = mp_obj_new_list(rwx_len[2], NULL); + rwx_len[0] = rwx_len[1] = rwx_len[2] = 0; + for (mp_uint_t i = 0; i < poll_map.alloc; ++i) { + if (!MP_MAP_SLOT_IS_FILLED(&poll_map, i)) { + continue; + } + poll_obj_t *poll_obj = (poll_obj_t*)poll_map.table[i].value; + if (poll_obj->flags_ret & MP_IOCTL_POLL_RD) { + ((mp_obj_list_t*)list_array[0])->items[rwx_len[0]++] = poll_obj->obj; + } + if (poll_obj->flags_ret & MP_IOCTL_POLL_WR) { + ((mp_obj_list_t*)list_array[1])->items[rwx_len[1]++] = poll_obj->obj; + } + if ((poll_obj->flags_ret & ~(MP_IOCTL_POLL_RD | MP_IOCTL_POLL_WR)) != 0) { + ((mp_obj_list_t*)list_array[2])->items[rwx_len[2]++] = poll_obj->obj; + } + } + mp_map_deinit(&poll_map); + return mp_obj_new_tuple(3, list_array); + } + __WFI(); + } +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_select_select_obj, 3, 4, select_select); + +/// \class Poll - poll class + +typedef struct _mp_obj_poll_t { + mp_obj_base_t base; + mp_map_t poll_map; +} mp_obj_poll_t; + +/// \method register(obj[, eventmask]) +STATIC mp_obj_t poll_register(uint n_args, const mp_obj_t *args) { + mp_obj_poll_t *self = args[0]; + mp_uint_t flags; + if (n_args == 3) { + flags = mp_obj_get_int(args[2]); + } else { + flags = MP_IOCTL_POLL_RD | MP_IOCTL_POLL_WR; + } + poll_map_add(&self->poll_map, &args[1], 1, flags, false); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_register_obj, 2, 3, poll_register); + +/// \method unregister(obj) +STATIC mp_obj_t poll_unregister(mp_obj_t self_in, mp_obj_t obj_in) { + mp_obj_poll_t *self = self_in; + mp_map_lookup(&self->poll_map, mp_obj_id(obj_in), MP_MAP_LOOKUP_REMOVE_IF_FOUND); + // TODO raise KeyError if obj didn't exist in map + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(poll_unregister_obj, poll_unregister); + +/// \method modify(obj, eventmask) +STATIC mp_obj_t poll_modify(mp_obj_t self_in, mp_obj_t obj_in, mp_obj_t eventmask_in) { + mp_obj_poll_t *self = self_in; + mp_map_elem_t *elem = mp_map_lookup(&self->poll_map, mp_obj_id(obj_in), MP_MAP_LOOKUP); + if (elem == NULL) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(ENOENT))); + } + ((poll_obj_t*)elem->value)->flags = mp_obj_get_int(eventmask_in); + return mp_const_none; +} +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) { + mp_obj_poll_t *self = args[0]; + + // work out timeout (its given already in ms) + mp_uint_t timeout = -1; + if (n_args == 2) { + if (args[1] != mp_const_none) { + mp_int_t timeout_i = mp_obj_get_int(args[1]); + if (timeout_i >= 0) { + timeout = timeout_i; + } + } + } + + mp_uint_t start_tick = HAL_GetTick(); + for (;;) { + // poll the objects + mp_uint_t n_ready = poll_map_poll(&self->poll_map, NULL); + + if (n_ready > 0 || (timeout != -1 && HAL_GetTick() - 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); + mp_uint_t 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); + } + } + return ret_list; + } + __WFI(); + } +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_poll_obj, 1, 2, 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_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, +}; + +/// \function poll() +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); + 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 }, +}; + +STATIC const mp_obj_dict_t mp_module_select_globals = { + .base = {&mp_type_dict}, + .map = { + .all_keys_are_qstrs = 1, + .table_is_fixed_array = 1, + .used = MP_ARRAY_SIZE(mp_module_select_globals_table), + .alloc = MP_ARRAY_SIZE(mp_module_select_globals_table), + .table = (mp_map_elem_t*)mp_module_select_globals_table, + }, +}; + +const mp_obj_module_t mp_module_uselect = { + .base = { &mp_type_module }, + .name = MP_QSTR_uselect, + .globals = (mp_obj_dict_t*)&mp_module_select_globals, +}; diff --git a/stmhal/modutime.c b/stmhal/modutime.c new file mode 100644 index 000000000..fd8fbb36a --- /dev/null +++ b/stmhal/modutime.c @@ -0,0 +1,362 @@ +/* + * 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 +#include +#include "stm32f4xx_hal.h" + +#include "mpconfig.h" +#include "nlr.h" +#include "misc.h" +#include "qstr.h" +#include "obj.h" +#include "portmodules.h" +#include "rtc.h" + +/// \module time - time related functions +/// +/// The `time` module provides functions for getting the current time and date, +/// and for sleeping. + +STATIC const uint16_t days_since_jan1[]= { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; + +STATIC bool is_leap_year(mp_uint_t year) { + return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; +} + +// Month is one based +STATIC mp_uint_t mod_time_days_in_month(mp_uint_t year, mp_uint_t month) { + mp_uint_t mdays = days_since_jan1[month] - days_since_jan1[month - 1]; + if (month == 2 && is_leap_year(year)) { + mdays++; + } + return mdays; +} + +// compute the day of the year, between 1 and 366 +// month should be between 1 and 12, date should start at 1 +STATIC mp_uint_t mod_time_year_day(mp_uint_t year, mp_uint_t month, mp_uint_t date) { + mp_uint_t yday = days_since_jan1[month - 1] + date; + if (month >= 3 && is_leap_year(year)) { + yday += 1; + } + return yday; +} + +// returns the number of seconds, as an integer, since 2000-01-01 +mp_uint_t mod_time_seconds_since_2000(mp_uint_t year, mp_uint_t month, mp_uint_t date, mp_uint_t hour, mp_uint_t minute, mp_uint_t second) { + return + second + + minute * 60 + + hour * 3600 + + (mod_time_year_day(year, month, date) - 1 + + ((year - 2000 + 3) / 4) // add a day each 4 years starting with 2001 + - ((year - 2000 + 99) / 100) // subtract a day each 100 years starting with 2001 + + ((year - 2000 + 399) / 400) // add a day each 400 years starting with 2001 + ) * 86400 + + (year - 2000) * 31536000; +} + +// LEAPOCH corresponds to 2000-03-01, which is a mod-400 year, immediately +// after Feb 29. We calculate seconds as a signed integer relative to that. +// +// Our timebase is is relative to 2000-01-01. + +#define LEAPOCH ((31 + 29) * 86400) + +#define DAYS_PER_400Y (365*400 + 97) +#define DAYS_PER_100Y (365*100 + 24) +#define DAYS_PER_4Y (365*4 + 1) + +typedef struct { + uint16_t tm_year; // i.e. 2014 + uint8_t tm_mon; // 1..12 + uint8_t tm_mday; // 1..31 + uint8_t tm_hour; // 0..23 + uint8_t tm_min; // 0..59 + uint8_t tm_sec; // 0..59 + uint8_t tm_wday; // 0..6 0 = Monday + uint16_t tm_yday; // 1..366 +} mod_struct_time; + +STATIC void mod_time_seconds_since_2000_to_struct_time(mp_uint_t t, mod_struct_time *tm) { + // The following algorithm was adapted from musl's __secs_to_tm and adapted + // for differences in Micro Python's timebase. + + mp_int_t seconds = t - LEAPOCH; + + mp_int_t days = seconds / 86400; + seconds %= 86400; + tm->tm_hour = seconds / 3600; + tm->tm_min = seconds / 60 % 60; + tm->tm_sec = seconds % 60; + + mp_int_t wday = (days + 2) % 7; // Mar 1, 2000 was a Wednesday (2) + if (wday < 0) { + wday += 7; + } + tm->tm_wday = wday; + + mp_int_t qc_cycles = days / DAYS_PER_400Y; + days %= DAYS_PER_400Y; + if (days < 0) { + days += DAYS_PER_400Y; + qc_cycles--; + } + mp_int_t c_cycles = days / DAYS_PER_100Y; + if (c_cycles == 4) { + c_cycles--; + } + days -= (c_cycles * DAYS_PER_100Y); + + mp_int_t q_cycles = days / DAYS_PER_4Y; + if (q_cycles == 25) { + q_cycles--; + } + days -= q_cycles * DAYS_PER_4Y; + + mp_int_t years = days / 365; + if (years == 4) { + years--; + } + days -= (years * 365); + + /* We will compute tm_yday at the very end + mp_int_t leap = !years && (q_cycles || !c_cycles); + + tm->tm_yday = days + 31 + 28 + leap; + if (tm->tm_yday >= 365 + leap) { + tm->tm_yday -= 365 + leap; + } + + tm->tm_yday++; // Make one based + */ + + tm->tm_year = 2000 + years + 4 * q_cycles + 100 * c_cycles + 400 * qc_cycles; + + // Note: days_in_month[0] corresponds to March + STATIC const int8_t days_in_month[] = {31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 29}; + + mp_int_t month; + for (month = 0; days_in_month[month] <= days; month++) { + days -= days_in_month[month]; + } + + tm->tm_mon = month + 2; + if (tm->tm_mon >= 12) { + tm->tm_mon -= 12; + tm->tm_year++; + } + tm->tm_mday = days + 1; // Make one based + tm->tm_mon++; // Make one based + + tm->tm_yday = mod_time_year_day(tm->tm_year, tm->tm_mon, tm->tm_mday); +} + +/// \function localtime([secs]) +/// Convert a time expressed in seconds since Jan 1, 2000 into an 8-tuple which +/// contains: (year, month, mday, hour, minute, second, weekday, yearday) +/// If secs is not provided or None, then the current time from the RTC is used. +/// year includes the century (for example 2014) +/// month is 1-12 +/// mday is 1-31 +/// hour is 0-23 +/// minute is 0-59 +/// second is 0-59 +/// weekday is 0-6 for Mon-Sun. +/// yearday is 1-366 +STATIC mp_obj_t time_localtime(mp_uint_t n_args, const mp_obj_t *args) { + if (n_args == 0 || args[0] == mp_const_none) { + // get current date and time + // note: need to call get time then get date to correctly access the registers + RTC_DateTypeDef date; + RTC_TimeTypeDef time; + HAL_RTC_GetTime(&RTCHandle, &time, FORMAT_BIN); + HAL_RTC_GetDate(&RTCHandle, &date, FORMAT_BIN); + mp_obj_t tuple[8] = { + mp_obj_new_int(2000 + date.Year), + mp_obj_new_int(date.Month), + mp_obj_new_int(date.Date), + mp_obj_new_int(time.Hours), + mp_obj_new_int(time.Minutes), + mp_obj_new_int(time.Seconds), + mp_obj_new_int(date.WeekDay - 1), + mp_obj_new_int(mod_time_year_day(2000 + date.Year, date.Month, date.Date)), + }; + return mp_obj_new_tuple(8, tuple); + } else { + mp_int_t seconds = mp_obj_get_int(args[0]); + mod_struct_time tm; + mod_time_seconds_since_2000_to_struct_time(seconds, &tm); + mp_obj_t tuple[8] = { + tuple[0] = mp_obj_new_int(tm.tm_year), + tuple[1] = mp_obj_new_int(tm.tm_mon), + tuple[2] = mp_obj_new_int(tm.tm_mday), + tuple[3] = mp_obj_new_int(tm.tm_hour), + tuple[4] = mp_obj_new_int(tm.tm_min), + tuple[5] = mp_obj_new_int(tm.tm_sec), + tuple[6] = mp_obj_new_int(tm.tm_wday), + tuple[7] = mp_obj_new_int(tm.tm_yday), + }; + return mp_obj_new_tuple(8, tuple); + } +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(time_localtime_obj, 0, 1, time_localtime); + + +/// \function mktime() +/// This is inverse function of localtime. It's argument is a full 8-tuple +/// which expresses a time as per localtime. It returns an integer which is +/// the number of seconds since Jan 1, 2000. +STATIC mp_obj_t time_mktime(mp_obj_t tuple) { + + mp_uint_t len; + mp_obj_t *elem; + + mp_obj_get_array(tuple, &len, &elem); + + // localtime generates a tuple of len 8. CPython uses 9, so we accept both. + if (len < 8 || len > 9) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "mktime needs a tuple of length 8 or 9 (%d given)", len)); + } + + mp_int_t year = mp_obj_get_int(elem[0]); + mp_int_t month = mp_obj_get_int(elem[1]); + mp_int_t mday = mp_obj_get_int(elem[2]); + mp_int_t hours = mp_obj_get_int(elem[3]); + mp_int_t minutes = mp_obj_get_int(elem[4]); + mp_int_t seconds = mp_obj_get_int(elem[5]); + + // Normalise the tuple. This allows things like: + // + // tm_tomorrow = list(time.localtime()) + // tm_tomorrow[2] += 1 # Adds 1 to mday + // tomorrow = time.mktime(tm_tommorrow) + // + // And not have to worry about all the weird overflows. + // + // You can subtract dates/times this way as well. + + minutes += seconds / 60; + if ((seconds = seconds % 60) < 0) { + seconds += 60; + minutes--; + } + + hours += minutes / 60; + if ((minutes = minutes % 60) < 0) { + minutes += 60; + hours--; + } + + mday += hours / 24; + if ((hours = hours % 24) < 0) { + hours += 24; + mday--; + } + + month--; // make month zero based + year += month / 12; + if ((month = month % 12) < 0) { + month += 12; + year--; + } + month++; // back to one based + + while (mday < 1) { + if (--month == 0) { + month = 12; + year--; + } + mday += mod_time_days_in_month(year, month); + } + while (mday > mod_time_days_in_month(year, month)) { + mday -= mod_time_days_in_month(year, month); + if (++month == 13) { + month = 1; + year++; + } + } + return mp_obj_new_int_from_uint(mod_time_seconds_since_2000(year, month, mday, hours, minutes, seconds)); +} +MP_DEFINE_CONST_FUN_OBJ_1(time_mktime_obj, time_mktime); + + +/// \function sleep(seconds) +/// Sleep for the given number of seconds. Seconds can be a floating-point number to +/// sleep for a fractional number of seconds. +STATIC mp_obj_t time_sleep(mp_obj_t seconds_o) { +#if MICROPY_PY_BUILTINS_FLOAT + if (MP_OBJ_IS_INT(seconds_o)) { +#endif + HAL_Delay(1000 * mp_obj_get_int(seconds_o)); +#if MICROPY_PY_BUILTINS_FLOAT + } else { + HAL_Delay((uint32_t)(1000 * mp_obj_get_float(seconds_o))); + } +#endif + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(time_sleep_obj, time_sleep); + +/// \function time() +/// Returns the number of seconds, as an integer, since 1/1/2000. +STATIC mp_obj_t time_time(void) { + // get date and time + // note: need to call get time then get date to correctly access the registers + RTC_DateTypeDef date; + RTC_TimeTypeDef time; + HAL_RTC_GetTime(&RTCHandle, &time, FORMAT_BIN); + HAL_RTC_GetDate(&RTCHandle, &date, FORMAT_BIN); + return mp_obj_new_int(mod_time_seconds_since_2000(2000 + date.Year, date.Month, date.Date, time.Hours, time.Minutes, time.Seconds)); +} +MP_DEFINE_CONST_FUN_OBJ_0(time_time_obj, time_time); + +STATIC const mp_map_elem_t time_module_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_utime) }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_localtime), (mp_obj_t)&time_localtime_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_mktime), (mp_obj_t)&time_mktime_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_sleep), (mp_obj_t)&time_sleep_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_time), (mp_obj_t)&time_time_obj }, +}; + +STATIC const mp_obj_dict_t time_module_globals = { + .base = {&mp_type_dict}, + .map = { + .all_keys_are_qstrs = 1, + .table_is_fixed_array = 1, + .used = MP_ARRAY_SIZE(time_module_globals_table), + .alloc = MP_ARRAY_SIZE(time_module_globals_table), + .table = (mp_map_elem_t*)time_module_globals_table, + }, +}; + +const mp_obj_module_t mp_module_utime = { + .base = { &mp_type_module }, + .name = MP_QSTR_utime, + .globals = (mp_obj_dict_t*)&time_module_globals, +}; -- cgit v1.2.3