2014-06-06 03:48:16 +04:00
|
|
|
/* $NetBSD: if_dmc.c,v 1.23 2014/06/05 23:48:16 rmind Exp $ */
|
2001-05-06 21:36:04 +04:00
|
|
|
/*
|
|
|
|
* Copyright (c) 1982, 1986 Regents of the University of California.
|
|
|
|
* 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.
|
2003-08-07 20:26:28 +04:00
|
|
|
* 3. Neither the name of the University nor the names of its contributors
|
2001-05-06 21:36:04 +04:00
|
|
|
* may be used to endorse or promote products derived from this software
|
|
|
|
* without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
|
|
|
|
*
|
|
|
|
* @(#)if_dmc.c 7.10 (Berkeley) 12/16/90
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* DMC11 device driver, internet version
|
|
|
|
*
|
|
|
|
* Bill Nesheim
|
|
|
|
* Cornell University
|
|
|
|
*
|
|
|
|
* Lou Salkind
|
|
|
|
* New York University
|
|
|
|
*/
|
|
|
|
|
2001-11-13 10:11:12 +03:00
|
|
|
#include <sys/cdefs.h>
|
2014-06-06 03:48:16 +04:00
|
|
|
__KERNEL_RCSID(0, "$NetBSD: if_dmc.c,v 1.23 2014/06/05 23:48:16 rmind Exp $");
|
2001-11-13 10:11:12 +03:00
|
|
|
|
2001-05-06 21:36:04 +04:00
|
|
|
#undef DMCDEBUG /* for base table dump on fatal error */
|
|
|
|
|
|
|
|
#include "opt_inet.h"
|
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/mbuf.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/syslog.h>
|
|
|
|
#include <sys/device.h>
|
|
|
|
|
|
|
|
#include <net/if.h>
|
|
|
|
|
|
|
|
#ifdef INET
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <netinet/in_var.h>
|
|
|
|
#endif
|
|
|
|
|
2007-10-19 15:59:34 +04:00
|
|
|
#include <sys/bus.h>
|
2001-05-06 21:36:04 +04:00
|
|
|
|
|
|
|
#include <dev/qbus/ubareg.h>
|
|
|
|
#include <dev/qbus/ubavar.h>
|
|
|
|
#include <dev/qbus/if_uba.h>
|
|
|
|
|
|
|
|
#include <dev/qbus/if_dmcreg.h>
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* output timeout value, sec.; should depend on line speed.
|
|
|
|
*/
|
|
|
|
static int dmc_timeout = 20;
|
|
|
|
|
|
|
|
#define NRCV 7
|
2005-02-26 15:45:06 +03:00
|
|
|
#define NXMT 3
|
2001-05-06 21:36:04 +04:00
|
|
|
#define NCMDS (NRCV+NXMT+4) /* size of command queue */
|
|
|
|
|
|
|
|
#define DMC_WBYTE(csr, val) \
|
|
|
|
bus_space_write_1(sc->sc_iot, sc->sc_ioh, csr, val)
|
|
|
|
#define DMC_WWORD(csr, val) \
|
|
|
|
bus_space_write_2(sc->sc_iot, sc->sc_ioh, csr, val)
|
|
|
|
#define DMC_RBYTE(csr) \
|
|
|
|
bus_space_read_1(sc->sc_iot, sc->sc_ioh, csr)
|
|
|
|
#define DMC_RWORD(csr) \
|
|
|
|
bus_space_read_2(sc->sc_iot, sc->sc_ioh, csr)
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef DMCDEBUG
|
|
|
|
#define printd if(dmcdebug)printf
|
|
|
|
int dmcdebug = 0;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* error reporting intervals */
|
|
|
|
#define DMC_RPNBFS 50
|
|
|
|
#define DMC_RPDSC 1
|
|
|
|
#define DMC_RPTMO 10
|
|
|
|
#define DMC_RPDCK 10
|
|
|
|
|
|
|
|
struct dmc_command {
|
|
|
|
char qp_cmd; /* command */
|
|
|
|
short qp_ubaddr; /* buffer address */
|
|
|
|
short qp_cc; /* character count || XMEM */
|
|
|
|
struct dmc_command *qp_next; /* next command on queue */
|
|
|
|
};
|
|
|
|
|
|
|
|
struct dmcbufs {
|
|
|
|
int ubinfo; /* from uballoc */
|
|
|
|
short cc; /* buffer size */
|
|
|
|
short flags; /* access control */
|
|
|
|
};
|
|
|
|
#define DBUF_OURS 0 /* buffer is available */
|
|
|
|
#define DBUF_DMCS 1 /* buffer claimed by somebody */
|
|
|
|
#define DBUF_XMIT 4 /* transmit buffer */
|
|
|
|
#define DBUF_RCV 8 /* receive buffer */
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* DMC software status per interface.
|
|
|
|
*
|
|
|
|
* Each interface is referenced by a network interface structure,
|
|
|
|
* sc_if, which the routing code uses to locate the interface.
|
|
|
|
* This structure contains the output queue for the interface, its address, ...
|
|
|
|
* We also have, for each interface, a set of 7 UBA interface structures
|
|
|
|
* for each, which
|
|
|
|
* contain information about the UNIBUS resources held by the interface:
|
|
|
|
* map registers, buffered data paths, etc. Information is cached in this
|
|
|
|
* structure for use by the if_uba.c routines in running the interface
|
|
|
|
* efficiently.
|
|
|
|
*/
|
|
|
|
struct dmc_softc {
|
2012-10-27 21:17:22 +04:00
|
|
|
device_t sc_dev; /* Configuration common part */
|
2001-05-06 21:36:04 +04:00
|
|
|
struct ifnet sc_if; /* network-visible interface */
|
|
|
|
short sc_oused; /* output buffers currently in use */
|
|
|
|
short sc_iused; /* input buffers given to DMC */
|
|
|
|
short sc_flag; /* flags */
|
|
|
|
struct ubinfo sc_ui; /* UBA mapping info for base table */
|
|
|
|
int sc_errors[4]; /* non-fatal error counters */
|
|
|
|
bus_space_tag_t sc_iot;
|
|
|
|
bus_addr_t sc_ioh;
|
|
|
|
bus_dma_tag_t sc_dmat;
|
|
|
|
struct evcnt sc_rintrcnt; /* Interrupt counting */
|
|
|
|
struct evcnt sc_tintrcnt; /* Interrupt counting */
|
|
|
|
#define sc_datck sc_errors[0]
|
|
|
|
#define sc_timeo sc_errors[1]
|
|
|
|
#define sc_nobuf sc_errors[2]
|
|
|
|
#define sc_disc sc_errors[3]
|
|
|
|
struct dmcbufs sc_rbufs[NRCV]; /* receive buffer info */
|
|
|
|
struct dmcbufs sc_xbufs[NXMT]; /* transmit buffer info */
|
|
|
|
struct ifubinfo sc_ifuba; /* UNIBUS resources */
|
|
|
|
struct ifrw sc_ifr[NRCV]; /* UNIBUS receive buffer maps */
|
|
|
|
struct ifxmt sc_ifw[NXMT]; /* UNIBUS receive buffer maps */
|
|
|
|
/* command queue stuff */
|
|
|
|
struct dmc_command sc_cmdbuf[NCMDS];
|
|
|
|
struct dmc_command *sc_qhead; /* head of command queue */
|
|
|
|
struct dmc_command *sc_qtail; /* tail of command queue */
|
|
|
|
struct dmc_command *sc_qactive; /* command in progress */
|
|
|
|
struct dmc_command *sc_qfreeh; /* head of list of free cmd buffers */
|
|
|
|
struct dmc_command *sc_qfreet; /* tail of list of free cmd buffers */
|
|
|
|
/* end command queue stuff */
|
|
|
|
struct dmc_base {
|
|
|
|
short d_base[128]; /* DMC base table */
|
|
|
|
} dmc_base;
|
|
|
|
};
|
|
|
|
|
2009-05-12 18:38:26 +04:00
|
|
|
static int dmcmatch(device_t, cfdata_t, void *);
|
|
|
|
static void dmcattach(device_t, device_t, void *);
|
2001-05-06 21:36:04 +04:00
|
|
|
static int dmcinit(struct ifnet *);
|
|
|
|
static void dmcrint(void *);
|
|
|
|
static void dmcxint(void *);
|
|
|
|
static void dmcdown(struct dmc_softc *sc);
|
|
|
|
static void dmcrestart(struct dmc_softc *);
|
|
|
|
static void dmcload(struct dmc_softc *, int, u_short, u_short);
|
|
|
|
static void dmcstart(struct ifnet *);
|
|
|
|
static void dmctimeout(struct ifnet *);
|
2007-03-04 08:59:00 +03:00
|
|
|
static int dmcioctl(struct ifnet *, u_long, void *);
|
2001-05-06 21:36:04 +04:00
|
|
|
static int dmcoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
|
|
|
|
struct rtentry *);
|
2009-05-12 18:38:26 +04:00
|
|
|
static void dmcreset(device_t);
|
2001-05-06 21:36:04 +04:00
|
|
|
|
2012-10-27 21:17:22 +04:00
|
|
|
CFATTACH_DECL_NEW(dmc, sizeof(struct dmc_softc),
|
2002-10-02 20:51:16 +04:00
|
|
|
dmcmatch, dmcattach, NULL, NULL);
|
2001-05-06 21:36:04 +04:00
|
|
|
|
|
|
|
/* flags */
|
|
|
|
#define DMC_RUNNING 0x01 /* device initialized */
|
|
|
|
#define DMC_BMAPPED 0x02 /* base table mapped */
|
|
|
|
#define DMC_RESTART 0x04 /* software restart in progress */
|
|
|
|
#define DMC_ONLINE 0x08 /* device running (had a RDYO) */
|
|
|
|
|
|
|
|
|
|
|
|
/* queue manipulation macros */
|
|
|
|
#define QUEUE_AT_HEAD(qp, head, tail) \
|
|
|
|
(qp)->qp_next = (head); \
|
|
|
|
(head) = (qp); \
|
|
|
|
if ((tail) == (struct dmc_command *) 0) \
|
2005-02-26 15:45:06 +03:00
|
|
|
(tail) = (head)
|
2001-05-06 21:36:04 +04:00
|
|
|
|
|
|
|
#define QUEUE_AT_TAIL(qp, head, tail) \
|
|
|
|
if ((tail)) \
|
|
|
|
(tail)->qp_next = (qp); \
|
|
|
|
else \
|
|
|
|
(head) = (qp); \
|
|
|
|
(qp)->qp_next = (struct dmc_command *) 0; \
|
|
|
|
(tail) = (qp)
|
|
|
|
|
|
|
|
#define DEQUEUE(head, tail) \
|
|
|
|
(head) = (head)->qp_next;\
|
|
|
|
if ((head) == (struct dmc_command *) 0)\
|
|
|
|
(tail) = (head)
|
|
|
|
|
|
|
|
int
|
2009-05-12 18:38:26 +04:00
|
|
|
dmcmatch(device_t parent, cfdata_t cf, void *aux)
|
2001-05-06 21:36:04 +04:00
|
|
|
{
|
|
|
|
struct uba_attach_args *ua = aux;
|
|
|
|
struct dmc_softc ssc;
|
|
|
|
struct dmc_softc *sc = &ssc;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
sc->sc_iot = ua->ua_iot;
|
|
|
|
sc->sc_ioh = ua->ua_ioh;
|
|
|
|
|
|
|
|
DMC_WBYTE(DMC_BSEL1, DMC_MCLR);
|
|
|
|
for (i = 100000; i && (DMC_RBYTE(DMC_BSEL1) & DMC_RUN) == 0; i--)
|
|
|
|
;
|
|
|
|
if ((DMC_RBYTE(DMC_BSEL1) & DMC_RUN) == 0) {
|
|
|
|
printf("dmcprobe: can't start device\n" );
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
DMC_WBYTE(DMC_BSEL0, DMC_RQI|DMC_IEI);
|
|
|
|
/* let's be paranoid */
|
|
|
|
DMC_WBYTE(DMC_BSEL0, DMC_RBYTE(DMC_BSEL0) | DMC_RQI|DMC_IEI);
|
|
|
|
DELAY(1000000);
|
|
|
|
DMC_WBYTE(DMC_BSEL1, DMC_MCLR);
|
|
|
|
for (i = 100000; i && (DMC_RBYTE(DMC_BSEL1) & DMC_RUN) == 0; i--)
|
|
|
|
;
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Interface exists: make available by filling in network interface
|
|
|
|
* record. System will initialize the interface when it is ready
|
|
|
|
* to accept packets.
|
|
|
|
*/
|
|
|
|
void
|
2009-05-12 18:38:26 +04:00
|
|
|
dmcattach(device_t parent, device_t self, void *aux)
|
2001-05-06 21:36:04 +04:00
|
|
|
{
|
|
|
|
struct uba_attach_args *ua = aux;
|
2006-03-29 22:17:36 +04:00
|
|
|
struct dmc_softc *sc = device_private(self);
|
2001-05-06 21:36:04 +04:00
|
|
|
|
2012-10-27 21:17:22 +04:00
|
|
|
sc->sc_dev = self;
|
2001-05-06 21:36:04 +04:00
|
|
|
sc->sc_iot = ua->ua_iot;
|
|
|
|
sc->sc_ioh = ua->ua_ioh;
|
|
|
|
sc->sc_dmat = ua->ua_dmat;
|
|
|
|
|
2012-10-27 21:17:22 +04:00
|
|
|
strlcpy(sc->sc_if.if_xname, device_xname(sc->sc_dev), IFNAMSIZ);
|
2001-05-06 21:36:04 +04:00
|
|
|
sc->sc_if.if_mtu = DMCMTU;
|
|
|
|
sc->sc_if.if_init = dmcinit;
|
|
|
|
sc->sc_if.if_output = dmcoutput;
|
|
|
|
sc->sc_if.if_ioctl = dmcioctl;
|
|
|
|
sc->sc_if.if_watchdog = dmctimeout;
|
|
|
|
sc->sc_if.if_flags = IFF_POINTOPOINT;
|
|
|
|
sc->sc_if.if_softc = sc;
|
2002-03-05 07:12:57 +03:00
|
|
|
IFQ_SET_READY(&sc->sc_if.if_snd);
|
2001-05-06 21:36:04 +04:00
|
|
|
|
|
|
|
uba_intr_establish(ua->ua_icookie, ua->ua_cvec, dmcrint, sc,
|
|
|
|
&sc->sc_rintrcnt);
|
|
|
|
uba_intr_establish(ua->ua_icookie, ua->ua_cvec+4, dmcxint, sc,
|
|
|
|
&sc->sc_tintrcnt);
|
2012-10-27 21:17:22 +04:00
|
|
|
uba_reset_establish(dmcreset, sc->sc_dev);
|
2001-05-06 21:36:04 +04:00
|
|
|
evcnt_attach_dynamic(&sc->sc_rintrcnt, EVCNT_TYPE_INTR, ua->ua_evcnt,
|
2012-10-27 21:17:22 +04:00
|
|
|
device_xname(sc->sc_dev), "intr");
|
2001-05-06 21:36:04 +04:00
|
|
|
evcnt_attach_dynamic(&sc->sc_tintrcnt, EVCNT_TYPE_INTR, ua->ua_evcnt,
|
2012-10-27 21:17:22 +04:00
|
|
|
device_xname(sc->sc_dev), "intr");
|
2001-05-06 21:36:04 +04:00
|
|
|
|
|
|
|
if_attach(&sc->sc_if);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reset of interface after UNIBUS reset.
|
|
|
|
* If interface is on specified UBA, reset its state.
|
|
|
|
*/
|
|
|
|
void
|
2009-05-12 18:38:26 +04:00
|
|
|
dmcreset(device_t dev)
|
2001-05-06 21:36:04 +04:00
|
|
|
{
|
2012-10-27 21:17:22 +04:00
|
|
|
struct dmc_softc *sc = device_private(dev);
|
2001-05-06 21:36:04 +04:00
|
|
|
|
|
|
|
sc->sc_flag = 0;
|
|
|
|
sc->sc_if.if_flags &= ~IFF_RUNNING;
|
|
|
|
dmcinit(&sc->sc_if);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialization of interface; reinitialize UNIBUS usage.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
dmcinit(struct ifnet *ifp)
|
|
|
|
{
|
|
|
|
struct dmc_softc *sc = ifp->if_softc;
|
|
|
|
struct ifrw *ifrw;
|
|
|
|
struct ifxmt *ifxp;
|
|
|
|
struct dmcbufs *rp;
|
|
|
|
struct dmc_command *qp;
|
|
|
|
struct ifaddr *ifa;
|
2012-10-27 21:17:22 +04:00
|
|
|
cfdata_t ui = device_cfdata(sc->sc_dev);
|
2001-05-06 21:36:04 +04:00
|
|
|
int base;
|
|
|
|
int s;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check to see that an address has been set
|
|
|
|
* (both local and destination for an address family).
|
|
|
|
*/
|
2005-01-25 00:25:09 +03:00
|
|
|
IFADDR_FOREACH(ifa, ifp)
|
2001-05-06 21:36:04 +04:00
|
|
|
if (ifa->ifa_addr->sa_family && ifa->ifa_dstaddr->sa_family)
|
|
|
|
break;
|
|
|
|
if (ifa == (struct ifaddr *) 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if ((DMC_RBYTE(DMC_BSEL1) & DMC_RUN) == 0) {
|
|
|
|
printf("dmcinit: DMC not running\n");
|
|
|
|
ifp->if_flags &= ~IFF_UP;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* map base table */
|
|
|
|
if ((sc->sc_flag & DMC_BMAPPED) == 0) {
|
|
|
|
sc->sc_ui.ui_size = sizeof(struct dmc_base);
|
2007-03-04 08:59:00 +03:00
|
|
|
sc->sc_ui.ui_vaddr = (void *)&sc->dmc_base;
|
2012-10-27 21:17:22 +04:00
|
|
|
uballoc(device_private(device_parent(sc->sc_dev)), &sc->sc_ui, 0);
|
2001-05-06 21:36:04 +04:00
|
|
|
sc->sc_flag |= DMC_BMAPPED;
|
|
|
|
}
|
|
|
|
/* initialize UNIBUS resources */
|
|
|
|
sc->sc_iused = sc->sc_oused = 0;
|
|
|
|
if ((ifp->if_flags & IFF_RUNNING) == 0) {
|
2006-03-25 07:08:45 +03:00
|
|
|
if (if_ubaminit(&sc->sc_ifuba,
|
2012-10-27 21:17:22 +04:00
|
|
|
device_private(device_parent(sc->sc_dev)),
|
2001-05-06 21:36:04 +04:00
|
|
|
sizeof(struct dmc_header) + DMCMTU,
|
|
|
|
sc->sc_ifr, NRCV, sc->sc_ifw, NXMT) == 0) {
|
2012-10-27 21:17:22 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "can't allocate uba resources\n");
|
2001-05-06 21:36:04 +04:00
|
|
|
ifp->if_flags &= ~IFF_UP;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
ifp->if_flags |= IFF_RUNNING;
|
|
|
|
}
|
|
|
|
sc->sc_flag &= ~DMC_ONLINE;
|
|
|
|
sc->sc_flag |= DMC_RUNNING;
|
|
|
|
/*
|
|
|
|
* Limit packets enqueued until we see if we're on the air.
|
|
|
|
*/
|
|
|
|
ifp->if_snd.ifq_maxlen = 3;
|
|
|
|
|
|
|
|
/* initialize buffer pool */
|
|
|
|
/* receives */
|
|
|
|
ifrw = &sc->sc_ifr[0];
|
|
|
|
for (rp = &sc->sc_rbufs[0]; rp < &sc->sc_rbufs[NRCV]; rp++) {
|
|
|
|
rp->ubinfo = ifrw->ifrw_info;
|
|
|
|
rp->cc = DMCMTU + sizeof (struct dmc_header);
|
|
|
|
rp->flags = DBUF_OURS|DBUF_RCV;
|
2005-02-26 15:45:06 +03:00
|
|
|
ifrw++;
|
2001-05-06 21:36:04 +04:00
|
|
|
}
|
|
|
|
/* transmits */
|
|
|
|
ifxp = &sc->sc_ifw[0];
|
|
|
|
for (rp = &sc->sc_xbufs[0]; rp < &sc->sc_xbufs[NXMT]; rp++) {
|
|
|
|
rp->ubinfo = ifxp->ifw_info;
|
|
|
|
rp->cc = 0;
|
|
|
|
rp->flags = DBUF_OURS|DBUF_XMIT;
|
2005-02-26 15:45:06 +03:00
|
|
|
ifxp++;
|
2001-05-06 21:36:04 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* set up command queues */
|
|
|
|
sc->sc_qfreeh = sc->sc_qfreet
|
|
|
|
= sc->sc_qhead = sc->sc_qtail = sc->sc_qactive =
|
|
|
|
(struct dmc_command *)0;
|
|
|
|
/* set up free command buffer list */
|
|
|
|
for (qp = &sc->sc_cmdbuf[0]; qp < &sc->sc_cmdbuf[NCMDS]; qp++) {
|
|
|
|
QUEUE_AT_HEAD(qp, sc->sc_qfreeh, sc->sc_qfreet);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* base in */
|
|
|
|
base = sc->sc_ui.ui_baddr;
|
|
|
|
dmcload(sc, DMC_BASEI, (u_short)base, (base>>2) & DMC_XMEM);
|
|
|
|
/* specify half duplex operation, flags tell if primary */
|
|
|
|
/* or secondary station */
|
|
|
|
if (ui->cf_flags == 0)
|
|
|
|
/* use DDCMP mode in full duplex */
|
|
|
|
dmcload(sc, DMC_CNTLI, 0, 0);
|
|
|
|
else if (ui->cf_flags == 1)
|
|
|
|
/* use MAINTENENCE mode */
|
|
|
|
dmcload(sc, DMC_CNTLI, 0, DMC_MAINT );
|
|
|
|
else if (ui->cf_flags == 2)
|
|
|
|
/* use DDCMP half duplex as primary station */
|
|
|
|
dmcload(sc, DMC_CNTLI, 0, DMC_HDPLX);
|
|
|
|
else if (ui->cf_flags == 3)
|
|
|
|
/* use DDCMP half duplex as secondary station */
|
|
|
|
dmcload(sc, DMC_CNTLI, 0, DMC_HDPLX | DMC_SEC);
|
|
|
|
|
|
|
|
/* enable operation done interrupts */
|
|
|
|
while ((DMC_RBYTE(DMC_BSEL2) & DMC_IEO) == 0)
|
|
|
|
DMC_WBYTE(DMC_BSEL2, DMC_RBYTE(DMC_BSEL2) | DMC_IEO);
|
|
|
|
s = splnet();
|
|
|
|
/* queue first NRCV buffers for DMC to fill */
|
|
|
|
for (rp = &sc->sc_rbufs[0]; rp < &sc->sc_rbufs[NRCV]; rp++) {
|
|
|
|
rp->flags |= DBUF_DMCS;
|
|
|
|
dmcload(sc, DMC_READ, rp->ubinfo,
|
|
|
|
(((rp->ubinfo>>2)&DMC_XMEM) | rp->cc));
|
|
|
|
sc->sc_iused++;
|
|
|
|
}
|
|
|
|
splx(s);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Start output on interface. Get another datagram
|
|
|
|
* to send from the interface queue and map it to
|
|
|
|
* the interface before starting output.
|
|
|
|
*
|
|
|
|
* Must be called at spl 5
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
dmcstart(struct ifnet *ifp)
|
|
|
|
{
|
|
|
|
struct dmc_softc *sc = ifp->if_softc;
|
|
|
|
struct mbuf *m;
|
|
|
|
struct dmcbufs *rp;
|
|
|
|
int n;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Dequeue up to NXMT requests and map them to the UNIBUS.
|
|
|
|
* If no more requests, or no dmc buffers available, just return.
|
|
|
|
*/
|
|
|
|
n = 0;
|
|
|
|
for (rp = &sc->sc_xbufs[0]; rp < &sc->sc_xbufs[NXMT]; rp++ ) {
|
|
|
|
/* find an available buffer */
|
|
|
|
if ((rp->flags & DBUF_DMCS) == 0) {
|
2002-03-05 07:12:57 +03:00
|
|
|
IFQ_DEQUEUE(&sc->sc_if.if_snd, m);
|
2001-05-06 21:36:04 +04:00
|
|
|
if (m == 0)
|
|
|
|
return;
|
|
|
|
/* mark it dmcs */
|
|
|
|
rp->flags |= (DBUF_DMCS);
|
|
|
|
/*
|
|
|
|
* Have request mapped to UNIBUS for transmission
|
|
|
|
* and start the output.
|
|
|
|
*/
|
|
|
|
rp->cc = if_ubaput(&sc->sc_ifuba, &sc->sc_ifw[n], m);
|
|
|
|
rp->cc &= DMC_CCOUNT;
|
|
|
|
if (++sc->sc_oused == 1)
|
|
|
|
sc->sc_if.if_timer = dmc_timeout;
|
2005-02-26 15:45:06 +03:00
|
|
|
dmcload(sc, DMC_WRITE, rp->ubinfo,
|
2001-05-06 21:36:04 +04:00
|
|
|
rp->cc | ((rp->ubinfo>>2)&DMC_XMEM));
|
|
|
|
}
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Utility routine to load the DMC device registers.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
dmcload(struct dmc_softc *sc, int type, u_short w0, u_short w1)
|
|
|
|
{
|
|
|
|
struct dmc_command *qp;
|
|
|
|
int sps;
|
|
|
|
|
|
|
|
sps = splnet();
|
|
|
|
|
|
|
|
/* grab a command buffer from the free list */
|
|
|
|
if ((qp = sc->sc_qfreeh) == (struct dmc_command *)0)
|
|
|
|
panic("dmc command queue overflow");
|
|
|
|
DEQUEUE(sc->sc_qfreeh, sc->sc_qfreet);
|
|
|
|
|
|
|
|
/* fill in requested info */
|
|
|
|
qp->qp_cmd = (type | DMC_RQI);
|
|
|
|
qp->qp_ubaddr = w0;
|
|
|
|
qp->qp_cc = w1;
|
2005-02-26 15:45:06 +03:00
|
|
|
|
2001-05-06 21:36:04 +04:00
|
|
|
if (sc->sc_qactive) { /* command in progress */
|
|
|
|
if (type == DMC_READ) {
|
|
|
|
QUEUE_AT_HEAD(qp, sc->sc_qhead, sc->sc_qtail);
|
|
|
|
} else {
|
|
|
|
QUEUE_AT_TAIL(qp, sc->sc_qhead, sc->sc_qtail);
|
|
|
|
}
|
|
|
|
} else { /* command port free */
|
|
|
|
sc->sc_qactive = qp;
|
|
|
|
DMC_WBYTE(DMC_BSEL0, qp->qp_cmd);
|
|
|
|
dmcrint(sc);
|
|
|
|
}
|
|
|
|
splx(sps);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* DMC interface receiver interrupt.
|
|
|
|
* Ready to accept another command,
|
|
|
|
* pull one off the command queue.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
dmcrint(void *arg)
|
|
|
|
{
|
|
|
|
struct dmc_softc *sc = arg;
|
|
|
|
struct dmc_command *qp;
|
|
|
|
int n;
|
|
|
|
|
|
|
|
if ((qp = sc->sc_qactive) == (struct dmc_command *) 0) {
|
2012-10-27 21:17:22 +04:00
|
|
|
printf("%s: dmcrint no command\n", device_xname(sc->sc_dev));
|
2001-05-06 21:36:04 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
while (DMC_RBYTE(DMC_BSEL0) & DMC_RDYI) {
|
|
|
|
DMC_WWORD(DMC_SEL4, qp->qp_ubaddr);
|
|
|
|
DMC_WWORD(DMC_SEL6, qp->qp_cc);
|
|
|
|
DMC_WBYTE(DMC_BSEL0, DMC_RBYTE(DMC_BSEL0) & ~(DMC_IEI|DMC_RQI));
|
|
|
|
/* free command buffer */
|
|
|
|
QUEUE_AT_HEAD(qp, sc->sc_qfreeh, sc->sc_qfreet);
|
|
|
|
while (DMC_RBYTE(DMC_BSEL0) & DMC_RDYI) {
|
|
|
|
/*
|
|
|
|
* Can't check for RDYO here 'cause
|
|
|
|
* this routine isn't reentrant!
|
|
|
|
*/
|
|
|
|
DELAY(5);
|
|
|
|
}
|
|
|
|
/* move on to next command */
|
|
|
|
if ((sc->sc_qactive = sc->sc_qhead) == (struct dmc_command *)0)
|
|
|
|
break; /* all done */
|
|
|
|
/* more commands to do, start the next one */
|
|
|
|
qp = sc->sc_qactive;
|
|
|
|
DEQUEUE(sc->sc_qhead, sc->sc_qtail);
|
|
|
|
DMC_WBYTE(DMC_BSEL0, qp->qp_cmd);
|
|
|
|
n = RDYSCAN;
|
|
|
|
while (n-- > 0)
|
|
|
|
if ((DMC_RBYTE(DMC_BSEL0) & DMC_RDYI) ||
|
|
|
|
(DMC_RBYTE(DMC_BSEL2) & DMC_RDYO))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (sc->sc_qactive) {
|
|
|
|
DMC_WBYTE(DMC_BSEL0, DMC_RBYTE(DMC_BSEL0) & (DMC_IEI|DMC_RQI));
|
|
|
|
/* VMS does it twice !*$%@# */
|
|
|
|
DMC_WBYTE(DMC_BSEL0, DMC_RBYTE(DMC_BSEL0) & (DMC_IEI|DMC_RQI));
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* DMC interface transmitter interrupt.
|
|
|
|
* A transfer may have completed, check for errors.
|
|
|
|
* If it was a read, notify appropriate protocol.
|
|
|
|
* If it was a write, pull the next one off the queue.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
dmcxint(void *a)
|
|
|
|
{
|
2005-02-26 15:45:06 +03:00
|
|
|
struct dmc_softc *sc = a;
|
2001-05-06 21:36:04 +04:00
|
|
|
|
|
|
|
struct ifnet *ifp;
|
|
|
|
struct mbuf *m;
|
|
|
|
int arg, pkaddr, cmd, len, s;
|
|
|
|
struct ifrw *ifrw;
|
|
|
|
struct dmcbufs *rp;
|
|
|
|
struct ifxmt *ifxp;
|
|
|
|
struct dmc_header *dh;
|
|
|
|
char buf[64];
|
|
|
|
|
|
|
|
ifp = &sc->sc_if;
|
|
|
|
|
|
|
|
while (DMC_RBYTE(DMC_BSEL2) & DMC_RDYO) {
|
|
|
|
|
|
|
|
cmd = DMC_RBYTE(DMC_BSEL2) & 0xff;
|
|
|
|
arg = DMC_RWORD(DMC_SEL6) & 0xffff;
|
|
|
|
/* reconstruct UNIBUS address of buffer returned to us */
|
|
|
|
pkaddr = ((arg&DMC_XMEM)<<2) | (DMC_RWORD(DMC_SEL4) & 0xffff);
|
|
|
|
/* release port */
|
|
|
|
DMC_WBYTE(DMC_BSEL2, DMC_RBYTE(DMC_BSEL2) & ~DMC_RDYO);
|
|
|
|
switch (cmd & 07) {
|
|
|
|
|
|
|
|
case DMC_OUR:
|
|
|
|
/*
|
2005-02-26 15:45:06 +03:00
|
|
|
* A read has completed.
|
2001-05-06 21:36:04 +04:00
|
|
|
* Pass packet to type specific
|
|
|
|
* higher-level input routine.
|
|
|
|
*/
|
|
|
|
ifp->if_ipackets++;
|
|
|
|
/* find location in dmcuba struct */
|
|
|
|
ifrw= &sc->sc_ifr[0];
|
|
|
|
for (rp = &sc->sc_rbufs[0]; rp < &sc->sc_rbufs[NRCV]; rp++) {
|
|
|
|
if(rp->ubinfo == pkaddr)
|
|
|
|
break;
|
|
|
|
ifrw++;
|
|
|
|
}
|
|
|
|
if (rp >= &sc->sc_rbufs[NRCV])
|
|
|
|
panic("dmc rcv");
|
|
|
|
if ((rp->flags & DBUF_DMCS) == 0)
|
2012-10-27 21:17:22 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "done unalloc rbuf\n");
|
2001-05-06 21:36:04 +04:00
|
|
|
|
|
|
|
len = (arg & DMC_CCOUNT) - sizeof (struct dmc_header);
|
|
|
|
if (len < 0 || len > DMCMTU) {
|
|
|
|
ifp->if_ierrors++;
|
|
|
|
#ifdef DMCDEBUG
|
|
|
|
printd("%s: bad rcv pkt addr 0x%x len 0x%x\n",
|
2012-10-27 21:17:22 +04:00
|
|
|
device_xname(sc->sc_dev), pkaddr, len);
|
2001-05-06 21:36:04 +04:00
|
|
|
#endif
|
|
|
|
goto setup;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Deal with trailer protocol: if type is trailer
|
|
|
|
* get true type from first 16-bit word past data.
|
|
|
|
* Remember that type was trailer by setting off.
|
|
|
|
*/
|
|
|
|
dh = (struct dmc_header *)ifrw->ifrw_addr;
|
|
|
|
dh->dmc_type = ntohs((u_short)dh->dmc_type);
|
|
|
|
if (len == 0)
|
|
|
|
goto setup;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Pull packet off interface. Off is nonzero if
|
|
|
|
* packet has trailing header; dmc_get will then
|
|
|
|
* force this header information to be at the front,
|
|
|
|
* but we still have to drop the type and length
|
|
|
|
* which are at the front of any trailer data.
|
|
|
|
*/
|
|
|
|
m = if_ubaget(&sc->sc_ifuba, ifrw, ifp, len);
|
|
|
|
if (m == 0)
|
|
|
|
goto setup;
|
|
|
|
/* Shave off dmc_header */
|
|
|
|
m_adj(m, sizeof(struct dmc_header));
|
|
|
|
switch (dh->dmc_type) {
|
|
|
|
#ifdef INET
|
|
|
|
case DMC_IPTYPE:
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
m_freem(m);
|
|
|
|
goto setup;
|
|
|
|
}
|
|
|
|
|
|
|
|
s = splnet();
|
2014-06-06 03:48:16 +04:00
|
|
|
if (__predict_false(!pktq_enqueue(ip_pktq, m, 0))) {
|
2001-05-06 21:36:04 +04:00
|
|
|
m_freem(m);
|
2014-05-15 13:23:03 +04:00
|
|
|
}
|
2001-05-06 21:36:04 +04:00
|
|
|
splx(s);
|
|
|
|
|
|
|
|
setup:
|
|
|
|
/* is this needed? */
|
|
|
|
rp->ubinfo = ifrw->ifrw_info;
|
|
|
|
|
2005-02-26 15:45:06 +03:00
|
|
|
dmcload(sc, DMC_READ, rp->ubinfo,
|
2001-05-06 21:36:04 +04:00
|
|
|
((rp->ubinfo >> 2) & DMC_XMEM) | rp->cc);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DMC_OUX:
|
|
|
|
/*
|
|
|
|
* A write has completed, start another
|
|
|
|
* transfer if there is more data to send.
|
|
|
|
*/
|
|
|
|
ifp->if_opackets++;
|
|
|
|
/* find associated dmcbuf structure */
|
|
|
|
ifxp = &sc->sc_ifw[0];
|
|
|
|
for (rp = &sc->sc_xbufs[0]; rp < &sc->sc_xbufs[NXMT]; rp++) {
|
|
|
|
if(rp->ubinfo == pkaddr)
|
|
|
|
break;
|
|
|
|
ifxp++;
|
|
|
|
}
|
|
|
|
if (rp >= &sc->sc_xbufs[NXMT]) {
|
2012-10-27 21:17:22 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "bad packet address 0x%x\n",
|
2008-04-05 23:16:49 +04:00
|
|
|
pkaddr);
|
2001-05-06 21:36:04 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if ((rp->flags & DBUF_DMCS) == 0)
|
2012-10-27 21:17:22 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "unallocated packet 0x%x\n",
|
2008-04-05 23:16:49 +04:00
|
|
|
pkaddr);
|
2001-05-06 21:36:04 +04:00
|
|
|
/* mark buffer free */
|
|
|
|
if_ubaend(&sc->sc_ifuba, ifxp);
|
|
|
|
rp->flags &= ~DBUF_DMCS;
|
|
|
|
if (--sc->sc_oused == 0)
|
|
|
|
sc->sc_if.if_timer = 0;
|
|
|
|
else
|
|
|
|
sc->sc_if.if_timer = dmc_timeout;
|
|
|
|
if ((sc->sc_flag & DMC_ONLINE) == 0) {
|
|
|
|
extern int ifqmaxlen;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We're on the air.
|
|
|
|
* Open the queue to the usual value.
|
|
|
|
*/
|
|
|
|
sc->sc_flag |= DMC_ONLINE;
|
|
|
|
ifp->if_snd.ifq_maxlen = ifqmaxlen;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DMC_CNTLO:
|
|
|
|
arg &= DMC_CNTMASK;
|
|
|
|
if (arg & DMC_FATAL) {
|
|
|
|
if (arg != DMC_START) {
|
2008-12-17 01:35:21 +03:00
|
|
|
snprintb(buf, sizeof(buf), CNTLO_BITS,
|
|
|
|
arg);
|
2001-05-06 21:36:04 +04:00
|
|
|
log(LOG_ERR,
|
|
|
|
"%s: fatal error, flags=%s\n",
|
2012-10-27 21:17:22 +04:00
|
|
|
device_xname(sc->sc_dev), buf);
|
2001-05-06 21:36:04 +04:00
|
|
|
}
|
|
|
|
dmcrestart(sc);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* ACCUMULATE STATISTICS */
|
|
|
|
switch(arg) {
|
|
|
|
case DMC_NOBUFS:
|
|
|
|
ifp->if_ierrors++;
|
|
|
|
if ((sc->sc_nobuf++ % DMC_RPNBFS) == 0)
|
|
|
|
goto report;
|
|
|
|
break;
|
|
|
|
case DMC_DISCONN:
|
|
|
|
if ((sc->sc_disc++ % DMC_RPDSC) == 0)
|
|
|
|
goto report;
|
|
|
|
break;
|
|
|
|
case DMC_TIMEOUT:
|
|
|
|
if ((sc->sc_timeo++ % DMC_RPTMO) == 0)
|
|
|
|
goto report;
|
|
|
|
break;
|
|
|
|
case DMC_DATACK:
|
|
|
|
ifp->if_oerrors++;
|
|
|
|
if ((sc->sc_datck++ % DMC_RPDCK) == 0)
|
|
|
|
goto report;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
goto report;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
report:
|
|
|
|
#ifdef DMCDEBUG
|
2008-12-17 01:35:21 +03:00
|
|
|
snprintb(buf, sizeof(buf), CNTLO_BITS, arg);
|
2001-05-06 21:36:04 +04:00
|
|
|
printd("%s: soft error, flags=%s\n",
|
2012-10-27 21:17:22 +04:00
|
|
|
device_xname(sc->sc_dev), buf);
|
2001-05-06 21:36:04 +04:00
|
|
|
#endif
|
|
|
|
if ((sc->sc_flag & DMC_RESTART) == 0) {
|
|
|
|
/*
|
|
|
|
* kill off the dmc to get things
|
|
|
|
* going again by generating a
|
|
|
|
* procedure error
|
|
|
|
*/
|
|
|
|
sc->sc_flag |= DMC_RESTART;
|
|
|
|
arg = sc->sc_ui.ui_baddr;
|
|
|
|
dmcload(sc, DMC_BASEI, arg, (arg>>2)&DMC_XMEM);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2005-02-26 15:45:06 +03:00
|
|
|
printf("%s: bad control %o\n",
|
2012-10-27 21:17:22 +04:00
|
|
|
device_xname(sc->sc_dev), cmd);
|
2001-05-06 21:36:04 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dmcstart(ifp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* DMC output routine.
|
|
|
|
* Encapsulate a packet of type family for the dmc.
|
|
|
|
* Use trailer local net encapsulation if enough data in first
|
|
|
|
* packet leaves a multiple of 512 bytes of data in remainder.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
dmcoutput(struct ifnet *ifp, struct mbuf *m0, struct sockaddr *dst,
|
|
|
|
struct rtentry *rt)
|
|
|
|
{
|
|
|
|
int type, error, s;
|
|
|
|
struct mbuf *m = m0;
|
|
|
|
struct dmc_header *dh;
|
2002-03-05 07:12:57 +03:00
|
|
|
ALTQ_DECL(struct altq_pktattr pktattr;)
|
2001-05-06 21:36:04 +04:00
|
|
|
|
|
|
|
if ((ifp->if_flags & IFF_UP) == 0) {
|
|
|
|
error = ENETDOWN;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
2002-03-05 07:12:57 +03:00
|
|
|
IFQ_CLASSIFY(&ifp->if_snd, m, dst->sa_family, &pktattr);
|
|
|
|
|
2001-05-06 21:36:04 +04:00
|
|
|
switch (dst->sa_family) {
|
|
|
|
#ifdef INET
|
|
|
|
case AF_INET:
|
|
|
|
type = DMC_IPTYPE;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
case AF_UNSPEC:
|
|
|
|
dh = (struct dmc_header *)dst->sa_data;
|
|
|
|
type = dh->dmc_type;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
printf("%s: can't handle af%d\n", ifp->if_xname,
|
|
|
|
dst->sa_family);
|
|
|
|
error = EAFNOSUPPORT;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add local network header
|
|
|
|
* (there is space for a uba on a vax to step on)
|
|
|
|
*/
|
|
|
|
M_PREPEND(m, sizeof(struct dmc_header), M_DONTWAIT);
|
|
|
|
if (m == 0) {
|
|
|
|
error = ENOBUFS;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
dh = mtod(m, struct dmc_header *);
|
|
|
|
dh->dmc_type = htons((u_short)type);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Queue message on interface, and start output if interface
|
|
|
|
* not yet active.
|
|
|
|
*/
|
|
|
|
s = splnet();
|
2002-03-05 07:12:57 +03:00
|
|
|
IFQ_ENQUEUE(&ifp->if_snd, m, &pktattr, error);
|
|
|
|
if (error) {
|
|
|
|
/* mbuf is already freed */
|
2001-05-06 21:36:04 +04:00
|
|
|
splx(s);
|
2002-03-05 07:12:57 +03:00
|
|
|
return (error);
|
2001-05-06 21:36:04 +04:00
|
|
|
}
|
|
|
|
dmcstart(ifp);
|
|
|
|
splx(s);
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
bad:
|
|
|
|
m_freem(m0);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Process an ioctl request.
|
|
|
|
*/
|
|
|
|
/* ARGSUSED */
|
|
|
|
int
|
2007-03-04 08:59:00 +03:00
|
|
|
dmcioctl(struct ifnet *ifp, u_long cmd, void *data)
|
2001-05-06 21:36:04 +04:00
|
|
|
{
|
|
|
|
int s = splnet(), error = 0;
|
|
|
|
register struct dmc_softc *sc = ifp->if_softc;
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
|
*** Summary ***
When a link-layer address changes (e.g., ifconfig ex0 link
02:de:ad:be:ef:02 active), send a gratuitous ARP and/or a Neighbor
Advertisement to update the network-/link-layer address bindings
on our LAN peers.
Refuse a change of ethernet address to the address 00:00:00:00:00:00
or to any multicast/broadcast address. (Thanks matt@.)
Reorder ifnet ioctl operations so that driver ioctls may inherit
the functions of their "class"---ether_ioctl(), fddi_ioctl(), et
cetera---and the class ioctls may inherit from the generic ioctl,
ifioctl_common(), but both driver- and class-ioctls may override
the generic behavior. Make network drivers share more code.
Distinguish a "factory" link-layer address from others for the
purposes of both protecting that address from deletion and computing
EUI64.
Return consistent, appropriate error codes from network drivers.
Improve readability. KNF.
*** Details ***
In if_attach(), always initialize the interface ioctl routine,
ifnet->if_ioctl, if the driver has not already initialized it.
Delete if_ioctl == NULL tests everywhere else, because it cannot
happen.
In the ioctl routines of network interfaces, inherit common ioctl
behaviors by calling either ifioctl_common() or whichever ioctl
routine is appropriate for the class of interface---e.g., ether_ioctl()
for ethernets.
Stop (ab)using SIOCSIFADDR and start to use SIOCINITIFADDR. In
the user->kernel interface, SIOCSIFADDR's argument was an ifreq,
but on the protocol->ifnet interface, SIOCSIFADDR's argument was
an ifaddr. That was confusing, and it would work against me as I
make it possible for a network interface to overload most ioctls.
On the protocol->ifnet interface, replace SIOCSIFADDR with
SIOCINITIFADDR. In ifioctl(), return EPERM if userland tries to
invoke SIOCINITIFADDR.
In ifioctl(), give the interface the first shot at handling most
interface ioctls, and give the protocol the second shot, instead
of the other way around. Finally, let compatibility code (COMPAT_OSOCK)
take a shot.
Pull device initialization out of switch statements under
SIOCINITIFADDR. For example, pull ..._init() out of any switch
statement that looks like this:
switch (...->sa_family) {
case ...:
..._init();
...
break;
...
default:
..._init();
...
break;
}
Rewrite many if-else clauses that handle all permutations of IFF_UP
and IFF_RUNNING to use a switch statement,
switch (x & (IFF_UP|IFF_RUNNING)) {
case 0:
...
break;
case IFF_RUNNING:
...
break;
case IFF_UP:
...
break;
case IFF_UP|IFF_RUNNING:
...
break;
}
unifdef lots of code containing #ifdef FreeBSD, #ifdef NetBSD, and
#ifdef SIOCSIFMTU, especially in fwip(4) and in ndis(4).
In ipw(4), remove an if_set_sadl() call that is out of place.
In nfe(4), reuse the jumbo MTU logic in ether_ioctl().
Let ethernets register a callback for setting h/w state such as
promiscuous mode and the multicast filter in accord with a change
in the if_flags: ether_set_ifflags_cb() registers a callback that
returns ENETRESET if the caller should reset the ethernet by calling
if_init(), 0 on success, != 0 on failure. Pull common code from
ex(4), gem(4), nfe(4), sip(4), tlp(4), vge(4) into ether_ioctl(),
and register if_flags callbacks for those drivers.
Return ENOTTY instead of EINVAL for inappropriate ioctls. In
zyd(4), use ENXIO instead of ENOTTY to indicate that the device is
not any longer attached.
Add to if_set_sadl() a boolean 'factory' argument that indicates
whether a link-layer address was assigned by the factory or some
other source. In a comment, recommend using the factory address
for generating an EUI64, and update in6_get_hw_ifid() to prefer a
factory address to any other link-layer address.
Add a routing message, RTM_LLINFO_UPD, that tells protocols to
update the binding of network-layer addresses to link-layer addresses.
Implement this message in IPv4 and IPv6 by sending a gratuitous
ARP or a neighbor advertisement, respectively. Generate RTM_LLINFO_UPD
messages on a change of an interface's link-layer address.
In ether_ioctl(), do not let SIOCALIFADDR set a link-layer address
that is broadcast/multicast or equal to 00:00:00:00:00:00.
Make ether_ioctl() call ifioctl_common() to handle ioctls that it
does not understand.
In gif(4), initialize if_softc and use it, instead of assuming that
the gif_softc and ifp overlap.
Let ifioctl_common() handle SIOCGIFADDR.
Sprinkle rtcache_invariants(), which checks on DIAGNOSTIC kernels
that certain invariants on a struct route are satisfied.
In agr(4), rewrite agr_ioctl_filter() to be a bit more explicit
about the ioctls that we do not allow on an agr(4) member interface.
bzero -> memset. Delete unnecessary casts to void *. Use
sockaddr_in_init() and sockaddr_in6_init(). Compare pointers with
NULL instead of "testing truth". Replace some instances of (type
*)0 with NULL. Change some K&R prototypes to ANSI C, and join
lines.
2008-11-07 03:20:01 +03:00
|
|
|
case SIOCINITIFADDR:
|
2001-05-06 21:36:04 +04:00
|
|
|
ifp->if_flags |= IFF_UP;
|
|
|
|
if ((ifp->if_flags & IFF_RUNNING) == 0)
|
2005-02-26 15:45:06 +03:00
|
|
|
dmcinit(ifp);
|
2001-05-06 21:36:04 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SIOCSIFDSTADDR:
|
|
|
|
if ((ifp->if_flags & IFF_RUNNING) == 0)
|
2005-02-26 15:45:06 +03:00
|
|
|
dmcinit(ifp);
|
2001-05-06 21:36:04 +04:00
|
|
|
break;
|
2005-02-26 15:45:06 +03:00
|
|
|
|
2001-05-06 21:36:04 +04:00
|
|
|
case SIOCSIFFLAGS:
|
*** Summary ***
When a link-layer address changes (e.g., ifconfig ex0 link
02:de:ad:be:ef:02 active), send a gratuitous ARP and/or a Neighbor
Advertisement to update the network-/link-layer address bindings
on our LAN peers.
Refuse a change of ethernet address to the address 00:00:00:00:00:00
or to any multicast/broadcast address. (Thanks matt@.)
Reorder ifnet ioctl operations so that driver ioctls may inherit
the functions of their "class"---ether_ioctl(), fddi_ioctl(), et
cetera---and the class ioctls may inherit from the generic ioctl,
ifioctl_common(), but both driver- and class-ioctls may override
the generic behavior. Make network drivers share more code.
Distinguish a "factory" link-layer address from others for the
purposes of both protecting that address from deletion and computing
EUI64.
Return consistent, appropriate error codes from network drivers.
Improve readability. KNF.
*** Details ***
In if_attach(), always initialize the interface ioctl routine,
ifnet->if_ioctl, if the driver has not already initialized it.
Delete if_ioctl == NULL tests everywhere else, because it cannot
happen.
In the ioctl routines of network interfaces, inherit common ioctl
behaviors by calling either ifioctl_common() or whichever ioctl
routine is appropriate for the class of interface---e.g., ether_ioctl()
for ethernets.
Stop (ab)using SIOCSIFADDR and start to use SIOCINITIFADDR. In
the user->kernel interface, SIOCSIFADDR's argument was an ifreq,
but on the protocol->ifnet interface, SIOCSIFADDR's argument was
an ifaddr. That was confusing, and it would work against me as I
make it possible for a network interface to overload most ioctls.
On the protocol->ifnet interface, replace SIOCSIFADDR with
SIOCINITIFADDR. In ifioctl(), return EPERM if userland tries to
invoke SIOCINITIFADDR.
In ifioctl(), give the interface the first shot at handling most
interface ioctls, and give the protocol the second shot, instead
of the other way around. Finally, let compatibility code (COMPAT_OSOCK)
take a shot.
Pull device initialization out of switch statements under
SIOCINITIFADDR. For example, pull ..._init() out of any switch
statement that looks like this:
switch (...->sa_family) {
case ...:
..._init();
...
break;
...
default:
..._init();
...
break;
}
Rewrite many if-else clauses that handle all permutations of IFF_UP
and IFF_RUNNING to use a switch statement,
switch (x & (IFF_UP|IFF_RUNNING)) {
case 0:
...
break;
case IFF_RUNNING:
...
break;
case IFF_UP:
...
break;
case IFF_UP|IFF_RUNNING:
...
break;
}
unifdef lots of code containing #ifdef FreeBSD, #ifdef NetBSD, and
#ifdef SIOCSIFMTU, especially in fwip(4) and in ndis(4).
In ipw(4), remove an if_set_sadl() call that is out of place.
In nfe(4), reuse the jumbo MTU logic in ether_ioctl().
Let ethernets register a callback for setting h/w state such as
promiscuous mode and the multicast filter in accord with a change
in the if_flags: ether_set_ifflags_cb() registers a callback that
returns ENETRESET if the caller should reset the ethernet by calling
if_init(), 0 on success, != 0 on failure. Pull common code from
ex(4), gem(4), nfe(4), sip(4), tlp(4), vge(4) into ether_ioctl(),
and register if_flags callbacks for those drivers.
Return ENOTTY instead of EINVAL for inappropriate ioctls. In
zyd(4), use ENXIO instead of ENOTTY to indicate that the device is
not any longer attached.
Add to if_set_sadl() a boolean 'factory' argument that indicates
whether a link-layer address was assigned by the factory or some
other source. In a comment, recommend using the factory address
for generating an EUI64, and update in6_get_hw_ifid() to prefer a
factory address to any other link-layer address.
Add a routing message, RTM_LLINFO_UPD, that tells protocols to
update the binding of network-layer addresses to link-layer addresses.
Implement this message in IPv4 and IPv6 by sending a gratuitous
ARP or a neighbor advertisement, respectively. Generate RTM_LLINFO_UPD
messages on a change of an interface's link-layer address.
In ether_ioctl(), do not let SIOCALIFADDR set a link-layer address
that is broadcast/multicast or equal to 00:00:00:00:00:00.
Make ether_ioctl() call ifioctl_common() to handle ioctls that it
does not understand.
In gif(4), initialize if_softc and use it, instead of assuming that
the gif_softc and ifp overlap.
Let ifioctl_common() handle SIOCGIFADDR.
Sprinkle rtcache_invariants(), which checks on DIAGNOSTIC kernels
that certain invariants on a struct route are satisfied.
In agr(4), rewrite agr_ioctl_filter() to be a bit more explicit
about the ioctls that we do not allow on an agr(4) member interface.
bzero -> memset. Delete unnecessary casts to void *. Use
sockaddr_in_init() and sockaddr_in6_init(). Compare pointers with
NULL instead of "testing truth". Replace some instances of (type
*)0 with NULL. Change some K&R prototypes to ANSI C, and join
lines.
2008-11-07 03:20:01 +03:00
|
|
|
if ((error = ifioctl_common(ifp, cmd, data)) != 0)
|
|
|
|
break;
|
2001-05-06 21:36:04 +04:00
|
|
|
if ((ifp->if_flags & IFF_UP) == 0 &&
|
|
|
|
sc->sc_flag & DMC_RUNNING)
|
|
|
|
dmcdown(sc);
|
|
|
|
else if (ifp->if_flags & IFF_UP &&
|
|
|
|
(sc->sc_flag & DMC_RUNNING) == 0)
|
|
|
|
dmcrestart(sc);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
*** Summary ***
When a link-layer address changes (e.g., ifconfig ex0 link
02:de:ad:be:ef:02 active), send a gratuitous ARP and/or a Neighbor
Advertisement to update the network-/link-layer address bindings
on our LAN peers.
Refuse a change of ethernet address to the address 00:00:00:00:00:00
or to any multicast/broadcast address. (Thanks matt@.)
Reorder ifnet ioctl operations so that driver ioctls may inherit
the functions of their "class"---ether_ioctl(), fddi_ioctl(), et
cetera---and the class ioctls may inherit from the generic ioctl,
ifioctl_common(), but both driver- and class-ioctls may override
the generic behavior. Make network drivers share more code.
Distinguish a "factory" link-layer address from others for the
purposes of both protecting that address from deletion and computing
EUI64.
Return consistent, appropriate error codes from network drivers.
Improve readability. KNF.
*** Details ***
In if_attach(), always initialize the interface ioctl routine,
ifnet->if_ioctl, if the driver has not already initialized it.
Delete if_ioctl == NULL tests everywhere else, because it cannot
happen.
In the ioctl routines of network interfaces, inherit common ioctl
behaviors by calling either ifioctl_common() or whichever ioctl
routine is appropriate for the class of interface---e.g., ether_ioctl()
for ethernets.
Stop (ab)using SIOCSIFADDR and start to use SIOCINITIFADDR. In
the user->kernel interface, SIOCSIFADDR's argument was an ifreq,
but on the protocol->ifnet interface, SIOCSIFADDR's argument was
an ifaddr. That was confusing, and it would work against me as I
make it possible for a network interface to overload most ioctls.
On the protocol->ifnet interface, replace SIOCSIFADDR with
SIOCINITIFADDR. In ifioctl(), return EPERM if userland tries to
invoke SIOCINITIFADDR.
In ifioctl(), give the interface the first shot at handling most
interface ioctls, and give the protocol the second shot, instead
of the other way around. Finally, let compatibility code (COMPAT_OSOCK)
take a shot.
Pull device initialization out of switch statements under
SIOCINITIFADDR. For example, pull ..._init() out of any switch
statement that looks like this:
switch (...->sa_family) {
case ...:
..._init();
...
break;
...
default:
..._init();
...
break;
}
Rewrite many if-else clauses that handle all permutations of IFF_UP
and IFF_RUNNING to use a switch statement,
switch (x & (IFF_UP|IFF_RUNNING)) {
case 0:
...
break;
case IFF_RUNNING:
...
break;
case IFF_UP:
...
break;
case IFF_UP|IFF_RUNNING:
...
break;
}
unifdef lots of code containing #ifdef FreeBSD, #ifdef NetBSD, and
#ifdef SIOCSIFMTU, especially in fwip(4) and in ndis(4).
In ipw(4), remove an if_set_sadl() call that is out of place.
In nfe(4), reuse the jumbo MTU logic in ether_ioctl().
Let ethernets register a callback for setting h/w state such as
promiscuous mode and the multicast filter in accord with a change
in the if_flags: ether_set_ifflags_cb() registers a callback that
returns ENETRESET if the caller should reset the ethernet by calling
if_init(), 0 on success, != 0 on failure. Pull common code from
ex(4), gem(4), nfe(4), sip(4), tlp(4), vge(4) into ether_ioctl(),
and register if_flags callbacks for those drivers.
Return ENOTTY instead of EINVAL for inappropriate ioctls. In
zyd(4), use ENXIO instead of ENOTTY to indicate that the device is
not any longer attached.
Add to if_set_sadl() a boolean 'factory' argument that indicates
whether a link-layer address was assigned by the factory or some
other source. In a comment, recommend using the factory address
for generating an EUI64, and update in6_get_hw_ifid() to prefer a
factory address to any other link-layer address.
Add a routing message, RTM_LLINFO_UPD, that tells protocols to
update the binding of network-layer addresses to link-layer addresses.
Implement this message in IPv4 and IPv6 by sending a gratuitous
ARP or a neighbor advertisement, respectively. Generate RTM_LLINFO_UPD
messages on a change of an interface's link-layer address.
In ether_ioctl(), do not let SIOCALIFADDR set a link-layer address
that is broadcast/multicast or equal to 00:00:00:00:00:00.
Make ether_ioctl() call ifioctl_common() to handle ioctls that it
does not understand.
In gif(4), initialize if_softc and use it, instead of assuming that
the gif_softc and ifp overlap.
Let ifioctl_common() handle SIOCGIFADDR.
Sprinkle rtcache_invariants(), which checks on DIAGNOSTIC kernels
that certain invariants on a struct route are satisfied.
In agr(4), rewrite agr_ioctl_filter() to be a bit more explicit
about the ioctls that we do not allow on an agr(4) member interface.
bzero -> memset. Delete unnecessary casts to void *. Use
sockaddr_in_init() and sockaddr_in6_init(). Compare pointers with
NULL instead of "testing truth". Replace some instances of (type
*)0 with NULL. Change some K&R prototypes to ANSI C, and join
lines.
2008-11-07 03:20:01 +03:00
|
|
|
error = ifioctl_common(ifp, cmd, data);
|
2001-05-06 21:36:04 +04:00
|
|
|
}
|
|
|
|
splx(s);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Restart after a fatal error.
|
|
|
|
* Clear device and reinitialize.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
dmcrestart(struct dmc_softc *sc)
|
|
|
|
{
|
|
|
|
int s, i;
|
2005-02-26 15:45:06 +03:00
|
|
|
|
2001-05-06 21:36:04 +04:00
|
|
|
#ifdef DMCDEBUG
|
|
|
|
/* dump base table */
|
2012-10-27 21:17:22 +04:00
|
|
|
printf("%s base table:\n", device_xname(sc->sc_dev));
|
2001-05-06 21:36:04 +04:00
|
|
|
for (i = 0; i < sizeof (struct dmc_base); i++)
|
|
|
|
printf("%o\n" ,dmc_base[unit].d_base[i]);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
dmcdown(sc);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Let the DMR finish the MCLR. At 1 Mbit, it should do so
|
|
|
|
* in about a max of 6.4 milliseconds with diagnostics enabled.
|
|
|
|
*/
|
|
|
|
for (i = 100000; i && (DMC_RBYTE(DMC_BSEL1) & DMC_RUN) == 0; i--)
|
|
|
|
;
|
|
|
|
/* Did the timer expire or did the DMR finish? */
|
|
|
|
if ((DMC_RBYTE(DMC_BSEL1) & DMC_RUN) == 0) {
|
2012-10-27 21:17:22 +04:00
|
|
|
log(LOG_ERR, "%s: M820 Test Failed\n", device_xname(sc->sc_dev));
|
2001-05-06 21:36:04 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* restart DMC */
|
|
|
|
dmcinit(&sc->sc_if);
|
|
|
|
sc->sc_flag &= ~DMC_RESTART;
|
|
|
|
s = splnet();
|
|
|
|
dmcstart(&sc->sc_if);
|
|
|
|
splx(s);
|
|
|
|
sc->sc_if.if_collisions++; /* why not? */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reset a device and mark down.
|
|
|
|
* Flush output queue and drop queue limit.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
dmcdown(struct dmc_softc *sc)
|
|
|
|
{
|
|
|
|
struct ifxmt *ifxp;
|
|
|
|
|
|
|
|
DMC_WBYTE(DMC_BSEL1, DMC_MCLR);
|
|
|
|
sc->sc_flag &= ~(DMC_RUNNING | DMC_ONLINE);
|
|
|
|
|
|
|
|
for (ifxp = sc->sc_ifw; ifxp < &sc->sc_ifw[NXMT]; ifxp++) {
|
|
|
|
#ifdef notyet
|
|
|
|
if (ifxp->ifw_xtofree) {
|
|
|
|
(void) m_freem(ifxp->ifw_xtofree);
|
|
|
|
ifxp->ifw_xtofree = 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
IF_PURGE(&sc->sc_if.if_snd);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Watchdog timeout to see that transmitted packets don't
|
|
|
|
* lose interrupts. The device has to be online (the first
|
|
|
|
* transmission may block until the other side comes up).
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
dmctimeout(struct ifnet *ifp)
|
|
|
|
{
|
|
|
|
struct dmc_softc *sc = ifp->if_softc;
|
|
|
|
char buf1[64], buf2[64];
|
|
|
|
|
|
|
|
if (sc->sc_flag & DMC_ONLINE) {
|
2008-12-17 01:35:21 +03:00
|
|
|
snprintb(buf1, sizeof(buf1), DMC0BITS,
|
|
|
|
DMC_RBYTE(DMC_BSEL0) & 0xff);
|
|
|
|
snprintb(buf2, sizeof(buf2), DMC2BITS,
|
|
|
|
DMC_RBYTE(DMC_BSEL2) & 0xff);
|
2001-05-06 21:36:04 +04:00
|
|
|
log(LOG_ERR, "%s: output timeout, bsel0=%s bsel2=%s\n",
|
2012-10-27 21:17:22 +04:00
|
|
|
device_xname(sc->sc_dev), buf1, buf2);
|
2001-05-06 21:36:04 +04:00
|
|
|
dmcrestart(sc);
|
|
|
|
}
|
|
|
|
}
|