/* * Copyright (c) 2019 Intel Corporation * Copyright (c) 2022 Martin Jäger * * SPDX-License-Identifier: Apache-2.0 */ /** * @file * * Routines setting up the host system. Those are placed in separate file * because there is naming conflicts between host and zephyr network stacks. */ #include #include #include #include #include #include /* Linux host include files. */ #ifdef __linux #include #include #include #include #include #include #include #include #else #error "This driver can only be built on Linux systems" #endif #include "can_native_posix_linux_socketcan.h" #ifndef CANFD_FDF /* Linux kernels before v5.14 do not define CANFD_FDF */ #define CANFD_FDF 0x04 #endif /* CANFD_FDF */ int linux_socketcan_iface_open(const char *if_name) { struct sockaddr_can addr; struct ifreq ifr; int fd, opt, ret = -EINVAL; fd = socket(PF_CAN, SOCK_RAW, CAN_RAW); if (fd < 0) { return -errno; } (void)memset(&ifr, 0, sizeof(ifr)); (void)memset(&addr, 0, sizeof(addr)); strncpy(ifr.ifr_name, if_name, IFNAMSIZ); ret = ioctl(fd, SIOCGIFINDEX, (void *)&ifr); if (ret < 0) { close(fd); return -errno; } addr.can_ifindex = ifr.ifr_ifindex; addr.can_family = PF_CAN; ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); if (ret < 0) { close(fd); return -errno; } /* this option must always be enabled in order to receive TX confirmations */ opt = 1; ret = setsockopt(fd, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, &opt, sizeof(opt)); if (ret < 0) { close(fd); return -errno; } return fd; } int linux_socketcan_iface_close(int fd) { return close(fd); } int linux_socketcan_poll_data(int fd) { struct timeval timeout; fd_set rset; int ret; FD_ZERO(&rset); FD_SET(fd, &rset); timeout.tv_sec = 0; timeout.tv_usec = 0; ret = select(fd + 1, &rset, NULL, NULL, &timeout); if (ret < 0 && errno != EINTR) { return -errno; } else if (ret > 0) { if (FD_ISSET(fd, &rset)) { return 0; } } return -EAGAIN; } ssize_t linux_socketcan_read_data(int fd, void *buf, size_t buf_len, bool *msg_confirm) { struct canfd_frame *frame = (struct canfd_frame *)buf; struct msghdr msg = {0}; struct iovec iov = { .iov_base = buf, .iov_len = buf_len, }; msg.msg_iov = &iov; msg.msg_iovlen = 1; int ret = recvmsg(fd, &msg, MSG_WAITALL); if (msg_confirm != NULL) { *msg_confirm = (msg.msg_flags & MSG_CONFIRM) != 0; } /* Make sure to set the flags for all frames received via the Linux API. * * Zephyr relies on defined flags field of the SocketCAN data for both FD and classical CAN * frames. In Linux the flags field is undefined for legacy frames. */ if (ret == CANFD_MTU) { frame->flags |= CANFD_FDF; } else if (ret == CAN_MTU) { frame->flags = 0; } return ret; } ssize_t linux_socketcan_write_data(int fd, void *buf, size_t buf_len) { return write(fd, buf, buf_len); } int linux_socketcan_setsockopt(int fd, int level, int optname, const void *optval, socklen_t optlen) { return setsockopt(fd, level, optname, optval, optlen); } int linux_socketcan_getsockopt(int fd, int level, int optname, void *optval, socklen_t *optlen) { return getsockopt(fd, level, optname, optval, optlen); } int linux_socketcan_set_mode_fd(int fd, bool mode_fd) { int opt = mode_fd ? 1 : 0; return setsockopt(fd, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &opt, sizeof(opt)); }