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