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