fwdev.c revision 330897
1/*-
2 * SPDX-License-Identifier: BSD-4-Clause
3 *
4 * Copyright (c) 2003 Hidetoshi Shimokawa
5 * Copyright (c) 1998-2002 Katsushi Kobayashi and Hidetoshi Shimokawa
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the acknowledgement as bellow:
18 *
19 *    This product includes software developed by K. Kobayashi and H. Shimokawa
20 *
21 * 4. The name of the author may not be used to endorse or promote products
22 *    derived from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
28 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 *
36 * $FreeBSD: stable/11/sys/dev/firewire/fwdev.c 330897 2018-03-14 03:19:51Z eadler $
37 *
38 */
39
40#include <sys/param.h>
41#include <sys/systm.h>
42#include <sys/types.h>
43#include <sys/mbuf.h>
44#include <sys/bio.h>
45
46#include <sys/kernel.h>
47#include <sys/malloc.h>
48#include <sys/conf.h>
49#include <sys/poll.h>
50
51#include <sys/bus.h>
52#include <sys/ctype.h>
53#include <machine/bus.h>
54
55#include <sys/ioccom.h>
56
57#include <dev/firewire/firewire.h>
58#include <dev/firewire/firewirereg.h>
59#include <dev/firewire/fwdma.h>
60#include <dev/firewire/fwmem.h>
61#include <dev/firewire/iec68113.h>
62
63#define	FWNODE_INVAL 0xffff
64
65static	d_open_t	fw_open;
66static	d_close_t	fw_close;
67static	d_ioctl_t	fw_ioctl;
68static	d_poll_t	fw_poll;
69static	d_read_t	fw_read;	/* for Isochronous packet */
70static	d_write_t	fw_write;
71static	d_mmap_t	fw_mmap;
72static	d_strategy_t	fw_strategy;
73
74struct cdevsw firewire_cdevsw = {
75	.d_version =	D_VERSION,
76	.d_open =	fw_open,
77	.d_close =	fw_close,
78	.d_read =	fw_read,
79	.d_write =	fw_write,
80	.d_ioctl =	fw_ioctl,
81	.d_poll =	fw_poll,
82	.d_mmap =	fw_mmap,
83	.d_strategy =	fw_strategy,
84	.d_name =	"fw",
85};
86
87struct fw_drv1 {
88	struct firewire_comm *fc;
89	struct fw_xferq *ir;
90	struct fw_xferq *it;
91	struct fw_isobufreq bufreq;
92	STAILQ_HEAD(, fw_bind) binds;
93	STAILQ_HEAD(, fw_xfer) rq;
94};
95
96static int
97fwdev_allocbuf(struct firewire_comm *fc, struct fw_xferq *q,
98	struct fw_bufspec *b)
99{
100	int i;
101
102	if (q->flag & (FWXFERQ_RUNNING | FWXFERQ_EXTBUF))
103		return (EBUSY);
104
105	q->bulkxfer = malloc(sizeof(struct fw_bulkxfer) * b->nchunk,
106	    M_FW, M_WAITOK);
107
108	b->psize = roundup2(b->psize, sizeof(uint32_t));
109	q->buf = fwdma_malloc_multiseg(fc, sizeof(uint32_t),
110	    b->psize, b->nchunk * b->npacket, BUS_DMA_WAITOK);
111
112	if (q->buf == NULL) {
113		free(q->bulkxfer, M_FW);
114		q->bulkxfer = NULL;
115		return (ENOMEM);
116	}
117	q->bnchunk = b->nchunk;
118	q->bnpacket = b->npacket;
119	q->psize = (b->psize + 3) & ~3;
120	q->queued = 0;
121
122	STAILQ_INIT(&q->stvalid);
123	STAILQ_INIT(&q->stfree);
124	STAILQ_INIT(&q->stdma);
125	q->stproc = NULL;
126
127	for (i = 0; i < q->bnchunk; i++) {
128		q->bulkxfer[i].poffset = i * q->bnpacket;
129		q->bulkxfer[i].mbuf = NULL;
130		STAILQ_INSERT_TAIL(&q->stfree, &q->bulkxfer[i], link);
131	}
132
133	q->flag &= ~FWXFERQ_MODEMASK;
134	q->flag |= FWXFERQ_STREAM;
135	q->flag |= FWXFERQ_EXTBUF;
136
137	return (0);
138}
139
140static int
141fwdev_freebuf(struct fw_xferq *q)
142{
143	if (q->flag & FWXFERQ_EXTBUF) {
144		if (q->buf != NULL)
145			fwdma_free_multiseg(q->buf);
146		q->buf = NULL;
147		free(q->bulkxfer, M_FW);
148		q->bulkxfer = NULL;
149		q->flag &= ~FWXFERQ_EXTBUF;
150		q->psize = 0;
151		q->maxq = FWMAXQUEUE;
152	}
153	return (0);
154}
155
156
157static int
158fw_open(struct cdev *dev, int flags, int fmt, fw_proc *td)
159{
160	int err = 0;
161	int unit = DEV2UNIT(dev);
162	struct fw_drv1 *d;
163	struct firewire_softc *sc;
164
165	if (DEV_FWMEM(dev))
166		return fwmem_open(dev, flags, fmt, td);
167
168	sc = devclass_get_softc(firewire_devclass, unit);
169	if (sc == NULL)
170		return (ENXIO);
171
172	FW_GLOCK(sc->fc);
173	if (dev->si_drv1 != NULL) {
174		FW_GUNLOCK(sc->fc);
175		return (EBUSY);
176	}
177	/* set dummy value for allocation */
178	dev->si_drv1 = (void *)-1;
179	FW_GUNLOCK(sc->fc);
180
181	dev->si_drv1 = malloc(sizeof(struct fw_drv1), M_FW, M_WAITOK | M_ZERO);
182
183	if ((dev->si_flags & SI_NAMED) == 0) {
184		int unit = DEV2UNIT(dev);
185		int sub = DEV2SUB(dev);
186
187		make_dev(&firewire_cdevsw, dev2unit(dev),
188		    UID_ROOT, GID_OPERATOR, 0660, "fw%d.%d", unit, sub);
189	}
190
191	d = dev->si_drv1;
192	d->fc = sc->fc;
193	STAILQ_INIT(&d->binds);
194	STAILQ_INIT(&d->rq);
195
196	return err;
197}
198
199static int
200fw_close(struct cdev *dev, int flags, int fmt, fw_proc *td)
201{
202	struct firewire_comm *fc;
203	struct fw_drv1 *d;
204	struct fw_xfer *xfer;
205	struct fw_bind *fwb;
206	int err = 0;
207
208	if (DEV_FWMEM(dev))
209		return fwmem_close(dev, flags, fmt, td);
210
211	d = dev->si_drv1;
212	fc = d->fc;
213
214	/* remove binding */
215	for (fwb = STAILQ_FIRST(&d->binds); fwb != NULL;
216	    fwb = STAILQ_FIRST(&d->binds)) {
217		fw_bindremove(fc, fwb);
218		STAILQ_REMOVE_HEAD(&d->binds, chlist);
219		fw_xferlist_remove(&fwb->xferlist);
220		free(fwb, M_FW);
221	}
222	if (d->ir != NULL) {
223		struct fw_xferq *ir = d->ir;
224
225		if ((ir->flag & FWXFERQ_OPEN) == 0)
226			return (EINVAL);
227		if (ir->flag & FWXFERQ_RUNNING) {
228			ir->flag &= ~FWXFERQ_RUNNING;
229			fc->irx_disable(fc, ir->dmach);
230		}
231		/* free extbuf */
232		fwdev_freebuf(ir);
233		/* drain receiving buffer */
234		for (xfer = STAILQ_FIRST(&ir->q);
235		    xfer != NULL; xfer = STAILQ_FIRST(&ir->q)) {
236			ir->queued--;
237			STAILQ_REMOVE_HEAD(&ir->q, link);
238
239			xfer->resp = 0;
240			fw_xfer_done(xfer);
241		}
242		ir->flag &= ~(FWXFERQ_OPEN | FWXFERQ_MODEMASK |
243		    FWXFERQ_CHTAGMASK);
244		d->ir = NULL;
245
246	}
247	if (d->it != NULL) {
248		struct fw_xferq *it = d->it;
249
250		if ((it->flag & FWXFERQ_OPEN) == 0)
251			return (EINVAL);
252		if (it->flag & FWXFERQ_RUNNING) {
253			it->flag &= ~FWXFERQ_RUNNING;
254			fc->itx_disable(fc, it->dmach);
255		}
256		/* free extbuf */
257		fwdev_freebuf(it);
258		it->flag &= ~(FWXFERQ_OPEN |
259		    FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK);
260		d->it = NULL;
261	}
262	free(dev->si_drv1, M_FW);
263	dev->si_drv1 = NULL;
264
265	return err;
266}
267
268static int
269fw_read_async(struct fw_drv1 *d, struct uio *uio, int ioflag)
270{
271	int err = 0, s;
272	struct fw_xfer *xfer;
273	struct fw_bind *fwb;
274	struct fw_pkt *fp;
275	struct tcode_info *tinfo;
276
277	FW_GLOCK(d->fc);
278	while ((xfer = STAILQ_FIRST(&d->rq)) == NULL && err == 0)
279		err = msleep(&d->rq, FW_GMTX(d->fc), FWPRI, "fwra", 0);
280
281	if (err != 0) {
282		FW_GUNLOCK(d->fc);
283		return (err);
284	}
285
286	s = splfw();
287	STAILQ_REMOVE_HEAD(&d->rq, link);
288	FW_GUNLOCK(xfer->fc);
289	splx(s);
290	fp = &xfer->recv.hdr;
291#if 0 /* for GASP ?? */
292	if (fc->irx_post != NULL)
293		fc->irx_post(fc, fp->mode.ld);
294#endif
295	tinfo = &xfer->fc->tcode[fp->mode.hdr.tcode];
296	err = uiomove(fp, tinfo->hdr_len, uio);
297	if (err)
298		goto out;
299	err = uiomove(xfer->recv.payload, xfer->recv.pay_len, uio);
300
301out:
302	/* recycle this xfer */
303	fwb = (struct fw_bind *)xfer->sc;
304	fw_xfer_unload(xfer);
305	xfer->recv.pay_len = PAGE_SIZE;
306	FW_GLOCK(xfer->fc);
307	STAILQ_INSERT_TAIL(&fwb->xferlist, xfer, link);
308	FW_GUNLOCK(xfer->fc);
309	return (err);
310}
311
312/*
313 * read request.
314 */
315static int
316fw_read(struct cdev *dev, struct uio *uio, int ioflag)
317{
318	struct fw_drv1 *d;
319	struct fw_xferq *ir;
320	struct firewire_comm *fc;
321	int err = 0, s, slept = 0;
322	struct fw_pkt *fp;
323
324	if (DEV_FWMEM(dev))
325		return (physio(dev, uio, ioflag));
326
327	d = dev->si_drv1;
328	fc = d->fc;
329	ir = d->ir;
330
331	if (ir == NULL)
332		return (fw_read_async(d, uio, ioflag));
333
334	if (ir->buf == NULL)
335		return (EIO);
336
337	FW_GLOCK(fc);
338readloop:
339	if (ir->stproc == NULL) {
340		/* iso bulkxfer */
341		ir->stproc = STAILQ_FIRST(&ir->stvalid);
342		if (ir->stproc != NULL) {
343			s = splfw();
344			STAILQ_REMOVE_HEAD(&ir->stvalid, link);
345			splx(s);
346			ir->queued = 0;
347		}
348	}
349	if (ir->stproc == NULL) {
350		/* no data available */
351		if (slept == 0) {
352			slept = 1;
353			ir->flag |= FWXFERQ_WAKEUP;
354			err = msleep(ir, FW_GMTX(fc), FWPRI, "fw_read", hz);
355			ir->flag &= ~FWXFERQ_WAKEUP;
356			if (err == 0)
357				goto readloop;
358		} else if (slept == 1)
359			err = EIO;
360		FW_GUNLOCK(fc);
361		return err;
362	} else if (ir->stproc != NULL) {
363		/* iso bulkxfer */
364		FW_GUNLOCK(fc);
365		fp = (struct fw_pkt *)fwdma_v_addr(ir->buf,
366		    ir->stproc->poffset + ir->queued);
367		if (fc->irx_post != NULL)
368			fc->irx_post(fc, fp->mode.ld);
369		if (fp->mode.stream.len == 0) {
370			err = EIO;
371			return err;
372		}
373		err = uiomove((caddr_t)fp,
374			fp->mode.stream.len + sizeof(uint32_t), uio);
375		ir->queued++;
376		if (ir->queued >= ir->bnpacket) {
377			s = splfw();
378			STAILQ_INSERT_TAIL(&ir->stfree, ir->stproc, link);
379			splx(s);
380			fc->irx_enable(fc, ir->dmach);
381			ir->stproc = NULL;
382		}
383		if (uio->uio_resid >= ir->psize) {
384			slept = -1;
385			FW_GLOCK(fc);
386			goto readloop;
387		}
388	}
389	return err;
390}
391
392static int
393fw_write_async(struct fw_drv1 *d, struct uio *uio, int ioflag)
394{
395	struct fw_xfer *xfer;
396	struct fw_pkt pkt;
397	struct tcode_info *tinfo;
398	int err;
399
400	bzero(&pkt, sizeof(struct fw_pkt));
401	if ((err = uiomove((caddr_t)&pkt, sizeof(uint32_t), uio)))
402		return (err);
403	tinfo = &d->fc->tcode[pkt.mode.hdr.tcode];
404	if ((err = uiomove((caddr_t)&pkt + sizeof(uint32_t),
405	    tinfo->hdr_len - sizeof(uint32_t), uio)))
406		return (err);
407
408	if ((xfer = fw_xfer_alloc_buf(M_FWXFER, uio->uio_resid,
409	    PAGE_SIZE/*XXX*/)) == NULL)
410		return (ENOMEM);
411
412	bcopy(&pkt, &xfer->send.hdr, sizeof(struct fw_pkt));
413	xfer->send.pay_len = uio->uio_resid;
414	if (uio->uio_resid > 0) {
415		if ((err = uiomove((caddr_t)&xfer->send.payload[0],
416		    uio->uio_resid, uio)))
417			goto out;
418	}
419
420	xfer->fc = d->fc;
421	xfer->sc = NULL;
422	xfer->hand = fw_xferwake;
423	xfer->send.spd = 2 /* XXX */;
424
425	if ((err = fw_asyreq(xfer->fc, -1, xfer)))
426		goto out;
427
428	if ((err = fw_xferwait(xfer)))
429		goto out;
430
431	if (xfer->resp != 0) {
432		err = xfer->resp;
433		goto out;
434	}
435
436	if (xfer->flag & FWXF_RCVD) {
437		FW_GLOCK(xfer->fc);
438		STAILQ_INSERT_TAIL(&d->rq, xfer, link);
439		FW_GUNLOCK(xfer->fc);
440		return (0);
441	}
442
443out:
444	fw_xfer_free(xfer);
445	return (err);
446}
447
448static int
449fw_write(struct cdev *dev, struct uio *uio, int ioflag)
450{
451	int err = 0;
452	int s, slept = 0;
453	struct fw_drv1 *d;
454	struct fw_pkt *fp;
455	struct firewire_comm *fc;
456	struct fw_xferq *it;
457
458	if (DEV_FWMEM(dev))
459		return (physio(dev, uio, ioflag));
460
461	d = dev->si_drv1;
462	fc = d->fc;
463	it = d->it;
464
465	if (it == NULL)
466		return (fw_write_async(d, uio, ioflag));
467
468	if (it->buf == NULL)
469		return (EIO);
470
471	FW_GLOCK(fc);
472isoloop:
473	if (it->stproc == NULL) {
474		it->stproc = STAILQ_FIRST(&it->stfree);
475		if (it->stproc != NULL) {
476			s = splfw();
477			STAILQ_REMOVE_HEAD(&it->stfree, link);
478			splx(s);
479			it->queued = 0;
480		} else if (slept == 0) {
481			slept = 1;
482#if 0	/* XXX to avoid lock recursion */
483			err = fc->itx_enable(fc, it->dmach);
484			if (err)
485				goto out;
486#endif
487			err = msleep(it, FW_GMTX(fc), FWPRI, "fw_write", hz);
488			if (err)
489				goto out;
490			goto isoloop;
491		} else {
492			err = EIO;
493			goto out;
494		}
495	}
496	FW_GUNLOCK(fc);
497	fp = (struct fw_pkt *)fwdma_v_addr(it->buf,
498			it->stproc->poffset + it->queued);
499	err = uiomove((caddr_t)fp, sizeof(struct fw_isohdr), uio);
500	err = uiomove((caddr_t)fp->mode.stream.payload,
501				fp->mode.stream.len, uio);
502	it->queued++;
503	if (it->queued >= it->bnpacket) {
504		s = splfw();
505		STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link);
506		splx(s);
507		it->stproc = NULL;
508		err = fc->itx_enable(fc, it->dmach);
509	}
510	if (uio->uio_resid >= sizeof(struct fw_isohdr)) {
511		slept = 0;
512		FW_GLOCK(fc);
513		goto isoloop;
514	}
515	return err;
516
517out:
518	FW_GUNLOCK(fc);
519	return err;
520}
521
522static void
523fw_hand(struct fw_xfer *xfer)
524{
525	struct fw_bind *fwb;
526	struct fw_drv1 *d;
527
528	fwb = (struct fw_bind *)xfer->sc;
529	d = fwb->sc;
530	FW_GLOCK(xfer->fc);
531	STAILQ_INSERT_TAIL(&d->rq, xfer, link);
532	FW_GUNLOCK(xfer->fc);
533	wakeup(&d->rq);
534}
535
536/*
537 * ioctl support.
538 */
539int
540fw_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, fw_proc *td)
541{
542	struct firewire_comm *fc;
543	struct fw_drv1 *d;
544	int i, len, err = 0;
545	struct fw_device *fwdev;
546	struct fw_bind *fwb;
547	struct fw_xferq *ir, *it;
548	struct fw_xfer *xfer;
549	struct fw_pkt *fp;
550	struct fw_devinfo *devinfo;
551	void *ptr;
552
553	struct fw_devlstreq *fwdevlst = (struct fw_devlstreq *)data;
554	struct fw_asyreq *asyreq = (struct fw_asyreq *)data;
555	struct fw_isochreq *ichreq = (struct fw_isochreq *)data;
556	struct fw_isobufreq *ibufreq = (struct fw_isobufreq *)data;
557	struct fw_asybindreq *bindreq = (struct fw_asybindreq *)data;
558	struct fw_crom_buf *crom_buf = (struct fw_crom_buf *)data;
559
560	if (DEV_FWMEM(dev))
561		return fwmem_ioctl(dev, cmd, data, flag, td);
562
563	if (!data)
564		return (EINVAL);
565
566	d = dev->si_drv1;
567	fc = d->fc;
568	ir = d->ir;
569	it = d->it;
570
571	switch (cmd) {
572	case FW_STSTREAM:
573		if (it == NULL) {
574			i = fw_open_isodma(fc, /* tx */1);
575			if (i < 0) {
576				err = EBUSY;
577				break;
578			}
579			it = fc->it[i];
580			err = fwdev_allocbuf(fc, it, &d->bufreq.tx);
581			if (err) {
582				it->flag &= ~FWXFERQ_OPEN;
583				break;
584			}
585		}
586		it->flag &= ~0xff;
587		it->flag |= (0x3f & ichreq->ch);
588		it->flag |= ((0x3 & ichreq->tag) << 6);
589		d->it = it;
590		break;
591	case FW_GTSTREAM:
592		if (it != NULL) {
593			ichreq->ch = it->flag & 0x3f;
594			ichreq->tag = it->flag >> 2 & 0x3;
595		} else
596			err = EINVAL;
597		break;
598	case FW_SRSTREAM:
599		if (ir == NULL) {
600			i = fw_open_isodma(fc, /* tx */0);
601			if (i < 0) {
602				err = EBUSY;
603				break;
604			}
605			ir = fc->ir[i];
606			err = fwdev_allocbuf(fc, ir, &d->bufreq.rx);
607			if (err) {
608				ir->flag &= ~FWXFERQ_OPEN;
609				break;
610			}
611		}
612		ir->flag &= ~0xff;
613		ir->flag |= (0x3f & ichreq->ch);
614		ir->flag |= ((0x3 & ichreq->tag) << 6);
615		d->ir = ir;
616		err = fc->irx_enable(fc, ir->dmach);
617		break;
618	case FW_GRSTREAM:
619		if (d->ir != NULL) {
620			ichreq->ch = ir->flag & 0x3f;
621			ichreq->tag = ir->flag >> 2 & 0x3;
622		} else
623			err = EINVAL;
624		break;
625	case FW_SSTBUF:
626		bcopy(ibufreq, &d->bufreq, sizeof(d->bufreq));
627		break;
628	case FW_GSTBUF:
629		bzero(&ibufreq->rx, sizeof(ibufreq->rx));
630		if (ir != NULL) {
631			ibufreq->rx.nchunk = ir->bnchunk;
632			ibufreq->rx.npacket = ir->bnpacket;
633			ibufreq->rx.psize = ir->psize;
634		}
635		bzero(&ibufreq->tx, sizeof(ibufreq->tx));
636		if (it != NULL) {
637			ibufreq->tx.nchunk = it->bnchunk;
638			ibufreq->tx.npacket = it->bnpacket;
639			ibufreq->tx.psize = it->psize;
640		}
641		break;
642	case FW_ASYREQ:
643	{
644		struct tcode_info *tinfo;
645		int pay_len = 0;
646
647		fp = &asyreq->pkt;
648		tinfo = &fc->tcode[fp->mode.hdr.tcode];
649
650		if ((tinfo->flag & FWTI_BLOCK_ASY) != 0)
651			pay_len = MAX(0, asyreq->req.len - tinfo->hdr_len);
652
653		xfer = fw_xfer_alloc_buf(M_FWXFER, pay_len, PAGE_SIZE/*XXX*/);
654		if (xfer == NULL)
655			return (ENOMEM);
656
657		switch (asyreq->req.type) {
658		case FWASREQNODE:
659			break;
660		case FWASREQEUI:
661			fwdev = fw_noderesolve_eui64(fc,
662						&asyreq->req.dst.eui);
663			if (fwdev == NULL) {
664				device_printf(fc->bdev,
665					"cannot find node\n");
666				err = EINVAL;
667				goto out;
668			}
669			fp->mode.hdr.dst = FWLOCALBUS | fwdev->dst;
670			break;
671		case FWASRESTL:
672			/* XXX what's this? */
673			break;
674		case FWASREQSTREAM:
675			/* nothing to do */
676			break;
677		}
678
679		bcopy(fp, (void *)&xfer->send.hdr, tinfo->hdr_len);
680		if (pay_len > 0)
681			bcopy((char *)fp + tinfo->hdr_len,
682			    xfer->send.payload, pay_len);
683		xfer->send.spd = asyreq->req.sped;
684		xfer->hand = fw_xferwake;
685
686		if ((err = fw_asyreq(fc, -1, xfer)) != 0)
687			goto out;
688		if ((err = fw_xferwait(xfer)) != 0)
689			goto out;
690		if (xfer->resp != 0) {
691			err = EIO;
692			goto out;
693		}
694		if ((tinfo->flag & FWTI_TLABEL) == 0)
695			goto out;
696
697		/* copy response */
698		tinfo = &fc->tcode[xfer->recv.hdr.mode.hdr.tcode];
699		if (xfer->recv.hdr.mode.hdr.tcode == FWTCODE_RRESB ||
700		    xfer->recv.hdr.mode.hdr.tcode == FWTCODE_LRES) {
701			pay_len = xfer->recv.pay_len;
702			if (asyreq->req.len >= xfer->recv.pay_len + tinfo->hdr_len) {
703				asyreq->req.len = xfer->recv.pay_len +
704				    tinfo->hdr_len;
705			} else {
706				err = EINVAL;
707				pay_len = 0;
708			}
709		} else {
710			pay_len = 0;
711		}
712		bcopy(&xfer->recv.hdr, fp, tinfo->hdr_len);
713		bcopy(xfer->recv.payload, (char *)fp + tinfo->hdr_len, pay_len);
714out:
715		fw_xfer_free_buf(xfer);
716		break;
717	}
718	case FW_IBUSRST:
719		fc->ibr(fc);
720		break;
721	case FW_CBINDADDR:
722		fwb = fw_bindlookup(fc,
723				bindreq->start.hi, bindreq->start.lo);
724		if (fwb == NULL) {
725			err = EINVAL;
726			break;
727		}
728		fw_bindremove(fc, fwb);
729		STAILQ_REMOVE(&d->binds, fwb, fw_bind, chlist);
730		fw_xferlist_remove(&fwb->xferlist);
731		free(fwb, M_FW);
732		break;
733	case FW_SBINDADDR:
734		if (bindreq->len <= 0) {
735			err = EINVAL;
736			break;
737		}
738		if (bindreq->start.hi > 0xffff) {
739			err = EINVAL;
740			break;
741		}
742		fwb = malloc(sizeof(struct fw_bind), M_FW, M_WAITOK);
743		fwb->start = ((u_int64_t)bindreq->start.hi << 32) |
744		    bindreq->start.lo;
745		fwb->end = fwb->start +  bindreq->len;
746		fwb->sc = d;
747		STAILQ_INIT(&fwb->xferlist);
748		err = fw_bindadd(fc, fwb);
749		if (err == 0) {
750			fw_xferlist_add(&fwb->xferlist, M_FWXFER,
751			    /* XXX */
752			    PAGE_SIZE, PAGE_SIZE, 5,
753			    fc, fwb, fw_hand);
754			STAILQ_INSERT_TAIL(&d->binds, fwb, chlist);
755		}
756		break;
757	case FW_GDEVLST:
758		i = len = 1;
759		/* myself */
760		devinfo = &fwdevlst->dev[0];
761		devinfo->dst = fc->nodeid;
762		devinfo->status = 0;	/* XXX */
763		devinfo->eui.hi = fc->eui.hi;
764		devinfo->eui.lo = fc->eui.lo;
765		STAILQ_FOREACH(fwdev, &fc->devices, link) {
766			if (len < FW_MAX_DEVLST) {
767				devinfo = &fwdevlst->dev[len++];
768				devinfo->dst = fwdev->dst;
769				devinfo->status =
770					(fwdev->status == FWDEVINVAL) ? 0 : 1;
771				devinfo->eui.hi = fwdev->eui.hi;
772				devinfo->eui.lo = fwdev->eui.lo;
773			}
774			i++;
775		}
776		fwdevlst->n = i;
777		fwdevlst->info_len = len;
778		break;
779	case FW_GTPMAP:
780		bcopy(fc->topology_map, data,
781		    (fc->topology_map->crc_len + 1) * 4);
782		break;
783	case FW_GCROM:
784		STAILQ_FOREACH(fwdev, &fc->devices, link)
785			if (FW_EUI64_EQUAL(fwdev->eui, crom_buf->eui))
786				break;
787		if (fwdev == NULL) {
788			if (!FW_EUI64_EQUAL(fc->eui, crom_buf->eui)) {
789				err = FWNODE_INVAL;
790				break;
791			}
792			/* myself */
793			ptr = malloc(CROMSIZE, M_FW, M_WAITOK);
794			len = CROMSIZE;
795			for (i = 0; i < CROMSIZE/4; i++)
796				((uint32_t *)ptr)[i]
797					= ntohl(fc->config_rom[i]);
798		} else {
799			/* found */
800			ptr = (void *)&fwdev->csrrom[0];
801			if (fwdev->rommax < CSRROMOFF)
802				len = 0;
803			else
804				len = fwdev->rommax - CSRROMOFF + 4;
805		}
806		if (crom_buf->len < len)
807			len = crom_buf->len;
808		else
809			crom_buf->len = len;
810		err = copyout(ptr, crom_buf->ptr, len);
811		if (fwdev == NULL)
812			/* myself */
813			free(ptr, M_FW);
814		break;
815	default:
816		fc->ioctl(dev, cmd, data, flag, td);
817		break;
818	}
819	return err;
820}
821int
822fw_poll(struct cdev *dev, int events, fw_proc *td)
823{
824	struct fw_xferq *ir;
825	int revents;
826	int tmp;
827
828	if (DEV_FWMEM(dev))
829		return fwmem_poll(dev, events, td);
830
831	ir = ((struct fw_drv1 *)dev->si_drv1)->ir;
832	revents = 0;
833	tmp = POLLIN | POLLRDNORM;
834	if (events & tmp) {
835		if (STAILQ_FIRST(&ir->q) != NULL)
836			revents |= tmp;
837		else
838			selrecord(td, &ir->rsel);
839	}
840	tmp = POLLOUT | POLLWRNORM;
841	if (events & tmp) {
842		/* XXX should be fixed */
843		revents |= tmp;
844	}
845
846	return revents;
847}
848
849static int
850fw_mmap (struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr,
851    int nproto, vm_memattr_t *memattr)
852{
853
854	if (DEV_FWMEM(dev))
855		return fwmem_mmap(dev, offset, paddr, nproto, memattr);
856
857	return EINVAL;
858}
859
860static void
861fw_strategy(struct bio *bp)
862{
863	struct cdev *dev;
864
865	dev = bp->bio_dev;
866	if (DEV_FWMEM(dev)) {
867		fwmem_strategy(bp);
868		return;
869	}
870
871	bp->bio_error = EOPNOTSUPP;
872	bp->bio_flags |= BIO_ERROR;
873	bp->bio_resid = bp->bio_bcount;
874	biodone(bp);
875}
876
877int
878fwdev_makedev(struct firewire_softc *sc)
879{
880	int err = 0;
881
882	struct cdev *d;
883	int unit;
884
885	unit = device_get_unit(sc->fc->bdev);
886	sc->dev = make_dev(&firewire_cdevsw, MAKEMINOR(0, unit, 0),
887	    UID_ROOT, GID_OPERATOR, 0660, "fw%d.%d", unit, 0);
888	d = make_dev(&firewire_cdevsw, MAKEMINOR(FWMEM_FLAG, unit, 0),
889	    UID_ROOT, GID_OPERATOR, 0660, "fwmem%d.%d", unit, 0);
890	dev_depends(sc->dev, d);
891	make_dev_alias(sc->dev, "fw%d", unit);
892	make_dev_alias(d, "fwmem%d", unit);
893
894	return (err);
895}
896
897int
898fwdev_destroydev(struct firewire_softc *sc)
899{
900	int err = 0;
901
902	destroy_dev(sc->dev);
903	return (err);
904}
905
906#define NDEVTYPE 2
907void
908fwdev_clone(void *arg, struct ucred *cred, char *name, int namelen,
909    struct cdev **dev)
910{
911	struct firewire_softc *sc;
912	char *devnames[NDEVTYPE] = {"fw", "fwmem"};
913	char *subp = NULL;
914	int devflag[NDEVTYPE] = {0, FWMEM_FLAG};
915	int i, unit = 0, sub = 0;
916
917	if (*dev != NULL)
918		return;
919
920	for (i = 0; i < NDEVTYPE; i++)
921		if (dev_stdclone(name, &subp, devnames[i], &unit) == 2)
922			goto found;
923	/* not match */
924	return;
925found:
926
927	if (subp == NULL || *subp++ != '.')
928		return;
929
930	/* /dev/fwU.S */
931	while (isdigit(*subp)) {
932		sub *= 10;
933		sub += *subp++ - '0';
934	}
935	if (*subp != '\0')
936		return;
937
938	sc = devclass_get_softc(firewire_devclass, unit);
939	if (sc == NULL)
940		return;
941	*dev = make_dev_credf(MAKEDEV_REF, &firewire_cdevsw,
942	    MAKEMINOR(devflag[i], unit, sub), cred, UID_ROOT, GID_OPERATOR,
943	    0660, "%s%d.%d", devnames[i], unit, sub);
944	dev_depends(sc->dev, *dev);
945	return;
946}
947