1 /*	$NetBSD: pdq_ifsubr.c,v 1.57 2016/06/10 13:27:13 ozaki-r Exp $	*/
2 
3 /*-
4  * Copyright (c) 1995, 1996 Matt Thomas <matt@3am-software.com>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. The name of the author may not be used to endorse or promote products
13  *    derived from this software without specific prior written permission
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  * Id: pdq_ifsubr.c,v 1.12 1997/06/05 01:56:35 thomas Exp
27  *
28  */
29 
30 /*
31  * DEC PDQ FDDI Controller; code for BSD derived operating systems
32  *
33  *	This module provide bus independent BSD specific O/S functions.
34  *	(ie. it provides an ifnet interface to the rest of the system)
35  */
36 
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: pdq_ifsubr.c,v 1.57 2016/06/10 13:27:13 ozaki-r Exp $");
39 
40 #ifdef __NetBSD__
41 #include "opt_inet.h"
42 #endif
43 
44 #include <sys/param.h>
45 #include <sys/kernel.h>
46 #include <sys/mbuf.h>
47 #include <sys/protosw.h>
48 #include <sys/socket.h>
49 #include <sys/ioctl.h>
50 #include <sys/errno.h>
51 #include <sys/malloc.h>
52 #if defined(__FreeBSD__) && BSD < 199401
53 #include <sys/devconf.h>
54 #elif defined(__bsdi__) || defined(__NetBSD__)
55 #include <sys/device.h>
56 #endif
57 
58 #include <net/if.h>
59 #include <net/if_types.h>
60 #include <net/if_dl.h>
61 #if !defined(__NetBSD__)
62 #include <net/route.h>
63 #endif
64 
65 #include <net/bpf.h>
66 #include <net/bpfdesc.h>
67 
68 #ifdef INET
69 #include <netinet/in.h>
70 #include <netinet/in_systm.h>
71 #include <netinet/in_var.h>
72 #include <netinet/ip.h>
73 #if defined(__NetBSD__)
74 #include <netinet/if_inarp.h>
75 #endif
76 #endif
77 #if defined(__FreeBSD__)
78 #include <netinet/if_ether.h>
79 #include <netinet/if_fddi.h>
80 #else
81 #include <net/if_fddi.h>
82 #endif
83 
84 #if defined(__bsdi__)
85 #include <netinet/if_ether.h>
86 #include <i386/isa/isavar.h>
87 #endif
88 
89 
90 #ifndef __NetBSD__
91 #include <vm/vm.h>
92 #endif
93 
94 #if defined(__FreeBSD__)
95 /*
96  * Yet another specific ifdef for FreeBSD as it diverges...
97  */
98 #include <dev/pdq/pdqvar.h>
99 #include <dev/pdq/pdqreg.h>
100 #else
101 #include "pdqvar.h"
102 #include "pdqreg.h"
103 #endif
104 
105 void
pdq_ifinit(pdq_softc_t * sc)106 pdq_ifinit(
107     pdq_softc_t *sc)
108 {
109     if (sc->sc_if.if_flags & IFF_UP) {
110 	sc->sc_if.if_flags |= IFF_RUNNING;
111 	if (sc->sc_if.if_flags & IFF_PROMISC) {
112 	    sc->sc_pdq->pdq_flags |= PDQ_PROMISC;
113 	} else {
114 	    sc->sc_pdq->pdq_flags &= ~PDQ_PROMISC;
115 	}
116 	if (sc->sc_if.if_flags & IFF_LINK1) {
117 	    sc->sc_pdq->pdq_flags |= PDQ_PASS_SMT;
118 	} else {
119 	    sc->sc_pdq->pdq_flags &= ~PDQ_PASS_SMT;
120 	}
121 	sc->sc_pdq->pdq_flags |= PDQ_RUNNING;
122 	pdq_run(sc->sc_pdq);
123     } else {
124 	sc->sc_if.if_flags &= ~IFF_RUNNING;
125 	sc->sc_pdq->pdq_flags &= ~PDQ_RUNNING;
126 	pdq_stop(sc->sc_pdq);
127     }
128 }
129 
130 void
pdq_ifwatchdog(struct ifnet * ifp)131 pdq_ifwatchdog(
132     struct ifnet *ifp)
133 {
134     /*
135      * No progress was made on the transmit queue for PDQ_OS_TX_TRANSMIT
136      * seconds.  Remove all queued packets.
137      */
138 
139     ifp->if_flags &= ~IFF_OACTIVE;
140     ifp->if_timer = 0;
141     for (;;) {
142 	struct mbuf *m;
143 	IFQ_DEQUEUE(&ifp->if_snd, m);
144 	if (m == NULL)
145 	    return;
146 	PDQ_OS_DATABUF_FREE(PDQ_OS_IFP_TO_SOFTC(ifp)->sc_pdq, m);
147     }
148 }
149 
150 ifnet_ret_t
pdq_ifstart(struct ifnet * ifp)151 pdq_ifstart(
152     struct ifnet *ifp)
153 {
154     pdq_softc_t * const sc = PDQ_OS_IFP_TO_SOFTC(ifp);
155     struct mbuf *m;
156     int tx = 0;
157 
158     if ((ifp->if_flags & IFF_RUNNING) == 0)
159 	return;
160 
161     if (sc->sc_if.if_timer == 0)
162 	sc->sc_if.if_timer = PDQ_OS_TX_TIMEOUT;
163 
164     if ((sc->sc_pdq->pdq_flags & PDQ_TXOK) == 0) {
165 	sc->sc_if.if_flags |= IFF_OACTIVE;
166 	return;
167     }
168     sc->sc_flags |= PDQIF_DOWNCALL;
169     for (;; tx = 1) {
170 	IFQ_POLL(&ifp->if_snd, m);
171 	if (m == NULL)
172 	    break;
173 #if defined(PDQ_BUS_DMA) && !defined(PDQ_BUS_DMA_NOTX)
174 	if ((m->m_flags & M_HASTXDMAMAP) == 0) {
175 	    bus_dmamap_t map;
176 	    if (PDQ_OS_HDR_OFFSET != PDQ_RX_FC_OFFSET) {
177 		m->m_data[0] = PDQ_FDDI_PH0;
178 		m->m_data[1] = PDQ_FDDI_PH1;
179 		m->m_data[2] = PDQ_FDDI_PH2;
180 	    }
181 	    if (!bus_dmamap_create(sc->sc_dmatag, m->m_pkthdr.len, 255,
182 				   m->m_pkthdr.len, 0, BUS_DMA_NOWAIT, &map)) {
183 		if (!bus_dmamap_load_mbuf(sc->sc_dmatag, map, m,
184 					  BUS_DMA_WRITE|BUS_DMA_NOWAIT)) {
185 		    bus_dmamap_sync(sc->sc_dmatag, map, 0, m->m_pkthdr.len,
186 				    BUS_DMASYNC_PREWRITE);
187 		    M_SETCTX(m, map);
188 		    m->m_flags |= M_HASTXDMAMAP;
189 		}
190 	    }
191 	    if ((m->m_flags & M_HASTXDMAMAP) == 0)
192 		break;
193 	}
194 #else
195 	if (PDQ_OS_HDR_OFFSET != PDQ_RX_FC_OFFSET) {
196 	    m->m_data[0] = PDQ_FDDI_PH0;
197 	    m->m_data[1] = PDQ_FDDI_PH1;
198 	    m->m_data[2] = PDQ_FDDI_PH2;
199 	}
200 #endif
201 
202 	if (pdq_queue_transmit_data(sc->sc_pdq, m) == PDQ_FALSE)
203 	    break;
204 	IFQ_DEQUEUE(&ifp->if_snd, m);
205     }
206     if (m != NULL)
207 	ifp->if_flags |= IFF_OACTIVE;
208     if (tx)
209 	PDQ_DO_TYPE2_PRODUCER(sc->sc_pdq);
210     sc->sc_flags &= ~PDQIF_DOWNCALL;
211 }
212 
213 void
pdq_os_receive_pdu(pdq_t * pdq,struct mbuf * m,size_t pktlen,int drop)214 pdq_os_receive_pdu(
215     pdq_t *pdq,
216     struct mbuf *m,
217     size_t pktlen,
218     int drop)
219 {
220     pdq_softc_t *sc = pdq->pdq_os_ctx;
221     struct fddi_header *fh;
222 
223     sc->sc_if.if_ipackets++;
224 #if defined(PDQ_BUS_DMA)
225     {
226 	/*
227 	 * Even though the first mbuf start at the first fddi header octet,
228 	 * the dmamap starts PDQ_OS_HDR_OFFSET octets earlier.  Any additional
229 	 * mbufs will start normally.
230 	 */
231 	int offset = PDQ_OS_HDR_OFFSET;
232 	struct mbuf *m0;
233 	for (m0 = m; m0 != NULL; m0 = m0->m_next, offset = 0) {
234 	    pdq_os_databuf_sync(sc, m0, offset, m0->m_len, BUS_DMASYNC_POSTREAD);
235 	    bus_dmamap_unload(sc->sc_dmatag, M_GETCTX(m0, bus_dmamap_t));
236 	    bus_dmamap_destroy(sc->sc_dmatag, M_GETCTX(m0, bus_dmamap_t));
237 	    m0->m_flags &= ~M_HASRXDMAMAP;
238 	    M_SETCTX(m0, NULL);
239 	}
240     }
241 #endif
242     m->m_pkthdr.len = pktlen;
243     if (sc->sc_bpf != NULL)
244 	PDQ_BPF_MTAP(sc, m);
245     fh = mtod(m, struct fddi_header *);
246     if (drop || (fh->fddi_fc & (FDDIFC_L|FDDIFC_F)) != FDDIFC_LLC_ASYNC) {
247 	PDQ_OS_DATABUF_FREE(pdq, m);
248 	return;
249     }
250 
251     m_set_rcvif(m, &sc->sc_if);
252     if_percpuq_enqueue((&sc->sc_if)->if_percpuq, m);
253 }
254 
255 void
pdq_os_restart_transmitter(pdq_t * pdq)256 pdq_os_restart_transmitter(
257     pdq_t *pdq)
258 {
259     pdq_softc_t *sc = pdq->pdq_os_ctx;
260     sc->sc_if.if_flags &= ~IFF_OACTIVE;
261     if (IFQ_IS_EMPTY(&sc->sc_if.if_snd) == 0) {
262 	sc->sc_if.if_timer = PDQ_OS_TX_TIMEOUT;
263 	if ((sc->sc_flags & PDQIF_DOWNCALL) == 0)
264 	    pdq_ifstart(&sc->sc_if);
265     } else {
266 	sc->sc_if.if_timer = 0;
267     }
268 }
269 
270 void
pdq_os_transmit_done(pdq_t * pdq,struct mbuf * m)271 pdq_os_transmit_done(
272     pdq_t *pdq,
273     struct mbuf *m)
274 {
275     pdq_softc_t *sc = pdq->pdq_os_ctx;
276     if (sc->sc_bpf != NULL)
277 	PDQ_BPF_MTAP(sc, m);
278     PDQ_OS_DATABUF_FREE(pdq, m);
279     sc->sc_if.if_opackets++;
280 }
281 
282 void
pdq_os_addr_fill(pdq_t * pdq,pdq_lanaddr_t * addr,size_t num_addrs)283 pdq_os_addr_fill(
284     pdq_t *pdq,
285     pdq_lanaddr_t *addr,
286     size_t num_addrs)
287 {
288     pdq_softc_t *sc = pdq->pdq_os_ctx;
289     struct ether_multistep step;
290     struct ether_multi *enm;
291 
292     /*
293      * ADDR_FILTER_SET is always issued before FILTER_SET so
294      * we can play with PDQ_ALLMULTI and not worry about
295      * queueing a FILTER_SET ourselves.
296      */
297 
298     pdq->pdq_flags &= ~PDQ_ALLMULTI;
299 #if defined(IFF_ALLMULTI)
300     sc->sc_if.if_flags &= ~IFF_ALLMULTI;
301 #endif
302 
303     ETHER_FIRST_MULTI(step, PDQ_FDDICOM(sc), enm);
304     while (enm != NULL && num_addrs > 0) {
305 	if (memcmp(enm->enm_addrlo, enm->enm_addrhi, 6) == 0) {
306 	    ((u_short *) addr->lanaddr_bytes)[0] = ((u_short *) enm->enm_addrlo)[0];
307 	    ((u_short *) addr->lanaddr_bytes)[1] = ((u_short *) enm->enm_addrlo)[1];
308 	    ((u_short *) addr->lanaddr_bytes)[2] = ((u_short *) enm->enm_addrlo)[2];
309 	    addr++;
310 	    num_addrs--;
311 	} else {
312 	    pdq->pdq_flags |= PDQ_ALLMULTI;
313 #if defined(IFF_ALLMULTI)
314 	    sc->sc_if.if_flags |= IFF_ALLMULTI;
315 #endif
316 	}
317 	ETHER_NEXT_MULTI(step, enm);
318     }
319     /*
320      * If not all the address fit into the CAM, turn on all-multicast mode.
321      */
322     if (enm != NULL) {
323 	pdq->pdq_flags |= PDQ_ALLMULTI;
324 #if defined(IFF_ALLMULTI)
325 	sc->sc_if.if_flags |= IFF_ALLMULTI;
326 #endif
327     }
328 }
329 
330 #if defined(IFM_FDDI)
331 static int
pdq_ifmedia_change(struct ifnet * ifp)332 pdq_ifmedia_change(
333     struct ifnet *ifp)
334 {
335     pdq_softc_t * const sc = PDQ_OS_IFP_TO_SOFTC(ifp);
336 
337     if (sc->sc_ifmedia.ifm_media & IFM_FDX) {
338 	if ((sc->sc_pdq->pdq_flags & PDQ_WANT_FDX) == 0) {
339 	    sc->sc_pdq->pdq_flags |= PDQ_WANT_FDX;
340 	    if (sc->sc_pdq->pdq_flags & PDQ_RUNNING)
341 		pdq_run(sc->sc_pdq);
342 	}
343     } else if (sc->sc_pdq->pdq_flags & PDQ_WANT_FDX) {
344 	sc->sc_pdq->pdq_flags &= ~PDQ_WANT_FDX;
345 	if (sc->sc_pdq->pdq_flags & PDQ_RUNNING)
346 	    pdq_run(sc->sc_pdq);
347     }
348 
349     return 0;
350 }
351 
352 static void
pdq_ifmedia_status(struct ifnet * ifp,struct ifmediareq * ifmr)353 pdq_ifmedia_status(
354     struct ifnet *ifp,
355     struct ifmediareq *ifmr)
356 {
357     pdq_softc_t * const sc = PDQ_OS_IFP_TO_SOFTC(ifp);
358 
359     ifmr->ifm_status = IFM_AVALID;
360     if (sc->sc_pdq->pdq_flags & PDQ_IS_ONRING)
361 	ifmr->ifm_status |= IFM_ACTIVE;
362 
363     ifmr->ifm_active = (ifmr->ifm_current & ~IFM_FDX);
364     if (sc->sc_pdq->pdq_flags & PDQ_IS_FDX)
365 	ifmr->ifm_active |= IFM_FDX;
366 }
367 
368 void
pdq_os_update_status(pdq_t * pdq,const void * arg)369 pdq_os_update_status(
370     pdq_t *pdq,
371     const void *arg)
372 {
373     pdq_softc_t * const sc = pdq->pdq_os_ctx;
374     const pdq_response_status_chars_get_t *rsp = arg;
375     int media = 0;
376 
377     switch (rsp->status_chars_get.pmd_type[0]) {
378 	case PDQ_PMD_TYPE_ANSI_MUTLI_MODE:         media = IFM_FDDI_MMF; break;
379 	case PDQ_PMD_TYPE_ANSI_SINGLE_MODE_TYPE_1: media = IFM_FDDI_SMF; break;
380 	case PDQ_PMD_TYPE_ANSI_SIGNLE_MODE_TYPE_2: media = IFM_FDDI_SMF; break;
381 	case PDQ_PMD_TYPE_UNSHIELDED_TWISTED_PAIR: media = IFM_FDDI_UTP; break;
382 	default: media |= IFM_MANUAL;
383     }
384 
385     if (rsp->status_chars_get.station_type == PDQ_STATION_TYPE_DAS)
386 	media |= IFM_FDDI_DA;
387 
388     sc->sc_ifmedia.ifm_media = media | IFM_FDDI;
389 }
390 #endif /* defined(IFM_FDDI) */
391 
392 int
pdq_ifioctl(struct ifnet * ifp,ioctl_cmd_t cmd,void * data)393 pdq_ifioctl(
394     struct ifnet *ifp,
395     ioctl_cmd_t cmd,
396     void *data)
397 {
398     pdq_softc_t *sc = PDQ_OS_IFP_TO_SOFTC(ifp);
399     int s, error = 0;
400 
401     s = PDQ_OS_SPL_RAISE();
402 
403     switch (cmd) {
404 	case SIOCINITIFADDR: {
405 	    struct ifaddr *ifa = (struct ifaddr *)data;
406 
407 	    ifp->if_flags |= IFF_UP;
408 	    pdq_ifinit(sc);
409 	    switch(ifa->ifa_addr->sa_family) {
410 #if defined(INET)
411 		case AF_INET:
412 		    PDQ_ARP_IFINIT(sc, ifa);
413 		    break;
414 #endif /* INET */
415 		default:
416 		    break;
417 	    }
418 	    break;
419 	}
420 	case SIOCSIFFLAGS: {
421 	    if ((error = ifioctl_common(ifp, cmd, data)) != 0)
422 		break;
423 	    pdq_ifinit(sc);
424 	    break;
425 	}
426 
427 	case SIOCADDMULTI:
428 	case SIOCDELMULTI: {
429 	    /*
430 	     * Update multicast listeners
431 	     */
432 	    if ((error = ether_ioctl(ifp, cmd, data)) == ENETRESET) {
433 		if (sc->sc_if.if_flags & IFF_RUNNING)
434 		    pdq_run(sc->sc_pdq);
435 		error = 0;
436 	    }
437 	    break;
438 	}
439 
440 #if defined(SIOCSIFMTU)
441 #if !defined(ifr_mtu)
442 #define ifr_mtu ifr_metric
443 #endif
444 	case SIOCSIFMTU: {
445 	    struct ifreq *ifr = (struct ifreq *)data;
446 	    /*
447 	     * Set the interface MTU.
448 	     */
449 	    if (ifr->ifr_mtu > FDDIMTU) {
450 		error = EINVAL;
451 		break;
452 	    }
453 	    if ((error = ifioctl_common(ifp, cmd, data)) == ENETRESET)
454 		error = 0;
455 	    break;
456 	}
457 #endif /* SIOCSIFMTU */
458 
459 #if defined(IFM_FDDI) && defined(SIOCSIFMEDIA)
460 	case SIOCSIFMEDIA:
461 	case SIOCGIFMEDIA: {
462 	    struct ifreq *ifr = (struct ifreq *)data;
463 	    error = ifmedia_ioctl(ifp, ifr, &sc->sc_ifmedia, cmd);
464 	    break;
465 	}
466 #endif
467 
468 	default: {
469 	    error = ether_ioctl(ifp, cmd, data);
470 	    break;
471 	}
472     }
473 
474     PDQ_OS_SPL_LOWER(s);
475     return error;
476 }
477 
478 #ifndef IFF_NOTRAILERS
479 #define	IFF_NOTRAILERS	0
480 #endif
481 
482 void
pdq_ifattach(pdq_softc_t * sc,ifnet_ret_t (* ifwatchdog)(int unit))483 pdq_ifattach(
484     pdq_softc_t *sc,
485     ifnet_ret_t (*ifwatchdog)(int unit))
486 {
487     struct ifnet *ifp = &sc->sc_if;
488 
489     ifp->if_flags = IFF_BROADCAST|IFF_SIMPLEX|IFF_NOTRAILERS|IFF_MULTICAST;
490 
491 #if (defined(__FreeBSD__) && BSD >= 199506) || defined(__NetBSD__)
492     ifp->if_watchdog = pdq_ifwatchdog;
493 #else
494     ifp->if_watchdog = ifwatchdog;
495 #endif
496 
497     ifp->if_ioctl = pdq_ifioctl;
498 #if !defined(__NetBSD__)
499     ifp->if_output = fddi_output;
500 #endif
501     ifp->if_start = pdq_ifstart;
502     IFQ_SET_READY(&ifp->if_snd);
503 
504 #if defined(IFM_FDDI)
505     {
506 	const int media = sc->sc_ifmedia.ifm_media;
507 	ifmedia_init(&sc->sc_ifmedia, IFM_FDX,
508 		     pdq_ifmedia_change, pdq_ifmedia_status);
509 	ifmedia_add(&sc->sc_ifmedia, media, 0, 0);
510 	ifmedia_set(&sc->sc_ifmedia, media);
511     }
512 #endif
513 
514     if_attach(ifp);
515 #if defined(__NetBSD__)
516     fddi_ifattach(ifp, (void *)&sc->sc_pdq->pdq_hwaddr);
517 #else
518     fddi_ifattach(ifp);
519 #endif
520 }
521 
522 #if defined(PDQ_BUS_DMA)
523 int
pdq_os_memalloc_contig(pdq_t * pdq)524 pdq_os_memalloc_contig(
525     pdq_t *pdq)
526 {
527     pdq_softc_t * const sc = pdq->pdq_os_ctx;
528     bus_dma_segment_t db_segs[1], ui_segs[1], cb_segs[1];
529     int db_nsegs = 0, ui_nsegs = 0;
530     int steps = 0;
531     int not_ok;
532 
533     not_ok = bus_dmamem_alloc(sc->sc_dmatag,
534 			 sizeof(*pdq->pdq_dbp), sizeof(*pdq->pdq_dbp),
535 			 sizeof(*pdq->pdq_dbp), db_segs, 1, &db_nsegs,
536 #if defined(__sparc__) || defined(__sparc64__)
537 			BUS_DMA_NOWAIT | BUS_DMA_COHERENT);
538 #else
539 			BUS_DMA_NOWAIT);
540 #endif
541     if (!not_ok) {
542 	steps = 1;
543 	not_ok = bus_dmamem_map(sc->sc_dmatag, db_segs, db_nsegs,
544 				sizeof(*pdq->pdq_dbp), (void **) &pdq->pdq_dbp,
545 				BUS_DMA_NOWAIT);
546     }
547     if (!not_ok) {
548 	steps = 2;
549 	not_ok = bus_dmamap_create(sc->sc_dmatag, db_segs[0].ds_len, 1,
550 				   0x2000, 0, BUS_DMA_NOWAIT, &sc->sc_dbmap);
551     }
552     if (!not_ok) {
553 	steps = 3;
554 	not_ok = bus_dmamap_load(sc->sc_dmatag, sc->sc_dbmap,
555 				 pdq->pdq_dbp, sizeof(*pdq->pdq_dbp),
556 				 NULL, BUS_DMA_NOWAIT);
557     }
558     if (!not_ok) {
559 	steps = 4;
560 	pdq->pdq_pa_descriptor_block = sc->sc_dbmap->dm_segs[0].ds_addr;
561 	not_ok = bus_dmamem_alloc(sc->sc_dmatag,
562 			 PDQ_OS_PAGESIZE, PDQ_OS_PAGESIZE, PDQ_OS_PAGESIZE,
563 			 ui_segs, 1, &ui_nsegs, BUS_DMA_NOWAIT);
564     }
565     if (!not_ok) {
566 	steps = 5;
567 	not_ok = bus_dmamem_map(sc->sc_dmatag, ui_segs, ui_nsegs,
568 			    PDQ_OS_PAGESIZE,
569 			    (void **) &pdq->pdq_unsolicited_info.ui_events,
570 			    BUS_DMA_NOWAIT);
571     }
572     if (!not_ok) {
573 	steps = 6;
574 	not_ok = bus_dmamap_create(sc->sc_dmatag, ui_segs[0].ds_len, 1,
575 				   PDQ_OS_PAGESIZE, 0, BUS_DMA_NOWAIT,
576 				   &sc->sc_uimap);
577     }
578     if (!not_ok) {
579 	steps = 7;
580 	not_ok = bus_dmamap_load(sc->sc_dmatag, sc->sc_uimap,
581 				 pdq->pdq_unsolicited_info.ui_events,
582 				 PDQ_OS_PAGESIZE, NULL, BUS_DMA_NOWAIT);
583     }
584     if (!not_ok) {
585 	steps = 8;
586 	pdq->pdq_unsolicited_info.ui_pa_bufstart = sc->sc_uimap->dm_segs[0].ds_addr;
587 	cb_segs[0] = db_segs[0];
588 	cb_segs[0].ds_addr += offsetof(pdq_descriptor_block_t, pdqdb_consumer);
589 	cb_segs[0].ds_len = sizeof(pdq_consumer_block_t);
590 #if defined(__sparc__) || defined(__sparc64__)
591 	pdq->pdq_cbp = (pdq_consumer_block_t*)((unsigned long int)pdq->pdq_dbp +
592 	    (unsigned long int)offsetof(pdq_descriptor_block_t,pdqdb_consumer));
593 #else
594 	not_ok = bus_dmamem_map(sc->sc_dmatag, cb_segs, 1,
595 				sizeof(*pdq->pdq_cbp),
596 				(void **)&pdq->pdq_cbp,
597 				BUS_DMA_NOWAIT|BUS_DMA_COHERENT);
598 #endif
599     }
600     if (!not_ok) {
601 	steps = 9;
602 	not_ok = bus_dmamap_create(sc->sc_dmatag, cb_segs[0].ds_len, 1,
603 				   0x2000, 0, BUS_DMA_NOWAIT, &sc->sc_cbmap);
604     }
605     if (!not_ok) {
606 	steps = 10;
607 	not_ok = bus_dmamap_load(sc->sc_dmatag, sc->sc_cbmap,
608 				 pdq->pdq_cbp, sizeof(*pdq->pdq_cbp),
609 				 NULL, BUS_DMA_NOWAIT);
610     }
611     if (!not_ok) {
612 	pdq->pdq_pa_consumer_block = sc->sc_cbmap->dm_segs[0].ds_addr;
613 	return not_ok;
614     }
615 
616     switch (steps) {
617 	case 11: {
618 	    bus_dmamap_unload(sc->sc_dmatag, sc->sc_cbmap);
619 	    /* FALL THROUGH */
620 	}
621 	case 10: {
622 	    bus_dmamap_destroy(sc->sc_dmatag, sc->sc_cbmap);
623 	    /* FALL THROUGH */
624 	}
625 	case 9: {
626 	    bus_dmamem_unmap(sc->sc_dmatag,
627 			     (void *)pdq->pdq_cbp, sizeof(*pdq->pdq_cbp));
628 	    /* FALL THROUGH */
629 	}
630 	case 8: {
631 	    bus_dmamap_unload(sc->sc_dmatag, sc->sc_uimap);
632 	    /* FALL THROUGH */
633 	}
634 	case 7: {
635 	    bus_dmamap_destroy(sc->sc_dmatag, sc->sc_uimap);
636 	    /* FALL THROUGH */
637 	}
638 	case 6: {
639 	    bus_dmamem_unmap(sc->sc_dmatag,
640 			     (void *) pdq->pdq_unsolicited_info.ui_events,
641 			     PDQ_OS_PAGESIZE);
642 	    /* FALL THROUGH */
643 	}
644 	case 5: {
645 	    bus_dmamem_free(sc->sc_dmatag, ui_segs, ui_nsegs);
646 	    /* FALL THROUGH */
647 	}
648 	case 4: {
649 	    bus_dmamap_unload(sc->sc_dmatag, sc->sc_dbmap);
650 	    /* FALL THROUGH */
651 	}
652 	case 3: {
653 	    bus_dmamap_destroy(sc->sc_dmatag, sc->sc_dbmap);
654 	    /* FALL THROUGH */
655 	}
656 	case 2: {
657 	    bus_dmamem_unmap(sc->sc_dmatag,
658 			     (void *) pdq->pdq_dbp,
659 			     sizeof(*pdq->pdq_dbp));
660 	    /* FALL THROUGH */
661 	}
662 	case 1: {
663 	    bus_dmamem_free(sc->sc_dmatag, db_segs, db_nsegs);
664 	    /* FALL THROUGH */
665 	}
666     }
667 
668     return not_ok;
669 }
670 
671 extern void
pdq_os_descriptor_block_sync(pdq_os_ctx_t * sc,size_t offset,size_t length,int ops)672 pdq_os_descriptor_block_sync(
673     pdq_os_ctx_t *sc,
674     size_t offset,
675     size_t length,
676     int ops)
677 {
678     bus_dmamap_sync(sc->sc_dmatag, sc->sc_dbmap, offset, length, ops);
679 }
680 
681 extern void
pdq_os_consumer_block_sync(pdq_os_ctx_t * sc,int ops)682 pdq_os_consumer_block_sync(
683     pdq_os_ctx_t *sc,
684     int ops)
685 {
686     bus_dmamap_sync(sc->sc_dmatag, sc->sc_cbmap, 0, sizeof(pdq_consumer_block_t), ops);
687 }
688 
689 extern void
pdq_os_unsolicited_event_sync(pdq_os_ctx_t * sc,size_t offset,size_t length,int ops)690 pdq_os_unsolicited_event_sync(
691     pdq_os_ctx_t *sc,
692     size_t offset,
693     size_t length,
694     int ops)
695 {
696     bus_dmamap_sync(sc->sc_dmatag, sc->sc_uimap, offset, length, ops);
697 }
698 
699 extern void
pdq_os_databuf_sync(pdq_os_ctx_t * sc,struct mbuf * m,size_t offset,size_t length,int ops)700 pdq_os_databuf_sync(
701     pdq_os_ctx_t *sc,
702     struct mbuf *m,
703     size_t offset,
704     size_t length,
705     int ops)
706 {
707     bus_dmamap_sync(sc->sc_dmatag, M_GETCTX(m, bus_dmamap_t), offset, length, ops);
708 }
709 
710 extern void
pdq_os_databuf_free(pdq_os_ctx_t * sc,struct mbuf * m)711 pdq_os_databuf_free(
712     pdq_os_ctx_t *sc,
713     struct mbuf *m)
714 {
715     if (m->m_flags & (M_HASRXDMAMAP|M_HASTXDMAMAP)) {
716 	bus_dmamap_t map = M_GETCTX(m, bus_dmamap_t);
717 	bus_dmamap_unload(sc->sc_dmatag, map);
718 	bus_dmamap_destroy(sc->sc_dmatag, map);
719 	m->m_flags &= ~(M_HASRXDMAMAP|M_HASTXDMAMAP);
720     }
721     m_freem(m);
722 }
723 
724 extern struct mbuf *
pdq_os_databuf_alloc(pdq_os_ctx_t * sc)725 pdq_os_databuf_alloc(
726     pdq_os_ctx_t *sc)
727 {
728     struct mbuf *m;
729     bus_dmamap_t map;
730 
731     MGETHDR(m, M_DONTWAIT, MT_DATA);
732     if (m == NULL) {
733 	aprint_error_dev(sc->sc_dev, "can't alloc small buf\n");
734 	return NULL;
735     }
736     MCLGET(m, M_DONTWAIT);
737     if ((m->m_flags & M_EXT) == 0) {
738 	aprint_error_dev(sc->sc_dev, "can't alloc cluster\n");
739         m_free(m);
740 	return NULL;
741     }
742     MCLAIM(m, &PDQ_FDDICOM(sc)->ec_rx_mowner);
743     m->m_pkthdr.len = m->m_len = PDQ_OS_DATABUF_SIZE;
744 
745     if (bus_dmamap_create(sc->sc_dmatag, PDQ_OS_DATABUF_SIZE,
746 			   1, PDQ_OS_DATABUF_SIZE, 0, BUS_DMA_NOWAIT, &map)) {
747 	aprint_error_dev(sc->sc_dev, "can't create dmamap\n");
748 	m_free(m);
749 	return NULL;
750     }
751     if (bus_dmamap_load_mbuf(sc->sc_dmatag, map, m,
752     			     BUS_DMA_READ|BUS_DMA_NOWAIT)) {
753 	aprint_error_dev(sc->sc_dev, "can't load dmamap\n");
754 	bus_dmamap_destroy(sc->sc_dmatag, map);
755 	m_free(m);
756 	return NULL;
757     }
758     m->m_flags |= M_HASRXDMAMAP;
759     M_SETCTX(m, map);
760     return m;
761 }
762 #endif
763