xref: /netbsd/sys/dev/ieee1394/fwdev.c (revision 52e18adc)
1 /*	$NetBSD: fwdev.c,v 1.33 2021/11/10 16:08:17 msaitoh 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.33 2021/11/10 16:08:17 msaitoh 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 	kcondvar_t cv;
108 };
109 
110 static int fwdev_allocbuf(struct firewire_comm *, struct fw_xferq *,
111 			  struct fw_bufspec *);
112 static int fwdev_freebuf(struct fw_xferq *);
113 static int fw_read_async(struct fw_drv1 *, struct uio *, int);
114 static int fw_write_async(struct fw_drv1 *, struct uio *, int);
115 static void fw_hand(struct fw_xfer *);
116 
117 
118 int
fw_open(dev_t dev,int flags,int fmt,struct lwp * td)119 fw_open(dev_t dev, int flags, int fmt, struct lwp *td)
120 {
121 	struct firewire_softc *sc;
122 	struct fw_drv1 *d;
123 	int err = 0;
124 
125 	sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
126 	if (sc == NULL)
127 		return ENXIO;
128 
129 	if (DEV_FWMEM(dev))
130 		return fwmem_open(dev, flags, fmt, td);
131 
132 	mutex_enter(&sc->fc->fc_mtx);
133 	if (sc->si_drv1 != NULL) {
134 		mutex_exit(&sc->fc->fc_mtx);
135 		return EBUSY;
136 	}
137 	/* set dummy value for allocation */
138 	sc->si_drv1 = (void *)-1;
139 	mutex_exit(&sc->fc->fc_mtx);
140 
141 	sc->si_drv1 = malloc(sizeof(struct fw_drv1), M_FW, M_WAITOK | M_ZERO);
142 	if (sc->si_drv1 == NULL)
143 		return ENOMEM;
144 
145 	d = (struct fw_drv1 *)sc->si_drv1;
146 	d->fc = sc->fc;
147 	STAILQ_INIT(&d->binds);
148 	STAILQ_INIT(&d->rq);
149 	cv_init(&d->cv, "fwra");
150 
151 	return err;
152 }
153 
154 int
fw_close(dev_t dev,int flags,int fmt,struct lwp * td)155 fw_close(dev_t dev, int flags, int fmt, struct lwp *td)
156 {
157 	struct firewire_softc *sc;
158 	struct firewire_comm *fc;
159 	struct fw_drv1 *d;
160 	struct fw_xfer *xfer;
161 	struct fw_bind *fwb;
162         int err = 0;
163 
164 	sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
165 	if (sc == NULL)
166 		return ENXIO;
167 
168 	if (DEV_FWMEM(dev))
169 		return fwmem_close(dev, flags, fmt, td);
170 
171 	d = (struct fw_drv1 *)sc->si_drv1;
172 	fc = d->fc;
173 
174 	/* remove binding */
175 	for (fwb = STAILQ_FIRST(&d->binds); fwb != NULL;
176 	    fwb = STAILQ_FIRST(&d->binds)) {
177 		fw_bindremove(fc, fwb);
178 		STAILQ_REMOVE_HEAD(&d->binds, chlist);
179 		fw_xferlist_remove(&fwb->xferlist);
180 		free(fwb, M_FW);
181 	}
182 	if (d->ir != NULL) {
183 		struct fw_xferq *ir = d->ir;
184 
185 		if ((ir->flag & FWXFERQ_OPEN) == 0)
186 			return EINVAL;
187 		if (ir->flag & FWXFERQ_RUNNING) {
188 			ir->flag &= ~FWXFERQ_RUNNING;
189 			fc->irx_disable(fc, ir->dmach);
190 		}
191 		/* free extbuf */
192 		fwdev_freebuf(ir);
193 		/* drain receiving buffer */
194 		for (xfer = STAILQ_FIRST(&ir->q); xfer != NULL;
195 		    xfer = STAILQ_FIRST(&ir->q)) {
196 			ir->queued--;
197 			STAILQ_REMOVE_HEAD(&ir->q, link);
198 
199 			xfer->resp = 0;
200 			fw_xfer_done(xfer);
201 		}
202 		ir->flag &=
203 		    ~(FWXFERQ_OPEN | FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK);
204 		d->ir = NULL;
205 
206 	}
207 	if (d->it != NULL) {
208 		struct fw_xferq *it = d->it;
209 
210 		if ((it->flag & FWXFERQ_OPEN) == 0)
211 			return EINVAL;
212 		if (it->flag & FWXFERQ_RUNNING) {
213 			it->flag &= ~FWXFERQ_RUNNING;
214 			fc->itx_disable(fc, it->dmach);
215 		}
216 		/* free extbuf */
217 		fwdev_freebuf(it);
218 		it->flag &=
219 		    ~(FWXFERQ_OPEN | FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK);
220 		d->it = NULL;
221 	}
222 	cv_destroy(&d->cv);
223 	free(sc->si_drv1, M_FW);
224 	sc->si_drv1 = NULL;
225 
226 	return err;
227 }
228 
229 int
fw_read(dev_t dev,struct uio * uio,int ioflag)230 fw_read(dev_t dev, struct uio *uio, int ioflag)
231 {
232 	struct firewire_softc *sc;
233 	struct firewire_comm *fc;
234 	struct fw_drv1 *d;
235 	struct fw_xferq *ir;
236 	struct fw_pkt *fp;
237 	int err = 0, slept = 0;
238 
239 	sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
240 	if (sc == NULL)
241 		return ENXIO;
242 
243 	if (DEV_FWMEM(dev))
244 		return physio(fw_strategy, NULL, dev, ioflag, minphys, uio);
245 
246 	d = (struct fw_drv1 *)sc->si_drv1;
247 	fc = d->fc;
248 	ir = d->ir;
249 
250 	if (ir == NULL)
251 		return fw_read_async(d, uio, ioflag);
252 
253 	if (ir->buf == NULL)
254 		return EIO;
255 
256 	mutex_enter(&fc->fc_mtx);
257 readloop:
258 	if (ir->stproc == NULL) {
259 		/* iso bulkxfer */
260 		ir->stproc = STAILQ_FIRST(&ir->stvalid);
261 		if (ir->stproc != NULL) {
262 			STAILQ_REMOVE_HEAD(&ir->stvalid, link);
263 			ir->queued = 0;
264 		}
265 	}
266 	if (ir->stproc == NULL) {
267 		/* no data available */
268 		if (slept == 0) {
269 			slept = 1;
270 			ir->flag |= FWXFERQ_WAKEUP;
271 			err = cv_timedwait_sig(&ir->cv, &fc->fc_mtx, hz);
272 			ir->flag &= ~FWXFERQ_WAKEUP;
273 			if (err == 0)
274 				goto readloop;
275 		} else if (slept == 1)
276 			err = EIO;
277 		mutex_exit(&fc->fc_mtx);
278 		return err;
279 	} else if (ir->stproc != NULL) {
280 		/* iso bulkxfer */
281 		mutex_exit(&fc->fc_mtx);
282 		fp = (struct fw_pkt *)fwdma_v_addr(ir->buf,
283 		    ir->stproc->poffset + ir->queued);
284 		if (fc->irx_post != NULL)
285 			fc->irx_post(fc, fp->mode.ld);
286 		if (fp->mode.stream.len == 0)
287 			return EIO;
288 		err = uiomove((void *)fp,
289 		    fp->mode.stream.len + sizeof(uint32_t), uio);
290 		ir->queued++;
291 		if (ir->queued >= ir->bnpacket) {
292 			STAILQ_INSERT_TAIL(&ir->stfree, ir->stproc, link);
293 			fc->irx_enable(fc, ir->dmach);
294 			ir->stproc = NULL;
295 		}
296 		if (uio->uio_resid >= ir->psize) {
297 			slept = -1;
298 			mutex_enter(&fc->fc_mtx);
299 			goto readloop;
300 		}
301 	} else
302 		mutex_exit(&fc->fc_mtx);
303 	return err;
304 }
305 
306 int
fw_write(dev_t dev,struct uio * uio,int ioflag)307 fw_write(dev_t dev, struct uio *uio, int ioflag)
308 {
309 	struct firewire_softc *sc;
310 	struct firewire_comm *fc;
311 	struct fw_drv1 *d;
312 	struct fw_pkt *fp;
313 	struct fw_xferq *it;
314         int slept = 0, err = 0;
315 
316 	sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
317 	if (sc == NULL)
318 		return ENXIO;
319 
320 	if (DEV_FWMEM(dev))
321 		return physio(fw_strategy, NULL, dev, ioflag, minphys, uio);
322 
323 	d = (struct fw_drv1 *)sc->si_drv1;
324 	fc = d->fc;
325 	it = d->it;
326 
327 	if (it == NULL)
328 		return fw_write_async(d, uio, ioflag);
329 
330 	if (it->buf == NULL)
331 		return EIO;
332 
333 	mutex_enter(&fc->fc_mtx);
334 isoloop:
335 	if (it->stproc == NULL) {
336 		it->stproc = STAILQ_FIRST(&it->stfree);
337 		if (it->stproc != NULL) {
338 			STAILQ_REMOVE_HEAD(&it->stfree, link);
339 			it->queued = 0;
340 		} else if (slept == 0) {
341 			slept = 1;
342 #if 0   /* XXX to avoid lock recursion */
343 			err = fc->itx_enable(fc, it->dmach);
344 			if (err)
345 				goto out;
346 #endif
347 			err = cv_timedwait_sig(&it->cv, &fc->fc_mtx, hz);
348 			if (err)
349 				goto out;
350 			goto isoloop;
351 		} else {
352 			err = EIO;
353 			goto out;
354 		}
355 	}
356 	mutex_exit(&fc->fc_mtx);
357 	fp = (struct fw_pkt *)fwdma_v_addr(it->buf,
358 	    it->stproc->poffset + it->queued);
359 	err = uiomove((void *)fp, sizeof(struct fw_isohdr), uio);
360 	if (err != 0)
361 		return err;
362 	err =
363 	    uiomove((void *)fp->mode.stream.payload, fp->mode.stream.len, uio);
364 	it->queued++;
365 	if (it->queued >= it->bnpacket) {
366 		STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link);
367 		it->stproc = NULL;
368 		err = fc->itx_enable(fc, it->dmach);
369 	}
370 	if (uio->uio_resid >= sizeof(struct fw_isohdr)) {
371 		slept = 0;
372 		mutex_enter(&fc->fc_mtx);
373 		goto isoloop;
374 	}
375 	return err;
376 
377 out:
378 	mutex_exit(&fc->fc_mtx);
379 	return err;
380 }
381 
382 int
fw_ioctl(dev_t dev,u_long cmd,void * data,int flag,struct lwp * td)383 fw_ioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *td)
384 {
385 	struct firewire_softc *sc;
386 	struct firewire_comm *fc;
387 	struct fw_drv1 *d;
388 	struct fw_device *fwdev;
389 	struct fw_bind *fwb;
390 	struct fw_xferq *ir, *it;
391 	struct fw_xfer *xfer;
392 	struct fw_pkt *fp;
393 	struct fw_devinfo *devinfo;
394 	struct fw_devlstreq *fwdevlst = (struct fw_devlstreq *)data;
395 	struct fw_asyreq *asyreq = (struct fw_asyreq *)data;
396 	struct fw_isochreq *ichreq = (struct fw_isochreq *)data;
397 	struct fw_isobufreq *ibufreq = (struct fw_isobufreq *)data;
398 	struct fw_asybindreq *bindreq = (struct fw_asybindreq *)data;
399 	struct fw_crom_buf *crom_buf = (struct fw_crom_buf *)data;
400 	int i, len, err = 0;
401 	void *ptr;
402 
403 	sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
404 	if (sc == NULL)
405 		return ENXIO;
406 
407 	if (DEV_FWMEM(dev))
408 		return fwmem_ioctl(dev, cmd, data, flag, td);
409 
410 	if (!data)
411 		return EINVAL;
412 
413 	d = (struct fw_drv1 *)sc->si_drv1;
414 	fc = d->fc;
415 	ir = d->ir;
416 	it = d->it;
417 
418 	switch (cmd) {
419 	case FW_STSTREAM:
420 		if (it == NULL) {
421 			i = fw_open_isodma(fc, /* tx */1);
422 			if (i < 0) {
423 				err = EBUSY;
424 				break;
425 			}
426 			it = fc->it[i];
427 			err = fwdev_allocbuf(fc, it, &d->bufreq.tx);
428 			if (err) {
429 				it->flag &= ~FWXFERQ_OPEN;
430 				break;
431 			}
432 		}
433 		it->flag &= ~0xff;
434 		it->flag |= (0x3f & ichreq->ch);
435 		it->flag |= ((0x3 & ichreq->tag) << 6);
436 		d->it = it;
437 		break;
438 
439 	case FW_GTSTREAM:
440 		if (it != NULL) {
441 			ichreq->ch = it->flag & 0x3f;
442 			ichreq->tag = it->flag >> 2 & 0x3;
443 		} else
444 			err = EINVAL;
445 		break;
446 
447 	case FW_SRSTREAM:
448 		if (ir == NULL) {
449 			i = fw_open_isodma(fc, /* tx */0);
450 			if (i < 0) {
451 				err = EBUSY;
452 				break;
453 			}
454 			ir = fc->ir[i];
455 			err = fwdev_allocbuf(fc, ir, &d->bufreq.rx);
456 			if (err) {
457 				ir->flag &= ~FWXFERQ_OPEN;
458 				break;
459 			}
460 		}
461 		ir->flag &= ~0xff;
462 		ir->flag |= (0x3f & ichreq->ch);
463 		ir->flag |= ((0x3 & ichreq->tag) << 6);
464 		d->ir = ir;
465 		err = fc->irx_enable(fc, ir->dmach);
466 		break;
467 
468 	case FW_GRSTREAM:
469 		if (d->ir != NULL) {
470 			ichreq->ch = ir->flag & 0x3f;
471 			ichreq->tag = ir->flag >> 2 & 0x3;
472 		} else
473 			err = EINVAL;
474 		break;
475 
476 	case FW_SSTBUF:
477 		memcpy(&d->bufreq, ibufreq, sizeof(d->bufreq));
478 		break;
479 
480 	case FW_GSTBUF:
481 		memset(&ibufreq->rx, 0, sizeof(ibufreq->rx));
482 		if (ir != NULL) {
483 			ibufreq->rx.nchunk = ir->bnchunk;
484 			ibufreq->rx.npacket = ir->bnpacket;
485 			ibufreq->rx.psize = ir->psize;
486 		}
487 		memset(&ibufreq->tx, 0, sizeof(ibufreq->tx));
488 		if (it != NULL) {
489 			ibufreq->tx.nchunk = it->bnchunk;
490 			ibufreq->tx.npacket = it->bnpacket;
491 			ibufreq->tx.psize = it->psize;
492 		}
493 		break;
494 
495 	case FW_ASYREQ:
496 	{
497 		const struct tcode_info *tinfo;
498 		int pay_len = 0;
499 
500 		fp = &asyreq->pkt;
501 		tinfo = &fc->tcode[fp->mode.hdr.tcode];
502 
503 		if ((tinfo->flag & FWTI_BLOCK_ASY) != 0)
504 			pay_len = MAX(0, asyreq->req.len - tinfo->hdr_len);
505 
506 		xfer = fw_xfer_alloc_buf(M_FW, pay_len, PAGE_SIZE/*XXX*/);
507 		if (xfer == NULL)
508 			return ENOMEM;
509 
510 		switch (asyreq->req.type) {
511 		case FWASREQNODE:
512 			break;
513 
514 		case FWASREQEUI:
515 			fwdev = fw_noderesolve_eui64(fc, &asyreq->req.dst.eui);
516 			if (fwdev == NULL) {
517 				aprint_error_dev(fc->bdev,
518 				    "cannot find node\n");
519 				err = EINVAL;
520 				goto out;
521 			}
522 			fp->mode.hdr.dst = FWLOCALBUS | fwdev->dst;
523 			break;
524 
525 		case FWASRESTL:
526 			/* XXX what's this? */
527 			break;
528 
529 		case FWASREQSTREAM:
530 			/* nothing to do */
531 			break;
532 		}
533 
534 		memcpy(&xfer->send.hdr, fp, tinfo->hdr_len);
535 		if (pay_len > 0)
536 			memcpy(xfer->send.payload, (char *)fp + tinfo->hdr_len,
537 			    pay_len);
538 		xfer->send.spd = asyreq->req.sped;
539 		xfer->hand = fw_xferwake;
540 
541 		if ((err = fw_asyreq(fc, -1, xfer)) != 0)
542 			goto out;
543 		if ((err = fw_xferwait(xfer)) != 0)
544 			goto out;
545 		if (xfer->resp != 0) {
546 			err = EIO;
547 			goto out;
548 		}
549 		if ((tinfo->flag & FWTI_TLABEL) == 0)
550 			goto out;
551 
552 		/* copy response */
553 		tinfo = &fc->tcode[xfer->recv.hdr.mode.hdr.tcode];
554 		if (xfer->recv.hdr.mode.hdr.tcode == FWTCODE_RRESB ||
555 		    xfer->recv.hdr.mode.hdr.tcode == FWTCODE_LRES) {
556 			pay_len = xfer->recv.pay_len;
557 			if (asyreq->req.len >=
558 			    xfer->recv.pay_len + tinfo->hdr_len)
559 				asyreq->req.len =
560 				    xfer->recv.pay_len + tinfo->hdr_len;
561 			else {
562 				err = EINVAL;
563 				pay_len = 0;
564 			}
565 		} else
566 			pay_len = 0;
567 		memcpy(fp, &xfer->recv.hdr, tinfo->hdr_len);
568 		memcpy((char *)fp + tinfo->hdr_len, xfer->recv.payload,
569 		    pay_len);
570 out:
571 		fw_xfer_free_buf(xfer);
572 		break;
573 	}
574 
575 	case FW_IBUSRST:
576 		fc->ibr(fc);
577 		break;
578 
579 	case FW_CBINDADDR:
580 		fwb = fw_bindlookup(fc, bindreq->start.hi, bindreq->start.lo);
581 		if (fwb == NULL) {
582 			err = EINVAL;
583 			break;
584 		}
585 		fw_bindremove(fc, fwb);
586 		STAILQ_REMOVE(&d->binds, fwb, fw_bind, chlist);
587 		fw_xferlist_remove(&fwb->xferlist);
588 		free(fwb, M_FW);
589 		break;
590 
591 	case FW_SBINDADDR:
592 		if (bindreq->len <= 0 ) {
593 			err = EINVAL;
594 			break;
595 		}
596 		if (bindreq->start.hi > 0xffff ) {
597 			err = EINVAL;
598 			break;
599 		}
600 		fwb = (struct fw_bind *)malloc(sizeof(struct fw_bind),
601 		    M_FW, M_WAITOK);
602 		if (fwb == NULL) {
603 			err = ENOMEM;
604 			break;
605 		}
606 		fwb->start = ((u_int64_t)bindreq->start.hi << 32) |
607 		    bindreq->start.lo;
608 		fwb->end = fwb->start +  bindreq->len;
609 		fwb->sc = (void *)d;
610 		STAILQ_INIT(&fwb->xferlist);
611 		err = fw_bindadd(fc, fwb);
612 		if (err == 0) {
613 			fw_xferlist_add(&fwb->xferlist, M_FW,
614 			    /* XXX */
615 			    PAGE_SIZE, PAGE_SIZE, 5, fc, (void *)fwb, fw_hand);
616 			STAILQ_INSERT_TAIL(&d->binds, fwb, chlist);
617 		} else {
618 			free(fwb, M_FW);
619 		}
620 		break;
621 
622 	case FW_GDEVLST:
623 		i = len = 1;
624 		/* myself */
625 		devinfo = fwdevlst->dev;
626 		devinfo->dst = fc->nodeid;
627 		devinfo->status = 0;	/* XXX */
628 		devinfo->eui.hi = fc->eui.hi;
629 		devinfo->eui.lo = fc->eui.lo;
630 		STAILQ_FOREACH(fwdev, &fc->devices, link) {
631 			if (len < FW_MAX_DEVLST) {
632 				devinfo = &fwdevlst->dev[len++];
633 				devinfo->dst = fwdev->dst;
634 				devinfo->status =
635 				    (fwdev->status == FWDEVINVAL) ? 0 : 1;
636 				devinfo->eui.hi = fwdev->eui.hi;
637 				devinfo->eui.lo = fwdev->eui.lo;
638 			}
639 			i++;
640 		}
641 		fwdevlst->n = i;
642 		fwdevlst->info_len = len;
643 		break;
644 
645 	case FW_GTPMAP:
646 		memcpy(data, fc->topology_map,
647 		    (fc->topology_map->crc_len + 1) * 4);
648 		break;
649 
650 	case FW_GCROM:
651 		STAILQ_FOREACH(fwdev, &fc->devices, link)
652 			if (FW_EUI64_EQUAL(fwdev->eui, crom_buf->eui))
653 				break;
654 		if (fwdev == NULL) {
655 			if (!FW_EUI64_EQUAL(fc->eui, crom_buf->eui)) {
656 				err = FWNODE_INVAL;
657 				break;
658 			}
659 			/* myself */
660 			ptr = malloc(CROMSIZE, M_FW, M_WAITOK);
661 			len = CROMSIZE;
662 			for (i = 0; i < CROMSIZE/4; i++)
663 				((uint32_t *)ptr)[i] = ntohl(fc->config_rom[i]);
664 		} else {
665 			/* found */
666 			ptr = (void *)fwdev->csrrom;
667 			if (fwdev->rommax < CSRROMOFF)
668 				len = 0;
669 			else
670 				len = fwdev->rommax - CSRROMOFF + 4;
671 		}
672 		if (crom_buf->len < len)
673 			len = crom_buf->len;
674 		else
675 			crom_buf->len = len;
676 		err = copyout(ptr, crom_buf->ptr, len);
677 		if (fwdev == NULL)
678 			/* myself */
679 			free(ptr, M_FW);
680 		break;
681 
682 	default:
683 		fc->ioctl(dev, cmd, data, flag, td);
684 		break;
685 	}
686 	return err;
687 }
688 
689 int
fw_poll(dev_t dev,int events,struct lwp * td)690 fw_poll(dev_t dev, int events, struct lwp *td)
691 {
692 	struct firewire_softc *sc;
693 	struct fw_xferq *ir;
694 	int revents, tmp;
695 
696 	sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
697 	if (sc == NULL)
698 		return ENXIO;
699 
700 	ir = ((struct fw_drv1 *)sc->si_drv1)->ir;
701 	revents = 0;
702 	tmp = POLLIN | POLLRDNORM;
703 	if (events & tmp) {
704 		if (STAILQ_FIRST(&ir->q) != NULL)
705 			revents |= tmp;
706 		else
707 			selrecord(td, &ir->rsel);
708 	}
709 	tmp = POLLOUT | POLLWRNORM;
710 	if (events & tmp)
711 		/* XXX should be fixed */
712 		revents |= tmp;
713 
714 	return revents;
715 }
716 
717 paddr_t
fw_mmap(dev_t dev,off_t offset,int nproto)718 fw_mmap(dev_t dev, off_t offset, int nproto)
719 {
720 	struct firewire_softc *sc;
721 
722 	sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
723 	if (sc == NULL)
724 		return ENXIO;
725 
726 	return EINVAL;
727 }
728 
729 void
fw_strategy(struct bio * bp)730 fw_strategy(struct bio *bp)
731 {
732 	struct firewire_softc *sc;
733 	dev_t dev = bp->bio_dev;
734 
735 	sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
736 	if (sc == NULL)
737 		return;
738 
739 	if (DEV_FWMEM(dev)) {
740 		fwmem_strategy(bp);
741 		return;
742 	}
743 
744 	bp->bio_error = EOPNOTSUPP;
745 	bp->bio_resid = bp->bio_bcount;
746 	biodone(bp);
747 }
748 
749 
750 static int
fwdev_allocbuf(struct firewire_comm * fc,struct fw_xferq * q,struct fw_bufspec * b)751 fwdev_allocbuf(struct firewire_comm *fc, struct fw_xferq *q,
752 	       struct fw_bufspec *b)
753 {
754 	int i;
755 
756 	if (q->flag & (FWXFERQ_RUNNING | FWXFERQ_EXTBUF))
757 		return EBUSY;
758 
759 	q->bulkxfer =
760 	    (struct fw_bulkxfer *)malloc(sizeof(struct fw_bulkxfer) * b->nchunk,
761 								M_FW, M_WAITOK);
762 	if (q->bulkxfer == NULL)
763 		return ENOMEM;
764 
765 	b->psize = roundup2(b->psize, sizeof(uint32_t));
766 	q->buf = fwdma_malloc_multiseg(fc, sizeof(uint32_t), b->psize,
767 	    b->nchunk * b->npacket, BUS_DMA_WAITOK);
768 
769 	if (q->buf == NULL) {
770 		free(q->bulkxfer, M_FW);
771 		q->bulkxfer = NULL;
772 		return ENOMEM;
773 	}
774 	q->bnchunk = b->nchunk;
775 	q->bnpacket = b->npacket;
776 	q->psize = (b->psize + 3) & ~3;
777 	q->queued = 0;
778 
779 	STAILQ_INIT(&q->stvalid);
780 	STAILQ_INIT(&q->stfree);
781 	STAILQ_INIT(&q->stdma);
782 	q->stproc = NULL;
783 
784 	for (i = 0 ; i < q->bnchunk; i++) {
785 		q->bulkxfer[i].poffset = i * q->bnpacket;
786 		q->bulkxfer[i].mbuf = NULL;
787 		STAILQ_INSERT_TAIL(&q->stfree, &q->bulkxfer[i], link);
788 	}
789 
790 	q->flag &= ~FWXFERQ_MODEMASK;
791 	q->flag |= FWXFERQ_STREAM;
792 	q->flag |= FWXFERQ_EXTBUF;
793 
794 	return 0;
795 }
796 
797 static int
fwdev_freebuf(struct fw_xferq * q)798 fwdev_freebuf(struct fw_xferq *q)
799 {
800 
801 	if (q->flag & FWXFERQ_EXTBUF) {
802 		if (q->buf != NULL)
803 			fwdma_free_multiseg(q->buf);
804 		q->buf = NULL;
805 		free(q->bulkxfer, M_FW);
806 		q->bulkxfer = NULL;
807 		q->flag &= ~FWXFERQ_EXTBUF;
808 		q->psize = 0;
809 		q->maxq = FWMAXQUEUE;
810 	}
811 	return 0;
812 }
813 
814 static int
fw_read_async(struct fw_drv1 * d,struct uio * uio,int ioflag)815 fw_read_async(struct fw_drv1 *d, struct uio *uio, int ioflag)
816 {
817 	struct fw_xfer *xfer;
818 	struct fw_bind *fwb;
819 	struct fw_pkt *fp;
820 	const struct tcode_info *tinfo;
821 	int err = 0;
822 
823 	mutex_enter(&d->fc->fc_mtx);
824 
825 	for (;;) {
826 		xfer = STAILQ_FIRST(&d->rq);
827 		if (xfer == NULL && err == 0) {
828 			err = cv_wait_sig(&d->cv, &d->fc->fc_mtx);
829 			if (err) {
830 				mutex_exit(&d->fc->fc_mtx);
831 				return err;
832 			}
833 			continue;
834 		}
835 		break;
836 	}
837 
838 	STAILQ_REMOVE_HEAD(&d->rq, link);
839 	mutex_exit(&d->fc->fc_mtx);
840 	fp = &xfer->recv.hdr;
841 #if 0 /* for GASP ?? */
842 	if (fc->irx_post != NULL)
843 		fc->irx_post(fc, fp->mode.ld);
844 #endif
845 	tinfo = &xfer->fc->tcode[fp->mode.hdr.tcode];
846 	err = uiomove((void *)fp, tinfo->hdr_len, uio);
847 	if (err)
848 		goto out;
849 	err = uiomove((void *)xfer->recv.payload, xfer->recv.pay_len, uio);
850 
851 out:
852 	/* recycle this xfer */
853 	fwb = (struct fw_bind *)xfer->sc;
854 	fw_xfer_unload(xfer);
855 	xfer->recv.pay_len = PAGE_SIZE;
856 	mutex_enter(&d->fc->fc_mtx);
857 	STAILQ_INSERT_TAIL(&fwb->xferlist, xfer, link);
858 	mutex_exit(&d->fc->fc_mtx);
859 	return err;
860 }
861 
862 static int
fw_write_async(struct fw_drv1 * d,struct uio * uio,int ioflag)863 fw_write_async(struct fw_drv1 *d, struct uio *uio, int ioflag)
864 {
865 	struct fw_xfer *xfer;
866 	struct fw_pkt pkt;
867 	const struct tcode_info *tinfo;
868 	int err;
869 
870 	memset(&pkt, 0, sizeof(struct fw_pkt));
871 	if ((err = uiomove((void *)&pkt, sizeof(uint32_t), uio)))
872 		return err;
873 	tinfo = &d->fc->tcode[pkt.mode.hdr.tcode];
874 	if ((err = uiomove((char *)&pkt + sizeof(uint32_t),
875 	    tinfo->hdr_len - sizeof(uint32_t), uio)))
876 		return err;
877 
878 	if ((xfer = fw_xfer_alloc_buf(M_FW, uio->uio_resid,
879 	    PAGE_SIZE/*XXX*/)) == NULL)
880 		return ENOMEM;
881 
882 	memcpy(&xfer->send.hdr, &pkt, sizeof(struct fw_pkt));
883 	xfer->send.pay_len = uio->uio_resid;
884 	if (uio->uio_resid > 0) {
885 		if ((err =
886 		    uiomove((void *)xfer->send.payload, uio->uio_resid, uio)))
887 			goto out;
888 	}
889 
890 	xfer->fc = d->fc;
891 	xfer->sc = NULL;
892 	xfer->hand = fw_xferwake;
893 	xfer->send.spd = 2 /* XXX */;
894 
895 	if ((err = fw_asyreq(xfer->fc, -1, xfer)))
896 		goto out;
897 
898 	if ((err = fw_xferwait(xfer)))
899 		goto out;
900 
901 	if (xfer->resp != 0) {
902 		err = xfer->resp;
903 		goto out;
904 	}
905 
906 	if (xfer->flag == FWXF_RCVD) {
907 		mutex_enter(&xfer->fc->fc_mtx);
908 		STAILQ_INSERT_TAIL(&d->rq, xfer, link);
909 		mutex_exit(&xfer->fc->fc_mtx);
910 		return 0;
911 	}
912 
913 out:
914 	fw_xfer_free(xfer);
915 	return err;
916 }
917 
918 static void
fw_hand(struct fw_xfer * xfer)919 fw_hand(struct fw_xfer *xfer)
920 {
921 	struct fw_bind *fwb;
922 	struct fw_drv1 *d;
923 
924 	fwb = (struct fw_bind *)xfer->sc;
925 	d = (struct fw_drv1 *)fwb->sc;
926 	mutex_enter(&xfer->fc->fc_mtx);
927 	STAILQ_INSERT_TAIL(&d->rq, xfer, link);
928 	cv_broadcast(&d->cv);
929 	mutex_exit(&xfer->fc->fc_mtx);
930 }
931