NetBSD/sys/dev/ic/tropic.c

1819 lines
44 KiB
C

/* $NetBSD: tropic.c,v 1.30 2007/03/04 06:02:02 christos 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.30 2007/03/04 06:02:02 christos Exp $");
#include "opt_inet.h"
#include "bpfilter.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
#if NBPFILTER > 0
#include <net/bpf.h>
#include <net/bpfdesc.h>
#endif
#include <machine/cpu.h>
#include <machine/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(sc)
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(sc, ifmr)
struct tr_softc *sc;
struct ifmediareq *ifmr;
{
struct ifmedia *ifm = &sc->sc_media;
ifmr->ifm_active = ifm->ifm_cur->ifm_media;
}
int
tr_config(sc)
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)) {
printf("%s: no response for fast path cfg\n",
sc->sc_dev.dv_xname);
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",
sc->sc_dev.dv_xname,
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(sc)
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
*/
strcpy(ifp->if_xname, sc->sc_dev.dv_xname);
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", sc->sc_dev.dv_xname,
token_sprintf(myaddr), (sc->sc_init_status & RSP_16) ? 16 : 4);
callout_init(&sc->sc_init_callout);
callout_init(&sc->sc_reinit_callout);
sc->sc_sdhook = shutdownhook_establish(tr_shutdown, sc);
return 0;
}
int
tr_setspeed(sc, speed)
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",
sc->sc_dev.dv_xname, SRB_INB(sc, sc->sc_srb, SRB_RETCODE));
return 1;
}
return 0;
}
int
tr_mediachange(ifp)
struct ifnet *ifp;
{
struct tr_softc *sc = ifp->if_softc;
if (sc->sc_mediachange)
return ((*sc->sc_mediachange)(sc));
return EINVAL;
}
void
tr_mediastatus(ifp, ifmr)
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(sc)
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) {
printf("%s: no response from adapter after reset\n",
sc->sc_dev.dv_xname);
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) {
printf("%s: initialization incomplete, status: 0x%02x\n",
sc->sc_dev.dv_xname, SRB_INB(sc, sc->sc_srb, SRB_CMD));
return 1;
}
if (SRB_INB(sc, sc->sc_srb, SRB_INIT_BUC) != 0) {
printf("%s: Bring Up Code %02x\n", sc->sc_dev.dv_xname,
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(sc)
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(arg)
void *arg;
{
struct tr_softc *sc = arg;
tr_stop(sc);
}
void
tr_reinit(arg)
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(arg)
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(arg)
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(ifp)
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(ifp)
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 NBPFILTER > 0
if (ifp->if_bpf)
bpf_mtap(ifp->if_bpf, m0);
#endif
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(arg)
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",
sc->sc_dev.dv_xname,
(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)
printf("%s: error on xmit request = "
"0x%x\n", sc->sc_dev.dv_xname,
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",
sc->sc_dev.dv_xname,
sc->sc_xmit_buffers);
#endif
sc->sc_xmit_correlator = 0;
wakeup(&sc->tr_sleepevent);
}
else
tr_opensap(sc, LLC_SNAP_LSAP);
}
else {
printf("%s: open error = 0x%x\n",
sc->sc_dev.dv_xname,
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)
printf("%s: close error = 0x%x\n",
sc->sc_dev.dv_xname, 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",
sc->sc_dev.dv_xname);
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)
printf("%s: read error log cmd err = "
"0x%x\n", sc->sc_dev.dv_xname,
retcode);
#ifdef TROPICDEBUG
log_srb = sc->sc_srb;
printf("%s: ERROR LOG:\n",sc->sc_dev.dv_xname);
printf("%s: Line=%d, Internal=%d, Burst=%d\n",
sc->sc_dev.dv_xname,
(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",
sc->sc_dev.dv_xname,
(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",
sc->sc_dev.dv_xname,
(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",sc->sc_dev.dv_xname,
(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",
sc->sc_dev.dv_xname, 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)
printf("%s: ASB bad receive response = 0x%x\n",
sc->sc_dev.dv_xname, retcode);
break;
case XMIT_DIR_FRAME: /* Transmit */
case XMIT_UI_FRM: /* Transmit */
/* Response not valid? */
if (retcode != 0xff)
printf("%s: ASB response err on xmit = 0x%x\n",
sc->sc_dev.dv_xname, retcode);
break;
default:
printf("%s: invalid command in ASB = 0x%x\n",
sc->sc_dev.dv_xname, 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",
sc->sc_dev.dv_xname,
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)){
printf("%s: signal loss / lobe fault\n",
sc->sc_dev.dv_xname);
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",
sc->sc_dev.dv_xname,
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:
printf("%s: invalid command in ARB = 0x%x\n",
sc->sc_dev.dv_xname, 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",
sc->sc_dev.dv_xname, retcode);
/* XXXchb */
if (retcode == 0x22) {
printf("%s: FS = 0x%2x\n",
sc->sc_dev.dv_xname,
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:
printf("%s: SSB error, invalid command =%x\n",
sc->sc_dev.dv_xname, 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",
sc->sc_dev.dv_xname, 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()
{
}
int asb_reply_xmit()
{
}
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(sc)
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 NBPFILTER > 0
if (ifp->if_bpf)
bpf_mtap(ifp->if_bpf, m);
#endif
(*ifp->if_input)(ifp, m);
}
}
/*
* Interrupt handler for old style "adapter requires data to transmit".
*/
void
tr_oldxint(sc)
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 NBPFILTER > 0
if (ifp->if_bpf)
bpf_mtap(ifp->if_bpf, m0);
#endif
/* 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 {
printf("%s: unexpected empty mbuf send queue\n",
sc->sc_dev.dv_xname);
/* 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(sc)
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++;
printf("%s: xmit error = 0x%x\n", sc->sc_dev.dv_xname,
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(sc, dhb, m0)
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(sc, totlen, ifp)
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(ifp, cmd, data)
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 SIOCSIFADDR:
if ((error = tr_enable(sc)) != 0)
break;
switch (ifa->ifa_addr->sa_family) {
#ifdef INET
case AF_INET:
/* XXX if not running */
if ((ifp->if_flags & IFF_RUNNING) == 0) {
tr_init(sc); /* before arp_ifinit */
tr_sleep(sc);
}
arp_ifinit(ifp, ifa);
break;
#endif /*INET*/
default:
/* XXX if not running */
if ((ifp->if_flags & IFF_RUNNING) == 0) {
tr_init(sc); /* before arpwhohas */
tr_sleep(sc);
}
break;
}
break;
case SIOCSIFFLAGS:
/*
* 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
*/
if ((ifp->if_flags & (IFF_RUNNING | IFF_UP)) == IFF_RUNNING) {
tr_stop(sc);
ifp->if_flags &= ~IFF_RUNNING;
tr_disable(sc);
}
else if ((ifp->if_flags & (IFF_RUNNING | IFF_UP)) == IFF_UP) {
if ((error = tr_enable(sc)) != 0)
break;
tr_init(sc);
tr_sleep(sc);
}
else {
/*
* XXX handle other flag changes
*/
}
break;
case SIOCGIFMEDIA:
case SIOCSIFMEDIA:
error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
break;
#ifdef SIOCSIFMTU
case SIOCSIFMTU:
if (ifr->ifr_mtu > sc->sc_maxmtu)
error = EINVAL;
else
ifp->if_mtu = ifr->ifr_mtu;
break;
#endif
default:
error = EINVAL;
}
splx(s);
return (error);
}
/*
* tr_bcopy - like bcopy except that it knows about the structure of
* adapter receive buffers.
*/
void
tr_bcopy(sc, dest, len)
struct tr_softc *sc; /* pointer to softc struct for this adapter */
u_char *dest; /* destination address */
int 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(sc, type)
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(sc)
struct tr_softc *sc;
{
int error;
error = tsleep(&sc->tr_sleepevent, 1, "trsleep", hz * 30);
if (error == EWOULDBLOCK)
printf("%s: sleep event timeout\n", sc->sc_dev.dv_xname);
}
void
tr_watchdog(ifp)
struct ifnet *ifp;
{
struct tr_softc *sc = ifp->if_softc;
log(LOG_ERR,"%s: device timeout\n", sc->sc_dev.dv_xname);
++ifp->if_oerrors;
tr_reset(sc);
}
int
tr_enable(sc)
struct tr_softc *sc;
{
if (sc->sc_enabled == 0 && sc->sc_enable != NULL) {
if ((*sc->sc_enable)(sc) != 0) {
printf("%s: device enable failed\n",
sc->sc_dev.dv_xname);
return (EIO);
}
}
sc->sc_enabled = 1;
return (0);
}
void
tr_disable(sc)
struct tr_softc *sc;
{
if (sc->sc_enabled != 0 && sc->sc_disable != NULL) {
(*sc->sc_disable)(sc);
sc->sc_enabled = 0;
}
}
int
tr_activate(self, act)
struct device *self;
enum devact act;
{
struct tr_softc *sc = (struct tr_softc *)self;
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
int rv = 0, s;
s = splnet();
switch (act) {
case DVACT_ACTIVATE:
rv = EOPNOTSUPP;
break;
case DVACT_DEACTIVATE:
if_deactivate(ifp);
break;
}
splx(s);
return (rv);
}
int
tr_detach(struct device *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);
}