Commit 65dd7bc1 authored by Damien George's avatar Damien George
Browse files

stmhal: Change 0:/ and 1:/ to /flash and /sd; add CWD support.

Some important changes to the way the file system is structured on the
pyboard:

1. 0: and 1: drive names are now replaced with POSIX inspired
directories, namely /flash and /sd.

2. Filesystem now supports the notion of a current working directory.
Supports the standard Python way of manipulating it: os.chdir and
os.getcwd.

3. On boot up, current directory is /flash if no SD inserted, else /sd
if SD inserted.  Then runs boot.py and main.py from the current dir.
This is the same as the old behaviour, but is much more consistent and
flexible (eg you can os.chdir in boot.py to change where main.py is run
from).

4. sys.path (for import) is now set to '' (current dir), plus /flash
and /flash/lib, and then /sd and /sd/lib if SD inserted.  This, along
with CWD, means that import now works properly.  You can import a file
from the current directory.

5. os.listdir is fixed to return just the basename, not the full path.

See issue #537 for background and discussion.
parent 5aac6aa4
......@@ -104,6 +104,7 @@ SRC_C = \
file.c \
sdcard.c \
diskio.c \
ffconf.c \
lcd.c \
accel.c \
servo.c \
......
......@@ -106,6 +106,8 @@
/ Fixed f_open(), f_mkdir() and f_setlabel() can return incorrect error code.
/---------------------------------------------------------------------------*/
#include <string.h>
#include "ff.h" /* Declarations of FatFs API */
#include "diskio.h" /* Declarations of disk I/O functions */
......@@ -485,7 +487,7 @@ static
WORD Fsid; /* File system mount ID */
#if _FS_RPATH && _VOLUMES >= 2
static
//static
BYTE CurrVol; /* Current drive */
#endif
......@@ -1992,6 +1994,8 @@ void get_fileinfo ( /* No return code */
/* Get logical drive number from path name */
/*-----------------------------------------------------------------------*/
/* now implemented by us for configurability of path names */
#if 0
static
int get_ldnumber ( /* Returns logical drive number (-1:invalid drive) */
const TCHAR** path /* Pointer to pointer to the path name */
......@@ -2016,7 +2020,7 @@ int get_ldnumber ( /* Returns logical drive number (-1:invalid drive) */
return vol;
}
#endif
......@@ -2131,7 +2135,7 @@ FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */
/* Get logical drive number from the path name */
*rfs = 0;
vol = get_ldnumber(path);
vol = ff_get_ldnumber(path);
if (vol < 0) return FR_INVALID_DRIVE;
/* Check if the file system object is valid or not */
......@@ -2326,7 +2330,7 @@ FRESULT f_mount (
FRESULT res;
const TCHAR *rp = path;
vol = get_ldnumber(&rp);
vol = ff_get_ldnumber(&rp);
if (vol < 0) return FR_INVALID_DRIVE;
cfs = FatFs[vol]; /* Pointer to fs object */
......@@ -2813,7 +2817,7 @@ FRESULT f_chdrive (
int vol;
vol = get_ldnumber(&path);
vol = ff_get_ldnumber(&path);
if (vol < 0) return FR_INVALID_DRIVE;
CurrVol = (BYTE)vol;
......@@ -2912,11 +2916,22 @@ FRESULT f_getcwd (
tp = buff;
if (res == FR_OK) {
#if _VOLUMES >= 2
#if 0
*tp++ = '0' + CurrVol; /* Put drive number */
*tp++ = ':';
#endif
if (CurrVol == 0) {
memcpy(tp, "/flash", 6);
tp += 6;
} else {
memcpy(tp, "/sd", 3);
tp += 3;
}
#endif
if (i == len) { /* Root-directory */
#if 0
*tp++ = '/';
#endif
} else { /* Sub-directroy */
do /* Add stacked path str */
*tp++ = buff[i++];
......@@ -3937,7 +3952,7 @@ FRESULT f_mkfs (
/* Check mounted drive and clear work area */
vol = get_ldnumber(&path);
vol = ff_get_ldnumber(&path);
if (vol < 0) return FR_INVALID_DRIVE;
if (sfd > 1) return FR_INVALID_PARAMETER;
if (au & (au - 1)) return FR_INVALID_PARAMETER;
......
......@@ -288,6 +288,8 @@ int ff_del_syncobj (_SYNC_t sobj); /* Delete a sync object */
#endif
/* Returns logical drive number (-1:invalid drive) */
int ff_get_ldnumber(const TCHAR** path);
/*--------------------------------------------------------------*/
......
/*
* 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 <string.h>
#include "mpconfigport.h"
#include "ff.h"
#include "ffconf.h"
extern BYTE CurrVol;
// "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 CurrVol;
#else
return -1;
#endif
} else if (strncmp(*path, "/flash", 6) == 0) {
if ((*path)[6] == '/') {
*path += 6;
} else if ((*path)[6] == '\0') {
*path = "/";
} else {
return -1;
}
return 0;
} else if (strncmp(*path, "/sd", 3) == 0) {
if ((*path)[3] == '/') {
*path += 3;
} else if ((*path)[3] == '\0') {
*path = "/";
} else {
return -1;
}
return 1;
} else {
return -1;
}
}
......@@ -143,7 +143,7 @@
/ This option has no effect when _LFN_UNICODE is 0. */
#define _FS_RPATH 0 /* 0 to 2 */
#define _FS_RPATH 2 /* 0 to 2 */
/* The _FS_RPATH option configures relative path feature.
/
/ 0: Disable relative path feature and remove related functions.
......
......@@ -332,8 +332,9 @@ soft_reset:
qstr_init();
mp_init();
mp_obj_list_init(mp_sys_path, 0);
mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR_0_colon__slash_));
mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR_0_colon__slash_lib));
mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR_)); // current dir (or base dir of the script)
mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_flash));
mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_flash_slash_lib));
mp_obj_list_init(mp_sys_argv, 0);
readline_init();
......@@ -344,7 +345,7 @@ soft_reset:
// local filesystem init
{
// try to mount the flash
FRESULT res = f_mount(&fatfs0, "0:", 1);
FRESULT res = f_mount(&fatfs0, "/flash", 1);
if (reset_mode == 3 || res == FR_NO_FILESYSTEM) {
// no filesystem, or asked to reset it, so create a fresh one
......@@ -352,7 +353,7 @@ soft_reset:
led_state(PYB_LED_R2, 1);
uint32_t start_tick = HAL_GetTick();
res = f_mkfs("0:", 0, 0);
res = f_mkfs("/flash", 0, 0);
if (res == FR_OK) {
// success creating fresh LFS
} else {
......@@ -361,19 +362,19 @@ soft_reset:
// create empty main.py
FIL fp;
f_open(&fp, "0:/main.py", FA_WRITE | FA_CREATE_ALWAYS);
f_open(&fp, "/flash/main.py", FA_WRITE | FA_CREATE_ALWAYS);
UINT n;
f_write(&fp, fresh_main_py, sizeof(fresh_main_py) - 1 /* don't count null terminator */, &n);
// TODO check we could write n bytes
f_close(&fp);
// create .inf driver file
f_open(&fp, "0:/pybcdc.inf", FA_WRITE | FA_CREATE_ALWAYS);
f_open(&fp, "/flash/pybcdc.inf", FA_WRITE | FA_CREATE_ALWAYS);
f_write(&fp, fresh_pybcdc_inf, sizeof(fresh_pybcdc_inf) - 1 /* don't count null terminator */, &n);
f_close(&fp);
// create readme file
f_open(&fp, "0:/README.txt", FA_WRITE | FA_CREATE_ALWAYS);
f_open(&fp, "/flash/README.txt", FA_WRITE | FA_CREATE_ALWAYS);
f_write(&fp, fresh_readme_txt, sizeof(fresh_readme_txt) - 1 /* don't count null terminator */, &n);
f_close(&fp);
......@@ -387,14 +388,14 @@ soft_reset:
}
}
// make sure we have a 0:/boot.py
// make sure we have a /flash/boot.py
{
FILINFO fno;
#if _USE_LFN
fno.lfname = NULL;
fno.lfsize = 0;
#endif
FRESULT res = f_stat("0:/boot.py", &fno);
FRESULT res = f_stat("/flash/boot.py", &fno);
if (res == FR_OK) {
if (fno.fattrib & AM_DIR) {
// exists as a directory
......@@ -411,7 +412,7 @@ soft_reset:
uint32_t start_tick = HAL_GetTick();
FIL fp;
f_open(&fp, "0:/boot.py", FA_WRITE | FA_CREATE_ALWAYS);
f_open(&fp, "/flash/boot.py", FA_WRITE | FA_CREATE_ALWAYS);
UINT n;
f_write(&fp, fresh_boot_py, sizeof(fresh_boot_py) - 1 /* don't count null terminator */, &n);
// TODO check we could write n bytes
......@@ -424,21 +425,25 @@ soft_reset:
}
// root device defaults to internal flash filesystem
uint root_device = 0;
f_chdrive("/flash");
#if defined(USE_DEVICE_MODE)
usb_storage_medium_t usb_medium = USB_STORAGE_MEDIUM_FLASH;
#endif
#if MICROPY_HW_HAS_SDCARD
// if an SD card is present then mount it on 1:/
// if an SD card is present then mount it on /sd/
if (reset_mode == 1 && sdcard_is_present()) {
FRESULT res = f_mount(&fatfs1, "1:", 1);
FRESULT res = f_mount(&fatfs1, "/sd", 1);
if (res != FR_OK) {
printf("[SD] could not mount SD card\n");
} else {
// use SD card as root device
root_device = 1;
f_chdrive("/sd");
// TODO these should go before the /flash entries in the path
mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_sd));
mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_sd_slash_lib));
if (first_soft_reset) {
// use SD card as medium for the USB MSD
......@@ -453,17 +458,12 @@ soft_reset:
(void)first_soft_reset;
#endif
// run <root>:/boot.py, if it exists
// run boot.py, if it exists
if (reset_mode == 1) {
const char *boot_file;
if (root_device == 0) {
boot_file = "0:/boot.py";
} else {
boot_file = "1:/boot.py";
}
FRESULT res = f_stat(boot_file, NULL);
const char *boot_py = "boot.py";
FRESULT res = f_stat(boot_py, NULL);
if (res == FR_OK) {
if (!pyexec_file(boot_file)) {
if (!pyexec_file(boot_py)) {
flash_error(4);
}
}
......@@ -518,20 +518,18 @@ soft_reset:
// now that everything is initialised, run main script
if (reset_mode == 1 && pyexec_mode_kind == PYEXEC_MODE_FRIENDLY_REPL) {
vstr_t *vstr = vstr_new();
vstr_printf(vstr, "%d:/", root_device);
const char *main_py;
if (pyb_config_main == MP_OBJ_NULL) {
vstr_add_str(vstr, "main.py");
main_py = "main.py";
} else {
vstr_add_str(vstr, mp_obj_str_get_str(pyb_config_main));
main_py = mp_obj_str_get_str(pyb_config_main);
}
FRESULT res = f_stat(vstr_str(vstr), NULL);
FRESULT res = f_stat(main_py, NULL);
if (res == FR_OK) {
if (!pyexec_file(vstr_str(vstr))) {
if (!pyexec_file(main_py)) {
flash_error(3);
}
}
vstr_free(vstr);
}
#if MICROPY_HW_ENABLE_CC3K
......
......@@ -38,22 +38,64 @@
#include "storage.h"
#include "ff.h"
#include "file.h"
#include "sdcard.h"
#include "portmodules.h"
#if _USE_LFN
static char lfn[_MAX_LFN + 1]; /* Buffer to store the LFN */
#endif
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);
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);
STATIC mp_obj_t os_listdir(uint n_args, const mp_obj_t *args) {
const mp_obj_type_t *local_str_type = &mp_type_str;
bool is_str_type = true;
const char *path;
if (n_args == 1) {
if (mp_obj_get_type(args[0]) == &mp_type_bytes) {
local_str_type = &mp_type_bytes;
is_str_type = false;
}
path = mp_obj_str_get_str(args[0]);
} else {
path = "0:";
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 (sdcard_is_present()) { // TODO this is not the correct logic to check for /sd
mp_obj_list_append(dir_list, MP_OBJ_NEW_QSTR(MP_QSTR_sd));
}
return dir_list;
}
FRESULT res;
......@@ -72,11 +114,6 @@ STATIC mp_obj_t os_listdir(uint n_args, const mp_obj_t *args) {
mp_obj_t dir_list = mp_obj_new_list(0, NULL);
uint path_len = strlen(path);
if (path[path_len - 1] == '/') {
path_len--;
}
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 */
......@@ -89,20 +126,24 @@ STATIC mp_obj_t os_listdir(uint n_args, const mp_obj_t *args) {
char *fn = fno.fname;
#endif
if (fno.fattrib & AM_DIR) { /* It is a directory */
} else { /* It is a file. */
/*
if (fno.fattrib & AM_DIR) {
// dir
} else {
// file
}
*/
// make a string object for this entry
byte *data;
uint fn_len = strlen(fn);
mp_obj_t entry_o = mp_obj_str_builder_start(local_str_type, path_len + 1 + fn_len, &data);
memcpy(data, path, path_len);
data[path_len] = '/';
memcpy(data + path_len + 1, fn, fn_len);
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, mp_obj_str_builder_end(entry_o));
mp_obj_list_append(dir_list, entry_o);
}
f_closedir(&dir);
......@@ -161,9 +202,17 @@ STATIC mp_obj_t os_stat(mp_obj_t path_in) {
fno.lfsize = 0;
#endif
FRESULT res = f_stat(path, &fno);
if (res != FR_OK) {
nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(fresult_to_errno_table[res])));
if (path[0] == '/' && path[1] == '\0') {
// stat root directory
fno.fsize = 0;
fno.fdate = 0;
fno.ftime = 0;
fno.fattrib = AM_DIR;
} else {
FRESULT res = f_stat(path, &fno);
if (res != 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);
......@@ -216,6 +265,8 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_urandom_obj, os_urandom);
STATIC const mp_map_elem_t os_module_globals_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_os) },
{ 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 },
......
......@@ -141,7 +141,7 @@ STATIC mp_obj_t pyb_info(uint n_args, const mp_obj_t *args) {
{
DWORD nclst;
FATFS *fatfs;
f_getfree("0:", &nclst, &fatfs);
f_getfree("/flash", &nclst, &fatfs);
printf("LFS free: %u bytes\n", (uint)(nclst * fatfs->csize * 512));
}
......
......@@ -63,8 +63,10 @@ Q(SDcard)
Q(FileIO)
Q(flush)
// Entries for sys.path
Q(0:/)
Q(0:/lib)
Q(/flash)
Q(/flash/lib)
Q(/sd)
Q(/sd/lib)
Q(millis)
// for file class
......@@ -239,6 +241,10 @@ Q(speed)
// for os module
Q(os)
Q(/)
Q(flash)
Q(sd)
Q(chdir)
Q(getcwd)
Q(listdir)
Q(mkdir)
Q(remove)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment