Skip to content

Commit

Permalink
canfd: add support for CAN FD in CAN_RAW sockets
Browse files Browse the repository at this point in the history
- 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 <[email protected]>
Signed-off-by: Marc Kleine-Budde <[email protected]>
  • Loading branch information
hartkopp authored and marckleinebudde committed Jun 19, 2012
1 parent 8b01939 commit e2d265d
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 5 deletions.
3 changes: 2 additions & 1 deletion include/linux/can/raw.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
50 changes: 46 additions & 4 deletions net/can/raw.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) */
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;

Expand All @@ -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) {
Expand Down

0 comments on commit e2d265d

Please sign in to comment.