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 <socketcan@hartkopp.net>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
This commit is contained in:
Oliver Hartkopp 2012-06-13 20:41:31 +02:00 committed by Marc Kleine-Budde
parent 8b01939f35
commit e2d265d3b5
2 changed files with 48 additions and 5 deletions

View file

@ -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

View file

@ -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)
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)
/* 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,
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,
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,
} 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,
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,
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) {