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