xref: /freebsd/sys/dev/firewire/fwdev.c (revision 6780ab54325a71e7e70112b11657973edde8655e)
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 #include <dev/firewire/iec68113.h>
56 
57 #define CDEV_MAJOR 127
58 #define	FWNODE_INVAL 0xffff
59 
60 static	d_open_t	fw_open;
61 static	d_close_t	fw_close;
62 static	d_ioctl_t	fw_ioctl;
63 static	d_poll_t	fw_poll;
64 static	d_read_t	fw_read;	/* for Isochronous packet */
65 static	d_write_t	fw_write;
66 static	d_mmap_t	fw_mmap;
67 
68 struct cdevsw firewire_cdevsw =
69 {
70 	fw_open, fw_close, fw_read, fw_write, fw_ioctl,
71 	fw_poll, fw_mmap, nostrategy, "fw", CDEV_MAJOR, nodump, nopsize, D_MEM
72 };
73 
74 static int
75 fw_open (dev_t dev, int flags, int fmt, fw_proc *td)
76 {
77 	struct firewire_softc *sc;
78 	int unit = DEV2UNIT(dev);
79 	int sub = DEV2DMACH(dev);
80 
81 	int err = 0;
82 
83 	if (DEV_FWMEM(dev))
84 		return fwmem_open(dev, flags, fmt, td);
85 
86 	sc = devclass_get_softc(firewire_devclass, unit);
87 	if(sc->fc->ir[sub]->flag & FWXFERQ_OPEN){
88 		err = EBUSY;
89 		return err;
90 	}
91 	if(sc->fc->it[sub]->flag & FWXFERQ_OPEN){
92 		err = EBUSY;
93 		return err;
94 	}
95 	if(sc->fc->ir[sub]->flag & FWXFERQ_MODEMASK){
96 		err = EBUSY;
97 		return err;
98 	}
99 /* Default is per packet mode */
100 	sc->fc->ir[sub]->flag |= FWXFERQ_OPEN;
101 	sc->fc->it[sub]->flag |= FWXFERQ_OPEN;
102 	sc->fc->ir[sub]->flag |= FWXFERQ_PACKET;
103 	return err;
104 }
105 
106 static int
107 fw_close (dev_t dev, int flags, int fmt, fw_proc *td)
108 {
109 	struct firewire_softc *sc;
110 	int unit = DEV2UNIT(dev);
111 	int sub = DEV2DMACH(dev);
112 	struct fw_xfer *xfer;
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 #ifdef FWXFERQ_DV
139 	if(sc->fc->it[sub]->flag & FWXFERQ_DV){
140 		struct fw_dvbuf *dvbuf;
141 
142 		if((dvbuf = sc->fc->it[sub]->dvproc) != NULL){
143 			free(dvbuf->buf, M_FW);
144 			sc->fc->it[sub]->dvproc = NULL;
145 		}
146 		if((dvbuf = sc->fc->it[sub]->dvdma) != NULL){
147 			free(dvbuf->buf, M_FW);
148 			sc->fc->it[sub]->dvdma = NULL;
149 		}
150 		while((dvbuf = STAILQ_FIRST(&sc->fc->it[sub]->dvvalid)) != NULL){
151 			STAILQ_REMOVE_HEAD(&sc->fc->it[sub]->dvvalid, link);
152 			free(dvbuf->buf, M_FW);
153 		}
154 		while((dvbuf = STAILQ_FIRST(&sc->fc->it[sub]->dvfree)) != NULL){
155 			STAILQ_REMOVE_HEAD(&sc->fc->it[sub]->dvfree, link);
156 			free(dvbuf->buf, M_FW);
157 		}
158 		free(sc->fc->it[sub]->dvbuf, M_FW);
159 		sc->fc->it[sub]->dvbuf = NULL;
160 	}
161 #endif
162 	if(sc->fc->ir[sub]->flag & FWXFERQ_EXTBUF){
163 		free(sc->fc->ir[sub]->buf, M_FW);
164 		sc->fc->ir[sub]->buf = NULL;
165 		free(sc->fc->ir[sub]->bulkxfer, M_FW);
166 		sc->fc->ir[sub]->bulkxfer = NULL;
167 		sc->fc->ir[sub]->flag &= ~FWXFERQ_EXTBUF;
168 		sc->fc->ir[sub]->psize = PAGE_SIZE;
169 		sc->fc->ir[sub]->maxq = FWMAXQUEUE;
170 	}
171 	if(sc->fc->it[sub]->flag & FWXFERQ_EXTBUF){
172 		free(sc->fc->it[sub]->buf, M_FW);
173 		sc->fc->it[sub]->buf = NULL;
174 		free(sc->fc->it[sub]->bulkxfer, M_FW);
175 		sc->fc->it[sub]->bulkxfer = NULL;
176 #ifdef FWXFERQ_DV
177 		sc->fc->it[sub]->dvbuf = NULL;
178 #endif
179 		sc->fc->it[sub]->flag &= ~FWXFERQ_EXTBUF;
180 		sc->fc->it[sub]->psize = 0;
181 		sc->fc->it[sub]->maxq = FWMAXQUEUE;
182 	}
183 	for(xfer = STAILQ_FIRST(&sc->fc->ir[sub]->q);
184 		xfer != NULL; xfer = STAILQ_FIRST(&sc->fc->ir[sub]->q)){
185 		sc->fc->ir[sub]->queued--;
186 		STAILQ_REMOVE_HEAD(&sc->fc->ir[sub]->q, link);
187 
188 		xfer->resp = 0;
189 		switch(xfer->act_type){
190 		case FWACT_XFER:
191 			fw_xfer_done(xfer);
192 			break;
193 		default:
194 			break;
195 		}
196 		fw_xfer_free(xfer);
197 	}
198 	for(fwb = STAILQ_FIRST(&sc->fc->ir[sub]->binds); fwb != NULL;
199 		fwb = STAILQ_FIRST(&sc->fc->ir[sub]->binds)){
200 		STAILQ_REMOVE(&sc->fc->binds, fwb, fw_bind, fclist);
201 		STAILQ_REMOVE_HEAD(&sc->fc->ir[sub]->binds, chlist);
202 		free(fwb, M_FW);
203 	}
204 	sc->fc->ir[sub]->flag &= ~FWXFERQ_MODEMASK;
205 	sc->fc->it[sub]->flag &= ~FWXFERQ_MODEMASK;
206 	return err;
207 }
208 
209 /*
210  * read request.
211  */
212 static int
213 fw_read (dev_t dev, struct uio *uio, int ioflag)
214 {
215 	struct firewire_softc *sc;
216 	struct fw_xferq *ir;
217 	struct fw_xfer *xfer;
218 	int err = 0, s, slept = 0;
219 	int unit = DEV2UNIT(dev);
220 	int sub = DEV2DMACH(dev);
221 	struct fw_pkt *fp;
222 
223 	if (DEV_FWMEM(dev))
224 		return fwmem_read(dev, uio, ioflag);
225 
226 	sc = devclass_get_softc(firewire_devclass, unit);
227 
228 	ir = sc->fc->ir[sub];
229 
230 	if (ir->flag & FWXFERQ_PACKET) {
231 		ir->stproc = NULL;
232 	}
233 readloop:
234 	xfer = STAILQ_FIRST(&ir->q);
235 	if ((ir->flag & FWXFERQ_PACKET) == 0 && ir->stproc == NULL) {
236 		/* iso bulkxfer */
237 		ir->stproc = STAILQ_FIRST(&ir->stvalid);
238 		if (ir->stproc != NULL) {
239 			s = splfw();
240 			STAILQ_REMOVE_HEAD(&ir->stvalid, link);
241 			splx(s);
242 			ir->queued = 0;
243 		}
244 	}
245 	if (xfer == NULL && ir->stproc == NULL) {
246 		/* no data avaliable */
247 		if (slept == 0) {
248 			slept = 1;
249 			if ((ir->flag & FWXFERQ_RUNNING) == 0
250 					&& (ir->flag & FWXFERQ_PACKET)) {
251 				err = sc->fc->irx_enable(sc->fc, sub);
252 				if (err)
253 					return err;
254 			}
255 			ir->flag |= FWXFERQ_WAKEUP;
256 			err = tsleep((caddr_t)ir, FWPRI, "fw_read", hz);
257 			ir->flag &= ~FWXFERQ_WAKEUP;
258 			if (err == 0)
259 				goto readloop;
260 		} else if (slept == 1)
261 			err = EIO;
262 		return err;
263 	} else if(xfer != NULL) {
264 		/* per packet mode */
265 		s = splfw();
266 		ir->queued --;
267 		STAILQ_REMOVE_HEAD(&ir->q, link);
268 		splx(s);
269 		fp = (struct fw_pkt *)(xfer->recv.buf + xfer->recv.off);
270 		if(sc->fc->irx_post != NULL)
271 			sc->fc->irx_post(sc->fc, fp->mode.ld);
272 		err = uiomove(xfer->recv.buf + xfer->recv.off, xfer->recv.len, uio);
273 		fw_xfer_free( xfer);
274 	} else if(ir->stproc != NULL) {
275 		/* iso bulkxfer */
276 		fp = (struct fw_pkt *)(ir->stproc->buf + ir->queued * ir->psize);
277 		if(sc->fc->irx_post != NULL)
278 			sc->fc->irx_post(sc->fc, fp->mode.ld);
279 		if(ntohs(fp->mode.stream.len) == 0){
280 			err = EIO;
281 			return err;
282 		}
283 		err = uiomove((caddr_t)fp,
284 			ntohs(fp->mode.stream.len) + sizeof(u_int32_t), uio);
285 #if 0
286 		fp->mode.stream.len = 0;
287 #endif
288 		ir->queued ++;
289 		if(ir->queued >= ir->bnpacket){
290 			s = splfw();
291 			STAILQ_INSERT_TAIL(&ir->stfree, ir->stproc, link);
292 			splx(s);
293 			sc->fc->irx_enable(sc->fc, sub);
294 			ir->stproc = NULL;
295 		}
296 		if (uio->uio_resid >= ir->psize) {
297 			slept = -1;
298 			goto readloop;
299 		}
300 	}
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 		STAILQ_INSERT_TAIL(&it->stfree, it->stproc, link);
357 		splx(s);
358 		it->stproc = NULL;
359 	}
360 #ifdef FWXFERQ_DV
361 	if(xferq == NULL && !(it->flag & FWXFERQ_DV)){
362 #else
363 	if (xferq == NULL) {
364 #endif
365 isoloop:
366 		if (it->stproc == NULL) {
367 			it->stproc = STAILQ_FIRST(&it->stfree);
368 			if (it->stproc != NULL) {
369 				s = splfw();
370 				STAILQ_REMOVE_HEAD(&it->stfree, link);
371 				splx(s);
372 				it->queued = 0;
373 			} else if (slept == 0) {
374 				slept = 1;
375 				err = sc->fc->itx_enable(sc->fc, sub);
376 				if (err)
377 					return err;
378 				err = tsleep((caddr_t)it, FWPRI,
379 							"fw_write", hz);
380 				if (err)
381 					return err;
382 				goto isoloop;
383 			} else {
384 				err = EIO;
385 				return err;
386 			}
387 		}
388 		fp = (struct fw_pkt *)
389 			(it->stproc->buf + it->queued * it->psize);
390 		err = uiomove((caddr_t)fp, sizeof(struct fw_isohdr), uio);
391 		err = uiomove((caddr_t)fp->mode.stream.payload,
392 					ntohs(fp->mode.stream.len), uio);
393 		it->queued ++;
394 		if (it->queued >= it->bnpacket) {
395 			s = splfw();
396 			STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link);
397 			splx(s);
398 			it->stproc = NULL;
399 			err = sc->fc->itx_enable(sc->fc, sub);
400 		}
401 		if (uio->uio_resid >= sizeof(struct fw_isohdr)) {
402 			slept = 0;
403 			goto isoloop;
404 		}
405 		return err;
406 	}
407 #ifdef FWXFERQ_DV
408 	if(xferq == NULL && it->flag & FWXFERQ_DV){
409 dvloop:
410 		if(it->dvproc == NULL){
411 			it->dvproc = STAILQ_FIRST(&it->dvfree);
412 			if(it->dvproc != NULL){
413 				s = splfw();
414 				STAILQ_REMOVE_HEAD(&it->dvfree, link);
415 				splx(s);
416 				it->dvptr = 0;
417 			}else if(slept == 0){
418 				slept = 1;
419 				err = sc->fc->itx_enable(sc->fc, sub);
420 				if(err){
421 					return err;
422 				}
423 				err = tsleep((caddr_t)it, FWPRI, "fw_write", hz);
424 				if(err){
425 					return err;
426 				}
427 				goto dvloop;
428 			}else{
429 				err = EIO;
430 				return err;
431 			}
432 		}
433 #if 0 /* What's this for? (it->dvptr? overwritten by the following uiomove)*/
434 		fp = (struct fw_pkt *)(it->dvproc->buf + it->queued * it->psize);
435 		fp->mode.stream.len = htons(uio->uio_resid - sizeof(u_int32_t));
436 #endif
437 		err = uiomove(it->dvproc->buf + it->dvptr,
438 							uio->uio_resid, uio);
439 		it->dvptr += it->psize;
440 		if(err){
441 			return err;
442 		}
443 		if(it->dvptr >= it->psize * it->dvpacket){
444 			s = splfw();
445 			STAILQ_INSERT_TAIL(&it->dvvalid, it->dvproc, link);
446 			splx(s);
447 			it->dvproc = NULL;
448 			err = fw_tbuf_update(sc->fc, sub, 0);
449 			if(err){
450 				return err;
451 			}
452 			err = sc->fc->itx_enable(sc->fc, sub);
453 		}
454 		return err;
455 	}
456 #endif
457 	if(xferq != NULL){
458 		xfer = fw_xfer_alloc(M_FWXFER);
459 		if(xfer == NULL){
460 			err = ENOMEM;
461 			return err;
462 		}
463 		xfer->send.buf = malloc(uio->uio_resid, M_FW, M_NOWAIT);
464 		if(xfer->send.buf == NULL){
465 			fw_xfer_free( xfer);
466 			err = ENOBUFS;
467 			return err;
468 		}
469 		xfer->dst = ntohs(fp->mode.hdr.dst);
470 #if 0
471 		switch(fp->mode.common.tcode){
472 		case FWTCODE_WREQQ:
473 		case FWTCODE_WREQB:
474 			if((tl = fw_get_tlabel(fc, xfer)) == -1 ){
475 				fw_xfer_free( xfer);
476 				err = EAGAIN;
477 				return err;
478 			}
479 			fp->mode.hdr.tlrt = tl << 2;
480 		default:
481 			break;
482 		}
483 
484 		xfer->tl = fp->mode.hdr.tlrt >> 2;
485 		xfer->tcode = fp->mode.common.tcode;
486 		xfer->fc = fc;
487 		xfer->q = xferq;
488 		xfer->act_type = FWACT_XFER;
489 		xfer->retry_req = fw_asybusy;
490 #endif
491 		xfer->send.len = uio->uio_resid;
492 		xfer->send.off = 0;
493 		xfer->spd = 0;/* XXX: how to setup it */
494 		xfer->act.hand = fw_asy_callback;
495 
496 		err = uiomove(xfer->send.buf, uio->uio_resid, uio);
497 		if(err){
498 			fw_xfer_free( xfer);
499 			return err;
500 		}
501 #if 0
502 		fw_asystart(xfer);
503 #else
504 		fw_asyreq(fc, -1, xfer);
505 #endif
506 		err = tsleep((caddr_t)xfer, FWPRI, "fw_write", hz);
507 		if(xfer->resp == EBUSY)
508 			return EBUSY;
509 		fw_xfer_free( xfer);
510 		return err;
511 	}
512 	return EINVAL;
513 }
514 
515 /*
516  * ioctl support.
517  */
518 int
519 fw_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td)
520 {
521 	struct firewire_softc *sc;
522 	int unit = DEV2UNIT(dev);
523 	int sub = DEV2DMACH(dev);
524 	int i, len, err = 0;
525 	struct fw_device *fwdev;
526 	struct fw_bind *fwb;
527 	struct fw_xferq *ir, *it;
528 	struct fw_xfer *xfer;
529 	struct fw_pkt *fp;
530 	struct fw_devinfo *devinfo;
531 
532 	struct fw_devlstreq *fwdevlst = (struct fw_devlstreq *)data;
533 	struct fw_asyreq *asyreq = (struct fw_asyreq *)data;
534 	struct fw_isochreq *ichreq = (struct fw_isochreq *)data;
535 	struct fw_isobufreq *ibufreq = (struct fw_isobufreq *)data;
536 	struct fw_asybindreq *bindreq = (struct fw_asybindreq *)data;
537 	struct fw_crom_buf *crom_buf = (struct fw_crom_buf *)data;
538 
539 	if (DEV_FWMEM(dev))
540 		return fwmem_ioctl(dev, cmd, data, flag, td);
541 
542 	sc = devclass_get_softc(firewire_devclass, unit);
543 	if (!data)
544 		return(EINVAL);
545 
546 	switch (cmd) {
547 	case FW_STSTREAM:
548 		sc->fc->it[sub]->flag &= ~0xff;
549 		sc->fc->it[sub]->flag |= (0x3f & ichreq->ch);
550 		sc->fc->it[sub]->flag |= ((0x3 & ichreq->tag) << 6);
551 		err = 0;
552 		break;
553 	case FW_GTSTREAM:
554 		ichreq->ch = sc->fc->it[sub]->flag & 0x3f;
555 		ichreq->tag =(sc->fc->it[sub]->flag) >> 2 & 0x3;
556 		err = 0;
557 		break;
558 	case FW_SRSTREAM:
559 		sc->fc->ir[sub]->flag &= ~0xff;
560 		sc->fc->ir[sub]->flag |= (0x3f & ichreq->ch);
561 		sc->fc->ir[sub]->flag |= ((0x3 & ichreq->tag) << 6);
562 		err = sc->fc->irx_enable(sc->fc, sub);
563 		break;
564 	case FW_GRSTREAM:
565 		ichreq->ch = sc->fc->ir[sub]->flag & 0x3f;
566 		ichreq->tag =(sc->fc->ir[sub]->flag) >> 2 & 0x3;
567 		err = 0;
568 		break;
569 #ifdef FWXFERQ_DV
570 	case FW_SSTDV:
571 		ibufreq = (struct fw_isobufreq *)
572 			malloc(sizeof(struct fw_isobufreq), M_FW, M_NOWAIT);
573 		if(ibufreq == NULL){
574 			err = ENOMEM;
575 			break;
576 		}
577 #if DV_PAL
578 #define FWDVPACKET 300
579 #else
580 #define FWDVPACKET 250
581 #endif
582 #define FWDVPMAX 512
583 		ibufreq->rx.nchunk = 8;
584 		ibufreq->rx.npacket = 50;
585 		ibufreq->rx.psize = FWDVPMAX;
586 
587 		ibufreq->tx.nchunk = 5;
588 		ibufreq->tx.npacket = FWDVPACKET + 30;	/* > 320 or 267 */
589 		ibufreq->tx.psize = FWDVPMAX;
590 
591 		err = fw_ioctl(dev, FW_SSTBUF, (caddr_t)ibufreq, flag, td);
592 		sc->fc->it[sub]->dvpacket = FWDVPACKET;
593 		free(ibufreq, M_FW);
594 /* reserve a buffer space */
595 #define NDVCHUNK 8
596 		sc->fc->it[sub]->dvproc = NULL;
597 		sc->fc->it[sub]->dvdma = NULL;
598 		sc->fc->it[sub]->flag |= FWXFERQ_DV;
599 		/* XXX check malloc failure */
600 		sc->fc->it[sub]->dvbuf
601 			= (struct fw_dvbuf *)malloc(sizeof(struct fw_dvbuf) * NDVCHUNK, M_FW, M_NOWAIT);
602 		STAILQ_INIT(&sc->fc->it[sub]->dvvalid);
603 		STAILQ_INIT(&sc->fc->it[sub]->dvfree);
604 		for( i = 0 ; i < NDVCHUNK ; i++){
605 			/* XXX check malloc failure */
606 			sc->fc->it[sub]->dvbuf[i].buf
607 				= malloc(FWDVPMAX * sc->fc->it[sub]->dvpacket, M_FW, M_NOWAIT);
608 			STAILQ_INSERT_TAIL(&sc->fc->it[sub]->dvfree,
609 					&sc->fc->it[sub]->dvbuf[i], link);
610 		}
611 		break;
612 #endif
613 	case FW_SSTBUF:
614 		ir = sc->fc->ir[sub];
615 		it = sc->fc->it[sub];
616 
617 		if(ir->flag & FWXFERQ_RUNNING || it->flag & FWXFERQ_RUNNING){
618 			return(EBUSY);
619 		}
620 		if((ir->flag & FWXFERQ_EXTBUF) || (it->flag & FWXFERQ_EXTBUF)){
621 			return(EBUSY);
622 		}
623 		if((ibufreq->rx.nchunk *
624 			ibufreq->rx.psize * ibufreq->rx.npacket) +
625 		   (ibufreq->tx.nchunk *
626 			ibufreq->tx.psize * ibufreq->tx.npacket) <= 0){
627 				return(EINVAL);
628 		}
629 		if(ibufreq->rx.nchunk > FWSTMAXCHUNK ||
630 				ibufreq->tx.nchunk > FWSTMAXCHUNK){
631 			return(EINVAL);
632 		}
633 		ir->bulkxfer
634 			= (struct fw_bulkxfer *)malloc(sizeof(struct fw_bulkxfer) * ibufreq->rx.nchunk, M_FW, 0);
635 		if(ir->bulkxfer == NULL){
636 			return(ENOMEM);
637 		}
638 		it->bulkxfer
639 			= (struct fw_bulkxfer *)malloc(sizeof(struct fw_bulkxfer) * ibufreq->tx.nchunk, M_FW, 0);
640 		if(it->bulkxfer == NULL){
641 			return(ENOMEM);
642 		}
643 		ir->buf = malloc(
644 			ibufreq->rx.nchunk * ibufreq->rx.npacket
645 			/* XXX psize must be 2^n and less or
646 						equal to PAGE_SIZE */
647 			* ((ibufreq->rx.psize + 3) &~3),
648 			M_FW, 0);
649 		if(ir->buf == NULL){
650 			free(ir->bulkxfer, M_FW);
651 			free(it->bulkxfer, M_FW);
652 			ir->bulkxfer = NULL;
653 			it->bulkxfer = NULL;
654 			it->buf = NULL;
655 			return(ENOMEM);
656 		}
657 		it->buf = malloc(
658 			ibufreq->tx.nchunk * ibufreq->tx.npacket
659 			/* XXX psize must be 2^n and less or
660 						equal to PAGE_SIZE */
661 			* ((ibufreq->tx.psize + 3) &~3),
662 			M_FW, 0);
663 		if(it->buf == NULL){
664 			free(ir->bulkxfer, M_FW);
665 			free(it->bulkxfer, M_FW);
666 			free(ir->buf, M_FW);
667 			ir->bulkxfer = NULL;
668 			it->bulkxfer = NULL;
669 			it->buf = NULL;
670 			return(ENOMEM);
671 		}
672 
673 		ir->bnchunk = ibufreq->rx.nchunk;
674 		ir->bnpacket = ibufreq->rx.npacket;
675 		ir->psize = (ibufreq->rx.psize + 3) & ~3;
676 		ir->queued = 0;
677 
678 		it->bnchunk = ibufreq->tx.nchunk;
679 		it->bnpacket = ibufreq->tx.npacket;
680 		it->psize = (ibufreq->tx.psize + 3) & ~3;
681 		it->queued = 0;
682 
683 #ifdef FWXFERQ_DV
684 		it->dvdbc = 0;
685 		it->dvdiff = 0;
686 		it->dvsync = 0;
687 		it->dvoffset = 0;
688 #endif
689 
690 		STAILQ_INIT(&ir->stvalid);
691 		STAILQ_INIT(&ir->stfree);
692 		STAILQ_INIT(&ir->stdma);
693 		ir->stproc = NULL;
694 
695 		STAILQ_INIT(&it->stvalid);
696 		STAILQ_INIT(&it->stfree);
697 		STAILQ_INIT(&it->stdma);
698 		it->stproc = NULL;
699 
700 		for(i = 0 ; i < sc->fc->ir[sub]->bnchunk; i++){
701 			ir->bulkxfer[i].buf =
702 				ir->buf +
703 				i * sc->fc->ir[sub]->bnpacket *
704 			  	sc->fc->ir[sub]->psize;
705 			STAILQ_INSERT_TAIL(&ir->stfree,
706 					&ir->bulkxfer[i], link);
707 			ir->bulkxfer[i].npacket = ir->bnpacket;
708 		}
709 		for(i = 0 ; i < sc->fc->it[sub]->bnchunk; i++){
710 			it->bulkxfer[i].buf =
711 				it->buf +
712 				i * sc->fc->it[sub]->bnpacket *
713 			  	sc->fc->it[sub]->psize;
714 			STAILQ_INSERT_TAIL(&it->stfree,
715 					&it->bulkxfer[i], link);
716 			it->bulkxfer[i].npacket = it->bnpacket;
717 		}
718 		ir->flag &= ~FWXFERQ_MODEMASK;
719 		ir->flag |= FWXFERQ_STREAM;
720 		ir->flag |= FWXFERQ_EXTBUF;
721 
722 		it->flag &= ~FWXFERQ_MODEMASK;
723 		it->flag |= FWXFERQ_STREAM;
724 		it->flag |= FWXFERQ_EXTBUF;
725 		err = 0;
726 		break;
727 	case FW_GSTBUF:
728 		ibufreq->rx.nchunk = sc->fc->ir[sub]->bnchunk;
729 		ibufreq->rx.npacket = sc->fc->ir[sub]->bnpacket;
730 		ibufreq->rx.psize = sc->fc->ir[sub]->psize;
731 
732 		ibufreq->tx.nchunk = sc->fc->it[sub]->bnchunk;
733 		ibufreq->tx.npacket = sc->fc->it[sub]->bnpacket;
734 		ibufreq->tx.psize = sc->fc->it[sub]->psize;
735 		break;
736 	case FW_ASYREQ:
737 		xfer = fw_xfer_alloc(M_FWXFER);
738 		if(xfer == NULL){
739 			err = ENOMEM;
740 			return err;
741 		}
742 		fp = &asyreq->pkt;
743 		switch (asyreq->req.type) {
744 		case FWASREQNODE:
745 			xfer->dst = ntohs(fp->mode.hdr.dst);
746 			break;
747 		case FWASREQEUI:
748 			fwdev = fw_noderesolve_eui64(sc->fc,
749 						asyreq->req.dst.eui);
750 			if (fwdev == NULL) {
751 				device_printf(sc->fc->bdev,
752 					"cannot find node\n");
753 				err = EINVAL;
754 				goto error;
755 			}
756 			xfer->dst = fwdev->dst;
757 			fp->mode.hdr.dst = htons(FWLOCALBUS | xfer->dst);
758 			break;
759 		case FWASRESTL:
760 			/* XXX what's this? */
761 			break;
762 		case FWASREQSTREAM:
763 			/* nothing to do */
764 			break;
765 		}
766 		xfer->spd = asyreq->req.sped;
767 		xfer->send.len = asyreq->req.len;
768 		xfer->send.buf = malloc(xfer->send.len, M_FW, M_NOWAIT);
769 		if(xfer->send.buf == NULL){
770 			return ENOMEM;
771 		}
772 		xfer->send.off = 0;
773 		bcopy(fp, xfer->send.buf, xfer->send.len);
774 		xfer->act.hand = fw_asy_callback;
775 		err = fw_asyreq(sc->fc, sub, xfer);
776 		if(err){
777 			fw_xfer_free( xfer);
778 			return err;
779 		}
780 		err = tsleep((caddr_t)xfer, FWPRI, "asyreq", hz);
781 		if(err == 0){
782 			if(asyreq->req.len >= xfer->recv.len){
783 				asyreq->req.len = xfer->recv.len;
784 			}else{
785 				err = EINVAL;
786 			}
787 			bcopy(xfer->recv.buf + xfer->recv.off, fp, asyreq->req.len);
788 		}
789 error:
790 		fw_xfer_free( xfer);
791 		break;
792 	case FW_IBUSRST:
793 		sc->fc->ibr(sc->fc);
794 		break;
795 	case FW_CBINDADDR:
796 		fwb = fw_bindlookup(sc->fc,
797 				bindreq->start.hi, bindreq->start.lo);
798 		if(fwb == NULL){
799 			err = EINVAL;
800 			break;
801 		}
802 		STAILQ_REMOVE(&sc->fc->binds, fwb, fw_bind, fclist);
803 		STAILQ_REMOVE(&sc->fc->ir[sub]->binds, fwb, fw_bind, chlist);
804 		free(fwb, M_FW);
805 		break;
806 	case FW_SBINDADDR:
807 		if(bindreq->len <= 0 ){
808 			err = EINVAL;
809 			break;
810 		}
811 		if(bindreq->start.hi > 0xffff ){
812 			err = EINVAL;
813 			break;
814 		}
815 		fwb = (struct fw_bind *)malloc(sizeof (struct fw_bind), M_FW, M_NOWAIT);
816 		if(fwb == NULL){
817 			err = ENOMEM;
818 			break;
819 		}
820 		fwb->start_hi = bindreq->start.hi;
821 		fwb->start_lo = bindreq->start.lo;
822 		fwb->addrlen = bindreq->len;
823 
824 		xfer = fw_xfer_alloc(M_FWXFER);
825 		if(xfer == NULL){
826 			err = ENOMEM;
827 			return err;
828 		}
829 		xfer->act_type = FWACT_CH;
830 		xfer->sub = sub;
831 		xfer->fc = sc->fc;
832 
833 		fwb->xfer = xfer;
834 		err = fw_bindadd(sc->fc, fwb);
835 		break;
836 	case FW_GDEVLST:
837 		i = len = 1;
838 		/* myself */
839 		devinfo = &fwdevlst->dev[0];
840 		devinfo->dst = sc->fc->nodeid;
841 		devinfo->status = 0;	/* XXX */
842 		devinfo->eui.hi = sc->fc->eui.hi;
843 		devinfo->eui.lo = sc->fc->eui.lo;
844 		STAILQ_FOREACH(fwdev, &sc->fc->devices, link) {
845 			if(len < FW_MAX_DEVLST){
846 				devinfo = &fwdevlst->dev[len++];
847 				devinfo->dst = fwdev->dst;
848 				devinfo->status =
849 					(fwdev->status == FWDEVINVAL)?0:1;
850 				devinfo->eui.hi = fwdev->eui.hi;
851 				devinfo->eui.lo = fwdev->eui.lo;
852 			}
853 			i++;
854 		}
855 		fwdevlst->n = i;
856 		fwdevlst->info_len = len;
857 		break;
858 	case FW_GTPMAP:
859 		bcopy(sc->fc->topology_map, data,
860 				(sc->fc->topology_map->crc_len + 1) * 4);
861 		break;
862 	case FW_GCROM:
863 		STAILQ_FOREACH(fwdev, &sc->fc->devices, link)
864 			if (FW_EUI64_EQUAL(fwdev->eui, crom_buf->eui))
865 				break;
866 		if (fwdev == NULL) {
867 			err = FWNODE_INVAL;
868 			break;
869 		}
870 #if 0
871 		if (fwdev->csrrom[0] >> 24 == 1)
872 			len = 4;
873 		else
874 			len = (1 + ((fwdev->csrrom[0] >> 16) & 0xff)) * 4;
875 #else
876 		if (fwdev->rommax < CSRROMOFF)
877 			len = 0;
878 		else
879 			len = fwdev->rommax - CSRROMOFF + 4;
880 #endif
881 		if (crom_buf->len < len)
882 			len = crom_buf->len;
883 		else
884 			crom_buf->len = len;
885 		err = copyout(&fwdev->csrrom[0], crom_buf->ptr, len);
886 		break;
887 	default:
888 		sc->fc->ioctl (dev, cmd, data, flag, td);
889 		break;
890 	}
891 	return err;
892 }
893 int
894 fw_poll(dev_t dev, int events, fw_proc *td)
895 {
896 	int revents;
897 	int tmp;
898 	int unit = DEV2UNIT(dev);
899 	int sub = DEV2DMACH(dev);
900 	struct firewire_softc *sc;
901 
902 	if (DEV_FWMEM(dev))
903 		return fwmem_poll(dev, events, td);
904 
905 	sc = devclass_get_softc(firewire_devclass, unit);
906 	revents = 0;
907 	tmp = POLLIN | POLLRDNORM;
908 	if (events & tmp) {
909 		if (STAILQ_FIRST(&sc->fc->ir[sub]->q) != NULL)
910 			revents |= tmp;
911 		else
912 			selrecord(td, &sc->fc->ir[sub]->rsel);
913 	}
914 	tmp = POLLOUT | POLLWRNORM;
915 	if (events & tmp) {
916 		/* XXX should be fixed */
917 		revents |= tmp;
918 	}
919 
920 	return revents;
921 }
922 
923 static int
924 fw_mmap (dev_t dev, vm_offset_t offset, int nproto)
925 {
926 	struct firewire_softc *fc;
927 	int unit = DEV2UNIT(dev);
928 
929 	if (DEV_FWMEM(dev))
930 		return fwmem_mmap(dev, offset, nproto);
931 
932 	fc = devclass_get_softc(firewire_devclass, unit);
933 
934 	return EINVAL;
935 }
936