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/if.h>
31 #include <linux/can.h>
32 #include <linux/can/raw.h>
33 #else
34 #error "This driver can only be built on Linux systems"
35 #endif
36
37 #include "can_native_linux_adapt.h"
38
39 #ifndef CANFD_FDF
40 /* Linux kernels before v5.14 do not define CANFD_FDF */
41 #define CANFD_FDF 0x04
42 #endif /* CANFD_FDF */
43
linux_socketcan_iface_open(const char * if_name)44 int linux_socketcan_iface_open(const char *if_name)
45 {
46 struct sockaddr_can addr;
47 struct ifreq ifr;
48 int fd, opt, ret = -EINVAL;
49
50 fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
51 if (fd < 0) {
52 return -errno;
53 }
54
55 (void)memset(&ifr, 0, sizeof(ifr));
56 (void)memset(&addr, 0, sizeof(addr));
57
58 strncpy(ifr.ifr_name, if_name, IFNAMSIZ - 1);
59
60 ret = ioctl(fd, SIOCGIFINDEX, (void *)&ifr);
61 if (ret < 0) {
62 close(fd);
63 return -errno;
64 }
65
66 addr.can_ifindex = ifr.ifr_ifindex;
67 addr.can_family = PF_CAN;
68
69 ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
70 if (ret < 0) {
71 close(fd);
72 return -errno;
73 }
74
75 /* this option must always be enabled in order to receive TX confirmations */
76 opt = 1;
77 ret = setsockopt(fd, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, &opt, sizeof(opt));
78 if (ret < 0) {
79 close(fd);
80 return -errno;
81 }
82
83 return fd;
84 }
85
linux_socketcan_iface_close(int fd)86 int linux_socketcan_iface_close(int fd)
87 {
88 return close(fd);
89 }
90
linux_socketcan_poll_data(int fd)91 int linux_socketcan_poll_data(int fd)
92 {
93 struct timeval timeout;
94 fd_set rset;
95 int ret;
96
97 FD_ZERO(&rset);
98
99 FD_SET(fd, &rset);
100
101 timeout.tv_sec = 0;
102 timeout.tv_usec = 0;
103
104 ret = select(fd + 1, &rset, NULL, NULL, &timeout);
105 if (ret < 0 && errno != EINTR) {
106 return -errno;
107 } else if (ret > 0) {
108 if (FD_ISSET(fd, &rset)) {
109 return 0;
110 }
111 }
112
113 return -EAGAIN;
114 }
115
linux_socketcan_read_data(int fd,void * buf,size_t buf_len,bool * msg_confirm)116 int linux_socketcan_read_data(int fd, void *buf, size_t buf_len, bool *msg_confirm)
117 {
118 struct canfd_frame *frame = (struct canfd_frame *)buf;
119 struct msghdr msg = {0};
120
121 struct iovec iov = {
122 .iov_base = buf,
123 .iov_len = buf_len,
124 };
125 msg.msg_iov = &iov;
126 msg.msg_iovlen = 1;
127
128 int ret = (int)recvmsg(fd, &msg, MSG_WAITALL);
129
130 if (msg_confirm != NULL) {
131 *msg_confirm = (msg.msg_flags & MSG_CONFIRM) != 0;
132 }
133
134 /* Make sure to set the flags for all frames received via the Linux API.
135 *
136 * Zephyr relies on defined flags field of the SocketCAN data for both FD and classical CAN
137 * frames. In Linux the flags field is undefined for legacy frames.
138 */
139 if (ret == CANFD_MTU) {
140 frame->flags |= CANFD_FDF;
141 } else if (ret == CAN_MTU) {
142 frame->flags = 0;
143 }
144
145 return ret;
146 }
147
linux_socketcan_set_mode_fd(int fd,bool mode_fd)148 int linux_socketcan_set_mode_fd(int fd, bool mode_fd)
149 {
150 int opt = mode_fd ? 1 : 0;
151
152 return setsockopt(fd, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &opt, sizeof(opt));
153 }
154