fwdev.c revision 117473
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 117473 2003-07-12 09:34:44Z 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;
352117473Ssimokawa	void *ptr;
353106813Ssimokawa
354106813Ssimokawa	struct fw_devlstreq *fwdevlst = (struct fw_devlstreq *)data;
355106813Ssimokawa	struct fw_asyreq *asyreq = (struct fw_asyreq *)data;
356106813Ssimokawa	struct fw_isochreq *ichreq = (struct fw_isochreq *)data;
357106813Ssimokawa	struct fw_isobufreq *ibufreq = (struct fw_isobufreq *)data;
358106813Ssimokawa	struct fw_asybindreq *bindreq = (struct fw_asybindreq *)data;
359106813Ssimokawa	struct fw_crom_buf *crom_buf = (struct fw_crom_buf *)data;
360106813Ssimokawa
361106813Ssimokawa	if (DEV_FWMEM(dev))
362106813Ssimokawa		return fwmem_ioctl(dev, cmd, data, flag, td);
363106813Ssimokawa
364106813Ssimokawa	sc = devclass_get_softc(firewire_devclass, unit);
365106813Ssimokawa	if (!data)
366106813Ssimokawa		return(EINVAL);
367106813Ssimokawa
368106813Ssimokawa	switch (cmd) {
369106813Ssimokawa	case FW_STSTREAM:
370106813Ssimokawa		sc->fc->it[sub]->flag &= ~0xff;
371106813Ssimokawa		sc->fc->it[sub]->flag |= (0x3f & ichreq->ch);
372106813Ssimokawa		sc->fc->it[sub]->flag |= ((0x3 & ichreq->tag) << 6);
373106813Ssimokawa		err = 0;
374106813Ssimokawa		break;
375106813Ssimokawa	case FW_GTSTREAM:
376106813Ssimokawa		ichreq->ch = sc->fc->it[sub]->flag & 0x3f;
377106813Ssimokawa		ichreq->tag =(sc->fc->it[sub]->flag) >> 2 & 0x3;
378106813Ssimokawa		err = 0;
379106813Ssimokawa		break;
380106813Ssimokawa	case FW_SRSTREAM:
381106813Ssimokawa		sc->fc->ir[sub]->flag &= ~0xff;
382106813Ssimokawa		sc->fc->ir[sub]->flag |= (0x3f & ichreq->ch);
383106813Ssimokawa		sc->fc->ir[sub]->flag |= ((0x3 & ichreq->tag) << 6);
384106813Ssimokawa		err = sc->fc->irx_enable(sc->fc, sub);
385106813Ssimokawa		break;
386106813Ssimokawa	case FW_GRSTREAM:
387106813Ssimokawa		ichreq->ch = sc->fc->ir[sub]->flag & 0x3f;
388106813Ssimokawa		ichreq->tag =(sc->fc->ir[sub]->flag) >> 2 & 0x3;
389106813Ssimokawa		err = 0;
390106813Ssimokawa		break;
391106813Ssimokawa	case FW_SSTBUF:
392106813Ssimokawa		ir = sc->fc->ir[sub];
393106813Ssimokawa		it = sc->fc->it[sub];
394106813Ssimokawa
395106813Ssimokawa		if(ir->flag & FWXFERQ_RUNNING || it->flag & FWXFERQ_RUNNING){
396106813Ssimokawa			return(EBUSY);
397106813Ssimokawa		}
398106813Ssimokawa		if((ir->flag & FWXFERQ_EXTBUF) || (it->flag & FWXFERQ_EXTBUF)){
399106813Ssimokawa			return(EBUSY);
400106813Ssimokawa		}
401106813Ssimokawa		if((ibufreq->rx.nchunk *
402106813Ssimokawa			ibufreq->rx.psize * ibufreq->rx.npacket) +
403106813Ssimokawa		   (ibufreq->tx.nchunk *
404106813Ssimokawa			ibufreq->tx.psize * ibufreq->tx.npacket) <= 0){
405106813Ssimokawa				return(EINVAL);
406106813Ssimokawa		}
407106813Ssimokawa		ir->bulkxfer
408113584Ssimokawa			= (struct fw_bulkxfer *)malloc(sizeof(struct fw_bulkxfer) * ibufreq->rx.nchunk, M_FW, M_WAITOK);
409106813Ssimokawa		if(ir->bulkxfer == NULL){
410106813Ssimokawa			return(ENOMEM);
411106813Ssimokawa		}
412106813Ssimokawa		it->bulkxfer
413113584Ssimokawa			= (struct fw_bulkxfer *)malloc(sizeof(struct fw_bulkxfer) * ibufreq->tx.nchunk, M_FW, M_WAITOK);
414106813Ssimokawa		if(it->bulkxfer == NULL){
415106813Ssimokawa			return(ENOMEM);
416106813Ssimokawa		}
417113584Ssimokawa		if (ibufreq->rx.psize > 0) {
418113584Ssimokawa			ibufreq->rx.psize = roundup2(ibufreq->rx.psize,
419113584Ssimokawa							sizeof(u_int32_t));
420113584Ssimokawa			ir->buf = fwdma_malloc_multiseg(
421113584Ssimokawa				sc->fc, sizeof(u_int32_t),
422113584Ssimokawa				ibufreq->rx.psize,
423113584Ssimokawa				ibufreq->rx.nchunk * ibufreq->rx.npacket,
424113584Ssimokawa				BUS_DMA_WAITOK);
425113584Ssimokawa
426113584Ssimokawa			if(ir->buf == NULL){
427113584Ssimokawa				free(ir->bulkxfer, M_FW);
428113584Ssimokawa				free(it->bulkxfer, M_FW);
429113584Ssimokawa				ir->bulkxfer = NULL;
430113584Ssimokawa				it->bulkxfer = NULL;
431113584Ssimokawa				it->buf = NULL;
432113584Ssimokawa				return(ENOMEM);
433113584Ssimokawa			}
434106813Ssimokawa		}
435113584Ssimokawa		if (ibufreq->tx.psize > 0) {
436113584Ssimokawa			ibufreq->tx.psize = roundup2(ibufreq->tx.psize,
437113584Ssimokawa							sizeof(u_int32_t));
438113584Ssimokawa			it->buf = fwdma_malloc_multiseg(
439113584Ssimokawa				sc->fc, sizeof(u_int32_t),
440113584Ssimokawa				ibufreq->tx.psize,
441113584Ssimokawa				ibufreq->tx.nchunk * ibufreq->tx.npacket,
442113584Ssimokawa				BUS_DMA_WAITOK);
443113584Ssimokawa
444113584Ssimokawa			if(it->buf == NULL){
445113584Ssimokawa				free(ir->bulkxfer, M_FW);
446113584Ssimokawa				free(it->bulkxfer, M_FW);
447113584Ssimokawa				fwdma_free_multiseg(ir->buf);
448113584Ssimokawa				ir->bulkxfer = NULL;
449113584Ssimokawa				it->bulkxfer = NULL;
450113584Ssimokawa				it->buf = NULL;
451113584Ssimokawa				return(ENOMEM);
452113584Ssimokawa			}
453106813Ssimokawa		}
454106813Ssimokawa
455106813Ssimokawa		ir->bnchunk = ibufreq->rx.nchunk;
456106813Ssimokawa		ir->bnpacket = ibufreq->rx.npacket;
457106813Ssimokawa		ir->psize = (ibufreq->rx.psize + 3) & ~3;
458106813Ssimokawa		ir->queued = 0;
459106813Ssimokawa
460106813Ssimokawa		it->bnchunk = ibufreq->tx.nchunk;
461106813Ssimokawa		it->bnpacket = ibufreq->tx.npacket;
462106813Ssimokawa		it->psize = (ibufreq->tx.psize + 3) & ~3;
463109890Ssimokawa		it->queued = 0;
464109890Ssimokawa
465106813Ssimokawa		STAILQ_INIT(&ir->stvalid);
466106813Ssimokawa		STAILQ_INIT(&ir->stfree);
467109890Ssimokawa		STAILQ_INIT(&ir->stdma);
468106813Ssimokawa		ir->stproc = NULL;
469106813Ssimokawa
470106813Ssimokawa		STAILQ_INIT(&it->stvalid);
471106813Ssimokawa		STAILQ_INIT(&it->stfree);
472109890Ssimokawa		STAILQ_INIT(&it->stdma);
473106813Ssimokawa		it->stproc = NULL;
474106813Ssimokawa
475106813Ssimokawa		for(i = 0 ; i < sc->fc->ir[sub]->bnchunk; i++){
476113584Ssimokawa			ir->bulkxfer[i].poffset = i * ir->bnpacket;
477113584Ssimokawa			ir->bulkxfer[i].mbuf = NULL;
478106813Ssimokawa			STAILQ_INSERT_TAIL(&ir->stfree,
479106813Ssimokawa					&ir->bulkxfer[i], link);
480106813Ssimokawa		}
481106813Ssimokawa		for(i = 0 ; i < sc->fc->it[sub]->bnchunk; i++){
482113584Ssimokawa			it->bulkxfer[i].poffset = i * it->bnpacket;
483113584Ssimokawa			it->bulkxfer[i].mbuf = NULL;
484106813Ssimokawa			STAILQ_INSERT_TAIL(&it->stfree,
485106813Ssimokawa					&it->bulkxfer[i], link);
486106813Ssimokawa		}
487106813Ssimokawa		ir->flag &= ~FWXFERQ_MODEMASK;
488106813Ssimokawa		ir->flag |= FWXFERQ_STREAM;
489106813Ssimokawa		ir->flag |= FWXFERQ_EXTBUF;
490106813Ssimokawa
491106813Ssimokawa		it->flag &= ~FWXFERQ_MODEMASK;
492106813Ssimokawa		it->flag |= FWXFERQ_STREAM;
493106813Ssimokawa		it->flag |= FWXFERQ_EXTBUF;
494106813Ssimokawa		err = 0;
495106813Ssimokawa		break;
496106813Ssimokawa	case FW_GSTBUF:
497106813Ssimokawa		ibufreq->rx.nchunk = sc->fc->ir[sub]->bnchunk;
498106813Ssimokawa		ibufreq->rx.npacket = sc->fc->ir[sub]->bnpacket;
499106813Ssimokawa		ibufreq->rx.psize = sc->fc->ir[sub]->psize;
500106813Ssimokawa
501106813Ssimokawa		ibufreq->tx.nchunk = sc->fc->it[sub]->bnchunk;
502106813Ssimokawa		ibufreq->tx.npacket = sc->fc->it[sub]->bnpacket;
503106813Ssimokawa		ibufreq->tx.psize = sc->fc->it[sub]->psize;
504106813Ssimokawa		break;
505106813Ssimokawa	case FW_ASYREQ:
506113584Ssimokawa		xfer = fw_xfer_alloc_buf(M_FWXFER, asyreq->req.len,
507113584Ssimokawa							PAGE_SIZE /* XXX */);
508106813Ssimokawa		if(xfer == NULL){
509106813Ssimokawa			err = ENOMEM;
510106813Ssimokawa			return err;
511106813Ssimokawa		}
512106813Ssimokawa		fp = &asyreq->pkt;
513106813Ssimokawa		switch (asyreq->req.type) {
514106813Ssimokawa		case FWASREQNODE:
515113584Ssimokawa			xfer->dst = fp->mode.hdr.dst;
516106813Ssimokawa			break;
517106813Ssimokawa		case FWASREQEUI:
518110072Ssimokawa			fwdev = fw_noderesolve_eui64(sc->fc,
519110582Ssimokawa						&asyreq->req.dst.eui);
520106813Ssimokawa			if (fwdev == NULL) {
521108782Ssimokawa				device_printf(sc->fc->bdev,
522108782Ssimokawa					"cannot find node\n");
523106813Ssimokawa				err = EINVAL;
524106813Ssimokawa				goto error;
525106813Ssimokawa			}
526113832Ssimokawa			xfer->dst = FWLOCALBUS | fwdev->dst;
527113832Ssimokawa			fp->mode.hdr.dst = xfer->dst;
528106813Ssimokawa			break;
529106813Ssimokawa		case FWASRESTL:
530106813Ssimokawa			/* XXX what's this? */
531106813Ssimokawa			break;
532106813Ssimokawa		case FWASREQSTREAM:
533106813Ssimokawa			/* nothing to do */
534106813Ssimokawa			break;
535106813Ssimokawa		}
536106813Ssimokawa		xfer->spd = asyreq->req.sped;
537106813Ssimokawa		bcopy(fp, xfer->send.buf, xfer->send.len);
538106813Ssimokawa		xfer->act.hand = fw_asy_callback;
539106813Ssimokawa		err = fw_asyreq(sc->fc, sub, xfer);
540106813Ssimokawa		if(err){
541106813Ssimokawa			fw_xfer_free( xfer);
542106813Ssimokawa			return err;
543106813Ssimokawa		}
544111748Sdes		err = tsleep(xfer, FWPRI, "asyreq", hz);
545106813Ssimokawa		if(err == 0){
546106813Ssimokawa			if(asyreq->req.len >= xfer->recv.len){
547106813Ssimokawa				asyreq->req.len = xfer->recv.len;
548106813Ssimokawa			}else{
549106813Ssimokawa				err = EINVAL;
550106813Ssimokawa			}
551113584Ssimokawa			bcopy(xfer->recv.buf, fp, asyreq->req.len);
552106813Ssimokawa		}
553106813Ssimokawaerror:
554106813Ssimokawa		fw_xfer_free( xfer);
555106813Ssimokawa		break;
556106813Ssimokawa	case FW_IBUSRST:
557106813Ssimokawa		sc->fc->ibr(sc->fc);
558106813Ssimokawa		break;
559106813Ssimokawa	case FW_CBINDADDR:
560106813Ssimokawa		fwb = fw_bindlookup(sc->fc,
561106813Ssimokawa				bindreq->start.hi, bindreq->start.lo);
562106813Ssimokawa		if(fwb == NULL){
563106813Ssimokawa			err = EINVAL;
564106813Ssimokawa			break;
565106813Ssimokawa		}
566106813Ssimokawa		STAILQ_REMOVE(&sc->fc->binds, fwb, fw_bind, fclist);
567106813Ssimokawa		STAILQ_REMOVE(&sc->fc->ir[sub]->binds, fwb, fw_bind, chlist);
568110195Ssimokawa		free(fwb, M_FW);
569106813Ssimokawa		break;
570106813Ssimokawa	case FW_SBINDADDR:
571106813Ssimokawa		if(bindreq->len <= 0 ){
572106813Ssimokawa			err = EINVAL;
573106813Ssimokawa			break;
574106813Ssimokawa		}
575106813Ssimokawa		if(bindreq->start.hi > 0xffff ){
576106813Ssimokawa			err = EINVAL;
577106813Ssimokawa			break;
578106813Ssimokawa		}
579110195Ssimokawa		fwb = (struct fw_bind *)malloc(sizeof (struct fw_bind), M_FW, M_NOWAIT);
580106813Ssimokawa		if(fwb == NULL){
581106813Ssimokawa			err = ENOMEM;
582106813Ssimokawa			break;
583106813Ssimokawa		}
584106813Ssimokawa		fwb->start_hi = bindreq->start.hi;
585106813Ssimokawa		fwb->start_lo = bindreq->start.lo;
586106813Ssimokawa		fwb->addrlen = bindreq->len;
587113584Ssimokawa		fwb->sub = sub;
588113584Ssimokawa		fwb->act_type = FWACT_CH;
589106813Ssimokawa
590110269Ssimokawa		xfer = fw_xfer_alloc(M_FWXFER);
591106813Ssimokawa		if(xfer == NULL){
592106813Ssimokawa			err = ENOMEM;
593106813Ssimokawa			return err;
594106813Ssimokawa		}
595106813Ssimokawa		xfer->fc = sc->fc;
596106813Ssimokawa
597113584Ssimokawa		s = splfw();
598113584Ssimokawa		/* XXX broken. need multiple xfer */
599113584Ssimokawa		STAILQ_INIT(&fwb->xferlist);
600113584Ssimokawa		STAILQ_INSERT_TAIL(&fwb->xferlist, xfer, link);
601113584Ssimokawa		splx(s);
602106813Ssimokawa		err = fw_bindadd(sc->fc, fwb);
603106813Ssimokawa		break;
604106813Ssimokawa	case FW_GDEVLST:
605109814Ssimokawa		i = len = 1;
606109814Ssimokawa		/* myself */
607109814Ssimokawa		devinfo = &fwdevlst->dev[0];
608109814Ssimokawa		devinfo->dst = sc->fc->nodeid;
609109814Ssimokawa		devinfo->status = 0;	/* XXX */
610109814Ssimokawa		devinfo->eui.hi = sc->fc->eui.hi;
611109814Ssimokawa		devinfo->eui.lo = sc->fc->eui.lo;
612110193Ssimokawa		STAILQ_FOREACH(fwdev, &sc->fc->devices, link) {
613109814Ssimokawa			if(len < FW_MAX_DEVLST){
614109814Ssimokawa				devinfo = &fwdevlst->dev[len++];
615109814Ssimokawa				devinfo->dst = fwdev->dst;
616109814Ssimokawa				devinfo->status =
617109814Ssimokawa					(fwdev->status == FWDEVINVAL)?0:1;
618109814Ssimokawa				devinfo->eui.hi = fwdev->eui.hi;
619109814Ssimokawa				devinfo->eui.lo = fwdev->eui.lo;
620106813Ssimokawa			}
621106813Ssimokawa			i++;
622106813Ssimokawa		}
623106813Ssimokawa		fwdevlst->n = i;
624109814Ssimokawa		fwdevlst->info_len = len;
625106813Ssimokawa		break;
626106813Ssimokawa	case FW_GTPMAP:
627106813Ssimokawa		bcopy(sc->fc->topology_map, data,
628106813Ssimokawa				(sc->fc->topology_map->crc_len + 1) * 4);
629106813Ssimokawa		break;
630106813Ssimokawa	case FW_GCROM:
631110193Ssimokawa		STAILQ_FOREACH(fwdev, &sc->fc->devices, link)
632110193Ssimokawa			if (FW_EUI64_EQUAL(fwdev->eui, crom_buf->eui))
633106813Ssimokawa				break;
634106813Ssimokawa		if (fwdev == NULL) {
635117473Ssimokawa			if (!FW_EUI64_EQUAL(sc->fc->eui, crom_buf->eui)) {
636117473Ssimokawa				err = FWNODE_INVAL;
637117473Ssimokawa				break;
638117473Ssimokawa			}
639117473Ssimokawa			/* myself */
640117473Ssimokawa			ptr = malloc(CROMSIZE, M_FW, M_WAITOK);
641117473Ssimokawa			len = CROMSIZE;
642117473Ssimokawa			for (i = 0; i < CROMSIZE/4; i++)
643117473Ssimokawa				((u_int32_t *)ptr)[i]
644117473Ssimokawa					= ntohl(sc->fc->config_rom[i]);
645117473Ssimokawa		} else {
646117473Ssimokawa			/* found */
647117473Ssimokawa			ptr = (void *)&fwdev->csrrom[0];
648117473Ssimokawa			if (fwdev->rommax < CSRROMOFF)
649117473Ssimokawa				len = 0;
650117473Ssimokawa			else
651117473Ssimokawa				len = fwdev->rommax - CSRROMOFF + 4;
652106813Ssimokawa		}
653106813Ssimokawa		if (crom_buf->len < len)
654106813Ssimokawa			len = crom_buf->len;
655106813Ssimokawa		else
656106813Ssimokawa			crom_buf->len = len;
657117473Ssimokawa		err = copyout(ptr, crom_buf->ptr, len);
658117473Ssimokawa		if (fwdev == NULL)
659117473Ssimokawa			/* myself */
660117473Ssimokawa			free(ptr, M_FW);
661106813Ssimokawa		break;
662106813Ssimokawa	default:
663106813Ssimokawa		sc->fc->ioctl (dev, cmd, data, flag, td);
664106813Ssimokawa		break;
665106813Ssimokawa	}
666106813Ssimokawa	return err;
667106813Ssimokawa}
668106813Ssimokawaint
669106813Ssimokawafw_poll(dev_t dev, int events, fw_proc *td)
670106813Ssimokawa{
671106813Ssimokawa	int revents;
672106813Ssimokawa	int tmp;
673106813Ssimokawa	int unit = DEV2UNIT(dev);
674106813Ssimokawa	int sub = DEV2DMACH(dev);
675106813Ssimokawa	struct firewire_softc *sc;
676106813Ssimokawa
677106813Ssimokawa	if (DEV_FWMEM(dev))
678106813Ssimokawa		return fwmem_poll(dev, events, td);
679106813Ssimokawa
680106813Ssimokawa	sc = devclass_get_softc(firewire_devclass, unit);
681106813Ssimokawa	revents = 0;
682106813Ssimokawa	tmp = POLLIN | POLLRDNORM;
683106813Ssimokawa	if (events & tmp) {
684106813Ssimokawa		if (STAILQ_FIRST(&sc->fc->ir[sub]->q) != NULL)
685106813Ssimokawa			revents |= tmp;
686106813Ssimokawa		else
687106813Ssimokawa			selrecord(td, &sc->fc->ir[sub]->rsel);
688106813Ssimokawa	}
689106813Ssimokawa	tmp = POLLOUT | POLLWRNORM;
690106813Ssimokawa	if (events & tmp) {
691106813Ssimokawa		/* XXX should be fixed */
692106813Ssimokawa		revents |= tmp;
693106813Ssimokawa	}
694106813Ssimokawa
695106813Ssimokawa	return revents;
696106813Ssimokawa}
697106813Ssimokawa
698106813Ssimokawastatic int
699113584Ssimokawa#if __FreeBSD_version < 500102
700111615Ssimokawafw_mmap (dev_t dev, vm_offset_t offset, int nproto)
701111615Ssimokawa#else
702113584Ssimokawafw_mmap (dev_t dev, vm_offset_t offset, vm_paddr_t *paddr, int nproto)
703111615Ssimokawa#endif
704106813Ssimokawa{
705106813Ssimokawa	struct firewire_softc *fc;
706106813Ssimokawa	int unit = DEV2UNIT(dev);
707106813Ssimokawa
708106813Ssimokawa	if (DEV_FWMEM(dev))
709113584Ssimokawa#if __FreeBSD_version < 500102
710111615Ssimokawa		return fwmem_mmap(dev, offset, nproto);
711111615Ssimokawa#else
712111462Smux		return fwmem_mmap(dev, offset, paddr, nproto);
713111615Ssimokawa#endif
714106813Ssimokawa
715106813Ssimokawa	fc = devclass_get_softc(firewire_devclass, unit);
716106813Ssimokawa
717106813Ssimokawa	return EINVAL;
718106813Ssimokawa}
719