diff options
author | Oliver Hartkopp <socketcan@hartkopp.net> | 2012-06-13 14:41:31 -0400 |
---|---|---|
committer | Marc Kleine-Budde <mkl@pengutronix.de> | 2012-06-19 15:40:08 -0400 |
commit | e2d265d3b587f5f6f8febc0222aace93302ff0be (patch) | |
tree | 36530611615f19f3591955de0517aaac7d1a1766 /net | |
parent | 8b01939f358d680cea971151375268cfdb6b9635 (diff) |
canfd: add support for CAN FD in CAN_RAW sockets
- introduce a new sockopt CAN_RAW_FD_FRAMES to allow CAN FD frames
- handle CAN frames and CAN FD frames simultaneously when enabled
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
Diffstat (limited to 'net')
-rw-r--r-- | net/can/raw.c | 50 |
1 files changed, 46 insertions, 4 deletions
diff --git a/net/can/raw.c b/net/can/raw.c index 46cca3a91d19..3e9c89356a93 100644 --- a/net/can/raw.c +++ b/net/can/raw.c | |||
@@ -82,6 +82,7 @@ struct raw_sock { | |||
82 | struct notifier_block notifier; | 82 | struct notifier_block notifier; |
83 | int loopback; | 83 | int loopback; |
84 | int recv_own_msgs; | 84 | int recv_own_msgs; |
85 | int fd_frames; | ||
85 | int count; /* number of active filters */ | 86 | int count; /* number of active filters */ |
86 | struct can_filter dfilter; /* default/single filter */ | 87 | struct can_filter dfilter; /* default/single filter */ |
87 | struct can_filter *filter; /* pointer to filter(s) */ | 88 | struct can_filter *filter; /* pointer to filter(s) */ |
@@ -119,6 +120,14 @@ static void raw_rcv(struct sk_buff *oskb, void *data) | |||
119 | if (!ro->recv_own_msgs && oskb->sk == sk) | 120 | if (!ro->recv_own_msgs && oskb->sk == sk) |
120 | return; | 121 | return; |
121 | 122 | ||
123 | /* do not pass frames with DLC > 8 to a legacy socket */ | ||
124 | if (!ro->fd_frames) { | ||
125 | struct canfd_frame *cfd = (struct canfd_frame *)oskb->data; | ||
126 | |||
127 | if (unlikely(cfd->len > CAN_MAX_DLEN)) | ||
128 | return; | ||
129 | } | ||
130 | |||
122 | /* clone the given skb to be able to enqueue it into the rcv queue */ | 131 | /* clone the given skb to be able to enqueue it into the rcv queue */ |
123 | skb = skb_clone(oskb, GFP_ATOMIC); | 132 | skb = skb_clone(oskb, GFP_ATOMIC); |
124 | if (!skb) | 133 | if (!skb) |
@@ -291,6 +300,7 @@ static int raw_init(struct sock *sk) | |||
291 | /* set default loopback behaviour */ | 300 | /* set default loopback behaviour */ |
292 | ro->loopback = 1; | 301 | ro->loopback = 1; |
293 | ro->recv_own_msgs = 0; | 302 | ro->recv_own_msgs = 0; |
303 | ro->fd_frames = 0; | ||
294 | 304 | ||
295 | /* set notifier */ | 305 | /* set notifier */ |
296 | ro->notifier.notifier_call = raw_notifier; | 306 | ro->notifier.notifier_call = raw_notifier; |
@@ -569,6 +579,15 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, | |||
569 | 579 | ||
570 | break; | 580 | break; |
571 | 581 | ||
582 | case CAN_RAW_FD_FRAMES: | ||
583 | if (optlen != sizeof(ro->fd_frames)) | ||
584 | return -EINVAL; | ||
585 | |||
586 | if (copy_from_user(&ro->fd_frames, optval, optlen)) | ||
587 | return -EFAULT; | ||
588 | |||
589 | break; | ||
590 | |||
572 | default: | 591 | default: |
573 | return -ENOPROTOOPT; | 592 | return -ENOPROTOOPT; |
574 | } | 593 | } |
@@ -627,6 +646,12 @@ static int raw_getsockopt(struct socket *sock, int level, int optname, | |||
627 | val = &ro->recv_own_msgs; | 646 | val = &ro->recv_own_msgs; |
628 | break; | 647 | break; |
629 | 648 | ||
649 | case CAN_RAW_FD_FRAMES: | ||
650 | if (len > sizeof(int)) | ||
651 | len = sizeof(int); | ||
652 | val = &ro->fd_frames; | ||
653 | break; | ||
654 | |||
630 | default: | 655 | default: |
631 | return -ENOPROTOOPT; | 656 | return -ENOPROTOOPT; |
632 | } | 657 | } |
@@ -662,8 +687,13 @@ static int raw_sendmsg(struct kiocb *iocb, struct socket *sock, | |||
662 | } else | 687 | } else |
663 | ifindex = ro->ifindex; | 688 | ifindex = ro->ifindex; |
664 | 689 | ||
665 | if (size != sizeof(struct can_frame)) | 690 | if (ro->fd_frames) { |
666 | return -EINVAL; | 691 | if (unlikely(size != CANFD_MTU && size != CAN_MTU)) |
692 | return -EINVAL; | ||
693 | } else { | ||
694 | if (unlikely(size != CAN_MTU)) | ||
695 | return -EINVAL; | ||
696 | } | ||
667 | 697 | ||
668 | dev = dev_get_by_index(&init_net, ifindex); | 698 | dev = dev_get_by_index(&init_net, ifindex); |
669 | if (!dev) | 699 | if (!dev) |
@@ -705,7 +735,9 @@ static int raw_recvmsg(struct kiocb *iocb, struct socket *sock, | |||
705 | struct msghdr *msg, size_t size, int flags) | 735 | struct msghdr *msg, size_t size, int flags) |
706 | { | 736 | { |
707 | struct sock *sk = sock->sk; | 737 | struct sock *sk = sock->sk; |
738 | struct raw_sock *ro = raw_sk(sk); | ||
708 | struct sk_buff *skb; | 739 | struct sk_buff *skb; |
740 | int rxmtu; | ||
709 | int err = 0; | 741 | int err = 0; |
710 | int noblock; | 742 | int noblock; |
711 | 743 | ||
@@ -716,10 +748,20 @@ static int raw_recvmsg(struct kiocb *iocb, struct socket *sock, | |||
716 | if (!skb) | 748 | if (!skb) |
717 | return err; | 749 | return err; |
718 | 750 | ||
719 | if (size < skb->len) | 751 | /* |
752 | * when serving a legacy socket the DLC <= 8 is already checked inside | ||
753 | * raw_rcv(). Now check if we need to pass a canfd_frame to a legacy | ||
754 | * socket and cut the possible CANFD_MTU/CAN_MTU length to CAN_MTU | ||
755 | */ | ||
756 | if (!ro->fd_frames) | ||
757 | rxmtu = CAN_MTU; | ||
758 | else | ||
759 | rxmtu = skb->len; | ||
760 | |||
761 | if (size < rxmtu) | ||
720 | msg->msg_flags |= MSG_TRUNC; | 762 | msg->msg_flags |= MSG_TRUNC; |
721 | else | 763 | else |
722 | size = skb->len; | 764 | size = rxmtu; |
723 | 765 | ||
724 | err = memcpy_toiovec(msg->msg_iov, skb->data, size); | 766 | err = memcpy_toiovec(msg->msg_iov, skb->data, size); |
725 | if (err < 0) { | 767 | if (err < 0) { |