| /* |
| * Copyright (c) 2019 Intel Corporation |
| * Copyright (c) 2022 Martin Jäger <martin@libre.solar> |
| * |
| * 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 <stdio.h> |
| #include <stdlib.h> |
| #include <stdarg.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <stdbool.h> |
| |
| /* Linux host include files. */ |
| #ifdef __linux |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <sys/ioctl.h> |
| #include <sys/socket.h> |
| #include <sys/select.h> |
| #include <net/if.h> |
| #include <linux/if.h> |
| #include <linux/can.h> |
| #include <linux/can/raw.h> |
| #else |
| #error "This driver can only be built on Linux systems" |
| #endif |
| |
| #include "can_native_linux_adapt.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 - 1); |
| |
| 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; |
| } |
| |
| int 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 = (int)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; |
| } |
| |
| 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)); |
| } |