fwdev.c revision 121466
10Sduke/*
22362Sohair * Copyright (c) 2003 Hidetoshi Shimokawa
30Sduke * Copyright (c) 1998-2002 Katsushi Kobayashi and Hidetoshi Shimokawa
40Sduke * All rights reserved.
50Sduke *
60Sduke * Redistribution and use in source and binary forms, with or without
70Sduke * modification, are permitted provided that the following conditions
80Sduke * are met:
90Sduke * 1. Redistributions of source code must retain the above copyright
100Sduke *    notice, this list of conditions and the following disclaimer.
110Sduke * 2. Redistributions in binary form must reproduce the above copyright
120Sduke *    notice, this list of conditions and the following disclaimer in the
130Sduke *    documentation and/or other materials provided with the distribution.
140Sduke * 3. All advertising materials mentioning features or use of this software
150Sduke *    must display the acknowledgement as bellow:
160Sduke *
170Sduke *    This product includes software developed by K. Kobayashi and H. Shimokawa
180Sduke *
192362Sohair * 4. The name of the author may not be used to endorse or promote products
202362Sohair *    derived from this software without specific prior written permission.
212362Sohair *
220Sduke * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
230Sduke * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
240Sduke * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
250Sduke * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
260Sduke * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
270Sduke * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
280Sduke * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
290Sduke * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
300Sduke * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
310Sduke * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
320Sduke * POSSIBILITY OF SUCH DAMAGE.
330Sduke *
340Sduke * $FreeBSD: head/sys/dev/firewire/fwdev.c 121466 2003-10-24 13:55:51Z simokawa $
350Sduke *
360Sduke */
370Sduke
380Sduke#include <sys/param.h>
390Sduke#include <sys/systm.h>
400Sduke#include <sys/types.h>
410Sduke#include <sys/mbuf.h>
420Sduke#if __FreeBSD_version < 500000
430Sduke#include <sys/buf.h>
440Sduke#else
450Sduke#include <sys/bio.h>
460Sduke#endif
470Sduke
480Sduke#include <sys/kernel.h>
490Sduke#include <sys/malloc.h>
500Sduke#include <sys/conf.h>
510Sduke#include <sys/poll.h>
520Sduke
530Sduke#include <sys/bus.h>
540Sduke#include <sys/ctype.h>
550Sduke#include <machine/bus.h>
560Sduke
570Sduke#include <sys/ioccom.h>
580Sduke
590Sduke#include <dev/firewire/firewire.h>
600Sduke#include <dev/firewire/firewirereg.h>
610Sduke#include <dev/firewire/fwdma.h>
620Sduke#include <dev/firewire/fwmem.h>
630Sduke#include <dev/firewire/iec68113.h>
640Sduke
650Sduke#define CDEV_MAJOR 127
660Sduke#define	FWNODE_INVAL 0xffff
670Sduke
680Sdukestatic	d_open_t	fw_open;
690Sdukestatic	d_close_t	fw_close;
700Sdukestatic	d_ioctl_t	fw_ioctl;
710Sdukestatic	d_poll_t	fw_poll;
720Sdukestatic	d_read_t	fw_read;	/* for Isochronous packet */
730Sdukestatic	d_write_t	fw_write;
740Sdukestatic	d_mmap_t	fw_mmap;
750Sdukestatic	d_strategy_t	fw_strategy;
760Sduke
770Sdukestruct cdevsw firewire_cdevsw =
780Sduke{
790Sduke#if __FreeBSD_version >= 500104
800Sduke	.d_open =	fw_open,
810Sduke	.d_close =	fw_close,
820Sduke	.d_read =	fw_read,
830Sduke	.d_write =	fw_write,
840Sduke	.d_ioctl =	fw_ioctl,
850Sduke	.d_poll =	fw_poll,
860Sduke	.d_mmap =	fw_mmap,
870Sduke	.d_strategy =	fw_strategy,
880Sduke	.d_name =	"fw",
890Sduke	.d_maj =	CDEV_MAJOR,
900Sduke	.d_flags =	D_MEM
910Sduke#else
920Sduke	fw_open, fw_close, fw_read, fw_write, fw_ioctl,
930Sduke	fw_poll, fw_mmap, fw_strategy, "fw", CDEV_MAJOR,
940Sduke	nodump, nopsize, D_MEM, -1
950Sduke#endif
960Sduke};
970Sduke
980Sdukestruct fw_drv1 {
990Sduke	struct fw_xferq *ir;
1000Sduke	struct fw_xferq *it;
1010Sduke	struct fw_isobufreq bufreq;
1020Sduke};
1030Sduke
1040Sdukestatic int
1050Sdukefwdev_allocbuf(struct firewire_comm *fc, struct fw_xferq *q,
1060Sduke	struct fw_bufspec *b)
1070Sduke{
1080Sduke	int i;
1090Sduke
1100Sduke	if (q->flag & (FWXFERQ_RUNNING | FWXFERQ_EXTBUF))
1110Sduke		return(EBUSY);
1120Sduke
1130Sduke	q->bulkxfer = (struct fw_bulkxfer *) malloc(
1140Sduke		sizeof(struct fw_bulkxfer) * b->nchunk,
1150Sduke		M_FW, M_WAITOK);
1160Sduke	if (q->bulkxfer == NULL)
1170Sduke		return(ENOMEM);
1180Sduke
1190Sduke	b->psize = roundup2(b->psize, sizeof(u_int32_t));
1200Sduke	q->buf = fwdma_malloc_multiseg(fc, sizeof(u_int32_t),
1210Sduke			b->psize, b->nchunk * b->npacket, BUS_DMA_WAITOK);
1220Sduke
1230Sduke	if (q->buf == NULL) {
1240Sduke		free(q->bulkxfer, M_FW);
1250Sduke		q->bulkxfer = NULL;
1260Sduke		return(ENOMEM);
1270Sduke	}
1280Sduke	q->bnchunk = b->nchunk;
1290Sduke	q->bnpacket = b->npacket;
1300Sduke	q->psize = (b->psize + 3) & ~3;
1310Sduke	q->queued = 0;
1320Sduke
1330Sduke	STAILQ_INIT(&q->stvalid);
1340Sduke	STAILQ_INIT(&q->stfree);
1350Sduke	STAILQ_INIT(&q->stdma);
1360Sduke	q->stproc = NULL;
1370Sduke
1380Sduke	for(i = 0 ; i < q->bnchunk; i++){
1390Sduke		q->bulkxfer[i].poffset = i * q->bnpacket;
1400Sduke		q->bulkxfer[i].mbuf = NULL;
1410Sduke		STAILQ_INSERT_TAIL(&q->stfree, &q->bulkxfer[i], link);
1420Sduke	}
1430Sduke
1440Sduke	q->flag &= ~FWXFERQ_MODEMASK;
1450Sduke	q->flag |= FWXFERQ_STREAM;
1460Sduke	q->flag |= FWXFERQ_EXTBUF;
1470Sduke
1480Sduke	return (0);
1490Sduke}
1500Sduke
1510Sdukestatic int
1520Sdukefwdev_freebuf(struct fw_xferq *q)
1530Sduke{
1540Sduke	if (q->flag & FWXFERQ_EXTBUF) {
1550Sduke		if (q->buf != NULL)
1560Sduke			fwdma_free_multiseg(q->buf);
1570Sduke		q->buf = NULL;
1580Sduke		free(q->bulkxfer, M_FW);
1590Sduke		q->bulkxfer = NULL;
1600Sduke		q->flag &= ~FWXFERQ_EXTBUF;
1610Sduke		q->psize = 0;
1620Sduke		q->maxq = FWMAXQUEUE;
1630Sduke	}
1640Sduke	return (0);
1650Sduke}
1660Sduke
1670Sduke
1680Sdukestatic int
1690Sdukefw_open (dev_t dev, int flags, int fmt, fw_proc *td)
1700Sduke{
1710Sduke	int err = 0;
1720Sduke
1730Sduke	if (dev->si_drv1 != NULL)
1740Sduke		return (EBUSY);
1750Sduke
1760Sduke	if (DEV_FWMEM(dev))
1770Sduke		return fwmem_open(dev, flags, fmt, td);
1780Sduke
1790Sduke#if __FreeBSD_version >= 500000
1800Sduke	if ((dev->si_flags & SI_NAMED) == 0) {
1810Sduke		int unit = DEV2UNIT(dev);
1820Sduke		int sub = DEV2SUB(dev);
1830Sduke
1840Sduke		make_dev(&firewire_cdevsw, minor(dev),
1850Sduke			UID_ROOT, GID_OPERATOR, 0660,
1860Sduke			"fw%d.%d", unit, sub);
1870Sduke	}
1880Sduke#endif
1890Sduke
1900Sduke	dev->si_drv1 = malloc(sizeof(struct fw_drv1), M_FW, M_WAITOK | M_ZERO);
1910Sduke
1920Sduke	return err;
1930Sduke}
1940Sduke
1950Sdukestatic int
1960Sdukefw_close (dev_t dev, int flags, int fmt, fw_proc *td)
1970Sduke{
1980Sduke	struct firewire_softc *sc;
1990Sduke	struct firewire_comm *fc;
2000Sduke	struct fw_drv1 *d;
2010Sduke	int unit = DEV2UNIT(dev);
2020Sduke	struct fw_xfer *xfer;
2030Sduke	struct fw_bind *fwb;
2040Sduke	int err = 0;
2050Sduke
2060Sduke	if (DEV_FWMEM(dev))
2070Sduke		return fwmem_close(dev, flags, fmt, td);
2080Sduke
2090Sduke	sc = devclass_get_softc(firewire_devclass, unit);
2100Sduke	fc = sc->fc;
2110Sduke	d = (struct fw_drv1 *)dev->si_drv1;
2120Sduke
2130Sduke	if (d->ir != NULL) {
2140Sduke		struct fw_xferq *ir = d->ir;
2150Sduke
2160Sduke		if ((ir->flag & FWXFERQ_OPEN) == 0)
2170Sduke			return (EINVAL);
2180Sduke		if (ir->flag & FWXFERQ_RUNNING) {
2190Sduke			ir->flag &= ~FWXFERQ_RUNNING;
2200Sduke			fc->irx_disable(fc, ir->dmach);
2210Sduke		}
2220Sduke		/* free extbuf */
2230Sduke		fwdev_freebuf(ir);
2240Sduke		/* drain receiving buffer */
2250Sduke		for (xfer = STAILQ_FIRST(&ir->q);
2260Sduke			xfer != NULL; xfer = STAILQ_FIRST(&ir->q)) {
2270Sduke			ir->queued --;
2280Sduke			STAILQ_REMOVE_HEAD(&ir->q, link);
2290Sduke
2300Sduke			xfer->resp = 0;
2310Sduke			fw_xfer_done(xfer);
2320Sduke		}
2330Sduke		/* remove binding */
2340Sduke		for (fwb = STAILQ_FIRST(&ir->binds); fwb != NULL;
2350Sduke				fwb = STAILQ_FIRST(&ir->binds)) {
2360Sduke			STAILQ_REMOVE(&fc->binds, fwb, fw_bind, fclist);
2370Sduke			STAILQ_REMOVE_HEAD(&ir->binds, chlist);
2380Sduke			free(fwb, M_FW);
2390Sduke		}
2400Sduke		ir->flag &= ~(FWXFERQ_OPEN |
2410Sduke			FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK);
2420Sduke		d->ir = NULL;
2430Sduke
2440Sduke	}
2450Sduke	if (d->it != NULL) {
2460Sduke		struct fw_xferq *it = d->it;
2470Sduke
2480Sduke		if ((it->flag & FWXFERQ_OPEN) == 0)
2490Sduke			return (EINVAL);
2500Sduke		if (it->flag & FWXFERQ_RUNNING) {
2510Sduke			it->flag &= ~FWXFERQ_RUNNING;
2520Sduke			fc->itx_disable(fc, it->dmach);
2530Sduke		}
2540Sduke		/* free extbuf */
2550Sduke		fwdev_freebuf(it);
2560Sduke		it->flag &= ~(FWXFERQ_OPEN |
2570Sduke			FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK);
2580Sduke		d->it = NULL;
2590Sduke	}
2600Sduke	free(dev->si_drv1, M_FW);
2610Sduke	dev->si_drv1 = NULL;
2620Sduke
2630Sduke	return err;
2640Sduke}
2650Sduke
2660Sduke/*
2670Sduke * read request.
2680Sduke */
2690Sdukestatic int
2700Sdukefw_read (dev_t dev, struct uio *uio, int ioflag)
2710Sduke{
2720Sduke	struct firewire_softc *sc;
2730Sduke	struct fw_xferq *ir;
2740Sduke	struct fw_xfer *xfer;
2750Sduke	int err = 0, s, slept = 0;
2760Sduke	int unit = DEV2UNIT(dev);
2770Sduke	struct fw_pkt *fp;
2780Sduke
2790Sduke	if (DEV_FWMEM(dev))
2800Sduke		return physio(dev, uio, ioflag);
2810Sduke
2820Sduke	sc = devclass_get_softc(firewire_devclass, unit);
2830Sduke
2840Sduke	ir = ((struct fw_drv1 *)dev->si_drv1)->ir;
2850Sduke	if (ir == NULL || ir->buf == NULL)
2860Sduke		return (EIO);
2870Sduke
2880Sdukereadloop:
2890Sduke	xfer = STAILQ_FIRST(&ir->q);
2900Sduke	if (ir->stproc == NULL) {
2910Sduke		/* iso bulkxfer */
2920Sduke		ir->stproc = STAILQ_FIRST(&ir->stvalid);
2930Sduke		if (ir->stproc != NULL) {
2940Sduke			s = splfw();
2950Sduke			STAILQ_REMOVE_HEAD(&ir->stvalid, link);
2960Sduke			splx(s);
2970Sduke			ir->queued = 0;
2980Sduke		}
2990Sduke	}
3000Sduke	if (xfer == NULL && ir->stproc == NULL) {
3010Sduke		/* no data avaliable */
3020Sduke		if (slept == 0) {
3030Sduke			slept = 1;
3040Sduke			ir->flag |= FWXFERQ_WAKEUP;
3050Sduke			err = tsleep(ir, FWPRI, "fw_read", hz);
3060Sduke			ir->flag &= ~FWXFERQ_WAKEUP;
3070Sduke			if (err == 0)
3080Sduke				goto readloop;
3090Sduke		} else if (slept == 1)
3100Sduke			err = EIO;
3110Sduke		return err;
3120Sduke	} else if(xfer != NULL) {
3130Sduke#if 0 /* XXX broken */
3140Sduke		/* per packet mode or FWACT_CH bind?*/
3150Sduke		s = splfw();
3160Sduke		ir->queued --;
3170Sduke		STAILQ_REMOVE_HEAD(&ir->q, link);
3180Sduke		splx(s);
3190Sduke		fp = &xfer->recv.hdr;
3200Sduke		if (sc->fc->irx_post != NULL)
3210Sduke			sc->fc->irx_post(sc->fc, fp->mode.ld);
3220Sduke		err = uiomove((void *)fp, 1 /* XXX header size */, uio);
3230Sduke		/* XXX copy payload too */
3240Sduke		/* XXX we should recycle this xfer */
3250Sduke#endif
3260Sduke		fw_xfer_free( xfer);
3270Sduke	} else if(ir->stproc != NULL) {
3280Sduke		/* iso bulkxfer */
3290Sduke		fp = (struct fw_pkt *)fwdma_v_addr(ir->buf,
3300Sduke				ir->stproc->poffset + ir->queued);
3310Sduke		if(sc->fc->irx_post != NULL)
3320Sduke			sc->fc->irx_post(sc->fc, fp->mode.ld);
3330Sduke		if(fp->mode.stream.len == 0){
334			err = EIO;
335			return err;
336		}
337		err = uiomove((caddr_t)fp,
338			fp->mode.stream.len + sizeof(u_int32_t), uio);
339		ir->queued ++;
340		if(ir->queued >= ir->bnpacket){
341			s = splfw();
342			STAILQ_INSERT_TAIL(&ir->stfree, ir->stproc, link);
343			splx(s);
344			sc->fc->irx_enable(sc->fc, ir->dmach);
345			ir->stproc = NULL;
346		}
347		if (uio->uio_resid >= ir->psize) {
348			slept = -1;
349			goto readloop;
350		}
351	}
352	return err;
353}
354
355static int
356fw_write (dev_t dev, struct uio *uio, int ioflag)
357{
358	int err = 0;
359	struct firewire_softc *sc;
360	int unit = DEV2UNIT(dev);
361	int s, slept = 0;
362	struct fw_pkt *fp;
363	struct firewire_comm *fc;
364	struct fw_xferq *it;
365
366	if (DEV_FWMEM(dev))
367		return physio(dev, uio, ioflag);
368
369	sc = devclass_get_softc(firewire_devclass, unit);
370	fc = sc->fc;
371	it = ((struct fw_drv1 *)dev->si_drv1)->it;
372	if (it == NULL || it->buf == NULL)
373		return (EIO);
374isoloop:
375	if (it->stproc == NULL) {
376		it->stproc = STAILQ_FIRST(&it->stfree);
377		if (it->stproc != NULL) {
378			s = splfw();
379			STAILQ_REMOVE_HEAD(&it->stfree, link);
380			splx(s);
381			it->queued = 0;
382		} else if (slept == 0) {
383			slept = 1;
384			err = sc->fc->itx_enable(sc->fc, it->dmach);
385			if (err)
386				return err;
387			err = tsleep(it, FWPRI, "fw_write", hz);
388			if (err)
389				return err;
390			goto isoloop;
391		} else {
392			err = EIO;
393			return err;
394		}
395	}
396	fp = (struct fw_pkt *)fwdma_v_addr(it->buf,
397			it->stproc->poffset + it->queued);
398	err = uiomove((caddr_t)fp, sizeof(struct fw_isohdr), uio);
399	err = uiomove((caddr_t)fp->mode.stream.payload,
400				fp->mode.stream.len, uio);
401	it->queued ++;
402	if (it->queued >= it->bnpacket) {
403		s = splfw();
404		STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link);
405		splx(s);
406		it->stproc = NULL;
407		err = sc->fc->itx_enable(sc->fc, it->dmach);
408	}
409	if (uio->uio_resid >= sizeof(struct fw_isohdr)) {
410		slept = 0;
411		goto isoloop;
412	}
413	return err;
414}
415/*
416 * ioctl support.
417 */
418int
419fw_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td)
420{
421	struct firewire_softc *sc;
422	struct firewire_comm *fc;
423	struct fw_drv1 *d;
424	int unit = DEV2UNIT(dev);
425	int s, i, len, err = 0;
426	struct fw_device *fwdev;
427	struct fw_bind *fwb;
428	struct fw_xferq *ir, *it;
429	struct fw_xfer *xfer;
430	struct fw_pkt *fp;
431	struct fw_devinfo *devinfo;
432	void *ptr;
433
434	struct fw_devlstreq *fwdevlst = (struct fw_devlstreq *)data;
435	struct fw_asyreq *asyreq = (struct fw_asyreq *)data;
436	struct fw_isochreq *ichreq = (struct fw_isochreq *)data;
437	struct fw_isobufreq *ibufreq = (struct fw_isobufreq *)data;
438	struct fw_asybindreq *bindreq = (struct fw_asybindreq *)data;
439	struct fw_crom_buf *crom_buf = (struct fw_crom_buf *)data;
440
441	if (DEV_FWMEM(dev))
442		return fwmem_ioctl(dev, cmd, data, flag, td);
443
444	if (!data)
445		return(EINVAL);
446
447	sc = devclass_get_softc(firewire_devclass, unit);
448	fc = sc->fc;
449	d = (struct fw_drv1 *)dev->si_drv1;
450	ir = d->ir;
451	it = d->it;
452
453	switch (cmd) {
454	case FW_STSTREAM:
455		if (it == NULL) {
456			for (i = 0; i < fc->nisodma; i ++) {
457				it = fc->it[i];
458				if ((it->flag & FWXFERQ_OPEN) == 0)
459					 break;
460	                }
461			if (i >= fc->nisodma) {
462				err = EBUSY;
463				break;
464			}
465			err = fwdev_allocbuf(fc, it, &d->bufreq.tx);
466			if (err)
467				break;
468			it->flag |=  FWXFERQ_OPEN;
469		}
470		it->flag &= ~0xff;
471		it->flag |= (0x3f & ichreq->ch);
472		it->flag |= ((0x3 & ichreq->tag) << 6);
473		d->it = it;
474		break;
475	case FW_GTSTREAM:
476		if (it != NULL) {
477			ichreq->ch = it->flag & 0x3f;
478			ichreq->tag = it->flag >> 2 & 0x3;
479		} else
480			err = EINVAL;
481		break;
482	case FW_SRSTREAM:
483		if (ir == NULL) {
484			for (i = 0; i < fc->nisodma; i ++) {
485				ir = fc->ir[i];
486				if ((ir->flag & FWXFERQ_OPEN) == 0)
487					break;
488			}
489			if (i >= fc->nisodma) {
490				err = EBUSY;
491				break;
492			}
493			err = fwdev_allocbuf(fc, ir, &d->bufreq.rx);
494			if (err)
495				break;
496			ir->flag |=  FWXFERQ_OPEN;
497		}
498		ir->flag &= ~0xff;
499		ir->flag |= (0x3f & ichreq->ch);
500		ir->flag |= ((0x3 & ichreq->tag) << 6);
501		d->ir = ir;
502		err = fc->irx_enable(fc, ir->dmach);
503		break;
504	case FW_GRSTREAM:
505		if (d->ir != NULL) {
506			ichreq->ch = ir->flag & 0x3f;
507			ichreq->tag = ir->flag >> 2 & 0x3;
508		} else
509			err = EINVAL;
510		break;
511	case FW_SSTBUF:
512		bcopy(ibufreq, &d->bufreq, sizeof(d->bufreq));
513		break;
514	case FW_GSTBUF:
515		bzero(&ibufreq->rx, sizeof(ibufreq->rx));
516		if (ir != NULL) {
517			ibufreq->rx.nchunk = ir->bnchunk;
518			ibufreq->rx.npacket = ir->bnpacket;
519			ibufreq->rx.psize = ir->psize;
520		}
521		bzero(&ibufreq->tx, sizeof(ibufreq->tx));
522		if (it != NULL) {
523			ibufreq->tx.nchunk = it->bnchunk;
524			ibufreq->tx.npacket = it->bnpacket;
525			ibufreq->tx.psize = it->psize;
526		}
527		break;
528	case FW_ASYREQ:
529	{
530		struct tcode_info *tinfo;
531		int pay_len = 0;
532
533		fp = &asyreq->pkt;
534		tinfo = &sc->fc->tcode[fp->mode.hdr.tcode];
535
536		if ((tinfo->flag & FWTI_BLOCK_ASY) != 0)
537			pay_len = MAX(0, asyreq->req.len - tinfo->hdr_len);
538
539		xfer = fw_xfer_alloc_buf(M_FWXFER, pay_len, PAGE_SIZE/*XXX*/);
540		if (xfer == NULL)
541			return (ENOMEM);
542
543		switch (asyreq->req.type) {
544		case FWASREQNODE:
545			break;
546		case FWASREQEUI:
547			fwdev = fw_noderesolve_eui64(sc->fc,
548						&asyreq->req.dst.eui);
549			if (fwdev == NULL) {
550				device_printf(sc->fc->bdev,
551					"cannot find node\n");
552				err = EINVAL;
553				goto out;
554			}
555			fp->mode.hdr.dst = FWLOCALBUS | fwdev->dst;
556			break;
557		case FWASRESTL:
558			/* XXX what's this? */
559			break;
560		case FWASREQSTREAM:
561			/* nothing to do */
562			break;
563		}
564
565		bcopy(fp, (void *)&xfer->send.hdr, tinfo->hdr_len);
566		if (pay_len > 0)
567			bcopy((char *)fp + tinfo->hdr_len,
568			    (void *)&xfer->send.payload, pay_len);
569		xfer->send.spd = asyreq->req.sped;
570		xfer->act.hand = fw_asy_callback;
571
572		if ((err = fw_asyreq(sc->fc, -1, xfer)) != 0)
573			goto out;
574		if ((err = tsleep(xfer, FWPRI, "asyreq", hz)) != 0)
575			goto out;
576		if (xfer->resp != 0) {
577			err = EIO;
578			goto out;
579		}
580		if ((tinfo->flag & FWTI_TLABEL) == 0)
581			goto out;
582
583		/* copy response */
584		tinfo = &sc->fc->tcode[xfer->recv.hdr.mode.hdr.tcode];
585		if (asyreq->req.len >= xfer->recv.pay_len + tinfo->hdr_len)
586			asyreq->req.len = xfer->recv.pay_len;
587		else
588			err = EINVAL;
589		bcopy(&xfer->recv.hdr, fp, tinfo->hdr_len);
590		bcopy(xfer->recv.payload, (char *)fp + tinfo->hdr_len,
591		    MAX(0, asyreq->req.len - tinfo->hdr_len));
592out:
593		fw_xfer_free_buf(xfer);
594		break;
595	}
596	case FW_IBUSRST:
597		sc->fc->ibr(sc->fc);
598		break;
599	case FW_CBINDADDR:
600		fwb = fw_bindlookup(sc->fc,
601				bindreq->start.hi, bindreq->start.lo);
602		if(fwb == NULL){
603			err = EINVAL;
604			break;
605		}
606		STAILQ_REMOVE(&sc->fc->binds, fwb, fw_bind, fclist);
607		STAILQ_REMOVE(&ir->binds, fwb, fw_bind, chlist);
608		free(fwb, M_FW);
609		break;
610	case FW_SBINDADDR:
611		if(bindreq->len <= 0 ){
612			err = EINVAL;
613			break;
614		}
615		if(bindreq->start.hi > 0xffff ){
616			err = EINVAL;
617			break;
618		}
619		fwb = (struct fw_bind *)malloc(sizeof (struct fw_bind), M_FW, M_NOWAIT);
620		if(fwb == NULL){
621			err = ENOMEM;
622			break;
623		}
624		fwb->start = ((u_int64_t)bindreq->start.hi << 32) |
625		    bindreq->start.lo;
626		fwb->end = fwb->start +  bindreq->len;
627		/* XXX */
628		fwb->sub = ir->dmach;
629		fwb->act_type = FWACT_CH;
630
631		/* XXX alloc buf */
632		xfer = fw_xfer_alloc(M_FWXFER);
633		if(xfer == NULL){
634			err = ENOMEM;
635			return err;
636		}
637		xfer->fc = sc->fc;
638
639		s = splfw();
640		/* XXX broken. need multiple xfer */
641		STAILQ_INIT(&fwb->xferlist);
642		STAILQ_INSERT_TAIL(&fwb->xferlist, xfer, link);
643		splx(s);
644		err = fw_bindadd(sc->fc, fwb);
645		break;
646	case FW_GDEVLST:
647		i = len = 1;
648		/* myself */
649		devinfo = &fwdevlst->dev[0];
650		devinfo->dst = sc->fc->nodeid;
651		devinfo->status = 0;	/* XXX */
652		devinfo->eui.hi = sc->fc->eui.hi;
653		devinfo->eui.lo = sc->fc->eui.lo;
654		STAILQ_FOREACH(fwdev, &sc->fc->devices, link) {
655			if(len < FW_MAX_DEVLST){
656				devinfo = &fwdevlst->dev[len++];
657				devinfo->dst = fwdev->dst;
658				devinfo->status =
659					(fwdev->status == FWDEVINVAL)?0:1;
660				devinfo->eui.hi = fwdev->eui.hi;
661				devinfo->eui.lo = fwdev->eui.lo;
662			}
663			i++;
664		}
665		fwdevlst->n = i;
666		fwdevlst->info_len = len;
667		break;
668	case FW_GTPMAP:
669		bcopy(sc->fc->topology_map, data,
670				(sc->fc->topology_map->crc_len + 1) * 4);
671		break;
672	case FW_GCROM:
673		STAILQ_FOREACH(fwdev, &sc->fc->devices, link)
674			if (FW_EUI64_EQUAL(fwdev->eui, crom_buf->eui))
675				break;
676		if (fwdev == NULL) {
677			if (!FW_EUI64_EQUAL(sc->fc->eui, crom_buf->eui)) {
678				err = FWNODE_INVAL;
679				break;
680			}
681			/* myself */
682			ptr = malloc(CROMSIZE, M_FW, M_WAITOK);
683			len = CROMSIZE;
684			for (i = 0; i < CROMSIZE/4; i++)
685				((u_int32_t *)ptr)[i]
686					= ntohl(sc->fc->config_rom[i]);
687		} else {
688			/* found */
689			ptr = (void *)&fwdev->csrrom[0];
690			if (fwdev->rommax < CSRROMOFF)
691				len = 0;
692			else
693				len = fwdev->rommax - CSRROMOFF + 4;
694		}
695		if (crom_buf->len < len)
696			len = crom_buf->len;
697		else
698			crom_buf->len = len;
699		err = copyout(ptr, crom_buf->ptr, len);
700		if (fwdev == NULL)
701			/* myself */
702			free(ptr, M_FW);
703		break;
704	default:
705		sc->fc->ioctl (dev, cmd, data, flag, td);
706		break;
707	}
708	return err;
709}
710int
711fw_poll(dev_t dev, int events, fw_proc *td)
712{
713	struct firewire_softc *sc;
714	struct fw_xferq *ir;
715	int revents;
716	int tmp;
717	int unit = DEV2UNIT(dev);
718
719	if (DEV_FWMEM(dev))
720		return fwmem_poll(dev, events, td);
721
722	sc = devclass_get_softc(firewire_devclass, unit);
723	ir = ((struct fw_drv1 *)dev->si_drv1)->ir;
724	revents = 0;
725	tmp = POLLIN | POLLRDNORM;
726	if (events & tmp) {
727		if (STAILQ_FIRST(&ir->q) != NULL)
728			revents |= tmp;
729		else
730			selrecord(td, &ir->rsel);
731	}
732	tmp = POLLOUT | POLLWRNORM;
733	if (events & tmp) {
734		/* XXX should be fixed */
735		revents |= tmp;
736	}
737
738	return revents;
739}
740
741static int
742#if __FreeBSD_version < 500102
743fw_mmap (dev_t dev, vm_offset_t offset, int nproto)
744#else
745fw_mmap (dev_t dev, vm_offset_t offset, vm_paddr_t *paddr, int nproto)
746#endif
747{
748	struct firewire_softc *sc;
749	int unit = DEV2UNIT(dev);
750
751	if (DEV_FWMEM(dev))
752#if __FreeBSD_version < 500102
753		return fwmem_mmap(dev, offset, nproto);
754#else
755		return fwmem_mmap(dev, offset, paddr, nproto);
756#endif
757
758	sc = devclass_get_softc(firewire_devclass, unit);
759
760	return EINVAL;
761}
762
763static void
764fw_strategy(struct bio *bp)
765{
766	dev_t dev;
767
768	dev = bp->bio_dev;
769	if (DEV_FWMEM(dev)) {
770		fwmem_strategy(bp);
771		return;
772	}
773
774	bp->bio_error = EOPNOTSUPP;
775	bp->bio_flags |= BIO_ERROR;
776	bp->bio_resid = bp->bio_bcount;
777	biodone(bp);
778}
779
780int
781fwdev_makedev(struct firewire_softc *sc)
782{
783	int err = 0;
784
785#if __FreeBSD_version >= 500000
786	dev_t d;
787	int unit;
788
789	unit = device_get_unit(sc->fc->bdev);
790	sc->dev = make_dev(&firewire_cdevsw, MAKEMINOR(0, unit, 0),
791			UID_ROOT, GID_OPERATOR, 0660,
792			"fw%d.%d", unit, 0);
793	d = make_dev(&firewire_cdevsw,
794			MAKEMINOR(FWMEM_FLAG, unit, 0),
795			UID_ROOT, GID_OPERATOR, 0660,
796			"fwmem%d.%d", unit, 0);
797	dev_depends(sc->dev, d);
798	make_dev_alias(sc->dev, "fw%d", unit);
799	make_dev_alias(d, "fwmem%d", unit);
800#else
801	cdevsw_add(&firewire_cdevsw);
802#endif
803
804	return (err);
805}
806
807int
808fwdev_destroydev(struct firewire_softc *sc)
809{
810	int err = 0;
811
812#if __FreeBSD_version >= 500000
813	destroy_dev(sc->dev);
814#else
815	cdevsw_remove(&firewire_cdevsw);
816#endif
817	return (err);
818}
819
820#if __FreeBSD_version >= 500000
821#define NDEVTYPE 2
822void
823fwdev_clone(void *arg, char *name, int namelen, dev_t *dev)
824{
825	struct firewire_softc *sc;
826	char *devnames[NDEVTYPE] = {"fw", "fwmem"};
827	char *subp = NULL;
828	int devflag[NDEVTYPE] = {0, FWMEM_FLAG};
829	int i, unit = 0, sub = 0;
830
831	if (*dev != NODEV)
832		return;
833
834	for (i = 0; i < NDEVTYPE; i++)
835		if (dev_stdclone(name, &subp, devnames[i], &unit) == 2)
836			goto found;
837	/* not match */
838	return;
839found:
840
841	if (subp == NULL || *subp++ != '.')
842		return;
843
844	/* /dev/fwU.S */
845	while (isdigit(*subp)) {
846		sub *= 10;
847		sub += *subp++ - '0';
848	}
849	if (*subp != '\0')
850		return;
851
852	sc = devclass_get_softc(firewire_devclass, unit);
853	if (sc == NULL)
854		return;
855	*dev = make_dev(&firewire_cdevsw, MAKEMINOR(devflag[i], unit, sub),
856		       UID_ROOT, GID_OPERATOR, 0660,
857		       "%s%d.%d", devnames[i], unit, sub);
858	(*dev)->si_flags |= SI_CHEAPCLONE;
859	dev_depends(sc->dev, *dev);
860	return;
861}
862#endif
863