xref: /netbsd-src/sys/arch/arm/gemini/if_gpn.c (revision be6f2fcee7fefd8149c125c7283a8c03adc8149e)
1*be6f2fceSriastradh /* $NetBSD: if_gpn.c,v 1.18 2024/06/29 12:11:10 riastradh Exp $ */
241bfa2c4Scliff /*-
341bfa2c4Scliff  * Copyright (c) 2008 The NetBSD Foundation, Inc.
441bfa2c4Scliff  * All rights reserved.
541bfa2c4Scliff  *
641bfa2c4Scliff  * This code is derived from software contributed to The NetBSD Foundation
741bfa2c4Scliff  * by Matt Thomas <matt@3am-software.com>
841bfa2c4Scliff  *
941bfa2c4Scliff  * Redistribution and use in source and binary forms, with or without
1041bfa2c4Scliff  * modification, are permitted provided that the following conditions
1141bfa2c4Scliff  * are met:
1241bfa2c4Scliff  * 1. Redistributions of source code must retain the above copyright
1341bfa2c4Scliff  *    notice, this list of conditions and the following disclaimer.
1441bfa2c4Scliff  * 2. Redistributions in binary form must reproduce the above copyright
1541bfa2c4Scliff  *    notice, this list of conditions and the following disclaimer in the
1641bfa2c4Scliff  *    documentation and/or other materials provided with the distribution.
1741bfa2c4Scliff  *
1841bfa2c4Scliff  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
1941bfa2c4Scliff  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2041bfa2c4Scliff  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2141bfa2c4Scliff  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2241bfa2c4Scliff  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2341bfa2c4Scliff  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2441bfa2c4Scliff  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2541bfa2c4Scliff  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2641bfa2c4Scliff  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2741bfa2c4Scliff  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2841bfa2c4Scliff  * POSSIBILITY OF SUCH DAMAGE.
2941bfa2c4Scliff  */
3041bfa2c4Scliff 
3141bfa2c4Scliff #include <sys/cdefs.h>
3241bfa2c4Scliff 
3341bfa2c4Scliff #include "opt_gemini.h"
3441bfa2c4Scliff 
35*be6f2fceSriastradh __KERNEL_RCSID(0, "$NetBSD: if_gpn.c,v 1.18 2024/06/29 12:11:10 riastradh Exp $");
3641bfa2c4Scliff 
3741bfa2c4Scliff #include <sys/param.h>
3841bfa2c4Scliff #include <sys/device.h>
3941bfa2c4Scliff #include <sys/mbuf.h>
4041bfa2c4Scliff #include <sys/ioctl.h>
4141bfa2c4Scliff 
4241bfa2c4Scliff #include <net/if.h>
4341bfa2c4Scliff #include <net/if_media.h>
4441bfa2c4Scliff #include <net/if_ether.h>
4541bfa2c4Scliff #include <net/if_dl.h>
4641bfa2c4Scliff #include <net/bpf.h>
4741bfa2c4Scliff 
48cf10107dSdyoung #include <sys/bus.h>
4941bfa2c4Scliff 
5041bfa2c4Scliff #include <arm/gemini/gemini_var.h>
5141bfa2c4Scliff #include <arm/gemini/gemini_ipm.h>
5241bfa2c4Scliff 
5341bfa2c4Scliff #define	GPN_MOF		0x00	/* Middle Of Frame */
5441bfa2c4Scliff #define	GPN_SOF		0x01	/* Start of Frame */
5541bfa2c4Scliff #define	GPN_EOF		0x02	/* End of Frame */
5641bfa2c4Scliff #define	GPN_FRAME	0x03	/* Complete Frame */
5741bfa2c4Scliff 
5841bfa2c4Scliff #define	GPN_IFUP	0x05	/* partner is up */
5941bfa2c4Scliff #define	GPN_IFDOWN	0x06	/* partner is down */
6041bfa2c4Scliff 
6141bfa2c4Scliff #define	GPN_ACK0	0x10	/* placeholder */
6241bfa2c4Scliff #define	GPN_ACK1	0x11	/* Ack 1 descriptor */
6341bfa2c4Scliff #define	GPN_ACK2	0x12	/* Ack 2 descriptors */
6441bfa2c4Scliff #define	GPN_ACK3	0x13	/* Ack 3 descriptors */
6541bfa2c4Scliff #define	GPN_ACK4	0x14	/* Ack 4 descriptors */
6641bfa2c4Scliff #define	GPN_ACK5	0x15	/* Ack 5 descriptors */
6741bfa2c4Scliff #define	GPN_ACK6	0x16	/* Ack 6 descriptors */
6841bfa2c4Scliff #define	GPN_ACK7	0x17	/* Ack 7 descriptors */
6941bfa2c4Scliff #define	GPN_ACK8	0x18	/* Ack 8 descriptors */
7041bfa2c4Scliff #define	GPN_ACK9	0x19	/* Ack 9 descriptors */
7141bfa2c4Scliff #define	GPN_ACK10	0x1a	/* Ack 10 descriptors */
7241bfa2c4Scliff #define	GPN_ACK11	0x1b	/* Ack 11 descriptors */
7341bfa2c4Scliff #define	GPN_ACK12	0x1c	/* Ack 12 descriptors */
7441bfa2c4Scliff #define	GPN_ACK13	0x1d	/* Ack 13 descriptors */
7541bfa2c4Scliff #define	GPN_ACK14	0x1e	/* Ack 14 descriptors */
7641bfa2c4Scliff 
7741bfa2c4Scliff typedef struct {
7841bfa2c4Scliff 	uint8_t gd_tag;
7941bfa2c4Scliff 	uint8_t gd_subtype;
8041bfa2c4Scliff 	uint8_t gd_txid;
8141bfa2c4Scliff 	uint8_t gd_pktlen64;
8241bfa2c4Scliff 	uint16_t gd_len1;
8341bfa2c4Scliff 	uint16_t gd_len2;
8441bfa2c4Scliff 	uint32_t gd_addr1;
8541bfa2c4Scliff 	uint32_t gd_addr2;
8641bfa2c4Scliff } ipm_gpn_desc_t;
8741bfa2c4Scliff 
8841bfa2c4Scliff typedef struct {
8941bfa2c4Scliff 	uint8_t agd_tag;
9041bfa2c4Scliff 	uint8_t agd_subtype;
9141bfa2c4Scliff 	uint8_t agd_txids[14];
9241bfa2c4Scliff } ipm_gpn_ack_desc_t;
9341bfa2c4Scliff 
9441bfa2c4Scliff #define	MAX_TXACTIVE	60
9541bfa2c4Scliff 
9641bfa2c4Scliff struct gpn_txinfo {
9741bfa2c4Scliff 	struct mbuf *ti_mbuf;
9841bfa2c4Scliff 	bus_dmamap_t ti_map;
9941bfa2c4Scliff };
10041bfa2c4Scliff 
10141bfa2c4Scliff struct gpn_softc {
10241bfa2c4Scliff 	device_t sc_dev;
10341bfa2c4Scliff 	bus_dma_tag_t sc_dmat;
10441bfa2c4Scliff 	struct ifmedia sc_im;
10541bfa2c4Scliff 	struct ethercom sc_ec;
10641bfa2c4Scliff #define	sc_if sc_ec.ec_if
10741bfa2c4Scliff 	size_t sc_free;
10841bfa2c4Scliff 	size_t sc_txactive;
1098329fbceSthorpej 	bool sc_txbusy;
11041bfa2c4Scliff 	void *sc_ih;
11141bfa2c4Scliff 	ipm_gpn_ack_desc_t sc_ack_desc;
11241bfa2c4Scliff 	struct mbuf *sc_rxmbuf;
11341bfa2c4Scliff 	struct gpn_txinfo sc_txinfo[MAX_TXACTIVE];
11441bfa2c4Scliff 	uint8_t sc_lastid;
11541bfa2c4Scliff 	bool sc_remoteup;		/* remote side up? */
11641bfa2c4Scliff };
11741bfa2c4Scliff 
11841bfa2c4Scliff CTASSERT((GPN_SOF | GPN_EOF) == GPN_FRAME);
11941bfa2c4Scliff CTASSERT((GPN_SOF & GPN_EOF) == 0);
12041bfa2c4Scliff 
12141bfa2c4Scliff extern struct cfdriver gpn_cd;
12241bfa2c4Scliff 
12341bfa2c4Scliff static void gpn_ifstart(struct ifnet *);
12441bfa2c4Scliff 
12541bfa2c4Scliff #ifdef GPNDEBUG
12641bfa2c4Scliff static uint32_t
m_crc32_le(struct mbuf * m)12741bfa2c4Scliff m_crc32_le(struct mbuf *m)
12841bfa2c4Scliff {
12941bfa2c4Scliff 	static const uint32_t crctab[] = {
13041bfa2c4Scliff 		0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
13141bfa2c4Scliff 		0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
13241bfa2c4Scliff 		0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
13341bfa2c4Scliff 		0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
13441bfa2c4Scliff 	};
13541bfa2c4Scliff 	uint32_t crc;
13641bfa2c4Scliff 	size_t i;
13741bfa2c4Scliff 
13841bfa2c4Scliff 	crc = 0xffffffffU;	/* initial value */
13941bfa2c4Scliff 
14041bfa2c4Scliff 	for (; m; m = m->m_next) {
14141bfa2c4Scliff 		for (i = 0; i < m->m_len; i++) {
14241bfa2c4Scliff 			crc ^= m->m_data[i];
14341bfa2c4Scliff 			crc = (crc >> 4) ^ crctab[crc & 0xf];
14441bfa2c4Scliff 			crc = (crc >> 4) ^ crctab[crc & 0xf];
14541bfa2c4Scliff 		}
14641bfa2c4Scliff 	}
14741bfa2c4Scliff 
14887d4693bSmsaitoh 	return crc;
14941bfa2c4Scliff }
15041bfa2c4Scliff #endif
15141bfa2c4Scliff 
15241bfa2c4Scliff static void
gpn_free_dmamaps(struct gpn_softc * sc)15341bfa2c4Scliff gpn_free_dmamaps(struct gpn_softc *sc)
15441bfa2c4Scliff {
15541bfa2c4Scliff 	struct gpn_txinfo *ti = sc->sc_txinfo;
15641bfa2c4Scliff 	struct gpn_txinfo * const end_ti = ti + __arraycount(sc->sc_txinfo);
15741bfa2c4Scliff 
15841bfa2c4Scliff 	for (; ti < end_ti; ti++) {
15941bfa2c4Scliff 		if (ti->ti_map == NULL)
16041bfa2c4Scliff 			continue;
16141bfa2c4Scliff 		bus_dmamap_destroy(sc->sc_dmat, ti->ti_map);
16241bfa2c4Scliff 		ti->ti_map = NULL;
16341bfa2c4Scliff 	}
16441bfa2c4Scliff }
16541bfa2c4Scliff 
16641bfa2c4Scliff static int
gpn_alloc_dmamaps(struct gpn_softc * sc)16741bfa2c4Scliff gpn_alloc_dmamaps(struct gpn_softc *sc)
16841bfa2c4Scliff {
16941bfa2c4Scliff 	struct gpn_txinfo *ti = sc->sc_txinfo;
17041bfa2c4Scliff 	struct gpn_txinfo * const end_ti = ti + __arraycount(sc->sc_txinfo);
17141bfa2c4Scliff 	int error;
17241bfa2c4Scliff 
17341bfa2c4Scliff 	for (error = 0; ti < end_ti; ti++) {
17441bfa2c4Scliff 		if (ti->ti_map != NULL)
17541bfa2c4Scliff 			continue;
17641bfa2c4Scliff 		error = bus_dmamap_create(sc->sc_dmat,
17741bfa2c4Scliff 		    10000, 2, 8192, 0,
17841bfa2c4Scliff 		    BUS_DMA_ALLOCNOW | BUS_DMA_WAITOK,
17941bfa2c4Scliff 		    &ti->ti_map);
18041bfa2c4Scliff 		if (error)
18141bfa2c4Scliff 			break;
18241bfa2c4Scliff 	}
18341bfa2c4Scliff 
18441bfa2c4Scliff 	if (error)
18541bfa2c4Scliff 		gpn_free_dmamaps(sc);
18641bfa2c4Scliff 
18741bfa2c4Scliff 	return error;
18841bfa2c4Scliff }
18941bfa2c4Scliff 
19041bfa2c4Scliff static bool
gpn_add_data(struct gpn_softc * sc,bus_addr_t addr,bus_size_t len)19141bfa2c4Scliff gpn_add_data(struct gpn_softc *sc, bus_addr_t addr, bus_size_t len)
19241bfa2c4Scliff {
19341bfa2c4Scliff 	struct mbuf *m, *m0;
19441bfa2c4Scliff 	size_t space;
19541bfa2c4Scliff 
19641bfa2c4Scliff 	m = sc->sc_rxmbuf;
19741bfa2c4Scliff 	KASSERT(m != NULL);
19841bfa2c4Scliff 
19941bfa2c4Scliff 	m->m_pkthdr.len += len;
20041bfa2c4Scliff 
20141bfa2c4Scliff 	while (m->m_next != NULL)
20241bfa2c4Scliff 		m = m->m_next;
20341bfa2c4Scliff 
20441bfa2c4Scliff 	KASSERT(len > 0);
20541bfa2c4Scliff 	space = M_TRAILINGSPACE(m);
20641bfa2c4Scliff 	for (;;) {
20741bfa2c4Scliff 		if (space > 0) {
20841bfa2c4Scliff 			if (len < space)
20941bfa2c4Scliff 				space = len;
21041bfa2c4Scliff 			gemini_ipm_copyin(mtod(m, uint8_t *) + m->m_len, addr,
21141bfa2c4Scliff 			    space);
21241bfa2c4Scliff 			len -= space;
21341bfa2c4Scliff 			m->m_len += space;
21441bfa2c4Scliff 			if (len == 0)
21541bfa2c4Scliff 				return true;
21641bfa2c4Scliff 			addr += space;
21741bfa2c4Scliff 		}
21841bfa2c4Scliff 		MGET(m0, M_DONTWAIT, MT_DATA);
21941bfa2c4Scliff 		if (m0 == NULL)
22041bfa2c4Scliff 			break;
22141bfa2c4Scliff 		space = MLEN;
22241bfa2c4Scliff 		if (len > space) {
22341bfa2c4Scliff 			MCLGET(m0, M_DONTWAIT);
22441bfa2c4Scliff 			if (m0->m_flags & M_EXT)
22541bfa2c4Scliff 				space = MCLBYTES;
22641bfa2c4Scliff 		}
22741bfa2c4Scliff 		m->m_len = 0;
22841bfa2c4Scliff 		m->m_next = m0;
22941bfa2c4Scliff 		m = m0;
23041bfa2c4Scliff 	}
23141bfa2c4Scliff 	return false;
23241bfa2c4Scliff }
23341bfa2c4Scliff 
23441bfa2c4Scliff static void
gpn_ack_txid(struct gpn_softc * sc,unsigned int txid)23541bfa2c4Scliff gpn_ack_txid(struct gpn_softc *sc, unsigned int txid)
23641bfa2c4Scliff {
23741bfa2c4Scliff 	ipm_gpn_ack_desc_t * const agd = &sc->sc_ack_desc;
23841bfa2c4Scliff 	agd->agd_txids[agd->agd_subtype] = txid;
23941bfa2c4Scliff 	if (++agd->agd_subtype == __arraycount(agd->agd_txids)) {
24041bfa2c4Scliff 		agd->agd_subtype += GPN_ACK0;
24141bfa2c4Scliff 		sc->sc_free--;
24241bfa2c4Scliff 		gemini_ipm_produce(agd, 1);
24341bfa2c4Scliff 		agd->agd_subtype = 0;
24441bfa2c4Scliff 	}
24541bfa2c4Scliff }
24641bfa2c4Scliff 
24741bfa2c4Scliff static void
gpn_process_data(struct gpn_softc * sc,const ipm_gpn_desc_t * gd)24841bfa2c4Scliff gpn_process_data(struct gpn_softc *sc, const ipm_gpn_desc_t *gd)
24941bfa2c4Scliff {
25041bfa2c4Scliff 	struct ifnet * const ifp = &sc->sc_if;
25141bfa2c4Scliff 	size_t pktlen = gd->gd_pktlen64 * 64;
25241bfa2c4Scliff 	unsigned int subtype = gd->gd_subtype;
25341bfa2c4Scliff 	bool ok;
25441bfa2c4Scliff 
25541bfa2c4Scliff 	if ((subtype & GPN_SOF) == 0 && sc->sc_rxmbuf == NULL) {
2569b9ae5ecSthorpej 		if_statinc(ifp, if_ierrors);
25741bfa2c4Scliff 		goto out;
25841bfa2c4Scliff 	}
25941bfa2c4Scliff 
26041bfa2c4Scliff 	if ((subtype & GPN_SOF) && sc->sc_rxmbuf != NULL) {
2619b9ae5ecSthorpej 		if_statinc(ifp, if_ierrors);
26241bfa2c4Scliff 		m_freem(sc->sc_rxmbuf);
26341bfa2c4Scliff 		sc->sc_rxmbuf = NULL;
26441bfa2c4Scliff 	}
26541bfa2c4Scliff 
26641bfa2c4Scliff 	if (sc->sc_rxmbuf == NULL) {
26741bfa2c4Scliff 		struct mbuf *m;
26841bfa2c4Scliff 		MGETHDR(m, M_DONTWAIT, MT_DATA);
26941bfa2c4Scliff 		if (m == NULL) {
2709b9ae5ecSthorpej 			if_statinc(ifp, if_ierrors);
27141bfa2c4Scliff 			goto out;
27241bfa2c4Scliff 		}
27341bfa2c4Scliff 		if (pktlen > MHLEN - 2) {
27441bfa2c4Scliff 			MCLGET(m, M_DONTWAIT);
27541bfa2c4Scliff 			if ((m->m_flags & M_EXT) == 0) {
2769b9ae5ecSthorpej 				if_statinc(ifp, if_ierrors);
27741bfa2c4Scliff 				m_free(m);
27841bfa2c4Scliff 				goto out;
27941bfa2c4Scliff 			}
28041bfa2c4Scliff 		}
28141bfa2c4Scliff 		m->m_data += 2;	/* makes ethernet payload 32bit aligned */
28241bfa2c4Scliff 		m->m_len = 0;
28341bfa2c4Scliff 		m->m_pkthdr.len = 0;
28441bfa2c4Scliff 		sc->sc_rxmbuf = m;
28541bfa2c4Scliff 	}
28641bfa2c4Scliff 
28741bfa2c4Scliff 	ok = gpn_add_data(sc, gd->gd_addr1, gd->gd_len1);
28841bfa2c4Scliff 	if (ok && gd->gd_addr2 && gd->gd_len2)
28941bfa2c4Scliff 		ok = gpn_add_data(sc, gd->gd_addr2, gd->gd_len2);
29041bfa2c4Scliff 	if (!ok) {
2919b9ae5ecSthorpej 		if_statinc(ifp, if_ierrors);
29241bfa2c4Scliff 		m_freem(sc->sc_rxmbuf);
29341bfa2c4Scliff 		sc->sc_rxmbuf = NULL;
29441bfa2c4Scliff 		goto out;
29541bfa2c4Scliff 	}
29641bfa2c4Scliff 
29741bfa2c4Scliff 	if (subtype & GPN_EOF) {
29841bfa2c4Scliff 		struct mbuf *m;
29941bfa2c4Scliff 		m = sc->sc_rxmbuf;
30041bfa2c4Scliff 		sc->sc_rxmbuf = NULL;
301d938d837Sozaki-r 		m_set_rcvif(m, ifp);
30241bfa2c4Scliff 		KASSERT(((m->m_pkthdr.len + 63) >> 6) == gd->gd_pktlen64);
30341bfa2c4Scliff #ifdef GPNDEBUG
30441bfa2c4Scliff 		printf("%s: rx len=%d crc=%#x\n", ifp->if_xname,
30541bfa2c4Scliff 		    m->m_pkthdr.len, m_crc32_le(m));
30641bfa2c4Scliff #endif
3079c4cd063Sozaki-r 		if_percpuq_enqueue(ifp->if_percpuq, m);
30841bfa2c4Scliff 	}
30941bfa2c4Scliff 
31041bfa2c4Scliff out:
31141bfa2c4Scliff 	gpn_ack_txid(sc, gd->gd_txid);
31241bfa2c4Scliff }
31341bfa2c4Scliff 
31441bfa2c4Scliff static void
gpn_free_txid(struct gpn_softc * sc,size_t txid)31541bfa2c4Scliff gpn_free_txid(struct gpn_softc *sc, size_t txid)
31641bfa2c4Scliff {
31741bfa2c4Scliff 	struct gpn_txinfo * const ti = sc->sc_txinfo + txid;
31841bfa2c4Scliff 
31941bfa2c4Scliff 	KASSERT(txid < MAX_TXACTIVE);
32041bfa2c4Scliff 
32141bfa2c4Scliff 	if (ti->ti_mbuf == NULL)
32241bfa2c4Scliff 		return;
32341bfa2c4Scliff 
32441bfa2c4Scliff 	bus_dmamap_sync(sc->sc_dmat, ti->ti_map,
32541bfa2c4Scliff 	    0, ti->ti_mbuf->m_len, BUS_DMASYNC_POSTREAD);
32641bfa2c4Scliff 	bus_dmamap_unload(sc->sc_dmat, ti->ti_map);
32741bfa2c4Scliff 	m_freem(ti->ti_mbuf);
32841bfa2c4Scliff 	ti->ti_mbuf = NULL;
32941bfa2c4Scliff 	sc->sc_txactive--;
33041bfa2c4Scliff 	KASSERT(sc->sc_txactive < MAX_TXACTIVE);
3318329fbceSthorpej 	if (sc->sc_txbusy) {
3328329fbceSthorpej 		sc->sc_txbusy = false;
33341bfa2c4Scliff 		gpn_ifstart(&sc->sc_if);
33441bfa2c4Scliff 	}
33541bfa2c4Scliff }
33641bfa2c4Scliff 
33741bfa2c4Scliff static void
gpn_ipm_rebate(void * arg,size_t count)33841bfa2c4Scliff gpn_ipm_rebate(void *arg, size_t count)
33941bfa2c4Scliff {
34041bfa2c4Scliff 	struct gpn_softc * const sc = arg;
34141bfa2c4Scliff 	int s;
34241bfa2c4Scliff 
34341bfa2c4Scliff 	s = splnet();
34441bfa2c4Scliff 	sc->sc_free += count;
34541bfa2c4Scliff 
3468329fbceSthorpej 	sc->sc_txbusy = false;
34741bfa2c4Scliff 	gpn_ifstart(&sc->sc_if);
34841bfa2c4Scliff 	splx(s);
34941bfa2c4Scliff }
35041bfa2c4Scliff 
35141bfa2c4Scliff static void
gpn_ifstart(struct ifnet * ifp)35241bfa2c4Scliff gpn_ifstart(struct ifnet *ifp)
35341bfa2c4Scliff {
35441bfa2c4Scliff 	struct gpn_softc * const sc = ifp->if_softc;
35541bfa2c4Scliff 
35641bfa2c4Scliff 	for (;;) {
35741bfa2c4Scliff 		struct mbuf *m, *m0;
35841bfa2c4Scliff 		ipm_gpn_desc_t gd;
35941bfa2c4Scliff 		ipm_gpn_desc_t *last_gd;
36041bfa2c4Scliff 		size_t count;
36141bfa2c4Scliff 
36241bfa2c4Scliff 		if (sc->sc_free == 0) {
3638329fbceSthorpej 			sc->sc_txbusy = true;
36441bfa2c4Scliff 			break;
36541bfa2c4Scliff 		}
36641bfa2c4Scliff 
367390356a4Sthorpej 		IF_POLL(&ifp->if_snd, m);
36841bfa2c4Scliff 		if (!m)
36941bfa2c4Scliff 			break;
37041bfa2c4Scliff 
37141bfa2c4Scliff 		if ((ifp->if_flags & IFF_UP) == 0) {
37241bfa2c4Scliff 			m_freem(m);
37341bfa2c4Scliff 			continue;
37441bfa2c4Scliff 		}
37541bfa2c4Scliff 
37641bfa2c4Scliff 		/*
37741bfa2c4Scliff 		 * Make sure to send any pending acks first.
37841bfa2c4Scliff 		 */
37941bfa2c4Scliff 		if (sc->sc_ack_desc.agd_subtype) {
38041bfa2c4Scliff 			sc->sc_free--;
38141bfa2c4Scliff 			sc->sc_ack_desc.agd_subtype += GPN_ACK0;
38241bfa2c4Scliff 			gemini_ipm_produce(&sc->sc_ack_desc, 1);
38341bfa2c4Scliff 			sc->sc_ack_desc.agd_subtype = 0;
38441bfa2c4Scliff 		}
38541bfa2c4Scliff 
38641bfa2c4Scliff 		/*
38741bfa2c4Scliff 		 * Let's find out how many mbufs we are using.
38841bfa2c4Scliff 		 */
38941bfa2c4Scliff 		for (m0 = m, count = 0; m0; m0 = m0->m_next) {
39041bfa2c4Scliff 			if (m0->m_len == 0)
39141bfa2c4Scliff 				continue;
39241bfa2c4Scliff 			count++;
39341bfa2c4Scliff 		}
39441bfa2c4Scliff 
39541bfa2c4Scliff 		/*
39641bfa2c4Scliff 		 * Make sure there is always enough room.
39741bfa2c4Scliff 		 */
39841bfa2c4Scliff 		if (sc->sc_free < count
39941bfa2c4Scliff 		    || sc->sc_txactive + count > MAX_TXACTIVE) {
4008329fbceSthorpej 			sc->sc_txbusy = true;
40141bfa2c4Scliff 			return;
40241bfa2c4Scliff 		}
403390356a4Sthorpej 		IF_DEQUEUE(&ifp->if_snd, m);
40441bfa2c4Scliff 
4053cd62456Smsaitoh 		bpf_mtap(ifp, m, BPF_D_OUT);
40641bfa2c4Scliff #ifdef GPNDEBUG
40741bfa2c4Scliff 		printf("%s: tx len=%d crc=%#x\n", ifp->if_xname,
40841bfa2c4Scliff 		    m->m_pkthdr.len, m_crc32_le(m));
40941bfa2c4Scliff #endif
41041bfa2c4Scliff 
41141bfa2c4Scliff 		last_gd = NULL;
41241bfa2c4Scliff 		gd.gd_tag = IPM_TAG_GPN;
41341bfa2c4Scliff 		gd.gd_subtype = GPN_SOF;
41441bfa2c4Scliff 		gd.gd_pktlen64 = (m->m_pkthdr.len + 63) >> 6;
4159b9ae5ecSthorpej 		net_stat_ref_t nsr = IF_STAT_GETREF(ifp);
41641bfa2c4Scliff 		for (; m != NULL; m = m0) {
41741bfa2c4Scliff 			struct gpn_txinfo *ti;
41841bfa2c4Scliff 			bus_dmamap_t map;
41941bfa2c4Scliff 			size_t id;
42041bfa2c4Scliff 			int error;
42141bfa2c4Scliff 
42241bfa2c4Scliff 			m0 = m->m_next;
42341bfa2c4Scliff 			m->m_next = NULL;
42441bfa2c4Scliff 			if (m->m_len == 0) {
42541bfa2c4Scliff 				m_free(m);
42641bfa2c4Scliff 				continue;
42741bfa2c4Scliff 			}
42841bfa2c4Scliff 			if (last_gd) {
42941bfa2c4Scliff 				sc->sc_txactive++;
43041bfa2c4Scliff 				sc->sc_free--;
43141bfa2c4Scliff 				gemini_ipm_produce(last_gd, 1);
43241bfa2c4Scliff 				last_gd = NULL;
43341bfa2c4Scliff 				gd.gd_subtype = GPN_MOF;
43441bfa2c4Scliff 			}
43541bfa2c4Scliff 			for (id = sc->sc_lastid;
43641bfa2c4Scliff 			     sc->sc_txinfo[id].ti_mbuf != NULL;) {
43741bfa2c4Scliff 				if (++id == __arraycount(sc->sc_txinfo))
43841bfa2c4Scliff 					id = 0;
43941bfa2c4Scliff 			}
44041bfa2c4Scliff 			KASSERT(id < MAX_TXACTIVE);
44141bfa2c4Scliff 			ti = sc->sc_txinfo + id;
44241bfa2c4Scliff 			map = ti->ti_map;
44341bfa2c4Scliff 			error = bus_dmamap_load(sc->sc_dmat, map,
44441bfa2c4Scliff 			    mtod(m, void *), m->m_len, NULL,
44541bfa2c4Scliff 			    BUS_DMA_READ | BUS_DMA_NOWAIT);
44641bfa2c4Scliff 			if (error) {
447*be6f2fceSriastradh 				if_statinc_ref(ifp, nsr, if_oerrors);
44841bfa2c4Scliff 				m_freem(m);
44941bfa2c4Scliff 				break;
45041bfa2c4Scliff 			}
45141bfa2c4Scliff 			bus_dmamap_sync(sc->sc_dmat, map, 0,
45241bfa2c4Scliff 			    m->m_len, BUS_DMASYNC_PREREAD);
45341bfa2c4Scliff 			KASSERT(map->dm_nsegs > 0);
45441bfa2c4Scliff 			KASSERT(map->dm_nsegs <= 2);
45541bfa2c4Scliff 			KASSERT(map->dm_segs[0].ds_addr != 0);
45641bfa2c4Scliff 			gd.gd_len1 = map->dm_segs[0].ds_len;
45741bfa2c4Scliff 			gd.gd_addr1 = map->dm_segs[0].ds_addr;
45841bfa2c4Scliff 			if (map->dm_nsegs == 1) {
45941bfa2c4Scliff 				gd.gd_len2 = 0;
46041bfa2c4Scliff 				gd.gd_addr2 = 0;
46141bfa2c4Scliff 			} else {
46241bfa2c4Scliff 				KASSERT(map->dm_segs[0].ds_addr != 0);
46341bfa2c4Scliff 				gd.gd_len2 = map->dm_segs[1].ds_len;
46441bfa2c4Scliff 				gd.gd_addr2 = map->dm_segs[1].ds_addr;
46541bfa2c4Scliff 			}
46641bfa2c4Scliff 
46741bfa2c4Scliff 			gd.gd_txid = id;
46841bfa2c4Scliff 			ti->ti_mbuf = m;
46941bfa2c4Scliff 			last_gd = &gd;
470*be6f2fceSriastradh 			if_statadd_ref(ifp, nsr, if_obytes, m->m_len);
47141bfa2c4Scliff 		}
472*be6f2fceSriastradh 		if_statinc_ref(ifp, nsr, if_opackets);
4739b9ae5ecSthorpej 		IF_STAT_PUTREF(ifp);
47460c4be69Smaxv 
4754208fe6aSandvar 		if (last_gd != NULL) {
47641bfa2c4Scliff 			last_gd->gd_subtype |= GPN_EOF;
47760c4be69Smaxv 
47841bfa2c4Scliff 			sc->sc_txactive++;
47941bfa2c4Scliff 			sc->sc_free--;
48041bfa2c4Scliff 			gemini_ipm_produce(last_gd, 1);
48141bfa2c4Scliff 		}
48241bfa2c4Scliff 	}
4834208fe6aSandvar }
48441bfa2c4Scliff 
48541bfa2c4Scliff static void
gpn_ipm_ifup(struct gpn_softc * sc)48641bfa2c4Scliff gpn_ipm_ifup(struct gpn_softc *sc)
48741bfa2c4Scliff {
48841bfa2c4Scliff 	sc->sc_remoteup = true;
48941bfa2c4Scliff 	if (sc->sc_if.if_flags & IFF_UP)
49041bfa2c4Scliff 		ifmedia_set(&sc->sc_im, IFM_ETHER | IFM_1000_T | IFM_FDX);
49141bfa2c4Scliff }
49241bfa2c4Scliff 
49341bfa2c4Scliff static void
gpn_ipm_ifdown(struct gpn_softc * sc)49441bfa2c4Scliff gpn_ipm_ifdown(struct gpn_softc *sc)
49541bfa2c4Scliff {
49641bfa2c4Scliff 	struct gpn_txinfo *ti = sc->sc_txinfo;
49741bfa2c4Scliff 	struct gpn_txinfo * const end_ti = ti + __arraycount(sc->sc_txinfo);
49841bfa2c4Scliff 
49941bfa2c4Scliff 	if (sc->sc_rxmbuf) {
50041bfa2c4Scliff 		m_freem(sc->sc_rxmbuf);
50141bfa2c4Scliff 		sc->sc_rxmbuf = NULL;
50241bfa2c4Scliff 	}
50341bfa2c4Scliff 
50441bfa2c4Scliff 	IF_PURGE(&sc->sc_if.if_snd);
50541bfa2c4Scliff 
50641bfa2c4Scliff 	for (; ti < end_ti; ti++) {
50741bfa2c4Scliff 		if (ti->ti_mbuf == NULL)
50841bfa2c4Scliff 			continue;
50941bfa2c4Scliff 		bus_dmamap_sync(sc->sc_dmat, ti->ti_map,
51041bfa2c4Scliff 		    0, ti->ti_mbuf->m_len, BUS_DMASYNC_POSTREAD);
51141bfa2c4Scliff 		bus_dmamap_unload(sc->sc_dmat, ti->ti_map);
51241bfa2c4Scliff 		m_freem(ti->ti_mbuf);
51341bfa2c4Scliff 		ti->ti_mbuf = NULL;
51441bfa2c4Scliff 	}
51541bfa2c4Scliff 	sc->sc_lastid = 0;
51641bfa2c4Scliff 	ifmedia_set(&sc->sc_im, IFM_ETHER | IFM_NONE);
51741bfa2c4Scliff 	sc->sc_remoteup = false;
51841bfa2c4Scliff }
51941bfa2c4Scliff 
52041bfa2c4Scliff static void
gpn_ipm_handler(void * arg,const void * desc)52141bfa2c4Scliff gpn_ipm_handler(void *arg, const void *desc)
52241bfa2c4Scliff {
52341bfa2c4Scliff 	struct gpn_softc * const sc = arg;
52441bfa2c4Scliff 	const ipm_gpn_desc_t * const gd = desc;
52541bfa2c4Scliff 	const ipm_gpn_ack_desc_t * const agd = desc;
52641bfa2c4Scliff 	int s;
52741bfa2c4Scliff 
52841bfa2c4Scliff 	s = splnet();
52941bfa2c4Scliff 
53041bfa2c4Scliff 	switch (gd->gd_subtype) {
53141bfa2c4Scliff 	case GPN_ACK14: gpn_free_txid(sc, agd->agd_txids[13]); /* FALLTHROUGH */
53241bfa2c4Scliff 	case GPN_ACK13: gpn_free_txid(sc, agd->agd_txids[12]); /* FALLTHROUGH */
53341bfa2c4Scliff 	case GPN_ACK12: gpn_free_txid(sc, agd->agd_txids[11]); /* FALLTHROUGH */
53441bfa2c4Scliff 	case GPN_ACK11: gpn_free_txid(sc, agd->agd_txids[10]); /* FALLTHROUGH */
53541bfa2c4Scliff 	case GPN_ACK10: gpn_free_txid(sc, agd->agd_txids[9]); /* FALLTHROUGH */
53641bfa2c4Scliff 	case GPN_ACK9: gpn_free_txid(sc, agd->agd_txids[8]); /* FALLTHROUGH */
53741bfa2c4Scliff 	case GPN_ACK8: gpn_free_txid(sc, agd->agd_txids[7]); /* FALLTHROUGH */
53841bfa2c4Scliff 	case GPN_ACK7: gpn_free_txid(sc, agd->agd_txids[6]); /* FALLTHROUGH */
53941bfa2c4Scliff 	case GPN_ACK6: gpn_free_txid(sc, agd->agd_txids[5]); /* FALLTHROUGH */
54041bfa2c4Scliff 	case GPN_ACK5: gpn_free_txid(sc, agd->agd_txids[4]); /* FALLTHROUGH */
54141bfa2c4Scliff 	case GPN_ACK4: gpn_free_txid(sc, agd->agd_txids[3]); /* FALLTHROUGH */
54241bfa2c4Scliff 	case GPN_ACK3: gpn_free_txid(sc, agd->agd_txids[2]); /* FALLTHROUGH */
54341bfa2c4Scliff 	case GPN_ACK2: gpn_free_txid(sc, agd->agd_txids[1]); /* FALLTHROUGH */
54441bfa2c4Scliff 	case GPN_ACK1: gpn_free_txid(sc, agd->agd_txids[0]); break;
54541bfa2c4Scliff 	case GPN_MOF:
54641bfa2c4Scliff 	case GPN_SOF:
54741bfa2c4Scliff 	case GPN_FRAME:
54841bfa2c4Scliff 	case GPN_EOF:
54941bfa2c4Scliff 		gpn_process_data(sc, gd);
55041bfa2c4Scliff 		break;
55141bfa2c4Scliff 	case GPN_IFUP:
55241bfa2c4Scliff 		gpn_ipm_ifup(sc);
55341bfa2c4Scliff 		break;
55441bfa2c4Scliff 	case GPN_IFDOWN:
55541bfa2c4Scliff 		gpn_ipm_ifdown(sc);
55641bfa2c4Scliff 		break;
55741bfa2c4Scliff 	default:
55841bfa2c4Scliff 		KASSERT(0);
55941bfa2c4Scliff 	}
56041bfa2c4Scliff 
56141bfa2c4Scliff 	splx(s);
56241bfa2c4Scliff }
56341bfa2c4Scliff 
56441bfa2c4Scliff static int
gpn_ifinit(struct ifnet * ifp)56541bfa2c4Scliff gpn_ifinit(struct ifnet *ifp)
56641bfa2c4Scliff {
56741bfa2c4Scliff 	struct gpn_softc * const sc = ifp->if_softc;
56841bfa2c4Scliff 	ipm_gpn_desc_t gd;
56941bfa2c4Scliff 	int error;
57041bfa2c4Scliff 
57141bfa2c4Scliff 	error = gpn_alloc_dmamaps(sc);
57241bfa2c4Scliff 	if (error)
57341bfa2c4Scliff 		return error;
57441bfa2c4Scliff 
57541bfa2c4Scliff 	memset(&gd, 0, sizeof(gd));
57641bfa2c4Scliff 	gd.gd_tag = IPM_TAG_GPN;
57741bfa2c4Scliff 	gd.gd_subtype = GPN_IFUP;
57841bfa2c4Scliff 	KASSERT(sc->sc_free > 0);
57941bfa2c4Scliff 	sc->sc_free--;
58041bfa2c4Scliff 	gemini_ipm_produce(&gd, 1);
58141bfa2c4Scliff 
58241bfa2c4Scliff 	if (sc->sc_remoteup)
58341bfa2c4Scliff 		ifmedia_set(&sc->sc_im, IFM_ETHER | IFM_1000_T | IFM_FDX);
58441bfa2c4Scliff 
58541bfa2c4Scliff 	ifp->if_flags |= IFF_RUNNING;
58641bfa2c4Scliff 
58741bfa2c4Scliff 	return error;
58841bfa2c4Scliff }
58941bfa2c4Scliff 
59041bfa2c4Scliff static void
gpn_ifstop(struct ifnet * ifp,int disable)59141bfa2c4Scliff gpn_ifstop(struct ifnet *ifp, int disable)
59241bfa2c4Scliff {
59341bfa2c4Scliff 	struct gpn_softc * const sc = ifp->if_softc;
59441bfa2c4Scliff 	ipm_gpn_desc_t gd;
59541bfa2c4Scliff 
59641bfa2c4Scliff 	memset(&gd, 0, sizeof(gd));
59741bfa2c4Scliff 	gd.gd_tag = IPM_TAG_GPN;
59841bfa2c4Scliff 	gd.gd_subtype = GPN_IFDOWN;
59941bfa2c4Scliff 	KASSERT(sc->sc_free > 0);
60041bfa2c4Scliff 	sc->sc_free--;
60141bfa2c4Scliff 	gemini_ipm_produce(&gd, 1);
60241bfa2c4Scliff 	ifp->if_flags &= ~IFF_RUNNING;
60341bfa2c4Scliff 	gpn_ipm_ifdown(sc);
60441bfa2c4Scliff 
60541bfa2c4Scliff 	if (disable) {
60641bfa2c4Scliff 		gpn_free_dmamaps(sc);
60741bfa2c4Scliff 	}
60841bfa2c4Scliff }
60941bfa2c4Scliff 
61041bfa2c4Scliff static int
gpn_ifioctl(struct ifnet * ifp,u_long cmd,void * data)61141bfa2c4Scliff gpn_ifioctl(struct ifnet *ifp, u_long cmd, void *data)
61241bfa2c4Scliff {
61341bfa2c4Scliff 	struct ifaliasreq * const ifra = data;
61441bfa2c4Scliff 	int s, error;
61541bfa2c4Scliff 
61641bfa2c4Scliff 	s = splnet();
61741bfa2c4Scliff 
61841bfa2c4Scliff 	switch (cmd) {
61941bfa2c4Scliff 	case SIOCSIFPHYADDR: {
62041bfa2c4Scliff 		const struct sockaddr_dl *sdl = satosdl(&ifra->ifra_addr);
62141bfa2c4Scliff 
62241bfa2c4Scliff 		if (sdl->sdl_family != AF_LINK) {
62341bfa2c4Scliff 			error = EINVAL;
62441bfa2c4Scliff 			break;
62541bfa2c4Scliff 		}
62641bfa2c4Scliff 
62741bfa2c4Scliff 		if_set_sadl(ifp, CLLADDR(sdl), ETHER_ADDR_LEN, false);
62841bfa2c4Scliff 		error = 0;
62941bfa2c4Scliff 		break;
63041bfa2c4Scliff 	}
63141bfa2c4Scliff 	default:
63241bfa2c4Scliff 		error = ether_ioctl(ifp, cmd, data);
63341bfa2c4Scliff 		if (error == ENETRESET)
63441bfa2c4Scliff 			error = 0;
63541bfa2c4Scliff 		break;
63641bfa2c4Scliff 	}
63741bfa2c4Scliff 
63841bfa2c4Scliff 	splx(s);
63941bfa2c4Scliff 	return error;
64041bfa2c4Scliff }
64141bfa2c4Scliff 
64241bfa2c4Scliff static int
gpn_mediachange(struct ifnet * ifp)64341bfa2c4Scliff gpn_mediachange(struct ifnet *ifp)
64441bfa2c4Scliff {
64541bfa2c4Scliff 	return 0;
64641bfa2c4Scliff }
64741bfa2c4Scliff 
64841bfa2c4Scliff static void
gpn_mediastatus(struct ifnet * ifp,struct ifmediareq * imr)64941bfa2c4Scliff gpn_mediastatus(struct ifnet *ifp, struct ifmediareq *imr)
65041bfa2c4Scliff {
65141bfa2c4Scliff 	struct gpn_softc * const sc = ifp->if_softc;
65241bfa2c4Scliff 	imr->ifm_active = sc->sc_im.ifm_cur->ifm_media;
65341bfa2c4Scliff }
65441bfa2c4Scliff 
65541bfa2c4Scliff static int
gpn_match(device_t parent,cfdata_t cf,void * aux)65641bfa2c4Scliff gpn_match(device_t parent, cfdata_t cf, void *aux)
65741bfa2c4Scliff {
65841bfa2c4Scliff 	return strcmp(gpn_cd.cd_name, aux) == 0;
65941bfa2c4Scliff }
66041bfa2c4Scliff 
66141bfa2c4Scliff static void
gpn_attach(device_t parent,device_t self,void * aux)66241bfa2c4Scliff gpn_attach(device_t parent, device_t self, void *aux)
66341bfa2c4Scliff {
66441bfa2c4Scliff 	struct gpn_softc * const sc = device_private(self);
66541bfa2c4Scliff 	struct ifnet * const ifp = &sc->sc_if;
66641bfa2c4Scliff 	char enaddr[6];
66741bfa2c4Scliff 
66841bfa2c4Scliff 	enaddr[0] = 2;
66941bfa2c4Scliff 	enaddr[1] = 0;
67041bfa2c4Scliff 	enaddr[2] = 0;
67141bfa2c4Scliff 	enaddr[3] = 0;
67241bfa2c4Scliff 	enaddr[4] = 0;
67341bfa2c4Scliff #ifdef GEMINI_MASTER
67441bfa2c4Scliff 	enaddr[5] = 0;
67541bfa2c4Scliff #elif defined(GEMINI_SLAVE)
67641bfa2c4Scliff 	enaddr[5] = 1;
67741bfa2c4Scliff #else
67841bfa2c4Scliff #error not master nor slave
67941bfa2c4Scliff #endif
68041bfa2c4Scliff 
68141bfa2c4Scliff 	aprint_normal("\n");
68241bfa2c4Scliff 	aprint_naive("\n");
68341bfa2c4Scliff 	sc->sc_dev = self;
68441bfa2c4Scliff 	sc->sc_dmat = &gemini_bus_dma_tag;
68541bfa2c4Scliff 
686b9958efeSmsaitoh 	/* Setup ifmedia interface */
687b9958efeSmsaitoh 	sc->sc_ec.ec_ifmedia = &sc->sc_im;
68841bfa2c4Scliff 	ifmedia_init(&sc->sc_im, 0, gpn_mediachange, gpn_mediastatus);
689b9958efeSmsaitoh 	/* Pretend we are full-duplex gigabit ethernet. */
69041bfa2c4Scliff 	ifmedia_add(&sc->sc_im, IFM_ETHER | IFM_1000_T | IFM_FDX, 0, NULL);
69141bfa2c4Scliff 	ifmedia_add(&sc->sc_im, IFM_ETHER | IFM_NONE, 0, NULL);
69241bfa2c4Scliff 	ifmedia_set(&sc->sc_im, IFM_ETHER | IFM_NONE);
69341bfa2c4Scliff 
69441bfa2c4Scliff 	strlcpy(ifp->if_xname, device_xname(self), sizeof(ifp->if_xname));
69541bfa2c4Scliff 	ifp->if_softc = sc;
69641bfa2c4Scliff 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
69741bfa2c4Scliff 	ifp->if_ioctl = gpn_ifioctl;
69841bfa2c4Scliff 	ifp->if_start = gpn_ifstart;
69941bfa2c4Scliff 	ifp->if_init  = gpn_ifinit;
70041bfa2c4Scliff 	ifp->if_stop  = gpn_ifstop;
70141bfa2c4Scliff 
70241bfa2c4Scliff 	IFQ_SET_READY(&ifp->if_snd);
70341bfa2c4Scliff 
70441bfa2c4Scliff 	sc->sc_ec.ec_capabilities = ETHERCAP_VLAN_MTU | ETHERCAP_JUMBO_MTU;
70541bfa2c4Scliff 
70641bfa2c4Scliff 	if_attach(ifp);
70741bfa2c4Scliff 	ether_ifattach(ifp, enaddr);
70841bfa2c4Scliff 
70941bfa2c4Scliff 	sc->sc_free = MAX_TXACTIVE*2;
71041bfa2c4Scliff 	sc->sc_ih = gemini_ipm_register(IPM_TAG_GPN, IPL_SOFTNET, sc->sc_free,
71141bfa2c4Scliff 	    gpn_ipm_handler, gpn_ipm_rebate, sc);
71241bfa2c4Scliff 	KASSERT(sc->sc_ih);
71341bfa2c4Scliff 
71441bfa2c4Scliff 	sc->sc_ack_desc.agd_tag = IPM_TAG_GPN;
71541bfa2c4Scliff }
71641bfa2c4Scliff 
71741bfa2c4Scliff void gpn_print_gd(ipm_gpn_desc_t *);
71841bfa2c4Scliff void
gpn_print_gd(ipm_gpn_desc_t * gd)71941bfa2c4Scliff gpn_print_gd(ipm_gpn_desc_t *gd)
72041bfa2c4Scliff {
72141bfa2c4Scliff 	printf("%s: %p\n", __FUNCTION__, gd);
72241bfa2c4Scliff 	printf("\ttag %d, subtype %d, id %d, pktlen64 %d\n",
72341bfa2c4Scliff 		gd->gd_tag, gd->gd_subtype, gd->gd_txid, gd->gd_pktlen64);
72441bfa2c4Scliff 	printf("\tlen1 %d, len2 %d, addr1 %#x, addr2 %#x\n",
72541bfa2c4Scliff 		gd->gd_len1, gd->gd_len2, gd->gd_addr1, gd->gd_addr2);
72641bfa2c4Scliff }
72741bfa2c4Scliff 
72841bfa2c4Scliff CFATTACH_DECL_NEW(gpn, sizeof(struct gpn_softc),
72941bfa2c4Scliff     gpn_match, gpn_attach, NULL, NULL);
730