Module Name: src Committed By: thorpej Date: Sun Mar 8 02:44:12 UTC 2020
Modified Files: src/sys/dev/pci: if_sip.c if_sipreg.h Log Message: Support 64-bit DMA addressing on the DP83820, used only when a 64-bit DMA tag is available. To generate a diff of this commit: cvs rdiff -u -r1.178 -r1.179 src/sys/dev/pci/if_sip.c cvs rdiff -u -r1.20 -r1.21 src/sys/dev/pci/if_sipreg.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/pci/if_sip.c diff -u src/sys/dev/pci/if_sip.c:1.178 src/sys/dev/pci/if_sip.c:1.179 --- src/sys/dev/pci/if_sip.c:1.178 Fri Feb 7 00:04:28 2020 +++ src/sys/dev/pci/if_sip.c Sun Mar 8 02:44:12 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: if_sip.c,v 1.178 2020/02/07 00:04:28 thorpej Exp $ */ +/* $NetBSD: if_sip.c,v 1.179 2020/03/08 02:44:12 thorpej Exp $ */ /*- * Copyright (c) 2001, 2002 The NetBSD Foundation, Inc. @@ -73,7 +73,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: if_sip.c,v 1.178 2020/02/07 00:04:28 thorpej Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_sip.c,v 1.179 2020/03/08 02:44:12 thorpej Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -208,9 +208,14 @@ struct sip_softc { struct ethercom sc_ethercom; /* ethernet common data */ const struct sip_product *sc_model; /* which model are we? */ - int sc_gigabit; /* 1: 83820, 0: other */ + bool sc_gigabit; /* 1: 83820, 0: other */ + bool sc_dma64; /* using 64-bit DMA addresses */ int sc_rev; /* chip revision */ + unsigned int sc_bufptr_idx; + unsigned int sc_cmdsts_idx; + unsigned int sc_extsts_idx; /* DP83820 only */ + void *sc_ih; /* interrupt cookie */ struct mii_data sc_mii; /* MII/media information */ @@ -471,6 +476,24 @@ sip_rxchain_link(struct sip_softc *sc, s #define SIP_CDRXADDR(sc, x) ((sc)->sc_cddma + SIP_CDRXOFF((x))) static inline void +sip_set_rxdp(struct sip_softc *sc, bus_addr_t addr) +{ + if (sc->sc_gigabit) + bus_space_write_4(sc->sc_st, sc->sc_sh, SIP_RXDP_HI, + BUS_ADDR_HI32(addr)); + bus_space_write_4(sc->sc_st, sc->sc_sh, SIP_RXDP, BUS_ADDR_LO32(addr)); +} + +static inline void +sip_set_txdp(struct sip_softc *sc, bus_addr_t addr) +{ + if (sc->sc_gigabit) + bus_space_write_4(sc->sc_st, sc->sc_sh, SIP_TXDP_HI, + BUS_ADDR_HI32(addr)); + bus_space_write_4(sc->sc_st, sc->sc_sh, SIP_TXDP, BUS_ADDR_LO32(addr)); +} + +static inline void sip_cdtxsync(struct sip_softc *sc, const int x0, const int n0, const int ops) { int x, n; @@ -499,26 +522,51 @@ sip_cdrxsync(struct sip_softc *sc, int x SIP_CDRXOFF(x), sizeof(struct sip_desc), ops); } -#if 0 -#ifdef DP83820 - uint32_t sipd_bufptr; /* pointer to DMA segment */ - uint32_t sipd_cmdsts; /* command/status word */ -#else - uint32_t sipd_cmdsts; /* command/status word */ - uint32_t sipd_bufptr; /* pointer to DMA segment */ -#endif /* DP83820 */ -#endif /* 0 */ - -static inline volatile uint32_t * -sipd_cmdsts(struct sip_softc *sc, struct sip_desc *sipd) +static void +sip_init_txring(struct sip_softc *sc) { - return &sipd->sipd_cbs[(sc->sc_gigabit) ? 1 : 0]; + struct sip_desc *sipd; + bus_addr_t next_desc; + int i; + + memset(sc->sc_txdescs, 0, sizeof(sc->sc_txdescs)); + for (i = 0; i < sc->sc_ntxdesc; i++) { + sipd = &sc->sc_txdescs[i]; + next_desc = SIP_CDTXADDR(sc, sip_nexttx(sc, i)); + if (sc->sc_dma64) { + sipd->sipd_words[GSIP64_DESC_LINK_LO] = + htole32(BUS_ADDR_LO32(next_desc)); + sipd->sipd_words[GSIP64_DESC_LINK_HI] = + htole32(BUS_ADDR_HI32(next_desc)); + } else { + /* SIP_DESC_LINK == GSIP_DESC_LINK */ + sipd->sipd_words[SIP_DESC_LINK] = htole32(next_desc); + } + } + sip_cdtxsync(sc, 0, sc->sc_ntxdesc, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + sc->sc_txfree = sc->sc_ntxdesc; + sc->sc_txnext = 0; + sc->sc_txwin = 0; } -static inline volatile uint32_t * -sipd_bufptr(struct sip_softc *sc, struct sip_desc *sipd) +static inline void +sip_init_txdesc(struct sip_softc *sc, int x, bus_addr_t bufptr, uint32_t cmdsts) { - return &sipd->sipd_cbs[(sc->sc_gigabit) ? 0 : 1]; + struct sip_desc *sipd = &sc->sc_txdescs[x]; + + if (sc->sc_dma64) { + sipd->sipd_words[GSIP64_DESC_BUFPTR_LO] = + htole32(BUS_ADDR_LO32(bufptr)); + sipd->sipd_words[GSIP64_DESC_BUFPTR_HI] = + htole32(BUS_ADDR_HI32(bufptr)); + } else { + sipd->sipd_words[sc->sc_bufptr_idx] = htole32(bufptr); + } + sipd->sipd_words[sc->sc_extsts_idx] = 0; + membar_producer(); + sipd->sipd_words[sc->sc_cmdsts_idx] = htole32(cmdsts); + /* sip_cdtxsync() will be done later. */ } static inline void @@ -526,12 +574,27 @@ sip_init_rxdesc(struct sip_softc *sc, in { struct sip_rxsoft *rxs = &sc->sc_rxsoft[x]; struct sip_desc *sipd = &sc->sc_rxdescs[x]; + const bus_addr_t next_desc = SIP_CDRXADDR(sc, sip_nextrx(sc, x)); - sipd->sipd_link = htole32(SIP_CDRXADDR(sc, sip_nextrx(sc, x))); - *sipd_bufptr(sc, sipd) = htole32(rxs->rxs_dmamap->dm_segs[0].ds_addr); - *sipd_cmdsts(sc, sipd) = htole32(CMDSTS_INTR | - (sc->sc_parm->p_rxbuf_len & sc->sc_bits.b_cmdsts_size_mask)); - sipd->sipd_extsts = 0; + if (sc->sc_dma64) { + sipd->sipd_words[GSIP64_DESC_LINK_LO] = + htole32(BUS_ADDR_LO32(next_desc)); + sipd->sipd_words[GSIP64_DESC_LINK_HI] = + htole32(BUS_ADDR_HI32(next_desc)); + sipd->sipd_words[GSIP64_DESC_BUFPTR_LO] = + htole32(BUS_ADDR_LO32(rxs->rxs_dmamap->dm_segs[0].ds_addr)); + sipd->sipd_words[GSIP64_DESC_BUFPTR_HI] = + htole32(BUS_ADDR_HI32(rxs->rxs_dmamap->dm_segs[0].ds_addr)); + } else { + sipd->sipd_words[SIP_DESC_LINK] = htole32(next_desc); + sipd->sipd_words[sc->sc_bufptr_idx] = + htole32(rxs->rxs_dmamap->dm_segs[0].ds_addr); + } + sipd->sipd_words[sc->sc_extsts_idx] = 0; + membar_producer(); + sipd->sipd_words[sc->sc_cmdsts_idx] = + htole32(CMDSTS_INTR | (sc->sc_parm->p_rxbuf_len & + sc->sc_bits.b_cmdsts_size_mask)); sip_cdrxsync(sc, x, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); } @@ -670,25 +733,26 @@ static const struct sip_product { pci_product_id_t sip_product; const char *sip_name; const struct sip_variant *sip_variant; - int sip_gigabit; + bool sip_gigabit; } sipcom_products[] = { { PCI_VENDOR_NS, PCI_PRODUCT_NS_DP83820, "NatSemi DP83820 Gigabit Ethernet", - &sipcom_variant_dp83820, 1 }, + &sipcom_variant_dp83820, true }, + { PCI_VENDOR_SIS, PCI_PRODUCT_SIS_900, "SiS 900 10/100 Ethernet", - &sipcom_variant_sis900, 0 }, + &sipcom_variant_sis900, false }, { PCI_VENDOR_SIS, PCI_PRODUCT_SIS_7016, "SiS 7016 10/100 Ethernet", - &sipcom_variant_sis900, 0 }, + &sipcom_variant_sis900, false }, { PCI_VENDOR_NS, PCI_PRODUCT_NS_DP83815, "NatSemi DP83815 10/100 Ethernet", - &sipcom_variant_dp83815, 0 }, + &sipcom_variant_dp83815, false }, { 0, 0, NULL, - NULL, 0 }, + NULL, false }, }; static const struct sip_product * @@ -812,24 +876,30 @@ sipcom_dp83820_attach(struct sip_softc * } printf("\n"); } - + /* - * XXX Need some PCI flags indicating support for - * XXX 64-bit addressing. + * The T64ADDR bit is loaded by the chip from the EEPROM and + * is read-only. */ -#if 0 - if (reg & CFG_M64ADDR) - sc->sc_cfg |= CFG_M64ADDR; if (reg & CFG_T64ADDR) sc->sc_cfg |= CFG_T64ADDR; -#endif + + /* + * We can use 64-bit DMA addressing regardless of what + * sort of slot we're in. + */ + if (pci_dma64_available(pa)) { + sc->sc_dmat = pa->pa_dmat64; + sc->sc_cfg |= CFG_M64ADDR; + sc->sc_dma64 = true; + } if (reg & (CFG_TBI_EN | CFG_EXT_125)) { const char *sep = ""; printf("%s: using ", device_xname(sc->sc_dev)); if (reg & CFG_EXT_125) { sc->sc_cfg |= CFG_EXT_125; - printf("%s125MHz clock", sep); + printf("%sexternal 125MHz clock", sep); sep = ", "; } if (reg & CFG_TBI_EN) { @@ -1004,15 +1074,32 @@ sipcom_attach(device_t parent, device_t } sc->sc_dev = self; sc->sc_gigabit = sip->sip_gigabit; + sc->sc_dma64 = false; pmf_self_suspensor_init(self, &sc->sc_suspensor, &sc->sc_qual); sc->sc_pc = pc; if (sc->sc_gigabit) { + if (sc->sc_dma64) { + sc->sc_bufptr_idx = GSIP64_DESC_BUFPTR_LO; + sc->sc_cmdsts_idx = GSIP64_DESC_CMDSTS; + sc->sc_extsts_idx = GSIP64_DESC_EXTSTS; + } else { + sc->sc_bufptr_idx = GSIP_DESC_BUFPTR; + sc->sc_cmdsts_idx = GSIP_DESC_CMDSTS; + sc->sc_extsts_idx = GSIP_DESC_EXTSTS; + } sc->sc_rxintr = gsip_rxintr; sc->sc_parm = &gsip_parm; } else { sc->sc_rxintr = sip_rxintr; sc->sc_parm = &sip_parm; + sc->sc_bufptr_idx = SIP_DESC_BUFPTR; + sc->sc_cmdsts_idx = SIP_DESC_CMDSTS; + /* + * EXTSTS doesn't really exist on non-GigE parts, + * but we initialize the index for simplicity later. + */ + sc->sc_extsts_idx = GSIP_DESC_EXTSTS; } tx_dmamap_size = sc->sc_parm->p_tx_dmamap_size; ntxsegs_alloc = sc->sc_parm->p_ntxsegs_alloc; @@ -1376,7 +1463,7 @@ static inline void sipcom_set_extsts(struct sip_softc *sc, int lasttx, struct mbuf *m0, uint64_t capenable) { - uint32_t extsts; + uint32_t extsts = 0; #ifdef DEBUG struct ifnet *ifp = &sc->sc_ethercom.ec_if; #endif @@ -1397,7 +1484,7 @@ sipcom_set_extsts(struct sip_softc *sc, * unconditional swap instead of htons() inside. */ if (vlan_has_tag(m0)) { - sc->sc_txdescs[lasttx].sipd_extsts |= + sc->sc_txdescs[lasttx].sipd_words[sc->sc_extsts_idx] |= htole32(EXTSTS_VPKT | (bswap16(vlan_get_tag(m0)) & EXTSTS_VTCI)); @@ -1413,7 +1500,6 @@ sipcom_set_extsts(struct sip_softc *sc, * * Byte-swap constants so the compiler can optimize. */ - extsts = 0; if (m0->m_pkthdr.csum_flags & M_CSUM_IPv4) { KDASSERT(ifp->if_capenable & IFCAP_CSUM_IPv4_Tx); SIP_EVCNT_INCR(&sc->sc_ev_txipsum); @@ -1428,7 +1514,7 @@ sipcom_set_extsts(struct sip_softc *sc, SIP_EVCNT_INCR(&sc->sc_ev_txudpsum); extsts |= htole32(EXTSTS_UDPPKT); } - sc->sc_txdescs[sc->sc_txnext].sipd_extsts |= extsts; + sc->sc_txdescs[sc->sc_txnext].sipd_words[sc->sc_extsts_idx] |= extsts; } /* @@ -1446,6 +1532,7 @@ sipcom_start(struct ifnet *ifp) bus_dmamap_t dmamap; int error, nexttx, lasttx, seg; int ofree = sc->sc_txfree; + uint32_t cmdsts; #if 0 int firsttx = sc->sc_txnext; #endif @@ -1586,26 +1673,24 @@ sipcom_start(struct ifnet *ifp) * yet. That could cause a race condition. * We'll do it below. */ - *sipd_bufptr(sc, &sc->sc_txdescs[nexttx]) = - htole32(dmamap->dm_segs[seg].ds_addr); - *sipd_cmdsts(sc, &sc->sc_txdescs[nexttx]) = - htole32((nexttx == sc->sc_txnext ? 0 : CMDSTS_OWN) - | CMDSTS_MORE | dmamap->dm_segs[seg].ds_len); - sc->sc_txdescs[nexttx].sipd_extsts = 0; + + cmdsts = dmamap->dm_segs[seg].ds_len; + if (nexttx != sc->sc_txnext) + cmdsts |= CMDSTS_OWN; + if (seg < dmamap->dm_nsegs - 1) + cmdsts |= CMDSTS_MORE; + sip_init_txdesc(sc, nexttx, + dmamap->dm_segs[seg].ds_addr, cmdsts); lasttx = nexttx; } - /* Clear the MORE bit on the last segment. */ - *sipd_cmdsts(sc, &sc->sc_txdescs[lasttx]) &= - htole32(~CMDSTS_MORE); - /* * If we're in the interrupt delay window, delay the * interrupt. */ if (++sc->sc_txwin >= (SIP_TXQUEUELEN * 2 / 3)) { SIP_EVCNT_INCR(&sc->sc_ev_txforceintr); - *sipd_cmdsts(sc, &sc->sc_txdescs[lasttx]) |= + sc->sc_txdescs[lasttx].sipd_words[sc->sc_cmdsts_idx] |= htole32(CMDSTS_INTR); sc->sc_txwin = 0; } @@ -1621,7 +1706,7 @@ sipcom_start(struct ifnet *ifp) * The entire packet is set up. Give the first descrptor * to the chip now. */ - *sipd_cmdsts(sc, &sc->sc_txdescs[sc->sc_txnext]) |= + sc->sc_txdescs[sc->sc_txnext].sipd_words[sc->sc_cmdsts_idx] |= htole32(CMDSTS_OWN); sip_cdtxsync(sc, sc->sc_txnext, 1, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); @@ -1670,8 +1755,7 @@ sipcom_start(struct ifnet *ifp) #if 0 if ((bus_space_read_4(sc->sc_st, sc->sc_sh, SIP_CR) & CR_TXE) == 0) { - bus_space_write_4(sc->sc_st, sc->sc_sh, SIP_TXDP, - SIP_CDTXADDR(sc, firsttx)); + sip_set_txdp(sc, SIP_CDTXADDR(sc, firsttx)); bus_space_write_4(sc->sc_st, sc->sc_sh, SIP_CR, CR_TXE); } #else @@ -1862,8 +1946,8 @@ sipcom_intr(void *arg) device_xname(sc->sc_dev)); /* Get the receive process going again. */ - bus_space_write_4(sc->sc_st, sc->sc_sh, - SIP_RXDP, SIP_CDRXADDR(sc, sc->sc_rxptr)); + sip_set_rxdp(sc, + SIP_CDRXADDR(sc, sc->sc_rxptr)); bus_space_write_4(sc->sc_st, sc->sc_sh, SIP_CR, CR_RXE); } @@ -1979,8 +2063,8 @@ sipcom_txintr(struct sip_softc *sc) sip_cdtxsync(sc, txs->txs_firstdesc, txs->txs_dmamap->dm_nsegs, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); - cmdsts = le32toh(*sipd_cmdsts(sc, - &sc->sc_txdescs[txs->txs_lastdesc])); + cmdsts = le32toh(sc->sc_txdescs[ + txs->txs_lastdesc].sipd_words[sc->sc_cmdsts_idx]); if (cmdsts & CMDSTS_OWN) break; @@ -2051,8 +2135,10 @@ gsip_rxintr(struct sip_softc *sc) sip_cdrxsync(sc, i, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); - cmdsts = le32toh(*sipd_cmdsts(sc, &sc->sc_rxdescs[i])); - extsts = le32toh(sc->sc_rxdescs[i].sipd_extsts); + cmdsts = + le32toh(sc->sc_rxdescs[i].sipd_words[sc->sc_cmdsts_idx]); + extsts = + le32toh(sc->sc_rxdescs[i].sipd_words[sc->sc_extsts_idx]); len = CMDSTS_SIZE(sc, cmdsts); /* @@ -2262,7 +2348,8 @@ sip_rxintr(struct sip_softc *sc) sip_cdrxsync(sc, i, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); - cmdsts = le32toh(*sipd_cmdsts(sc, &sc->sc_rxdescs[i])); + cmdsts = + le32toh(sc->sc_rxdescs[i].sipd_words[sc->sc_cmdsts_idx]); /* * NOTE: OWN is set if owned by _consumer_. We're the @@ -2527,7 +2614,6 @@ sipcom_init(struct ifnet *ifp) bus_space_handle_t sh = sc->sc_sh; struct sip_txsoft *txs; struct sip_rxsoft *rxs; - struct sip_desc *sipd; int i, error = 0; if (device_is_active(sc->sc_dev)) { @@ -2574,19 +2660,8 @@ sipcom_init(struct ifnet *ifp) bus_space_write_4(st, sh, 0x00cc, 0x0000); } - /* - * Initialize the transmit descriptor ring. - */ - for (i = 0; i < sc->sc_ntxdesc; i++) { - sipd = &sc->sc_txdescs[i]; - memset(sipd, 0, sizeof(struct sip_desc)); - sipd->sipd_link = htole32(SIP_CDTXADDR(sc, sip_nexttx(sc, i))); - } - sip_cdtxsync(sc, 0, sc->sc_ntxdesc, - BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); - sc->sc_txfree = sc->sc_ntxdesc; - sc->sc_txnext = 0; - sc->sc_txwin = 0; + /* Initialize the transmit descriptor ring. */ + sip_init_txring(sc); /* * Initialize the transmit job descriptors. @@ -2708,8 +2783,8 @@ sipcom_init(struct ifnet *ifp) /* * Give the transmit and receive rings to the chip. */ - bus_space_write_4(st, sh, SIP_TXDP, SIP_CDTXADDR(sc, sc->sc_txnext)); - bus_space_write_4(st, sh, SIP_RXDP, SIP_CDRXADDR(sc, sc->sc_rxptr)); + sip_set_txdp(sc, SIP_CDTXADDR(sc, sc->sc_txnext)); + sip_set_rxdp(sc, SIP_CDRXADDR(sc, sc->sc_rxptr)); /* * Initialize the interrupt mask. @@ -2840,8 +2915,9 @@ sipcom_stop(struct ifnet *ifp, int disab while ((txs = SIMPLEQ_FIRST(&sc->sc_txdirtyq)) != NULL) { if ((ifp->if_flags & IFF_DEBUG) != 0 && SIMPLEQ_NEXT(txs, txs_q) == NULL && - (le32toh(*sipd_cmdsts(sc, &sc->sc_txdescs[txs->txs_lastdesc])) & - CMDSTS_INTR) == 0) + (sc->sc_txdescs[ + txs->txs_lastdesc].sipd_words[ + sc->sc_cmdsts_idx] & htole32(CMDSTS_INTR)) == 0) printf("%s: sip_stop: last descriptor does not " "have INTR bit set\n", device_xname(sc->sc_dev)); SIMPLEQ_REMOVE_HEAD(&sc->sc_txdirtyq, txs_q); @@ -2853,7 +2929,8 @@ sipcom_stop(struct ifnet *ifp, int disab } #endif cmdsts |= /* DEBUG */ - le32toh(*sipd_cmdsts(sc, &sc->sc_txdescs[txs->txs_lastdesc])); + le32toh(sc->sc_txdescs[ + txs->txs_lastdesc].sipd_words[sc->sc_cmdsts_idx]); bus_dmamap_unload(sc->sc_dmat, txs->txs_dmamap); m_freem(txs->txs_mbuf); txs->txs_mbuf = NULL; Index: src/sys/dev/pci/if_sipreg.h diff -u src/sys/dev/pci/if_sipreg.h:1.20 src/sys/dev/pci/if_sipreg.h:1.21 --- src/sys/dev/pci/if_sipreg.h:1.20 Thu Feb 8 09:05:19 2018 +++ src/sys/dev/pci/if_sipreg.h Sun Mar 8 02:44:12 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: if_sipreg.h,v 1.20 2018/02/08 09:05:19 dholland Exp $ */ +/* $NetBSD: if_sipreg.h,v 1.21 2020/03/08 02:44:12 thorpej Exp $ */ /*- * Copyright (c) 2001 The NetBSD Foundation, Inc. @@ -85,17 +85,32 @@ * and receive descriptor chains. * * Note the DP83820 can use 64-bit DMA addresses for link and bufptr. - * However, we do not yet support that. + * Note also that the buffer pointer and command/status words are in + * the opposite order on the DP83820 to facilitate 64-bit DMA addresses. * * For transmit, buffers need not be aligned. For receive, buffers * must be aligned to 4-byte (8-byte on DP83820) boundaries. */ +#define SIP_DESC_LINK 0 /* link to next descriptor */ +#define SIP_DESC_CMDSTS 1 /* ccommand/status */ +#define SIP_DESC_BUFPTR 2 /* pointer to DMA segment */ + +#define GSIP_DESC_LINK 0 +#define GSIP_DESC_BUFPTR 1 +#define GSIP_DESC_CMDSTS 2 +#define GSIP_DESC_EXTSTS 3 /* extended status */ + +#define GSIP64_DESC_LINK_LO 0 +#define GSIP64_DESC_LINK_HI 1 +#define GSIP64_DESC_BUFPTR_LO 2 +#define GSIP64_DESC_BUFPTR_HI 3 +#define GSIP64_DESC_CMDSTS 4 +#define GSIP64_DESC_EXTSTS 5 + +#define SIP_NDESC_WORDS 6 + struct sip_desc { - u_int32_t sipd_link; /* link to next descriptor */ - uint32_t sipd_cbs[2]; /* command/status and pointer to - * DMA segment - */ - u_int32_t sipd_extsts; /* extended status */ + uint32_t sipd_words[SIP_NDESC_WORDS]; }; /*