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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 * |
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 | |