mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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…
	
		Reference in a new issue