Lely core libraries 2.3.4
ixxat.c
Go to the documentation of this file.
1
24#include "io.h"
25
26#if !LELY_NO_STDIO && _WIN32 && LELY_HAVE_IXXAT
27
28// Rename error flags to avoid conflicts with definitions in <cantype.h>.
29#define CAN_ERROR_BIT LELY_IO_CAN_ERROR_BIT
30#define CAN_ERROR_STUFF LELY_IO_CAN_ERROR_STUFF
31#define CAN_ERROR_CRC LELY_IO_CAN_ERROR_CRC
32#define CAN_ERROR_FORM LELY_IO_CAN_ERROR_FORM
33#define CAN_ERROR_ACK LELY_IO_CAN_ERROR_ACK
34#define CAN_ERROR_OTHER LELY_IO_CAN_ERROR_OTHER
35
36#include "../can.h"
37#include <lely/io2/ctx.h>
39#include <lely/util/diag.h>
40#include <lely/util/endian.h>
41#include <lely/util/util.h>
42
43#undef CAN_ERROR_OTHER
44#undef CAN_ERROR_ACK
45#undef CAN_ERROR_FORM
46#undef CAN_ERROR_CRC
47#undef CAN_ERROR_STUFF
48#undef CAN_ERROR_BIT
49
50#include <assert.h>
51#include <stdlib.h>
52#include <string.h>
53
54#include <processthreadsapi.h>
55#include <windows.h>
56
57#ifdef __MINGW32__
58#pragma GCC diagnostic push
59#pragma GCC diagnostic ignored "-Wpedantic"
60#pragma GCC diagnostic ignored "-Wunknown-pragmas"
61#define _MSC_VER 1900
62#endif
63
64#if LELY_NO_CANFD
65#include <vcinpl.h>
66#else
67#include <vcinpl2.h>
68#endif
69#ifdef __MINGW32__
70#undef _MSC_VER
71#pragma GCC diagnostic pop
72#endif
73
74#ifndef LELY_IO_IXXAT_RX_FIFO_SIZE
79#define LELY_IO_IXXAT_RX_FIFO_SIZE 1024
80#endif
81
82#ifndef LELY_IO_IXXAT_TX_FIFO_SIZE
87#define LELY_IO_IXXAT_TX_FIFO_SIZE 128
88#endif
89
90static size_t io_ixxat_init_refcnt = 0;
91
92static HMODULE hLibModule;
93
94#define LELY_IO_DEFINE_IXXAT(name) static PF_##name p_##name;
95#include "ixxat.inc"
96#undef LELY_IO_DEFINE_IXXAT
97
98static int io_ixxat_read(HANDLE hCanChn, struct can_msg *msg,
99 struct can_err *err, PUINT32 pdwTime, PUINT32 pdwTimeOvr,
100 int timeout);
101static int io_ixxat_write(
102 HANDLE hCanChn, const struct can_msg *msg, int timeout);
103static DWORD io_ixxat_error(HRESULT hResult);
104static int io_ixxat_state(UINT32 dwStatus);
105static struct timespec io_ixxat_time(
106 UINT64 qwTime, UINT32 dwTscClkFreq, UINT32 dwTscDivisor);
107
108static int io_ixxat_ctrl_stop(io_can_ctrl_t *ctrl);
109static int io_ixxat_ctrl_stopped(const io_can_ctrl_t *ctrl);
110static int io_ixxat_ctrl_restart(io_can_ctrl_t *ctrl);
111static int io_ixxat_ctrl_get_bitrate(
112 const io_can_ctrl_t *ctrl, int *pnominal, int *pdata);
113static int io_ixxat_ctrl_set_bitrate(
114 io_can_ctrl_t *ctrl, int nominal, int data);
115static int io_ixxat_ctrl_get_state(const io_can_ctrl_t *ctrl);
116
117// clang-format off
118static const struct io_can_ctrl_vtbl io_ixxat_ctrl_vtbl = {
119 &io_ixxat_ctrl_stop,
120 &io_ixxat_ctrl_stopped,
121 &io_ixxat_ctrl_restart,
122 &io_ixxat_ctrl_get_bitrate,
123 &io_ixxat_ctrl_set_bitrate,
124 &io_ixxat_ctrl_get_state
125};
126// clang-format on
127
128struct io_ixxat_ctrl {
129 const struct io_can_ctrl_vtbl *ctrl_vptr;
130 HANDLE hDevice;
131 UINT32 dwCanNo;
132 HANDLE hCanCtl;
133 int flags;
134 UINT32 dwTscClkFreq;
135 UINT32 dwTscDivisor;
136};
137
138static inline struct io_ixxat_ctrl *io_ixxat_ctrl_from_ctrl(
139 const io_can_ctrl_t *ctrl);
140
141static io_ctx_t *io_ixxat_chan_dev_get_ctx(const io_dev_t *dev);
142static ev_exec_t *io_ixxat_chan_dev_get_exec(const io_dev_t *dev);
143static size_t io_ixxat_chan_dev_cancel(io_dev_t *dev, struct ev_task *task);
144static size_t io_ixxat_chan_dev_abort(io_dev_t *dev, struct ev_task *task);
145
146// clang-format off
147static const struct io_dev_vtbl io_ixxat_chan_dev_vtbl = {
148 &io_ixxat_chan_dev_get_ctx,
149 &io_ixxat_chan_dev_get_exec,
150 &io_ixxat_chan_dev_cancel,
151 &io_ixxat_chan_dev_abort
152};
153// clang-format on
154
155static io_dev_t *io_ixxat_chan_get_dev(const io_can_chan_t *chan);
156static int io_ixxat_chan_get_flags(const io_can_chan_t *chan);
157static int io_ixxat_chan_read(io_can_chan_t *chan, struct can_msg *msg,
158 struct can_err *err, struct timespec *tp, int timeout);
159static void io_ixxat_chan_submit_read(
160 io_can_chan_t *chan, struct io_can_chan_read *read);
161static int io_ixxat_chan_write(
162 io_can_chan_t *chan, const struct can_msg *msg, int timeout);
163static void io_ixxat_chan_submit_write(
164 io_can_chan_t *chan, struct io_can_chan_write *write);
165
166// clang-format off
167static const struct io_can_chan_vtbl io_ixxat_chan_vtbl = {
168 &io_ixxat_chan_get_dev,
169 &io_ixxat_chan_get_flags,
170 &io_ixxat_chan_read,
171 &io_ixxat_chan_submit_read,
172 &io_ixxat_chan_write,
173 &io_ixxat_chan_submit_write
174};
175// clang-format on
176
177static void io_ixxat_chan_svc_shutdown(struct io_svc *svc);
178
179// clang-format off
180static const struct io_svc_vtbl io_ixxat_chan_svc_vtbl = {
181 NULL,
182 &io_ixxat_chan_svc_shutdown
183};
184// clang-format on
185
186struct io_ixxat_chan {
187 const struct io_dev_vtbl *dev_vptr;
188 const struct io_can_chan_vtbl *chan_vptr;
189 struct io_svc svc;
190 io_ctx_t *ctx;
191 ev_exec_t *exec;
192 int rxtimeo;
193 int txtimeo;
194 struct ev_task read_task;
195 struct ev_task write_task;
196#if !LELY_NO_THREADS
197 CRITICAL_SECTION CriticalSection;
198#endif
199 HANDLE hCanChn;
200 int flags;
201 UINT32 dwTscClkFreq;
202 UINT32 dwTscDivisor;
203 UINT32 dwTimeOvr;
204 unsigned shutdown : 1;
205 unsigned read_posted : 1;
206 unsigned write_posted : 1;
207 struct sllist read_queue;
208 struct sllist write_queue;
209 struct io_can_chan_read *current_read;
210 struct io_can_chan_write *current_write;
211};
212
213static void io_ixxat_chan_read_task_func(struct ev_task *task);
214static void io_ixxat_chan_write_task_func(struct ev_task *task);
215
216static inline struct io_ixxat_chan *io_ixxat_chan_from_dev(const io_dev_t *dev);
217static inline struct io_ixxat_chan *io_ixxat_chan_from_chan(
218 const io_can_chan_t *chan);
219static inline struct io_ixxat_chan *io_ixxat_chan_from_svc(
220 const struct io_svc *svc);
221
222static int io_ixxat_chan_read_impl(struct io_ixxat_chan *ixxat,
223 struct can_msg *msg, struct can_err *err, struct timespec *tp,
224 int timeout);
225static int io_ixxat_chan_write_impl(struct io_ixxat_chan *ixxat,
226 const struct can_msg *msg, int timeout);
227
228static void io_ixxat_chan_pop(struct io_ixxat_chan *ixxat,
229 struct sllist *read_queue, struct sllist *write_queue,
230 struct ev_task *task);
231
232static size_t io_ixxat_chan_do_abort_tasks(struct io_ixxat_chan *ixxat);
233
234static HANDLE io_ixxat_chan_set_handle(struct io_ixxat_chan *ixxat,
235 HANDLE hCanChn, int flags, UINT32 dwTscClkFreq,
236 UINT32 dwTscDivisor);
237
238int
239io_ixxat_init(void)
240{
241 if (io_ixxat_init_refcnt++)
242 return 0;
243
244 DWORD dwErrCode = 0;
245
246#if LELY_NO_CANFD
247 hLibModule = LoadLibraryA("vcinpl.dll");
248#else
249 hLibModule = LoadLibraryA("vcinpl2.dll");
250#endif
251 if (!hLibModule) {
252 dwErrCode = GetLastError();
253 goto error_LoadLibrary;
254 }
255
256#if defined(__MINGW32__) && __GNUC__ >= 8
257#pragma GCC diagnostic push
258#pragma GCC diagnostic ignored "-Wcast-function-type"
259#endif
260#define LELY_IO_DEFINE_IXXAT(name) \
261 if (!(p_##name = (PF_##name)GetProcAddress(hLibModule, #name))) { \
262 dwErrCode = GetLastError(); \
263 goto error_GetProcAddress; \
264 }
265#include "ixxat.inc"
266#undef LELY_IO_DEFINE_IXXAT
267#if defined(__MINGW32__) && __GNUC__ >= 8
268#pragma GCC diagnostic pop
269#endif
270
271 return 0;
272
273error_GetProcAddress:
274#define LELY_IO_DEFINE_IXXAT(name) p_##name = NULL;
275#include "ixxat.inc"
276#undef LELY_IO_DEFINE_IXXAT
277 FreeLibrary(hLibModule);
278 hLibModule = NULL;
279error_LoadLibrary:
280 SetLastError(dwErrCode);
281 io_ixxat_init_refcnt--;
282 return -1;
283}
284
285void
286io_ixxat_fini(void)
287{
288 assert(io_ixxat_init_refcnt);
289
290 if (--io_ixxat_init_refcnt)
291 return;
292
293#define LELY_IO_DEFINE_IXXAT(name) p_##name = NULL;
294#include "ixxat.inc"
295#undef LELY_IO_DEFINE_IXXAT
296
297 FreeLibrary(hLibModule);
298 hLibModule = NULL;
299}
300
301void *
302io_ixxat_ctrl_alloc(void)
303{
304 struct io_ixxat_ctrl *ixxat = malloc(sizeof(*ixxat));
305 if (!ixxat) {
306 set_errc(errno2c(errno));
307 return NULL;
308 }
309 return &ixxat->ctrl_vptr;
310}
311
312void
313io_ixxat_ctrl_free(void *ptr)
314{
315 if (ptr)
316 free(io_ixxat_ctrl_from_ctrl(ptr));
317}
318
320io_ixxat_ctrl_init(io_can_ctrl_t *ctrl, const LUID *lpLuid, UINT32 dwCanNo,
321 int flags, int nominal, int data)
322{
323 struct io_ixxat_ctrl *ixxat = io_ixxat_ctrl_from_ctrl(ctrl);
324 assert(lpLuid);
325
326 DWORD dwErrCode = 0;
327 HRESULT hResult = VCI_OK;
328
329 ixxat->ctrl_vptr = &io_ixxat_ctrl_vtbl;
330
331 VCIID VciObjectId = { .AsLuid = *lpLuid };
332 ixxat->hDevice = NULL;
333 if ((hResult = p_vciDeviceOpen(&VciObjectId, &ixxat->hDevice))
334 != VCI_OK) {
335 if (hResult == VCI_E_INVALIDARG)
336 dwErrCode = ERROR_DEV_NOT_EXIST;
337 else
338 dwErrCode = io_ixxat_error(hResult);
339 goto error_vciDeviceOpen;
340 }
341
342 ixxat->dwCanNo = dwCanNo;
343 ixxat->hCanCtl = NULL;
344 // clang-format off
345 if ((hResult = p_canControlOpen(ixxat->hDevice, ixxat->dwCanNo,
346 &ixxat->hCanCtl)) != VCI_OK) {
347 // clang-format on
348 if (hResult == VCI_E_INVALID_INDEX)
349 dwErrCode = ERROR_DEV_NOT_EXIST;
350 else
351 dwErrCode = io_ixxat_error(hResult);
352 goto error_canControlOpen;
353 }
354
355#if LELY_NO_CANFD
356 CANCAPABILITIES sCanCaps;
357#else
358 CANCAPABILITIES2 sCanCaps;
359#endif
360 if ((hResult = p_canControlGetCaps(ixxat->hCanCtl, &sCanCaps))
361 != VCI_OK) {
362 dwErrCode = io_ixxat_error(hResult);
363 goto error_canControlGetCaps;
364 }
365 if (!(sCanCaps.dwFeatures & CAN_FEATURE_STDANDEXT)) {
366 dwErrCode = io_ixxat_error(hResult);
367 goto error_canControlGetCaps;
368 }
369
370 ixxat->flags = flags;
371 if (ixxat->flags & ~IO_CAN_BUS_FLAG_MASK) {
372 dwErrCode = ERROR_INVALID_PARAMETER;
373 goto error_flags;
374 }
375 if ((ixxat->flags & IO_CAN_BUS_FLAG_ERR)
376 && !(sCanCaps.dwFeatures & CAN_FEATURE_ERRFRAME)) {
377 dwErrCode = ERROR_INVALID_PARAMETER;
378 goto error_flags;
379 }
380#if !LELY_NO_CANFD
381 if ((ixxat->flags & IO_CAN_BUS_FLAG_FDF)
382 && !(sCanCaps.dwFeatures & CAN_FEATURE_EXTDATA)) {
383 dwErrCode = ERROR_INVALID_PARAMETER;
384 goto error_flags;
385 }
386 if ((ixxat->flags & IO_CAN_BUS_FLAG_BRS)
387 && !(sCanCaps.dwFeatures & CAN_FEATURE_FASTDATA)) {
388 dwErrCode = ERROR_INVALID_PARAMETER;
389 goto error_flags;
390 }
391#endif
392
393#if LELY_NO_CANFD
394 ixxat->dwTscClkFreq = sCanCaps.dwClockFreq;
395#else
396 ixxat->dwTscClkFreq = sCanCaps.dwTscClkFreq;
397#endif
398 ixxat->dwTscDivisor = sCanCaps.dwTscDivisor;
399
400 if (io_can_ctrl_set_bitrate(ctrl, nominal, data) == -1) {
401 dwErrCode = GetLastError();
402 goto error_set_bitrate;
403 }
404
405 return ctrl;
406
407error_set_bitrate:
408error_flags:
409error_canControlGetCaps:
410 p_canControlClose(ixxat->hCanCtl);
411error_canControlOpen:
412 p_vciDeviceClose(ixxat->hDevice);
413error_vciDeviceOpen:
414 SetLastError(dwErrCode);
415 return NULL;
416}
417
418void
419io_ixxat_ctrl_fini(io_can_ctrl_t *ctrl)
420{
421 struct io_ixxat_ctrl *ixxat = io_ixxat_ctrl_from_ctrl(ctrl);
422
423 p_canControlClose(ixxat->hCanCtl);
424 p_vciDeviceClose(ixxat->hDevice);
425}
426
428io_ixxat_ctrl_create_from_index(UINT32 dwIndex, UINT32 dwCanNo, int flags,
429 int nominal, int data)
430{
431 HRESULT hResult = VCI_OK;
432
433 HANDLE hEnum = NULL;
434 if ((hResult = p_vciEnumDeviceOpen(&hEnum)) != VCI_OK) {
435 SetLastError(io_ixxat_error(hResult));
436 return NULL;
437 }
438
439 VCIDEVICEINFO sInfo = { .VciObjectId = { .AsLuid = { 0, 0 } } };
440 do {
441 if ((hResult = p_vciEnumDeviceNext(hEnum, &sInfo)) != VCI_OK) {
442 p_vciEnumDeviceClose(hEnum);
443 if (hResult == VCI_E_NO_MORE_ITEMS)
444 SetLastError(ERROR_DEV_NOT_EXIST);
445 else
446 SetLastError(io_ixxat_error(hResult));
447 return NULL;
448 }
449 } while (dwIndex-- > 0);
450
451 p_vciEnumDeviceClose(hEnum);
452
453 return io_ixxat_ctrl_create_from_luid(&sInfo.VciObjectId.AsLuid,
454 dwCanNo, flags, nominal, data);
455}
456
458io_ixxat_ctrl_create_from_luid(const LUID *lpLuid, UINT32 dwCanNo, int flags,
459 int nominal, int data)
460{
461 DWORD dwErrCode = 0;
462
463 io_can_ctrl_t *ctrl = io_ixxat_ctrl_alloc();
464 if (!ctrl) {
465 dwErrCode = GetLastError();
466 goto error_alloc;
467 }
468
469 io_can_ctrl_t *tmp = io_ixxat_ctrl_init(
470 ctrl, lpLuid, dwCanNo, flags, nominal, data);
471 if (!tmp) {
472 dwErrCode = GetLastError();
473 goto error_init;
474 }
475 ctrl = tmp;
476
477 return ctrl;
478
479error_init:
480 io_ixxat_ctrl_free((void *)ctrl);
481error_alloc:
482 SetLastError(dwErrCode);
483 return NULL;
484}
485
486void
488{
489 if (ctrl) {
490 io_ixxat_ctrl_fini(ctrl);
491 io_ixxat_ctrl_free((void *)ctrl);
492 }
493}
494
495HANDLE
497{
498 const struct io_ixxat_ctrl *ixxat = io_ixxat_ctrl_from_ctrl(ctrl);
499
500 return ixxat->hCanCtl;
501}
502
503void *
504io_ixxat_chan_alloc(void)
505{
506 struct io_ixxat_chan *ixxat = malloc(sizeof(*ixxat));
507 if (!ixxat) {
508 set_errc(errno2c(errno));
509 return NULL;
510 }
511 return &ixxat->chan_vptr;
512}
513
514void
515io_ixxat_chan_free(void *ptr)
516{
517 if (ptr)
518 free(io_ixxat_chan_from_chan(ptr));
519}
520
522io_ixxat_chan_init(io_can_chan_t *chan, io_ctx_t *ctx, ev_exec_t *exec,
523 int rxtimeo, int txtimeo)
524{
525 struct io_ixxat_chan *ixxat = io_ixxat_chan_from_chan(chan);
526 assert(ctx);
527
528 if (!rxtimeo)
529 rxtimeo = LELY_IO_RX_TIMEOUT;
530 if (!txtimeo)
531 txtimeo = LELY_IO_TX_TIMEOUT;
532
533 ixxat->dev_vptr = &io_ixxat_chan_dev_vtbl;
534 ixxat->chan_vptr = &io_ixxat_chan_vtbl;
535
536 ixxat->svc = (struct io_svc)IO_SVC_INIT(&io_ixxat_chan_svc_vtbl);
537 ixxat->ctx = ctx;
538
539 ixxat->exec = exec;
540
541 ixxat->rxtimeo = rxtimeo;
542 ixxat->txtimeo = txtimeo;
543
544 ixxat->read_task = (struct ev_task)EV_TASK_INIT(
545 ixxat->exec, &io_ixxat_chan_read_task_func);
546 ixxat->write_task = (struct ev_task)EV_TASK_INIT(
547 ixxat->exec, &io_ixxat_chan_write_task_func);
548
549#if !LELY_NO_THREADS
550 InitializeCriticalSection(&ixxat->CriticalSection);
551#endif
552
553 ixxat->hCanChn = NULL;
554
555 ixxat->flags = 0;
556 ixxat->dwTscClkFreq = 0;
557 ixxat->dwTscDivisor = 0;
558 ixxat->dwTimeOvr = 0;
559
560 ixxat->shutdown = 0;
561 ixxat->read_posted = 0;
562 ixxat->write_posted = 0;
563
564 sllist_init(&ixxat->read_queue);
565 sllist_init(&ixxat->write_queue);
566
567 ixxat->current_read = NULL;
568 ixxat->current_write = NULL;
569
570 io_ctx_insert(ixxat->ctx, &ixxat->svc);
571
572 return chan;
573}
574
575void
576io_ixxat_chan_fini(io_can_chan_t *chan)
577{
578 struct io_ixxat_chan *ixxat = io_ixxat_chan_from_chan(chan);
579
580 io_ctx_remove(ixxat->ctx, &ixxat->svc);
581 // Cancel all pending operations.
582 io_ixxat_chan_svc_shutdown(&ixxat->svc);
583
584 // Deactivate the channel.
585 if (ixxat->hCanChn)
586 p_canChannelActivate(ixxat->hCanChn, FALSE);
587
588#if !LELY_NO_THREADS
589 int warning = 0;
590 EnterCriticalSection(&ixxat->CriticalSection);
591 // If necessary, busy-wait until io_ixxat_chan_read_task_func() and
592 // io_ixxat_chan_write_task_func() complete.
593 while (ixxat->read_posted || ixxat->write_posted) {
594 if (io_ixxat_chan_do_abort_tasks(ixxat))
595 continue;
596 LeaveCriticalSection(&ixxat->CriticalSection);
597 if (!warning) {
598 warning = 1;
600 "io_ixxat_chan_fini() invoked with pending operations");
601 }
602 SwitchToThread();
603 EnterCriticalSection(&ixxat->CriticalSection);
604 }
605 LeaveCriticalSection(&ixxat->CriticalSection);
606#endif
607
608 // Close the channel.
609 if (ixxat->hCanChn)
610 p_canChannelClose(ixxat->hCanChn);
611
612#if !LELY_NO_THREADS
613 DeleteCriticalSection(&ixxat->CriticalSection);
614#endif
615}
616
618io_ixxat_chan_create(io_ctx_t *ctx, ev_exec_t *exec, int rxtimeo, int txtimeo)
619{
620 DWORD dwErrCode = 0;
621
622 io_can_chan_t *chan = io_ixxat_chan_alloc();
623 if (!chan) {
624 dwErrCode = GetLastError();
625 goto error_alloc;
626 }
627
628 io_can_chan_t *tmp =
629 io_ixxat_chan_init(chan, ctx, exec, rxtimeo, txtimeo);
630 if (!tmp) {
631 dwErrCode = GetLastError();
632 goto error_init;
633 }
634 chan = tmp;
635
636 return chan;
637
638error_init:
639 io_ixxat_chan_free((void *)chan);
640error_alloc:
641 SetLastError(dwErrCode);
642 return NULL;
643}
644
645void
647{
648 if (chan) {
649 io_ixxat_chan_fini(chan);
650 io_ixxat_chan_free((void *)chan);
651 }
652}
653
654HANDLE
656{
657 const struct io_ixxat_chan *ixxat = io_ixxat_chan_from_chan(chan);
658
659#if !LELY_NO_THREADS
660 EnterCriticalSection((LPCRITICAL_SECTION)&ixxat->CriticalSection);
661#endif
662 HANDLE hCanChn = ixxat->hCanChn;
663#if !LELY_NO_THREADS
664 LeaveCriticalSection((LPCRITICAL_SECTION)&ixxat->CriticalSection);
665#endif
666 return hCanChn;
667}
668
669int
671 UINT16 wRxFifoSize, UINT16 wTxFifoSize)
672{
673 struct io_ixxat_chan *ixxat_chan = io_ixxat_chan_from_chan(chan);
674 struct io_ixxat_ctrl *ixxat_ctrl = io_ixxat_ctrl_from_ctrl(ctrl);
675
676 if (!wRxFifoSize)
677 wRxFifoSize = LELY_IO_IXXAT_RX_FIFO_SIZE;
678 if (!wTxFifoSize)
679 wTxFifoSize = LELY_IO_IXXAT_TX_FIFO_SIZE;
680
681 DWORD dwErrCode = 0;
682 HRESULT hResult;
683
684 HANDLE hCanChn = NULL;
685 // clang-format off
686 if ((hResult = p_canChannelOpen(ixxat_ctrl->hDevice,
687 ixxat_ctrl->dwCanNo, FALSE, &hCanChn)) != VCI_OK) {
688 // clang-format on
689 dwErrCode = io_ixxat_error(hResult);
690 goto error_canChannelOpen;
691 }
692
693 // clang-format off
694#if LELY_NO_CANFD
695 if ((hResult = p_canChannelInitialize(hCanChn, wRxFifoSize, 1,
696 wTxFifoSize, 1)) != VCI_OK) {
697#else
698 if ((hResult = p_canChannelInitialize(hCanChn, wRxFifoSize, 1,
699 wTxFifoSize, 1, 0, CAN_FILTER_PASS)) != VCI_OK) {
700#endif
701 // clang-format on
702 dwErrCode = io_ixxat_error(hResult);
703 goto error_canChannelInitialize;
704 }
705
706 if ((hResult = p_canChannelActivate(hCanChn, TRUE)) != VCI_OK) {
707 dwErrCode = io_ixxat_error(hResult);
708 goto error_canChannelActivate;
709 }
710
711 hCanChn = io_ixxat_chan_set_handle(ixxat_chan, hCanChn,
712 ixxat_ctrl->flags, ixxat_ctrl->dwTscClkFreq,
713 ixxat_ctrl->dwTscDivisor);
714 if (hCanChn)
715 p_canChannelClose(hCanChn);
716
717 return 0;
718
719error_canChannelActivate:
720error_canChannelInitialize:
721 p_canChannelClose(hCanChn);
722error_canChannelOpen:
723 SetLastError(dwErrCode);
724 return -1;
725}
726
727int
728io_ixxat_chan_assign(io_can_chan_t *chan, HANDLE hCanChn, UINT32 dwTscClkFreq,
729 UINT32 dwTscDivisor)
730{
731 struct io_ixxat_chan *ixxat = io_ixxat_chan_from_chan(chan);
732
733 HRESULT hResult;
734
735 if (!hCanChn) {
736 SetLastError(ERROR_INVALID_HANDLE);
737 return -1;
738 }
739
740#if LELY_NO_CANFD
741 CANCHANSTATUS sStatus;
742#else
743 CANCHANSTATUS2 sStatus;
744#endif
745 if ((hResult = p_canChannelGetStatus(hCanChn, &sStatus)) != VCI_OK) {
746 SetLastError(io_ixxat_error(hResult));
747 return -1;
748 }
749
750 int flags = 0;
751 if (sStatus.sLineStatus.bOpMode & CAN_OPMODE_ERRFRAME)
752 flags |= IO_CAN_BUS_FLAG_ERR;
753#if !LELY_NO_CANFD
754 if (sStatus.sLineStatus.bExMode & CAN_EXMODE_EXTDATA)
755 flags |= IO_CAN_BUS_FLAG_FDF;
756 if (sStatus.sLineStatus.bExMode & CAN_EXMODE_FASTDATA)
757 flags |= IO_CAN_BUS_FLAG_BRS;
758#endif
759
760 if (!sStatus.fActivated
761 && (hResult = p_canChannelActivate(hCanChn, TRUE))
762 != VCI_OK) {
763 SetLastError(io_ixxat_error(hResult));
764 return -1;
765 }
766
767 hCanChn = io_ixxat_chan_set_handle(
768 ixxat, hCanChn, flags, dwTscClkFreq, dwTscDivisor);
769 if (hCanChn)
770 p_canChannelClose(hCanChn);
771
772 return 0;
773}
774
775HANDLE
777{
778 struct io_ixxat_chan *ixxat = io_ixxat_chan_from_chan(chan);
779
780 return io_ixxat_chan_set_handle(ixxat, NULL, 0, 0, 0);
781}
782
783int
785{
786 return io_ixxat_chan_get_handle(chan) != NULL;
787}
788
789int
791{
792 HANDLE hCanChn = io_ixxat_chan_release(chan);
793 if (hCanChn) {
794 HRESULT hResult = p_canChannelClose(hCanChn);
795 if (hResult != VCI_OK) {
796 SetLastError(io_ixxat_error(hResult));
797 return -1;
798 }
799 }
800 return 0;
801}
802
803static int
804io_ixxat_read(HANDLE hCanChn, struct can_msg *msg, struct can_err *err,
805 PUINT32 pdwTime, PUINT32 pdwTimeOvr, int timeout)
806{
807 if (pdwTimeOvr)
808 *pdwTimeOvr = 0;
809
810 if (!hCanChn) {
811 SetLastError(ERROR_INVALID_HANDLE);
812 return -1;
813 }
814
815#if LELY_NO_CANFD
816 CANMSG CanMsg = { .dwMsgId = 0 };
817#else
818 CANMSG2 CanMsg = { .dwMsgId = 0 };
819#endif
820 UINT32 type = 0;
821 DWORD dwTimeout = timeout < 0 ? INFINITE : (DWORD)timeout;
822 for (;;) {
823 HRESULT hResult = p_canChannelReadMessage(
824 hCanChn, dwTimeout, &CanMsg);
825 if (hResult != VCI_OK) {
826 SetLastError(io_ixxat_error(hResult));
827 return -1;
828 }
829 type = CanMsg.uMsgInfo.Bits.type;
830 if (type == CAN_MSGTYPE_DATA || type == CAN_MSGTYPE_ERROR
831 || type == CAN_MSGTYPE_STATUS) {
832 break;
833 } else {
834 if (type == CAN_MSGTYPE_TIMEOVR && pdwTimeOvr)
835 // The number of occurred time stamp counter
836 // overflows is stored in the CAN ID.
837 *pdwTimeOvr += CanMsg.dwMsgId;
838 if (dwTimeout != INFINITE) {
839 SetLastError(ERROR_TIMEOUT);
840 return -1;
841 }
842 }
843 }
844
845 int result = 1;
846 if (type == CAN_MSGTYPE_ERROR) {
847 result = 0;
848
849 enum can_error error = err ? err->error : 0;
850 switch (CanMsg.abData[0]) {
851 case CAN_ERROR_STUFF: error |= LELY_IO_CAN_ERROR_STUFF; break;
852 case CAN_ERROR_FORM: error |= LELY_IO_CAN_ERROR_FORM; break;
853 case CAN_ERROR_ACK: error |= LELY_IO_CAN_ERROR_ACK; break;
854 case CAN_ERROR_BIT: error |= LELY_IO_CAN_ERROR_BIT; break;
855 case CAN_ERROR_CRC: error |= LELY_IO_CAN_ERROR_CRC; break;
856 default: error |= LELY_IO_CAN_ERROR_OTHER; break;
857 }
858
859 enum can_state state = io_ixxat_state(CanMsg.abData[1]);
860
861 if (err) {
862 err->state = state;
863 err->error = error;
864 }
865 } else if (type == CAN_MSGTYPE_STATUS) {
866 result = 0;
867
868 if (err)
869 err->state = io_ixxat_state(CanMsg.abData[0]);
870 } else if (msg) {
871 assert(type == CAN_MSGTYPE_DATA);
872
873 *msg = (struct can_msg)CAN_MSG_INIT;
874 if (CanMsg.uMsgInfo.Bits.ext) {
875 msg->id = letoh32(CanMsg.dwMsgId) & CAN_MASK_EID;
876 msg->flags |= CAN_FLAG_IDE;
877 } else {
878 msg->id = letoh32(CanMsg.dwMsgId) & CAN_MASK_BID;
879 }
880 if (CanMsg.uMsgInfo.Bits.rtr)
881 msg->flags |= CAN_FLAG_RTR;
882#if !LELY_NO_CANFD
883 if (CanMsg.uMsgInfo.Bits.edl)
884 msg->flags |= CAN_FLAG_FDF;
885 if (CanMsg.uMsgInfo.Bits.fdr)
886 msg->flags |= CAN_FLAG_BRS;
887 if (CanMsg.uMsgInfo.Bits.esi)
888 msg->flags |= CAN_FLAG_ESI;
889#endif
890#if !LELY_NO_CANFD
891 if (CanMsg.uMsgInfo.Bits.edl) {
892 if (CanMsg.uMsgInfo.Bits.dlc <= 8) {
893 msg->len = CanMsg.uMsgInfo.Bits.dlc;
894 } else {
895 switch (CanMsg.uMsgInfo.Bits.dlc) {
896 case 0x9: msg->len = 12; break;
897 case 0xa: msg->len = 16; break;
898 case 0xb: msg->len = 20; break;
899 case 0xc: msg->len = 24; break;
900 case 0xd: msg->len = 32; break;
901 case 0xe: msg->len = 48; break;
902 case 0xf: msg->len = 64; break;
903 }
904 }
905 } else
906#endif
907 msg->len = CanMsg.uMsgInfo.Bits.dlc;
908 memcpy(msg->data, CanMsg.abData, msg->len);
909 }
910
911 if (pdwTime)
912 *pdwTime = CanMsg.dwTime;
913
914 return result;
915}
916
917static int
918io_ixxat_write(HANDLE hCanChn, const struct can_msg *msg, int timeout)
919{
920 assert(msg);
921
922 if (!hCanChn) {
923 SetLastError(ERROR_INVALID_HANDLE);
924 return -1;
925 }
926
927#if LELY_NO_CANFD
928 CANMSG CanMsg = { .dwMsgId = 0 };
929#else
930 CANMSG2 CanMsg = { .dwMsgId = 0 };
931#endif
932 if (msg->flags & CAN_FLAG_IDE) {
933 CanMsg.dwMsgId = htole32(msg->id & CAN_MASK_EID);
934 CanMsg.uMsgInfo.Bits.ext = 1;
935 } else {
936 CanMsg.dwMsgId = htole32(msg->id & CAN_MASK_BID);
937 }
938 CanMsg.uMsgInfo.Bits.type = CAN_MSGTYPE_DATA;
939 if (msg->flags & CAN_FLAG_RTR)
940 CanMsg.uMsgInfo.Bits.rtr = 1;
941#if !LELY_NO_CANFD
942 if (msg->flags & CAN_FLAG_FDF)
943 CanMsg.uMsgInfo.Bits.edl = 1;
944 if (msg->flags & CAN_FLAG_BRS)
945 CanMsg.uMsgInfo.Bits.fdr = 1;
946 if (msg->flags & CAN_FLAG_ESI)
947 CanMsg.uMsgInfo.Bits.esi = 1;
948#endif
949 int len;
950#if !LELY_NO_CANFD
951 if (msg->flags & CAN_FLAG_FDF) {
952 len = MIN(msg->len, CANFD_MAX_LEN);
953 if (len <= 8)
954 CanMsg.uMsgInfo.Bits.dlc = len;
955 else if (len <= 12)
956 CanMsg.uMsgInfo.Bits.dlc = 0x9;
957 else if (len <= 16)
958 CanMsg.uMsgInfo.Bits.dlc = 0xa;
959 else if (len <= 20)
960 CanMsg.uMsgInfo.Bits.dlc = 0xb;
961 else if (len <= 24)
962 CanMsg.uMsgInfo.Bits.dlc = 0xc;
963 else if (len <= 32)
964 CanMsg.uMsgInfo.Bits.dlc = 0xd;
965 else if (len <= 48)
966 CanMsg.uMsgInfo.Bits.dlc = 0xe;
967 else
968 CanMsg.uMsgInfo.Bits.dlc = 0xf;
969 } else
970#endif
971 len = CanMsg.uMsgInfo.Bits.dlc = MIN(msg->len, CAN_MAX_LEN);
972 memcpy(CanMsg.abData, msg->data, len);
973
974 DWORD dwTimeout = timeout < 0 ? INFINITE : (DWORD)timeout;
975 HRESULT hResult = p_canChannelSendMessage(hCanChn, dwTimeout, &CanMsg);
976 if (hResult != VCI_OK) {
977 SetLastError(io_ixxat_error(hResult));
978 return -1;
979 }
980
981 return 0;
982}
983
984static DWORD
985io_ixxat_error(HRESULT hResult)
986{
987 if (GETSEVERITY(hResult) != SEV_ERROR)
988 return 0;
989
990 switch (GETSCODE(hResult)) {
991 case 0x0001: // VCI_E_UNEXPECTED
992 return ERROR_UNEXP_NET_ERR;
993 case 0x0002: // VCI_E_NOT_IMPLEMENTED
994 return ERROR_CALL_NOT_IMPLEMENTED;
995 case 0x0003: // VCI_E_OUTOFMEMORY
996 return ERROR_NOT_ENOUGH_MEMORY;
997 case 0x0004: // VCI_E_INVALIDARG
998 return ERROR_BAD_ARGUMENTS;
999 case 0x0005: // VCI_E_NOINTERFACE
1000#ifndef ERROR_NOINTERFACE
1001#define ERROR_NOINTERFACE 632
1002#endif
1003 return ERROR_NOINTERFACE;
1004 case 0x0006: // VCI_E_INVPOINTER
1005 return ERROR_INVALID_ADDRESS;
1006 case 0x0007: // VCI_E_INVHANDLE
1007 return ERROR_INVALID_HANDLE;
1008 case 0x0008: // VCI_E_ABORT
1009 return ERROR_OPERATION_ABORTED;
1010 case 0x0009: // VCI_E_FAIL
1011 return ERROR_GEN_FAILURE;
1012 case 0x000a: // VCI_E_ACCESSDENIED
1013 return ERROR_ACCESS_DENIED;
1014 case 0x000b: // VCI_E_TIMEOUT
1015 return ERROR_TIMEOUT;
1016 case 0x000c: // VCI_E_BUSY
1017 return ERROR_BUSY;
1018 case 0x000d: // VCI_E_PENDING
1019 return ERROR_IO_PENDING;
1020 case 0x000e: // VCI_E_NO_DATA
1021 return ERROR_NO_DATA;
1022 case 0x000f: // VCI_E_NO_MORE_ITEMS
1023 return ERROR_NO_MORE_ITEMS;
1024 case 0x0010: // VCI_E_NOT_INITIALIZED
1025#ifndef PEERDIST_ERROR_NOT_INITIALIZED
1026#define PEERDIST_ERROR_NOT_INITIALIZED 4054
1027#endif
1028 return PEERDIST_ERROR_NOT_INITIALIZED;
1029 case 0x0011: // VCI_E_ALREADY_INITIALIZED
1030#ifndef PEERDIST_ERROR_ALREADY_INITIALIZED
1031#define PEERDIST_ERROR_ALREADY_INITIALIZED 4055
1032#endif
1033 return PEERDIST_ERROR_ALREADY_INITIALIZED;
1034 case 0x0012: // VCI_E_RXQUEUE_EMPTY
1035 case 0x0013: // VCI_E_TXQUEUE_FULL
1036 return ERROR_RETRY;
1037 case 0x0014: // VCI_E_BUFFER_OVERFLOW
1038 return ERROR_BUFFER_OVERFLOW;
1039 case 0x0015: // VCI_E_INVALID_STATE
1040#ifndef PEERDIST_ERROR_INVALIDATED
1041#define PEERDIST_ERROR_INVALIDATED 4057
1042#endif
1043 return PEERDIST_ERROR_INVALIDATED;
1044 case 0x0016: // VCI_E_OBJECT_ALREADY_EXISTS
1045 return ERROR_ALREADY_EXISTS;
1046 case 0x0017: // VCI_E_INVALID_INDEX
1047 return ERROR_INVALID_INDEX;
1048 case 0x0018: // VCI_E_END_OF_FILE
1049 return ERROR_HANDLE_EOF;
1050 case 0x0019: // VCI_E_DISCONNECTED
1051 return ERROR_GRACEFUL_DISCONNECT;
1052 case 0x001a: // VCI_E_INVALID_FIRMWARE
1053#ifndef PEERDIST_ERROR_VERSION_UNSUPPORTED
1054#define PEERDIST_ERROR_VERSION_UNSUPPORTED 4062
1055#endif
1056 return PEERDIST_ERROR_VERSION_UNSUPPORTED;
1057 case 0x001b: // VCI_E_INVALID_LICENSE
1058 case 0x001c: // VCI_E_NO_SUCH_LICENSE
1059 case 0x001d: // VCI_E_LICENSE_EXPIRED
1060#ifndef PEERDIST_ERROR_NOT_LICENSED
1061#define PEERDIST_ERROR_NOT_LICENSED 4064
1062#endif
1063 return PEERDIST_ERROR_NOT_LICENSED;
1064 case 0x001e: // VCI_E_LICENSE_QUOTA_EXCEEDED
1065 return ERROR_LICENSE_QUOTA_EXCEEDED;
1066 case 0x001f: // VCI_E_INVALID_TIMING
1067 return ERROR_INVALID_PARAMETER;
1068 case 0x0020: // VCI_E_IN_USE
1069 return ERROR_DEVICE_IN_USE;
1070 case 0x0021: // VCI_E_NO_SUCH_DEVICE
1071 return ERROR_DEV_NOT_EXIST;
1072 case 0x0022: // VCI_E_DEVICE_NOT_CONNECTED
1073 return ERROR_NOT_CONNECTED;
1074 case 0x0023: // VCI_E_DEVICE_NOT_READY
1075 return ERROR_NOT_READY;
1076 case 0x0024: // VCI_E_TYPE_MISMATCH
1077 return ERROR_BAD_DEV_TYPE;
1078 case 0x0025: // VCI_E_NOT_SUPPORTED
1079 return ERROR_NOT_SUPPORTED;
1080 case 0x0026: // VCI_E_DUPLICATE_OBJECTID
1081 return ERROR_ALREADY_ASSIGNED;
1082 case 0x0027: // VCI_E_OBJECTID_NOT_FOUND
1083 return ERROR_NOT_FOUND;
1084 case 0x0028: // VCI_E_WRONG_LEVEL
1085 return ERROR_INVALID_LEVEL;
1086 case 0x0029: // VCI_E_WRONG_DRV_VERSION
1087 return PEERDIST_ERROR_VERSION_UNSUPPORTED;
1088 case 0x002a: // VCI_E_LUIDS_EXHAUSTED
1089 return ERROR_LUIDS_EXHAUSTED;
1090 default: return ERROR_UNEXP_NET_ERR;
1091 }
1092}
1093
1094static int
1095io_ixxat_state(UINT32 dwStatus)
1096{
1097 if (dwStatus & CAN_STATUS_BUSOFF)
1098 return CAN_STATE_BUSOFF;
1099 if (dwStatus & CAN_STATUS_ERRLIM)
1100 return CAN_STATE_PASSIVE;
1101 return CAN_STATE_ACTIVE;
1102}
1103
1104static struct timespec
1105io_ixxat_time(UINT64 qwTime, UINT32 dwTscClkFreq, UINT32 dwTscDivisor)
1106{
1107 struct timespec ts = { 0, 0 };
1108 if (dwTscClkFreq && dwTscDivisor) {
1109 qwTime *= dwTscDivisor;
1110 ts.tv_sec = qwTime / dwTscClkFreq;
1111 qwTime -= ts.tv_sec * dwTscClkFreq;
1112 ts.tv_nsec = (qwTime * 1000000000ul) / dwTscClkFreq;
1113 }
1114 return ts;
1115}
1116
1117static int
1118io_ixxat_ctrl_stop(io_can_ctrl_t *ctrl)
1119{
1120 struct io_ixxat_ctrl *ixxat = io_ixxat_ctrl_from_ctrl(ctrl);
1121
1122 HRESULT hResult = p_canControlStart(ixxat->hCanCtl, FALSE);
1123 if (hResult != VCI_OK) {
1124 SetLastError(io_ixxat_error(hResult));
1125 return -1;
1126 }
1127 return 0;
1128}
1129
1130static int
1131io_ixxat_ctrl_stopped(const io_can_ctrl_t *ctrl)
1132{
1133 const struct io_ixxat_ctrl *ixxat = io_ixxat_ctrl_from_ctrl(ctrl);
1134
1135#if LELY_NO_CANFD
1136 CANLINESTATUS sStatus;
1137#else
1138 CANLINESTATUS2 sStatus;
1139#endif
1140 HRESULT hResult = p_canControlGetStatus(ixxat->hCanCtl, &sStatus);
1141 if (hResult != VCI_OK) {
1142 SetLastError(io_ixxat_error(hResult));
1143 return -1;
1144 }
1145 return !!(sStatus.dwStatus & CAN_STATUS_ININIT);
1146}
1147
1148static int
1149io_ixxat_ctrl_restart(io_can_ctrl_t *ctrl)
1150{
1151 struct io_ixxat_ctrl *ixxat = io_ixxat_ctrl_from_ctrl(ctrl);
1152
1153 HRESULT hResult = p_canControlStart(ixxat->hCanCtl, TRUE);
1154 if (hResult != VCI_OK) {
1155 SetLastError(io_ixxat_error(hResult));
1156 return -1;
1157 }
1158 return 0;
1159}
1160
1161static int
1162io_ixxat_ctrl_get_bitrate(const io_can_ctrl_t *ctrl, int *pnominal, int *pdata)
1163{
1164 struct io_ixxat_ctrl *ixxat = io_ixxat_ctrl_from_ctrl(ctrl);
1165
1166#if LELY_NO_CANFD
1167 CANLINESTATUS sStatus;
1168#else
1169 CANLINESTATUS2 sStatus;
1170#endif
1171 HRESULT hResult = p_canControlGetStatus(ixxat->hCanCtl, &sStatus);
1172 if (hResult != VCI_OK) {
1173 SetLastError(io_ixxat_error(hResult));
1174 return -1;
1175 }
1176
1177#if LELY_NO_CANFD
1178 if (pnominal) {
1179 UINT8 bBtr0 = sStatus.bBtReg0;
1180 UINT8 bBtr1 = sStatus.bBtReg1;
1181 if (bBtr0 == CAN_BT0_5KB && bBtr1 == CAN_BT1_5KB) {
1182 *pnominal = 5000;
1183 } else if (bBtr0 == CAN_BT0_10KB && bBtr1 == CAN_BT1_10KB) {
1184 *pnominal = 10000;
1185 } else if (bBtr0 == CAN_BT0_20KB && bBtr1 == CAN_BT1_20KB) {
1186 *pnominal = 20000;
1187 } else if (bBtr0 == CAN_BT0_50KB && bBtr1 == CAN_BT1_50KB) {
1188 *pnominal = 50000;
1189 } else if (bBtr0 == CAN_BT0_100KB && bBtr1 == CAN_BT1_100KB) {
1190 *pnominal = 100000;
1191 } else if (bBtr0 == CAN_BT0_125KB && bBtr1 == CAN_BT1_125KB) {
1192 *pnominal = 125000;
1193 } else if (bBtr0 == CAN_BT0_250KB && bBtr1 == CAN_BT1_250KB) {
1194 *pnominal = 250000;
1195 } else if (bBtr0 == CAN_BT0_500KB && bBtr1 == CAN_BT1_500KB) {
1196 *pnominal = 500000;
1197 } else if (bBtr0 == CAN_BT0_800KB && bBtr1 == CAN_BT1_800KB) {
1198 *pnominal = 800000;
1199 } else if (bBtr0 == CAN_BT0_1000KB && bBtr1 == CAN_BT1_1000KB) {
1200 *pnominal = 1000000;
1201 } else {
1202 *pnominal = 0;
1203 }
1204 }
1205 if (pdata)
1206 *pdata = 0;
1207#else
1208 if (pnominal)
1209 *pnominal = sStatus.sBtpSdr.dwBPS;
1210 if (pdata)
1211 *pdata = (sStatus.bExMode & CAN_FEATURE_FASTDATA)
1212 ? sStatus.sBtpFdr.dwBPS
1213 : 0;
1214#endif
1215
1216 return 0;
1217}
1218
1219static int
1220io_ixxat_ctrl_set_bitrate(io_can_ctrl_t *ctrl, int nominal, int data)
1221{
1222 struct io_ixxat_ctrl *ixxat = io_ixxat_ctrl_from_ctrl(ctrl);
1223#if LELY_NO_CANFD
1224 (void)data;
1225#endif
1226
1227 UINT8 bOpMode = CAN_OPMODE_STANDARD | CAN_OPMODE_EXTENDED;
1228 if (ixxat->flags & IO_CAN_BUS_FLAG_ERR)
1229 bOpMode |= CAN_OPMODE_ERRFRAME;
1230#if LELY_NO_CANFD
1231 UINT8 bBtr0 = 0;
1232 UINT8 bBtr1 = 0;
1233 switch (nominal) {
1234 case 5000:
1235 bBtr0 = CAN_BT0_5KB;
1236 bBtr1 = CAN_BT1_5KB;
1237 break;
1238 case 10000:
1239 bBtr0 = CAN_BT0_10KB;
1240 bBtr1 = CAN_BT1_10KB;
1241 break;
1242 case 20000:
1243 bBtr0 = CAN_BT0_20KB;
1244 bBtr1 = CAN_BT1_20KB;
1245 break;
1246 case 50000:
1247 bBtr0 = CAN_BT0_50KB;
1248 bBtr1 = CAN_BT1_50KB;
1249 break;
1250 case 100000:
1251 bBtr0 = CAN_BT0_100KB;
1252 bBtr1 = CAN_BT1_100KB;
1253 break;
1254 case 125000:
1255 bBtr0 = CAN_BT0_125KB;
1256 bBtr1 = CAN_BT1_125KB;
1257 break;
1258 case 250000:
1259 bBtr0 = CAN_BT0_250KB;
1260 bBtr1 = CAN_BT1_250KB;
1261 break;
1262 case 500000:
1263 bBtr0 = CAN_BT0_500KB;
1264 bBtr1 = CAN_BT1_500KB;
1265 break;
1266 case 800000:
1267 bBtr0 = CAN_BT0_800KB;
1268 bBtr1 = CAN_BT1_800KB;
1269 break;
1270 case 1000000:
1271 bBtr0 = CAN_BT0_1000KB;
1272 bBtr1 = CAN_BT1_1000KB;
1273 break;
1274 default: SetLastError(ERROR_INVALID_PARAMETER); return -1;
1275 }
1276 HRESULT hResult = p_canControlInitialize(
1277 ixxat->hCanCtl, bOpMode, bBtr0, bBtr1);
1278#else
1279 UINT8 bExMode = CAN_EXMODE_DISABLED;
1280 if (ixxat->flags & IO_CAN_BUS_FLAG_FDF)
1281 bExMode |= CAN_EXMODE_EXTDATA;
1282 if (ixxat->flags & IO_CAN_BUS_FLAG_BRS)
1283 bExMode |= CAN_EXMODE_FASTDATA;
1284 CANBTP sBtpSDR, sBtpFDR;
1285 switch (nominal) {
1286 case 5000: sBtpSDR = (CANBTP)CAN_BTP_5KB; break;
1287 case 10000: sBtpSDR = (CANBTP)CAN_BTP_10KB; break;
1288 case 20000: sBtpSDR = (CANBTP)CAN_BTP_20KB; break;
1289 case 50000: sBtpSDR = (CANBTP)CAN_BTP_50KB; break;
1290 case 100000: sBtpSDR = (CANBTP)CAN_BTP_100KB; break;
1291 case 125000: sBtpSDR = (CANBTP)CAN_BTP_125KB; break;
1292 case 250000: sBtpSDR = (CANBTP)CAN_BTP_250KB; break;
1293 case 500000: sBtpSDR = (CANBTP)CAN_BTP_500KB; break;
1294 case 800000: sBtpSDR = (CANBTP)CAN_BTP_800KB; break;
1295 case 1000000: sBtpSDR = (CANBTP)CAN_BTP_1000KB; break;
1296 default: SetLastError(ERROR_INVALID_PARAMETER); return -1;
1297 }
1298 if (ixxat->flags & IO_CAN_BUS_FLAG_BRS) {
1299 switch (data) {
1300 case 5000: sBtpFDR = (CANBTP)CAN_BTP_5KB; break;
1301 case 10000: sBtpFDR = (CANBTP)CAN_BTP_10KB; break;
1302 case 20000: sBtpFDR = (CANBTP)CAN_BTP_20KB; break;
1303 case 50000: sBtpFDR = (CANBTP)CAN_BTP_50KB; break;
1304 case 100000: sBtpFDR = (CANBTP)CAN_BTP_100KB; break;
1305 case 125000: sBtpFDR = (CANBTP)CAN_BTP_125KB; break;
1306 case 250000: sBtpFDR = (CANBTP)CAN_BTP_250KB; break;
1307 case 500000: sBtpFDR = (CANBTP)CAN_BTP_500KB; break;
1308 case 800000: sBtpFDR = (CANBTP)CAN_BTP_800KB; break;
1309 case 1000000: sBtpFDR = (CANBTP)CAN_BTP_1000KB; break;
1310 default: SetLastError(ERROR_INVALID_PARAMETER); return -1;
1311 }
1312 }
1313 HRESULT hResult = p_canControlInitialize(ixxat->hCanCtl, bOpMode,
1314 bExMode, CAN_FILTER_PASS, CAN_FILTER_PASS, 0, 0,
1315 &sBtpSDR,
1316 (ixxat->flags & IO_CAN_BUS_FLAG_BRS) ? &sBtpFDR : NULL);
1317#endif
1318 if (hResult != VCI_OK) {
1319 SetLastError(io_ixxat_error(hResult));
1320 return -1;
1321 }
1322
1323 return 0;
1324}
1325
1326static int
1327io_ixxat_ctrl_get_state(const io_can_ctrl_t *ctrl)
1328{
1329 const struct io_ixxat_ctrl *ixxat = io_ixxat_ctrl_from_ctrl(ctrl);
1330
1331#if LELY_NO_CANFD
1332 CANLINESTATUS sStatus;
1333#else
1334 CANLINESTATUS2 sStatus;
1335#endif
1336 HRESULT hResult = p_canControlGetStatus(ixxat->hCanCtl, &sStatus);
1337 if (hResult != VCI_OK) {
1338 SetLastError(io_ixxat_error(hResult));
1339 return -1;
1340 }
1341
1342 return io_ixxat_state(sStatus.dwStatus);
1343}
1344
1345static inline struct io_ixxat_ctrl *
1346io_ixxat_ctrl_from_ctrl(const io_can_ctrl_t *ctrl)
1347{
1348 assert(ctrl);
1349
1350 return structof(ctrl, struct io_ixxat_ctrl, ctrl_vptr);
1351}
1352
1353static io_ctx_t *
1354io_ixxat_chan_dev_get_ctx(const io_dev_t *dev)
1355{
1356 const struct io_ixxat_chan *ixxat = io_ixxat_chan_from_dev(dev);
1357
1358 return ixxat->ctx;
1359}
1360
1361static ev_exec_t *
1362io_ixxat_chan_dev_get_exec(const io_dev_t *dev)
1363{
1364 const struct io_ixxat_chan *ixxat = io_ixxat_chan_from_dev(dev);
1365
1366 return ixxat->exec;
1367}
1368
1369static size_t
1370io_ixxat_chan_dev_cancel(io_dev_t *dev, struct ev_task *task)
1371{
1372 struct io_ixxat_chan *ixxat = io_ixxat_chan_from_dev(dev);
1373
1374 struct sllist read_queue, write_queue;
1375 sllist_init(&read_queue);
1376 sllist_init(&write_queue);
1377
1378 io_ixxat_chan_pop(ixxat, &read_queue, &write_queue, task);
1379
1380 size_t nread = io_can_chan_read_queue_post(
1381 &read_queue, -1, ERROR_OPERATION_ABORTED);
1382 size_t nwrite = io_can_chan_write_queue_post(
1383 &write_queue, ERROR_OPERATION_ABORTED);
1384 return nwrite < SIZE_MAX - nread ? nread + nwrite : SIZE_MAX;
1385}
1386
1387static size_t
1388io_ixxat_chan_dev_abort(io_dev_t *dev, struct ev_task *task)
1389{
1390 struct io_ixxat_chan *ixxat = io_ixxat_chan_from_dev(dev);
1391
1392 struct sllist queue;
1393 sllist_init(&queue);
1394
1395 io_ixxat_chan_pop(ixxat, &queue, &queue, task);
1396
1397 return ev_task_queue_abort(&queue);
1398}
1399
1400static io_dev_t *
1401io_ixxat_chan_get_dev(const io_can_chan_t *chan)
1402{
1403 const struct io_ixxat_chan *ixxat = io_ixxat_chan_from_chan(chan);
1404
1405 return &ixxat->dev_vptr;
1406}
1407
1408static int
1409io_ixxat_chan_get_flags(const io_can_chan_t *chan)
1410{
1411 const struct io_ixxat_chan *ixxat = io_ixxat_chan_from_chan(chan);
1412
1413#if !LELY_NO_THREADS
1414 EnterCriticalSection((CRITICAL_SECTION *)&ixxat->CriticalSection);
1415#endif
1416 int flags = ixxat->flags;
1417#if !LELY_NO_THREADS
1418 LeaveCriticalSection((CRITICAL_SECTION *)&ixxat->CriticalSection);
1419#endif
1420 return flags;
1421}
1422
1423static int
1424io_ixxat_chan_read(io_can_chan_t *chan, struct can_msg *msg,
1425 struct can_err *err, struct timespec *tp, int timeout)
1426{
1427 struct io_ixxat_chan *ixxat = io_ixxat_chan_from_chan(chan);
1428
1429 return io_ixxat_chan_read_impl(ixxat, msg, err, tp, timeout);
1430}
1431
1432static void
1433io_ixxat_chan_submit_read(io_can_chan_t *chan, struct io_can_chan_read *read)
1434{
1435 struct io_ixxat_chan *ixxat = io_ixxat_chan_from_chan(chan);
1436 assert(read);
1437 struct ev_task *task = &read->task;
1438
1439 if (!task->exec)
1440 task->exec = ixxat->exec;
1441 assert(task->exec);
1443
1444#if !LELY_NO_THREADS
1445 EnterCriticalSection(&ixxat->CriticalSection);
1446#endif
1447 if (ixxat->shutdown) {
1448#if !LELY_NO_THREADS
1449 LeaveCriticalSection(&ixxat->CriticalSection);
1450#endif
1451 io_can_chan_read_post(read, -1, ERROR_OPERATION_ABORTED);
1452 } else {
1453 int post_read = !ixxat->read_posted
1454 && sllist_empty(&ixxat->read_queue);
1455 sllist_push_back(&ixxat->read_queue, &task->_node);
1456 if (post_read)
1457 ixxat->read_posted = 1;
1458#if !LELY_NO_THREADS
1459 LeaveCriticalSection(&ixxat->CriticalSection);
1460#endif
1461 assert(ixxat->read_task.exec);
1462 if (post_read)
1463 ev_exec_post(ixxat->read_task.exec, &ixxat->read_task);
1464 }
1465}
1466
1467static int
1468io_ixxat_chan_write(io_can_chan_t *chan, const struct can_msg *msg, int timeout)
1469{
1470 struct io_ixxat_chan *ixxat = io_ixxat_chan_from_chan(chan);
1471
1472 return io_ixxat_chan_write_impl(ixxat, msg, timeout);
1473}
1474
1475static void
1476io_ixxat_chan_submit_write(io_can_chan_t *chan, struct io_can_chan_write *write)
1477{
1478 struct io_ixxat_chan *ixxat = io_ixxat_chan_from_chan(chan);
1479 assert(write);
1480 assert(write->msg);
1481 struct ev_task *task = &write->task;
1482
1483#if !LELY_NO_CANFD
1484 int flags = 0;
1485 if (write->msg->flags & CAN_FLAG_FDF)
1486 flags |= IO_CAN_BUS_FLAG_FDF;
1487 if (write->msg->flags & CAN_FLAG_BRS)
1488 flags |= IO_CAN_BUS_FLAG_BRS;
1489#endif
1490
1491 if (!task->exec)
1492 task->exec = ixxat->exec;
1493 assert(task->exec);
1495
1496#if !LELY_NO_THREADS
1497 EnterCriticalSection(&ixxat->CriticalSection);
1498#endif
1499 if (ixxat->shutdown) {
1500#if !LELY_NO_THREADS
1501 LeaveCriticalSection(&ixxat->CriticalSection);
1502#endif
1503 io_can_chan_write_post(write, ERROR_OPERATION_ABORTED);
1504#if !LELY_NO_CANFD
1505 } else if ((flags & ixxat->flags) != flags) {
1506#if !LELY_NO_THREADS
1507 LeaveCriticalSection(&ixxat->CriticalSection);
1508#endif
1509 io_can_chan_write_post(write, ERROR_INVALID_PARAMETER);
1510#endif
1511 } else {
1512 int post_write = !ixxat->write_posted
1513 && sllist_empty(&ixxat->write_queue);
1514 sllist_push_back(&ixxat->write_queue, &task->_node);
1515 if (post_write)
1516 ixxat->write_posted = 1;
1517#if !LELY_NO_THREADS
1518 LeaveCriticalSection(&ixxat->CriticalSection);
1519#endif
1520 assert(ixxat->write_task.exec);
1521 if (post_write)
1522 ev_exec_post(ixxat->write_task.exec,
1523 &ixxat->write_task);
1524 }
1525}
1526
1527static void
1528io_ixxat_chan_svc_shutdown(struct io_svc *svc)
1529{
1530 struct io_ixxat_chan *ixxat = io_ixxat_chan_from_svc(svc);
1531 io_dev_t *dev = &ixxat->dev_vptr;
1532
1533#if !LELY_NO_THREADS
1534 EnterCriticalSection(&ixxat->CriticalSection);
1535#endif
1536 int shutdown = !ixxat->shutdown;
1537 ixxat->shutdown = 1;
1538 if (shutdown)
1539 // Try to abort io_ixxat_chan_read_task_func() and
1540 // io_ixxat_chan_write_task_func().
1541 io_ixxat_chan_do_abort_tasks(ixxat);
1542#if !LELY_NO_THREADS
1543 LeaveCriticalSection(&ixxat->CriticalSection);
1544#endif
1545
1546 if (shutdown)
1547 // Cancel all pending operations.
1548 io_ixxat_chan_dev_cancel(dev, NULL);
1549}
1550
1551static void
1552io_ixxat_chan_read_task_func(struct ev_task *task)
1553{
1554 assert(task);
1555 struct io_ixxat_chan *ixxat =
1556 structof(task, struct io_ixxat_chan, read_task);
1557
1558 DWORD dwErrCode = GetLastError();
1559
1560 HANDLE hCanChn = NULL;
1561 struct io_can_chan_read *read = NULL;
1562
1563#if !LELY_NO_THREADS
1564 EnterCriticalSection(&ixxat->CriticalSection);
1565#endif
1566 if (!ixxat->shutdown)
1567 hCanChn = ixxat->hCanChn;
1568 UINT32 dwTscClkFreq = ixxat->dwTscClkFreq;
1569 UINT32 dwTscDivisor = ixxat->dwTscDivisor;
1570 UINT64 qwTime = (UINT64)ixxat->dwTimeOvr << 32;
1571 if ((task = ev_task_from_node(sllist_pop_front(&ixxat->read_queue))))
1572 read = ixxat->current_read = io_can_chan_read_from_task(task);
1573#if !LELY_NO_THREADS
1574 LeaveCriticalSection(&ixxat->CriticalSection);
1575#endif
1576
1577 int result = 0;
1578 int errc = 0;
1579 UINT32 dwTime = 0;
1580 UINT32 dwTimeOvr = 0;
1581 if (read) {
1582 result = io_ixxat_read(hCanChn, read->msg, read->err, &dwTime,
1583 &dwTimeOvr, ixxat->rxtimeo);
1584 if (result >= 0 && read->tp) {
1585 qwTime += ((UINT64)dwTimeOvr << 32) + dwTime;
1586 *read->tp = io_ixxat_time(
1587 qwTime, dwTscClkFreq, dwTscDivisor);
1588 }
1589 if (result == -1) {
1590 errc = GetLastError();
1591 if (errc == ERROR_TIMEOUT)
1592 errc = ERROR_RETRY;
1593 }
1594 }
1595
1596#if !LELY_NO_THREADS
1597 EnterCriticalSection(&ixxat->CriticalSection);
1598#endif
1599 if (hCanChn == ixxat->hCanChn)
1600 ixxat->dwTimeOvr += dwTimeOvr;
1601 // Put the read operation back on the queue if it would block, unless
1602 // it was canceled.
1603 if (result == -1 && errc == ERROR_RETRY
1604 && read == ixxat->current_read) {
1605 assert(read);
1606 sllist_push_front(&ixxat->read_queue, &task->_node);
1607 read = NULL;
1608 }
1609 ixxat->current_read = NULL;
1610 int post_read = ixxat->read_posted =
1611 !sllist_empty(&ixxat->read_queue) && !ixxat->shutdown;
1612#if !LELY_NO_THREADS
1613 LeaveCriticalSection(&ixxat->CriticalSection);
1614#endif
1615 if (read) {
1616 if (result == -1 && errc == ERROR_RETRY)
1617 errc = ERROR_OPERATION_ABORTED;
1618 io_can_chan_read_post(read, result, errc);
1619 }
1620
1621 if (post_read)
1622 ev_exec_post(ixxat->read_task.exec, &ixxat->read_task);
1623
1624 SetLastError(dwErrCode);
1625}
1626
1627static void
1628io_ixxat_chan_write_task_func(struct ev_task *task)
1629{
1630 assert(task);
1631 struct io_ixxat_chan *ixxat =
1632 structof(task, struct io_ixxat_chan, write_task);
1633
1634 DWORD dwErrCode = GetLastError();
1635
1636 HANDLE hCanChn = NULL;
1637 struct io_can_chan_write *write = NULL;
1638
1639#if !LELY_NO_THREADS
1640 EnterCriticalSection(&ixxat->CriticalSection);
1641#endif
1642 if (!ixxat->shutdown)
1643 hCanChn = ixxat->hCanChn;
1644 if ((task = ev_task_from_node(sllist_pop_front(&ixxat->write_queue))))
1645 write = ixxat->current_write =
1647#if !LELY_NO_THREADS
1648 LeaveCriticalSection(&ixxat->CriticalSection);
1649#endif
1650
1651 int result = 0;
1652 int errc = 0;
1653 if (write) {
1654 result = io_ixxat_write(hCanChn, write->msg, ixxat->txtimeo);
1655 if (result == -1) {
1656 errc = GetLastError();
1657 if (errc == ERROR_TIMEOUT)
1658 errc = ERROR_RETRY;
1659 }
1660 }
1661
1662#if !LELY_NO_THREADS
1663 EnterCriticalSection(&ixxat->CriticalSection);
1664#endif
1665 // Put the write operation back on the queue if it would block, unless
1666 // it was canceled.
1667 if (result == -1 && errc == ERROR_RETRY
1668 && write == ixxat->current_write) {
1669 assert(write);
1670 sllist_push_front(&ixxat->write_queue, &task->_node);
1671 write = NULL;
1672 }
1673 ixxat->current_write = NULL;
1674 int post_write = ixxat->write_posted =
1675 !sllist_empty(&ixxat->write_queue) && !ixxat->shutdown;
1676#if !LELY_NO_THREADS
1677 LeaveCriticalSection(&ixxat->CriticalSection);
1678#endif
1679
1680 if (write) {
1681 if (result == -1 && errc == ERROR_RETRY)
1682 errc = ERROR_OPERATION_ABORTED;
1683 io_can_chan_write_post(write, errc);
1684 }
1685
1686 if (post_write)
1687 ev_exec_post(ixxat->write_task.exec, &ixxat->write_task);
1688
1689 SetLastError(dwErrCode);
1690}
1691
1692static inline struct io_ixxat_chan *
1693io_ixxat_chan_from_dev(const io_dev_t *dev)
1694{
1695 assert(dev);
1696
1697 return structof(dev, struct io_ixxat_chan, dev_vptr);
1698}
1699
1700static inline struct io_ixxat_chan *
1701io_ixxat_chan_from_chan(const io_can_chan_t *chan)
1702{
1703 assert(chan);
1704
1705 return structof(chan, struct io_ixxat_chan, chan_vptr);
1706}
1707
1708static inline struct io_ixxat_chan *
1709io_ixxat_chan_from_svc(const struct io_svc *svc)
1710{
1711 assert(svc);
1712
1713 return structof(svc, struct io_ixxat_chan, svc);
1714}
1715
1716static int
1717io_ixxat_chan_read_impl(struct io_ixxat_chan *ixxat, struct can_msg *msg,
1718 struct can_err *err, struct timespec *tp, int timeout)
1719{
1720 assert(ixxat);
1721
1722#if !LELY_NO_THREADS
1723 EnterCriticalSection(&ixxat->CriticalSection);
1724#endif
1725 HANDLE hCanChn = ixxat->hCanChn;
1726 UINT32 dwTscClkFreq = ixxat->dwTscClkFreq;
1727 UINT32 dwTscDivisor = ixxat->dwTscDivisor;
1728 UINT64 qwTime = (UINT64)ixxat->dwTimeOvr << 32;
1729#if !LELY_NO_THREADS
1730 LeaveCriticalSection(&ixxat->CriticalSection);
1731#endif
1732
1733 UINT32 dwTime = 0;
1734 UINT32 dwTimeOvr = 0;
1735 int result = io_ixxat_read(
1736 hCanChn, msg, err, &dwTime, &dwTimeOvr, timeout);
1737 if (result >= 0 && tp) {
1738 qwTime += ((UINT64)dwTimeOvr << 32) + dwTime;
1739 *tp = io_ixxat_time(qwTime, dwTscClkFreq, dwTscDivisor);
1740 }
1741
1742#if !LELY_NO_THREADS
1743 EnterCriticalSection(&ixxat->CriticalSection);
1744#endif
1745 if (hCanChn == ixxat->hCanChn)
1746 ixxat->dwTimeOvr += dwTimeOvr;
1747#if !LELY_NO_THREADS
1748 LeaveCriticalSection(&ixxat->CriticalSection);
1749#endif
1750
1751 return result;
1752}
1753
1754static int
1755io_ixxat_chan_write_impl(struct io_ixxat_chan *ixxat, const struct can_msg *msg,
1756 int timeout)
1757{
1758 assert(ixxat);
1759 assert(msg);
1760
1761#if !LELY_NO_CANFD
1762 int flags = 0;
1763 if (msg->flags & CAN_FLAG_FDF)
1764 flags |= IO_CAN_BUS_FLAG_FDF;
1765 if (msg->flags & CAN_FLAG_BRS)
1766 flags |= IO_CAN_BUS_FLAG_BRS;
1767#endif
1768
1769#if !LELY_NO_THREADS
1770 EnterCriticalSection(&ixxat->CriticalSection);
1771#endif
1772#if !LELY_NO_CANFD
1773 if ((flags & ixxat->flags) != flags) {
1774#if !LELY_NO_THREADS
1775 LeaveCriticalSection(&ixxat->CriticalSection);
1776#endif
1777 SetLastError(ERROR_INVALID_PARAMETER);
1778 return -1;
1779 }
1780#endif
1781 HANDLE hCanChn = ixxat->hCanChn;
1782#if !LELY_NO_THREADS
1783 LeaveCriticalSection(&ixxat->CriticalSection);
1784#endif
1785
1786 return io_ixxat_write(hCanChn, msg, timeout);
1787}
1788
1789static void
1790io_ixxat_chan_pop(struct io_ixxat_chan *ixxat, struct sllist *read_queue,
1791 struct sllist *write_queue, struct ev_task *task)
1792{
1793 assert(ixxat);
1794 assert(read_queue);
1795 assert(write_queue);
1796
1797#if !LELY_NO_THREADS
1798 EnterCriticalSection(&ixxat->CriticalSection);
1799#endif
1800 if (!task) {
1801 sllist_append(read_queue, &ixxat->read_queue);
1802 sllist_append(write_queue, &ixxat->write_queue);
1803 } else if (sllist_remove(&ixxat->read_queue, &task->_node)) {
1804 sllist_push_back(read_queue, &task->_node);
1805 } else if (sllist_remove(&ixxat->write_queue, &task->_node)) {
1806 sllist_push_back(write_queue, &task->_node);
1807 }
1808#if !LELY_NO_THREADS
1809 LeaveCriticalSection(&ixxat->CriticalSection);
1810#endif
1811}
1812
1813static size_t
1814io_ixxat_chan_do_abort_tasks(struct io_ixxat_chan *ixxat)
1815{
1816 assert(ixxat);
1817
1818 size_t n = 0;
1819
1820 // Try to abort io_ixxat_chan_read_task_func().
1821 // clang-format off
1822 if (ixxat->read_posted && ev_exec_abort(ixxat->read_task.exec,
1823 &ixxat->read_task)) {
1824 // clang-format on
1825 ixxat->read_posted = 0;
1826 n++;
1827 }
1828
1829 // Try to abort io_ixxat_chan_write_task_func().
1830 // clang-format off
1831 if (ixxat->write_posted && ev_exec_abort(ixxat->write_task.exec,
1832 &ixxat->write_task)) {
1833 // clang-format on
1834 ixxat->write_posted = 0;
1835 n++;
1836 }
1837
1838 return n;
1839}
1840
1841static HANDLE
1842io_ixxat_chan_set_handle(struct io_ixxat_chan *ixxat, HANDLE hCanChn, int flags,
1843 UINT32 dwTscClkFreq, UINT32 dwTscDivisor)
1844{
1845 assert(ixxat);
1846 assert(!(flags & ~IO_CAN_BUS_FLAG_MASK));
1847
1848 struct sllist read_queue, write_queue;
1849 sllist_init(&read_queue);
1850 sllist_init(&write_queue);
1851
1852#if !LELY_NO_THREADS
1853 EnterCriticalSection(&ixxat->CriticalSection);
1854#endif
1855
1856 HANDLE tmp = ixxat->hCanChn;
1857 ixxat->hCanChn = hCanChn;
1858 hCanChn = tmp;
1859
1860 ixxat->flags = flags;
1861 ixxat->dwTscClkFreq = dwTscClkFreq;
1862 ixxat->dwTscDivisor = dwTscDivisor;
1863 ixxat->dwTimeOvr = 0;
1864
1865 sllist_append(&read_queue, &ixxat->read_queue);
1866 sllist_append(&write_queue, &ixxat->write_queue);
1867
1868 ixxat->current_read = NULL;
1869 ixxat->current_write = NULL;
1870
1871#if !LELY_NO_THREADS
1872 LeaveCriticalSection(&ixxat->CriticalSection);
1873#endif
1874
1875 io_can_chan_read_queue_post(&read_queue, -1, ERROR_OPERATION_ABORTED);
1876 io_can_chan_write_queue_post(&write_queue, ERROR_OPERATION_ABORTED);
1877
1878 return hCanChn;
1879}
1880
1881#endif // !LELY_NO_STDIO && _WIN32 && LELY_HAVE_IXXAT
can_error
The error flags of a CAN bus, which are not mutually exclusive.
Definition err.h:42
can_state
The states of a CAN node, depending on the TX/RX error count.
Definition err.h:28
@ CAN_STATE_BUSOFF
The bus off state (TX/RX error count >= 256).
Definition err.h:34
@ CAN_STATE_PASSIVE
The error passive state (TX/RX error count < 256).
Definition err.h:32
@ CAN_STATE_ACTIVE
The error active state (TX/RX error count < 128).
Definition err.h:30
@ CAN_FLAG_IDE
The Identifier Extension (IDE) flag.
Definition msg.h:43
@ CAN_FLAG_RTR
The Remote Transmission Request (RTR) flag (unavailable in CAN FD format frames).
Definition msg.h:48
@ 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
@ CAN_FLAG_ESI
The Error State Indicator (ESI) flag (only available in CAN FD format frames).
Definition msg.h:67
#define CAN_MASK_EID
The mask used to extract the 29-bit Extended Identifier from a CAN frame.
Definition msg.h:34
#define CAN_MAX_LEN
The maximum number of bytes in the payload of a CAN format frame.
Definition msg.h:72
#define CANFD_MAX_LEN
The maximum number of bytes in the payload of a CAN FD format frame.
Definition msg.h:76
#define CAN_MASK_BID
The mask used to extract the 11-bit Base Identifier from a CAN frame.
Definition msg.h:31
#define CAN_MSG_INIT
The static initializer for can_msg.
Definition msg.h:113
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
This header file is part of the utilities library; it contains the byte order (endianness) function d...
uint_least32_t htole32(uint_least32_t x)
Converts a 32-bit unsigned integer from host to little-endian byte order.
Definition endian.h:393
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
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
int io_can_ctrl_set_bitrate(io_can_ctrl_t *ctrl, int nominal, int data)
Configures the bitrate(s) of a CAN controller.
Definition can.h:433
@ 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
#define LELY_IO_RX_TIMEOUT
The default timeout (in milliseconds) for I/O read operations.
Definition io2.h:31
#define LELY_IO_TX_TIMEOUT
The default timeout (in milliseconds) for I/O write operations.
Definition io2.h:36
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 MIN(a, b)
Returns the minimum of a and b.
Definition util.h:57
const struct io_dev_vtbl *const io_dev_t
An abstract I/O device.
Definition dev.h:35
This header file is part of the I/O library; it contains the IXXAT CAN bus declarations for Windows.
io_can_ctrl_t * io_ixxat_ctrl_create_from_luid(const LUID *lpLuid, UINT32 dwCanNo, int flags, int nominal, int data)
Creates a new IXXAT CAN controller from a locally unique identifier (LUID).
HANDLE io_ixxat_ctrl_get_handle(const io_can_ctrl_t *ctrl)
Returns the native handle of the CAN controller.
io_can_ctrl_t * io_ixxat_ctrl_create_from_index(UINT32 dwIndex, UINT32 dwCanNo, int flags, int nominal, int data)
Creates a new IXXAT CAN controller from a device index.
io_can_chan_t * io_ixxat_chan_create(io_ctx_t *ctx, ev_exec_t *exec, int rxtimeo, int txtimeo)
Creates a new IXXAT CAN channel.
void io_ixxat_fini(void)
Frees the "vcinpl.dll" or "vcinpl2.dll" library and terminates the availability of the IXXAT function...
int io_ixxat_chan_assign(io_can_chan_t *chan, HANDLE hCanChn, UINT32 dwTscClkFreq, UINT32 dwTscDivisor)
Assigns an existing handle to a CAN channel, and activates the channel if necessary.
int io_ixxat_chan_open(io_can_chan_t *chan, const io_can_ctrl_t *ctrl, UINT16 wRxFifoSize, UINT16 wTxFifoSize)
Opens a CAN channel.
int io_ixxat_chan_close(io_can_chan_t *chan)
Closes a CAN channel.
void io_ixxat_ctrl_destroy(io_can_ctrl_t *ctrl)
Destroys an IXXAT CAN controller.
int io_ixxat_init(void)
Loads the "vcinpl.dll" or "vcinpl2.dll" library and makes the IXXAT functions available for use.
void io_ixxat_chan_destroy(io_can_chan_t *chan)
Destroys an IXXAT CAN channel.
int io_ixxat_chan_is_open(const io_can_chan_t *chan)
Returns 1 is the CAN channel is open and 0 if not.
HANDLE io_ixxat_chan_get_handle(const io_can_chan_t *chan)
Returns the native handle of the CAN channel, or NULL if the channel is closed.
HANDLE io_ixxat_chan_release(io_can_chan_t *chan)
Dissociates and returns the native handle from a CAN channel.
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
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
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
This is the internal header file of the Windows-specific I/O declarations.
This header file is part of the C11 and POSIX compatibility library; it includes <stdlib....
This header file is part of the C11 and POSIX compatibility library; it includes <string....
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
uint_least8_t data[CAN_MSG_MAX_LEN]
The frame payload (in case of a data frame).
Definition msg.h:102
uint_least32_t id
The identifier (11 or 29 bits, depending on the CAN_FLAG_IDE flag).
Definition msg.h:89
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
uint_least8_t len
The number of bytes in data (or the requested number of bytes in case of a remote frame).
Definition msg.h:100
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
A singly-linked list.
Definition sllist.h:52
A time type with nanosecond resolution.
Definition time.h:88
long tv_nsec
Nanoseconds [0, 999999999].
Definition time.h:92
time_t tv_sec
Whole seconds (>= 0).
Definition time.h:90
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