Module Name: src Committed By: nisimura Date: Tue Mar 31 02:32:25 UTC 2020
Modified Files: src/sys/dev/ic: dm9000.c dm9000reg.h dm9000var.h Log Message: - reorgazine the entire structure to utilise ifmedia(4)/mii(4) and redefine starting point of debug. - nuke duplicated standard MII register definition. Davicom PHY extension is defined in dev/mii/dmphyreg.h To generate a diff of this commit: cvs rdiff -u -r1.23 -r1.24 src/sys/dev/ic/dm9000.c cvs rdiff -u -r1.3 -r1.4 src/sys/dev/ic/dm9000reg.h cvs rdiff -u -r1.5 -r1.6 src/sys/dev/ic/dm9000var.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/ic/dm9000.c diff -u src/sys/dev/ic/dm9000.c:1.23 src/sys/dev/ic/dm9000.c:1.24 --- src/sys/dev/ic/dm9000.c:1.23 Sun Mar 29 23:16:52 2020 +++ src/sys/dev/ic/dm9000.c Tue Mar 31 02:32:25 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: dm9000.c,v 1.23 2020/03/29 23:16:52 nisimura Exp $ */ +/* $NetBSD: dm9000.c,v 1.24 2020/03/31 02:32:25 nisimura Exp $ */ /* * Copyright (c) 2009 Paul Fleischer @@ -89,29 +89,26 @@ #include <sys/cdefs.h> #include <sys/param.h> -#include <sys/kernel.h> -#include <sys/systm.h> -#include <sys/mbuf.h> -#include <sys/syslog.h> -#include <sys/socket.h> +#include <sys/bus.h> +#include <sys/intr.h> #include <sys/device.h> +#include <sys/mbuf.h> +#include <sys/sockio.h> #include <sys/malloc.h> -#include <sys/ioctl.h> #include <sys/errno.h> +#include <sys/cprng.h> +#include <sys/rndsource.h> +#include <sys/kernel.h> +#include <sys/systm.h> #include <net/if.h> +#include <net/if_dl.h> #include <net/if_ether.h> #include <net/if_media.h> +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> #include <net/bpf.h> -#ifdef INET -#include <netinet/in.h> -#include <netinet/if_inarp.h> -#endif - -#include <sys/bus.h> -#include <sys/intr.h> - #include <dev/ic/dm9000var.h> #include <dev/ic/dm9000reg.h> @@ -159,561 +156,424 @@ #define TX_DATA_DPRINTF(s) do {} while (/*CONSTCOND*/0) #endif -/*** Internal PHY functions ***/ -uint16_t dme_phy_read(struct dme_softc *, int ); -void dme_phy_write(struct dme_softc *, int, uint16_t); -void dme_phy_init(struct dme_softc *); -void dme_phy_reset(struct dme_softc *); -void dme_phy_update_media(struct dme_softc *); -void dme_phy_check_link(void *); - -/*** Methods registered in struct ifnet ***/ -void dme_start_output(struct ifnet *); -int dme_init(struct ifnet *); -int dme_ioctl(struct ifnet *, u_long, void *); -void dme_stop(struct ifnet *, int); - -int dme_mediachange(struct ifnet *); -void dme_mediastatus(struct ifnet *, struct ifmediareq *); - -/*** Internal methods ***/ - -/* Prepare data to be transmitted (i.e. dequeue and load it into the DM9000) */ -void dme_prepare(struct dme_softc *, struct ifnet *); - -/* Transmit prepared data */ -void dme_transmit(struct dme_softc *); - -/* Receive data */ -void dme_receive(struct dme_softc *, struct ifnet *); - -/* Software Initialize/Reset of the DM9000 */ -void dme_reset(struct dme_softc *); - -/* Configure multicast filter */ -void dme_set_addr_filter(struct dme_softc *); - -/* Set media */ -int dme_set_media(struct dme_softc *, int ); - -/* Read/write packet data from/to DM9000 IC in various transfer sizes */ -int dme_pkt_read_2(struct dme_softc *, struct ifnet *, struct mbuf **); -int dme_pkt_write_2(struct dme_softc *, struct mbuf *); -int dme_pkt_read_1(struct dme_softc *, struct ifnet *, struct mbuf **); -int dme_pkt_write_1(struct dme_softc *, struct mbuf *); -/* TODO: Implement 32 bit read/write functions */ - -uint16_t -dme_phy_read(struct dme_softc *sc, int reg) -{ - uint16_t val; - /* Select Register to read*/ - dme_write(sc, DM9000_EPAR, DM9000_EPAR_INT_PHY + - (reg & DM9000_EPAR_EROA_MASK)); - /* Select read operation (DM9000_EPCR_ERPRR) from the PHY */ - dme_write(sc, DM9000_EPCR, DM9000_EPCR_ERPRR + DM9000_EPCR_EPOS_PHY); - - /* Wait until access to PHY has completed */ - while (dme_read(sc, DM9000_EPCR) & DM9000_EPCR_ERRE) - ; - - /* Reset ERPRR-bit */ - dme_write(sc, DM9000_EPCR, DM9000_EPCR_EPOS_PHY); - - val = dme_read(sc, DM9000_EPDRL); - val += dme_read(sc, DM9000_EPDRH) << 8; - - return val; -} - -void -dme_phy_write(struct dme_softc *sc, int reg, uint16_t value) -{ - /* Select Register to write*/ - dme_write(sc, DM9000_EPAR, DM9000_EPAR_INT_PHY + - (reg & DM9000_EPAR_EROA_MASK)); - - /* Write data to the two data registers */ - dme_write(sc, DM9000_EPDRL, value & 0xFF); - dme_write(sc, DM9000_EPDRH, (value >> 8) & 0xFF); - - /* Select write operation (DM9000_EPCR_ERPRW) from the PHY */ - dme_write(sc, DM9000_EPCR, DM9000_EPCR_ERPRW + DM9000_EPCR_EPOS_PHY); - - /* Wait until access to PHY has completed */ - while (dme_read(sc, DM9000_EPCR) & DM9000_EPCR_ERRE) - ; - - /* Reset ERPRR-bit */ - dme_write(sc, DM9000_EPCR, DM9000_EPCR_EPOS_PHY); -} - -void -dme_phy_init(struct dme_softc *sc) -{ - u_int ifm_media = sc->sc_media.ifm_media; - uint32_t bmcr, anar; - - bmcr = dme_phy_read(sc, DM9000_PHY_BMCR); - anar = dme_phy_read(sc, DM9000_PHY_ANAR); - - anar = anar & ~DM9000_PHY_ANAR_10_HDX - & ~DM9000_PHY_ANAR_10_FDX - & ~DM9000_PHY_ANAR_TX_HDX - & ~DM9000_PHY_ANAR_TX_FDX; - - switch (IFM_SUBTYPE(ifm_media)) { - case IFM_AUTO: - bmcr |= DM9000_PHY_BMCR_AUTO_NEG_EN; - anar |= DM9000_PHY_ANAR_10_HDX | - DM9000_PHY_ANAR_10_FDX | - DM9000_PHY_ANAR_TX_HDX | - DM9000_PHY_ANAR_TX_FDX; - break; - case IFM_10_T: - //bmcr &= ~DM9000_PHY_BMCR_AUTO_NEG_EN; - bmcr &= ~DM9000_PHY_BMCR_SPEED_SELECT; - if (ifm_media & IFM_FDX) - anar |= DM9000_PHY_ANAR_10_FDX; - else - anar |= DM9000_PHY_ANAR_10_HDX; - break; - case IFM_100_TX: - //bmcr &= ~DM9000_PHY_BMCR_AUTO_NEG_EN; - bmcr |= DM9000_PHY_BMCR_SPEED_SELECT; - if (ifm_media & IFM_FDX) - anar |= DM9000_PHY_ANAR_TX_FDX; - else - anar |= DM9000_PHY_ANAR_TX_HDX; - - break; - } - - if (ifm_media & IFM_FDX) - bmcr |= DM9000_PHY_BMCR_DUPLEX_MODE; - else - bmcr &= ~DM9000_PHY_BMCR_DUPLEX_MODE; - - dme_phy_write(sc, DM9000_PHY_BMCR, bmcr); - dme_phy_write(sc, DM9000_PHY_ANAR, anar); -} - -void -dme_phy_reset(struct dme_softc *sc) -{ - uint32_t reg; - - /* PHY Reset */ - dme_phy_write(sc, DM9000_PHY_BMCR, DM9000_PHY_BMCR_RESET); - - reg = dme_read(sc, DM9000_GPCR); - dme_write(sc, DM9000_GPCR, reg & ~DM9000_GPCR_GPIO0_OUT); - reg = dme_read(sc, DM9000_GPR); - dme_write(sc, DM9000_GPR, reg | DM9000_GPR_PHY_PWROFF); - - dme_phy_init(sc); - - reg = dme_read(sc, DM9000_GPR); - dme_write(sc, DM9000_GPR, reg & ~DM9000_GPR_PHY_PWROFF); - reg = dme_read(sc, DM9000_GPCR); - dme_write(sc, DM9000_GPCR, reg | DM9000_GPCR_GPIO0_OUT); - - dme_phy_update_media(sc); -} - -void -dme_phy_update_media(struct dme_softc *sc) -{ - u_int ifm_media = sc->sc_media.ifm_media; - uint32_t reg; - - if (IFM_SUBTYPE(ifm_media) == IFM_AUTO) { - /* If auto-negotiation is used, ensures that it is completed - before trying to extract any media information. */ - reg = dme_phy_read(sc, DM9000_PHY_BMSR); - if ((reg & DM9000_PHY_BMSR_AUTO_NEG_AB) == 0) { - /* Auto-negotation not possible, therefore there is no - reason to try obtain any media information. */ - return; - } - - /* Then loop until the negotiation is completed. */ - while ((reg & DM9000_PHY_BMSR_AUTO_NEG_COM) == 0) { - /* TODO: Bail out after a finite number of attempts - in case something goes wrong. */ - preempt(); - reg = dme_phy_read(sc, DM9000_PHY_BMSR); - } - } - - - sc->sc_media_active = IFM_ETHER; - reg = dme_phy_read(sc, DM9000_PHY_BMCR); - - if (reg & DM9000_PHY_BMCR_SPEED_SELECT) - sc->sc_media_active |= IFM_100_TX; - else - sc->sc_media_active |= IFM_10_T; - - if (reg & DM9000_PHY_BMCR_DUPLEX_MODE) - sc->sc_media_active |= IFM_FDX; -} - -void -dme_phy_check_link(void *arg) -{ - struct dme_softc *sc = arg; - uint32_t reg; - int s; - - s = splnet(); - - reg = dme_read(sc, DM9000_NSR) & DM9000_NSR_LINKST; - - if (reg) - reg = IFM_ETHER | IFM_AVALID | IFM_ACTIVE; - else { - reg = IFM_ETHER | IFM_AVALID; - sc->sc_media_active = IFM_NONE; - } - - if ((sc->sc_media_status != reg) && (reg & IFM_ACTIVE)) - dme_phy_reset(sc); - - sc->sc_media_status = reg; - - callout_schedule(&sc->sc_link_callout, mstohz(2000)); - splx(s); -} - -int -dme_set_media(struct dme_softc *sc, int media) -{ - int s; - - s = splnet(); - sc->sc_media.ifm_media = media; - dme_phy_reset(sc); - - splx(s); - - return 0; -} +static void dme_reset(struct dme_softc *); +static int dme_init(struct ifnet *); +static void dme_stop(struct ifnet *, int); +static void dme_start(struct ifnet *); +static int dme_ioctl(struct ifnet *, u_long, void *); + +static void dme_set_rcvfilt(struct dme_softc *); +static void mii_statchg(struct ifnet *); +static void lnkchg(struct dme_softc *); +static void phy_tick(void *); +static int mii_readreg(device_t, int, int, uint16_t *); +static int mii_writereg(device_t, int, int, uint16_t); + +static void dme_prepare(struct ifnet *); +static void dme_transmit(struct ifnet *); +static void dme_receive(struct ifnet *); + +static int pkt_read_2(struct dme_softc *, struct mbuf **); +static int pkt_write_2(struct dme_softc *, struct mbuf *); +static int pkt_read_1(struct dme_softc *, struct mbuf **); +static int pkt_write_1(struct dme_softc *, struct mbuf *); +#define PKT_READ(ii,m) (*(ii)->sc_pkt_read)((ii),(m)) +#define PKT_WRITE(ii,m) (*(ii)->sc_pkt_write)((ii),(m)) + +#define ETHER_IS_ONE(x) \ + (((x)[0] & (x)[1] & (x)[2] & (x)[3] & (x)[4] & (x)[5]) == 255) +#define ETHER_IS_ZERO(x) \ + (((x)[0] | (x)[1] | (x)[2] | (x)[3] | (x)[4] | (x)[5]) == 0) int -dme_attach(struct dme_softc *sc, const uint8_t *enaddr) +dme_attach(struct dme_softc *sc, const uint8_t *notusedanymore) { - struct ifnet *ifp = &sc->sc_ethercom.ec_if; - uint8_t b[2]; - uint16_t io_mode; + struct ifnet *ifp = &sc->sc_ethercom.ec_if; + struct mii_data *mii = &sc->sc_mii; + struct ifmedia *ifm = &mii->mii_media; + uint8_t b[2]; + uint16_t io_mode; + uint8_t enaddr[ETHER_ADDR_LEN]; + prop_dictionary_t dict; + prop_data_t ea; dme_read_c(sc, DM9000_VID0, b, 2); -#if BYTE_ORDER == BIG_ENDIAN - sc->sc_vendor_id = (b[0] << 8) | b[1]; -#else - sc->sc_vendor_id = b[0] | (b[1] << 8); -#endif + sc->sc_vendor_id = le16toh((uint16_t)b[1] << 8 | b[0]); dme_read_c(sc, DM9000_PID0, b, 2); -#if BYTE_ORDER == BIG_ENDIAN - sc->sc_product_id = (b[0] << 8) | b[1]; -#else - sc->sc_product_id = b[0] | (b[1] << 8); -#endif + sc->sc_product_id = le16toh((uint16_t)b[1] << 8 | b[0]); + /* TODO: Check the vendor ID as well */ if (sc->sc_product_id != 0x9000) { panic("dme_attach: product id mismatch (0x%hx != 0x9000)", sc->sc_product_id); } +#if 1 || DM9000_DEBUG + { + dme_read_c(sc, DM9000_PAB0, enaddr, 6); + aprint_normal_dev(sc->sc_dev, + "DM9000 was configured with MAC address: %s\n", + ether_sprintf(enaddr)); + } +#endif + dict = device_properties(sc->sc_dev); + ea = (dict) ? prop_dictionary_get(dict, "mac-address") : NULL; + if (ea != NULL) { + /* + * If the MAC address is overriden by a device property, + * use that. + */ + KASSERT(prop_object_type(ea) == PROP_TYPE_DATA); + KASSERT(prop_data_size(ea) == ETHER_ADDR_LEN); + memcpy(enaddr, prop_data_data_nocopy(ea), ETHER_ADDR_LEN); + } else { + /* + * If we did not get an externaly configure address, + * try to read one from the current setup, before + * resetting the chip. + */ + dme_read_c(sc, DM9000_PAB0, enaddr, 6); + if (ETHER_IS_ONE(enaddr) || ETHER_IS_ZERO(enaddr)) { + /* make a random MAC address */ + uint32_t maclo = 0x00f2 | (cprng_strong32() << 16); + uint32_t machi = cprng_strong32(); + enaddr[0] = maclo; + enaddr[1] = maclo >> 8; + enaddr[2] = maclo >> 16; + enaddr[3] = maclo >> 26; + enaddr[4] = machi; + enaddr[5] = machi >> 8; + } + } + /* TODO: perform explicit EEPROM read op if it's availble */ + + dme_reset(sc); + + mii->mii_ifp = ifp; + mii->mii_readreg = mii_readreg; + mii->mii_writereg = mii_writereg; + mii->mii_statchg = mii_statchg; + + sc->sc_ethercom.ec_mii = mii; + ifmedia_init(ifm, 0, ether_mediachange, ether_mediastatus); + mii_attach(sc->sc_dev, mii, 0xffffffff, + 1 /* PHY 1 */, MII_OFFSET_ANY, 0); + if (LIST_FIRST(&mii->mii_phys) == NULL) { + ifmedia_add(ifm, IFM_ETHER | IFM_NONE, 0, NULL); + ifmedia_set(ifm, IFM_ETHER | IFM_NONE); + } else + ifmedia_set(ifm, IFM_ETHER | IFM_AUTO); + ifm->ifm_media = ifm->ifm_cur->ifm_media; - /* Initialize ifnet structure. */ strlcpy(ifp->if_xname, device_xname(sc->sc_dev), IFNAMSIZ); ifp->if_softc = sc; - ifp->if_start = dme_start_output; + ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; ifp->if_init = dme_init; - ifp->if_ioctl = dme_ioctl; + ifp->if_start = dme_start; ifp->if_stop = dme_stop; - ifp->if_watchdog = NULL; /* no watchdog at this stage */ - ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; + ifp->if_ioctl = dme_ioctl; + ifp->if_watchdog = NULL; /* no watchdog used */ + IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); IFQ_SET_READY(&ifp->if_snd); - /* Initialize ifmedia structures. */ - sc->sc_ethercom.ec_ifmedia = &sc->sc_media; - ifmedia_init(&sc->sc_media, 0, dme_mediachange, dme_mediastatus); - ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL); - ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_10_T | IFM_FDX, 0, NULL); - ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_10_T, 0, NULL); - ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_100_TX | IFM_FDX, 0, NULL); - ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_100_TX, 0, NULL); - - ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO); - - if (enaddr != NULL) - memcpy(sc->sc_enaddr, enaddr, sizeof(sc->sc_enaddr)); - /* TODO: Support an EEPROM attached to the DM9000 chip */ - - callout_init(&sc->sc_link_callout, 0); - callout_setfunc(&sc->sc_link_callout, dme_phy_check_link, sc); - - sc->sc_media_status = 0; - - /* Configure DM9000 with the MAC address */ - dme_write_c(sc, DM9000_PAB0, sc->sc_enaddr, 6); - -#ifdef DM9000_DEBUG - { - uint8_t macAddr[6]; - dme_read_c(sc, DM9000_PAB0, macAddr, 6); - printf("DM9000 configured with MAC address: "); - for (int i = 0; i < 6; i++) - printf("%02X:", macAddr[i]); - printf("\n"); - } -#endif - if_attach(ifp); - ether_ifattach(ifp, sc->sc_enaddr); + ether_ifattach(ifp, enaddr); + if_deferred_start_init(ifp, NULL); -#ifdef DM9000_DEBUG - { - uint8_t network_state; - network_state = dme_read(sc, DM9000_NSR); - printf("DM9000 Link status: "); - if (network_state & DM9000_NSR_LINKST) { - if (network_state & DM9000_NSR_SPEED) - printf("10Mbps"); - else - printf("100Mbps"); - } else - printf("Down"); - printf("\n"); - } -#endif + rnd_attach_source(&sc->rnd_source, device_xname(sc->sc_dev), + RND_TYPE_NET, RND_FLAG_DEFAULT); + + /* might be unnecessary as link change interrupt works well */ + callout_init(&sc->sc_link_callout, 0); + callout_setfunc(&sc->sc_link_callout, phy_tick, sc); io_mode = (dme_read(sc, DM9000_ISR) & DM9000_IOMODE_MASK) >> DM9000_IOMODE_SHIFT; DPRINTF(("DM9000 Operation Mode: ")); switch (io_mode) { + case DM9000_MODE_8BIT: + DPRINTF(("8-bit mode")); + sc->sc_data_width = 1; + sc->sc_pkt_write = pkt_write_1; + sc->sc_pkt_read = pkt_read_1; + break; case DM9000_MODE_16BIT: DPRINTF(("16-bit mode")); sc->sc_data_width = 2; - sc->sc_pkt_write = dme_pkt_write_2; - sc->sc_pkt_read = dme_pkt_read_2; + sc->sc_pkt_write = pkt_write_2; + sc->sc_pkt_read = pkt_read_2; break; case DM9000_MODE_32BIT: DPRINTF(("32-bit mode")); sc->sc_data_width = 4; panic("32bit mode is unsupported\n"); break; - case DM9000_MODE_8BIT: - DPRINTF(("8-bit mode")); - sc->sc_data_width = 1; - sc->sc_pkt_write = dme_pkt_write_1; - sc->sc_pkt_read = dme_pkt_read_1; - break; default: DPRINTF(("Invalid mode")); break; } DPRINTF(("\n")); - callout_schedule(&sc->sc_link_callout, mstohz(2000)); - return 0; } -int dme_intr(void *arg) +int +dme_detach(struct dme_softc *sc) { - struct dme_softc *sc = arg; - struct ifnet *ifp = &sc->sc_ethercom.ec_if; - uint8_t status; - - - DPRINTF(("dme_intr: Begin\n")); + return 0; +} - /* Disable interrupts */ - dme_write(sc, DM9000_IMR, DM9000_IMR_PAR ); +/* Software Initialize/Reset of the DM9000 */ +static void +dme_reset(struct dme_softc *sc) +{ + uint8_t misc; - status = dme_read(sc, DM9000_ISR); - dme_write(sc, DM9000_ISR, status); + /* We only re-initialized the PHY in this function the first time it is + * called. */ + if (!sc->sc_phy_initialized) { + /* PHY Reset */ + mii_writereg(sc->sc_dev, 1, MII_BMCR, BMCR_RESET); - if (status & DM9000_ISR_PRS) { - if (ifp->if_flags & IFF_RUNNING ) - dme_receive(sc, ifp); + /* PHY Power Down */ + misc = dme_read(sc, DM9000_GPR); + dme_write(sc, DM9000_GPR, misc | DM9000_GPR_PHY_PWROFF); } - if (status & DM9000_ISR_PTS) { - uint8_t nsr; - uint8_t tx_status = 0x01; /* Initialize to an error value */ - - /* A packet has been transmitted */ - sc->txbusy = 0; - - nsr = dme_read(sc, DM9000_NSR); - if (nsr & DM9000_NSR_TX1END) { - tx_status = dme_read(sc, DM9000_TSR1); - TX_DPRINTF(("dme_intr: Sent using channel 0\n")); - } else if (nsr & DM9000_NSR_TX2END) { - tx_status = dme_read(sc, DM9000_TSR2); - TX_DPRINTF(("dme_intr: Sent using channel 1\n")); - } + /* Reset the DM9000 twice, as described in section 2 of the Programming + * Guide. + * The PHY is initialized and enabled between those two resets. + */ - if (tx_status == 0x0) { - /* Frame successfully sent */ - if_statinc(ifp, if_opackets); - } else { - if_statinc(ifp, if_oerrors); - } + /* Software Reset */ + dme_write(sc, DM9000_NCR, + DM9000_NCR_RST | DM9000_NCR_LBK_MAC_INTERNAL); + delay(20); + dme_write(sc, DM9000_NCR, 0x0); - /* If we have nothing ready to transmit, prepare something */ - if (!sc->txready) - dme_prepare(sc, ifp); + if (!sc->sc_phy_initialized) { + /* PHY Enable */ + misc = dme_read(sc, DM9000_GPR); + dme_write(sc, DM9000_GPR, misc & ~DM9000_GPR_PHY_PWROFF); + misc = dme_read(sc, DM9000_GPCR); + dme_write(sc, DM9000_GPCR, misc | DM9000_GPCR_GPIO0_OUT); - if (sc->txready) - dme_transmit(sc); + dme_write(sc, DM9000_NCR, + DM9000_NCR_RST | DM9000_NCR_LBK_MAC_INTERNAL); + delay(20); + dme_write(sc, DM9000_NCR, 0x0); + } - /* Prepare the next frame */ - dme_prepare(sc, ifp); + /* Select internal PHY, no wakeup event, no collosion mode, + * normal loopback mode. + */ + dme_write(sc, DM9000_NCR, DM9000_NCR_LBK_NORMAL); - } -#ifdef notyet - if (status & DM9000_ISR_LNKCHNG) { - } -#endif + /* Will clear TX1END, TX2END, and WAKEST fields by reading DM9000_NSR*/ + dme_read(sc, DM9000_NSR); - /* Enable interrupts again */ + /* Enable wraparound of read/write pointer, frame received latch, + * and frame transmitted latch. + */ dme_write(sc, DM9000_IMR, DM9000_IMR_PAR | DM9000_IMR_PRM | DM9000_IMR_PTM); - DPRINTF(("dme_intr: End\n")); + dme_write(sc, DM9000_RCR, + DM9000_RCR_DIS_CRC | DM9000_RCR_DIS_LONG | DM9000_RCR_WTDIS); - return 1; + sc->sc_phy_initialized = 1; } -void -dme_start_output(struct ifnet *ifp) +static int +dme_init(struct ifnet *ifp) { - struct dme_softc *sc; - - sc = ifp->if_softc; + struct dme_softc *sc = ifp->if_softc; - DPRINTF(("dme_start_output: Begin\n")); + dme_stop(ifp, 0); + dme_reset(sc); + dme_write_c(sc, DM9000_PAB0, CLLADDR(ifp->if_sadl), ETHER_ADDR_LEN); + dme_set_rcvfilt(sc); + (void)ether_mediachange(ifp); + + sc->txbusy = sc->txready = 0; + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + callout_schedule(&sc->sc_link_callout, hz); - if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) { - printf("No output\n"); - return; - } + return 0; +} - if (sc->txbusy && sc->txready) - panic("DM9000: Internal error, trying to send without" - " any empty queue\n"); +/* Configure multicast filter */ +static void +dme_set_rcvfilt(struct dme_softc *sc) +{ + struct ethercom *ec = &sc->sc_ethercom; + struct ifnet *ifp = &ec->ec_if; + struct ether_multi *enm; + struct ether_multistep step; + uint8_t mchash[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; /* 64bit mchash */ + uint32_t h = 0; + int rcr; - dme_prepare(sc, ifp); + rcr = dme_read(sc, DM9000_RCR); + rcr &= ~(DM9000_RCR_PRMSC | DM9000_RCR_ALL); + dme_write(sc, DM9000_RCR, rcr &~ DM9000_RCR_RXEN); - if (sc->txbusy == 0) { - /* We are ready to transmit right away */ - dme_transmit(sc); - dme_prepare(sc, ifp); /* Prepare next one */ - } else { - /* We need to wait until the current packet has - * been transmitted. - */ - ifp->if_flags |= IFF_OACTIVE; + ETHER_LOCK(ec); + if (ifp->if_flags & IFF_PROMISC) { + ec->ec_flags |= ETHER_F_ALLMULTI; + ETHER_UNLOCK(ec); + /* run promisc. mode */ + rcr |= DM9000_RCR_PRMSC; + goto update; } - - DPRINTF(("dme_start_output: End\n")); + ec->ec_flags &= ~ETHER_F_ALLMULTI; + ETHER_FIRST_MULTI(step, ec, enm); + while (enm != NULL) { + if (memcpy(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) { + /* + * We must listen to a range of multicast addresses. + * For now, just accept all multicasts, rather than + * trying to set only those filter bits needed to match + * the range. (At this time, the only use of address + * ranges is for IP multicast routing, for which the + * range is big enough to require all bits set.) + */ + ec->ec_flags |= ETHER_F_ALLMULTI; + ETHER_UNLOCK(ec); + memset(mchash, 0xff, sizeof(mchash)); /* necessary? */ + /* accept all mulicast frame */ + rcr |= DM9000_RCR_ALL; + break; + } + h = ether_crc32_le(enm->enm_addrlo, ETHER_ADDR_LEN) & 0x3f; + /* 3(5:3) and 3(2:0) sampling to have uint8_t[8] */ + mchash[h / 8] |= 1 << (h % 8); + ETHER_NEXT_MULTI(step, enm); + } + ETHER_UNLOCK(ec); + /* DM9000 receive filter is always on */ + mchash[7] |= 0x80; /* to catch bcast frame */ + update: + dme_write_c(sc, DM9000_MAB0, mchash, sizeof(mchash)); + dme_write(sc, DM9000_RCR, rcr | DM9000_RCR_RXEN); + return; } void -dme_prepare(struct dme_softc *sc, struct ifnet *ifp) +lnkchg(struct dme_softc *sc) { - struct mbuf *bufChain; - uint16_t length; + struct ifnet *ifp = &sc->sc_ethercom.ec_if; + struct ifmediareq ifmr; - TX_DPRINTF(("dme_prepare: Entering\n")); + ether_mediastatus(ifp, &ifmr); +} - if (sc->txready) - panic("dme_prepare: Someone called us with txready set\n"); +static void +mii_statchg(struct ifnet *ifp) +{ + struct dme_softc *sc = ifp->if_softc; + struct mii_data *mii = &sc->sc_mii; + uint8_t nsr, fcr, ncr; + const uint8_t Mbps[2] = { 10, 100 }; + int spd; + + nsr = dme_read(sc, DM9000_NSR); + spd = Mbps[!!(nsr & DM9000_NSR_SPEED)]; + /* speed/duplexity available also in reg 0x11 of internal PHY */ +#if 1 + if (nsr & DM9000_NSR_LINKST) + printf("link up,spd%d", spd); + else + printf("link down"); - IFQ_DEQUEUE(&ifp->if_snd, bufChain); - if (bufChain == NULL) { - TX_DPRINTF(("dme_prepare: Nothing to transmit\n")); - ifp->if_flags &= ~IFF_OACTIVE; /* Clear OACTIVE bit */ - return; /* Nothing to transmit */ + /* show resolved mii(4) parameters */ + printf("MII spd%d", + (int)(sc->sc_ethercom.ec_if.if_baudrate / IF_Mbps(1))); + if (mii->mii_media_active & IFM_FDX) + printf(",full-duplex"); + printf("\n"); +#endif + + /* Adjust duplexity and PAUSE flow control. */ + fcr = dme_read(sc, DM9000_FCR) &~ DM9000_FCR_FLCE; + ncr = dme_read(sc, DM9000_NCR) &~ DM9000_NCR_FDX; + if ((mii->mii_media_active & IFM_FDX) + && (mii->mii_media_active & IFM_FLOW)) { + fcr |= DM9000_FCR_FLCE; + ncr |= DM9000_NCR_FDX; } + dme_write(sc, DM9000_FCR, fcr); + dme_write(sc, DM9000_NCR, ncr); +} - /* Element has now been removed from the queue, so we better send it */ +static void +phy_tick(void *arg) +{ + struct dme_softc *sc = arg; + struct mii_data *mii = &sc->sc_mii; + int s; - bpf_mtap(ifp, bufChain, BPF_D_OUT); + s = splnet(); + mii_tick(mii); + splx(s); - /* Setup the DM9000 to accept the writes, and then write each buf in - the chain. */ + callout_schedule(&sc->sc_link_callout, hz); +} - TX_DATA_DPRINTF(("dme_prepare: Writing data: ")); - bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->dme_io, DM9000_MWCMD); - length = sc->sc_pkt_write(sc, bufChain); - TX_DATA_DPRINTF(("\n")); +static int +mii_readreg(device_t self, int phy, int reg, uint16_t *val) +{ + struct dme_softc *sc = device_private(self); - if (length % sc->sc_data_width != 0) - panic("dme_prepare: length is not compatible with IO_MODE"); + if (phy != 1) + return EINVAL; - sc->txready_length = length; - sc->txready = 1; + /* Select Register to read*/ + dme_write(sc, DM9000_EPAR, DM9000_EPAR_INT_PHY + + (reg & DM9000_EPAR_EROA_MASK)); + /* Select read operation (DM9000_EPCR_ERPRR) from the PHY */ + dme_write(sc, DM9000_EPCR, DM9000_EPCR_ERPRR + DM9000_EPCR_EPOS_PHY); - TX_DPRINTF(("dme_prepare: txbusy: %d\ndme_prepare: " - "txready: %d, txready_length: %d\n", - sc->txbusy, sc->txready, sc->txready_length)); + /* Wait until access to PHY has completed */ + while (dme_read(sc, DM9000_EPCR) & DM9000_EPCR_ERRE) + ; - m_freem(bufChain); + /* Reset ERPRR-bit */ + dme_write(sc, DM9000_EPCR, DM9000_EPCR_EPOS_PHY); - TX_DPRINTF(("dme_prepare: Leaving\n")); + *val = dme_read(sc, DM9000_EPDRL) | (dme_read(sc, DM9000_EPDRH) << 8); + return 0; } -int -dme_init(struct ifnet *ifp) +static int +mii_writereg(device_t self, int phy, int reg, uint16_t val) { - int s; - struct dme_softc *sc = ifp->if_softc; - - dme_stop(ifp, 0); - - s = splnet(); - - dme_reset(sc); + struct dme_softc *sc = device_private(self); - sc->sc_ethercom.ec_if.if_flags |= IFF_RUNNING; - sc->sc_ethercom.ec_if.if_flags &= ~IFF_OACTIVE; - sc->sc_ethercom.ec_if.if_timer = 0; + if (phy != 1) + return EINVAL; - splx(s); + /* Select Register to write */ + dme_write(sc, DM9000_EPAR, DM9000_EPAR_INT_PHY + + (reg & DM9000_EPAR_EROA_MASK)); - return 0; -} + /* Write data to the two data registers */ + dme_write(sc, DM9000_EPDRL, val & 0xFF); + dme_write(sc, DM9000_EPDRH, (val >> 8) & 0xFF); -int -dme_ioctl(struct ifnet *ifp, u_long cmd, void *data) -{ - struct dme_softc *sc = ifp->if_softc; - int s, error = 0; + /* Select write operation (DM9000_EPCR_ERPRW) from the PHY */ + dme_write(sc, DM9000_EPCR, DM9000_EPCR_ERPRW + DM9000_EPCR_EPOS_PHY); - s = splnet(); + /* Wait until access to PHY has completed */ + while (dme_read(sc, DM9000_EPCR) & DM9000_EPCR_ERRE) + ; - switch (cmd) { - default: - error = ether_ioctl(ifp, cmd, data); - if (error == ENETRESET) { - if (ifp->if_flags && IFF_RUNNING) { - /* Address list has changed, reconfigure - filter */ - dme_set_addr_filter(sc); - } - error = 0; - } - break; - } + /* Reset ERPRR-bit */ + dme_write(sc, DM9000_EPCR, DM9000_EPCR_EPOS_PHY); - splx(s); - return error; + return 0; } void @@ -726,228 +586,300 @@ dme_stop(struct ifnet *ifp, int disable) /* Disable RX */ dme_write(sc, DM9000_RCR, 0x0); } + mii_down(&sc->sc_mii); + callout_stop(&sc->sc_link_callout); ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); ifp->if_timer = 0; } -int -dme_mediachange(struct ifnet *ifp) +static void +dme_start(struct ifnet *ifp) { struct dme_softc *sc = ifp->if_softc; - return dme_set_media(sc, sc->sc_media.ifm_cur->ifm_media); + if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) { + printf("No output\n"); + return; + } + if (sc->txbusy && sc->txready) + panic("DM9000: Internal error, trying to send without" + " any empty queue\n"); + + dme_prepare(ifp); + if (sc->txbusy) { + /* We need to wait until the current frame has + * been transmitted. + */ + ifp->if_flags |= IFF_OACTIVE; + return; + } + /* We are ready to transmit right away */ + dme_transmit(ifp); + dme_prepare(ifp); /* Prepare next one */ } -void -dme_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) +/* Prepare data to be transmitted (i.e. dequeue and load it into the DM9000) */ +static void +dme_prepare(struct ifnet *ifp) { struct dme_softc *sc = ifp->if_softc; + uint16_t length; + struct mbuf *m; + + if (sc->txready) + panic("dme_prepare: Someone called us with txready set\n"); + + IFQ_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) { + TX_DPRINTF(("dme_prepare: Nothing to transmit\n")); + ifp->if_flags &= ~IFF_OACTIVE; /* Clear OACTIVE bit */ + return; /* Nothing to transmit */ + } + + /* Element has now been removed from the queue, so we better send it */ + + bpf_mtap(ifp, m, BPF_D_OUT); + + /* Setup the DM9000 to accept the writes, and then write each buf in + the chain. */ + + TX_DATA_DPRINTF(("dme_prepare: Writing data: ")); + bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->dme_io, DM9000_MWCMD); + length = PKT_WRITE(sc, m); + bpf_mtap(ifp, m, BPF_D_OUT); + TX_DATA_DPRINTF(("\n")); + + if (length % sc->sc_data_width != 0) + panic("dme_prepare: length is not compatible with IO_MODE"); - ifmr->ifm_active = sc->sc_media_active; - ifmr->ifm_status = sc->sc_media_status; + sc->txready_length = length; + sc->txready = 1; + m_freem(m); } -void -dme_transmit(struct dme_softc *sc) +/* Transmit prepared data */ +static void +dme_transmit(struct ifnet *ifp) { + struct dme_softc *sc = ifp->if_softc; TX_DPRINTF(("dme_transmit: PRE: txready: %d, txbusy: %d\n", sc->txready, sc->txbusy)); + /* prime frame length first */ dme_write(sc, DM9000_TXPLL, sc->txready_length & 0xff); - dme_write(sc, DM9000_TXPLH, (sc->txready_length >> 8) & 0xff ); - - /* Request to send the packet */ + dme_write(sc, DM9000_TXPLH, (sc->txready_length >> 8) & 0xff); + /* read isr next */ dme_read(sc, DM9000_ISR); - + /* finally issue a request to send */ dme_write(sc, DM9000_TCR, DM9000_TCR_TXREQ); - sc->txready = 0; sc->txbusy = 1; sc->txready_length = 0; } -void -dme_receive(struct dme_softc *sc, struct ifnet *ifp) +/* Receive data */ +static void +dme_receive(struct ifnet *ifp) { - uint8_t ready = 0x01; + struct dme_softc *sc = ifp->if_softc; + struct mbuf *m; + uint8_t avail, rsr; DPRINTF(("inside dme_receive\n")); - while (ready == 0x01) { - /* Packet received, retrieve it */ - - /* Read without address increment to get the ready byte without + /* frame has just arrived, retrieve it */ + /* called right after Rx frame available interrupt */ + do { + /* "no increment" read to get the avail byte without moving past it. */ - bus_space_write_1(sc->sc_iot, sc->sc_ioh, - sc->dme_io, DM9000_MRCMDX); - /* Dummy ready */ - ready = bus_space_read_1(sc->sc_iot, sc->sc_ioh, sc->dme_data); - ready = bus_space_read_1(sc->sc_iot, sc->sc_ioh, sc->dme_data); - ready &= 0x03; /* we only want bits 1:0 */ - if (ready == 0x01) { - uint8_t rx_status; - struct mbuf *m; - + bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->dme_io, + DM9000_MRCMDX); + /* Read twice */ + avail = bus_space_read_1(sc->sc_iot, sc->sc_ioh, sc->dme_data); + avail = bus_space_read_1(sc->sc_iot, sc->sc_ioh, sc->dme_data); + avail &= 03; /* 1:0 we only want these bits */ + if (avail == 01) { /* Read with address increment. */ - bus_space_write_1(sc->sc_iot, sc->sc_ioh, - sc->dme_io, DM9000_MRCMD); - - rx_status = sc->sc_pkt_read(sc, ifp, &m); + bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->dme_io, + DM9000_MRCMD); + rsr = PKT_READ(sc, &m); if (m == NULL) { /* failed to allocate a receive buffer */ - if_statinc(ifp, if_ierrors); RX_DPRINTF(("dme_receive: " "Error allocating buffer\n")); - } else if (rx_status & (DM9000_RSR_CE | DM9000_RSR_PLE)) { - /* Error while receiving the packet, + if_statinc(ifp, if_ierrors); + continue; + } + if (rsr & (DM9000_RSR_CE | DM9000_RSR_PLE)) { + /* Error while receiving the frame, * discard it and keep track of counters */ - if_statinc(ifp, if_ierrors); RX_DPRINTF(("dme_receive: " - "Error reciving packet\n")); - } else if (rx_status & DM9000_RSR_LCS) { + "Error reciving frame\n")); + if_statinc(ifp, if_ierrors); + continue; + } + if (rsr & DM9000_RSR_LCS) { if_statinc(ifp, if_collisions); - } else { - if_percpuq_enqueue(ifp->if_percpuq, m); + continue; } - - } else if (ready != 0x00) { + /* pick and forward the this frame to ifq */ + if_percpuq_enqueue(ifp->if_percpuq, m); + } else if (avail != 00) { /* Should this be logged somehow? */ printf("%s: Resetting chip\n", device_xname(sc->sc_dev)); dme_reset(sc); } - } + } while (avail != 01); + /* frame receieved successfully */ } -void -dme_reset(struct dme_softc *sc) +int +dme_intr(void *arg) { - uint8_t var; + struct dme_softc *sc = arg; + struct ifnet *ifp = &sc->sc_ethercom.ec_if; + uint8_t isr, nsr, tsr; - /* We only re-initialized the PHY in this function the first time it is - called. */ - if (!sc->sc_phy_initialized) { - /* PHY Reset */ - dme_phy_write(sc, DM9000_PHY_BMCR, DM9000_PHY_BMCR_RESET); + DPRINTF(("dme_intr: Begin\n")); - /* PHY Power Down */ - var = dme_read(sc, DM9000_GPR); - dme_write(sc, DM9000_GPR, var | DM9000_GPR_PHY_PWROFF); + /* Disable interrupts */ + dme_write(sc, DM9000_IMR, DM9000_IMR_PAR); + + isr = dme_read(sc, DM9000_ISR); + dme_write(sc, DM9000_ISR, isr); /* write to clear */ + + if (isr & DM9000_ISR_PRS) { + KASSERT(ifp->if_flags & IFF_RUNNING); + dme_receive(ifp); } + if (isr & DM9000_ISR_LNKCHNG) + lnkchg(sc); + if (isr & DM9000_ISR_PTS) { + tsr = 0x01; /* Initialize to an error value */ - /* Reset the DM9000 twice, as described in section 2 of the Programming - Guide. - The PHY is initialized and enabled between those two resets. - */ + /* A frame has been transmitted */ + sc->txbusy = 0; - /* Software Reset*/ - dme_write(sc, DM9000_NCR, - DM9000_NCR_RST | DM9000_NCR_LBK_MAC_INTERNAL); - delay(20); - dme_write(sc, DM9000_NCR, 0x0); + nsr = dme_read(sc, DM9000_NSR); + if (nsr & DM9000_NSR_TX1END) { + tsr = dme_read(sc, DM9000_TSR1); + TX_DPRINTF(("dme_intr: Sent using channel 0\n")); + } else if (nsr & DM9000_NSR_TX2END) { + tsr = dme_read(sc, DM9000_TSR2); + TX_DPRINTF(("dme_intr: Sent using channel 1\n")); + } - if (!sc->sc_phy_initialized) { - /* PHY Initialization */ - dme_phy_init(sc); + if (tsr == 0x0) { + /* Frame successfully sent */ + if_statinc(ifp, if_opackets); + } else { + if_statinc(ifp, if_oerrors); + } - /* PHY Enable */ - var = dme_read(sc, DM9000_GPR); - dme_write(sc, DM9000_GPR, var & ~DM9000_GPR_PHY_PWROFF); - var = dme_read(sc, DM9000_GPCR); - dme_write(sc, DM9000_GPCR, var | DM9000_GPCR_GPIO0_OUT); + /* If we have nothing ready to transmit, prepare something */ + if (!sc->txready) + dme_prepare(ifp); - dme_write(sc, DM9000_NCR, - DM9000_NCR_RST | DM9000_NCR_LBK_MAC_INTERNAL); - delay(20); - dme_write(sc, DM9000_NCR, 0x0); - } + if (sc->txready) + dme_transmit(ifp); - /* Select internal PHY, no wakeup event, no collosion mode, - * normal loopback mode. - */ - dme_write(sc, DM9000_NCR, DM9000_NCR_LBK_NORMAL ); + /* Prepare the next frame */ + dme_prepare(ifp); - /* Will clear TX1END, TX2END, and WAKEST fields by reading DM9000_NSR*/ - dme_read(sc, DM9000_NSR); + if_schedule_deferred_start(ifp); + } - /* Enable wraparound of read/write pointer, packet received latch, - * and packet transmitted latch. - */ + /* Enable interrupts again */ dme_write(sc, DM9000_IMR, DM9000_IMR_PAR | DM9000_IMR_PRM | DM9000_IMR_PTM); - /* Setup multicast address filter, or run promisc. mode. */ - dme_set_addr_filter(sc); + DPRINTF(("dme_intr: End\n")); - /* Obtain media information from PHY */ - dme_phy_update_media(sc); + return (isr != 0); +} - sc->txbusy = 0; - sc->txready = 0; - sc->sc_phy_initialized = 1; +static int +dme_ioctl(struct ifnet *ifp, u_long cmd, void *data) +{ + struct dme_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *)data; + struct ifmedia *ifm = &sc->sc_mii.mii_media; + int s, error; + + s = splnet(); + switch (cmd) { + case SIOCSIFMEDIA: + /* Flow control requires full-duplex mode. */ + if (IFM_SUBTYPE(ifr->ifr_media) == IFM_AUTO || + (ifr->ifr_media & IFM_FDX) == 0) + ifr->ifr_media &= ~IFM_ETH_FMASK; + if (IFM_SUBTYPE(ifr->ifr_media) != IFM_AUTO) { + if ((ifr->ifr_media & IFM_ETH_FMASK) == IFM_FLOW) { + ifr->ifr_media |= + IFM_ETH_TXPAUSE | IFM_ETH_RXPAUSE; + } + } + error = ifmedia_ioctl(ifp, ifr, ifm, cmd); + break; + default: + if ((error = ether_ioctl(ifp, cmd, data)) != ENETRESET) + break; + error = 0; + if (cmd == SIOCSIFCAP) + error = (*ifp->if_init)(ifp); + else if (cmd != SIOCADDMULTI && cmd != SIOCDELMULTI) + ; + else if (ifp->if_flags && IFF_RUNNING) { + /* Address list has changed, reconfigure filter */ + dme_set_rcvfilt(sc); + } + break; + } + splx(s); + return error; } -void -dme_set_addr_filter(struct dme_softc *sc) +static struct mbuf * +dme_alloc_receive_buffer(struct ifnet *ifp, unsigned int frame_length) { - struct ethercom *ec = &sc->sc_ethercom; - struct ifnet *ifp = &ec->ec_if; - struct ether_multi *enm; - struct ether_multistep step; - uint8_t mchash[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; /* 64bit mchash */ - uint32_t h = 0; - uint8_t rcr; + struct dme_softc *sc = ifp->if_softc; + struct mbuf *m; + int pad, quantum; - rcr = dme_read(sc, DM9000_RCR); - rcr &= ~(DM9000_RCR_PRMSC | DM9000_RCR_ALL); - dme_write(sc, DM9000_RCR, rcr &~ DM9000_RCR_RXEN); + quantum = sc->sc_data_width; + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return NULL; - ETHER_LOCK(ec); - if (ifp->if_flags & IFF_PROMISC) { - ec->ec_flags |= ETHER_F_ALLMULTI; - ETHER_UNLOCK(ec); - /* run promisc. mode */ - rcr |= DM9000_RCR_PRMSC; - goto update; - } - ec->ec_flags &= ~ETHER_F_ALLMULTI; - ETHER_FIRST_MULTI(step, ec, enm); - while (enm != NULL) { - if (memcpy(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) { - /* - * We must listen to a range of multicast addresses. - * For now, just accept all multicasts, rather than - * trying to set only those filter bits needed to match - * the range. (At this time, the only use of address - * ranges is for IP multicast routing, for which the - * range is big enough to require all bits set.) - */ - ec->ec_flags |= ETHER_F_ALLMULTI; - ETHER_UNLOCK(ec); - memset(mchash, 0xff, sizeof(mchash)); /* necessary? */ - /* accept all mulicast frame */ - rcr |= DM9000_RCR_ALL; - break; + m_set_rcvif(m, ifp); + /* Ensure that we always allocate an even number of + * bytes in order to avoid writing beyond the buffer + */ + m->m_pkthdr.len = frame_length + (frame_length % quantum); + pad = ALIGN(sizeof(struct ether_header)) - + sizeof(struct ether_header); + /* All our frames have the CRC attached */ + m->m_flags |= M_HASFCS; + if (m->m_pkthdr.len + pad > MHLEN) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_freem(m); + return NULL; } - h = ether_crc32_le(enm->enm_addrlo, ETHER_ADDR_LEN) & 0x3f; - /* 3(5:3) and 3(2:0) sampling to make uint8_t[8] */ - mchash[h / 8] |= 1 << (h % 8); - ETHER_NEXT_MULTI(step, enm); } - ETHER_UNLOCK(ec); - /* DM9000 receive filter is always on */ - mchash[7] |= 0x80; /* to catch bcast frame */ - update: - dme_write_c(sc, DM9000_MAB0, mchash, sizeof(mchash)); - dme_write(sc, DM9000_RCR, rcr | DM9000_RCR_RXEN | DM9000_RCR_WTDIS); - return; + + m->m_data += pad; + m->m_len = frame_length + (frame_length % quantum); + + return m; } -int -dme_pkt_write_2(struct dme_softc *sc, struct mbuf *bufChain) +static int +pkt_write_2(struct dme_softc *sc, struct mbuf *bufChain) { int left_over_count = 0; /* Number of bytes from previous mbuf, which need to be written with the next.*/ @@ -969,7 +901,7 @@ dme_pkt_write_2(struct dme_softc *sc, st (buf->m_next == NULL && left_over_count > 0)) { if (left_over_count > 0) { uint8_t b = 0; - DPRINTF(("dme_pkt_write_16: " + DPRINTF(("pkt_write_16: " "Writing left over byte\n")); if (to_write > 0) { @@ -992,7 +924,7 @@ dme_pkt_write_2(struct dme_softc *sc, st left_over_count = 0; } else if ((long)write_ptr % 2 != 0) { /* Misaligned data */ - DPRINTF(("dme_pkt_write_16: " + DPRINTF(("pkt_write_16: " "Detected misaligned data\n")); left_over_buf = *write_ptr; left_over_count = 1; @@ -1016,7 +948,7 @@ dme_pkt_write_2(struct dme_softc *sc, st write_ptr += i * 2; if (to_write % 2 != 0) { - DPRINTF(("dme_pkt_write_16: " + DPRINTF(("pkt_write_16: " "to_write %% 2: %d\n", to_write % 2)); left_over_count = 1; @@ -1027,10 +959,10 @@ dme_pkt_write_2(struct dme_softc *sc, st write_ptr++; to_write--; - DPRINTF(("dme_pkt_write_16: " + DPRINTF(("pkt_write_16: " "to_write (after): %d\n", to_write)); - DPRINTF(("dme_pkt_write_16: i * 2: %d\n", + DPRINTF(("pkt_write_16: i * 2: %d\n", i*2)); } to_write -= i * 2; @@ -1041,9 +973,10 @@ dme_pkt_write_2(struct dme_softc *sc, st return length; } -int -dme_pkt_read_2(struct dme_softc *sc, struct ifnet *ifp, struct mbuf **outBuf) +static int +pkt_read_2(struct dme_softc *sc, struct mbuf **outBuf) { + struct ifnet *ifp = &sc->sc_ethercom.ec_if; uint8_t rx_status; struct mbuf *m; uint16_t data; @@ -1052,8 +985,8 @@ dme_pkt_read_2(struct dme_softc *sc, str uint16_t *buf; data = bus_space_read_2(sc->sc_iot, sc->sc_ioh, sc->dme_data); - rx_status = data & 0xFF; + frame_length = bus_space_read_2(sc->sc_iot, sc->sc_ioh, sc->dme_data); if (frame_length > ETHER_MAX_LEN) { @@ -1064,14 +997,13 @@ dme_pkt_read_2(struct dme_softc *sc, str RX_DPRINTF(("dme_receive: rx_statux: 0x%x, frame_length: %d\n", rx_status, frame_length)); - m = dme_alloc_receive_buffer(ifp, frame_length); if (m == NULL) { /* * didn't get a receive buffer, so we read the rest of the - * packet, throw it away and return an error + * frame, throw it away and return an error */ - for (i = 0; i < frame_length; i += 2 ) { + for (i = 0; i < frame_length; i += 2) { data = bus_space_read_2(sc->sc_iot, sc->sc_ioh, sc->dme_data); } @@ -1083,7 +1015,7 @@ dme_pkt_read_2(struct dme_softc *sc, str RX_DPRINTF(("dme_receive: ")); - for (i = 0; i < frame_length; i += 2 ) { + for (i = 0; i < frame_length; i += 2) { data = bus_space_read_2(sc->sc_iot, sc->sc_ioh, sc->dme_data); if ( (frame_length % 2 != 0) && @@ -1104,8 +1036,8 @@ dme_pkt_read_2(struct dme_softc *sc, str return rx_status; } -int -dme_pkt_write_1(struct dme_softc *sc, struct mbuf *bufChain) +static int +pkt_write_1(struct dme_softc *sc, struct mbuf *bufChain) { int length = 0, i; struct mbuf *buf; @@ -1132,9 +1064,10 @@ dme_pkt_write_1(struct dme_softc *sc, st return length; } -int -dme_pkt_read_1(struct dme_softc *sc, struct ifnet *ifp, struct mbuf **outBuf) +static int +pkt_read_1(struct dme_softc *sc, struct mbuf **outBuf) { + struct ifnet *ifp = &sc->sc_ethercom.ec_if; uint8_t rx_status; struct mbuf *m; uint8_t *buf; @@ -1159,12 +1092,11 @@ dme_pkt_read_1(struct dme_softc *sc, str "rx_statux: 0x%x, frame_length: %d\n", rx_status, frame_length)); - m = dme_alloc_receive_buffer(ifp, frame_length); if (m == NULL) { /* * didn't get a receive buffer, so we read the rest of the - * packet, throw it away and return an error + * frame, throw it away and return an error */ for (i = 0; i < frame_length; i++ ) { data = bus_space_read_2(sc->sc_iot, @@ -1177,8 +1109,7 @@ dme_pkt_read_1(struct dme_softc *sc, str buf = mtod(m, uint8_t *); RX_DPRINTF(("dme_receive: ")); - - for (i = 0; i< frame_length; i += 1 ) { + for (i = 0; i< frame_length; i += 1) { data = bus_space_read_1(sc->sc_iot, sc->sc_ioh, sc->dme_data); *buf = data; buf++; @@ -1191,36 +1122,3 @@ dme_pkt_read_1(struct dme_softc *sc, str *outBuf = m; return rx_status; } - -struct mbuf* -dme_alloc_receive_buffer(struct ifnet *ifp, unsigned int frame_length) -{ - struct dme_softc *sc = ifp->if_softc; - struct mbuf *m; - int pad; - - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (m == NULL) return NULL; - - m_set_rcvif(m, ifp); - /* Ensure that we always allocate an even number of - * bytes in order to avoid writing beyond the buffer - */ - m->m_pkthdr.len = frame_length + (frame_length % sc->sc_data_width); - pad = ALIGN(sizeof(struct ether_header)) - - sizeof(struct ether_header); - /* All our frames have the CRC attached */ - m->m_flags |= M_HASFCS; - if (m->m_pkthdr.len + pad > MHLEN) { - MCLGET(m, M_DONTWAIT); - if ((m->m_flags & M_EXT) == 0) { - m_freem(m); - return NULL; - } - } - - m->m_data += pad; - m->m_len = frame_length + (frame_length % sc->sc_data_width); - - return m; -} Index: src/sys/dev/ic/dm9000reg.h diff -u src/sys/dev/ic/dm9000reg.h:1.3 src/sys/dev/ic/dm9000reg.h:1.4 --- src/sys/dev/ic/dm9000reg.h:1.3 Mon Mar 30 00:01:57 2020 +++ src/sys/dev/ic/dm9000reg.h Tue Mar 31 02:32:25 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: dm9000reg.h,v 1.3 2020/03/30 00:01:57 nisimura Exp $ */ +/* $NetBSD: dm9000reg.h,v 1.4 2020/03/31 02:32:25 nisimura Exp $ */ /* * Copyright (c) 2009 Paul Fleischer @@ -26,17 +26,26 @@ * SUCH DAMAGE. */ -/* Registers accesible on the DM9000, extracted from pp. 11-12 from the data sheet */ +#ifndef _DEV_IC_DM9000REG_H_ +#define _DEV_IC_DM9000REG_H_ -/* There are two interesting addresses for the DM9000 - * (at least in the context of the FriendlyARM MINI2440) - * The I/O or register select address, which is the base address. - * The DATA address, which is located at offset 4 from the base address. +/* + * Registers accesible on the DM9000, extracted from pp. 11-12 from + * the data sheet + */ + +/* + * There are two interesting addresses for the DM9000 (at least in + * the context of the FriendlyARM MINI2440) The I/O or register select + * address, which is the base address. The DATA address, which is + * located at offset 4 from the base address. * - * Chances are that this will not work generally, as it really depends on how the address lines are - * mapped from the CPU to the DM9000. But for now it is a good starting point. + * Chances are that this will not work generally, as it really depends + * on how the address lines are mapped from the CPU to the DM9000. + * But for now it is a good starting point. */ -#define DM9000_IOSIZE 4 /* XXX: Depends on the wiring of the address lines. */ + +#define DM9000_IOSIZE 4 #define DM9000_NCR 0x00 #define DM9000_NCR_RST (1<<0) /* reset chip, self clear */ @@ -168,38 +177,4 @@ #define DM9000_IMR_ROOM (1<<3) #define DM9000_IMR_PAR (1<<7) /* use 3/13K SRAM w/ auto wrap */ -#define DM9000_PHY_BMCR 0x00 -#define DM9000_PHY_BMCR_COLL_TEST (1<<7) -#define DM9000_PHY_BMCR_DUPLEX_MODE (1<<8) -#define DM9000_PHY_BMCR_RESTART_AN (1<<9) -#define DM9000_PHY_BMCR_ISOLATE (1<<10) -#define DM9000_PHY_BMCR_POWER_DOWN (1<<11) -#define DM9000_PHY_BMCR_AUTO_NEG_EN (1<<12) -#define DM9000_PHY_BMCR_SPEED_SELECT (1<<13) -#define DM9000_PHY_BMCR_LOOPBACK (1<<14) -#define DM9000_PHY_BMCR_RESET (1<<15) -#define DM9000_PHY_BMSR 0x01 -#define DM9000_PHY_BMSR_EXT_CAP (1<<0) -#define DM9000_PHY_BMSR_JAB_DET (1<<1) -#define DM9000_PHY_BMSR_LINK_ST (1<<2) -#define DM9000_PHY_BMSR_AUTO_NEG_AB (1<<3) -#define DM9000_PHY_BMSR_REMOTE_FAULT (1<<4) -#define DM9000_PHY_BMSR_AUTO_NEG_COM (1<<5) -#define DM9000_PHY_BMSR_MF_PRE_SUP (1<<6) -#define DM9000_PHY_BMSR_10BASE_HALF_DUPLEX (1<<11) -#define DM9000_PHY_BMSR_10BASE_FULL_DUPLEX (1<<12) -#define DM9000_PHY_BMSR_100BASE_HALF_DUPLEX (1<<13) -#define DM9000_PHY_BMSR_100BASE_FULL_DUPLEX (1<<14) -#define DM9000_PHY_BMSR_100BASE_T4 (1<<15) -#define DM9000_PHY_PHYID1 0x02 -#define DM9000_PHY_PHYID2 0x03 -#define DM9000_PHY_ANAR 0x04 -#define DM9000_PHY_ANAR_10_HDX (1<<5) -#define DM9000_PHY_ANAR_10_FDX (1<<6) -#define DM9000_PHY_ANAR_TX_HDX (1<<7) -#define DM9000_PHY_ANAR_TX_FDX (1<<8) -#define DM9000_PHY_ANLPAR 0x05 -#define DM9000_PHY_ANER 0x06 -#define DM9000_PHY_DSCR 0x16 /* Davicom extention */ -#define DM9000_PHY_DSCSR 0x17 /* Davicom extention */ -#define DM9000_PHY_10BTCSR 0x18 /* Davicom extention */ +#endif Index: src/sys/dev/ic/dm9000var.h diff -u src/sys/dev/ic/dm9000var.h:1.5 src/sys/dev/ic/dm9000var.h:1.6 --- src/sys/dev/ic/dm9000var.h:1.5 Thu Mar 5 15:18:55 2020 +++ src/sys/dev/ic/dm9000var.h Tue Mar 31 02:32:25 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: dm9000var.h,v 1.5 2020/03/05 15:18:55 riastradh Exp $ */ +/* $NetBSD: dm9000var.h,v 1.6 2020/03/31 02:32:25 nisimura Exp $ */ /* * Copyright (c) 2009 Paul Fleischer @@ -64,6 +64,7 @@ #define _DEV_IC_DM9000VAR_H_ #include <sys/callout.h> +#include <sys/rndsource.h> #define DM9000_MODE_8BIT 2 #define DM9000_MODE_16BIT 0 @@ -71,13 +72,8 @@ struct dme_softc { device_t sc_dev; /* Generic Base Device */ - struct ethercom sc_ethercom; /* Ethernet common data */ - struct ifmedia sc_media; /* Media control structures */ - - uint sc_media_active; - uint sc_media_status; - + struct mii_data sc_mii; /* MII/media information */ bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; void *sc_ih; @@ -97,8 +93,8 @@ struct dme_softc { for transmission. */ uint16_t txready_length; - int (*sc_pkt_write)(struct dme_softc*, struct mbuf *); - int (*sc_pkt_read)(struct dme_softc*, struct ifnet *, struct mbuf **); + int (*sc_pkt_write)(struct dme_softc *, struct mbuf *); + int (*sc_pkt_read)(struct dme_softc *, struct mbuf **); callout_t sc_link_callout; @@ -107,6 +103,7 @@ struct dme_softc { #ifdef DIAGNOSTIC bool sc_inside_interrupt; #endif + krndsource_t rnd_source; }; /* Function declarations */ @@ -114,9 +111,6 @@ int dme_attach(struct dme_softc *, const int dme_detach(struct dme_softc *); int dme_intr(void *); -/* Helper method used by sc_pkt_read */ -struct mbuf* dme_alloc_receive_buffer(struct ifnet *, unsigned int); - /* Inline memory access methods */ static __inline uint8_t dme_read(struct dme_softc *sc, int reg) @@ -140,7 +134,7 @@ dme_write2(struct dme_softc *sc, int reg } static __inline void -dme_write_c(struct dme_softc *sc, int reg, uint8_t value[], uint count) +dme_write_c(struct dme_softc *sc, int reg, const uint8_t value[], uint count) { for(int i=0; i<count; i++) { dme_write(sc, reg+i, value[i]); @@ -156,4 +150,3 @@ dme_read_c(struct dme_softc *sc, int reg } #endif /* _DEV_IC_DM9000VAR_H_ */ -