diff options
author | Oliver Hartkopp <socketcan@hartkopp.net> | 2020-11-19 09:49:21 +0100 |
---|---|---|
committer | Marc Kleine-Budde <mkl@pengutronix.de> | 2020-11-20 12:05:14 +0100 |
commit | 94c23097f991cd4568388564b3d2816b0b83f924 (patch) | |
tree | c206200379d41453f89e3d1293d7d87f6bddf324 /net/can | |
parent | 396b3cedc9cf54bf6266a89150136d72af7fcb1e (diff) | |
download | linux-94c23097f991cd4568388564b3d2816b0b83f924.tar.gz linux-94c23097f991cd4568388564b3d2816b0b83f924.tar.bz2 linux-94c23097f991cd4568388564b3d2816b0b83f924.zip |
can: gw: support modification of Classical CAN DLCs
Add support for data length code modifications for Classical CAN.
The netlink configuration interface always allowed to pass any value
that fits into a byte, therefore only the modification process had to be
extended to handle the raw DLC represenation of Classical CAN frames.
When a DLC value from 0 .. F is provided for Classical CAN frame
modifications the 'len' value is modified as-is with the exception that
potentially existing 9 .. F DLC values in the len8_dlc element are moved
to the 'len' element for the modification operation by mod_retrieve_ccdlc().
After the modification the Classical CAN frame DLC information is brought
back into the correct format by mod_store_ccdlc() which is filling 'len'
and 'len8_dlc' accordingly.
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
Link: https://lore.kernel.org/r/20201119084921.2621-1-socketcan@hartkopp.net
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
Diffstat (limited to 'net/can')
-rw-r--r-- | net/can/gw.c | 78 |
1 files changed, 70 insertions, 8 deletions
diff --git a/net/can/gw.c b/net/can/gw.c index de5e8859ec9b..8598d9da0e5f 100644 --- a/net/can/gw.c +++ b/net/can/gw.c @@ -199,6 +199,68 @@ static void mod_set_fddata(struct canfd_frame *cf, struct cf_mod *mod) memcpy(cf->data, mod->modframe.set.data, CANFD_MAX_DLEN); } +/* retrieve valid CC DLC value and store it into 'len' */ +static void mod_retrieve_ccdlc(struct canfd_frame *cf) +{ + struct can_frame *ccf = (struct can_frame *)cf; + + /* len8_dlc is only valid if len == CAN_MAX_DLEN */ + if (ccf->len != CAN_MAX_DLEN) + return; + + /* do we have a valid len8_dlc value from 9 .. 15 ? */ + if (ccf->len8_dlc > CAN_MAX_DLEN && ccf->len8_dlc <= CAN_MAX_RAW_DLC) + ccf->len = ccf->len8_dlc; +} + +/* convert valid CC DLC value in 'len' into struct can_frame elements */ +static void mod_store_ccdlc(struct canfd_frame *cf) +{ + struct can_frame *ccf = (struct can_frame *)cf; + + /* clear potential leftovers */ + ccf->len8_dlc = 0; + + /* plain data length 0 .. 8 - that was easy */ + if (ccf->len <= CAN_MAX_DLEN) + return; + + /* potentially broken values are catched in can_can_gw_rcv() */ + if (ccf->len > CAN_MAX_RAW_DLC) + return; + + /* we have a valid dlc value from 9 .. 15 in ccf->len */ + ccf->len8_dlc = ccf->len; + ccf->len = CAN_MAX_DLEN; +} + +static void mod_and_ccdlc(struct canfd_frame *cf, struct cf_mod *mod) +{ + mod_retrieve_ccdlc(cf); + mod_and_len(cf, mod); + mod_store_ccdlc(cf); +} + +static void mod_or_ccdlc(struct canfd_frame *cf, struct cf_mod *mod) +{ + mod_retrieve_ccdlc(cf); + mod_or_len(cf, mod); + mod_store_ccdlc(cf); +} + +static void mod_xor_ccdlc(struct canfd_frame *cf, struct cf_mod *mod) +{ + mod_retrieve_ccdlc(cf); + mod_xor_len(cf, mod); + mod_store_ccdlc(cf); +} + +static void mod_set_ccdlc(struct canfd_frame *cf, struct cf_mod *mod) +{ + mod_set_len(cf, mod); + mod_store_ccdlc(cf); +} + static void canframecpy(struct canfd_frame *dst, struct can_frame *src) { /* Copy the struct members separately to ensure that no uninitialized @@ -842,8 +904,8 @@ static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod, if (mb.modtype & CGW_MOD_ID) mod->modfunc[modidx++] = mod_and_id; - if (mb.modtype & CGW_MOD_LEN) - mod->modfunc[modidx++] = mod_and_len; + if (mb.modtype & CGW_MOD_DLC) + mod->modfunc[modidx++] = mod_and_ccdlc; if (mb.modtype & CGW_MOD_DATA) mod->modfunc[modidx++] = mod_and_data; @@ -858,8 +920,8 @@ static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod, if (mb.modtype & CGW_MOD_ID) mod->modfunc[modidx++] = mod_or_id; - if (mb.modtype & CGW_MOD_LEN) - mod->modfunc[modidx++] = mod_or_len; + if (mb.modtype & CGW_MOD_DLC) + mod->modfunc[modidx++] = mod_or_ccdlc; if (mb.modtype & CGW_MOD_DATA) mod->modfunc[modidx++] = mod_or_data; @@ -874,8 +936,8 @@ static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod, if (mb.modtype & CGW_MOD_ID) mod->modfunc[modidx++] = mod_xor_id; - if (mb.modtype & CGW_MOD_LEN) - mod->modfunc[modidx++] = mod_xor_len; + if (mb.modtype & CGW_MOD_DLC) + mod->modfunc[modidx++] = mod_xor_ccdlc; if (mb.modtype & CGW_MOD_DATA) mod->modfunc[modidx++] = mod_xor_data; @@ -890,8 +952,8 @@ static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod, if (mb.modtype & CGW_MOD_ID) mod->modfunc[modidx++] = mod_set_id; - if (mb.modtype & CGW_MOD_LEN) - mod->modfunc[modidx++] = mod_set_len; + if (mb.modtype & CGW_MOD_DLC) + mod->modfunc[modidx++] = mod_set_ccdlc; if (mb.modtype & CGW_MOD_DATA) mod->modfunc[modidx++] = mod_set_data; |