Skip to content

Commit 858259f

Browse files
committed
extmod/modopenamp: Implement streaming protocol for endpoints.
This patch implements Endpoint's streaming protocol, allowing endpoints to be used with select/poll or asyncio. It also implements an `endpoint.recv()` function, whose behavior matches that of UDP sockets' recv(). Calling `endpoint.recv()` returns a single message, truncated to fit the requested number of bytes. If two messages are pending on the endpoint, the code must do two separate reads to get them both. The queuing of RPMsg messages is done by holding RPMsg buffers and releasing them when they are no longer needed (i.e., when `recv` is called on all of them). This avoids the double copying of buffers, saves memory, but also allows the host to block the remote if it's not receiving messages (if the queue size is the same as the number of buffers in the VRING). Note that the queuing of RPMsg messages is only enabled if `callback=None` (i.e., when the application is not processing messages synchronously). Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
1 parent 5da4387 commit 858259f

File tree

1 file changed

+129
-6
lines changed

1 file changed

+129
-6
lines changed

extmod/modopenamp.c

Lines changed: 129 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
#include "py/obj.h"
3333
#include "py/nlr.h"
3434
#include "py/runtime.h"
35+
#include "py/stream.h"
36+
#include "py/mperrno.h"
3537
#include "py/mpprint.h"
3638

3739
#include "metal/sys.h"
@@ -44,6 +46,7 @@
4446
#include "openamp/open_amp.h"
4547
#include "openamp/remoteproc.h"
4648
#include "openamp/remoteproc_loader.h"
49+
#include "lib/rpmsg/rpmsg_internal.h"
4750
#include "modopenamp.h"
4851

4952
#if !MICROPY_ENABLE_FINALISER
@@ -128,19 +131,52 @@ static MP_DEFINE_CONST_OBJ_TYPE(
128131
);
129132

130133
// ###################### RPMsg Endpoint class ######################
134+
// The number of RPMsg buffers to hold for an Endpoint. Note if this number matches
135+
// the number of buffers per VRING, a full ring buffer/queue will cause the other
136+
// side to block, because all buffers will be held by the application. If it's less
137+
// than the number of buffers in a VRING, messages will be dropped if this side is not
138+
// receiving messages.
139+
#define ENDPOINT_RPMSG_RING_SIZE VRING_NUM_BUFFS
131140
typedef struct _endpoint_obj_t {
132141
mp_obj_base_t base;
133142
mp_obj_t name;
134143
mp_obj_t callback;
135144
struct rpmsg_endpoint ep;
145+
volatile uint32_t head;
146+
volatile uint32_t tail;
147+
void *rpmsg_buf[ENDPOINT_RPMSG_RING_SIZE];
136148
} endpoint_obj_t;
137149

138150
static const mp_obj_type_t endpoint_type;
139151

152+
static int endpoint_rpmsg_enqueue(endpoint_obj_t *ept, void *buf) {
153+
if (((ept->tail + 1) % ENDPOINT_RPMSG_RING_SIZE) != ept->head) {
154+
ept->rpmsg_buf[ept->tail] = buf;
155+
ept->tail = (ept->tail + 1) % ENDPOINT_RPMSG_RING_SIZE;
156+
rpmsg_hold_rx_buffer(&ept->ep, buf);
157+
return 0;
158+
}
159+
return -1;
160+
}
161+
162+
static size_t endpoint_rpmsg_dequeue(endpoint_obj_t *ept, void *buf, size_t len) {
163+
size_t size = 0;
164+
if (ept->head != ept->tail) {
165+
void *rpmsg = ept->rpmsg_buf[ept->head];
166+
size = MIN(RPMSG_LOCATE_HDR(rpmsg)->len, len);
167+
memcpy(buf, rpmsg, size);
168+
ept->head = (ept->head + 1) % ENDPOINT_RPMSG_RING_SIZE;
169+
rpmsg_release_rx_buffer(&ept->ep, rpmsg);
170+
}
171+
return size;
172+
}
173+
140174
static int endpoint_recv_callback(struct rpmsg_endpoint *ept, void *data, size_t len, uint32_t src, void *priv) {
141175
debug_printf("endpoint_recv_callback() message received src: %lu msg len: %d\n", src, len);
142176
endpoint_obj_t *self = metal_container_of(ept, endpoint_obj_t, ep);
143-
if (self->callback != mp_const_none) {
177+
if (self->callback == mp_const_none) {
178+
endpoint_rpmsg_enqueue(self, data);
179+
} else {
144180
mp_call_function_2(self->callback, mp_obj_new_int(src), mp_obj_new_bytearray_by_ref(len, data));
145181
}
146182
return 0;
@@ -195,6 +231,40 @@ static mp_obj_t endpoint_send(uint n_args, const mp_obj_t *pos_args, mp_map_t *k
195231
}
196232
static MP_DEFINE_CONST_FUN_OBJ_KW(endpoint_send_obj, 2, endpoint_send);
197233

234+
static mp_obj_t endpoint_recv(uint n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
235+
enum { ARG_timeout };
236+
static const mp_arg_t allowed_args[] = {
237+
{ MP_QSTR_timeout, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0 } },
238+
};
239+
240+
// Parse args.
241+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
242+
mp_arg_parse_all(n_args - 2, pos_args + 2, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
243+
244+
endpoint_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
245+
vstr_t vstr;
246+
size_t len = mp_obj_get_int(pos_args[1]);
247+
vstr_init_len(&vstr, len);
248+
249+
mp_int_t timeout = args[ARG_timeout].u_int;
250+
for (mp_uint_t start = mp_hal_ticks_ms(); ;) {
251+
vstr.len = endpoint_rpmsg_dequeue(self, vstr.buf, len);
252+
if (vstr.len > 0) {
253+
break;
254+
}
255+
if (timeout == 0) {
256+
break;
257+
}
258+
if (timeout > 0 && (mp_hal_ticks_ms() - start > timeout)) {
259+
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("timeout waiting for message"));
260+
}
261+
MICROPY_EVENT_POLL_HOOK
262+
}
263+
264+
return mp_obj_new_bytes_from_vstr(&vstr);
265+
}
266+
static MP_DEFINE_CONST_FUN_OBJ_KW(endpoint_recv_obj, 2, endpoint_recv);
267+
198268
static mp_obj_t endpoint_is_ready(mp_obj_t self_in) {
199269
endpoint_obj_t *self = MP_OBJ_TO_PTR(self_in);
200270
return is_rpmsg_ept_ready(&self->ep) ? mp_const_true : mp_const_false;
@@ -209,12 +279,13 @@ static mp_obj_t endpoint_deinit(mp_obj_t self_in) {
209279
static MP_DEFINE_CONST_FUN_OBJ_1(endpoint_deinit_obj, endpoint_deinit);
210280

211281
static mp_obj_t endpoint_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
212-
enum { ARG_name, ARG_callback, ARG_src, ARG_dest };
282+
enum { ARG_name, ARG_callback, ARG_src, ARG_dest, ARG_buf };
213283
static const mp_arg_t allowed_args[] = {
214-
{ MP_QSTR_name, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE } },
215-
{ MP_QSTR_callback, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE } },
216-
{ MP_QSTR_src, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = RPMSG_ADDR_ANY } },
217-
{ MP_QSTR_dest, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = RPMSG_ADDR_ANY } },
284+
{ MP_QSTR_name, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE } },
285+
{ MP_QSTR_callback, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
286+
{ MP_QSTR_src, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = RPMSG_ADDR_ANY } },
287+
{ MP_QSTR_dest, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = RPMSG_ADDR_ANY } },
288+
{ MP_QSTR_buf, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 2048 } },
218289
};
219290

220291
// Parse args.
@@ -224,6 +295,7 @@ static mp_obj_t endpoint_make_new(const mp_obj_type_t *type, size_t n_args, size
224295
endpoint_obj_t *self = mp_obj_malloc_with_finaliser(endpoint_obj_t, &endpoint_type);
225296
self->name = args[ARG_name].u_obj;
226297
self->callback = args[ARG_callback].u_obj;
298+
self->head = self->tail = 0;
227299

228300
if (MP_STATE_PORT(virtio_device) == NULL) {
229301
openamp_init();
@@ -241,15 +313,66 @@ static const mp_rom_map_elem_t endpoint_locals_dict_table[] = {
241313
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_Endpoint) },
242314
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&endpoint_deinit_obj) },
243315
{ MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&endpoint_send_obj) },
316+
{ MP_ROM_QSTR(MP_QSTR_recv), MP_ROM_PTR(&endpoint_recv_obj) },
244317
{ MP_ROM_QSTR(MP_QSTR_is_ready), MP_ROM_PTR(&endpoint_is_ready_obj) },
245318
};
246319
static MP_DEFINE_CONST_DICT(endpoint_locals_dict, endpoint_locals_dict_table);
247320

321+
mp_uint_t endpoint_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) {
322+
endpoint_obj_t *self = MP_OBJ_TO_PTR(self_in);
323+
if (!is_rpmsg_ept_ready(&self->ep)) {
324+
return MP_STREAM_ERROR;
325+
}
326+
return endpoint_rpmsg_dequeue(self, buf, size);
327+
}
328+
329+
mp_uint_t endpoint_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) {
330+
endpoint_obj_t *self = MP_OBJ_TO_PTR(self_in);
331+
int ret = 0;
332+
if (!is_rpmsg_ept_ready(&self->ep)) {
333+
ret = MP_STREAM_ERROR;
334+
} else {
335+
ret = rpmsg_send(&self->ep, buf, size);
336+
if (ret < 0) {
337+
ret = MP_STREAM_ERROR;
338+
}
339+
}
340+
return ret;
341+
}
342+
343+
mp_uint_t endpoint_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) {
344+
endpoint_obj_t *self = MP_OBJ_TO_PTR(self_in);
345+
mp_uint_t ret = 0;
346+
if (request == MP_STREAM_CLOSE) {
347+
rpmsg_destroy_ept(&self->ep);
348+
} else if (request == MP_STREAM_POLL) {
349+
if ((arg & MP_STREAM_POLL_RD) && self->head != self->tail) {
350+
ret |= MP_STREAM_POLL_RD;
351+
}
352+
353+
if ((arg & MP_STREAM_POLL_WR) && is_rpmsg_ept_ready(&self->ep)) {
354+
ret |= MP_STREAM_POLL_WR;
355+
}
356+
} else {
357+
*errcode = MP_EINVAL;
358+
ret = MP_STREAM_ERROR;
359+
}
360+
return ret;
361+
}
362+
363+
static const mp_stream_p_t endpoint_stream_p = {
364+
.read = endpoint_read,
365+
.write = endpoint_write,
366+
.ioctl = endpoint_ioctl,
367+
.is_text = false,
368+
};
369+
248370
static MP_DEFINE_CONST_OBJ_TYPE(
249371
endpoint_type,
250372
MP_QSTR_Endpoint,
251373
MP_TYPE_FLAG_NONE,
252374
make_new, endpoint_make_new,
375+
protocol, &endpoint_stream_p,
253376
locals_dict, &endpoint_locals_dict
254377
);
255378

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy