Annotation of coherent/b/STREAMS/conf/streams/src/ether.c, revision 1.1

1.1     ! root        1: /* STREAMS style driver for the DP8390 Ethernet interface */
        !             2: /*
        !             3:  * An interesting problem is how to manage the interface address binding,
        !             4:  * especially for upper protocols (specifically, the Internet ARP protocol)
        !             5:  * that need to advertise the physical host address, especially in the
        !             6:  * presence of multiple hardware adapters (or network types).
        !             7:  *
        !             8:  * For now, I am happy with having a specific version of ARP for each network
        !             9:  * type, and having ARP catch any M_PROTO SP_BIND messages going past.
        !            10:  */
        !            11: #include <sys/kernel.h>
        !            12: #include <sys/dma.h>
        !            13: #include <sys/stream.h>
        !            14: #include <sys/strmlib.h>
        !            15: #include <sys/strproto.h>
        !            16: #include <sys/dp8390.h>
        !            17: #include <sys/ether.h>
        !            18: #include <sys/ints.h>
        !            19: #include <sys/netstuff.h>
        !            20: #include <string.h>
        !            21: 
        !            22: /*
        !            23:  * Allow up to 8 minor devices connected to us - each one receives a copy of
        !            24:  * all incoming traffic, and each can place datagrams.
        !            25:  */
        !            26: #define MAXMINOR       8
        !            27: static queue_t * minortab[MAXMINOR];
        !            28: 
        !            29: #ifdef M68K
        !            30: /*
        !            31:  * The 68070 uses memory mapped I/O exclusively, so treat the 8390 as a
        !            32:  * structure at a fixed location in memory. The header file also defines
        !            33:  * the padding of the structure on the 16-bit 68000 bus.
        !            34:  *
        !            35:  * We usually assign this to a local to put the base into an address
        !            36:  * register, which gets faster code than using the folded immediate address.
        !            37:  */
        !            38: #define DP8390         volatile dp8390 *
        !            39: #define DP8390_BASE    ((DP8390)(0x1FFC00 + PHYSICAL_SEG))
        !            40: #define        DEVICE          _device
        !            41: #define DEVICE_REG      DP8390 DEVICE = DP8390_BASE;
        !            42: 
        !            43: #define input(dev, dp_reg)     (dev)->dp_pg0rd.dp_reg
        !            44: #define input1(dev, dp_reg)    (dev)->dp_pg1rdwr.dp_reg
        !            45: #define output(dev, dp_reg, val)       (dev)->dp_pg0wr.dp_reg = val
        !            46: #define output1(dev, dp_reg, val)      (dev)->dp_pg1rdwr.dp_reg = val
        !            47: #endif
        !            48: 
        !            49: #ifdef IBMPC
        !            50: /*
        !            51:  * The 8086 has special instructions for I/O, but if we treat the base
        !            52:  * address as a NEAR mode pointer (16-bit, current data segment) then cast
        !            53:  * it to a short for the I/O instructions, we'll have the right kind of
        !            54:  * effect.
        !            55:  *
        !            56:  * We DON'T assign the base to a local, since better code is produced by
        !            57:  * folding the constants and loading a new one into DX every I/O.
        !            58:  *
        !            59:  * Note that we emit a JMP $+2 just before the I/O instruction to waste a
        !            60:  * little time, as a NIC CS should not occur more often than 400ns.
        !            61:  */
        !            62: #define DP8390                 dp8390 near *
        !            63: #define DP8390_BASE    ((DP8390)0x300)
        !            64: #define DEVICE         DP8390_BASE
        !            65: #define DEVICE_REG
        !            66: 
        !            67: #define _input(addr)           (_DX=(UWORD)addr, __emit__ (0xEB, 0, 0xEB, 0, 0xEB, 0, 0xEB, 0, 0xEB, 0, 0xEC), _AL)
        !            68: #define _output(addr,val)      (_DX=(UWORD)addr, _AL = val, __emit__ (0xEB, 0, 0xEB, 0, 0xEB, 0, 0xEB, 0, 0xEB, 0, 0xEE))
        !            69: #define input(dev, dp_reg)     _input (&(dev)->dp_pg0rd.dp_reg)
        !            70: #define input1(dev, dp_reg)    _input (&(dev)->dp_pg1rdwr.dp_reg)
        !            71: #define output(dev, dp_reg, val)       _output (&(dev)->dp_pg0wr.dp_reg,(val))
        !            72: #define output1(dev, dp_reg, val)      _output (&(dev)->dp_pg1rdwr.dp_reg,(val))
        !            73: 
        !            74: 
        !            75: #define ETHER_IRQ      2
        !            76: 
        !            77: #endif
        !            78: 
        !            79: 
        !            80: /*
        !            81:  * For scheduling remote DMA operations on the NIC, since there is only one
        !            82:  * channel on the NIC which is shared between remote read and remote write
        !            83:  * operations. Complicated since the TX buffer area is held until the NIC has
        !            84:  * actually completed the transmission, and because RX DMA may be halted in the
        !            85:  * middle of a packet due to STREAMS buffer depletion.
        !            86:  *
        !            87:  * Made a structure in the anticipation of one day having multiple interfaces.
        !            88:  */
        !            89: static struct ethcb {          /* control block for a single interface */
        !            90:        char    tx_state;       /* TX DMA in progress or TX DMA blocked */
        !            91:                                /* cleared when packet send completed */
        !            92:        char    rx_state;       /* RX DMA in progress or waiting for buffers */
        !            93:        char    rx_pending;     /* flag that RX packets for DMA are waiting */
        !            94:        short   rx_length;      /* remaining length of RX packet */
        !            95:        UWORD   rx_base;        /* NIC buffer address of RX DMA */
        !            96:        mblk_t  * rx_start;     /* first mblk in RX packet */
        !            97:        mblk_t  * rx_current;   /* current RX DMA buffer */
        !            98:        short   tx_length;      /* length of packet being transmitted */
        !            99: 
        !           100:        short   tx_count;       /* count packets sent */
        !           101:        short   rx_count;       /* RX activity */
        !           102: 
        !           103:        char    tx_error;       /* packet never ended transmission */
        !           104:        short   tx_lastcount;   /* count at last timer tick */
        !           105:        short   rx_lastcount;   /* count at last timre tick */
        !           106: 
        !           107: /*
        !           108:  * About write packet queueing - since this device effectively functions as a
        !           109:  * kind of multiplexor, flow control needs explicit attention. I use the
        !           110:  * general methods developed for the other multiplexing drivers (like TCP),
        !           111:  * namely the generic scheduler in my STREAMS implementation.
        !           112:  */
        !           113:        /* schedule of driver queues that have output packets queued */
        !           114:        struct schedule tx_sched;
        !           115: 
        !           116:        struct dp8390info dp8390info;
        !           117: 
        !           118:        /* useful addresses that we may wish to keep copies of */
        !           119:        Eth_addr myaddr;                /* our own address */
        !           120: 
        !           121:        /* This flag gets set when the NIC tests OK */
        !           122:        int netok;
        !           123: 
        !           124:        /* error counter extensions */
        !           125:        int errcnt0, errcnt1, errcnt2;
        !           126: } eth;
        !           127: 
        !           128: 
        !           129: #define ETH    LONG_TO_PTR (eth, struct ethcb *)
        !           130: #define QETH   ((struct ethcb *) q->q_ptr)
        !           131: 
        !           132: /*
        !           133:  * Possible states for TX
        !           134:  */
        !           135: #define TX_IDLE                0
        !           136: #define TX_DMA         1
        !           137: #define TX_SEND                2
        !           138: /*
        !           139:  * Possible events for TX
        !           140:  */
        !           141: #define TXE_CRANK      0
        !           142: #define TXE_DMADONE    1
        !           143: #define TXE_SENDDONE   2
        !           144: 
        !           145: /*
        !           146:  * Possible states for RX.
        !           147:  *
        !           148:  * The two states RX_HALT and RX_BLOCKED distinguish whether or not to
        !           149:  * continue RX dma after a TX DMA or not.
        !           150:  */
        !           151: #define RX_IDLE                0
        !           152: #define RX_DMA         1
        !           153: #define RX_HALT                2
        !           154: #define RX_BLOCKED     3
        !           155: /*
        !           156:  * possible events for RX
        !           157:  */
        !           158: #define RXE_CRANK      0
        !           159: #define RXE_GOTMEM     1
        !           160: #define RXE_NOMEM      2
        !           161: #define RXE_DMADONE    3
        !           162: #define RXE_ALLDONE    4
        !           163: 
        !           164: /*
        !           165:  * To aid in detecting when the cable has been unplugged (which causes a
        !           166:  * constant collision state, so the backoff count doesn't get incremented).
        !           167:  */
        !           168: static P_TIMER eth_timer = NIL_TIMER;
        !           169: 
        !           170: /*
        !           171:  * program the address into the address registers.
        !           172:  * Note : assumes that register set 1 has already been chosen!
        !           173:  */
        !           174: static void set_addr (int selpage)
        !           175: {
        !           176:        DEVICE_REG
        !           177: 
        !           178:        if (selpage)
        !           179:                output (DEVICE, dp_cr, CR_PS_P1 | CR_DM_ABORT | CR_STA);
        !           180:        output1 (DEVICE, dp_par0, eth.myaddr.e[0]);
        !           181:        output1 (DEVICE, dp_par1, eth.myaddr.e[1]);
        !           182:        output1 (DEVICE, dp_par2, eth.myaddr.e[2]);
        !           183:        output1 (DEVICE, dp_par3, eth.myaddr.e[3]);
        !           184:        output1 (DEVICE, dp_par4, eth.myaddr.e[4]);
        !           185:        output1 (DEVICE, dp_par5, eth.myaddr.e[5]);
        !           186:        if (input1 (DEVICE, dp_par0) != eth.myaddr.e[0] ||
        !           187:            input1 (DEVICE, dp_par1) != eth.myaddr.e[1] ||
        !           188:            input1 (DEVICE, dp_par2) != eth.myaddr.e[2] ||
        !           189:            input1 (DEVICE, dp_par3) != eth.myaddr.e[3] ||
        !           190:            input1 (DEVICE, dp_par4) != eth.myaddr.e[4] ||
        !           191:            input1 (DEVICE, dp_par5) != eth.myaddr.e[5] )
        !           192:                STREAMS_DEBUG ("Device not accepting physical address!\n");
        !           193:        if (selpage)
        !           194:                output (DEVICE, dp_cr, CR_PS_P0 | CR_DM_ABORT | CR_STA);
        !           195: }
        !           196: 
        !           197: /*
        !           198:  * Since the DMA channel to the DP8390 is word-wide, I have taken special care
        !           199:  * to deal with odd-length subunits of packets, or packets that are not word
        !           200:  * aligned with pullupmsg(). It's not fast, but such ugly messages should not
        !           201:  * occur often.
        !           202:  */
        !           203: 
        !           204: extern void streams_levels (void);
        !           205: 
        !           206: static void eth_timer_func (ULONG eth)
        !           207: {
        !           208:        char buf [4];
        !           209:        TIMER_SET (eth_timer, 1, eth_timer_func, eth);
        !           210: 
        !           211:        if (ETH->tx_lastcount != ETH->tx_count)
        !           212:                ETH->tx_lastcount = ETH->tx_count;
        !           213:        else if (ETH->tx_state != TX_IDLE &&
        !           214:                 ETH->tx_error == 0) {/* packet send taking too long */
        !           215:                mblk_t * temp;
        !           216: 
        !           217:                ETH->tx_error = 1;
        !           218: 
        !           219:                /* dispose of all the packets queued here */
        !           220:                while (ETH->tx_sched.s_head != NULL) {
        !           221:                        temp = getq (ETH->tx_sched.s_head);
        !           222:                        freemsg (temp);
        !           223:                        muxrobin (& ETH->tx_sched);
        !           224:                }
        !           225: #if 1
        !           226:                STREAMS_DEBUG ("Stuck in TX ");
        !           227: #endif
        !           228:        }
        !           229:        if (ETH->rx_lastcount != ETH->rx_count)
        !           230:                ETH->rx_lastcount = ETH->rx_count;
        !           231:        else if (ETH->rx_state != RX_IDLE &&
        !           232:                 ETH->tx_error == 0) {
        !           233:                ETH->tx_error = 1;
        !           234:                STREAMS_DEBUG ("Stuck in RX ");
        !           235:        }
        !           236: }
        !           237: 
        !           238: 
        !           239: static UWORD dma_base;         /* DMA base address in NIC */
        !           240: static UWORD dma_len;          /* remote DMA length */
        !           241: static void *dma_buf;          /* local buffer address */
        !           242: static int dma_dir;            /* 1 => remote write */
        !           243: 
        !           244: /* start a piece of remote DMA, rounding the transfer size up */
        !           245: void dma_start (void)
        !           246: {
        !           247:        DEVICE_REG
        !           248:        int xfer_len;
        !           249: 
        !           250:        if (dma_dir) {
        !           251:                output(DEVICE, dp_rbcr0, 15);           /* dummy byte count */
        !           252:                output(DEVICE, dp_rbcr1, 0);
        !           253:                output(DEVICE, dp_cr, CR_PS_P0 | CR_DM_RR | CR_STA);
        !           254: //             for (xfer_len = 0; xfer_len < 5; xfer_len ++)
        !           255: //                     ;
        !           256:        }
        !           257: 
        !           258:        output(DEVICE, dp_rsar0, dma_base & 0xff);      /* remote DMA base */
        !           259:        output(DEVICE, dp_rsar1, dma_base >> 8 );
        !           260:        if ((xfer_len = dma_len) & 1)
        !           261:                xfer_len++;
        !           262:        output(DEVICE, dp_rbcr0, xfer_len & 0xff);      /* remote DMA count */
        !           263:        output(DEVICE, dp_rbcr1, xfer_len >> 8 );
        !           264: 
        !           265:        output(DEVICE, dp_cr, dma_dir ?
        !           266:                CR_PS_P0 | CR_DM_RW | CR_STA :
        !           267:                CR_PS_P0 | CR_DM_RR | CR_STA);
        !           268: 
        !           269:        dma_setup (DMA_0, (char *)dma_buf, xfer_len >> 1,
        !           270:                        dma_dir ? DMAF_TODEV|DMAF_WORDWIDE|DMAF_BURST :
        !           271:                                DMAF_TOMEM|DMAF_WORDWIDE|DMAF_BURST);
        !           272: }
        !           273: 
        !           274: /*
        !           275:  * During test, we want to poll for DMA completion. Also used when reading
        !           276:  * NIC packet header, since it's so short.
        !           277:  */
        !           278: void wait_for_dma (void)
        !           279: {
        !           280:        DEVICE_REG
        !           281: 
        !           282:        for (;;) {
        !           283:                while ((input (DEVICE, dp_cr) & CR_DM_ABORT) == 0)
        !           284:                        ;
        !           285:                if (dma_done (DMA_0) == -1)
        !           286:                        break;
        !           287:                dma_reset (DMA_0);
        !           288:                dma_start ();           /* restart */
        !           289:        }
        !           290: }
        !           291: 
        !           292: /*
        !           293:  * retrieve NIC memory from "base" to "buffer"
        !           294:  */
        !           295: static INLINE void get_mem (int base, int len, void * buffer)
        !           296: {
        !           297:        dma_base = base;
        !           298:        dma_len = len;
        !           299:        dma_buf = buffer;
        !           300:        dma_dir = 0;
        !           301:        dma_start ();
        !           302: }
        !           303: static INLINE void set_mem (int base, int len, unsigned char * buf)
        !           304: {
        !           305:        dma_base = base;
        !           306:        dma_len = len;
        !           307:        dma_buf = buf;
        !           308:        dma_dir = 1;
        !           309:        dma_start ();
        !           310: }
        !           311: 
        !           312: /* send streams message block "mp" to NIC */
        !           313: static void send_block (queue_t * q, mblk_t * mp)
        !           314: {
        !           315:        /*
        !           316:         * identify the losers, and fix them with pullupmsg(), which both
        !           317:         * concatenates (fixing length) and aligns at the same time.
        !           318:         */
        !           319:        if ((mp->b_cont != NULL && ((mp->b_wptr - mp->b_rptr) & 1) != 0) ||
        !           320:                        ((ULONG)mp->b_rptr & 1) != 0)
        !           321:                pullupmsg (mp, -1);
        !           322: 
        !           323:        dma_base = (QETH->dp8390info.dpi_tbuf << 8) + QETH->tx_length;
        !           324:        dma_len = mp->b_wptr - mp->b_rptr;
        !           325:        dma_buf = mp->b_rptr;
        !           326:        dma_dir = 1;
        !           327: 
        !           328:        dma_start ();
        !           329: }
        !           330: 
        !           331: /* initiate transmission of the buffered packet, with length "len". */
        !           332: static void send_pkt (struct ethcb * eth)
        !           333: {
        !           334:        DEVICE_REG
        !           335: 
        !           336:        if (eth->tx_length < 64)        /* Ethernet magic */
        !           337:                eth->tx_length = 64;
        !           338:        output (DEVICE, dp_tbcr0, eth->tx_length & 0xff);
        !           339:        output (DEVICE, dp_tbcr1, eth->tx_length >> 8);
        !           340:        output (DEVICE, dp_tpsr, eth->dp8390info.dpi_tbuf);
        !           341: 
        !           342:        output (DEVICE, dp_cr, CR_PS_P0|CR_DM_ABORT|CR_TXP|CR_STA);
        !           343: }
        !           344: 
        !           345: static void rx_next (struct ethcb * eth, int event);
        !           346: static void tx_next (struct ethcb * eth, int event)
        !           347: {
        !           348: again:
        !           349:        SCREEN (26) = 'T';
        !           350:        SCREEN (28) = eth->tx_state + '0';
        !           351:        SCREEN (30) = event + '0';
        !           352:        switch (eth->tx_state) {
        !           353:        case TX_IDLE:
        !           354:                if (event == TXE_CRANK) {
        !           355:                        if (eth->tx_sched.s_head != NULL &&
        !           356:                            eth->rx_state != RX_DMA) {
        !           357:                                eth->tx_count ++;
        !           358:                                eth->tx_state = TX_DMA;
        !           359:                                eth->tx_length = 0;
        !           360:                                send_block (eth->tx_sched.s_head,
        !           361:                                            eth->tx_sched.s_head->q_first);
        !           362:                        }
        !           363:                        return;
        !           364:                }
        !           365:                break;
        !           366:        case TX_DMA:
        !           367:                if (event == TXE_DMADONE) {
        !           368:                        mblk_t * temp = getq (eth->tx_sched.s_head),
        !           369:                               * msg = temp->b_cont;
        !           370: 
        !           371:                        eth->tx_length += dma_len;
        !           372:                        freeb (temp);
        !           373:                        if (msg != NULL) {
        !           374:                                putbq (eth->tx_sched.s_head, msg);
        !           375:                                send_block (eth->tx_sched.s_head, msg);
        !           376:                        } else {
        !           377:                                send_pkt (eth);         /* spit it out */
        !           378:                                muxrobin (& eth->tx_sched);
        !           379:                                eth->tx_state = TX_SEND;
        !           380: 
        !           381:                                rx_next (eth, RXE_CRANK);
        !           382:                        }
        !           383:                        return;
        !           384:                }
        !           385:                if (event == TXE_CRANK)
        !           386:                        return;
        !           387:                break;
        !           388:        case TX_SEND:
        !           389:                if (event == TXE_SENDDONE) {
        !           390:                        eth->tx_error = 0;
        !           391:                        eth->tx_state = TX_IDLE;
        !           392:                        rx_next (eth, RXE_CRANK);
        !           393:                        event = TXE_CRANK;
        !           394:                        goto again;
        !           395:                }
        !           396:                if (event == TXE_CRANK)
        !           397:                        return;
        !           398:                break;
        !           399:        }
        !           400:        STREAMS_DEBUG ("Bad TX state/event ");
        !           401: }
        !           402: 
        !           403: static void rx_got_mem (long eth)
        !           404: {
        !           405:        short s = SPL7 ();
        !           406:        rx_next (LONG_TO_PTR (eth, struct ethcb *), RXE_GOTMEM);
        !           407:        SPLX (s);
        !           408: }
        !           409: static void rx_next (struct ethcb * eth, int event)
        !           410: {
        !           411:        mblk_t * mp;
        !           412:        int len;
        !           413: 
        !           414: again:
        !           415:        SCREEN (26) = 'R';
        !           416:        SCREEN (28) = eth->rx_state + '0';
        !           417:        SCREEN (30) = event + '0';
        !           418:        switch (eth->rx_state) {
        !           419:        case RX_IDLE:
        !           420:                if (event == RXE_CRANK) {
        !           421:                        if (eth->rx_pending && eth->tx_state != TX_DMA) {
        !           422:                                /* busy wait on the header part */
        !           423:                                struct rcvdheader nic;
        !           424: 
        !           425:                                get_mem (eth->dp8390info.dpi_next << 8, 4, & nic);
        !           426:                                wait_for_dma ();
        !           427:                                eth->dp8390info.dpi_next = nic.rp_next;
        !           428:                                /*
        !           429:                                 * count in packet header includes FCS, which we
        !           430:                                 * are not really interested in.
        !           431:                                 */
        !           432:                                eth->rx_length = (nic.rp_rbch << 8) + nic.rp_rbcl - 4;
        !           433:                                eth->rx_base = dma_base + 4;
        !           434:                                if ((input (DEVICE, dp_isr) & ISR_RDC) == 0)
        !           435:                                        STREAMS_DEBUG ("IMPOSSIBLE 1");
        !           436:                                output (DEVICE, dp_isr, ISR_RDC);
        !           437: 
        !           438:                                eth->rx_count ++;
        !           439:                                eth->rx_state = RX_DMA;
        !           440:                                event = RXE_DMADONE;
        !           441: 
        !           442:                                if (input (DEVICE, dp_isr) & ISR_RDC)
        !           443:                                        STREAMS_DEBUG ("IMPOSSIBLE 2");
        !           444:                                goto again;
        !           445:                        }
        !           446:                        return;
        !           447:                }
        !           448:                break;
        !           449:        case RX_DMA:
        !           450:                len = eth->rx_length;
        !           451:                if (len > 768) {
        !           452:                        if (len > 1024)
        !           453:                                len = 1024;
        !           454:                } else if (len > 192) {
        !           455:                        if (len > 256)
        !           456:                                len = 256;
        !           457:                } else if (len > 64)
        !           458:                        len = 64;
        !           459:                if (event == RXE_DMADONE) {
        !           460:                        if (len == 0) {
        !           461:                                event = RXE_ALLDONE;
        !           462:                                goto again;
        !           463:                        }
        !           464:                        if ((mp = allocb (len, BPRI_MED)) == NULL) {
        !           465:                                while (len < 1024) {
        !           466:                                        if ((mp = allocb (len, BPRI_LO)) != NULL)
        !           467:                                                break;
        !           468:                                        len *= 2;
        !           469:                                }
        !           470:                                if (len >= 1024) {
        !           471:                                        event = RXE_NOMEM;
        !           472:                                        goto again;
        !           473:                                }
        !           474:                                if (len > eth->rx_length)
        !           475:                                        len = eth->rx_length;
        !           476:                        }
        !           477: 
        !           478:                        eth->rx_count ++;
        !           479:                        mp->b_wptr += len;
        !           480:                        if (eth->rx_start == NULL)
        !           481:                                eth->rx_start = mp;             /* first buffer */
        !           482:                        else
        !           483:                                eth->rx_current->b_cont = mp;   /* link buffers */
        !           484:                        eth->rx_current = mp;
        !           485:                        get_mem (eth->rx_base, len, mp->b_rptr);
        !           486:                        /* take care to wrap the rx_base! */
        !           487:                        if (((eth->rx_base += len) >> 8) >= eth->dp8390info.dpi_pstop)
        !           488:                                eth->rx_base -= (eth->dp8390info.dpi_pstop - eth->dp8390info.dpi_pstart) << 8;
        !           489:                        eth->rx_length -= len;
        !           490:                        return;
        !           491:                }
        !           492:                if (event == RXE_NOMEM) {
        !           493:                        eth->rx_state = RX_HALT;
        !           494:                        bufcall (len, BPRI_MED, rx_got_mem, (long) eth);
        !           495:                        tx_next (eth, TXE_CRANK);
        !           496:                        event = RXE_CRANK;
        !           497:                        goto again;
        !           498:                }
        !           499:                if (event == RXE_ALLDONE) {
        !           500:                        /*
        !           501:                         * finished a piece of receive DMA - advance buffer
        !           502:                         * queue endpoint over just received packet. In
        !           503:                         * addition, we should check to see if there are any
        !           504:                         * more buffered receive packets.
        !           505:                         */
        !           506:                        output (DEVICE, dp_bnry, eth->dp8390info.dpi_next == eth->dp8390info.dpi_pstart ?
        !           507:                                                eth->dp8390info.dpi_pstop - 1 :
        !           508:                                                eth->dp8390info.dpi_next - 1);
        !           509:                        output (DEVICE, dp_cr, CR_PS_P1 | CR_DM_ABORT | CR_STA);
        !           510:                        if (input1 (DEVICE, dp_curr) == eth->dp8390info.dpi_next)
        !           511:                                eth->rx_pending = 0;
        !           512:                        output (DEVICE, dp_cr, CR_PS_P0 | CR_DM_ABORT | CR_STA);
        !           513:                        if (eth->rx_start != NULL) {
        !           514:                                /* send a copy of every message upwards */
        !           515:                                for (len = 0; len < MAXMINOR ; len ++)
        !           516:                                        if (minortab [len] &&
        !           517:                                            (mp = dupmsg (eth->rx_start)) != NULL)
        !           518:                                                putq (minortab [len], mp);
        !           519:                                SCREEN (22) ++;
        !           520:                                freemsg (eth->rx_start);
        !           521:                                eth->rx_start = eth->rx_current = NULL;
        !           522:                        }
        !           523:                        eth->rx_state = RX_IDLE;
        !           524:                        tx_next (eth, TXE_CRANK);
        !           525:                        event = RXE_CRANK;
        !           526:                        goto again;
        !           527:                }
        !           528:                return;
        !           529:        case RX_HALT:
        !           530:                if (event == RXE_GOTMEM) {
        !           531:                        if (eth->tx_state == TX_DMA) {
        !           532:                                eth->rx_state = RX_BLOCKED;
        !           533:                                return;
        !           534:                        }
        !           535:                        eth->rx_state = RX_DMA;
        !           536:                        event = RXE_DMADONE;
        !           537:                        goto again;
        !           538:                }
        !           539:                if (event == RXE_CRANK)
        !           540:                        return;
        !           541:                break;
        !           542:        case RX_BLOCKED:
        !           543:                if (event == RXE_CRANK && eth->tx_state != TX_DMA) {
        !           544:                        eth->rx_state = RX_DMA;
        !           545:                        event = RXE_DMADONE;
        !           546:                        goto again;
        !           547:                }
        !           548:                break;
        !           549:        default:
        !           550:                STREAMS_DEBUG ("Impossible state");
        !           551:                return;
        !           552:        }
        !           553:        STREAMS_DEBUG ("Bad RX state/event combo ");
        !           554: }
        !           555: 
        !           556: 
        !           557: /* Referenced in "system.asm", here is the interrupt service routine */
        !           558: HW_INT_FUNC (void) etherint (void)
        !           559: {
        !           560:        DEVICE_REG
        !           561:        UBYTE   status = input (DEVICE, dp_isr);
        !           562: static UBYTE   reentry;
        !           563: 
        !           564:        if (reentry ++ != 0) {
        !           565:                STREAMS_DEBUG ("Reentry ");
        !           566:                reentry --;
        !           567:                return;
        !           568:        }
        !           569: 
        !           570:        /*
        !           571:         * GCC - specific kernel hack, save all used registers on entry to
        !           572:         * this function. We don't use INTERRUPT for the PC version since
        !           573:         * hardware IRQ handlers need assembly-language wrappers to give
        !           574:         * us enough stack to use STREAMS library routines.
        !           575:         */
        !           576:        GNU_INTERRUPT;
        !           577: 
        !           578: rescan:
        !           579:        /* reset the flag bits that were indicated for us */
        !           580:        output (DEVICE, dp_isr, status);
        !           581: 
        !           582:        if (eth.netok == 0) {                   /* device not active! */
        !           583:                reentry --;
        !           584:                return;
        !           585:        }
        !           586:        if ((status & ISR_PRX) != 0) {
        !           587:                eth.rx_pending = 1;
        !           588:                rx_next (& eth, RXE_CRANK);
        !           589:        }
        !           590:        SCREEN (16) ++;
        !           591:        if ((status & ISR_PTX) != 0) {
        !           592:                if ((input (DEVICE, dp_tsr) & ~(TSR_COL | TSR_CRS | TSR_PTX | TSR_DFR))
        !           593:                    != 0)
        !           594:                        STREAMS_DEBUG ("Packet TX error\n");
        !           595:                SCREEN (18) ++;
        !           596:                tx_next (& eth, TXE_SENDDONE);
        !           597:        }
        !           598:        /*
        !           599:         * The ISR_RDC bit is gated with CR_DM_ABORT since the fix for the
        !           600:         * NIC remote write problem causes spurious RDCs, which are not
        !           601:         * normally detected unless the TX DMA is delayed by arrive DMA to
        !           602:         * the NIC local memory.
        !           603:         */
        !           604:        if ((status & ISR_RDC) != 0 &&
        !           605:            (input (DEVICE, dp_cr) & CR_DM_ABORT) != 0)
        !           606:                if (dma_done (DMA_0) != -1) {
        !           607:                        dma_reset (DMA_0);
        !           608:                        dma_start ();
        !           609:                } else if (dma_dir)
        !           610:                        tx_next (& eth, TXE_DMADONE);
        !           611:                else
        !           612:                        rx_next (& eth, RXE_DMADONE);
        !           613: 
        !           614:        if ((status & (ISR_RXE | ISR_TXE | ISR_OVW | ISR_CNT)) != 0) {
        !           615:                if (status & ISR_RXE)
        !           616:                        STREAMS_DEBUG ("Receive error\n");
        !           617:                if (status & ISR_TXE) {
        !           618:                        /*
        !           619:                         * This can happen due to excessive collisions if
        !           620:                         * there is an open cable end or due to a FIFO
        !           621:                         * underrun (which indicates a board problem).
        !           622:                         */
        !           623:                        if ((input (DEVICE, dp_tsr) & (TSR_FU)) != 0)
        !           624:                                STREAMS_DEBUG ("TX FIFO underrun");
        !           625:                        tx_next (& eth, TXE_SENDDONE);
        !           626:                }
        !           627:                if (status & ISR_OVW)
        !           628:                        STREAMS_DEBUG ("Buffer overflow\n");
        !           629:                if (status & ISR_CNT) {
        !           630:                        /* reset the error counters by reading them */
        !           631:                        eth.errcnt0 += input (DEVICE, dp_cntr0);
        !           632:                        eth.errcnt1 += input (DEVICE, dp_cntr1);
        !           633:                        eth.errcnt2 += input (DEVICE, dp_cntr2);
        !           634:                }
        !           635:        }
        !           636:        if ((status = input (DEVICE, dp_isr)) != 0)
        !           637:                goto rescan;
        !           638: 
        !           639:        reentry --;
        !           640: }
        !           641: 
        !           642: /*
        !           643:  * Routines for reading from/writing to the NIC memory.
        !           644:  */
        !           645: 
        !           646: /* queue a packet for transmission */
        !           647: static void queue_packet (queue_t *q, mblk_t *mp)
        !           648: {
        !           649:        if (QETH->tx_error != 0)
        !           650:                return;
        !           651: 
        !           652:        short s = SPL7 ();
        !           653:        /*
        !           654:         * We queue the message "normally" for STREAMS so that flow control
        !           655:         * operates as one would expect. Then we place the streams queue onto
        !           656:         * our own "scheduling" list for processing by the NIC interrupt.
        !           657:         */
        !           658:        putq (q, mp);
        !           659:        qschedule (q, & QETH->tx_sched);
        !           660: 
        !           661:        /*
        !           662:         * If there is no I/O presently being executed, we must start
        !           663:         * the ball rolling.
        !           664:         */
        !           665:        tx_next (QETH, TXE_CRANK);
        !           666:        SPLX (s);
        !           667: }
        !           668: 
        !           669: /*
        !           670:  * Set up the DP8390. This initialisation procedure comes fairly directly
        !           671:  * from the NatSemi manual - much dark magic.
        !           672:  */
        !           673: static void chipinit (Eth_addr * addr)
        !           674: {
        !           675:        unsigned char * testbuf, * test2;
        !           676:        mblk_t * testmem;
        !           677:        int i, start = -1, len;
        !           678:        DEVICE_REG
        !           679: 
        !           680:        if (input (DEVICE, dp_cr) == 0xFF) {
        !           681:                eth.netok = -1;
        !           682:                return;
        !           683:        }
        !           684: 
        !           685:        /* reset dp8390 */
        !           686:        output(DEVICE, dp_cr, CR_STP|CR_PS_P0|CR_DM_ABORT);
        !           687: 
        !           688: #ifdef M68K
        !           689:        output(DEVICE, dp_dcr,
        !           690:                DCR_LOOP | DCR_WORDWIDE | DCR_BIGENDIAN | DCR_8BYTES);
        !           691: #endif
        !           692: #ifdef IBMPC
        !           693:        set_hw_int (ETHER_IRQ, etherint);
        !           694:        output(DEVICE, dp_dcr, DCR_LOOP | DCR_WORDWIDE | DCR_8BYTES);
        !           695: #endif
        !           696: 
        !           697:        output(DEVICE, dp_rbcr0, 0);
        !           698:        output(DEVICE, dp_rbcr1, 0);
        !           699:        output(DEVICE, dp_rcr, RCR_AB);
        !           700:        output(DEVICE, dp_tcr, TCR_INTERNAL);
        !           701:        output(DEVICE, dp_isr, 0xff);
        !           702:        output(DEVICE, dp_imr, 0);
        !           703: 
        !           704:        /*
        !           705:         * We start the device here (and shut it down later) since the
        !           706:         * Remote DMA doesn't work until we do, and we can't find out how
        !           707:         * much memory we have until we can DMA. (Sigh)
        !           708:         */
        !           709:        output (DEVICE, dp_cr, CR_PS_P0 | CR_DM_ABORT | CR_STA);
        !           710: 
        !           711:        /*
        !           712:         * Since the NIC is in internal loopback mode (TCR_INTERNAL), and
        !           713:         * interrupts are disabled, we can now test the NIC's memory to
        !           714:         * determine how much there is.
        !           715:         */
        !           716:        testmem = getbuf (1024);        /* get temporary space */
        !           717:        testbuf = testmem->b_rptr;
        !           718:        test2 = testbuf + 256;
        !           719: 
        !           720:        for (i = 0 ; i < 256 ; i ++)
        !           721:                testbuf [i] = i;
        !           722:        for (i = 0 ; i < 256 ; i ++) {
        !           723:                testbuf [0] = i;
        !           724:                testbuf [1] = ~i;
        !           725:                set_mem (i << 8, 256, testbuf);
        !           726:                wait_for_dma ();
        !           727:        }
        !           728: 
        !           729:        /* find start of memory */
        !           730:        for (i = 0 ; i < 256 ; i++) {
        !           731:                testbuf [0] = i;
        !           732:                testbuf [1] = ~i;
        !           733:                get_mem (i << 8, 256, test2);
        !           734:                wait_for_dma ();
        !           735:                if (memcmp (test2, testbuf, 256) == 0)
        !           736:                        /* got one - either start or extend a span */
        !           737:                        if (start < 0) {
        !           738:                                start = i;
        !           739:                                len = 1;
        !           740:                        } else
        !           741:                                len++;
        !           742:                else
        !           743:                        if (start >= 0)
        !           744:                                break;
        !           745:        }
        !           746:        if (len < 16) {         /* needs at least 4k to work */
        !           747:                eth.netok = -1;
        !           748:                goto alldone;
        !           749:        }
        !           750: 
        !           751:        eth.dp8390info.dpi_pstart = start + 8;
        !           752:        eth.dp8390info.dpi_pstop = start + len;
        !           753:        eth.dp8390info.dpi_tbuf = start;
        !           754: 
        !           755:        /*
        !           756:         * Since we're at it... if the board has an address PROM, it should
        !           757:         * be at address 0 (where the NIC has trouble locating RAM), so
        !           758:         * unless we're given an address, try finding our location.
        !           759:         */
        !           760:        if (addr != NULL)
        !           761:                eth.myaddr = * addr;
        !           762:        else if (start > 0) {           /* may have PROM, there's no RAM */
        !           763:                get_mem (0, 12, testbuf);
        !           764:                wait_for_dma ();
        !           765: 
        !           766:                /* Fetch the PROM data from every second word */
        !           767:                for (i = 0;i < 6;i++)
        !           768:                        eth.myaddr.e [i] = testbuf [i + i];
        !           769:        }
        !           770: 
        !           771:        STREAMS_DEBUG_LONG (len << 8, 16);
        !           772:        STREAMS_DEBUG ("h bytes of Ethernet memory, Address = $");
        !           773:        for (i = 0 ; i < 6 ; i ++)
        !           774:                STREAMS_DEBUG_LONG (eth.myaddr.e [i], 16);
        !           775:        STREAMS_DEBUG (", IRQ ");
        !           776:        STREAMS_DEBUG_LONG (ETHER_IRQ, 10);
        !           777:        STREAMS_DEBUG ("\r\n");
        !           778: 
        !           779:        output (DEVICE, dp_isr, 0xFF);
        !           780:        output (DEVICE, dp_tpsr, eth.dp8390info.dpi_tbuf);
        !           781:        output (DEVICE, dp_pstart, eth.dp8390info.dpi_pstart);
        !           782:        output (DEVICE, dp_bnry, eth.dp8390info.dpi_pstart);
        !           783:        output (DEVICE, dp_pstop, eth.dp8390info.dpi_pstop);
        !           784: 
        !           785:        output (DEVICE, dp_cr, CR_PS_P1|CR_DM_ABORT|CR_STP);
        !           786:        output1 (DEVICE, dp_curr, eth.dp8390info.dpi_pstart + 1);
        !           787:        eth.dp8390info.dpi_next = eth.dp8390info.dpi_pstart + 1;
        !           788: 
        !           789:        set_addr (0);
        !           790: 
        !           791:        output (DEVICE, dp_cr, CR_PS_P0|CR_DM_ABORT|CR_STA);
        !           792:        output (DEVICE, dp_tcr, 0);
        !           793:        output (DEVICE, dp_imr, 0x7F);  /* all interrupts */
        !           794: 
        !           795:        if ((eth_timer = TIMER_ALLOC ()) != NIL_TIMER)
        !           796:                TIMER_SET (eth_timer, 1, eth_timer_func, (long) & eth);
        !           797:        eth.netok ++;
        !           798: 
        !           799: alldone:
        !           800:        freemsg (testmem);
        !           801: }
        !           802: 
        !           803: /*
        !           804:  * Shut down the E'net chip. Whenever I get around to it, this will send
        !           805:  * the ARP-style packet that indicates this station is dead'n'gone. This
        !           806:  * will use the poll-type DMA access shown in the open.
        !           807:  */
        !           808: static void chipoff (void)
        !           809: {
        !           810:        DEVICE_REG
        !           811: 
        !           812:        if (eth.netok < 0)
        !           813:                return;
        !           814: #ifdef IBMPC
        !           815:        set_hw_int (ETHER_IRQ, NULL);
        !           816: #endif /* IBMPC */
        !           817:        output (DEVICE, dp_imr, 0);
        !           818:        output (DEVICE, dp_cr, CR_PS_P0 | CR_DM_ABORT | CR_STP);
        !           819: }
        !           820: 
        !           821: /*
        !           822:  * stream open routine for the driver
        !           823:  */
        !           824: static int ethopen (queue_t * q, dev_t dev, int /* flag */, int sflag)
        !           825: {
        !           826:        /* refuse to do anything unless everything checked out OK at boot */
        !           827:        W (q)->q_ptr = q->q_ptr = & eth;
        !           828:        if (QETH->netok < 0)
        !           829:                return OPENFAIL;
        !           830:        if (QETH->netok == 0) {
        !           831:                chipinit (NULL);
        !           832:                if (QETH->netok < 0)
        !           833:                        return OPENFAIL;
        !           834: 
        !           835:        } else
        !           836:                QETH->netok ++;
        !           837:        /* allow regular or clone device open */
        !           838:        if (sflag == CLONEOPEN) {
        !           839:                for (dev = 0;dev < MAXMINOR;dev++)
        !           840:                        if (minortab[dev] == NULL)
        !           841:                                break;
        !           842:        } else
        !           843:                dev = minor(dev);
        !           844:        if (dev < 0 || dev >= MAXMINOR)
        !           845:                return OPENFAIL;
        !           846: 
        !           847:        minortab[dev] = q;
        !           848:        return dev;
        !           849: }
        !           850: 
        !           851: /* ethernet write put procedure */
        !           852: static void ethwput (queue_t * q, mblk_t * mp)
        !           853: {
        !           854:        Framehdr * frame;
        !           855:        struct sp_bind * spbind;
        !           856:        Eth_addr * addr;
        !           857: 
        !           858:        switch (mp->b_datap->db_type) {
        !           859:        case M_PROTO:
        !           860:                spbind = (struct sp_bind *)mp->b_rptr;
        !           861:                addr = (Eth_addr *)(spbind + 1);
        !           862:                if (spbind->protoid == SP_BIND) {
        !           863:                        if (spbind->family != AF_ENET) {
        !           864:                                freemsg (mp);
        !           865:                                return;
        !           866:                        }
        !           867:                        if (mp->b_wptr > (unsigned char *) addr) {
        !           868:                                QETH->myaddr = * addr ++;
        !           869:                                set_addr (1);
        !           870:                        }
        !           871:                        freemsg (mp);
        !           872:                        mp = allocb (sizeof (struct sp_bind) + sizeof (Eth_addr),
        !           873:                                     BPRI_MED);
        !           874:                        if (mp != NULL) {
        !           875:                                spbind = (struct sp_bind *)mp->b_rptr;
        !           876:                                addr = (Eth_addr *)(spbind + 1);
        !           877:                                spbind->protoid = SP_BIND;
        !           878:                                spbind->family = AF_ENET;
        !           879:                                spbind->connect = TPS_BOUND;
        !           880:                                * addr ++ = QETH->myaddr;
        !           881:                                mp->b_wptr = (unsigned char *) addr;
        !           882:                                mp->b_datap->db_type = M_PROTO;
        !           883:                                qreply (q, mp);
        !           884:                        }
        !           885:                        return;
        !           886:                }
        !           887:        default:
        !           888: nogood:
        !           889:                freemsg (mp);
        !           890:                break;
        !           891:        case M_DATA:
        !           892:                frame = (Framehdr *)mp->b_rptr;
        !           893:                if ((unsigned char *)(frame + 1) > mp->b_wptr)
        !           894:                        goto nogood;
        !           895:                SCREEN (20) ++;
        !           896:                frame->f_srcaddr = QETH->myaddr;
        !           897:                queue_packet (q, mp);
        !           898:                break;
        !           899:        case M_FLUSH:
        !           900:                /* canonical flush processing */
        !           901:                if ((* mp->b_rptr & FLUSHW) != 0)
        !           902:                        flushq (q, FLUSHDATA);
        !           903:                if ((* mp->b_rptr & FLUSHR) != 0) {
        !           904:                        flushq (RD(q), FLUSHDATA);
        !           905:                        * mp->b_rptr &= ~FLUSHW;
        !           906:                        qreply (q, mp);
        !           907:                } else
        !           908:                        freemsg (mp);
        !           909:                break;
        !           910:        case M_IOCTL:
        !           911:                mp->b_datap->db_type = M_IOCNAK;
        !           912:                qreply (q, mp);
        !           913:                break;
        !           914:        }
        !           915: }
        !           916: 
        !           917: /* read service routine */
        !           918: static void ethrsrv (queue_t * q)
        !           919: {
        !           920:        mblk_t * mp;
        !           921: 
        !           922:        while ((mp = getq (q)) != NULL)
        !           923:                if (canput (q->q_next))
        !           924:                        putnext (q, mp);
        !           925:                else {
        !           926:                        putbq (q, mp);
        !           927:                        break;
        !           928:                }
        !           929:        SCREEN (24) ++;
        !           930: }
        !           931: 
        !           932: /* close procedure - called on last close of stream */
        !           933: static int ethclose(queue_t *q)
        !           934: {
        !           935:        int i;
        !           936: 
        !           937:        if (QETH->netok == 1)
        !           938:                chipoff ();
        !           939:        QETH->netok --;
        !           940:        for (i = 0;i < MAXMINOR;i++)
        !           941:                if (minortab[i] == q)
        !           942:                        break;
        !           943:        minortab[i] = NULL;
        !           944:        return(0);
        !           945: }
        !           946: 
        !           947: static struct module_info ethrinfo = {
        !           948:        0, "ether", 0, 1024, 1024, 0
        !           949: };
        !           950: static struct qinit ethread = {
        !           951:        NULLFUNC, ethrsrv, ethopen, ethclose, NULLFUNC,
        !           952:        &ethrinfo, NULLSTAT
        !           953: }, ethwrite = {
        !           954:        ethwput, NULLFUNC, NULLFUNC, NULLFUNC, NULLFUNC,
        !           955:        &ethrinfo, NULLSTAT
        !           956: };
        !           957: struct streamtab ethinfo = {
        !           958:        &ethread, &ethwrite, NULLINIT, NULLINIT
        !           959: };

unix.superglobalmegacorp.com

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