xref: /netbsd-src/sys/dev/ieee1394/fwdev.c (revision e5548b402ae4c44fb816de42c7bba9581ce23ef5)
1 /*	$NetBSD: fwdev.c,v 1.3 2005/12/11 12:22:02 christos 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: /repoman/r/ncvs/src/sys/dev/firewire/fwdev.c,v 1.46 2005/03/31 12:19:42 phk Exp $
36  *
37  */
38 
39 #if defined(__FreeBSD__)
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/types.h>
43 #include <sys/mbuf.h>
44 #if defined(__DragonFly__) || __FreeBSD_version < 500000
45 #include <sys/buf.h>
46 #else
47 #include <sys/bio.h>
48 #endif
49 
50 #include <sys/kernel.h>
51 #include <sys/malloc.h>
52 #include <sys/conf.h>
53 #include <sys/poll.h>
54 
55 #include <sys/bus.h>
56 #include <sys/ctype.h>
57 #include <machine/bus.h>
58 
59 #include <sys/ioccom.h>
60 
61 #ifdef __DragonFly__
62 #include "fw_port.h"
63 #include "firewire.h"
64 #include "firewirereg.h"
65 #include "fwdma.h"
66 #include "fwmem.h"
67 #include "iec68113.h"
68 #else
69 #include <dev/firewire/fw_port.h>
70 #include <dev/firewire/firewire.h>
71 #include <dev/firewire/firewirereg.h>
72 #include <dev/firewire/fwdma.h>
73 #include <dev/firewire/fwmem.h>
74 #include <dev/firewire/iec68113.h>
75 #endif
76 #elif defined(__NetBSD__)
77 #include <sys/param.h>
78 #include <sys/device.h>
79 #include <sys/errno.h>
80 #include <sys/buf.h>
81 #include <sys/conf.h>
82 #include <sys/kernel.h>
83 #include <sys/malloc.h>
84 #include <sys/mbuf.h>
85 #include <sys/poll.h>
86 #include <sys/proc.h>
87 
88 #include <machine/bus.h>
89 
90 #include <dev/ieee1394/fw_port.h>
91 #include <dev/ieee1394/firewire.h>
92 #include <dev/ieee1394/firewirereg.h>
93 #include <dev/ieee1394/fwdma.h>
94 #include <dev/ieee1394/fwmem.h>
95 #include <dev/ieee1394/iec68113.h>
96 #endif
97 
98 #define	FWNODE_INVAL 0xffff
99 
100 #if defined(__FreeBSD__)
101 static	d_open_t	fw_open;
102 static	d_close_t	fw_close;
103 static	d_ioctl_t	fw_ioctl;
104 static	d_poll_t	fw_poll;
105 static	d_read_t	fw_read;	/* for Isochronous packet */
106 static	d_write_t	fw_write;
107 static	d_mmap_t	fw_mmap;
108 static	d_strategy_t	fw_strategy;
109 
110 struct cdevsw firewire_cdevsw = {
111 #ifdef __DragonFly__
112 #define CDEV_MAJOR 127
113 	"fw", CDEV_MAJOR, D_MEM, NULL, 0,
114 	fw_open, fw_close, fw_read, fw_write, fw_ioctl,
115 	fw_poll, fw_mmap, fw_strategy, nodump, nopsize,
116 #elif __FreeBSD_version >= 500104
117 	.d_version =	D_VERSION,
118 	.d_open =	fw_open,
119 	.d_close =	fw_close,
120 	.d_read =	fw_read,
121 	.d_write =	fw_write,
122 	.d_ioctl =	fw_ioctl,
123 	.d_poll =	fw_poll,
124 	.d_mmap =	fw_mmap,
125 	.d_strategy =	fw_strategy,
126 	.d_name =	"fw",
127 	.d_flags =	D_MEM | D_NEEDGIANT
128 #else
129 #define CDEV_MAJOR 127
130 	fw_open, fw_close, fw_read, fw_write, fw_ioctl,
131 	fw_poll, fw_mmap, fw_strategy, "fw", CDEV_MAJOR,
132 	nodump, nopsize, D_MEM, -1
133 #endif
134 };
135 #elif defined(__NetBSD__)
136 dev_type_open(fw_open);
137 dev_type_close(fw_close);
138 dev_type_read(fw_read);
139 dev_type_write(fw_write);
140 dev_type_ioctl(fw_ioctl);
141 dev_type_poll(fw_poll);
142 dev_type_mmap(fw_mmap);
143 dev_type_strategy(fw_strategy);
144 
145 const struct bdevsw fw_bdevsw = {
146 	fw_open, fw_close, fw_strategy, fw_ioctl, nodump, nosize,
147 };
148 
149 const struct cdevsw fw_cdevsw = {
150 	fw_open, fw_close, fw_read, fw_write, fw_ioctl,
151 	nostop, notty, fw_poll, fw_mmap, nokqfilter,
152 };
153 #endif
154 
155 struct fw_drv1 {
156 	struct firewire_comm *fc;
157 	struct fw_xferq *ir;
158 	struct fw_xferq *it;
159 	struct fw_isobufreq bufreq;
160 	STAILQ_HEAD(, fw_bind) binds;
161 	STAILQ_HEAD(, fw_xfer) rq;
162 };
163 
164 static int
165 fwdev_allocbuf(struct firewire_comm *fc, struct fw_xferq *q,
166 	struct fw_bufspec *b)
167 {
168 	int i;
169 
170 	if (q->flag & (FWXFERQ_RUNNING | FWXFERQ_EXTBUF))
171 		return(EBUSY);
172 
173 	q->bulkxfer = (struct fw_bulkxfer *) malloc(
174 		sizeof(struct fw_bulkxfer) * b->nchunk,
175 		M_FW, M_WAITOK);
176 	if (q->bulkxfer == NULL)
177 		return(ENOMEM);
178 
179 	b->psize = roundup2(b->psize, sizeof(uint32_t));
180 	q->buf = fwdma_malloc_multiseg(fc, sizeof(uint32_t),
181 			b->psize, b->nchunk * b->npacket, BUS_DMA_WAITOK);
182 
183 	if (q->buf == NULL) {
184 		free(q->bulkxfer, M_FW);
185 		q->bulkxfer = NULL;
186 		return(ENOMEM);
187 	}
188 	q->bnchunk = b->nchunk;
189 	q->bnpacket = b->npacket;
190 	q->psize = (b->psize + 3) & ~3;
191 	q->queued = 0;
192 
193 	STAILQ_INIT(&q->stvalid);
194 	STAILQ_INIT(&q->stfree);
195 	STAILQ_INIT(&q->stdma);
196 	q->stproc = NULL;
197 
198 	for(i = 0 ; i < q->bnchunk; i++){
199 		q->bulkxfer[i].poffset = i * q->bnpacket;
200 		q->bulkxfer[i].mbuf = NULL;
201 		STAILQ_INSERT_TAIL(&q->stfree, &q->bulkxfer[i], link);
202 	}
203 
204 	q->flag &= ~FWXFERQ_MODEMASK;
205 	q->flag |= FWXFERQ_STREAM;
206 	q->flag |= FWXFERQ_EXTBUF;
207 
208 	return (0);
209 }
210 
211 static int
212 fwdev_freebuf(struct fw_xferq *q)
213 {
214 	if (q->flag & FWXFERQ_EXTBUF) {
215 		if (q->buf != NULL)
216 			fwdma_free_multiseg(q->buf);
217 		q->buf = NULL;
218 		free(q->bulkxfer, M_FW);
219 		q->bulkxfer = NULL;
220 		q->flag &= ~FWXFERQ_EXTBUF;
221 		q->psize = 0;
222 		q->maxq = FWMAXQUEUE;
223 	}
224 	return (0);
225 }
226 
227 
228 FW_OPEN(fw)
229 {
230 	int err = 0;
231 	struct fw_drv1 *d;
232 	FW_OPEN_START;
233 
234 	FWDEV_OPEN_START;
235 
236 	if (dev->si_drv1 != NULL)
237 		return (EBUSY);
238 
239 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000
240 	if ((dev->si_flags & SI_NAMED) == 0) {
241 		int unit = DEV2UNIT(dev);
242 		int sub = DEV2SUB(dev);
243 
244 		make_dev(&firewire_cdevsw, minor(dev),
245 			UID_ROOT, GID_OPERATOR, 0660,
246 			"fw%d.%d", unit, sub);
247 	}
248 #endif
249 
250 	dev->si_drv1 = malloc(sizeof(struct fw_drv1), M_FW, M_WAITOK | M_ZERO);
251 	if (dev->si_drv1 == NULL)
252 		return (ENOMEM);
253 
254 	d = (struct fw_drv1 *)dev->si_drv1;
255 	d->fc = sc->fc;
256 	STAILQ_INIT(&d->binds);
257 	STAILQ_INIT(&d->rq);
258 
259 	return err;
260 }
261 
262 FW_CLOSE(fw)
263 {
264 	struct firewire_comm *fc;
265 	struct fw_drv1 *d;
266 	struct fw_xfer *xfer;
267 	struct fw_bind *fwb;
268 	int err = 0;
269 	FW_CLOSE_START;
270 
271 	FWDEV_CLOSE_START;
272 
273 	d = (struct fw_drv1 *)dev->si_drv1;
274 	fc = d->fc;
275 
276 	/* remove binding */
277 	for (fwb = STAILQ_FIRST(&d->binds); fwb != NULL;
278 			fwb = STAILQ_FIRST(&d->binds)) {
279 		fw_bindremove(fc, fwb);
280 		STAILQ_REMOVE_HEAD(&d->binds, chlist);
281 		fw_xferlist_remove(&fwb->xferlist);
282 		free(fwb, M_FW);
283 	}
284 	if (d->ir != NULL) {
285 		struct fw_xferq *ir = d->ir;
286 
287 		if ((ir->flag & FWXFERQ_OPEN) == 0)
288 			return (EINVAL);
289 		if (ir->flag & FWXFERQ_RUNNING) {
290 			ir->flag &= ~FWXFERQ_RUNNING;
291 			fc->irx_disable(fc, ir->dmach);
292 		}
293 		/* free extbuf */
294 		fwdev_freebuf(ir);
295 		/* drain receiving buffer */
296 		for (xfer = STAILQ_FIRST(&ir->q);
297 			xfer != NULL; xfer = STAILQ_FIRST(&ir->q)) {
298 			ir->queued --;
299 			STAILQ_REMOVE_HEAD(&ir->q, link);
300 
301 			xfer->resp = 0;
302 			fw_xfer_done(xfer);
303 		}
304 		ir->flag &= ~(FWXFERQ_OPEN |
305 			FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK);
306 		d->ir = NULL;
307 
308 	}
309 	if (d->it != NULL) {
310 		struct fw_xferq *it = d->it;
311 
312 		if ((it->flag & FWXFERQ_OPEN) == 0)
313 			return (EINVAL);
314 		if (it->flag & FWXFERQ_RUNNING) {
315 			it->flag &= ~FWXFERQ_RUNNING;
316 			fc->itx_disable(fc, it->dmach);
317 		}
318 		/* free extbuf */
319 		fwdev_freebuf(it);
320 		it->flag &= ~(FWXFERQ_OPEN |
321 			FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK);
322 		d->it = NULL;
323 	}
324 	free(dev->si_drv1, M_FW);
325 	dev->si_drv1 = NULL;
326 
327 	return err;
328 }
329 
330 static int
331 fw_read_async(struct fw_drv1 *d, struct uio *uio, int ioflag)
332 {
333 	int err = 0, s;
334 	struct fw_xfer *xfer;
335 	struct fw_bind *fwb;
336 	struct fw_pkt *fp;
337 	const struct tcode_info *tinfo;
338 
339 	while ((xfer = STAILQ_FIRST(&d->rq)) == NULL && err == 0)
340 		err = tsleep(&d->rq, FWPRI, "fwra", 0);
341 
342 	if (err != 0)
343 		return (err);
344 
345 	s = splfw();
346 	STAILQ_REMOVE_HEAD(&d->rq, link);
347 	splx(s);
348 	fp = &xfer->recv.hdr;
349 #if 0 /* for GASP ?? */
350 	if (fc->irx_post != NULL)
351 		fc->irx_post(fc, fp->mode.ld);
352 #endif
353 	tinfo = &xfer->fc->tcode[fp->mode.hdr.tcode];
354 	err = uiomove((void *)fp, tinfo->hdr_len, uio);
355 	if (err)
356 		goto out;
357 	err = uiomove((void *)xfer->recv.payload, xfer->recv.pay_len, uio);
358 
359 out:
360 	/* recycle this xfer */
361 	fwb = (struct fw_bind *)xfer->sc;
362 	fw_xfer_unload(xfer);
363 	xfer->recv.pay_len = PAGE_SIZE;
364 	STAILQ_INSERT_TAIL(&fwb->xferlist, xfer, link);
365 	return (err);
366 }
367 
368 /*
369  * read request.
370  */
371 FW_READ(fw)
372 {
373 	FW_READ_START;
374 	struct fw_drv1 *d;
375 	struct fw_xferq *ir;
376 	struct firewire_comm *fc;
377 	int err = 0, s, slept = 0;
378 	struct fw_pkt *fp;
379 
380 	FWDEV_READ_START;
381 
382 	d = (struct fw_drv1 *)dev->si_drv1;
383 	fc = d->fc;
384 	ir = d->ir;
385 
386 	if (ir == NULL)
387 		return (fw_read_async(d, uio, ioflag));
388 
389 	if (ir->buf == NULL)
390 		return (EIO);
391 
392 readloop:
393 	if (ir->stproc == NULL) {
394 		/* iso bulkxfer */
395 		ir->stproc = STAILQ_FIRST(&ir->stvalid);
396 		if (ir->stproc != NULL) {
397 			s = splfw();
398 			STAILQ_REMOVE_HEAD(&ir->stvalid, link);
399 			splx(s);
400 			ir->queued = 0;
401 		}
402 	}
403 	if (ir->stproc == NULL) {
404 		/* no data avaliable */
405 		if (slept == 0) {
406 			slept = 1;
407 			ir->flag |= FWXFERQ_WAKEUP;
408 			err = tsleep(ir, FWPRI, "fw_read", hz);
409 			ir->flag &= ~FWXFERQ_WAKEUP;
410 			if (err == 0)
411 				goto readloop;
412 		} else if (slept == 1)
413 			err = EIO;
414 		return err;
415 	} else if(ir->stproc != NULL) {
416 		/* iso bulkxfer */
417 		fp = (struct fw_pkt *)fwdma_v_addr(ir->buf,
418 				ir->stproc->poffset + ir->queued);
419 		if(fc->irx_post != NULL)
420 			fc->irx_post(fc, fp->mode.ld);
421 		if(fp->mode.stream.len == 0){
422 			err = EIO;
423 			return err;
424 		}
425 		err = uiomove((caddr_t)fp,
426 			fp->mode.stream.len + sizeof(uint32_t), uio);
427 		ir->queued ++;
428 		if(ir->queued >= ir->bnpacket){
429 			s = splfw();
430 			STAILQ_INSERT_TAIL(&ir->stfree, ir->stproc, link);
431 			splx(s);
432 			fc->irx_enable(fc, ir->dmach);
433 			ir->stproc = NULL;
434 		}
435 		if (uio->uio_resid >= ir->psize) {
436 			slept = -1;
437 			goto readloop;
438 		}
439 	}
440 	return err;
441 }
442 
443 static int
444 fw_write_async(struct fw_drv1 *d, struct uio *uio, int ioflag)
445 {
446 	struct fw_xfer *xfer;
447 	struct fw_pkt pkt;
448 	const struct tcode_info *tinfo;
449 	int err;
450 
451 	bzero(&pkt, sizeof(struct fw_pkt));
452 	if ((err = uiomove((caddr_t)&pkt, sizeof(uint32_t), uio)))
453 		return (err);
454 	tinfo = &d->fc->tcode[pkt.mode.hdr.tcode];
455 	if ((err = uiomove((caddr_t)&pkt + sizeof(uint32_t),
456 	    tinfo->hdr_len - sizeof(uint32_t), uio)))
457 		return (err);
458 
459 	if ((xfer = fw_xfer_alloc_buf(M_FWXFER, uio->uio_resid,
460 	    PAGE_SIZE/*XXX*/)) == NULL)
461 		return (ENOMEM);
462 
463 	bcopy(&pkt, &xfer->send.hdr, sizeof(struct fw_pkt));
464 	xfer->send.pay_len = uio->uio_resid;
465 	if (uio->uio_resid > 0) {
466 		if ((err = uiomove((caddr_t)&xfer->send.payload[0],
467 		    uio->uio_resid, uio)));
468 			goto out;
469 	}
470 
471 	xfer->fc = d->fc;
472 	xfer->sc = NULL;
473 	xfer->hand = fw_asy_callback;
474 	xfer->send.spd = 2 /* XXX */;
475 
476 	if ((err = fw_asyreq(xfer->fc, -1, xfer)))
477 		goto out;
478 
479 	if ((err = tsleep(xfer, FWPRI, "fwwa", 0)))
480 		goto out;
481 
482 	if (xfer->resp != 0) {
483 		err = xfer->resp;
484 		goto out;
485 	}
486 
487 	if (xfer->state == FWXF_RCVD) {
488 		STAILQ_INSERT_TAIL(&d->rq, xfer, link);
489 		return (0);
490 	}
491 
492 out:
493 	fw_xfer_free(xfer);
494 	return (err);
495 }
496 
497 FW_WRITE(fw)
498 {
499 	FW_WRITE_START;
500 	int err = 0;
501 	int s, slept = 0;
502 	struct fw_drv1 *d;
503 	struct fw_pkt *fp;
504 	struct firewire_comm *fc;
505 	struct fw_xferq *it;
506 
507 	FWDEV_WRITE_START;
508 	d = (struct fw_drv1 *)dev->si_drv1;
509 	fc = d->fc;
510 	it = d->it;
511 
512 	if (it == NULL)
513 		return (fw_write_async(d, uio, ioflag));
514 
515 	if (it->buf == NULL)
516 		return (EIO);
517 isoloop:
518 	if (it->stproc == NULL) {
519 		it->stproc = STAILQ_FIRST(&it->stfree);
520 		if (it->stproc != NULL) {
521 			s = splfw();
522 			STAILQ_REMOVE_HEAD(&it->stfree, link);
523 			splx(s);
524 			it->queued = 0;
525 		} else if (slept == 0) {
526 			slept = 1;
527 			err = fc->itx_enable(fc, it->dmach);
528 			if (err)
529 				return err;
530 			err = tsleep(it, FWPRI, "fw_write", hz);
531 			if (err)
532 				return err;
533 			goto isoloop;
534 		} else {
535 			err = EIO;
536 			return err;
537 		}
538 	}
539 	fp = (struct fw_pkt *)fwdma_v_addr(it->buf,
540 			it->stproc->poffset + it->queued);
541 	err = uiomove((caddr_t)fp, sizeof(struct fw_isohdr), uio);
542 	err = uiomove((caddr_t)fp->mode.stream.payload,
543 				fp->mode.stream.len, uio);
544 	it->queued ++;
545 	if (it->queued >= it->bnpacket) {
546 		s = splfw();
547 		STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link);
548 		splx(s);
549 		it->stproc = NULL;
550 		err = fc->itx_enable(fc, it->dmach);
551 	}
552 	if (uio->uio_resid >= sizeof(struct fw_isohdr)) {
553 		slept = 0;
554 		goto isoloop;
555 	}
556 	return err;
557 }
558 
559 static void
560 fw_hand(struct fw_xfer *xfer)
561 {
562 	struct fw_bind *fwb;
563 	struct fw_drv1 *d;
564 
565 	fwb = (struct fw_bind *)xfer->sc;
566 	d = (struct fw_drv1 *)fwb->sc;
567 	STAILQ_INSERT_TAIL(&d->rq, xfer, link);
568 	wakeup(&d->rq);
569 }
570 
571 /*
572  * ioctl support.
573  */
574 FW_IOCTL(fw)
575 {
576 	FW_IOCTL_START;
577 	struct firewire_comm *fc;
578 	struct fw_drv1 *d;
579 	int i, len, err = 0;
580 	struct fw_device *fwdev;
581 	struct fw_bind *fwb;
582 	struct fw_xferq *ir, *it;
583 	struct fw_xfer *xfer;
584 	struct fw_pkt *fp;
585 	struct fw_devinfo *devinfo;
586 	void *ptr;
587 
588 	struct fw_devlstreq *fwdevlst = (struct fw_devlstreq *)data;
589 	struct fw_asyreq *asyreq = (struct fw_asyreq *)data;
590 	struct fw_isochreq *ichreq = (struct fw_isochreq *)data;
591 	struct fw_isobufreq *ibufreq = (struct fw_isobufreq *)data;
592 	struct fw_asybindreq *bindreq = (struct fw_asybindreq *)data;
593 	struct fw_crom_buf *crom_buf = (struct fw_crom_buf *)data;
594 
595 	FWDEV_IOCTL_START;
596 
597 	if (!data)
598 		return(EINVAL);
599 
600 	d = (struct fw_drv1 *)dev->si_drv1;
601 	fc = d->fc;
602 	ir = d->ir;
603 	it = d->it;
604 
605 	switch (cmd) {
606 	case FW_STSTREAM:
607 		if (it == NULL) {
608 			for (i = 0; i < fc->nisodma; i ++) {
609 				it = fc->it[i];
610 				if ((it->flag & FWXFERQ_OPEN) == 0)
611 					 break;
612 	                }
613 			if (i >= fc->nisodma) {
614 				err = EBUSY;
615 				break;
616 			}
617 			err = fwdev_allocbuf(fc, it, &d->bufreq.tx);
618 			if (err)
619 				break;
620 			it->flag |=  FWXFERQ_OPEN;
621 		}
622 		it->flag &= ~0xff;
623 		it->flag |= (0x3f & ichreq->ch);
624 		it->flag |= ((0x3 & ichreq->tag) << 6);
625 		d->it = it;
626 		break;
627 	case FW_GTSTREAM:
628 		if (it != NULL) {
629 			ichreq->ch = it->flag & 0x3f;
630 			ichreq->tag = it->flag >> 2 & 0x3;
631 		} else
632 			err = EINVAL;
633 		break;
634 	case FW_SRSTREAM:
635 		if (ir == NULL) {
636 			for (i = 0; i < fc->nisodma; i ++) {
637 				ir = fc->ir[i];
638 				if ((ir->flag & FWXFERQ_OPEN) == 0)
639 					break;
640 			}
641 			if (i >= fc->nisodma) {
642 				err = EBUSY;
643 				break;
644 			}
645 			err = fwdev_allocbuf(fc, ir, &d->bufreq.rx);
646 			if (err)
647 				break;
648 			ir->flag |=  FWXFERQ_OPEN;
649 		}
650 		ir->flag &= ~0xff;
651 		ir->flag |= (0x3f & ichreq->ch);
652 		ir->flag |= ((0x3 & ichreq->tag) << 6);
653 		d->ir = ir;
654 		err = fc->irx_enable(fc, ir->dmach);
655 		break;
656 	case FW_GRSTREAM:
657 		if (d->ir != NULL) {
658 			ichreq->ch = ir->flag & 0x3f;
659 			ichreq->tag = ir->flag >> 2 & 0x3;
660 		} else
661 			err = EINVAL;
662 		break;
663 	case FW_SSTBUF:
664 		bcopy(ibufreq, &d->bufreq, sizeof(d->bufreq));
665 		break;
666 	case FW_GSTBUF:
667 		bzero(&ibufreq->rx, sizeof(ibufreq->rx));
668 		if (ir != NULL) {
669 			ibufreq->rx.nchunk = ir->bnchunk;
670 			ibufreq->rx.npacket = ir->bnpacket;
671 			ibufreq->rx.psize = ir->psize;
672 		}
673 		bzero(&ibufreq->tx, sizeof(ibufreq->tx));
674 		if (it != NULL) {
675 			ibufreq->tx.nchunk = it->bnchunk;
676 			ibufreq->tx.npacket = it->bnpacket;
677 			ibufreq->tx.psize = it->psize;
678 		}
679 		break;
680 	case FW_ASYREQ:
681 	{
682 		const struct tcode_info *tinfo;
683 		int pay_len = 0;
684 
685 		fp = &asyreq->pkt;
686 		tinfo = &fc->tcode[fp->mode.hdr.tcode];
687 
688 		if ((tinfo->flag & FWTI_BLOCK_ASY) != 0)
689 			pay_len = MAX(0, asyreq->req.len - tinfo->hdr_len);
690 
691 		xfer = fw_xfer_alloc_buf(M_FWXFER, pay_len, PAGE_SIZE/*XXX*/);
692 		if (xfer == NULL)
693 			return (ENOMEM);
694 
695 		switch (asyreq->req.type) {
696 		case FWASREQNODE:
697 			break;
698 		case FWASREQEUI:
699 			fwdev = fw_noderesolve_eui64(fc,
700 						&asyreq->req.dst.eui);
701 			if (fwdev == NULL) {
702 				device_printf(fc->bdev,
703 					"cannot find node\n");
704 				err = EINVAL;
705 				goto out;
706 			}
707 			fp->mode.hdr.dst = FWLOCALBUS | fwdev->dst;
708 			break;
709 		case FWASRESTL:
710 			/* XXX what's this? */
711 			break;
712 		case FWASREQSTREAM:
713 			/* nothing to do */
714 			break;
715 		}
716 
717 		bcopy(fp, (void *)&xfer->send.hdr, tinfo->hdr_len);
718 		if (pay_len > 0)
719 			bcopy((char *)fp + tinfo->hdr_len,
720 			    (void *)xfer->send.payload, pay_len);
721 		xfer->send.spd = asyreq->req.sped;
722 		xfer->hand = fw_asy_callback;
723 
724 		if ((err = fw_asyreq(fc, -1, xfer)) != 0)
725 			goto out;
726 		if ((err = tsleep(xfer, FWPRI, "asyreq", hz)) != 0)
727 			goto out;
728 		if (xfer->resp != 0) {
729 			err = EIO;
730 			goto out;
731 		}
732 		if ((tinfo->flag & FWTI_TLABEL) == 0)
733 			goto out;
734 
735 		/* copy response */
736 		tinfo = &fc->tcode[xfer->recv.hdr.mode.hdr.tcode];
737 		if (xfer->recv.hdr.mode.hdr.tcode == FWTCODE_RRESB ||
738 		    xfer->recv.hdr.mode.hdr.tcode == FWTCODE_LRES) {
739 			pay_len = xfer->recv.pay_len;
740 			if (asyreq->req.len >= xfer->recv.pay_len + tinfo->hdr_len) {
741 				asyreq->req.len = xfer->recv.pay_len +
742 					tinfo->hdr_len;
743 			} else {
744 				err = EINVAL;
745 				pay_len = 0;
746 			}
747 		} else {
748 			pay_len = 0;
749 		}
750 		bcopy(&xfer->recv.hdr, fp, tinfo->hdr_len);
751 		bcopy(xfer->recv.payload, (char *)fp + tinfo->hdr_len, pay_len);
752 out:
753 		fw_xfer_free_buf(xfer);
754 		break;
755 	}
756 	case FW_IBUSRST:
757 		fc->ibr(fc);
758 		break;
759 	case FW_CBINDADDR:
760 		fwb = fw_bindlookup(fc,
761 				bindreq->start.hi, bindreq->start.lo);
762 		if(fwb == NULL){
763 			err = EINVAL;
764 			break;
765 		}
766 		fw_bindremove(fc, fwb);
767 		STAILQ_REMOVE(&d->binds, fwb, fw_bind, chlist);
768 		fw_xferlist_remove(&fwb->xferlist);
769 		free(fwb, M_FW);
770 		break;
771 	case FW_SBINDADDR:
772 		if(bindreq->len <= 0 ){
773 			err = EINVAL;
774 			break;
775 		}
776 		if(bindreq->start.hi > 0xffff ){
777 			err = EINVAL;
778 			break;
779 		}
780 		fwb = (struct fw_bind *)malloc(sizeof (struct fw_bind), M_FW, M_NOWAIT);
781 		if(fwb == NULL){
782 			err = ENOMEM;
783 			break;
784 		}
785 		fwb->start = ((u_int64_t)bindreq->start.hi << 32) |
786 		    bindreq->start.lo;
787 		fwb->end = fwb->start +  bindreq->len;
788 		fwb->sc = (void *)d;
789 		STAILQ_INIT(&fwb->xferlist);
790 		err = fw_bindadd(fc, fwb);
791 		if (err == 0) {
792 			fw_xferlist_add(&fwb->xferlist, M_FWXFER,
793 			    /* XXX */
794 			    PAGE_SIZE, PAGE_SIZE, 5,
795 			    fc, (void *)fwb, fw_hand);
796 			STAILQ_INSERT_TAIL(&d->binds, fwb, chlist);
797 		}
798 		break;
799 	case FW_GDEVLST:
800 		i = len = 1;
801 		/* myself */
802 		devinfo = &fwdevlst->dev[0];
803 		devinfo->dst = fc->nodeid;
804 		devinfo->status = 0;	/* XXX */
805 		devinfo->eui.hi = fc->eui.hi;
806 		devinfo->eui.lo = fc->eui.lo;
807 		STAILQ_FOREACH(fwdev, &fc->devices, link) {
808 			if(len < FW_MAX_DEVLST){
809 				devinfo = &fwdevlst->dev[len++];
810 				devinfo->dst = fwdev->dst;
811 				devinfo->status =
812 					(fwdev->status == FWDEVINVAL)?0:1;
813 				devinfo->eui.hi = fwdev->eui.hi;
814 				devinfo->eui.lo = fwdev->eui.lo;
815 			}
816 			i++;
817 		}
818 		fwdevlst->n = i;
819 		fwdevlst->info_len = len;
820 		break;
821 	case FW_GTPMAP:
822 		bcopy(fc->topology_map, data,
823 				(fc->topology_map->crc_len + 1) * 4);
824 		break;
825 	case FW_GCROM:
826 		STAILQ_FOREACH(fwdev, &fc->devices, link)
827 			if (FW_EUI64_EQUAL(fwdev->eui, crom_buf->eui))
828 				break;
829 		if (fwdev == NULL) {
830 			if (!FW_EUI64_EQUAL(fc->eui, crom_buf->eui)) {
831 				err = FWNODE_INVAL;
832 				break;
833 			}
834 			/* myself */
835 			ptr = malloc(CROMSIZE, M_FW, M_WAITOK);
836 			len = CROMSIZE;
837 			for (i = 0; i < CROMSIZE/4; i++)
838 				((uint32_t *)ptr)[i]
839 					= ntohl(fc->config_rom[i]);
840 		} else {
841 			/* found */
842 			ptr = (void *)&fwdev->csrrom[0];
843 			if (fwdev->rommax < CSRROMOFF)
844 				len = 0;
845 			else
846 				len = fwdev->rommax - CSRROMOFF + 4;
847 		}
848 		if (crom_buf->len < len)
849 			len = crom_buf->len;
850 		else
851 			crom_buf->len = len;
852 		err = copyout(ptr, crom_buf->ptr, len);
853 		if (fwdev == NULL)
854 			/* myself */
855 			free(ptr, M_FW);
856 		break;
857 	default:
858 		FWDEV_IOCTL_REDIRECT;
859 		break;
860 	}
861 	return err;
862 }
863 
864 FW_POLL(fw)
865 {
866 	FW_POLL_START;
867 	struct fw_xferq *ir;
868 	int revents;
869 	int tmp;
870 
871 	FWDEV_POLL_START;
872 
873 	ir = ((struct fw_drv1 *)dev->si_drv1)->ir;
874 	revents = 0;
875 	tmp = POLLIN | POLLRDNORM;
876 	if (events & tmp) {
877 		if (STAILQ_FIRST(&ir->q) != NULL)
878 			revents |= tmp;
879 		else
880 			selrecord(td, &ir->rsel);
881 	}
882 	tmp = POLLOUT | POLLWRNORM;
883 	if (events & tmp) {
884 		/* XXX should be fixed */
885 		revents |= tmp;
886 	}
887 
888 	return revents;
889 }
890 
891 FW_MMAP(fw)
892 {
893 	FW_MMAP_START;
894 
895 	FWDEV_MMAP_START;
896 
897 	return EINVAL;
898 }
899 
900 void
901 fw_strategy(struct bio *bp)
902 {
903 	FW_STRATEGY_START;
904 
905 	FWDEV_STRATEGY_START;
906 
907 	bp->bio_error = EOPNOTSUPP;
908 	bp->bio_flags |= BIO_ERROR;
909 	bp->bio_resid = bp->bio_bcount;
910 	biodone(bp);
911 }
912 
913 #if defined(__FreeBSD__)
914 int
915 fwdev_makedev(struct firewire_softc *sc)
916 {
917 	int err = 0;
918 
919 #if defined(__DragonFly__)
920 	int unit;
921 
922 	unit = device_get_unit(sc->fc->bdev);
923 	cdevsw_add(&firewire_cdevsw, FW_UNITMASK, FW_UNIT(unit));
924 #elif __FreeBSD_version < 500000
925 	cdevsw_add(&firewire_cdevsw);
926 #else
927 	DEV_T d;
928 	int unit;
929 
930 	unit = device_get_unit(sc->fc->bdev);
931 	sc->dev = make_dev(&firewire_cdevsw, MAKEMINOR(0, unit, 0),
932 			UID_ROOT, GID_OPERATOR, 0660,
933 			"fw%d.%d", unit, 0);
934 	d = make_dev(&firewire_cdevsw,
935 			MAKEMINOR(FWMEM_FLAG, unit, 0),
936 			UID_ROOT, GID_OPERATOR, 0660,
937 			"fwmem%d.%d", unit, 0);
938 	dev_depends(sc->dev, d);
939 	make_dev_alias(sc->dev, "fw%d", unit);
940 	make_dev_alias(d, "fwmem%d", unit);
941 #endif
942 
943 	return (err);
944 }
945 
946 int
947 fwdev_destroydev(struct firewire_softc *sc)
948 {
949 	int err = 0;
950 
951 #if defined(__DragonFly__)
952 	int unit;
953 
954 	unit = device_get_unit(sc->fc->bdev);
955 	cdevsw_remove(&firewire_cdevsw, FW_UNITMASK, FW_UNIT(unit));
956 #elif __FreeBSD_version < 500000
957 	cdevsw_remove(&firewire_cdevsw);
958 #else
959 	destroy_dev(sc->dev);
960 #endif
961 	return (err);
962 }
963 
964 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000
965 #define NDEVTYPE 2
966 void
967 fwdev_clone(void *arg, char *name, int namelen, DEV_T *dev)
968 {
969 	struct firewire_softc *sc;
970 	char *devnames[NDEVTYPE] = {"fw", "fwmem"};
971 	char *subp = NULL;
972 	int devflag[NDEVTYPE] = {0, FWMEM_FLAG};
973 	int i, unit = 0, sub = 0;
974 
975 	if (*dev != NULL)
976 		return;
977 
978 	for (i = 0; i < NDEVTYPE; i++)
979 		if (dev_stdclone(name, &subp, devnames[i], &unit) == 2)
980 			goto found;
981 	/* not match */
982 	return;
983 found:
984 
985 	if (subp == NULL || *subp++ != '.')
986 		return;
987 
988 	/* /dev/fwU.S */
989 	while (isdigit(*subp)) {
990 		sub *= 10;
991 		sub += *subp++ - '0';
992 	}
993 	if (*subp != '\0')
994 		return;
995 
996 	sc = devclass_get_softc(firewire_devclass, unit);
997 	if (sc == NULL)
998 		return;
999 	*dev = make_dev(&firewire_cdevsw, MAKEMINOR(devflag[i], unit, sub),
1000 		       UID_ROOT, GID_OPERATOR, 0660,
1001 		       "%s%d.%d", devnames[i], unit, sub);
1002 	dev_ref(*dev);
1003 	(*dev)->si_flags |= SI_CHEAPCLONE;
1004 	dev_depends(sc->dev, *dev);
1005 	return;
1006 }
1007 #endif
1008 #endif
1009