xref: /dragonfly/sys/bus/firewire/fwdev.c (revision 37de577a)
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.36 2004/01/22 14:41:17 simokawa Exp $
35  *
36  */
37 
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/types.h>
41 #include <sys/mbuf.h>
42 #include <sys/buf.h>
43 
44 #include <sys/kernel.h>
45 #include <sys/malloc.h>
46 #include <sys/conf.h>
47 #include <sys/event.h>
48 
49 #include <sys/bus.h>
50 #include <sys/ctype.h>
51 
52 #include <sys/thread2.h>
53 
54 #include <bus/firewire/firewire.h>
55 #include <bus/firewire/firewirereg.h>
56 #include <bus/firewire/fwdma.h>
57 #include <bus/firewire/fwmem.h>
58 #include <bus/firewire/iec68113.h>
59 
60 #define	FWNODE_INVAL 0xffff
61 
62 static	d_open_t	fw_open;
63 static	d_close_t	fw_close;
64 static	d_ioctl_t	fw_ioctl;
65 static	d_kqfilter_t	fw_kqfilter;
66 static	d_read_t	fw_read;	/* for Isochronous packet */
67 static	d_write_t	fw_write;
68 static	d_mmap_t	fw_mmap;
69 static	d_strategy_t	fw_strategy;
70 
71 static void fwfilt_detach(struct knote *);
72 static int fwfilt_read(struct knote *, long);
73 static int fwfilt_write(struct knote *, long);
74 
75 struct dev_ops firewire_ops =
76 {
77 	{ "fw", 0, D_MEM },
78 	.d_open =	fw_open,
79 	.d_close =	fw_close,
80 	.d_read =	fw_read,
81 	.d_write =	fw_write,
82 	.d_ioctl =	fw_ioctl,
83 	.d_kqfilter =	fw_kqfilter,
84 	.d_mmap =	fw_mmap,
85 	.d_strategy =	fw_strategy,
86 };
87 
88 struct fw_drv1 {
89 	struct fw_xferq *ir;
90 	struct fw_xferq *it;
91 	struct fw_isobufreq bufreq;
92 };
93 
94 static int
95 fwdev_allocbuf(struct firewire_comm *fc, struct fw_xferq *q,
96 	struct fw_bufspec *b)
97 {
98 	int i;
99 
100 	if (q->flag & (FWXFERQ_RUNNING | FWXFERQ_EXTBUF))
101 		return(EBUSY);
102 
103 	q->bulkxfer = (struct fw_bulkxfer *) kmalloc(
104 		sizeof(struct fw_bulkxfer) * b->nchunk,
105 		M_FW, M_WAITOK);
106 
107 	b->psize = roundup2(b->psize, sizeof(u_int32_t));
108 	q->buf = fwdma_malloc_multiseg(fc, sizeof(u_int32_t),
109 			b->psize, b->nchunk * b->npacket, BUS_DMA_WAITOK);
110 
111 	if (q->buf == NULL) {
112 		kfree(q->bulkxfer, M_FW);
113 		q->bulkxfer = NULL;
114 		return(ENOMEM);
115 	}
116 	q->bnchunk = b->nchunk;
117 	q->bnpacket = b->npacket;
118 	q->psize = (b->psize + 3) & ~3;
119 	q->queued = 0;
120 
121 	STAILQ_INIT(&q->stvalid);
122 	STAILQ_INIT(&q->stfree);
123 	STAILQ_INIT(&q->stdma);
124 	q->stproc = NULL;
125 
126 	for(i = 0 ; i < q->bnchunk; i++){
127 		q->bulkxfer[i].poffset = i * q->bnpacket;
128 		q->bulkxfer[i].mbuf = NULL;
129 		STAILQ_INSERT_TAIL(&q->stfree, &q->bulkxfer[i], link);
130 	}
131 
132 	q->flag &= ~FWXFERQ_MODEMASK;
133 	q->flag |= FWXFERQ_STREAM;
134 	q->flag |= FWXFERQ_EXTBUF;
135 
136 	return (0);
137 }
138 
139 static int
140 fwdev_freebuf(struct fw_xferq *q)
141 {
142 	if (q->flag & FWXFERQ_EXTBUF) {
143 		if (q->buf != NULL)
144 			fwdma_free_multiseg(q->buf);
145 		q->buf = NULL;
146 		kfree(q->bulkxfer, M_FW);
147 		q->bulkxfer = NULL;
148 		q->flag &= ~FWXFERQ_EXTBUF;
149 		q->psize = 0;
150 		q->maxq = FWMAXQUEUE;
151 	}
152 	return (0);
153 }
154 
155 
156 static int
157 fw_open (struct dev_open_args *ap)
158 {
159 	cdev_t dev = ap->a_head.a_dev;
160 	int err = 0;
161 
162 	if (DEV_FWMEM(dev))
163 		return fwmem_open(ap);
164 
165 	if (dev->si_drv1 != NULL)
166 		return (EBUSY);
167 
168 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000
169 	if ((dev->si_flags & SI_NAMED) == 0) {
170 		int unit = DEV2UNIT(dev);
171 		int sub = DEV2SUB(dev);
172 
173 		make_dev(&firewire_ops, minor(dev),
174 			UID_ROOT, GID_OPERATOR, 0660,
175 			"fw%d.%d", unit, sub);
176 	}
177 #endif
178 
179 	dev->si_drv1 = kmalloc(sizeof(struct fw_drv1), M_FW, M_WAITOK | M_ZERO);
180 
181 	return err;
182 }
183 
184 static int
185 fw_close (struct dev_close_args *ap)
186 {
187 	cdev_t dev = ap->a_head.a_dev;
188 	struct firewire_softc *sc;
189 	struct firewire_comm *fc;
190 	struct fw_drv1 *d;
191 	int unit = DEV2UNIT(dev);
192 	struct fw_xfer *xfer;
193 	struct fw_bind *fwb;
194 	int err = 0;
195 
196 	if (DEV_FWMEM(dev))
197 		return fwmem_close(ap);
198 
199 	sc = devclass_get_softc(firewire_devclass, unit);
200 	fc = sc->fc;
201 	d = (struct fw_drv1 *)dev->si_drv1;
202 
203 	if (d->ir != NULL) {
204 		struct fw_xferq *ir = d->ir;
205 
206 		if ((ir->flag & FWXFERQ_OPEN) == 0)
207 			return (EINVAL);
208 		if (ir->flag & FWXFERQ_RUNNING) {
209 			ir->flag &= ~FWXFERQ_RUNNING;
210 			fc->irx_disable(fc, ir->dmach);
211 		}
212 		/* free extbuf */
213 		fwdev_freebuf(ir);
214 		/* drain receiving buffer */
215 		for (xfer = STAILQ_FIRST(&ir->q);
216 			xfer != NULL; xfer = STAILQ_FIRST(&ir->q)) {
217 			ir->queued --;
218 			STAILQ_REMOVE_HEAD(&ir->q, link);
219 
220 			xfer->resp = 0;
221 			fw_xfer_done(xfer);
222 		}
223 		/* remove binding */
224 		for (fwb = STAILQ_FIRST(&ir->binds); fwb != NULL;
225 				fwb = STAILQ_FIRST(&ir->binds)) {
226 			STAILQ_REMOVE(&fc->binds, fwb, fw_bind, fclist);
227 			STAILQ_REMOVE_HEAD(&ir->binds, chlist);
228 			kfree(fwb, M_FW);
229 		}
230 		ir->flag &= ~(FWXFERQ_OPEN |
231 			FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK);
232 		d->ir = NULL;
233 
234 	}
235 	if (d->it != NULL) {
236 		struct fw_xferq *it = d->it;
237 
238 		if ((it->flag & FWXFERQ_OPEN) == 0)
239 			return (EINVAL);
240 		if (it->flag & FWXFERQ_RUNNING) {
241 			it->flag &= ~FWXFERQ_RUNNING;
242 			fc->itx_disable(fc, it->dmach);
243 		}
244 		/* free extbuf */
245 		fwdev_freebuf(it);
246 		it->flag &= ~(FWXFERQ_OPEN |
247 			FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK);
248 		d->it = NULL;
249 	}
250 	kfree(dev->si_drv1, M_FW);
251 	dev->si_drv1 = NULL;
252 
253 	return err;
254 }
255 
256 /*
257  * read request.
258  */
259 static int
260 fw_read (struct dev_read_args *ap)
261 {
262 	cdev_t dev = ap->a_head.a_dev;
263 	struct uio *uio = ap->a_uio;
264 	struct firewire_softc *sc;
265 	struct fw_xferq *ir;
266 	struct fw_xfer *xfer;
267 	int err = 0, slept = 0;
268 	int unit = DEV2UNIT(dev);
269 	struct fw_pkt *fp;
270 
271 	if (DEV_FWMEM(dev))
272 		return physread(ap);
273 
274 	sc = devclass_get_softc(firewire_devclass, unit);
275 
276 	ir = ((struct fw_drv1 *)dev->si_drv1)->ir;
277 	if (ir == NULL || ir->buf == NULL)
278 		return (EIO);
279 
280 readloop:
281 	xfer = STAILQ_FIRST(&ir->q);
282 	if (ir->stproc == NULL) {
283 		/* iso bulkxfer */
284 		ir->stproc = STAILQ_FIRST(&ir->stvalid);
285 		if (ir->stproc != NULL) {
286 			crit_enter();
287 			STAILQ_REMOVE_HEAD(&ir->stvalid, link);
288 			crit_exit();
289 			ir->queued = 0;
290 		}
291 	}
292 	if (xfer == NULL && ir->stproc == NULL) {
293 		/* no data avaliable */
294 		if (slept == 0) {
295 			slept = 1;
296 			ir->flag |= FWXFERQ_WAKEUP;
297 			err = tsleep(ir, FWPRI, "fw_read", hz);
298 			ir->flag &= ~FWXFERQ_WAKEUP;
299 			if (err == 0)
300 				goto readloop;
301 		} else if (slept == 1)
302 			err = EIO;
303 		return err;
304 	} else if(xfer != NULL) {
305 #if 0 /* XXX broken */
306 		/* per packet mode or FWACT_CH bind?*/
307 		crit_enter();
308 		ir->queued --;
309 		STAILQ_REMOVE_HEAD(&ir->q, link);
310 		crit_exit();
311 		fp = &xfer->recv.hdr;
312 		if (sc->fc->irx_post != NULL)
313 			sc->fc->irx_post(sc->fc, fp->mode.ld);
314 		err = uiomove((void *)fp, 1 /* XXX header size */, uio);
315 		/* XXX copy payload too */
316 		/* XXX we should recycle this xfer */
317 #endif
318 		fw_xfer_free( xfer);
319 	} else if(ir->stproc != NULL) {
320 		/* iso bulkxfer */
321 		fp = (struct fw_pkt *)fwdma_v_addr(ir->buf,
322 				ir->stproc->poffset + ir->queued);
323 		if(sc->fc->irx_post != NULL)
324 			sc->fc->irx_post(sc->fc, fp->mode.ld);
325 		if(fp->mode.stream.len == 0){
326 			err = EIO;
327 			return err;
328 		}
329 		err = uiomove((caddr_t)fp,
330 			fp->mode.stream.len + sizeof(u_int32_t), uio);
331 		ir->queued ++;
332 		if(ir->queued >= ir->bnpacket){
333 			crit_enter();
334 			STAILQ_INSERT_TAIL(&ir->stfree, ir->stproc, link);
335 			crit_exit();
336 			sc->fc->irx_enable(sc->fc, ir->dmach);
337 			ir->stproc = NULL;
338 		}
339 		if (uio->uio_resid >= ir->psize) {
340 			slept = -1;
341 			goto readloop;
342 		}
343 	}
344 	return err;
345 }
346 
347 static int
348 fw_write (struct dev_write_args *ap)
349 {
350 	cdev_t dev = ap->a_head.a_dev;
351 	struct uio *uio = ap->a_uio;
352 	int err = 0;
353 	struct firewire_softc *sc;
354 	int unit = DEV2UNIT(dev);
355 	int slept = 0;
356 	struct fw_pkt *fp;
357 	struct fw_xferq *it;
358 
359 	if (DEV_FWMEM(dev))
360 		return physwrite(ap);
361 
362 	sc = devclass_get_softc(firewire_devclass, unit);
363 	it = ((struct fw_drv1 *)dev->si_drv1)->it;
364 	if (it == NULL || it->buf == NULL)
365 		return (EIO);
366 isoloop:
367 	if (it->stproc == NULL) {
368 		it->stproc = STAILQ_FIRST(&it->stfree);
369 		if (it->stproc != NULL) {
370 			crit_enter();
371 			STAILQ_REMOVE_HEAD(&it->stfree, link);
372 			crit_exit();
373 			it->queued = 0;
374 		} else if (slept == 0) {
375 			slept = 1;
376 			err = sc->fc->itx_enable(sc->fc, it->dmach);
377 			if (err)
378 				return err;
379 			err = tsleep(it, FWPRI, "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 *)fwdma_v_addr(it->buf,
389 			it->stproc->poffset + it->queued);
390 	err = uiomove((caddr_t)fp, sizeof(struct fw_isohdr), uio);
391 	err = uiomove((caddr_t)fp->mode.stream.payload,
392 				fp->mode.stream.len, uio);
393 	it->queued ++;
394 	if (it->queued >= it->bnpacket) {
395 		crit_enter();
396 		STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link);
397 		crit_exit();
398 		it->stproc = NULL;
399 		err = sc->fc->itx_enable(sc->fc, it->dmach);
400 	}
401 	if (uio->uio_resid >= sizeof(struct fw_isohdr)) {
402 		slept = 0;
403 		goto isoloop;
404 	}
405 	return err;
406 }
407 /*
408  * ioctl support.
409  */
410 static int
411 fw_ioctl (struct dev_ioctl_args *ap)
412 {
413 	cdev_t dev = ap->a_head.a_dev;
414 	struct firewire_softc *sc;
415 	struct firewire_comm *fc;
416 	struct fw_drv1 *d;
417 	int unit = DEV2UNIT(dev);
418 	int i, len, err = 0;
419 	struct fw_device *fwdev;
420 	struct fw_bind *fwb;
421 	struct fw_xferq *ir, *it;
422 	struct fw_xfer *xfer;
423 	struct fw_pkt *fp;
424 	struct fw_devinfo *devinfo;
425 	void *ptr;
426 
427 	struct fw_devlstreq *fwdevlst = (struct fw_devlstreq *)ap->a_data;
428 	struct fw_asyreq *asyreq = (struct fw_asyreq *)ap->a_data;
429 	struct fw_isochreq *ichreq = (struct fw_isochreq *)ap->a_data;
430 	struct fw_isobufreq *ibufreq = (struct fw_isobufreq *)ap->a_data;
431 	struct fw_asybindreq *bindreq = (struct fw_asybindreq *)ap->a_data;
432 	struct fw_crom_buf *crom_buf = (struct fw_crom_buf *)ap->a_data;
433 
434 	if (DEV_FWMEM(dev))
435 		return fwmem_ioctl(ap);
436 
437 	if (!ap->a_data)
438 		return(EINVAL);
439 
440 	sc = devclass_get_softc(firewire_devclass, unit);
441 	fc = sc->fc;
442 	d = (struct fw_drv1 *)dev->si_drv1;
443 	ir = d->ir;
444 	it = d->it;
445 
446 	switch (ap->a_cmd) {
447 	case FW_STSTREAM:
448 		if (it == NULL) {
449 			for (i = 0; i < fc->nisodma; i ++) {
450 				it = fc->it[i];
451 				if ((it->flag & FWXFERQ_OPEN) == 0)
452 					 break;
453 	                }
454 			if (i >= fc->nisodma) {
455 				err = EBUSY;
456 				break;
457 			}
458 			err = fwdev_allocbuf(fc, it, &d->bufreq.tx);
459 			if (err)
460 				break;
461 			it->flag |=  FWXFERQ_OPEN;
462 		}
463 		it->flag &= ~0xff;
464 		it->flag |= (0x3f & ichreq->ch);
465 		it->flag |= ((0x3 & ichreq->tag) << 6);
466 		d->it = it;
467 		break;
468 	case FW_GTSTREAM:
469 		if (it != NULL) {
470 			ichreq->ch = it->flag & 0x3f;
471 			ichreq->tag = it->flag >> 2 & 0x3;
472 		} else
473 			err = EINVAL;
474 		break;
475 	case FW_SRSTREAM:
476 		if (ir == NULL) {
477 			for (i = 0; i < fc->nisodma; i ++) {
478 				ir = fc->ir[i];
479 				if ((ir->flag & FWXFERQ_OPEN) == 0)
480 					break;
481 			}
482 			if (i >= fc->nisodma) {
483 				err = EBUSY;
484 				break;
485 			}
486 			err = fwdev_allocbuf(fc, ir, &d->bufreq.rx);
487 			if (err)
488 				break;
489 			ir->flag |=  FWXFERQ_OPEN;
490 		}
491 		ir->flag &= ~0xff;
492 		ir->flag |= (0x3f & ichreq->ch);
493 		ir->flag |= ((0x3 & ichreq->tag) << 6);
494 		d->ir = ir;
495 		err = fc->irx_enable(fc, ir->dmach);
496 		break;
497 	case FW_GRSTREAM:
498 		if (d->ir != NULL) {
499 			ichreq->ch = ir->flag & 0x3f;
500 			ichreq->tag = ir->flag >> 2 & 0x3;
501 		} else
502 			err = EINVAL;
503 		break;
504 	case FW_SSTBUF:
505 		bcopy(ibufreq, &d->bufreq, sizeof(d->bufreq));
506 		break;
507 	case FW_GSTBUF:
508 		bzero(&ibufreq->rx, sizeof(ibufreq->rx));
509 		if (ir != NULL) {
510 			ibufreq->rx.nchunk = ir->bnchunk;
511 			ibufreq->rx.npacket = ir->bnpacket;
512 			ibufreq->rx.psize = ir->psize;
513 		}
514 		bzero(&ibufreq->tx, sizeof(ibufreq->tx));
515 		if (it != NULL) {
516 			ibufreq->tx.nchunk = it->bnchunk;
517 			ibufreq->tx.npacket = it->bnpacket;
518 			ibufreq->tx.psize = it->psize;
519 		}
520 		break;
521 	case FW_ASYREQ:
522 	{
523 		struct tcode_info *tinfo;
524 		int pay_len = 0;
525 
526 		fp = &asyreq->pkt;
527 		tinfo = &sc->fc->tcode[fp->mode.hdr.tcode];
528 
529 		if ((tinfo->flag & FWTI_BLOCK_ASY) != 0)
530 			pay_len = MAX(0, asyreq->req.len - tinfo->hdr_len);
531 
532 		xfer = fw_xfer_alloc_buf(M_FWXFER, pay_len, PAGE_SIZE/*XXX*/);
533 		if (xfer == NULL)
534 			return (ENOMEM);
535 
536 		switch (asyreq->req.type) {
537 		case FWASREQNODE:
538 			break;
539 		case FWASREQEUI:
540 			fwdev = fw_noderesolve_eui64(sc->fc,
541 						&asyreq->req.dst.eui);
542 			if (fwdev == NULL) {
543 				device_printf(sc->fc->bdev,
544 					"cannot find node\n");
545 				err = EINVAL;
546 				goto out;
547 			}
548 			fp->mode.hdr.dst = FWLOCALBUS | fwdev->dst;
549 			break;
550 		case FWASRESTL:
551 			/* XXX what's this? */
552 			break;
553 		case FWASREQSTREAM:
554 			/* nothing to do */
555 			break;
556 		}
557 
558 		bcopy(fp, (void *)&xfer->send.hdr, tinfo->hdr_len);
559 		if (pay_len > 0)
560 			bcopy((char *)fp + tinfo->hdr_len,
561 			    (void *)&xfer->send.payload, pay_len);
562 		xfer->send.spd = asyreq->req.sped;
563 		xfer->act.hand = fw_asy_callback;
564 
565 		if ((err = fw_asyreq(sc->fc, -1, xfer)) != 0)
566 			goto out;
567 		if ((err = tsleep(xfer, FWPRI, "asyreq", hz)) != 0)
568 			goto out;
569 		if (xfer->resp != 0) {
570 			err = EIO;
571 			goto out;
572 		}
573 		if ((tinfo->flag & FWTI_TLABEL) == 0)
574 			goto out;
575 
576 		/* copy response */
577 		tinfo = &sc->fc->tcode[xfer->recv.hdr.mode.hdr.tcode];
578 		if (asyreq->req.len >= xfer->recv.pay_len + tinfo->hdr_len)
579 			asyreq->req.len = xfer->recv.pay_len;
580 		else
581 			err = EINVAL;
582 		bcopy(&xfer->recv.hdr, fp, tinfo->hdr_len);
583 		bcopy(xfer->recv.payload, (char *)fp + tinfo->hdr_len,
584 		    MAX(0, asyreq->req.len - tinfo->hdr_len));
585 out:
586 		fw_xfer_free_buf(xfer);
587 		break;
588 	}
589 	case FW_IBUSRST:
590 		sc->fc->ibr(sc->fc);
591 		break;
592 	case FW_CBINDADDR:
593 		fwb = fw_bindlookup(sc->fc,
594 				bindreq->start.hi, bindreq->start.lo);
595 		if(fwb == NULL){
596 			err = EINVAL;
597 			break;
598 		}
599 		STAILQ_REMOVE(&sc->fc->binds, fwb, fw_bind, fclist);
600 		STAILQ_REMOVE(&ir->binds, fwb, fw_bind, chlist);
601 		kfree(fwb, M_FW);
602 		break;
603 	case FW_SBINDADDR:
604 		if(bindreq->len <= 0 ){
605 			err = EINVAL;
606 			break;
607 		}
608 		if(bindreq->start.hi > 0xffff ){
609 			err = EINVAL;
610 			break;
611 		}
612 		fwb = kmalloc(sizeof (struct fw_bind), M_FW, M_WAITOK);
613 		fwb->start = ((u_int64_t)bindreq->start.hi << 32) |
614 		    bindreq->start.lo;
615 		fwb->end = fwb->start +  bindreq->len;
616 		/* XXX */
617 		fwb->sub = ir->dmach;
618 		fwb->act_type = FWACT_CH;
619 
620 		/* XXX alloc buf */
621 		xfer = fw_xfer_alloc(M_FWXFER);
622 		if(xfer == NULL){
623 			kfree(fwb, M_FW);
624 			return (ENOMEM);
625 		}
626 		xfer->fc = sc->fc;
627 
628 		crit_enter();
629 		/* XXX broken. need multiple xfer */
630 		STAILQ_INIT(&fwb->xferlist);
631 		STAILQ_INSERT_TAIL(&fwb->xferlist, xfer, link);
632 		crit_exit();
633 		err = fw_bindadd(sc->fc, fwb);
634 		break;
635 	case FW_GDEVLST:
636 		i = len = 1;
637 		/* myself */
638 		devinfo = &fwdevlst->dev[0];
639 		devinfo->dst = sc->fc->nodeid;
640 		devinfo->status = 0;	/* XXX */
641 		devinfo->eui.hi = sc->fc->eui.hi;
642 		devinfo->eui.lo = sc->fc->eui.lo;
643 		STAILQ_FOREACH(fwdev, &sc->fc->devices, link) {
644 			if(len < FW_MAX_DEVLST){
645 				devinfo = &fwdevlst->dev[len++];
646 				devinfo->dst = fwdev->dst;
647 				devinfo->status =
648 					(fwdev->status == FWDEVINVAL)?0:1;
649 				devinfo->eui.hi = fwdev->eui.hi;
650 				devinfo->eui.lo = fwdev->eui.lo;
651 			}
652 			i++;
653 		}
654 		fwdevlst->n = i;
655 		fwdevlst->info_len = len;
656 		break;
657 	case FW_GTPMAP:
658 		bcopy(sc->fc->topology_map, ap->a_data,
659 				(sc->fc->topology_map->crc_len + 1) * 4);
660 		break;
661 	case FW_GCROM:
662 		STAILQ_FOREACH(fwdev, &sc->fc->devices, link)
663 			if (FW_EUI64_EQUAL(fwdev->eui, crom_buf->eui))
664 				break;
665 		if (fwdev == NULL) {
666 			if (!FW_EUI64_EQUAL(sc->fc->eui, crom_buf->eui)) {
667 				err = FWNODE_INVAL;
668 				break;
669 			}
670 			/* myself */
671 			ptr = kmalloc(CROMSIZE, M_FW, M_WAITOK);
672 			len = CROMSIZE;
673 			for (i = 0; i < CROMSIZE/4; i++)
674 				((u_int32_t *)ptr)[i]
675 					= ntohl(sc->fc->config_rom[i]);
676 		} else {
677 			/* found */
678 			ptr = (void *)&fwdev->csrrom[0];
679 			if (fwdev->rommax < CSRROMOFF)
680 				len = 0;
681 			else
682 				len = fwdev->rommax - CSRROMOFF + 4;
683 		}
684 		if (crom_buf->len < len && crom_buf->len >= 0)
685 			len = crom_buf->len;
686 		else
687 			crom_buf->len = len;
688 		err = copyout(ptr, crom_buf->ptr, len);
689 		if (fwdev == NULL)
690 			/* myself */
691 			kfree(ptr, M_FW);
692 		break;
693 	default:
694 		sc->fc->ioctl(ap);
695 		break;
696 	}
697 	return err;
698 }
699 
700 static struct filterops fw_read_filterops =
701 	{ FILTEROP_ISFD, NULL, fwfilt_detach, fwfilt_read };
702 static struct filterops fw_write_filterops =
703 	{ FILTEROP_ISFD, NULL, fwfilt_detach, fwfilt_write };
704 
705 static int
706 fw_kqfilter(struct dev_kqfilter_args *ap)
707 {
708 	cdev_t dev = ap->a_head.a_dev;
709 	struct fw_xferq *ir;
710 	struct knote *kn = ap->a_kn;
711 	struct klist *klist;
712 
713 	/*
714 	 * XXX Implement filters for mem?
715 	 */
716 	if (DEV_FWMEM(dev)) {
717 		ap->a_result = EOPNOTSUPP;
718 		return (0);
719 	}
720 
721 	ir = ((struct fw_drv1 *)dev->si_drv1)->ir;
722 
723 	ap->a_result = 0;
724 
725 	switch (kn->kn_filter) {
726 	case EVFILT_READ:
727 		kn->kn_fop = &fw_read_filterops;
728 		kn->kn_hook = (caddr_t)ir;
729 		break;
730 	case EVFILT_WRITE:
731 		kn->kn_fop = &fw_write_filterops;
732 		kn->kn_hook = (caddr_t)ir;
733 		break;
734 	default:
735 		ap->a_result = EOPNOTSUPP;
736 		return (0);
737 	}
738 
739 	klist = &ir->rkq.ki_note;
740 	knote_insert(klist, kn);
741 
742 	return (0);
743 }
744 
745 static void
746 fwfilt_detach(struct knote *kn)
747 {
748 	struct fw_xferq *ir = (struct fw_xferq *)kn->kn_hook;
749 	struct klist *klist = &ir->rkq.ki_note;
750 
751 	knote_remove(klist, kn);
752 }
753 
754 static int
755 fwfilt_read(struct knote *kn, long hint)
756 {
757 	struct fw_xferq *ir = (struct fw_xferq *)kn->kn_hook;
758 	int ready = 0;
759 
760 	if (STAILQ_FIRST(&ir->q) != NULL)
761 		ready = 1;
762 
763 	return (ready);
764 }
765 
766 static int
767 fwfilt_write(struct knote *kn, long hint)
768 {
769 	/* XXX should be fixed */
770 	return (1);
771 }
772 
773 static int
774 fw_mmap (struct dev_mmap_args *ap)
775 {
776 	cdev_t dev = ap->a_head.a_dev;
777 
778 	if (DEV_FWMEM(dev))
779 		return fwmem_mmap(ap);
780 
781 	return EINVAL;
782 }
783 
784 static int
785 fw_strategy(struct dev_strategy_args *ap)
786 {
787 	cdev_t dev = ap->a_head.a_dev;
788 	struct bio *bio = ap->a_bio;
789 	struct buf *bp = bio->bio_buf;
790 
791 	if (DEV_FWMEM(dev)) {
792 		fwmem_strategy(ap);
793 		return(0);
794 	}
795 	bp->b_error = EOPNOTSUPP;
796 	bp->b_flags |= B_ERROR;
797 	bp->b_resid = bp->b_bcount;
798 	biodone(bio);
799 	return(0);
800 }
801 
802 int
803 fwdev_makedev(struct firewire_softc *sc)
804 {
805 	/*HELPME dev_ops_add(&firewire_ops, FW_UNITMASK, FW_UNIT(unit));*/
806 	return(0);
807 }
808 
809 int
810 fwdev_destroydev(struct firewire_softc *sc)
811 {
812 	int unit;
813 
814 	unit = device_get_unit(sc->fc->bdev);
815 	dev_ops_remove_minor(&firewire_ops, /*FW_UNITMASK, */FW_UNIT(unit));
816 	return(0);
817 }
818 
819 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000
820 #define NDEVTYPE 2
821 void
822 fwdev_clone(void *arg, char *name, int namelen, cdev_t *dev)
823 {
824 	struct firewire_softc *sc;
825 	char *devnames[NDEVTYPE] = {"fw", "fwmem"};
826 	char *subp = NULL;
827 	int devflag[NDEVTYPE] = {0, FWMEM_FLAG};
828 	int i, unit = 0, sub = 0;
829 
830 	if (*dev != NULL)
831 		return;
832 
833 	for (i = 0; i < NDEVTYPE; i++)
834 		if (dev_stdclone(name, &subp, devnames[i], &unit) == 2)
835 			goto found;
836 	/* not match */
837 	return;
838 found:
839 
840 	if (subp == NULL || *subp++ != '.')
841 		return;
842 
843 	/* /dev/fwU.S */
844 	while (isdigit(*subp)) {
845 		sub *= 10;
846 		sub += *subp++ - '0';
847 	}
848 	if (*subp != '\0')
849 		return;
850 
851 	sc = devclass_get_softc(firewire_devclass, unit);
852 	if (sc == NULL)
853 		return;
854 	*dev = make_dev(&firewire_ops, MAKEMINOR(devflag[i], unit, sub),
855 		       UID_ROOT, GID_OPERATOR, 0660,
856 		       "%s%d.%d", devnames[i], unit, sub);
857 	(*dev)->si_flags |= SI_CHEAPCLONE;
858 	dev_depends(sc->dev, *dev);
859 	return;
860 }
861 #endif
862