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