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