canfd: add support for CAN FD in CAN_RAW sockets
Subsystems (2)
Oliver Hartkopp
maintainerPATCH 3/6 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 <[email protected]>
--- include/linux/can/raw.h | 3 ++- net/can/raw.c | 50 +++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 48 insertions(+), 5 deletions(-)
include/linux/can/raw.h +2 -1
@@ -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
net/can/raw.c +31 -1
@@ -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)
@@ -708,7 +738,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; @@ -719,10 +751,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) {
Signature · 1 line
1.7.10.4