zephyr/drivers/can/can_native_linux_adapt.c
Alberto Escolar Piedras 5382f827d6 drivers can_native_linux: Avoid using ssize
ssize is a POSIX.1-2001 extension, which may or may
not be provided by the C library, or may be defined
to a different size in the host and embedded C library.

Two internal functions were returning ssize, but
one of them was a trampoline into the same host API,
which is already provided by the native simulator
so let's just use that instead.

The other is only carrying data that fits into an
int and is anyhow being cast in/to ints, so let's just
avoid the trouble by defining it as returning int.

Signed-off-by: Alberto Escolar Piedras <alberto.escolar.piedras@nordicsemi.no>
2024-01-15 21:59:56 +01:00

154 lines
3.1 KiB
C

/*
* 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));
}