summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2025-03-26 22:18:47 -0600
committerLuke T. Shumaker <lukeshu@lukeshu.com>2025-03-26 22:18:47 -0600
commit6ab74d74ee6dc1663b66d0a9a0471f63ade5659a (patch)
treeb579303cc5df38191ee9e8ad63793fbe4c867c02
parent9f2e2e96321f14da97adda618a7e4721cbb9791c (diff)
parent865bb702f828784a0225b5eae9ed8803094140d5 (diff)
Merge branch 'lukeshu/9p-read-iovec'HEADmain
-rw-r--r--CMakeLists.txt3
-rw-r--r--HACKING.md2
-rw-r--r--cmd/sbc_harness/CMakeLists.txt2
-rw-r--r--lib9p/include/lib9p/srv.h145
-rw-r--r--lib9p/srv.c122
-rw-r--r--lib9p/tests/test_server/CMakeLists.txt2
-rw-r--r--lib9p/tests/test_server/main.c27
-rw-r--r--lib9p_util/static.c63
-rw-r--r--libcr_ipc/CMakeLists.txt9
-rw-r--r--libhw/CMakeLists.txt38
-rw-r--r--libhw_cr/CMakeLists.txt43
-rw-r--r--libhw_cr/alarmclock.c23
-rw-r--r--libhw_cr/host_alarmclock.c (renamed from libhw/host_alarmclock.c)2
-rw-r--r--libhw_cr/host_include/libhw/host_alarmclock.h (renamed from libhw/host_include/libhw/host_alarmclock.h)0
-rw-r--r--libhw_cr/host_include/libhw/host_net.h (renamed from libhw/host_include/libhw/host_net.h)0
-rw-r--r--libhw_cr/host_net.c (renamed from libhw/host_net.c)2
-rw-r--r--libhw_cr/host_util.c (renamed from libhw/host_util.c)2
-rw-r--r--libhw_cr/host_util.h (renamed from libhw/host_util.h)8
-rw-r--r--libhw_cr/rp2040_dma.c (renamed from libhw/rp2040_dma.c)2
-rw-r--r--libhw_cr/rp2040_dma.h (renamed from libhw/rp2040_dma.h)8
-rw-r--r--libhw_cr/rp2040_gpioirq.c (renamed from libhw/rp2040_gpioirq.c)2
-rw-r--r--libhw_cr/rp2040_gpioirq.h (renamed from libhw/rp2040_gpioirq.h)8
-rw-r--r--libhw_cr/rp2040_hwspi.c (renamed from libhw/rp2040_hwspi.c)10
-rw-r--r--libhw_cr/rp2040_hwtimer.c (renamed from libhw/rp2040_hwtimer.c)2
-rw-r--r--libhw_cr/rp2040_include/libhw/rp2040_hwspi.h (renamed from libhw/rp2040_include/libhw/rp2040_hwspi.h)2
-rw-r--r--libhw_cr/rp2040_include/libhw/rp2040_hwtimer.h (renamed from libhw/rp2040_include/libhw/rp2040_hwtimer.h)0
-rw-r--r--libhw_cr/rp2040_include/libhw/w5500.h (renamed from libhw/rp2040_include/libhw/w5500.h)0
-rw-r--r--libhw_cr/w5500.c (renamed from libhw/w5500.c)2
-rw-r--r--libhw_cr/w5500_ll.h (renamed from libhw/w5500_ll.h)71
-rw-r--r--libhw_generic/CMakeLists.txt4
-rw-r--r--libhw_generic/alarmclock.c18
-rw-r--r--libhw_generic/include/libhw/generic/io.h28
-rw-r--r--libhw_generic/io.c98
l---------libhw_generic/tests/test.h1
-rw-r--r--libhw_generic/tests/test_io.c57
35 files changed, 550 insertions, 256 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 386b13c..b379a8f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -98,6 +98,7 @@ function(add_lib_test arg_libname arg_testname)
if (ENABLE_TESTS)
add_executable("${arg_testname}" "tests/${arg_testname}.c")
target_link_libraries("${arg_testname}" "${arg_libname}")
+ target_include_directories("${arg_testname}" PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/tests)
add_test(
NAME "${arg_libname}/${arg_testname}"
COMMAND valgrind --error-exitcode=2 "./${arg_testname}"
@@ -139,7 +140,7 @@ add_subdirectory(libobj)
add_subdirectory(libcr)
add_subdirectory(libcr_ipc)
add_subdirectory(libhw_generic)
-add_subdirectory(libhw)
+add_subdirectory(libhw_cr)
add_subdirectory(libdhcp)
add_subdirectory(libusb)
add_subdirectory(lib9p)
diff --git a/HACKING.md b/HACKING.md
index d522034..9e19c26 100644
--- a/HACKING.md
+++ b/HACKING.md
@@ -18,8 +18,8 @@ Our own tiny cooperative-multitasking OS:
- `libcr/`: The kernel (a simple coroutine library)
- `libcr_ipc/`: IPC primatives (semaphores, mutexes, channels, ...)
for libcr
- - `libhw/`: The hardware drivers
- `libhw_generic/`: The hardware abstraction layer
+ - `libhw_cr/`: The hardware drivers
Libraries for generic parts of what the harness is doing:
diff --git a/cmd/sbc_harness/CMakeLists.txt b/cmd/sbc_harness/CMakeLists.txt
index d7923c2..fa42b47 100644
--- a/cmd/sbc_harness/CMakeLists.txt
+++ b/cmd/sbc_harness/CMakeLists.txt
@@ -24,7 +24,7 @@ target_link_libraries(sbc_harness_objs
libmisc
libusb
libdhcp
- libhw
+ libhw_cr
lib9p
lib9p_util
)
diff --git a/lib9p/include/lib9p/srv.h b/lib9p/include/lib9p/srv.h
index 83aabc0..ff5ebdc 100644
--- a/lib9p/include/lib9p/srv.h
+++ b/lib9p/include/lib9p/srv.h
@@ -37,53 +37,98 @@ int lib9p_srv_acknowledge_flush(struct lib9p_srv_ctx *ctx);
/* interface definitions ******************************************************/
-#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, \
+lo_interface lib9p_srv_fio;
+lo_interface lib9p_srv_dio;
+
+/* FIXME: I don't like that the pointers returned by stat() and
+ * pread() have to remain live after they return. Perhaps a
+ * `respond()`-callback? But that just reads as gross in C.
+ *
+ * FIXME: It would be nice if pread() could return more than 1 iovec.
+ */
+#define lib9p_srv_file_LO_IFACE \
+ /* resource management **********************************************/ \
+ \
+ /** \
+ * free() is be called when all FIDs associated with the file are \
+ * clunked. \
+ * \
+ * free() MUST NOT error. \
+ */ \
+ LO_FUNC(void , free ) \
+ \
+ /** \
+ * qid() is called frequently and returns the current QID of the file. \
+ * The .path field MUST never change, the .type field may change in \
+ * response to wstat() calls (but the QT_DIR bit MUST NOT change), and \
+ * the .vers field may change frequently in response to any number of \
+ * things (wstat(), write(), or non-9P events). \
+ * \
+ * qid() MUST NOT error. \
+ */ \
+ LO_FUNC(struct lib9p_qid , qid ) \
+ \
+ /* non-"opened" generic I/O *****************************************/ \
+ \
+ 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 *) \
+ \
+ /* non-"opened" directory I/O ***************************************/ \
+ \
+ LO_FUNC(lo_interface lib9p_srv_file, dwalk , 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) \
+ \
+ /* open() for I/O ***************************************************/ \
+ \
+ LO_FUNC(lo_interface lib9p_srv_fio , fopen , struct lib9p_srv_ctx *, \
+ bool rd, bool wr, \
+ bool trunc) \
+ LO_FUNC(lo_interface lib9p_srv_dio , dopen , struct lib9p_srv_ctx *)
+LO_INTERFACE(lib9p_srv_file);
+
+#define lib9p_srv_fio_LO_IFACE \
+ LO_FUNC(struct lib9p_qid , qid ) \
+ LO_FUNC(void , iofree ) \
+ LO_FUNC(uint32_t , iounit ) \
+ LO_FUNC(void , pread , struct lib9p_srv_ctx *, \
+ uint32_t byte_count, \
+ uint64_t byte_offset, \
+ struct iovec *ret) \
+ LO_FUNC(uint32_t , pwrite , struct lib9p_srv_ctx *, \
+ void *buf, \
+ uint32_t byte_count, \
uint64_t byte_offset)
-LO_INTERFACE(lib9p_srv_file)
+LO_INTERFACE(lib9p_srv_fio);
+
+/* FIXME: The dio interface just feels clunky. I'm not in a rush to
+ * change it because util9p_static_dir is already implemented and I
+ * don't anticipate the sbc-harness needing another dio
+ * implementation. But if I wanted lib9p to be used outside of
+ * sbc-harness, this is one of the first things that I'd want to
+ * change.
+ */
+#define lib9p_srv_dio_LO_IFACE \
+ LO_FUNC(struct lib9p_qid , qid ) \
+ LO_FUNC(void , iofree ) \
+ 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)
+LO_INTERFACE(lib9p_srv_dio);
#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##_dwalk (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"); }
+ static lo_interface lib9p_srv_dio NAM##_dopen (TYP *, struct lib9p_srv_ctx *) { 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"); }
+ static lo_interface lib9p_srv_fio NAM##_fopen (TYP *, struct lib9p_srv_ctx *, bool, bool, bool) { assert_notreached("not a file"); }
/* main server entrypoints ****************************************************/
@@ -103,16 +148,32 @@ struct lib9p_srv {
};
/**
+ * In an infinite loop, accept a connection and read messages from it until
+ * close; dispatching requests to a pool of lib9p_srv_write_cr() coroutines
+ * with the same `srv`.
+ *
* Will just close the connection if a T-message has a size[4] <7.
*
+ * @param srv: The server configuration and state; has an associated pool of
+ * lib9p_srv_write_cr() coroutines.
+ * @param listener: The listener object to accept connections from.
+ *
* @errno LINUX_EMSGSIZE T-message has size[4] bigger than max_msg_size
* @errno LINUX_EDOM Tversion specified an impossibly small max_msg_size
* @errno LINUX_EOPNOTSUPP T-message has an R-message type, or an unrecognized T-message type
* @errno LINUX_EBADMSG T-message has wrong size[4] for its content, or has invalid UTF-8
* @errno LINUX_ERANGE R-message does not fit into max_msg_size
*/
-
[[noreturn]] void lib9p_srv_read_cr(struct lib9p_srv *srv, lo_interface net_stream_listener listener);
+
+/**
+ * Service requests to the `struct lib9p_srv *srv` argument that have been
+ * read by lib9p_srv_read_cr().
+ *
+ * @param struct lib9p_srv *srv: The server configuration and state; has an
+ * associated pool of lib9p_srv_read_cr()
+ * coroutines.
+ */
COROUTINE lib9p_srv_write_cr(void *_srv);
#endif /* _LIB9P_SRV_H_ */
diff --git a/lib9p/srv.c b/lib9p/srv.c
index 2475baf..50d0b78 100644
--- a/lib9p/srv.c
+++ b/lib9p/srv.c
@@ -60,8 +60,7 @@ struct srv_pathinfo {
* from FIDs. */
unsigned int gc_refcount;
/* References from fids with FIDFLAG_OPEN_R/FIDFLAG_OPEN_W. */
- unsigned int rd_refcount;
- unsigned int wr_refcount;
+ unsigned int io_refcount;
};
#define NAME pathmap
@@ -77,11 +76,18 @@ struct srv_pathinfo {
#define FIDFLAG_OPEN (FIDFLAG_OPEN_R|FIDFLAG_OPEN_W)
struct _srv_fidinfo {
- srv_path_t path;
- uint8_t flags;
- uint32_t iounit;
- size_t dir_idx;
- uint64_t dir_off;
+ srv_path_t path;
+ uint8_t flags;
+ union {
+ struct {
+ lo_interface lib9p_srv_fio io;
+ } file;
+ struct {
+ lo_interface lib9p_srv_dio io;
+ size_t idx;
+ uint64_t off;
+ } dir;
+ };
};
#define NAME fidmap
@@ -122,9 +128,9 @@ struct _srv_sess {
/* mutable */
bool initialized;
bool closing;
- struct pathmap paths;
- struct reqmap reqs;
- struct fidmap fids;
+ struct pathmap paths; /* srv_path_t => lib9p_srv_file + metadata */
+ struct fidmap fids; /* lib9p_fid_t => lib9p_srv_{fio,dio} + metadata */
+ struct reqmap reqs; /* lib9p_tag_t => *_lib9p_srv_req */
};
struct _lib9p_srv_req {
@@ -463,8 +469,7 @@ static inline struct srv_pathinfo *srv_util_pathsave(struct _lib9p_srv_req *ctx,
.file = file,
.parent_dir = parent_path,
.gc_refcount = 0,
- .rd_refcount = 0,
- .wr_refcount = 0,
+ .io_refcount = 0,
});
assert(pathinfo);
if (parent_path != qid.path) {
@@ -515,6 +520,12 @@ static inline struct _srv_fidinfo *srv_util_fidsave(struct _lib9p_srv_req *ctx,
struct _srv_fidinfo *fidinfo = fidmap_load(&ctx->parent_sess->fids, fid);
if (fidinfo) {
if (overwrite) {
+ struct srv_pathinfo *old_pathinfo = pathmap_load(&ctx->parent_sess->paths, fidinfo->path);
+ assert(old_pathinfo);
+ if (srv_util_pathisdir(old_pathinfo))
+ LO_CALL(fidinfo->dir.io, iofree);
+ else
+ LO_CALL(fidinfo->file.io, iofree);
srv_util_pathfree(ctx, fidinfo->path);
} else {
lib9p_error(&ctx->ctx.basectx,
@@ -748,7 +759,7 @@ static void handle_Twalk(struct _lib9p_srv_req *ctx,
break;
}
- lo_interface lib9p_srv_file member_file = LO_CALL(pathinfo->file, dopen, &ctx->ctx, req->wname[resp->nwqid]);
+ lo_interface lib9p_srv_file member_file = LO_CALL(pathinfo->file, dwalk, &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;
@@ -774,6 +785,13 @@ static void handle_Twalk(struct _lib9p_srv_req *ctx,
pathinfo = new_pathinfo;
}
if (resp->nwqid == req->nwname) {
+ if (req->newfid == req->fid) {
+ if (srv_util_pathisdir(pathinfo))
+ LO_CALL(fidinfo->dir.io, iofree);
+ else
+ LO_CALL(fidinfo->file.io, iofree);
+ fidinfo->flags = 0;
+ }
if (!srv_util_fidsave(ctx, req->newfid, pathinfo, req->newfid == req->fid))
srv_util_pathfree(ctx, LO_CALL(pathinfo->file, qid).path);
} else {
@@ -816,7 +834,6 @@ static void handle_Topen(struct _lib9p_srv_req *ctx,
/* Variables. */
lib9p_o_t reqmode = req->mode;
uint8_t fidflags = fidinfo->flags;
- uint32_t iounit = fidinfo->iounit;
/* Check permissions. */
if (reqmode & LIB9P_O_RCLOSE) {
@@ -831,13 +848,13 @@ static void handle_Topen(struct _lib9p_srv_req *ctx,
LINUX_EACCES, "permission denied to remove-on-close");
return;
}
- fidflags = fidflags | FIDFLAG_RCLOSE;
+ fidflags |= FIDFLAG_RCLOSE;
}
struct lib9p_stat stat = LO_CALL(pathinfo->file, stat, &ctx->ctx);
if (lib9p_ctx_has_error(&ctx->ctx.basectx))
return;
lib9p_stat_assert(stat);
- if ((stat.file_mode & LIB9P_DM_EXCL) && (pathinfo->rd_refcount || pathinfo->wr_refcount)) {
+ if ((stat.file_mode & LIB9P_DM_EXCL) && pathinfo->io_refcount) {
lib9p_error(&ctx->ctx.basectx,
LINUX_EEXIST, "exclusive file is already opened");
return;
@@ -870,26 +887,36 @@ 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 = LO_CALL(pathinfo->file, chio, &ctx->ctx,
- fidflags & FIDFLAG_OPEN_R,
- fidflags & FIDFLAG_OPEN_W,
- reqmode & LIB9P_O_TRUNC);
+ uint32_t iounit;
+ struct lib9p_qid qid;
+ if (srv_util_pathisdir(pathinfo)) {
+ fidinfo->dir.io = LO_CALL(pathinfo->file, dopen, &ctx->ctx);
+ assert(LO_IS_NULL(fidinfo->dir.io) == lib9p_ctx_has_error(&ctx->ctx.basectx));
+ if (lib9p_ctx_has_error(&ctx->ctx.basectx))
+ return;
+ fidinfo->dir.idx = 0;
+ fidinfo->dir.off = 0;
+ qid = LO_CALL(fidinfo->dir.io, qid);
+ iounit = 0;
+ } else {
+ fidinfo->file.io = LO_CALL(pathinfo->file, fopen, &ctx->ctx,
+ rd, wr,
+ reqmode & LIB9P_O_TRUNC);
+ assert(LO_IS_NULL(fidinfo->file.io) == lib9p_ctx_has_error(&ctx->ctx.basectx));
if (lib9p_ctx_has_error(&ctx->ctx.basectx))
return;
+ qid = LO_CALL(fidinfo->file.io, qid);
+ iounit = LO_CALL(fidinfo->file.io, iounit);
}
/* Success. */
- if (rd) {
- fidflags = fidflags | FIDFLAG_OPEN_R;
- pathinfo->rd_refcount++;
- }
- if (wr) {
- fidflags = fidflags | FIDFLAG_OPEN_W;
- pathinfo->wr_refcount++;
- }
+ if (rd)
+ fidflags |= FIDFLAG_OPEN_R;
+ if (wr)
+ fidflags |= FIDFLAG_OPEN_W;
+ pathinfo->io_refcount++;
fidinfo->flags = fidflags;
- resp->qid = stat.file_qid;
+ resp->qid = qid;
resp->iounit = iounit;
}
@@ -923,7 +950,6 @@ static void handle_Tread(struct _lib9p_srv_req *ctx,
/* Variables. */
struct srv_pathinfo *pathinfo = pathmap_load(&ctx->parent_sess->paths, fidinfo->path);
assert(pathinfo);
- resp->data = (char *)(&resp[1]);
/* Do it. */
if (srv_util_pathisdir(pathinfo)) {
@@ -931,16 +957,17 @@ static void handle_Tread(struct _lib9p_srv_req *ctx,
size_t idx;
if (req->offset == 0)
idx = 0;
- else if (req->offset == fidinfo->dir_off)
- idx = fidinfo->dir_idx;
+ else if (req->offset == fidinfo->dir.off)
+ idx = fidinfo->dir.idx;
else {
lib9p_errorf(&ctx->ctx.basectx,
LINUX_EINVAL, "invalid offset (must be 0 or %"PRIu64"): %"PRIu64,
- fidinfo->dir_off, req->offset);
+ fidinfo->dir.off, req->offset);
return;
}
/* Do it. */
- size_t num = LO_CALL(pathinfo->file, dread, &ctx->ctx, (uint8_t *)resp->data, req->count, idx);
+ resp->data = (char *)(&resp[1]);
+ size_t num = LO_CALL(fidinfo->dir.io, 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++) {
@@ -950,10 +977,18 @@ static void handle_Tread(struct _lib9p_srv_req *ctx,
}
resp->count = len;
/* Remember. */
- fidinfo->dir_idx = idx+num;
- fidinfo->dir_off = req->offset + len;
- } else
- resp->count = LO_CALL(pathinfo->file, pread, &ctx->ctx, resp->data, req->count, req->offset);
+ fidinfo->dir.idx = idx+num;
+ fidinfo->dir.off = req->offset + len;
+ } else {
+ struct iovec iov;
+ LO_CALL(fidinfo->file.io, pread, &ctx->ctx, req->count, req->offset, &iov);
+ if (!lib9p_ctx_has_error(&ctx->ctx.basectx)) {
+ resp->count = iov.iov_len;
+ resp->data = iov.iov_base;
+ if (resp->count > req->count)
+ resp->count = req->count;
+ }
+ }
}
static void handle_Twrite(struct _lib9p_srv_req *ctx,
@@ -979,7 +1014,7 @@ static void handle_Twrite(struct _lib9p_srv_req *ctx,
assert(pathinfo);
/* Do it. */
- resp->count = LO_CALL(pathinfo->file, pwrite, &ctx->ctx, req->data, req->count, req->offset);
+ resp->count = LO_CALL(fidinfo->file.io, pwrite, &ctx->ctx, req->data, req->count, req->offset);
}
static void clunkremove(struct _lib9p_srv_req *ctx, lib9p_fid_t fid, bool remove) {
@@ -1012,6 +1047,13 @@ static void clunkremove(struct _lib9p_srv_req *ctx, lib9p_fid_t fid, bool remove
}
clunk:
+ if (fidinfo->flags & FIDFLAG_OPEN) {
+ if (srv_util_pathisdir(pathinfo))
+ LO_CALL(fidinfo->dir.io, iofree);
+ else
+ LO_CALL(fidinfo->file.io, iofree);
+ pathinfo->io_refcount--;
+ }
srv_util_pathfree(ctx, LO_CALL(pathinfo->file, qid).path);
fidmap_del(&ctx->parent_sess->fids, fid);
}
diff --git a/lib9p/tests/test_server/CMakeLists.txt b/lib9p/tests/test_server/CMakeLists.txt
index 74a759d..5313917 100644
--- a/lib9p/tests/test_server/CMakeLists.txt
+++ b/lib9p/tests/test_server/CMakeLists.txt
@@ -18,7 +18,7 @@ target_link_libraries(test_server_objs
libmisc
lib9p
lib9p_util
- libhw
+ libhw_cr
)
# Analyze the stack ############################################################
diff --git a/lib9p/tests/test_server/main.c b/lib9p/tests/test_server/main.c
index 074dbe7..c759029 100644
--- a/lib9p/tests/test_server/main.c
+++ b/lib9p/tests/test_server/main.c
@@ -48,7 +48,10 @@ struct api_file {
uint64_t pathnum;
};
LO_IMPLEMENTATION_H(lib9p_srv_file, struct api_file, api)
+LO_IMPLEMENTATION_H(lib9p_srv_fio, struct api_file, api)
+
LO_IMPLEMENTATION_C(lib9p_srv_file, struct api_file, api, static)
+LO_IMPLEMENTATION_C(lib9p_srv_fio, struct api_file, api, static)
static void api_free(struct api_file *self) {
assert(self);
@@ -61,11 +64,6 @@ static struct lib9p_qid api_qid(struct api_file *self) {
.path = self->pathnum,
};
}
-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);
@@ -101,6 +99,21 @@ static void api_remove(struct api_file *self, struct lib9p_srv_ctx *ctx) {
LIB9P_SRV_NOTDIR(struct api_file, api)
+static lo_interface lib9p_srv_fio api_fopen(struct api_file *self, struct lib9p_srv_ctx *ctx, bool, bool, bool) {
+ assert(self);
+ assert(ctx);
+ return lo_box_api_as_lib9p_srv_fio(self);
+}
+
+static void api_iofree(struct api_file *self) {
+ assert(self);
+}
+
+static uint32_t api_iounit(struct api_file *self) {
+ assert(self);
+ return 0;
+}
+
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);
@@ -111,7 +124,9 @@ static uint32_t api_pwrite(struct api_file *self, struct lib9p_srv_ctx *ctx, voi
LO_CALL(lo_box_hostnet_tcplist_as_net_stream_listener(&globals.listeners[i]), close);
return byte_count;
}
-static uint32_t api_pread(struct api_file *, struct lib9p_srv_ctx *, void *, uint32_t, uint64_t) {
+static void api_pread(struct api_file *LM_UNUSED(self), struct lib9p_srv_ctx *LM_UNUSED(ctx),
+ uint32_t LM_UNUSED(byte_count), uint64_t LM_UNUSED(byte_offset),
+ struct iovec *LM_UNUSED(ret)) {
assert_notreached("not readable");
}
diff --git a/lib9p_util/static.c b/lib9p_util/static.c
index a6ea8f6..7f1e6b7 100644
--- a/lib9p_util/static.c
+++ b/lib9p_util/static.c
@@ -12,6 +12,12 @@
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);
+LO_IMPLEMENTATION_H(lib9p_srv_dio, struct util9p_static_dir, util9p_static_dir);
+LO_IMPLEMENTATION_C(lib9p_srv_dio, struct util9p_static_dir, util9p_static_dir, static);
+
+LO_IMPLEMENTATION_H(lib9p_srv_fio, struct util9p_static_file, util9p_static_file);
+LO_IMPLEMENTATION_C(lib9p_srv_fio, struct util9p_static_file, util9p_static_file, static);
+
/* dir ************************************************************************/
static void util9p_static_dir_free(struct util9p_static_dir *self) {
@@ -26,11 +32,6 @@ static struct lib9p_qid util9p_static_dir_qid(struct util9p_static_dir *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(struct util9p_static_dir *self, struct lib9p_srv_ctx *ctx) {
assert(self);
@@ -68,7 +69,7 @@ static void util9p_static_dir_remove(struct util9p_static_dir *self, struct lib9
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,
+static lo_interface lib9p_srv_file util9p_static_dir_dwalk(struct util9p_static_dir *self, struct lib9p_srv_ctx *ctx,
struct lib9p_s childname) {
assert(self);
assert(ctx);
@@ -97,6 +98,16 @@ static lo_interface lib9p_srv_file util9p_static_dir_dcreate(struct util9p_stati
return LO_NULL(lib9p_srv_file);
}
+LIB9P_SRV_NOTFILE(struct util9p_static_dir, util9p_static_dir);
+
+static lo_interface lib9p_srv_dio util9p_static_dir_dopen(struct util9p_static_dir *self, struct lib9p_srv_ctx *ctx) {
+ assert(self);
+ assert(ctx);
+ return lo_box_util9p_static_dir_as_lib9p_srv_dio(self);
+}
+static void util9p_static_dir_iofree(struct util9p_static_dir *self) {
+ assert(self);
+}
static size_t util9p_static_dir_dread(struct util9p_static_dir *self, struct lib9p_srv_ctx *ctx,
uint8_t *buf,
uint32_t byte_count,
@@ -126,8 +137,6 @@ static size_t util9p_static_dir_dread(struct util9p_static_dir *self, struct lib
return obj_offset - _obj_offset;
}
-LIB9P_SRV_NOTFILE(struct util9p_static_dir, util9p_static_dir)
-
/* file ***********************************************************************/
static void util9p_static_file_free(struct util9p_static_file *self) {
@@ -142,11 +151,6 @@ static struct lib9p_qid util9p_static_file_qid(struct util9p_static_file *self)
.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);
@@ -196,31 +200,46 @@ static void util9p_static_file_remove(struct util9p_static_file *self, struct li
lib9p_error(&ctx->basectx, LINUX_EROFS, "read-only part of filesystem");
}
-LIB9P_SRV_NOTDIR(struct util9p_static_file, util9p_static_file)
+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) {
+static lo_interface lib9p_srv_fio util9p_static_file_fopen(struct util9p_static_file *self, struct lib9p_srv_ctx *ctx,
+ bool rd, bool wr, bool trunc) {
assert(self);
assert(ctx);
+ assert(rd);
+ assert(!wr);
+ assert(!trunc);
+ return lo_box_util9p_static_file_as_lib9p_srv_fio(self);
+}
+static void util9p_static_file_iofree(struct util9p_static_file *self) {
+ assert(self);
+}
+static uint32_t util9p_static_file_iounit(struct util9p_static_file *self) {
+ assert(self);
+ return 0;
+}
+static void util9p_static_file_pread(struct util9p_static_file *self, struct lib9p_srv_ctx *ctx,
+ uint32_t byte_count, uint64_t byte_offset,
+ struct iovec *ret) {
+ assert(self);
+ assert(ctx);
+ assert(ret);
size_t data_size = util9p_static_file_size(self);
if (byte_offset > (uint64_t)data_size) {
lib9p_error(&ctx->basectx,
LINUX_EINVAL, "offset is past end-of-file length");
- return 0;
+ return;
}
size_t beg_off = (size_t)byte_offset;
size_t end_off = beg_off + (size_t)byte_count;
if (end_off > data_size)
end_off = data_size;
- memcpy(buf, &self->data_start[beg_off], end_off-beg_off);
- return (uint32_t)(end_off-beg_off);
+ ret->iov_base = &self->data_start[beg_off];
+ ret->iov_len = end_off-beg_off;
}
-
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),
diff --git a/libcr_ipc/CMakeLists.txt b/libcr_ipc/CMakeLists.txt
index 4590bdd..3746584 100644
--- a/libcr_ipc/CMakeLists.txt
+++ b/libcr_ipc/CMakeLists.txt
@@ -21,9 +21,6 @@ set(ipc_tests
rpc
sema
)
-if (ENABLE_TESTS)
- foreach(test IN LISTS ipc_tests)
- add_lib_test(libcr_ipc "test_${test}")
- target_include_directories("test_${test}" PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/tests)
- endforeach()
-endif()
+foreach(test IN LISTS ipc_tests)
+ add_lib_test(libcr_ipc "test_${test}")
+endforeach()
diff --git a/libhw/CMakeLists.txt b/libhw/CMakeLists.txt
deleted file mode 100644
index 242a3fa..0000000
--- a/libhw/CMakeLists.txt
+++ /dev/null
@@ -1,38 +0,0 @@
-# libhw/CMakeLists.txt - Device drivers
-#
-# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-add_library(libhw INTERFACE)
-target_link_libraries(libhw INTERFACE
- libhw_generic
-)
-
-if (PICO_PLATFORM STREQUAL "rp2040")
- target_include_directories(libhw SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/rp2040_include)
- target_link_libraries(libhw INTERFACE
- libcr_ipc
- )
- target_sources(libhw INTERFACE
- rp2040_dma.c
- rp2040_gpioirq.c
- rp2040_hwspi.c
- rp2040_hwtimer.c
- w5500.c
- )
- target_link_libraries(libhw INTERFACE
- hardware_gpio
- hardware_irq
- hardware_spi
- hardware_timer
- )
-endif()
-
-if (PICO_PLATFORM STREQUAL "host")
- target_include_directories(libhw SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/host_include)
- target_sources(libhw INTERFACE
- host_util.c
- host_alarmclock.c
- host_net.c
- )
-endif()
diff --git a/libhw_cr/CMakeLists.txt b/libhw_cr/CMakeLists.txt
new file mode 100644
index 0000000..caeac21
--- /dev/null
+++ b/libhw_cr/CMakeLists.txt
@@ -0,0 +1,43 @@
+# libhw_cr/CMakeLists.txt - Device drivers for libcr
+#
+# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+add_library(libhw_cr INTERFACE)
+target_link_libraries(libhw_cr INTERFACE
+ libhw_generic
+ libcr
+)
+
+target_sources(libhw_cr INTERFACE
+ alarmclock.c
+)
+
+if (PICO_PLATFORM STREQUAL "rp2040")
+ target_include_directories(libhw_cr SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/rp2040_include)
+ target_link_libraries(libhw_cr INTERFACE
+ libcr_ipc
+ )
+ target_sources(libhw_cr INTERFACE
+ rp2040_dma.c
+ rp2040_gpioirq.c
+ rp2040_hwspi.c
+ rp2040_hwtimer.c
+ w5500.c
+ )
+ target_link_libraries(libhw_cr INTERFACE
+ hardware_gpio
+ hardware_irq
+ hardware_spi
+ hardware_timer
+ )
+endif()
+
+if (PICO_PLATFORM STREQUAL "host")
+ target_include_directories(libhw_cr SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/host_include)
+ target_sources(libhw_cr INTERFACE
+ host_util.c
+ host_alarmclock.c
+ host_net.c
+ )
+endif()
diff --git a/libhw_cr/alarmclock.c b/libhw_cr/alarmclock.c
new file mode 100644
index 0000000..6eec52b
--- /dev/null
+++ b/libhw_cr/alarmclock.c
@@ -0,0 +1,23 @@
+/* libhw_cr/alarmclock.c - sleep() implementation for libcr
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libcr/coroutine.h>
+
+#include <libhw/generic/alarmclock.h>
+
+static void alarmclock_sleep_intrhandler(void *_arg) {
+ cid_t cid = *(cid_t *)_arg;
+ cr_unpause_from_intrhandler(cid);
+}
+
+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;
+ LO_CALL(clock, add_trigger, &trigger, abstime_ns, alarmclock_sleep_intrhandler, &cid);
+ cr_pause_and_yield();
+ cr_restore_interrupts(saved);
+}
diff --git a/libhw/host_alarmclock.c b/libhw_cr/host_alarmclock.c
index 19ece7c..2f255e0 100644
--- a/libhw/host_alarmclock.c
+++ b/libhw_cr/host_alarmclock.c
@@ -1,4 +1,4 @@
-/* libhw/host_alarmclock.c - <libhw/generic/alarmclock.h> implementation for POSIX hosts
+/* libhw_cr/host_alarmclock.c - <libhw/generic/alarmclock.h> implementation for POSIX hosts
*
* Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/libhw/host_include/libhw/host_alarmclock.h b/libhw_cr/host_include/libhw/host_alarmclock.h
index 89df68a..89df68a 100644
--- a/libhw/host_include/libhw/host_alarmclock.h
+++ b/libhw_cr/host_include/libhw/host_alarmclock.h
diff --git a/libhw/host_include/libhw/host_net.h b/libhw_cr/host_include/libhw/host_net.h
index fced229..fced229 100644
--- a/libhw/host_include/libhw/host_net.h
+++ b/libhw_cr/host_include/libhw/host_net.h
diff --git a/libhw/host_net.c b/libhw_cr/host_net.c
index e89cd66..f1c988c 100644
--- a/libhw/host_net.c
+++ b/libhw_cr/host_net.c
@@ -1,4 +1,4 @@
-/* libhw/host_net.c - <libhw/generic/net.h> implementation for hosted glibc
+/* libhw_cr/host_net.c - <libhw/generic/net.h> implementation for hosted glibc
*
* Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/libhw/host_util.c b/libhw_cr/host_util.c
index b862e39..958ed9c 100644
--- a/libhw/host_util.c
+++ b/libhw_cr/host_util.c
@@ -1,4 +1,4 @@
-/* libhw/host_util.c - Utilities for GNU/Linux hosts
+/* libhw_cr/host_util.c - Utilities for GNU/Linux hosts
*
* Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/libhw/host_util.h b/libhw_cr/host_util.h
index f3ef6b4..8c53fab 100644
--- a/libhw/host_util.h
+++ b/libhw_cr/host_util.h
@@ -1,11 +1,11 @@
-/* libhw/host_util.h - Utilities for GNU/Linux hosts
+/* libhw_cr/host_util.h - Utilities for GNU/Linux hosts
*
* Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-#ifndef _LIBHW_HOST_UTIL_H_
-#define _LIBHW_HOST_UTIL_H_
+#ifndef _LIBHW_CR_HOST_UTIL_H_
+#define _LIBHW_CR_HOST_UTIL_H_
#include <time.h> /* for struct timespec */
#include <sys/time.h> /* for struct timeval */
@@ -44,4 +44,4 @@ static inline uint64_t ns_from_host_ns_time(host_ns_time_t host_time) {
((uint64_t)host_time.tv_nsec);
}
-#endif /* _LIBHW_HOST_UTIL_H_ */
+#endif /* _LIBHW_CR_HOST_UTIL_H_ */
diff --git a/libhw/rp2040_dma.c b/libhw_cr/rp2040_dma.c
index dfbf136..69116bf 100644
--- a/libhw/rp2040_dma.c
+++ b/libhw_cr/rp2040_dma.c
@@ -1,4 +1,4 @@
-/* libhw/rp2040_dma.c - Utilities for sharing the DMA IRQs
+/* libhw_cr/rp2040_dma.c - Utilities for sharing the DMA IRQs
*
* Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/libhw/rp2040_dma.h b/libhw_cr/rp2040_dma.h
index c8d69b1..e295adf 100644
--- a/libhw/rp2040_dma.h
+++ b/libhw_cr/rp2040_dma.h
@@ -1,4 +1,4 @@
-/* libhw/rp2040_dma.h - Utilities for using DMA on the RP2040 (replaces <hardware/dma.h>)
+/* libhw_cr/rp2040_dma.h - Utilities for using DMA on the RP2040 (replaces <hardware/dma.h>)
*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
* SPDX-License-Identifier: BSD-3-Clause
@@ -7,8 +7,8 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-#ifndef _LIBHW_RP2040_DMA_H_
-#define _LIBHW_RP2040_DMA_H_
+#ifndef _LIBHW_CR_RP2040_DMA_H_
+#define _LIBHW_CR_RP2040_DMA_H_
#include <assert.h>
#include <stdint.h> /* for uint32_t */
@@ -125,4 +125,4 @@ struct dma_alias3_short3 { READ_ADDR ; };
#define _DMA_TRIGGER_trans_count struct dma_alias1
#define _DMA_TRIGGER_ctrl struct dma_alias0
-#endif /* _LIBHW_RP2040_DMA_H_ */
+#endif /* _LIBHW_CR_RP2040_DMA_H_ */
diff --git a/libhw/rp2040_gpioirq.c b/libhw_cr/rp2040_gpioirq.c
index 2f0ceac..1ae74f9 100644
--- a/libhw/rp2040_gpioirq.c
+++ b/libhw_cr/rp2040_gpioirq.c
@@ -1,4 +1,4 @@
-/* libhw/rp2040_gpioirq.c - Utilities for sharing the GPIO IRQ (IO_IRQ_BANK0)
+/* libhw_cr/rp2040_gpioirq.c - Utilities for sharing the GPIO IRQ (IO_IRQ_BANK0)
*
* Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/libhw/rp2040_gpioirq.h b/libhw_cr/rp2040_gpioirq.h
index 06041c9..75a264f 100644
--- a/libhw/rp2040_gpioirq.h
+++ b/libhw_cr/rp2040_gpioirq.h
@@ -1,11 +1,11 @@
-/* libhw/rp2040_gpioirq.h - Utilities for sharing the GPIO IRQ (IO_IRQ_BANK0)
+/* libhw_cr/rp2040_gpioirq.h - Utilities for sharing the GPIO IRQ (IO_IRQ_BANK0)
*
* Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-#ifndef _LIBHW_RP2040_GPIOIRQ_H_
-#define _LIBHW_RP2040_GPIOIRQ_H_
+#ifndef _LIBHW_CR_RP2040_GPIOIRQ_H_
+#define _LIBHW_CR_RP2040_GPIOIRQ_H_
#include <hardware/gpio.h> /* for enum gpio_irq_level */
@@ -30,4 +30,4 @@ typedef void (*gpioirq_handler_t)(void *arg, uint gpio, enum gpio_irq_level even
*/
void gpioirq_set_and_enable_exclusive_handler(uint gpio, enum gpio_irq_level event, gpioirq_handler_t fn, void *arg);
-#endif /* _LIBHW_RP2040_GPIOIRQ_H_ */
+#endif /* _LIBHW_CR_RP2040_GPIOIRQ_H_ */
diff --git a/libhw/rp2040_hwspi.c b/libhw_cr/rp2040_hwspi.c
index f747b1e..d4adb11 100644
--- a/libhw/rp2040_hwspi.c
+++ b/libhw_cr/rp2040_hwspi.c
@@ -1,4 +1,4 @@
-/* libhw/rp2040_hwspi.c - <libhw/generic/spi.h> implementation for the RP2040's ARM Primecell SSP (PL022)
+/* libhw_cr/rp2040_hwspi.c - <libhw/generic/spi.h> implementation for the RP2040's ARM Primecell SSP (PL022)
*
* Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -185,23 +185,23 @@ static void rp2040_hwspi_readwritev(struct rp2040_hwspi *self, const struct dupl
if (!iov[i].iov_len)
continue;
tx_data_blocks[j] = (typeof(tx_data_blocks[0])){
- .read_addr = iov[i].iov_write_src ?: &self->bogus_data,
+ .read_addr = (iov[i].iov_write_from != IOVEC_DISCARD) ? iov[i].iov_write_from : &self->bogus_data,
.write_addr = &spi_get_hw(self->inst)->dr,
.trans_count = iov[i].iov_len,
.ctrl = (DMA_CTRL_ENABLE
| DMA_CTRL_DATA_SIZE(DMA_SIZE_8)
- | (iov[i].iov_write_src ? DMA_CTRL_INCR_READ : 0)
+ | ((iov[i].iov_write_from != IOVEC_DISCARD) ? DMA_CTRL_INCR_READ : 0)
| DMA_CTRL_CHAIN_TO(self->dma_tx_ctrl)
| DMA_CTRL_TREQ_SEL(SPI_DREQ_NUM(self->inst, true))
| DMA_CTRL_IRQ_QUIET),
};
rx_data_blocks[j] = (typeof(rx_data_blocks[0])){
.read_addr = &spi_get_hw(self->inst)->dr,
- .write_addr = iov[i].iov_read_dst ?: &bogus_rx_dst,
+ .write_addr = (iov[i].iov_read_to != IOVEC_DISCARD) ? iov[i].iov_read_to : &bogus_rx_dst,
.trans_count = iov[i].iov_len,
.ctrl = (DMA_CTRL_ENABLE
| DMA_CTRL_DATA_SIZE(DMA_SIZE_8)
- | (iov[i].iov_read_dst ? DMA_CTRL_INCR_WRITE : 0)
+ | ((iov[i].iov_read_to != IOVEC_DISCARD) ? DMA_CTRL_INCR_WRITE : 0)
| DMA_CTRL_CHAIN_TO(self->dma_rx_ctrl)
| DMA_CTRL_TREQ_SEL(SPI_DREQ_NUM(self->inst, false))
| DMA_CTRL_IRQ_QUIET),
diff --git a/libhw/rp2040_hwtimer.c b/libhw_cr/rp2040_hwtimer.c
index ada6246..8227abb 100644
--- a/libhw/rp2040_hwtimer.c
+++ b/libhw_cr/rp2040_hwtimer.c
@@ -1,4 +1,4 @@
-/* libhw/rp2040_hwtimer.c - <libhw/generic/alarmclock.h> implementation for the RP2040's hardware timer
+/* libhw_cr/rp2040_hwtimer.c - <libhw/generic/alarmclock.h> implementation for the RP2040's hardware timer
*
* Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/libhw/rp2040_include/libhw/rp2040_hwspi.h b/libhw_cr/rp2040_include/libhw/rp2040_hwspi.h
index eb54cdc..9d99f7b 100644
--- a/libhw/rp2040_include/libhw/rp2040_hwspi.h
+++ b/libhw_cr/rp2040_include/libhw/rp2040_hwspi.h
@@ -48,7 +48,7 @@ LO_IMPLEMENTATION_H(spi, struct rp2040_hwspi, rp2040_hwspi)
* @param mode : enum spi_mode : the SPI mode; SPI_MODE_{0..3}
* @param baudrate_hz : uint : baudrate in Hz
* @param min_delay_ns: uint64_t : minimum time for pin_cs to be high between messages
- * @param bogus_data : uint8_t : bogus data to write when .iov_write_src is NULL
+ * @param bogus_data : uint8_t : bogus data to write when .iov_write_from is IOVEC_DISCARD
* @param pin_miso : uint : pin number; 0, 4, 16, or 20 for _HWSPI_0; 8, 12, 24, or 28 for _HWSPI_1
* @param pin_mosi : uint : pin number; 3, 7, 19, or 23 for _HWSPI_0; 11, 15, or 27 for _HWSPI_1
* @param pin_clk : uint : pin number; 2, 6, 18, or 22 for _HWSPI_0; 10, 14, or 26 for _HWSPI_1
diff --git a/libhw/rp2040_include/libhw/rp2040_hwtimer.h b/libhw_cr/rp2040_include/libhw/rp2040_hwtimer.h
index 40e4172..40e4172 100644
--- a/libhw/rp2040_include/libhw/rp2040_hwtimer.h
+++ b/libhw_cr/rp2040_include/libhw/rp2040_hwtimer.h
diff --git a/libhw/rp2040_include/libhw/w5500.h b/libhw_cr/rp2040_include/libhw/w5500.h
index 51effba..51effba 100644
--- a/libhw/rp2040_include/libhw/w5500.h
+++ b/libhw_cr/rp2040_include/libhw/w5500.h
diff --git a/libhw/w5500.c b/libhw_cr/w5500.c
index c4d36f3..295add2 100644
--- a/libhw/w5500.c
+++ b/libhw_cr/w5500.c
@@ -1,4 +1,4 @@
-/* libhw/w5500.c - <libhw/generic/net.h> implementation for the WIZnet W5500 chip
+/* libhw_cr/w5500.c - <libhw/generic/net.h> implementation for the WIZnet W5500 chip
*
* Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/libhw/w5500_ll.h b/libhw_cr/w5500_ll.h
index 3822ad4..2506cd2 100644
--- a/libhw/w5500_ll.h
+++ b/libhw_cr/w5500_ll.h
@@ -1,4 +1,4 @@
-/* libhw/w5500_ll.h - Low-level header library for the WIZnet W5500 chip
+/* libhw_cr/w5500_ll.h - Low-level header library for the WIZnet W5500 chip
*
* Based entirely on the W5500 datasheet, v1.1.0.
* https://docs.wiznet.io/img/products/w5500/W5500_ds_v110e.pdf
@@ -7,8 +7,8 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-#ifndef _LIBHW_W5500_LL_H_
-#define _LIBHW_W5500_LL_H_
+#ifndef _LIBHW_CR_W5500_LL_H_
+#define _LIBHW_CR_W5500_LL_H_
#include <alloca.h> /* for alloca() */
#include <stdint.h> /* for uint{n}_t */
@@ -93,39 +93,15 @@ w5500ll_writev(
(uint8_t)(addr & 0xFF),
(block & CTL_MASK_BLOCK) | CTL_W | CTL_OM_VDM,
};
- struct duplex_iovec *inner = alloca(sizeof(struct duplex_iovec)*(iovcnt+1));
+ int inner_cnt = 1+io_slice_cnt(iov, iovcnt, skip, max);
+ struct duplex_iovec *inner = alloca(sizeof(struct duplex_iovec)*inner_cnt);
inner[0] = (struct duplex_iovec){
- .iov_read_dst = NULL,
- .iov_write_src = header,
- .iov_len = sizeof(header),
+ .iov_read_to = IOVEC_DISCARD,
+ .iov_write_from = header,
+ .iov_len = sizeof(header),
};
- int j = 1;
- size_t skipped = 0, done = 0;
- for (int i = 0; i < iovcnt && (max == 0 || done < max); i++) {
- if (skipped < skip) {
- if (skip - skipped >= iov[i].iov_len) {
- skipped += iov[i].iov_len;
- continue;
- }
- inner[j] = (struct duplex_iovec){
- .iov_read_dst = NULL,
- .iov_write_src = iov[i].iov_base+(skip-skipped),
- .iov_len = iov[i].iov_len-(skip-skipped),
- };
- skipped = skip;
- } else {
- inner[j] = (struct duplex_iovec){
- .iov_read_dst = NULL,
- .iov_write_src = iov[i].iov_base,
- .iov_len = iov[i].iov_len,
- };
- }
- done += inner[j].iov_len;
- if (max > 0 && done > max)
- inner[j].iov_len -= done - max;
- j++;
- };
- LO_CALL(spidev, readwritev, inner, j);
+ io_slice_wr_to_duplex(&inner[1], iov, iovcnt, skip, max);
+ LO_CALL(spidev, readwritev, inner, inner_cnt);
}
static inline void
@@ -154,26 +130,15 @@ w5500ll_readv(
(uint8_t)(addr & 0xFF),
(block & CTL_MASK_BLOCK) | CTL_R | CTL_OM_VDM,
};
- struct duplex_iovec *inner = alloca(sizeof(struct duplex_iovec)*(iovcnt+1));
+ int inner_cnt = 1+io_slice_cnt(iov, iovcnt, 0, max);
+ struct duplex_iovec *inner = alloca(sizeof(struct duplex_iovec)*inner_cnt);
inner[0] = (struct duplex_iovec){
- .iov_read_dst = NULL,
- .iov_write_src = header,
- .iov_len = sizeof(header),
- };
- int j = 1;
- size_t done = 0;
- for (int i = 0; i < iovcnt && (max == 0 || done < max); i++) {
- inner[j] = (struct duplex_iovec){
- .iov_read_dst = iov[i].iov_base,
- .iov_write_src = NULL,
- .iov_len = iov[i].iov_len,
- };
- done += inner[j].iov_len;
- if (max > 0 && done > max)
- inner[j].iov_len -= done - max;
- j++;
+ .iov_read_to = IOVEC_DISCARD,
+ .iov_write_from = header,
+ .iov_len = sizeof(header),
};
- LO_CALL(spidev, readwritev, inner, j);
+ io_slice_rd_to_duplex(&inner[1], iov, iovcnt, 0, max);
+ LO_CALL(spidev, readwritev, inner, inner_cnt);
}
/* Common chip-wide registers. ***********************************************/
@@ -458,4 +423,4 @@ static_assert(sizeof(struct w5500ll_block_sock_reg) == 0x30);
val; \
})
-#endif /* _LIBHW_W5500_LL_H_ */
+#endif /* _LIBHW_CR_W5500_LL_H_ */
diff --git a/libhw_generic/CMakeLists.txt b/libhw_generic/CMakeLists.txt
index e38fbe9..5a6014b 100644
--- a/libhw_generic/CMakeLists.txt
+++ b/libhw_generic/CMakeLists.txt
@@ -8,10 +8,12 @@ target_include_directories(libhw_generic SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE
target_link_libraries(libhw_generic INTERFACE
libmisc
libobj
- libcr
)
target_sources(libhw_generic INTERFACE
alarmclock.c
+ io.c
net.c
)
+
+add_lib_test(libhw_generic test_io)
diff --git a/libhw_generic/alarmclock.c b/libhw_generic/alarmclock.c
index 7fd049e..31fbbaf 100644
--- a/libhw_generic/alarmclock.c
+++ b/libhw_generic/alarmclock.c
@@ -1,25 +1,9 @@
-/* libhw_generic/alarmclock.c - Device-independent <libhw/generic/alarmclock.h> utilities
+/* libhw_generic/alarmclock.c - Device-independent <libhw/generic/alarmclock.h> storage
*
* Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-#include <libcr/coroutine.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(lo_interface alarmclock clock, uint64_t abstime_ns) {
- bool saved = cr_save_and_disable_interrupts();
- cid_t cid = cr_getcid();
- struct alarmclock_trigger trigger;
- 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/io.h b/libhw_generic/include/libhw/generic/io.h
index a7f7378..7825c9f 100644
--- a/libhw_generic/include/libhw/generic/io.h
+++ b/libhw_generic/include/libhw/generic/io.h
@@ -8,6 +8,7 @@
#define _LIBHW_GENERIC_IO_H_
#include <stddef.h> /* for size_t */
+#include <stdint.h> /* for uintptr_t */
#include <sys/types.h> /* for ssize_t */
#include <libobj/obj.h>
@@ -23,12 +24,35 @@ struct iovec {
};
#endif
+#define IOVEC_DISCARD ((void*)(~((uintptr_t)0)))
+
struct duplex_iovec {
- void *iov_read_dst;
- void *iov_write_src;
+ /**
+ * NULL is a valid pointer value in iov_read_to and
+ * iov_write_from. To skip a read or write, use the special
+ * value IOVEC_DISCARD.
+ */
+ void *iov_read_to;
+ void *iov_write_from;
size_t iov_len;
};
+/* utilities ******************************************************************/
+
+/* slice iovec lists */
+int io_slice_cnt ( const struct iovec *src, int src_cnt, size_t byte_start, size_t byte_max_cnt);
+void io_slice (struct iovec *dst, const struct iovec *src, int src_cnt, size_t byte_start, size_t byte_max_cnt);
+int io_duplex_slice_cnt( const struct duplex_iovec *src, int src_cnt, size_t byte_start, size_t byte_max_cnt);
+void io_duplex_slice (struct duplex_iovec *dst, const struct duplex_iovec *src, int src_cnt, size_t byte_start, size_t byte_max_cnt);
+
+/* convert iovec lists */
+void io_rd_to_duplex(struct duplex_iovec *dst, const struct iovec *src, int iovcnt);
+void io_wr_to_duplex(struct duplex_iovec *dst, const struct iovec *src, int iovcnt);
+
+/* slice and convert in one go */
+void io_slice_rd_to_duplex(struct duplex_iovec *dst, const struct iovec *src, int src_cnt, size_t byte_start, size_t byte_max_cnt);
+void io_slice_wr_to_duplex(struct duplex_iovec *dst, const struct iovec *src, int src_cnt, size_t byte_start, size_t byte_max_cnt);
+
/* basic interfaces ***********************************************************/
/**
diff --git a/libhw_generic/io.c b/libhw_generic/io.c
new file mode 100644
index 0000000..4ebff10
--- /dev/null
+++ b/libhw_generic/io.c
@@ -0,0 +1,98 @@
+/* libhw_generic/io.c - Utilities for device-independent I/O definitions
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libmisc/assert.h>
+
+#include <libhw/generic/io.h>
+
+#define IOV_ITER(ACTION) \
+ assert(src_cnt >= 0); \
+ assert(src_cnt == 0 || src); \
+ \
+ const size_t byte_end = byte_beg + byte_max_cnt; \
+ int j = 0; \
+ size_t byte_pos = 0; \
+ for (int i = 0; \
+ i < src_cnt && (byte_max_cnt == 0 || byte_pos < byte_end); \
+ i++) { \
+ size_t off = 0, len; \
+ if (byte_pos < byte_beg) { \
+ if (byte_beg - byte_pos >= src[i].iov_len) { \
+ byte_pos += src[i].iov_len; \
+ continue; \
+ } \
+ off = byte_beg-byte_pos; \
+ len = src[i].iov_len-off; \
+ byte_pos = byte_beg; \
+ } else { \
+ len = src[i].iov_len; \
+ } \
+ if (byte_max_cnt && byte_end - byte_pos < len) \
+ len = byte_end - byte_pos; \
+ do { ACTION } while (0); \
+ byte_pos += len; \
+ j++; \
+ }
+
+int io_slice_cnt(const struct iovec *src, int src_cnt, size_t byte_beg, size_t byte_max_cnt) {
+ IOV_ITER();
+ return j;
+}
+
+int io_duplex_slice_cnt(const struct duplex_iovec *src, int src_cnt, size_t byte_beg, size_t byte_max_cnt) {
+ IOV_ITER();
+ return j;
+}
+
+void io_slice(struct iovec *dst, const struct iovec *src, int src_cnt, size_t byte_beg, size_t byte_max_cnt) {
+ assert(src_cnt == 0 || dst);
+ IOV_ITER(
+ dst[j] = ((struct iovec){
+ .iov_base = src[i].iov_base+off,
+ .iov_len = len,
+ });
+ );
+}
+void io_slice_duplex(struct duplex_iovec *dst, const struct duplex_iovec *src, int src_cnt, size_t byte_beg, size_t byte_max_cnt) {
+ assert(src_cnt == 0 || dst);
+ IOV_ITER(
+ dst[j] = ((struct duplex_iovec){
+ .iov_read_to = src[i].iov_read_to+off,
+ .iov_write_from = src[i].iov_write_from+off,
+ .iov_len = len,
+ });
+ );
+}
+
+void io_slice_rd_to_duplex(struct duplex_iovec *dst, const struct iovec *src, int src_cnt, size_t byte_beg, size_t byte_max_cnt) {
+ assert(src_cnt == 0 || dst);
+ IOV_ITER(
+ dst[j] = ((struct duplex_iovec){
+ .iov_read_to = src[i].iov_base+off,
+ .iov_write_from = IOVEC_DISCARD,
+ .iov_len = len,
+ });
+ );
+}
+
+void io_slice_wr_to_duplex(struct duplex_iovec *dst, const struct iovec *src, int src_cnt, size_t byte_beg, size_t byte_max_cnt) {
+ assert(src_cnt == 0 || dst);
+ IOV_ITER(
+ dst[j] = ((struct duplex_iovec){
+ .iov_read_to = IOVEC_DISCARD,
+ .iov_write_from = src[i].iov_base+off,
+ .iov_len = len,
+ });
+ );
+}
+
+void io_rd_to_duplex(struct duplex_iovec *dst, const struct iovec *src, int iovcnt) {
+ io_slice_rd_to_duplex(dst, src, iovcnt, 0, 0);
+}
+
+void io_wr_to_duplex(struct duplex_iovec *dst, const struct iovec *src, int iovcnt) {
+ io_slice_wr_to_duplex(dst, src, iovcnt, 0, 0);
+}
diff --git a/libhw_generic/tests/test.h b/libhw_generic/tests/test.h
new file mode 120000
index 0000000..2fb1bd5
--- /dev/null
+++ b/libhw_generic/tests/test.h
@@ -0,0 +1 @@
+../../libmisc/tests/test.h \ No newline at end of file
diff --git a/libhw_generic/tests/test_io.c b/libhw_generic/tests/test_io.c
new file mode 100644
index 0000000..0d6df11
--- /dev/null
+++ b/libhw_generic/tests/test_io.c
@@ -0,0 +1,57 @@
+/* libhw_generic/tests/test_io.c - Tests for <libmisc/io.h>
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <string.h>
+
+#include <libhw/generic/io.h>
+
+#include "test.h"
+
+int main() {
+ char data[] = "abcdefghijklmnopqrstuvwxyz";
+ struct iovec src[] = {
+ {.iov_base = &data[1], .iov_len=3}, /* "bcd" */
+ {.iov_base = &data[20], .iov_len=4}, /* "uvwx" */
+ };
+ const int src_cnt = sizeof(src)/sizeof(src[0]);
+
+ struct duplex_iovec dst[2];
+
+#define TC(start, max, ...) do { \
+ char *exp[] = {__VA_ARGS__}; \
+ int exp_cnt = sizeof(exp)/sizeof(exp[0]); \
+ int act_cnt = io_slice_cnt(src, src_cnt, start, max); \
+ test_assert(act_cnt == exp_cnt); \
+ memset(dst, 0, sizeof(dst)); \
+ io_slice_wr_to_duplex(dst, src, src_cnt, start, max); \
+ for (int i = 0; i < act_cnt; i++) { \
+ test_assert(dst[i].iov_read_to == IOVEC_DISCARD); \
+ test_assert(dst[i].iov_write_from != IOVEC_DISCARD); \
+ test_assert(dst[i].iov_len == strlen(exp[i])); \
+ test_assert(memcmp(dst[i].iov_write_from, exp[i], dst[i].iov_len) == 0); \
+ } \
+ } while (0)
+
+ TC(0, 0, /* => */ "bcd", "uvwx");
+ TC(1, 0, /* => */ "cd", "uvwx");
+ TC(2, 0, /* => */ "d", "uvwx");
+ TC(3, 0, /* => */ "uvwx");
+ TC(4, 0, /* => */ "vwx");
+ TC(5, 0, /* => */ "wx");
+ TC(6, 0, /* => */ "x");
+ TC(7, 0, /* => */ );
+
+ TC(0, 2, /* => */ "bc");
+ TC(1, 2, /* => */ "cd");
+ TC(2, 2, /* => */ "d", "u");
+ TC(3, 2, /* => */ "uv");
+ TC(4, 2, /* => */ "vw");
+ TC(5, 2, /* => */ "wx");
+ TC(6, 2, /* => */ "x");
+ TC(7, 2, /* => */ );
+
+ return 0;
+}