if_pn.c
来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 2,280 行 · 第 1/4 页
C
2,280 行
PN_SETBIT(sc, PN_NWAY, PN_NWAY_AUTOENB); DELAY(5000000); break; case PN_FLAG_SCHEDDELAY: /* * Wait for the transmitter to go idle before starting * an autoneg session, otherwise pn_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->pn_cdata.pn_tx_head != NULL) { sc->pn_want_auto = 1; return; } CSR_WRITE_4(sc, PN_GEN, PN_GEN_MUSTBEONE|PN_GEN_100TX_LOOP); PN_CLRBIT(sc, PN_NWAY, PN_NWAY_AUTONEGRSTR); PN_SETBIT(sc, PN_NWAY, PN_NWAY_AUTOENB); ifp->if_timer = 5; sc->pn_autoneg = 1; sc->pn_want_auto = 0; return; break; case PN_FLAG_DELAYTIMEO: ifp->if_timer = 0; sc->pn_autoneg = 0; break; default: printf("pn%d: invalid autoneg flag: %d\n", sc->pn_unit, flag); return; } if (CSR_READ_4(sc, PN_NWAY) & PN_NWAY_LPAR) { if (verbose) printf("pn%d: autoneg complete, ", sc->pn_unit); } else { if (verbose) printf("pn%d: autoneg not complete, ", sc->pn_unit); } /* Link is good. Report modes and set duplex mode. */ if (CSR_READ_4(sc, PN_ISR) & PN_ISR_LINKPASS) { if (verbose) printf("link status good "); ability = CSR_READ_4(sc, PN_NWAY); if (ability & PN_NWAY_LPAR100T4) { ifm->ifm_media = IFM_ETHER|IFM_100_T4; nway = PN_NWAY_MODE_100T4; printf("(100baseT4)\n"); } else if (ability & PN_NWAY_LPAR100FULL) { ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_FDX; nway = PN_NWAY_MODE_100FD; printf("(full-duplex, 100Mbps)\n"); } else if (ability & PN_NWAY_LPAR100HALF) { ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_HDX; nway = PN_NWAY_MODE_100HD; printf("(half-duplex, 100Mbps)\n"); } else if (ability & PN_NWAY_LPAR10FULL) { ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_FDX; nway = PN_NWAY_MODE_10FD; printf("(full-duplex, 10Mbps)\n"); } else if (ability & PN_NWAY_LPAR10HALF) { ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX; nway = PN_NWAY_MODE_10HD; printf("(half-duplex, 10Mbps)\n"); } /* Set ASIC's duplex mode to match the PHY. */ pn_setcfg(sc, ifm->ifm_media); CSR_WRITE_4(sc, PN_NWAY, nway); } else { if (verbose) printf("no carrier\n"); } pn_init(sc); if (sc->pn_tx_pend) { sc->pn_autoneg = 0; sc->pn_tx_pend = 0; pn_start(ifp); } return;}static void pn_setmode(sc, media) struct pn_softc *sc; int media;{ struct ifnet *ifp; ifp = &sc->arpcom.ac_if; /* * If an autoneg session is in progress, stop it. */ if (sc->pn_autoneg) { printf("pn%d: canceling autoneg session\n", sc->pn_unit); ifp->if_timer = sc->pn_autoneg = sc->pn_want_auto = 0; PN_CLRBIT(sc, PN_NWAY, PN_NWAY_AUTONEGRSTR); } printf("pn%d: selecting NWAY, ", sc->pn_unit); if (IFM_SUBTYPE(media) == IFM_100_T4) { printf("100Mbps/T4, half-duplex\n"); } if (IFM_SUBTYPE(media) == IFM_100_TX) { printf("100Mbps, "); } if (IFM_SUBTYPE(media) == IFM_10_T) { printf("10Mbps, "); } if ((media & IFM_GMASK) == IFM_FDX) { printf("full duplex\n"); } else { printf("half duplex\n"); } pn_setcfg(sc, media); return;}static void pn_setmode_mii(sc, media) struct pn_softc *sc; int media;{ u_int16_t bmcr; struct ifnet *ifp; ifp = &sc->arpcom.ac_if; /* * If an autoneg session is in progress, stop it. */ if (sc->pn_autoneg) { printf("pn%d: canceling autoneg session\n", sc->pn_unit); ifp->if_timer = sc->pn_autoneg = sc->pn_want_auto = 0; bmcr = pn_phy_readreg(sc, PHY_BMCR); bmcr &= ~PHY_BMCR_AUTONEGENBL; pn_phy_writereg(sc, PHY_BMCR, bmcr); } printf("pn%d: selecting MII, ", sc->pn_unit); bmcr = pn_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; } pn_setcfg(sc, media); pn_phy_writereg(sc, PHY_BMCR, bmcr); return;}/* * Programming the receiver filter on the tulip/PNIC is gross. You * have to construct a special setup frame and download it to the * chip via the transmit DMA engine. This routine is also somewhat * gross, as the setup frame is sent synchronously rather than putting * on the transmit queue. The transmitter has to be stopped, then we * can download the frame and wait for the 'owned' bit to clear. * * We always program the chip using 'hash perfect' mode, i.e. one perfect * address (our node address) and a 512-bit hash filter for multicast * frames. We also sneak the broadcast address into the hash filter since * we need that too. */void pn_setfilt(sc) struct pn_softc *sc;{ struct pn_desc *sframe; u_int32_t h, *sp; struct ifmultiaddr *ifma; struct ifnet *ifp; int i; ifp = &sc->arpcom.ac_if; PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_TX_ON); PN_SETBIT(sc, PN_ISR, PN_ISR_TX_IDLE); sframe = &sc->pn_cdata.pn_sframe; sp = (u_int32_t *)&sc->pn_cdata.pn_sbuf; bzero((char *)sp, PN_SFRAME_LEN); sframe->pn_status = PN_TXSTAT_OWN; sframe->pn_next = vtophys(&sc->pn_ldata->pn_tx_list[0]); sframe->pn_data = vtophys(&sc->pn_cdata.pn_sbuf); sframe->pn_ctl = PN_SFRAME_LEN | PN_TXCTL_TLINK | PN_TXCTL_SETUP | PN_FILTER_HASHPERF; /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_RX_PROMISC); else PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_RX_PROMISC); if (ifp->if_flags & IFF_ALLMULTI) PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_RX_ALLMULTI); 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 = pn_calchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); sp[h >> 4] |= 1 << (h & 0xF); } if (ifp->if_flags & IFF_BROADCAST) { h = pn_calchash(etherbroadcastaddr); sp[h >> 4] |= 1 << (h & 0xF); } sp[39] = ((u_int16_t *)sc->arpcom.ac_enaddr)[0]; sp[40] = ((u_int16_t *)sc->arpcom.ac_enaddr)[1]; sp[41] = ((u_int16_t *)sc->arpcom.ac_enaddr)[2]; CSR_WRITE_4(sc, PN_TXADDR, vtophys(sframe)); PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_TX_ON); CSR_WRITE_4(sc, PN_TXSTART, 0xFFFFFFFF); /* * Wait for chip to clear the 'own' bit. */ for (i = 0; i < PN_TIMEOUT; i++) { DELAY(10); if (sframe->pn_status != PN_TXSTAT_OWN) break; } if (i == PN_TIMEOUT) printf("pn%d: failed to send setup frame\n", sc->pn_unit); PN_SETBIT(sc, PN_ISR, PN_ISR_TX_NOBUF|PN_ISR_TX_IDLE); return;}/* * In order to fiddle with the * 'full-duplex' and '100Mbps' bits in the netconfig register, we * first have to put the transmit and/or receive logic in the idle state. */static void pn_setcfg(sc, media) struct pn_softc *sc; u_int32_t media;{ int i, restart = 0; if (CSR_READ_4(sc, PN_NETCFG) & (PN_NETCFG_TX_ON|PN_NETCFG_RX_ON)) { restart = 1; PN_CLRBIT(sc, PN_NETCFG, (PN_NETCFG_TX_ON|PN_NETCFG_RX_ON)); for (i = 0; i < PN_TIMEOUT; i++) { DELAY(10); if ((CSR_READ_4(sc, PN_ISR) & PN_ISR_TX_IDLE) && (CSR_READ_4(sc, PN_ISR) & PN_ISR_RX_IDLE)) break; } if (i == PN_TIMEOUT) printf("pn%d: failed to force tx and " "rx to idle state\n", sc->pn_unit); } if (IFM_SUBTYPE(media) == IFM_100_TX) { PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_SPEEDSEL); if (sc->pn_pinfo == NULL) { CSR_WRITE_4(sc, PN_GEN, PN_GEN_MUSTBEONE| PN_GEN_SPEEDSEL|PN_GEN_100TX_LOOP); PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_PCS| PN_NETCFG_SCRAMBLER|PN_NETCFG_MIIENB); PN_SETBIT(sc, PN_NWAY, PN_NWAY_SPEEDSEL); } } else { PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_SPEEDSEL); if (sc->pn_pinfo == NULL) { CSR_WRITE_4(sc, PN_GEN, PN_GEN_MUSTBEONE|PN_GEN_100TX_LOOP); PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_PCS| PN_NETCFG_SCRAMBLER|PN_NETCFG_MIIENB); PN_CLRBIT(sc, PN_NWAY, PN_NWAY_SPEEDSEL); } } if ((media & IFM_GMASK) == IFM_FDX) { PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_FULLDUPLEX); if (sc->pn_pinfo == NULL) PN_SETBIT(sc, PN_NWAY, PN_NWAY_DUPLEX); } else { PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_FULLDUPLEX); if (sc->pn_pinfo == NULL) PN_CLRBIT(sc, PN_NWAY, PN_NWAY_DUPLEX); } if (restart) PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_TX_ON|PN_NETCFG_RX_ON); return;}static void pn_reset(sc) struct pn_softc *sc;{ register int i; PN_SETBIT(sc, PN_BUSCTL, PN_BUSCTL_RESET); for (i = 0; i < PN_TIMEOUT; i++) { DELAY(10); if (!(CSR_READ_4(sc, PN_BUSCTL) & PN_BUSCTL_RESET)) break; } if (i == PN_TIMEOUT) printf("pn%d: reset never completed!\n", sc->pn_unit); /* Wait a little while for the chip to get its brains in order. */ DELAY(1000); return;}/* * Probe for a Lite-On PNIC chip. Check the PCI vendor and device * IDs against our list and return a device name if we find a match. */static const char *pn_probe(config_id, device_id) pcici_t config_id; pcidi_t device_id;{ struct pn_type *t; u_int32_t rev; t = pn_devs; while(t->pn_name != NULL) { if ((device_id & 0xFFFF) == t->pn_vid && ((device_id >> 16) & 0xFFFF) == t->pn_did) { if (t->pn_did == PN_DEVICEID_PNIC) { rev = pci_conf_read(config_id, PN_PCI_REVISION) & 0xFF; switch(rev & PN_REVMASK) { case PN_REVID_82C168: return(t->pn_name); break; case PN_REVID_82C169: t++; return(t->pn_name); default: printf("unknown PNIC rev: %x\n", rev); break; } } return(t->pn_name); } t++; } return(NULL);}/* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */static voidpn_attach(config_id, unit) pcici_t config_id; int unit;{ int s, i;#ifndef PN_USEIOSPACE vm_offset_t pbase, vbase;#endif u_char eaddr[ETHER_ADDR_LEN]; u_int32_t command; struct pn_softc *sc; struct ifnet *ifp; int media = IFM_ETHER|IFM_100_TX|IFM_FDX; unsigned int round; caddr_t roundptr; struct pn_type *p; u_int16_t phy_vid, phy_did, phy_sts;#ifdef PN_RX_BUG_WAR u_int32_t revision = 0;#endif s = splimp(); sc = malloc(sizeof(struct pn_softc), M_DEVBUF, M_NOWAIT); if (sc == NULL) { printf("pn%d: no memory for softc struct!\n", unit); return; } bzero(sc, sizeof(struct pn_softc)); /* * Handle power management nonsense. */ command = pci_conf_read(config_id, PN_PCI_CAPID) & 0x000000FF; if (command == 0x01) { command = pci_conf_read(config_id, PN_PCI_PWRMGMTCTRL); if (command & PN_PSTATE_MASK) { u_int32_t iobase, membase, irq; /* Save important PCI config data. */ iobase = pci_conf_read(config_id, PN_PCI_LOIO); membase = pci_conf_read(config_id, PN_PCI_LOMEM); irq = pci_conf_read(config_id, PN_PCI_INTLINE); /* Reset the power state. */ printf("pn%d: chip is in D%d power mode " "-- setting to D0\n", unit, command & PN_PSTATE_MASK); command &= 0xFFFFFFFC; pci_conf_write(config_id, PN_PCI_PWRMGMTCTRL, command); /* Restore PCI config data. */ pci_conf_write(config_id, PN_PCI_LOIO, iobase); pci_conf_write(config_id, PN_PCI_LOMEM, membase); pci_conf_write(config_id, PN_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 PN_USEIOSPACE if (!(command & PCIM_CMD_PORTEN)) { printf("pn%d: failed to enable I/O ports!\n", unit); free(sc, M_DEVBUF); goto fail; } if (!pci_map_port(config_id, PN_PCI_LOIO, (u_short *)&(sc->pn_bhandle))) { printf ("pn%d: couldn't map ports\n", unit); goto fail; }#ifdef __i386__ sc->pn_btag = I386_BUS_SPACE_IO;#endif#ifdef __alpha__ sc->pn_btag = ALPHA_BUS_SPACE_IO;#endif#else if (!(command & PCIM_CMD_MEMEN)) { printf("pn%d: failed to enable memory mapping!\n", unit); goto fail; } if (!pci_map_mem(config_id, PN_PCI_LOMEM, &vbase, &pbase)) { printf ("pn%d: couldn't map memory\n", unit); goto fail; } sc->pn_bhandle = vbase;#ifdef __i386__ sc->pn_btag = I386_BUS_SPACE_MEM;#endif#ifdef __alpha__ sc->pn_btag = ALPHA_BUS_SPACE_MEM;#endif#endif /* Allocate interrupt */ if (!pci_map_int(config_id, pn_intr, sc, &net_imask)) { printf("pn%d: couldn't map interrupt\n", unit); goto fail; } /* Save the cache line size. */ sc->pn_cachesize = pci_conf_read(config_id, PN_PCI_CACHELEN) & 0xFF; /* Reset the adapter. */ pn_reset(sc); /* * Get station address from the EEPROM. */ pn_read_eeprom(sc, (caddr_t)&eaddr, 0, 3, 1); /* * A PNIC chip was detected. Inform the world. */ printf("pn%d: Ethernet address: %6D\n", unit, eaddr, ":"); sc->pn_unit = unit; bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); sc->pn_ldata_ptr = malloc(sizeof(struct pn_list_data) + 8, M_DEVBUF, M_NOWAIT); if (sc->pn_ldata_ptr == NULL) { free(sc, M_DEVBUF); printf("pn%d: no memory for list buffers!\n", unit); goto fail; } sc->pn_ldata = (struct pn_list_data *)sc->pn_ldata_ptr; round = (unsigned int)sc->pn_ldata_ptr & 0xF; roundptr = sc->pn_ldata_ptr; for (i = 0; i < 8; i++) { if (round % 8) { round++; roundptr++; } else break; } sc->pn_ldata = (struct pn_list_data *)roundptr; bzero(sc->pn_ldata, sizeof(struct pn_list_data));#ifdef PN_RX_BUG_WAR revision = pci_conf_read(config_id, PN_PCI_REVISION) & 0x000000FF; if (revision == PN_169B_REV || revision == PN_169_REV || (revision & 0xF0) == PN_168_REV) { sc->pn_rx_war = 1; sc->pn_rx_buf = malloc(PN_RXLEN * 5, M_DEVBUF, M_NOWAIT); if (sc->pn_rx_buf == NULL) { printf("pn%d: no memory for workaround buffer\n", unit); goto fail; } } else { sc->pn_rx_war = 0; }#endif ifp = &sc->arpcom.ac_if; ifp->if_softc = sc; ifp->if_unit = unit; ifp->if_name = "pn"; ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?