diff options
36 files changed, 964 insertions, 912 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index ce1a942..b90d000 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -134,6 +134,7 @@ function(apply_matrix _m_arg_action _m_arg_matrix) endfunction() add_subdirectory(libmisc) +add_subdirectory(libobj) add_subdirectory(libcr) add_subdirectory(libcr_ipc) add_subdirectory(libhw_generic) diff --git a/build-aux/stack.c.gen b/build-aux/stack.c.gen index 2a23565..e327298 100755 --- a/build-aux/stack.c.gen +++ b/build-aux/stack.c.gen @@ -353,7 +353,7 @@ def main( # The sbc-harness codebase ####################################### - vcalls: dict[str, set[str]] = {} + objcalls: dict[str, set[str]] = {} re_vtable_start = re.compile(r"_vtable\s*=\s*\{") re_vtable_entry = re.compile(r"^\s+\.(?P<meth>\S+)\s*=\s*(?P<impl>\S+),.*") for fname in c_fnames: @@ -367,9 +367,9 @@ def main( impl = m.group("impl") if impl == "NULL": continue - if m.group("meth") not in vcalls: - vcalls[meth] = set() - vcalls[meth].add(impl) + if m.group("meth") not in objcalls: + objcalls[meth] = set() + objcalls[meth].add(impl) if "}" in line: in_vtable = False elif re_vtable_start.search(line): @@ -401,15 +401,15 @@ def main( typ = m.group("typ") lib9p_msgs.add(typ) - re_call_vcall = re.compile(r"VCALL\((?P<obj>[^,]+), (?P<meth>[^,)]+)[,)].*") + re_call_objcall = re.compile(r"LO_CALL\((?P<obj>[^,]+), (?P<meth>[^,)]+)[,)].*") def sbc_indirect_callees(loc: str, line: str) -> list[str] | None: if "/3rd-party/" in loc: return None - if m := re_call_vcall.fullmatch(line): - if m.group("meth") in vcalls: - return sorted(vcalls[m.group("meth")]) - return [f"__indirect_call:{m.group('obj')}->vtable->{m.group('meth')}"] + if m := re_call_objcall.fullmatch(line): + if m.group("meth") in objcalls: + return sorted(objcalls[m.group("meth")]) + return [f"__indirect_call:{m.group('obj')}.vtable->{m.group('meth')}"] if "trigger->cb(trigger->cb_arg)" in line: return [ "alarmclock_sleep_intrhandler", diff --git a/cmd/sbc_harness/main.c b/cmd/sbc_harness/main.c index b17725d..ce80711 100644 --- a/cmd/sbc_harness/main.c +++ b/cmd/sbc_harness/main.c @@ -10,10 +10,11 @@ #include <hardware/flash.h> /* pico-sdk:hardware_flash: for flash_get_unique_id() */ #include <libcr/coroutine.h> +#include <libhw/generic/alarmclock.h> /* so we can set `bootclock` */ #include <libhw/rp2040_hwspi.h> +#include <libhw/rp2040_hwtimer.h> #include <libhw/w5500.h> #include <libmisc/hash.h> -#include <libmisc/vcall.h> #include <libusb/usb_common.h> #include <libdhcp/client.h> #include <lib9p/srv.h> @@ -41,15 +42,6 @@ static COROUTINE hello_world_cr(void *_chan) { cr_end(); } -static COROUTINE dhcp_cr(void *_chip) { - struct w5500 *chip = _chip; - cr_begin(); - - dhcp_client_main(chip, "harness"); - - cr_end(); -} - struct { struct rp2040_hwspi dev_spi; struct w5500 dev_w5500; @@ -58,11 +50,19 @@ struct { struct lib9p_srv srv; } globals; +static COROUTINE dhcp_cr(void *) { + cr_begin(); + + dhcp_client_main(lo_box_w5500_if_as_net_iface(&globals.dev_w5500), "harness"); + + cr_end(); +} + static COROUTINE read9p_cr(void *) { cr_begin(); lib9p_srv_read_cr(&globals.srv, - VCALL(&globals.dev_w5500, tcp_listen, CONFIG_9P_PORT)); + LO_CALL(lo_box_w5500_if_as_net_iface(&globals.dev_w5500), tcp_listen, CONFIG_9P_PORT)); cr_end(); } @@ -94,7 +94,8 @@ COROUTINE init_cr(void *) { 19, /* PIN_MOSI */ 18, /* PIN_CLK */ 17); /* PIN_CS */ - w5500_init(&globals.dev_w5500, "W5500", &globals.dev_spi, + w5500_init(&globals.dev_w5500, "W5500", + lo_box_rp2040_hwspi_as_spi(&globals.dev_spi), 21, /* PIN_INTR */ 20, /* PIN_RESET */ ((struct net_eth_addr){{ @@ -117,7 +118,7 @@ COROUTINE init_cr(void *) { coroutine_add("usb_common", usb_common_cr, NULL); coroutine_add("usb_keyboard", usb_keyboard_cr, &globals.keyboard_chan); coroutine_add("hello_world", hello_world_cr, &globals.keyboard_chan); - coroutine_add_with_stack_size(4*1024, "dhcp", dhcp_cr, &globals.dev_w5500); + coroutine_add_with_stack_size(4*1024, "dhcp", dhcp_cr, NULL); for (int i = 0; i < _CONFIG_9P_NUM_SOCKS; i++) { char name[] = {'r', 'e', 'a', 'd', '-', hexdig[i], '\0'}; coroutine_add(name, read9p_cr, NULL); @@ -131,6 +132,7 @@ COROUTINE init_cr(void *) { } int main() { + bootclock = rp2040_hwtimer(0); stdio_uart_init(); /* char *hdr = "=" * (80-strlen("info : MAIN: ")); */ infof("==================================================================="); diff --git a/lib9p/include/lib9p/srv.h b/lib9p/include/lib9p/srv.h index e9d2d7b..83aabc0 100644 --- a/lib9p/include/lib9p/srv.h +++ b/lib9p/include/lib9p/srv.h @@ -11,7 +11,9 @@ #include <libcr_ipc/rpc.h> #include <libcr_ipc/chan.h> #include <libhw/generic/net.h> +#include <libmisc/assert.h> #include <libmisc/private.h> +#include <libobj/obj.h> #include <lib9p/9p.h> @@ -35,48 +37,53 @@ int lib9p_srv_acknowledge_flush(struct lib9p_srv_ctx *ctx); /* interface definitions ******************************************************/ -struct lib9p_srv_file_vtable; - -typedef struct { - struct lib9p_srv_file_vtable *vtable; -} implements_lib9p_srv_file; - -struct lib9p_srv_file_vtable { - /* all - resource management */ - void (*free )(implements_lib9p_srv_file *); /* must not error */ - struct lib9p_qid (*qid )(implements_lib9p_srv_file *); /* must not error */ - uint32_t (*chio )(implements_lib9p_srv_file *, struct lib9p_srv_ctx *, - bool rd, bool wr, bool trunc); - - /* all - syscalls */ - struct lib9p_stat (*stat )(implements_lib9p_srv_file *, struct lib9p_srv_ctx *); - void (*wstat )(implements_lib9p_srv_file *, struct lib9p_srv_ctx *, - struct lib9p_stat new); - void (*remove )(implements_lib9p_srv_file *, struct lib9p_srv_ctx *); - - /* directories - base */ - implements_lib9p_srv_file *(*dopen )(implements_lib9p_srv_file *, struct lib9p_srv_ctx *, - struct lib9p_s childname); - implements_lib9p_srv_file *(*dcreate)(implements_lib9p_srv_file *, struct lib9p_srv_ctx *, - struct lib9p_s childname, - lib9p_dm_t perm, lib9p_o_t flags); - - /* directories - once opened */ - size_t /* <- obj cnt */ (*dread )(implements_lib9p_srv_file *, struct lib9p_srv_ctx *, - uint8_t *buf, - uint32_t byte_count, /* <- num bytes */ - size_t obj_offset); /* <- starting at this object */ - - /* non-directories - once opened */ - uint32_t (*pread )(implements_lib9p_srv_file *, struct lib9p_srv_ctx *, - void *buf, - uint32_t byte_count, - uint64_t byte_offset); - uint32_t (*pwrite )(implements_lib9p_srv_file *, struct lib9p_srv_ctx *, - void *buf, - uint32_t byte_count, - uint64_t byte_offset); -}; +#define lib9p_srv_file_LO_IFACE \ + /* all - resource management */ \ + LO_FUNC(void , free ) /* must not error */ \ + LO_FUNC(struct lib9p_qid , qid ) /* must not error */ \ + LO_FUNC(uint32_t , chio , struct lib9p_srv_ctx *, \ + bool rd, bool wr, \ + bool trunc) \ + \ + /* all - syscalls */ \ + LO_FUNC(struct lib9p_stat , stat , struct lib9p_srv_ctx *) \ + LO_FUNC(void , wstat , struct lib9p_srv_ctx *, \ + struct lib9p_stat new) \ + LO_FUNC(void , remove , struct lib9p_srv_ctx *) \ + \ + /* directories - base */ \ + LO_FUNC(lo_interface lib9p_srv_file, dopen , struct lib9p_srv_ctx *, \ + struct lib9p_s childname) \ + LO_FUNC(lo_interface lib9p_srv_file, dcreate, struct lib9p_srv_ctx *, \ + struct lib9p_s childname, \ + lib9p_dm_t perm, \ + lib9p_o_t flags) \ + \ + /* directories - once opened */ \ + LO_FUNC(size_t /* <- obj cnt */ , dread , struct lib9p_srv_ctx *, \ + uint8_t *buf, \ + /* num bytes -> */ uint32_t byte_count, \ + /* starting at this object -> */ size_t obj_offset) \ + \ + /* non-directories - once opened */ \ + LO_FUNC(uint32_t , pread , struct lib9p_srv_ctx *, \ + void *buf, \ + uint32_t byte_count, \ + uint64_t byte_offset) \ + LO_FUNC(uint32_t , pwrite , struct lib9p_srv_ctx *, \ + void *buf, \ + uint32_t byte_count, \ + uint64_t byte_offset) +LO_INTERFACE(lib9p_srv_file) + +#define LIB9P_SRV_NOTDIR(TYP, NAM) \ + static lo_interface lib9p_srv_file NAM##_dopen (TYP *, struct lib9p_srv_ctx *, struct lib9p_s) { assert_notreached("not a directory"); } \ + static lo_interface lib9p_srv_file NAM##_dcreate(TYP *, struct lib9p_srv_ctx *, struct lib9p_s, lib9p_dm_t, lib9p_o_t) { assert_notreached("not a directory"); } \ + static size_t NAM##_dread (TYP *, struct lib9p_srv_ctx *, uint8_t *, uint32_t, size_t) { assert_notreached("not a directory"); } + +#define LIB9P_SRV_NOTFILE(TYP, NAM) \ + static uint32_t NAM##_pread (TYP *, struct lib9p_srv_ctx *, void *, uint32_t, uint64_t) { assert_notreached("not a file"); } \ + static uint32_t NAM##_pwrite(TYP *, struct lib9p_srv_ctx *, void *, uint32_t, uint64_t) { assert_notreached("not a file"); } /* main server entrypoints ****************************************************/ @@ -84,8 +91,8 @@ CR_RPC_DECLARE(_lib9p_srv_reqch, struct _lib9p_srv_req *, bool) struct lib9p_srv { /* Things you provide */ - void /*TODO*/ (*auth )(struct lib9p_srv_ctx *, struct lib9p_s treename); /* optional */ - implements_lib9p_srv_file *(*rootdir)(struct lib9p_srv_ctx *, struct lib9p_s treename); + void /*TODO*/ (*auth )(struct lib9p_srv_ctx *, struct lib9p_s treename); /* optional */ + lo_interface lib9p_srv_file (*rootdir)(struct lib9p_srv_ctx *, struct lib9p_s treename); /* For internal use */ BEGIN_PRIVATE(LIB9P_SRV_H) @@ -105,7 +112,7 @@ struct lib9p_srv { * @errno LINUX_ERANGE R-message does not fit into max_msg_size */ -[[noreturn]] void lib9p_srv_read_cr(struct lib9p_srv *srv, implements_net_stream_listener *listener); +[[noreturn]] void lib9p_srv_read_cr(struct lib9p_srv *srv, lo_interface net_stream_listener listener); COROUTINE lib9p_srv_write_cr(void *_srv); #endif /* _LIB9P_SRV_H_ */ diff --git a/lib9p/srv.c b/lib9p/srv.c index 47dd78d..0e58068 100644 --- a/lib9p/srv.c +++ b/lib9p/srv.c @@ -12,7 +12,6 @@ #include <libcr_ipc/mutex.h> #include <libcr_ipc/select.h> #include <libmisc/assert.h> -#include <libmisc/vcall.h> #include <libhw/generic/net.h> #define LOG_NAME 9P_SRV @@ -54,7 +53,7 @@ int lib9p_srv_acknowledge_flush(struct lib9p_srv_ctx *ctx) { typedef typeof( ((struct lib9p_qid){}).path ) srv_path_t; struct srv_pathinfo { - implements_lib9p_srv_file *file; + lo_interface lib9p_srv_file file; srv_path_t parent_dir; /* References from other srv_pathinfos (via .parent_dir) or @@ -108,7 +107,7 @@ struct _srv_fidinfo { struct _srv_conn { /* immutable */ struct lib9p_srv *parent_srv; - implements_net_stream_conn *fd; + lo_interface net_stream_conn fd; cid_t reader; /* the lib9p_srv_read_cr() coroutine */ /* mutable */ cr_mutex_t writelock; @@ -197,7 +196,7 @@ static void respond_error(struct _lib9p_srv_req *req) { &host, req->net_bytes); cr_mutex_lock(&sess->parent_conn->writelock); - r = VCALL(sess->parent_conn->fd, write, + r = LO_CALL(sess->parent_conn->fd, write, req->net_bytes, uint32le_decode(req->net_bytes)); cr_mutex_unlock(&sess->parent_conn->writelock); if (r < 0) @@ -206,12 +205,12 @@ static void respond_error(struct _lib9p_srv_req *req) { /* read coroutine *************************************************************/ -static bool read_at_least(implements_net_stream_conn *fd, uint8_t *buf, size_t goal, size_t *done) { +static bool read_at_least(lo_interface net_stream_conn fd, uint8_t *buf, size_t goal, size_t *done) { assert(buf); assert(goal); assert(done); while (*done < goal) { - ssize_t r = VCALL(fd, read, &buf[*done], CONFIG_9P_MAX_MSG_SIZE - *done); + ssize_t r = LO_CALL(fd, read, &buf[*done], CONFIG_9P_MAX_MSG_SIZE - *done); if (r < 0) { nonrespond_errorf("read: %s", net_strerror(-r)); return true; @@ -227,12 +226,12 @@ static bool read_at_least(implements_net_stream_conn *fd, uint8_t *buf, size_t g static void handle_message(struct _lib9p_srv_req *ctx); -[[noreturn]] void lib9p_srv_read_cr(struct lib9p_srv *srv, implements_net_stream_listener *listener) { +[[noreturn]] void lib9p_srv_read_cr(struct lib9p_srv *srv, lo_interface net_stream_listener listener) { uint8_t buf[CONFIG_9P_MAX_MSG_SIZE]; assert(srv); assert(srv->rootdir); - assert(listener); + assert(!LO_IS_NULL(listener)); srv->readers++; @@ -241,10 +240,10 @@ static void handle_message(struct _lib9p_srv_req *ctx); for (;;) { struct _srv_conn conn = { .parent_srv = srv, - .fd = VCALL(listener, accept), + .fd = LO_CALL(listener, accept), .reader = cr_getcid(), }; - if (!conn.fd) { + if (LO_IS_NULL(conn.fd)) { nonrespond_errorf("accept: error"); srv->readers--; if (srv->readers == 0) @@ -305,12 +304,12 @@ static void handle_message(struct _lib9p_srv_req *ctx); _lib9p_srv_reqch_send_req(&srv->_reqch, &req); } close: - VCALL(conn.fd, close, true, sess.reqs.len == 0); + LO_CALL(conn.fd, close, true, sess.reqs.len == 0); if (sess.reqs.len) { sess.closing = true; cr_pause_and_yield(); assert(sess.reqs.len == 0); - VCALL(conn.fd, close, true, true); + LO_CALL(conn.fd, close, true, true); } } } @@ -438,8 +437,8 @@ static void handle_message(struct _lib9p_srv_req *ctx) { goto write; cr_mutex_lock(&ctx->parent_sess->parent_conn->writelock); - VCALL(ctx->parent_sess->parent_conn->fd, write, - ctx->net_bytes, uint32le_decode(ctx->net_bytes)); + LO_CALL(ctx->parent_sess->parent_conn->fd, write, + ctx->net_bytes, uint32le_decode(ctx->net_bytes)); cr_mutex_unlock(&ctx->parent_sess->parent_conn->writelock); } } @@ -470,15 +469,15 @@ static inline bool srv_util_check_perm(struct _lib9p_srv_req *ctx, struct lib9p_ * Returns a pointer to the stored pathinfo. */ static inline struct srv_pathinfo *srv_util_pathsave(struct _lib9p_srv_req *ctx, - implements_lib9p_srv_file *file, + lo_interface lib9p_srv_file file, srv_path_t parent_path) { assert(ctx); - assert(file); + assert(!LO_IS_NULL(file)); - struct lib9p_qid qid = VCALL(file, qid); + struct lib9p_qid qid = LO_CALL(file, qid); struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, qid.path); if (pathinfo) - assert(pathinfo->file == file); + assert(LO_EQ(pathinfo->file, file)); else { pathinfo = pathmap_store(&ctx->parent_sess->paths, qid.path, (struct srv_pathinfo){ @@ -512,14 +511,14 @@ static inline void srv_util_pathfree(struct _lib9p_srv_req *ctx, srv_path_t path if (pathinfo->gc_refcount == 0) { if (pathinfo->parent_dir != path) srv_util_pathfree(ctx, pathinfo->parent_dir); - VCALL(pathinfo->file, free); + LO_CALL(pathinfo->file, free); pathmap_del(&ctx->parent_sess->paths, path); } } static inline bool srv_util_pathisdir(struct srv_pathinfo *pathinfo) { assert(pathinfo); - return VCALL(pathinfo->file, qid).type & LIB9P_QT_DIR; + return LO_CALL(pathinfo->file, qid).type & LIB9P_QT_DIR; } /** @@ -532,7 +531,7 @@ static inline struct _srv_fidinfo *srv_util_fidsave(struct _lib9p_srv_req *ctx, assert(fid != LIB9P_FID_NOFID); assert(pathinfo); - struct lib9p_qid qid = VCALL(pathinfo->file, qid); + struct lib9p_qid qid = LO_CALL(pathinfo->file, qid); struct _srv_fidinfo *fidinfo = fidmap_load(&ctx->parent_sess->fids, fid); if (fidinfo) { @@ -703,12 +702,12 @@ static void handle_Tattach(struct _lib9p_srv_req *ctx, } /* 1. File object */ - implements_lib9p_srv_file *root_file = srv->rootdir(&ctx->ctx, req->aname); - assert((root_file == NULL) == lib9p_ctx_has_error(&ctx->ctx.basectx)); + lo_interface lib9p_srv_file root_file = srv->rootdir(&ctx->ctx, req->aname); + assert(LO_IS_NULL(root_file) == lib9p_ctx_has_error(&ctx->ctx.basectx)); if (lib9p_ctx_has_error(&ctx->ctx.basectx)) return; - struct lib9p_qid root_qid = VCALL(root_file, qid); + struct lib9p_qid root_qid = LO_CALL(root_file, qid); assert(root_qid.type & LIB9P_QT_DIR); /* 2. pathinfo */ @@ -770,37 +769,37 @@ static void handle_Twalk(struct _lib9p_srv_req *ctx, break; } - implements_lib9p_srv_file *member_file = VCALL(pathinfo->file, dopen, &ctx->ctx, req->wname[resp->nwqid]); - assert((member_file == NULL) == lib9p_ctx_has_error(&ctx->ctx.basectx)); + lo_interface lib9p_srv_file member_file = LO_CALL(pathinfo->file, dopen, &ctx->ctx, req->wname[resp->nwqid]); + assert(LO_IS_NULL(member_file) == lib9p_ctx_has_error(&ctx->ctx.basectx)); if (lib9p_ctx_has_error(&ctx->ctx.basectx)) break; - new_pathinfo = srv_util_pathsave(ctx, member_file, VCALL(pathinfo->file, qid).path); + new_pathinfo = srv_util_pathsave(ctx, member_file, LO_CALL(pathinfo->file, qid).path); } if (srv_util_pathisdir(new_pathinfo)) { - struct lib9p_stat stat = VCALL(new_pathinfo->file, stat, &ctx->ctx); + struct lib9p_stat stat = LO_CALL(new_pathinfo->file, stat, &ctx->ctx); if (lib9p_ctx_has_error(&ctx->ctx.basectx)) break; lib9p_stat_assert(stat); if (!srv_util_check_perm(ctx, &stat, 0b001)) { lib9p_error(&ctx->ctx.basectx, LINUX_EACCES, "you do not have execute permission on that directory"); - srv_util_pathfree(ctx, VCALL(new_pathinfo->file, qid).path); + srv_util_pathfree(ctx, LO_CALL(new_pathinfo->file, qid).path); break; } } - resp->wqid[resp->nwqid] = VCALL(new_pathinfo->file, qid); + resp->wqid[resp->nwqid] = LO_CALL(new_pathinfo->file, qid); - srv_util_pathfree(ctx, VCALL(pathinfo->file, qid).path); + srv_util_pathfree(ctx, LO_CALL(pathinfo->file, qid).path); pathinfo = new_pathinfo; } if (resp->nwqid == req->nwname) { if (!srv_util_fidsave(ctx, req->newfid, pathinfo, req->newfid == req->fid)) - srv_util_pathfree(ctx, VCALL(pathinfo->file, qid).path); + srv_util_pathfree(ctx, LO_CALL(pathinfo->file, qid).path); } else { assert(lib9p_ctx_has_error(&ctx->ctx.basectx)); - srv_util_pathfree(ctx, VCALL(pathinfo->file, qid).path); + srv_util_pathfree(ctx, LO_CALL(pathinfo->file, qid).path); if (resp->nwqid > 0) lib9p_ctx_clear_error(&ctx->ctx.basectx); } @@ -844,7 +843,7 @@ static void handle_Topen(struct _lib9p_srv_req *ctx, if (reqmode & LIB9P_O_RCLOSE) { struct srv_pathinfo *parent = pathmap_load(&ctx->parent_sess->paths, pathinfo->parent_dir); assert(parent); - struct lib9p_stat parent_stat = VCALL(parent->file, stat, &ctx->ctx); + struct lib9p_stat parent_stat = LO_CALL(parent->file, stat, &ctx->ctx); if (lib9p_ctx_has_error(&ctx->ctx.basectx)) return; lib9p_stat_assert(parent_stat); @@ -855,7 +854,7 @@ static void handle_Topen(struct _lib9p_srv_req *ctx, } fidflags = fidflags | FIDFLAG_RCLOSE; } - struct lib9p_stat stat = VCALL(pathinfo->file, stat, &ctx->ctx); + struct lib9p_stat stat = LO_CALL(pathinfo->file, stat, &ctx->ctx); if (lib9p_ctx_has_error(&ctx->ctx.basectx)) return; lib9p_stat_assert(stat); @@ -893,10 +892,10 @@ static void handle_Topen(struct _lib9p_srv_req *ctx, /* Actually make the call. */ if ((reqmode & LIB9P_O_TRUNC) || (rd && !pathinfo->rd_refcount) || (wr && !pathinfo->wr_refcount) ) { - iounit = VCALL(pathinfo->file, chio, &ctx->ctx, - fidflags & FIDFLAG_OPEN_R, - fidflags & FIDFLAG_OPEN_W, - reqmode & LIB9P_O_TRUNC); + iounit = LO_CALL(pathinfo->file, chio, &ctx->ctx, + fidflags & FIDFLAG_OPEN_R, + fidflags & FIDFLAG_OPEN_W, + reqmode & LIB9P_O_TRUNC); if (lib9p_ctx_has_error(&ctx->ctx.basectx)) return; } @@ -962,7 +961,7 @@ static void handle_Tread(struct _lib9p_srv_req *ctx, return; } /* Do it. */ - size_t num = VCALL(pathinfo->file, dread, &ctx->ctx, (uint8_t *)resp->data, req->count, idx); + size_t num = LO_CALL(pathinfo->file, dread, &ctx->ctx, (uint8_t *)resp->data, req->count, idx); /* Translate object-count back to byte-count. */ uint32_t len = 0; for (size_t i = 0; i < num; i++) { @@ -975,7 +974,7 @@ static void handle_Tread(struct _lib9p_srv_req *ctx, fidinfo->dir_idx = idx+num; fidinfo->dir_off = req->offset + len; } else - resp->count = VCALL(pathinfo->file, pread, &ctx->ctx, resp->data, req->count, req->offset); + resp->count = LO_CALL(pathinfo->file, pread, &ctx->ctx, resp->data, req->count, req->offset); } static void handle_Twrite(struct _lib9p_srv_req *ctx, @@ -1001,7 +1000,7 @@ static void handle_Twrite(struct _lib9p_srv_req *ctx, assert(pathinfo); /* Do it. */ - resp->count = VCALL(pathinfo->file, pwrite, &ctx->ctx, req->data, req->count, req->offset); + resp->count = LO_CALL(pathinfo->file, pwrite, &ctx->ctx, req->data, req->count, req->offset); } static void clunkremove(struct _lib9p_srv_req *ctx, lib9p_fid_t fid, bool remove) { @@ -1024,17 +1023,17 @@ static void clunkremove(struct _lib9p_srv_req *ctx, lib9p_fid_t fid, bool remove } struct srv_pathinfo *parent = pathmap_load(&ctx->parent_sess->paths, pathinfo->parent_dir); assert(parent); - struct lib9p_stat parent_stat = VCALL(parent->file, stat, &ctx->ctx); + struct lib9p_stat parent_stat = LO_CALL(parent->file, stat, &ctx->ctx); if (!srv_util_check_perm(ctx, &parent_stat, 0b010)) { lib9p_error(&ctx->ctx.basectx, LINUX_EACCES, "you do not have write permission on the parent directory"); goto clunk; } - VCALL(pathinfo->file, remove, &ctx->ctx); + LO_CALL(pathinfo->file, remove, &ctx->ctx); } clunk: - srv_util_pathfree(ctx, VCALL(pathinfo->file, qid).path); + srv_util_pathfree(ctx, LO_CALL(pathinfo->file, qid).path); fidmap_del(&ctx->parent_sess->fids, fid); } @@ -1069,7 +1068,7 @@ static void handle_Tstat(struct _lib9p_srv_req *ctx, struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, fidinfo->path); assert(pathinfo); - resp->stat = VCALL(pathinfo->file, stat, &ctx->ctx); + resp->stat = LO_CALL(pathinfo->file, stat, &ctx->ctx); if (!lib9p_ctx_has_error(&ctx->ctx.basectx)) lib9p_stat_assert(resp->stat); } diff --git a/lib9p/tests/runtest b/lib9p/tests/runtest index bb83a83..0966000 100755 --- a/lib9p/tests/runtest +++ b/lib9p/tests/runtest @@ -43,7 +43,7 @@ expect_lines \ out=$("${client[@]}" stat 'Documentation/x') expect_lines \ - "'x' 'root' 'root' 'root' q (0000000000000008 1 ) m 0444 at 1728337905 mt 1728337904 l 4 t 0 d 0" + "'x' 'root' 'root' 'root' q (0000000000000001 1 ) m 0444 at 1728337905 mt 1728337904 l 4 t 0 d 0" out=$("${client[@]}" write 'shutdown' <<<1) expect_lines '' diff --git a/lib9p/tests/test_server/main.c b/lib9p/tests/test_server/main.c index 07fc74b..10f67a3 100644 --- a/lib9p/tests/test_server/main.c +++ b/lib9p/tests/test_server/main.c @@ -10,6 +10,7 @@ #include <libcr/coroutine.h> #include <libhw/generic/net.h> #include <libhw/generic/alarmclock.h> +#include <libhw/host_alarmclock.h> #include <libhw/host_net.h> #include <libmisc/macro.h> #include <util9p/static.h> @@ -26,7 +27,7 @@ /* globals ********************************************************************/ -static implements_lib9p_srv_file *get_root(struct lib9p_srv_ctx *, struct lib9p_s); +static lo_interface lib9p_srv_file get_root(struct lib9p_srv_ctx *, struct lib9p_s); const char *hexdig = "0123456789abcdef"; @@ -42,31 +43,31 @@ struct { /* api ************************************************************************/ struct api_file { - implements_lib9p_srv_file; - uint64_t pathnum; }; +LO_IMPLEMENTATION_H(lib9p_srv_file, struct api_file, api) +LO_IMPLEMENTATION_C(lib9p_srv_file, struct api_file, api, static) -static void api_free(implements_lib9p_srv_file *) {} -static uint32_t api_chio(implements_lib9p_srv_file *, struct lib9p_srv_ctx *, bool, bool, bool) { return 0; } - -static void api_wstat(implements_lib9p_srv_file *, struct lib9p_srv_ctx *ctx, struct lib9p_stat) { lib9p_error(&ctx->basectx, LINUX_EROFS, "cannot wstat API file"); } -static void api_remove(implements_lib9p_srv_file *, struct lib9p_srv_ctx *ctx) { lib9p_error(&ctx->basectx, LINUX_EROFS, "cannot remove API file"); } - -static struct lib9p_qid api_qid(implements_lib9p_srv_file *_self) { - struct api_file *self = VCALL_SELF(struct api_file, implements_lib9p_srv_file, _self); +static void api_free(struct api_file *self) { + assert(self); +} +static struct lib9p_qid api_qid(struct api_file *self) { assert(self); - return (struct lib9p_qid){ .type = LIB9P_QT_FILE, .vers = 1, .path = self->pathnum, }; } -static struct lib9p_stat api_stat(implements_lib9p_srv_file *_self, struct lib9p_srv_ctx *) { - struct api_file *self = VCALL_SELF(struct api_file, implements_lib9p_srv_file, _self); +static uint32_t api_chio(struct api_file *self, struct lib9p_srv_ctx *ctx, bool, bool, bool) { assert(self); + assert(ctx); + return 0; +} +static struct lib9p_stat api_stat(struct api_file *self, struct lib9p_srv_ctx *ctx) { + assert(self); + assert(ctx); return (struct lib9p_stat){ .kern_type = 0, .kern_dev = 0, @@ -85,85 +86,89 @@ static struct lib9p_stat api_stat(implements_lib9p_srv_file *_self, struct lib9p .file_last_modified_n_uid = 0, }; } -static uint32_t api_pwrite(implements_lib9p_srv_file *, struct lib9p_srv_ctx *, void *, uint32_t byte_count, uint64_t) { +static void api_wstat(struct api_file *self, struct lib9p_srv_ctx *ctx, struct lib9p_stat) { + assert(self); + assert(ctx); + lib9p_error(&ctx->basectx, LINUX_EROFS, "cannot wstat API file"); +} +static void api_remove(struct api_file *self, struct lib9p_srv_ctx *ctx) { + assert(self); + assert(ctx); + lib9p_error(&ctx->basectx, LINUX_EROFS, "cannot remove API file"); +} + +LIB9P_SRV_NOTDIR(struct api_file, api) + +static uint32_t api_pwrite(struct api_file *self, struct lib9p_srv_ctx *ctx, void *buf, uint32_t byte_count, uint64_t LM_UNUSED(offset)) { + assert(self); + assert(ctx); + assert(buf); if (byte_count == 0) return 0; for (int i = 0; i < CONFIG_SRV9P_NUM_CONNS; i++) - VCALL(&globals.listeners[i], close); + LO_CALL(lo_box_hostnet_tcplist_as_net_stream_listener(&globals.listeners[i]), close); return byte_count; } - -static struct lib9p_srv_file_vtable api_file_vtable = { - .free = api_free, - .qid = api_qid, - .chio = api_chio, - - .stat = api_stat, - .wstat = api_wstat, - .remove = api_remove, - - .pread = NULL, - .pwrite = api_pwrite, -}; +static uint32_t api_pread(struct api_file *, struct lib9p_srv_ctx *, void *, uint32_t, uint64_t) { + assert_notreached("not readable"); +} /* file tree ******************************************************************/ -#define FILE_COMMON(NAME) { \ - .vtable = &util9p_static_file_vtable, \ - \ - .u_name = "root", .u_num = 0, /* owner user */ \ - .g_name = "root", .g_num = 0, /* owner group */ \ - .m_name = "root", .m_num = 0, /* last-modified-by user */ \ - \ - .pathnum = __COUNTER__, \ - .name = NAME, \ - .perm = 0444, \ - .atime = 1728337905, \ - .mtime = 1728337904, \ - } - -#define DIR_COMMON(NAME) { \ - .vtable = &util9p_static_dir_vtable, \ - \ - .u_name = "root", .u_num = 0, /* owner user */ \ - .g_name = "root", .g_num = 0, /* owner group */ \ - .m_name = "root", .m_num = 0, /* last-modified-by user */ \ - \ - .pathnum = __COUNTER__, \ - .name = NAME, \ - .perm = 0555, \ - .atime = 1728337905, \ - .mtime = 1728337904, \ - } - -#define STATIC_FILE(STRNAME, SYMNAME) \ - &((static struct util9p_static_file){ \ - ._util9p_static_common = FILE_COMMON(STRNAME), \ - .data_start = _binary_static_##SYMNAME##_start, \ - .data_end = _binary_static_##SYMNAME##_end, \ +#define _box(nam, obj) \ + ((struct lib9p_srv_file){ \ + .self = obj, \ + .vtable = (void*)&_lo_##nam##_lib9p_srv_file_vtable, \ }) - -static struct util9p_static_dir root = { - ._util9p_static_common = DIR_COMMON(""), - .members = { - &((static struct util9p_static_dir){ - ._util9p_static_common = DIR_COMMON("Documentation"), - .members = { - STATIC_FILE("x", Documentation_x), - NULL - }, - }), - STATIC_FILE("README.md", README_md), - &((struct api_file){ - .vtable = &api_file_vtable, - .pathnum = __COUNTER__, - }), - NULL, - }, -}; - -static implements_lib9p_srv_file *get_root(struct lib9p_srv_ctx *LM_UNUSED(ctx), struct lib9p_s LM_UNUSED(treename)) { - return &root; +#define lo_box_util9p_static_file_as_lib9p_srv_file(obj) _box(util9p_static_file, obj) +#define lo_box_util9p_static_dir_as_lib9p_srv_file(obj) _box(util9p_static_dir, obj) +#define lo_box_api_as_lib9p_srv_file(obj) _box(api, obj) + +enum { PATH_BASE = __COUNTER__ }; +#define PATH_COUNTER __COUNTER__ - PATH_BASE + +#define STATIC_FILE(STRNAME, SYMNAME) \ + lo_box_util9p_static_file_as_lib9p_srv_file(&((struct util9p_static_file){ \ + ._util9p_static_common = { \ + .u_name = "root", .u_num = 0, /* owner user */ \ + .g_name = "root", .g_num = 0, /* owner group */ \ + .m_name = "root", .m_num = 0, /* last-modified-by user */ \ + \ + .pathnum = PATH_COUNTER, \ + .name = STRNAME, \ + .perm = 0444, \ + .atime = 1728337905, \ + .mtime = 1728337904, \ + }, \ + .data_start = _binary_static_##SYMNAME##_start, \ + .data_end = _binary_static_##SYMNAME##_end, \ + })) + +#define STATIC_DIR(STRNAME, ...) \ + lo_box_util9p_static_dir_as_lib9p_srv_file(&((struct util9p_static_dir){ \ + ._util9p_static_common = { \ + .u_name = "root", .u_num = 0, /* owner user */ \ + .g_name = "root", .g_num = 0, /* owner group */ \ + .m_name = "root", .m_num = 0, /* last-modified-by user */ \ + \ + .pathnum = PATH_COUNTER, \ + .name = STRNAME, \ + .perm = 0555, \ + .atime = 1728337905, \ + .mtime = 1728337904, \ + }, \ + .members = { __VA_ARGS__ __VA_OPT__(,) LO_NULL(lib9p_srv_file) }, \ + })) + +struct lib9p_srv_file root = + STATIC_DIR("", + STATIC_DIR("Documentation", + STATIC_FILE("x", Documentation_x)), + STATIC_FILE("README.md", README_md), + lo_box_api_as_lib9p_srv_file(&(struct api_file){.pathnum = PATH_COUNTER})); + +static lo_interface lib9p_srv_file get_root(struct lib9p_srv_ctx *LM_UNUSED(ctx), struct lib9p_s LM_UNUSED(treename)) { + return root; } /* main ***********************************************************************/ @@ -174,7 +179,7 @@ static COROUTINE read_cr(void *_i) { hostnet_tcp_listener_init(&globals.listeners[i], 9000); - lib9p_srv_read_cr(&globals.srv, &globals.listeners[i]); + lib9p_srv_read_cr(&globals.srv, lo_box_hostnet_tcplist_as_net_stream_listener(&globals.listeners[i])); cr_end(); } @@ -199,6 +204,10 @@ static COROUTINE init_cr(void *) { } int main() { + struct hostclock clock_monotonic = { + .clock_id = CLOCK_MONOTONIC, + }; + bootclock = lo_box_hostclock_as_alarmclock(&clock_monotonic); coroutine_add("init", init_cr, NULL); coroutine_main(); return 0; diff --git a/lib9p_util/include/util9p/static.h b/lib9p_util/include/util9p/static.h index 9ec03ef..4afdb51 100644 --- a/lib9p_util/include/util9p/static.h +++ b/lib9p_util/include/util9p/static.h @@ -1,6 +1,6 @@ /* util9p/static.h - Serve static files over 9P * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later */ @@ -10,8 +10,6 @@ #include <lib9p/srv.h> typedef struct { - implements_lib9p_srv_file; - char *u_name; uint32_t u_num; char *g_name; @@ -29,9 +27,9 @@ struct util9p_static_dir { _util9p_static_common; /* NULL-terminated */ - implements_lib9p_srv_file *members[]; + lo_interface lib9p_srv_file members[]; }; - +LO_IMPLEMENTATION_H(lib9p_srv_file, struct util9p_static_dir, util9p_static_dir); struct util9p_static_file { _util9p_static_common; @@ -40,8 +38,6 @@ struct util9p_static_file { char *data_end; /* may be NULL, in which case data_size is used */ size_t data_size; /* only used if .data_end==NULL */ }; - -extern struct lib9p_srv_file_vtable util9p_static_dir_vtable; -extern struct lib9p_srv_file_vtable util9p_static_file_vtable; +LO_IMPLEMENTATION_H(lib9p_srv_file, struct util9p_static_file, util9p_static_file); #endif /* _UTIL9P_STATIC_H_ */ diff --git a/lib9p_util/static.c b/lib9p_util/static.c index 0f0efad..a6ea8f6 100644 --- a/lib9p_util/static.c +++ b/lib9p_util/static.c @@ -6,49 +6,18 @@ #include <libmisc/assert.h> #include <libmisc/macro.h> -#include <libmisc/vcall.h> #include <util9p/static.h> -/* common *********************************************************************/ +LO_IMPLEMENTATION_C(lib9p_srv_file, struct util9p_static_dir, util9p_static_dir, static); +LO_IMPLEMENTATION_C(lib9p_srv_file, struct util9p_static_file, util9p_static_file, static); -static void util9p_static_common_free(implements_lib9p_srv_file *_self) { - _util9p_static_common *self = VCALL_SELF(_util9p_static_common, implements_lib9p_srv_file, _self); - assert(self); - - /* do nothing */ -} - -static uint32_t util9p_static_common_chio(implements_lib9p_srv_file *_self, struct lib9p_srv_ctx *ctx, - bool LM_UNUSED(rd), bool LM_UNUSED(wr), bool LM_UNUSED(trunc)) { - _util9p_static_common *self = VCALL_SELF(_util9p_static_common, implements_lib9p_srv_file, _self); - assert(self); - assert(ctx); - - return 0; -} +/* dir ************************************************************************/ -static void util9p_static_common_wstat(implements_lib9p_srv_file *_self, struct lib9p_srv_ctx *ctx, - struct lib9p_stat LM_UNUSED(new)) { - _util9p_static_common *self = VCALL_SELF(_util9p_static_common, implements_lib9p_srv_file, _self); +static void util9p_static_dir_free(struct util9p_static_dir *self) { assert(self); - assert(ctx); - - lib9p_error(&ctx->basectx, LINUX_EROFS, "read-only part of filesystem"); } - -static void util9p_static_common_remove(implements_lib9p_srv_file *_self, struct lib9p_srv_ctx *ctx) { - _util9p_static_common *self = VCALL_SELF(_util9p_static_common, implements_lib9p_srv_file, _self); - assert(self); - assert(ctx); - - lib9p_error(&ctx->basectx, LINUX_EROFS, "read-only part of filesystem"); -} - -/* dir ************************************************************************/ - -static struct lib9p_qid util9p_static_dir_qid(implements_lib9p_srv_file *_self) { - struct util9p_static_dir *self = VCALL_SELF(struct util9p_static_dir, implements_lib9p_srv_file, _self); +static struct lib9p_qid util9p_static_dir_qid(struct util9p_static_dir *self) { assert(self); return (struct lib9p_qid){ @@ -57,9 +26,13 @@ static struct lib9p_qid util9p_static_dir_qid(implements_lib9p_srv_file *_self) .path = self->pathnum, }; } +static uint32_t util9p_static_dir_chio(struct util9p_static_dir *self, struct lib9p_srv_ctx *ctx, bool, bool, bool) { + assert(self); + assert(ctx); + return 0; +} -static struct lib9p_stat util9p_static_dir_stat(implements_lib9p_srv_file *_self, struct lib9p_srv_ctx *ctx) { - struct util9p_static_dir *self = VCALL_SELF(struct util9p_static_dir, implements_lib9p_srv_file, _self); +static struct lib9p_stat util9p_static_dir_stat(struct util9p_static_dir *self, struct lib9p_srv_ctx *ctx) { assert(self); assert(ctx); @@ -81,51 +54,61 @@ static struct lib9p_stat util9p_static_dir_stat(implements_lib9p_srv_file *_self .file_last_modified_n_uid = self->m_num, }; } +static void util9p_static_dir_wstat(struct util9p_static_dir *self, struct lib9p_srv_ctx *ctx, + struct lib9p_stat) { + assert(self); + assert(ctx); -static implements_lib9p_srv_file *util9p_static_dir_dopen(implements_lib9p_srv_file *_self, struct lib9p_srv_ctx *ctx, - struct lib9p_s childname) { - struct util9p_static_dir *self = VCALL_SELF(struct util9p_static_dir, implements_lib9p_srv_file, _self); + lib9p_error(&ctx->basectx, LINUX_EROFS, "read-only part of filesystem"); +} +static void util9p_static_dir_remove(struct util9p_static_dir *self, struct lib9p_srv_ctx *ctx) { assert(self); assert(ctx); - for (size_t i = 0; self->members[i]; i++) { - implements_lib9p_srv_file *filep = self->members[i]; - struct lib9p_stat stat = VCALL(filep, stat, ctx); + lib9p_error(&ctx->basectx, LINUX_EROFS, "read-only part of filesystem"); +} + +static lo_interface lib9p_srv_file util9p_static_dir_dopen(struct util9p_static_dir *self, struct lib9p_srv_ctx *ctx, + struct lib9p_s childname) { + assert(self); + assert(ctx); + + for (size_t i = 0; !LO_IS_NULL(self->members[i]); i++) { + lo_interface lib9p_srv_file file = self->members[i]; + struct lib9p_stat stat = LO_CALL(file, stat, ctx); if (lib9p_ctx_has_error(&ctx->basectx)) break; lib9p_stat_assert(stat); if (lib9p_str_eq(stat.file_name, childname)) - return filep; + return file; } lib9p_error(&ctx->basectx, LINUX_ENOENT, "no such file or directory"); - return NULL; + return LO_NULL(lib9p_srv_file); } -static implements_lib9p_srv_file *util9p_static_dir_dcreate(implements_lib9p_srv_file *_self, struct lib9p_srv_ctx *ctx, - struct lib9p_s LM_UNUSED(childname), - lib9p_dm_t LM_UNUSED(perm), lib9p_o_t LM_UNUSED(flags)) { - struct util9p_static_dir *self = VCALL_SELF(struct util9p_static_dir, implements_lib9p_srv_file, _self); +static lo_interface lib9p_srv_file util9p_static_dir_dcreate(struct util9p_static_dir *self, struct lib9p_srv_ctx *ctx, + struct lib9p_s LM_UNUSED(childname), + lib9p_dm_t LM_UNUSED(perm), lib9p_o_t LM_UNUSED(flags)) { assert(self); assert(ctx); lib9p_error(&ctx->basectx, LINUX_EROFS, "read-only part of filesystem"); - return NULL; + return LO_NULL(lib9p_srv_file); } -static size_t util9p_static_dir_dread(implements_lib9p_srv_file *_self, struct lib9p_srv_ctx *ctx, +static size_t util9p_static_dir_dread(struct util9p_static_dir *self, struct lib9p_srv_ctx *ctx, uint8_t *buf, uint32_t byte_count, size_t _obj_offset) { - struct util9p_static_dir *self = VCALL_SELF(struct util9p_static_dir, implements_lib9p_srv_file, _self); assert(self); assert(ctx); uint32_t byte_offset = 0; size_t obj_offset = _obj_offset; - while (self->members[obj_offset]) { - implements_lib9p_srv_file *filep = self->members[obj_offset]; - struct lib9p_stat stat = VCALL(filep, stat, ctx); + while (!LO_IS_NULL(self->members[obj_offset])) { + lo_interface lib9p_srv_file file = self->members[obj_offset]; + struct lib9p_stat stat = LO_CALL(file, stat, ctx); if (lib9p_ctx_has_error(&ctx->basectx)) break; lib9p_stat_assert(stat); @@ -143,22 +126,27 @@ static size_t util9p_static_dir_dread(implements_lib9p_srv_file *_self, struct l return obj_offset - _obj_offset; } -struct lib9p_srv_file_vtable util9p_static_dir_vtable = { - .free = util9p_static_common_free, - .qid = util9p_static_dir_qid, - .chio = util9p_static_common_chio, - - .stat = util9p_static_dir_stat, - .wstat = util9p_static_common_wstat, - .remove = util9p_static_common_remove, +LIB9P_SRV_NOTFILE(struct util9p_static_dir, util9p_static_dir) - .dopen = util9p_static_dir_dopen, - .dcreate = util9p_static_dir_dcreate, +/* file ***********************************************************************/ - .dread = util9p_static_dir_dread, -}; +static void util9p_static_file_free(struct util9p_static_file *self) { + assert(self); +} +static struct lib9p_qid util9p_static_file_qid(struct util9p_static_file *self) { + assert(self); -/* file ***********************************************************************/ + return (struct lib9p_qid){ + .type = LIB9P_QT_FILE, + .vers = 1, + .path = self->pathnum, + }; +} +static uint32_t util9p_static_file_chio(struct util9p_static_file *self, struct lib9p_srv_ctx *ctx, bool, bool, bool) { + assert(self); + assert(ctx); + return 0; +} static inline size_t util9p_static_file_size(struct util9p_static_file *file) { assert(file); @@ -171,19 +159,8 @@ static inline size_t util9p_static_file_size(struct util9p_static_file *file) { return (size_t)((uintptr_t)file->data_end - (uintptr_t)file->data_start); } -static struct lib9p_qid util9p_static_file_qid(implements_lib9p_srv_file *_self) { - struct util9p_static_file *self = VCALL_SELF(struct util9p_static_file, implements_lib9p_srv_file, _self); - assert(self); - - return (struct lib9p_qid){ - .type = LIB9P_QT_FILE, - .vers = 1, - .path = self->pathnum, - }; -} -static struct lib9p_stat util9p_static_file_stat(implements_lib9p_srv_file *_self, struct lib9p_srv_ctx *ctx) { - struct util9p_static_file *self = VCALL_SELF(struct util9p_static_file, implements_lib9p_srv_file, _self); +static struct lib9p_stat util9p_static_file_stat(struct util9p_static_file *self, struct lib9p_srv_ctx *ctx) { assert(self); assert(ctx); @@ -205,12 +182,26 @@ static struct lib9p_stat util9p_static_file_stat(implements_lib9p_srv_file *_sel .file_last_modified_n_uid = self->m_num, }; } +static void util9p_static_file_wstat(struct util9p_static_file *self, struct lib9p_srv_ctx *ctx, + struct lib9p_stat) { + assert(self); + assert(ctx); + + lib9p_error(&ctx->basectx, LINUX_EROFS, "read-only part of filesystem"); +} +static void util9p_static_file_remove(struct util9p_static_file *self, struct lib9p_srv_ctx *ctx) { + assert(self); + assert(ctx); -static uint32_t util9p_static_file_pread(implements_lib9p_srv_file *_self, struct lib9p_srv_ctx *ctx, + lib9p_error(&ctx->basectx, LINUX_EROFS, "read-only part of filesystem"); +} + +LIB9P_SRV_NOTDIR(struct util9p_static_file, util9p_static_file) + +static uint32_t util9p_static_file_pread(struct util9p_static_file *self, struct lib9p_srv_ctx *ctx, void *buf, uint32_t byte_count, uint64_t byte_offset) { - struct util9p_static_file *self = VCALL_SELF(struct util9p_static_file, implements_lib9p_srv_file, _self); assert(self); assert(ctx); @@ -230,15 +221,13 @@ static uint32_t util9p_static_file_pread(implements_lib9p_srv_file *_self, struc return (uint32_t)(end_off-beg_off); } -struct lib9p_srv_file_vtable util9p_static_file_vtable = { - .free = util9p_static_common_free, - .qid = util9p_static_file_qid, - .chio = util9p_static_common_chio, - - .stat = util9p_static_file_stat, - .wstat = util9p_static_common_wstat, - .remove = util9p_static_common_remove, +static uint32_t util9p_static_file_pwrite(struct util9p_static_file *self, struct lib9p_srv_ctx *ctx, + void *LM_UNUSED(buf), + uint32_t LM_UNUSED(byte_count), + uint64_t LM_UNUSED(byte_offset)) { + assert(self); + assert(ctx); - .pread = util9p_static_file_pread, - .pwrite = NULL, -}; + lib9p_error(&ctx->basectx, LINUX_EROFS, "read-only part of filesystem"); + return 0; +} diff --git a/libdhcp/dhcp_client.c b/libdhcp/dhcp_client.c index ea6ed28..e50c5f3 100644 --- a/libdhcp/dhcp_client.c +++ b/libdhcp/dhcp_client.c @@ -1,6 +1,6 @@ /* libdhcp/dhcp_client.c - A DHCP client * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later * * ----------------------------------------------------------------------------- @@ -87,7 +87,6 @@ #include <string.h> /* for strlen(), memcpy(), memset() */ #include <libmisc/rand.h> -#include <libmisc/vcall.h> #include <libhw/generic/alarmclock.h> #define LOG_NAME DHCP @@ -125,8 +124,8 @@ enum requirement { struct dhcp_client { /* Static. */ - implements_net_iface *iface; - implements_net_packet_conn *sock; + lo_interface net_iface iface; + lo_interface net_packet_conn sock; struct net_eth_addr self_eth_addr; char *self_hostname; size_t self_id_len; @@ -313,7 +312,7 @@ static bool dhcp_client_send(struct dhcp_client *client, uint8_t msgtyp, const c switch (msgtyp) { case DHCP_MSGTYP_DISCOVER: case DHCP_MSGTYP_INFORM: case DHCP_MSGTYP_REQUEST: - secs = (VCALL(bootclock, get_time_ns) - client->time_ns_init)/NS_PER_S; + secs = (LO_CALL(bootclock, get_time_ns) - client->time_ns_init)/NS_PER_S; if (!secs) /* systemd's sd-dhcp-client.c asserts that some * servers are broken and require .secs to be @@ -462,7 +461,7 @@ static bool dhcp_client_send(struct dhcp_client *client, uint8_t msgtyp, const c * Send * \**********************************************************************/ debugf("state %s: sending DHCP %s", state_strs[client->state], dhcp_msgtyp_str(msgtyp)); - ssize_t r = VCALL(client->sock, sendto, scratch_msg, DHCP_MSG_BASE_SIZE + optlen, + ssize_t r = LO_CALL(client->sock, sendto, scratch_msg, DHCP_MSG_BASE_SIZE + optlen, client_broadcasts ? net_ip4_addr_broadcast : client->lease_server_id, DHCP_PORT_SERVER); if (r < 0) { debugf("error: sendto: %zd", r); @@ -579,7 +578,7 @@ static ssize_t dhcp_client_recv(struct dhcp_client *client, struct dhcp_recv_msg assert(client); ignore: - msg_len = VCALL(client->sock, recvfrom, &ret->raw, sizeof(ret->raw), &srv_addr, &srv_port); + msg_len = LO_CALL(client->sock, recvfrom, &ret->raw, sizeof(ret->raw), &srv_addr, &srv_port); if (msg_len < 0) /* msg_len is -errno */ return msg_len; @@ -702,9 +701,9 @@ static ssize_t dhcp_client_recv(struct dhcp_client *client, struct dhcp_recv_msg } /** @return true if there's a conflict, false if the addr appears to be unused */ -static bool dhcp_check_conflict(implements_net_packet_conn *sock, struct net_ip4_addr addr) { - assert(sock); - ssize_t v = VCALL(sock, sendto, "CHECK_IP_CONFLICT", 17, addr, 5000); +static bool dhcp_check_conflict(lo_interface net_packet_conn sock, struct net_ip4_addr addr) { + assert(!LO_IS_NULL(sock)); + ssize_t v = LO_CALL(sock, sendto, "CHECK_IP_CONFLICT", 17, addr, 5000); debugf("check_ip_conflict => %zd", v); return v != -NET_EARP_TIMEOUT; } @@ -745,7 +744,7 @@ static void dhcp_client_take_lease(struct dhcp_client *client, struct dhcp_recv_ infof(":: addr = "PRI_net_ip4_addr, ARG_net_ip4_addr(client->lease_client_addr)); infof(":: gateway_addr = "PRI_net_ip4_addr, ARG_net_ip4_addr(client->lease_auxdata.gateway_addr)); infof(":: subnet_mask = "PRI_net_ip4_addr, ARG_net_ip4_addr(client->lease_auxdata.subnet_mask)); - VCALL(client->iface, ifup, (struct net_iface_config){ + LO_CALL(client->iface, ifup, (struct net_iface_config){ .addr = client->lease_client_addr, .gateway_addr = client->lease_auxdata.gateway_addr, .subnet_mask = client->lease_auxdata.subnet_mask, @@ -772,11 +771,11 @@ static void dhcp_client_setstate(struct dhcp_client *client, switch (client->state) { case STATE_INIT: client->xid = rand_uint63n(UINT32_MAX); - client->time_ns_init = VCALL(bootclock, get_time_ns); + client->time_ns_init = LO_CALL(bootclock, get_time_ns); dhcp_client_setstate(client, STATE_SELECTING, DHCP_MSGTYP_DISCOVER, NULL, scratch_msg); break; case STATE_SELECTING: - VCALL(client->sock, set_read_deadline, client->time_ns_init+CONFIG_DHCP_SELECTING_NS); + LO_CALL(client->sock, set_read_deadline, client->time_ns_init+CONFIG_DHCP_SELECTING_NS); switch ((r = dhcp_client_recv(client, scratch_msg))) { case 0: switch (scratch_msg->option_dat[scratch_msg->options[DHCP_OPT_DHCP_MSG_TYPE].off]) { @@ -798,7 +797,7 @@ static void dhcp_client_setstate(struct dhcp_client *client, } break; case STATE_REQUESTING: - VCALL(client->sock, set_read_deadline, 0); + LO_CALL(client->sock, set_read_deadline, 0); switch ((r = dhcp_client_recv(client, scratch_msg))) { case 0: switch (scratch_msg->option_dat[scratch_msg->options[DHCP_OPT_DHCP_MSG_TYPE].off]) { @@ -825,7 +824,7 @@ static void dhcp_client_setstate(struct dhcp_client *client, } break; case STATE_BOUND: - VCALL(client->sock, set_read_deadline, client->lease_time_ns_t1); + LO_CALL(client->sock, set_read_deadline, client->lease_time_ns_t1); switch ((r = dhcp_client_recv(client, scratch_msg))) { case 0: /* discard */ @@ -840,14 +839,14 @@ static void dhcp_client_setstate(struct dhcp_client *client, break; case STATE_RENEWING: client->xid = rand_uint63n(UINT32_MAX); - client->time_ns_init = VCALL(bootclock, get_time_ns); + client->time_ns_init = LO_CALL(bootclock, get_time_ns); - VCALL(client->sock, set_read_deadline, client->lease_time_ns_t2); + LO_CALL(client->sock, set_read_deadline, client->lease_time_ns_t2); switch ((r = dhcp_client_recv(client, scratch_msg))) { case 0: switch (scratch_msg->option_dat[scratch_msg->options[DHCP_OPT_DHCP_MSG_TYPE].off]) { case DHCP_MSGTYP_NAK: - VCALL(client->iface, ifdown); + LO_CALL(client->iface, ifdown); dhcp_client_setstate(client, STATE_INIT, 0, NULL, scratch_msg); break; case DHCP_MSGTYP_ACK: @@ -868,12 +867,12 @@ static void dhcp_client_setstate(struct dhcp_client *client, } break; case STATE_REBINDING: - VCALL(client->sock, set_read_deadline, client->lease_time_ns_end); + LO_CALL(client->sock, set_read_deadline, client->lease_time_ns_end); switch ((r = dhcp_client_recv(client, scratch_msg))) { case 0: switch (scratch_msg->option_dat[scratch_msg->options[DHCP_OPT_DHCP_MSG_TYPE].off]) { case DHCP_MSGTYP_NAK: - VCALL(client->iface, ifdown); + LO_CALL(client->iface, ifdown); dhcp_client_setstate(client, STATE_BOUND, 0, NULL, scratch_msg); break; case DHCP_MSGTYP_ACK: @@ -885,7 +884,7 @@ static void dhcp_client_setstate(struct dhcp_client *client, } break; case -NET_ERECV_TIMEOUT: - VCALL(client->iface, ifdown); + LO_CALL(client->iface, ifdown); dhcp_client_setstate(client, STATE_BOUND, 0, NULL, scratch_msg); break; default: @@ -902,9 +901,9 @@ static void dhcp_client_setstate(struct dhcp_client *client, } } -[[noreturn]] void dhcp_client_main(implements_net_iface *iface, +[[noreturn]] void dhcp_client_main(lo_interface net_iface iface, char *self_hostname) { - assert(iface); + assert(!LO_IS_NULL(iface)); /* Even though a client ID is optional and not meaningful for * us (the best we can do is to duplicate .chaddr), systemd's @@ -912,14 +911,14 @@ static void dhcp_client_setstate(struct dhcp_client *client, * require it to be set. */ struct {uint8_t typ; struct net_eth_addr dat;} client_id = { DHCP_HTYPE_ETHERNET, - VCALL(iface, hwaddr), + LO_CALL(iface, hwaddr), }; struct dhcp_client client = { /* Static. */ .iface = iface, - .sock = VCALL(iface, udp_conn, DHCP_PORT_CLIENT), - .self_eth_addr = VCALL(iface, hwaddr), + .sock = LO_CALL(iface, udp_conn, DHCP_PORT_CLIENT), + .self_eth_addr = LO_CALL(iface, hwaddr), .self_hostname = self_hostname, .self_id_len = sizeof(client_id), .self_id_dat = &client_id, @@ -927,7 +926,7 @@ static void dhcp_client_setstate(struct dhcp_client *client, /* Mutable. */ .state = STATE_INIT, }; - assert(client.sock); + assert(!LO_IS_NULL(client.sock)); struct dhcp_recv_msg scratch; diff --git a/libdhcp/include/libdhcp/client.h b/libdhcp/include/libdhcp/client.h index c3cb76f..f81e9b1 100644 --- a/libdhcp/include/libdhcp/client.h +++ b/libdhcp/include/libdhcp/client.h @@ -1,6 +1,6 @@ /* libdhcp/client.h - A DHCP client * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later * * ----------------------------------------------------------------------------- @@ -69,7 +69,7 @@ #include <libhw/generic/net.h> -[[noreturn]] void dhcp_client_main(implements_net_iface *iface, +[[noreturn]] void dhcp_client_main(lo_interface net_iface iface, char *self_hostname); #endif /* _LIBDHCP_CLIENT_H_ */ diff --git a/libhw/host_alarmclock.c b/libhw/host_alarmclock.c index 5f7e494..19ece7c 100644 --- a/libhw/host_alarmclock.c +++ b/libhw/host_alarmclock.c @@ -1,62 +1,27 @@ /* libhw/host_alarmclock.c - <libhw/generic/alarmclock.h> implementation for POSIX hosts * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later */ #include <errno.h> #include <error.h> #include <signal.h> -#include <time.h> #include <libcr/coroutine.h> #include <libmisc/assert.h> -#include <libmisc/vcall.h> #define IMPLEMENTATION_FOR_LIBHW_GENERIC_ALARMCLOCK_H YES #include <libhw/generic/alarmclock.h> +#define IMPLEMENTATION_FOR_LIBHW_HOST_ALARMCLOCK_H YES +#include <libhw/host_alarmclock.h> + #include "host_util.h" /* for host_sigrt_alloc(), ns_to_host_ns_time() */ -/* Types **********************************************************************/ - -struct hostclock { - implements_alarmclock; - bool initialized; - clockid_t clock_id; - timer_t timer_id; - struct alarmclock_trigger *queue; -}; - -/* Globals ********************************************************************/ - -static uint64_t hostclock_get_time_ns(implements_alarmclock *self); -static bool hostclock_add_trigger(implements_alarmclock *self, - struct alarmclock_trigger *trigger, - uint64_t fire_at_ns, - void (*cb)(void *), - void *cb_arg); -static void hostclock_del_trigger(implements_alarmclock *self, - struct alarmclock_trigger *trigger); - -static struct alarmclock_vtable hostclock_vtable = { - .get_time_ns = hostclock_get_time_ns, - .add_trigger = hostclock_add_trigger, - .del_trigger = hostclock_del_trigger, -}; - -static struct hostclock clock_monotonic = { - .vtable = &hostclock_vtable, - .clock_id = CLOCK_MONOTONIC, -}; - -implements_alarmclock *bootclock = &clock_monotonic; - -/* Main implementation ********************************************************/ - -static uint64_t hostclock_get_time_ns(implements_alarmclock *_alarmclock) { - struct hostclock *alarmclock = - VCALL_SELF(struct hostclock, implements_alarmclock, _alarmclock); +LO_IMPLEMENTATION_C(alarmclock, struct hostclock, hostclock, static) + +static uint64_t hostclock_get_time_ns(struct hostclock *alarmclock) { assert(alarmclock); struct timespec ts; @@ -91,13 +56,11 @@ static void hostclock_handle_sig_alarm(int LM_UNUSED(sig), siginfo_t *info, void } } -static bool hostclock_add_trigger(implements_alarmclock *_alarmclock, - struct alarmclock_trigger *trigger, - uint64_t fire_at_ns, - void (*cb)(void *), - void *cb_arg) { - struct hostclock *alarmclock = - VCALL_SELF(struct hostclock, implements_alarmclock, _alarmclock); +static bool hostclock_add_trigger(struct hostclock *alarmclock, + struct alarmclock_trigger *trigger, + uint64_t fire_at_ns, + void (*cb)(void *), + void *cb_arg) { assert(alarmclock); assert(trigger); assert(fire_at_ns); @@ -148,11 +111,8 @@ static bool hostclock_add_trigger(implements_alarmclock *_alarmclock, return false; } -static void hostclock_del_trigger(implements_alarmclock *_alarmclock, - struct alarmclock_trigger *trigger) { - struct hostclock *alarmclock = - VCALL_SELF(struct hostclock, implements_alarmclock, _alarmclock); - +static void hostclock_del_trigger(struct hostclock *alarmclock, + struct alarmclock_trigger *trigger) { assert(alarmclock); assert(trigger); diff --git a/libhw/host_include/libhw/host_alarmclock.h b/libhw/host_include/libhw/host_alarmclock.h new file mode 100644 index 0000000..89df68a --- /dev/null +++ b/libhw/host_include/libhw/host_alarmclock.h @@ -0,0 +1,27 @@ +/* libhw/host_alarmclock.h - <libhw/generic/alarmclock.h> implementation for hosted glibc + * + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef _LIBHW_HOST_ALARMCLOCK_H_ +#define _LIBHW_HOST_ALARMCLOCK_H_ + +#include <stdbool.h> /* for bool */ +#include <time.h> /* for clockid_t, timer_t */ + +#include <libmisc/private.h> +#include <libhw/generic/alarmclock.h> + +struct hostclock { + clockid_t clock_id; + + BEGIN_PRIVATE(LIBHW_HOST_ALARMCLOCK_H) + bool initialized; + timer_t timer_id; + struct alarmclock_trigger *queue; + END_PRIVATE(LIBHW_HOST_ALARMCLOCK_H) +}; +LO_IMPLEMENTATION_H(alarmclock, struct hostclock, hostclock) + +#endif /* _LIBHW_HOST_ALARMCLOCK_H_ */ diff --git a/libhw/host_include/libhw/host_net.h b/libhw/host_include/libhw/host_net.h index bfef5c9..fced229 100644 --- a/libhw/host_include/libhw/host_net.h +++ b/libhw/host_include/libhw/host_net.h @@ -1,6 +1,6 @@ /* libhw/host_net.h - <libhw/generic/net.h> implementation for hosted glibc * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later */ @@ -14,33 +14,30 @@ #include <libhw/generic/net.h> struct _hostnet_tcp_conn { - implements_net_stream_conn; - BEGIN_PRIVATE(LIBHW_HOST_NET_H) int fd; uint64_t read_deadline_ns; END_PRIVATE(LIBHW_HOST_NET_H) }; +LO_IMPLEMENTATION_H(net_stream_conn, struct _hostnet_tcp_conn, hostnet_tcp) struct hostnet_tcp_listener { - implements_net_stream_listener; - BEGIN_PRIVATE(LIBHW_HOST_NET_H) int fd; struct _hostnet_tcp_conn active_conn; END_PRIVATE(LIBHW_HOST_NET_H) }; +LO_IMPLEMENTATION_H(net_stream_listener, struct hostnet_tcp_listener, hostnet_tcplist) void hostnet_tcp_listener_init(struct hostnet_tcp_listener *self, uint16_t port); struct hostnet_udp_conn { - implements_net_packet_conn; - BEGIN_PRIVATE(LIBHW_HOST_NET_H) int fd; uint64_t read_deadline_ns; END_PRIVATE(LIBHW_HOST_NET_H) }; +LO_IMPLEMENTATION_H(net_packet_conn, struct hostnet_udp_conn, hostnet_udp) void hostnet_udp_conn_init(struct hostnet_udp_conn *self, uint16_t port); diff --git a/libhw/host_net.c b/libhw/host_net.c index bf79480..505c7dc 100644 --- a/libhw/host_net.c +++ b/libhw/host_net.c @@ -21,7 +21,7 @@ #include <libcr/coroutine.h> #include <libmisc/assert.h> #include <libmisc/macro.h> -#include <libmisc/vcall.h> +#include <libobj/obj.h> #include <libhw/generic/alarmclock.h> @@ -30,6 +30,10 @@ #include "host_util.h" /* for host_sigrt_alloc(), ns_to_host_us_time() */ +LO_IMPLEMENTATION_C(net_stream_conn, struct _hostnet_tcp_conn, hostnet_tcp, static) +LO_IMPLEMENTATION_C(net_stream_listener, struct hostnet_tcp_listener, hostnet_tcplist, static) +LO_IMPLEMENTATION_C(net_packet_conn, struct hostnet_udp_conn, hostnet_udp, static) + /* common *********************************************************************/ static int hostnet_sig_io = 0; @@ -109,25 +113,6 @@ static inline ssize_t hostnet_map_negerrno(ssize_t v, enum hostnet_timeout_op op /* TCP init() ( AKA socket(3) + listen(3) )************************************/ -static implements_net_stream_conn *hostnet_tcplist_accept(implements_net_stream_listener *); -static int hostnet_tcplist_close(implements_net_stream_listener *); -static void hostnet_tcp_set_read_deadline(implements_net_stream_conn *conn, uint64_t ts_ns); -static ssize_t hostnet_tcp_read(implements_net_stream_conn *conn, void *buf, size_t count); -static ssize_t hostnet_tcp_write(implements_net_stream_conn *conn, void *buf, size_t count); -static int hostnet_tcp_close(implements_net_stream_conn *conn, bool rd, bool wr); - -static struct net_stream_listener_vtable hostnet_tcp_listener_vtable = { - .accept = hostnet_tcplist_accept, - .close = hostnet_tcplist_close, -}; - -static struct net_stream_conn_vtable hostnet_tcp_conn_vtable = { - .set_read_deadline = hostnet_tcp_set_read_deadline, - .read = hostnet_tcp_read, - .write = hostnet_tcp_write, - .close = hostnet_tcp_close, -}; - void hostnet_tcp_listener_init(struct hostnet_tcp_listener *self, uint16_t port) { int listenerfd; union { @@ -151,7 +136,6 @@ void hostnet_tcp_listener_init(struct hostnet_tcp_listener *self, uint16_t port) if (listen(listenerfd, 0) < 0) error(1, errno, "listen(fd=%d)", listenerfd); - self->vtable = &hostnet_tcp_listener_vtable; self->fd = listenerfd; } @@ -175,9 +159,7 @@ static void *hostnet_pthread_accept(void *_args) { return NULL; } -static implements_net_stream_conn *hostnet_tcplist_accept(implements_net_stream_listener *_listener) { - struct hostnet_tcp_listener *listener = - VCALL_SELF(struct hostnet_tcp_listener, implements_net_stream_listener, _listener); +static lo_interface net_stream_conn hostnet_tcplist_accept(struct hostnet_tcp_listener *listener) { assert(listener); int ret_connfd; @@ -188,22 +170,19 @@ static implements_net_stream_conn *hostnet_tcplist_accept(implements_net_stream_ .ret_connfd = &ret_connfd, }; if (RUN_PTHREAD(hostnet_pthread_accept, &args)) - return NULL; + return LO_NULL(net_stream_conn); if (ret_connfd < 0) - return NULL; + return LO_NULL(net_stream_conn); - listener->active_conn.vtable = &hostnet_tcp_conn_vtable; listener->active_conn.fd = ret_connfd; listener->active_conn.read_deadline_ns = 0; - return &listener->active_conn; + return lo_box_hostnet_tcp_as_net_stream_conn(&listener->active_conn); } /* TCP listener close() *******************************************************/ -static int hostnet_tcplist_close(implements_net_stream_listener *_listener) { - struct hostnet_tcp_listener *listener = - VCALL_SELF(struct hostnet_tcp_listener, implements_net_stream_listener, _listener); +static int hostnet_tcplist_close(struct hostnet_tcp_listener *listener) { assert(listener); return hostnet_map_negerrno(shutdown(listener->fd, SHUT_RDWR) ? -errno : 0, OP_NONE); @@ -211,9 +190,7 @@ static int hostnet_tcplist_close(implements_net_stream_listener *_listener) { /* TCP read() *****************************************************************/ -static void hostnet_tcp_set_read_deadline(implements_net_stream_conn *_conn, uint64_t ts_ns) { - struct _hostnet_tcp_conn *conn = - VCALL_SELF(struct _hostnet_tcp_conn, implements_net_stream_conn, _conn); +static void hostnet_tcp_set_read_deadline(struct _hostnet_tcp_conn *conn, uint64_t ts_ns) { assert(conn); conn->read_deadline_ns = ts_ns; @@ -250,9 +227,7 @@ static void *hostnet_pthread_read(void *_args) { return NULL; } -static ssize_t hostnet_tcp_read(implements_net_stream_conn *_conn, void *buf, size_t count) { - struct _hostnet_tcp_conn *conn = - VCALL_SELF(struct _hostnet_tcp_conn, implements_net_stream_conn, _conn); +static ssize_t hostnet_tcp_read(struct _hostnet_tcp_conn *conn, void *buf, size_t count) { assert(conn); ssize_t ret; @@ -267,7 +242,7 @@ static ssize_t hostnet_tcp_read(implements_net_stream_conn *_conn, void *buf, si .ret = &ret, }; if (conn->read_deadline_ns) { - uint64_t now_ns = VCALL(bootclock, get_time_ns); + uint64_t now_ns = LO_CALL(bootclock, get_time_ns); if (conn->read_deadline_ns < now_ns) return -NET_ERECV_TIMEOUT; args.timeout = ns_to_host_us_time(conn->read_deadline_ns-now_ns); @@ -310,9 +285,7 @@ static void *hostnet_pthread_write(void *_args) { return NULL; } -static ssize_t hostnet_tcp_write(implements_net_stream_conn *_conn, void *buf, size_t count) { - struct _hostnet_tcp_conn *conn = - VCALL_SELF(struct _hostnet_tcp_conn, implements_net_stream_conn, _conn); +static ssize_t hostnet_tcp_write(struct _hostnet_tcp_conn *conn, void *buf, size_t count) { assert(conn); ssize_t ret; @@ -333,9 +306,7 @@ static ssize_t hostnet_tcp_write(implements_net_stream_conn *_conn, void *buf, s /* TCP close() ****************************************************************/ -static int hostnet_tcp_close(implements_net_stream_conn *_conn, bool rd, bool wr) { - struct _hostnet_tcp_conn *conn = - VCALL_SELF(struct _hostnet_tcp_conn, implements_net_stream_conn, _conn); +static int hostnet_tcp_close(struct _hostnet_tcp_conn *conn, bool rd, bool wr) { assert(conn); int how; @@ -352,21 +323,6 @@ static int hostnet_tcp_close(implements_net_stream_conn *_conn, bool rd, bool wr /* UDP init() *****************************************************************/ -static void hostnet_udp_set_read_deadline(implements_net_packet_conn *self, - uint64_t ts_ns); -static ssize_t hostnet_udp_sendto(implements_net_packet_conn *self, void *buf, size_t len, - struct net_ip4_addr addr, uint16_t port); -static ssize_t hostnet_udp_recvfrom(implements_net_packet_conn *self, void *buf, size_t len, - struct net_ip4_addr *ret_addr, uint16_t *ret_port); -static int hostnet_udp_close(implements_net_packet_conn *self); - -static struct net_packet_conn_vtable hostnet_udp_conn_vtable = { - .set_read_deadline = hostnet_udp_set_read_deadline, - .sendto = hostnet_udp_sendto, - .recvfrom = hostnet_udp_recvfrom, - .close = hostnet_udp_close, -}; - void hostnet_udp_conn_init(struct hostnet_udp_conn *self, uint16_t port) { int fd; union { @@ -385,7 +341,6 @@ void hostnet_udp_conn_init(struct hostnet_udp_conn *self, uint16_t port) { if (bind(fd, &addr.gen, sizeof addr) < 0) error(1, errno, "bind"); - self->vtable = &hostnet_udp_conn_vtable; self->fd = fd; self->read_deadline_ns = 0; } @@ -427,10 +382,8 @@ static void *hostnet_pthread_sendto(void *_args) { return NULL; } -static ssize_t hostnet_udp_sendto(implements_net_packet_conn *_conn, void *buf, size_t count, +static ssize_t hostnet_udp_sendto(struct hostnet_udp_conn *conn, void *buf, size_t count, struct net_ip4_addr node, uint16_t port) { - struct hostnet_udp_conn *conn = - VCALL_SELF(struct hostnet_udp_conn, implements_net_packet_conn, _conn); assert(conn); ssize_t ret; @@ -453,10 +406,8 @@ static ssize_t hostnet_udp_sendto(implements_net_packet_conn *_conn, void *buf, /* UDP recvfrom() *************************************************************/ -static void hostnet_udp_set_read_deadline(implements_net_packet_conn *_conn, +static void hostnet_udp_set_read_deadline(struct hostnet_udp_conn *conn, uint64_t ts_ns) { - struct hostnet_udp_conn *conn = - VCALL_SELF(struct hostnet_udp_conn, implements_net_packet_conn, _conn); assert(conn); conn->read_deadline_ns = ts_ns; @@ -514,10 +465,8 @@ static void *hostnet_pthread_recvfrom(void *_args) { return NULL; } -static ssize_t hostnet_udp_recvfrom(implements_net_packet_conn *_conn, void *buf, size_t count, +static ssize_t hostnet_udp_recvfrom(struct hostnet_udp_conn *conn, void *buf, size_t count, struct net_ip4_addr *ret_node, uint16_t *ret_port) { - struct hostnet_udp_conn *conn = - VCALL_SELF(struct hostnet_udp_conn, implements_net_packet_conn, _conn); assert(conn); ssize_t ret; @@ -534,7 +483,7 @@ static ssize_t hostnet_udp_recvfrom(implements_net_packet_conn *_conn, void *buf .ret_port = ret_port, }; if (conn->read_deadline_ns) { - uint64_t now_ns = VCALL(bootclock, get_time_ns); + uint64_t now_ns = LO_CALL(bootclock, get_time_ns); if (conn->read_deadline_ns < now_ns) return -NET_ERECV_TIMEOUT; args.timeout = ns_to_host_us_time(conn->read_deadline_ns-now_ns); @@ -549,9 +498,7 @@ static ssize_t hostnet_udp_recvfrom(implements_net_packet_conn *_conn, void *buf /* UDP close() ****************************************************************/ -static int hostnet_udp_close(implements_net_packet_conn *_conn) { - struct hostnet_udp_conn *conn = - VCALL_SELF(struct hostnet_udp_conn, implements_net_packet_conn, _conn); +static int hostnet_udp_close(struct hostnet_udp_conn *conn) { assert(conn); return hostnet_map_negerrno(close(conn->fd) ? -errno : 0, OP_NONE); diff --git a/libhw/rp2040_hwspi.c b/libhw/rp2040_hwspi.c index 47dfc97..23f3e8c 100644 --- a/libhw/rp2040_hwspi.c +++ b/libhw/rp2040_hwspi.c @@ -1,6 +1,6 @@ /* libhw/rp2040_hwspi.c - <libhw/generic/spi.h> implementation for the RP2040's ARM Primecell SSP (PL022) * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later */ @@ -8,16 +8,11 @@ #include <hardware/gpio.h> /* pico-sdk:hardware_gpio */ #include <libmisc/assert.h> -#include <libmisc/vcall.h> #define IMPLEMENTATION_FOR_LIBHW_RP2040_HWSPI_H YES #include <libhw/rp2040_hwspi.h> -static void rp2040_hwspi_readwritev(implements_spi *, const struct bidi_iovec *iov, int iovcnt); - -struct spi_vtable rp2040_hwspi_vtable = { - .readwritev = rp2040_hwspi_readwritev, -}; +LO_IMPLEMENTATION_C(spi, struct rp2040_hwspi, rp2040_hwspi, static) void _rp2040_hwspi_init(struct rp2040_hwspi *self, enum rp2040_hwspi_instance inst_num, @@ -84,13 +79,11 @@ void _rp2040_hwspi_init(struct rp2040_hwspi *self, gpio_put(pin_cs, 1); /* Return. */ - self->vtable = &rp2040_hwspi_vtable; self->inst = inst; self->pin_cs = pin_cs; } -static void rp2040_hwspi_readwritev(implements_spi *_self, const struct bidi_iovec *iov, int iovcnt) { - struct rp2040_hwspi *self = VCALL_SELF(struct rp2040_hwspi, implements_spi, _self); +static void rp2040_hwspi_readwritev(struct rp2040_hwspi *self, const struct bidi_iovec *iov, int iovcnt) { assert(self); spi_inst_t *inst = self->inst; diff --git a/libhw/rp2040_hwtimer.c b/libhw/rp2040_hwtimer.c index c8c281e..ada6246 100644 --- a/libhw/rp2040_hwtimer.c +++ b/libhw/rp2040_hwtimer.c @@ -1,6 +1,6 @@ /* libhw/rp2040_hwtimer.c - <libhw/generic/alarmclock.h> implementation for the RP2040's hardware timer * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later */ @@ -9,10 +9,10 @@ #include <libcr/coroutine.h> #include <libmisc/assert.h> -#include <libmisc/vcall.h> #define IMPLEMENTATION_FOR_LIBHW_GENERIC_ALARMCLOCK_H YES #include <libhw/generic/alarmclock.h> + #include <libhw/rp2040_hwtimer.h> /******************************************************************************/ @@ -23,48 +23,32 @@ void add_alarm_at(void) {}; /* Types **********************************************************************/ struct rp2040_hwtimer { - implements_alarmclock; enum rp2040_hwalarm_instance alarm_num; bool initialized; struct alarmclock_trigger *queue; }; +LO_IMPLEMENTATION_H(alarmclock, struct rp2040_hwtimer, rp2040_hwtimer); +LO_IMPLEMENTATION_C(alarmclock, struct rp2040_hwtimer, rp2040_hwtimer, static); /* Globals ********************************************************************/ -static uint64_t rp2040_hwtimer_get_time_ns(implements_alarmclock *self); -static bool rp2040_hwtimer_add_trigger(implements_alarmclock *self, - struct alarmclock_trigger *trigger, - uint64_t fire_at_ns, - void (*cb)(void *), - void *cb_arg); -static void rp2040_hwtimer_del_trigger(implements_alarmclock *self, - struct alarmclock_trigger *trigger); - -static struct alarmclock_vtable rp2040_hwtimer_vtable = { - .get_time_ns = rp2040_hwtimer_get_time_ns, - .add_trigger = rp2040_hwtimer_add_trigger, - .del_trigger = rp2040_hwtimer_del_trigger, -}; - static struct rp2040_hwtimer hwtimers[] = { - { .vtable = &rp2040_hwtimer_vtable, .alarm_num = 0 }, - { .vtable = &rp2040_hwtimer_vtable, .alarm_num = 1 }, - { .vtable = &rp2040_hwtimer_vtable, .alarm_num = 2 }, - { .vtable = &rp2040_hwtimer_vtable, .alarm_num = 3 }, + { .alarm_num = 0 }, + { .alarm_num = 1 }, + { .alarm_num = 2 }, + { .alarm_num = 3 }, }; static_assert(sizeof(hwtimers)/sizeof(hwtimers[0]) == _RP2040_HWALARM_NUM); -implements_alarmclock *bootclock = &hwtimers[0]; - /* Main implementation ********************************************************/ -implements_alarmclock *rp2040_hwtimer(enum rp2040_hwalarm_instance alarm_num) { +lo_interface alarmclock rp2040_hwtimer(enum rp2040_hwalarm_instance alarm_num) { assert(alarm_num < _RP2040_HWALARM_NUM); - return &hwtimers[alarm_num]; + return lo_box_rp2040_hwtimer_as_alarmclock(&hwtimers[alarm_num]); } -static uint64_t rp2040_hwtimer_get_time_ns(implements_alarmclock *) { +static uint64_t rp2040_hwtimer_get_time_ns(struct rp2040_hwtimer *) { return timer_time_us_64(timer_hw) * (NS_PER_S/US_PER_S); } @@ -93,13 +77,11 @@ static void rp2040_hwtimer_intrhandler(void) { timer_hw->alarm[alarm_num] = (uint32_t)NS_TO_US_ROUNDUP(alarmclock->queue->fire_at_ns); } -static bool rp2040_hwtimer_add_trigger(implements_alarmclock *_alarmclock, +static bool rp2040_hwtimer_add_trigger(struct rp2040_hwtimer *alarmclock, struct alarmclock_trigger *trigger, uint64_t fire_at_ns, void (*cb)(void *), void *cb_arg) { - struct rp2040_hwtimer *alarmclock = - VCALL_SELF(struct rp2040_hwtimer, implements_alarmclock, _alarmclock); assert(alarmclock); assert(trigger); assert(fire_at_ns); @@ -149,10 +131,8 @@ static bool rp2040_hwtimer_add_trigger(implements_alarmclock *_alarmclock, return false; } -static void rp2040_hwtimer_del_trigger(implements_alarmclock *_alarmclock, +static void rp2040_hwtimer_del_trigger(struct rp2040_hwtimer *alarmclock, struct alarmclock_trigger *trigger) { - struct rp2040_hwtimer *alarmclock = - VCALL_SELF(struct rp2040_hwtimer, implements_alarmclock, _alarmclock); assert(alarmclock); assert(trigger); diff --git a/libhw/rp2040_include/libhw/rp2040_hwspi.h b/libhw/rp2040_include/libhw/rp2040_hwspi.h index 7c4991b..b1abe0c 100644 --- a/libhw/rp2040_include/libhw/rp2040_hwspi.h +++ b/libhw/rp2040_include/libhw/rp2040_hwspi.h @@ -1,6 +1,6 @@ /* libhw/rp2040_hwspi.h - <libhw/generic/spi.h> implementation for the RP2040's ARM Primecell SSP (PL022) * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later */ @@ -19,13 +19,12 @@ enum rp2040_hwspi_instance { }; struct rp2040_hwspi { - implements_spi; - BEGIN_PRIVATE(LIBHW_RP2040_HWSPI_H) void /*spi_inst_t*/ *inst; uint pin_cs; END_PRIVATE(LIBHW_RP2040_HWSPI_H) }; +LO_IMPLEMENTATION_H(spi, struct rp2040_hwspi, rp2040_hwspi) /** * Initialize an instance of `struct rp2040_hwspi`. diff --git a/libhw/rp2040_include/libhw/rp2040_hwtimer.h b/libhw/rp2040_include/libhw/rp2040_hwtimer.h index 6710ab1..40e4172 100644 --- a/libhw/rp2040_include/libhw/rp2040_hwtimer.h +++ b/libhw/rp2040_include/libhw/rp2040_hwtimer.h @@ -1,6 +1,6 @@ /* libhw/rp2040_hwtimer.h - <libhw/generic/alarmclock.h> implementation for the RP2040's hardware timer * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later */ @@ -10,8 +10,7 @@ #include <libhw/generic/alarmclock.h> /** - * The RP2040 has one system "timer" (which we also use for - * ./rp2040_bootclock.c) with 4 alarm interrupts. + * The RP2040 has one system "timer" with 4 alarm interrupts. */ enum rp2040_hwalarm_instance { RP2040_HWALARM_0 = 0, @@ -21,6 +20,6 @@ enum rp2040_hwalarm_instance { _RP2040_HWALARM_NUM, }; -implements_alarmclock *rp2040_hwtimer(enum rp2040_hwalarm_instance alarm_num); +lo_interface alarmclock rp2040_hwtimer(enum rp2040_hwalarm_instance alarm_num); #endif /* _LIBHW_RP2040_HWTIMER_H_ */ diff --git a/libhw/rp2040_include/libhw/w5500.h b/libhw/rp2040_include/libhw/w5500.h index 3cae620..ab9f50e 100644 --- a/libhw/rp2040_include/libhw/w5500.h +++ b/libhw/rp2040_include/libhw/w5500.h @@ -1,6 +1,6 @@ /* libhw/w5500.h - <libhw/generic/net.h> implementation for the WIZnet W5500 chip * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later */ @@ -20,11 +20,8 @@ CR_CHAN_DECLARE(_w5500_sockintr_ch, uint8_t) struct _w5500_socket { - /* const-after-init */ - implements_net_stream_listener implements_net_stream_listener; - implements_net_stream_conn implements_net_stream_conn; - implements_net_packet_conn implements_net_packet_conn; BEGIN_PRIVATE(LIBHW_W5500_H) + /* const-after-init */ uint8_t socknum; /* mutable */ @@ -43,12 +40,14 @@ struct _w5500_socket { END_PRIVATE(LIBHW_W5500_H) }; +LO_IMPLEMENTATION_H(net_stream_listener, struct _w5500_socket, w5500_tcplist) +LO_IMPLEMENTATION_H(net_stream_conn, struct _w5500_socket, w5500_tcp) +LO_IMPLEMENTATION_H(net_packet_conn, struct _w5500_socket, w5500_udp) struct w5500 { - /* const-after-init */ - implements_net_iface; BEGIN_PRIVATE(LIBHW_W5500_H) - implements_spi *spidev; + /* const-after-init */ + lo_interface spi spidev; uint pin_intr; uint pin_reset; struct net_eth_addr hwaddr; @@ -61,6 +60,7 @@ struct w5500 { cr_mutex_t mu; END_PRIVATE(LIBHW_W5500_H) }; +LO_IMPLEMENTATION_H(net_iface, struct w5500, w5500_if) /** * Initialize a WIZnet W5500 Ethernet-and-TCP/IP-offload chip. @@ -82,7 +82,7 @@ struct w5500 { _w5500_init(self, spi, pin_intr, pin_reset, eth_addr); \ } while (0) void _w5500_init(struct w5500 *self, - implements_spi *spi, uint pin_intr, uint pin_reset, + lo_interface spi spi, uint pin_intr, uint pin_reset, struct net_eth_addr addr); /** diff --git a/libhw/w5500.c b/libhw/w5500.c index cebcf8e..dfe169f 100644 --- a/libhw/w5500.c +++ b/libhw/w5500.c @@ -1,6 +1,6 @@ /* libhw/w5500.c - <libhw/generic/net.h> implementation for the WIZnet W5500 chip * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later * * ----------------------------------------------------------------------------- @@ -74,7 +74,6 @@ #include <hardware/gpio.h> /* pico-sdk:hardware_gpio */ #include <libcr/coroutine.h> /* for cr_yield() */ -#include <libmisc/vcall.h> /* for VCALL_SELF() */ #include <libhw/generic/alarmclock.h> /* for sleep_*() */ @@ -124,61 +123,12 @@ static const char *w5500_state_str(uint8_t state) { } } -/* vtables ********************************************************************/ - -/* iface */ -static struct net_eth_addr w5500_if_hwaddr (implements_net_iface *); -static void w5500_if_up (implements_net_iface *, struct net_iface_config); -static void w5500_if_down (implements_net_iface *); -static implements_net_stream_listener *w5500_if_tcp_listen (implements_net_iface *, uint16_t local_port); -static implements_net_stream_conn *w5500_if_tcp_dial (implements_net_iface *, struct net_ip4_addr, uint16_t remote_port); -static implements_net_packet_conn *w5500_if_udp_conn (implements_net_iface *, uint16_t local_port); - -/* stream_listener */ -static implements_net_stream_conn *w5500_tcplist_accept(implements_net_stream_listener *); -static int w5500_tcplist_close (implements_net_stream_listener *); - -/* stream_conn */ -static void w5500_tcp_set_read_deadline (implements_net_stream_conn *, uint64_t ns); -static ssize_t w5500_tcp_read (implements_net_stream_conn *, void *, size_t); -static ssize_t w5500_tcp_write (implements_net_stream_conn *, void *, size_t); -static int w5500_tcp_close (implements_net_stream_conn *, bool rd, bool wr); - -/* packet_conn */ -static void w5500_udp_set_read_deadline (implements_net_packet_conn *, uint64_t ns); -static ssize_t w5500_udp_recvfrom (implements_net_packet_conn *, void *, size_t, struct net_ip4_addr *, uint16_t *); -static ssize_t w5500_udp_sendto (implements_net_packet_conn *, void *, size_t, struct net_ip4_addr, uint16_t); -static int w5500_udp_close (implements_net_packet_conn *); - -/* tables */ - -static struct net_iface_vtable w5500_iface_vtable = { - .hwaddr = w5500_if_hwaddr, - .ifup = w5500_if_up, - .ifdown = w5500_if_down, - .tcp_listen = w5500_if_tcp_listen, - .tcp_dial = w5500_if_tcp_dial, - .udp_conn = w5500_if_udp_conn, -}; - -static struct net_stream_listener_vtable w5500_tcp_listener_vtable = { - .accept = w5500_tcplist_accept, - .close = w5500_tcplist_close, -}; - -static struct net_stream_conn_vtable w5500_tcp_conn_vtable = { - .set_read_deadline = w5500_tcp_set_read_deadline, - .read = w5500_tcp_read, - .write = w5500_tcp_write, - .close = w5500_tcp_close, -}; - -static struct net_packet_conn_vtable w5500_udp_conn_vtable = { - .set_read_deadline = w5500_udp_set_read_deadline, - .recvfrom = w5500_udp_recvfrom, - .sendto = w5500_udp_sendto, - .close = w5500_udp_close, -}; +/* libobj *********************************************************************/ + +LO_IMPLEMENTATION_C(net_stream_listener, struct _w5500_socket, w5500_tcplist, static) +LO_IMPLEMENTATION_C(net_stream_conn, struct _w5500_socket, w5500_tcp, static) +LO_IMPLEMENTATION_C(net_packet_conn, struct _w5500_socket, w5500_udp, static) +LO_IMPLEMENTATION_C(net_iface, struct w5500, w5500_if, static) /* mid-level utilities ********************************************************/ @@ -311,9 +261,6 @@ static inline void w5500_socket_close(struct _w5500_socket *socket) { } #define ASSERT_SELF(_iface, _mode) \ - struct _w5500_socket *socket = \ - VCALL_SELF(struct _w5500_socket, \ - implements_net_##_iface, _socket); \ assert(socket); \ uint8_t socknum = socket->socknum; \ assert(socknum < 8); \ @@ -333,15 +280,14 @@ static void w5500_intrhandler(uint gpio, uint32_t LM_UNUSED(event_mask)) { } void _w5500_init(struct w5500 *chip, - implements_spi *spi, uint pin_intr, uint pin_reset, + lo_interface spi spi, uint pin_intr, uint pin_reset, struct net_eth_addr addr) { assert(chip); - assert(spi); + assert(!LO_IS_NULL(spi)); /* Initialize the data structures. */ *chip = (struct w5500){ /* const-after-init */ - .implements_net_iface = { .vtable = &w5500_iface_vtable }, .spidev = spi, .pin_intr = pin_intr, .pin_reset = pin_reset, @@ -353,9 +299,6 @@ void _w5500_init(struct w5500 *chip, for (uint8_t i = 0; i < 8; i++) { chip->sockets[i] = (struct _w5500_socket){ /* const-after-init */ - .implements_net_stream_listener = { .vtable = &w5500_tcp_listener_vtable }, - .implements_net_stream_conn = { .vtable = &w5500_tcp_conn_vtable }, - .implements_net_packet_conn = { .vtable = &w5500_udp_conn_vtable }, .socknum = i, /* mutable */ .next_free = (i + 1 < 8) ? &chip->sockets[i+1] : NULL, @@ -449,15 +392,13 @@ void w5500_soft_reset(struct w5500 *chip) { cr_mutex_unlock(&chip->mu); } -static struct net_eth_addr w5500_if_hwaddr(implements_net_iface *_chip) { - struct w5500 *chip = VCALL_SELF(struct w5500, implements_net_iface, _chip); +static struct net_eth_addr w5500_if_hwaddr(struct w5500 *chip) { assert(chip); return chip->hwaddr; } -static void _w5500_if_up(implements_net_iface *_chip, struct net_iface_config cfg) { - struct w5500 *chip = VCALL_SELF(struct w5500, implements_net_iface, _chip); +static void _w5500_if_up(struct w5500 *chip, struct net_iface_config cfg) { assert(chip); cr_mutex_lock(&chip->mu); @@ -469,27 +410,26 @@ static void _w5500_if_up(implements_net_iface *_chip, struct net_iface_config cf cr_mutex_unlock(&chip->mu); } -static void w5500_if_up(implements_net_iface *_chip, struct net_iface_config cfg) { +static void w5500_if_ifup(struct w5500 *chip, struct net_iface_config cfg) { debugf("if_up()"); debugf(":: addr = "PRI_net_ip4_addr, ARG_net_ip4_addr(cfg.addr)); debugf(":: gateway_addr = "PRI_net_ip4_addr, ARG_net_ip4_addr(cfg.gateway_addr)); debugf(":: subnet_mask = "PRI_net_ip4_addr, ARG_net_ip4_addr(cfg.subnet_mask)); - _w5500_if_up(_chip, cfg); + _w5500_if_up(chip, cfg); } -static void w5500_if_down(implements_net_iface *_chip) { +static void w5500_if_ifdown(struct w5500 *chip) { debugf("if_down()"); - _w5500_if_up(_chip, (struct net_iface_config){0}); + _w5500_if_up(chip, (struct net_iface_config){0}); } -static implements_net_stream_listener *w5500_if_tcp_listen(implements_net_iface *_chip, uint16_t local_port) { - struct w5500 *chip = VCALL_SELF(struct w5500, implements_net_iface, _chip); +static lo_interface net_stream_listener w5500_if_tcp_listen(struct w5500 *chip, uint16_t local_port) { assert(chip); struct _w5500_socket *sock = w5500_alloc_socket(chip); if (!sock) { debugf("tcp_listen() => no sock"); - return NULL; + return LO_NULL(net_stream_listener); } debugf("tcp_listen() => sock[%"PRIu8"]", sock->socknum); @@ -502,12 +442,11 @@ static implements_net_stream_listener *w5500_if_tcp_listen(implements_net_iface sock->read_deadline_ns = 0; sock->list_open = true; - return &sock->implements_net_stream_listener; + return lo_box_w5500_tcplist_as_net_stream_listener(sock); } -static implements_net_stream_conn *w5500_if_tcp_dial(implements_net_iface *_chip, - struct net_ip4_addr node, uint16_t port) { - struct w5500 *chip = VCALL_SELF(struct w5500, implements_net_iface, _chip); +static lo_interface net_stream_conn w5500_if_tcp_dial(struct w5500 *chip, + struct net_ip4_addr node, uint16_t port) { assert(chip); assert(memcmp(node.octets, net_ip4_addr_zero.octets, 4)); assert(memcmp(node.octets, net_ip4_addr_broadcast.octets, 4)); @@ -516,7 +455,7 @@ static implements_net_stream_conn *w5500_if_tcp_dial(implements_net_iface *_chip struct _w5500_socket *socket = w5500_alloc_socket(chip); if (!socket) { debugf("tcp_dial() => no sock"); - return NULL; + return LO_NULL(net_stream_conn); } uint8_t socknum = socket->socknum; debugf("tcp_dial() => sock[%"PRIu8"]", socknum); @@ -553,21 +492,20 @@ static implements_net_stream_conn *w5500_if_tcp_dial(implements_net_iface *_chip cr_yield(); break; case STATE_TCP_ESTABLISHED: - return &socket->implements_net_stream_conn; + return lo_box_w5500_tcp_as_net_stream_conn(socket); default: goto restart; } } } -static implements_net_packet_conn *w5500_if_udp_conn(implements_net_iface *_chip, uint16_t local_port) { - struct w5500 *chip = VCALL_SELF(struct w5500, implements_net_iface, _chip); +static lo_interface net_packet_conn w5500_if_udp_conn(struct w5500 *chip, uint16_t local_port) { assert(chip); struct _w5500_socket *socket = w5500_alloc_socket(chip); if (!socket) { debugf("udp_conn() => no sock"); - return NULL; + return LO_NULL(net_packet_conn); } uint8_t socknum = socket->socknum; debugf("udp_conn() => sock[%"PRIu8"]", socknum); @@ -590,18 +528,18 @@ static implements_net_packet_conn *w5500_if_udp_conn(implements_net_iface *_chip cr_yield(); cr_mutex_unlock(&chip->mu); - return &socket->implements_net_packet_conn; + return lo_box_w5500_udp_as_net_packet_conn(socket); } /* tcp_listener methods *******************************************************/ -static implements_net_stream_conn *w5500_tcplist_accept(implements_net_stream_listener *_socket) { +static lo_interface net_stream_conn w5500_tcplist_accept(struct _w5500_socket *socket) { ASSERT_SELF(stream_listener, TCP); restart: if (!socket->list_open) { debugf("tcp_listener.accept() => already closed"); - return NULL; + return LO_NULL(net_stream_conn); } cr_mutex_lock(&chip->mu); @@ -630,14 +568,14 @@ static implements_net_stream_conn *w5500_tcplist_accept(implements_net_stream_li /* fall-through */ case STATE_TCP_CLOSE_WAIT: socket->write_open = true; - return &socket->implements_net_stream_conn; + return lo_box_w5500_tcp_as_net_stream_conn(socket); default: goto restart; } } } -static int w5500_tcplist_close(implements_net_stream_listener *_socket) { +static int w5500_tcplist_close(struct _w5500_socket *socket) { debugf("tcp_listener.close()"); ASSERT_SELF(stream_listener, TCP); @@ -648,7 +586,7 @@ static int w5500_tcplist_close(implements_net_stream_listener *_socket) { /* tcp_conn methods ***********************************************************/ -static ssize_t w5500_tcp_write(implements_net_stream_conn *_socket, void *buf, size_t count) { +static ssize_t w5500_tcp_write(struct _w5500_socket *socket, void *buf, size_t count) { debugf("tcp_conn.write(%zu)", count); ASSERT_SELF(stream_conn, TCP); assert(buf); @@ -725,7 +663,7 @@ static ssize_t w5500_tcp_write(implements_net_stream_conn *_socket, void *buf, s return done; } -static void w5500_tcp_set_read_deadline(implements_net_stream_conn *_socket, uint64_t ns) { +static void w5500_tcp_set_read_deadline(struct _w5500_socket *socket, uint64_t ns) { debugf("tcp_conn.set_read_deadline(%"PRIu64")", ns); ASSERT_SELF(stream_conn, TCP); socket->read_deadline_ns = ns; @@ -736,7 +674,7 @@ static void w5500_tcp_alarm_handler(void *_arg) { cr_sema_signal_from_intrhandler(&socket->read_sema); } -static ssize_t w5500_tcp_read(implements_net_stream_conn *_socket, void *buf, size_t count) { +static ssize_t w5500_tcp_read(struct _w5500_socket *socket, void *buf, size_t count) { debugf("tcp_conn.read()"); ASSERT_SELF(stream_conn, TCP); assert(buf); @@ -744,21 +682,21 @@ static ssize_t w5500_tcp_read(implements_net_stream_conn *_socket, void *buf, si struct alarmclock_trigger trigger = {0}; if (socket->read_deadline_ns) - VCALL(bootclock, add_trigger, &trigger, - socket->read_deadline_ns, - w5500_tcp_alarm_handler, - socket); + LO_CALL(bootclock, add_trigger, &trigger, + socket->read_deadline_ns, + w5500_tcp_alarm_handler, + socket); /* Wait until there is data to read. */ uint16_t avail = 0; for (;;) { if (!socket->read_open) { - VCALL(bootclock, del_trigger, &trigger); + LO_CALL(bootclock, del_trigger, &trigger); debugf(" => soft closed"); return -NET_ECLOSED; } - if (socket->read_deadline_ns && socket->read_deadline_ns <= VCALL(bootclock, get_time_ns)) { - VCALL(bootclock, del_trigger, &trigger); + if (socket->read_deadline_ns && socket->read_deadline_ns <= LO_CALL(bootclock, get_time_ns)) { + LO_CALL(bootclock, del_trigger, &trigger); debugf(" => recv timeout"); return -NET_ERECV_TIMEOUT; } @@ -770,7 +708,7 @@ static ssize_t w5500_tcp_read(implements_net_stream_conn *_socket, void *buf, si case STATE_TCP_FIN_WAIT: break; /* OK */ default: - VCALL(bootclock, del_trigger, &trigger); + LO_CALL(bootclock, del_trigger, &trigger); cr_mutex_unlock(&chip->mu); debugf(" => hard closed"); return -NET_ECLOSED; @@ -781,7 +719,7 @@ static ssize_t w5500_tcp_read(implements_net_stream_conn *_socket, void *buf, si /* We have data to read. */ break; if (state == STATE_TCP_CLOSE_WAIT) { - VCALL(bootclock, del_trigger, &trigger); + LO_CALL(bootclock, del_trigger, &trigger); cr_mutex_unlock(&chip->mu); debugf(" => EOF"); return 0; @@ -801,12 +739,12 @@ static ssize_t w5500_tcp_read(implements_net_stream_conn *_socket, void *buf, si w5500ll_write_sock_reg(chip->spidev, socknum, rx_read_pointer, uint16be_marshal(ptr+avail)); w5500_socket_cmd(socket, CMD_RECV); /* Return. */ - VCALL(bootclock, del_trigger, &trigger); + LO_CALL(bootclock, del_trigger, &trigger); cr_mutex_unlock(&chip->mu); return avail; } -static int w5500_tcp_close(implements_net_stream_conn *_socket, bool rd, bool wr) { +static int w5500_tcp_close(struct _w5500_socket *socket, bool rd, bool wr) { debugf("tcp_conn.close(rd=%s, wr=%s)", rd ? "true" : "false", wr ? "true" : "false"); ASSERT_SELF(stream_conn, TCP); @@ -839,7 +777,7 @@ static int w5500_tcp_close(implements_net_stream_conn *_socket, bool rd, bool wr /* udp_conn methods ***********************************************************/ -static ssize_t w5500_udp_sendto(implements_net_packet_conn *_socket, void *buf, size_t count, +static ssize_t w5500_udp_sendto(struct _w5500_socket *socket, void *buf, size_t count, struct net_ip4_addr node, uint16_t port) { debugf("udp_conn.sendto()"); ASSERT_SELF(packet_conn, UDP); @@ -896,7 +834,7 @@ static ssize_t w5500_udp_sendto(implements_net_packet_conn *_socket, void *buf, } } -static void w5500_udp_set_read_deadline(implements_net_packet_conn *_socket, uint64_t ns) { +static void w5500_udp_set_read_deadline(struct _w5500_socket *socket, uint64_t ns) { debugf("udp_conn.set_read_deadline(%"PRIu64")", ns); ASSERT_SELF(packet_conn, UDP); socket->read_deadline_ns = ns; @@ -907,7 +845,7 @@ static void w5500_udp_alarm_handler(void *_arg) { cr_sema_signal_from_intrhandler(&socket->read_sema); } -static ssize_t w5500_udp_recvfrom(implements_net_packet_conn *_socket, void *buf, size_t count, +static ssize_t w5500_udp_recvfrom(struct _w5500_socket *socket, void *buf, size_t count, struct net_ip4_addr *ret_node, uint16_t *ret_port) { debugf("udp_conn.recvfrom()"); ASSERT_SELF(packet_conn, UDP); @@ -916,23 +854,23 @@ static ssize_t w5500_udp_recvfrom(implements_net_packet_conn *_socket, void *buf struct alarmclock_trigger trigger = {0}; if (socket->read_deadline_ns) - VCALL(bootclock, add_trigger, &trigger, - socket->read_deadline_ns, - w5500_udp_alarm_handler, - socket); + LO_CALL(bootclock, add_trigger, &trigger, + socket->read_deadline_ns, + w5500_udp_alarm_handler, + socket); /* Wait until there is data to read. */ uint16_t avail = 0; for (;;) { - if (socket->read_deadline_ns && socket->read_deadline_ns <= VCALL(bootclock, get_time_ns)) { - VCALL(bootclock, del_trigger, &trigger); + if (socket->read_deadline_ns && socket->read_deadline_ns <= LO_CALL(bootclock, get_time_ns)) { + LO_CALL(bootclock, del_trigger, &trigger); debugf(" => recv timeout"); return -NET_ERECV_TIMEOUT; } cr_mutex_lock(&chip->mu); uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state); if (state != STATE_UDP) { - VCALL(bootclock, del_trigger, &trigger); + LO_CALL(bootclock, del_trigger, &trigger); debugf(" => hard closed"); return -NET_ECLOSED; } @@ -970,12 +908,12 @@ static ssize_t w5500_udp_recvfrom(implements_net_packet_conn *_socket, void *buf w5500ll_write_sock_reg(chip->spidev, socknum, rx_read_pointer, uint16be_marshal(ptr+8+len)); w5500_socket_cmd(socket, CMD_RECV); /* Return. */ - VCALL(bootclock, del_trigger, &trigger); + LO_CALL(bootclock, del_trigger, &trigger); cr_mutex_unlock(&chip->mu); return len; } -static int w5500_udp_close(implements_net_packet_conn *_socket) { +static int w5500_udp_close(struct _w5500_socket *socket) { debugf("udp_conn.close()"); ASSERT_SELF(packet_conn, UDP); diff --git a/libhw/w5500_ll.h b/libhw/w5500_ll.h index 25aa6b5..92d9f14 100644 --- a/libhw/w5500_ll.h +++ b/libhw/w5500_ll.h @@ -3,7 +3,7 @@ * Based entirely on the W5500 datasheet, v1.1.0. * https://docs.wiznet.io/img/products/w5500/W5500_ds_v110e.pdf * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later */ @@ -15,10 +15,9 @@ #include <libmisc/assert.h> /* for assert(), static_assert() */ #include <libmisc/endian.h> /* for uint16be_t */ -#include <libmisc/vcall.h> /* for VCALL() */ #include <libhw/generic/net.h> /* for struct net_eth_addr, struct net_ip4_addr */ -#include <libhw/generic/spi.h> /* for implements_spi */ +#include <libhw/generic/spi.h> /* for lo_interface spi */ /* Config *********************************************************************/ @@ -74,8 +73,8 @@ _w5500ll_write(const char *func, #else w5500ll_write( #endif - implements_spi *spidev, uint16_t addr, uint8_t block, void *data, size_t data_len) { - assert(spidev); + lo_interface spi spidev, uint16_t addr, uint8_t block, void *data, size_t data_len) { + assert(!LO_IS_NULL(spidev)); assert((block & ~CTL_MASK_BLOCK) == 0); assert(data); assert(data_len); @@ -94,7 +93,7 @@ w5500ll_write( {.iov_read_dst = NULL, .iov_write_src = header, .iov_len = sizeof(header)}, {.iov_read_dst = NULL, .iov_write_src = data, .iov_len = data_len}, }; - VCALL(spidev, readwritev, iov, 2); + LO_CALL(spidev, readwritev, iov, 2); } static inline void @@ -104,8 +103,8 @@ _w5500ll_read(const char *func, #else w5500ll_read( #endif - implements_spi *spidev, uint16_t addr, uint8_t block, void *data, size_t data_len) { - assert(spidev); + lo_interface spi spidev, uint16_t addr, uint8_t block, void *data, size_t data_len) { + assert(!LO_IS_NULL(spidev)); assert((block & ~CTL_MASK_BLOCK) == 0); assert(data); assert(data_len); @@ -124,7 +123,7 @@ w5500ll_read( {.iov_read_dst = NULL, .iov_write_src = header, .iov_len = sizeof(header)}, {.iov_read_dst = data, .iov_write_src = NULL, .iov_len = data_len}, }; - VCALL(spidev, readwritev, iov, 2); + LO_CALL(spidev, readwritev, iov, 2); } /* Common chip-wide registers. ***********************************************/ diff --git a/libhw_generic/CMakeLists.txt b/libhw_generic/CMakeLists.txt index 0356770..e38fbe9 100644 --- a/libhw_generic/CMakeLists.txt +++ b/libhw_generic/CMakeLists.txt @@ -1,12 +1,13 @@ # libhw_generic/CMakeLists.txt - UAPI device interfaces # -# Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> +# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> # SPDX-License-Identifier: AGPL-3.0-or-later add_library(libhw_generic INTERFACE) target_include_directories(libhw_generic SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) target_link_libraries(libhw_generic INTERFACE libmisc + libobj libcr ) diff --git a/libhw_generic/alarmclock.c b/libhw_generic/alarmclock.c index a16f2f6..7fd049e 100644 --- a/libhw_generic/alarmclock.c +++ b/libhw_generic/alarmclock.c @@ -1,24 +1,25 @@ /* libhw_generic/alarmclock.c - Device-independent <libhw/generic/alarmclock.h> utilities * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later */ #include <libcr/coroutine.h> -#include <libmisc/vcall.h> #include <libhw/generic/alarmclock.h> +lo_interface alarmclock bootclock = {0}; + static void alarmclock_sleep_intrhandler(void *_arg) { cid_t cid = *(cid_t *)_arg; cr_unpause_from_intrhandler(cid); } -void alarmclock_sleep_until_ns(implements_alarmclock *clock, uint64_t abstime_ns) { +void alarmclock_sleep_until_ns(lo_interface alarmclock clock, uint64_t abstime_ns) { bool saved = cr_save_and_disable_interrupts(); cid_t cid = cr_getcid(); struct alarmclock_trigger trigger; - VCALL(clock, add_trigger, &trigger, abstime_ns, alarmclock_sleep_intrhandler, &cid); + LO_CALL(clock, add_trigger, &trigger, abstime_ns, alarmclock_sleep_intrhandler, &cid); cr_pause_and_yield(); cr_restore_interrupts(saved); } diff --git a/libhw_generic/include/libhw/generic/alarmclock.h b/libhw_generic/include/libhw/generic/alarmclock.h index a9d816b..3817b4b 100644 --- a/libhw_generic/include/libhw/generic/alarmclock.h +++ b/libhw_generic/include/libhw/generic/alarmclock.h @@ -1,6 +1,6 @@ /* libhw/generic/alarmclock.h - Device-independent alarmclock definitions * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later */ @@ -11,7 +11,7 @@ #include <stdint.h> /* for uint{n}_t and UINT{n}_C */ #include <libmisc/private.h> -#include <libmisc/vcall.h> +#include <libobj/obj.h> /* Useful constants ***********************************************************/ @@ -35,59 +35,53 @@ struct alarmclock_trigger { /* Interface ******************************************************************/ -struct alarmclock_vtable; - -typedef struct { - struct alarmclock_vtable *vtable; -} implements_alarmclock; - -struct alarmclock_vtable { - /** - * (2⁶⁴-1 nanoseconds is more than 500 years; there is little - * risk of this overflowing) - */ - uint64_t (*get_time_ns)(implements_alarmclock *self); - - /** - * Returns true on error. - * - * Implementations may return an error if fire_at_ns is more - * than UINT32_MAX µs (72 minutes) in the future. - * - * If fire_at_ns is in the past, then it will fire - * immediately. - */ - bool (*add_trigger)(implements_alarmclock *self, struct alarmclock_trigger *trigger, - uint64_t fire_at_ns, - void (*cb)(void *), - void *cb_arg); - - void (*del_trigger)(implements_alarmclock *self, struct alarmclock_trigger *trigger); -}; +#define alarmclock_LO_IFACE \ + /** \ + * (2⁶⁴-1 nanoseconds is more than 500 years; there is little \ + * risk of this overflowing) \ + */ \ + LO_FUNC(uint64_t, get_time_ns) \ + \ + /** \ + * Returns true on error. \ + * \ + * Implementations may return an error if fire_at_ns is more \ + * than UINT32_MAX µs (72 minutes) in the future. \ + * \ + * If fire_at_ns is in the past, then it will fire \ + * immediately. \ + */ \ + LO_FUNC(bool, add_trigger, struct alarmclock_trigger *trigger, \ + uint64_t fire_at_ns, \ + void (*cb)(void *), \ + void *cb_arg) \ + \ + LO_FUNC(void, del_trigger, struct alarmclock_trigger *trigger) +LO_INTERFACE(alarmclock) /* Utilities ******************************************************************/ -void alarmclock_sleep_until_ns(implements_alarmclock *clock, uint64_t abstime_ns); +void alarmclock_sleep_until_ns(lo_interface alarmclock clock, uint64_t abstime_ns); -static inline void alarmclock_sleep_for_ns(implements_alarmclock *clock, uint64_t delta_ns) { - alarmclock_sleep_until_ns(clock, VCALL(clock, get_time_ns) + delta_ns); +static inline void alarmclock_sleep_for_ns(lo_interface alarmclock clock, uint64_t delta_ns) { + alarmclock_sleep_until_ns(clock, LO_CALL(clock, get_time_ns) + delta_ns); } -static inline void alarmclock_sleep_for_us(implements_alarmclock *clock, uint64_t delta_us) { +static inline void alarmclock_sleep_for_us(lo_interface alarmclock clock, uint64_t delta_us) { alarmclock_sleep_for_ns(clock, delta_us * (NS_PER_S/US_PER_S)); } -static inline void alarmclock_sleep_for_ms(implements_alarmclock *clock, uint64_t delta_ms) { +static inline void alarmclock_sleep_for_ms(lo_interface alarmclock clock, uint64_t delta_ms) { alarmclock_sleep_for_ns(clock, delta_ms * (NS_PER_S/MS_PER_S)); } -static inline void alarmclock_sleep_for_s(implements_alarmclock *clock, uint64_t delta_s) { +static inline void alarmclock_sleep_for_s(lo_interface alarmclock clock, uint64_t delta_s) { alarmclock_sleep_for_ns(clock, delta_s * NS_PER_S); } /* Globals ********************************************************************/ -extern implements_alarmclock *bootclock; +extern lo_interface alarmclock bootclock; #define sleep_until_ns(t) alarmclock_sleep_until_ns(bootclock, t) #define sleep_for_ns(t) alarmclock_sleep_for_ns(bootclock, t) diff --git a/libhw_generic/include/libhw/generic/net.h b/libhw_generic/include/libhw/generic/net.h index 0f9872e..c888735 100644 --- a/libhw_generic/include/libhw/generic/net.h +++ b/libhw_generic/include/libhw/generic/net.h @@ -1,6 +1,6 @@ /* libhw/generic/net.h - Device-independent network definitions * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later */ @@ -13,6 +13,8 @@ #include <stdint.h> /* for uint{n}_t} */ #include <sys/types.h> /* for ssize_t */ +#include <libobj/obj.h> + /* Errnos *********************************************************************/ #define NET_EOTHER 1 @@ -54,97 +56,81 @@ struct net_eth_addr { /* Streams (e.g. TCP) *********************************************************/ -struct net_stream_listener_vtable; -struct net_stream_conn_vtable; - -typedef struct { - struct net_stream_listener_vtable *vtable; -} implements_net_stream_listener; - -typedef struct { - struct net_stream_conn_vtable *vtable; -} implements_net_stream_conn; - -struct net_stream_listener_vtable { - /** - * It is invalid to accept() a new connection if an existing - * connection is still open. - */ - implements_net_stream_conn *(*accept)(implements_net_stream_listener *self); - - /** - * The net_stream_conn returned from accept() may still be - * valid after the listener is closed. - * - * Return 0 on success, -errno on error. - */ - int (*close)(implements_net_stream_listener *self); -}; - -struct net_stream_conn_vtable { - /** - * Return bytes-read on success, 0 on EOF, -errno on error; a - * short read is *not* an error. - */ - ssize_t (*read)(implements_net_stream_conn *self, - void *buf, size_t count); - - /** - * Set a timestamp after which calls to read() will return - * NET_ETIMEDOUT. The timestamp is in nanoseconds on the - * system monotonic clock, which is usually (on pico-sdk and - * on the Linux kernel) nanoseconds-since-boot. - * - * A zero value disables the deadline. - * - * (2⁶⁴-1 nanoseconds is more than 500 years; there is little - * risk of this overflowing) - */ - void (*set_read_deadline)(implements_net_stream_conn *self, - uint64_t ns_since_boot); - - /** - * Return `count` on success, -errno on error; a short write *is* an - * error. - * - * Writes are *not* guaranteed to be atomic (as this would be - * expensive to implement), so if you have concurrent writers then you - * should arrange for a mutex to protect the connection. - */ - ssize_t (*write)(implements_net_stream_conn *self, - void *buf, size_t count); - - /** - * Return 0 on success, -errno on error. - */ - int (*close)(implements_net_stream_conn *self, - bool rd, bool wr); -}; +lo_interface net_stream_conn; + +#define net_stream_listener_LO_IFACE \ + /** \ + * It is invalid to accept() a new connection if an existing \ + * connection is still open. \ + */ \ + LO_FUNC(lo_interface net_stream_conn, accept) \ + \ + /** \ + * The net_stream_conn returned from accept() may still be \ + * valid after the listener is closed. \ + * \ + * Return 0 on success, -errno on error. \ + */ \ + LO_FUNC(int, close) +LO_INTERFACE(net_stream_listener) + +#define net_stream_conn_LO_IFACE \ + /** \ + * Return bytes-read on success, 0 on EOF, -errno on error; a \ + * short read is *not* an error. \ + */ \ + LO_FUNC(ssize_t, read, void *buf, size_t count) \ + \ + /** \ + * Set a timestamp after which calls to read() will return \ + * NET_ETIMEDOUT. The timestamp is in nanoseconds on the \ + * system monotonic clock, which is usually (on pico-sdk and \ + * on the Linux kernel) nanoseconds-since-boot. \ + * \ + * A zero value disables the deadline. \ + * \ + * (2⁶⁴-1 nanoseconds is more than 500 years; there is little \ + * risk of this overflowing) \ + */ \ + LO_FUNC(void, set_read_deadline, uint64_t ns_since_boot) \ + \ + /** \ + * Return `count` on success, -errno on error; a short write *is* an \ + * error. \ + * \ + * Writes are *not* guaranteed to be atomic (as this would be \ + * expensive to implement), so if you have concurrent writers then you \ + * should arrange for a mutex to protect the connection. \ + */ \ + LO_FUNC(ssize_t, write, void *buf, size_t count) \ + \ + /** \ + * Return 0 on success, -errno on error. \ + */ \ + LO_FUNC(int, close, bool rd, bool wr) +LO_INTERFACE(net_stream_conn) /* Packets (e.g. UDP) *********************************************************/ -struct net_packet_conn_vtable; - -typedef struct { - struct net_packet_conn_vtable *vtable; -} implements_net_packet_conn; - -struct net_packet_conn_vtable { - ssize_t (*sendto )(implements_net_packet_conn *self, - void *buf, size_t len, - struct net_ip4_addr node, uint16_t port); - /** - * @return The full length of the message, which may be more - * than the given `len` (as if the Linux MSG_TRUNC flag were - * given). - */ - ssize_t (*recvfrom)(implements_net_packet_conn *self, - void *buf, size_t len, - struct net_ip4_addr *ret_node, uint16_t *ret_port); - void (*set_read_deadline)(implements_net_packet_conn *self, - uint64_t ns_since_boot); - int (*close )(implements_net_packet_conn *self); -}; +#define net_packet_conn_LO_IFACE \ + LO_FUNC(ssize_t, sendto, \ + void *buf, size_t len, \ + struct net_ip4_addr node, uint16_t port) \ + \ + /** \ + * @return The full length of the message, which may be more \ + * than the given `len` (as if the Linux MSG_TRUNC flag were \ + * given). \ + */ \ + LO_FUNC(ssize_t, recvfrom, \ + void *buf, size_t len, \ + struct net_ip4_addr *ret_node, uint16_t *ret_port) \ + \ + LO_FUNC(void, set_read_deadline, \ + uint64_t ns_since_boot) \ + \ + LO_FUNC(int, close) +LO_INTERFACE(net_packet_conn) /* Interfaces *****************************************************************/ @@ -154,20 +140,14 @@ struct net_iface_config { struct net_ip4_addr subnet_mask; }; -struct net_iface_vtable; - -typedef struct { - struct net_iface_vtable *vtable; -} implements_net_iface; - -struct net_iface_vtable { - struct net_eth_addr (*hwaddr )(implements_net_iface *); - void (*ifup )(implements_net_iface *, struct net_iface_config); - void (*ifdown )(implements_net_iface *); - - implements_net_stream_listener *(*tcp_listen)(implements_net_iface *, uint16_t local_port); - implements_net_stream_conn *(*tcp_dial )(implements_net_iface *, struct net_ip4_addr remote_node, uint16_t remote_port); - implements_net_packet_conn *(*udp_conn )(implements_net_iface *, uint16_t local_port); -}; +#define net_iface_LO_IFACE \ + LO_FUNC(struct net_eth_addr , hwaddr ) \ + LO_FUNC(void , ifup , struct net_iface_config) \ + LO_FUNC(void , ifdown ) \ + \ + LO_FUNC(lo_interface net_stream_listener, tcp_listen, uint16_t local_port) \ + LO_FUNC(lo_interface net_stream_conn , tcp_dial , struct net_ip4_addr remote_node, uint16_t remote_port) \ + LO_FUNC(lo_interface net_packet_conn , udp_conn , uint16_t local_port) +LO_INTERFACE(net_iface) #endif /* _LIBHW_GENERIC_NET_H_ */ diff --git a/libhw_generic/include/libhw/generic/spi.h b/libhw_generic/include/libhw/generic/spi.h index 2207a2c..aeeca37 100644 --- a/libhw_generic/include/libhw/generic/spi.h +++ b/libhw_generic/include/libhw/generic/spi.h @@ -1,6 +1,6 @@ /* libhw/generic/spi.h - Device-independent SPI definitions * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later */ @@ -9,6 +9,8 @@ #include <stddef.h> /* for size_t */ +#include <libobj/obj.h> + enum spi_mode { SPI_MODE_0 = 0, /* clk_polarity=0 (idle low), clk_phase=0 (sample on rise) */ SPI_MODE_1 = 1, /* clk_polarity=0 (idle low), clk_phase=1 (sample on fall) */ @@ -22,12 +24,6 @@ struct bidi_iovec { size_t iov_len; }; -struct spi_vtable; - -typedef struct { - struct spi_vtable *vtable; -} implements_spi; - /* This API assumes that an SPI frame is a multiple of 8-bits. * * It is my understanding that this is a common constraint of SPI @@ -40,8 +36,8 @@ typedef struct { * octets; so we have no need for an API that allows a * non-multiple-of-8 number of bits. */ -struct spi_vtable { - void (*readwritev)(implements_spi *, const struct bidi_iovec *iov, int iovcnt); -}; +#define spi_LO_IFACE \ + LO_FUNC(void, readwritev, const struct bidi_iovec *iov, int iovcnt) +LO_INTERFACE(spi) #endif /* _LIBHW_GENERIC_SPI_H_ */ diff --git a/libmisc/CMakeLists.txt b/libmisc/CMakeLists.txt index 8d842c3..f8f15bc 100644 --- a/libmisc/CMakeLists.txt +++ b/libmisc/CMakeLists.txt @@ -1,6 +1,6 @@ -# libmisc/CMakeLists.txt - A simple Go-ish object system built on GCC -fplan9-extensions +# libmisc/CMakeLists.txt - TODO # -# Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> +# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> # SPDX-License-Identifier: AGPL-3.0-or-later add_library(libmisc INTERFACE) @@ -20,4 +20,3 @@ add_lib_test(libmisc test_log) add_lib_test(libmisc test_macro) add_lib_test(libmisc test_private) add_lib_test(libmisc test_rand) -add_lib_test(libmisc test_vcall) diff --git a/libmisc/include/libmisc/macro.h b/libmisc/include/libmisc/macro.h index 9bb068f..d11b99f 100644 --- a/libmisc/include/libmisc/macro.h +++ b/libmisc/include/libmisc/macro.h @@ -1,6 +1,6 @@ /* libmisc/macro.h - Useful C preprocessor macros * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later */ @@ -36,6 +36,7 @@ /* macro arguments */ +#define LM_FIRST(a, ...) a #define LM_SECOND(a, b, ...) b #define LM_EAT(...) #define LM_EXPAND(...) __VA_ARGS__ @@ -52,4 +53,42 @@ #define _LM_IF__xxTxx(...) __VA_ARGS__ LM_EAT #define _LM_IF__xxFxx(...) LM_EXPAND +/* tuples */ + +#define LM_IS_TUPLE(x) LM_IS_SENTINEL(_LM_IS_TUPLE x) +#define _LM_IS_TUPLE(...) LM_SENTINEL() + +/* `tuples` is a sequence of `(tuple1)(tuple2)(tuple3)` */ +#define _LM_TUPLES_COMMA(tuple...) (tuple), +#define LM_TUPLES_NONEMPTY(tuples) LM_IS_TUPLE(_LM_TUPLES_COMMA tuples) +#define LM_TUPLES_HEAD(tuples) LM_EXPAND(LM_FIRST LM_EAT() (_LM_TUPLES_COMMA tuples)) +#define LM_TUPLES_TAIL(tuples) LM_EAT tuples + +/* iteration */ + +/* BUG: LM_FOREACH_TUPLE maxes out at 1024 tuples. */ +#define LM_FOREACH_TUPLE(tuples, func, ...) \ + _LM_EVAL(_LM_FOREACH_TUPLE(tuples, func, __VA_ARGS__)) +#define _LM_FOREACH_TUPLE(tuples, func, ...) \ + LM_IF(LM_TUPLES_NONEMPTY(tuples))( \ + _LM_DEFER2(func)(__VA_ARGS__ __VA_OPT__(,) LM_EXPAND LM_TUPLES_HEAD(tuples)) \ + _LM_DEFER2(_LM_FOREACH_TUPLE_indirect)()(LM_TUPLES_TAIL(tuples), func, __VA_ARGS__) \ + )() +#define _LM_FOREACH_TUPLE_indirect() _LM_FOREACH_TUPLE + +#define _LM_DEFER2(macro) macro LM_EAT LM_EAT()() + +#define _LM_EVAL(...) _LM_EVAL__1024(__VA_ARGS__) /* 1024 iterations aught to be enough for anybody */ +#define _LM_EVAL__1024(...) _LM_EVAL__512(_LM_EVAL__512(__VA_ARGS__)) +#define _LM_EVAL__512(...) _LM_EVAL__256(_LM_EVAL__256(__VA_ARGS__)) +#define _LM_EVAL__256(...) _LM_EVAL__128(_LM_EVAL__128(__VA_ARGS__)) +#define _LM_EVAL__128(...) _LM_EVAL__64(_LM_EVAL__64(__VA_ARGS__)) +#define _LM_EVAL__64(...) _LM_EVAL__32(_LM_EVAL__32(__VA_ARGS__)) +#define _LM_EVAL__32(...) _LM_EVAL__16(_LM_EVAL__16(__VA_ARGS__)) +#define _LM_EVAL__16(...) _LM_EVAL__8(_LM_EVAL__8(__VA_ARGS__)) +#define _LM_EVAL__8(...) _LM_EVAL__4(_LM_EVAL__4(__VA_ARGS__)) +#define _LM_EVAL__4(...) _LM_EVAL__2(_LM_EVAL__2(__VA_ARGS__)) +#define _LM_EVAL__2(...) _LM_EVAL__1(_LM_EVAL__1(__VA_ARGS__)) +#define _LM_EVAL__1(...) __VA_ARGS__ + #endif /* _LIBMISC_MACRO_H_ */ diff --git a/libmisc/include/libmisc/vcall.h b/libmisc/include/libmisc/vcall.h deleted file mode 100644 index 31a8c7e..0000000 --- a/libmisc/include/libmisc/vcall.h +++ /dev/null @@ -1,28 +0,0 @@ -/* libmisc/vcall.h - A simple Go-ish object system built on GCC -fplan9-extensions - * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -#ifndef _LIBMISC_VCALL_H_ -#define _LIBMISC_VCALL_H_ - -#include <stddef.h> /* for offsetof() */ - -#include <libmisc/assert.h> - -#define VCALL(o, m, ...) \ - ({ \ - assert(o); \ - (o)->vtable->m(o __VA_OPT__(,) __VA_ARGS__); \ - }) - -#define VCALL_SELF(obj_typ, iface_typ, iface_ptr) \ - ({ \ - static_assert(_Generic(iface_ptr, iface_typ *: 1, default: 0), \ - "typeof("#iface_ptr") != "#iface_typ" *"); \ - assert(iface_ptr); \ - ((obj_typ*)(((void*)iface_ptr)-offsetof(obj_typ,iface_typ))); \ - }) - -#endif /* _LIBMISC_VCALL_H_ */ diff --git a/libmisc/tests/test_vcall.c b/libmisc/tests/test_vcall.c deleted file mode 100644 index f36fc4b..0000000 --- a/libmisc/tests/test_vcall.c +++ /dev/null @@ -1,74 +0,0 @@ -/* libmisc/tests/test_vcall.c - Tests for <libmisc/vcall.h> - * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -#include <libmisc/assert.h> -#include <libmisc/vcall.h> - -#include "test.h" - -/******************************************************************************/ - -struct frobber_vtable; - -typedef struct { - struct frobber_vtable *vtable; -} implements_frobber; - -struct frobber_vtable { - int (*frob)(implements_frobber *); - int (*frob1)(implements_frobber *, int); - void (*frob0)(implements_frobber *); -}; - -/******************************************************************************/ - -struct myclass { - int a; - implements_frobber; -}; -static_assert(offsetof(struct myclass, implements_frobber) != 0); - -static int myclass_frob(implements_frobber *_self) { - struct myclass *self = VCALL_SELF(struct myclass, implements_frobber, _self); - test_assert(self); - test_assert((void*)self != (void*)_self); - return self->a; -} - -static int myclass_frob1(implements_frobber *_self, int arg) { - struct myclass *self = VCALL_SELF(struct myclass, implements_frobber, _self); - test_assert(self); - test_assert((void*)self != (void*)_self); - return arg; -} - -static void myclass_frob0(implements_frobber *_self) { - struct myclass *self = VCALL_SELF(struct myclass, implements_frobber, _self); - test_assert(self); - test_assert((void*)self != (void*)_self); -} - -struct frobber_vtable myclass_vtable = { - .frob = myclass_frob, - .frob1 = myclass_frob1, - .frob0 = myclass_frob0, -}; - -/******************************************************************************/ - -#define MAGIC1 909837 -#define MAGIC2 657441 - -int main() { - struct myclass obj = { - .implements_frobber = { .vtable = &myclass_vtable }, - .a = MAGIC1, - }; - test_assert(VCALL(&obj, frob) == MAGIC1); - test_assert(VCALL(&obj, frob1, MAGIC2) == MAGIC2); - VCALL(&obj, frob0); - return 0; -} diff --git a/libobj/CMakeLists.txt b/libobj/CMakeLists.txt new file mode 100644 index 0000000..1cc552c --- /dev/null +++ b/libobj/CMakeLists.txt @@ -0,0 +1,14 @@ +# libobj/CMakeLists.txt - A simple Go-ish object system built on GCC -fplan9-extensions +# +# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> +# SPDX-License-Identifier: AGPL-3.0-or-later + +add_library(libobj INTERFACE) +target_include_directories(libobj SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(libobj INTERFACE + libmisc +) +target_compile_options(libobj INTERFACE "$<$<COMPILE_LANGUAGE:C>:-fplan9-extensions>") + +add_lib_test(libobj test_obj) +add_lib_test(libobj test_nest) diff --git a/libobj/include/libobj/obj.h b/libobj/include/libobj/obj.h new file mode 100644 index 0000000..d8a528a --- /dev/null +++ b/libobj/include/libobj/obj.h @@ -0,0 +1,154 @@ +/* libobj/obj.h - A simple Go-ish object system + * + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef _LIBOBJ_OBJ_H_ +#define _LIBOBJ_OBJ_H_ + +#include <libmisc/macro.h> + +/** + * Use `lo_interface` similarly to how you would use + * `struct`/`enum`/`union` when writing the type of an interface + * value. + */ +#define lo_interface struct + +/** + * Use `LO_INTERFACE` in a .h file to define an interface. + * + * First define a macro named `{iface_name}_LO_IFACE` consisting of a + * series of calls to LO_NEST and/or LO_FUNC, then call + * `LO_INTERFACE({iface_name})`: + * + * #define myiface_LO_IFACE \ + * LO_NEST(wrapped_iface_name) \ + * LO_FUNC(ret_type, func_name, args...) + * LO_INTERFACE(myiface) + * + * Use `lo_interface {iface_name}` as the type of this interface; it + * should not be a pointer type. + * + * If there are any LO_NEST interfaces, this will define a + * `lo_box_{iface_name}_as_{wrapped_iface_name}(obj)` function for + * each. + */ +#define LO_NEST(_ARG_child_iface_name) \ + (lo_nest, _ARG_child_iface_name) +#define LO_FUNC(_ARG_ret_type, _ARG_func_name, ...) \ + (lo_func, _ARG_ret_type, _ARG_func_name __VA_OPT__(,) __VA_ARGS__) +#define LO_INTERFACE(_ARG_iface_name) \ + typedef struct { \ + LM_FOREACH_TUPLE(_ARG_iface_name##_LO_IFACE, \ + _LO_IFACE_VTABLE) \ + } _lo_##_ARG_iface_name##_vtable; \ + struct _ARG_iface_name { \ + void *self; \ + const _lo_##_ARG_iface_name##_vtable *vtable; \ + }; \ + LM_FOREACH_TUPLE(_ARG_iface_name##_LO_IFACE, \ + _LO_IFACE_PROTO, _ARG_iface_name) +#define _LO_IFACE_VTABLE(_tuple_typ, ...) \ + _LO_IFACE_VTABLE_##_tuple_typ(__VA_ARGS__) +#define _LO_IFACE_VTABLE_lo_nest(_ARG_child_iface_name) \ + _lo_##_ARG_child_iface_name##_vtable; +#define _LO_IFACE_VTABLE_lo_func(_ARG_ret_type, _ARG_func_name, ...) \ + _ARG_ret_type (*_ARG_func_name)(void * __VA_OPT__(,) __VA_ARGS__); +#define _LO_IFACE_PROTO(_ARG_iface_name, _tuple_typ, ...) \ + _LO_IFACE_PROTO_##_tuple_typ(_ARG_iface_name, __VA_ARGS__) +#define _LO_IFACE_PROTO_lo_nest(_ARG_iface_name, _ARG_child_iface_name) \ + LM_ALWAYS_INLINE static lo_interface _ARG_child_iface_name \ + box_##_ARG_iface_name##_as_##_ARG_child_iface_name(lo_interface _ARG_iface_name obj) { \ + return (lo_interface _ARG_child_iface_name){ \ + .self = obj.self, \ + .vtable = &obj.vtable->_lo_##_ARG_child_iface_name##_vtable, \ + }; \ + } +#define _LO_IFACE_PROTO_lo_func(_ARG_iface_name, _ARG_ret_type, _ARG_func_name, ...) \ + /* empty */ + +/** + * `LO_NULL(iface_name)` is the null/nil/zero value for `lo_interface {iface_name}`. + */ +#define LO_NULL(_ARG_iface_name) ((lo_interface _ARG_iface_name){0}) + +/** + * `LO_IS_NULL(iface_val)` returns whether `iface_val` is LO_NULL. + */ +#define LO_IS_NULL(_ARG_iface_val) ((_ARG_iface_val).vtable == NULL) + +/** + * `LO_IFACE_EQ(a, b)` returns whether the interface values `a` and + * `b` are the same object. + */ +#define LO_EQ(_ARG_iface_val_a, _ARG_iface_val_b) \ + ((_ARG_iface_val_a).self == (_ARG_iface_val_b).self) + +/** + * Use LO_CALL(obj, method_name, args...) to call a method on an `lo_interface`. + */ +#define LO_CALL(_ARG_obj, _ARG_meth, ...) \ + (_ARG_obj).vtable->_ARG_meth((_ARG_obj).self __VA_OPT__(,) __VA_ARGS__) + +/** + * Use `LO_IMPLEMENTATION_H(iface_name, impl_type, impl_name)` in a .h + * file to declare that `{impl_type}` implements the `{iface_name}` + * interface with functions named `{impl_name}_{method_name}`. + * + * This will also define a `lo_box_{impl_name}_as_{iface_name}(obj)` + * function. + * + * You must also call the LO_IMPLEMENTATION_C in a single .c file. + */ +#define LO_IMPLEMENTATION_H(_ARG_iface_name, _ARG_impl_type, _ARG_impl_name) \ + /* Vtable. */ \ + extern const _lo_##_ARG_iface_name##_vtable \ + _lo_##_ARG_impl_name##_##_ARG_iface_name##_vtable; \ + /* Boxing. */ \ + LM_ALWAYS_INLINE static lo_interface _ARG_iface_name \ + lo_box_##_ARG_impl_name##_as_##_ARG_iface_name(_ARG_impl_type *self) { \ + return (lo_interface _ARG_iface_name){ \ + .self = self, \ + .vtable = &_lo_##_ARG_impl_name##_##_ARG_iface_name##_vtable, \ + }; \ + } + +/** + * Use `LO_IMPLEMENTATION_C(iface_name, impl_type, impl_name[, static])` in a .c + * file to declare that `{impl_type}` implements the `{iface_name}` interface + * with functions named `{impl_name}_{method_name}`. + * + * You must also call the LO_IMPLEMENTATION_H in the corresponding .h file. + * + * If `iface_name` contains a nested interface, then the + * implementation of the nested interfaces must be declared with + * `LO_IMPLEMENTATION_C` first. + */ +#define LO_IMPLEMENTATION_C(_ARG_iface_name, _ARG_impl_type, _ARG_impl_name, ...) \ + /* Method prototypes. */ \ + LM_FOREACH_TUPLE(_ARG_iface_name##_LO_IFACE, \ + _LO_IMPL_PROTO, _ARG_impl_type, _ARG_impl_name, __VA_ARGS__) \ + /* Vtable. */ \ + const _lo_##_ARG_iface_name##_vtable \ + _lo_##_ARG_impl_name##_##_ARG_iface_name##_vtable = { \ + LM_FOREACH_TUPLE(_ARG_iface_name##_LO_IFACE, \ + _LO_IMPL_VTABLE, _ARG_impl_name) \ + }; \ + +#define _LO_IMPL_PROTO(_ARG_impl_type, _ARG_impl_name, _ARG_quals, _tuple_typ, ...) \ + _LO_IMPL_PROTO_##_tuple_typ(_ARG_impl_type, _ARG_impl_name, _ARG_quals, __VA_ARGS__) +#define _LO_IMPL_PROTO_lo_nest(_ARG_impl_type, _ARG_impl_name, _ARG_quals, _ARG_child_iface_name) \ + /* empty */ +#define _LO_IMPL_PROTO_lo_func(_ARG_impl_type, _ARG_impl_name, _ARG_quals, _ARG_ret_type, _ARG_func_name, ...) \ + _ARG_quals _ARG_ret_type _ARG_impl_name##_##_ARG_func_name(_ARG_impl_type * __VA_OPT__(,) __VA_ARGS__); + +#define _LO_IMPL_VTABLE(_ARG_impl_name, _tuple_typ, ...) \ + _LO_IMPL_VTABLE_##_tuple_typ(_ARG_impl_name, __VA_ARGS__) +#define _LO_IMPL_VTABLE_lo_nest(_ARG_impl_name, _ARG_child_iface_name) \ + ._lo_##_ARG_child_iface_name##_vtable = _lo_##_ARG_impl_name##_##_ARG_child_iface_name##_vtable, +#define _LO_IMPL_VTABLE_lo_func(_ARG_impl_name, _ARG_ret_type, _ARG_func_name, ...) \ + ._ARG_func_name = (void*)_ARG_impl_name##_##_ARG_func_name, + +#endif /* _LIBOBJ_OBJ_H_ */ diff --git a/libobj/tests/test.h b/libobj/tests/test.h new file mode 120000 index 0000000..2fb1bd5 --- /dev/null +++ b/libobj/tests/test.h @@ -0,0 +1 @@ +../../libmisc/tests/test.h
\ No newline at end of file diff --git a/libobj/tests/test_nest.c b/libobj/tests/test_nest.c new file mode 100644 index 0000000..c9f9eba --- /dev/null +++ b/libobj/tests/test_nest.c @@ -0,0 +1,73 @@ +/* libobj/tests/test_nest.c - Tests for <libobj/obj.h> + * + * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include <string.h> /* for memcpy() */ + +#include <libobj/obj.h> + +#include "test.h" + +/* interfaces *****************************************************************/ + +#define reader_LO_IFACE \ + LO_FUNC(ssize_t, read, void *, size_t) +LO_INTERFACE(reader) + +#define writer_LO_IFACE \ + LO_FUNC(ssize_t, write, void *, size_t) +LO_INTERFACE(writer) + +#define read_writer_LO_IFACE \ + LO_NEST(reader) \ + LO_NEST(writer) +LO_INTERFACE(read_writer) + +/* implementation header ******************************************************/ + +struct myclass { + size_t len; + char buf[512]; +}; +LO_IMPLEMENTATION_H(reader, struct myclass, myclass) +LO_IMPLEMENTATION_H(writer, struct myclass, myclass) +LO_IMPLEMENTATION_H(read_writer, struct myclass, myclass) + +/* implementation main ********************************************************/ + +LO_IMPLEMENTATION_C(reader, struct myclass, myclass, static) +LO_IMPLEMENTATION_C(writer, struct myclass, myclass, static) +LO_IMPLEMENTATION_C(read_writer, struct myclass, myclass, static) + +static ssize_t myclass_read(struct myclass *self, void *buf, size_t count) { + test_assert(self); + if (count > self->len) + count = self->len; + memcpy(buf, self->buf, count); + return count; +} + +static ssize_t myclass_write(struct myclass *self, void *buf, size_t count) { + test_assert(self); + if (self->len) + return -1; + if (count > sizeof(self->buf)) + count = sizeof(self->buf); + memcpy(self->buf, buf, count); + self->len = count; + return count; +} + +/* main test body *************************************************************/ + +int main() { + struct myclass _obj = {0}; + lo_interface read_writer obj = lo_box_myclass_as_read_writer(&_obj); + test_assert(LO_CALL(obj, write, "Hello", 6) == 6); + char buf[6] = {0}; + test_assert(LO_CALL(obj, read, buf, 3) == 3); + test_assert(memcmp(buf, "Hel\0\0\0", 6) == 0); + return 0; +} diff --git a/libobj/tests/test_obj.c b/libobj/tests/test_obj.c new file mode 100644 index 0000000..89fff68 --- /dev/null +++ b/libobj/tests/test_obj.c @@ -0,0 +1,61 @@ +/* libobj/tests/test_obj.c - Tests for <libobj/obj.h> + * + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include <libobj/obj.h> + +#include "test.h" + +/* `lo_inteface frobber` header ***********************************************/ + +#define frobber_LO_IFACE \ + /** Basic function. */ \ + LO_FUNC(int, frob) \ + /** Function that takes 1 argument. */ \ + LO_FUNC(int, frob1, int) \ + /** Function that returns nothing. */ \ + LO_FUNC(void, frob0) +LO_INTERFACE(frobber) + +/* `struct myclass` header ****************************************************/ + +struct myclass { + int a; +}; +LO_IMPLEMENTATION_H(frobber, struct myclass, myclass) + +/* `struct myclass` implementation ********************************************/ + +LO_IMPLEMENTATION_C(frobber, struct myclass, myclass, static) + +static int myclass_frob(struct myclass *self) { + test_assert(self); + return self->a; +} + +static int myclass_frob1(struct myclass *self, int arg) { + test_assert(self); + return arg; +} + +static void myclass_frob0(struct myclass *self) { + test_assert(self); +} + +/* main test body *************************************************************/ + +#define MAGIC1 909837 +#define MAGIC2 657441 + +int main() { + struct myclass obj = { + .a = MAGIC1, + }; + lo_interface frobber iface = lo_box_myclass_as_frobber(&obj); + test_assert(LO_CALL(iface, frob) == MAGIC1); + test_assert(LO_CALL(iface, frob1, MAGIC2) == MAGIC2); + LO_CALL(iface, frob0); + return 0; +} |