Merge remote-tracking branch 'upstream/master' into nrf5_no_sdk

This commit is contained in:
Glenn Ruben Bakke 2017-05-15 20:15:32 +02:00
commit acce3a59d5
63 changed files with 668 additions and 247 deletions

View File

@ -155,6 +155,7 @@ STATIC const mp_map_elem_t os_module_globals_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR_chdir), (mp_obj_t)&mp_vfs_chdir_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_chdir), (mp_obj_t)&mp_vfs_chdir_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_getcwd), (mp_obj_t)&mp_vfs_getcwd_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_getcwd), (mp_obj_t)&mp_vfs_getcwd_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_ilistdir), (mp_obj_t)&mp_vfs_ilistdir_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_listdir), (mp_obj_t)&mp_vfs_listdir_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_listdir), (mp_obj_t)&mp_vfs_listdir_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_mkdir), (mp_obj_t)&mp_vfs_mkdir_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_mkdir), (mp_obj_t)&mp_vfs_mkdir_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_rename), (mp_obj_t)&mp_vfs_rename_obj}, { MP_OBJ_NEW_QSTR(MP_QSTR_rename), (mp_obj_t)&mp_vfs_rename_obj},

View File

@ -1,6 +1,17 @@
MicroPython libraries MicroPython libraries
===================== =====================
.. warning::
Important summary of this section
* MicroPython implements a subset of Python functionality for each module.
* To ease extensibility, MicroPython versions of standard Python modules
usually have ``u`` (micro) prefix.
* Any particular MicroPython variant or port may miss any feature/function
described in this general documentation, due to resource constraints.
This chapter describes modules (function and class libraries) which are built This chapter describes modules (function and class libraries) which are built
into MicroPython. There are a few categories of modules: into MicroPython. There are a few categories of modules:

View File

@ -158,15 +158,6 @@ Methods
and get the value of the pin. It is equivalent to Pin.value([x]). and get the value of the pin. It is equivalent to Pin.value([x]).
See :meth:`Pin.value` for more details. See :meth:`Pin.value` for more details.
.. method:: Pin.toggle()
Toggle the output value of the pin. Equivalent to ``pin.value(not pin.out_value())``.
Returns ``None``.
Not all ports implement this method.
Availability: WiPy.
.. method:: Pin.id() .. method:: Pin.id()
Get the pin identifier. This may return the ``id`` as specified in the Get the pin identifier. This may return the ``id`` as specified in the

View File

@ -0,0 +1,96 @@
.. currentmodule:: machine
.. _machine.Signal:
class Signal -- control and sense external I/O devices
======================================================
The Signal class is a simple extension of Pin class. Unlike Pin, which
can be only in "absolute" 0 and 1 states, a Signal can be in "asserted"
(on) or "deasserted" (off) states, while being inverted (active-low) or
not. Summing up, it adds logical inversion support to Pin functionality.
While this may seem a simple addition, it is exactly what is needed to
support wide array of simple digital devices in a way portable across
different boards, which is one of the major MicroPython goals. Regardless
whether different users have an active-high or active-low LED, a normally
open or normally closed relay - you can develop single, nicely looking
application which works with each of them, and capture hardware
configuration differences in few lines on the config file of your app.
Following is the guide when Signal vs Pin should be used:
* Use Signal: If you want to control a simple on/off (including software
PWM!) devices like LEDs, multi-segment indicators, relays, buzzers, or
read simple binary sensors, like normally open or normally closed buttons,
pulled high or low, Reed switches, moisture/flame detectors, etc. etc.
Summing up, if you have a real physical device/sensor requiring GPIO
access, you likely should use a Signal.
* Use Pin: If you implement a higher-level protocol or bus to communicate
with more complex devices.
The split between Pin and Signal come from the usecases above and the
architecture of MicroPython: Pin offers the lowest overhead, which may
be important when bit-banging protocols. But Signal adds additional
flexibility on top of Pin, at the cost of minor overhead (much smaller
than if you implemented active-high vs active-low device differences in
Python manually!). Also, Pin is low-level object which needs to be
implemented for each support board, while Signal is a high-level object
which comes for free once Pin is implemented.
If in doubt, give the Signal a try! Once again, it is developed to save
developers from the need to handle unexciting differences like active-low
vs active-high signals, and allow other users to share and enjoy your
application, instead of being frustrated by the fact that it doesn't
work for them simply because their LEDs or relays are wired in a slightly
different way.
Constructors
------------
.. class:: Signal(pin_obj, invert=False)
Signal(pin_arguments..., \*, invert=False)
Create a Signal object. There're two ways to create it:
* By wrapping existing Pin object - universal method which works for
any board.
* By passing required Pin parameters directly to Signal constructor,
skipping the need to create intermediate Pin object. Available on
many, but not all boards.
The arguments are:
- ``pin_obj`` is existing Pin object.
- ``pin_arguments`` are the same arguments as can be passed to Pin constructor.
- ``invert`` - if True, the signal will be inverted (active low).
Methods
-------
.. method:: Signal.value([x])
This method allows to set and get the value of the signal, depending on whether
the argument ``x`` is supplied or not.
If the argument is omitted then this method gets the signal level, 1 meaning
signal is asserted (active) and 0 - signal inactive.
If the argument is supplied then this method sets the signal level. The
argument ``x`` can be anything that converts to a boolean. If it converts
to ``True``, the signal is active, otherwise it is inactive.
Correspondence between signal being active and actual logic level on the
underlying pin depends on whether signal is inverted (active-low) or not.
For non-inverted signal, active status corresponds to logical 1, inactive -
to logical 0. For inverted/active-low signal, active status corresponds
to logical 0, while inactive - to logical 1.
.. method:: Signal.on()
Activate signal.
.. method:: Signal.off()
Deactivate signal.

View File

@ -147,6 +147,7 @@ Classes
machine.I2C.rst machine.I2C.rst
machine.Pin.rst machine.Pin.rst
machine.Signal.rst
machine.RTC.rst machine.RTC.rst
machine.SPI.rst machine.SPI.rst
machine.Timer.rst machine.Timer.rst

View File

@ -37,6 +37,21 @@ Functions
Get the current directory. Get the current directory.
.. function:: ilistdir([dir])
This function returns an iterator which then yields 3-tuples corresponding to
the entries in the directory that it is listing. With no argument it lists the
current directory, otherwise it lists the directory given by `dir`.
The 3-tuples have the form `(name, type, inode)`:
- `name` is a string (or bytes if `dir` is a bytes object) and is the name of
the entry;
- `type` is an integer that specifies the type of the entry, with 0x4000 for
directories and 0x8000 for regular files;
- `inode` is an integer corresponding to the inode of the file, and may be 0
for filesystems that don't have such a notion.
.. function:: listdir([dir]) .. function:: listdir([dir])
With no argument, list the current directory. Otherwise list the given directory. With no argument, list the current directory. Otherwise list the given directory.

View File

@ -5,7 +5,7 @@ MEMORY
dport0_0_seg : org = 0x3ff00000, len = 0x10 dport0_0_seg : org = 0x3ff00000, len = 0x10
dram0_0_seg : org = 0x3ffe8000, len = 0x14000 dram0_0_seg : org = 0x3ffe8000, len = 0x14000
iram1_0_seg : org = 0x40100000, len = 0x8000 iram1_0_seg : org = 0x40100000, len = 0x8000
irom0_0_seg : org = 0x40209000, len = 0x87000 irom0_0_seg : org = 0x40209000, len = 0x8f000
} }
/* define common sections and symbols */ /* define common sections and symbols */

View File

@ -19,6 +19,8 @@ EXTERN(_KernelExceptionVector)
EXTERN(_NMIExceptionVector) EXTERN(_NMIExceptionVector)
EXTERN(_UserExceptionVector) EXTERN(_UserExceptionVector)
_firmware_size = ORIGIN(irom0_0_seg) + LENGTH(irom0_0_seg) - 0x40200000;
PROVIDE(_memmap_vecbase_reset = 0x40000000); PROVIDE(_memmap_vecbase_reset = 0x40000000);
/* Various memory-map dependent cache attribute settings: */ /* Various memory-map dependent cache attribute settings: */

View File

@ -6,7 +6,7 @@ MEMORY
dram0_0_seg : org = 0x3ffe8000, len = 0x14000 dram0_0_seg : org = 0x3ffe8000, len = 0x14000
iram1_0_seg : org = 0x40100000, len = 0x8000 iram1_0_seg : org = 0x40100000, len = 0x8000
/* 0x3c000 is size of bootloader, 0x9000 is size of packed RAM segments */ /* 0x3c000 is size of bootloader, 0x9000 is size of packed RAM segments */
irom0_0_seg : org = 0x40200000 + 0x3c000 + 0x9000, len = 0x87000 irom0_0_seg : org = 0x40200000 + 0x3c000 + 0x9000, len = 0x8f000
} }
/* define common sections and symbols */ /* define common sections and symbols */

View File

@ -120,11 +120,13 @@ bool ets_loop_iter(void) {
} }
// handle overflow of system microsecond counter // handle overflow of system microsecond counter
ets_intr_lock();
uint32_t system_time_cur = system_get_time(); uint32_t system_time_cur = system_get_time();
if (system_time_cur < system_time_prev) { if (system_time_cur < system_time_prev) {
system_time_high_word += 1; // record overflow of low 32-bits system_time_high_word += 1; // record overflow of low 32-bits
} }
system_time_prev = system_time_cur; system_time_prev = system_time_cur;
ets_intr_unlock();
//static unsigned cnt; //static unsigned cnt;
bool progress = false; bool progress = false;

View File

@ -159,12 +159,10 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(esp_flash_size_obj, esp_flash_size);
// we assume there's a yaota8266 bootloader. // we assume there's a yaota8266 bootloader.
#define IS_OTA_FIRMWARE() ((*(uint32_t*)0x40200000 & 0xff00) == 0x100) #define IS_OTA_FIRMWARE() ((*(uint32_t*)0x40200000 & 0xff00) == 0x100)
extern byte _firmware_size[];
STATIC mp_obj_t esp_flash_user_start(void) { STATIC mp_obj_t esp_flash_user_start(void) {
if (IS_OTA_FIRMWARE()) { return MP_OBJ_NEW_SMALL_INT((uint32_t)_firmware_size);
return MP_OBJ_NEW_SMALL_INT(0x3c000 + 0x90000);
} else {
return MP_OBJ_NEW_SMALL_INT(0x90000);
}
} }
STATIC MP_DEFINE_CONST_FUN_OBJ_0(esp_flash_user_start_obj, esp_flash_user_start); STATIC MP_DEFINE_CONST_FUN_OBJ_0(esp_flash_user_start_obj, esp_flash_user_start);

View File

@ -3,7 +3,7 @@ import esp
class FlashBdev: class FlashBdev:
SEC_SIZE = 4096 SEC_SIZE = 4096
RESERVED_SECS = 0 RESERVED_SECS = 1
START_SEC = esp.flash_user_start() // SEC_SIZE + RESERVED_SECS START_SEC = esp.flash_user_start() // SEC_SIZE + RESERVED_SECS
NUM_BLK = 0x6b - RESERVED_SECS NUM_BLK = 0x6b - RESERVED_SECS

View File

@ -93,6 +93,7 @@ STATIC const mp_rom_map_elem_t os_module_globals_table[] = {
#endif #endif
#if MICROPY_VFS_FAT #if MICROPY_VFS_FAT
{ MP_ROM_QSTR(MP_QSTR_VfsFat), MP_ROM_PTR(&mp_fat_vfs_type) }, { MP_ROM_QSTR(MP_QSTR_VfsFat), MP_ROM_PTR(&mp_fat_vfs_type) },
{ MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&mp_vfs_ilistdir_obj) },
{ MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&mp_vfs_listdir_obj) }, { MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&mp_vfs_listdir_obj) },
{ MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mp_vfs_mkdir_obj) }, { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mp_vfs_mkdir_obj) },
{ MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&mp_vfs_rmdir_obj) }, { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&mp_vfs_rmdir_obj) },

View File

@ -2,4 +2,4 @@ from machine import Pin, Signal
# ESP12 module as used by many boards # ESP12 module as used by many boards
# Blue LED on pin 2, active low (inverted) # Blue LED on pin 2, active low (inverted)
LED = Signal(Pin(2, Pin.OUT), invert=True) LED = Signal(2, Pin.OUT, invert=True)

View File

@ -1,13 +1,13 @@
from machine import Pin, Signal from machine import Pin, Signal
# Red LED on pin LED_RED also kown as A13 # Red LED on pin LED_RED also kown as A13
LED = Signal(Pin('LED_RED', Pin.OUT)) LED = Signal('LED_RED', Pin.OUT)
# Green LED on pin LED_GREEN also known as A14 # Green LED on pin LED_GREEN also known as A14
LED2 = Signal(Pin('LED_GREEN', Pin.OUT)) LED2 = Signal('LED_GREEN', Pin.OUT)
# Yellow LED on pin LED_YELLOW also known as A15 # Yellow LED on pin LED_YELLOW also known as A15
LED3 = Signal(Pin('LED_YELLOW', Pin.OUT)) LED3 = Signal('LED_YELLOW', Pin.OUT)
# Blue LED on pin LED_BLUE also known as B4 # Blue LED on pin LED_BLUE also known as B4
LED4 = Signal(Pin('LED_BLUE', Pin.OUT)) LED4 = Signal('LED_BLUE', Pin.OUT)

View File

@ -2,4 +2,4 @@ from machine import Pin, Signal
# Freescale/NXP FRDM-K64F board # Freescale/NXP FRDM-K64F board
# Blue LED on port B, pin 21 # Blue LED on port B, pin 21
LED = Signal(Pin(("GPIO_1", 21), Pin.OUT)) LED = Signal(("GPIO_1", 21), Pin.OUT)

View File

@ -286,7 +286,49 @@ mp_obj_t mp_vfs_getcwd(void) {
} }
MP_DEFINE_CONST_FUN_OBJ_0(mp_vfs_getcwd_obj, mp_vfs_getcwd); MP_DEFINE_CONST_FUN_OBJ_0(mp_vfs_getcwd_obj, mp_vfs_getcwd);
mp_obj_t mp_vfs_listdir(size_t n_args, const mp_obj_t *args) { typedef struct _mp_vfs_ilistdir_it_t {
mp_obj_base_t base;
mp_fun_1_t iternext;
union {
mp_vfs_mount_t *vfs;
mp_obj_t iter;
} cur;
bool is_str;
bool is_iter;
} mp_vfs_ilistdir_it_t;
STATIC mp_obj_t mp_vfs_ilistdir_it_iternext(mp_obj_t self_in) {
mp_vfs_ilistdir_it_t *self = MP_OBJ_TO_PTR(self_in);
if (self->is_iter) {
// continue delegating to root dir
return mp_iternext(self->cur.iter);
} else if (self->cur.vfs == NULL) {
// finished iterating mount points and no root dir is mounted
return MP_OBJ_STOP_ITERATION;
} else {
// continue iterating mount points
mp_vfs_mount_t *vfs = self->cur.vfs;
self->cur.vfs = vfs->next;
if (vfs->len == 1) {
// vfs is mounted at root dir, delegate to it
mp_obj_t root = mp_obj_new_str("/", 1, false);
self->is_iter = true;
self->cur.iter = mp_vfs_proxy_call(vfs, MP_QSTR_ilistdir, 1, &root);
return mp_iternext(self->cur.iter);
} else {
// a mounted directory
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL));
t->items[0] = mp_obj_new_str_of_type(
self->is_str ? &mp_type_str : &mp_type_bytes,
(const byte*)vfs->str + 1, vfs->len - 1);
t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFDIR);
t->items[2] = MP_OBJ_NEW_SMALL_INT(0); // no inode number
return MP_OBJ_FROM_PTR(t);
}
}
}
mp_obj_t mp_vfs_ilistdir(size_t n_args, const mp_obj_t *args) {
mp_obj_t path_in; mp_obj_t path_in;
if (n_args == 1) { if (n_args == 1) {
path_in = args[0]; path_in = args[0];
@ -299,22 +341,29 @@ mp_obj_t mp_vfs_listdir(size_t n_args, const mp_obj_t *args) {
if (vfs == MP_VFS_ROOT) { if (vfs == MP_VFS_ROOT) {
// list the root directory // list the root directory
mp_obj_t dir_list = mp_obj_new_list(0, NULL); mp_vfs_ilistdir_it_t *iter = m_new_obj(mp_vfs_ilistdir_it_t);
for (vfs = MP_STATE_VM(vfs_mount_table); vfs != NULL; vfs = vfs->next) { iter->base.type = &mp_type_polymorph_iter;
if (vfs->len == 1) { iter->iternext = mp_vfs_ilistdir_it_iternext;
// vfs is mounted at root dir, delegate to it iter->cur.vfs = MP_STATE_VM(vfs_mount_table);
mp_obj_t root = mp_obj_new_str("/", 1, false); iter->is_str = mp_obj_get_type(path_in) == &mp_type_str;
mp_obj_t dir_list2 = mp_vfs_proxy_call(vfs, MP_QSTR_listdir, 1, &root); iter->is_iter = false;
dir_list = mp_binary_op(MP_BINARY_OP_ADD, dir_list, dir_list2); return MP_OBJ_FROM_PTR(iter);
} else {
mp_obj_list_append(dir_list, mp_obj_new_str_of_type(mp_obj_get_type(path_in),
(const byte*)vfs->str + 1, vfs->len - 1));
}
}
return dir_list;
} }
return mp_vfs_proxy_call(vfs, MP_QSTR_listdir, 1, &path_out); return mp_vfs_proxy_call(vfs, MP_QSTR_ilistdir, 1, &path_out);
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_vfs_ilistdir_obj, 0, 1, mp_vfs_ilistdir);
mp_obj_t mp_vfs_listdir(size_t n_args, const mp_obj_t *args) {
mp_obj_t iter = mp_vfs_ilistdir(n_args, args);
mp_obj_t dir_list = mp_obj_new_list(0, NULL);
mp_obj_t next;
while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
mp_obj_t *items;
mp_obj_get_array_fixed_n(next, 3, &items);
mp_obj_list_append(dir_list, items[0]);
}
return dir_list;
} }
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_vfs_listdir_obj, 0, 1, mp_vfs_listdir); MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_vfs_listdir_obj, 0, 1, mp_vfs_listdir);
@ -359,7 +408,7 @@ mp_obj_t mp_vfs_stat(mp_obj_t path_in) {
mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out); mp_vfs_mount_t *vfs = lookup_path(path_in, &path_out);
if (vfs == MP_VFS_ROOT) { if (vfs == MP_VFS_ROOT) {
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL)); mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
t->items[0] = MP_OBJ_NEW_SMALL_INT(0x4000); // st_mode = stat.S_IFDIR t->items[0] = MP_OBJ_NEW_SMALL_INT(MP_S_IFDIR); // st_mode
for (int i = 1; i <= 9; ++i) { for (int i = 1; i <= 9; ++i) {
t->items[i] = MP_OBJ_NEW_SMALL_INT(0); // dev, nlink, uid, gid, size, atime, mtime, ctime t->items[i] = MP_OBJ_NEW_SMALL_INT(0); // dev, nlink, uid, gid, size, atime, mtime, ctime
} }

View File

@ -35,6 +35,10 @@
#define MP_VFS_NONE ((mp_vfs_mount_t*)1) #define MP_VFS_NONE ((mp_vfs_mount_t*)1)
#define MP_VFS_ROOT ((mp_vfs_mount_t*)0) #define MP_VFS_ROOT ((mp_vfs_mount_t*)0)
// MicroPython's port-standardized versions of stat constants
#define MP_S_IFDIR (0x4000)
#define MP_S_IFREG (0x8000)
// constants for block protocol ioctl // constants for block protocol ioctl
#define BP_IOCTL_INIT (1) #define BP_IOCTL_INIT (1)
#define BP_IOCTL_DEINIT (2) #define BP_IOCTL_DEINIT (2)
@ -56,6 +60,7 @@ mp_obj_t mp_vfs_umount(mp_obj_t mnt_in);
mp_obj_t mp_vfs_open(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); mp_obj_t mp_vfs_open(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
mp_obj_t mp_vfs_chdir(mp_obj_t path_in); mp_obj_t mp_vfs_chdir(mp_obj_t path_in);
mp_obj_t mp_vfs_getcwd(void); mp_obj_t mp_vfs_getcwd(void);
mp_obj_t mp_vfs_ilistdir(size_t n_args, const mp_obj_t *args);
mp_obj_t mp_vfs_listdir(size_t n_args, const mp_obj_t *args); mp_obj_t mp_vfs_listdir(size_t n_args, const mp_obj_t *args);
mp_obj_t mp_vfs_mkdir(mp_obj_t path_in); mp_obj_t mp_vfs_mkdir(mp_obj_t path_in);
mp_obj_t mp_vfs_remove(mp_obj_t path_in); mp_obj_t mp_vfs_remove(mp_obj_t path_in);
@ -69,6 +74,7 @@ MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_umount_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(mp_vfs_open_obj); MP_DECLARE_CONST_FUN_OBJ_KW(mp_vfs_open_obj);
MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_chdir_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_chdir_obj);
MP_DECLARE_CONST_FUN_OBJ_0(mp_vfs_getcwd_obj); MP_DECLARE_CONST_FUN_OBJ_0(mp_vfs_getcwd_obj);
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_vfs_ilistdir_obj);
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_vfs_listdir_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_vfs_listdir_obj);
MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_mkdir_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_mkdir_obj);
MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_remove_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_remove_obj);

View File

@ -91,7 +91,7 @@ STATIC MP_DEFINE_CONST_STATICMETHOD_OBJ(fat_vfs_mkfs_obj, MP_ROM_PTR(&fat_vfs_mk
STATIC MP_DEFINE_CONST_FUN_OBJ_3(fat_vfs_open_obj, fatfs_builtin_open_self); STATIC MP_DEFINE_CONST_FUN_OBJ_3(fat_vfs_open_obj, fatfs_builtin_open_self);
STATIC mp_obj_t fat_vfs_listdir_func(size_t n_args, const mp_obj_t *args) { STATIC mp_obj_t fat_vfs_ilistdir_func(size_t n_args, const mp_obj_t *args) {
mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(args[0]); mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(args[0]);
bool is_str_type = true; bool is_str_type = true;
const char *path; const char *path;
@ -104,9 +104,9 @@ STATIC mp_obj_t fat_vfs_listdir_func(size_t n_args, const mp_obj_t *args) {
path = ""; path = "";
} }
return fat_vfs_listdir2(self, path, is_str_type); return fat_vfs_ilistdir2(self, path, is_str_type);
} }
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fat_vfs_listdir_obj, 1, 2, fat_vfs_listdir_func); STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fat_vfs_ilistdir_obj, 1, 2, fat_vfs_ilistdir_func);
STATIC mp_obj_t fat_vfs_remove_internal(mp_obj_t vfs_in, mp_obj_t path_in, mp_int_t attr) { STATIC mp_obj_t fat_vfs_remove_internal(mp_obj_t vfs_in, mp_obj_t path_in, mp_int_t attr) {
mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in); mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in);
@ -225,9 +225,9 @@ STATIC mp_obj_t fat_vfs_stat(mp_obj_t vfs_in, mp_obj_t path_in) {
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL)); mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(10, NULL));
mp_int_t mode = 0; mp_int_t mode = 0;
if (fno.fattrib & AM_DIR) { if (fno.fattrib & AM_DIR) {
mode |= 0x4000; // stat.S_IFDIR mode |= MP_S_IFDIR;
} else { } else {
mode |= 0x8000; // stat.S_IFREG mode |= MP_S_IFREG;
} }
mp_int_t seconds = timeutils_seconds_since_2000( mp_int_t seconds = timeutils_seconds_since_2000(
1980 + ((fno.fdate >> 9) & 0x7f), 1980 + ((fno.fdate >> 9) & 0x7f),
@ -321,7 +321,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_umount_obj, vfs_fat_umount);
STATIC const mp_rom_map_elem_t fat_vfs_locals_dict_table[] = { STATIC const mp_rom_map_elem_t fat_vfs_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_mkfs), MP_ROM_PTR(&fat_vfs_mkfs_obj) }, { MP_ROM_QSTR(MP_QSTR_mkfs), MP_ROM_PTR(&fat_vfs_mkfs_obj) },
{ MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&fat_vfs_open_obj) }, { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&fat_vfs_open_obj) },
{ MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&fat_vfs_listdir_obj) }, { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&fat_vfs_ilistdir_obj) },
{ MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&fat_vfs_mkdir_obj) }, { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&fat_vfs_mkdir_obj) },
{ MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&fat_vfs_rmdir_obj) }, { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&fat_vfs_rmdir_obj) },
{ MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&fat_vfs_chdir_obj) }, { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&fat_vfs_chdir_obj) },

View File

@ -57,4 +57,4 @@ mp_import_stat_t fat_vfs_import_stat(struct _fs_user_mount_t *vfs, const char *p
mp_obj_t fatfs_builtin_open_self(mp_obj_t self_in, mp_obj_t path, mp_obj_t mode); mp_obj_t fatfs_builtin_open_self(mp_obj_t self_in, mp_obj_t path, mp_obj_t mode);
MP_DECLARE_CONST_FUN_OBJ_KW(mp_builtin_open_obj); MP_DECLARE_CONST_FUN_OBJ_KW(mp_builtin_open_obj);
mp_obj_t fat_vfs_listdir2(struct _fs_user_mount_t *vfs, const char *path, bool is_str_type); mp_obj_t fat_vfs_ilistdir2(struct _fs_user_mount_t *vfs, const char *path, bool is_str_type);

View File

@ -34,51 +34,62 @@
#include "extmod/vfs_fat.h" #include "extmod/vfs_fat.h"
#include "py/lexer.h" #include "py/lexer.h"
// TODO: actually, the core function should be ilistdir() typedef struct _mp_vfs_fat_ilistdir_it_t {
mp_obj_base_t base;
mp_obj_t fat_vfs_listdir2(fs_user_mount_t *vfs, const char *path, bool is_str_type) { mp_fun_1_t iternext;
FRESULT res; bool is_str;
FILINFO fno;
FF_DIR dir; FF_DIR dir;
} mp_vfs_fat_ilistdir_it_t;
res = f_opendir(&vfs->fatfs, &dir, path); STATIC mp_obj_t mp_vfs_fat_ilistdir_it_iternext(mp_obj_t self_in) {
mp_vfs_fat_ilistdir_it_t *self = MP_OBJ_TO_PTR(self_in);
for (;;) {
FILINFO fno;
FRESULT res = f_readdir(&self->dir, &fno);
char *fn = fno.fname;
if (res != FR_OK || fn[0] == 0) {
// stop on error or end of dir
break;
}
// Note that FatFS already filters . and .., so we don't need to
// make 3-tuple with info about this entry
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL));
if (self->is_str) {
t->items[0] = mp_obj_new_str(fn, strlen(fn), false);
} else {
t->items[0] = mp_obj_new_bytes((const byte*)fn, strlen(fn));
}
if (fno.fattrib & AM_DIR) {
// dir
t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFDIR);
} else {
// file
t->items[1] = MP_OBJ_NEW_SMALL_INT(MP_S_IFREG);
}
t->items[2] = MP_OBJ_NEW_SMALL_INT(0); // no inode number
return MP_OBJ_FROM_PTR(t);
}
// ignore error because we may be closing a second time
f_closedir(&self->dir);
return MP_OBJ_STOP_ITERATION;
}
mp_obj_t fat_vfs_ilistdir2(fs_user_mount_t *vfs, const char *path, bool is_str_type) {
mp_vfs_fat_ilistdir_it_t *iter = m_new_obj(mp_vfs_fat_ilistdir_it_t);
iter->base.type = &mp_type_polymorph_iter;
iter->iternext = mp_vfs_fat_ilistdir_it_iternext;
iter->is_str = is_str_type;
FRESULT res = f_opendir(&vfs->fatfs, &iter->dir, path);
if (res != FR_OK) { if (res != FR_OK) {
mp_raise_OSError(fresult_to_errno_table[res]); mp_raise_OSError(fresult_to_errno_table[res]);
} }
return MP_OBJ_FROM_PTR(iter);
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 */
char *fn = fno.fname;
/*
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;
} }
mp_import_stat_t fat_vfs_import_stat(fs_user_mount_t *vfs, const char *path) { mp_import_stat_t fat_vfs_import_stat(fs_user_mount_t *vfs, const char *path) {

View File

@ -1,3 +1,14 @@
# The following is a temporary hack to forefully undefine vars that might have
# be defined by a calling Makefile (from recursive make).
# TODO: Find a better way to be able to call this Makefile recursively.
override undefine COPT
override undefine CFLAGS_EXTRA
override undefine LDFLAGS_EXTRA
override undefine FROZEN_DIR
override undefine FROZEN_MPY_DIR
override undefine BUILD
override undefine PROG
include ../py/mkenv.mk include ../py/mkenv.mk
# define main target # define main target

View File

@ -327,9 +327,10 @@ void mp_binary_set_val_array(char typecode, void *p, mp_uint_t index, mp_obj_t v
break; break;
default: default:
#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE
if ((typecode | 0x20) == 'q' && MP_OBJ_IS_TYPE(val_in, &mp_type_int)) { if (MP_OBJ_IS_TYPE(val_in, &mp_type_int)) {
size_t size = mp_binary_get_size('@', typecode, NULL);
mp_obj_int_to_bytes_impl(val_in, MP_ENDIANNESS_BIG, mp_obj_int_to_bytes_impl(val_in, MP_ENDIANNESS_BIG,
sizeof(long long), (byte*)&((long long*)p)[index]); size, (uint8_t*)p + index * size);
return; return;
} }
#endif #endif

View File

@ -429,8 +429,13 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) {
// if args[3] (fromtuple) has magic value False, set up // if args[3] (fromtuple) has magic value False, set up
// this module for command-line "-m" option (set module's // this module for command-line "-m" option (set module's
// name to __main__ instead of real name). // name to __main__ instead of real name). Do this only
if (i == mod_len && fromtuple == mp_const_false) { // for *modules* however - packages never have their names
// replaced, instead they're -m'ed using a special __main__
// submodule in them. (This all apparently is done to not
// touch package name itself, which is important for future
// imports).
if (i == mod_len && fromtuple == mp_const_false && stat != MP_IMPORT_STAT_DIR) {
mp_obj_module_t *o = MP_OBJ_TO_PTR(module_obj); mp_obj_module_t *o = MP_OBJ_TO_PTR(module_obj);
mp_obj_dict_store(MP_OBJ_FROM_PTR(o->globals), MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR___main__)); mp_obj_dict_store(MP_OBJ_FROM_PTR(o->globals), MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR___main__));
#if MICROPY_CPYTHON_COMPAT #if MICROPY_CPYTHON_COMPAT

View File

@ -137,24 +137,19 @@ STATIC void next_char(mp_lexer_t *lex) {
lex->chr1 = lex->chr2; lex->chr1 = lex->chr2;
lex->chr2 = lex->reader.readbyte(lex->reader.data); lex->chr2 = lex->reader.readbyte(lex->reader.data);
if (lex->chr0 == '\r') { if (lex->chr1 == '\r') {
// CR is a new line, converted to LF // CR is a new line, converted to LF
lex->chr0 = '\n'; lex->chr1 = '\n';
if (lex->chr1 == '\n') { if (lex->chr2 == '\n') {
// CR LF is a single new line // CR LF is a single new line, throw out the extra LF
lex->chr1 = lex->chr2;
lex->chr2 = lex->reader.readbyte(lex->reader.data); lex->chr2 = lex->reader.readbyte(lex->reader.data);
} }
} }
if (lex->chr2 == MP_LEXER_EOF) { // check if we need to insert a newline at end of file
// EOF, check if we need to insert a newline at end of file if (lex->chr2 == MP_LEXER_EOF && lex->chr1 != MP_LEXER_EOF && lex->chr1 != '\n') {
if (lex->chr1 != MP_LEXER_EOF && lex->chr1 != '\n') {
// if lex->chr1 == '\r' then this makes a CR LF which will be converted to LF above
// otherwise it just inserts a LF
lex->chr2 = '\n'; lex->chr2 = '\n';
} }
}
} }
STATIC void indent_push(mp_lexer_t *lex, size_t indent) { STATIC void indent_push(mp_lexer_t *lex, size_t indent) {
@ -677,7 +672,7 @@ mp_lexer_t *mp_lexer_new(qstr src_name, mp_reader_t reader) {
lex->source_name = src_name; lex->source_name = src_name;
lex->reader = reader; lex->reader = reader;
lex->line = 1; lex->line = 1;
lex->column = 1; lex->column = -2; // account for 3 dummy bytes
lex->emit_dent = 0; lex->emit_dent = 0;
lex->nested_bracket_level = 0; lex->nested_bracket_level = 0;
lex->alloc_indent_level = MICROPY_ALLOC_LEXER_INDENT_INIT; lex->alloc_indent_level = MICROPY_ALLOC_LEXER_INDENT_INIT;
@ -688,27 +683,12 @@ mp_lexer_t *mp_lexer_new(qstr src_name, mp_reader_t reader) {
// store sentinel for first indentation level // store sentinel for first indentation level
lex->indent_level[0] = 0; lex->indent_level[0] = 0;
// preload characters // load lexer with start of file, advancing lex->column to 1
lex->chr0 = reader.readbyte(reader.data); // start with dummy bytes and use next_char() for proper EOL/EOF handling
lex->chr1 = reader.readbyte(reader.data); lex->chr0 = lex->chr1 = lex->chr2 = 0;
lex->chr2 = reader.readbyte(reader.data); next_char(lex);
next_char(lex);
// if input stream is 0, 1 or 2 characters long and doesn't end in a newline, then insert a newline at the end next_char(lex);
if (lex->chr0 == MP_LEXER_EOF) {
lex->chr0 = '\n';
} else if (lex->chr1 == MP_LEXER_EOF) {
if (lex->chr0 == '\r') {
lex->chr0 = '\n';
} else if (lex->chr0 != '\n') {
lex->chr1 = '\n';
}
} else if (lex->chr2 == MP_LEXER_EOF) {
if (lex->chr1 == '\r') {
lex->chr1 = '\n';
} else if (lex->chr1 != '\n') {
lex->chr2 = '\n';
}
}
// preload first token // preload first token
mp_lexer_to_next(lex); mp_lexer_to_next(lex);

View File

@ -102,15 +102,19 @@ $(BUILD)/frozen.c: $(wildcard $(FROZEN_DIR)/*) $(HEADER_BUILD) $(FROZEN_EXTRA_DE
endif endif
ifneq ($(FROZEN_MPY_DIR),) ifneq ($(FROZEN_MPY_DIR),)
# to build the MicroPython cross compiler
$(TOP)/mpy-cross/mpy-cross: $(TOP)/py/*.[ch] $(TOP)/mpy-cross/*.[ch] $(TOP)/windows/fmode.c
$(Q)$(MAKE) -C $(TOP)/mpy-cross
# make a list of all the .py files that need compiling and freezing # make a list of all the .py files that need compiling and freezing
FROZEN_MPY_PY_FILES := $(shell find -L $(FROZEN_MPY_DIR) -type f -name '*.py' | $(SED) -e 's=^$(FROZEN_MPY_DIR)/==') FROZEN_MPY_PY_FILES := $(shell find -L $(FROZEN_MPY_DIR) -type f -name '*.py' | $(SED) -e 's=^$(FROZEN_MPY_DIR)/==')
FROZEN_MPY_MPY_FILES := $(addprefix $(BUILD)/frozen_mpy/,$(FROZEN_MPY_PY_FILES:.py=.mpy)) FROZEN_MPY_MPY_FILES := $(addprefix $(BUILD)/frozen_mpy/,$(FROZEN_MPY_PY_FILES:.py=.mpy))
# to build .mpy files from .py files # to build .mpy files from .py files
$(BUILD)/frozen_mpy/%.mpy: $(FROZEN_MPY_DIR)/%.py $(BUILD)/frozen_mpy/%.mpy: $(FROZEN_MPY_DIR)/%.py $(TOP)/mpy-cross/mpy-cross
@$(ECHO) "MPY $<" @$(ECHO) "MPY $<"
$(Q)$(MKDIR) -p $(dir $@) $(Q)$(MKDIR) -p $(dir $@)
$(Q)$(MPY_CROSS) -o $@ -s $(^:$(FROZEN_MPY_DIR)/%=%) $(MPY_CROSS_FLAGS) $^ $(Q)$(MPY_CROSS) -o $@ -s $(<:$(FROZEN_MPY_DIR)/%=%) $(MPY_CROSS_FLAGS) $<
# to build frozen_mpy.c from all .mpy files # to build frozen_mpy.c from all .mpy files
$(BUILD)/frozen_mpy.c: $(FROZEN_MPY_MPY_FILES) $(BUILD)/genhdr/qstrdefs.generated.h $(BUILD)/frozen_mpy.c: $(FROZEN_MPY_MPY_FILES) $(BUILD)/genhdr/qstrdefs.generated.h

View File

@ -406,7 +406,7 @@ STATIC mp_obj_t mp_builtin_print(size_t n_args, const mp_obj_t *args, mp_map_t *
if (end_elem != NULL && end_elem->value != mp_const_none) { if (end_elem != NULL && end_elem->value != mp_const_none) {
end_data = mp_obj_str_get_data(end_elem->value, &end_len); end_data = mp_obj_str_get_data(end_elem->value, &end_len);
} }
#if MICROPY_PY_IO #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES
void *stream_obj = &mp_sys_stdout_obj; void *stream_obj = &mp_sys_stdout_obj;
mp_map_elem_t *file_elem = mp_map_lookup(kwargs, MP_OBJ_NEW_QSTR(MP_QSTR_file), MP_MAP_LOOKUP); mp_map_elem_t *file_elem = mp_map_lookup(kwargs, MP_OBJ_NEW_QSTR(MP_QSTR_file), MP_MAP_LOOKUP);
if (file_elem != NULL && file_elem->value != mp_const_none) { if (file_elem != NULL && file_elem->value != mp_const_none) {
@ -417,19 +417,19 @@ STATIC mp_obj_t mp_builtin_print(size_t n_args, const mp_obj_t *args, mp_map_t *
#endif #endif
for (mp_uint_t i = 0; i < n_args; i++) { for (mp_uint_t i = 0; i < n_args; i++) {
if (i > 0) { if (i > 0) {
#if MICROPY_PY_IO #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES
mp_stream_write_adaptor(stream_obj, sep_data, sep_len); mp_stream_write_adaptor(stream_obj, sep_data, sep_len);
#else #else
mp_print_strn(&mp_plat_print, sep_data, sep_len, 0, 0, 0); mp_print_strn(&mp_plat_print, sep_data, sep_len, 0, 0, 0);
#endif #endif
} }
#if MICROPY_PY_IO #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES
mp_obj_print_helper(&print, args[i], PRINT_STR); mp_obj_print_helper(&print, args[i], PRINT_STR);
#else #else
mp_obj_print_helper(&mp_plat_print, args[i], PRINT_STR); mp_obj_print_helper(&mp_plat_print, args[i], PRINT_STR);
#endif #endif
} }
#if MICROPY_PY_IO #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES
mp_stream_write_adaptor(stream_obj, end_data, end_len); mp_stream_write_adaptor(stream_obj, end_data, end_len);
#else #else
mp_print_strn(&mp_plat_print, end_data, end_len, 0, 0, 0); mp_print_strn(&mp_plat_print, end_data, end_len, 0, 0, 0);

View File

@ -45,7 +45,7 @@ extern struct _mp_dummy_t mp_sys_stdin_obj;
extern struct _mp_dummy_t mp_sys_stdout_obj; extern struct _mp_dummy_t mp_sys_stdout_obj;
extern struct _mp_dummy_t mp_sys_stderr_obj; extern struct _mp_dummy_t mp_sys_stderr_obj;
#if MICROPY_PY_IO #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES
const mp_print_t mp_sys_stdout_print = {&mp_sys_stdout_obj, mp_stream_write_adaptor}; const mp_print_t mp_sys_stdout_print = {&mp_sys_stdout_obj, mp_stream_write_adaptor};
#endif #endif
@ -106,7 +106,7 @@ STATIC mp_obj_t mp_sys_exit(size_t n_args, const mp_obj_t *args) {
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_sys_exit_obj, 0, 1, mp_sys_exit); MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_sys_exit_obj, 0, 1, mp_sys_exit);
STATIC mp_obj_t mp_sys_print_exception(size_t n_args, const mp_obj_t *args) { STATIC mp_obj_t mp_sys_print_exception(size_t n_args, const mp_obj_t *args) {
#if MICROPY_PY_IO #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES
void *stream_obj = &mp_sys_stdout_obj; void *stream_obj = &mp_sys_stdout_obj;
if (n_args > 1) { if (n_args > 1) {
stream_obj = MP_OBJ_TO_PTR(args[1]); // XXX may fail stream_obj = MP_OBJ_TO_PTR(args[1]); // XXX may fail

View File

@ -39,7 +39,7 @@
#define PF_FLAG_ADD_PERCENT (0x100) #define PF_FLAG_ADD_PERCENT (0x100)
#define PF_FLAG_SHOW_OCTAL_LETTER (0x200) #define PF_FLAG_SHOW_OCTAL_LETTER (0x200)
#if MICROPY_PY_IO #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES
# define MP_PYTHON_PRINTER &mp_sys_stdout_print # define MP_PYTHON_PRINTER &mp_sys_stdout_print
#else #else
# define MP_PYTHON_PRINTER &mp_plat_print # define MP_PYTHON_PRINTER &mp_plat_print
@ -55,7 +55,7 @@ typedef struct _mp_print_t {
// All (non-debug) prints go through one of the two interfaces below. // All (non-debug) prints go through one of the two interfaces below.
// 1) Wrapper for platform print function, which wraps MP_PLAT_PRINT_STRN. // 1) Wrapper for platform print function, which wraps MP_PLAT_PRINT_STRN.
extern const mp_print_t mp_plat_print; extern const mp_print_t mp_plat_print;
#if MICROPY_PY_IO #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES
// 2) Wrapper for printing to sys.stdout. // 2) Wrapper for printing to sys.stdout.
extern const mp_print_t mp_sys_stdout_print; extern const mp_print_t mp_sys_stdout_print;
#endif #endif

View File

@ -31,6 +31,7 @@ STATIC const mp_rom_map_elem_t os_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&mp_vfs_chdir_obj) }, { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&mp_vfs_chdir_obj) },
{ MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&mp_vfs_getcwd_obj) }, { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&mp_vfs_getcwd_obj) },
{ MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&mp_vfs_ilistdir_obj) },
{ MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&mp_vfs_listdir_obj) }, { MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&mp_vfs_listdir_obj) },
{ MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mp_vfs_mkdir_obj) }, { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mp_vfs_mkdir_obj) },
{ MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&mp_vfs_remove_obj) }, { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&mp_vfs_remove_obj) },

View File

@ -135,6 +135,7 @@ STATIC const mp_rom_map_elem_t os_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&mp_vfs_chdir_obj) }, { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&mp_vfs_chdir_obj) },
{ MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&mp_vfs_getcwd_obj) }, { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&mp_vfs_getcwd_obj) },
{ MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&mp_vfs_ilistdir_obj) },
{ MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&mp_vfs_listdir_obj) }, { MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&mp_vfs_listdir_obj) },
{ MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mp_vfs_mkdir_obj) }, { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mp_vfs_mkdir_obj) },
{ MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&mp_vfs_remove_obj) }, { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&mp_vfs_remove_obj) },

View File

@ -1,4 +1,4 @@
# test array('q') and array('Q') # test array types QqLl that require big-ints
try: try:
from array import array from array import array
@ -7,6 +7,9 @@ except ImportError:
print("SKIP") print("SKIP")
sys.exit() sys.exit()
print(array('L', [0, 2**32-1]))
print(array('l', [-2**31, 0, 2**31-1]))
print(array('q')) print(array('q'))
print(array('Q')) print(array('Q'))

View File

@ -9,6 +9,14 @@ exec("\n")
exec("\n\n") exec("\n\n")
exec("\r") exec("\r")
exec("\r\r") exec("\r\r")
exec("\t")
exec("\r\n")
exec("\nprint(1)")
exec("\rprint(2)")
exec("\r\nprint(3)")
exec("\n5")
exec("\r6")
exec("\r\n7")
print(eval("1")) print(eval("1"))
print(eval("12")) print(eval("12"))
print(eval("123")) print(eval("123"))
@ -19,6 +27,14 @@ print(eval("1\r"))
print(eval("12\r")) print(eval("12\r"))
print(eval("123\r")) print(eval("123\r"))
# line continuation
print(eval("'123' \\\r '456'"))
print(eval("'123' \\\n '456'"))
print(eval("'123' \\\r\n '456'"))
print(eval("'123'\\\r'456'"))
print(eval("'123'\\\n'456'"))
print(eval("'123'\\\r\n'456'"))
# backslash used to escape a line-break in a string # backslash used to escape a line-break in a string
print('a\ print('a\
b') b')

View File

@ -12,7 +12,3 @@ print(list(memoryview(array('b', [0x7f, -0x80]))))
print(list(memoryview(array('B', [0x7f, 0x80, 0x81, 0xff])))) print(list(memoryview(array('B', [0x7f, 0x80, 0x81, 0xff]))))
print(list(memoryview(array('h', [0x7f00, -0x8000])))) print(list(memoryview(array('h', [0x7f00, -0x8000]))))
print(list(memoryview(array('H', [0x7f00, 0x8000, 0x8100, 0xffff])))) print(list(memoryview(array('H', [0x7f00, 0x8000, 0x8100, 0xffff]))))
# these constructors give an internal overflow in uPy
#print(list(memoryview(array('i', [0x7f000000, -0x80000000]))))
#print(list(memoryview(array('I', [0x7f000000, 0x80000000, 0x81000000, 0xffffffff]))))

View File

@ -0,0 +1,11 @@
# test memoryview accessing maximum values for signed/unsigned elements
try:
from array import array
memoryview
except:
import sys
print("SKIP")
sys.exit()
print(list(memoryview(array('i', [0x7f000000, -0x80000000]))))
print(list(memoryview(array('I', [0x7f000000, 0x80000000, 0x81000000, 0xffffffff]))))

View File

@ -20,9 +20,9 @@ class Filesystem:
print(self.id, 'mount', readonly, mkfs) print(self.id, 'mount', readonly, mkfs)
def umount(self): def umount(self):
print(self.id, 'umount') print(self.id, 'umount')
def listdir(self, dir): def ilistdir(self, dir):
print(self.id, 'listdir', dir) print(self.id, 'ilistdir', dir)
return ['a%d' % self.id] return iter([('a%d' % self.id, 0, 0)])
def chdir(self, dir): def chdir(self, dir):
print(self.id, 'chdir', dir) print(self.id, 'chdir', dir)
def getcwd(self): def getcwd(self):
@ -47,6 +47,10 @@ class Filesystem:
# first we umount any existing mount points the target may have # first we umount any existing mount points the target may have
try:
uos.umount('/')
except OSError:
pass
for path in uos.listdir('/'): for path in uos.listdir('/'):
uos.umount('/' + path) uos.umount('/' + path)
@ -60,6 +64,18 @@ print(uos.getcwd())
uos.mount(Filesystem(1), '/test_mnt') uos.mount(Filesystem(1), '/test_mnt')
print(uos.listdir()) print(uos.listdir())
# ilistdir
i = uos.ilistdir()
print(next(i))
try:
next(i)
except StopIteration:
print('StopIteration')
try:
next(i)
except StopIteration:
print('StopIteration')
# referencing the mount point in different ways # referencing the mount point in different ways
print(uos.listdir('test_mnt')) print(uos.listdir('test_mnt'))
print(uos.listdir('/test_mnt')) print(uos.listdir('/test_mnt'))

View File

@ -2,20 +2,23 @@
/ /
1 mount False False 1 mount False False
['test_mnt'] ['test_mnt']
1 listdir / ('test_mnt', 16384, 0)
StopIteration
StopIteration
1 ilistdir /
['a1'] ['a1']
1 listdir / 1 ilistdir /
['a1'] ['a1']
2 mount True False 2 mount True False
['test_mnt', 'test_mnt2'] ['test_mnt', 'test_mnt2']
2 listdir / 2 ilistdir /
['a2'] ['a2']
3 mount False False 3 mount False False
OSError OSError
OSError OSError
OSError OSError
1 chdir / 1 chdir /
1 listdir 1 ilistdir
['a1'] ['a1']
1 getcwd 1 getcwd
/test_mntdir1 /test_mntdir1
@ -33,19 +36,19 @@ OSError
2 umount 2 umount
OSError OSError
3 mount False False 3 mount False False
3 listdir / 3 ilistdir /
['a3'] ['a3']
3 open test r 3 open test r
4 mount False False 4 mount False False
3 listdir / 3 ilistdir /
['mnt', 'a3'] ['mnt', 'a3']
4 listdir / 4 ilistdir /
['a4'] ['a4']
4 chdir / 4 chdir /
4 listdir 4 ilistdir
['a4'] ['a4']
3 chdir /subdir 3 chdir /subdir
3 listdir 3 ilistdir
['a3'] ['a3']
3 chdir / 3 chdir /
3 umount 3 umount

View File

@ -115,4 +115,4 @@ except OSError as e:
print(e.args[0] == 20) # uerrno.ENOTDIR print(e.args[0] == 20) # uerrno.ENOTDIR
vfs.remove("foo_file.txt") vfs.remove("foo_file.txt")
print(vfs.listdir()) print(list(vfs.ilistdir()))

View File

@ -10,4 +10,4 @@ e
True True
d d
True True
['foo_dir'] [('foo_dir', 16384, 0)]

View File

@ -91,23 +91,23 @@ except OSError as e:
# trim full path # trim full path
vfs.rename("foo_dir/file-in-dir.txt", "foo_dir/file.txt") vfs.rename("foo_dir/file-in-dir.txt", "foo_dir/file.txt")
print(vfs.listdir("foo_dir")) print(list(vfs.ilistdir("foo_dir")))
vfs.rename("foo_dir/file.txt", "moved-to-root.txt") vfs.rename("foo_dir/file.txt", "moved-to-root.txt")
print(vfs.listdir()) print(list(vfs.ilistdir()))
# check that renaming to existing file will overwrite it # check that renaming to existing file will overwrite it
with open("temp", "w") as f: with open("temp", "w") as f:
f.write("new text") f.write("new text")
vfs.rename("temp", "moved-to-root.txt") vfs.rename("temp", "moved-to-root.txt")
print(vfs.listdir()) print(list(vfs.ilistdir()))
with open("moved-to-root.txt") as f: with open("moved-to-root.txt") as f:
print(f.read()) print(f.read())
# valid removes # valid removes
vfs.remove("foo_dir/sub_file.txt") vfs.remove("foo_dir/sub_file.txt")
vfs.rmdir("foo_dir") vfs.rmdir("foo_dir")
print(vfs.listdir()) print(list(vfs.ilistdir()))
# disk full # disk full
try: try:

View File

@ -3,9 +3,9 @@ True
True True
b'data in file' b'data in file'
True True
['sub_file.txt', 'file.txt'] [('sub_file.txt', 32768, 0), ('file.txt', 32768, 0)]
['foo_dir', 'moved-to-root.txt'] [('foo_dir', 16384, 0), ('moved-to-root.txt', 32768, 0)]
['foo_dir', 'moved-to-root.txt'] [('foo_dir', 16384, 0), ('moved-to-root.txt', 32768, 0)]
new text new text
['moved-to-root.txt'] [('moved-to-root.txt', 32768, 0)]
ENOSPC: True ENOSPC: True

View File

@ -1,10 +1,15 @@
import sys import sys
import uerrno import uerrno
try: try:
try:
import uos_vfs as uos import uos_vfs as uos
open = uos.vfs_open open = uos.vfs_open
except ImportError: except ImportError:
import uos import uos
except ImportError:
print("SKIP")
sys.exit()
try: try:
uos.VfsFat uos.VfsFat
except AttributeError: except AttributeError:
@ -44,6 +49,14 @@ except MemoryError:
print("SKIP") print("SKIP")
sys.exit() sys.exit()
# first we umount any existing mount points the target may have
try:
uos.umount('/')
except OSError:
pass
for path in uos.listdir('/'):
uos.umount('/' + path)
uos.VfsFat.mkfs(bdev) uos.VfsFat.mkfs(bdev)
uos.mount(bdev, '/') uos.mount(bdev, '/')

View File

@ -53,10 +53,10 @@ uos.mount(vfs, "/ramdisk")
with vfs.open("file.txt", "w") as f: with vfs.open("file.txt", "w") as f:
f.write("hello!") f.write("hello!")
print(vfs.listdir()) print(list(vfs.ilistdir()))
with vfs.open("file.txt", "r") as f: with vfs.open("file.txt", "r") as f:
print(f.read()) print(f.read())
vfs.remove("file.txt") vfs.remove("file.txt")
print(vfs.listdir()) print(list(vfs.ilistdir()))

View File

@ -1,3 +1,3 @@
['file.txt'] [('file.txt', 32768, 0)]
hello! hello!
[] []

View File

@ -65,7 +65,7 @@ except OSError as e:
with vfs.open("foo_file.txt", "w") as f: with vfs.open("foo_file.txt", "w") as f:
f.write("hello!") f.write("hello!")
print(vfs.listdir()) print(list(vfs.ilistdir()))
print("stat root:", vfs.stat("/")) print("stat root:", vfs.stat("/"))
print("stat file:", vfs.stat("foo_file.txt")[:-3]) # timestamps differ across runs print("stat file:", vfs.stat("foo_file.txt")[:-3]) # timestamps differ across runs
@ -76,7 +76,7 @@ print(b"hello!" in bdev.data)
vfs.mkdir("foo_dir") vfs.mkdir("foo_dir")
vfs.chdir("foo_dir") vfs.chdir("foo_dir")
print("getcwd:", vfs.getcwd()) print("getcwd:", vfs.getcwd())
print(vfs.listdir()) print(list(vfs.ilistdir()))
with vfs.open("sub_file.txt", "w") as f: with vfs.open("sub_file.txt", "w") as f:
f.write("subdir file") f.write("subdir file")
@ -92,4 +92,10 @@ print("getcwd:", vfs.getcwd())
uos.umount(vfs) uos.umount(vfs)
vfs = uos.VfsFat(bdev) vfs = uos.VfsFat(bdev)
print(vfs.listdir(b"")) print(list(vfs.ilistdir(b"")))
# list a non-existent directory
try:
vfs.ilistdir(b"no_exist")
except OSError as e:
print('ENOENT:', e.args[0] == uerrno.ENOENT)

View File

@ -3,7 +3,7 @@ True
statvfs: (512, 512, 16, 16, 16, 0, 0, 0, 0, 255) statvfs: (512, 512, 16, 16, 16, 0, 0, 0, 0, 255)
getcwd: / getcwd: /
True True
['foo_file.txt'] [('foo_file.txt', 32768, 0)]
stat root: (16384, 0, 0, 0, 0, 0, 0, 0, 0, 0) stat root: (16384, 0, 0, 0, 0, 0, 0, 0, 0, 0)
stat file: (32768, 0, 0, 0, 0, 0, 6) stat file: (32768, 0, 0, 0, 0, 0, 6)
True True
@ -12,4 +12,5 @@ getcwd: /foo_dir
[] []
True True
getcwd: / getcwd: /
[b'foo_file.txt', b'foo_dir'] [(b'foo_file.txt', 32768, 0), (b'foo_dir', 16384, 0)]
ENOENT: True

View File

@ -332,25 +332,25 @@ class RawCode:
raise FreezeError(self, 'freezing of object %r is not implemented' % (obj,)) raise FreezeError(self, 'freezing of object %r is not implemented' % (obj,))
# generate constant table # generate constant table
print('STATIC const mp_uint_t const_table_data_%s[%u] = {' print('STATIC const mp_rom_obj_t const_table_data_%s[%u] = {'
% (self.escaped_name, len(self.qstrs) + len(self.objs) + len(self.raw_codes))) % (self.escaped_name, len(self.qstrs) + len(self.objs) + len(self.raw_codes)))
for qst in self.qstrs: for qst in self.qstrs:
print(' (mp_uint_t)MP_OBJ_NEW_QSTR(%s),' % global_qstrs[qst].qstr_id) print(' MP_ROM_QSTR(%s),' % global_qstrs[qst].qstr_id)
for i in range(len(self.objs)): for i in range(len(self.objs)):
if type(self.objs[i]) is float: if type(self.objs[i]) is float:
print('#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_A || MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_B') print('#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_A || MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_B')
print(' (mp_uint_t)&const_obj_%s_%u,' % (self.escaped_name, i)) print(' MP_ROM_PTR(&const_obj_%s_%u),' % (self.escaped_name, i))
print('#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C') print('#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C')
n = struct.unpack('<I', struct.pack('<f', self.objs[i]))[0] n = struct.unpack('<I', struct.pack('<f', self.objs[i]))[0]
n = ((n & ~0x3) | 2) + 0x80800000 n = ((n & ~0x3) | 2) + 0x80800000
print(' (mp_uint_t)0x%08x,' % (n,)) print(' MP_ROM_INT(0x%08x),' % (n,))
print('#else') print('#else')
print('#error "MICROPY_OBJ_REPR_D not supported with floats in frozen mpy files"') print('#error "MICROPY_OBJ_REPR_D not supported with floats in frozen mpy files"')
print('#endif') print('#endif')
else: else:
print(' (mp_uint_t)&const_obj_%s_%u,' % (self.escaped_name, i)) print(' MP_ROM_PTR(&const_obj_%s_%u),' % (self.escaped_name, i))
for rc in self.raw_codes: for rc in self.raw_codes:
print(' (mp_uint_t)&raw_code_%s,' % rc.escaped_name) print(' MP_ROM_PTR(&raw_code_%s),' % rc.escaped_name)
print('};') print('};')
# generate module # generate module
@ -362,7 +362,7 @@ class RawCode:
print(' .n_pos_args = %u,' % self.prelude[3]) print(' .n_pos_args = %u,' % self.prelude[3])
print(' .data.u_byte = {') print(' .data.u_byte = {')
print(' .bytecode = bytecode_data_%s,' % self.escaped_name) print(' .bytecode = bytecode_data_%s,' % self.escaped_name)
print(' .const_table = const_table_data_%s,' % self.escaped_name) print(' .const_table = (mp_uint_t*)const_table_data_%s,' % self.escaped_name)
print(' #if MICROPY_PERSISTENT_CODE_SAVE') print(' #if MICROPY_PERSISTENT_CODE_SAVE')
print(' .bc_len = %u,' % len(self.bytecode)) print(' .bc_len = %u,' % len(self.bytecode))
print(' .n_obj = %u,' % len(self.objs)) print(' .n_obj = %u,' % len(self.objs))

38
tools/mpy_cross_all.py Executable file
View File

@ -0,0 +1,38 @@
#!/usr/bin/env python3
import argparse
import os
import os.path
argparser = argparse.ArgumentParser(description="Compile all .py files to .mpy recursively")
argparser.add_argument("-o", "--out", help="output directory (default: input dir)")
argparser.add_argument("--target", help="select MicroPython target config")
argparser.add_argument("-mcache-lookup-bc", action="store_true", help="cache map lookups in the bytecode")
argparser.add_argument("dir", help="input directory")
args = argparser.parse_args()
TARGET_OPTS = {
"unix": "-mcache-lookup-bc",
"baremetal": "",
}
args.dir = args.dir.rstrip("/")
if not args.out:
args.out = args.dir
path_prefix_len = len(args.dir) + 1
for path, subdirs, files in os.walk(args.dir):
for f in files:
if f.endswith(".py"):
fpath = path + "/" + f
#print(fpath)
out_fpath = args.out + "/" + fpath[path_prefix_len:-3] + ".mpy"
out_dir = os.path.dirname(out_fpath)
if not os.path.isdir(out_dir):
os.makedirs(out_dir)
cmd = "mpy-cross -v -v %s -s %s %s -o %s" % (TARGET_OPTS.get(args.target, ""),
fpath[path_prefix_len:], fpath, out_fpath)
#print(cmd)
res = os.system(cmd)
assert res == 0

View File

@ -2,6 +2,7 @@
include ../py/mkenv.mk include ../py/mkenv.mk
FROZEN_DIR = scripts FROZEN_DIR = scripts
FROZEN_MPY_DIR = modules
# define main target # define main target
PROG = micropython PROG = micropython

View File

@ -553,6 +553,9 @@ MP_NOINLINE int main_(int argc, char **argv) {
mp_obj_t mod; mp_obj_t mod;
nlr_buf_t nlr; nlr_buf_t nlr;
bool subpkg_tried = false;
reimport:
if (nlr_push(&nlr) == 0) { if (nlr_push(&nlr) == 0) {
mod = mp_builtin___import__(MP_ARRAY_SIZE(import_args), import_args); mod = mp_builtin___import__(MP_ARRAY_SIZE(import_args), import_args);
nlr_pop(); nlr_pop();
@ -561,11 +564,17 @@ MP_NOINLINE int main_(int argc, char **argv) {
return handle_uncaught_exception(nlr.ret_val) & 0xff; return handle_uncaught_exception(nlr.ret_val) & 0xff;
} }
if (mp_obj_is_package(mod)) { if (mp_obj_is_package(mod) && !subpkg_tried) {
// TODO subpkg_tried = true;
mp_printf(&mp_stderr_print, "%s: -m for packages not yet implemented\n", argv[0]); vstr_t vstr;
exit(1); int len = strlen(argv[a + 1]);
vstr_init(&vstr, len + sizeof(".__main__"));
vstr_add_strn(&vstr, argv[a + 1], len);
vstr_add_strn(&vstr, ".__main__", sizeof(".__main__") - 1);
import_args[0] = mp_obj_new_str_from_vstr(&mp_type_str, &vstr);
goto reimport;
} }
ret = 0; ret = 0;
break; break;
} else if (strcmp(argv[a], "-X") == 0) { } else if (strcmp(argv[a], "-X") == 0) {

View File

@ -42,6 +42,7 @@ STATIC const mp_rom_map_elem_t uos_vfs_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&mp_vfs_chdir_obj) }, { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&mp_vfs_chdir_obj) },
{ MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&mp_vfs_getcwd_obj) }, { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&mp_vfs_getcwd_obj) },
{ MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&mp_vfs_ilistdir_obj) },
{ MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&mp_vfs_listdir_obj) }, { MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&mp_vfs_listdir_obj) },
{ MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mp_vfs_mkdir_obj) }, { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mp_vfs_mkdir_obj) },
{ MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&mp_vfs_remove_obj) }, { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&mp_vfs_remove_obj) },

View File

@ -1,20 +1,19 @@
MicroPython port to Zephyr RTOS MicroPython port to Zephyr RTOS
=============================== ===============================
This is an initial port of MicroPython to Zephyr RTOS This is an work-in-progress port of MicroPython to Zephyr RTOS
(http://zephyrproject.org). (http://zephyrproject.org).
The port integrates well with Zephyr build system, using the latest This port requires Zephyr version 1.8 or higher. All boards supported
features which will be available in 1.6.0, and thus requires Zephyr by Zephyr (with standard level of features support, like UART console)
master to build against. All boards supported by Zephyr (with standard should work with MicroPython (but not all were tested).
level of feature support, like UART console) should work with
MicroPython (but not all were tested).
Features supported at this time: Features supported at this time:
* REPL (interactive prompt) over Zephyr UART console. * REPL (interactive prompt) over Zephyr UART console.
* `utime` module for time measurements and delays. * `utime` module for time measurements and delays.
* `machine.Pin` class for GPIO control. * `machine.Pin` class for GPIO control.
* `usocket` module for networking (IPv4/IPv6).
* "Frozen modules" support to allow to bundle Python modules together * "Frozen modules" support to allow to bundle Python modules together
with firmware. Including complete applications, including with with firmware. Including complete applications, including with
run-on-boot capability. run-on-boot capability.
@ -97,10 +96,10 @@ MicroPython is committed to maintain minimal binary size for Zephyr port
below 128KB, as long as Zephyr project is committed to maintain stable below 128KB, as long as Zephyr project is committed to maintain stable
minimal size of their kernel (which they appear to be). Note that at such minimal size of their kernel (which they appear to be). Note that at such
size, there is no support for any Zephyr features beyond REPL over UART, size, there is no support for any Zephyr features beyond REPL over UART,
and only very minimal set of builtin Python modules. Thus, this build and only very minimal set of builtin Python modules is available. Thus,
is more suitable for code size control and quick demonstrations on this build is more suitable for code size control and quick demonstrations
smaller systems. It's also suitable for careful enabling of features one on smaller systems. It's also suitable for careful enabling of features
by one to achieve needed functionality and code size. This is in a one by one to achieve needed functionality and code size. This is in the
contrast to the "default" build, which may get more and more features contrast to the "default" build, which may get more and more features
enabled over time. enabled over time.

View File

@ -35,7 +35,8 @@
// Zephyr's generated version header // Zephyr's generated version header
#include <version.h> #include <version.h>
#include <net/net_context.h> #include <net/net_context.h>
#include <net/nbuf.h> #include <net/net_pkt.h>
#include <net/dns_resolve.h>
#define DEBUG 0 #define DEBUG 0
#if DEBUG // print debugging info #if DEBUG // print debugging info
@ -51,7 +52,7 @@ typedef struct _socket_obj_t {
struct k_fifo recv_q; struct k_fifo recv_q;
struct k_fifo accept_q; struct k_fifo accept_q;
}; };
struct net_buf *cur_buf; struct net_pkt *cur_pkt;
#define STATE_NEW 0 #define STATE_NEW 0
#define STATE_CONNECTING 1 #define STATE_CONNECTING 1
@ -114,13 +115,34 @@ STATIC void parse_inet_addr(socket_obj_t *socket, mp_obj_t addr_in, struct socka
sockaddr_in->sin_port = htons(mp_obj_get_int(addr_items[1])); sockaddr_in->sin_port = htons(mp_obj_get_int(addr_items[1]));
} }
STATIC mp_obj_t format_inet_addr(struct sockaddr *addr, mp_obj_t port) {
// We employ the fact that port and address offsets are the same for IPv4 & IPv6
struct sockaddr_in6 *sockaddr_in6 = (struct sockaddr_in6*)addr;
char buf[40];
net_addr_ntop(addr->family, &sockaddr_in6->sin6_addr, buf, sizeof(buf));
mp_obj_tuple_t *tuple = mp_obj_new_tuple(addr->family == AF_INET ? 2 : 4, NULL);
tuple->items[0] = mp_obj_new_str(buf, strlen(buf), false);
// We employ the fact that port offset is the same for IPv4 & IPv6
// not filled in
//tuple->items[1] = mp_obj_new_int(ntohs(((struct sockaddr_in*)addr)->sin_port));
tuple->items[1] = port;
if (addr->family == AF_INET6) {
tuple->items[2] = MP_OBJ_NEW_SMALL_INT(0); // flow_info
tuple->items[3] = MP_OBJ_NEW_SMALL_INT(sockaddr_in6->sin6_scope_id);
}
return MP_OBJ_FROM_PTR(tuple);
}
// Copy data from Zephyr net_buf chain into linear buffer. // Copy data from Zephyr net_buf chain into linear buffer.
// We don't use net_nbuf_read(), because it's weird (e.g., we'd like to // We don't use net_pkt_read(), because it's weird (e.g., we'd like to
// free processed data fragment ASAP, while net_nbuf_read() holds onto // free processed data fragment ASAP, while net_pkt_read() holds onto
// the whole fragment chain to do its deeds, and that's minor comparing // the whole fragment chain to do its deeds, and that's minor comparing
// to the fact that it copies data byte by byte). // to the fact that it copies data byte by byte).
static char *net_buf_gather(struct net_buf *buf, char *to, unsigned max_len) { static char *net_pkt_gather(struct net_pkt *pkt, char *to, unsigned max_len) {
struct net_buf *tmp = buf->frags; struct net_buf *tmp = pkt->frags;
while (tmp && max_len) { while (tmp && max_len) {
unsigned len = tmp->len; unsigned len = tmp->len;
@ -130,48 +152,47 @@ static char *net_buf_gather(struct net_buf *buf, char *to, unsigned max_len) {
memcpy(to, tmp->data, len); memcpy(to, tmp->data, len);
to += len; to += len;
max_len -= len; max_len -= len;
tmp = net_buf_frag_del(buf, tmp); tmp = net_pkt_frag_del(pkt, NULL, tmp);
} }
return to; return to;
} }
// Callback for incoming packets. // Callback for incoming packets.
static void sock_received_cb(struct net_context *context, struct net_buf *net_buf, int status, void *user_data) { static void sock_received_cb(struct net_context *context, struct net_pkt *pkt, int status, void *user_data) {
socket_obj_t *socket = (socket_obj_t*)user_data; socket_obj_t *socket = (socket_obj_t*)user_data;
DEBUG_printf("recv cb: context: %p, status: %d, buf: %p", context, status, net_buf); DEBUG_printf("recv cb: context: %p, status: %d, pkt: %p", context, status, pkt);
if (net_buf) { if (pkt) {
DEBUG_printf(" (sz=%d, l=%d), token: %p", net_buf->size, net_buf->len, net_nbuf_token(net_buf)); DEBUG_printf(" (appdatalen=%d), token: %p", pkt->appdatalen, net_pkt_token(pkt));
} }
DEBUG_printf("\n"); DEBUG_printf("\n");
#if DEBUG > 1 #if DEBUG > 1
net_nbuf_print_frags(net_buf); net_pkt_print_frags(pkt);
#endif #endif
// if net_buf == NULL, EOF // if net_buf == NULL, EOF
if (net_buf == NULL) { if (pkt == NULL) {
struct net_buf *last_buf = _k_fifo_peek_tail(&socket->recv_q); struct net_pkt *last_pkt = _k_fifo_peek_tail(&socket->recv_q);
if (last_buf == NULL) { if (last_pkt == NULL) {
socket->state = STATE_PEER_CLOSED; socket->state = STATE_PEER_CLOSED;
k_fifo_cancel_wait(&socket->recv_q);
DEBUG_printf("Marked socket %p as peer-closed\n", socket); DEBUG_printf("Marked socket %p as peer-closed\n", socket);
} else { } else {
// We abuse "buf_sent" flag to store EOF flag // We abuse "buf_sent" flag to store EOF flag
net_nbuf_set_buf_sent(last_buf, true); net_pkt_set_sent(last_pkt, true);
DEBUG_printf("Set EOF flag on %p\n", last_buf); DEBUG_printf("Set EOF flag on %p\n", last_pkt);
} }
return; return;
} }
// Make sure that "EOF flag" is not set // Make sure that "EOF flag" is not set
net_nbuf_set_buf_sent(net_buf, false); net_pkt_set_sent(pkt, false);
// We don't care about packet header, so get rid of it asap // We don't care about packet header, so get rid of it asap
unsigned header_len = net_nbuf_appdata(net_buf) - net_buf->frags->data; unsigned header_len = net_pkt_appdata(pkt) - pkt->frags->data;
net_buf_pull(net_buf->frags, header_len); net_buf_pull(pkt->frags, header_len);
// net_buf->frags will be overwritten by fifo, so save it k_fifo_put(&socket->recv_q, pkt);
net_nbuf_set_token(net_buf, net_buf->frags);
k_fifo_put(&socket->recv_q, net_buf);
} }
// Callback for incoming connections. // Callback for incoming connections.
@ -187,7 +208,7 @@ socket_obj_t *socket_new(void) {
socket_obj_t *socket = m_new_obj_with_finaliser(socket_obj_t); socket_obj_t *socket = m_new_obj_with_finaliser(socket_obj_t);
socket->base.type = (mp_obj_t)&socket_type; socket->base.type = (mp_obj_t)&socket_type;
k_fifo_init(&socket->recv_q); k_fifo_init(&socket->recv_q);
socket->cur_buf = NULL; socket->cur_pkt = NULL;
socket->state = STATE_NEW; socket->state = STATE_NEW;
return socket; return socket;
} }
@ -309,7 +330,7 @@ STATIC mp_uint_t sock_write(mp_obj_t self_in, const void *buf, mp_uint_t size, i
return MP_STREAM_ERROR; return MP_STREAM_ERROR;
} }
struct net_buf *send_buf = net_nbuf_get_tx(socket->ctx, K_FOREVER); struct net_pkt *send_pkt = net_pkt_get_tx(socket->ctx, K_FOREVER);
unsigned len = net_if_get_mtu(net_context_get_iface(socket->ctx)); unsigned len = net_if_get_mtu(net_context_get_iface(socket->ctx));
// Arbitrary value to account for protocol headers // Arbitrary value to account for protocol headers
@ -318,11 +339,11 @@ STATIC mp_uint_t sock_write(mp_obj_t self_in, const void *buf, mp_uint_t size, i
len = size; len = size;
} }
if (!net_nbuf_append(send_buf, len, buf, K_FOREVER)) { // TODO: Return value of 0 is a hard case (as we wait forever, should
len = net_buf_frags_len(send_buf); // not happen).
} len = net_pkt_append(send_pkt, len, buf, K_FOREVER);
int err = net_context_send(send_buf, /*cb*/NULL, K_FOREVER, NULL, NULL); int err = net_context_send(send_pkt, /*cb*/NULL, K_FOREVER, NULL, NULL);
if (err < 0) { if (err < 0) {
*errcode = -err; *errcode = -err;
return MP_STREAM_ERROR; return MP_STREAM_ERROR;
@ -356,41 +377,42 @@ STATIC mp_uint_t sock_read(mp_obj_t self_in, void *buf, mp_uint_t max_len, int *
if (sock_type == SOCK_DGRAM) { if (sock_type == SOCK_DGRAM) {
struct net_buf *net_buf = k_fifo_get(&socket->recv_q, K_FOREVER); struct net_pkt *pkt = k_fifo_get(&socket->recv_q, K_FOREVER);
// Restore ->frags overwritten by fifo
net_buf->frags = net_nbuf_token(net_buf);
recv_len = net_nbuf_appdatalen(net_buf); recv_len = net_pkt_appdatalen(pkt);
DEBUG_printf("recv: net_buf=%p, appdatalen: %d\n", net_buf, recv_len); DEBUG_printf("recv: pkt=%p, appdatalen: %d\n", pkt, recv_len);
if (recv_len > max_len) { if (recv_len > max_len) {
recv_len = max_len; recv_len = max_len;
} }
net_buf_gather(net_buf, buf, recv_len); net_pkt_gather(pkt, buf, recv_len);
net_nbuf_unref(net_buf); net_pkt_unref(pkt);
} else if (sock_type == SOCK_STREAM) { } else if (sock_type == SOCK_STREAM) {
do { do {
if (socket->cur_buf == NULL) { if (socket->cur_pkt == NULL) {
if (socket->state == STATE_PEER_CLOSED) { if (socket->state == STATE_PEER_CLOSED) {
return 0; return 0;
} }
DEBUG_printf("TCP recv: no cur_buf, getting\n"); DEBUG_printf("TCP recv: no cur_pkt, getting\n");
struct net_buf *net_buf = k_fifo_get(&socket->recv_q, K_FOREVER); struct net_pkt *pkt = k_fifo_get(&socket->recv_q, K_FOREVER);
// Restore ->frags overwritten by fifo
net_buf->frags = net_nbuf_token(net_buf);
DEBUG_printf("TCP recv: new cur_buf: %p\n", net_buf); if (pkt == NULL) {
socket->cur_buf = net_buf; DEBUG_printf("TCP recv: NULL return from fifo\n");
continue;
} }
struct net_buf *frag = socket->cur_buf->frags; DEBUG_printf("TCP recv: new cur_pkt: %p\n", pkt);
socket->cur_pkt = pkt;
}
struct net_buf *frag = socket->cur_pkt->frags;
if (frag == NULL) { if (frag == NULL) {
printf("net_buf has empty fragments on start!\n"); printf("net_pkt has empty fragments on start!\n");
assert(0); assert(0);
} }
@ -406,20 +428,20 @@ STATIC mp_uint_t sock_read(mp_obj_t self_in, void *buf, mp_uint_t max_len, int *
if (recv_len != frag_len) { if (recv_len != frag_len) {
net_buf_pull(frag, recv_len); net_buf_pull(frag, recv_len);
} else { } else {
frag = net_buf_frag_del(socket->cur_buf, frag); frag = net_pkt_frag_del(socket->cur_pkt, NULL, frag);
if (frag == NULL) { if (frag == NULL) {
DEBUG_printf("Finished processing net_buf %p\n", socket->cur_buf); DEBUG_printf("Finished processing pkt %p\n", socket->cur_pkt);
// If "buf_sent" flag was set, it's last packet and we reached EOF // If "sent" flag was set, it's last packet and we reached EOF
if (net_nbuf_buf_sent(socket->cur_buf)) { if (net_pkt_sent(socket->cur_pkt)) {
socket->state = STATE_PEER_CLOSED; socket->state = STATE_PEER_CLOSED;
} }
net_nbuf_unref(socket->cur_buf); net_pkt_unref(socket->cur_pkt);
socket->cur_buf = NULL; socket->cur_pkt = NULL;
} }
} }
// Keep repeating while we're getting empty fragments // Keep repeating while we're getting empty fragments
// Zephyr IP stack appears to feed empty net_buf's with empty // Zephyr IP stack appears to have fed empty net_buf's with empty
// frags for various TCP control packets. // frags for various TCP control packets - in previous versions.
} while (recv_len == 0); } while (recv_len == 0);
} }
@ -450,6 +472,13 @@ STATIC mp_obj_t socket_recv(mp_obj_t self_in, mp_obj_t len_in) {
} }
STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_recv_obj, socket_recv); STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_recv_obj, socket_recv);
STATIC mp_obj_t socket_setsockopt(size_t n_args, const mp_obj_t *args) {
(void)n_args; // always 4
mp_warning("setsockopt() not implemented");
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_setsockopt_obj, 4, 4, socket_setsockopt);
STATIC mp_obj_t socket_makefile(size_t n_args, const mp_obj_t *args) { STATIC mp_obj_t socket_makefile(size_t n_args, const mp_obj_t *args) {
(void)n_args; (void)n_args;
return args[0]; return args[0];
@ -475,6 +504,7 @@ STATIC const mp_map_elem_t socket_locals_dict_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR_accept), (mp_obj_t)&socket_accept_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_accept), (mp_obj_t)&socket_accept_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_send), (mp_obj_t)&socket_send_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_send), (mp_obj_t)&socket_send_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_recv), (mp_obj_t)&socket_recv_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_recv), (mp_obj_t)&socket_recv_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_setsockopt), (mp_obj_t)&socket_setsockopt_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_read), (mp_obj_t)&mp_stream_read_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_read), (mp_obj_t)&mp_stream_read_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_readinto), (mp_obj_t)&mp_stream_readinto_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_readinto), (mp_obj_t)&mp_stream_readinto_obj },
@ -499,6 +529,82 @@ STATIC const mp_obj_type_t socket_type = {
.locals_dict = (mp_obj_t)&socket_locals_dict, .locals_dict = (mp_obj_t)&socket_locals_dict,
}; };
//
// getaddrinfo() implementation
//
typedef struct _getaddrinfo_state_t {
mp_obj_t result;
struct k_sem sem;
mp_obj_t port;
} getaddrinfo_state_t;
void dns_resolve_cb(enum dns_resolve_status status, struct dns_addrinfo *info, void *user_data) {
getaddrinfo_state_t *state = user_data;
if (info == NULL) {
k_sem_give(&state->sem);
return;
}
mp_obj_tuple_t *tuple = mp_obj_new_tuple(5, NULL);
tuple->items[0] = MP_OBJ_NEW_SMALL_INT(info->ai_family);
// info->ai_socktype not filled
tuple->items[1] = MP_OBJ_NEW_SMALL_INT(SOCK_STREAM);
// info->ai_protocol not filled
tuple->items[2] = MP_OBJ_NEW_SMALL_INT(IPPROTO_TCP);
tuple->items[3] = MP_OBJ_NEW_QSTR(MP_QSTR_);
tuple->items[4] = format_inet_addr(&info->ai_addr, state->port);
mp_obj_list_append(state->result, MP_OBJ_FROM_PTR(tuple));
}
STATIC mp_obj_t mod_getaddrinfo(size_t n_args, const mp_obj_t *args) {
mp_obj_t host_in = args[0], port_in = args[1];
const char *host = mp_obj_str_get_str(host_in);
mp_int_t family = 0;
if (n_args > 2) {
family = mp_obj_get_int(args[2]);
}
getaddrinfo_state_t state;
// Just validate that it's int
(void)mp_obj_get_int(port_in);
state.port = port_in;
state.result = mp_obj_new_list(0, NULL);
k_sem_init(&state.sem, 0, UINT_MAX);
int status;
for (int i = 2; i--;) {
int type = (family != AF_INET6 ? DNS_QUERY_TYPE_A : DNS_QUERY_TYPE_AAAA);
status = dns_get_addr_info(host, type, NULL, dns_resolve_cb, &state, 3000);
if (status < 0) {
mp_raise_OSError(status);
}
k_sem_take(&state.sem, K_FOREVER);
if (family != 0) {
break;
}
family = AF_INET6;
}
return state.result;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_getaddrinfo_obj, 2, 3, mod_getaddrinfo);
STATIC mp_obj_t pkt_get_info(void) {
struct k_mem_slab *rx, *tx;
struct net_buf_pool *rx_data, *tx_data;
net_pkt_get_info(&rx, &tx, &rx_data, &tx_data);
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(4, NULL));
t->items[0] = MP_OBJ_NEW_SMALL_INT(k_mem_slab_num_free_get(rx));
t->items[1] = MP_OBJ_NEW_SMALL_INT(k_mem_slab_num_free_get(tx));
t->items[2] = MP_OBJ_NEW_SMALL_INT(rx_data->avail_count);
t->items[3] = MP_OBJ_NEW_SMALL_INT(tx_data->avail_count);
return MP_OBJ_FROM_PTR(t);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(pkt_get_info_obj, pkt_get_info);
STATIC const mp_map_elem_t mp_module_usocket_globals_table[] = { STATIC const mp_map_elem_t mp_module_usocket_globals_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_usocket) }, { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_usocket) },
// objects // objects
@ -509,6 +615,12 @@ STATIC const mp_map_elem_t mp_module_usocket_globals_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR_SOCK_STREAM), MP_OBJ_NEW_SMALL_INT(SOCK_STREAM) }, { MP_OBJ_NEW_QSTR(MP_QSTR_SOCK_STREAM), MP_OBJ_NEW_SMALL_INT(SOCK_STREAM) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_SOCK_DGRAM), MP_OBJ_NEW_SMALL_INT(SOCK_DGRAM) }, { MP_OBJ_NEW_QSTR(MP_QSTR_SOCK_DGRAM), MP_OBJ_NEW_SMALL_INT(SOCK_DGRAM) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_SOL_SOCKET), MP_OBJ_NEW_SMALL_INT(1) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_SO_REUSEADDR), MP_OBJ_NEW_SMALL_INT(2) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_getaddrinfo), (mp_obj_t)&mod_getaddrinfo_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_pkt_get_info), (mp_obj_t)&pkt_get_info_obj },
}; };
STATIC MP_DEFINE_CONST_DICT(mp_module_usocket_globals, mp_module_usocket_globals_table); STATIC MP_DEFINE_CONST_DICT(mp_module_usocket_globals, mp_module_usocket_globals_table);

View File

@ -17,6 +17,15 @@ CONFIG_NET_TCP=y
CONFIG_TEST_RANDOM_GENERATOR=y CONFIG_TEST_RANDOM_GENERATOR=y
CONFIG_NET_NBUF_RX_COUNT=5 CONFIG_NET_NBUF_RX_COUNT=5
# DNS
CONFIG_DNS_RESOLVER=y
CONFIG_DNS_RESOLVER_ADDITIONAL_QUERIES=2
CONFIG_DNS_SERVER_IP_ADDRESSES=y
CONFIG_DNS_SERVER1="192.0.2.2"
# Required for usocket.pkt_get_info()
CONFIG_NET_BUF_POOL_USAGE=y
# Uncomment to enable "INFO" level net_buf logging # Uncomment to enable "INFO" level net_buf logging
#CONFIG_NET_LOG=y #CONFIG_NET_LOG=y
#CONFIG_NET_DEBUG_NET_BUF=y #CONFIG_NET_DEBUG_NET_BUF=y

View File

@ -3,4 +3,4 @@
CONFIG_NET_SLIP_TAP=y CONFIG_NET_SLIP_TAP=y
# Default RAM easily overflows with uPy and networking # Default RAM easily overflows with uPy and networking
CONFIG_RAM_SIZE=256 CONFIG_RAM_SIZE=320