6#include "can_internal.hxx"
10#include "dawn/io/common.hxx"
11#include "dawn/porting/can.hxx"
15static constexpr size_t CAN_SEGMENT_TRANSFER_MAX = 0x380;
16static constexpr size_t CAN_SINGLE_ID_TRANSFER_MAX = 0x300;
18static size_t canIoDataBytes(
CIOCommon *io)
23#ifdef CONFIG_DAWN_IO_NOTIFY
24int CProtoCan::notifierCb(
void *priv,
io_ddata_t *data)
26 SCanIoData *canio = (SCanIoData *)priv;
32 DAWNERR(
"NULL canio pointer in notifier callback\n");
39 DAWNERR(
"NULL io pointer in notifier callback\n");
43 msg.id = canio->canid;
46 msg.extid = CAN_EXTID_FLAG;
51 return can_send(canio->proto->fd, &msg);
55int CProtoCan::validateCanIo(
const SProtoCanIOBind *v,
CIOCommon *io, SCanIoData *canio)
59#ifdef CONFIG_DAWN_PROTO_CAN_SIMPLE
60# ifdef CONFIG_DAWN_IO_NOTIFY
65 DAWNERR(
"input reg doesn't support read\n");
71 int ret = io->setNotifier(notifierCb, 0, canio);
74 DAWNERR(
"ERROR: set notifier failed for objId = "
81 DAWNERR(
"IO notify required\n");
85 if (canio->data_len > 8)
87 DAWNERR(
"not supported\n");
97# ifdef CONFIG_DAWN_PROTO_CAN_RTR
100 DAWNERR(
"input reg doesn't support read\n");
104 if (canio->data_len > 8)
106 DAWNERR(
"not supported\n");
110 DAWNERR(
"CAN RTR required\n");
120 DAWNERR(
"reg doesn't support write\n");
124 if (canio->data_len > 8)
126 DAWNERR(
"not supported\n");
134#ifdef CONFIG_DAWN_PROTO_CAN_SINGLE_ID
137 if (io->
isRead() ==
false)
139 DAWNERR(
"input reg doesn't support read\n");
150 DAWNERR(
"reg doesn't support write\n");
158#ifdef CONFIG_DAWN_PROTO_CAN_SEG
161 if (io->
isRead() ==
false)
163 DAWNERR(
"input reg doesn't support read\n");
167 if (!io->
isSeekable() && canio->data_len > CAN_SEGMENT_TRANSFER_MAX)
169 DAWNERR(
"not supported\n");
179 DAWNERR(
"reg doesn't support write\n");
183 if (canio->data_len > CAN_SEGMENT_TRANSFER_MAX)
185 DAWNERR(
"not supported\n");
195 DAWNERR(
"unsupported CAN access type\n");
203int CProtoCan::createRegsForGroup(SProtoCanIOBind *v)
208 tmp =
new (std::nothrow) SProtoCanRegs();
211 DAWNERR(
"failed to allocate register handler\n");
215 vregs.push_back(tmp);
220#ifdef CONFIG_DAWN_PROTO_CAN_SINGLE_ID
223 tmp->write_all =
new (std::nothrow) SProtoCanWriteAll();
229 CIsoTp::initState(tmp->write_all->isotp);
230 tmp->write_all->io_idx = 0;
231 tmp->write_all->io_off = 0;
235 tmp->write_all =
nullptr;
239 tmp->io =
new (std::nothrow) SCanIoData[v->size]();
245#ifdef CONFIG_DAWN_PROTO_CAN_SINGLE_ID
246 size_t group_total = 0;
249 for (
size_t i = 0; i < v->size; i++)
253 SCanIoData *canio = &tmp->io[i];
257 io =
getIO(v->objid[i]);
260 DAWNERR(
"Failed to get IO 0x%08" PRIx32
"\n", v->objid[i]);
266 DAWNERR(
"seekable IO is supported only for segmented read "
267 "or write types (objid=0x%08" PRIx32
", type=%" PRIu32
")\n",
273 size_t chunk_size = 0;
283 chunk_size = CAN_SEGMENT_TRANSFER_MAX;
290 DAWNERR(
"failed to allocate register buffer\n");
294 canio->iodata = iobuffer;
296 canio->data_len = canIoDataBytes(io);
302#ifdef CONFIG_DAWN_PROTO_CAN_SINGLE_ID
305 canio->canid = nodeid + v->start;
310 canio->canid = nodeid + v->start + tmp->ios;
313#ifdef DAWN_PROTO_HAS_ISOTP
316 canio->isotp =
new (std::nothrow) CIsoTp::State();
322 CIsoTp::initState(*canio->isotp);
326 canio->isotp =
nullptr;
330#ifdef CONFIG_DAWN_PROTO_CAN_EXTID
331 if (canio->canid > CAN_MAX_EXTMSGID)
333 DAWNERR(
"CAN ID 0x%08" PRIx32
" exceeds extended ID max\n",
334 static_cast<uint32_t
>(canio->canid));
338 if (canio->canid > CAN_MAX_STDMSGID)
340 DAWNERR(
"CAN ID 0x%08" PRIx32
" exceeds standard ID max\n",
341 static_cast<uint32_t
>(canio->canid));
346 ret = validateCanIo(v, io, canio);
352#ifdef CONFIG_DAWN_PROTO_CAN_SINGLE_ID
357 DAWNERR(
"too many IOs for single ID\n");
361 if (canio->data_len > CAN_SINGLE_ID_TRANSFER_MAX)
363 DAWNERR(
"not supported\n");
367 group_total += canio->data_len;
368 if (group_total > CAN_SINGLE_ID_TRANSFER_MAX)
370 DAWNERR(
"not supported\n");
382int CProtoCan::createRegs()
391 for (
auto *v : valloc)
393 ret = createRegsForGroup(v);
405int CProtoCan::destroyRegs()
407 for (
auto *v : vregs)
416 for (
size_t i = 0; i < v->ios; i++)
418 SCanIoData *canio = &v->io[i];
420#ifdef CONFIG_DAWN_IO_NOTIFY
421 if (canio->io && canio->io->isNotify())
423 canio->io->setNotifier(
nullptr, 0,
nullptr);
429 delete canio->iodata;
430 canio->iodata =
nullptr;
433#ifdef DAWN_PROTO_HAS_ISOTP
437 canio->isotp =
nullptr;
446#ifdef CONFIG_DAWN_PROTO_CAN_SINGLE_ID
450 v->write_all =
nullptr;
470#ifdef CONFIG_DAWN_PROTO_CAN_SINGLE_ID
473 ret = handleSingleIdGroup(msg, v);
478 ret = handleLegacyGroup(msg, v);
490#ifdef DAWN_PROTO_HAS_ISOTP
491int CProtoCan::sendSegmented(uint16_t canid,
500 size_t header = with_index ? 2 : 1;
501 size_t payload_max = with_index ? 6 : 7;
503 if (len > payload_max * 0x80)
515 resp.extid = CAN_EXTID_FLAG;
520 resp.data[1] = index;
523 return can_send(fd, &resp);
529 size_t chunk = len - offset;
532 if (chunk > payload_max)
542 resp.len = header + chunk;
544 resp.extid = CAN_EXTID_FLAG;
550 resp.data[0] |= 0x80;
555 resp.data[1] = index;
556 std::memcpy(resp.data + 2, data + offset, chunk);
560 std::memcpy(resp.data + 1, data + offset, chunk);
563 ret = can_send(fd, &resp);
576int CProtoCan::sendSegmentedRead(SCanIoData *canio,
586 size_t payload_max = with_index ? 6 : 7;
587 size_t max_window = payload_max * CAN_SEG_SEQ_MAX;
589 if (!canio || !canio->io || !canio->iodata)
595 iodata = canio->iodata;
600 return sendSegmented(
601 canio->canid,
static_cast<const uint8_t *
>(iodata->
getDataPtr()), len, with_index, index);
604 if (offset >= total_len)
609 if (offset + len > total_len)
611 len = total_len - offset;
614 if (len > max_window)
628 if (buf_off >= buf_len)
632 buf_len = len - sent;
638 ret = io->
getData(*iodata, 1, offset + sent);
647 size_t remaining = buf_len - buf_off;
648 size_t chunk = remaining;
650 if (chunk > payload_max)
655 resp.id = canio->canid;
656 resp.len = (with_index ? 2 : 1) + chunk;
658 resp.extid = CAN_EXTID_FLAG;
662 if ((sent + chunk) >= len)
664 resp.data[0] |= 0x80;
669 resp.data[1] = index;
670 std::memcpy(resp.data + 2, (uint8_t *)iodata->
getDataPtr() + buf_off, chunk);
674 std::memcpy(resp.data + 1, (uint8_t *)iodata->
getDataPtr() + buf_off, chunk);
677 ret = can_send(fd, &resp);
692int CProtoCan::sendIoResponse(SCanIoData *canio, uint8_t index,
bool segmented)
697 data_len = canio->data_len;
698 if (canio->io->isSeekable())
700 data_len = canio->io->getDataSize();
703 if (!segmented || !canio->io->isSeekable())
705 ret = canio->io->getData(*canio->iodata, 1);
708 DAWNERR(
"failed to get IO\n");
722 resp.id = canio->canid;
725 resp.extid = CAN_EXTID_FLAG;
728 std::memcpy(resp.data, canio->iodata->getDataPtr(), resp.len);
730 return can_send(fd, &resp);
733#ifdef DAWN_PROTO_HAS_ISOTP
734 return sendSegmentedRead(canio, data_len, index > 0, index);
740#ifdef CONFIG_DAWN_PROTO_CAN_SINGLE_ID
747 if (!ignore_len && msg.len != 2)
752 for (
size_t i = 0; i < v->ios; i++)
754 SCanIoData *canio = &v->io[i];
756 ret = sendIoResponse(canio,
static_cast<uint8_t
>(i + 1),
true);
772 size_t payload_len = msg.len - 2;
773 size_t payload_off = 0;
774 uint64_t now = canTimestampUs();
775 uint64_t timeout = CONFIG_DAWN_PROTO_CAN_SEG_TIMEOUT_US;
777 if (!v->write_all->isotp.active)
784 v->write_all->isotp.active =
true;
785 v->write_all->isotp.seq_next = 0;
786 v->write_all->io_idx = 0;
787 v->write_all->io_off = 0;
788 v->write_all->isotp.timestamp = now;
790 else if (timeout > 0 && (now - v->write_all->isotp.timestamp) > timeout)
792 v->write_all->isotp.active =
false;
798 v->write_all->isotp.active =
true;
799 v->write_all->isotp.seq_next = 0;
800 v->write_all->io_idx = 0;
801 v->write_all->io_off = 0;
802 v->write_all->isotp.timestamp = now;
805 if (seg_no != v->write_all->isotp.seq_next)
807 v->write_all->isotp.active =
false;
811 while (payload_off < payload_len && v->write_all->io_idx < v->ios)
813 SCanIoData *canio = &v->io[v->write_all->io_idx];
814 size_t need = canio->data_len - v->write_all->io_off;
815 size_t avail = payload_len - payload_off;
816 size_t chunk = (avail < need) ? avail : need;
818 std::memcpy((uint8_t *)canio->iodata->getDataPtr() + v->write_all->io_off,
819 msg.data + 2 + payload_off,
822 v->write_all->io_off += chunk;
823 payload_off += chunk;
825 if (v->write_all->io_off == canio->data_len)
827 ret = canio->io->setData(*canio->iodata);
830 DAWNERR(
"failed to set IO\n");
831 v->write_all->isotp.active =
false;
835 v->write_all->io_idx += 1;
836 v->write_all->io_off = 0;
840 if (payload_off != payload_len)
842 v->write_all->isotp.active =
false;
848 if (v->write_all->io_idx != v->ios)
850 v->write_all->isotp.active =
false;
854 CIsoTp::resetState(v->write_all->isotp);
855 v->write_all->io_idx = 0;
856 v->write_all->io_off = 0;
861 v->write_all->isotp.timestamp = now;
862 v->write_all->isotp.seq_next += 1;
873 SCanIoData *canio = &v->io[index - 1];
877 uint64_t now = canTimestampUs();
878 uint64_t timeout = CONFIG_DAWN_PROTO_CAN_SEG_TIMEOUT_US;
882 canio->iodata->getDataPtr(),
895 ret = canio->io->setData(*canio->iodata);
898 DAWNERR(
"failed to set IO\n");
911 if (canio->io->isRead() ==
false)
913 DAWNERR(
"IO doesn't support read\n");
917 return sendIoResponse(canio, index,
true);
927 if (msg.id != nodeid + v->cfg->start)
937 uint8_t seg = msg.data[0];
938 uint8_t seg_no = seg & 0x7f;
939 bool seg_last = (seg & 0x80) != 0;
940 uint8_t index = msg.data[1];
958 return handleSingleIdReadAll(msg, v,
true);
961 return handleSingleIdWriteAll(msg, v, seg_no, seg_last);
964 if (index > v->cfg->size)
969 return handleSingleIdIndex(msg, v, index, seg_no, seg_last);
977 if (msg.id < nodeid + v->cfg->start || msg.id >= nodeid + v->cfg->start + v->cfg->size)
982 uint8_t idx = msg.id - nodeid - v->cfg->start;
983 SCanIoData *canio = &v->io[idx];
990#ifdef CONFIG_DAWN_PROTO_CAN_SIMPLE
993 if (canio->io->isWrite() ==
false)
995 DAWNERR(
"IO doesn't support write\n");
999 std::memcpy(canio->iodata->getDataPtr(), msg.data, msg.len);
1001 ret = canio->io->setData(*canio->iodata);
1004 DAWNERR(
"failed to set IO\n");
1012#ifdef CONFIG_DAWN_PROTO_CAN_SEG
1026 if (canio->io->isRead() ==
false)
1028 DAWNERR(
"IO doesn't support read\n");
1032 return sendIoResponse(canio, 0,
true);
1037 uint64_t now = canTimestampUs();
1038 uint64_t timeout = CONFIG_DAWN_PROTO_CAN_SEG_TIMEOUT_US;
1049 msg, *canio->isotp, canio->iodata->getDataPtr(), canio->data_len, now, timeout);
1059 payload_len = canio->isotp->total_len;
1061 if (canio->io->isSeekable())
1065 old_size = canio->iodata->N;
1066 canio->iodata->N = payload_len;
1067 ret = canio->io->setData(*canio->iodata);
1068 canio->iodata->N = old_size;
1072 ret = canio->io->setData(*canio->iodata);
1075 CIsoTp::resetState(*canio->isotp);
1078 DAWNERR(
"failed to set IO\n");
1087#ifdef CONFIG_DAWN_PROTO_CAN_RTR
1090# ifdef CONFIG_DAWN_PROTO_CAN_SIMPLE
1093 if (canio->io->isRead() ==
false)
1095 DAWNERR(
"IO doesn't support read\n");
1099 return sendIoResponse(canio, 0,
false);
1103# ifdef CONFIG_DAWN_PROTO_CAN_SEG
CIOCommon * getIO(SObjectId::ObjectId id)
Get an I/O object by ID.
Base class for all I/O objects.
virtual bool isWrite() const =0
Check if IO supports write operations.
int getData(IODataCmn &data, size_t len, size_t offset=0)
Get data from I/O (public interface with stats tracking).
virtual bool isNotify() const =0
Check if IO supports notifications.
virtual bool isSeekable() const
Check if IO supports partial (seekable) access.
io_ddata_t * ddata_alloc(size_t batch, size_t chunk_size=0)
Allocate data buffer for this I/O.
virtual size_t getDataSize() const =0
Get data size in bytes.
virtual bool isRead() const =0
Check if IO supports read operations.
static int receiveCustom(const porting::canmsg_s &msg, State &state, void *data_buf, size_t buf_size, uint8_t seg_no, bool seg_last, uint64_t now, uint64_t timeout)
Process incoming Custom Segmented frame (Dawn specific).
static int receive(const porting::canmsg_s &msg, State &state, void *data_buf, size_t buf_size, uint64_t now, uint64_t timeout)
Process incoming ISO-TP frame.
SObjectId::ObjectId getIdV() const
Get object identifier as raw 32-bit value.
@ CAN_TYPE_READ_SEG
segmented response, requested by data frame
@ CAN_TYPE_WRITE_SEG
segmented request
@ CAN_TYPE_PUSH
device autonomously sends data
@ CAN_TYPE_INDEXED_WRITE
indexed request/response
@ CAN_TYPE_INDEXED_READ
indexed request/response
@ CAN_TYPE_READ
request/response with RTR frame
@ CAN_TYPE_WRITE
master sends command
Out-of-tree user-extension hooks for Dawn.
Heap-allocated dynamic I/O data buffer.
size_t getDataSize()
Get data size in bytes.
void * getDataPtr(size_t batch=0)
Get pointer to data only (skips timestamp if present).
Common CAN message format for chardev and socketCAN.