159 lines
3.8 KiB
C
159 lines
3.8 KiB
C
/*
|
|
* 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_DEFINE_MSGQ(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(uint32_t error_flags, void *arg)
|
|
{
|
|
char *caller_str = (char *)arg;
|
|
if (error_flags) {
|
|
LOG_DBG("TX error from %s! error-code: %d",
|
|
caller_str, error_flags);
|
|
}
|
|
}
|
|
|
|
/* 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_attach_msgq(socket_context->can_dev, socket_context->msgq,
|
|
optval);
|
|
if (ret == CAN_NO_FREE_FILTER) {
|
|
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_detach(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 msg;
|
|
int ret;
|
|
|
|
ARG_UNUSED(unused1);
|
|
ARG_UNUSED(unused2);
|
|
|
|
while (1) {
|
|
k_msgq_get((struct k_msgq *)socket_context->msgq, &msg,
|
|
K_FOREVER);
|
|
|
|
pkt = net_pkt_rx_alloc_with_buffer(socket_context->iface,
|
|
sizeof(msg),
|
|
AF_CAN, 0,
|
|
BUF_ALLOC_TIMEOUT);
|
|
if (!pkt) {
|
|
LOG_ERR("Failed to obtain RX buffer");
|
|
continue;
|
|
}
|
|
|
|
if (net_pkt_write(pkt, (void *)&msg, sizeof(msg))) {
|
|
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_ */
|