fwdev.c revision 113802
1106813Ssimokawa/*
2113584Ssimokawa * Copyright (c) 2003 Hidetoshi Shimokawa
3106813Ssimokawa * Copyright (c) 1998-2002 Katsushi Kobayashi and Hidetoshi Shimokawa
4106813Ssimokawa * All rights reserved.
5106813Ssimokawa *
6106813Ssimokawa * Redistribution and use in source and binary forms, with or without
7106813Ssimokawa * modification, are permitted provided that the following conditions
8106813Ssimokawa * are met:
9106813Ssimokawa * 1. Redistributions of source code must retain the above copyright
10106813Ssimokawa *    notice, this list of conditions and the following disclaimer.
11106813Ssimokawa * 2. Redistributions in binary form must reproduce the above copyright
12106813Ssimokawa *    notice, this list of conditions and the following disclaimer in the
13106813Ssimokawa *    documentation and/or other materials provided with the distribution.
14106813Ssimokawa * 3. All advertising materials mentioning features or use of this software
15106813Ssimokawa *    must display the acknowledgement as bellow:
16106813Ssimokawa *
17106813Ssimokawa *    This product includes software developed by K. Kobayashi and H. Shimokawa
18106813Ssimokawa *
19106813Ssimokawa * 4. The name of the author may not be used to endorse or promote products
20106813Ssimokawa *    derived from this software without specific prior written permission.
21106813Ssimokawa *
22106813Ssimokawa * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23106813Ssimokawa * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24106813Ssimokawa * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25106813Ssimokawa * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
26106813Ssimokawa * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27106813Ssimokawa * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28106813Ssimokawa * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29106813Ssimokawa * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
30106813Ssimokawa * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31106813Ssimokawa * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32106813Ssimokawa * POSSIBILITY OF SUCH DAMAGE.
33106813Ssimokawa *
34106813Ssimokawa * $FreeBSD: head/sys/dev/firewire/fwdev.c 113802 2003-04-21 16:41:20Z simokawa $
35106813Ssimokawa *
36106813Ssimokawa */
37106813Ssimokawa
38106813Ssimokawa#include <sys/param.h>
39106813Ssimokawa#include <sys/systm.h>
40106813Ssimokawa#include <sys/types.h>
41106813Ssimokawa#include <sys/mbuf.h>
42106813Ssimokawa
43106813Ssimokawa#include <sys/kernel.h>
44106813Ssimokawa#include <sys/malloc.h>
45106813Ssimokawa#include <sys/conf.h>
46106813Ssimokawa#include <sys/poll.h>
47106813Ssimokawa
48106813Ssimokawa#include <sys/bus.h>
49113584Ssimokawa#include <machine/bus.h>
50106813Ssimokawa
51106813Ssimokawa#include <sys/ioccom.h>
52106813Ssimokawa
53106813Ssimokawa#include <dev/firewire/firewire.h>
54106813Ssimokawa#include <dev/firewire/firewirereg.h>
55113584Ssimokawa#include <dev/firewire/fwdma.h>
56106813Ssimokawa#include <dev/firewire/fwmem.h>
57109282Ssimokawa#include <dev/firewire/iec68113.h>
58106813Ssimokawa
59106813Ssimokawa#define CDEV_MAJOR 127
60106813Ssimokawa#define	FWNODE_INVAL 0xffff
61106813Ssimokawa
62106813Ssimokawastatic	d_open_t	fw_open;
63106813Ssimokawastatic	d_close_t	fw_close;
64106813Ssimokawastatic	d_ioctl_t	fw_ioctl;
65106813Ssimokawastatic	d_poll_t	fw_poll;
66106813Ssimokawastatic	d_read_t	fw_read;	/* for Isochronous packet */
67106813Ssimokawastatic	d_write_t	fw_write;
68106813Ssimokawastatic	d_mmap_t	fw_mmap;
69106813Ssimokawa
70106813Ssimokawastruct cdevsw firewire_cdevsw =
71106813Ssimokawa{
72111942Ssimokawa#if __FreeBSD_version >= 500104
73111815Sphk	.d_open =	fw_open,
74111815Sphk	.d_close =	fw_close,
75111815Sphk	.d_read =	fw_read,
76111815Sphk	.d_write =	fw_write,
77111815Sphk	.d_ioctl =	fw_ioctl,
78111815Sphk	.d_poll =	fw_poll,
79111815Sphk	.d_mmap =	fw_mmap,
80111815Sphk	.d_name =	"fw",
81111815Sphk	.d_maj =	CDEV_MAJOR,
82111815Sphk	.d_flags =	D_MEM
83111942Ssimokawa#else
84111942Ssimokawa	fw_open, fw_close, fw_read, fw_write, fw_ioctl,
85111942Ssimokawa	fw_poll, fw_mmap, nostrategy, "fw", CDEV_MAJOR, nodump, nopsize, D_MEM
86111942Ssimokawa#endif
87106813Ssimokawa};
88106813Ssimokawa
89106813Ssimokawastatic int
90106813Ssimokawafw_open (dev_t dev, int flags, int fmt, fw_proc *td)
91106813Ssimokawa{
92106813Ssimokawa	struct firewire_softc *sc;
93106813Ssimokawa	int unit = DEV2UNIT(dev);
94106813Ssimokawa	int sub = DEV2DMACH(dev);
95106813Ssimokawa
96106813Ssimokawa	int err = 0;
97106813Ssimokawa
98106813Ssimokawa	if (DEV_FWMEM(dev))
99106813Ssimokawa		return fwmem_open(dev, flags, fmt, td);
100106813Ssimokawa
101106813Ssimokawa	sc = devclass_get_softc(firewire_devclass, unit);
102106813Ssimokawa	if(sc->fc->ir[sub]->flag & FWXFERQ_OPEN){
103106813Ssimokawa		err = EBUSY;
104106813Ssimokawa		return err;
105106813Ssimokawa	}
106106813Ssimokawa	if(sc->fc->it[sub]->flag & FWXFERQ_OPEN){
107106813Ssimokawa		err = EBUSY;
108106813Ssimokawa		return err;
109106813Ssimokawa	}
110106813Ssimokawa	if(sc->fc->ir[sub]->flag & FWXFERQ_MODEMASK){
111106813Ssimokawa		err = EBUSY;
112106813Ssimokawa		return err;
113106813Ssimokawa	}
114106813Ssimokawa/* Default is per packet mode */
115106813Ssimokawa	sc->fc->ir[sub]->flag |= FWXFERQ_OPEN;
116106813Ssimokawa	sc->fc->it[sub]->flag |= FWXFERQ_OPEN;
117106813Ssimokawa	return err;
118106813Ssimokawa}
119106813Ssimokawa
120106813Ssimokawastatic int
121106813Ssimokawafw_close (dev_t dev, int flags, int fmt, fw_proc *td)
122106813Ssimokawa{
123106813Ssimokawa	struct firewire_softc *sc;
124106813Ssimokawa	int unit = DEV2UNIT(dev);
125106813Ssimokawa	int sub = DEV2DMACH(dev);
126106813Ssimokawa	struct fw_xfer *xfer;
127106813Ssimokawa	struct fw_bind *fwb;
128106813Ssimokawa	int err = 0;
129106813Ssimokawa
130106813Ssimokawa	if (DEV_FWMEM(dev))
131106813Ssimokawa		return fwmem_close(dev, flags, fmt, td);
132106813Ssimokawa
133106813Ssimokawa	sc = devclass_get_softc(firewire_devclass, unit);
134106813Ssimokawa	if(!(sc->fc->ir[sub]->flag & FWXFERQ_OPEN)){
135106813Ssimokawa		err = EINVAL;
136106813Ssimokawa		return err;
137106813Ssimokawa	}
138106813Ssimokawa	sc->fc->ir[sub]->flag &= ~FWXFERQ_OPEN;
139106813Ssimokawa	if(!(sc->fc->it[sub]->flag & FWXFERQ_OPEN)){
140106813Ssimokawa		err = EINVAL;
141106813Ssimokawa		return err;
142106813Ssimokawa	}
143106813Ssimokawa	sc->fc->it[sub]->flag &= ~FWXFERQ_OPEN;
144106813Ssimokawa
145106813Ssimokawa	if(sc->fc->ir[sub]->flag & FWXFERQ_RUNNING){
146106813Ssimokawa		sc->fc->irx_disable(sc->fc, sub);
147106813Ssimokawa	}
148106813Ssimokawa	if(sc->fc->it[sub]->flag & FWXFERQ_RUNNING){
149106813Ssimokawa		sc->fc->it[sub]->flag &= ~FWXFERQ_RUNNING;
150106813Ssimokawa		sc->fc->itx_disable(sc->fc, sub);
151106813Ssimokawa	}
152106813Ssimokawa	if(sc->fc->ir[sub]->flag & FWXFERQ_EXTBUF){
153113584Ssimokawa		if (sc->fc->ir[sub]->buf != NULL)
154113584Ssimokawa			fwdma_free_multiseg(sc->fc->ir[sub]->buf);
155106813Ssimokawa		sc->fc->ir[sub]->buf = NULL;
156110195Ssimokawa		free(sc->fc->ir[sub]->bulkxfer, M_FW);
157106813Ssimokawa		sc->fc->ir[sub]->bulkxfer = NULL;
158106813Ssimokawa		sc->fc->ir[sub]->flag &= ~FWXFERQ_EXTBUF;
159109379Ssimokawa		sc->fc->ir[sub]->psize = PAGE_SIZE;
160106813Ssimokawa		sc->fc->ir[sub]->maxq = FWMAXQUEUE;
161106813Ssimokawa	}
162106813Ssimokawa	if(sc->fc->it[sub]->flag & FWXFERQ_EXTBUF){
163113584Ssimokawa		if (sc->fc->it[sub]->buf != NULL)
164113584Ssimokawa			fwdma_free_multiseg(sc->fc->it[sub]->buf);
165106813Ssimokawa		sc->fc->it[sub]->buf = NULL;
166110195Ssimokawa		free(sc->fc->it[sub]->bulkxfer, M_FW);
167106813Ssimokawa		sc->fc->it[sub]->bulkxfer = NULL;
168106813Ssimokawa		sc->fc->it[sub]->flag &= ~FWXFERQ_EXTBUF;
169109379Ssimokawa		sc->fc->it[sub]->psize = 0;
170106813Ssimokawa		sc->fc->it[sub]->maxq = FWMAXQUEUE;
171106813Ssimokawa	}
172106813Ssimokawa	for(xfer = STAILQ_FIRST(&sc->fc->ir[sub]->q);
173106813Ssimokawa		xfer != NULL; xfer = STAILQ_FIRST(&sc->fc->ir[sub]->q)){
174106813Ssimokawa		sc->fc->ir[sub]->queued--;
175106813Ssimokawa		STAILQ_REMOVE_HEAD(&sc->fc->ir[sub]->q, link);
176106813Ssimokawa
177106813Ssimokawa		xfer->resp = 0;
178113584Ssimokawa		fw_xfer_done(xfer);
179106813Ssimokawa	}
180106813Ssimokawa	for(fwb = STAILQ_FIRST(&sc->fc->ir[sub]->binds); fwb != NULL;
181106813Ssimokawa		fwb = STAILQ_FIRST(&sc->fc->ir[sub]->binds)){
182106813Ssimokawa		STAILQ_REMOVE(&sc->fc->binds, fwb, fw_bind, fclist);
183106813Ssimokawa		STAILQ_REMOVE_HEAD(&sc->fc->ir[sub]->binds, chlist);
184110195Ssimokawa		free(fwb, M_FW);
185106813Ssimokawa	}
186113584Ssimokawa	sc->fc->ir[sub]->flag &= ~(FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK);
187113584Ssimokawa	sc->fc->it[sub]->flag &= ~(FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK);
188106813Ssimokawa	return err;
189106813Ssimokawa}
190106813Ssimokawa
191106813Ssimokawa/*
192106813Ssimokawa * read request.
193106813Ssimokawa */
194106813Ssimokawastatic int
195106813Ssimokawafw_read (dev_t dev, struct uio *uio, int ioflag)
196106813Ssimokawa{
197106813Ssimokawa	struct firewire_softc *sc;
198106813Ssimokawa	struct fw_xferq *ir;
199106813Ssimokawa	struct fw_xfer *xfer;
200106813Ssimokawa	int err = 0, s, slept = 0;
201106813Ssimokawa	int unit = DEV2UNIT(dev);
202106813Ssimokawa	int sub = DEV2DMACH(dev);
203106813Ssimokawa	struct fw_pkt *fp;
204106813Ssimokawa
205106813Ssimokawa	if (DEV_FWMEM(dev))
206106813Ssimokawa		return fwmem_read(dev, uio, ioflag);
207106813Ssimokawa
208106813Ssimokawa	sc = devclass_get_softc(firewire_devclass, unit);
209106813Ssimokawa
210106813Ssimokawa	ir = sc->fc->ir[sub];
211106813Ssimokawa
212106813Ssimokawareadloop:
213106813Ssimokawa	xfer = STAILQ_FIRST(&ir->q);
214113584Ssimokawa	if (ir->stproc == NULL) {
215109988Ssimokawa		/* iso bulkxfer */
216106813Ssimokawa		ir->stproc = STAILQ_FIRST(&ir->stvalid);
217109988Ssimokawa		if (ir->stproc != NULL) {
218106813Ssimokawa			s = splfw();
219106813Ssimokawa			STAILQ_REMOVE_HEAD(&ir->stvalid, link);
220106813Ssimokawa			splx(s);
221106813Ssimokawa			ir->queued = 0;
222106813Ssimokawa		}
223106813Ssimokawa	}
224109988Ssimokawa	if (xfer == NULL && ir->stproc == NULL) {
225109988Ssimokawa		/* no data avaliable */
226109988Ssimokawa		if (slept == 0) {
227106813Ssimokawa			slept = 1;
228106813Ssimokawa			ir->flag |= FWXFERQ_WAKEUP;
229111748Sdes			err = tsleep(ir, FWPRI, "fw_read", hz);
230109988Ssimokawa			ir->flag &= ~FWXFERQ_WAKEUP;
231109988Ssimokawa			if (err == 0)
232109988Ssimokawa				goto readloop;
233109988Ssimokawa		} else if (slept == 1)
234106813Ssimokawa			err = EIO;
235109988Ssimokawa		return err;
236109988Ssimokawa	} else if(xfer != NULL) {
237113584Ssimokawa		/* per packet mode or FWACT_CH bind?*/
238106813Ssimokawa		s = splfw();
239106813Ssimokawa		ir->queued --;
240106813Ssimokawa		STAILQ_REMOVE_HEAD(&ir->q, link);
241106813Ssimokawa		splx(s);
242113584Ssimokawa		fp = (struct fw_pkt *)xfer->recv.buf;
243106813Ssimokawa		if(sc->fc->irx_post != NULL)
244106813Ssimokawa			sc->fc->irx_post(sc->fc, fp->mode.ld);
245113584Ssimokawa		err = uiomove(xfer->recv.buf, xfer->recv.len, uio);
246113584Ssimokawa		/* XXX we should recycle this xfer */
247106813Ssimokawa		fw_xfer_free( xfer);
248109988Ssimokawa	} else if(ir->stproc != NULL) {
249109988Ssimokawa		/* iso bulkxfer */
250113584Ssimokawa		fp = (struct fw_pkt *)fwdma_v_addr(ir->buf,
251113584Ssimokawa				ir->stproc->poffset + ir->queued);
252106813Ssimokawa		if(sc->fc->irx_post != NULL)
253106813Ssimokawa			sc->fc->irx_post(sc->fc, fp->mode.ld);
254113584Ssimokawa		if(fp->mode.stream.len == 0){
255106813Ssimokawa			err = EIO;
256106813Ssimokawa			return err;
257106813Ssimokawa		}
258109988Ssimokawa		err = uiomove((caddr_t)fp,
259113584Ssimokawa			fp->mode.stream.len + sizeof(u_int32_t), uio);
260106813Ssimokawa		ir->queued ++;
261106813Ssimokawa		if(ir->queued >= ir->bnpacket){
262106813Ssimokawa			s = splfw();
263106813Ssimokawa			STAILQ_INSERT_TAIL(&ir->stfree, ir->stproc, link);
264106813Ssimokawa			splx(s);
265109890Ssimokawa			sc->fc->irx_enable(sc->fc, sub);
266106813Ssimokawa			ir->stproc = NULL;
267106813Ssimokawa		}
268109988Ssimokawa		if (uio->uio_resid >= ir->psize) {
269109988Ssimokawa			slept = -1;
270109988Ssimokawa			goto readloop;
271109988Ssimokawa		}
272106813Ssimokawa	}
273106813Ssimokawa	return err;
274106813Ssimokawa}
275106813Ssimokawa
276106813Ssimokawastatic int
277106813Ssimokawafw_write (dev_t dev, struct uio *uio, int ioflag)
278106813Ssimokawa{
279106813Ssimokawa	int err = 0;
280106813Ssimokawa	struct firewire_softc *sc;
281106813Ssimokawa	int unit = DEV2UNIT(dev);
282106813Ssimokawa	int sub = DEV2DMACH(dev);
283106813Ssimokawa	int s, slept = 0;
284106813Ssimokawa	struct fw_pkt *fp;
285106813Ssimokawa	struct firewire_comm *fc;
286106813Ssimokawa	struct fw_xferq *it;
287106813Ssimokawa
288106813Ssimokawa	if (DEV_FWMEM(dev))
289106813Ssimokawa		return fwmem_write(dev, uio, ioflag);
290106813Ssimokawa
291106813Ssimokawa	sc = devclass_get_softc(firewire_devclass, unit);
292106813Ssimokawa	fc = sc->fc;
293106813Ssimokawa	it = sc->fc->it[sub];
294106813Ssimokawaisoloop:
295113802Ssimokawa	if (it->stproc == NULL) {
296113802Ssimokawa		it->stproc = STAILQ_FIRST(&it->stfree);
297113802Ssimokawa		if (it->stproc != NULL) {
298106813Ssimokawa			s = splfw();
299113802Ssimokawa			STAILQ_REMOVE_HEAD(&it->stfree, link);
300106813Ssimokawa			splx(s);
301113802Ssimokawa			it->queued = 0;
302113802Ssimokawa		} else if (slept == 0) {
303113802Ssimokawa			slept = 1;
304106813Ssimokawa			err = sc->fc->itx_enable(sc->fc, sub);
305113802Ssimokawa			if (err)
306113802Ssimokawa				return err;
307113802Ssimokawa			err = tsleep(it, FWPRI, "fw_write", hz);
308113802Ssimokawa			if (err)
309113802Ssimokawa				return err;
310109988Ssimokawa			goto isoloop;
311113802Ssimokawa		} else {
312113802Ssimokawa			err = EIO;
313106813Ssimokawa			return err;
314106813Ssimokawa		}
315106813Ssimokawa	}
316113802Ssimokawa	fp = (struct fw_pkt *)fwdma_v_addr(it->buf,
317113802Ssimokawa			it->stproc->poffset + it->queued);
318113802Ssimokawa	err = uiomove((caddr_t)fp, sizeof(struct fw_isohdr), uio);
319113802Ssimokawa	err = uiomove((caddr_t)fp->mode.stream.payload,
320113802Ssimokawa				fp->mode.stream.len, uio);
321113802Ssimokawa	it->queued ++;
322113802Ssimokawa	if (it->queued >= it->bnpacket) {
323113802Ssimokawa		s = splfw();
324113802Ssimokawa		STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link);
325113802Ssimokawa		splx(s);
326113802Ssimokawa		it->stproc = NULL;
327113802Ssimokawa		err = sc->fc->itx_enable(sc->fc, sub);
328113802Ssimokawa	}
329113802Ssimokawa	if (uio->uio_resid >= sizeof(struct fw_isohdr)) {
330113802Ssimokawa		slept = 0;
331113802Ssimokawa		goto isoloop;
332113802Ssimokawa	}
333113802Ssimokawa	return err;
334106813Ssimokawa}
335106813Ssimokawa
336106813Ssimokawa/*
337106813Ssimokawa * ioctl support.
338106813Ssimokawa */
339106813Ssimokawaint
340106813Ssimokawafw_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td)
341106813Ssimokawa{
342106813Ssimokawa	struct firewire_softc *sc;
343106813Ssimokawa	int unit = DEV2UNIT(dev);
344106813Ssimokawa	int sub = DEV2DMACH(dev);
345113584Ssimokawa	int s, i, len, err = 0;
346106813Ssimokawa	struct fw_device *fwdev;
347106813Ssimokawa	struct fw_bind *fwb;
348106813Ssimokawa	struct fw_xferq *ir, *it;
349106813Ssimokawa	struct fw_xfer *xfer;
350106813Ssimokawa	struct fw_pkt *fp;
351109814Ssimokawa	struct fw_devinfo *devinfo;
352106813Ssimokawa
353106813Ssimokawa	struct fw_devlstreq *fwdevlst = (struct fw_devlstreq *)data;
354106813Ssimokawa	struct fw_asyreq *asyreq = (struct fw_asyreq *)data;
355106813Ssimokawa	struct fw_isochreq *ichreq = (struct fw_isochreq *)data;
356106813Ssimokawa	struct fw_isobufreq *ibufreq = (struct fw_isobufreq *)data;
357106813Ssimokawa	struct fw_asybindreq *bindreq = (struct fw_asybindreq *)data;
358106813Ssimokawa	struct fw_crom_buf *crom_buf = (struct fw_crom_buf *)data;
359106813Ssimokawa
360106813Ssimokawa	if (DEV_FWMEM(dev))
361106813Ssimokawa		return fwmem_ioctl(dev, cmd, data, flag, td);
362106813Ssimokawa
363106813Ssimokawa	sc = devclass_get_softc(firewire_devclass, unit);
364106813Ssimokawa	if (!data)
365106813Ssimokawa		return(EINVAL);
366106813Ssimokawa
367106813Ssimokawa	switch (cmd) {
368106813Ssimokawa	case FW_STSTREAM:
369106813Ssimokawa		sc->fc->it[sub]->flag &= ~0xff;
370106813Ssimokawa		sc->fc->it[sub]->flag |= (0x3f & ichreq->ch);
371106813Ssimokawa		sc->fc->it[sub]->flag |= ((0x3 & ichreq->tag) << 6);
372106813Ssimokawa		err = 0;
373106813Ssimokawa		break;
374106813Ssimokawa	case FW_GTSTREAM:
375106813Ssimokawa		ichreq->ch = sc->fc->it[sub]->flag & 0x3f;
376106813Ssimokawa		ichreq->tag =(sc->fc->it[sub]->flag) >> 2 & 0x3;
377106813Ssimokawa		err = 0;
378106813Ssimokawa		break;
379106813Ssimokawa	case FW_SRSTREAM:
380106813Ssimokawa		sc->fc->ir[sub]->flag &= ~0xff;
381106813Ssimokawa		sc->fc->ir[sub]->flag |= (0x3f & ichreq->ch);
382106813Ssimokawa		sc->fc->ir[sub]->flag |= ((0x3 & ichreq->tag) << 6);
383106813Ssimokawa		err = sc->fc->irx_enable(sc->fc, sub);
384106813Ssimokawa		break;
385106813Ssimokawa	case FW_GRSTREAM:
386106813Ssimokawa		ichreq->ch = sc->fc->ir[sub]->flag & 0x3f;
387106813Ssimokawa		ichreq->tag =(sc->fc->ir[sub]->flag) >> 2 & 0x3;
388106813Ssimokawa		err = 0;
389106813Ssimokawa		break;
390106813Ssimokawa	case FW_SSTBUF:
391106813Ssimokawa		ir = sc->fc->ir[sub];
392106813Ssimokawa		it = sc->fc->it[sub];
393106813Ssimokawa
394106813Ssimokawa		if(ir->flag & FWXFERQ_RUNNING || it->flag & FWXFERQ_RUNNING){
395106813Ssimokawa			return(EBUSY);
396106813Ssimokawa		}
397106813Ssimokawa		if((ir->flag & FWXFERQ_EXTBUF) || (it->flag & FWXFERQ_EXTBUF)){
398106813Ssimokawa			return(EBUSY);
399106813Ssimokawa		}
400106813Ssimokawa		if((ibufreq->rx.nchunk *
401106813Ssimokawa			ibufreq->rx.psize * ibufreq->rx.npacket) +
402106813Ssimokawa		   (ibufreq->tx.nchunk *
403106813Ssimokawa			ibufreq->tx.psize * ibufreq->tx.npacket) <= 0){
404106813Ssimokawa				return(EINVAL);
405106813Ssimokawa		}
406106813Ssimokawa		ir->bulkxfer
407113584Ssimokawa			= (struct fw_bulkxfer *)malloc(sizeof(struct fw_bulkxfer) * ibufreq->rx.nchunk, M_FW, M_WAITOK);
408106813Ssimokawa		if(ir->bulkxfer == NULL){
409106813Ssimokawa			return(ENOMEM);
410106813Ssimokawa		}
411106813Ssimokawa		it->bulkxfer
412113584Ssimokawa			= (struct fw_bulkxfer *)malloc(sizeof(struct fw_bulkxfer) * ibufreq->tx.nchunk, M_FW, M_WAITOK);
413106813Ssimokawa		if(it->bulkxfer == NULL){
414106813Ssimokawa			return(ENOMEM);
415106813Ssimokawa		}
416113584Ssimokawa		if (ibufreq->rx.psize > 0) {
417113584Ssimokawa			ibufreq->rx.psize = roundup2(ibufreq->rx.psize,
418113584Ssimokawa							sizeof(u_int32_t));
419113584Ssimokawa			ir->buf = fwdma_malloc_multiseg(
420113584Ssimokawa				sc->fc, sizeof(u_int32_t),
421113584Ssimokawa				ibufreq->rx.psize,
422113584Ssimokawa				ibufreq->rx.nchunk * ibufreq->rx.npacket,
423113584Ssimokawa				BUS_DMA_WAITOK);
424113584Ssimokawa
425113584Ssimokawa			if(ir->buf == NULL){
426113584Ssimokawa				free(ir->bulkxfer, M_FW);
427113584Ssimokawa				free(it->bulkxfer, M_FW);
428113584Ssimokawa				ir->bulkxfer = NULL;
429113584Ssimokawa				it->bulkxfer = NULL;
430113584Ssimokawa				it->buf = NULL;
431113584Ssimokawa				return(ENOMEM);
432113584Ssimokawa			}
433106813Ssimokawa		}
434113584Ssimokawa		if (ibufreq->tx.psize > 0) {
435113584Ssimokawa			ibufreq->tx.psize = roundup2(ibufreq->tx.psize,
436113584Ssimokawa							sizeof(u_int32_t));
437113584Ssimokawa			it->buf = fwdma_malloc_multiseg(
438113584Ssimokawa				sc->fc, sizeof(u_int32_t),
439113584Ssimokawa				ibufreq->tx.psize,
440113584Ssimokawa				ibufreq->tx.nchunk * ibufreq->tx.npacket,
441113584Ssimokawa				BUS_DMA_WAITOK);
442113584Ssimokawa
443113584Ssimokawa			if(it->buf == NULL){
444113584Ssimokawa				free(ir->bulkxfer, M_FW);
445113584Ssimokawa				free(it->bulkxfer, M_FW);
446113584Ssimokawa				fwdma_free_multiseg(ir->buf);
447113584Ssimokawa				ir->bulkxfer = NULL;
448113584Ssimokawa				it->bulkxfer = NULL;
449113584Ssimokawa				it->buf = NULL;
450113584Ssimokawa				return(ENOMEM);
451113584Ssimokawa			}
452106813Ssimokawa		}
453106813Ssimokawa
454106813Ssimokawa		ir->bnchunk = ibufreq->rx.nchunk;
455106813Ssimokawa		ir->bnpacket = ibufreq->rx.npacket;
456106813Ssimokawa		ir->psize = (ibufreq->rx.psize + 3) & ~3;
457106813Ssimokawa		ir->queued = 0;
458106813Ssimokawa
459106813Ssimokawa		it->bnchunk = ibufreq->tx.nchunk;
460106813Ssimokawa		it->bnpacket = ibufreq->tx.npacket;
461106813Ssimokawa		it->psize = (ibufreq->tx.psize + 3) & ~3;
462109890Ssimokawa		it->queued = 0;
463109890Ssimokawa
464106813Ssimokawa		STAILQ_INIT(&ir->stvalid);
465106813Ssimokawa		STAILQ_INIT(&ir->stfree);
466109890Ssimokawa		STAILQ_INIT(&ir->stdma);
467106813Ssimokawa		ir->stproc = NULL;
468106813Ssimokawa
469106813Ssimokawa		STAILQ_INIT(&it->stvalid);
470106813Ssimokawa		STAILQ_INIT(&it->stfree);
471109890Ssimokawa		STAILQ_INIT(&it->stdma);
472106813Ssimokawa		it->stproc = NULL;
473106813Ssimokawa
474106813Ssimokawa		for(i = 0 ; i < sc->fc->ir[sub]->bnchunk; i++){
475113584Ssimokawa			ir->bulkxfer[i].poffset = i * ir->bnpacket;
476113584Ssimokawa			ir->bulkxfer[i].mbuf = NULL;
477106813Ssimokawa			STAILQ_INSERT_TAIL(&ir->stfree,
478106813Ssimokawa					&ir->bulkxfer[i], link);
479106813Ssimokawa		}
480106813Ssimokawa		for(i = 0 ; i < sc->fc->it[sub]->bnchunk; i++){
481113584Ssimokawa			it->bulkxfer[i].poffset = i * it->bnpacket;
482113584Ssimokawa			it->bulkxfer[i].mbuf = NULL;
483106813Ssimokawa			STAILQ_INSERT_TAIL(&it->stfree,
484106813Ssimokawa					&it->bulkxfer[i], link);
485106813Ssimokawa		}
486106813Ssimokawa		ir->flag &= ~FWXFERQ_MODEMASK;
487106813Ssimokawa		ir->flag |= FWXFERQ_STREAM;
488106813Ssimokawa		ir->flag |= FWXFERQ_EXTBUF;
489106813Ssimokawa
490106813Ssimokawa		it->flag &= ~FWXFERQ_MODEMASK;
491106813Ssimokawa		it->flag |= FWXFERQ_STREAM;
492106813Ssimokawa		it->flag |= FWXFERQ_EXTBUF;
493106813Ssimokawa		err = 0;
494106813Ssimokawa		break;
495106813Ssimokawa	case FW_GSTBUF:
496106813Ssimokawa		ibufreq->rx.nchunk = sc->fc->ir[sub]->bnchunk;
497106813Ssimokawa		ibufreq->rx.npacket = sc->fc->ir[sub]->bnpacket;
498106813Ssimokawa		ibufreq->rx.psize = sc->fc->ir[sub]->psize;
499106813Ssimokawa
500106813Ssimokawa		ibufreq->tx.nchunk = sc->fc->it[sub]->bnchunk;
501106813Ssimokawa		ibufreq->tx.npacket = sc->fc->it[sub]->bnpacket;
502106813Ssimokawa		ibufreq->tx.psize = sc->fc->it[sub]->psize;
503106813Ssimokawa		break;
504106813Ssimokawa	case FW_ASYREQ:
505113584Ssimokawa		xfer = fw_xfer_alloc_buf(M_FWXFER, asyreq->req.len,
506113584Ssimokawa							PAGE_SIZE /* XXX */);
507106813Ssimokawa		if(xfer == NULL){
508106813Ssimokawa			err = ENOMEM;
509106813Ssimokawa			return err;
510106813Ssimokawa		}
511106813Ssimokawa		fp = &asyreq->pkt;
512106813Ssimokawa		switch (asyreq->req.type) {
513106813Ssimokawa		case FWASREQNODE:
514113584Ssimokawa			xfer->dst = fp->mode.hdr.dst;
515106813Ssimokawa			break;
516106813Ssimokawa		case FWASREQEUI:
517110072Ssimokawa			fwdev = fw_noderesolve_eui64(sc->fc,
518110582Ssimokawa						&asyreq->req.dst.eui);
519106813Ssimokawa			if (fwdev == NULL) {
520108782Ssimokawa				device_printf(sc->fc->bdev,
521108782Ssimokawa					"cannot find node\n");
522106813Ssimokawa				err = EINVAL;
523106813Ssimokawa				goto error;
524106813Ssimokawa			}
525106813Ssimokawa			xfer->dst = fwdev->dst;
526113584Ssimokawa			fp->mode.hdr.dst = FWLOCALBUS | xfer->dst;
527106813Ssimokawa			break;
528106813Ssimokawa		case FWASRESTL:
529106813Ssimokawa			/* XXX what's this? */
530106813Ssimokawa			break;
531106813Ssimokawa		case FWASREQSTREAM:
532106813Ssimokawa			/* nothing to do */
533106813Ssimokawa			break;
534106813Ssimokawa		}
535106813Ssimokawa		xfer->spd = asyreq->req.sped;
536106813Ssimokawa		bcopy(fp, xfer->send.buf, xfer->send.len);
537106813Ssimokawa		xfer->act.hand = fw_asy_callback;
538106813Ssimokawa		err = fw_asyreq(sc->fc, sub, xfer);
539106813Ssimokawa		if(err){
540106813Ssimokawa			fw_xfer_free( xfer);
541106813Ssimokawa			return err;
542106813Ssimokawa		}
543111748Sdes		err = tsleep(xfer, FWPRI, "asyreq", hz);
544106813Ssimokawa		if(err == 0){
545106813Ssimokawa			if(asyreq->req.len >= xfer->recv.len){
546106813Ssimokawa				asyreq->req.len = xfer->recv.len;
547106813Ssimokawa			}else{
548106813Ssimokawa				err = EINVAL;
549106813Ssimokawa			}
550113584Ssimokawa			bcopy(xfer->recv.buf, fp, asyreq->req.len);
551106813Ssimokawa		}
552106813Ssimokawaerror:
553106813Ssimokawa		fw_xfer_free( xfer);
554106813Ssimokawa		break;
555106813Ssimokawa	case FW_IBUSRST:
556106813Ssimokawa		sc->fc->ibr(sc->fc);
557106813Ssimokawa		break;
558106813Ssimokawa	case FW_CBINDADDR:
559106813Ssimokawa		fwb = fw_bindlookup(sc->fc,
560106813Ssimokawa				bindreq->start.hi, bindreq->start.lo);
561106813Ssimokawa		if(fwb == NULL){
562106813Ssimokawa			err = EINVAL;
563106813Ssimokawa			break;
564106813Ssimokawa		}
565106813Ssimokawa		STAILQ_REMOVE(&sc->fc->binds, fwb, fw_bind, fclist);
566106813Ssimokawa		STAILQ_REMOVE(&sc->fc->ir[sub]->binds, fwb, fw_bind, chlist);
567110195Ssimokawa		free(fwb, M_FW);
568106813Ssimokawa		break;
569106813Ssimokawa	case FW_SBINDADDR:
570106813Ssimokawa		if(bindreq->len <= 0 ){
571106813Ssimokawa			err = EINVAL;
572106813Ssimokawa			break;
573106813Ssimokawa		}
574106813Ssimokawa		if(bindreq->start.hi > 0xffff ){
575106813Ssimokawa			err = EINVAL;
576106813Ssimokawa			break;
577106813Ssimokawa		}
578110195Ssimokawa		fwb = (struct fw_bind *)malloc(sizeof (struct fw_bind), M_FW, M_NOWAIT);
579106813Ssimokawa		if(fwb == NULL){
580106813Ssimokawa			err = ENOMEM;
581106813Ssimokawa			break;
582106813Ssimokawa		}
583106813Ssimokawa		fwb->start_hi = bindreq->start.hi;
584106813Ssimokawa		fwb->start_lo = bindreq->start.lo;
585106813Ssimokawa		fwb->addrlen = bindreq->len;
586113584Ssimokawa		fwb->sub = sub;
587113584Ssimokawa		fwb->act_type = FWACT_CH;
588106813Ssimokawa
589110269Ssimokawa		xfer = fw_xfer_alloc(M_FWXFER);
590106813Ssimokawa		if(xfer == NULL){
591106813Ssimokawa			err = ENOMEM;
592106813Ssimokawa			return err;
593106813Ssimokawa		}
594106813Ssimokawa		xfer->fc = sc->fc;
595106813Ssimokawa
596113584Ssimokawa		s = splfw();
597113584Ssimokawa		/* XXX broken. need multiple xfer */
598113584Ssimokawa		STAILQ_INIT(&fwb->xferlist);
599113584Ssimokawa		STAILQ_INSERT_TAIL(&fwb->xferlist, xfer, link);
600113584Ssimokawa		splx(s);
601106813Ssimokawa		err = fw_bindadd(sc->fc, fwb);
602106813Ssimokawa		break;
603106813Ssimokawa	case FW_GDEVLST:
604109814Ssimokawa		i = len = 1;
605109814Ssimokawa		/* myself */
606109814Ssimokawa		devinfo = &fwdevlst->dev[0];
607109814Ssimokawa		devinfo->dst = sc->fc->nodeid;
608109814Ssimokawa		devinfo->status = 0;	/* XXX */
609109814Ssimokawa		devinfo->eui.hi = sc->fc->eui.hi;
610109814Ssimokawa		devinfo->eui.lo = sc->fc->eui.lo;
611110193Ssimokawa		STAILQ_FOREACH(fwdev, &sc->fc->devices, link) {
612109814Ssimokawa			if(len < FW_MAX_DEVLST){
613109814Ssimokawa				devinfo = &fwdevlst->dev[len++];
614109814Ssimokawa				devinfo->dst = fwdev->dst;
615109814Ssimokawa				devinfo->status =
616109814Ssimokawa					(fwdev->status == FWDEVINVAL)?0:1;
617109814Ssimokawa				devinfo->eui.hi = fwdev->eui.hi;
618109814Ssimokawa				devinfo->eui.lo = fwdev->eui.lo;
619106813Ssimokawa			}
620106813Ssimokawa			i++;
621106813Ssimokawa		}
622106813Ssimokawa		fwdevlst->n = i;
623109814Ssimokawa		fwdevlst->info_len = len;
624106813Ssimokawa		break;
625106813Ssimokawa	case FW_GTPMAP:
626106813Ssimokawa		bcopy(sc->fc->topology_map, data,
627106813Ssimokawa				(sc->fc->topology_map->crc_len + 1) * 4);
628106813Ssimokawa		break;
629106813Ssimokawa	case FW_GCROM:
630110193Ssimokawa		STAILQ_FOREACH(fwdev, &sc->fc->devices, link)
631110193Ssimokawa			if (FW_EUI64_EQUAL(fwdev->eui, crom_buf->eui))
632106813Ssimokawa				break;
633106813Ssimokawa		if (fwdev == NULL) {
634106813Ssimokawa			err = FWNODE_INVAL;
635106813Ssimokawa			break;
636106813Ssimokawa		}
637106813Ssimokawa		if (fwdev->rommax < CSRROMOFF)
638106813Ssimokawa			len = 0;
639106813Ssimokawa		else
640106813Ssimokawa			len = fwdev->rommax - CSRROMOFF + 4;
641106813Ssimokawa		if (crom_buf->len < len)
642106813Ssimokawa			len = crom_buf->len;
643106813Ssimokawa		else
644106813Ssimokawa			crom_buf->len = len;
645106813Ssimokawa		err = copyout(&fwdev->csrrom[0], crom_buf->ptr, len);
646106813Ssimokawa		break;
647106813Ssimokawa	default:
648106813Ssimokawa		sc->fc->ioctl (dev, cmd, data, flag, td);
649106813Ssimokawa		break;
650106813Ssimokawa	}
651106813Ssimokawa	return err;
652106813Ssimokawa}
653106813Ssimokawaint
654106813Ssimokawafw_poll(dev_t dev, int events, fw_proc *td)
655106813Ssimokawa{
656106813Ssimokawa	int revents;
657106813Ssimokawa	int tmp;
658106813Ssimokawa	int unit = DEV2UNIT(dev);
659106813Ssimokawa	int sub = DEV2DMACH(dev);
660106813Ssimokawa	struct firewire_softc *sc;
661106813Ssimokawa
662106813Ssimokawa	if (DEV_FWMEM(dev))
663106813Ssimokawa		return fwmem_poll(dev, events, td);
664106813Ssimokawa
665106813Ssimokawa	sc = devclass_get_softc(firewire_devclass, unit);
666106813Ssimokawa	revents = 0;
667106813Ssimokawa	tmp = POLLIN | POLLRDNORM;
668106813Ssimokawa	if (events & tmp) {
669106813Ssimokawa		if (STAILQ_FIRST(&sc->fc->ir[sub]->q) != NULL)
670106813Ssimokawa			revents |= tmp;
671106813Ssimokawa		else
672106813Ssimokawa			selrecord(td, &sc->fc->ir[sub]->rsel);
673106813Ssimokawa	}
674106813Ssimokawa	tmp = POLLOUT | POLLWRNORM;
675106813Ssimokawa	if (events & tmp) {
676106813Ssimokawa		/* XXX should be fixed */
677106813Ssimokawa		revents |= tmp;
678106813Ssimokawa	}
679106813Ssimokawa
680106813Ssimokawa	return revents;
681106813Ssimokawa}
682106813Ssimokawa
683106813Ssimokawastatic int
684113584Ssimokawa#if __FreeBSD_version < 500102
685111615Ssimokawafw_mmap (dev_t dev, vm_offset_t offset, int nproto)
686111615Ssimokawa#else
687113584Ssimokawafw_mmap (dev_t dev, vm_offset_t offset, vm_paddr_t *paddr, int nproto)
688111615Ssimokawa#endif
689106813Ssimokawa{
690106813Ssimokawa	struct firewire_softc *fc;
691106813Ssimokawa	int unit = DEV2UNIT(dev);
692106813Ssimokawa
693106813Ssimokawa	if (DEV_FWMEM(dev))
694113584Ssimokawa#if __FreeBSD_version < 500102
695111615Ssimokawa		return fwmem_mmap(dev, offset, nproto);
696111615Ssimokawa#else
697111462Smux		return fwmem_mmap(dev, offset, paddr, nproto);
698111615Ssimokawa#endif
699106813Ssimokawa
700106813Ssimokawa	fc = devclass_get_softc(firewire_devclass, unit);
701106813Ssimokawa
702106813Ssimokawa	return EINVAL;
703106813Ssimokawa}
704