etherelnk3.c
来自「这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易」· C语言 代码 · 共 1,932 行 · 第 1/3 页
C
1,932 行
txrxreset(port); COMMAND(port, AcknowledgeInterrupt, 0xFF); ctlr = tcmadapter(port, irq, p); switch(p->did){ default: break; case 0x5157: ctlr->eepromcmd = EepromRead8bRegister; ctlr->cbfnpa = p->mem[2].bar&~0x0F; ctlr->cbfn = KADDR(ctlr->cbfnpa); break; case 0x6056: ctlr->eepromcmd = EepromReadOffRegister; ctlr->cbfnpa = p->mem[2].bar&~0x0F; ctlr->cbfn = KADDR(ctlr->cbfnpa); break; } pcisetbme(p); }}static char* tcmpcmcia[] = { "3C589", /* 3COM 589[ABCD] */ "3C562", /* 3COM 562 */ "589E", /* 3COM Megahertz 589E */ nil,};static Ctlr*tcm5XXpcmcia(Ether* ether){ int i; Ctlr *ctlr; if(ether->type == nil) return nil; for(i = 0; tcmpcmcia[i] != nil; i++){ if(cistrcmp(ether->type, tcmpcmcia[i])) continue; ctlr = tcmadapter(ether->port, ether->irq, nil); ctlr->active = 1; return ctlr; } return nil;}static voidsetxcvr(Ctlr* ctlr, int xcvr){ int port, x; port = ctlr->port; if(ctlr->rxstatus9){ COMMAND(port, SelectRegisterWindow, Wsetup); x = ins(port+AddressConfig) & ~xcvrMask9; x |= (xcvr>>20)<<14; outs(port+AddressConfig, x); } else{ COMMAND(port, SelectRegisterWindow, Wfifo); x = inl(port+InternalConfig) & ~xcvrMask; x |= xcvr; outl(port+InternalConfig, x); } txrxreset(port);}static voidsetfullduplex(int port){ int x; COMMAND(port, SelectRegisterWindow, Wfifo); x = ins(port+MacControl); outs(port+MacControl, fullDuplexEnable|x); txrxreset(port);}static intmiimdi(int port, int n){ int data, i; /* * Read n bits from the MII Management Register. */ data = 0; for(i = n-1; i >= 0; i--){ if(ins(port) & mgmtData) data |= (1<<i); microdelay(1); outs(port, mgmtClk); microdelay(1); outs(port, 0); microdelay(1); } return data;}static voidmiimdo(int port, int bits, int n){ int i, mdo; /* * Write n bits to the MII Management Register. */ for(i = n-1; i >= 0; i--){ if(bits & (1<<i)) mdo = mgmtDir|mgmtData; else mdo = mgmtDir; outs(port, mdo); microdelay(1); outs(port, mdo|mgmtClk); microdelay(1); outs(port, mdo); microdelay(1); }}static intmiir(int port, int phyad, int regad){ int data, w; w = (STATUS(port)>>13) & 0x07; COMMAND(port, SelectRegisterWindow, Wdiagnostic); port += PhysicalMgmt; /* * Preamble; * ST+OP+PHYAD+REGAD; * TA + 16 data bits. */ miimdo(port, 0xFFFFFFFF, 32); miimdo(port, 0x1800|(phyad<<5)|regad, 14); data = miimdi(port, 18); port -= PhysicalMgmt; COMMAND(port, SelectRegisterWindow, w); if(data & 0x10000) return -1; return data & 0xFFFF;}static intscanphy(int port){ int i, x; for(i = 0; i < 32; i++){ if((x = miir(port, i, 2)) == -1 || x == 0) continue; x <<= 6; x |= miir(port, i, 3)>>10; XCVRDEBUG("phy%d: oui %uX reg1 %uX\n", i, x, miir(port, i, 1)); USED(x); return i; } return 24;}#ifdef notdefstatic struct xxx { int available; int next;} xxx[8] = { { base10TAvailable, 1, }, /* xcvr10BaseT -> xcvrAui */ { auiAvailable, 3, }, /* xcvrAui -> xcvr10Base2 */ { 0, -1, }, { coaxAvailable, -1, }, /* xcvr10Base2 -> nowhere */ { baseTXAvailable, 5, }, /* xcvr100BaseTX-> xcvr100BaseFX */ { baseFXAvailable, -1, }, /* xcvr100BaseFX-> nowhere */ { miiConnector, -1, }, /* xcvrMii -> nowhere */ { 0, -1, },};#endif /* notdef */static struct { char *name; int avail; int xcvr;} media[] = { "10BaseT", base10TAvailable, xcvr10BaseT, "10Base2", coaxAvailable, xcvr10Base2, "100BaseTX", baseTXAvailable, xcvr100BaseTX, "100BaseFX", baseFXAvailable, xcvr100BaseFX, "aui", auiAvailable, xcvrAui, "mii", miiConnector, xcvrMii};static intautoselect(Ctlr* ctlr){ int media, port, x; /* * Pathetic attempt at automatic media selection. * Really just to get the Fast Etherlink 10BASE-T/100BASE-TX * cards operational. * It's a bonus if it works for anything else. */ port = ctlr->port; if(ctlr->rxstatus9){ COMMAND(port, SelectRegisterWindow, Wsetup); x = ins(port+ConfigControl); media = 0; if(x & base10TAvailable9) media |= base10TAvailable; if(x & coaxAvailable9) media |= coaxAvailable; if(x & auiAvailable9) media |= auiAvailable; } else{ COMMAND(port, SelectRegisterWindow, Wfifo); media = ins(port+ResetOptions); } XCVRDEBUG("autoselect: media %uX\n", media); if(media & miiConnector) return xcvrMii; COMMAND(port, SelectRegisterWindow, Wdiagnostic); XCVRDEBUG("autoselect: media status %uX\n", ins(port+MediaStatus)); if(media & baseTXAvailable){ /* * Must have InternalConfig register. */ setxcvr(ctlr, xcvr100BaseTX); COMMAND(port, SelectRegisterWindow, Wdiagnostic); x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable); outs(port+MediaStatus, linkBeatEnable|x); delay(10); if(ins(port+MediaStatus) & linkBeatDetect) return xcvr100BaseTX; outs(port+MediaStatus, x); } if(media & base10TAvailable){ setxcvr(ctlr, xcvr10BaseT); COMMAND(port, SelectRegisterWindow, Wdiagnostic); x = ins(port+MediaStatus) & ~dcConverterEnabled; outs(port+MediaStatus, linkBeatEnable|jabberGuardEnable|x); delay(100); XCVRDEBUG("autoselect: 10BaseT media status %uX\n", ins(port+MediaStatus)); if(ins(port+MediaStatus) & linkBeatDetect) return xcvr10BaseT; outs(port+MediaStatus, x); } /* * Botch. */ return autoSelect;}static inteepromdata(Ctlr* ctlr, int offset){ int port; port = ctlr->port; COMMAND(port, SelectRegisterWindow, Wsetup); while(EEPROMBUSY(port)) ; EEPROMCMD(port, ctlr->eepromcmd, offset); while(EEPROMBUSY(port)) ; return EEPROMDATA(port);}static voidresetctlr(Ctlr *ctlr){ int port, x; port = ctlr->port; txrxreset(port); x = ins(port+ResetOp905B); XCVRDEBUG("905[BC] reset ops 0x%uX\n", x); x &= ~0x4010; if(ctlr->did == 0x5157){ x |= 0x0010; /* Invert LED */ outs(port+ResetOp905B, x); } if(ctlr->did == 0x6056){ x |= 0x4000; outs(port+ResetOp905B, x); COMMAND(port, SelectRegisterWindow, Wsetup); outs(port, 0x0800); }}intelnk3reset(Ether* ether){ char *p; Ctlr *ctlr; uchar ea[Eaddrlen]; static int scandone; int anar, anlpar, i, j, phyaddr, phystat, port, timeo, x; /* * Scan for adapter on PCI, EISA and finally * using the little ISA configuration dance. */ if(scandone == 0){ tcm59Xpci(); tcm5XXeisa(); tcm509isa(); scandone = 1; } /* * Any adapter matches if no ether->port is supplied, * otherwise the ports must match. */ for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ if(ctlr->active) continue; if(ether->port == 0 || ether->port == ctlr->port){ ctlr->active = 1; break; } } if(ctlr == nil && (ctlr = tcm5XXpcmcia(ether)) == 0) return -1; ether->ctlr = ctlr; port = ctlr->port; ether->port = port; ether->irq = ctlr->irq; if(ctlr->pcidev != nil) ether->tbdf = ctlr->pcidev->tbdf; else ether->tbdf = BUSUNKNOWN; /* * Read the DeviceID from the EEPROM, it's at offset 0x03, * and do something depending on capabilities. */ switch(ctlr->did = eepromdata(ctlr, 0x03)){ case 0x5157: /* 3C575 Cyclone */ case 0x6056: /*FALLTHROUGH*/ case 0x4500: /* 3C450 HomePNA Tornado */ case 0x7646: /* 3CSOHO100-TX */ case 0x9055: /* 3C905B-TX */ case 0x9200: /* 3C905C-TX */ case 0x9201: /* 3C920 */ case 0x9805: /* 3C9805: 3C980-TX Python-T 10/100baseTX */ /*FALLTHROUGH*/ case 0x9000: /* 3C900-TPO */ case 0x9001: /* 3C900-COMBO */ case 0x9005: /* 3C900B-COMBO */ case 0x9050: /* 3C905-TX */ case 0x9051: /* 3C905-T4 */ if(BUSTYPE(ether->tbdf) != BusPCI) goto buggery; ctlr->busmaster = 2; goto vortex; case 0x5900: /* 3C590-[TP|COMBO|TPO] */ case 0x5920: /* 3C592-[TP|COMBO|TPO] */ case 0x5950: /* 3C595-TX */ case 0x5951: /* 3C595-T4 */ case 0x5952: /* 3C595-MII */ case 0x5970: /* 3C597-TX */ case 0x5971: /* 3C597-T4 */ case 0x5972: /* 3C597-MII */ ctlr->busmaster = 1; vortex: COMMAND(port, SelectRegisterWindow, Wfifo); ctlr->xcvr = inl(port+InternalConfig) & (autoSelect|xcvrMask); ctlr->rxearly = 8188; ctlr->rxstatus9 = 0; break; buggery: default: ctlr->busmaster = 0; COMMAND(port, SelectRegisterWindow, Wsetup); x = ins(port+AddressConfig); ctlr->xcvr = ((x & xcvrMask9)>>14)<<20; if(x & autoSelect9) ctlr->xcvr |= autoSelect; ctlr->rxearly = 2044; ctlr->rxstatus9 = 1; break; } if(ctlr->rxearly >= 2048) ctlr->ts = 2; /* * Check if the adapter's station address is to be overridden. * If not, read it from the EEPROM and set in ether->ea prior to * loading the station address in Wstation. * The EEPROM returns 16-bits at a time. */ memset(ea, 0, Eaddrlen); if(memcmp(ea, ether->ea, Eaddrlen) == 0){ for(i = 0; i < Eaddrlen/2; i++){ x = eepromdata(ctlr, i); ether->ea[2*i] = x>>8; ether->ea[2*i+1] = x; } } COMMAND(port, SelectRegisterWindow, Wstation); for(i = 0; i < Eaddrlen; i++) outb(port+i, ether->ea[i]); /* * Enable the transceiver if necessary and determine whether * busmastering can be used. Due to bugs in the first revision * of the 3C59[05], don't use busmastering at 10Mbps. */ XCVRDEBUG("reset: xcvr %uX\n", ctlr->xcvr); /* * Allow user to specify desired media in plan9.ini */ for(i = 0; i < ether->nopt; i++){ if(cistrncmp(ether->opt[i], "media=", 6) != 0) continue; p = ether->opt[i]+6; for(j = 0; j < nelem(media); j++) if(cistrcmp(p, media[j].name) == 0) ctlr->xcvr = media[j].xcvr; } /* * forgive me, but i am weak */ switch(ctlr->did){ default: if(ctlr->xcvr & autoSelect) ctlr->xcvr = autoselect(ctlr); break; case 0x5157: case 0x6056: case 0x4500: case 0x7646: case 0x9055: case 0x9200: case 0x9201: case 0x9805: ctlr->xcvr = xcvrMii; resetctlr(ctlr); break; } XCVRDEBUG("xcvr selected: %uX, did 0x%uX\n", ctlr->xcvr, ctlr->did); switch(ctlr->xcvr){ case xcvrMii: /* * Quick hack. */ if(ctlr->did == 0x5157) phyaddr = 0; else if(ctlr->did == 0x6056) phyaddr = scanphy(port); else phyaddr = 24; for(i = 0; i < 7; i++) XCVRDEBUG(" %2.2uX", miir(port, phyaddr, i)); XCVRDEBUG("\n"); for(timeo = 0; timeo < 30; timeo++){ phystat = miir(port, phyaddr, 0x01); if(phystat & 0x20) break; XCVRDEBUG(" %2.2uX", phystat); delay(100); } XCVRDEBUG(" %2.2uX", miir(port, phyaddr, 0x01)); XCVRDEBUG("\n"); anar = miir(port, phyaddr, 0x04); anlpar = miir(port, phyaddr, 0x05) & 0x03E0; anar &= anlpar; miir(port, phyaddr, 0x00); XCVRDEBUG("mii an: %uX anlp: %uX r0:%uX r1:%uX\n", anar, anlpar, miir(port, phyaddr, 0x00), miir(port, phyaddr, 0x01)); for(i = 0; i < ether->nopt; i++){ if(cistrcmp(ether->opt[i], "fullduplex") == 0) anar |= 0x0100; else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0) anar |= 0x0100; else if(cistrcmp(ether->opt[i], "force100") == 0) anar |= 0x0080; } XCVRDEBUG("mii anar: %uX\n", anar); if(anar & 0x0100){ /* 100BASE-TXFD */ //ether->mbps = 100; setfullduplex(port); } else if(anar & 0x0200){ /* 100BASE-T4 */ /* nothing to do */ } else if(anar & 0x0080){ /* 100BASE-TX */ //ether->mbps = 100; } else if(anar & 0x0040) /* 10BASE-TFD */ setfullduplex(port); else{ /* 10BASE-T */ /* nothing to do */; } break; case xcvr100BaseTX: case xcvr100BaseFX: COMMAND(port, SelectRegisterWindow, Wfifo); x = inl(port+InternalConfig) & ~ramPartitionMask; outl(port+InternalConfig, x|ramPartition1to1); COMMAND(port, SelectRegisterWindow, Wdiagnostic); x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable); x |= linkBeatEnable; outs(port+MediaStatus, x); //if(x & dataRate100) // ether->mbps = 100; break; case xcvr10BaseT: /* * Enable Link Beat and Jabber to start the * transceiver. */ COMMAND(port, SelectRegisterWindow, Wdiagnostic); x = ins(port+MediaStatus) & ~dcConverterEnabled; x |= linkBeatEnable|jabberGuardEnable; outs(port+MediaStatus, x); if((ctlr->did & 0xFF00) == 0x5900) ctlr->busmaster = 0; break; case xcvr10Base2: COMMAND(port, SelectRegisterWindow, Wdiagnostic); x = ins(port+MediaStatus) & ~(linkBeatEnable|jabberGuardEnable); outs(port+MediaStatus, x); /* * Start the DC-DC converter. * Wait > 800 microseconds. */ COMMAND(port, EnableDcConverter, 0); delay(1); break; } /* * Wop is the normal operating register set. * The 3C59[0257] adapters allow access to more than one register window * at a time, but there are situations where switching still needs to be * done, so just do it. * Clear out any lingering Tx status. */ COMMAND(port, SelectRegisterWindow, Wop); if(ctlr->busmaster == 2) x = port+TxStatus905; else x = port+TxStatus; while(inb(x)) outb(x, 0); /* * Clear out the * adapter statistics, clear the statistics logged into ctlr. */ ilock(&ctlr->wlock); statistics(ether); memset(ctlr->stats, 0, sizeof(ctlr->stats)); //COMMAND(port, StatisticsEnable, 0); /* * Allocate any receive buffers. */ if (ctlr->busmaster == 2) { ctlr->dnenabled = 1; /* * 10MUpldBug. * Disabling is too severe, can use receive busmastering at * 100Mbps OK, but how to tell which rate is actually being used - * the 3c905 always seems to have dataRate100 set? * Believe the bug doesn't apply if upRxEarlyEnable is set * and the threshold is set such that uploads won't start * until the whole packet has been received. */ ctlr->upenabled = 1; x = eepromdata(ctlr, 0x0F); if(!(x & 0x01)) outl(port+PktStatus, upRxEarlyEnable); ctlr->nup = Nup; ctlr->ndn = Ndn; init905(ctlr); outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256)); } /* * Set a base TxStartThresh which will be incremented * if any txUnderrun errors occur and ensure no RxEarly * interrupts happen. */ ctlr->txthreshold = ETHERMAXTU/2; COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts); COMMAND(port, SetRxEarlyThresh, ctlr->rxearly>>ctlr->ts); iunlock(&ctlr->wlock); /* * Linkage to the generic ethernet driver. */ ether->attach = attach; ether->transmit = transmit; ether->interrupt = interrupt; return 0;}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?