/* * SMC EtherEZ (SMC91cXX chip) PCMCIA card support. */ #include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "io.h"#include "../port/error.h"#include "../port/netif.h"#include "etherif.h" enum { IoSize = 0x10, /* port pool size */ TxTimeout = 150,}; enum { /* PCMCIA related */ TupleFunce = 0x22, TfNodeId = 0x04,}; enum { /* bank 0 registers */ Tcr = 0x0000, /* transmit control */ Eph = 0x0002, /* ethernet protocol handler */ Rcr = 0x0004, /* receiver control */ Counter = 0x0006, /* statistics counter */ MemInfo = 0x0008, MemCfg = 0x000A,}; enum { /* bank 1 registers */ Config = 0x0000, BaseAddr = 0x0002, Addr0 = 0x0004, /* ethernet address */ Addr1 = 0x0006, Addr2 = 0x0008, General = 0x000A, Control = 0x000C,}; enum { /* bank 2 registers */ MmuCmd = 0x0000, PktNo = 0x0002, AllocRes = 0x0003, FifoPorts = 0x0004, Pointer = 0x0006, Data1 = 0x0008, Interrupt = 0x000C, IntrMask = 0x000D,}; enum { /* bank 3 registers */ Mcast0 = 0x0000, Mcast2 = 0x0002, Mcast4 = 0x0004, Mcast6 = 0x0006, Revision = 0x000A,}; enum { BankSelect = 0x000E /* bank select register */}; enum { BsrMask = 0xFF00, /* mask for chip identification */ BsrId = 0x3300,}; enum { /* Tcr values */ TcrClear = 0x0000, TcrEnable = 0x0001, /* enable transmit */ TcrLoop = 0x0002, /* enable internal analogue loopback */ TcrForceCol = 0x0004, /* force collision on next tx */ TcrPadEn = 0x0080, /* pad short packets to 64 bytes */ TcrNoCrc = 0x0100, /* do not append CRC */ TcrMonCns = 0x0400, /* monitor carrier status */ TcrFduplx = 0x0800, TcrStpSqet = 0x1000, TcrEphLoop = 0x2000, TcrNormal = TcrEnable,}; enum { /* Eph values */ EphTxOk = 0x0001, Eph1Col = 0x0002, /* single collision */ EphMCol = 0x0004, /* multiple collisions */ EphTxMcast = 0x0008, /* multicast transmit */ Eph16Col = 0x0010, /* 16 collisions, tx disabled */ EphSqet = 0x0020, /* SQE test failed, tx disabled */ EphTxBcast = 0x0040, /* broadcast tx */ EphDefr = 0x0080, /* deffered tx */ EphLatCol = 0x0200, /* late collision, tx disabled */ EphLostCarr = 0x0400, /* lost carrier, tx disabled */ EphExcDefr = 0x0800, /* excessive defferals */ EphCntRol = 0x1000, /* ECR counter(s) rolled over */ EphRxOvrn = 0x2000, /* receiver overrun, packets dropped */ EphLinkOk = 0x4000, EphTxUnrn = 0x8000, /* tx underrun */}; enum { /* Rcr values */ RcrClear = 0x0000, RcrPromisc = 0x0002, RcrAllMcast = 0x0004, RcrEnable = 0x0100, RcrStripCrc = 0x0200, RcrSoftReset = 0x8000, RcrNormal = RcrStripCrc | RcrEnable,}; enum { /* Counter value masks */ CntColMask = 0x000F, /* collisions */ CntMColMask = 0x00F0, /* multiple collisions */ CntDtxMask = 0x0F00, /* deferred transmits */ CntExDtxMask = 0xF000, /* excessively deferred transmits */ CntColShr = 1, CntMColShr = 4, CntDtxShr = 8,}; enum { /* MemInfo value masks */ MirTotalMask = 0x00FF, MirFreeMask = 0xFF00,}; enum { /* Config values */ CfgIrqSel0 = 0x0002, CfgIrqSel1 = 0x0004, CfgDisLink = 0x0040, /* disable 10BaseT link test */ Cfg16Bit = 0x0080, CfgAuiSelect = 0x0100, CfgSetSqlch = 0x0200, CfgFullStep = 0x0400, CfgNoWait = 0x1000, CfgMiiSelect = 0x8000,}; enum { /* Control values */ CtlStore = 0x0001, /* store to EEPROM */ CtlReload = 0x0002, /* reload EEPROM into registers */ CtlEeSelect = 0x0004, /* select registers for reload/store */ CtlTeEnable = 0x0020, /* tx error detection via eph irq */ CtlCrEnable = 0x0040, /* counter rollover via eph irq */ CtlLeEnable = 0x0080, /* link error detection via eph irq*/ CtlAutoRls = 0x0800, /* auto release mode */ CtlPowerDn = 0x2000,}; enum { /* MmuCmd values */ McBusy = 0x0001, McAlloc = 0x0020, /* | with number of 256 byte packets - 1 */ McReset = 0x0040, McRelease = 0x0080, /* dequeue (but not free) current rx packet */ McFreePkt = 0x00A0, /* dequeue and free current rx packet */ McEnqueue = 0x00C0, /* enqueue the packet for tx */ McTxReset = 0x00E0, /* reset transmit queues */}; enum { /* AllocRes values */ ArFailed = 0x80,}; enum { /* FifoPorts values */ FpTxEmpty = 0x0080, FpRxEmpty = 0x8000, FpTxMask = 0x007F, FpRxMask = 0x7F00,}; enum { /* Pointer values */ PtrRead = 0x2000, PtrAutoInc = 0x4000, PtrRcv = 0x8000,}; enum { /* Interrupt values */ IntRcv = 0x0001, IntTxError = 0x0002, IntTxEmpty = 0x0004, IntAlloc = 0x0008, IntRxOvrn = 0x0010, IntEph = 0x0020,}; enum { /* transmit status bits */ TsSuccess = 0x0001, Ts16Col = 0x00A0, TsLatCol = 0x0200, TsLostCar = 0x0400,}; enum { /* receive status bits */ RsMcast = 0x0001, RsTooShort = 0x0400, RsTooLong = 0x0800, RsOddFrame = 0x1000, RsBadCrc = 0x2000, RsAlgnErr = 0x8000, RsError = RsAlgnErr | RsBadCrc | RsTooLong | RsTooShort,}; enum { RxLenMask = 0x07FF, /* significant rx len bits */ HdrSize = 6, /* packet header length */ PageSize = 256, /* page length */}; typedef struct Smc91xx Smc91xx;struct Smc91xx { Lock; ushort rev; int attached; Block *txbp; ulong txtime; ulong rovrn; ulong lcar; ulong col; ulong scol; ulong mcol; ulong lcol; ulong dfr;}; #define SELECT_BANK(x) outs(port + BankSelect, x) static intreadnodeid(int slot, Ether* ether){ uchar data[Eaddrlen + 1]; int len; len = sizeof(data); if (pcmcistuple(slot, TupleFunce, TfNodeId, data, len) != len) return -1; if (data[0] != Eaddrlen) return -1; memmove(ether->ea, &data[1], Eaddrlen); return 0;} static voidchipreset(Ether* ether){ int port; int i; port = ether->port; /* reset the chip */ SELECT_BANK(0); outs(port + Rcr, RcrSoftReset); delay(1); outs(port + Rcr, RcrClear); outs(port + Tcr, TcrClear); SELECT_BANK(1); outs(port + Control, CtlAutoRls | CtlTeEnable | CtlCrEnable); for(i = 0; i < 6; i++) { outb(port + Addr0 + i, ether->ea[i]); } SELECT_BANK(2); outs(port + MmuCmd, McReset);} static voidchipenable(Ether* ether){ int port; port = ether->port; SELECT_BANK(0); outs(port + Tcr, TcrNormal); outs(port + Rcr, RcrNormal); SELECT_BANK(2); outb(port + IntrMask, IntEph | IntRxOvrn | IntRcv);} static voidattach(Ether *ether){ Smc91xx* ctlr; ctlr = ether->ctlr; ilock(ctlr); if (ctlr->attached) { iunlock(ctlr); return; } chipenable(ether); ctlr->attached = 1; iunlock(ctlr);} static voidtxstart(Ether* ether){ int port; Smc91xx* ctlr; Block* bp; int len, npages; int pno; /* assumes ctlr is locked and bank 2 is selected */ /* leaves bank 2 selected on return */ port = ether->port; ctlr = ether->ctlr; if (ctlr->txbp) { bp = ctlr->txbp; ctlr->txbp = 0; } else { bp = qget(ether->oq); if (bp == 0) return; len = BLEN(bp); npages = (len + HdrSize) / PageSize; outs(port + MmuCmd, McAlloc | npages); } pno = inb(port + AllocRes); if (pno & ArFailed) { outb(port + IntrMask, inb(port + IntrMask) | IntAlloc); ctlr->txbp = bp; ctlr->txtime = MACHP(0)->ticks; return; } outb(port + PktNo, pno); outs(port + Pointer, PtrAutoInc); len = BLEN(bp); outs(port + Data1, 0); outb(port + Data1, (len + HdrSize) & 0xFF); outb(port + Data1, (len + HdrSize) >> 8); outss(port + Data1, bp->rp, len / 2); if ((len & 1) == 0) { outs(port + Data1, 0); } else { outb(port + Data1, bp->rp[len - 1]); outb(port + Data1, 0x20); /* no info what 0x20 means */ } outb(port + IntrMask, inb(port + IntrMask) | IntTxError | IntTxEmpty); outs(port + MmuCmd, McEnqueue); freeb(bp);} static voidreceive(Ether* ether){ int port; Block* bp; int pktno, status, len; /* assumes ctlr is locked and bank 2 is selected */ /* leaves bank 2 selected on return */ port = ether->port; pktno = ins(port + FifoPorts); if (pktno & FpRxEmpty) { return; } outs(port + Pointer, PtrRead | PtrRcv | PtrAutoInc); status = ins(port + Data1); len = ins(port + Data1) & RxLenMask - HdrSize; if (status & RsOddFrame) len++; if ((status & RsError) || (bp = iallocb(len)) == 0) { if (status & RsAlgnErr) ether->frames++; if (status & (RsTooShort | RsTooLong)) ether->buffs++; if (status & RsBadCrc) ether->crcs++; outs(port + MmuCmd, McRelease); return; } /* packet length is padded to word */ inss(port + Data1, bp->rp, len / 2); bp->wp = bp->rp + (len & ~1); if (len & 1) { *bp->wp = inb(port + Data1); bp->wp++; } etheriq(ether, bp, 1); ether->inpackets++; outs(port + MmuCmd, McRelease);} static voidtxerror(Ether* ether){ int port; Smc91xx* ctlr; int save_pkt; int pktno, status; /* assumes ctlr is locked and bank 2 is selected */ /* leaves bank 2 selected on return */ port = ether->port; ctlr = ether->ctlr; save_pkt = inb(port + PktNo); pktno = ins(port + FifoPorts) & FpTxMask; outb(port + PktNo, pktno); outs(port + Pointer, PtrAutoInc | PtrRead); status = ins(port + Data1); if (status & TsLostCar) ctlr->lcar++; if (status & TsLatCol) ctlr->lcol++; if (status & Ts16Col) ctlr->scol++; ether->oerrs++; SELECT_BANK(0); outs(port + Tcr, ins(port + Tcr) | TcrEnable); SELECT_BANK(2); outs(port + MmuCmd, McFreePkt); outb(port + PktNo, save_pkt);} static voideph_irq(Ether* ether){ int port; Smc91xx* ctlr; ushort status; int n; /* assumes ctlr is locked and bank 2 is selected */ /* leaves bank 2 selected on return */ port = ether->port; ctlr = ether->ctlr; SELECT_BANK(0); status = ins(port + Eph); if (status & EphCntRol) { /* read the counter register even if we don't need it */ /* otherwise we will keep getting this interrupt */ n = ins(port + Counter); ctlr->col += (n & CntColMask) >> CntColShr; ctlr->mcol += (n & CntMColMask) >> CntMColShr; ctlr->dfr += (n & CntDtxMask) >> CntDtxShr; } /* if there was a transmit error, Tcr is disabled */ outs(port + Tcr, ins(port + Tcr) | TcrEnable); /* clear a link error interrupt */ SELECT_BANK(1); outs(port + Control, CtlAutoRls); outs(port + Control, CtlAutoRls | CtlTeEnable | CtlCrEnable); SELECT_BANK(2);} static voidtransmit(Ether* ether){ Smc91xx* ctlr; int port, n; ctlr = ether->ctlr; port = ether->port; ilock(ctlr); if (ctlr->txbp) { n = TK2MS(MACHP(0)->ticks - ctlr->txtime);Lines 1–500 of 781. Load more
/* * SMC EtherEZ (SMC91cXX chip) PCMCIA card support. */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "../port/error.h" #include "../port/netif.h" #include "etherif.h" enum { IoSize = 0x10, /* port pool size */ TxTimeout = 150, }; enum { /* PCMCIA related */ TupleFunce = 0x22, TfNodeId = 0x04, }; enum { /* bank 0 registers */ Tcr = 0x0000, /* transmit control */ Eph = 0x0002, /* ethernet protocol handler */ Rcr = 0x0004, /* receiver control */ Counter = 0x0006, /* statistics counter */ MemInfo = 0x0008, MemCfg = 0x000A, }; enum { /* bank 1 registers */ Config = 0x0000, BaseAddr = 0x0002, Addr0 = 0x0004, /* ethernet address */ Addr1 = 0x0006, Addr2 = 0x0008, General = 0x000A, Control = 0x000C, }; enum { /* bank 2 registers */ MmuCmd = 0x0000, PktNo = 0x0002, AllocRes = 0x0003, FifoPorts = 0x0004, Pointer = 0x0006, Data1 = 0x0008, Interrupt = 0x000C, IntrMask = 0x000D, }; enum { /* bank 3 registers */ Mcast0 = 0x0000, Mcast2 = 0x0002, Mcast4 = 0x0004, Mcast6 = 0x0006, Revision = 0x000A, }; enum { BankSelect = 0x000E /* bank select register */ }; enum { BsrMask = 0xFF00, /* mask for chip identification */ BsrId = 0x3300, }; enum { /* Tcr values */ TcrClear = 0x0000, TcrEnable = 0x0001, /* enable transmit */ TcrLoop = 0x0002, /* enable internal analogue loopback */ TcrForceCol = 0x0004, /* force collision on next tx */ TcrPadEn = 0x0080, /* pad short packets to 64 bytes */ TcrNoCrc = 0x0100, /* do not append CRC */ TcrMonCns = 0x0400, /* monitor carrier status */ TcrFduplx = 0x0800, TcrStpSqet = 0x1000, TcrEphLoop = 0x2000, TcrNormal = TcrEnable, }; enum { /* Eph values */ EphTxOk = 0x0001, Eph1Col = 0x0002, /* single collision */ EphMCol = 0x0004, /* multiple collisions */ EphTxMcast = 0x0008, /* multicast transmit */ Eph16Col = 0x0010, /* 16 collisions, tx disabled */ EphSqet = 0x0020, /* SQE test failed, tx disabled */ EphTxBcast = 0x0040, /* broadcast tx */ EphDefr = 0x0080, /* deffered tx */ EphLatCol = 0x0200, /* late collision, tx disabled */ EphLostCarr = 0x0400, /* lost carrier, tx disabled */ EphExcDefr = 0x0800, /* excessive defferals */ EphCntRol = 0x1000, /* ECR counter(s) rolled over */ EphRxOvrn = 0x2000, /* receiver overrun, packets dropped */ EphLinkOk = 0x4000, EphTxUnrn = 0x8000, /* tx underrun */ }; enum { /* Rcr values */ RcrClear = 0x0000, RcrPromisc = 0x0002, RcrAllMcast = 0x0004, RcrEnable = 0x0100, RcrStripCrc = 0x0200, RcrSoftReset = 0x8000, RcrNormal = RcrStripCrc | RcrEnable, }; enum { /* Counter value masks */ CntColMask = 0x000F, /* collisions */ CntMColMask = 0x00F0, /* multiple collisions */ CntDtxMask = 0x0F00, /* deferred transmits */ CntExDtxMask = 0xF000, /* excessively deferred transmits */ CntColShr = 1, CntMColShr = 4, CntDtxShr = 8, }; enum { /* MemInfo value masks */ MirTotalMask = 0x00FF, MirFreeMask = 0xFF00, }; enum { /* Config values */ CfgIrqSel0 = 0x0002, CfgIrqSel1 = 0x0004, CfgDisLink = 0x0040, /* disable 10BaseT link test */ Cfg16Bit = 0x0080, CfgAuiSelect = 0x0100, CfgSetSqlch = 0x0200, CfgFullStep = 0x0400, CfgNoWait = 0x1000, CfgMiiSelect = 0x8000, }; enum { /* Control values */ CtlStore = 0x0001, /* store to EEPROM */ CtlReload = 0x0002, /* reload EEPROM into registers */ CtlEeSelect = 0x0004, /* select registers for reload/store */ CtlTeEnable = 0x0020, /* tx error detection via eph irq */ CtlCrEnable = 0x0040, /* counter rollover via eph irq */ CtlLeEnable = 0x0080, /* link error detection via eph irq*/ CtlAutoRls = 0x0800, /* auto release mode */ CtlPowerDn = 0x2000, }; enum { /* MmuCmd values */ McBusy = 0x0001, McAlloc = 0x0020, /* | with number of 256 byte packets - 1 */ McReset = 0x0040, McRelease = 0x0080, /* dequeue (but not free) current rx packet */ McFreePkt = 0x00A0, /* dequeue and free current rx packet */ McEnqueue = 0x00C0, /* enqueue the packet for tx */ McTxReset = 0x00E0, /* reset transmit queues */ }; enum { /* AllocRes values */ ArFailed = 0x80, }; enum { /* FifoPorts values */ FpTxEmpty = 0x0080, FpRxEmpty = 0x8000, FpTxMask = 0x007F, FpRxMask = 0x7F00, }; enum { /* Pointer values */ PtrRead = 0x2000, PtrAutoInc = 0x4000, PtrRcv = 0x8000, }; enum { /* Interrupt values */ IntRcv = 0x0001, IntTxError = 0x0002, IntTxEmpty = 0x0004, IntAlloc = 0x0008, IntRxOvrn = 0x0010, IntEph = 0x0020, }; enum { /* transmit status bits */ TsSuccess = 0x0001, Ts16Col = 0x00A0, TsLatCol = 0x0200, TsLostCar = 0x0400, }; enum { /* receive status bits */ RsMcast = 0x0001, RsTooShort = 0x0400, RsTooLong = 0x0800, RsOddFrame = 0x1000, RsBadCrc = 0x2000, RsAlgnErr = 0x8000, RsError = RsAlgnErr | RsBadCrc | RsTooLong | RsTooShort, }; enum { RxLenMask = 0x07FF, /* significant rx len bits */ HdrSize = 6, /* packet header length */ PageSize = 256, /* page length */ }; typedef struct Smc91xx Smc91xx; struct Smc91xx { Lock; ushort rev; int attached; Block *txbp; ulong txtime; ulong rovrn; ulong lcar; ulong col; ulong scol; ulong mcol; ulong lcol; ulong dfr; }; #define SELECT_BANK(x) outs(port + BankSelect, x) static int readnodeid(int slot, Ether* ether) { uchar data[Eaddrlen + 1]; int len; len = sizeof(data); if (pcmcistuple(slot, TupleFunce, TfNodeId, data, len) != len) return -1; if (data[0] != Eaddrlen) return -1; memmove(ether->ea, &data[1], Eaddrlen); return 0; } static void chipreset(Ether* ether) { int port; int i; port = ether->port; /* reset the chip */ SELECT_BANK(0); outs(port + Rcr, RcrSoftReset); delay(1); outs(port + Rcr, RcrClear); outs(port + Tcr, TcrClear); SELECT_BANK(1); outs(port + Control, CtlAutoRls | CtlTeEnable | CtlCrEnable); for(i = 0; i < 6; i++) { outb(port + Addr0 + i, ether->ea[i]); } SELECT_BANK(2); outs(port + MmuCmd, McReset); } static void chipenable(Ether* ether) { int port; port = ether->port; SELECT_BANK(0); outs(port + Tcr, TcrNormal); outs(port + Rcr, RcrNormal); SELECT_BANK(2); outb(port + IntrMask, IntEph | IntRxOvrn | IntRcv); } static void attach(Ether *ether) { Smc91xx* ctlr; ctlr = ether->ctlr; ilock(ctlr); if (ctlr->attached) { iunlock(ctlr); return; } chipenable(ether); ctlr->attached = 1; iunlock(ctlr); } static void txstart(Ether* ether) { int port; Smc91xx* ctlr; Block* bp; int len, npages; int pno; /* assumes ctlr is locked and bank 2 is selected */ /* leaves bank 2 selected on return */ port = ether->port; ctlr = ether->ctlr; if (ctlr->txbp) { bp = ctlr->txbp; ctlr->txbp = 0; } else { bp = qget(ether->oq); if (bp == 0) return; len = BLEN(bp); npages = (len + HdrSize) / PageSize; outs(port + MmuCmd, McAlloc | npages); } pno = inb(port + AllocRes); if (pno & ArFailed) { outb(port + IntrMask, inb(port + IntrMask) | IntAlloc); ctlr->txbp = bp; ctlr->txtime = MACHP(0)->ticks; return; } outb(port + PktNo, pno); outs(port + Pointer, PtrAutoInc); len = BLEN(bp); outs(port + Data1, 0); outb(port + Data1, (len + HdrSize) & 0xFF); outb(port + Data1, (len + HdrSize) >> 8); outss(port + Data1, bp->rp, len / 2); if ((len & 1) == 0) { outs(port + Data1, 0); } else { outb(port + Data1, bp->rp[len - 1]); outb(port + Data1, 0x20); /* no info what 0x20 means */ } outb(port + IntrMask, inb(port + IntrMask) | IntTxError | IntTxEmpty); outs(port + MmuCmd, McEnqueue); freeb(bp); } static void receive(Ether* ether) { int port; Block* bp; int pktno, status, len; /* assumes ctlr is locked and bank 2 is selected */ /* leaves bank 2 selected on return */ port = ether->port; pktno = ins(port + FifoPorts); if (pktno & FpRxEmpty) { return; } outs(port + Pointer, PtrRead | PtrRcv | PtrAutoInc); status = ins(port + Data1); len = ins(port + Data1) & RxLenMask - HdrSize; if (status & RsOddFrame) len++; if ((status & RsError) || (bp = iallocb(len)) == 0) { if (status & RsAlgnErr) ether->frames++; if (status & (RsTooShort | RsTooLong)) ether->buffs++; if (status & RsBadCrc) ether->crcs++; outs(port + MmuCmd, McRelease); return; } /* packet length is padded to word */ inss(port + Data1, bp->rp, len / 2); bp->wp = bp->rp + (len & ~1); if (len & 1) { *bp->wp = inb(port + Data1); bp->wp++; } etheriq(ether, bp, 1); ether->inpackets++; outs(port + MmuCmd, McRelease); } static void txerror(Ether* ether) { int port; Smc91xx* ctlr; int save_pkt; int pktno, status; /* assumes ctlr is locked and bank 2 is selected */ /* leaves bank 2 selected on return */ port = ether->port; ctlr = ether->ctlr; save_pkt = inb(port + PktNo); pktno = ins(port + FifoPorts) & FpTxMask; outb(port + PktNo, pktno); outs(port + Pointer, PtrAutoInc | PtrRead); status = ins(port + Data1); if (status & TsLostCar) ctlr->lcar++; if (status & TsLatCol) ctlr->lcol++; if (status & Ts16Col) ctlr->scol++; ether->oerrs++; SELECT_BANK(0); outs(port + Tcr, ins(port + Tcr) | TcrEnable); SELECT_BANK(2); outs(port + MmuCmd, McFreePkt); outb(port + PktNo, save_pkt); } static void eph_irq(Ether* ether) { int port; Smc91xx* ctlr; ushort status; int n; /* assumes ctlr is locked and bank 2 is selected */ /* leaves bank 2 selected on return */ port = ether->port; ctlr = ether->ctlr; SELECT_BANK(0); status = ins(port + Eph); if (status & EphCntRol) { /* read the counter register even if we don't need it */ /* otherwise we will keep getting this interrupt */ n = ins(port + Counter); ctlr->col += (n & CntColMask) >> CntColShr; ctlr->mcol += (n & CntMColMask) >> CntMColShr; ctlr->dfr += (n & CntDtxMask) >> CntDtxShr; } /* if there was a transmit error, Tcr is disabled */ outs(port + Tcr, ins(port + Tcr) | TcrEnable); /* clear a link error interrupt */ SELECT_BANK(1); outs(port + Control, CtlAutoRls); outs(port + Control, CtlAutoRls | CtlTeEnable | CtlCrEnable); SELECT_BANK(2); } static void transmit(Ether* ether) { Smc91xx* ctlr; int port, n; ctlr = ether->ctlr; port = ether->port; ilock(ctlr); if (ctlr->txbp) { n = TK2MS(MACHP(0)->ticks - ctlr->txtime);