diff options
| author | Dan Halbert <halbert@halwitz.org> | 2021-02-17 23:24:11 -0500 |
|---|---|---|
| committer | Dan Halbert <halbert@halwitz.org> | 2021-02-17 23:24:11 -0500 |
| commit | ed49c02feb7e206e962bf8a40cd69771b83e25cf (patch) | |
| tree | 98b75891eb1956c222bdb1df48dbc31e7d9fbe74 | |
| parent | c26de0136a0ad746bf0ace600a1a9d3ff428c4c6 (diff) | |
add timeout; finish up for PR
| -rw-r--r-- | py/stream.c | 32 | ||||
| -rw-r--r-- | py/stream.h | 4 | ||||
| -rw-r--r-- | shared-bindings/_bleio/CharacteristicBuffer.c | 4 | ||||
| -rw-r--r-- | shared-bindings/busio/UART.c | 4 | ||||
| -rw-r--r-- | shared-bindings/usb_cdc/Serial.c | 74 | ||||
| -rw-r--r-- | shared-bindings/usb_cdc/Serial.h | 3 | ||||
| -rw-r--r-- | shared-module/usb_cdc/Serial.c | 30 | ||||
| -rw-r--r-- | shared-module/usb_cdc/Serial.h | 4 | ||||
| -rw-r--r-- | shared-module/usb_cdc/__init__.c | 4 |
9 files changed, 119 insertions, 40 deletions
diff --git a/py/stream.c b/py/stream.c index ae702bb1b..0813c1d7f 100644 --- a/py/stream.c +++ b/py/stream.c @@ -99,14 +99,22 @@ const mp_stream_p_t *mp_get_stream_raise(mp_obj_t self_in, int flags) { STATIC mp_obj_t stream_read_generic(size_t n_args, const mp_obj_t *args, byte flags) { // What to do if sz < -1? Python docs don't specify this case. - // CPython does a readall, but here we silently let negatives through, - // and they will cause a MemoryError. + // CPython does a readall, let's do the same. mp_int_t sz; - if (n_args == 1 || args[1] == mp_const_none || ((sz = mp_obj_get_int(args[1])) == -1)) { - return stream_readall(args[0]); - } - const mp_stream_p_t *stream_p = mp_get_stream(args[0]); + if (stream_p->pyserial_read_compatibility) { + // Pyserial defaults to sz=1 if not specified. + if (n_args == 1) { + sz = 1; + } else { + // Pyserial treats negative size as 0. + sz = MAX(0, mp_obj_get_int(args[1])); + } + } else { + if (n_args == 1 || args[1] == mp_const_none || (sz = mp_obj_get_int(args[1])) <= -1) { + return stream_readall(args[0]); + } + } #if MICROPY_PY_BUILTINS_STR_UNICODE if (stream_p->is_text) { @@ -284,7 +292,7 @@ STATIC mp_obj_t stream_readinto(size_t n_args, const mp_obj_t *args) { // https://docs.python.org/3/library/socket.html#socket.socket.recv_into mp_uint_t len = bufinfo.len; if (n_args > 2) { - if (mp_get_stream(args[0])->pyserial_compatibility) { + if (mp_get_stream(args[0])->pyserial_readinto_compatibility) { mp_raise_ValueError(translate("length argument not allowed for this type")); } len = mp_obj_get_int(args[2]); @@ -297,7 +305,10 @@ STATIC mp_obj_t stream_readinto(size_t n_args, const mp_obj_t *args) { mp_uint_t out_sz = mp_stream_read_exactly(args[0], bufinfo.buf, len, &error); if (error != 0) { if (mp_is_nonblocking_error(error)) { - return mp_const_none; + // pyserial readinto never returns None, just 0. + return mp_get_stream(args[0])->pyserial_dont_return_none_compatibility + ? MP_OBJ_NEW_SMALL_INT(0) + : mp_const_none; } mp_raise_OSError(error); } else { @@ -323,7 +334,10 @@ STATIC mp_obj_t stream_readall(mp_obj_t self_in) { // If we read nothing, return None, just like read(). // Otherwise, return data read so far. if (total_size == 0) { - return mp_const_none; + // pyserial read() never returns None, just b''. + return stream_p->pyserial_dont_return_none_compatibility + ? mp_const_empty_bytes + : mp_const_none; } break; } diff --git a/py/stream.h b/py/stream.h index be6b23d40..c05dcfc50 100644 --- a/py/stream.h +++ b/py/stream.h @@ -72,7 +72,9 @@ typedef struct _mp_stream_p_t { mp_uint_t (*write)(mp_obj_t obj, const void *buf, mp_uint_t size, int *errcode); mp_uint_t (*ioctl)(mp_obj_t obj, mp_uint_t request, uintptr_t arg, int *errcode); mp_uint_t is_text : 1; // default is bytes, set this for text stream - bool pyserial_compatibility: 1; // adjust API to match pyserial more closely + bool pyserial_readinto_compatibility: 1; // Disallow size parameter in readinto() + bool pyserial_read_compatibility: 1; // Disallow omitting read(size) size parameter + bool pyserial_dont_return_none_compatibility: 1; // Don't return None for read() or readinto() } mp_stream_p_t; MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_read_obj); diff --git a/shared-bindings/_bleio/CharacteristicBuffer.c b/shared-bindings/_bleio/CharacteristicBuffer.c index 4d6c836c1..333b275ff 100644 --- a/shared-bindings/_bleio/CharacteristicBuffer.c +++ b/shared-bindings/_bleio/CharacteristicBuffer.c @@ -231,8 +231,8 @@ STATIC const mp_stream_p_t characteristic_buffer_stream_p = { .write = bleio_characteristic_buffer_write, .ioctl = bleio_characteristic_buffer_ioctl, .is_text = false, - // Match PySerial when possible, such as disallowing optional length argument for .readinto() - .pyserial_compatibility = true, + // Disallow readinto() size parameter. + .pyserial_readinto_compatibility = true, }; diff --git a/shared-bindings/busio/UART.c b/shared-bindings/busio/UART.c index bf0b7e721..f48109fde 100644 --- a/shared-bindings/busio/UART.c +++ b/shared-bindings/busio/UART.c @@ -415,8 +415,8 @@ STATIC const mp_stream_p_t uart_stream_p = { .write = busio_uart_write, .ioctl = busio_uart_ioctl, .is_text = false, - // Match PySerial when possible, such as disallowing optional length argument for .readinto() - .pyserial_compatibility = true, + // Disallow optional length argument for .readinto() + .pyserial_readinto_compatibility = true, }; const mp_obj_type_t busio_uart_type = { diff --git a/shared-bindings/usb_cdc/Serial.c b/shared-bindings/usb_cdc/Serial.c index 03fb94f31..64f851a48 100644 --- a/shared-bindings/usb_cdc/Serial.c +++ b/shared-bindings/usb_cdc/Serial.c @@ -41,36 +41,34 @@ //| def __init__(self) -> None: //| """You cannot create an instance of `usb_cdc.Serial`. //| -//| Serial objects are constructed for every corresponding entry in the USB +//| Serial objects are pre-constructed for each CDC device in the USB //| descriptor and added to the ``usb_cdc.ports`` tuple.""" //| ... //| -// These are standard stream methods. Code is in py/stream.c. -// -//| def read(self, nbytes: Optional[int] = None) -> Optional[bytes]: -//| """Read characters. If ``nbytes`` is specified then read at most that many -//| bytes. Otherwise, read everything that arrives until the connection -//| times out. Providing the number of bytes expected is highly recommended -//| because it will be faster. +//| def read(self, size: int = 1) -> bytes: +//| """Read at most ``size`` bytes. If ``size`` exceeds the internal buffer size +//| only the bytes in the buffer will be read. If `timeout` is > 0 or ``None``, +//| and fewer than ``size`` bytes are available, keep waiting until the timeout +//| expires or ``size`` bytes are available. //| //| :return: Data read -//| :rtype: bytes or None""" +//| :rtype: bytes""" //| ... //| -//| def readinto(self, buf: WriteableBuffer, nbytes: Optional[int] = None) -> Optional[bytes]: +//| def readinto(self, buf: WriteableBuffer) -> bytes: //| """Read bytes into the ``buf``. If ``nbytes`` is specified then read at most -//| that many bytes. Otherwise, read at most ``len(buf)`` bytes. +//| that many bytes, subject to `timeout`. Otherwise, read at most ``len(buf)`` bytes. //| //| :return: number of bytes read and stored into ``buf`` -//| :rtype: bytes or None""" +//| :rtype: bytes""" //| ... //| -//| def write(self, buf: ReadableBuffer) -> Optional[int]: +//| def write(self, buf: ReadableBuffer) -> int: //| """Write as many bytes as possible from the buffer of bytes. //| //| :return: the number of bytes written -//| :rtype: int or None""" +//| :rtype: int""" //| ... //| //| def flush(self) -> None: @@ -79,7 +77,7 @@ //| // These three methods are used by the shared stream methods. -STATIC mp_uint_t usb_cdc_serial_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { +STATIC mp_uint_t usb_cdc_serial_read_stream(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); byte *buf = buf_in; @@ -91,16 +89,16 @@ STATIC mp_uint_t usb_cdc_serial_read(mp_obj_t self_in, void *buf_in, mp_uint_t s return common_hal_usb_cdc_serial_read(self, buf, size, errcode); } -STATIC mp_uint_t usb_cdc_serial_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) { +STATIC mp_uint_t usb_cdc_serial_write_stream(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) { usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); const byte *buf = buf_in; return common_hal_usb_cdc_serial_write(self, buf, size, errcode); } -STATIC mp_uint_t usb_cdc_serial_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { +STATIC mp_uint_t usb_cdc_serial_ioctl_stream(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_uint_t ret; + mp_uint_t ret = 0; switch (request) { case MP_IOCTL_POLL: { mp_uint_t flags = arg; @@ -134,7 +132,7 @@ STATIC mp_obj_t usb_cdc_serial_get_connected(mp_obj_t self_in) { } MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_get_connected_obj, usb_cdc_serial_get_connected); -const mp_obj_property_t usb_cdc_serial__connected_obj = { +const mp_obj_property_t usb_cdc_serial_connected_obj = { .base.type = &mp_type_property, .proxy = {(mp_obj_t)&usb_cdc_serial_get_connected_obj, (mp_obj_t)&mp_const_none_obj, @@ -195,6 +193,32 @@ STATIC mp_obj_t usb_cdc_serial_reset_output_buffer(mp_obj_t self_in) { } MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_reset_output_buffer_obj, usb_cdc_serial_reset_output_buffer); +//| timeout: Optional[float] +//| """The initial value of `timeout` is ``None``. If ``None``, wait indefinitely to satisfy +//| the conditions of a read operation. If 0, do not wait. If > 0, wait only ``timeout`` seconds.""" +//| +STATIC mp_obj_t usb_cdc_serial_get_timeout(mp_obj_t self_in) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_float_t timeout = common_hal_usb_cdc_serial_get_timeout(self); + return (timeout < 0.0f) ? mp_const_none : mp_obj_new_float(self->timeout); +} +MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_get_timeout_obj, usb_cdc_serial_get_timeout); + +STATIC mp_obj_t usb_cdc_serial_set_timeout(mp_obj_t self_in, mp_obj_t timeout_in) { + usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_usb_cdc_serial_set_timeout(self, + timeout_in == mp_const_none ? -1.0f : mp_obj_get_float(timeout_in)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(usb_cdc_serial_set_timeout_obj, usb_cdc_serial_set_timeout); + +const mp_obj_property_t usb_cdc_serial_timeout_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&usb_cdc_serial_get_timeout_obj, + (mp_obj_t)&usb_cdc_serial_set_timeout_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + STATIC const mp_rom_map_elem_t usb_cdc_serial_locals_dict_table[] = { // Standard stream methods. @@ -210,9 +234,10 @@ STATIC const mp_rom_map_elem_t usb_cdc_serial_locals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_out_waiting), MP_ROM_PTR(&usb_cdc_serial_out_waiting_obj) }, { MP_OBJ_NEW_QSTR(MP_QSTR_reset_input_buffer), MP_ROM_PTR(&usb_cdc_serial_reset_input_buffer_obj) }, { MP_OBJ_NEW_QSTR(MP_QSTR_reset_output_buffer), MP_ROM_PTR(&usb_cdc_serial_reset_output_buffer_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_timeout), MP_ROM_PTR(&usb_cdc_serial_timeout_obj) }, // Not in pyserial protocol. - { MP_OBJ_NEW_QSTR(MP_QSTR_connected), MP_ROM_PTR(&usb_cdc_serial_get_connected_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_connected), MP_ROM_PTR(&usb_cdc_serial_connected_obj) }, @@ -221,10 +246,13 @@ STATIC MP_DEFINE_CONST_DICT(usb_cdc_serial_locals_dict, usb_cdc_serial_locals_di STATIC const mp_stream_p_t usb_cdc_serial_stream_p = { MP_PROTO_IMPLEMENT(MP_QSTR_protocol_stream) - .read = usb_cdc_serial_read, - .write = usb_cdc_serial_write, - .ioctl = usb_cdc_serial_ioctl, + .read = usb_cdc_serial_read_stream, + .write = usb_cdc_serial_write_stream, + .ioctl = usb_cdc_serial_ioctl_stream, .is_text = false, + .pyserial_read_compatibility = true, + .pyserial_readinto_compatibility = true, + .pyserial_dont_return_none_compatibility = true, }; const mp_obj_type_t usb_cdc_serial_type = { diff --git a/shared-bindings/usb_cdc/Serial.h b/shared-bindings/usb_cdc/Serial.h index 6e54b9ad3..149d2c2d8 100644 --- a/shared-bindings/usb_cdc/Serial.h +++ b/shared-bindings/usb_cdc/Serial.h @@ -44,4 +44,7 @@ extern uint32_t common_hal_usb_cdc_serial_flush(usb_cdc_serial_obj_t *self); extern bool common_hal_usb_cdc_serial_get_connected(usb_cdc_serial_obj_t *self); +extern mp_float_t common_hal_usb_cdc_serial_get_timeout(usb_cdc_serial_obj_t *self); +extern void common_hal_usb_cdc_serial_set_timeout(usb_cdc_serial_obj_t *self, mp_float_t timeout); + #endif // MICROPY_INCLUDED_SHARED_BINDINGS_USB_CDC_SERIAL_H diff --git a/shared-module/usb_cdc/Serial.c b/shared-module/usb_cdc/Serial.c index bd3972c8d..f569a1913 100644 --- a/shared-module/usb_cdc/Serial.c +++ b/shared-module/usb_cdc/Serial.c @@ -24,10 +24,32 @@ * THE SOFTWARE. */ +#include "lib/utils/interrupt_char.h" #include "shared-module/usb_cdc/Serial.h" +#include "supervisor/shared/tick.h" + #include "tusb.h" size_t common_hal_usb_cdc_serial_read(usb_cdc_serial_obj_t *self, uint8_t *data, size_t len, int *errcode) { + if (self->timeout < 0.0f) { + while (tud_cdc_n_available(self->idx) < len) { + RUN_BACKGROUND_TASKS; + if (mp_hal_is_interrupted()) { + return 0; + } + } + } else if (self->timeout > 0.0f) { + uint64_t timeout_ms = self->timeout * 1000; + uint64_t start_ticks = supervisor_ticks_ms64(); + while (tud_cdc_n_available(self->idx) < len && + supervisor_ticks_ms64() - start_ticks <= timeout_ms) { + RUN_BACKGROUND_TASKS; + if (mp_hal_is_interrupted()) { + return 0; + } + } + } + // Timeout of 0.0f falls through to here with no waiting or unnecessary calculation. return tud_cdc_n_read(self->idx, data, len); } @@ -61,3 +83,11 @@ uint32_t common_hal_usb_cdc_serial_flush(usb_cdc_serial_obj_t *self) { bool common_hal_usb_cdc_serial_get_connected(usb_cdc_serial_obj_t *self) { return tud_cdc_n_connected(self->idx); } + +mp_float_t common_hal_usb_cdc_serial_get_timeout(usb_cdc_serial_obj_t *self) { + return self->timeout; +} + +void common_hal_usb_cdc_serial_set_timeout(usb_cdc_serial_obj_t *self, mp_float_t timeout) { + self->timeout = timeout; +} diff --git a/shared-module/usb_cdc/Serial.h b/shared-module/usb_cdc/Serial.h index 60a1d1922..ad4bed1a3 100644 --- a/shared-module/usb_cdc/Serial.h +++ b/shared-module/usb_cdc/Serial.h @@ -31,8 +31,8 @@ typedef struct { mp_obj_base_t base; - // Which CDC device? - uint8_t idx; + mp_float_t timeout; // if negative, wait forever. + uint8_t idx; // which CDC device? } usb_cdc_serial_obj_t; #endif // SHARED_MODULE_USB_CDC_SERIAL_H diff --git a/shared-module/usb_cdc/__init__.c b/shared-module/usb_cdc/__init__.c index ecb5777b2..9eae18811 100644 --- a/shared-module/usb_cdc/__init__.c +++ b/shared-module/usb_cdc/__init__.c @@ -38,11 +38,13 @@ #error CFG_TUD_CDC must be exactly 2 #endif -static const usb_cdc_serial_obj_t serial_objs[CFG_TUD_CDC] = { +static usb_cdc_serial_obj_t serial_objs[CFG_TUD_CDC] = { { .base.type = &usb_cdc_serial_type, + .timeout = -1.0f, .idx = 0, }, { .base.type = &usb_cdc_serial_type, + .timeout = -1.0f, .idx = 1, } }; |
