fwdev.c revision 144389
1139749Simp/*-
28471Sache * Copyright (c) 2003 Hidetoshi Shimokawa
38471Sache * Copyright (c) 1998-2002 Katsushi Kobayashi and Hidetoshi Shimokawa
4367457Sdim * All rights reserved.
5105806Sjhb *
68471Sache * Redistribution and use in source and binary forms, with or without
78471Sache * modification, are permitted provided that the following conditions
88471Sache * are met:
98471Sache * 1. Redistributions of source code must retain the above copyright
108471Sache *    notice, this list of conditions and the following disclaimer.
118471Sache * 2. Redistributions in binary form must reproduce the above copyright
128471Sache *    notice, this list of conditions and the following disclaimer in the
138471Sache *    documentation and/or other materials provided with the distribution.
148471Sache * 3. All advertising materials mentioning features or use of this software
158471Sache *    must display the acknowledgement as bellow:
168471Sache *
178471Sache *    This product includes software developed by K. Kobayashi and H. Shimokawa
188471Sache *
198471Sache * 4. The name of the author may not be used to endorse or promote products
208471Sache *    derived from this software without specific prior written permission.
218471Sache *
228471Sache * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
238471Sache * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
248471Sache * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
258471Sache * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
268471Sache * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2751654Sphk * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2851654Sphk * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
298471Sache * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
308471Sache * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
318471Sache * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
328471Sache * POSSIBILITY OF SUCH DAMAGE.
338471Sache *
348471Sache * $FreeBSD: head/sys/dev/firewire/fwdev.c 144389 2005-03-31 12:19:44Z phk $
358471Sache *
369232Sache */
378471Sache
38111899Sdas#include <sys/param.h>
39111899Sdas#include <sys/systm.h>
408471Sache#include <sys/types.h>
418471Sache#include <sys/mbuf.h>
42105806Sjhb#if defined(__DragonFly__) || __FreeBSD_version < 500000
438471Sache#include <sys/buf.h>
4424131Sbde#else
4538246Sbde#include <sys/bio.h>
46105806Sjhb#endif
47105806Sjhb
48129879Sphk#include <sys/kernel.h>
49131096Sphk#include <sys/malloc.h>
50105806Sjhb#include <sys/conf.h>
51105806Sjhb#include <sys/poll.h>
52105806Sjhb
53105806Sjhb#include <sys/bus.h>
5466824Sbde#include <sys/ctype.h>
55105806Sjhb#include <machine/bus.h>
56105806Sjhb
57105806Sjhb#include <sys/ioccom.h>
588471Sache
59105806Sjhb#ifdef __DragonFly__
608471Sache#include "firewire.h"
61105806Sjhb#include "firewirereg.h"
62105806Sjhb#include "fwdma.h"
638471Sache#include "fwmem.h"
64105806Sjhb#include "iec68113.h"
658471Sache#else
66105806Sjhb#include <dev/firewire/firewire.h>
67105806Sjhb#include <dev/firewire/firewirereg.h>
68105806Sjhb#include <dev/firewire/fwdma.h>
69105806Sjhb#include <dev/firewire/fwmem.h>
708471Sache#include <dev/firewire/iec68113.h>
719232Sache#endif
729232Sache
739232Sache#define	FWNODE_INVAL 0xffff
748471Sache
758471Sachestatic	d_open_t	fw_open;
768471Sachestatic	d_close_t	fw_close;
778471Sachestatic	d_ioctl_t	fw_ioctl;
788471Sachestatic	d_poll_t	fw_poll;
798471Sachestatic	d_read_t	fw_read;	/* for Isochronous packet */
80105806Sjhbstatic	d_write_t	fw_write;
81105806Sjhbstatic	d_mmap_t	fw_mmap;
82105806Sjhbstatic	d_strategy_t	fw_strategy;
83105806Sjhb
84105806Sjhbstruct cdevsw firewire_cdevsw = {
85105806Sjhb#ifdef __DragonFly__
86105806Sjhb#define CDEV_MAJOR 127
87105806Sjhb	"fw", CDEV_MAJOR, D_MEM, NULL, 0,
88105806Sjhb	fw_open, fw_close, fw_read, fw_write, fw_ioctl,
89105806Sjhb	fw_poll, fw_mmap, fw_strategy, nodump, nopsize,
90136210Sphk#elif __FreeBSD_version >= 500104
91105806Sjhb	.d_version =	D_VERSION,
92105806Sjhb	.d_open =	fw_open,
93105806Sjhb	.d_close =	fw_close,
94105806Sjhb	.d_read =	fw_read,
95105806Sjhb	.d_write =	fw_write,
96105806Sjhb	.d_ioctl =	fw_ioctl,
97105806Sjhb	.d_poll =	fw_poll,
98105806Sjhb	.d_mmap =	fw_mmap,
99105806Sjhb	.d_strategy =	fw_strategy,
1008471Sache	.d_name =	"fw",
101105806Sjhb	.d_flags =	D_MEM | D_NEEDGIANT
102105806Sjhb#else
103105806Sjhb#define CDEV_MAJOR 127
104105806Sjhb	fw_open, fw_close, fw_read, fw_write, fw_ioctl,
105105806Sjhb	fw_poll, fw_mmap, fw_strategy, "fw", CDEV_MAJOR,
106105806Sjhb	nodump, nopsize, D_MEM, -1
107105806Sjhb#endif
108105806Sjhb};
109105806Sjhb
110105806Sjhbstruct fw_drv1 {
111105806Sjhb	struct fw_xferq *ir;
112105806Sjhb	struct fw_xferq *it;
113105806Sjhb	struct fw_isobufreq bufreq;
114105806Sjhb};
1158471Sache
1168471Sachestatic int
117105806Sjhbfwdev_allocbuf(struct firewire_comm *fc, struct fw_xferq *q,
118136210Sphk	struct fw_bufspec *b)
119131373Sphk{
120105806Sjhb	int i;
121105806Sjhb
122105806Sjhb	if (q->flag & (FWXFERQ_RUNNING | FWXFERQ_EXTBUF))
123105806Sjhb		return(EBUSY);
124105806Sjhb
125131096Sphk	q->bulkxfer = (struct fw_bulkxfer *) malloc(
126105806Sjhb		sizeof(struct fw_bulkxfer) * b->nchunk,
127105806Sjhb		M_FW, M_WAITOK);
128105806Sjhb	if (q->bulkxfer == NULL)
129105806Sjhb		return(ENOMEM);
130105806Sjhb
131105806Sjhb	b->psize = roundup2(b->psize, sizeof(uint32_t));
132105806Sjhb	q->buf = fwdma_malloc_multiseg(fc, sizeof(uint32_t),
133105806Sjhb			b->psize, b->nchunk * b->npacket, BUS_DMA_WAITOK);
134105806Sjhb
135105806Sjhb	if (q->buf == NULL) {
136105806Sjhb		free(q->bulkxfer, M_FW);
1378471Sache		q->bulkxfer = NULL;
1388471Sache		return(ENOMEM);
1399232Sache	}
1409232Sache	q->bnchunk = b->nchunk;
1419232Sache	q->bnpacket = b->npacket;
1429232Sache	q->psize = (b->psize + 3) & ~3;
1439232Sache	q->queued = 0;
1449232Sache
1459232Sache	STAILQ_INIT(&q->stvalid);
1469232Sache	STAILQ_INIT(&q->stfree);
1479232Sache	STAILQ_INIT(&q->stdma);
1489232Sache	q->stproc = NULL;
1499232Sache
1509232Sache	for(i = 0 ; i < q->bnchunk; i++){
1518471Sache		q->bulkxfer[i].poffset = i * q->bnpacket;
1528471Sache		q->bulkxfer[i].mbuf = NULL;
1538471Sache		STAILQ_INSERT_TAIL(&q->stfree, &q->bulkxfer[i], link);
1548471Sache	}
1558471Sache
1568471Sache	q->flag &= ~FWXFERQ_MODEMASK;
1578471Sache	q->flag |= FWXFERQ_STREAM;
1588471Sache	q->flag |= FWXFERQ_EXTBUF;
1598471Sache
1608471Sache	return (0);
1618471Sache}
162105806Sjhb
163105806Sjhbstatic int
164105806Sjhbfwdev_freebuf(struct fw_xferq *q)
165105806Sjhb{
166105806Sjhb	if (q->flag & FWXFERQ_EXTBUF) {
16767551Sjhb		if (q->buf != NULL)
1688471Sache			fwdma_free_multiseg(q->buf);
1698471Sache		q->buf = NULL;
17012724Sphk		free(q->bulkxfer, M_FW);
171105806Sjhb		q->bulkxfer = NULL;
1728471Sache		q->flag &= ~FWXFERQ_EXTBUF;
173105806Sjhb		q->psize = 0;
174105806Sjhb		q->maxq = FWMAXQUEUE;
1758471Sache	}
176105806Sjhb	return (0);
177105806Sjhb}
178105806Sjhb
179105806Sjhb
180105806Sjhbstatic int
1819232Sachefw_open (struct cdev *dev, int flags, int fmt, fw_proc *td)
182105806Sjhb{
183105806Sjhb	int err = 0;
184105806Sjhb
185105806Sjhb	if (DEV_FWMEM(dev))
186105806Sjhb		return fwmem_open(dev, flags, fmt, td);
187105806Sjhb
188105806Sjhb	if (dev->si_drv1 != NULL)
189298307Spfg		return (EBUSY);
190105806Sjhb
191105806Sjhb#if defined(__FreeBSD__) && __FreeBSD_version >= 500000
192105806Sjhb	if ((dev->si_flags & SI_NAMED) == 0) {
193105806Sjhb		int unit = DEV2UNIT(dev);
194105806Sjhb		int sub = DEV2SUB(dev);
195105806Sjhb
196105806Sjhb		make_dev(&firewire_cdevsw, minor(dev),
197105806Sjhb			UID_ROOT, GID_OPERATOR, 0660,
198105806Sjhb			"fw%d.%d", unit, sub);
199105806Sjhb	}
200105806Sjhb#endif
201105806Sjhb
202105806Sjhb	dev->si_drv1 = malloc(sizeof(struct fw_drv1), M_FW, M_WAITOK | M_ZERO);
203105806Sjhb
204105806Sjhb	return err;
2058471Sache}
2068471Sache
20712724Sphkstatic int
208105806Sjhbfw_close (struct cdev *dev, int flags, int fmt, fw_proc *td)
2098471Sache{
210105806Sjhb	struct firewire_softc *sc;
211105806Sjhb	struct firewire_comm *fc;
212105806Sjhb	struct fw_drv1 *d;
213105806Sjhb	int unit = DEV2UNIT(dev);
214105806Sjhb	struct fw_xfer *xfer;
2158471Sache	struct fw_bind *fwb;
216105806Sjhb	int err = 0;
217105806Sjhb
21840565Sbde	if (DEV_FWMEM(dev))
219105806Sjhb		return fwmem_close(dev, flags, fmt, td);
220105806Sjhb
221105806Sjhb	sc = devclass_get_softc(firewire_devclass, unit);
222105806Sjhb	fc = sc->fc;
223105806Sjhb	d = (struct fw_drv1 *)dev->si_drv1;
224105806Sjhb
225105806Sjhb	if (d->ir != NULL) {
226105806Sjhb		struct fw_xferq *ir = d->ir;
227105806Sjhb
228105806Sjhb		if ((ir->flag & FWXFERQ_OPEN) == 0)
229105806Sjhb			return (EINVAL);
230105806Sjhb		if (ir->flag & FWXFERQ_RUNNING) {
231105806Sjhb			ir->flag &= ~FWXFERQ_RUNNING;
232105806Sjhb			fc->irx_disable(fc, ir->dmach);
233105806Sjhb		}
234105806Sjhb		/* free extbuf */
235105806Sjhb		fwdev_freebuf(ir);
236105806Sjhb		/* drain receiving buffer */
237105806Sjhb		for (xfer = STAILQ_FIRST(&ir->q);
238105806Sjhb			xfer != NULL; xfer = STAILQ_FIRST(&ir->q)) {
239105806Sjhb			ir->queued --;
240105806Sjhb			STAILQ_REMOVE_HEAD(&ir->q, link);
241105806Sjhb
242105806Sjhb			xfer->resp = 0;
243105806Sjhb			fw_xfer_done(xfer);
244105806Sjhb		}
245296137Sjhibbits		/* remove binding */
246296137Sjhibbits		for (fwb = STAILQ_FIRST(&ir->binds); fwb != NULL;
247105806Sjhb				fwb = STAILQ_FIRST(&ir->binds)) {
248105806Sjhb			STAILQ_REMOVE(&fc->binds, fwb, fw_bind, fclist);
249105806Sjhb			STAILQ_REMOVE_HEAD(&ir->binds, chlist);
250105806Sjhb			free(fwb, M_FW);
251105806Sjhb		}
252105806Sjhb		ir->flag &= ~(FWXFERQ_OPEN |
253105806Sjhb			FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK);
254105806Sjhb		d->ir = NULL;
255105806Sjhb
256105806Sjhb	}
257105806Sjhb	if (d->it != NULL) {
258105806Sjhb		struct fw_xferq *it = d->it;
259105806Sjhb
2608471Sache		if ((it->flag & FWXFERQ_OPEN) == 0)
261127135Snjl			return (EINVAL);
262127135Snjl		if (it->flag & FWXFERQ_RUNNING) {
263105806Sjhb			it->flag &= ~FWXFERQ_RUNNING;
264105806Sjhb			fc->itx_disable(fc, it->dmach);
265105806Sjhb		}
266105806Sjhb		/* free extbuf */
267105806Sjhb		fwdev_freebuf(it);
268105806Sjhb		it->flag &= ~(FWXFERQ_OPEN |
269105806Sjhb			FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK);
270105806Sjhb		d->it = NULL;
271105806Sjhb	}
272105806Sjhb	free(dev->si_drv1, M_FW);
273105806Sjhb	dev->si_drv1 = NULL;
274105806Sjhb
275105806Sjhb	return err;
276105806Sjhb}
277105806Sjhb
278105806Sjhb/*
279105806Sjhb * read request.
280105806Sjhb */
281105806Sjhbstatic int
282105806Sjhbfw_read (struct cdev *dev, struct uio *uio, int ioflag)
283105806Sjhb{
284105806Sjhb	struct firewire_softc *sc;
285105806Sjhb	struct fw_xferq *ir;
286105806Sjhb	struct fw_xfer *xfer;
287105806Sjhb	int err = 0, s, slept = 0;
2888471Sache	int unit = DEV2UNIT(dev);
289105806Sjhb	struct fw_pkt *fp;
2908471Sache
2918471Sache	if (DEV_FWMEM(dev))
2928471Sache		return physio(dev, uio, ioflag);
2938471Sache
2948471Sache	sc = devclass_get_softc(firewire_devclass, unit);
295105806Sjhb
296136210Sphk	ir = ((struct fw_drv1 *)dev->si_drv1)->ir;
297136210Sphk	if (ir == NULL || ir->buf == NULL)
298136210Sphk		return (EIO);
299136210Sphk
300136210Sphkreadloop:
301136210Sphk	xfer = STAILQ_FIRST(&ir->q);
302136210Sphk	if (ir->stproc == NULL) {
303136210Sphk		/* iso bulkxfer */
304151383Sphk		ir->stproc = STAILQ_FIRST(&ir->stvalid);
3058471Sache		if (ir->stproc != NULL) {
306105806Sjhb			s = splfw();
307166901Spiso			STAILQ_REMOVE_HEAD(&ir->stvalid, link);
308166901Spiso			splx(s);
309105806Sjhb			ir->queued = 0;
310105806Sjhb		}
311105806Sjhb	}
3128471Sache	if (xfer == NULL && ir->stproc == NULL) {
313105806Sjhb		/* no data avaliable */
314151700Sjhb		if (slept == 0) {
315105806Sjhb			slept = 1;
316105806Sjhb			ir->flag |= FWXFERQ_WAKEUP;
317105806Sjhb			err = tsleep(ir, FWPRI, "fw_read", hz);
318105806Sjhb			ir->flag &= ~FWXFERQ_WAKEUP;
319105806Sjhb			if (err == 0)
320105806Sjhb				goto readloop;
3218471Sache		} else if (slept == 1)
3228471Sache			err = EIO;
323105806Sjhb		return err;
324105806Sjhb	} else if(xfer != NULL) {
325105806Sjhb#if 0 /* XXX broken */
326105806Sjhb		/* per packet mode or FWACT_CH bind?*/
327105806Sjhb		s = splfw();
328131981Sphk		ir->queued --;
329105806Sjhb		STAILQ_REMOVE_HEAD(&ir->q, link);
330105806Sjhb		splx(s);
331105806Sjhb		fp = &xfer->recv.hdr;
332105806Sjhb		if (sc->fc->irx_post != NULL)
333136210Sphk			sc->fc->irx_post(sc->fc, fp->mode.ld);
334136210Sphk		err = uiomove((void *)fp, 1 /* XXX header size */, uio);
335105806Sjhb		/* XXX copy payload too */
336105806Sjhb		/* XXX we should recycle this xfer */
337105806Sjhb#endif
338105806Sjhb		fw_xfer_free( xfer);
339151700Sjhb	} else if(ir->stproc != NULL) {
340105806Sjhb		/* iso bulkxfer */
341105806Sjhb		fp = (struct fw_pkt *)fwdma_v_addr(ir->buf,
342105806Sjhb				ir->stproc->poffset + ir->queued);
343105806Sjhb		if(sc->fc->irx_post != NULL)
344105806Sjhb			sc->fc->irx_post(sc->fc, fp->mode.ld);
34540565Sbde		if(fp->mode.stream.len == 0){
346105806Sjhb			err = EIO;
3478471Sache			return err;
348105806Sjhb		}
349105806Sjhb		err = uiomove((caddr_t)fp,
3508471Sache			fp->mode.stream.len + sizeof(uint32_t), uio);
351105806Sjhb		ir->queued ++;
352105806Sjhb		if(ir->queued >= ir->bnpacket){
353105806Sjhb			s = splfw();
354105806Sjhb			STAILQ_INSERT_TAIL(&ir->stfree, ir->stproc, link);
355105806Sjhb			splx(s);
3569232Sache			sc->fc->irx_enable(sc->fc, ir->dmach);
357105806Sjhb			ir->stproc = NULL;
358105806Sjhb		}
359105806Sjhb		if (uio->uio_resid >= ir->psize) {
360105806Sjhb			slept = -1;
361105806Sjhb			goto readloop;
362105806Sjhb		}
363105806Sjhb	}
3648471Sache	return err;
365105806Sjhb}
366105806Sjhb
367105806Sjhbstatic int
368105806Sjhbfw_write (struct cdev *dev, struct uio *uio, int ioflag)
369105806Sjhb{
370105806Sjhb	int err = 0;
371105806Sjhb	struct firewire_softc *sc;
372105806Sjhb	int unit = DEV2UNIT(dev);
373105806Sjhb	int s, slept = 0;
3748471Sache	struct fw_pkt *fp;
375105806Sjhb	struct firewire_comm *fc;
376105806Sjhb	struct fw_xferq *it;
3779232Sache
378105806Sjhb	if (DEV_FWMEM(dev))
379105806Sjhb		return physio(dev, uio, ioflag);
3809232Sache
3819232Sache	sc = devclass_get_softc(firewire_devclass, unit);
3829232Sache	fc = sc->fc;
3839232Sache	it = ((struct fw_drv1 *)dev->si_drv1)->it;
3849232Sache	if (it == NULL || it->buf == NULL)
385105806Sjhb		return (EIO);
3869232Sacheisoloop:
3879232Sache	if (it->stproc == NULL) {
3889232Sache		it->stproc = STAILQ_FIRST(&it->stfree);
3899232Sache		if (it->stproc != NULL) {
3908471Sache			s = splfw();
3919232Sache			STAILQ_REMOVE_HEAD(&it->stfree, link);
392105806Sjhb			splx(s);
393105806Sjhb			it->queued = 0;
394105806Sjhb		} else if (slept == 0) {
395105806Sjhb			slept = 1;
3969232Sache			err = sc->fc->itx_enable(sc->fc, it->dmach);
3978471Sache			if (err)
3989232Sache				return err;
399105806Sjhb			err = tsleep(it, FWPRI, "fw_write", hz);
4009232Sache			if (err)
4019232Sache				return err;
402105806Sjhb			goto isoloop;
403105806Sjhb		} else {
4049232Sache			err = EIO;
4059232Sache			return err;
406105806Sjhb		}
407105806Sjhb	}
408136210Sphk	fp = (struct fw_pkt *)fwdma_v_addr(it->buf,
4099232Sache			it->stproc->poffset + it->queued);
4109232Sache	err = uiomove((caddr_t)fp, sizeof(struct fw_isohdr), uio);
4119232Sache	err = uiomove((caddr_t)fp->mode.stream.payload,
4129232Sache				fp->mode.stream.len, uio);
4139232Sache	it->queued ++;
4149232Sache	if (it->queued >= it->bnpacket) {
4159232Sache		s = splfw();
4169232Sache		STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link);
4179232Sache		splx(s);
418105806Sjhb		it->stproc = NULL;
4199232Sache		err = sc->fc->itx_enable(sc->fc, it->dmach);
4209232Sache	}
421105806Sjhb	if (uio->uio_resid >= sizeof(struct fw_isohdr)) {
4229232Sache		slept = 0;
4239232Sache		goto isoloop;
424105806Sjhb	}
4259232Sache	return err;
4268471Sache}
4279232Sache/*
4289232Sache * ioctl support.
4299232Sache */
4309232Sacheint
4319232Sachefw_ioctl (struct cdev *dev, u_long cmd, caddr_t data, int flag, fw_proc *td)
4329232Sache{
4339232Sache	struct firewire_softc *sc;
4349232Sache	struct firewire_comm *fc;
435105806Sjhb	struct fw_drv1 *d;
4369232Sache	int unit = DEV2UNIT(dev);
4378471Sache	int s, i, len, err = 0;
4389232Sache	struct fw_device *fwdev;
4399232Sache	struct fw_bind *fwb;
4409232Sache	struct fw_xferq *ir, *it;
4419232Sache	struct fw_xfer *xfer;
442105806Sjhb	struct fw_pkt *fp;
4439232Sache	struct fw_devinfo *devinfo;
4449232Sache	void *ptr;
4459232Sache
446105806Sjhb	struct fw_devlstreq *fwdevlst = (struct fw_devlstreq *)data;
447136210Sphk	struct fw_asyreq *asyreq = (struct fw_asyreq *)data;
448105806Sjhb	struct fw_isochreq *ichreq = (struct fw_isochreq *)data;
4498471Sache	struct fw_isobufreq *ibufreq = (struct fw_isobufreq *)data;
4509232Sache	struct fw_asybindreq *bindreq = (struct fw_asybindreq *)data;
4519232Sache	struct fw_crom_buf *crom_buf = (struct fw_crom_buf *)data;
4529232Sache
453105806Sjhb	if (DEV_FWMEM(dev))
4549232Sache		return fwmem_ioctl(dev, cmd, data, flag, td);
4559232Sache
4569232Sache	if (!data)
4579232Sache		return(EINVAL);
4589232Sache
459105806Sjhb	sc = devclass_get_softc(firewire_devclass, unit);
4609232Sache	fc = sc->fc;
461105806Sjhb	d = (struct fw_drv1 *)dev->si_drv1;
4629232Sache	ir = d->ir;
4639232Sache	it = d->it;
4649232Sache
4659232Sache	switch (cmd) {
4669232Sache	case FW_STSTREAM:
4679232Sache		if (it == NULL) {
46846704Speter			for (i = 0; i < fc->nisodma; i ++) {
469136210Sphk				it = fc->it[i];
4709232Sache				if ((it->flag & FWXFERQ_OPEN) == 0)
471136210Sphk					 break;
4729232Sache	                }
4739232Sache			if (i >= fc->nisodma) {
4749232Sache				err = EBUSY;
47546704Speter				break;
476136210Sphk			}
4779232Sache			err = fwdev_allocbuf(fc, it, &d->bufreq.tx);
478136210Sphk			if (err)
479105806Sjhb				break;
4809232Sache			it->flag |=  FWXFERQ_OPEN;
4819232Sache		}
4829232Sache		it->flag &= ~0xff;
483105806Sjhb		it->flag |= (0x3f & ichreq->ch);
4849232Sache		it->flag |= ((0x3 & ichreq->tag) << 6);
4859232Sache		d->it = it;
4868471Sache		break;
4879232Sache	case FW_GTSTREAM:
4889232Sache		if (it != NULL) {
4899232Sache			ichreq->ch = it->flag & 0x3f;
4909232Sache			ichreq->tag = it->flag >> 2 & 0x3;
4919232Sache		} else
4929232Sache			err = EINVAL;
4939232Sache		break;
494105806Sjhb	case FW_SRSTREAM:
4959232Sache		if (ir == NULL) {
4969232Sache			for (i = 0; i < fc->nisodma; i ++) {
4979232Sache				ir = fc->ir[i];
4989232Sache				if ((ir->flag & FWXFERQ_OPEN) == 0)
499105806Sjhb					break;
5008471Sache			}
5019232Sache			if (i >= fc->nisodma) {
5028471Sache				err = EBUSY;
5039232Sache				break;
504105806Sjhb			}
5059232Sache			err = fwdev_allocbuf(fc, ir, &d->bufreq.rx);
506105806Sjhb			if (err)
507105806Sjhb				break;
5089232Sache			ir->flag |=  FWXFERQ_OPEN;
5099232Sache		}
510105806Sjhb		ir->flag &= ~0xff;
511105806Sjhb		ir->flag |= (0x3f & ichreq->ch);
512105806Sjhb		ir->flag |= ((0x3 & ichreq->tag) << 6);
513105806Sjhb		d->ir = ir;
514105806Sjhb		err = fc->irx_enable(fc, ir->dmach);
5158471Sache		break;
5169232Sache	case FW_GRSTREAM:
5178471Sache		if (d->ir != NULL) {
5189232Sache			ichreq->ch = ir->flag & 0x3f;
5199232Sache			ichreq->tag = ir->flag >> 2 & 0x3;
5209232Sache		} else
5219232Sache			err = EINVAL;
5229232Sache		break;
5239232Sache	case FW_SSTBUF:
5248471Sache		bcopy(ibufreq, &d->bufreq, sizeof(d->bufreq));
5259232Sache		break;
526105806Sjhb	case FW_GSTBUF:
5279232Sache		bzero(&ibufreq->rx, sizeof(ibufreq->rx));
528105806Sjhb		if (ir != NULL) {
5299232Sache			ibufreq->rx.nchunk = ir->bnchunk;
5309232Sache			ibufreq->rx.npacket = ir->bnpacket;
5318471Sache			ibufreq->rx.psize = ir->psize;
5329232Sache		}
533105806Sjhb		bzero(&ibufreq->tx, sizeof(ibufreq->tx));
5349232Sache		if (it != NULL) {
535105806Sjhb			ibufreq->tx.nchunk = it->bnchunk;
536105806Sjhb			ibufreq->tx.npacket = it->bnpacket;
5379232Sache			ibufreq->tx.psize = it->psize;
5389232Sache		}
539105806Sjhb		break;
540105806Sjhb	case FW_ASYREQ:
5419232Sache	{
5429232Sache		struct tcode_info *tinfo;
5439232Sache		int pay_len = 0;
5449232Sache
5459232Sache		fp = &asyreq->pkt;
5469232Sache		tinfo = &sc->fc->tcode[fp->mode.hdr.tcode];
547105806Sjhb
548105806Sjhb		if ((tinfo->flag & FWTI_BLOCK_ASY) != 0)
549105806Sjhb			pay_len = MAX(0, asyreq->req.len - tinfo->hdr_len);
550105806Sjhb
5519232Sache		xfer = fw_xfer_alloc_buf(M_FWXFER, pay_len, PAGE_SIZE/*XXX*/);
5529232Sache		if (xfer == NULL)
5539232Sache			return (ENOMEM);
5549232Sache
5559232Sache		switch (asyreq->req.type) {
5569232Sache		case FWASREQNODE:
5579232Sache			break;
5589232Sache		case FWASREQEUI:
559105806Sjhb			fwdev = fw_noderesolve_eui64(sc->fc,
5609232Sache						&asyreq->req.dst.eui);
5618471Sache			if (fwdev == NULL) {
5629232Sache				device_printf(sc->fc->bdev,
5639232Sache					"cannot find node\n");
564105806Sjhb				err = EINVAL;
5658471Sache				goto out;
566105806Sjhb			}
567105806Sjhb			fp->mode.hdr.dst = FWLOCALBUS | fwdev->dst;
568105806Sjhb			break;
5698471Sache		case FWASRESTL:
5709232Sache			/* XXX what's this? */
571105806Sjhb			break;
5729232Sache		case FWASREQSTREAM:
573105806Sjhb			/* nothing to do */
5749232Sache			break;
5759232Sache		}
5768471Sache
5779232Sache		bcopy(fp, (void *)&xfer->send.hdr, tinfo->hdr_len);
578105806Sjhb		if (pay_len > 0)
579105806Sjhb			bcopy((char *)fp + tinfo->hdr_len,
580105806Sjhb			    (void *)xfer->send.payload, pay_len);
5818471Sache		xfer->send.spd = asyreq->req.sped;
5828471Sache		xfer->act.hand = fw_asy_callback;
5838471Sache
5848471Sache		if ((err = fw_asyreq(sc->fc, -1, xfer)) != 0)
585105806Sjhb			goto out;
586105806Sjhb		if ((err = tsleep(xfer, FWPRI, "asyreq", hz)) != 0)
5878471Sache			goto out;
588105806Sjhb		if (xfer->resp != 0) {
589105806Sjhb			err = EIO;
590105806Sjhb			goto out;
5918471Sache		}
592136210Sphk		if ((tinfo->flag & FWTI_TLABEL) == 0)
5938471Sache			goto out;
5948471Sache
595105806Sjhb		/* copy response */
5968471Sache		tinfo = &sc->fc->tcode[xfer->recv.hdr.mode.hdr.tcode];
5978471Sache		if (xfer->recv.hdr.mode.hdr.tcode == FWTCODE_RRESB ||
598106653Sjhb		    xfer->recv.hdr.mode.hdr.tcode == FWTCODE_LRES) {
5998471Sache			pay_len = xfer->recv.pay_len;
6008471Sache			if (asyreq->req.len >= xfer->recv.pay_len + tinfo->hdr_len) {
6018471Sache				asyreq->req.len = xfer->recv.pay_len +
6028471Sache					tinfo->hdr_len;
6038471Sache			} else {
6049232Sache				err = EINVAL;
6059232Sache				pay_len = 0;
6069232Sache			}
6079232Sache		} else {
608105806Sjhb			pay_len = 0;
609105806Sjhb		}
6109232Sache		bcopy(&xfer->recv.hdr, fp, tinfo->hdr_len);
611105806Sjhb		bcopy(xfer->recv.payload, (char *)fp + tinfo->hdr_len, pay_len);
612105806Sjhbout:
6138471Sache		fw_xfer_free_buf(xfer);
614106653Sjhb		break;
6158471Sache	}
6168471Sache	case FW_IBUSRST:
6178471Sache		sc->fc->ibr(sc->fc);
6188471Sache		break;
6198471Sache	case FW_CBINDADDR:
6209626Sbde		fwb = fw_bindlookup(sc->fc,
6218471Sache				bindreq->start.hi, bindreq->start.lo);
6229232Sache		if(fwb == NULL){
6238471Sache			err = EINVAL;
6248471Sache			break;
6259232Sache		}
626105806Sjhb		STAILQ_REMOVE(&sc->fc->binds, fwb, fw_bind, fclist);
6278471Sache		STAILQ_REMOVE(&ir->binds, fwb, fw_bind, chlist);
6288471Sache		free(fwb, M_FW);
6298471Sache		break;
6308471Sache	case FW_SBINDADDR:
6318471Sache		if(bindreq->len <= 0 ){
6328471Sache			err = EINVAL;
633106653Sjhb			break;
6348471Sache		}
6359232Sache		if(bindreq->start.hi > 0xffff ){
636106653Sjhb			err = EINVAL;
6379232Sache			break;
6388471Sache		}
639105806Sjhb		fwb = (struct fw_bind *)malloc(sizeof (struct fw_bind), M_FW, M_NOWAIT);
640105806Sjhb		if(fwb == NULL){
6418471Sache			err = ENOMEM;
642105806Sjhb			break;
643105806Sjhb		}
6448471Sache		fwb->start = ((u_int64_t)bindreq->start.hi << 32) |
6458471Sache		    bindreq->start.lo;
6468471Sache		fwb->end = fwb->start +  bindreq->len;
6478471Sache		/* XXX */
6488471Sache		fwb->sub = ir->dmach;
6498471Sache		fwb->act_type = FWACT_CH;
6508471Sache
6518471Sache		/* XXX alloc buf */
652105806Sjhb		xfer = fw_xfer_alloc(M_FWXFER);
653105806Sjhb		if(xfer == NULL){
6548471Sache			free(fwb, M_FW);
655105806Sjhb			return (ENOMEM);
656105806Sjhb		}
657105806Sjhb		xfer->fc = sc->fc;
658105806Sjhb
659105806Sjhb		s = splfw();
6608471Sache		/* XXX broken. need multiple xfer */
661105806Sjhb		STAILQ_INIT(&fwb->xferlist);
662105806Sjhb		STAILQ_INSERT_TAIL(&fwb->xferlist, xfer, link);
6638471Sache		splx(s);
664105806Sjhb		err = fw_bindadd(sc->fc, fwb);
665105806Sjhb		break;
6668471Sache	case FW_GDEVLST:
667136210Sphk		i = len = 1;
6688471Sache		/* myself */
6698471Sache		devinfo = &fwdevlst->dev[0];
6708471Sache		devinfo->dst = sc->fc->nodeid;
6718471Sache		devinfo->status = 0;	/* XXX */
6728471Sache		devinfo->eui.hi = sc->fc->eui.hi;
6738471Sache		devinfo->eui.lo = sc->fc->eui.lo;
674106653Sjhb		STAILQ_FOREACH(fwdev, &sc->fc->devices, link) {
6758471Sache			if(len < FW_MAX_DEVLST){
676105806Sjhb				devinfo = &fwdevlst->dev[len++];
677106653Sjhb				devinfo->dst = fwdev->dst;
678105806Sjhb				devinfo->status =
679105806Sjhb					(fwdev->status == FWDEVINVAL)?0:1;
680105806Sjhb				devinfo->eui.hi = fwdev->eui.hi;
6818471Sache				devinfo->eui.lo = fwdev->eui.lo;
6828471Sache			}
683106653Sjhb			i++;
6848471Sache		}
685105806Sjhb		fwdevlst->n = i;
686106653Sjhb		fwdevlst->info_len = len;
687105806Sjhb		break;
688105806Sjhb	case FW_GTPMAP:
6898471Sache		bcopy(sc->fc->topology_map, data,
6908471Sache				(sc->fc->topology_map->crc_len + 1) * 4);
691106653Sjhb		break;
6928471Sache	case FW_GCROM:
693105806Sjhb		STAILQ_FOREACH(fwdev, &sc->fc->devices, link)
694106653Sjhb			if (FW_EUI64_EQUAL(fwdev->eui, crom_buf->eui))
695130077Sphk				break;
6968471Sache		if (fwdev == NULL) {
6978471Sache			if (!FW_EUI64_EQUAL(sc->fc->eui, crom_buf->eui)) {
698106653Sjhb				err = FWNODE_INVAL;
6998471Sache				break;
7008471Sache			}
7018471Sache			/* myself */
7028471Sache			ptr = malloc(CROMSIZE, M_FW, M_WAITOK);
7038471Sache			len = CROMSIZE;
7048471Sache			for (i = 0; i < CROMSIZE/4; i++)
7058471Sache				((uint32_t *)ptr)[i]
7068471Sache					= ntohl(sc->fc->config_rom[i]);
7078471Sache		} else {
7088471Sache			/* found */
7098471Sache			ptr = (void *)&fwdev->csrrom[0];
7108471Sache			if (fwdev->rommax < CSRROMOFF)
7118471Sache				len = 0;
7128471Sache			else
7138471Sache				len = fwdev->rommax - CSRROMOFF + 4;
7148471Sache		}
7158471Sache		if (crom_buf->len < len)
7168471Sache			len = crom_buf->len;
7179232Sache		else
7189232Sache			crom_buf->len = len;
7199232Sache		err = copyout(ptr, crom_buf->ptr, len);
7208471Sache		if (fwdev == NULL)
7219232Sache			/* myself */
722105806Sjhb			free(ptr, M_FW);
723105806Sjhb		break;
7248471Sache	default:
7258471Sache		sc->fc->ioctl (dev, cmd, data, flag, td);
726105806Sjhb		break;
7278471Sache	}
728106653Sjhb	return err;
7298471Sache}
7309232Sacheint
7318471Sachefw_poll(struct cdev *dev, int events, fw_proc *td)
7328471Sache{
7338471Sache	struct firewire_softc *sc;
7348471Sache	struct fw_xferq *ir;
7359822Sbde	int revents;
7369822Sbde	int tmp;
7379822Sbde	int unit = DEV2UNIT(dev);
7389822Sbde
7398471Sache	if (DEV_FWMEM(dev))
7408471Sache		return fwmem_poll(dev, events, td);
7418471Sache
7428471Sache	sc = devclass_get_softc(firewire_devclass, unit);
743105806Sjhb	ir = ((struct fw_drv1 *)dev->si_drv1)->ir;
744105806Sjhb	revents = 0;
745105806Sjhb	tmp = POLLIN | POLLRDNORM;
7468471Sache	if (events & tmp) {
7478471Sache		if (STAILQ_FIRST(&ir->q) != NULL)
7488471Sache			revents |= tmp;
7498471Sache		else
7508471Sache			selrecord(td, &ir->rsel);
7519754Sbde	}
7528471Sache	tmp = POLLOUT | POLLWRNORM;
7538471Sache	if (events & tmp) {
7548471Sache		/* XXX should be fixed */
755130095Sphk		revents |= tmp;
7568471Sache	}
757130095Sphk
7588471Sache	return revents;
75927125Sbde}
7608471Sache
7618471Sachestatic int
762106653Sjhb#if defined(__DragonFly__) || __FreeBSD_version < 500102
763105806Sjhbfw_mmap (struct cdev *dev, vm_offset_t offset, int nproto)
7649232Sache#else
765136210Sphkfw_mmap (struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nproto)
766106653Sjhb#endif
767130077Sphk{
7688471Sache	struct firewire_softc *sc;
769111564Sjhb	int unit = DEV2UNIT(dev);
770111564Sjhb
7718471Sache	if (DEV_FWMEM(dev))
772111564Sjhb#if defined(__DragonFly__) || __FreeBSD_version < 500102
7738471Sache		return fwmem_mmap(dev, offset, nproto);
7748471Sache#else
775105806Sjhb		return fwmem_mmap(dev, offset, paddr, nproto);
776105806Sjhb#endif
7778471Sache
778105806Sjhb	sc = devclass_get_softc(firewire_devclass, unit);
779105806Sjhb
7808471Sache	return EINVAL;
7818471Sache}
782136210Sphk
783105806Sjhbstatic void
7848471Sachefw_strategy(struct bio *bp)
785105806Sjhb{
786105806Sjhb	struct cdev *dev;
7878471Sache
7888471Sache	dev = bp->bio_dev;
7898471Sache	if (DEV_FWMEM(dev)) {
790106653Sjhb		fwmem_strategy(bp);
7918471Sache		return;
7929232Sache	}
7938471Sache
7948471Sache	bp->bio_error = EOPNOTSUPP;
7958471Sache	bp->bio_flags |= BIO_ERROR;
7968471Sache	bp->bio_resid = bp->bio_bcount;
7978471Sache	biodone(bp);
7988471Sache}
7998471Sache
8008471Sacheint
801105806Sjhbfwdev_makedev(struct firewire_softc *sc)
8028471Sache{
8038471Sache	int err = 0;
8048471Sache
8058471Sache#if defined(__DragonFly__) || __FreeBSD_version < 500000
8068471Sache	cdevsw_add(&firewire_cdevsw);
807106653Sjhb#else
8088471Sache	struct cdev *d;
8098471Sache	int unit;
810136210Sphk
811136210Sphk	unit = device_get_unit(sc->fc->bdev);
8128471Sache	sc->dev = make_dev(&firewire_cdevsw, MAKEMINOR(0, unit, 0),
813105806Sjhb			UID_ROOT, GID_OPERATOR, 0660,
814105806Sjhb			"fw%d.%d", unit, 0);
815105806Sjhb	d = make_dev(&firewire_cdevsw,
8168471Sache			MAKEMINOR(FWMEM_FLAG, unit, 0),
817136210Sphk			UID_ROOT, GID_OPERATOR, 0660,
818105806Sjhb			"fwmem%d.%d", unit, 0);
8198471Sache	dev_depends(sc->dev, d);
820105806Sjhb	make_dev_alias(sc->dev, "fw%d", unit);
8218471Sache	make_dev_alias(d, "fwmem%d", unit);
8229232Sache#endif
823105806Sjhb
8249232Sache	return (err);
82546704Speter}
8268471Sache
82746704Speterint
8289232Sachefwdev_destroydev(struct firewire_softc *sc)
8299232Sache{
830105806Sjhb	int err = 0;
831105806Sjhb
832131096Sphk#if defined(__DragonFly__) || __FreeBSD_version < 500000
833131981Sphk	cdevsw_remove(&firewire_cdevsw);
8348471Sache#else
8358471Sache	destroy_dev(sc->dev);
836111748Sdes#endif
8379639Sbde	return (err);
8388471Sache}
8398471Sache
8408471Sache#if defined(__FreeBSD__) && __FreeBSD_version >= 500000
8418471Sache#define NDEVTYPE 2
842105806Sjhbvoid
843118607Sjhbfwdev_clone(void *arg, char *name, int namelen, struct cdev **dev)
8448471Sache{
845105806Sjhb	struct firewire_softc *sc;
8468471Sache	char *devnames[NDEVTYPE] = {"fw", "fwmem"};
847105806Sjhb	char *subp = NULL;
8489232Sache	int devflag[NDEVTYPE] = {0, FWMEM_FLAG};
849105806Sjhb	int i, unit = 0, sub = 0;
850105806Sjhb
851105806Sjhb	if (*dev != NULL)
8528471Sache		return;
8538471Sache
854105806Sjhb	for (i = 0; i < NDEVTYPE; i++)
855105806Sjhb		if (dev_stdclone(name, &subp, devnames[i], &unit) == 2)
8568471Sache			goto found;
8578471Sache	/* not match */
858105806Sjhb	return;
859105806Sjhbfound:
860105806Sjhb
8618471Sache	if (subp == NULL || *subp++ != '.')
8628471Sache		return;
863105806Sjhb
8648471Sache	/* /dev/fwU.S */
8658471Sache	while (isdigit(*subp)) {
8668471Sache		sub *= 10;
867105806Sjhb		sub += *subp++ - '0';
868105806Sjhb	}
8698471Sache	if (*subp != '\0')
870105806Sjhb		return;
871105806Sjhb
872105806Sjhb	sc = devclass_get_softc(firewire_devclass, unit);
8738471Sache	if (sc == NULL)
8749855Sache		return;
8759855Sache	*dev = make_dev(&firewire_cdevsw, MAKEMINOR(devflag[i], unit, sub),
8769855Sache		       UID_ROOT, GID_OPERATOR, 0660,
8779855Sache		       "%s%d.%d", devnames[i], unit, sub);
8788471Sache	dev_ref(*dev);
8798471Sache	(*dev)->si_flags |= SI_CHEAPCLONE;
8809855Sache	dev_depends(sc->dev, *dev);
8819855Sache	return;
8828471Sache}
883136210Sphk#endif
884105806Sjhb