summaryrefslogtreecommitdiff
path: root/libhw_cr
diff options
context:
space:
mode:
Diffstat (limited to 'libhw_cr')
-rw-r--r--libhw_cr/CMakeLists.txt1
-rw-r--r--libhw_cr/host_alarmclock.c43
-rw-r--r--libhw_cr/host_include/libhw/host_alarmclock.h5
-rw-r--r--libhw_cr/host_include/libhw/host_net.h9
-rw-r--r--libhw_cr/host_net.c339
-rw-r--r--libhw_cr/host_util.c128
-rw-r--r--libhw_cr/host_util.h41
-rw-r--r--libhw_cr/rp2040_dma.c109
-rw-r--r--libhw_cr/rp2040_dma.h142
-rw-r--r--libhw_cr/rp2040_gpioirq.c4
-rw-r--r--libhw_cr/rp2040_hwspi.c218
-rw-r--r--libhw_cr/rp2040_hwtimer.c29
-rw-r--r--libhw_cr/rp2040_include/libhw/rp2040_hwspi.h6
-rw-r--r--libhw_cr/rp2040_include/libhw/w5500.h13
-rw-r--r--libhw_cr/w5500.c257
-rw-r--r--libhw_cr/w5500_ll.c116
-rw-r--r--libhw_cr/w5500_ll.h201
17 files changed, 1035 insertions, 626 deletions
diff --git a/libhw_cr/CMakeLists.txt b/libhw_cr/CMakeLists.txt
index ba20b26..9dd6a27 100644
--- a/libhw_cr/CMakeLists.txt
+++ b/libhw_cr/CMakeLists.txt
@@ -24,6 +24,7 @@ if (PICO_PLATFORM STREQUAL "rp2040")
rp2040_hwspi.c
rp2040_hwtimer.c
w5500.c
+ w5500_ll.c
)
target_link_libraries(libhw_cr INTERFACE
hardware_gpio
diff --git a/libhw_cr/host_alarmclock.c b/libhw_cr/host_alarmclock.c
index 9eedec2..0c42677 100644
--- a/libhw_cr/host_alarmclock.c
+++ b/libhw_cr/host_alarmclock.c
@@ -5,9 +5,12 @@
*/
#include <errno.h>
-#include <error.h>
#include <signal.h>
+#define error __error
+#include <error.h>
+#undef error
+
#include <libcr/coroutine.h>
#include <libmisc/assert.h>
@@ -19,15 +22,15 @@
#include "host_util.h" /* for host_sigrt_alloc(), ns_to_host_ns_time() */
-LO_IMPLEMENTATION_C(alarmclock, struct hostclock, hostclock, static);
+LO_IMPLEMENTATION_C(alarmclock, struct hostclock, hostclock);
-static uint64_t hostclock_get_time_ns(struct hostclock *alarmclock) {
+uint64_t hostclock_get_time_ns(struct hostclock *alarmclock) {
assert(alarmclock);
struct timespec ts;
if (clock_gettime(alarmclock->clock_id, &ts) != 0)
- error(1, errno, "clock_gettime(%d)", (int)alarmclock->clock_id);
+ __error(1, errno, "clock_gettime(%d)", (int)alarmclock->clock_id);
return ns_from_host_ns_time(ts);
}
@@ -49,18 +52,18 @@ static void hostclock_handle_sig_alarm(int LM_UNUSED(sig), siginfo_t *info, void
if (alarmclock->queue) {
struct itimerspec alarmspec = {
.it_value = ns_to_host_ns_time(alarmclock->queue->fire_at_ns),
- .it_interval = {0},
+ .it_interval = {},
};
if (timer_settime(alarmclock->timer_id, TIMER_ABSTIME, &alarmspec, NULL) != 0)
- error(1, errno, "timer_settime");
+ __error(1, errno, "timer_settime");
}
}
-static bool hostclock_add_trigger(struct hostclock *alarmclock,
- struct alarmclock_trigger *trigger,
- uint64_t fire_at_ns,
- void (*cb)(void *),
- void *cb_arg) {
+void 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);
@@ -72,6 +75,7 @@ static bool hostclock_add_trigger(struct hostclock *alarmclock,
trigger->cb_arg = cb_arg;
bool saved = cr_save_and_disable_interrupts();
+
struct alarmclock_trigger **dst = &alarmclock->queue;
while (*dst && fire_at_ns >= (*dst)->fire_at_ns)
dst = &(*dst)->next;
@@ -80,6 +84,7 @@ static bool hostclock_add_trigger(struct hostclock *alarmclock,
if (*dst)
(*dst)->prev = trigger;
*dst = trigger;
+
if (!alarmclock->initialized) {
struct sigevent how_to_notify = {
.sigev_notify = SIGEV_SIGNAL,
@@ -93,26 +98,26 @@ static bool hostclock_add_trigger(struct hostclock *alarmclock,
.sa_sigaction = hostclock_handle_sig_alarm,
};
if (sigaction(how_to_notify.sigev_signo, &action, NULL) != 0)
- error(1, errno, "sigaction");
+ __error(1, errno, "sigaction");
if (timer_create(alarmclock->clock_id, &how_to_notify, &alarmclock->timer_id) != 0)
- error(1, errno, "timer_create(%d)", (int)alarmclock->clock_id);
+ __error(1, errno, "timer_create(%d)", (int)alarmclock->clock_id);
alarmclock->initialized = true;
}
+
if (alarmclock->queue == trigger) {
struct itimerspec alarmspec = {
.it_value = ns_to_host_ns_time(trigger->fire_at_ns),
- .it_interval = {0},
+ .it_interval = {},
};
if (timer_settime(alarmclock->timer_id, TIMER_ABSTIME, &alarmspec, NULL) != 0)
- error(1, errno, "timer_settime");
+ __error(1, errno, "timer_settime");
}
- cr_restore_interrupts(saved);
- return false;
+ cr_restore_interrupts(saved);
}
-static void hostclock_del_trigger(struct hostclock *alarmclock,
- struct alarmclock_trigger *trigger) {
+ void hostclock_del_trigger(struct hostclock *alarmclock,
+ struct alarmclock_trigger *trigger) {
assert(alarmclock);
assert(trigger);
diff --git a/libhw_cr/host_include/libhw/host_alarmclock.h b/libhw_cr/host_include/libhw/host_alarmclock.h
index 0cb8d30..2ddb054 100644
--- a/libhw_cr/host_include/libhw/host_alarmclock.h
+++ b/libhw_cr/host_include/libhw/host_alarmclock.h
@@ -7,11 +7,10 @@
#ifndef _LIBHW_HOST_ALARMCLOCK_H_
#define _LIBHW_HOST_ALARMCLOCK_H_
-#include <stdbool.h> /* for bool */
-#include <time.h> /* for clockid_t, timer_t */
+#include <time.h> /* for clockid_t, timer_t */
-#include <libmisc/private.h>
#include <libhw/generic/alarmclock.h>
+#include <libmisc/private.h>
struct hostclock {
clockid_t clock_id;
diff --git a/libhw_cr/host_include/libhw/host_net.h b/libhw_cr/host_include/libhw/host_net.h
index a16ed01..6ff2779 100644
--- a/libhw_cr/host_include/libhw/host_net.h
+++ b/libhw_cr/host_include/libhw/host_net.h
@@ -13,13 +13,16 @@
#include <libhw/generic/net.h>
+/* TCP connection *************************************************************/
+
struct _hostnet_tcp_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);
+
+/* TCP listener ***************************************************************/
struct hostnet_tcp_listener {
BEGIN_PRIVATE(LIBHW_HOST_NET_H);
@@ -27,16 +30,20 @@ struct hostnet_tcp_listener {
struct _hostnet_tcp_conn active_conn;
END_PRIVATE(LIBHW_HOST_NET_H);
};
+LO_IMPLEMENTATION_H(io_closer, struct hostnet_tcp_listener, hostnet_tcplist);
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);
+/* UDP connection *************************************************************/
+
struct hostnet_udp_conn {
BEGIN_PRIVATE(LIBHW_HOST_NET_H);
int fd;
uint64_t read_deadline_ns;
END_PRIVATE(LIBHW_HOST_NET_H);
};
+LO_IMPLEMENTATION_H(io_closer, struct hostnet_udp_conn, hostnet_udp);
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_cr/host_net.c b/libhw_cr/host_net.c
index 6ed6e46..71a5c37 100644
--- a/libhw_cr/host_net.c
+++ b/libhw_cr/host_net.c
@@ -7,11 +7,13 @@
#define _GNU_SOURCE /* for pthread_sigqueue(3gnu) */
/* misc */
#include <errno.h> /* for errno, EAGAIN, EINVAL */
+#define error __error
#include <error.h> /* for error(3gnu) */
+#undef error
#include <stdlib.h> /* for abs(), shutdown(), SHUT_RD, SHUT_WR, SHUT_RDWR */
-#include <unistd.h> /* for read(), write() */
+#include <sys/uio.h> /* for readv(), writev(), struct iovec */
/* net */
-#include <arpa/inet.h> /* for htons(3p) */
+#include <arpa/inet.h> /* for htons() */
#include <netinet/in.h> /* for struct sockaddr_in */
#include <sys/socket.h> /* for struct sockaddr{,_storage}, SOCK_*, SOL_*, SO_*, socket(), setsockopt(), bind(), listen(), accept() */
/* async */
@@ -19,9 +21,10 @@
#include <signal.h> /* for siginfo_t, struct sigaction, enum sigval, sigaction(), SA_SIGINFO */
#include <libcr/coroutine.h>
+#include <libmisc/alloc.h>
#include <libmisc/assert.h>
#include <libmisc/macro.h>
-#include <libobj/obj.h>
+#include <libmisc/obj.h>
#include <libhw/generic/alarmclock.h>
@@ -30,18 +33,17 @@
#include "host_util.h" /* for host_sigrt_alloc(), ns_to_host_us_time() */
-LO_IMPLEMENTATION_C(io_closer, struct hostnet_tcp_listener, hostnet_tcplist, static);
-LO_IMPLEMENTATION_C(net_stream_listener, struct hostnet_tcp_listener, hostnet_tcplist, static);
+LO_IMPLEMENTATION_C(io_closer, struct hostnet_tcp_listener, hostnet_tcplist);
+LO_IMPLEMENTATION_C(net_stream_listener, struct hostnet_tcp_listener, hostnet_tcplist);
-LO_IMPLEMENTATION_C(io_reader, struct _hostnet_tcp_conn, hostnet_tcp, static);
-LO_IMPLEMENTATION_C(io_writer, struct _hostnet_tcp_conn, hostnet_tcp, static);
-LO_IMPLEMENTATION_C(io_readwriter, struct _hostnet_tcp_conn, hostnet_tcp, static);
-LO_IMPLEMENTATION_C(io_closer, struct _hostnet_tcp_conn, hostnet_tcp, static);
-LO_IMPLEMENTATION_C(io_bidi_closer, struct _hostnet_tcp_conn, hostnet_tcp, static);
-LO_IMPLEMENTATION_C(net_stream_conn, struct _hostnet_tcp_conn, hostnet_tcp, static);
+LO_IMPLEMENTATION_STATIC(io_reader, struct _hostnet_tcp_conn, hostnet_tcp);
+LO_IMPLEMENTATION_STATIC(io_writer, struct _hostnet_tcp_conn, hostnet_tcp);
+LO_IMPLEMENTATION_STATIC(io_closer, struct _hostnet_tcp_conn, hostnet_tcp);
+LO_IMPLEMENTATION_STATIC(io_bidi_closer, struct _hostnet_tcp_conn, hostnet_tcp);
+LO_IMPLEMENTATION_STATIC(net_stream_conn, struct _hostnet_tcp_conn, hostnet_tcp);
-LO_IMPLEMENTATION_C(io_closer, struct hostnet_udp_conn, hostnet_udp, static);
-LO_IMPLEMENTATION_C(net_packet_conn, struct hostnet_udp_conn, hostnet_udp, static);
+LO_IMPLEMENTATION_C(io_closer, struct hostnet_udp_conn, hostnet_udp);
+LO_IMPLEMENTATION_C(net_packet_conn, struct hostnet_udp_conn, hostnet_udp);
/* common *********************************************************************/
@@ -52,7 +54,7 @@ static void hostnet_handle_sig_io(int LM_UNUSED(sig), siginfo_t *info, void *LM_
}
static void hostnet_init(void) {
- struct sigaction action = {0};
+ struct sigaction action = {};
if (hostnet_sig_io)
return;
@@ -62,12 +64,12 @@ static void hostnet_init(void) {
action.sa_flags = SA_SIGINFO;
action.sa_sigaction = hostnet_handle_sig_io;
if (sigaction(hostnet_sig_io, &action, NULL) < 0)
- error(1, errno, "sigaction");
+ __error(1, errno, "sigaction");
}
#define WAKE_COROUTINE(args) do { \
int r; \
- union sigval val = {0}; \
+ union sigval val = {}; \
val.sival_int = (int)((args)->cr_coroutine); \
do { \
r = pthread_sigqueue((args)->cr_thread, \
@@ -77,46 +79,47 @@ static void hostnet_init(void) {
} while (r == EAGAIN); \
} while (0)
-static inline bool RUN_PTHREAD(void *(*fn)(void *), void *args) {
+static inline host_errno_t RUN_PTHREAD(void *(*fn)(void *), void *args) {
pthread_t thread;
+ host_errno_t r;
bool saved = cr_save_and_disable_interrupts();
- if (pthread_create(&thread, NULL, fn, args))
- return true;
+ r = pthread_create(&thread, NULL, fn, args);
+ if (r) {
+ cr_restore_interrupts(saved);
+ return r;
+ }
cr_pause_and_yield();
cr_restore_interrupts(saved);
- if (pthread_join(thread, NULL))
- return true;
- return false;
+ return pthread_join(thread, NULL);
}
enum hostnet_timeout_op {
- OP_NONE,
+ OP_CLOSE,
OP_SEND,
OP_RECV,
};
-static inline ssize_t hostnet_map_negerrno(ssize_t v, enum hostnet_timeout_op op) {
- if (v >= 0)
- return v;
- switch (v) {
- case -EHOSTUNREACH:
- return -NET_EARP_TIMEOUT;
- case -ETIMEDOUT:
+static inline error hostnet_error(host_errno_t errnum, enum hostnet_timeout_op op) {
+ assert(errnum > 0);
+ switch (errnum) {
+ case EHOSTUNREACH:
+ return error_new(E_NET_EARP_TIMEOUT);
+ case ETIMEDOUT:
switch (op) {
- case OP_NONE:
+ case OP_CLOSE:
assert_notreached("impossible ETIMEDOUT");
case OP_SEND:
- return -NET_EACK_TIMEOUT;
+ return error_new(E_NET_EACK_TIMEOUT);
case OP_RECV:
- return -NET_ERECV_TIMEOUT;
+ return error_new(E_NET_ERECV_TIMEOUT);
}
assert_notreached("invalid timeout op");
- case -EBADF:
- return -NET_ECLOSED;
- case -EMSGSIZE:
- return -NET_EMSGSIZE;
+ case EBADF:
+ return error_new(E_NET_ECLOSED);
+ case EMSGSIZE:
+ return error_new(E_POSIX_EMSGSIZE);
default:
- return -NET_EOTHER;
+ return errno2lm(errnum);
}
}
@@ -127,7 +130,7 @@ void hostnet_tcp_listener_init(struct hostnet_tcp_listener *self, uint16_t port)
union {
struct sockaddr_in in;
struct sockaddr gen;
- } addr = { 0 };
+ } addr = {};
hostnet_init();
@@ -135,15 +138,15 @@ void hostnet_tcp_listener_init(struct hostnet_tcp_listener *self, uint16_t port)
addr.in.sin_port = htons(port);
listenerfd = socket(AF_INET, SOCK_STREAM, 0);
if (listenerfd < 0)
- error(1, errno, "socket");
+ __error(1, errno, "socket");
if (setsockopt(listenerfd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) < 0)
- error(1, errno, "setsockopt(fd=%d, SO_REUSEADDR=1)", listenerfd);
+ __error(1, errno, "setsockopt(fd=%d, SO_REUSEADDR=1)", listenerfd);
if (setsockopt(listenerfd, SOL_SOCKET, SO_REUSEPORT, &(int){1}, sizeof(int)) < 0)
- error(1, errno, "setsockopt(fd=%d, SO_REUSEPORT=1)", listenerfd);
+ __error(1, errno, "setsockopt(fd=%d, SO_REUSEPORT=1)", listenerfd);
if (bind(listenerfd, &addr.gen, sizeof addr) < 0)
- error(1, errno, "bind(fd=%d)", listenerfd);
+ __error(1, errno, "bind(fd=%d)", listenerfd);
if (listen(listenerfd, 0) < 0)
- error(1, errno, "listen(fd=%d)", listenerfd);
+ __error(1, errno, "listen(fd=%d)", listenerfd);
self->fd = listenerfd;
}
@@ -157,44 +160,48 @@ struct hostnet_pthread_accept_args {
int listenerfd;
int *ret_connfd;
+ host_errno_t *ret_errno;
};
static void *hostnet_pthread_accept(void *_args) {
struct hostnet_pthread_accept_args *args = _args;
*(args->ret_connfd) = accept(args->listenerfd, NULL, NULL);
- if (*(args->ret_connfd) < 0)
- *(args->ret_connfd) = -errno;
+ *(args->ret_errno) = errno;
WAKE_COROUTINE(args);
return NULL;
}
-static lo_interface net_stream_conn hostnet_tcplist_accept(struct hostnet_tcp_listener *listener) {
+net_stream_conn_or_error hostnet_tcplist_accept(struct hostnet_tcp_listener *listener) {
assert(listener);
int ret_connfd;
+ host_errno_t ret_errno;
struct hostnet_pthread_accept_args args = {
.cr_thread = pthread_self(),
.cr_coroutine = cr_getcid(),
.listenerfd = listener->fd,
.ret_connfd = &ret_connfd,
+ .ret_errno = &ret_errno,
};
- if (RUN_PTHREAD(hostnet_pthread_accept, &args))
- return LO_NULL(net_stream_conn);
-
+ host_errno_t thread_errno = RUN_PTHREAD(hostnet_pthread_accept, &args);
+ if (thread_errno)
+ return ERROR_NEW_ERR(net_stream_conn, errno2lm(thread_errno));
if (ret_connfd < 0)
- return LO_NULL(net_stream_conn);
+ return ERROR_NEW_ERR(net_stream_conn, hostnet_error(ret_errno, OP_RECV));
listener->active_conn.fd = ret_connfd;
listener->active_conn.read_deadline_ns = 0;
- return lo_box_hostnet_tcp_as_net_stream_conn(&listener->active_conn);
+ return ERROR_NEW_VAL(net_stream_conn, LO_BOX(net_stream_conn, &listener->active_conn));
}
/* TCP listener close() *******************************************************/
-static int hostnet_tcplist_close(struct hostnet_tcp_listener *listener) {
+error hostnet_tcplist_close(struct hostnet_tcp_listener *listener) {
assert(listener);
- return hostnet_map_negerrno(shutdown(listener->fd, SHUT_RDWR) ? -errno : 0, OP_NONE);
+ if (shutdown(listener->fd, SHUT_RDWR))
+ return hostnet_error(errno, OP_CLOSE);
+ return ERROR_NULL;
}
/* TCP read() *****************************************************************/
@@ -214,56 +221,74 @@ struct hostnet_pthread_readv_args {
const struct iovec *iov;
int iovcnt;
- ssize_t *ret;
+ size_t *ret_size;
+ host_errno_t *ret_errno;
};
static void *hostnet_pthread_readv(void *_args) {
struct hostnet_pthread_readv_args *args = _args;
- *(args->ret) = setsockopt(args->connfd, SOL_SOCKET, SO_RCVTIMEO,
- &args->timeout, sizeof(args->timeout));
- if (*(args->ret) < 0)
+ int r_i = setsockopt(args->connfd, SOL_SOCKET, SO_RCVTIMEO,
+ &args->timeout, sizeof(args->timeout));
+ if (r_i) {
+ *args->ret_size = 0;
+ *args->ret_errno = errno;
goto end;
+ }
- *(args->ret) = readv(args->connfd, args->iov, args->iovcnt);
- if (*(args->ret) < 0)
+ ssize_t r_ss = readv(args->connfd, args->iov, args->iovcnt);
+ if (r_ss < 0) {
+ *args->ret_size = 0;
+ *args->ret_errno = errno;
goto end;
+ }
+ *args->ret_size = r_ss;
+ *args->ret_errno = 0;
end:
- if (*(args->ret) < 0)
- *(args->ret) = hostnet_map_negerrno(-errno, OP_SEND);
WAKE_COROUTINE(args);
return NULL;
}
-static ssize_t hostnet_tcp_readv(struct _hostnet_tcp_conn *conn, const struct iovec *iov, int iovcnt) {
+static size_t_or_error hostnet_tcp_readv(struct _hostnet_tcp_conn *conn, const struct rd_iovec *iov, int iovcnt) {
assert(conn);
assert(iov);
assert(iovcnt > 0);
+ size_t count = 0;
+ for (int i = 0; i < iovcnt; i++)
+ count += iov[i].iov_len;
+ assert(count);
- ssize_t ret;
+ size_t ret_size;
+ host_errno_t ret_errno;
struct hostnet_pthread_readv_args args = {
.cr_thread = pthread_self(),
.cr_coroutine = cr_getcid(),
.connfd = conn->fd,
- .iov = iov,
+ .iov = (const struct iovec *)iov,
.iovcnt = iovcnt,
- .ret = &ret,
+ .ret_size = &ret_size,
+ .ret_errno = &ret_errno,
};
if (conn->read_deadline_ns) {
uint64_t now_ns = LO_CALL(bootclock, get_time_ns);
if (conn->read_deadline_ns < now_ns)
- return -NET_ERECV_TIMEOUT;
+ return ERROR_NEW_ERR(size_t, error_new(E_NET_ERECV_TIMEOUT));
args.timeout = ns_to_host_us_time(conn->read_deadline_ns-now_ns);
} else {
- args.timeout = (host_us_time_t){0};
+ args.timeout = (host_us_time_t){};
}
- if (RUN_PTHREAD(hostnet_pthread_readv, &args))
- return -NET_ETHREAD;
- return ret;
+ host_errno_t thread_errno = RUN_PTHREAD(hostnet_pthread_readv, &args);
+ if (thread_errno)
+ return ERROR_NEW_ERR(size_t, errno2lm(thread_errno));
+ if (ret_errno)
+ return ERROR_NEW_ERR(size_t, hostnet_error(ret_errno, OP_RECV));
+ if (!ret_size)
+ return ERROR_NEW_ERR(size_t, error_new(E_EOF));
+ return ERROR_NEW_VAL(size_t, ret_size);
}
/* TCP write() ****************************************************************/
@@ -276,14 +301,15 @@ struct hostnet_pthread_writev_args {
const struct iovec *iov;
int iovcnt;
- ssize_t *ret;
+ size_t *ret_size;
+ host_errno_t *ret_errno;
};
static void *hostnet_pthread_writev(void *_args) {
struct hostnet_pthread_writev_args *args = _args;
size_t count = 0;
- struct iovec *iov = alloca(sizeof(struct iovec)*args->iovcnt);
+ struct iovec *iov = stack_alloc(args->iovcnt, struct iovec);
for (int i = 0; i < args->iovcnt; i++) {
iov[i] = args->iov[i];
count += args->iov[i].iov_len;
@@ -294,7 +320,7 @@ static void *hostnet_pthread_writev(void *_args) {
while (done < count) {
ssize_t r = writev(args->connfd, iov, iovcnt);
if (r < 0) {
- hostnet_map_negerrno(-errno, OP_RECV);
+ *args->ret_errno = errno;
break;
}
done += r;
@@ -309,45 +335,59 @@ static void *hostnet_pthread_writev(void *_args) {
}
}
if (done == count)
- *(args->ret) = done;
+ *args->ret_errno = 0;
+ *args->ret_size = done;
WAKE_COROUTINE(args);
return NULL;
}
-static ssize_t hostnet_tcp_writev(struct _hostnet_tcp_conn *conn, const struct iovec *iov, int iovcnt) {
+static size_t_and_error hostnet_tcp_writev(struct _hostnet_tcp_conn *conn, const struct wr_iovec *iov, int iovcnt) {
assert(conn);
assert(iov);
assert(iovcnt > 0);
+ size_t count = 0;
+ for (int i = 0; i < iovcnt; i++)
+ count += iov[i].iov_len;
+ assert(count);
- ssize_t ret;
+ size_t ret_size;
+ host_errno_t ret_errno;
struct hostnet_pthread_writev_args args = {
.cr_thread = pthread_self(),
.cr_coroutine = cr_getcid(),
.connfd = conn->fd,
- .iov = iov,
+ .iov = (const struct iovec *)iov,
.iovcnt = iovcnt,
- .ret = &ret,
+ .ret_size = &ret_size,
+ .ret_errno = &ret_errno,
};
- if (RUN_PTHREAD(hostnet_pthread_writev, &args))
- return -NET_ETHREAD;
- return ret;
+ int thread_errno = RUN_PTHREAD(hostnet_pthread_writev, &args);
+ if (thread_errno)
+ return ERROR_AND(size_t, 0, errno2lm(thread_errno));
+ return ERROR_AND(size_t, ret_size, ret_errno ? hostnet_error(ret_errno, OP_SEND) : ERROR_NULL);
}
/* TCP close() ****************************************************************/
-static int hostnet_tcp_close(struct _hostnet_tcp_conn *conn) {
+static error hostnet_tcp_close(struct _hostnet_tcp_conn *conn) {
assert(conn);
- return hostnet_map_negerrno(shutdown(conn->fd, SHUT_RDWR) ? -errno : 0, OP_NONE);
+ if (shutdown(conn->fd, SHUT_RDWR))
+ return hostnet_error(errno, OP_CLOSE);
+ return ERROR_NULL;
}
-static int hostnet_tcp_close_read(struct _hostnet_tcp_conn *conn) {
+static error hostnet_tcp_close_read(struct _hostnet_tcp_conn *conn) {
assert(conn);
- return hostnet_map_negerrno(shutdown(conn->fd, SHUT_RD) ? -errno : 0, OP_NONE);
+ if (shutdown(conn->fd, SHUT_RD))
+ return hostnet_error(errno, OP_CLOSE);
+ return ERROR_NULL;
}
-static int hostnet_tcp_close_write(struct _hostnet_tcp_conn *conn) {
+static error hostnet_tcp_close_write(struct _hostnet_tcp_conn *conn) {
assert(conn);
- return hostnet_map_negerrno(shutdown(conn->fd, SHUT_WR) ? -errno : 0, OP_NONE);
+ if (shutdown(conn->fd, SHUT_WR))
+ return hostnet_error(errno, OP_CLOSE);
+ return ERROR_NULL;
}
/* UDP init() *****************************************************************/
@@ -358,7 +398,7 @@ void hostnet_udp_conn_init(struct hostnet_udp_conn *self, uint16_t port) {
struct sockaddr_in in;
struct sockaddr gen;
struct sockaddr_storage stor;
- } addr = { 0 };
+ } addr = {};
hostnet_init();
@@ -366,9 +406,11 @@ void hostnet_udp_conn_init(struct hostnet_udp_conn *self, uint16_t port) {
addr.in.sin_port = htons(port);
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0)
- error(1, errno, "socket");
+ __error(1, errno, "socket");
+ if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &(int){1}, sizeof(int)) < 0)
+ __error(1, errno, "setsockopt(fd=%d, SO_BROADCAST=1)", fd);
if (bind(fd, &addr.gen, sizeof addr) < 0)
- error(1, errno, "bind");
+ __error(1, errno, "bind");
self->fd = fd;
self->read_deadline_ns = 0;
@@ -386,7 +428,8 @@ struct hostnet_pthread_sendto_args {
struct net_ip4_addr node;
uint16_t port;
- ssize_t *ret;
+ size_t *ret_size;
+ host_errno_t *ret_errno;
};
static void *hostnet_pthread_sendto(void *_args) {
@@ -395,27 +438,33 @@ static void *hostnet_pthread_sendto(void *_args) {
struct sockaddr_in in;
struct sockaddr gen;
struct sockaddr_storage stor;
- } addr = { 0 };
+ } addr = {};
addr.in.sin_family = AF_INET;
addr.in.sin_addr.s_addr =
- (((uint32_t)args->node.octets[0])<<24) |
- (((uint32_t)args->node.octets[1])<<16) |
- (((uint32_t)args->node.octets[2])<< 8) |
- (((uint32_t)args->node.octets[3])<< 0) ;
+ (((uint32_t)args->node.octets[3])<<24) |
+ (((uint32_t)args->node.octets[2])<<16) |
+ (((uint32_t)args->node.octets[1])<< 8) |
+ (((uint32_t)args->node.octets[0])<< 0) ;
addr.in.sin_port = htons(args->port);
- *(args->ret) = sendto(args->connfd, args->buf, args->count, 0, &addr.gen, sizeof(addr));
- if (*(args->ret) < 0)
- *(args->ret) = hostnet_map_negerrno(-errno, OP_SEND);
+ ssize_t r = sendto(args->connfd, args->buf, args->count, 0, &addr.gen, sizeof(addr));
+ if (r < 0) {
+ *args->ret_size = 0;
+ *args->ret_errno = errno;
+ } else {
+ *args->ret_size = r;
+ *args->ret_errno = 0;
+ }
WAKE_COROUTINE(args);
return NULL;
}
-static ssize_t hostnet_udp_sendto(struct hostnet_udp_conn *conn, void *buf, size_t count,
- struct net_ip4_addr node, uint16_t port) {
+error hostnet_udp_sendto(struct hostnet_udp_conn *conn, void *buf, size_t count,
+ struct net_ip4_addr node, uint16_t port) {
assert(conn);
- ssize_t ret;
+ size_t ret_size;
+ host_errno_t ret_errno;
struct hostnet_pthread_sendto_args args = {
.cr_thread = pthread_self(),
.cr_coroutine = cr_getcid(),
@@ -426,17 +475,21 @@ static ssize_t hostnet_udp_sendto(struct hostnet_udp_conn *conn, void *buf, size
.node = node,
.port = port,
- .ret = &ret,
+ .ret_size = &ret_size,
+ .ret_errno = &ret_errno,
};
- if (RUN_PTHREAD(hostnet_pthread_sendto, &args))
- return -NET_ETHREAD;
- return ret;
+ int thread_errno = RUN_PTHREAD(hostnet_pthread_sendto, &args);
+ if (thread_errno)
+ return errno2lm(thread_errno);
+ if (ret_errno)
+ return hostnet_error(ret_errno, OP_SEND);
+ return ERROR_NULL;
}
/* UDP recvfrom() *************************************************************/
-static void hostnet_udp_set_recv_deadline(struct hostnet_udp_conn *conn,
- uint64_t ts_ns) {
+void hostnet_udp_set_recv_deadline(struct hostnet_udp_conn *conn,
+ uint64_t ts_ns) {
assert(conn);
conn->read_deadline_ns = ts_ns;
@@ -451,7 +504,8 @@ struct hostnet_pthread_recvfrom_args {
void *buf;
size_t count;
- ssize_t *ret_size;
+ size_t *ret_size;
+ host_errno_t *ret_errno;
struct net_ip4_addr *ret_node;
uint16_t *ret_port;
};
@@ -463,42 +517,49 @@ static void *hostnet_pthread_recvfrom(void *_args) {
struct sockaddr_in in;
struct sockaddr gen;
struct sockaddr_storage stor;
- } addr = { 0 };
- socklen_t addr_size;
-
- *(args->ret_size) = setsockopt(args->connfd, SOL_SOCKET, SO_RCVTIMEO,
- &args->timeout, sizeof(args->timeout));
- if (*(args->ret_size) < 0)
+ } addr = {};
+ socklen_t addr_size = sizeof(addr);
+
+ int r_i = setsockopt(args->connfd, SOL_SOCKET, SO_RCVTIMEO,
+ &args->timeout, sizeof(args->timeout));
+ if (r_i) {
+ *args->ret_size = 0;
+ *args->ret_errno = errno;
goto end;
+ }
- *(args->ret_size) = recvfrom(args->connfd, args->buf, args->count,
- MSG_TRUNC, &addr.gen, &addr_size);
- if (*(args->ret_size) < 0)
+ ssize_t r_ss = recvfrom(args->connfd, args->buf, args->count,
+ MSG_TRUNC, &addr.gen, &addr_size);
+ if (r_ss < 0) {
+ *args->ret_size = 0;
+ *args->ret_errno = errno;
goto end;
+ }
+ *args->ret_size = r_ss;
+ *args->ret_errno = 0;
assert(addr.in.sin_family == AF_INET);
if (args->ret_node) {
- args->ret_node->octets[0] = (addr.in.sin_addr.s_addr >> 24) & 0xFF;
- args->ret_node->octets[1] = (addr.in.sin_addr.s_addr >> 16) & 0xFF;
- args->ret_node->octets[2] = (addr.in.sin_addr.s_addr >> 8) & 0xFF;
- args->ret_node->octets[3] = (addr.in.sin_addr.s_addr >> 0) & 0xFF;
+ args->ret_node->octets[3] = (addr.in.sin_addr.s_addr >> 24) & 0xFF;
+ args->ret_node->octets[2] = (addr.in.sin_addr.s_addr >> 16) & 0xFF;
+ args->ret_node->octets[1] = (addr.in.sin_addr.s_addr >> 8) & 0xFF;
+ args->ret_node->octets[0] = (addr.in.sin_addr.s_addr >> 0) & 0xFF;
}
if (args->ret_port) {
(*args->ret_port) = ntohs(addr.in.sin_port);
}
end:
- if (*(args->ret_size) < 0)
- *(args->ret_size) = hostnet_map_negerrno(-errno, OP_RECV);
WAKE_COROUTINE(args);
return NULL;
}
-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) {
+size_t_or_error hostnet_udp_recvfrom(struct hostnet_udp_conn *conn, void *buf, size_t count,
+ struct net_ip4_addr *ret_node, uint16_t *ret_port) {
assert(conn);
- ssize_t ret;
+ size_t ret_size;
+ host_errno_t ret_errno;
struct hostnet_pthread_recvfrom_args args = {
.cr_thread = pthread_self(),
.cr_coroutine = cr_getcid(),
@@ -507,28 +568,34 @@ static ssize_t hostnet_udp_recvfrom(struct hostnet_udp_conn *conn, void *buf, si
.buf = buf,
.count = count,
- .ret_size = &ret,
+ .ret_size = &ret_size,
+ .ret_errno = &ret_errno,
.ret_node = ret_node,
.ret_port = ret_port,
};
if (conn->read_deadline_ns) {
uint64_t now_ns = LO_CALL(bootclock, get_time_ns);
if (conn->read_deadline_ns < now_ns)
- return -NET_ERECV_TIMEOUT;
+ return ERROR_NEW_ERR(size_t, error_new(E_NET_ERECV_TIMEOUT));
args.timeout = ns_to_host_us_time(conn->read_deadline_ns-now_ns);
} else {
- args.timeout = (host_us_time_t){0};
+ args.timeout = (host_us_time_t){};
}
- if (RUN_PTHREAD(hostnet_pthread_recvfrom, &args))
- return -NET_ETHREAD;
- return ret;
+ host_errno_t thread_errno = RUN_PTHREAD(hostnet_pthread_recvfrom, &args);
+ if (thread_errno)
+ return ERROR_NEW_ERR(size_t, errno2lm(thread_errno));
+ if (ret_errno)
+ return ERROR_NEW_ERR(size_t, hostnet_error(ret_errno, OP_RECV));
+ return ERROR_NEW_VAL(size_t, ret_size);
}
/* UDP close() ****************************************************************/
-static int hostnet_udp_close(struct hostnet_udp_conn *conn) {
+error hostnet_udp_close(struct hostnet_udp_conn *conn) {
assert(conn);
- return hostnet_map_negerrno(close(conn->fd) ? -errno : 0, OP_NONE);
+ if (close(conn->fd))
+ return hostnet_error(errno, OP_CLOSE);
+ return ERROR_NULL;
}
diff --git a/libhw_cr/host_util.c b/libhw_cr/host_util.c
index 7b3200c..2d45490 100644
--- a/libhw_cr/host_util.c
+++ b/libhw_cr/host_util.c
@@ -4,9 +4,15 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-#include <error.h> /* for error(3gnu) */
+#include <errno.h> /* for E* */
#include <signal.h> /* for SIGRTMIN, SIGRTMAX */
+#define error __error
+#include <error.h>
+#undef error
+
+#include <libhw/generic/alarmclock.h> /* for {X}S_PER_S */
+
#include "host_util.h"
int host_sigrt_alloc(void) {
@@ -16,6 +22,124 @@ int host_sigrt_alloc(void) {
next = SIGRTMIN;
int ret = next++;
if (ret > SIGRTMAX)
- error(1, 0, "SIGRTMAX exceeded");
+ __error(1, 0, "SIGRTMAX exceeded");
+ return ret;
+}
+
+host_us_time_t ns_to_host_us_time(uint64_t time_ns) {
+ host_us_time_t ret;
+ ret.tv_sec = time_ns
+ /NS_PER_S;
+ ret.tv_usec = (time_ns - ((uint64_t)ret.tv_sec)*NS_PER_S)
+ /(NS_PER_S/US_PER_S);
+ return ret;
+}
+
+host_ns_time_t ns_to_host_ns_time(uint64_t time_ns) {
+ host_ns_time_t ret;
+ ret.tv_sec = time_ns
+ /NS_PER_S;
+ ret.tv_nsec = time_ns - ((uint64_t)ret.tv_sec)*NS_PER_S;
return ret;
}
+
+uint64_t ns_from_host_us_time(host_us_time_t host_time) {
+ return (((uint64_t)host_time.tv_sec) * NS_PER_S) +
+ ((uint64_t)host_time.tv_usec * (NS_PER_S/US_PER_S));
+}
+
+uint64_t ns_from_host_ns_time(host_ns_time_t host_time) {
+ return (((uint64_t)host_time.tv_sec) * NS_PER_S) +
+ ((uint64_t)host_time.tv_nsec);
+}
+
+_errnum errno_host2lm(host_errno_t host) {
+ switch (host) {
+ case E2BIG: return E_POSIX_E2BIG;
+ case EACCES: return E_POSIX_EACCES;
+ case EADDRINUSE: return E_POSIX_EADDRINUSE;
+ case EADDRNOTAVAIL: return E_POSIX_EADDRNOTAVAIL;
+ case EAFNOSUPPORT: return E_POSIX_EAFNOSUPPORT;
+ case EAGAIN: return E_POSIX_EAGAIN;
+ case EALREADY: return E_POSIX_EALREADY;
+ case EBADF: return E_POSIX_EBADF;
+ case EBADMSG: return E_POSIX_EBADMSG;
+ case EBUSY: return E_POSIX_EBUSY;
+ case ECANCELED: return E_POSIX_ECANCELED;
+ case ECHILD: return E_POSIX_ECHILD;
+ case ECONNABORTED: return E_POSIX_ECONNABORTED;
+ case ECONNREFUSED: return E_POSIX_ECONNREFUSED;
+ case ECONNRESET: return E_POSIX_ECONNRESET;
+ case EDEADLK: return E_POSIX_EDEADLK;
+ case EDESTADDRREQ: return E_POSIX_EDESTADDRREQ;
+ case EDOM: return E_POSIX_EDOM;
+ case EDQUOT: return E_POSIX_EDQUOT;
+ case EEXIST: return E_POSIX_EEXIST;
+ case EFAULT: return E_POSIX_EFAULT;
+ case EFBIG: return E_POSIX_EFBIG;
+ case EHOSTUNREACH: return E_POSIX_EHOSTUNREACH;
+ case EIDRM: return E_POSIX_EIDRM;
+ case EILSEQ: return E_POSIX_EILSEQ;
+ case EINPROGRESS: return E_POSIX_EINPROGRESS;
+ case EINTR: return E_POSIX_EINTR;
+ case EINVAL: return E_POSIX_EINVAL;
+ case EIO: return E_POSIX_EIO;
+ case EISCONN: return E_POSIX_EISCONN;
+ case EISDIR: return E_POSIX_EISDIR;
+ case ELOOP: return E_POSIX_ELOOP;
+ case EMFILE: return E_POSIX_EMFILE;
+ case EMLINK: return E_POSIX_EMLINK;
+ case EMSGSIZE: return E_POSIX_EMSGSIZE;
+ case EMULTIHOP: return E_POSIX_EMULTIHOP;
+ case ENAMETOOLONG: return E_POSIX_ENAMETOOLONG;
+ case ENETDOWN: return E_POSIX_ENETDOWN;
+ case ENETRESET: return E_POSIX_ENETRESET;
+ case ENETUNREACH: return E_POSIX_ENETUNREACH;
+ case ENFILE: return E_POSIX_ENFILE;
+ case ENOBUFS: return E_POSIX_ENOBUFS;
+ case ENODEV: return E_POSIX_ENODEV;
+ case ENOENT: return E_POSIX_ENOENT;
+ case ENOEXEC: return E_POSIX_ENOEXEC;
+ case ENOLCK: return E_POSIX_ENOLCK;
+ case ENOLINK: return E_POSIX_ENOLINK;
+ case ENOMEM: return E_POSIX_ENOMEM;
+ case ENOMSG: return E_POSIX_ENOMSG;
+ case ENOPROTOOPT: return E_POSIX_ENOPROTOOPT;
+ case ENOSPC: return E_POSIX_ENOSPC;
+ case ENOSYS: return E_POSIX_ENOSYS;
+ case ENOTCONN: return E_POSIX_ENOTCONN;
+ case ENOTDIR: return E_POSIX_ENOTDIR;
+ case ENOTEMPTY: return E_POSIX_ENOTEMPTY;
+ case ENOTRECOVERABLE: return E_POSIX_ENOTRECOVERABLE;
+ case ENOTSOCK: return E_POSIX_ENOTSOCK;
+ case ENOTSUP: return E_POSIX_ENOTSUP;
+ case ENOTTY: return E_POSIX_ENOTTY;
+ case ENXIO: return E_POSIX_ENXIO;
+ case EOVERFLOW: return E_POSIX_EOVERFLOW;
+ case EOWNERDEAD: return E_POSIX_EOWNERDEAD;
+ case EPERM: return E_POSIX_EPERM;
+ case EPIPE: return E_POSIX_EPIPE;
+ case EPROTO: return E_POSIX_EPROTO;
+ case EPROTONOSUPPORT: return E_POSIX_EPROTONOSUPPORT;
+ case EPROTOTYPE: return E_POSIX_EPROTOTYPE;
+ case ERANGE: return E_POSIX_ERANGE;
+ case EROFS: return E_POSIX_EROFS;
+ case ESOCKTNOSUPPORT: return E_POSIX_ESOCKTNOSUPPORT;
+ case ESPIPE: return E_POSIX_ESPIPE;
+ case ESRCH: return E_POSIX_ESRCH;
+ case ESTALE: return E_POSIX_ESTALE;
+ case ETIMEDOUT: return E_POSIX_ETIMEDOUT;
+ case ETXTBSY: return E_POSIX_ETXTBSY;
+ case EXDEV: return E_POSIX_EXDEV;
+ default:
+ switch (host) {
+ case EOPNOTSUPP: return E_POSIX_EOPNOTSUPP;
+ case EWOULDBLOCK: return E_POSIX_EWOULDBLOCK;
+ default: return E_EUNKNOWN;
+ }
+ }
+}
+
+error errno2lm(host_errno_t host) {
+ return error_new(errno_host2lm(host));
+}
diff --git a/libhw_cr/host_util.h b/libhw_cr/host_util.h
index 02c04dc..7e559ef 100644
--- a/libhw_cr/host_util.h
+++ b/libhw_cr/host_util.h
@@ -7,41 +7,24 @@
#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 */
+#include <stdint.h> /* for uint{n}_t */
+#include <sys/time.h> /* for struct timeval */
+#include <time.h> /* for struct timespec */
-#include <libhw/generic/alarmclock.h> /* for {X}S_PER_S */
+#include <libmisc/error.h> /* for _errnum, error */
int host_sigrt_alloc(void);
typedef struct timeval host_us_time_t;
typedef struct timespec host_ns_time_t;
-static inline host_us_time_t ns_to_host_us_time(uint64_t time_ns) {
- host_us_time_t ret;
- ret.tv_sec = time_ns
- /NS_PER_S;
- ret.tv_usec = (time_ns - ((uint64_t)ret.tv_sec)*NS_PER_S)
- /(NS_PER_S/US_PER_S);
- return ret;
-}
-
-static inline host_ns_time_t ns_to_host_ns_time(uint64_t time_ns) {
- host_ns_time_t ret;
- ret.tv_sec = time_ns
- /NS_PER_S;
- ret.tv_nsec = time_ns - ((uint64_t)ret.tv_sec)*NS_PER_S;
- return ret;
-}
-
-static inline uint64_t ns_from_host_us_time(host_us_time_t host_time) {
- return (((uint64_t)host_time.tv_sec) * NS_PER_S) +
- ((uint64_t)host_time.tv_usec * (NS_PER_S/US_PER_S));
-}
-
-static inline uint64_t ns_from_host_ns_time(host_ns_time_t host_time) {
- return (((uint64_t)host_time.tv_sec) * NS_PER_S) +
- ((uint64_t)host_time.tv_nsec);
-}
+host_us_time_t ns_to_host_us_time(uint64_t time_ns);
+host_ns_time_t ns_to_host_ns_time(uint64_t time_ns);
+uint64_t ns_from_host_us_time(host_us_time_t host_time);
+uint64_t ns_from_host_ns_time(host_ns_time_t host_time);
+
+#define host_errno_t int
+_errnum errno_host2lm(host_errno_t host);
+error errno2lm(host_errno_t host);
#endif /* _LIBHW_CR_HOST_UTIL_H_ */
diff --git a/libhw_cr/rp2040_dma.c b/libhw_cr/rp2040_dma.c
index 69116bf..7b78535 100644
--- a/libhw_cr/rp2040_dma.c
+++ b/libhw_cr/rp2040_dma.c
@@ -1,22 +1,120 @@
/* libhw_cr/rp2040_dma.c - Utilities for sharing the DMA IRQs
*
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
* Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-#include <stdbool.h>
-
#include <hardware/irq.h> /* for irq_set_exclusive_handler() */
+#include <libmisc/log.h>
+
#include "rp2040_dma.h"
+/* static_assert("rp2040_dma.h" == <hardware/irq.h>); */
+static_assert((uint)DMAIRQ_0 == (uint)DMA_IRQ_0);
+static_assert((uint)DMAIRQ_1 == (uint)DMA_IRQ_1);
+
+/* Borrowed from <hardware/dma.h> *********************************************/
+
+dma_channel_hw_t *dma_channel_hw_addr(uint channel) {
+ assert(channel < NUM_DMA_CHANNELS);
+ return &dma_hw->ch[channel];
+}
+
+/* Our own code ***************************************************************/
+
+typedef uint8_t addr_flag_t;
+#define ADDR_FLAG_UNMAPPED ((addr_flag_t)(1<<0))
+#define ADDR_FLAG_UNSAFE ((addr_flag_t)(1<<1))
+#define ADDR_FLAG_RD_OK ((addr_flag_t)(1<<2))
+#define ADDR_FLAG_WR_OK ((addr_flag_t)(1<<3))
+#define ADDR_FLAG_NEEDS_DREQ ((addr_flag_t)(1<<4))
+
+static addr_flag_t dma_classify_addr(volatile const void *_addr) {
+ uintptr_t addr = (uintptr_t)_addr;
+ switch (addr >> 28) {
+ case 0x0: /* ROM */
+ if (addr < 0x4000)
+ return ADDR_FLAG_RD_OK;
+ return ADDR_FLAG_UNMAPPED;
+ case 0x1: /* XIP */
+ switch ((addr >> 24)&0xf) {
+ case 0x0: case 0x1: case 0x2: case 0x3: /* not safe for DMA */
+ return ADDR_FLAG_UNSAFE;
+ case 0x4: /* CTRL registers */
+ if (addr < 0x14000020)
+ return ADDR_FLAG_RD_OK | ADDR_FLAG_WR_OK;
+ return ADDR_FLAG_UNMAPPED;
+ case 0x5: /* SRAM */
+ if (addr < 0x15004000)
+ return ADDR_FLAG_RD_OK | ADDR_FLAG_WR_OK;
+ return ADDR_FLAG_UNMAPPED;
+ case 0x8: /* SSI registers */
+ if (addr < 0x18000064)
+ return ADDR_FLAG_RD_OK | ADDR_FLAG_WR_OK;
+ if (0x180000f0 <= addr && addr < 0x180000fc)
+ return ADDR_FLAG_RD_OK | ADDR_FLAG_WR_OK;
+ return ADDR_FLAG_UNMAPPED;
+ }
+ return ADDR_FLAG_UNMAPPED;
+ case 0x2: /* SRAM */
+ if ((addr & 0xfeffffff) < 0x20040000) /* banks 0-3 striped/unstriped depending on bit */
+ return ADDR_FLAG_RD_OK | ADDR_FLAG_WR_OK;
+ if (addr < 0x20042000) /* banks 4-5 */
+ return ADDR_FLAG_RD_OK | ADDR_FLAG_WR_OK;
+ return ADDR_FLAG_UNMAPPED;
+ case 0x4: /* APB Peripherals */
+ /* TODO */
+ return ADDR_FLAG_RD_OK | ADDR_FLAG_WR_OK;
+ case 0x5: /* AHB-Lite Peripherals */
+ /* TODO */
+ return ADDR_FLAG_RD_OK | ADDR_FLAG_WR_OK;
+ case 0xd: /* IOPORT Registers */
+ /* TODO */
+ return ADDR_FLAG_RD_OK | ADDR_FLAG_WR_OK;
+ case 0xe: /* Cortex-M0+ internal registers */
+ /* TODO */
+ return ADDR_FLAG_RD_OK | ADDR_FLAG_WR_OK;
+ }
+ return ADDR_FLAG_UNMAPPED;
+}
+
+bool dma_is_unsafe(volatile const void *addr) {
+ return dma_classify_addr(addr) & ADDR_FLAG_UNSAFE;
+}
+
+#ifndef NDEBUG
+void dma_assert_addrs(volatile void *dst, volatile const void *src) {
+ addr_flag_t dst_flags = dma_classify_addr(dst);
+ addr_flag_t src_flags = dma_classify_addr(src);
+ bool bad = false;
+ if (!(dst_flags & ADDR_FLAG_WR_OK)) {
+ log_n_errorln(ASSERT, "dma_assert_addrs(", (ptr, dst), ", ", (ptr, src), "): invalid destination");
+ bad = true;
+ }
+ if (!(src_flags & ADDR_FLAG_RD_OK)) {
+ log_n_errorln(ASSERT, "dma_assert_addrs(", (ptr, dst), ", ", (ptr, src), "): invalid source");
+ bad = true;
+ }
+ if (!bad && (dst_flags & ADDR_FLAG_NEEDS_DREQ && src_flags & ADDR_FLAG_NEEDS_DREQ) ) {
+ log_n_errorln(ASSERT, "dma_assert_addrs(", (ptr, dst), ", ", (ptr, src), "): source and destination both required DREQs");
+ bad = true;
+ }
+ if (bad)
+ __lm_abort();
+}
+#endif
+
struct dmairq_handler_entry {
dmairq_handler_t fn;
void *arg;
};
-struct dmairq_handler_entry dmairq_handlers[NUM_DMA_CHANNELS] = {0};
+struct dmairq_handler_entry dmairq_handlers[NUM_DMA_CHANNELS] = {};
-bool dmairq_initialized[NUM_DMA_IRQS] = {0};
+bool dmairq_initialized[NUM_DMA_IRQS] = {};
static void dmairq_handler(void) {
enum dmairq irq = __get_current_exception() - VTABLE_FIRST_IRQ;
@@ -24,6 +122,7 @@ static void dmairq_handler(void) {
assert(irq_idx < NUM_DMA_IRQS);
uint32_t regval = dma_hw->irq_ctrl[irq_idx].ints;
+ dma_hw->intr = regval; /* acknowledge irq */
for (uint channel = 0; channel < NUM_DMA_CHANNELS; channel++) {
if (regval & 1u<<channel) {
struct dmairq_handler_entry *handler = &dmairq_handlers[channel];
@@ -31,8 +130,6 @@ static void dmairq_handler(void) {
handler->fn(handler->arg, irq, channel);
}
}
- /* acknowledge irq */
- dma_hw->intr = regval;
}
void dmairq_set_and_enable_exclusive_handler(enum dmairq irq, uint channel, dmairq_handler_t fn, void *arg) {
diff --git a/libhw_cr/rp2040_dma.h b/libhw_cr/rp2040_dma.h
index c7f5a8f..1392e1f 100644
--- a/libhw_cr/rp2040_dma.h
+++ b/libhw_cr/rp2040_dma.h
@@ -10,33 +10,20 @@
#ifndef _LIBHW_CR_RP2040_DMA_H_
#define _LIBHW_CR_RP2040_DMA_H_
-#include <assert.h>
#include <stddef.h> /* for offsetof() */
#include <stdint.h> /* for uint32_t */
#include <hardware/regs/dreq.h> /* for DREQ_* for use with DMA_CTRL_TREQ_SEL() */
-#include <hardware/structs/dma.h> /* for dma_hw, dma_channel_hw_t, DMA_NUM_CHANNELS */
+#include <hardware/structs/dma.h> /* for dma_hw, DMA_NUM_CHANNELS */
+#include <libmisc/assert.h>
#include <libmisc/macro.h> /* for LM_FLOORLOG2() */
-/* Borrowed from <hardware/dma.h> *********************************************/
-
-static inline dma_channel_hw_t *dma_channel_hw_addr(uint channel) {
- assert(channel < NUM_DMA_CHANNELS);
- return &dma_hw->ch[channel];
-}
-
-enum dma_channel_transfer_size {
- DMA_SIZE_8 = 0, ///< Byte transfer (8 bits)
- DMA_SIZE_16 = 1, ///< Half word transfer (16 bits)
- DMA_SIZE_32 = 2 ///< Word transfer (32 bits)
-};
-
-/* Our own code ***************************************************************/
+/* IRQ ************************************************************************/
enum dmairq {
- DMAIRQ_0 = DMA_IRQ_0,
- DMAIRQ_1 = DMA_IRQ_1,
+ DMAIRQ_0 = 11,
+ DMAIRQ_1 = 12,
};
typedef void (*dmairq_handler_t)(void *arg, enum dmairq irq, uint channel);
@@ -46,61 +33,64 @@ typedef void (*dmairq_handler_t)(void *arg, enum dmairq irq, uint channel);
* has a NULL trigger (depending on the channel's configuration).
*
* Your handler does not need to acknowledge the IRQ; that will be
- * done for you after your handler is called.
+ * done for you before your handler is called.
*
* It is illegal to enable the same channel on more than one IRQ.
*/
void dmairq_set_and_enable_exclusive_handler(enum dmairq irq, uint channel, dmairq_handler_t fn, void *arg);
-#define DMA_CTRL_ENABLE (1<<0)
-#define DMA_CTRL_HI_PRIO (1<<1)
-#define DMA_CTRL_DATA_SIZE(sz) ((sz)<<2)
-#define DMA_CTRL_INCR_READ (1<<4)
-#define DMA_CTRL_INCR_WRITE (1<<5)
-#define _DMA_CTRL_RING_BITS(b) ((b)<<6)
-#define _DMA_CTRL_RING_RD (0)
-#define _DMA_CTRL_RING_WR (1<<10)
-#define DMA_CTRL_RING(rdwr, bits) (_DMA_CTRL_RING_##rdwr | _DMA_CTRL_RING_BITS(bits))
-#define DMA_CTRL_CHAIN_TO(ch) ((ch)<<11)
-#define DMA_CTRL_TREQ_SEL(dreq) ((dreq)<<15)
-#define DMA_CTRL_IRQ_QUIET (1<<21)
-#define DMA_CTRL_BSWAP (1<<22)
-#define DMA_CTRL_SNIFF_EN (1<<23)
-
-/* | elem | val | name */
-#define READ_ADDR /*|*/volatile const void/*|*/ * /*|*/read_addr
-#define WRITE_ADDR /*|*/volatile void/*|*/ * /*|*/write_addr
-#define TRANS_COUNT /*|*/ /*|*/uint32_t/*|*/trans_count
-#define CTRL /*|*/ /*|*/uint32_t/*|*/ctrl
-
-/* { +0x0 ; +0x4 ; +0x8 ; +0xC (Trigger) */
-struct dma_alias0 { READ_ADDR ; WRITE_ADDR ; TRANS_COUNT ; CTRL ; };
-struct dma_alias1 { CTRL ; READ_ADDR ; WRITE_ADDR ; TRANS_COUNT ; };
-struct dma_alias2 { CTRL ; TRANS_COUNT ; READ_ADDR ; WRITE_ADDR ; };
-struct dma_alias3 { CTRL ; WRITE_ADDR ; TRANS_COUNT ; READ_ADDR ; };
-struct dma_alias0_short2 { TRANS_COUNT ; CTRL ; };
-struct dma_alias1_short2 { WRITE_ADDR ; TRANS_COUNT ; };
-struct dma_alias2_short2 { READ_ADDR ; WRITE_ADDR ; };
-struct dma_alias3_short2 { TRANS_COUNT ; READ_ADDR ; };
-struct dma_alias0_short3 { CTRL ; };
-struct dma_alias1_short3 { TRANS_COUNT ; };
-struct dma_alias2_short3 { WRITE_ADDR ; };
-struct dma_alias3_short3 { READ_ADDR ; };
+/* Control blocks *************************************************************/
+
+bool dma_is_unsafe(volatile const void *addr);
+#ifdef NDEBUG
+#define dma_assert_addrs(dst, src) ((void)0)
+#else
+void dma_assert_addrs(volatile void *dst, volatile const void *src);
+#endif
+
+/* types =================================================*/
+
+/* | elem | val | name */
+#define READ_ADDR /*|*/volatile const void/*|*/ * /*|*/read_addr
+#define WRITE_ADDR /*|*/volatile void/*|*/ * /*|*/write_addr
+#define XFER_COUNT /*|*/ /*|*/uint32_t/*|*/xfer_count
+#define CTRL /*|*/ /*|*/uint32_t/*|*/ctrl
+
+/* { +0x0 ; +0x4 ; +0x8 ; +0xC (Trigger) */
+struct dma_alias0 { READ_ADDR ; WRITE_ADDR ; XFER_COUNT ; CTRL ; };
+struct dma_alias1 { CTRL ; READ_ADDR ; WRITE_ADDR ; XFER_COUNT ; };
+struct dma_alias2 { CTRL ; XFER_COUNT ; READ_ADDR ; WRITE_ADDR ; };
+struct dma_alias3 { CTRL ; WRITE_ADDR ; XFER_COUNT ; READ_ADDR ; };
+struct dma_alias0_short2 { XFER_COUNT ; CTRL ; };
+struct dma_alias1_short2 { WRITE_ADDR ; XFER_COUNT ; };
+struct dma_alias2_short2 { READ_ADDR ; WRITE_ADDR ; };
+struct dma_alias3_short2 { XFER_COUNT ; READ_ADDR ; };
+struct dma_alias0_short3 { CTRL ; };
+struct dma_alias1_short3 { XFER_COUNT ; };
+struct dma_alias2_short3 { WRITE_ADDR ; };
+struct dma_alias3_short3 { READ_ADDR ; };
#undef CTRL
-#undef TRANS_COUNT
+#undef XFER_COUNT
#undef WRITE_ADDR
#undef READ_ADDR
+/* locations =============================================*/
+
+dma_channel_hw_t *dma_channel_hw_addr(uint channel);
+
#define DMA_CHAN_ADDR(CH, TYP) ((TYP *volatile)_Generic((TYP){}, \
+ \
struct dma_alias0: &dma_channel_hw_addr(CH)->read_addr, \
struct dma_alias1: &dma_channel_hw_addr(CH)->al1_ctrl, \
struct dma_alias2: &dma_channel_hw_addr(CH)->al2_ctrl, \
struct dma_alias3: &dma_channel_hw_addr(CH)->al3_ctrl, \
+ \
struct dma_alias0_short2: &dma_channel_hw_addr(CH)->transfer_count, \
struct dma_alias1_short2: &dma_channel_hw_addr(CH)->al1_write_addr, \
struct dma_alias2_short2: &dma_channel_hw_addr(CH)->al2_read_addr, \
struct dma_alias3_short2: &dma_channel_hw_addr(CH)->al3_transfer_count, \
+ \
struct dma_alias0_short3: &dma_channel_hw_addr(CH)->ctrl_trig, \
struct dma_alias1_short3: &dma_channel_hw_addr(CH)->al1_transfer_count_trig, \
struct dma_alias2_short3: &dma_channel_hw_addr(CH)->al2_write_addr_trig, \
@@ -108,24 +98,48 @@ struct dma_alias3_short3 { READ_ADDR ; };
#define DMA_IS_TRIGGER(TYP, FIELD) (offsetof(TYP, FIELD) == 0xC)
-#define DMA_CHAN_WR_TRANS_COUNT(TYP) \
- (sizeof(TYP)/4)
-
-#define DMA_CHAN_WR_CTRL(TYP) ( DMA_CTRL_DATA_SIZE(DMA_SIZE_32) \
- | DMA_CTRL_INCR_WRITE \
- | DMA_CTRL_RING(WR, LM_FLOORLOG2(sizeof(TYP))) \
- )
-
#define DMA_NONTRIGGER(CH, FIELD) (DMA_CHAN_ADDR(CH, _DMA_NONTRIGGER_##FIELD)->FIELD)
#define _DMA_NONTRIGGER_read_addr struct dma_alias0
#define _DMA_NONTRIGGER_write_addr struct dma_alias0
-#define _DMA_NONTRIGGER_trans_count struct dma_alias0
+#define _DMA_NONTRIGGER_xfer_count struct dma_alias0
#define _DMA_NONTRIGGER_ctrl struct dma_alias1
#define DMA_TRIGGER(CH, FIELD) (DMA_CHAN_ADDR(CH, _DMA_TRIGGER_##FIELD)->FIELD)
#define _DMA_TRIGGER_read_addr struct dma_alias3
#define _DMA_TRIGGER_write_addr struct dma_alias2
-#define _DMA_TRIGGER_trans_count struct dma_alias1
+#define _DMA_TRIGGER_xfer_count struct dma_alias1
#define _DMA_TRIGGER_ctrl struct dma_alias0
+/* block->ctrl register *******************************************************/
+
+enum dma_transfer_size {
+ DMA_SIZE_8 = 0, /** Byte transfer (8 bits) */
+ DMA_SIZE_16 = 1, /** Half word transfer (16 bits) */
+ DMA_SIZE_32 = 2, /** Word transfer (32 bits) */
+};
+
+#define DMA_CTRL_ENABLE (1<<0)
+#define DMA_CTRL_HI_PRIO (1<<1)
+#define DMA_CTRL_DATA_SIZE(sz) ((sz)<<2)
+#define DMA_CTRL_INCR_READ (1<<4)
+#define DMA_CTRL_INCR_WRITE (1<<5)
+#define _DMA_CTRL_RING_BITS(b) ((b)<<6)
+#define _DMA_CTRL_RING_RD (0)
+#define _DMA_CTRL_RING_WR (1<<10)
+#define DMA_CTRL_RING(rdwr, bits) (_DMA_CTRL_RING_##rdwr | _DMA_CTRL_RING_BITS(bits))
+#define DMA_CTRL_CHAIN_TO(ch) ((ch)<<11)
+#define DMA_CTRL_TREQ_SEL(dreq) ((dreq)<<15)
+#define DMA_CTRL_IRQ_QUIET (1<<21)
+#define DMA_CTRL_BSWAP (1<<22)
+#define DMA_CTRL_SNIFF_EN (1<<23)
+
+/* Utilities to build control blocks that write to other control blocks *******/
+
+#define DMA_CHAN_WR_XFER_COUNT(TYP) \
+ (sizeof(TYP)/4)
+#define DMA_CHAN_WR_CTRL(TYP) ( DMA_CTRL_DATA_SIZE(DMA_SIZE_32) \
+ | DMA_CTRL_INCR_WRITE \
+ | DMA_CTRL_RING(WR, LM_FLOORLOG2(sizeof(TYP))) \
+ )
+
#endif /* _LIBHW_CR_RP2040_DMA_H_ */
diff --git a/libhw_cr/rp2040_gpioirq.c b/libhw_cr/rp2040_gpioirq.c
index 1ae74f9..ecbdb04 100644
--- a/libhw_cr/rp2040_gpioirq.c
+++ b/libhw_cr/rp2040_gpioirq.c
@@ -4,8 +4,8 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
+#include <hardware/irq.h> /* for irq_set_exclusive_handler() */
#include <hardware/structs/io_bank0.h> /* for io_bank0_hw */
-#include <hardware/irq.h> /* for irq_set_exclusive_handler() */
#include <libmisc/macro.h>
@@ -15,7 +15,7 @@ struct gpioirq_handler_entry {
gpioirq_handler_t fn;
void *arg;
};
-struct gpioirq_handler_entry gpioirq_handlers[NUM_BANK0_GPIOS][4] = {0};
+struct gpioirq_handler_entry gpioirq_handlers[NUM_BANK0_GPIOS][4] = {};
int gpioirq_core = -1;
diff --git a/libhw_cr/rp2040_hwspi.c b/libhw_cr/rp2040_hwspi.c
index d181650..f667332 100644
--- a/libhw_cr/rp2040_hwspi.c
+++ b/libhw_cr/rp2040_hwspi.c
@@ -4,14 +4,12 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-#include <alloca.h>
-#include <inttypes.h> /* for PRIu{n} */
-
#include <hardware/clocks.h> /* for clock_get_hz() and clk_peri */
#include <hardware/gpio.h>
#include <hardware/spi.h>
#include <libcr/coroutine.h>
+#include <libmisc/alloc.h>
#include <libmisc/assert.h>
#define LOG_NAME RP2040_SPI
@@ -29,23 +27,40 @@
#ifndef CONFIG_RP2040_SPI_DEBUG
#error config.h must define CONFIG_RP2040_SPI_DEBUG (bool)
#endif
+#ifndef CONFIG_RP2040_SPI_MAX_DMABUF
+ #error config.h must define CONFIG_RP2040_SPI_DEBUG (non-negative integer)
+#endif
-LO_IMPLEMENTATION_C(io_duplex_readwriter, struct rp2040_hwspi, rp2040_hwspi, static);
-LO_IMPLEMENTATION_C(spi, struct rp2040_hwspi, rp2040_hwspi, static);
+LO_IMPLEMENTATION_C(io_duplex_readwriter, struct rp2040_hwspi, rp2040_hwspi);
+LO_IMPLEMENTATION_C(spi, struct rp2040_hwspi, rp2040_hwspi);
static void rp2040_hwspi_intrhandler(void *_self, enum dmairq LM_UNUSED(irq), uint LM_UNUSED(channel)) {
struct rp2040_hwspi *self = _self;
gpio_put(self->pin_cs, 1);
- cr_sema_signal_from_intrhandler(&self->sema);
+ assert(((spi_hw_t *)self->inst)->sr == 0b11);
+ cr_unpause_from_intrhandler(self->waiter);
}
+#define assert_2distinct(a, b) \
+ assert(a != b)
+
+#define assert_3distinct(a, b, c) \
+ assert_2distinct(a, b); \
+ assert(c != a); \
+ assert(c != b)
+
#define assert_4distinct(a, b, c, d) \
- assert(a != b); \
- assert(a != c); \
- assert(a != d); \
- assert(b != c); \
- assert(b != d); \
- assert(c != d);
+ assert_3distinct(a, b, c); \
+ assert(d != a); \
+ assert(d != b); \
+ assert(d != c)
+
+#define assert_5distinct(a, b, c, d, e) \
+ assert_4distinct(a, b, c, d); \
+ assert(e != a); \
+ assert(e != b); \
+ assert(e != c); \
+ assert(e != d)
void _rp2040_hwspi_init(struct rp2040_hwspi *self,
enum rp2040_hwspi_instance inst_num,
@@ -70,10 +85,12 @@ void _rp2040_hwspi_init(struct rp2040_hwspi *self,
assert(self);
assert(baudrate_hz);
uint32_t clk_peri_hz = clock_get_hz(clk_peri);
- debugf("clk_peri = %"PRIu32"Hz", clk_peri_hz);
+ log_debugln("clk_peri = ", clk_peri_hz, "Hz");
assert(baudrate_hz*2 <= clk_peri_hz);
assert_4distinct(pin_miso, pin_mosi, pin_clk, pin_cs);
- assert_4distinct(dma1, dma2, dma3, dma4);
+ /* I don't trust DMA channel 0
+ * https://github.com/raspberrypi/pico-feedback/issues/464 */
+ assert_5distinct(0, dma1, dma2, dma3, dma4);
/* Regarding the constraints on pin assignments: see the
* RP2040 datasheet, table 2, in §1.4.3 "GPIO Functions". */
@@ -96,8 +113,9 @@ void _rp2040_hwspi_init(struct rp2040_hwspi *self,
assert_notreached("invalid hwspi instance number");
}
+ /* Initialize the PL022. */
actual_baudrate_hz = spi_init(inst, baudrate_hz);
- debugf("baudrate = %uHz", actual_baudrate_hz);
+ log_debugln("baudrate = ", actual_baudrate_hz, "Hz");
assert(actual_baudrate_hz == baudrate_hz);
spi_set_format(inst, 8,
(mode & 0b10) ? SPI_CPOL_1 : SPI_CPOL_0,
@@ -130,7 +148,6 @@ void _rp2040_hwspi_init(struct rp2040_hwspi *self,
self->dma_tx_data = dma3;
self->dma_rx_data = dma4;
self->dead_until_ns = 0;
- self->sema = (cr_sema_t){};
/* Initialize the interrupt handler. */
/* We do this on (just) the rx channel, because the way the
@@ -138,7 +155,7 @@ void _rp2040_hwspi_init(struct rp2040_hwspi *self,
dmairq_set_and_enable_exclusive_handler(DMAIRQ_0, self->dma_rx_data, rp2040_hwspi_intrhandler, self);
}
-static void rp2040_hwspi_readwritev(struct rp2040_hwspi *self, const struct duplex_iovec *iov, int iovcnt) {
+size_t_and_error rp2040_hwspi_readwritev(struct rp2040_hwspi *self, const struct duplex_iovec *iov, int iovcnt) {
assert(self);
assert(self->inst);
assert(iov);
@@ -158,29 +175,38 @@ static void rp2040_hwspi_readwritev(struct rp2040_hwspi *self, const struct dupl
uint8_t bogus_rx_dst;
+ size_t count = 0;
+ size_t unsafe_count = 0;
int pruned_iovcnt = 0;
- for (int i = 0; i < iovcnt; i++)
- if (iov[i].iov_len)
- pruned_iovcnt++;
- if (!pruned_iovcnt)
- return;
+ for (int i = 0; i < iovcnt; i++) {
+ if (!iov[i].iov_len)
+ continue;
+ pruned_iovcnt++;
+ count += iov[i].iov_len;
+ if (dma_is_unsafe(iov[i].iov_write_from))
+ unsafe_count += iov[i].iov_len;
+ }
+ assert(count);
+ assert(unsafe_count <= CONFIG_RP2040_SPI_MAX_DMABUF);
- /* It doesn't *really* matter which aliases we choose:
+ assert(((spi_hw_t *)self->inst)->sr == 0b11);
+
+ /* The code following this initial declaration is generic to
+ * the alias, so changing which alias is used is easy. But
+ * which aliases should we choose?
*
- * - None of our fields can be NULL (so no
- * false-termination).
+ * Hard requirements:
*
- * - Moving const fields first so they don't have to be
- * re-programmed each time isn't possible for us; there
- * need to be at least 2 const fields, and we only have 1
- * (read_addr for rx_data_blocks, and write_addr for
- * tx_data_blocks).
+ * - The RP2040 can read from NULL (that's where the ROM is),
+ * so we need the tx channel's read_addr to not be the
+ * trigger, to avoid accidental null-triggers.
+ * false-termination).
*
- * The code following this initial declaration is generic to
- * the alias, so changing which alias is used is easy.
+ * Soft requirements:
*
- * Since we have no hard requirements, here are some mild
- * preferences:
+ * - We can't write to NULL (it's ROM), but let's give the
+ * same consideration to the rx channel's write_addr
+ * anyway.
*
* - I like the aliases being different for each channel,
* because it helps prevent alias-specific code from
@@ -192,80 +218,108 @@ static void rp2040_hwspi_readwritev(struct rp2040_hwspi *self, const struct dupl
* cleared before the trigger, and at the end the control
* block is clean and zeroed-out.
*
- * - Conversely, I like the tx channel (the non-interrupt
- * channel) having ctrl *not* be the trigger, so that
- * DMA_CTRL_IRQ_QUIET is cleared by the time the trigger
- * happens, so the IRQ machinery doesn't need to be engaged
- * at all.
+ * Non-requirements:
+ *
+ * - Moving const fields first so they don't have to be
+ * re-programmed each time isn't possible for us; there
+ * need to be at least 2 const fields, and we only have 1
+ * (read_addr for rx_data_blocks, and write_addr for
+ * tx_data_blocks).
*/
- struct dma_alias1 *tx_data_blocks = alloca(sizeof(struct dma_alias1)*(pruned_iovcnt+1));
- struct dma_alias0 *rx_data_blocks = alloca(sizeof(struct dma_alias0)*(pruned_iovcnt+1));
- static_assert(!DMA_IS_TRIGGER(typeof(tx_data_blocks[0]), ctrl));
- static_assert(DMA_IS_TRIGGER(typeof(rx_data_blocks[0]), ctrl));
+ [[gnu::cleanup(heap_cleanup)]] struct dma_alias1 *tx_data_blocks = heap_alloc(pruned_iovcnt, struct dma_alias1);
+ [[gnu::cleanup(heap_cleanup)]] struct dma_alias0 *rx_data_blocks = heap_alloc(pruned_iovcnt+1, struct dma_alias0); /* extra +1 block for null trigger */
+ /* hard requirements */
+ static_assert(!DMA_IS_TRIGGER(typeof(tx_data_blocks[0]), read_addr)); /* avoid accidental null-trigger */
+ /* soft requirements */
+ static_assert(!DMA_IS_TRIGGER(typeof(rx_data_blocks[0]), write_addr)); /* avoid accidental null-trigger */
+ static_assert(!__builtin_types_compatible_p(typeof(tx_data_blocks[0]), typeof(rx_data_blocks[0]))); /* help detect code errors */
+ static_assert(DMA_IS_TRIGGER(typeof(rx_data_blocks[0]), ctrl)); /* avoid needing to set IRQ_QUIET in the null-trigger block */
+ /* Core data blocks. */
+ [[gnu::cleanup(heap_cleanup)]] void *dmabuf = NULL;
+ if (unsafe_count)
+ dmabuf = heap_alloc(unsafe_count, char);
+ size_t dmabuf_pos = 0;
for (int i = 0, j = 0; i < iovcnt; i++) {
if (!iov[i].iov_len)
continue;
- tx_data_blocks[j] = (typeof(tx_data_blocks[0])){
- .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_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),
+
+ const void *write_from = iov[i].iov_write_from;
+ if (write_from == IOVEC_DISCARD)
+ write_from = &self->bogus_data;
+ else if (dma_is_unsafe(write_from)) {
+ memcpy(dmabuf+dmabuf_pos, write_from, iov[i].iov_len);
+ write_from = dmabuf+dmabuf_pos;
+ dmabuf_pos += iov[i].iov_len;
+ }
+ tx_data_blocks[j] = (typeof(tx_data_blocks[0])){
+ .read_addr = write_from,
+ .write_addr = &spi_get_hw(self->inst)->dr,
+ .xfer_count = iov[i].iov_len,
+ .ctrl = (DMA_CTRL_ENABLE
+ | DMA_CTRL_DATA_SIZE(DMA_SIZE_8)
+ | ((iov[i].iov_write_from != IOVEC_DISCARD) ? DMA_CTRL_INCR_READ : 0)
+ | ((j+1 < pruned_iovcnt) ? DMA_CTRL_CHAIN_TO(self->dma_tx_ctrl) : 0)
+ | DMA_CTRL_TREQ_SEL(SPI_DREQ_NUM(self->inst, true))),
};
- rx_data_blocks[j] = (typeof(rx_data_blocks[0])){
- .read_addr = &spi_get_hw(self->inst)->dr,
- .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_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),
+ dma_assert_addrs(tx_data_blocks[j].write_addr, tx_data_blocks[j].read_addr);
+
+ void *read_to = iov[i].iov_read_to;
+ if (read_to == IOVEC_DISCARD)
+ read_to = &bogus_rx_dst;
+ rx_data_blocks[j] = (typeof(rx_data_blocks[0])){
+ .read_addr = &spi_get_hw(self->inst)->dr,
+ .write_addr = read_to,
+ .xfer_count = iov[i].iov_len,
+ .ctrl = (DMA_CTRL_ENABLE
+ | DMA_CTRL_DATA_SIZE(DMA_SIZE_8)
+ | ((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),
};
+ dma_assert_addrs(rx_data_blocks[j].write_addr, rx_data_blocks[j].read_addr);
+
j++;
}
- tx_data_blocks[pruned_iovcnt] = (typeof(tx_data_blocks[0])){0};
- rx_data_blocks[pruned_iovcnt] = (typeof(rx_data_blocks[0])){0};
- /* If ctrl isn't the trigger then we need to make sure that
- * DMA_CTRL_IRQ_QUIET isn't cleared before the trigger
- * happens. */
- if (!DMA_IS_TRIGGER(typeof(rx_data_blocks[0]), ctrl))
- rx_data_blocks[pruned_iovcnt].ctrl = DMA_CTRL_IRQ_QUIET;
+ /* Null-trigger (generate IRQ). */
+ rx_data_blocks[pruned_iovcnt] = (typeof(rx_data_blocks[0])){
+ /* If ctrl isn't the trigger then we need to make sure
+ * that DMA_CTRL_IRQ_QUIET isn't cleared before the
+ * trigger happens. */
+ .ctrl = DMA_IS_TRIGGER(typeof(rx_data_blocks[0]), ctrl) ? 0 : DMA_CTRL_IRQ_QUIET,
+ };
/* Set up ctrl. */
DMA_NONTRIGGER(self->dma_tx_ctrl, read_addr) = tx_data_blocks;
DMA_NONTRIGGER(self->dma_tx_ctrl, write_addr) = DMA_CHAN_ADDR(self->dma_tx_data, typeof(tx_data_blocks[0]));
- DMA_NONTRIGGER(self->dma_tx_ctrl, trans_count) = DMA_CHAN_WR_TRANS_COUNT(typeof(tx_data_blocks[0]));
+ DMA_NONTRIGGER(self->dma_tx_ctrl, xfer_count) = DMA_CHAN_WR_XFER_COUNT(typeof(tx_data_blocks[0]));
DMA_NONTRIGGER(self->dma_tx_ctrl, ctrl) = (DMA_CTRL_ENABLE
| DMA_CHAN_WR_CTRL(typeof(tx_data_blocks[0]))
| DMA_CTRL_INCR_READ
- | DMA_CTRL_CHAIN_TO(self->dma_tx_data)
- | DMA_CTRL_TREQ_SEL(DREQ_FORCE)
- | DMA_CTRL_IRQ_QUIET);
+ | DMA_CTRL_TREQ_SEL(DREQ_FORCE));
DMA_NONTRIGGER(self->dma_rx_ctrl, read_addr) = rx_data_blocks;
DMA_NONTRIGGER(self->dma_rx_ctrl, write_addr) = DMA_CHAN_ADDR(self->dma_rx_data, typeof(rx_data_blocks[0]));
- DMA_NONTRIGGER(self->dma_rx_ctrl, trans_count) = DMA_CHAN_WR_TRANS_COUNT(typeof(rx_data_blocks[0]));
+ DMA_NONTRIGGER(self->dma_rx_ctrl, xfer_count) = DMA_CHAN_WR_XFER_COUNT(typeof(rx_data_blocks[0]));
DMA_NONTRIGGER(self->dma_rx_ctrl, ctrl) = (DMA_CTRL_ENABLE
| DMA_CHAN_WR_CTRL(typeof(rx_data_blocks[0]))
| DMA_CTRL_INCR_READ
- | DMA_CTRL_CHAIN_TO(self->dma_rx_data)
- | DMA_CTRL_TREQ_SEL(DREQ_FORCE)
- | DMA_CTRL_IRQ_QUIET);
+ | DMA_CTRL_TREQ_SEL(DREQ_FORCE));
/* Run. */
- uint64_t now = LO_CALL(bootclock, get_time_ns);
- if (now < self->dead_until_ns)
+
+ self->waiter = cr_getcid();
+
+ if (LO_CALL(bootclock, get_time_ns) < self->dead_until_ns)
sleep_until_ns(self->dead_until_ns);
+
bool saved = cr_save_and_disable_interrupts();
gpio_put(self->pin_cs, 0);
- dma_hw->multi_channel_trigger = (1u<<self->dma_tx_ctrl) | (1u<<self->dma_rx_ctrl);
+ dma_hw->multi_channel_trigger = (1<<self->dma_tx_ctrl) | (1<<self->dma_rx_ctrl);
+ cr_pause_and_yield();
+ assert(((spi_hw_t *)self->inst)->sr == 0b11);
cr_restore_interrupts(saved);
- cr_sema_wait(&self->sema);
+
self->dead_until_ns = LO_CALL(bootclock, get_time_ns) + self->min_delay_ns;
+ return ERROR_AND(size_t, count, ERROR_NULL);
}
diff --git a/libhw_cr/rp2040_hwtimer.c b/libhw_cr/rp2040_hwtimer.c
index 8227abb..6d7e868 100644
--- a/libhw_cr/rp2040_hwtimer.c
+++ b/libhw_cr/rp2040_hwtimer.c
@@ -27,8 +27,7 @@ struct rp2040_hwtimer {
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);
+LO_IMPLEMENTATION_STATIC(alarmclock, struct rp2040_hwtimer, rp2040_hwtimer);
/* Globals ********************************************************************/
@@ -44,7 +43,7 @@ static_assert(sizeof(hwtimers)/sizeof(hwtimers[0]) == _RP2040_HWALARM_NUM);
lo_interface alarmclock rp2040_hwtimer(enum rp2040_hwalarm_instance alarm_num) {
assert(alarm_num < _RP2040_HWALARM_NUM);
- return lo_box_rp2040_hwtimer_as_alarmclock(&hwtimers[alarm_num]);
+ return LO_BOX(alarmclock, &hwtimers[alarm_num]);
}
@@ -52,7 +51,11 @@ static uint64_t rp2040_hwtimer_get_time_ns(struct rp2040_hwtimer *) {
return timer_time_us_64(timer_hw) * (NS_PER_S/US_PER_S);
}
-#define NS_TO_US_ROUNDUP(x) LM_CEILDIV(x, NS_PER_S/US_PER_S)
+static uint32_t ns_to_us_roundup_and_cap(uint64_t ns) {
+ if (ns >= ((uint64_t)(UINT32_MAX))*1000)
+ return UINT32_MAX;
+ return (ns+999)/1000;
+}
static void rp2040_hwtimer_intrhandler(void) {
uint irq_num = __get_current_exception() - VTABLE_FIRST_IRQ;
@@ -62,7 +65,7 @@ static void rp2040_hwtimer_intrhandler(void) {
struct rp2040_hwtimer *alarmclock = &hwtimers[alarm_num];
while (alarmclock->queue &&
- NS_TO_US_ROUNDUP(alarmclock->queue->fire_at_ns) <= timer_time_us_64(timer_hw)) {
+ alarmclock->queue->fire_at_ns <= timer_time_us_64(timer_hw)*1000) {
struct alarmclock_trigger *trigger = alarmclock->queue;
trigger->cb(trigger->cb_arg);
alarmclock->queue = trigger->next;
@@ -74,10 +77,10 @@ static void rp2040_hwtimer_intrhandler(void) {
hw_clear_bits(&timer_hw->intf, 1 << alarm_num); /* Clear "force"ing the interrupt. */
hw_clear_bits(&timer_hw->intr, 1 << alarm_num); /* Clear natural firing of the alarm. */
if (alarmclock->queue)
- timer_hw->alarm[alarm_num] = (uint32_t)NS_TO_US_ROUNDUP(alarmclock->queue->fire_at_ns);
+ timer_hw->alarm[alarm_num] = ns_to_us_roundup_and_cap(alarmclock->queue->fire_at_ns);
}
-static bool rp2040_hwtimer_add_trigger(struct rp2040_hwtimer *alarmclock,
+static void rp2040_hwtimer_add_trigger(struct rp2040_hwtimer *alarmclock,
struct alarmclock_trigger *trigger,
uint64_t fire_at_ns,
void (*cb)(void *),
@@ -87,18 +90,13 @@ static bool rp2040_hwtimer_add_trigger(struct rp2040_hwtimer *alarmclock,
assert(fire_at_ns);
assert(cb);
- uint64_t now_us = timer_time_us_64(timer_hw);
- if (NS_TO_US_ROUNDUP(fire_at_ns) > now_us &&
- (NS_TO_US_ROUNDUP(fire_at_ns) - now_us) > UINT32_MAX)
- /* Too far in the future. */
- return true;
-
trigger->alarmclock = alarmclock;
trigger->fire_at_ns = fire_at_ns;
trigger->cb = cb;
trigger->cb_arg = cb_arg;
bool saved = cr_save_and_disable_interrupts();
+
struct alarmclock_trigger **dst = &alarmclock->queue;
while (*dst && fire_at_ns >= (*dst)->fire_at_ns)
dst = &(*dst)->next;
@@ -107,6 +105,7 @@ static bool rp2040_hwtimer_add_trigger(struct rp2040_hwtimer *alarmclock,
if (*dst)
(*dst)->prev = trigger;
*dst = trigger;
+
if (!alarmclock->initialized) {
hw_set_bits(&timer_hw->inte, 1 << alarmclock->alarm_num);
irq_set_exclusive_handler(TIMER_ALARM_IRQ_NUM(timer_hw, alarmclock->alarm_num),
@@ -114,6 +113,7 @@ static bool rp2040_hwtimer_add_trigger(struct rp2040_hwtimer *alarmclock,
irq_set_enabled(TIMER_ALARM_IRQ_NUM(timer_hw, alarmclock->alarm_num), true);
alarmclock->initialized = true;
}
+
if (alarmclock->queue == trigger) {
/* "Force" the interrupt handler to trigger as soon as
* we enable interrupts. This handles the case of
@@ -126,9 +126,8 @@ static bool rp2040_hwtimer_add_trigger(struct rp2040_hwtimer *alarmclock,
* fire. */
hw_set_bits(&timer_hw->intf, 1 << alarmclock->alarm_num);
}
- cr_restore_interrupts(saved);
- return false;
+ cr_restore_interrupts(saved);
}
static void rp2040_hwtimer_del_trigger(struct rp2040_hwtimer *alarmclock,
diff --git a/libhw_cr/rp2040_include/libhw/rp2040_hwspi.h b/libhw_cr/rp2040_include/libhw/rp2040_hwspi.h
index 4951136..cbd8dcf 100644
--- a/libhw_cr/rp2040_include/libhw/rp2040_hwspi.h
+++ b/libhw_cr/rp2040_include/libhw/rp2040_hwspi.h
@@ -9,7 +9,7 @@
#include <pico/binary_info.h> /* for bi_* */
-#include <libcr_ipc/sema.h>
+#include <libcr/coroutine.h>
#include <libmisc/private.h>
#include <libhw/generic/spi.h>
@@ -33,7 +33,7 @@ struct rp2040_hwspi {
/* mutable */
uint64_t dead_until_ns;
- cr_sema_t sema;
+ cid_t waiter;
END_PRIVATE(LIBHW_RP2040_HWSPI_H);
};
LO_IMPLEMENTATION_H(io_duplex_readwriter, struct rp2040_hwspi, rp2040_hwspi);
@@ -99,7 +99,7 @@ LO_IMPLEMENTATION_H(spi, struct rp2040_hwspi, rp2040_hwspi);
min_delay_ns, bogus_data, \
pin_miso, pin_mosi, pin_clk, pin_cs, \
dma1, dma2, dma3, dma4); \
- } while(0)
+ } while (0)
void _rp2040_hwspi_init(struct rp2040_hwspi *self,
enum rp2040_hwspi_instance inst_num,
enum spi_mode mode,
diff --git a/libhw_cr/rp2040_include/libhw/w5500.h b/libhw_cr/rp2040_include/libhw/w5500.h
index 8dda1a1..43c58a3 100644
--- a/libhw_cr/rp2040_include/libhw/w5500.h
+++ b/libhw_cr/rp2040_include/libhw/w5500.h
@@ -41,19 +41,6 @@ struct _w5500_socket {
END_PRIVATE(LIBHW_W5500_H);
};
-LO_IMPLEMENTATION_H(io_closer, struct _w5500_socket, w5500_tcplist);
-LO_IMPLEMENTATION_H(net_stream_listener, struct _w5500_socket, w5500_tcplist);
-
-LO_IMPLEMENTATION_H(io_reader, struct _w5500_socket, w5500_tcp);
-LO_IMPLEMENTATION_H(io_writer, struct _w5500_socket, w5500_tcp);
-LO_IMPLEMENTATION_H(io_readwriter, struct _w5500_socket, w5500_tcp);
-LO_IMPLEMENTATION_H(io_closer, struct _w5500_socket, w5500_tcp);
-LO_IMPLEMENTATION_H(io_bidi_closer, struct _w5500_socket, w5500_tcp);
-LO_IMPLEMENTATION_H(net_stream_conn, struct _w5500_socket, w5500_tcp);
-
-LO_IMPLEMENTATION_H(io_closer, struct _w5500_socket, w5500_udp);
-LO_IMPLEMENTATION_H(net_packet_conn, struct _w5500_socket, w5500_udp);
-
struct w5500 {
BEGIN_PRIVATE(LIBHW_W5500_H);
/* const-after-init */
diff --git a/libhw_cr/w5500.c b/libhw_cr/w5500.c
index c04e344..5189512 100644
--- a/libhw_cr/w5500.c
+++ b/libhw_cr/w5500.c
@@ -8,6 +8,7 @@
* https://github.com/Wiznet/ioLibrary_Driver/blob/b981401e7f3d07015619adf44c13998e13e777f9/Ethernet/W5500/w5500.h
* https://github.com/Wiznet/ioLibrary_Driver/blob/b981401e7f3d07015619adf44c13998e13e777f9/Ethernet/W5500/w5500.c
* https://github.com/Wiznet/ioLibrary_Driver/blob/b981401e7f3d07015619adf44c13998e13e777f9/Ethernet/socket.c
+ * https://github.com/Wiznet/ioLibrary_Driver/blob/b981401e7f3d07015619adf44c13998e13e777f9/Internet/DHCP/dhcp.c
*
* Copyright (c) 2013, WIZnet Co., LTD.
* All rights reserved.
@@ -67,19 +68,19 @@
* SPDX-License-Identifier: MIT
*/
-#include <inttypes.h> /* for PRIu{n} */
+#include <string.h> /* for memcmp() */
/* TODO: Write a <libhw/generic/gpio.h> to avoid w5500.c being
* pico-sdk-specific. */
-#include <hardware/gpio.h> /* pico-sdk:hardware_gpio */
#include "rp2040_gpioirq.h"
+#include <hardware/gpio.h> /* pico-sdk:hardware_gpio */
#include <libcr/coroutine.h> /* for cr_yield() */
#include <libhw/generic/alarmclock.h> /* for sleep_*() */
#define LOG_NAME W5500
-#include <libmisc/log.h> /* for errorf(), debugf(), const_byte_str() */
+#include <libmisc/log.h>
#define IMPLEMENTATION_FOR_LIBHW_W5500_H YES
#include <libhw/w5500.h>
@@ -124,22 +125,21 @@ static const char *w5500_state_str(uint8_t state) {
}
}
-/* libobj *********************************************************************/
+/* libmisc/obj.h **************************************************************/
-LO_IMPLEMENTATION_C(io_closer, struct _w5500_socket, w5500_tcplist, static);
-LO_IMPLEMENTATION_C(net_stream_listener, struct _w5500_socket, w5500_tcplist, static);
+LO_IMPLEMENTATION_STATIC(io_closer, struct _w5500_socket, w5500_tcplist);
+LO_IMPLEMENTATION_STATIC(net_stream_listener, struct _w5500_socket, w5500_tcplist);
-LO_IMPLEMENTATION_C(io_reader, struct _w5500_socket, w5500_tcp, static);
-LO_IMPLEMENTATION_C(io_writer, struct _w5500_socket, w5500_tcp, static);
-LO_IMPLEMENTATION_C(io_readwriter, struct _w5500_socket, w5500_tcp, static);
-LO_IMPLEMENTATION_C(io_closer, struct _w5500_socket, w5500_tcp, static);
-LO_IMPLEMENTATION_C(io_bidi_closer, struct _w5500_socket, w5500_tcp, static);
-LO_IMPLEMENTATION_C(net_stream_conn, struct _w5500_socket, w5500_tcp, static);
+LO_IMPLEMENTATION_STATIC(io_reader, struct _w5500_socket, w5500_tcp);
+LO_IMPLEMENTATION_STATIC(io_writer, struct _w5500_socket, w5500_tcp);
+LO_IMPLEMENTATION_STATIC(io_closer, struct _w5500_socket, w5500_tcp);
+LO_IMPLEMENTATION_STATIC(io_bidi_closer, struct _w5500_socket, w5500_tcp);
+LO_IMPLEMENTATION_STATIC(net_stream_conn, struct _w5500_socket, w5500_tcp);
-LO_IMPLEMENTATION_C(io_closer, struct _w5500_socket, w5500_udp, static);
-LO_IMPLEMENTATION_C(net_packet_conn, struct _w5500_socket, w5500_udp, static);
+LO_IMPLEMENTATION_STATIC(io_closer, struct _w5500_socket, w5500_udp);
+LO_IMPLEMENTATION_STATIC(net_packet_conn, struct _w5500_socket, w5500_udp);
-LO_IMPLEMENTATION_C(net_iface, struct w5500, w5500_if, static);
+LO_IMPLEMENTATION_C(net_iface, struct w5500, w5500_if);
/* mid-level utilities ********************************************************/
@@ -187,7 +187,7 @@ static COROUTINE w5500_irq_cr(void *_chip) {
bool had_intr = false;
uint8_t chipintr = w5500ll_read_common_reg(chip->spidev, chip_interrupt);
- n_debugf(W5500_LL, "w5500_irq_cr(): chipintr=%"PRIu8, chipintr);
+ log_n_debugln(W5500_LL, "w5500_irq_cr(): chipintr=", chipintr);
had_intr = had_intr || (chipintr != 0);
if (chipintr)
w5500ll_write_common_reg(chip->spidev, chip_interrupt, 0xFF);
@@ -196,7 +196,7 @@ static COROUTINE w5500_irq_cr(void *_chip) {
struct _w5500_socket *socket = &chip->sockets[socknum];
uint8_t sockintr = w5500ll_read_sock_reg(chip->spidev, socknum, interrupt);
- n_debugf(W5500_LL, "w5500_irq_cr(): sockintr[%"PRIu8"]=%"PRIu8, socknum, sockintr);
+ log_n_debugln(W5500_LL, "w5500_irq_cr(): sockintr[", socknum, "]=", sockintr);
had_intr = had_intr || (sockintr != 0);
switch (socket->mode) {
@@ -208,15 +208,15 @@ static COROUTINE w5500_irq_cr(void *_chip) {
recv_bits = sockintr & (SOCKINTR_RECV_DAT|SOCKINTR_RECV_FIN);
if (listen_bits) {
- debugf("w5500_irq_cr(): signal sock[%"PRIu8"]->listen_sema", socknum);
+ log_debugln("w5500_irq_cr(): signal sock[", socknum, "]->listen_sema");
cr_sema_signal(&socket->listen_sema);
}
if (recv_bits) {
- debugf("w5500_irq_cr(): signal sock[%"PRIu8"]->read_sema", socknum);
+ log_debugln("w5500_irq_cr(): signal sock[", socknum, "]->read_sema");
cr_sema_signal(&socket->read_sema);
}
if (send_bits) {
- debugf("w5500_irq_cr(): signal sock[%"PRIu8"]->write_ch", socknum);
+ log_debugln("w5500_irq_cr(): signal sock[", socknum, "]->write_ch");
cr_chan_send(&socket->write_ch, send_bits);
}
break;
@@ -228,7 +228,7 @@ static COROUTINE w5500_irq_cr(void *_chip) {
cr_mutex_unlock(&chip->mu);
if (!had_intr && gpio_get(chip->pin_intr)) {
- debugf("w5500_irq_cr(): looks like all interrupts have been processed, sleeping...");
+ log_debugln("w5500_irq_cr(): looks like all interrupts have been processed, sleeping...");
cr_sema_wait(&chip->intr);
} else
cr_yield();
@@ -283,7 +283,7 @@ static inline void w5500_socket_close(struct _w5500_socket *socket) {
static void w5500_intrhandler(void *_chip, uint LM_UNUSED(gpio), enum gpio_irq_level LM_UNUSED(event)) {
struct w5500 *chip = _chip;
- debugf("w5500_intrhandler()");
+ log_debugln("w5500_intrhandler()");
cr_sema_signal_from_intrhandler(&chip->intr);
}
@@ -322,7 +322,7 @@ void _w5500_init(struct w5500 *chip,
w5500ll_write_sock_reg(chip->spidev, 0, mode, a);
uint8_t b = w5500ll_read_sock_reg(chip->spidev, 0, mode);
if (b != a) {
- errorf("SPI to W5500 does not appear to be functional: wrote:0x%02"PRIx16" != read:0x%02"PRIx8, a, b);
+ log_errorln("SPI to W5500 does not appear to be functional: wrote:", (base16_u8_, a), " != read:", (base16_u8_, b));
spi_ok = false;
}
}
@@ -337,7 +337,8 @@ void _w5500_init(struct w5500 *chip,
w5500_hard_reset(chip);
/* Finally, wire in the interrupt handler. */
- coroutine_add("w5500_irq", w5500_irq_cr, chip);
+ cid_t cid = coroutine_add("w5500_irq", w5500_irq_cr, chip);
+ assert(cid);
}
/* chip methods ***************************************************************/
@@ -408,7 +409,7 @@ void w5500_soft_reset(struct w5500 *chip) {
cr_mutex_unlock(&chip->mu);
}
-static struct net_eth_addr w5500_if_hwaddr(struct w5500 *chip) {
+struct net_eth_addr w5500_if_hwaddr(struct w5500 *chip) {
assert(chip);
return chip->hwaddr;
@@ -426,28 +427,28 @@ static void _w5500_if_up(struct w5500 *chip, struct net_iface_config cfg) {
cr_mutex_unlock(&chip->mu);
}
-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));
+void w5500_if_ifup(struct w5500 *chip, struct net_iface_config cfg) {
+ log_debugln("if_up()");
+ log_debugln(":: addr = ", (net_ip4_addr, cfg.addr));
+ log_debugln(":: gateway_addr = ", (net_ip4_addr, cfg.gateway_addr));
+ log_debugln(":: subnet_mask = ", (net_ip4_addr, cfg.subnet_mask));
_w5500_if_up(chip, cfg);
}
-static void w5500_if_ifdown(struct w5500 *chip) {
- debugf("if_down()");
- _w5500_if_up(chip, (struct net_iface_config){0});
+void w5500_if_ifdown(struct w5500 *chip) {
+ log_debugln("if_down()");
+ _w5500_if_up(chip, (struct net_iface_config){});
}
-static lo_interface net_stream_listener w5500_if_tcp_listen(struct w5500 *chip, uint16_t local_port) {
+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");
+ log_debugln("tcp_listen() => no sock");
return LO_NULL(net_stream_listener);
}
- debugf("tcp_listen() => sock[%"PRIu8"]", sock->socknum);
+ log_debugln("tcp_listen() => sock[", sock->socknum, "]");
if (!local_port)
local_port = w5500_alloc_local_port(chip);
@@ -458,11 +459,11 @@ static lo_interface net_stream_listener w5500_if_tcp_listen(struct w5500 *chip,
sock->read_deadline_ns = 0;
sock->list_open = true;
- return lo_box_w5500_tcplist_as_net_stream_listener(sock);
+ return LO_BOX(net_stream_listener, sock);
}
-static lo_interface net_stream_conn w5500_if_tcp_dial(struct w5500 *chip,
- struct net_ip4_addr node, uint16_t port) {
+net_stream_conn_or_error 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));
@@ -470,11 +471,11 @@ static lo_interface net_stream_conn w5500_if_tcp_dial(struct w5500 *chip,
struct _w5500_socket *socket = w5500_alloc_socket(chip);
if (!socket) {
- debugf("tcp_dial() => no sock");
- return LO_NULL(net_stream_conn);
+ log_debugln("tcp_dial() => no sock");
+ return ERROR_NEW_ERR(net_stream_conn, error_new(E_POSIX_ENOTSOCK));
}
uint8_t socknum = socket->socknum;
- debugf("tcp_dial() => sock[%"PRIu8"]", socknum);
+ log_debugln("tcp_dial() => sock[", socknum, "]");
uint16_t local_port = w5500_alloc_local_port(chip);
@@ -502,29 +503,29 @@ static lo_interface net_stream_conn w5500_if_tcp_dial(struct w5500 *chip,
cr_mutex_unlock(&chip->mu);
for (;;) {
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
- debugf("tcp_dial(): state=%s", w5500_state_str(state));
+ log_debugln("tcp_dial(): state=", w5500_state_str(state));
switch (state) {
case STATE_TCP_SYNSENT:
cr_yield();
break;
case STATE_TCP_ESTABLISHED:
- return lo_box_w5500_tcp_as_net_stream_conn(socket);
+ return ERROR_NEW_VAL(net_stream_conn, LO_BOX(net_stream_conn, socket));
default:
goto restart;
}
}
}
-static lo_interface net_packet_conn w5500_if_udp_conn(struct w5500 *chip, uint16_t local_port) {
+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");
+ log_debugln("udp_conn() => no sock");
return LO_NULL(net_packet_conn);
}
uint8_t socknum = socket->socknum;
- debugf("udp_conn() => sock[%"PRIu8"]", socknum);
+ log_debugln("udp_conn() => sock[", socknum, "]");
if (!local_port)
local_port = w5500_alloc_local_port(chip);
@@ -544,18 +545,34 @@ static lo_interface net_packet_conn w5500_if_udp_conn(struct w5500 *chip, uint16
cr_yield();
cr_mutex_unlock(&chip->mu);
- return lo_box_w5500_udp_as_net_packet_conn(socket);
+ return LO_BOX(net_packet_conn, socket);
+}
+
+bool w5500_if_arp_ping(struct w5500 *chip, struct net_ip4_addr addr) {
+ /* FIXME: This arp_ping implementation is really bad (and
+ * assumes that a UDP socket is open, which is "safe" because
+ * I only use it from inside of a DHCP client). */
+ assert(chip);
+ struct _w5500_socket *sock = NULL;
+ for (size_t i = 0; i < LM_ARRAY_LEN(chip->sockets) && !sock; i++) {
+ if (chip->sockets[i].mode == W5500_MODE_UDP)
+ sock = &chip->sockets[i];
+ }
+ assert(sock);
+ error err = w5500_udp_sendto(sock, "BOGUS_PACKET_TO_TRIGGER_AN_ARP", 17, addr, 5000);
+ log_debugln("arp_ping => ", (error, err));
+ return err.num != E_NET_EARP_TIMEOUT;
}
/* tcp_listener methods *******************************************************/
-static lo_interface net_stream_conn w5500_tcplist_accept(struct _w5500_socket *socket) {
+static net_stream_conn_or_error w5500_tcplist_accept(struct _w5500_socket *socket) {
ASSERT_SELF(stream_listener, TCP);
restart:
if (!socket->list_open) {
- debugf("tcp_listener.accept() => already closed");
- return LO_NULL(net_stream_conn);
+ log_debugln("tcp_listener.accept() => already closed");
+ return ERROR_NEW_ERR(net_stream_conn, error_new(E_NET_ECLOSED));
}
cr_mutex_lock(&chip->mu);
@@ -573,7 +590,7 @@ static lo_interface net_stream_conn w5500_tcplist_accept(struct _w5500_socket *s
cr_mutex_unlock(&chip->mu);
for (;;) {
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
- debugf("tcp_listener.accept() => state=%s", w5500_state_str(state));
+ log_debugln("tcp_listener.accept() => state=", w5500_state_str(state));
switch (state) {
case STATE_TCP_LISTEN:
case STATE_TCP_SYNRECV:
@@ -581,37 +598,36 @@ static lo_interface net_stream_conn w5500_tcplist_accept(struct _w5500_socket *s
break;
case STATE_TCP_ESTABLISHED:
socket->read_open = true;
- /* fall-through */
+ [[fallthrough]];
case STATE_TCP_CLOSE_WAIT:
socket->write_open = true;
- return lo_box_w5500_tcp_as_net_stream_conn(socket);
+ return ERROR_NEW_VAL(net_stream_conn, LO_BOX(net_stream_conn, socket));
default:
goto restart;
}
}
}
-static int w5500_tcplist_close(struct _w5500_socket *socket) {
- debugf("tcp_listener.close()");
+static error w5500_tcplist_close(struct _w5500_socket *socket) {
+ log_debugln("tcp_listener.close()");
ASSERT_SELF(stream_listener, TCP);
socket->list_open = false;
w5500_tcp_maybe_free(chip, socket);
- return 0;
+ return ERROR_NULL;
}
/* tcp_conn methods ***********************************************************/
-static ssize_t w5500_tcp_writev(struct _w5500_socket *socket, const struct iovec *iov, int iovcnt) {
+static size_t_and_error w5500_tcp_writev(struct _w5500_socket *socket, const struct wr_iovec *iov, int iovcnt) {
assert(iov);
assert(iovcnt > 0);
size_t count = 0;
for (int i = 0; i < iovcnt; i++)
count += iov[i].iov_len;
- debugf("tcp_conn.write(%zu)", count);
+ assert(count);
+ log_debugln("tcp_conn.write(", count, ")");
ASSERT_SELF(stream_conn, TCP);
- if (count == 0)
- return 0;
/* What we really want is to pause until we receive an ACK for
* some data we just queued, so that we can line up some new
@@ -636,15 +652,15 @@ static ssize_t w5500_tcp_writev(struct _w5500_socket *socket, const struct iovec
size_t done = 0;
while (done < count) {
if (!socket->write_open) {
- debugf(" => soft closed");
- return -NET_ECLOSED;
+ log_debugln(" => soft closed");
+ return ERROR_AND(size_t, done, error_new(E_NET_ECLOSED));
}
cr_mutex_lock(&chip->mu);
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
if (state != STATE_TCP_ESTABLISHED && state != STATE_TCP_CLOSE_WAIT) {
cr_mutex_unlock(&chip->mu);
- debugf(" => hard closed");
- return -NET_ECLOSED;
+ log_debugln(" => hard closed");
+ return ERROR_AND(size_t, done, error_new(E_NET_ECLOSED));
}
uint16_t freesize = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, tx_free_size));
@@ -668,24 +684,24 @@ static ssize_t w5500_tcp_writev(struct _w5500_socket *socket, const struct iovec
cr_mutex_unlock(&chip->mu);
switch (cr_chan_recv(&socket->write_ch)) {
case SOCKINTR_SEND_OK:
- debugf(" => sent %zu", freesize);
+ log_debugln(" => sent ", freesize);
done += freesize;
break;
case SOCKINTR_SEND_TIMEOUT:
- debugf(" => ACK timeout");
- return -NET_EACK_TIMEOUT;
+ log_debugln(" => ACK timeout");
+ return ERROR_AND(size_t, done, error_new(E_NET_EACK_TIMEOUT));
case SOCKINTR_SEND_OK|SOCKINTR_SEND_TIMEOUT:
assert_notreached("send both OK and timed out?");
default:
assert_notreached("invalid write_ch bits");
}
}
- debugf(" => send finished");
- return done;
+ log_debugln(" => send finished");
+ return ERROR_AND(size_t, done, ERROR_NULL);
}
static void w5500_tcp_set_read_deadline(struct _w5500_socket *socket, uint64_t ns) {
- debugf("tcp_conn.set_read_deadline(%"PRIu64")", ns);
+ log_debugln("tcp_conn.set_read_deadline(", ns, ")");
ASSERT_SELF(stream_conn, TCP);
socket->read_deadline_ns = ns;
}
@@ -695,16 +711,15 @@ static void w5500_tcp_alarm_handler(void *_arg) {
cr_sema_signal_from_intrhandler(&socket->read_sema);
}
-static ssize_t w5500_tcp_readv(struct _w5500_socket *socket, const struct iovec *iov, int iovcnt) {
+static size_t_or_error w5500_tcp_readv(struct _w5500_socket *socket, const struct rd_iovec *iov, int iovcnt) {
assert(iov);
assert(iovcnt > 0);
size_t count = 0;
for (int i = 0; i < iovcnt; i++)
count += iov[i].iov_len;
- debugf("tcp_conn.read(%zu)", count);
+ assert(count);
+ log_debugln("tcp_conn.read(", count, ")");
ASSERT_SELF(stream_conn, TCP);
- if (count == 0)
- return 0;
struct alarmclock_trigger trigger = {};
if (socket->read_deadline_ns)
@@ -718,13 +733,13 @@ static ssize_t w5500_tcp_readv(struct _w5500_socket *socket, const struct iovec
for (;;) {
if (!socket->read_open) {
LO_CALL(bootclock, del_trigger, &trigger);
- debugf(" => soft closed");
- return -NET_ECLOSED;
+ log_debugln(" => soft closed");
+ return ERROR_NEW_ERR(size_t, error_new(E_NET_ECLOSED));
}
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;
+ log_debugln(" => recv timeout");
+ return ERROR_NEW_ERR(size_t, error_new(E_NET_ERECV_TIMEOUT));
}
cr_mutex_lock(&chip->mu);
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
@@ -736,8 +751,8 @@ static ssize_t w5500_tcp_readv(struct _w5500_socket *socket, const struct iovec
default:
LO_CALL(bootclock, del_trigger, &trigger);
cr_mutex_unlock(&chip->mu);
- debugf(" => hard closed");
- return -NET_ECLOSED;
+ log_debugln(" => hard closed");
+ return ERROR_NEW_ERR(size_t, error_new(E_NET_ECLOSED));
}
avail = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, rx_size));
@@ -747,15 +762,15 @@ static ssize_t w5500_tcp_readv(struct _w5500_socket *socket, const struct iovec
if (state == STATE_TCP_CLOSE_WAIT) {
LO_CALL(bootclock, del_trigger, &trigger);
cr_mutex_unlock(&chip->mu);
- debugf(" => EOF");
- return 0;
+ log_debugln(" => EOF");
+ return ERROR_NEW_ERR(size_t, error_new(E_EOF));
}
cr_mutex_unlock(&chip->mu);
cr_sema_wait(&socket->read_sema);
}
assert(avail);
- debugf(" => received %"PRIu16" bytes", avail);
+ log_debugln(" => received ", avail, " bytes");
uint16_t ptr = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, rx_read_pointer));
/* Read the data. */
if ((size_t)avail > count)
@@ -767,11 +782,11 @@ static ssize_t w5500_tcp_readv(struct _w5500_socket *socket, const struct iovec
/* Return. */
LO_CALL(bootclock, del_trigger, &trigger);
cr_mutex_unlock(&chip->mu);
- return avail;
+ return ERROR_NEW_VAL(size_t, avail);
}
-static int w5500_tcp_close_inner(struct _w5500_socket *socket, bool rd, bool wr) {
- debugf("tcp_conn.close(rd=%s, wr=%s)", rd ? "true" : "false", wr ? "true" : "false");
+static error w5500_tcp_close_inner(struct _w5500_socket *socket, bool rd, bool wr) {
+ log_debugln("tcp_conn.close(rd=", rd, ", wr=", wr, ")");
ASSERT_SELF(stream_conn, TCP);
if (rd)
@@ -798,26 +813,26 @@ static int w5500_tcp_close_inner(struct _w5500_socket *socket, bool rd, bool wr)
}
w5500_tcp_maybe_free(chip, socket);
- return 0;
+ return ERROR_NULL;
}
-static int w5500_tcp_close(struct _w5500_socket *socket) { return w5500_tcp_close_inner(socket, true, true); }
-static int w5500_tcp_close_read(struct _w5500_socket *socket) { return w5500_tcp_close_inner(socket, true, false); }
-static int w5500_tcp_close_write(struct _w5500_socket *socket) { return w5500_tcp_close_inner(socket, false, true); }
+static error w5500_tcp_close(struct _w5500_socket *socket) { return w5500_tcp_close_inner(socket, true, true); }
+static error w5500_tcp_close_read(struct _w5500_socket *socket) { return w5500_tcp_close_inner(socket, true, false); }
+static error w5500_tcp_close_write(struct _w5500_socket *socket) { return w5500_tcp_close_inner(socket, false, true); }
/* udp_conn methods ***********************************************************/
-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()");
+static error w5500_udp_sendto(struct _w5500_socket *socket, void *buf, size_t count,
+ struct net_ip4_addr node, uint16_t port) {
+ log_debugln("udp_conn.sendto()");
ASSERT_SELF(packet_conn, UDP);
assert(buf);
assert(count);
uint16_t bufsize = ((uint16_t)w5500ll_read_sock_reg(chip->spidev, socknum, tx_buf_size))*1024;
if (count > bufsize) {
- debugf(" => msg too large");
- return -NET_EMSGSIZE;
+ log_debugln(" => msg too large");
+ return error_new(E_POSIX_EMSGSIZE);
}
for (;;) {
@@ -825,8 +840,8 @@ static ssize_t w5500_udp_sendto(struct _w5500_socket *socket, void *buf, size_t
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
if (state != STATE_UDP) {
cr_mutex_unlock(&chip->mu);
- debugf(" => closed");
- return -NET_ECLOSED;
+ log_debugln(" => closed");
+ return error_new(E_NET_ECLOSED);
}
uint16_t freesize = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, tx_free_size));
@@ -844,8 +859,8 @@ static ssize_t w5500_udp_sendto(struct _w5500_socket *socket, void *buf, size_t
w5500ll_write_sock_reg(chip->spidev, socknum, remote_port, uint16be_marshal(port));
/* Queue data to be sent. */
uint16_t ptr = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, tx_write_pointer));
- w5500ll_writev(chip->spidev, ptr, CTL_BLOCK_SOCK(socknum, TX), &((struct iovec){
- .iov_base = buf,
+ w5500ll_writev(chip->spidev, ptr, CTL_BLOCK_SOCK(socknum, TX), &((struct wr_iovec){
+ .iov_write_from = buf,
.iov_len = count,
}), 1, 0, 0);
w5500ll_write_sock_reg(chip->spidev, socknum, tx_write_pointer, uint16be_marshal(ptr+count));
@@ -855,11 +870,11 @@ static ssize_t w5500_udp_sendto(struct _w5500_socket *socket, void *buf, size_t
cr_mutex_unlock(&chip->mu);
switch (cr_chan_recv(&socket->write_ch)) {
case SOCKINTR_SEND_OK:
- debugf(" => sent");
- return count;
+ log_debugln(" => sent");
+ return ERROR_NULL;
case SOCKINTR_SEND_TIMEOUT:
- debugf(" => ARP timeout");
- return -NET_EARP_TIMEOUT;
+ log_debugln(" => ARP timeout");
+ return error_new(E_NET_EARP_TIMEOUT);
case SOCKINTR_SEND_OK|SOCKINTR_SEND_TIMEOUT:
assert_notreached("send both OK and timed out?");
default:
@@ -868,7 +883,7 @@ static ssize_t w5500_udp_sendto(struct _w5500_socket *socket, void *buf, size_t
}
static void w5500_udp_set_recv_deadline(struct _w5500_socket *socket, uint64_t ns) {
- debugf("udp_conn.set_recv_deadline(%"PRIu64")", ns);
+ log_debugln("udp_conn.set_recv_deadline(", ns, ")");
ASSERT_SELF(packet_conn, UDP);
socket->read_deadline_ns = ns;
}
@@ -878,9 +893,9 @@ static void w5500_udp_alarm_handler(void *_arg) {
cr_sema_signal_from_intrhandler(&socket->read_sema);
}
-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()");
+static size_t_or_error w5500_udp_recvfrom(struct _w5500_socket *socket, void *buf, size_t count,
+ struct net_ip4_addr *ret_node, uint16_t *ret_port) {
+ log_debugln("udp_conn.recvfrom()");
ASSERT_SELF(packet_conn, UDP);
assert(buf);
assert(count);
@@ -897,15 +912,15 @@ static ssize_t w5500_udp_recvfrom(struct _w5500_socket *socket, void *buf, size_
for (;;) {
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;
+ log_debugln(" => recv timeout");
+ return ERROR_NEW_ERR(size_t, error_new(E_NET_ERECV_TIMEOUT));
}
cr_mutex_lock(&chip->mu);
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
if (state != STATE_UDP) {
LO_CALL(bootclock, del_trigger, &trigger);
- debugf(" => hard closed");
- return -NET_ECLOSED;
+ log_debugln(" => hard closed");
+ return ERROR_NEW_ERR(size_t, error_new(E_NET_ECLOSED));
}
avail = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, rx_size));
@@ -922,8 +937,8 @@ static ssize_t w5500_udp_recvfrom(struct _w5500_socket *socket, void *buf, size_
* can't find in the datasheet where it describes
* this; this is based off of socket.c:recvfrom(). */
uint8_t hdr[8];
- w5500ll_readv(chip->spidev, ptr, CTL_BLOCK_SOCK(socknum, RX), &((struct iovec){
- .iov_base = hdr,
+ w5500ll_readv(chip->spidev, ptr, CTL_BLOCK_SOCK(socknum, RX), &((struct rd_iovec){
+ .iov_read_to = hdr,
.iov_len = sizeof(hdr),
}), 1, 0);
if (ret_node) {
@@ -935,12 +950,12 @@ static ssize_t w5500_udp_recvfrom(struct _w5500_socket *socket, void *buf, size_
if (ret_port)
*ret_port = uint16be_decode(&hdr[4]);
uint16_t len = uint16be_decode(&hdr[6]);
- debugf(" => received %"PRIu16" bytes%s", len, len < avail-8 ? " (plus more messages)" : "");
+ log_debugln(" => received ", len, " bytes", len < avail-8 ? " (plus more messages)" : "");
/* Now read the actual data. */
if (count > len)
count = len;
- w5500ll_readv(chip->spidev, ptr+8, CTL_BLOCK_SOCK(socknum, RX), &((struct iovec){
- .iov_base = buf,
+ w5500ll_readv(chip->spidev, ptr+8, CTL_BLOCK_SOCK(socknum, RX), &((struct rd_iovec){
+ .iov_read_to = buf,
.iov_len = len,
}), 1, 0);
/* Tell the chip that we read the data. */
@@ -949,14 +964,14 @@ static ssize_t w5500_udp_recvfrom(struct _w5500_socket *socket, void *buf, size_
/* Return. */
LO_CALL(bootclock, del_trigger, &trigger);
cr_mutex_unlock(&chip->mu);
- return len;
+ return ERROR_NEW_VAL(size_t, len);
}
-static int w5500_udp_close(struct _w5500_socket *socket) {
- debugf("udp_conn.close()");
+static error w5500_udp_close(struct _w5500_socket *socket) {
+ log_debugln("udp_conn.close()");
ASSERT_SELF(packet_conn, UDP);
w5500_socket_close(socket);
w5500_free_socket(chip, socket);
- return 0;
+ return ERROR_NULL;
}
diff --git a/libhw_cr/w5500_ll.c b/libhw_cr/w5500_ll.c
new file mode 100644
index 0000000..ec40e5e
--- /dev/null
+++ b/libhw_cr/w5500_ll.c
@@ -0,0 +1,116 @@
+/* libhw_cr/w5500_ll.c - Low-level library for the WIZnet W5500 chip
+ *
+ * Based entirely on the W5500 datasheet (v1.1.0), not on reference code.
+ * https://docs.wiznet.io/img/products/w5500/W5500_ds_v110e.pdf
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <string.h> /* for memcmp() and mempy() */
+
+#include <libmisc/alloc.h>
+#include <libmisc/fmt.h>
+#include <libmisc/log.h>
+
+#include "w5500_ll.h"
+
+#if CONFIG_W5500_LL_DEBUG
+static void fmt_print_ctl_block(lo_interface fmt_dest w, uint8_t b) {
+ static char *strs[] = {
+ "RES",
+ "REG",
+ "TX",
+ "RX",
+ };
+ fmt_print(w, "CTL_BLOCK_SOCK(", (base10, (((b)>>5) & 0b111)), ", ", strs[((b)>>3)&0b11], ")");
+}
+#endif
+
+void _w5500ll_n_writev(const char *func,
+ lo_interface spi spidev, uint16_t addr, uint8_t block,
+ const struct wr_iovec *iov, int iovcnt,
+ size_t skip, size_t max)
+{
+ assert(!LO_IS_NULL(spidev));
+ assert((block & ~CTL_MASK_BLOCK) == 0);
+ assert(iov);
+ assert(iovcnt > 0);
+#if CONFIG_W5500_LL_DEBUG
+ log_n_debugln(W5500_LL,
+ func, "(): w5500ll_write(spidev",
+ ", addr=", (base16_u16_, addr),
+ ", block=", (ctl_block, block),
+ ", iov",
+ ", iovcnt=", iovcnt,
+ ")");
+#endif
+
+ uint8_t header[] = {
+ (uint8_t)((addr >> 8) & 0xFF),
+ (uint8_t)(addr & 0xFF),
+ (block & CTL_MASK_BLOCK) | CTL_W | CTL_OM_VDM,
+ };
+ int inner_cnt = 1+io_wr_slice_cnt(iov, iovcnt, skip, max);
+ [[gnu::cleanup(heap_cleanup)]] struct duplex_iovec *inner = heap_alloc(inner_cnt, struct duplex_iovec);
+ inner[0] = (struct duplex_iovec){
+ .iov_read_to = IOVEC_DISCARD,
+ .iov_write_from = header,
+ .iov_len = sizeof(header),
+ };
+ io_slice_wr_to_duplex(&inner[1], iov, iovcnt, skip, max);
+ LO_CALL(spidev, readwritev, inner, inner_cnt);
+}
+
+
+void _w5500ll_n_readv(const char *func,
+ lo_interface spi spidev, uint16_t addr, uint8_t block,
+ const struct rd_iovec *iov, int iovcnt,
+ size_t max)
+{
+ assert(!LO_IS_NULL(spidev));
+ assert((block & ~CTL_MASK_BLOCK) == 0);
+ assert(iov);
+ assert(iovcnt > 0);
+#if CONFIG_W5500_LL_DEBUG
+ log_n_debugln(W5500_LL,
+ func, "(): w5500ll_read(spidev",
+ ", addr=", (base16_u16_, addr),
+ ", block=", (ctl_block, block),
+ ", iov",
+ ", iovcnt=", iovcnt,
+ ")");
+#endif
+
+ uint8_t header[] = {
+ (uint8_t)((addr >> 8) & 0xFF),
+ (uint8_t)(addr & 0xFF),
+ (block & CTL_MASK_BLOCK) | CTL_R | CTL_OM_VDM,
+ };
+ int inner_cnt = 1+io_rd_slice_cnt(iov, iovcnt, 0, max);
+ [[gnu::cleanup(heap_cleanup)]] struct duplex_iovec *inner = heap_alloc(inner_cnt, struct duplex_iovec);
+ inner[0] = (struct duplex_iovec){
+ .iov_read_to = IOVEC_DISCARD,
+ .iov_write_from = header,
+ .iov_len = sizeof(header),
+ };
+ io_slice_rd_to_duplex(&inner[1], iov, iovcnt, 0, max);
+ LO_CALL(spidev, readwritev, inner, inner_cnt);
+}
+
+void _w5500ll_n_read_repeated(const char *func,
+ lo_interface spi spidev, uint16_t addr, uint8_t block,
+ void *buf, void *buf_scratch, size_t len)
+{
+ _w5500ll_n_readv(func, spidev, addr, block,
+ &((struct rd_iovec){.iov_read_to=buf, .iov_len=len}), 1,
+ 0);
+ for (;;) {
+ _w5500ll_n_readv(func, spidev, addr, block,
+ &((struct rd_iovec){.iov_read_to=buf_scratch, .iov_len=len}), 1,
+ 0);
+ if (memcmp(buf, buf_scratch, len) == 0)
+ break;
+ memcpy(buf, buf_scratch, len);
+ }
+}
diff --git a/libhw_cr/w5500_ll.h b/libhw_cr/w5500_ll.h
index 2506cd2..e375ebd 100644
--- a/libhw_cr/w5500_ll.h
+++ b/libhw_cr/w5500_ll.h
@@ -1,6 +1,6 @@
-/* libhw_cr/w5500_ll.h - Low-level header library for the WIZnet W5500 chip
+/* libhw_cr/w5500_ll.h - Low-level library for the WIZnet W5500 chip
*
- * Based entirely on the W5500 datasheet, v1.1.0.
+ * Based entirely on the W5500 datasheet (v1.1.0), not on reference code.
* https://docs.wiznet.io/img/products/w5500/W5500_ds_v110e.pdf
*
* Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
@@ -10,17 +10,15 @@
#ifndef _LIBHW_CR_W5500_LL_H_
#define _LIBHW_CR_W5500_LL_H_
-#include <alloca.h> /* for alloca() */
#include <stdint.h> /* for uint{n}_t */
-#include <string.h> /* for memcmp() */
-#include <libmisc/assert.h> /* for assert(), static_assert() */
+#include <libmisc/assert.h> /* for static_assert() */
#include <libmisc/endian.h> /* for uint16be_t */
#include <libhw/generic/net.h> /* for struct net_eth_addr, struct net_ip4_addr */
#include <libhw/generic/spi.h> /* for lo_interface spi */
-/* Config *********************************************************************/
+/* Config. ***************************************************************************************/
#include "config.h"
@@ -28,7 +26,7 @@
#error config.h must define CONFIG_W5500_LL_DEBUG
#endif
-/* Low-level protocol built on SPI frames. ***********************************/
+/* Low-level protocol built on SPI frames. *******************************************************/
/* A u8 control byte has 3 parts: block-ID, R/W, and operating-mode. */
@@ -53,93 +51,81 @@
#define CTL_OM_FDM2 0b10 /* fixed-length data mode: 2 byte data length */
#define CTL_OM_FDM4 0b11 /* fixed-length data mode: 4 byte data length */
-#if CONFIG_W5500_LL_DEBUG
-static char *_ctl_block_part_strs[] = {
- "RES",
- "REG",
- "TX",
- "RX",
-};
-#define PRI_ctl_block "CTL_BLOCK_SOCK(%d, %s)"
-#define ARG_ctl_block(b) (((b)>>5) & 0b111), _ctl_block_part_strs[((b)>>3)&0b11]
-#endif
-
-/* Even though SPI is a full-duplex protocol, the W5500's spiframe on top of it is only half-duplex.
- * Lame. */
+/* Even though SPI is a full-duplex protocol, the W5500's spiframe on
+ * top of it is only half-duplex. Lame. */
-static inline void
#if CONFIG_W5500_LL_DEBUG
-#define w5500ll_writev(...) _w5500ll_writev(__func__, __VA_ARGS__)
-_w5500ll_writev(const char *func,
+ #define w5500ll_writev(...) _w5500ll_n_writev (__func__, __VA_ARGS__)
+ #define w5500ll_readv(...) _w5500ll_n_readv (__func__, __VA_ARGS__)
#else
-w5500ll_writev(
-#endif
- lo_interface spi spidev, uint16_t addr, uint8_t block,
- const struct iovec *iov, int iovcnt,
- size_t skip, size_t max)
-{
- assert(!LO_IS_NULL(spidev));
- assert((block & ~CTL_MASK_BLOCK) == 0);
- assert(iov);
- assert(iovcnt > 0);
-#if CONFIG_W5500_LL_DEBUG
- n_debugf(W5500_LL,
- "%s(): w5500ll_write(spidev, addr=%#04x, block="PRI_ctl_block", iov, iovcnt=%d)",
- func, addr, ARG_ctl_block(block), iovcnt);
+ #define _w5500ll_n_writev(func, ...) w5500ll_writev (__VA_ARGS__)
+ #define _w5500ll_n_readv(func, ...) w5500ll_readv (__VA_ARGS__)
#endif
- uint8_t header[] = {
- (uint8_t)((addr >> 8) & 0xFF),
- (uint8_t)(addr & 0xFF),
- (block & CTL_MASK_BLOCK) | CTL_W | CTL_OM_VDM,
- };
- 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_to = IOVEC_DISCARD,
- .iov_write_from = header,
- .iov_len = sizeof(header),
- };
- io_slice_wr_to_duplex(&inner[1], iov, iovcnt, skip, max);
- LO_CALL(spidev, readwritev, inner, inner_cnt);
-}
-
-static inline void
+/* `skip` and `max` correspond to the <libhw/generic/io.h> `slice`
+ * functions' `byte_start` and `max_byte_cnt` arguments for working on
+ * just a subset of the iovec list. */
+
+void _w5500ll_n_writev(const char *func,
+ lo_interface spi spidev, uint16_t addr, uint8_t block,
+ const struct wr_iovec *iov, int iovcnt,
+ size_t skip, size_t max);
+void _w5500ll_n_readv(const char *func,
+ lo_interface spi spidev, uint16_t addr, uint8_t block,
+ const struct rd_iovec *iov, int iovcnt,
+ size_t max);
+
+/* Operate on registers. *************************************************************************/
+
+/*
+ * Given a `blocktyp` that is a struct describing the layout of
+ * registers in a block (e.g. `blocktyp = struct
+ * w5500_ll_block_common_reg`), read or write the `field` register
+ * (where `field` must be a field in that struct).
+ */
+
+#define w5500ll_write_reg(spidev, blockid, blocktyp, field, val) do { \
+ typeof((blocktyp){}.field) lval = val; \
+ w5500ll_writev(spidev, \
+ offsetof(blocktyp, field), blockid, \
+ &((struct wr_iovec){ \
+ .iov_write_from = &lval, \
+ .iov_len = sizeof(lval), \
+ }), 1, \
+ 0, 0); \
+ } while (0)
+
+/* The datasheet tells us that multi-byte reads are non-atomic and
+ * that "it is recommended that you read all 16-bits twice or more
+ * until getting the same value". */
#if CONFIG_W5500_LL_DEBUG
-#define w5500ll_readv(...) _w5500ll_read(__func__, __VA_ARGS__)
-_w5500ll_readv(const char *func,
+ #define _w5500ll_read_repeated(...) _w5500ll_n_read_repeated (__func__, __VA_ARGS__)
#else
-w5500ll_readv(
-#endif
- lo_interface spi spidev, uint16_t addr, uint8_t block,
- const struct iovec *iov, int iovcnt,
- size_t max)
-{
- assert(!LO_IS_NULL(spidev));
- assert((block & ~CTL_MASK_BLOCK) == 0);
- assert(iov);
- assert(iovcnt > 0);
-#if CONFIG_W5500_LL_DEBUG
- n_debugf(W5500_LL,
- "%s(): w5500ll_read(spidev, addr=%#04x, block="PRI_ctl_block", iov, iovcnt=%d)",
- func, addr, ARG_ctl_block(block), iovcnt);
+ #define _w5500ll_n_read_repeated(func, ...) _w5500ll_read_repeated (__VA_ARGS__)
#endif
+void _w5500ll_n_read_repeated(const char *func,
+ lo_interface spi spidev, uint16_t addr, uint8_t block,
+ void *buf, void *buf_scratch, size_t len);
+#define w5500ll_read_reg(spidev, blockid, blocktyp, field) ({ \
+ typeof((blocktyp){}.field) val; \
+ if (sizeof(val) == 1) { \
+ w5500ll_readv(spidev, \
+ offsetof(blocktyp, field), blockid, \
+ &((struct rd_iovec){ \
+ .iov_read_to = &val, \
+ .iov_len = sizeof(val), \
+ }), 1, \
+ 0); \
+ } else { \
+ typeof(val) val2; \
+ _w5500ll_read_repeated(spidev, \
+ offsetof(blocktyp, field), blockid, \
+ &val, &val2, sizeof(val)); \
+ } \
+ val; \
+ })
- uint8_t header[] = {
- (uint8_t)((addr >> 8) & 0xFF),
- (uint8_t)(addr & 0xFF),
- (block & CTL_MASK_BLOCK) | CTL_R | CTL_OM_VDM,
- };
- 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_to = IOVEC_DISCARD,
- .iov_write_from = header,
- .iov_len = sizeof(header),
- };
- io_slice_rd_to_duplex(&inner[1], iov, iovcnt, 0, max);
- LO_CALL(spidev, readwritev, inner, inner_cnt);
-}
+/* Register blocks. ******************************************************************************/
/* Common chip-wide registers. ***********************************************/
@@ -378,49 +364,4 @@ static_assert(sizeof(struct w5500ll_block_sock_reg) == 0x30);
struct w5500ll_block_sock_reg, \
field)
-/******************************************************************************/
-
-#define w5500ll_write_reg(spidev, blockid, blocktyp, field, val) do { \
- typeof((blocktyp){}.field) lval = val; \
- w5500ll_writev(spidev, \
- offsetof(blocktyp, field), \
- blockid, \
- &((struct iovec){ \
- &lval, \
- sizeof(lval), \
- }), \
- 1, 0, 0); \
- } while (0)
-
-/* The datasheet tells us that multi-byte reads are non-atomic and
- * that "it is recommended that you read all 16-bits twice or more
- * until getting the same value". */
-#define w5500ll_read_reg(spidev, blockid, blocktyp, field) ({ \
- typeof((blocktyp){}.field) val; \
- w5500ll_readv(spidev, \
- offsetof(blocktyp, field), \
- blockid, \
- &((struct iovec){ \
- .iov_base = &val, \
- .iov_len = sizeof(val), \
- }), \
- 1, 0); \
- if (sizeof(val) > 1) \
- for (;;) { \
- typeof(val) val2; \
- w5500ll_readv(spidev, \
- offsetof(blocktyp, field), \
- blockid, \
- &((struct iovec){ \
- .iov_base = &val2, \
- .iov_len = sizeof(val), \
- }), \
- 1, 0); \
- if (memcmp(&val2, &val, sizeof(val)) == 0) \
- break; \
- val = val2; \
- } \
- val; \
- })
-
#endif /* _LIBHW_CR_W5500_LL_H_ */