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:
		
					parent
					
						
							
								8b01939f35
							
						
					
				
			
			
				commit
				
					
						e2d265d3b5
					
				
			
		
					 2 changed files with 48 additions and 5 deletions
				
			
		|  | @ -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 { | ||||||
| 	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) { | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Oliver Hartkopp
				Oliver Hartkopp