netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [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).