blob: c9c8dc1760cccd3fc3f594d78d31b05171903290 [file] [log] [blame]
/*
* Copyright (c) 2019 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
/* CANBUS related functions that are generic in all the drivers. */
#include <net/net_pkt.h>
#include <net/socket_can.h>
#ifndef ZEPHYR_DRIVERS_CAN_SOCKET_CAN_GENERIC_H_
#define ZEPHYR_DRIVERS_CAN_SOCKET_CAN_GENERIC_H_
#define SOCKET_CAN_NAME_0 "SOCKET_CAN_0"
#define SOCKET_CAN_NAME_1 "SOCKET_CAN_1"
#define SOCKET_CAN_NAME_2 "SOCKET_CAN_2"
#define SEND_TIMEOUT K_MSEC(100)
#define RX_THREAD_STACK_SIZE 512
#define RX_THREAD_PRIORITY 2
#define BUF_ALLOC_TIMEOUT K_MSEC(50)
/* TODO: make msgq size configurable */
CAN_MSGQ_DEFINE(socket_can_msgq, 5);
K_KERNEL_STACK_DEFINE(rx_thread_stack, RX_THREAD_STACK_SIZE);
struct socket_can_context {
const struct device *can_dev;
struct net_if *iface;
struct k_msgq *msgq;
/* TODO: remove the thread and push data to net directly from rx isr */
k_tid_t rx_tid;
struct k_thread rx_thread_data;
};
static inline void socket_can_iface_init(struct net_if *iface)
{
const struct device *dev = net_if_get_device(iface);
struct socket_can_context *socket_context = dev->data;
socket_context->iface = iface;
LOG_DBG("Init CAN interface %p dev %p", iface, dev);
}
static inline void tx_irq_callback(const struct device *dev, int error, void *arg)
{
char *caller_str = (char *)arg;
ARG_UNUSED(dev);
if (error != 0) {
LOG_DBG("TX error from %s! error-code: %d",
caller_str, error);
}
}
/* This is called by net_if.c when packet is about to be sent */
static inline int socket_can_send(const struct device *dev,
struct net_pkt *pkt)
{
struct socket_can_context *socket_context = dev->data;
int ret;
if (net_pkt_family(pkt) != AF_CAN) {
return -EPFNOSUPPORT;
}
ret = can_send(socket_context->can_dev,
(struct zcan_frame *)pkt->frags->data,
SEND_TIMEOUT, tx_irq_callback, "socket_can_send");
if (ret) {
LOG_DBG("Cannot send socket CAN msg (%d)", ret);
}
/* If something went wrong, then we need to return negative value to
* net_if.c:net_if_tx() so that the net_pkt will get released.
*/
return -ret;
}
static inline int socket_can_setsockopt(const struct device *dev, void *obj,
int level, int optname,
const void *optval, socklen_t optlen)
{
struct socket_can_context *socket_context = dev->data;
struct net_context *ctx = obj;
int ret;
if (level != SOL_CAN_RAW && optname != CAN_RAW_FILTER) {
errno = EINVAL;
return -1;
}
__ASSERT_NO_MSG(optlen == sizeof(struct zcan_filter));
ret = can_add_rx_filter_msgq(socket_context->can_dev, socket_context->msgq,
optval);
if (ret == -ENOSPC) {
errno = ENOSPC;
return -1;
}
net_context_set_filter_id(ctx, ret);
return 0;
}
static inline void socket_can_close(const struct device *dev, int filter_id)
{
struct socket_can_context *socket_context = dev->data;
can_remove_rx_filter(socket_context->can_dev, filter_id);
}
static struct canbus_api socket_can_api = {
.iface_api.init = socket_can_iface_init,
.send = socket_can_send,
.close = socket_can_close,
.setsockopt = socket_can_setsockopt,
};
static inline void rx_thread(void *ctx, void *unused1, void *unused2)
{
struct socket_can_context *socket_context = ctx;
struct net_pkt *pkt;
struct zcan_frame frame;
int ret;
ARG_UNUSED(unused1);
ARG_UNUSED(unused2);
while (1) {
k_msgq_get((struct k_msgq *)socket_context->msgq, &frame,
K_FOREVER);
pkt = net_pkt_rx_alloc_with_buffer(socket_context->iface,
sizeof(frame),
AF_CAN, 0,
BUF_ALLOC_TIMEOUT);
if (!pkt) {
LOG_ERR("Failed to obtain RX buffer");
continue;
}
if (net_pkt_write(pkt, (void *)&frame, sizeof(frame))) {
LOG_ERR("Failed to append RX data");
net_pkt_unref(pkt);
continue;
}
ret = net_recv_data(socket_context->iface, pkt);
if (ret < 0) {
net_pkt_unref(pkt);
}
}
}
#endif /* ZEPHYR_DRIVERS_CAN_SOCKET_CAN_GENERIC_H_ */