if_rl.c
来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 1,918 行 · 第 1/3 页
C
1,918 行
return; } /* first, zot all the existing hash bits */ CSR_WRITE_4(sc, RL_MAR0, 0); CSR_WRITE_4(sc, RL_MAR4, 0); /* now program new ones */ for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; ifma = ifma->ifma_link.le_next) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; h = rl_calchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); if (h < 32) hashes[0] |= (1 << h); else hashes[1] |= (1 << (h - 32)); mcnt++; } if (mcnt) rxfilt |= RL_RXCFG_RX_MULTI; else rxfilt &= ~RL_RXCFG_RX_MULTI; CSR_WRITE_4(sc, RL_RXCFG, rxfilt); CSR_WRITE_4(sc, RL_MAR0, hashes[0]); CSR_WRITE_4(sc, RL_MAR4, hashes[1]); return;}/* * Initiate an autonegotiation session. */static void rl_autoneg_xmit(sc) struct rl_softc *sc;{ u_int16_t phy_sts; rl_phy_writereg(sc, PHY_BMCR, PHY_BMCR_RESET); DELAY(500); while(rl_phy_readreg(sc, PHY_BMCR) & PHY_BMCR_RESET); phy_sts = rl_phy_readreg(sc, PHY_BMCR); phy_sts |= PHY_BMCR_AUTONEGENBL|PHY_BMCR_AUTONEGRSTR; rl_phy_writereg(sc, PHY_BMCR, phy_sts); return;}/* * Invoke autonegotiation on a PHY. Also used with the 8139 internal * transceiver. */static void rl_autoneg_mii(sc, flag, verbose) struct rl_softc *sc; int flag; int verbose;{ u_int16_t phy_sts = 0, media, advert, ability; struct ifnet *ifp; struct ifmedia *ifm; ifm = &sc->ifmedia; ifp = &sc->arpcom.ac_if; /* * The 100baseT4 PHY sometimes has the 'autoneg supported' * bit cleared in the status register, but has the 'autoneg enabled' * bit set in the control register. This is a contradiction, and * I'm not sure how to handle it. If you want to force an attempt * to autoneg for 100baseT4 PHYs, #define FORCE_AUTONEG_TFOUR * and see what happens. */#ifndef FORCE_AUTONEG_TFOUR /* * First, see if autoneg is supported. If not, there's * no point in continuing. */ phy_sts = rl_phy_readreg(sc, PHY_BMSR); if (!(phy_sts & PHY_BMSR_CANAUTONEG)) { if (verbose) printf("rl%d: autonegotiation not supported\n", sc->rl_unit); return; }#endif switch (flag) { case RL_FLAG_FORCEDELAY: /* * XXX Never use this option anywhere but in the probe * routine: making the kernel stop dead in its tracks * for three whole seconds after we've gone multi-user * is really bad manners. */ rl_autoneg_xmit(sc); DELAY(5000000); break; case RL_FLAG_SCHEDDELAY: /* * Wait for the transmitter to go idle before starting * an autoneg session, otherwise rl_start() may clobber * our timeout, and we don't want to allow transmission * during an autoneg session since that can screw it up. */ if (sc->rl_cdata.last_tx != sc->rl_cdata.cur_tx) { sc->rl_want_auto = 1; return; } rl_autoneg_xmit(sc); ifp->if_timer = 5; sc->rl_autoneg = 1; sc->rl_want_auto = 0; return; break; case RL_FLAG_DELAYTIMEO: ifp->if_timer = 0; sc->rl_autoneg = 0; break; default: printf("rl%d: invalid autoneg flag: %d\n", sc->rl_unit, flag); return; } if (rl_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_AUTONEGCOMP) { if (verbose) printf("rl%d: autoneg complete, ", sc->rl_unit); phy_sts = rl_phy_readreg(sc, PHY_BMSR); } else { if (verbose) printf("rl%d: autoneg not complete, ", sc->rl_unit); } media = rl_phy_readreg(sc, PHY_BMCR); /* Link is good. Report modes and set duplex mode. */ if (rl_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_LINKSTAT) { if (verbose) printf("link status good "); advert = rl_phy_readreg(sc, PHY_ANAR); ability = rl_phy_readreg(sc, PHY_LPAR); if (advert & PHY_ANAR_100BT4 && ability & PHY_ANAR_100BT4) { ifm->ifm_media = IFM_ETHER|IFM_100_T4; media |= PHY_BMCR_SPEEDSEL; media &= ~PHY_BMCR_DUPLEX; printf("(100baseT4)\n"); } else if (advert & PHY_ANAR_100BTXFULL && ability & PHY_ANAR_100BTXFULL) { ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_FDX; media |= PHY_BMCR_SPEEDSEL; media |= PHY_BMCR_DUPLEX; printf("(full-duplex, 100Mbps)\n"); } else if (advert & PHY_ANAR_100BTXHALF && ability & PHY_ANAR_100BTXHALF) { ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_HDX; media |= PHY_BMCR_SPEEDSEL; media &= ~PHY_BMCR_DUPLEX; printf("(half-duplex, 100Mbps)\n"); } else if (advert & PHY_ANAR_10BTFULL && ability & PHY_ANAR_10BTFULL) { ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_FDX; media &= ~PHY_BMCR_SPEEDSEL; media |= PHY_BMCR_DUPLEX; printf("(full-duplex, 10Mbps)\n"); } else { ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX; media &= ~PHY_BMCR_SPEEDSEL; media &= ~PHY_BMCR_DUPLEX; printf("(half-duplex, 10Mbps)\n"); } /* Set ASIC's duplex mode to match the PHY. */ rl_phy_writereg(sc, PHY_BMCR, media); } else { if (verbose) printf("no carrier\n"); } rl_init(sc); if (sc->rl_tx_pend) { sc->rl_autoneg = 0; sc->rl_tx_pend = 0; rl_start(ifp); } return;}static void rl_getmode_mii(sc) struct rl_softc *sc;{ u_int16_t bmsr; struct ifnet *ifp; ifp = &sc->arpcom.ac_if; bmsr = rl_phy_readreg(sc, PHY_BMSR); if (bootverbose) printf("rl%d: PHY status word: %x\n", sc->rl_unit, bmsr); /* fallback */ sc->ifmedia.ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX; if (bmsr & PHY_BMSR_10BTHALF) { if (bootverbose) printf("rl%d: 10Mbps half-duplex mode supported\n", sc->rl_unit); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); } if (bmsr & PHY_BMSR_10BTFULL) { if (bootverbose) printf("rl%d: 10Mbps full-duplex mode supported\n", sc->rl_unit); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); sc->ifmedia.ifm_media = IFM_ETHER|IFM_10_T|IFM_FDX; } if (bmsr & PHY_BMSR_100BTXHALF) { if (bootverbose) printf("rl%d: 100Mbps half-duplex mode supported\n", sc->rl_unit); ifp->if_baudrate = 100000000; ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX|IFM_HDX, 0, NULL); sc->ifmedia.ifm_media = IFM_ETHER|IFM_100_TX|IFM_HDX; } if (bmsr & PHY_BMSR_100BTXFULL) { if (bootverbose) printf("rl%d: 100Mbps full-duplex mode supported\n", sc->rl_unit); ifp->if_baudrate = 100000000; ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL); sc->ifmedia.ifm_media = IFM_ETHER|IFM_100_TX|IFM_FDX; } /* Some also support 100BaseT4. */ if (bmsr & PHY_BMSR_100BT4) { if (bootverbose) printf("rl%d: 100baseT4 mode supported\n", sc->rl_unit); ifp->if_baudrate = 100000000; ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_T4, 0, NULL); sc->ifmedia.ifm_media = IFM_ETHER|IFM_100_T4;#ifdef FORCE_AUTONEG_TFOUR if (bootverbose) printf("rl%d: forcing on autoneg support for BT4\n", sc->rl_unit); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0 NULL): sc->ifmedia.ifm_media = IFM_ETHER|IFM_AUTO;#endif } if (bmsr & PHY_BMSR_CANAUTONEG) { if (bootverbose) printf("rl%d: autoneg supported\n", sc->rl_unit); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL); sc->ifmedia.ifm_media = IFM_ETHER|IFM_AUTO; } return;}/* * Set speed and duplex mode. */static void rl_setmode_mii(sc, media) struct rl_softc *sc; int media;{ u_int16_t bmcr; printf("rl%d: selecting MII, ", sc->rl_unit); bmcr = rl_phy_readreg(sc, PHY_BMCR); bmcr &= ~(PHY_BMCR_AUTONEGENBL|PHY_BMCR_SPEEDSEL| PHY_BMCR_DUPLEX|PHY_BMCR_LOOPBK); if (IFM_SUBTYPE(media) == IFM_100_T4) { printf("100Mbps/T4, half-duplex\n"); bmcr |= PHY_BMCR_SPEEDSEL; bmcr &= ~PHY_BMCR_DUPLEX; } if (IFM_SUBTYPE(media) == IFM_100_TX) { printf("100Mbps, "); bmcr |= PHY_BMCR_SPEEDSEL; } if (IFM_SUBTYPE(media) == IFM_10_T) { printf("10Mbps, "); bmcr &= ~PHY_BMCR_SPEEDSEL; } if ((media & IFM_GMASK) == IFM_FDX) { printf("full duplex\n"); bmcr |= PHY_BMCR_DUPLEX; } else { printf("half duplex\n"); bmcr &= ~PHY_BMCR_DUPLEX; } rl_phy_writereg(sc, PHY_BMCR, bmcr); return;}static void rl_reset(sc) struct rl_softc *sc;{ register int i; CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_RESET); for (i = 0; i < RL_TIMEOUT; i++) { DELAY(10); if (!(CSR_READ_1(sc, RL_COMMAND) & RL_CMD_RESET)) break; } if (i == RL_TIMEOUT) printf("rl%d: reset never completed!\n", sc->rl_unit); return;}/* * Probe for a RealTek 8129/8139 chip. Check the PCI vendor and device * IDs against our list and return a device name if we find a match. */static const char *rl_probe(config_id, device_id) pcici_t config_id; pcidi_t device_id;{ struct rl_type *t; t = rl_devs; while(t->rl_name != NULL) { if ((device_id & 0xFFFF) == t->rl_vid && ((device_id >> 16) & 0xFFFF) == t->rl_did) { return(t->rl_name); } t++; } return(NULL);}/* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */static voidrl_attach(config_id, unit) pcici_t config_id; int unit;{ int s, i;#ifndef RL_USEIOSPACE vm_offset_t pbase, vbase;#endif u_char eaddr[ETHER_ADDR_LEN]; u_int32_t command; struct rl_softc *sc; struct ifnet *ifp; int media = IFM_ETHER|IFM_100_TX|IFM_FDX; struct rl_type *p; u_int16_t phy_vid, phy_did, phy_sts; u_int16_t rl_did = 0; s = splimp(); sc = malloc(sizeof(struct rl_softc), M_DEVBUF, M_NOWAIT); if (sc == NULL) { printf("rl%d: no memory for softc struct!\n", unit); goto fail; } bzero(sc, sizeof(struct rl_softc)); /* * Handle power management nonsense. */ command = pci_conf_read(config_id, RL_PCI_CAPID) & 0x000000FF; if (command == 0x01) { command = pci_conf_read(config_id, RL_PCI_PWRMGMTCTRL); if (command & RL_PSTATE_MASK) { u_int32_t iobase, membase, irq; /* Save important PCI config data. */ iobase = pci_conf_read(config_id, RL_PCI_LOIO); membase = pci_conf_read(config_id, RL_PCI_LOMEM); irq = pci_conf_read(config_id, RL_PCI_INTLINE); /* Reset the power state. */ printf("rl%d: chip is is in D%d power mode " "-- setting to D0\n", unit, command & RL_PSTATE_MASK); command &= 0xFFFFFFFC; pci_conf_write(config_id, RL_PCI_PWRMGMTCTRL, command); /* Restore PCI config data. */ pci_conf_write(config_id, RL_PCI_LOIO, iobase); pci_conf_write(config_id, RL_PCI_LOMEM, membase); pci_conf_write(config_id, RL_PCI_INTLINE, irq); } } /* * Map control/status registers. */ command = pci_conf_read(config_id, PCI_COMMAND_STATUS_REG); command |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); pci_conf_write(config_id, PCI_COMMAND_STATUS_REG, command); command = pci_conf_read(config_id, PCI_COMMAND_STATUS_REG);#ifdef RL_USEIOSPACE if (!(command & PCIM_CMD_PORTEN)) { printf("rl%d: failed to enable I/O ports!\n", unit); free(sc, M_DEVBUF); goto fail; } if (!pci_map_port(config_id, RL_PCI_LOIO, (u_int16_t *)&(sc->rl_bhandle))) { printf ("rl%d: couldn't map ports\n", unit); goto fail; } sc->rl_btag = I386_BUS_SPACE_IO;#else if (!(command & PCIM_CMD_MEMEN)) { printf("rl%d: failed to enable memory mapping!\n", unit); goto fail; } if (!pci_map_mem(config_id, RL_PCI_LOMEM, &vbase, &pbase)) { printf ("rl%d: couldn't map memory\n", unit); goto fail; } sc->rl_btag = I386_BUS_SPACE_MEM; sc->rl_bhandle = vbase;#endif /* Allocate interrupt */ if (!pci_map_int(config_id, rl_intr, sc, &net_imask)) { printf("rl%d: couldn't map interrupt\n", unit); goto fail; } /* Reset the adapter. */ rl_reset(sc); /* * Get station address from the EEPROM. */ rl_read_eeprom(sc, (caddr_t)&eaddr, RL_EE_EADDR, 3, 0); /* * A RealTek chip was detected. Inform the world. */ printf("rl%d: Ethernet address: %6D\n", unit, eaddr, ":"); sc->rl_unit = unit; bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); /* * Now read the exact device type from the EEPROM to find * out if it's an 8129 or 8139. */ rl_read_eeprom(sc, (caddr_t)&rl_did, RL_EE_PCI_DID, 1, 0); if (rl_did == RT_DEVICEID_8139 || rl_did == ACCTON_DEVICEID_5030 || rl_did == DELTA_DEVICEID_8139) sc->rl_type = RL_8139; else if (rl_did == RT_DEVICEID_8129) sc->rl_type = RL_8129; else { printf("rl%d: unknown device ID: %x\n", unit, rl_did); free(sc, M_DEVBUF); goto fail; } sc->rl_cdata.rl_rx_buf = contigmalloc(RL_RXBUFLEN + 16, M_DEVBUF, M_NOWAIT, 0x100000, 0xffffffff, PAGE_SIZE, 0); if (sc->rl_cdata.rl_rx_buf == NULL) { free(sc, M_DEVBUF); printf("rl%d: no memory for list buffers!\n", unit); goto fail; } ifp = &sc->arpcom.ac_if; ifp->if_softc = sc; ifp->if_unit = unit; ifp->if_name = "rl"; ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = rl_ioctl; ifp->if_output = ether_output; ifp->if_start = rl_start; ifp->if_watchdog = rl_watchdog; ifp->if_init = rl_init; ifp->if_baudrate = 10000000; ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; if (sc->rl_type == RL_8129) { if (bootverbose) printf("rl%d: probing for a PHY\n", sc->rl_unit); for (i = RL_PHYADDR_MIN; i < RL_PHYADDR_MAX + 1; i++) { if (bootverbose) printf("rl%d: checking address: %d\n", sc->rl_unit, i); sc->rl_phy_addr = i; rl_phy_writereg(sc, PHY_BMCR, PHY_BMCR_RESET); DELAY(500); while(rl_phy_readreg(sc, PHY_BMCR) & PHY_BMCR_RESET); if ((phy_sts = rl_phy_readreg(sc, PHY_BMSR))) break; } if (phy_sts) { phy_vid = rl_phy_readreg(sc, PHY_VENID); phy_did = rl_phy_readreg(sc, PHY_DEVID); if (bootverbose) printf("rl%d: found PHY at address %d, ", sc->rl_unit, sc->rl_phy_addr); if (bootverbose) printf("vendor id: %x device id: %x\n", phy_vid, phy_did); p = rl_phys; while(p->rl_vid) { if (phy_vid == p->rl_vid && (phy_did | 0x000F) == p->rl_did) { sc->rl_pinfo = p; break; } p++; } if (sc->rl_pinfo == NULL) sc->rl_pinfo = &rl_phys[PHY_UNKNOWN]; if (bootverbose) printf("rl%d: PHY type: %s\n", sc->rl_unit, sc->rl_pinfo->rl_name); } else { printf("rl%d: MII without any phy!\n", sc->rl_unit); } } /* * Do ifmedia setup. */ ifmedia_init(&sc->ifmedia, 0, rl_ifmedia_upd, rl_ifmedia_sts); rl_getmode_mii(sc); /* Choose a default media. */ media = IFM_ETHER|IFM_AUTO; ifmedia_set(&sc->ifmedia, media); rl_autoneg_mii(sc, RL_FLAG_FORCEDELAY, 1); /* * Call MI attach routines. */ if_attach(ifp); ether_ifattach(ifp);#if NBPFILTER > 0 bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));#endif at_shutdown(rl_shutdown, sc, SHUTDOWN_POST_SYNC);fail: splx(s); return;}/* * Initialize the transmit descriptors. */static int rl_list_tx_init(sc) struct rl_softc *sc;{ struct rl_chain_data *cd; int i; cd = &sc->rl_cdata; for (i = 0; i < RL_TX_LIST_CNT; i++) { cd->rl_tx_chain[i] = NULL; CSR_WRITE_4(sc, RL_TXADDR0 + i, 0x0000000); } sc->rl_cdata.cur_tx = 0; sc->rl_cdata.last_tx = 0; return(0);}/* * A frame has been uploaded: pass the resulting mbuf chain up to * the higher level protocols. * * You know there's something wrong with a PCI bus-master chip design * when you have to use m_devget(). * * The receive operation is badly documented in the datasheet, so I'll * attempt to document it here. The driver provides a buffer area and * places its base address in the RX buffer start address register. * The chip then begins copying frames into the RX buffer. Each frame * is preceeded by a 32-bit RX status word which specifies the length * of the frame and certain other status bits. Each frame (starting with * the status word) is also 32-bit aligned. The frame length is in the * first 16 bits of the status word; the lower 15 bits correspond with * the 'rx status register' mentioned in the datasheet. */static void rl_rxeof(sc) struct rl_softc *sc;{ struct ether_header *eh; struct mbuf *m; struct ifnet *ifp; int total_len = 0; u_int32_t rxstat; caddr_t rxbufpos; int wrap = 0; u_int16_t cur_rx; u_int16_t limit; u_int16_t rx_bytes = 0, max_bytes;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?