Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH] macb: detect IP version to determin if we are on at91 or avr32
From: Jamie Iles @ 2011-03-14 10:39 UTC (permalink / raw)
  To: Jean-Christophe PLAGNIOL-VILLARD
  Cc: linux-arm-kernel, netdev, linux-kernel, Hans-Christian Egtvedt,
	Nicolas Ferre, Jamie Iles
In-Reply-To: <1299863585-17263-1-git-send-email-plagnioj@jcrosoft.com>

Hi,

I agree with Russell's comments about providing a fake hclk to remove 
the conditional clock stuff.  I was planning on posting the patch below 
in my next spin of the gem support patches which also renames macb_clk 
to pclk to be consistent with avr32 and enables us to remove all of the 
conditional clock stuff.

On Fri, Mar 11, 2011 at 06:13:05PM +0100, Jean-Christophe PLAGNIOL-VILLARD wrote:
> this will make macb soc generic and will allow to use it on other arch
> 
> Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
> Cc: Hans-Christian Egtvedt <hans-christian.egtvedt@atmel.com>
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Jamie Iles <jamie@jamieiles.com>
> ---
[...]
> diff --git a/drivers/net/macb.h b/drivers/net/macb.h
> index d3212f6..56a4fcb 100644
> --- a/drivers/net/macb.h
> +++ b/drivers/net/macb.h
> @@ -59,6 +59,7 @@
>  #define MACB_TPQ				0x00bc
>  #define MACB_USRIO				0x00c0
>  #define MACB_WOL				0x00c4
> +#define MACB_VERSION				0x00fc
>  
>  /* Bitfields in NCR */
>  #define MACB_LB_OFFSET				0
> @@ -389,6 +390,14 @@ struct macb {
>  	unsigned int 		link;
>  	unsigned int 		speed;
>  	unsigned int 		duplex;
> +
> +	uint32_t		version;

Minor nitpick, this should be u32 to be consistent with the rest of the 
fields.

>  };
>  
> +#define MACB_VERSION_MASK	0xffff0000
> +#define macb_is_at91(bp)	\
> +	(((bp)->version & MACB_VERSION_MASK) == 0x06010000)
> +#define macb_is_avr32(bp)	\
> +	(((bp)->version & MACB_VERSION_MASK) == 0x00010000)
> +
>  #endif /* _MACB_H */

I can't convince myself that this is safe and correct.  From the Cadence 
GEM spec:

31:16 Module identification number - for the GEM, this
      value is fixed at 0x0002.
15:0 Module revision - fixed byte value specific to the
     revision of the design which is incremented after
     each release of the IP.

I don't have access to the MACB spec, but it seems that 31:16 are 
defined by Cadence so there could well be other chips with a module ID 
of 0x0001 that aren't avr32.

We can certainly use this to tell us if we are GEM/MACB but if we need 
to know if we are AT91/AVR32 for the RMII/MII USRIO stuff, then couldn't 
we just keep the #ifdef CONFIG_ARCH_AT91 and factor that out into a 
macb_set_usrio() type method?

Jamie

8<---------

diff --git a/arch/arm/mach-at91/at572d940hf.c b/arch/arm/mach-at91/at572d940hf.c
index a6b9c68..9b3a37e 100644
--- a/arch/arm/mach-at91/at572d940hf.c
+++ b/arch/arm/mach-at91/at572d940hf.c
@@ -71,10 +71,15 @@ static struct clk pioC_clk = {
 	.type		= CLK_TYPE_PERIPHERAL,
 };
 static struct clk macb_clk = {
-	.name		= "macb_clk",
+	.name		= "pclk",
 	.pmc_mask	= 1 << AT572D940HF_ID_EMAC,
 	.type		= CLK_TYPE_PERIPHERAL,
 };
+static struct clk macb_hclk = {
+	.name		= "hclk",
+	.pmc_mask	= 0,
+	.type		= CLK_TYPE_PERIPHERAL,
+};
 static struct clk usart0_clk = {
 	.name		= "usart0_clk",
 	.pmc_mask	= 1 << AT572D940HF_ID_US0,
@@ -182,6 +187,7 @@ static struct clk *periph_clocks[] __initdata = {
 	&pioB_clk,
 	&pioC_clk,
 	&macb_clk,
+	&macb_hclk,
 	&usart0_clk,
 	&usart1_clk,
 	&usart2_clk,
diff --git a/arch/arm/mach-at91/at91cap9.c b/arch/arm/mach-at91/at91cap9.c
index 7337617..0d38ce7 100644
--- a/arch/arm/mach-at91/at91cap9.c
+++ b/arch/arm/mach-at91/at91cap9.c
@@ -150,10 +150,15 @@ static struct clk pwm_clk = {
 	.type		= CLK_TYPE_PERIPHERAL,
 };
 static struct clk macb_clk = {
-	.name		= "macb_clk",
+	.name		= "pclk",
 	.pmc_mask	= 1 << AT91CAP9_ID_EMAC,
 	.type		= CLK_TYPE_PERIPHERAL,
 };
+static struct clk macb_hclk = {
+	.name		= "hclk",
+	.pmc_mask	= 0,
+	.type		= CLK_TYPE_PERIPHERAL,
+};
 static struct clk aestdes_clk = {
 	.name		= "aestdes_clk",
 	.pmc_mask	= 1 << AT91CAP9_ID_AESTDES,
@@ -212,6 +217,7 @@ static struct clk *periph_clocks[] __initdata = {
 	&tcb_clk,
 	&pwm_clk,
 	&macb_clk,
+	&macb_hclk,
 	&aestdes_clk,
 	&adc_clk,
 	&isi_clk,
diff --git a/arch/arm/mach-at91/at91sam9260.c b/arch/arm/mach-at91/at91sam9260.c
index 195208b..f00774c 100644
--- a/arch/arm/mach-at91/at91sam9260.c
+++ b/arch/arm/mach-at91/at91sam9260.c
@@ -162,10 +162,15 @@ static struct clk ohci_clk = {
 	.type		= CLK_TYPE_PERIPHERAL,
 };
 static struct clk macb_clk = {
-	.name		= "macb_clk",
+	.name		= "pclk",
 	.pmc_mask	= 1 << AT91SAM9260_ID_EMAC,
 	.type		= CLK_TYPE_PERIPHERAL,
 };
+static struct clk macb_hclk = {
+	.name		= "hclk",
+	.pmc_mask	= 0,
+	.type		= CLK_TYPE_PERIPHERAL,
+};
 static struct clk isi_clk = {
 	.name		= "isi_clk",
 	.pmc_mask	= 1 << AT91SAM9260_ID_ISI,
@@ -221,6 +226,7 @@ static struct clk *periph_clocks[] __initdata = {
 	&tc2_clk,
 	&ohci_clk,
 	&macb_clk,
+	&macb_hclk,
 	&isi_clk,
 	&usart3_clk,
 	&usart4_clk,
diff --git a/arch/arm/mach-at91/at91sam9263.c b/arch/arm/mach-at91/at91sam9263.c
index 249f900..25cbae1 100644
--- a/arch/arm/mach-at91/at91sam9263.c
+++ b/arch/arm/mach-at91/at91sam9263.c
@@ -136,10 +136,15 @@ static struct clk pwm_clk = {
 	.type		= CLK_TYPE_PERIPHERAL,
 };
 static struct clk macb_clk = {
-	.name		= "macb_clk",
+	.name		= "pclk",
 	.pmc_mask	= 1 << AT91SAM9263_ID_EMAC,
 	.type		= CLK_TYPE_PERIPHERAL,
 };
+static struct clk macb_hclk = {
+	.name		= "hclk",
+	.pmc_mask	= 0,
+	.type		= CLK_TYPE_PERIPHERAL,
+};
 static struct clk dma_clk = {
 	.name		= "dma_clk",
 	.pmc_mask	= 1 << AT91SAM9263_ID_DMA,
@@ -190,6 +195,7 @@ static struct clk *periph_clocks[] __initdata = {
 	&tcb_clk,
 	&pwm_clk,
 	&macb_clk,
+	&macb_hclk,
 	&twodge_clk,
 	&udc_clk,
 	&isi_clk,
diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c
index c67b47f..a4d4a2d 100644
--- a/arch/arm/mach-at91/at91sam9g45.c
+++ b/arch/arm/mach-at91/at91sam9g45.c
@@ -157,10 +157,15 @@ static struct clk ac97_clk = {
 	.type		= CLK_TYPE_PERIPHERAL,
 };
 static struct clk macb_clk = {
-	.name		= "macb_clk",
+	.name		= "pclk",
 	.pmc_mask	= 1 << AT91SAM9G45_ID_EMAC,
 	.type		= CLK_TYPE_PERIPHERAL,
 };
+static struct clk macb_hclk = {
+	.name		= "hclk",
+	.pmc_mask	= 0,
+	.type		= CLK_TYPE_PERIPHERAL,
+};
 static struct clk isi_clk = {
 	.name		= "isi_clk",
 	.pmc_mask	= 1 << AT91SAM9G45_ID_ISI,
@@ -224,6 +229,7 @@ static struct clk *periph_clocks[] __initdata = {
 	&lcdc_clk,
 	&ac97_clk,
 	&macb_clk,
+	&macb_hclk,
 	&isi_clk,
 	&udphs_clk,
 	&mmc1_clk,
-- 
1.7.4


^ permalink raw reply related

* Re: possible issue between bridge igmp/multicast  handling & bnx2x on kernel 2.6.34 and >
From: Yann Dupont @ 2011-03-14 10:40 UTC (permalink / raw)
  To: Yann Dupont; +Cc: Eric Dumazet, netdev
In-Reply-To: <4D495C39.6020002@univ-nantes.fr>

Le 02/02/2011 14:29, Yann Dupont a écrit :
>> Le vendredi 07 janvier 2011 à 11:40 +0100, Yann Dupont a écrit :
>>> Le 04/01/2011 14:40, Yann Dupont a écrit :
>>> ...
>>>> We just added BCM57711 10G cards (bnx2x driver) on our blade servers
>>>> (connected to 10G Power Connect M8024).
>>>> Since then, we are experiencing random lost of packets.
>>>>
>>>> Symptom : packets are lost on some vlans for a few seconds, then
>>>> things go back to normal (and stops again a few minutes later)
>>>>
>>> As I didn't had answer so far , I digged a little more and captured 
>>> more
>>> packets.
>>> I just noticed that an event trigger that problem : IPv6 neighbor
>>> discovery packet .
>>>
>>> This is , of course, a multicast packet.
>>>
>>> Just saw that 2.6.36.3 should include this fix :
>>>
> Just a little update, the problem doesn't seem to be what we thought 
> at first.
>
> It may not be related to the bnx2x driver after all.
> We noticed that we had the same symptoms on target machine using bnx2 
> drivers  (we missed that at first since the outages are way briefer).
>
> We're now rather suspecting our own firewall (also a linux in a kvm 
> machine) since without it we don't get any more problem and the packet 
> drops occurs on _THIS_ network, when packets are routed by _THIS_ 
> firewall.
>
> Anyway, all of that is very puzzling, we have made a lot of network 
> dumps and we have really no clue of what's happening there.
> We don't understand why, if the problem is really on our firewall 
> machine, setting CONFIG_BRIDGE_IGMP_SNOOPING to 'n' on the target 
> machine efficiently fix the problem, Especially since it doesn't seem 
> related at all with our setup and we don't see anything in our network 
> dumps that could explain this.
>
> It's probably not a single problem, but a sum of different problems.
> We continue to search.
> Sorry for the noise.
>
> Regards,
>
One of my collegue noticied that :

https://lists.linux-foundation.org/pipermail/bridge/2010-October/007362.html

Exact same problem.
In fact, the problem **really** seems to be on the network switch.

Our servers are DELL M605 on a DELL M1000e chassis, with powerconnect 
M6220 (G) and  M8024 (10G)

IGMP snooping is also activated on those switches. If we turn igmp 
snooping off on M8024 for exemple, we don't have problems anymore, that 
is, we can activate IGMP snooping ON the linux bridge without loosing 
packets.

M6220 & M8024 seems concerned. Time to make a bug report (again and 
again...if you ask me, I can tell you they are crap.)

Sorry for the noise,

-- 
Yann Dupont - Service IRTS, DSI Université de Nantes
Tel : 02.53.48.49.20 - Mail/Jabber : Yann.Dupont@univ-nantes.fr


^ permalink raw reply

* Re: kernel 2.6.37 : oops in cleanup_once
From: Yann Dupont @ 2011-03-14 10:44 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: linux-kernel, netdev
In-Reply-To: <1296659336.20445.22.camel@edumazet-laptop>

Le 02/02/2011 16:08, Eric Dumazet a écrit :


> I suspect a mem corruption from another layer (not inetpeer)
>
> Unfortunately many kmem caches share the "64 bytes" cache.
>
> Could you please add "slub_nomerge" on your boot command ?
>
...

>
>> -Is there a very severe impact on performance ?
>>
> not at all
>
Maybe there is an impact after all : since then, we don't have problems 
anymore !

linkwood:~# uptime
  11:42:03 up 39 days, 17:08,  3 users,  load average: 0.01, 0.03, 0.05

So... could slub_nomerge hide or simply avoid the problem ?
Or are we just lucky this time ?


-- 
Yann Dupont - Service IRTS, DSI Université de Nantes
Tel : 02.53.48.49.20 - Mail/Jabber : Yann.Dupont@univ-nantes.fr


^ permalink raw reply

* [PATCH net-2.6] bnx2x: fix swap of rx-ticks and tx-ticks parameters in interrupt coalescing flow
From: Ariel Elior @ 2011-03-14 11:07 UTC (permalink / raw)
  To: David Miller; +Cc: Eilon Greenstein, netdev

Signed-off-by: Ariel Elior <ariele@broadcom.com>
Signed-off-by: Eilon Greenstein <eilong@broadcom.com>
---
 drivers/net/bnx2x/bnx2x_main.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/net/bnx2x/bnx2x_main.c b/drivers/net/bnx2x/bnx2x_main.c
index aa03233..10bc0a6 100644
--- a/drivers/net/bnx2x/bnx2x_main.c
+++ b/drivers/net/bnx2x/bnx2x_main.c
@@ -4208,7 +4208,7 @@ void bnx2x_update_coalesce(struct bnx2x *bp)
 
 	for_each_eth_queue(bp, i)
 		bnx2x_update_coalesce_sb(bp, bp->fp[i].fw_sb_id,
-					 bp->rx_ticks, bp->tx_ticks);
+					 bp->tx_ticks, bp->rx_ticks);
 }
 
 static void bnx2x_init_sp_ring(struct bnx2x *bp)
-- 
1.7.4.1





^ permalink raw reply related

* Re: dccp test-tree [RFC] [Patch 1/1] dccp: Only activate NN values after receiving the Confirm option
From: Gerrit Renker @ 2011-03-14 11:55 UTC (permalink / raw)
  To: Samuel Jero; +Cc: dccp, netdev
In-Reply-To: <1300048497.31664.158.camel@jero-laptop>

[-- Attachment #1: Type: text/plain, Size: 2999 bytes --]

Thank you for the comments, there is also some update (attached).


| > Well done, this looks good. I did some minor editing:
| >  * whitespace/formatting/comments,
| >  * simplification/subsumption,
| >  * function should not be called for non-NN or non-known
| >    feature, hence turned that into a DCCP_BUG() condition.
| 
| Okay
| 
I revised this some more, the function remains the same, but it is shorter 
http://eden-feed.erg.abdn.ac.uk/cgi-bin/gitweb.cgi?p=dccp_exp.git;a=commitdiff;h=94da7d70003e8185fe189146f336db72c8fa1f0b

(If there is disagreement regarding allowing a get_nn_ function to query non-nn
 features, I will add the test for type == NN back in.)


| >  1) Since ccid2_ack_ratio_next(sk) is just a wrapper around
| >     dccp_feat_get_nn_next_val(sk, DCCPF_ACK_RATIO), ok to
| >     use this instead?
| 
| It's just fine to use dccp_feat_get_nn_next_val() instead. My primary
| reason for creating ccid2_ack_ratio_next() was to keep line lengths
| down.
| 
Perhaps a shorter name, e.g.    dccp_feat_nn_get()
(since it returns either the current or next value, and there is no other
 accessor function defined).



| >  3) There is room for some refactoring:
| >     a) dccp_feat_signal_nn_change() always implies also in part
| >        dccp_feat_get_nn_next_val(): if the latter function returns
| >        the same value as the supposedly 'new' one, it is not
| >        necessary to start a new negotiation. So all the repeated
| >        tests could be folded into that function.
| 
| 
| The problem here is that the ack ratio should only be changed after a
| loss, idle period, etc if the new cwnd is going to be less than the
| (negotiating) ack ratio. We need to call dccp_feat_get_nn_next_val() to
| decide whether we need to adjust the ack ratio or not.
|
That is a good point, maybe I need to reread the patch again. From what I saw,
dccp_nn_get_next_val() is so far only called immediately before changing the
value, whereas the above comment hints more at a peek/read-only functionality.

This understanding was also the basis of the attached patch - actually I was
quite happy since its use further simplified the interface.

I think once the above is resolved (which also includes the point below), then
there is quite a good milestone for ccid-2.

| We don't want to change the ack ratio every time we have a loss, etc.
| Doing so will result in pointless negotiations and more fluctuations in
| the ack ratio, neither of which is desirable.
| 
Agree, having done some testing over the weekend I found that some kind of
hysteresis would be desirable.

I don't know if blocking the Ack Ratio code is the reason, but during the
initial slow-start there were ChangeL requests for Sequence Window on nearly
every packet, which seems too much.

If low_threshold == high_threshold, it oscillates. I think you have already done
some work on this in the code using CCID2_WIN_CHANGE_FACTOR.

During steady state of the connection I did not see many ChangeL requests. 

[-- Attachment #2: simplify_dynamic_nn_negotation.diff --]
[-- Type: text/x-diff, Size: 1964 bytes --]

dccp ccid-2: use new interface to refactor dynamic negotiation

Using the new function dccp_feat_get_nn_next_val(), this simplifies existing
code to not trigger a new negotiation if the requested new value equals the
old one.
---
 net/dccp/ccids/ccid2.c |   22 +++++-----------------
 net/dccp/feat.c        |    5 +++--
 2 files changed, 8 insertions(+), 19 deletions(-)

--- a/net/dccp/feat.c
+++ b/net/dccp/feat.c
@@ -815,10 +815,11 @@ int dccp_feat_signal_nn_change(struct so
 	    !dccp_feat_is_valid_nn_val(feat, nn_val))
 		return -EINVAL;
 
+	if (nn_val == dccp_feat_get_nn_next_val(sk, feat))
+		return 0;	/* already set or negotiation under way */
+
 	entry = dccp_feat_list_lookup(fn, feat, 1);
 	if (entry != NULL) {
-		if (entry->val.nn == fval.nn)
-			return 0;
 		dccp_pr_debug("Clobbering existing NN entry %llu -> %llu\n",
 			      (unsigned long long)entry->val.nn,
 			      (unsigned long long)nn_val);
--- a/net/dccp/ccids/ccid2.c
+++ b/net/dccp/ccids/ccid2.c
@@ -102,27 +102,15 @@ static void ccid2_change_l_ack_ratio(str
 		DCCP_WARN("Limiting Ack Ratio (%u) to %u\n", val, max_ratio);
 		val = max_ratio;
 	}
-	if (val > DCCPF_ACK_RATIO_MAX)
-		val = DCCPF_ACK_RATIO_MAX;
-
-	if (val == ccid2_ack_ratio_next(sk))
-		return;
-
-	ccid2_pr_debug("changing local ack ratio to %u\n", val);
-	dccp_feat_signal_nn_change(sk, DCCPF_ACK_RATIO, val);
+	dccp_feat_signal_nn_change(sk, DCCPF_ACK_RATIO,
+				   min_t(u32, val, DCCPF_ACK_RATIO_MAX));
 }
 
 static void ccid2_change_l_seq_window(struct sock *sk, u64 val)
 {
-	struct dccp_sock *dp = dccp_sk(sk);
-
-	if (val < DCCPF_SEQ_WMIN)
-		val = DCCPF_SEQ_WMIN;
-	if (val > DCCPF_SEQ_WMAX)
-		val = DCCPF_SEQ_WMAX;
-
-	if (val != dp->dccps_l_seq_win)
-		dccp_feat_signal_nn_change(sk, DCCPF_SEQUENCE_WINDOW, val);
+	dccp_feat_signal_nn_change(sk, DCCPF_SEQUENCE_WINDOW,
+				   clamp_val(val, DCCPF_SEQ_WMIN,
+						  DCCPF_SEQ_WMAX));
 }
 
 static void ccid2_hc_tx_rto_expire(unsigned long data)

^ permalink raw reply

* Re: [PATCH 1/4] netfilter: xt_connlimit: fix daddr connlimit in SNAT scenario
From: Jan Engelhardt @ 2011-03-14 12:26 UTC (permalink / raw)
  To: Changli Gao; +Cc: Patrick McHardy, David S. Miller, netfilter-devel, netdev
In-Reply-To: <1300085414-27275-1-git-send-email-xiaosuo@gmail.com>

On Monday 2011-03-14 07:50, Changli Gao wrote:

>We use the reply tuples when limiting the connections by the destination
>addresses, however, in SNAT scenario, the final reply tuples won't be
>ready until SNAT is done in POSTROUING or INPUT chain

If I am not mistaken: if you do daddr counting, SNAT is irrelevant.
Consider ruleset
 -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to 1.2.3.4:80
 -t nat -A PREROUTING -p tcp --dport 443 -j DNAT --to 1.2.3.5:443

The tuple will first be (as per conntrack -L):
 src=home dst=router src=router dst=home
After DNAT:
 src=home dst=router src=1.2.3.4 dst=home

Thus looking at the src of the reply tuple seems correct — at least this 
is what was wanted, counting per stashed servers (=1 customer), not per 
globally visible address.
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH 1/4] netfilter: xt_connlimit: fix daddr connlimit in SNAT scenario
From: Changli Gao @ 2011-03-14 12:42 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Patrick McHardy, David S. Miller, netfilter-devel, netdev
In-Reply-To: <alpine.LNX.2.01.1103141310140.27542@obet.zrqbmnf.qr>

On Mon, Mar 14, 2011 at 8:26 PM, Jan Engelhardt <jengelh@medozas.de> wrote:
> On Monday 2011-03-14 07:50, Changli Gao wrote:
>
>>We use the reply tuples when limiting the connections by the destination
>>addresses, however, in SNAT scenario, the final reply tuples won't be
>>ready until SNAT is done in POSTROUING or INPUT chain
>
> If I am not mistaken: if you do daddr counting, SNAT is irrelevant.
> Consider ruleset
>  -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to 1.2.3.4:80
>  -t nat -A PREROUTING -p tcp --dport 443 -j DNAT --to 1.2.3.5:443
>
> The tuple will first be (as per conntrack -L):
>  src=home dst=router src=router dst=home
> After DNAT:
>  src=home dst=router src=1.2.3.4 dst=home
>
> Thus looking at the src of the reply tuple seems correct — at least this
> is what was wanted, counting per stashed servers (=1 customer), not per
> globally visible address.
>

Yes, you are correct only when there is no SNAT rule. If there is an SNAT rule:

-t nat -A POSTROUTING -p tcp --dport 80 -j SNAT --to-source 192.168.0.1

the final tuples will be:
src = home dst = router src=1.2.3.4 dst=192.168.0.1

However, the tuple saved by connlimit is src=1.2.3.4 dst=home, so this
conn will be removed later as there isn't any conntrack, which has
this tuple in any direction.

You can't prevent a user from doing such a configuration, although you
might think it is stupid to do that.

Thanks for your review.

-- 
Regards,
Changli Gao(xiaosuo@gmail.com)

^ permalink raw reply

* [PATCH v2 0/2] CS89x0: add QQ2440 networking support
From: Domenico Andreoli @ 2011-03-14 13:46 UTC (permalink / raw)
  To: David S. Miller, Andrew Morton; +Cc: linux-arm-kernel, netdev

Hi,

  this patchset cleans some bit left from the CS89x0_NONISA_IRQ
transition and then enables the support of CS89x0 on QQ2440.

Thanks,
Domenico

-----[ Domenico Andreoli, aka cavok
 --[ http://www.dandreoli.com/gpgkey.asc
   ---[ 3A0F 2F80 F79C 678A 8936  4FEE 0677 9033 A20E BC50

^ permalink raw reply

* [PATCH v2 1/2] CS89x0: Finish transition to CS89x0_NONISA_IRQ
From: Domenico Andreoli @ 2011-03-14 13:46 UTC (permalink / raw)
  To: David S. Miller, Andrew Morton
  Cc: linux-arm-kernel, netdev, Domenico Andreoli
In-Reply-To: <20110314133302.137497490@gmail.com>

[-- Attachment #1: cs89x0-finish-config-nonisa-irq-conversion --]
[-- Type: text/plain, Size: 1526 bytes --]

From: Domenico Andreoli <cavokz@gmail.com>

CS89x0_NONISA_IRQ is selected by all those non-ISA boards which use
CS89x0. This patch only cleans the last bits left after its introduction.

Signed-off-by: Domenico Andreoli <cavokz@gmail.com>

Index: arm-2.6.git/drivers/net/cs89x0.c
===================================================================
--- arm-2.6.git.orig/drivers/net/cs89x0.c	2011-03-14 11:29:35.000000000 +0000
+++ arm-2.6.git/drivers/net/cs89x0.c	2011-03-14 11:35:11.000000000 +0000
@@ -943,10 +943,10 @@
 static void __init reset_chip(struct net_device *dev)
 {
 #if !defined(CONFIG_MACH_MX31ADS)
-#if !defined(CONFIG_MACH_IXDP2351) && !defined(CONFIG_ARCH_IXDP2X01)
+#if !defined(CS89x0_NONISA_IRQ)
 	struct net_local *lp = netdev_priv(dev);
 	int ioaddr = dev->base_addr;
-#endif
+#endif /* CS89x0_NONISA_IRQ */
 	int reset_start_time;
 
 	writereg(dev, PP_SelfCTL, readreg(dev, PP_SelfCTL) | POWER_ON_RESET);
@@ -954,7 +954,7 @@
 	/* wait 30 ms */
 	msleep(30);
 
-#if !defined(CONFIG_MACH_IXDP2351) && !defined(CONFIG_ARCH_IXDP2X01)
+#if !defined(CS89x0_NONISA_IRQ)
 	if (lp->chip_type != CS8900) {
 		/* Hardware problem requires PNP registers to be reconfigured after a reset */
 		writeword(ioaddr, ADD_PORT, PP_CS8920_ISAINT);
@@ -965,7 +965,7 @@
 		outb((dev->mem_start >> 16) & 0xff, ioaddr + DATA_PORT);
 		outb((dev->mem_start >> 8) & 0xff,   ioaddr + DATA_PORT + 1);
 	}
-#endif	/* IXDP2x01 */
+#endif /* CS89x0_NONISA_IRQ */
 
 	/* Wait until the chip is reset */
 	reset_start_time = jiffies;


^ permalink raw reply

* [PATCH v2 2/2] CS89x0: Add networking support for QQ2440
From: Domenico Andreoli @ 2011-03-14 13:47 UTC (permalink / raw)
  To: David S. Miller, Andrew Morton
  Cc: linux-arm-kernel, netdev, Domenico Andreoli
In-Reply-To: <20110314133302.137497490@gmail.com>

[-- Attachment #1: cs89x0-add-qq2440-support --]
[-- Type: text/plain, Size: 3018 bytes --]

From: Domenico Andreoli <cavokz@gmail.com>

QQ2440 is only another non-ISA board using CS89x0. This patch adds the
minimum bits required to make QQ2440 work with CS89x0.

Signed-off-by: Domenico Andreoli <cavokz@gmail.com>

---

I'm working on another series that brings CS89x0 to the third millenium,
it properly uses the modern ISA and platform drivers model. That series
is a little more invasive and will require more testing than this one.

 arch/arm/mach-s3c2440/include/mach/qq2440.h |   22 ++++++++++++
 arch/arm/mach-s3c2440/mach-qq2440.c         |   16 ++++++++
 drivers/net/Kconfig                         |    4 +-
 drivers/net/cs89x0.c                        |   33 ++++++++++++------
 4 files changed, 61 insertions(+), 14 deletions(-)

Index: arm-2.6.git/drivers/net/cs89x0.c
===================================================================
--- arm-2.6.git.orig/drivers/net/cs89x0.c	2011-02-07 17:04:06.000000000 +0000
+++ arm-2.6.git/drivers/net/cs89x0.c	2011-02-07 17:07:36.000000000 +0000
@@ -95,6 +95,9 @@
   Dmitry Pervushin  : dpervushin@ru.mvista.com
                     : PNX010X platform support
 
+  Domenico Andreoli : cavokz@gmail.com
+                    : QQ2440 platform support
+
 */
 
 /* Always include 'config.h' first in case the user wants to turn on
@@ -176,6 +179,10 @@
 #elif defined(CONFIG_ARCH_IXDP2X01)
 static unsigned int netcard_portlist[] __used __initdata = {IXDP2X01_CS8900_VIRT_BASE, 0};
 static unsigned int cs8900_irq_map[] = {IRQ_IXDP2X01_CS8900, 0, 0, 0};
+#elif defined(CONFIG_MACH_QQ2440)
+#include <mach/qq2440.h>
+static unsigned int netcard_portlist[] __used __initdata = { QQ2440_CS8900_VIRT_BASE + 0x300, 0 };
+static unsigned int cs8900_irq_map[] = { QQ2440_CS8900_IRQ, 0, 0, 0 };
 #elif defined(CONFIG_MACH_MX31ADS)
 #include <mach/board-mx31ads.h>
 static unsigned int netcard_portlist[] __used __initdata = {
@@ -521,6 +528,10 @@
 #endif
 		lp->force = g_cs89x0_media__force;
 #endif
+
+#if defined(CONFIG_MACH_QQ2440)
+		lp->force |= FORCE_RJ45 | FORCE_FULL;
+#endif
         }
 
 	/* Grab the region so we can find another board if autoIRQ fails. */
Index: arm-2.6.git/drivers/net/Kconfig
===================================================================
--- arm-2.6.git.orig/drivers/net/Kconfig	2011-02-07 17:04:06.000000000 +0000
+++ arm-2.6.git/drivers/net/Kconfig	2011-02-07 17:04:42.000000000 +0000
@@ -1498,7 +1498,7 @@
 config CS89x0
 	tristate "CS89x0 support"
 	depends on NET_ETHERNET && (ISA || EISA || MACH_IXDP2351 \
-		|| ARCH_IXDP2X01 || MACH_MX31ADS)
+		|| ARCH_IXDP2X01 || MACH_MX31ADS || MACH_QQ2440)
 	---help---
 	  Support for CS89x0 chipset based Ethernet cards. If you have a
 	  network (Ethernet) card of this type, say Y and read the
@@ -1512,7 +1512,7 @@
 config CS89x0_NONISA_IRQ
 	def_bool y
 	depends on CS89x0 != n
-	depends on MACH_IXDP2351 || ARCH_IXDP2X01 || MACH_MX31ADS
+	depends on MACH_IXDP2351 || ARCH_IXDP2X01 || MACH_MX31ADS || MACH_QQ2440
 
 config TC35815
 	tristate "TOSHIBA TC35815 Ethernet support"


^ permalink raw reply

* Re: kernel 2.6.37 : oops in cleanup_once
From: Eric Dumazet @ 2011-03-14 13:14 UTC (permalink / raw)
  To: Yann Dupont; +Cc: linux-kernel, netdev
In-Reply-To: <4D7DF1A3.6020501@univ-nantes.fr>

Le lundi 14 mars 2011 à 11:44 +0100, Yann Dupont a écrit :
> Le 02/02/2011 16:08, Eric Dumazet a écrit :
> 
> 
> > I suspect a mem corruption from another layer (not inetpeer)
> >
> > Unfortunately many kmem caches share the "64 bytes" cache.
> >
> > Could you please add "slub_nomerge" on your boot command ?
> >
> ...
> 
> >
> >> -Is there a very severe impact on performance ?
> >>
> > not at all
> >
> Maybe there is an impact after all : since then, we don't have problems 
> anymore !
> 
> linkwood:~# uptime
>   11:42:03 up 39 days, 17:08,  3 users,  load average: 0.01, 0.03, 0.05
> 
> So... could slub_nomerge hide or simply avoid the problem ?
> Or are we just lucky this time ?
> 
> 

I would say you are lucky ;)

Not all memory corruptions are noticed. Sometimes it touch unused parts
of memory, or some parts with no critical content.




^ permalink raw reply

* [RFC v3 0/6] CAN: add SAE J1939 protocol
From: Kurt Van Dijck @ 2011-03-14 13:20 UTC (permalink / raw)
  To: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
	netdev-u79uwXL29TY76Z2rM5mHXA

This series adds SAE J1939 support to the current net-next-2.6.
For the rtnetlink operations to work, a 2.6.38-rc1 is required.

1/6: can: extend sockaddr_can to include j1939 members
2/6: can: add rtnetlink support
3/6: can: make struct proto const
4/6: j1939: initial import of SAE J1939
5/6: j1939: add documentation & MAINTAINERS
6/6: iproute2: use CAN & J1939

Still a lot of things probably need explanation...

Kind regards,
Kurt Van Dijck
EIA Electronics

^ permalink raw reply

* [patch 0/1] s390: qeth patch for net-next
From: frank.blaschka @ 2011-03-14 13:20 UTC (permalink / raw)
  To: davem; +Cc: netdev, linux-s390

Hi Dave,

here is another qeth patch for net-next.

shortlog:
Frank Blaschka (1)
qeth: change some configurations defaults

Thanks,
        Frank


^ permalink raw reply

* [patch 1/1] [PATCH] qeth: change some configurations defaults
From: frank.blaschka @ 2011-03-14 13:20 UTC (permalink / raw)
  To: davem; +Cc: netdev, linux-s390
In-Reply-To: <20110314132053.132019386@de.ibm.com>

[-- Attachment #1: 603-qeth-defaults.diff --]
[-- Type: text/plain, Size: 2524 bytes --]

From: Frank Blaschka <frank.blaschka@de.ibm.com>

This patch turns on RX checksum and GRO by default. To improve
receiving performance and reduce congestion in case of network
bursts we also increase the default number of inbound buffers.

Signed-off-by: Frank Blaschka <frank.blaschka@de.ibm.com>
---


 drivers/s390/net/qeth_core.h      |    3 ++-
 drivers/s390/net/qeth_core_main.c |    5 ++++-
 drivers/s390/net/qeth_l3_main.c   |    5 +++++
 3 files changed, 11 insertions(+), 2 deletions(-)
--- a/drivers/s390/net/qeth_core.h
+++ b/drivers/s390/net/qeth_core.h
@@ -225,7 +225,8 @@ static inline int qeth_is_ipa_enabled(st
 /*****************************************************************************/
 #define QETH_MAX_QUEUES 4
 #define QETH_IN_BUF_SIZE_DEFAULT 65536
-#define QETH_IN_BUF_COUNT_DEFAULT 16
+#define QETH_IN_BUF_COUNT_DEFAULT 64
+#define QETH_IN_BUF_COUNT_HSDEFAULT 128
 #define QETH_IN_BUF_COUNT_MIN 8
 #define QETH_IN_BUF_COUNT_MAX 128
 #define QETH_MAX_BUFFER_ELEMENTS(card) ((card)->qdio.in_buf_size >> 12)
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -1026,7 +1026,10 @@ static void qeth_init_qdio_info(struct q
 	atomic_set(&card->qdio.state, QETH_QDIO_UNINITIALIZED);
 	/* inbound */
 	card->qdio.in_buf_size = QETH_IN_BUF_SIZE_DEFAULT;
-	card->qdio.init_pool.buf_count = QETH_IN_BUF_COUNT_DEFAULT;
+	if (card->info.type == QETH_CARD_TYPE_IQD)
+		card->qdio.init_pool.buf_count = QETH_IN_BUF_COUNT_HSDEFAULT;
+	else
+		card->qdio.init_pool.buf_count = QETH_IN_BUF_COUNT_DEFAULT;
 	card->qdio.in_buf_pool.buf_count = card->qdio.init_pool.buf_count;
 	INIT_LIST_HEAD(&card->qdio.in_buf_pool.entry_list);
 	INIT_LIST_HEAD(&card->qdio.init_pool.entry_list);
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -3392,6 +3392,8 @@ static int qeth_l3_setup_netdev(struct q
 			if (!(card->info.unique_id & UNIQUE_ID_NOT_BY_CARD))
 				card->dev->dev_id = card->info.unique_id &
 							 0xffff;
+			if (!card->info.guestlan)
+				card->dev->features |= NETIF_F_GRO;
 		}
 	} else if (card->info.type == QETH_CARD_TYPE_IQD) {
 		card->dev = alloc_netdev(0, "hsi%d", ether_setup);
@@ -3430,6 +3432,9 @@ static int qeth_l3_probe_device(struct c
 	card->discipline.output_handler = (qdio_handler_t *)
 		qeth_qdio_output_handler;
 	card->discipline.recover = qeth_l3_recover;
+	if ((card->info.type == QETH_CARD_TYPE_OSD) ||
+	    (card->info.type == QETH_CARD_TYPE_OSX))
+		card->options.checksum_type = HW_CHECKSUMMING;
 	return 0;
 }
 


^ permalink raw reply

* [PATCH] pktgen: fix transmission headers with frags=0
From: Daniel Turull @ 2011-03-14 13:22 UTC (permalink / raw)
  To: netdev; +Cc: Robert Olsson, Jens Laas, Voravit Tanyingyong, Daniel Turull

The headers of pktgen were incorrectly added in a pktgen packet
without frags (frags=0). There was an offset in the pktgen headers.

The cause was in reusing the pgh variable as a return variable in skb_put
when adding the payload to the skb.

A rename of the variable is done.

Signed-off-by: Daniel Turull <daniel.turull@gmail.com>
---
The PKTGEN magic (be9b e955) now starts in the correct offset.
Before the patch, it was starting at the end of the packet (be9b)

Capture from tcpdump:

before patch:
14:57:37.854812 IP 10.0.0.2.discard > 10.254.254.84.discard: UDP, length 18
	0x0000:  001b 2157 ed84 001b 215d 01d0 0800 4500
	0x0010:  002e 0004 0000 2011 8767 0a00 0002 0afe
	0x0020:  fe54 0009 0009 001a 0000 0000 0000 b072
	0x0030:  9102 00ea ffff 0010 0000 be9b

after patch:
14:44:32.896048 IP 10.0.0.2.discard > 10.217.234.56.discard: UDP, length 18
	0x0000:  001b 2157 ed84 001b 215d 01d0 0800 4500
	0x0010:  002e 0000 0000 2011 9bac 0a00 0002 0ad9
	0x0020:  ea38 0009 0009 001a 0000 be9b e955 0000
	0x0030:  0001 4d7e 1b09 0005 5f23 af00

---
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index f0aec6c..5baa9d9 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -2615,13 +2615,14 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
 {
 	struct timeval timestamp;
 	struct pktgen_hdr *pgh;
+	void *data;
 
 	pgh = (struct pktgen_hdr *)skb_put(skb, sizeof(*pgh));
 	datalen -= sizeof(*pgh);
 
 	if (pkt_dev->nfrags <= 0) {
-		pgh = (struct pktgen_hdr *)skb_put(skb, datalen);
-		memset(pgh + 1, 0, datalen);
+		data = skb_put(skb, datalen);
+		memset(data + 1, 0, datalen);
 	} else {
 		int frags = pkt_dev->nfrags;
 		int i, len;

^ permalink raw reply related

* [RFC v3 1/6] can: extend sockaddr_can to include j1939 members
From: Kurt Van Dijck @ 2011-03-14 13:24 UTC (permalink / raw)
  To: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
	netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20110314132004.GA333-MxZ6Iy/zr/UdbCeoMzGj59i2O/JbrIOy@public.gmane.org>

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>
---
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 6c507be..653c33e 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 092dc88..b286e45 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)
@@ -1493,7 +1493,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 883e9d7..a5f1f41 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -350,7 +350,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);
@@ -649,7 +649,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)

^ permalink raw reply related

* [RFC v3 2/6] can: add rtnetlink support
From: Kurt Van Dijck @ 2011-03-14 13:26 UTC (permalink / raw)
  To: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
	netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20110314132004.GA333-MxZ6Iy/zr/UdbCeoMzGj59i2O/JbrIOy@public.gmane.org>

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>
---
diff --git a/include/linux/can/core.h b/include/linux/can/core.h
index 653c33e..430c446 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,28 @@ struct can_proto {
 	int              protocol;
 	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 +76,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 702be5a..db59c6e 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];
 static DEFINE_SPINLOCK(proto_tab_lock);
 
 struct timer_list can_stattimer;   /* timer for statistics update */
@@ -92,6 +94,48 @@ struct s_stats    can_stats;       /* packet statistics */
 struct s_pstats   can_pstats;      /* receive list statistics */
 
 /*
+ * can protocol lookup
+ */
+#define CGP_F_LOAD	0x01 /* try to load protocol when absent */
+static inline const struct can_proto *can_get_proto(int protocol, int flags)
+{
+	const struct can_proto *cp;
+
+	if (protocol < 0 || protocol >= CAN_NPROTO)
+		return ERR_PTR(-EINVAL);
+
+#ifdef CONFIG_MODULES
+	/* try to load protocol module kernel is modular */
+	if (!proto_tab[protocol] && (flags & CGP_F_LOAD)) {
+		int ret;
+
+		ret = request_module("can-proto-%d", protocol);
+		/*
+		 * In case of error we only print a message but don't
+		 * return the error code immediately.  Below we will
+		 * return -EPROTONOSUPPORT
+		 */
+		if (ret)
+			pr_err_ratelimited("can: request_module "
+			       "(can-proto-%d) failed.\n", protocol);
+	}
+#endif
+
+	spin_lock(&proto_tab_lock);
+	cp = proto_tab[protocol];
+	if (cp && !try_module_get(cp->prot->owner))
+		cp = 0;
+	spin_unlock(&proto_tab_lock);
+	if (!cp)
+		cp = ERR_PTR(-ENOPROTOOPT);
+	return cp;
+}
+
+static inline void can_put_proto(const struct can_proto *cp)
+{
+	module_put(cp->prot->owner);
+}
+/*
  * af_can socket functions
  */
 
@@ -118,46 +162,22 @@ 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;
 
-#ifdef CONFIG_MODULES
-	/* try to load protocol module kernel is modular */
-	if (!proto_tab[protocol]) {
-		err = request_module("can-proto-%d", protocol);
-
-		/*
-		 * In case of error we only print a message but don't
-		 * return the error code immediately.  Below we will
-		 * return -EPROTONOSUPPORT
-		 */
-		if (err && printk_ratelimit())
-			printk(KERN_ERR "can: request_module "
-			       "(can-proto-%d) failed.\n", protocol);
-	}
-#endif
-
-	spin_lock(&proto_tab_lock);
-	cp = proto_tab[protocol];
-	if (cp && !try_module_get(cp->prot->owner))
-		cp = NULL;
-	spin_unlock(&proto_tab_lock);
-
+	cp = can_get_proto(protocol, CGP_F_LOAD);
 	/* 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 = -EPROTONOSUPPORT;
+		err = -ESOCKTNOSUPPORT;
 		goto errout;
 	}
 
@@ -182,7 +202,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;
 }
 
@@ -678,7 +698,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;
@@ -718,7 +738,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;
 
@@ -809,6 +829,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_get_proto(j, 0);
+		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_get_proto(j, 0);
+		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_get_proto(nla_type(prot), 0);
+		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_get_proto(nla_type(prot), 0);
+		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);
@@ -834,6 +1054,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;
 }
 
@@ -844,6 +1069,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 b286e45..ac1961d 100644
--- a/net/can/bcm.c
+++ b/net/can/bcm.c
@@ -1596,7 +1596,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 a5f1f41..9ad3dfc 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -769,7 +769,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,

^ permalink raw reply related

* Re: [GIT/PATCH v4] xen network backend driver
From: Ian Campbell @ 2011-03-14 13:43 UTC (permalink / raw)
  To: Michał Mirosław
  Cc: netdev@vger.kernel.org, xen-devel, Ben Hutchings,
	Jeremy Fitzhardinge, Herbert Xu, Konrad Rzeszutek Wilk,
	Francois Romieu, Michał Mirosław
In-Reply-To: <AANLkTinHyFLRkeb60x4Q6uUw6p43XBjk5rpqCMo49WKF@mail.gmail.com>

On Thu, 2011-03-10 at 17:33 +0000, Michał Mirosław wrote:
> 2011/3/10 Ian Campbell <Ian.Campbell@citrix.com>:
> > The following patch is the fourth iteration of the Xen network backend
> > driver for upstream Linux.
> >
> > This driver ("netback") is the host side counterpart to the frontend
> > driver in drivers/net/xen-netfront.c. The PV protocol is also
> > implemented by frontend drivers in other OSes too, such as the BSDs and
> > even Windows.
> [...]
> > --- /dev/null
> > +++ b/drivers/net/xen-netback/common.h
> > @@ -0,0 +1,162 @@
> [...]
> > +struct xenvif {
> [...]
> > +       /* Flags that must not be set in dev->features */
> > +       int features_disabled;
> > +
> > +       /* Frontend feature information. */
> > +       u8 can_sg:1;
> > +       u8 gso:1;
> > +       u8 gso_prefix:1;
> > +       u8 csum:1;
> [...]
> > --- /dev/null
> > +++ b/drivers/net/xen-netback/interface.c
> > @@ -0,0 +1,424 @@
> [...]
> > +static int xenvif_change_mtu(struct net_device *dev, int mtu)
> > +{
> > +       struct xenvif *vif = netdev_priv(dev);
> > +       int max = vif->can_sg ? 65535 - VLAN_ETH_HLEN : ETH_DATA_LEN;
> > +
> > +       if (mtu > max)
> > +               return -EINVAL;
> > +       dev->mtu = mtu;
> > +       return 0;
> > +}
> > +
> > +static void xenvif_set_features(struct xenvif *vif)
> > +{
> > +       struct net_device *dev = vif->dev;
> > +       int features = dev->features;
> > +
> > +       if (vif->can_sg)
> > +               features |= NETIF_F_SG;
> > +       if (vif->gso || vif->gso_prefix)
> > +               features |= NETIF_F_TSO;
> > +       if (vif->csum)
> > +               features |= NETIF_F_IP_CSUM;
> > +
> > +       features &= ~(vif->features_disabled);
> > +
> > +       if (!(features & NETIF_F_SG) && dev->mtu > ETH_DATA_LEN)
> > +               dev->mtu = ETH_DATA_LEN;
> > +
> > +       dev->features = features;
> > +}
> > +
> > +static int xenvif_set_tx_csum(struct net_device *dev, u32 data)
> > +{
> > +       struct xenvif *vif = netdev_priv(dev);
> > +       if (data) {
> > +               if (!vif->csum)
> > +                       return -EOPNOTSUPP;
> > +               vif->features_disabled &= ~NETIF_F_IP_CSUM;
> > +       } else {
> > +               vif->features_disabled |= NETIF_F_IP_CSUM;
> > +       }
> > +
> > +       xenvif_set_features(vif);
> > +       return 0;
> > +}
> > +
> > +static int xenvif_set_sg(struct net_device *dev, u32 data)
> > +{
> > +       struct xenvif *vif = netdev_priv(dev);
> > +       if (data) {
> > +               if (!vif->can_sg)
> > +                       return -EOPNOTSUPP;
> > +               vif->features_disabled &= ~NETIF_F_SG;
> > +       } else {
> > +               vif->features_disabled |= NETIF_F_SG;
> > +       }
> > +
> > +       xenvif_set_features(vif);
> > +       return 0;
> > +}
> > +
> > +static int xenvif_set_tso(struct net_device *dev, u32 data)
> > +{
> > +       struct xenvif *vif = netdev_priv(dev);
> > +       if (data) {
> > +               if (!vif->gso && !vif->gso_prefix)
> > +                       return -EOPNOTSUPP;
> > +               vif->features_disabled &= ~NETIF_F_TSO;
> > +       } else {
> > +               vif->features_disabled |= NETIF_F_TSO;
> > +       }
> > +
> > +       xenvif_set_features(vif);
> > +       return 0;
> > +}
> [...]
> 
> You could make this simpler by using netdev->hw_features and
> ndo_fix_features/ndo_set_features calls recently introduced in
> net-next.

Thanks, this does look like it could simplify things. Doesn't look like
its use is widespread (even net-next) yet though?

I'll add it to my TODO list for the future.

>  I'll reply with a proof-of-concept patch for xen-netfront.
> 
> Best Regards,
> Michał Mirosław



^ permalink raw reply

* [RFC v3 3/6] can: make struct proto const
From: Kurt Van Dijck @ 2011-03-14 13:47 UTC (permalink / raw)
  To: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
	netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20110314132004.GA333-MxZ6Iy/zr/UdbCeoMzGj59i2O/JbrIOy@public.gmane.org>

can_ioctl is the only reason for struct proto to be non-const.
script/check-patch.pl suggests struct proto be const.
This patch performs the necessary change.

Signed-off-by: Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
---
diff --git a/include/linux/can/core.h b/include/linux/can/core.h
index 430c446..0767cc6 100644
--- a/include/linux/can/core.h
+++ b/include/linux/can/core.h
@@ -39,7 +39,7 @@
 struct can_proto {
 	int              type;
 	int              protocol;
-	struct proto_ops *ops;
+	const struct proto_ops *ops;
 	struct proto     *prot;
 
 	const struct rtnl_af_ops *rtnl_link_ops;
@@ -78,6 +78,8 @@ struct rtgencanmsg {
 
 extern int  can_proto_register(const struct can_proto *cp);
 extern void can_proto_unregister(const struct can_proto *cp);
+extern int can_sock_ioctl(struct socket *sock, unsigned int cmd,
+		unsigned long arg);
 
 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 db59c6e..c1f8c05 100644
--- a/net/can/af_can.c
+++ b/net/can/af_can.c
@@ -139,7 +139,7 @@ static inline void can_put_proto(const struct can_proto *cp)
  * af_can socket functions
  */
 
-static int can_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+int can_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
 	struct sock *sk = sock->sk;
 
@@ -152,6 +152,7 @@ static int can_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 		return -ENOIOCTLCMD;
 	}
 }
+EXPORT_SYMBOL(can_sock_ioctl);
 
 static void can_sock_destruct(struct sock *sk)
 {
@@ -720,10 +721,6 @@ int can_proto_register(const struct can_proto *cp)
 		err = -EBUSY;
 	} else {
 		proto_tab[proto] = cp;
-
-		/* use generic ioctl function if not defined by module */
-		if (!cp->ops->ioctl)
-			cp->ops->ioctl = can_ioctl;
 	}
 	spin_unlock(&proto_tab_lock);
 
diff --git a/net/can/bcm.c b/net/can/bcm.c
index ac1961d..fd89542 100644
--- a/net/can/bcm.c
+++ b/net/can/bcm.c
@@ -1569,7 +1569,7 @@ static int bcm_recvmsg(struct kiocb *iocb, struct socket *sock,
 	return size;
 }
 
-static struct proto_ops bcm_ops __read_mostly = {
+static const struct proto_ops bcm_ops = {
 	.family        = PF_CAN,
 	.release       = bcm_release,
 	.bind          = sock_no_bind,
@@ -1578,7 +1578,7 @@ static struct proto_ops bcm_ops __read_mostly = {
 	.accept        = sock_no_accept,
 	.getname       = sock_no_getname,
 	.poll          = datagram_poll,
-	.ioctl         = NULL,		/* use can_ioctl() from af_can.c */
+	.ioctl         = can_sock_ioctl,
 	.listen        = sock_no_listen,
 	.shutdown      = sock_no_shutdown,
 	.setsockopt    = sock_no_setsockopt,
diff --git a/net/can/raw.c b/net/can/raw.c
index 9ad3dfc..8581596 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -742,7 +742,7 @@ static int raw_recvmsg(struct kiocb *iocb, struct socket *sock,
 	return size;
 }
 
-static struct proto_ops raw_ops __read_mostly = {
+static const struct proto_ops raw_ops = {
 	.family        = PF_CAN,
 	.release       = raw_release,
 	.bind          = raw_bind,
@@ -751,7 +751,7 @@ static struct proto_ops raw_ops __read_mostly = {
 	.accept        = sock_no_accept,
 	.getname       = raw_getname,
 	.poll          = datagram_poll,
-	.ioctl         = NULL,		/* use can_ioctl() from af_can.c */
+	.ioctl         = can_sock_ioctl,
 	.listen        = sock_no_listen,
 	.shutdown      = sock_no_shutdown,
 	.setsockopt    = raw_setsockopt,

^ permalink raw reply related

* Re: [PATCH] pktgen: fix transmission headers with frags=0
From: Eric Dumazet @ 2011-03-14 13:56 UTC (permalink / raw)
  To: Daniel Turull; +Cc: netdev, Robert Olsson, Jens Laas, Voravit Tanyingyong
In-Reply-To: <4D7E167E.2090807@gmail.com>

Le lundi 14 mars 2011 à 14:22 +0100, Daniel Turull a écrit :
> The headers of pktgen were incorrectly added in a pktgen packet
> without frags (frags=0). There was an offset in the pktgen headers.
> 
> The cause was in reusing the pgh variable as a return variable in skb_put
> when adding the payload to the skb.
> 
> A rename of the variable is done.
> 
> Signed-off-by: Daniel Turull <daniel.turull@gmail.com>
> ---
> The PKTGEN magic (be9b e955) now starts in the correct offset.
> Before the patch, it was starting at the end of the packet (be9b)
> 

not exactly, but offseted  +16 bytes
(sizeof(struct pktgen_hdr))

> Capture from tcpdump:
> 
> before patch:
> 14:57:37.854812 IP 10.0.0.2.discard > 10.254.254.84.discard: UDP, length 18
> 	0x0000:  001b 2157 ed84 001b 215d 01d0 0800 4500
> 	0x0010:  002e 0004 0000 2011 8767 0a00 0002 0afe
> 	0x0020:  fe54 0009 0009 001a 0000 0000 0000 b072
> 	0x0030:  9102 00ea ffff 0010 0000 be9b
> 
> after patch:
> 14:44:32.896048 IP 10.0.0.2.discard > 10.217.234.56.discard: UDP, length 18
> 	0x0000:  001b 2157 ed84 001b 215d 01d0 0800 4500
> 	0x0010:  002e 0000 0000 2011 9bac 0a00 0002 0ad9
> 	0x0020:  ea38 0009 0009 001a 0000 be9b e955 0000
> 	0x0030:  0001 4d7e 1b09 0005 5f23 af00
> 
> ---
> diff --git a/net/core/pktgen.c b/net/core/pktgen.c
> index f0aec6c..5baa9d9 100644
> --- a/net/core/pktgen.c
> +++ b/net/core/pktgen.c
> @@ -2615,13 +2615,14 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
>  {
>  	struct timeval timestamp;
>  	struct pktgen_hdr *pgh;
> +	void *data;
>  
>  	pgh = (struct pktgen_hdr *)skb_put(skb, sizeof(*pgh));
>  	datalen -= sizeof(*pgh);
>  
>  	if (pkt_dev->nfrags <= 0) {
> -		pgh = (struct pktgen_hdr *)skb_put(skb, datalen);
> -		memset(pgh + 1, 0, datalen);
> +		data = skb_put(skb, datalen);
> +		memset(data + 1, 0, datalen);
>  	} else {
>  		int frags = pkt_dev->nfrags;
>  		int i, len;
> --

Good catch !

Hmm this patch is not correct, why memset(data + 1, ...) ?

Also, this patch is needed for net-next-2.6 only
(bug introduced by commit 26ad787962ef84677a48c560
(pktgen: speedup fragmented skbs)

I would avoid the "void *data;" declaration and just use following :

diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index f0aec6c..f727c83 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -2620,7 +2620,7 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
 	datalen -= sizeof(*pgh);
 
 	if (pkt_dev->nfrags <= 0) {
-		pgh = (struct pktgen_hdr *)skb_put(skb, datalen);
+		skb_put(skb, datalen);
 		memset(pgh + 1, 0, datalen);
 	} else {
 		int frags = pkt_dev->nfrags;



or even :

		memset(skb_put(skb, datalen), 0, datalen);





^ permalink raw reply related

* [RFC v3 4/6] j1939: initial import of SAE J1939
From: Kurt Van Dijck @ 2011-03-14 13:56 UTC (permalink / raw)
  To: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
	netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20110314132004.GA333-MxZ6Iy/zr/UdbCeoMzGj59i2O/JbrIOy@public.gmane.org>

This patch contains the main SAE J1939 stack.

scripts/checkpatch.pl reports 3 errors like:
ERROR: return is not a function, parentheses are not required
#3819: FILE: net/can/j1939/transport.c:246:
+       return (dat[2] << 8) + (dat[1] << 0);

I don't see how to fix the checkpatch.pl error _and_ improving
the code.

Signed-off-by: Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
---
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..87d28e9
--- /dev/null
+++ b/include/linux/can/j1939.h
@@ -0,0 +1,100 @@
+/*
+ * 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	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_SEND_PRIO = 4,
+	SO_J1939_DEST_MASK = 5, /* mask names in connect() & sendto() */
+};
+
+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..4793b9f
--- /dev/null
+++ b/net/can/j1939/address-claim.c
@@ -0,0 +1,527 @@
+/*
+ * 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"
+
+static void ac_sm_step(struct j1939_ecu *ecu, int event, int new_sa);
+
+#define J1939_STACK_MODULE	"AC "
+#define j1939_ac_err(...)	j1939_err(J1939_STACK_MODULE __VA_ARGS__)
+#define j1939_ac_warning(...)	j1939_warning(J1939_STACK_MODULE __VA_ARGS__)
+#define j1939_ac_debug(...)	j1939_debug(J1939_STACK_MODULE __VA_ARGS__)
+
+#define PGN_REQUEST		0x00EA00
+#define PGN_ADDRESS_CLAIMED	0x00EE00
+
+#define NULL_ADDRESS		0xFE
+#define BROADCAST		0xFF
+
+#define CANDATA2NAME(data) le64_to_cpu(*(name_t *)(data))
+
+/* ECU address claiming states. */
+enum {
+	STATE_AC_SA_NONE = 0, /* no address claimed */
+	STATE_AC_SA_PENDING, /* address claim pending (waiting for 250ms) */
+	STATE_AC_SA_CLAIMED, /* successfully claimed sa */
+	STATE_AC_MAX /* do not remove */
+};
+
+/* State to name mapping. */
+static const char *st2name[STATE_AC_MAX] = {
+	"sa none",
+	"sa pending",
+	"sa claimed",
+};
+
+/* ECU address claiming events. */
+enum {
+	EVENT_AC_TX, /* tx of ac msg:
+	 * msg will be processed at rx side (via CAN intf echo)
+	 * but might already block outgoing traffic
+	 */
+	EVENT_AC_SA_CC,	/* cannot claim sa, 2 reasons:
+	 * rx of cannot claim msg or,
+	 * rx of address claim msg of conflicting ecu with higher prio
+	 */
+	EVENT_AC_SA_CL,	/* sa can be claimed, reason:
+	 * rx of address claim msg
+	 */
+	EVENT_AC_TIMEOUT, /* (250ms) timer expired */
+	EVENT_AC_MAX /* do not remove */
+};
+
+/* Event to name mapping. */
+static const char *ev2name[EVENT_AC_MAX] = {
+	"ac tx",
+	"sa cc",
+	"sa cl",
+	"timeout"
+};
+
+/* Impossible state change: print error. */
+static void ac_sm_bug(struct j1939_ecu *ecu, int new_sa)
+{
+	j1939_ac_err("BUG: invalid state change\n");
+}
+
+/*
+ * Process tx of an ac.
+ * The new_sa will be processed when the msg is received via CAN intf echo.
+ * Don't change source address as it can still be used for at rx side.
+ * Avoid further state changes (by cancelling the timer)
+ * until the ac msg (or another one) is received again.
+ */
+static void ac_sm_tx(struct j1939_ecu *ecu, int new_sa)
+{
+	if (new_sa != ecu->sa) {
+		hrtimer_try_to_cancel(&ecu->ac_timer);
+		ecu->block_tx = 1;
+	}
+}
+
+/*
+ * Process cannot claim sa
+ * as a result of cannot claim msg or lower prio name.
+ */
+static void ac_sm_sa_cc(struct j1939_ecu *ecu, int new_sa)
+{
+	hrtimer_try_to_cancel(&ecu->ac_timer);
+	ecu->ac_state = STATE_AC_SA_NONE;
+	j1939_ecu_set_address(ecu, -1);
+}
+
+/*
+ * Process address claim.
+ */
+static void ac_sm_sa_cl(struct j1939_ecu *ecu, int new_sa)
+{
+	if (ecu->sa == new_sa)
+		return;
+
+	hrtimer_try_to_cancel(&ecu->ac_timer);
+	ecu->ac_state = STATE_AC_SA_PENDING;
+	j1939_ecu_set_address(ecu, new_sa);
+	hrtimer_start(&ecu->ac_timer, ktime_set(0, 250000000),
+			HRTIMER_MODE_REL);
+}
+
+/*
+ * Process timer expiry.
+ */
+static void ac_sm_timeout(struct j1939_ecu *ecu, int new_sa)
+{
+	ecu->ac_state = STATE_AC_SA_CLAIMED;
+	ecu->block_tx = 0;
+}
+
+/*
+ * Address Claiming state machine.
+ * Redundant (unnecessary) state transitions get NULL.
+ * Invalid (impossible) state transitions dump an error (ac_sm_bug).
+ */
+static void (*const ac_sm[STATE_AC_MAX][EVENT_AC_MAX])
+	(struct j1939_ecu *ecu, int new_sa) = {
+	/* TX, SA_CC, SA_CL, TIMEOUT */
+	[STATE_AC_SA_NONE] = {
+		NULL, NULL, ac_sm_sa_cl, ac_sm_bug, },
+	[STATE_AC_SA_PENDING] = {
+		ac_sm_tx, ac_sm_sa_cc, ac_sm_sa_cl, ac_sm_timeout, },
+	[STATE_AC_SA_CLAIMED] = {
+		ac_sm_tx, ac_sm_sa_cc, ac_sm_sa_cl, ac_sm_bug, },
+};
+
+/*
+ * Progress state machine one step.
+ */
+static void ac_sm_step(struct j1939_ecu *ecu, int event, int new_sa)
+{
+	char *str;
+	char out[100];
+
+	str = out;
+	memset(out, '\0', 100);
+
+	BUG_ON(!ecu);
+	BUG_ON(event >= EVENT_AC_MAX || event < 0);
+
+	write_lock_bh(&ecu->ac_lock);
+
+	str += sprintf(str, "%i:%016llx (%02x,%s)",
+			ecu->ifindex, ecu->name,
+			(ecu->sa > 0) ? ecu->sa : 0x1ff,
+			st2name[ecu->ac_state]);
+	str += sprintf(str, " =[%s,0x%02x]=> ", ev2name[event], new_sa);
+
+	if (ac_sm[ecu->ac_state][event])
+		ac_sm[ecu->ac_state][event](ecu, new_sa);
+
+	str += sprintf(str, "(%02x,%s)",
+			(ecu->sa > 0) ? ecu->sa : 0x1ff,
+			st2name[ecu->ac_state]);
+
+	write_unlock_bh(&ecu->ac_lock);
+
+	j1939_ac_debug("%s\n", out);
+}
+
+static inline int ac_msg_is_address_claimed(pgn_t pgn, int sa)
+{
+	return (pgn == PGN_ADDRESS_CLAIMED) && (sa != NULL_ADDRESS);
+}
+
+static inline int ac_msg_is_cannot_claim(pgn_t pgn, int sa)
+{
+	return (pgn == PGN_ADDRESS_CLAIMED) && (sa == NULL_ADDRESS);
+}
+
+static inline int ac_msg_is_request_for_address_claimed(pgn_t pgn,
+		const unsigned char *d, unsigned int len)
+{
+	pgn_t req_pgn;
+
+	if (len < 3)
+		return 0;
+	/* Little Endian safe */
+	req_pgn = (d[0] << 0) | (d[1] << 8) | (d[2] << 16);
+	return (pgn == PGN_REQUEST) && (PGN_ADDRESS_CLAIMED == req_pgn);
+}
+
+/*
+ * Resolve a NAME to a SA, taking into account ac state.
+ * Returns invalid sa (<0) in case of failure.
+ */
+static int ac_name2sa(name_t name, int ifindex, int check_block_tx)
+{
+	int sa;
+	struct j1939_ecu *ecu = j1939_ecu_find_by_name(name, ifindex);
+
+	if (!ecu)
+		return -1;
+
+	read_lock_bh(&ecu->ac_lock);
+	if (ecu->ac_state == STATE_AC_SA_CLAIMED) {
+		if (check_block_tx && ecu->block_tx)
+			sa = -1;
+		else
+			sa = ecu->sa;
+	} else {
+		sa = -1;
+	}
+	read_unlock_bh(&ecu->ac_lock);
+
+	put_j1939_ecu(ecu);
+	return sa;
+}
+
+/*
+ * Resolve SA to NAME, taking into account ac state.
+ * Returns invalid name (=0) in case of failure.
+ */
+static name_t ac_sa2name(int sa, int ifindex)
+{
+	name_t name;
+	struct j1939_ecu *ecu = j1939_ecu_find_by_addr(sa, ifindex);
+
+	if (!ecu)
+		return 0;
+
+	read_lock_bh(&ecu->ac_lock);
+	if (ecu->ac_state == STATE_AC_SA_CLAIMED)
+		name = ecu->name;
+	else
+		name = 0;
+	read_unlock_bh(&ecu->ac_lock);
+
+	put_j1939_ecu(ecu);
+	return name;
+}
+
+static int ac_process_outgoing_address_claim(struct j1939_sk_buff_cb *sk_addr,
+		unsigned char *data, unsigned int len)
+{
+	int err = 0;
+	struct j1939_ecu *ecu;
+
+	if (len != 8) {
+		j1939_ac_warning("tx: invalid data length\n");
+		return -EPROTO;
+	}
+
+	if (sk_addr->src.name != CANDATA2NAME(data)) {
+		j1939_ac_warning("tx: src name (0x%016llx) != "
+				"data (0x%016llx)\n",
+				(long long)sk_addr->src.name,
+				(long long)CANDATA2NAME(data));
+		return -EPROTO;
+	}
+
+	if (sk_addr->src.addr == BROADCAST) {
+		j1939_ac_warning("tx: address claim with broadcast sa\n");
+		return -EPROTO;
+	}
+
+	/* ac must always be a broadcast */
+	if (sk_addr->dst.addr != BROADCAST) {
+		j1939_ac_warning("tx: address claim with da, not broadcast\n");
+		return -EPROTO;
+	}
+
+	/* if no matching ecu (name, ifindex) is found
+	 * then ecu will be created at the receive path of the AC msg. */
+	ecu = j1939_ecu_find_by_name(sk_addr->src.name, sk_addr->ifindex);
+
+	if (ecu) {
+		ac_sm_step(ecu, EVENT_AC_TX, sk_addr->src.addr);
+		put_j1939_ecu(ecu);
+	}
+	return err;
+}
+
+int j1939_send_address_claim(struct sk_buff *skb)
+{
+	int ret;
+	int sa;
+	struct j1939_sk_buff_cb *sk_addr = (struct j1939_sk_buff_cb *)skb->cb;
+
+	/*
+	 * network mgmt: address claiming msgs
+	 */
+	if (sk_addr->pgn == PGN_ADDRESS_CLAIMED) {
+		ret = ac_process_outgoing_address_claim(sk_addr, skb->data,
+				skb->len);
+		if (ret < 0)
+			j1939_ac_warning("tx drop: invalid address claim\n");
+		/* return both when failure & when successfull */
+		return ret;
+	}
+
+	/*
+	 * assign source address
+	 */
+	if (sk_addr->src.name) {
+		sa = ac_name2sa(sk_addr->src.name, sk_addr->ifindex,
+				1/* check block tx*/);
+		if (sa >= 0)
+			sk_addr->src.addr = sa;
+		else if (ac_msg_is_request_for_address_claimed(sk_addr->pgn,
+					skb->data, skb->len))
+			sk_addr->src.addr = NULL_ADDRESS;
+		else {
+			j1939_ac_warning("tx drop: invalid sa for name "
+					"0x%016llx\n", sk_addr->src.name);
+			return -EADDRNOTAVAIL;
+		}
+	}
+
+	/*
+	 * assign destination address
+	 */
+	if (sk_addr->dst.name) {
+		sa = ac_name2sa(sk_addr->dst.name, sk_addr->ifindex, 0);
+		if (sa >= 0) {
+			sk_addr->dst.addr = sa;
+		} else {
+			j1939_ac_warning("tx drop: invalid da for name "
+					"0x%016llx\n", sk_addr->dst.name);
+			return -EADDRNOTAVAIL;
+		}
+	}
+	return 0;
+}
+
+static struct j1939_ecu *ac_ecu_get(name_t name, int ifindex, uint8_t new_sa)
+{
+	struct j1939_ecu *ecu;
+	int ret;
+
+	ecu = j1939_ecu_find_by_name(name, ifindex);
+	if (ecu)
+		return ecu;
+
+	ecu = j1939_ecu_alloc(ifindex, name);
+	if (!ecu) {
+		j1939_ac_err("rx: failed to alloc ecu\n");
+		ret = -ENOMEM;
+		goto fail;
+	}
+	ecu->flags |= ECUFLAG_REMOTE;
+	ecu->sa = new_sa;
+	ret = j1939_ecu_register(ecu);
+	if (ret < 0) {
+		j1939_ac_err("rx: failed to register ecu\n");
+		goto fail_free;
+	}
+
+	if (!get_j1939_ecu(ecu)) {
+		ret = -EINVAL;
+		j1939_ac_err("rx: failed to incr ref of ecu\n");
+		goto fail_unreg;
+	}
+
+	return ecu;
+
+fail_unreg:
+	j1939_ecu_unregister(ecu);
+fail_free:
+	j1939_ecu_free(ecu);
+fail:
+	return ERR_PTR(ret);
+}
+
+/*
+ * Update the ac state of an ecu and resolves conflicts.
+ */
+static void ac_ecu_update(struct j1939_ecu *ecu, int new_sa)
+{
+	int event = EVENT_AC_MAX;
+
+	if (new_sa == NULL_ADDRESS) {
+		/* cannot claim */
+		event = EVENT_AC_SA_CC;
+	} else {
+		/* claim for sa => resolve conflicts */
+		int ev_conflict = EVENT_AC_MAX;
+		struct j1939_ecu *ecu_conflict;
+
+		ecu_conflict = j1939_ecu_find_by_addr(new_sa, ecu->ifindex);
+		if (ecu_conflict == ecu) {
+			put_j1939_ecu(ecu_conflict);
+			ecu_conflict = NULL;
+		}
+
+		if (!ecu_conflict) {
+			event = EVENT_AC_SA_CL;
+		} else if (!ecu_conflict->name) {
+			/*
+			 * a local, static ecu was using this address
+			 * This is stupid.
+			 * There's no point in contesting the address,
+			 * assume noone had claimed it
+			 * (which is in fact, true).
+			 * Both Pieter & Kurt are not really sure
+			 * EVENT_AC_SA_CL is the right thing to do here.
+			 */
+			j1939_ac_err("bus conflict on 0x%02x "
+					"with static ECU\n", new_sa);
+			event = EVENT_AC_SA_CL;
+		} else if (ecu->name < ecu_conflict->name) {
+			ev_conflict = EVENT_AC_SA_CC;
+			event = EVENT_AC_SA_CL;
+		} else if (ecu_conflict->name < ecu->name) {
+			ev_conflict = EVENT_AC_SA_CL;
+			event = EVENT_AC_SA_CC;
+		} else {
+			if (ecu_conflict)
+				put_j1939_ecu(ecu_conflict);
+			j1939_ac_err("2 ecu's with same name on bus?\n");
+			return;
+		}
+		if (ecu_conflict && ecu_conflict->name)
+			ac_sm_step(ecu_conflict, ev_conflict, new_sa);
+		if (ecu_conflict)
+			put_j1939_ecu(ecu_conflict);
+	}
+	ac_sm_step(ecu, event, new_sa);
+}
+
+static int ac_process_incoming_address_claim(struct j1939_sk_buff_cb *sk_addr,
+		unsigned char *data, unsigned int len)
+{
+	struct j1939_ecu *ecu;
+	name_t name;
+
+	if (len < 8) {
+		j1939_ac_warning("rx: address claim with dlc < 8\n");
+		return -EPROTO;
+	}
+
+	name = CANDATA2NAME(data);
+	if (!name) {
+		j1939_ac_warning("rx: address claim without name\n");
+		return -EPROTO;
+	}
+
+	if (!j1939_address_is_valid(sk_addr->src.addr)) {
+		j1939_ac_warning("rx: address claim with broadcast sa\n");
+		return -EPROTO;
+	}
+
+	sk_addr->src.name = name;
+	ecu = ac_ecu_get(name, sk_addr->ifindex, sk_addr->src.addr);
+	if (IS_ERR(ecu))
+		return PTR_ERR(ecu);
+
+	sk_addr->src.flags = ecu->flags;
+	ecu->rxtime = ktime_get();
+	ac_ecu_update(ecu, sk_addr->src.addr);
+
+	put_j1939_ecu(ecu);
+	return 0;
+}
+
+int j1939_recv_address_claim(struct sk_buff *skb)
+{
+	struct j1939_sk_buff_cb *sk_addr = (struct j1939_sk_buff_cb *)skb->cb;
+
+	/*
+	 * network mgmt
+	 */
+	if (sk_addr->pgn == PGN_ADDRESS_CLAIMED) {
+		int ret;
+		ret = ac_process_incoming_address_claim(sk_addr,
+				skb->data, skb->len);
+		if (ret)
+			return ret;
+	} else if (j1939_address_is_unicast(sk_addr->src.addr)) {
+		/*
+		 * assign source name
+		 */
+		sk_addr->src.name =
+			ac_sa2name(sk_addr->src.addr, sk_addr->ifindex);
+	}
+
+	/*
+	 * assign dest name
+	 */
+	if (j1939_address_is_unicast(sk_addr->dst.addr))
+		sk_addr->dst.name =
+			ac_sa2name(sk_addr->dst.addr, sk_addr->ifindex);
+
+	return 0;
+}
+
+enum hrtimer_restart j1939_ac_timer_handler(struct hrtimer *hrtimer)
+{
+	struct j1939_ecu *ecu =
+		container_of(hrtimer, struct j1939_ecu, ac_timer);
+
+	ac_sm_step(ecu, EVENT_AC_TIMEOUT, ecu->sa);
+	return HRTIMER_NORESTART;
+}
+
diff --git a/net/can/j1939/bus.c b/net/can/j1939/bus.c
new file mode 100644
index 0000000..78973c2
--- /dev/null
+++ b/net/can/j1939/bus.c
@@ -0,0 +1,533 @@
+/*
+ * 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)->ifindex, (ecu)->name, \
+		((ecu)->sa >= 0) ? (ecu)->sa : J1939_NO_ADDR, ##__VA_ARGS__)
+#define ecu_alert(ecu, fmt, ...) \
+	pr_alert("j1939-%i,%016llx,%02x: " fmt, (ecu)->ifindex, (ecu)->name, \
+		((ecu)->sa >= 0) ? (ecu)->sa : J1939_NO_ADDR, ##__VA_ARGS__)
+
+static inline
+struct j1939_segment *j1939_ecu_to_segment(struct j1939_ecu *ecu)
+{
+	return ecu->parent;
+}
+
+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;
+}
+
+/*
+ * segment device interface
+ */
+static inline void jseg_release(struct j1939_segment *jseg)
+{
+	kfree(jseg);
+}
+static void cb_put_j1939_segment(struct kref *kref)
+{
+	jseg_release(container_of(kref, struct j1939_segment, kref));
+}
+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;
+	}
+	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
+ */
+void j1939_ecu_free(struct j1939_ecu *ecu)
+{
+	hrtimer_try_to_cancel(&ecu->ac_timer);
+	kfree(ecu);
+}
+EXPORT_SYMBOL_GPL(j1939_ecu_free);
+
+static void cb_put_j1939_ecu(struct kref *kref)
+{
+	j1939_ecu_free(container_of(kref, struct j1939_ecu, kref));
+}
+void put_j1939_ecu(struct j1939_ecu *ecu)
+{
+	kref_put(&ecu->kref, cb_put_j1939_ecu);
+}
+
+struct j1939_ecu *j1939_ecu_alloc(int ifindex, name_t name)
+{
+	struct j1939_ecu *ecu;
+
+	if (!name) {
+		pr_alert("%s(%i, 0), no name\n", __func__, ifindex);
+		return NULL;
+	}
+	ecu = kzalloc(sizeof(*ecu), GFP_KERNEL);
+	if (!ecu)
+		return NULL;
+	kref_init(&ecu->kref);
+	ecu->sa = J1939_IDLE_ADDR;
+	ecu->name = name;
+	ecu->ifindex = ifindex;
+
+	/* address claiming init */
+	hrtimer_init(&ecu->ac_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	ecu->ac_timer.function = j1939_ac_timer_handler;
+	rwlock_init(&ecu->ac_lock);
+
+	INIT_LIST_HEAD(&ecu->list);
+	return ecu;
+}
+EXPORT_SYMBOL_GPL(j1939_ecu_alloc);
+
+int j1939_ecu_register(struct j1939_ecu *ecu)
+{
+	int ret;
+	struct j1939_segment *parent;
+	struct list_head *lst;
+	struct j1939_ecu *dut;
+
+	if (ecu->ifindex < 0) {
+		ecu_alert(ecu, "register with no ifindex\n");
+		return -EINVAL;
+	}
+	parent = j1939_segment_find(ecu->ifindex);
+	if (!parent) {
+		ecu_alert(ecu, "parent not found\n");
+		return -EINVAL;
+	}
+	ecu->parent = parent;
+
+	/* first add to internal list */
+	write_lock_bh(&parent->lock);
+	if (ecu->name) {
+		/* test for duplicate name */
+		list_for_each(lst, &ecu->list) {
+			dut = list_entry(lst, struct j1939_ecu, list);
+			if (dut->name == ecu->name) {
+				ecu_alert(ecu, "duplicate name\n");
+				ret = -EEXIST;
+				goto fail_duplicate;
+			}
+		}
+	}
+	list_add_tail(&ecu->list, &parent->ecus);
+	if (j1939_address_is_unicast(ecu->sa))
+		parent->ents[ecu->sa].ecu = ecu;
+	write_unlock_bh(&parent->lock);
+
+	put_j1939_segment(parent);
+	ecu_dbg(ecu, "register\n");
+	return 0;
+
+fail_duplicate:
+	write_unlock_bh(&parent->lock);
+
+	put_j1939_segment(parent);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(j1939_ecu_register);
+
+void j1939_ecu_unregister(struct j1939_ecu *ecu)
+{
+	struct j1939_segment *parent;
+
+	if (!ecu)
+		return;
+
+	ecu_dbg(ecu, "unregister\n");
+	parent = j1939_ecu_to_segment(ecu);
+	get_j1939_segment(parent);
+	j1939_ecu_set_address(ecu, -1);
+	write_lock_bh(&parent->lock);
+	list_del_init(&ecu->list);
+	write_unlock_bh(&parent->lock);
+	put_j1939_ecu(ecu);
+	put_j1939_segment(parent);
+}
+EXPORT_SYMBOL_GPL(j1939_ecu_unregister);
+
+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;
+}
+EXPORT_SYMBOL_GPL(j1939_ecu_find_by_addr);
+
+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;
+}
+EXPORT_SYMBOL_GPL(j1939_ecu_find_segment_default_tx);
+
+/* find ecu in segment (having segment's kref & readlock) */
+static struct j1939_ecu *_j1939_segment_find_name_masked(
+		name_t name, name_t mask, struct j1939_segment *parent)
+{
+	struct j1939_ecu *ecu;
+
+	if (!name)
+		return NULL;
+	if (!parent)
+		return NULL;
+	name &= mask;
+	list_for_each_entry(ecu, &parent->ecus, list) {
+		if ((ecu->name & mask) == name)
+			return get_j1939_ecu(ecu);
+	}
+	return NULL;
+}
+/* find ecu in segment (having segment's kref) */
+static struct j1939_ecu *j1939_segment_find_name_masked(
+		name_t name, name_t mask, struct j1939_segment *parent)
+{
+	struct j1939_ecu *ecu;
+
+	if (!name || !parent)
+		return NULL;
+	read_lock_bh(&parent->lock);
+	ecu = _j1939_segment_find_name_masked(name, mask, parent);
+	read_unlock_bh(&parent->lock);
+	return ecu;
+}
+struct j1939_ecu *j1939_ecu_find_name_masked(name_t name, name_t mask,
+		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_segment_find_name_masked(name, mask, 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_segment_find_name_masked(name, mask, jseg);
+		put_j1939_segment(jseg);
+		if (ecu)
+			goto found;
+	}
+	ecu = NULL;
+found:
+	spin_unlock_bh(&segments.lock);
+	return ecu;
+}
+EXPORT_SYMBOL_GPL(j1939_ecu_find_name_masked);
+
+static void _clear_ecu_flags(struct j1939_ecu *ecu, struct addr_ent *ent)
+{
+	int clearflags = ECUFLAG_LOCAL | ECUFLAG_REMOTE;
+
+	/*
+	 * when the SA is a static one too,
+	 * prevent it from getting cleared.
+	 */
+	clearflags = ECUFLAG_REMOTE |
+		((ent->flags & ECUFLAG_STATIC) ? 0 : ECUFLAG_LOCAL);
+	ent->flags &= ~(ecu->flags & clearflags);
+}
+
+int j1939_ecu_set_address(struct j1939_ecu *ecu, int sa)
+{
+	struct j1939_segment *parent = j1939_ecu_to_segment(ecu);
+	struct addr_ent *ent;
+
+	write_lock_bh(&parent->lock);
+	if (j1939_address_is_unicast(ecu->sa)) {
+		ent = &parent->ents[ecu->sa];
+		if (ent->ecu == ecu) {
+			ent->ecu = NULL;
+			_clear_ecu_flags(ecu, ent);
+		}
+	}
+	if (j1939_address_is_unicast(sa)) {
+		ent = &parent->ents[ecu->sa];
+		if (ent->ecu) {
+			ent->ecu->sa = J1939_IDLE_ADDR;
+			_clear_ecu_flags(ecu, ent);
+		}
+		ent->ecu = ecu;
+		ent->flags |= ecu->flags;
+	}
+	ecu->sa = sa;
+	write_unlock_bh(&parent->lock);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(j1939_ecu_set_address);
+
+/* 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;
+	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) {
+			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,
+				(paddr->flags & ECUFLAG_STATIC) ? 'S' : '-',
+				(paddr->flags & ECUFLAG_LOCAL) ? 'L' : '-',
+				(paddr->flags & ECUFLAG_REMOTE) ? 'R' : '-',
+				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 (ecu->sa < 0)
+				strcpy(sa, "-");
+			else
+				snprintf(sa, sizeof(sa), "%02x", ecu->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..47fc52c
--- /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(&j1939_receiver_rwlock);
+	list_for_each_entry(filter, &filters, list)
+		filter->fn(skb, filter->vp);
+	read_unlock(&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..64008e9
--- /dev/null
+++ b/net/can/j1939/j1939-priv.h
@@ -0,0 +1,287 @@
+/*
+ * 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 ECUFLAG_STATIC	0x04
+/*
+ * j1939 devices
+ */
+struct j1939_ecu {
+	struct list_head list;
+	name_t		name;
+	int		flags;
+	ktime_t		rxtime;
+	int		ifindex;
+	int		sa;
+	int		ac_state;
+	struct hrtimer	ac_timer;
+	uint8_t		block_tx; /*
+	 * Outgoing traffic can be blocked by an outgoing Address Claim msg,
+	 * The Address Claim msg itself is processed
+	 * when it's received again via CAN intf echo.
+	 * State changes (ac_state) are only done at rx path.
+	 */
+	rwlock_t	ac_lock; /*
+	 * protects members which are updated by address claiming logic:
+	 * sa, ac_state, ac_timer, block_tx
+	 */
+	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 {
+		struct j1939_ecu *ecu;
+		int flags;
+		ktime_t rxtime;
+	} ents[256];
+	/*
+	 * 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(int sa)
+{
+	return (sa >= 0) && (sa < 0xff);
+}
+
+static inline int j1939_address_is_unicast(int sa)
+{
+	return (sa >= 0) && (sa < 0xfe);
+}
+
+static inline int pgn_is_pdu1(pgn_t pgn)
+{
+	return (pgn & 0xff00) < 0xf000;
+}
+
+#define NO_PGN	0x40000
+static inline int pgn_is_valid(pgn_t pgn)
+{
+	return (pgn < NO_PGN);
+}
+
+extern struct j1939_ecu *j1939_ecu_find_by_addr(int sa, int ifindex);
+extern struct j1939_ecu *j1939_ecu_find_name_masked(
+		name_t name, name_t mask, int ifindex);
+static inline struct j1939_ecu *j1939_ecu_find_by_name(name_t name, int ifindex)
+{
+	return j1939_ecu_find_name_masked(name, 0xffffffffffffffffULL, ifindex);
+}
+/* find_by_name, with kref & read_lock taken */
+extern int j1939_ecu_set_address(struct j1939_ecu *, int sa);
+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 */
+extern struct j1939_ecu *j1939_ecu_alloc(int ifindex, name_t name);
+extern void j1939_ecu_free(struct j1939_ecu *); /*
+	 * should no be used for registered ecu's
+	 */
+extern int j1939_ecu_register(struct j1939_ecu *);
+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);
+
+/* address claiming timer callback */
+enum hrtimer_restart j1939_ac_timer_handler(struct hrtimer *hrtimer);
+/*
+ * 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..71963d5
--- /dev/null
+++ b/net/can/j1939/main.c
@@ -0,0 +1,454 @@
+/*
+ * 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);
+	paddr = &jseg->ents[cb->src.addr];
+	paddr->rxtime = ktime_get();
+	if (paddr->ecu) {
+		cb->src.flags = paddr->ecu->flags;
+		paddr->ecu->rxtime = paddr->rxtime;
+	} 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 {
+			if (!paddr->flags)
+				paddr->flags |= ECUFLAG_REMOTE;
+			cb->dst.flags = paddr->flags;
+		}
+	}
+	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);
+		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..3345b51
--- /dev/null
+++ b/net/can/j1939/rtnl.c
@@ -0,0 +1,314 @@
+/*
+ * 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 & ECUFLAG_STATIC) {
+			ent->flags &= ~ECUFLAG_STATIC;
+			if (!ent->ecu || !(ent->ecu->flags & ECUFLAG_LOCAL))
+				ent->flags &= ~ECUFLAG_LOCAL;
+			ret = 0;
+		} else {
+			ret = -EADDRNOTAVAIL;
+		}
+		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);
+				j1939_ecu_free(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->flags & ECUFLAG_REMOTE)
+			ret = -EREMOTE;
+		else
+			ent->flags |= ECUFLAG_LOCAL | ECUFLAG_STATIC;
+		write_unlock_bh(&jseg->lock);
+	} else if (jname) {
+		struct j1939_ecu *ecu;
+
+		ecu = j1939_ecu_find_by_name(jname, ifm->ifa_index);
+		if (ecu) {
+			put_j1939_ecu(ecu);
+			ret = -EBUSY;
+		} else {
+			ecu = j1939_ecu_alloc(ifm->ifa_index, jname);
+			if (ecu) {
+				ecu->flags |= ECUFLAG_LOCAL;
+				ret = j1939_ecu_register(ecu);
+			} else
+				ret = -ENOMEM;
+		}
+	}
+	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;
+	struct net_device *netdev;
+	struct j1939_segment *jseg;
+	struct j1939_ecu *ecu;
+	struct addr_ent *ent;
+	struct list_head *lst;
+
+	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 & ECUFLAG_STATIC))
+				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(lst, &jseg->ecus) {
+			ecu = list_entry(lst, struct j1939_ecu, list);
+			if (addr++ < cb->args[2])
+				continue;
+			if (!(ecu->flags & ECUFLAG_LOCAL))
+				continue;
+			ret = j1939rtnl_fill_ifaddr(skb, netdev->ifindex,
+					ecu->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..c94e3b1
--- /dev/null
+++ b/net/can/j1939/socket.c
@@ -0,0 +1,983 @@
+/*
+ * 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, mask;
+		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)
+{
+	/*
+	 * 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))
+			return 0;
+		if ((cb->src.addr & f->addr_mask) != (f->addr & f->addr_mask))
+			return 0;
+		if ((cb->src.name & f->name_mask) != (f->name & f->name_mask))
+			return 0;
+	}
+	return 1;
+}
+
+/*
+ * 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);
+		j1939_warning("sock_queue_rcv_skb failed\n");
+	}
+}
+
+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;
+	/* per default, mask out the serial number in destinations */
+	jsk->addr.mask = 0xffffffffffe00000ULL;
+
+	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 != 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;
+		j1939_recv_suspend();
+		/* set to be able to send address claims */
+		jsk->addr.sa = addr->can_addr.j1939.addr;
+		j1939_recv_resume();
+		/* 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->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_STATIC)) {
+			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_name_masked(addr->can_addr.j1939.name,
+			jsk->addr.mask, ifindex);
+		if (!ecu) {
+			ret = -EADDRNOTAVAIL;
+			goto fail_locked;
+		}
+		if (ifindex && (ifindex != ecu->ifindex)) {
+			ret = -EHOSTUNREACH;
+			goto fail_locked;
+		}
+		ifindex = ecu->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 = 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();
+		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;
+	case SO_J1939_DEST_MASK:
+		lock_sock(&jsk->sk);
+		if (optlen != sizeof(jsk->addr.mask))
+			ret = -EINVAL;
+		else if (jsk->state & JSK_CONNECTED)
+			ret = -EBUSY;
+		else if (copy_from_user(&jsk->addr.mask, optval, optlen))
+			ret = -EFAULT;
+		else
+			ret = 0;
+		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;
+	case SO_J1939_DEST_MASK:
+		val = &jsk->addr.mask;
+		len = sizeof(jsk->addr.mask);
+		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_name_masked(
+				addr->can_addr.j1939.name, jsk->addr.mask,
+				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 list_head *lst;
+	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(lst, &s.socks) {
+		jsk = list_entry(lst, struct j1939_sock, 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);
+		seq_printf(sqf, ",%02x\t", jsk->addr.sa);
+		if (jsk->addr.dst)
+			seq_printf(sqf, "%016llx", (long long)jsk->addr.dst);
+		seq_printf(sqf, "/%016llx", (long long)jsk->addr.mask);
+		seq_printf(sqf, ",%02x\t", jsk->addr.da);
+		seq_printf(sqf, "%05x\t", jsk->addr.pgn);
+		seq_printf(sqf, "%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 list_head *lst;
+	struct j1939_sock *sock;
+
+	mutex_lock(&s.lock);
+	list_for_each(lst, &s.socks) {
+		sock = list_entry(lst, struct j1939_sock, list);
+		if (sock->sk.sk_bound_dev_if != ifindex)
+			continue;
+		sock->sk.sk_err = error_code;
+		if (!sock_flag(&sock->sk, SOCK_DEAD))
+			sock->sk.sk_error_report(&sock->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_sock_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..5e39c62
--- /dev/null
+++ b/net/can/j1939/transport.c
@@ -0,0 +1,1481 @@
+/*
+ * 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;
+	struct tasklet_struct txtask;
+};
+
+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 session *list_to_session(struct list_head *lst)
+{
+	return container_of(lst, struct session, list);
+}
+
+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->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->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 list_head *lst;
+	struct session *session;
+
+	list_for_each(lst, root) {
+		session = list_to_session(lst);
+		get_session(session);
+		if (j1939tp_match(cb, session->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
+ *
+ * When PGN >= NO_PGN, take the 'related' pgn
+ */
+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);
+	/* fix pgn */
+	if (!pgn_is_valid(pgn))
+		/* save related pgn */
+		pgn = rel_cb->pgn;
+	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,
+			NO_PGN, dat);
+}
+
+static int j1939xtp_tx_abort(struct sk_buff *related, int extd,
+		int swap_src_dst, pgn_t pgn, int err)
+{
+	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),
+				NO_PGN, err);
+		}
+	}
+	j1939session_drop(session);
+}
+
+static enum hrtimer_restart j1939tp_rxtimer(struct hrtimer *hrtimer)
+{
+	struct session *session =
+		container_of(hrtimer, struct session, rxtimer);
+	pr_alert("%s: timeout on %i\n", __func__, session->cb->ifindex);
+	j1939session_cancel(session, ABORT_TIMEOUT);
+	return HRTIMER_NORESTART;
+}
+
+/*
+ * 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, cb->pgn);
+	_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;
+	ktime_t ktime;
+
+	get_session(session);
+	ret = j1939tp_txnext(session);
+	if (ret < 0) {
+		if (ret == -EADDRNOTAVAIL) {
+			/*
+			 * during address claiming delays, postpone
+			 * the rxtimer too
+			 */
+			ktime = hrtimer_get_remaining(&session->rxtimer);
+			if ((ktime_to_ns(ktime)*NSEC_PER_MSEC) < (retry_ms+20))
+				j1939tp_set_rxtimeout(session, retry_ms+20);
+		}
+		j1939tp_schedule_txtimer(session, retry_ms);
+	}
+	put_session(session);
+}
+
+static inline int j1939tp_tx_initial(struct session *session)
+{
+	int ret;
+
+	get_session(session);
+	while (1) {
+		ret = j1939tp_txnext(session);
+		if (!ret)
+			break;
+		else if (session->cb->msg_flags & MSG_DONTWAIT)
+			break;
+		else if (ret == -EADDRNOTAVAIL) {
+			/* address claim delay */
+			if (signal_pending(current)) {
+				ret = -ERESTARTSYS;
+				break;
+			}
+			msleep(retry_ms);
+		}
+	}
+
+	/* clear MSG_DONTWAIT flag here, it makes no sense from this point on */
+	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);
+	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;
+	struct list_head *lst, *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_safe(lst, saved, &s.sessionq) {
+		session = list_to_session(lst);
+		if (session->cb->ifindex != netdev->ifindex)
+			continue;
+		list_del_init(&session->list);
+		put_session(session);
+	}
+	list_for_each_safe(lst, saved, &s.extsessionq) {
+		session = list_to_session(lst);
+		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 list_head *lst;
+
+	seq_printf(sqf, "iface\tsrc\tdst\tpgn\tdone/total\n");
+	sessionlist_lock();
+	list_for_each(lst, &s.sessionq)
+		j1939tp_proc_show_session(sqf, list_to_session(lst));
+	list_for_each(lst, &s.extsessionq)
+		j1939tp_proc_show_session(sqf, list_to_session(lst));
+	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;
+
+	wake_up_all(&s.wait);
+
+	unregister_sysctl_table(j1939tp_table_header);
+	unregister_netdevice_notifier(&s.notifier);
+	j1939_proc_remove("transport");
+	sessionlist_lock();
+	while (!list_empty(&s.extsessionq)) {
+		session = list_to_session(s.extsessionq.next);
+		list_del_init(&session->list);
+		put_session(session);
+	}
+	while (!list_empty(&s.sessionq)) {
+		session = list_to_session(s.sessionq.next);
+		list_del_init(&session->list);
+		put_session(session);
+	}
+	sessionlist_unlock();
+	flush_scheduled_work();
+}
+

^ permalink raw reply related

* [RFC v3 5/6] j1939: add documentation and MAINTAINERS
From: Kurt Van Dijck @ 2011-03-14 13:59 UTC (permalink / raw)
  To: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
	netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20110314132004.GA333-MxZ6Iy/zr/UdbCeoMzGj59i2O/JbrIOy@public.gmane.org>

This patch adds the documentation and MAINTAINERS.
This is seperated from 4/6 for size constraints
on berlios' SocketCAN mailing list.

Signed-off-by: Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
---
diff --git a/Documentation/networking/j1939.txt b/Documentation/networking/j1939.txt
new file mode 100644
index 0000000..690e0a0
--- /dev/null
+++ b/Documentation/networking/j1939.txt
@@ -0,0 +1,532 @@
+============================================================================
+
+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.3 dynamic addressing
+
+  5 socket options
+    5.1 SO_J1939_FILTER
+    5.2 SO_J1939_PROMISC
+    5.3 SO_J1939_RECV_OWN
+    5.4 SO_J1939_RECV_DEST
+    5.5 SO_J1939_RECV_PRIO
+    5.6 SO_J1939_SEND_PRIO
+    5.7 SO_J1939_DEST_MASK
+
+  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.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.
+
+
+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_RECV_DEST
+
+  Received j1939 packets that make their way up to the socket, had a destination
+  address matching the socket's local address. This can have several reasons:
+  - broadcast packet.
+  - destination spec matches the local address
+  - destination spec matches _a_ local address on the system, and the socket
+    had no local address defined.
+  - SO_J1939_PROMISC was set
+  If the user is interested in the original destination spec, SO_J1939_RECV_DEST
+  can be changed to 1 to receive the destination spec with each packet.
+  The destination is 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.
+  the returned data is a struct sockaddr_can, with the regular j1939 fields
+  stuffed. Mind that PGN is the same as the PGN in sockaddr after recvfrom.
+
+
+5.5 SO_J1939_RECV_PRIO
+
+  As stated earlier, the priority field is stripped very soon. In order to
+  allow retreiving the packet's priority, SO_J1939_RECV_PRIO can be set to 1.
+
+  As a result, an extra int will be attached during recvmsg(2), similar
+  as in SO_J1939_RECV_DEST, but with cmsg_type == SCM_J1939_PRIO
+
+6.6 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.
+
+5.7 SO_J1939_DEST_MASK
+
+  When a destination is specified by its name (and thus using dynamic addressing),
+  and such name should be unique amongst the world, it may be hard to predict the
+  name of eg. a gearbox controller on the bus, although its type and manufacturer
+  are know. This is because the serial number is part of the name.
+  To simplify specifying a destination, a per-socket destination mask is provided
+  that is activated whenever a destination name is wanted. Any bits cleared in
+  this mask are ignored during the lookup. As a result, more than 1 ECU may match
+  this name/mask pair. In all cases, the first match is used.
+  The API is thus capable of specifying a name for eg. the gearbox controller,
+  without knowing its serial number.
+  This mask can mask out any part in the name. Note there's only 1 mask per socket.
+
+  this mask is default set to mask the serial number.
+
+  when can_addr.j1939.name is used for destination in outgoing packets
+  (see bind(2), sendto(2)), the exact name is often not known due to serial
+  numbers in it.
+  Therefore, SO_J1939_DEST_MASK sets an uint64_t mask that will be used
+  for resolving these names. Only the bits set to 1 in the mask will be
+  evaluated for find the destination name.
+  Per default, the mask is set to mask out the serial number
+  (0xffffffffffe00000ULL)
+
+  to mask out only the manufacturer code (bits 21-31), do
+
+    uint64_t mask = 0xffffffff001fffffULL;
+
+    setsockopt(s, SOL_CAN_J1939, SO_J1939_DEST_MASK, &mask, sizeof(mask));
+
+
+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 75760e7..a7d7c17 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1631,6 +1631,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

^ permalink raw reply related

* [RFC v3 6/6] iproute2: add CAN and J1939 rtnetlink support
From: Kurt Van Dijck @ 2011-03-14 14:05 UTC (permalink / raw)
  To: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
	netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20110314132004.GA333-MxZ6Iy/zr/UdbCeoMzGj59i2O/JbrIOy@public.gmane.org>

Add iproute2 support for CAN and J1939 rtnetlink.

Signed-off-by: Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
---
diff --git a/Makefile b/Makefile
index c03d74c..efd06de 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 3da6998..b6344d5 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 a775ecd..09db9fd 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"
@@ -68,6 +69,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");
@@ -77,6 +80,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);
 }
@@ -421,6 +428,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;
 }
@@ -445,6 +465,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)
 {
@@ -452,6 +479,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];
@@ -471,10 +499,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;
@@ -536,38 +566,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)));
@@ -1091,12 +1147,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;
@@ -1202,8 +1264,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 cb2c4f5..06fc34c 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"
@@ -71,6 +72,7 @@ void iplink_usage(void)
 	fprintf(stderr, "	                  [ vf NUM [ mac LLADDR ]\n");
 	fprintf(stderr, "				   [ vlan VLANID [ qos VLAN-QOS ] ]\n");
 	fprintf(stderr, "				   [ rate TXRATE ] ] \n");
+	fprintf(stderr, "			  [ j1939 { on | off } ]\n");
 	fprintf(stderr, "       ip link show [ DEVICE ]\n");
 
 	if (iplink_have_newlink()) {
@@ -383,6 +385,12 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req,
 				  *argv, strlen(*argv));
 			argc--; argv++;
 			break;
+		} 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 a60d884..02dda80 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"
@@ -513,7 +514,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:
@@ -527,6 +529,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 8d55fa9..d17e06a 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
@@ -95,7 +95,9 @@ ip \- show / manipulate routing, devices, policy routing and tunnels
 .B qos
 .IR VLAN-QOS " ] ] ["
 .B rate
-.IR TXRATE " ]"
+.IR TXRATE " ] |"
+.br
+.BR j1939 " { " on " | " off " }"
 
 .ti -8
 .B ip link show
@@ -126,7 +128,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 " := "
@@ -142,6 +146,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
@@ -1038,6 +1051,11 @@ Setting this parameter to 0 disables rate limiting. The
 parameter must be specified.
 .in -8
 
+.TP
+.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:
 If multiple parameter changes are requested,

^ permalink raw reply related

* Re: [RFC v3 3/6] can: make struct proto const
From: Eric Dumazet @ 2011-03-14 14:09 UTC (permalink / raw)
  To: Kurt Van Dijck; +Cc: socketcan-core, netdev
In-Reply-To: <20110314134721.GD333@e-circ.dyndns.org>

Le lundi 14 mars 2011 à 14:47 +0100, Kurt Van Dijck a écrit :
> can_ioctl is the only reason for struct proto to be non-const.
> script/check-patch.pl suggests struct proto be const.
> This patch performs the necessary change.
> 
> Signed-off-by: Kurt Van Dijck <kurt.van.dijck@eia.be>
> ---
> diff --git a/include/linux/can/core.h b/include/linux/can/core.h
> index 430c446..0767cc6 100644
> --- a/include/linux/can/core.h
> +++ b/include/linux/can/core.h
> @@ -39,7 +39,7 @@
>  struct can_proto {
>  	int              type;
>  	int              protocol;
> -	struct proto_ops *ops;
> +	const struct proto_ops *ops;
>  	struct proto     *prot;
>  
>  	const struct rtnl_af_ops *rtnl_link_ops;
> @@ -78,6 +78,8 @@ struct rtgencanmsg {
>  
>  extern int  can_proto_register(const struct can_proto *cp);
>  extern void can_proto_unregister(const struct can_proto *cp);
> +extern int can_sock_ioctl(struct socket *sock, unsigned int cmd,
> +		unsigned long arg);
>  
>  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 db59c6e..c1f8c05 100644
> --- a/net/can/af_can.c
> +++ b/net/can/af_can.c
> @@ -139,7 +139,7 @@ static inline void can_put_proto(const struct can_proto *cp)
>   * af_can socket functions
>   */
>  
> -static int can_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
> +int can_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
>  {
>  	struct sock *sk = sock->sk;
>  
> @@ -152,6 +152,7 @@ static int can_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
>  		return -ENOIOCTLCMD;
>  	}
>  }
> +EXPORT_SYMBOL(can_sock_ioctl);
>  
>  static void can_sock_destruct(struct sock *sk)
>  {
> @@ -720,10 +721,6 @@ int can_proto_register(const struct can_proto *cp)
>  		err = -EBUSY;
>  	} else {
>  		proto_tab[proto] = cp;
> -
> -		/* use generic ioctl function if not defined by module */
> -		if (!cp->ops->ioctl)
> -			cp->ops->ioctl = can_ioctl;

Hmm, you actually fixed a race / bug, since we installed in proto_tab[]
a pointer to a not yet initted ops structure.

I suggest you change patch title and changelog to properly describe the
bug fix.




^ permalink raw reply

* Re: [PATCH] pktgen: fix transmission headers with frags=0
From: Daniel Turull @ 2011-03-14 14:13 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: netdev, Robert Olsson, Jens Laas, Voravit Tanyingyong
In-Reply-To: <1300110977.3423.16.camel@edumazet-laptop>

On 03/14/2011 02:56 PM, Eric Dumazet wrote:
> Le lundi 14 mars 2011 à 14:22 +0100, Daniel Turull a écrit :
>> The headers of pktgen were incorrectly added in a pktgen packet
>> without frags (frags=0). There was an offset in the pktgen headers.
>>
>> The cause was in reusing the pgh variable as a return variable in skb_put
>> when adding the payload to the skb.
>>
>> A rename of the variable is done.
>>
>> Signed-off-by: Daniel Turull <daniel.turull@gmail.com>
>> ---
>> The PKTGEN magic (be9b e955) now starts in the correct offset.
>> Before the patch, it was starting at the end of the packet (be9b)
>>
> 
> not exactly, but offseted  +16 bytes
> (sizeof(struct pktgen_hdr))
True, I shouldn't say end.

> 
>> Capture from tcpdump:
>>
>> before patch:
>> 14:57:37.854812 IP 10.0.0.2.discard > 10.254.254.84.discard: UDP, length 18
>> 	0x0000:  001b 2157 ed84 001b 215d 01d0 0800 4500
>> 	0x0010:  002e 0004 0000 2011 8767 0a00 0002 0afe
>> 	0x0020:  fe54 0009 0009 001a 0000 0000 0000 b072
>> 	0x0030:  9102 00ea ffff 0010 0000 be9b
>>
>> after patch:
>> 14:44:32.896048 IP 10.0.0.2.discard > 10.217.234.56.discard: UDP, length 18
>> 	0x0000:  001b 2157 ed84 001b 215d 01d0 0800 4500
>> 	0x0010:  002e 0000 0000 2011 9bac 0a00 0002 0ad9
>> 	0x0020:  ea38 0009 0009 001a 0000 be9b e955 0000
>> 	0x0030:  0001 4d7e 1b09 0005 5f23 af00
>>
>> ---
>> diff --git a/net/core/pktgen.c b/net/core/pktgen.c
>> index f0aec6c..5baa9d9 100644
>> --- a/net/core/pktgen.c
>> +++ b/net/core/pktgen.c
>> @@ -2615,13 +2615,14 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
>>  {
>>  	struct timeval timestamp;
>>  	struct pktgen_hdr *pgh;
>> +	void *data;
>>  
>>  	pgh = (struct pktgen_hdr *)skb_put(skb, sizeof(*pgh));
>>  	datalen -= sizeof(*pgh);
>>  
>>  	if (pkt_dev->nfrags <= 0) {
>> -		pgh = (struct pktgen_hdr *)skb_put(skb, datalen);
>> -		memset(pgh + 1, 0, datalen);
>> +		data = skb_put(skb, datalen);
>> +		memset(data + 1, 0, datalen);
>>  	} else {
>>  		int frags = pkt_dev->nfrags;
>>  		int i, len;
>> --
> 
> Good catch !
> 
> Hmm this patch is not correct, why memset(data + 1, ...) ?

I kept the +1 as it was in the original code, but I supposed it can be avoided.

> Also, this patch is needed for net-next-2.6 only
> (bug introduced by commit 26ad787962ef84677a48c560
> (pktgen: speedup fragmented skbs)
> 
> I would avoid the "void *data;" declaration and just use following :
> 
> diff --git a/net/core/pktgen.c b/net/core/pktgen.c
> index f0aec6c..f727c83 100644
> --- a/net/core/pktgen.c
> +++ b/net/core/pktgen.c
> @@ -2620,7 +2620,7 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
>  	datalen -= sizeof(*pgh);
>  
>  	if (pkt_dev->nfrags <= 0) {
> -		pgh = (struct pktgen_hdr *)skb_put(skb, datalen);
> +		skb_put(skb, datalen);
>  		memset(pgh + 1, 0, datalen);
>  	} else {
>  		int frags = pkt_dev->nfrags;
> 
> 
> 
> or even :
> 
> 		memset(skb_put(skb, datalen), 0, datalen);
> 
I think that the second is better and more compact.

Do I resend the new patch?

> 
> 
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox