diff options
| author | bouyer <bouyer@NetBSD.org> | 2017-05-27 21:02:54 +0000 |
|---|---|---|
| committer | bouyer <bouyer@NetBSD.org> | 2017-05-27 21:02:54 +0000 |
| commit | 3e7aaa91a8b5dd2a6aebff25d0f9fcab4a2edb90 (patch) | |
| tree | f19f0d1a6f18fcabb5481e3b1fc16611f1e3b95b /sys | |
| parent | cdd3ff897b961874efb8ed508de493f2315f541a (diff) | |
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.
Diffstat (limited to 'sys')
27 files changed, 2966 insertions, 18 deletions
diff --git a/sys/Makefile b/sys/Makefile index ee8b33c46ed..068476b63b4 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -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 diff --git a/sys/arch/arm/allwinner/awin_can.c b/sys/arch/arm/allwinner/awin_can.c new file mode 100644 index 00000000000..68b7889a33e --- /dev/null +++ b/sys/arch/arm/allwinner/awin_can.c @@ -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)); +} diff --git a/sys/arch/arm/allwinner/awin_io.c b/sys/arch/arm/allwinner/awin_io.c index 9bb54268503..d1bf078c4d9 100644 --- a/sys/arch/arm/allwinner/awin_io.c +++ b/sys/arch/arm/allwinner/awin_io.c @@ -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 diff --git a/sys/arch/arm/allwinner/awin_reg.h b/sys/arch/arm/allwinner/awin_reg.h index 1d35bb9e61e..dc5c70159db 100644 --- a/sys/arch/arm/allwinner/awin_reg.h +++ b/sys/arch/arm/allwinner/awin_reg.h @@ -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_ */ diff --git a/sys/arch/arm/allwinner/files.awin b/sys/arch/arm/allwinner/files.awin index 575a67a7e9e..9df7a802ef6 100644 --- a/sys/arch/arm/allwinner/files.awin +++ b/sys/arch/arm/allwinner/files.awin @@ -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 diff --git a/sys/conf/files b/sys/conf/files index 0fafb25e209..16e2ca89660 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -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 diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c index 9b37147048a..bbe6d1d2418 100644 --- a/sys/kern/uipc_socket.c +++ b/sys/kern/uipc_socket.c @@ -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: diff --git a/sys/net/netisr.h b/sys/net/netisr.h index f98b5becf8b..c355794d10c 100644 --- a/sys/net/netisr.h +++ b/sys/net/netisr.h @@ -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) diff --git a/sys/net/netisr_dispatch.h b/sys/net/netisr_dispatch.h index 2e500a81f8c..470e6522f42 100644 --- a/sys/net/netisr_dispatch.h +++ b/sys/net/netisr_dispatch.h @@ -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_ */ diff --git a/sys/netcan/Makefile b/sys/netcan/Makefile new file mode 100644 index 00000000000..965fa4b24ed --- /dev/null +++ b/sys/netcan/Makefile @@ -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> diff --git a/sys/netcan/can.c b/sys/netcan/can.c new file mode 100644 index 00000000000..7afe1c823f0 --- /dev/null +++ b/sys/netcan/can.c @@ -0,0 +1,1013 @@ +/* $NetBSD: can.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.c,v 1.2 2017/05/27 21:02:56 bouyer Exp $"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/ioctl.h> +#include <sys/domain.h> +#include <sys/protosw.h> +#include <sys/errno.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/proc.h> +#include <sys/kauth.h> + +#include <net/if.h> +#include <net/if_types.h> +#include <net/netisr.h> +#include <net/route.h> +#include <net/bpf.h> + +#include <netcan/can.h> +#include <netcan/can_pcb.h> +#include <netcan/can_var.h> + +struct canpcb canpcb; +#if 0 +struct canpcb canrawpcb; +#endif + +struct canpcbtable cbtable; + +struct ifqueue canintrq; +int canqmaxlen = IFQ_MAXLEN; + +int can_copy_output = 0; +int can_output_cnt = 0; +struct mbuf *can_lastout; + +int can_sendspace = 4096; /* really max datagram size */ +int can_recvspace = 40 * (1024 + sizeof(struct sockaddr_can)); + /* 40 1K datagrams */ +#ifndef CANHASHSIZE +#define CANHASHSIZE 128 +#endif +int canhashsize = CANHASHSIZE; + +static int can_output(struct mbuf *, struct canpcb *); + +static int can_control(struct socket *, u_long, void *, struct ifnet *); + +void +can_init(void) +{ + canintrq.ifq_maxlen = canqmaxlen; + IFQ_LOCK_INIT(&canintrq); + can_pcbinit(&cbtable, canhashsize, canhashsize); +} + +/* + * Generic control operations (ioctl's). + */ +static int +can_get_netlink(struct ifnet *ifp, struct ifdrv *ifd) +{ + struct canif_softc *csc = ifp->if_softc; + + if (ifp->if_dlt != DLT_CAN_SOCKETCAN || csc == NULL) + return EOPNOTSUPP; + + switch(ifd->ifd_cmd) { + case CANGLINKTIMECAP: + if (ifd->ifd_len != sizeof(struct can_link_timecaps)) + return EINVAL; + return copyout(&csc->csc_timecaps, ifd->ifd_data, ifd->ifd_len); + case CANGLINKTIMINGS: + if (ifd->ifd_len != sizeof(struct can_link_timings)) + return EINVAL; + return copyout(&csc->csc_timings, ifd->ifd_data, ifd->ifd_len); + case CANGLINKMODE: + if (ifd->ifd_len != sizeof(uint32_t)) + return EINVAL; + return copyout(&csc->csc_linkmodes, ifd->ifd_data, ifd->ifd_len); + } + return EOPNOTSUPP; +} + +static int +can_set_netlink(struct ifnet *ifp, struct ifdrv *ifd) +{ + struct canif_softc *csc = ifp->if_softc; + uint32_t mode; + int error; + + if (ifp->if_dlt != DLT_CAN_SOCKETCAN || csc == NULL) + return EOPNOTSUPP; + + error = kauth_authorize_network(curlwp->l_cred, + KAUTH_NETWORK_INTERFACE, + KAUTH_REQ_NETWORK_INTERFACE_SETPRIV, ifp, + (void *)SIOCSDRVSPEC, NULL); + if (error != 0) + return error; + + if ((ifp->if_flags & IFF_UP) != 0) { + return EBUSY; + } + + switch(ifd->ifd_cmd) { + case CANSLINKTIMINGS: + if (ifd->ifd_len != sizeof(struct can_link_timings)) + return EINVAL; + return copyin(ifd->ifd_data, &csc->csc_timings, ifd->ifd_len); + + case CANSLINKMODE: + case CANCLINKMODE: + if (ifd->ifd_len != sizeof(uint32_t)) + return EINVAL; + error = copyin(ifd->ifd_data, &mode, ifd->ifd_len); + if (error) + return error; + if ((mode & csc->csc_timecaps.cltc_linkmode_caps) != mode) + return EINVAL; + /* XXX locking */ + if (ifd->ifd_cmd == CANSLINKMODE) + csc->csc_linkmodes |= mode; + else + csc->csc_linkmodes &= ~mode; + return 0; + } + return EOPNOTSUPP; +} + +/* ARGSUSED */ +static int +can_control(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) +{ +#if 0 + struct can_ifreq *cfr = (struct can_ifreq *)data; + int error = 0; +#endif + if (ifp == NULL) + return (EOPNOTSUPP); + + switch (cmd) { + case SIOCGDRVSPEC: + return can_get_netlink(ifp, (struct ifdrv *) data); + case SIOCSDRVSPEC: + return can_set_netlink(ifp, (struct ifdrv *) data); + default: + if (ifp->if_ioctl == 0) + return (EOPNOTSUPP); + return ((*ifp->if_ioctl)(ifp, cmd, data)); + } + return (0); +} + +static int +can_purgeif(struct socket *so, struct ifnet *ifp) +{ + return 0; +} + +void +can_ifattach(struct ifnet *ifp) +{ + if_attach(ifp); + ifp->if_mtu = sizeof(struct can_frame); + ifp->if_type = IFT_OTHER; + ifp->if_hdrlen = 0; + ifp->if_addrlen = 0; + ifp->if_dlt = DLT_CAN_SOCKETCAN; + ifp->if_output = NULL; /* unused */ + IFQ_SET_READY(&ifp->if_snd); + if_alloc_sadl(ifp); + bpf_attach(ifp, DLT_CAN_SOCKETCAN, 0); +} + +void +can_ifdetach(struct ifnet *ifp) +{ + bpf_detach(ifp); + if_detach(ifp); +} + +void +can_ifinit_timings(struct canif_softc *csc) +{ + /* uninitialized parameters is all-one */ + memset(&csc->csc_timings, 0xff, sizeof(struct can_link_timings)); +} + +static int +can_output(struct mbuf *m, struct canpcb *canp) +{ + struct ifnet *ifp; + struct m_tag *sotag; + struct canif_softc *csc; + + if (canp == NULL) { + printf("can_output: no pcb\n"); + return EINVAL; + } + ifp = canp->canp_ifp; + if (ifp == 0) { + return EDESTADDRREQ; + } + csc = ifp->if_softc; + if (csc && (csc->csc_linkmodes & CAN_LINKMODE_LISTENONLY)) { + return ENETUNREACH; + } + + sotag = m_tag_get(PACKET_TAG_SO, sizeof(struct socket *), PR_NOWAIT); + if (sotag == NULL) { + ifp->if_oerrors++; + return ENOMEM; + } + mutex_enter(&canp->canp_mtx); + canp_ref(canp); + mutex_exit(&canp->canp_mtx); + *(struct canpcb **)(sotag + 1) = canp; + m_tag_prepend(m, sotag); + + if (m->m_len <= ifp->if_mtu) { + can_output_cnt++; + return ifq_enqueue(ifp, m); + } else + return EMSGSIZE; +} + +/* + * cleanup mbuf tag, keeping the PACKET_TAG_SO tag + */ +void +can_mbuf_tag_clean(struct mbuf *m) +{ + struct m_tag *sotag; + + sotag = m_tag_find(m, PACKET_TAG_SO, NULL); + if (sotag) + m_tag_unlink(m, sotag); + + m_tag_delete_nonpersistent(m); + if (sotag) + m_tag_prepend(m, sotag); +} + +/* + * Process a received CAN frame + * the packet is in the mbuf chain m with + * the CAN header. + */ +void +can_input(struct ifnet *ifp, struct mbuf *m) +{ + struct ifqueue *inq; + + if ((ifp->if_flags & IFF_UP) == 0) { + m_freem(m); + return; + } + + inq = &canintrq; + + IFQ_LOCK(inq); + if (IF_QFULL(inq)) { + IF_DROP(inq); + IFQ_UNLOCK(inq); + m_freem(m); + } else { + IF_ENQUEUE(inq, m); + IFQ_UNLOCK(inq); + ifp->if_ipackets++; + ifp->if_ibytes += m->m_pkthdr.len; + schednetisr(NETISR_CAN); + } +} + +void +canintr(void) +{ + int rcv_ifindex; + struct mbuf *m; + + struct sockaddr_can from; + struct canpcb *canp; + struct m_tag *sotag; + struct canpcb *sender_canp; + + mutex_enter(softnet_lock); + for (;;) { + IFQ_LOCK(&canintrq); + IF_DEQUEUE(&canintrq, m); + IFQ_UNLOCK(&canintrq); + + if (m == NULL) /* no more queued packets */ + break; + +#if 0 + m_claim(m, &can_rx_mowner); +#endif + sotag = m_tag_find(m, PACKET_TAG_SO, NULL); + if (sotag) { + sender_canp = *(struct canpcb **)(sotag + 1); + m_tag_delete(m, sotag); + KASSERT(sender_canp != NULL); + /* if the sender doesn't want loopback, don't do it */ + if ((sender_canp->canp_flags & CANP_NO_LOOPBACK) != 0) { + m_freem(m); + canp_unref(sender_canp); + continue; + } + } else { + sender_canp = NULL; + } + memset(&from, 0, sizeof(struct sockaddr_can)); + rcv_ifindex = m->m_pkthdr.rcvif_index; + from.can_ifindex = rcv_ifindex; + from.can_len = sizeof(struct sockaddr_can); + from.can_family = AF_CAN; + + TAILQ_FOREACH(canp, &cbtable.canpt_queue, canp_queue) { + struct mbuf *mc; + + mutex_enter(&canp->canp_mtx); + /* skip if we're detached */ + if (canp->canp_state == CANP_DETACHED) { + mutex_exit(&canp->canp_mtx); + continue; + } + + /* don't loop back to sockets on other interfaces */ + if (canp->canp_ifp != NULL && + canp->canp_ifp->if_index != rcv_ifindex) { + mutex_exit(&canp->canp_mtx); + continue; + } + /* don't loop back to myself if I don't want it */ + if (canp == sender_canp && + (canp->canp_flags & CANP_RECEIVE_OWN) == 0) { + mutex_exit(&canp->canp_mtx); + continue; + } + + /* skip if the accept filter doen't match this pkt */ + if (!can_pcbfilter(canp, m)) { + mutex_exit(&canp->canp_mtx); + continue; + } + + if (TAILQ_NEXT(canp, canp_queue) != NULL) { + /* + * we can't be sure we won't need + * the original mbuf later so copy + */ + mc = m_copypacket(m, M_NOWAIT); + if (mc == NULL) { + /* deliver this mbuf and abort */ + mc = m; + m = NULL; + } + } else { + mc = m; + m = NULL; + } + if (sbappendaddr(&canp->canp_socket->so_rcv, + (struct sockaddr *) &from, mc, + (struct mbuf *) 0) == 0) { + m_freem(mc); + } else + sorwakeup(canp->canp_socket); + mutex_exit(&canp->canp_mtx); + if (m == NULL) + break; + } + if (sender_canp) { + canp_unref(sender_canp); + } + /* If it didn't go anywhere just delete it */ + if (m) { + m_freem(m); + } + } + mutex_exit(softnet_lock); +} + +void +can_bpf_mtap(struct ifnet *ifp, struct mbuf *m, bool do_softint) +{ + /* bpf wants the CAN id in network byte order */ + struct can_frame *cf; + canid_t oid; + + cf = mtod(m, struct can_frame *); + oid = cf->can_id; + cf->can_id = htonl(oid); + if (do_softint) + bpf_mtap_softint(ifp, m); + else + bpf_mtap(ifp, m); + cf->can_id = oid; +} + +static int +can_attach(struct socket *so, int proto) +{ + int error; + + KASSERT(sotocanpcb(so) == NULL); + + /* Assign the lock (must happen even if we will error out). */ + sosetlock(so); + +#ifdef MBUFTRACE + so->so_mowner = &can_mowner; + so->so_rcv.sb_mowner = &can_rx_mowner; + so->so_snd.sb_mowner = &can_tx_mowner; +#endif + if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) { + error = soreserve(so, can_sendspace, can_recvspace); + if (error) { + return error; + } + } + + error = can_pcballoc(so, &cbtable); + if (error) { + return error; + } + KASSERT(solocked(so)); + + return error; +} + +static void +can_detach(struct socket *so) +{ + struct canpcb *canp; + + KASSERT(solocked(so)); + canp = sotocanpcb(so); + can_pcbdetach(canp); +} + +static int +can_accept(struct socket *so, struct sockaddr *nam) +{ + KASSERT(solocked(so)); + + panic("can_accept"); + + return EOPNOTSUPP; +} + +static int +can_bind(struct socket *so, struct sockaddr *nam, struct lwp *l) +{ + struct canpcb *canp = sotocanpcb(so); + struct sockaddr_can *scan = (struct sockaddr_can *)nam; + + KASSERT(solocked(so)); + KASSERT(nam != NULL); + + return can_pcbbind(canp, scan, l); +} + +static int +can_listen(struct socket *so, struct lwp *l) +{ + KASSERT(solocked(so)); + + return EOPNOTSUPP; +} + +static int +can_connect(struct socket *so, struct sockaddr *nam, struct lwp *l) +{ + struct canpcb *canp = sotocanpcb(so); + int error = 0; + + KASSERT(solocked(so)); + KASSERT(canp != NULL); + KASSERT(nam != NULL); + + error = can_pcbconnect(canp, (struct sockaddr_can *)nam); + if (! error) + soisconnected(so); + return error; +} + +static int +can_connect2(struct socket *so, struct socket *so2) +{ + KASSERT(solocked(so)); + + return EOPNOTSUPP; +} + +static int +can_disconnect(struct socket *so) +{ + struct canpcb *canp = sotocanpcb(so); + + KASSERT(solocked(so)); + KASSERT(canp != NULL); + + /*soisdisconnected(so);*/ + so->so_state &= ~SS_ISCONNECTED; /* XXX */ + can_pcbdisconnect(canp); + return 0; +} + +static int +can_shutdown(struct socket *so) +{ + KASSERT(solocked(so)); + + socantsendmore(so); + return 0; +} + +static int +can_abort(struct socket *so) +{ + KASSERT(solocked(so)); + + panic("can_abort"); + + return EOPNOTSUPP; +} + +static int +can_ioctl(struct socket *so, u_long cmd, void *nam, struct ifnet *ifp) +{ + return can_control(so, cmd, nam, ifp); +} + +static int +can_stat(struct socket *so, struct stat *ub) +{ + KASSERT(solocked(so)); + + /* stat: don't bother with a blocksize. */ + return 0; +} + +static int +can_peeraddr(struct socket *so, struct sockaddr *nam) +{ + KASSERT(solocked(so)); + KASSERT(sotocanpcb(so) != NULL); + KASSERT(nam != NULL); + + return EOPNOTSUPP; +} + +static int +can_sockaddr(struct socket *so, struct sockaddr *nam) +{ + KASSERT(solocked(so)); + KASSERT(sotocanpcb(so) != NULL); + KASSERT(nam != NULL); + + can_setsockaddr(sotocanpcb(so), (struct sockaddr_can *)nam); + + return 0; +} + +static int +can_rcvd(struct socket *so, int flags, struct lwp *l) +{ + KASSERT(solocked(so)); + + return EOPNOTSUPP; +} + +static int +can_recvoob(struct socket *so, struct mbuf *m, int flags) +{ + KASSERT(solocked(so)); + + return EOPNOTSUPP; +} + +static int +can_send(struct socket *so, struct mbuf *m, struct sockaddr *nam, + struct mbuf *control, struct lwp *l) +{ + struct canpcb *canp = sotocanpcb(so); + int error = 0; + int s; + + if (control && control->m_len) { + m_freem(control); + error = EINVAL; + goto err; + } + if (m->m_len > sizeof(struct can_frame) || + m->m_len < offsetof(struct can_frame, can_dlc)) { + error = EINVAL; + goto err; + } + + /* we expect all data in the first mbuf */ + KASSERT((m->m_flags & M_PKTHDR) != 0); + KASSERT(m->m_len == m->m_pkthdr.len); + + if (nam) { + if ((so->so_state & SS_ISCONNECTED) != 0) { + error = EISCONN; + goto err; + } + s = splnet(); + error = can_pcbbind(canp, (struct sockaddr_can *)nam, l); + if (error) { + splx(s); + goto err; + } + } else { + if ((so->so_state & SS_ISCONNECTED) == 0) { + error = EDESTADDRREQ; + goto err; + } + } + error = can_output(m, canp); + if (nam) { + struct sockaddr_can lscan; + memset(&lscan, 0, sizeof(lscan)); + lscan.can_family = AF_CAN; + lscan.can_len = sizeof(lscan); + can_pcbbind(canp, &lscan, l); + } + if (error) + goto err; + return 0; + +err: + m_freem(m); + return error; +} + +static int +can_sendoob(struct socket *so, struct mbuf *m, struct mbuf *control) +{ + KASSERT(solocked(so)); + + m_freem(m); + m_freem(control); + + return EOPNOTSUPP; +} + +#if 0 +int +can_usrreq(struct socket *so, int req, struct mbuf *m, struct mbuf *nam, + struct mbuf *control, struct lwp *l) +{ + struct canpcb *canp; + int s; + int error = 0; + + if (req == PRU_CONTROL) + return (can_control(so, (long)m, nam, + (struct ifnet *)control)); + + if (req == PRU_PURGEIF) { +#if 0 + can_pcbpurgeif0(&udbtable, (struct ifnet *)control); + can_purgeif((struct ifnet *)control); + can_pcbpurgeif(&udbtable, (struct ifnet *)control); +#endif + return (0); + } + + s = splsoftnet(); + canp = sotocanpcb(so); +#ifdef DIAGNOSTIC + if (req != PRU_SEND && req != PRU_SENDOOB && control) + panic("can_usrreq: unexpected control mbuf"); +#endif + if (canp == 0 && req != PRU_ATTACH) { + printf("can_usrreq: no pcb %p %d\n", canp, req); + error = EINVAL; + goto release; + } + + /* + * Note: need to block can_input while changing + * the can pcb queue and/or pcb addresses. + */ + switch (req) { + + case PRU_ATTACH: + if (canp != 0) { + error = EISCONN; + break; + } +#ifdef MBUFTRACE + so->so_mowner = &can_mowner; + so->so_rcv.sb_mowner = &can_rx_mowner; + so->so_snd.sb_mowner = &can_tx_mowner; +#endif + if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) { + error = soreserve(so, can_sendspace, can_recvspace); + if (error) + break; + } + error = can_pcballoc(so, &cbtable); + if (error) + break; + canp = sotocanpcb(so); +#if 0 + inp->inp_ip.ip_ttl = ip_defttl; +#endif + break; + + case PRU_DETACH: + can_pcbdetach(canp); + break; + + case PRU_BIND: + error = can_pcbbind(canp, nam, l); + break; + + case PRU_LISTEN: + error = EOPNOTSUPP; + break; + + case PRU_CONNECT: + error = can_pcbconnect(canp, nam); + if (error) + break; + soisconnected(so); + break; + + case PRU_CONNECT2: + error = EOPNOTSUPP; + break; + + case PRU_DISCONNECT: + /*soisdisconnected(so);*/ + so->so_state &= ~SS_ISCONNECTED; /* XXX */ + can_pcbdisconnect(canp); + can_pcbstate(canp, CANP_BOUND); /* XXX */ + break; + + case PRU_SHUTDOWN: + socantsendmore(so); + break; + + case PRU_RCVD: + error = EOPNOTSUPP; + break; + + case PRU_SEND: + break; + + case PRU_SENSE: + /* + * stat: don't bother with a blocksize. + */ + splx(s); + return (0); + + case PRU_RCVOOB: + error = EOPNOTSUPP; + break; + + case PRU_SENDOOB: + m_freem(control); + m_freem(m); + error = EOPNOTSUPP; + break; + + case PRU_SOCKADDR: + + break; + + case PRU_PEERADDR: + error = EOPNOTSUPP; + break; + + default: + panic("can_usrreq"); + } + +release: + splx(s); + return (error); +} +#endif + +#if 0 +static void +can_notify(struct canpcb *canp, int errno) +{ + + canp->canp_socket->so_error = errno; + sorwakeup(canp->canp_socket); + sowwakeup(canp->canp_socket); +} + +void * +can_ctlinput(int cmd, struct sockaddr *sa, void *v) +{ + struct ip *ip = v; + struct canhdr *uh; + void (*notify) __P((struct inpcb *, int)) = can_notify; + int errno; + + if (sa->sa_family != AF_CAN + || sa->sa_len != sizeof(struct sockaddr_can)) + return NULL; + if ((unsigned)cmd >= PRC_NCMDS) + return NULL; + errno = inetctlerrmap[cmd]; + if (PRC_IS_REDIRECT(cmd)) + notify = in_rtchange, ip = 0; + else if (cmd == PRC_HOSTDEAD) + ip = 0; + else if (errno == 0) + return NULL; + if (ip) { + uh = (struct canhdr *)((caddr_t)ip + (ip->ip_hl << 2)); + in_pcbnotify(&udbtable, satosin(sa)->sin_addr, uh->uh_dport, + ip->ip_src, uh->uh_sport, errno, notify); + + /* XXX mapped address case */ + } else + can_pcbnotifyall(&cbtable, satoscan(sa)->scan_addr, errno, + notify); + return NULL; +} +#endif + +static int +can_raw_getop(struct canpcb *canp, struct sockopt *sopt) +{ + int optval = 0; + int error; + + switch (sopt->sopt_name) { + case CAN_RAW_LOOPBACK: + optval = (canp->canp_flags & CANP_NO_LOOPBACK) ? 0 : 1; + error = sockopt_set(sopt, &optval, sizeof(optval)); + break; + case CAN_RAW_RECV_OWN_MSGS: + optval = (canp->canp_flags & CANP_RECEIVE_OWN) ? 1 : 0; + error = sockopt_set(sopt, &optval, sizeof(optval)); + break; + case CAN_RAW_FILTER: + error = sockopt_set(sopt, canp->canp_filters, + sizeof(struct can_filter) * canp->canp_nfilters); + break; + default: + error = ENOPROTOOPT; + break; + } + return error; +} + +static int +can_raw_setop(struct canpcb *canp, struct sockopt *sopt) +{ + int optval = 0; + int error; + + switch (sopt->sopt_name) { + case CAN_RAW_LOOPBACK: + error = sockopt_getint(sopt, &optval); + if (error == 0) { + if (optval) { + canp->canp_flags &= ~CANP_NO_LOOPBACK; + } else { + canp->canp_flags |= CANP_NO_LOOPBACK; + } + } + break; + case CAN_RAW_RECV_OWN_MSGS: + error = sockopt_getint(sopt, &optval); + if (error == 0) { + if (optval) { + canp->canp_flags |= CANP_RECEIVE_OWN; + } else { + canp->canp_flags &= ~CANP_RECEIVE_OWN; + } + } + break; + case CAN_RAW_FILTER: + { + int nfilters = sopt->sopt_size / sizeof(struct can_filter); + if (sopt->sopt_size % sizeof(struct can_filter) != 0) + return EINVAL; + mutex_enter(&canp->canp_mtx); + error = can_pcbsetfilter(canp, sopt->sopt_data, nfilters); + mutex_exit(&canp->canp_mtx); + break; + } + default: + error = ENOPROTOOPT; + break; + } + return error; +} + +/* + * Called by getsockopt and setsockopt. + * + */ +int +can_ctloutput(int op, struct socket *so, struct sockopt *sopt) +{ + struct canpcb *canp; + int error; + int s; + + if (so->so_proto->pr_domain->dom_family != PF_CAN) + return EAFNOSUPPORT; + + if (sopt->sopt_level != SOL_CAN_RAW) + return EINVAL; + + s = splsoftnet(); + canp = sotocanpcb(so); + if (canp == NULL) { + splx(s); + return ECONNRESET; + } + + if (op == PRCO_SETOPT) { + error = can_raw_setop(canp, sopt); + } else if (op == PRCO_GETOPT) { + error = can_raw_getop(canp, sopt); + } else { + error = EINVAL; + } + splx(s); + return error; +} + +PR_WRAP_USRREQS(can) +#define can_attach can_attach_wrapper +#define can_detach can_detach_wrapper +#define can_accept can_accept_wrapper +#define can_bind can_bind_wrapper +#define can_listen can_listen_wrapper +#define can_connect can_connect_wrapper +#define can_connect2 can_connect2_wrapper +#define can_disconnect can_disconnect_wrapper +#define can_shutdown can_shutdown_wrapper +#define can_abort can_abort_wrapper +#define can_ioctl can_ioctl_wrapper +#define can_stat can_stat_wrapper +#define can_peeraddr can_peeraddr_wrapper +#define can_sockaddr can_sockaddr_wrapper +#define can_rcvd can_rcvd_wrapper +#define can_recvoob can_recvoob_wrapper +#define can_send can_send_wrapper +#define can_sendoob can_sendoob_wrapper +#define can_purgeif can_purgeif_wrapper + +const struct pr_usrreqs can_usrreqs = { + .pr_attach = can_attach, + .pr_detach = can_detach, + .pr_accept = can_accept, + .pr_bind = can_bind, + .pr_listen = can_listen, + .pr_connect = can_connect, + .pr_connect2 = can_connect2, + .pr_disconnect = can_disconnect, + .pr_shutdown = can_shutdown, + .pr_abort = can_abort, + .pr_ioctl = can_ioctl, + .pr_stat = can_stat, + .pr_peeraddr = can_peeraddr, + .pr_sockaddr = can_sockaddr, + .pr_rcvd = can_rcvd, + .pr_recvoob = can_recvoob, + .pr_send = can_send, + .pr_sendoob = can_sendoob, + .pr_purgeif = can_purgeif, +}; diff --git a/sys/netcan/can.h b/sys/netcan/can.h new file mode 100644 index 00000000000..8454b383845 --- /dev/null +++ b/sys/netcan/can.h @@ -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 */ diff --git a/sys/netcan/can_link.h b/sys/netcan/can_link.h new file mode 100644 index 00000000000..094dfee9384 --- /dev/null +++ b/sys/netcan/can_link.h @@ -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 */ + diff --git a/sys/netcan/can_pcb.c b/sys/netcan/can_pcb.c new file mode 100644 index 00000000000..057ecf05525 --- /dev/null +++ b/sys/netcan/can_pcb.c @@ -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; +} diff --git a/sys/netcan/can_pcb.h b/sys/netcan/can_pcb.h new file mode 100644 index 00000000000..8ddd527642f --- /dev/null +++ b/sys/netcan/can_pcb.h @@ -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_ */ diff --git a/sys/netcan/can_proto.c b/sys/netcan/can_proto.c new file mode 100644 index 00000000000..1d5c2cc5348 --- /dev/null +++ b/sys/netcan/can_proto.c @@ -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) +}; diff --git a/sys/netcan/can_var.h b/sys/netcan/can_var.h new file mode 100644 index 00000000000..22d9206c236 --- /dev/null +++ b/sys/netcan/can_var.h @@ -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 diff --git a/sys/netcan/files.netcan b/sys/netcan/files.netcan new file mode 100644 index 00000000000..32f20c7b302 --- /dev/null +++ b/sys/netcan/files.netcan @@ -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 diff --git a/sys/netcan/if_canloop.c b/sys/netcan/if_canloop.c new file mode 100644 index 00000000000..2fd367ff0c8 --- /dev/null +++ b/sys/netcan/if_canloop.c @@ -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, "") diff --git a/sys/rump/include/opt/opt_rumpkernel.h b/sys/rump/include/opt/opt_rumpkernel.h index c0cf25ba665..7415acc7444 100644 --- a/sys/rump/include/opt/opt_rumpkernel.h +++ b/sys/rump/include/opt/opt_rumpkernel.h @@ -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 */ diff --git a/sys/rump/include/rump/rumpdefs.h b/sys/rump/include/rump/rumpdefs.h index b60a2dc6396..25b2e481fc7 100644 --- a/sys/rump/include/rump/rumpdefs.h +++ b/sys/rump/include/rump/rumpdefs.h @@ -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 diff --git a/sys/rump/net/Makefile.rumpnetcomp b/sys/rump/net/Makefile.rumpnetcomp index f128a858148..19a766e6263 100644 --- a/sys/rump/net/Makefile.rumpnetcomp +++ b/sys/rump/net/Makefile.rumpnetcomp @@ -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) diff --git a/sys/rump/net/lib/libnetcan/Makefile b/sys/rump/net/lib/libnetcan/Makefile new file mode 100644 index 00000000000..9f5b94358cc --- /dev/null +++ b/sys/rump/net/lib/libnetcan/Makefile @@ -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> diff --git a/sys/rump/net/lib/libnetcan/NETCAN.ioconf b/sys/rump/net/lib/libnetcan/NETCAN.ioconf new file mode 100644 index 00000000000..bd36d8fd69b --- /dev/null +++ b/sys/rump/net/lib/libnetcan/NETCAN.ioconf @@ -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 diff --git a/sys/rump/net/lib/libnetcan/netcan_component.c b/sys/rump/net/lib/libnetcan/netcan_component.c new file mode 100644 index 00000000000..75291a0f5c3 --- /dev/null +++ b/sys/rump/net/lib/libnetcan/netcan_component.c @@ -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(); +} diff --git a/sys/sys/mbuf.h b/sys/sys/mbuf.h index 44d34e0beea..7c3900135ed 100644 --- a/sys/sys/mbuf.h +++ b/sys/sys/mbuf.h @@ -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 */ diff --git a/sys/sys/socket.h b/sys/sys/socket.h index dd78157d850..a6daecbdbd3 100644 --- a/sys/sys/socket.h +++ b/sys/sys/socket.h @@ -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 |
