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
105void
106pdq_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
130void
131pdq_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
150ifnet_ret_t
151pdq_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
213void
214pdq_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
255void
256pdq_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
270void
271pdq_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
282void
283pdq_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)
331static int
332pdq_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
352static void
353pdq_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
368void
369pdq_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
392int
393pdq_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
482void
483pdq_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)
523int
524pdq_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
671extern void
672pdq_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
681extern void
682pdq_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
689extern void
690pdq_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
699extern void
700pdq_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
710extern void
711pdq_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
724extern struct mbuf *
725pdq_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