1 /*
2  * Copyright (c) 2019 Intel Corporation
3  * Copyright (c) 2022 Martin Jäger <martin@libre.solar>
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 /**
9  * @file
10  *
11  * Routines setting up the host system. Those are placed in separate file
12  * because there is naming conflicts between host and zephyr network stacks.
13  */
14 
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <stdarg.h>
18 #include <errno.h>
19 #include <string.h>
20 #include <stdbool.h>
21 
22 /* Linux host include files. */
23 #ifdef __linux
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <sys/ioctl.h>
27 #include <sys/socket.h>
28 #include <sys/select.h>
29 #include <net/if.h>
30 #include <linux/can.h>
31 #include <linux/can/raw.h>
32 #else
33 #error "This driver can only be built on Linux systems"
34 #endif
35 
36 #include "can_native_posix_linux_socketcan.h"
37 
38 #ifndef CANFD_FDF
39 /* Linux kernels before v5.14 do not define CANFD_FDF */
40 #define CANFD_FDF 0x04
41 #endif /* CANFD_FDF */
42 
linux_socketcan_iface_open(const char * if_name)43 int linux_socketcan_iface_open(const char *if_name)
44 {
45 	struct sockaddr_can addr;
46 	struct ifreq ifr;
47 	int fd, opt, ret = -EINVAL;
48 
49 	fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
50 	if (fd < 0) {
51 		return -errno;
52 	}
53 
54 	(void)memset(&ifr, 0, sizeof(ifr));
55 	(void)memset(&addr, 0, sizeof(addr));
56 
57 	strncpy(ifr.ifr_name, if_name, IFNAMSIZ);
58 
59 	ret = ioctl(fd, SIOCGIFINDEX, (void *)&ifr);
60 	if (ret < 0) {
61 		close(fd);
62 		return -errno;
63 	}
64 
65 	addr.can_ifindex = ifr.ifr_ifindex;
66 	addr.can_family = PF_CAN;
67 
68 	ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
69 	if (ret < 0) {
70 		close(fd);
71 		return -errno;
72 	}
73 
74 	/* this option must always be enabled in order to receive TX confirmations */
75 	opt = 1;
76 	ret = setsockopt(fd, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, &opt, sizeof(opt));
77 	if (ret < 0) {
78 		close(fd);
79 		return -errno;
80 	}
81 
82 	return fd;
83 }
84 
linux_socketcan_iface_close(int fd)85 int linux_socketcan_iface_close(int fd)
86 {
87 	return close(fd);
88 }
89 
linux_socketcan_poll_data(int fd)90 int linux_socketcan_poll_data(int fd)
91 {
92 	struct timeval timeout;
93 	fd_set rset;
94 	int ret;
95 
96 	FD_ZERO(&rset);
97 
98 	FD_SET(fd, &rset);
99 
100 	timeout.tv_sec = 0;
101 	timeout.tv_usec = 0;
102 
103 	ret = select(fd + 1, &rset, NULL, NULL, &timeout);
104 	if (ret < 0 && errno != EINTR) {
105 		return -errno;
106 	} else if (ret > 0) {
107 		if (FD_ISSET(fd, &rset)) {
108 			return 0;
109 		}
110 	}
111 
112 	return -EAGAIN;
113 }
114 
linux_socketcan_read_data(int fd,void * buf,size_t buf_len,bool * msg_confirm)115 ssize_t linux_socketcan_read_data(int fd, void *buf, size_t buf_len, bool *msg_confirm)
116 {
117 	struct canfd_frame *frame = (struct canfd_frame *)buf;
118 	struct msghdr msg = {0};
119 
120 	struct iovec iov = {
121 		.iov_base = buf,
122 		.iov_len = buf_len,
123 	};
124 	msg.msg_iov = &iov;
125 	msg.msg_iovlen = 1;
126 
127 	int ret = recvmsg(fd, &msg, MSG_WAITALL);
128 
129 	if (msg_confirm != NULL) {
130 		*msg_confirm = (msg.msg_flags & MSG_CONFIRM) != 0;
131 	}
132 
133 	/* Make sure to set the flags for all frames received via the Linux API.
134 	 *
135 	 * Zephyr relies on defined flags field of the SocketCAN data for both FD and classical CAN
136 	 * frames. In Linux the flags field is undefined for legacy frames.
137 	 */
138 	if (ret == CANFD_MTU) {
139 		frame->flags |= CANFD_FDF;
140 	} else if (ret == CAN_MTU) {
141 		frame->flags = 0;
142 	}
143 
144 	return ret;
145 }
146 
linux_socketcan_write_data(int fd,void * buf,size_t buf_len)147 ssize_t linux_socketcan_write_data(int fd, void *buf, size_t buf_len)
148 {
149 	return write(fd, buf, buf_len);
150 }
151 
linux_socketcan_setsockopt(int fd,int level,int optname,const void * optval,socklen_t optlen)152 int linux_socketcan_setsockopt(int fd, int level, int optname,
153 			       const void *optval, socklen_t optlen)
154 {
155 	return setsockopt(fd, level, optname, optval, optlen);
156 }
157 
linux_socketcan_getsockopt(int fd,int level,int optname,void * optval,socklen_t * optlen)158 int linux_socketcan_getsockopt(int fd, int level, int optname,
159 			       void *optval, socklen_t *optlen)
160 {
161 	return getsockopt(fd, level, optname, optval, optlen);
162 }
163 
linux_socketcan_set_mode_fd(int fd,bool mode_fd)164 int linux_socketcan_set_mode_fd(int fd, bool mode_fd)
165 {
166 	int opt = mode_fd ? 1 : 0;
167 
168 	return setsockopt(fd, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &opt, sizeof(opt));
169 }
170