Enable the PF_CAN infrastructure to handle CAN XL frames. A new ethernet protocol type ETH_P_CANXL is defined to tag skbuffs containing the CAN XL frame data structure. As the length information is now a uint16 value for CAN XL three new helper functions have been introduced to retrieve the data length from all types of CAN frames. Signed-off-by: Oliver Hartkopp <socketcan@xxxxxxxxxxxx> --- include/linux/can/skb.h | 39 +++++++++++++++++++++++++++++++++++ include/uapi/linux/if_ether.h | 1 + net/can/af_can.c | 32 +++++++++++++++++++++++----- 3 files changed, 67 insertions(+), 5 deletions(-) diff --git a/include/linux/can/skb.h b/include/linux/can/skb.h index 182749e858b3..51481f5afe62 100644 --- a/include/linux/can/skb.h +++ b/include/linux/can/skb.h @@ -101,6 +101,45 @@ static inline bool can_is_canfd_skb(const struct sk_buff *skb) { /* the CAN specific type of skb is identified by its data length */ return skb->len == CANFD_MTU; } +static inline bool can_is_canxl_skb(const struct sk_buff *skb) +{ + const struct canxl_frame *cfx = (struct canxl_frame *)skb->data; + + if (skb->len != CANXL_MTU) + return false; + + /* check valid CAN XL data length boundaries */ + if (cfx->len < CANXL_MIN_DLEN || cfx->len > CANXL_MAX_DLEN) + return false; + + return cfx->flags & CANXL_XLF; +} + +/* get length element value from can[|fd|xl]_frame structure */ +static inline unsigned int can_skb_get_len_val(struct sk_buff *skb) +{ + const struct canxl_frame *cfx = (struct canxl_frame *)skb->data; + const struct canfd_frame *cfd = (struct canfd_frame *)skb->data; + + if (can_is_canxl_skb(skb)) + return cfx->len; + + return cfd->len; +} + +/* get needed data length inside of CAN frame for all frame types (RTR aware) */ +static inline unsigned int can_skb_get_data_len(struct sk_buff *skb) +{ + unsigned int len = can_skb_get_len_val(skb); + const struct can_frame *cf = (struct can_frame *)skb->data; + + /* RTR frames have an actual length of zero */ + if (skb->len == CAN_MTU && cf->can_id & CAN_RTR_FLAG) + return 0; + + return len; +} + #endif /* !_CAN_SKB_H */ diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h index d370165bc621..69e0457eb200 100644 --- a/include/uapi/linux/if_ether.h +++ b/include/uapi/linux/if_ether.h @@ -136,10 +136,11 @@ #define ETH_P_WAN_PPP 0x0007 /* Dummy type for WAN PPP frames*/ #define ETH_P_PPP_MP 0x0008 /* Dummy type for PPP MP frames */ #define ETH_P_LOCALTALK 0x0009 /* Localtalk pseudo type */ #define ETH_P_CAN 0x000C /* CAN: Controller Area Network */ #define ETH_P_CANFD 0x000D /* CANFD: CAN flexible data rate*/ +#define ETH_P_CANXL 0x000E /* CANXL: eXtended frame Length */ #define ETH_P_PPPTALK 0x0010 /* Dummy type for Atalk over PPP*/ #define ETH_P_TR_802_2 0x0011 /* 802.2 frames */ #define ETH_P_MOBITEX 0x0015 /* Mobitex (kaz@xxxxxxxx) */ #define ETH_P_CONTROL 0x0016 /* Card specific control frames */ #define ETH_P_IRDA 0x0017 /* Linux-IrDA */ diff --git a/net/can/af_can.c b/net/can/af_can.c index 1fb49d51b25d..23e56e4e2457 100644 --- a/net/can/af_can.c +++ b/net/can/af_can.c @@ -209,19 +209,20 @@ int can_send(struct sk_buff *skb, int loop) goto inval_skb; } else if (skb->len == CANFD_MTU) { skb->protocol = htons(ETH_P_CANFD); if (unlikely(cfd->len > CANFD_MAX_DLEN)) goto inval_skb; + } else if (skb->len == CANXL_MTU) { + skb->protocol = htons(ETH_P_CANXL); + if (unlikely(!can_is_canxl_skb(skb))) + goto inval_skb; } else { goto inval_skb; } - /* Make sure the CAN frame can pass the selected CAN netdevice. - * As structs can_frame and canfd_frame are similar, we can provide - * CAN FD frames to legacy CAN drivers as long as the length is <= 8 - */ - if (unlikely(skb->len > skb->dev->mtu && cfd->len > CAN_MAX_DLEN)) { + /* Make sure the CAN frame can pass the selected CAN netdevice */ + if (unlikely(skb->len > skb->dev->mtu)) { err = -EMSGSIZE; goto inval_skb; } if (unlikely(skb->dev->type != ARPHRD_CAN)) { @@ -725,10 +726,25 @@ static int canfd_rcv(struct sk_buff *skb, struct net_device *dev, free_skb: kfree_skb(skb); return NET_RX_DROP; } +static int canxl_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) +{ + if (unlikely(dev->type != ARPHRD_CAN || (!can_is_canxl_skb(skb)))) { + pr_warn_once("PF_CAN: dropped non conform CAN XL skbuff: dev type %d, len %d\n", + dev->type, skb->len); + + kfree_skb(skb); + return NET_RX_DROP; + } + + can_receive(skb, dev); + return NET_RX_SUCCESS; +} + /* af_can protocol functions */ /** * can_proto_register - register CAN transport protocol * @cp: pointer to CAN protocol structure @@ -849,10 +865,15 @@ static struct packet_type can_packet __read_mostly = { static struct packet_type canfd_packet __read_mostly = { .type = cpu_to_be16(ETH_P_CANFD), .func = canfd_rcv, }; +static struct packet_type canxl_packet __read_mostly = { + .type = cpu_to_be16(ETH_P_CANXL), + .func = canxl_rcv, +}; + static const struct net_proto_family can_family_ops = { .family = PF_CAN, .create = can_create, .owner = THIS_MODULE, }; @@ -888,10 +909,11 @@ static __init int can_init(void) if (err) goto out_sock; dev_add_pack(&can_packet); dev_add_pack(&canfd_packet); + dev_add_pack(&canxl_packet); return 0; out_sock: unregister_pernet_subsys(&can_pernet_ops); -- 2.30.2