// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2010-2011 EIA Electronics,
// Pieter Beyens <pieter.beyens@eia.be>
// Copyright (c) 2010-2011 EIA Electronics,
// Kurt Van Dijck <kurt.van.dijck@eia.be>
// Copyright (c) 2018 Protonic,
// Robin van der Gracht <robin@protonic.nl>
// Copyright (c) 2017-2019 Pengutronix,
// Marc Kleine-Budde <kernel@pengutronix.de>
// Copyright (c) 2017-2019 Pengutronix,
// Oleksij Rempel <kernel@pengutronix.de>
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/can/can-ml.h>
#include <linux/can/core.h>
#include <linux/can/skb.h>
#include <linux/errqueue.h>
#include <linux/if_arp.h>
#include "j1939-priv.h"
#define J1939_MIN_NAMELEN CAN_REQUIRED_SIZE(struct sockaddr_can, can_addr.j1939)
/* conversion function between struct sock::sk_priority from linux and
* j1939 priority field
*/
static inline priority_t j1939_prio(u32 sk_priority)
{
sk_priority = min(sk_priority, 7U);
return 7 - sk_priority;
}
static inline u32 j1939_to_sk_priority(priority_t prio)
{
return 7 - prio;
}
/* function to see if pgn is to be evaluated */
static inline bool j1939_pgn_is_valid(pgn_t pgn)
{
return pgn <= J1939_PGN_MAX;
}
/* test function to avoid non-zero DA placeholder for pdu1 pgn's */
static inline bool j1939_pgn_is_clean_pdu(pgn_t pgn)
{
if (j1939_pgn_is_pdu1(pgn))
return !(pgn & 0xff);
else
return true;
}
static inline void j1939_sock_pending_add(struct sock *sk)
{
struct j1939_sock *jsk = j1939_sk(sk);
atomic_inc(&jsk->skb_pending);
}
static int j1939_sock_pending_get(struct sock *sk)
{
struct j1939_sock *jsk = j1939_sk(sk);
return atomic_read(&jsk->skb_pending);
}
void j1939_sock_pending_del(struct sock *sk)
{
struct j1939_sock *jsk = j1939_sk(sk);
/* atomic_dec_return returns the new value */
if (!atomic_dec_return(&jsk->skb_pending))
wake_up(&jsk->waitq); /* no pending SKB's */
}
static void j1939_jsk_add(struct j1939_priv *priv, struct j1939_sock *jsk)
{
jsk->state |= J1939_SOCK_BOUND;
j1939_priv_get(priv);
spin_lock_bh(&priv->j1939_socks_lock);
list_add_tail(&jsk->list, &priv->j1939_socks);
spin_unlock_bh(&priv->j1939_socks_lock);
}
static void j1939_jsk_del(struct j1939_priv *priv, struct j1939_sock *jsk)
{
spin_lock_bh(&priv->j1939_socks_lock);
list_del_init(&jsk->list);
spin_unlock_bh(&priv->j1939_socks_lock);
j1939_priv_put(priv);
jsk->state &= ~J1939_SOCK_BOUND;
}
static bool j1939_sk_queue_session(struct j1939_session *session)
{
struct j1939_sock *jsk = j1939_sk(session->sk);
bool empty;
spin_lock_bh