xref: /netbsd-src/sys/dev/ieee1394/fwdev.c (revision 4fee23f98c45552038ad6b5bd05124a41302fb01)
1 /*	$NetBSD: fwdev.c,v 1.25 2011/05/25 16:33:37 uebayasi Exp $	*/
2 /*-
3  * Copyright (c) 2003 Hidetoshi Shimokawa
4  * Copyright (c) 1998-2002 Katsushi Kobayashi and Hidetoshi Shimokawa
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. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the acknowledgement as bellow:
17  *
18  *    This product includes software developed by K. Kobayashi and H. Shimokawa
19  *
20  * 4. The name of the author may not be used to endorse or promote products
21  *    derived from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
27  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  *
35  * $FreeBSD: src/sys/dev/firewire/fwdev.c,v 1.52 2007/06/06 14:31:36 simokawa Exp $
36  *
37  */
38 
39 #include <sys/cdefs.h>
40 __KERNEL_RCSID(0, "$NetBSD: fwdev.c,v 1.25 2011/05/25 16:33:37 uebayasi Exp $");
41 
42 #include <sys/param.h>
43 #include <sys/device.h>
44 #include <sys/errno.h>
45 #include <sys/buf.h>
46 #include <sys/bus.h>
47 #include <sys/conf.h>
48 #include <sys/kernel.h>
49 #include <sys/malloc.h>
50 #include <sys/mbuf.h>
51 #include <sys/poll.h>
52 #include <sys/proc.h>
53 #include <sys/select.h>
54 
55 #include <dev/ieee1394/firewire.h>
56 #include <dev/ieee1394/firewirereg.h>
57 #include <dev/ieee1394/fwdma.h>
58 #include <dev/ieee1394/fwmem.h>
59 #include <dev/ieee1394/iec68113.h>
60 
61 #define	FWNODE_INVAL 0xffff
62 
63 dev_type_open(fw_open);
64 dev_type_close(fw_close);
65 dev_type_read(fw_read);
66 dev_type_write(fw_write);
67 dev_type_ioctl(fw_ioctl);
68 dev_type_poll(fw_poll);
69 dev_type_mmap(fw_mmap);
70 dev_type_strategy(fw_strategy);
71 
72 const struct bdevsw fw_bdevsw = {
73 	fw_open, fw_close, fw_strategy, fw_ioctl, nodump, nosize, D_OTHER,
74 };
75 
76 const struct cdevsw fw_cdevsw = {
77 	fw_open, fw_close, fw_read, fw_write, fw_ioctl,
78 	nostop, notty, fw_poll, fw_mmap, nokqfilter, D_OTHER,
79 };
80 
81 struct fw_drv1 {
82 	struct firewire_comm *fc;
83 	struct fw_xferq *ir;
84 	struct fw_xferq *it;
85 	struct fw_isobufreq bufreq;
86 	STAILQ_HEAD(, fw_bind) binds;
87 	STAILQ_HEAD(, fw_xfer) rq;
88 };
89 
90 static int fwdev_allocbuf(struct firewire_comm *, struct fw_xferq *,
91 			  struct fw_bufspec *);
92 static int fwdev_freebuf(struct fw_xferq *);
93 static int fw_read_async(struct fw_drv1 *, struct uio *, int);
94 static int fw_write_async(struct fw_drv1 *, struct uio *, int);
95 static void fw_hand(struct fw_xfer *);
96 
97 extern struct cfdriver ieee1394if_cd;
98 
99 int
100 fw_open(dev_t dev, int flags, int fmt, struct lwp *td)
101 {
102 	struct firewire_softc *sc;
103 	struct fw_drv1 *d;
104 	int err = 0;
105 
106 	sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
107 	if (sc == NULL)
108 		return ENXIO;
109 
110 	if (DEV_FWMEM(dev))
111 		return fwmem_open(dev, flags, fmt, td);
112 
113 	mutex_enter(&sc->fc->fc_mtx);
114 	if (sc->si_drv1 != NULL) {
115 		mutex_exit(&sc->fc->fc_mtx);
116 		return EBUSY;
117 	}
118 	/* set dummy value for allocation */
119 	sc->si_drv1 = (void *)-1;
120 	mutex_exit(&sc->fc->fc_mtx);
121 
122 	sc->si_drv1 = malloc(sizeof(struct fw_drv1), M_FW, M_WAITOK | M_ZERO);
123 	if (sc->si_drv1 == NULL)
124 		return ENOMEM;
125 
126 	d = (struct fw_drv1 *)sc->si_drv1;
127 	d->fc = sc->fc;
128 	STAILQ_INIT(&d->binds);
129 	STAILQ_INIT(&d->rq);
130 
131 	return err;
132 }
133 
134 int
135 fw_close(dev_t dev, int flags, int fmt, struct lwp *td)
136 {
137 	struct firewire_softc *sc;
138 	struct firewire_comm *fc;
139 	struct fw_drv1 *d;
140 	struct fw_xfer *xfer;
141 	struct fw_bind *fwb;
142         int err = 0;
143 
144 	sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
145 	if (sc == NULL)
146 		return ENXIO;
147 
148 	if (DEV_FWMEM(dev))
149 		return fwmem_close(dev, flags, fmt, td);
150 
151 	d = (struct fw_drv1 *)sc->si_drv1;
152 	fc = d->fc;
153 
154 	/* remove binding */
155 	for (fwb = STAILQ_FIRST(&d->binds); fwb != NULL;
156 	    fwb = STAILQ_FIRST(&d->binds)) {
157 		fw_bindremove(fc, fwb);
158 		STAILQ_REMOVE_HEAD(&d->binds, chlist);
159 		fw_xferlist_remove(&fwb->xferlist);
160 		free(fwb, M_FW);
161 	}
162 	if (d->ir != NULL) {
163 		struct fw_xferq *ir = d->ir;
164 
165 		if ((ir->flag & FWXFERQ_OPEN) == 0)
166 			return EINVAL;
167 		if (ir->flag & FWXFERQ_RUNNING) {
168 			ir->flag &= ~FWXFERQ_RUNNING;
169 			fc->irx_disable(fc, ir->dmach);
170 		}
171 		/* free extbuf */
172 		fwdev_freebuf(ir);
173 		/* drain receiving buffer */
174 		for (xfer = STAILQ_FIRST(&ir->q); xfer != NULL;
175 		    xfer = STAILQ_FIRST(&ir->q)) {
176 			ir->queued--;
177 			STAILQ_REMOVE_HEAD(&ir->q, link);
178 
179 			xfer->resp = 0;
180 			fw_xfer_done(xfer);
181 		}
182 		ir->flag &=
183 		    ~(FWXFERQ_OPEN | FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK);
184 		d->ir = NULL;
185 
186 	}
187 	if (d->it != NULL) {
188 		struct fw_xferq *it = d->it;
189 
190 		if ((it->flag & FWXFERQ_OPEN) == 0)
191 			return EINVAL;
192 		if (it->flag & FWXFERQ_RUNNING) {
193 			it->flag &= ~FWXFERQ_RUNNING;
194 			fc->itx_disable(fc, it->dmach);
195 		}
196 		/* free extbuf */
197 		fwdev_freebuf(it);
198 		it->flag &=
199 		    ~(FWXFERQ_OPEN | FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK);
200 		d->it = NULL;
201 	}
202 	free(sc->si_drv1, M_FW);
203 	sc->si_drv1 = NULL;
204 
205 	return err;
206 }
207 
208 int
209 fw_read(dev_t dev, struct uio *uio, int ioflag)
210 {
211 	struct firewire_softc *sc;
212 	struct firewire_comm *fc;
213 	struct fw_drv1 *d;
214 	struct fw_xferq *ir;
215 	struct fw_pkt *fp;
216 	int err = 0, slept = 0;
217 
218 	sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
219 	if (sc == NULL)
220 		return ENXIO;
221 
222 	if (DEV_FWMEM(dev))
223 		return physio(fw_strategy, NULL, dev, ioflag, minphys, uio);
224 
225 	d = (struct fw_drv1 *)sc->si_drv1;
226 	fc = d->fc;
227 	ir = d->ir;
228 
229 	if (ir == NULL)
230 		return fw_read_async(d, uio, ioflag);
231 
232 	if (ir->buf == NULL)
233 		return EIO;
234 
235 	mutex_enter(&fc->fc_mtx);
236 readloop:
237 	if (ir->stproc == NULL) {
238 		/* iso bulkxfer */
239 		ir->stproc = STAILQ_FIRST(&ir->stvalid);
240 		if (ir->stproc != NULL) {
241 			STAILQ_REMOVE_HEAD(&ir->stvalid, link);
242 			ir->queued = 0;
243 		}
244 	}
245 	if (ir->stproc == NULL) {
246 		/* no data avaliable */
247 		if (slept == 0) {
248 			slept = 1;
249 			ir->flag |= FWXFERQ_WAKEUP;
250 			mutex_exit(&fc->fc_mtx);
251 			err = tsleep(ir, FWPRI, "fw_read", hz);
252 			mutex_enter(&fc->fc_mtx);
253 			ir->flag &= ~FWXFERQ_WAKEUP;
254 			if (err == 0)
255 				goto readloop;
256 		} else if (slept == 1)
257 			err = EIO;
258 		mutex_exit(&fc->fc_mtx);
259 		return err;
260 	} else if (ir->stproc != NULL) {
261 		/* iso bulkxfer */
262 		mutex_exit(&fc->fc_mtx);
263 		fp = (struct fw_pkt *)fwdma_v_addr(ir->buf,
264 		    ir->stproc->poffset + ir->queued);
265 		if (fc->irx_post != NULL)
266 			fc->irx_post(fc, fp->mode.ld);
267 		if (fp->mode.stream.len == 0)
268 			return EIO;
269 		err = uiomove((void *)fp,
270 		    fp->mode.stream.len + sizeof(uint32_t), uio);
271 		ir->queued++;
272 		if (ir->queued >= ir->bnpacket) {
273 			STAILQ_INSERT_TAIL(&ir->stfree, ir->stproc, link);
274 			fc->irx_enable(fc, ir->dmach);
275 			ir->stproc = NULL;
276 		}
277 		if (uio->uio_resid >= ir->psize) {
278 			slept = -1;
279 			mutex_enter(&fc->fc_mtx);
280 			goto readloop;
281 		}
282 	} else
283 		mutex_exit(&fc->fc_mtx);
284 	return err;
285 }
286 
287 int
288 fw_write(dev_t dev, struct uio *uio, int ioflag)
289 {
290 	struct firewire_softc *sc;
291 	struct firewire_comm *fc;
292 	struct fw_drv1 *d;
293 	struct fw_pkt *fp;
294 	struct fw_xferq *it;
295         int slept = 0, err = 0;
296 
297 	sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
298 	if (sc == NULL)
299 		return ENXIO;
300 
301 	if (DEV_FWMEM(dev))
302 		return physio(fw_strategy, NULL, dev, ioflag, minphys, uio);
303 
304 	d = (struct fw_drv1 *)sc->si_drv1;
305 	fc = d->fc;
306 	it = d->it;
307 
308 	if (it == NULL)
309 		return fw_write_async(d, uio, ioflag);
310 
311 	if (it->buf == NULL)
312 		return EIO;
313 
314 	mutex_enter(&fc->fc_mtx);
315 isoloop:
316 	if (it->stproc == NULL) {
317 		it->stproc = STAILQ_FIRST(&it->stfree);
318 		if (it->stproc != NULL) {
319 			STAILQ_REMOVE_HEAD(&it->stfree, link);
320 			it->queued = 0;
321 		} else if (slept == 0) {
322 			slept = 1;
323 #if 0   /* XXX to avoid lock recursion */
324 			err = fc->itx_enable(fc, it->dmach);
325 			if (err)
326 				goto out;
327 #endif
328 			mutex_exit(&fc->fc_mtx);
329 			err = tsleep(it, FWPRI, "fw_write", hz);
330 			mutex_enter(&fc->fc_mtx);
331 			if (err)
332 				goto out;
333 			goto isoloop;
334 		} else {
335 			err = EIO;
336 			goto out;
337 		}
338 	}
339 	mutex_exit(&fc->fc_mtx);
340 	fp = (struct fw_pkt *)fwdma_v_addr(it->buf,
341 	    it->stproc->poffset + it->queued);
342 	err = uiomove((void *)fp, sizeof(struct fw_isohdr), uio);
343 	if (err != 0)
344 		return err;
345 	err =
346 	    uiomove((void *)fp->mode.stream.payload, fp->mode.stream.len, uio);
347 	it->queued++;
348 	if (it->queued >= it->bnpacket) {
349 		STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link);
350 		it->stproc = NULL;
351 		err = fc->itx_enable(fc, it->dmach);
352 	}
353 	if (uio->uio_resid >= sizeof(struct fw_isohdr)) {
354 		slept = 0;
355 		mutex_enter(&fc->fc_mtx);
356 		goto isoloop;
357 	}
358 	return err;
359 
360 out:
361 	mutex_exit(&fc->fc_mtx);
362 	return err;
363 }
364 
365 int
366 fw_ioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *td)
367 {
368 	struct firewire_softc *sc;
369 	struct firewire_comm *fc;
370 	struct fw_drv1 *d;
371 	struct fw_device *fwdev;
372 	struct fw_bind *fwb;
373 	struct fw_xferq *ir, *it;
374 	struct fw_xfer *xfer;
375 	struct fw_pkt *fp;
376 	struct fw_devinfo *devinfo;
377 	struct fw_devlstreq *fwdevlst = (struct fw_devlstreq *)data;
378 	struct fw_asyreq *asyreq = (struct fw_asyreq *)data;
379 	struct fw_isochreq *ichreq = (struct fw_isochreq *)data;
380 	struct fw_isobufreq *ibufreq = (struct fw_isobufreq *)data;
381 	struct fw_asybindreq *bindreq = (struct fw_asybindreq *)data;
382 	struct fw_crom_buf *crom_buf = (struct fw_crom_buf *)data;
383 	int i, len, err = 0;
384 	void *ptr;
385 
386 	sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
387 	if (sc == NULL)
388 		return ENXIO;
389 
390 	if (DEV_FWMEM(dev))
391 		return fwmem_ioctl(dev, cmd, data, flag, td);
392 
393 	if (!data)
394 		return EINVAL;
395 
396 	d = (struct fw_drv1 *)sc->si_drv1;
397 	fc = d->fc;
398 	ir = d->ir;
399 	it = d->it;
400 
401 	switch (cmd) {
402 	case FW_STSTREAM:
403 		if (it == NULL) {
404 			i = fw_open_isodma(fc, /* tx */1);
405 			if (i < 0) {
406 				err = EBUSY;
407 				break;
408 			}
409 			it = fc->it[i];
410 			err = fwdev_allocbuf(fc, it, &d->bufreq.tx);
411 			if (err) {
412 				it->flag &= ~FWXFERQ_OPEN;
413 				break;
414 			}
415 		}
416 		it->flag &= ~0xff;
417 		it->flag |= (0x3f & ichreq->ch);
418 		it->flag |= ((0x3 & ichreq->tag) << 6);
419 		d->it = it;
420 		break;
421 
422 	case FW_GTSTREAM:
423 		if (it != NULL) {
424 			ichreq->ch = it->flag & 0x3f;
425 			ichreq->tag = it->flag >> 2 & 0x3;
426 		} else
427 			err = EINVAL;
428 		break;
429 
430 	case FW_SRSTREAM:
431 		if (ir == NULL) {
432 			i = fw_open_isodma(fc, /* tx */0);
433 			if (i < 0) {
434 				err = EBUSY;
435 				break;
436 			}
437 			ir = fc->ir[i];
438 			err = fwdev_allocbuf(fc, ir, &d->bufreq.rx);
439 			if (err) {
440 				ir->flag &= ~FWXFERQ_OPEN;
441 				break;
442 			}
443 		}
444 		ir->flag &= ~0xff;
445 		ir->flag |= (0x3f & ichreq->ch);
446 		ir->flag |= ((0x3 & ichreq->tag) << 6);
447 		d->ir = ir;
448 		err = fc->irx_enable(fc, ir->dmach);
449 		break;
450 
451 	case FW_GRSTREAM:
452 		if (d->ir != NULL) {
453 			ichreq->ch = ir->flag & 0x3f;
454 			ichreq->tag = ir->flag >> 2 & 0x3;
455 		} else
456 			err = EINVAL;
457 		break;
458 
459 	case FW_SSTBUF:
460 		memcpy(&d->bufreq, ibufreq, sizeof(d->bufreq));
461 		break;
462 
463 	case FW_GSTBUF:
464 		memset(&ibufreq->rx, 0, sizeof(ibufreq->rx));
465 		if (ir != NULL) {
466 			ibufreq->rx.nchunk = ir->bnchunk;
467 			ibufreq->rx.npacket = ir->bnpacket;
468 			ibufreq->rx.psize = ir->psize;
469 		}
470 		memset(&ibufreq->tx, 0, sizeof(ibufreq->tx));
471 		if (it != NULL) {
472 			ibufreq->tx.nchunk = it->bnchunk;
473 			ibufreq->tx.npacket = it->bnpacket;
474 			ibufreq->tx.psize = it->psize;
475 		}
476 		break;
477 
478 	case FW_ASYREQ:
479 	{
480 		const struct tcode_info *tinfo;
481 		int pay_len = 0;
482 
483 		fp = &asyreq->pkt;
484 		tinfo = &fc->tcode[fp->mode.hdr.tcode];
485 
486 		if ((tinfo->flag & FWTI_BLOCK_ASY) != 0)
487 			pay_len = MAX(0, asyreq->req.len - tinfo->hdr_len);
488 
489 		xfer = fw_xfer_alloc_buf(M_FWXFER, pay_len, PAGE_SIZE/*XXX*/);
490 		if (xfer == NULL)
491 			return ENOMEM;
492 
493 		switch (asyreq->req.type) {
494 		case FWASREQNODE:
495 			break;
496 
497 		case FWASREQEUI:
498 			fwdev = fw_noderesolve_eui64(fc, &asyreq->req.dst.eui);
499 			if (fwdev == NULL) {
500 				aprint_error_dev(fc->bdev,
501 				    "cannot find node\n");
502 				err = EINVAL;
503 				goto out;
504 			}
505 			fp->mode.hdr.dst = FWLOCALBUS | fwdev->dst;
506 			break;
507 
508 		case FWASRESTL:
509 			/* XXX what's this? */
510 			break;
511 
512 		case FWASREQSTREAM:
513 			/* nothing to do */
514 			break;
515 		}
516 
517 		memcpy(&xfer->send.hdr, fp, tinfo->hdr_len);
518 		if (pay_len > 0)
519 			memcpy(xfer->send.payload, (char *)fp + tinfo->hdr_len,
520 			    pay_len);
521 		xfer->send.spd = asyreq->req.sped;
522 		xfer->hand = fw_xferwake;
523 
524 		if ((err = fw_asyreq(fc, -1, xfer)) != 0)
525 			goto out;
526 		if ((err = fw_xferwait(xfer)) != 0)
527 			goto out;
528 		if (xfer->resp != 0) {
529 			err = EIO;
530 			goto out;
531 		}
532 		if ((tinfo->flag & FWTI_TLABEL) == 0)
533 			goto out;
534 
535 		/* copy response */
536 		tinfo = &fc->tcode[xfer->recv.hdr.mode.hdr.tcode];
537 		if (xfer->recv.hdr.mode.hdr.tcode == FWTCODE_RRESB ||
538 		    xfer->recv.hdr.mode.hdr.tcode == FWTCODE_LRES) {
539 			pay_len = xfer->recv.pay_len;
540 			if (asyreq->req.len >=
541 			    xfer->recv.pay_len + tinfo->hdr_len)
542 				asyreq->req.len =
543 				    xfer->recv.pay_len + tinfo->hdr_len;
544 			else {
545 				err = EINVAL;
546 				pay_len = 0;
547 			}
548 		} else
549 			pay_len = 0;
550 		memcpy(fp, &xfer->recv.hdr, tinfo->hdr_len);
551 		memcpy((char *)fp + tinfo->hdr_len, xfer->recv.payload,
552 		    pay_len);
553 out:
554 		fw_xfer_free_buf(xfer);
555 		break;
556 	}
557 
558 	case FW_IBUSRST:
559 		fc->ibr(fc);
560 		break;
561 
562 	case FW_CBINDADDR:
563 		fwb = fw_bindlookup(fc, bindreq->start.hi, bindreq->start.lo);
564 		if (fwb == NULL) {
565 			err = EINVAL;
566 			break;
567 		}
568 		fw_bindremove(fc, fwb);
569 		STAILQ_REMOVE(&d->binds, fwb, fw_bind, chlist);
570 		fw_xferlist_remove(&fwb->xferlist);
571 		free(fwb, M_FW);
572 		break;
573 
574 	case FW_SBINDADDR:
575 		if (bindreq->len <= 0 ) {
576 			err = EINVAL;
577 			break;
578 		}
579 		if (bindreq->start.hi > 0xffff ) {
580 			err = EINVAL;
581 			break;
582 		}
583 		fwb = (struct fw_bind *)malloc(sizeof(struct fw_bind),
584 		    M_FW, M_WAITOK);
585 		if (fwb == NULL) {
586 			err = ENOMEM;
587 			break;
588 		}
589 		fwb->start = ((u_int64_t)bindreq->start.hi << 32) |
590 		    bindreq->start.lo;
591 		fwb->end = fwb->start +  bindreq->len;
592 		fwb->sc = (void *)d;
593 		STAILQ_INIT(&fwb->xferlist);
594 		err = fw_bindadd(fc, fwb);
595 		if (err == 0) {
596 			fw_xferlist_add(&fwb->xferlist, M_FWXFER,
597 			    /* XXX */
598 			    PAGE_SIZE, PAGE_SIZE, 5, fc, (void *)fwb, fw_hand);
599 			STAILQ_INSERT_TAIL(&d->binds, fwb, chlist);
600 		}
601 		break;
602 
603 	case FW_GDEVLST:
604 		i = len = 1;
605 		/* myself */
606 		devinfo = fwdevlst->dev;
607 		devinfo->dst = fc->nodeid;
608 		devinfo->status = 0;	/* XXX */
609 		devinfo->eui.hi = fc->eui.hi;
610 		devinfo->eui.lo = fc->eui.lo;
611 		STAILQ_FOREACH(fwdev, &fc->devices, link) {
612 			if (len < FW_MAX_DEVLST) {
613 				devinfo = &fwdevlst->dev[len++];
614 				devinfo->dst = fwdev->dst;
615 				devinfo->status =
616 				    (fwdev->status == FWDEVINVAL) ? 0 : 1;
617 				devinfo->eui.hi = fwdev->eui.hi;
618 				devinfo->eui.lo = fwdev->eui.lo;
619 			}
620 			i++;
621 		}
622 		fwdevlst->n = i;
623 		fwdevlst->info_len = len;
624 		break;
625 
626 	case FW_GTPMAP:
627 		memcpy(data, fc->topology_map,
628 		    (fc->topology_map->crc_len + 1) * 4);
629 		break;
630 
631 	case FW_GCROM:
632 		STAILQ_FOREACH(fwdev, &fc->devices, link)
633 			if (FW_EUI64_EQUAL(fwdev->eui, crom_buf->eui))
634 				break;
635 		if (fwdev == NULL) {
636 			if (!FW_EUI64_EQUAL(fc->eui, crom_buf->eui)) {
637 				err = FWNODE_INVAL;
638 				break;
639 			}
640 			/* myself */
641 			ptr = malloc(CROMSIZE, M_FW, M_WAITOK);
642 			len = CROMSIZE;
643 			for (i = 0; i < CROMSIZE/4; i++)
644 				((uint32_t *)ptr)[i] = ntohl(fc->config_rom[i]);
645 		} else {
646 			/* found */
647 			ptr = (void *)fwdev->csrrom;
648 			if (fwdev->rommax < CSRROMOFF)
649 				len = 0;
650 			else
651 				len = fwdev->rommax - CSRROMOFF + 4;
652 		}
653 		if (crom_buf->len < len)
654 			len = crom_buf->len;
655 		else
656 			crom_buf->len = len;
657 		err = copyout(ptr, crom_buf->ptr, len);
658 		if (fwdev == NULL)
659 			/* myself */
660 			free(ptr, M_FW);
661 		break;
662 
663 	default:
664 		fc->ioctl(dev, cmd, data, flag, td);
665 		break;
666 	}
667 	return err;
668 }
669 
670 int
671 fw_poll(dev_t dev, int events, struct lwp *td)
672 {
673 	struct firewire_softc *sc;
674 	struct fw_xferq *ir;
675 	int revents, tmp;
676 
677 	sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
678 	if (sc == NULL)
679 		return ENXIO;
680 
681 	ir = ((struct fw_drv1 *)sc->si_drv1)->ir;
682 	revents = 0;
683 	tmp = POLLIN | POLLRDNORM;
684 	if (events & tmp) {
685 		if (STAILQ_FIRST(&ir->q) != NULL)
686 			revents |= tmp;
687 		else
688 			selrecord(td, &ir->rsel);
689 	}
690 	tmp = POLLOUT | POLLWRNORM;
691 	if (events & tmp)
692 		/* XXX should be fixed */
693 		revents |= tmp;
694 
695 	return revents;
696 }
697 
698 paddr_t
699 fw_mmap(dev_t dev, off_t offset, int nproto)
700 {
701 	struct firewire_softc *sc;
702 
703 	sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
704 	if (sc == NULL)
705 		return ENXIO;
706 
707 	return EINVAL;
708 }
709 
710 void
711 fw_strategy(struct bio *bp)
712 {
713 	struct firewire_softc *sc;
714 	dev_t dev = bp->bio_dev;
715 
716 	sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
717 	if (sc == NULL)
718 		return;
719 
720 	if (DEV_FWMEM(dev)) {
721 		fwmem_strategy(bp);
722 		return;
723 	}
724 
725 	bp->bio_error = EOPNOTSUPP;
726 	bp->bio_resid = bp->bio_bcount;
727 	biodone(bp);
728 }
729 
730 
731 static int
732 fwdev_allocbuf(struct firewire_comm *fc, struct fw_xferq *q,
733 	       struct fw_bufspec *b)
734 {
735 	int i;
736 
737 	if (q->flag & (FWXFERQ_RUNNING | FWXFERQ_EXTBUF))
738 		return EBUSY;
739 
740 	q->bulkxfer =
741 	    (struct fw_bulkxfer *)malloc(sizeof(struct fw_bulkxfer) * b->nchunk,
742 								M_FW, M_WAITOK);
743 	if (q->bulkxfer == NULL)
744 		return ENOMEM;
745 
746 	b->psize = roundup2(b->psize, sizeof(uint32_t));
747 	q->buf = fwdma_malloc_multiseg(fc, sizeof(uint32_t), b->psize,
748 	    b->nchunk * b->npacket, BUS_DMA_WAITOK);
749 
750 	if (q->buf == NULL) {
751 		free(q->bulkxfer, M_FW);
752 		q->bulkxfer = NULL;
753 		return ENOMEM;
754 	}
755 	q->bnchunk = b->nchunk;
756 	q->bnpacket = b->npacket;
757 	q->psize = (b->psize + 3) & ~3;
758 	q->queued = 0;
759 
760 	STAILQ_INIT(&q->stvalid);
761 	STAILQ_INIT(&q->stfree);
762 	STAILQ_INIT(&q->stdma);
763 	q->stproc = NULL;
764 
765 	for (i = 0 ; i < q->bnchunk; i++) {
766 		q->bulkxfer[i].poffset = i * q->bnpacket;
767 		q->bulkxfer[i].mbuf = NULL;
768 		STAILQ_INSERT_TAIL(&q->stfree, &q->bulkxfer[i], link);
769 	}
770 
771 	q->flag &= ~FWXFERQ_MODEMASK;
772 	q->flag |= FWXFERQ_STREAM;
773 	q->flag |= FWXFERQ_EXTBUF;
774 
775 	return 0;
776 }
777 
778 static int
779 fwdev_freebuf(struct fw_xferq *q)
780 {
781 
782 	if (q->flag & FWXFERQ_EXTBUF) {
783 		if (q->buf != NULL)
784 			fwdma_free_multiseg(q->buf);
785 		q->buf = NULL;
786 		free(q->bulkxfer, M_FW);
787 		q->bulkxfer = NULL;
788 		q->flag &= ~FWXFERQ_EXTBUF;
789 		q->psize = 0;
790 		q->maxq = FWMAXQUEUE;
791 	}
792 	return 0;
793 }
794 
795 static int
796 fw_read_async(struct fw_drv1 *d, struct uio *uio, int ioflag)
797 {
798 	struct fw_xfer *xfer;
799 	struct fw_bind *fwb;
800 	struct fw_pkt *fp;
801 	const struct tcode_info *tinfo;
802 	int err = 0;
803 
804 	mutex_enter(&d->fc->fc_mtx);
805 
806 	for (;;) {
807 		xfer = STAILQ_FIRST(&d->rq);
808 		if (xfer == NULL && err == 0) {
809 			mutex_exit(&d->fc->fc_mtx);
810 			err = tsleep(&d->rq, FWPRI, "fwra", 0);
811 			if (err != 0)
812 				return err;
813 			mutex_enter(&d->fc->fc_mtx);
814 			continue;
815 		}
816 		break;
817 	}
818 
819 	STAILQ_REMOVE_HEAD(&d->rq, link);
820 	mutex_exit(&d->fc->fc_mtx);
821 	fp = &xfer->recv.hdr;
822 #if 0 /* for GASP ?? */
823 	if (fc->irx_post != NULL)
824 		fc->irx_post(fc, fp->mode.ld);
825 #endif
826 	tinfo = &xfer->fc->tcode[fp->mode.hdr.tcode];
827 	err = uiomove((void *)fp, tinfo->hdr_len, uio);
828 	if (err)
829 		goto out;
830 	err = uiomove((void *)xfer->recv.payload, xfer->recv.pay_len, uio);
831 
832 out:
833 	/* recycle this xfer */
834 	fwb = (struct fw_bind *)xfer->sc;
835 	fw_xfer_unload(xfer);
836 	xfer->recv.pay_len = PAGE_SIZE;
837 	mutex_enter(&d->fc->fc_mtx);
838 	STAILQ_INSERT_TAIL(&fwb->xferlist, xfer, link);
839 	mutex_exit(&d->fc->fc_mtx);
840 	return err;
841 }
842 
843 static int
844 fw_write_async(struct fw_drv1 *d, struct uio *uio, int ioflag)
845 {
846 	struct fw_xfer *xfer;
847 	struct fw_pkt pkt;
848 	const struct tcode_info *tinfo;
849 	int err;
850 
851 	memset(&pkt, 0, sizeof(struct fw_pkt));
852 	if ((err = uiomove((void *)&pkt, sizeof(uint32_t), uio)))
853 		return err;
854 	tinfo = &d->fc->tcode[pkt.mode.hdr.tcode];
855 	if ((err = uiomove((char *)&pkt + sizeof(uint32_t),
856 	    tinfo->hdr_len - sizeof(uint32_t), uio)))
857 		return err;
858 
859 	if ((xfer = fw_xfer_alloc_buf(M_FWXFER, uio->uio_resid,
860 	    PAGE_SIZE/*XXX*/)) == NULL)
861 		return ENOMEM;
862 
863 	memcpy(&xfer->send.hdr, &pkt, sizeof(struct fw_pkt));
864 	xfer->send.pay_len = uio->uio_resid;
865 	if (uio->uio_resid > 0) {
866 		if ((err =
867 		    uiomove((void *)xfer->send.payload, uio->uio_resid, uio)))
868 			goto out;
869 	}
870 
871 	xfer->fc = d->fc;
872 	xfer->sc = NULL;
873 	xfer->hand = fw_xferwake;
874 	xfer->send.spd = 2 /* XXX */;
875 
876 	if ((err = fw_asyreq(xfer->fc, -1, xfer)))
877 		goto out;
878 
879 	if ((err = fw_xferwait(xfer)))
880 		goto out;
881 
882 	if (xfer->resp != 0) {
883 		err = xfer->resp;
884 		goto out;
885 	}
886 
887 	if (xfer->flag == FWXF_RCVD) {
888 		mutex_enter(&xfer->fc->fc_mtx);
889 		STAILQ_INSERT_TAIL(&d->rq, xfer, link);
890 		mutex_exit(&xfer->fc->fc_mtx);
891 		return 0;
892 	}
893 
894 out:
895 	fw_xfer_free(xfer);
896 	return err;
897 }
898 
899 static void
900 fw_hand(struct fw_xfer *xfer)
901 {
902 	struct fw_bind *fwb;
903 	struct fw_drv1 *d;
904 
905 	fwb = (struct fw_bind *)xfer->sc;
906 	d = (struct fw_drv1 *)fwb->sc;
907 	mutex_enter(&xfer->fc->fc_mtx);
908 	STAILQ_INSERT_TAIL(&d->rq, xfer, link);
909 	mutex_exit(&xfer->fc->fc_mtx);
910 	wakeup(&d->rq);
911 }
912