canfd: add support for CAN FD in CAN_RAW sockets
authorOliver Hartkopp <socketcan@hartkopp.net>
Wed, 13 Jun 2012 18:41:31 +0000 (20:41 +0200)
committerMarc Kleine-Budde <mkl@pengutronix.de>
Tue, 19 Jun 2012 19:40:08 +0000 (21:40 +0200)
- 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>
include/linux/can/raw.h
net/can/raw.c

index 781f3a3..a814062 100644 (file)
@@ -23,7 +23,8 @@ enum {
        CAN_RAW_FILTER = 1,     /* set 0 .. n can_filter(s)          */
        CAN_RAW_ERR_FILTER,     /* set filter for error frames       */
        CAN_RAW_LOOPBACK,       /* local loopback (default:on)       */
-       CAN_RAW_RECV_OWN_MSGS   /* receive my own msgs (default:off) */
+       CAN_RAW_RECV_OWN_MSGS,  /* receive my own msgs (default:off) */
+       CAN_RAW_FD_FRAMES,      /* allow CAN FD frames (default:off) */
 };
 
 #endif
index 46cca3a..3e9c893 100644 (file)
@@ -82,6 +82,7 @@ struct raw_sock {
        struct notifier_block notifier;
        int loopback;
        int recv_own_msgs;
+       int fd_frames;
        int count;                 /* number of active filters */
        struct can_filter dfilter; /* default/single filter */
        struct can_filter *filter; /* pointer to filter(s) */
@@ -119,6 +120,14 @@ static void raw_rcv(struct sk_buff *oskb, void *data)
        if (!ro->recv_own_msgs && oskb->sk == sk)
                return;
 
+       /* do not pass frames with DLC > 8 to a legacy socket */
+       if (!ro->fd_frames) {
+               struct canfd_frame *cfd = (struct canfd_frame *)oskb->data;
+
+               if (unlikely(cfd->len > CAN_MAX_DLEN))
+                       return;
+       }
+
        /* clone the given skb to be able to enqueue it into the rcv queue */
        skb = skb_clone(oskb, GFP_ATOMIC);
        if (!skb)
@@ -291,6 +300,7 @@ static int raw_init(struct sock *sk)
        /* set default loopback behaviour */
        ro->loopback         = 1;
        ro->recv_own_msgs    = 0;
+       ro->fd_frames        = 0;
 
        /* set notifier */
        ro->notifier.notifier_call = raw_notifier;
@@ -569,6 +579,15 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
 
                break;
 
+       case CAN_RAW_FD_FRAMES:
+               if (optlen != sizeof(ro->fd_frames))
+                       return -EINVAL;
+
+               if (copy_from_user(&ro->fd_frames, optval, optlen))
+                       return -EFAULT;
+
+               break;
+
        default:
                return -ENOPROTOOPT;
        }
@@ -627,6 +646,12 @@ static int raw_getsockopt(struct socket *sock, int level, int optname,
                val = &ro->recv_own_msgs;
                break;
 
+       case CAN_RAW_FD_FRAMES:
+               if (len > sizeof(int))
+                       len = sizeof(int);
+               val = &ro->fd_frames;
+               break;
+
        default:
                return -ENOPROTOOPT;
        }
@@ -662,8 +687,13 @@ static int raw_sendmsg(struct kiocb *iocb, struct socket *sock,
        } else
                ifindex = ro->ifindex;
 
-       if (size != sizeof(struct can_frame))
-               return -EINVAL;
+       if (ro->fd_frames) {
+               if (unlikely(size != CANFD_MTU && size != CAN_MTU))
+                       return -EINVAL;
+       } else {
+               if (unlikely(size != CAN_MTU))
+                       return -EINVAL;
+       }
 
        dev = dev_get_by_index(&init_net, ifindex);
        if (!dev)
@@ -705,7 +735,9 @@ static int raw_recvmsg(struct kiocb *iocb, struct socket *sock,
                       struct msghdr *msg, size_t size, int flags)
 {
        struct sock *sk = sock->sk;
+       struct raw_sock *ro = raw_sk(sk);
        struct sk_buff *skb;
+       int rxmtu;
        int err = 0;
        int noblock;
 
@@ -716,10 +748,20 @@ static int raw_recvmsg(struct kiocb *iocb, struct socket *sock,
        if (!skb)
                return err;
 
-       if (size < skb->len)
+       /*
+        * when serving a legacy socket the DLC <= 8 is already checked inside
+        * raw_rcv(). Now check if we need to pass a canfd_frame to a legacy
+        * socket and cut the possible CANFD_MTU/CAN_MTU length to CAN_MTU
+        */
+       if (!ro->fd_frames)
+               rxmtu = CAN_MTU;
+       else
+               rxmtu = skb->len;
+
+       if (size < rxmtu)
                msg->msg_flags |= MSG_TRUNC;
        else
-               size = skb->len;
+               size = rxmtu;
 
        err = memcpy_toiovec(msg->msg_iov, skb->data, size);
        if (err < 0) {