Lely core libraries 2.3.4
vcan.c
Go to the documentation of this file.
1
24#include "can.h"
25
26#if !LELY_NO_MALLOC
27
28#if !LELY_NO_THREADS
29#include <lely/libc/threads.h>
30#endif
31#include <lely/io2/ctx.h>
32#include <lely/io2/vcan.h>
33#include <lely/util/diag.h>
34#include <lely/util/spscring.h>
35#include <lely/util/time.h>
36#include <lely/util/util.h>
37
38#include <assert.h>
39#include <stdint.h>
40#include <stdlib.h>
41
42#ifndef LELY_IO_VCAN_BITRATE
44#define LELY_IO_VCAN_BITRATE 1000000
45#endif
46
47#ifndef LELY_IO_VCAN_RXLEN
52#define LELY_IO_VCAN_RXLEN 1024
53#endif
54
56 int is_err;
57 union {
58 struct can_msg msg;
59 struct can_err err;
60 } u;
61 struct timespec ts;
62};
63
64static int io_vcan_ctrl_stop(io_can_ctrl_t *ctrl);
65static int io_vcan_ctrl_stopped(const io_can_ctrl_t *ctrl);
66static int io_vcan_ctrl_restart(io_can_ctrl_t *ctrl);
67static int io_vcan_ctrl_get_bitrate(
68 const io_can_ctrl_t *ctrl, int *pnominal, int *pdata);
69static int io_vcan_ctrl_set_bitrate(io_can_ctrl_t *ctrl, int nominal, int data);
70static int io_vcan_ctrl_get_state(const io_can_ctrl_t *ctrl);
71
72// clang-format off
73static const struct io_can_ctrl_vtbl io_vcan_ctrl_vtbl = {
74 &io_vcan_ctrl_stop,
75 &io_vcan_ctrl_stopped,
76 &io_vcan_ctrl_restart,
77 &io_vcan_ctrl_get_bitrate,
78 &io_vcan_ctrl_set_bitrate,
79 &io_vcan_ctrl_get_state
80};
81// clang-format on
82
93 int flags;
94#if !LELY_NO_THREADS
99 mtx_t mtx;
104 cnd_t cond;
105#endif
111 int data;
113 int state;
115 struct sllist list;
116};
117
118static inline struct io_vcan_ctrl *io_vcan_ctrl_from_ctrl(
119 const io_can_ctrl_t *ctrl);
120
121static void io_vcan_ctrl_insert(io_can_ctrl_t *ctrl, io_can_chan_t *chan);
122static void io_vcan_ctrl_remove(io_can_ctrl_t *ctrl, io_can_chan_t *chan);
123
124static void io_vcan_ctrl_signal(struct spscring *ring, void *arg);
125
126static int io_vcan_ctrl_write(io_can_ctrl_t *ctrl, io_can_chan_t *chan,
127 const struct can_msg *msg, const struct can_err *err,
128 int timeout);
129
130static void io_vcan_ctrl_do_stop(struct io_vcan_ctrl *vcan_ctrl);
131
132static io_ctx_t *io_vcan_chan_dev_get_ctx(const io_dev_t *dev);
133static ev_exec_t *io_vcan_chan_dev_get_exec(const io_dev_t *dev);
134static size_t io_vcan_chan_dev_cancel(io_dev_t *dev, struct ev_task *task);
135static size_t io_vcan_chan_dev_abort(io_dev_t *dev, struct ev_task *task);
136
137// clang-format off
138static const struct io_dev_vtbl io_vcan_chan_dev_vtbl = {
139 &io_vcan_chan_dev_get_ctx,
140 &io_vcan_chan_dev_get_exec,
141 &io_vcan_chan_dev_cancel,
142 &io_vcan_chan_dev_abort
143};
144// clang-format on
145
146static io_dev_t *io_vcan_chan_get_dev(const io_can_chan_t *chan);
147static int io_vcan_chan_get_flags(const io_can_chan_t *chan);
148static int io_vcan_chan_read(io_can_chan_t *chan, struct can_msg *msg,
149 struct can_err *err, struct timespec *tp, int timeout);
150static void io_vcan_chan_submit_read(
151 io_can_chan_t *chan, struct io_can_chan_read *read);
152static int io_vcan_chan_write(
153 io_can_chan_t *chan, const struct can_msg *msg, int timeout);
154static void io_vcan_chan_submit_write(
155 io_can_chan_t *chan, struct io_can_chan_write *write);
156
157// clang-format off
158static const struct io_can_chan_vtbl io_vcan_chan_vtbl = {
159 &io_vcan_chan_get_dev,
160 &io_vcan_chan_get_flags,
161 &io_vcan_chan_read,
162 &io_vcan_chan_submit_read,
163 &io_vcan_chan_write,
164 &io_vcan_chan_submit_write
165};
166// clang-format on
167
168static void io_vcan_chan_svc_shutdown(struct io_svc *svc);
169
170// clang-format off
171static const struct io_svc_vtbl io_vcan_chan_svc_vtbl = {
172 NULL,
173 &io_vcan_chan_svc_shutdown
174};
175// clang-format on
176
180 const struct io_dev_vtbl *dev_vptr;
184 struct io_svc svc;
197#if !LELY_NO_THREADS
202 mtx_t mtx;
207 cnd_t cond;
208#endif
218 struct slnode node;
220 unsigned shutdown : 1;
222 unsigned stopped : 1;
224 unsigned read_posted : 1;
226 unsigned write_posted : 1;
233};
234
235static void io_vcan_chan_read_task_func(struct ev_task *task);
236static void io_vcan_chan_write_task_func(struct ev_task *task);
237
238static inline struct io_vcan_chan *io_vcan_chan_from_dev(const io_dev_t *dev);
239static inline struct io_vcan_chan *io_vcan_chan_from_chan(
240 const io_can_chan_t *chan);
241static inline struct io_vcan_chan *io_vcan_chan_from_svc(
242 const struct io_svc *svc);
243
244static void io_vcan_chan_signal(struct spscring *ring, void *arg);
245
246static void io_vcan_chan_do_pop(struct io_vcan_chan *vcan,
247 struct sllist *read_queue, struct sllist *write_queue,
248 struct ev_task *task);
249
250static size_t io_vcan_chan_do_abort_tasks(struct io_vcan_chan *vcan);
251
252void *
253io_vcan_ctrl_alloc(void)
254{
255 struct io_vcan_ctrl *vcan = malloc(sizeof(*vcan));
256 if (!vcan) {
257#if !LELY_NO_ERRNO
258 set_errc(errno2c(errno));
259#endif
260 return NULL;
261 }
262 // Suppress a GCC maybe-uninitialized warning.
263 vcan->ctrl_vptr = NULL;
264 // cppcheck-suppress memleak symbolName=vcan
265 return &vcan->ctrl_vptr;
266}
267
268void
269io_vcan_ctrl_free(void *ptr)
270{
271 if (ptr)
272 free(io_vcan_ctrl_from_ctrl(ptr));
273}
274
276io_vcan_ctrl_init(io_can_ctrl_t *ctrl, io_clock_t *clock, int flags,
277 int nominal, int data, int state)
278{
279 struct io_vcan_ctrl *vcan = io_vcan_ctrl_from_ctrl(ctrl);
280 assert(clock);
281
282 if (!nominal)
284#if !LELY_NO_CANFD
285 if (!data)
286 data = nominal;
287#else
288 (void)data;
289#endif
290
291#if !LELY_NO_THREADS
292 int errc = 0;
293#endif
294
295 vcan->ctrl_vptr = &io_vcan_ctrl_vtbl;
296
297 vcan->clock = clock;
298
299 vcan->flags = flags;
300
301#if !LELY_NO_THREADS
302 if (mtx_init(&vcan->mtx, mtx_plain) != thrd_success) {
303 errc = get_errc();
304 goto error_init_mtx;
305 }
306
307 if (cnd_init(&vcan->cond) != thrd_success) {
308 errc = get_errc();
309 goto error_init_cond;
310 }
311#endif
312
313 vcan->stopped = state == CAN_STATE_STOPPED;
314 vcan->nominal = nominal;
315 vcan->data = 0;
316#if !LELY_NO_CANFD
317 if (vcan->flags & IO_CAN_BUS_FLAG_BRS)
318 vcan->data = data;
319#endif
320 vcan->state = state;
321
322 sllist_init(&vcan->list);
323
324 return ctrl;
325
326#if !LELY_NO_THREADS
327 // cnd_destroy(&vcan->cond);
328error_init_cond:
329 mtx_destroy(&vcan->mtx);
330error_init_mtx:
331 set_errc(errc);
332 return NULL;
333#endif
334}
335
336void
337io_vcan_ctrl_fini(io_can_ctrl_t *ctrl)
338{
339 struct io_vcan_ctrl *vcan = io_vcan_ctrl_from_ctrl(ctrl);
340
341 assert(sllist_empty(&vcan->list));
342
343#if LELY_NO_THREADS
344 (void)vcan;
345#else
346 cnd_destroy(&vcan->cond);
347 mtx_destroy(&vcan->mtx);
348#endif
349}
350
353 io_clock_t *clock, int flags, int nominal, int data, int state)
354{
355 int errc = 0;
356
357 io_can_ctrl_t *ctrl = io_vcan_ctrl_alloc();
358 if (!ctrl) {
359 errc = get_errc();
360 goto error_alloc;
361 }
362
363 io_can_ctrl_t *tmp = io_vcan_ctrl_init(
364 ctrl, clock, flags, nominal, data, state);
365 if (!tmp) {
366 errc = get_errc();
367 goto error_init;
368 }
369 ctrl = tmp;
370
371 return ctrl;
372
373error_init:
374 io_vcan_ctrl_free((void *)ctrl);
375error_alloc:
376 set_errc(errc);
377 return NULL;
378}
379
380void
382{
383 if (ctrl) {
384 io_vcan_ctrl_fini(ctrl);
385 io_vcan_ctrl_free((void *)ctrl);
386 }
387}
388
389void
391{
392 struct io_vcan_ctrl *vcan = io_vcan_ctrl_from_ctrl(ctrl);
393
394#if !LELY_NO_THREADS
395 mtx_lock(&vcan->mtx);
396#endif
397 int changed = vcan->state != state;
398 if (changed) {
399 vcan->state = state;
400 if (!vcan->stopped && vcan->state == CAN_STATE_STOPPED)
401 io_vcan_ctrl_do_stop(vcan);
402 }
403#if !LELY_NO_THREADS
404 mtx_unlock(&vcan->mtx);
405#endif
406
407 // Send an error frame if the state of the CAN bus changed. We ignore
408 // write errors, since the user does not expect this operation to block.
409 if (changed && state != CAN_STATE_STOPPED
410 && (vcan->flags & IO_CAN_BUS_FLAG_ERR)) {
411 struct can_err err = { .state = state };
412 int errsv = get_errc();
413 if (io_vcan_ctrl_write_err(ctrl, &err, 0) == -1)
414 set_errc(errsv);
415 }
416}
417
418int
420 io_can_ctrl_t *ctrl, const struct can_msg *msg, int timeout)
421{
422 return io_vcan_ctrl_write(ctrl, NULL, msg, NULL, timeout);
423}
424
425int
427 io_can_ctrl_t *ctrl, const struct can_err *err, int timeout)
428{
429 return io_vcan_ctrl_write(ctrl, NULL, NULL, err, timeout);
430}
431
432void *
433io_vcan_chan_alloc(void)
434{
435 struct io_vcan_chan *vcan = malloc(sizeof(*vcan));
436 if (!vcan) {
437#if !LELY_NO_ERRNO
438 set_errc(errno2c(errno));
439#endif
440 return NULL;
441 }
442 // Suppress a GCC maybe-uninitialized warning.
443 vcan->chan_vptr = NULL;
444 // cppcheck-suppress memleak symbolName=vcan
445 return &vcan->chan_vptr;
446}
447
448void
449io_vcan_chan_free(void *ptr)
450{
451 if (ptr)
452 free(io_vcan_chan_from_chan(ptr));
453}
454
456io_vcan_chan_init(io_can_chan_t *chan, io_ctx_t *ctx, ev_exec_t *exec,
457 size_t rxlen)
458{
459 struct io_vcan_chan *vcan = io_vcan_chan_from_chan(chan);
460 assert(ctx);
461 assert(exec);
462
463 if (!rxlen)
464 rxlen = LELY_IO_VCAN_RXLEN;
465
466 int errc = 0;
467
468 vcan->dev_vptr = &io_vcan_chan_dev_vtbl;
469 vcan->chan_vptr = &io_vcan_chan_vtbl;
470
471 vcan->svc = (struct io_svc)IO_SVC_INIT(&io_vcan_chan_svc_vtbl);
472 vcan->ctx = ctx;
473
474 vcan->exec = exec;
475
476 vcan->read_task = (struct ev_task)EV_TASK_INIT(
477 vcan->exec, &io_vcan_chan_read_task_func);
478 vcan->write_task = (struct ev_task)EV_TASK_INIT(
479 vcan->exec, &io_vcan_chan_write_task_func);
480
481 spscring_init(&vcan->rxring, rxlen);
482 vcan->rxbuf = calloc(rxlen, sizeof(struct io_vcan_frame));
483 if (!vcan->rxbuf) {
484#if !LELY_NO_ERRNO
485 errc = errno2c(errno);
486#endif
487 goto error_alloc_rxbuf;
488 }
489
490#if !LELY_NO_THREADS
491 if (mtx_init(&vcan->mtx, mtx_plain) != thrd_success) {
492 errc = get_errc();
493 goto error_init_mtx;
494 }
495
496 if (cnd_init(&vcan->cond) != thrd_success) {
497 errc = get_errc();
498 goto error_init_cond;
499 }
500#endif
501
502 vcan->ctrl = NULL;
503 slnode_init(&vcan->node);
504
505 vcan->shutdown = 0;
506 vcan->stopped = 0;
507 vcan->read_posted = 0;
508 vcan->write_posted = 0;
509
510 sllist_init(&vcan->read_queue);
511 sllist_init(&vcan->write_queue);
512 vcan->current_write = NULL;
513
514 io_ctx_insert(vcan->ctx, &vcan->svc);
515
516 return chan;
517
518#if !LELY_NO_THREADS
519 // cnd_destroy(&vcan->cond);
520error_init_cond:
521 mtx_destroy(&vcan->mtx);
522error_init_mtx:
523#endif
524 free(vcan->rxbuf);
525error_alloc_rxbuf:
526 set_errc(errc);
527 return NULL;
528}
529
530void
531io_vcan_chan_fini(io_can_chan_t *chan)
532{
533 struct io_vcan_chan *vcan = io_vcan_chan_from_chan(chan);
534
535 // Close the CAN channel.
536 io_vcan_chan_close(chan);
537
538 io_ctx_remove(vcan->ctx, &vcan->svc);
539 // Cancel all pending tasks.
540 io_vcan_chan_svc_shutdown(&vcan->svc);
541
542 // Abort any consumer wait operation. Any producer wait operation was
543 // canceled when the channel was closed.
545
546#if !LELY_NO_THREADS
547 int warning = 0;
548 mtx_lock(&vcan->mtx);
549 // If necessary, busy-wait until io_vcan_chan_read_task_func() and
550 // io_vcan_chan_write_task_func() complete.
551 while (vcan->read_posted || vcan->write_posted) {
552 if (io_vcan_chan_do_abort_tasks(vcan))
553 continue;
554 mtx_unlock(&vcan->mtx);
555 if (!warning) {
556 warning = 1;
558 "io_vcan_chan_fini() invoked with pending operations");
559 }
560 thrd_yield();
561 mtx_lock(&vcan->mtx);
562 }
563 mtx_unlock(&vcan->mtx);
564
565 cnd_destroy(&vcan->cond);
566 mtx_destroy(&vcan->mtx);
567#endif
568
569 free(vcan->rxbuf);
570}
571
574{
575 int errc = 0;
576
577 io_can_chan_t *chan = io_vcan_chan_alloc();
578 if (!chan) {
579 errc = get_errc();
580 goto error_alloc;
581 }
582
583 io_can_chan_t *tmp = io_vcan_chan_init(chan, ctx, exec, rxlen);
584 if (!tmp) {
585 errc = get_errc();
586 goto error_init;
587 }
588 chan = tmp;
589
590 return chan;
591
592error_init:
593 io_vcan_chan_free((void *)chan);
594error_alloc:
595 set_errc(errc);
596 return NULL;
597}
598
599void
601{
602 if (chan) {
603 io_vcan_chan_fini(chan);
604 io_vcan_chan_free((void *)chan);
605 }
606}
607
610{
611 const struct io_vcan_chan *vcan = io_vcan_chan_from_chan(chan);
612
613#if !LELY_NO_THREADS
614 mtx_lock((mtx_t *)&vcan->mtx);
615#endif
616 io_can_ctrl_t *ctrl = vcan->ctrl;
617#if !LELY_NO_THREADS
618 mtx_unlock((mtx_t *)&vcan->mtx);
619#endif
620
621 return ctrl;
622}
623
624void
626{
627 io_vcan_chan_close(chan);
628 if (ctrl)
629 io_vcan_ctrl_insert(ctrl, chan);
630}
631
632int
634{
635 return io_vcan_chan_get_ctrl(chan) != NULL;
636}
637
638void
640{
642 if (ctrl)
643 io_vcan_ctrl_remove(ctrl, chan);
644}
645
646static int
647io_vcan_ctrl_stop(io_can_ctrl_t *ctrl)
648{
649 struct io_vcan_ctrl *vcan = io_vcan_ctrl_from_ctrl(ctrl);
650
651#if !LELY_NO_THREADS
652 mtx_lock(&vcan->mtx);
653#endif
654 io_vcan_ctrl_do_stop(vcan);
655#if !LELY_NO_THREADS
656 mtx_unlock(&vcan->mtx);
657#endif
658
659 return 0;
660}
661
662static int
663io_vcan_ctrl_stopped(const io_can_ctrl_t *ctrl)
664{
665 const struct io_vcan_ctrl *vcan = io_vcan_ctrl_from_ctrl(ctrl);
666
667#if !LELY_NO_THREADS
668 mtx_lock((mtx_t *)&vcan->mtx);
669#endif
670 int stopped = vcan->stopped;
671#if !LELY_NO_THREADS
672 mtx_unlock((mtx_t *)&vcan->mtx);
673#endif
674
675 return stopped;
676}
677
678static int
679io_vcan_ctrl_restart(io_can_ctrl_t *ctrl)
680{
681 struct io_vcan_ctrl *vcan_ctrl = io_vcan_ctrl_from_ctrl(ctrl);
682
683#if !LELY_NO_THREADS
684 mtx_lock(&vcan_ctrl->mtx);
685#endif
686 int stopped = vcan_ctrl->stopped;
687 if (stopped) {
688 vcan_ctrl->stopped = 0;
689 vcan_ctrl->state = CAN_STATE_ACTIVE;
690 // Update the state of each channel.
691 sllist_foreach (&vcan_ctrl->list, node) {
692 struct io_vcan_chan *vcan_chan = structof(
693 node, struct io_vcan_chan, node);
694#if !LELY_NO_THREADS
695 mtx_lock(&vcan_chan->mtx);
696#endif
697 vcan_chan->stopped = 0;
698#if !LELY_NO_THREADS
699 mtx_unlock(&vcan_chan->mtx);
700#endif
701 }
702 }
703#if !LELY_NO_THREADS
704 mtx_unlock(&vcan_ctrl->mtx);
705#endif
706
707 // Send an error frame if the CAN bus was restarted. We ignore write
708 // errors, since the user does not expect this operation to block.
709 if (stopped && (vcan_ctrl->flags & IO_CAN_BUS_FLAG_ERR)) {
710 struct can_err err = { .state = CAN_STATE_ACTIVE };
711 int errsv = get_errc();
712 if (io_vcan_ctrl_write_err(ctrl, &err, 0) == -1)
713 set_errc(errsv);
714 }
715
716 return 0;
717}
718
719static int
720io_vcan_ctrl_get_bitrate(const io_can_ctrl_t *ctrl, int *pnominal, int *pdata)
721{
722 const struct io_vcan_ctrl *vcan = io_vcan_ctrl_from_ctrl(ctrl);
723
724#if !LELY_NO_THREADS
725 mtx_lock((mtx_t *)&vcan->mtx);
726#endif
727 if (pnominal)
728 *pnominal = vcan->nominal;
729 if (pdata)
730 *pdata = vcan->data;
731#if !LELY_NO_THREADS
732 mtx_unlock((mtx_t *)&vcan->mtx);
733#endif
734
735 return 0;
736}
737
738static int
739io_vcan_ctrl_set_bitrate(io_can_ctrl_t *ctrl, int nominal, int data)
740{
741 struct io_vcan_ctrl *vcan = io_vcan_ctrl_from_ctrl(ctrl);
742
743#if !LELY_NO_THREADS
744 mtx_lock(&vcan->mtx);
745#endif
746 io_vcan_ctrl_do_stop(vcan);
747 vcan->nominal = nominal;
748#if !LELY_NO_CANFD
749 if (vcan->flags & IO_CAN_BUS_FLAG_BRS)
750 vcan->data = data;
751#else
752 (void)data;
753#endif
754#if !LELY_NO_THREADS
755 mtx_unlock(&vcan->mtx);
756#endif
757
758 return 0;
759}
760
761static int
762io_vcan_ctrl_get_state(const io_can_ctrl_t *ctrl)
763{
764 const struct io_vcan_ctrl *vcan = io_vcan_ctrl_from_ctrl(ctrl);
765
766#if !LELY_NO_THREADS
767 mtx_lock((mtx_t *)&vcan->mtx);
768#endif
769 int state = vcan->state;
770#if !LELY_NO_THREADS
771 mtx_unlock((mtx_t *)&vcan->mtx);
772#endif
773
774 return state;
775}
776
777static inline struct io_vcan_ctrl *
778io_vcan_ctrl_from_ctrl(const io_can_ctrl_t *ctrl)
779{
780 assert(ctrl);
781
782 return structof(ctrl, struct io_vcan_ctrl, ctrl_vptr);
783}
784
785static void
786io_vcan_ctrl_insert(io_can_ctrl_t *ctrl, io_can_chan_t *chan)
787{
788 struct io_vcan_ctrl *vcan_ctrl = io_vcan_ctrl_from_ctrl(ctrl);
789 struct io_vcan_chan *vcan_chan = io_vcan_chan_from_chan(chan);
790
791#if !LELY_NO_THREADS
792 mtx_lock(&vcan_ctrl->mtx);
793#endif
794 sllist_push_back(&vcan_ctrl->list, &vcan_chan->node);
795#if !LELY_NO_THREADS
796 mtx_lock(&vcan_chan->mtx);
797#endif
798 vcan_chan->ctrl = ctrl;
799 vcan_chan->stopped = vcan_ctrl->stopped;
800#if !LELY_NO_THREADS
801 mtx_unlock(&vcan_chan->mtx);
802#endif
803#if !LELY_NO_THREADS
804 mtx_unlock(&vcan_ctrl->mtx);
805#endif
806}
807
808static void
809io_vcan_ctrl_remove(io_can_ctrl_t *ctrl, io_can_chan_t *chan)
810{
811 struct io_vcan_ctrl *vcan_ctrl = io_vcan_ctrl_from_ctrl(ctrl);
812 struct io_vcan_chan *vcan_chan = io_vcan_chan_from_chan(chan);
813
814 struct sllist read_queue, write_queue;
815 sllist_init(&read_queue);
816 sllist_init(&write_queue);
817
818#if !LELY_NO_THREADS
819 mtx_lock(&vcan_ctrl->mtx);
820#endif
821 if (sllist_remove(&vcan_ctrl->list, &vcan_chan->node)) {
822 // Abort any wait operation started by io_vcan_ctrl_write().
823 spscring_p_abort_wait(&vcan_chan->rxring);
824#if !LELY_NO_THREADS
825 mtx_lock(&vcan_chan->mtx);
826#endif
827 vcan_chan->ctrl = NULL;
828 // Cancel all pending operations.
829 sllist_append(&read_queue, &vcan_chan->read_queue);
830 sllist_append(&write_queue, &vcan_chan->write_queue);
831 vcan_chan->current_write = NULL;
832#if !LELY_NO_THREADS
833 mtx_unlock(&vcan_chan->mtx);
834#endif
835 }
836#if !LELY_NO_THREADS
837 mtx_unlock(&vcan_ctrl->mtx);
838#endif
839
840 io_can_chan_read_queue_post(&read_queue, -1, errnum2c(ERRNUM_CANCELED));
841 io_can_chan_write_queue_post(&write_queue, errnum2c(ERRNUM_CANCELED));
842}
843
844static void
845io_vcan_ctrl_signal(struct spscring *ring, void *arg)
846{
847 (void)ring;
848 struct io_vcan_ctrl *vcan = arg;
849 assert(vcan);
850
851#if !LELY_NO_THREADS
852 mtx_lock(&vcan->mtx);
853#endif
854 // Post the write tasks of all channels, if necessary, to process any
855 // pending write operations.
856 sllist_foreach (&vcan->list, node) {
857 struct io_vcan_chan *chan =
858 structof(node, struct io_vcan_chan, node);
859#if !LELY_NO_THREADS
860 mtx_lock(&chan->mtx);
861#endif
862 if (!chan->write_posted && !sllist_empty(&chan->write_queue)) {
863 chan->write_posted = 1;
864 ev_exec_post(chan->write_task.exec, &chan->write_task);
865 }
866#if !LELY_NO_THREADS
867 mtx_unlock(&chan->mtx);
868#endif
869 }
870#if !LELY_NO_THREADS
871 // Wake up all threads waiting for this signal.
872 cnd_broadcast(&vcan->cond);
873 mtx_unlock(&vcan->mtx);
874#endif
875}
876
877static int
878io_vcan_ctrl_write(io_can_ctrl_t *ctrl, io_can_chan_t *chan,
879 const struct can_msg *msg, const struct can_err *err,
880 int timeout)
881{
882 struct io_vcan_ctrl *vcan_ctrl = io_vcan_ctrl_from_ctrl(ctrl);
883 assert(!msg || !err);
884
885 if (!msg && !err)
886 return 0;
887
888 struct timespec ts = { 0, 0 };
889 // Compute the absolute timeout for cnd_timedwait().
890#if !LELY_NO_THREADS
891 if (timeout > 0) {
892#if LELY_NO_TIMEOUT
893 timeout = 0;
894#else
895 if (!timespec_get(&ts, TIME_UTC))
896 return -1;
897 timespec_add_msec(&ts, timeout);
898#endif
899 }
900#endif
901
902#if !LELY_NO_CANFD
903 // Check if CAN FD frames are supported.
904 if (msg) {
905 int flags = 0;
906 if (msg->flags & CAN_FLAG_FDF)
907 flags |= IO_CAN_BUS_FLAG_FDF;
908 if (msg->flags & CAN_FLAG_BRS)
909 flags |= IO_CAN_BUS_FLAG_BRS;
910 if ((flags & vcan_ctrl->flags) != flags) {
912 return -1;
913 }
914 }
915#endif
916 // Check if error frames are supported.
917 if (err && !(vcan_ctrl->flags & IO_CAN_BUS_FLAG_ERR)) {
919 return -1;
920 }
921
922#if !LELY_NO_THREADS
923 mtx_lock(&vcan_ctrl->mtx);
924#endif
925
926 // Return the same error as SocketCAN when the controller is stopped.
927 if (vcan_ctrl->stopped) {
928#if !LELY_NO_THREADS
929 mtx_unlock(&vcan_ctrl->mtx);
930#endif
932 return -1;
933 }
934
935 // Check if all registered channels have a slot available in their
936 // receive queue.
937 for (;;) {
938 struct io_vcan_chan *vcan_chan = NULL;
939 int wouldblock = 0;
940 sllist_foreach (&vcan_ctrl->list, node) {
941 vcan_chan = structof(node, struct io_vcan_chan, node);
942 // Skip the sending channel.
943 if (chan && chan == &vcan_chan->chan_vptr)
944 continue;
945 // Try to allocate a single slot.
946 size_t n = 1;
947 spscring_p_alloc(&vcan_chan->rxring, &n);
948 if (!n) {
949 wouldblock = 1;
950 break;
951 }
952 }
953 // If all channels are ready, continue below with writing the
954 // frame.
955 if (!wouldblock)
956 break;
957 // Always submit a wait operation, even when timeout is 0, so
958 // pending write operations will be signaled once the blocked
959 // channel becomes ready.
960 // clang-format off
961 if (!spscring_p_submit_wait(&vcan_chan->rxring, 1,
962 &io_vcan_ctrl_signal, vcan_ctrl))
963 // clang-format on
964 // If the wait condition was already sastisfied, try
965 // again.
966 continue;
967 // Wait for the blocked channel to signal it is ready, or time
968 // out if that takes too long.
969 if (!timeout) {
970#if !LELY_NO_THREADS
971 mtx_unlock(&vcan_ctrl->mtx);
972#endif
974 return -1;
975 }
976#if !LELY_NO_THREADS
977 int result;
978#if !LELY_NO_TIMEOUT
979 if (timeout > 0)
980 result = cnd_timedwait(
981 &vcan_ctrl->cond, &vcan_ctrl->mtx, &ts);
982 else
983#endif
984 result = cnd_wait(&vcan_ctrl->cond, &vcan_ctrl->mtx);
985 if (result != thrd_success) {
986 mtx_unlock(&vcan_ctrl->mtx);
987#if !LELY_NO_TIMEOUT
988 if (result == thrd_timedout)
990#endif
991 return -1;
992 }
993#endif
994 }
995
996 // Obtain a timestamp for the CAN frame. We obtain it here, after
997 // waiting above, so the timestamp reflects the actual time the frame
998 // was put on the bus. This guarantees that all frames are ordered in
999 // time.
1000 if (io_clock_gettime(vcan_ctrl->clock, &ts) == -1) {
1001#if !LELY_NO_THREADS
1002 mtx_unlock(&vcan_ctrl->mtx);
1003#endif
1004 return -1;
1005 }
1006
1007 // Put the frame in the receive queue of every channel.
1008 sllist_foreach (&vcan_ctrl->list, node) {
1009 struct io_vcan_chan *vcan_chan =
1010 structof(node, struct io_vcan_chan, node);
1011 // Skip the sending channel.
1012 if (chan && chan == &vcan_chan->chan_vptr)
1013 continue;
1014
1015 size_t n = 1;
1016 size_t i = spscring_p_alloc(&vcan_chan->rxring, &n);
1017 // We did not release the mutex after checking that all channels
1018 // are ready, so this cannot fail.
1019 assert(n == 1);
1020 if (msg)
1021 vcan_chan->rxbuf[i] = (struct io_vcan_frame){
1022 .is_err = 0, .u.msg = *msg, .ts = ts
1023 };
1024 else
1025 vcan_chan->rxbuf[i] = (struct io_vcan_frame){
1026 .is_err = 1, .u.err = *err, .ts = ts
1027 };
1028 spscring_p_commit(&vcan_chan->rxring, n);
1029 }
1030
1031#if !LELY_NO_THREADS
1032 mtx_unlock(&vcan_ctrl->mtx);
1033#endif
1034
1035 return 0;
1036}
1037
1038static void
1039io_vcan_ctrl_do_stop(struct io_vcan_ctrl *vcan_ctrl)
1040{
1041 assert(vcan_ctrl);
1042
1043 vcan_ctrl->stopped = 1;
1044 vcan_ctrl->state = CAN_STATE_STOPPED;
1045
1046 struct sllist read_queue, write_queue;
1047 sllist_init(&read_queue);
1048 sllist_init(&write_queue);
1049
1050 sllist_foreach (&vcan_ctrl->list, node) {
1051 struct io_vcan_chan *vcan_chan =
1052 structof(node, struct io_vcan_chan, node);
1053#if !LELY_NO_THREADS
1054 mtx_lock(&vcan_chan->mtx);
1055#endif
1056 vcan_chan->stopped = 1;
1057 // Cancel all pending operations.
1058 sllist_append(&read_queue, &vcan_chan->read_queue);
1059 sllist_append(&write_queue, &vcan_chan->write_queue);
1060 vcan_chan->current_write = NULL;
1061#if !LELY_NO_THREADS
1062 mtx_unlock(&vcan_chan->mtx);
1063#endif
1064 }
1065
1066 io_can_chan_read_queue_post(&read_queue, -1, errnum2c(ERRNUM_NETDOWN));
1067 io_can_chan_write_queue_post(&write_queue, errnum2c(ERRNUM_NETDOWN));
1068}
1069
1070static io_ctx_t *
1071io_vcan_chan_dev_get_ctx(const io_dev_t *dev)
1072{
1073 const struct io_vcan_chan *vcan = io_vcan_chan_from_dev(dev);
1074
1075 return vcan->ctx;
1076}
1077
1078static ev_exec_t *
1079io_vcan_chan_dev_get_exec(const io_dev_t *dev)
1080{
1081 const struct io_vcan_chan *vcan = io_vcan_chan_from_dev(dev);
1082
1083 return vcan->exec;
1084}
1085
1086static size_t
1087io_vcan_chan_dev_cancel(io_dev_t *dev, struct ev_task *task)
1088{
1089 struct io_vcan_chan *vcan = io_vcan_chan_from_dev(dev);
1090
1091 size_t n = 0;
1092
1093 struct sllist read_queue, write_queue;
1094 sllist_init(&read_queue);
1095 sllist_init(&write_queue);
1096
1097#if !LELY_NO_THREADS
1098 mtx_lock(&vcan->mtx);
1099#endif
1100 io_vcan_chan_do_pop(vcan, &read_queue, &write_queue, task);
1101 // Mark the ongoing write operation as canceled, if necessary.
1102 if (vcan->current_write && (!task || task == vcan->current_write)) {
1103 vcan->current_write = NULL;
1104 n++;
1105 }
1106#if !LELY_NO_THREADS
1107 mtx_unlock(&vcan->mtx);
1108#endif
1109
1110 size_t nread = io_can_chan_read_queue_post(
1111 &read_queue, -1, errnum2c(ERRNUM_CANCELED));
1112 n = n < SIZE_MAX - nread ? n + nread : SIZE_MAX;
1113 size_t nwrite = io_can_chan_write_queue_post(
1114 &write_queue, errnum2c(ERRNUM_CANCELED));
1115 n = n < SIZE_MAX - nwrite ? n + nwrite : SIZE_MAX;
1116
1117 return n;
1118}
1119
1120static size_t
1121io_vcan_chan_dev_abort(io_dev_t *dev, struct ev_task *task)
1122{
1123 struct io_vcan_chan *vcan = io_vcan_chan_from_dev(dev);
1124
1125 struct sllist queue;
1126 sllist_init(&queue);
1127
1128#if !LELY_NO_THREADS
1129 mtx_lock(&vcan->mtx);
1130#endif
1131 io_vcan_chan_do_pop(vcan, &queue, &queue, task);
1132#if !LELY_NO_THREADS
1133 mtx_unlock(&vcan->mtx);
1134#endif
1135
1136 return ev_task_queue_abort(&queue);
1137}
1138
1139static io_dev_t *
1140io_vcan_chan_get_dev(const io_can_chan_t *chan)
1141{
1142 const struct io_vcan_chan *vcan = io_vcan_chan_from_chan(chan);
1143
1144 return &vcan->dev_vptr;
1145}
1146
1147static int
1148io_vcan_chan_get_flags(const io_can_chan_t *chan)
1149{
1150 const struct io_vcan_chan *vcan_chan = io_vcan_chan_from_chan(chan);
1151 const struct io_vcan_ctrl *vcan_ctrl =
1152 io_vcan_ctrl_from_ctrl(vcan_chan->ctrl);
1153
1154 return vcan_ctrl->flags;
1155}
1156
1157static int
1158io_vcan_chan_read(io_can_chan_t *chan, struct can_msg *msg, struct can_err *err,
1159 struct timespec *tp, int timeout)
1160{
1161 struct io_vcan_chan *vcan = io_vcan_chan_from_chan(chan);
1162
1163#if !LELY_NO_THREADS
1164 // Compute the absolute timeout for cnd_timedwait().
1165 struct timespec ts = { 0, 0 };
1166 if (timeout > 0) {
1167#if LELY_NO_TIMEOUT
1168 timeout = 0;
1169 (void)ts;
1170#else
1171 if (!timespec_get(&ts, TIME_UTC))
1172 return -1;
1173 timespec_add_msec(&ts, timeout);
1174#endif
1175 }
1176#endif
1177
1178#if !LELY_NO_THREADS
1179 mtx_lock(&vcan->mtx);
1180#endif
1181 size_t i = 0;
1182 for (;;) {
1183 // Check if a frame is available in the receive queue.
1184 size_t n = 1;
1185 i = spscring_c_alloc(&vcan->rxring, &n);
1186 if (n)
1187 break;
1188 // Return the same error as SocketCAN when the channel is
1189 // closed.
1190 if (!vcan->ctrl) {
1191#if !LELY_NO_THREADS
1192 mtx_unlock(&vcan->mtx);
1193#endif
1195 return -1;
1196 }
1197 // Return the same error as SocketCAN when the controller is
1198 // stopped.
1199 if (vcan->stopped) {
1200#if !LELY_NO_THREADS
1201 mtx_unlock(&vcan->mtx);
1202#endif
1204 return -1;
1205 }
1206 // Always submit a wait operation, even when timeout is 0, so
1207 // pending read operations will be signaled once a frame is
1208 // available.
1209 // clang-format off
1210 if (!spscring_c_submit_wait(&vcan->rxring, 1,
1211 &io_vcan_chan_signal, vcan))
1212 // clang-format on
1213 // If the wait condition was already satisfied, try
1214 // again.
1215 continue;
1216 // Wait for the buffer to signal that a frame is available, or
1217 // time out if that takes too long.
1218 if (!timeout) {
1219#if !LELY_NO_THREADS
1220 mtx_unlock(&vcan->mtx);
1221#endif
1223 return -1;
1224 }
1225#if !LELY_NO_THREADS
1226 int result;
1227#if !LELY_NO_TIMEOUT
1228 if (timeout > 0)
1229 result = cnd_timedwait(&vcan->cond, &vcan->mtx, &ts);
1230 else
1231#endif
1232 result = cnd_wait(&vcan->cond, &vcan->mtx);
1233 if (result != thrd_success) {
1234 mtx_unlock(&vcan->mtx);
1235#if !LELY_NO_TIMEOUT
1236 if (result == thrd_timedout)
1238#endif
1239 return -1;
1240 }
1241#endif
1242 }
1243 // Copy the frame from the buffer.
1244 struct io_vcan_frame *frame = &vcan->rxbuf[i];
1245 int is_err = frame->is_err;
1246 if (!is_err && msg)
1247 *msg = frame->u.msg;
1248 else if (is_err && err)
1249 *err = frame->u.err;
1250 if (tp)
1251 *tp = frame->ts;
1252 spscring_c_commit(&vcan->rxring, 1);
1253#if !LELY_NO_THREADS
1254 mtx_unlock(&vcan->mtx);
1255#endif
1256
1257 return !is_err;
1258}
1259
1260static void
1261io_vcan_chan_submit_read(io_can_chan_t *chan, struct io_can_chan_read *read)
1262{
1263 struct io_vcan_chan *vcan = io_vcan_chan_from_chan(chan);
1264 assert(read);
1265 struct ev_task *task = &read->task;
1266
1267 if (!task->exec)
1268 task->exec = vcan->exec;
1270
1271#if !LELY_NO_THREADS
1272 mtx_lock(&vcan->mtx);
1273#endif
1274 if (vcan->shutdown) {
1275#if !LELY_NO_THREADS
1276 mtx_unlock(&vcan->mtx);
1277#endif
1278 io_can_chan_read_post(read, -1, errnum2c(ERRNUM_CANCELED));
1279 } else if (!vcan->ctrl) {
1280#if !LELY_NO_THREADS
1281 mtx_unlock(&vcan->mtx);
1282#endif
1283 io_can_chan_read_post(read, -1, errnum2c(ERRNUM_BADF));
1284 } else if (vcan->stopped) {
1285#if !LELY_NO_THREADS
1286 mtx_unlock(&vcan->mtx);
1287#endif
1288 io_can_chan_read_post(read, -1, errnum2c(ERRNUM_NETDOWN));
1289 } else {
1290 int post_read = !vcan->read_posted
1291 && sllist_empty(&vcan->read_queue);
1292 sllist_push_back(&vcan->read_queue, &task->_node);
1293 if (post_read)
1294 vcan->read_posted = 1;
1295#if !LELY_NO_THREADS
1296 mtx_unlock(&vcan->mtx);
1297#endif
1298 // cppcheck-suppress duplicateCondition
1299 if (post_read)
1300 ev_exec_post(vcan->read_task.exec, &vcan->read_task);
1301 }
1302}
1303
1304static int
1305io_vcan_chan_write(io_can_chan_t *chan, const struct can_msg *msg, int timeout)
1306{
1308 if (!ctrl) {
1310 return -1;
1311 }
1312 return io_vcan_ctrl_write(ctrl, chan, msg, NULL, timeout);
1313}
1314
1315static void
1316io_vcan_chan_submit_write(io_can_chan_t *chan, struct io_can_chan_write *write)
1317{
1318 struct io_vcan_chan *vcan = io_vcan_chan_from_chan(chan);
1319 assert(write);
1320 struct ev_task *task = &write->task;
1321
1322 if (!task->exec)
1323 task->exec = vcan->exec;
1325
1326#if !LELY_NO_THREADS
1327 mtx_lock(&vcan->mtx);
1328#endif
1329 if (vcan->shutdown) {
1330#if !LELY_NO_THREADS
1331 mtx_unlock(&vcan->mtx);
1332#endif
1333 io_can_chan_write_post(write, errnum2c(ERRNUM_CANCELED));
1334 } else if (!vcan->ctrl) {
1335#if !LELY_NO_THREADS
1336 mtx_unlock(&vcan->mtx);
1337#endif
1338 io_can_chan_write_post(write, errnum2c(ERRNUM_BADF));
1339 } else if (vcan->stopped) {
1340#if !LELY_NO_THREADS
1341 mtx_unlock(&vcan->mtx);
1342#endif
1343 io_can_chan_write_post(write, errnum2c(ERRNUM_NETDOWN));
1344 } else {
1345 int post_write = !vcan->write_posted
1346 && sllist_empty(&vcan->write_queue);
1347 sllist_push_back(&vcan->write_queue, &task->_node);
1348 if (post_write)
1349 vcan->write_posted = 1;
1350#if !LELY_NO_THREADS
1351 mtx_unlock(&vcan->mtx);
1352#endif
1353 // cppcheck-suppress duplicateCondition
1354 if (post_write)
1355 ev_exec_post(vcan->write_task.exec, &vcan->write_task);
1356 }
1357}
1358
1359static void
1360io_vcan_chan_svc_shutdown(struct io_svc *svc)
1361{
1362 struct io_vcan_chan *vcan = io_vcan_chan_from_svc(svc);
1363 io_dev_t *dev = &vcan->dev_vptr;
1364
1365#if !LELY_NO_THREADS
1366 mtx_lock(&vcan->mtx);
1367#endif
1368 int shutdown = !vcan->shutdown;
1369 vcan->shutdown = 1;
1370 if (shutdown)
1371 // Try to abort io_vcan_chan_read_task_func() and
1372 // io_vcan_chan_write_task_func().
1373 io_vcan_chan_do_abort_tasks(vcan);
1374#if !LELY_NO_THREADS
1375 mtx_unlock(&vcan->mtx);
1376#endif
1377 // cppcheck-suppress duplicateCondition
1378 if (shutdown)
1379 // Cancel all pending operations.
1380 io_vcan_chan_dev_cancel(dev, NULL);
1381}
1382
1383static void
1384io_vcan_chan_read_task_func(struct ev_task *task)
1385{
1386 assert(task);
1387 struct io_vcan_chan *vcan =
1388 structof(task, struct io_vcan_chan, read_task);
1389
1390#if !LELY_NO_THREADS
1391 mtx_lock(&vcan->mtx);
1392#endif
1393 vcan->read_posted = 0;
1394 // Try to process all pending read operations at once.
1395 while ((task = ev_task_from_node(sllist_first(&vcan->read_queue)))) {
1396 // Check if a frame is available in the receive queue.
1397 size_t n = 1;
1398 size_t i = spscring_c_alloc(&vcan->rxring, &n);
1399 if (!n) {
1400 // If the queue is empty, register a wait operation and
1401 // return.
1402 // clang-format off
1403 if (!spscring_c_submit_wait(&vcan->rxring, 1,
1404 io_vcan_chan_signal, vcan))
1405 // clang-format on
1406 continue;
1407 break;
1408 }
1410 struct io_vcan_frame *frame = &vcan->rxbuf[i];
1411
1412 struct io_can_chan_read *read =
1414 if (!frame->is_err && read->msg)
1415 *read->msg = frame->u.msg;
1416 if (frame->is_err && read->err)
1417 *read->err = frame->u.err;
1418 if (read->tp)
1419 *read->tp = frame->ts;
1420 io_can_chan_read_post(read, !frame->is_err, 0);
1421
1422 spscring_c_commit(&vcan->rxring, 1);
1423 }
1424#if !LELY_NO_THREADS
1425 mtx_unlock(&vcan->mtx);
1426#endif
1427}
1428
1429static void
1430io_vcan_chan_write_task_func(struct ev_task *task)
1431{
1432 assert(task);
1433 struct io_vcan_chan *vcan =
1434 structof(task, struct io_vcan_chan, write_task);
1435
1436 int errsv = get_errc();
1437
1438 struct io_can_chan_write *write = NULL;
1439 int wouldblock = 0;
1440
1441#if !LELY_NO_THREADS
1442 mtx_lock(&vcan->mtx);
1443#endif
1444 vcan->write_posted = 0;
1445 // Try to process all pending write operations at once.
1446 while ((task = vcan->current_write = ev_task_from_node(
1447 sllist_pop_front(&vcan->write_queue)))) {
1449#if !LELY_NO_THREADS
1450 mtx_unlock(&vcan->mtx);
1451#endif
1452 // Perform a non-blocking write.
1453 int result = io_vcan_chan_write(
1454 &vcan->chan_vptr, write->msg, 0);
1455 int errc = !result ? 0 : get_errc();
1456 wouldblock = errc == errnum2c(ERRNUM_AGAIN)
1458 if (!wouldblock)
1459 // The operation succeeded or failed immediately.
1460 io_can_chan_write_post(write, errc);
1461#if !LELY_NO_THREADS
1462 mtx_lock(&vcan->mtx);
1463#endif
1464 if (task == vcan->current_write) {
1465 // Put the write operation back on the queue if it would
1466 // block, unless it was canceled.
1467 if (wouldblock) {
1469 &task->_node);
1470 task = NULL;
1471 }
1472 vcan->current_write = NULL;
1473 }
1474 assert(!vcan->current_write);
1475 // Stop if the operation would block, or this task has been
1476 // reposted.
1477 if (wouldblock || vcan->write_posted)
1478 break;
1479 }
1480 int post_write = !vcan->write_posted
1481 && !sllist_empty(&vcan->write_queue) && !vcan->shutdown;
1482 if (post_write)
1483 vcan->write_posted = 1;
1484#if !LELY_NO_THREADS
1485 mtx_unlock(&vcan->mtx);
1486#endif
1487
1488 if (task && wouldblock)
1489 // The operation would block but was canceled before it could be
1490 // requeued.
1491 io_can_chan_write_post(io_can_chan_write_from_task(task),
1493
1494 if (post_write)
1495 ev_exec_post(vcan->write_task.exec, &vcan->write_task);
1496
1497 set_errc(errsv);
1498}
1499
1500static inline struct io_vcan_chan *
1501io_vcan_chan_from_dev(const io_dev_t *dev)
1502{
1503 assert(dev);
1504
1505 return structof(dev, struct io_vcan_chan, dev_vptr);
1506}
1507
1508static inline struct io_vcan_chan *
1509io_vcan_chan_from_chan(const io_can_chan_t *chan)
1510{
1511 assert(chan);
1512
1513 return structof(chan, struct io_vcan_chan, chan_vptr);
1514}
1515
1516static inline struct io_vcan_chan *
1517io_vcan_chan_from_svc(const struct io_svc *svc)
1518{
1519 assert(svc);
1520
1521 return structof(svc, struct io_vcan_chan, svc);
1522}
1523
1524static void
1525io_vcan_chan_signal(struct spscring *ring, void *arg)
1526{
1527 (void)ring;
1528 struct io_vcan_chan *vcan = arg;
1529 assert(vcan);
1530
1531#if !LELY_NO_THREADS
1532 mtx_lock(&vcan->mtx);
1533#endif
1534 int post_read = !vcan->read_posted && !sllist_empty(&vcan->read_queue);
1535 if (post_read)
1536 vcan->read_posted = 1;
1537#if !LELY_NO_THREADS
1538 cnd_broadcast(&vcan->cond);
1539 mtx_unlock(&vcan->mtx);
1540#endif
1541 // cppcheck-suppress duplicateCondition
1542 if (post_read)
1543 ev_exec_post(vcan->read_task.exec, &vcan->read_task);
1544}
1545
1546static void
1547io_vcan_chan_do_pop(struct io_vcan_chan *vcan, struct sllist *read_queue,
1548 struct sllist *write_queue, struct ev_task *task)
1549{
1550 assert(vcan);
1551 assert(read_queue);
1552 assert(write_queue);
1553
1554 if (!task) {
1557 } else if (sllist_remove(&vcan->read_queue, &task->_node)) {
1558 sllist_push_back(read_queue, &task->_node);
1559 } else if (sllist_remove(&vcan->write_queue, &task->_node)) {
1560 sllist_push_back(write_queue, &task->_node);
1561 }
1562}
1563
1564static size_t
1565io_vcan_chan_do_abort_tasks(struct io_vcan_chan *vcan)
1566{
1567 assert(vcan);
1568
1569 size_t n = 0;
1570
1571 // Try to abort io_vcan_chan_read_task_func().
1572 // clang-format off
1573 if (vcan->read_posted && ev_exec_abort(vcan->read_task.exec,
1574 &vcan->read_task)) {
1575 // clang-format on
1576 vcan->read_posted = 0;
1577 n++;
1578 }
1579
1580 // Try to abort io_vcan_chan_write_task_func().
1581 // clang-format off
1582 if (vcan->write_posted && ev_exec_abort(vcan->write_task.exec,
1583 &vcan->write_task)) {
1584 // clang-format on
1585 vcan->write_posted = 0;
1586 n++;
1587 }
1588
1589 return n;
1590}
1591
1592#endif // !LELY_NO_MALLOC
@ CAN_STATE_ACTIVE
The error active state (TX/RX error count < 128).
Definition err.h:30
@ CAN_FLAG_FDF
The FD Format (FDF) flag, formerly known as Extended Data Length (EDL).
Definition msg.h:54
@ CAN_FLAG_BRS
The Bit Rate Switch (BRS) flag (only available in CAN FD format frames).
Definition msg.h:62
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
void diag(enum diag_severity severity, int errc, const char *format,...)
Emits a diagnostic message.
Definition diag.c:171
int errnum2c(errnum_t errnum)
Transforms a platform-independent error number to a native error code.
Definition errnum.c:810
@ ERRNUM_WOULDBLOCK
Operation would block.
Definition errnum.h:233
@ ERRNUM_NETDOWN
Network is down.
Definition errnum.h:151
@ ERRNUM_BADF
Bad file descriptor.
Definition errnum.h:93
@ ERRNUM_INVAL
Invalid argument.
Definition errnum.h:132
@ 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
size_t ev_exec_abort(ev_exec_t *exec, struct ev_task *task)
Aborts the specified task submitted to *exec, if it has not yet begun executing, or all pending tasks...
Definition exec.h:136
void ev_exec_post(ev_exec_t *exec, struct ev_task *task)
Submits *task to *exec for execution.
Definition exec.h:124
void ev_exec_on_task_init(ev_exec_t *exec)
Indicates to the specified executor that a task will be submitted for execution in the future.
Definition exec.h:106
const struct ev_exec_vtbl *const ev_exec_t
An abstract task executor.
Definition ev.h:29
const struct io_can_chan_vtbl *const io_can_chan_t
An abstract CAN channel.
Definition can.h:59
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
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
const struct io_can_ctrl_vtbl *const io_can_ctrl_t
An abstract CAN controller.
Definition can.h:56
@ IO_CAN_BUS_FLAG_BRS
Bit Rate Switch support is enabled.
Definition can.h:44
@ IO_CAN_BUS_FLAG_FDF
FD Format (formerly Extended Data Length) support is enabled.
Definition can.h:42
@ IO_CAN_BUS_FLAG_ERR
Reception of error frames is enabled.
Definition can.h:39
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
const struct io_dev_vtbl *const io_dev_t
An abstract I/O device.
Definition dev.h:35
int timespec_get(struct timespec *ts, int base)
Sets the interval at ts to hold the current calendar time based on the specified time base.
Definition time.c:32
#define TIME_UTC
An integer constant greater than 0 that designates the UTC time base.
Definition time.h:207
void sllist_init(struct sllist *list)
Initializes a singly-linked list.
Definition sllist.h:194
struct sllist * sllist_append(struct sllist *dst, struct sllist *src)
Appends the singly-linked list at src to the one at dst.
Definition sllist.h:257
void sllist_push_front(struct sllist *list, struct slnode *node)
Pushes a node to the front of a singly-linked list.
Definition sllist.h:221
#define sllist_foreach(list, node)
Iterates in order over each node in a singly-linked list.
Definition sllist.h:183
struct slnode * sllist_remove(struct sllist *list, struct slnode *node)
Removes a node from a singly-linked list.
Definition sllist.c:46
void sllist_push_back(struct sllist *list, struct slnode *node)
Pushes a node to the back of a singly-linked list.
Definition sllist.h:232
void slnode_init(struct slnode *node)
Initializes a node in a singly-linked list.
Definition sllist.h:186
int sllist_empty(const struct sllist *list)
Returns 1 if the singly-linked list is empty, and 0 if not.
Definition sllist.h:202
struct slnode * sllist_pop_front(struct sllist *list)
Pops a node from the front of a singly-linked list.
Definition sllist.h:243
struct slnode * sllist_first(const struct sllist *list)
Returns a pointer to the first node in a singly-linked list.
Definition sllist.h:271
This header file is part of the utilities library; it contains the single-producer,...
int spscring_p_abort_wait(struct spscring *ring)
Aborts a wait operation previously registered with spscring_p_submit_wait().
Definition spscring.c:204
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
int spscring_p_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:163
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
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 CAN bus operation queue functions.
This header file is part of the C11 and POSIX compatibility library; it includes <stdint....
This header file is part of the C11 and POSIX compatibility library; it includes <stdlib....
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
A CAN or CAN FD format frame.
Definition msg.h:87
uint_least8_t flags
The flags (any combination of CAN_FLAG_IDE, CAN_FLAG_RTR, CAN_FLAG_FDF, CAN_FLAG_BRS and CAN_FLAG_ESI...
Definition msg.h:94
An executable task.
Definition task.h:41
ev_exec_t * exec
A pointer to the executor to which the task is (to be) submitted.
Definition task.h:43
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 timespec * tp
The address at which to store the system time at which the CAN frame or CAN error frame was received.
Definition can.h:93
struct can_msg * msg
The address at which to store the CAN frame.
Definition can.h:80
struct can_err * err
The address at which to store the CAN error frame.
Definition can.h:86
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
Definition ctx.c:38
The virtual table of an I/O service.
Definition ctx.h:67
An I/O service.
Definition ctx.h:49
The implementation of a virtual CAN channel.
Definition vcan.c:178
struct io_vcan_frame * rxbuf
The receive queue.
Definition vcan.c:196
struct ev_task read_task
The task responsible for initiating read operations.
Definition vcan.c:190
unsigned shutdown
A flag indicating whether the I/O service has been shut down.
Definition vcan.c:220
cnd_t cond
The condition variable used to wake up blocked synchronous read operations.
Definition vcan.c:207
struct sllist read_queue
The queue containing pending read operations.
Definition vcan.c:228
struct sllist write_queue
The queue containing pending write operations.
Definition vcan.c:230
unsigned read_posted
A flag indicating whether read_task has been posted to exec.
Definition vcan.c:224
struct io_svc svc
The I/O service representing the channel.
Definition vcan.c:184
struct slnode node
The node of the channel in the list of the virtual CAN controller.
Definition vcan.c:218
io_ctx_t * ctx
A pointer to the I/O context with which the channel is registered.
Definition vcan.c:186
io_can_ctrl_t * ctrl
A pointer to the virtual CAN controller with which this channel is registered.
Definition vcan.c:213
mtx_t mtx
The mutex protecting the channel and the queues of pending operations.
Definition vcan.c:202
unsigned stopped
A flag indicating the virtual CAN controller is stopped.
Definition vcan.c:222
struct spscring rxring
The ring buffer used to control the receive queue.
Definition vcan.c:194
const struct io_can_chan_vtbl * chan_vptr
A pointer to the virtual table for the CAN channel interface.
Definition vcan.c:182
unsigned write_posted
A flag indicating whether write_task has been posted to exec.
Definition vcan.c:226
const struct io_dev_vtbl * dev_vptr
A pointer to the virtual table for the I/O device interface.
Definition vcan.c:180
struct ev_task * current_write
The write operation currently being executed.
Definition vcan.c:232
ev_exec_t * exec
A pointer to the executor used to execute all I/O tasks.
Definition vcan.c:188
struct ev_task write_task
The task responsible for initiating write operations.
Definition vcan.c:192
The implementation of a virtual CAN controller.
Definition vcan.c:84
int state
The state of the virtual CAN bus.
Definition vcan.c:113
int flags
The flags specifying which CAN bus features are enabled.
Definition vcan.c:93
mtx_t mtx
The mutex protecting the controller and the list of virtual CAN channels.
Definition vcan.c:99
int nominal
The nominal bitrate.
Definition vcan.c:109
int data
The data bitrate.
Definition vcan.c:111
int stopped
A flag indicating whether the controller is stopped.
Definition vcan.c:107
cnd_t cond
The condition variable used to wake up blocked synchronous write operations.
Definition vcan.c:104
io_clock_t * clock
A pointer to the clock used to obtain the timestamp when sending CAN frames.
Definition vcan.c:91
const struct io_can_ctrl_vtbl * ctrl_vptr
A pointer to the virtual table for the CAN controller interface.
Definition vcan.c:86
struct sllist list
The list of registered virtual CAN channels.
Definition vcan.c:115
A singly-linked list.
Definition sllist.h:52
A node in a singly-linked list.
Definition sllist.h:40
A single-producer, single-consumer ring buffer.
Definition spscring.h:63
A time type with nanosecond resolution.
Definition time.h:88
size_t ev_task_queue_abort(struct sllist *queue)
Aborts the tasks in queue by invoking ev_exec_on_task_fini() for each of them.
Definition task.c:55
struct ev_task * ev_task_from_node(struct slnode *node)
Converts a pointer to a node in a queue to the address of the task containing the node.
Definition task.c:32
#define EV_TASK_INIT(exec, func)
The static initializer for ev_task.
Definition task.h:53
This header file is part of the C11 and POSIX compatibility library; it includes <threads....
int cnd_init(cnd_t *cond)
Creates a condition variable.
int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts)
Atomically unlocks the mutex at mtx and endeavors to block until the condition variable at cond is si...
int cnd_broadcast(cnd_t *cond)
Unblocks all of the threads that are blocked on the condition variable at cond at the time of the cal...
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 cnd_destroy(cnd_t *cond)
Releases all resources used by the condition variable at cond.
int cnd_wait(cnd_t *cond, mtx_t *mtx)
Atomically unlocks the mutex at mtx and endeavors to block until the condition variable at cond is si...
void thrd_yield(void)
Endeavors to permit other threads to run, even if the current thread would ordinarily continue to run...
@ thrd_timedout
Indicates that the time specified in the call was reached without acquiring the requested resource.
Definition threads.h:128
@ thrd_success
Indicates that the requested operation succeeded.
Definition threads.h:121
int mtx_unlock(mtx_t *mtx)
Unlocks the mutex at mtx.
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
This header file is part of the utilities library; it contains the time function declarations.
void timespec_add_msec(struct timespec *tp, uint_least64_t msec)
Adds msec milliseconds to the time at tp.
Definition time.h:141
void io_vcan_chan_destroy(io_can_chan_t *chan)
Destroys a virtual CAN channel.
Definition vcan.c:600
void io_vcan_ctrl_set_state(io_can_ctrl_t *ctrl, int state)
Sets the state of a virtual CAN bus: one of CAN_STATE_ACTIVE, CAN_STATE_PASSIVE, CAN_STATE_BUSOFF,...
Definition vcan.c:390
io_can_ctrl_t * io_vcan_ctrl_create(io_clock_t *clock, int flags, int nominal, int data, int state)
Creates a new virtual CAN controller.
Definition vcan.c:352
#define LELY_IO_VCAN_BITRATE
The default bitrate of a virtual CAN bus.
Definition vcan.c:44
void io_vcan_chan_close(io_can_chan_t *chan)
Closes a virtual CAN channel.
Definition vcan.c:639
int io_vcan_ctrl_write_err(io_can_ctrl_t *ctrl, const struct can_err *err, int timeout)
Writes a CAN error frame to a all virtual CAN channels registered with a virtual CAN controller.
Definition vcan.c:426
void io_vcan_chan_open(io_can_chan_t *chan, io_can_ctrl_t *ctrl)
Opens a virtual CAN channel by registering it with the specified virtual CAN controller.
Definition vcan.c:625
int io_vcan_chan_is_open(const io_can_chan_t *chan)
Returns 1 if the CAN channel is open and 0 if not.
Definition vcan.c:633
#define LELY_IO_VCAN_RXLEN
The default receive queue length (in number of CAN frames) of a vritual CAN channel.
Definition vcan.c:52
int io_vcan_ctrl_write_msg(io_can_ctrl_t *ctrl, const struct can_msg *msg, int timeout)
Writes a CAN frame to a all virtual CAN channels registered with a virtual CAN controller.
Definition vcan.c:419
io_can_chan_t * io_vcan_chan_create(io_ctx_t *ctx, ev_exec_t *exec, size_t rxlen)
Creates a new virtual CAN channel.
Definition vcan.c:573
io_can_ctrl_t * io_vcan_chan_get_ctrl(const io_can_chan_t *chan)
Returns a pointer to the virtual CAN controller with which a virtual CAN channel is registered,...
Definition vcan.c:609
void io_vcan_ctrl_destroy(io_can_ctrl_t *ctrl)
Destroys a virtual CAN controller.
Definition vcan.c:381
This header file is part of the I/O library; it contains the virtual CAN bus declarations.