xref: /dragonfly/sys/bus/firewire/fwdev.c (revision 4e7eb5cc)
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: src/sys/dev/firewire/fwdev.c,v 1.2.4.11 2003/04/28 03:29:18 simokawa Exp $
35  * $DragonFly: src/sys/bus/firewire/fwdev.c,v 1.5 2003/08/07 21:16:45 dillon Exp $
36  *
37  */
38 
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/types.h>
42 #include <sys/mbuf.h>
43 
44 #include <sys/kernel.h>
45 #include <sys/malloc.h>
46 #include <sys/conf.h>
47 #include <sys/proc.h>
48 #include <sys/poll.h>
49 
50 #include <sys/bus.h>
51 #include <machine/bus.h>
52 
53 #include <sys/ioccom.h>
54 
55 #include "firewire.h"
56 #include "firewirereg.h"
57 #include "fwdma.h"
58 #include "fwmem.h"
59 #include "iec68113.h"
60 
61 #define CDEV_MAJOR 127
62 #define	FWNODE_INVAL 0xffff
63 
64 static	d_open_t	fw_open;
65 static	d_close_t	fw_close;
66 static	d_ioctl_t	fw_ioctl;
67 static	d_poll_t	fw_poll;
68 static	d_read_t	fw_read;	/* for Isochronous packet */
69 static	d_write_t	fw_write;
70 static	d_mmap_t	fw_mmap;
71 
72 struct cdevsw firewire_cdevsw =
73 {
74 #if __FreeBSD_version >= 500104
75 	.d_open =	fw_open,
76 	.d_close =	fw_close,
77 	.d_read =	fw_read,
78 	.d_write =	fw_write,
79 	.d_ioctl =	fw_ioctl,
80 	.d_poll =	fw_poll,
81 	.d_mmap =	fw_mmap,
82 	.d_name =	"fw",
83 	.d_maj =	CDEV_MAJOR,
84 	.d_flags =	D_MEM
85 #else
86 	"fw", CDEV_MAJOR, D_MEM, NULL, 0,
87 	fw_open, fw_close, fw_read, fw_write, fw_ioctl,
88 	fw_poll, fw_mmap, nostrategy, nodump, nopsize,
89 #endif
90 };
91 
92 static int
93 fw_open (dev_t dev, int flags, int fmt, fw_proc *td)
94 {
95 	struct firewire_softc *sc;
96 	int unit = DEV2UNIT(dev);
97 	int sub = DEV2DMACH(dev);
98 
99 	int err = 0;
100 
101 	if (DEV_FWMEM(dev))
102 		return fwmem_open(dev, flags, fmt, td);
103 
104 	sc = devclass_get_softc(firewire_devclass, unit);
105 	if(sc->fc->ir[sub]->flag & FWXFERQ_OPEN){
106 		err = EBUSY;
107 		return err;
108 	}
109 	if(sc->fc->it[sub]->flag & FWXFERQ_OPEN){
110 		err = EBUSY;
111 		return err;
112 	}
113 	if(sc->fc->ir[sub]->flag & FWXFERQ_MODEMASK){
114 		err = EBUSY;
115 		return err;
116 	}
117 /* Default is per packet mode */
118 	sc->fc->ir[sub]->flag |= FWXFERQ_OPEN;
119 	sc->fc->it[sub]->flag |= FWXFERQ_OPEN;
120 	return err;
121 }
122 
123 static int
124 fw_close (dev_t dev, int flags, int fmt, fw_proc *td)
125 {
126 	struct firewire_softc *sc;
127 	int unit = DEV2UNIT(dev);
128 	int sub = DEV2DMACH(dev);
129 	struct fw_xfer *xfer;
130 	struct fw_bind *fwb;
131 	int err = 0;
132 
133 	if (DEV_FWMEM(dev))
134 		return fwmem_close(dev, flags, fmt, td);
135 
136 	sc = devclass_get_softc(firewire_devclass, unit);
137 	if(!(sc->fc->ir[sub]->flag & FWXFERQ_OPEN)){
138 		err = EINVAL;
139 		return err;
140 	}
141 	sc->fc->ir[sub]->flag &= ~FWXFERQ_OPEN;
142 	if(!(sc->fc->it[sub]->flag & FWXFERQ_OPEN)){
143 		err = EINVAL;
144 		return err;
145 	}
146 	sc->fc->it[sub]->flag &= ~FWXFERQ_OPEN;
147 
148 	if(sc->fc->ir[sub]->flag & FWXFERQ_RUNNING){
149 		sc->fc->irx_disable(sc->fc, sub);
150 	}
151 	if(sc->fc->it[sub]->flag & FWXFERQ_RUNNING){
152 		sc->fc->it[sub]->flag &= ~FWXFERQ_RUNNING;
153 		sc->fc->itx_disable(sc->fc, sub);
154 	}
155 	if(sc->fc->ir[sub]->flag & FWXFERQ_EXTBUF){
156 		if (sc->fc->ir[sub]->buf != NULL)
157 			fwdma_free_multiseg(sc->fc->ir[sub]->buf);
158 		sc->fc->ir[sub]->buf = NULL;
159 		free(sc->fc->ir[sub]->bulkxfer, M_FW);
160 		sc->fc->ir[sub]->bulkxfer = NULL;
161 		sc->fc->ir[sub]->flag &= ~FWXFERQ_EXTBUF;
162 		sc->fc->ir[sub]->psize = PAGE_SIZE;
163 		sc->fc->ir[sub]->maxq = FWMAXQUEUE;
164 	}
165 	if(sc->fc->it[sub]->flag & FWXFERQ_EXTBUF){
166 		if (sc->fc->it[sub]->buf != NULL)
167 			fwdma_free_multiseg(sc->fc->it[sub]->buf);
168 		sc->fc->it[sub]->buf = NULL;
169 		free(sc->fc->it[sub]->bulkxfer, M_FW);
170 		sc->fc->it[sub]->bulkxfer = NULL;
171 		sc->fc->it[sub]->flag &= ~FWXFERQ_EXTBUF;
172 		sc->fc->it[sub]->psize = 0;
173 		sc->fc->it[sub]->maxq = FWMAXQUEUE;
174 	}
175 	for(xfer = STAILQ_FIRST(&sc->fc->ir[sub]->q);
176 		xfer != NULL; xfer = STAILQ_FIRST(&sc->fc->ir[sub]->q)){
177 		sc->fc->ir[sub]->queued--;
178 		STAILQ_REMOVE_HEAD(&sc->fc->ir[sub]->q, link);
179 
180 		xfer->resp = 0;
181 		fw_xfer_done(xfer);
182 	}
183 	for(fwb = STAILQ_FIRST(&sc->fc->ir[sub]->binds); fwb != NULL;
184 		fwb = STAILQ_FIRST(&sc->fc->ir[sub]->binds)){
185 		STAILQ_REMOVE(&sc->fc->binds, fwb, fw_bind, fclist);
186 		STAILQ_REMOVE_HEAD(&sc->fc->ir[sub]->binds, chlist);
187 		free(fwb, M_FW);
188 	}
189 	sc->fc->ir[sub]->flag &= ~(FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK);
190 	sc->fc->it[sub]->flag &= ~(FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK);
191 	return err;
192 }
193 
194 /*
195  * read request.
196  */
197 static int
198 fw_read (dev_t dev, struct uio *uio, int ioflag)
199 {
200 	struct firewire_softc *sc;
201 	struct fw_xferq *ir;
202 	struct fw_xfer *xfer;
203 	int err = 0, s, slept = 0;
204 	int unit = DEV2UNIT(dev);
205 	int sub = DEV2DMACH(dev);
206 	struct fw_pkt *fp;
207 
208 	if (DEV_FWMEM(dev))
209 		return fwmem_read(dev, uio, ioflag);
210 
211 	sc = devclass_get_softc(firewire_devclass, unit);
212 
213 	ir = sc->fc->ir[sub];
214 
215 readloop:
216 	xfer = STAILQ_FIRST(&ir->q);
217 	if (ir->stproc == NULL) {
218 		/* iso bulkxfer */
219 		ir->stproc = STAILQ_FIRST(&ir->stvalid);
220 		if (ir->stproc != NULL) {
221 			s = splfw();
222 			STAILQ_REMOVE_HEAD(&ir->stvalid, link);
223 			splx(s);
224 			ir->queued = 0;
225 		}
226 	}
227 	if (xfer == NULL && ir->stproc == NULL) {
228 		/* no data avaliable */
229 		if (slept == 0) {
230 			slept = 1;
231 			ir->flag |= FWXFERQ_WAKEUP;
232 			err = tsleep(ir, FWPRI, "fw_read", hz);
233 			ir->flag &= ~FWXFERQ_WAKEUP;
234 			if (err == 0)
235 				goto readloop;
236 		} else if (slept == 1)
237 			err = EIO;
238 		return err;
239 	} else if(xfer != NULL) {
240 		/* per packet mode or FWACT_CH bind?*/
241 		s = splfw();
242 		ir->queued --;
243 		STAILQ_REMOVE_HEAD(&ir->q, link);
244 		splx(s);
245 		fp = (struct fw_pkt *)xfer->recv.buf;
246 		if(sc->fc->irx_post != NULL)
247 			sc->fc->irx_post(sc->fc, fp->mode.ld);
248 		err = uiomove(xfer->recv.buf, xfer->recv.len, uio);
249 		/* XXX we should recycle this xfer */
250 		fw_xfer_free( xfer);
251 	} else if(ir->stproc != NULL) {
252 		/* iso bulkxfer */
253 		fp = (struct fw_pkt *)fwdma_v_addr(ir->buf,
254 				ir->stproc->poffset + ir->queued);
255 		if(sc->fc->irx_post != NULL)
256 			sc->fc->irx_post(sc->fc, fp->mode.ld);
257 		if(fp->mode.stream.len == 0){
258 			err = EIO;
259 			return err;
260 		}
261 		err = uiomove((caddr_t)fp,
262 			fp->mode.stream.len + sizeof(u_int32_t), uio);
263 		ir->queued ++;
264 		if(ir->queued >= ir->bnpacket){
265 			s = splfw();
266 			STAILQ_INSERT_TAIL(&ir->stfree, ir->stproc, link);
267 			splx(s);
268 			sc->fc->irx_enable(sc->fc, sub);
269 			ir->stproc = NULL;
270 		}
271 		if (uio->uio_resid >= ir->psize) {
272 			slept = -1;
273 			goto readloop;
274 		}
275 	}
276 	return err;
277 }
278 
279 static int
280 fw_write (dev_t dev, struct uio *uio, int ioflag)
281 {
282 	int err = 0;
283 	struct firewire_softc *sc;
284 	int unit = DEV2UNIT(dev);
285 	int sub = DEV2DMACH(dev);
286 	int s, slept = 0;
287 	struct fw_pkt *fp;
288 	struct firewire_comm *fc;
289 	struct fw_xferq *it;
290 
291 	if (DEV_FWMEM(dev))
292 		return fwmem_write(dev, uio, ioflag);
293 
294 	sc = devclass_get_softc(firewire_devclass, unit);
295 	fc = sc->fc;
296 	it = sc->fc->it[sub];
297 isoloop:
298 	if (it->stproc == NULL) {
299 		it->stproc = STAILQ_FIRST(&it->stfree);
300 		if (it->stproc != NULL) {
301 			s = splfw();
302 			STAILQ_REMOVE_HEAD(&it->stfree, link);
303 			splx(s);
304 			it->queued = 0;
305 		} else if (slept == 0) {
306 			slept = 1;
307 			err = sc->fc->itx_enable(sc->fc, sub);
308 			if (err)
309 				return err;
310 			err = tsleep(it, FWPRI, "fw_write", hz);
311 			if (err)
312 				return err;
313 			goto isoloop;
314 		} else {
315 			err = EIO;
316 			return err;
317 		}
318 	}
319 	fp = (struct fw_pkt *)fwdma_v_addr(it->buf,
320 			it->stproc->poffset + it->queued);
321 	err = uiomove((caddr_t)fp, sizeof(struct fw_isohdr), uio);
322 	err = uiomove((caddr_t)fp->mode.stream.payload,
323 				fp->mode.stream.len, uio);
324 	it->queued ++;
325 	if (it->queued >= it->bnpacket) {
326 		s = splfw();
327 		STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link);
328 		splx(s);
329 		it->stproc = NULL;
330 		err = sc->fc->itx_enable(sc->fc, sub);
331 	}
332 	if (uio->uio_resid >= sizeof(struct fw_isohdr)) {
333 		slept = 0;
334 		goto isoloop;
335 	}
336 	return err;
337 }
338 
339 /*
340  * ioctl support.
341  */
342 int
343 fw_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td)
344 {
345 	struct firewire_softc *sc;
346 	int unit = DEV2UNIT(dev);
347 	int sub = DEV2DMACH(dev);
348 	int s, i, len, err = 0;
349 	struct fw_device *fwdev;
350 	struct fw_bind *fwb;
351 	struct fw_xferq *ir, *it;
352 	struct fw_xfer *xfer;
353 	struct fw_pkt *fp;
354 	struct fw_devinfo *devinfo;
355 
356 	struct fw_devlstreq *fwdevlst = (struct fw_devlstreq *)data;
357 	struct fw_asyreq *asyreq = (struct fw_asyreq *)data;
358 	struct fw_isochreq *ichreq = (struct fw_isochreq *)data;
359 	struct fw_isobufreq *ibufreq = (struct fw_isobufreq *)data;
360 	struct fw_asybindreq *bindreq = (struct fw_asybindreq *)data;
361 	struct fw_crom_buf *crom_buf = (struct fw_crom_buf *)data;
362 
363 	if (DEV_FWMEM(dev))
364 		return fwmem_ioctl(dev, cmd, data, flag, td);
365 
366 	sc = devclass_get_softc(firewire_devclass, unit);
367 	if (!data)
368 		return(EINVAL);
369 
370 	switch (cmd) {
371 	case FW_STSTREAM:
372 		sc->fc->it[sub]->flag &= ~0xff;
373 		sc->fc->it[sub]->flag |= (0x3f & ichreq->ch);
374 		sc->fc->it[sub]->flag |= ((0x3 & ichreq->tag) << 6);
375 		err = 0;
376 		break;
377 	case FW_GTSTREAM:
378 		ichreq->ch = sc->fc->it[sub]->flag & 0x3f;
379 		ichreq->tag =(sc->fc->it[sub]->flag) >> 2 & 0x3;
380 		err = 0;
381 		break;
382 	case FW_SRSTREAM:
383 		sc->fc->ir[sub]->flag &= ~0xff;
384 		sc->fc->ir[sub]->flag |= (0x3f & ichreq->ch);
385 		sc->fc->ir[sub]->flag |= ((0x3 & ichreq->tag) << 6);
386 		err = sc->fc->irx_enable(sc->fc, sub);
387 		break;
388 	case FW_GRSTREAM:
389 		ichreq->ch = sc->fc->ir[sub]->flag & 0x3f;
390 		ichreq->tag =(sc->fc->ir[sub]->flag) >> 2 & 0x3;
391 		err = 0;
392 		break;
393 	case FW_SSTBUF:
394 		ir = sc->fc->ir[sub];
395 		it = sc->fc->it[sub];
396 
397 		if(ir->flag & FWXFERQ_RUNNING || it->flag & FWXFERQ_RUNNING){
398 			return(EBUSY);
399 		}
400 		if((ir->flag & FWXFERQ_EXTBUF) || (it->flag & FWXFERQ_EXTBUF)){
401 			return(EBUSY);
402 		}
403 		if((ibufreq->rx.nchunk *
404 			ibufreq->rx.psize * ibufreq->rx.npacket) +
405 		   (ibufreq->tx.nchunk *
406 			ibufreq->tx.psize * ibufreq->tx.npacket) <= 0){
407 				return(EINVAL);
408 		}
409 		ir->bulkxfer
410 			= (struct fw_bulkxfer *)malloc(sizeof(struct fw_bulkxfer) * ibufreq->rx.nchunk, M_FW, M_WAITOK);
411 		if(ir->bulkxfer == NULL){
412 			return(ENOMEM);
413 		}
414 		it->bulkxfer
415 			= (struct fw_bulkxfer *)malloc(sizeof(struct fw_bulkxfer) * ibufreq->tx.nchunk, M_FW, M_WAITOK);
416 		if(it->bulkxfer == NULL){
417 			return(ENOMEM);
418 		}
419 		if (ibufreq->rx.psize > 0) {
420 			ibufreq->rx.psize = roundup2(ibufreq->rx.psize,
421 							sizeof(u_int32_t));
422 			ir->buf = fwdma_malloc_multiseg(
423 				sc->fc, sizeof(u_int32_t),
424 				ibufreq->rx.psize,
425 				ibufreq->rx.nchunk * ibufreq->rx.npacket,
426 				BUS_DMA_WAITOK);
427 
428 			if(ir->buf == NULL){
429 				free(ir->bulkxfer, M_FW);
430 				free(it->bulkxfer, M_FW);
431 				ir->bulkxfer = NULL;
432 				it->bulkxfer = NULL;
433 				it->buf = NULL;
434 				return(ENOMEM);
435 			}
436 		}
437 		if (ibufreq->tx.psize > 0) {
438 			ibufreq->tx.psize = roundup2(ibufreq->tx.psize,
439 							sizeof(u_int32_t));
440 			it->buf = fwdma_malloc_multiseg(
441 				sc->fc, sizeof(u_int32_t),
442 				ibufreq->tx.psize,
443 				ibufreq->tx.nchunk * ibufreq->tx.npacket,
444 				BUS_DMA_WAITOK);
445 
446 			if(it->buf == NULL){
447 				free(ir->bulkxfer, M_FW);
448 				free(it->bulkxfer, M_FW);
449 				fwdma_free_multiseg(ir->buf);
450 				ir->bulkxfer = NULL;
451 				it->bulkxfer = NULL;
452 				it->buf = NULL;
453 				return(ENOMEM);
454 			}
455 		}
456 
457 		ir->bnchunk = ibufreq->rx.nchunk;
458 		ir->bnpacket = ibufreq->rx.npacket;
459 		ir->psize = (ibufreq->rx.psize + 3) & ~3;
460 		ir->queued = 0;
461 
462 		it->bnchunk = ibufreq->tx.nchunk;
463 		it->bnpacket = ibufreq->tx.npacket;
464 		it->psize = (ibufreq->tx.psize + 3) & ~3;
465 		it->queued = 0;
466 
467 		STAILQ_INIT(&ir->stvalid);
468 		STAILQ_INIT(&ir->stfree);
469 		STAILQ_INIT(&ir->stdma);
470 		ir->stproc = NULL;
471 
472 		STAILQ_INIT(&it->stvalid);
473 		STAILQ_INIT(&it->stfree);
474 		STAILQ_INIT(&it->stdma);
475 		it->stproc = NULL;
476 
477 		for(i = 0 ; i < sc->fc->ir[sub]->bnchunk; i++){
478 			ir->bulkxfer[i].poffset = i * ir->bnpacket;
479 			ir->bulkxfer[i].mbuf = NULL;
480 			STAILQ_INSERT_TAIL(&ir->stfree,
481 					&ir->bulkxfer[i], link);
482 		}
483 		for(i = 0 ; i < sc->fc->it[sub]->bnchunk; i++){
484 			it->bulkxfer[i].poffset = i * it->bnpacket;
485 			it->bulkxfer[i].mbuf = NULL;
486 			STAILQ_INSERT_TAIL(&it->stfree,
487 					&it->bulkxfer[i], link);
488 		}
489 		ir->flag &= ~FWXFERQ_MODEMASK;
490 		ir->flag |= FWXFERQ_STREAM;
491 		ir->flag |= FWXFERQ_EXTBUF;
492 
493 		it->flag &= ~FWXFERQ_MODEMASK;
494 		it->flag |= FWXFERQ_STREAM;
495 		it->flag |= FWXFERQ_EXTBUF;
496 		err = 0;
497 		break;
498 	case FW_GSTBUF:
499 		ibufreq->rx.nchunk = sc->fc->ir[sub]->bnchunk;
500 		ibufreq->rx.npacket = sc->fc->ir[sub]->bnpacket;
501 		ibufreq->rx.psize = sc->fc->ir[sub]->psize;
502 
503 		ibufreq->tx.nchunk = sc->fc->it[sub]->bnchunk;
504 		ibufreq->tx.npacket = sc->fc->it[sub]->bnpacket;
505 		ibufreq->tx.psize = sc->fc->it[sub]->psize;
506 		break;
507 	case FW_ASYREQ:
508 		xfer = fw_xfer_alloc_buf(M_FWXFER, asyreq->req.len,
509 							PAGE_SIZE /* XXX */);
510 		if(xfer == NULL){
511 			err = ENOMEM;
512 			return err;
513 		}
514 		fp = &asyreq->pkt;
515 		switch (asyreq->req.type) {
516 		case FWASREQNODE:
517 			xfer->dst = fp->mode.hdr.dst;
518 			break;
519 		case FWASREQEUI:
520 			fwdev = fw_noderesolve_eui64(sc->fc,
521 						&asyreq->req.dst.eui);
522 			if (fwdev == NULL) {
523 				device_printf(sc->fc->bdev,
524 					"cannot find node\n");
525 				err = EINVAL;
526 				goto error;
527 			}
528 			xfer->dst = FWLOCALBUS | fwdev->dst;
529 			fp->mode.hdr.dst = xfer->dst;
530 			break;
531 		case FWASRESTL:
532 			/* XXX what's this? */
533 			break;
534 		case FWASREQSTREAM:
535 			/* nothing to do */
536 			break;
537 		}
538 		xfer->spd = asyreq->req.sped;
539 		bcopy(fp, xfer->send.buf, xfer->send.len);
540 		xfer->act.hand = fw_asy_callback;
541 		err = fw_asyreq(sc->fc, sub, xfer);
542 		if(err){
543 			fw_xfer_free( xfer);
544 			return err;
545 		}
546 		err = tsleep(xfer, FWPRI, "asyreq", hz);
547 		if(err == 0){
548 			if(asyreq->req.len >= xfer->recv.len){
549 				asyreq->req.len = xfer->recv.len;
550 			}else{
551 				err = EINVAL;
552 			}
553 			bcopy(xfer->recv.buf, fp, asyreq->req.len);
554 		}
555 error:
556 		fw_xfer_free( xfer);
557 		break;
558 	case FW_IBUSRST:
559 		sc->fc->ibr(sc->fc);
560 		break;
561 	case FW_CBINDADDR:
562 		fwb = fw_bindlookup(sc->fc,
563 				bindreq->start.hi, bindreq->start.lo);
564 		if(fwb == NULL){
565 			err = EINVAL;
566 			break;
567 		}
568 		STAILQ_REMOVE(&sc->fc->binds, fwb, fw_bind, fclist);
569 		STAILQ_REMOVE(&sc->fc->ir[sub]->binds, fwb, fw_bind, chlist);
570 		free(fwb, M_FW);
571 		break;
572 	case FW_SBINDADDR:
573 		if(bindreq->len <= 0 ){
574 			err = EINVAL;
575 			break;
576 		}
577 		if(bindreq->start.hi > 0xffff ){
578 			err = EINVAL;
579 			break;
580 		}
581 		fwb = (struct fw_bind *)malloc(sizeof (struct fw_bind), M_FW, M_NOWAIT);
582 		if(fwb == NULL){
583 			err = ENOMEM;
584 			break;
585 		}
586 		fwb->start_hi = bindreq->start.hi;
587 		fwb->start_lo = bindreq->start.lo;
588 		fwb->addrlen = bindreq->len;
589 		fwb->sub = sub;
590 		fwb->act_type = FWACT_CH;
591 
592 		xfer = fw_xfer_alloc(M_FWXFER);
593 		if(xfer == NULL){
594 			err = ENOMEM;
595 			return err;
596 		}
597 		xfer->fc = sc->fc;
598 
599 		s = splfw();
600 		/* XXX broken. need multiple xfer */
601 		STAILQ_INIT(&fwb->xferlist);
602 		STAILQ_INSERT_TAIL(&fwb->xferlist, xfer, link);
603 		splx(s);
604 		err = fw_bindadd(sc->fc, fwb);
605 		break;
606 	case FW_GDEVLST:
607 		i = len = 1;
608 		/* myself */
609 		devinfo = &fwdevlst->dev[0];
610 		devinfo->dst = sc->fc->nodeid;
611 		devinfo->status = 0;	/* XXX */
612 		devinfo->eui.hi = sc->fc->eui.hi;
613 		devinfo->eui.lo = sc->fc->eui.lo;
614 		STAILQ_FOREACH(fwdev, &sc->fc->devices, link) {
615 			if(len < FW_MAX_DEVLST){
616 				devinfo = &fwdevlst->dev[len++];
617 				devinfo->dst = fwdev->dst;
618 				devinfo->status =
619 					(fwdev->status == FWDEVINVAL)?0:1;
620 				devinfo->eui.hi = fwdev->eui.hi;
621 				devinfo->eui.lo = fwdev->eui.lo;
622 			}
623 			i++;
624 		}
625 		fwdevlst->n = i;
626 		fwdevlst->info_len = len;
627 		break;
628 	case FW_GTPMAP:
629 		bcopy(sc->fc->topology_map, data,
630 				(sc->fc->topology_map->crc_len + 1) * 4);
631 		break;
632 	case FW_GCROM:
633 		STAILQ_FOREACH(fwdev, &sc->fc->devices, link)
634 			if (FW_EUI64_EQUAL(fwdev->eui, crom_buf->eui))
635 				break;
636 		if (fwdev == NULL) {
637 			err = FWNODE_INVAL;
638 			break;
639 		}
640 		if (fwdev->rommax < CSRROMOFF)
641 			len = 0;
642 		else
643 			len = fwdev->rommax - CSRROMOFF + 4;
644 		if (crom_buf->len < len)
645 			len = crom_buf->len;
646 		else
647 			crom_buf->len = len;
648 		err = copyout(&fwdev->csrrom[0], crom_buf->ptr, len);
649 		break;
650 	default:
651 		sc->fc->ioctl (dev, cmd, data, flag, td);
652 		break;
653 	}
654 	return err;
655 }
656 int
657 fw_poll(dev_t dev, int events, fw_proc *td)
658 {
659 	int revents;
660 	int tmp;
661 	int unit = DEV2UNIT(dev);
662 	int sub = DEV2DMACH(dev);
663 	struct firewire_softc *sc;
664 
665 	if (DEV_FWMEM(dev))
666 		return fwmem_poll(dev, events, td);
667 
668 	sc = devclass_get_softc(firewire_devclass, unit);
669 	revents = 0;
670 	tmp = POLLIN | POLLRDNORM;
671 	if (events & tmp) {
672 		if (STAILQ_FIRST(&sc->fc->ir[sub]->q) != NULL)
673 			revents |= tmp;
674 		else
675 			selrecord(td, &sc->fc->ir[sub]->rsel); /* YYY */
676 	}
677 	tmp = POLLOUT | POLLWRNORM;
678 	if (events & tmp) {
679 		/* XXX should be fixed */
680 		revents |= tmp;
681 	}
682 
683 	return revents;
684 }
685 
686 static int
687 #if __FreeBSD_version < 500102
688 fw_mmap (dev_t dev, vm_offset_t offset, int nproto)
689 #else
690 fw_mmap (dev_t dev, vm_offset_t offset, vm_paddr_t *paddr, int nproto)
691 #endif
692 {
693 	struct firewire_softc *fc;
694 	int unit = DEV2UNIT(dev);
695 
696 	if (DEV_FWMEM(dev))
697 #if __FreeBSD_version < 500102
698 		return fwmem_mmap(dev, offset, nproto);
699 #else
700 		return fwmem_mmap(dev, offset, paddr, nproto);
701 #endif
702 
703 	fc = devclass_get_softc(firewire_devclass, unit);
704 
705 	return EINVAL;
706 }
707