Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -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
+46 −4
Original line number Diff line number Diff line
@@ -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))
	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) {