summaryrefslogtreecommitdiffstatshomepage
path: root/extmod
diff options
context:
space:
mode:
Diffstat (limited to 'extmod')
-rw-r--r--extmod/fsusermount.c10
-rw-r--r--extmod/machine_i2c.c464
-rw-r--r--extmod/machine_i2c.h34
-rw-r--r--extmod/modframebuf.c205
-rw-r--r--extmod/modlwip.c130
-rw-r--r--extmod/moduos_dupterm.c5
-rw-r--r--extmod/modussl.c55
-rw-r--r--extmod/modwebrepl.c330
-rw-r--r--extmod/modwebsocket.c66
9 files changed, 1251 insertions, 48 deletions
diff --git a/extmod/fsusermount.c b/extmod/fsusermount.c
index d86e57b4b8..8f789ca033 100644
--- a/extmod/fsusermount.c
+++ b/extmod/fsusermount.c
@@ -81,8 +81,7 @@ fs_user_mount_t *fatfs_mount_mkfs(mp_uint_t n_args, const mp_obj_t *pos_args, mp
}
// create new object
- fs_user_mount_t *vfs;
- MP_STATE_PORT(fs_user_mount)[i] = vfs = m_new_obj(fs_user_mount_t);
+ fs_user_mount_t *vfs = m_new_obj(fs_user_mount_t);
vfs->str = mnt_str;
vfs->len = mnt_len;
vfs->flags = FSUSER_FREE_OBJ;
@@ -108,6 +107,11 @@ fs_user_mount_t *fatfs_mount_mkfs(mp_uint_t n_args, const mp_obj_t *pos_args, mp
vfs->writeblocks[0] = MP_OBJ_NULL;
}
+ // Register the vfs object so that it can be found by the FatFS driver using
+ // ff_get_ldnumber. We don't register it any earlier than this point in case there
+ // is an exception, in which case there would remain a partially mounted device.
+ MP_STATE_PORT(fs_user_mount)[i] = vfs;
+
// mount the block device (if mkfs, only pre-mount)
FRESULT res = f_mount(&vfs->fatfs, vfs->str, !mkfs);
// check the result
@@ -120,6 +124,7 @@ mkfs:
res = f_mkfs(vfs->str, 1, 0);
if (res != FR_OK) {
mkfs_error:
+ MP_STATE_PORT(fs_user_mount)[i] = NULL;
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't mkfs"));
}
if (mkfs) {
@@ -132,6 +137,7 @@ mkfs_error:
return NULL;
}
} else {
+ MP_STATE_PORT(fs_user_mount)[i] = NULL;
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't mount"));
}
diff --git a/extmod/machine_i2c.c b/extmod/machine_i2c.c
new file mode 100644
index 0000000000..e3bdb36925
--- /dev/null
+++ b/extmod/machine_i2c.c
@@ -0,0 +1,464 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 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 <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "py/mphal.h"
+#include "py/runtime.h"
+#include "extmod/machine_i2c.h"
+
+#if MICROPY_PY_MACHINE_I2C
+
+typedef struct _machine_i2c_obj_t {
+ mp_obj_base_t base;
+ uint32_t us_delay;
+ mp_hal_pin_obj_t scl;
+ mp_hal_pin_obj_t sda;
+} machine_i2c_obj_t;
+
+STATIC void mp_hal_i2c_delay(machine_i2c_obj_t *self) {
+ // We need to use an accurate delay to get acceptable I2C
+ // speeds (eg 1us should be not much more than 1us).
+ mp_hal_delay_us_fast(self->us_delay);
+}
+
+STATIC void mp_hal_i2c_scl_low(machine_i2c_obj_t *self) {
+ mp_hal_pin_low(self->scl);
+}
+
+STATIC void mp_hal_i2c_scl_release(machine_i2c_obj_t *self) {
+ mp_hal_pin_od_high(self->scl);
+}
+
+STATIC void mp_hal_i2c_sda_low(machine_i2c_obj_t *self) {
+ mp_hal_pin_low(self->sda);
+}
+
+STATIC void mp_hal_i2c_sda_release(machine_i2c_obj_t *self) {
+ mp_hal_pin_od_high(self->sda);
+}
+
+STATIC int mp_hal_i2c_sda_read(machine_i2c_obj_t *self) {
+ return mp_hal_pin_read(self->sda);
+}
+
+STATIC void mp_hal_i2c_start(machine_i2c_obj_t *self) {
+ mp_hal_i2c_sda_release(self);
+ mp_hal_i2c_delay(self);
+ mp_hal_i2c_scl_release(self);
+ mp_hal_i2c_delay(self);
+ mp_hal_i2c_sda_low(self);
+ mp_hal_i2c_delay(self);
+}
+
+STATIC void mp_hal_i2c_stop(machine_i2c_obj_t *self) {
+ mp_hal_i2c_delay(self);
+ mp_hal_i2c_sda_low(self);
+ mp_hal_i2c_delay(self);
+ mp_hal_i2c_scl_release(self);
+ mp_hal_i2c_delay(self);
+ mp_hal_i2c_sda_release(self);
+ mp_hal_i2c_delay(self);
+}
+
+STATIC void mp_hal_i2c_init(machine_i2c_obj_t *self, uint32_t freq) {
+ self->us_delay = 500000 / freq;
+ if (self->us_delay == 0) {
+ self->us_delay = 1;
+ }
+ mp_hal_pin_config_od(self->scl);
+ mp_hal_pin_config_od(self->sda);
+ mp_hal_i2c_stop(self);
+}
+
+STATIC int mp_hal_i2c_write_byte(machine_i2c_obj_t *self, uint8_t val) {
+ mp_hal_i2c_delay(self);
+ mp_hal_i2c_scl_low(self);
+
+ for (int i = 7; i >= 0; i--) {
+ if ((val >> i) & 1) {
+ mp_hal_i2c_sda_release(self);
+ } else {
+ mp_hal_i2c_sda_low(self);
+ }
+ mp_hal_i2c_delay(self);
+ mp_hal_i2c_scl_release(self);
+ mp_hal_i2c_delay(self);
+ mp_hal_i2c_scl_low(self);
+ }
+
+ mp_hal_i2c_sda_release(self);
+ mp_hal_i2c_delay(self);
+ mp_hal_i2c_scl_release(self);
+ mp_hal_i2c_delay(self);
+
+ int ret = mp_hal_i2c_sda_read(self);
+ mp_hal_i2c_delay(self);
+ mp_hal_i2c_scl_low(self);
+
+ return !ret;
+}
+
+STATIC void mp_hal_i2c_write(machine_i2c_obj_t *self, uint8_t addr, uint8_t *data, size_t len) {
+ mp_hal_i2c_start(self);
+ if (!mp_hal_i2c_write_byte(self, addr << 1)) {
+ goto er;
+ }
+ while (len--) {
+ if (!mp_hal_i2c_write_byte(self, *data++)) {
+ goto er;
+ }
+ }
+ mp_hal_i2c_stop(self);
+ return;
+
+er:
+ mp_hal_i2c_stop(self);
+ nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "I2C bus error"));
+}
+
+STATIC int mp_hal_i2c_read_byte(machine_i2c_obj_t *self, uint8_t *val, int nack) {
+ mp_hal_i2c_delay(self);
+ mp_hal_i2c_scl_low(self);
+ mp_hal_i2c_delay(self);
+
+ uint8_t data = 0;
+ for (int i = 7; i >= 0; i--) {
+ mp_hal_i2c_scl_release(self);
+ mp_hal_i2c_delay(self);
+ data = (data << 1) | mp_hal_i2c_sda_read(self);
+ mp_hal_i2c_scl_low(self);
+ mp_hal_i2c_delay(self);
+ }
+ *val = data;
+
+ // send ack/nack bit
+ if (!nack) {
+ mp_hal_i2c_sda_low(self);
+ }
+ mp_hal_i2c_delay(self);
+ mp_hal_i2c_scl_release(self);
+ mp_hal_i2c_delay(self);
+ mp_hal_i2c_scl_low(self);
+ mp_hal_i2c_sda_release(self);
+
+ return 1; // success
+}
+
+STATIC void mp_hal_i2c_read(machine_i2c_obj_t *self, uint8_t addr, uint8_t *data, size_t len) {
+ mp_hal_i2c_start(self);
+ if (!mp_hal_i2c_write_byte(self, (addr << 1) | 1)) {
+ goto er;
+ }
+ while (len--) {
+ if (!mp_hal_i2c_read_byte(self, data++, len == 0)) {
+ goto er;
+ }
+ }
+ mp_hal_i2c_stop(self);
+ return;
+
+er:
+ mp_hal_i2c_stop(self);
+ nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "I2C bus error"));
+}
+
+STATIC void mp_hal_i2c_write_mem(machine_i2c_obj_t *self, uint8_t addr, uint16_t memaddr, const uint8_t *src, size_t len) {
+ // start the I2C transaction
+ mp_hal_i2c_start(self);
+
+ // write the slave address and the memory address within the slave
+ if (!mp_hal_i2c_write_byte(self, addr << 1)) {
+ goto er;
+ }
+ if (!mp_hal_i2c_write_byte(self, memaddr)) {
+ goto er;
+ }
+
+ // write the buffer to the I2C memory
+ while (len--) {
+ if (!mp_hal_i2c_write_byte(self, *src++)) {
+ goto er;
+ }
+ }
+
+ // finish the I2C transaction
+ mp_hal_i2c_stop(self);
+ return;
+
+er:
+ mp_hal_i2c_stop(self);
+ nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "I2C bus error"));
+}
+
+STATIC void mp_hal_i2c_read_mem(machine_i2c_obj_t *self, uint8_t addr, uint16_t memaddr, uint8_t *dest, size_t len) {
+ // start the I2C transaction
+ mp_hal_i2c_start(self);
+
+ // write the slave address and the memory address within the slave
+ if (!mp_hal_i2c_write_byte(self, addr << 1)) {
+ goto er;
+ }
+ if (!mp_hal_i2c_write_byte(self, memaddr)) {
+ goto er;
+ }
+
+ // i2c_read will do a repeated start, and then read the I2C memory
+ mp_hal_i2c_read(self, addr, dest, len);
+ return;
+
+er:
+ mp_hal_i2c_stop(self);
+ nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "I2C bus error"));
+}
+
+/******************************************************************************/
+// MicroPython bindings for I2C
+
+STATIC void machine_i2c_obj_init_helper(machine_i2c_obj_t *self, mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ enum { ARG_scl, ARG_sda, ARG_freq };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_scl, MP_ARG_REQUIRED | MP_ARG_OBJ },
+ { MP_QSTR_sda, MP_ARG_REQUIRED | MP_ARG_OBJ },
+ { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 400000} },
+ };
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+ self->scl = mp_hal_get_pin_obj(args[ARG_scl].u_obj);
+ self->sda = mp_hal_get_pin_obj(args[ARG_sda].u_obj);
+ mp_hal_i2c_init(self, args[ARG_freq].u_int);
+}
+
+STATIC mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+ mp_arg_check_num(n_args, n_kw, 0, MP_OBJ_FUN_ARGS_MAX, true);
+ machine_i2c_obj_t *self = m_new_obj(machine_i2c_obj_t);
+ self->base.type = &machine_i2c_type;
+ mp_map_t kw_args;
+ mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
+ machine_i2c_obj_init_helper(self, n_args, args, &kw_args);
+ return (mp_obj_t)self;
+}
+
+STATIC mp_obj_t machine_i2c_obj_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
+ machine_i2c_obj_init_helper(args[0], n_args - 1, args + 1, kw_args);
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2c_init_obj, 1, machine_i2c_obj_init);
+
+STATIC mp_obj_t machine_i2c_scan(mp_obj_t self_in) {
+ machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ mp_obj_t list = mp_obj_new_list(0, NULL);
+ // 7-bit addresses 0b0000xxx and 0b1111xxx are reserved
+ for (int addr = 0x08; addr < 0x78; ++addr) {
+ mp_hal_i2c_start(self);
+ int ack = mp_hal_i2c_write_byte(self, (addr << 1) | 1);
+ if (ack) {
+ mp_obj_list_append(list, MP_OBJ_NEW_SMALL_INT(addr));
+ }
+ mp_hal_i2c_stop(self);
+ }
+ return list;
+}
+MP_DEFINE_CONST_FUN_OBJ_1(machine_i2c_scan_obj, machine_i2c_scan);
+
+STATIC mp_obj_t machine_i2c_start(mp_obj_t self_in) {
+ machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ mp_hal_i2c_start(self);
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_1(machine_i2c_start_obj, machine_i2c_start);
+
+STATIC mp_obj_t machine_i2c_stop(mp_obj_t self_in) {
+ machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ mp_hal_i2c_stop(self);
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_1(machine_i2c_stop_obj, machine_i2c_stop);
+
+STATIC mp_obj_t machine_i2c_readinto(mp_obj_t self_in, mp_obj_t buf_in) {
+ machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);
+
+ // get the buffer to read into
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_WRITE);
+
+ // do the read
+ uint8_t *dest = bufinfo.buf;
+ while (bufinfo.len--) {
+ if (!mp_hal_i2c_read_byte(self, dest++, bufinfo.len == 0)) {
+ nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "I2C bus error"));
+ }
+ }
+
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_2(machine_i2c_readinto_obj, machine_i2c_readinto);
+
+STATIC mp_obj_t machine_i2c_write(mp_obj_t self_in, mp_obj_t buf_in) {
+ machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);
+
+ // get the buffer to write from
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ);
+
+ // do the write
+ uint8_t *src = bufinfo.buf;
+ while (bufinfo.len--) {
+ if (!mp_hal_i2c_write_byte(self, *src++)) {
+ nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "I2C bus error"));
+ }
+ }
+
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_2(machine_i2c_write_obj, machine_i2c_write);
+
+STATIC mp_obj_t machine_i2c_readfrom(mp_obj_t self_in, mp_obj_t addr_in, mp_obj_t nbytes_in) {
+ machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ vstr_t vstr;
+ vstr_init_len(&vstr, mp_obj_get_int(nbytes_in));
+ mp_hal_i2c_read(self, mp_obj_get_int(addr_in), (uint8_t*)vstr.buf, vstr.len);
+ return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
+}
+MP_DEFINE_CONST_FUN_OBJ_3(machine_i2c_readfrom_obj, machine_i2c_readfrom);
+
+STATIC mp_obj_t machine_i2c_readfrom_into(mp_obj_t self_in, mp_obj_t addr_in, mp_obj_t buf_in) {
+ machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_WRITE);
+ mp_hal_i2c_read(self, mp_obj_get_int(addr_in), (uint8_t*)bufinfo.buf, bufinfo.len);
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_3(machine_i2c_readfrom_into_obj, machine_i2c_readfrom_into);
+
+STATIC mp_obj_t machine_i2c_writeto(mp_obj_t self_in, mp_obj_t addr_in, mp_obj_t buf_in) {
+ machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ);
+ mp_hal_i2c_write(self, mp_obj_get_int(addr_in), bufinfo.buf, bufinfo.len);
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_3(machine_i2c_writeto_obj, machine_i2c_writeto);
+
+STATIC mp_obj_t machine_i2c_readfrom_mem(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ enum { ARG_addr, ARG_memaddr, ARG_n, ARG_addrsize };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_addr, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} },
+ { MP_QSTR_memaddr, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} },
+ { MP_QSTR_n, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} },
+ //{ MP_QSTR_addrsize, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} }, TODO
+ };
+ machine_i2c_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ // create the buffer to store data into
+ vstr_t vstr;
+ vstr_init_len(&vstr, args[ARG_n].u_int);
+
+ // do the transfer
+ mp_hal_i2c_read_mem(self, args[ARG_addr].u_int, args[ARG_memaddr].u_int, (uint8_t*)vstr.buf, vstr.len);
+ return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
+}
+MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2c_readfrom_mem_obj, 1, machine_i2c_readfrom_mem);
+
+STATIC mp_obj_t machine_i2c_readfrom_mem_into(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ enum { ARG_addr, ARG_memaddr, ARG_buf, ARG_addrsize };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_addr, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} },
+ { MP_QSTR_memaddr, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} },
+ { MP_QSTR_buf, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
+ //{ MP_QSTR_addrsize, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} }, TODO
+ };
+ machine_i2c_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ // get the buffer to store data into
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer_raise(args[ARG_buf].u_obj, &bufinfo, MP_BUFFER_WRITE);
+
+ // do the transfer
+ mp_hal_i2c_read_mem(self, args[ARG_addr].u_int, args[ARG_memaddr].u_int, bufinfo.buf, bufinfo.len);
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2c_readfrom_mem_into_obj, 1, machine_i2c_readfrom_mem_into);
+
+STATIC mp_obj_t machine_i2c_writeto_mem(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ enum { ARG_addr, ARG_memaddr, ARG_buf, ARG_addrsize };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_addr, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} },
+ { MP_QSTR_memaddr, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} },
+ { MP_QSTR_buf, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
+ //{ MP_QSTR_addrsize, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} }, TODO
+ };
+ machine_i2c_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ // get the buffer to write the data from
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer_raise(args[ARG_buf].u_obj, &bufinfo, MP_BUFFER_READ);
+
+ // do the transfer
+ mp_hal_i2c_write_mem(self, args[ARG_addr].u_int, args[ARG_memaddr].u_int, bufinfo.buf, bufinfo.len);
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2c_writeto_mem_obj, 1, machine_i2c_writeto_mem);
+
+STATIC const mp_rom_map_elem_t machine_i2c_locals_dict_table[] = {
+ { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_i2c_init_obj) },
+ { MP_ROM_QSTR(MP_QSTR_scan), MP_ROM_PTR(&machine_i2c_scan_obj) },
+
+ // primitive I2C operations
+ { MP_ROM_QSTR(MP_QSTR_start), MP_ROM_PTR(&machine_i2c_start_obj) },
+ { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&machine_i2c_stop_obj) },
+ { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&machine_i2c_readinto_obj) },
+ { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&machine_i2c_write_obj) },
+
+ // standard bus operations
+ { MP_ROM_QSTR(MP_QSTR_readfrom), MP_ROM_PTR(&machine_i2c_readfrom_obj) },
+ { MP_ROM_QSTR(MP_QSTR_readfrom_into), MP_ROM_PTR(&machine_i2c_readfrom_into_obj) },
+ { MP_ROM_QSTR(MP_QSTR_writeto), MP_ROM_PTR(&machine_i2c_writeto_obj) },
+
+ // memory operations
+ { MP_ROM_QSTR(MP_QSTR_readfrom_mem), MP_ROM_PTR(&machine_i2c_readfrom_mem_obj) },
+ { MP_ROM_QSTR(MP_QSTR_readfrom_mem_into), MP_ROM_PTR(&machine_i2c_readfrom_mem_into_obj) },
+ { MP_ROM_QSTR(MP_QSTR_writeto_mem), MP_ROM_PTR(&machine_i2c_writeto_mem_obj) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(machine_i2c_locals_dict, machine_i2c_locals_dict_table);
+
+const mp_obj_type_t machine_i2c_type = {
+ { &mp_type_type },
+ .name = MP_QSTR_I2C,
+ .make_new = machine_i2c_make_new,
+ .locals_dict = (mp_obj_dict_t*)&machine_i2c_locals_dict,
+};
+
+#endif // MICROPY_PY_MACHINE_I2C
diff --git a/extmod/machine_i2c.h b/extmod/machine_i2c.h
new file mode 100644
index 0000000000..03fa6422ad
--- /dev/null
+++ b/extmod/machine_i2c.h
@@ -0,0 +1,34 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 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.
+ */
+
+#ifndef __MICROPY_INCLUDED_EXTMOD_MACHINE_I2C_H__
+#define __MICROPY_INCLUDED_EXTMOD_MACHINE_I2C_H__
+
+#include "py/obj.h"
+
+extern const mp_obj_type_t machine_i2c_type;
+
+#endif // __MICROPY_INCLUDED_EXTMOD_MACHINE_I2C_H__
diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c
new file mode 100644
index 0000000000..d0ef238075
--- /dev/null
+++ b/extmod/modframebuf.c
@@ -0,0 +1,205 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 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 <stdio.h>
+#include <string.h>
+
+#include "py/nlr.h"
+#include "py/obj.h"
+#include "py/runtime.h"
+
+#if MICROPY_PY_FRAMEBUF
+
+#include "font_petme128_8x8.h"
+
+// 1-bit frame buffer, each byte is a column of 8 pixels
+typedef struct _mp_obj_framebuf1_t {
+ mp_obj_base_t base;
+ uint8_t *buf;
+ uint16_t width, height, stride;
+} mp_obj_framebuf1_t;
+
+STATIC mp_obj_t framebuf1_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+ mp_arg_check_num(n_args, n_kw, 3, 4, false);
+
+ mp_obj_framebuf1_t *o = m_new_obj(mp_obj_framebuf1_t);
+ o->base.type = type;
+
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_WRITE);
+ o->buf = bufinfo.buf;
+
+ o->width = mp_obj_get_int(args[1]);
+ o->height = mp_obj_get_int(args[2]);
+ o->stride = o->width;
+ if (n_args >= 4) {
+ o->stride = mp_obj_get_int(args[3]);
+ }
+
+ return MP_OBJ_FROM_PTR(o);
+}
+
+STATIC mp_obj_t framebuf1_fill(mp_obj_t self_in, mp_obj_t col_in) {
+ mp_obj_framebuf1_t *self = MP_OBJ_TO_PTR(self_in);
+ mp_int_t col = mp_obj_get_int(col_in);
+ if (col) {
+ col = 0xff;
+ }
+ for (int y = 0; y < self->height / 8; ++y) {
+ memset(self->buf + y * self->stride, col, self->width);
+ }
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(framebuf1_fill_obj, framebuf1_fill);
+
+STATIC mp_obj_t framebuf1_pixel(size_t n_args, const mp_obj_t *args) {
+ mp_obj_framebuf1_t *self = MP_OBJ_TO_PTR(args[0]);
+ mp_int_t x = mp_obj_get_int(args[1]);
+ mp_int_t y = mp_obj_get_int(args[2]);
+ if (0 <= x && x < self->width && 0 <= y && y < self->height) {
+ int index = (y / 8) * self->stride + x;
+ if (n_args == 3) {
+ // get
+ return MP_OBJ_NEW_SMALL_INT(self->buf[index] >> (y & 7));
+ } else {
+ // set
+ if (mp_obj_get_int(args[3])) {
+ self->buf[index] |= (1 << (y & 7));
+ } else {
+ self->buf[index] &= ~(1 << (y & 7));
+ }
+ }
+ }
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf1_pixel_obj, 3, 4, framebuf1_pixel);
+
+STATIC mp_obj_t framebuf1_scroll(mp_obj_t self_in, mp_obj_t xstep_in, mp_obj_t ystep_in) {
+ mp_obj_framebuf1_t *self = MP_OBJ_TO_PTR(self_in);
+ mp_int_t xstep = mp_obj_get_int(xstep_in);
+ mp_int_t ystep = mp_obj_get_int(ystep_in);
+ if (xstep == 0 && ystep > 0) {
+ for (int y = self->height / 8; y > 0;) {
+ --y;
+ for (int x = 0; x < self->width; ++x) {
+ int prev = 0;
+ if (y > 0) {
+ prev = (self->buf[(y - 1) * self->stride + x] >> (8 - ystep)) & ((1 << ystep) - 1);
+ }
+ self->buf[y * self->stride + x] = (self->buf[y * self->stride + x] << ystep) | prev;
+ }
+ }
+ } else if (xstep == 0 && ystep < 0) {
+ for (int y = 0; y < self->height / 8; ++y) {
+ for (int x = 0; x < self->width; ++x) {
+ int prev = 0;
+ if (y + 1 < self->height / 8) {
+ prev = self->buf[(y + 1) * self->stride + x] << (8 + ystep);
+ }
+ self->buf[y * self->stride + x] = (self->buf[y * self->stride + x] >> -ystep) | prev;
+ }
+ }
+ }
+ // TODO xstep!=0
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_3(framebuf1_scroll_obj, framebuf1_scroll);
+
+STATIC mp_obj_t framebuf1_text(size_t n_args, const mp_obj_t *args) {
+ // extract arguments
+ mp_obj_framebuf1_t *self = MP_OBJ_TO_PTR(args[0]);
+ const char *str = mp_obj_str_get_str(args[1]);
+ mp_int_t x0 = mp_obj_get_int(args[2]);
+ mp_int_t y0 = mp_obj_get_int(args[3]);
+ mp_int_t col = 1;
+ if (n_args >= 5) {
+ col = mp_obj_get_int(args[4]);
+ }
+
+ // loop over chars
+ for (; *str; ++str) {
+ // get char and make sure its in range of font
+ int chr = *(uint8_t*)str;
+ if (chr < 32 || chr > 127) {
+ chr = 127;
+ }
+ // get char data
+ const uint8_t *chr_data = &font_petme128_8x8[(chr - 32) * 8];
+ // loop over char data
+ for (int j = 0; j < 8; j++, x0++) {
+ if (0 <= x0 && x0 < self->width) { // clip x
+ uint vline_data = chr_data[j]; // each byte is a column of 8 pixels, LSB at top
+ for (int y = y0; vline_data; vline_data >>= 1, y++) { // scan over vertical column
+ if (vline_data & 1) { // only draw if pixel set
+ if (0 <= y && y < self->height) { // clip y
+ uint byte_pos = x0 + self->stride * ((uint)y >> 3);
+ if (col == 0) {
+ // clear pixel
+ self->buf[byte_pos] &= ~(1 << (y & 7));
+ } else {
+ // set pixel
+ self->buf[byte_pos] |= 1 << (y & 7);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf1_text_obj, 4, 5, framebuf1_text);
+
+STATIC const mp_rom_map_elem_t framebuf1_locals_dict_table[] = {
+ { MP_ROM_QSTR(MP_QSTR_fill), MP_ROM_PTR(&framebuf1_fill_obj) },
+ { MP_ROM_QSTR(MP_QSTR_pixel), MP_ROM_PTR(&framebuf1_pixel_obj) },
+ { MP_ROM_QSTR(MP_QSTR_scroll), MP_ROM_PTR(&framebuf1_scroll_obj) },
+ { MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&framebuf1_text_obj) },
+};
+STATIC MP_DEFINE_CONST_DICT(framebuf1_locals_dict, framebuf1_locals_dict_table);
+
+STATIC const mp_obj_type_t mp_type_framebuf1 = {
+ { &mp_type_type },
+ .name = MP_QSTR_FrameBuffer1,
+ .make_new = framebuf1_make_new,
+ .locals_dict = (mp_obj_t)&framebuf1_locals_dict,
+};
+
+STATIC const mp_rom_map_elem_t framebuf_module_globals_table[] = {
+ { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_framebuf) },
+ { MP_ROM_QSTR(MP_QSTR_FrameBuffer1), MP_ROM_PTR(&mp_type_framebuf1) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(framebuf_module_globals, framebuf_module_globals_table);
+
+const mp_obj_module_t mp_module_framebuf = {
+ .base = { &mp_type_module },
+ .name = MP_QSTR_framebuf,
+ .globals = (mp_obj_dict_t*)&framebuf_module_globals,
+};
+
+#endif // MICROPY_PY_FRAMEBUF
diff --git a/extmod/modlwip.c b/extmod/modlwip.c
index 1ebcd8923c..090e1005a8 100644
--- a/extmod/modlwip.c
+++ b/extmod/modlwip.c
@@ -43,6 +43,13 @@
#include "lwip/udp.h"
//#include "lwip/raw.h"
#include "lwip/dns.h"
+#include "lwip/tcp_impl.h"
+
+#if 0 // print debugging info
+#define DEBUG_printf DEBUG_printf
+#else // don't print debugging info
+#define DEBUG_printf(...) (void)0
+#endif
// For compatibilily with older lwIP versions.
#ifndef ip_set_option
@@ -228,6 +235,7 @@ typedef struct _lwip_socket_obj_t {
struct pbuf *pbuf;
struct tcp_pcb *connection;
} incoming;
+ mp_obj_t callback;
byte peer[4];
mp_uint_t peer_port;
mp_uint_t timeout;
@@ -255,6 +263,12 @@ static inline void poll_sockets(void) {
/*******************************************************************************/
// Callback functions for the lwIP raw API.
+static inline void exec_user_callback(lwip_socket_obj_t *socket) {
+ if (socket->callback != MP_OBJ_NULL) {
+ mp_call_function_1_protected(socket->callback, socket);
+ }
+}
+
// Callback for incoming UDP packets. We simply stash the packet and the source address,
// in case we need it for recvfrom.
STATIC void _lwip_udp_incoming(void *arg, struct udp_pcb *upcb, struct pbuf *p, ip_addr_t *addr, u16_t port) {
@@ -303,11 +317,13 @@ STATIC err_t _lwip_tcp_accept(void *arg, struct tcp_pcb *newpcb, err_t err) {
tcp_recv(newpcb, _lwip_tcp_recv_unaccepted);
if (socket->incoming.connection != NULL) {
+ DEBUG_printf("_lwip_tcp_accept: Tried to queue >1 pcb waiting for accept\n");
// We need to handle this better. This single-level structure makes the
// backlog setting kind of pointless. FIXME
return ERR_BUF;
} else {
socket->incoming.connection = newpcb;
+ exec_user_callback(socket);
return ERR_OK;
}
}
@@ -318,13 +334,18 @@ STATIC err_t _lwip_tcp_recv(void *arg, struct tcp_pcb *tcpb, struct pbuf *p, err
if (p == NULL) {
// Other side has closed connection.
+ DEBUG_printf("_lwip_tcp_recv[%p]: other side closed connection\n", socket);
socket->state = STATE_PEER_CLOSED;
+ exec_user_callback(socket);
return ERR_OK;
} else if (socket->incoming.pbuf != NULL) {
// No room in the inn, let LWIP know it's still responsible for delivery later
return ERR_BUF;
}
socket->incoming.pbuf = p;
+
+ exec_user_callback(socket);
+
return ERR_OK;
}
@@ -359,7 +380,10 @@ STATIC mp_uint_t lwip_udp_send(lwip_socket_obj_t *socket, const byte *buf, mp_ui
pbuf_free(p);
- if (err != ERR_OK) {
+ // udp_sendto can return 1 on occasion for ESP8266 port. It's not known why
+ // but it seems that the send actually goes through without error in this case.
+ // So we treat such cases as a success until further investigation.
+ if (err != ERR_OK && err != 1) {
*_errno = error_lookup_table[-err];
return -1;
}
@@ -401,15 +425,27 @@ STATIC mp_uint_t lwip_udp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_
return (mp_uint_t) result;
}
+// For use in stream virtual methods
+#define STREAM_ERROR_CHECK(socket) \
+ if (socket->state < 0) { \
+ *_errno = error_lookup_table[-socket->state]; \
+ return MP_STREAM_ERROR; \
+ } \
+ assert(socket->pcb.tcp);
+
+
// Helper function for send/sendto to handle TCP packets
STATIC mp_uint_t lwip_tcp_send(lwip_socket_obj_t *socket, const byte *buf, mp_uint_t len, int *_errno) {
+ // Check for any pending errors
+ STREAM_ERROR_CHECK(socket);
+
u16_t available = tcp_sndbuf(socket->pcb.tcp);
if (available == 0) {
// Non-blocking socket
if (socket->timeout == 0) {
*_errno = EAGAIN;
- return -1;
+ return MP_STREAM_ERROR;
}
mp_uint_t start = mp_hal_ticks_ms();
@@ -422,15 +458,13 @@ STATIC mp_uint_t lwip_tcp_send(lwip_socket_obj_t *socket, const byte *buf, mp_ui
while (socket->state >= STATE_CONNECTED && (available = tcp_sndbuf(socket->pcb.tcp)) < 16) {
if (socket->timeout != -1 && mp_hal_ticks_ms() - start > socket->timeout) {
*_errno = ETIMEDOUT;
- return -1;
+ return MP_STREAM_ERROR;
}
poll_sockets();
}
- if (socket->state < 0) {
- *_errno = error_lookup_table[-socket->state];
- return -1;
- }
+ // While we waited, something could happen
+ STREAM_ERROR_CHECK(socket);
}
u16_t write_len = MIN(available, len);
@@ -439,7 +473,7 @@ STATIC mp_uint_t lwip_tcp_send(lwip_socket_obj_t *socket, const byte *buf, mp_ui
if (err != ERR_OK) {
*_errno = error_lookup_table[-err];
- return -1;
+ return MP_STREAM_ERROR;
}
return write_len;
@@ -447,10 +481,16 @@ STATIC mp_uint_t lwip_tcp_send(lwip_socket_obj_t *socket, const byte *buf, mp_ui
// Helper function for recv/recvfrom to handle TCP packets
STATIC mp_uint_t lwip_tcp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_t len, int *_errno) {
+ // Check for any pending errors
+ STREAM_ERROR_CHECK(socket);
+
if (socket->incoming.pbuf == NULL) {
// Non-blocking socket
if (socket->timeout == 0) {
+ if (socket->state == STATE_PEER_CLOSED) {
+ return 0;
+ }
*_errno = EAGAIN;
return -1;
}
@@ -463,6 +503,7 @@ STATIC mp_uint_t lwip_tcp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_
}
poll_sockets();
}
+
if (socket->state == STATE_PEER_CLOSED) {
if (socket->incoming.pbuf == NULL) {
// socket closed and no data left in buffer
@@ -475,6 +516,8 @@ STATIC mp_uint_t lwip_tcp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_
}
}
+ assert(socket->pcb.tcp != NULL);
+
struct pbuf *p = socket->incoming.pbuf;
if (socket->leftover_count == 0) {
@@ -515,6 +558,7 @@ STATIC mp_obj_t lwip_socket_make_new(const mp_obj_type_t *type, mp_uint_t n_args
socket->base.type = (mp_obj_t)&lwip_socket_type;
socket->domain = MOD_NETWORK_AF_INET;
socket->type = MOD_NETWORK_SOCK_STREAM;
+ socket->callback = MP_OBJ_NULL;
if (n_args >= 1) {
socket->domain = mp_obj_get_int(args[0]);
if (n_args >= 2) {
@@ -569,6 +613,7 @@ STATIC mp_obj_t lwip_socket_close(mp_obj_t self_in) {
socket_is_listener = true;
}
if (tcp_close(socket->pcb.tcp) != ERR_OK) {
+ DEBUG_printf("lwip_close: had to call tcp_abort()\n");
tcp_abort(socket->pcb.tcp);
}
break;
@@ -689,6 +734,7 @@ STATIC mp_obj_t lwip_socket_accept(mp_obj_t self_in) {
socket2->timeout = socket->timeout;
socket2->state = STATE_CONNECTED;
socket2->leftover_count = 0;
+ socket2->callback = MP_OBJ_NULL;
tcp_arg(socket2->pcb.tcp, (void*)socket2);
tcp_err(socket2->pcb.tcp, _lwip_tcp_error);
tcp_recv(socket2->pcb.tcp, _lwip_tcp_recv);
@@ -918,13 +964,55 @@ STATIC mp_obj_t lwip_socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_recvfrom_obj, lwip_socket_recvfrom);
+STATIC mp_obj_t lwip_socket_sendall(mp_obj_t self_in, mp_obj_t buf_in) {
+ lwip_socket_obj_t *socket = self_in;
+ lwip_socket_check_connected(socket);
+
+ int _errno;
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ);
+
+ mp_uint_t ret = 0;
+ switch (socket->type) {
+ case MOD_NETWORK_SOCK_STREAM: {
+ if (socket->timeout == 0) {
+ // Behavior of sendall() for non-blocking sockets isn't explicitly specified.
+ // But it's specified that "On error, an exception is raised, there is no
+ // way to determine how much data, if any, was successfully sent." Then, the
+ // most useful behavior is: check whether we will be able to send all of input
+ // data without EAGAIN, and if won't be, raise it without sending any.
+ if (bufinfo.len > tcp_sndbuf(socket->pcb.tcp)) {
+ nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(EAGAIN)));
+ }
+ }
+ // TODO: In CPython3.5, socket timeout should apply to the
+ // entire sendall() operation, not to individual send() chunks.
+ while (bufinfo.len != 0) {
+ ret = lwip_tcp_send(socket, bufinfo.buf, bufinfo.len, &_errno);
+ if (ret == -1) {
+ nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(_errno)));
+ }
+ bufinfo.len -= ret;
+ bufinfo.buf = (char*)bufinfo.buf + ret;
+ }
+ break;
+ }
+ case MOD_NETWORK_SOCK_DGRAM:
+ mp_not_implemented("");
+ break;
+ }
+
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_sendall_obj, lwip_socket_sendall);
+
STATIC mp_obj_t lwip_socket_settimeout(mp_obj_t self_in, mp_obj_t timeout_in) {
lwip_socket_obj_t *socket = self_in;
mp_uint_t timeout;
if (timeout_in == mp_const_none) {
timeout = -1;
} else {
- #if MICROPY_PY_BUILTIN_FLOAT
+ #if MICROPY_PY_BUILTINS_FLOAT
timeout = 1000 * mp_obj_get_float(timeout_in);
#else
timeout = 1000 * mp_obj_get_int(timeout_in);
@@ -950,8 +1038,20 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_setblocking_obj, lwip_socket_setblo
STATIC mp_obj_t lwip_socket_setsockopt(mp_uint_t n_args, const mp_obj_t *args) {
(void)n_args; // always 4
lwip_socket_obj_t *socket = args[0];
+
+ int opt = mp_obj_get_int(args[2]);
+ if (opt == 20) {
+ if (args[3] == mp_const_none) {
+ socket->callback = MP_OBJ_NULL;
+ } else {
+ socket->callback = args[3];
+ }
+ return mp_const_none;
+ }
+
+ // Integer options
mp_int_t val = mp_obj_get_int(args[3]);
- switch (mp_obj_get_int(args[2])) {
+ switch (opt) {
case SOF_REUSEADDR:
// Options are common for UDP and TCP pcb's.
if (val) {
@@ -1010,6 +1110,7 @@ STATIC const mp_map_elem_t lwip_socket_locals_dict_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR_recv), (mp_obj_t)&lwip_socket_recv_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_sendto), (mp_obj_t)&lwip_socket_sendto_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_recvfrom), (mp_obj_t)&lwip_socket_recvfrom_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_sendall), (mp_obj_t)&lwip_socket_sendall_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_settimeout), (mp_obj_t)&lwip_socket_settimeout_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_setblocking), (mp_obj_t)&lwip_socket_setblocking_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_setsockopt), (mp_obj_t)&lwip_socket_setsockopt_obj },
@@ -1143,6 +1244,14 @@ STATIC mp_obj_t lwip_getaddrinfo(mp_obj_t host_in, mp_obj_t port_in) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(lwip_getaddrinfo_obj, lwip_getaddrinfo);
+// Debug functions
+
+STATIC mp_obj_t lwip_print_pcbs() {
+ tcp_debug_print_pcbs();
+ return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_0(lwip_print_pcbs_obj, lwip_print_pcbs);
+
#ifdef MICROPY_PY_LWIP
STATIC const mp_map_elem_t mp_module_lwip_globals_table[] = {
@@ -1150,6 +1259,7 @@ STATIC const mp_map_elem_t mp_module_lwip_globals_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR_reset), (mp_obj_t)&mod_lwip_reset_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_callback), (mp_obj_t)&mod_lwip_callback_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_getaddrinfo), (mp_obj_t)&lwip_getaddrinfo_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_print_pcbs), (mp_obj_t)&lwip_print_pcbs_obj },
// objects
{ MP_OBJ_NEW_QSTR(MP_QSTR_socket), (mp_obj_t)&lwip_socket_type },
#ifdef MICROPY_PY_LWIP_SLIP
diff --git a/extmod/moduos_dupterm.c b/extmod/moduos_dupterm.c
index 99022df37a..41b8b3c81e 100644
--- a/extmod/moduos_dupterm.c
+++ b/extmod/moduos_dupterm.c
@@ -44,12 +44,9 @@ void mp_uos_dupterm_tx_strn(const char *str, size_t len) {
mp_call_method_n_kw(1, 0, write_m);
nlr_pop();
} else {
- // Temporarily disable dupterm to avoid infinite recursion
- mp_obj_t save_term = MP_STATE_PORT(term_obj);
MP_STATE_PORT(term_obj) = NULL;
- mp_printf(&mp_plat_print, "dupterm: ");
+ mp_printf(&mp_plat_print, "dupterm: Exception in write() method, deactivating: ");
mp_obj_print_exception(&mp_plat_print, nlr.ret_val);
- MP_STATE_PORT(term_obj) = save_term;
}
}
}
diff --git a/extmod/modussl.c b/extmod/modussl.c
index 5e18faa579..51f4fead81 100644
--- a/extmod/modussl.c
+++ b/extmod/modussl.c
@@ -26,6 +26,7 @@
#include <stdio.h>
#include <string.h>
+#include <errno.h>
#include "py/nlr.h"
#include "py/runtime.h"
@@ -46,7 +47,7 @@ typedef struct _mp_obj_ssl_socket_t {
STATIC const mp_obj_type_t ussl_socket_type;
-STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock) {
+STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, bool server_side) {
mp_obj_ssl_socket_t *o = m_new_obj(mp_obj_ssl_socket_t);
o->base.type = &ussl_socket_type;
o->buf = NULL;
@@ -54,21 +55,22 @@ STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock) {
o->sock = sock;
uint32_t options = SSL_SERVER_VERIFY_LATER;
- if ((o->ssl_ctx = ssl_ctx_new(options, SSL_DEFAULT_CLNT_SESS)) == NULL)
- {
- fprintf(stderr, "Error: Client context is invalid\n");
- assert(0);
+ if ((o->ssl_ctx = ssl_ctx_new(options, SSL_DEFAULT_CLNT_SESS)) == NULL) {
+ nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(EINVAL)));
}
- o->ssl_sock = ssl_client_new(o->ssl_ctx, (long)sock, NULL, 0);
-
- int res;
- /* check the return status */
- if ((res = ssl_handshake_status(o->ssl_sock)) != SSL_OK)
- {
- printf("ssl_handshake_status: %d\n", res);
- ssl_display_error(res);
- assert(0);
+ if (server_side) {
+ o->ssl_sock = ssl_server_new(o->ssl_ctx, (long)sock);
+ } else {
+ o->ssl_sock = ssl_client_new(o->ssl_ctx, (long)sock, NULL, 0);
+
+ int res;
+ /* check the return status */
+ if ((res = ssl_handshake_status(o->ssl_sock)) != SSL_OK) {
+ printf("ssl_handshake_status: %d\n", res);
+ ssl_display_error(res);
+ nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(EIO)));
+ }
}
return o;
@@ -85,6 +87,11 @@ STATIC mp_uint_t socket_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errc
while (o->bytes_left == 0) {
mp_int_t r = ssl_read(o->ssl_sock, &o->buf);
+ if (r == SSL_OK) {
+ // SSL_OK from ssl_read() means "everything is ok, but there's
+ // not user data yet. So, we just keep reading.
+ continue;
+ }
if (r < 0) {
if (r == SSL_CLOSE_NOTIFY || r == SSL_ERROR_CONN_LOST) {
// EOF
@@ -153,14 +160,24 @@ STATIC const mp_obj_type_t ussl_socket_type = {
.locals_dict = (mp_obj_t)&ussl_socket_locals_dict,
};
-STATIC mp_obj_t mod_ssl_wrap_socket(mp_uint_t n_args, const mp_obj_t *args) {
+STATIC mp_obj_t mod_ssl_wrap_socket(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
// TODO: Implement more args
- assert(n_args == 1);
- mp_obj_t sock = args[0];
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_server_side, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
+ };
+
// TODO: Check that sock implements stream protocol
- return socket_new(sock);
+ mp_obj_t sock = pos_args[0];
+
+ struct {
+ mp_arg_val_t server_side;
+ } args;
+ mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args,
+ MP_ARRAY_SIZE(allowed_args), allowed_args, (mp_arg_val_t*)&args);
+
+ return socket_new(sock, args.server_side.u_bool);
}
-STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_ssl_wrap_socket_obj, 1, 6, mod_ssl_wrap_socket);
+STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_ssl_wrap_socket_obj, 1, mod_ssl_wrap_socket);
STATIC const mp_map_elem_t mp_module_ssl_globals_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_ussl) },
diff --git a/extmod/modwebrepl.c b/extmod/modwebrepl.c
new file mode 100644
index 0000000000..c160abea2f
--- /dev/null
+++ b/extmod/modwebrepl.c
@@ -0,0 +1,330 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Paul Sokolovsky
+ *
+ * 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 <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include "py/nlr.h"
+#include "py/obj.h"
+#include "py/runtime.h"
+#include "py/stream.h"
+#include "py/builtin.h"
+#ifdef MICROPY_PY_WEBREPL_DELAY
+#include "py/mphal.h"
+#endif
+#include "extmod/modwebsocket.h"
+
+#if MICROPY_PY_WEBREPL
+
+#if 0 // print debugging info
+#define DEBUG_printf DEBUG_printf
+#else // don't print debugging info
+#define DEBUG_printf(...) (void)0
+#endif
+
+struct webrepl_file {
+ char sig[2];
+ char type;
+ char flags;
+ uint64_t offset;
+ uint32_t size;
+ uint16_t fname_len;
+ char fname[64];
+} __attribute__((packed));
+
+enum { PUT_FILE = 1, GET_FILE, LIST_DIR };
+enum { STATE_PASSWD, STATE_NORMAL };
+
+typedef struct _mp_obj_webrepl_t {
+ mp_obj_base_t base;
+ mp_obj_t sock;
+ byte state;
+ byte hdr_to_recv;
+ uint32_t data_to_recv;
+ struct webrepl_file hdr;
+ mp_obj_t cur_file;
+} mp_obj_webrepl_t;
+
+// These get passed to functions which aren't force-l32, so can't be const
+STATIC char passwd_prompt[] = "Password: ";
+STATIC char connected_prompt[] = "\r\nWebREPL connected\r\n>>> ";
+STATIC char denied_prompt[] = "\r\nAccess denied\r\n";
+
+STATIC char webrepl_passwd[10];
+
+static inline void close_meth(mp_obj_t stream) {
+ mp_obj_t dest[2];
+ mp_load_method(stream, MP_QSTR_close, dest);
+ mp_call_method_n_kw(0, 0, dest);
+}
+
+STATIC void write_webrepl(mp_obj_t websock, const void *buf, size_t len) {
+ const mp_stream_p_t *sock_stream = mp_get_stream_raise(websock, MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL);
+ int err;
+ int old_opts = sock_stream->ioctl(websock, MP_STREAM_SET_DATA_OPTS, FRAME_BIN, &err);
+ sock_stream->write(websock, buf, len, &err);
+ sock_stream->ioctl(websock, MP_STREAM_SET_DATA_OPTS, old_opts, &err);
+}
+
+#define SSTR(s) s, sizeof(s) - 1
+STATIC void write_webrepl_str(mp_obj_t websock, const char *str, int sz) {
+ int err;
+ const mp_stream_p_t *sock_stream = mp_get_stream_raise(websock, MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL);
+ sock_stream->write(websock, str, sz, &err);
+}
+
+STATIC void write_webrepl_resp(mp_obj_t websock, uint16_t code) {
+ char buf[4] = {'W', 'B', code & 0xff, code >> 8};
+ write_webrepl(websock, buf, sizeof(buf));
+}
+
+STATIC mp_obj_t webrepl_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+ mp_arg_check_num(n_args, n_kw, 1, 2, false);
+ mp_get_stream_raise(args[0], MP_STREAM_OP_READ | MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL);
+ DEBUG_printf("sizeof(struct webrepl_file) = %lu\n", sizeof(struct webrepl_file));
+ mp_obj_webrepl_t *o = m_new_obj(mp_obj_webrepl_t);
+ o->base.type = type;
+ o->sock = args[0];
+ o->hdr_to_recv = sizeof(struct webrepl_file);
+ o->data_to_recv = 0;
+ o->state = STATE_PASSWD;
+ write_webrepl_str(args[0], SSTR(passwd_prompt));
+ return o;
+}
+
+STATIC void handle_op(mp_obj_webrepl_t *self) {
+ mp_obj_t open_args[2] = {
+ mp_obj_new_str(self->hdr.fname, strlen(self->hdr.fname), false),
+ MP_OBJ_NEW_QSTR(MP_QSTR_rb)
+ };
+
+ if (self->hdr.type == PUT_FILE) {
+ open_args[1] = MP_OBJ_NEW_QSTR(MP_QSTR_wb);
+ }
+
+ self->cur_file = mp_builtin_open(2, open_args, (mp_map_t*)&mp_const_empty_map);
+ const mp_stream_p_t *file_stream =
+ mp_get_stream_raise(self->cur_file, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE | MP_STREAM_OP_IOCTL);
+
+ #if 0
+ struct mp_stream_seek_t seek = { .offset = self->hdr.offset, .whence = 0 };
+ int err;
+ mp_uint_t res = file_stream->ioctl(self->cur_file, MP_STREAM_SEEK, (uintptr_t)&seek, &err);
+ assert(res != MP_STREAM_ERROR);
+ #endif
+
+ write_webrepl_resp(self->sock, 0);
+
+ if (self->hdr.type == PUT_FILE) {
+ self->data_to_recv = self->hdr.size;
+ } else if (self->hdr.type == GET_FILE) {
+ byte readbuf[2 + 256];
+ int err;
+ // TODO: It's not ideal that we block connection while sending file
+ // and don't process any input.
+ while (1) {
+ mp_uint_t out_sz = file_stream->read(self->cur_file, readbuf + 2, sizeof(readbuf) - 2, &err);
+ assert(out_sz != MP_STREAM_ERROR);
+ readbuf[0] = out_sz;
+ readbuf[1] = out_sz >> 8;
+ DEBUG_printf("webrepl: Sending %d bytes of file\n", out_sz);
+ write_webrepl(self->sock, readbuf, 2 + out_sz);
+ if (out_sz == 0) {
+ break;
+ }
+ }
+
+ write_webrepl_resp(self->sock, 0);
+ self->hdr_to_recv = sizeof(struct webrepl_file);
+ }
+}
+
+STATIC mp_uint_t _webrepl_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode);
+
+STATIC mp_uint_t webrepl_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) {
+ mp_uint_t out_sz;
+ do {
+ out_sz = _webrepl_read(self_in, buf, size, errcode);
+ } while (out_sz == -2);
+ return out_sz;
+}
+
+STATIC mp_uint_t _webrepl_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) {
+ // We know that os.dupterm always calls with size = 1
+ assert(size == 1);
+ mp_obj_webrepl_t *self = self_in;
+ const mp_stream_p_t *sock_stream = mp_get_stream_raise(self->sock, MP_STREAM_OP_READ);
+ mp_uint_t out_sz = sock_stream->read(self->sock, buf, size, errcode);
+ //DEBUG_printf("webrepl: Read %d initial bytes from websocket\n", out_sz);
+ if (out_sz == 0 || out_sz == MP_STREAM_ERROR) {
+ return out_sz;
+ }
+
+ if (self->state == STATE_PASSWD) {
+ char c = *(char*)buf;
+ if (c == '\r' || c == '\n') {
+ self->hdr.fname[self->data_to_recv] = 0;
+ DEBUG_printf("webrepl: entered password: %s\n", self->hdr.fname);
+
+ if (strcmp(self->hdr.fname, webrepl_passwd) != 0) {
+ write_webrepl_str(self->sock, SSTR(denied_prompt));
+ return 0;
+ }
+
+ self->state = STATE_NORMAL;
+ self->data_to_recv = 0;
+ write_webrepl_str(self->sock, SSTR(connected_prompt));
+ } else if (self->data_to_recv < 10) {
+ self->hdr.fname[self->data_to_recv++] = c;
+ }
+ return -2;
+ }
+
+ // If last read data belonged to text record (== REPL)
+ int err;
+ if (sock_stream->ioctl(self->sock, MP_STREAM_GET_DATA_OPTS, 0, &err) == 1) {
+ return out_sz;
+ }
+
+ DEBUG_printf("webrepl: received bin data, hdr_to_recv: %d, data_to_recv=%d\n", self->hdr_to_recv, self->data_to_recv);
+
+ if (self->hdr_to_recv != 0) {
+ char *p = (char*)&self->hdr + sizeof(self->hdr) - self->hdr_to_recv;
+ *p++ = *(char*)buf;
+ if (--self->hdr_to_recv != 0) {
+ mp_uint_t hdr_sz = sock_stream->read(self->sock, p, self->hdr_to_recv, errcode);
+ if (hdr_sz == MP_STREAM_ERROR) {
+ return hdr_sz;
+ }
+ self->hdr_to_recv -= hdr_sz;
+ if (self->hdr_to_recv != 0) {
+ return -2;
+ }
+ }
+
+ DEBUG_printf("webrepl: op: %d, file: %s, chunk @%x, sz=%d\n", self->hdr.type, self->hdr.fname, (uint32_t)self->hdr.offset, self->hdr.size);
+
+ handle_op(self);
+
+ return -2;
+ }
+
+ if (self->data_to_recv != 0) {
+ static byte filebuf[512];
+ filebuf[0] = *(byte*)buf;
+ mp_uint_t buf_sz = 1;
+ if (--self->data_to_recv != 0) {
+ size_t to_read = MIN(sizeof(filebuf) - 1, self->data_to_recv);
+ mp_uint_t sz = sock_stream->read(self->sock, filebuf + 1, to_read, errcode);
+ if (sz == MP_STREAM_ERROR) {
+ return sz;
+ }
+ self->data_to_recv -= sz;
+ buf_sz += sz;
+ }
+
+ DEBUG_printf("webrepl: Writing %lu bytes to file\n", buf_sz);
+ int err;
+ mp_uint_t res = mp_stream_writeall(self->cur_file, filebuf, buf_sz, &err);
+ if(res == MP_STREAM_ERROR) {
+ assert(0);
+ }
+
+ if (self->data_to_recv == 0) {
+ close_meth(self->cur_file);
+ self->hdr_to_recv = sizeof(struct webrepl_file);
+ DEBUG_printf("webrepl: Finished writing file\n");
+ write_webrepl_resp(self->sock, 0);
+ }
+
+ #ifdef MICROPY_PY_WEBREPL_DELAY
+ // Some platforms may have broken drivers and easily gets
+ // overloaded with modest traffic WebREPL file transfers
+ // generate. The basic workaround is a crude rate control
+ // done in such way.
+ mp_hal_delay_ms(MICROPY_PY_WEBREPL_DELAY);
+ #endif
+ }
+
+ return -2;
+}
+
+STATIC mp_uint_t webrepl_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) {
+ mp_obj_webrepl_t *self = self_in;
+ if (self->state == STATE_PASSWD) {
+ // Don't forward output until passwd is entered
+ return size;
+ }
+ const mp_stream_p_t *stream_p = mp_get_stream_raise(self->sock, MP_STREAM_OP_WRITE);
+ return stream_p->write(self->sock, buf, size, errcode);
+}
+
+STATIC mp_obj_t webrepl_set_password(mp_obj_t passwd_in) {
+ mp_uint_t len;
+ const char *passwd = mp_obj_str_get_data(passwd_in, &len);
+ len = MIN(len, sizeof(webrepl_passwd) - 1);
+ memcpy(webrepl_passwd, passwd, len);
+ webrepl_passwd[len] = 0;
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(webrepl_set_password_obj, webrepl_set_password);
+
+STATIC const mp_map_elem_t webrepl_locals_dict_table[] = {
+ { MP_OBJ_NEW_QSTR(MP_QSTR_read), (mp_obj_t)&mp_stream_read_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_write), (mp_obj_t)&mp_stream_write_obj },
+};
+STATIC MP_DEFINE_CONST_DICT(webrepl_locals_dict, webrepl_locals_dict_table);
+
+STATIC const mp_stream_p_t webrepl_stream_p = {
+ .read = webrepl_read,
+ .write = webrepl_write,
+};
+
+STATIC const mp_obj_type_t webrepl_type = {
+ { &mp_type_type },
+ .name = MP_QSTR__webrepl,
+ .make_new = webrepl_make_new,
+ .stream_p = &webrepl_stream_p,
+ .locals_dict = (mp_obj_t)&webrepl_locals_dict,
+};
+
+STATIC const mp_map_elem_t webrepl_module_globals_table[] = {
+ { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_websocket) },
+ { MP_OBJ_NEW_QSTR(MP_QSTR__webrepl), (mp_obj_t)&webrepl_type },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_password), (mp_obj_t)&webrepl_set_password_obj },
+};
+
+STATIC MP_DEFINE_CONST_DICT(webrepl_module_globals, webrepl_module_globals_table);
+
+const mp_obj_module_t mp_module_webrepl = {
+ .base = { &mp_type_module },
+ .name = MP_QSTR__webrepl,
+ .globals = (mp_obj_dict_t*)&webrepl_module_globals,
+};
+
+#endif // MICROPY_PY_WEBREPL
diff --git a/extmod/modwebsocket.c b/extmod/modwebsocket.c
index 78bbe6d13e..344933ded3 100644
--- a/extmod/modwebsocket.c
+++ b/extmod/modwebsocket.c
@@ -37,7 +37,7 @@
#if MICROPY_PY_WEBSOCKET
-enum { FRAME_HEADER, FRAME_OPT, PAYLOAD };
+enum { FRAME_HEADER, FRAME_OPT, PAYLOAD, CONTROL };
enum { BLOCKING_WRITE = 0x80 };
@@ -52,10 +52,14 @@ typedef struct _mp_obj_websocket_t {
byte buf_pos;
byte buf[6];
byte opts;
- // Copy of current frame's flags
+ // Copy of last data frame flags
byte ws_flags;
+ // Copy of current frame flags
+ byte last_flags;
} mp_obj_websocket_t;
+STATIC mp_uint_t websocket_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode);
+
STATIC mp_obj_t websocket_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 1, 2, false);
mp_obj_websocket_t *o = m_new_obj(mp_obj_websocket_t);
@@ -97,10 +101,9 @@ STATIC mp_uint_t websocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int
// "Control frames MAY be injected in the middle of a fragmented message."
// So, they must be processed before data frames (and not alter
// self->ws_flags)
- if ((self->buf[0] & FRAME_OPCODE_MASK) >= FRAME_CLOSE) {
- // TODO: implement
- assert(0);
- }
+ byte frame_type = self->buf[0];
+ self->last_flags = frame_type;
+ frame_type &= FRAME_OPCODE_MASK;
if ((self->buf[0] & FRAME_OPCODE_MASK) == FRAME_CONT) {
// Preserve previous frame type
@@ -119,7 +122,7 @@ STATIC mp_uint_t websocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int
// Msg size is next 2 bytes
to_recv += 2;
} else if (sz == 127) {
- // Msg size is next 2 bytes
+ // Msg size is next 8 bytes
assert(0);
}
if (self->buf[1] & 0x80) {
@@ -133,7 +136,11 @@ STATIC mp_uint_t websocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int
if (to_recv != 0) {
self->state = FRAME_OPT;
} else {
- self->state = PAYLOAD;
+ if (frame_type >= FRAME_CLOSE) {
+ self->state = CONTROL;
+ } else {
+ self->state = PAYLOAD;
+ }
}
continue;
}
@@ -148,14 +155,25 @@ STATIC mp_uint_t websocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int
memcpy(self->mask, self->buf + self->buf_pos - 4, 4);
}
self->buf_pos = 0;
- self->state = PAYLOAD;
+ if ((self->last_flags & FRAME_OPCODE_MASK) >= FRAME_CLOSE) {
+ self->state = CONTROL;
+ } else {
+ self->state = PAYLOAD;
+ }
continue;
}
- case PAYLOAD: {
+ case PAYLOAD:
+ case CONTROL: {
+ mp_uint_t out_sz = 0;
+ if (self->msg_sz == 0) {
+ // In case message had zero payload
+ goto no_payload;
+ }
+
size_t sz = MIN(size, self->msg_sz);
- mp_uint_t out_sz = stream_p->read(self->sock, buf, sz, errcode);
- if (out_sz == MP_STREAM_ERROR) {
+ out_sz = stream_p->read(self->sock, buf, sz, errcode);
+ if (out_sz == 0 || out_sz == MP_STREAM_ERROR) {
return out_sz;
}
@@ -166,12 +184,34 @@ STATIC mp_uint_t websocket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int
self->msg_sz -= out_sz;
if (self->msg_sz == 0) {
+ byte last_state;
+no_payload:
+ last_state = self->state;
self->state = FRAME_HEADER;
self->to_recv = 2;
self->mask_pos = 0;
self->buf_pos = 0;
+
+ // Handle control frame
+ if (last_state == CONTROL) {
+ byte frame_type = self->last_flags & FRAME_OPCODE_MASK;
+ if (frame_type == FRAME_CLOSE) {
+ static char close_resp[2] = {0x88, 0};
+ int err;
+ websocket_write(self_in, close_resp, sizeof(close_resp), &err);
+ return 0;
+ }
+
+ //DEBUG_printf("Finished receiving ctrl message %x, ignoring\n", self->last_flags);
+ continue;
+ }
}
- return out_sz;
+
+ if (out_sz != 0) {
+ return out_sz;
+ }
+ // Empty (data) frame received is not EOF
+ continue;
}
}