/* 
 * Copyright (c) 1998, Larry Lile
 * All rights reserved.
 *
 * For latest sources and information on this driver, please
 * go to http://anarchy.stdio.com.
 *
 * Questions, comments or suggestions should be directed to
 * Larry Lile <lile@stdio.com>.
 *
 * 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 unmodified, this list of conditions, and the following
 *    disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $FreeBSD: src/sys/contrib/dev/oltr/if_oltr.c,v 1.11 2000/01/29 18:38:31 peter Exp $
 */


#include "pci.h"
#include "oltr.h"
#include "opt_inet.h"

/*#define TRlldInlineIO*/

#define ISA_ADAPTERS  (OC_3115 | OC_3117 | OC_3118)
#define PCI_ADAPTERS  (OC_3133 | OC_3136 | OC_3137 | \
                       OC_3139 | OC_3140 | OC_3141 | \
                       OC_3250 | OC_3540 )

#define PCI_VENDOR_OLICOM 0x108D

char *AdapterName[] = {
   /*  0 */ "Olicom XT Adapter [unsupported]",
   /*  1 */ "Olicom OC-3115",
   /*  2 */ "Olicom ISA 16/4 Adapter (OC-3117)",
   /*  3 */ "Olicom ISA 16/4 Adapter (OC-3118)",
   /*  4 */ "Olicom MCA 16/4 Adapter (OC-3129) [unsupported]",
   /*  5 */ "Olicom MCA 16/4 Adapter (OC-3129) [unsupported]",
   /*  6 */ "Olicom MCA 16/4 Adapter (OC-3129) [unsupported]",
   /*  7 */ "Olicom EISA 16/4 Adapter (OC-3133)",
   /*  8 */ "Olicom EISA 16/4 Adapter (OC-3133)",
   /*  9 */ "Olicom EISA 16/4 Server Adapter (OC-3135)",
   /* 10 */ "Olicom PCI 16/4 Adapter (OC-3136)",
   /* 11 */ "Olicom PCI 16/4 Adapter (OC-3136)",
   /* 12 */ "Olicom PCI/II 16/4 Adapter (OC-3137)",
   /* 13 */ "Olicom PCI 16/4 Adapter (OC-3139)",
   /* 14 */ "Olicom RapidFire 3140 16/4 PCI Adapter (OC-3140)",
   /* 15 */ "Olicom RapidFire 3141 Fiber Adapter (OC-3141)",
   /* 16 */ "Olicom PCMCIA 16/4 Adapter (OC-3220) [unsupported]",
   /* 17 */ "Olicom PCMCIA 16/4 Adapter (OC-3121, OC-3230, OC-3232) [unsupported]",
   /* 18 */ "Olicom PCMCIA 16/4 Adapter (OC-3250)",
   /* 19 */ "Olicom RapidFire 3540 4/16/100 Adapter (OC-3540)"
};

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/sockio.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/syslog.h>
#include <sys/kernel.h>
#include <sys/interrupt.h>

#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <net/iso88025.h>
#include <net/if_media.h>

#include <net/bpf.h>
 
#include <machine/clock.h>
#include <machine/md_var.h>
#include <i386/isa/isa_device.h>

#if NPCI > 0
#include <pci/pcivar.h>
#include <pci/pcireg.h>
#endif

#include "contrib/dev/oltr/trlld.h"

#ifndef TRLLD_SPEED_AUTO
#define TRLLD_SPEED_AUTO 0
#endif

#define MIN(A,B) (((A) < (B)) ? (A) : (B))
#define MIN3(A,B,C) (MIN(A, (MIN(B, C))))

void *oltr_malloc(ssize_t, TRlldAdapterConfig_t *);

/*
 * Glue functions prototypes for PMW kit IO
 */

#ifndef TRlldInlineIO
static void DriverOutByte           __P((unsigned short, unsigned char));
static void DriverOutWord           __P((unsigned short, unsigned short));
static void DriverOutDword          __P((unsigned short, unsigned long));
static void DriverRepOutByte        __P((unsigned short, unsigned char  *, int));
static void DriverRepOutWord        __P((unsigned short, unsigned short *, int));
static void DriverRepOutDword       __P((unsigned short, unsigned long  *, int));
static unsigned char  DriverInByte  __P((unsigned short));
static unsigned short DriverInWord  __P((unsigned short));
static unsigned long  DriverInDword __P((unsigned short));
static void DriverRepInByte         __P((unsigned short, unsigned char  *, int));
static void DriverRepInWord         __P((unsigned short, unsigned short *, int));
static void DriverRepInDword        __P((unsigned short, unsigned long  *, int));
#endif /*TRlldInlineIO*/
static void DriverSuspend                __P((unsigned short));
static void DriverStatus                 __P((void *, TRlldStatus_t *));
static void DriverCloseCompleted         __P((void *));
static void DriverStatistics             __P((void *, TRlldStatistics_t *));
static void DriverTransmitFrameCompleted __P((void *, void *, int));
static void DriverReceiveFrameCompleted  __P((void *, int, int, void *, int));

typedef struct tx_buf {
    int         index;
    int         count;
    char        *buf;
    struct mbuf *m;
} tx_buf_t;

typedef struct rx_buf {
    int         index;
    char        *buf;
} rx_buf_t;

#ifndef EXTRA_OLTR
#if NPCI > 0
#define EXTRA_OLTR	8
#else 
#define EXTRA_OLTR	0
#endif /* NPCI */
#endif /* EXTRA_OLTR */

#ifndef OLTR_PROMISC_MODE
#define OLTR_PROMISC_MODE (TRLLD_PROM_LLC)
#endif

#define ALL_OPTIONS (IFM_TOK_ETR | IFM_TOK_SRCRT | IFM_TOK_ALLR | IFM_TOK_DTR | IFM_TOK_CLASSIC | IFM_TOK_AUTO)

/* List sizes MUST be a power of 2 */
#define TX_LIST_SIZE    16
#define RX_LIST_SIZE    16 
#define TX_LIST_MASK    (TX_LIST_SIZE - 1)
#define RX_LIST_MASK    (RX_LIST_SIZE - 1)
#define RX_BUFFER_LEN   (8*1024)
#define TX_BUFFER_LEN   (8*1024)

struct oltr_softc {
    struct arpcom arpcom;
    struct ifmedia ifmedia;
    TRlldAdapterConfig_t *config;
    TRlldAdapter_t *TRlldAdapter;
    int unit;
    u_short PromiscMode;
    u_short AdapterMode;
    int hw_state;
#define HW_UNKNOWN      0  /* initial/absent state */
#define HW_FOUND        1  /* found, not initialized */
#define HW_BAD          2  /* fatal error */
#define HW_FAILED       3  /* closed eg. by remove, allow manual reopen */
#define HW_LOADING      4
#define HW_CLOSING      5
#define HW_CLOSING2     6
#define HW_CLOSED       7
#define HW_OPENING      8
#define HW_OPEN         9
#define HW_ERROR        10 /* temporary error */

    u_long GroupAddress;
    u_long FunctionalAddress;
    int poll_adapter; 

    int tx_next;
    int tx_avail;
    tx_buf_t tx_buffer[TX_LIST_SIZE];
    TRlldTransmit_t tx_frame;

    int rx_next;
    int rx_avail;
    rx_buf_t rx_buffer[RX_LIST_SIZE];

    struct callout_handle oltr_ch;
    struct callout_handle poll_ch;

};

static struct oltr_softc oltr_softc[NOLTR + EXTRA_OLTR];

/*
 * Driver function prototypes
 */

static int  oltr_probe   __P((struct isa_device *));
static int  oltr_attach  __P((struct isa_device *));  
static void oltr_init    __P((struct oltr_softc *));
static void oltr_intr    __P((int));
static void oltr_start   __P((struct ifnet *));
static void oltr_stop    __P((struct oltr_softc *));
static int  oltr_ioctl   __P((struct ifnet *, u_long, caddr_t));

static int oltr_attach_common   __P((struct oltr_softc *));

void oltr_timeout __P((void *));
void adapter_poll __P((void *));

struct isa_driver oltrdriver = {
    oltr_probe,
    oltr_attach,
    "oltr",
    0
};

int isa_cards = 0;

#if NPCI > 0
static u_long oltr_count = NOLTR;
static const char *oltr_pci_probe	__P((pcici_t, pcidi_t));
static void oltr_pci_attach		__P((pcici_t, int));
static void oltr_pci_intr    		__P((void *));

static struct pci_device oltr_device = {
    "oltr",
    oltr_pci_probe,
    oltr_pci_attach,
    &oltr_count,
    NULL
};

COMPAT_PCI_DRIVER(oltr_pci, oltr_device);
int pci_cards = 0;
#endif /* NPCI */

static int  oltr_ifmedia_upd	__P((struct ifnet *));
static void oltr_ifmedia_sts    __P((struct ifnet *, struct ifmediareq *));

static TRlldDriver_t oltrLldDriver = {
    TRLLD_VERSION,
#ifndef TRlldInlineIO
    DriverOutByte,
    DriverOutWord,
    DriverOutDword,
    DriverRepOutByte,
    DriverRepOutWord,
    DriverRepOutDword,
    DriverInByte,
    DriverInWord,
    DriverInDword,
    DriverRepInByte,
    DriverRepInWord,
    DriverRepInDword,
#endif /*TRlldInlineIO*/
    DriverSuspend,
    DriverStatus,
    DriverCloseCompleted,
    DriverStatistics,
    DriverTransmitFrameCompleted,
    DriverReceiveFrameCompleted,
};

TRlldAdapterConfig_t oltr_config[NOLTR + EXTRA_OLTR];

void *
oltr_malloc(Size, Adapter)
    ssize_t Size;
    TRlldAdapterConfig_t *Adapter;
{

    /* If the adapter needs memory below 16M for DMA then use contigmalloc */
    if (Adapter->mode & TRLLD_MODE_16M)  /* Adapter using ISA DMA buffer below 16M */
        return(contigmalloc(Size, M_DEVBUF, M_NOWAIT, 0ul, 0xfffffful, 1ul, 0x10000ul));
    else
        return(malloc(Size, M_DEVBUF, M_NOWAIT));
}
    
/*
 * Driver Functions 
 */

static int
oltr_probe(is)
    struct isa_device *is;
{
    static int find_completed = 0, assigned[NOLTR];
    struct oltr_softc *sc = &oltr_softc[is->id_unit];
    int i;

    printf("oltr%d: oltr_probe\n", is->id_unit);

    /* Make life easy, use the Olicom supplied find function on the first probe
     * to probe all of the ISA adapters.  Then give them to each unit as requested.
     * Try to match the adapters to units based on the iobase, but if iobase? then
     * just give out the next available adapter.
     */
    if (!find_completed) {
        isa_cards = TRlldFind(&oltrLldDriver, &oltr_config[0], ISA_ADAPTERS, NOLTR);
        /*for (i = 0; i < isa_cards; i++) {
            printf("TRlldFind: card %d - %s MAC %6D\n", i + 1, AdapterName[oltr_config[i].type], oltr_config[i].macaddress, ":");
        }*/
        for (i = 0; i < NOLTR; i++)
            assigned[i] = 0;
        find_completed = 1;
    }

    sc->unit = is->id_unit;
    sc->hw_state = HW_UNKNOWN;

    if (find_completed && ((isa_cards == 0) || (is->id_unit > isa_cards))) 
        return(0);

    if (((is->id_iobase < 0xa00) || (is->id_iobase > 0xbe0)) && (is->id_iobase != 0xffffffff)) {
        printf("oltr%d: port address impossible (0x%X)\n", is->id_unit, is->id_iobase);
        return(0);
    }

    /* Auto assign lowest available card not already in use */
    if (is->id_iobase == 0xffffffff) {
        printf("oltr%d: auto assigning card.\n", is->id_unit);
        for (i = 0; assigned[i]; i++);
        assigned[i] = 1;
        sc->config = &oltr_config[i];
        is->id_iobase = sc->config->iobase0;                        /* Claim our port space */
        if (!is->id_irq) 
            is->id_irq = (1 << sc->config->interruptlevel);         /* Claim our interrupt */
        is->id_intr = (inthand2_t *)oltr_intr;
        if ((is->id_drq == 0xffffffff) && (sc->config->dmalevel != TRLLD_DMA_PIO))
            is->id_drq = sc->config->dmalevel;                      /* Claim our dma channel */
        printf("oltr%d: <%s> [%6D]\n", is->id_unit, AdapterName[sc->config->type], sc->config->macaddress, ":");
        sc->hw_state = HW_FOUND;
        return(1);
    } else {
    /* Assign based on iobase address provided in kernel config */
        for (i = 0; i < NOLTR; i++) {
            if (is->id_iobase == oltr_config[i].iobase0) {
                if (assigned[i]) {
                    printf("oltr%d: adapter (0x%X) already assigned.\n", is->id_unit, is->id_iobase);
                    return(0);
                }
                assigned[i] = 1;
                sc->config = &oltr_config[i];
                if (is->id_irq == 0)
                    is->id_irq = (1 << sc->config->interruptlevel);         /* Claim our interrupt */
                is->id_intr = (inthand2_t *)oltr_intr;
                if ((is->id_drq == 0xffffffff) && (sc->config->dmalevel != TRLLD_DMA_PIO))
                    is->id_drq = sc->config->dmalevel;                      /* Claim our dma channel */
                printf("oltr%d: <%s> [%6D]\n", is->id_unit, AdapterName[sc->config->type], sc->config->macaddress, ":");
                sc->hw_state = HW_FOUND;
                return(1);
            }
        }
    }
    return(0); /* Card was not found */
}

#if NPCI > 0
static const char *
oltr_pci_probe(config_id, device_id)
    pcici_t config_id;
    pcidi_t device_id;
{
    u_char PCIConfigurationSpace[64];
    u_long command;
    int i, j, rc;

    printf("oltr: oltr_pci_probe\n");

    j = NOLTR + pci_cards;

    if (pci_cards == EXTRA_OLTR)
        return(NULL);

    if (((device_id & 0xffff) == PCI_VENDOR_OLICOM) && 
          ((((device_id >> 16) & 0xffff) == 0x0001) || 
           (((device_id >> 16) & 0xffff) == 0x0004) || 
           (((device_id >> 16) & 0xffff) == 0x0005) || 
           (((device_id >> 16) & 0xffff) == 0x0007) || 
           (((device_id >> 16) & 0xffff) == 0x0008))) {

        for (i = 0; i < 64; i++)
            PCIConfigurationSpace[i] = pci_cfgread(config_id, i, /*bytes*/1);

        rc = TRlldPCIConfig(&oltrLldDriver, &oltr_config[j], PCIConfigurationSpace);

        if ((rc == TRLLD_PCICONFIG_OK) || (rc == TRLLD_PCICONFIG_SET_COMMAND)) {
            if (rc == TRLLD_PCICONFIG_SET_COMMAND) {
                printf("oltr: setting bus-master mode\n");
                command = pci_conf_read(config_id, PCIR_COMMAND);
                pci_conf_write(config_id, PCIR_COMMAND, (command | PCIM_CMD_BUSMASTEREN));
            }
            pci_cards++;
            return (AdapterName[oltr_config[j].type]);
        } else {
            if (rc == TRLLD_PCICONFIG_FAIL)
                printf("oltr: TRlldPCIConfig failed!\n");
            if (rc == TRLLD_PCICONFIG_VERSION)
                printf("oltr: wrong LLD version\n");
        }
    }
    return(NULL);
}
#endif /* NPCI */

static int
oltr_attach(is)
    struct isa_device *is;
{
    struct oltr_softc *sc = &oltr_softc[is->id_unit];
    int rc;

    sc->unit = is->id_unit;

    if (!oltr_attach_common(sc))
        return(0);

    /* If the kernel config does not match the current card configuration then
     * adjust the card settings to match the kernel.
     */
    if ((ffs(is->id_irq) - 1) != sc->config->interruptlevel) {
        rc = TRlldSetInterrupt(sc->TRlldAdapter, is->id_irq); 
        if (rc != TRLLD_CONFIG_OK) {  
            printf("oltr%d: Unable to change adapter interrupt level (%x)\n", sc->unit, rc);
            return(0);
        }
    }   
        
    /* Set dma level, fall back to pio if possible. (following SCO driver example) */
    if (is->id_drq != sc->config->dmalevel) {
        rc = TRlldSetDMA(sc->TRlldAdapter, is->id_drq, &sc->config->mode);
        if (rc != TRLLD_CONFIG_OK) {
            if ((sc->config->dmalevel != TRLLD_DMA_PIO) &&
                (TRlldSetDMA(sc->TRlldAdapter, TRLLD_DMA_PIO, &sc->config->mode) != TRLLD_CONFIG_OK)) {
                printf("oltr%d: unable to change dma level from %d to %d (%x)\n", sc->unit,
                        sc->config->dmalevel, is->id_drq, rc);
            }
            printf("oltr%d: Unable to change adapter dma level, using PIO mode (%x)\n", sc->unit, rc);
            sc->config->dmalevel = TRLLD_DMA_PIO;
            rc = TRlldSetDMA(sc->TRlldAdapter, is->id_drq, &sc->config->mode);
        }
        is->id_irq = sc->config->dmalevel;
    }
    return(1);
}

#if NPCI > 0
static void
oltr_pci_attach(config_id, unit)
    pcici_t config_id;
    int unit;
{
    struct oltr_softc *sc = &oltr_softc[unit];

    sc->unit = unit;
    sc->config = &oltr_config[unit];
    sc->hw_state = HW_FOUND;

    printf("oltr%d: mac address [%6D]\n", sc->unit, sc->config->macaddress, ":");

    if (!oltr_attach_common(sc))
        return;

    /* Map our interrupt */
    if (!pci_map_int(config_id, oltr_pci_intr, sc, &net_imask)) {
        printf("oltr%d: couldn't map interrupt\n", unit);
        return;
    }
}
#endif /* NPCI */

static int
oltr_attach_common(sc)
    struct oltr_softc *sc;
{
    struct ifnet *ifp = &sc->arpcom.ac_if;
    u_int  bufsize;
    int    rc, i, j;
  
    /*printf("oltr%d: attach_common called\n", sc->unit);*/

    /* Allocate adapter memory buffer */
    bufsize = TRlldAdapterSize();
    sc->TRlldAdapter = (TRlldAdapter_t *)oltr_malloc(bufsize, sc->config);
    if (sc->TRlldAdapter == NULL) {
        printf("oltr%d: Unable to allocate adapter memory block (%d bytes)\n", sc->unit, bufsize);
    } 
    /*printf("oltr%d: Adapter memory block (%p %d bytes)\n", sc->unit, sc->TRlldAdapter, bufsize);*/

    /* Setup transmit pool */
    for (i = 0; i < TX_LIST_SIZE; i++) {
        sc->tx_buffer[i].index = i;
        sc->tx_buffer[i].buf = (char *)oltr_malloc(TX_BUFFER_LEN, sc->config);
        /* If we have a failure then free everything and get out */
        if (!sc->tx_buffer[i].buf) {
            printf("oltr%d: Unable to allocate transmit buffers.\n", sc->unit);
            for (j = 0; j < i; j++)
                free(sc->tx_buffer[j].buf, M_DEVBUF);
            return(0);
        }
    }
    sc->tx_next = 0;
    sc->tx_avail = TX_LIST_SIZE;
    sc->tx_frame.FragmentCount = 0;

    /* Setup receive pool */
    for (i = 0; i < RX_LIST_SIZE; i++) {
        sc->rx_buffer[i].index = i;
        sc->rx_buffer[i].buf = (char *)oltr_malloc(RX_BUFFER_LEN, sc->config);
        /* If we have a failure then free everything and get out */
        if (!sc->rx_buffer[i].buf) {          
            printf("oltr%d: Unable to allocate receive buffers.\n", sc->unit);
            for (j = 0; j < i; j++)
                free(sc->rx_buffer[j].buf, M_DEVBUF);
            return(0);
        }
    }   
    sc->rx_next = 0;
    sc->rx_avail = RX_LIST_SIZE; 
    /*printf("oltr%d: Allocated receive buffers\n", sc->unit); */
    
    /* Set up adapter polling mechanism */
    sc->poll_adapter = 1;
    callout_handle_init(&sc->poll_ch);
    sc->poll_ch = timeout(adapter_poll, (void *)sc->unit, (1*hz)/1000);
    callout_handle_init(&sc->oltr_ch);

    /* Initialize adapter */
    rc = TRlldAdapterInit(&oltrLldDriver, sc->TRlldAdapter, kvtop(sc->TRlldAdapter), 
                          (void *)sc->unit, sc->config);
    if (rc != TRLLD_INIT_OK) {
        switch (rc) {
            case TRLLD_INIT_NOT_FOUND:
                printf("oltr%d: Adapter not found or malfunctioning.\n", sc->unit);
                sc->hw_state = HW_BAD;
                return(0);
            case TRLLD_INIT_UNSUPPORTED:
                printf("oltr%d: Adapter not supported by low level driver.\n", sc->unit);
                sc->hw_state = HW_UNKNOWN;
                return(0);
            case TRLLD_INIT_PHYS16:
                printf("oltr%d: Adapter memory block above 16M, must be below 16M.\n", sc->unit);
                return(0);
            case TRLLD_INIT_VERSION:
                printf("oltr%d: Low level driver version mismatch.\n", sc->unit);
                return(0);
            default:
                printf("oltr%d: Unknown initilization error occoured (%x).\n", sc->unit, rc);
                return(0);
        }
    }

    /* Download Adapter Microcode */
    /*printf("oltr%d: Downloading adapter microcode...", sc->unit);*/
    sc->hw_state = HW_LOADING;
    switch(sc->config->mactype) {
        case TRLLD_MAC_TMS:           /* TMS microcode */
            rc = TRlldDownload(sc->TRlldAdapter, TRlldMacCode);
            break;
        case TRLLD_MAC_HAWKEYE:        /* Hawkeye microcode */
            rc = TRlldDownload(sc->TRlldAdapter, TRlldHawkeyeMac);
            break;
        case TRLLD_MAC_BULLSEYE:      /* Bullseye microcode */
            rc = TRlldDownload(sc->TRlldAdapter, TRlldBullseyeMac);
            break;
        default:
            printf("oltr%d: unknown mactype %d\n", sc->unit, sc->config->mactype);
            return(0);
    }
    /*if (rc == TRLLD_DOWNLOAD_OK) 
        printf("done\n");*/
    if ((rc == TRLLD_DOWNLOAD_ERROR) || (rc == TRLLD_STATE)) {
        printf("oltr%d: Adapter microcode download failed! (rc = %x)\n", sc->unit, rc);
        sc->hw_state = HW_BAD;
        return(0);     
    }

    TRlldSetSpeed(sc->TRlldAdapter, TRLLD_SPEED_AUTO);

    sc->PromiscMode = 0;
    sc->AdapterMode = 0;

    /* Do the ifnet initialization */
    ifp->if_softc   = sc;
    ifp->if_unit    = sc->unit;
    ifp->if_name    = "oltr";
    ifp->if_output  = iso88025_output;
    ifp->if_init    = (if_init_f_t *)oltr_init;
    ifp->if_start   = oltr_start;
    ifp->if_ioctl   = oltr_ioctl;
    ifp->if_flags   = IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX;
    bcopy(sc->config->macaddress, sc->arpcom.ac_enaddr, sizeof(sc->config->macaddress));

    /* Set up common ifmedia options */
    ifmedia_init(&sc->ifmedia, 0, oltr_ifmedia_upd, oltr_ifmedia_sts);

    ifmedia_add(&sc->ifmedia, IFM_TOKEN | IFM_AUTO, 0 , NULL);
    ifmedia_add(&sc->ifmedia, IFM_TOKEN | IFM_TOK_UTP4, 0 , NULL);
    ifmedia_add(&sc->ifmedia, IFM_TOKEN | IFM_TOK_UTP16, 0 , NULL);
    
    ifmedia_set(&sc->ifmedia, IFM_TOKEN | IFM_AUTO);

    if_attach(ifp);
    iso88025_ifattach(ifp);

    bpfattach(ifp, DLT_IEEE802, sizeof(struct iso88025_header));

    printf("oltr%d: Adapter modes - ", sc->unit);
    if (sc->config->mode & TRLLD_MODE_16M) printf("TRLLD_MODE_16M ");
    if (sc->config->mode & TRLLD_MODE_PHYSICAL) printf("TRLLD_MODE_PHYSICAL ");
    if (sc->config->mode & TRLLD_MODE_FIXED_CFG) printf("TRLLD_MODE_FIXED_CFG ");
    if (sc->config->mode & TRLLD_MODE_SHORT_SLOT) printf("TRLLD_MODE_SHORT_SLOT ");
    if (sc->config->mode & TRLLD_MODE_CANNOT_DISABLE) printf("TRLLD_MODE_CANNOT_DISABLE ");
    if (sc->config->mode & TRLLD_MODE_SHARE_INTERRUPT) printf("TRLLD_MODE_SHARE_INTERRUPT ");
    if (sc->config->mode & TRLLD_MODE_MEMORY) printf("TRLLD_MODE_MEMORY ");
    printf("\n");

    return(1);
}

static int
oltr_ifmedia_upd(ifp)
    struct ifnet *ifp;
{
    struct oltr_softc *sc = ifp->if_softc;
    struct ifmedia *ifm   = &sc->ifmedia;

    if (IFM_TYPE(ifm->ifm_media) != IFM_TOKEN)
        return(EINVAL);
    
    switch(IFM_SUBTYPE(ifm->ifm_media)) {
        case IFM_AUTO:
            TRlldSetSpeed(sc->TRlldAdapter, TRLLD_SPEED_AUTO);
            break;
        case IFM_TOK_UTP4:
            TRlldSetSpeed(sc->TRlldAdapter, TRLLD_SPEED_4MBPS);
            break;
        case IFM_TOK_UTP16:
            TRlldSetSpeed(sc->TRlldAdapter, TRLLD_SPEED_16MBPS);
            break;
        default:
            return(EINVAL);
    }

    if (IFM_TYPE_OPTIONS(ifm->ifm_media) & IFM_TOK_ETR)
        printf("oltr%d: ETR not implemented\n", sc->unit);
    if (IFM_TYPE_OPTIONS(ifm->ifm_media) & IFM_TOK_SRCRT)
        printf("oltr%d: source-routing not implemented\n", sc->unit);
    if (IFM_TYPE_OPTIONS(ifm->ifm_media) & IFM_TOK_ALLR)
        printf("oltr%d: all source routes not implemented\n", sc->unit);
    if (IFM_TYPE_OPTIONS(ifm->ifm_media) & IFM_TOK_DTR) {
        sc->AdapterMode |= TRLLD_MODE_FORCE_TXI;
        sc->AdapterMode &= ~TRLLD_MODE_FORCE_TKP;
    }
    if (IFM_TYPE_OPTIONS(ifm->ifm_media) & IFM_TOK_CLASSIC) {
        sc->AdapterMode |= TRLLD_MODE_FORCE_TKP;
        sc->AdapterMode &= ~TRLLD_MODE_FORCE_TXI;
    }
    if (IFM_TYPE_OPTIONS(ifm->ifm_media) & IFM_TOK_AUTO)
        sc->AdapterMode &= ~(TRLLD_MODE_FORCE_TXI | TRLLD_MODE_FORCE_TKP);

    if (IFM_TYPE_OPTIONS(ifm->ifm_media) & ~ALL_OPTIONS)
        return(EINVAL);

    return(0);
}

static void
oltr_ifmedia_sts(ifp, ifmr)
    struct ifnet *ifp;
    struct ifmediareq *ifmr;
{
    struct oltr_softc *sc = ifp->if_softc;
    struct ifmedia *ifm = &sc->ifmedia;

    ifmr->ifm_active = IFM_TYPE(ifm->ifm_media)|IFM_SUBTYPE(ifm->ifm_media)|IFM_TYPE_OPTIONS(ifm->ifm_media);

    return;
}

void
oltr_timeout(token)
    void *token;
{
    struct oltr_softc *sc = &oltr_softc[(int)token];
    int unit = (int)token, s;

    s = splimp();

    printf("oltr%d: adapter timed out (%x)\n", unit, sc->hw_state);

    splx(s);
}


void
adapter_poll(token)
    void *token;
{
    int unit = (int)token, poll_timeout = 0, s;
    struct oltr_softc *sc = &oltr_softc[unit];
#if 0
    static int rx_buffers = 0, tx_buffers = 0, rc; 
#endif
    
    s = splimp();

    /* Check to make sure we are not polling a dead card */
    if ((sc->hw_state == HW_BAD) || (sc->hw_state == HW_UNKNOWN)) {
        sc->poll_adapter = -1;
        splx(s);
        return;
    }

    /*printf("oltr%d: adapter poll.\n", unit);*/
    
    /* If the adapter is to be polled again, then set up
     * next timeout poll 
     */
    if (sc->poll_adapter) {
        poll_timeout = TRlldPoll(sc->TRlldAdapter);
        sc->poll_ch = timeout(adapter_poll, (void *)unit, (poll_timeout * hz)/1000);
    }
#if 0
    rc = TRlldReceiveFree(sc->TRlldAdapter);
    if (rx_buffers != rc) {
        printf("oltr%d: %d receive buffers available\n", sc->unit, rc);
        rx_buffers = rc;
    } 
    rc = TRlldTransmitFree(sc->TRlldAdapter);
    if (tx_buffers != rc) {
        printf("oltr%d: %d transmit buffers available\n", sc->unit, rc);
        tx_buffers = rc;
    } 
#endif

    splx(s);            
}

static void 
oltr_init(sc)
    struct oltr_softc *sc;
{
    struct ifnet *ifp = &sc->arpcom.ac_if;
    int i, rc;

    /*printf("oltr%d: oltr_init\n", sc->unit);*/
    
    /* 
     * Adapter should be freshly downloaded or previously closed before
     * bringing it back on line.
     */
    if ((sc->hw_state != HW_CLOSED) && (sc->hw_state != HW_LOADING) && (sc->hw_state != HW_CLOSING2)) {
        printf("oltr%d: adapter not ready to be opened (%d).\n", sc->unit, sc->hw_state);
        return;
    }

    /* Allocate and set up the DMA channel */
    if (sc->config->dmalevel != TRLLD_DMA_PIO) {
        rc = isa_dma_acquire(sc->config->dmalevel);
        isa_dmacascade(sc->config->dmalevel);
    }

    /* Open the adapter */
    sc->hw_state = HW_OPENING;
    rc = TRlldOpen(sc->TRlldAdapter, sc->arpcom.ac_enaddr, sc->GroupAddress, 
                   sc->FunctionalAddress, ifp->if_mtu + 52, sc->AdapterMode);
    if (rc != TRLLD_OPEN_OK) {
        printf("oltr%d: Adapter failed to open (rc = %x)\n", sc->unit, rc);
        sc->hw_state = HW_FAILED;
    } else {
        /*printf("oltr%d: adapter opening...\n", sc->unit);*/
        /*ifp->if_flags |= (IFF_UP | IFF_RUNNING);*/
        ifp->if_flags &= ~IFF_OACTIVE;
    }
    sc->oltr_ch = timeout(oltr_timeout, (void *)sc->unit, 30*hz);
    tsleep((void *)sc->unit, 1, "oltrop", 30*hz);

    /* Give the receive buffers to the adapter */
    for (i = 0; i < RX_LIST_SIZE; i++) { 
        rc = TRlldReceiveFragment(sc->TRlldAdapter,
                                  (void *)sc->rx_buffer[sc->rx_next & RX_LIST_MASK].buf,
                                  kvtop(sc->rx_buffer[sc->rx_next & RX_LIST_MASK].buf),
                                  RX_BUFFER_LEN,
                                  (void *)sc->rx_buffer[sc->rx_next & RX_LIST_MASK].index);
        if (rc != TRLLD_RECEIVE_OK) {
            printf("oltr%d: Adapter refused fragment %d (rc = %d).\n", sc->unit, i, rc);
            break;
        }  else {
            sc->rx_avail--;
        }
        sc->rx_next++;
    }
    sc->tx_frame.FragmentCount = 0;
    
    return;
}
    
static void
oltr_intr(unit)
    int unit;
{
    struct oltr_softc *sc = &oltr_softc[unit];
    int rc;

    /*printf("oltr%d: oltr_intr\n", unit);*/ /* Too noisy */
    rc= TRlldInterruptService(sc->TRlldAdapter);
    if (rc == TRLLD_NO_INTERRUPT) 
        printf("oltr%d: interrupt not serviced.\n", unit);
}

#if NPCI > 0
static void
oltr_pci_intr(psc)
    void *psc;
{
    struct oltr_softc *sc = (struct oltr_softc *)psc;
    int rc = 0;

    /*printf("oltr%d: oltr_pci_intr\n", sc->unit);*/ /* Too noisy */
    rc = TRlldInterruptService(sc->TRlldAdapter);
    if (rc == TRLLD_NO_INTERRUPT)
        printf("oltr%d: pci interrupt not serviced.\n", sc->unit);
}
#endif /* NPCI */

static void 
oltr_start(ifp)
    struct ifnet *ifp;
{
    struct oltr_softc *sc = &oltr_softc[ifp->if_unit];
    struct mbuf *m0, *m;
    int  len, i, k, rc;

    /*printf("oltr%d: oltr_start\n", sc->unit);*/

outloop:

    i = (sc->tx_next  & TX_LIST_MASK); /* Just to shorten thing up */

    /* Check to see if we have enough room to transmit */
    if (sc->tx_avail <= 0) {
        /* No free buffers, hold off the upper layers */
        /*printf("oltr%d: transmit queue full.\n", sc->unit);*/
        ifp->if_flags |= IFF_OACTIVE;
        return;
    }

    if (sc->tx_frame.FragmentCount > 0) {
        if (!(sc->config->mode & TRLLD_MODE_16M)) {
            sc->tx_next++;
            m0 = sc->tx_buffer[i].m;
            goto restart;
        }
    }

    IF_DEQUEUE(&ifp->if_snd, m);
    if (m == 0) {
        /*printf("oltr%d: oltr_start NULL packet dequeued.\n", sc->unit);*/
        ifp->if_flags &= ~IFF_OACTIVE;
        return;
    }

    /* Keep a pointer to the head of the packet */
    m0 = m;

    if (sc->config->mode & TRLLD_MODE_16M) {  /*  ISA Adapters - bounce buffers */

        for (len = 0; m != 0; m = m->m_next) {
            sc->tx_frame.TransmitFragment[0].VirtualAddress = sc->tx_buffer[i].buf;
            sc->tx_frame.TransmitFragment[0].PhysicalAddress = kvtop(sc->tx_buffer[i].buf);
            bcopy(mtod(m, caddr_t), sc->tx_buffer[i].buf + len, m->m_len);
            len += m->m_len;
        }
        sc->tx_frame.FragmentCount = 1;
        sc->tx_frame.TransmitFragment[0].count = len;

        sc->tx_next++;
        sc->tx_avail--;

    } else {                                  /* PCI Adapters w/DMA */

        for (k = 0; m!= 0; m = m->m_next) {
            sc->tx_frame.TransmitFragment[k].VirtualAddress = mtod(m, caddr_t);
            sc->tx_frame.TransmitFragment[k].PhysicalAddress = kvtop(mtod(m, caddr_t));
            sc->tx_frame.TransmitFragment[k].count = m->m_len;
            k++;
            sc->tx_avail--;
        }
        sc->tx_frame.FragmentCount = k;
        sc->tx_buffer[i].count = k;
        sc->tx_buffer[i].m = m0;

        if (sc->tx_avail < 0) {
            /*printf("oltr%d: transmit buffers exhausted.\n", sc->unit);*/
            goto nobuffers;
        }
        sc->tx_next++;
    }

restart:
    rc = TRlldTransmitFrame(sc->TRlldAdapter, &sc->tx_frame, (void *)sc->tx_buffer[i].index);
    sc->tx_frame.FragmentCount = 0;

    if (rc != TRLLD_TRANSMIT_OK) {
        printf("oltr%d: TRlldTransmitFrame returned (%x)\n", sc->unit, rc);
        ifp->if_oerrors++;
        goto bad;
    }

    if (ifp->if_bpf)
        bpf_mtap(ifp, m0);

bad:

    if (sc->config->mode & TRLLD_MODE_16M) {
        m_freem(m0);
    }

    goto outloop;

nobuffers:

    ifp->if_flags |= IFF_OACTIVE;

    return;
}

static void
oltr_stop(sc)
    struct oltr_softc *sc;
{
    struct ifnet *ifp = &sc->arpcom.ac_if;
    printf("oltr%d: otlr_stop\n", sc->unit);
    ifp->if_flags &= ~(IFF_UP | IFF_RUNNING | IFF_OACTIVE);
    sc->hw_state = HW_CLOSING;
    TRlldClose(sc->TRlldAdapter, 0);
    sc->oltr_ch = timeout(oltr_timeout, (void *)sc->unit, 30*hz);
    tsleep((void *)sc->unit, 1, "oltrcl", 30*hz);
}

static int
oltr_ioctl(ifp, cmd, data)
    struct ifnet *ifp;
    u_long cmd;
    caddr_t data;
{
    struct oltr_softc *sc = &oltr_softc[ifp->if_unit];
    struct ifreq *ifr = (struct ifreq *)data;
    int error = 0, s;

    /*printf("oltr%d: oltr_ioctl\n", ifp->if_unit);*/

    s = splimp();

    switch (cmd) {
   
        case SIOCSIFADDR:
        case SIOCGIFADDR:
        case SIOCSIFMTU:
            error = iso88025_ioctl(ifp, cmd, data);
            break;

        case SIOCSIFFLAGS:
            /*
             * If the interface is marked up and stopped, then start it.
             * If it is marked down and running, then stop it.
             */
            if (ifp->if_flags & IFF_UP) {
                    if ((ifp->if_flags & IFF_RUNNING) == 0)
                            oltr_init(sc);
            } else {
                    if (ifp->if_flags & IFF_RUNNING) {
                            oltr_stop(sc);
                            ifp->if_flags &= ~IFF_RUNNING;
                    }
            }

            if ((ifp->if_flags & IFF_PROMISC) != sc->PromiscMode) {
                if (ifp->if_flags & IFF_PROMISC)
                    TRlldSetPromiscuousMode(sc->TRlldAdapter, OLTR_PROMISC_MODE);
                else
                    TRlldSetPromiscuousMode(sc->TRlldAdapter, 0);
                sc->PromiscMode = (ifp->if_flags & IFF_PROMISC);
            }
            
            break;
        case SIOCGIFMEDIA:
        case SIOCSIFMEDIA:
            error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, cmd);
            break;
        default:
            error = EINVAL;
    }
    splx(s);
    return(error);
}

/*
 * PMW Callback functions ----------------------------------------------------
 */

static void
DriverSuspend(MicroSeconds)
    unsigned short MicroSeconds;
{
    DELAY(MicroSeconds);
}


static void
DriverStatus(DriverHandle, Status)
    void *DriverHandle;
    TRlldStatus_t *Status;
{
    struct oltr_softc *sc = &oltr_softc[(int)DriverHandle];
    struct ifnet *ifp = &sc->arpcom.ac_if;

    switch (Status->Type) {
        case TRLLD_STS_ON_WIRE:
            if (sc->hw_state == HW_OPENING) {
                sc->hw_state = HW_OPEN;
                ifp->if_flags |= (IFF_UP | IFF_RUNNING);
                /*printf("oltr%d: Adapter inserted.\n", sc->unit);*/
                untimeout(oltr_timeout, (void *)sc->unit, sc->oltr_ch);
                wakeup_one((void *)sc->unit);
            }
            break;
        case TRLLD_STS_SELFTEST_STATUS:
            if (Status->Specification.SelftestStatus == TRLLD_ST_OK) {
                printf("oltr%d: adapter status good. (close completed/self-test)\n", sc->unit);
                if ((sc->hw_state == HW_LOADING) || (sc->hw_state == HW_CLOSING) || (sc->hw_state == HW_CLOSING2)) {
                    sc->hw_state = HW_CLOSED;
                    break;
                }
            } else {
                printf("oltr%d: Self test failed: ", sc->unit);
                switch (Status->Specification.SelftestStatus) {
                    case TRLLD_ST_ERROR + 0: printf("Initial Test Error\n"); break;
                    case TRLLD_ST_ERROR + 1: printf("Adapter Software Checksum Error\n"); break;
                    case TRLLD_ST_ERROR + 2: printf("Adapter RAM Error\n"); break;
                    case TRLLD_ST_ERROR + 4: printf("Instruction Test Error\n"); break;
                    case TRLLD_ST_ERROR + 5: printf("Protocol Handler/RI Hw Error\n"); break;
                    case TRLLD_ST_ERROR + 6: printf("System Interface Register Error\n"); break;
                    case TRLLD_ST_TIMEOUT:   printf("Selftest did not complete\n"); break;
                    default: printf("Unknown error (%x)\n", Status->Specification.SelftestStatus);
                }
            }
            break;
        case TRLLD_STS_INIT_STATUS:
            printf("oltr%d: Adapter initialization failed: ", sc->unit);
            switch(Status->Specification.InitStatus) {
                case TRLLD_INIT_ERROR + 0x01: printf("Invalid init block (LLD error)\n"); break;
                case TRLLD_INIT_ERROR + 0x02: printf("Invalid options (LLD error)\n"); break;
                case TRLLD_INIT_ERROR + 0x03: printf("Invalid rcv burst (LLD error)\n"); break;
                case TRLLD_INIT_ERROR + 0x04: printf("Invalid xmt burst (LLD error)\n"); break;
                case TRLLD_INIT_ERROR + 0x05: printf("Invalid DMA threshold (LLD error)\n"); break;
                case TRLLD_INIT_ERROR + 0x06: printf("Invalid scb addr\n"); break;
                case TRLLD_INIT_ERROR + 0x07: printf("Invalid ssb addr\n"); break;
                case TRLLD_INIT_ERROR + 0x08: printf("DIO parity error (HW error)\n"); break;
                case TRLLD_INIT_ERROR + 0x09: printf("DMA timeout (May be interrupt failing if PIO mode or PCI2)\n"); break;
                case TRLLD_INIT_ERROR + 0x0A: printf("DMA parity error (HW error)\n"); break;
                case TRLLD_INIT_ERROR + 0x0B: printf("DMA bus error (HW error)\n"); break;
                case TRLLD_INIT_ERROR + 0x0C: printf("DMA data error\n"); break;
                case TRLLD_INIT_ERROR + 0x0D: printf("Adapter Check\n"); break;
                case TRLLD_INIT_TIMEOUT:      printf("Adapter initialization did not complete\n"); break;
                case TRLLD_INIT_DMA_ERROR:    printf("Adapter cannot access system memory\n"); break;
                case TRLLD_INIT_INTR_ERROR:   printf("Adapter cannot interrupt\n"); break;
                case TRLLD_OPEN_TIMEOUT:      printf("Adapter did not complete open within 30 seconds\n"); break;
                case TRLLD_OPEN_ERROR + 0x01: printf("Invalid open options (LLD error)\n"); break;
                case TRLLD_OPEN_ERROR + 0x04: printf("TxBuffer count error (LLD error)\n"); break;
                case TRLLD_OPEN_ERROR + 0x10: printf("Buffer size error (LLD error)\n"); break;
                case TRLLD_OPEN_ERROR + 0x20: printf("List size error (LLD error)\n"); break;
                default: 
                    if (Status->Specification.InitStatus & 0x700) {
                        switch (Status->Specification.InitStatus & 0x70F) {
                            case TRLLD_OPEN_REPEAT + 0x01: printf("Lobe media test - "); break;
                            case TRLLD_OPEN_REPEAT + 0x02: printf("Physical insertion - "); break;
                            case TRLLD_OPEN_REPEAT + 0x03: printf("Address verification - "); break;
                            case TRLLD_OPEN_REPEAT + 0x04: printf("Participation in ring poll - "); break;
                            case TRLLD_OPEN_REPEAT + 0x05: printf("Request initialization - "); break;
                            case TRLLD_OPEN_REPEAT + 0x09: printf("Request registration (TXI) - "); break;
                            case TRLLD_OPEN_REPEAT + 0x0A: printf("Lobe media test (TXI) - "); break;
                            default:                       printf("Unknown phase (%x) - ", Status->Specification.InitStatus & 0x00F);
                        }
                        switch (Status->Specification.InitStatus & 0x7F0) {
                            case TRLLD_OPEN_REPEAT + 0x10: printf("Function failure (No cable?)\n"); break;
                            case TRLLD_OPEN_REPEAT + 0x20: printf("Signal loss\n"); break;
                            case TRLLD_OPEN_REPEAT + 0x50: printf("Timeout\n"); break;
                            case TRLLD_OPEN_REPEAT + 0x60: printf("Ring failure (TKP) / Protocol error (TXI)\n"); break;
                            case TRLLD_OPEN_REPEAT + 0x70: printf("Ring beaconing\n"); break;
                            case TRLLD_OPEN_REPEAT + 0x80: printf("Duplicate node address (TKP) / Insert denied (TXI)\n"); break;
                            case TRLLD_OPEN_REPEAT + 0x90: printf("Request initialization (TKP)\n"); break;
                            case TRLLD_OPEN_REPEAT + 0xa0: printf("Remove received\n"); break;
                            case TRLLD_OPEN_REPEAT + 0xb0: printf("C-port address changed (TXI)\n"); break;
                            default:                       printf("Unknown type (%x)\n", Status->Specification.InitStatus & 0x0F0);
                        }
                    } else {
                        printf("Unknown error (%x)\n", Status->Specification.InitStatus); 
                    }
            }
            break;
        case TRLLD_STS_RING_STATUS:
            if (Status->Specification.RingStatus != 0) {
                printf("oltr%d: Ring status change: ", sc->unit);
                if (Status->Specification.RingStatus & TRLLD_RS_HARD_ERROR)         printf("[Hard error] ");
                if (Status->Specification.RingStatus & TRLLD_RS_SOFT_ERROR)         printf("[Soft error] ");
                if (Status->Specification.RingStatus & TRLLD_RS_TRANSMIT_BEACON)    printf("[Transmit beacon] ");
                if (Status->Specification.RingStatus & TRLLD_RS_LOBE_WIRE_FAULT)    printf("[Wire fault] ");
                if (Status->Specification.RingStatus & TRLLD_RS_AUTO_REMOVAL_ERROR) printf("[Auto removal] ");
                if (Status->Specification.RingStatus & TRLLD_RS_REMOVE_RECEIVED)    printf("[Remove received] ");
                if (Status->Specification.RingStatus & TRLLD_RS_COUNTER_OVERFLOW)   printf("[Counter overflow] ");
                if (Status->Specification.RingStatus & TRLLD_RS_SINGLE_STATION)     printf("[Single station] ");
                if (Status->Specification.RingStatus & TRLLD_RS_RING_RECOVERY)      printf("[Ring recovery] ");
                printf("\n");
            }
            break;
        case TRLLD_STS_ADAPTER_CHECK:
            printf("oltr%d: Adapter check (%x %x %x %x)\n", sc->unit, Status->Specification.AdapterCheck[0], 
                    Status->Specification.AdapterCheck[1], Status->Specification.AdapterCheck[2], 
                    Status->Specification.AdapterCheck[3]); 
            break;
        case TRLLD_STS_PROMISCUOUS_STOPPED:
            printf("oltr%d: Promiscuous mode stopped: ", sc->unit);
            switch(Status->Specification.PromRemovedCause) {
                case TRLLD_PROM_REMOVE_RECEIVED: printf("Remove received\n"); break;
                case TRLLD_PROM_POLL_FAILURE:    printf("Poll failure\n"); break;
                default:                         printf("Unknown (%x)\n", Status->Specification.PromRemovedCause);
            }
            break;
        case TRLLD_STS_LLD_ERROR:
            printf("oltr%d: LLD error (%x %x %x %x) ", sc->unit, Status->Specification.InternalError[0], 
                    Status->Specification.InternalError[1], Status->Specification.InternalError[2], 
                    Status->Specification.InternalError[3]);
            break;
        case TRLLD_STS_ADAPTER_TIMEOUT:
            printf("oltr%d: Adapter operation timed out: ", sc->unit);
            switch(Status->Specification.AdapterTimeout) {
                case TRLLD_COMMAND_TIMEOUT:   printf("Command\n");
                case TRLLD_TRANSMIT_TIMEOUT:  printf("Transmit\n");
                case TRLLD_INTERRUPT_TIMEOUT: printf("Interrupt\n");
                default:                      printf("Unknown (%x)\n", Status->Specification.AdapterTimeout);
            }
            break;
        default:
            printf("oltr%d: Unknown status type (%x)\n", sc->unit, Status->Type);

    }
    if (Status->Closed) {
        if (sc->hw_state > HW_BAD) {
            sc->hw_state = HW_FAILED;
            printf("oltr%d: closing adapter due to failure.\n", sc->unit);
            oltr_stop(sc);
        }
    }
}

static void
DriverCloseCompleted(DriverHandle)
    void *DriverHandle;
{
    struct oltr_softc *sc = &oltr_softc[(int)DriverHandle];

    printf("oltr%d: DriverCloseCompleted\n", sc->unit);

    untimeout(oltr_timeout, (void *)sc->unit, sc->oltr_ch);
    wakeup_one((void *)sc->unit);

    if ((sc->hw_state != HW_CLOSING) && (sc->hw_state != HW_CLOSING2) && (sc->hw_state != HW_CLOSED)) {
        printf("oltr%d: adapter close complete called in wrong state (%d)\n", sc->unit, sc->hw_state);
        return;
    }
    sc->hw_state = HW_CLOSING2;
    if (sc->config->dmalevel != TRLLD_DMA_PIO)
        isa_dma_release(sc->config->dmalevel);
    
}

static void
DriverStatistics(DriverHandle, Statistics)
    void *DriverHandle;
    TRlldStatistics_t *Statistics;
{
    printf("oltr: DriverStatistics\n");
}

static void
DriverTransmitFrameCompleted(DriverHandle, FrameHandle, TransmitStatus)
    void *DriverHandle;
    void *FrameHandle;
    int TransmitStatus;
{
    int frame = (int)FrameHandle;
    struct oltr_softc *sc = &oltr_softc[(int)DriverHandle];
    struct ifnet *ifp = &sc->arpcom.ac_if;

    /*printf("oltr%d: transmit complete frame %d\n", sc->unit, frame);*/
    if (TransmitStatus == TRLLD_TRANSMIT_OK) {
        ifp->if_opackets++;
    } else {
        printf("oltr%d: DriverTransmitFrameCompleted (frame %d status %x)\n", sc->unit, frame, TransmitStatus);
        ifp->if_oerrors++;
    }

    if ((frame < 0) || (frame > TX_LIST_SIZE)) {
        printf("oltr%d: bogus transmit frame. (%d)\n", sc->unit, frame);
        return;
    }

    if (sc->config->mode & TRLLD_MODE_16M) {
        sc->tx_avail++;
    } else {
        m_freem(sc->tx_buffer[frame].m);
        sc->tx_avail += sc->tx_buffer[frame].count;
    }

    if ((ifp->if_flags & IFF_OACTIVE) && (sc->tx_avail > 0)) {
        ifp->if_flags &= ~(IFF_OACTIVE);
        oltr_start(ifp);
    }

}

static void
DriverReceiveFrameCompleted(DriverHandle, ByteCount, FragmentCount, FragmentHandle, ReceiveStatus)
    void *DriverHandle;
    int ByteCount;
    int FragmentCount;
    void *FragmentHandle;
    int ReceiveStatus;
{
    struct oltr_softc *sc = &oltr_softc[(int)DriverHandle];
    struct ifnet *ifp = &sc->arpcom.ac_if;
    struct iso88025_header *th;
    struct mbuf *m0, *m1, *m;
    int j = (int)FragmentHandle, rc, frame_len = ByteCount, mac_hdr_len;
    int mbuf_offset, mbuf_size, frag_offset, length;
    char *frag = sc->rx_buffer[j].buf;

    /*printf("oltr%d: ReceiveFrameCompleted (Size %d Count %d Start %d)\n", sc->unit, ByteCount, FragmentCount, j);*/

    if (sc->hw_state >=  HW_OPEN) {             /* Hardware operating normally */
        if (frag != sc->rx_buffer[sc->rx_next & RX_LIST_MASK].buf) {
            printf("oltr%d: ring buffer pointer blown\n", sc->unit);
            oltr_stop(sc);
            return;
        }
        if (ReceiveStatus == TRLLD_RCV_OK) {    /* Receive good frame */
            MGETHDR(m0, M_DONTWAIT, MT_DATA);
            mbuf_size = MHLEN;
            if (m0 == NULL) {
                ifp->if_ierrors++;
                goto out;
            }
            if (ByteCount + 2 > MHLEN) {
                MCLGET(m0, M_DONTWAIT);
                mbuf_size = MCLBYTES;
                if ((m0->m_flags & M_EXT) == 0) {
                    m_freem(m0);
                    ifp->if_ierrors++;
                    goto out;
                }
            }

            m0->m_pkthdr.rcvif = &sc->arpcom.ac_if;
            m0->m_pkthdr.len = ByteCount;
            m0->m_len = 0;
            m0->m_data += 2;
            mbuf_size -=2;
            th = mtod(m0, struct iso88025_header *);
            m0->m_pkthdr.header = (void *)th;

            m = m0; mbuf_offset = 0; frag_offset = 0;
            while (frame_len > 0) {
                length = MIN3(frame_len, (RX_BUFFER_LEN - frag_offset), (mbuf_size - mbuf_offset));
                bcopy(frag + frag_offset, mtod(m, char *) + mbuf_offset, length);
                m->m_len += length;
                mbuf_offset += length;
                frag_offset += length;
                frame_len -= length;
                if (frag_offset == RX_BUFFER_LEN) {
                    frag = sc->rx_buffer[++j].buf;
                    frag_offset = 0;
                }
                if ((mbuf_offset == mbuf_size) && (frame_len > 0)) {
                    MGET(m1, M_DONTWAIT, MT_DATA);
                    mbuf_size = MHLEN;
                    if (m1 == NULL) {
                        ifp->if_ierrors++;
                        m_freem(m0);
                        goto out;
                    }  
                    if (frame_len > MHLEN) {
                        MCLGET(m1, M_DONTWAIT);
                        mbuf_size = MCLBYTES;
                        if ((m1->m_flags & M_EXT) == 0) {
                            m_freem(m0);
                            m_freem(m1);
                            ifp->if_ierrors++;
                            goto out;
                        }
                    }
                    m->m_next = m1;
                    m = m1;
                    mbuf_offset = 0;
                    m->m_len = 0;
                }
            }
            ifp->if_ipackets++;
        
            if (ifp->if_bpf)
                bpf_mtap(ifp, m0);

            if (ifp->if_flags & IFF_PROMISC)
                if (bcmp(th->iso88025_dhost, etherbroadcastaddr, sizeof(th->iso88025_dhost)) != 0) {
                    if (((th->iso88025_dhost[0] & 0x7f) != sc->arpcom.ac_enaddr[0]) ||
                        (bcmp(th->iso88025_dhost + 1, sc->arpcom.ac_enaddr + 1, ISO88025_ADDR_LEN - 1))) {
	                m_freem(m0);
                        goto out;
                    }
            }

            mac_hdr_len = ISO88025_HDR_LEN;
            if (th->iso88025_shost[0] & 0x80) /* Check for source routing info */
                mac_hdr_len +=  (ntohs(th->rcf) & 0x1f00) >> 8;
            
            m0->m_pkthdr.len -= mac_hdr_len;
            m0->m_len -= mac_hdr_len;
            m0->m_data += mac_hdr_len;

            iso88025_input(&sc->arpcom.ac_if, th, m0);

        } else {
            if (ReceiveStatus != TRLLD_RCV_NO_DATA) {
                printf("oltr%d: receive error. (ReceiveStatus=%d)\n", sc->unit, ReceiveStatus);
                ifp->if_ierrors++;
            }
        }
out:
        while (FragmentCount > 0) {
            rc = TRlldReceiveFragment(sc->TRlldAdapter, 
                                     (void *)sc->rx_buffer[sc->rx_next & RX_LIST_MASK].buf,
                                     kvtop(sc->rx_buffer[sc->rx_next & RX_LIST_MASK].buf),
                                     RX_BUFFER_LEN,
                                     (void *)sc->rx_buffer[sc->rx_next & RX_LIST_MASK].index);
            if (rc == TRLLD_RECEIVE_OK) {
                sc->rx_next++;
                FragmentCount--;
            } else {
                printf("oltr%d: Adapter refused fragment (%d).\n", sc->unit, sc->rx_next - 1);
                sc->rx_avail += FragmentCount;
                break;
            }
        }
    } else {                                    /* Hardware being closed */
        if (frag != sc->rx_buffer[sc->rx_next++ & RX_LIST_MASK].buf) {
            printf("oltr%d: ring buffer pointer blown\n", sc->unit);  
        }           
        sc->rx_avail += FragmentCount;
    }
    
}


/*
 * ---------------------------- PMW Glue -------------------------------
 */

#ifndef TRlldInlineIO

static void 
DriverOutByte(IOAddress, value)
    unsigned short IOAddress;
    unsigned char value;
{
    outb(IOAddress, value);
}

static void
DriverOutWord(IOAddress, value)
    unsigned short IOAddress;
    unsigned short value;
{
    outw(IOAddress, value);
}

static void
DriverOutDword(IOAddress, value)
    unsigned short IOAddress;
    unsigned long value;
{
    outl(IOAddress, value);
}

static void
DriverRepOutByte(IOAddress, DataPointer, ByteCount)
    unsigned short IOAddress;
    unsigned char  *DataPointer;
    int ByteCount;
{
    outsb(IOAddress, (void *)DataPointer, ByteCount);
}

static void
DriverRepOutWord(IOAddress, DataPointer, WordCount)
    unsigned short IOAddress;
    unsigned short *DataPointer;
    int WordCount;
{
    outsw(IOAddress, (void *)DataPointer, WordCount);
}

static void
DriverRepOutDword(IOAddress, DataPointer, DWordCount)
    unsigned short IOAddress;
    unsigned long  *DataPointer;
    int DWordCount;
{
    outsl(IOAddress, (void *)DataPointer, DWordCount);
}

static unsigned char
DriverInByte(IOAddress)
    unsigned short IOAddress;
{
    return(inb(IOAddress));
}

static unsigned short
DriverInWord(IOAddress)
    unsigned short IOAddress;
{
   return(inw(IOAddress));
}

static unsigned long
DriverInDword(IOAddress)
    unsigned short IOAddress;
{
    return(inl(IOAddress));
}

static void
DriverRepInByte(IOAddress, DataPointer, ByteCount)
    unsigned short IOAddress;
    unsigned char  *DataPointer;
    int ByteCount;
{
    insb(IOAddress, (void *)DataPointer, ByteCount);
}

static void
DriverRepInWord(IOAddress, DataPointer, WordCount)
    unsigned short IOAddress;
    unsigned short *DataPointer;
    int WordCount;
{
    insw(IOAddress, (void *)DataPointer, WordCount);
}
static void
DriverRepInDword(IOAddress, DataPointer, DWordCount)
    unsigned short IOAddress;
    unsigned long  *DataPointer;
    int DWordCount;
{
    insl(IOAddress, (void *)DataPointer, DWordCount);
}
#endif /* TRlldInlineIO */
