net: sockets: can: Close the socket cleanly
If the socket is closed, then do CAN detach if that is needed. This way the CAN interrupts are not received if there are no CAN sockets listening the data. Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
This commit is contained in:
parent
7e37fd7203
commit
06b500b6bd
5 changed files with 150 additions and 20 deletions
|
@ -80,6 +80,7 @@ static inline int socket_can_setsockopt(struct device *dev, void *obj,
|
||||||
const void *optval, socklen_t optlen)
|
const void *optval, socklen_t optlen)
|
||||||
{
|
{
|
||||||
struct socket_can_context *socket_context = dev->driver_data;
|
struct socket_can_context *socket_context = dev->driver_data;
|
||||||
|
struct net_context *ctx = obj;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (level != SOL_CAN_RAW && optname != CAN_RAW_FILTER) {
|
if (level != SOL_CAN_RAW && optname != CAN_RAW_FILTER) {
|
||||||
|
@ -96,12 +97,22 @@ static inline int socket_can_setsockopt(struct device *dev, void *obj,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
net_context_set_filter_id(ctx, ret);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void socket_can_close(struct device *dev, int filter_id)
|
||||||
|
{
|
||||||
|
struct socket_can_context *socket_context = dev->driver_data;
|
||||||
|
|
||||||
|
can_detach(socket_context->can_dev, filter_id);
|
||||||
|
}
|
||||||
|
|
||||||
static struct canbus_api socket_can_api = {
|
static struct canbus_api socket_can_api = {
|
||||||
.iface_api.init = socket_can_iface_init,
|
.iface_api.init = socket_can_iface_init,
|
||||||
.send = socket_can_send,
|
.send = socket_can_send,
|
||||||
|
.close = socket_can_close,
|
||||||
.setsockopt = socket_can_setsockopt,
|
.setsockopt = socket_can_setsockopt,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -274,6 +274,10 @@ struct net_context {
|
||||||
void *offload_context;
|
void *offload_context;
|
||||||
#endif /* CONFIG_NET_OFFLOAD */
|
#endif /* CONFIG_NET_OFFLOAD */
|
||||||
|
|
||||||
|
#if defined(CONFIG_NET_SOCKETS_CAN)
|
||||||
|
int can_filter_id;
|
||||||
|
#endif /* CONFIG_NET_SOCKETS_CAN */
|
||||||
|
|
||||||
/** Option values */
|
/** Option values */
|
||||||
struct {
|
struct {
|
||||||
#if defined(CONFIG_NET_CONTEXT_PRIORITY)
|
#if defined(CONFIG_NET_CONTEXT_PRIORITY)
|
||||||
|
@ -432,6 +436,56 @@ static inline void net_context_set_type(struct net_context *context,
|
||||||
context->flags |= flag;
|
context->flags |= flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set CAN filter id for this network context.
|
||||||
|
*
|
||||||
|
* @details This function sets the CAN filter id of the context.
|
||||||
|
*
|
||||||
|
* @param context Network context.
|
||||||
|
* @param filter_id CAN filter id
|
||||||
|
*/
|
||||||
|
#if defined(CONFIG_NET_SOCKETS_CAN)
|
||||||
|
static inline void net_context_set_filter_id(struct net_context *context,
|
||||||
|
int filter_id)
|
||||||
|
{
|
||||||
|
NET_ASSERT(context);
|
||||||
|
|
||||||
|
context->can_filter_id = filter_id;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static inline void net_context_set_filter_id(struct net_context *context,
|
||||||
|
int filter_id)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(context);
|
||||||
|
ARG_UNUSED(filter_id);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get CAN filter id for this network context.
|
||||||
|
*
|
||||||
|
* @details This function gets the CAN filter id of the context.
|
||||||
|
*
|
||||||
|
* @param context Network context.
|
||||||
|
*
|
||||||
|
* @return Filter id of this network context
|
||||||
|
*/
|
||||||
|
#if defined(CONFIG_NET_SOCKETS_CAN)
|
||||||
|
static inline int net_context_get_filter_id(struct net_context *context)
|
||||||
|
{
|
||||||
|
NET_ASSERT(context);
|
||||||
|
|
||||||
|
return context->can_filter_id;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static inline int net_context_get_filter_id(struct net_context *context)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(context);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get context IP protocol for this network context.
|
* @brief Get context IP protocol for this network context.
|
||||||
*
|
*
|
||||||
|
|
|
@ -65,6 +65,9 @@ struct canbus_api {
|
||||||
/** Send a CAN packet by socket */
|
/** Send a CAN packet by socket */
|
||||||
int (*send)(struct device *dev, struct net_pkt *pkt);
|
int (*send)(struct device *dev, struct net_pkt *pkt);
|
||||||
|
|
||||||
|
/** Close the related CAN socket */
|
||||||
|
void (*close)(struct device *dev, int filter_id);
|
||||||
|
|
||||||
/** Set socket CAN option */
|
/** Set socket CAN option */
|
||||||
int (*setsockopt)(struct device *dev, void *obj, int level, int optname,
|
int (*setsockopt)(struct device *dev, void *obj, int level, int optname,
|
||||||
const void *optval, socklen_t optlen);
|
const void *optval, socklen_t optlen);
|
||||||
|
|
|
@ -330,7 +330,8 @@ int net_context_unref(struct net_context *context)
|
||||||
net_tcp_unref(context);
|
net_tcp_unref(context);
|
||||||
|
|
||||||
if (context->conn_handler) {
|
if (context->conn_handler) {
|
||||||
if (IS_ENABLED(CONFIG_NET_TCP) || IS_ENABLED(CONFIG_NET_UDP)) {
|
if (IS_ENABLED(CONFIG_NET_TCP) || IS_ENABLED(CONFIG_NET_UDP) ||
|
||||||
|
IS_ENABLED(CONFIG_NET_SOCKETS_CAN)) {
|
||||||
net_conn_unregister(context->conn_handler);
|
net_conn_unregister(context->conn_handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -102,7 +102,8 @@ static void zcan_received_cb(struct net_context *ctx, struct net_pkt *pkt,
|
||||||
(struct zcan_frame *)net_pkt_data(pkt);
|
(struct zcan_frame *)net_pkt_data(pkt);
|
||||||
struct can_frame frame;
|
struct can_frame frame;
|
||||||
|
|
||||||
if (receivers[i].iface != net_pkt_iface(pkt)) {
|
if (!receivers[i].ctx ||
|
||||||
|
receivers[i].iface != net_pkt_iface(pkt)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -343,8 +344,86 @@ static ssize_t can_sock_write_vmeth(void *obj, const void *buffer,
|
||||||
return zcan_sendto_ctx(obj, buffer, count, 0, NULL, 0);
|
return zcan_sendto_ctx(obj, buffer, count, 0, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool is_already_attached(struct can_filter *filter,
|
||||||
|
struct net_if *iface,
|
||||||
|
struct net_context *ctx)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(receivers); i++) {
|
||||||
|
if (receivers[i].ctx != ctx && receivers[i].iface == iface &&
|
||||||
|
((receivers[i].can_id & receivers[i].can_mask) ==
|
||||||
|
(UNALIGNED_GET(&filter->can_id) &
|
||||||
|
UNALIGNED_GET(&filter->can_mask)))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int close_socket(struct net_context *ctx)
|
||||||
|
{
|
||||||
|
const struct canbus_api *api;
|
||||||
|
struct net_if *iface;
|
||||||
|
struct device *dev;
|
||||||
|
|
||||||
|
iface = net_context_get_iface(ctx);
|
||||||
|
dev = net_if_get_device(iface);
|
||||||
|
api = dev->driver_api;
|
||||||
|
|
||||||
|
if (!api || !api->close) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
api->close(dev, net_context_get_filter_id(ctx));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int can_close_socket(struct net_context *ctx)
|
||||||
|
{
|
||||||
|
int i, ret;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(receivers); i++) {
|
||||||
|
if (receivers[i].ctx == ctx) {
|
||||||
|
struct can_filter filter;
|
||||||
|
|
||||||
|
receivers[i].ctx = NULL;
|
||||||
|
|
||||||
|
filter.can_id = receivers[i].can_id;
|
||||||
|
filter.can_mask = receivers[i].can_mask;
|
||||||
|
|
||||||
|
if (!is_already_attached(&filter,
|
||||||
|
net_context_get_iface(ctx),
|
||||||
|
ctx)) {
|
||||||
|
/* We can detach now as there are no other
|
||||||
|
* sockets that have same filter.
|
||||||
|
*/
|
||||||
|
ret = close_socket(ctx);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int can_sock_ioctl_vmeth(void *obj, unsigned int request, va_list args)
|
static int can_sock_ioctl_vmeth(void *obj, unsigned int request, va_list args)
|
||||||
{
|
{
|
||||||
|
if (request == ZFD_IOCTL_CLOSE) {
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = can_close_socket(obj);
|
||||||
|
if (ret < 0) {
|
||||||
|
NET_DBG("Cannot detach net_context %p (%d)", obj, ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return sock_fd_op_vtable.fd_vtable.ioctl(obj, request, args);
|
return sock_fd_op_vtable.fd_vtable.ioctl(obj, request, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -506,24 +585,6 @@ static void can_unregister_filters(struct net_if *iface,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_already_attached(struct can_filter *filter,
|
|
||||||
struct net_if *iface,
|
|
||||||
struct net_context *ctx)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(receivers); i++) {
|
|
||||||
if (receivers[i].ctx != ctx && receivers[i].iface == iface &&
|
|
||||||
((receivers[i].can_id & receivers[i].can_mask) ==
|
|
||||||
(UNALIGNED_GET(&filter->can_id) &
|
|
||||||
UNALIGNED_GET(&filter->can_mask)))) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int can_sock_setsockopt_vmeth(void *obj, int level, int optname,
|
static int can_sock_setsockopt_vmeth(void *obj, int level, int optname,
|
||||||
const void *optval, socklen_t optlen)
|
const void *optval, socklen_t optlen)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue