Linux CAN drivers development
 help / color / mirror / Atom feed
* Re: j1939
From: Marc Kleine-Budde @ 2016-10-04 17:32 UTC (permalink / raw)
  To: David Jander, Austin Schuh, Oliver Hartkopp, linux-can
In-Reply-To: <20161004135701.GA25008@airbook.vandijck-laurijssen.be>


[-- Attachment #1.1: Type: text/plain, Size: 745 bytes --]

On 10/04/2016 03:57 PM, Kurt Van Dijck wrote:
> Recently, someone on this list was confronted with an engine ECU that
> dropped TP that had the last packet not 8 bytes long.
> And I believe I've encountered an issue a long time ago where this
> happened on all PGNs.
> So I introduced this option, having 3 possibilities:
> * no padding
> * padding for TP
> * padding for all PGN.

How to switch between padding for TP only and padding for all PGN?

Marc

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

^ permalink raw reply

* Re: j1939
From: Austin Schuh @ 2016-10-04 16:12 UTC (permalink / raw)
  To: David Jander, Kurt Van Dijck
  Cc: Oliver Hartkopp, Marc Kleine-Budde, linux-can
In-Reply-To: <20161004164725.08802a61@erd980>

On Tue, Oct 4, 2016 at 7:47 AM David Jander <david@protonic.nl> wrote:
>
> I have yet to check, but I fear that in ISOBus conformance tests on some
> places at least this is tested for. I'd say that if the standard says "pad and
> fill with 0xff", any ECU should do this and not doing it should be regarded as
> a bug...
> I know that PGN 0xEA00 is an exception, and proprietary messages should be
> permitted any length IMHO, so padding every frame (even coming from user-space)
> seems unnecessary and unacceptable to me, as it clearly violates the standard.
> Turning off padding for TP-messages might be a nice-to-have feature, but it
> would not be standards-compliant anymore AFAICS.
>
> I guess I don't violate anyone's copyright if I cite a small fragment from
> ISO-11783-3 here:
>
> In "Transport Protocol — Data Transfer messages (TP.DT)":
>   [...]
>   "The last packet of a multi-packet parameter group can require less than
>   8B. The extra bytes shall be filled with 0xFF"
>
> If someone can confirm this is the same language as in J1939, I'd say we don't
> really need to support non-padded (E)TP-packets. Please note that I am only
> referring to (E)TP-packets here!


From SAE-J1939-21

"Data ranges for parameters used by this Group Function:
Sequence Number:
1 to 255 (1 byte)
Byte:
1
Sequence Number
2-8
Packetized Data (7 bytes). Note the last packet of a multipacket
Parameter Group may
require less than 8 data bytes. The extra bytes should be filled with FF 16 ."

J1939-21 matches what is in ISO-11783-3

J1939-21 section 5.2.7.1 says that messages with less than 8 bytes of
data are legal.  The recommendation is to pad messages out to 8 bytes
for compatibility and future proofing reasons, but it is not required.

Austin

^ permalink raw reply

* Re: j1939
From: Marc Kleine-Budde @ 2016-10-04 15:05 UTC (permalink / raw)
  To: Austin Schuh, Oliver Hartkopp, linux-can, David Jander
In-Reply-To: <20161004072820.GA13813@airbook.vandijck-laurijssen.be>


[-- Attachment #1.1: Type: text/plain, Size: 1617 bytes --]

On 10/04/2016 09:28 AM, Kurt Van Dijck wrote:
> Please note my @eia.be address is obsolete now.
> I should investigate why the kbuild robot send mail to that address.
> this (dev.kurt@...) is meant to stay :-)

Maybe it was due to the Author in the git commits. I can (technically)
change the address, don't know about the legal aspects of this, though.

[...]

>> SAE J1939-21 defines all the timing requirements and other constraints
>> pretty well.  Is there a reason to make them configurable?
> 
> The timing parameters are defined pretty well, but sometimes
> light-weight ECU's cannot handle what's defined by the standard.
> Therefore, a sort of rate-limiting may be defined.
> 
> The 8-byte sutffing also is particular. Do you need to send 7 unused
> bytes? The standard says yes, but what happens if you don't.
> So I put module parameters there.
> 
> Oliver idea is what I was heading to, making a lot of those module
> paramters also a socket option, with the module parameters still being
> the default. So, the module parameters do not disappear.

I've a bad feeling about introducing new module parameters to the
kernel. There are ways to configure them properly. Being it a per
interface (netlink) or a per socket option.

> I'm still catching up, I agree with David Jander's comments.

Marc

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

^ permalink raw reply

* Re: j1939
From: David Jander @ 2016-10-04 14:47 UTC (permalink / raw)
  To: Kurt Van Dijck
  Cc: Austin Schuh, Oliver Hartkopp, Marc Kleine-Budde, linux-can
In-Reply-To: <20161004135701.GA25008@airbook.vandijck-laurijssen.be>

On Tue, 4 Oct 2016 15:57:01 +0200
Kurt Van Dijck <dev.kurt@vandijck-laurijssen.be> wrote:

> > 
> > On Tue, 4 Oct 2016 09:37:03 +0200
> > Kurt Van Dijck <dev.kurt@vandijck-laurijssen.be> wrote:
> >   
> > > --- Original message ---  
> > > > Date:   Tue, 20 Sep 2016 09:15:25 +0200
> > > > From: David Jander <david@protonic.nl>
> > > > To: Austin Schuh <austin@peloton-tech.com>
> > > > Cc: Oliver Hartkopp <socketcan@hartkopp.net>, Marc Kleine-Budde
> > > >  <mkl@pengutronix.de>, linux-can <linux-can@vger.kernel.org>, Kurt Van
> > > >  Dijck <kurt.van.dijck@eia.be>
> > > > Subject: Re: j1939
> > > > X-Mailer: Claws Mail 3.13.2 (GTK+ 2.24.30; x86_64-pc-linux-gnu)
> > > > 
> > > > On Mon, 19 Sep 2016 14:37:15 -0700
> > > > Austin Schuh <austin@peloton-tech.com> wrote:
> > > >     
> > > > > On Mon, Sep 19, 2016 at 12:45 PM Oliver Hartkopp <socketcan@hartkopp.net> wrote:    
> > > > > >
> > > > > >
> > > > > >
> > > > > > On 09/19/2016 08:52 PM, Marc Kleine-Budde wrote:      
> > > > > > > On 09/19/2016 08:27 PM, Oliver Hartkopp wrote:      
> > > > > > >> On 09/19/2016 06:54 PM, Marc Kleine-Budde wrote:
> > > > > > >>      
> > > > > > >>>
> > > > > > >>> Another thing that IMHO has to be sorted out is the use of module
> > > > > > >>> parameters:
> > > > > > >>>      
> > > > > > >>>> ./main.c:49:MODULE_PARM_DESC(padding, "Pad all packets to 8 bytes, and stuff with 0xff");    
> > > > 
> > > > This is true only for 99% of all J1939 messages. I need to investigate some
> > > > more, but I fear this option can better be removed. I couldn't think of a
> > > > situation where I'd ponder setting this parameter to true.    
> > > 
> > > There exist engine controller software that adhere to this for the
> > > transport protocol messages. This means, I implemented the transport
> > > protocol to not necessarily use all 8 bytes at the end.  
> > 
> > Do you mean this only applies to TP messages?
> > In that case, the option may make sense, although according to the standard,
> > all TP messages should always be 8-bytes long, so when is it desirable to
> > have non-standard TP-messages? Is this ECU software you mention not
> > standards-compliant? Does it bark on standards-compliant TP messages?  
> 
> From what I remember (I have no legal access to the specs anymore),
> J1939 defines _all_ pgns but 0xea00 to be 8 bytes long.

I do have legal access to ISOBus (ISO-11783), which is supposedly a superset
of J1939.

> But often, especially for proprietary messages, not all 8 bytes are
> used, and the DLC is cut accordingly.
> And less often, this is done for TP too.
> I also did that for as long as I can remember.
> As long as all defined bytes are there, this should not really be a
> problem.
> 
> Recently, someone on this list was confronted with an engine ECU that
> dropped TP that had the last packet not 8 bytes long.
> And I believe I've encountered an issue a long time ago where this
> happened on all PGNs.
> So I introduced this option, having 3 possibilities:
> * no padding
> * padding for TP
> * padding for all PGN.

I have yet to check, but I fear that in ISOBus conformance tests on some
places at least this is tested for. I'd say that if the standard says "pad and
fill with 0xff", any ECU should do this and not doing it should be regarded as
a bug... 
I know that PGN 0xEA00 is an exception, and proprietary messages should be
permitted any length IMHO, so padding every frame (even coming from user-space)
seems unnecessary and unacceptable to me, as it clearly violates the standard.
Turning off padding for TP-messages might be a nice-to-have feature, but it
would not be standards-compliant anymore AFAICS.

I guess I don't violate anyone's copyright if I cite a small fragment from
ISO-11783-3 here:

In "Transport Protocol — Data Transfer messages (TP.DT)":
  [...]
  "The last packet of a multi-packet parameter group can require less than
  8B. The extra bytes shall be filled with 0xFF"

If someone can confirm this is the same language as in J1939, I'd say we don't
really need to support non-padded (E)TP-packets. Please note that I am only
referring to (E)TP-packets here!

> > > > > > >>>> ./transport.c:60:MODULE_PARM_DESC(transport_burst_count, "Number
> > > > > > >>>> of packets to send in burst between flow control (1..255, default
> > > > > > >>>> 255)");    
> > > > 
> > > > Usually you'd want this parameter to be 255, except for some special
> > > > situations, and I think this should be a per-socket setting.    
> > > 
> > > ack. This should also become a per-socket setting.  
> > > >     
> > > > > > >>>> ./transport.c:61:MODULE_PARM_DESC(transport_max_size, "Maximum
> > > > > > >>>> packet size (default 100k)");    
> > > > 
> > > > No idea if this limit makes any sense to the kernel driver... for the
> > > > protocol it doesn't. For ISOBus applications this limit is much too low.
> > > > ISOBus VT clients for example can easily send packets of several times
> > > > that amount. IMHO, this parameter should either be removed or set to
> > > > infinite per default.    
> > > 
> > > The default is questionable :-)
> > > On the other hand, on a low-memory device, it may make no sense to
> > > allocate buffers for large packets that are not intended for the host
> > > anyway. To avoid out-of-memory conditions triggered by multiple j1939
> > > transport sessions, I added this parameter to protect against such
> > > attack.  
> > 
> > Ok, I understand your point. I still feel a bit uncomfortable with having a
> > default limit that, if the user is not aware of it, can cause a lot of
> > confusion. Things can suddenly and seemingly randomly not work as expected,
> > and IMHO this is never good. What can we do about this?  
> 
> I have no problems modifying the default.
> You could even move this problem to Kconfig: make a Kconfig entry
> with the (default) max packet size.

Hmm... I could live with that, but I fear that the general consensus is to not
put these kind of "tunables" in KConfig anymore... Can someone confirm this?

> Your point is valid, but positioned with isobus in mind.
> ISOBus VT clients are the exception that forced isobus to cross the 1785
> byte limit.
> For a significant amount of people, 1785 is what they expect to be the
> limit :-)

Yes, that's what Extended-TP sessions are for...
What about firmware-upgrades and such then? Don't those also use ETP?

> On the other hand, it is configurable, and it should not cause hard
> discussions. If one has something specific in mind, he/she can always
> apply it as part of the boot sequence?

You are right in this sense, I am only trying to avoid potential pitfalls for
users that may not be aware this limit exists. I also think this limit should
exist, but I am at a loss about how to implement it in a sensible way.

> > > > > > >>>> ./transport.c:62:MODULE_PARM_DESC(transport_retry_time,
> > > > > > >>>> "Packet retransmission timeout in msecs, used in case of
> > > > > > >>>> buffer full. (default
> > > > > > >>>> 20)"); ./transport.c:63:MODULE_PARM_DESC(transport_packet_delay,
> > > > > > >>>> "Delay between packets to avoid buffer overruns (default
> > > > > > >>>> 0)");      
> > > > 
> > > > These two parameters could be a per interface setting I believe.    
> > > 
> > > as mentioned earlier, the parameters may serve as a system-wide default,
> > > and may be overruled per socket even (it about the TX path).  
> > 
> > System-wide would mean per-interface setting (ip link set...?), but
> > overruled per socket would imply setsockopt... this seems a bit
> > contradictory to me. Shouldn't it be either one _or_ the other?  
> 
> I believe it should eventually be a per-socket option.
> On the other hand, having it system-wide allows it to be set without
> per-application modification.
> I don't see a real per-interface need, due to the per-socket goal.

I can agree to per-socket configuration. As for existing software, I'd say
that we should assume that no prior software does exist until this code hits
mainline, right?
In other words, all prior software should assume the API will change as long
as this code is not in mainline.

> The contradictory nature is eliminated due to module parameters.
> /proc/... settings should be evaluated later on, but modules parameters
> should not necessarily? Once you set the (e.g. packet retransmission
> timeout) on your socket, the system-wide module parameter does not apply
> for your socket anymore. Seems reasonable to me.

Then IMHO, the system-wide setting should not really be a setting, but rather
just a default. It seems confusing to me to have two different sources for a
single parameter value.

> > > > > > >>>
> > > > > > >>> Better convert them to netlink options.    
> > > 
> > > I just dropped netlink?
> > > and I'd first import j1939 in the kernel before going to netlink again.
> > > having iproute2 out-of-tree patches is not handy.  
> > 
> > I agree here.
> > 
> > Best regards,
> > 

Best regards,

-- 
David Jander
Protonic Holland.

^ permalink raw reply

* Re: j1939
From: Kurt Van Dijck @ 2016-10-04 13:57 UTC (permalink / raw)
  To: David Jander; +Cc: Austin Schuh, Oliver Hartkopp, Marc Kleine-Budde, linux-can
In-Reply-To: <20161004145202.6c5238d9@erd980>

> 
> On Tue, 4 Oct 2016 09:37:03 +0200
> Kurt Van Dijck <dev.kurt@vandijck-laurijssen.be> wrote:
> 
> > --- Original message ---
> > > Date:   Tue, 20 Sep 2016 09:15:25 +0200
> > > From: David Jander <david@protonic.nl>
> > > To: Austin Schuh <austin@peloton-tech.com>
> > > Cc: Oliver Hartkopp <socketcan@hartkopp.net>, Marc Kleine-Budde
> > >  <mkl@pengutronix.de>, linux-can <linux-can@vger.kernel.org>, Kurt Van
> > >  Dijck <kurt.van.dijck@eia.be>
> > > Subject: Re: j1939
> > > X-Mailer: Claws Mail 3.13.2 (GTK+ 2.24.30; x86_64-pc-linux-gnu)
> > > 
> > > On Mon, 19 Sep 2016 14:37:15 -0700
> > > Austin Schuh <austin@peloton-tech.com> wrote:
> > >   
> > > > On Mon, Sep 19, 2016 at 12:45 PM Oliver Hartkopp <socketcan@hartkopp.net> wrote:  
> > > > >
> > > > >
> > > > >
> > > > > On 09/19/2016 08:52 PM, Marc Kleine-Budde wrote:    
> > > > > > On 09/19/2016 08:27 PM, Oliver Hartkopp wrote:    
> > > > > >> On 09/19/2016 06:54 PM, Marc Kleine-Budde wrote:
> > > > > >>    
> > > > > >>>
> > > > > >>> Another thing that IMHO has to be sorted out is the use of module
> > > > > >>> parameters:
> > > > > >>>    
> > > > > >>>> ./main.c:49:MODULE_PARM_DESC(padding, "Pad all packets to 8 bytes, and stuff with 0xff");  
> > > 
> > > This is true only for 99% of all J1939 messages. I need to investigate some
> > > more, but I fear this option can better be removed. I couldn't think of a
> > > situation where I'd ponder setting this parameter to true.  
> > 
> > There exist engine controller software that adhere to this for the
> > transport protocol messages. This means, I implemented the transport
> > protocol to not necessarily use all 8 bytes at the end.
> 
> Do you mean this only applies to TP messages?
> In that case, the option may make sense, although according to the standard,
> all TP messages should always be 8-bytes long, so when is it desirable to
> have non-standard TP-messages? Is this ECU software you mention not
> standards-compliant? Does it bark on standards-compliant TP messages?

From what I remember (I have no legal access to the specs anymore),
J1939 defines _all_ pgns but 0xea00 to be 8 bytes long.
But often, especially for proprietary messages, not all 8 bytes are
used, and the DLC is cut accordingly.
And less often, this is done for TP too.
I also did that for as long as I can remember.
As long as all defined bytes are there, this should not really be a
problem.

Recently, someone on this list was confronted with an engine ECU that
dropped TP that had the last packet not 8 bytes long.
And I believe I've encountered an issue a long time ago where this
happened on all PGNs.
So I introduced this option, having 3 possibilities:
* no padding
* padding for TP
* padding for all PGN.

> 
> > > > > >>>> ./transport.c:60:MODULE_PARM_DESC(transport_burst_count, "Number
> > > > > >>>> of packets to send in burst between flow control (1..255, default
> > > > > >>>> 255)");  
> > > 
> > > Usually you'd want this parameter to be 255, except for some special
> > > situations, and I think this should be a per-socket setting.  
> > 
> > ack. This should also become a per-socket setting.
> > >   
> > > > > >>>> ./transport.c:61:MODULE_PARM_DESC(transport_max_size, "Maximum
> > > > > >>>> packet size (default 100k)");  
> > > 
> > > No idea if this limit makes any sense to the kernel driver... for the
> > > protocol it doesn't. For ISOBus applications this limit is much too low.
> > > ISOBus VT clients for example can easily send packets of several times
> > > that amount. IMHO, this parameter should either be removed or set to
> > > infinite per default.  
> > 
> > The default is questionable :-)
> > On the other hand, on a low-memory device, it may make no sense to
> > allocate buffers for large packets that are not intended for the host
> > anyway. To avoid out-of-memory conditions triggered by multiple j1939
> > transport sessions, I added this parameter to protect against such
> > attack.
> 
> Ok, I understand your point. I still feel a bit uncomfortable with having a
> default limit that, if the user is not aware of it, can cause a lot of
> confusion. Things can suddenly and seemingly randomly not work as expected,
> and IMHO this is never good. What can we do about this?

I have no problems modifying the default.
You could even move this problem to Kconfig: make a Kconfig entry
with the (default) max packet size.

Your point is valid, but positioned with isobus in mind.
ISOBus VT clients are the exception that forced isobus to cross the 1785
byte limit.
For a significant amount of people, 1785 is what they expect to be the limit :-)

On the other hand, it is configurable, and it should not cause hard
discussions. If one has something specific in mind, he/she can always
apply it as part of the boot sequence?

> 
> > > > > >>>> ./transport.c:62:MODULE_PARM_DESC(transport_retry_time, "Packet
> > > > > >>>> retransmission timeout in msecs, used in case of buffer full.
> > > > > >>>> (default
> > > > > >>>> 20)"); ./transport.c:63:MODULE_PARM_DESC(transport_packet_delay,
> > > > > >>>> "Delay between packets to avoid buffer overruns (default 0)");    
> > > 
> > > These two parameters could be a per interface setting I believe.  
> > 
> > as mentioned earlier, the parameters may serve as a system-wide default,
> > and may be overruled per socket even (it about the TX path).
> 
> System-wide would mean per-interface setting (ip link set...?), but overruled
> per socket would imply setsockopt... this seems a bit contradictory to me.
> Shouldn't it be either one _or_ the other?

I believe it should eventually be a per-socket option.
On the other hand, having it system-wide allows it to be set without
per-application modification.
I don't see a real per-interface need, due to the per-socket goal.

The contradictory nature is eliminated due to module parameters.
/proc/... settings should be evaluated later on, but modules parameters
should not necessarily? Once you set the (e.g. packet retransmission
timeout) on your socket, the system-wide module parameter does not apply
for your socket anymore. Seems reasonable to me.

> 
> > > > > >>>
> > > > > >>> Better convert them to netlink options.  
> > 
> > I just dropped netlink?
> > and I'd first import j1939 in the kernel before going to netlink again.
> > having iproute2 out-of-tree patches is not handy.
> 
> I agree here.
> 
> Best regards,
> 
> -- 
> David Jander
> Protonic Holland.
> --
> To unsubscribe from this list: send the line "unsubscribe linux-can" 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: j1939
From: David Jander @ 2016-10-04 12:52 UTC (permalink / raw)
  To: Kurt Van Dijck
  Cc: Austin Schuh, Oliver Hartkopp, Marc Kleine-Budde, linux-can
In-Reply-To: <20161004073703.GB13813@airbook.vandijck-laurijssen.be>

On Tue, 4 Oct 2016 09:37:03 +0200
Kurt Van Dijck <dev.kurt@vandijck-laurijssen.be> wrote:

> --- Original message ---
> > Date:   Tue, 20 Sep 2016 09:15:25 +0200
> > From: David Jander <david@protonic.nl>
> > To: Austin Schuh <austin@peloton-tech.com>
> > Cc: Oliver Hartkopp <socketcan@hartkopp.net>, Marc Kleine-Budde
> >  <mkl@pengutronix.de>, linux-can <linux-can@vger.kernel.org>, Kurt Van
> >  Dijck <kurt.van.dijck@eia.be>
> > Subject: Re: j1939
> > X-Mailer: Claws Mail 3.13.2 (GTK+ 2.24.30; x86_64-pc-linux-gnu)
> > 
> > On Mon, 19 Sep 2016 14:37:15 -0700
> > Austin Schuh <austin@peloton-tech.com> wrote:
> >   
> > > On Mon, Sep 19, 2016 at 12:45 PM Oliver Hartkopp <socketcan@hartkopp.net> wrote:  
> > > >
> > > >
> > > >
> > > > On 09/19/2016 08:52 PM, Marc Kleine-Budde wrote:    
> > > > > On 09/19/2016 08:27 PM, Oliver Hartkopp wrote:    
> > > > >> On 09/19/2016 06:54 PM, Marc Kleine-Budde wrote:
> > > > >>    
> > > > >>>
> > > > >>> Another thing that IMHO has to be sorted out is the use of module
> > > > >>> parameters:
> > > > >>>    
> > > > >>>> ./main.c:49:MODULE_PARM_DESC(padding, "Pad all packets to 8 bytes, and stuff with 0xff");  
> > 
> > This is true only for 99% of all J1939 messages. I need to investigate some
> > more, but I fear this option can better be removed. I couldn't think of a
> > situation where I'd ponder setting this parameter to true.  
> 
> There exist engine controller software that adhere to this for the
> transport protocol messages. This means, I implemented the transport
> protocol to not necessarily use all 8 bytes at the end.

Do you mean this only applies to TP messages?
In that case, the option may make sense, although according to the standard,
all TP messages should always be 8-bytes long, so when is it desirable to
have non-standard TP-messages? Is this ECU software you mention not
standards-compliant? Does it bark on standards-compliant TP messages?

> > > > >>>> ./transport.c:60:MODULE_PARM_DESC(transport_burst_count, "Number
> > > > >>>> of packets to send in burst between flow control (1..255, default
> > > > >>>> 255)");  
> > 
> > Usually you'd want this parameter to be 255, except for some special
> > situations, and I think this should be a per-socket setting.  
> 
> ack. This should also become a per-socket setting.
> >   
> > > > >>>> ./transport.c:61:MODULE_PARM_DESC(transport_max_size, "Maximum
> > > > >>>> packet size (default 100k)");  
> > 
> > No idea if this limit makes any sense to the kernel driver... for the
> > protocol it doesn't. For ISOBus applications this limit is much too low.
> > ISOBus VT clients for example can easily send packets of several times
> > that amount. IMHO, this parameter should either be removed or set to
> > infinite per default.  
> 
> The default is questionable :-)
> On the other hand, on a low-memory device, it may make no sense to
> allocate buffers for large packets that are not intended for the host
> anyway. To avoid out-of-memory conditions triggered by multiple j1939
> transport sessions, I added this parameter to protect against such
> attack.

Ok, I understand your point. I still feel a bit uncomfortable with having a
default limit that, if the user is not aware of it, can cause a lot of
confusion. Things can suddenly and seemingly randomly not work as expected,
and IMHO this is never good. What can we do about this?

> > > > >>>> ./transport.c:62:MODULE_PARM_DESC(transport_retry_time, "Packet
> > > > >>>> retransmission timeout in msecs, used in case of buffer full.
> > > > >>>> (default
> > > > >>>> 20)"); ./transport.c:63:MODULE_PARM_DESC(transport_packet_delay,
> > > > >>>> "Delay between packets to avoid buffer overruns (default 0)");    
> > 
> > These two parameters could be a per interface setting I believe.  
> 
> as mentioned earlier, the parameters may serve as a system-wide default,
> and may be overruled per socket even (it about the TX path).

System-wide would mean per-interface setting (ip link set...?), but overruled
per socket would imply setsockopt... this seems a bit contradictory to me.
Shouldn't it be either one _or_ the other?

> > > > >>>
> > > > >>> Better convert them to netlink options.  
> 
> I just dropped netlink?
> and I'd first import j1939 in the kernel before going to netlink again.
> having iproute2 out-of-tree patches is not handy.

I agree here.

Best regards,

-- 
David Jander
Protonic Holland.

^ permalink raw reply

* Re: j1939
From: Kurt Van Dijck @ 2016-10-04  7:37 UTC (permalink / raw)
  To: David Jander
  Cc: Austin Schuh, Oliver Hartkopp, Marc Kleine-Budde, linux-can,
	Kurt Van Dijck
In-Reply-To: <20160920091525.2ff22d58@erd980>


--- Original message ---
> Date:   Tue, 20 Sep 2016 09:15:25 +0200
> From: David Jander <david@protonic.nl>
> To: Austin Schuh <austin@peloton-tech.com>
> Cc: Oliver Hartkopp <socketcan@hartkopp.net>, Marc Kleine-Budde
>  <mkl@pengutronix.de>, linux-can <linux-can@vger.kernel.org>, Kurt Van
>  Dijck <kurt.van.dijck@eia.be>
> Subject: Re: j1939
> X-Mailer: Claws Mail 3.13.2 (GTK+ 2.24.30; x86_64-pc-linux-gnu)
> 
> On Mon, 19 Sep 2016 14:37:15 -0700
> Austin Schuh <austin@peloton-tech.com> wrote:
> 
> > On Mon, Sep 19, 2016 at 12:45 PM Oliver Hartkopp <socketcan@hartkopp.net> wrote:
> > >
> > >
> > >
> > > On 09/19/2016 08:52 PM, Marc Kleine-Budde wrote:  
> > > > On 09/19/2016 08:27 PM, Oliver Hartkopp wrote:  
> > > >> On 09/19/2016 06:54 PM, Marc Kleine-Budde wrote:
> > > >>  
> > > >>>
> > > >>> Another thing that IMHO has to be sorted out is the use of module
> > > >>> parameters:
> > > >>>  
> > > >>>> ./main.c:49:MODULE_PARM_DESC(padding, "Pad all packets to 8 bytes, and stuff with 0xff");
> 
> This is true only for 99% of all J1939 messages. I need to investigate some
> more, but I fear this option can better be removed. I couldn't think of a
> situation where I'd ponder setting this parameter to true.

There exist engine controller software that adhere to this for the
transport protocol messages. This means, I implemented the transport
protocol to not necessarily use all 8 bytes at the end.

> 
> > > >>>> ./transport.c:60:MODULE_PARM_DESC(transport_burst_count, "Number of packets to send in burst between flow control (1..255, default 255)");
> 
> Usually you'd want this parameter to be 255, except for some special
> situations, and I think this should be a per-socket setting.

ack. This should also become a per-socket setting.
> 
> > > >>>> ./transport.c:61:MODULE_PARM_DESC(transport_max_size, "Maximum packet size (default 100k)");
> 
> No idea if this limit makes any sense to the kernel driver... for the protocol
> it doesn't. For ISOBus applications this limit is much too low. ISOBus VT
> clients for example can easily send packets of several times that amount.
> IMHO, this parameter should either be removed or set to infinite per default.

The default is questionable :-)
On the other hand, on a low-memory device, it may make no sense to
allocate buffers for large packets that are not intended for the host
anyway. To avoid out-of-memory conditions triggered by multiple j1939
transport sessions, I added this parameter to protect against such
attack.
> 
> > > >>>> ./transport.c:62:MODULE_PARM_DESC(transport_retry_time, "Packet retransmission timeout in msecs, used in case of buffer full. (default 20)");
> > > >>>> ./transport.c:63:MODULE_PARM_DESC(transport_packet_delay, "Delay between packets to avoid buffer overruns (default 0)");  
> 
> These two parameters could be a per interface setting I believe.

as mentioned earlier, the parameters may serve as a system-wide default,
and may be overruled per socket even (it about the TX path).
> 
> > > >>>
> > > >>> Better convert them to netlink options.

I just dropped netlink?
and I'd first import j1939 in the kernel before going to netlink again.
having iproute2 out-of-tree patches is not handy.

Kurt

^ permalink raw reply

* Re: j1939
From: Kurt Van Dijck @ 2016-10-04  7:28 UTC (permalink / raw)
  To: Austin Schuh; +Cc: Oliver Hartkopp, Marc Kleine-Budde, linux-can, David Jander
In-Reply-To: <CANGgnMaxNRBDG9WWAanHEem-wDJ1ujxYdqgko7nAx3iJNcPUGA@mail.gmail.com>

Hey ho,

Please note my @eia.be address is obsolete now.
I should investigate why the kbuild robot send mail to that address.
this (dev.kurt@...) is meant to stay :-)

Ans I was very occupied the due to a move.
I'm glad there's momentum on the J1939 side, a bit sad it happened just
when I was a bit off.

--- Original message ---
> Date:   Mon, 19 Sep 2016 14:37:15 -0700
> From: Austin Schuh <austin@peloton-tech.com>
> To: Oliver Hartkopp <socketcan@hartkopp.net>, Marc Kleine-Budde
>  <mkl@pengutronix.de>, linux-can <linux-can@vger.kernel.org>
> Cc: David Jander <david@protonic.nl>, Kurt Van Dijck <kurt.van.dijck@eia.be>
> Subject: Re: j1939
> 
> On Mon, Sep 19, 2016 at 12:45 PM Oliver Hartkopp <socketcan@hartkopp.net> wrote:
> >
> >
> >
> > On 09/19/2016 08:52 PM, Marc Kleine-Budde wrote:
> > > On 09/19/2016 08:27 PM, Oliver Hartkopp wrote:
> > >> On 09/19/2016 06:54 PM, Marc Kleine-Budde wrote:
> > >>
> > >>>
> > >>> Another thing that IMHO has to be sorted out is the use of module
> > >>> parameters:
> > >>>
> > >>>> ./main.c:49:MODULE_PARM_DESC(padding, "Pad all packets to 8 bytes, and stuff with 0xff");
> > >>>> ./transport.c:60:MODULE_PARM_DESC(transport_burst_count, "Number of packets to send in burst between flow control (1..255, default 255)");
> > >>>> ./transport.c:61:MODULE_PARM_DESC(transport_max_size, "Maximum packet size (default 100k)");
> > >>>> ./transport.c:62:MODULE_PARM_DESC(transport_retry_time, "Packet retransmission timeout in msecs, used in case of buffer full. (default 20)");
> > >>>> ./transport.c:63:MODULE_PARM_DESC(transport_packet_delay, "Delay between packets to avoid buffer overruns (default 0)");
> > >>>
> > >>> Better convert them to netlink options.
> > >>>
> > >>
> > >> What about putting them into socket depended sockopts with setsockopt()?
> > >
> > > Is it a per interface or a per socket setting?
> > >
> >
> > At least for iso-tp these kind of settings are transport channel
> > specific. Don't know if this can be applied to j1939 too.
> >
> > This is something Kurt or others can answer better than me.
> 
> These parameters should be defined by the standard.  For the padding,
> I'm not aware of any messages (besides the RQST message which should
> be 3 bytes long) which are not already 8 bytes.  If you want to
> enforce that, I would think it would be better to refuse to accept the
> packet rather than pad it out to surprise the user less.  The unused
> bits should be set to 1, but that's hard to do at the driver level
> when you don't know which bits are used and which are unused in a
> request.
> 
> SAE J1939-21 defines all the timing requirements and other constraints
> pretty well.  Is there a reason to make them configurable?

The timing parameters are defined pretty well, but sometimes
light-weight ECU's cannot handle what's defined by the standard.
Therefore, a sort of rate-limiting may be defined.

The 8-byte sutffing also is particular. Do you need to send 7 unused
bytes? The standard says yes, but what happens if you don't.
So I put module parameters there.

Oliver idea is what I was heading to, making a lot of those module
paramters also a socket option, with the module parameters still being
the default. So, the module parameters do not disappear.

I'm still catching up, I agree with David Jander's comments.

^ permalink raw reply

* Re: j1939
From: Kurt Van Dijck @ 2016-10-04  7:41 UTC (permalink / raw)
  To: Marc Kleine-Budde; +Cc: Austin Schuh, Oliver Hartkopp, linux-can, David Jander
In-Reply-To: <6d3c3af0-12bb-ba14-c8f9-a8837da82795@pengutronix.de>

> 
> On 09/19/2016 11:37 PM, Austin Schuh wrote:
> >>>> What about putting them into socket depended sockopts with setsockopt()?
> >>>
> >>> Is it a per interface or a per socket setting?
> >>
> >> At least for iso-tp these kind of settings are transport channel
> >> specific. Don't know if this can be applied to j1939 too.
> >>
> >> This is something Kurt or others can answer better than me.
> > 
> > These parameters should be defined by the standard.  For the padding,
> > I'm not aware of any messages (besides the RQST message which should
> > be 3 bytes long) which are not already 8 bytes.  If you want to
> > enforce that, I would think it would be better to refuse to accept the
> > packet rather than pad it out to surprise the user less.
> 
> So an option is to check if PGN is 0xea00 then length must be 3,
> otherwise length must be 8?
> 
> > The unused bits should be set to 1, but that's hard to do at the
> > driver level when you don't know which bits are used and which are
> > unused in a request.
> 
> The padding is done in the j1939_send() function. So padding is no
> problem here.
> 
> > SAE J1939-21 defines all the timing requirements and other constraints
> > pretty well.  Is there a reason to make them configurable?
> 
> Dunno...Kurt?

As said in a previous email, to support different environments.
The timings are defined in the standard, but in a 'receive' way, i.e.
the standard defines what timing is acceptable.
For the TX path, you may prefer to send ASAP (most of the times) or to
relax the timing so the opposite side can keep up. This preference does
not violate the standard.

> 
> Marc
> 
> -- 
> Pengutronix e.K.                  | Marc Kleine-Budde           |
> Industrial Linux Solutions        | Phone: +49-231-2826-924     |
> Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
> Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |
> 




^ permalink raw reply

* Re: [PATCH v2 00/12] can: rx-offload: add implmentation and switch flexcan driver to use it
From: David Jander @ 2016-10-04 12:33 UTC (permalink / raw)
  To: Alexander Stein
  Cc: Marc Kleine-Budde, linux-can, Martin Däumler,
	Daniel Krüger
In-Reply-To: <12311540.OPjVtcIYq6@ws-stein>

On Tue, 04 Oct 2016 13:57:33 +0200
Alexander Stein <alexander.stein@systec-electronic.com> wrote:

> Hi,
> 
> On Monday 04 July 2016 20:32:05, Marc Kleine-Budde wrote:
> > this patch takes up the idea to read the CAN frames in IRQ context and send
> > them later in NAPI. The first two patches add each an offloading scheme.
> > 
> > The first one is for hardware FIFO based cores, like the flexcan in FIFO
> > mode. The second one requires mailboxes with timestamps. The mailboxes are
> > read and sorted by timestamp in IRQ context, sending is done later in NAPI
> > aswell.
> > 
> > The remaining patches modify the flexcan driver to make use of it. imx6 and
> > vf610 SoCs can make use of the 64 mailbox software FIFO, while older SoCs
> > still use flexcan's 6 mailbox deep hardware FIFO.
> > 
> > Testing on any flexcan core is highly appreciated.  
> 
> We did some tests on our custom i.MX35 based board. This means we are stuck
> at the 6 mailbox deep hardware FIFO. This is how our test works:
> * Send 2 * 250000 CAN frames to the i.MX board, in 2 * 250 burst sizes at
> 1MBit/s
> * Running iperf in parallel
> 
> Originally we used 3.10.95-rt102 (RT enabled) and lost CAN frames. The
> CAN-IRQ-Thread has been prioritized to SCHED_FIFO 91. Then we updated to
> 4.6.7-rt13. Without those patches in either case, RT enabled or not, the
> hardware FIFO still overran. When this patchset is applied the non-RT
> configuration successfully received all CAN frames without any drop.
> Unfortunately when RT is enabled there are still hardware overruns. A first
> try on the newly released 4.6.7-rt14 improved the situation, there are less
> overruns, but they still exist.
> 
> In summary the non-RT case seems fine now, but I wonder what causes the
> delays on RT to the CAN-IRQ-Thread which seem to be about 500us (bus time of
> 6 4Byte CAN frames).

Thanks for testing!
Well, in -RT I guess anything with a higher priority than the CAN-IRQ can
cause a delay... but since going from -rt13 to -rt14 improved the situation,
this sounds like bugs in the -rt code. Are you running any code in SCHED_FIFO
that you don't run in the non-RT case?
It's nevertheless very nice to see that for non-RT, you got from losing
messages to not losing any message with this patchset, even on a lowly i.MX35
at 1Mbit/s! This proves that the patchset is certainly worth it.

Best regards,

-- 
David Jander
Protonic Holland.

^ permalink raw reply

* Re: [PATCH v2 00/12] can: rx-offload: add implmentation and switch flexcan driver to use it
From: Alexander Stein @ 2016-10-04 11:57 UTC (permalink / raw)
  To: Marc Kleine-Budde
  Cc: linux-can, david, Martin Däumler, Daniel Krüger
In-Reply-To: <1467657137-18891-1-git-send-email-mkl@pengutronix.de>

Hi,

On Monday 04 July 2016 20:32:05, Marc Kleine-Budde wrote:
> this patch takes up the idea to read the CAN frames in IRQ context and send
> them later in NAPI. The first two patches add each an offloading scheme.
> 
> The first one is for hardware FIFO based cores, like the flexcan in FIFO
> mode. The second one requires mailboxes with timestamps. The mailboxes are
> read and sorted by timestamp in IRQ context, sending is done later in NAPI
> aswell.
> 
> The remaining patches modify the flexcan driver to make use of it. imx6 and
> vf610 SoCs can make use of the 64 mailbox software FIFO, while older SoCs
> still use flexcan's 6 mailbox deep hardware FIFO.
> 
> Testing on any flexcan core is highly appreciated.

We did some tests on our custom i.MX35 based board. This means we are stuck at the 6 mailbox deep hardware FIFO.
This is how our test works:
* Send 2 * 250000 CAN frames to the i.MX board, in 2 * 250 burst sizes at 1MBit/s
* Running iperf in parallel

Originally we used 3.10.95-rt102 (RT enabled) and lost CAN frames. The CAN-IRQ-Thread has been prioritized to SCHED_FIFO 91.
Then we updated to 4.6.7-rt13. Without those patches in either case, RT enabled or not, the hardware FIFO still overran.
When this patchset is applied the non-RT configuration successfully received all CAN frames without any drop. Unfortunately when RT is enabled there are still hardware overruns. A first try on the newly released 4.6.7-rt14 improved the situation, there are less overruns, but they still exist.

In summary the non-RT case seems fine now, but I wonder what causes the delays on RT to the CAN-IRQ-Thread which seem to be about 500us (bus time of 6 4Byte CAN frames).

Best regards,
Alexander


^ permalink raw reply

* Re: [PATCH v2 00/12] can: rx-offload: add implmentation and switch flexcan driver to use it
From: Holger Schurig @ 2016-10-04  6:32 UTC (permalink / raw)
  To: Marc Kleine-Budde, linux-can
In-Reply-To: <878tv48bhm.fsf@gmail.com>

Holger Schurig <holgerschurig@gmail.com> writes:

> Then they did same CAN ping-ping test with reboots and wrote: "Out of
> about 16,000 reboots we had one failure. This failed both CAN interfaces
> and the logs show that the PTX-C received all CAN messages and returned
> them, but the test machine for some reason did not receive any CAN
> messages."

It turned out that the error was not in the FlexCAN + rx-fifo patches,
but at the receiving end.

So you can have a "Tested-By: Holger Schurig <holgerschurig@gmail.com>"
for your patch series on top of 4.7.5.

^ permalink raw reply

* CAN transmit queue, revisited
From: Constantine Vetoshev @ 2016-10-03 22:39 UTC (permalink / raw)
  To: linux-can

I'm using SocketCAN on a project, and have run into trouble with error
recovery. Apologies if this topic has been covered recently, but with
gmane out of commission for newer content, I have found no other
archives of this mailing list to search through.

From everything I read and understand, once the transmit queue fills
up, there is no user-space way to clean it out other than restarting
the CAN network interface. This leaves me scratching my head about how
to recover from bus failures.

Suppose I have a (Linux) process which uses a CAN interface (CANtact
in my case) to talk to a bunch of peripheral devices, and the bus goes
off-line (say, power failure to the peripherals). The peripherals stop
acking all the packets my process has sent. The tx queue fills up
almost immediately with messages I'd been sending. Let's say I detect
this condition (it's pretty obvious, with write() calls setting errno
to ENOBUFS), stop sending, do some internal cleanup... and now what?
My application code can figure out if it needs to retransmit anything,
but even when the CAN peripherals go back online, it doesn't matter:
the tx queue is full, write() calls to it will fail, and I have to
kill the network interface to recover.

I found this post:
http://thread.gmane.org/gmane.linux.can/4167 — which suggests using
loopback and CAN_RAW_RECV_OWN_MSGS on the socket to send acks to
self-sent frames and thereby drain the tx queue. I tried it, and it
doesn't work. At least with non-blocking sockets. Is there
confirmation that this technique works at all? Before I go down the
road of hacking CAN's kernel modules to stop using the tx queue and
just using fire-and-forget to send every frame, is there a better way
around this?

^ permalink raw reply

* Credit Suisse Bank England
From: Credit Suisse Bank England @ 2016-10-03  6:21 UTC (permalink / raw)


Guten Tag, wie geht es Ihnen heute? Ich habe Ihre E-Mail-Kontakt aus Ihrem Land Business Directory und beschlossen, mit Ihnen in Verbindung zu diesem Business-Vorschlag. Ich bin Herr Chris Williams der Investment Banking Abteilung der Credit Suisse Bank One Cabot Square, London E14 4QJ, Großbritannien. Ich bin Kontaktaufnahme mit Ihnen in Bezug auf ein Geschäft Vorschlag, die von einer immensen Vorteil für uns beide sein wird.

In meiner Abteilung, entdeckte ich eine verlassene Summe von £ 17,5 Mio. GBP (Siebzehn Millionen fünfhunderttausend britische Pfund Sterling) in einem Konto, das zu einem unserer ausländischen Kunden verstorbenen Herrn Brodskij Nikolai, ein russischer Geschäftsmann, der ein Opfer war, gehört der Malaysia Airlines Flight 370 (MH370 / MAS370), die im Süden der indische Ozean auf 8. März 2014 auf dem Weg von Kuala Lumpur international Airport abgestürzt zu Beijing Capital International Airport zu töten alle an Bord.

Informieren Sie sich über den untenstehenden Link für die Passagierliste:


http://online.wsj.com/news/articles/SB10001424052702303369904579427230127866184

MR Brodskij Nikolai Nummer 7 auf der Liste, Seit seinem Tod unserer Bank hat für die nächsten Angehörigen zu kommen für die Behauptung seiner Gelder und Güter schätzten die Summe von £ 17,5 Mio. GBP, aber niemand hat so, ich persönlich getan gewartet , waren nicht erfolgreich bei der Suche nach einer seiner Verwandten. Ich suche Ihre Zustimmung an Sie an die Bank, wie die nächsten Angehörigen des Verstorbenen zu präsentieren, so dass die Erlöse aus diesem Konto bei £ 17,5 Mio. GBP an Sie gezahlt werden bewertet.


Der Deal wird in diesem Verhältnis aufgeteilt: 55% für mich und 45% zu Ihnen. Ich habe in meinem Besitz alle notwendigen und wichtigen Dokumente, die in diesem Geschäft verwendet werden kann. Ich brauche Ihre ehrliche Zusammenarbeit, Verschwiegenheit und Vertrauen, damit wir sehen diese Verhandlung durch. Ich garantiere Ihnen 100% Erfolg in diesem Geschäft, wenden Sie sich bitte sicher sein, dass Sie das Angebot unter einer legitimen Anordnung, die Sie aus der Verletzung des Gesetzes sowohl hier als auch in Großbritannien und in Ihrem Land zu schützen wird ausgeführt.

Wenn Sie in der Lage, dem Umgang mit diesem Angebot sind, bitte zurück zu mir mit folgenden Angaben, damit wir fortfahren:

1. Ihr vollständiger Name:
2. Telefonnummer:
3. Kontakt-Adresse:
4. Alter:
5. Geschlecht:
6. Beruf:
7. Ihre Nationalität:


Nachdem durch eine methodische Suche gegangen, entschied ich mich, Sie zu kontaktieren hoffen, dass Sie diesen Vorschlag interessant, Bitte auf Ihrer Bestätigung dieser Nachricht und geben Sie Ihr Interesse, ich will dich mit mehr Informationen liefern zu finden.

Ihre Zustimmung zu dieser E-Mail und Business-Vorschlag wird sehr geschätzt werden.

Freundliche Grüße,
Chris Williams

^ permalink raw reply

* Re: [PATCH] can: add ifi_nioscan driver
From: Damien Dusha @ 2016-09-29 23:35 UTC (permalink / raw)
  To: Marc Kleine-Budde; +Cc: linux-can
In-Reply-To: <20160929070919.22382-1-mkl@pengutronix.de>

Hi Marc,

Thanks for the driver. My own implementation is well advanced and will
hopefully be ready in a few days, but I can see some interesting
quirks which I will need to carefully look at.

Kind regards,
Damien.

On Thu, Sep 29, 2016 at 5:09 PM, Marc Kleine-Budde <mkl@pengutronix.de> wrote:
> Just for reference.
>
> Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
> ---
> Hey Damien,
>
> the old driver just for reference, maybe this helps.
>
> regards,
> Marc
>
>  drivers/net/can/ifi_nioscan.c | 1070 +++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 1070 insertions(+)
>  create mode 100644 drivers/net/can/ifi_nioscan.c
>
> diff --git a/drivers/net/can/ifi_nioscan.c b/drivers/net/can/ifi_nioscan.c
> new file mode 100644
> index 000000000000..8bb04473c07d
> --- /dev/null
> +++ b/drivers/net/can/ifi_nioscan.c
> @@ -0,0 +1,1070 @@
> +/*
> + * drivers/can/nioscan.c
> + *
> + * Copyright (C) 2005, 2006, 2007
> + *
> + * - Sascha Hauer, Marc Kleine-Budde, Pengutronix
> + *
> + * 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
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + */
> +
> +/* #define DEBUG       1 */
> +
> +#include <linux/config.h>
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/device.h>
> +#include <linux/netdevice.h>
> +#include <linux/etherdevice.h>
> +#include <linux/skbuff.h>
> +#include <linux/can.h>
> +#include <linux/version.h>
> +#include <linux/fpbus.h>
> +
> +#include "can_driver_compat.h"
> +
> +#include <asm/io.h>
> +#include <asm/irq.h>
> +
> +#define NIOS_VERSION_WORKING   0x0b053335
> +
> +/* Linux compatibility stuff */
> +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13)
> +#define nios_show_version(_dev, _attr, _buf)   nios_show_version(_dev, _buf)
> +#endif
> +
> +#define CARDNAME       "nioscan"
> +
> +#define TX_TIMEOUT     (6*HZ)
> +
> +static int max_interrupt_work = 4;
> +module_param(max_interrupt_work, int, 0400);
> +MODULE_PARM_DESC(max_interrupt_work, "maximum events handled per interrupt");
> +
> +static int chip_clock = -1;
> +module_param(chip_clock, int, 0400);
> +MODULE_PARM_DESC(chip_clock, "Chip Clock Frequency in HZ, default use FPBUS value");
> +
> +enum nios_regs {
> +       NIOS_LENGTH             = 0x000,
> +       NIOS_ID                 = 0x004,
> +       NIOS_DATA_0_3           = 0x008,
> +       NIOS_DATA_4_7           = 0x00C,
> +       NIOS_TIMING             = 0x010,
> +       NIOS_INTMASK            = 0x014,
> +       NIOS_STATUS             = 0x018,
> +       NIOS_ERROR_COUNT        = 0x01C,
> +       NIOS_VERSION            = 0x020,
> +       NIOS_FILTER_MASK        = 0x200,
> +       NIOS_FILTER_ID          = 0x204,
> +};
> +
> +/*
> + * Bit definitionss of Data Lengh Code Reg
> + */
> +#define NIOS_RTR               (1<<4)
> +
> +/*
> + * Bit definitionss of Identifier Reg
> + */
> +#define NIOS_EXTID             (1<<29)
> +
> +/*
> + * Bit Definitions of Timing Reg
> + */
> +#define NIOS_TIME_PRESCALE_OFFS (16)           /* offset of prescale value     */
> +#define NIOS_TIME_PRESCALE_MASK (0xff << NIOS_TIME_PRESCALE_OFFS)
> +#define NIOS_TIME_TIMEA_OFFS   (8)             /* offset of timeA value        */
> +#define NIOS_TIME_TIMEA_MASK   (0x1f << NIOS_TIME_TIMEA_OFFS)
> +#define NIOS_TIME_TIMEB_OFFS   (0)             /* offset of timeB value        */
> +#define NIOS_TIME_TIMEB_MASK   (0x1f << NIOS_TIME_TIMEB_OFFS)
> +#define NIOS_TIME_SET_PRESCALE (1 << 31)       /* prescale value set           */
> +#define NIOS_TIME_SET_TIMEA            (1 << 15)       /* timea value set              */
> +#define NIOS_TIME_SET_TIMEB            (1 <<  7)       /* timeb value set              */
> +#define NIOS_TIME_NORMAL_MODE  (1 << 24)       /* normal mode                  */
> +#define NIOS_TIME_SET_SILENT   (1 << 30)       /* set silent mode              */
> +#define NIOS_TIME_SILENT_MODE  (1 << 28)       /* silent mode                  */
> +
> +/*
> + * Bit Definitions of Interrupt Mask Reg
> + */
> +#define NIOS_INTMASK_SET       (1 << 31)       /* set interrupt mask           */
> +#define NIOS_RX_FULL           (1 << 29)       /* receive buffer full          */
> +#define NIOS_RX_OVFL           (1 << 28)       /* receive buffer overflow      */
> +#define NIOS_RX_NEMPTY         (1 << 27)       /* receive buffer not empty     */
> +#define NIOS_TX_FULL           (1 << 26)       /* transmit buffer full         */
> +#define NIOS_TX_OVFL           (1 << 25)       /* transmit buffer overflow     */
> +#define NIOS_TX_EMPTY          (1 << 24)       /* transmit buffer empty        */
> +#define NIOS_ERRMASK_SET       (1 << 7)        /* set error mask               */
> +#define NIOS_ERR_WARN          (1 << 1)        /* error warning                */
> +#define NIOS_ERR_BUSOFF                (1 << 0)        /* error bus off                */
> +
> +static const u32 nios_int_mask         = NIOS_RX_OVFL  | NIOS_TX_OVFL | NIOS_RX_FULL | NIOS_RX_NEMPTY | NIOS_TX_EMPTY;
> +static const u32 nios_int_mask_abnormal        = NIOS_RX_OVFL  | NIOS_TX_OVFL | NIOS_RX_FULL;
> +static const u32 nios_err_mask         = NIOS_ERR_WARN | NIOS_ERR_BUSOFF;
> +static const u32 nios_all_mask         = NIOS_RX_OVFL  | NIOS_TX_OVFL | NIOS_RX_FULL |
> +       NIOS_RX_NEMPTY | NIOS_TX_EMPTY | NIOS_ERR_WARN | NIOS_ERR_BUSOFF;
> +
> +/*
> + * Bit Definitions of Status Reg
> + */
> +#define NIOS_RX_BUSY           (1 << 31)       /* receive busy                 */
> +#define NIOS_TX_BUSY           (1 << 30)       /* transmit busy                */
> +#define NIOS_RX_BUFFER_FULL    (1 << 23)       /* receive buffer full          */
> +#define NIOS_TX_BUFFER_FULL    (1 << 22)       /* transmit buffer full         */
> +#define NIOS_RX_POINTER_RESET  (1 << 23)       /* reset receive pointer        */
> +#define NIOS_TX_POINTER_RESET  (1 << 15)       /* reset transmit pointer       */
> +#define NIOS_RX_NEXT_VALUE     (1 <<  7)       /* receive fifo next value      */
> +#define NIOS_ERR_PASSIVE       (1 <<  3)       /* error passive mode           */
> +#define NIOS_ERR_ACTIVE                (1 <<  2)       /* error active mode            */
> +#define NIOS_RX_PTR_MASK       (0x003F0000)    /* receive buffer pointer mask  */
> +#define NIOS_RX_PTR_OFFS       (16)            /* receive buffer pointer offset*/
> +#define NIOS_TX_PTR_MASK       (0x00003F00)    /* transmit buffer pointer mask */
> +#define NIOS_TX_PTR_OFFS       (8)             /* transmit buf. pointer offset */
> +
> +/*
> + * Bit Definitions of Error Counter Reg
> + */
> +#define NIOS_RX_ERR_CNT_MASK   (0x00ff0000)
> +#define NIOS_TX_ERR_CNT_MASK   (0x000001ff)
> +#define NIOS_RX_ERR_CNT_OFFS   (16)
> +#define NIOS_TX_ERR_CNT_OFFS   (0)
> +
> +/*
> + * Bit Definitions of Filter mask and Filter Id
> + */
> +#define NIOS_FILTER_NR         (64)
> +#define NIOS_FILTER_MASK_VALID (1 << 31)
> +#define NIOS_FILTER_ID_VALID   (1 << 31)
> +
> +#define NIOS_VERSION_MONTH(version)   ((version >> 24) & 0xff)
> +#define NIOS_VERSION_YEAR(version)    ((version >> 16) & 0xff)
> +#define NIOS_VERSION_QUARTUS(version) ((version >>  8) & 0xff)
> +#define NIOS_VERSION_REV(version)     ((version >>  0) & 0xff)
> +
> +enum nios_chip_clock {
> +       NIOS_CC_33 = 0,
> +       NIOS_CC_48,
> +       NIOS_CC_60,
> +       NIOS_CC_MAX,
> +};
> +
> +enum nios_priv_flags {
> +       NIOS_PRIV_FLAGS_ERR_BUSOFF      = 1 << 0,
> +       NIOS_PRIV_FLAGS_TX_FULL         = 1 << 1,
> +};
> +
> +struct nioscan_private
> +{
> +       void __iomem            *base_addr;
> +       struct net_device_stats stats;
> +       unsigned int            baudrate;
> +       enum nios_priv_flags    flags;
> +       enum nios_chip_clock    chip_clock;
> +       spinlock_t              lock;
> +};
> +
> +static int do_set_baudrate(struct net_device *ndev, struct can_baudrate *br);
> +
> +static inline u32 nios_read_reg(const void __iomem *base, enum nios_regs reg)
> +{
> +       volatile u32 val;
> +
> +       val = readl(base + reg);
> +
> +       pr_debug("(%s) base 0x%p, reg 0x%x, val 0x%08x\n",
> +                __FUNCTION__, base, reg, val);
> +
> +       return val;
> +}
> +
> +static inline void nios_write_reg(const void __iomem *base, enum nios_regs reg, u32 val)
> +{
> +       pr_debug("(%s) base 0x%08lx, reg 0x%x, val 0x%08x\n",
> +                __FUNCTION__, base, reg, val);
> +
> +       writel(val, base + reg);
> +}
> +
> +static inline void nios_write_filter(const void __iomem *base, unsigned int nr,
> +                                    u32 mask, u32 id)
> +{
> +       nios_write_reg(base, NIOS_FILTER_MASK + (nr << 3), mask);
> +       nios_write_reg(base, NIOS_FILTER_ID   + (nr << 3), id);
> +}
> +
> +static inline u32 nios_id_can_to_reg(u32 can_id)
> +{
> +       if (can_id & CAN_FLAG_EXTENDED) {
> +               return ((can_id & 0x1ffC0000) >> 18) |
> +                       ((can_id & 0x0003ffff) << 11) |
> +                       NIOS_EXTID;
> +       } else
> +               return can_id & 0x7ff;
> +}
> +
> +static inline u32 nios_id_reg_to_can(u32 reg_id)
> +{
> +       if (reg_id & NIOS_EXTID) {
> +               return ((reg_id & 0x000007ff) << 18) |
> +                       ((reg_id & 0x1ffff800) >> 11) |
> +                       CAN_FLAG_EXTENDED;
> +       } else
> +               return reg_id & 0x7ff;
> +}
> +
> +static inline void nios_enable_txempty(struct nioscan_private *tp)
> +{
> +       /* ACK */
> +       nios_write_reg(tp->base_addr, NIOS_STATUS, NIOS_TX_EMPTY);
> +
> +       /* activate int */
> +       nios_write_reg(tp->base_addr, NIOS_INTMASK, nios_int_mask | NIOS_INTMASK_SET);
> +       tp->flags |= NIOS_PRIV_FLAGS_TX_FULL;
> +}
> +
> +
> +static inline void nios_disable_txempty(struct nioscan_private *tp)
> +{
> +       nios_write_reg(tp->base_addr, NIOS_INTMASK, (nios_int_mask & ~NIOS_TX_EMPTY ) | NIOS_INTMASK_SET );
> +       tp->flags &= ~NIOS_PRIV_FLAGS_TX_FULL;
> +}
> +
> +static void nioscan_chip_reset(struct net_device *ndev)
> +{
> +       struct nioscan_private *tp = netdev_priv(ndev);
> +       int i;
> +
> +       /* set filter */
> +       nios_write_filter(tp->base_addr, 0,
> +                         0x0 | NIOS_FILTER_MASK_VALID,
> +                         0x0 | NIOS_FILTER_ID_VALID);
> +       for (i = 1; i < 64; i++) {
> +               nios_write_filter(tp->base_addr, i, 0x0, 0x0);
> +       }
> +
> +       /* unmask interrupts and errors */
> +       nios_write_reg(tp->base_addr, NIOS_INTMASK, (nios_int_mask & ~NIOS_TX_EMPTY ) | NIOS_INTMASK_SET );
> +       nios_write_reg(tp->base_addr, NIOS_INTMASK, nios_err_mask | NIOS_ERRMASK_SET);
> +       tp->flags &= ~NIOS_PRIV_FLAGS_TX_FULL;
> +
> +       /*
> +        * _NOTE_: DO NOT combine these 2 write statements into 1!
> +        *         The RX & TX buffer pointer reset has to be taken back.
> +        *         (which does the 2nd write)
> +        */
> +       /* pointer reset */
> +       nios_write_reg(tp->base_addr, NIOS_STATUS, NIOS_RX_POINTER_RESET | NIOS_TX_POINTER_RESET);
> +       nios_write_reg(tp->base_addr, NIOS_STATUS, NIOS_ERR_WARN | NIOS_ERR_BUSOFF);
> +
> +       /* status reset */
> +       nios_write_reg(tp->base_addr, NIOS_STATUS,
> +                        NIOS_RX_FULL
> +                      | NIOS_RX_OVFL
> +                      | NIOS_RX_NEMPTY
> +                      | NIOS_TX_FULL
> +                      | NIOS_TX_OVFL
> +                      | NIOS_TX_EMPTY);
> +
> +       /* normal mode */
> +       tp->flags &= ~NIOS_PRIV_FLAGS_ERR_BUSOFF;
> +       nios_write_reg(tp->base_addr, NIOS_TIMING, NIOS_TIME_NORMAL_MODE);
> +
> +       return;
> +}
> +
> +
> +static struct net_device_stats *nioscan_get_stats (struct net_device *ndev)
> +{
> +       struct nioscan_private *tp = netdev_priv(ndev);
> +       u32 reg_error_count;
> +
> +       reg_error_count = nios_read_reg(tp->base_addr, NIOS_ERROR_COUNT);
> +
> +       tp->stats.rx_errors = (reg_error_count & NIOS_RX_ERR_CNT_MASK) >> NIOS_RX_ERR_CNT_OFFS;
> +       tp->stats.tx_errors = (reg_error_count & NIOS_TX_ERR_CNT_MASK) >> NIOS_TX_ERR_CNT_OFFS;
> +
> +       return &tp->stats;
> +}
> +
> +
> +static void nioscan_read_msg(struct net_device *ndev)
> +{
> +       struct nioscan_private *tp = netdev_priv(ndev);
> +       struct sk_buff *skb;
> +       struct can_frame *frame;
> +       u32 reg_dlc, reg_id;
> +
> +       while (nios_read_reg(tp->base_addr, NIOS_STATUS) & NIOS_RX_PTR_MASK) {
> +               skb = alloc_skb(sizeof(struct can_frame), GFP_ATOMIC);
> +               if (unlikely(skb == NULL)) {
> +                       /* drop packet with this write */
> +                       nios_write_reg(tp->base_addr, NIOS_STATUS, NIOS_RX_NEXT_VALUE);
> +                       tp->stats.rx_dropped++;
> +                       if (net_ratelimit())
> +                               dev_warn(ND2D(ndev), "Memory squeeze, dropping packet.\n");
> +                       continue;
> +               }
> +               skb->dev = ndev;
> +
> +               frame = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
> +
> +               reg_id = nios_read_reg(tp->base_addr, NIOS_ID);
> +
> +               /* process canid */
> +               frame->can_id = nios_id_reg_to_can(reg_id);
> +
> +               /* process data length code & rtr */
> +               reg_dlc = nios_read_reg(tp->base_addr, NIOS_LENGTH);
> +               frame->can_dlc = reg_dlc & 0xf;
> +               if (reg_dlc & NIOS_RTR)
> +                       frame->can_id |= CAN_FLAG_RTR;
> +
> +               /* process payload */
> +               frame->payload.data_u32[0] = nios_read_reg(tp->base_addr, NIOS_DATA_0_3);
> +               frame->payload.data_u32[1] = nios_read_reg(tp->base_addr, NIOS_DATA_4_7);
> +
> +               wmb();
> +               nios_write_reg(tp->base_addr, NIOS_STATUS, NIOS_RX_NEXT_VALUE);
> +
> +               skb->protocol = __constant_htons(ETH_P_CAN);
> +
> +               netif_rx(skb);
> +
> +               /* do stats */
> +               ndev->last_rx = jiffies;
> +               tp->stats.rx_packets++;
> +               tp->stats.rx_bytes += frame->can_dlc;
> +       }
> +
> +       return;
> +}
> +
> +static int nioscan_hard_start_xmit(struct sk_buff *skb, struct net_device *ndev)
> +{
> +       struct nioscan_private *tp = netdev_priv(ndev);
> +       struct can_frame *frame = (struct can_frame *)skb->data;
> +       unsigned long flags;
> +       u32 reg_dlc, reg_id;
> +
> +       reg_dlc = frame->can_dlc;
> +       if (frame->can_id & CAN_FLAG_RTR)
> +               reg_dlc |= NIOS_RTR;
> +
> +       reg_id = nios_id_can_to_reg(frame->can_id);
> +       nios_write_reg(tp->base_addr, NIOS_DATA_0_3, frame->payload.data_u32[0]);
> +       nios_write_reg(tp->base_addr, NIOS_DATA_4_7, frame->payload.data_u32[1]);
> +       nios_write_reg(tp->base_addr, NIOS_ID, reg_id);
> +       wmb();
> +       nios_write_reg(tp->base_addr, NIOS_LENGTH, reg_dlc);            /* must be last, starts xmit in HW */
> +
> +       ndev->trans_start = jiffies;
> +
> +       kfree_skb(skb);
> +
> +       /* do stats */
> +       ndev->last_rx = jiffies;
> +       tp->stats.tx_packets++;
> +       tp->stats.tx_bytes += frame->can_dlc;
> +
> +       if (unlikely(nios_read_reg(tp->base_addr, NIOS_STATUS) & NIOS_TX_BUFFER_FULL)) {
> +               spin_lock_irqsave(&tp->lock, flags);
> +               if (likely(nios_read_reg(tp->base_addr, NIOS_STATUS) & NIOS_TX_BUFFER_FULL)) {
> +                       nios_enable_txempty(tp);
> +                       netif_stop_queue(ndev);
> +                       spin_unlock_irqrestore(&tp->lock, flags);
> +               } else
> +                       spin_unlock_irqrestore(&tp->lock, flags);
> +       }
> +
> +       return 0;
> +}
> +
> +
> +static void nioscan_tx_timeout(struct net_device *ndev)
> +{
> +       struct nioscan_private *tp = netdev_priv(ndev);
> +
> +       dev_err(ND2D(ndev), "%s status: 0x%08x, flags 0x%08x\n",
> +               __FUNCTION__,
> +               nios_read_reg(tp->base_addr, NIOS_STATUS),
> +               tp->flags);
> +}
> +
> +
> +static const u32 baudrate2timing[NIOS_CC_MAX][CAN_BAUD_MAX] = {
> +#if 0
> +       [NIOS_CC_33][CAN_BAUD_10K]  =     << NIOS_TIME_PRESCALE_OFFS |    << NIOS_TIME_TIMEA_OFFS |    << NIOS_TIME_TIMEB_OFFS,
> +       [NIOS_CC_33][CAN_BAUD_20K]  =     << NIOS_TIME_PRESCALE_OFFS |    << NIOS_TIME_TIMEA_OFFS |    << NIOS_TIME_TIMEB_OFFS,
> +       [NIOS_CC_33][CAN_BAUD_50K]  =     << NIOS_TIME_PRESCALE_OFFS |    << NIOS_TIME_TIMEA_OFFS |    << NIOS_TIME_TIMEB_OFFS,
> +       [NIOS_CC_33][CAN_BAUD_100K] =     << NIOS_TIME_PRESCALE_OFFS |    << NIOS_TIME_TIMEA_OFFS |    << NIOS_TIME_TIMEB_OFFS,
> +       [NIOS_CC_33][CAN_BAUD_125K] =     << NIOS_TIME_PRESCALE_OFFS |    << NIOS_TIME_TIMEA_OFFS |    << NIOS_TIME_TIMEB_OFFS,
> +       [NIOS_CC_33][CAN_BAUD_250K] =     << NIOS_TIME_PRESCALE_OFFS |    << NIOS_TIME_TIMEA_OFFS |    << NIOS_TIME_TIMEB_OFFS,
> +       [NIOS_CC_33][CAN_BAUD_500K] =     << NIOS_TIME_PRESCALE_OFFS |    << NIOS_TIME_TIMEA_OFFS |    << NIOS_TIME_TIMEB_OFFS,
> +       [NIOS_CC_33][CAN_BAUD_800K] =     << NIOS_TIME_PRESCALE_OFFS |    << NIOS_TIME_TIMEA_OFFS |    << NIOS_TIME_TIMEB_OFFS,
> +       [NIOS_CC_33][CAN_BAUD_1M]   =     << NIOS_TIME_PRESCALE_OFFS |    << NIOS_TIME_TIMEA_OFFS |    << NIOS_TIME_TIMEB_OFFS,
> +#endif
> +       [NIOS_CC_48][CAN_BAUD_10K]  = 198 << NIOS_TIME_PRESCALE_OFFS | 15 << NIOS_TIME_TIMEA_OFFS |  5 << NIOS_TIME_TIMEB_OFFS,
> +       [NIOS_CC_48][CAN_BAUD_20K]  =  98 << NIOS_TIME_PRESCALE_OFFS | 15 << NIOS_TIME_TIMEA_OFFS |  5 << NIOS_TIME_TIMEB_OFFS,
> +       [NIOS_CC_48][CAN_BAUD_50K]  =  38 << NIOS_TIME_PRESCALE_OFFS | 15 << NIOS_TIME_TIMEA_OFFS |  5 << NIOS_TIME_TIMEB_OFFS,
> +       [NIOS_CC_48][CAN_BAUD_100K] =  18 << NIOS_TIME_PRESCALE_OFFS | 15 << NIOS_TIME_TIMEA_OFFS |  5 << NIOS_TIME_TIMEB_OFFS,
> +       [NIOS_CC_48][CAN_BAUD_125K] =  14 << NIOS_TIME_PRESCALE_OFFS | 15 << NIOS_TIME_TIMEA_OFFS |  5 << NIOS_TIME_TIMEB_OFFS,
> +       [NIOS_CC_48][CAN_BAUD_250K] =   6 << NIOS_TIME_PRESCALE_OFFS | 15 << NIOS_TIME_TIMEA_OFFS |  5 << NIOS_TIME_TIMEB_OFFS,
> +       [NIOS_CC_48][CAN_BAUD_500K] =   2 << NIOS_TIME_PRESCALE_OFFS | 15 << NIOS_TIME_TIMEA_OFFS |  5 << NIOS_TIME_TIMEB_OFFS,
> +#if 0
> +       [NIOS_CC_48][CAN_BAUD_800K] =     << NIOS_TIME_PRESCALE_OFFS |    << NIOS_TIME_TIMEA_OFFS |    << NIOS_TIME_TIMEB_OFFS,
> +#endif
> +       [NIOS_CC_48][CAN_BAUD_1M]   =   0 << NIOS_TIME_PRESCALE_OFFS | 15 << NIOS_TIME_TIMEA_OFFS |  5 << NIOS_TIME_TIMEB_OFFS,
> +
> +       [NIOS_CC_60][CAN_BAUD_10K]  = 248 << NIOS_TIME_PRESCALE_OFFS | 15 << NIOS_TIME_TIMEA_OFFS |  5 << NIOS_TIME_TIMEB_OFFS,
> +       [NIOS_CC_60][CAN_BAUD_20K]  = 123 << NIOS_TIME_PRESCALE_OFFS | 15 << NIOS_TIME_TIMEA_OFFS |  5 << NIOS_TIME_TIMEB_OFFS,
> +       [NIOS_CC_60][CAN_BAUD_50K]  =  48 << NIOS_TIME_PRESCALE_OFFS | 15 << NIOS_TIME_TIMEA_OFFS |  5 << NIOS_TIME_TIMEB_OFFS,
> +       [NIOS_CC_60][CAN_BAUD_100K] =  23 << NIOS_TIME_PRESCALE_OFFS | 15 << NIOS_TIME_TIMEA_OFFS |  5 << NIOS_TIME_TIMEB_OFFS,
> +       [NIOS_CC_60][CAN_BAUD_125K] =  18 << NIOS_TIME_PRESCALE_OFFS | 15 << NIOS_TIME_TIMEA_OFFS |  5 << NIOS_TIME_TIMEB_OFFS,
> +       [NIOS_CC_60][CAN_BAUD_250K] =   8 << NIOS_TIME_PRESCALE_OFFS | 15 << NIOS_TIME_TIMEA_OFFS |  5 << NIOS_TIME_TIMEB_OFFS,
> +       [NIOS_CC_60][CAN_BAUD_500K] =   3 << NIOS_TIME_PRESCALE_OFFS | 15 << NIOS_TIME_TIMEA_OFFS |  5 << NIOS_TIME_TIMEB_OFFS,
> +#if 0
> +       [NIOS_CC_60][CAN_BAUD_800K] =     << NIOS_TIME_PRESCALE_OFFS |    << NIOS_TIME_TIMEA_OFFS |    << NIOS_TIME_TIMEB_OFFS,
> +       [NIOS_CC_60][CAN_BAUD_1M]   =     << NIOS_TIME_PRESCALE_OFFS |    << NIOS_TIME_TIMEA_OFFS |    << NIOS_TIME_TIMEB_OFFS,
> +#endif
> +};
> +
> +
> +static const int freq2enum[] = {
> +       [NIOS_CC_33] = 33300000,
> +       [NIOS_CC_48] = 48700000,
> +       [NIOS_CC_60] = 60000000,
> +};
> +
> +
> +static int do_set_baudrate(struct net_device *ndev, struct can_baudrate *br)
> +{
> +       struct nioscan_private *tp = netdev_priv(ndev);
> +       u32 reg_timing;
> +       int err;
> +
> +       dev_dbg(ND2D(ndev), "%s baudrate: %d\n", __FUNCTION__, br->baudrate);
> +
> +       err = -EINVAL;
> +       if (br->baudrate == CAN_BAUD_BTR_NIOS) {
> +               struct can_baudrate_nios *br_nios = &br->btr.nios;
> +
> +               reg_timing = ((br_nios->prescale << NIOS_TIME_PRESCALE_OFFS) & NIOS_TIME_PRESCALE_MASK) |
> +                            ((br_nios->timea << NIOS_TIME_TIMEA_OFFS) & NIOS_TIME_TIMEA_MASK) |
> +                            ((br_nios->timeb << NIOS_TIME_TIMEB_OFFS) & NIOS_TIME_TIMEB_MASK);
> +
> +       } else if (br->baudrate >= 0 && br->baudrate < CAN_BAUD_MAX)
> +               reg_timing = baudrate2timing[tp->chip_clock][br->baudrate];
> +       else
> +               goto exit;
> +
> +       err = 0;
> +
> +       reg_timing |= NIOS_TIME_SET_PRESCALE | NIOS_TIME_SET_TIMEA | NIOS_TIME_SET_TIMEB;
> +       nios_write_reg(tp->base_addr, NIOS_TIMING, reg_timing);
> +       tp->baudrate = br->baudrate;
> +
> + exit:
> +       return err;
> +}
> +
> +
> +static int do_get_baudrate(struct net_device *ndev, struct can_baudrate *br)
> +{
> +       struct nioscan_private *tp = netdev_priv(ndev);
> +       u32 reg_timing;
> +
> +       br->baudrate = tp->baudrate;
> +       if(br->baudrate == CAN_BAUD_BTR_NIOS) {
> +               struct can_baudrate_nios *br_nios = &br->btr.nios;
> +
> +               reg_timing = nios_read_reg(tp->base_addr, NIOS_TIMING);
> +
> +               br_nios->prescale = (reg_timing & NIOS_TIME_PRESCALE_MASK) >> NIOS_TIME_PRESCALE_OFFS;
> +               br_nios->timea    = (reg_timing & NIOS_TIME_TIMEA_MASK)    >> NIOS_TIME_TIMEA_OFFS;
> +               br_nios->timeb    = (reg_timing & NIOS_TIME_TIMEB_MASK)    >> NIOS_TIME_TIMEB_OFFS;
> +       }
> +
> +       return 0;
> +}
> +
> +
> +static int do_set_mode(struct net_device *ndev, enum CAN_MODE mode)
> +{
> +       struct nioscan_private *tp = netdev_priv(ndev);
> +       unsigned long flags;
> +
> +       switch (mode) {
> +       case CAN_MODE_START:
> +               spin_lock_irqsave(&tp->lock, flags);
> +
> +               tp->flags &= ~NIOS_PRIV_FLAGS_ERR_BUSOFF;
> +               nios_write_reg(tp->base_addr, NIOS_STATUS, NIOS_ERR_BUSOFF);            /* ack busoff int */
> +               nios_write_reg(tp->base_addr, NIOS_TIMING, NIOS_TIME_NORMAL_MODE);      /* request normal mode */
> +
> +               if (!(tp->flags & NIOS_PRIV_FLAGS_TX_FULL))
> +                       netif_wake_queue(ndev);
> +
> +               spin_unlock_irqrestore(&tp->lock, flags);
> +
> +               break;
> +       default:
> +               return -EOPNOTSUPP;
> +       }
> +
> +       return 0;
> +}
> +
> +
> +static int do_get_state(struct net_device *ndev, enum CAN_STATE *state)
> +{
> +       struct nioscan_private *tp = netdev_priv(ndev);
> +       u32 reg_status;
> +
> +       *state = -1;
> +
> +       reg_status = nios_read_reg(tp->base_addr, NIOS_STATUS);
> +       if      (reg_status & NIOS_ERR_PASSIVE)
> +               *state = CAN_STATE_BUS_PASSIVE;
> +       else if (reg_status & NIOS_ERR_ACTIVE)
> +               *state = CAN_STATE_ACTIVE;
> +       else if (tp->flags & NIOS_PRIV_FLAGS_ERR_BUSOFF)
> +               *state = CAN_STATE_BUS_OFF;
> +       else if ((reg_status & (NIOS_ERR_PASSIVE | NIOS_ERR_ACTIVE)) == 0) {
> +               *state = CAN_STATE_BUS_OFF;
> +               dev_err(ND2D(ndev), "Device is in Busoff, but we didn't get IRQ. (status: 0x%08x)\n", reg_status);
> +       }
> +       return 0;
> +}
> +
> +
> +static int do_set_hwfilt(struct net_device *ndev, struct can_hwfilt *hwfilt)
> +{
> +       struct nioscan_private *tp = netdev_priv(ndev);
> +       struct can_hwfilt_accmask *accmask = &hwfilt->param.accmask;
> +       unsigned long flags;
> +       int i;
> +
> +       if (hwfilt->type != CAN_HWFILT_ACCMASK)
> +               return -EOPNOTSUPP;
> +       if (hwfilt->param.accmask.len > 64)
> +               return -E2BIG;
> +
> +       spin_lock_irqsave(&tp->lock, flags);
> +       if (hwfilt->param.accmask.len == 0) {
> +               nios_write_filter(tp->base_addr, 0,
> +                                 0x0 | NIOS_FILTER_MASK_VALID,
> +                                 0x0 | NIOS_FILTER_ID_VALID);
> +               i = 1;  /* i is next filter to clear */
> +       } else
> +               for (i = 0; i < accmask->len; i++)
> +                       nios_write_filter(tp->base_addr, i,
> +                                         nios_id_can_to_reg(accmask->filter[i].can_mask) | NIOS_FILTER_MASK_VALID,
> +                                         nios_id_can_to_reg(accmask->filter[i].can_id)   | NIOS_FILTER_ID_VALID);
> +
> +       /* clean remaining filters */
> +       for (/* nix */; i < NIOS_FILTER_NR; i++)
> +               nios_write_filter(tp->base_addr, i, 0x0, 0x0);
> +
> +       spin_unlock_irqrestore(&tp->lock, flags);
> +
> +       return 0;
> +}
> +
> +
> +static int nioscan_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd)
> +{
> +       struct can_baudrate *baudrate = (struct can_baudrate *)&ifr->ifr_ifru;
> +       union can_settings *settings = (union can_settings *)&ifr->ifr_ifru;
> +       struct can_hwfilt *hwfilt = (struct can_hwfilt *)&ifr->ifr_ifru;
> +
> +       dev_dbg(ND2D(ndev), "(%s) 0x%08x %p\n", __FUNCTION__, cmd);
> +
> +       switch (cmd) {
> +       case SIOCSCANBAUDRATE:
> +               return do_set_baudrate(ndev, baudrate);
> +       case SIOCGCANBAUDRATE:
> +               return do_get_baudrate(ndev, baudrate);
> +       case SIOCSCANMODE:
> +               return do_set_mode(ndev, settings->mode);
> +       case SIOCGCANSTATE:
> +               return do_get_state(ndev, &settings->state);
> +       case SIOCSCANHWFILT: {
> +               struct can_hwfilt my_hwfilt = {
> +                       .type   = CAN_HWFILT_ACCMASK,
> +               };
> +               int err;
> +               size_t len;
> +
> +               if (hwfilt->type != CAN_HWFILT_ACCMASK)
> +                       return -EOPNOTSUPP;
> +               if (hwfilt->param.accmask.len > 64)
> +                       return -E2BIG;
> +               if (hwfilt->param.accmask.len == 0 &&
> +                   hwfilt->param.accmask.filter != NULL)
> +                       return -EINVAL;
> +
> +               len = hwfilt->param.accmask.len * sizeof(struct can_filter);
> +               if (len > 0) {
> +                       my_hwfilt.param.accmask.filter = kmalloc(len, GFP_KERNEL);
> +                       if (!my_hwfilt.param.accmask.filter)
> +                               return -ENOMEM;
> +
> +                       err = copy_from_user(my_hwfilt.param.accmask.filter,
> +                                            hwfilt->param.accmask.filter,
> +                                            len);
> +                       if (err) {
> +                               err = -EACCES;
> +                               goto out_free;
> +                       }
> +               }
> +
> +               err = do_set_hwfilt(ndev, &my_hwfilt);
> +
> +       out_free:
> +               if (my_hwfilt.param.accmask.filter)
> +                       kfree(my_hwfilt.param.accmask.filter);
> +
> +               return err;
> +       }
> +       default:
> +               return -EOPNOTSUPP;
> +       }
> +
> +       return 0; /* notreached */
> +}
> +
> +
> +static void do_weird_interrupt(struct net_device *ndev, u32 reg_status)
> +{
> +       struct nioscan_private *tp = netdev_priv(ndev);
> +       int verbose = net_ratelimit();
> +
> +       if (verbose)
> +               dev_err(ND2D(ndev), "Abnormal interrupt, status: 0x%08x, flags 0x%08x\n", reg_status, tp->flags);
> +
> +       if (reg_status & NIOS_ERR_WARN) {
> +               nios_write_reg(tp->base_addr, NIOS_STATUS, NIOS_ERR_WARN);
> +               dev_err(ND2D(ndev), "Device triggered a warning INT\n");
> +       }
> +
> +       if (reg_status & NIOS_ERR_BUSOFF) {
> +               tp->flags |= NIOS_PRIV_FLAGS_ERR_BUSOFF;
> +               netif_stop_queue(ndev);
> +               nios_write_reg(tp->base_addr, NIOS_STATUS, NIOS_ERR_BUSOFF);
> +               dev_err(ND2D(ndev), "Device is in Bus Off Mode\n");
> +       }
> +
> +
> +       /* RX-Queue full? Ooops */
> +       if (unlikely (reg_status & NIOS_RX_FULL)) {
> +               nios_write_reg(tp->base_addr, NIOS_STATUS, NIOS_RX_FULL);
> +               if (verbose)
> +                       dev_warn(ND2D(ndev), "RX-Buffer Full\n");
> +       }
> +
> +       if (reg_status & NIOS_RX_OVFL) {
> +               nios_write_reg(tp->base_addr, NIOS_STATUS, NIOS_RX_OVFL);
> +               tp->stats.rx_fifo_errors++;
> +               if (verbose)
> +                       dev_err(ND2D(ndev), "RX-Buffer Overflow\n");
> +       }
> +
> +
> +       /* This should not happen, the netif_queue should have been stopped before */
> +       if (reg_status & NIOS_TX_OVFL) {
> +               netif_stop_queue(ndev);
> +               nios_enable_txempty(tp);
> +               nios_write_reg(tp->base_addr, NIOS_STATUS, NIOS_TX_OVFL);
> +
> +               tp->stats.tx_fifo_errors++;
> +               if (verbose)
> +                       dev_err(ND2D(ndev), "TX-Buffer Overflow\n");
> +       }
> +
> +       return;
> +}
> +
> +
> +static irqreturn_t nioscan_interrupt(int irq, void *dev_id, struct pt_regs *regs)
> +{
> +       struct net_device *ndev = (struct net_device *) dev_id;
> +       struct nioscan_private *tp = netdev_priv(ndev);
> +       u32 reg_status;
> +       u32 mask = nios_all_mask;
> +       irqreturn_t handled = IRQ_NONE;
> +       unsigned long flags;
> +       int boguscnt = max_interrupt_work;
> +
> +       spin_lock_irqsave(&tp->lock, flags);
> +       do {
> +               if (unlikely(tp->flags & NIOS_PRIV_FLAGS_TX_FULL))
> +                       mask |= NIOS_TX_EMPTY;
> +               else
> +                       mask &= ~NIOS_TX_EMPTY;
> +
> +               reg_status = nios_read_reg(tp->base_addr, NIOS_STATUS);
> +
> +               /* ready with work or shared IRQ-line? */
> +               if (!(reg_status & mask))
> +                       goto exit;
> +
> +               handled = IRQ_HANDLED;
> +
> +               /* packet in the RX fifo? */
> +               if (reg_status & NIOS_RX_BUSY)
> +                       nioscan_read_msg(ndev);
> +
> +               /* RX-Queue not empty INT? */
> +               if (reg_status & NIOS_RX_NEMPTY)
> +                       nios_write_reg(tp->base_addr, NIOS_STATUS, NIOS_RX_NEMPTY);
> +
> +
> +               /* space left in TX fifo? */
> +               if ((tp->flags & NIOS_PRIV_FLAGS_TX_FULL) &&
> +                   !(reg_status & NIOS_TX_BUFFER_FULL)) {
> +                       nios_disable_txempty(tp);
> +                       netif_wake_queue(ndev);
> +               }
> +
> +               /* TX-Queue empty INT? */
> +               if (reg_status & NIOS_TX_EMPTY)
> +                       nios_write_reg(tp->base_addr, NIOS_STATUS, NIOS_TX_EMPTY);
> +
> +               if (unlikely(reg_status & (nios_int_mask_abnormal | nios_err_mask)))
> +                       do_weird_interrupt(ndev, reg_status);
> +       } while (--boguscnt > 0);
> +
> +       if (unlikely(boguscnt <= 0)) {
> +               dev_warn(ND2D(ndev), "Too much work at interrupt, "
> +                        "status (at enter): 0x%08x, now: 0x%08x\n",
> +                        reg_status,
> +                        nios_read_reg(tp->base_addr, NIOS_STATUS));
> +
> +               /* Clear all interrupt sources. */
> +               nios_write_reg(tp->base_addr, NIOS_STATUS,
> +                              nios_int_mask | nios_err_mask);
> +       }
> +
> + exit:
> +       spin_unlock_irqrestore(&tp->lock, flags);
> +
> +       return handled;
> +}
> +
> +
> +static int nioscan_open(struct net_device *ndev)
> +{
> +       struct nioscan_private *tp = netdev_priv(ndev);
> +       int ret;
> +
> +       if (tp->baudrate == CAN_BAUD_UNCONFIGURED) {
> +               dev_err(ND2D(ndev), "Baudrate is unconfigured\n");
> +               return -EINVAL;
> +       }
> +
> +       /* Grab the IRQ */
> +       ret = request_irq(ndev->irq, &nioscan_interrupt, 0, ndev->name, ndev);
> +       if (ret) {
> +               dev_err(ND2D(ndev), "Request of interrupt %d failed, err: %d\n", ndev->irq, ret);
> +               ret = -ENODEV;
> +               goto err_out;
> +       }
> +
> +       set_irq_type(ndev->irq, IRQT_FALLING);
> +
> +       /* reset chip, setup chip, unmask IRQs */
> +       nioscan_chip_reset(ndev);
> +
> +       netif_wake_queue(ndev);
> +
> +       return 0;
> +err_out:
> +       return ret;
> +}
> +
> +
> +static int nioscan_stop(struct net_device *ndev)
> +{
> +       struct nioscan_private *tp = netdev_priv(ndev);
> +       unsigned long flags;
> +
> +       netif_stop_queue(ndev);
> +
> +       /* mask all IRQs and errors */
> +       spin_lock_irqsave(&tp->lock, flags);
> +       nios_write_reg(tp->base_addr, NIOS_INTMASK, 0x0 | NIOS_INTMASK_SET);
> +       nios_write_reg(tp->base_addr, NIOS_INTMASK, 0x0 | NIOS_ERRMASK_SET);
> +       spin_unlock_irqrestore(&tp->lock, flags);
> +
> +       free_irq(ndev->irq, ndev);
> +
> +       return 0;
> +}
> +
> +
> +static ssize_t nios_show_version(struct device *dev, struct device_attribute *attr, char *buf)
> +{
> +       struct net_device *ndev = dev_get_drvdata(dev);
> +       struct nioscan_private *tp = netdev_priv(ndev);
> +       unsigned int version = nios_read_reg(tp->base_addr, NIOS_VERSION);
> +
> +       return sprintf (buf, "%d/20%02d (quartus %d, rev %d)\n",
> +                       NIOS_VERSION_MONTH(version),
> +                       NIOS_VERSION_YEAR(version),
> +                       NIOS_VERSION_QUARTUS(version),
> +                       NIOS_VERSION_REV(version));
> +}
> +
> +static DEVICE_ATTR(version, S_IWUSR|S_IRUGO, nios_show_version, NULL);
> +
> +
> +static int nioscan_get_clock(struct net_device *ndev)
> +{
> +       struct device *dev = ND2D(ndev);
> +       struct nioscan_private *tp = netdev_priv(ndev);
> +       struct fpbus_unit_info *info;
> +       int i, clock;
> +
> +       if (chip_clock == -1) { /* use fpbus */
> +               if (!dev->platform_data) {
> +                       dev_err(dev, "no platform data found. please use module parameter so set chip_clock.\n");
> +                       return -ENODEV;
> +               }
> +               info = (struct fpbus_unit_info *)(dev->platform_data);
> +               clock = info->fpga_clock;
> +       } else                  /* use module param */
> +               clock = chip_clock;
> +
> +       for (i = 0; i < NIOS_CC_MAX; i++) {
> +               if (freq2enum[i] == clock) {
> +                       tp->chip_clock = (enum nios_chip_clock)i;
> +                       return 0;
> +               }
> +       }
> +
> +       dev_err(dev, "unknown chip clock %d, please send patches :)\n", clock);
> +
> +       return -ENODEV;
> +}
> +
> +
> +static int nioscan_probe_check_version(struct net_device *ndev, u32 version)
> +{
> +       const struct nioscan_private *tp = netdev_priv(ndev);
> +
> +       if (version == NIOS_VERSION_WORKING)
> +               return 0;
> +
> +       if (version == 0x0604291e ||
> +           version == 0x0705322d ||
> +           version == 0x0705322e ||
> +           version == 0x0705322f ||
> +           version == 0x07053230) {
> +               dev_err(ND2D(ndev),
> +                       "WARNING - buggy version 0x%08x found, please update your IP-Core\n",
> +                       version);
> +
> +       } else if (((NIOS_VERSION_YEAR(version) == NIOS_VERSION_YEAR(NIOS_VERSION_WORKING)) &&
> +                   (NIOS_VERSION_MONTH(version) > NIOS_VERSION_MONTH(NIOS_VERSION_WORKING))) ||
> +                  (NIOS_VERSION_YEAR(version) > NIOS_VERSION_YEAR(NIOS_VERSION_WORKING))) {
> +
> +               dev_warn(ND2D(ndev),
> +                        "found nioscan core from %d/20%02d (quartus %d, rev %d). "
> +                        "this core has not been tested, but it's newer than the latest tested version...\n",
> +                        NIOS_VERSION_MONTH(version),
> +                        NIOS_VERSION_YEAR(version),
> +                        NIOS_VERSION_QUARTUS(version),
> +                        NIOS_VERSION_REV(version));
> +       } else
> +               dev_err(ND2D(ndev), "no or old nios CAN chip found on address 0x%p, found 0x%08x instead\n",
> +                       tp->base_addr, version);
> +
> +       return -ENODEV;
> +}
> +
> +static int nioscan_probe(struct net_device *ndev)
> +{
> +       struct nioscan_private *tp = netdev_priv(ndev);
> +       u32 version = nios_read_reg(tp->base_addr, NIOS_VERSION);
> +       int ret;
> +
> +       ret = nioscan_probe_check_version(ndev, version);
> +       if (ret)
> +               return ret;
> +
> +       dev_info(ND2D(ndev), "found nioscan core from %d/20%02d (quartus %d, rev %d)\n",
> +                NIOS_VERSION_MONTH(version),
> +                NIOS_VERSION_YEAR(version),
> +                NIOS_VERSION_QUARTUS(version),
> +                NIOS_VERSION_REV(version));
> +
> +       ret = nioscan_get_clock(ndev);
> +       if (ret)
> +               return ret;
> +       tp->baudrate = CAN_BAUD_UNCONFIGURED;
> +
> +       ndev->open            = nioscan_open;
> +       ndev->stop            = nioscan_stop;
> +       ndev->hard_start_xmit = nioscan_hard_start_xmit;
> +       ndev->do_ioctl        = nioscan_ioctl;
> +       ndev->get_stats       = nioscan_get_stats;
> +       ndev->tx_timeout      = nioscan_tx_timeout;
> +       ndev->watchdog_timeo  = TX_TIMEOUT;
> +
> +       spin_lock_init(&tp->lock);
> +
> +       ret = register_netdev(ndev);
> +       if (ret) {
> +               dev_err(ND2D(ndev), "register_netdev failed\n");
> +               return -1;
> +       }
> +
> +       return 0;
> +}
> +
> +
> +static int nioscan_drv_probe(struct device *dev)
> +{
> +       struct platform_device *pdev = to_platform_device(dev);
> +       struct nioscan_private *tp;
> +       struct resource *res;
> +       struct net_device *ndev;
> +       void __iomem *addr;
> +       int irq, err;
> +
> +       err = -ENODEV;
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       irq = platform_get_irq(pdev, 0);
> +       if (!res || !irq) {
> +               goto exit;
> +       }
> +
> +       err = -ENOMEM;
> +       ndev = alloc_candev(sizeof(struct nioscan_private));
> +       if (!ndev) {
> +               dev_err(dev, "could not allocate device\n");
> +               goto exit;
> +       }
> +
> +       tp = netdev_priv(ndev);
> +
> +       /*
> +        * Request the regions.
> +        */
> +       err = -EBUSY;
> +       if (!request_mem_region(res->start, res->end - res->start + 1, CARDNAME)) {
> +               goto exit_free_netdev;
> +       }
> +
> +       SET_MODULE_OWNER(ndev);
> +       SET_NETDEV_DEV(ndev, dev);
> +
> +       err = -ENOMEM;
> +       addr = ioremap_nocache(res->start, res->end - res->start + 1);
> +       if (!addr) {
> +               goto exit_release;
> +       }
> +
> +       ndev->irq = irq;
> +       tp->base_addr = addr;
> +
> +       dev_set_drvdata(dev, ndev);
> +
> +       err = nioscan_probe(ndev);
> +       if (err) {
> +               dev_err(dev, "Device not found (%d).\n", err);
> +               goto exit_unmap;
> +       }
> +
> +       err = device_create_file(dev, &dev_attr_version);
> +        if (err)
> +                goto exit_unregister;
> +
> +       err = 0;
> +
> + exit:
> +       return err;
> +
> + exit_unregister:
> +       unregister_netdev(ndev);
> + exit_unmap:
> +       iounmap(addr);
> + exit_release:
> +       release_mem_region(res->start, res->end - res->start + 1);
> + exit_free_netdev:
> +       dev_set_drvdata(dev, NULL);
> +       free_netdev(ndev);
> +       goto exit;
> +}
> +
> +
> +static int nioscan_drv_remove(struct device *dev)
> +{
> +       struct platform_device *pdev = to_platform_device(dev);
> +       struct net_device *ndev = dev_get_drvdata(dev);
> +       struct nioscan_private *tp = netdev_priv(ndev);
> +       struct resource *res;
> +
> +       dev_set_drvdata(dev, NULL);
> +
> +        device_remove_file(dev, &dev_attr_version);
> +
> +       unregister_netdev(ndev);
> +
> +       iounmap(tp->base_addr);
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       release_mem_region(res->start, res->end - res->start + 1);
> +
> +       free_netdev(ndev);
> +
> +       return 0;
> +}
> +
> +static int nioscan_drv_suspend(struct device *dev, u32 state, u32 level)
> +{
> +       dev_err(dev, "suspend not implemented\n");
> +       return 0;
> +}
> +
> +static int nioscan_drv_resume(struct device *dev, u32 level)
> +{
> +       dev_err(dev, "resume not implemented\n");
> +       return 0;
> +}
> +
> +static struct device_driver nioscan_driver = {
> +       .name           = CARDNAME,
> +       .bus            = &platform_bus_type,
> +       .probe          = nioscan_drv_probe,
> +       .remove         = __devexit_p(nioscan_drv_remove),
> +       .suspend        = nioscan_drv_suspend,
> +       .resume         = nioscan_drv_resume,
> +};
> +
> +static int __init nioscan_init(void)
> +{
> +       return driver_register(&nioscan_driver);
> +}
> +
> +static void __exit nioscan_cleanup(void)
> +{
> +       driver_unregister(&nioscan_driver);
> +}
> +
> +module_init(nioscan_init);
> +module_exit(nioscan_cleanup);
> +
> +MODULE_LICENSE("GPL v2");
> --
> 2.9.3
>

^ permalink raw reply

* [PATCH] can: add ifi_nioscan driver
From: Marc Kleine-Budde @ 2016-09-29  7:09 UTC (permalink / raw)
  To: d.dusha; +Cc: linux-can, Marc Kleine-Budde

Just for reference.

Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
---
Hey Damien,

the old driver just for reference, maybe this helps.

regards,
Marc

 drivers/net/can/ifi_nioscan.c | 1070 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 1070 insertions(+)
 create mode 100644 drivers/net/can/ifi_nioscan.c

diff --git a/drivers/net/can/ifi_nioscan.c b/drivers/net/can/ifi_nioscan.c
new file mode 100644
index 000000000000..8bb04473c07d
--- /dev/null
+++ b/drivers/net/can/ifi_nioscan.c
@@ -0,0 +1,1070 @@
+/*
+ * drivers/can/nioscan.c
+ *
+ * Copyright (C) 2005, 2006, 2007
+ *
+ * - Sascha Hauer, Marc Kleine-Budde, Pengutronix
+ *
+ * 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
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* #define DEBUG	1 */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/can.h>
+#include <linux/version.h>
+#include <linux/fpbus.h>
+
+#include "can_driver_compat.h"
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#define NIOS_VERSION_WORKING	0x0b053335
+
+/* Linux compatibility stuff */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13)
+#define nios_show_version(_dev, _attr, _buf)	nios_show_version(_dev, _buf)
+#endif
+
+#define CARDNAME	"nioscan"
+
+#define TX_TIMEOUT 	(6*HZ)
+
+static int max_interrupt_work = 4;
+module_param(max_interrupt_work, int, 0400);
+MODULE_PARM_DESC(max_interrupt_work, "maximum events handled per interrupt");
+
+static int chip_clock = -1;
+module_param(chip_clock, int, 0400);
+MODULE_PARM_DESC(chip_clock, "Chip Clock Frequency in HZ, default use FPBUS value");
+
+enum nios_regs {
+	NIOS_LENGTH 		= 0x000,
+	NIOS_ID 		= 0x004,
+	NIOS_DATA_0_3 		= 0x008,
+	NIOS_DATA_4_7 		= 0x00C,
+	NIOS_TIMING 		= 0x010,
+	NIOS_INTMASK 		= 0x014,
+	NIOS_STATUS 		= 0x018,
+	NIOS_ERROR_COUNT 	= 0x01C,
+	NIOS_VERSION		= 0x020,
+	NIOS_FILTER_MASK 	= 0x200,
+	NIOS_FILTER_ID		= 0x204,
+};
+
+/*
+ * Bit definitionss of Data Lengh Code Reg
+ */
+#define NIOS_RTR 		(1<<4)
+
+/*
+ * Bit definitionss of Identifier Reg
+ */
+#define NIOS_EXTID 		(1<<29)
+
+/*
+ * Bit Definitions of Timing Reg
+ */
+#define NIOS_TIME_PRESCALE_OFFS (16)		/* offset of prescale value 	*/
+#define NIOS_TIME_PRESCALE_MASK (0xff << NIOS_TIME_PRESCALE_OFFS)
+#define NIOS_TIME_TIMEA_OFFS	(8)		/* offset of timeA value    	*/
+#define NIOS_TIME_TIMEA_MASK	(0x1f << NIOS_TIME_TIMEA_OFFS)
+#define NIOS_TIME_TIMEB_OFFS	(0)		/* offset of timeB value    	*/
+#define NIOS_TIME_TIMEB_MASK	(0x1f << NIOS_TIME_TIMEB_OFFS)
+#define NIOS_TIME_SET_PRESCALE	(1 << 31)	/* prescale value set  		*/
+#define NIOS_TIME_SET_TIMEA    	(1 << 15)	/* timea value set    	 	*/
+#define NIOS_TIME_SET_TIMEB    	(1 <<  7)	/* timeb value set     		*/
+#define NIOS_TIME_NORMAL_MODE	(1 << 24)	/* normal mode         		*/
+#define NIOS_TIME_SET_SILENT	(1 << 30)	/* set silent mode		*/
+#define NIOS_TIME_SILENT_MODE	(1 << 28)	/* silent mode			*/
+
+/*
+ * Bit Definitions of Interrupt Mask Reg
+ */
+#define NIOS_INTMASK_SET	(1 << 31)	/* set interrupt mask		*/
+#define NIOS_RX_FULL		(1 << 29)	/* receive buffer full		*/
+#define NIOS_RX_OVFL		(1 << 28)	/* receive buffer overflow	*/
+#define NIOS_RX_NEMPTY		(1 << 27)	/* receive buffer not empty	*/
+#define NIOS_TX_FULL		(1 << 26)	/* transmit buffer full		*/
+#define NIOS_TX_OVFL		(1 << 25)	/* transmit buffer overflow	*/
+#define NIOS_TX_EMPTY		(1 << 24)	/* transmit buffer empty	*/
+#define NIOS_ERRMASK_SET	(1 << 7)	/* set error mask		*/
+#define NIOS_ERR_WARN		(1 << 1)	/* error warning		*/
+#define NIOS_ERR_BUSOFF		(1 << 0)	/* error bus off		*/
+
+static const u32 nios_int_mask		= NIOS_RX_OVFL  | NIOS_TX_OVFL | NIOS_RX_FULL | NIOS_RX_NEMPTY | NIOS_TX_EMPTY;
+static const u32 nios_int_mask_abnormal	= NIOS_RX_OVFL  | NIOS_TX_OVFL | NIOS_RX_FULL;
+static const u32 nios_err_mask		= NIOS_ERR_WARN | NIOS_ERR_BUSOFF;
+static const u32 nios_all_mask		= NIOS_RX_OVFL  | NIOS_TX_OVFL | NIOS_RX_FULL |
+	NIOS_RX_NEMPTY | NIOS_TX_EMPTY | NIOS_ERR_WARN | NIOS_ERR_BUSOFF;
+
+/*
+ * Bit Definitions of Status Reg
+ */
+#define NIOS_RX_BUSY		(1 << 31)	/* receive busy			*/
+#define NIOS_TX_BUSY		(1 << 30)	/* transmit busy		*/
+#define NIOS_RX_BUFFER_FULL	(1 << 23)	/* receive buffer full		*/
+#define NIOS_TX_BUFFER_FULL	(1 << 22)	/* transmit buffer full		*/
+#define NIOS_RX_POINTER_RESET	(1 << 23)	/* reset receive pointer	*/
+#define NIOS_TX_POINTER_RESET	(1 << 15)	/* reset transmit pointer	*/
+#define NIOS_RX_NEXT_VALUE	(1 <<  7)	/* receive fifo next value	*/
+#define NIOS_ERR_PASSIVE	(1 <<  3)	/* error passive mode		*/
+#define NIOS_ERR_ACTIVE		(1 <<  2)	/* error active mode		*/
+#define NIOS_RX_PTR_MASK 	(0x003F0000)	/* receive buffer pointer mask	*/
+#define NIOS_RX_PTR_OFFS 	(16)		/* receive buffer pointer offset*/
+#define NIOS_TX_PTR_MASK 	(0x00003F00)	/* transmit buffer pointer mask	*/
+#define NIOS_TX_PTR_OFFS 	(8)		/* transmit buf. pointer offset	*/
+
+/*
+ * Bit Definitions of Error Counter Reg
+ */
+#define NIOS_RX_ERR_CNT_MASK	(0x00ff0000)
+#define NIOS_TX_ERR_CNT_MASK	(0x000001ff)
+#define NIOS_RX_ERR_CNT_OFFS	(16)
+#define NIOS_TX_ERR_CNT_OFFS	(0)
+
+/*
+ * Bit Definitions of Filter mask and Filter Id
+ */
+#define NIOS_FILTER_NR		(64)
+#define NIOS_FILTER_MASK_VALID	(1 << 31)
+#define NIOS_FILTER_ID_VALID	(1 << 31)
+
+#define NIOS_VERSION_MONTH(version)   ((version >> 24) & 0xff)
+#define NIOS_VERSION_YEAR(version)    ((version >> 16) & 0xff)
+#define NIOS_VERSION_QUARTUS(version) ((version >>  8) & 0xff)
+#define NIOS_VERSION_REV(version)     ((version >>  0) & 0xff)
+
+enum nios_chip_clock {
+	NIOS_CC_33 = 0,
+	NIOS_CC_48,
+	NIOS_CC_60,
+	NIOS_CC_MAX,
+};
+
+enum nios_priv_flags {
+	NIOS_PRIV_FLAGS_ERR_BUSOFF	= 1 << 0,
+	NIOS_PRIV_FLAGS_TX_FULL		= 1 << 1,
+};
+
+struct nioscan_private
+{
+	void __iomem		*base_addr;
+	struct net_device_stats	stats;
+	unsigned int 		baudrate;
+	enum nios_priv_flags	flags;
+	enum nios_chip_clock	chip_clock;
+	spinlock_t		lock;
+};
+
+static int do_set_baudrate(struct net_device *ndev, struct can_baudrate *br);
+
+static inline u32 nios_read_reg(const void __iomem *base, enum nios_regs reg)
+{
+	volatile u32 val;
+
+	val = readl(base + reg);
+
+	pr_debug("(%s) base 0x%p, reg 0x%x, val 0x%08x\n",
+		 __FUNCTION__, base, reg, val);
+
+	return val;
+}
+
+static inline void nios_write_reg(const void __iomem *base, enum nios_regs reg, u32 val)
+{
+	pr_debug("(%s) base 0x%08lx, reg 0x%x, val 0x%08x\n",
+		 __FUNCTION__, base, reg, val);
+
+	writel(val, base + reg);
+}
+
+static inline void nios_write_filter(const void __iomem *base, unsigned int nr,
+				     u32 mask, u32 id)
+{
+	nios_write_reg(base, NIOS_FILTER_MASK + (nr << 3), mask);
+	nios_write_reg(base, NIOS_FILTER_ID   + (nr << 3), id);
+}
+
+static inline u32 nios_id_can_to_reg(u32 can_id)
+{
+	if (can_id & CAN_FLAG_EXTENDED) {
+		return ((can_id & 0x1ffC0000) >> 18) |
+			((can_id & 0x0003ffff) << 11) |
+			NIOS_EXTID;
+	} else
+		return can_id & 0x7ff;
+}
+
+static inline u32 nios_id_reg_to_can(u32 reg_id)
+{
+	if (reg_id & NIOS_EXTID) {
+		return ((reg_id & 0x000007ff) << 18) |
+			((reg_id & 0x1ffff800) >> 11) |
+			CAN_FLAG_EXTENDED;
+	} else
+		return reg_id & 0x7ff;
+}
+
+static inline void nios_enable_txempty(struct nioscan_private *tp)
+{
+	/* ACK */
+	nios_write_reg(tp->base_addr, NIOS_STATUS, NIOS_TX_EMPTY);
+
+	/* activate int */
+	nios_write_reg(tp->base_addr, NIOS_INTMASK, nios_int_mask | NIOS_INTMASK_SET);
+	tp->flags |= NIOS_PRIV_FLAGS_TX_FULL;
+}
+
+
+static inline void nios_disable_txempty(struct nioscan_private *tp)
+{
+	nios_write_reg(tp->base_addr, NIOS_INTMASK, (nios_int_mask & ~NIOS_TX_EMPTY ) | NIOS_INTMASK_SET );
+	tp->flags &= ~NIOS_PRIV_FLAGS_TX_FULL;
+}
+
+static void nioscan_chip_reset(struct net_device *ndev)
+{
+	struct nioscan_private *tp = netdev_priv(ndev);
+	int i;
+
+	/* set filter */
+	nios_write_filter(tp->base_addr, 0,
+			  0x0 | NIOS_FILTER_MASK_VALID,
+			  0x0 | NIOS_FILTER_ID_VALID);
+	for (i = 1; i < 64; i++) {
+		nios_write_filter(tp->base_addr, i, 0x0, 0x0);
+	}
+
+	/* unmask interrupts and errors */
+	nios_write_reg(tp->base_addr, NIOS_INTMASK, (nios_int_mask & ~NIOS_TX_EMPTY ) | NIOS_INTMASK_SET );
+	nios_write_reg(tp->base_addr, NIOS_INTMASK, nios_err_mask | NIOS_ERRMASK_SET);
+	tp->flags &= ~NIOS_PRIV_FLAGS_TX_FULL;
+
+	/*
+	 * _NOTE_: DO NOT combine these 2 write statements into 1!
+	 *         The RX & TX buffer pointer reset has to be taken back.
+	 *         (which does the 2nd write)
+	 */
+	/* pointer reset */
+	nios_write_reg(tp->base_addr, NIOS_STATUS, NIOS_RX_POINTER_RESET | NIOS_TX_POINTER_RESET);
+	nios_write_reg(tp->base_addr, NIOS_STATUS, NIOS_ERR_WARN | NIOS_ERR_BUSOFF);
+
+	/* status reset */
+	nios_write_reg(tp->base_addr, NIOS_STATUS,
+		         NIOS_RX_FULL
+		       | NIOS_RX_OVFL
+		       | NIOS_RX_NEMPTY
+		       | NIOS_TX_FULL
+		       | NIOS_TX_OVFL
+		       | NIOS_TX_EMPTY);
+
+	/* normal mode */
+	tp->flags &= ~NIOS_PRIV_FLAGS_ERR_BUSOFF;
+	nios_write_reg(tp->base_addr, NIOS_TIMING, NIOS_TIME_NORMAL_MODE);
+
+	return;
+}
+
+
+static struct net_device_stats *nioscan_get_stats (struct net_device *ndev)
+{
+	struct nioscan_private *tp = netdev_priv(ndev);
+	u32 reg_error_count;
+
+	reg_error_count = nios_read_reg(tp->base_addr, NIOS_ERROR_COUNT);
+
+	tp->stats.rx_errors = (reg_error_count & NIOS_RX_ERR_CNT_MASK) >> NIOS_RX_ERR_CNT_OFFS;
+	tp->stats.tx_errors = (reg_error_count & NIOS_TX_ERR_CNT_MASK) >> NIOS_TX_ERR_CNT_OFFS;
+
+	return &tp->stats;
+}
+
+
+static void nioscan_read_msg(struct net_device *ndev)
+{
+	struct nioscan_private *tp = netdev_priv(ndev);
+	struct sk_buff *skb;
+	struct can_frame *frame;
+	u32 reg_dlc, reg_id;
+
+	while (nios_read_reg(tp->base_addr, NIOS_STATUS) & NIOS_RX_PTR_MASK) {
+		skb = alloc_skb(sizeof(struct can_frame), GFP_ATOMIC);
+		if (unlikely(skb == NULL)) {
+			/* drop packet with this write */
+			nios_write_reg(tp->base_addr, NIOS_STATUS, NIOS_RX_NEXT_VALUE);
+			tp->stats.rx_dropped++;
+			if (net_ratelimit())
+				dev_warn(ND2D(ndev), "Memory squeeze, dropping packet.\n");
+			continue;
+		}
+		skb->dev = ndev;
+
+		frame = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
+
+		reg_id = nios_read_reg(tp->base_addr, NIOS_ID);
+
+		/* process canid */
+		frame->can_id =	nios_id_reg_to_can(reg_id);
+
+		/* process data length code & rtr */
+		reg_dlc = nios_read_reg(tp->base_addr, NIOS_LENGTH);
+		frame->can_dlc = reg_dlc & 0xf;
+		if (reg_dlc & NIOS_RTR)
+			frame->can_id |= CAN_FLAG_RTR;
+
+		/* process payload */
+		frame->payload.data_u32[0] = nios_read_reg(tp->base_addr, NIOS_DATA_0_3);
+		frame->payload.data_u32[1] = nios_read_reg(tp->base_addr, NIOS_DATA_4_7);
+
+		wmb();
+		nios_write_reg(tp->base_addr, NIOS_STATUS, NIOS_RX_NEXT_VALUE);
+
+		skb->protocol = __constant_htons(ETH_P_CAN);
+
+		netif_rx(skb);
+
+		/* do stats */
+		ndev->last_rx = jiffies;
+		tp->stats.rx_packets++;
+		tp->stats.rx_bytes += frame->can_dlc;
+	}
+
+	return;
+}
+
+static int nioscan_hard_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+	struct nioscan_private *tp = netdev_priv(ndev);
+	struct can_frame *frame = (struct can_frame *)skb->data;
+	unsigned long flags;
+	u32 reg_dlc, reg_id;
+
+	reg_dlc = frame->can_dlc;
+	if (frame->can_id & CAN_FLAG_RTR)
+		reg_dlc |= NIOS_RTR;
+
+	reg_id = nios_id_can_to_reg(frame->can_id);
+	nios_write_reg(tp->base_addr, NIOS_DATA_0_3, frame->payload.data_u32[0]);
+	nios_write_reg(tp->base_addr, NIOS_DATA_4_7, frame->payload.data_u32[1]);
+	nios_write_reg(tp->base_addr, NIOS_ID, reg_id);
+	wmb();
+	nios_write_reg(tp->base_addr, NIOS_LENGTH, reg_dlc);		/* must be last, starts xmit in HW */
+
+	ndev->trans_start = jiffies;
+
+	kfree_skb(skb);
+
+	/* do stats */
+	ndev->last_rx = jiffies;
+	tp->stats.tx_packets++;
+	tp->stats.tx_bytes += frame->can_dlc;
+
+	if (unlikely(nios_read_reg(tp->base_addr, NIOS_STATUS) & NIOS_TX_BUFFER_FULL)) {
+		spin_lock_irqsave(&tp->lock, flags);
+		if (likely(nios_read_reg(tp->base_addr, NIOS_STATUS) & NIOS_TX_BUFFER_FULL)) {
+			nios_enable_txempty(tp);
+			netif_stop_queue(ndev);
+			spin_unlock_irqrestore(&tp->lock, flags);
+		} else
+			spin_unlock_irqrestore(&tp->lock, flags);
+	}
+
+	return 0;
+}
+
+
+static void nioscan_tx_timeout(struct net_device *ndev)
+{
+	struct nioscan_private *tp = netdev_priv(ndev);
+
+	dev_err(ND2D(ndev), "%s status: 0x%08x, flags 0x%08x\n",
+		__FUNCTION__,
+		nios_read_reg(tp->base_addr, NIOS_STATUS),
+		tp->flags);
+}
+
+
+static const u32 baudrate2timing[NIOS_CC_MAX][CAN_BAUD_MAX] = {
+#if 0
+ 	[NIOS_CC_33][CAN_BAUD_10K]  =     << NIOS_TIME_PRESCALE_OFFS |    << NIOS_TIME_TIMEA_OFFS |    << NIOS_TIME_TIMEB_OFFS,
+ 	[NIOS_CC_33][CAN_BAUD_20K]  =     << NIOS_TIME_PRESCALE_OFFS |    << NIOS_TIME_TIMEA_OFFS |    << NIOS_TIME_TIMEB_OFFS,
+ 	[NIOS_CC_33][CAN_BAUD_50K]  =     << NIOS_TIME_PRESCALE_OFFS |    << NIOS_TIME_TIMEA_OFFS |    << NIOS_TIME_TIMEB_OFFS,
+ 	[NIOS_CC_33][CAN_BAUD_100K] =     << NIOS_TIME_PRESCALE_OFFS |    << NIOS_TIME_TIMEA_OFFS |    << NIOS_TIME_TIMEB_OFFS,
+ 	[NIOS_CC_33][CAN_BAUD_125K] =     << NIOS_TIME_PRESCALE_OFFS |    << NIOS_TIME_TIMEA_OFFS |    << NIOS_TIME_TIMEB_OFFS,
+ 	[NIOS_CC_33][CAN_BAUD_250K] =     << NIOS_TIME_PRESCALE_OFFS |    << NIOS_TIME_TIMEA_OFFS |    << NIOS_TIME_TIMEB_OFFS,
+ 	[NIOS_CC_33][CAN_BAUD_500K] =     << NIOS_TIME_PRESCALE_OFFS |    << NIOS_TIME_TIMEA_OFFS |    << NIOS_TIME_TIMEB_OFFS,
+ 	[NIOS_CC_33][CAN_BAUD_800K] =     << NIOS_TIME_PRESCALE_OFFS |    << NIOS_TIME_TIMEA_OFFS |    << NIOS_TIME_TIMEB_OFFS,
+ 	[NIOS_CC_33][CAN_BAUD_1M]   =     << NIOS_TIME_PRESCALE_OFFS |    << NIOS_TIME_TIMEA_OFFS |    << NIOS_TIME_TIMEB_OFFS,
+#endif
+ 	[NIOS_CC_48][CAN_BAUD_10K]  = 198 << NIOS_TIME_PRESCALE_OFFS | 15 << NIOS_TIME_TIMEA_OFFS |  5 << NIOS_TIME_TIMEB_OFFS,
+ 	[NIOS_CC_48][CAN_BAUD_20K]  =  98 << NIOS_TIME_PRESCALE_OFFS | 15 << NIOS_TIME_TIMEA_OFFS |  5 << NIOS_TIME_TIMEB_OFFS,
+ 	[NIOS_CC_48][CAN_BAUD_50K]  =  38 << NIOS_TIME_PRESCALE_OFFS | 15 << NIOS_TIME_TIMEA_OFFS |  5 << NIOS_TIME_TIMEB_OFFS,
+ 	[NIOS_CC_48][CAN_BAUD_100K] =  18 << NIOS_TIME_PRESCALE_OFFS | 15 << NIOS_TIME_TIMEA_OFFS |  5 << NIOS_TIME_TIMEB_OFFS,
+	[NIOS_CC_48][CAN_BAUD_125K] =  14 << NIOS_TIME_PRESCALE_OFFS | 15 << NIOS_TIME_TIMEA_OFFS |  5 << NIOS_TIME_TIMEB_OFFS,
+	[NIOS_CC_48][CAN_BAUD_250K] =   6 << NIOS_TIME_PRESCALE_OFFS | 15 << NIOS_TIME_TIMEA_OFFS |  5 << NIOS_TIME_TIMEB_OFFS,
+	[NIOS_CC_48][CAN_BAUD_500K] =   2 << NIOS_TIME_PRESCALE_OFFS | 15 << NIOS_TIME_TIMEA_OFFS |  5 << NIOS_TIME_TIMEB_OFFS,
+#if 0
+ 	[NIOS_CC_48][CAN_BAUD_800K] =     << NIOS_TIME_PRESCALE_OFFS |    << NIOS_TIME_TIMEA_OFFS |    << NIOS_TIME_TIMEB_OFFS,
+#endif
+ 	[NIOS_CC_48][CAN_BAUD_1M]   =   0 << NIOS_TIME_PRESCALE_OFFS | 15 << NIOS_TIME_TIMEA_OFFS |  5 << NIOS_TIME_TIMEB_OFFS,
+
+ 	[NIOS_CC_60][CAN_BAUD_10K]  = 248 << NIOS_TIME_PRESCALE_OFFS | 15 << NIOS_TIME_TIMEA_OFFS |  5 << NIOS_TIME_TIMEB_OFFS,
+ 	[NIOS_CC_60][CAN_BAUD_20K]  = 123 << NIOS_TIME_PRESCALE_OFFS | 15 << NIOS_TIME_TIMEA_OFFS |  5 << NIOS_TIME_TIMEB_OFFS,
+ 	[NIOS_CC_60][CAN_BAUD_50K]  =  48 << NIOS_TIME_PRESCALE_OFFS | 15 << NIOS_TIME_TIMEA_OFFS |  5 << NIOS_TIME_TIMEB_OFFS,
+ 	[NIOS_CC_60][CAN_BAUD_100K] =  23 << NIOS_TIME_PRESCALE_OFFS | 15 << NIOS_TIME_TIMEA_OFFS |  5 << NIOS_TIME_TIMEB_OFFS,
+ 	[NIOS_CC_60][CAN_BAUD_125K] =  18 << NIOS_TIME_PRESCALE_OFFS | 15 << NIOS_TIME_TIMEA_OFFS |  5 << NIOS_TIME_TIMEB_OFFS,
+ 	[NIOS_CC_60][CAN_BAUD_250K] =   8 << NIOS_TIME_PRESCALE_OFFS | 15 << NIOS_TIME_TIMEA_OFFS |  5 << NIOS_TIME_TIMEB_OFFS,
+ 	[NIOS_CC_60][CAN_BAUD_500K] =   3 << NIOS_TIME_PRESCALE_OFFS | 15 << NIOS_TIME_TIMEA_OFFS |  5 << NIOS_TIME_TIMEB_OFFS,
+#if 0
+ 	[NIOS_CC_60][CAN_BAUD_800K] =     << NIOS_TIME_PRESCALE_OFFS |    << NIOS_TIME_TIMEA_OFFS |    << NIOS_TIME_TIMEB_OFFS,
+ 	[NIOS_CC_60][CAN_BAUD_1M]   =     << NIOS_TIME_PRESCALE_OFFS |    << NIOS_TIME_TIMEA_OFFS |    << NIOS_TIME_TIMEB_OFFS,
+#endif
+};
+
+
+static const int freq2enum[] = {
+	[NIOS_CC_33] = 33300000,
+	[NIOS_CC_48] = 48700000,
+	[NIOS_CC_60] = 60000000,
+};
+
+
+static int do_set_baudrate(struct net_device *ndev, struct can_baudrate *br)
+{
+	struct nioscan_private *tp = netdev_priv(ndev);
+	u32 reg_timing;
+	int err;
+
+	dev_dbg(ND2D(ndev), "%s baudrate: %d\n", __FUNCTION__, br->baudrate);
+
+	err = -EINVAL;
+	if (br->baudrate == CAN_BAUD_BTR_NIOS) {
+		struct can_baudrate_nios *br_nios = &br->btr.nios;
+
+		reg_timing = ((br_nios->prescale << NIOS_TIME_PRESCALE_OFFS) & NIOS_TIME_PRESCALE_MASK) |
+		             ((br_nios->timea << NIOS_TIME_TIMEA_OFFS) & NIOS_TIME_TIMEA_MASK) |
+		             ((br_nios->timeb << NIOS_TIME_TIMEB_OFFS) & NIOS_TIME_TIMEB_MASK);
+
+	} else if (br->baudrate >= 0 && br->baudrate < CAN_BAUD_MAX)
+		reg_timing = baudrate2timing[tp->chip_clock][br->baudrate];
+	else
+		goto exit;
+
+	err = 0;
+
+	reg_timing |= NIOS_TIME_SET_PRESCALE | NIOS_TIME_SET_TIMEA | NIOS_TIME_SET_TIMEB;
+	nios_write_reg(tp->base_addr, NIOS_TIMING, reg_timing);
+	tp->baudrate = br->baudrate;
+
+ exit:
+	return err;
+}
+
+
+static int do_get_baudrate(struct net_device *ndev, struct can_baudrate *br)
+{
+	struct nioscan_private *tp = netdev_priv(ndev);
+	u32 reg_timing;
+
+	br->baudrate = tp->baudrate;
+	if(br->baudrate == CAN_BAUD_BTR_NIOS) {
+		struct can_baudrate_nios *br_nios = &br->btr.nios;
+
+		reg_timing = nios_read_reg(tp->base_addr, NIOS_TIMING);
+
+		br_nios->prescale = (reg_timing & NIOS_TIME_PRESCALE_MASK) >> NIOS_TIME_PRESCALE_OFFS;
+		br_nios->timea    = (reg_timing & NIOS_TIME_TIMEA_MASK)    >> NIOS_TIME_TIMEA_OFFS;
+		br_nios->timeb    = (reg_timing & NIOS_TIME_TIMEB_MASK)    >> NIOS_TIME_TIMEB_OFFS;
+	}
+
+	return 0;
+}
+
+
+static int do_set_mode(struct net_device *ndev, enum CAN_MODE mode)
+{
+	struct nioscan_private *tp = netdev_priv(ndev);
+	unsigned long flags;
+
+	switch (mode) {
+	case CAN_MODE_START:
+		spin_lock_irqsave(&tp->lock, flags);
+
+		tp->flags &= ~NIOS_PRIV_FLAGS_ERR_BUSOFF;
+		nios_write_reg(tp->base_addr, NIOS_STATUS, NIOS_ERR_BUSOFF);		/* ack busoff int */
+		nios_write_reg(tp->base_addr, NIOS_TIMING, NIOS_TIME_NORMAL_MODE);	/* request normal mode */
+
+		if (!(tp->flags & NIOS_PRIV_FLAGS_TX_FULL))
+			netif_wake_queue(ndev);
+
+		spin_unlock_irqrestore(&tp->lock, flags);
+
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+
+static int do_get_state(struct net_device *ndev, enum CAN_STATE *state)
+{
+	struct nioscan_private *tp = netdev_priv(ndev);
+	u32 reg_status;
+
+	*state = -1;
+
+	reg_status = nios_read_reg(tp->base_addr, NIOS_STATUS);
+	if      (reg_status & NIOS_ERR_PASSIVE)
+		*state = CAN_STATE_BUS_PASSIVE;
+	else if (reg_status & NIOS_ERR_ACTIVE)
+		*state = CAN_STATE_ACTIVE;
+	else if (tp->flags & NIOS_PRIV_FLAGS_ERR_BUSOFF)
+		*state = CAN_STATE_BUS_OFF;
+	else if ((reg_status & (NIOS_ERR_PASSIVE | NIOS_ERR_ACTIVE)) == 0) {
+		*state = CAN_STATE_BUS_OFF;
+		dev_err(ND2D(ndev), "Device is in Busoff, but we didn't get IRQ. (status: 0x%08x)\n", reg_status);
+	}
+	return 0;
+}
+
+
+static int do_set_hwfilt(struct net_device *ndev, struct can_hwfilt *hwfilt)
+{
+	struct nioscan_private *tp = netdev_priv(ndev);
+	struct can_hwfilt_accmask *accmask = &hwfilt->param.accmask;
+	unsigned long flags;
+	int i;
+
+	if (hwfilt->type != CAN_HWFILT_ACCMASK)
+		return -EOPNOTSUPP;
+	if (hwfilt->param.accmask.len > 64)
+		return -E2BIG;
+
+	spin_lock_irqsave(&tp->lock, flags);
+	if (hwfilt->param.accmask.len == 0) {
+		nios_write_filter(tp->base_addr, 0,
+				  0x0 | NIOS_FILTER_MASK_VALID,
+				  0x0 | NIOS_FILTER_ID_VALID);
+		i = 1;	/* i is next filter to clear */
+	} else
+		for (i = 0; i < accmask->len; i++)
+			nios_write_filter(tp->base_addr, i,
+					  nios_id_can_to_reg(accmask->filter[i].can_mask) | NIOS_FILTER_MASK_VALID,
+					  nios_id_can_to_reg(accmask->filter[i].can_id)   | NIOS_FILTER_ID_VALID);
+
+	/* clean remaining filters */
+	for (/* nix */; i < NIOS_FILTER_NR; i++)
+		nios_write_filter(tp->base_addr, i, 0x0, 0x0);
+
+	spin_unlock_irqrestore(&tp->lock, flags);
+
+	return 0;
+}
+
+
+static int nioscan_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd)
+{
+	struct can_baudrate *baudrate = (struct can_baudrate *)&ifr->ifr_ifru;
+	union can_settings *settings = (union can_settings *)&ifr->ifr_ifru;
+	struct can_hwfilt *hwfilt = (struct can_hwfilt *)&ifr->ifr_ifru;
+
+	dev_dbg(ND2D(ndev), "(%s) 0x%08x %p\n", __FUNCTION__, cmd);
+
+	switch (cmd) {
+	case SIOCSCANBAUDRATE:
+		return do_set_baudrate(ndev, baudrate);
+	case SIOCGCANBAUDRATE:
+		return do_get_baudrate(ndev, baudrate);
+	case SIOCSCANMODE:
+		return do_set_mode(ndev, settings->mode);
+	case SIOCGCANSTATE:
+		return do_get_state(ndev, &settings->state);
+	case SIOCSCANHWFILT: {
+		struct can_hwfilt my_hwfilt = {
+			.type	= CAN_HWFILT_ACCMASK,
+		};
+		int err;
+		size_t len;
+
+		if (hwfilt->type != CAN_HWFILT_ACCMASK)
+			return -EOPNOTSUPP;
+		if (hwfilt->param.accmask.len > 64)
+			return -E2BIG;
+		if (hwfilt->param.accmask.len == 0 &&
+		    hwfilt->param.accmask.filter != NULL)
+			return -EINVAL;
+
+		len = hwfilt->param.accmask.len * sizeof(struct can_filter);
+		if (len > 0) {
+			my_hwfilt.param.accmask.filter = kmalloc(len, GFP_KERNEL);
+			if (!my_hwfilt.param.accmask.filter)
+				return -ENOMEM;
+
+			err = copy_from_user(my_hwfilt.param.accmask.filter,
+					     hwfilt->param.accmask.filter,
+					     len);
+			if (err) {
+				err = -EACCES;
+				goto out_free;
+			}
+		}
+
+		err = do_set_hwfilt(ndev, &my_hwfilt);
+
+	out_free:
+		if (my_hwfilt.param.accmask.filter)
+			kfree(my_hwfilt.param.accmask.filter);
+
+		return err;
+	}
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0; /* notreached */
+}
+
+
+static void do_weird_interrupt(struct net_device *ndev, u32 reg_status)
+{
+	struct nioscan_private *tp = netdev_priv(ndev);
+	int verbose = net_ratelimit();
+
+	if (verbose)
+		dev_err(ND2D(ndev), "Abnormal interrupt, status: 0x%08x, flags 0x%08x\n", reg_status, tp->flags);
+
+	if (reg_status & NIOS_ERR_WARN) {
+		nios_write_reg(tp->base_addr, NIOS_STATUS, NIOS_ERR_WARN);
+		dev_err(ND2D(ndev), "Device triggered a warning INT\n");
+	}
+
+	if (reg_status & NIOS_ERR_BUSOFF) {
+		tp->flags |= NIOS_PRIV_FLAGS_ERR_BUSOFF;
+		netif_stop_queue(ndev);
+		nios_write_reg(tp->base_addr, NIOS_STATUS, NIOS_ERR_BUSOFF);
+		dev_err(ND2D(ndev), "Device is in Bus Off Mode\n");
+	}
+
+
+	/* RX-Queue full? Ooops */
+	if (unlikely (reg_status & NIOS_RX_FULL)) {
+		nios_write_reg(tp->base_addr, NIOS_STATUS, NIOS_RX_FULL);
+		if (verbose)
+			dev_warn(ND2D(ndev), "RX-Buffer Full\n");
+	}
+
+	if (reg_status & NIOS_RX_OVFL) {
+		nios_write_reg(tp->base_addr, NIOS_STATUS, NIOS_RX_OVFL);
+		tp->stats.rx_fifo_errors++;
+		if (verbose)
+			dev_err(ND2D(ndev), "RX-Buffer Overflow\n");
+	}
+
+
+	/* This should not happen, the netif_queue should have been stopped before */
+	if (reg_status & NIOS_TX_OVFL) {
+		netif_stop_queue(ndev);
+		nios_enable_txempty(tp);
+		nios_write_reg(tp->base_addr, NIOS_STATUS, NIOS_TX_OVFL);
+
+		tp->stats.tx_fifo_errors++;
+		if (verbose)
+			dev_err(ND2D(ndev), "TX-Buffer Overflow\n");
+	}
+
+	return;
+}
+
+
+static irqreturn_t nioscan_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct net_device *ndev = (struct net_device *) dev_id;
+	struct nioscan_private *tp = netdev_priv(ndev);
+	u32 reg_status;
+	u32 mask = nios_all_mask;
+	irqreturn_t handled = IRQ_NONE;
+	unsigned long flags;
+	int boguscnt = max_interrupt_work;
+
+	spin_lock_irqsave(&tp->lock, flags);
+	do {
+		if (unlikely(tp->flags & NIOS_PRIV_FLAGS_TX_FULL))
+			mask |= NIOS_TX_EMPTY;
+		else
+			mask &= ~NIOS_TX_EMPTY;
+
+		reg_status = nios_read_reg(tp->base_addr, NIOS_STATUS);
+
+		/* ready with work or shared IRQ-line? */
+		if (!(reg_status & mask))
+			goto exit;
+
+		handled = IRQ_HANDLED;
+
+		/* packet in the RX fifo? */
+		if (reg_status & NIOS_RX_BUSY)
+			nioscan_read_msg(ndev);
+
+		/* RX-Queue not empty INT? */
+		if (reg_status & NIOS_RX_NEMPTY)
+			nios_write_reg(tp->base_addr, NIOS_STATUS, NIOS_RX_NEMPTY);
+
+
+		/* space left in TX fifo? */
+		if ((tp->flags & NIOS_PRIV_FLAGS_TX_FULL) &&
+		    !(reg_status & NIOS_TX_BUFFER_FULL)) {
+			nios_disable_txempty(tp);
+			netif_wake_queue(ndev);
+		}
+
+		/* TX-Queue empty INT? */
+		if (reg_status & NIOS_TX_EMPTY)
+			nios_write_reg(tp->base_addr, NIOS_STATUS, NIOS_TX_EMPTY);
+
+		if (unlikely(reg_status & (nios_int_mask_abnormal | nios_err_mask)))
+			do_weird_interrupt(ndev, reg_status);
+	} while (--boguscnt > 0);
+
+ 	if (unlikely(boguscnt <= 0)) {
+		dev_warn(ND2D(ndev), "Too much work at interrupt, "
+			 "status (at enter): 0x%08x, now: 0x%08x\n",
+			 reg_status,
+			 nios_read_reg(tp->base_addr, NIOS_STATUS));
+
+		/* Clear all interrupt sources. */
+		nios_write_reg(tp->base_addr, NIOS_STATUS,
+			       nios_int_mask | nios_err_mask);
+	}
+
+ exit:
+	spin_unlock_irqrestore(&tp->lock, flags);
+
+	return handled;
+}
+
+
+static int nioscan_open(struct net_device *ndev)
+{
+	struct nioscan_private *tp = netdev_priv(ndev);
+	int ret;
+
+	if (tp->baudrate == CAN_BAUD_UNCONFIGURED) {
+		dev_err(ND2D(ndev), "Baudrate is unconfigured\n");
+		return -EINVAL;
+	}
+
+	/* Grab the IRQ */
+      	ret = request_irq(ndev->irq, &nioscan_interrupt, 0, ndev->name, ndev);
+      	if (ret) {
+		dev_err(ND2D(ndev), "Request of interrupt %d failed, err: %d\n", ndev->irq, ret);
+		ret = -ENODEV;
+      		goto err_out;
+	}
+
+	set_irq_type(ndev->irq, IRQT_FALLING);
+
+	/* reset chip, setup chip, unmask IRQs */
+	nioscan_chip_reset(ndev);
+
+	netif_wake_queue(ndev);
+
+	return 0;
+err_out:
+	return ret;
+}
+
+
+static int nioscan_stop(struct net_device *ndev)
+{
+	struct nioscan_private *tp = netdev_priv(ndev);
+	unsigned long flags;
+
+	netif_stop_queue(ndev);
+
+	/* mask all IRQs and errors */
+	spin_lock_irqsave(&tp->lock, flags);
+	nios_write_reg(tp->base_addr, NIOS_INTMASK, 0x0 | NIOS_INTMASK_SET);
+	nios_write_reg(tp->base_addr, NIOS_INTMASK, 0x0 | NIOS_ERRMASK_SET);
+	spin_unlock_irqrestore(&tp->lock, flags);
+
+	free_irq(ndev->irq, ndev);
+
+	return 0;
+}
+
+
+static ssize_t nios_show_version(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct net_device *ndev = dev_get_drvdata(dev);
+	struct nioscan_private *tp = netdev_priv(ndev);
+	unsigned int version = nios_read_reg(tp->base_addr, NIOS_VERSION);
+
+	return sprintf (buf, "%d/20%02d (quartus %d, rev %d)\n",
+			NIOS_VERSION_MONTH(version),
+			NIOS_VERSION_YEAR(version),
+			NIOS_VERSION_QUARTUS(version),
+			NIOS_VERSION_REV(version));
+}
+
+static DEVICE_ATTR(version, S_IWUSR|S_IRUGO, nios_show_version, NULL);
+
+
+static int nioscan_get_clock(struct net_device *ndev)
+{
+	struct device *dev = ND2D(ndev);
+	struct nioscan_private *tp = netdev_priv(ndev);
+	struct fpbus_unit_info *info;
+	int i, clock;
+
+	if (chip_clock == -1) {	/* use fpbus */
+		if (!dev->platform_data) {
+			dev_err(dev, "no platform data found. please use module parameter so set chip_clock.\n");
+			return -ENODEV;
+		}
+		info = (struct fpbus_unit_info *)(dev->platform_data);
+		clock = info->fpga_clock;
+	} else 			/* use module param */
+		clock = chip_clock;
+
+	for (i = 0; i < NIOS_CC_MAX; i++) {
+		if (freq2enum[i] == clock) {
+			tp->chip_clock = (enum nios_chip_clock)i;
+			return 0;
+		}
+	}
+
+	dev_err(dev, "unknown chip clock %d, please send patches :)\n", clock);
+
+	return -ENODEV;
+}
+
+
+static int nioscan_probe_check_version(struct net_device *ndev, u32 version)
+{
+	const struct nioscan_private *tp = netdev_priv(ndev);
+
+	if (version == NIOS_VERSION_WORKING)
+		return 0;
+
+	if (version == 0x0604291e ||
+	    version == 0x0705322d ||
+	    version == 0x0705322e ||
+	    version == 0x0705322f ||
+	    version == 0x07053230) {
+		dev_err(ND2D(ndev),
+			"WARNING - buggy version 0x%08x found, please update your IP-Core\n",
+			version);
+
+	} else if (((NIOS_VERSION_YEAR(version) == NIOS_VERSION_YEAR(NIOS_VERSION_WORKING)) &&
+		    (NIOS_VERSION_MONTH(version) > NIOS_VERSION_MONTH(NIOS_VERSION_WORKING))) ||
+		   (NIOS_VERSION_YEAR(version) > NIOS_VERSION_YEAR(NIOS_VERSION_WORKING))) {
+
+		dev_warn(ND2D(ndev),
+			 "found nioscan core from %d/20%02d (quartus %d, rev %d). "
+			 "this core has not been tested, but it's newer than the latest tested version...\n",
+			 NIOS_VERSION_MONTH(version),
+			 NIOS_VERSION_YEAR(version),
+			 NIOS_VERSION_QUARTUS(version),
+			 NIOS_VERSION_REV(version));
+	} else
+		dev_err(ND2D(ndev), "no or old nios CAN chip found on address 0x%p, found 0x%08x instead\n",
+			tp->base_addr, version);
+
+	return -ENODEV;
+}
+
+static int nioscan_probe(struct net_device *ndev)
+{
+	struct nioscan_private *tp = netdev_priv(ndev);
+	u32 version = nios_read_reg(tp->base_addr, NIOS_VERSION);
+	int ret;
+
+	ret = nioscan_probe_check_version(ndev, version);
+	if (ret)
+		return ret;
+
+	dev_info(ND2D(ndev), "found nioscan core from %d/20%02d (quartus %d, rev %d)\n",
+		 NIOS_VERSION_MONTH(version),
+		 NIOS_VERSION_YEAR(version),
+		 NIOS_VERSION_QUARTUS(version),
+		 NIOS_VERSION_REV(version));
+
+	ret = nioscan_get_clock(ndev);
+	if (ret)
+		return ret;
+	tp->baudrate = CAN_BAUD_UNCONFIGURED;
+
+	ndev->open            = nioscan_open;
+	ndev->stop            = nioscan_stop;
+	ndev->hard_start_xmit = nioscan_hard_start_xmit;
+	ndev->do_ioctl        = nioscan_ioctl;
+	ndev->get_stats	      = nioscan_get_stats;
+	ndev->tx_timeout      = nioscan_tx_timeout;
+	ndev->watchdog_timeo  = TX_TIMEOUT;
+
+	spin_lock_init(&tp->lock);
+
+	ret = register_netdev(ndev);
+	if (ret) {
+		dev_err(ND2D(ndev), "register_netdev failed\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int nioscan_drv_probe(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct nioscan_private *tp;
+	struct resource *res;
+	struct net_device *ndev;
+	void __iomem *addr;
+	int irq, err;
+
+	err = -ENODEV;
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	irq = platform_get_irq(pdev, 0);
+	if (!res || !irq) {
+		goto exit;
+	}
+
+	err = -ENOMEM;
+	ndev = alloc_candev(sizeof(struct nioscan_private));
+	if (!ndev) {
+		dev_err(dev, "could not allocate device\n");
+		goto exit;
+	}
+
+	tp = netdev_priv(ndev);
+
+	/*
+	 * Request the regions.
+	 */
+	err = -EBUSY;
+	if (!request_mem_region(res->start, res->end - res->start + 1, CARDNAME)) {
+		goto exit_free_netdev;
+	}
+
+	SET_MODULE_OWNER(ndev);
+	SET_NETDEV_DEV(ndev, dev);
+
+	err = -ENOMEM;
+	addr = ioremap_nocache(res->start, res->end - res->start + 1);
+	if (!addr) {
+		goto exit_release;
+	}
+
+	ndev->irq = irq;
+	tp->base_addr = addr;
+
+	dev_set_drvdata(dev, ndev);
+
+	err = nioscan_probe(ndev);
+	if (err) {
+		dev_err(dev, "Device not found (%d).\n", err);
+		goto exit_unmap;
+	}
+
+	err = device_create_file(dev, &dev_attr_version);
+        if (err)
+                goto exit_unregister;
+
+	err = 0;
+
+ exit:
+	return err;
+
+ exit_unregister:
+	unregister_netdev(ndev);
+ exit_unmap:
+	iounmap(addr);
+ exit_release:
+	release_mem_region(res->start, res->end - res->start + 1);
+ exit_free_netdev:
+	dev_set_drvdata(dev, NULL);
+	free_netdev(ndev);
+	goto exit;
+}
+
+
+static int nioscan_drv_remove(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct net_device *ndev = dev_get_drvdata(dev);
+	struct nioscan_private *tp = netdev_priv(ndev);
+	struct resource *res;
+
+	dev_set_drvdata(dev, NULL);
+
+        device_remove_file(dev, &dev_attr_version);
+
+	unregister_netdev(ndev);
+
+	iounmap(tp->base_addr);
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(res->start, res->end - res->start + 1);
+
+	free_netdev(ndev);
+
+	return 0;
+}
+
+static int nioscan_drv_suspend(struct device *dev, u32 state, u32 level)
+{
+	dev_err(dev, "suspend not implemented\n");
+	return 0;
+}
+
+static int nioscan_drv_resume(struct device *dev, u32 level)
+{
+	dev_err(dev, "resume not implemented\n");
+	return 0;
+}
+
+static struct device_driver nioscan_driver = {
+	.name		= CARDNAME,
+	.bus		= &platform_bus_type,
+	.probe		= nioscan_drv_probe,
+	.remove		= __devexit_p(nioscan_drv_remove),
+	.suspend	= nioscan_drv_suspend,
+	.resume		= nioscan_drv_resume,
+};
+
+static int __init nioscan_init(void)
+{
+	return driver_register(&nioscan_driver);
+}
+
+static void __exit nioscan_cleanup(void)
+{
+	driver_unregister(&nioscan_driver);
+}
+
+module_init(nioscan_init);
+module_exit(nioscan_cleanup);
+
+MODULE_LICENSE("GPL v2");
-- 
2.9.3


^ permalink raw reply related

* Re: Recommended NAPI practices for new drivers
From: Marc Kleine-Budde @ 2016-09-29  6:35 UTC (permalink / raw)
  To: Damien Dusha, linux-can
In-Reply-To: <CABeNA8f3Tyhqt0AQPA-T_KeMSZE3gshfrWKGAx1dhy=Ae0fNdQ@mail.gmail.com>


[-- Attachment #1.1: Type: text/plain, Size: 1175 bytes --]

On 09/29/2016 07:05 AM, Damien Dusha wrote:
> Dear All,
> 
> I am currently writing a driver for the IFI CAN IP Core [1]*.  It uses
> TX and RX FIFOs rather than mailboxes.
> 
> What is the current best practice for using NAPI? The models appear to
> be quite different between drivers:
> 
> Uses NAPI for RX only:
>  - rcan
>  - xilinx
>  - mscan
> -  ti_hecc
> 
> Uses NAPI for RX and ERR:
>  - flexcan
>  - at91
>  - m_can
>  - ifi_canfd
> 
> Uses NAPI for the whole IRQ:
>  - pch_can
>  - c_can
>  - ican3
> 
> Some others do not use NAPI at all, notably the venerable SJA1000 driver.
> 
> For new drivers, what scheme is recommended?

I'd go for RX-only NAPI.

Marc

BTW: I've a very old driver that's probably for this hardware.
Interested? It was written before socketcan was mainlined but never
adopted, as I didn't have access to the hardware anymore.

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

^ permalink raw reply

* Recommended NAPI practices for new drivers
From: Damien Dusha @ 2016-09-29  5:05 UTC (permalink / raw)
  To: linux-can

Dear All,

I am currently writing a driver for the IFI CAN IP Core [1]*.  It uses
TX and RX FIFOs rather than mailboxes.

What is the current best practice for using NAPI? The models appear to
be quite different between drivers:

Uses NAPI for RX only:
 - rcan
 - xilinx
 - mscan
-  ti_hecc

Uses NAPI for RX and ERR:
 - flexcan
 - at91
 - m_can
 - ifi_canfd

Uses NAPI for the whole IRQ:
 - pch_can
 - c_can
 - ican3

Some others do not use NAPI at all, notably the venerable SJA1000 driver.

For new drivers, what scheme is recommended?

Kind regards.
Damien.


[1] http://www.ifi-pld.de/IP/NiosII_CAN/niosii_can.html
* Not to be confused with the recently-mainlined ifi-canfd core from
the same company.

^ permalink raw reply

* Re: libsocketcan proposal for upstream (get typekind vcan or can, etc.)
From: André Hartmann @ 2016-09-23 12:15 UTC (permalink / raw)
  To: aj neu, linux-can
In-Reply-To: <CAPgtGZOuJEkRC+X4yWz8E4Qp1SZivkj6RV9kZ3RPJ20MUBAfUA@mail.gmail.com>

Hi again AJ,

I've now taken a second look at your patch.

As all functions in libsocketcan start with can_*, your new functions 
device_exists and device_is_up are not consistent. As all existing 
functions already have const char *name as first parameter, I think the 
word "device" is superflous. Having all functions starting with can_* 
helps including the library in existing projects.

Suggestions:

int can_is_(existing|available|present)(const char *name);
int can_is_up(const char *name);

Best regards,
André

Am 23.09.2016 um 08:19 schrieb aj neu:
> On Fri, Sep 23, 2016 at 8:16 AM, aj neu wrote:
>> Hello,
>>
>> below, please find a patch which adds the following functions to libsocketcan
>>
>>     device_exists, device_is_up, can_get_typekind
>>
>>     int device_exists(const char *name);
>>      // determine if device is libsocketcan-compatible and exists
>>
>>     int device_is_up(const char *name);
>>      // determine if device is up (also usable for vcan)
>>
>>     int can_get_typekind(const char *name, char *buf, size_t buflen);
>>      // determine if typekind is "can" or "vcan"
>>
>>
>> The patch can be applied to
>> https://git.platon.pengutronix.de/cgit/tools/libsocketcan
>> as follows:
>>    git apply diff.patch
>>
>>
>> Can this be applied to upstream?
>>
>> Thanks.
>> ajneu
>
> Here's the patch as attachment.
>


^ permalink raw reply

* Re: pull-request: can 2016-09-22
From: David Miller @ 2016-09-23 11:15 UTC (permalink / raw)
  To: mkl; +Cc: netdev, linux-can, kernel
In-Reply-To: <20160922124222.31598-1-mkl@pengutronix.de>

From: Marc Kleine-Budde <mkl@pengutronix.de>
Date: Thu, 22 Sep 2016 14:42:21 +0200

> this is a pull request of one patch for the upcoming linux-4.8 release.
> 
> The patch by Sergei Miroshnichenko fixes a potential deadlock in the generic
> CAN device code that cann occour after a bus-off.

Applied, thanks.

^ permalink raw reply

* Re: [PATCH v4] can: sja1000: Optimise register accesses
From: Alexander Gerasiov @ 2016-09-23 11:06 UTC (permalink / raw)
  To: Marc Kleine-Budde; +Cc: Nikita Edward Baruzdin, linux-can
In-Reply-To: <6f96b7d9-e8b6-28cc-b1d3-dbd04bf1a46d@pengutronix.de>

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

Hello Marc,

On Mon, 19 Sep 2016 16:08:04 +0200
Marc Kleine-Budde <mkl@pengutronix.de> wrote:

> I talked to my PCI expert and we came to the conclusion that it's a
> very bad idea to acess the PCI bus with 32 bit on non aligned
> addresses.
> 
> #define SJA1000_ID1		0x11

Agree, I've missed this. I'll update (write|read)_reg_rep() and send
updated patch next week.

-- 
Best regards,
 Alexander Gerasiov

 Contacts:
 e-mail: gq@cs.msu.su  Homepage: http://gerasiov.net  Skype: gerasiov
 PGP fingerprint: 04B5 9D90 DF7C C2AB CD49  BAEA CA87 E9E8 2AAC 33F1

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

^ permalink raw reply

* Re: libsocketcan proposal for upstream (get typekind vcan or can, etc.)
From: André Hartmann @ 2016-09-23 10:19 UTC (permalink / raw)
  To: aj neu, linux-can, mkl
In-Reply-To: <CAPgtGZOuJEkRC+X4yWz8E4Qp1SZivkj6RV9kZ3RPJ20MUBAfUA@mail.gmail.com>

Am 23.09.2016 um 08:19 schrieb aj neu:
> On Fri, Sep 23, 2016 at 8:16 AM, aj neu wrote:
>> Hello,
>>
>> below, please find a patch which adds the following functions to libsocketcan
>>
>>     device_exists, device_is_up, can_get_typekind
>>
>>     int device_exists(const char *name);
>>      // determine if device is libsocketcan-compatible and exists
>>
>>     int device_is_up(const char *name);
>>      // determine if device is up (also usable for vcan)
>>
>>     int can_get_typekind(const char *name, char *buf, size_t buflen);
>>      // determine if typekind is "can" or "vcan"
>>
>>
>> The patch can be applied to
>> https://git.platon.pengutronix.de/cgit/tools/libsocketcan
>> as follows:
>>    git apply diff.patch
>>
>>
>> Can this be applied to upstream?
>>
>> Thanks.
>> ajneu
>
> Here's the patch as attachment.
>

Hello AJ,

without having a deeper look at your patch, I think it is a quite useful 
addition. Please find my comments below.

 >>     int device_exists(const char *name);

This is good, but even better would be something that enumerates 
available interfaces. Looking for available interfaces is a common task, 
I guess. Something like Kurt's library [1] does and what I do in 
QtSerialBus [2] because I was unable to use [1] because of license 
reasons :(

 >>     int device_is_up(const char *name);

OK, if device_exists returns true for devices that are not up.

 >>     int can_get_typekind(const char *name, char *buf, size_t buflen);

Can buf contains anything else beside "can" and "vcan"? If not, I 
instead propose a more concise function:

     int device_is_virtual(const char *name);

Regarding the patch itself:

Marc, what do you say? Will you accept patches to libsocketcan  The last 
commits are from 2014...

And last, one more comment to the code:

+case GET_TYPEKIND:
+  if (!linkinfo[IFLA_INFO_KIND])
+    fprintf(stderr, "no type-kind found\n");
+  else {

I don't know if there are any rules in libsocketcan, but I really prefer 
having curly braces { } around the if also, when there are curly braces 
around the else branch.

+    const size_t buflen = ((struct str_buf *)res)->capacity;
+    if (buflen) {
+      char * const buf = ((struct str_buf *)res)->buf;
+      *buf = '\0';
+      strncat(buf, RTA_DATA(linkinfo[IFLA_INFO_KIND]), buflen-1);

First I asked why not using strncpy, but I guess this is a trick to make 
sure buf is null-terminated? On the other hand, the following code is 
not longer (buflen - 1 is allowed because you checked for zero already):

       strncpy(buf, RTA_DATA(linkinfo[IFLA_INFO_KIND]), buflen);
       buf[buflen - 1] = '\0';

+    }
+    ret = 0;
+}

Best regards,
André

[1] https://github.com/kurt-vd/enumif
[2] 
https://codereview.qt-project.org/#/c/166460/13/src/plugins/canbus/socketcan/socketcanbackend.cpp



^ permalink raw reply

* Re: libsocketcan proposal for upstream (get typekind vcan or can, etc.)
From: aj neu @ 2016-09-23  6:19 UTC (permalink / raw)
  To: linux-can
In-Reply-To: <CAPgtGZNhuinRb-gpj9QkTVnhfY8PE03JKoqhKm8Lw3g2s1+rzw@mail.gmail.com>

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

On Fri, Sep 23, 2016 at 8:16 AM, aj neu wrote:
> Hello,
>
> below, please find a patch which adds the following functions to libsocketcan
>
>     device_exists, device_is_up, can_get_typekind
>
>     int device_exists(const char *name);
>      // determine if device is libsocketcan-compatible and exists
>
>     int device_is_up(const char *name);
>      // determine if device is up (also usable for vcan)
>
>     int can_get_typekind(const char *name, char *buf, size_t buflen);
>      // determine if typekind is "can" or "vcan"
>
>
> The patch can be applied to
> https://git.platon.pengutronix.de/cgit/tools/libsocketcan
> as follows:
>    git apply diff.patch
>
>
> Can this be applied to upstream?
>
> Thanks.
> ajneu

Here's the patch as attachment.

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

diff --git a/include/libsocketcan.h b/include/libsocketcan.h
index dc52053..16fd60a 100644
--- a/include/libsocketcan.h
+++ b/include/libsocketcan.h
@@ -27,11 +27,15 @@
  */
 
 #include <can_netlink.h>
+#include <stddef.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+int device_exists(const char *name);
+int device_is_up(const char *name);
+
 int can_do_restart(const char *name);
 int can_do_stop(const char *name);
 int can_do_start(const char *name);
@@ -46,6 +50,7 @@ int can_get_restart_ms(const char *name, __u32 *restart_ms);
 int can_get_bittiming(const char *name, struct can_bittiming *bt);
 int can_get_ctrlmode(const char *name, struct can_ctrlmode *cm);
 int can_get_state(const char *name, int *state);
+int can_get_typekind(const char *name, char *buf, size_t buflen);
 int can_get_clock(const char *name, struct can_clock *clock);
 int can_get_bittiming_const(const char *name, struct can_bittiming_const *btc);
 int can_get_berr_counter(const char *name, struct can_berr_counter *bc);
diff --git a/src/libsocketcan.c b/src/libsocketcan.c
index c97a28c..7c7f9bb 100644
--- a/src/libsocketcan.c
+++ b/src/libsocketcan.c
@@ -46,6 +46,10 @@
 #define IF_UP 1
 #define IF_DOWN 2
 
+#ifndef IFF_UP
+#define IFF_UP IF_UP
+#endif
+
 #define GET_STATE 1
 #define GET_RESTART_MS 2
 #define GET_BITTIMING 3
@@ -54,6 +58,9 @@
 #define GET_BITTIMING_CONST 6
 #define GET_BERR_COUNTER 7
 #define GET_XSTATS 8
+#define GET_TYPEKIND 9
+#define GET_DEVICEEXISTS 10
+#define GET_DEVICE_UP 11
 
 struct get_req {
 	struct nlmsghdr n;
@@ -74,6 +81,11 @@ struct req_info {
 	struct can_bittiming *bittiming;
 };
 
+struct str_buf {
+	char * buf;
+	const size_t capacity; // storage capacity of buf: number of bytes
+};
+
 static void
 parse_rtattr(struct rtattr **tb, int max, struct rtattr *rta, int len)
 {
@@ -389,7 +401,8 @@ static int do_get_nl_link(int fd, __u8 acquire, const char *name, void *res)
 			else
 				continue;
 
-			if (acquire == GET_XSTATS) {
+			switch (acquire) {
+			case GET_XSTATS:
 				if (!linkinfo[IFLA_INFO_XSTATS])
 					fprintf(stderr, "no can statistics found\n");
 				else {
@@ -398,6 +411,30 @@ static int do_get_nl_link(int fd, __u8 acquire, const char *name, void *res)
 					ret = 0;
 				}
 				continue;
+
+			case GET_TYPEKIND:
+				if (!linkinfo[IFLA_INFO_KIND])
+					fprintf(stderr, "no type-kind found\n");
+				else {
+					const size_t buflen = ((struct str_buf *)res)->capacity;
+					if (buflen) {
+						char * const buf = ((struct str_buf *)res)->buf;
+						*buf = '\0';
+						strncat(buf, RTA_DATA(linkinfo[IFLA_INFO_KIND]), buflen-1);
+					}
+					ret = 0;
+				}
+				continue;
+
+			case GET_DEVICEEXISTS:
+				ret = 0;
+				continue;
+
+			case GET_DEVICE_UP:
+				if ((ifi->ifi_flags & IFF_UP) == IFF_UP) {
+					ret = 0;
+				}
+				continue;
 			}
 
 			if (!linkinfo[IFLA_INFO_DATA]) {
@@ -652,6 +689,38 @@ static int set_link(const char *name, __u8 if_state, struct req_info *req_info)
 
 /**
  * @ingroup extern
+ * device_exists - return non-zero if the device exists
+ *
+ * @param name name of the device. This is the netdev name, as ifconfig -a shows
+ * in your system. usually it contains prefix "can" and the numer of the can
+ * line. e.g. "can0"
+ *
+ * @return 1 if device exists
+ * @return 0 if device does not exist
+ */
+int device_exists(const char *name)
+{
+	return !get_link(name, GET_DEVICEEXISTS, NULL);
+}
+
+/**
+ * @ingroup extern
+ * device_is_up - return non-zero if the device is up
+ *
+ * @param name name of the device. This is the netdev name, as ifconfig -a shows
+ * in your system. usually it contains prefix "can" and the numer of the can
+ * line. e.g. "can0"
+ *
+ * @return 1 if device is up
+ * @return 0 if device is not up
+ */
+int device_is_up(const char *name)
+{
+	return !get_link(name, GET_DEVICE_UP, NULL);
+}
+
+/**
+ * @ingroup extern
  * can_do_start - start the can interface
  * @param name name of the can device. This is the netdev name, as ifconfig -a shows
  * in your system. usually it contains prefix "can" and the numer of the can
@@ -961,6 +1030,25 @@ int can_get_state(const char *name, int *state)
 
 /**
  * @ingroup extern
+ * can_get_typekind - get the type (kind) of the netlink, i.e. can or vcan
+ *
+ * @param name name of the can device. This is the netdev name, as ifconfig -a shows
+ * in your system. usually it contains prefix "can" and the numer of the can
+ * line. e.g. "can0"
+ * @param buf pointer to the first element of a string buffer, to store the kind
+ * @param buflen length of the string buffer (at most buflen-1 chars will be written)
+ *
+ * @return 0 if success
+ * @return -1 if failed
+ */
+int can_get_typekind(const char *name, char *buf, size_t buflen)
+{
+	struct str_buf sbuf = { buf, buflen };
+	return get_link(name, GET_TYPEKIND, &sbuf);
+}
+
+/**
+ * @ingroup extern
  * can_get_restart_ms - get the current interval of auto restarting.
  *
  * @param name name of the can device. This is the netdev name, as ifconfig -a shows

^ permalink raw reply related

* libsocketcan proposal for upstream (get typekind vcan or can, etc.)
From: aj neu @ 2016-09-23  6:16 UTC (permalink / raw)
  To: linux-can

Hello,

below, please find a patch which adds the following functions to libsocketcan

    device_exists, device_is_up, can_get_typekind

    int device_exists(const char *name);
     // determine if device is libsocketcan-compatible and exists

    int device_is_up(const char *name);
     // determine if device is up (also usable for vcan)

    int can_get_typekind(const char *name, char *buf, size_t buflen);
     // determine if typekind is "can" or "vcan"


The patch can be applied to
https://git.platon.pengutronix.de/cgit/tools/libsocketcan
as follows:
   git apply diff.patch


Can this be applied to upstream?

Thanks.
ajneu


diff --git a/include/libsocketcan.h b/include/libsocketcan.h
index dc52053..16fd60a 100644
--- a/include/libsocketcan.h
+++ b/include/libsocketcan.h
@@ -27,11 +27,15 @@
  */

 #include <can_netlink.h>
+#include <stddef.h>

 #ifdef __cplusplus
 extern "C" {
 #endif

+int device_exists(const char *name);
+int device_is_up(const char *name);
+
 int can_do_restart(const char *name);
 int can_do_stop(const char *name);
 int can_do_start(const char *name);
@@ -46,6 +50,7 @@ int can_get_restart_ms(const char *name, __u32 *restart_ms);
 int can_get_bittiming(const char *name, struct can_bittiming *bt);
 int can_get_ctrlmode(const char *name, struct can_ctrlmode *cm);
 int can_get_state(const char *name, int *state);
+int can_get_typekind(const char *name, char *buf, size_t buflen);
 int can_get_clock(const char *name, struct can_clock *clock);
 int can_get_bittiming_const(const char *name, struct can_bittiming_const *btc);
 int can_get_berr_counter(const char *name, struct can_berr_counter *bc);
diff --git a/src/libsocketcan.c b/src/libsocketcan.c
index c97a28c..7c7f9bb 100644
--- a/src/libsocketcan.c
+++ b/src/libsocketcan.c
@@ -46,6 +46,10 @@
 #define IF_UP 1
 #define IF_DOWN 2

+#ifndef IFF_UP
+#define IFF_UP IF_UP
+#endif
+
 #define GET_STATE 1
 #define GET_RESTART_MS 2
 #define GET_BITTIMING 3
@@ -54,6 +58,9 @@
 #define GET_BITTIMING_CONST 6
 #define GET_BERR_COUNTER 7
 #define GET_XSTATS 8
+#define GET_TYPEKIND 9
+#define GET_DEVICEEXISTS 10
+#define GET_DEVICE_UP 11

 struct get_req {
     struct nlmsghdr n;
@@ -74,6 +81,11 @@ struct req_info {
     struct can_bittiming *bittiming;
 };

+struct str_buf {
+    char * buf;
+    const size_t capacity; // storage capacity of buf: number of bytes
+};
+
 static void
 parse_rtattr(struct rtattr **tb, int max, struct rtattr *rta, int len)
 {
@@ -389,7 +401,8 @@ static int do_get_nl_link(int fd, __u8 acquire,
const char *name, void *res)
             else
                 continue;

-            if (acquire == GET_XSTATS) {
+            switch (acquire) {
+            case GET_XSTATS:
                 if (!linkinfo[IFLA_INFO_XSTATS])
                     fprintf(stderr, "no can statistics found\n");
                 else {
@@ -398,6 +411,30 @@ static int do_get_nl_link(int fd, __u8 acquire,
const char *name, void *res)
                     ret = 0;
                 }
                 continue;
+
+            case GET_TYPEKIND:
+                if (!linkinfo[IFLA_INFO_KIND])
+                    fprintf(stderr, "no type-kind found\n");
+                else {
+                    const size_t buflen = ((struct str_buf *)res)->capacity;
+                    if (buflen) {
+                        char * const buf = ((struct str_buf *)res)->buf;
+                        *buf = '\0';
+                        strncat(buf,
RTA_DATA(linkinfo[IFLA_INFO_KIND]), buflen-1);
+                    }
+                    ret = 0;
+                }
+                continue;
+
+            case GET_DEVICEEXISTS:
+                ret = 0;
+                continue;
+
+            case GET_DEVICE_UP:
+                if ((ifi->ifi_flags & IFF_UP) == IFF_UP) {
+                    ret = 0;
+                }
+                continue;
             }

             if (!linkinfo[IFLA_INFO_DATA]) {
@@ -652,6 +689,38 @@ static int set_link(const char *name, __u8
if_state, struct req_info *req_info)

 /**
  * @ingroup extern
+ * device_exists - return non-zero if the device exists
+ *
+ * @param name name of the device. This is the netdev name, as
ifconfig -a shows
+ * in your system. usually it contains prefix "can" and the numer of the can
+ * line. e.g. "can0"
+ *
+ * @return 1 if device exists
+ * @return 0 if device does not exist
+ */
+int device_exists(const char *name)
+{
+    return !get_link(name, GET_DEVICEEXISTS, NULL);
+}
+
+/**
+ * @ingroup extern
+ * device_is_up - return non-zero if the device is up
+ *
+ * @param name name of the device. This is the netdev name, as
ifconfig -a shows
+ * in your system. usually it contains prefix "can" and the numer of the can
+ * line. e.g. "can0"
+ *
+ * @return 1 if device is up
+ * @return 0 if device is not up
+ */
+int device_is_up(const char *name)
+{
+    return !get_link(name, GET_DEVICE_UP, NULL);
+}
+
+/**
+ * @ingroup extern
  * can_do_start - start the can interface
  * @param name name of the can device. This is the netdev name, as
ifconfig -a shows
  * in your system. usually it contains prefix "can" and the numer of the can
@@ -961,6 +1030,25 @@ int can_get_state(const char *name, int *state)

 /**
  * @ingroup extern
+ * can_get_typekind - get the type (kind) of the netlink, i.e. can or vcan
+ *
+ * @param name name of the can device. This is the netdev name, as
ifconfig -a shows
+ * in your system. usually it contains prefix "can" and the numer of the can
+ * line. e.g. "can0"
+ * @param buf pointer to the first element of a string buffer, to
store the kind
+ * @param buflen length of the string buffer (at most buflen-1 chars
will be written)
+ *
+ * @return 0 if success
+ * @return -1 if failed
+ */
+int can_get_typekind(const char *name, char *buf, size_t buflen)
+{
+    struct str_buf sbuf = { buf, buflen };
+    return get_link(name, GET_TYPEKIND, &sbuf);
+}
+
+/**
+ * @ingroup extern
  * can_get_restart_ms - get the current interval of auto restarting.
  *
  * @param name name of the can device. This is the netdev name, as
ifconfig -a shows

^ permalink raw reply related

* [PATCH] can: dev: fix deadlock reported after bus-off
From: Marc Kleine-Budde @ 2016-09-22 12:42 UTC (permalink / raw)
  To: netdev
  Cc: davem, linux-can, kernel, Sergei Miroshnichenko, linux-stable,
	Marc Kleine-Budde
In-Reply-To: <20160922124222.31598-1-mkl@pengutronix.de>

From: Sergei Miroshnichenko <sergeimir@emcraft.com>

A timer was used to restart after the bus-off state, leading to a
relatively large can_restart() executed in an interrupt context,
which in turn sets up pinctrl. When this happens during system boot,
there is a high probability of grabbing the pinctrl_list_mutex,
which is locked already by the probe() of other device, making the
kernel suspect a deadlock condition [1].

To resolve this issue, the restart_timer is replaced by a delayed
work.

[1] https://github.com/victronenergy/venus/issues/24

Signed-off-by: Sergei Miroshnichenko <sergeimir@emcraft.com>
Cc: linux-stable <stable@vger.kernel.org>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
---
 drivers/net/can/dev.c   | 27 +++++++++++++++++----------
 include/linux/can/dev.h |  3 ++-
 2 files changed, 19 insertions(+), 11 deletions(-)

diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c
index e21f7cc5ae4d..8d6208c0b400 100644
--- a/drivers/net/can/dev.c
+++ b/drivers/net/can/dev.c
@@ -21,6 +21,7 @@
 #include <linux/slab.h>
 #include <linux/netdevice.h>
 #include <linux/if_arp.h>
+#include <linux/workqueue.h>
 #include <linux/can.h>
 #include <linux/can/dev.h>
 #include <linux/can/skb.h>
@@ -501,9 +502,8 @@ EXPORT_SYMBOL_GPL(can_free_echo_skb);
 /*
  * CAN device restart for bus-off recovery
  */
-static void can_restart(unsigned long data)
+static void can_restart(struct net_device *dev)
 {
-	struct net_device *dev = (struct net_device *)data;
 	struct can_priv *priv = netdev_priv(dev);
 	struct net_device_stats *stats = &dev->stats;
 	struct sk_buff *skb;
@@ -543,6 +543,14 @@ restart:
 		netdev_err(dev, "Error %d during restart", err);
 }
 
+static void can_restart_work(struct work_struct *work)
+{
+	struct delayed_work *dwork = to_delayed_work(work);
+	struct can_priv *priv = container_of(dwork, struct can_priv, restart_work);
+
+	can_restart(priv->dev);
+}
+
 int can_restart_now(struct net_device *dev)
 {
 	struct can_priv *priv = netdev_priv(dev);
@@ -556,8 +564,8 @@ int can_restart_now(struct net_device *dev)
 	if (priv->state != CAN_STATE_BUS_OFF)
 		return -EBUSY;
 
-	/* Runs as soon as possible in the timer context */
-	mod_timer(&priv->restart_timer, jiffies);
+	cancel_delayed_work_sync(&priv->restart_work);
+	can_restart(dev);
 
 	return 0;
 }
@@ -578,8 +586,8 @@ void can_bus_off(struct net_device *dev)
 	netif_carrier_off(dev);
 
 	if (priv->restart_ms)
-		mod_timer(&priv->restart_timer,
-			  jiffies + (priv->restart_ms * HZ) / 1000);
+		schedule_delayed_work(&priv->restart_work,
+				      msecs_to_jiffies(priv->restart_ms));
 }
 EXPORT_SYMBOL_GPL(can_bus_off);
 
@@ -688,6 +696,7 @@ struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max)
 		return NULL;
 
 	priv = netdev_priv(dev);
+	priv->dev = dev;
 
 	if (echo_skb_max) {
 		priv->echo_skb_max = echo_skb_max;
@@ -697,7 +706,7 @@ struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max)
 
 	priv->state = CAN_STATE_STOPPED;
 
-	init_timer(&priv->restart_timer);
+	INIT_DELAYED_WORK(&priv->restart_work, can_restart_work);
 
 	return dev;
 }
@@ -778,8 +787,6 @@ int open_candev(struct net_device *dev)
 	if (!netif_carrier_ok(dev))
 		netif_carrier_on(dev);
 
-	setup_timer(&priv->restart_timer, can_restart, (unsigned long)dev);
-
 	return 0;
 }
 EXPORT_SYMBOL_GPL(open_candev);
@@ -794,7 +801,7 @@ void close_candev(struct net_device *dev)
 {
 	struct can_priv *priv = netdev_priv(dev);
 
-	del_timer_sync(&priv->restart_timer);
+	cancel_delayed_work_sync(&priv->restart_work);
 	can_flush_echo_skb(dev);
 }
 EXPORT_SYMBOL_GPL(close_candev);
diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h
index 5261751f6bd4..5f5270941ba0 100644
--- a/include/linux/can/dev.h
+++ b/include/linux/can/dev.h
@@ -32,6 +32,7 @@ enum can_mode {
  * CAN common private data
  */
 struct can_priv {
+	struct net_device *dev;
 	struct can_device_stats can_stats;
 
 	struct can_bittiming bittiming, data_bittiming;
@@ -47,7 +48,7 @@ struct can_priv {
 	u32 ctrlmode_static;	/* static enabled options for driver/hardware */
 
 	int restart_ms;
-	struct timer_list restart_timer;
+	struct delayed_work restart_work;
 
 	int (*do_set_bittiming)(struct net_device *dev);
 	int (*do_set_data_bittiming)(struct net_device *dev);
-- 
2.9.3


^ permalink raw reply related


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