You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
153 lines
3.1 KiB
153 lines
3.1 KiB
/* |
|
* 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)); |
|
}
|
|
|