if_fxp.c
来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 1,926 行 · 第 1/4 页
C
1,926 行
break; default: return (EINVAL); } return (0);}#else /* __FreeBSD__ */static u_long fxp_count;static const char *fxp_probe __P((pcici_t, pcidi_t));static void fxp_attach __P((pcici_t, int));static void fxp_shutdown __P((int, void *));static struct pci_device fxp_device = { "fxp", fxp_probe, fxp_attach, &fxp_count, NULL};DATA_SET(pcidevice_set, fxp_device);/* * Return identification string if this is device is ours. */static const char *fxp_probe(config_id, device_id) pcici_t config_id; pcidi_t device_id;{ if (((device_id & 0xffff) == FXP_VENDORID_INTEL) && ((device_id >> 16) & 0xffff) == FXP_DEVICEID_i82557) return ("Intel EtherExpress Pro 10/100B Ethernet"); return NULL;}static voidfxp_attach(config_id, unit) pcici_t config_id; int unit;{ struct fxp_softc *sc; vm_offset_t pbase; struct ifnet *ifp; int s; u_long val; sc = malloc(sizeof(struct fxp_softc), M_DEVBUF, M_NOWAIT); if (sc == NULL) return; bzero(sc, sizeof(struct fxp_softc)); callout_handle_init(&sc->stat_ch); s = splimp(); /* * Enable bus mastering. */ val = pci_conf_read(config_id, PCI_COMMAND_STATUS_REG); val |= (PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); pci_conf_write(config_id, PCI_COMMAND_STATUS_REG, val); /* * Map control/status registers. */ if (!pci_map_mem(config_id, FXP_PCI_MMBA, (vm_offset_t *)&sc->csr, &pbase)) { printf("fxp%d: couldn't map memory\n", unit); goto fail; } /* * Allocate our interrupt. */ if (!pci_map_int(config_id, fxp_intr, sc, &net_imask)) { printf("fxp%d: couldn't map interrupt\n", unit); goto fail; } /* Do generic parts of attach. */ if (fxp_attach_common(sc, sc->arpcom.ac_enaddr)) { /* Failed! */ (void) pci_unmap_int(config_id); goto fail; } printf("fxp%d: Ethernet address %6D%s\n", unit, sc->arpcom.ac_enaddr, ":", sc->phy_10Mbps_only ? ", 10Mbps" : ""); ifp = &sc->arpcom.ac_if; ifp->if_unit = unit; ifp->if_name = "fxp"; ifp->if_output = ether_output; ifp->if_baudrate = 100000000; ifp->if_init = fxp_init; ifp->if_softc = sc; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = fxp_ioctl; ifp->if_start = fxp_start; ifp->if_watchdog = fxp_watchdog; /* * Attach the interface. */ if_attach(ifp); /* * Let the system queue as many packets as we have available * TX descriptors. */ ifp->if_snd.ifq_maxlen = FXP_NTXCB - 1; ether_ifattach(ifp);#if NBPFILTER > 0 bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));#endif /* * Add shutdown hook so that DMA is disabled prior to reboot. Not * doing do could allow DMA to corrupt kernel memory during the * reboot before the driver initializes. */ at_shutdown(fxp_shutdown, sc, SHUTDOWN_POST_SYNC); splx(s); return; fail: free(sc, M_DEVBUF); splx(s);}/* * Device shutdown routine. Called at system shutdown after sync. The * main purpose of this routine is to shut off receiver DMA so that * kernel memory doesn't get clobbered during warmboot. */static voidfxp_shutdown(howto, sc) int howto; void *sc;{ fxp_stop((struct fxp_softc *) sc);}#endif /* __NetBSD__ *//************************************************************* * End of operating system-specific autoconfiguration glue *************************************************************//* * Do generic parts of attach. */static intfxp_attach_common(sc, enaddr) struct fxp_softc *sc; u_int8_t *enaddr;{ u_int16_t data; int i, nmedia, defmedia; const int *media; /* * Reset to a stable state. */ CSR_WRITE_4(sc, FXP_CSR_PORT, FXP_PORT_SELECTIVE_RESET); DELAY(10); sc->cbl_base = malloc(sizeof(struct fxp_cb_tx) * FXP_NTXCB, M_DEVBUF, M_NOWAIT); if (sc->cbl_base == NULL) goto fail; bzero(sc->cbl_base, sizeof(struct fxp_cb_tx) * FXP_NTXCB); sc->fxp_stats = malloc(sizeof(struct fxp_stats), M_DEVBUF, M_NOWAIT); if (sc->fxp_stats == NULL) goto fail; bzero(sc->fxp_stats, sizeof(struct fxp_stats)); sc->mcsp = malloc(sizeof(struct fxp_cb_mcs), M_DEVBUF, M_NOWAIT); if (sc->mcsp == NULL) goto fail; /* * Pre-allocate our receive buffers. */ for (i = 0; i < FXP_NRFABUFS; i++) { if (fxp_add_rfabuf(sc, NULL) != 0) { goto fail; } } /* * Get info about the primary PHY */ fxp_read_eeprom(sc, (u_int16_t *)&data, 6, 1); sc->phy_primary_addr = data & 0xff; sc->phy_primary_device = (data >> 8) & 0x3f; sc->phy_10Mbps_only = data >> 15; /* * Read MAC address. */ fxp_read_eeprom(sc, (u_int16_t *)enaddr, 0, 3); /* * Initialize the media structures. */ media = fxp_media_default; nmedia = sizeof(fxp_media_default) / sizeof(fxp_media_default[0]); defmedia = FXP_MEDIA_DEFAULT_DEFMEDIA; for (i = 0; i < NFXPMEDIA; i++) { if (sc->phy_primary_device == fxp_media[i].fsm_phy) { media = fxp_media[i].fsm_media; nmedia = fxp_media[i].fsm_nmedia; defmedia = fxp_media[i].fsm_defmedia; } } ifmedia_init(&sc->sc_media, 0, fxp_mediachange, fxp_mediastatus); for (i = 0; i < nmedia; i++) { if (IFM_SUBTYPE(media[i]) == IFM_100_TX && sc->phy_10Mbps_only) continue; ifmedia_add(&sc->sc_media, media[i], 0, NULL); } ifmedia_set(&sc->sc_media, defmedia); return (0); fail: printf(FXP_FORMAT ": Failed to malloc memory\n", FXP_ARGS(sc)); if (sc->cbl_base) free(sc->cbl_base, M_DEVBUF); if (sc->fxp_stats) free(sc->fxp_stats, M_DEVBUF); if (sc->mcsp) free(sc->mcsp, M_DEVBUF); /* frees entire chain */ if (sc->rfa_headm) m_freem(sc->rfa_headm); return (ENOMEM);}/* * Read from the serial EEPROM. Basically, you manually shift in * the read opcode (one bit at a time) and then shift in the address, * and then you shift out the data (all of this one bit at a time). * The word size is 16 bits, so you have to provide the address for * every 16 bits of data. */static voidfxp_read_eeprom(sc, data, offset, words) struct fxp_softc *sc; u_short *data; int offset; int words;{ u_int16_t reg; int i, x; for (i = 0; i < words; i++) { CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS); /* * Shift in read opcode. */ for (x = 3; x > 0; x--) { if (FXP_EEPROM_OPC_READ & (1 << (x - 1))) { reg = FXP_EEPROM_EECS | FXP_EEPROM_EEDI; } else { reg = FXP_EEPROM_EECS; } CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg | FXP_EEPROM_EESK); DELAY(1); CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); DELAY(1); } /* * Shift in address. */ for (x = 6; x > 0; x--) { if ((i + offset) & (1 << (x - 1))) { reg = FXP_EEPROM_EECS | FXP_EEPROM_EEDI; } else { reg = FXP_EEPROM_EECS; } CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg | FXP_EEPROM_EESK); DELAY(1); CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); DELAY(1); } reg = FXP_EEPROM_EECS; data[i] = 0; /* * Shift out data. */ for (x = 16; x > 0; x--) { CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg | FXP_EEPROM_EESK); DELAY(1); if (CSR_READ_2(sc, FXP_CSR_EEPROMCONTROL) & FXP_EEPROM_EEDO) data[i] |= (1 << (x - 1)); CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); DELAY(1); } CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 0); DELAY(1); }}/* * Start packet transmission on the interface. */static voidfxp_start(ifp) struct ifnet *ifp;{ struct fxp_softc *sc = ifp->if_softc; struct fxp_cb_tx *txp; /* * See if we need to suspend xmit until the multicast filter * has been reprogrammed (which can only be done at the head * of the command chain). */ if (sc->need_mcsetup) return; txp = NULL; /* * We're finished if there is nothing more to add to the list or if * we're all filled up with buffers to transmit. * NOTE: One TxCB is reserved to guarantee that fxp_mc_setup() can add * a NOP command when needed. */ while (ifp->if_snd.ifq_head != NULL && sc->tx_queued < FXP_NTXCB - 1) { struct mbuf *m, *mb_head; int segment; /* * Grab a packet to transmit. */ IF_DEQUEUE(&ifp->if_snd, mb_head); /* * Get pointer to next available tx desc. */ txp = sc->cbl_last->next; /* * Go through each of the mbufs in the chain and initialize * the transmit buffer descriptors with the physical address * and size of the mbuf. */tbdinit: for (m = mb_head, segment = 0; m != NULL; m = m->m_next) { if (m->m_len != 0) { if (segment == FXP_NTXSEG) break; txp->tbd[segment].tb_addr = vtophys(mtod(m, vm_offset_t)); txp->tbd[segment].tb_size = m->m_len; segment++; } } if (m != NULL) { struct mbuf *mn; /* * We ran out of segments. We have to recopy this mbuf * chain first. Bail out if we can't get the new buffers. */ MGETHDR(mn, M_DONTWAIT, MT_DATA); if (mn == NULL) { m_freem(mb_head); break; } if (mb_head->m_pkthdr.len > MHLEN) { MCLGET(mn, M_DONTWAIT); if ((mn->m_flags & M_EXT) == 0) { m_freem(mn); m_freem(mb_head); break; } } m_copydata(mb_head, 0, mb_head->m_pkthdr.len, mtod(mn, caddr_t)); mn->m_pkthdr.len = mn->m_len = mb_head->m_pkthdr.len; m_freem(mb_head); mb_head = mn; goto tbdinit; } txp->tbd_number = segment; txp->mb_head = mb_head; txp->cb_status = 0; if (sc->tx_queued != FXP_CXINT_THRESH - 1) { txp->cb_command = FXP_CB_COMMAND_XMIT | FXP_CB_COMMAND_SF | FXP_CB_COMMAND_S; } else { txp->cb_command = FXP_CB_COMMAND_XMIT | FXP_CB_COMMAND_SF | FXP_CB_COMMAND_S | FXP_CB_COMMAND_I; /* * Set a 5 second timer just in case we don't hear from the * card again. */ ifp->if_timer = 5; } txp->tx_threshold = tx_threshold; /* * Advance the end of list forward. */ sc->cbl_last->cb_command &= ~FXP_CB_COMMAND_S; sc->cbl_last = txp; /* * Advance the beginning of the list forward if there are * no other packets queued (when nothing is queued, cbl_first * sits on the last TxCB that was sent out). */ if (sc->tx_queued == 0) sc->cbl_first = txp; sc->tx_queued++;#if NBPFILTER > 0 /* * Pass packet to bpf if there is a listener. */ if (ifp->if_bpf) bpf_mtap(FXP_BPFTAP_ARG(ifp), mb_head);#endif } /* * We're finished. If we added to the list, issue a RESUME to get DMA * going again if suspended. */ if (txp != NULL) { fxp_scb_wait(sc); CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_RESUME); }}/* * Process interface interrupts. */static FXP_INTR_TYPEfxp_intr(arg) void *arg;{ struct fxp_softc *sc = arg; struct ifnet *ifp = &sc->sc_if; u_int8_t statack;#if defined(__NetBSD__) int claimed = 0;#endif while ((statack = CSR_READ_1(sc, FXP_CSR_SCB_STATACK)) != 0) {#if defined(__NetBSD__) claimed = 1;#endif /* * First ACK all the interrupts in this pass. */ CSR_WRITE_1(sc, FXP_CSR_SCB_STATACK, statack); /* * Free any finished transmit mbuf chains.
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?