Home | History | Annotate | Line # | Download | only in ieee1394
fwdev.c revision 1.31
      1 /*	$NetBSD: fwdev.c,v 1.30 2014/07/25 08:10:37 dholland Exp $	*/
      2 /*-
      3  * Copyright (c) 2003 Hidetoshi Shimokawa
      4  * Copyright (c) 1998-2002 Katsushi Kobayashi and Hidetoshi Shimokawa
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. All advertising materials mentioning features or use of this software
     16  *    must display the acknowledgement as bellow:
     17  *
     18  *    This product includes software developed by K. Kobayashi and H. Shimokawa
     19  *
     20  * 4. The name of the author may not be used to endorse or promote products
     21  *    derived from this software without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     25  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     26  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
     27  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     28  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     29  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
     31  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
     32  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     33  * POSSIBILITY OF SUCH DAMAGE.
     34  *
     35  * $FreeBSD: src/sys/dev/firewire/fwdev.c,v 1.52 2007/06/06 14:31:36 simokawa Exp $
     36  *
     37  */
     38 
     39 #include <sys/cdefs.h>
     40 __KERNEL_RCSID(0, "$NetBSD: fwdev.c,v 1.30 2014/07/25 08:10:37 dholland Exp $");
     41 
     42 #include <sys/param.h>
     43 #include <sys/device.h>
     44 #include <sys/errno.h>
     45 #include <sys/buf.h>
     46 #include <sys/bus.h>
     47 #include <sys/conf.h>
     48 #include <sys/kernel.h>
     49 #include <sys/malloc.h>
     50 #include <sys/mbuf.h>
     51 #include <sys/poll.h>
     52 #include <sys/proc.h>
     53 #include <sys/select.h>
     54 
     55 #include <dev/ieee1394/firewire.h>
     56 #include <dev/ieee1394/firewirereg.h>
     57 #include <dev/ieee1394/fwdma.h>
     58 #include <dev/ieee1394/fwmem.h>
     59 #include <dev/ieee1394/iec68113.h>
     60 
     61 #include "ioconf.h"
     62 
     63 #define	FWNODE_INVAL 0xffff
     64 
     65 dev_type_open(fw_open);
     66 dev_type_close(fw_close);
     67 dev_type_read(fw_read);
     68 dev_type_write(fw_write);
     69 dev_type_ioctl(fw_ioctl);
     70 dev_type_poll(fw_poll);
     71 dev_type_mmap(fw_mmap);
     72 dev_type_strategy(fw_strategy);
     73 
     74 const struct bdevsw fw_bdevsw = {
     75 	.d_open = fw_open,
     76 	.d_close = fw_close,
     77 	.d_strategy = fw_strategy,
     78 	.d_ioctl = fw_ioctl,
     79 	.d_dump = nodump,
     80 	.d_psize = nosize,
     81 	.d_discard = nodiscard,
     82 	.d_flag = D_OTHER
     83 };
     84 
     85 const struct cdevsw fw_cdevsw = {
     86 	.d_open = fw_open,
     87 	.d_close = fw_close,
     88 	.d_read = fw_read,
     89 	.d_write = fw_write,
     90 	.d_ioctl = fw_ioctl,
     91 	.d_stop = nostop,
     92 	.d_tty = notty,
     93 	.d_poll = fw_poll,
     94 	.d_mmap = fw_mmap,
     95 	.d_kqfilter = nokqfilter,
     96 	.d_discard = nodiscard,
     97 	.d_flag = D_OTHER
     98 };
     99 
    100 struct fw_drv1 {
    101 	struct firewire_comm *fc;
    102 	struct fw_xferq *ir;
    103 	struct fw_xferq *it;
    104 	struct fw_isobufreq bufreq;
    105 	STAILQ_HEAD(, fw_bind) binds;
    106 	STAILQ_HEAD(, fw_xfer) rq;
    107 	kcondvar_t cv;
    108 };
    109 
    110 static int fwdev_allocbuf(struct firewire_comm *, struct fw_xferq *,
    111 			  struct fw_bufspec *);
    112 static int fwdev_freebuf(struct fw_xferq *);
    113 static int fw_read_async(struct fw_drv1 *, struct uio *, int);
    114 static int fw_write_async(struct fw_drv1 *, struct uio *, int);
    115 static void fw_hand(struct fw_xfer *);
    116 
    117 
    118 int
    119 fw_open(dev_t dev, int flags, int fmt, struct lwp *td)
    120 {
    121 	struct firewire_softc *sc;
    122 	struct fw_drv1 *d;
    123 	int err = 0;
    124 
    125 	sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
    126 	if (sc == NULL)
    127 		return ENXIO;
    128 
    129 	if (DEV_FWMEM(dev))
    130 		return fwmem_open(dev, flags, fmt, td);
    131 
    132 	mutex_enter(&sc->fc->fc_mtx);
    133 	if (sc->si_drv1 != NULL) {
    134 		mutex_exit(&sc->fc->fc_mtx);
    135 		return EBUSY;
    136 	}
    137 	/* set dummy value for allocation */
    138 	sc->si_drv1 = (void *)-1;
    139 	mutex_exit(&sc->fc->fc_mtx);
    140 
    141 	sc->si_drv1 = malloc(sizeof(struct fw_drv1), M_FW, M_WAITOK | M_ZERO);
    142 	if (sc->si_drv1 == NULL)
    143 		return ENOMEM;
    144 
    145 	d = (struct fw_drv1 *)sc->si_drv1;
    146 	d->fc = sc->fc;
    147 	STAILQ_INIT(&d->binds);
    148 	STAILQ_INIT(&d->rq);
    149 	cv_init(&d->cv, "fwra");
    150 
    151 	return err;
    152 }
    153 
    154 int
    155 fw_close(dev_t dev, int flags, int fmt, struct lwp *td)
    156 {
    157 	struct firewire_softc *sc;
    158 	struct firewire_comm *fc;
    159 	struct fw_drv1 *d;
    160 	struct fw_xfer *xfer;
    161 	struct fw_bind *fwb;
    162         int err = 0;
    163 
    164 	sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
    165 	if (sc == NULL)
    166 		return ENXIO;
    167 
    168 	if (DEV_FWMEM(dev))
    169 		return fwmem_close(dev, flags, fmt, td);
    170 
    171 	d = (struct fw_drv1 *)sc->si_drv1;
    172 	fc = d->fc;
    173 
    174 	/* remove binding */
    175 	for (fwb = STAILQ_FIRST(&d->binds); fwb != NULL;
    176 	    fwb = STAILQ_FIRST(&d->binds)) {
    177 		fw_bindremove(fc, fwb);
    178 		STAILQ_REMOVE_HEAD(&d->binds, chlist);
    179 		fw_xferlist_remove(&fwb->xferlist);
    180 		free(fwb, M_FW);
    181 	}
    182 	if (d->ir != NULL) {
    183 		struct fw_xferq *ir = d->ir;
    184 
    185 		if ((ir->flag & FWXFERQ_OPEN) == 0)
    186 			return EINVAL;
    187 		if (ir->flag & FWXFERQ_RUNNING) {
    188 			ir->flag &= ~FWXFERQ_RUNNING;
    189 			fc->irx_disable(fc, ir->dmach);
    190 		}
    191 		/* free extbuf */
    192 		fwdev_freebuf(ir);
    193 		/* drain receiving buffer */
    194 		for (xfer = STAILQ_FIRST(&ir->q); xfer != NULL;
    195 		    xfer = STAILQ_FIRST(&ir->q)) {
    196 			ir->queued--;
    197 			STAILQ_REMOVE_HEAD(&ir->q, link);
    198 
    199 			xfer->resp = 0;
    200 			fw_xfer_done(xfer);
    201 		}
    202 		ir->flag &=
    203 		    ~(FWXFERQ_OPEN | FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK);
    204 		d->ir = NULL;
    205 
    206 	}
    207 	if (d->it != NULL) {
    208 		struct fw_xferq *it = d->it;
    209 
    210 		if ((it->flag & FWXFERQ_OPEN) == 0)
    211 			return EINVAL;
    212 		if (it->flag & FWXFERQ_RUNNING) {
    213 			it->flag &= ~FWXFERQ_RUNNING;
    214 			fc->itx_disable(fc, it->dmach);
    215 		}
    216 		/* free extbuf */
    217 		fwdev_freebuf(it);
    218 		it->flag &=
    219 		    ~(FWXFERQ_OPEN | FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK);
    220 		d->it = NULL;
    221 	}
    222 	cv_destroy(&d->cv);
    223 	free(sc->si_drv1, M_FW);
    224 	sc->si_drv1 = NULL;
    225 
    226 	return err;
    227 }
    228 
    229 int
    230 fw_read(dev_t dev, struct uio *uio, int ioflag)
    231 {
    232 	struct firewire_softc *sc;
    233 	struct firewire_comm *fc;
    234 	struct fw_drv1 *d;
    235 	struct fw_xferq *ir;
    236 	struct fw_pkt *fp;
    237 	int err = 0, slept = 0;
    238 
    239 	sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
    240 	if (sc == NULL)
    241 		return ENXIO;
    242 
    243 	if (DEV_FWMEM(dev))
    244 		return physio(fw_strategy, NULL, dev, ioflag, minphys, uio);
    245 
    246 	d = (struct fw_drv1 *)sc->si_drv1;
    247 	fc = d->fc;
    248 	ir = d->ir;
    249 
    250 	if (ir == NULL)
    251 		return fw_read_async(d, uio, ioflag);
    252 
    253 	if (ir->buf == NULL)
    254 		return EIO;
    255 
    256 	mutex_enter(&fc->fc_mtx);
    257 readloop:
    258 	if (ir->stproc == NULL) {
    259 		/* iso bulkxfer */
    260 		ir->stproc = STAILQ_FIRST(&ir->stvalid);
    261 		if (ir->stproc != NULL) {
    262 			STAILQ_REMOVE_HEAD(&ir->stvalid, link);
    263 			ir->queued = 0;
    264 		}
    265 	}
    266 	if (ir->stproc == NULL) {
    267 		/* no data avaliable */
    268 		if (slept == 0) {
    269 			slept = 1;
    270 			ir->flag |= FWXFERQ_WAKEUP;
    271 			err = cv_timedwait_sig(&ir->cv, &fc->fc_mtx, hz);
    272 			ir->flag &= ~FWXFERQ_WAKEUP;
    273 			if (err == 0)
    274 				goto readloop;
    275 		} else if (slept == 1)
    276 			err = EIO;
    277 		mutex_exit(&fc->fc_mtx);
    278 		return err;
    279 	} else if (ir->stproc != NULL) {
    280 		/* iso bulkxfer */
    281 		mutex_exit(&fc->fc_mtx);
    282 		fp = (struct fw_pkt *)fwdma_v_addr(ir->buf,
    283 		    ir->stproc->poffset + ir->queued);
    284 		if (fc->irx_post != NULL)
    285 			fc->irx_post(fc, fp->mode.ld);
    286 		if (fp->mode.stream.len == 0)
    287 			return EIO;
    288 		err = uiomove((void *)fp,
    289 		    fp->mode.stream.len + sizeof(uint32_t), uio);
    290 		ir->queued++;
    291 		if (ir->queued >= ir->bnpacket) {
    292 			STAILQ_INSERT_TAIL(&ir->stfree, ir->stproc, link);
    293 			fc->irx_enable(fc, ir->dmach);
    294 			ir->stproc = NULL;
    295 		}
    296 		if (uio->uio_resid >= ir->psize) {
    297 			slept = -1;
    298 			mutex_enter(&fc->fc_mtx);
    299 			goto readloop;
    300 		}
    301 	} else
    302 		mutex_exit(&fc->fc_mtx);
    303 	return err;
    304 }
    305 
    306 int
    307 fw_write(dev_t dev, struct uio *uio, int ioflag)
    308 {
    309 	struct firewire_softc *sc;
    310 	struct firewire_comm *fc;
    311 	struct fw_drv1 *d;
    312 	struct fw_pkt *fp;
    313 	struct fw_xferq *it;
    314         int slept = 0, err = 0;
    315 
    316 	sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
    317 	if (sc == NULL)
    318 		return ENXIO;
    319 
    320 	if (DEV_FWMEM(dev))
    321 		return physio(fw_strategy, NULL, dev, ioflag, minphys, uio);
    322 
    323 	d = (struct fw_drv1 *)sc->si_drv1;
    324 	fc = d->fc;
    325 	it = d->it;
    326 
    327 	if (it == NULL)
    328 		return fw_write_async(d, uio, ioflag);
    329 
    330 	if (it->buf == NULL)
    331 		return EIO;
    332 
    333 	mutex_enter(&fc->fc_mtx);
    334 isoloop:
    335 	if (it->stproc == NULL) {
    336 		it->stproc = STAILQ_FIRST(&it->stfree);
    337 		if (it->stproc != NULL) {
    338 			STAILQ_REMOVE_HEAD(&it->stfree, link);
    339 			it->queued = 0;
    340 		} else if (slept == 0) {
    341 			slept = 1;
    342 #if 0   /* XXX to avoid lock recursion */
    343 			err = fc->itx_enable(fc, it->dmach);
    344 			if (err)
    345 				goto out;
    346 #endif
    347 			err = cv_timedwait_sig(&it->cv, &fc->fc_mtx, hz);
    348 			if (err)
    349 				goto out;
    350 			goto isoloop;
    351 		} else {
    352 			err = EIO;
    353 			goto out;
    354 		}
    355 	}
    356 	mutex_exit(&fc->fc_mtx);
    357 	fp = (struct fw_pkt *)fwdma_v_addr(it->buf,
    358 	    it->stproc->poffset + it->queued);
    359 	err = uiomove((void *)fp, sizeof(struct fw_isohdr), uio);
    360 	if (err != 0)
    361 		return err;
    362 	err =
    363 	    uiomove((void *)fp->mode.stream.payload, fp->mode.stream.len, uio);
    364 	it->queued++;
    365 	if (it->queued >= it->bnpacket) {
    366 		STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link);
    367 		it->stproc = NULL;
    368 		err = fc->itx_enable(fc, it->dmach);
    369 	}
    370 	if (uio->uio_resid >= sizeof(struct fw_isohdr)) {
    371 		slept = 0;
    372 		mutex_enter(&fc->fc_mtx);
    373 		goto isoloop;
    374 	}
    375 	return err;
    376 
    377 out:
    378 	mutex_exit(&fc->fc_mtx);
    379 	return err;
    380 }
    381 
    382 int
    383 fw_ioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *td)
    384 {
    385 	struct firewire_softc *sc;
    386 	struct firewire_comm *fc;
    387 	struct fw_drv1 *d;
    388 	struct fw_device *fwdev;
    389 	struct fw_bind *fwb;
    390 	struct fw_xferq *ir, *it;
    391 	struct fw_xfer *xfer;
    392 	struct fw_pkt *fp;
    393 	struct fw_devinfo *devinfo;
    394 	struct fw_devlstreq *fwdevlst = (struct fw_devlstreq *)data;
    395 	struct fw_asyreq *asyreq = (struct fw_asyreq *)data;
    396 	struct fw_isochreq *ichreq = (struct fw_isochreq *)data;
    397 	struct fw_isobufreq *ibufreq = (struct fw_isobufreq *)data;
    398 	struct fw_asybindreq *bindreq = (struct fw_asybindreq *)data;
    399 	struct fw_crom_buf *crom_buf = (struct fw_crom_buf *)data;
    400 	int i, len, err = 0;
    401 	void *ptr;
    402 
    403 	sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
    404 	if (sc == NULL)
    405 		return ENXIO;
    406 
    407 	if (DEV_FWMEM(dev))
    408 		return fwmem_ioctl(dev, cmd, data, flag, td);
    409 
    410 	if (!data)
    411 		return EINVAL;
    412 
    413 	d = (struct fw_drv1 *)sc->si_drv1;
    414 	fc = d->fc;
    415 	ir = d->ir;
    416 	it = d->it;
    417 
    418 	switch (cmd) {
    419 	case FW_STSTREAM:
    420 		if (it == NULL) {
    421 			i = fw_open_isodma(fc, /* tx */1);
    422 			if (i < 0) {
    423 				err = EBUSY;
    424 				break;
    425 			}
    426 			it = fc->it[i];
    427 			err = fwdev_allocbuf(fc, it, &d->bufreq.tx);
    428 			if (err) {
    429 				it->flag &= ~FWXFERQ_OPEN;
    430 				break;
    431 			}
    432 		}
    433 		it->flag &= ~0xff;
    434 		it->flag |= (0x3f & ichreq->ch);
    435 		it->flag |= ((0x3 & ichreq->tag) << 6);
    436 		d->it = it;
    437 		break;
    438 
    439 	case FW_GTSTREAM:
    440 		if (it != NULL) {
    441 			ichreq->ch = it->flag & 0x3f;
    442 			ichreq->tag = it->flag >> 2 & 0x3;
    443 		} else
    444 			err = EINVAL;
    445 		break;
    446 
    447 	case FW_SRSTREAM:
    448 		if (ir == NULL) {
    449 			i = fw_open_isodma(fc, /* tx */0);
    450 			if (i < 0) {
    451 				err = EBUSY;
    452 				break;
    453 			}
    454 			ir = fc->ir[i];
    455 			err = fwdev_allocbuf(fc, ir, &d->bufreq.rx);
    456 			if (err) {
    457 				ir->flag &= ~FWXFERQ_OPEN;
    458 				break;
    459 			}
    460 		}
    461 		ir->flag &= ~0xff;
    462 		ir->flag |= (0x3f & ichreq->ch);
    463 		ir->flag |= ((0x3 & ichreq->tag) << 6);
    464 		d->ir = ir;
    465 		err = fc->irx_enable(fc, ir->dmach);
    466 		break;
    467 
    468 	case FW_GRSTREAM:
    469 		if (d->ir != NULL) {
    470 			ichreq->ch = ir->flag & 0x3f;
    471 			ichreq->tag = ir->flag >> 2 & 0x3;
    472 		} else
    473 			err = EINVAL;
    474 		break;
    475 
    476 	case FW_SSTBUF:
    477 		memcpy(&d->bufreq, ibufreq, sizeof(d->bufreq));
    478 		break;
    479 
    480 	case FW_GSTBUF:
    481 		memset(&ibufreq->rx, 0, sizeof(ibufreq->rx));
    482 		if (ir != NULL) {
    483 			ibufreq->rx.nchunk = ir->bnchunk;
    484 			ibufreq->rx.npacket = ir->bnpacket;
    485 			ibufreq->rx.psize = ir->psize;
    486 		}
    487 		memset(&ibufreq->tx, 0, sizeof(ibufreq->tx));
    488 		if (it != NULL) {
    489 			ibufreq->tx.nchunk = it->bnchunk;
    490 			ibufreq->tx.npacket = it->bnpacket;
    491 			ibufreq->tx.psize = it->psize;
    492 		}
    493 		break;
    494 
    495 	case FW_ASYREQ:
    496 	{
    497 		const struct tcode_info *tinfo;
    498 		int pay_len = 0;
    499 
    500 		fp = &asyreq->pkt;
    501 		tinfo = &fc->tcode[fp->mode.hdr.tcode];
    502 
    503 		if ((tinfo->flag & FWTI_BLOCK_ASY) != 0)
    504 			pay_len = MAX(0, asyreq->req.len - tinfo->hdr_len);
    505 
    506 		xfer = fw_xfer_alloc_buf(M_FW, pay_len, PAGE_SIZE/*XXX*/);
    507 		if (xfer == NULL)
    508 			return ENOMEM;
    509 
    510 		switch (asyreq->req.type) {
    511 		case FWASREQNODE:
    512 			break;
    513 
    514 		case FWASREQEUI:
    515 			fwdev = fw_noderesolve_eui64(fc, &asyreq->req.dst.eui);
    516 			if (fwdev == NULL) {
    517 				aprint_error_dev(fc->bdev,
    518 				    "cannot find node\n");
    519 				err = EINVAL;
    520 				goto out;
    521 			}
    522 			fp->mode.hdr.dst = FWLOCALBUS | fwdev->dst;
    523 			break;
    524 
    525 		case FWASRESTL:
    526 			/* XXX what's this? */
    527 			break;
    528 
    529 		case FWASREQSTREAM:
    530 			/* nothing to do */
    531 			break;
    532 		}
    533 
    534 		memcpy(&xfer->send.hdr, fp, tinfo->hdr_len);
    535 		if (pay_len > 0)
    536 			memcpy(xfer->send.payload, (char *)fp + tinfo->hdr_len,
    537 			    pay_len);
    538 		xfer->send.spd = asyreq->req.sped;
    539 		xfer->hand = fw_xferwake;
    540 
    541 		if ((err = fw_asyreq(fc, -1, xfer)) != 0)
    542 			goto out;
    543 		if ((err = fw_xferwait(xfer)) != 0)
    544 			goto out;
    545 		if (xfer->resp != 0) {
    546 			err = EIO;
    547 			goto out;
    548 		}
    549 		if ((tinfo->flag & FWTI_TLABEL) == 0)
    550 			goto out;
    551 
    552 		/* copy response */
    553 		tinfo = &fc->tcode[xfer->recv.hdr.mode.hdr.tcode];
    554 		if (xfer->recv.hdr.mode.hdr.tcode == FWTCODE_RRESB ||
    555 		    xfer->recv.hdr.mode.hdr.tcode == FWTCODE_LRES) {
    556 			pay_len = xfer->recv.pay_len;
    557 			if (asyreq->req.len >=
    558 			    xfer->recv.pay_len + tinfo->hdr_len)
    559 				asyreq->req.len =
    560 				    xfer->recv.pay_len + tinfo->hdr_len;
    561 			else {
    562 				err = EINVAL;
    563 				pay_len = 0;
    564 			}
    565 		} else
    566 			pay_len = 0;
    567 		memcpy(fp, &xfer->recv.hdr, tinfo->hdr_len);
    568 		memcpy((char *)fp + tinfo->hdr_len, xfer->recv.payload,
    569 		    pay_len);
    570 out:
    571 		fw_xfer_free_buf(xfer);
    572 		break;
    573 	}
    574 
    575 	case FW_IBUSRST:
    576 		fc->ibr(fc);
    577 		break;
    578 
    579 	case FW_CBINDADDR:
    580 		fwb = fw_bindlookup(fc, bindreq->start.hi, bindreq->start.lo);
    581 		if (fwb == NULL) {
    582 			err = EINVAL;
    583 			break;
    584 		}
    585 		fw_bindremove(fc, fwb);
    586 		STAILQ_REMOVE(&d->binds, fwb, fw_bind, chlist);
    587 		fw_xferlist_remove(&fwb->xferlist);
    588 		free(fwb, M_FW);
    589 		break;
    590 
    591 	case FW_SBINDADDR:
    592 		if (bindreq->len <= 0 ) {
    593 			err = EINVAL;
    594 			break;
    595 		}
    596 		if (bindreq->start.hi > 0xffff ) {
    597 			err = EINVAL;
    598 			break;
    599 		}
    600 		fwb = (struct fw_bind *)malloc(sizeof(struct fw_bind),
    601 		    M_FW, M_WAITOK);
    602 		if (fwb == NULL) {
    603 			err = ENOMEM;
    604 			break;
    605 		}
    606 		fwb->start = ((u_int64_t)bindreq->start.hi << 32) |
    607 		    bindreq->start.lo;
    608 		fwb->end = fwb->start +  bindreq->len;
    609 		fwb->sc = (void *)d;
    610 		STAILQ_INIT(&fwb->xferlist);
    611 		err = fw_bindadd(fc, fwb);
    612 		if (err == 0) {
    613 			fw_xferlist_add(&fwb->xferlist, M_FW,
    614 			    /* XXX */
    615 			    PAGE_SIZE, PAGE_SIZE, 5, fc, (void *)fwb, fw_hand);
    616 			STAILQ_INSERT_TAIL(&d->binds, fwb, chlist);
    617 		}
    618 		break;
    619 
    620 	case FW_GDEVLST:
    621 		i = len = 1;
    622 		/* myself */
    623 		devinfo = fwdevlst->dev;
    624 		devinfo->dst = fc->nodeid;
    625 		devinfo->status = 0;	/* XXX */
    626 		devinfo->eui.hi = fc->eui.hi;
    627 		devinfo->eui.lo = fc->eui.lo;
    628 		STAILQ_FOREACH(fwdev, &fc->devices, link) {
    629 			if (len < FW_MAX_DEVLST) {
    630 				devinfo = &fwdevlst->dev[len++];
    631 				devinfo->dst = fwdev->dst;
    632 				devinfo->status =
    633 				    (fwdev->status == FWDEVINVAL) ? 0 : 1;
    634 				devinfo->eui.hi = fwdev->eui.hi;
    635 				devinfo->eui.lo = fwdev->eui.lo;
    636 			}
    637 			i++;
    638 		}
    639 		fwdevlst->n = i;
    640 		fwdevlst->info_len = len;
    641 		break;
    642 
    643 	case FW_GTPMAP:
    644 		memcpy(data, fc->topology_map,
    645 		    (fc->topology_map->crc_len + 1) * 4);
    646 		break;
    647 
    648 	case FW_GCROM:
    649 		STAILQ_FOREACH(fwdev, &fc->devices, link)
    650 			if (FW_EUI64_EQUAL(fwdev->eui, crom_buf->eui))
    651 				break;
    652 		if (fwdev == NULL) {
    653 			if (!FW_EUI64_EQUAL(fc->eui, crom_buf->eui)) {
    654 				err = FWNODE_INVAL;
    655 				break;
    656 			}
    657 			/* myself */
    658 			ptr = malloc(CROMSIZE, M_FW, M_WAITOK);
    659 			len = CROMSIZE;
    660 			for (i = 0; i < CROMSIZE/4; i++)
    661 				((uint32_t *)ptr)[i] = ntohl(fc->config_rom[i]);
    662 		} else {
    663 			/* found */
    664 			ptr = (void *)fwdev->csrrom;
    665 			if (fwdev->rommax < CSRROMOFF)
    666 				len = 0;
    667 			else
    668 				len = fwdev->rommax - CSRROMOFF + 4;
    669 		}
    670 		if (crom_buf->len < len)
    671 			len = crom_buf->len;
    672 		else
    673 			crom_buf->len = len;
    674 		err = copyout(ptr, crom_buf->ptr, len);
    675 		if (fwdev == NULL)
    676 			/* myself */
    677 			free(ptr, M_FW);
    678 		break;
    679 
    680 	default:
    681 		fc->ioctl(dev, cmd, data, flag, td);
    682 		break;
    683 	}
    684 	return err;
    685 }
    686 
    687 int
    688 fw_poll(dev_t dev, int events, struct lwp *td)
    689 {
    690 	struct firewire_softc *sc;
    691 	struct fw_xferq *ir;
    692 	int revents, tmp;
    693 
    694 	sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
    695 	if (sc == NULL)
    696 		return ENXIO;
    697 
    698 	ir = ((struct fw_drv1 *)sc->si_drv1)->ir;
    699 	revents = 0;
    700 	tmp = POLLIN | POLLRDNORM;
    701 	if (events & tmp) {
    702 		if (STAILQ_FIRST(&ir->q) != NULL)
    703 			revents |= tmp;
    704 		else
    705 			selrecord(td, &ir->rsel);
    706 	}
    707 	tmp = POLLOUT | POLLWRNORM;
    708 	if (events & tmp)
    709 		/* XXX should be fixed */
    710 		revents |= tmp;
    711 
    712 	return revents;
    713 }
    714 
    715 paddr_t
    716 fw_mmap(dev_t dev, off_t offset, int nproto)
    717 {
    718 	struct firewire_softc *sc;
    719 
    720 	sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
    721 	if (sc == NULL)
    722 		return ENXIO;
    723 
    724 	return EINVAL;
    725 }
    726 
    727 void
    728 fw_strategy(struct bio *bp)
    729 {
    730 	struct firewire_softc *sc;
    731 	dev_t dev = bp->bio_dev;
    732 
    733 	sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
    734 	if (sc == NULL)
    735 		return;
    736 
    737 	if (DEV_FWMEM(dev)) {
    738 		fwmem_strategy(bp);
    739 		return;
    740 	}
    741 
    742 	bp->bio_error = EOPNOTSUPP;
    743 	bp->bio_resid = bp->bio_bcount;
    744 	biodone(bp);
    745 }
    746 
    747 
    748 static int
    749 fwdev_allocbuf(struct firewire_comm *fc, struct fw_xferq *q,
    750 	       struct fw_bufspec *b)
    751 {
    752 	int i;
    753 
    754 	if (q->flag & (FWXFERQ_RUNNING | FWXFERQ_EXTBUF))
    755 		return EBUSY;
    756 
    757 	q->bulkxfer =
    758 	    (struct fw_bulkxfer *)malloc(sizeof(struct fw_bulkxfer) * b->nchunk,
    759 								M_FW, M_WAITOK);
    760 	if (q->bulkxfer == NULL)
    761 		return ENOMEM;
    762 
    763 	b->psize = roundup2(b->psize, sizeof(uint32_t));
    764 	q->buf = fwdma_malloc_multiseg(fc, sizeof(uint32_t), b->psize,
    765 	    b->nchunk * b->npacket, BUS_DMA_WAITOK);
    766 
    767 	if (q->buf == NULL) {
    768 		free(q->bulkxfer, M_FW);
    769 		q->bulkxfer = NULL;
    770 		return ENOMEM;
    771 	}
    772 	q->bnchunk = b->nchunk;
    773 	q->bnpacket = b->npacket;
    774 	q->psize = (b->psize + 3) & ~3;
    775 	q->queued = 0;
    776 
    777 	STAILQ_INIT(&q->stvalid);
    778 	STAILQ_INIT(&q->stfree);
    779 	STAILQ_INIT(&q->stdma);
    780 	q->stproc = NULL;
    781 
    782 	for (i = 0 ; i < q->bnchunk; i++) {
    783 		q->bulkxfer[i].poffset = i * q->bnpacket;
    784 		q->bulkxfer[i].mbuf = NULL;
    785 		STAILQ_INSERT_TAIL(&q->stfree, &q->bulkxfer[i], link);
    786 	}
    787 
    788 	q->flag &= ~FWXFERQ_MODEMASK;
    789 	q->flag |= FWXFERQ_STREAM;
    790 	q->flag |= FWXFERQ_EXTBUF;
    791 
    792 	return 0;
    793 }
    794 
    795 static int
    796 fwdev_freebuf(struct fw_xferq *q)
    797 {
    798 
    799 	if (q->flag & FWXFERQ_EXTBUF) {
    800 		if (q->buf != NULL)
    801 			fwdma_free_multiseg(q->buf);
    802 		q->buf = NULL;
    803 		free(q->bulkxfer, M_FW);
    804 		q->bulkxfer = NULL;
    805 		q->flag &= ~FWXFERQ_EXTBUF;
    806 		q->psize = 0;
    807 		q->maxq = FWMAXQUEUE;
    808 	}
    809 	return 0;
    810 }
    811 
    812 static int
    813 fw_read_async(struct fw_drv1 *d, struct uio *uio, int ioflag)
    814 {
    815 	struct fw_xfer *xfer;
    816 	struct fw_bind *fwb;
    817 	struct fw_pkt *fp;
    818 	const struct tcode_info *tinfo;
    819 	int err = 0;
    820 
    821 	mutex_enter(&d->fc->fc_mtx);
    822 
    823 	for (;;) {
    824 		xfer = STAILQ_FIRST(&d->rq);
    825 		if (xfer == NULL && err == 0) {
    826 			err = cv_wait_sig(&d->cv, &d->fc->fc_mtx);
    827 			if (err) {
    828 				mutex_exit(&d->fc->fc_mtx);
    829 				return err;
    830 			}
    831 			continue;
    832 		}
    833 		break;
    834 	}
    835 
    836 	STAILQ_REMOVE_HEAD(&d->rq, link);
    837 	mutex_exit(&d->fc->fc_mtx);
    838 	fp = &xfer->recv.hdr;
    839 #if 0 /* for GASP ?? */
    840 	if (fc->irx_post != NULL)
    841 		fc->irx_post(fc, fp->mode.ld);
    842 #endif
    843 	tinfo = &xfer->fc->tcode[fp->mode.hdr.tcode];
    844 	err = uiomove((void *)fp, tinfo->hdr_len, uio);
    845 	if (err)
    846 		goto out;
    847 	err = uiomove((void *)xfer->recv.payload, xfer->recv.pay_len, uio);
    848 
    849 out:
    850 	/* recycle this xfer */
    851 	fwb = (struct fw_bind *)xfer->sc;
    852 	fw_xfer_unload(xfer);
    853 	xfer->recv.pay_len = PAGE_SIZE;
    854 	mutex_enter(&d->fc->fc_mtx);
    855 	STAILQ_INSERT_TAIL(&fwb->xferlist, xfer, link);
    856 	mutex_exit(&d->fc->fc_mtx);
    857 	return err;
    858 }
    859 
    860 static int
    861 fw_write_async(struct fw_drv1 *d, struct uio *uio, int ioflag)
    862 {
    863 	struct fw_xfer *xfer;
    864 	struct fw_pkt pkt;
    865 	const struct tcode_info *tinfo;
    866 	int err;
    867 
    868 	memset(&pkt, 0, sizeof(struct fw_pkt));
    869 	if ((err = uiomove((void *)&pkt, sizeof(uint32_t), uio)))
    870 		return err;
    871 	tinfo = &d->fc->tcode[pkt.mode.hdr.tcode];
    872 	if ((err = uiomove((char *)&pkt + sizeof(uint32_t),
    873 	    tinfo->hdr_len - sizeof(uint32_t), uio)))
    874 		return err;
    875 
    876 	if ((xfer = fw_xfer_alloc_buf(M_FW, uio->uio_resid,
    877 	    PAGE_SIZE/*XXX*/)) == NULL)
    878 		return ENOMEM;
    879 
    880 	memcpy(&xfer->send.hdr, &pkt, sizeof(struct fw_pkt));
    881 	xfer->send.pay_len = uio->uio_resid;
    882 	if (uio->uio_resid > 0) {
    883 		if ((err =
    884 		    uiomove((void *)xfer->send.payload, uio->uio_resid, uio)))
    885 			goto out;
    886 	}
    887 
    888 	xfer->fc = d->fc;
    889 	xfer->sc = NULL;
    890 	xfer->hand = fw_xferwake;
    891 	xfer->send.spd = 2 /* XXX */;
    892 
    893 	if ((err = fw_asyreq(xfer->fc, -1, xfer)))
    894 		goto out;
    895 
    896 	if ((err = fw_xferwait(xfer)))
    897 		goto out;
    898 
    899 	if (xfer->resp != 0) {
    900 		err = xfer->resp;
    901 		goto out;
    902 	}
    903 
    904 	if (xfer->flag == FWXF_RCVD) {
    905 		mutex_enter(&xfer->fc->fc_mtx);
    906 		STAILQ_INSERT_TAIL(&d->rq, xfer, link);
    907 		mutex_exit(&xfer->fc->fc_mtx);
    908 		return 0;
    909 	}
    910 
    911 out:
    912 	fw_xfer_free(xfer);
    913 	return err;
    914 }
    915 
    916 static void
    917 fw_hand(struct fw_xfer *xfer)
    918 {
    919 	struct fw_bind *fwb;
    920 	struct fw_drv1 *d;
    921 
    922 	fwb = (struct fw_bind *)xfer->sc;
    923 	d = (struct fw_drv1 *)fwb->sc;
    924 	mutex_enter(&xfer->fc->fc_mtx);
    925 	STAILQ_INSERT_TAIL(&d->rq, xfer, link);
    926 	cv_broadcast(&d->cv);
    927 	mutex_exit(&xfer->fc->fc_mtx);
    928 }
    929