6e4cb2b9ab
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.
1014 lines
21 KiB
C
1014 lines
21 KiB
C
/* $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,
|
|
};
|