xref: /netbsd-src/sys/arch/arm/gemini/if_gpn.c (revision 7f21db1c0118155e0dd40b75182e30c589d9f63e)
1 /* $NetBSD: if_gpn.c,v 1.2 2010/01/19 22:06:19 pooka Exp $ */
2 /*-
3  * Copyright (c) 2008 The NetBSD Foundation, Inc.
4  * All rights reserved.
5  *
6  * This code is derived from software contributed to The NetBSD Foundation
7  * by Matt Thomas <matt@3am-software.com>
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
19  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
22  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include <sys/cdefs.h>
32 
33 #include "opt_gemini.h"
34 
35 __KERNEL_RCSID(0, "$NetBSD: if_gpn.c,v 1.2 2010/01/19 22:06:19 pooka Exp $");
36 
37 #include <sys/param.h>
38 #include <sys/device.h>
39 #include <sys/mbuf.h>
40 #include <sys/ioctl.h>
41 
42 #include <net/if.h>
43 #include <net/if_media.h>
44 #include <net/if_ether.h>
45 #include <net/if_dl.h>
46 
47 #include <net/bpf.h>
48 
49 #include <machine/bus.h>
50 
51 #include <arm/gemini/gemini_var.h>
52 #include <arm/gemini/gemini_ipm.h>
53 
54 #define	GPN_MOF		0x00	/* Middle Of Frame */
55 #define	GPN_SOF		0x01	/* Start of Frame */
56 #define	GPN_EOF		0x02	/* End of Frame */
57 #define	GPN_FRAME	0x03	/* Complete Frame */
58 
59 #define	GPN_IFUP	0x05	/* partner is up */
60 #define	GPN_IFDOWN	0x06	/* partner is down */
61 
62 #define	GPN_ACK0	0x10	/* placeholder */
63 #define	GPN_ACK1	0x11	/* Ack 1 descriptor */
64 #define	GPN_ACK2	0x12	/* Ack 2 descriptors */
65 #define	GPN_ACK3	0x13	/* Ack 3 descriptors */
66 #define	GPN_ACK4	0x14	/* Ack 4 descriptors */
67 #define	GPN_ACK5	0x15	/* Ack 5 descriptors */
68 #define	GPN_ACK6	0x16	/* Ack 6 descriptors */
69 #define	GPN_ACK7	0x17	/* Ack 7 descriptors */
70 #define	GPN_ACK8	0x18	/* Ack 8 descriptors */
71 #define	GPN_ACK9	0x19	/* Ack 9 descriptors */
72 #define	GPN_ACK10	0x1a	/* Ack 10 descriptors */
73 #define	GPN_ACK11	0x1b	/* Ack 11 descriptors */
74 #define	GPN_ACK12	0x1c	/* Ack 12 descriptors */
75 #define	GPN_ACK13	0x1d	/* Ack 13 descriptors */
76 #define	GPN_ACK14	0x1e	/* Ack 14 descriptors */
77 
78 typedef struct {
79 	uint8_t gd_tag;
80 	uint8_t gd_subtype;
81 	uint8_t gd_txid;
82 	uint8_t gd_pktlen64;
83 	uint16_t gd_len1;
84 	uint16_t gd_len2;
85 	uint32_t gd_addr1;
86 	uint32_t gd_addr2;
87 } ipm_gpn_desc_t;
88 
89 typedef struct {
90 	uint8_t agd_tag;
91 	uint8_t agd_subtype;
92 	uint8_t agd_txids[14];
93 } ipm_gpn_ack_desc_t;
94 
95 #define	MAX_TXACTIVE	60
96 
97 struct gpn_txinfo {
98 	struct mbuf *ti_mbuf;
99 	bus_dmamap_t ti_map;
100 };
101 
102 struct gpn_softc {
103 	device_t sc_dev;
104 	bus_dma_tag_t sc_dmat;
105 	struct ifmedia sc_im;
106 	struct ethercom sc_ec;
107 #define	sc_if sc_ec.ec_if
108 	size_t sc_free;
109 	size_t sc_txactive;
110 	void *sc_ih;
111 	ipm_gpn_ack_desc_t sc_ack_desc;
112 	struct mbuf *sc_rxmbuf;
113 	struct gpn_txinfo sc_txinfo[MAX_TXACTIVE];
114 	uint8_t sc_lastid;
115 	bool sc_remoteup;		/* remote side up? */
116 };
117 
118 CTASSERT((GPN_SOF | GPN_EOF) == GPN_FRAME);
119 CTASSERT((GPN_SOF & GPN_EOF) == 0);
120 
121 extern struct cfdriver gpn_cd;
122 
123 static void gpn_ifstart(struct ifnet *);
124 
125 #ifdef GPNDEBUG
126 static uint32_t
127 m_crc32_le(struct mbuf *m)
128 {
129 	static const uint32_t crctab[] = {
130 		0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
131 		0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
132 		0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
133 		0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
134 	};
135 	uint32_t crc;
136 	size_t i;
137 
138 	crc = 0xffffffffU;	/* initial value */
139 
140 	for (; m; m = m->m_next) {
141 		for (i = 0; i < m->m_len; i++) {
142 			crc ^= m->m_data[i];
143 			crc = (crc >> 4) ^ crctab[crc & 0xf];
144 			crc = (crc >> 4) ^ crctab[crc & 0xf];
145 		}
146 	}
147 
148 	return (crc);
149 }
150 #endif
151 
152 static void
153 gpn_free_dmamaps(struct gpn_softc *sc)
154 {
155 	struct gpn_txinfo *ti = sc->sc_txinfo;
156 	struct gpn_txinfo * const end_ti = ti + __arraycount(sc->sc_txinfo);
157 
158 	for (; ti < end_ti; ti++) {
159 		if (ti->ti_map == NULL)
160 			continue;
161 		bus_dmamap_destroy(sc->sc_dmat, ti->ti_map);
162 		ti->ti_map = NULL;
163 	}
164 }
165 
166 static int
167 gpn_alloc_dmamaps(struct gpn_softc *sc)
168 {
169 	struct gpn_txinfo *ti = sc->sc_txinfo;
170 	struct gpn_txinfo * const end_ti = ti + __arraycount(sc->sc_txinfo);
171 	int error;
172 
173 	for (error = 0; ti < end_ti; ti++) {
174 		if (ti->ti_map != NULL)
175 			continue;
176 		error = bus_dmamap_create(sc->sc_dmat,
177 		    10000, 2, 8192, 0,
178 		    BUS_DMA_ALLOCNOW|BUS_DMA_WAITOK,
179 		    &ti->ti_map);
180 		if (error)
181 			break;
182 	}
183 
184 	if (error)
185 		gpn_free_dmamaps(sc);
186 
187 	return error;
188 }
189 
190 static bool
191 gpn_add_data(struct gpn_softc *sc, bus_addr_t addr, bus_size_t len)
192 {
193 	struct mbuf *m, *m0;
194 	size_t space;
195 
196 	m = sc->sc_rxmbuf;
197 	KASSERT(m != NULL);
198 
199 	m->m_pkthdr.len += len;
200 
201 	while (m->m_next != NULL)
202 		m = m->m_next;
203 
204 	KASSERT(len > 0);
205 	space = M_TRAILINGSPACE(m);
206 	for (;;) {
207 		if (space > 0) {
208 			if (len < space)
209 				space = len;
210 			gemini_ipm_copyin(mtod(m, uint8_t *) + m->m_len, addr,
211 			    space);
212 			len -= space;
213 			m->m_len += space;
214 			if (len == 0)
215 				return true;
216 			addr += space;
217 		}
218 		MGET(m0, M_DONTWAIT, MT_DATA);
219 		if (m0 == NULL)
220 			break;
221 		space = MLEN;
222 		if (len > space) {
223 			MCLGET(m0, M_DONTWAIT);
224 			if (m0->m_flags & M_EXT)
225 				space = MCLBYTES;
226 		}
227 		m->m_len = 0;
228 		m->m_next = m0;
229 		m = m0;
230 	}
231 	return false;
232 }
233 
234 static void
235 gpn_ack_txid(struct gpn_softc *sc, unsigned int txid)
236 {
237 	ipm_gpn_ack_desc_t * const agd = &sc->sc_ack_desc;
238 	agd->agd_txids[agd->agd_subtype] = txid;
239 	if (++agd->agd_subtype == __arraycount(agd->agd_txids)) {
240 		agd->agd_subtype += GPN_ACK0;
241 		sc->sc_free--;
242 		gemini_ipm_produce(agd, 1);
243 		agd->agd_subtype = 0;
244 	}
245 }
246 
247 static void
248 gpn_process_data(struct gpn_softc *sc, const ipm_gpn_desc_t *gd)
249 {
250 	struct ifnet * const ifp = &sc->sc_if;
251 	size_t pktlen = gd->gd_pktlen64 * 64;
252 	unsigned int subtype = gd->gd_subtype;
253 	bool ok;
254 
255 	if ((subtype & GPN_SOF) == 0 && sc->sc_rxmbuf == NULL) {
256 		ifp->if_ierrors++;
257 		goto out;
258 	}
259 
260 	if ((subtype & GPN_SOF) && sc->sc_rxmbuf != NULL) {
261 		ifp->if_ierrors++;
262 		m_freem(sc->sc_rxmbuf);
263 		sc->sc_rxmbuf = NULL;
264 	}
265 
266 	if (sc->sc_rxmbuf == NULL) {
267 		struct mbuf *m;
268 		MGETHDR(m, M_DONTWAIT, MT_DATA);
269 		if (m == NULL) {
270 			ifp->if_ierrors++;
271 			goto out;
272 		}
273 		if (pktlen > MHLEN - 2) {
274 			MCLGET(m, M_DONTWAIT);
275 			if ((m->m_flags & M_EXT) == 0) {
276 				ifp->if_ierrors++;
277 				m_free(m);
278 				goto out;
279 			}
280 		}
281 		m->m_data += 2;	/* makes ethernet payload 32bit aligned */
282 		m->m_len = 0;
283 		m->m_pkthdr.len = 0;
284 		sc->sc_rxmbuf = m;
285 	}
286 
287 	ok = gpn_add_data(sc, gd->gd_addr1, gd->gd_len1);
288 	if (ok && gd->gd_addr2 && gd->gd_len2)
289 		ok = gpn_add_data(sc, gd->gd_addr2, gd->gd_len2);
290 	if (!ok) {
291 		ifp->if_ierrors++;
292 		m_freem(sc->sc_rxmbuf);
293 		sc->sc_rxmbuf = NULL;
294 		goto out;
295 	}
296 
297 	if (subtype & GPN_EOF) {
298 		struct mbuf *m;
299 		m = sc->sc_rxmbuf;
300 		sc->sc_rxmbuf = NULL;
301 		m->m_pkthdr.rcvif = ifp;
302 		KASSERT(((m->m_pkthdr.len + 63) >> 6) == gd->gd_pktlen64);
303 		ifp->if_ipackets++;
304 		ifp->if_ibytes += m->m_pkthdr.len;
305 		if (ifp->if_bpf)
306 			bpf_ops->bpf_mtap(ifp->if_bpf, m);
307 #ifdef GPNDEBUG
308 		printf("%s: rx len=%d crc=%#x\n", ifp->if_xname,
309 		    m->m_pkthdr.len, m_crc32_le(m));
310 #endif
311 		(*ifp->if_input)(ifp, m);
312 	}
313 
314 out:
315 	gpn_ack_txid(sc, gd->gd_txid);
316 }
317 
318 static void
319 gpn_free_txid(struct gpn_softc *sc, size_t txid)
320 {
321 	struct gpn_txinfo * const ti = sc->sc_txinfo + txid;
322 
323 	KASSERT(txid < MAX_TXACTIVE);
324 
325 	if (ti->ti_mbuf == NULL)
326 		return;
327 
328 	bus_dmamap_sync(sc->sc_dmat, ti->ti_map,
329 	    0, ti->ti_mbuf->m_len, BUS_DMASYNC_POSTREAD);
330 	bus_dmamap_unload(sc->sc_dmat, ti->ti_map);
331 	m_freem(ti->ti_mbuf);
332 	ti->ti_mbuf = NULL;
333 	sc->sc_txactive--;
334 	KASSERT(sc->sc_txactive < MAX_TXACTIVE);
335 	if (sc->sc_if.if_flags & IFF_OACTIVE) {
336 		sc->sc_if.if_flags &= ~IFF_OACTIVE;
337 		gpn_ifstart(&sc->sc_if);
338 	}
339 
340 }
341 
342 static void
343 gpn_ipm_rebate(void *arg, size_t count)
344 {
345 	struct gpn_softc * const sc = arg;
346 	int s;
347 
348 	s = splnet();
349 	sc->sc_free += count;
350 
351 	sc->sc_if.if_flags &= ~IFF_OACTIVE;
352 	gpn_ifstart(&sc->sc_if);
353 	splx(s);
354 }
355 
356 static void
357 gpn_ifstart(struct ifnet *ifp)
358 {
359 	struct gpn_softc * const sc = ifp->if_softc;
360 
361 	for (;;) {
362 		struct mbuf *m, *m0;
363 		ipm_gpn_desc_t gd;
364 		ipm_gpn_desc_t *last_gd;
365 		size_t count;
366 
367 		if (sc->sc_free == 0) {
368 			ifp->if_flags |= IFF_OACTIVE;
369 			break;
370 		}
371 
372 		IF_DEQUEUE(&ifp->if_snd, m);
373 		if (!m)
374 			break;
375 
376 		if ((ifp->if_flags & IFF_UP) == 0) {
377 			m_freem(m);
378 			continue;
379 		}
380 
381 		/*
382 		 * Make sure to send any pending acks first.
383 		 */
384 		if (sc->sc_ack_desc.agd_subtype) {
385 			sc->sc_free--;
386 			sc->sc_ack_desc.agd_subtype += GPN_ACK0;
387 			gemini_ipm_produce(&sc->sc_ack_desc, 1);
388 			sc->sc_ack_desc.agd_subtype = 0;
389 		}
390 
391 		/*
392 		 * Let's find out how many mbufs we are using.
393 		 */
394 		for (m0 = m, count = 0; m0; m0 = m0->m_next) {
395 			if (m0->m_len == 0)
396 				continue;
397 			count++;
398 		}
399 
400 		/*
401 		 * Make sure there is always enough room.
402 		 */
403 		if (sc->sc_free < count
404 		    || sc->sc_txactive + count > MAX_TXACTIVE) {
405 			IF_PREPEND(&ifp->if_snd, m);
406 			ifp->if_flags |= IFF_OACTIVE;
407 			return;
408 		}
409 
410 		if (ifp->if_bpf)
411 			bpf_ops->bpf_mtap(ifp->if_bpf, m);
412 #ifdef GPNDEBUG
413 		printf("%s: tx len=%d crc=%#x\n", ifp->if_xname,
414 		    m->m_pkthdr.len, m_crc32_le(m));
415 #endif
416 
417 		last_gd = NULL;
418 		gd.gd_tag = IPM_TAG_GPN;
419 		gd.gd_subtype = GPN_SOF;
420 		gd.gd_pktlen64 = (m->m_pkthdr.len + 63) >> 6;
421 		for (; m != NULL; m = m0) {
422 			struct gpn_txinfo *ti;
423 			bus_dmamap_t map;
424 			size_t id;
425 			int error;
426 
427 			m0 = m->m_next;
428 			m->m_next = NULL;
429 			if (m->m_len == 0) {
430 				m_free(m);
431 				continue;
432 			}
433 			if (last_gd) {
434 				sc->sc_txactive++;
435 				sc->sc_free--;
436 				gemini_ipm_produce(last_gd, 1);
437 				last_gd = NULL;
438 				gd.gd_subtype = GPN_MOF;
439 			}
440 			for (id = sc->sc_lastid;
441 			     sc->sc_txinfo[id].ti_mbuf != NULL;) {
442 				if (++id == __arraycount(sc->sc_txinfo))
443 					id = 0;
444 			}
445 			KASSERT(id < MAX_TXACTIVE);
446 			ti = sc->sc_txinfo + id;
447 			map = ti->ti_map;
448 			error = bus_dmamap_load(sc->sc_dmat, map,
449 			    mtod(m, void *), m->m_len, NULL,
450 			    BUS_DMA_READ|BUS_DMA_NOWAIT);
451 			if (error) {
452 				ifp->if_oerrors++;
453 				m_freem(m);
454 				break;
455 			}
456 			bus_dmamap_sync(sc->sc_dmat, map, 0,
457 			    m->m_len, BUS_DMASYNC_PREREAD);
458 			KASSERT(map->dm_nsegs > 0);
459 			KASSERT(map->dm_nsegs <= 2);
460 			KASSERT(map->dm_segs[0].ds_addr != 0);
461 			gd.gd_len1 = map->dm_segs[0].ds_len;
462 			gd.gd_addr1 = map->dm_segs[0].ds_addr;
463 			if (map->dm_nsegs == 1) {
464 				gd.gd_len2 = 0;
465 				gd.gd_addr2 = 0;
466 			} else {
467 				KASSERT(map->dm_segs[0].ds_addr != 0);
468 				gd.gd_len2 = map->dm_segs[1].ds_len;
469 				gd.gd_addr2 = map->dm_segs[1].ds_addr;
470 			}
471 
472 			gd.gd_txid = id;
473 			ti->ti_mbuf = m;
474 			last_gd = &gd;
475 			ifp->if_obytes += m->m_len;
476 		}
477 		ifp->if_opackets++;
478 		last_gd->gd_subtype |= GPN_EOF;
479 		sc->sc_txactive++;
480 		sc->sc_free--;
481 		gemini_ipm_produce(last_gd, 1);
482 	}
483 }
484 
485 static void
486 gpn_ipm_ifup(struct gpn_softc *sc)
487 {
488 	sc->sc_remoteup = true;
489 	if (sc->sc_if.if_flags & IFF_UP)
490 		ifmedia_set(&sc->sc_im, IFM_ETHER|IFM_1000_T|IFM_FDX);
491 }
492 
493 static void
494 gpn_ipm_ifdown(struct gpn_softc *sc)
495 {
496 	struct gpn_txinfo *ti = sc->sc_txinfo;
497 	struct gpn_txinfo * const end_ti = ti + __arraycount(sc->sc_txinfo);
498 
499 	if (sc->sc_rxmbuf) {
500 		m_freem(sc->sc_rxmbuf);
501 		sc->sc_rxmbuf = NULL;
502 	}
503 
504 	IF_PURGE(&sc->sc_if.if_snd);
505 
506 	for (; ti < end_ti; ti++) {
507 		if (ti->ti_mbuf == NULL)
508 			continue;
509 		bus_dmamap_sync(sc->sc_dmat, ti->ti_map,
510 		    0, ti->ti_mbuf->m_len, BUS_DMASYNC_POSTREAD);
511 		bus_dmamap_unload(sc->sc_dmat, ti->ti_map);
512 		m_freem(ti->ti_mbuf);
513 		ti->ti_mbuf = NULL;
514 	}
515 	sc->sc_lastid = 0;
516 	ifmedia_set(&sc->sc_im, IFM_ETHER|IFM_NONE);
517 	sc->sc_remoteup = false;
518 }
519 
520 static void
521 gpn_ipm_handler(void *arg, const void *desc)
522 {
523 	struct gpn_softc * const sc = arg;
524 	const ipm_gpn_desc_t * const gd = desc;
525 	const ipm_gpn_ack_desc_t * const agd = desc;
526 	int s;
527 
528 	s = splnet();
529 
530 	switch (gd->gd_subtype) {
531 	case GPN_ACK14: gpn_free_txid(sc, agd->agd_txids[13]); /* FALLTHROUGH */
532 	case GPN_ACK13: gpn_free_txid(sc, agd->agd_txids[12]); /* FALLTHROUGH */
533 	case GPN_ACK12: gpn_free_txid(sc, agd->agd_txids[11]); /* FALLTHROUGH */
534 	case GPN_ACK11: gpn_free_txid(sc, agd->agd_txids[10]); /* FALLTHROUGH */
535 	case GPN_ACK10: gpn_free_txid(sc, agd->agd_txids[9]); /* FALLTHROUGH */
536 	case GPN_ACK9: gpn_free_txid(sc, agd->agd_txids[8]); /* FALLTHROUGH */
537 	case GPN_ACK8: gpn_free_txid(sc, agd->agd_txids[7]); /* FALLTHROUGH */
538 	case GPN_ACK7: gpn_free_txid(sc, agd->agd_txids[6]); /* FALLTHROUGH */
539 	case GPN_ACK6: gpn_free_txid(sc, agd->agd_txids[5]); /* FALLTHROUGH */
540 	case GPN_ACK5: gpn_free_txid(sc, agd->agd_txids[4]); /* FALLTHROUGH */
541 	case GPN_ACK4: gpn_free_txid(sc, agd->agd_txids[3]); /* FALLTHROUGH */
542 	case GPN_ACK3: gpn_free_txid(sc, agd->agd_txids[2]); /* FALLTHROUGH */
543 	case GPN_ACK2: gpn_free_txid(sc, agd->agd_txids[1]); /* FALLTHROUGH */
544 	case GPN_ACK1: gpn_free_txid(sc, agd->agd_txids[0]); break;
545 	case GPN_MOF:
546 	case GPN_SOF:
547 	case GPN_FRAME:
548 	case GPN_EOF:
549 		gpn_process_data(sc, gd);
550 		break;
551 	case GPN_IFUP:
552 		gpn_ipm_ifup(sc);
553 		break;
554 	case GPN_IFDOWN:
555 		gpn_ipm_ifdown(sc);
556 		break;
557 	default:
558 		KASSERT(0);
559 	}
560 
561 	splx(s);
562 }
563 
564 static int
565 gpn_ifinit(struct ifnet *ifp)
566 {
567 	struct gpn_softc * const sc = ifp->if_softc;
568 	ipm_gpn_desc_t gd;
569 	int error;
570 
571 	error = gpn_alloc_dmamaps(sc);
572 	if (error)
573 		return error;
574 
575 	memset(&gd, 0, sizeof(gd));
576 	gd.gd_tag = IPM_TAG_GPN;
577 	gd.gd_subtype = GPN_IFUP;
578 	KASSERT(sc->sc_free > 0);
579 	sc->sc_free--;
580 	gemini_ipm_produce(&gd, 1);
581 
582 	if (sc->sc_remoteup)
583 		ifmedia_set(&sc->sc_im, IFM_ETHER|IFM_1000_T|IFM_FDX);
584 
585 	ifp->if_flags |= IFF_RUNNING;
586 
587 	return error;
588 }
589 
590 static void
591 gpn_ifstop(struct ifnet *ifp, int disable)
592 {
593 	struct gpn_softc * const sc = ifp->if_softc;
594 	ipm_gpn_desc_t gd;
595 
596 	memset(&gd, 0, sizeof(gd));
597 	gd.gd_tag = IPM_TAG_GPN;
598 	gd.gd_subtype = GPN_IFDOWN;
599 	KASSERT(sc->sc_free > 0);
600 	sc->sc_free--;
601 	gemini_ipm_produce(&gd, 1);
602 	ifp->if_flags &= ~IFF_RUNNING;
603 	gpn_ipm_ifdown(sc);
604 
605 	if (disable) {
606 		gpn_free_dmamaps(sc);
607 	}
608 }
609 
610 static int
611 gpn_ifioctl(struct ifnet *ifp, u_long cmd, void *data)
612 {
613 	struct gpn_softc * const sc = ifp->if_softc;
614 	struct ifreq * const ifr = data;
615 	struct ifaliasreq * const ifra = data;
616 	int s, error;
617 
618 	s = splnet();
619 
620 	switch (cmd) {
621 	case SIOCSIFMEDIA:
622 	case SIOCGIFMEDIA:
623 		error = ifmedia_ioctl(ifp, ifr, &sc->sc_im, cmd);
624 		break;
625 	case SIOCSIFPHYADDR: {
626 		const struct sockaddr_dl *sdl = satosdl(&ifra->ifra_addr);
627 
628 		if (sdl->sdl_family != AF_LINK) {
629 			error = EINVAL;
630 			break;
631 		}
632 
633 		if_set_sadl(ifp, CLLADDR(sdl), ETHER_ADDR_LEN, false);
634 		error = 0;
635 		break;
636 	}
637 	default:
638 		error = ether_ioctl(ifp, cmd, data);
639 		if (error == ENETRESET)
640 			error = 0;
641 		break;
642 	}
643 
644 	splx(s);
645 	return error;
646 }
647 
648 static int
649 gpn_mediachange(struct ifnet *ifp)
650 {
651 	return 0;
652 }
653 
654 static void
655 gpn_mediastatus(struct ifnet *ifp, struct ifmediareq *imr)
656 {
657 	struct gpn_softc * const sc = ifp->if_softc;
658 	imr->ifm_active = sc->sc_im.ifm_cur->ifm_media;
659 }
660 
661 static int
662 gpn_match(device_t parent, cfdata_t cf, void *aux)
663 {
664 	return strcmp(gpn_cd.cd_name, aux) == 0;
665 }
666 
667 static void
668 gpn_attach(device_t parent, device_t self, void *aux)
669 {
670 	struct gpn_softc * const sc = device_private(self);
671 	struct ifnet * const ifp = &sc->sc_if;
672 	char enaddr[6];
673 
674 	enaddr[0] = 2;
675 	enaddr[1] = 0;
676 	enaddr[2] = 0;
677 	enaddr[3] = 0;
678 	enaddr[4] = 0;
679 #ifdef GEMINI_MASTER
680 	enaddr[5] = 0;
681 #elif defined(GEMINI_SLAVE)
682 	enaddr[5] = 1;
683 #else
684 #error not master nor slave
685 #endif
686 
687 	aprint_normal("\n");
688 	aprint_naive("\n");
689 	sc->sc_dev = self;
690 	sc->sc_dmat = &gemini_bus_dma_tag;
691 
692 	/*
693 	 * Pretend we are full-duplex gigabit ethernet.
694 	 */
695 	ifmedia_init(&sc->sc_im, 0, gpn_mediachange, gpn_mediastatus);
696 	ifmedia_add(&sc->sc_im, IFM_ETHER|IFM_1000_T|IFM_FDX, 0, NULL);
697 	ifmedia_add(&sc->sc_im, IFM_ETHER|IFM_NONE, 0, NULL);
698 	ifmedia_set(&sc->sc_im, IFM_ETHER|IFM_NONE);
699 
700 	strlcpy(ifp->if_xname, device_xname(self), sizeof(ifp->if_xname));
701 	ifp->if_softc = sc;
702 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
703 	ifp->if_ioctl = gpn_ifioctl;
704 	ifp->if_start = gpn_ifstart;
705 	ifp->if_init  = gpn_ifinit;
706 	ifp->if_stop  = gpn_ifstop;
707 
708 	IFQ_SET_READY(&ifp->if_snd);
709 
710 	sc->sc_ec.ec_capabilities = ETHERCAP_VLAN_MTU | ETHERCAP_JUMBO_MTU;
711 
712 	if_attach(ifp);
713 	ether_ifattach(ifp, enaddr);
714 
715 	sc->sc_free = MAX_TXACTIVE*2;
716 	sc->sc_ih = gemini_ipm_register(IPM_TAG_GPN, IPL_SOFTNET, sc->sc_free,
717 	    gpn_ipm_handler, gpn_ipm_rebate, sc);
718 	KASSERT(sc->sc_ih);
719 
720 	sc->sc_ack_desc.agd_tag = IPM_TAG_GPN;
721 }
722 
723 void gpn_print_gd(ipm_gpn_desc_t *);
724 void
725 gpn_print_gd(ipm_gpn_desc_t *gd)
726 {
727 	printf("%s: %p\n", __FUNCTION__, gd);
728 	printf("\ttag %d, subtype %d, id %d, pktlen64 %d\n",
729 		gd->gd_tag, gd->gd_subtype, gd->gd_txid, gd->gd_pktlen64);
730 	printf("\tlen1 %d, len2 %d, addr1 %#x, addr2 %#x\n",
731 		gd->gd_len1, gd->gd_len2, gd->gd_addr1, gd->gd_addr2);
732 }
733 
734 CFATTACH_DECL_NEW(gpn, sizeof(struct gpn_softc),
735     gpn_match, gpn_attach, NULL, NULL);
736