Annotation of Gnu-Mach/i386/i386at/gpl/if_wd.c, revision 1.1.1.1

1.1       root        1: /* 
                      2:  * Copyright (c) 1994 Shantanu Goel
                      3:  * All Rights Reserved.
                      4:  * 
                      5:  * Permission to use, copy, modify and distribute this software and its
                      6:  * documentation is hereby granted, provided that both the copyright
                      7:  * notice and this permission notice appear in all copies of the
                      8:  * software, derivative works or modified versions, and any portions
                      9:  * thereof, and that both notices appear in supporting documentation.
                     10:  * 
                     11:  * THE AUTHOR ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
                     12:  * CONDITION.  THE AUTHOR DISCLAIMS ANY LIABILITY OF ANY KIND FOR
                     13:  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
                     14:  */
                     15: 
                     16: /*
                     17:  * Written 1993 by Donald Becker.
                     18:  * 
                     19:  * Copyright 1993 United States Government as represented by the
                     20:  * Director, National Security Agency.  This software may be used and
                     21:  * distributed according to the terms of the GNU Public License,
                     22:  * incorporated herein by reference.
                     23:  *
                     24:  * The Author may be reached as [email protected] or
                     25:  * C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
                     26:  */
                     27: 
                     28: #include <wd.h>
                     29: #if NWD > 0
                     30: /*
                     31:  * Driver for SMC/Western Digital Ethernet adaptors.
                     32:  * Derived from the Linux driver by Donald Becker.
                     33:  *
                     34:  * Shantanu Goel ([email protected])
                     35:  */
                     36: #include <mach/sa/sys/types.h>
                     37: #include "vm_param.h"
                     38: #include <kern/time_out.h>
                     39: #include <device/device_types.h>
                     40: #include <device/errno.h>
                     41: #include <device/io_req.h>
                     42: #include <device/if_hdr.h>
                     43: #include <device/if_ether.h>
                     44: #include <device/net_status.h>
                     45: #include <device/net_io.h>
                     46: #include <chips/busses.h>
                     47: #include <i386/machspl.h>
                     48: #include <i386/pio.h>
                     49: #include <i386at/gpl/if_nsreg.h>
                     50: 
                     51: #define WD_START_PG    0x00    /* first page of TX buffer */
                     52: #define WD03_STOP_PG   0x20    /* last page +1 of RX ring */
                     53: #define WD13_STOP_PG   0x40    /* last page +1 of RX ring */
                     54: 
                     55: #define WD_CMDREG      0       /* offset of ASIC command register */
                     56: #define  WD_RESET      0x80    /* board reset in WDTRA_CMDREG */
                     57: #define  WD_MEMEN      0x40    /* enable shared memory */
                     58: #define WD_CMDREG5     5       /* offset of 16-bit-only ASIC register 5 */
                     59: #define  ISA16         0x80    /* enable 16 bit access from the ISA bus */
                     60: #define  NIC16         0x40    /* enable 16 bit access from the 8390 */
                     61: #define WD_NIC_OFF     16      /* NIC register offset */
                     62: 
                     63: #define wdunit(dev)    minor(dev)
                     64: 
                     65: /*
                     66:  * Autoconfiguration stuff.
                     67:  */
                     68: int    wdprobe();
                     69: void   wdattach();
                     70: int    wdstd[] = { 0x300, 0x280, 0x380, 0x240, 0 };
                     71: struct bus_device *wdinfo[NWD];
                     72: struct bus_driver wddriver = {
                     73:        wdprobe, 0, wdattach, 0, wdstd, "wd", wdinfo, 0, 0, 0
                     74: };
                     75: 
                     76: /*
                     77:  * NS8390 state.
                     78:  */
                     79: struct nssoftc wdnssoftc[NWD];
                     80: 
                     81: /*
                     82:  * Board state.
                     83:  */
                     84: struct wdsoftc {
                     85:        int     sc_mstart;      /* start of board's RAM */
                     86:        int     sc_mend;        /* end of board's RAM */
                     87:        int     sc_rmstart;     /* start of receive RAM */
                     88:        int     sc_rmend;       /* end of receive RAM */
                     89:        int     sc_reg0;        /* copy of register 0 of ASIC */
                     90:        int     sc_reg5;        /* copy of register 5 of ASIC */
                     91: } wdsoftc[NWD];
                     92: 
                     93: void   wdstart(int);
                     94: void   wd_reset(struct nssoftc *sc);
                     95: void   wd_input(struct nssoftc *sc, int, char *, int);
                     96: int    wd_output(struct nssoftc *sc, int, char *, int);
                     97: 
                     98: /*
                     99:  * Watchdog.
                    100:  */
                    101: int    wdwstart = 0;
                    102: void   wdwatch(void);
                    103: 
                    104: #define WDDEBUG
                    105: #ifdef WDDEBUG
                    106: int    wddebug = 0;
                    107: #define DEBUGF(stmt)   { if (wddebug) stmt; }
                    108: #else
                    109: #define DEBUGF(stmt)
                    110: #endif
                    111: 
                    112: /*
                    113:  * Probe for the WD8003 and WD8013.
                    114:  * These cards have the station address PROM at I/O ports <base>+8
                    115:  * to <base>+13, with a checksum following.  A Soundblaster can have
                    116:  * the same checksum as a WD ethercard, so we have an extra exclusionary
                    117:  * check for it.
                    118:  */
                    119: int
                    120: wdprobe(xxx, ui)
                    121:        int xxx;
                    122:        struct bus_device *ui;
                    123: {
                    124:        int *port;
                    125: 
                    126:        if (ui->unit >= NWD) {
                    127:                printf("wd%d: not configured\n", ui->unit);
                    128:                return (0);
                    129:        }
                    130:        for (port = wdstd; *port; port++) {
                    131:                if (*port < 0)
                    132:                        continue;
                    133:                if (inb(*port + 8) != 0xff
                    134:                    && inb(*port + 9) != 0xff
                    135:                    && wdprobe1(*port, ui)) {
                    136:                        ui->address = *port;
                    137:                        *port = -1;
                    138:                        return (1);
                    139:                }
                    140:        }
                    141:        return (0);
                    142: }
                    143: 
                    144: int
                    145: wdprobe1(port, ui)
                    146:        int port;
                    147:        struct bus_device *ui;
                    148: {
                    149: 
                    150:        int i, irq = 0, checksum = 0, ancient = 0, word16 = 0;
                    151:        struct wdsoftc *wd = &wdsoftc[ui->unit];
                    152:        struct nssoftc *ns = &wdnssoftc[ui->unit];
                    153:        struct ifnet *ifp = &ns->sc_if;
                    154: 
                    155:        for (i = 0; i < 8; i++)
                    156:                checksum += inb(port + 8 + i);
                    157:        if ((checksum & 0xff) != 0xff)
                    158:                return (0);
                    159: 
                    160:        printf("wd%d: WD80x3 at 0x%03x, ", ui->unit, port);
                    161:        for (i = 0; i < ETHER_ADDR_LEN; i++) {
                    162:                if (i == 0)
                    163:                        printf("%02x", ns->sc_addr[i] = inb(port + 8 + i));
                    164:                else
                    165:                        printf(":%02x", ns->sc_addr[i] = inb(port + 8 + i));
                    166:        }
                    167:        /*
                    168:         * Check for PureData.
                    169:         */
                    170:        if (inb(port) == 'P' && inb(port + 1) == 'D') {
                    171:                u_char reg5 = inb(port + 5);
                    172: 
                    173:                switch (inb(port + 2)) {
                    174: 
                    175:                case 0x03:
                    176:                case 0x05:
                    177:                        word16 = 0;
                    178:                        break;
                    179: 
                    180:                case 0x0a:
                    181:                        word16 = 1;
                    182:                        break;
                    183: 
                    184:                default:
                    185:                        word16 = 0;
                    186:                        break;
                    187:                }
                    188:                wd->sc_mstart = ((reg5 & 0x1c) + 0xc0) << 12;
                    189:                irq = (reg5 & 0xe0) == 0xe0 ? 10 : (reg5 >> 5) + 1;
                    190:        } else {
                    191:                /*
                    192:                 * Check for 8 bit vs 16 bit card.
                    193:                 */
                    194:                for (i = 0; i < ETHER_ADDR_LEN; i++)
                    195:                        if (inb(port + i) != inb(port + 8 + i))
                    196:                                break;
                    197:                if (i >= ETHER_ADDR_LEN) {
                    198:                        ancient = 1;
                    199:                        word16 = 0;
                    200:                } else {
                    201:                        int tmp = inb(port + 1);
                    202: 
                    203:                        /*
                    204:                         * Attempt to clear 16bit bit.
                    205:                         */
                    206:                        outb(port + 1, tmp ^ 0x01);
                    207:                        if (((inb(port + 1) & 0x01) == 0x01)    /* 16 bit */
                    208:                            && (tmp & 0x01) == 0x01) {  /* in 16 bit slot */
                    209:                                int asic_reg5 = inb(port + WD_CMDREG5);
                    210: 
                    211:                                /*
                    212:                                 * Magic to set ASIC to word-wide mode.
                    213:                                 */
                    214:                                outb(port+WD_CMDREG5, NIC16|(asic_reg5&0x1f));
                    215:                                outb(port + 1, tmp);
                    216:                                word16 = 1;
                    217:                        } else
                    218:                                word16 = 0;
                    219:                        outb(port + 1, tmp);
                    220:                }
                    221:                if (!ancient && (inb(port + 1) & 0x01) != (word16 & 0x01))
                    222:                        printf("\nwd%d: bus width conflict, "
                    223:                               "%d (probe) != %d (reg report)", ui->unit,
                    224:                               word16 ? 16 : 8, (inb(port+1) & 0x01) ? 16 : 8);
                    225:        }
                    226:        /*
                    227:         * Determine board's RAM location.
                    228:         */
                    229:        if (wd->sc_mstart == 0) {
                    230:                int reg0 = inb(port);
                    231: 
                    232:                if (reg0 == 0xff || reg0 == 0)
                    233:                        wd->sc_mstart = 0xd0000;
                    234:                else {
                    235:                        int high_addr_bits = inb(port + WD_CMDREG5) & 0x1f;
                    236: 
                    237:                        if (high_addr_bits == 0x1f || word16 == 0)
                    238:                                high_addr_bits = 0x01;
                    239:                        wd->sc_mstart = ((reg0&0x3f)<<13)+(high_addr_bits<<19);
                    240:                }
                    241:        }
                    242:        /*
                    243:         * Determine irq.
                    244:         */
                    245:        if (irq == 0) {
                    246:                int irqmap[] = { 9, 3, 5, 7, 10, 11, 15, 4 };
                    247:                int reg1 = inb(port + 1);
                    248:                int reg4 = inb(port + 4);
                    249: 
                    250:                /*
                    251:                 * For old card, irq must be supplied.
                    252:                 */
                    253:                if (ancient || reg1 == 0xff) {
                    254:                        if (ui->sysdep1 == 0) {
                    255:                                printf("\nwd%d: must specify IRQ for card\n",
                    256:                                       ui->unit);
                    257:                                return (0);
                    258:                        }
                    259:                        irq = ui->sysdep1;
                    260:                } else {
                    261:                        DEBUGF({
                    262:                                int i = ((reg4 >> 5) & 0x03) + (reg1 & 0x04);
                    263: 
                    264:                                printf("\nwd%d: irq index %d\n", ui->unit, i);
                    265:                                printf("wd%d:", ui->unit);
                    266:                        })
                    267:                        irq = irqmap[((reg4 >> 5) & 0x03) + (reg1 & 0x04)];
                    268:                }
                    269:        } else if (irq == 2)
                    270:                irq = 9;
                    271:        ui->sysdep1 = irq;
                    272:        take_dev_irq(ui);
                    273:        printf(", irq %d", irq);
                    274: 
                    275:        /*
                    276:         * Initialize 8390 state.
                    277:         */
                    278:        ns->sc_name = ui->name;
                    279:        ns->sc_unit = ui->unit;
                    280:        ns->sc_port = port + WD_NIC_OFF;
                    281:        ns->sc_reset = wd_reset;
                    282:        ns->sc_input = wd_input;
                    283:        ns->sc_output = wd_output;
                    284:        ns->sc_pingpong = 1;
                    285:        ns->sc_word16 = word16;
                    286:        ns->sc_txstrtpg = WD_START_PG;
                    287:        ns->sc_rxstrtpg = WD_START_PG + TX_PAGES(ns);
                    288:        ns->sc_stoppg = word16 ? WD13_STOP_PG : WD03_STOP_PG;
                    289: 
                    290:        wd->sc_rmstart = wd->sc_mstart + TX_PAGES(ns) * 256;
                    291:        wd->sc_mend = wd->sc_rmend
                    292:                = wd->sc_mstart + (ns->sc_stoppg - WD_START_PG) * 256;
                    293:        printf(", memory 0x%05x-0x%05x", wd->sc_mstart, wd->sc_mend);
                    294: 
                    295:        if (word16)
                    296:                printf(", 16 bit");
                    297:        printf("\n");
                    298: 
                    299:        DEBUGF(printf("wd%d: txstrtpg %d rxstrtpg %d num_pages %d\n",
                    300:                      ui->unit, ns->sc_txstrtpg, ns->sc_rxstrtpg,
                    301:                      (wd->sc_mend - wd->sc_mstart) / 256));
                    302: 
                    303:        /*
                    304:         * Initialize interface header.
                    305:         */
                    306:        ifp->if_unit = ui->unit;
                    307:        ifp->if_mtu = ETHERMTU;
                    308:        ifp->if_flags = IFF_BROADCAST;
                    309:        ifp->if_header_size = sizeof(struct ether_header);
                    310:        ifp->if_header_format = HDR_ETHERNET;
                    311:        ifp->if_address_size = ETHER_ADDR_LEN;
                    312:        ifp->if_address = ns->sc_addr;
                    313:        if_init_queues(ifp);
                    314: 
                    315:        return (1);
                    316: }
                    317: 
                    318: void
                    319: wdattach(ui)
                    320:        struct bus_device *ui;
                    321: {
                    322:        /*
                    323:         * void
                    324:         */
                    325: }
                    326: 
                    327: int
                    328: wdopen(dev, flag)
                    329:        dev_t dev;
                    330:        int flag;
                    331: {
                    332:        int unit = wdunit(dev), s;
                    333:        struct bus_device *ui;
                    334:        struct wdsoftc *wd;
                    335:        struct nssoftc *ns;
                    336: 
                    337:        if (unit >= NWD || (ui = wdinfo[unit]) == 0 || ui->alive == 0)
                    338:                return (ENXIO);
                    339: 
                    340:        /*
                    341:         * Start watchdog.
                    342:         */
                    343:        if (!wdwstart) {
                    344:                wdwstart++;
                    345:                timeout(wdwatch, 0, hz);
                    346:        }
                    347:        wd = &wdsoftc[unit];
                    348:        ns = &wdnssoftc[unit];
                    349:        ns->sc_if.if_flags |= IFF_UP;
                    350:        s = splimp();
                    351:        wd->sc_reg0 = ((wd->sc_mstart >> 13) & 0x3f) | WD_MEMEN;
                    352:        wd->sc_reg5 = ((wd->sc_mstart >> 19) & 0x1f) | NIC16;
                    353:        if (ns->sc_word16)
                    354:                outb(ui->address + WD_CMDREG5, wd->sc_reg5);
                    355:        outb(ui->address, wd->sc_reg0);
                    356:        nsinit(ns);
                    357:        splx(s);
                    358:        return (0);
                    359: }
                    360: 
                    361: int
                    362: wdoutput(dev, ior)
                    363:        dev_t dev;
                    364:        io_req_t ior;
                    365: {
                    366:        int unit = wdunit(dev);
                    367:        struct bus_device *ui;
                    368: 
                    369:        if (unit >= NWD || (ui = wdinfo[unit]) == 0 || ui->alive == 0)
                    370:                return (ENXIO);
                    371: 
                    372:        return (net_write(&wdnssoftc[unit].sc_if, wdstart, ior));
                    373: }
                    374: 
                    375: int
                    376: wdsetinput(dev, receive_port, priority, filter, filter_count)
                    377:        dev_t dev;
                    378:        mach_port_t receive_port;
                    379:        int priority;
                    380:        filter_t *filter;
                    381:        unsigned filter_count;
                    382: {
                    383:        int unit = wdunit(dev);
                    384:        struct bus_device *ui;
                    385: 
                    386:        if (unit >= NWD || (ui = wdinfo[unit]) == 0 || ui->alive == 0)
                    387:                return (ENXIO);
                    388: 
                    389:        return (net_set_filter(&wdnssoftc[unit].sc_if, receive_port,
                    390:                               priority, filter, filter_count));
                    391: }
                    392: 
                    393: int
                    394: wdgetstat(dev, flavor, status, count)
                    395:        dev_t dev;
                    396:        int flavor;
                    397:        dev_status_t status;
                    398:        unsigned *count;
                    399: {
                    400:        int unit = wdunit(dev);
                    401:        struct bus_device *ui;
                    402: 
                    403:        if (unit >= NWD || (ui = wdinfo[unit]) == 0 || ui->alive == 0)
                    404:                return (ENXIO);
                    405: 
                    406:        return (net_getstat(&wdnssoftc[unit].sc_if, flavor, status, count));
                    407: }
                    408: 
                    409: int
                    410: wdsetstat(dev, flavor, status, count)
                    411:        dev_t dev;
                    412:        int flavor;
                    413:        dev_status_t status;
                    414:        unsigned count;
                    415: {
                    416:        int unit = wdunit(dev), oflags, s;
                    417:        struct bus_device *ui;
                    418:        struct ifnet *ifp;
                    419:        struct net_status *ns;
                    420: 
                    421:        if (unit >= NWD || (ui = wdinfo[unit]) == 0 || ui->alive == 0)
                    422:                return (ENXIO);
                    423: 
                    424:        ifp = &wdnssoftc[unit].sc_if;
                    425: 
                    426:        switch (flavor) {
                    427: 
                    428:        case NET_STATUS:
                    429:                if (count < NET_STATUS_COUNT)
                    430:                        return (D_INVALID_SIZE);
                    431:                ns = (struct net_status *)status;
                    432:                oflags = ifp->if_flags & (IFF_ALLMULTI|IFF_PROMISC);
                    433:                ifp->if_flags &= ~(IFF_ALLMULTI|IFF_PROMISC);
                    434:                ifp->if_flags |= ns->flags & (IFF_ALLMULTI|IFF_PROMISC);
                    435:                if ((ifp->if_flags & (IFF_ALLMULTI|IFF_PROMISC)) != oflags) {
                    436:                        s = splimp();
                    437:                        nsinit(&wdnssoftc[unit]);
                    438:                        splx(s);
                    439:                }
                    440:                break;
                    441: 
                    442:        default:
                    443:                return (D_INVALID_OPERATION);
                    444:        }
                    445:        return (D_SUCCESS);
                    446: }
                    447: 
                    448: void
                    449: wdintr(unit)
                    450:        int unit;
                    451: {
                    452:        nsintr(&wdnssoftc[unit]);
                    453: }
                    454: 
                    455: void
                    456: wdstart(unit)
                    457:        int unit;
                    458: {
                    459:        nsstart(&wdnssoftc[unit]);
                    460: }
                    461: 
                    462: void
                    463: wd_reset(ns)
                    464:        struct nssoftc *ns;
                    465: {
                    466:        int port = ns->sc_port - WD_NIC_OFF;    /* ASIC base address */
                    467:        struct wdsoftc *wd = &wdsoftc[ns->sc_unit];
                    468: 
                    469:        outb(port, WD_RESET);
                    470:        outb(0x80, 0);          /* I/O delay */
                    471:        /*
                    472:         * Set up the ASIC registers, just in case something changed them.
                    473:         */
                    474:        outb(port, ((wd->sc_mstart >> 13) & 0x3f) | WD_MEMEN);
                    475:        if (ns->sc_word16)
                    476:                outb(port + WD_CMDREG5, NIC16 | ((wd->sc_mstart>>19) & 0x1f));
                    477: }
                    478: 
                    479: void
                    480: wd_input(ns, count, buf, ring_offset)
                    481:        struct nssoftc *ns;
                    482:        int count;
                    483:        char *buf;
                    484:        int ring_offset;
                    485: {
                    486:        int port = ns->sc_port - WD_NIC_OFF;
                    487:        int xfer_start;
                    488:        struct wdsoftc *wd = &wdsoftc[ns->sc_unit];
                    489: 
                    490:        DEBUGF(printf("wd%d: ring_offset = %d\n", ns->sc_unit, ring_offset));
                    491: 
                    492:        xfer_start = wd->sc_mstart + ring_offset - (WD_START_PG << 8);
                    493: 
                    494:        /*
                    495:         * The NIC driver calls us 3 times.  Once to read the NIC 4 byte
                    496:         * header, next to read the Ethernet header and finally to read
                    497:         * the actual data.  We enable 16 bit mode before the NIC header
                    498:         * and disable it after the packet body.
                    499:         */
                    500:        if (count == 4) {
                    501:                if (ns->sc_word16)
                    502:                        outb(port + WD_CMDREG5, ISA16 | wd->sc_reg5);
                    503:                ((int *)buf)[0] = ((int *)phystokv(xfer_start))[0];
                    504:                return;
                    505:        }
                    506:        if (count == sizeof(struct ether_header)) {
                    507:                xfer_start = (int)phystokv(xfer_start);
                    508:                ((int *)buf)[0] = ((int *)xfer_start)[0];
                    509:                ((int *)buf)[1] = ((int *)xfer_start)[1];
                    510:                ((int *)buf)[2] = ((int *)xfer_start)[2];
                    511:                ((short *)(buf + 12))[0] = ((short *)(xfer_start + 12))[0];
                    512:                return;
                    513:        }
                    514:        if (xfer_start + count > wd->sc_rmend) {
                    515:                int semi_count = wd->sc_rmend - xfer_start;
                    516: 
                    517:                /*
                    518:                 * Input move must be wrapped.
                    519:                 */
                    520:                bcopy((char *)phystokv(xfer_start), buf, semi_count);
                    521:                count -= semi_count;
                    522:                bcopy((char *)phystokv(wd->sc_rmstart),buf+semi_count,count);
                    523:        } else
                    524:                bcopy((char *)phystokv(xfer_start), buf, count);
                    525:        if (ns->sc_word16)
                    526:                outb(port + WD_CMDREG5, wd->sc_reg5);
                    527: }
                    528: 
                    529: int
                    530: wd_output(ns, count, buf, start_page)
                    531:        struct nssoftc *ns;
                    532:        int count;
                    533:        char *buf;
                    534:        int start_page;
                    535: {
                    536:        char *shmem;
                    537:        int i, port = ns->sc_port - WD_NIC_OFF;
                    538:        struct wdsoftc *wd = &wdsoftc[ns->sc_unit];
                    539: 
                    540:        DEBUGF(printf("wd%d: start_page = %d\n", ns->sc_unit, start_page));
                    541: 
                    542:        shmem = (char *)phystokv(wd->sc_mstart+((start_page-WD_START_PG)<<8));
                    543:        if (ns->sc_word16) {
                    544:                outb(port + WD_CMDREG5, ISA16 | wd->sc_reg5);
                    545:                bcopy(buf, shmem, count);
                    546:                outb(port + WD_CMDREG5, wd->sc_reg5);
                    547:        } else
                    548:                bcopy(buf, shmem, count);
                    549:        while (count <  ETHERMIN + sizeof(struct ether_header)) {
                    550:                *(shmem + count) = 0;
                    551:                count++;
                    552:        }
                    553:        return (count);
                    554: }
                    555: 
                    556: /*
                    557:  * Watchdog.
                    558:  * Check for hung transmissions.
                    559:  */
                    560: void
                    561: wdwatch()
                    562: {
                    563:        int unit, s;
                    564:        struct nssoftc *ns;
                    565: 
                    566:        timeout(wdwatch, 0, hz);
                    567: 
                    568:        s = splimp();
                    569:        for (unit = 0; unit < NWD; unit++) {
                    570:                if (wdinfo[unit] == 0 || wdinfo[unit]->alive == 0)
                    571:                        continue;
                    572:                ns = &wdnssoftc[unit];
                    573:                if (ns->sc_timer && --ns->sc_timer == 0) {
                    574:                        printf("wd%d: transmission timeout\n", unit);
                    575:                        nsinit(ns);
                    576:                }
                    577:        }
                    578:        splx(s);
                    579: }
                    580: 
                    581: #endif /* NWD > 0 */

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.