66#if PYBRICKS_PY_IODEVICES && PYBRICKS_PY_IODEVICES_I2CDEVICE
77
88#include "py/mphal.h"
9+ #include "py/objstr.h"
10+
911
1012#include <pbdrv/i2c.h>
1113#include <pbio/port_interface.h>
@@ -31,18 +33,19 @@ typedef struct {
3133 pbio_os_state_t state ;
3234 uint8_t address ;
3335 bool nxt_quirk ;
34- size_t read_len ;
3536 size_t write_len ;
36- size_t read_buf_len ;
37- uint8_t read_buf [];
37+ /**
38+ * The read buffer is allocated as a bytes object when the operation
39+ * begins. It is returned to the user on completion.
40+ */
41+ mp_obj_str_t * read_result ;
3842} device_obj_t ;
3943
4044// pybricks.iodevices.I2CDevice.__init__
4145static mp_obj_t make_new (const mp_obj_type_t * type , size_t n_args , size_t n_kw , const mp_obj_t * args ) {
4246 PB_PARSE_ARGS_CLASS (n_args , n_kw , args ,
4347 PB_ARG_REQUIRED (port ),
4448 PB_ARG_REQUIRED (address ),
45- PB_ARG_DEFAULT_INT (max_read_size , 32 ),
4649 PB_ARG_DEFAULT_FALSE (custom ),
4750 PB_ARG_DEFAULT_FALSE (powered ),
4851 PB_ARG_DEFAULT_FALSE (nxt_quirk )
@@ -69,11 +72,9 @@ static mp_obj_t make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw,
6972 pbdrv_i2c_dev_t * i2c_dev ;
7073 pb_assert (pbio_port_get_i2c_dev (port , & i2c_dev ));
7174
72- size_t read_buf_len = mp_obj_get_int (max_read_size_in );
73- device_obj_t * self = mp_obj_malloc_var (device_obj_t , read_buf , uint8_t , read_buf_len , type );
75+ device_obj_t * self = mp_obj_malloc (device_obj_t , type );
7476 self -> i2c_dev = i2c_dev ;
7577 self -> address = mp_obj_get_int (address_in );
76- self -> read_buf_len = read_buf_len ;
7778 self -> nxt_quirk = mp_obj_is_true (nxt_quirk_in );
7879 if (mp_obj_is_true (powered_in )) {
7980 pbio_port_p1p2_set_power (port , PBIO_PORT_POWER_REQUIREMENTS_BATTERY_VOLTAGE_P1_POS );
@@ -105,14 +106,17 @@ static pbio_error_t operation_iterate_once(device_obj_t *device) {
105106 device -> address ,
106107 NULL , // Already memcpy'd on initial iteration. No need to provide here.
107108 device -> write_len ,
108- device -> read_buf ,
109- device -> read_len ,
109+ ( uint8_t * ) device -> read_result -> data ,
110+ device -> read_result -> len ,
110111 device -> nxt_quirk
111112 );
112113}
113114
114- static mp_obj_t operation_get_return_obj (device_obj_t * device ) {
115- return mp_obj_new_bytes (device -> read_buf , device -> read_len );
115+ static mp_obj_t operation_get_result_obj (device_obj_t * device ) {
116+ mp_obj_t result = MP_OBJ_FROM_PTR (device -> read_result );
117+ // The device should not hold up garbage collection of the result.
118+ device -> read_result = MP_OBJ_NULL ;
119+ return result ;
116120}
117121
118122static mp_obj_t operation_iternext (mp_obj_t self_in ) {
@@ -129,15 +133,8 @@ static mp_obj_t operation_iternext(mp_obj_t self_in) {
129133 // Raises on Timeout and other I/O errors. Proceeds on success.
130134 pb_assert (err );
131135
132- mp_obj_t result = operation_get_return_obj (device );
133-
134- // None is treated as a special case.
135- if (result == mp_const_none ) {
136- return MP_OBJ_STOP_ITERATION ;
137- }
138-
139- // Otherwise, set return value via stop iteration.
140- return mp_make_stop_iteration (result );
136+ // Set return value via stop iteration.
137+ return mp_make_stop_iteration (operation_get_result_obj (device ));
141138}
142139
143140static const mp_rom_map_elem_t operation_locals_dict_table [] = {
@@ -162,17 +159,19 @@ static mp_obj_t write_then_read(size_t n_args, const mp_obj_t *pos_args, mp_map_
162159 uint8_t * write_data = (uint8_t * )mp_obj_str_get_data (write_data_in , & self -> write_len );
163160
164161 self -> state = 0 ;
165- self -> read_len = mp_obj_get_int (read_size_in );
166- if (self -> read_len > self -> read_buf_len ) {
167- self -> read_len = self -> read_buf_len ;
168- }
162+
163+ // Pre-allocate the return value so we have something to write the result to.
164+ self -> read_result = mp_obj_malloc (mp_obj_str_t , & mp_type_bytes );
165+ self -> read_result -> len = mp_obj_get_int (read_size_in );
166+ self -> read_result -> hash = 0 ;
167+ self -> read_result -> data = m_new (byte , self -> read_result -> len );
169168
170169 // Kick off the operation. This will immediately raise if a transaction is
171170 // in progress.
172171 pbio_error_t err = pbdrv_i2c_write_then_read (
173172 & self -> state , self -> i2c_dev , self -> address ,
174173 write_data , self -> write_len ,
175- self -> read_buf , self -> read_len , self -> nxt_quirk );
174+ ( uint8_t * ) self -> read_result -> data , self -> read_result -> len , self -> nxt_quirk );
176175
177176 // Expect yield after the initial call.
178177 if (err == PBIO_SUCCESS ) {
@@ -193,7 +192,8 @@ static mp_obj_t write_then_read(size_t n_args, const mp_obj_t *pos_args, mp_map_
193192 MICROPY_EVENT_POLL_HOOK ;
194193 }
195194 pb_assert (err );
196- return operation_get_return_obj (self );
195+
196+ return operation_get_result_obj (self );
197197}
198198// See also experimental_globals_table below. This function object is added there to make it importable.
199199static MP_DEFINE_CONST_FUN_OBJ_KW (write_then_read_obj , 0 , write_then_read ) ;
0 commit comments