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