merge the bouyer-socketcan branch to HEAD.

CAN stands for Controller Area Network, a broadcast network used
in automation and automotive fields. For example, the NMEA2000 standard
developped for marine devices uses a CAN network as the link layer.

This is an implementation of the linux socketcan API:
https://www.kernel.org/doc/Documentation/networking/can.txt
you can also see can(4).

This adds a new socket family (AF_CAN) and protocol (PF_CAN),
as well as the canconfig(8) utility, used to set timing parameter of
CAN hardware. Also inclued is a driver for the CAN controller
found in the allwinner A20 SoC (I tested it with an Olimex lime2 board,
connected with PIC18-based CAN devices).

There is also the canloop(4) pseudo-device, which allows to use
the socketcan API without CAN hardware.

At this time the CANFD part of the linux socketcan API is not implemented.
Error frames are not implemented either. But I could get the cansend and
canreceive utilities from the canutils package to build and run with minimal
changes. tcpudmp(8) can also be used to record frames, which can be
decoded with etherreal.
This commit is contained in:
bouyer 2017-05-27 21:02:54 +00:00
parent 99b53e88ef
commit 6e4cb2b9ab
51 changed files with 5343 additions and 39 deletions

View File

@ -1,4 +1,4 @@
# $NetBSD: mi,v 1.1157 2017/05/21 15:28:37 riastradh Exp $
# $NetBSD: mi,v 1.1158 2017/05/27 21:02:54 bouyer Exp $
#
# Note: Don't delete entries from here - mark them as "obsolete" instead,
# unless otherwise stated below.
@ -456,6 +456,7 @@
./sbin/blacklistctl base-sysutil-root
./sbin/blacklistd base-sysutil-root
./sbin/brconfig base-netutil-root
./sbin/canconfig base-netutil-root
./sbin/ccdconfig base-sysutil-root
./sbin/cgdconfig base-sysutil-root
./sbin/chown base-sysutil-root
@ -1179,6 +1180,7 @@
./usr/include/net80211 base-c-usr
./usr/include/netatalk base-c-usr
./usr/include/netbt base-c-usr
./usr/include/netcan base-c-usr
./usr/include/netccitt base-obsolete obsolete
./usr/include/netinet base-c-usr
./usr/include/netinet6 base-c-usr

View File

@ -1,4 +1,4 @@
# $NetBSD: shl.mi,v 1.816 2017/05/21 15:28:37 riastradh Exp $
# $NetBSD: shl.mi,v 1.817 2017/05/27 21:02:54 bouyer Exp $
#
# Note: Don't delete entries from here - mark them as "obsolete" instead,
# unless otherwise stated below.
@ -718,6 +718,9 @@
./usr/lib/librumpnet_netbt.so base-rump-shlib rump
./usr/lib/librumpnet_netbt.so.0 base-rump-shlib rump
./usr/lib/librumpnet_netbt.so.0.0 base-rump-shlib rump
./usr/lib/librumpnet_netcan.so base-rump-shlib rump
./usr/lib/librumpnet_netcan.so.0 base-rump-shlib rump
./usr/lib/librumpnet_netcan.so.0.0 base-rump-shlib rump
./usr/lib/librumpnet_netinet.so base-rump-shlib rump
./usr/lib/librumpnet_netinet.so.0 base-rump-shlib rump
./usr/lib/librumpnet_netinet.so.0.0 base-rump-shlib rump

View File

@ -1,4 +1,4 @@
# $NetBSD: mi,v 1.2137 2017/05/21 22:48:25 kamil Exp $
# $NetBSD: mi,v 1.2138 2017/05/27 21:02:54 bouyer Exp $
#
# Note: don't delete entries from here - mark them as "obsolete" instead.
./etc/mtree/set.comp comp-sys-root
@ -2273,6 +2273,8 @@
./usr/include/netbt/l2cap.h comp-c-include
./usr/include/netbt/rfcomm.h comp-c-include
./usr/include/netbt/sco.h comp-c-include
./usr/include/netcan/can.h comp-c-include
./usr/include/netcan/can_link.h comp-c-include
./usr/include/netccitt/dll.h comp-obsolete obsolete
./usr/include/netccitt/hd_var.h comp-obsolete obsolete
./usr/include/netccitt/hdlc.h comp-obsolete obsolete
@ -3565,6 +3567,8 @@
./usr/lib/librumpnet_net_p.a comp-c-proflib rump,profile
./usr/lib/librumpnet_netbt.a comp-c-lib rump
./usr/lib/librumpnet_netbt_p.a comp-c-proflib rump,profile
./usr/lib/librumpnet_netcan.a comp-c-lib rump
./usr/lib/librumpnet_netcan_p.a comp-c-proflib rump,profile
./usr/lib/librumpnet_netinet.a comp-c-lib rump
./usr/lib/librumpnet_netinet6.a comp-c-lib rump
./usr/lib/librumpnet_netinet6_p.a comp-c-proflib rump,profile

View File

@ -1,4 +1,4 @@
# $NetBSD: shl.mi,v 1.305 2017/05/21 15:28:37 riastradh Exp $
# $NetBSD: shl.mi,v 1.306 2017/05/27 21:02:54 bouyer Exp $
#
# Note: don't delete entries from here - mark them as "obsolete" instead.
#
@ -224,6 +224,7 @@
./usr/lib/librumpnet_net80211_pic.a comp-c-piclib picinstall,rump
./usr/lib/librumpnet_net_pic.a comp-c-piclib picinstall,rump
./usr/lib/librumpnet_netbt_pic.a comp-c-piclib picinstall,rump
./usr/lib/librumpnet_netcan_pic.a comp-c-piclib picinstall,rump
./usr/lib/librumpnet_netinet6_pic.a comp-c-piclib picinstall,rump
./usr/lib/librumpnet_netinet_pic.a comp-c-piclib picinstall,rump
./usr/lib/librumpnet_netipsec_pic.a comp-c-piclib picinstall,rump

View File

@ -1,4 +1,4 @@
# $NetBSD: mi,v 1.212 2017/05/26 11:16:18 martin Exp $
# $NetBSD: mi,v 1.213 2017/05/27 21:02:55 bouyer Exp $
./etc/mtree/set.debug comp-sys-root
./usr/lib comp-sys-usr compatdir
./usr/lib/i18n/libBIG5_g.a comp-c-debuglib debuglib,compatfile
@ -216,6 +216,7 @@
./usr/lib/librumpnet_net80211_g.a comp-c-debuglib debuglib,rump
./usr/lib/librumpnet_net_g.a comp-c-debuglib debuglib,rump
./usr/lib/librumpnet_netbt_g.a comp-c-debuglib debuglib,rump
./usr/lib/librumpnet_netcan_g.a comp-c-debuglib debuglib,rump
./usr/lib/librumpnet_netinet6_g.a comp-c-debuglib debuglib,rump
./usr/lib/librumpnet_netinet_g.a comp-c-debuglib debuglib,rump
./usr/lib/librumpnet_netipsec_g.a comp-c-debuglib debuglib,rump
@ -308,6 +309,7 @@
./usr/libdata/debug/sbin/blacklistctl.debug comp-sysutil-debug debug
./usr/libdata/debug/sbin/blacklistd.debug comp-sysutil-debug debug
./usr/libdata/debug/sbin/brconfig.debug comp-netutil-debug debug
./usr/libdata/debug/sbin/canconfig.debug comp-netutil-debug debug
./usr/libdata/debug/sbin/ccdconfig.debug comp-sysutil-debug debug
./usr/libdata/debug/sbin/cgdconfig.debug comp-sysutil-debug debug
./usr/libdata/debug/sbin/chown.debug comp-sysutil-debug debug
@ -2254,6 +2256,8 @@
./usr/libdata/debug/usr/tests/net/bpfjit/t_cop.debug tests-net-debug debug,atf,sljit,rump
./usr/libdata/debug/usr/tests/net/bpfjit/t_extmem.debug tests-net-debug debug,atf,sljit,rump
./usr/libdata/debug/usr/tests/net/bpfjit/t_mbuf.debug tests-net-debug debug,atf,sljit,rump
./usr/libdata/debug/usr/tests/net/can/t_can.debug tests-net-debug debug,atf,rump
./usr/libdata/debug/usr/tests/net/can/t_canfilter.debug tests-net-debug debug,atf,rump
./usr/libdata/debug/usr/tests/net/carp/t_basic.debug tests-net-debug debug,atf,rump,obsolete
./usr/libdata/debug/usr/tests/net/fdpass/fdpass32.debug tests-net-debug debug,atf,compattestfile
./usr/libdata/debug/usr/tests/net/fdpass/fdpass64.debug tests-net-debug debug,atf,compattestfile

View File

@ -1,4 +1,4 @@
# $NetBSD: shl.mi,v 1.175 2017/05/21 15:28:37 riastradh Exp $
# $NetBSD: shl.mi,v 1.176 2017/05/27 21:02:55 bouyer Exp $
./usr/lib/libbfd_g.a comp-c-debuglib debuglib,compatfile,binutils
./usr/libdata/debug/lib base-sys-usr debug,dynamicroot,compatdir
./usr/libdata/debug/lib/libblacklist.so.0.0.debug comp-sys-debug debug,dynamicroot
@ -246,6 +246,7 @@
./usr/libdata/debug/usr/lib/librumpnet_net.so.0.0.debug comp-rump-debug debug,rump
./usr/libdata/debug/usr/lib/librumpnet_net80211.so.0.0.debug comp-rump-debug debug,rump
./usr/libdata/debug/usr/lib/librumpnet_netbt.so.0.0.debug comp-rump-debug debug,rump
./usr/libdata/debug/usr/lib/librumpnet_netcan.so.0.0.debug comp-rump-debug debug,rump
./usr/libdata/debug/usr/lib/librumpnet_netinet.so.0.0.debug comp-rump-debug debug,rump
./usr/libdata/debug/usr/lib/librumpnet_netinet6.so.0.0.debug comp-rump-debug debug,rump
./usr/libdata/debug/usr/lib/librumpnet_netipsec.so.0.0.debug comp-rump-debug debug,rump

View File

@ -1,4 +1,4 @@
# $NetBSD: mi,v 1.1556 2017/05/21 15:28:38 riastradh Exp $
# $NetBSD: mi,v 1.1557 2017/05/27 21:02:55 bouyer Exp $
#
# Note: don't delete entries from here - mark them as "obsolete" instead.
#
@ -954,6 +954,8 @@
./usr/share/man/cat4/bwi.0 man-sys-catman .cat
./usr/share/man/cat4/ca.0 man-obsolete obsolete
./usr/share/man/cat4/cac.0 man-sys-catman .cat
./usr/share/man/cat4/can.0 man-sys-catman .cat
./usr/share/man/cat4/canloop.0 man-sys-catman .cat
./usr/share/man/cat4/cardbus.0 man-sys-catman .cat
./usr/share/man/cat4/cardslot.0 man-sys-catman .cat
./usr/share/man/cat4/carp.0 man-sys-catman .cat
@ -2380,6 +2382,7 @@
./usr/share/man/cat8/bthcid.0 man-sysutil-catman .cat
./usr/share/man/cat8/btpand.0 man-sysutil-catman .cat
./usr/share/man/cat8/btuartd.0 man-obsolete obsolete
./usr/share/man/cat8/canconfig.0 man-netutil-catman .cat
./usr/share/man/cat8/catman.0 man-man-catman .cat
./usr/share/man/cat8/cats/MAKEDEV.0 man-obsolete obsolete
./usr/share/man/cat8/cats/makedev.0 man-obsolete obsolete
@ -4066,6 +4069,8 @@
./usr/share/man/html4/btuart.html man-sys-htmlman html
./usr/share/man/html4/bwi.html man-sys-htmlman html
./usr/share/man/html4/cac.html man-sys-htmlman html
./usr/share/man/html4/can.html man-sys-htmlman html
./usr/share/man/html4/canloop.html man-sys-htmlman html
./usr/share/man/html4/cardbus.html man-sys-htmlman html
./usr/share/man/html4/cardslot.html man-sys-htmlman html
./usr/share/man/html4/carp.html man-sys-htmlman html
@ -5358,6 +5363,7 @@
./usr/share/man/html8/btdevctl.html man-sysutil-htmlman html
./usr/share/man/html8/bthcid.html man-sysutil-htmlman html
./usr/share/man/html8/btpand.html man-sysutil-htmlman html
./usr/share/man/html8/canconfig.html man-netutil-htmlman html
./usr/share/man/html8/catman.html man-man-htmlman html
./usr/share/man/html8/ccdconfig.html man-sysutil-htmlman html
./usr/share/man/html8/cgdconfig.html man-sysutil-htmlman html
@ -6950,6 +6956,8 @@
./usr/share/man/man4/bwi.4 man-sys-man .man
./usr/share/man/man4/ca.4 man-obsolete obsolete
./usr/share/man/man4/cac.4 man-sys-man .man
./usr/share/man/man4/can.4 man-sys-man .man
./usr/share/man/man4/canloop.4 man-sys-man .man
./usr/share/man/man4/cardbus.4 man-sys-man .man
./usr/share/man/man4/cardslot.4 man-sys-man .man
./usr/share/man/man4/carp.4 man-sys-man .man
@ -8375,6 +8383,7 @@
./usr/share/man/man8/bthcid.8 man-sysutil-man .man
./usr/share/man/man8/btpand.8 man-sysutil-man .man
./usr/share/man/man8/btuartd.8 man-obsolete obsolete
./usr/share/man/man8/canconfig.8 man-netutil-man .man
./usr/share/man/man8/catman.8 man-man-man .man
./usr/share/man/man8/cats/MAKEDEV.8 man-obsolete obsolete
./usr/share/man/man8/cats/makedev.8 man-obsolete obsolete

View File

@ -1,4 +1,4 @@
# $NetBSD: mi,v 1.748 2017/05/26 09:02:33 martin Exp $
# $NetBSD: mi,v 1.749 2017/05/27 21:02:55 bouyer Exp $
#
# Note: don't delete entries from here - mark them as "obsolete" instead.
#
@ -155,6 +155,7 @@
./usr/libdata/debug/usr/tests/net/bpf tests-net-debug compattestfile,atf
./usr/libdata/debug/usr/tests/net/bpfilter tests-net-debug compattestfile,atf
./usr/libdata/debug/usr/tests/net/bpfjit tests-net-debug compattestfile,atf
./usr/libdata/debug/usr/tests/net/can tests-net-debug compattestfile,atf
./usr/libdata/debug/usr/tests/net/carp tests-net-debug compattestfile,atf
./usr/libdata/debug/usr/tests/net/fdpass tests-net-debug compattestfile,atf
./usr/libdata/debug/usr/tests/net/icmp tests-net-debug compattestfile,atf
@ -3232,6 +3233,10 @@
./usr/tests/net/bpfjit/t_cop tests-net-tests atf,rump,sljit
./usr/tests/net/bpfjit/t_extmem tests-net-tests atf,rump,sljit
./usr/tests/net/bpfjit/t_mbuf tests-net-tests atf,rump,sljit
./usr/tests/net/can tests-net-tests compattestfile,atf
./usr/tests/net/can/Atffile tests-net-tests atf,rump
./usr/tests/net/can/t_can tests-net-tests atf,rump
./usr/tests/net/can/t_canfilter tests-net-tests atf,rump
./usr/tests/net/carp tests-net-tests compattestfile,atf
./usr/tests/net/carp/Atffile tests-net-tests atf,rump
./usr/tests/net/carp/Kyuafile tests-net-tests atf,rump,kyua

View File

@ -1,4 +1,4 @@
# $NetBSD: NetBSD.dist.base,v 1.159 2017/04/10 16:38:40 christos Exp $
# $NetBSD: NetBSD.dist.base,v 1.160 2017/05/27 21:02:55 bouyer Exp $
# @(#)4.4BSD.dist 8.1 (Berkeley) 6/13/93
# Do not customize this file as it may be overwritten on upgrades.
@ -209,6 +209,7 @@
./usr/include/net/agr
./usr/include/netatalk
./usr/include/netbt
./usr/include/netcan
./usr/include/netinet
./usr/include/net80211
./usr/include/netinet6

View File

@ -1,4 +1,4 @@
# $NetBSD: NetBSD.dist.tests,v 1.146 2017/05/14 00:07:07 kamil Exp $
# $NetBSD: NetBSD.dist.tests,v 1.147 2017/05/27 21:02:55 bouyer Exp $
./usr/libdata/debug/usr/tests
./usr/libdata/debug/usr/tests/atf
@ -136,6 +136,7 @@
./usr/libdata/debug/usr/tests/net/bpf
./usr/libdata/debug/usr/tests/net/bpfilter
./usr/libdata/debug/usr/tests/net/bpfjit
./usr/libdata/debug/usr/tests/net/can
./usr/libdata/debug/usr/tests/net/carp
./usr/libdata/debug/usr/tests/net/fdpass
./usr/libdata/debug/usr/tests/net/icmp
@ -325,6 +326,7 @@
./usr/tests/net/bpf
./usr/tests/net/bpfilter
./usr/tests/net/bpfjit
./usr/tests/net/can
./usr/tests/net/carp
./usr/tests/net/fdpass
./usr/tests/net/icmp

View File

@ -1,4 +1,4 @@
.\" $NetBSD: socket.2,v 1.41 2013/03/01 18:25:16 joerg Exp $
.\" $NetBSD: socket.2,v 1.42 2017/05/27 21:02:55 bouyer Exp $
.\"
.\" Copyright (c) 1983, 1991, 1993
.\" The Regents of the University of California. All rights reserved.
@ -29,7 +29,7 @@
.\"
.\" @(#)socket.2 8.1 (Berkeley) 6/4/93
.\"
.Dd February 5, 2013
.Dd April 27, 2017
.Dt SOCKET 2
.Os
.Sh NAME
@ -61,6 +61,7 @@ PF_INET6 IPv6 (Internet Protocol version 6) protocols
PF_NS Xerox Network Systems protocols
PF_APPLETALK AppleTalk protocols
PF_BLUETOOTH Bluetooth protocols
PF_CAN CAN bus protocols
.Ed
.Pp
The socket has the indicated

View File

@ -1,4 +1,4 @@
# $NetBSD: Makefile,v 1.129 2016/07/01 22:50:50 christos Exp $
# $NetBSD: Makefile,v 1.130 2017/05/27 21:02:55 bouyer Exp $
# @(#)Makefile 8.5 (Berkeley) 3/31/94
# Not ported: XNSrouted enpload scsiformat startslip
@ -6,8 +6,9 @@
.include <bsd.own.mk>
SUBDIR= amrctl apmlabel atactl badsect bioctl brconfig ccdconfig cgdconfig \
chown devpubd disklabel dkctl dkscan_bsdlabel dmesg dmctl \
SUBDIR= amrctl apmlabel atactl badsect bioctl brconfig \
canconfig ccdconfig cgdconfig chown \
devpubd disklabel dkctl dkscan_bsdlabel dmesg dmctl \
drvctl fastboot fdisk fsck fsirand gpt ifconfig init ldconfig luactl \
mbrlabel mknod modload modstat modunload mount \
newbtconf nologin nvmectl \

6
sbin/canconfig/Makefile Normal file
View File

@ -0,0 +1,6 @@
# $NetBSD: Makefile,v 1.2 2017/05/27 21:02:55 bouyer Exp $
PROG= canconfig
MAN= canconfig.8
.include <bsd.prog.mk>

128
sbin/canconfig/canconfig.8 Normal file
View File

@ -0,0 +1,128 @@
.\" $NetBSD: canconfig.8,v 1.2 2017/05/27 21:02:55 bouyer Exp $
.\"
.\" Copyright (c) 2017 Manuel Bouyer.
*
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
*
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.Dd Apris 15, 2017
.Dt CANCONFIG 8
.Os
.Sh NAME
.Nm canconfig
.Nd configure CAN network interface parameters
.Sh SYNOPSIS
.Nm
.Fl a
.Nm
.Ar canif
.Nm
.Ar can
.Ar command
.Op Ar args ...
.Sh DESCRIPTION
The
.Nm
utility is used to configure CAN network interface parameters and retrieve
CAN network interface parameters and status from the kernel.
.Ss Timing
.Pp
The base time unit used to define the network bit rate is the time quanta (tq),
its value is defined by the input clock freqency and the prescaler value
as (1/ freq * prescaler).
.Pp
The network bit time is split in 4 segments:
.Bl -tag -width "phase_seg1" -offset indent -compact
.It sync_seg
fixed to 1 tq
.It prop_seg
.It phase_seg1
.It phase_seg2
.El
The network bit time is (1 + prop_seg + phase_seg1 + phase_seg2), in tq.
The sample point is between phase_seg1 and phase_seg2.
.Pp
The receiver hardware is allowed to shorten prop_seg or phase_seg2 to
resynchronise with the sender. The swj (Synchronisation Jump Width) parameter
defines by how much the hardware can shorten these segments.
.Pp
Some hardware can use 3 sample points instead of one, and keep the majority as
the final value.
.Ss Options
.Pp
The options are as follows:
.Bl -tag -width indent
.It Fl a
Display the status of all CAN devices present on the system.
This flag is mutually exclusive with all other sub-commands.
.El
.Pp
All other operations require that a CAN interface be specified.
If a CAN interface is specified with no sub-commands,
the status of that interface is displayed.
.Pp
The following sub-commands are available:
.Pp
.Bl -tag -width indent
.It Cm up
Start network operations on the interface. Requires that all timing parameters
have been set.
.It Cm down
Stop network operations on the interface.
.It Cm brp Ar value
Set the value of the baud rate prescaler.
.It Cm prop_seg Ar value
set the number of tq for the propagation segment.
.It Cm phase_seg1 Ar value
set the number of tq for the phase segment 1.
.It Cm phase_seg2 Ar value
set the number of tq for the phase segment 2.
.It Cm sjw Ar value
set the number of tq for the Synchronisation Jump Width.
.It Cm 3samples
enables triple-sampling.
.It Cm -3samples
disables triple-sampling.
.It Cm listenonly
enables listenonly mode. In this mode the controller is passive, and
doesn't send ACKs on the bus.
.It Cm -listenonly
disables listenonly mode.
.It Cm loopback
enables loopback mode. In this mode, the controller doens't expect ACK from
the bus.
.It Cm -loopback
disables loopback mode.
.El
.Sh EXAMPLES
TODO
.Sh SEE ALSO
.Xr ifconfig.if 5 ,
.Xr ifconfig 8 ,
.Sh HISTORY
The
.Nm
utility first appeared in
.Nx 8.0 .
.Sh AUTHORS
This version of the
.Nm
utility was written by
.An Manuel Bouyer .

583
sbin/canconfig/canconfig.c Normal file
View File

@ -0,0 +1,583 @@
/* $NetBSD: canconfig.c,v 1.2 2017/05/27 21:02:55 bouyer Exp $ */
/*
* Copyright 2001 Wasabi Systems, Inc.
* All rights reserved.
*
* Written by Jason R. Thorpe for Wasabi Systems, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed for the NetBSD Project by
* Wasabi Systems, Inc.
* 4. The name of Wasabi Systems, Inc. may not be used to endorse
* or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: canconfig.c,v 1.2 2017/05/27 21:02:55 bouyer Exp $");
#endif
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netcan/can.h>
#include <netcan/can_link.h>
#include <ifaddrs.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
struct command {
const char *cmd_keyword;
int cmd_argcnt;
int cmd_flags;
void (*cmd_func)(const struct command *, int, const char *,
char **);
};
#define CMD_INVERT 0x01 /* "invert" the sense of the command */
static void cmd_up(const struct command *, int, const char *, char **);
static void cmd_down(const struct command *, int, const char *, char **);
static void cmd_brp(const struct command *, int, const char *, char **);
static void cmd_prop_seg(const struct command *, int, const char *, char **);
static void cmd_phase_seg1(const struct command *, int, const char *, char **);
static void cmd_phase_seg2(const struct command *, int, const char *, char **);
static void cmd_sjw(const struct command *, int, const char *, char **);
static void cmd_3samples(const struct command *, int, const char *, char **);
static void cmd_listenonly(const struct command *, int, const char *, char **);
static void cmd_loopback(const struct command *, int, const char *, char **);
static const struct command command_table[] = {
{ "up", 0, 0, cmd_up },
{ "down", 0, 0, cmd_down },
{ "brp", 1, 0, cmd_brp },
{ "prop_seg", 1, 0, cmd_prop_seg },
{ "phase_seg1", 1, 0, cmd_phase_seg1 },
{ "phase_seg2", 1, 0, cmd_phase_seg2 },
{ "sjw", 1, 0, cmd_sjw },
{ "3samples", 0, 0, cmd_3samples },
{ "-3samples", 0, CMD_INVERT, cmd_3samples },
{ "listenonly", 0, 0, cmd_listenonly },
{ "-listenonly", 0, CMD_INVERT, cmd_listenonly },
{ "loopback", 0, 0, cmd_loopback },
{ "-loopback", 0, CMD_INVERT, cmd_loopback },
{ NULL, 0, 0, NULL },
};
static void printall(int);
static void status(int, const char *);
static void show_timings(int, const char *, const char *);
static int is_can(int s, const char *);
static int get_val(const char *, u_long *);
#define do_cmd(a,b,c,d,e,f) do_cmd2((a),(b),(c),(d),(e),NULL,(f))
static int do_cmd2(int, const char *, u_long, void *, size_t, size_t *, int);
__dead static void usage(void);
static int aflag;
static struct ifreq g_ifr;
static int g_ifr_updated = 0;
struct can_link_timecaps g_cltc;
struct can_link_timings g_clt;
static int g_clt_updated = 0;
int
main(int argc, char *argv[])
{
const struct command *cmd;
char *canifname;
int sock, ch;
if (argc < 2)
usage();
while ((ch = getopt(argc, argv, "a")) != -1) {
switch (ch) {
case 'a':
aflag = 1;
break;
default:
usage();
}
}
argc -= optind;
argv += optind;
if (aflag) {
if (argc != 0)
usage();
sock = socket(AF_CAN, SOCK_RAW, CAN_RAW);
if (sock < 0)
err(1, "socket");
printall(sock);
exit(0);
}
if (argc == 0)
usage();
sock = socket(AF_CAN, SOCK_RAW, CAN_RAW);
if (sock < 0)
err(1, "socket");
canifname = argv[0];
if (is_can(sock, canifname) == 0)
errx(1, "%s is not a can interface", canifname);
/* Get a copy of the interface flags. */
strlcpy(g_ifr.ifr_name, canifname, sizeof(g_ifr.ifr_name));
if (ioctl(sock, SIOCGIFFLAGS, &g_ifr) < 0)
err(1, "unable to get interface flags");
argc--;
argv++;
if (argc == 0) {
status(sock, canifname);
exit(0);
}
if (do_cmd(sock, canifname, CANGLINKTIMECAP, &g_cltc, sizeof(g_cltc), 0)
< 0)
err(1, "unable to get can link timecaps");
if (do_cmd(sock, canifname, CANGLINKTIMINGS, &g_clt, sizeof(g_clt), 0) < 0)
err(1, "unable to get can link timings");
while (argc != 0) {
for (cmd = command_table; cmd->cmd_keyword != NULL; cmd++) {
if (strcmp(cmd->cmd_keyword, argv[0]) == 0)
break;
}
if (cmd->cmd_keyword == NULL)
errx(1, "unknown command: %s", argv[0]);
argc--;
argv++;
if (argc < cmd->cmd_argcnt)
errx(1, "command %s requires %d argument%s",
cmd->cmd_keyword, cmd->cmd_argcnt,
cmd->cmd_argcnt == 1 ? "" : "s");
(*cmd->cmd_func)(cmd, sock, canifname, argv);
argc -= cmd->cmd_argcnt;
argv += cmd->cmd_argcnt;
}
/* If the timings changed, update them. */
if (g_clt_updated &&
do_cmd(sock, canifname, CANSLINKTIMINGS, &g_clt, sizeof(g_clt), 1) < 0)
err(1, "unable to set can link timings");
/* If the flags changed, update them. */
if (g_ifr_updated && ioctl(sock, SIOCSIFFLAGS, &g_ifr) < 0)
err(1, "unable to set interface flags");
exit (0);
}
static void
usage(void)
{
static const char *usage_strings[] = {
"-a",
"<canif>",
"<canif> up|down",
"<canif> brp <value>",
"<canif> prop_seg <value>",
"<canif> phase_seg1 <value>",
"<canif> phase_seg2 <value>",
"<canif> sjw <value>",
"<canif> 3samples | -3samples",
"<canif> listenonly | -listenonly",
"<canif> loopback | -loopback",
NULL,
};
extern const char *__progname;
int i;
for (i = 0; usage_strings[i] != NULL; i++)
fprintf(stderr, "%s %s %s\n",
i == 0 ? "usage:" : " ",
__progname, usage_strings[i]);
exit(1);
}
static int
is_can(int s, const char *canif)
{
uint32_t linkmode;
if (do_cmd(s, canif, CANGLINKMODE, &linkmode, sizeof(linkmode), 0) < 0)
return (0);
return (1);
}
static void
printb(const char *s, u_int v, const char *bits)
{
int i, any = 0;
char c;
if (bits && *bits == 8)
printf("%s=%o", s, v);
else
printf("%s=%x", s, v);
if (bits) {
bits++;
putchar('<');
while ((i = *bits++) != 0) {
if (v & (1 << (i-1))) {
if (any)
putchar(',');
any = 1;
for (; (c = *bits) > 32; bits++)
putchar(c);
} else
for (; *bits > 32; bits++)
;
}
putchar('>');
}
}
static void
printall(int sock)
{
struct ifaddrs *ifap, *ifa;
char *p;
if (getifaddrs(&ifap) != 0)
err(1, "getifaddrs");
p = NULL;
for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
if (is_can(sock, ifa->ifa_name) == 0)
continue;
if (p != NULL && strcmp(p, ifa->ifa_name) == 0)
continue;
p = ifa->ifa_name;
status(sock, ifa->ifa_name);
}
freeifaddrs(ifap);
}
static void
status(int sock, const char *canifname)
{
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
strlcpy(ifr.ifr_name, canifname, sizeof(ifr.ifr_name));
if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0)
err(1, "unable to get flags");
printf("%s: ", canifname);
printb("flags", ifr.ifr_flags, IFFBITS);
printf("\n");
show_timings(sock, canifname, "\t");
}
static int
valid_timings(struct can_link_timecaps *cltc, struct can_link_timings *clt)
{
if (clt->clt_brp < cltc->cltc_brp_min ||
clt->clt_brp > cltc->cltc_brp_max)
return 0;
if (clt->clt_prop < cltc->cltc_prop_min ||
clt->clt_prop > cltc->cltc_prop_max)
return 0;
if (clt->clt_ps1 < cltc->cltc_ps1_min ||
clt->clt_ps1 > cltc->cltc_ps1_max)
return 0;
if (clt->clt_ps2 < cltc->cltc_ps2_min ||
clt->clt_ps2 > cltc->cltc_ps2_max)
return 0;
return 1;
}
static void
show_timings(int sock, const char *canifname, const char *prefix)
{
struct can_link_timecaps cltc;
struct can_link_timings clt;
u_int32_t linkmode;
char hbuf[8];
if (do_cmd(sock, canifname, CANGLINKTIMECAP, &cltc, sizeof(cltc), 0)
< 0)
err(1, "unable to get can link timecaps");
if (do_cmd(sock, canifname, CANGLINKTIMINGS, &clt, sizeof(clt), 0) < 0)
err(1, "unable to get can link timings");
if (do_cmd(sock, canifname, CANGLINKMODE, &linkmode, sizeof(linkmode),
0) < 0)
err(1, "unable to get can link mode");
humanize_number(hbuf, sizeof(hbuf), cltc.cltc_clock_freq, "Hz",
HN_AUTOSCALE, HN_NOSPACE | HN_DIVISOR_1000);
printf("%stiming caps:\n", prefix);
printf("%s clock %s, brp [%d..%d]/%d, prop_seg [%d..%d]\n",
prefix, hbuf,
cltc.cltc_brp_min, cltc.cltc_brp_max, cltc.cltc_brp_inc,
cltc.cltc_prop_min, cltc.cltc_prop_max);
printf("%s phase_seg1 [%d..%d], phase_seg2 [%d..%d], sjw [0..%d]\n",
prefix,
cltc.cltc_ps1_min, cltc.cltc_ps1_max,
cltc.cltc_ps2_min, cltc.cltc_ps2_max,
cltc.cltc_sjw_max);
printf("%s ", prefix);
printb("capabilities", cltc.cltc_linkmode_caps, CAN_IFFBITS);
printf("\n");
printf("%soperational timings:", prefix);
if (valid_timings(&cltc, &clt)) {
uint32_t tq, ntq, bps;
tq = ((uint64_t)clt.clt_brp * (uint64_t)1000000000) /
cltc.cltc_clock_freq;
ntq = 1 + clt.clt_prop + clt.clt_ps1 + clt.clt_ps2;
printf(" %d time quanta of %dns",
1 + clt.clt_prop + clt.clt_ps1 + clt.clt_ps2, tq);
bps = 1000000000 / (tq * ntq);
humanize_number(hbuf, sizeof(hbuf), bps, "bps",
HN_AUTOSCALE, HN_NOSPACE | HN_DIVISOR_1000);
printf(", %s", hbuf);
};
printf("\n");
printf("%s brp %d, prop_seg %d, phase_seg1 %d, phase_seg2 %d, sjw %d\n",
prefix,
clt.clt_brp, clt.clt_prop, clt.clt_ps1, clt.clt_ps2, clt.clt_sjw);
printf("%s ", prefix);
printb("mode", linkmode, CAN_IFFBITS);
printf("\n");
}
static int
get_val(const char *cp, u_long *valp)
{
char *endptr;
u_long val;
errno = 0;
val = strtoul(cp, &endptr, 0);
if (cp[0] == '\0' || endptr[0] != '\0' || errno == ERANGE)
return (-1);
*valp = val;
return (0);
}
static int
do_cmd2(int sock, const char *canifname, u_long op, void *arg, size_t argsize,
size_t *outsizep, int set)
{
struct ifdrv ifd;
int error;
memset(&ifd, 0, sizeof(ifd));
strlcpy(ifd.ifd_name, canifname, sizeof(ifd.ifd_name));
ifd.ifd_cmd = op;
ifd.ifd_len = argsize;
ifd.ifd_data = arg;
error = ioctl(sock, set ? SIOCSDRVSPEC : SIOCGDRVSPEC, &ifd);
if (outsizep)
*outsizep = ifd.ifd_len;
return error;
}
static void
do_ifflag(int sock, const char *canifname, int flag, int set)
{
if (set)
g_ifr.ifr_flags |= flag;
else
g_ifr.ifr_flags &= ~flag;
g_ifr_updated = 1;
}
static int
do_canflag(int sock, const char *canifname, uint32_t flag, int set)
{
int cmd;
if (set)
cmd = CANSLINKMODE;
else
cmd = CANCLINKMODE;
return do_cmd(sock, canifname, cmd, &flag, sizeof(flag), 1);
}
static void
cmd_up(const struct command *cmd, int sock, const char *canifname,
char **argv)
{
do_ifflag(sock, canifname, IFF_UP, 1);
}
static void
cmd_down(const struct command *cmd, int sock, const char *canifname,
char **argv)
{
do_ifflag(sock, canifname, IFF_UP, 0);
}
static void
cmd_brp(const struct command *cmd, int sock, const char *bridge,
char **argv)
{
u_long val;
if (get_val(argv[0], &val) < 0 || (val & ~0xffffffff) != 0)
errx(1, "%s: invalid value: %s", cmd->cmd_keyword, argv[0]);
if (val < g_cltc.cltc_brp_min || val > g_cltc.cltc_brp_max)
errx(1, "%s: out of range value: %s", cmd->cmd_keyword, argv[0]);
g_clt.clt_brp = val;
g_clt_updated=1;
}
static void
cmd_prop_seg(const struct command *cmd, int sock, const char *bridge,
char **argv)
{
u_long val;
if (get_val(argv[0], &val) < 0 || (val & ~0xffffffff) != 0)
errx(1, "%s: invalid value: %s", cmd->cmd_keyword, argv[0]);
if (val < g_cltc.cltc_prop_min || val > g_cltc.cltc_prop_max)
errx(1, "%s: out of range value: %s", cmd->cmd_keyword, argv[0]);
g_clt.clt_prop = val;
g_clt_updated=1;
}
static void
cmd_phase_seg1(const struct command *cmd, int sock, const char *bridge,
char **argv)
{
u_long val;
if (get_val(argv[0], &val) < 0 || (val & ~0xffffffff) != 0)
errx(1, "%s: invalid value: %s", cmd->cmd_keyword, argv[0]);
if (val < g_cltc.cltc_ps1_min || val > g_cltc.cltc_ps1_max)
errx(1, "%s: out of range value: %s", cmd->cmd_keyword, argv[0]);
g_clt.clt_ps1 = val;
g_clt_updated=1;
}
static void
cmd_phase_seg2(const struct command *cmd, int sock, const char *bridge,
char **argv)
{
u_long val;
if (get_val(argv[0], &val) < 0 || (val & ~0xffffffff) != 0)
errx(1, "%s: invalid value: %s", cmd->cmd_keyword, argv[0]);
if (val < g_cltc.cltc_ps2_min || val > g_cltc.cltc_ps2_max)
errx(1, "%s: out of range value: %s", cmd->cmd_keyword, argv[0]);
g_clt.clt_ps2 = val;
g_clt_updated=1;
}
static void
cmd_sjw(const struct command *cmd, int sock, const char *bridge,
char **argv)
{
u_long val;
if (get_val(argv[0], &val) < 0 || (val & ~0xffffffff) != 0)
errx(1, "%s: invalid value: %s", cmd->cmd_keyword, argv[0]);
if (val > g_cltc.cltc_sjw_max)
errx(1, "%s: out of range value: %s", cmd->cmd_keyword, argv[0]);
g_clt.clt_sjw = val;
g_clt_updated=1;
}
static void
cmd_3samples(const struct command *cmd, int sock, const char *canifname,
char **argv)
{
if (do_canflag(sock, canifname, CAN_LINKMODE_3SAMPLES,
(cmd->cmd_flags & CMD_INVERT) ? 0 : 1) < 0)
err(1, "%s", cmd->cmd_keyword);
}
static void
cmd_listenonly(const struct command *cmd, int sock, const char *canifname,
char **argv)
{
if (do_canflag(sock, canifname, CAN_LINKMODE_LISTENONLY,
(cmd->cmd_flags & CMD_INVERT) ? 0 : 1) < 0)
err(1, "%s", cmd->cmd_keyword);
}
static void
cmd_loopback(const struct command *cmd, int sock, const char *canifname,
char **argv)
{
if (do_canflag(sock, canifname, CAN_LINKMODE_LOOPBACK,
(cmd->cmd_flags & CMD_INVERT) ? 0 : 1) < 0)
err(1, "%s", cmd->cmd_keyword);
}

View File

@ -1,4 +1,4 @@
# $NetBSD: Makefile,v 1.636 2017/05/16 23:21:53 jdolecek Exp $
# $NetBSD: Makefile,v 1.637 2017/05/27 21:02:55 bouyer Exp $
# @(#)Makefile 8.1 (Berkeley) 6/18/93
MAN= aac.4 ac97.4 acardide.4 aceride.4 acphy.4 \
@ -16,7 +16,7 @@ MAN= aac.4 ac97.4 acardide.4 aceride.4 acphy.4 \
brgphy.4 bridge.4 bthidev.4 bthub.4 btkbd.4 \
btmagic.4 btms.4 btsco.4 btuart.4 \
bwi.4 \
cac.4 cardbus.4 carp.4 cas.4 ccd.4 cd.4 \
cac.4 can.4 canloop.4 cardbus.4 carp.4 cas.4 ccd.4 cd.4 \
cec.4 cgd.4 cfb.4 ch.4 chipsfb.4 ciphy.4 ciss.4 clcs.4 clct.4 \
clockctl.4 cmdide.4 cmpci.4 cms.4 cnw.4 \
com.4 coram.4 crypto.4 cs80bus.4 cuda.4 cypide.4 cxdtv.4 \

96
share/man/man4/can.4 Normal file
View File

@ -0,0 +1,96 @@
.\" $NetBSD: can.4,v 1.2 2017/05/27 21:02:55 bouyer Exp $
.\"
.\" Copyright (c) 2017 Manuel Bouyer.
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.Dd May 18, 2017
.Dt CAN 4
.Os
.Sh NAME
.Nm CAN
.Nd CAN Protocol
.Sh SYNOPSIS
.In sys/socket.h
.In netcan/can.h
.Ft int
.Fn socket AF_CAN SOCK_RAW CAN_RAW
.Sh DESCRIPTION
.Nm
is the network layer protocol used on top of CAN bus networks. At this time
only the SOCK_RAW socket type is supported.
This protocol layer is intended to be compatible with the linux SocketCAN implementation.
.Ss ADDRESSING
A CAN frame consists of a 11 bits (standard frame format) or 29 bits
(extended frame format) identifier, followed by up to 8 data bytes.
The interpretation of the identifier is application-dependant, the CAN
standard itself doesn't define an addressing.
.Pp
The
.Nm
layer uses a 32bits identifier. The 3 upper bits are used as control flags.
The extended frame format is selected by setting the CAN_EFF_FLAG control bit.
.Pp
The socket address is defined as
.Bd -literal
struct sockaddr_can {
u_int8_t can_len;
sa_family_t can_family;
int can_ifindex;
union {
/* transport protocol class address information */
struct { canid_t rx_id, tx_id; } tp;
/* reserved for future CAN protocols address information */
} can_addr;
};
.Ed
For CAN raw sockets, the 32bits identifier is part of the message data.
The can_addr field of the sockaddr structure is not used.
.Ss MESSAGE
Raw CAN sockets use fixed-length messages defined as follow:
.Bd -literal
struct can_frame {
canid_t can_id; /* ID + EFF/RTR/ERR flags */
uint8_t can_dlc; /* frame payload length in byte (0 .. CAN_MAX_DLEN) */
uint8_t __pad;
uint8_t __res0;
uint8_t __res1;
uint8_t data[CAN_MAX_DLEN] __aligned(8);
};
.Ed
The lower 11 bits (for standard frames) or 29 bits (for exended frames) are
used as the on-wire identifier. The CAN_EFF_FLAG bit is set in can_id for
extended frames. The CAN_RTR_FLAG bit is set in can_id for remote transmission
request frames.
.Sh SEE ALSO
.Xr socket 2 ,
.Xr netintro 4 ,
.Xr canloop 4 ,
.Xr canconfig 8 ,
.Pa /usr/include/netcan/can.h
.Pp
.Lk https://en.wikipedia.org/wiki/SocketCAN "SocketCAN - Wikipedia"
.Lk https://www.kernel.org/doc/Documentation/networking/can.txt "Readme file for the Controller Area Network Protocol Family"
.Sh BUGS
CANFD and error frames are not implemented.
.Sh HISTORY
The
.Nm
protocol appeared in
.Nx 8.0 .

53
share/man/man4/canloop.4 Normal file
View File

@ -0,0 +1,53 @@
.\" $NetBSD: canloop.4,v 1.2 2017/05/27 21:02:55 bouyer Exp $
.\"
.\" Copyright (c) 2017 Manuel Bouyer.
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.Dd May 18, 2017
.Dt CANLOOP 4
.Os
.Sh NAME
.Nm canloop
.Nd software loopback CAN network interface
.Sh SYNOPSIS
.Cd "pseudo-device canloop"
.Sh DESCRIPTION
The
.Nm canloop
pseudo interface is a loopback interface for the CAN layer. It can be used
as destination by a CAN application, when no CAN hardware is available.
Other sockets bound to the same
.Nm canloop
interface (or not bound to any interface) will receive the frames.
.Pp
.Nm canloop
interfaces can be created by using the
.Xr ifconfig 8
.Cm create
command.
.Sh SEE ALSO
.Xr can 4 ,
.Xr intro 4 ,
.Xr ifconfig 8
.Sh HISTORY
The
.Nm
device appeared in
.Nx 8.0 .

View File

@ -1,9 +1,9 @@
# $NetBSD: Makefile,v 1.79 2013/03/01 18:25:27 joerg Exp $
# $NetBSD: Makefile,v 1.80 2017/05/27 21:02:55 bouyer Exp $
.include <bsd.own.mk>
SUBDIR= altq arch compat dev fs miscfs \
net net80211 netatalk netbt netipsec netinet netinet6 \
net net80211 netatalk netbt netcan netipsec netinet netinet6 \
netisdn netkey netmpls netnatm netsmb \
nfs opencrypto sys ufs uvm

View File

@ -0,0 +1,627 @@
/* $NetBSD: awin_can.c,v 1.2 2017/05/27 21:02:55 bouyer Exp $ */
/*-
* Copyright (c) 2017 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Manuel Bouyer.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "locators.h"
#include "opt_can.h"
#include <sys/cdefs.h>
__KERNEL_RCSID(1, "$NetBSD: awin_can.c,v 1.2 2017/05/27 21:02:55 bouyer Exp $");
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/device.h>
#include <sys/intr.h>
#include <sys/ioctl.h>
#include <sys/mutex.h>
#include <sys/rndsource.h>
#include <sys/mbuf.h>
#include <sys/systm.h>
#include <net/if.h>
#include <net/if_types.h>
#include <net/bpf.h>
#ifdef CAN
#include <netcan/can.h>
#include <netcan/can_var.h>
#endif
#include <arm/allwinner/awin_reg.h>
#include <arm/allwinner/awin_var.h>
/* shortcut for all error interrupts */
#define AWIN_CAN_INT_ALLERRS (\
AWIN_CAN_INT_BERR | \
AWIN_CAN_INT_ARB_LOST | \
AWIN_CAN_INT_ERR_PASSIVE | \
AWIN_CAN_INT_DATA_OR | \
AWIN_CAN_INT_ERR \
)
struct awin_can_softc {
struct canif_softc sc_cansc;
bus_space_tag_t sc_bst;
bus_space_handle_t sc_bsh;
kmutex_t sc_intr_lock;
void *sc_ih;
struct ifnet *sc_ifp;
krndsource_t sc_rnd_source; /* random source */
struct mbuf *sc_m_transmit; /* mbuf being transmitted */
};
#define sc_dev sc_cansc.csc_dev
#define sc_timecaps sc_cansc.csc_timecaps
#define sc_timings sc_cansc.csc_timings
#define sc_linkmodes sc_cansc.csc_linkmodes
static int awin_can_match(device_t, cfdata_t, void *);
static void awin_can_attach(device_t, device_t, void *);
static int awin_can_intr(void *);
static void awin_can_ifstart(struct ifnet *);
static int awin_can_ifioctl(struct ifnet *, u_long, void *);
static void awin_can_ifwatchdog(struct ifnet *);
static void awin_can_enter_reset(struct awin_can_softc *);
static void awin_can_exit_reset(struct awin_can_softc *);
CFATTACH_DECL_NEW(awin_can, sizeof(struct awin_can_softc),
awin_can_match, awin_can_attach, NULL, NULL);
static const struct awin_gpio_pinset awin_can_pinsets[2] = {
[0] = { 'A', AWIN_PIO_PA_CAN_FUNC, AWIN_PIO_PA_CAN_PINS },
[1] = { 'H', AWIN_PIO_PH_CAN_FUNC, AWIN_PIO_PH_CAN_PINS },
};
static inline uint32_t
awin_can_read(struct awin_can_softc *sc, bus_size_t o)
{
return bus_space_read_4(sc->sc_bst, sc->sc_bsh, o);
}
static inline void
awin_can_write(struct awin_can_softc *sc, bus_size_t o, uint32_t v)
{
return bus_space_write_4(sc->sc_bst, sc->sc_bsh, o, v);
}
static int
awin_can_match(device_t parent, cfdata_t cf, void *aux)
{
struct awinio_attach_args * const aio __diagused = aux;
const struct awin_locators * const loc __diagused = &aio->aio_loc;
const struct awin_gpio_pinset * const pinset =
&awin_can_pinsets[cf->cf_flags & 1];
KASSERT(!strcmp(cf->cf_name, loc->loc_name));
KASSERT(cf->cf_loc[AWINIOCF_PORT] == AWINIOCF_PORT_DEFAULT
|| cf->cf_loc[AWINIOCF_PORT] == loc->loc_port);
if (!awin_gpio_pinset_available(pinset)) {
return 0;
}
return 1;
}
static void
awin_can_attach(device_t parent, device_t self, void *aux)
{
cfdata_t cf = device_cfdata(self);
struct awin_can_softc * const sc = device_private(self);
struct awinio_attach_args * const aio = aux;
const struct awin_locators * const loc = &aio->aio_loc;
const struct awin_gpio_pinset * const pinset =
&awin_can_pinsets[cf->cf_flags & 1];
struct ifnet *ifp;
sc->sc_dev = self;
awin_gpio_pinset_acquire(pinset);
awin_reg_set_clear(aio->aio_core_bst, aio->aio_ccm_bsh,
AWIN_APB1_GATING_REG, AWIN_APB_GATING1_CAN, 0);
mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_NET);
sc->sc_bst = aio->aio_core_bst;
bus_space_subregion(sc->sc_bst, aio->aio_core_bsh,
loc->loc_offset, loc->loc_size, &sc->sc_bsh);
sc->sc_timecaps.cltc_prop_min = 0;
sc->sc_timecaps.cltc_prop_max = 0;
sc->sc_timecaps.cltc_ps1_min = 1;
sc->sc_timecaps.cltc_ps1_max = 16;
sc->sc_timecaps.cltc_ps2_min = 1;
sc->sc_timecaps.cltc_ps2_max = 8;
sc->sc_timecaps.cltc_sjw_max = 4;
sc->sc_timecaps.cltc_brp_min = 1;
sc->sc_timecaps.cltc_brp_max = 64;
sc->sc_timecaps.cltc_brp_inc = 1;
sc->sc_timecaps.cltc_clock_freq = AWIN_REF_FREQ;
sc->sc_timecaps.cltc_linkmode_caps =
CAN_LINKMODE_3SAMPLES | CAN_LINKMODE_LISTENONLY |
CAN_LINKMODE_LOOPBACK;
can_ifinit_timings(&sc->sc_cansc);
sc->sc_timings.clt_prop = 0;
sc->sc_timings.clt_sjw = 1;
aprint_naive("\n");
aprint_normal(": CAN bus controller\n");
awin_can_enter_reset(sc);
/*
* Disable and then clear all interrupts
*/
awin_can_write(sc, AWIN_CAN_INTE_REG, 0);
awin_can_write(sc, AWIN_CAN_INT_REG,
awin_can_read(sc, AWIN_CAN_INT_REG));
sc->sc_ih = intr_establish(loc->loc_intr, IPL_NET, IST_LEVEL,
awin_can_intr, sc);
if (sc->sc_ih == NULL) {
aprint_error_dev(self, "failed to establish interrupt %d\n",
loc->loc_intr);
return;
}
aprint_normal_dev(self, "interrupting on irq %d\n", loc->loc_intr);
ifp = if_alloc(IFT_OTHER);
sc->sc_ifp = ifp;
strlcpy(ifp->if_xname, device_xname(self), IFNAMSIZ);
ifp->if_softc = sc;
ifp->if_capabilities = 0;
ifp->if_flags = 0;
ifp->if_start = awin_can_ifstart;
ifp->if_ioctl = awin_can_ifioctl;
ifp->if_watchdog = awin_can_ifwatchdog;
/*
* Attach the interface.
*/
can_ifattach(ifp);
if_deferred_start_init(ifp, NULL);
bpf_mtap_softint_init(ifp);
rnd_attach_source(&sc->sc_rnd_source, device_xname(self),
RND_TYPE_NET, RND_FLAG_DEFAULT);
#ifdef MBUFTRACE
ifp->if_mowner = malloc(sizeof(struct mowner), M_DEVBUF,
M_WAITOK | M_ZERO);
strlcpy(ifp->if_mowner->mo_name, ifp->if_xname,
sizeof(ifp->if_mowner->mo_name));
MOWNER_ATTACH(ifp->if_mowner);
#endif
}
static void
awin_can_rx_intr(struct awin_can_softc *sc)
{
uint32_t reg0v;
struct mbuf *m;
struct ifnet *ifp = sc->sc_ifp;
struct can_frame *cf;
int dlc;
int regd, i;
KASSERT(mutex_owned(&sc->sc_intr_lock));
reg0v = awin_can_read(sc, AWIN_CAN_TXBUF0_REG);
dlc = reg0v & AWIN_CAN_TXBUF0_DL;
if (dlc > CAN_MAX_DLC) {
ifp->if_ierrors++;
return;
}
m = m_gethdr(M_NOWAIT, MT_HEADER);
if (m == NULL) {
ifp->if_ierrors++;
return;
}
cf = mtod(m, struct can_frame *);
memset(cf, 0, sizeof(struct can_frame));
cf->can_dlc = dlc;
if (reg0v & AWIN_CAN_TXBUF0_EFF) {
cf->can_id =
(awin_can_read(sc, AWIN_CAN_TXBUF1_REG) << 21) |
(awin_can_read(sc, AWIN_CAN_TXBUF2_REG) << 13) |
(awin_can_read(sc, AWIN_CAN_TXBUF3_REG) << 5) |
((awin_can_read(sc, AWIN_CAN_TXBUF4_REG) >> 3) & 0x1f);
cf->can_id |= CAN_EFF_FLAG;
regd = AWIN_CAN_TXBUF5_REG;
} else {
cf->can_id =
(awin_can_read(sc, AWIN_CAN_TXBUF1_REG) << 3) |
((awin_can_read(sc, AWIN_CAN_TXBUF2_REG) << 5) & 0x7);
regd = AWIN_CAN_TXBUF3_REG;
}
if (reg0v & AWIN_CAN_TXBUF0_RTR) {
cf->can_id |= CAN_RTR_FLAG;
} else {
for (i = 0; i < cf->can_dlc; i++) {
cf->data[i] = awin_can_read(sc, regd + i * 4);
}
}
awin_can_write(sc, AWIN_CAN_CMD_REG, AWIN_CAN_CMD_REL_RX_BUF);
m->m_len = m->m_pkthdr.len = CAN_MTU;
ifp->if_ipackets++;
ifp->if_ibytes += m->m_len;
m_set_rcvif(m, ifp);
can_bpf_mtap(ifp, m, 1);
can_input(ifp, m);
}
static void
awin_can_tx_intr(struct awin_can_softc *sc)
{
struct ifnet * const ifp = sc->sc_ifp;
struct mbuf *m;
KASSERT(mutex_owned(&sc->sc_intr_lock));
if ((m = sc->sc_m_transmit) != NULL) {
ifp->if_obytes += m->m_len;
ifp->if_opackets++;
can_mbuf_tag_clean(m);
m_set_rcvif(m, ifp);
can_input(ifp, m); /* loopback */
sc->sc_m_transmit = NULL;
ifp->if_timer = 0;
}
ifp->if_flags &= ~IFF_OACTIVE;
if_schedule_deferred_start(ifp);
}
static int
awin_can_tx_abort(struct awin_can_softc *sc)
{
KASSERT(mutex_owned(&sc->sc_intr_lock));
if (sc->sc_m_transmit) {
m_freem(sc->sc_m_transmit);
sc->sc_m_transmit = NULL;
sc->sc_ifp->if_timer = 0;
/*
* the transmit abort will trigger a TX interrupt
* which will restart the queue or cleae OACTIVE,
* as appropriate
*/
awin_can_write(sc, AWIN_CAN_CMD_REG, AWIN_CAN_CMD_ABT_REQ);
return 1;
}
return 0;
}
static void
awin_can_err_intr(struct awin_can_softc *sc, uint32_t irq, uint32_t sts)
{
struct ifnet * const ifp = sc->sc_ifp;
KASSERT(mutex_owned(&sc->sc_intr_lock));
int txerr = 0;
uint32_t reg;
if (irq & AWIN_CAN_INT_DATA_OR) {
ifp->if_ierrors++;
awin_can_write(sc, AWIN_CAN_CMD_REG, AWIN_CAN_CMD_CLR_OR);
}
if (irq & AWIN_CAN_INT_ERR) {
reg = awin_can_read(sc, AWIN_CAN_REC_REG);
printf("%s: ERR interrupt status 0x%x counters 0x%x\n",
device_xname(sc->sc_dev), sts, reg);
}
if (irq & AWIN_CAN_INT_BERR) {
if (sts & AWIN_CAN_STA_TX)
txerr++;
if (sts & AWIN_CAN_STA_RX)
ifp->if_ierrors++;
}
if (irq & AWIN_CAN_INT_ERR_PASSIVE) {
printf("%s: PASSV interrupt status 0x%x\n",
device_xname(sc->sc_dev), sts);
}
if (irq & AWIN_CAN_INT_ARB_LOST) {
txerr++;
}
if (txerr) {
ifp->if_oerrors += txerr;
(void) awin_can_tx_abort(sc);
}
}
int
awin_can_intr(void *arg)
{
struct awin_can_softc * const sc = arg;
int rv = 0;
int irq;
mutex_enter(&sc->sc_intr_lock);
while ((irq = awin_can_read(sc, AWIN_CAN_INT_REG)) != 0) {
uint32_t sts = awin_can_read(sc, AWIN_CAN_STA_REG);
rv = 1;
if (irq & AWIN_CAN_INT_TX_FLAG) {
awin_can_tx_intr(sc);
}
if (irq & AWIN_CAN_INT_RX_FLAG) {
while (sts & AWIN_CAN_STA_RX_RDY) {
awin_can_rx_intr(sc);
sts = awin_can_read(sc, AWIN_CAN_STA_REG);
}
}
if (irq & AWIN_CAN_INT_ALLERRS) {
awin_can_err_intr(sc, irq, sts);
}
awin_can_write(sc, AWIN_CAN_INT_REG, irq);
rnd_add_uint32(&sc->sc_rnd_source, irq);
}
mutex_exit(&sc->sc_intr_lock);
return rv;
}
void
awin_can_ifstart(struct ifnet *ifp)
{
struct awin_can_softc * const sc = ifp->if_softc;
struct mbuf *m;
struct can_frame *cf;
int regd;
uint32_t reg0val;
int i;
mutex_enter(&sc->sc_intr_lock);
if (ifp->if_flags & IFF_OACTIVE)
goto out;
IF_DEQUEUE(&ifp->if_snd, m);
if (m == NULL)
goto out;
MCLAIM(m, ifp->if_mowner);
sc->sc_m_transmit = m;
KASSERT((m->m_flags & M_PKTHDR) != 0);
KASSERT(m->m_len == m->m_pkthdr.len);
cf = mtod(m, struct can_frame *);
reg0val = cf->can_dlc & AWIN_CAN_TXBUF0_DL;
if (cf->can_id & CAN_RTR_FLAG)
reg0val |= AWIN_CAN_TXBUF0_RTR;
if (cf->can_id & CAN_EFF_FLAG) {
reg0val |= AWIN_CAN_TXBUF0_EFF;
awin_can_write(sc, AWIN_CAN_TXBUF1_REG,
(cf->can_id >> 21) & 0xff);
awin_can_write(sc, AWIN_CAN_TXBUF2_REG,
(cf->can_id >> 13) & 0xff);
awin_can_write(sc, AWIN_CAN_TXBUF3_REG,
(cf->can_id >> 5) & 0xff);
awin_can_write(sc, AWIN_CAN_TXBUF4_REG,
(cf->can_id << 3) & 0xf8);
regd = AWIN_CAN_TXBUF5_REG;
} else {
awin_can_write(sc, AWIN_CAN_TXBUF1_REG,
(cf->can_id >> 3) & 0xff);
awin_can_write(sc, AWIN_CAN_TXBUF2_REG,
(cf->can_id << 5) & 0xe0);
regd = AWIN_CAN_TXBUF3_REG;
}
for (i = 0; i < cf->can_dlc; i++) {
awin_can_write(sc, regd + i * 4, cf->data[i]);
}
awin_can_write(sc, AWIN_CAN_TXBUF0_REG, reg0val);
if (sc->sc_linkmodes & CAN_LINKMODE_LOOPBACK) {
awin_can_write(sc, AWIN_CAN_CMD_REG,
AWIN_CAN_CMD_TANS_REQ | AWIN_CAN_CMD_SELF_REQ);
} else {
awin_can_write(sc, AWIN_CAN_CMD_REG, AWIN_CAN_CMD_TANS_REQ);
}
ifp->if_flags |= IFF_OACTIVE;
ifp->if_timer = 5;
can_bpf_mtap(ifp, m, 0);
out:
mutex_exit(&sc->sc_intr_lock);
}
static int
awin_can_ifup(struct awin_can_softc * const sc)
{
uint32_t reg;
/* setup timings and mode - has to be done in reset */
reg = AWIN_CAN_MODSEL_RST;
if (sc->sc_linkmodes & CAN_LINKMODE_LISTENONLY)
reg |= AWIN_CAN_MODSEL_LST_ONLY;
if (sc->sc_linkmodes & CAN_LINKMODE_LOOPBACK)
reg |= AWIN_CAN_MODSEL_LB_MOD;
awin_can_write(sc, AWIN_CAN_MODSEL_REG, reg);
reg = 0;
if (sc->sc_timings.clt_prop != 0)
return EINVAL;
if (sc->sc_timings.clt_brp > sc->sc_timecaps.cltc_brp_max ||
sc->sc_timings.clt_brp < sc->sc_timecaps.cltc_brp_min)
return EINVAL;
reg |= (sc->sc_timings.clt_brp - 1) << 0;
if (sc->sc_timings.clt_ps1 > sc->sc_timecaps.cltc_ps1_max ||
sc->sc_timings.clt_ps1 < sc->sc_timecaps.cltc_ps1_min)
return EINVAL;
reg |= (sc->sc_timings.clt_ps1 - 1) << 16;
if (sc->sc_timings.clt_ps2 > sc->sc_timecaps.cltc_ps2_max ||
sc->sc_timings.clt_ps2 < sc->sc_timecaps.cltc_ps2_min)
return EINVAL;
reg |= (sc->sc_timings.clt_ps2 - 1) << 20;
if (sc->sc_timings.clt_sjw > sc->sc_timecaps.cltc_sjw_max ||
sc->sc_timings.clt_sjw < 1)
return EINVAL;
reg |= (sc->sc_timings.clt_sjw - 1) << 14;
if (sc->sc_linkmodes & CAN_LINKMODE_3SAMPLES)
reg |= AWIN_CAN_BUS_TIME_SAM;
awin_can_write(sc, AWIN_CAN_BUS_TIME_REG, reg);
/* set filters to accept all frames */
awin_can_write(sc, AWIN_CAN_ACPC, 0x00000000);
awin_can_write(sc, AWIN_CAN_ACPM, 0xffffffff);
/* clear errors counter */
awin_can_write(sc, AWIN_CAN_REC_REG, 0);
/* leave reset mode and enable interrupts */
awin_can_exit_reset(sc);
awin_can_write(sc, AWIN_CAN_INTE_REG,
AWIN_CAN_INT_TX_FLAG | AWIN_CAN_INT_RX_FLAG | AWIN_CAN_INT_ALLERRS);
sc->sc_ifp->if_flags |= IFF_RUNNING;
return 0;
}
static void
awin_can_ifdown(struct awin_can_softc * const sc)
{
sc->sc_ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
sc->sc_ifp->if_timer = 0;
awin_can_enter_reset(sc);
awin_can_write(sc, AWIN_CAN_INTE_REG, 0);
awin_can_write(sc, AWIN_CAN_INT_REG,
awin_can_read(sc, AWIN_CAN_INT_REG));
}
static int
awin_can_ifioctl(struct ifnet *ifp, u_long cmd, void *data)
{
struct awin_can_softc * const sc = ifp->if_softc;
struct ifreq *ifr = (struct ifreq *)data;
int error = 0;
mutex_enter(&sc->sc_intr_lock);
switch (cmd) {
case SIOCINITIFADDR:
error = EAFNOSUPPORT;
break;
case SIOCSIFMTU:
if ((unsigned)ifr->ifr_mtu != sizeof(struct can_frame))
error = EINVAL;
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
error = EAFNOSUPPORT;
break;
default:
error = ifioctl_common(ifp, cmd, data);
if (error == 0) {
if ((ifp->if_flags & IFF_UP) != 0 &&
(ifp->if_flags & IFF_RUNNING) == 0) {
error = awin_can_ifup(sc);
if (error) {
ifp->if_flags &= ~IFF_UP;
}
} else if ((ifp->if_flags & IFF_UP) == 0 &&
(ifp->if_flags & IFF_RUNNING) != 0) {
awin_can_ifdown(sc);
}
}
break;
}
mutex_exit(&sc->sc_intr_lock);
return error;
}
void
awin_can_ifwatchdog(struct ifnet *ifp)
{
struct awin_can_softc * const sc = ifp->if_softc;
printf("%s: watchdog timeout\n", device_xname(sc->sc_dev));
mutex_enter(&sc->sc_intr_lock);
printf("irq 0x%x en 0x%x mode 0x%x status 0x%x timings 0x%x err 0x%x\n",
awin_can_read(sc, AWIN_CAN_INT_REG),
awin_can_read(sc, AWIN_CAN_INTE_REG),
awin_can_read(sc, AWIN_CAN_MODSEL_REG),
awin_can_read(sc, AWIN_CAN_STA_REG),
awin_can_read(sc, AWIN_CAN_BUS_TIME_REG),
awin_can_read(sc, AWIN_CAN_REC_REG));
/* if there is a transmit in progress abort */
if (awin_can_tx_abort(sc)) {
ifp->if_oerrors++;
}
mutex_exit(&sc->sc_intr_lock);
}
static void
awin_can_enter_reset(struct awin_can_softc *sc)
{
int i;
uint32_t val;
for (i = 0; i < 1000; i++) {
val = awin_can_read(sc, AWIN_CAN_MODSEL_REG);
val |= AWIN_CAN_MODSEL_RST;
awin_can_write(sc, AWIN_CAN_MODSEL_REG, val);
val = awin_can_read(sc, AWIN_CAN_MODSEL_REG);
if (val & AWIN_CAN_MODSEL_RST)
return;
}
printf("%s: couldn't enter reset mode\n", device_xname(sc->sc_dev));
}
static void
awin_can_exit_reset(struct awin_can_softc *sc)
{
int i;
uint32_t val;
for (i = 0; i < 1000; i++) {
val = awin_can_read(sc, AWIN_CAN_MODSEL_REG);
val &= ~AWIN_CAN_MODSEL_RST;
awin_can_write(sc, AWIN_CAN_MODSEL_REG, val);
val = awin_can_read(sc, AWIN_CAN_MODSEL_REG);
if ((val & AWIN_CAN_MODSEL_RST) == 0)
return;
}
printf("%s: couldn't leave reset mode\n", device_xname(sc->sc_dev));
}

View File

@ -31,7 +31,7 @@
#include <sys/cdefs.h>
__KERNEL_RCSID(1, "$NetBSD: awin_io.c,v 1.46 2016/05/11 18:33:40 bouyer Exp $");
__KERNEL_RCSID(1, "$NetBSD: awin_io.c,v 1.47 2017/05/27 21:02:55 bouyer Exp $");
#include <sys/param.h>
#include <sys/bus.h>
@ -192,6 +192,7 @@ static const struct awin_locators awin_locators[] = {
{ "awinir", OFFANDSIZE(A31_CIR), NOPORT, AWIN_A31_IRQ_CIR, A31 },
{ "awinir", OFFANDSIZE(A80_CIR), NOPORT, AWIN_A80_IRQ_R_CIR, A80 },
{ "awinlradc", OFFANDSIZE(LRADC), NOPORT, AWIN_IRQ_LRADC, A20 },
{ "awincan", OFFANDSIZE(CAN), NOPORT, AWIN_IRQ_CAN, A20 },
};
static int

View File

@ -1,4 +1,4 @@
/* $NetBSD: awin_reg.h,v 1.90 2016/12/26 16:20:17 rjs Exp $ */
/* $NetBSD: awin_reg.h,v 1.91 2017/05/27 21:02:55 bouyer Exp $ */
/*-
* Copyright (c) 2013 The NetBSD Foundation, Inc.
@ -3071,4 +3071,120 @@ struct awin_a31_dma_desc {
#define AWIN_LRADC_DATA0_REG 0x0c
#define AWIN_LRADC_DATA1_REG 0x10
/* CAN mode select register */
#define AWIN_CAN_MODSEL_REG 0x00
#define AWIN_CAN_MODSEL_SLEEP __BIT(4)
#define AWIN_CAN_MODSEL_ACP_FLT_MOD __BIT(3)
#define AWIN_CAN_MODSEL_LB_MOD __BIT(2)
#define AWIN_CAN_MODSEL_LST_ONLY __BIT(1)
#define AWIN_CAN_MODSEL_RST __BIT(0)
/* CAN command register */
#define AWIN_CAN_CMD_REG 0x04
#define AWIN_CAN_CMD_BUS_OFF __BIT(5)
#define AWIN_CAN_CMD_SELF_REQ __BIT(4)
#define AWIN_CAN_CMD_CLR_OR __BIT(3)
#define AWIN_CAN_CMD_REL_RX_BUF __BIT(2)
#define AWIN_CAN_CMD_ABT_REQ __BIT(1)
#define AWIN_CAN_CMD_TANS_REQ __BIT(0)
/* CAN status register */
#define AWIN_CAN_STA_REG 0x08
#define AWIN_CAN_STA_ERR_CODE __BITS(23,22)
#define AWIN_CAN_STA_ERR_CODE_BIT 0
#define AWIN_CAN_STA_ERR_CODE_FORM 1
#define AWIN_CAN_STA_ERR_CODE_STUFF 2
#define AWIN_CAN_STA_ERR_CODE_OTHER 3
#define AWIN_CAN_STA_ERR_DIR _BIT(21)
#define AWIN_CAN_STA_ERR_SEG_CODE __BITS(20,16)
#define AWIN_CAN_STA_ARB_LOST __BITS(12,8)
#define AWIN_CAN_STA_BUS __BIT(7)
#define AWIN_CAN_STA_ERR __BIT(6)
#define AWIN_CAN_STA_TX __BIT(5)
#define AWIN_CAN_STA_RX __BIT(4)
#define AWIN_CAN_STA_TX_OVER __BIT(3)
#define AWIN_CAN_STA_TX_RDY __BIT(2)
#define AWIN_CAN_STA_DATA_OR __BIT(1)
#define AWIN_CAN_STA_RX_RDY __BIT(0)
/* CAN interrupt register */
#define AWIN_CAN_INT_REG 0x0c
#define AWIN_CAN_INT_BERR __BIT(7)
#define AWIN_CAN_INT_ARB_LOST __BIT(6)
#define AWIN_CAN_INT_ERR_PASSIVE __BIT(5)
#define AWIN_CAN_INT_WAKEUP __BIT(4)
#define AWIN_CAN_INT_DATA_OR __BIT(3)
#define AWIN_CAN_INT_ERR __BIT(2)
#define AWIN_CAN_INT_TX_FLAG __BIT(1)
#define AWIN_CAN_INT_RX_FLAG __BIT(0)
/* CAN interrupt enable register */
#define AWIN_CAN_INTE_REG 0x10
/* CAN bus timing register */
#define AWIN_CAN_BUS_TIME_REG 0x14
#define AWIN_CAN_BUS_TIME_SAM __BIT(23)
#define AWIN_CAN_BUS_TIME_PHSEG2 __BITS(22,20)
#define AWIN_CAN_BUS_TIME_PHSEG1 __BITS(19,16)
#define AWIN_CAN_BUS_TIME_SJW __BITS(15,14)
#define AWIN_CAN_BUS_TIME_TQ_BRP __BITS(9,0)
/* CAN tx error warning limit register */
#define AWIN_CAN_EWL_REG 0x18
#define AWIN_CAN_EWL_ERR_WRN_LMT __BITS(7,0)
/* CAN error counter register */
#define AWIN_CAN_REC_REG 0x1c
#define AWIN_CAN_REC_RX_ERR_CNT __BITS(23,16)
#define AWIN_CAN_REC_TX_ERR_CNT __BITS(7,0)
/* CAN receive message register */
#define AWIN_CAN_RMSGC_REG 0x20
#define AWIN_CAN_RMSGC_RX_MSG_CNT __BITS(7,0)
/* CAN receive buffer start address register */
#define AWIN_CAN_RSADDR_REG 0x24
#define AWIN_CAN_RSADDR_RX_BUF_SADDR __BITS(5,0)
/* CAN rx/tx message buffer 0 register */
#define AWIN_CAN_TXBUF0_REG 0x40
#define AWIN_CAN_TXBUF0_EFF __BIT(7)
#define AWIN_CAN_TXBUF0_RTR __BIT(6)
#define AWIN_CAN_TXBUF0_DL __BITS(3,0)
/* CAN rx/tx message buffer registers */
#define AWIN_CAN_TXBUF1_REG 0x44
#define AWIN_CAN_TXBUF2_REG 0x48
#define AWIN_CAN_TXBUF3_REG 0x4c
#define AWIN_CAN_TXBUF4_REG 0x50
#define AWIN_CAN_TXBUF5_REG 0x54
#define AWIN_CAN_TXBUF6_REG 0x58
#define AWIN_CAN_TXBUF7_REG 0x5c
#define AWIN_CAN_TXBUF8_REG 0x60
#define AWIN_CAN_TXBUF9_REG 0x64
#define AWIN_CAN_TXBUF10_REG 0x68
#define AWIN_CAN_TXBUF11_REG 0x6c
#define AWIN_CAN_TXBUF12_REG 0x70
/* CAN acceptance code 0 register */
#define AWIN_CAN_ACPC 0x40
/* CAN acceptance mask 0 register */
#define AWIN_CAN_ACPM 0x44
/* CAN transmit buffer for read back registers */
#define AWIN_CAN_RBUF_RBACK0 0x180
#define AWIN_CAN_RBUF_RBACK1 0x184
#define AWIN_CAN_RBUF_RBACK2 0x188
#define AWIN_CAN_RBUF_RBACK3 0x18c
#define AWIN_CAN_RBUF_RBACK4 0x190
#define AWIN_CAN_RBUF_RBACK5 0x194
#define AWIN_CAN_RBUF_RBACK6 0x198
#define AWIN_CAN_RBUF_RBACK7 0x19c
#define AWIN_CAN_RBUF_RBACK8 0x1a0
#define AWIN_CAN_RBUF_RBACK9 0x1a4
#define AWIN_CAN_RBUF_RBACK10 0x1a8
#define AWIN_CAN_RBUF_RBACK11 0x1ac
#define AWIN_CAN_RBUF_RBACK12 0x1b0
#endif /* _ARM_ALLWINNER_AWIN_REG_H_ */

View File

@ -1,4 +1,4 @@
# $NetBSD: files.awin,v 1.36 2016/05/27 20:01:49 bouyer Exp $
# $NetBSD: files.awin,v 1.37 2017/05/27 21:02:55 bouyer Exp $
#
# Configuration info for Allwinner ARM Peripherals
#
@ -110,6 +110,11 @@ file arch/arm/allwinner/awin_eth.c awin_eth
attach awge at awinio with awin_gige
file arch/arm/allwinner/awin_gige.c awin_gige
# A20 CAN
device awincan { } : ifnet
attach awincan at awinio with awin_can
file arch/arm/allwinner/awin_can.c awin_can
# USB2 OTG Controller
attach motg at awinio with awin_otg
file arch/arm/allwinner/awin_otg.c awin_otg

View File

@ -1,4 +1,4 @@
# $NetBSD: files,v 1.1171 2017/02/26 11:56:49 rin Exp $
# $NetBSD: files,v 1.1172 2017/05/27 21:02:56 bouyer Exp $
# @(#)files.newconf 7.5 (Berkeley) 5/10/93
version 20150846
@ -232,6 +232,7 @@ file net/bpfjit.c sljit & bpfjit
include "net80211/files.net80211"
include "netatalk/files.netatalk"
include "netbt/files.netbt"
include "netcan/files.netcan"
include "netinet/files.netinet"
include "netinet6/files.netinet6"
include "netipsec/files.netipsec"
@ -1439,6 +1440,7 @@ defpseudodev tap: ifnet, ether, arp
defpseudo carp: ifnet, ether, arp
defpseudodev etherip: ifnet, ether, arp
defpseudodev l2tp: ifnet, ether, arp
defpseudo canloop: ifnet
defpseudo sequencer
defpseudo clockctl

View File

@ -1,4 +1,4 @@
/* $NetBSD: uipc_socket.c,v 1.254 2017/05/25 20:42:36 christos Exp $ */
/* $NetBSD: uipc_socket.c,v 1.255 2017/05/27 21:02:56 bouyer Exp $ */
/*-
* Copyright (c) 2002, 2007, 2008, 2009 The NetBSD Foundation, Inc.
@ -71,7 +71,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: uipc_socket.c,v 1.254 2017/05/25 20:42:36 christos Exp $");
__KERNEL_RCSID(0, "$NetBSD: uipc_socket.c,v 1.255 2017/05/27 21:02:56 bouyer Exp $");
#ifdef _KERNEL_OPT
#include "opt_compat_netbsd.h"
@ -439,6 +439,7 @@ socket_listener_cb(kauth_cred_t cred, kauth_action_t action, void *cookie,
case PF_ROUTE:
case PF_OROUTE:
case PF_BLUETOOTH:
case PF_CAN:
result = KAUTH_RESULT_ALLOW;
break;
default:

View File

@ -1,4 +1,4 @@
/* $NetBSD: netisr.h,v 1.44 2015/05/25 08:29:01 ozaki-r Exp $ */
/* $NetBSD: netisr.h,v 1.45 2017/05/27 21:02:56 bouyer Exp $ */
/*
* Copyright (c) 1980, 1986, 1989, 1993
@ -53,6 +53,7 @@
#include "opt_atalk.h"
#include "opt_mpls.h"
#include "opt_natm.h"
#include "opt_can.h"
#include "arp.h"
#endif /* defined(_KERNEL_OPT) */
@ -90,6 +91,10 @@
#ifdef NETATALK
#include <netatalk/at_extern.h>
#endif
#ifdef CAN
#include <netcan/can.h>
#include <netcan/can_var.h>
#endif
#endif /* !defined(_LOCORE) */
#endif /* defined(_KERNEL) */
@ -109,6 +114,7 @@
#define NETISR_NATM 27 /* same as AF_NATM */
#define NETISR_ARP 28 /* same as AF_ARP */
#define NETISR_MPLS 33 /* same as AF_MPLS */
#define NETISR_CAN 35 /* same as AF_CAN */
#define NETISR_MAX AF_MAX
#if !defined(_LOCORE) && defined(_KERNEL)

View File

@ -1,4 +1,4 @@
/* $NetBSD: netisr_dispatch.h,v 1.18 2014/06/05 23:48:16 rmind Exp $ */
/* $NetBSD: netisr_dispatch.h,v 1.19 2017/05/27 21:02:56 bouyer Exp $ */
#ifndef _NET_NETISR_DISPATCH_H_
#define _NET_NETISR_DISPATCH_H_
@ -41,5 +41,8 @@
#ifdef NATM
DONETISR(NETISR_NATM,natmintr);
#endif
#ifdef CAN
DONETISR(NETISR_CAN,canintr);
#endif
#endif /* !_NET_NETISR_DISPATCH_H_ */

8
sys/netcan/Makefile Normal file
View File

@ -0,0 +1,8 @@
# $NetBSD: Makefile,v 1.2 2017/05/27 21:02:56 bouyer Exp $
KDIR= /sys/netcan
INCSDIR= /usr/include/netcan
INCS= can.h can_link.h
.include <bsd.kinc.mk>

1013
sys/netcan/can.c Normal file

File diff suppressed because it is too large Load Diff

128
sys/netcan/can.h Normal file
View File

@ -0,0 +1,128 @@
/* $NetBSD: can.h,v 1.2 2017/05/27 21:02:56 bouyer Exp $ */
/*-
* Copyright (c) 2003, 2017 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Robert Swindells and Manuel Bouyer
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _NETCAN_CAN_H
#define _NETCAN_CAN_H
#include <sys/featuretest.h>
#include <machine/int_types.h>
/* Definitions compatible (as much as possible) with socketCAN */
/*
* CAN id structure
* bits 0-28 : CAN identifier (11/29 bits, see bit 31)
* bit2 29-31 : see below
*/
typedef uint32_t canid_t;
typedef uint32_t can_err_mask_t;
/* canid_t bits 29-31 descriptions */
#define CAN_EFF_FLAG 0x80000000U /* extended frame format */
#define CAN_RTR_FLAG 0x40000000U /* remote transmission request */
#define CAN_ERR_FLAG 0x20000000U /* error message frame */
/* valid bits in CAN ID for frame formats */
#define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */
#define CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */
#define CAN_ERR_MASK 0x1FFFFFFFU /* error frame format */
/* CAN payload length and DLC definitions according to ISO 11898-1 */
#define CAN_MAX_DLC 8
#define CAN_MAX_DLEN 8
/* CAN frame */
struct can_frame {
canid_t can_id; /* ID + EFF/RTR/ERR flags */
uint8_t can_dlc; /* frame payload length in byte (0 .. CAN_MAX_DLEN) */
uint8_t __pad;
uint8_t __res0;
uint8_t __res1;
uint8_t data[CAN_MAX_DLEN] __aligned(8);
};
#define CAN_MTU (sizeof(struct can_frame))
/* protocols */
#define CAN_RAW 1 /* RAW sockets */
#define CAN_NPROTO 2
/*
* Socket address, CAN style
*/
struct sockaddr_can {
u_int8_t can_len;
sa_family_t can_family;
int can_ifindex;
union {
/* transport protocol class address information (e.g. ISOTP) */
struct { canid_t rx_id, tx_id; } tp;
/* reserved for future CAN protocols address information */
} can_addr;
};
/*
* Options for use with [gs]etsockopt for raw sockets
* First word of comment is data type; bool is stored in int.
*/
#define SOL_CAN_RAW CAN_RAW
#define CAN_RAW_FILTER 1 /* struct can_filter: set filter */
#define CAN_RAW_LOOPBACK 4 /* bool: loopback to local sockets (default:on) */
#define CAN_RAW_RECV_OWN_MSGS 5 /* bool: receive my own msgs (default:off) */
/*
* CAN ID based filter
* checks received can_id & can_filter.can_mask against
* can_filter.can_id & can_filter.can_mask
* valid flags for can_id:
* CAN_INV_FILTER: invert filter
* valid flags for can_mask:
* CAN_ERR_FLAG: filter for error message frames
*/
struct can_filter {
canid_t can_id;
canid_t can_mask;
};
#define CAN_INV_FILTER 0x20000000U
#ifdef _NETBSD_SOURCE
#ifdef _KERNEL
#define satoscan(sa) ((struct sockaddr_can *)(sa))
#define scantosa(scan) ((struct sockaddr *)(scan))
#endif /* _KERNEL */
#endif /* _NETBSD_SOURCE */
#endif /* _NETCAN_CAN_H */

80
sys/netcan/can_link.h Normal file
View File

@ -0,0 +1,80 @@
/* $NetBSD: can_link.h,v 1.2 2017/05/27 21:02:56 bouyer Exp $ */
/*-
* Copyright (c) 2017 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Manuel Bouyer
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _NETCAN_CAN_LINK_H
#define _NETCAN_CAN_LINK_H
/*
* CAN bus link-layer related commands, from the SIOCSDRVSPEC
*/
/* get timing capabilities from HW */
struct can_link_timecaps {
uint32_t cltc_prop_min; /* prop seg, in tq */
uint32_t cltc_prop_max;
uint32_t cltc_ps1_min; /* phase1 seg, in tq */
uint32_t cltc_ps1_max;
uint32_t cltc_ps2_min; /* phase 2 seg, in tq */
uint32_t cltc_ps2_max;
uint32_t cltc_sjw_max; /* Synchronisation Jump Width */
uint32_t cltc_brp_min; /* bit-rate prescaler */
uint32_t cltc_brp_max;
uint32_t cltc_brp_inc;
uint32_t cltc_clock_freq; /* prescaler input clock, in hz */
uint32_t cltc_linkmode_caps; /* link mode, see below */
};
#define CANGLINKTIMECAP 0 /* get struct can_link_timecaps */
/* get/set timing parameters */
struct can_link_timings {
uint32_t clt_brp; /* prescaler value */
uint32_t clt_prop; /* Propagation segment in tq */
uint32_t clt_ps1; /* Phase segment 1 in tq */
uint32_t clt_ps2; /* Phase segment 2 in tq */
uint32_t clt_sjw; /* Synchronisation jump width in tq */
};
#define CANGLINKTIMINGS 1 /* get struct can_link_timings */
#define CANSLINKTIMINGS 2 /* set struct can_link_timings */
/* link-level modes */
#define CAN_LINKMODE_LOOPBACK 0x01 /* Loopback mode */
#define CAN_LINKMODE_LISTENONLY 0x02 /* Listen-only mode */
#define CAN_LINKMODE_3SAMPLES 0x04 /* Triple sampling mode */
#define CAN_LINKMODE_PRESUME_ACK 0x08 /* Ignore missing CAN ACKs */
#define CAN_IFFBITS \
"\020\1LOOPBACK\2LISTENONLY\3TRIPLESAMPLE\4PRESUMEACK"
#define CANGLINKMODE 3 /* (uint32_t) get bits */
#define CANSLINKMODE 4 /* (uint32_t) set bits */
#define CANCLINKMODE 5 /* (uint32_t) clear bits */
#endif /* _NETCAN_CAN_LINK_H */

380
sys/netcan/can_pcb.c Normal file
View File

@ -0,0 +1,380 @@
/* $NetBSD: can_pcb.c,v 1.2 2017/05/27 21:02:56 bouyer Exp $ */
/*-
* Copyright (c) 2003, 2017 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Robert Swindells and Manuel Bouyer
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: can_pcb.c,v 1.2 2017/05/27 21:02:56 bouyer Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/kmem.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/pool.h>
#include <sys/proc.h>
#include <net/if.h>
#include <net/route.h>
#include <netcan/can.h>
#include <netcan/can_var.h>
#include <netcan/can_pcb.h>
#define CANPCBHASH_BIND(table, ifindex) \
&(table)->canpt_bindhashtbl[ \
(ifindex) & (table)->canpt_bindhash]
#define CANPCBHASH_CONNECT(table, ifindex) \
&(table)->canpt_connecthashtbl[ \
(ifindex) & (table)->canpt_bindhash]
struct pool canpcb_pool;
void
can_pcbinit(struct canpcbtable *table, int bindhashsize, int connecthashsize)
{
static int canpcb_pool_initialized;
if (canpcb_pool_initialized == 0) {
pool_init(&canpcb_pool, sizeof(struct canpcb), 0, 0, 0,
"canpcbpl", NULL, IPL_SOFTNET);
canpcb_pool_initialized = 1;
}
TAILQ_INIT(&table->canpt_queue);
table->canpt_bindhashtbl = hashinit(bindhashsize, HASH_LIST, true,
&table->canpt_bindhash);
table->canpt_connecthashtbl = hashinit(connecthashsize, HASH_LIST,
true, &table->canpt_connecthash);
}
int
can_pcballoc(struct socket *so, void *v)
{
struct canpcbtable *table = v;
struct canpcb *canp;
struct can_filter *can_init_filter;
int s;
can_init_filter = kmem_alloc(sizeof(struct can_filter), KM_NOSLEEP);
if (can_init_filter == NULL)
return (ENOBUFS);
can_init_filter->can_id = 0;
can_init_filter->can_mask = 0; /* accept all by default */
s = splnet();
canp = pool_get(&canpcb_pool, PR_NOWAIT);
splx(s);
if (canp == NULL) {
kmem_free(can_init_filter, sizeof(struct can_filter));
return (ENOBUFS);
}
memset(canp, 0, sizeof(*canp));
canp->canp_table = table;
canp->canp_socket = so;
canp->canp_filters = can_init_filter;
canp->canp_nfilters = 1;
mutex_init(&canp->canp_mtx, MUTEX_DEFAULT, IPL_NET);
canp->canp_refcount = 1;
so->so_pcb = canp;
mutex_enter(&canp->canp_mtx);
TAILQ_INSERT_HEAD(&table->canpt_queue, canp, canp_queue);
can_pcbstate(canp, CANP_ATTACHED);
mutex_exit(&canp->canp_mtx);
return (0);
}
int
can_pcbbind(void *v, struct sockaddr_can *scan, struct lwp *l)
{
struct canpcb *canp = v;
if (scan->can_family != AF_CAN)
return (EAFNOSUPPORT);
mutex_enter(&canp->canp_mtx);
if (scan->can_ifindex != 0) {
canp->canp_ifp = if_byindex(scan->can_ifindex);
if (canp->canp_ifp == NULL)
return (EADDRNOTAVAIL);
soisconnected(canp->canp_socket);
} else {
canp->canp_ifp = NULL;
canp->canp_socket->so_state &= ~SS_ISCONNECTED; /* XXX */
}
can_pcbstate(canp, CANP_BOUND);
mutex_exit(&canp->canp_mtx);
return 0;
}
/*
* Connect from a socket to a specified address.
*/
int
can_pcbconnect(void *v, struct sockaddr_can *scan)
{
#if 0
struct canpcb *canp = v;
struct sockaddr_can *ifaddr = NULL;
int error;
#endif
if (scan->can_family != AF_CAN)
return (EAFNOSUPPORT);
#if 0
mutex_enter(&canp->canp_mtx);
memcpy(&canp->canp_dst, scan, sizeof(struct sockaddr_can));
can_pcbstate(canp, CANP_CONNECTED);
mutex_exit(&canp->canp_mtx);
return 0;
#endif
return EOPNOTSUPP;
}
void
can_pcbdisconnect(void *v)
{
struct canpcb *canp = v;
mutex_enter(&canp->canp_mtx);
can_pcbstate(canp, CANP_DETACHED);
mutex_exit(&canp->canp_mtx);
if (canp->canp_socket->so_state & SS_NOFDREF)
can_pcbdetach(canp);
}
void
can_pcbdetach(void *v)
{
struct canpcb *canp = v;
struct socket *so = canp->canp_socket;
KASSERT(mutex_owned(softnet_lock));
so->so_pcb = NULL;
mutex_enter(&canp->canp_mtx);
can_pcbstate(canp, CANP_DETACHED);
can_pcbsetfilter(canp, NULL, 0);
mutex_exit(&canp->canp_mtx);
TAILQ_REMOVE(&canp->canp_table->canpt_queue, canp, canp_queue);
sofree(so); /* sofree drops the softnet_lock */
canp_unref(canp);
mutex_enter(softnet_lock);
}
void
canp_ref(struct canpcb *canp)
{
KASSERT(mutex_owned(&canp->canp_mtx));
canp->canp_refcount++;
}
void
canp_unref(struct canpcb *canp)
{
mutex_enter(&canp->canp_mtx);
canp->canp_refcount--;
KASSERT(canp->canp_refcount >= 0);
if (canp->canp_refcount > 0) {
mutex_exit(&canp->canp_mtx);
return;
}
mutex_exit(&canp->canp_mtx);
mutex_destroy(&canp->canp_mtx);
pool_put(&canpcb_pool, canp);
}
void
can_setsockaddr(struct canpcb *canp, struct sockaddr_can *scan)
{
mutex_enter(&canp->canp_mtx);
memset(scan, 0, sizeof (*scan));
scan->can_family = AF_CAN;
scan->can_len = sizeof(*scan);
scan->can_ifindex = canp->canp_ifp->if_index;
mutex_exit(&canp->canp_mtx);
}
int
can_pcbsetfilter(struct canpcb *canp, struct can_filter *fp, int nfilters)
{
struct can_filter *newf;
KASSERT(mutex_owned(&canp->canp_mtx));
if (nfilters > 0) {
newf =
kmem_alloc(sizeof(struct can_filter) * nfilters, KM_SLEEP);
if (newf == NULL)
return ENOMEM;
memcpy(newf, fp, sizeof(struct can_filter) * nfilters);
} else {
newf = NULL;
}
if (canp->canp_filters != NULL) {
kmem_free(canp->canp_filters,
sizeof(struct can_filter) * canp->canp_nfilters);
}
canp->canp_filters = newf;
canp->canp_nfilters = nfilters;
return 0;
}
#if 0
/*
* Pass some notification to all connections of a protocol
* associated with address dst. The local address and/or port numbers
* may be specified to limit the search. The "usual action" will be
* taken, depending on the ctlinput cmd. The caller must filter any
* cmds that are uninteresting (e.g., no error in the map).
* Call the protocol specific routine (if any) to report
* any errors for each matching socket.
*
* Must be called at splsoftnet.
*/
int
can_pcbnotify(struct canpcbtable *table, u_int32_t faddr, u_int32_t laddr,
int errno, void (*notify)(struct canpcb *, int))
{
struct canpcbhead *head;
struct canpcb *canp, *ncanp;
int nmatch;
if (faddr == 0 || notify == 0)
return (0);
nmatch = 0;
head = CANPCBHASH_CONNECT(table, faddr, laddr);
for (canp = LIST_FIRST(head); canp != NULL; canp = ncanp) {
ncanp = LIST_NEXT(canp, canp_hash);
if (canp->canp_faddr == faddr &&
canp->canp_laddr == laddr) {
(*notify)(canp, errno);
nmatch++;
}
}
return (nmatch);
}
void
can_pcbnotifyall(struct canpcbtable *table, u_int32_t faddr, int errno,
void (*notify)(struct canpcb *, int))
{
struct canpcb *canp, *ncanp;
if (faddr == 0 || notify == 0)
return;
TAILQ_FOREACH_SAFE(canp, &table->canpt_queue, canp_queue, ncanp) {
if (canp->canp_faddr == faddr)
(*notify)(canp, errno);
}
}
#endif
#if 0
void
can_pcbpurgeif0(struct canpcbtable *table, struct ifnet *ifp)
{
struct canpcb *canp, *ncanp;
struct ip_moptions *imo;
int i, gap;
}
void
can_pcbpurgeif(struct canpcbtable *table, struct ifnet *ifp)
{
struct canpcb *canp, *ncanp;
for (canp = CIRCLEQ_FIRST(&table->canpt_queue);
canp != (void *)&table->canpt_queue;
canp = ncanp) {
ncanp = CIRCLEQ_NEXT(canp, canp_queue);
}
}
#endif
void
can_pcbstate(struct canpcb *canp, int state)
{
int ifindex = canp->canp_ifp ? canp->canp_ifp->if_index : 0;
KASSERT(mutex_owned(&canp->canp_mtx));
if (canp->canp_state > CANP_ATTACHED)
LIST_REMOVE(canp, canp_hash);
switch (state) {
case CANP_BOUND:
LIST_INSERT_HEAD(CANPCBHASH_BIND(canp->canp_table,
ifindex), canp, canp_hash);
break;
case CANP_CONNECTED:
LIST_INSERT_HEAD(CANPCBHASH_CONNECT(canp->canp_table,
ifindex), canp, canp_hash);
break;
}
canp->canp_state = state;
}
/*
* check mbuf against socket accept filter.
* returns true if mbuf is accepted, false otherwise
*/
bool
can_pcbfilter(struct canpcb *canp, struct mbuf *m)
{
int i;
struct can_frame *fmp;
struct can_filter *fip;
KASSERT(mutex_owned(&canp->canp_mtx));
KASSERT((m->m_flags & M_PKTHDR) != 0);
KASSERT(m->m_len == m->m_pkthdr.len);
fmp = mtod(m, struct can_frame *);
for (i = 0; i < canp->canp_nfilters; i++) {
fip = &canp->canp_filters[i];
if ((fmp->can_id & fip->can_mask) == fip->can_id)
return true;
}
/* no match */
return false;
}

113
sys/netcan/can_pcb.h Normal file
View File

@ -0,0 +1,113 @@
/* $NetBSD: can_pcb.h,v 1.2 2017/05/27 21:02:56 bouyer Exp $ */
/*-
* Copyright (c) 2003, 2017 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Robert Swindells and Manuel Bouyer
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _NETCAN_CAN_PCB_H_
#define _NETCAN_CAN_PCB_H_
#include <sys/queue.h>
/*
* Common structure pcb for can protocol implementation.
* Here are stored pointers to local and foreign host table
* entries, local and foreign socket numbers, and pointers
* up (to a socket structure) and down (to a protocol-specific)
* control block.
*/
struct canpcbpolicy;
struct canpcb {
LIST_ENTRY(canpcb) canp_hash;
LIST_ENTRY(canpcb) canp_lhash;
TAILQ_ENTRY(canpcb) canp_queue;
kmutex_t canp_mtx; /* protect states and refcount */
int canp_state;
int canp_flags;
struct socket *canp_socket; /* back pointer to socket */
struct ifnet *canp_ifp; /* interface this socket is bound to */
struct canpcbtable *canp_table;
struct can_filter *canp_filters; /* filter array */
int canp_nfilters; /* size of canp_filters */
int canp_refcount;
};
LIST_HEAD(canpcbhead, canpcb);
TAILQ_HEAD(canpcbqueue, canpcb);
struct canpcbtable {
struct canpcbqueue canpt_queue;
struct canpcbhead *canpt_bindhashtbl;
struct canpcbhead *canpt_connecthashtbl;
u_long canpt_bindhash;
u_long canpt_connecthash;
};
/* states in canp_state: */
#define CANP_DETACHED 0
#define CANP_ATTACHED 1
#define CANP_BOUND 2
#define CANP_CONNECTED 3
/* flags in canp_flags: */
#define CANP_NO_LOOPBACK 0x0001 /* local loopback disabled */
#define CANP_RECEIVE_OWN 0x0002 /* receive own message */
#define sotocanpcb(so) ((struct canpcb *)(so)->so_pcb)
#ifdef _KERNEL
void can_losing(struct canpcb *);
int can_pcballoc (struct socket *, void *);
int can_pcbbind(void *, struct sockaddr_can *, struct lwp *);
int can_pcbconnect(void *, struct sockaddr_can *);
void can_pcbdetach(void *);
void can_pcbdisconnect(void *);
void can_pcbinit(struct canpcbtable *, int, int);
int can_pcbnotify(struct canpcbtable *, u_int32_t,
u_int32_t, int, void (*)(struct canpcb *, int));
void can_pcbnotifyall(struct canpcbtable *, u_int32_t, int,
void (*)(struct canpcb *, int));
void can_pcbpurgeif0(struct canpcbtable *, struct ifnet *);
void can_pcbpurgeif(struct canpcbtable *, struct ifnet *);
void can_pcbstate(struct canpcb *, int);
void can_setsockaddr(struct canpcb *, struct sockaddr_can *);
int can_pcbsetfilter(struct canpcb *, struct can_filter *, int);
bool can_pcbfilter(struct canpcb *, struct mbuf *);
/* refcount management */
void canp_ref(struct canpcb *);
void canp_unref(struct canpcb *);
#endif
#endif /* _NETCAN_CAN_PCB_H_ */

76
sys/netcan/can_proto.c Normal file
View File

@ -0,0 +1,76 @@
/* $NetBSD: can_proto.c,v 1.2 2017/05/27 21:02:56 bouyer Exp $ */
/*-
* Copyright (c) 2003, 2017 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Robert Swindells and Manuel Bouyer
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: can_proto.c,v 1.2 2017/05/27 21:02:56 bouyer Exp $");
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/protosw.h>
#include <sys/domain.h>
#include <sys/mbuf.h>
#include <net/if.h>
#include <net/radix.h>
#include <net/route.h>
/*
* CAN protocol family
*/
#include <netcan/can.h>
#include <netcan/can_var.h>
DOMAIN_DEFINE(candomain); /* forward declare and add to link set */
const struct protosw cansw[] = {
{
.pr_type = SOCK_RAW,
.pr_domain = &candomain,
.pr_init = can_init,
.pr_flags = PR_ATOMIC|PR_ADDR,
.pr_usrreqs = &can_usrreqs,
.pr_ctloutput = &can_ctloutput,
}
};
struct domain candomain = {
.dom_family = PF_CAN,
.dom_name = "can",
.dom_init = can_init,
.dom_externalize = NULL, .dom_dispose = NULL,
.dom_protosw = cansw,
.dom_protoswNPROTOSW = &cansw[__arraycount(cansw)],
.dom_ifqueues = { &canintrq, NULL },
.dom_link = { NULL },
.dom_mowner = MOWNER_INIT("",""),
.dom_sa_cmpofs = offsetof(struct sockaddr_can, can_ifindex),
.dom_sa_cmplen = sizeof(int)
};

75
sys/netcan/can_var.h Normal file
View File

@ -0,0 +1,75 @@
/* $NetBSD: can_var.h,v 1.2 2017/05/27 21:02:56 bouyer Exp $ */
/*-
* Copyright (c) 2003, 2017 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Robert Swindells and Manuel Bouyer
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _NETCAN_CAN_VAR_H_
#define _NETCAN_CAN_VAR_H_
#include <sys/queue.h>
#include <sys/device.h>
#include <netcan/can_link.h>
struct can_ifreq {
char cfr_name[IFNAMSIZ]; /* if name, e.g. "sja0" */
};
#ifdef _KERNEL
#include <sys/socketvar.h>
/*
* common structure for CAN interface drivers. Should be at the start of
* each driver's softc.
*/
struct canif_softc {
device_t csc_dev;
struct can_link_timecaps csc_timecaps; /* timing capabilities */
struct can_link_timings csc_timings; /* operating timing values */
uint32_t csc_linkmodes;
};
extern struct ifqueue canintrq;
extern struct domain candomain;
extern const struct pr_usrreqs can_usrreqs;
void can_ifattach(struct ifnet *);
void can_ifdetach(struct ifnet *);
void can_ifinit_timings(struct canif_softc *);
void can_mbuf_tag_clean(struct mbuf *);
void can_input(struct ifnet *, struct mbuf *);
void *can_ctlinput(int, struct sockaddr *, void *);
int can_ctloutput(int, struct socket *, struct sockopt *);
void can_init(void);
void canintr(void);
void can_bpf_mtap(struct ifnet *, struct mbuf *, bool);
#endif
#endif

8
sys/netcan/files.netcan Normal file
View File

@ -0,0 +1,8 @@
# $NetBSD: files.netcan,v 1.2 2017/05/27 21:02:56 bouyer Exp $
defflag opt_can.h CAN # ISO-15765 CAN network stack
file netcan/can.c can
file netcan/can_proto.c can
file netcan/can_pcb.c can
file netcan/if_canloop.c canloop

220
sys/netcan/if_canloop.c Normal file
View File

@ -0,0 +1,220 @@
/* $NetBSD: if_canloop.c,v 1.2 2017/05/27 21:02:56 bouyer Exp $ */
/*-
* Copyright (c) 2017 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Manuel Bouyer.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Loopback interface driver for the CAN protocol
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: if_canloop.c,v 1.2 2017/05/27 21:02:56 bouyer Exp $");
#ifdef _KERNEL_OPT
#include "opt_can.h"
#include "opt_net_mpsafe.h"
#endif
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/device.h>
#include <sys/module.h>
#include <sys/cpu.h>
#include <net/if.h>
#include <net/if_types.h>
#include <net/netisr.h>
#ifdef CAN
#include <netcan/can.h>
#endif
void canloopattach(int);
void canloopinit(void);
static int canloop_clone_create(struct if_clone *, int);
static int canloop_clone_destroy(struct ifnet *);
static int canloop_ioctl(struct ifnet *, u_long, void *);
static void canloop_ifstart(struct ifnet *);
static int canloop_count;
static struct if_clone canloop_cloner =
IF_CLONE_INITIALIZER("canlo", canloop_clone_create, canloop_clone_destroy);
void
canloopattach(int n)
{
/*
* Nothing to do here, initialization is handled by the
* module initialization code in canloopinit() below).
*/
}
void
canloopinit(void)
{
canloop_count = 0;
if_clone_attach(&canloop_cloner);
}
static int
canloopdetach(void)
{
if (canloop_count > 0)
return EBUSY;
if_clone_detach(&canloop_cloner);
return 0;
}
static int
canloop_clone_create(struct if_clone *ifc, int unit)
{
struct ifnet *ifp;
ifp = if_alloc(IFT_OTHER);
if_initname(ifp, ifc->ifc_name, unit);
ifp->if_flags = IFF_LOOPBACK | IFF_RUNNING;
ifp->if_extflags = IFEF_OUTPUT_MPSAFE;
ifp->if_ioctl = canloop_ioctl;
ifp->if_start = canloop_ifstart;
can_ifattach(ifp);
#ifdef MBUFTRACE
ifp->if_mowner = malloc(sizeof(struct mowner), M_DEVBUF,
M_WAITOK | M_ZERO);
strlcpy(ifp->if_mowner->mo_name, ifp->if_xname,
sizeof(ifp->if_mowner->mo_name));
MOWNER_ATTACH(ifp->if_mowner);
#endif
canloop_count++;
return (0);
}
static int
canloop_clone_destroy(struct ifnet *ifp)
{
#ifdef MBUFTRACE
MOWNER_DETACH(ifp->if_mowner);
free(ifp->if_mowner, M_DEVBUF);
#endif
can_ifdetach(ifp);
if_free(ifp);
canloop_count--;
KASSERT(canloop_count >= 0);
return (0);
}
static void
canloop_ifstart(struct ifnet *ifp)
{
size_t pktlen;
struct mbuf *m;
KERNEL_LOCK(1, NULL);
while (true) {
IF_DEQUEUE(&ifp->if_snd, m);
if (m == NULL)
break;
MCLAIM(m, ifp->if_mowner);
if ((m->m_flags & M_PKTHDR) == 0)
panic("canloop_output: no header mbuf");
m_set_rcvif(m, ifp);
if (ifp->if_flags & IFF_LOOPBACK)
can_bpf_mtap(ifp, m, 0);
pktlen = m->m_pkthdr.len;
ifp->if_opackets++;
ifp->if_obytes += pktlen;
#ifdef CAN
can_mbuf_tag_clean(m);
can_input(ifp, m);
#else
printf("%s: can't handle CAN packet\n", ifp->if_xname);
m_freem(m);
#endif
}
KERNEL_UNLOCK_ONE(NULL);
}
/*
* Process an ioctl request.
*/
/* ARGSUSED */
static int
canloop_ioctl(struct ifnet *ifp, u_long cmd, void *data)
{
struct ifreq *ifr = data;
int error = 0;
switch (cmd) {
case SIOCINITIFADDR:
error = EAFNOSUPPORT;
break;
case SIOCSIFMTU:
if ((unsigned)ifr->ifr_mtu != sizeof(struct can_frame))
error = EINVAL;
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
error = EAFNOSUPPORT;
break;
default:
error = ifioctl_common(ifp, cmd, data);
}
return (error);
}
/*
* Module infrastructure
*/
#include "../net/if_module.h"
IF_MODULE(MODULE_CLASS_DRIVER, canloop, "")

View File

@ -1,4 +1,4 @@
/* $NetBSD: opt_rumpkernel.h,v 1.5 2016/04/15 01:35:26 ozaki-r Exp $ */
/* $NetBSD: opt_rumpkernel.h,v 1.6 2017/05/27 21:02:56 bouyer Exp $ */
#ifndef __NetBSD__
#define __NetBSD__
@ -22,6 +22,8 @@
#define MPLS 1
#define CAN 1
#define SOSEND_NO_LOAN
#undef PIPE_SOCKETPAIR /* would need uipc_usrreq.c */

View File

@ -1,4 +1,4 @@
/* $NetBSD: rumpdefs.h,v 1.36 2016/02/02 01:15:58 pooka Exp $ */
/* $NetBSD: rumpdefs.h,v 1.37 2017/05/27 21:02:56 bouyer Exp $ */
/*
* AUTOMATICALLY GENERATED. DO NOT EDIT.
@ -240,7 +240,8 @@ enum rump_vtype { RUMP_VNON, RUMP_VREG, RUMP_VDIR, RUMP_VBLK, RUMP_VCHR, RUMP_VL
#define RUMP_AF_IEEE80211 32
#define RUMP_AF_MPLS 33
#define RUMP_AF_ROUTE 34
#define RUMP_AF_MAX 35
#define RUMP_AF_CAN 35
#define RUMP_AF_MAX 36
#define RUMP_PF_UNSPEC RUMP_AF_UNSPEC
#define RUMP_PF_LOCAL RUMP_AF_LOCAL
#define RUMP_PF_UNIX RUMP_PF_LOCAL
@ -277,6 +278,7 @@ enum rump_vtype { RUMP_VNON, RUMP_VREG, RUMP_VDIR, RUMP_VBLK, RUMP_VCHR, RUMP_VL
#define RUMP_PF_BLUETOOTH RUMP_AF_BLUETOOTH
#define RUMP_PF_MPLS RUMP_AF_MPLS
#define RUMP_PF_ROUTE RUMP_AF_ROUTE
#define RUMP_PF_CAN RUMP_AF_CAN
#define RUMP_PF_MAX RUMP_AF_MAX
#define RUMP_SO_DEBUG 0x0001
#define RUMP_SO_ACCEPTCONN 0x0002

View File

@ -1,9 +1,9 @@
# $NetBSD: Makefile.rumpnetcomp,v 1.18 2017/04/14 02:43:28 ozaki-r Exp $
# $NetBSD: Makefile.rumpnetcomp,v 1.19 2017/05/27 21:02:56 bouyer Exp $
#
.include <bsd.own.mk>
RUMPNETCOMP= agr bridge net net80211 netbt netinet netinet6 netipsec
RUMPNETCOMP= agr bridge net net80211 netbt netcan netinet netinet6 netipsec
RUMPNETCOMP+= gif netmpls npf l2tp local pppoe shmif tap tun vlan
.if ${MKSLJIT} != "no" || make(rumpdescribe)

View File

@ -0,0 +1,20 @@
# $NetBSD: Makefile,v 1.2 2017/05/27 21:02:56 bouyer Exp $
#
NOLINT= #defined
.include <bsd.own.mk>
.PATH: ${.CURDIR}/../../../../netcan
LIB= rumpnet_netcan
COMMENT=CAN (PF_CAN)
IOCONF= NETCAN.ioconf
SRCS= can.c can_pcb.c can_proto.c if_canloop.c
SRCS+= netcan_component.c
.include <bsd.lib.mk>
.include <bsd.klinks.mk>

View File

@ -0,0 +1,7 @@
# $NetBSD: NETCAN.ioconf,v 1.2 2017/05/27 21:02:56 bouyer Exp $
ioconf netcan
include "conf/files"
pseudo-device canloop

View File

@ -0,0 +1,52 @@
/* $NetBSD: netcan_component.c,v 1.2 2017/05/27 21:02:56 bouyer Exp $ */
/*
* Copyright (c) 2010 Antti Kantee. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: netcan_component.c,v 1.2 2017/05/27 21:02:56 bouyer Exp $");
#include <sys/param.h>
#include <sys/domain.h>
#include <sys/protosw.h>
#include <rump-sys/kern.h>
#include <rump-sys/net.h>
#include <net/netisr.h>
#include <netcan/can_var.h>
RUMP_COMPONENT(RUMP_COMPONENT_NET)
{
extern struct domain candomain;
domain_attach(&candomain);
rump_netisr_register(NETISR_CAN, canintr);
}
void canloopinit(void);
RUMP_COMPONENT(RUMP_COMPONENT_NET_IF)
{
canloopinit();
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: mbuf.h,v 1.169 2017/03/31 06:49:44 ozaki-r Exp $ */
/* $NetBSD: mbuf.h,v 1.170 2017/05/27 21:02:56 bouyer Exp $ */
/*-
* Copyright (c) 1996, 1997, 1999, 2001, 2007 The NetBSD Foundation, Inc.
@ -897,6 +897,7 @@ struct m_tag *m_tag_next(struct mbuf *, struct m_tag *);
#define PACKET_TAG_VLAN 1 /* VLAN ID */
#define PACKET_TAG_ENCAP 2 /* encapsulation data */
#define PACKET_TAG_ESP 3 /* ESP information */
#define PACKET_TAG_SO 4 /* sending socket pointer */
#define PACKET_TAG_PF 11 /* packet filter */
#define PACKET_TAG_ALTQ_QID 12 /* ALTQ queue id */

View File

@ -1,4 +1,4 @@
/* $NetBSD: socket.h,v 1.121 2017/02/08 17:58:41 maya Exp $ */
/* $NetBSD: socket.h,v 1.122 2017/05/27 21:02:56 bouyer Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@ -221,7 +221,8 @@ struct accept_filter_arg {
#define AF_IEEE80211 32 /* IEEE80211 */
#define AF_MPLS 33 /* MultiProtocol Label Switching */
#define AF_ROUTE 34 /* Internal Routing Protocol */
#define AF_MAX 35
#define AF_CAN 35
#define AF_MAX 36
/*
* Structure used by kernel to store most
@ -330,6 +331,7 @@ struct sockaddr_storage {
#define PF_BLUETOOTH AF_BLUETOOTH
#define PF_MPLS AF_MPLS
#define PF_ROUTE AF_ROUTE
#define PF_CAN AF_CAN
#define PF_MAX AF_MAX

View File

@ -1,4 +1,4 @@
# $NetBSD: Makefile,v 1.32 2017/04/14 02:56:48 ozaki-r Exp $
# $NetBSD: Makefile,v 1.33 2017/05/27 21:02:56 bouyer Exp $
.include <bsd.own.mk>
@ -6,9 +6,9 @@ TESTSDIR= ${TESTSBASE}/net
TESTS_SUBDIRS= fdpass in_cksum net sys
.if (${MKRUMP} != "no") && !defined(BSD_MK_COMPAT_FILE)
TESTS_SUBDIRS+= arp bpf bpfilter carp icmp if if_bridge if_gif if_l2tp
TESTS_SUBDIRS+= if_loop if_pppoe if_tap if_tun ipsec mcast mpls ndp npf
TESTS_SUBDIRS+= route if_vlan
TESTS_SUBDIRS+= arp bpf bpfilter can carp icmp if if_bridge if_gif
TESTS_SUBDIRS+= if_l2tp if_loop if_pppoe if_tap if_tun ipsec
TESTS_SUBDIRS+= mcast mpls ndp npf route if_vlan
.if (${MKSLJIT} != "no")
TESTS_SUBDIRS+= bpfjit
.endif

21
tests/net/can/Makefile Normal file
View File

@ -0,0 +1,21 @@
# $NetBSD: Makefile,v 1.2 2017/05/27 21:02:56 bouyer Exp $
#
.include <bsd.own.mk>
TESTSDIR= ${TESTSBASE}/net/can
TESTS_C= t_can t_canfilter
SRCS.t_can= t_can.c h_canutils.c
SRCS.t_canfilter= t_canfilter.c h_canutils.c
# XXX we don't use INET here, but we need rumpnet_netinet anyway:
# common code in if.c is compiled with -DINET and will dereference ip_pktq,
# which is NULL if rumpnet_netinet is not inclued.
#
LDADD+= -lrumpnet_netcan -lrumpnet_netinet -lrumpnet_net -lrumpnet
LDADD+= -lrump -lrumpuser -lrump -lpthread -lrumpdev -lrumpvfs
.include <bsd.test.mk>

200
tests/net/can/h_canutils.c Normal file
View File

@ -0,0 +1,200 @@
/* $NetBSD: h_canutils.c,v 1.2 2017/05/27 21:02:56 bouyer Exp $ */
/*-
* Copyright (c) 2017 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Manuel Bouyer
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
* CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: h_canutils.c,v 1.2 2017/05/27 21:02:56 bouyer Exp $");
#endif /* not lint */
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <sys/sockio.h>
#include <sys/param.h>
#include <atf-c.h>
#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <netcan/can.h>
#include <rump/rump.h>
#include <rump/rump_syscalls.h>
#include "h_macros.h"
#include "h_canutils.h"
void
cancfg_rump_createif(const char *ifname)
{
int s, rv;
struct ifreq ifr;
s = -1;
if ((s = rump_sys_socket(AF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
atf_tc_fail_errno("if config socket");
}
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
if ((rv = rump_sys_ioctl(s, SIOCIFCREATE, &ifr)) < 0) {
atf_tc_fail_errno("if config create");
}
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
if ((rv = rump_sys_ioctl(s, SIOCGIFFLAGS, &ifr)) < 0) {
atf_tc_fail_errno("if config get flags");
}
ifr.ifr_flags |= IFF_UP;
if ((rv = rump_sys_ioctl(s, SIOCSIFFLAGS, &ifr)) < 0) {
atf_tc_fail_errno("if config set flags");
}
}
int
can_recvfrom(int s, struct can_frame *cf, int *len, struct sockaddr_can *sa)
{
socklen_t salen;
fd_set rfds;
struct timeval tmout;
int rv;
memset(cf, 0, sizeof(struct can_frame));
FD_ZERO(&rfds);
FD_SET(s, &rfds);
/* wait 1 second for the message (in some tests we expect no message) */
tmout.tv_sec = 1;
tmout.tv_usec = 0;
rv = rump_sys_select(s + 1, &rfds, NULL, NULL, &tmout);
switch(rv) {
case -1:
atf_tc_fail_errno("select");
/* NOTREACHED */
case 0:
/* timeout */
errno = EWOULDBLOCK;
return -1;
default: break;
}
ATF_CHECK_MSG(FD_ISSET(s, &rfds), "select returns but s not in set");
salen = sizeof(struct sockaddr_can);
if (( *len = rump_sys_recvfrom(s, cf, sizeof(struct can_frame),
0, (struct sockaddr *)sa, &salen)) < 0) {
atf_tc_fail_errno("recvfrom");
}
ATF_CHECK_MSG(rv > 0, "short read on socket");
ATF_CHECK_MSG(sa->can_family == AF_CAN,
"recvfrom provided wrong %d family", sa->can_family);
ATF_CHECK_MSG(salen == sizeof(struct sockaddr_can),
"recvfrom provided wrong size %d (!= %ld)", salen, sizeof(sa));
return 0;
}
int
can_read(int s, struct can_frame *cf, int *len)
{
fd_set rfds;
struct timeval tmout;
int rv;
memset(cf, 0, sizeof(struct can_frame));
FD_ZERO(&rfds);
FD_SET(s, &rfds);
/* wait 1 second for the message (in some tests we expect no message) */
tmout.tv_sec = 1;
tmout.tv_usec = 0;
rv = rump_sys_select(s + 1, &rfds, NULL, NULL, &tmout);
switch(rv) {
case -1:
atf_tc_fail_errno("select");
/* NOTREACHED */
case 0:
/* timeout */
errno = EWOULDBLOCK;
return -1;
default: break;
}
ATF_CHECK_MSG(FD_ISSET(s, &rfds), "select returns but s not in set");
if (( *len = rump_sys_read(s, cf, sizeof(struct can_frame))) < 0) {
atf_tc_fail_errno("read");
}
ATF_CHECK_MSG(rv > 0, "short read on socket");
return 0;
}
int
can_bind(int s, const char *ifname)
{
struct ifreq ifr;
struct sockaddr_can sa;
strcpy(ifr.ifr_name, ifname );
if (rump_sys_ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
atf_tc_fail_errno("SIOCGIFINDEX");
}
ATF_CHECK_MSG(ifr.ifr_ifindex > 0, "%s index is %d (not > 0)",
ifname, ifr.ifr_ifindex);
sa.can_family = AF_CAN;
sa.can_ifindex = ifr.ifr_ifindex;
if (rump_sys_bind(s, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
atf_tc_fail_errno("bind");
}
return ifr.ifr_ifindex;
}
int
can_socket_with_own(void)
{
int s;
int v;
if ((s = rump_sys_socket(AF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
atf_tc_fail_errno("CAN socket");
}
v = 1;
if (rump_sys_setsockopt(s, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS,
&v, sizeof(v)) < 0) {
atf_tc_fail_errno("setsockopt(CAN_RAW_RECV_OWN_MSGS)");
}
return s;
}

View File

@ -0,0 +1,37 @@
/* $NetBSD: h_canutils.h,v 1.2 2017/05/27 21:02:56 bouyer Exp $ */
/*-
* Copyright (c) 2017 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Manuel Bouyer
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
* CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
void cancfg_rump_createif(const char *);
int can_recvfrom(int, struct can_frame *, int *, struct sockaddr_can *);
int can_read(int, struct can_frame *, int *);
int can_bind(int s, const char *);
int can_socket_with_own(void);

734
tests/net/can/t_can.c Normal file
View File

@ -0,0 +1,734 @@
/* $NetBSD: t_can.c,v 1.2 2017/05/27 21:02:56 bouyer Exp $ */
/*-
* Copyright (c) 2017 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Manuel Bouyer
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
* CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: t_can.c,v 1.2 2017/05/27 21:02:56 bouyer Exp $");
#endif /* not lint */
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <sys/sockio.h>
#include <sys/param.h>
#include <atf-c.h>
#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <netcan/can.h>
#include <rump/rump.h>
#include <rump/rump_syscalls.h>
#include "h_macros.h"
#include "h_canutils.h"
ATF_TC(canlocreate);
ATF_TC_HEAD(canlocreate, tc)
{
atf_tc_set_md_var(tc, "descr", "check CAN loopback create/destroy");
atf_tc_set_md_var(tc, "timeout", "5");
}
ATF_TC_BODY(canlocreate, tc)
{
const char ifname[] = "canlo0";
int s, rv;
struct ifreq ifr;
rump_init();
cancfg_rump_createif(ifname);
s = -1;
if ((s = rump_sys_socket(AF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
atf_tc_fail_errno("if config socket(2)");
}
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
if ((rv = rump_sys_ioctl(s, SIOCIFDESTROY, &ifr)) < 0) {
atf_tc_fail_errno("if config destroy");
}
}
ATF_TC(cannoown);
ATF_TC_HEAD(cannoown, tc)
{
atf_tc_set_md_var(tc, "descr", "check that CAN sockets don't gets its own message");
atf_tc_set_md_var(tc, "timeout", "5");
}
ATF_TC_BODY(cannoown, tc)
{
const char ifname[] = "canlo0";
int s, rv, v;
socklen_t vlen;
struct sockaddr_can sa;
int ifindex;
struct can_frame cf_send, cf_receive;
rump_init();
cancfg_rump_createif(ifname);
if ((s = rump_sys_socket(AF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
atf_tc_fail_errno("CAN socket");
}
ifindex = can_bind(s, ifname);
/* check sockopt CAN_RAW_LOOPBACK */
vlen = sizeof(v);
if (rump_sys_getsockopt(s, SOL_CAN_RAW, CAN_RAW_LOOPBACK,
&v, &vlen) < 0) {
atf_tc_fail_errno("getsockopt(CAN_RAW_LOOPBACK)");
}
ATF_CHECK_MSG(vlen == sizeof(v), "getsockopt(CAN_RAW_LOOPBACK) returns wrong len %d", vlen);
ATF_CHECK_MSG(v == 1, "CAN_RAW_LOOPBACK is not on by default");
/* check sockopt CAN_RAW_RECV_OWN_MSGS, and set it */
vlen = sizeof(v);
if (rump_sys_getsockopt(s, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS,
&v, &vlen) < 0) {
atf_tc_fail_errno("getsockopt(CAN_RAW_RECV_OWN_MSGS)");
}
ATF_CHECK_MSG(vlen == sizeof(v), "getsockopt(CAN_RAW_RECV_OWN_MSGS) returns wrong len %d", vlen);
ATF_CHECK_MSG(v == 0, "CAN_RAW_RECV_OWN_MSGS is not off by default");
/*
* send a single byte message, but make sure remaining payload is
* not 0.
*/
memset(&cf_send, 0, sizeof(cf_send));
cf_send.can_id = 1;
cf_send.can_dlc = 1;
cf_send.data[0] = 0xde;
cf_send.data[1] = 0xad;
cf_send.data[2] = 0xbe;
cf_send.data[3] = 0xef;
if (rump_sys_write(s, &cf_send, sizeof(cf_send) - 7) < 0) {
atf_tc_fail_errno("write");
}
/* now try to read */
if (can_recvfrom(s, &cf_receive, &rv, &sa) < 0) {
if (errno == EWOULDBLOCK)
return; /* expected timeout */
atf_tc_fail_errno("can_recvfrom");
}
ATF_CHECK_MSG(sa.can_ifindex == ifindex,
"recvfrom provided wrong ifindex %d (!= %d)",
sa.can_ifindex, ifindex);
atf_tc_fail("we got our own message");
}
ATF_TC(canwritelo);
ATF_TC_HEAD(canwritelo, tc)
{
atf_tc_set_md_var(tc, "descr", "check that CAN sockets gets its own message via write");
atf_tc_set_md_var(tc, "timeout", "5");
}
ATF_TC_BODY(canwritelo, tc)
{
const char ifname[] = "canlo0";
int s, rv, v;
socklen_t vlen;
struct can_frame cf_send, cf_receive;
rump_init();
cancfg_rump_createif(ifname);
s = can_socket_with_own();
can_bind(s, ifname);
/* check sockopt CAN_RAW_LOOPBACK */
vlen = sizeof(v);
if (rump_sys_getsockopt(s, SOL_CAN_RAW, CAN_RAW_LOOPBACK,
&v, &vlen) < 0) {
atf_tc_fail_errno("getsockopt(CAN_RAW_LOOPBACK)");
}
ATF_CHECK_MSG(vlen == sizeof(v), "getsockopt(CAN_RAW_LOOPBACK) returns wrong len %d", vlen);
ATF_CHECK_MSG(v == 1, "CAN_RAW_LOOPBACK is not on by default");
/* check that sockopt CAN_RAW_RECV_OWN_MSGS is on */
vlen = sizeof(v);
if (rump_sys_getsockopt(s, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS,
&v, &vlen) < 0) {
atf_tc_fail_errno("getsockopt(CAN_RAW_RECV_OWN_MSGS)");
}
ATF_CHECK_MSG(v == 1, "CAN_RAW_RECV_OWN_MSGS is not on");
/*
* send a single byte message, but make sure remaining payload is
* not 0.
*/
memset(&cf_send, 0, sizeof(cf_send));
cf_send.can_id = 1;
cf_send.can_dlc = 1;
cf_send.data[0] = 0xde;
cf_send.data[1] = 0xad;
cf_send.data[2] = 0xbe;
cf_send.data[3] = 0xef;
if (rump_sys_write(s, &cf_send, sizeof(cf_send) - 7) < 0) {
atf_tc_fail_errno("write");
}
if (can_read(s, &cf_receive, &rv) < 0) {
atf_tc_fail_errno("can_read");
}
memset(&cf_send, 0, sizeof(cf_send));
cf_send.can_id = 1;
cf_send.can_dlc = 1;
cf_send.data[0] = 0xde;
/* other data[] are expected to be 0 */
ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive, sizeof(cf_send)) == 0,
"received packet is not what we sent");
}
ATF_TC(canwriteunbound);
ATF_TC_HEAD(canwriteunbound, tc)
{
atf_tc_set_md_var(tc, "descr", "check that write to unbound CAN sockets fails");
atf_tc_set_md_var(tc, "timeout", "5");
}
ATF_TC_BODY(canwriteunbound, tc)
{
const char ifname[] = "canlo0";
int s, rv;
struct can_frame cf_send;
rump_init();
cancfg_rump_createif(ifname);
s = -1;
if ((s = rump_sys_socket(AF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
atf_tc_fail_errno("CAN socket");
}
/*
* send a single byte message.
* not 0.
*/
memset(&cf_send, 0, sizeof(cf_send));
cf_send.can_id = 1;
cf_send.can_dlc = 1;
cf_send.data[0] = 0xde;
rv = rump_sys_write(s, &cf_send, sizeof(cf_send) - 7);
ATF_CHECK_MSG(rv < 0 && errno == EDESTADDRREQ,
"write to unbound socket didn't fail");
}
ATF_TC(cansendtolo);
ATF_TC_HEAD(cansendtolo, tc)
{
atf_tc_set_md_var(tc, "descr", "check that CAN sockets gets its own message via sendto");
atf_tc_set_md_var(tc, "timeout", "5");
}
ATF_TC_BODY(cansendtolo, tc)
{
const char ifname[] = "canlo0";
int s, rv;
struct sockaddr_can sa;
struct ifreq ifr;
struct can_frame cf_send, cf_receive;
rump_init();
cancfg_rump_createif(ifname);
s = can_socket_with_own();
strcpy(ifr.ifr_name, ifname );
if ((rv = rump_sys_ioctl(s, SIOCGIFINDEX, &ifr)) < 0) {
atf_tc_fail_errno("SIOCGIFINDEX");
}
ATF_CHECK_MSG(ifr.ifr_ifindex > 0, "%s index is %d (not > 0)",
ifname, ifr.ifr_ifindex);
sa.can_family = AF_CAN;
sa.can_ifindex = ifr.ifr_ifindex;
/*
* send a single byte message, but make sure remaining payload is
* not 0.
*/
memset(&cf_send, 0, sizeof(cf_send));
cf_send.can_id = 1;
cf_send.can_dlc = 1;
cf_send.data[0] = 0xde;
cf_send.data[1] = 0xad;
cf_send.data[2] = 0xbe;
cf_send.data[3] = 0xef;
if (rump_sys_sendto(s, &cf_send, sizeof(cf_send) - 7,
0, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
atf_tc_fail_errno("sendto");
}
if (can_read(s, &cf_receive, &rv) < 0) {
atf_tc_fail_errno("read");
}
memset(&cf_send, 0, sizeof(cf_send));
cf_send.can_id = 1;
cf_send.can_dlc = 1;
cf_send.data[0] = 0xde;
/* other data[] are expected to be 0 */
ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive, sizeof(cf_send)) == 0,
"received packet is not what we sent");
}
ATF_TC(cansendtowrite);
ATF_TC_HEAD(cansendtowrite, tc)
{
atf_tc_set_md_var(tc, "descr", "check that write after sendto on unbound socket fails");
atf_tc_set_md_var(tc, "timeout", "5");
}
ATF_TC_BODY(cansendtowrite, tc)
{
const char ifname[] = "canlo0";
int s, rv;
struct sockaddr_can sa;
struct ifreq ifr;
struct can_frame cf_send, cf_receive;
rump_init();
cancfg_rump_createif(ifname);
s = can_socket_with_own();
strcpy(ifr.ifr_name, ifname );
if ((rv = rump_sys_ioctl(s, SIOCGIFINDEX, &ifr)) < 0) {
atf_tc_fail_errno("SIOCGIFINDEX");
}
ATF_CHECK_MSG(ifr.ifr_ifindex > 0, "%s index is %d (not > 0)",
ifname, ifr.ifr_ifindex);
sa.can_family = AF_CAN;
sa.can_ifindex = ifr.ifr_ifindex;
/*
* send a single byte message.
* not 0.
*/
memset(&cf_send, 0, sizeof(cf_send));
cf_send.can_id = 1;
cf_send.can_dlc = 1;
cf_send.data[0] = 0xde;
if (rump_sys_sendto(s, &cf_send, sizeof(cf_send) - 7,
0, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
atf_tc_fail_errno("sendto");
}
if (can_read(s, &cf_receive, &rv) < 0) {
atf_tc_fail_errno("read");
}
memset(&cf_send, 0, sizeof(cf_send));
cf_send.can_id = 1;
cf_send.can_dlc = 1;
cf_send.data[0] = 0xde;
/* other data[] are expected to be 0 */
ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive, sizeof(cf_send)) == 0,
"received packet is not what we sent");
rv = rump_sys_write(s, &cf_send, sizeof(cf_send) - 7);
ATF_CHECK_MSG(rv < 0 && errno == EDESTADDRREQ,
"write to unbound socket didn't fail");
}
ATF_TC(canreadlocal);
ATF_TC_HEAD(canreadlocal, tc)
{
atf_tc_set_md_var(tc, "descr", "check all CAN sockets get messages");
atf_tc_set_md_var(tc, "timeout", "5");
}
ATF_TC_BODY(canreadlocal, tc)
{
const char ifname[] = "canlo0";
int s1, rv1;
int s2, rv2;
struct can_frame cf_send, cf_receive1, cf_receive2;
rump_init();
cancfg_rump_createif(ifname);
memset(&cf_send, 0, sizeof(cf_send));
cf_send.can_id = 1;
cf_send.can_dlc = 8;
cf_send.data[0] = 0xde;
cf_send.data[1] = 0xad;
cf_send.data[2] = 0xbe;
cf_send.data[3] = 0xef;
if ((s1 = rump_sys_socket(AF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
atf_tc_fail_errno("CAN socket");
}
/* create a second socket */
s2 = can_socket_with_own();
can_bind(s2, ifname);
/*
* send a single byte message, but make sure remaining payload is
* not 0.
*/
if (rump_sys_write(s2, &cf_send, sizeof(cf_send)) < 0) {
atf_tc_fail_errno("write");
}
if (can_read(s2, &cf_receive2, &rv2) < 0) {
atf_tc_fail_errno("can_read");
}
ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive2, sizeof(cf_send)) == 0,
"received (2) packet is not what we sent");
/* now check first socket */
if (can_read(s1, &cf_receive1, &rv1) < 0) {
atf_tc_fail_errno("can_read");
}
ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive1, sizeof(cf_send)) == 0,
"received (1) packet is not what we sent");
}
ATF_TC(canrecvfrom);
ATF_TC_HEAD(canrecvfrom, tc)
{
atf_tc_set_md_var(tc, "descr", "check that recvfrom gets the CAN interface");
atf_tc_set_md_var(tc, "timeout", "5");
}
ATF_TC_BODY(canrecvfrom, tc)
{
const char ifname[] = "canlo0";
int s1, rv1;
int s2, rv2;
struct can_frame cf_send, cf_receive1, cf_receive2;
int ifindex;
struct sockaddr_can sa;
rump_init();
cancfg_rump_createif(ifname);
memset(&cf_send, 0, sizeof(cf_send));
cf_send.can_id = 1;
cf_send.can_dlc = 8;
cf_send.data[0] = 0xde;
cf_send.data[1] = 0xad;
cf_send.data[2] = 0xbe;
cf_send.data[3] = 0xef;
s1 = -1;
if ((s1 = rump_sys_socket(AF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
atf_tc_fail_errno("CAN socket");
}
/* create a second socket */
s2 = can_socket_with_own();
ifindex = can_bind(s2, ifname);
if (rump_sys_write(s2, &cf_send, sizeof(cf_send)) < 0) {
atf_tc_fail_errno("write");
}
if (can_read(s2, &cf_receive2, &rv2) < 0) {
atf_tc_fail_errno("can_read");
}
ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive2, sizeof(cf_send)) == 0,
"received (2) packet is not what we sent");
/* now check first socket */
memset(&sa, 0, sizeof(sa));
if (can_recvfrom(s1, &cf_receive1, &rv1, &sa) < 0) {
atf_tc_fail_errno("can_recvfrom");
}
ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive1, sizeof(cf_send)) == 0,
"recvfrom (1) packet is not what we sent");
ATF_CHECK_MSG(sa.can_ifindex == ifindex,
"recvfrom provided wrong ifindex %d (!= %d)",
sa.can_ifindex, ifindex);
}
ATF_TC(canbindfilter);
ATF_TC_HEAD(canbindfilter, tc)
{
atf_tc_set_md_var(tc, "descr", "check that socket bound to an interface doens't get other interface's messages");
atf_tc_set_md_var(tc, "timeout", "5");
}
ATF_TC_BODY(canbindfilter, tc)
{
const char ifname[] = "canlo0";
const char ifname2[] = "canlo1";
int s1, rv1;
int s2, rv2;
struct sockaddr_can sa;
int ifindex2;
struct can_frame cf_send, cf_receive1, cf_receive2;
rump_init();
cancfg_rump_createif(ifname);
cancfg_rump_createif(ifname2);
memset(&cf_send, 0, sizeof(cf_send));
cf_send.can_id = 1;
cf_send.can_dlc = 8;
cf_send.data[0] = 0xde;
cf_send.data[1] = 0xad;
cf_send.data[2] = 0xbe;
cf_send.data[3] = 0xef;
s1 = can_socket_with_own();
can_bind(s1, ifname);
/* create a second socket */
s2 = can_socket_with_own();
ifindex2 = can_bind(s2, ifname2);
if (rump_sys_write(s2, &cf_send, sizeof(cf_send)) < 0) {
atf_tc_fail_errno("write");
}
if (can_read(s2, &cf_receive2, &rv2) < 0) {
atf_tc_fail_errno("read");
}
ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive2, sizeof(cf_send)) == 0,
"received (2) packet is not what we sent");
/* now check first socket */
if (can_recvfrom(s1, &cf_receive1, &rv1, &sa) < 0) {
if (errno == EWOULDBLOCK) {
/* expected case */
return;
}
atf_tc_fail_errno("can_recvfrom");
}
ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive1, sizeof(cf_send)) == 0,
"recvfrom (1) packet is not what we sent");
ATF_CHECK_MSG(sa.can_ifindex == ifindex2,
"recvfrom provided wrong ifindex %d (!= %d)",
sa.can_ifindex, ifindex2);
atf_tc_fail("we got message from other interface");
}
ATF_TC(cannoloop);
ATF_TC_HEAD(cannoloop, tc)
{
atf_tc_set_md_var(tc, "descr", "check that disabling loopback works");
atf_tc_set_md_var(tc, "timeout", "5");
}
ATF_TC_BODY(cannoloop, tc)
{
const char ifname[] = "canlo0";
int s1, rv1;
int s2, rv2;
int v;
socklen_t vlen;
struct sockaddr_can sa;
struct can_frame cf_send, cf_receive1, cf_receive2;
socklen_t salen;
int ifindex;
fd_set rfds;
struct timeval tmout;
rump_init();
cancfg_rump_createif(ifname);
memset(&cf_send, 0, sizeof(cf_send));
cf_send.can_id = 1;
cf_send.can_dlc = 8;
cf_send.data[0] = 0xde;
cf_send.data[1] = 0xad;
cf_send.data[2] = 0xbe;
cf_send.data[3] = 0xef;
s1 = can_socket_with_own();
v = 0;
if (rump_sys_setsockopt(s1, SOL_CAN_RAW, CAN_RAW_LOOPBACK,
&v, sizeof(v)) < 0) {
atf_tc_fail_errno("setsockopt(LOOPBACK)");
}
v = -1;
vlen = sizeof(v);
if (rump_sys_getsockopt(s1, SOL_CAN_RAW, CAN_RAW_LOOPBACK,
&v, &vlen) < 0) {
atf_tc_fail_errno("getsockopt(CAN_RAW_LOOPBACK)");
}
ATF_CHECK_MSG(vlen == sizeof(v), "getsockopt(CAN_RAW_LOOPBACK) returns wrong len %d", vlen);
ATF_CHECK_MSG(v == 0, "CAN_RAW_LOOPBACK is not off");
ifindex = can_bind(s1, ifname);
/* create a second socket */
s2 = can_socket_with_own();
if (rump_sys_write(s1, &cf_send, sizeof(cf_send)) < 0) {
atf_tc_fail_errno("write");
}
/* now check the sockets */
memset(&cf_receive1, 0, sizeof(cf_receive1));
memset(&cf_receive2, 0, sizeof(cf_receive2));
FD_ZERO(&rfds);
FD_SET(s1, &rfds);
FD_SET(s2, &rfds);
/* we should receive no message; wait for 1 seconds */
tmout.tv_sec = 1;
tmout.tv_usec = 0;
rv1 = rump_sys_select(MAX(s1,s2) + 1, &rfds, NULL, NULL, &tmout);
switch(rv1) {
case -1:
atf_tc_fail_errno("select");
break;
case 0:
/* timeout: expected case */
return;
default: break;
}
salen = sizeof(sa);
ATF_CHECK_MSG(FD_ISSET(s1, &rfds) || FD_ISSET(s2, &rfds),
"select returns but s1 nor s2 is in set");
if (FD_ISSET(s1, &rfds)) {
if (( rv1 = rump_sys_recvfrom(s1, &cf_receive1,
sizeof(cf_receive1), 0,
(struct sockaddr *)&sa, &salen)) < 0) {
atf_tc_fail_errno("recvfrom");
}
ATF_CHECK_MSG(rv1 > 0, "short read on socket");
ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive1,
sizeof(cf_send)) == 0,
"recvfrom (1) packet is not what we sent");
ATF_CHECK_MSG(sa.can_family == AF_CAN,
"recvfrom provided wrong %d family", sa.can_family);
ATF_CHECK_MSG(salen == sizeof(sa),
"recvfrom provided wrong size %d (!= %ld)",
salen, sizeof(sa));
ATF_CHECK_MSG(sa.can_ifindex == ifindex,
"recvfrom provided wrong ifindex %d (!= %d)",
sa.can_ifindex, ifindex);
atf_tc_fail_nonfatal("we got message on s1");
}
if (FD_ISSET(s2, &rfds)) {
if (( rv2 = rump_sys_recvfrom(s2, &cf_receive2,
sizeof(cf_receive2), 0,
(struct sockaddr *)&sa, &salen)) < 0) {
atf_tc_fail_errno("recvfrom");
}
ATF_CHECK_MSG(rv2 > 0, "short read on socket");
ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive2,
sizeof(cf_send)) == 0,
"recvfrom (2) packet is not what we sent");
ATF_CHECK_MSG(sa.can_family == AF_CAN,
"recvfrom provided wrong %d family", sa.can_family);
ATF_CHECK_MSG(salen == sizeof(sa),
"recvfrom provided wrong size %d (!= %ld)",
salen, sizeof(sa));
ATF_CHECK_MSG(sa.can_ifindex == ifindex,
"recvfrom provided wrong ifindex %d (!= %d)",
sa.can_ifindex, ifindex);
atf_tc_fail_nonfatal("we got message on s2");
}
}
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, canlocreate);
ATF_TP_ADD_TC(tp, cannoown);
ATF_TP_ADD_TC(tp, canwritelo);
ATF_TP_ADD_TC(tp, canwriteunbound);
ATF_TP_ADD_TC(tp, cansendtolo);
ATF_TP_ADD_TC(tp, cansendtowrite);
ATF_TP_ADD_TC(tp, canreadlocal);
ATF_TP_ADD_TC(tp, canrecvfrom);
ATF_TP_ADD_TC(tp, canbindfilter);
ATF_TP_ADD_TC(tp, cannoloop);
return atf_no_error();
}

464
tests/net/can/t_canfilter.c Normal file
View File

@ -0,0 +1,464 @@
/* $NetBSD: t_canfilter.c,v 1.2 2017/05/27 21:02:56 bouyer Exp $ */
/*-
* Copyright (c) 2017 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Manuel Bouyer
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
* CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: t_canfilter.c,v 1.2 2017/05/27 21:02:56 bouyer Exp $");
#endif /* not lint */
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <sys/sockio.h>
#include <sys/param.h>
#include <atf-c.h>
#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <netcan/can.h>
#include <rump/rump.h>
#include <rump/rump_syscalls.h>
#include "h_macros.h"
#include "h_canutils.h"
ATF_TC(canfilter_basic);
ATF_TC_HEAD(canfilter_basic, tc)
{
atf_tc_set_md_var(tc, "descr", "check a simple CAN filter");
atf_tc_set_md_var(tc, "timeout", "5");
}
ATF_TC_BODY(canfilter_basic, tc)
{
const char ifname[] = "canlo0";
int s, rv;
struct can_frame cf_send, cf_receive;
struct can_filter cfi;
rump_init();
cancfg_rump_createif(ifname);
s = can_socket_with_own();
can_bind(s, ifname);
/* set filter */
#define MY_ID 1
cfi.can_id = MY_ID;
cfi.can_mask = CAN_SFF_MASK | CAN_EFF_FLAG;
if (rump_sys_setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER,
&cfi, sizeof(cfi)) < 0) {
atf_tc_fail_errno("setsockopt(CAN_RAW_FILTER)");
}
/*
* send a single byte message, but make sure remaining payload is
* not 0.
*/
memset(&cf_send, 0, sizeof(cf_send));
cf_send.can_id = MY_ID;
cf_send.can_dlc = 1;
cf_send.data[0] = 0xde;
cf_send.data[1] = 0xad;
cf_send.data[2] = 0xbe;
cf_send.data[3] = 0xef;
if (rump_sys_write(s, &cf_send, sizeof(cf_send) - 7) < 0) {
atf_tc_fail_errno("write");
}
if (can_read(s, &cf_receive, &rv) < 0) {
atf_tc_fail_errno("read");
}
ATF_CHECK_MSG(rv > 0, "short read on socket");
memset(&cf_send, 0, sizeof(cf_send));
cf_send.can_id = MY_ID;
cf_send.can_dlc = 1;
cf_send.data[0] = 0xde;
/* other data[] are expected to be 0 */
ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive, sizeof(cf_send)) == 0,
"received packet is not what we sent");
/* now send a packet with CAN_RTR_FLAG. Should pass too */
memset(&cf_send, 0, sizeof(cf_send));
cf_send.can_id = MY_ID | CAN_RTR_FLAG;
cf_send.can_dlc = 1;
cf_send.data[0] = 0xde;
cf_send.data[1] = 0xad;
cf_send.data[2] = 0xbe;
cf_send.data[3] = 0xef;
if (rump_sys_write(s, &cf_send, sizeof(cf_send) - 7) < 0) {
atf_tc_fail_errno("write");
}
if (can_read(s, &cf_receive, &rv) < 0) {
atf_tc_fail_errno("read");
}
ATF_CHECK_MSG(rv > 0, "short read on socket");
memset(&cf_send, 0, sizeof(cf_send));
cf_send.can_id = MY_ID | CAN_RTR_FLAG;
cf_send.can_dlc = 1;
cf_send.data[0] = 0xde;
/* other data[] are expected to be 0 */
ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive, sizeof(cf_send)) == 0,
"received packet is not what we sent");
/* now send a packet for a different id. Should not pass */
memset(&cf_send, 0, sizeof(cf_send));
cf_send.can_id = MY_ID + 1;
cf_send.can_dlc = 1;
cf_send.data[0] = 0xde;
cf_send.data[1] = 0xad;
cf_send.data[2] = 0xbe;
cf_send.data[3] = 0xef;
if (rump_sys_write(s, &cf_send, sizeof(cf_send) - 7) < 0) {
atf_tc_fail_errno("write");
}
if (can_read(s, &cf_receive, &rv) < 0) {
if (errno == EWOULDBLOCK)
return; /* expected timeout */
atf_tc_fail_errno("read");
}
ATF_CHECK_MSG(rv > 0, "short read on socket");
memset(&cf_send, 0, sizeof(cf_send));
cf_send.can_id = MY_ID + 1;
cf_send.can_dlc = 1;
cf_send.data[0] = 0xde;
/* other data[] are expected to be 0 */
ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive, sizeof(cf_send)) == 0,
"received packet is not what we sent");
atf_tc_fail("we got our own message");
#undef MY_ID
}
ATF_TC(canfilter_null);
ATF_TC_HEAD(canfilter_null, tc)
{
atf_tc_set_md_var(tc, "descr", "check a NULL CAN filter");
atf_tc_set_md_var(tc, "timeout", "5");
}
ATF_TC_BODY(canfilter_null, tc)
{
const char ifname[] = "canlo0";
int s, rv;
struct can_frame cf_send, cf_receive;
struct can_filter cfi[2];
socklen_t cfilen;
rump_init();
cancfg_rump_createif(ifname);
s = can_socket_with_own();
can_bind(s, ifname);
if (rump_sys_setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER,
NULL, 0) < 0) {
atf_tc_fail_errno("setsockopt(CAN_RAW_FILTER)");
}
/* get filter: should be NULL */
cfilen = sizeof(cfi);
if (rump_sys_getsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER,
&cfi, &cfilen) < 0) {
atf_tc_fail_errno("getsockopt(CAN_RAW_FILTER)");
}
ATF_CHECK_MSG(cfilen == 0,
"CAN_RAW_FILTER returns wrong len (%d)", cfilen);
/*
* send a single byte message, but make sure remaining payload is
* not 0.
*/
#define MY_ID 1
memset(&cf_send, 0, sizeof(cf_send));
cf_send.can_id = MY_ID;
cf_send.can_dlc = 1;
cf_send.data[0] = 0xde;
cf_send.data[1] = 0xad;
cf_send.data[2] = 0xbe;
cf_send.data[3] = 0xef;
if (rump_sys_write(s, &cf_send, sizeof(cf_send) - 7) < 0) {
atf_tc_fail_errno("write");
}
if (can_read(s, &cf_receive, &rv) < 0) {
if (errno == EWOULDBLOCK)
return; /* expected timeout */
atf_tc_fail_errno("read");
}
ATF_CHECK_MSG(rv > 0, "short read on socket");
memset(&cf_send, 0, sizeof(cf_send));
cf_send.can_id = MY_ID;
cf_send.can_dlc = 1;
cf_send.data[0] = 0xde;
/* other data[] are expected to be 0 */
ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive, sizeof(cf_send)) == 0,
"received packet is not what we sent");
atf_tc_fail("we got our own message");
#undef MY_ID
}
ATF_TC(canfilter_multiple);
ATF_TC_HEAD(canfilter_multiple, tc)
{
atf_tc_set_md_var(tc, "descr", "check multiple CAN filters");
atf_tc_set_md_var(tc, "timeout", "5");
}
ATF_TC_BODY(canfilter_multiple, tc)
{
const char ifname[] = "canlo0";
int s, rv;
struct can_frame cf_send, cf_receive;
struct can_filter cfi[2];
rump_init();
cancfg_rump_createif(ifname);
s = can_socket_with_own();
can_bind(s, ifname);
/* set filter: accept MY_ID and MY_ID+1 */
#define MY_ID 1
cfi[0].can_id = MY_ID;
cfi[0].can_mask = CAN_SFF_MASK | CAN_EFF_FLAG;
cfi[1].can_id = MY_ID + 1;
cfi[1].can_mask = CAN_SFF_MASK | CAN_EFF_FLAG;
if (rump_sys_setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER,
&cfi, sizeof(cfi)) < 0) {
atf_tc_fail_errno("setsockopt(CAN_RAW_FILTER)");
}
/*
* send a single byte message, but make sure remaining payload is
* not 0.
*/
memset(&cf_send, 0, sizeof(cf_send));
cf_send.can_id = MY_ID;
cf_send.can_dlc = 1;
cf_send.data[0] = 0xde;
cf_send.data[1] = 0xad;
cf_send.data[2] = 0xbe;
cf_send.data[3] = 0xef;
if (rump_sys_write(s, &cf_send, sizeof(cf_send) - 7) < 0) {
atf_tc_fail_errno("write");
}
if (can_read(s, &cf_receive, &rv) < 0) {
atf_tc_fail_errno("read");
}
ATF_CHECK_MSG(rv > 0, "short read on socket");
memset(&cf_send, 0, sizeof(cf_send));
cf_send.can_id = MY_ID;
cf_send.can_dlc = 1;
cf_send.data[0] = 0xde;
/* other data[] are expected to be 0 */
ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive, sizeof(cf_send)) == 0,
"received packet is not what we sent");
/* now send a packet with MY_ID+1. Should pass too */
memset(&cf_send, 0, sizeof(cf_send));
cf_send.can_id = MY_ID + 1;
cf_send.can_dlc = 1;
cf_send.data[0] = 0xde;
cf_send.data[1] = 0xad;
cf_send.data[2] = 0xbe;
cf_send.data[3] = 0xef;
if (rump_sys_write(s, &cf_send, sizeof(cf_send) - 7) < 0) {
atf_tc_fail_errno("write");
}
if (can_read(s, &cf_receive, &rv) < 0) {
atf_tc_fail_errno("read");
}
ATF_CHECK_MSG(rv > 0, "short read on socket");
memset(&cf_send, 0, sizeof(cf_send));
cf_send.can_id = MY_ID + 1;
cf_send.can_dlc = 1;
cf_send.data[0] = 0xde;
/* other data[] are expected to be 0 */
ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive, sizeof(cf_send)) == 0,
"received packet is not what we sent");
/* now send a packet with MY_ID + 2. Should not pass */
memset(&cf_send, 0, sizeof(cf_send));
cf_send.can_id = MY_ID + 2;
cf_send.can_dlc = 1;
cf_send.data[0] = 0xde;
cf_send.data[1] = 0xad;
cf_send.data[2] = 0xbe;
cf_send.data[3] = 0xef;
if (rump_sys_write(s, &cf_send, sizeof(cf_send) - 7) < 0) {
atf_tc_fail_errno("write");
}
if (can_read(s, &cf_receive, &rv) < 0) {
if (errno == EWOULDBLOCK)
return; /* expected timeout */
atf_tc_fail_errno("read");
}
ATF_CHECK_MSG(rv > 0, "short read on socket");
memset(&cf_send, 0, sizeof(cf_send));
cf_send.can_id = MY_ID + 2;
cf_send.can_dlc = 1;
cf_send.data[0] = 0xde;
/* other data[] are expected to be 0 */
ATF_CHECK_MSG(memcmp(&cf_send, &cf_receive, sizeof(cf_send)) == 0,
"received packet is not what we sent");
atf_tc_fail("we got our own message");
#undef MY_ID
}
ATF_TC(canfilter_get);
ATF_TC_HEAD(canfilter_get, tc)
{
atf_tc_set_md_var(tc, "descr", "check reading CAN filters");
atf_tc_set_md_var(tc, "timeout", "5");
}
ATF_TC_BODY(canfilter_get, tc)
{
const char ifname[] = "canlo0";
int s;
struct can_filter cfi[2];
socklen_t cfilen;
rump_init();
cancfg_rump_createif(ifname);
s = -1;
if ((s = rump_sys_socket(AF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
atf_tc_fail_errno("CAN socket");
}
cfilen = sizeof(cfi);
/* get filter: should be default filter */
if (rump_sys_getsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER,
&cfi, &cfilen) < 0) {
atf_tc_fail_errno("getsockopt(CAN_RAW_FILTER)");
}
ATF_CHECK_MSG(cfilen == sizeof(struct can_filter),
"CAN_RAW_FILTER returns wrong len (%d)", cfilen);
ATF_CHECK_MSG(cfi[0].can_id == 0 && cfi[0].can_mask == 0,
"CAN_RAW_FILTER returns wrong filter (%d, %d)",
cfi[0].can_id, cfi[0].can_mask);
/* set filter: accept MY_ID and MY_ID+1 */
#define MY_ID 1
cfi[0].can_id = MY_ID;
cfi[0].can_mask = CAN_SFF_MASK | CAN_EFF_FLAG;
cfi[1].can_id = MY_ID + 1;
cfi[1].can_mask = CAN_SFF_MASK;
if (rump_sys_setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER,
&cfi, sizeof(cfi)) < 0) {
atf_tc_fail_errno("setsockopt(CAN_RAW_FILTER)");
}
/* and read back */
cfilen = sizeof(cfi);
memset(cfi, 0, cfilen);
if (rump_sys_getsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER,
&cfi, &cfilen) < 0) {
atf_tc_fail_errno("getsockopt(CAN_RAW_FILTER)");
}
ATF_CHECK_MSG(cfilen == sizeof(struct can_filter) * 2,
"CAN_RAW_FILTER returns wrong len (%d)", cfilen);
ATF_CHECK_MSG(cfi[0].can_id == MY_ID &&
cfi[0].can_mask == (CAN_SFF_MASK | CAN_EFF_FLAG),
"CAN_RAW_FILTER returns wrong filter 0 (%d, %d)",
cfi[0].can_id, cfi[0].can_mask);
ATF_CHECK_MSG(cfi[1].can_id == MY_ID + 1 &&
cfi[1].can_mask == CAN_SFF_MASK,
"CAN_RAW_FILTER returns wrong filter 1 (%d, %d)",
cfi[1].can_id, cfi[1].can_mask);
#undef MY_ID
}
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, canfilter_basic);
ATF_TP_ADD_TC(tp, canfilter_null);
ATF_TP_ADD_TC(tp, canfilter_multiple);
ATF_TP_ADD_TC(tp, canfilter_get);
return atf_no_error();
}