Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

canfd: add support for CAN FD in PF_CAN core

- handle ETH_P_CAN and ETH_P_CANFD skbuffs
- update sanity checks for CAN and CAN FD
- make sure the CAN frame can pass the selected CAN netdevice on send
- bump core version and abi version to indicate the new CAN FD support

Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>

authored by

Oliver Hartkopp and committed by
Marc Kleine-Budde
8b01939f 7c941636

+84 -29
+2 -2
include/linux/can/core.h
··· 17 17 #include <linux/skbuff.h> 18 18 #include <linux/netdevice.h> 19 19 20 - #define CAN_VERSION "20090105" 20 + #define CAN_VERSION "20120528" 21 21 22 22 /* increment this number each time you change some user-space interface */ 23 - #define CAN_ABI_VERSION "8" 23 + #define CAN_ABI_VERSION "9" 24 24 25 25 #define CAN_VERSION_STRING "rev " CAN_VERSION " abi " CAN_ABI_VERSION 26 26
+82 -27
net/can/af_can.c
··· 221 221 * -ENOBUFS on full driver queue (see net_xmit_errno()) 222 222 * -ENOMEM when local loopback failed at calling skb_clone() 223 223 * -EPERM when trying to send on a non-CAN interface 224 + * -EMSGSIZE CAN frame size is bigger than CAN interface MTU 224 225 * -EINVAL when the skb->data does not contain a valid CAN frame 225 226 */ 226 227 int can_send(struct sk_buff *skb, int loop) 227 228 { 228 229 struct sk_buff *newskb = NULL; 229 - struct can_frame *cf = (struct can_frame *)skb->data; 230 - int err; 230 + struct canfd_frame *cfd = (struct canfd_frame *)skb->data; 231 + int err = -EINVAL; 231 232 232 - if (skb->len != sizeof(struct can_frame) || cf->can_dlc > 8) { 233 - kfree_skb(skb); 234 - return -EINVAL; 233 + if (skb->len == CAN_MTU) { 234 + skb->protocol = htons(ETH_P_CAN); 235 + if (unlikely(cfd->len > CAN_MAX_DLEN)) 236 + goto inval_skb; 237 + } else if (skb->len == CANFD_MTU) { 238 + skb->protocol = htons(ETH_P_CANFD); 239 + if (unlikely(cfd->len > CANFD_MAX_DLEN)) 240 + goto inval_skb; 241 + } else 242 + goto inval_skb; 243 + 244 + /* 245 + * Make sure the CAN frame can pass the selected CAN netdevice. 246 + * As structs can_frame and canfd_frame are similar, we can provide 247 + * CAN FD frames to legacy CAN drivers as long as the length is <= 8 248 + */ 249 + if (unlikely(skb->len > skb->dev->mtu && cfd->len > CAN_MAX_DLEN)) { 250 + err = -EMSGSIZE; 251 + goto inval_skb; 235 252 } 236 253 237 - if (skb->dev->type != ARPHRD_CAN) { 238 - kfree_skb(skb); 239 - return -EPERM; 254 + if (unlikely(skb->dev->type != ARPHRD_CAN)) { 255 + err = -EPERM; 256 + goto inval_skb; 240 257 } 241 258 242 - if (!(skb->dev->flags & IFF_UP)) { 243 - kfree_skb(skb); 244 - return -ENETDOWN; 259 + if (unlikely(!(skb->dev->flags & IFF_UP))) { 260 + err = -ENETDOWN; 261 + goto inval_skb; 245 262 } 246 263 247 - skb->protocol = htons(ETH_P_CAN); 248 264 skb_reset_network_header(skb); 249 265 skb_reset_transport_header(skb); 250 266 ··· 317 301 can_stats.tx_frames_delta++; 318 302 319 303 return 0; 304 + 305 + inval_skb: 306 + kfree_skb(skb); 307 + return err; 320 308 } 321 309 EXPORT_SYMBOL(can_send); 322 310 ··· 653 633 return matches; 654 634 } 655 635 656 - static int can_rcv(struct sk_buff *skb, struct net_device *dev, 657 - struct packet_type *pt, struct net_device *orig_dev) 636 + static void can_receive(struct sk_buff *skb, struct net_device *dev) 658 637 { 659 638 struct dev_rcv_lists *d; 660 - struct can_frame *cf = (struct can_frame *)skb->data; 661 639 int matches; 662 - 663 - if (!net_eq(dev_net(dev), &init_net)) 664 - goto drop; 665 - 666 - if (WARN_ONCE(dev->type != ARPHRD_CAN || 667 - skb->len != sizeof(struct can_frame) || 668 - cf->can_dlc > 8, 669 - "PF_CAN: dropped non conform skbuf: " 670 - "dev type %d, len %d, can_dlc %d\n", 671 - dev->type, skb->len, cf->can_dlc)) 672 - goto drop; 673 640 674 641 /* update statistics */ 675 642 can_stats.rx_frames++; ··· 681 674 can_stats.matches++; 682 675 can_stats.matches_delta++; 683 676 } 677 + } 684 678 679 + static int can_rcv(struct sk_buff *skb, struct net_device *dev, 680 + struct packet_type *pt, struct net_device *orig_dev) 681 + { 682 + struct canfd_frame *cfd = (struct canfd_frame *)skb->data; 683 + 684 + if (unlikely(!net_eq(dev_net(dev), &init_net))) 685 + goto drop; 686 + 687 + if (WARN_ONCE(dev->type != ARPHRD_CAN || 688 + skb->len != CAN_MTU || 689 + cfd->len > CAN_MAX_DLEN, 690 + "PF_CAN: dropped non conform CAN skbuf: " 691 + "dev type %d, len %d, datalen %d\n", 692 + dev->type, skb->len, cfd->len)) 693 + goto drop; 694 + 695 + can_receive(skb, dev); 696 + return NET_RX_SUCCESS; 697 + 698 + drop: 699 + kfree_skb(skb); 700 + return NET_RX_DROP; 701 + } 702 + 703 + static int canfd_rcv(struct sk_buff *skb, struct net_device *dev, 704 + struct packet_type *pt, struct net_device *orig_dev) 705 + { 706 + struct canfd_frame *cfd = (struct canfd_frame *)skb->data; 707 + 708 + if (unlikely(!net_eq(dev_net(dev), &init_net))) 709 + goto drop; 710 + 711 + if (WARN_ONCE(dev->type != ARPHRD_CAN || 712 + skb->len != CANFD_MTU || 713 + cfd->len > CANFD_MAX_DLEN, 714 + "PF_CAN: dropped non conform CAN FD skbuf: " 715 + "dev type %d, len %d, datalen %d\n", 716 + dev->type, skb->len, cfd->len)) 717 + goto drop; 718 + 719 + can_receive(skb, dev); 685 720 return NET_RX_SUCCESS; 686 721 687 722 drop: ··· 857 808 858 809 static struct packet_type can_packet __read_mostly = { 859 810 .type = cpu_to_be16(ETH_P_CAN), 860 - .dev = NULL, 861 811 .func = can_rcv, 812 + }; 813 + 814 + static struct packet_type canfd_packet __read_mostly = { 815 + .type = cpu_to_be16(ETH_P_CANFD), 816 + .func = canfd_rcv, 862 817 }; 863 818 864 819 static const struct net_proto_family can_family_ops = { ··· 906 853 sock_register(&can_family_ops); 907 854 register_netdevice_notifier(&can_netdev_notifier); 908 855 dev_add_pack(&can_packet); 856 + dev_add_pack(&canfd_packet); 909 857 910 858 return 0; 911 859 } ··· 921 867 can_remove_proc(); 922 868 923 869 /* protocol unregister */ 870 + dev_remove_pack(&canfd_packet); 924 871 dev_remove_pack(&can_packet); 925 872 unregister_netdevice_notifier(&can_netdev_notifier); 926 873 sock_unregister(PF_CAN);