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 + -
显示快捷键?