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