10fe49d72c
#if NBPFILTER is no longer required in the client. This change doesn't yet add support for loading bpf as a module, since drivers can register before bpf is attached. However, callers of bpf can now be modularized. Dynamically loadable bpf could probably be done fairly easily with coordination from the stub driver and the real driver by registering attachments in the stub before the real driver is loaded and doing a handoff. ... and I'm not going to ponder the depths of unload here. Tested with i386/MONOLITHIC, modified MONOLITHIC without bpf and rump.
1747 lines
44 KiB
C
1747 lines
44 KiB
C
/* $NetBSD: tropic.c,v 1.41 2010/01/19 22:06:25 pooka Exp $ */
|
|
|
|
/*
|
|
* Ported to NetBSD by Onno van der Linden
|
|
* Many thanks to Larry Lile for sending me the IBM TROPIC documentation.
|
|
*
|
|
* Mach Operating System
|
|
* Copyright (c) 1991 Carnegie Mellon University
|
|
* Copyright (c) 1991 IBM Corporation
|
|
* All Rights Reserved.
|
|
*
|
|
* Permission to use, copy, modify and distribute this software and its
|
|
* documentation is hereby granted, provided that both the copyright
|
|
* notice and this permission notice appear in all copies of the
|
|
* software, derivative works or modified versions, and any portions
|
|
* thereof, and that both notices appear in supporting documentation,
|
|
* and that the name IBM not be used in advertising or publicity
|
|
* pertaining to distribution of the software without specific, written
|
|
* prior permission.
|
|
*
|
|
* CARNEGIE MELLON AND IBM ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS"
|
|
* CONDITION. CARNEGIE MELLON AND IBM DISCLAIM ANY LIABILITY OF ANY KIND FOR
|
|
* ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
|
|
*
|
|
* Carnegie Mellon requests users of this software to return to
|
|
*
|
|
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
|
|
* School of Computer Science
|
|
* Carnegie Mellon University
|
|
* Pittsburgh PA 15213-3890
|
|
*
|
|
* any improvements or extensions that they make and grant Carnegie Mellon
|
|
* the rights to redistribute these changes.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: tropic.c,v 1.41 2010/01/19 22:06:25 pooka Exp $");
|
|
|
|
#include "opt_inet.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/callout.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/buf.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/syslog.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/device.h>
|
|
|
|
#include <net/if.h>
|
|
#include <net/if_llc.h>
|
|
#include <net/if_ether.h>
|
|
#include <net/if_media.h>
|
|
#include <net/netisr.h>
|
|
#include <net/route.h>
|
|
#include <net/if_token.h>
|
|
|
|
#ifdef INET
|
|
#include <netinet/in.h>
|
|
#include <netinet/if_inarp.h>
|
|
#include <netinet/in_systm.h>
|
|
#include <netinet/ip.h>
|
|
#include <netinet/in_var.h>
|
|
#endif
|
|
|
|
|
|
#include <net/bpf.h>
|
|
#include <net/bpfdesc.h>
|
|
|
|
#include <sys/cpu.h>
|
|
#include <sys/bus.h>
|
|
|
|
#include <dev/ic/tropicreg.h>
|
|
#include <dev/ic/tropicvar.h>
|
|
|
|
static void tr_shutdown(void *);
|
|
static void tr_reopen(void *);
|
|
|
|
void tr_rint(struct tr_softc *);
|
|
void tr_xint(struct tr_softc *);
|
|
void tr_oldxint(struct tr_softc *);
|
|
struct mbuf *tr_get(struct tr_softc *, int, struct ifnet *);
|
|
void tr_opensap(struct tr_softc *, u_char);
|
|
int tr_mbcopy(struct tr_softc *, bus_size_t, struct mbuf *);
|
|
void tr_bcopy(struct tr_softc *, u_char *, int);
|
|
void tr_start(struct ifnet *);
|
|
void tr_oldstart(struct ifnet *);
|
|
void tr_watchdog(struct ifnet *);
|
|
int tr_mediachange(struct ifnet *);
|
|
void tr_mediastatus(struct ifnet *, struct ifmediareq *);
|
|
int tropic_mediachange(struct tr_softc *);
|
|
void tropic_mediastatus(struct tr_softc *, struct ifmediareq *);
|
|
void tr_reinit(void *);
|
|
|
|
/*
|
|
* TODO:
|
|
* clean up tr_intr: more subroutines
|
|
* IFF_LINK0 == IFM_TOK_SRCRT change to link flag implies media flag change
|
|
* IFF_LINK1 == IFM_TOK_ALLR change to link flag implies media flag change
|
|
* XXX Create receive_done queue to kill "ASB not free", but does this ever
|
|
* XXX happen ?
|
|
*/
|
|
|
|
static int media[] = {
|
|
IFM_TOKEN | IFM_TOK_UTP4,
|
|
IFM_TOKEN | IFM_TOK_STP4,
|
|
IFM_TOKEN | IFM_TOK_UTP16,
|
|
IFM_TOKEN | IFM_TOK_STP16,
|
|
IFM_TOKEN | IFM_TOK_UTP4,
|
|
IFM_TOKEN | IFM_TOK_UTP16,
|
|
IFM_TOKEN | IFM_TOK_STP4,
|
|
IFM_TOKEN | IFM_TOK_STP16
|
|
};
|
|
|
|
int
|
|
tropic_mediachange(struct tr_softc *sc)
|
|
{
|
|
if (IFM_TYPE(sc->sc_media.ifm_media) != IFM_TOKEN)
|
|
return EINVAL;
|
|
|
|
switch (IFM_SUBTYPE(sc->sc_media.ifm_media)) {
|
|
case IFM_TOK_STP16:
|
|
case IFM_TOK_UTP16:
|
|
if ((sc->sc_init_status & RSP_16) == 0) {
|
|
tr_stop(sc);
|
|
if (tr_setspeed(sc, 16))
|
|
return EINVAL;
|
|
if (tr_reset(sc))
|
|
return EINVAL;
|
|
if (tr_config(sc))
|
|
return EINVAL;
|
|
}
|
|
break;
|
|
case IFM_TOK_STP4:
|
|
case IFM_TOK_UTP4:
|
|
if ((sc->sc_init_status & RSP_16) != 0) {
|
|
tr_stop(sc);
|
|
if (tr_setspeed(sc, 4))
|
|
return EINVAL;
|
|
if (tr_reset(sc))
|
|
return EINVAL;
|
|
if (tr_config(sc))
|
|
return EINVAL;
|
|
}
|
|
break;
|
|
}
|
|
/*
|
|
* XXX Handle Early Token Release !!!!
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
tropic_mediastatus(struct tr_softc *sc, struct ifmediareq *ifmr)
|
|
{
|
|
struct ifmedia *ifm = &sc->sc_media;
|
|
|
|
ifmr->ifm_active = ifm->ifm_cur->ifm_media;
|
|
}
|
|
|
|
int
|
|
tr_config(struct tr_softc *sc)
|
|
{
|
|
if (sc->sc_init_status & FAST_PATH_TRANSMIT) {
|
|
int i;
|
|
|
|
for (i=0; i < SRB_CFP_CMDSIZE; i++)
|
|
SRB_OUTB(sc, sc->sc_srb, i, 0);
|
|
|
|
SRB_OUTB(sc, sc->sc_srb, SRB_CMD, DIR_CONFIG_FAST_PATH_RAM);
|
|
|
|
SRB_OUTW(sc, sc->sc_srb, SRB_CFP_RAMSIZE,
|
|
(16 + (sc->sc_nbuf * FP_BUF_LEN) / 8));
|
|
SRB_OUTW(sc, sc->sc_srb, SRB_CFP_BUFSIZE, FP_BUF_LEN);
|
|
|
|
/* tell adapter: command in SRB */
|
|
ACA_SETB(sc, ACA_ISRA_o, CMD_IN_SRB);
|
|
|
|
for (i = 0; i < 30000; i++) {
|
|
if (ACA_RDB(sc, ACA_ISRP_o) & SRB_RESP_INT)
|
|
break;
|
|
delay(100);
|
|
}
|
|
|
|
if (i == 30000 && sc->sc_srb == ACA_RDW(sc, ACA_WRBR)) {
|
|
aprint_error_dev(&sc->sc_dev, "no response for fast path cfg\n");
|
|
return 1;
|
|
}
|
|
|
|
ACA_RSTB(sc, ACA_ISRP_o, ~(SRB_RESP_INT));
|
|
|
|
if ((SRB_INB(sc, sc->sc_srb, SRB_RETCODE) != 0)) {
|
|
printf("%s: cfg fast path returned: 0x%02x\n",
|
|
device_xname(&sc->sc_dev),
|
|
SRB_INB(sc, sc->sc_srb, SRB_RETCODE));
|
|
return 1;
|
|
}
|
|
|
|
sc->sc_txca = SRB_INW(sc, sc->sc_srb, SRB_CFPRESP_FPXMIT);
|
|
sc->sc_srb = SRB_INW(sc, sc->sc_srb, SRB_CFPRESP_SRBADDR);
|
|
}
|
|
else {
|
|
if (sc->sc_init_status & RSP_16)
|
|
sc->sc_maxmtu = sc->sc_dhb16maxsz;
|
|
else
|
|
sc->sc_maxmtu = sc->sc_dhb4maxsz;
|
|
/*
|
|
* XXX Not completely true because Fast Path Transmit has 514 byte buffers
|
|
* XXX and TR_MAX_LINK_HDR is only correct when source-routing is used.
|
|
* XXX depending on wether source routing is used change the calculation
|
|
* XXX use IFM_TOK_SRCRT (IFF_LINK0)
|
|
* XXX recompute sc_minbuf !!
|
|
*/
|
|
sc->sc_maxmtu -= TR_MAX_LINK_HDR;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
tr_attach(struct tr_softc *sc)
|
|
{
|
|
int nmedia, *mediaptr, *defmediaptr;
|
|
int i, temp;
|
|
u_int8_t myaddr[ISO88025_ADDR_LEN];
|
|
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
|
|
|
|
if (sc->sc_init_status & FAST_PATH_TRANSMIT) {
|
|
int numbuf = 0;
|
|
|
|
switch (sc->sc_memsize) {
|
|
case 65536:
|
|
numbuf = 58;
|
|
sc->sc_maxmtu = IPMTU_4MBIT_MAX;
|
|
break;
|
|
case 32768:
|
|
numbuf = 29;
|
|
sc->sc_maxmtu = IPMTU_4MBIT_MAX;
|
|
break;
|
|
case 16384:
|
|
numbuf = 13;
|
|
sc->sc_maxmtu = IPMTU_4MBIT_MAX;
|
|
break;
|
|
case 8192:
|
|
numbuf = 5;
|
|
sc->sc_maxmtu = ISO88025_MTU;
|
|
}
|
|
|
|
sc->sc_minbuf = ((sc->sc_maxmtu + 511) / 512) + 1;
|
|
sc->sc_nbuf = numbuf;
|
|
|
|
/*
|
|
* Create circular queues caching the buffer pointers ?
|
|
*/
|
|
}
|
|
else {
|
|
/*
|
|
* MAX_MACFRAME_SIZE = DHB_SIZE - 6
|
|
* IPMTU = MAX_MACFRAME_SIZE - (14 + 18 + 8)
|
|
* (14 = header, 18 = sroute, 8 = llcsnap)
|
|
*/
|
|
|
|
switch (sc->sc_memsize) {
|
|
case 8192:
|
|
sc->sc_dhb4maxsz = 2048;
|
|
sc->sc_dhb16maxsz = 2048;
|
|
break;
|
|
case 16384:
|
|
sc->sc_dhb4maxsz = 4096;
|
|
sc->sc_dhb16maxsz = 4096;
|
|
break;
|
|
case 32768:
|
|
sc->sc_dhb4maxsz = 4464;
|
|
sc->sc_dhb16maxsz = 8192;
|
|
break;
|
|
case 65536:
|
|
sc->sc_dhb4maxsz = 4464;
|
|
sc->sc_dhb16maxsz = 8192;
|
|
break;
|
|
}
|
|
switch (MM_INB(sc, TR_DHB4_OFFSET)) {
|
|
case 0xF:
|
|
if (sc->sc_dhb4maxsz > 2048)
|
|
sc->sc_dhb4maxsz = 2048;
|
|
break;
|
|
case 0xE:
|
|
if (sc->sc_dhb4maxsz > 4096)
|
|
sc->sc_dhb4maxsz = 4096;
|
|
break;
|
|
case 0xD:
|
|
if (sc->sc_dhb4maxsz > 4464)
|
|
sc->sc_dhb4maxsz = 4464;
|
|
break;
|
|
}
|
|
|
|
switch (MM_INB(sc, TR_DHB16_OFFSET)) {
|
|
case 0xF:
|
|
if (sc->sc_dhb16maxsz > 2048)
|
|
sc->sc_dhb16maxsz = 2048;
|
|
break;
|
|
case 0xE:
|
|
if (sc->sc_dhb16maxsz > 4096)
|
|
sc->sc_dhb16maxsz = 4096;
|
|
break;
|
|
case 0xD:
|
|
if (sc->sc_dhb16maxsz > 8192)
|
|
sc->sc_dhb16maxsz = 8192;
|
|
break;
|
|
case 0xC:
|
|
if (sc->sc_dhb16maxsz > 8192)
|
|
sc->sc_dhb16maxsz = 8192;
|
|
break;
|
|
case 0xB:
|
|
if (sc->sc_dhb16maxsz > 8192)
|
|
sc->sc_dhb16maxsz = 8192;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (tr_config(sc))
|
|
return 1;
|
|
|
|
/*
|
|
* init network-visible interface
|
|
*/
|
|
strlcpy(ifp->if_xname, device_xname(&sc->sc_dev), IFNAMSIZ);
|
|
ifp->if_softc = sc;
|
|
ifp->if_ioctl = tr_ioctl;
|
|
if (sc->sc_init_status & FAST_PATH_TRANSMIT)
|
|
ifp->if_start = tr_start;
|
|
else
|
|
ifp->if_start = tr_oldstart;
|
|
ifp->if_flags = IFF_BROADCAST | IFF_NOTRAILERS;
|
|
ifp->if_watchdog = tr_watchdog;
|
|
IFQ_SET_READY(&ifp->if_snd);
|
|
|
|
switch (MM_INB(sc, TR_MEDIAS_OFFSET)) {
|
|
case 0xF:
|
|
nmedia = 1;
|
|
mediaptr = &media[6];
|
|
break;
|
|
case 0xE:
|
|
nmedia = 2;
|
|
mediaptr = &media[0];
|
|
break;
|
|
case 0xD:
|
|
nmedia = 1;
|
|
mediaptr = &media[4];
|
|
break;
|
|
default:
|
|
nmedia = 0;
|
|
mediaptr = NULL;
|
|
}
|
|
|
|
switch (MM_INB(sc, TR_RATES_OFFSET)) {
|
|
case 0xF:
|
|
/* 4 Mbps */
|
|
break;
|
|
case 0xE:
|
|
/* 16 Mbps */
|
|
if (mediaptr)
|
|
mediaptr += nmedia;
|
|
break;
|
|
case 0xD:
|
|
/* 4/16 Mbps */
|
|
nmedia *= 2;
|
|
break;
|
|
}
|
|
|
|
switch (MM_INB(sc, TR_MEDIA_OFFSET)) {
|
|
case 0xF:
|
|
/* STP */
|
|
defmediaptr = &media[6];
|
|
break;
|
|
case 0xE:
|
|
/* UTP */
|
|
defmediaptr = &media[4];
|
|
break;
|
|
case 0xD:
|
|
/* STP and UTP == a single shielded RJ45 which supports both */
|
|
/* XXX additional types in net/if_media.h ?? */
|
|
defmediaptr = &media[4];
|
|
break;
|
|
default:
|
|
defmediaptr = NULL;
|
|
}
|
|
|
|
if (defmediaptr && (sc->sc_init_status & RSP_16))
|
|
++defmediaptr;
|
|
|
|
if (sc->sc_mediachange == NULL && sc->sc_mediastatus == NULL) {
|
|
switch (MM_INB(sc, TR_TYP_OFFSET)) {
|
|
case 0x0D:
|
|
case 0x0C:
|
|
sc->sc_mediachange = tropic_mediachange;
|
|
sc->sc_mediastatus = tropic_mediastatus;
|
|
}
|
|
}
|
|
|
|
ifmedia_init(&sc->sc_media, 0, tr_mediachange, tr_mediastatus);
|
|
if (mediaptr != NULL) {
|
|
for (i = 0; i < nmedia; i++)
|
|
ifmedia_add(&sc->sc_media, mediaptr[i], 0, NULL);
|
|
if (defmediaptr)
|
|
ifmedia_set(&sc->sc_media, *defmediaptr);
|
|
else
|
|
ifmedia_set(&sc->sc_media, 0);
|
|
}
|
|
else {
|
|
ifmedia_add(&sc->sc_media, IFM_TOKEN | IFM_MANUAL, 0, NULL);
|
|
ifmedia_set(&sc->sc_media, IFM_TOKEN | IFM_MANUAL);
|
|
}
|
|
|
|
if_attach(ifp);
|
|
|
|
for (i = 0, temp = 0; i < ISO88025_ADDR_LEN; i++, temp += 4) {
|
|
myaddr[i] = (MM_INB(sc, (TR_MAC_OFFSET + temp)) & 0xf) << 4;
|
|
myaddr[i] |= MM_INB(sc, (TR_MAC_OFFSET + temp + 2)) & 0xf;
|
|
}
|
|
|
|
token_ifattach(ifp, myaddr);
|
|
|
|
printf("%s: address %s ring speed %d Mbps\n", device_xname(&sc->sc_dev),
|
|
token_sprintf(myaddr), (sc->sc_init_status & RSP_16) ? 16 : 4);
|
|
|
|
callout_init(&sc->sc_init_callout, 0);
|
|
callout_init(&sc->sc_reinit_callout, 0);
|
|
|
|
sc->sc_sdhook = shutdownhook_establish(tr_shutdown, sc);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
tr_setspeed(struct tr_softc *sc, u_int8_t speed)
|
|
{
|
|
SRB_OUTB(sc, sc->sc_srb, SRB_CMD, DIR_SET_DEFAULT_RING_SPEED);
|
|
SRB_OUTB(sc, sc->sc_srb, CMD_RETCODE, 0xfe);
|
|
SRB_OUTB(sc, sc->sc_srb, SRB_SET_DEFRSP, speed);
|
|
/* Tell adapter: command in SRB. */
|
|
ACA_SETB(sc, ACA_ISRA_o, CMD_IN_SRB);
|
|
|
|
/* Wait for it to complete. */
|
|
tr_sleep(sc);
|
|
|
|
if ((SRB_INB(sc, sc->sc_srb, SRB_RETCODE) != 0)) {
|
|
printf("%s: set default ringspeed returned: 0x%02x\n",
|
|
device_xname(&sc->sc_dev), SRB_INB(sc, sc->sc_srb, SRB_RETCODE));
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
tr_mediachange(struct ifnet *ifp)
|
|
{
|
|
struct tr_softc *sc = ifp->if_softc;
|
|
|
|
if (sc->sc_mediachange)
|
|
return ((*sc->sc_mediachange)(sc));
|
|
return EINVAL;
|
|
}
|
|
|
|
void
|
|
tr_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr)
|
|
{
|
|
struct tr_softc *sc = ifp->if_softc;
|
|
|
|
/* set LINK0 and/or LINK1 */
|
|
if (sc->sc_mediastatus)
|
|
(*sc->sc_mediastatus)(sc, ifmr);
|
|
}
|
|
|
|
int
|
|
tr_reset(struct tr_softc *sc)
|
|
{
|
|
int i;
|
|
|
|
sc->sc_srb = 0;
|
|
|
|
/*
|
|
* Reset the card.
|
|
*/
|
|
/* latch on an unconditional adapter reset */
|
|
bus_space_write_1(sc->sc_piot, sc->sc_pioh, TR_RESET, 0);
|
|
delay(50000); /* delay 50ms */
|
|
/*
|
|
* XXX set paging if we have the right type of card
|
|
*/
|
|
/* turn off adapter reset */
|
|
bus_space_write_1(sc->sc_piot, sc->sc_pioh, TR_RELEASE, 0);
|
|
|
|
/* Enable interrupts. */
|
|
|
|
ACA_SETB(sc, ACA_ISRP_e, INT_ENABLE);
|
|
|
|
/* Wait for an answer from the adapter. */
|
|
|
|
for (i = 0; i < 35000; i++) {
|
|
if (ACA_RDB(sc, ACA_ISRP_o) & SRB_RESP_INT)
|
|
break;
|
|
delay(100);
|
|
}
|
|
|
|
if (i == 35000 && sc->sc_srb == 0) {
|
|
aprint_error_dev(&sc->sc_dev, "no response from adapter after reset\n");
|
|
return 1;
|
|
}
|
|
|
|
ACA_RSTB(sc, ACA_ISRP_o, ~(SRB_RESP_INT));
|
|
|
|
ACA_OUTB(sc, ACA_RRR_e, (sc->sc_maddr >> 12));
|
|
sc->sc_srb = ACA_RDW(sc, ACA_WRBR);
|
|
if (SRB_INB(sc, sc->sc_srb, SRB_CMD) != 0x80) {
|
|
aprint_error_dev(&sc->sc_dev, "initialization incomplete, status: 0x%02x\n",
|
|
SRB_INB(sc, sc->sc_srb, SRB_CMD));
|
|
return 1;
|
|
}
|
|
if (SRB_INB(sc, sc->sc_srb, SRB_INIT_BUC) != 0) {
|
|
aprint_error_dev(&sc->sc_dev, "Bring Up Code %02x\n",
|
|
SRB_INB(sc, sc->sc_srb, SRB_INIT_BUC));
|
|
return 1;
|
|
}
|
|
|
|
sc->sc_init_status = SRB_INB(sc, sc->sc_srb, SRB_INIT_STATUS);
|
|
|
|
sc->sc_xmit_head = sc->sc_xmit_tail = 0;
|
|
|
|
/* XXX should depend on sc_resvdmem. */
|
|
if (MM_INB(sc, TR_RAM_OFFSET) == 0xB && sc->sc_memsize == 65536)
|
|
for (i = 0; i < 512; i++)
|
|
SR_OUTB(sc, 0xfe00 + i, 0);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* tr_stop - stop interface (issue a DIR.CLOSE.ADAPTER command)
|
|
*/
|
|
void
|
|
tr_stop(struct tr_softc *sc)
|
|
{
|
|
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
|
|
|
|
if ((ifp->if_flags & IFF_RUNNING) != 0) {
|
|
/*
|
|
* transmitter cannot be used from now on
|
|
*/
|
|
ifp->if_flags |= IFF_OACTIVE;
|
|
|
|
/* Close command. */
|
|
SRB_OUTB(sc, sc->sc_srb, SRB_CMD, DIR_CLOSE);
|
|
/* Tell adapter: command in SRB. */
|
|
ACA_SETB(sc, ACA_ISRA_o, CMD_IN_SRB);
|
|
|
|
/* Wait for it to complete. */
|
|
tr_sleep(sc);
|
|
sc->sc_srb = ACA_RDW(sc, ACA_WRBR);
|
|
}
|
|
}
|
|
|
|
static void
|
|
tr_shutdown(void *arg)
|
|
{
|
|
struct tr_softc *sc = arg;
|
|
|
|
tr_stop(sc);
|
|
}
|
|
|
|
void
|
|
tr_reinit(void *arg)
|
|
{
|
|
struct tr_softc *sc = arg;
|
|
int s;
|
|
|
|
s = splnet();
|
|
if (tr_reset(sc) == 0) {
|
|
if (tr_config(sc) == 0)
|
|
tr_init(arg);
|
|
}
|
|
splx(s);
|
|
}
|
|
|
|
static void
|
|
tr_reopen(void *arg)
|
|
{
|
|
int s;
|
|
|
|
s = splnet();
|
|
tr_init(arg);
|
|
splx(s);
|
|
}
|
|
|
|
/*
|
|
* tr_init - initialize network interface, open adapter for packet
|
|
* - reception and start any pending output
|
|
* - must be called at splnet
|
|
*/
|
|
void
|
|
tr_init(void *arg)
|
|
{
|
|
struct tr_softc *sc = arg;
|
|
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
|
|
bus_size_t open_srb;
|
|
int num_dhb, resvdmem, availmem, dhbsize;
|
|
|
|
if ((ifp->if_flags & IFF_RUNNING) != 0)
|
|
return;
|
|
|
|
ifp->if_flags &= ~IFF_OACTIVE;
|
|
sc->sc_xmit_head = sc->sc_xmit_tail = 0; /* XXX tr_reset() */
|
|
|
|
open_srb = sc->sc_srb;
|
|
|
|
/* Zero SRB. */
|
|
bus_space_set_region_1(sc->sc_memt, sc->sc_sramh,
|
|
open_srb, 0, SRB_OPEN_CMDSIZE);
|
|
|
|
/* Open command. */
|
|
SRB_OUTB(sc, open_srb, SRB_CMD, DIR_OPEN_ADAPTER);
|
|
/*
|
|
* XXX handle IFM_TOK_ETR !!!!
|
|
*/
|
|
/* Set open parameters in SRB. */
|
|
SRB_OUTW(sc, open_srb, SRB_OPEN_OPTIONS, OPEN_PASS_BCON_MAC);
|
|
|
|
num_dhb = 1;
|
|
|
|
if ((sc->sc_init_status & FAST_PATH_TRANSMIT) == 0) {
|
|
availmem = sc->sc_memsize;
|
|
resvdmem = RESVDMEM_SIZE + sc->sc_memreserved;
|
|
|
|
/* allow MAX of two SAPS */
|
|
SRB_OUTB(sc, open_srb, SRB_OPEN_DLCMAXSAP, 2);
|
|
resvdmem += 2 * SAPCB_SIZE;
|
|
|
|
/* allow MAX of 4 stations */
|
|
SRB_OUTB(sc, open_srb, SRB_OPEN_DLCMAXSTA, 4);
|
|
resvdmem += 4 * LSCB_SIZE;
|
|
|
|
if (sc->sc_init_status & RSP_16) {
|
|
dhbsize = sc->sc_dhb16maxsz;
|
|
}
|
|
else {
|
|
dhbsize = sc->sc_dhb4maxsz;
|
|
}
|
|
#if 0 /* XXXchb unneeded? */
|
|
if (dhbsize > 2048)
|
|
num_dhb = 2;
|
|
#endif
|
|
SRB_OUTW(sc, open_srb, SRB_OPEN_DHBLEN, dhbsize);
|
|
sc->sc_nbuf = (dhbsize + 511) / 512;
|
|
/*
|
|
* Try to leave room for two fullsized packets when
|
|
* requesting DHBs.
|
|
*/
|
|
availmem -= resvdmem;
|
|
num_dhb = (availmem / dhbsize) - 2;
|
|
if (num_dhb > 2)
|
|
num_dhb = 2; /* firmware can't cope with more DHBs */
|
|
if (num_dhb < 1)
|
|
num_dhb = 1; /* we need at least one */
|
|
}
|
|
else
|
|
SRB_OUTW(sc, open_srb, SRB_OPEN_DHBLEN, DHB_LENGTH);
|
|
|
|
SRB_OUTB(sc, open_srb, SRB_OPEN_NUMDHB, num_dhb);
|
|
SRB_OUTW(sc, open_srb, SRB_OPEN_RCVBUFLEN, RCV_BUF_LEN);
|
|
SRB_OUTW(sc, open_srb, SRB_OPEN_NUMRCVBUF, sc->sc_nbuf);
|
|
|
|
/* Tell adapter: command in SRB. */
|
|
ACA_SETB(sc, ACA_ISRA_o, CMD_IN_SRB);
|
|
|
|
}
|
|
|
|
/*
|
|
* tr_oldstart - Present transmit request to adapter
|
|
*/
|
|
void
|
|
tr_oldstart(struct ifnet *ifp)
|
|
{
|
|
struct tr_softc *sc = ifp->if_softc;
|
|
bus_size_t srb = sc->sc_srb;
|
|
|
|
if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
|
|
return;
|
|
|
|
ifp->if_flags |= IFF_OACTIVE;
|
|
|
|
/* Load SRB to request transmit. */
|
|
SRB_OUTB(sc, srb, SRB_CMD, XMIT_UI_FRM);
|
|
SRB_OUTW(sc, srb, XMIT_STATIONID, sc->exsap_station);
|
|
ACA_SETB(sc, ACA_ISRA_o, CMD_IN_SRB);
|
|
}
|
|
|
|
void
|
|
tr_start(struct ifnet *ifp)
|
|
{
|
|
struct tr_softc *sc = ifp->if_softc;
|
|
bus_size_t first_txbuf, txbuf;
|
|
struct mbuf *m0, *m;
|
|
int size, bufspace;
|
|
bus_size_t framedata;
|
|
|
|
if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
|
|
return;
|
|
|
|
|
|
next:
|
|
if (sc->sc_xmit_buffers < sc->sc_minbuf)
|
|
return;
|
|
|
|
/* if data in queue, copy mbuf chain to fast path buffers */
|
|
IFQ_DEQUEUE(&ifp->if_snd, m0);
|
|
|
|
if (m0 == 0)
|
|
return;
|
|
if (ifp->if_bpf)
|
|
bpf_ops->bpf_mtap(ifp->if_bpf, m0);
|
|
first_txbuf = txbuf = TXCA_INW(sc, TXCA_FREE_QUEUE_HEAD) - XMIT_NEXTBUF;
|
|
framedata = txbuf + XMIT_FP_DATA;
|
|
size = 0;
|
|
bufspace = FP_BUF_LEN - XMIT_FP_DATA;
|
|
for (m = m0; m; m = m->m_next) {
|
|
int len = m->m_len;
|
|
char *ptr = mtod(m, char *);
|
|
|
|
while (len >= bufspace) {
|
|
--sc->sc_xmit_buffers;
|
|
bus_space_write_region_1(sc->sc_memt, sc->sc_sramh,
|
|
framedata, ptr, bufspace);
|
|
size += bufspace;
|
|
ptr += bufspace;
|
|
len -= bufspace;
|
|
TXB_OUTW(sc, txbuf, XMIT_BUFLEN,
|
|
(FP_BUF_LEN - XMIT_FP_DATA));
|
|
txbuf = TXB_INW(sc, txbuf, XMIT_NEXTBUF) - XMIT_NEXTBUF;
|
|
framedata = txbuf + XMIT_FP_DATA;
|
|
bufspace = FP_BUF_LEN - XMIT_FP_DATA;
|
|
}
|
|
if (len > 0) {
|
|
bus_space_write_region_1(sc->sc_memt, sc->sc_sramh,
|
|
framedata, ptr, len);
|
|
size += len;
|
|
bufspace -= len;
|
|
framedata += len;
|
|
}
|
|
}
|
|
if (size % (FP_BUF_LEN - XMIT_FP_DATA)) {
|
|
--sc->sc_xmit_buffers;
|
|
TXB_OUTW(sc, txbuf, XMIT_BUFLEN,
|
|
(FP_BUF_LEN - XMIT_FP_DATA - bufspace));
|
|
}
|
|
|
|
m_freem(m0); /* free mbuf chain */
|
|
|
|
TXB_OUTB(sc, first_txbuf, XMIT_RETCODE, 0xfe);
|
|
TXB_OUTW(sc, first_txbuf, XMIT_FRAMELEN, size);
|
|
TXB_OUTW(sc, first_txbuf, XMIT_LASTBUF, (txbuf + XMIT_NEXTBUF));
|
|
TXB_OUTB(sc, first_txbuf, XMIT_CMD, XMIT_DIR_FRAME);
|
|
TXB_OUTW(sc, first_txbuf, XMIT_STATIONID, 0);
|
|
TXB_OUTB(sc, first_txbuf, XMIT_CMDCORR, sc->sc_xmit_correlator);
|
|
sc->sc_xmit_correlator = (sc->sc_xmit_correlator + 1) & 0x7f;
|
|
|
|
/*
|
|
* To prevent race conditions on 8-bit cards when reading or writing
|
|
* 16-bit values. See page 4-12 of the IBM manual.
|
|
*/
|
|
TXCA_OUTW(sc, TXCA_FREE_QUEUE_HEAD, 1);
|
|
TXCA_OUTW(sc, TXCA_FREE_QUEUE_HEAD, TXB_INW(sc, txbuf, XMIT_NEXTBUF));
|
|
|
|
ACA_SETB(sc, ACA_ISRA_o, XMIT_REQ);
|
|
|
|
ifp->if_flags |= IFF_OACTIVE;
|
|
ifp->if_opackets++;
|
|
#if 1
|
|
/* XXX do while construction */
|
|
goto next;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
* tr_intr - interrupt handler. Find the cause of the interrupt and
|
|
* service it.
|
|
*/
|
|
int
|
|
tr_intr(void *arg)
|
|
{
|
|
struct tr_softc *sc = arg;
|
|
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
|
|
u_char status; /* holds status from adapter status register */
|
|
u_char command; /* holds command from status or request block */
|
|
u_char retcode; /* holds return value from status or request block */
|
|
int rc = 0; /* 0 = unclaimed interrupt, 1 = interrupt claimed */
|
|
|
|
status = ACA_RDB(sc, ACA_ISRP_o);
|
|
while (status != 0) {
|
|
|
|
/* Is this interrupt caused by an adapter check? */
|
|
if (status & ADAP_CHK_INT) {
|
|
printf("%s: adapter check 0x%04x\n",
|
|
device_xname(&sc->sc_dev),
|
|
(unsigned int)ntohs(ACA_RDW(sc, ACA_WWCR)));
|
|
|
|
/* Clear this interrupt bit */
|
|
ACA_RSTB(sc, ACA_ISRP_o, ~(ADAP_CHK_INT));
|
|
|
|
rc = 1; /* Claim interrupt. */
|
|
break; /* Terminate loop. */
|
|
}
|
|
else if (status & XMIT_COMPLETE) {
|
|
ACA_RSTB(sc, ACA_ISRP_o, ~(XMIT_COMPLETE));
|
|
tr_xint(sc);
|
|
rc = 1;
|
|
}
|
|
|
|
/*
|
|
* Process SRB_RESP_INT, ASB_FREE_INT, ARB_CMD_INT
|
|
* & SSB_RESP_INT in that order, ISRP-L Hi to Lo
|
|
*/
|
|
else if (status & SRB_RESP_INT) { /* Adapter response in SRB? */
|
|
bus_size_t sap_srb;
|
|
bus_size_t srb;
|
|
#ifdef TROPICDEBUG
|
|
bus_size_t log_srb;
|
|
#endif
|
|
if (sc->sc_srb == 0)
|
|
sc->sc_srb = ACA_RDW(sc, ACA_WRBR);
|
|
srb = sc->sc_srb; /* pointer to SRB */
|
|
retcode = SRB_INB(sc, srb, SRB_RETCODE);
|
|
command = SRB_INB(sc, srb, SRB_CMD);
|
|
switch (command) {
|
|
case 0x80: /* 0x80 == initialization complete */
|
|
case DIR_CONFIG_FAST_PATH_RAM:
|
|
break;
|
|
case XMIT_DIR_FRAME: /* Response to xmit request */
|
|
case XMIT_UI_FRM: /* Response to xmit request */
|
|
/* Response not valid? */
|
|
if (retcode != 0xff)
|
|
aprint_error_dev(&sc->sc_dev, "error on xmit request = "
|
|
"0x%x\n", retcode);
|
|
break;
|
|
|
|
case DIR_OPEN_ADAPTER: /* open-adapter-cmd response */
|
|
/* Open successful? */
|
|
if (retcode == 0) {
|
|
ifp->if_flags |= IFF_UP | IFF_RUNNING;
|
|
/* Save new ACA ctrl block addresses */
|
|
sc->sc_ssb = SRB_INW(sc, srb,
|
|
SRB_OPENRESP_SSBADDR);
|
|
sc->sc_arb = SRB_INW(sc, srb,
|
|
SRB_OPENRESP_ARBADDR);
|
|
sc->sc_srb = SRB_INW(sc, srb,
|
|
SRB_OPENRESP_SRBADDR);
|
|
sc->sc_asb = SRB_INW(sc, srb,
|
|
SRB_OPENRESP_ASBADDR);
|
|
|
|
/*
|
|
* XXX, what about LLC_{X25,ISO}_LSAP ?
|
|
* open two more saps .....
|
|
*/
|
|
if (sc->sc_init_status &
|
|
FAST_PATH_TRANSMIT) {
|
|
sc->sc_xmit_buffers =
|
|
TXCA_INW(sc,
|
|
TXCA_BUFFER_COUNT);
|
|
sc->sc_nbuf =
|
|
sc->sc_xmit_buffers;
|
|
#ifdef TROPICDEBUG
|
|
printf("%s: %d buffers\n",
|
|
device_xname(&sc->sc_dev),
|
|
sc->sc_xmit_buffers);
|
|
#endif
|
|
sc->sc_xmit_correlator = 0;
|
|
wakeup(&sc->tr_sleepevent);
|
|
}
|
|
else
|
|
tr_opensap(sc, LLC_SNAP_LSAP);
|
|
}
|
|
else {
|
|
aprint_error_dev(&sc->sc_dev, "open error = 0x%x\n",
|
|
SRB_INB(sc, srb, SRB_RETCODE));
|
|
ifp->if_flags &= ~IFF_RUNNING;
|
|
ifp->if_flags &= ~IFF_UP;
|
|
/*
|
|
* XXX untimeout depending on the error, timeout in other cases
|
|
* XXX error 0x24 && autospeed mode: open again !!!!
|
|
*/
|
|
callout_reset(&sc->sc_init_callout,
|
|
hz * 30, tr_reopen, sc);
|
|
}
|
|
break;
|
|
|
|
case DIR_CLOSE: /* Response to close adapter command */
|
|
/* Close not successful? */
|
|
if (retcode != 0)
|
|
aprint_error_dev(&sc->sc_dev, "close error = 0x%x\n", retcode);
|
|
else {
|
|
ifp->if_flags &= ~IFF_RUNNING;
|
|
ifp->if_flags &= ~IFF_UP;
|
|
ifp->if_flags &= ~IFF_OACTIVE;
|
|
wakeup(&sc->tr_sleepevent);
|
|
}
|
|
break;
|
|
case DIR_SET_DEFAULT_RING_SPEED:
|
|
wakeup(&sc->tr_sleepevent);
|
|
break;
|
|
|
|
case DLC_OPEN_SAP: /* Response to open sap cmd */
|
|
sap_srb = sc->sc_srb;
|
|
if (SRB_INB(sc, sap_srb, SRB_OPNSAP_SAPVALUE)
|
|
== LLC_SNAP_LSAP)
|
|
sc->exsap_station =
|
|
SRB_INW(sc, sap_srb,
|
|
SRB_OPNSAP_STATIONID);
|
|
printf("%s: Token Ring opened\n",
|
|
device_xname(&sc->sc_dev));
|
|
wakeup(&sc->tr_sleepevent);
|
|
break;
|
|
/* XXX DLC_CLOSE_SAP not needed ? */
|
|
case DLC_CLOSE_SAP: /* Response to close sap cmd */
|
|
break;
|
|
case DIR_READ_LOG: /* Response to read log */
|
|
/* Cmd not successful? */
|
|
if (retcode != 0)
|
|
aprint_error_dev(&sc->sc_dev, "read error log cmd err = "
|
|
"0x%x\n", retcode);
|
|
#ifdef TROPICDEBUG
|
|
log_srb = sc->sc_srb;
|
|
printf("%s: ERROR LOG:\n", device_xname(&sc->sc_dev));
|
|
printf("%s: Line=%d, Internal=%d, Burst=%d\n",
|
|
device_xname(&sc->sc_dev),
|
|
(SRB_INB(sc, log_srb, SRB_LOG_LINEERRS)),
|
|
(SRB_INB(sc, log_srb, SRB_LOG_INTERRS)),
|
|
(SRB_INB(sc, log_srb, SRB_LOG_BRSTERRS)));
|
|
printf("%s: A/C=%d, Abort=%d, Lost frames=%d\n",
|
|
device_xname(&sc->sc_dev),
|
|
(SRB_INB(sc, log_srb, SRB_LOG_ACERRS)),
|
|
(SRB_INB(sc, log_srb, SRB_LOG_ABRTERRS)),
|
|
(SRB_INB(sc, log_srb, SRB_LOG_LOSTFRMS)));
|
|
printf("%s: Receive congestion=%d, Frame copied=%d, Frequency=%d\n",
|
|
device_xname(&sc->sc_dev),
|
|
(SRB_INB(sc, log_srb, SRB_LOG_RCVCONG)),
|
|
(SRB_INB(sc, log_srb, SRB_LOG_FCPYERRS)),
|
|
(SRB_INB(sc, log_srb, SRB_LOG_FREQERRS)));
|
|
printf("%s: Token=%d\n", device_xname(&sc->sc_dev),
|
|
(SRB_INB(sc, log_srb, SRB_LOG_TOKENERRS)));
|
|
#endif /* TROPICDEBUG */
|
|
ifp->if_flags &= ~IFF_OACTIVE;
|
|
break;
|
|
default:
|
|
printf("%s: bad SRB command encountered 0x%x\n",
|
|
device_xname(&sc->sc_dev), command);
|
|
break;
|
|
}
|
|
/* clear the SRB-response interrupt bit */
|
|
ACA_RSTB(sc, ACA_ISRP_o, ~(SRB_RESP_INT));
|
|
|
|
}
|
|
|
|
else if (status & ASB_FREE_INT) { /* Is ASB Free? */
|
|
bus_size_t asb = sc->sc_asb;
|
|
|
|
/*
|
|
* Remove message from asb queue, first element in
|
|
* structure is the command. command == REC_DATA?
|
|
* size = 8 : size = 10
|
|
* reply in isra_l with (RESP_IN_ASB | ASB_FREE)
|
|
*/
|
|
retcode = ASB_INB(sc, asb, CMD_RETCODE);
|
|
command = ASB_INB(sc, asb, CMD_CMD);
|
|
switch (command) {
|
|
case REC_DATA: /* Receive */
|
|
/* Response not valid? */
|
|
if (retcode != 0xff)
|
|
aprint_error_dev(&sc->sc_dev, "ASB bad receive response = 0x%x\n", retcode);
|
|
break;
|
|
case XMIT_DIR_FRAME: /* Transmit */
|
|
case XMIT_UI_FRM: /* Transmit */
|
|
/* Response not valid? */
|
|
if (retcode != 0xff)
|
|
aprint_error_dev(&sc->sc_dev, "ASB response err on xmit = 0x%x\n", retcode);
|
|
break;
|
|
default:
|
|
aprint_error_dev(&sc->sc_dev, "invalid command in ASB = 0x%x\n", command);
|
|
break;
|
|
}
|
|
/* Clear this interrupt bit */
|
|
ACA_RSTB(sc, ACA_ISRP_o, ~(ASB_FREE_INT));
|
|
}
|
|
else if (status & ARB_CMD_INT) { /* Command for PC to handle? */
|
|
bus_size_t arb = sc->sc_arb;
|
|
|
|
command = ARB_INB(sc, arb, ARB_CMD);
|
|
switch (command) {
|
|
case DLC_STATUS: /* DLC status change */
|
|
printf("%s: ARB new DLC status = 0x%x\n",
|
|
device_xname(&sc->sc_dev),
|
|
ARB_INW(sc, arb, ARB_DLCSTAT_STATUS));
|
|
break;
|
|
case REC_DATA: /* Adapter has data for PC */
|
|
/* Call receive interrupt handler */
|
|
tr_rint(sc);
|
|
break;
|
|
|
|
case RING_STAT_CHANGE: /* Ring status change */
|
|
if (ARB_INW(sc, arb, ARB_RINGSTATUS) &
|
|
(SIGNAL_LOSS + LOBE_FAULT)){
|
|
aprint_error_dev(&sc->sc_dev, "signal loss / lobe fault\n");
|
|
ifp->if_flags &= ~IFF_RUNNING;
|
|
ifp->if_flags &= ~IFF_UP;
|
|
IFQ_PURGE(&ifp->if_snd);
|
|
callout_reset(&sc->sc_reinit_callout,
|
|
hz * 30, tr_reinit, sc);
|
|
}
|
|
else {
|
|
#ifdef TROPICDEBUG
|
|
if (ARB_INW(sc, arb, ARB_RINGSTATUS) &
|
|
~(SOFT_ERR))
|
|
printf("%s: ARB new ring status"
|
|
" = 0x%x\n",
|
|
device_xname(&sc->sc_dev),
|
|
ARB_INW(sc, arb,
|
|
ARB_RINGSTATUS));
|
|
#endif /* TROPICDEBUG */
|
|
}
|
|
if (ARB_INW(sc, arb, ARB_RINGSTATUS) &
|
|
LOG_OFLOW){
|
|
/*
|
|
* XXX CMD_IN_SRB, handle with SRB_FREE_INT ?
|
|
*/
|
|
ifp->if_flags |= IFF_OACTIVE;
|
|
SRB_OUTB(sc, sc->sc_srb, SRB_CMD,
|
|
DIR_READ_LOG);
|
|
/* Read & reset err log cmnd in SRB. */
|
|
ACA_SETB(sc, ACA_ISRA_o, CMD_IN_SRB);
|
|
}
|
|
break;
|
|
|
|
case XMIT_DATA_REQ: /* Adapter wants data to transmit */
|
|
/* Call transmit interrupt handler */
|
|
tr_oldxint(sc);
|
|
break;
|
|
|
|
default:
|
|
aprint_error_dev(&sc->sc_dev, "invalid command in ARB = 0x%x\n", command);
|
|
break;
|
|
}
|
|
|
|
/* Clear this interrupt bit */
|
|
ACA_RSTB(sc, ACA_ISRP_o, ~(ARB_CMD_INT));
|
|
|
|
/* Tell adapter that ARB is now free */
|
|
ACA_SETB(sc, ACA_ISRA_o, ARB_FREE);
|
|
}
|
|
|
|
|
|
else if (status & SSB_RESP_INT) { /* SSB resp. to SRB cmd? */
|
|
bus_size_t ssb = sc->sc_ssb;
|
|
|
|
retcode = SSB_INB(sc, ssb, SSB_RETCODE);
|
|
command = SSB_INB(sc, ssb, SSB_CMD);
|
|
switch (command) {
|
|
case XMIT_UI_FRM:
|
|
case XMIT_DIR_FRAME: /* SSB response to SRB xmit cmd */
|
|
/* collect status on last packet */
|
|
if (retcode != 0) {
|
|
printf("%s: xmit return code = 0x%x\n",
|
|
device_xname(&sc->sc_dev), retcode);
|
|
/* XXXchb */
|
|
if (retcode == 0x22) {
|
|
printf("%s: FS = 0x%2x\n",
|
|
device_xname(&sc->sc_dev),
|
|
SSB_INB(sc, ssb,
|
|
SSB_XMITERR));
|
|
}
|
|
ifp->if_oerrors++;
|
|
}
|
|
else
|
|
ifp->if_opackets++;
|
|
|
|
ifp->if_flags &= ~IFF_OACTIVE;
|
|
/*
|
|
* XXX should this be done here ?
|
|
*/
|
|
/* if data on send queue */
|
|
if (IFQ_IS_EMPTY(&ifp->if_snd) == 0)
|
|
tr_oldstart(ifp);
|
|
break;
|
|
|
|
case XMIT_XID_CMD:
|
|
printf("tr_int: xmit XID return code = 0x%x\n",
|
|
retcode);
|
|
break;
|
|
default:
|
|
aprint_error_dev(&sc->sc_dev, "SSB error, invalid command =%x\n", command);
|
|
}
|
|
/* clear this interrupt bit */
|
|
ACA_RSTB(sc, ACA_ISRP_o, ~(SSB_RESP_INT));
|
|
|
|
/* tell adapter that SSB is available */
|
|
ACA_SETB(sc, ACA_ISRA_o, SSB_FREE);
|
|
}
|
|
rc = 1; /* Claim responsibility for interrupt */
|
|
status = ACA_RDB(sc, ACA_ISRP_o);
|
|
}
|
|
/* Is this interrupt caused by an adapter error or access violation? */
|
|
if (ACA_RDB(sc, ACA_ISRP_e) & (TCR_INT | ERR_INT | ACCESS_INT)) {
|
|
printf("%s: adapter error, ISRP_e = 0x%x\n",
|
|
device_xname(&sc->sc_dev), ACA_RDB(sc, ACA_ISRP_e));
|
|
|
|
/* Clear these interrupt bits */
|
|
ACA_RSTB(sc, ACA_ISRP_e, ~(TCR_INT | ERR_INT | ACCESS_INT));
|
|
rc = 1; /* Claim responsibility for interrupt */
|
|
|
|
}
|
|
|
|
/* Clear IRQ latch in order to reenable interrupts. */
|
|
bus_space_write_1(sc->sc_piot, sc->sc_pioh, TR_CLEARINT, 0);
|
|
return (rc);
|
|
}
|
|
|
|
#ifdef notyet
|
|
int asb_reply_rcv(void)
|
|
{
|
|
}
|
|
|
|
int asb_reply_xmit(void)
|
|
{
|
|
}
|
|
|
|
int asb_response(bus_size_t asb, size_t len)
|
|
{
|
|
if (empty_queue) {
|
|
answer with RESP_IN_ASB | ASB_FREE
|
|
}
|
|
else {
|
|
put asb in queue
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
* U-B receive interrupt.
|
|
*
|
|
* in the original version, this routine had three tasks:
|
|
*
|
|
* 1. move the data into the receive buffer and set up various pointers
|
|
* in the tr_softc struct
|
|
* 2. switch on the type field for ip and arp, dropping all else
|
|
* 3. resetting the adaptor status block info (asb) and updating the
|
|
* tr_softc struct
|
|
* determine lan message type, pull packet off interface and
|
|
* pass to an appropriate higher-level routine
|
|
*
|
|
*/
|
|
void
|
|
tr_rint(struct tr_softc *sc)
|
|
{
|
|
bus_size_t arb = sc->sc_arb;
|
|
bus_size_t asb = sc->sc_asb;
|
|
struct rbcb *rbc = &sc->rbc;
|
|
struct mbuf *m;
|
|
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
|
|
|
|
#ifdef TROPICDEBUG
|
|
printf("tr_rint: arb.command = %x, arb.station_id= %x\n",
|
|
ARB_INB(sc, arb, ARB_CMD), ARB_INW(sc, arb, ARB_STATIONID));
|
|
printf("arb.buf_addr = %x, arb.lan_hdr_len = %x\n",
|
|
ARB_INW(sc, arb, ARB_RXD_BUFADDR),
|
|
ARB_INB(sc, arb, ARB_RXD_LANHDRLEN));
|
|
printf("arb.dlc_hdr_len = %d, arb.frame_len = %d\n",
|
|
ARB_INB(sc, arb, ARB_RXD_DLCHDRLEN),
|
|
ARB_INW(sc, arb, ARB_RXD_FRAMELEN));
|
|
printf("arb.msg_type = %x\n", ARB_INB(sc, arb, ARB_RXD_MSGTYPE));
|
|
#endif /* TROPICDEBUG */
|
|
/*
|
|
* copy the offset in RAM of the first receive buffer from the
|
|
* receive-data block of the adapter request block associated
|
|
* with the unit's softc struct into the receive control block.
|
|
*/
|
|
rbc->rbufp = ARB_INW(sc, arb, ARB_RXD_BUFADDR);
|
|
|
|
/*
|
|
* copy the pointer to data in first receive buffer
|
|
*/
|
|
rbc->rbuf_datap = rbc->rbufp + RB_DATA;
|
|
/*
|
|
* the token-ring header is viewed as two header structs: the physical
|
|
* header (aka TR header) with access, frame, dest, src, and routing
|
|
* information, and the logical link control header (aka LLC header)
|
|
* with dsap, ssap, llc, proto and type fields.
|
|
*
|
|
* rfc1042 requires support for unnumbered information (UI) commands,
|
|
* but does not specify a required semantic, so we'll discard them.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* if there is a second receive buffer, set up the next pointer
|
|
*/
|
|
if (RB_INW(sc, rbc->rbufp, RB_NEXTBUF))
|
|
rbc->rbufp_next = RB_INW(sc, rbc->rbufp, RB_NEXTBUF) -
|
|
RB_NEXTBUF;
|
|
else
|
|
rbc->rbufp_next = 0; /* we're finished */
|
|
|
|
rbc->data_len = RB_INW(sc, rbc->rbufp, RB_BUFLEN);
|
|
/*
|
|
* At this point we move the packet from the adapter to a chain
|
|
* of mbufs
|
|
*/
|
|
m = tr_get(sc, ARB_INW(sc, arb, ARB_RXD_FRAMELEN), ifp);
|
|
/*
|
|
* XXX Clear ARB interrupt here?
|
|
*/
|
|
/*
|
|
* XXX create a queue where the responses are buffered
|
|
* XXX but is it really needed ?
|
|
*/
|
|
|
|
if (ASB_INB(sc, asb, RECV_RETCODE) != 0xff)
|
|
printf("tr_rint: ASB IS NOT FREE!!!\n");
|
|
/*
|
|
* Load receive response into ASB.
|
|
*/
|
|
ASB_OUTB(sc, asb, RECV_CMD, REC_DATA);
|
|
ASB_OUTW(sc, asb, RECV_STATIONID, ARB_INW(sc, arb, ARB_STATIONID));
|
|
ASB_OUTW(sc, asb, RECV_RESP_RECBUFADDR,
|
|
ARB_INW(sc, arb, ARB_RXD_BUFADDR));
|
|
|
|
if (m == 0) {
|
|
/*
|
|
* Tell adapter data lost, no mbufs.
|
|
*/
|
|
ASB_OUTB(sc, asb, RECV_RETCODE, 0x20);
|
|
ACA_SETB(sc, ACA_ISRA_o, RESP_IN_ASB);
|
|
++ifp->if_ierrors;
|
|
#ifdef TROPICDEBUG
|
|
printf("tr_rint: packet dropped\n");
|
|
#endif /* TROPICDEBUG */
|
|
}
|
|
else {
|
|
/*
|
|
* Indicate successful receive.
|
|
*/
|
|
ASB_OUTB(sc, asb, RECV_RETCODE, 0);
|
|
ACA_SETB(sc, ACA_ISRA_o, RESP_IN_ASB);
|
|
++ifp->if_ipackets;
|
|
|
|
if (ifp->if_bpf)
|
|
bpf_ops->bpf_mtap(ifp->if_bpf, m);
|
|
(*ifp->if_input)(ifp, m);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Interrupt handler for old style "adapter requires data to transmit".
|
|
*/
|
|
void
|
|
tr_oldxint(struct tr_softc *sc)
|
|
{
|
|
bus_size_t arb = sc->sc_arb; /* pointer to ARB */
|
|
bus_size_t asb = sc->sc_asb; /* pointer to ASB */
|
|
bus_size_t dhb; /* pointer to DHB */
|
|
struct mbuf *m0; /* pointer to top of mbuf chain */
|
|
u_short size = 0;
|
|
char command;
|
|
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
|
|
struct token_header *trh;
|
|
int i;
|
|
u_int8_t hlen;
|
|
|
|
/*
|
|
* XXX xmit_asb_response()
|
|
*/
|
|
if (ASB_INB(sc, asb, XMIT_RETCODE) != 0xff)
|
|
printf("tr_oldxint: ASB IS NOT FREE!!!\n");
|
|
|
|
/* load parameters into ASB */
|
|
ASB_OUTB(sc, asb, XMIT_CMDCORR, ARB_INB(sc, arb, ARB_XMT_CMDCORR));
|
|
ASB_OUTW(sc, asb, XMIT_STATIONID, ARB_INW(sc, arb, ARB_STATIONID));
|
|
ASB_OUTB(sc, asb, XMIT_RETCODE, 0);
|
|
/*
|
|
* XXX LLC_{X25,ISO}_LSAP
|
|
*/
|
|
ASB_OUTB(sc, asb, XMIT_REMSAP, LLC_SNAP_LSAP);
|
|
|
|
/* XXX if num_dhb == 2 this should alternate between the two buffers */
|
|
dhb = ARB_INW(sc, arb, ARB_XMT_DHBADDR);
|
|
|
|
command = SRB_INB(sc, sc->sc_srb, SRB_CMD);
|
|
|
|
if (command == XMIT_XID_CMD || command == XMIT_TEST_CMD) {
|
|
ASB_OUTB(sc, asb, XMIT_CMD, command);
|
|
ASB_OUTW(sc, asb, XMIT_FRAMELEN, 0x11);
|
|
/*
|
|
* XXX 0xe == sizeof(struct token_header)
|
|
*/
|
|
ASB_OUTB(sc, asb, XMIT_HDRLEN, 0x0e);
|
|
|
|
SR_OUTB(sc, (dhb + 0), TOKEN_AC);
|
|
SR_OUTB(sc, (dhb + 1), TOKEN_FC);
|
|
/* Load destination and source addresses. */
|
|
for (i=0; i < ISO88025_ADDR_LEN; i++) {
|
|
SR_OUTB(sc, (dhb + 2 + i), 0xff);
|
|
SR_OUTB(sc, (dhb + 8 + i), 0x00);
|
|
}
|
|
}
|
|
else {
|
|
/*
|
|
* XXX what's command here ? command = 0x0d (always ?)
|
|
*/
|
|
/* if data in queue, copy mbuf chain to DHB */
|
|
IFQ_DEQUEUE(&ifp->if_snd, m0);
|
|
if (m0 != 0) {
|
|
if (ifp->if_bpf)
|
|
bpf_ops->bpf_mtap(ifp->if_bpf, m0);
|
|
/* Pull packet off interface send queue, fill DHB. */
|
|
trh = mtod(m0, struct token_header *);
|
|
hlen = sizeof(struct token_header);
|
|
if (trh->token_shost[0] & TOKEN_RI_PRESENT) {
|
|
/*
|
|
* XXX assumes route info is in the same mbuf as the token-ring header
|
|
*/
|
|
struct token_rif *rif;
|
|
|
|
rif = TOKEN_RIF(trh);
|
|
hlen += ((ntohs(rif->tr_rcf) & TOKEN_RCF_LEN_MASK) >> 8);
|
|
}
|
|
size = tr_mbcopy(sc, dhb, m0);
|
|
m_freem(m0);
|
|
|
|
ASB_OUTB(sc, asb, XMIT_CMD, XMIT_UI_FRM);
|
|
ASB_OUTB(sc, asb, XMIT_HDRLEN, hlen);
|
|
|
|
/* Set size of transmission frame in ASB. */
|
|
ASB_OUTW(sc, asb, XMIT_FRAMELEN, size);
|
|
}
|
|
else {
|
|
aprint_error_dev(&sc->sc_dev, "unexpected empty mbuf send queue\n");
|
|
|
|
/* Set size of transmission frame in ASB to zero. */
|
|
ASB_OUTW(sc, asb, XMIT_FRAMELEN, 0);
|
|
}
|
|
}
|
|
/*
|
|
* XXX asb_response(void *asb, len)
|
|
*/
|
|
/* tell adapter that there is a response in the ASB */
|
|
ACA_SETB(sc, ACA_ISRA_o, RESP_IN_ASB);
|
|
}
|
|
|
|
/*
|
|
* Interrupt handler for fast path transmit complete
|
|
*/
|
|
void
|
|
tr_xint(struct tr_softc *sc)
|
|
{
|
|
u_short tail;
|
|
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
|
|
bus_size_t txbuf;
|
|
|
|
/*
|
|
* To prevent race conditions on 8-bit cards when reading or writing
|
|
* 16-bit values. See page 4-12 of the IBM manual.
|
|
* XXX use volatile ?
|
|
*/
|
|
do {
|
|
tail = TXCA_INW(sc, TXCA_COMPLETION_QUEUE_TAIL);
|
|
} while (tail != TXCA_INW(sc, TXCA_COMPLETION_QUEUE_TAIL));
|
|
while (tail != TXCA_INW(sc, TXCA_FREE_QUEUE_TAIL)) {
|
|
txbuf = TXCA_INW(sc, TXCA_FREE_QUEUE_TAIL) - XMIT_NEXTBUF;
|
|
txbuf = TXB_INW(sc, txbuf, XMIT_NEXTBUF) - XMIT_NEXTBUF;
|
|
if (TXB_INB(sc, txbuf, XMIT_RETCODE) != 0) {
|
|
ifp->if_oerrors++;
|
|
aprint_error_dev(&sc->sc_dev, "xmit error = 0x%x\n",
|
|
TXB_INB(sc, txbuf, XMIT_RETCODE));
|
|
}
|
|
sc->sc_xmit_buffers +=
|
|
(TXB_INW(sc, txbuf, XMIT_FRAMELEN) + 514 - 1) / 514;
|
|
tail = TXB_INW(sc, txbuf, XMIT_LASTBUF);
|
|
TXCA_OUTW(sc, TXCA_FREE_QUEUE_TAIL, tail);
|
|
tail = TXCA_INW(sc, TXCA_COMPLETION_QUEUE_TAIL);
|
|
do {
|
|
tail = TXCA_INW(sc, TXCA_COMPLETION_QUEUE_TAIL);
|
|
} while (tail != TXCA_INW(sc, TXCA_COMPLETION_QUEUE_TAIL));
|
|
}
|
|
if (sc->sc_xmit_buffers == sc->sc_nbuf)
|
|
ifp->if_flags &= ~IFF_OACTIVE;
|
|
tr_start(ifp);
|
|
}
|
|
|
|
|
|
/*
|
|
* copy out the packet byte-by-byte in reasonably optimal fashion
|
|
*/
|
|
int
|
|
tr_mbcopy(struct tr_softc *sc, bus_size_t dhb, struct mbuf *m0)
|
|
{
|
|
bus_size_t addr = dhb;
|
|
int len, size = 0;
|
|
char *ptr;
|
|
struct mbuf *m;
|
|
|
|
for (m = m0; m; m = m->m_next) {
|
|
len = m->m_len;
|
|
ptr = mtod(m, char *);
|
|
|
|
bus_space_write_region_1(sc->sc_memt, sc->sc_sramh,
|
|
addr, ptr, len);
|
|
size += len;
|
|
addr += len;
|
|
}
|
|
return (size);
|
|
}
|
|
|
|
/*
|
|
* Pull read data off an interface.
|
|
* Len is length of data, with local net header stripped.
|
|
* Off is non-zero if a trailer protocol was used, and
|
|
* gives the offset of the trailer information.
|
|
* XXX trailer information, really ????
|
|
* We copy the trailer information and then all the normal
|
|
* data into mbufs.
|
|
*
|
|
* called from tr_rint - receive interrupt routine
|
|
*/
|
|
struct mbuf *
|
|
tr_get(struct tr_softc *sc, int totlen, struct ifnet *ifp)
|
|
{
|
|
int len;
|
|
struct mbuf *m, *m0, *newm;
|
|
|
|
MGETHDR(m0, M_DONTWAIT, MT_DATA);
|
|
if (m0 == 0)
|
|
return (0);
|
|
|
|
m0->m_pkthdr.rcvif = ifp;
|
|
m0->m_pkthdr.len = totlen;
|
|
len = MHLEN;
|
|
|
|
m = m0;
|
|
while (totlen > 0) {
|
|
if (totlen >= MINCLSIZE) {
|
|
MCLGET(m, M_DONTWAIT);
|
|
if ((m->m_flags & M_EXT) == 0) {
|
|
m_free(m0);
|
|
return 0;
|
|
}
|
|
len = MCLBYTES;
|
|
}
|
|
|
|
/*
|
|
* Make sure data after the MAC header is aligned.
|
|
*/
|
|
if (m == m0) {
|
|
char *newdata = (char *)
|
|
ALIGN(m->m_data + sizeof(struct token_header)) -
|
|
sizeof(struct token_header);
|
|
len -= newdata - m->m_data;
|
|
m->m_data = newdata;
|
|
}
|
|
m->m_len = len = min(totlen, len);
|
|
tr_bcopy(sc, mtod(m, char *), len);
|
|
totlen -= len;
|
|
if (totlen > 0) {
|
|
MGET(newm, M_DONTWAIT, MT_DATA);
|
|
if (newm == 0){
|
|
m_freem(m0);
|
|
return (0);
|
|
}
|
|
m->m_next = newm;
|
|
m = newm;
|
|
len = MLEN;
|
|
}
|
|
/*
|
|
* ignore trailers case again
|
|
*/
|
|
}
|
|
return (m0);
|
|
}
|
|
|
|
/*
|
|
* tr_ioctl - process an ioctl request
|
|
*/
|
|
int
|
|
tr_ioctl(struct ifnet *ifp, u_long cmd, void *data)
|
|
{
|
|
struct tr_softc *sc = ifp->if_softc;
|
|
struct ifreq *ifr = (struct ifreq *) data;
|
|
struct ifaddr *ifa = (struct ifaddr *) data;
|
|
int s;
|
|
int error = 0;
|
|
|
|
s = splnet();
|
|
|
|
switch (cmd) {
|
|
case SIOCINITIFADDR:
|
|
if ((error = tr_enable(sc)) != 0)
|
|
break;
|
|
|
|
/* XXX if not running */
|
|
if ((ifp->if_flags & IFF_RUNNING) == 0) {
|
|
tr_init(sc); /* before arp_ifinit/arpwhohas */
|
|
tr_sleep(sc);
|
|
}
|
|
switch (ifa->ifa_addr->sa_family) {
|
|
#ifdef INET
|
|
case AF_INET:
|
|
arp_ifinit(ifp, ifa);
|
|
break;
|
|
#endif /*INET*/
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case SIOCSIFFLAGS:
|
|
if ((error = ifioctl_common(ifp, cmd, data)) != 0)
|
|
break;
|
|
/*
|
|
* 1- If the adapter is DOWN , turn the device off
|
|
* ie. adapter down but still running
|
|
* 2- If the adapter is UP, turn the device on
|
|
* ie. adapter up but not running yet
|
|
*/
|
|
switch (ifp->if_flags & (IFF_UP|IFF_RUNNING)) {
|
|
case IFF_RUNNING:
|
|
tr_stop(sc);
|
|
ifp->if_flags &= ~IFF_RUNNING;
|
|
tr_disable(sc);
|
|
break;
|
|
case IFF_UP:
|
|
if ((error = tr_enable(sc)) != 0)
|
|
break;
|
|
tr_init(sc);
|
|
tr_sleep(sc);
|
|
break;
|
|
default:
|
|
/*
|
|
* XXX handle other flag changes
|
|
*/
|
|
break;
|
|
}
|
|
break;
|
|
case SIOCGIFMEDIA:
|
|
case SIOCSIFMEDIA:
|
|
error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
|
|
break;
|
|
case SIOCSIFMTU:
|
|
if (ifr->ifr_mtu > sc->sc_maxmtu)
|
|
error = EINVAL;
|
|
else if ((error = ifioctl_common(ifp, cmd, data)) == ENETRESET)
|
|
error = 0;
|
|
break;
|
|
default:
|
|
error = ifioctl_common(ifp, cmd, data);
|
|
}
|
|
splx(s);
|
|
return (error);
|
|
}
|
|
|
|
|
|
/*
|
|
* tr_bcopy - like bcopy except that it knows about the structure of
|
|
* adapter receive buffers.
|
|
*/
|
|
void
|
|
tr_bcopy(struct tr_softc *sc, u_char *dest, int len)
|
|
/* sc: pointer to softc struct for this adapter */
|
|
/* dest: destination address */
|
|
/* len: number of bytes to copy */
|
|
{
|
|
struct rbcb *rbc = &sc->rbc; /* pointer to rec buf ctl blk */
|
|
|
|
/* While amount of data needed >= amount in current receive buffer. */
|
|
while (len >= rbc->data_len) {
|
|
/* Copy all data from receive buffer to destination. */
|
|
|
|
bus_space_read_region_1(sc->sc_memt, sc->sc_sramh,
|
|
rbc->rbuf_datap, dest, (bus_size_t)rbc->data_len);
|
|
len -= rbc->data_len; /* update length left to transfer */
|
|
dest += rbc->data_len; /* update destination address */
|
|
|
|
/* Make next receive buffer current receive buffer. */
|
|
rbc->rbufp = rbc->rbufp_next;
|
|
if (rbc->rbufp != 0) { /* More receive buffers? */
|
|
|
|
/* Calculate pointer to next receive buffer. */
|
|
rbc->rbufp_next = RB_INW(sc, rbc->rbufp, RB_NEXTBUF);
|
|
if (rbc->rbufp_next != 0)
|
|
rbc->rbufp_next -= RB_NEXTBUF;
|
|
|
|
/* Get pointer to data in current receive buffer. */
|
|
rbc->rbuf_datap = rbc->rbufp + RB_DATA;
|
|
|
|
/* Get length of data in current receive buffer. */
|
|
rbc->data_len = RB_INW(sc, rbc->rbufp, RB_BUFLEN);
|
|
}
|
|
else {
|
|
if (len != 0) /* len should equal zero. */
|
|
printf("tr_bcopy: residual data not copied\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Amount of data needed is < amount in current receive buffer. */
|
|
|
|
bus_space_read_region_1(sc->sc_memt, sc->sc_sramh,
|
|
rbc->rbuf_datap, dest, (bus_size_t)len);
|
|
rbc->data_len -= len; /* Update count of data in receive buffer. */
|
|
rbc->rbuf_datap += len; /* Update pointer to receive buffer data. */
|
|
}
|
|
|
|
/*
|
|
* tr_opensap - open the token ring SAP interface
|
|
*/
|
|
void
|
|
tr_opensap(struct tr_softc *sc, u_char type)
|
|
{
|
|
bus_size_t srb = sc->sc_srb;
|
|
|
|
/************************************************************************
|
|
** To use the SAP level interface, we will have to execute a **
|
|
** DLC.OPEN.SAP (pg.6-61 of the Token Ring Tech. Ref.) after we have **
|
|
** received a good return code from the DIR.OPEN.ADAPTER command. **
|
|
** We will open the IP SAP x'aa'. **
|
|
** **
|
|
** STEPS: **
|
|
** 1) Reset SRB response interrupt bit **
|
|
** 2) Use the open_sap srb. **
|
|
** 3) Fill the following fields: **
|
|
** command - x'15' **
|
|
** sap_value - x'aa' **
|
|
** sap_options- x'24' **
|
|
** **
|
|
***********************************************************************/
|
|
|
|
ACA_RSTB(sc, ACA_ISRP_o, ~(SRB_RESP_INT));
|
|
|
|
SRB_OUTB(sc, srb, SRB_CMD, DLC_OPEN_SAP);
|
|
SRB_OUTB(sc, srb, SRB_RETCODE, 0x00);
|
|
SRB_OUTW(sc, srb, SRB_OPNSAP_STATIONID, 0x0000);
|
|
SRB_OUTB(sc, srb, SRB_OPNSAP_TIMERT1, 0x00);
|
|
SRB_OUTB(sc, srb, SRB_OPNSAP_TIMERT2, 0x00);
|
|
SRB_OUTB(sc, srb, SRB_OPNSAP_TIMERTI, 0x00);
|
|
SRB_OUTB(sc, srb, SRB_OPNSAP_MAXOUT, 0x00);
|
|
SRB_OUTB(sc, srb, SRB_OPNSAP_MAXIN, 0x00);
|
|
SRB_OUTB(sc, srb, SRB_OPNSAP_MAXOUTINCR, 0x00);
|
|
SRB_OUTB(sc, srb, SRB_OPNSAP_MAXRETRY, 0x00);
|
|
SRB_OUTB(sc, srb, SRB_OPNSAP_GSAPMAXMEMB, 0x00);
|
|
SRB_OUTW(sc, srb, SRB_OPNSAP_MAXIFIELD, 0x0088);
|
|
SRB_OUTB(sc, srb, SRB_OPNSAP_SAPVALUE, type);
|
|
SRB_OUTB(sc, srb, SRB_OPNSAP_SAPOPTIONS, 0x24);
|
|
SRB_OUTB(sc, srb, SRB_OPNSAP_STATIONCNT, 0x01);
|
|
SRB_OUTB(sc, srb, SRB_OPNSAP_SAPGSAPMEMB, 0x00);
|
|
|
|
ACA_SETB(sc, ACA_ISRP_e, INT_ENABLE);
|
|
ACA_SETB(sc, ACA_ISRA_o, CMD_IN_SRB);
|
|
}
|
|
|
|
/*
|
|
* tr_sleep - sleep to wait for adapter to open
|
|
*/
|
|
void
|
|
tr_sleep(struct tr_softc *sc)
|
|
{
|
|
int error;
|
|
|
|
error = tsleep(&sc->tr_sleepevent, 1, "trsleep", hz * 30);
|
|
if (error == EWOULDBLOCK)
|
|
printf("%s: sleep event timeout\n", device_xname(&sc->sc_dev));
|
|
}
|
|
|
|
void
|
|
tr_watchdog(struct ifnet *ifp)
|
|
{
|
|
struct tr_softc *sc = ifp->if_softc;
|
|
|
|
log(LOG_ERR,"%s: device timeout\n", device_xname(&sc->sc_dev));
|
|
++ifp->if_oerrors;
|
|
|
|
tr_reset(sc);
|
|
}
|
|
|
|
int
|
|
tr_enable(struct tr_softc *sc)
|
|
{
|
|
if (sc->sc_enabled == 0 && sc->sc_enable != NULL) {
|
|
if ((*sc->sc_enable)(sc) != 0) {
|
|
aprint_error_dev(&sc->sc_dev, "device enable failed\n");
|
|
return (EIO);
|
|
}
|
|
}
|
|
|
|
sc->sc_enabled = 1;
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
tr_disable(struct tr_softc *sc)
|
|
{
|
|
if (sc->sc_enabled != 0 && sc->sc_disable != NULL) {
|
|
(*sc->sc_disable)(sc);
|
|
sc->sc_enabled = 0;
|
|
}
|
|
}
|
|
|
|
int
|
|
tr_activate(device_t self, enum devact act)
|
|
{
|
|
struct tr_softc *sc = device_private(self);
|
|
|
|
switch (act) {
|
|
case DVACT_DEACTIVATE:
|
|
if_deactivate(&sc->sc_ethercom.ec_if);
|
|
return 0;
|
|
default:
|
|
return EOPNOTSUPP;
|
|
}
|
|
}
|
|
|
|
int
|
|
tr_detach(device_t self, int flags)
|
|
{
|
|
struct tr_softc *sc = (struct tr_softc *)self;
|
|
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
|
|
|
|
tr_disable(sc);
|
|
|
|
callout_stop(&sc->sc_init_callout);
|
|
callout_stop(&sc->sc_reinit_callout);
|
|
|
|
/* Delete all remaining media. */
|
|
ifmedia_delete_instance(&sc->sc_media, IFM_INST_ANY);
|
|
|
|
token_ifdetach(ifp);
|
|
if_detach(ifp);
|
|
|
|
shutdownhook_disestablish(sc->sc_sdhook);
|
|
|
|
return (0);
|
|
}
|