* [PATCH v4 0/5] CAN: add SAE J1939 protocol
@ 2011-04-27 8:53 Kurt Van Dijck
[not found] ` <20110427085330.GA757-ozGf4kBk5synFtIcQ8t7k3L8HoS0Hn3T@public.gmane.org>
0 siblings, 1 reply; 11+ messages in thread
From: Kurt Van Dijck @ 2011-04-27 8:53 UTC (permalink / raw)
To: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
netdev-u79uwXL29TY76Z2rM5mHXA
This series adds SAE J1939 support to the current net-next-2.6.
1/5: can: extend sockaddr_can to include j1939 members
2/5: can: add rtnetlink support
3/5: j1939: initial import of SAE J1939
4/5: j1939: add documentation & MAINTAINERS
5/5: iproute2: use CAN & J1939
Differences from v3:
* Address claiming does not need a state machine anymore
* Dropped a per-ECU lock.
* Tasklets fill the gap between hrtimer (hardirq context)
and networking (softirq context), without needing
spin_lock_irqsave() & friends.
* Added example sendto() code in documentation
Differences from v2:
* replace magic constants with macro's
* use a static protocol stack, not a dynamic one.
* remove feature that have limited use and are
difficult to explain.
* renamed some structure types to increase code readability.
* block on transport sessions until CAN frame is queued.
Kind regards,
Kurt Van Dijck
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v4 1/5] can: extend sockaddr_can to include j1939 members
[not found] ` <20110427085330.GA757-ozGf4kBk5synFtIcQ8t7k3L8HoS0Hn3T@public.gmane.org>
@ 2011-04-27 8:55 ` Kurt Van Dijck
2011-04-27 8:57 ` [PATCH v4 2/5] can: add rtnetlink support Kurt Van Dijck
` (3 subsequent siblings)
4 siblings, 0 replies; 11+ messages in thread
From: Kurt Van Dijck @ 2011-04-27 8:55 UTC (permalink / raw)
To: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
netdev-u79uwXL29TY76Z2rM5mHXA
This patch prepares struct sockaddr_can for SAE J1939.
The size of this structure increases. To stay binary compatible,
the required_size macro is introduced for existing CAN protocols.
Signed-off-by: Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
---
include/linux/can.h | 20 +++++++++++++++++++-
include/linux/can/core.h | 9 +++++++++
net/can/bcm.c | 4 ++--
net/can/raw.c | 4 ++--
4 files changed, 32 insertions(+), 5 deletions(-)
diff --git a/include/linux/can.h b/include/linux/can.h
index d183333..9c2523c 100644
--- a/include/linux/can.h
+++ b/include/linux/can.h
@@ -67,7 +67,8 @@ struct can_frame {
#define CAN_TP20 4 /* VAG Transport Protocol v2.0 */
#define CAN_MCNET 5 /* Bosch MCNet */
#define CAN_ISOTP 6 /* ISO 15765-2 Transport Protocol */
-#define CAN_NPROTO 7
+#define CAN_J1939 7 /* SAE J1939 */
+#define CAN_NPROTO 8
#define SOL_CAN_BASE 100
@@ -84,6 +85,23 @@ struct sockaddr_can {
/* transport protocol class address information (e.g. ISOTP) */
struct { canid_t rx_id, tx_id; } tp;
+ /* J1939 address information */
+ struct {
+ /* 8 byte name when using dynamic addressing */
+ __u64 name;
+ /*
+ * pgn:
+ * 8bit: PS in PDU2 case, else 0
+ * 8bit: PF
+ * 1bit: DP
+ * 1bit: reserved
+ */
+ __u32 pgn;
+
+ /* 1byte address */
+ __u8 addr;
+ } j1939;
+
/* reserved for future CAN protocols address information */
} can_addr;
};
diff --git a/include/linux/can/core.h b/include/linux/can/core.h
index 6f70a6d..d803a5e 100644
--- a/include/linux/can/core.h
+++ b/include/linux/can/core.h
@@ -42,6 +42,15 @@ struct can_proto {
struct proto *prot;
};
+/*
+ * required_size
+ * macro to find the minimum size of a struct
+ * that includes a requested member
+ */
+#define required_size(member, struct_type) \
+ (offsetof(typeof(struct_type), member) + \
+ sizeof(((typeof(struct_type) *)(0))->member))
+
/* function prototypes for the CAN networklayer core (af_can.c) */
extern int can_proto_register(struct can_proto *cp);
diff --git a/net/can/bcm.c b/net/can/bcm.c
index 8a6a05e..3dc4d4e 100644
--- a/net/can/bcm.c
+++ b/net/can/bcm.c
@@ -1256,7 +1256,7 @@ static int bcm_sendmsg(struct kiocb *iocb, struct socket *sock,
struct sockaddr_can *addr =
(struct sockaddr_can *)msg->msg_name;
- if (msg->msg_namelen < sizeof(*addr))
+ if (msg->msg_namelen < required_size(can_ifindex, *addr))
return -EINVAL;
if (addr->can_family != AF_CAN)
@@ -1498,7 +1498,7 @@ static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len,
struct sock *sk = sock->sk;
struct bcm_sock *bo = bcm_sk(sk);
- if (len < sizeof(*addr))
+ if (len < required_size(can_ifindex, *addr))
return -EINVAL;
if (bo->bound)
diff --git a/net/can/raw.c b/net/can/raw.c
index 0eb39a7..6009d5e 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -355,7 +355,7 @@ static int raw_bind(struct socket *sock, struct sockaddr *uaddr, int len)
int err = 0;
int notify_enetdown = 0;
- if (len < sizeof(*addr))
+ if (len < required_size(can_ifindex, *addr))
return -EINVAL;
lock_sock(sk);
@@ -654,7 +654,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct socket *sock,
struct sockaddr_can *addr =
(struct sockaddr_can *)msg->msg_name;
- if (msg->msg_namelen < sizeof(*addr))
+ if (msg->msg_namelen < required_size(can_ifindex, *addr))
return -EINVAL;
if (addr->can_family != AF_CAN)
--
1.7.2.5
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v4 2/5] can: add rtnetlink support
[not found] ` <20110427085330.GA757-ozGf4kBk5synFtIcQ8t7k3L8HoS0Hn3T@public.gmane.org>
2011-04-27 8:55 ` [PATCH v4 1/5] can: extend sockaddr_can to include j1939 members Kurt Van Dijck
@ 2011-04-27 8:57 ` Kurt Van Dijck
2011-05-01 17:16 ` Oliver Hartkopp
2011-04-27 8:58 ` [PATCH v4 3/5] can-j1939: Import SAE J1939 stack Kurt Van Dijck
` (2 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Kurt Van Dijck @ 2011-04-27 8:57 UTC (permalink / raw)
To: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
netdev-u79uwXL29TY76Z2rM5mHXA
This patch adds rtnetlink support for AF_CAN. This support is really
a multiplexer towards the different CAN protocols.
Signed-off-by: Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
---
include/linux/can/core.h | 26 +++++-
net/can/af_can.c | 240 +++++++++++++++++++++++++++++++++++++++++++---
net/can/bcm.c | 2 +-
net/can/raw.c | 2 +-
4 files changed, 253 insertions(+), 17 deletions(-)
diff --git a/include/linux/can/core.h b/include/linux/can/core.h
index d803a5e..7ffd839 100644
--- a/include/linux/can/core.h
+++ b/include/linux/can/core.h
@@ -18,6 +18,7 @@
#include <linux/can.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
+#include <net/rtnetlink.h>
#define CAN_VERSION "20090105"
@@ -40,6 +41,27 @@ struct can_proto {
int protocol;
const struct proto_ops *ops;
struct proto *prot;
+ const struct rtnl_af_ops *rtnl_link_ops;
+ /*
+ * hooks for rtnl hooks
+ * for the *dump* functions, cb->args[0] is reserved
+ * for use by af_can.c, so keep your fingers off.
+ */
+ rtnl_doit_func rtnl_new_addr;
+ rtnl_doit_func rtnl_del_addr;
+ rtnl_dumpit_func rtnl_dump_addr;
+};
+
+/*
+ * this is quite a dirty hack:
+ * reuse the second byte of a rtnetlink msg
+ * to indicate the precise protocol.
+ * The major problem is that is may conflict
+ * with the prefixlen in struct ifaddrmsg.
+ */
+struct rtgencanmsg {
+ unsigned char rtgen_family;
+ unsigned char can_protocol;
};
/*
@@ -53,8 +75,8 @@ struct can_proto {
/* function prototypes for the CAN networklayer core (af_can.c) */
-extern int can_proto_register(struct can_proto *cp);
-extern void can_proto_unregister(struct can_proto *cp);
+extern int can_proto_register(const struct can_proto *cp);
+extern void can_proto_unregister(const struct can_proto *cp);
extern int can_rx_register(struct net_device *dev, canid_t can_id,
canid_t mask,
diff --git a/net/can/af_can.c b/net/can/af_can.c
index a8dcaa4..c7e9cdf 100644
--- a/net/can/af_can.c
+++ b/net/can/af_can.c
@@ -3,6 +3,7 @@
* (used by different CAN protocol modules)
*
* Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * Copyright (C) 2011 Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -69,7 +70,8 @@ static __initdata const char banner[] = KERN_INFO
MODULE_DESCRIPTION("Controller Area Network PF_CAN core");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Urs Thuermann <urs.thuermann-l29pVbxQd1IUtdQbppsyvg@public.gmane.org>, "
- "Oliver Hartkopp <oliver.hartkopp-l29pVbxQd1IUtdQbppsyvg@public.gmane.org>");
+ "Oliver Hartkopp <oliver.hartkopp-l29pVbxQd1IUtdQbppsyvg@public.gmane.org>, "
+ "Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>");
MODULE_ALIAS_NETPROTO(PF_CAN);
@@ -84,7 +86,7 @@ static DEFINE_SPINLOCK(can_rcvlists_lock);
static struct kmem_cache *rcv_cache __read_mostly;
/* table of registered CAN protocols */
-static struct can_proto *proto_tab[CAN_NPROTO] __read_mostly;
+static const struct can_proto *proto_tab[CAN_NPROTO] __read_mostly;
static DEFINE_MUTEX(proto_tab_lock);
struct timer_list can_stattimer; /* timer for statistics update */
@@ -115,9 +117,9 @@ static void can_sock_destruct(struct sock *sk)
skb_queue_purge(&sk->sk_receive_queue);
}
-static struct can_proto *can_try_module_get(int protocol)
+static const struct can_proto *can_try_module_get(int protocol)
{
- struct can_proto *cp;
+ const struct can_proto *cp;
rcu_read_lock();
cp = rcu_dereference(proto_tab[protocol]);
@@ -128,18 +130,20 @@ static struct can_proto *can_try_module_get(int protocol)
return cp;
}
+static inline void can_put_proto(const struct can_proto *cp)
+{
+ module_put(cp->prot->owner);
+}
+
static int can_create(struct net *net, struct socket *sock, int protocol,
int kern)
{
struct sock *sk;
- struct can_proto *cp;
+ const struct can_proto *cp;
int err = 0;
sock->state = SS_UNCONNECTED;
- if (protocol < 0 || protocol >= CAN_NPROTO)
- return -EINVAL;
-
if (!net_eq(net, &init_net))
return -EAFNOSUPPORT;
@@ -166,8 +170,8 @@ static int can_create(struct net *net, struct socket *sock, int protocol,
/* check for available protocol and correct usage */
- if (!cp)
- return -EPROTONOSUPPORT;
+ if (IS_ERR(cp))
+ return PTR_ERR(cp);
if (cp->type != sock->type) {
err = -EPROTOTYPE;
@@ -195,7 +199,7 @@ static int can_create(struct net *net, struct socket *sock, int protocol,
}
errout:
- module_put(cp->prot->owner);
+ can_put_proto(cp);
return err;
}
@@ -691,7 +695,7 @@ drop:
* -EBUSY protocol already in use
* -ENOBUF if proto_register() fails
*/
-int can_proto_register(struct can_proto *cp)
+int can_proto_register(const struct can_proto *cp)
{
int proto = cp->protocol;
int err = 0;
@@ -728,7 +732,7 @@ EXPORT_SYMBOL(can_proto_register);
* can_proto_unregister - unregister CAN transport protocol
* @cp: pointer to CAN protocol structure
*/
-void can_proto_unregister(struct can_proto *cp)
+void can_proto_unregister(const struct can_proto *cp)
{
int proto = cp->protocol;
@@ -818,6 +822,206 @@ static struct notifier_block can_netdev_notifier __read_mostly = {
.notifier_call = can_notifier,
};
+/*
+ * RTNETLINK
+ */
+static int can_rtnl_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ int ret, protocol;
+ const struct can_proto *cp;
+ rtnl_doit_func fn;
+
+ protocol = ((struct rtgencanmsg *)NLMSG_DATA(nlh))->can_protocol;
+ /* since rtnl_lock is held, dont try to load protocol */
+ cp = can_get_proto(protocol, 0);
+ if (IS_ERR(cp))
+ return PTR_ERR(cp);
+
+ switch (nlh->nlmsg_type) {
+ case RTM_NEWADDR:
+ fn = cp->rtnl_new_addr;
+ break;
+ case RTM_DELADDR:
+ fn = cp->rtnl_del_addr;
+ break;
+ default:
+ fn = 0;
+ break;
+ }
+ if (fn)
+ ret = fn(skb, nlh, arg);
+ else
+ ret = -EPROTONOSUPPORT;
+ can_put_proto(cp);
+ return ret;
+}
+
+static int can_rtnl_dumpit(struct sk_buff *skb, struct netlink_callback *cb,
+ int offset)
+{
+ int ret, j;
+ const struct can_proto *cp;
+ rtnl_dumpit_func fn;
+
+ ret = 0;
+ for (j = cb->args[0]; j < CAN_NPROTO; ++j) {
+ /* save state */
+ cb->args[0] = j;
+ cp = can_get_proto(j, 0);
+ if (IS_ERR(cp))
+ /* we are looping, any error is our own fault */
+ continue;
+ fn = *((rtnl_dumpit_func *)(&((const uint8_t *)cp)[offset]));
+ if (fn)
+ ret = fn(skb, cb);
+ can_put_proto(cp);
+ if (ret < 0)
+ /* suspend this skb */
+ return ret;
+ }
+ return ret;
+}
+
+static int can_rtnl_dump_addr(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ return can_rtnl_dumpit(skb, cb,
+ offsetof(struct can_proto, rtnl_dump_addr));
+}
+
+/*
+ * LINK AF properties
+ */
+static size_t can_get_link_af_size(const struct net_device *dev)
+{
+ int ret, j, total;
+ const struct can_proto *cp;
+
+ if (!net_eq(dev_net(dev), &init_net) || (dev->type != ARPHRD_CAN))
+ return 0;
+
+ total = 0;
+ for (j = 0; j < CAN_NPROTO; ++j) {
+ cp = can_try_mdoule_get(j);
+ if (IS_ERR(cp))
+ /* no worry */
+ continue;
+ ret = 0;
+ if (cp->rtnl_link_ops && cp->rtnl_link_ops->get_link_af_size)
+ ret = cp->rtnl_link_ops->get_link_af_size(dev) +
+ nla_total_size(sizeof(struct nlattr));
+ can_put_proto(cp);
+ if (ret < 0)
+ return ret;
+ total += ret;
+ }
+ return nla_total_size(total);
+}
+
+static int can_fill_link_af(struct sk_buff *skb, const struct net_device *dev)
+{
+ int ret, j, n;
+ struct nlattr *nla;
+ const struct can_proto *cp;
+
+ if (!net_eq(dev_net(dev), &init_net) || (dev->type != ARPHRD_CAN))
+ return -ENODATA;
+
+ n = 0;
+ for (j = 0; j < CAN_NPROTO; ++j) {
+ cp = can_try_module_get(j);
+ if (IS_ERR(cp))
+ /* no worry */
+ continue;
+ if (cp->rtnl_link_ops && cp->rtnl_link_ops->fill_link_af) {
+ nla = nla_nest_start(skb, j);
+ if (!nla)
+ goto nla_put_failure;
+
+ ret = cp->rtnl_link_ops->fill_link_af(skb, dev);
+ /*
+ * Caller may return ENODATA to indicate that there
+ * was no data to be dumped. This is not an error, it
+ * means we should trim the attribute header and
+ * continue.
+ */
+ if (ret == -ENODATA)
+ nla_nest_cancel(skb, nla);
+ else if (ret < 0)
+ goto nla_put_failure;
+ nla_nest_end(skb, nla);
+ ++n;
+ }
+ can_put_proto(cp);
+ }
+ return n ? 0 : -ENODATA;
+
+nla_put_failure:
+ nla_nest_cancel(skb, nla);
+ can_put_proto(cp);
+ return -EMSGSIZE;
+}
+
+static int can_validate_link_af(const struct net_device *dev,
+ const struct nlattr *nla)
+{
+ int ret, rem;
+ const struct can_proto *cp;
+ struct nlattr *prot;
+
+ if (!net_eq(dev_net(dev), &init_net) || (dev->type != ARPHRD_CAN))
+ return -EPROTONOSUPPORT;
+
+ nla_for_each_nested(prot, nla, rem) {
+ cp = can_try_module_get(nla_type(prot));
+ if (IS_ERR(cp))
+ return PTR_ERR(cp);
+ if (!cp->rtnl_link_ops)
+ ret = -EPROTONOSUPPORT;
+ else if (!cp->rtnl_link_ops->validate_link_af)
+ ret = 0;
+ else
+ ret = cp->rtnl_link_ops->validate_link_af(dev, prot);
+ can_put_proto(cp);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+static int can_set_link_af(struct net_device *dev, const struct nlattr *nla)
+{
+ int ret, rem;
+ const struct can_proto *cp;
+ struct nlattr *prot;
+
+ if (!net_eq(dev_net(dev), &init_net) || (dev->type != ARPHRD_CAN))
+ return -EPROTONOSUPPORT;
+
+ nla_for_each_nested(prot, nla, rem) {
+ cp = can_try_module_get(nla_type(prot));
+ if (IS_ERR(cp))
+ return PTR_ERR(cp);
+ if (!cp->rtnl_link_ops || !cp->rtnl_link_ops->set_link_af)
+ ret = -EPROTONOSUPPORT;
+ else
+ ret = cp->rtnl_link_ops->set_link_af(dev, prot);
+ can_put_proto(cp);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+static struct rtnl_af_ops can_rtnl_af_ops = {
+ .family = AF_CAN,
+ .fill_link_af = can_fill_link_af,
+ .get_link_af_size = can_get_link_af_size,
+ .validate_link_af = can_validate_link_af,
+ .set_link_af = can_set_link_af,
+};
+
+/* exported init */
+
static __init int can_init(void)
{
printk(banner);
@@ -843,6 +1047,11 @@ static __init int can_init(void)
register_netdevice_notifier(&can_netdev_notifier);
dev_add_pack(&can_packet);
+ rtnl_af_register(&can_rtnl_af_ops);
+ rtnl_register(PF_CAN, RTM_NEWADDR, can_rtnl_doit, NULL);
+ rtnl_register(PF_CAN, RTM_DELADDR, can_rtnl_doit, NULL);
+ rtnl_register(PF_CAN, RTM_GETADDR, NULL, can_rtnl_dump_addr);
+
return 0;
}
@@ -853,6 +1062,11 @@ static __exit void can_exit(void)
if (stats_timer)
del_timer(&can_stattimer);
+ rtnl_unregister(PF_CAN, RTM_NEWADDR);
+ rtnl_unregister(PF_CAN, RTM_DELADDR);
+ rtnl_unregister(PF_CAN, RTM_GETADDR);
+ rtnl_af_unregister(&can_rtnl_af_ops);
+
can_remove_proc();
/* protocol unregister */
diff --git a/net/can/bcm.c b/net/can/bcm.c
index 3dc4d4e..4f65f4f 100644
--- a/net/can/bcm.c
+++ b/net/can/bcm.c
@@ -1601,7 +1601,7 @@ static struct proto bcm_proto __read_mostly = {
.init = bcm_init,
};
-static struct can_proto bcm_can_proto __read_mostly = {
+static const struct can_proto bcm_can_proto = {
.type = SOCK_DGRAM,
.protocol = CAN_BCM,
.ops = &bcm_ops,
diff --git a/net/can/raw.c b/net/can/raw.c
index 6009d5e..60d4282 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -774,7 +774,7 @@ static struct proto raw_proto __read_mostly = {
.init = raw_init,
};
-static struct can_proto raw_can_proto __read_mostly = {
+static const struct can_proto raw_can_proto = {
.type = SOCK_RAW,
.protocol = CAN_RAW,
.ops = &raw_ops,
--
1.7.2.5
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v4 3/5] can-j1939: Import SAE J1939 stack
[not found] ` <20110427085330.GA757-ozGf4kBk5synFtIcQ8t7k3L8HoS0Hn3T@public.gmane.org>
2011-04-27 8:55 ` [PATCH v4 1/5] can: extend sockaddr_can to include j1939 members Kurt Van Dijck
2011-04-27 8:57 ` [PATCH v4 2/5] can: add rtnetlink support Kurt Van Dijck
@ 2011-04-27 8:58 ` Kurt Van Dijck
2011-04-27 9:00 ` [PATCH v4 4/5] can-j1939: add documentation Kurt Van Dijck
2011-04-27 9:03 ` [PATCH v4 5/5] iproute2: add can-j1939 support Kurt Van Dijck
4 siblings, 0 replies; 11+ messages in thread
From: Kurt Van Dijck @ 2011-04-27 8:58 UTC (permalink / raw)
To: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
netdev-u79uwXL29TY76Z2rM5mHXA
Import SAE J1939
Signed-off-by: Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
---
include/linux/can/Kbuild | 1 +
include/linux/can/j1939.h | 99 +++
net/can/Kconfig | 1 +
net/can/Makefile | 2 +
net/can/j1939/Kconfig | 22 +
net/can/j1939/Makefile | 14 +
net/can/j1939/address-claim.c | 227 +++++++
net/can/j1939/bus.c | 523 +++++++++++++++
net/can/j1939/filter.c | 76 +++
net/can/j1939/j1939-priv.h | 314 +++++++++
net/can/j1939/main.c | 458 +++++++++++++
net/can/j1939/proc.c | 104 +++
net/can/j1939/promisc.c | 43 ++
net/can/j1939/rtnl.c | 308 +++++++++
net/can/j1939/socket.c | 969 +++++++++++++++++++++++++++
net/can/j1939/transport.c | 1449 +++++++++++++++++++++++++++++++++++++++++
16 files changed, 4610 insertions(+), 0 deletions(-)
create mode 100644 include/linux/can/j1939.h
create mode 100644 net/can/j1939/Kconfig
create mode 100644 net/can/j1939/Makefile
create mode 100644 net/can/j1939/address-claim.c
create mode 100644 net/can/j1939/bus.c
create mode 100644 net/can/j1939/filter.c
create mode 100644 net/can/j1939/j1939-priv.h
create mode 100644 net/can/j1939/main.c
create mode 100644 net/can/j1939/proc.c
create mode 100644 net/can/j1939/promisc.c
create mode 100644 net/can/j1939/rtnl.c
create mode 100644 net/can/j1939/socket.c
create mode 100644 net/can/j1939/transport.c
diff --git a/include/linux/can/Kbuild b/include/linux/can/Kbuild
index 8cb05aa..0364eef 100644
--- a/include/linux/can/Kbuild
+++ b/include/linux/can/Kbuild
@@ -2,3 +2,4 @@ header-y += raw.h
header-y += bcm.h
header-y += error.h
header-y += netlink.h
+header-y += j1939.h
diff --git a/include/linux/can/j1939.h b/include/linux/can/j1939.h
new file mode 100644
index 0000000..7ff419e
--- /dev/null
+++ b/include/linux/can/j1939.h
@@ -0,0 +1,99 @@
+/*
+ * j1939.h
+ *
+ * Copyright (c) 2010-2011 EIA Electronics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _J1939_H_
+#define _J1939_H_
+
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/can.h>
+
+#define J1939_IDLE_ADDR 0xfe
+#define J1939_NO_ADDR 0xff
+#define J1939_NO_NAME 0
+#define J1939_NO_PGN 0x40000
+/*
+ * J1939 Parameter Group Number
+ *
+ * bit 0-7 : PDU Specific (PS)
+ * bit 8-15 : PDU Format (PF)
+ * bit 16 : Data Page (DP)
+ * bit 17 : Reserved (R)
+ * bit 19-31 : set to zero
+ */
+typedef __u32 pgn_t;
+
+/*
+ * J1939 Priority
+ *
+ * bit 0-2 : Priority (P)
+ * bit 3-7 : set to zero
+ */
+typedef __u8 priority_t;
+
+/*
+ * J1939 NAME
+ *
+ * bit 0-20 : Identity Number
+ * bit 21-31 : Manufacturer Code
+ * bit 32-34 : ECU Instance
+ * bit 35-39 : Function Instance
+ * bit 40-47 : Function
+ * bit 48 : Reserved
+ * bit 49-55 : Vehicle System
+ * bit 56-59 : Vehicle System Instance
+ * bit 60-62 : Industry Group
+ * bit 63 : Arbitrary Address Capable
+ */
+typedef __u64 name_t;
+
+/*
+ * J1939 socket options
+ */
+#define SOL_CAN_J1939 (SOL_CAN_BASE + CAN_J1939)
+enum {
+ SO_J1939_FILTER = 1, /* set filters */
+ SO_J1939_PROMISC = 2, /* set/clr promiscuous mode */
+ SO_J1939_RECV_OWN = 3,
+ SO_J1939_SEND_PRIO = 4,
+};
+
+enum {
+ SCM_J1939_DEST_ADDR = 1,
+ SCM_J1939_DEST_NAME = 2,
+ SCM_J1939_PRIO = 3,
+};
+
+struct j1939_filter {
+ name_t name;
+ name_t name_mask;
+ __u8 addr;
+ __u8 addr_mask;
+ pgn_t pgn;
+ pgn_t pgn_mask;
+};
+
+/*
+ * RTNETLINK
+ */
+enum {
+ IFLA_J1939_UNSPEC,
+ IFLA_J1939_ENABLE,
+ IFLA_J1939_MAX,
+};
+
+enum {
+ IFA_J1939_UNSPEC,
+ IFA_J1939_ADDR,
+ IFA_J1939_NAME,
+ IFA_J1939_MAX,
+};
+
+#endif /* _J1939_H_ */
diff --git a/net/can/Kconfig b/net/can/Kconfig
index 89395b2..7feb58c 100644
--- a/net/can/Kconfig
+++ b/net/can/Kconfig
@@ -40,5 +40,6 @@ config CAN_BCM
CAN messages are used on the bus (e.g. in automotive environments).
To use the Broadcast Manager, use AF_CAN with protocol CAN_BCM.
+source "net/can/j1939/Kconfig"
source "drivers/net/can/Kconfig"
diff --git a/net/can/Makefile b/net/can/Makefile
index 2d3894b..953d851 100644
--- a/net/can/Makefile
+++ b/net/can/Makefile
@@ -10,3 +10,5 @@ can-raw-y := raw.o
obj-$(CONFIG_CAN_BCM) += can-bcm.o
can-bcm-y := bcm.o
+
+obj-$(CONFIG_CAN_J1939) += j1939/
diff --git a/net/can/j1939/Kconfig b/net/can/j1939/Kconfig
new file mode 100644
index 0000000..74d2a86
--- /dev/null
+++ b/net/can/j1939/Kconfig
@@ -0,0 +1,22 @@
+#
+# SAE J1939 network layer core configuration
+#
+
+config CAN_J1939
+ tristate "SAE J1939"
+ depends on CAN
+ ---help---
+ SAE J1939
+ Say Y to have in-kernel support for j1939 socket type. This
+ allows communication according to SAE j1939.
+ The relevant parts in kernel are
+ SAE j1939-21 (datalink & transport protocol)
+ & SAE j1939-81 (network management).
+
+config CAN_J1939_DEBUG
+ bool "debug SAE J1939"
+ depends on CAN_J1939
+ default n
+ ---help---
+ Say Y to add extra debug code (via printk) in the j1939 stack
+
diff --git a/net/can/j1939/Makefile b/net/can/j1939/Makefile
new file mode 100644
index 0000000..7ca2fc9
--- /dev/null
+++ b/net/can/j1939/Makefile
@@ -0,0 +1,14 @@
+
+obj-$(CONFIG_CAN_J1939) += can-j1939.o
+
+can-j1939-objs := main.o \
+ proc.o bus.o \
+ rtnl.o \
+ socket.o \
+ address-claim.o transport.o \
+ promisc.o filter.o
+
+ifeq ($(CONFIG_CAN_J1939_DEBUG),y)
+ EXTRA_CFLAGS += -DDEBUG
+endif
+
diff --git a/net/can/j1939/address-claim.c b/net/can/j1939/address-claim.c
new file mode 100644
index 0000000..826c7e9
--- /dev/null
+++ b/net/can/j1939/address-claim.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2010-2011 EIA Electronics
+ *
+ * Authors:
+ * Pieter Beyens <pieter.beyens-/BeEPy95v10@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+
+/*
+ * J1939 Address Claiming.
+ * Address Claiming in the kernel
+ * - keeps track of the AC states of ECU's,
+ * - resolves NAME<=>SA taking into account the AC states of ECU's.
+ *
+ * All Address Claim msgs (including host-originated msg) are processed
+ * at the receive path (a sent msg is always received again via CAN echo).
+ * As such, the processing of AC msgs is done in the order on which msgs
+ * are sent on the bus.
+ *
+ * This module doesn't send msgs itself (e.g. replies on Address Claims),
+ * this is the responsibility of a user space application or daemon.
+ */
+
+#include <linux/skbuff.h>
+#include <linux/byteorder/generic.h>
+
+#include "j1939-priv.h"
+
+#define CANDATA2NAME(data) le64_to_cpup((uint64_t *)data)
+
+static inline int ac_msg_is_request_for_ac(struct sk_buff *skb)
+{
+ struct j1939_sk_buff_cb *sk_addr = (void *)skb->cb;
+ int req_pgn;
+
+ if ((skb->len < 3) || (sk_addr->pgn != PGN_REQUEST))
+ return 0;
+ req_pgn = skb->data[0] | (skb->data[1] << 8) | (skb->data[2] << 16);
+ return req_pgn = PGN_ADDRESS_CLAIMED;
+}
+
+static int j1939_verify_outgoing_address_claim(struct sk_buff *skb)
+{
+ struct j1939_sk_buff_cb *sk_addr = (void *)skb->cb;
+
+ if (skb->len != 8) {
+ j1939_notice("tx address claim with dlc %i\n", skb->len);
+ return -EPROTO;
+ }
+
+ if (sk_addr->src.name != CANDATA2NAME(skb->data)) {
+ j1939_notice("tx address claim with different name\n");
+ return -EPROTO;
+ }
+
+ if (sk_addr->src.addr == J1939_NO_ADDR) {
+ j1939_notice("tx address claim with broadcast sa\n");
+ return -EPROTO;
+ }
+
+ /* ac must always be a broadcast */
+ if (sk_addr->dst.name || (sk_addr->dst.addr != J1939_NO_ADDR)) {
+ j1939_notice("tx address claim with dest, not broadcast\n");
+ return -EPROTO;
+ }
+ return 0;
+}
+
+int j1939_send_address_claim(struct sk_buff *skb)
+{
+ int ret, sa;
+ struct j1939_sk_buff_cb *sk_addr = (void *)skb->cb;
+
+ /* network mgmt: address claiming msgs */
+ if (sk_addr->pgn == PGN_ADDRESS_CLAIMED) {
+ struct j1939_ecu *ecu;
+
+ ret = j1939_verify_outgoing_address_claim(skb);
+ /* return both when failure & when successfull */
+ if (ret < 0)
+ return ret;
+ ecu = j1939_ecu_find_by_name(sk_addr->src.name,
+ sk_addr->ifindex);
+ if (!ecu)
+ return -ENODEV;
+ if (!(ecu->flags & ECUFLAG_LOCAL)) {
+ put_j1939_ecu(ecu);
+ return -EREMOTE;
+ }
+
+ if (ecu->sa != sk_addr->src.addr)
+ /* hold further traffic for ecu, remove from parent */
+ j1939_ecu_remove_sa(ecu);
+ put_j1939_ecu(ecu);
+ } else if (sk_addr->src.name) {
+ /* assign source address */
+ sa = j1939_name_to_sa(sk_addr->src.name, sk_addr->ifindex);
+ if (!j1939_address_is_unicast(sa) &&
+ !ac_msg_is_request_for_ac(skb)) {
+ j1939_notice("tx drop: invalid sa for name "
+ "0x%016llx\n", sk_addr->src.name);
+ return -EADDRNOTAVAIL;
+ }
+ sk_addr->src.addr = sa;
+ }
+
+ /* assign destination address */
+ if (sk_addr->dst.name) {
+ sa = j1939_name_to_sa(sk_addr->dst.name, sk_addr->ifindex);
+ if (!j1939_address_is_unicast(sa)) {
+ j1939_notice("tx drop: invalid da for name "
+ "0x%016llx\n", sk_addr->dst.name);
+ return -EADDRNOTAVAIL;
+ }
+ sk_addr->dst.addr = sa;
+ }
+ return 0;
+}
+
+static struct j1939_ecu *j1939_process_address_claim(struct sk_buff *skb)
+{
+ struct j1939_sk_buff_cb *sk_addr = (void *)skb->cb;
+ struct j1939_ecu *ecu, *dut, **pref;
+ name_t name;
+
+ if (skb->len < 8) {
+ j1939_notice("rx address claim with wrong dlc %i\n", skb->len);
+ return ERR_PTR(-EPROTO);
+ }
+
+ name = CANDATA2NAME(skb->data);
+ if (!name) {
+ j1939_notice("rx address claim without name\n");
+ return ERR_PTR(-EPROTO);
+ }
+
+ if (!j1939_address_is_valid(sk_addr->src.addr)) {
+ j1939_notice("rx address claim with broadcast sa\n");
+ return ERR_PTR(-EPROTO);
+ }
+
+ ecu = j1939_ecu_get_register(name, sk_addr->ifindex, ECUFLAG_REMOTE, 1);
+ if (IS_ERR(ecu))
+ return ecu;
+ if ((ecu->flags & ECUFLAG_LOCAL) && !skb->sk)
+ j1939_warning("duplicate name on the bus %016llx!\n",
+ (long long)name);
+
+ if (sk_addr->src.addr >= J1939_IDLE_ADDR) {
+ j1939_ecu_remove_sa(ecu);
+ if (ecu->flags & ECUFLAG_REMOTE)
+ /* extra put => schedule removal */
+ j1939_ecu_unregister(ecu);
+ return ecu;
+ }
+
+ write_lock_bh(&ecu->parent->lock);
+ /* save new SA */
+ if (sk_addr->src.addr != ecu->sa)
+ j1939_ecu_remove_sa_locked(ecu);
+ ecu->sa = sk_addr->src.addr;
+ /* iterate this segment */
+ list_for_each_entry(dut, &ecu->parent->ecus, list) {
+ /* cancel pending claims for this SA */
+ /* this includes myself ! */
+ if (ecu->sa == dut->sa)
+ /*
+ * cancel pending claims for our new SA
+ * this includes 'ecu', since we will
+ * schedule a timer soon now
+ */
+ hrtimer_try_to_cancel(&dut->ac_timer);
+ if (dut->name > ecu->name)
+ dut->sa = J1939_IDLE_ADDR;
+ }
+
+ pref = &ecu->parent->ents[sk_addr->src.addr].ecu;
+ if (*pref && ((*pref)->name > ecu->name))
+ *pref = NULL;
+
+ /* schedule timer in 250 msec to commit address change */
+ hrtimer_start(&ecu->ac_timer, ktime_set(0, 250000000),
+ HRTIMER_MODE_REL);
+ write_unlock_bh(&ecu->parent->lock);
+
+ return ecu;
+}
+
+int j1939_recv_address_claim(struct sk_buff *skb)
+{
+ struct j1939_sk_buff_cb *sk_addr = (void *)skb->cb;
+ struct j1939_ecu *ecu;
+
+ /*
+ * network mgmt
+ */
+ if (sk_addr->pgn == PGN_ADDRESS_CLAIMED) {
+ ecu = j1939_process_address_claim(skb);
+ if (IS_ERR(ecu))
+ return PTR_ERR(ecu);
+ } else if (j1939_address_is_unicast(sk_addr->src.addr)) {
+ ecu = j1939_ecu_find_by_addr(sk_addr->src.addr,
+ sk_addr->ifindex);
+ } else {
+ ecu = NULL;
+ }
+
+ /* assign source stuff */
+ if (ecu) {
+ ecu->rxtime = ktime_get();
+ sk_addr->src.flags = ecu->flags;
+ sk_addr->src.name = ecu->name;
+ put_j1939_ecu(ecu);
+ }
+ /* assign destination stuff */
+ ecu = j1939_ecu_find_by_addr(sk_addr->dst.addr, sk_addr->ifindex);
+ if (ecu) {
+ sk_addr->dst.flags = ecu->flags;
+ sk_addr->dst.name = ecu->name;
+ put_j1939_ecu(ecu);
+ }
+ return 0;
+}
+
diff --git a/net/can/j1939/bus.c b/net/can/j1939/bus.c
new file mode 100644
index 0000000..c51245d
--- /dev/null
+++ b/net/can/j1939/bus.c
@@ -0,0 +1,523 @@
+/*
+ * Copyright (c) 2010-2011 EIA Electronics
+ *
+ * Authors:
+ * Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+
+/*
+ * j1939-bus.c - bus for j1939 remote devices
+ * Since rtnetlink, no real bus is used.
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/workqueue.h>
+
+#include "j1939-priv.h"
+
+#define jseg_dbg(jseg, fmt, ...) \
+ pr_debug("j1939-%i: " fmt, (jseg)->ifindex, ##__VA_ARGS__)
+
+#define ecu_dbg(ecu, fmt, ...) \
+ pr_debug("j1939-%i,%016llx,%02x: " fmt, (ecu)->parent->ifindex, \
+ (ecu)->name, (ecu)->sa, ##__VA_ARGS__)
+#define ecu_alert(ecu, fmt, ...) \
+ pr_alert("j1939-%i,%016llx,%02x: " fmt, (ecu)->parent->ifindex, \
+ (ecu)->name, (ecu)->sa, ##__VA_ARGS__)
+
+static struct {
+ struct list_head list;
+ spinlock_t lock;
+} segments;
+
+struct j1939_segment *j1939_segment_find(int ifindex)
+{
+ struct j1939_segment *jseg;
+
+ spin_lock_bh(&segments.lock);
+ list_for_each_entry(jseg, &segments.list, flist) {
+ if (jseg->ifindex == ifindex) {
+ get_j1939_segment(jseg);
+ goto found;
+ }
+ }
+ jseg = NULL;
+found:
+ spin_unlock_bh(&segments.lock);
+ return jseg;
+}
+
+/*
+ * iterate over ECU's,
+ * and register flagged ecu's on their claimed SA
+ */
+static void j1939_segment_ac_task(unsigned long val)
+{
+ struct j1939_segment *jseg = (void *)val;
+ struct j1939_ecu *ecu;
+
+ write_lock_bh(&jseg->lock);
+ list_for_each_entry(ecu, &jseg->ecus, list) {
+ /* next 2 (read & set) could be merged into xxx? */
+ if (!atomic_read(&ecu->ac_delay_expired))
+ continue;
+ atomic_set(&ecu->ac_delay_expired, 0);
+ if (j1939_address_is_unicast(ecu->sa))
+ ecu->parent->ents[ecu->sa].ecu = ecu;
+ }
+ write_unlock_bh(&jseg->lock);
+}
+/*
+ * segment device interface
+ */
+static void cb_put_j1939_segment(struct kref *kref)
+{
+ struct j1939_segment *jseg =
+ container_of(kref, struct j1939_segment, kref);
+
+ tasklet_disable_nosync(&jseg->ac_task);
+ kfree(jseg);
+}
+
+void put_j1939_segment(struct j1939_segment *segment)
+{
+ kref_put(&segment->kref, cb_put_j1939_segment);
+}
+
+int j1939_segment_register(struct net_device *netdev)
+{
+ int ret;
+ struct j1939_segment *jseg;
+
+ jseg = j1939_segment_find(netdev->ifindex);
+ if (jseg) {
+ put_j1939_segment(jseg);
+ ret = -EALREADY;
+ goto fail_exist;
+ }
+ jseg = kzalloc(sizeof(*jseg), GFP_KERNEL);
+ if (!jseg) {
+ ret = -ENOMEM;
+ goto fail_malloc;
+ }
+ tasklet_init(&jseg->ac_task, j1939_segment_ac_task,
+ (unsigned long)jseg);
+ rwlock_init(&jseg->lock);
+ INIT_LIST_HEAD(&jseg->ecus);
+ INIT_LIST_HEAD(&jseg->flist);
+ jseg->ifindex = netdev->ifindex;
+
+ kref_init(&jseg->kref);
+
+ spin_lock_bh(&segments.lock);
+ list_add_tail(&jseg->flist, &segments.list);
+ spin_unlock_bh(&segments.lock);
+
+ jseg_dbg(jseg, "register\n");
+ return 0;
+
+fail_malloc:
+fail_exist:
+ return ret;
+}
+
+void j1939_segment_unregister(struct j1939_segment *jseg)
+{
+ struct j1939_ecu *ecu;
+
+ if (!jseg)
+ return;
+
+ spin_lock_bh(&segments.lock);
+ list_del_init(&jseg->flist);
+ spin_unlock_bh(&segments.lock);
+
+ write_lock_bh(&jseg->lock);
+ while (!list_empty(&jseg->ecus)) {
+ ecu = list_first_entry(&jseg->ecus, struct j1939_ecu, list);
+ write_unlock_bh(&jseg->lock);
+ j1939_ecu_unregister(ecu);
+ write_lock_bh(&jseg->lock);
+ }
+ write_unlock_bh(&jseg->lock);
+ jseg_dbg(jseg, "unregister\n");
+ put_j1939_segment(jseg);
+}
+
+/*
+ * ECU device interface
+ */
+static enum hrtimer_restart j1939_ecu_timer_handler(struct hrtimer *hrtimer)
+{
+ struct j1939_ecu *ecu =
+ container_of(hrtimer, struct j1939_ecu, ac_timer);
+
+ atomic_set(&ecu->ac_delay_expired, 1);
+ tasklet_schedule(&ecu->parent->ac_task);
+ return HRTIMER_NORESTART;
+}
+
+static void cb_put_j1939_ecu(struct kref *kref)
+{
+ struct j1939_ecu *ecu =container_of(kref, struct j1939_ecu, kref);
+
+ kfree(ecu);
+}
+void put_j1939_ecu(struct j1939_ecu *ecu)
+{
+ kref_put(&ecu->kref, cb_put_j1939_ecu);
+}
+
+struct j1939_ecu *j1939_ecu_get_register(name_t name, int ifindex, int flags,
+ int return_existing)
+{
+ struct j1939_segment *parent;
+ struct j1939_ecu *ecu, *dut;
+
+ if (!ifindex || !name) {
+ pr_alert("%s(%i, %016llx) invalid\n",
+ __func__, ifindex, (long long)name);
+ return ERR_PTR(-EINVAL);
+ }
+
+ parent = j1939_segment_find(ifindex);
+ if (!parent) {
+ pr_alert("%s %i: segment not found\n", __func__, ifindex);
+ return ERR_PTR(-EINVAL);
+ }
+ if (return_existing) {
+ read_lock_bh(&parent->lock);
+ /* test for existing name */
+ list_for_each_entry(dut, &parent->ecus, list) {
+ if (dut->name == name) {
+ get_j1939_ecu(dut);
+ read_unlock_bh(&parent->lock);
+ return dut;
+ }
+ }
+ read_unlock_bh(&parent->lock);
+ }
+ /* alloc */
+ ecu = kzalloc(sizeof(*ecu), gfp_any());
+ if (!ecu)
+ /* should we look for an existing ecu */
+ return ERR_PTR(-ENOMEM);
+ kref_init(&ecu->kref);
+ ecu->sa = J1939_IDLE_ADDR;
+ ecu->name = name;
+ ecu->flags = flags;
+
+ hrtimer_init(&ecu->ac_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ ecu->ac_timer.function = j1939_ecu_timer_handler;
+ INIT_LIST_HEAD(&ecu->list);
+
+ /* first add to internal list */
+ write_lock_bh(&parent->lock);
+ /* test for duplicate name */
+ list_for_each_entry(dut, &parent->ecus, list) {
+ if (dut->name == ecu->name)
+ goto duplicate;
+ }
+ get_j1939_ecu(ecu);
+ /* a ref to parent is held */
+ ecu->parent = parent;
+ list_add_tail(&ecu->list, &parent->ecus);
+ write_unlock_bh(&parent->lock);
+ ecu_dbg(ecu, "register\n");
+ return ecu;
+
+duplicate:
+ get_j1939_ecu(dut);
+ write_unlock_bh(&parent->lock);
+ put_j1939_segment(parent);
+ if (return_existing)
+ return dut;
+ ecu_alert(ecu, "duplicate name\n");
+ put_j1939_ecu(ecu);
+ return ERR_PTR(-EEXIST);
+}
+
+void j1939_ecu_unregister(struct j1939_ecu *ecu)
+{
+ BUG_ON(!ecu);
+ ecu_dbg(ecu, "unregister\n");
+ hrtimer_try_to_cancel(&ecu->ac_timer);
+
+ write_lock_bh(&ecu->parent->lock);
+ j1939_ecu_remove_sa_locked(ecu);
+ list_del_init(&ecu->list);
+ write_unlock_bh(&ecu->parent->lock);
+ /* put segment, reverting the effect done by ..._register() */
+ put_j1939_segment(ecu->parent);
+ put_j1939_ecu(ecu);
+}
+
+struct j1939_ecu *j1939_ecu_find_by_addr(int sa, int ifindex)
+{
+ struct j1939_ecu *ecu;
+ struct j1939_segment *parent;
+
+ if (!j1939_address_is_unicast(sa))
+ return NULL;
+ parent = j1939_segment_find(ifindex);
+ if (!parent)
+ return NULL;
+ read_lock_bh(&parent->lock);
+ ecu = parent->ents[sa].ecu;
+ if (ecu)
+ get_j1939_ecu(ecu);
+ read_unlock_bh(&parent->lock);
+ put_j1939_segment(parent);
+ return ecu;
+}
+
+int j1939_name_to_sa(uint64_t name, int ifindex)
+{
+ struct j1939_ecu *ecu;
+ struct j1939_segment *parent;
+ int sa;
+
+ if (!name)
+ return J1939_IDLE_ADDR;
+ parent = j1939_segment_find(ifindex);
+ if (!parent)
+ return J1939_IDLE_ADDR;
+
+ sa = J1939_IDLE_ADDR;
+ read_lock_bh(&parent->lock);
+ list_for_each_entry(ecu, &parent->ecus, list) {
+ if (ecu->name == name) {
+ if ((sa == J1939_IDLE_ADDR) &&
+ (parent->ents[ecu->sa].ecu == ecu))
+ /* ecu's SA is registered */
+ sa = ecu->sa;
+ break;
+ }
+ }
+ read_unlock_bh(&parent->lock);
+ put_j1939_segment(parent);
+ return sa;
+}
+
+struct j1939_ecu *j1939_ecu_find_segment_default_tx(int ifindex,
+ name_t *name, uint8_t *addr)
+{
+ struct j1939_ecu *ecu;
+ struct j1939_segment *parent;
+ struct addr_ent *paddr;
+ int j;
+
+ if (ifindex <= 0)
+ return ERR_PTR(-EINVAL);
+ parent = j1939_segment_find(ifindex);
+ if (!parent)
+ return ERR_PTR(-ENETUNREACH);
+ read_lock_bh(&parent->lock);
+ list_for_each_entry(ecu, &parent->ecus, list) {
+ if (ecu->flags & ECUFLAG_LOCAL) {
+ get_j1939_ecu(ecu);
+ if (name)
+ *name = ecu->name;
+ if (addr)
+ *addr = ecu->sa;
+ goto found;
+ }
+ }
+ ecu = NULL;
+ for (j = 0, paddr = parent->ents; j < J1939_IDLE_ADDR; ++j, ++paddr) {
+ if (paddr->ecu)
+ continue;
+ if (paddr->flags & ECUFLAG_LOCAL) {
+ if (name)
+ *name = 0;
+ if (addr)
+ *addr = j;
+ goto found;
+ }
+ }
+ ecu = ERR_PTR(-EHOSTDOWN);
+found:
+ read_unlock_bh(&parent->lock);
+ put_j1939_segment(parent);
+ return ecu;
+}
+
+/* ecu lookup helper */
+static struct j1939_ecu *_j1939_ecu_find_by_name(name_t name,
+ struct j1939_segment *jseg)
+{
+ struct j1939_ecu *ecu;
+
+ read_lock_bh(&jseg->lock);
+ list_for_each_entry(ecu, &jseg->ecus, list) {
+ if (ecu->name == name) {
+ get_j1939_ecu(ecu);
+ goto found_on_intf;
+ }
+ }
+ ecu = NULL;
+found_on_intf:
+ read_unlock_bh(&jseg->lock);
+ return ecu;
+}
+
+/* ecu lookup by name */
+struct j1939_ecu *j1939_ecu_find_by_name(name_t name, int ifindex)
+{
+ struct j1939_ecu *ecu;
+ struct j1939_segment *jseg;
+
+ if (!name)
+ return NULL;
+ if (ifindex) {
+ jseg = j1939_segment_find(ifindex);
+ if (!jseg)
+ return NULL;
+ ecu = _j1939_ecu_find_by_name(name, jseg);
+ put_j1939_segment(jseg);
+ return ecu;
+ }
+ /* iterate segments */
+ spin_lock_bh(&segments.lock);
+ list_for_each_entry(jseg, &segments.list, flist) {
+ get_j1939_segment(jseg);
+ ecu = _j1939_ecu_find_by_name(name, jseg);
+ put_j1939_segment(jseg);
+ if (ecu)
+ goto found;
+ }
+ ecu = NULL;
+found:
+ spin_unlock_bh(&segments.lock);
+ return ecu;
+}
+
+/* PROC */
+static int j1939_proc_addr(struct seq_file *sqf, void *v)
+{
+ struct j1939_segment *jseg;
+ struct net_device *netdev;
+ struct addr_ent *paddr;
+ int j, flags;
+ ktime_t now;
+ struct timeval tv;
+
+ now = ktime_get();
+ seq_printf(sqf, "iface\tSA\tflags\trxtime\n");
+ spin_lock_bh(&segments.lock);
+ list_for_each_entry(jseg, &segments.list, flist) {
+ get_j1939_segment(jseg);
+ netdev = dev_get_by_index(&init_net, jseg->ifindex);
+ if (!netdev) {
+ pr_alert("j1939 proc: ifindex %i not found\n",
+ jseg->ifindex);
+ put_j1939_segment(jseg);
+ continue;
+ }
+ read_lock_bh(&jseg->lock);
+ for (j = 0, paddr = jseg->ents; j < J1939_IDLE_ADDR;
+ ++j, ++paddr) {
+ flags = paddr->flags;
+ if (paddr->ecu)
+ flags |= paddr->ecu->flags;
+ tv = ktime_to_timeval(ktime_sub(now, paddr->rxtime));
+ if (!paddr->flags && !paddr->ecu)
+ continue;
+ seq_printf(sqf, "%s\t%02x\t%c%c%c%c\t-%lu.%06lu\n",
+ netdev->name, j,
+ (flags & ECUFLAG_LOCAL) ? 'L' : '-',
+ (flags & ECUFLAG_REMOTE) ? 'R' : '-',
+ (paddr->flags) ? 'S' : '-',
+ paddr->ecu ? 'E' : '-',
+ tv.tv_sec, tv.tv_usec);
+ }
+ read_unlock_bh(&jseg->lock);
+ dev_put(netdev);
+ put_j1939_segment(jseg);
+ }
+ spin_unlock_bh(&segments.lock);
+ return 0;
+}
+
+static int j1939_proc_ecu(struct seq_file *sqf, void *v)
+{
+ struct j1939_segment *jseg;
+ struct j1939_ecu *ecu;
+ struct net_device *netdev;
+ ktime_t now;
+ struct timeval tv;
+ char sa[4];
+
+ now = ktime_get();
+ seq_printf(sqf, "iface\taddr\tname\tflags\trxtime\n");
+ spin_lock_bh(&segments.lock);
+ list_for_each_entry(jseg, &segments.list, flist) {
+ get_j1939_segment(jseg);
+ netdev = dev_get_by_index(&init_net, jseg->ifindex);
+ if (!netdev) {
+ pr_alert("j1939 proc: ifindex %i not found\n",
+ jseg->ifindex);
+ put_j1939_segment(jseg);
+ continue;
+ }
+ read_lock_bh(&jseg->lock);
+ list_for_each_entry(ecu, &jseg->ecus, list) {
+ tv = ktime_to_timeval(ktime_sub(now, ecu->rxtime));
+ if (j1939_address_is_unicast(ecu->sa) &&
+ (ecu->parent->ents[ecu->sa].ecu == ecu))
+ snprintf(sa, sizeof(sa), "%02x", ecu->sa);
+ else
+ strcpy(sa, "-");
+ seq_printf(sqf, "%s\t%s\t%016llx\t%c\t-%lu.%06lu\n",
+ netdev->name, sa,
+ (unsigned long long)ecu->name,
+ (ecu->flags & ECUFLAG_LOCAL) ? 'L' : 'R',
+ tv.tv_sec, tv.tv_usec);
+ }
+ read_unlock_bh(&jseg->lock);
+ dev_put(netdev);
+ put_j1939_segment(jseg);
+ }
+ spin_unlock_bh(&segments.lock);
+ return 0;
+}
+
+/* exported init */
+int __init j1939bus_module_init(void)
+{
+ INIT_LIST_HEAD(&segments.list);
+ spin_lock_init(&segments.lock);
+ j1939_proc_add("addr", j1939_proc_addr, NULL);
+ j1939_proc_add("ecu", j1939_proc_ecu, NULL);
+ return 0;
+}
+
+void j1939bus_module_exit(void)
+{
+ struct j1939_segment *jseg;
+ struct net_device *netdev;
+
+ spin_lock_bh(&segments.lock);
+ while (!list_empty(&segments.list)) {
+ jseg = list_first_entry(&segments.list,
+ struct j1939_segment, flist);
+ netdev = dev_get_by_index(&init_net, jseg->ifindex);
+ spin_unlock_bh(&segments.lock);
+ j1939_segment_detach(netdev);
+ dev_put(netdev);
+ spin_lock_bh(&segments.lock);
+ }
+ spin_unlock_bh(&segments.lock);
+
+ j1939_proc_remove("ecu");
+ j1939_proc_remove("addr");
+}
+
+
diff --git a/net/can/j1939/filter.c b/net/can/j1939/filter.c
new file mode 100644
index 0000000..c10f7b9
--- /dev/null
+++ b/net/can/j1939/filter.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2010-2011 EIA Electronics
+ *
+ * Authors:
+ * Pieter Beyens <pieter.beyens-/BeEPy95v10@public.gmane.org>
+ * Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+
+#include "j1939-priv.h"
+
+static LIST_HEAD(filters);
+DEFINE_RWLOCK(j1939_receiver_rwlock); /* protects the filter list */
+
+struct filter {
+ struct list_head list;
+ void *vp;
+ void (*fn)(struct sk_buff *, void *);
+};
+
+int j1939_recv_distribute(struct sk_buff *skb)
+{
+ struct filter *filter;
+
+ read_lock_bh(&j1939_receiver_rwlock);
+ list_for_each_entry(filter, &filters, list)
+ filter->fn(skb, filter->vp);
+ read_unlock_bh(&j1939_receiver_rwlock);
+
+ return 0;
+}
+
+int j1939_recv_add(void *vp, void (*fn)(struct sk_buff *, void *))
+{
+ struct filter *f;
+
+ f = kzalloc(sizeof(*f), GFP_KERNEL);
+ if (!f)
+ return -ENOMEM;
+
+ f->vp = vp;
+ f->fn = fn;
+
+ j1939_recv_suspend();
+ list_add(&f->list, &filters);
+ j1939_recv_resume();
+ return 0;
+}
+
+int j1939_recv_remove(void *vp, void (*fn)(struct sk_buff *, void *))
+{
+ struct filter *filter;
+ int found = 0;
+
+ j1939_recv_suspend();
+ list_for_each_entry(filter, &filters, list) {
+ if ((filter->vp == vp) && (filter->fn == fn)) {
+ list_del_init(&filter->list);
+ kfree(filter);
+ found = 1;
+ break;
+ }
+ }
+ j1939_recv_resume();
+ return found ? 0 : -ENOENT;
+}
+
diff --git a/net/can/j1939/j1939-priv.h b/net/can/j1939/j1939-priv.h
new file mode 100644
index 0000000..3531ee5
--- /dev/null
+++ b/net/can/j1939/j1939-priv.h
@@ -0,0 +1,314 @@
+/*
+ * j1939-priv.h
+ *
+ * Copyright (c) 2010-2011 EIA Electronics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _J1939_PRIV_H_
+#define _J1939_PRIV_H_
+
+#include <linux/kref.h>
+#include <linux/list.h>
+#include <net/sock.h>
+
+#include <linux/seq_file.h>
+#include <linux/proc_fs.h>
+#include <linux/module.h>
+#include <linux/can/j1939.h>
+#include <linux/atomic.h>
+
+/* TODO: return ENETRESET on busoff. */
+
+#define ECUFLAG_LOCAL 0x01
+#define ECUFLAG_REMOTE 0x02
+
+#define PGN_REQUEST 0x0ea00
+#define PGN_ADDRESS_CLAIMED 0x0ee00
+#define PGN_MAX 0x3ffff
+
+#define SA_MAX_UNICAST 0xfd
+/*
+ * j1939 devices
+ */
+struct j1939_ecu {
+ struct list_head list;
+ ktime_t rxtime;
+ name_t name;
+ int flags;
+ uint8_t sa;
+ /*
+ * atomic flag, set by ac_timer
+ * cleared/processed by segment's tasklet
+ * indicates that this ecu successfully claimed @sa as its address
+ * By communicating this from the ac_timer event to segments tasklet,
+ * a context locking problem is solved. All other 'ecu readers'
+ * must only lock with _bh, not with _irq.
+ */
+ atomic_t ac_delay_expired;
+ struct hrtimer ac_timer;
+ struct kref kref;
+ struct j1939_segment *parent;
+};
+#define to_j1939_ecu(x) container_of((x), struct j1939_ecu, dev)
+
+struct j1939_segment {
+ struct list_head ecus; /*
+ * local list entry in parent
+ * These allow irq (& softirq) context lookups on j1939 devices
+ * This approach (seperate lists) is done as the other 2 alternatives
+ * are not easier or even wrong
+ * 1) using the pure kobject methods involves mutexes, which are not
+ * allowed in irq context.
+ * 2) duplicating data structures would require a lot of synchronization
+ * code
+ * usage:
+ */
+ rwlock_t lock; /*
+ * segments need a lock to protect the above list
+ */
+ struct list_head flist; /*
+ * list entry for use by interrupt lookup routines
+ */
+ int ifindex;
+ struct addr_ent {
+ ktime_t rxtime;
+ struct j1939_ecu *ecu;
+ int flags;
+ } ents[256];
+
+ /*
+ * tasklet to process ecu address claimed events.
+ * These events raise in hardirq context. Signalling the event
+ * and scheduling this tasklet successfully moves the
+ * event to softirq context
+ */
+ struct tasklet_struct ac_task;
+ /*
+ * list of 256 ecu ptrs, that cache the claimed addresses.
+ * also protected by the above lock
+ * don't use directly, use j1939_ecu_set_address() instead
+ */
+ struct kref kref;
+};
+#define to_j1939_segment(x) container_of((x), struct j1939_segment, dev)
+
+extern void put_j1939_ecu(struct j1939_ecu *ecu);
+extern void put_j1939_segment(struct j1939_segment *segment);
+static inline struct j1939_ecu *get_j1939_ecu(struct j1939_ecu *dut)
+{
+ kref_get(&dut->kref);
+ return dut;
+}
+static inline struct j1939_segment *get_j1939_segment(struct j1939_segment *dut)
+{
+ kref_get(&dut->kref);
+ return dut;
+}
+
+/*
+ * conversion function between (struct sock | struct sk_buff)->sk_priority
+ * from linux and j1939 priority field
+ */
+static inline int j1939_prio(int sk_priority)
+{
+ if (sk_priority < 0)
+ return 6; /* default */
+ else if (sk_priority > 7)
+ return 0;
+ else
+ return 7 - sk_priority;
+}
+static inline int j1939_to_sk_priority(int j1939_prio)
+{
+ return 7 - j1939_prio;
+}
+
+static inline int j1939_address_is_valid(uint8_t sa)
+{
+ return sa != J1939_NO_ADDR;
+}
+
+static inline int j1939_address_is_unicast(uint8_t sa)
+{
+ return sa <= SA_MAX_UNICAST;
+}
+
+static inline int pgn_is_pdu1(pgn_t pgn)
+{
+ /* ignore dp & res bits for this */
+ return (pgn & 0xff00) < 0xf000;
+}
+
+static inline int pgn_is_valid(pgn_t pgn)
+{
+ return pgn <= PGN_MAX;
+}
+
+/* utility to correctly unregister a SA */
+static inline void j1939_ecu_remove_sa_locked(struct j1939_ecu *ecu)
+{
+ if (!j1939_address_is_unicast(ecu->sa))
+ return;
+ if (ecu->parent->ents[ecu->sa].ecu == ecu)
+ ecu->parent->ents[ecu->sa].ecu = NULL;
+}
+
+static inline void j1939_ecu_remove_sa(struct j1939_ecu *ecu)
+{
+ if (!j1939_address_is_unicast(ecu->sa))
+ return;
+ write_lock_bh(&ecu->parent->lock);
+ j1939_ecu_remove_sa_locked(ecu);
+ write_unlock_bh(&ecu->parent->lock);
+}
+
+extern int j1939_name_to_sa(uint64_t name, int ifindex);
+extern struct j1939_ecu *j1939_ecu_find_by_addr(int sa, int ifindex);
+extern struct j1939_ecu *j1939_ecu_find_by_name(name_t name, int ifindex);
+/* find_by_name, with kref & read_lock taken */
+extern struct j1939_ecu *j1939_ecu_find_segment_default_tx(
+ int ifindex, name_t *pname, uint8_t *paddr);
+
+extern void j1939_put_promisc_receiver(int ifindex);
+extern void j1939_get_promisc_receiver(int ifindex);
+
+extern int j1939_proc_add(const char *file,
+ int (*seq_show)(struct seq_file *sqf, void *v),
+ write_proc_t write);
+extern void j1939_proc_remove(const char *file);
+
+extern const char j1939_procname[];
+/* j1939 printk */
+#define j1939_printk(level, ...) printk(level "J1939 " __VA_ARGS__)
+
+#define j1939_err(...) j1939_printk(KERN_ERR , __VA_ARGS__)
+#define j1939_warning(...) j1939_printk(KERN_WARNING , __VA_ARGS__)
+#define j1939_notice(...) j1939_printk(KERN_NOTICE , __VA_ARGS__)
+#define j1939_info(...) j1939_printk(KERN_INFO , __VA_ARGS__)
+#ifdef DEBUG
+#define j1939_debug(...) j1939_printk(KERN_DEBUG , __VA_ARGS__)
+#else
+#define j1939_debug(...)
+#endif
+
+struct sk_buff;
+
+/* control buffer of the sk_buff */
+struct j1939_sk_buff_cb {
+ int ifindex;
+ priority_t priority;
+ struct {
+ name_t name;
+ uint8_t addr;
+ int flags;
+ } src, dst;
+ pgn_t pgn;
+ int msg_flags;
+ /* for tx, MSG_SYN will be used to sync on sockets */
+};
+#define J1939_MSG_RESERVED MSG_SYN
+#define J1939_MSG_SYNC MSG_SYN
+
+static inline int j1939cb_is_broadcast(const struct j1939_sk_buff_cb *cb)
+{
+ return (!cb->dst.name && (cb->dst.addr >= 0xff));
+}
+
+/* J1939 stack */
+enum {
+ j1939_level_can,
+ j1939_level_transport,
+ j1939_level_sky,
+};
+
+#define RESULT_STOP 1
+/*
+ * return RESULT_STOP when stack processing may stop.
+ * it is up to the stack entry itself to kfree_skb() the sk_buff
+ */
+
+extern int j1939_send(struct sk_buff *, int level);
+extern int j1939_recv(struct sk_buff *, int level);
+
+/* stack entries */
+extern int j1939_recv_promisc(struct sk_buff *);
+extern int j1939_send_transport(struct sk_buff *);
+extern int j1939_recv_transport(struct sk_buff *);
+extern int j1939_send_address_claim(struct sk_buff *);
+extern int j1939_recv_address_claim(struct sk_buff *);
+
+extern int j1939_recv_distribute(struct sk_buff *);
+
+/* network management */
+/*
+ * j1939_ecu_get_register
+ * 'create' & 'register' & 'get' new ecu
+ * when a matching ecu already exists, the behaviour depends
+ * on @return_existing.
+ * when @return_existing is 0, -EEXISTS is returned
+ * when @return_exsiting is 1, that ecu is 'get' & returned.
+ * @flags is only used when creating new ecu.
+ */
+extern struct j1939_ecu *j1939_ecu_get_register(name_t name, int ifindex,
+ int flags, int return_existing);
+extern void j1939_ecu_unregister(struct j1939_ecu *);
+
+extern int j1939_segment_attach(struct net_device *);
+extern int j1939_segment_detach(struct net_device *);
+
+extern int j1939_segment_register(struct net_device *);
+extern void j1939_segment_unregister(struct j1939_segment *);
+extern struct j1939_segment *j1939_segment_find(int ifindex);
+
+extern void j1939sk_netdev_event(int ifindex, int error_code);
+
+/* add/remove receiver */
+extern int j1939_recv_add(void *vp, void (*fn)(struct sk_buff *, void *));
+extern int j1939_recv_remove(void *vp, void (*fn)(struct sk_buff *, void *));
+
+/*
+ * provide public access to this lock
+ * so sparse can verify the context balance
+ */
+extern rwlock_t j1939_receiver_rwlock;
+static inline void j1939_recv_suspend(void)
+{
+ write_lock_bh(&j1939_receiver_rwlock);
+}
+
+static inline void j1939_recv_resume(void)
+{
+ write_unlock_bh(&j1939_receiver_rwlock);
+}
+
+/* locks the recv module */
+extern void j1939_recv_suspend(void);
+extern void j1939_recv_resume(void);
+
+/*
+ * decrement pending skb for a j1939 socket
+ */
+extern void j1939_sock_pending_del(struct sock *sk);
+
+/* seperate module-init/modules-exit's */
+extern __init int j1939_proc_module_init(void);
+extern __init int j1939bus_module_init(void);
+extern __init int j1939sk_module_init(void);
+extern __init int j1939tp_module_init(void);
+
+extern void j1939_proc_module_exit(void);
+extern void j1939bus_module_exit(void);
+extern void j1939sk_module_exit(void);
+extern void j1939tp_module_exit(void);
+
+/* rtnetlink */
+extern const struct rtnl_af_ops j1939_rtnl_af_ops;
+extern int j1939rtnl_new_addr(struct sk_buff *, struct nlmsghdr *, void *arg);
+extern int j1939rtnl_del_addr(struct sk_buff *, struct nlmsghdr *, void *arg);
+extern int j1939rtnl_dump_addr(struct sk_buff *, struct netlink_callback *);
+
+#endif /* _J1939_PRIV_H_ */
diff --git a/net/can/j1939/main.c b/net/can/j1939/main.c
new file mode 100644
index 0000000..7edf843
--- /dev/null
+++ b/net/can/j1939/main.c
@@ -0,0 +1,458 @@
+/*
+ * Copyright (c) 2010-2011 EIA Electronics
+ *
+ * Authors:
+ * Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
+ * Pieter Beyens <pieter.beyens-/BeEPy95v10@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+
+/*
+ * Core of can-j1939 that links j1939 to CAN.
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/socket.h>
+#include <linux/list.h>
+#include <linux/if_arp.h>
+#include <net/tcp_states.h>
+
+#include <linux/can.h>
+#include <linux/can/core.h>
+#include "j1939-priv.h"
+
+MODULE_DESCRIPTION("PF_CAN SAE J1939");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("EIA Electronics (Kurt Van Dijck & Pieter Beyens)");
+
+static struct {
+ struct notifier_block notifier;
+} s;
+
+/* LOWLEVEL CAN interface */
+
+/* CAN_HDR: #bytes before can_frame data part */
+#define CAN_HDR (offsetof(struct can_frame, data))
+/* CAN_FTR: #bytes beyond data part */
+#define CAN_FTR (sizeof(struct can_frame)-CAN_HDR-\
+ sizeof(((struct can_frame *)0)->data))
+
+static void j1939_recv_ecu_flags(struct sk_buff *skb, void *data)
+{
+ struct j1939_segment *jseg = data;
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ struct addr_ent *paddr;
+
+ if (!jseg)
+ return;
+ write_lock_bh(&jseg->lock);
+ if (j1939_address_is_unicast(cb->src.addr)) {
+ paddr = &jseg->ents[cb->src.addr];
+ paddr->rxtime = ktime_get();
+ if (0x0ee00 == cb->pgn) {
+ /* do not touch many things for Address claims */
+ } else if (paddr->ecu) {
+ paddr->ecu->rxtime = paddr->rxtime;
+ cb->src.flags = paddr->ecu->flags;
+ } else {
+ if (!paddr->flags)
+ paddr->flags |= ECUFLAG_REMOTE;
+ cb->src.flags = paddr->flags;
+ }
+ }
+
+ if (j1939_address_is_unicast(cb->dst.addr)) {
+ paddr = &jseg->ents[cb->dst.addr];
+ if (paddr->ecu)
+ cb->dst.flags = paddr->ecu->flags;
+ else
+ cb->dst.flags = paddr->flags ?: ECUFLAG_REMOTE;
+ }
+ write_unlock_bh(&jseg->lock);
+}
+
+/* lowest layer */
+static void j1939_can_recv(struct sk_buff *skb, void *data)
+{
+ int orig_len;
+ struct j1939_sk_buff_cb *sk_addr;
+ struct can_frame *msg;
+ uint8_t saved_cb[sizeof(skb->cb)];
+
+ BUILD_BUG_ON(sizeof(*sk_addr) > sizeof(skb->cb));
+ /*
+ * get a pointer to the header of the skb
+ * the skb payload (pointer) is moved, so that the next skb_data
+ * returns the actual payload
+ */
+ msg = (void *)skb->data;
+ orig_len = skb->len;
+ skb_pull(skb, CAN_HDR);
+ /* fix length, set to dlc, with 8 maximum */
+ skb_trim(skb, min_t(uint8_t, msg->can_dlc, 8));
+
+ /* set addr */
+ sk_addr = (struct j1939_sk_buff_cb *)skb->cb;
+ memcpy(saved_cb, sk_addr, sizeof(saved_cb));
+ memset(sk_addr, 0, sizeof(*sk_addr));
+ if (skb->dev)
+ sk_addr->ifindex = skb->dev->ifindex;
+ sk_addr->priority = (msg->can_id & 0x1c000000) >> 26;
+ sk_addr->src.addr = msg->can_id & 0xff;
+ sk_addr->pgn = (msg->can_id & 0x3ffff00) >> 8;
+ if (pgn_is_pdu1(sk_addr->pgn)) {
+ /* Type 1: with destination address */
+ sk_addr->dst.addr = sk_addr->pgn & 0xff;
+ /* normalize pgn: strip dst address */
+ sk_addr->pgn &= 0x3ff00;
+ } else {
+ /* set broadcast address */
+ sk_addr->dst.addr = J1939_NO_ADDR;
+ }
+ j1939_recv_ecu_flags(skb, data);
+ j1939_recv(skb, j1939_level_can);
+
+ /* restore the original skb, should always work */
+ skb_push(skb, CAN_HDR);
+ /* no safety check, it just restores the skbuf's contents */
+ __skb_trim(skb, orig_len);
+ memcpy(sk_addr, saved_cb, sizeof(saved_cb));
+}
+
+static int j1939_send_can(struct sk_buff *skb)
+{
+ int ret, dlc;
+ canid_t canid;
+ struct j1939_sk_buff_cb *sk_addr;
+ struct net_device *netdev = NULL;
+ struct can_frame *msg;
+
+ dlc = skb->len;
+ if (dlc > 8)
+ return -EMSGSIZE;
+ ret = pskb_expand_head(skb, SKB_DATA_ALIGN(CAN_HDR),
+ CAN_FTR + (8-dlc), GFP_ATOMIC);
+ if (ret < 0)
+ return ret;
+
+ msg = (void *)skb_push(skb, CAN_HDR);
+ BUG_ON(!msg);
+ /* make it a full can frame */
+ skb_put(skb, CAN_FTR + (8 - dlc));
+
+ sk_addr = (struct j1939_sk_buff_cb *)skb->cb;
+ canid = CAN_EFF_FLAG |
+ (sk_addr->src.addr & 0xff) |
+ ((sk_addr->priority & 0x7) << 26);
+ if (pgn_is_pdu1(sk_addr->pgn))
+ canid |= ((sk_addr->pgn & 0x3ff00) << 8) |
+ ((sk_addr->dst.addr & 0xff) << 8);
+ else
+ canid |= ((sk_addr->pgn & 0x3ffff) << 8);
+
+ msg->can_id = canid;
+ msg->can_dlc = dlc;
+
+ /* set net_device */
+ ret = -ENODEV;
+ if (!skb->dev) {
+ if (!sk_addr->ifindex)
+ goto failed;
+ netdev = dev_get_by_index(&init_net, sk_addr->ifindex);
+ if (!netdev)
+ goto failed;
+ skb->dev = netdev;
+ }
+
+ /* fix the 'always free' policy of can_send */
+ skb = skb_get(skb);
+ ret = can_send(skb, 1);
+ if (!ret) {
+ /* free when can_send succeeded */
+ kfree_skb(skb);
+ /* is this necessary ? */
+ ret = RESULT_STOP;
+ }
+failed:
+ if (netdev)
+ dev_put(netdev);
+ return ret;
+}
+
+static int j1939_send_normalize(struct sk_buff *skb)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ struct j1939_segment *jseg;
+ struct addr_ent *paddr;
+ struct j1939_ecu *ecu;
+ int ret = 0;
+
+ /* apply sanity checks */
+ cb->pgn &= (pgn_is_pdu1(cb->pgn)) ? 0x3ff00 : 0x3ffff;
+ if (cb->priority > 7)
+ cb->priority = 6;
+
+ /* verify source */
+ if (!cb->ifindex)
+ return -ENETUNREACH;
+ jseg = j1939_segment_find(cb->ifindex);
+ if (!jseg)
+ return -ENETUNREACH;
+ read_lock_bh(&jseg->lock);
+ /* verify source */
+ if (cb->src.name) {
+ ecu = j1939_ecu_find_by_name(cb->src.name, cb->ifindex);
+ cb->src.flags = ecu ? ecu->flags : 0;
+ if (ecu)
+ put_j1939_ecu(ecu);
+ } else if (j1939_address_is_unicast(cb->src.addr)) {
+ paddr = &jseg->ents[cb->src.addr];
+ cb->src.flags = paddr->flags;
+ } else if (cb->src.addr == J1939_IDLE_ADDR) {
+ /* allow always */
+ cb->src.flags = ECUFLAG_LOCAL;
+ } else {
+ /* J1939_NO_ADDR */
+ cb->src.flags = 0;
+ }
+ if (cb->src.flags & ECUFLAG_REMOTE) {
+ ret = -EREMOTE;
+ goto failed;
+ } else if (!(cb->src.flags & ECUFLAG_LOCAL)) {
+ ret = -EADDRNOTAVAIL;
+ goto failed;
+ }
+
+ /* verify destination */
+ if (cb->dst.name) {
+ ecu = j1939_ecu_find_by_name(cb->dst.name, cb->ifindex);
+ if (!ecu) {
+ ret = -EADDRNOTAVAIL;
+ goto failed;
+ }
+ cb->dst.flags = ecu->flags;
+ put_j1939_ecu(ecu);
+ } else if (cb->dst.addr == J1939_IDLE_ADDR) {
+ /* not a valid destination */
+ ret = -EADDRNOTAVAIL;
+ goto failed;
+ } else if (j1939_address_is_unicast(cb->dst.addr)) {
+ paddr = &jseg->ents[cb->dst.addr];
+ cb->dst.flags = paddr->flags;
+ } else {
+ cb->dst.flags = 0;
+ }
+
+ ret = 0;
+failed:
+ read_unlock_bh(&jseg->lock);
+ put_j1939_segment(jseg);
+ return ret;
+}
+
+/* TOPLEVEL interface */
+int j1939_recv(struct sk_buff *skb, int level)
+{
+ int ret;
+
+ /* this stack operates with fallthrough switch statement */
+ switch (level) {
+ default:
+ WARN_ONCE(1, "%s: unsupported level %i\n", __func__, level);
+ return 0;
+ case j1939_level_can:
+ ret = j1939_recv_address_claim(skb);
+ if (unlikely(ret))
+ break;
+ ret = j1939_recv_promisc(skb);
+ if (unlikely(ret))
+ break;
+ ret = j1939_recv_transport(skb);
+ if (unlikely(ret))
+ break;
+ case j1939_level_transport:
+ case j1939_level_sky:
+ ret = j1939_recv_distribute(skb);
+ break;
+ }
+ if (ret == RESULT_STOP)
+ return 0;
+ return ret;
+
+}
+EXPORT_SYMBOL_GPL(j1939_recv);
+
+int j1939_send(struct sk_buff *skb, int level)
+{
+ int ret;
+ struct sock *sk = NULL;
+
+ /* this stack operates with fallthrough switch statement */
+ switch (level) {
+ default:
+ WARN_ONCE(1, "%s: unsupported level %i\n", __func__, level);
+ case j1939_level_sky:
+ sk = skb->sk;
+ if (sk)
+ sock_hold(sk);
+ ret = j1939_send_normalize(skb);
+ if (unlikely(ret))
+ break;
+ ret = j1939_send_transport(skb);
+ if (unlikely(ret))
+ break;
+ case j1939_level_transport:
+ ret = j1939_send_address_claim(skb);
+ if (unlikely(ret))
+ break;
+ case j1939_level_can:
+ ret = j1939_send_can(skb);
+ if (RESULT_STOP == ret)
+ /* don't mark as stopped, it can't be better */
+ ret = 0;
+ break;
+ }
+ if (ret == RESULT_STOP)
+ ret = 0;
+ else if (!ret && sk)
+ j1939_sock_pending_del(sk);
+ if (sk)
+ sock_put(sk);
+ return ret;
+
+}
+EXPORT_SYMBOL_GPL(j1939_send);
+
+/* NETDEV MANAGEMENT */
+
+#define J1939_CAN_ID CAN_EFF_FLAG
+#define J1939_CAN_MASK (CAN_EFF_FLAG | CAN_RTR_FLAG)
+int j1939_segment_attach(struct net_device *netdev)
+{
+ int ret;
+ struct j1939_segment *jseg;
+
+ if (!netdev)
+ return -ENODEV;
+ if (netdev->type != ARPHRD_CAN)
+ return -EAFNOSUPPORT;
+
+ ret = j1939_segment_register(netdev);
+ if (ret < 0)
+ goto fail_register;
+ jseg = j1939_segment_find(netdev->ifindex);
+ ret = can_rx_register(netdev, J1939_CAN_ID, J1939_CAN_MASK,
+ j1939_can_recv, jseg, "j1939");
+ if (ret < 0)
+ goto fail_can_rx;
+ return 0;
+
+fail_can_rx:
+ j1939_segment_unregister(jseg);
+ put_j1939_segment(jseg);
+fail_register:
+ return ret;
+}
+
+int j1939_segment_detach(struct net_device *netdev)
+{
+ struct j1939_segment *jseg;
+
+ BUG_ON(!netdev);
+ jseg = j1939_segment_find(netdev->ifindex);
+ if (!jseg)
+ return -EHOSTDOWN;
+ can_rx_unregister(netdev, J1939_CAN_ID, J1939_CAN_MASK,
+ j1939_can_recv, jseg);
+ j1939_segment_unregister(jseg);
+ put_j1939_segment(jseg);
+ j1939sk_netdev_event(netdev->ifindex, EHOSTDOWN);
+ return 0;
+}
+
+static int j1939_notifier(struct notifier_block *nb,
+ unsigned long msg, void *data)
+{
+ struct net_device *netdev = (struct net_device *)data;
+ struct j1939_segment *jseg;
+
+ if (!net_eq(dev_net(netdev), &init_net))
+ return NOTIFY_DONE;
+
+ if (netdev->type != ARPHRD_CAN)
+ return NOTIFY_DONE;
+
+ switch (msg) {
+ case NETDEV_UNREGISTER:
+ jseg = j1939_segment_find(netdev->ifindex);
+ if (!jseg)
+ break;
+ j1939_segment_unregister(jseg);
+ j1939sk_netdev_event(netdev->ifindex, ENODEV);
+ break;
+
+ case NETDEV_DOWN:
+ j1939sk_netdev_event(netdev->ifindex, ENETDOWN);
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+/* MODULE interface */
+
+static __init int j1939_module_init(void)
+{
+ int ret;
+
+ pr_info("can: SAE J1939\n");
+
+ ret = j1939_proc_module_init();
+ if (ret < 0)
+ goto fail_proc;
+
+ s.notifier.notifier_call = j1939_notifier;
+ register_netdevice_notifier(&s.notifier);
+
+ ret = j1939bus_module_init();
+ if (ret < 0)
+ goto fail_bus;
+ ret = j1939sk_module_init();
+ if (ret < 0)
+ goto fail_sk;
+ ret = j1939tp_module_init();
+ if (ret < 0)
+ goto fail_tp;
+ return 0;
+
+ j1939tp_module_exit();
+fail_tp:
+ j1939sk_module_exit();
+fail_sk:
+ j1939bus_module_exit();
+fail_bus:
+ unregister_netdevice_notifier(&s.notifier);
+
+ j1939_proc_module_exit();
+fail_proc:
+ return ret;
+}
+
+static __exit void j1939_module_exit(void)
+{
+ j1939tp_module_exit();
+ j1939sk_module_exit();
+ j1939bus_module_exit();
+
+ unregister_netdevice_notifier(&s.notifier);
+
+ j1939_proc_module_exit();
+}
+
+module_init(j1939_module_init);
+module_exit(j1939_module_exit);
diff --git a/net/can/j1939/proc.c b/net/can/j1939/proc.c
new file mode 100644
index 0000000..76acfa0
--- /dev/null
+++ b/net/can/j1939/proc.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2010-2011 EIA Electronics
+ *
+ * Authors:
+ * Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+
+#include <linux/version.h>
+#include <linux/string.h>
+#include <linux/seq_file.h>
+#include <linux/proc_fs.h>
+#include <linux/uaccess.h>
+
+#include "j1939-priv.h"
+
+const char j1939_procname[] = "can-j1939";
+
+static struct proc_dir_entry *rootdir;
+
+static int j1939_proc_open(struct inode *inode, struct file *file)
+{
+ struct proc_dir_entry *pde = PDE(inode);
+ int (*fn)(struct seq_file *sqf, void *v) = pde->data;
+
+ return single_open(file, fn, pde);
+}
+
+/* copied from fs/proc/generic.c */
+static ssize_t
+proc_file_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct inode *inode = file->f_path.dentry->d_inode;
+ struct proc_dir_entry *dp;
+
+ dp = PDE(inode);
+
+ if (!dp->write_proc)
+ return -EIO;
+
+ /* FIXME: does this routine need ppos? probably... */
+ return dp->write_proc(file, buffer, count, dp->data);
+}
+
+static const struct file_operations j1939_proc_ops = {
+ .owner = THIS_MODULE,
+ .open = j1939_proc_open,
+ .read = seq_read,
+ .write = proc_file_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+int j1939_proc_add(const char *file,
+ int (*seq_show)(struct seq_file *sqf, void *v),
+ write_proc_t write)
+{
+ struct proc_dir_entry *pde;
+ int mode = 0;
+
+ if (seq_show)
+ mode |= 0444;
+ if (write)
+ mode |= 0200;
+
+ if (!rootdir)
+ return -ENODEV;
+ pde = proc_create(file, mode, rootdir, &j1939_proc_ops);
+ if (!pde)
+ goto fail_create;
+ pde->data = seq_show;
+ pde->write_proc = write;
+ return 0;
+
+fail_create:
+ return -ENOENT;
+}
+EXPORT_SYMBOL(j1939_proc_add);
+
+void j1939_proc_remove(const char *file)
+{
+ remove_proc_entry(file, rootdir);
+}
+EXPORT_SYMBOL(j1939_proc_remove);
+
+__init int j1939_proc_module_init(void)
+{
+ /* create /proc/net/can directory */
+ rootdir = proc_mkdir(j1939_procname, init_net.proc_net);
+ if (!rootdir)
+ return -EINVAL;
+ return 0;
+}
+
+void j1939_proc_module_exit(void)
+{
+ if (rootdir)
+ proc_net_remove(&init_net, j1939_procname);
+}
+
diff --git a/net/can/j1939/promisc.c b/net/can/j1939/promisc.c
new file mode 100644
index 0000000..14be755
--- /dev/null
+++ b/net/can/j1939/promisc.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2010-2011 EIA Electronics
+ *
+ * Authors:
+ * Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+
+#include <linux/version.h>
+#include <linux/skbuff.h>
+#include <linux/sysctl.h>
+#include "j1939-priv.h"
+
+static atomic_t n_promisc = ATOMIC_INIT(0);
+
+void j1939_get_promisc_receiver(int ifindex)
+{
+ atomic_inc(&n_promisc);
+}
+EXPORT_SYMBOL_GPL(j1939_get_promisc_receiver);
+
+void j1939_put_promisc_receiver(int ifindex)
+{
+ atomic_dec(&n_promisc);
+}
+EXPORT_SYMBOL_GPL(j1939_put_promisc_receiver);
+
+int j1939_recv_promisc(struct sk_buff *skb)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+
+ if ((cb->src.flags & ECUFLAG_REMOTE) &&
+ (cb->dst.flags & ECUFLAG_REMOTE)) {
+ if (!atomic_read(&n_promisc))
+ /* stop receive path */
+ return RESULT_STOP;
+ }
+ return 0;
+}
+
diff --git a/net/can/j1939/rtnl.c b/net/can/j1939/rtnl.c
new file mode 100644
index 0000000..851060f
--- /dev/null
+++ b/net/can/j1939/rtnl.c
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 2011 EIA Electronics
+ *
+ * Authors:
+ * Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+
+/*
+ * j1939-rtnl.c - netlink addressing interface
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/if_arp.h>
+
+#include "j1939-priv.h"
+
+static const struct nla_policy j1939_ifa_policy[IFA_J1939_MAX] = {
+ [IFA_J1939_ADDR] = { .type = NLA_U8, },
+ [IFA_J1939_NAME] = { .type = NLA_U64, },
+};
+
+int j1939rtnl_del_addr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ int ret;
+ struct ifaddrmsg *ifm;
+ struct j1939_segment *jseg;
+ uint8_t jaddr = J1939_NO_ADDR;
+ uint64_t jname = J1939_NO_NAME;
+
+ struct nlattr *nla, *tb[IFA_J1939_MAX];
+
+ if (!net_eq(sock_net(skb->sk), &init_net))
+ return -EINVAL;
+
+ nla = nlmsg_find_attr(nlh, sizeof(*ifm), IFA_LOCAL);
+ if (!nla)
+ return -EINVAL;
+
+ nla_parse_nested(tb, IFA_J1939_MAX-1, nla, j1939_ifa_policy);
+ if (tb[IFA_J1939_ADDR])
+ jaddr = nla_get_u8(tb[IFA_J1939_ADDR]);
+ if (tb[IFA_J1939_NAME])
+ jname = be64_to_cpu(nla_get_u64(tb[IFA_J1939_NAME]));
+
+ ifm = nlmsg_data(nlh);
+ jseg = j1939_segment_find(ifm->ifa_index);
+ if (!jseg)
+ return -EHOSTDOWN;
+
+ ret = 0;
+ if (j1939_address_is_unicast(jaddr)) {
+ struct addr_ent *ent;
+
+ ent = &jseg->ents[jaddr];
+ write_lock_bh(&jseg->lock);
+ if (!ent->flags)
+ ret = -EADDRNOTAVAIL;
+ else if (!(ent->flags & ECUFLAG_LOCAL))
+ ret = -EREMOTE;
+ else
+ ent->flags = 0;
+ write_unlock_bh(&jseg->lock);
+ } else if (jname) {
+ struct j1939_ecu *ecu;
+
+ ecu = j1939_ecu_find_by_name(jname, ifm->ifa_index);
+ if (ecu) {
+ if (ecu->flags & ECUFLAG_LOCAL) {
+ j1939_ecu_unregister(ecu);
+ put_j1939_ecu(ecu);
+ } else {
+ ret = -EREMOTE;
+ }
+ } else {
+ ret = -ENODEV;
+ }
+ }
+ put_j1939_segment(jseg);
+ return ret;
+}
+
+int j1939rtnl_new_addr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ struct ifaddrmsg *ifm;
+ struct j1939_segment *jseg;
+ uint8_t jaddr = J1939_NO_ADDR;
+ uint64_t jname = J1939_NO_NAME;
+ struct addr_ent *ent;
+ int ret;
+ struct nlattr *nla, *tb[IFA_J1939_MAX];
+
+ if (!net_eq(sock_net(skb->sk), &init_net))
+ return -EINVAL;
+
+ nla = nlmsg_find_attr(nlh, sizeof(*ifm), IFA_LOCAL);
+ if (!nla)
+ return -EINVAL;
+
+ ifm = nlmsg_data(nlh);
+ jseg = j1939_segment_find(ifm->ifa_index);
+ if (!jseg)
+ return -EHOSTDOWN;
+
+ nla_parse_nested(tb, IFA_J1939_MAX-1, nla, j1939_ifa_policy);
+ if (tb[IFA_J1939_ADDR])
+ jaddr = nla_get_u8(tb[IFA_J1939_ADDR]);
+ if (tb[IFA_J1939_NAME])
+ jname = be64_to_cpu(nla_get_u64(tb[IFA_J1939_NAME]));
+
+
+ ret = 0;
+ if (j1939_address_is_unicast(jaddr)) {
+ ent = &jseg->ents[jaddr];
+ write_lock_bh(&jseg->lock);
+ if ((ent->ecu && (ent->ecu->flags & ECUFLAG_REMOTE)) ||
+ (ent->flags & ECUFLAG_REMOTE))
+ ret = -EREMOTE;
+ else
+ ent->flags |= ECUFLAG_LOCAL;
+ write_unlock_bh(&jseg->lock);
+ } else if (jname) {
+ struct j1939_ecu *ecu;
+
+ ecu = j1939_ecu_get_register(jname, ifm->ifa_index,
+ ECUFLAG_LOCAL, 0);
+ if (IS_ERR(ecu))
+ ret = PTR_ERR(ecu);
+ else
+ put_j1939_ecu(ecu);
+ }
+ put_j1939_segment(jseg);
+ return ret;
+}
+
+static int j1939rtnl_fill_ifaddr(struct sk_buff *skb, int ifindex,
+ uint8_t addr, uint64_t name, int j1939_flags,
+ u32 pid, u32 seq, int event, unsigned int flags)
+{
+ struct ifaddrmsg *ifm;
+ struct nlmsghdr *nlh;
+ struct nlattr *nla;
+
+ nlh = nlmsg_put(skb, pid, seq, event, sizeof(*ifm), flags);
+ if (nlh == NULL)
+ return -EMSGSIZE;
+
+ ifm = nlmsg_data(nlh);
+ ifm->ifa_family = AF_CAN;
+ ifm->ifa_prefixlen = CAN_J1939;
+ ifm->ifa_flags = name ? 0 : IFA_F_PERMANENT;
+ ifm->ifa_scope = RT_SCOPE_LINK;
+ ifm->ifa_index = ifindex;
+
+ nla = nla_nest_start(skb, IFA_LOCAL);
+ if (j1939_address_is_unicast(addr))
+ NLA_PUT_U8(skb, IFA_J1939_ADDR, addr);
+ if (name)
+ NLA_PUT_U64(skb, IFA_J1939_NAME, cpu_to_be64(name));
+ nla_nest_end(skb, nla);
+
+ return nlmsg_end(skb, nlh);
+
+nla_put_failure:
+ nlmsg_cancel(skb, nlh);
+ return -EMSGSIZE;
+}
+
+int j1939rtnl_dump_addr(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int ndev, addr, ret, sa;
+ struct net_device *netdev;
+ struct j1939_segment *jseg;
+ struct j1939_ecu *ecu;
+ struct addr_ent *ent;
+
+ if (!net_eq(sock_net(skb->sk), &init_net))
+ return 0;
+
+ ndev = 0;
+ for_each_netdev(&init_net, netdev) {
+ ++ndev;
+ if (ndev < cb->args[1])
+ continue;
+ if (netdev->type != ARPHRD_CAN)
+ continue;
+
+ jseg = j1939_segment_find(netdev->ifindex);
+ if (!jseg)
+ continue;
+
+ read_lock_bh(&jseg->lock);
+ for (addr = cb->args[2]; addr < J1939_IDLE_ADDR; ++addr) {
+ ent = &jseg->ents[addr];
+ if (!ent->flags)
+ continue;
+ ret = j1939rtnl_fill_ifaddr(skb, netdev->ifindex, addr,
+ 0, ent->flags, NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq, RTM_NEWADDR,
+ NLM_F_MULTI);
+ if (ret < 0) {
+ read_unlock_bh(&jseg->lock);
+ goto done;
+ }
+ cb->args[2] = addr + 1;
+ }
+
+ if (addr > J1939_IDLE_ADDR)
+ addr = J1939_IDLE_ADDR;
+ list_for_each_entry(ecu, &jseg->ecus, list) {
+ if (addr++ < cb->args[2])
+ continue;
+ if (!(ecu->flags & ECUFLAG_LOCAL))
+ continue;
+ sa = ecu->sa;
+ if (ecu->parent->ents[sa].ecu != ecu)
+ sa = J1939_IDLE_ADDR;
+ ret = j1939rtnl_fill_ifaddr(skb, netdev->ifindex,
+ sa, ecu->name, ecu->flags,
+ NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq, RTM_NEWADDR,
+ NLM_F_MULTI);
+ if (ret < 0) {
+ read_unlock_bh(&jseg->lock);
+ goto done;
+ }
+ cb->args[2] = addr;
+ }
+ read_unlock_bh(&jseg->lock);
+ /* reset first address for device */
+ cb->args[2] = 0;
+ }
+ ++ndev;
+done:
+ cb->args[1] = ndev;
+
+ return skb->len;
+}
+
+/*
+ * rtnl_link_ops
+ */
+
+static const struct nla_policy j1939_ifla_policy[IFLA_J1939_MAX] = {
+ [IFLA_J1939_ENABLE] = { .type = NLA_U8, },
+};
+
+static size_t j1939_get_link_af_size(const struct net_device *dev)
+{
+ return nla_policy_len(j1939_ifla_policy, IFLA_J1939_MAX-1);
+}
+
+static int j1939_validate_link_af(const struct net_device *dev,
+ const struct nlattr *nla)
+{
+ return nla_validate_nested(nla, IFLA_J1939_MAX-1, j1939_ifla_policy);
+}
+
+static int j1939_fill_link_af(struct sk_buff *skb, const struct net_device *dev)
+{
+ struct j1939_segment *jseg;
+
+ if (!dev)
+ return -ENODEV;
+ jseg = j1939_segment_find(dev->ifindex);
+ if (jseg)
+ put_j1939_segment(jseg);
+ NLA_PUT_U8(skb, IFLA_J1939_ENABLE, jseg ? 1 : 0);
+ return 0;
+
+nla_put_failure:
+ return -EMSGSIZE;
+}
+
+static int j1939_set_link_af(struct net_device *dev, const struct nlattr *nla)
+{
+ int ret;
+ struct nlattr *tb[IFLA_J1939_MAX];
+
+ ret = nla_parse_nested(tb, IFLA_J1939_MAX-1, nla, j1939_ifla_policy);
+ if (ret < 0)
+ return ret;
+
+ if (tb[IFLA_J1939_ENABLE]) {
+ if (nla_get_u8(tb[IFLA_J1939_ENABLE]))
+ ret = j1939_segment_attach(dev);
+ else
+ ret = j1939_segment_detach(dev);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+const struct rtnl_af_ops j1939_rtnl_af_ops = {
+ .family = AF_CAN,
+ .fill_link_af = j1939_fill_link_af,
+ .get_link_af_size = j1939_get_link_af_size,
+ .validate_link_af = j1939_validate_link_af,
+ .set_link_af = j1939_set_link_af,
+};
+
diff --git a/net/can/j1939/socket.c b/net/can/j1939/socket.c
new file mode 100644
index 0000000..e41cb31
--- /dev/null
+++ b/net/can/j1939/socket.c
@@ -0,0 +1,969 @@
+/*
+ * Copyright (c) 2010-2011 EIA Electronics
+ *
+ * Authors:
+ * Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
+ * Pieter Beyens <pieter.beyens-/BeEPy95v10@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/socket.h>
+#include <linux/list.h>
+#include <linux/if_arp.h>
+#include <net/tcp_states.h>
+
+#include <linux/can/core.h>
+#include <linux/can/j1939.h>
+#include "j1939-priv.h"
+
+struct j1939_sock {
+ struct sock sk; /* must be first to skip with memset */
+ struct list_head list;
+
+ int state;
+ #define JSK_BOUND BIT(0)
+ #define JSK_CONNECTED BIT(1)
+ #define PROMISC BIT(2)
+ #define RECV_OWN BIT(3)
+
+ struct {
+ name_t src, dst;
+ pgn_t pgn;
+
+ uint8_t sa, da;
+ } addr;
+
+ struct j1939_filter *filters;
+ int nfilters;
+
+ int skb_pending;
+ spinlock_t lock;
+ wait_queue_head_t waitq;
+};
+
+static inline struct j1939_sock *j1939_sk(const struct sock *sk)
+{
+ return container_of(sk, struct j1939_sock, sk);
+}
+
+/* skb_pending issues */
+static inline int j1939_sock_pending_add_first(struct sock *sk)
+{
+ int saved;
+ struct j1939_sock *jsk = j1939_sk(sk);
+
+ spin_lock_bh(&jsk->lock);
+ if (!jsk->skb_pending) {
+ ++jsk->skb_pending;
+ saved = 1;
+ } else
+ saved = 0;
+ spin_unlock_bh(&jsk->lock);
+ return saved;
+}
+
+static inline void j1939_sock_pending_add(struct sock *sk)
+{
+ struct j1939_sock *jsk = j1939_sk(sk);
+
+ spin_lock_bh(&jsk->lock);
+ ++jsk->skb_pending;
+ spin_unlock_bh(&jsk->lock);
+}
+
+void j1939_sock_pending_del(struct sock *sk)
+{
+ struct j1939_sock *jsk = j1939_sk(sk);
+ int saved;
+
+ spin_lock_bh(&jsk->lock);
+ --jsk->skb_pending;
+ saved = jsk->skb_pending;
+ spin_unlock_bh(&jsk->lock);
+ if (!saved)
+ wake_up(&jsk->waitq);
+}
+
+
+static inline int j1939_no_address(const struct sock *sk)
+{
+ const struct j1939_sock *jsk = j1939_sk(sk);
+ return (jsk->addr.sa == J1939_NO_ADDR) && !jsk->addr.src;
+}
+
+/*
+ * list of sockets
+ */
+static struct {
+ struct mutex lock;
+ struct list_head socks;
+} s;
+
+/* matches skb control buffer (addr) with a j1939 filter */
+static inline int packet_match(const struct j1939_sk_buff_cb *cb,
+ const struct j1939_filter *f, int nfilter)
+{
+ if (!nfilter)
+ /* receive all when no filters are assigned */
+ return 1;
+ /*
+ * Filters relying on the addr for static addressing _should_ get
+ * packets from dynamic addressed ECU's too if they match their SA.
+ * Sockets using dynamic addressing in their filters should not set it.
+ */
+ for (; nfilter; ++f, --nfilter) {
+ if ((cb->pgn & f->pgn_mask) != (f->pgn & f->pgn_mask))
+ continue;
+ if ((cb->src.addr & f->addr_mask) != (f->addr & f->addr_mask))
+ continue;
+ if ((cb->src.name & f->name_mask) != (f->name & f->name_mask))
+ continue;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * callback per socket, called from filter infrastructure
+ */
+static void j1939sk_recv_skb(struct sk_buff *oskb, void *data)
+{
+ struct sk_buff *skb;
+ struct j1939_sock *jsk = (struct j1939_sock *)data;
+ struct j1939_sk_buff_cb *cb = (void *)oskb->cb;
+
+ if (jsk->sk.sk_bound_dev_if && (jsk->sk.sk_bound_dev_if != cb->ifindex))
+ /* this socket does not take packets from this iface */
+ return;
+ if (!(jsk->state & PROMISC)) {
+ if (cb->dst.flags & ECUFLAG_REMOTE)
+ /*
+ * this msg was destined for an ECU associated
+ * with this socket
+ */
+ return;
+ if (jsk->addr.src) {
+ if (cb->dst.name &&
+ (cb->dst.name != jsk->addr.src))
+ /*
+ * the msg is not destined for the name
+ * that the socket is bound to
+ */
+ return;
+ } else if (j1939_address_is_unicast(jsk->addr.sa)) {
+ if (j1939_address_is_unicast(cb->dst.addr) &&
+ (cb->dst.addr != jsk->addr.sa))
+ /*
+ * the msg is not destined for the name
+ * that the socket is bound to
+ */
+ return;
+ }
+ }
+
+ if ((oskb->sk == &jsk->sk) && !(jsk->state & RECV_OWN))
+ /* own message */
+ return;
+
+ if (!packet_match(cb, jsk->filters, jsk->nfilters))
+ return;
+
+ skb = skb_clone(oskb, GFP_ATOMIC);
+ if (!skb) {
+ j1939_warning("skb clone failed\n");
+ return;
+ }
+ cb = (void *)skb->cb;
+ cb->msg_flags &= ~(MSG_DONTROUTE | MSG_CONFIRM);
+ if (oskb->sk)
+ cb->msg_flags |= MSG_DONTROUTE;
+ if (oskb->sk == &jsk->sk)
+ cb->msg_flags |= MSG_CONFIRM;
+
+ skb->sk = &jsk->sk;
+ if (sock_queue_rcv_skb(&jsk->sk, skb) < 0)
+ kfree_skb(skb);
+}
+
+static int j1939sk_init(struct sock *sk)
+{
+ struct j1939_sock *jsk = j1939_sk(sk);
+
+ INIT_LIST_HEAD(&jsk->list);
+ spin_lock_init(&jsk->lock);
+ init_waitqueue_head(&jsk->waitq);
+ jsk->sk.sk_priority = j1939_to_sk_priority(6);
+ jsk->sk.sk_reuse = 1; /* per default */
+ jsk->addr.sa = J1939_NO_ADDR;
+ jsk->addr.da = J1939_NO_ADDR;
+ return 0;
+}
+
+/*
+ * helper: return <0 for error, >0 for error to notify
+ */
+static int j1939sk_bind_netdev_helper(struct socket *sock)
+{
+ struct j1939_sock *jsk = j1939_sk(sock->sk);
+ int ret;
+ struct net_device *netdev;
+ struct j1939_segment *jseg;
+
+ if (!jsk->sk.sk_bound_dev_if)
+ return 0;
+ ret = 0;
+
+ netdev = dev_get_by_index(&init_net, jsk->sk.sk_bound_dev_if);
+ if (!netdev) {
+ ret = -ENODEV;
+ goto fail_netdev;
+ }
+
+ /* no need to test for CAN device,
+ * implicitely done by j1939_segment
+ */
+ jseg = j1939_segment_find(netdev->ifindex);
+ if (!jseg) {
+ ret = -EHOSTDOWN;
+ goto fail_segment;
+ }
+
+ if (!(netdev->flags & IFF_UP)) {
+ sock->sk->sk_err = ENETDOWN;
+ sock->sk->sk_error_report(sock->sk);
+ }
+ put_j1939_segment(jseg);
+fail_segment:
+ dev_put(netdev);
+fail_netdev:
+ return ret;
+}
+
+static int j1939sk_bind(struct socket *sock, struct sockaddr *uaddr, int len)
+{
+ struct sockaddr_can *addr = (struct sockaddr_can *)uaddr;
+ struct j1939_sock *jsk = j1939_sk(sock->sk);
+ struct j1939_ecu *ecu = NULL;
+ int ret, old_state;
+
+ if (len < required_size(can_addr.j1939, *addr))
+ return -EINVAL;
+ if (addr->can_family != AF_CAN)
+ return -EINVAL;
+
+ /* lock s.lock first, to avoid circular lock dependancy */
+ mutex_lock(&s.lock);
+ lock_sock(sock->sk);
+ if (jsk->state & JSK_BOUND) {
+ ret = -EBUSY;
+ if (addr->can_ifindex &&
+ (addr->can_ifindex != jsk->sk.sk_bound_dev_if))
+ goto fail_locked;
+ /*
+ * do not allow to change addres after first bind(),
+ * (it would require updating the j1939_ecu list)
+ * but allow the change SA when using dynaddr,
+ * and allow to change PGN
+ */
+ if (!jsk->addr.src ||
+ (jsk->addr.src != addr->can_addr.j1939.name) ||
+ (jsk->addr.pgn != addr->can_addr.j1939.pgn))
+ goto fail_locked;
+ /* set to be able to send address claims */
+ jsk->addr.sa = addr->can_addr.j1939.addr;
+ /* since this socket is bound already, we can skip a lot */
+ release_sock(sock->sk);
+ mutex_unlock(&s.lock);
+ return 0;
+ }
+
+ /* do netdev */
+ if (jsk->sk.sk_bound_dev_if && addr->can_ifindex &&
+ (jsk->sk.sk_bound_dev_if != addr->can_ifindex)) {
+ ret = -EBADR;
+ goto fail_locked;
+ }
+ if (!jsk->sk.sk_bound_dev_if)
+ jsk->sk.sk_bound_dev_if = addr->can_ifindex;
+
+ ret = j1939sk_bind_netdev_helper(sock);
+ if (ret < 0)
+ goto fail_locked;
+
+ /* bind name/addr */
+ if (addr->can_addr.j1939.name) {
+ ecu = j1939_ecu_find_by_name(addr->can_addr.j1939.name,
+ jsk->sk.sk_bound_dev_if);
+ if (!ecu) {
+ ret = -EADDRNOTAVAIL;
+ goto fail_locked;
+ } else if (ecu->flags & ECUFLAG_REMOTE) {
+ ret = -EREMOTE;
+ goto fail_with_ecu;
+ } else if (jsk->sk.sk_bound_dev_if != ecu->parent->ifindex) {
+ ret = -EHOSTUNREACH;
+ goto fail_with_ecu;
+ }
+ jsk->addr.src = ecu->name;
+ jsk->addr.sa = addr->can_addr.j1939.addr;
+ } else if (j1939_address_is_unicast(addr->can_addr.j1939.addr)) {
+ struct j1939_segment *jseg;
+ struct addr_ent *paddr;
+ int flags;
+
+ /* static addressing, netdev is required */
+ if (!jsk->sk.sk_bound_dev_if) {
+ ret = -EINVAL;
+ goto fail_locked;
+ }
+ jseg = j1939_segment_find(jsk->sk.sk_bound_dev_if);
+ if (!jseg) {
+ ret = -ENETUNREACH;
+ goto fail_locked;
+ }
+ paddr = &jseg->ents[addr->can_addr.j1939.addr];
+ ret = 0;
+ read_lock_bh(&jseg->lock);
+ flags = paddr->flags;
+ read_unlock_bh(&jseg->lock);
+ put_j1939_segment(jseg);
+ if (!(flags & ECUFLAG_LOCAL)) {
+ ret = -EADDRNOTAVAIL;
+ goto fail_locked;
+ }
+ jsk->addr.sa = addr->can_addr.j1939.addr;
+ } else if (addr->can_addr.j1939.addr == J1939_IDLE_ADDR) {
+ /* static addressing, netdev is required */
+ if (!jsk->sk.sk_bound_dev_if) {
+ ret = -EINVAL;
+ goto fail_locked;
+ }
+ jsk->addr.sa = addr->can_addr.j1939.addr;
+ } else {
+ /* no name, no addr */
+ }
+
+ /* set default transmit pgn/priority */
+ jsk->addr.pgn = addr->can_addr.j1939.pgn;
+
+ old_state = jsk->state;
+ jsk->state |= JSK_BOUND;
+
+ if (!(old_state & (JSK_BOUND | JSK_CONNECTED))) {
+ list_add_tail(&jsk->list, &s.socks);
+ j1939_recv_add(jsk, j1939sk_recv_skb);
+ }
+
+ ret = 0;
+
+fail_with_ecu:
+ if (ecu && !IS_ERR(ecu))
+ put_j1939_ecu(ecu);
+fail_locked:
+ release_sock(sock->sk);
+ mutex_unlock(&s.lock);
+ return ret;
+}
+
+static int j1939sk_connect(struct socket *sock, struct sockaddr *uaddr,
+ int len, int flags)
+{
+ int ret, old_state;
+ struct sockaddr_can *addr = (struct sockaddr_can *)uaddr;
+ struct j1939_sock *jsk = j1939_sk(sock->sk);
+ struct j1939_ecu *ecu;
+ int ifindex;
+
+ if (!uaddr)
+ return -EDESTADDRREQ;
+
+ if (len < required_size(can_addr.j1939, *addr))
+ return -EINVAL;
+ if (addr->can_family != AF_CAN)
+ return -EINVAL;
+
+ mutex_lock(&s.lock);
+ lock_sock(sock->sk);
+ if (jsk->state & JSK_CONNECTED) {
+ ret = -EISCONN;
+ goto fail_locked;
+ }
+
+ ifindex = jsk->sk.sk_bound_dev_if;
+ if (ifindex && addr->can_ifindex && (ifindex != addr->can_ifindex)) {
+ ret = -ECONNREFUSED;
+ goto fail_locked;
+ }
+ if (!ifindex)
+ ifindex = addr->can_ifindex;
+
+ /* lookup destination */
+ if (addr->can_addr.j1939.name) {
+ ecu = j1939_ecu_find_by_name(addr->can_addr.j1939.name,
+ ifindex);
+ if (!ecu) {
+ ret = -EADDRNOTAVAIL;
+ goto fail_locked;
+ }
+ if (ifindex && (ifindex != ecu->parent->ifindex)) {
+ ret = -EHOSTUNREACH;
+ goto fail_locked;
+ }
+ ifindex = ecu->parent->ifindex;
+ jsk->addr.dst = ecu->name;
+ jsk->addr.da = ecu->sa;
+ put_j1939_ecu(ecu);
+ } else {
+ /* broadcast */
+ jsk->addr.dst = 0;
+ jsk->addr.da = addr->can_addr.j1939.addr;
+ }
+ /*
+ * take a default source when not present, so connected sockets
+ * will stick to the same source ECU
+ */
+ if (!jsk->addr.src && !j1939_address_is_valid(jsk->addr.sa)) {
+ ecu = j1939_ecu_find_segment_default_tx(ifindex,
+ &jsk->addr.src, &jsk->addr.sa);
+ if (IS_ERR(ecu)) {
+ ret = PTR_ERR(ecu);
+ goto fail_locked;
+ }
+ put_j1939_ecu(ecu);
+ }
+
+ /* start assigning, no problem can occur at this point anymore */
+ jsk->sk.sk_bound_dev_if = ifindex;
+
+ if (!(jsk->state & JSK_BOUND) || !pgn_is_valid(jsk->addr.pgn)) {
+ /*
+ * bind() takes precedence over connect() for the
+ * pgn to use ourselve
+ */
+ jsk->addr.pgn = addr->can_addr.j1939.pgn;
+ }
+
+ old_state = jsk->state;
+ jsk->state |= JSK_CONNECTED;
+
+ if (!(old_state & (JSK_BOUND | JSK_CONNECTED))) {
+ list_add_tail(&jsk->list, &s.socks);
+ j1939_recv_add(jsk, j1939sk_recv_skb);
+ }
+ release_sock(sock->sk);
+ mutex_unlock(&s.lock);
+ return 0;
+
+fail_locked:
+ release_sock(sock->sk);
+ mutex_unlock(&s.lock);
+ return ret;
+}
+
+static void j1939sk_sock2sockaddr_can(struct sockaddr_can *addr,
+ const struct j1939_sock *jsk, int peer)
+{
+ addr->can_family = AF_CAN;
+ addr->can_ifindex = jsk->sk.sk_bound_dev_if;
+ addr->can_addr.j1939.name = peer ? jsk->addr.dst : jsk->addr.src;
+ addr->can_addr.j1939.pgn = jsk->addr.pgn;
+ addr->can_addr.j1939.addr = peer ? jsk->addr.da : jsk->addr.sa;
+}
+
+static int j1939sk_getname(struct socket *sock, struct sockaddr *uaddr,
+ int *len, int peer)
+{
+ struct sockaddr_can *addr = (struct sockaddr_can *)uaddr;
+ struct sock *sk = sock->sk;
+ struct j1939_sock *jsk = j1939_sk(sk);
+ int ret = 0;
+
+ lock_sock(sk);
+
+ if (peer && !(jsk->state & JSK_CONNECTED)) {
+ ret = -EADDRNOTAVAIL;
+ goto failure;
+ }
+
+ j1939sk_sock2sockaddr_can(addr, jsk, peer);
+ *len = sizeof(*addr);
+
+failure:
+ release_sock(sk);
+
+ return ret;
+}
+
+static int j1939sk_release(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+ struct j1939_sock *jsk;
+
+ if (!sk)
+ return 0;
+ jsk = j1939_sk(sk);
+ j1939_recv_remove(jsk, j1939sk_recv_skb);
+ mutex_lock(&s.lock);
+ list_del_init(&jsk->list);
+ mutex_unlock(&s.lock);
+
+ lock_sock(sk);
+ if (jsk->state & PROMISC)
+ j1939_put_promisc_receiver(jsk->sk.sk_bound_dev_if);
+
+ sock_orphan(sk);
+ sock->sk = NULL;
+
+ release_sock(sk);
+ sock_put(sk);
+
+ return 0;
+}
+
+static int j1939sk_setsockopt_flag(struct j1939_sock *jsk,
+ char __user *optval, unsigned int optlen, int flag)
+{
+ int tmp;
+
+ if (optlen != sizeof(tmp))
+ return -EINVAL;
+ if (copy_from_user(&tmp, optval, optlen))
+ return -EFAULT;
+ lock_sock(&jsk->sk);
+ if (tmp)
+ jsk->state |= flag;
+ else
+ jsk->state &= ~flag;
+ release_sock(&jsk->sk);
+ return tmp;
+}
+
+static int j1939sk_setsockopt(struct socket *sock, int level, int optname,
+ char __user *optval, unsigned int optlen)
+{
+ struct sock *sk = sock->sk;
+ struct j1939_sock *jsk = j1939_sk(sk);
+ int ret = 0, tmp, count;
+ struct j1939_filter *filters, *ofilters;
+
+ if (level != SOL_CAN_J1939)
+ return -EINVAL;
+
+ switch (optname) {
+ case SO_J1939_FILTER:
+ if (optval) {
+ if (optlen % sizeof(*filters) != 0)
+ return -EINVAL;
+ count = optlen / sizeof(*filters);
+ filters = kmalloc(optlen, GFP_KERNEL);
+ if (!filters)
+ return -ENOMEM;
+ if (copy_from_user(filters, optval, optlen)) {
+ kfree(filters);
+ return -EFAULT;
+ }
+ } else {
+ filters = NULL;
+ count = 0;
+ }
+
+ j1939_recv_suspend();
+ ofilters = jsk->filters;
+ jsk->filters = filters;
+ jsk->nfilters = count;
+ j1939_recv_resume();
+ if (ofilters)
+ kfree(ofilters);
+ break;
+ case SO_J1939_PROMISC:
+ tmp = jsk->state & PROMISC;
+ ret = j1939sk_setsockopt_flag(jsk, optval, optlen, PROMISC);
+ if (ret && !tmp)
+ j1939_get_promisc_receiver(jsk->sk.sk_bound_dev_if);
+ else if (!ret && tmp)
+ j1939_put_promisc_receiver(jsk->sk.sk_bound_dev_if);
+ ret = 0;
+ break;
+ case SO_J1939_RECV_OWN:
+ j1939sk_setsockopt_flag(jsk, optval, optlen, RECV_OWN);
+ break;
+ case SO_J1939_SEND_PRIO:
+ if (optlen != sizeof(tmp))
+ return -EINVAL;
+ if (copy_from_user(&tmp, optval, optlen))
+ return -EFAULT;
+ if ((tmp < 0) || (tmp > 7))
+ return -EDOM;
+ if ((tmp < 2) && !capable(CAP_NET_ADMIN))
+ return -EPERM;
+ lock_sock(&jsk->sk);
+ jsk->sk.sk_priority = j1939_to_sk_priority(tmp);
+ release_sock(&jsk->sk);
+ break;
+ default:
+ return -ENOPROTOOPT;
+ }
+
+ return ret;
+}
+
+static int j1939sk_getsockopt(struct socket *sock, int level, int optname,
+ char __user *optval, int __user *optlen)
+{
+ struct sock *sk = sock->sk;
+ struct j1939_sock *jsk = j1939_sk(sk);
+ int ret, ulen;
+ /* set defaults for using 'int' properties */
+ int tmp = 0;
+ int len = sizeof(tmp);
+ void *val = &tmp;
+
+ if (level != SOL_CAN_J1939)
+ return -EINVAL;
+ if (get_user(ulen, optlen))
+ return -EFAULT;
+ if (ulen < 0)
+ return -EINVAL;
+
+ lock_sock(&jsk->sk);
+ switch (optname) {
+ case SO_J1939_PROMISC:
+ tmp = (jsk->state & PROMISC) ? 1 : 0;
+ break;
+ case SO_J1939_RECV_OWN:
+ tmp = (jsk->state & RECV_OWN) ? 1 : 0;
+ break;
+ case SO_J1939_SEND_PRIO:
+ tmp = j1939_prio(jsk->sk.sk_priority);
+ break;
+ default:
+ ret = -ENOPROTOOPT;
+ goto no_copy;
+ }
+
+ /*
+ * copy to user, based on 'len' & 'val'
+ * but most sockopt's are 'int' properties, and have 'len' & 'val'
+ * left unchanged, but instead modified 'tmp'
+ */
+ if (len > ulen)
+ ret = -EFAULT;
+ else if (put_user(len, optlen))
+ ret = -EFAULT;
+ else if (copy_to_user(optval, val, len))
+ ret = -EFAULT;
+ else
+ ret = 0;
+no_copy:
+ release_sock(&jsk->sk);
+ return ret;
+}
+
+static int j1939sk_recvmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *msg, size_t size, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct sk_buff *skb;
+ struct j1939_sk_buff_cb *sk_addr;
+ int ret = 0;
+
+ skb = skb_recv_datagram(sk, flags, 0, &ret);
+ if (!skb)
+ return ret;
+
+ if (size < skb->len)
+ msg->msg_flags |= MSG_TRUNC;
+ else
+ size = skb->len;
+
+ ret = memcpy_toiovec(msg->msg_iov, skb->data, size);
+ if (ret < 0)
+ goto failed_with_skb;
+
+ sock_recv_timestamp(msg, sk, skb);
+ sk_addr = (void *)skb->cb;
+
+ if (j1939_address_is_valid(sk_addr->dst.addr))
+ put_cmsg(msg, SOL_CAN_J1939, SCM_J1939_DEST_ADDR,
+ sizeof(sk_addr->dst.addr), &sk_addr->dst.addr);
+
+ if (sk_addr->dst.name)
+ put_cmsg(msg, SOL_CAN_J1939, SCM_J1939_DEST_NAME,
+ sizeof(sk_addr->dst.name), &sk_addr->dst.name);
+
+ put_cmsg(msg, SOL_CAN_J1939, SCM_J1939_PRIO,
+ sizeof(sk_addr->priority), &sk_addr->priority);
+
+ if (msg->msg_name) {
+ struct sockaddr_can *paddr = msg->msg_name;
+
+ msg->msg_namelen = required_size(can_addr.j1939, *paddr);
+ memset(msg->msg_name, 0, msg->msg_namelen);
+ paddr->can_family = AF_CAN;
+ paddr->can_ifindex = sk_addr->ifindex;
+ paddr->can_addr.j1939.name = sk_addr->src.name;
+ paddr->can_addr.j1939.addr = sk_addr->src.addr;
+ paddr->can_addr.j1939.pgn = sk_addr->pgn;
+ }
+
+ skb_free_datagram(sk, skb);
+
+ return size;
+
+failed_with_skb:
+ skb_kill_datagram(sk, skb, flags);
+ return ret;
+}
+
+static int j1939sk_sendmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *msg, size_t size)
+{
+ struct sock *sk = sock->sk;
+ struct j1939_sock *jsk = j1939_sk(sk);
+ struct j1939_sk_buff_cb *skb_cb;
+ struct sk_buff *skb;
+ struct net_device *dev;
+ struct j1939_ecu *ecu;
+ int ifindex;
+ int ret;
+
+ if (!(jsk->state | JSK_BOUND))
+ return -ENOTCONN;
+
+ if (msg->msg_name && (msg->msg_namelen <
+ required_size(can_addr.j1939, struct sockaddr_can)))
+ return -EINVAL;
+
+ ifindex = jsk->sk.sk_bound_dev_if;
+ if (msg->msg_name) {
+ struct sockaddr_can *addr = msg->msg_name;
+ if (msg->msg_namelen < required_size(can_addr.j1939, *addr))
+ return -EFAULT;
+ if (addr->can_family != AF_CAN)
+ return -EINVAL;
+ if (ifindex && addr->can_ifindex &&
+ (ifindex != addr->can_ifindex))
+ return -ENONET;
+ if (!ifindex)
+ /* take destination intf when intf not yet set */
+ ifindex = addr->can_ifindex;
+ }
+
+ if (!ifindex)
+ return -EDESTADDRREQ;
+ if (j1939_no_address(&jsk->sk)) {
+ lock_sock(&jsk->sk);
+ ecu = j1939_ecu_find_segment_default_tx(
+ jsk->sk.sk_bound_dev_if,
+ &jsk->addr.src, &jsk->addr.sa);
+ release_sock(&jsk->sk);
+ if (IS_ERR(ecu))
+ return PTR_ERR(ecu);
+ }
+
+ dev = dev_get_by_index(&init_net, ifindex);
+ if (!dev)
+ return -ENXIO;
+
+ skb = sock_alloc_send_skb(sk, size,
+ msg->msg_flags & MSG_DONTWAIT, &ret);
+ if (!skb)
+ goto put_dev;
+
+ ret = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
+ if (ret < 0)
+ goto free_skb;
+ skb->dev = dev;
+ skb->sk = sk;
+
+ BUILD_BUG_ON(sizeof(skb->cb) < sizeof(*skb_cb));
+
+ skb_cb = (void *) skb->cb;
+ memset(skb_cb, 0, sizeof(*skb_cb));
+ skb_cb->msg_flags = msg->msg_flags;
+ skb_cb->ifindex = ifindex;
+ skb_cb->src.name = jsk->addr.src;
+ skb_cb->dst.name = jsk->addr.dst;
+ skb_cb->pgn = jsk->addr.pgn;
+ skb_cb->priority = j1939_prio(jsk->sk.sk_priority);
+ skb_cb->src.addr = jsk->addr.sa;
+ skb_cb->dst.addr = jsk->addr.da;
+
+ if (msg->msg_name) {
+ struct sockaddr_can *addr = msg->msg_name;
+ if (addr->can_addr.j1939.name) {
+ ecu = j1939_ecu_find_by_name(addr->can_addr.j1939.name,
+ ifindex);
+ if (!ecu)
+ return -EADDRNOTAVAIL;
+ skb_cb->dst.name = ecu->name;
+ skb_cb->dst.addr = ecu->sa;
+ put_j1939_ecu(ecu);
+ } else {
+ skb_cb->dst.name = 0;
+ skb_cb->dst.addr = addr->can_addr.j1939.addr;
+ }
+ if (pgn_is_valid(addr->can_addr.j1939.pgn))
+ skb_cb->pgn = addr->can_addr.j1939.pgn;
+ }
+
+ if (skb_cb->msg_flags & J1939_MSG_SYNC) {
+ if (skb_cb->msg_flags & MSG_DONTWAIT) {
+ ret = j1939_sock_pending_add_first(&jsk->sk);
+ if (ret > 0)
+ ret = -EAGAIN;
+ } else {
+ ret = wait_event_interruptible(jsk->waitq,
+ j1939_sock_pending_add_first(&jsk->sk));
+ }
+ if (ret < 0)
+ goto free_skb;
+ } else {
+ j1939_sock_pending_add(&jsk->sk);
+ }
+
+ ret = j1939_send(skb, j1939_level_sky);
+ if (ret < 0)
+ goto decrement_pending;
+
+ dev_put(dev);
+ return size;
+
+decrement_pending:
+ j1939_sock_pending_del(&jsk->sk);
+free_skb:
+ kfree_skb(skb);
+put_dev:
+ dev_put(dev);
+ return ret;
+}
+
+/* PROC */
+static int j1939sk_proc_show(struct seq_file *sqf, void *v)
+{
+ struct j1939_sock *jsk;
+ struct net_device *netdev;
+
+ seq_printf(sqf, "iface\tflags\tlocal\tremote\tpgn\tprio\tpending\n");
+ mutex_lock(&s.lock);
+ list_for_each_entry(jsk, &s.socks, list) {
+ lock_sock(&jsk->sk);
+ netdev = NULL;
+ if (jsk->sk.sk_bound_dev_if)
+ netdev = dev_get_by_index(&init_net,
+ jsk->sk.sk_bound_dev_if);
+ seq_printf(sqf, "%s\t", netdev ? netdev->name : "-");
+ if (netdev)
+ dev_put(netdev);
+ seq_printf(sqf, "%c%c%c%c\t",
+ (jsk->state & JSK_BOUND) ? 'b' : '-',
+ (jsk->state & JSK_CONNECTED) ? 'c' : '-',
+ (jsk->state & PROMISC) ? 'P' : '-',
+ (jsk->state & RECV_OWN) ? 'o' : '-');
+ if (jsk->addr.src)
+ seq_printf(sqf, "%016llx", (long long)jsk->addr.src);
+ else if (j1939_address_is_unicast(jsk->addr.sa))
+ seq_printf(sqf, "%02x", jsk->addr.sa);
+ else
+ seq_printf(sqf, "-");
+ seq_printf(sqf, "\t");
+ if (jsk->addr.dst)
+ seq_printf(sqf, "%016llx", (long long)jsk->addr.dst);
+ else if (j1939_address_is_unicast(jsk->addr.da))
+ seq_printf(sqf, "%02x", jsk->addr.da);
+ else
+ seq_printf(sqf, "-");
+ seq_printf(sqf, "\t%05x", jsk->addr.pgn);
+ seq_printf(sqf, "\t%u", j1939_prio(jsk->sk.sk_priority));
+ seq_printf(sqf, "\t%u", jsk->skb_pending);
+ release_sock(&jsk->sk);
+ seq_printf(sqf, "\n");
+ }
+ mutex_unlock(&s.lock);
+ return 0;
+}
+
+void j1939sk_netdev_event(int ifindex, int error_code)
+{
+ struct j1939_sock *jsk;
+
+ mutex_lock(&s.lock);
+ list_for_each_entry(jsk, &s.socks, list) {
+ if (jsk->sk.sk_bound_dev_if != ifindex)
+ continue;
+ jsk->sk.sk_err = error_code;
+ if (!sock_flag(&jsk->sk, SOCK_DEAD))
+ jsk->sk.sk_error_report(&jsk->sk);
+ /* do not remove filters here */
+ }
+ mutex_unlock(&s.lock);
+}
+
+static const struct proto_ops j1939_ops = {
+ .family = PF_CAN,
+ .release = j1939sk_release,
+ .bind = j1939sk_bind,
+ .connect = j1939sk_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .getname = j1939sk_getname,
+ .poll = datagram_poll,
+ .ioctl = can_ioctl,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = j1939sk_setsockopt,
+ .getsockopt = j1939sk_getsockopt,
+ .sendmsg = j1939sk_sendmsg,
+ .recvmsg = j1939sk_recvmsg,
+ .mmap = sock_no_mmap,
+ .sendpage = sock_no_sendpage,
+};
+
+static struct proto j1939_proto __read_mostly = {
+ .name = "CAN_J1939",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct j1939_sock),
+ .init = j1939sk_init,
+};
+
+static const struct can_proto j1939_can_proto = {
+ .type = SOCK_DGRAM,
+ .protocol = CAN_J1939,
+ .ops = &j1939_ops,
+ .prot = &j1939_proto,
+
+ .rtnl_link_ops = &j1939_rtnl_af_ops,
+ .rtnl_new_addr = j1939rtnl_new_addr,
+ .rtnl_del_addr = j1939rtnl_del_addr,
+ .rtnl_dump_addr = j1939rtnl_dump_addr,
+};
+
+__init int j1939sk_module_init(void)
+{
+ int ret;
+
+ INIT_LIST_HEAD(&s.socks);
+ mutex_init(&s.lock);
+
+ ret = can_proto_register(&j1939_can_proto);
+ if (ret < 0)
+ pr_err("can: registration of j1939 protocol failed\n");
+ else
+ j1939_proc_add("sock", j1939sk_proc_show, NULL);
+ return ret;
+}
+
+void j1939sk_module_exit(void)
+{
+ j1939_proc_remove("sock");
+ can_proto_unregister(&j1939_can_proto);
+}
+
+MODULE_ALIAS("can-proto-" __stringify(CAN_J1939));
+
diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c
new file mode 100644
index 0000000..9f723c6
--- /dev/null
+++ b/net/can/j1939/transport.c
@@ -0,0 +1,1449 @@
+/*
+ * Copyright (c) 2010-2011 EIA Electronics
+ *
+ * Authors:
+ * Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ */
+
+#include <linux/skbuff.h>
+#include <linux/hrtimer.h>
+#include <linux/version.h>
+#include <linux/if_arp.h>
+#include <linux/wait.h>
+#include "j1939-priv.h"
+
+#define REGULAR 0
+#define EXTENDED 1
+
+#define etp_pgn_ctl 0xc800
+#define etp_pgn_dat 0xc700
+#define tp_pgn_ctl 0xec00
+#define tp_pgn_dat 0xeb00
+
+#define tp_cmd_bam 0x20
+#define tp_cmd_rts 0x10
+#define tp_cmd_cts 0x11
+#define tp_cmd_eof 0x13
+#define tp_cmd_abort 0xff
+
+#define etp_cmd_rts 0x14
+#define etp_cmd_cts 0x15
+#define etp_cmd_dpo 0x16
+#define etp_cmd_eof 0x17
+#define etp_cmd_abort 0xff
+
+#define ABORT_BUSY 1
+#define ABORT_RESOURCE 2
+#define ABORT_TIMEOUT 3
+#define ABORT_GENERIC 4
+#define ABORT_FAULT 5
+
+#define MAX_TP_PACKET_SIZE (7*255)
+#define MAX_ETP_PACKET_SIZE (7*0xffffff)
+
+static int block = 255;
+static int max_packet_size = 1024*100;
+static int retry_ms = 20;
+
+struct session {
+ struct list_head list;
+ atomic_t refs;
+ spinlock_t lock;
+
+ struct j1939_sk_buff_cb *cb; /*
+ * ifindex, src, dst, pgn define the session block
+ * the are _never_ modified after insertion in the list
+ * this decreases locking problems a _lot_
+ */
+ struct sk_buff *skb;
+
+ /*
+ * all tx related stuff (last_txcmd, pkt.tx)
+ * is protected (modified only) with the txtask tasklet
+ * 'total' & 'block' are never changed,
+ * last_cmd, last & block are protected by ->lock
+ * this means that the tx may run after cts is received that should
+ * have stopped tx, but this time discrepancy is never avoided anyhow
+ */
+ uint8_t last_cmd, last_txcmd;
+ uint8_t transmission;
+ uint8_t extd;
+ struct {
+ /*
+ * these do not require 16 bit, they should fit in uint8_t
+ * but putting in int makes it easier to deal with
+ */
+ unsigned int total, done, last, tx;
+ unsigned int block; /* for TP */
+ unsigned int dpo; /* for ETP */
+ } pkt;
+ struct hrtimer txtimer, rxtimer;
+ /* tasklets for execution of tx/rx timer hander in softirq */
+ struct tasklet_struct txtask, rxtask;
+};
+
+static struct j1939tp {
+ spinlock_t lock;
+ struct list_head sessionq;
+ struct list_head extsessionq;
+ struct {
+ struct list_head sessionq;
+ spinlock_t lock;
+ struct work_struct work;
+ } del;
+ wait_queue_head_t wait;
+ struct notifier_block notifier;
+} s;
+
+static struct session *j1939session_new(struct sk_buff *skb);
+static struct session *j1939session_fresh_new(int size,
+ struct j1939_sk_buff_cb *rel_cb, pgn_t pgn);
+
+static inline void fix_cb(struct j1939_sk_buff_cb *cb)
+{
+ cb->msg_flags &= ~J1939_MSG_RESERVED;
+}
+
+static inline struct list_head *sessionq(int extd)
+{
+ return extd ? &s.extsessionq : &s.sessionq;
+}
+
+static inline void j1939session_destroy(struct session *session)
+{
+ if (session->skb)
+ kfree_skb(session->skb);
+ hrtimer_cancel(&session->rxtimer);
+ hrtimer_cancel(&session->txtimer);
+ tasklet_disable(&session->rxtask);
+ tasklet_disable(&session->txtask);
+ kfree(session);
+}
+
+/* clean up work queue */
+static void j1939tp_del_work(struct work_struct *work)
+{
+ struct session *session;
+ int cnt = 0;
+
+ do {
+ session = NULL;
+ spin_lock_bh(&s.del.lock);
+ if (list_empty(&s.del.sessionq)) {
+ spin_unlock_bh(&s.del.lock);
+ break;
+ }
+ session = list_first_entry(&s.del.sessionq,
+ struct session, list);
+ list_del_init(&session->list);
+ spin_unlock_bh(&s.del.lock);
+ j1939session_destroy(session);
+ ++cnt;
+ } while (1);
+}
+/* reference counter */
+static inline void get_session(struct session *session)
+{
+ atomic_inc(&session->refs);
+}
+
+static void put_session(struct session *session)
+{
+ BUG_ON(!session);
+ if (atomic_add_return(-1, &session->refs) >= 0)
+ /* not the last one */
+ return;
+ /* it should have been removed from any list long time ago */
+ BUG_ON(!list_empty(&session->list));
+
+ hrtimer_try_to_cancel(&session->rxtimer);
+ hrtimer_try_to_cancel(&session->txtimer);
+ tasklet_disable_nosync(&session->rxtask);
+ tasklet_disable_nosync(&session->txtask);
+
+ if (in_interrupt()) {
+ spin_lock_bh(&s.del.lock);
+ list_add_tail(&session->list, &s.del.sessionq);
+ spin_unlock_bh(&s.del.lock);
+ schedule_work(&s.del.work);
+ } else {
+ /* destroy session right here */
+ j1939session_destroy(session);
+ }
+}
+
+/* transport status locking */
+static inline void session_lock(struct session *session)
+{
+ get_session(session); /* safety measure */
+ spin_lock_bh(&session->lock);
+}
+
+static inline void session_unlock(struct session *session)
+{
+ spin_unlock_bh(&session->lock);
+ put_session(session);
+}
+
+static inline void sessionlist_lock(void)
+{
+ spin_lock_bh(&s.lock);
+}
+
+static inline void sessionlist_unlock(void)
+{
+ spin_unlock_bh(&s.lock);
+}
+
+/*
+ * see if we are receiver
+ * returns 0 for broadcasts, although we will receive them
+ */
+static inline int j1939tp_im_receiver(const struct j1939_sk_buff_cb *cb)
+{
+ return (cb->dst.flags & ECUFLAG_LOCAL) ? 1 : 0;
+}
+
+/* see if we are sender */
+static inline int j1939tp_im_transmitter(const struct j1939_sk_buff_cb *cb)
+{
+ return (cb->src.flags & ECUFLAG_LOCAL) ? 1 : 0;
+}
+
+/* see if we are involved as either receiver or transmitter */
+/* reverse = -1 means : any direction */
+static int j1939tp_im_involved(const struct j1939_sk_buff_cb *cb, int reverse)
+{
+ if (reverse < 0) {
+ return ((cb->src.flags | cb->dst.flags) & ECUFLAG_LOCAL)
+ ? 1 : 0;
+ } else if (reverse) {
+ return j1939tp_im_receiver(cb);
+ } else {
+ return j1939tp_im_transmitter(cb);
+ }
+}
+
+/* extract pgn from flow-ctl message */
+static inline pgn_t j1939xtp_ctl_to_pgn(const uint8_t *dat)
+{
+ pgn_t pgn;
+
+ pgn = (dat[7] << 16) | (dat[6] << 8) | (dat[5] << 0);
+ if (pgn_is_pdu1(pgn))
+ pgn &= 0xffff00;
+ return pgn;
+}
+
+static inline unsigned int j1939tp_ctl_to_size(const uint8_t *dat)
+{
+ return (dat[2] << 8) + (dat[1] << 0);
+}
+static inline unsigned int j1939etp_ctl_to_packet(const uint8_t *dat)
+{
+ return (dat[4] << 16) | (dat[3] << 8) | (dat[2] << 0);
+}
+static inline unsigned int j1939etp_ctl_to_size(const uint8_t *dat)
+{
+ return (dat[4] << 24) | (dat[3] << 16) |
+ (dat[2] << 8) | (dat[1] << 0);
+}
+
+/*
+ * find existing session:
+ * reverse: swap cb's src & dst
+ * there is no problem with matching broadcasts, since
+ * broadcasts (no dst, no da) would never call this
+ * with reverse==1
+ */
+static int j1939tp_match(const struct j1939_sk_buff_cb *a,
+ const struct j1939_sk_buff_cb *b, int reverse)
+{
+ if (a->ifindex != b->ifindex)
+ return 0;
+ if (!reverse) {
+ if (a->src.name) {
+ if (a->src.name != b->src.name)
+ return 0;
+ } else if (a->src.addr != b->src.addr)
+ return 0;
+ if (a->dst.name) {
+ if (a->dst.name != b->dst.name)
+ return 0;
+ } else if (a->dst.addr != b->dst.addr)
+ return 0;
+ } else {
+ if (a->src.name) {
+ if (a->src.name != b->dst.name)
+ return 0;
+ } else if (a->src.addr != b->dst.addr)
+ return 0;
+ if (a->dst.name) {
+ if (a->dst.name != b->src.name)
+ return 0;
+ } else if (a->dst.addr != b->src.addr)
+ return 0;
+ }
+ return 1;
+}
+
+static struct session *_j1939tp_find(struct list_head *root,
+ const struct j1939_sk_buff_cb *cb, int reverse)
+{
+ struct session *session;
+
+ list_for_each_entry(session, root, list) {
+ get_session(session);
+ if (j1939tp_match(session->cb, cb, reverse))
+ return session;
+ put_session(session);
+ }
+ return NULL;
+}
+
+static struct session *j1939tp_find(struct list_head *root,
+ const struct j1939_sk_buff_cb *cb, int reverse)
+{
+ struct session *session;
+ sessionlist_lock();
+ session = _j1939tp_find(root, cb, reverse);
+ sessionlist_unlock();
+ return session;
+}
+
+static void j1939_skbcb_swap(struct j1939_sk_buff_cb *cb)
+{
+ name_t name;
+ uint8_t addr;
+ int flags;
+
+ name = cb->dst.name;
+ cb->dst.name = cb->src.name;
+ cb->src.name = name;
+
+ addr = cb->dst.addr;
+ cb->dst.addr = cb->src.addr;
+ cb->src.addr = addr;
+
+ flags = cb->dst.flags;
+ cb->dst.flags = cb->src.flags;
+ cb->src.flags = flags;
+}
+/* TP transmit packet functions */
+static int j1939tp_tx_dat(struct session *related,
+ const uint8_t *dat, int len)
+{
+ int ret;
+ struct sk_buff *skb;
+ struct j1939_sk_buff_cb *skb_cb;
+ uint8_t *skdat;
+
+ skb = dev_alloc_skb(8);
+ if (unlikely(!skb)) {
+ pr_alert("%s: out of memory?\n", __func__);
+ return -ENOMEM;
+ }
+ skb->protocol = related->skb->protocol;
+ skb->pkt_type = related->skb->pkt_type;
+ skb->ip_summed = related->skb->ip_summed;
+ skb->sk = related->skb->sk;
+
+ skb_cb = (void *)skb->cb;
+ *skb_cb = *(related->cb);
+ fix_cb(skb_cb);
+ /* fix pgn */
+ skb_cb->pgn = related->extd ? etp_pgn_dat : tp_pgn_dat;
+
+ skdat = skb_put(skb, len);
+ memcpy(skdat, dat, len);
+ ret = j1939_send(skb, j1939_level_transport);
+ if (ret < 0)
+ kfree_skb(skb);
+ return ret;
+}
+
+static int j1939xtp_do_tx_ctl(struct sk_buff *related, int extd,
+ int swap_src_dst, pgn_t pgn, const uint8_t dat[5])
+{
+ int ret;
+ struct sk_buff *skb;
+ struct j1939_sk_buff_cb *skb_cb, *rel_cb;
+ uint8_t *skdat;
+
+ rel_cb = (void *)related->cb;
+ if (!j1939tp_im_involved(rel_cb, swap_src_dst))
+ return 0;
+
+ skb = dev_alloc_skb(8);
+ if (unlikely(!skb)) {
+ pr_alert("%s: out of memory?\n", __func__);
+ return -ENOMEM;
+ }
+ skb->protocol = related->protocol;
+ skb->pkt_type = related->pkt_type;
+ skb->ip_summed = related->ip_summed;
+ skb->sk = related->sk;
+
+ skb_cb = (void *)skb->cb;
+ *skb_cb = *rel_cb;
+ fix_cb(skb_cb);
+ if (swap_src_dst)
+ j1939_skbcb_swap(skb_cb);
+ skb_cb->pgn = extd ? etp_pgn_ctl : tp_pgn_ctl;
+
+ skdat = skb_put(skb, 8);
+ memcpy(skdat, dat, 5);
+ skdat[7] = (pgn >> 16) & 0xff;
+ skdat[6] = (pgn >> 8) & 0xff;
+ skdat[5] = (pgn >> 0) & 0xff;
+
+ ret = j1939_send(skb, j1939_level_transport);
+ if (ret)
+ kfree_skb(skb);
+ return ret;
+}
+
+static inline int j1939tp_tx_ctl(struct session *session,
+ int swap_src_dst, const uint8_t dat[8])
+{
+ return j1939xtp_do_tx_ctl(session->skb, session->extd, swap_src_dst,
+ session->cb->pgn, dat);
+}
+
+static int j1939xtp_tx_abort(struct sk_buff *related, int extd,
+ int swap_src_dst, int err, pgn_t pgn)
+{
+ struct j1939_sk_buff_cb *cb = (void *)related->cb;
+ uint8_t dat[5];
+
+ if (!j1939tp_im_involved(cb, swap_src_dst))
+ return 0;
+
+ memset(dat, 0xff, sizeof(dat));
+ dat[0] = tp_cmd_abort;
+ if (!extd)
+ dat[1] = err ?: ABORT_GENERIC;
+ return j1939xtp_do_tx_ctl(related, extd, swap_src_dst, pgn, dat);
+}
+
+/* timer & scheduler functions */
+static inline void j1939session_schedule_txnow(struct session *session)
+{
+ tasklet_schedule(&session->txtask);
+}
+static enum hrtimer_restart j1939tp_txtimer(struct hrtimer *hrtimer)
+{
+ struct session *session =
+ container_of(hrtimer, struct session, txtimer);
+ j1939session_schedule_txnow(session);
+ return HRTIMER_NORESTART;
+}
+static inline void j1939tp_schedule_txtimer(struct session *session, int msec)
+{
+ hrtimer_start(&session->txtimer,
+ ktime_set(msec / 1000, (msec % 1000)*1000000UL),
+ HRTIMER_MODE_REL);
+}
+static inline void j1939tp_set_rxtimeout(struct session *session, int msec)
+{
+ hrtimer_start(&session->rxtimer,
+ ktime_set(msec / 1000, (msec % 1000)*1000000UL),
+ HRTIMER_MODE_REL);
+}
+
+/*
+ * session completion functions
+ */
+/*
+ * j1939session_drop
+ * removes a session from open session list
+ */
+static inline void j1939session_drop(struct session *session)
+{
+ sessionlist_lock();
+ list_del_init(&session->list);
+ sessionlist_unlock();
+
+ if (session->transmission) {
+ if (session->skb && session->skb->sk)
+ j1939_sock_pending_del(session->skb->sk);
+ wake_up_all(&s.wait);
+ }
+ put_session(session);
+}
+
+static inline void j1939session_completed(struct session *session)
+{
+ j1939_recv(session->skb, j1939_level_transport);
+ j1939session_drop(session);
+}
+
+static void j1939session_cancel(struct session *session, int err)
+{
+ if ((err >= 0) && j1939tp_im_involved(session->cb, -1)) {
+ if (!j1939cb_is_broadcast(session->cb)) {
+ /* do not send aborts on incoming broadcasts */
+ j1939xtp_tx_abort(session->skb, session->extd,
+ !j1939tp_im_transmitter(session->cb),
+ err, session->cb->pgn);
+ }
+ }
+ j1939session_drop(session);
+}
+
+static enum hrtimer_restart j1939tp_rxtimer(struct hrtimer *hrtimer)
+{
+ struct session *session =
+ container_of(hrtimer, struct session, rxtimer);
+ tasklet_schedule(&session->rxtask);
+ return HRTIMER_NORESTART;
+}
+
+static void j1939tp_rxtask(unsigned long val)
+{
+ struct session *session = (void *)val;
+
+ get_session(session);
+ pr_alert("%s: timeout on %i\n", __func__, session->cb->ifindex);
+ j1939session_cancel(session, ABORT_TIMEOUT);
+ put_session(session);
+}
+
+/*
+ * receive packet functions
+ */
+static void _j1939xtp_rx_bad_message(struct sk_buff *skb, int extd)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ struct session *session;
+ pgn_t pgn;
+
+ pgn = j1939xtp_ctl_to_pgn(skb->data);
+ session = j1939tp_find(sessionq(extd), cb, 0);
+ if (session /*&& (session->cb->pgn == pgn)*/) {
+ /* do not allow TP control messages on 2 pgn's */
+ j1939session_cancel(session, ABORT_FAULT);
+ put_session(session); /* ~j1939tp_find */
+ return;
+ }
+ j1939xtp_tx_abort(skb, extd, 0, ABORT_FAULT, pgn);
+ if (!session)
+ return;
+ put_session(session); /* ~j1939tp_find */
+}
+
+/* abort packets may come in 2 directions */
+static void j1939xtp_rx_bad_message(struct sk_buff *skb, int extd)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+
+ pr_info("%s, pgn %05x\n", __func__, j1939xtp_ctl_to_pgn(skb->data));
+ _j1939xtp_rx_bad_message(skb, extd);
+ j1939_skbcb_swap(cb);
+ _j1939xtp_rx_bad_message(skb, extd);
+ /* restore skb */
+ j1939_skbcb_swap(cb);
+ return;
+}
+
+static void _j1939xtp_rx_abort(struct sk_buff *skb, int extd)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ struct session *session;
+ pgn_t pgn;
+
+ pgn = j1939xtp_ctl_to_pgn(skb->data);
+ session = j1939tp_find(sessionq(extd), cb, 0);
+ if (!session)
+ return;
+ if (session->transmission && !session->last_txcmd) {
+ /*
+ * empty block:
+ * do not drop session when a transmit session did not
+ * start yet
+ */
+ } else if (session->cb->pgn == pgn)
+ j1939session_drop(session);
+ /* another PGN had a bad message */
+ /*
+ * TODO: maybe cancel current connection
+ * as another pgn was communicated
+ */
+ put_session(session); /* ~j1939tp_find */
+}
+/* abort packets may come in 2 directions */
+static inline void j1939xtp_rx_abort(struct sk_buff *skb, int extd)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+
+ pr_info("%s %i, %05x\n", __func__, cb->ifindex,
+ j1939xtp_ctl_to_pgn(skb->data));
+ _j1939xtp_rx_abort(skb, extd);
+ j1939_skbcb_swap(cb);
+ _j1939xtp_rx_abort(skb, extd);
+ /* restore skb */
+ j1939_skbcb_swap(cb);
+ return;
+}
+
+static void j1939xtp_rx_eof(struct sk_buff *skb, int extd)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ struct session *session;
+ pgn_t pgn;
+
+ /* end of tx cycle */
+ pgn = j1939xtp_ctl_to_pgn(skb->data);
+ session = j1939tp_find(sessionq(extd), cb, 1);
+ if (!session)
+ /*
+ * strange, we had EOF on closed connection
+ * do nothing, as EOF closes the connection anyway
+ */
+ return;
+
+ if (session->cb->pgn != pgn) {
+ j1939xtp_tx_abort(skb, extd, 1, ABORT_BUSY, pgn);
+ j1939session_cancel(session, ABORT_BUSY);
+ } else {
+ /* transmitted without problems */
+ j1939session_completed(session);
+ }
+ put_session(session); /* ~j1939tp_find */
+}
+
+static void j1939xtp_rx_cts(struct sk_buff *skb, int extd)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ struct session *session;
+ pgn_t pgn;
+ unsigned int pkt;
+ const uint8_t *dat;
+
+ dat = skb->data;
+ pgn = j1939xtp_ctl_to_pgn(skb->data);
+ session = j1939tp_find(sessionq(extd), cb, 1);
+ if (!session) {
+ /* 'CTS shall be ignored' */
+ return;
+ }
+ if (session->cb->pgn != pgn) {
+ /* what to do? */
+ j1939xtp_tx_abort(skb, extd, 1, ABORT_BUSY, pgn);
+ j1939session_cancel(session, ABORT_BUSY);
+ put_session(session); /* ~j1939tp_find */
+ return;
+ }
+ session_lock(session);
+ pkt = extd ? j1939etp_ctl_to_packet(dat) : dat[2];
+ if (!dat[0])
+ hrtimer_cancel(&session->txtimer);
+ else if (!pkt)
+ goto bad_fmt;
+ else if (dat[1] > session->pkt.block /* 0xff for etp */)
+ goto bad_fmt;
+ else {
+ /* set packet counters only when not CTS(0) */
+ session->pkt.done = pkt - 1;
+ session->pkt.last = session->pkt.done + dat[1];
+ if (session->pkt.last > session->pkt.total)
+ /* safety measure */
+ session->pkt.last = session->pkt.total;
+ /* TODO: do not set tx here, do it in txtask */
+ session->pkt.tx = session->pkt.done;
+ }
+ session->last_cmd = dat[0];
+ session_unlock(session);
+ if (dat[1]) {
+ j1939tp_set_rxtimeout(session, 1250);
+ if (j1939tp_im_transmitter(session->cb))
+ j1939session_schedule_txnow(session);
+ } else {
+ /* CTS(0) */
+ j1939tp_set_rxtimeout(session, 550);
+ }
+ put_session(session); /* ~j1939tp_find */
+ return;
+bad_fmt:
+ session_unlock(session);
+ j1939session_cancel(session, ABORT_FAULT);
+ put_session(session); /* ~j1939tp_find */
+}
+
+static void j1939xtp_rx_rts(struct sk_buff *skb, int extd)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ struct session *session;
+ int len;
+ const uint8_t *dat;
+ pgn_t pgn;
+
+ dat = skb->data;
+ pgn = j1939xtp_ctl_to_pgn(dat);
+
+ if ((tp_cmd_rts == dat[0]) && j1939cb_is_broadcast(cb)) {
+ pr_alert("%s: rts without destination (%i %02x)\n", __func__,
+ cb->ifindex, cb->src.addr);
+ return;
+ }
+ /*
+ * TODO: abort RTS when a similar
+ * TP is pending in the other direction
+ */
+ session = j1939tp_find(sessionq(extd), cb, 0);
+ if (session && !j1939tp_im_transmitter(cb)) {
+ /* RTS on pending connection */
+ j1939session_cancel(session, ABORT_BUSY);
+ if ((pgn != session->cb->pgn) && (tp_cmd_bam != dat[0]))
+ j1939xtp_tx_abort(skb, extd, 1, ABORT_BUSY, pgn);
+ put_session(session); /* ~j1939tp_find */
+ return;
+ } else if (!session && j1939tp_im_transmitter(cb)) {
+ pr_alert("%s: I should tx (%i %02x %02x)\n", __func__,
+ cb->ifindex, cb->src.addr, cb->dst.addr);
+ return;
+ }
+ if (session && (0 != session->last_cmd)) {
+ /* we received a second rts on the same connection */
+ pr_alert("%s: connection exists (%i %02x %02x)\n", __func__,
+ cb->ifindex, cb->src.addr, cb->dst.addr);
+ j1939session_cancel(session, ABORT_BUSY);
+ put_session(session); /* ~j1939tp_find */
+ return;
+ }
+ if (session) {
+ /*
+ * make sure 'sa' & 'da' are correct !
+ * They may be 'not filled in yet' for sending
+ * skb's, since they did not pass the Address Claim ever.
+ */
+ session->cb->src.addr = cb->src.addr;
+ session->cb->dst.addr = cb->dst.addr;
+ } else {
+ int abort = 0;
+ if (extd) {
+ len = j1939etp_ctl_to_size(dat);
+ if (len > (max_packet_size ?: MAX_ETP_PACKET_SIZE))
+ abort = ABORT_RESOURCE;
+ else if (len <= MAX_TP_PACKET_SIZE)
+ abort = ABORT_FAULT;
+ } else {
+ len = j1939tp_ctl_to_size(dat);
+ if (len > MAX_TP_PACKET_SIZE)
+ abort = ABORT_FAULT;
+ else if (max_packet_size && (len > max_packet_size))
+ abort = ABORT_RESOURCE;
+ }
+ if (abort) {
+ j1939xtp_tx_abort(skb, extd, 1, abort, pgn);
+ return;
+ }
+ session = j1939session_fresh_new(len, cb, pgn);
+ if (!session) {
+ j1939xtp_tx_abort(skb, extd, 1, ABORT_RESOURCE, pgn);
+ return;
+ }
+ session->extd = extd;
+ /* initialize the control buffer: plain copy */
+ session->pkt.total = (len+6)/7;
+ session->pkt.block = 0xff;
+ if (!extd) {
+ if (dat[3] != session->pkt.total)
+ pr_alert("%s: strange total,"
+ " %u != %u\n", __func__,
+ session->pkt.total, dat[3]);
+ session->pkt.total = dat[3];
+ session->pkt.block = dat[4];
+ }
+ session->pkt.done = session->pkt.tx = 0;
+ get_session(session); /* equivalent to j1939tp_find() */
+ sessionlist_lock();
+ list_add_tail(&session->list, sessionq(extd));
+ sessionlist_unlock();
+ }
+ session->last_cmd = dat[0];
+
+ j1939tp_set_rxtimeout(session, 1250);
+
+ if (j1939tp_im_receiver(session->cb)) {
+ if (extd || (tp_cmd_bam != dat[0]))
+ j1939session_schedule_txnow(session);
+ }
+ /*
+ * as soon as it's inserted, things can go fast
+ * protect against a long delay
+ * between spin_unlock & next statement
+ * so, only release here, at the end
+ */
+ put_session(session); /* ~j1939tp_find */
+ return;
+}
+
+static void j1939xtp_rx_dpo(struct sk_buff *skb, int extd)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ struct session *session;
+ pgn_t pgn;
+ const uint8_t *dat = skb->data;
+
+ pgn = j1939xtp_ctl_to_pgn(dat);
+ session = j1939tp_find(sessionq(extd), cb, 0);
+ if (!session) {
+ pr_info("%s: %s\n", __func__, "no connection found");
+ return;
+ }
+
+ if (session->cb->pgn != pgn) {
+ pr_info("%s: different pgn\n", __func__);
+ j1939xtp_tx_abort(skb, 1, 1, ABORT_BUSY, pgn);
+ j1939session_cancel(session, ABORT_BUSY);
+ put_session(session); /* ~j1939tp_find */
+ return;
+ }
+ /* transmitted without problems */
+ session->pkt.dpo = j1939etp_ctl_to_packet(skb->data);
+ session->last_cmd = dat[0];
+ j1939tp_set_rxtimeout(session, 750);
+ put_session(session); /* ~j1939tp_find */
+}
+
+static void j1939xtp_rx_dat(struct sk_buff *skb, int extd)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ struct session *session;
+ const uint8_t *dat;
+ uint8_t *tpdat;
+ int offset;
+ int nbytes;
+ int final;
+ int do_cts_eof;
+ int packet;
+
+ session = j1939tp_find(sessionq(extd), cb, 0);
+ if (!session) {
+ pr_info("%s:%s\n", __func__, "no connection found");
+ return;
+ }
+ dat = skb->data;
+ if (skb->len <= 1)
+ /* makes no sense */
+ goto strange_packet_unlocked;
+
+ session_lock(session);
+
+ switch (session->last_cmd) {
+ case 0xff:
+ break;
+ case etp_cmd_dpo:
+ if (extd)
+ break;
+ case tp_cmd_bam:
+ case tp_cmd_cts:
+ if (!extd)
+ break;
+ default:
+ pr_info("%s: last %02x\n", __func__,
+ session->last_cmd);
+ goto strange_packet;
+ }
+
+ packet = (dat[0]-1+session->pkt.dpo);
+ offset = packet * 7;
+ if ((packet > session->pkt.total) ||
+ (session->pkt.done+1) > session->pkt.total) {
+ pr_info("%s: should have been completed\n", __func__);
+ goto strange_packet;
+ }
+ nbytes = session->skb->len - offset;
+ if (nbytes > 7)
+ nbytes = 7;
+ if ((nbytes <= 0) || ((nbytes + 1) > skb->len)) {
+ pr_info("%s: nbytes %i, len %i\n", __func__, nbytes,
+ skb->len);
+ goto strange_packet;
+ }
+ tpdat = session->skb->data;
+ memcpy(&tpdat[offset], &dat[1], nbytes);
+ if (packet == session->pkt.done)
+ ++session->pkt.done;
+
+ if (!extd && j1939cb_is_broadcast(session->cb)) {
+ final = session->pkt.done >= session->pkt.total;
+ do_cts_eof = 0;
+ } else {
+ final = 0; /* never final, an EOF must follow */
+ do_cts_eof = (session->pkt.done >= session->pkt.last);
+ }
+ session_unlock(session);
+ if (final) {
+ j1939session_completed(session);
+ } else if (do_cts_eof) {
+ j1939tp_set_rxtimeout(session, 1250);
+ if (j1939tp_im_receiver(session->cb))
+ j1939session_schedule_txnow(session);
+ } else {
+ j1939tp_set_rxtimeout(session, 250);
+ }
+ session->last_cmd = 0xff;
+ put_session(session); /* ~j1939tp_find */
+ return;
+
+strange_packet:
+ /* unlock session (spinlock) before trying to send */
+ session_unlock(session);
+strange_packet_unlocked:
+ j1939session_cancel(session, ABORT_FAULT);
+ put_session(session); /* ~j1939tp_find */
+}
+
+/*
+ * transmit function
+ */
+static int j1939tp_txnext(struct session *session)
+{
+ uint8_t dat[8];
+ const uint8_t *tpdat;
+ int ret, offset, len, pkt_done, pkt_end;
+ unsigned int pkt;
+
+ memset(dat, 0xff, sizeof(dat));
+ get_session(session); /* do not loose it */
+
+ switch (session->last_cmd) {
+ case 0:
+ if (!j1939tp_im_transmitter(session->cb))
+ break;
+ dat[1] = (session->skb->len >> 0) & 0xff;
+ dat[2] = (session->skb->len >> 8) & 0xff;
+ dat[3] = session->pkt.total;
+ if (session->extd) {
+ dat[0] = etp_cmd_rts;
+ dat[1] = (session->skb->len >> 0) & 0xff;
+ dat[2] = (session->skb->len >> 8) & 0xff;
+ dat[3] = (session->skb->len >> 16) & 0xff;
+ dat[4] = (session->skb->len >> 24) & 0xff;
+ } else if (j1939cb_is_broadcast(session->cb)) {
+ dat[0] = tp_cmd_bam;
+ /* fake cts for broadcast */
+ session->pkt.tx = 0;
+ } else {
+ dat[0] = tp_cmd_rts;
+ dat[4] = dat[3];
+ }
+ if (dat[0] == session->last_txcmd)
+ /* done already */
+ break;
+ ret = j1939tp_tx_ctl(session, 0, dat);
+ if (ret < 0)
+ goto failed;
+ session->last_txcmd = dat[0];
+ /* must lock? */
+ if (tp_cmd_bam == dat[0])
+ j1939tp_schedule_txtimer(session, 50);
+ j1939tp_set_rxtimeout(session, 1250);
+ break;
+ case tp_cmd_rts:
+ case etp_cmd_rts:
+ if (!j1939tp_im_receiver(session->cb))
+ break;
+tx_cts:
+ ret = 0;
+ len = session->pkt.total - session->pkt.done;
+ if (len > 255)
+ len = 255;
+ if (len > session->pkt.block)
+ len = session->pkt.block;
+ if (block && (len > block))
+ len = block;
+
+ if (session->extd) {
+ pkt = session->pkt.done+1;
+ dat[0] = etp_cmd_cts;
+ dat[1] = len;
+ dat[2] = (pkt >> 0) & 0xff;
+ dat[3] = (pkt >> 8) & 0xff;
+ dat[4] = (pkt >> 16) & 0xff;
+ } else {
+ dat[0] = tp_cmd_cts;
+ dat[1] = len;
+ dat[2] = session->pkt.done+1;
+ }
+ if (dat[0] == session->last_txcmd)
+ /* done already */
+ break;
+ ret = j1939tp_tx_ctl(session, 1, dat);
+ if (ret < 0)
+ goto failed;
+ if (len)
+ /* only mark cts done when len is set */
+ session->last_txcmd = dat[0];
+ j1939tp_set_rxtimeout(session, 1250);
+ break;
+ case etp_cmd_cts:
+ if (j1939tp_im_transmitter(session->cb) && session->extd &&
+ (etp_cmd_dpo != session->last_txcmd)) {
+ /* do dpo */
+ dat[0] = etp_cmd_dpo;
+ session->pkt.dpo = session->pkt.done;
+ pkt = session->pkt.dpo;
+ dat[1] = session->pkt.last - session->pkt.done;
+ dat[2] = (pkt >> 0) & 0xff;
+ dat[3] = (pkt >> 8) & 0xff;
+ dat[4] = (pkt >> 16) & 0xff;
+ ret = j1939tp_tx_ctl(session, 0, dat);
+ if (ret < 0)
+ goto failed;
+ session->last_txcmd = dat[0];
+ j1939tp_set_rxtimeout(session, 1250);
+ session->pkt.tx = session->pkt.done;
+ }
+ case tp_cmd_cts:
+ case 0xff: /* did some data */
+ case etp_cmd_dpo:
+ if ((session->extd || !j1939cb_is_broadcast(session->cb)) &&
+ j1939tp_im_receiver(session->cb)) {
+ if (session->pkt.done >= session->pkt.total) {
+ if (session->extd) {
+ dat[0] = etp_cmd_eof;
+ dat[1] = session->skb->len >> 0;
+ dat[2] = session->skb->len >> 8;
+ dat[3] = session->skb->len >> 16;
+ dat[4] = session->skb->len >> 24;
+ } else {
+ dat[0] = tp_cmd_eof;
+ dat[1] = session->skb->len;
+ dat[2] = session->skb->len >> 8;
+ dat[3] = session->pkt.total;
+ }
+ if (dat[0] == session->last_txcmd)
+ /* done already */
+ break;
+ ret = j1939tp_tx_ctl(session, 1, dat);
+ if (ret < 0)
+ goto failed;
+ session->last_txcmd = dat[0];
+ j1939tp_set_rxtimeout(session, 1250);
+ /* wait for the EOF packet to come in */
+ break;
+ } else if (session->pkt.done >= session->pkt.last) {
+ session->last_txcmd = 0;
+ goto tx_cts;
+ }
+ }
+ case tp_cmd_bam:
+ if (!j1939tp_im_transmitter(session->cb))
+ break;
+ tpdat = session->skb->data;
+ ret = 0;
+ pkt_done = 0;
+ pkt_end = (!session->extd && j1939cb_is_broadcast(session->cb))
+ ? session->pkt.total : session->pkt.last;
+
+ while (session->pkt.tx < pkt_end) {
+ dat[0] = session->pkt.tx - session->pkt.dpo+1;
+ offset = session->pkt.tx * 7;
+ len = session->skb->len - offset;
+ if (len > 7)
+ len = 7;
+ memcpy(&dat[1], &tpdat[offset], len);
+ ret = j1939tp_tx_dat(session, dat, len+1);
+ if (ret < 0)
+ break;
+ session->last_txcmd = 0xff;
+ ++pkt_done;
+ ++session->pkt.tx;
+ if (j1939cb_is_broadcast(session->cb)) {
+ if (session->pkt.tx < session->pkt.total)
+ j1939tp_schedule_txtimer(session, 50);
+ break;
+ }
+ }
+ if (pkt_done)
+ j1939tp_set_rxtimeout(session, 250);
+ if (ret)
+ goto failed;
+ break;
+ }
+ put_session(session);
+ return 0;
+failed:
+ put_session(session);
+ return ret;
+}
+
+static void j1939tp_txtask(unsigned long val)
+{
+ struct session *session = (void *)val;
+ int ret;
+
+ get_session(session);
+ ret = j1939tp_txnext(session);
+ if (ret < 0)
+ j1939tp_schedule_txtimer(session, retry_ms);
+ put_session(session);
+}
+
+static inline int j1939tp_tx_initial(struct session *session)
+{
+ int ret;
+
+ get_session(session);
+ ret = j1939tp_txnext(session);
+ /* set nonblocking for further packets */
+ session->cb->msg_flags |= MSG_DONTWAIT;
+ put_session(session);
+ return ret;
+}
+
+/* this call is to be used as probe within wait_event_xxx() */
+static int j1939session_insert(struct session *session)
+{
+ struct session *pending;
+
+ sessionlist_lock();
+ pending = _j1939tp_find(sessionq(session->extd), session->cb, 0);
+ if (pending)
+ /* revert the effect of find() */
+ put_session(pending);
+ else
+ list_add_tail(&session->list, sessionq(session->extd));
+ sessionlist_unlock();
+ return pending ? 0 : 1;
+}
+/*
+ * j1939 main intf
+ */
+int j1939_send_transport(struct sk_buff *skb)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ struct session *session;
+ int ret;
+
+ if ((tp_pgn_dat == cb->pgn) || (tp_pgn_ctl == cb->pgn) ||
+ (etp_pgn_dat == cb->pgn) || (etp_pgn_ctl == cb->pgn))
+ /* avoid conflict */
+ return -EDOM;
+ if (skb->len <= 8)
+ return 0;
+ else if (skb->len > (max_packet_size ?: MAX_ETP_PACKET_SIZE))
+ return -EMSGSIZE;
+
+ if (skb->len > MAX_TP_PACKET_SIZE) {
+ if (j1939cb_is_broadcast(cb))
+ return -EDESTADDRREQ;
+ }
+
+ /* prepare new session */
+ session = j1939session_new(skb);
+ if (!session)
+ return -ENOMEM;
+
+ session->extd = (skb->len > MAX_TP_PACKET_SIZE) ? EXTENDED : REGULAR;
+ session->transmission = 1;
+ session->pkt.total = (skb->len + 6)/7;
+ session->pkt.block = session->extd ? 255 :
+ (block ?: session->pkt.total);
+ if (j1939cb_is_broadcast(session->cb))
+ /* set the end-packet for broadcast */
+ session->pkt.last = session->pkt.total;
+
+ /* insert into queue, but avoid collision with pending session */
+ if (session->cb->msg_flags & MSG_DONTWAIT)
+ ret = j1939session_insert(session) ? 0 : -EAGAIN;
+ else
+ ret = wait_event_interruptible(s.wait,
+ j1939session_insert(session));
+ if (ret < 0)
+ goto failed;
+
+ ret = j1939tp_tx_initial(session);
+ if (!ret)
+ /* transmission started */
+ return RESULT_STOP;
+ sessionlist_lock();
+ list_del_init(&session->list);
+ sessionlist_unlock();
+failed:
+ /*
+ * hide the skb from j1939session_drop, as it would
+ * kfree_skb, but our caller will kfree_skb(skb) too.
+ */
+ session->skb = NULL;
+ j1939session_drop(session);
+ return ret;
+}
+
+int j1939_recv_transport(struct sk_buff *skb)
+{
+ struct j1939_sk_buff_cb *cb = (void *)skb->cb;
+ const uint8_t *dat;
+
+ switch (cb->pgn) {
+ case etp_pgn_dat:
+ j1939xtp_rx_dat(skb, EXTENDED);
+ break;
+ case etp_pgn_ctl:
+ if (skb->len < 8) {
+ j1939xtp_rx_bad_message(skb, EXTENDED);
+ break;
+ }
+ dat = skb->data;
+ switch (*dat) {
+ case etp_cmd_rts:
+ j1939xtp_rx_rts(skb, EXTENDED);
+ break;
+ case etp_cmd_cts:
+ j1939xtp_rx_cts(skb, EXTENDED);
+ break;
+ case etp_cmd_dpo:
+ j1939xtp_rx_dpo(skb, EXTENDED);
+ break;
+ case etp_cmd_eof:
+ j1939xtp_rx_eof(skb, EXTENDED);
+ break;
+ case etp_cmd_abort:
+ j1939xtp_rx_abort(skb, EXTENDED);
+ break;
+ default:
+ j1939xtp_rx_bad_message(skb, EXTENDED);
+ break;
+ }
+ break;
+ case tp_pgn_dat:
+ j1939xtp_rx_dat(skb, REGULAR);
+ break;
+ case tp_pgn_ctl:
+ if (skb->len < 8) {
+ j1939xtp_rx_bad_message(skb, REGULAR);
+ break;
+ }
+ dat = skb->data;
+ switch (*dat) {
+ case tp_cmd_bam:
+ case tp_cmd_rts:
+ j1939xtp_rx_rts(skb, REGULAR);
+ break;
+ case tp_cmd_cts:
+ j1939xtp_rx_cts(skb, REGULAR);
+ break;
+ case tp_cmd_eof:
+ j1939xtp_rx_eof(skb, REGULAR);
+ break;
+ case tp_cmd_abort:
+ j1939xtp_rx_abort(skb, REGULAR);
+ break;
+ default:
+ j1939xtp_rx_bad_message(skb, REGULAR);
+ break;
+ }
+ break;
+ default:
+ return 0;
+ }
+ return RESULT_STOP;
+}
+
+static struct session *j1939session_fresh_new(int size,
+ struct j1939_sk_buff_cb *rel_cb, pgn_t pgn)
+{
+ struct sk_buff *skb;
+ struct j1939_sk_buff_cb *cb;
+ struct session *session;
+
+ skb = dev_alloc_skb(size);
+ if (!skb)
+ return NULL;
+ cb = (void *)skb->cb;
+ *cb = *rel_cb;
+ fix_cb(cb);
+ cb->pgn = pgn;
+
+ session = j1939session_new(skb);
+ if (!session) {
+ kfree(skb);
+ return NULL;
+ }
+ /* alloc data area */
+ skb_put(skb, size);
+ return session;
+}
+static struct session *j1939session_new(struct sk_buff *skb)
+{
+ struct session *session;
+
+ session = kzalloc(sizeof(*session), gfp_any());
+ if (!session)
+ return NULL;
+ INIT_LIST_HEAD(&session->list);
+ spin_lock_init(&session->lock);
+ session->skb = skb;
+
+ session->cb = (void *)session->skb->cb;
+ hrtimer_init(&session->txtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ session->txtimer.function = j1939tp_txtimer;
+ hrtimer_init(&session->rxtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ session->rxtimer.function = j1939tp_rxtimer;
+ tasklet_init(&session->txtask, j1939tp_txtask, (unsigned long)session);
+ tasklet_init(&session->rxtask, j1939tp_rxtask, (unsigned long)session);
+ return session;
+}
+
+static int j1939tp_notifier(struct notifier_block *nb,
+ unsigned long msg, void *data)
+{
+ struct net_device *netdev = (struct net_device *)data;
+ struct session *session, *saved;
+
+ if (!net_eq(dev_net(netdev), &init_net))
+ return NOTIFY_DONE;
+
+ if (netdev->type != ARPHRD_CAN)
+ return NOTIFY_DONE;
+
+ if (msg != NETDEV_UNREGISTER)
+ return NOTIFY_DONE;
+
+ sessionlist_lock();
+ list_for_each_entry_safe(session, saved, &s.sessionq, list) {
+ if (session->cb->ifindex != netdev->ifindex)
+ continue;
+ list_del_init(&session->list);
+ put_session(session);
+ }
+ list_for_each_entry_safe(session, saved, &s.extsessionq, list) {
+ if (session->cb->ifindex != netdev->ifindex)
+ continue;
+ list_del_init(&session->list);
+ put_session(session);
+ }
+ sessionlist_unlock();
+ return NOTIFY_DONE;
+}
+
+/* SYSCTL */
+static struct ctl_table_header *j1939tp_table_header;
+
+static int min_block = 1;
+static int max_block = 255;
+static int min_packet = 8;
+static int max_packet = ((2 << 24)-1)*7;
+
+static int min_retry = 5;
+static int max_retry = 5000;
+
+static ctl_table j1939tp_table[] = {
+ {
+ .procname = "transport_cts_nr_of_frames",
+ .data = &block,
+ .maxlen = sizeof(block),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec_minmax,
+ .extra1 = &min_block,
+ .extra2 = &max_block,
+ },
+ {
+ .procname = "transport_max_payload_in_bytes",
+ .data = &max_packet_size,
+ .maxlen = sizeof(max_packet_size),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec_minmax,
+ .extra1 = &min_packet,
+ .extra2 = &max_packet,
+ },
+ {
+ .procname = "transport_tx_retry_ms",
+ .data = &retry_ms,
+ .maxlen = sizeof(retry_ms),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec_minmax,
+ .extra1 = &min_retry,
+ .extra2 = &max_retry,
+ },
+ { },
+};
+
+static struct ctl_path j1939tp_path[] = {
+ { .procname = "net", },
+ { .procname = j1939_procname, },
+ { }
+};
+
+/* PROC */
+static int j1939tp_proc_show_session(struct seq_file *sqf,
+ struct session *session)
+{
+ seq_printf(sqf, "%i", session->cb->ifindex);
+ if (session->cb->src.name)
+ seq_printf(sqf, "\t%016llx", session->cb->src.name);
+ else
+ seq_printf(sqf, "\t%02x", session->cb->src.addr);
+ if (session->cb->dst.name)
+ seq_printf(sqf, "\t%016llx", session->cb->dst.name);
+ else if (j1939_address_is_unicast(session->cb->dst.addr))
+ seq_printf(sqf, "\t%02x", session->cb->dst.addr);
+ else
+ seq_printf(sqf, "\t-");
+ seq_printf(sqf, "\t%05x\t%u/%u\n", session->cb->pgn,
+ session->pkt.done*7, session->skb->len);
+ return 0;
+}
+
+static int j1939tp_proc_show(struct seq_file *sqf, void *v)
+{
+ struct session *session;
+
+ seq_printf(sqf, "iface\tsrc\tdst\tpgn\tdone/total\n");
+ sessionlist_lock();
+ list_for_each_entry(session, &s.sessionq, list)
+ j1939tp_proc_show_session(sqf, session);
+ list_for_each_entry(session, &s.extsessionq, list)
+ j1939tp_proc_show_session(sqf, session);
+ sessionlist_unlock();
+ return 0;
+}
+
+int __init j1939tp_module_init(void)
+{
+ spin_lock_init(&s.lock);
+ INIT_LIST_HEAD(&s.sessionq);
+ INIT_LIST_HEAD(&s.extsessionq);
+ spin_lock_init(&s.del.lock);
+ INIT_LIST_HEAD(&s.del.sessionq);
+ INIT_WORK(&s.del.work, j1939tp_del_work);
+
+ s.notifier.notifier_call = j1939tp_notifier;
+ register_netdevice_notifier(&s.notifier);
+
+ j1939_proc_add("transport", j1939tp_proc_show, NULL);
+ j1939tp_table_header =
+ register_sysctl_paths(j1939tp_path, j1939tp_table);
+ init_waitqueue_head(&s.wait);
+ return 0;
+}
+
+void j1939tp_module_exit(void)
+{
+ struct session *session, *saved;
+
+ wake_up_all(&s.wait);
+
+ unregister_sysctl_table(j1939tp_table_header);
+ unregister_netdevice_notifier(&s.notifier);
+ j1939_proc_remove("transport");
+ sessionlist_lock();
+ list_for_each_entry_safe(session, saved, &s.extsessionq, list) {
+ list_del_init(&session->list);
+ put_session(session);
+ }
+ list_for_each_entry_safe(session, saved, &s.sessionq, list) {
+ list_del_init(&session->list);
+ put_session(session);
+ }
+ sessionlist_unlock();
+ flush_scheduled_work();
+}
+
--
1.7.2.5
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v4 4/5] can-j1939: add documentation
[not found] ` <20110427085330.GA757-ozGf4kBk5synFtIcQ8t7k3L8HoS0Hn3T@public.gmane.org>
` (2 preceding siblings ...)
2011-04-27 8:58 ` [PATCH v4 3/5] can-j1939: Import SAE J1939 stack Kurt Van Dijck
@ 2011-04-27 9:00 ` Kurt Van Dijck
2011-04-27 9:03 ` [PATCH v4 5/5] iproute2: add can-j1939 support Kurt Van Dijck
4 siblings, 0 replies; 11+ messages in thread
From: Kurt Van Dijck @ 2011-04-27 9:00 UTC (permalink / raw)
To: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
netdev-u79uwXL29TY76Z2rM5mHXA
Add documentation of the SAE J1939 protocol stack
Signed-off-by: Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
---
Documentation/networking/j1939.txt | 576 ++++++++++++++++++++++++++++++++++++
MAINTAINERS | 8 +
2 files changed, 584 insertions(+), 0 deletions(-)
create mode 100644 Documentation/networking/j1939.txt
diff --git a/Documentation/networking/j1939.txt b/Documentation/networking/j1939.txt
new file mode 100644
index 0000000..ef0a963
--- /dev/null
+++ b/Documentation/networking/j1939.txt
@@ -0,0 +1,576 @@
+============================================================================
+
+j1939.txt
+
+Readme file for the J1939 Protocol
+
+This file contains
+
+ 1 Overview / What is j1939
+ 1.1 specifications used
+
+ 2 Motivation
+
+ 3 J1939 concepts
+ 3.1 socket type
+ 3.2 addressing
+ 3.3 priority
+ 3.4 PGN
+ 3.5 filtering
+ 3.6 destinations with dynamic address
+
+ 4 How to use J1939
+ 4.1 rtnetlink interface
+ 4.2 API calls
+ 4.2.1 Message flags during sendmsg
+ 4.2.2 SCM_J1939_DEST_ADDR & SCM_J1939_DEST_NAME
+ 4.2.3 SCM_J1939_PRIORITY
+ 4.3 Dynamic addressing
+ 4.4 Send Examples
+ 4.4.1 Static address
+ 4.4.2 Dynamic address
+ 4.4.3 Mixed mode
+
+ 5 socket options
+ 5.1 SO_J1939_FILTER
+ 5.2 SO_J1939_PROMISC
+ 5.3 SO_J1939_RECV_OWN
+ 5.4 SO_J1939_SEND_PRIO
+
+ 6 can-j1939 procfs interface
+ 6.1 /proc/net/can-j1939/ecu
+ 6.2 /proc/net/can-j1939/filter
+ 6.3 /proc/net/can-j1939/sock
+ 6.4 /proc/net/can-j1939/transport
+
+ 7 can-j1939 SYSCTL
+ 7.1 /proc/sys/net/can-j1939/transport_max_payload_in_bytes
+ 7.2 /proc/sys/net/can-j1939/transport_cts_nr_of_frames
+ 7.3 /proc/sys/net/can-j1939/transport_tx_retry_ms
+
+ 8 Credits
+
+============================================================================
+
+1. Introduction
+--------------------------------
+
+ SAE J1939 defines a higher layer protocol on CAN. It implements a more
+ sophisticated addressing scheme and extends the maximum packet size above
+ 8 bytes. Several derived specifications exists, which differ from the
+ original j1939 on the application level, like MilCAN, NMEA2000 and
+ especially ISO-11783 (ISOBUS). This last one specifies the so-called ETP
+ (Extended Transport Protocol) which is has been included in this
+ implementation. This inclusion results in a maximum packet size of
+ ((2^24)-1)*7 bytes
+
+
+1.1 specifications used
+
+ SAE J1939-21 : data link layer
+ SAE J1939-81 : network management
+ ISO 11783-6 : Virtual Terminal (Extended Transport Protocol)
+
+
+2. Motivation
+--------------------------------
+
+ Given the fact there's something like SocketCAN with an API similar to BSD
+ sockets, we found some reasons to justify a kernel implementation for the
+ addressing and transport methods used by J1939.
+
+ * addressing:
+ When a process on an ECU communicates via j1939, it should not necessarily
+ know its source address. Although at least 1 process per ECU should know
+ the source address. Other processes should be able to reuse that address.
+ This way, address parameters for different processes cooperating for the
+ same ECU, are not duplicated.
+ This way of working is closely related to the unix concept where programs
+ do just 1 thing, and do it well.
+
+ * dynamic addressing:
+ Address Claiming in J1939 is time critical. Furthermore data transport
+ should be handled properly during the address negotiation. Putting these
+ functionality in the kernel eliminates this functionality as a requirement
+ for _every_ userspace process that communicates via J1939. This results in
+ a consistent J1939 bus with proper addressing.
+
+ * transport:
+ Both TP & ETP reuse some PGN's to relay big packets over them. Different
+ processes may thus use the same TP & ETP PGN's without actually knowing it.
+ The individual TP & ETP sessions _must_ be serialized (synchronised)
+ between different processes. The kernel solves this problem properly, and
+ eliminates the serialisation (synchronisation) as a requirement for
+ _every_ userspace process that communicates via J1939.
+
+ J1939 defines some other features (relaying, gateway, Fast Packet transport,
+ ...). In-kernel code for these would not contribute to protocol stability.
+ Therefore, these parts are left to userspace.
+
+ The j1939 sockets operate on CAN network devices (see SocketCAN). Any j1939
+ userspace library operating on CAN raw sockets will still operate properly.
+ Since such library does not communicate with the in-kernel implementation,
+ care must be taken that these 2 do not interfere. In practice, this means
+ they cannot share ECU addresses. A single ECU (or virtual ECU) address is
+ used by the library exclusively, or by the in-kernel system exclusively.
+
+
+3. J1939 concepts
+--------------------------------
+
+3.1 PGN
+
+ The PGN (Parameter Group Number) is a number to identify a packet. The PGN
+ is composed as follows:
+ 1 bit : Reserved Bit
+ 1 bit : Data Page
+ 8 bits : PF (PDU Format)
+ 8 bits : PS (PDU Specific)
+
+ In J1939-21, distinction is made between PDU1 Format (where PF < 240) and
+ PDU2 Format (where PF >= 240). Furthermore, when using PDU2 Format, the
+ PS-field contains a so-called Group Extension, which is part of the PGN.
+ When using PDU2 Format, the Group Extension is set in the PS-field.
+
+ On the other hand, when using PDU1 Format, the PS-field contains a so-called
+ Destination Address, which is _not_ part of the PGN. When communicating a
+ PGN from userspace to kernel (or visa versa) and PDU2 Format is used, the
+ PS-field of the PGN shall be set to zero. The Destination Address shall be
+ set elsewhere.
+
+ Regarding PGN mapping to 29-bit CAN identifier, the Destination Address
+ shall be get/set from/to the apropriate bits of the identifier by the kernel.
+
+
+3.2 addressing
+
+ Both static and dynamic addressing methods can be used.
+
+ For static addresses, no extra checks are made by the kernel, and provided
+ addresses are considered right. This responsibility is for the OEM or system
+ integrator.
+
+ For dynamic addressing, so-called Address Claiming, extra support is forseen
+ in the kernel. In J1939 any ECU is known by it's 64-bit NAME. At the moment
+ of succesfull address claim, the kernel keeps track of both NAME and source
+ address being claimed. This serves as a base for filter schemes. By default,
+ packets with a destination that is not locally, will be rejected soon after
+ reception.
+
+ Mixed mode packets (from a static to a dynamic address or vice versa) are
+ allowed. The BSD sockets define seperate API calls for getting/setting the
+ local & remote address and are applicable for J1939 sockets.
+
+
+3.3 Filtering
+
+ Similar to SocketCAN, j1939 defines filters per socket that a user can set
+ in order to receive a subset of the j1939 traffic. Filtering can base on
+ * SA
+ * NAME
+ * PGN
+
+ There is a semantic difference with SocketCAN with regard to filtering.
+ When multiple filters are in place for a single socket, and a packet comes
+ in that matches several of those filters, the packet is only received once
+ for that socket.
+ The rationale behind this difference originates in the filter capabilities.
+ Where SocketCAN filters on only 1 orthogonal (can id), J1939 can filter
+ on 3 orthogonal properties (sa, name, pgn).
+
+ When a filter on the SA is set, j1939 traffic with a matching SA, but with
+ its NAME set (aka having claimed SA successfully) will match, although
+ the filter would not match its NAME.
+
+ Filtering on priority is _not_ supported.
+
+
+4. How to use J1939
+--------------------------------
+
+4.1 rtnetlink interface
+
+ Per default j1939 is not active. Specifying can_ifindex != 0 in bind(2)
+ or connect(2) needs an active j1939 on that interface. You must have done
+ $ ip link set canX j1939 on
+ on that interface.
+
+ $ ip link set canX j1939 down
+ disables j1939 on canX.
+
+ Assigning addresses is done via
+ $ ip addr add dev canX j1939 0xXX
+ statically or
+ $ ip addr add dev canX j1939 name 0xXX
+ dynamically. In the latter case, address claiming must take place
+ before other traffic can leave.
+
+ Removing addresses is done similarly via
+ $ ip addr del dev canX j1939 0xXX
+ $ ip addr del dev canX j1939 name 0xXX
+
+ A static address cannot be assigned together with a 64bit name.
+
+4.2 API calls
+
+ Like TCP/IP and CAN, you first need to open a socket for communicating over a
+ CAN network. To use j1939, include <include/linux/j1939.h>. From there,
+ <include/linux/can.h> will be included too.
+ To open a socket, you would write
+
+ s = socket(PF_CAN, SOCK_DGRAM, CAN_J1939);
+
+ J1939 does use SOCK_DGRAM sockets. In the j1939 specification, connections are
+ mentioned in the context of transport protocol sessions. These still deliver
+ packets to the other end (using several CAN packets).
+ SOCK_STREAM is never appropriate.
+
+ After the successful creation of the socket, you would normally use the
+ bind(2) and/or connect(2) system call to bind the socket to a CAN interface
+ (which is different from TCP/IP due to different addressing) After binding
+ and/or connecting the socket, you can read(2) and write(2) from/to the socket
+ or use send(2), sendto(2), sendmsg(2) and the recv* counterpart operations on
+ the socket as usual. There are also J1939 specific socket options described
+ below.
+
+ In order to send data, a bind(2) must have succeeded. bind(2) assigns a local
+ address to a socket. For this to succeed, you can only choose addresses
+ that have been assigned earlier (see 4.1). When an empty address is assigned
+ (ie. SA=0xff && name=0), a default is taken for the device that is bound to.
+
+ Different from CAN is that the payload data is just the data that get send,
+ without it's header info. The header info is derived from the sockaddr
+ supplied to bind(2), connect(2), sendto(2) and recvfrom(2). A write(2) with
+ size 4 will result in a packet with 4 bytes.
+
+ The sockaddr structure has extensions for use with j1939 as specified below:
+ struct sockaddr_can {
+ sa_family_t can_family;
+ int can_ifindex;
+ union {
+ struct {
+ __u64 name;
+ __u32 pgn;
+ __u8 addr;
+ } j1939;
+ } can_addr;
+ }
+
+ can_family & can_ifindex serve the same purpose as for other SocketCAN sockets.
+
+ can_addr.j1939.pgn specifies the PGN (max 0x3ffff). Individual bits are
+ specified above.
+
+ can_addr.j1939.name contains the 64-bit J1939 NAME.
+
+ can_addr.j1939.addr contains the source address.
+
+ When sending data, the source address is applied as follows: If
+ can_addr.j1939.name != 0 the NAME is looked up by the kernel and the
+ corresponding Source Address is used. If can_addr.j1939.name == 0,
+ can_addr.j1939.addr is used.
+
+ After a bind(2), the local address is assigned, i.e. the source address.
+ After a connect(2), the remote address is assigned, i.e. the destination
+ address.
+
+ Both write(2) and send(2) will send a packet with local address from bind,
+ remote address from connect(2). When the address was not set, a broadcast is
+ sent. The PGN is used from bind(2) or overruled with sendto(2), which will
+ override the destination address when valid, and the PGN when valid.
+
+ Both read(2) and recv(2) will receive packets matching the sockets filters.
+ recvfrom(2) will receive these packets with originator's address.
+
+ When creating a socket, reasonable defaults have been set. Some options can be
+ modified with setsockopt(2) & getsockopt(2).
+
+4.2.1 Message flags during sendmsg
+
+ send(2), sendto(2) and sendmsg(2) take a 'flags' argument. J1939 interpretes
+ these flags during outgoing traffic:
+
+ * MSG_DONTWAIT determines nonblocking operation. When a packet must wait for
+ any reason, -EAGAIN is returned.
+
+ * MSG_SYN
+ Packets flagged with MSG_SYN will wait for all pending packets on a socket
+ to be sent before trying to send. This means that if a socket just started
+ a Transport Protocol session, a packet with MSG_SYN will wait for that
+ session to complete before proceeding.
+ Traffic without MSG_SYN (on that very same socket) will still continue.
+
+4.2.2 SCM_J1939_DEST_ADDR & SCM_J1939_DEST_NAME
+
+ Different received j1939 packets could have had different destionations:
+ - broadcast packet, i.e. no destination address
+ - destination address that matches the sockets local address
+ - destination address that matches _a_ local address on the system, and the
+ socket had no local address defined.
+ - SO_J1939_PROMISC was set
+
+ The destination address & destination name (if applicable) are attached
+ to the msghdr in the recvmsg(2) call. It can be extracted using cmsg(3) macros,
+ with cmsg_level == SOL_J1939 && cmsg_type == SCM_J1939_DEST_ADDR
+ or SCM_J1939_DEST_NAME. The returned data is a uint8_t/uint64_t.
+
+4.2.3 SCM_J1939_PRIORITY
+
+ Attached to the msghdr is also the packet's priority on the bus. This is a
+ uint8_t, packed as cmsg_type == SCM_J1939_PRIORITY.
+
+4.3 Dynamic Addressing
+
+ Distinction has to be made in and using the claimed address and doing an
+ address claim. To use an already claimed address, one has to fill in the
+ j1939.name member and provide it to bind(2). If the name had claimed an
+ address earlier, all further PGN's being sent will use that address. And the
+ j1939.addr member will be ignored.
+
+ An exception on this is pgn 0x0ee00. This is the "Address Claim/Cannot Claim
+ Address" message and when the kernel will use the j1939.addr member for that
+ pgn if necessary.
+
+ To claim an address, bind(2) with:
+ j1939.pgn set to 0x0ee00
+ j1939.addr set to the desired Source Address.
+ j1939.name set to the NAME you want the Source Address to claim to.
+
+ Afterwards do a write(2) with data set to the NAME (Little Endian). If the
+ NAME provided, does not match the j1939.name provided to bind(2), EPROTO
+ will be returned. One might use sendto(2) also to send the Addres Claim. In
+ that case, the j1939.addr member must be set to the broadcast address (255)
+ and the j1939.pgn must be set to 0x0ee00. If This combination is not given,
+ EPROTO is returned.
+
+ If no-one else contest the address claim within 250ms after transmission, the
+ kernel marks the NAME-SA assignment as valid. The valid assignment will be
+ kept, among other valid NAME-SA assignments. From that point, any socket
+ bound to the NAME can send packets.
+
+ If another ECU claims the address, the kernel will mark the NAME-SA expired.
+ No socket bound to the NAME can send packets (other than address claims).
+ To claim another address, some socket bound to NAME, must bind(2) again,
+ but with only j1939.addr changed to the new SA, and must then send a
+ valid address claim packet. This restarts the state machine in the kernel
+ (and any other participant on the bus) for this NAME.
+
+
+4.4 Send Examples
+
+4.4.1 Static addressing
+
+ This example will send a pgn (0x12300) from SA 0x20 to DA 0x30.
+
+ Add the address to the system:
+ $ ip addr add j1939 0x20 dev can0
+
+ Bind:
+ struct sockaddr_can addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.can_ifindex = ifindex("can0"); // ifindex is a substitute.
+ addr.can_addr.j1939.name = J1939_NO_NAME;
+ addr.can_addr.j1939.addr = 0x20;
+ addr.can_addr.j1939.pgn = J1939_NO_PGN;
+
+ bind(sk, (void *)&addr, sizeof(addr));
+
+ Now, the socket 'sk' is bound to the address 0x20. Since no pgn
+ was specified during bound, a pgn will be necessary during sendto() operations.
+ Alternatively, specifying addr.can_addr.j1939.pgn during bind() allows
+ for using send() & write(), since a default pgn (the pgn specified during bind())
+ can be used then.
+
+ Send:
+ struct sockaddr_can addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.can_addr.j1939.name = J1939_NO_NAME;
+ addr.can_addr.j1939.addr = 0x30;
+ addr.can_addr.j1939.pgn = 0x12300;
+ // addr.can_ifindex is not necessary here.
+
+ sendto(sk, data, sizeof(data), 0, (void *)&addr, sizeof(addr));
+
+4.4.2 Dynamic addressing
+
+ This example will send a pgn (0x12300) from 12345678 to 9ABCDEF
+
+ Add the name to the system:
+ $ ip addr add j1939 name 12345678 dev can0
+
+ Start an address claiming daemon (e.g. jacd)
+ $ jacd -r 0x20-0x30 12345678 can0 &
+
+ Bind:
+ struct sockaddr_can addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.can_ifindex = ifindex("can0"); // ifindex is a substitute.
+ addr.can_addr.j1939.name = 0x12345678ULL;
+ addr.can_addr.j1939.addr = J1939_NO_ADDR;
+ addr.can_addr.j1939.pgn = J1939_NO_PGN;
+
+ bind(sk, (void *)&addr, sizeof(addr));
+
+ Send:
+ struct sockaddr_can addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.can_addr.j1939.name = 0x9ABCDEFULL;
+ addr.can_addr.j1939. = J1939_NO_ADDR;
+ addr.can_addr.j1939.pgn = 0x12300;
+
+ sendto(sk, data, sizeof(data), 0, (void *)&addr, sizeof(addr));
+
+4.4.3 Mixed mode
+
+ A scenario that sends a packet from a static address to a dynamic address
+ or vice versa is called 'mixed mode' here.
+
+ Combining the setup of the static address with a sendto() to a dynamic
+ address from the above examples is legal, and implements such mixed mode
+ addressing. The same applies for the setup of the dynamic address combined
+ with the sendto() towards a dynamic address.
+
+
+5 Socket Options
+--------------------------------
+
+ j1939 sockets have some options that are configurable via setsockopt(2).
+ Each of those options is initialized with a reasonable default.
+
+
+5.1 SO_J1939_FILTER
+
+ As mentioned above, J1939 supports filtering in both NAME, Source Address
+ and PGN. All members must match.
+
+ struct j1939_filter filter = {
+ .name = ...
+ .name_mask = ...
+ .addr = ...
+ .addr_mask = ...
+ .pgn = ...
+ .pgn_mask = ...
+ }
+
+ setsockopt(s, SOL_CAN_J1939, SO_J1939_FILTER, &filter, sizeof(filter));
+
+
+5.2 SO_J1939_PROMISC
+
+ When set, j1939 will receive all packets, not just those with a destination
+ on the local system.
+ default off.
+
+ int promisc = 1; /* 0 = disabled (default), 1 = enabled */
+
+ setsockopt(s, SOL_CAN_J1939, SO_J1939_PROMISC, &promisc, sizeof(promisc));
+
+
+5.3 SO_J1939_RECV_OWN
+
+ All the sent j1939 packets are looped back in the system.
+ The reception of the j1939 packets on the same socket that was
+ sending the j1939 packet is assumed to be unwanted and therefore
+ disabled by default. This default behaviour may be changed on
+ demand:
+
+ int recv_own_msgs = 1; /* 0 = disabled (default), 1 = enabled */
+
+ setsockopt(s, SOL_CAN_J1939, SO_J1939_RECV_OWN,
+ &recv_own_msgs, sizeof(recv_own_msgs));
+
+
+5.4 SO_J1939_SEND_PRIO
+
+ To set the priority field for outgoing packets, the SO_J1939_SEND_PRIO can
+ be changed. This int field specifies the priority that will be used.
+ j1939 defines a priority between 0 and 7 inclusive,
+ with 7 the lowest priority.
+ Per default, the priority is set to 6 (conforming J1939).
+ This priority socket option operates on the same value that is modified
+ with
+
+ setsockopt(s, SOL_SOCKET, SO_PRIORITY, &pri, sizeof(pri))
+
+ socketoption, with a difference that SOL_SOCKET/SO_PRIORITY is defined with
+ 0 the lowest priority. SOL_CAN_J1939/SO_J1939_SEND_PRIO inverts this value
+ for you.
+
+
+6. /proc/net/can-j1939 Interface.
+--------------------------------
+
+ Files giving you a view on the in-kernel operation of J1939 are located at:
+ /proc/net/j1939.
+
+6.1 /proc/net/can-j1939/ecu
+
+ This file gives an overview of the known ECU's to the kernel.
+ - iface : network interface they operate on.
+ - SA : current address.
+ - name : 64bit NAME
+ - flags : 'L' = local, 'R' = remote
+
+6.2 /proc/net/can-j1939/filter
+
+6.3 /proc/net/can-j1939/sock
+
+ This file gives a list of all j1939 sockets currently open.
+ - iface : network interface
+ - flags :
+ 'b' : bound
+ 'c' : connected
+ 'P' : PROMISC
+ 'o' : RECV_OWN
+ 'd' : RECV_DEST
+ 'p' : RECV_PRIO
+ - local: [NAME],SA
+ - remote: [NAME]/MASK,DA
+ - pgn : PGN
+ - prio : priority
+ - pending : # packets pending (see MSG_SYN on 4.2.1)
+
+6.4 /proc/net/can-j1939/transport
+
+ This file shows a list of pending transport sessions
+ - iface
+ - src : XX (addr) or XXXXXXXXXXXXXXXX (name)
+ - dst : XX or XXXXXXXXXXXXXXXX or '-' (broadcast)
+ - pgn :
+ - done/total : current # transferred bytes / total
+
+
+7. /proc/sys/net/can-j1939 - SYSCTL
+--------------------------------
+
+ Via these sysctl files, some parameters of the j1939 module can be tuned.
+
+7.1 /proc/sys/net/can-j1939/transport_max_payload_in_bytes [int]
+
+ Is the maximum packet size to accept on both transmit & receive side.
+ Bigger packets will be rejected (local sender), aborted (local receiver)
+ or ignored (broadcasts & remote recievers in PROMISC).
+
+7.2 /proc/sys/net/can-j1939/transport_cts_nr_frames [int]
+
+ Controls the number of packets to allow between consecutive CTS frames
+ (default 255).
+ This number is communicated within the CTS frame from receiver to transmitter.
+ Setting this has effect on received transport sessions only.
+
+7.3 /proc/sys/net/can-j1939/transport_tx_retry_ms [int]
+
+ Controls how many time to wait before retrying to send an individual TP
+ flow or data packet after transmission failure (default 20).
+
+
+8. Credits
+--------------------------------
+
+ Kurt Van Dijck (j1939 core, transport protocol, API)
+ Pieter Beyens (j1939 core, address claiming)
+
diff --git a/MAINTAINERS b/MAINTAINERS
index b5266ad..ec7b016 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1648,6 +1648,14 @@ F: include/linux/can/error.h
F: include/linux/can/netlink.h
F: include/linux/can/platform/
+CAN-J1939 NETWORK LAYER
+M: Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
+L: socketcan-core-0fE9KPoRgkgATYTw5x5z8w@public.gmane.org
+L: netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
+S: Maintained
+F: net/can/j1939/
+F: include/linux/can/j1939.h
+
CELL BROADBAND ENGINE ARCHITECTURE
M: Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>
L: linuxppc-dev-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org
--
1.7.2.5
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v4 5/5] iproute2: add can-j1939 support
[not found] ` <20110427085330.GA757-ozGf4kBk5synFtIcQ8t7k3L8HoS0Hn3T@public.gmane.org>
` (3 preceding siblings ...)
2011-04-27 9:00 ` [PATCH v4 4/5] can-j1939: add documentation Kurt Van Dijck
@ 2011-04-27 9:03 ` Kurt Van Dijck
[not found] ` <20110427090302.GF757-ozGf4kBk5synFtIcQ8t7k3L8HoS0Hn3T@public.gmane.org>
4 siblings, 1 reply; 11+ messages in thread
From: Kurt Van Dijck @ 2011-04-27 9:03 UTC (permalink / raw)
To: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
netdev-u79uwXL29TY76Z2rM5mHXA
Add j1939 support to iproute2
Signed-off-by: Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
---
diff --git a/Makefile b/Makefile
index d1ace1f..06bf281 100644
--- a/Makefile
+++ b/Makefile
@@ -27,9 +27,12 @@ ADDLIB+=dnet_ntop.o dnet_pton.o
#options for ipx
ADDLIB+=ipx_ntop.o ipx_pton.o
+#options for j1939
+ADDLIB+=j1939.o
+
CC = gcc
HOSTCC = gcc
-CCOPTS = -D_GNU_SOURCE -O2 -Wstrict-prototypes -Wall
+CCOPTS = -D_GNU_SOURCE -Wstrict-prototypes -Wall
CFLAGS = $(CCOPTS) -I../include $(DEFINES)
YACCFLAGS = -d -t -v
diff --git a/include/linux/can.h b/include/linux/can.h
new file mode 100644
index 0000000..9c2523c
--- /dev/null
+++ b/include/linux/can.h
@@ -0,0 +1,129 @@
+/*
+ * linux/can.h
+ *
+ * Definitions for CAN network layer (socket addr / CAN frame / CAN filter)
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp-l29pVbxQd1IUtdQbppsyvg@public.gmane.org>
+ * Urs Thuermann <urs.thuermann-l29pVbxQd1IUtdQbppsyvg@public.gmane.org>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users-0fE9KPoRgkgATYTw5x5z8w@public.gmane.org>
+ *
+ */
+
+#ifndef CAN_H
+#define CAN_H
+
+#include <linux/types.h>
+#include <linux/socket.h>
+
+/* controller area network (CAN) kernel definitions */
+
+/* special address description flags for the CAN_ID */
+#define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */
+#define CAN_RTR_FLAG 0x40000000U /* remote transmission request */
+#define CAN_ERR_FLAG 0x20000000U /* error frame */
+
+/* valid bits in CAN ID for frame formats */
+#define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */
+#define CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */
+#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */
+
+/*
+ * Controller Area Network Identifier structure
+ *
+ * bit 0-28 : CAN identifier (11/29 bit)
+ * bit 29 : error frame flag (0 = data frame, 1 = error frame)
+ * bit 30 : remote transmission request flag (1 = rtr frame)
+ * bit 31 : frame format flag (0 = standard 11 bit, 1 = extended 29 bit)
+ */
+typedef __u32 canid_t;
+
+/*
+ * Controller Area Network Error Frame Mask structure
+ *
+ * bit 0-28 : error class mask (see include/linux/can/error.h)
+ * bit 29-31 : set to zero
+ */
+typedef __u32 can_err_mask_t;
+
+/**
+ * struct can_frame - basic CAN frame structure
+ * @can_id: the CAN ID of the frame and CAN_*_FLAG flags, see above.
+ * @can_dlc: the data length field of the CAN frame
+ * @data: the CAN frame payload.
+ */
+struct can_frame {
+ canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
+ __u8 can_dlc; /* data length code: 0 .. 8 */
+ __u8 data[8] __attribute__((aligned(8)));
+};
+
+/* particular protocols of the protocol family PF_CAN */
+#define CAN_RAW 1 /* RAW sockets */
+#define CAN_BCM 2 /* Broadcast Manager */
+#define CAN_TP16 3 /* VAG Transport Protocol v1.6 */
+#define CAN_TP20 4 /* VAG Transport Protocol v2.0 */
+#define CAN_MCNET 5 /* Bosch MCNet */
+#define CAN_ISOTP 6 /* ISO 15765-2 Transport Protocol */
+#define CAN_J1939 7 /* SAE J1939 */
+#define CAN_NPROTO 8
+
+#define SOL_CAN_BASE 100
+
+/**
+ * struct sockaddr_can - the sockaddr structure for CAN sockets
+ * @can_family: address family number AF_CAN.
+ * @can_ifindex: CAN network interface index.
+ * @can_addr: protocol specific address information
+ */
+struct sockaddr_can {
+ sa_family_t can_family;
+ int can_ifindex;
+ union {
+ /* transport protocol class address information (e.g. ISOTP) */
+ struct { canid_t rx_id, tx_id; } tp;
+
+ /* J1939 address information */
+ struct {
+ /* 8 byte name when using dynamic addressing */
+ __u64 name;
+ /*
+ * pgn:
+ * 8bit: PS in PDU2 case, else 0
+ * 8bit: PF
+ * 1bit: DP
+ * 1bit: reserved
+ */
+ __u32 pgn;
+
+ /* 1byte address */
+ __u8 addr;
+ } j1939;
+
+ /* reserved for future CAN protocols address information */
+ } can_addr;
+};
+
+/**
+ * struct can_filter - CAN ID based filter in can_register().
+ * @can_id: relevant bits of CAN ID which are not masked out.
+ * @can_mask: CAN mask (see description)
+ *
+ * Description:
+ * A filter matches, when
+ *
+ * <received_can_id> & mask == can_id & mask
+ *
+ * The filter can be inverted (CAN_INV_FILTER bit set in can_id) or it can
+ * filter for error frames (CAN_ERR_FLAG bit set in mask).
+ */
+struct can_filter {
+ canid_t can_id;
+ canid_t can_mask;
+};
+
+#define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */
+
+#endif /* CAN_H */
diff --git a/include/linux/can/j1939.h b/include/linux/can/j1939.h
new file mode 100644
index 0000000..fa62562
--- /dev/null
+++ b/include/linux/can/j1939.h
@@ -0,0 +1,93 @@
+/*
+ * j1939.h
+ *
+ */
+
+#ifndef _J1939_H_
+#define _J1939_H_
+
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/can.h>
+
+#define J1939_NO_ADDR 0xff
+#define J1939_NO_NAME 0
+#define J1939_NO_PGN 0x7ffff
+/*
+ * J1939 Parameter Group Number
+ *
+ * bit 0-7 : PDU Specific (PS)
+ * bit 8-15 : PDU Format (PF)
+ * bit 16 : Data Page (DP)
+ * bit 17 : Reserved (R)
+ * bit 19-31 : set to zero
+ */
+typedef __u32 pgn_t;
+
+/*
+ * J1939 Priority
+ *
+ * bit 0-2 : Priority (P)
+ * bit 3-7 : set to zero
+ */
+typedef __u8 priority_t;
+
+/*
+ * J1939 NAME
+ *
+ * bit 0-20 : Identity Number
+ * bit 21-31 : Manufacturer Code
+ * bit 32-34 : ECU Instance
+ * bit 35-39 : Function Instance
+ * bit 40-47 : Function
+ * bit 48 : Reserved
+ * bit 49-55 : Vehicle System
+ * bit 56-59 : Vehicle System Instance
+ * bit 60-62 : Industry Group
+ * bit 63 : Arbitrary Address Capable
+ */
+typedef __u64 name_t;
+
+/*
+ * J1939 socket options
+ */
+#define SOL_CAN_J1939 (SOL_CAN_BASE + CAN_J1939)
+enum {
+ SO_J1939_FILTER = 1, /* set filters */
+ SO_J1939_PROMISC = 2, /* set/clr promiscuous mode */
+ SO_J1939_RECV_OWN = 3,
+ SO_J1939_RECV_DEST = 4, /* set/clr attach dest control message */
+ SO_J1939_RECV_PRIO = 5,
+ SO_J1939_SEND_PRIO = 6,
+ SO_J1939_DEST_MASK = 7, /* mask names in connect() & sendto() */
+};
+
+#define SCM_J1939_DEST SO_J1939_RECV_DEST
+#define SCM_J1939_PRIO SO_J1939_RECV_PRIO
+
+struct j1939_filter {
+ name_t name;
+ name_t name_mask;
+ __u8 addr;
+ __u8 addr_mask;
+ pgn_t pgn;
+ pgn_t pgn_mask;
+};
+
+/*
+ * RTNETLINK
+ */
+enum {
+ IFLA_J1939_UNSPEC,
+ IFLA_J1939_ENABLE,
+ IFLA_J1939_MAX,
+};
+
+enum {
+ IFA_J1939_UNSPEC,
+ IFA_J1939_ADDR,
+ IFA_J1939_NAME,
+ IFA_J1939_MAX,
+};
+
+#endif /* _J1939_H_ */
diff --git a/include/utils.h b/include/utils.h
index 47f8e07..cbd249a 100644
--- a/include/utils.h
+++ b/include/utils.h
@@ -95,8 +95,15 @@ extern __u8* hexstring_a2n(const char *str, __u8 *buf, int blen);
extern const char *format_host(int af, int len, const void *addr,
char *buf, int buflen);
-extern const char *rt_addr_n2a(int af, int len, const void *addr,
+/* 'address with protocol' n2a */
+extern const char *rt_addrpr_n2a(int af, int protocol, int len, const void *addr,
char *buf, int buflen);
+static inline const char *rt_addr_n2a(int af, int len, const void *addr,
+ char *buf, int buflen)
+{
+ return rt_addrpr_n2a(af, 0, len, addr, buf, buflen);
+
+}
void missarg(const char *) __attribute__((noreturn));
void invarg(const char *, const char *) __attribute__((noreturn));
@@ -111,6 +118,16 @@ int dnet_pton(int af, const char *src, void *addr);
const char *ipx_ntop(int af, const void *addr, char *str, size_t len);
int ipx_pton(int af, const char *src, void *addr);
+/* j1939 */
+extern const char *j1939_ntop(int af, const void *addr, size_t vlen,
+ char *str, size_t len);
+extern const char *j1939_link_attrtop(struct rtattr *nla);
+
+extern int j1939_addr_args(int argc, char *argv[],
+ struct nlmsghdr *msg, int msg_size);
+extern int j1939_link_args(int argc, char *argv[],
+ struct nlmsghdr *msg, int msg_size);
+
extern int __iproute2_hz_internal;
extern int __get_hz(void);
diff --git a/ip/ip.c b/ip/ip.c
index b127d57..50fcb1c 100644
--- a/ip/ip.c
+++ b/ip/ip.c
@@ -46,7 +46,7 @@ static void usage(void)
"where OBJECT := { link | addr | addrlabel | route | rule | neigh | ntable |\n"
" tunnel | tuntap | maddr | mroute | mrule | monitor | xfrm }\n"
" OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |\n"
-" -f[amily] { inet | inet6 | ipx | dnet | link } |\n"
+" -f[amily] { inet | inet6 | ipx | dnet | link | can} |\n"
" -l[oops] { maximum-addr-flush-attempts } |\n"
" -o[neline] | -t[imestamp] | -b[atch] [filename] |\n"
" -rc[vbuf] [size]}\n");
@@ -181,6 +181,8 @@ int main(int argc, char **argv)
preferred_family = AF_PACKET;
else if (strcmp(argv[1], "ipx") == 0)
preferred_family = AF_IPX;
+ else if (strcmp(argv[1], "can") == 0)
+ preferred_family = AF_CAN;
else if (strcmp(argv[1], "help") == 0)
usage();
else
diff --git a/ip/ipaddress.c b/ip/ipaddress.c
index a1f78b9..d7ee83a 100644
--- a/ip/ipaddress.c
+++ b/ip/ipaddress.c
@@ -27,6 +27,7 @@
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/sockios.h>
+#include <linux/can.h>
#include "rt_names.h"
#include "utils.h"
@@ -69,6 +70,8 @@ static void usage(void)
fprintf(stderr, "IFADDR := PREFIX | ADDR peer PREFIX\n");
fprintf(stderr, " [ broadcast ADDR ] [ anycast ADDR ]\n");
fprintf(stderr, " [ label STRING ] [ scope SCOPE-ID ]\n");
+ fprintf(stderr, " | j1939 J1939IFADDR\n");
+ fprintf(stderr, " \n");
fprintf(stderr, "SCOPE-ID := [ host | link | global | NUMBER ]\n");
fprintf(stderr, "FLAG-LIST := [ FLAG-LIST ] FLAG\n");
fprintf(stderr, "FLAG := [ permanent | dynamic | secondary | primary |\n");
@@ -78,6 +81,10 @@ static void usage(void)
fprintf(stderr, "CONFFLAG := [ home | nodad ]\n");
fprintf(stderr, "LIFETIME := [ valid_lft LFT ] [ preferred_lft LFT ]\n");
fprintf(stderr, "LFT := forever | SECONDS\n");
+ fprintf(stderr, " \n");
+ fprintf(stderr, "J1939IFADDR := [SA] [ name NODENAME ]\n");
+ fprintf(stderr, "SA := U8\n");
+ fprintf(stderr, "NODENAME := U64\n");
exit(-1);
}
@@ -426,6 +433,19 @@ int print_linkinfo(const struct sockaddr_nl *who,
}
fprintf(fp, "\n");
+
+ if (do_link && tb[IFLA_AF_SPEC]) {
+ struct rtattr *af[AF_MAX];
+
+ parse_rtattr_nested(af, AF_MAX, tb[IFLA_AF_SPEC]);
+ if (af[AF_CAN]) {
+ struct rtattr *prot[CAN_NPROTO];
+
+ parse_rtattr_nested(prot, CAN_NPROTO, af[AF_CAN]);
+ if (prot[CAN_J1939])
+ fprintf(fp, " %s\n", j1939_link_attrtop(prot[CAN_J1939]));
+ }
+ }
fflush(fp);
return 0;
}
@@ -450,6 +470,13 @@ static int set_lifetime(unsigned int *lifetime, char *argv)
return 0;
}
+static const int af_use_prefix[AF_MAX] = {
+ [AF_INET] = 1,
+ [AF_INET6] = 1,
+ [AF_DECnet] = 1,
+ [AF_IPX] = 1,
+};
+
int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n,
void *arg)
{
@@ -457,6 +484,7 @@ int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n,
struct ifaddrmsg *ifa = NLMSG_DATA(n);
int len = n->nlmsg_len;
int deprecated = 0;
+ int protocol = 0;
/* Use local copy of ifa_flags to not interfere with filtering code */
unsigned int ifa_flags;
struct rtattr * rta_tb[IFA_MAX+1];
@@ -476,10 +504,12 @@ int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n,
parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
- if (!rta_tb[IFA_LOCAL])
- rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS];
- if (!rta_tb[IFA_ADDRESS])
- rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL];
+ if (af_use_prefix[ifa->ifa_family]) {
+ if (!rta_tb[IFA_LOCAL])
+ rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS];
+ if (!rta_tb[IFA_ADDRESS])
+ rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL];
+ }
if (filter.ifindex && filter.ifindex != ifa->ifa_index)
return 0;
@@ -541,38 +571,64 @@ int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n,
fprintf(fp, " dnet ");
else if (ifa->ifa_family == AF_IPX)
fprintf(fp, " ipx ");
+ else if (ifa->ifa_family == AF_CAN) {
+ /* ifa->ifa_prefixlen is abused for protocol number */
+ const char *sprotocol;
+ char num[16];
+
+ /* 1st: set protocol, as this is rather tricky */
+ protocol = ifa->ifa_prefixlen;
+
+ /* 2nd: set label */
+ switch (protocol) {
+ case CAN_J1939:
+ sprotocol = "j1939";
+ break;
+ default:
+ sprintf(num, "%i", ifa->ifa_prefixlen);
+ sprotocol = num;
+ break;
+ }
+ fprintf(fp, " can-%s ", sprotocol);
+ }
else
fprintf(fp, " family %d ", ifa->ifa_family);
if (rta_tb[IFA_LOCAL]) {
- fprintf(fp, "%s", rt_addr_n2a(ifa->ifa_family,
+ fprintf(fp, "%s", rt_addrpr_n2a(ifa->ifa_family, protocol,
RTA_PAYLOAD(rta_tb[IFA_LOCAL]),
RTA_DATA(rta_tb[IFA_LOCAL]),
abuf, sizeof(abuf)));
if (rta_tb[IFA_ADDRESS] == NULL ||
memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]), RTA_DATA(rta_tb[IFA_LOCAL]), 4) == 0) {
- fprintf(fp, "/%d ", ifa->ifa_prefixlen);
} else {
- fprintf(fp, " peer %s/%d ",
- rt_addr_n2a(ifa->ifa_family,
+ fprintf(fp, " peer %s",
+ rt_addrpr_n2a(ifa->ifa_family, protocol,
RTA_PAYLOAD(rta_tb[IFA_ADDRESS]),
RTA_DATA(rta_tb[IFA_ADDRESS]),
- abuf, sizeof(abuf)),
- ifa->ifa_prefixlen);
+ abuf, sizeof(abuf)));
}
+ if (af_use_prefix[ifa->ifa_family])
+ fprintf(fp, "/%d", ifa->ifa_prefixlen);
+ fprintf(fp, " ");
+ } else if (rta_tb[IFA_ADDRESS]) {
+ fprintf(fp, "peer %s ", rt_addrpr_n2a(ifa->ifa_family, protocol,
+ RTA_PAYLOAD(rta_tb[IFA_ADDRESS]),
+ RTA_DATA(rta_tb[IFA_ADDRESS]),
+ abuf, sizeof(abuf)));
}
if (rta_tb[IFA_BROADCAST]) {
fprintf(fp, "brd %s ",
- rt_addr_n2a(ifa->ifa_family,
+ rt_addrpr_n2a(ifa->ifa_family, protocol,
RTA_PAYLOAD(rta_tb[IFA_BROADCAST]),
RTA_DATA(rta_tb[IFA_BROADCAST]),
abuf, sizeof(abuf)));
}
if (rta_tb[IFA_ANYCAST]) {
fprintf(fp, "any %s ",
- rt_addr_n2a(ifa->ifa_family,
+ rt_addrpr_n2a(ifa->ifa_family, protocol,
RTA_PAYLOAD(rta_tb[IFA_ANYCAST]),
RTA_DATA(rta_tb[IFA_ANYCAST]),
abuf, sizeof(abuf)));
@@ -1103,12 +1159,18 @@ static int ipaddr_modify(int cmd, int flags, int argc, char **argv)
req.ifa.ifa_flags |= IFA_F_HOMEADDRESS;
} else if (strcmp(*argv, "nodad") == 0) {
req.ifa.ifa_flags |= IFA_F_NODAD;
+ } else if (matches(*argv, "j1939") == 0) {
+ int ret;
+
+ ret = j1939_addr_args(argc, argv, &req.n, sizeof(req));
+ if (ret < 0)
+ return ret;
+ argc -= ret;
+ argv += ret;
} else {
if (strcmp(*argv, "local") == 0) {
NEXT_ARG();
}
- if (matches(*argv, "help") == 0)
- usage();
if (local_len)
duparg2("local", *argv);
lcl_arg = *argv;
@@ -1214,8 +1276,9 @@ int do_ipaddr(int argc, char **argv)
return ipaddr_list_or_flush(argc-1, argv+1, 0);
if (matches(*argv, "flush") == 0)
return ipaddr_list_or_flush(argc-1, argv+1, 1);
- if (matches(*argv, "help") == 0)
+ if (matches(*argv, "help") == 0) {
usage();
+ }
fprintf(stderr, "Command \"%s\" is unknown, try \"ip addr help\".\n", *argv);
exit(-1);
}
diff --git a/ip/iplink.c b/ip/iplink.c
index 48c0254..cd8d906 100644
--- a/ip/iplink.c
+++ b/ip/iplink.c
@@ -27,6 +27,7 @@
#include <string.h>
#include <sys/ioctl.h>
#include <linux/sockios.h>
+#include <linux/can.h>
#include "rt_names.h"
#include "utils.h"
@@ -73,6 +74,7 @@ void iplink_usage(void)
fprintf(stderr, " [ rate TXRATE ] ] \n");
fprintf(stderr, " [ master DEVICE ]\n");
fprintf(stderr, " [ nomaster ]\n");
+ fprintf(stderr, " [ j1939 { on | off } ]\n");
fprintf(stderr, " ip link show [ DEVICE | group GROUP ]\n");
if (iplink_have_newlink()) {
@@ -402,6 +404,12 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req,
duparg("group", *argv);
if (rtnl_group_a2n(group, *argv))
invarg("Invalid \"group\" value\n", *argv);
+ } else if (matches(*argv, "j1939") == 0) {
+ ret = j1939_link_args(argc, argv, &req->n, sizeof(*req));
+ if (ret < 0)
+ return ret;
+ argc -= ret;
+ argv += ret;
} else {
if (strcmp(*argv, "dev") == 0) {
NEXT_ARG();
diff --git a/lib/j1939.c b/lib/j1939.c
new file mode 100644
index 0000000..157a8ce
--- /dev/null
+++ b/lib/j1939.c
@@ -0,0 +1,153 @@
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <endian.h>
+#include <linux/can/j1939.h>
+
+#include "utils.h"
+
+#ifndef htobe64
+# if __BYTE_ORDER == __LITTLE_ENDIAN
+# define htobe64(x) __bswap_64 (x)
+# define htole64(x) (x)
+# define be64toh(x) __bswap_64 (x)
+# define le64toh(x) (x)
+# else
+# define htobe64(x) (x)
+# define htole64(x) __bswap_64 (x)
+# define be64toh(x) (x)
+# define le64toh(x) __bswap_64 (x)
+# endif
+#endif
+/*
+ * print J1939 name
+ * for use from rt_addr_n2a
+ */
+const char *j1939_ntop(int af, const void *vaddr, size_t vlen,
+ char *str, size_t len)
+{
+ struct rtattr *tb[IFA_J1939_MAX];
+ int strdone = 0;
+
+ /* cast vaddr to non-const pointer */
+ parse_rtattr(tb, IFA_J1939_MAX-1, (void *)vaddr, vlen);
+ if (tb[IFA_J1939_ADDR]) {
+ strdone += sprintf(&str[strdone], "0x%02x",
+ *(uint8_t *)RTA_DATA(tb[IFA_J1939_ADDR]));
+ if (tb[IFA_J1939_NAME])
+ str[strdone++] = ' ';
+ }
+ if (tb[IFA_J1939_NAME])
+ strdone += sprintf(&str[strdone], "name %016llx",
+ (unsigned long long)be64toh(*(uint64_t *)RTA_DATA(tb[IFA_J1939_NAME])));
+ errno = 0;
+ return str;
+}
+
+/*
+ * fill an ifaddr message from program arguments
+ */
+int j1939_addr_args(int argc, char *argv[], struct nlmsghdr *msg, int msg_size)
+{
+ int saved_argc = argc;
+ struct ifaddrmsg *ifa = (void *)&msg[1];
+ struct rtattr *local;
+
+ if (ifa->ifa_family == AF_UNSPEC)
+ ifa->ifa_family = AF_CAN;
+ else {
+ fprintf(stderr, "j1939 only allowed for AF_CAN\n");
+ return -1;
+ }
+ if (!ifa->ifa_prefixlen)
+ ifa->ifa_prefixlen = CAN_J1939;
+ else {
+ fprintf(stderr, "CAN protocol %i already specified",
+ ifa->ifa_prefixlen);
+ return -1;
+ }
+ NEXT_ARG();
+ /* j1939 SA & NAME never need to be specified together */
+ if (matches(*argv, "name") == 0) {
+ uint64_t name;
+
+ NEXT_ARG();
+ name = htobe64(strtoull(*argv, 0, 16));
+ if (!name) {
+ fprintf(stderr, "0 name is not valid\n");
+ return -1;
+ }
+ local = addattr_nest(msg, msg_size, IFA_LOCAL);
+ addattr_l(msg, msg_size, IFA_J1939_NAME, &name, sizeof(name));
+ addattr_nest_end(msg, local);
+ } else {
+ unsigned int laddr;
+ uint8_t addr;
+
+ addr = laddr = strtoul(*argv, 0, 0);
+ if (laddr >= 0xfe) {
+ fprintf(stderr, "address '%s' not valid\n", *argv);
+ return -1;
+ }
+ local = addattr_nest(msg, msg_size, IFA_LOCAL);
+ addattr_l(msg, msg_size, IFA_J1939_ADDR, &addr, sizeof(addr));
+ addattr_nest_end(msg, local);
+ }
+
+ return saved_argc - argc;
+}
+
+/*
+ * fill an link_af message from program arguments
+ */
+int j1939_link_args(int argc, char *argv[], struct nlmsghdr *msg, int msg_size)
+{
+ int saved_argc = argc;
+ struct rtattr *afspec, *can, *j1939;
+ uint8_t enable;
+
+ NEXT_ARG();
+ if (strcmp(*argv, "on") == 0) {
+ enable = 1;
+ } else if (strcmp(*argv, "off") == 0) {
+ enable = 0;
+ } else {
+ enable = 1;
+ /* revert arguments */
+ ++argc;
+ --argv;
+ }
+
+ afspec = addattr_nest(msg, msg_size, IFLA_AF_SPEC);
+ can = addattr_nest(msg, msg_size, AF_CAN);
+ j1939 = addattr_nest(msg, msg_size, CAN_J1939);
+ addattr_l(msg, msg_size, IFLA_J1939_ENABLE, &enable, sizeof(enable));
+ addattr_nest_end(msg, j1939);
+ addattr_nest_end(msg, can);
+ addattr_nest_end(msg, afspec);
+
+ return saved_argc - argc;
+}
+
+/*
+ * process the returned IFLA_AF_SPEC/AF_CAN/CAN_J1939 attribute
+ */
+const char *j1939_link_attrtop(struct rtattr *nla)
+{
+ static char str[32];
+ int pos;
+ struct rtattr *tb[IFLA_J1939_MAX];
+
+ pos = 0;
+ str[0] = 0;
+ parse_rtattr_nested(tb, IFLA_J1939_MAX-1, nla);
+ if (tb[IFLA_J1939_ENABLE]) {
+ uint8_t *u8ptr;
+
+ u8ptr = RTA_DATA(tb[IFLA_J1939_ENABLE]);
+ pos += sprintf(&str[pos], "j1939 %s", *u8ptr ? "on" : "off");
+ }
+ return str;
+}
+
diff --git a/lib/utils.c b/lib/utils.c
index 1b42222..570e43f 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -25,6 +25,7 @@
#include <linux/pkt_sched.h>
#include <time.h>
#include <sys/time.h>
+#include <linux/can.h>
#include "utils.h"
@@ -499,7 +500,8 @@ int __get_user_hz(void)
return sysconf(_SC_CLK_TCK);
}
-const char *rt_addr_n2a(int af, int len, const void *addr, char *buf, int buflen)
+const char *rt_addrpr_n2a(int af, int protocol, int len, const void *addr,
+ char *buf, int buflen)
{
switch (af) {
case AF_INET:
@@ -513,6 +515,11 @@ const char *rt_addr_n2a(int af, int len, const void *addr, char *buf, int buflen
memcpy(dna.a_addr, addr, 2);
return dnet_ntop(af, &dna, buf, buflen);
}
+ case AF_CAN:
+ switch (protocol) {
+ case CAN_J1939:
+ return j1939_ntop(af, addr, len, buf, buflen);
+ }
default:
return "???";
}
diff --git a/man/man8/ip.8 b/man/man8/ip.8
index c5248ef..eab2fd4 100644
--- a/man/man8/ip.8
+++ b/man/man8/ip.8
@@ -23,7 +23,7 @@ ip \- show / manipulate routing, devices, policy routing and tunnels
\fB\-s\fR[\fItatistics\fR] |
\fB\-r\fR[\fIesolve\fR] |
\fB\-f\fR[\fIamily\fR] {
-.BR inet " | " inet6 " | " ipx " | " dnet " | " link " } | "
+.BR inet " | " inet6 " | " ipx " | " dnet " | " link " | " can " } | "
\fB\-o\fR[\fIneline\fR] }
.ti -8
@@ -103,6 +103,8 @@ ip \- show / manipulate routing, devices, policy routing and tunnels
.IR DEVICE
.br
.B nomaster
+.br
+.BR j1939 " { " on " | " off " }"
.ti -8
.B ip link show
@@ -135,7 +137,9 @@ ip \- show / manipulate routing, devices, policy routing and tunnels
.B label
.IR STRING " ] [ "
.B scope
-.IR SCOPE-ID " ]"
+.IR SCOPE-ID " ] | "
+.B j1939
+.IR IFADDRJ1939 " ] "
.ti -8
.IR SCOPE-ID " := "
@@ -151,6 +155,15 @@ ip \- show / manipulate routing, devices, policy routing and tunnels
tentative " | " deprecated " | " dadfailed " | " temporary " ]"
.ti -8
+.IR J1939IFADDR " := [ " SA " ] [ "
+.B name
+.IR " NODENAME " ]
+.br
+.IR SA " := " U8
+.br
+.IR NODENAME " := " U64
+
+.ti -8
.BR "ip addrlabel" " { " add " | " del " } " prefix
.BR PREFIX " [ "
.B dev
@@ -1067,6 +1080,9 @@ set master device of the device (enslave device).
.TP
.BI nomaster
unset master device of the device (release device).
+.BR "j1939 on " or "j1939 off"
+Enable or disable SAE J1939 on the device. This will only
+work when the device is a CAN device.
.PP
.B Warning:
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v4 2/5] can: add rtnetlink support
2011-04-27 8:57 ` [PATCH v4 2/5] can: add rtnetlink support Kurt Van Dijck
@ 2011-05-01 17:16 ` Oliver Hartkopp
[not found] ` <4DBD9586.7080908-fJ+pQTUTwRTk1uMJSBkQmQ@public.gmane.org>
0 siblings, 1 reply; 11+ messages in thread
From: Oliver Hartkopp @ 2011-05-01 17:16 UTC (permalink / raw)
To: Kurt Van Dijck; +Cc: socketcan-core, netdev
On 27.04.2011 10:57, Kurt Van Dijck wrote:
> This patch adds rtnetlink support for AF_CAN. This support is really
> a multiplexer towards the different CAN protocols.
Hello Kurt,
i applied all your patches for the can-utils and the network layer stuff to
the BerliOS SVN in socketcan/branches/j1939 for testing.
Besides some minor adaptions to support the SVN i left out some infrastructure
changes you made in patch 2/5 to constify this ...
> -static struct can_proto *proto_tab[CAN_NPROTO] __read_mostly;
> +static const struct can_proto *proto_tab[CAN_NPROTO] __read_mostly;
and this ...
> -static struct can_proto *can_try_module_get(int protocol)
> +static const struct can_proto *can_try_module_get(int protocol)
(..)
> +static inline void can_put_proto(const struct can_proto *cp)
> +{
> + module_put(cp->prot->owner);
> +}
These infrastructure changes did not really belong to the rtnetlink support.
The can-j1939.ko builds properly in the SVN against 2.6.39-rc5 and the
net-next-2.6 tree - and e.g. against a debian wheezy kernel:
make KERNELDIR=/usr/src/linux-headers-2.6.38-2-686-bigmem
As it is pretty easy now to build the kernel modules & your provided can-utils
we hopefully get some more feedback from j1939 users.
Best regards,
Oliver
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v4 2/5] can: add rtnetlink support
[not found] ` <4DBD9586.7080908-fJ+pQTUTwRTk1uMJSBkQmQ@public.gmane.org>
@ 2011-05-02 7:36 ` Kurt Van Dijck
2011-05-04 4:20 ` Kurt Van Dijck
1 sibling, 0 replies; 11+ messages in thread
From: Kurt Van Dijck @ 2011-05-02 7:36 UTC (permalink / raw)
To: Oliver Hartkopp
Cc: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
netdev-u79uwXL29TY76Z2rM5mHXA
On Sun, May 01, 2011 at 07:16:54PM +0200, Oliver Hartkopp wrote:
> On 27.04.2011 10:57, Kurt Van Dijck wrote:
> Hello Kurt,
>
> i applied all your patches for the can-utils and the network layer stuff to
> the BerliOS SVN in socketcan/branches/j1939 for testing.
A branch looks like a good alternative here.
Thanks.
>
>
[...]
> As it is pretty easy now to build the kernel modules & your provided can-utils
> we hopefully get some more feedback from j1939 users.
Ok. I'm curious...
>
> Best regards,
> Oliver
Kurt
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v4 5/5] iproute2: add can-j1939 support
[not found] ` <20110427090302.GF757-ozGf4kBk5synFtIcQ8t7k3L8HoS0Hn3T@public.gmane.org>
@ 2011-05-02 13:21 ` Marc Kleine-Budde
[not found] ` <4DBEAFC6.7010308-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
0 siblings, 1 reply; 11+ messages in thread
From: Marc Kleine-Budde @ 2011-05-02 13:21 UTC (permalink / raw)
To: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
netdev-u79uwXL29TY76Z2rM5mHXA
[-- Attachment #1.1: Type: text/plain, Size: 1000 bytes --]
On 04/27/2011 11:03 AM, Kurt Van Dijck wrote:
> Add j1939 support to iproute2
>
> Signed-off-by: Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
> ---
> diff --git a/Makefile b/Makefile
> index d1ace1f..06bf281 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -27,9 +27,12 @@ ADDLIB+=dnet_ntop.o dnet_pton.o
> #options for ipx
> ADDLIB+=ipx_ntop.o ipx_pton.o
>
> +#options for j1939
> +ADDLIB+=j1939.o
> +
> CC = gcc
> HOSTCC = gcc
> -CCOPTS = -D_GNU_SOURCE -O2 -Wstrict-prototypes -Wall
^^^^
why do you remove -O2?
> +CCOPTS = -D_GNU_SOURCE -Wstrict-prototypes -Wall
> CFLAGS = $(CCOPTS) -I../include $(DEFINES)
> YACCFLAGS = -d -t -v
cheers, Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Industrial Linux Solutions | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
[-- Attachment #1.2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 262 bytes --]
[-- Attachment #2: Type: text/plain, Size: 188 bytes --]
_______________________________________________
Socketcan-core mailing list
Socketcan-core-0fE9KPoRgkgATYTw5x5z8w@public.gmane.org
https://lists.berlios.de/mailman/listinfo/socketcan-core
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v4 5/5] iproute2: add can-j1939 support
[not found] ` <4DBEAFC6.7010308-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
@ 2011-05-02 13:36 ` Kurt Van Dijck
0 siblings, 0 replies; 11+ messages in thread
From: Kurt Van Dijck @ 2011-05-02 13:36 UTC (permalink / raw)
To: Marc Kleine-Budde
Cc: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
netdev-u79uwXL29TY76Z2rM5mHXA
On Mon, May 02, 2011 at 03:21:10PM +0200, Marc Kleine-Budde wrote:
> On 04/27/2011 11:03 AM, Kurt Van Dijck wrote:
> > Add j1939 support to iproute2
> >
> > Signed-off-by: Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
> > -CCOPTS = -D_GNU_SOURCE -O2 -Wstrict-prototypes -Wall
> ^^^^
>
> why do you remove -O2?
I did -O0 for debugging, but must have forgotten to properly restore.
I need to fix this for a definite version.
Thanks,
Kurt
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v4 2/5] can: add rtnetlink support
[not found] ` <4DBD9586.7080908-fJ+pQTUTwRTk1uMJSBkQmQ@public.gmane.org>
2011-05-02 7:36 ` Kurt Van Dijck
@ 2011-05-04 4:20 ` Kurt Van Dijck
1 sibling, 0 replies; 11+ messages in thread
From: Kurt Van Dijck @ 2011-05-04 4:20 UTC (permalink / raw)
To: Oliver Hartkopp
Cc: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
netdev-u79uwXL29TY76Z2rM5mHXA
> Besides some minor adaptions to support the SVN i left out some infrastructure
> changes you made in patch 2/5 to constify this ...
>
> > -static struct can_proto *proto_tab[CAN_NPROTO] __read_mostly;
> > +static const struct can_proto *proto_tab[CAN_NPROTO] __read_mostly;
>
> and this ...
>
> > -static struct can_proto *can_try_module_get(int protocol)
> > +static const struct can_proto *can_try_module_get(int protocol)
>
> (..)
>
> > +static inline void can_put_proto(const struct can_proto *cp)
> > +{
> > + module_put(cp->prot->owner);
> > +}
>
> These infrastructure changes did not really belong to the rtnetlink support.
I see. I see I've made a mistake in the patch set here.
I'll go for a seperate 'make it const' patch.
Thanks,
Kurt
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2011-05-04 4:20 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-04-27 8:53 [PATCH v4 0/5] CAN: add SAE J1939 protocol Kurt Van Dijck
[not found] ` <20110427085330.GA757-ozGf4kBk5synFtIcQ8t7k3L8HoS0Hn3T@public.gmane.org>
2011-04-27 8:55 ` [PATCH v4 1/5] can: extend sockaddr_can to include j1939 members Kurt Van Dijck
2011-04-27 8:57 ` [PATCH v4 2/5] can: add rtnetlink support Kurt Van Dijck
2011-05-01 17:16 ` Oliver Hartkopp
[not found] ` <4DBD9586.7080908-fJ+pQTUTwRTk1uMJSBkQmQ@public.gmane.org>
2011-05-02 7:36 ` Kurt Van Dijck
2011-05-04 4:20 ` Kurt Van Dijck
2011-04-27 8:58 ` [PATCH v4 3/5] can-j1939: Import SAE J1939 stack Kurt Van Dijck
2011-04-27 9:00 ` [PATCH v4 4/5] can-j1939: add documentation Kurt Van Dijck
2011-04-27 9:03 ` [PATCH v4 5/5] iproute2: add can-j1939 support Kurt Van Dijck
[not found] ` <20110427090302.GF757-ozGf4kBk5synFtIcQ8t7k3L8HoS0Hn3T@public.gmane.org>
2011-05-02 13:21 ` Marc Kleine-Budde
[not found] ` <4DBEAFC6.7010308-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
2011-05-02 13:36 ` Kurt Van Dijck
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).