Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] Add PF_CAN protocol for socket CAN #11454

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion include/net/net_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
#include <net/net_if.h>
#include <net/net_stats.h>

#include <net/socket_can.h>

#ifdef __cplusplus
extern "C" {
#endif
Expand Down Expand Up @@ -287,7 +289,7 @@ struct net_context {
u8_t iface;

/** Flags for the context */
u8_t flags;
u16_t flags;
};

static inline bool net_context_is_used(struct net_context *context)
Expand Down Expand Up @@ -354,6 +356,11 @@ static inline sa_family_t net_context_get_family(struct net_context *context)
return AF_INET6;
}

if (IS_ENABLED(CONFIG_SOCKET_CAN) &&
(context->flags & CAN_CONTEXT_FAMILY)) {
return AF_CAN;
}

return AF_INET;
}

Expand All @@ -376,6 +383,12 @@ static inline void net_context_set_family(struct net_context *context,
return;
}

if (IS_ENABLED(CONFIG_SOCKET_CAN) &&
(family == AF_CAN)) {
context->flags |= CAN_CONTEXT_FAMILY;
return;
}

context->flags &= ~NET_CONTEXT_FAMILY;
}

Expand All @@ -398,6 +411,11 @@ enum net_sock_type net_context_get_type(struct net_context *context)
return SOCK_STREAM;
}

if (IS_ENABLED(CONFIG_SOCKET_CAN) &&
(context->flags & CAN_CONTEXT_FAMILY)) {
return SOCK_RAW;
}

return SOCK_DGRAM;
}

Expand All @@ -420,6 +438,12 @@ static inline void net_context_set_type(struct net_context *context,
return;
}

if (IS_ENABLED(CONFIG_SOCKET_CAN) &&
(type == SOCK_RAW)) {
context->flags |= CAN_CONTEXT_TYPE;
return;
}

context->flags &= ~NET_CONTEXT_TYPE;
}

Expand All @@ -442,6 +466,11 @@ enum net_ip_protocol net_context_get_ip_proto(struct net_context *context)
return IPPROTO_TCP;
}

if (IS_ENABLED(CONFIG_SOCKET_CAN) &&
(context->flags & CAN_CONTEXT_PROTO)) {
return CAN_RAW;
}

return IPPROTO_UDP;
}

Expand All @@ -464,6 +493,12 @@ static inline void net_context_set_ip_proto(struct net_context *context,
return;
}

if (IS_ENABLED(CONFIG_SOCKET_CAN) &&
(ip_proto == CAN_RAW)) {
context->flags |= CAN_CONTEXT_PROTO;
return;
}

context->flags &= ~NET_CONTEXT_PROTO;
}

Expand Down
4 changes: 4 additions & 0 deletions include/net/net_ip.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,21 @@ extern "C" {
#define PF_UNSPEC 0 /* Unspecified. */
#define PF_INET 2 /* IP protocol family. */
#define PF_INET6 10 /* IP version 6. */
#define PF_CAN 13 /* CAN protocol family */

/** Address families. */
#define AF_UNSPEC PF_UNSPEC
#define AF_INET PF_INET
#define AF_INET6 PF_INET6
#define AF_CAN PF_CAN /* CAN address family */

/** Protocol numbers from IANA */
enum net_ip_protocol {
IPPROTO_ICMP = 1,
IPPROTO_TCP = 6,
IPPROTO_UDP = 17,
IPPROTO_ICMPV6 = 58,
CAN_RAW = 252,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How CAN (and raw even), can be part of IP protocol? Is it transported through IP? Just asking, I don't know CAN.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"The SocketCAN concept extends the Berkeley sockets API in Linux by introducing a new protocol family, PF_CAN, that coexists with other protocol families like PF_INET for the Internet Protocol. The communication with the CAN bus is therefore done analogously to the use of the Internet Protocol via sockets."
Source: https://en.wikipedia.org/wiki/SocketCAN

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Few comments here:

  1. I'm relatively fine with this particular change, the problem can be seen as in enum naming.
  2. BUT, linux has it as a separate define in can.h, and I would like to extend my suggestion is not to invent our own special ways and follow well-known/established API examples (and belonging of symbols to headers is part of API in C).
  3. "The communication with the CAN bus is therefore done analogously to the use of the Internet Protocol via sockets." - "Analogously" does not mean "the same". Because otherwise, absolutely all protocols work analogously - shoving bits back and forth. But somehow, this patch patches the IP protocol files and not I2C or MQTT. Looking a bit down the same Wikipedia article, example shows that what is sent over CAN sockets is struct can_frame. This is very, very different from normal TCP or UDP sockets.

};

/* Protocol numbers for TLS protocols */
Expand All @@ -67,6 +70,7 @@ enum net_ip_protocol_secure {
enum net_sock_type {
SOCK_STREAM = 1,
SOCK_DGRAM,
SOCK_RAW,
};

#define ntohs(x) sys_be16_to_cpu(x)
Expand Down
6 changes: 6 additions & 0 deletions include/net/net_l2.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,12 @@ NET_L2_DECLARE_PUBLIC(BLUETOOTH_L2);
NET_L2_DECLARE_PUBLIC(OPENTHREAD_L2);
#endif /* CONFIG_NET_L2_OPENTHREAD */

#ifdef CONFIG_NET_L2_CAN
#define CAN_L2 CAN
#define CAN_L2_CTX_TYPE void*
NET_L2_DECLARE_PUBLIC(CAN_L2);
#endif /* CONFIG_NET_L2_CAN */

#define NET_L2_INIT(_name, _recv_fn, _send_fn, _reserve_fn, _enable_fn, \
_get_flags_fn) \
const struct net_l2 (NET_L2_GET_NAME(_name)) __used \
Expand Down
14 changes: 14 additions & 0 deletions include/net/socket.h
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ struct zsock_pollfd {
*/
#define SOL_TLS 282

/* Protocl level of CAN */
#define SOL_CAN_BASE 100
#define SOL_CAN_RAW (SOL_CAN_BASE + CAN_RAW)

/* Socket options for TLS */

/* Socket option to select TLS credentials to use. It accepts and returns an
Expand Down Expand Up @@ -93,6 +97,16 @@ struct zsock_pollfd {
* 0 - client,
* 1 - server.
*/

/* Socket optaions for CAN */

/* Get ifindex for corresponding socket*/
#define SOCKET_CAN_GET_IF_INDEX 6
/* Confifgure CAN controller operation mode defined in can.h */
#define SOCKET_CAN_SET_MODE 7
/* Configure CAN filter for acceptance filtering */
#define SOCKET_CAN_SET_FILTER 8

#define TLS_DTLS_ROLE 6

struct zsock_addrinfo {
Expand Down
158 changes: 158 additions & 0 deletions include/net/socket_can.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
* Copyright (c) 2018 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef _SOCKET_CAN_H_
#define _SOCKET_CAN_H_

#include <device.h>
#include <net/net_ip.h>
#include <can.h>
#include <zephyr/types.h>

/** Max length of socket packet */
#define CAN_NET_MTU (72)

/** Protocol family of socket can connection */
#define CAN_CONTEXT_FAMILY BIT(8)

/** Context type of socket can connection */
#define CAN_CONTEXT_TYPE BIT(9)

/** Context proto for socket can connection */
#define CAN_CONTEXT_PROTO BIT(10)

/**
* @brief socket addr structure for AF_CAN protocol
*
* Socket address structure for AF_CAN protocol family
*
*/
struct sockaddr_can {
/** protocol family for socket can */
sa_family_t can_family;
/** net_if interface associated with socket */
u8_t can_ifindex;
};

/**
* @brief check matched CAN filter for incoming CAN message.
*
* This routine matches id with configured filters id and return true or false.
* *
* @param dev Pointer to the device structure for the driver instance.
* @param pkt incoming net_pkt
*
* @retval 1 if filter matched otherwise 0.
*/
typedef int (*socket_can_check_filter_matched_t)(struct device *dev,
struct net_pkt *pkt);

/**
* @brief configure filter for accpetance filter.
*
* This routine configure can filter and register rx callback.
* *
* @param dev Pointer to the device structure for the driver instance.
* @param filter can_filter structure
*
* @retval return < 0 in case of error and 0 for success.
*/
typedef int (*socket_can_config_filter_t)(struct device *dev,
struct can_filter *filter);

int socket_can_get_opt(struct net_context *context, int optname,
void *optval, socklen_t *optlen);
int socket_can_set_opt(struct net_context *context, int optname,
const void *optval, socklen_t optlen);



/**
* @brief socket can driver api structure
* Socket CAN driver apis for iface interface functions and ioctl call.
*
*/
struct socket_can_driver_api_t {
/** iface interface for socket apis to send and init function*/
struct net_if_api iface_api;
/** check matching filter for loopback messages */
socket_can_check_filter_matched_t check_matched_filter;
/** configure filter with rx callback */
socket_can_config_filter_t config_filter;
};

/**
* @brief socket can context structure
*
* socket can driver specific structure.
*
*/
struct socket_can_context {
/** id of CAN instance */
u32_t id;
/** iface interface reference structure*/
struct net_if *iface;
/** can device structure reference */
struct device *can_dev;
};

/**
* @brief socket can mode structure
*
* This structure will be passed via setSocketOpt and configure can controller
* operation mode as well as baud rate
*/
struct socket_can_mode {
enum can_mode op_mode;
u32_t baud_rate;
};

__syscall int socket_can_check_filter_matched(struct device *dev,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar comment to the above - no socket_can_check_filter_matched() function in POSIX.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pfalcon: What if we get this patch in as-is and update it as soon as you'll be ready with setsockopt code?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@laperie that's not a good idea actually

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you actually need a function like socket_can_check_filter_matched?
If you don't set a filter that matches, the CAN driver will never receive a message and therefore if you get a message from the can API it passed the filter already.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexanderwachter This function implements local loopback functionality mentioned in section 3.2 of https://www.kernel.org/doc/Documentation/networking/can.txt. And in https://www.can-cia.org/fileadmin/resources/documents/proceedings/2012_kleine-budde.pdf document it is mentioned to implement it via software filtering. So this function checks if sent CAN packet Id is matched with any of configured filter id on sender node then socket application will receive this locally loopbacked packet.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Be aware that the loopback mode bit does not only check the filter and send the message back if it matches. It also set the ACK bit while sending. Basically it acknowledges its own package.
This makes a difference when your device is alone on the bus. If you do not set the loopback mode here, the controller will try to send the message forever.

Copy link
Author

@vipinana vipinana Nov 27, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This loopback is not via can controller. It will check the matched filter in SW layer only and if matched then connection callback will be trigger with this pkt and socket recv call will get the packet. So for every sent packet this check will happen and if current mode of CAN is loopback mode then same mode will receive two packets. One from CAN controller and another from sw filtering.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed ioctl calls and implemented getsockopt and setsockopt APIs for configuring can mode, bit rate and filters.

struct net_pkt *pkt);

static inline int _impl_socket_can_check_filter_matched(struct device *dev,
struct net_pkt *pkt)
{
const struct socket_can_driver_api_t *api = NULL;
int result = 0;

if (pkt == NULL) {
goto err;
}

api = dev->driver_api;
if (api && api->check_matched_filter) {
result = api->check_matched_filter(dev, pkt);
}
err:
return result;
}

__syscall int socket_can_config_filter(struct device *dev,
struct can_filter *filter);

static inline int _impl_socket_can_config_filter(struct device *dev,
struct can_filter *filter)
{
const struct socket_can_driver_api_t *api = NULL;
int res = 0;

if (filter == NULL) {
res = -EBADF;
goto err;
}

api = dev->driver_api;
if (api && api->config_filter) {
res = api->config_filter(dev, filter);
}
err:
return res;
}

#include <syscalls/socket_can.h>

#endif /*_SOCKET_CAN_H_ */
1 change: 1 addition & 0 deletions subsys/net/ip/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ zephyr_library_sources_ifdef(CONFIG_NET_TCP connection.c tcp.c)
zephyr_library_sources_ifdef(CONFIG_NET_TRICKLE trickle.c)
zephyr_library_sources_ifdef(CONFIG_NET_UDP connection.c udp.c)
zephyr_library_sources_ifdef(CONFIG_NET_PROMISCUOUS_MODE promiscuous.c)
zephyr_library_sources_ifdef(CONFIG_SOCKET_CAN connection.c)

if(CONFIG_NET_SHELL)
zephyr_library_include_directories(. ${ZEPHYR_BASE}/subsys/net/l2)
Expand Down
49 changes: 47 additions & 2 deletions subsys/net/ip/connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,10 @@ int net_conn_register(enum net_ip_protocol proto,
}
} else
#endif
{
if (IS_ENABLED(CONFIG_SOCKET_CAN) &&
(local_addr->sa_family == AF_CAN)) {
conns[i].local_addr.sa_family = AF_CAN;
} else {
NET_ERR("Remote address family not set");
return -EINVAL;
}
Expand Down Expand Up @@ -639,7 +642,12 @@ int net_conn_register(enum net_ip_protocol proto,
}
} else
#endif
{
if (IS_ENABLED(CONFIG_SOCKET_CAN) &&
(local_addr->sa_family == AF_CAN)) {
memcpy(&conns[i].local_addr,
local_addr,
sizeof(struct sockaddr_can));
} else {
NET_ERR("Local address family not set");
return -EINVAL;
}
Expand Down Expand Up @@ -1053,3 +1061,40 @@ void net_conn_init(void)
} while (0);
#endif /* CONFIG_NET_CONN_CACHE */
}
#if defined(CONFIG_SOCKET_CAN)
extern bool socket_can_check_matched_id_filter(struct net_pkt *pkt);

enum net_verdict can_conn_input(struct net_pkt *pkt, bool loopback)
{
int i;
int ret = NET_DROP;

if (!pkt || !(pkt->frags)) {
ret = NET_CONTINUE;
goto err;
}
for (i = 0; i < CONFIG_NET_MAX_CONN; i++) {
if ((conns[i].flags & NET_CONN_IN_USE) &&
(conns[i].proto == CAN_RAW)) {
if (loopback) {
if (!socket_can_check_matched_id_filter(pkt)) {
/* No filter matched but dont unref */
/* net_pkt data */
ret = NET_OK;
goto err;
}
}

if (conns[i].cb(&conns[i], pkt, conns[i].user_data)
== NET_DROP) {
goto err;
}
ret = NET_OK;
break;
}
}
err:
return ret;
}
#endif

Loading