Commit e2d265d3 authored by Oliver Hartkopp's avatar Oliver Hartkopp Committed by Marc Kleine-Budde

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: default avatarOliver Hartkopp <socketcan@hartkopp.net>
Signed-off-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
parent 8b01939f
...@@ -23,7 +23,8 @@ enum { ...@@ -23,7 +23,8 @@ enum {
CAN_RAW_FILTER = 1, /* set 0 .. n can_filter(s) */ CAN_RAW_FILTER = 1, /* set 0 .. n can_filter(s) */
CAN_RAW_ERR_FILTER, /* set filter for error frames */ CAN_RAW_ERR_FILTER, /* set filter for error frames */
CAN_RAW_LOOPBACK, /* local loopback (default:on) */ 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 #endif
...@@ -82,6 +82,7 @@ struct raw_sock { ...@@ -82,6 +82,7 @@ struct raw_sock {
struct notifier_block notifier; struct notifier_block notifier;
int loopback; int loopback;
int recv_own_msgs; int recv_own_msgs;
int fd_frames;
int count; /* number of active filters */ int count; /* number of active filters */
struct can_filter dfilter; /* default/single filter */ struct can_filter dfilter; /* default/single filter */
struct can_filter *filter; /* pointer to filter(s) */ struct can_filter *filter; /* pointer to filter(s) */
...@@ -119,6 +120,14 @@ static void raw_rcv(struct sk_buff *oskb, void *data) ...@@ -119,6 +120,14 @@ static void raw_rcv(struct sk_buff *oskb, void *data)
if (!ro->recv_own_msgs && oskb->sk == sk) if (!ro->recv_own_msgs && oskb->sk == sk)
return; 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 */ /* clone the given skb to be able to enqueue it into the rcv queue */
skb = skb_clone(oskb, GFP_ATOMIC); skb = skb_clone(oskb, GFP_ATOMIC);
if (!skb) if (!skb)
...@@ -291,6 +300,7 @@ static int raw_init(struct sock *sk) ...@@ -291,6 +300,7 @@ static int raw_init(struct sock *sk)
/* set default loopback behaviour */ /* set default loopback behaviour */
ro->loopback = 1; ro->loopback = 1;
ro->recv_own_msgs = 0; ro->recv_own_msgs = 0;
ro->fd_frames = 0;
/* set notifier */ /* set notifier */
ro->notifier.notifier_call = raw_notifier; ro->notifier.notifier_call = raw_notifier;
...@@ -569,6 +579,15 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, ...@@ -569,6 +579,15 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
break; 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: default:
return -ENOPROTOOPT; return -ENOPROTOOPT;
} }
...@@ -627,6 +646,12 @@ static int raw_getsockopt(struct socket *sock, int level, int optname, ...@@ -627,6 +646,12 @@ static int raw_getsockopt(struct socket *sock, int level, int optname,
val = &ro->recv_own_msgs; val = &ro->recv_own_msgs;
break; break;
case CAN_RAW_FD_FRAMES:
if (len > sizeof(int))
len = sizeof(int);
val = &ro->fd_frames;
break;
default: default:
return -ENOPROTOOPT; return -ENOPROTOOPT;
} }
...@@ -662,8 +687,13 @@ static int raw_sendmsg(struct kiocb *iocb, struct socket *sock, ...@@ -662,8 +687,13 @@ static int raw_sendmsg(struct kiocb *iocb, struct socket *sock,
} else } else
ifindex = ro->ifindex; ifindex = ro->ifindex;
if (size != sizeof(struct can_frame)) if (ro->fd_frames) {
return -EINVAL; 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); dev = dev_get_by_index(&init_net, ifindex);
if (!dev) if (!dev)
...@@ -705,7 +735,9 @@ static int raw_recvmsg(struct kiocb *iocb, struct socket *sock, ...@@ -705,7 +735,9 @@ static int raw_recvmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t size, int flags) struct msghdr *msg, size_t size, int flags)
{ {
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
struct raw_sock *ro = raw_sk(sk);
struct sk_buff *skb; struct sk_buff *skb;
int rxmtu;
int err = 0; int err = 0;
int noblock; int noblock;
...@@ -716,10 +748,20 @@ static int raw_recvmsg(struct kiocb *iocb, struct socket *sock, ...@@ -716,10 +748,20 @@ static int raw_recvmsg(struct kiocb *iocb, struct socket *sock,
if (!skb) if (!skb)
return err; 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; msg->msg_flags |= MSG_TRUNC;
else else
size = skb->len; size = rxmtu;
err = memcpy_toiovec(msg->msg_iov, skb->data, size); err = memcpy_toiovec(msg->msg_iov, skb->data, size);
if (err < 0) { if (err < 0) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment