if_tl.c
来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 2,302 行 · 第 1/5 页
C
2,302 行
* is to check everything but the 8 least significant bits. */ while(p->tl_vid) { if (sc->tl_phy_vid == p->tl_vid && (sc->tl_phy_did | 0x000F) == p->tl_did) { sc->tl_pinfo = p; break; } p++; } if (sc->tl_pinfo == NULL) { sc->tl_pinfo = &tl_phys[PHY_UNKNOWN]; } if (sc->tl_phy_sts & PHY_BMSR_100BT4 || sc->tl_phy_sts & PHY_BMSR_100BTXFULL || sc->tl_phy_sts & PHY_BMSR_100BTXHALF) ifp->if_baudrate = 100000000; else ifp->if_baudrate = 10000000; if (bootverbose) { printf("tl%d: phy at mii address %d\n", sc->tl_unit, sc->tl_phy_addr); printf("tl%d: %s ", sc->tl_unit, sc->tl_pinfo->tl_name); } if (sc->tl_phy_sts & PHY_BMSR_100BT4 || sc->tl_phy_sts & PHY_BMSR_100BTXHALF || sc->tl_phy_sts & PHY_BMSR_100BTXHALF) if (bootverbose) printf("10/100Mbps "); else { media &= ~IFM_100_TX; media |= IFM_10_T; if (bootverbose) printf("10Mbps "); } if (sc->tl_phy_sts & PHY_BMSR_100BTXFULL || sc->tl_phy_sts & PHY_BMSR_10BTFULL) if (bootverbose) printf("full duplex "); else { if (bootverbose) printf("half duplex "); media &= ~IFM_FDX; } if (sc->tl_phy_sts & PHY_BMSR_CANAUTONEG) { media = IFM_ETHER|IFM_AUTO; if (bootverbose) printf("autonegotiating\n"); } else if (bootverbose) printf("\n"); /* If this isn't a known PHY, print the PHY indentifier info. */ if (sc->tl_pinfo->tl_vid == 0 && bootverbose) printf("tl%d: vendor id: %04x product id: %04x\n", sc->tl_unit, sc->tl_phy_vid, sc->tl_phy_did); /* Set up ifmedia data and callbacks. */ ifmedia_init(&sc->ifmedia, 0, tl_ifmedia_upd, tl_ifmedia_sts); /* * All ThunderLANs support at least 10baseT half duplex. * They also support AUI selection if used in 10Mb/s modes. */ ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL); /* Some ThunderLAN PHYs support autonegotiation. */ if (sc->tl_phy_sts & PHY_BMSR_CANAUTONEG) ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL); /* Some support 10baseT full duplex. */ if (sc->tl_phy_sts & PHY_BMSR_10BTFULL) ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); /* Some support 100BaseTX half duplex. */ if (sc->tl_phy_sts & PHY_BMSR_100BTXHALF) ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX, 0, NULL); if (sc->tl_phy_sts & PHY_BMSR_100BTXHALF) ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX|IFM_HDX, 0, NULL); /* Some support 100BaseTX full duplex. */ if (sc->tl_phy_sts & PHY_BMSR_100BTXFULL) ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL); /* Some also support 100BaseT4. */ if (sc->tl_phy_sts & PHY_BMSR_100BT4) ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_T4, 0, NULL); /* Set default media. */ ifmedia_set(&sc->ifmedia, media); /* * Kick off an autonegotiation session if this PHY supports it. * This is necessary to make sure the chip's duplex mode matches * the PHY's duplex mode. It may not: once enabled, the PHY may * autonegotiate full-duplex mode with its link partner, but the * ThunderLAN chip defaults to half-duplex and stays there unless * told otherwise. */ if (sc->tl_phy_sts & PHY_BMSR_CANAUTONEG) { tl_init(sc);#ifdef TL_BACKGROUND_AUTONEG tl_autoneg(sc, TL_FLAG_SCHEDDELAY, 1);#else tl_autoneg(sc, TL_FLAG_FORCEDELAY, 1);#endif } return(0);}static voidtl_attach(config_id, unit) pcici_t config_id; int unit;{ int s, i, phys = 0;#ifndef TL_USEIOSPACE vm_offset_t pbase, vbase;#endif u_int32_t command; u_int16_t did, vid; struct tl_type *t; struct ifnet *ifp; struct tl_softc *sc; unsigned int round; caddr_t roundptr; s = splimp(); vid = pci_cfgread(config_id, PCIR_VENDOR, 2); did = pci_cfgread(config_id, PCIR_DEVICE, 2); t = tl_devs; while(t->tl_name != NULL) { if (vid == t->tl_vid && did == t->tl_did) break; t++; } if (t->tl_name == NULL) { printf("tl%d: unknown device!?\n", unit); goto fail; } /* First, allocate memory for the softc struct. */ sc = malloc(sizeof(struct tl_softc), M_DEVBUF, M_NOWAIT); if (sc == NULL) { printf("tl%d: no memory for softc struct!\n", unit); goto fail; } bzero(sc, sizeof(struct tl_softc)); /* * 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 TL_USEIOSPACE if (!(command & PCIM_CMD_PORTEN)) { printf("tl%d: failed to enable I/O ports!\n", unit); free(sc, M_DEVBUF); goto fail; } if (!pci_map_port(config_id, TL_PCI_LOIO, (u_short *)&(sc->tl_bhandle))) { if (!pci_map_port(config_id, TL_PCI_LOMEM, (u_short *)&(sc->tl_bhandle))) { printf ("tl%d: couldn't map ports\n", unit); goto fail; } }#ifdef __alpha__ sc->tl_btag = ALPHA_BUS_SPACE_IO;#endif#ifdef __i386__ sc->tl_btag = I386_BUS_SPACE_IO;#endif#else if (!(command & PCIM_CMD_MEMEN)) { printf("tl%d: failed to enable memory mapping!\n", unit); goto fail; } if (!pci_map_mem(config_id, TL_PCI_LOMEM, &vbase, &pbase)) { if (!pci_map_mem(config_id, TL_PCI_LOIO, &vbase, &pbase)) { printf ("tl%d: couldn't map memory\n", unit); goto fail; } }#ifdef __alpha__ sc->tl_btag = ALPHA_BUS_SPACE_MEM;#endif#ifdef __i386__ sc->tl_btag = I386_BUS_SPACE_MEM;#endif sc->tl_bhandle = vbase;#endif#ifdef notdef /* * The ThunderLAN manual suggests jacking the PCI latency * timer all the way up to its maximum value. I'm not sure * if this is really necessary, but what the manual wants, * the manual gets. */ command = pci_conf_read(config_id, TL_PCI_LATENCY_TIMER); command |= 0x0000FF00; pci_conf_write(config_id, TL_PCI_LATENCY_TIMER, command);#endif /* Allocate interrupt */ if (!pci_map_int(config_id, tl_intr, sc, &net_imask)) { printf("tl%d: couldn't map interrupt\n", unit); goto fail; } /* * Now allocate memory for the TX and RX lists. Note that * we actually allocate 8 bytes more than we really need: * this is because we need to adjust the final address to * be aligned on a quadword (64-bit) boundary in order to * make the chip happy. If the list structures aren't properly * aligned, DMA fails and the chip generates an adapter check * interrupt and has to be reset. If you set up the softc struct * just right you can sort of obtain proper alignment 'by chance.' * But I don't want to depend on this, so instead the alignment * is forced here. */ sc->tl_ldata_ptr = malloc(sizeof(struct tl_list_data) + 8, M_DEVBUF, M_NOWAIT); if (sc->tl_ldata_ptr == NULL) { free(sc, M_DEVBUF); printf("tl%d: no memory for list buffers!\n", unit); goto fail; } /* * Convoluted but satisfies my ANSI sensibilities. GCC lets * you do casts on the LHS of an assignment, but ANSI doesn't * allow that. */ sc->tl_ldata = (struct tl_list_data *)sc->tl_ldata_ptr; round = (unsigned int)sc->tl_ldata_ptr & 0xF; roundptr = sc->tl_ldata_ptr; for (i = 0; i < 8; i++) { if (round % 8) { round++; roundptr++; } else break; } sc->tl_ldata = (struct tl_list_data *)roundptr; bzero(sc->tl_ldata, sizeof(struct tl_list_data)); sc->tl_unit = unit; sc->tl_dinfo = t; if (t->tl_vid == COMPAQ_VENDORID || t->tl_vid == TI_VENDORID) sc->tl_eeaddr = TL_EEPROM_EADDR; if (t->tl_vid == OLICOM_VENDORID) sc->tl_eeaddr = TL_EEPROM_EADDR_OC; /* Reset the adapter. */ tl_softreset(sc, 1); tl_hardreset(sc); tl_softreset(sc, 1); /* * Get station address from the EEPROM. */ if (tl_read_eeprom(sc, (caddr_t)&sc->arpcom.ac_enaddr, sc->tl_eeaddr, ETHER_ADDR_LEN)) { printf("tl%d: failed to read station address\n", unit); goto fail; } /* * XXX Olicom, in its desire to be different from the * rest of the world, has done strange things with the * encoding of the station address in the EEPROM. First * of all, they store the address at offset 0xF8 rather * than at 0x83 like the ThunderLAN manual suggests. * Second, they store the address in three 16-bit words in * network byte order, as opposed to storing it sequentially * like all the other ThunderLAN cards. In order to get * the station address in a form that matches what the Olicom * diagnostic utility specifies, we have to byte-swap each * word. To make things even more confusing, neither 00:00:28 * nor 00:00:24 appear in the IEEE OUI database. */ if (sc->tl_dinfo->tl_vid == OLICOM_VENDORID) { for (i = 0; i < ETHER_ADDR_LEN; i += 2) { u_int16_t *p; p = (u_int16_t *)&sc->arpcom.ac_enaddr[i]; *p = ntohs(*p); } } /* * A ThunderLAN chip was detected. Inform the world. */ printf("tl%d: Ethernet address: %6D\n", unit, sc->arpcom.ac_enaddr, ":"); ifp = &sc->arpcom.ac_if; ifp->if_softc = sc; ifp->if_unit = sc->tl_unit; ifp->if_name = "tl"; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = tl_ioctl; ifp->if_output = ether_output; ifp->if_start = tl_start; ifp->if_watchdog = tl_watchdog; ifp->if_init = tl_init; ifp->if_mtu = ETHERMTU; ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; callout_handle_init(&sc->tl_stat_ch); /* Reset the adapter again. */ tl_softreset(sc, 1); tl_hardreset(sc); tl_softreset(sc, 1); /* * Now attach the ThunderLAN's PHYs. There will always * be at least one PHY; if the PHY address is 0x1F, then * it's the internal one. */ for (i = TL_PHYADDR_MIN; i < TL_PHYADDR_MAX + 1; i++) { sc->tl_phy_addr = i; if (bootverbose) printf("tl%d: looking for phy at addr %x\n", unit, i); tl_phy_writereg(sc, PHY_BMCR, PHY_BMCR_RESET); DELAY(500); while(tl_phy_readreg(sc, PHY_BMCR) & PHY_BMCR_RESET); sc->tl_phy_sts = tl_phy_readreg(sc, PHY_BMSR); if (bootverbose) printf("tl%d: status: %x\n", unit, sc->tl_phy_sts); if (!sc->tl_phy_sts) continue; if (tl_attach_phy(sc)) { printf("tl%d: failed to attach a phy %d\n", unit, i); goto fail; } phys++; if (phys && i != TL_PHYADDR_MAX) break; } /* * If no MII-based PHYs were detected, then this is a * TNETE110 device with a bit rate PHY. There's no autoneg * support, so just default to 10baseT mode. */ if (!phys) { struct ifmedia *ifm; sc->tl_bitrate = 1; ifmedia_init(&sc->ifmedia, 0, tl_ifmedia_upd, tl_ifmedia_sts); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL); ifmedia_set(&sc->ifmedia, IFM_ETHER|IFM_10_T); /* Reset again, this time setting bitrate mode. */ tl_softreset(sc, 1); ifm = &sc->ifmedia; ifm->ifm_media = ifm->ifm_cur->ifm_media; tl_ifmedia_upd(ifp); } tl_intvec_adchk((void *)sc, 0); tl_stop(sc); /* * Attempt to clear any stray interrupts * that may be lurking. */ tl_intr((void *)sc); /* * Call MI attach routines. */ if_attach(ifp); ether_ifattach(ifp);#if NBPFILTER > 0 bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));#endif at_shutdown(tl_shutdown, sc, SHUTDOWN_POST_SYNC);fail: splx(s); return;}/* * Initialize the transmit lists. */static int tl_list_tx_init(sc) struct tl_softc *sc;{ struct tl_chain_data *cd; struct tl_list_data *ld; int i; cd = &sc->tl_cdata; ld = sc->tl_ldata; for (i = 0; i < TL_TX_LIST_CNT; i++) { cd->tl_tx_chain[i].tl_ptr = &ld->tl_tx_list[i]; if (i == (TL_TX_LIST_CNT - 1)) cd->tl_tx_chain[i].tl_next = NULL; else cd->tl_tx_chain[i].tl_next = &cd->tl_tx_chain[i + 1]; } cd->tl_tx_free = &cd->tl_tx_chain[0]; cd->tl_tx_tail = cd->tl_tx_head = NULL; sc->tl_txeoc = 1; return(0);}/* * Initialize the RX lists and allocate mbufs for them. */static int tl_list_rx_init(sc) struct tl_softc *sc;{ struct tl_chain_data *cd; struct tl_list_data *ld; int i; cd = &sc->tl_cdata; ld = sc->tl_ldata; for (i = 0; i < TL_RX_LIST_CNT; i++) { cd->tl_rx_chain[i].tl_ptr = (struct tl_list_onefrag *)&ld->tl_rx_list[i]; if (tl_newbuf(sc, &cd->tl_rx_chain[i]) == ENOBUFS) return(ENOBUFS);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?