fwdev.c revision 271795
1139749Simp/*-
2123120Simp * Copyright (c) 2003 Hidetoshi Shimokawa
3123120Simp * Copyright (c) 1998-2002 Katsushi Kobayashi and Hidetoshi Shimokawa
4123120Simp * All rights reserved.
5123120Simp *
6123120Simp * Redistribution and use in source and binary forms, with or without
7123120Simp * modification, are permitted provided that the following conditions
8123120Simp * are met:
9123120Simp * 1. Redistributions of source code must retain the above copyright
10123120Simp *    notice, this list of conditions and the following disclaimer.
11123120Simp * 2. Redistributions in binary form must reproduce the above copyright
12123120Simp *    notice, this list of conditions and the following disclaimer in the
13123120Simp *    documentation and/or other materials provided with the distribution.
14123120Simp * 3. All advertising materials mentioning features or use of this software
15123120Simp *    must display the acknowledgement as bellow:
16123120Simp *
17123120Simp *    This product includes software developed by K. Kobayashi and H. Shimokawa
18123120Simp *
19123120Simp * 4. The name of the author may not be used to endorse or promote products
20123120Simp *    derived from this software without specific prior written permission.
21123120Simp *
22123120Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23123120Simp * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24123120Simp * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25123120Simp * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
26123120Simp * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27123120Simp * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28123120Simp * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29123120Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
30123120Simp * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31123120Simp * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32123120Simp * POSSIBILITY OF SUCH DAMAGE.
33123120Simp *
34123120Simp * $FreeBSD: head/sys/dev/firewire/fwdev.c 271795 2014-09-18 17:28:21Z will $
35123120Simp *
36123120Simp */
37123120Simp
38123120Simp#include <sys/param.h>
39123120Simp#include <sys/systm.h>
40123120Simp#include <sys/types.h>
41123120Simp#include <sys/mbuf.h>
42123120Simp#include <sys/bio.h>
43123120Simp
44123120Simp#include <sys/kernel.h>
45123120Simp#include <sys/malloc.h>
46123120Simp#include <sys/conf.h>
47123120Simp#include <sys/poll.h>
48123120Simp
49123120Simp#include <sys/bus.h>
50123120Simp#include <sys/ctype.h>
51123120Simp#include <machine/bus.h>
52123120Simp
53123120Simp#include <sys/ioccom.h>
54123120Simp
55123120Simp#ifdef __DragonFly__
56123120Simp#include "firewire.h"
57123120Simp#include "firewirereg.h"
58123120Simp#include "fwdma.h"
59123120Simp#include "fwmem.h"
60123120Simp#include "iec68113.h"
61123120Simp#else
62123120Simp#include <dev/firewire/firewire.h>
63123120Simp#include <dev/firewire/firewirereg.h>
64123120Simp#include <dev/firewire/fwdma.h>
65123120Simp#include <dev/firewire/fwmem.h>
66123120Simp#include <dev/firewire/iec68113.h>
67123120Simp#endif
68123120Simp
69123120Simp#define	FWNODE_INVAL 0xffff
70123120Simp
71123120Simpstatic	d_open_t	fw_open;
72123120Simpstatic	d_close_t	fw_close;
73123120Simpstatic	d_ioctl_t	fw_ioctl;
74123120Simpstatic	d_poll_t	fw_poll;
75123120Simpstatic	d_read_t	fw_read;	/* for Isochronous packet */
76123120Simpstatic	d_write_t	fw_write;
77123120Simpstatic	d_mmap_t	fw_mmap;
78123120Simpstatic	d_strategy_t	fw_strategy;
79123120Simp
80123120Simpstruct cdevsw firewire_cdevsw = {
81123120Simp#ifdef __DragonFly__
82123120Simp#define CDEV_MAJOR 127
83123120Simp	"fw", CDEV_MAJOR, D_MEM, NULL, 0,
84123120Simp	fw_open, fw_close, fw_read, fw_write, fw_ioctl,
85123120Simp	fw_poll, fw_mmap, fw_strategy, nodump, nopsize,
86123120Simp#elif __FreeBSD_version >= 500104
87123120Simp	.d_version =	D_VERSION,
88123120Simp	.d_open =	fw_open,
89123120Simp	.d_close =	fw_close,
90123120Simp	.d_read =	fw_read,
91123120Simp	.d_write =	fw_write,
92123120Simp	.d_ioctl =	fw_ioctl,
93123120Simp	.d_poll =	fw_poll,
94123120Simp	.d_mmap =	fw_mmap,
95123120Simp	.d_strategy =	fw_strategy,
96123120Simp	.d_name =	"fw",
97123120Simp	.d_flags =	D_MEM
98123120Simp#else
99123120Simp#define CDEV_MAJOR 127
100123120Simp	fw_open, fw_close, fw_read, fw_write, fw_ioctl,
101123120Simp	fw_poll, fw_mmap, fw_strategy, "fw", CDEV_MAJOR,
102123120Simp	nodump, nopsize, D_MEM, -1
103123120Simp#endif
104123120Simp};
105123120Simp
106123120Simpstruct fw_drv1 {
107123120Simp	struct firewire_comm *fc;
108123120Simp	struct fw_xferq *ir;
109123120Simp	struct fw_xferq *it;
110123120Simp	struct fw_isobufreq bufreq;
111123120Simp	STAILQ_HEAD(, fw_bind) binds;
112123120Simp	STAILQ_HEAD(, fw_xfer) rq;
113123120Simp};
114123120Simp
115123120Simpstatic int
116123120Simpfwdev_allocbuf(struct firewire_comm *fc, struct fw_xferq *q,
117123120Simp	struct fw_bufspec *b)
118123120Simp{
119123120Simp	int i;
120123120Simp
121123120Simp	if (q->flag & (FWXFERQ_RUNNING | FWXFERQ_EXTBUF))
122123120Simp		return(EBUSY);
123123120Simp
124123120Simp	q->bulkxfer = (struct fw_bulkxfer *) malloc(
125123120Simp		sizeof(struct fw_bulkxfer) * b->nchunk,
126123120Simp		M_FW, M_WAITOK);
127123120Simp	if (q->bulkxfer == NULL)
128123120Simp		return(ENOMEM);
129123120Simp
130123120Simp	b->psize = roundup2(b->psize, sizeof(uint32_t));
131123120Simp	q->buf = fwdma_malloc_multiseg(fc, sizeof(uint32_t),
132123120Simp			b->psize, b->nchunk * b->npacket, BUS_DMA_WAITOK);
133123120Simp
134123120Simp	if (q->buf == NULL) {
135123120Simp		free(q->bulkxfer, M_FW);
136123120Simp		q->bulkxfer = NULL;
137123120Simp		return(ENOMEM);
138123120Simp	}
139123120Simp	q->bnchunk = b->nchunk;
140123120Simp	q->bnpacket = b->npacket;
141123120Simp	q->psize = (b->psize + 3) & ~3;
142123120Simp	q->queued = 0;
143123120Simp
144123120Simp	STAILQ_INIT(&q->stvalid);
145123120Simp	STAILQ_INIT(&q->stfree);
146123120Simp	STAILQ_INIT(&q->stdma);
147123120Simp	q->stproc = NULL;
148123120Simp
149123120Simp	for(i = 0 ; i < q->bnchunk; i++){
150123120Simp		q->bulkxfer[i].poffset = i * q->bnpacket;
151123120Simp		q->bulkxfer[i].mbuf = NULL;
152123120Simp		STAILQ_INSERT_TAIL(&q->stfree, &q->bulkxfer[i], link);
153123120Simp	}
154123120Simp
155123120Simp	q->flag &= ~FWXFERQ_MODEMASK;
156123120Simp	q->flag |= FWXFERQ_STREAM;
157123120Simp	q->flag |= FWXFERQ_EXTBUF;
158123120Simp
159123120Simp	return (0);
160123120Simp}
161123120Simp
162123120Simpstatic int
163123120Simpfwdev_freebuf(struct fw_xferq *q)
164123120Simp{
165123120Simp	if (q->flag & FWXFERQ_EXTBUF) {
166123120Simp		if (q->buf != NULL)
167123120Simp			fwdma_free_multiseg(q->buf);
168123120Simp		q->buf = NULL;
169123120Simp		free(q->bulkxfer, M_FW);
170123120Simp		q->bulkxfer = NULL;
171123120Simp		q->flag &= ~FWXFERQ_EXTBUF;
172123120Simp		q->psize = 0;
173123120Simp		q->maxq = FWMAXQUEUE;
174123120Simp	}
175123120Simp	return (0);
176123120Simp}
177123120Simp
178123120Simp
179123120Simpstatic int
180123120Simpfw_open (struct cdev *dev, int flags, int fmt, fw_proc *td)
181123120Simp{
182123120Simp	int err = 0;
183123120Simp	int unit = DEV2UNIT(dev);
184123120Simp	struct fw_drv1 *d;
185123120Simp	struct firewire_softc *sc;
186123120Simp
187123120Simp	if (DEV_FWMEM(dev))
188123120Simp		return fwmem_open(dev, flags, fmt, td);
189123120Simp
190123120Simp	sc = devclass_get_softc(firewire_devclass, unit);
191123120Simp	if (sc == NULL)
192123120Simp		return (ENXIO);
193123120Simp
194123120Simp	FW_GLOCK(sc->fc);
195123120Simp	if (dev->si_drv1 != NULL) {
196123120Simp		FW_GUNLOCK(sc->fc);
197123120Simp		return (EBUSY);
198123120Simp	}
199123120Simp	/* set dummy value for allocation */
200123120Simp	dev->si_drv1 = (void *)-1;
201123120Simp	FW_GUNLOCK(sc->fc);
202123120Simp
203123120Simp	dev->si_drv1 = malloc(sizeof(struct fw_drv1), M_FW, M_WAITOK | M_ZERO);
204123120Simp	if (dev->si_drv1 == NULL)
205123120Simp		return (ENOMEM);
206123120Simp
207123120Simp	if ((dev->si_flags & SI_NAMED) == 0) {
208123120Simp		int unit = DEV2UNIT(dev);
209123120Simp		int sub = DEV2SUB(dev);
210123120Simp
211123120Simp		make_dev(&firewire_cdevsw, dev2unit(dev),
212123120Simp			UID_ROOT, GID_OPERATOR, 0660,
213123120Simp			"fw%d.%d", unit, sub);
214123120Simp	}
215123120Simp	d = (struct fw_drv1 *)dev->si_drv1;
216123120Simp	d->fc = sc->fc;
217123120Simp	STAILQ_INIT(&d->binds);
218123120Simp	STAILQ_INIT(&d->rq);
219123120Simp
220123120Simp	return err;
221123120Simp}
222123120Simp
223123120Simpstatic int
224123120Simpfw_close (struct cdev *dev, int flags, int fmt, fw_proc *td)
225123120Simp{
226123120Simp	struct firewire_comm *fc;
227123120Simp	struct fw_drv1 *d;
228123120Simp	struct fw_xfer *xfer;
229123120Simp	struct fw_bind *fwb;
230123120Simp	int err = 0;
231123120Simp
232123120Simp	if (DEV_FWMEM(dev))
233123120Simp		return fwmem_close(dev, flags, fmt, td);
234123120Simp
235123120Simp	d = (struct fw_drv1 *)dev->si_drv1;
236123120Simp	fc = d->fc;
237123120Simp
238123120Simp	/* remove binding */
239123120Simp	for (fwb = STAILQ_FIRST(&d->binds); fwb != NULL;
240123120Simp			fwb = STAILQ_FIRST(&d->binds)) {
241123120Simp		fw_bindremove(fc, fwb);
242123120Simp		STAILQ_REMOVE_HEAD(&d->binds, chlist);
243123120Simp		fw_xferlist_remove(&fwb->xferlist);
244123120Simp		free(fwb, M_FW);
245123120Simp	}
246123120Simp	if (d->ir != NULL) {
247123120Simp		struct fw_xferq *ir = d->ir;
248123120Simp
249123120Simp		if ((ir->flag & FWXFERQ_OPEN) == 0)
250123120Simp			return (EINVAL);
251123120Simp		if (ir->flag & FWXFERQ_RUNNING) {
252123120Simp			ir->flag &= ~FWXFERQ_RUNNING;
253123120Simp			fc->irx_disable(fc, ir->dmach);
254123120Simp		}
255123120Simp		/* free extbuf */
256123120Simp		fwdev_freebuf(ir);
257123120Simp		/* drain receiving buffer */
258123120Simp		for (xfer = STAILQ_FIRST(&ir->q);
259123120Simp			xfer != NULL; xfer = STAILQ_FIRST(&ir->q)) {
260123120Simp			ir->queued --;
261123120Simp			STAILQ_REMOVE_HEAD(&ir->q, link);
262123120Simp
263123120Simp			xfer->resp = 0;
264123120Simp			fw_xfer_done(xfer);
265123120Simp		}
266123120Simp		ir->flag &= ~(FWXFERQ_OPEN |
267123120Simp			FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK);
268123120Simp		d->ir = NULL;
269123120Simp
270123120Simp	}
271123120Simp	if (d->it != NULL) {
272123120Simp		struct fw_xferq *it = d->it;
273123120Simp
274123120Simp		if ((it->flag & FWXFERQ_OPEN) == 0)
275123120Simp			return (EINVAL);
276123120Simp		if (it->flag & FWXFERQ_RUNNING) {
277123120Simp			it->flag &= ~FWXFERQ_RUNNING;
278123120Simp			fc->itx_disable(fc, it->dmach);
279123120Simp		}
280123120Simp		/* free extbuf */
281123120Simp		fwdev_freebuf(it);
282123120Simp		it->flag &= ~(FWXFERQ_OPEN |
283123120Simp			FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK);
284123120Simp		d->it = NULL;
285123120Simp	}
286123120Simp	free(dev->si_drv1, M_FW);
287123120Simp	dev->si_drv1 = NULL;
288123120Simp
289123120Simp	return err;
290123120Simp}
291123120Simp
292123120Simpstatic int
293123120Simpfw_read_async(struct fw_drv1 *d, struct uio *uio, int ioflag)
294123120Simp{
295123120Simp	int err = 0, s;
296123120Simp	struct fw_xfer *xfer;
297123120Simp	struct fw_bind *fwb;
298123120Simp	struct fw_pkt *fp;
299123120Simp	struct tcode_info *tinfo;
300123120Simp
301123120Simp	FW_GLOCK(d->fc);
302123120Simp	while ((xfer = STAILQ_FIRST(&d->rq)) == NULL && err == 0)
303123120Simp		err = msleep(&d->rq, FW_GMTX(d->fc), FWPRI, "fwra", 0);
304123120Simp
305123120Simp	if (err != 0) {
306123120Simp		FW_GUNLOCK(d->fc);
307123120Simp		return (err);
308123120Simp	}
309123120Simp
310123120Simp	s = splfw();
311123120Simp	STAILQ_REMOVE_HEAD(&d->rq, link);
312123120Simp	FW_GUNLOCK(xfer->fc);
313123120Simp	splx(s);
314123120Simp	fp = &xfer->recv.hdr;
315123120Simp#if 0 /* for GASP ?? */
316123120Simp	if (fc->irx_post != NULL)
317123120Simp		fc->irx_post(fc, fp->mode.ld);
318123120Simp#endif
319123120Simp	tinfo = &xfer->fc->tcode[fp->mode.hdr.tcode];
320123120Simp	err = uiomove((void *)fp, tinfo->hdr_len, uio);
321123120Simp	if (err)
322123120Simp		goto out;
323123120Simp	err = uiomove((void *)xfer->recv.payload, xfer->recv.pay_len, uio);
324123120Simp
325123120Simpout:
326123120Simp	/* recycle this xfer */
327123120Simp	fwb = (struct fw_bind *)xfer->sc;
328123120Simp	fw_xfer_unload(xfer);
329123120Simp	xfer->recv.pay_len = PAGE_SIZE;
330123120Simp	FW_GLOCK(xfer->fc);
331123120Simp	STAILQ_INSERT_TAIL(&fwb->xferlist, xfer, link);
332123120Simp	FW_GUNLOCK(xfer->fc);
333123120Simp	return (err);
334123120Simp}
335123120Simp
336123120Simp/*
337123120Simp * read request.
338123120Simp */
339123120Simpstatic int
340123120Simpfw_read (struct cdev *dev, struct uio *uio, int ioflag)
341123120Simp{
342123120Simp	struct fw_drv1 *d;
343123120Simp	struct fw_xferq *ir;
344123120Simp	struct firewire_comm *fc;
345123120Simp	int err = 0, s, slept = 0;
346123120Simp	struct fw_pkt *fp;
347123120Simp
348123120Simp	if (DEV_FWMEM(dev))
349123120Simp		return (physio(dev, uio, ioflag));
350123120Simp
351123120Simp	d = (struct fw_drv1 *)dev->si_drv1;
352123120Simp	fc = d->fc;
353123120Simp	ir = d->ir;
354123120Simp
355123120Simp	if (ir == NULL)
356123120Simp		return (fw_read_async(d, uio, ioflag));
357123120Simp
358123120Simp	if (ir->buf == NULL)
359123120Simp		return (EIO);
360123120Simp
361123120Simp	FW_GLOCK(fc);
362123120Simpreadloop:
363123120Simp	if (ir->stproc == NULL) {
364123120Simp		/* iso bulkxfer */
365123120Simp		ir->stproc = STAILQ_FIRST(&ir->stvalid);
366123120Simp		if (ir->stproc != NULL) {
367123120Simp			s = splfw();
368123120Simp			STAILQ_REMOVE_HEAD(&ir->stvalid, link);
369123120Simp			splx(s);
370123120Simp			ir->queued = 0;
371123120Simp		}
372123120Simp	}
373123120Simp	if (ir->stproc == NULL) {
374123120Simp		/* no data avaliable */
375123120Simp		if (slept == 0) {
376123120Simp			slept = 1;
377123120Simp			ir->flag |= FWXFERQ_WAKEUP;
378123120Simp			err = msleep(ir, FW_GMTX(fc), FWPRI, "fw_read", hz);
379123120Simp			ir->flag &= ~FWXFERQ_WAKEUP;
380123120Simp			if (err == 0)
381123120Simp				goto readloop;
382123120Simp		} else if (slept == 1)
383123120Simp			err = EIO;
384123120Simp		FW_GUNLOCK(fc);
385123120Simp		return err;
386123120Simp	} else if(ir->stproc != NULL) {
387123120Simp		/* iso bulkxfer */
388123120Simp		FW_GUNLOCK(fc);
389123120Simp		fp = (struct fw_pkt *)fwdma_v_addr(ir->buf,
390123120Simp				ir->stproc->poffset + ir->queued);
391123120Simp		if(fc->irx_post != NULL)
392123120Simp			fc->irx_post(fc, fp->mode.ld);
393123120Simp		if(fp->mode.stream.len == 0){
394123120Simp			err = EIO;
395123120Simp			return err;
396123120Simp		}
397123120Simp		err = uiomove((caddr_t)fp,
398123120Simp			fp->mode.stream.len + sizeof(uint32_t), uio);
399123120Simp		ir->queued ++;
400123120Simp		if(ir->queued >= ir->bnpacket){
401123120Simp			s = splfw();
402123120Simp			STAILQ_INSERT_TAIL(&ir->stfree, ir->stproc, link);
403123120Simp			splx(s);
404123120Simp			fc->irx_enable(fc, ir->dmach);
405123120Simp			ir->stproc = NULL;
406123120Simp		}
407123120Simp		if (uio->uio_resid >= ir->psize) {
408123120Simp			slept = -1;
409123120Simp			FW_GLOCK(fc);
410123120Simp			goto readloop;
411123120Simp		}
412123120Simp	}
413123120Simp	return err;
414123120Simp}
415123120Simp
416123120Simpstatic int
417123120Simpfw_write_async(struct fw_drv1 *d, struct uio *uio, int ioflag)
418123120Simp{
419123120Simp	struct fw_xfer *xfer;
420123120Simp	struct fw_pkt pkt;
421123120Simp	struct tcode_info *tinfo;
422123120Simp	int err;
423123120Simp
424123120Simp	bzero(&pkt, sizeof(struct fw_pkt));
425123120Simp	if ((err = uiomove((caddr_t)&pkt, sizeof(uint32_t), uio)))
426123120Simp		return (err);
427123120Simp	tinfo = &d->fc->tcode[pkt.mode.hdr.tcode];
428123120Simp	if ((err = uiomove((caddr_t)&pkt + sizeof(uint32_t),
429123120Simp	    tinfo->hdr_len - sizeof(uint32_t), uio)))
430123120Simp		return (err);
431123120Simp
432123120Simp	if ((xfer = fw_xfer_alloc_buf(M_FWXFER, uio->uio_resid,
433123120Simp	    PAGE_SIZE/*XXX*/)) == NULL)
434123120Simp		return (ENOMEM);
435123120Simp
436123120Simp	bcopy(&pkt, &xfer->send.hdr, sizeof(struct fw_pkt));
437123120Simp	xfer->send.pay_len = uio->uio_resid;
438123120Simp	if (uio->uio_resid > 0) {
439123120Simp		if ((err = uiomove((caddr_t)&xfer->send.payload[0],
440123120Simp		    uio->uio_resid, uio)))
441123120Simp			goto out;
442123120Simp	}
443315221Spfg
444123120Simp	xfer->fc = d->fc;
445123120Simp	xfer->sc = NULL;
446123120Simp	xfer->hand = fw_xferwake;
447123120Simp	xfer->send.spd = 2 /* XXX */;
448123120Simp
449123120Simp	if ((err = fw_asyreq(xfer->fc, -1, xfer)))
450123120Simp		goto out;
451123120Simp
452123120Simp	if ((err = fw_xferwait(xfer)))
453123120Simp		goto out;
454123120Simp
455123120Simp	if (xfer->resp != 0) {
456123120Simp		err = xfer->resp;
457123120Simp		goto out;
458123120Simp	}
459123120Simp
460123120Simp	if (xfer->flag & FWXF_RCVD) {
461123120Simp		FW_GLOCK(xfer->fc);
462123120Simp		STAILQ_INSERT_TAIL(&d->rq, xfer, link);
463123120Simp		FW_GUNLOCK(xfer->fc);
464123120Simp		return (0);
465123120Simp	}
466123120Simp
467123120Simpout:
468123120Simp	fw_xfer_free(xfer);
469123120Simp	return (err);
470123120Simp}
471123120Simp
472123120Simpstatic int
473123120Simpfw_write (struct cdev *dev, struct uio *uio, int ioflag)
474123120Simp{
475123120Simp	int err = 0;
476123120Simp	int s, slept = 0;
477123120Simp	struct fw_drv1 *d;
478123120Simp	struct fw_pkt *fp;
479123120Simp	struct firewire_comm *fc;
480123120Simp	struct fw_xferq *it;
481123120Simp
482123120Simp	if (DEV_FWMEM(dev))
483123120Simp		return (physio(dev, uio, ioflag));
484123120Simp
485123120Simp	d = (struct fw_drv1 *)dev->si_drv1;
486123120Simp	fc = d->fc;
487123120Simp	it = d->it;
488123120Simp
489123120Simp	if (it == NULL)
490123120Simp		return (fw_write_async(d, uio, ioflag));
491123120Simp
492123120Simp	if (it->buf == NULL)
493123120Simp		return (EIO);
494123120Simp
495123120Simp	FW_GLOCK(fc);
496123120Simpisoloop:
497123120Simp	if (it->stproc == NULL) {
498123120Simp		it->stproc = STAILQ_FIRST(&it->stfree);
499123120Simp		if (it->stproc != NULL) {
500123120Simp			s = splfw();
501123120Simp			STAILQ_REMOVE_HEAD(&it->stfree, link);
502123120Simp			splx(s);
503123120Simp			it->queued = 0;
504123120Simp		} else if (slept == 0) {
505123120Simp			slept = 1;
506123120Simp#if 0	/* XXX to avoid lock recursion */
507123120Simp			err = fc->itx_enable(fc, it->dmach);
508123120Simp			if (err)
509123120Simp				goto out;
510123120Simp#endif
511123120Simp			err = msleep(it, FW_GMTX(fc), FWPRI, "fw_write", hz);
512123120Simp			if (err)
513123120Simp				goto out;
514123120Simp			goto isoloop;
515123120Simp		} else {
516123120Simp			err = EIO;
517123120Simp			goto out;
518123120Simp		}
519123120Simp	}
520123120Simp	FW_GUNLOCK(fc);
521123120Simp	fp = (struct fw_pkt *)fwdma_v_addr(it->buf,
522123120Simp			it->stproc->poffset + it->queued);
523123120Simp	err = uiomove((caddr_t)fp, sizeof(struct fw_isohdr), uio);
524123120Simp	err = uiomove((caddr_t)fp->mode.stream.payload,
525123120Simp				fp->mode.stream.len, uio);
526123120Simp	it->queued ++;
527123120Simp	if (it->queued >= it->bnpacket) {
528123120Simp		s = splfw();
529123120Simp		STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link);
530123120Simp		splx(s);
531123120Simp		it->stproc = NULL;
532123120Simp		err = fc->itx_enable(fc, it->dmach);
533123120Simp	}
534123120Simp	if (uio->uio_resid >= sizeof(struct fw_isohdr)) {
535123120Simp		slept = 0;
536123120Simp		FW_GLOCK(fc);
537123120Simp		goto isoloop;
538123120Simp	}
539123120Simp	return err;
540123120Simp
541123120Simpout:
542123120Simp	FW_GUNLOCK(fc);
543123120Simp	return err;
544123120Simp}
545123120Simp
546123120Simpstatic void
547123120Simpfw_hand(struct fw_xfer *xfer)
548123120Simp{
549123120Simp	struct fw_bind *fwb;
550123120Simp	struct fw_drv1 *d;
551123120Simp
552123120Simp	fwb = (struct fw_bind *)xfer->sc;
553123120Simp	d = (struct fw_drv1 *)fwb->sc;
554123120Simp	FW_GLOCK(xfer->fc);
555123120Simp	STAILQ_INSERT_TAIL(&d->rq, xfer, link);
556123120Simp	FW_GUNLOCK(xfer->fc);
557123120Simp	wakeup(&d->rq);
558123120Simp}
559123120Simp
560123120Simp/*
561123120Simp * ioctl support.
562123120Simp */
563123120Simpint
564123120Simpfw_ioctl (struct cdev *dev, u_long cmd, caddr_t data, int flag, fw_proc *td)
565123120Simp{
566123120Simp	struct firewire_comm *fc;
567123120Simp	struct fw_drv1 *d;
568123120Simp	int i, len, err = 0;
569123120Simp	struct fw_device *fwdev;
570123120Simp	struct fw_bind *fwb;
571123120Simp	struct fw_xferq *ir, *it;
572123120Simp	struct fw_xfer *xfer;
573123120Simp	struct fw_pkt *fp;
574123120Simp	struct fw_devinfo *devinfo;
575123120Simp	void *ptr;
576123120Simp
577123120Simp	struct fw_devlstreq *fwdevlst = (struct fw_devlstreq *)data;
578123120Simp	struct fw_asyreq *asyreq = (struct fw_asyreq *)data;
579123120Simp	struct fw_isochreq *ichreq = (struct fw_isochreq *)data;
580123120Simp	struct fw_isobufreq *ibufreq = (struct fw_isobufreq *)data;
581123120Simp	struct fw_asybindreq *bindreq = (struct fw_asybindreq *)data;
582123120Simp	struct fw_crom_buf *crom_buf = (struct fw_crom_buf *)data;
583123120Simp
584123120Simp	if (DEV_FWMEM(dev))
585123120Simp		return fwmem_ioctl(dev, cmd, data, flag, td);
586123120Simp
587123120Simp	if (!data)
588123120Simp		return(EINVAL);
589123120Simp
590123120Simp	d = (struct fw_drv1 *)dev->si_drv1;
591123120Simp	fc = d->fc;
592123120Simp	ir = d->ir;
593123120Simp	it = d->it;
594123120Simp
595123120Simp	switch (cmd) {
596123120Simp	case FW_STSTREAM:
597123120Simp		if (it == NULL) {
598123120Simp			i = fw_open_isodma(fc, /* tx */1);
599123120Simp			if (i < 0) {
600123120Simp				err = EBUSY;
601123120Simp				break;
602123120Simp			}
603123120Simp			it = fc->it[i];
604123120Simp			err = fwdev_allocbuf(fc, it, &d->bufreq.tx);
605123120Simp			if (err) {
606123120Simp				it->flag &= ~FWXFERQ_OPEN;
607123120Simp				break;
608123120Simp			}
609123120Simp		}
610123120Simp		it->flag &= ~0xff;
611123120Simp		it->flag |= (0x3f & ichreq->ch);
612123120Simp		it->flag |= ((0x3 & ichreq->tag) << 6);
613123120Simp		d->it = it;
614123120Simp		break;
615123120Simp	case FW_GTSTREAM:
616123120Simp		if (it != NULL) {
617123120Simp			ichreq->ch = it->flag & 0x3f;
618123120Simp			ichreq->tag = it->flag >> 2 & 0x3;
619123120Simp		} else
620123120Simp			err = EINVAL;
621123120Simp		break;
622123120Simp	case FW_SRSTREAM:
623123120Simp		if (ir == NULL) {
624123120Simp			i = fw_open_isodma(fc, /* tx */0);
625123120Simp			if (i < 0) {
626123120Simp				err = EBUSY;
627123120Simp				break;
628123120Simp			}
629123120Simp			ir = fc->ir[i];
630123120Simp			err = fwdev_allocbuf(fc, ir, &d->bufreq.rx);
631123120Simp			if (err) {
632123120Simp				ir->flag &= ~FWXFERQ_OPEN;
633123120Simp				break;
634123120Simp			}
635123120Simp		}
636123120Simp		ir->flag &= ~0xff;
637123120Simp		ir->flag |= (0x3f & ichreq->ch);
638123120Simp		ir->flag |= ((0x3 & ichreq->tag) << 6);
639123120Simp		d->ir = ir;
640123120Simp		err = fc->irx_enable(fc, ir->dmach);
641123120Simp		break;
642123120Simp	case FW_GRSTREAM:
643123120Simp		if (d->ir != NULL) {
644123120Simp			ichreq->ch = ir->flag & 0x3f;
645123120Simp			ichreq->tag = ir->flag >> 2 & 0x3;
646123120Simp		} else
647123120Simp			err = EINVAL;
648123120Simp		break;
649123120Simp	case FW_SSTBUF:
650123120Simp		bcopy(ibufreq, &d->bufreq, sizeof(d->bufreq));
651123120Simp		break;
652123120Simp	case FW_GSTBUF:
653123120Simp		bzero(&ibufreq->rx, sizeof(ibufreq->rx));
654123120Simp		if (ir != NULL) {
655123120Simp			ibufreq->rx.nchunk = ir->bnchunk;
656123120Simp			ibufreq->rx.npacket = ir->bnpacket;
657123120Simp			ibufreq->rx.psize = ir->psize;
658123120Simp		}
659123120Simp		bzero(&ibufreq->tx, sizeof(ibufreq->tx));
660123120Simp		if (it != NULL) {
661123120Simp			ibufreq->tx.nchunk = it->bnchunk;
662123120Simp			ibufreq->tx.npacket = it->bnpacket;
663123120Simp			ibufreq->tx.psize = it->psize;
664123120Simp		}
665123120Simp		break;
666123120Simp	case FW_ASYREQ:
667123120Simp	{
668123120Simp		struct tcode_info *tinfo;
669123120Simp		int pay_len = 0;
670123120Simp
671123120Simp		fp = &asyreq->pkt;
672123120Simp		tinfo = &fc->tcode[fp->mode.hdr.tcode];
673123120Simp
674123120Simp		if ((tinfo->flag & FWTI_BLOCK_ASY) != 0)
675123120Simp			pay_len = MAX(0, asyreq->req.len - tinfo->hdr_len);
676123120Simp
677123120Simp		xfer = fw_xfer_alloc_buf(M_FWXFER, pay_len, PAGE_SIZE/*XXX*/);
678123120Simp		if (xfer == NULL)
679123120Simp			return (ENOMEM);
680123120Simp
681123120Simp		switch (asyreq->req.type) {
682123120Simp		case FWASREQNODE:
683123120Simp			break;
684123120Simp		case FWASREQEUI:
685123120Simp			fwdev = fw_noderesolve_eui64(fc,
686123120Simp						&asyreq->req.dst.eui);
687123120Simp			if (fwdev == NULL) {
688123120Simp				device_printf(fc->bdev,
689123120Simp					"cannot find node\n");
690123120Simp				err = EINVAL;
691123120Simp				goto out;
692123120Simp			}
693123120Simp			fp->mode.hdr.dst = FWLOCALBUS | fwdev->dst;
694123120Simp			break;
695123120Simp		case FWASRESTL:
696123120Simp			/* XXX what's this? */
697123120Simp			break;
698123120Simp		case FWASREQSTREAM:
699123120Simp			/* nothing to do */
700123120Simp			break;
701123120Simp		}
702123120Simp
703123120Simp		bcopy(fp, (void *)&xfer->send.hdr, tinfo->hdr_len);
704123120Simp		if (pay_len > 0)
705123120Simp			bcopy((char *)fp + tinfo->hdr_len,
706123120Simp			    (void *)xfer->send.payload, pay_len);
707123120Simp		xfer->send.spd = asyreq->req.sped;
708123120Simp		xfer->hand = fw_xferwake;
709123120Simp
710123120Simp		if ((err = fw_asyreq(fc, -1, xfer)) != 0)
711123120Simp			goto out;
712123120Simp		if ((err = fw_xferwait(xfer)) != 0)
713123120Simp			goto out;
714123120Simp		if (xfer->resp != 0) {
715123120Simp			err = EIO;
716123120Simp			goto out;
717123120Simp		}
718123120Simp		if ((tinfo->flag & FWTI_TLABEL) == 0)
719123120Simp			goto out;
720123120Simp
721123120Simp		/* copy response */
722123120Simp		tinfo = &fc->tcode[xfer->recv.hdr.mode.hdr.tcode];
723123120Simp		if (xfer->recv.hdr.mode.hdr.tcode == FWTCODE_RRESB ||
724123120Simp		    xfer->recv.hdr.mode.hdr.tcode == FWTCODE_LRES) {
725123120Simp			pay_len = xfer->recv.pay_len;
726123120Simp			if (asyreq->req.len >= xfer->recv.pay_len + tinfo->hdr_len) {
727123120Simp				asyreq->req.len = xfer->recv.pay_len +
728123120Simp					tinfo->hdr_len;
729123120Simp			} else {
730123120Simp				err = EINVAL;
731123120Simp				pay_len = 0;
732123120Simp			}
733123120Simp		} else {
734123120Simp			pay_len = 0;
735123120Simp		}
736123120Simp		bcopy(&xfer->recv.hdr, fp, tinfo->hdr_len);
737123120Simp		bcopy(xfer->recv.payload, (char *)fp + tinfo->hdr_len, pay_len);
738123120Simpout:
739123120Simp		fw_xfer_free_buf(xfer);
740123120Simp		break;
741123120Simp	}
742123120Simp	case FW_IBUSRST:
743123120Simp		fc->ibr(fc);
744123120Simp		break;
745123120Simp	case FW_CBINDADDR:
746123120Simp		fwb = fw_bindlookup(fc,
747123120Simp				bindreq->start.hi, bindreq->start.lo);
748123120Simp		if(fwb == NULL){
749123120Simp			err = EINVAL;
750123120Simp			break;
751123120Simp		}
752123120Simp		fw_bindremove(fc, fwb);
753123120Simp		STAILQ_REMOVE(&d->binds, fwb, fw_bind, chlist);
754123120Simp		fw_xferlist_remove(&fwb->xferlist);
755123120Simp		free(fwb, M_FW);
756123120Simp		break;
757123120Simp	case FW_SBINDADDR:
758123120Simp		if(bindreq->len <= 0 ){
759123120Simp			err = EINVAL;
760123120Simp			break;
761123120Simp		}
762123120Simp		if(bindreq->start.hi > 0xffff ){
763123120Simp			err = EINVAL;
764123120Simp			break;
765123120Simp		}
766123120Simp		fwb = (struct fw_bind *)malloc(sizeof (struct fw_bind), M_FW, M_WAITOK);
767123120Simp		if(fwb == NULL){
768123120Simp			err = ENOMEM;
769123120Simp			break;
770123120Simp		}
771123120Simp		fwb->start = ((u_int64_t)bindreq->start.hi << 32) |
772123120Simp		    bindreq->start.lo;
773123120Simp		fwb->end = fwb->start +  bindreq->len;
774123120Simp		fwb->sc = (void *)d;
775123120Simp		STAILQ_INIT(&fwb->xferlist);
776123120Simp		err = fw_bindadd(fc, fwb);
777123120Simp		if (err == 0) {
778123120Simp			fw_xferlist_add(&fwb->xferlist, M_FWXFER,
779123120Simp			    /* XXX */
780123120Simp			    PAGE_SIZE, PAGE_SIZE, 5,
781123120Simp			    fc, (void *)fwb, fw_hand);
782123120Simp			STAILQ_INSERT_TAIL(&d->binds, fwb, chlist);
783123120Simp		}
784123120Simp		break;
785123120Simp	case FW_GDEVLST:
786123120Simp		i = len = 1;
787123120Simp		/* myself */
788123120Simp		devinfo = &fwdevlst->dev[0];
789123120Simp		devinfo->dst = fc->nodeid;
790123120Simp		devinfo->status = 0;	/* XXX */
791123120Simp		devinfo->eui.hi = fc->eui.hi;
792123120Simp		devinfo->eui.lo = fc->eui.lo;
793123120Simp		STAILQ_FOREACH(fwdev, &fc->devices, link) {
794123120Simp			if(len < FW_MAX_DEVLST){
795123120Simp				devinfo = &fwdevlst->dev[len++];
796123120Simp				devinfo->dst = fwdev->dst;
797123120Simp				devinfo->status =
798123120Simp					(fwdev->status == FWDEVINVAL)?0:1;
799123120Simp				devinfo->eui.hi = fwdev->eui.hi;
800123120Simp				devinfo->eui.lo = fwdev->eui.lo;
801123120Simp			}
802123120Simp			i++;
803123120Simp		}
804123120Simp		fwdevlst->n = i;
805123120Simp		fwdevlst->info_len = len;
806123120Simp		break;
807123120Simp	case FW_GTPMAP:
808123120Simp		bcopy(fc->topology_map, data,
809123120Simp				(fc->topology_map->crc_len + 1) * 4);
810123120Simp		break;
811123120Simp	case FW_GCROM:
812123120Simp		STAILQ_FOREACH(fwdev, &fc->devices, link)
813123120Simp			if (FW_EUI64_EQUAL(fwdev->eui, crom_buf->eui))
814123120Simp				break;
815123120Simp		if (fwdev == NULL) {
816123120Simp			if (!FW_EUI64_EQUAL(fc->eui, crom_buf->eui)) {
817123120Simp				err = FWNODE_INVAL;
818123120Simp				break;
819123120Simp			}
820123120Simp			/* myself */
821123120Simp			ptr = malloc(CROMSIZE, M_FW, M_WAITOK);
822123120Simp			len = CROMSIZE;
823123120Simp			for (i = 0; i < CROMSIZE/4; i++)
824123120Simp				((uint32_t *)ptr)[i]
825123120Simp					= ntohl(fc->config_rom[i]);
826123120Simp		} else {
827123120Simp			/* found */
828123120Simp			ptr = (void *)&fwdev->csrrom[0];
829123120Simp			if (fwdev->rommax < CSRROMOFF)
830123120Simp				len = 0;
831123120Simp			else
832123120Simp				len = fwdev->rommax - CSRROMOFF + 4;
833123120Simp		}
834123120Simp		if (crom_buf->len < len)
835123120Simp			len = crom_buf->len;
836123120Simp		else
837123120Simp			crom_buf->len = len;
838123120Simp		err = copyout(ptr, crom_buf->ptr, len);
839123120Simp		if (fwdev == NULL)
840123120Simp			/* myself */
841123120Simp			free(ptr, M_FW);
842123120Simp		break;
843123120Simp	default:
844123120Simp		fc->ioctl (dev, cmd, data, flag, td);
845123120Simp		break;
846123120Simp	}
847123120Simp	return err;
848123120Simp}
849123120Simpint
850123120Simpfw_poll(struct cdev *dev, int events, fw_proc *td)
851123120Simp{
852123120Simp	struct fw_xferq *ir;
853123120Simp	int revents;
854123120Simp	int tmp;
855123120Simp
856123120Simp	if (DEV_FWMEM(dev))
857123120Simp		return fwmem_poll(dev, events, td);
858123120Simp
859123120Simp	ir = ((struct fw_drv1 *)dev->si_drv1)->ir;
860123120Simp	revents = 0;
861123120Simp	tmp = POLLIN | POLLRDNORM;
862123120Simp	if (events & tmp) {
863123120Simp		if (STAILQ_FIRST(&ir->q) != NULL)
864123120Simp			revents |= tmp;
865123120Simp		else
866123120Simp			selrecord(td, &ir->rsel);
867123120Simp	}
868123120Simp	tmp = POLLOUT | POLLWRNORM;
869123120Simp	if (events & tmp) {
870123120Simp		/* XXX should be fixed */
871123120Simp		revents |= tmp;
872123120Simp	}
873123120Simp
874123120Simp	return revents;
875123120Simp}
876123120Simp
877123120Simpstatic int
878277565Skevlofw_mmap (struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr,
879277565Skevlo    int nproto, vm_memattr_t *memattr)
880277565Skevlo{
881123120Simp
882123120Simp	if (DEV_FWMEM(dev))
883123120Simp		return fwmem_mmap(dev, offset, paddr, nproto, memattr);
884123120Simp
885123120Simp	return EINVAL;
886123120Simp}
887123120Simp
888123120Simpstatic void
889123120Simpfw_strategy(struct bio *bp)
890123120Simp{
891123120Simp	struct cdev *dev;
892123120Simp
893123120Simp	dev = bp->bio_dev;
894123120Simp	if (DEV_FWMEM(dev)) {
895123120Simp		fwmem_strategy(bp);
896123120Simp		return;
897123120Simp	}
898123120Simp
899123120Simp	bp->bio_error = EOPNOTSUPP;
900123120Simp	bp->bio_flags |= BIO_ERROR;
901123120Simp	bp->bio_resid = bp->bio_bcount;
902123120Simp	biodone(bp);
903123120Simp}
904123120Simp
905123120Simpint
906fwdev_makedev(struct firewire_softc *sc)
907{
908	int err = 0;
909
910	struct cdev *d;
911	int unit;
912
913	unit = device_get_unit(sc->fc->bdev);
914	sc->dev = make_dev(&firewire_cdevsw, MAKEMINOR(0, unit, 0),
915			UID_ROOT, GID_OPERATOR, 0660,
916			"fw%d.%d", unit, 0);
917	d = make_dev(&firewire_cdevsw,
918			MAKEMINOR(FWMEM_FLAG, unit, 0),
919			UID_ROOT, GID_OPERATOR, 0660,
920			"fwmem%d.%d", unit, 0);
921	dev_depends(sc->dev, d);
922	make_dev_alias(sc->dev, "fw%d", unit);
923	make_dev_alias(d, "fwmem%d", unit);
924
925	return (err);
926}
927
928int
929fwdev_destroydev(struct firewire_softc *sc)
930{
931	int err = 0;
932
933	destroy_dev(sc->dev);
934	return (err);
935}
936
937#define NDEVTYPE 2
938void
939fwdev_clone(void *arg, struct ucred *cred, char *name, int namelen,
940    struct cdev **dev)
941{
942	struct firewire_softc *sc;
943	char *devnames[NDEVTYPE] = {"fw", "fwmem"};
944	char *subp = NULL;
945	int devflag[NDEVTYPE] = {0, FWMEM_FLAG};
946	int i, unit = 0, sub = 0;
947
948	if (*dev != NULL)
949		return;
950
951	for (i = 0; i < NDEVTYPE; i++)
952		if (dev_stdclone(name, &subp, devnames[i], &unit) == 2)
953			goto found;
954	/* not match */
955	return;
956found:
957
958	if (subp == NULL || *subp++ != '.')
959		return;
960
961	/* /dev/fwU.S */
962	while (isdigit(*subp)) {
963		sub *= 10;
964		sub += *subp++ - '0';
965	}
966	if (*subp != '\0')
967		return;
968
969	sc = devclass_get_softc(firewire_devclass, unit);
970	if (sc == NULL)
971		return;
972	*dev = make_dev_credf(MAKEDEV_REF, &firewire_cdevsw,
973	    MAKEMINOR(devflag[i], unit, sub), cred, UID_ROOT, GID_OPERATOR,
974	    0660, "%s%d.%d", devnames[i], unit, sub);
975	dev_depends(sc->dev, *dev);
976	return;
977}
978