fwdev.c revision 121466
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 121466 2003-10-24 13:55:51Z 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		break;
475	case FW_GTSTREAM:
476		if (it != NULL) {
477			ichreq->ch = it->flag & 0x3f;
478			ichreq->tag = it->flag >> 2 & 0x3;
479		} else
480			err = EINVAL;
481		break;
482	case FW_SRSTREAM:
483		if (ir == NULL) {
484			for (i = 0; i < fc->nisodma; i ++) {
485				ir = fc->ir[i];
486				if ((ir->flag & FWXFERQ_OPEN) == 0)
487					break;
488			}
489			if (i >= fc->nisodma) {
490				err = EBUSY;
491				break;
492			}
493			err = fwdev_allocbuf(fc, ir, &d->bufreq.rx);
494			if (err)
495				break;
496			ir->flag |=  FWXFERQ_OPEN;
497		}
498		ir->flag &= ~0xff;
499		ir->flag |= (0x3f & ichreq->ch);
500		ir->flag |= ((0x3 & ichreq->tag) << 6);
501		d->ir = ir;
502		err = fc->irx_enable(fc, ir->dmach);
503		break;
504	case FW_GRSTREAM:
505		if (d->ir != NULL) {
506			ichreq->ch = ir->flag & 0x3f;
507			ichreq->tag = ir->flag >> 2 & 0x3;
508		} else
509			err = EINVAL;
510		break;
511	case FW_SSTBUF:
512		bcopy(ibufreq, &d->bufreq, sizeof(d->bufreq));
513		break;
514	case FW_GSTBUF:
515		bzero(&ibufreq->rx, sizeof(ibufreq->rx));
516		if (ir != NULL) {
517			ibufreq->rx.nchunk = ir->bnchunk;
518			ibufreq->rx.npacket = ir->bnpacket;
519			ibufreq->rx.psize = ir->psize;
520		}
521		bzero(&ibufreq->tx, sizeof(ibufreq->tx));
522		if (it != NULL) {
523			ibufreq->tx.nchunk = it->bnchunk;
524			ibufreq->tx.npacket = it->bnpacket;
525			ibufreq->tx.psize = it->psize;
526		}
527		break;
528	case FW_ASYREQ:
529	{
530		struct tcode_info *tinfo;
531		int pay_len = 0;
532
533		fp = &asyreq->pkt;
534		tinfo = &sc->fc->tcode[fp->mode.hdr.tcode];
535
536		if ((tinfo->flag & FWTI_BLOCK_ASY) != 0)
537			pay_len = MAX(0, asyreq->req.len - tinfo->hdr_len);
538
539		xfer = fw_xfer_alloc_buf(M_FWXFER, pay_len, PAGE_SIZE/*XXX*/);
540		if (xfer == NULL)
541			return (ENOMEM);
542
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 out;
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
565		bcopy(fp, (void *)&xfer->send.hdr, tinfo->hdr_len);
566		if (pay_len > 0)
567			bcopy((char *)fp + tinfo->hdr_len,
568			    (void *)&xfer->send.payload, pay_len);
569		xfer->send.spd = asyreq->req.sped;
570		xfer->act.hand = fw_asy_callback;
571
572		if ((err = fw_asyreq(sc->fc, -1, xfer)) != 0)
573			goto out;
574		if ((err = tsleep(xfer, FWPRI, "asyreq", hz)) != 0)
575			goto out;
576		if (xfer->resp != 0) {
577			err = EIO;
578			goto out;
579		}
580		if ((tinfo->flag & FWTI_TLABEL) == 0)
581			goto out;
582
583		/* copy response */
584		tinfo = &sc->fc->tcode[xfer->recv.hdr.mode.hdr.tcode];
585		if (asyreq->req.len >= xfer->recv.pay_len + tinfo->hdr_len)
586			asyreq->req.len = xfer->recv.pay_len;
587		else
588			err = EINVAL;
589		bcopy(&xfer->recv.hdr, fp, tinfo->hdr_len);
590		bcopy(xfer->recv.payload, (char *)fp + tinfo->hdr_len,
591		    MAX(0, asyreq->req.len - tinfo->hdr_len));
592out:
593		fw_xfer_free_buf(xfer);
594		break;
595	}
596	case FW_IBUSRST:
597		sc->fc->ibr(sc->fc);
598		break;
599	case FW_CBINDADDR:
600		fwb = fw_bindlookup(sc->fc,
601				bindreq->start.hi, bindreq->start.lo);
602		if(fwb == NULL){
603			err = EINVAL;
604			break;
605		}
606		STAILQ_REMOVE(&sc->fc->binds, fwb, fw_bind, fclist);
607		STAILQ_REMOVE(&ir->binds, fwb, fw_bind, chlist);
608		free(fwb, M_FW);
609		break;
610	case FW_SBINDADDR:
611		if(bindreq->len <= 0 ){
612			err = EINVAL;
613			break;
614		}
615		if(bindreq->start.hi > 0xffff ){
616			err = EINVAL;
617			break;
618		}
619		fwb = (struct fw_bind *)malloc(sizeof (struct fw_bind), M_FW, M_NOWAIT);
620		if(fwb == NULL){
621			err = ENOMEM;
622			break;
623		}
624		fwb->start = ((u_int64_t)bindreq->start.hi << 32) |
625		    bindreq->start.lo;
626		fwb->end = fwb->start +  bindreq->len;
627		/* XXX */
628		fwb->sub = ir->dmach;
629		fwb->act_type = FWACT_CH;
630
631		/* XXX alloc buf */
632		xfer = fw_xfer_alloc(M_FWXFER);
633		if(xfer == NULL){
634			err = ENOMEM;
635			return err;
636		}
637		xfer->fc = sc->fc;
638
639		s = splfw();
640		/* XXX broken. need multiple xfer */
641		STAILQ_INIT(&fwb->xferlist);
642		STAILQ_INSERT_TAIL(&fwb->xferlist, xfer, link);
643		splx(s);
644		err = fw_bindadd(sc->fc, fwb);
645		break;
646	case FW_GDEVLST:
647		i = len = 1;
648		/* myself */
649		devinfo = &fwdevlst->dev[0];
650		devinfo->dst = sc->fc->nodeid;
651		devinfo->status = 0;	/* XXX */
652		devinfo->eui.hi = sc->fc->eui.hi;
653		devinfo->eui.lo = sc->fc->eui.lo;
654		STAILQ_FOREACH(fwdev, &sc->fc->devices, link) {
655			if(len < FW_MAX_DEVLST){
656				devinfo = &fwdevlst->dev[len++];
657				devinfo->dst = fwdev->dst;
658				devinfo->status =
659					(fwdev->status == FWDEVINVAL)?0:1;
660				devinfo->eui.hi = fwdev->eui.hi;
661				devinfo->eui.lo = fwdev->eui.lo;
662			}
663			i++;
664		}
665		fwdevlst->n = i;
666		fwdevlst->info_len = len;
667		break;
668	case FW_GTPMAP:
669		bcopy(sc->fc->topology_map, data,
670				(sc->fc->topology_map->crc_len + 1) * 4);
671		break;
672	case FW_GCROM:
673		STAILQ_FOREACH(fwdev, &sc->fc->devices, link)
674			if (FW_EUI64_EQUAL(fwdev->eui, crom_buf->eui))
675				break;
676		if (fwdev == NULL) {
677			if (!FW_EUI64_EQUAL(sc->fc->eui, crom_buf->eui)) {
678				err = FWNODE_INVAL;
679				break;
680			}
681			/* myself */
682			ptr = malloc(CROMSIZE, M_FW, M_WAITOK);
683			len = CROMSIZE;
684			for (i = 0; i < CROMSIZE/4; i++)
685				((u_int32_t *)ptr)[i]
686					= ntohl(sc->fc->config_rom[i]);
687		} else {
688			/* found */
689			ptr = (void *)&fwdev->csrrom[0];
690			if (fwdev->rommax < CSRROMOFF)
691				len = 0;
692			else
693				len = fwdev->rommax - CSRROMOFF + 4;
694		}
695		if (crom_buf->len < len)
696			len = crom_buf->len;
697		else
698			crom_buf->len = len;
699		err = copyout(ptr, crom_buf->ptr, len);
700		if (fwdev == NULL)
701			/* myself */
702			free(ptr, M_FW);
703		break;
704	default:
705		sc->fc->ioctl (dev, cmd, data, flag, td);
706		break;
707	}
708	return err;
709}
710int
711fw_poll(dev_t dev, int events, fw_proc *td)
712{
713	struct firewire_softc *sc;
714	struct fw_xferq *ir;
715	int revents;
716	int tmp;
717	int unit = DEV2UNIT(dev);
718
719	if (DEV_FWMEM(dev))
720		return fwmem_poll(dev, events, td);
721
722	sc = devclass_get_softc(firewire_devclass, unit);
723	ir = ((struct fw_drv1 *)dev->si_drv1)->ir;
724	revents = 0;
725	tmp = POLLIN | POLLRDNORM;
726	if (events & tmp) {
727		if (STAILQ_FIRST(&ir->q) != NULL)
728			revents |= tmp;
729		else
730			selrecord(td, &ir->rsel);
731	}
732	tmp = POLLOUT | POLLWRNORM;
733	if (events & tmp) {
734		/* XXX should be fixed */
735		revents |= tmp;
736	}
737
738	return revents;
739}
740
741static int
742#if __FreeBSD_version < 500102
743fw_mmap (dev_t dev, vm_offset_t offset, int nproto)
744#else
745fw_mmap (dev_t dev, vm_offset_t offset, vm_paddr_t *paddr, int nproto)
746#endif
747{
748	struct firewire_softc *sc;
749	int unit = DEV2UNIT(dev);
750
751	if (DEV_FWMEM(dev))
752#if __FreeBSD_version < 500102
753		return fwmem_mmap(dev, offset, nproto);
754#else
755		return fwmem_mmap(dev, offset, paddr, nproto);
756#endif
757
758	sc = devclass_get_softc(firewire_devclass, unit);
759
760	return EINVAL;
761}
762
763static void
764fw_strategy(struct bio *bp)
765{
766	dev_t dev;
767
768	dev = bp->bio_dev;
769	if (DEV_FWMEM(dev)) {
770		fwmem_strategy(bp);
771		return;
772	}
773
774	bp->bio_error = EOPNOTSUPP;
775	bp->bio_flags |= BIO_ERROR;
776	bp->bio_resid = bp->bio_bcount;
777	biodone(bp);
778}
779
780int
781fwdev_makedev(struct firewire_softc *sc)
782{
783	int err = 0;
784
785#if __FreeBSD_version >= 500000
786	dev_t d;
787	int unit;
788
789	unit = device_get_unit(sc->fc->bdev);
790	sc->dev = make_dev(&firewire_cdevsw, MAKEMINOR(0, unit, 0),
791			UID_ROOT, GID_OPERATOR, 0660,
792			"fw%d.%d", unit, 0);
793	d = make_dev(&firewire_cdevsw,
794			MAKEMINOR(FWMEM_FLAG, unit, 0),
795			UID_ROOT, GID_OPERATOR, 0660,
796			"fwmem%d.%d", unit, 0);
797	dev_depends(sc->dev, d);
798	make_dev_alias(sc->dev, "fw%d", unit);
799	make_dev_alias(d, "fwmem%d", unit);
800#else
801	cdevsw_add(&firewire_cdevsw);
802#endif
803
804	return (err);
805}
806
807int
808fwdev_destroydev(struct firewire_softc *sc)
809{
810	int err = 0;
811
812#if __FreeBSD_version >= 500000
813	destroy_dev(sc->dev);
814#else
815	cdevsw_remove(&firewire_cdevsw);
816#endif
817	return (err);
818}
819
820#if __FreeBSD_version >= 500000
821#define NDEVTYPE 2
822void
823fwdev_clone(void *arg, char *name, int namelen, dev_t *dev)
824{
825	struct firewire_softc *sc;
826	char *devnames[NDEVTYPE] = {"fw", "fwmem"};
827	char *subp = NULL;
828	int devflag[NDEVTYPE] = {0, FWMEM_FLAG};
829	int i, unit = 0, sub = 0;
830
831	if (*dev != NODEV)
832		return;
833
834	for (i = 0; i < NDEVTYPE; i++)
835		if (dev_stdclone(name, &subp, devnames[i], &unit) == 2)
836			goto found;
837	/* not match */
838	return;
839found:
840
841	if (subp == NULL || *subp++ != '.')
842		return;
843
844	/* /dev/fwU.S */
845	while (isdigit(*subp)) {
846		sub *= 10;
847		sub += *subp++ - '0';
848	}
849	if (*subp != '\0')
850		return;
851
852	sc = devclass_get_softc(firewire_devclass, unit);
853	if (sc == NULL)
854		return;
855	*dev = make_dev(&firewire_cdevsw, MAKEMINOR(devflag[i], unit, sub),
856		       UID_ROOT, GID_OPERATOR, 0660,
857		       "%s%d.%d", devnames[i], unit, sub);
858	(*dev)->si_flags |= SI_CHEAPCLONE;
859	dev_depends(sc->dev, *dev);
860	return;
861}
862#endif
863