Lely core libraries 2.3.4
can_net.c
Go to the documentation of this file.
1
24#include "io2.h"
25
26#if !LELY_NO_MALLOC
27
28#include <lely/can/net.h>
29#include <lely/io2/can_net.h>
30#include <lely/io2/ctx.h>
31#include <lely/libc/stdlib.h>
32#if !LELY_NO_THREADS
33#include <lely/libc/threads.h>
34#endif
35#include <lely/util/diag.h>
36#include <lely/util/spscring.h>
37#include <lely/util/time.h>
38#include <lely/util/util.h>
39
40#include <assert.h>
41
42#ifndef LELY_IO_CAN_NET_TXLEN
47#define LELY_IO_CAN_NET_TXLEN 1000
48#endif
49
50#ifndef LELY_IO_CAN_NET_TXTIMEO
55#define LELY_IO_CAN_NET_TXTIMEO 100
56#endif
57
58static void io_can_net_svc_shutdown(struct io_svc *svc);
59
60// clang-format off
61static const struct io_svc_vtbl io_can_net_svc_vtbl = {
62 NULL,
63 &io_can_net_svc_shutdown
64};
65// clang-format on
66
68struct io_can_net {
70 struct io_svc svc;
101 int state;
115 size_t tx_errcnt;
116#if !LELY_NO_THREADS
122#endif
162 unsigned started : 1;
164 unsigned shutdown : 1;
170 unsigned read_submitted : 1;
172 unsigned write_submitted : 1;
176 struct timespec next;
177};
178
179static void io_can_net_wait_next_func(struct ev_task *task);
180static void io_can_net_wait_confirm_func(struct ev_task *task);
181static void io_can_net_read_func(struct ev_task *task);
182static void io_can_net_write_func(struct ev_task *task);
183
184static int io_can_net_next_func(const struct timespec *tp, void *data);
185static int io_can_net_send_func(const struct can_msg *msg, void *data);
186
187static void io_can_net_c_wait_func(struct spscring *ring, void *arg);
188
189static inline io_can_net_t *io_can_net_from_svc(const struct io_svc *svc);
190
191static int io_can_net_do_wait(io_can_net_t *net);
192static void io_can_net_do_write(io_can_net_t *net);
193
194#if !LELY_NO_THREADS
195static size_t io_can_net_do_abort_tasks(io_can_net_t *net);
196#endif
197
198static void default_on_read_error_func(int errc, size_t errcnt, void *arg);
199static void default_on_queue_error_func(int errc, size_t errcnt, void *arg);
200static void default_on_write_error_func(int errc, size_t errcnt, void *arg);
201static void default_on_can_state_func(int new_state, int old_state, void *arg);
202static void default_on_can_error_func(int error, void *arg);
203
204void *
205io_can_net_alloc(void)
206{
207 void *ptr = aligned_alloc(_Alignof(io_can_net_t), sizeof(io_can_net_t));
208#if !LELY_NO_ERRNO
209 if (!ptr)
210 set_errc(errno2c(errno));
211#endif
212 return ptr;
213}
214
215void
216io_can_net_free(void *ptr)
217{
218 aligned_free(ptr);
219}
220
222io_can_net_init(io_can_net_t *net, ev_exec_t *exec, io_timer_t *timer,
223 io_can_chan_t *chan, size_t txlen, int txtimeo)
224{
225 assert(net);
226 assert(timer);
227 assert(chan);
228
229 int errc = 0;
230
231 if (!exec)
232 exec = io_can_chan_get_exec(chan);
233
234 if (!txlen)
235 txlen = LELY_IO_CAN_NET_TXLEN;
236
237 if (!txtimeo)
238 txtimeo = LELY_IO_CAN_NET_TXTIMEO;
239
240 net->svc = (struct io_svc)IO_SVC_INIT(&io_can_net_svc_vtbl);
241 net->ctx = io_can_chan_get_ctx(chan);
242 assert(net->ctx);
243
244 net->exec = exec;
245
246 net->timer = timer;
247 if (!(net->tq = io_tqueue_create(net->timer, NULL))) {
248 errc = get_errc();
249 goto error_create_tq;
250 }
252 0, 0, NULL, &io_can_net_wait_next_func);
254 0, 0, NULL, &io_can_net_wait_confirm_func);
255 net->txtimeo = txtimeo;
256
257 net->chan = chan;
258
259 net->read_msg = (struct can_msg)CAN_MSG_INIT;
260 net->read_err = (struct can_err)CAN_ERR_INIT;
262 &net->read_msg, &net->read_err, NULL, NULL,
263 &io_can_net_read_func);
264 net->read_errc = 0;
265 net->read_errcnt = 0;
266
267 net->state = CAN_STATE_ACTIVE;
268
269 net->write_msg = (struct can_msg)CAN_MSG_INIT;
271 &net->write_msg, NULL, &io_can_net_write_func);
272 net->write_errc = 0;
273 net->write_errcnt = 0;
274
275 spscring_init(&net->tx_ring, txlen);
276 net->tx_buf = calloc(txlen, sizeof(struct can_msg));
277 if (!net->tx_buf) {
278 errc = get_errc();
279 goto error_alloc_tx_buf;
280 }
281 net->tx_errcnt = 0;
282
283#if !LELY_NO_THREADS
284 if (mtx_init(&net->mtx, mtx_plain) != thrd_success) {
285 errc = get_errc();
286 goto error_init_mtx;
287 }
288#endif
289
290 net->on_read_error_func = &default_on_read_error_func;
291 net->on_read_error_arg = NULL;
292 net->on_queue_error_func = &default_on_queue_error_func;
293 net->on_queue_error_arg = NULL;
294 net->on_write_error_func = &default_on_write_error_func;
295 net->on_write_error_arg = NULL;
296 net->on_can_state_func = &default_on_can_state_func;
297 net->on_can_state_arg = NULL;
298 net->on_can_error_func = &default_on_can_error_func;
299 net->on_can_error_arg = NULL;
300
301 net->started = 0;
302 net->shutdown = 0;
303 net->wait_next_submitted = 0;
304 net->wait_confirm_submitted = 0;
305 net->read_submitted = 0;
306 net->write_submitted = 0;
307
308 if (!(net->net = can_net_create())) {
309 errc = get_errc();
310 goto error_create_net;
311 }
312 net->next = (struct timespec){ 0, 0 };
313
314 // Initialize the CAN network clock with the current time.
315 if (io_can_net_set_time(net) == -1) {
316 errc = get_errc();
317 goto error_set_time;
318 }
319
320 // Register the function to be invoked when the time at which the next
321 // timer triggers is updated.
322 can_net_set_next_func(net->net, &io_can_net_next_func, net);
323 // Register the function to be invoked when a CAN frame needs to be
324 // sent.
325 can_net_set_send_func(net->net, &io_can_net_send_func, net);
326
327 io_ctx_insert(net->ctx, &net->svc);
328
329 return net;
330
331error_set_time:
332 can_net_destroy(net->net);
333error_create_net:
334#if !LELY_NO_THREADS
335 mtx_destroy(&net->mtx);
336error_init_mtx:
337#endif
338 free(net->tx_buf);
339error_alloc_tx_buf:
340 io_tqueue_destroy(net->tq);
341error_create_tq:
342 set_errc(errc);
343 return NULL;
344}
345
346void
347io_can_net_fini(io_can_net_t *net)
348{
349 assert(net);
350
351 io_ctx_remove(net->ctx, &net->svc);
352 // Cancel all pending operations.
353 io_can_net_svc_shutdown(&net->svc);
354
355#if !LELY_NO_THREADS
356 int warning = 0;
357 mtx_lock(&net->mtx);
358 // If necessary, busy-wait until all submitted operations complete.
360 || net->read_submitted || net->write_submitted) {
361 if (io_can_net_do_abort_tasks(net))
362 continue;
363 mtx_unlock(&net->mtx);
364 if (!warning) {
365 warning = 1;
367 "io_can_net_fini() invoked with pending operations");
368 }
369 thrd_yield();
370 mtx_lock(&net->mtx);
371 }
372 mtx_unlock(&net->mtx);
373#endif
374
375 can_net_destroy(net->net);
376#if !LELY_NO_THREADS
377 mtx_destroy(&net->mtx);
378#endif
379 free(net->tx_buf);
380 io_tqueue_destroy(net->tq);
381}
382
385 size_t txlen, int txtimeo)
386{
387 int errc = 0;
388
389 io_can_net_t *net = io_can_net_alloc();
390 if (!net) {
391 errc = get_errc();
392 goto error_alloc;
393 }
394
395 io_can_net_t *tmp =
396 io_can_net_init(net, exec, timer, chan, txlen, txtimeo);
397 if (!tmp) {
398 errc = get_errc();
399 goto error_init;
400 }
401 net = tmp;
402
403 return net;
404
405error_init:
406 io_can_net_free(net);
407error_alloc:
408 set_errc(errc);
409 return NULL;
410}
411
412void
414{
415 if (net) {
416 io_can_net_fini(net);
417 io_can_net_free(net);
418 }
419}
420
421void
423{
424 assert(net);
425
426#if !LELY_NO_THREADS
427 mtx_lock(&net->mtx);
428#endif
429 if (!net->started && !net->shutdown) {
430 net->started = 1;
431
432 // Start waiting for CAN frames to be put into the transmit
433 // queue.
434 io_can_net_do_wait(net);
435
436 assert(!net->read_submitted);
437 net->read_submitted = 1;
438 // Start receiving CAN frames.
439 io_can_chan_submit_read(net->chan, &net->read);
440 }
441#if !LELY_NO_THREADS
442 mtx_unlock(&net->mtx);
443#endif
444}
445
446io_ctx_t *
448{
449 assert(net);
450
451 return net->ctx;
452}
453
454ev_exec_t *
456{
457 assert(net);
458
459 return net->exec;
460}
461
464{
465 assert(net);
466
468}
469
472{
473 assert(net);
474
475 return net->tq;
476}
477
478void
480 io_can_net_on_error_func_t **pfunc, void **parg)
481{
482 assert(net);
483
484#if !LELY_NO_THREADS
485 mtx_lock((mtx_t *)&net->mtx);
486#endif
487 if (pfunc)
488 *pfunc = net->on_read_error_func;
489 if (parg)
490 *parg = net->on_read_error_arg;
491#if !LELY_NO_THREADS
492 mtx_unlock((mtx_t *)&net->mtx);
493#endif
494}
495
496void
498 io_can_net_t *net, io_can_net_on_error_func_t *func, void *arg)
499{
500 assert(net);
501
502#if !LELY_NO_THREADS
503 mtx_lock(&net->mtx);
504#endif
505 net->on_read_error_func = func ? func : &default_on_read_error_func;
506 net->on_read_error_arg = func ? arg : NULL;
507#if !LELY_NO_THREADS
508 mtx_unlock(&net->mtx);
509#endif
510}
511
512void
514 io_can_net_on_error_func_t **pfunc, void **parg)
515{
516 assert(net);
517
518#if !LELY_NO_THREADS
519 mtx_lock((mtx_t *)&net->mtx);
520#endif
521 if (pfunc)
522 *pfunc = net->on_queue_error_func;
523 if (parg)
524 *parg = net->on_queue_error_arg;
525#if !LELY_NO_THREADS
526 mtx_unlock((mtx_t *)&net->mtx);
527#endif
528}
529
530void
532 io_can_net_t *net, io_can_net_on_error_func_t *func, void *arg)
533{
534 assert(net);
535
536#if !LELY_NO_THREADS
537 mtx_lock(&net->mtx);
538#endif
539 net->on_queue_error_func = func ? func : &default_on_queue_error_func;
540 net->on_queue_error_arg = func ? arg : NULL;
541#if !LELY_NO_THREADS
542 mtx_unlock(&net->mtx);
543#endif
544}
545
546void
548 io_can_net_on_error_func_t **pfunc, void **parg)
549{
550 assert(net);
551
552#if !LELY_NO_THREADS
553 mtx_lock((mtx_t *)&net->mtx);
554#endif
555 if (pfunc)
556 *pfunc = net->on_write_error_func;
557 if (parg)
558 *parg = net->on_write_error_arg;
559#if !LELY_NO_THREADS
560 mtx_unlock((mtx_t *)&net->mtx);
561#endif
562}
563
564void
566 io_can_net_t *net, io_can_net_on_error_func_t *func, void *arg)
567{
568 assert(net);
569
570#if !LELY_NO_THREADS
571 mtx_lock(&net->mtx);
572#endif
573 net->on_write_error_func = func ? func : &default_on_write_error_func;
574 net->on_write_error_arg = func ? arg : NULL;
575#if !LELY_NO_THREADS
576 mtx_unlock(&net->mtx);
577#endif
578}
579
580void
582 io_can_net_on_can_state_func_t **pfunc, void **parg)
583{
584 assert(net);
585
586#if !LELY_NO_THREADS
587 mtx_lock((mtx_t *)&net->mtx);
588#endif
589 if (pfunc)
590 *pfunc = net->on_can_state_func;
591 if (parg)
592 *parg = net->on_can_state_arg;
593#if !LELY_NO_THREADS
594 mtx_unlock((mtx_t *)&net->mtx);
595#endif
596}
597
598void
600 io_can_net_on_can_state_func_t *func, void *arg)
601{
602 assert(net);
603
604#if !LELY_NO_THREADS
605 mtx_lock(&net->mtx);
606#endif
607 net->on_can_state_func = func ? func : &default_on_can_state_func;
608 net->on_can_state_arg = func ? arg : NULL;
609#if !LELY_NO_THREADS
610 mtx_unlock(&net->mtx);
611#endif
612}
613
614void
616 io_can_net_on_can_error_func_t **pfunc, void **parg)
617{
618 assert(net);
619
620#if !LELY_NO_THREADS
621 mtx_lock((mtx_t *)&net->mtx);
622#endif
623 if (pfunc)
624 *pfunc = net->on_can_error_func;
625 if (parg)
626 *parg = net->on_can_error_arg;
627#if !LELY_NO_THREADS
628 mtx_unlock((mtx_t *)&net->mtx);
629#endif
630}
631
632void
634 io_can_net_on_can_error_func_t *func, void *arg)
635{
636 assert(net);
637
638#if !LELY_NO_THREADS
639 mtx_lock(&net->mtx);
640#endif
641 net->on_can_error_func = func ? func : &default_on_can_error_func;
642 net->on_can_error_arg = func ? arg : NULL;
643#if !LELY_NO_THREADS
644 mtx_unlock(&net->mtx);
645#endif
646}
647
648int
650{
651#if LELY_NO_THREADS
652 (void)net;
653
654 return 0;
655#else
656 assert(net);
657
658 return mtx_lock(&net->mtx);
659#endif
660}
661
662int
664{
665#if LELY_NO_THREADS
666 (void)net;
667
668 return 0;
669#else
670 assert(net);
671
672 return mtx_unlock(&net->mtx);
673#endif
674 assert(net);
675}
676
677can_net_t *
679{
680 assert(net);
681
682 return net->net;
683}
684
685int
687{
688 struct timespec now = { 0, 0 };
689 if (io_clock_gettime(io_can_net_get_clock(net), &now) == -1)
690 return -1;
691 return can_net_set_time(io_can_net_get_net(net), &now);
692}
693
694static void
695io_can_net_svc_shutdown(struct io_svc *svc)
696{
697 io_can_net_t *net = io_can_net_from_svc(svc);
698
699#if !LELY_NO_THREADS
700 mtx_lock(&net->mtx);
701#endif
702 int shutdown = !net->shutdown;
703 net->shutdown = 1;
704#if !LELY_NO_THREADS
705 mtx_unlock(&net->mtx);
706#endif
707
708 if (shutdown)
710}
711
712static void
713io_can_net_wait_next_func(struct ev_task *task)
714{
715 assert(task);
716 struct io_tqueue_wait *wait_next = io_tqueue_wait_from_task(task);
717 io_can_net_t *net = structof(wait_next, io_can_net_t, wait_next);
718 assert(net->wait_next_submitted);
719
720#if !LELY_NO_THREADS
721 mtx_lock(&net->mtx);
722#endif
723
724 // Update the time of the CAN network interface.
726
727 // Check if the next timeout is in the future and another wait operation
728 // needs to be submitted.
729 int submit_wait_next = 0;
730 if (!net->shutdown) {
731 struct timespec now = { 0, 0 };
732 can_net_get_time(net->net, &now);
733 if (timespec_cmp(&now, &net->next) < 0) {
734 net->wait_next.value = net->next;
735 submit_wait_next = 1;
736 }
737 }
738 net->wait_next_submitted = submit_wait_next;
739
740#if !LELY_NO_THREADS
741 mtx_unlock(&net->mtx);
742#endif
743
744 if (submit_wait_next)
746}
747
748static void
749io_can_net_wait_confirm_func(struct ev_task *task)
750{
751 assert(task);
752 struct io_tqueue_wait *wait_confirm = io_tqueue_wait_from_task(task);
753 io_can_net_t *net = structof(wait_confirm, io_can_net_t, wait_confirm);
754 assert(net->txtimeo >= 0);
755 assert(net->wait_confirm_submitted);
756
757#if !LELY_NO_THREADS
758 mtx_lock(&net->mtx);
759#endif
760 net->wait_confirm_submitted = 0;
761#if !LELY_NO_THREADS
762 mtx_unlock(&net->mtx);
763#endif
764
765 // No confirmation message was received; cancel the ongoing write
766 // operation.
768}
769
770static void
771io_can_net_read_func(struct ev_task *task)
772{
773 assert(task);
775 io_can_net_t *net = structof(read, io_can_net_t, read);
776
777#if !LELY_NO_THREADS
778 mtx_lock(&net->mtx);
779#endif
780
781 if (read->r.errc && errc2num(read->r.errc) != ERRNUM_CANCELED) {
782 net->read_errcnt += net->read_errcnt < SIZE_MAX;
783 if (read->r.errc != net->read_errc) {
784 net->read_errc = read->r.errc;
785 // Only invoke the callback for unique read errors.
786 assert(net->on_read_error_func);
788 net->read_errcnt,
789 net->on_read_error_arg);
790 }
791 } else if (!read->r.errc && net->read_errc) {
792 assert(net->on_read_error_func);
794 0, net->read_errcnt, net->on_read_error_arg);
795 net->read_errc = 0;
796 net->read_errcnt = 0;
797 }
798
799 if (read->r.result == 1) {
800 // Update the internal clock before processing the incoming CAN
801 // frame.
803 can_net_recv(net->net, &net->read_msg);
804 } else if (read->r.result == 0) {
805 if (net->read_err.state != net->state) {
806 int new_state = net->read_err.state;
807 int old_state = net->state;
808 net->state = net->read_err.state;
809
810 if (old_state == CAN_STATE_BUSOFF)
811 // Cancel the ongoing write operation if we just
812 // recovered from bus off.
814 net->chan, &net->write);
815
816 assert(net->on_can_state_func);
817 net->on_can_state_func(new_state, old_state,
818 net->on_can_state_arg);
819 }
820
821 if (net->read_err.error) {
822 assert(net->on_can_error_func);
824 net->on_can_error_arg);
825 }
826 }
827
828 int submit_read = net->read_submitted = !net->shutdown;
829
830#if !LELY_NO_THREADS
831 mtx_unlock(&net->mtx);
832#endif
833
834 if (submit_read)
835 io_can_chan_submit_read(net->chan, &net->read);
836}
837
838static void
839io_can_net_write_func(struct ev_task *task)
840{
841 assert(task);
843 io_can_net_t *net = structof(write, io_can_net_t, write);
844
845#if !LELY_NO_THREADS
846 mtx_lock(&net->mtx);
847#endif
848
849 if (write->errc) {
850 net->write_errcnt += net->write_errcnt < SIZE_MAX;
851 if (write->errc != net->write_errc) {
852 net->write_errc = write->errc;
853 // Only invoke the callback for unique write errors.
855 net->write_errcnt,
856 net->on_write_error_arg);
857 }
858 } else if (net->write_errc) {
859 assert(net->on_write_error_func);
861 0, net->write_errcnt, net->on_write_error_arg);
862 net->write_errc = 0;
863 net->write_errcnt = 0;
864 }
865
866 // Remove the frame from the transmit queue, unless the write operation
867 // was canceled, in which we discard the entire queue.
868 assert(spscring_c_capacity(&net->tx_ring) >= 1);
869 size_t n = 1;
870 if (errc2num(write->errc) == ERRNUM_CANCELED) {
871 n = spscring_c_capacity(&net->tx_ring);
872 // Track the number of dropped frames. The first frame has
873 // already been accounted for.
874 net->write_errcnt += n - 1;
875 }
876 spscring_c_commit(&net->tx_ring, n);
877
878 // Stop the timeout after receiving a write confirmation (or write
879 // error).
881 && io_tqueue_abort_wait(net->tq, &net->wait_confirm))
882 net->wait_confirm_submitted = 0;
883
884 // Write the next frame, if available.
885 net->write_submitted = 0;
886 if (!net->shutdown && !io_can_net_do_wait(net))
887 io_can_net_do_write(net);
888
889#if !LELY_NO_THREADS
890 mtx_unlock(&net->mtx);
891#endif
892}
893
894static int
895io_can_net_next_func(const struct timespec *tp, void *data)
896{
897 assert(tp);
898 io_can_net_t *net = data;
899 assert(net);
900
901 // Ignore calls that do not change the next timeout.
902 if (!timespec_cmp(&net->next, tp))
903 return 0;
904
905 // In case io_can_net_wait_next_func() is currently running, store the
906 // time for the next earliest timeout so io_can_net_wait_next_func() can
907 // re-submit the wait operation.
908 net->next = *tp;
909
910 if (net->shutdown)
911 return 0;
912
913 // Re-submit the wait operation with the new timeout, but only if we can
914 // be sure io_can_net_wait_next_func() is not currently running.
915 if (!net->wait_next_submitted
916 || io_tqueue_abort_wait(net->tq, &net->wait_next)) {
917 net->wait_next_submitted = 1;
918 net->wait_next.value = *tp;
920 }
921
922 return 0;
923}
924
925static int
926io_can_net_send_func(const struct can_msg *msg, void *data)
927{
928 assert(msg);
929 io_can_net_t *net = data;
930 assert(net);
931
932 size_t n = 1;
933 size_t i = spscring_p_alloc(&net->tx_ring, &n);
934 if (n) {
935 net->tx_buf[i] = *msg;
936 spscring_p_commit(&net->tx_ring, n);
937 if (net->tx_errcnt) {
938 assert(net->on_queue_error_func);
939 net->on_queue_error_func(0, net->tx_errcnt,
940 net->on_queue_error_arg);
941 net->tx_errcnt = 0;
942 }
943 return 0;
944 } else {
946 net->tx_errcnt += net->tx_errcnt < SIZE_MAX;
947 if (net->tx_errcnt == 1) {
948 // Only invoke the callback for the first transmission
949 // error.
950 assert(net->on_queue_error_func);
952 net->on_queue_error_arg);
953 }
954 return -1;
955 }
956}
957
958static void
959io_can_net_c_wait_func(struct spscring *ring, void *arg)
960{
961 (void)ring;
962 io_can_net_t *net = arg;
963 assert(net);
964
965 // A frame was just added to the transmit queue; try to send it.
966 if (!io_can_net_do_wait(net))
967 io_can_net_do_write(net);
968}
969
970static inline io_can_net_t *
971io_can_net_from_svc(const struct io_svc *svc)
972{
973 assert(svc);
974
975 return structof(svc, io_can_net_t, svc);
976}
977
978static int
979io_can_net_do_wait(io_can_net_t *net)
980{
981 assert(net);
982
983 // Wait for next frame to become available.
984 // clang-format off
986 &net->tx_ring, 1, &io_can_net_c_wait_func, net))
987 // clang-format on
988 return 1;
989
990 // Extract the frame from the transmit queue.
991 size_t n = 1;
992 size_t i = spscring_c_alloc(&net->tx_ring, &n);
993 assert(n == 1);
994 net->write_msg = net->tx_buf[i];
995
996 return 0;
997}
998
999static void
1000io_can_net_do_write(io_can_net_t *net)
1001{
1002 assert(net);
1003 assert(spscring_c_capacity(&net->tx_ring) >= 1);
1004 assert(!net->write_submitted);
1005
1006 // Send the frame.
1007 net->write_submitted = 1;
1008 io_can_chan_submit_write(net->chan, &net->write);
1009
1010 // Register a timeout for the write confirmation, if necessary.
1011 if (net->txtimeo >= 0
1012 && (!net->wait_confirm_submitted
1013 || io_tqueue_abort_wait(net->tq,
1014 &net->wait_confirm))) {
1015 net->wait_confirm_submitted = 1;
1017 &net->wait_confirm.value);
1020 }
1021}
1022
1023#if !LELY_NO_THREADS
1024static size_t
1025io_can_net_do_abort_tasks(io_can_net_t *net)
1026{
1027 assert(net);
1028
1029 size_t n = 0;
1030
1031 if (net->wait_next_submitted
1032 && io_tqueue_abort_wait(net->tq, &net->wait_next)) {
1033 net->wait_next_submitted = 0;
1034 n++;
1035 }
1036
1037 if (net->wait_confirm_submitted
1038 && io_tqueue_abort_wait(net->tq, &net->wait_confirm)) {
1039 net->wait_confirm_submitted = 0;
1040 n++;
1041 }
1042
1043 if (net->read_submitted
1044 && io_can_chan_abort_read(net->chan, &net->read)) {
1045 net->read_submitted = 0;
1046 n++;
1047 }
1048
1049 if (net->write_submitted
1050 && io_can_chan_abort_write(net->chan, &net->write)) {
1051 net->write_submitted = 0;
1052 n++;
1053 }
1054
1055 return 0;
1056}
1057#endif // !LELY_NO_THREADS
1058
1059static void
1060default_on_read_error_func(int errc, size_t errcnt, void *arg)
1061{
1062 (void)arg;
1063
1064 if (errc)
1065 diag(DIAG_WARNING, errc, "error reading CAN frame");
1066 else
1067 diag(DIAG_INFO, 0,
1068 "CAN frame successfully read after %zu read error%s",
1069 errcnt, errcnt > 1 ? "s" : "");
1070}
1071
1072static void
1073default_on_queue_error_func(int errc, size_t errcnt, void *arg)
1074{
1075 (void)arg;
1076
1077 if (errc)
1079 "CAN transmit queue full; dropping frame");
1080 else
1081 diag(DIAG_INFO, 0,
1082 "CAN frame successfully queued after dropping %zd frame%s",
1083 errcnt, errcnt > 1 ? "s" : "");
1084}
1085
1086static void
1087default_on_write_error_func(int errc, size_t errcnt, void *arg)
1088{
1089 (void)arg;
1090
1091 if (errc)
1092 diag(DIAG_WARNING, errc, "error writing CAN frame");
1093 else
1094 diag(DIAG_INFO, 0,
1095 "CAN frame successfully written after %zu write error%s",
1096 errcnt, errcnt > 1 ? "s" : "");
1097}
1098
1099static void
1100default_on_can_state_func(int new_state, int old_state, void *arg)
1101{
1102 (void)old_state;
1103 (void)arg;
1104
1105 switch (new_state) {
1106 case CAN_STATE_ACTIVE:
1107 diag(DIAG_INFO, 0, "CAN bus is in the error active state");
1108 break;
1109 case CAN_STATE_PASSIVE:
1110 diag(DIAG_INFO, 0, "CAN bus is in the error passive state");
1111 break;
1112 case CAN_STATE_BUSOFF:
1113 diag(DIAG_WARNING, 0, "CAN bus is in the bus off state");
1114 break;
1115 case CAN_STATE_SLEEPING:
1116 diag(DIAG_INFO, 0, "CAN interface is in sleep mode");
1117 break;
1118 case CAN_STATE_STOPPED:
1119 diag(DIAG_WARNING, 0, "CAN interface is stopped");
1120 break;
1121 }
1122}
1123
1124static void
1125default_on_can_error_func(int error, void *arg)
1126{
1127 (void)arg;
1128
1129 if (error & CAN_ERROR_BIT)
1130 diag(DIAG_WARNING, 0, "single bit error detected on CAN bus");
1131 if (error & CAN_ERROR_STUFF)
1132 diag(DIAG_WARNING, 0, "bit stuffing error detected on CAN bus");
1133 if (error & CAN_ERROR_CRC)
1134 diag(DIAG_WARNING, 0, "CRC sequence error detected on CAN bus");
1135 if (error & CAN_ERROR_FORM)
1136 diag(DIAG_WARNING, 0, "form error detected on CAN bus");
1137 if (error & CAN_ERROR_ACK)
1138 diag(DIAG_WARNING, 0,
1139 "acknowledgment error detected on CAN bus");
1140 if (error & CAN_ERROR_OTHER)
1141 diag(DIAG_WARNING, 0,
1142 "one or more unknown errors detected on CAN bus");
1143}
1144
1145#endif // !LELY_NO_MALLOC
@ CAN_ERROR_FORM
A form error.
Definition: err.h:50
@ CAN_ERROR_BIT
A single bit error.
Definition: err.h:44
@ CAN_ERROR_STUFF
A bit stuffing error.
Definition: err.h:46
@ CAN_ERROR_ACK
An acknowledgment error.
Definition: err.h:52
@ CAN_ERROR_CRC
A CRC sequence error.
Definition: err.h:48
@ CAN_ERROR_OTHER
One or more other errors.
Definition: err.h:54
@ CAN_STATE_BUSOFF
The bus off state (TX/RX error count >= 256).
Definition: err.h:34
@ CAN_STATE_SLEEPING
The device is in sleep mode.
Definition: err.h:36
@ CAN_STATE_PASSIVE
The error passive state (TX/RX error count < 256).
Definition: err.h:32
@ CAN_STATE_STOPPED
The device is stopped.
Definition: err.h:38
@ CAN_STATE_ACTIVE
The error active state (TX/RX error count < 128).
Definition: err.h:30
#define CAN_MSG_INIT
The static initializer for can_msg.
Definition: msg.h:113
io_clock_t * io_can_net_get_clock(const io_can_net_t *net)
Returns a pointer to the clock used by the CAN network interface.
Definition: can_net.c:463
void io_can_net_set_on_read_error_func(io_can_net_t *net, io_can_net_on_error_func_t *func, void *arg)
Sets the function invoked when a new CAN frame read error occurs, or when a read operation completes ...
Definition: can_net.c:497
void io_can_net_set_on_can_state_func(io_can_net_t *net, io_can_net_on_can_state_func_t *func, void *arg)
Sets the function to be invoked when a CAN bus state change is detected.
Definition: can_net.c:599
io_tqueue_t * io_can_net_get_tqueue(const io_can_net_t *net)
Returns a pointer to the internal timer queue of a CAN network interface.
Definition: can_net.c:471
can_net_t * io_can_net_get_net(const io_can_net_t *net)
Returns a pointer to the internal interface of a CAN network interface.
Definition: can_net.c:678
io_ctx_t * io_can_net_get_ctx(const io_can_net_t *net)
Returns a pointer to the I/O context with which the CAN network interface is registered.
Definition: can_net.c:447
void io_can_net_get_on_read_error_func(const io_can_net_t *net, io_can_net_on_error_func_t **pfunc, void **parg)
Retrieves the function invoked when a new CAN frame read error occurs, or when a read operation compl...
Definition: can_net.c:479
void io_can_net_start(io_can_net_t *net)
Starts a CAN network interface and begins processing CAN frames.
Definition: can_net.c:422
int io_can_net_unlock(io_can_net_t *net)
Unlocks the mutex protecting the CAN network interface.
Definition: can_net.c:663
#define LELY_IO_CAN_NET_TXTIMEO
The default timeout (in milliseconds) of a CAN network interface when waiting for a CAN frame write c...
Definition: can_net.c:55
io_can_net_t * io_can_net_create(ev_exec_t *exec, io_timer_t *timer, io_can_chan_t *chan, size_t txlen, int txtimeo)
Creates a new CAN network interface.
Definition: can_net.c:384
void io_can_net_set_on_queue_error_func(io_can_net_t *net, io_can_net_on_error_func_t *func, void *arg)
Sets the function invoked when a CAN frame is dropped because the transmit queue is full,...
Definition: can_net.c:531
void io_can_net_get_on_write_error_func(const io_can_net_t *net, io_can_net_on_error_func_t **pfunc, void **parg)
Retrieves the function invoked when a new CAN frame write error occurs, or when a write operation com...
Definition: can_net.c:547
void io_can_net_get_on_queue_error_func(const io_can_net_t *net, io_can_net_on_error_func_t **pfunc, void **parg)
Retrieves the function invoked when a CAN frame is dropped because the transmit queue is full,...
Definition: can_net.c:513
void io_can_net_get_on_can_state_func(const io_can_net_t *net, io_can_net_on_can_state_func_t **pfunc, void **parg)
Retrieves the function invoked when a CAN bus state change is detected.
Definition: can_net.c:581
int io_can_net_set_time(io_can_net_t *net)
Updates the CAN network time.
Definition: can_net.c:686
void io_can_net_destroy(io_can_net_t *net)
Destroys a CAN network interface.
Definition: can_net.c:413
ev_exec_t * io_can_net_get_exec(const io_can_net_t *net)
Returns a pointer to the executor used by the CAN network interface to execute asynchronous tasks.
Definition: can_net.c:455
void io_can_net_set_on_can_error_func(io_can_net_t *net, io_can_net_on_can_error_func_t *func, void *arg)
Sets the function to be invoked when a CAN bus error is detected.
Definition: can_net.c:633
void io_can_net_get_on_can_error_func(const io_can_net_t *net, io_can_net_on_can_error_func_t **pfunc, void **parg)
Retrieves the function invoked when a CAN bus error is detected.
Definition: can_net.c:615
#define LELY_IO_CAN_NET_TXLEN
The default length (in number of CAN frames) of the user-space transmit queue of a CAN network interf...
Definition: can_net.c:47
int io_can_net_lock(io_can_net_t *net)
Locks the mutex protecting the CAN network interface.
Definition: can_net.c:649
void io_can_net_set_on_write_error_func(io_can_net_t *net, io_can_net_on_error_func_t *func, void *arg)
Sets the function invoked when a new CAN frame write error occurs, or when a write operation complete...
Definition: can_net.c:565
This header file is part of the I/O library; it contains the CAN network interface declarations.
void io_can_net_on_can_state_func_t(int new_state, int old_state, void *arg)
The type of function invoked when a CAN bus state change is detected by a CAN network interface.
Definition: can_net.h:72
void io_can_net_on_error_func_t(int errc, size_t errcnt, void *arg)
The type of function invoked when an error occurs during a CAN network interface operations,...
Definition: can_net.h:54
void io_can_net_on_can_error_func_t(int error, void *arg)
The type of function invoked when a CAN bus error is detected by an CAN network interface.
Definition: can_net.h:87
int io_clock_gettime(const io_clock_t *clock, struct timespec *tp)
Obtains the current time value of the specified clock.
Definition: clock.h:96
const struct io_clock_vtbl *const io_clock_t
An abstract clock.
Definition: clock.h:36
This header file is part of the I/O library; it contains the I/O context and service declarations.
void io_ctx_insert(io_ctx_t *ctx, struct io_svc *svc)
Registers an I/O service with an I/O context.
Definition: ctx.c:126
#define IO_SVC_INIT(vptr)
The static initializer for io_svc.
Definition: ctx.h:57
void io_ctx_remove(io_ctx_t *ctx, struct io_svc *svc)
Unregisters an I/O service with an I/O context.
Definition: ctx.c:141
This header file is part of the utilities library; it contains the diagnostic declarations.
@ DIAG_WARNING
A warning.
Definition: diag.h:55
@ DIAG_INFO
An informational message.
Definition: diag.h:53
void diag(enum diag_severity severity, int errc, const char *format,...)
Emits a diagnostic message.
Definition: diag.c:171
errnum_t errc2num(int errc)
Transforms a native error code to a platform-independent error number.
Definition: errnum.c:309
@ ERRNUM_AGAIN
Resource unavailable, try again.
Definition: errnum.h:89
@ ERRNUM_CANCELED
Operation canceled.
Definition: errnum.h:99
int get_errc(void)
Returns the last (thread-specific) native error code set by a system call or library function.
Definition: errnum.c:932
void set_errc(int errc)
Sets the current (thread-specific) native error code to errc.
Definition: errnum.c:944
int errno2c(int errnum)
Transforms a standard C error number to a native error code.
Definition: errnum.c:46
void set_errnum(errnum_t errnum)
Sets the current (thread-specific) platform-independent error number to errnum.
Definition: errnum.h:424
#define _Alignof(x)
Specifies the alignment requirement of the declared object or member.
Definition: features.h:200
const struct ev_exec_vtbl *const ev_exec_t
An abstract task executor.
Definition: ev.h:29
static io_ctx_t * io_can_chan_get_ctx(const io_can_chan_t *chan)
Definition: can.h:445
const struct io_can_chan_vtbl *const io_can_chan_t
An abstract CAN channel.
Definition: can.h:59
static size_t io_can_chan_abort_write(io_can_chan_t *chan, struct io_can_chan_write *write)
Aborts the specified CAN channel write operation if it is pending.
Definition: can.h:524
struct io_can_chan_read * io_can_chan_read_from_task(struct ev_task *task)
Obtains a pointer to a CAN channel read operation from a pointer to its completion task.
Definition: can.c:93
static size_t io_can_chan_abort_read(io_can_chan_t *chan, struct io_can_chan_read *read)
Aborts the specified CAN channel read operation if it is pending.
Definition: can.h:500
struct io_can_chan_write * io_can_chan_write_from_task(struct ev_task *task)
Obtains a pointer to a CAN channel write operation from a pointer to its completion task.
Definition: can.c:99
void io_can_chan_submit_write(io_can_chan_t *chan, struct io_can_chan_write *write)
Submits a write operation to a CAN channel.
Definition: can.h:512
void io_can_chan_submit_read(io_can_chan_t *chan, struct io_can_chan_read *read)
Submits a read operation to a CAN channel.
Definition: can.h:488
#define IO_CAN_CHAN_WRITE_INIT(msg, exec, func)
The static initializer for io_can_chan_write.
Definition: can.h:130
#define IO_CAN_CHAN_READ_INIT(msg, err, tp, exec, func)
The static initializer for io_can_chan_read.
Definition: can.h:104
static ev_exec_t * io_can_chan_get_exec(const io_can_chan_t *chan)
Definition: can.h:451
static size_t io_can_chan_cancel_write(io_can_chan_t *chan, struct io_can_chan_write *write)
Cancels the specified CAN channel write operation if it is pending.
Definition: can.h:518
const struct io_timer_vtbl *const io_timer_t
An abstract timer.
Definition: timer.h:38
io_clock_t * io_timer_get_clock(const io_timer_t *timer)
Returns a pointer to the clock used by the timer.
Definition: timer.h:238
This is the public header file of the utilities library.
#define structof(ptr, type, member)
Obtains the address of a structure from the address of one of its members.
Definition: util.h:93
#define CAN_ERR_INIT
The static initializer for a can_err struct.
Definition: err.h:43
This header file is part of the CAN library; it contains the CAN network interface declarations.
void can_net_get_time(const can_net_t *net, struct timespec *tp)
Retrieves the current time of a CAN network interface.
Definition: net.c:196
void can_net_set_send_func(can_net_t *net, can_send_func_t *func, void *data)
Sets the callback function used to send CAN frames from a network interface.
Definition: net.c:326
void can_net_destroy(can_net_t *net)
Destroys a CAN network interface.
Definition: net.c:187
can_net_t * can_net_create(void)
Creates a new CAN network interface.
Definition: net.c:162
void can_net_set_next_func(can_net_t *net, can_timer_func_t *func, void *data)
Sets the callback function invoked when the time at which the next CAN timer triggers is updated.
Definition: net.c:261
int can_net_recv(can_net_t *net, const struct can_msg *msg)
Receives a CAN frame with a network interface and processes it with the corresponding receiver(s).
Definition: net.c:270
int can_net_set_time(can_net_t *net, const struct timespec *tp)
Sets the current time of a CAN network interface.
Definition: net.c:205
This header file is part of the utilities library; it contains the single-producer,...
size_t spscring_c_alloc(struct spscring *ring, size_t *psize)
Allocates a consecutive range of indices, including wrapping, in a single-producer,...
Definition: spscring.c:262
int spscring_c_submit_wait(struct spscring *ring, size_t size, void(*func)(struct spscring *ring, void *arg), void *arg)
Checks if the requested range of indices, including wrapping, in a single-producer,...
Definition: spscring.c:327
size_t spscring_p_commit(struct spscring *ring, size_t size)
Makes the specified number of indices available to a consumer and, if this satisfies a wait operation...
Definition: spscring.c:138
size_t spscring_c_commit(struct spscring *ring, size_t size)
Makes the specified number of indices available to a producer and, if this satisfies a wait operation...
Definition: spscring.c:302
size_t spscring_c_capacity(struct spscring *ring)
Returns the total capacity available for a consumer in a single-producer single-consumer ring buffer,...
Definition: spscring.c:229
int spscring_c_abort_wait(struct spscring *ring)
Aborts a wait operation previously registered with spscring_c_submit_wait().
Definition: spscring.c:368
void spscring_init(struct spscring *ring, size_t size)
Initializes a single-producer, single-consumer ring buffer with the specified size.
Definition: spscring.c:47
size_t spscring_p_alloc(struct spscring *ring, size_t *psize)
Allocates a consecutive range of indices, including wrapping, in a single-producer,...
Definition: spscring.c:98
This is the internal header file of the I/O library.
This header file is part of the C11 and POSIX compatibility library; it includes <stdlib....
void aligned_free(void *ptr)
Causes the space at ptr to be deallocated, that is, made available for further allocation.
Definition: stdlib.c:94
void * aligned_alloc(size_t alignment, size_t size)
Allocates space for an object whose alignment is specified by alignment, whose size is specified by s...
Definition: stdlib.c:48
A CAN network interface.
Definition: net.c:37
A CAN error frame.
Definition: err.h:28
int state
The state of the CAN node (one of CAN_STATE_ACTIVE, CAN_STATE_PASSIVE or CAN_STATE_BUSOFF).
Definition: err.h:33
int error
The error flags of the CAN bus (any combination of CAN_ERROR_BIT, CAN_ERROR_STUFF,...
Definition: err.h:39
A CAN or CAN FD format frame.
Definition: msg.h:87
An executable task.
Definition: task.h:41
int result
The result of the read operation: 1 if a CAN frame is received, 0 if an error frame is received,...
Definition: can.h:68
int errc
The error number, obtained as if by get_errc(), if result is -1.
Definition: can.h:70
A CAN channel read operation.
Definition: can.h:74
struct ev_task task
The task (to be) submitted upon completion (or cancellation) of the read operation.
Definition: can.h:98
struct io_can_chan_read_result r
The result of the read operation.
Definition: can.h:100
A CAN channel write operation.
Definition: can.h:110
const struct can_msg * msg
A pointer to the CAN frame to be written.
Definition: can.h:116
struct ev_task task
The task (to be) submitted upon completion (or cancellation) of the write operation.
Definition: can.h:121
int errc
The error number, obtained as if by get_errc(), if an error occurred or the operation was canceled.
Definition: can.h:126
The implementation of a CAN network interface.
Definition: can_net.c:68
struct can_msg write_msg
The CAN frame being written.
Definition: can_net.c:103
void * on_can_error_arg
The user-specified argument for #on_error_func.
Definition: can_net.c:160
struct io_svc svc
The I/O service representing the channel.
Definition: can_net.c:70
mtx_t mtx
The mutex protecting the callbacks, flags and internal CAN network interface.
Definition: can_net.c:121
int read_errc
The error code of the last read operation.
Definition: can_net.c:97
unsigned started
A flag indicating wheter the CAN network interface has been started.
Definition: can_net.c:162
struct can_msg * tx_buf
The transmit queue.
Definition: can_net.c:113
int txtimeo
The timeout (in milliseconds) when waiting for a CAN frame write confirmation.
Definition: can_net.c:87
struct can_msg read_msg
The CAN frame being read.
Definition: can_net.c:91
void * on_queue_error_arg
The user-specified argument for on_queue_error_func.
Definition: can_net.c:138
unsigned shutdown
A flag indicating whether the I/O service has been shut down.
Definition: can_net.c:164
void * on_can_state_arg
The user-specified argument for #on_state_func.
Definition: can_net.c:153
io_can_net_on_can_state_func_t * on_can_state_func
A pointer to the function to be invoked when a CAN bus state change is detected.
Definition: can_net.c:151
void * on_read_error_arg
The user-specified argument for on_read_error_func.
Definition: can_net.c:130
unsigned read_submitted
A flag indicating whether read has been submitted to chan.
Definition: can_net.c:170
io_can_net_on_error_func_t * on_write_error_func
A pointer to the function invoked when a new CAN frame write error occurs, or when a write operation ...
Definition: can_net.c:144
int write_errc
The error code of the last write operation.
Definition: can_net.c:107
int state
The current state of the CAN bus.
Definition: can_net.c:101
struct io_can_chan_write write
The operation used to write CAN frames.
Definition: can_net.c:105
size_t tx_errcnt
The number of frames dropped due to the transmit queue being full.
Definition: can_net.c:115
size_t write_errcnt
The number of errors since the last successful write operation.
Definition: can_net.c:109
io_timer_t * timer
A pointer to the timer used for CAN network events.
Definition: can_net.c:76
io_can_net_on_error_func_t * on_queue_error_func
A pointer to the function invoked when a CAN frame is dropped because the transmit queue is full,...
Definition: can_net.c:136
struct io_tqueue_wait wait_next
The operation used to wait for the next CANopen event.
Definition: can_net.c:80
io_can_chan_t * chan
A pointer to a CAN channel.
Definition: can_net.c:89
unsigned wait_next_submitted
A flag indicating whether wait_next has been submitted to tq.
Definition: can_net.c:166
io_ctx_t * ctx
A pointer to the I/O context with which the channel is registered.
Definition: can_net.c:72
io_tqueue_t * tq
A pointer to the timer queue used to schedule wait operations.
Definition: can_net.c:78
struct spscring tx_ring
The ring buffer used to control the transmit queue.
Definition: can_net.c:111
struct io_tqueue_wait wait_confirm
The operation used to wait for a CAN frame write confirmation.
Definition: can_net.c:82
can_net_t * net
A pointer to the internal CAN network interface.
Definition: can_net.c:174
size_t read_errcnt
The number of errors since the last successful read operation.
Definition: can_net.c:99
unsigned write_submitted
A flag indicating whether write has been submitted to chan.
Definition: can_net.c:172
io_can_net_on_can_error_func_t * on_can_error_func
A pointer to the function to be invoked when an error is detected on the CAN bus.
Definition: can_net.c:158
io_can_net_on_error_func_t * on_read_error_func
A pointer to the function invoked when a new CAN frame read error occurs, or when a read operation co...
Definition: can_net.c:128
struct io_can_chan_read read
The operation used to read CAN frames.
Definition: can_net.c:95
void * on_write_error_arg
The user-specified argument for on_write_error_func.
Definition: can_net.c:146
unsigned wait_confirm_submitted
A flag indicating whether wait_confirm has been submitted to tq.
Definition: can_net.c:168
struct timespec next
The time at which the next CAN timer will trigger.
Definition: can_net.c:176
ev_exec_t * exec
A pointer to the executor ...
Definition: can_net.c:74
struct can_err read_err
The CAN error frame being read.
Definition: can_net.c:93
Definition: ctx.c:38
The virtual table of an I/O service.
Definition: ctx.h:67
An I/O service.
Definition: ctx.h:49
A wait operation suitable for use with a timer queue.
Definition: tqueue.h:36
struct ev_task task
The task (to be) submitted upon completion (or cancellation) of the wait operation.
Definition: tqueue.h:43
struct timespec value
The absolute expiration time.
Definition: tqueue.h:38
A single-producer, single-consumer ring buffer.
Definition: spscring.h:63
This header file is part of the C11 and POSIX compatibility library; it includes <threads....
int mtx_init(mtx_t *mtx, int type)
Creates a mutex object with properties indicated by type, which must have one of the four values:
int mtx_lock(mtx_t *mtx)
Blocks until it locks the mutex at mtx.
void thrd_yield(void)
Endeavors to permit other threads to run, even if the current thread would ordinarily continue to run...
@ thrd_success
Indicates that the requested operation succeeded.
Definition: threads.h:121
int mtx_unlock(mtx_t *mtx)
Unlocks the mutex at mtx.
pthread_mutex_t mtx_t
A complete object type that holds an identifier for a mutex.
Definition: threads.h:102
void mtx_destroy(mtx_t *mtx)
Releases any resources used by the mutex at mtx.
@ mtx_plain
A mutex type that supports neither timeout nor test and return.
Definition: threads.h:109
void io_tqueue_destroy(io_tqueue_t *tq)
Destroys a timer queue.
Definition: tqueue.c:211
struct io_tqueue_wait * io_tqueue_wait_from_task(struct ev_task *task)
Obtains a pointer to a timer queue wait operation from a pointer to its completion task.
Definition: tqueue.c:358
size_t io_tqueue_abort_wait(io_tqueue_t *tq, struct io_tqueue_wait *wait)
Aborts the specified timer queue wait operation if it is pending.
Definition: tqueue.c:313
io_tqueue_t * io_tqueue_create(io_timer_t *timer, ev_exec_t *exec)
Creates a new timer queue.
Definition: tqueue.c:184
io_timer_t * io_tqueue_get_timer(const io_tqueue_t *tq)
Returns a pointer to the I/O timer used by the timer queue.
Definition: tqueue.c:228
void io_tqueue_submit_wait(io_tqueue_t *tq, struct io_tqueue_wait *wait)
Submits a wait operation to a timer queue.
Definition: tqueue.c:236
#define IO_TQUEUE_WAIT_INIT(sec, nsec, exec, func)
The static initializer for io_tqueue_wait.
Definition: tqueue.h:53
This header file is part of the utilities library; it contains the time function declarations.
int timespec_cmp(const void *p1, const void *p2)
Compares two times.
Definition: time.h:251
void timespec_add_msec(struct timespec *tp, uint_least64_t msec)
Adds msec milliseconds to the time at tp.
Definition: time.h:141