Skip to content

Commit 7690b13

Browse files
committed
stmhal: Add ability to mount custom block device.
1 parent e2745b3 commit 7690b13

10 files changed

Lines changed: 302 additions & 23 deletions

File tree

docs/library/pyb.rst

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,37 @@ Miscellaneous functions
154154

155155
Print out lots of information about the board.
156156

157+
.. function:: mount(device, mountpoint, \*, readonly=False, mkfs=False)
158+
159+
Mount a block device and make it available as part of the filesystem.
160+
``device`` must be an object that provides the block protocol:
161+
162+
- ``readblocks(self, blocknum, buf)``
163+
- ``writeblocks(self, blocknum, buf)`` (optional)
164+
- ``count(self)``
165+
- ``sync(self)`` (optional)
166+
167+
``readblocks`` and ``writeblocks`` should copy data between ``buf`` and
168+
the block device, starting from block number ``blocknum`` on the device.
169+
``buf`` will be a bytearray with length a multiple of 512. If
170+
``writeblocks`` is not defined then the device is mounted read-only.
171+
The return value of these two functions is ignored.
172+
173+
``count`` should return the number of blocks available on the device.
174+
``sync``, if implemented, should sync the data on the device.
175+
176+
The parameter ``mountpoint`` is the location in the root of the filesystem
177+
to mount the device. It must begin with a forward-slash.
178+
179+
If ``readonly`` is ``True``, then the device is mounted read-only,
180+
otherwise it is mounted read-write.
181+
182+
If ``mkfs`` is ``True``, then a new filesystem is created if one does not
183+
already exist.
184+
185+
To unmount a device, pass ``None`` as the device and the mount location
186+
as ``mountpoint``.
187+
157188
.. function:: repl_uart(uart)
158189

159190
Get or set the UART object that the REPL is repeated on.

stmhal/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ SRC_C = \
133133
storage.c \
134134
file.c \
135135
sdcard.c \
136+
fsusermount.c \
136137
diskio.c \
137138
ffconf.c \
138139
lcd.c \

stmhal/diskio.c

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,19 @@
3636
#include "misc.h"
3737
#include "qstr.h"
3838
#include "obj.h"
39+
#include "runtime.h"
3940
#include "systick.h"
4041
#include "rtc.h"
4142
#include "storage.h"
4243
#include "sdcard.h"
4344
#include "ff.h" /* FatFs lower layer API */
4445
#include "diskio.h" /* FatFs lower layer API */
46+
#include "fsusermount.h"
4547

4648
const PARTITION VolToPart[] = {
4749
{0, 1}, // Logical drive 0 ==> Physical drive 0, 1st partition
4850
{1, 0}, // Logical drive 1 ==> Physical drive 1 (auto detection)
51+
{2, 0}, // Logical drive 2 ==> Physical drive 2 (auto detection)
4952
/*
5053
{0, 2}, // Logical drive 2 ==> Physical drive 0, 2nd partition
5154
{0, 3}, // Logical drive 3 ==> Physical drive 0, 3rd partition
@@ -55,6 +58,7 @@ const PARTITION VolToPart[] = {
5558
/* Definitions of physical drive number for each media */
5659
#define PD_FLASH (0)
5760
#define PD_SDCARD (1)
61+
#define PD_USER (2)
5862

5963
/*-----------------------------------------------------------------------*/
6064
/* Initialize a Drive */
@@ -77,6 +81,15 @@ DSTATUS disk_initialize (
7781
// TODO return STA_PROTECT if SD card is read only
7882
return 0;
7983
#endif
84+
85+
case PD_USER:
86+
if (fs_user_mount == NULL) {
87+
return STA_NODISK;
88+
}
89+
if (fs_user_mount->writeblocks[0] == MP_OBJ_NULL) {
90+
return STA_PROTECT;
91+
}
92+
return 0;
8093
}
8194

8295
return STA_NOINIT;
@@ -100,6 +113,15 @@ DSTATUS disk_status (
100113
// TODO return STA_PROTECT if SD card is read only
101114
return 0;
102115
#endif
116+
117+
case PD_USER:
118+
if (fs_user_mount == NULL) {
119+
return STA_NODISK;
120+
}
121+
if (fs_user_mount->writeblocks[0] == MP_OBJ_NULL) {
122+
return STA_PROTECT;
123+
}
124+
return 0;
103125
}
104126

105127
return STA_NOINIT;
@@ -132,6 +154,12 @@ DRESULT disk_read (
132154
}
133155
return RES_OK;
134156
#endif
157+
158+
case PD_USER:
159+
fs_user_mount->readblocks[2] = MP_OBJ_NEW_SMALL_INT(sector);
160+
fs_user_mount->readblocks[3] = mp_obj_new_bytearray_by_ref(count * 512, buff);
161+
mp_call_method_n_kw(2, 0, fs_user_mount->readblocks);
162+
return RES_OK;
135163
}
136164

137165
return RES_PARERR;
@@ -165,6 +193,16 @@ DRESULT disk_write (
165193
}
166194
return RES_OK;
167195
#endif
196+
197+
case PD_USER:
198+
if (fs_user_mount->writeblocks[0] == MP_OBJ_NULL) {
199+
// read-only block device
200+
return RES_ERROR;
201+
}
202+
fs_user_mount->writeblocks[2] = MP_OBJ_NEW_SMALL_INT(sector);
203+
fs_user_mount->writeblocks[3] = mp_obj_new_bytearray_by_ref(count * 512, (void*)buff);
204+
mp_call_method_n_kw(2, 0, fs_user_mount->writeblocks);
205+
return RES_OK;
168206
}
169207

170208
return RES_PARERR;
@@ -208,6 +246,26 @@ DRESULT disk_ioctl (
208246
}
209247
break;
210248
#endif
249+
250+
case PD_USER:
251+
switch (cmd) {
252+
case CTRL_SYNC:
253+
if (fs_user_mount->sync[0] != MP_OBJ_NULL) {
254+
mp_call_method_n_kw(0, 0, fs_user_mount->sync);
255+
}
256+
return RES_OK;
257+
258+
case GET_BLOCK_SIZE:
259+
*((DWORD*)buff) = 1; // high-level sector erase size in units of the small (512) bl
260+
return RES_OK;
261+
262+
case GET_SECTOR_COUNT: {
263+
mp_obj_t ret = mp_call_method_n_kw(0, 0, fs_user_mount->count);
264+
*((DWORD*)buff) = mp_obj_get_int(ret);
265+
return RES_OK;
266+
}
267+
}
268+
break;
211269
}
212270

213271
return RES_PARERR;

stmhal/ffconf.c

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,43 +26,52 @@
2626

2727
#include <string.h>
2828

29-
#include "mpconfigport.h"
29+
#include "mpconfig.h"
30+
#include "misc.h"
31+
#include "qstr.h"
32+
#include "obj.h"
3033
#include "ff.h"
3134
#include "ffconf.h"
35+
#include "fsusermount.h"
3236

37+
#if _FS_RPATH
3338
extern BYTE ff_CurrVol;
39+
#endif
40+
41+
STATIC bool check_path(const TCHAR **path, const char *mount_point_str, mp_uint_t mount_point_len) {
42+
if (strncmp(*path, mount_point_str, mount_point_len) == 0) {
43+
if ((*path)[mount_point_len] == '/') {
44+
*path += mount_point_len;
45+
return true;
46+
} else if ((*path)[mount_point_len] == '\0') {
47+
*path = "/";
48+
return true;
49+
}
50+
}
51+
return false;
52+
}
3453

3554
// "path" is the path to lookup; will advance this pointer beyond the volume name.
3655
// Returns logical drive number (-1 means invalid path).
37-
int ff_get_ldnumber (const TCHAR** path) {
56+
int ff_get_ldnumber (const TCHAR **path) {
3857
if (!(*path)) {
3958
return -1;
4059
}
4160

4261
if (**path != '/') {
43-
#if _FS_RPATH
62+
#if _FS_RPATH
4463
return ff_CurrVol;
45-
#else
64+
#else
4665
return -1;
47-
#endif
48-
} else if (strncmp(*path, "/flash", 6) == 0) {
49-
if ((*path)[6] == '/') {
50-
*path += 6;
51-
} else if ((*path)[6] == '\0') {
52-
*path = "/";
53-
} else {
54-
return -1;
55-
}
66+
#endif
67+
}
68+
69+
if (check_path(path, "/flash", 6)) {
5670
return 0;
57-
} else if (strncmp(*path, "/sd", 3) == 0) {
58-
if ((*path)[3] == '/') {
59-
*path += 3;
60-
} else if ((*path)[3] == '\0') {
61-
*path = "/";
62-
} else {
63-
return -1;
64-
}
71+
} else if (check_path(path, "/sd", 3)) {
6572
return 1;
73+
} else if (fs_user_mount != NULL && check_path(path, fs_user_mount->str, fs_user_mount->len)) {
74+
return 2;
6675
} else {
6776
return -1;
6877
}
@@ -72,8 +81,11 @@ void ff_get_volname(BYTE vol, TCHAR **dest) {
7281
if (vol == 0) {
7382
memcpy(*dest, "/flash", 6);
7483
*dest += 6;
75-
} else {
84+
} else if (vol == 1) {
7685
memcpy(*dest, "/sd", 3);
7786
*dest += 3;
87+
} else {
88+
memcpy(*dest, fs_user_mount->str, fs_user_mount->len);
89+
*dest += fs_user_mount->len;
7890
}
7991
}

stmhal/ffconf.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@
176176
/ Drive/Volume Configurations
177177
/---------------------------------------------------------------------------*/
178178

179-
#define _VOLUMES 2
179+
#define _VOLUMES 3
180180
/* Number of volumes (logical drives) to be used. */
181181

182182

stmhal/fsusermount.c

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/*
2+
* This file is part of the Micro Python project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2014 Damien P. George
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#include "mpconfig.h"
28+
#include "misc.h"
29+
#include "nlr.h"
30+
#include "qstr.h"
31+
#include "obj.h"
32+
#include "runtime.h"
33+
#include "ff.h"
34+
#include "fsusermount.h"
35+
36+
// for user-mountable block device
37+
fs_user_mount_t *fs_user_mount;
38+
39+
STATIC mp_obj_t pyb_mount(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
40+
static const mp_arg_t allowed_args[] = {
41+
{ MP_QSTR_readonly, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
42+
{ MP_QSTR_mkfs, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
43+
};
44+
45+
// parse args
46+
mp_obj_t device = pos_args[0];
47+
mp_obj_t mount_point = pos_args[1];
48+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
49+
mp_arg_parse_all(n_args - 2, pos_args + 2, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
50+
51+
// get the mount point
52+
mp_uint_t mnt_len;
53+
const char *mnt_str = mp_obj_str_get_data(mount_point, &mnt_len);
54+
55+
if (device == mp_const_none) {
56+
// umount
57+
FRESULT res = FR_NO_FILESYSTEM;
58+
if (fs_user_mount != NULL) {
59+
res = f_mount(NULL, fs_user_mount->str, 0);
60+
m_del_obj(fs_user_mount_t, fs_user_mount);
61+
fs_user_mount = NULL;
62+
}
63+
if (res != FR_OK) {
64+
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't umount"));
65+
}
66+
} else {
67+
// mount
68+
if (fs_user_mount != NULL) {
69+
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "device already mounted"));
70+
}
71+
72+
// create new object
73+
fs_user_mount = m_new_obj(fs_user_mount_t);
74+
fs_user_mount->str = mnt_str;
75+
fs_user_mount->len = mnt_len;
76+
77+
// load block protocol methods
78+
mp_load_method(device, MP_QSTR_readblocks, fs_user_mount->readblocks);
79+
mp_load_method_maybe(device, MP_QSTR_writeblocks, fs_user_mount->writeblocks);
80+
mp_load_method_maybe(device, MP_QSTR_sync, fs_user_mount->sync);
81+
mp_load_method(device, MP_QSTR_count, fs_user_mount->count);
82+
83+
// Read-only device indicated by writeblocks[0] == MP_OBJ_NULL.
84+
// User can specify read-only device by:
85+
// 1. readonly=True keyword argument
86+
// 2. nonexistent writeblocks method (then writeblocks[0] == MP_OBJ_NULL already)
87+
if (args[0].u_bool) {
88+
fs_user_mount->writeblocks[0] = MP_OBJ_NULL;
89+
}
90+
91+
// mount the block device
92+
FRESULT res = f_mount(&fs_user_mount->fatfs, fs_user_mount->str, 1);
93+
94+
// check the result
95+
if (res == FR_OK) {
96+
} else if (res == FR_NO_FILESYSTEM && args[1].u_bool) {
97+
res = f_mkfs(fs_user_mount->str, 1, 0);
98+
if (res != FR_OK) {
99+
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't mkfs"));
100+
}
101+
} else {
102+
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't mount"));
103+
}
104+
105+
/*
106+
if (fs_user_mount->writeblocks[0] == MP_OBJ_NULL) {
107+
printf("mounted read-only");
108+
} else {
109+
printf("mounted read-write");
110+
}
111+
DWORD nclst;
112+
FATFS *fatfs;
113+
f_getfree(fs_user_mount.str, &nclst, &fatfs);
114+
printf(" on %s with %u bytes free\n", fs_user_mount.str, (uint)(nclst * fatfs->csize * 512));
115+
*/
116+
}
117+
return mp_const_none;
118+
}
119+
MP_DEFINE_CONST_FUN_OBJ_KW(pyb_mount_obj, 2, pyb_mount);

0 commit comments

Comments
 (0)