@@ -71,3 +71,170 @@ int ringbuf_put16(ringbuf_t *r, uint16_t v) {
7171 r -> iput = iput_b ;
7272 return 0 ;
7373}
74+
75+ #if MICROPY_PY_MICROPYTHON_RINGBUFFER
76+
77+ #include "py/runtime.h"
78+ #include "py/stream.h"
79+ #include "py/mphal.h"
80+
81+ typedef struct _micropython_ringbuffer_obj_t {
82+ mp_obj_base_t base ;
83+ ringbuf_t ringbuffer ;
84+ uint16_t timeout ; // timeout waiting for first char (in ms)
85+ } micropython_ringbuffer_obj_t ;
86+
87+ STATIC mp_obj_t micropython_ringbuffer_make_new (const mp_obj_type_t * type , size_t n_args , size_t n_kw , const mp_obj_t * args ) {
88+ mp_arg_check_num (n_args , n_kw , 1 , 2 , false);
89+ mp_int_t buff_size = -1 ;
90+ mp_buffer_info_t bufinfo = {NULL , 0 , 0 };
91+
92+ if (!mp_get_buffer (args [0 ], & bufinfo , MP_BUFFER_RW )) {
93+ buff_size = mp_obj_get_int (args [0 ]);
94+ }
95+ micropython_ringbuffer_obj_t * self = mp_obj_malloc (micropython_ringbuffer_obj_t , type );
96+ if (bufinfo .buf != NULL ) {
97+ // buffer passed in, use it directly for ringbuffer
98+ self -> ringbuffer .buf = bufinfo .buf ;
99+ self -> ringbuffer .size = bufinfo .len ;
100+ self -> ringbuffer .iget = self -> ringbuffer .iput = 0 ;
101+ } else {
102+ // Allocation buffer, sdd one extra to buff_size as ringbuf consumes one byte for tracking.
103+ ringbuf_alloc (& (self -> ringbuffer ), buff_size + 1 );
104+ }
105+
106+ if (n_args > 1 ) {
107+ self -> timeout = mp_obj_get_int (args [1 ]);
108+ }
109+ return MP_OBJ_FROM_PTR (self );
110+ }
111+
112+ STATIC mp_obj_t micropython_ringbuffer_settimeout (mp_obj_t self_in , mp_obj_t timeout_in ) {
113+ micropython_ringbuffer_obj_t * self = MP_OBJ_TO_PTR (self_in );
114+ self -> timeout = mp_obj_get_int (timeout_in );
115+ return mp_const_none ;
116+ }
117+ STATIC MP_DEFINE_CONST_FUN_OBJ_2 (micropython_ringbuffer_settimeout_obj , micropython_ringbuffer_settimeout );
118+
119+
120+ STATIC mp_uint_t micropython_ringbuffer_read (mp_obj_t self_in , void * buf_in , mp_uint_t size , int * errcode ) {
121+ micropython_ringbuffer_obj_t * self = MP_OBJ_TO_PTR (self_in );
122+ uint32_t t = mp_hal_ticks_ms () + self -> timeout ;
123+ uint8_t * dest = buf_in ;
124+
125+ for (size_t i = 0 ; i < size ; i ++ ) {
126+ // Wait for the first/next character.
127+ while (ringbuf_avail (& self -> ringbuffer ) == 0 ) {
128+ if (mp_hal_ticks_ms () > t ) { // timed out
129+ if (i <= 0 ) {
130+ * errcode = MP_EAGAIN ;
131+ return MP_STREAM_ERROR ;
132+ } else {
133+ return i ;
134+ }
135+ }
136+ MICROPY_EVENT_POLL_HOOK
137+ }
138+ * dest ++ = ringbuf_get (& (self -> ringbuffer ));
139+ t = mp_hal_ticks_ms () + self -> timeout ;
140+ }
141+ return size ;
142+ }
143+
144+ STATIC mp_uint_t micropython_ringbuffer_write (mp_obj_t self_in , const void * buf_in , mp_uint_t size , int * errcode ) {
145+ micropython_ringbuffer_obj_t * self = MP_OBJ_TO_PTR (self_in );
146+ uint32_t t = mp_hal_ticks_ms () + self -> timeout ;
147+ const uint8_t * src = buf_in ;
148+ size_t i = 0 ;
149+
150+ // Put as many bytes as possible into the transmit buffer.
151+ while (i < size && ringbuf_free (& (self -> ringbuffer )) > 0 ) {
152+ ringbuf_put (& (self -> ringbuffer ), * src ++ );
153+ ++ i ;
154+ }
155+ // If ringbuf full, block until drained elsewhere (eg. irq) or timeout.
156+ while (i < size ) {
157+ while (ringbuf_free (& (self -> ringbuffer )) == 0 ) {
158+ if (mp_hal_ticks_ms () > t ) { // timed out
159+ if (i <= 0 ) {
160+ * errcode = MP_EAGAIN ;
161+ return MP_STREAM_ERROR ;
162+ } else {
163+ return i ;
164+ }
165+ }
166+ MICROPY_EVENT_POLL_HOOK
167+ }
168+ ringbuf_put (& (self -> ringbuffer ), * src ++ );
169+ ++ i ;
170+ t = mp_hal_ticks_ms () + self -> timeout ;
171+ }
172+ // Just in case the fifo was drained during refill of the ringbuf.
173+ return size ;
174+ }
175+
176+ STATIC mp_uint_t micropython_ringbuffer_ioctl (mp_obj_t self_in , mp_uint_t request , uintptr_t arg , int * errcode ) {
177+ micropython_ringbuffer_obj_t * self = MP_OBJ_TO_PTR (self_in );
178+ mp_uint_t ret ;
179+ if (request == MP_STREAM_POLL ) {
180+ ret = 0 ;
181+ if ((arg & MP_STREAM_POLL_RD ) && ringbuf_avail (& self -> ringbuffer ) > 0 ) {
182+ ret |= MP_STREAM_POLL_RD ;
183+ }
184+ if ((arg & MP_STREAM_POLL_WR ) && ringbuf_free (& self -> ringbuffer ) > 0 ) {
185+ ret |= MP_STREAM_POLL_WR ;
186+ }
187+ } else if (request == MP_STREAM_FLUSH ) {
188+ // Should we wait here until empty / timeout?
189+ ret = 0 ;
190+ } else if (request == MP_STREAM_CLOSE ) {
191+ // We don't want to reset head/tail pointers as there might
192+ // still be someone using it, eg. if ringbuffer is used instead of
193+ // a socket, a "writer" might call close before the "reader" is
194+ // finished.
195+ // Should we flush here though?
196+ ret = 0 ;
197+ } else {
198+ * errcode = MP_EINVAL ;
199+ ret = MP_STREAM_ERROR ;
200+ }
201+ return ret ;
202+ }
203+
204+ STATIC mp_obj_t micropython_ringbuffer_any (mp_obj_t self_in ) {
205+ micropython_ringbuffer_obj_t * self = MP_OBJ_TO_PTR (self_in );
206+ return MP_OBJ_NEW_SMALL_INT (ringbuf_avail (& self -> ringbuffer ));
207+ }
208+ STATIC MP_DEFINE_CONST_FUN_OBJ_1 (micropython_ringbuffer_any_obj , micropython_ringbuffer_any );
209+
210+
211+ STATIC const mp_rom_map_elem_t micropython_ringbuffer_locals_dict_table [] = {
212+ { MP_ROM_QSTR (MP_QSTR_any ), MP_ROM_PTR (& micropython_ringbuffer_any_obj ) },
213+ { MP_ROM_QSTR (MP_QSTR_settimeout ), MP_ROM_PTR (& micropython_ringbuffer_settimeout_obj ) },
214+ { MP_ROM_QSTR (MP_QSTR_flush ), MP_ROM_PTR (& mp_stream_flush_obj ) },
215+ { MP_ROM_QSTR (MP_QSTR_read ), MP_ROM_PTR (& mp_stream_read_obj ) },
216+ { MP_ROM_QSTR (MP_QSTR_readline ), MP_ROM_PTR (& mp_stream_unbuffered_readline_obj ) },
217+ { MP_ROM_QSTR (MP_QSTR_readinto ), MP_ROM_PTR (& mp_stream_readinto_obj ) },
218+ { MP_ROM_QSTR (MP_QSTR_write ), MP_ROM_PTR (& mp_stream_write_obj ) },
219+ { MP_ROM_QSTR (MP_QSTR_close ), MP_ROM_PTR (& mp_stream_close_obj ) },
220+
221+ };
222+ STATIC MP_DEFINE_CONST_DICT (micropython_ringbuffer_locals_dict , micropython_ringbuffer_locals_dict_table );
223+
224+ STATIC const mp_stream_p_t ringbuffer_stream_p = {
225+ .read = micropython_ringbuffer_read ,
226+ .write = micropython_ringbuffer_write ,
227+ .ioctl = micropython_ringbuffer_ioctl ,
228+ .is_text = false,
229+ };
230+
231+ MP_DEFINE_CONST_OBJ_TYPE (
232+ mp_type_micropython_ringbuffer ,
233+ MP_QSTR_ringbuffer ,
234+ MP_TYPE_FLAG_NONE ,
235+ make_new , micropython_ringbuffer_make_new ,
236+ protocol , & ringbuffer_stream_p ,
237+ locals_dict , & micropython_ringbuffer_locals_dict
238+ );
239+
240+ #endif
0 commit comments