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